Esempio n. 1
0
    def _write_wrap_exe(self, wrapexec, wrappath,
                        shebang=None, args=None, cwd=None):
        args = ' '.join(args) + ' "$@"' if args else '"$@"'
        cwd = 'cd {}'.format(cwd) if cwd else ''

        assembled_env = common.assemble_env().replace(self._snap_dir, '$SNAP')
        replace_path = r'{}/[a-z0-9][a-z0-9+-]*/install'.format(
            self._parts_dir)
        assembled_env = re.sub(replace_path, '$SNAP', assembled_env)
        executable = '"{}"'.format(wrapexec)
        if shebang is not None:
            new_shebang = re.sub(replace_path, '$SNAP', shebang)
            if new_shebang != shebang:
                # If the shebang was pointing to and executable within the
                # local 'parts' dir, have the wrapper script execute it
                # directly, since we can't use $SNAP in the shebang itself.
                executable = '"{}" "{}"'.format(new_shebang, wrapexec)
        script = ('#!/bin/sh\n' +
                  '{}\n'.format(assembled_env) +
                  '{}\n'.format(cwd) +
                  'LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH\n'
                  'exec {} {}\n'.format(executable, args))

        with open(wrappath, 'w+') as f:
            f.write(script)

        os.chmod(wrappath, 0o755)
Esempio n. 2
0
    def _write_wrap_exe(self, wrapexec, wrappath,
                        shebang=None, args=None, cwd=None):
        args = ' '.join(args) + ' "$@"' if args else '"$@"'
        cwd = 'cd {}'.format(cwd) if cwd else ''

        # If we are dealing with classic confinement it means all our
        # binaries are linked with `nodefaultlib` so this is harmless.
        # We do however want to be on the safe side and make sure no
        # ABI breakage happens by accidentally loading a library from
        # the classic system.
        include_library_paths = self._config_data['confinement'] != 'classic'
        assembled_env = common.assemble_env(include_library_paths)
        assembled_env = assembled_env.replace(self._snap_dir, '$SNAP')
        replace_path = r'{}/[a-z0-9][a-z0-9+-]*/install'.format(
            self._parts_dir)
        assembled_env = re.sub(replace_path, '$SNAP', assembled_env)
        executable = '"{}"'.format(wrapexec)
        if shebang is not None:
            new_shebang = re.sub(replace_path, '$SNAP', shebang)
            if new_shebang != shebang:
                # If the shebang was pointing to and executable within the
                # local 'parts' dir, have the wrapper script execute it
                # directly, since we can't use $SNAP in the shebang itself.
                executable = '"{}" "{}"'.format(new_shebang, wrapexec)
        script = ('#!/bin/sh\n' +
                  '{}\n'.format(assembled_env) +
                  '{}\n'.format(cwd) +
                  'LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH\n'
                  'exec {} {}\n'.format(executable, args))

        with open(wrappath, 'w+') as f:
            f.write(script)

        os.chmod(wrappath, 0o755)
Esempio n. 3
0
    def _assemble_runtime_environment(self) -> str:
        # Classic confinement or building on a host that does not match the target base
        # means we cannot setup an environment that will work.
        if self._config_data["confinement"] == "classic":
            # Temporary workaround for snapd bug not expanding PATH:
            # We generate an empty runner which addresses the issue.
            # https://bugs.launchpad.net/snapd/+bug/1860369
            return ""

        env = list()
        if self._project_config.project._snap_meta.base in ("core", "core16", "core18"):
            common.env = self._project_config.snap_env()
            assembled_env = common.assemble_env()
            common.reset_env()

            assembled_env = assembled_env.replace(self._prime_dir, "$SNAP")
            env.append(self._install_path_pattern.sub("$SNAP", assembled_env))
        else:
            # TODO use something local to the meta package and
            # only add paths for directory items that actually exist.
            runtime_env = project_loader.runtime_env(
                self._prime_dir, self._project_config.project.arch_triplet
            )
            for e in runtime_env:
                env.append(re.sub(self._prime_dir, "$SNAP", e))

        env.append("export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH")

        return "\n".join(env)
