Example #1
0
def init():
    """Initialize a snapcraft project."""
    snapcraft_yaml_path = os.path.join('snap', 'snapcraft.yaml')

    if os.path.exists(snapcraft_yaml_path):
        raise errors.SnapcraftEnvironmentError(
            '{} already exists!'.format(snapcraft_yaml_path))
    elif os.path.exists('snapcraft.yaml'):
        raise errors.SnapcraftEnvironmentError(
            'snapcraft.yaml already exists!')
    elif os.path.exists('.snapcraft.yaml'):
        raise errors.SnapcraftEnvironmentError(
            '.snapcraft.yaml already exists!')
    yaml = _TEMPLATE_YAML
    with contextlib.suppress(FileExistsError):
        os.mkdir(os.path.dirname(snapcraft_yaml_path))
    with open(snapcraft_yaml_path, mode='w') as f:
        f.write(yaml)

    if os.path.exists('.git'):
        # Don't mess with an existing .gitignore
        if not os.path.exists('.gitignore'):
            gitignore = _TEMPLATE_GITIGNORE
            with open('.gitignore', mode='w') as f:
                f.write(gitignore)

    return snapcraft_yaml_path
Example #2
0
    def pull(self):
        """Copy source into build directory and fetch dependencies.

        Catkin packages can specify their system dependencies in their
        package.xml. In order to support that, the Catkin packages are
        interrogated for their dependencies here. Since `stage-packages` are
        already installed by the time this function is run, the dependencies
        from the package.xml are pulled down explicitly.
        """

        super().pull()

        # Make sure the package path exists before continuing
        if self.catkin_packages and not os.path.exists(self._ros_package_path):
            raise FileNotFoundError('Unable to find package path: "{}"'.format(
                self._ros_package_path))

        # Validate the underlay. Note that this validation can't happen in
        # __init__ as the underlay will probably only be valid once a
        # dependency has been staged.
        catkin = None
        underlay_build_path = None
        if self.options.underlay:
            underlay_build_path = self.options.underlay['build-path']
        if underlay_build_path:
            if not os.path.isdir(underlay_build_path):
                raise errors.SnapcraftEnvironmentError(
                    'Requested underlay ({!r}) does not point to a valid '
                    'directory'.format(underlay_build_path))

            if not os.path.isfile(os.path.join(underlay_build_path,
                                               'setup.sh')):
                raise errors.SnapcraftEnvironmentError(
                    'Requested underlay ({!r}) does not contain a '
                    'setup.sh'.format(underlay_build_path))

            # Use catkin_find to discover dependencies already in the underlay
            catkin = _Catkin(self.options.rosdistro, underlay_build_path,
                             self._catkin_path, self.PLUGIN_STAGE_SOURCES,
                             self.project)
            catkin.setup()

            self._generate_snapcraft_setup_sh(self.installdir,
                                              underlay_build_path)

        # Pull our own compilers so we use ones that match up with the version
        # of ROS we're using.
        compilers = _Compilers(self._compilers_path, self.PLUGIN_STAGE_SOURCES,
                               self.project)
        compilers.setup()

        # Use rosdep for dependency detection and resolution
        rosdep = _Rosdep(self.options.rosdistro, self._ros_package_path,
                         self._rosdep_path, self.PLUGIN_STAGE_SOURCES,
                         self.project)
        rosdep.setup()

        self._setup_dependencies(rosdep, catkin)
