def write_snap_directory(self): # First migrate the snap directory. It will overwrite any conflicting # files. for root, directories, files in os.walk('snap'): for directory in directories: source = os.path.join(root, directory) destination = os.path.join(self._snap_dir, source) file_utils.create_similar_directory(source, destination) for file_path in files: source = os.path.join(root, file_path) destination = os.path.join(self._snap_dir, source) with contextlib.suppress(FileNotFoundError): os.remove(destination) file_utils.link_or_copy(source, destination) # Now copy the hooks contained within the snap directory directly into # meta (they don't get wrappers like the ones that come from parts). snap_hooks_dir = os.path.join('snap', 'hooks') hooks_dir = os.path.join(self._snap_dir, 'meta', 'hooks') if os.path.isdir(snap_hooks_dir): os.makedirs(hooks_dir, exist_ok=True) for hook_name in os.listdir(snap_hooks_dir): source = os.path.join(snap_hooks_dir, hook_name) destination = os.path.join(hooks_dir, hook_name) # First, verify that the hook is actually executable if not os.stat(source).st_mode & stat.S_IEXEC: raise CommandError('hook {!r} is not executable'.format( hook_name)) with contextlib.suppress(FileNotFoundError): os.remove(destination) file_utils.link_or_copy(source, destination)
def _get(self, apt_cache): # Ideally we'd use apt.Cache().fetch_archives() here, but it seems to # mangle some package names on disk such that we can't match it up to # the archive later. We could get around this a few different ways: # # 1. Store each stage package in the cache named by a hash instead of # its name from the archive. # 2. Download packages in a different manner. # # In the end, (2) was chosen for minimal overhead and a simpler cache # implementation. So we're using fetch_binary() here instead. # Downloading each package individually has the drawback of witholding # any clue of how long the whole pulling process will take, but that's # something we'll have to live with. pkg_list = [] for package in apt_cache.get_changes(): pkg_list.append(str(package.candidate)) source = package.candidate.fetch_binary( self._cache.packages_dir, progress=self._apt.progress) destination = os.path.join( self._downloaddir, os.path.basename(source)) with contextlib.suppress(FileNotFoundError): os.remove(destination) file_utils.link_or_copy(source, destination) return pkg_list
def _migrate_files(snap_files, snap_dirs, srcdir, dstdir, missing_ok=False, follow_symlinks=False, fixup_func=lambda *args: None): for directory in snap_dirs: src = os.path.join(srcdir, directory) dst = os.path.join(dstdir, directory) snapcraft.file_utils.create_similar_directory(src, dst) for snap_file in snap_files: src = os.path.join(srcdir, snap_file) dst = os.path.join(dstdir, snap_file) snapcraft.file_utils.create_similar_directory(os.path.dirname(src), os.path.dirname(dst)) if missing_ok and not os.path.exists(src): continue # If the file is already here and it's a symlink, leave it alone. if os.path.islink(dst): continue # Otherwise, remove and re-link it. if os.path.exists(dst): os.remove(dst) if src.endswith('.pc'): shutil.copy2(src, dst, follow_symlinks=follow_symlinks) else: file_utils.link_or_copy(src, dst, follow_symlinks=follow_symlinks) fixup_func(dst)
def _restore_cached_packages(self, apt_changes, package_cache_dir, download_dir): for pkg in apt_changes: src = os.path.join(package_cache_dir, pkg.name) dst = os.path.join(download_dir, pkg.name) if os.path.exists(src): file_utils.link_or_copy(src, dst)
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')
def _ensure_snapcraft_yaml(self): source = project_loader.get_snapcraft_yaml() destination = os.path.join(self._snap_dir, 'snap', 'snapcraft.yaml') with contextlib.suppress(FileNotFoundError): os.remove(destination) os.makedirs(os.path.dirname(destination), exist_ok=True) file_utils.link_or_copy(source, destination)
def wheel(self, packages, *, setup_py_dir=None, constraints=None, requirements=None, process_dependency_links=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 """ args = _process_common_args( process_dependency_links=process_dependency_links, packages=packages, constraints=constraints, requirements=requirements) cwd = None if setup_py_dir: args.append('.') cwd = setup_py_dir if not args: return # No operation was requested wheels = [] 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, cwd=cwd) 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]
def _store_cached_packages(self, package_cache_dir, download_dir): os.makedirs(package_cache_dir, exist_ok=True) for pkg in os.listdir(download_dir): if not pkg.endswith('.deb'): continue src = os.path.join(download_dir, pkg) dst = os.path.join(package_cache_dir, pkg) # The dst may be an incomplete or broken so let's update # just in case. if os.path.exists(dst): os.unlink(dst) file_utils.link_or_copy(src, dst)
def cache(self, snap_filename, revision): """Cache snap revision in XDG cache. :returns: path to cached revision. """ cached_snap = _rewrite_snap_filename_with_revision( snap_filename, revision) cached_snap_path = os.path.join(self.snap_cache_dir, cached_snap) try: link_or_copy(snap_filename, cached_snap_path) except OSError: logger.warning( 'Unable to cache snap {}.'.format(cached_snap)) return cached_snap_path
def write_snap_directory(self) -> None: # First migrate the snap directory. It will overwrite any conflicting # files. for root, directories, files in os.walk("snap"): with contextlib.suppress(ValueError): directories.remove(".snapcraft") with contextlib.suppress(ValueError): # The snapcraft.yaml is migrated later files.remove("snapcraft.yaml") for directory in directories: source = os.path.join(root, directory) destination = os.path.join(self._prime_dir, source) file_utils.create_similar_directory(source, destination) for file_path in files: source = os.path.join(root, file_path) destination = os.path.join(self._prime_dir, source) with contextlib.suppress(FileNotFoundError): os.remove(destination) file_utils.link_or_copy(source, destination) # Now copy the assets contained within the snap directory directly into # meta. for origin in ["gui", "hooks"]: src_dir = os.path.join("snap", origin) dst_dir = os.path.join(self.meta_dir, origin) 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) # First, verify that the hook is actually executable if origin == "hooks": _validate_hook(source) with contextlib.suppress(FileNotFoundError): os.remove(destination) file_utils.link_or_copy(source, destination) self._record_manifest_and_source_snapcraft_yaml()
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 RuntimeError( "Could not find any built jar files for part") 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()
def _migrate_files( snap_files, snap_dirs, srcdir, dstdir, missing_ok=False, follow_symlinks=False, fixup_func=lambda *args: None, ): for directory in snap_dirs: src = os.path.join(srcdir, directory) dst = os.path.join(dstdir, directory) snapcraft.file_utils.create_similar_directory(src, dst) for snap_file in snap_files: src = os.path.join(srcdir, snap_file) dst = os.path.join(dstdir, snap_file) snapcraft.file_utils.create_similar_directory( os.path.dirname(src), os.path.dirname(dst) ) if missing_ok and not os.path.exists(src): continue # If the file is already here and it's a symlink, leave it alone. if os.path.islink(dst): continue # Otherwise, remove and re-link it. if os.path.exists(dst): os.remove(dst) if src.endswith(".pc"): shutil.copy2(src, dst, follow_symlinks=follow_symlinks) else: file_utils.link_or_copy(src, dst, follow_symlinks=follow_symlinks) fixup_func(dst)
def write_snap_directory(self): # First migrate the snap directory. It will overwrite any conflicting # files. for root, directories, files in os.walk('snap'): for directory in directories: source = os.path.join(root, directory) destination = os.path.join(self._snap_dir, source) file_utils.create_similar_directory(source, destination) for file_path in files: source = os.path.join(root, file_path) destination = os.path.join(self._snap_dir, source) with contextlib.suppress(FileNotFoundError): os.remove(destination) file_utils.link_or_copy(source, destination) # Now copy the assets contained within the snap directory directly into # meta. for origin in ['gui', 'hooks']: src_dir = os.path.join('snap', origin) dst_dir = os.path.join(self.meta_dir, origin) 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) # First, verify that the hook is actually executable if origin == 'hooks': _validate_hook(source) with contextlib.suppress(FileNotFoundError): os.remove(destination) file_utils.link_or_copy(source, destination) # FIXME hide this functionality behind a feature flag for now if os.environ.get('SNAPCRAFT_BUILD_INFO'): self._ensure_snapcraft_yaml() else: self._ensure_no_build_artifacts()
def build(self): super().build() self.run(["shards", "install", "--production"], self.builddir) self.run(["shards", "build", "--production"], 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_core_path( self.project.info.get_build_base()), ) 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)
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() if "icon" in self._config_data: # TODO: use developer.ubuntu.com once it has updated documentation. icon_ext = self._config_data["icon"].split(os.path.extsep)[-1] icon_dir = os.path.join(self.meta_dir, "gui") icon_path = os.path.join(icon_dir, "icon.{}".format(icon_ext)) if not os.path.exists(icon_dir): os.mkdir(icon_dir) if os.path.exists(icon_path): os.unlink(icon_path) file_utils.link_or_copy(self._config_data["icon"], icon_path) 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"))
def fetch_stage_packages(cls, *, package_names: List[str], base: str, stage_packages_path: pathlib.Path) -> List[str]: logger.debug(f"Requested stage-packages: {sorted(package_names)!r}") installed: Set[str] = set() stage_packages_path.mkdir(exist_ok=True) with AptCache(stage_cache=_STAGE_CACHE_DIR) as apt_cache: filter_packages = set(get_packages_in_base(base=base)) apt_cache.update() apt_cache.mark_packages(set(package_names)) apt_cache.unmark_packages(required_names=set(package_names), filtered_names=filter_packages) 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)
def setup_assets(self): # We do _setup_from_setup first since it is legacy and let the # declarative items take over. self._setup_gui() if 'icon' in self._config_data: # TODO: use developer.ubuntu.com once it has updated documentation. icon_ext = self._config_data['icon'].split(os.path.extsep)[-1] icon_dir = os.path.join(self.meta_dir, 'gui') icon_path = os.path.join(icon_dir, 'icon.{}'.format(icon_ext)) if not os.path.exists(icon_dir): os.mkdir(icon_dir) if os.path.exists(icon_path): os.unlink(icon_path) os.link(self._config_data['icon'], icon_path) 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'))
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() if 'icon' in self._config_data: # TODO: use developer.ubuntu.com once it has updated documentation. icon_ext = self._config_data['icon'].split(os.path.extsep)[-1] icon_dir = os.path.join(self.meta_dir, 'gui') icon_path = os.path.join(icon_dir, 'icon.{}'.format(icon_ext)) if not os.path.exists(icon_dir): os.mkdir(icon_dir) if os.path.exists(icon_path): os.unlink(icon_path) os.link(self._config_data['icon'], icon_path) 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'))
def wheel(self, args, **kwargs): cmd = [ *self._runnable, 'wheel', '--disable-pip-version-check', '--no-index', '--find-links', self._package_dir, ] cmd.extend(self._extra_pip_args) os.makedirs(self._package_dir, exist_ok=True) wheels = [] with tempfile.TemporaryDirectory() as temp_dir: cmd.extend(['--wheel-dir', temp_dir]) cmd.extend(args) self._exec_func(cmd, env=self._env, **kwargs) wheels = os.listdir(temp_dir) for wheel in wheels: file_utils.link_or_copy( os.path.join(temp_dir, wheel), os.path.join(self._package_dir, wheel)) return [os.path.join(self._package_dir, wheel) for wheel in wheels]
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() if "icon" in self._config_data: # TODO: use developer.ubuntu.com once it has updated documentation. icon_ext = self._config_data["icon"].split(os.path.extsep)[-1] icon_dir = os.path.join(self.meta_dir, "gui") icon_path = os.path.join(icon_dir, "icon.{}".format(icon_ext)) if not os.path.exists(icon_dir): os.mkdir(icon_dir) if os.path.exists(icon_path): os.unlink(icon_path) os.link(self._config_data["icon"], icon_path) 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") )
def write_snap_directory(self): # First migrate the snap directory. It will overwrite any conflicting # files. for root, directories, files in os.walk('snap'): if '.snapcraft' in directories: directories.remove('.snapcraft') for directory in directories: source = os.path.join(root, directory) destination = os.path.join(self._prime_dir, source) file_utils.create_similar_directory(source, destination) for file_path in files: source = os.path.join(root, file_path) destination = os.path.join(self._prime_dir, source) with contextlib.suppress(FileNotFoundError): os.remove(destination) file_utils.link_or_copy(source, destination) # Now copy the assets contained within the snap directory directly into # meta. for origin in ['gui', 'hooks']: src_dir = os.path.join('snap', origin) dst_dir = os.path.join(self.meta_dir, origin) 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) # First, verify that the hook is actually executable if origin == 'hooks': _validate_hook(source) with contextlib.suppress(FileNotFoundError): os.remove(destination) file_utils.link_or_copy(source, destination) self._record_snapcraft()
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() # Extracted metadata (e.g. from the AppStream) can override the # icon location. if self._extracted_metadata: icon_path = self._extracted_metadata.get_icon() else: 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=icon_path, ) app.validate_command_chain_executables(self._prime_dir) if "icon" in self._config_data: # TODO: use developer.ubuntu.com once it has updated documentation. icon_ext = self._config_data["icon"].split(os.path.extsep)[-1] icon_path = os.path.join(self.meta_gui_dir, "icon.{}".format(icon_ext)) os.makedirs(self.meta_gui_dir, exist_ok=True) if os.path.exists(icon_path): os.unlink(icon_path) file_utils.link_or_copy(self._config_data["icon"], icon_path) 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"))
def setup_assets(self): if 'icon' in self._config_data: # TODO: use developer.ubuntu.com once it has updated documentation. logger.warning( "DEPRECATED: 'icon' defined in snapcraft.yaml. Look at " "https://github.com/snapcore/snapcraft/blob/master/docs/" "metadata.md#snap-icon for more information") icon_ext = self._config_data['icon'].split(os.path.extsep)[1] icon_dir = os.path.join(self.meta_dir, 'gui') icon_path = os.path.join(icon_dir, 'icon.{}'.format(icon_ext)) if not os.path.exists(icon_dir): os.mkdir(icon_dir) if os.path.exists(icon_path): os.unlink(icon_path) os.link(self._config_data['icon'], icon_path) self._setup_from_setup() if self._config_data.get('type', '') == 'gadget': if not os.path.exists('gadget.yaml'): raise MissingGadgetError() file_utils.link_or_copy( 'gadget.yaml', os.path.join(self.meta_dir, 'gadget.yaml'))
def setup_assets(self): if 'icon' in self._config_data: # TODO: use developer.ubuntu.com once it has updated documentation. logger.warning( "DEPRECATED: 'icon' defined in snapcraft.yaml. Look at " "https://github.com/snapcore/snapcraft/blob/master/docs/" "metadata.md#snap-icon for more information") icon_ext = self._config_data['icon'].split(os.path.extsep)[1] icon_dir = os.path.join(self.meta_dir, 'gui') icon_path = os.path.join(icon_dir, 'icon.{}'.format(icon_ext)) if not os.path.exists(icon_dir): os.mkdir(icon_dir) if os.path.exists(icon_path): os.unlink(icon_path) os.link(self._config_data['icon'], icon_path) self._setup_from_setup() if self._config_data.get('type', '') == 'gadget': if not os.path.exists('gadget.yaml'): raise MissingGadgetError() file_utils.link_or_copy('gadget.yaml', os.path.join(self.meta_dir, 'gadget.yaml'))
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()
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"] if self.options.gradle_build_jar else []), rootdir=self.builddir, ) src = os.path.join(self.builddir, self.options.gradle_output_dir) basedir = "java" if self.options.gradle_build_jar: 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()
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"))
def wheel(self, packages, *, setup_py_dir=None, constraints=None, requirements=None, process_dependency_links=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 """ args = _process_common_args( process_dependency_links=process_dependency_links, packages=packages, constraints=constraints, requirements=requirements) cwd = None if setup_py_dir: args.append('.') cwd = setup_py_dir if not args: return # No operation was requested wheels = [] 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, cwd=cwd) 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 ]
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'))
def handle_glibc_mismatch(*, elf_files: FrozenSet[elf.ElfFile], root_path: str, core_base_path: str, snap_base_path: str, preferred_patchelf_path=None) -> None: """Copy over libc6 libraries from the host and patch necessary elf files. If no newer glibc version is detected in elf_files, this function returns. :param snapcraft.internal.elf.ElfFile elf_files: set of candidate elf files to patch if a newer libc6 is required. :param str root_path: the root path of a snap tree. :param str core_base_path: the path to the base snap. :param str snap_base_path: absolute path to the snap once installed to setup proper rpaths. :param str preferred_patchelf_path: patch the necessary elf_files with this patchelf. """ formatted_list = list() # type: List[str] patch_elf_files = list() # type: List[elf.ElfFile] for elf_file in elf_files: required_glibc = elf_file.get_required_glibc() if not required_glibc: continue # We need to verify now that the GLIBC version would be compatible # with that of the base. # TODO the linker version depends on the chosen base, but that # base may not be installed so we cannot depend on # get_core_dynamic_linker to resolve the final path for which # we resort to our only working base 16, ld-2.23.so. if not elf_file.is_linker_compatible(linker='ld-2.23.so'): formatted_list.append('- {} -> GLIBC {}'.format( elf_file.path, required_glibc)) patch_elf_files.append(elf_file) if not patch_elf_files: return logger.warning('The primed files will not work with the current ' 'base given the GLIBC mismatch of the primed ' 'files and the linker version (2.23) used in the ' 'base. These are the GLIBC versions required by ' 'the primed files that do not match and will be ' 'patched:\n {}\n' 'To work around this, the newer libc will be ' 'migrated into the snap, and these files will be ' 'patched to use it.'.format('\n'.join(formatted_list))) # We assume the current system will satisfy the GLIBC requirement, # get the current libc6 libraries (which includes the linker) libc6_libraries = repo.Repo.get_package_libraries('libc6') libc6_path = os.path.join('snap', 'libc6') # Before doing anything else, verify there's a dynamic linker we can use. dynamic_linker_path = os.path.join(snap_base_path, libc6_path, _get_dynamic_linker(libc6_libraries)) dest_dir = os.path.join(root_path, libc6_path) os.makedirs(dest_dir, exist_ok=True) for src in libc6_libraries: dst = os.path.join(dest_dir, os.path.basename(src)) # follow_symlinks is set to True for elf crawling to work. file_utils.link_or_copy(src, dst, follow_symlinks=True) elf_patcher = elf.Patcher(dynamic_linker=dynamic_linker_path, root_path=root_path, preferred_patchelf_path=preferred_patchelf_path) for elf_file in patch_elf_files: # Search for dependencies again now that the new libc6 is # migrated. elf_file.load_dependencies(root_path=root_path, core_base_path=core_base_path) elf_patcher.patch(elf_file=elf_file)