Esempio n. 4
0
    def _run_scriptlet(self, scriptlet_name: str, scriptlet: str,
                       workdir: str) -> None:
        with tempfile.TemporaryDirectory() as tempdir:
            call_fifo = _NonBlockingRWFifo(
                os.path.join(tempdir, 'function_call'))
            feedback_fifo = _NonBlockingRWFifo(
                os.path.join(tempdir, 'call_feedback'))

            env = ''
            if common.is_snap():
                # Since the snap is classic, $SNAP/bin is not on the $PATH.
                # Let's set an alias to make sure it's found (but only if it
                # exists).
                snapcraftctl_path = os.path.join(os.getenv('SNAP'), 'bin',
                                                 'snapcraftctl')
                if os.path.exists(snapcraftctl_path):
                    env += 'alias snapcraftctl="$SNAP/bin/snapcraftctl"\n'
            env += common.assemble_env()

            # snapcraftctl only works consistently if it's using the exact same
            # interpreter as that used by snapcraft itself, thus the definition
            # of SNAPCRAFT_INTERPRETER.
            script = textwrap.dedent("""\
                export SNAPCRAFTCTL_CALL_FIFO={call_fifo}
                export SNAPCRAFTCTL_FEEDBACK_FIFO={feedback_fifo}
                export SNAPCRAFT_INTERPRETER={interpreter}
                {env}
                {scriptlet}
            """.format(interpreter=sys.executable,
                       call_fifo=call_fifo.path,
                       feedback_fifo=feedback_fifo.path,
                       env=env,
                       scriptlet=scriptlet))

            process = subprocess.Popen(['/bin/sh', '-e', '-c', script],
                                       cwd=workdir)

            status = None
            try:
                while status is None:
                    function_call = call_fifo.read()
                    if function_call:
                        # Handle the function and let caller know that function
                        # call has been handled (must contain at least a
                        # newline, anything beyond is considered an error by
                        # snapcraftctl)
                        feedback_fifo.write('{}\n'.format(
                            self._handle_builtin_function(
                                scriptlet_name, function_call.strip())))
                    status = process.poll()

                    # Don't loop TOO busily
                    time.sleep(0.1)
            finally:
                call_fifo.close()
                feedback_fifo.close()

            if status:
                raise errors.ScriptletRunError(scriptlet_name=scriptlet_name,
                                               code=status)
Esempio n. 5
0
    def _generate_command_chain(self) -> List[str]:
        if self._command_chain is not None:
            return self._command_chain

        command_chain = list()
        # Classic confinement or building on a host that does not match the target base
        # means we cannot setup an environment that will work.
        if (self._config_data["confinement"] == "classic"
                or not self._is_host_compatible_with_base):
            assembled_env = None
        else:
            meta_runner = os.path.join(self._prime_dir, "snap",
                                       "command-chain", "snapcraft-runner")
            assembled_env = common.assemble_env()
            assembled_env = assembled_env.replace(self._prime_dir, "$SNAP")
            assembled_env = self._install_path_pattern.sub(
                "$SNAP", assembled_env)

            if assembled_env:
                os.makedirs(os.path.dirname(meta_runner), exist_ok=True)
                with open(meta_runner, "w") as f:
                    print("#!/bin/sh", file=f)
                    print(assembled_env, file=f)
                    print(
                        "export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH",
                        file=f,
                    )
                    print('exec "$@"', file=f)
                os.chmod(meta_runner, 0o755)
            command_chain.append(os.path.relpath(meta_runner, self._prime_dir))

        self._command_chain = command_chain

        return command_chain
Esempio n. 6
0
    def _generate_snapcraft_runner(self) -> Optional[str]:
        """Create runner if required.

        Return path relative to prime directory, if created."""

        # Classic confinement or building on a host that does not match the target base
        # means we cannot setup an environment that will work.
        if (self._config_data["confinement"] == "classic"
                or not self._is_host_compatible_with_base
                or not self._snap_meta.apps):
            return None

        meta_runner = os.path.join(self._prime_dir, "snap", "command-chain",
                                   "snapcraft-runner")

        common.env = self._project_config.snap_env()
        assembled_env = common.assemble_env()
        assembled_env = assembled_env.replace(self._prime_dir, "$SNAP")
        assembled_env = self._install_path_pattern.sub("$SNAP", assembled_env)

        if assembled_env:
            os.makedirs(os.path.dirname(meta_runner), exist_ok=True)
            with open(meta_runner, "w") as f:
                print("#!/bin/sh", file=f)
                print(assembled_env, file=f)
                print(
                    "export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH",
                    file=f)
                print('exec "$@"', file=f)
            os.chmod(meta_runner, 0o755)

        common.reset_env()
        return os.path.relpath(meta_runner, self._prime_dir)