Example #3
0
    def __init__(self,
                 *,
                 default="host",
                 additional_providers: List[str] = None) -> None:
        """Instantiate a BuildEnvironmentConfig.

        :param str default: the default provider to use among the list of valid
                            ones.
        :param str additional_providers: Additional providers allowed in the
                                         environment.
        """
        valid_providers = ["host", "multipass", "lxd", "managed-host"]
        if additional_providers is not None:
            valid_providers.extend(additional_providers)

        use_lxd = None
        container_builds = os.environ.get("SNAPCRAFT_CONTAINER_BUILDS")
        if container_builds:
            echo.warning(
                "The flag SNAPCRAFT_CONTAINER_BUILDS has been deprecated. "
                "Use SNAPCRAFT_BUILD_ENVIRONMENT=lxd instead.")
            try:
                use_lxd = util.strtobool(container_builds)
            except ValueError:
                raise errors.SnapcraftEnvironmentError(
                    "The experimental feature of using non-local LXD remotes "
                    "with SNAPCRAFT_CONTAINER_BUILDS has been dropped.")

        build_provider = os.environ.get("SNAPCRAFT_BUILD_ENVIRONMENT")
        if build_provider and use_lxd:
            raise errors.SnapcraftEnvironmentError(
                "SNAPCRAFT_BUILD_ENVIRONMENT and SNAPCRAFT_CONTAINER_BUILDS "
                "cannot be used together.\n"
                "Given that SNAPCRAFT_CONTAINER_BUILDS is deprecated, "
                "unset that variable from the environment and try again.")

        if use_lxd:
            build_provider = "lxd"
        elif not build_provider:
            build_provider = default
        elif build_provider not in valid_providers:
            raise errors.SnapcraftEnvironmentError(
                "SNAPCRAFT_BUILD_ENVIRONMENT must be one of: {}.".format(
                    humanize_list(items=valid_providers, conjunction="or")))

        self.provider = build_provider
        self.is_host = build_provider == "host"
        self.is_lxd = build_provider == "lxd"
        self.is_multipass = build_provider == "multipass"
        self.is_managed_host = build_provider == "managed-host"
Example #4
0
def refresh(debug, **kwargs):
    """Refresh an existing LXD container.

    \b
    Examples:
        snapcraft refresh

    This will take care of updating the apt package cache, upgrading packages
    as needed as well as refreshing snaps.
    """

    build_environment = env.BuilderEnvironmentConfig()
    if build_environment.is_host:
        raise errors.SnapcraftEnvironmentError(
            "The 'refresh' command only applies to LXD containers but "
            "SNAPCRAFT_BUILD_ENVIRONMENT is not set or set to host.\n"
            "Maybe you meant to update the parts cache instead? "
            "You can do that with the following command:\n\n"
            "snapcraft update")

    project = get_project(**kwargs, debug=debug)
    config = project_loader.load_config(project)
    lxd.Project(
        project_options=project,
        output=None,
        source=os.path.curdir,
        metadata=config.get_metadata(),
    ).refresh()
Example #5
0
def cleanbuild():
    """Create a snap using a clean environment managed by a build provider.

    \b
    Examples:
        snapcraft cleanbuild

    The cleanbuild command requires a properly setup lxd environment that
    can connect to external networks. Refer to the "Ubuntu Desktop and
    Ubuntu Server" section on
    https://linuxcontainers.org/lxd/getting-started-cli
    to get started.

    If using a remote, a prior setup is required which is described on:
    https://linuxcontainers.org/lxd/getting-started-cli/#multiple-host

    This command is no longer available when using the base keyword."""
    # We can only end up here with an invalid yaml
    project = get_project()
    conduct_project_sanity_check(project)

    # if we got this far, the environment must be broken
    raise errors.SnapcraftEnvironmentError(
        "The cleanbuild command is no longer supported when using the base keyword."
    )
Example #6
0
def _install_multipass():
    if sys.platform == "linux":
        repo.snaps.install_snaps(["multipass/latest/beta"])
    elif sys.platform == "darwin":
        try:
            subprocess.check_call(["brew", "cask", "install", "multipass"])
        except subprocess.CalledProcessError:
            raise errors.SnapcraftEnvironmentError(
                "Failed to install multipass using homebrew.\n"
                "Verify your homebrew installation and try again.\n"
                "Alternatively, manually install multipass by running 'brew cask install multipass'."
            )

    # wait for multipassd to be available
    click.echo("Waiting for multipass...")
    retry_count = 20
    while retry_count:
        try:
            output = subprocess.check_output(["multipass", "version"]).decode()
        except subprocess.CalledProcessError:
            output = ""
        # if multipassd is in the version information, it means the service is up
        # and we can carry on
        if "multipassd" in output:
            break
        retry_count -= 1
        sleep(1)
