Example #1
0
    def fetch_stage_packages(
        cls,
        *,
        package_names: List[str],
        base: str,
        stage_packages_path: pathlib.Path,
        target_arch: str,
    ) -> List[str]:
        logger.debug(f"Requested stage-packages: {sorted(package_names)!r}")

        installed: Set[str] = set()

        package_list = [DebPackage.from_unparsed(name) for name in package_names]
        filtered_names = _get_filtered_stage_package_names(
            base=base, package_list=package_list
        )

        stage_packages_path.mkdir(exist_ok=True)
        with AptCache(
            stage_cache=_STAGE_CACHE_DIR, stage_cache_arch=target_arch
        ) as apt_cache:
            apt_cache.mark_packages(set(package_names))
            apt_cache.unmark_packages(filtered_names)
            for pkg_name, pkg_version, dl_path in apt_cache.fetch_archives(
                _DEB_CACHE_DIR
            ):
                logger.debug(f"Extracting stage package: {pkg_name}")
                installed.add(f"{pkg_name}={pkg_version}")
                file_utils.link_or_copy(
                    str(dl_path), str(stage_packages_path / dl_path.name)
                )

        return sorted(installed)
Example #2
0
    def setup_assets(self) -> None:
        # We do _setup_from_setup first since it is legacy and let the
        # declarative items take over.
        self._setup_gui()

        icon_path = self._finalize_icon()
        if icon_path is not None:
            if str(icon_path).startswith(self._prime_dir):
                icon_path = icon_path.relative_to(self._prime_dir)
            relative_icon_path: Optional[str] = str(icon_path)
        else:
            relative_icon_path = None

        snap_name = self._project_config.project.info.name
        for app_name, app in self._snap_meta.apps.items():
            app.write_command_wrappers(prime_dir=self._prime_dir)
            app.write_application_desktop_file(
                snap_name=snap_name,
                prime_dir=self._prime_dir,
                gui_dir=self.meta_gui_dir,
                icon_path=relative_icon_path,
            )
            app.validate_command_chain_executables(self._prime_dir)

        if self._config_data.get("type", "") == "gadget":
            if not os.path.exists("gadget.yaml"):
                raise errors.MissingGadgetError()
            file_utils.link_or_copy("gadget.yaml",
                                    os.path.join(self.meta_dir, "gadget.yaml"))
        if self._config_data.get("type", "") == "kernel":
            if os.path.exists("kernel.yaml"):
                file_utils.link_or_copy(
                    "kernel.yaml", os.path.join(self.meta_dir, "kernel.yaml"))
Example #3
0
    def _install_workspace_artifacts(self) -> None:
        """Install workspace artifacts."""
        # Find artifacts in release directory.
        release_dir = Path(self.builddir, "target", "release")

        # Install binaries to bin/.
        bins_dir = Path(self.installdir, "bin")
        bins_dir.mkdir(parents=True, exist_ok=True)

        # Install shared objects to usr/lib/<arch-triplet>.
        # TODO: Dynamic library support needs to be properly added.
        # Although we install libraries if we find them, they are most
        # likely going to be missing dependencies, e.g.:
        # /home/ubuntu/.cargo/toolchains/stable-x86_64-unknown-linux-gnu/lib/libstd-fae576517123aa4e.so
        libs_dir = Path(self.installdir, "usr", "lib",
                        self.project.arch_triplet)
        libs_dir.mkdir(parents=True, exist_ok=True)

        # Cargo build marks binaries and shared objects executable...
        # Search target directory to get these and install them to the
        # correct location (*.so to lib directory, otherwise bin directory).
        executables = [
            i for i in release_dir.iterdir()
            if os.path.isfile(i) and os.access(i, os.X_OK)
        ]
        for exe_path in executables:
            if exe_path.name.endswith(".so"):
                file_utils.link_or_copy(exe_path.as_posix(),
                                        libs_dir.as_posix())
            else:
                file_utils.link_or_copy(exe_path.as_posix(),
                                        bins_dir.as_posix())