Esempio n. 7
0
 def _get_installed_node_packages(self, cwd):
     # There is no yarn ls
     cmd = [os.path.join(self._npm_dir, "bin", "npm"), "ls", "--json"]
     try:
         full_cmd = [
             '/bin/sh', '-c', ' '.join(
                 list(
                     map(lambda s: s.replace('export ', ''),
                         common.assemble_env().split('\n'))) + cmd)
         ]
         output = subprocess.check_output(full_cmd, cwd=cwd)
     except subprocess.CalledProcessError as error:
         # XXX When dependencies have missing dependencies, an error like
         # this is printed to stderr:
         # npm ERR! peer dep missing: glob@*, required by [email protected]
         # retcode is not 0, which raises an exception.
         output = error.output.decode(sys.getfilesystemencoding()).strip()
     packages = collections.OrderedDict()
     output_json = json.loads(output,
                              object_pairs_hook=collections.OrderedDict)
     dependencies = output_json.get("dependencies", [])
     while dependencies:
         key, value = dependencies.popitem(last=False)
         # XXX Just as above, dependencies without version are the ones
         # missing.
         if "version" in value:
             packages[key] = value["version"]
         if "dependencies" in value:
             dependencies.update(value["dependencies"])
     return packages
Esempio n. 8
0
    def _write_wrap_exe(self,
                        wrapexec,
                        wrappath,
                        shebang=None,
                        args=None,
                        cwd=None):
        args = ' '.join(args) + ' "$@"' if args else '"$@"'
        cwd = 'cd {}'.format(cwd) if cwd else ''

        assembled_env = common.assemble_env().replace(self._snap_dir, '$SNAP')
        replace_path = r'{}/[a-z0-9][a-z0-9+-]*/install'.format(
            self._parts_dir)
        assembled_env = re.sub(replace_path, '$SNAP', assembled_env)
        executable = '"{}"'.format(wrapexec)
        if shebang is not None:
            new_shebang = re.sub(replace_path, '$SNAP', shebang)
            if new_shebang != shebang:
                # If the shebang was pointing to and executable within the
                # local 'parts' dir, have the wrapper script execute it
                # directly, since we can't use $SNAP in the shebang itself.
                executable = '"{}" "{}"'.format(new_shebang, wrapexec)
        script = ('#!/bin/sh\n' + '{}\n'.format(assembled_env) +
                  '{}\n'.format(cwd) +
                  'LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH\n'
                  'exec {} {}\n'.format(executable, args))

        with open(wrappath, 'w+') as f:
            f.write(script)

        os.chmod(wrappath, 0o755)