Example #7
0
    def get_core_dynamic_linker(self):
        """Returns the dynamic linker used for the targeted core.
        If not found realpath for `/lib/ld-linux.so.2` is returned.
        However if core is not installed None will be returned.
        """
        core_path = common.get_core_path()
        dynamic_linker_path = os.path.join(
            core_path,
            self.__machine_info.get('core-dynamic-linker',
                                    'lib/ld-linux.so.2'))

        # We can't use os.path.realpath because any absolute symlinks
        # have to be interpreted relative to core_path, not the real
        # root.
        seen_paths = set()
        while True:
            if dynamic_linker_path in seen_paths:
                raise errors.SnapcraftEnvironmentError(
                    "found symlink loop resolving dynamic linker path")

            seen_paths.add(dynamic_linker_path)
            if not os.path.lexists(dynamic_linker_path):
                return None
            if not os.path.islink(dynamic_linker_path):
                return dynamic_linker_path

            link_contents = os.readlink(dynamic_linker_path)
            if os.path.isabs(link_contents):
                dynamic_linker_path = os.path.join(core_path,
                                                   link_contents.lstrip('/'))
            else:
                dynamic_linker_path = os.path.join(
                    os.path.dirname(dynamic_linker_path), link_contents)
Example #8
0
def refresh(debug, **kwargs):
    """Refresh an existing LXD container.

    \b
    Examples:
        snapcraft refresh

    This will take care of updating the apt package cache, upgrading packages
    as needed as well as refreshing snaps.
    """

    container_config = env.get_container_config()
    if not container_config.use_container:
        raise errors.SnapcraftEnvironmentError(
            "The 'refresh' command only applies to LXD containers but "
            "SNAPCRAFT_CONTAINER_BUILDS is not set or 0.\n"
            "Maybe you meant to update the parts cache instead? "
            "You can do that with the following command:\n\n"
            "snapcraft update")

    project_options = get_project_options(**kwargs, debug=debug)
    config = project_loader.load_config(project_options)
    lxd.Project(project_options=project_options,
                remote=container_config.remote,
                output=None,
                source=os.path.curdir,
                metadata=config.get_metadata()).refresh()
Example #9
0
    def __init__(self, *, force_provider: str = None) -> None:
        """Instantiate a BuildEnvironmentConfig.

        :param str force_provider: ignore the hints from the environment and use
                                   the specified provider.
        """
        if force_provider:
            build_provider = force_provider
        elif common.is_docker_instance():
            build_provider = "host"
        else:
            build_provider = os.environ.get("SNAPCRAFT_BUILD_ENVIRONMENT", "multipass")

        valid_providers = ["host", "multipass", "managed-host", "lxd"]
        if build_provider not in valid_providers:
            raise errors.SnapcraftEnvironmentError(
                "The snapcraft build environment must be one of: {}.".format(
                    humanize_list(items=valid_providers, conjunction="or")
                )
            )

        self.provider = build_provider
        self.is_host = build_provider == "host"
        self.is_multipass = build_provider == "multipass"
        self.is_multipass = build_provider == "lxd"
        self.is_managed_host = build_provider == "managed-host"
Example #10
0
    def _fetch_rust(self):
        options = []

        if self.options.rust_revision:
            options.append("--revision={}".format(self.options.rust_revision))

        if self.options.rust_channel:
            if self.options.rust_channel in ["stable", "beta", "nightly"]:
                options.append("--channel={}".format(
                    self.options.rust_channel))
            else:
                raise errors.SnapcraftEnvironmentError(
                    "{} is not a valid rust channel".format(
                        self.options.rust_channel))
        os.makedirs(self._rustpath, exist_ok=True)
        self._rustup_get.download()
        cmd = [
            self._rustup,
            "--prefix={}".format(self._rustpath),
            "--disable-sudo",
            "--save",
        ] + options
        if self.project.is_cross_compiling:
            cmd.append("--with-target={}".format(self._target))
        self.run(cmd)