Example #4
0
    def build(self):
        super().build()

        package_dir = self._install(rootdir=self.builddir)

        # Now move everything over to the plugin's installdir
        link_or_copy_tree(package_dir, self.installdir)
        # Copy in the node binary
        link_or_copy(
            os.path.join(self._npm_dir, "bin", "node"),
            os.path.join(self.installdir, "bin", "node"),
        )
        # Create binary entries
        package_json = self._get_package_json(rootdir=self.builddir)
        _create_bins(package_json, self.installdir)

        lock_file_path = os.path.join(self.installdir, "yarn.lock")
        if os.path.isfile(lock_file_path):
            with open(lock_file_path) as lock_file:
                self._manifest["yarn-lock-contents"] = lock_file.read()

        # Get the names and versions of installed packages
        if self.options.nodejs_package_manager == "npm":
            installed_node_packages = self._get_installed_node_packages(self.installdir)
            self._manifest["node-packages"] = [
                "{}={}".format(name, installed_node_packages[name])
                for name in installed_node_packages
            ]
        # Skip this step if yarn is used, as it may produce different
        # dependency trees than npm
        else:
            self._manifest["node-packages"] = []
Example #5
0
    def test_link_file_ioerror(self):
        orig_link = os.link

        def link_and_ioerror(a, b, **kwargs):
            orig_link(a, b)
            raise IOError()

        with mock.patch("os.link") as mock_link:
            mock_link.side_effect = link_and_ioerror
            file_utils.link_or_copy("1", "foo/1")
Example #6
0
    def _finalize_icon(self) -> Optional[pathlib.Path]:
        """Ensure sure icon is properly configured and installed.

        Fetch from a remote URL, if required, and place in the meta/gui
        directory.
        """

        # Nothing to do if no icon is configured, search for existing icon.
        icon: Optional[str] = self._config_data.get("icon")
        if icon is None and self._extracted_metadata is not None:
            icon = self._extracted_metadata.get_icon()

        if icon is None:
            return _find_icon_file()

        # Extracted appstream icon paths will either:
        # (1) point to a file relative to prime
        # (2) point to a remote http(s) url
        #
        # The 'icon' specified in the snapcraft.yaml has the same
        # constraint as (2) and would have already been validated
        # as existing by the schema.  So we can treat it the same
        # at this point, regardless of the source of the icon.
        parsed_url = urllib.parse.urlparse(icon)
        parsed_path = pathlib.Path(parsed_url.path)
        icon_ext = parsed_path.suffix[1:]
        target_icon_path = pathlib.Path(self.meta_gui_dir, f"icon.{icon_ext}")

        target_icon_path.parent.mkdir(parents=True, exist_ok=True)
        if parsed_url.scheme in ["http", "https"]:
            # Remote - fetch URL and write to target.
            logger.info(f"Fetching icon from {icon!r}.")
            icon_data = requests.get(icon).content
            target_icon_path.write_bytes(icon_data)
        elif parsed_url.scheme == "":
            source_path = pathlib.Path(
                self._prime_dir,
                parsed_path.relative_to("/")
                if parsed_path.is_absolute() else parsed_path,
            )
            if source_path.exists():
                # Local with path relative to prime.
                file_utils.link_or_copy(str(source_path),
                                        str(target_icon_path))
            elif parsed_path.exists():
                # Local with path relative to project.
                file_utils.link_or_copy(str(parsed_path),
                                        str(target_icon_path))
            else:
                # No icon found, fall back to searching for existing icon.
                return _find_icon_file()
        else:
            raise RuntimeError(f"Unexpected icon path: {parsed_url!r}")

        return target_icon_path
Example #7
0
    def common_decorator(*, snap_filename: str) -> None:
        snap_filename_common_path = (_get_review_tools_user_common() /
                                     pathlib.Path(snap_filename).name)
        # Create the directory tree for the cases where the review-tools have not run before.
        snap_filename_common_path.parent.mkdir(parents=True, exist_ok=True)
        file_utils.link_or_copy(snap_filename, str(snap_filename_common_path))

        try:
            method(snap_filename=snap_filename_common_path)
        finally:
            if snap_filename_common_path.exists():
                snap_filename_common_path.unlink()