Esempio n. 9
0
    def _write_wrap_exe(self,
                        wrapexec,
                        wrappath,
                        shebang=None,
                        args=None,
                        cwd=None):
        if args:
            quoted_args = ['"{}"'.format(arg) for arg in args]
        else:
            quoted_args = []
        args = ' '.join(quoted_args) + ' "$@"' if args else '"$@"'
        cwd = 'cd {}'.format(cwd) if cwd else ''

        # If we are dealing with classic confinement it means all our
        # binaries are linked with `nodefaultlib` but we still do
        # not want to leak PATH or other environment variables
        # that would affect the applications view of the classic
        # environment it is dropped into.
        replace_path = re.compile(r'{}/[a-z0-9][a-z0-9+-]*/install'.format(
            re.escape(self._parts_dir)))
        if self._config_data['confinement'] == 'classic':
            assembled_env = None
        else:
            assembled_env = common.assemble_env()
            assembled_env = assembled_env.replace(self._prime_dir, '$SNAP')
            assembled_env = replace_path.sub('$SNAP', assembled_env)

        executable = '"{}"'.format(wrapexec)

        if shebang:
            if shebang.startswith('/usr/bin/env '):
                shebang = shell_utils.which(shebang.split()[1])
            new_shebang = replace_path.sub('$SNAP', shebang)
            new_shebang = re.sub(self._prime_dir, '$SNAP', new_shebang)
            if new_shebang != shebang:
                # If the shebang was pointing to and executable within the
                # local 'parts' dir, have the wrapper script execute it
                # directly, since we can't use $SNAP in the shebang itself.
                executable = '"{}" "{}"'.format(new_shebang, wrapexec)

        with open(wrappath, 'w+') as f:
            print('#!/bin/sh', file=f)
            if assembled_env:
                print('{}'.format(assembled_env), file=f)
                print(
                    'export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:'
                    '$LD_LIBRARY_PATH',
                    file=f)
            if cwd:
                print('{}'.format(cwd), file=f)
            # TODO remove this once LP: #1656340 is fixed in snapd.
            print(dedent("""\
                # Workaround for LP: #1656340
                [ -n "$XDG_RUNTIME_DIR" ] && mkdir -p $XDG_RUNTIME_DIR -m 700
                """),
                  file=f)
            print('exec {} {}'.format(executable, args), file=f)

        os.chmod(wrappath, 0o755)
Esempio n. 10
0
    def _write_wrap_exe(self,
                        wrapexec,
                        wrappath,
                        shebang=None,
                        args=None,
                        cwd=None):
        if args:
            quoted_args = ['"{}"'.format(arg) for arg in args]
        else:
            quoted_args = []
        args = ' '.join(quoted_args) + ' "$@"' if args else '"$@"'
        cwd = 'cd {}'.format(cwd) if cwd else ''

        # If we are dealing with classic confinement it means all our
        # binaries are linked with `nodefaultlib` but we still do
        # not want to leak PATH or other environment variables
        # that would affect the applications view of the classic
        # environment it is dropped into.
        replace_path = re.compile(r'{}/[a-z0-9][a-z0-9+-]*/install'.format(
            re.escape(self._parts_dir)))
        # Confinement classic or when building on a host that does not match
        # the target base means we cannot setup an environment that will work.
        if (self._config_data['confinement'] == 'classic'
                or not self._is_host_compatible_with_base):
            assembled_env = None
        else:
            assembled_env = common.assemble_env()
            assembled_env = assembled_env.replace(self._prime_dir, '$SNAP')
            assembled_env = replace_path.sub('$SNAP', assembled_env)

        executable = '"{}"'.format(wrapexec)

        if shebang:
            if shebang.startswith('/usr/bin/env '):
                shebang = shell_utils.which(shebang.split()[1])
            new_shebang = replace_path.sub('$SNAP', shebang)
            new_shebang = re.sub(self._prime_dir, '$SNAP', new_shebang)
            if new_shebang != shebang:
                # If the shebang was pointing to and executable within the
                # local 'parts' dir, have the wrapper script execute it
                # directly, since we can't use $SNAP in the shebang itself.
                executable = '"{}" "{}"'.format(new_shebang, wrapexec)

        with open(wrappath, 'w+') as f:
            print('#!/bin/sh', file=f)
            if assembled_env:
                print('{}'.format(assembled_env), file=f)
                print(
                    'export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:'
                    '$LD_LIBRARY_PATH',
                    file=f)
            if cwd:
                print('{}'.format(cwd), file=f)
            print('exec {} {}'.format(executable, args), file=f)

        os.chmod(wrappath, 0o755)
Esempio n. 11
0
def _get_env():
    env = ""
    if common.is_snap():
        # Since the snap is classic, there is no $PATH pointing into the snap, which
        # means snapcraftctl won't be found. We can't use aliases since they don't
        # persist into subshells. However, we know that snapcraftctl lives in its own
        # directory, so adding that to the PATH should have no ill side effects.
        env += 'export PATH="$PATH:$SNAP/bin/scriptlet-bin"\n'
    env += common.assemble_env()

    return env
Esempio n. 12
0
def _get_env():
    env = ""
    if common.is_snap():
        # Since the snap is classic, $SNAP/bin is not on the $PATH.
        # Let's set an alias to make sure it's found (but only if it
        # exists).
        snapcraftctl_path = os.path.join(os.getenv("SNAP"), "bin", "snapcraftctl")
        if os.path.exists(snapcraftctl_path):
            env += 'alias snapcraftctl="$SNAP/bin/snapcraftctl"\n'
    env += common.assemble_env()

    return env