Example #11
0
def _create_bins(package_json, directory):
    bin_entry = package_json.get("bin")
    if not bin_entry:
        return

    bin_dir = os.path.join(directory, "bin")
    os.makedirs(bin_dir, exist_ok=True)

    if type(bin_entry) == dict:
        binaries = bin_entry
    elif type(bin_entry) == str:
        # Support for scoped names of the form of @org/name
        name = package_json["name"]
        binaries = {name[name.find("/") + 1:]: bin_entry}
    else:
        raise errors.SnapcraftEnvironmentError(
            "The plugin is not prepared to handle bin entries of "
            "type {!r}".format(type(bin_entry)))

    for bin_name, bin_path in binaries.items():
        target = os.path.join(bin_dir, bin_name)
        # The binary might be already created from upstream sources.
        if os.path.exists(os.path.join(target)):
            continue
        source = os.path.join("..", bin_path)
        os.symlink(source, target)
        # Make it executable
        os.chmod(os.path.realpath(target), 0o755)
Example #12
0
def _call_function(function_name, args=None):
    if not args:
        args = {}

    data = {"function": function_name, "args": args}

    # We could load the FIFOs in `run` and shove them in the context, but
    # that's too early to error out if these variables aren't defined. Doing it
    # here allows one to run e.g. `snapcraftctl build --help` without needing
    # these variables defined, which is a win for usability.
    try:
        call_fifo = os.environ["SNAPCRAFTCTL_CALL_FIFO"]
        feedback_fifo = os.environ["SNAPCRAFTCTL_FEEDBACK_FIFO"]
    except KeyError as e:
        raise errors.SnapcraftEnvironmentError(
            "{!s} environment variable must be defined. Note that this "
            "utility is only designed for use within a snapcraft.yaml".format(
                e)) from e

    with open(call_fifo, "w") as f:
        f.write(json.dumps(data))
        f.flush()

    with open(feedback_fifo, "r") as f:
        feedback = f.readline().strip()

    # Any feedback is considered a fatal error.
    if feedback:
        sys.exit(-1)
Example #13
0
def get_python2_path(root):
    """Return a valid PYTHONPATH or raise an exception."""
    python_paths = glob.glob(
        os.path.join(root, 'usr', 'lib', 'python2*', 'dist-packages'))
    try:
        return python_paths[0]
    except IndexError:
        raise errors.SnapcraftEnvironmentError(
            'PYTHONPATH cannot be set for {!r}'.format(root))
Example #14
0
    def patch(self) -> None:
        """Executes the patching process for elf_files.

        First it verifies there are no GLIBC mismatches, if any are found,
        libc6 must be part of stage-packages. The verification is only
        performed if the core base is not compatible with the host the
        snapcraft build is taking place on (e.g.; building on 18.04 while
        targeting 'core' as the core base).

        If the snapcraft project is not a classic confined one and there
        are no GLIBC mismatches, then this method returns with no modifications
        to elf_files. However, if one of those cases are true, the
        required dynamic linker is retrieved and the necessary elf files
        in elf_files are patched to behave accordinginly in the environment by
        means of setting the correct interpreter and rpaths (not runpaths).

        :raises errors.SnapcraftMissingLinkerInBaseError:
            if the linker within the core base cannot be found.
        :raises errors.StagePackageMissingError:
            if there are libc6 mismatches and libc6 is not in stage-packages.
        :raises errors.SnapcraftEnvironementError:
            if something is horribly wrong.
        """
        # Just return if this is a static base and libc6 has not been staged.
        if self._project.is_static_base(self._core_base) and not self._is_libc6_staged:
            return
        if not (
            self._project.is_static_base(self._core_base)
            or self._project.is_host_compatible_with_base(self._core_base)
        ):
            logger.debug("Host is not compatible with base")
            self._verify_compat()
        logger.debug("Is classic: {!r}".format(self._is_classic))
        logger.debug("Is libc6 in stage-packages: {!r}".format(self._is_libc6_staged))
        if not (self._is_classic or self._is_libc6_staged):
            return

        if self._is_libc6_staged:
            dynamic_linker = elf.find_linker(
                root_path=self._primedir, snap_base_path=self._snap_base_path
            )
            logger.warning(
                "libc6 has been staged into the snap: only do this if you know what "
                "what you are doing."
            )
        elif self._is_classic:
            dynamic_linker = self._project.get_core_dynamic_linker(
                self._core_base, expand=False
            )
        else:
            raise errors.SnapcraftEnvironmentError(
                "An unexpected error has occurred while patching. "
                "Please log an issue against the snapcraft tool."
            )

        logger.debug("Dynamic linker set to {!r}".format(dynamic_linker))
        self._patch(dynamic_linker)