Example #8
0
    def build(self):
        super().build()

        if self._using_gradlew():
            gradle_cmd = ["./gradlew"]
        else:
            self._gradle_tar.provision(self._gradle_dir, keep_zip=True)
            gradle_cmd = ["gradle"]
        self.run(
            gradle_cmd + self._get_proxy_options() +
            self.options.gradle_options + ["jar"],
            rootdir=self.builddir,
        )

        src = os.path.join(self.builddir, self.options.gradle_output_dir)
        jarfiles = glob(os.path.join(src, "*.jar"))
        warfiles = glob(os.path.join(src, "*.war"))

        if len(jarfiles) > 0:
            basedir = "jar"
        elif len(warfiles) > 0:
            basedir = "war"
            jarfiles = warfiles
        else:
            raise RuntimeError("Could not find any built jar files for part")

        file_utils.link_or_copy_tree(
            src,
            os.path.join(self.installdir, basedir),
            copy_function=lambda src, dst: file_utils.link_or_copy(
                src, dst, self.installdir),
        )

        self._create_symlinks()
Example #9
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 #10
0
def stage_runtime_dependencies(
    part_src: str,
    part_install: str,
    part_build: str,
    arch_triplet: str,
    content_dirs: str,
):
    build_path = os.path.join(part_build, "bin")
    install_path = os.path.join(part_install, "bin")

    if not os.path.exists(build_path):
        raise errors.SnapcraftEnvironmentError(
            "No binaries were built. Ensure the shards.yaml contains valid targets."
        )

    bin_paths = (os.path.join(build_path, b) for b in os.listdir(build_path))
    elf_files = (elf.ElfFile(path=b) for b in bin_paths if elf.ElfFile.is_elf(b))
    os.makedirs(install_path, exist_ok=True)

    # convert colon-delimited paths into a set
    if content_dirs == "":
        content_dirs_set = set()
    else:
        content_dirs_set = set(content_dirs.split(":"))

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

        elf_dependencies_path = elf_file.load_dependencies(
            root_path=part_install,
            core_base_path=common.get_installed_snap_path("core20"),
            arch_triplet=arch_triplet,
            content_dirs=content_dirs_set,
        )

        for elf_dependency_path in elf_dependencies_path:
            lib_install_path = os.path.join(part_install, 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 #11
0
    def build(self):
        super().build()

        self._maven_tar.provision(self._maven_dir,
                                  clean_target=False,
                                  keep_tarball=True)

        mvn_cmd = ["mvn", "package"]
        if self._use_proxy():
            settings_path = os.path.join(self.partdir, "m2", "settings.xml")
            _create_settings(settings_path)
            mvn_cmd += ["-s", settings_path]

        self.run(mvn_cmd + self.options.maven_options, rootdir=self.builddir)

        for f in self.options.maven_targets:
            src = os.path.join(self.builddir, f, "target")
            jarfiles = glob(os.path.join(src, "*.jar"))
            warfiles = glob(os.path.join(src, "*.war"))
            arfiles = glob(os.path.join(src, "*.[jw]ar"))

            if len(arfiles) == 0:
                raise EmptyBuildTargetDirectoryError(src)
            if len(jarfiles) > 0 and len(f) == 0:
                basedir = "jar"
            elif len(warfiles) > 0 and len(f) == 0:
                basedir = "war"
            else:
                basedir = f

            targetdir = os.path.join(self.installdir, basedir)
            os.makedirs(targetdir, exist_ok=True)
            for src in arfiles:
                # Make rebuilding easier when enabled
                base = os.path.basename(src)
                dst = os.path.join(targetdir, base)
                if os.path.exists(dst):
                    os.unlink(dst)
                file_utils.link_or_copy(src, dst)

        self._create_symlinks()
Example #12
0
    def write_snap_directory(self) -> None:
        """Record manifest and copy assets found under $SNAPCRAFT_PROJECT_ROOT/snap.

        These assets have priority over any code generated assets and include:
        - hooks
        - gui
        """
        snap_assets_dir = self._project_config.project._get_snapcraft_assets_dir(
        )
        prime_snap_dir = os.path.join(self._prime_dir, "snap")

        snap_dir_iter = itertools.product([prime_snap_dir], ["hooks"])
        meta_dir_iter = itertools.product([self.meta_dir], ["hooks", "gui"])

        for origin in itertools.chain(snap_dir_iter, meta_dir_iter):
            src_dir = os.path.join(snap_assets_dir, origin[1])
            dst_dir = os.path.join(origin[0], origin[1])
            if os.path.isdir(src_dir):
                os.makedirs(dst_dir, exist_ok=True)
                for asset in os.listdir(src_dir):
                    source = os.path.join(src_dir, asset)
                    destination = os.path.join(dst_dir, asset)

                    with contextlib.suppress(FileNotFoundError):
                        os.remove(destination)

                    file_utils.link_or_copy(source,
                                            destination,
                                            follow_symlinks=True)

                    # Ensure that the hook is executable in meta/hooks, this is a moot
                    # point considering the prior link_or_copy call, but is technically
                    # correct and allows for this operation to take place only once.
                    if origin[0] == self.meta_dir and origin[1] == "hooks":
                        _prepare_hook(destination)

        self._record_manifest_and_source_snapcraft_yaml()
Example #13
0
 def test_copy_nested_file(self):
     file_utils.link_or_copy("foo/bar/baz/4", "foo2/bar/baz/4")
     self.assertTrue(os.path.isfile("foo2/bar/baz/4"))
Example #14
0
    def wheel(
        self,
        packages,
        *,
        setup_py_dir: Optional[str] = None,
        constraints: Optional[Set[str]] = None,
        requirements: Optional[Sequence[str]] = None,
        process_dependency_links: bool = False,
    ):
        """Build wheels of packages in the cache.

        The packages should have already been downloaded via `download()`.

        :param iterable packages: Packages in cache for which to build wheels.
        :param str setup_py_dir: Directory containing setup.py.
        :param iterable constraints: Collection of paths to constraints files.
        :param iterable requirements: Collection of paths to requirements
                                      files.
        :param boolean process_dependency_links: Enable the processing of
                                                 dependency links.

        :return: List of paths to each wheel that was built.
        :rtype: list
        """
        package_args = _process_package_args(packages=packages,
                                             requirements=requirements,
                                             setup_py_dir=setup_py_dir)

        if not package_args:
            return []  # No operation was requested

        args = _process_common_args(
            process_dependency_links=process_dependency_links,
            constraints=constraints)

        wheels = []  # type: List[str]
        with tempfile.TemporaryDirectory() as temp_dir:

            # Using pip with a few special parameters:
            #
            # --no-index: Don't hit pypi, assume the packages are already
            #             downloaded (i.e. by using `self.download()`)
            # --find-links: Provide the directory into which the packages
            #               should have already been fetched
            # --wheel-dir: Build wheels into a temporary working area rather
            #              rather than cwd. We'll copy them over. FIXME: We can
            #              probably get away just building them in the package
            #              dir. Try that once this refactor has been validated.
            self._run(
                [
                    "wheel",
                    "--no-index",
                    "--find-links",
                    self._python_package_dir,
                    "--wheel-dir",
                    temp_dir,
                ] + args + package_args,
                cwd=setup_py_dir,
            )
            wheels = os.listdir(temp_dir)
            for wheel in wheels:
                file_utils.link_or_copy(
                    os.path.join(temp_dir, wheel),
                    os.path.join(self._python_package_dir, wheel),
                )

        return [
            os.path.join(self._python_package_dir, wheel) for wheel in wheels
        ]