Esempio n. 13
0
    def _write_wrap_exe(self, wrapexec, wrappath, shebang=None, args=None, cwd=None):
        if args:
            quoted_args = ['"{}"'.format(arg) for arg in args]
        else:
            quoted_args = []
        args = " ".join(quoted_args) + ' "$@"' if args else '"$@"'
        cwd = "cd {}".format(cwd) if cwd else ""

        # If we are dealing with classic confinement it means all our
        # binaries are linked with `nodefaultlib` but we still do
        # not want to leak PATH or other environment variables
        # that would affect the applications view of the classic
        # environment it is dropped into.
        replace_path = re.compile(
            r"{}/[a-z0-9][a-z0-9+-]*/install".format(re.escape(self._parts_dir))
        )
        # Confinement classic or when building on a host that does not match
        # the target base means we cannot setup an environment that will work.
        if (
            self._config_data["confinement"] == "classic"
            or not self._is_host_compatible_with_base
        ):
            assembled_env = None
        else:
            assembled_env = common.assemble_env()
            assembled_env = assembled_env.replace(self._prime_dir, "$SNAP")
            assembled_env = replace_path.sub("$SNAP", assembled_env)

        executable = '"{}"'.format(wrapexec)

        if shebang:
            if shebang.startswith("/usr/bin/env "):
                shebang = shell_utils.which(shebang.split()[1])
            new_shebang = replace_path.sub("$SNAP", shebang)
            new_shebang = re.sub(self._prime_dir, "$SNAP", new_shebang)
            if new_shebang != shebang:
                # If the shebang was pointing to and executable within the
                # local 'parts' dir, have the wrapper script execute it
                # directly, since we can't use $SNAP in the shebang itself.
                executable = '"{}" "{}"'.format(new_shebang, wrapexec)

        with open(wrappath, "w+") as f:
            print("#!/bin/sh", file=f)
            if assembled_env:
                print("{}".format(assembled_env), file=f)
                print(
                    "export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH", file=f
                )
            if cwd:
                print("{}".format(cwd), file=f)
            print("exec {} {}".format(executable, args), file=f)

        os.chmod(wrappath, 0o755)
Esempio n. 14
0
def _get_env():
    env = ""
    if common.is_snap():
        # Since the snap is classic, $SNAP/bin is not on the $PATH.
        # Let's set an alias to make sure it's found (but only if it
        # exists).
        snapcraftctl_path = os.path.join(os.getenv("SNAP"), "bin",
                                         "snapcraftctl")
        if os.path.exists(snapcraftctl_path):
            env += 'alias snapcraftctl="$SNAP/bin/snapcraftctl"\n'
    env += common.assemble_env()

    return env
Esempio n. 15
0
def _find_bin(binary, basedir):
    # If it doesn't exist it might be in the path
    logger.debug('Checking that {!r} is in the $PATH'.format(binary))
    script = ('#!/bin/sh\n' +
              '{}\n'.format(common.assemble_env()) +
              'which "{}"\n'.format(binary))
    with tempfile.NamedTemporaryFile('w+') as tempf:
        tempf.write(script)
        tempf.flush()
        try:
            common.run(['/bin/sh', tempf.name], cwd=basedir,
                       stdout=subprocess.DEVNULL)
        except subprocess.CalledProcessError:
            raise CommandError(binary)
Esempio n. 16
0
def _find_bin(binary, basedir):
    # If it doesn't exist it might be in the path
    logger.debug('Checking that {!r} is in the $PATH'.format(binary))
    script = ('#!/bin/sh\n' + '{}\n'.format(common.assemble_env()) +
              'which "{}"\n'.format(binary))
    with tempfile.NamedTemporaryFile('w+') as tempf:
        tempf.write(script)
        tempf.flush()
        try:
            common.run(['/bin/sh', tempf.name],
                       cwd=basedir,
                       stdout=subprocess.DEVNULL)
        except subprocess.CalledProcessError:
            raise CommandError(binary)