Example #15
0
def _find_machine(deb_arch):
    for machine in _ARCH_TRANSLATIONS:
        if _ARCH_TRANSLATIONS[machine].get("deb", "") == deb_arch:
            return machine
        elif _ARCH_TRANSLATIONS[machine].get("uts_machine", "") == deb_arch:
            return machine

    raise errors.SnapcraftEnvironmentError(
        "Cannot set machine from deb_arch {!r}".format(deb_arch))
Example #16
0
def _find_machine(deb_arch):
    for machine in _ARCH_TRANSLATIONS:
        if _ARCH_TRANSLATIONS[machine].get('deb', '') == deb_arch:
            return machine
        elif _ARCH_TRANSLATIONS[machine].get('uts_machine', '') == deb_arch:
            return machine

    raise errors.SnapcraftEnvironmentError(
        'Cannot set machine from deb_arch {!r}'.format(deb_arch))
Example #17
0
def get_python2_path(root):
    """Return a valid PYTHONPATH or raise an exception."""
    python_paths = glob.glob(
        os.path.join(root, "usr", "lib", "python2*", "dist-packages"))
    try:
        return python_paths[0]
    except IndexError:
        raise errors.SnapcraftEnvironmentError(
            "PYTHONPATH cannot be set for {!r}".format(root))
Example #18
0
def init():
    """Initialize a snapcraft project."""
    snapcraft_yaml_path = os.path.join("snap", "snapcraft.yaml")

    if os.path.exists(snapcraft_yaml_path):
        raise errors.SnapcraftEnvironmentError(
            "{} already exists!".format(snapcraft_yaml_path)
        )
    elif os.path.exists("snapcraft.yaml"):
        raise errors.SnapcraftEnvironmentError("snapcraft.yaml already exists!")
    elif os.path.exists(".snapcraft.yaml"):
        raise errors.SnapcraftEnvironmentError(".snapcraft.yaml already exists!")
    text = _TEMPLATE_YAML
    with contextlib.suppress(FileExistsError):
        os.mkdir(os.path.dirname(snapcraft_yaml_path))
    with open(snapcraft_yaml_path, mode="w") as f:
        f.write(text)

    return snapcraft_yaml_path
Example #19
0
def cleanbuild():
    """This command is no longer available when using the base keyword."""
    # We can only end up here with an invalid yaml
    project = get_project()
    conduct_project_sanity_check(project)

    # if we got this far, the environment must be broken
    raise errors.SnapcraftEnvironmentError(
        "The cleanbuild command is no longer supported when using the base keyword."
    )
Example #20
0
 def cross_compiler_prefix(self):
     try:
         # cross-compilation of x86 32bit binaries on a x86_64 host is
         # possible by reusing the native toolchain - let Kbuild figure
         # it out by itself and pass down an empty cross-compiler-prefix
         # to start the build
         if self.__platform_arch == "x86_64" and self.__target_machine == "i686":
             return ""
         return self.__machine_info["cross-compiler-prefix"]
     except KeyError:
         raise errors.SnapcraftEnvironmentError(
             "Cross compilation not supported for target arch {!r}".format(
                 self.__target_machine))
Example #21
0
def _build_env(root, snap_name, confinement, arch_triplet,
               core_dynamic_linker=None):
    """Set the environment variables required for building.

    This is required for the current parts installdir due to stage-packages
    and also to setup the stagedir.
    """
    env = []

    paths = common.get_include_paths(root, arch_triplet)
    if paths:
        for envvar in ['CPPFLAGS', 'CFLAGS', 'CXXFLAGS']:
            env.append(formatting_utils.format_path_variable(
                envvar, paths, prepend='-I', separator=' '))

    if confinement == 'classic':
        if not core_dynamic_linker:
            raise errors.SnapcraftEnvironmentError(
                'classic confinement requires the core snap to be installed. '
                'Install it by running `snap install core`.')

        core_path = common.get_core_path()
        core_rpaths = common.get_library_paths(core_path, arch_triplet,
                                               existing_only=False)
        snap_path = os.path.join('/snap', snap_name, 'current')
        snap_rpaths = common.get_library_paths(snap_path, arch_triplet,
                                               existing_only=False)

        # snap_rpaths before core_rpaths to prefer libraries from the snap.
        rpaths = formatting_utils.combine_paths(
            snap_rpaths + core_rpaths, prepend='', separator=':')
        env.append('LDFLAGS="$LDFLAGS '
                   # Building tools to continue the build becomes problematic
                   # with nodefaultlib.
                   # '-Wl,-z,nodefaultlib '
                   '-Wl,--dynamic-linker={0} '
                   '-Wl,-rpath,{1}"'.format(core_dynamic_linker, rpaths))

    paths = common.get_library_paths(root, arch_triplet)
    if paths:
        env.append(formatting_utils.format_path_variable(
            'LDFLAGS', paths, prepend='-L', separator=' '))

    paths = common.get_pkg_config_paths(root, arch_triplet)
    if paths:
        env.append(formatting_utils.format_path_variable(
            'PKG_CONFIG_PATH', paths, prepend='', separator=':'))

    return env
Example #22
0
    def get_core_dynamic_linker(self, base: str, expand: bool = True) -> str:
        """Returns the dynamic linker used for the targeted core.

        :param str base: the base core snap to search for linker.
        :param bool expand: expand the linker to the actual linker if True,
                            else the main entry point to the linker for the
                            projects architecture.
        :return: the absolute path to the linker
        :rtype: str
        :raises snapcraft.internal.errors.SnapcraftMissingLinkerInBaseError:
            if the linker cannot be found in the base.
        :raises snapcraft.internal.errors.SnapcraftEnvironmentError:
            if a loop is found while resolving the real path to the linker.
        """
        core_path = common.get_installed_snap_path(base)
        dynamic_linker_path = os.path.join(
            core_path,
            self.__machine_info.get("core-dynamic-linker", "lib/ld-linux.so.2"),
        )

        # return immediately if we do not need to expand
        if not expand:
            return dynamic_linker_path

        # We can't use os.path.realpath because any absolute symlinks
        # have to be interpreted relative to core_path, not the real
        # root.
        seen_paths = set()  # type: Set[str]
        while True:
            if dynamic_linker_path in seen_paths:
                raise errors.SnapcraftEnvironmentError(
                    "found symlink loop resolving dynamic linker path"
                )

            seen_paths.add(dynamic_linker_path)
            if not os.path.lexists(dynamic_linker_path):
                raise errors.SnapcraftMissingLinkerInBaseError(
                    base=base, linker_path=dynamic_linker_path
                )
            if not os.path.islink(dynamic_linker_path):
                return dynamic_linker_path

            link_contents = os.readlink(dynamic_linker_path)
            if os.path.isabs(link_contents):
                dynamic_linker_path = os.path.join(core_path, link_contents.lstrip("/"))
            else:
                dynamic_linker_path = os.path.join(
                    os.path.dirname(dynamic_linker_path), link_contents
                )
Example #23
0
    def _env_dict(self, root):
        env = dict()
        rubydir = os.path.join(root, "lib", "ruby")

        # Patch versions of ruby continue to use the minor version's RUBYLIB,
        # GEM_HOME, and GEM_PATH. Fortunately there should just be one, so we
        # can detect it by globbing instead of trying to determine what the
        # minor version is programmatically
        versions = glob.glob(os.path.join(rubydir, "gems", "*"))

        # Before Ruby has been pulled/installed, no versions will be found.
        # If that's the case, we won't define any Ruby-specific variables yet
        if len(versions) == 1:
            ruby_version = os.path.basename(versions[0])

            rubylib = os.path.join(rubydir, ruby_version)

            # Ruby uses some pretty convoluted rules for determining its
            # arch-specific RUBYLIB. Rather than try and duplicate that logic
            # here, let's just look for a file that we know is in there:
            # rbconfig.rb. There should only be one.
            paths = glob.glob(os.path.join(rubylib, "*", "rbconfig.rb"))
            if len(paths) != 1:
                raise errors.SnapcraftEnvironmentError(
                    "Expected a single rbconfig.rb, but found {}".format(
                        len(paths)))

            env["RUBYLIB"] = "{}:{}".format(rubylib, os.path.dirname(paths[0]))
            env["GEM_HOME"] = os.path.join(rubydir, "gems", ruby_version)
            env["GEM_PATH"] = os.path.join(rubydir, "gems", ruby_version)
        elif len(versions) > 1:
            raise errors.SnapcraftEnvironmentError(
                "Expected a single Ruby version, but found {}".format(
                    len(versions)))

        return env