Esempio n. 17
0
    def _assemble_runtime_environment(self) -> str:
        # Classic confinement or building on a host that does not match the target base
        # means we cannot setup an environment that will work.
        if self._config_data["confinement"] == "classic":
            # Temporary workaround for snapd bug not expanding PATH:
            # We generate an empty runner which addresses the issue.
            # https://bugs.launchpad.net/snapd/+bug/1860369
            return ""

        common.env = self._project_config.snap_env()
        assembled_env = common.assemble_env()
        common.reset_env()

        assembled_env = assembled_env.replace(self._prime_dir, "$SNAP")
        assembled_env = self._install_path_pattern.sub("$SNAP", assembled_env)
        ld_library_env = "export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH"
        return "\n".join([assembled_env, ld_library_env])
Esempio n. 18
0
    def _write_wrap_exe(self, wrapexec, wrappath,
                        shebang=None, args=None, cwd=None):
        args = ' '.join(args) + ' "$@"' if args else '"$@"'
        cwd = 'cd {}'.format(cwd) if cwd else ''

        # If we are dealing with classic confinement it means all our
        # binaries are linked with `nodefaultlib` but we still do
        # not want to leak PATH or other environment variables
        # that would affect the applications view of the classic
        # environment it is dropped into.
        replace_path = re.compile(r'{}/[a-z0-9][a-z0-9+-]*/install'.format(
            re.escape(self._parts_dir)))
        if self._config_data['confinement'] == 'classic':
            assembled_env = None
        else:
            assembled_env = common.assemble_env()
            assembled_env = assembled_env.replace(self._prime_dir, '$SNAP')
            assembled_env = replace_path.sub('$SNAP', assembled_env)

        executable = '"{}"'.format(wrapexec)

        if shebang:
            if shebang.startswith('/usr/bin/env '):
                shebang = shell_utils.which(shebang.split()[1])
            new_shebang = replace_path.sub('$SNAP', shebang)
            new_shebang = re.sub(self._prime_dir, '$SNAP', new_shebang)
            if new_shebang != shebang:
                # If the shebang was pointing to and executable within the
                # local 'parts' dir, have the wrapper script execute it
                # directly, since we can't use $SNAP in the shebang itself.
                executable = '"{}" "{}"'.format(new_shebang, wrapexec)

        with open(wrappath, 'w+') as f:
            print('#!/bin/sh', file=f)
            if assembled_env:
                print('{}'.format(assembled_env), file=f)
                print('export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:'
                      '$LD_LIBRARY_PATH', file=f)
            if cwd:
                print('{}'.format(cwd), file=f)
            print('exec {} {}'.format(executable, args), file=f)

        os.chmod(wrappath, 0o755)
Esempio n. 19
0
    def _assemble_runtime_environment(self) -> str:
        # Classic confinement or building on a host that does not match the target base
        # means we cannot setup an environment that will work.
        if self._config_data["confinement"] == "classic":
            # Temporary workaround for snapd bug not expanding PATH:
            # We generate an empty runner which addresses the issue.
            # https://bugs.launchpad.net/snapd/+bug/1860369
            return ""

        env = list()
        if self._project_config.project._snap_meta.base == "core18":
            common.env = self._project_config.snap_env()
            assembled_env = common.assemble_env()
            common.reset_env()

            assembled_env = assembled_env.replace(self._prime_dir, "$SNAP")
            env.append(self._install_path_pattern.sub("$SNAP", assembled_env))
        else:
            # TODO use something local to the meta package and
            # only add paths for directory items that actually exist.
            runtime_env = project_loader.runtime_env(
                self._prime_dir, self._project_config.project.arch_triplet
            )
            for e in runtime_env:
                env.append(re.sub(self._prime_dir, "$SNAP", e))

        if all(
            [
                part._build_attributes.enable_patchelf()
                for part in self._project_config.all_parts
            ]
        ):
            # All ELF files have had rpath and interpreter patched. Strip all LD_LIBRARY_PATH variables
            env = [e for e in env if not e.startswith("export LD_LIBRARY_PATH=")]
        else:
            env.append(
                'export LD_LIBRARY_PATH="$SNAP_LIBRARY_PATH${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"'
            )

        return "\n".join(env)