Example #24
0
def run_legacy_snapcraft(argv=sys.argv[1:]) -> None:
    if not common.is_snap():
        raise errors.SnapcraftEnvironmentError(
            "Legacy mode not supported in this installation. "
            "Install snapcraft from https://snapcraft.io/snapcraft and try again."
        )

    legacy_python = os.path.join(common.get_legacy_snapcraft_dir(), "usr",
                                 "bin", "python3")
    legacy_snapcraft = os.path.join(common.get_legacy_snapcraft_dir(), "bin",
                                    "snapcraft")

    cmd = [legacy_python, legacy_snapcraft] + argv
    logging.debug("Running legacy snapcraft with: {}".format(cmd))
    os.execv(legacy_python, cmd)
Example #25
0
 def _get_target(self) -> str:
     # Cf. rustc --print target-list
     targets = {
         "armhf": "armv7-{}-{}eabihf",
         "arm64": "aarch64-{}-{}",
         "i386": "i686-{}-{}",
         "amd64": "x86_64-{}-{}",
         "ppc64el": "powerpc64le-{}-{}",
         "s390x": "s390x-{}-{}",
     }
     rust_target = targets.get(self.project.deb_arch)
     if not rust_target:
         raise errors.SnapcraftEnvironmentError(
             "{!r} is not supported as a target architecture ".format(
                 self.project.deb_arch))
     return rust_target.format("unknown-linux", "gnu")
Example #26
0
def _verify_dependents_will_be_cleaned(part_name, clean_part_names, step,
                                       config):
    # Get the name of the parts that depend upon this one
    dependents = config.parts.get_dependents(part_name)

    # Verify that they're either already clean, or that they will be cleaned.
    if not dependents.issubset(clean_part_names):
        for part in config.all_parts:
            if part.name in dependents and not part.is_clean(step):
                humanized_parts = formatting_utils.humanize_list(
                    dependents, 'and')

                raise errors.SnapcraftEnvironmentError(
                    'Requested clean of {!r} but {} depend{} upon it. Please '
                    "add each to the clean command if that's what you "
                    'intended.'.format(part_name, humanized_parts,
                                       's' if len(dependents) == 1 else ''))
Example #27
0
def get_snapcraft_yaml():
    possible_yamls = [
        os.path.join('snap', 'snapcraft.yaml'),
        'snapcraft.yaml',
        '.snapcraft.yaml',
    ]

    snapcraft_yamls = [y for y in possible_yamls if os.path.exists(y)]

    if not snapcraft_yamls:
        raise SnapcraftYamlFileError('snap/snapcraft.yaml')
    elif len(snapcraft_yamls) > 1:
        raise errors.SnapcraftEnvironmentError(
            'Found a {!r} and a {!r}, please remove one.'.format(
                snapcraft_yamls[0], snapcraft_yamls[1]))

    return snapcraft_yamls[0]
Example #28
0
def clean(parts, step_name):
    """Remove content - cleans downloads, builds or install artifacts.

    \b
    Examples:
        snapcraft clean
        snapcraft clean my-part --step build
    """
    build_environment = env.BuilderEnvironmentConfig()
    try:
        project = get_project(
            is_managed_host=build_environment.is_managed_host
        )
    except YamlValidationError:
        # We need to be able to clean invalid projects too.
        project = get_project(
            is_managed_host=build_environment.is_managed_host,
            skip_snapcraft_yaml=True
        )

    step = None
    if step_name:
        if step_name == "strip":
            echo.warning(
                "DEPRECATED: Use `prime` instead of `strip` as the step to clean"
            )
            step_name = "prime"
        step = steps.get_step_by_name(step_name)

    if build_environment.is_lxd:
        lxd.Project(project=project, output=None, source=os.path.curdir).clean(
            parts, step
        )
    elif build_environment.is_host:
        lifecycle.clean(project, parts, step)
    else:
        # TODO support for steps.
        if parts or step_name:
            raise errors.SnapcraftEnvironmentError(
                "Build providers are still not feature complete, specifying parts or a step name "
                "is not yet supported.")
        build_provider_class = build_providers.get_provider_for(
            build_environment.provider
        )
        build_provider_class(project=project, echoer=echo).clean_project()