Esempio n. 20
0
    def _write_wrap_exe(self,
                        wrapexec,
                        wrappath,
                        shebang=None,
                        args=None,
                        cwd=None):
        args = ' '.join(args) + ' "$@"' if args else '"$@"'
        cwd = 'cd {}'.format(cwd) if cwd else ''

        # If we are dealing with classic confinement it means all our
        # binaries are linked with `nodefaultlib` so this is harmless.
        # We do however want to be on the safe side and make sure no
        # ABI breakage happens by accidentally loading a library from
        # the classic system.
        include_library_paths = self._config_data['confinement'] != 'classic'
        assembled_env = common.assemble_env(include_library_paths)
        assembled_env = assembled_env.replace(self._snap_dir, '$SNAP')
        replace_path = r'{}/[a-z0-9][a-z0-9+-]*/install'.format(
            self._parts_dir)
        assembled_env = re.sub(replace_path, '$SNAP', assembled_env)
        executable = '"{}"'.format(wrapexec)
        if shebang is not None:
            new_shebang = re.sub(replace_path, '$SNAP', shebang)
            if new_shebang != shebang:
                # If the shebang was pointing to and executable within the
                # local 'parts' dir, have the wrapper script execute it
                # directly, since we can't use $SNAP in the shebang itself.
                executable = '"{}" "{}"'.format(new_shebang, wrapexec)
        script = ('#!/bin/sh\n' + '{}\n'.format(assembled_env) +
                  '{}\n'.format(cwd) +
                  'LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH\n'
                  'exec {} {}\n'.format(executable, args))

        with open(wrappath, 'w+') as f:
            f.write(script)

        os.chmod(wrappath, 0o755)
    def _write_wrap_exe(self,
                        wrapexec,
                        wrappath,
                        shebang=None,
                        args=None,
                        cwd=None):
        if args:
            quoted_args = ['"{}"'.format(arg) for arg in args]
        else:
            quoted_args = []
        args = " ".join(quoted_args) + ' "$@"' if args else '"$@"'
        cwd = "cd {}".format(cwd) if cwd else ""

        # If we are dealing with classic confinement it means all our
        # binaries are linked with `nodefaultlib` but we still do
        # not want to leak PATH or other environment variables
        # that would affect the applications view of the classic
        # environment it is dropped into.
        replace_path = re.compile(r"{}/[a-z0-9][a-z0-9+-]*/install".format(
            re.escape(self._parts_dir)))
        # Confinement classic or when building on a host that does not match
        # the target base means we cannot setup an environment that will work.
        if (self._config_data["confinement"] == "classic"
                or not self._is_host_compatible_with_base):
            assembled_env = None
        else:
            assembled_env = common.assemble_env()
            assembled_env = assembled_env.replace(self._prime_dir, "$SNAP")
            assembled_env = replace_path.sub("$SNAP", assembled_env)

        executable = '"{}"'.format(wrapexec)

        if shebang:
            if shebang.startswith("/usr/bin/env "):
                shebang = shell_utils.which(shebang.split()[1])
            new_shebang = replace_path.sub("$SNAP", shebang)
            new_shebang = re.sub(self._prime_dir, "$SNAP", new_shebang)
            if new_shebang != shebang:
                # If the shebang was pointing to and executable within the
                # local 'parts' dir, have the wrapper script execute it
                # directly, since we can't use $SNAP in the shebang itself.
                executable = '"{}" "{}"'.format(new_shebang, wrapexec)

        with open(wrappath, "w+") as f:
            print("#!/bin/sh", file=f)
            if assembled_env:
                print("{}".format(assembled_env), file=f)
                print(
                    'export LD_LIBRARY_PATH="$SNAP_LIBRARY_PATH${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"',
                    file=f,
                )
                print(
                    'echo $LD_LIBRARY_PATH | grep -qE "::|^:|:$" && '
                    'echo "WARNING: an empty LD_LIBRARY_PATH has been set. '
                    "CWD will be added to the library path. "
                    'This can cause the incorrect library to be loaded."',
                    file=f,
                )
            if cwd:
                print("{}".format(cwd), file=f)
            print("exec {} {}".format(executable, args), file=f)

        os.chmod(wrappath, 0o755)