Example #29
0
    def build(self):
        super().build()
        self.run(
            ["shards", "build", "--without-development"] +
            self.options.crystal_build_options,
            self.builddir,
        )

        output_bin = os.path.join(self.builddir, "bin")
        if not os.path.exists(output_bin):
            raise errors.SnapcraftEnvironmentError(
                "No binaries were built. Ensure the shards.yaml contains valid targets."
            )

        install_bin_path = os.path.join(self.installdir, "bin")

        bin_paths = (os.path.join(output_bin, b)
                     for b in os.listdir(output_bin))
        elf_files = (elf.ElfFile(path=b) for b in bin_paths
                     if elf.ElfFile.is_elf(b))

        os.makedirs(install_bin_path, exist_ok=True)

        for elf_file in elf_files:
            shutil.copy2(
                elf_file.path,
                os.path.join(install_bin_path,
                             os.path.basename(elf_file.path)),
            )

            elf_dependencies_path = elf_file.load_dependencies(
                root_path=self.installdir,
                core_base_path=common.get_installed_snap_path(
                    self.project._get_build_base()),
                arch_triplet=self.project.arch_triplet,
                content_dirs=self.project._get_provider_content_dirs(),
            )
            for elf_dependency_path in elf_dependencies_path:
                lib_install_path = os.path.join(self.installdir,
                                                elf_dependency_path[1:])
                os.makedirs(os.path.dirname(lib_install_path), exist_ok=True)
                if not os.path.exists(lib_install_path):
                    file_utils.link_or_copy(elf_dependency_path,
                                            lib_install_path,
                                            follow_symlinks=True)
Example #30
0
def _sanity_check_build_provider_flags(build_provider: str, **kwargs) -> None:
    destructive_mode = kwargs.get("destructive_mode")
    env_provider = os.getenv("SNAPCRAFT_BUILD_ENVIRONMENT")

    # Specifying --provider=host requires the use of --destructive-mode.
    # Exceptions include:
    # (1) SNAPCRAFT_BUILD_ENVIRONMENT=host.
    # (2) Running inside of a container.
    if (build_provider == "host" and not env_provider == "host"
            and not destructive_mode and not common.is_process_container()):
        raise click.BadArgumentUsage(
            "--provider=host requires --destructive-mode to acknowledge side effects"
        )

    if env_provider and env_provider != build_provider:
        raise click.BadArgumentUsage(
            "mismatch between --provider={} and SNAPCRAFT_BUILD_ENVIRONMENT={}"
            .format(build_provider, env_provider))

    # Error if any sys.argv params are for unsupported providers.
    # Values from environment variables and configuration files only
    # change defaults, so they are safe to ignore due to filtering
    # in get_build_provider_flags().
    for option in _PROVIDER_OPTIONS:
        key: str = option["param_decls"]  # type: ignore
        supported_providers: List[str] = option[
            "supported_providers"]  # type: ignore
        if key in sys.argv and build_provider not in supported_providers:
            raise click.BadArgumentUsage(
                f"{key} cannot be used with build provider {build_provider!r}")

    # Check if running as sudo.
    if os.getenv("SUDO_USER") and os.geteuid() == 0:
        if build_provider in ["lxd", "multipass"]:
            raise errors.SnapcraftEnvironmentError(
                f"'sudo' cannot be used with build provider {build_provider!r}"
            )

        if build_provider in ["host"]:
            click.echo(
                "Running with 'sudo' may cause permission errors and is discouraged. Use 'sudo' when cleaning."
            )