def test_architecture_options(self, mock_platform_machine, mock_platform_architecture): mock_platform_machine.return_value = self.machine mock_platform_architecture.return_value = self.architecture options = snapcraft.ProjectOptions() self.assertThat(options.arch_triplet, Equals(self.expected_arch_triplet)) self.assertThat(options.deb_arch, Equals(self.expected_deb_arch)) self.assertThat(options.kernel_arch, Equals(self.expected_kernel_arch)) # The core dynamic linker is correct. Guard against stray absolute # paths, as they cause os.path.join to discard the previous # argument. self.assertFalse(os.path.isabs(self.expected_core_dynamic_linker)) with mock.patch("os.path.lexists") as mock_lexists: mock_lexists.return_value = True with mock.patch("os.path.islink") as mock_islink: mock_islink.return_value = False self.assertThat( options.get_core_dynamic_linker("core"), Equals( os.path.join( common.get_core_path("core"), self.expected_core_dynamic_linker, )), )
def _setup_core(deb_arch): core_path = common.get_core_path() if os.path.exists(core_path) and os.listdir(core_path): logger.debug( '{!r} already exists, skipping core setup'.format(core_path)) return snap_cache = SnapCache(project_name='snapcraft-core') # Try to get the latest revision. core_snap = snap_cache.get(deb_arch=deb_arch) if core_snap: # The current hash matches the filename current_hash = os.path.splitext(os.path.basename(core_snap))[0] else: current_hash = '' with TemporaryDirectory() as d: download_path = os.path.join(d, 'core.snap') download_hash = snapcraft.download('core', 'stable', download_path, deb_arch, except_hash=current_hash) if download_hash != current_hash: snap_cache.cache(snap_filename=download_path) snap_cache.prune(deb_arch=deb_arch, keep_hash=download_hash) core_snap = snap_cache.get(deb_arch=deb_arch) # Now unpack logger.info('Setting up {!r} in {!r}'.format(core_snap, core_path)) if os.path.exists(core_path) and not os.listdir(core_path): check_call(['sudo', 'rmdir', core_path]) check_call(['sudo', 'mkdir', '-p', os.path.dirname(core_path)]) check_call(['sudo', 'unsquashfs', '-d', core_path, core_snap])
def test_architecture_options( self, mock_platform_machine, mock_platform_architecture ): mock_platform_machine.return_value = self.machine mock_platform_architecture.return_value = self.architecture options = snapcraft.ProjectOptions() self.assertThat(options.arch_triplet, Equals(self.expected_arch_triplet)) self.assertThat(options.deb_arch, Equals(self.expected_deb_arch)) self.assertThat(options.kernel_arch, Equals(self.expected_kernel_arch)) # The core dynamic linker is correct. Guard against stray absolute # paths, as they cause os.path.join to discard the previous # argument. self.assertFalse(os.path.isabs(self.expected_core_dynamic_linker)) with mock.patch("os.path.lexists") as mock_lexists: mock_lexists.return_value = True with mock.patch("os.path.islink") as mock_islink: mock_islink.return_value = False self.assertThat( options.get_core_dynamic_linker("core"), Equals( os.path.join( common.get_core_path("core"), self.expected_core_dynamic_linker, ) ), )
def get_core_dynamic_linker(self): """Returns the dynamic linker used for the targetted 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 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)
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 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)
def _setup_core(deb_arch): core_path = common.get_core_path() if os.path.exists(core_path) and os.listdir(core_path): logger.debug('{!r} already exists, skipping core setup'.format( core_path)) return snap_cache = SnapCache(project_name='snapcraft-core') # Try to get the latest revision. core_snap = snap_cache.get(deb_arch=deb_arch) if core_snap: # The current hash matches the filename current_hash = os.path.splitext(os.path.basename(core_snap))[0] else: current_hash = '' with TemporaryDirectory() as d: download_path = os.path.join(d, 'core.snap') download_hash = snapcraft.download('core', 'stable', download_path, deb_arch, except_hash=current_hash) if download_hash != current_hash: snap_cache.cache(snap_filename=download_path) snap_cache.prune(deb_arch=deb_arch, keep_hash=download_hash) core_snap = snap_cache.get(deb_arch=deb_arch) # Now unpack logger.info('Setting up {!r} in {!r}'.format(core_snap, core_path)) if os.path.exists(core_path) and not os.listdir(core_path): check_call(['sudo', 'rmdir', core_path]) check_call(['sudo', 'mkdir', '-p', os.path.dirname(core_path)]) check_call(['sudo', 'unsquashfs', '-d', core_path, core_snap])
def prime(self, force=False) -> None: self.makedirs() self.notify_part_progress('Priming') snap_files, snap_dirs = self.migratable_fileset_for('prime') _migrate_files(snap_files, snap_dirs, self.stagedir, self.primedir) elf_files = elf.get_elf_files(self.primedir, snap_files) all_dependencies = set() # TODO: base snap support core_path = common.get_core_path() for elf_file in elf_files: all_dependencies.update( elf_file.load_dependencies(root_path=self.primedir, core_base_path=core_path)) # Split the necessary dependencies into their corresponding location. # We'll both migrate and track the system dependencies, but we'll only # track the part and staged dependencies, since they should have # already been primed by other means, and migrating them again could # potentially override the `stage` or `snap` filtering. (in_part, staged, primed, system) = _split_dependencies( all_dependencies, self.installdir, self.stagedir, self.primedir) part_dependency_paths = {os.path.dirname(d) for d in in_part} staged_dependency_paths = {os.path.dirname(d) for d in staged} dependency_paths = part_dependency_paths | staged_dependency_paths if not self._build_attributes.no_system_libraries(): system_dependency_paths = {os.path.dirname(d) for d in system} dependency_paths.update(system_dependency_paths) if system: # Lots of dependencies are linked with a symlink, so we need to # make sure we follow those symlinks when we migrate the # dependencies. _migrate_files(system, system_dependency_paths, '/', self.primedir, follow_symlinks=True) formatted_system = '\n'.join(sorted(system)) logger.warning( 'Files from the build host were migrated into the snap to ' 'satisfy dependencies that would otherwise not be met. ' 'This feature will be removed in a future release. If ' 'these libraries are needed in the final snap, ensure ' 'that the following are either satisfied by a ' 'stage-packages entry or through a part:\n{}'.format( formatted_system)) if self._confinement == 'classic': dynamic_linker = self._project_options.get_core_dynamic_linker() elf_patcher = elf.Patcher(dynamic_linker=dynamic_linker, root_path=self.primedir) for elf_file in elf_files: elf_patcher.patch(elf_file=elf_file) self.mark_prime_done(snap_files, snap_dirs, dependency_paths)
def env_for_classic(arch_triplet: str) -> List[str]: """Set the required environment variables for a classic confined build.""" env = [] core_path = common.get_core_path() paths = common.get_library_paths(core_path, arch_triplet, existing_only=False) env.append(formatting_utils.format_path_variable( 'LD_LIBRARY_PATH', paths, prepend='', separator=':')) return env
def env_for_classic(base: str, arch_triplet: str) -> List[str]: """Set the required environment variables for a classic confined build.""" env = [] core_path = common.get_core_path(base) paths = common.get_library_paths(core_path, arch_triplet, existing_only=False) env.append( formatting_utils.format_path_variable( "LD_LIBRARY_PATH", paths, prepend="", separator=":" ) ) return env
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 EnvironmentError( '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
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_core_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 )
def _handle_elf(self, snap_files: Sequence[str]) -> Set[str]: elf_files = elf.get_elf_files(self.primedir, snap_files) all_dependencies = set() # TODO: base snap support core_path = common.get_core_path(self._base) # Clear the cache of all libs that aren't already in the primedir self._soname_cache.reset_except_root(self.primedir) for elf_file in elf_files: all_dependencies.update( elf_file.load_dependencies( root_path=self.primedir, core_base_path=core_path, soname_cache=self._soname_cache, ) ) dependency_paths = self._handle_dependencies(all_dependencies) if not self._build_attributes.keep_execstack(): clear_execstack(elf_files=elf_files) if self._build_attributes.no_patchelf(): logger.warning( "The primed files for part {!r} will not be verified for " "correctness or patched: build-attributes: [no-patchelf] " "is set.".format(self.name) ) else: part_patcher = PartPatcher( elf_files=elf_files, plugin=self.plugin, project=self._project_options, confinement=self._confinement, core_base=self._base, snap_base_path=self._snap_base_path, stagedir=self.stagedir, primedir=self.primedir, stage_packages=self._part_properties.get("stage-packages", []), ) part_patcher.patch() return dependency_paths
def _handle_elf(self, snap_files: Sequence[str]) -> Set[str]: elf_files = elf.get_elf_files(self.primedir, snap_files) all_dependencies = set() core_path = common.get_core_path(self._base) # Clear the cache of all libs that aren't already in the primedir self._soname_cache.reset_except_root(self.primedir) for elf_file in elf_files: all_dependencies.update( elf_file.load_dependencies( root_path=self.primedir, core_base_path=core_path, soname_cache=self._soname_cache, ) ) dependency_paths = self._handle_dependencies(all_dependencies) if not self._build_attributes.keep_execstack(): clear_execstack(elf_files=elf_files) if self._build_attributes.no_patchelf(): logger.warning( "The primed files for part {!r} will not be verified for " "correctness or patched: build-attributes: [no-patchelf] " "is set.".format(self.name) ) else: part_patcher = PartPatcher( elf_files=elf_files, plugin=self.plugin, project=self._project_options, confinement=self._confinement, core_base=self._base, snap_base_path=self._snap_base_path, stagedir=self.stagedir, primedir=self.primedir, stage_packages=self._part_properties.get("stage-packages", []), ) part_patcher.patch() return dependency_paths
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_core(deb_arch, base): core_path = common.get_core_path(base) if os.path.exists(core_path) and os.listdir(core_path): logger.debug( "{!r} already exists, skipping core setup".format(core_path)) return # for backwards compatibility if base == "core": snap_cache = SnapCache(project_name="snapcraft-core") else: snap_cache = SnapCache(project_name=base) # Try to get the latest revision. core_snap = snap_cache.get(deb_arch=deb_arch) if core_snap: # The current hash matches the filename current_hash = os.path.splitext(os.path.basename(core_snap))[0] else: current_hash = "" with TemporaryDirectory() as d: download_path = os.path.join(d, "{}.snap".format(base)) download_hash = snapcraft.download(base, "stable", download_path, deb_arch, except_hash=current_hash) if download_hash != current_hash: snap_cache.cache(snap_filename=download_path) snap_cache.prune(deb_arch=deb_arch, keep_hash=download_hash) core_snap = snap_cache.get(deb_arch=deb_arch) # Now unpack logger.info("Setting up {!r} in {!r}".format(core_snap, core_path)) if os.path.exists(core_path) and not os.listdir(core_path): check_call(["sudo", "rmdir", core_path]) check_call(["sudo", "mkdir", "-p", os.path.dirname(core_path)]) unsquashfs_path = snapcraft.file_utils.get_tool_path("unsquashfs") check_call(["sudo", unsquashfs_path, "-d", core_path, core_snap])
def _setup_core(deb_arch, base): core_path = common.get_core_path(base) if os.path.exists(core_path) and os.listdir(core_path): logger.debug("{!r} already exists, skipping core setup".format(core_path)) return # for backwards compatibility if base == "core": snap_cache = SnapCache(project_name="snapcraft-core") else: snap_cache = SnapCache(project_name=base) # Try to get the latest revision. core_snap = snap_cache.get(deb_arch=deb_arch) if core_snap: # The current hash matches the filename current_hash = os.path.splitext(os.path.basename(core_snap))[0] else: current_hash = "" with TemporaryDirectory() as d: download_path = os.path.join(d, "{}.snap".format(base)) download_hash = snapcraft.download( base, "stable", download_path, deb_arch, except_hash=current_hash ) if download_hash != current_hash: snap_cache.cache(snap_filename=download_path) snap_cache.prune(deb_arch=deb_arch, keep_hash=download_hash) core_snap = snap_cache.get(deb_arch=deb_arch) # Now unpack logger.info("Setting up {!r} in {!r}".format(core_snap, core_path)) if os.path.exists(core_path) and not os.listdir(core_path): check_call(["sudo", "rmdir", core_path]) check_call(["sudo", "mkdir", "-p", os.path.dirname(core_path)]) unsquashfs_path = snapcraft.file_utils.get_tool_path("unsquashfs") check_call(["sudo", unsquashfs_path, "-d", core_path, core_snap])
def prime(self, force=False) -> None: self.makedirs() self.notify_part_progress('Priming') snap_files, snap_dirs = self.migratable_fileset_for('prime') _migrate_files(snap_files, snap_dirs, self.stagedir, self.primedir) elf_files = elf.get_elf_files(self.primedir, snap_files) all_dependencies = set() for elf_file in elf_files: all_dependencies.update(elf_file.load_dependencies()) # Split the necessary dependencies into their corresponding location. # We'll both migrate and track the system dependencies, but we'll only # track the part and staged dependencies, since they should have # already been primed by other means, and migrating them again could # potentially override the `stage` or `snap` filtering. (in_part, staged, primed, system) = _split_dependencies( all_dependencies, self.installdir, self.stagedir, self.primedir) part_dependency_paths = {os.path.dirname(d) for d in in_part} staged_dependency_paths = {os.path.dirname(d) for d in staged} dependency_paths = part_dependency_paths | staged_dependency_paths if not self._build_attributes.no_system_libraries(): system_dependency_paths = {os.path.dirname(d) for d in system} dependency_paths.update(system_dependency_paths) if system: # Lots of dependencies are linked with a symlink, so we need to # make sure we follow those symlinks when we migrate the # dependencies. _migrate_files(system, system_dependency_paths, '/', self.primedir, follow_symlinks=True) # TODO: base snap support core_path = common.get_core_path() def mangle_library_path(library_path: str, elf_file_path: str) -> str: # If the path is is in the core snap, use the absolute path, # if the path is primed, use $ORIGIN, and last if the dependency # is not anywhere return an empty string. # # Once we move away from the system library grabbing logic # we can move to a smarter library capturing mechanism. library_path = library_path.replace(self.installdir, self.primedir) if library_path.startswith(core_path): return os.path.dirname(library_path) elif (library_path.startswith(self.primedir) and os.path.exists(library_path)): rel_library_path = os.path.relpath(library_path, elf_file_path) rel_library_path_dir = os.path.dirname(rel_library_path) # return the dirname, with the first .. replace with $ORIGIN return rel_library_path_dir.replace('..', '$ORIGIN', 1) else: return '' if self._confinement == 'classic': core_rpaths = common.get_library_paths( core_path, self._project_options.arch_triplet, existing_only=False) dynamic_linker = self._project_options.get_core_dynamic_linker() elf_patcher = elf.Patcher(dynamic_linker=dynamic_linker, library_path_func=mangle_library_path, base_rpaths=core_rpaths) for elf_file in elf_files: elf_patcher.patch(elf_file=elf_file) self.mark_prime_done(snap_files, snap_dirs, dependency_paths)
def prime(self, force=False) -> None: # noqa: C901 self.makedirs() self.notify_part_progress('Priming') snap_files, snap_dirs = self.migratable_fileset_for('prime') _migrate_files(snap_files, snap_dirs, self.stagedir, self.primedir) elf_files = elf.get_elf_files(self.primedir, snap_files) all_dependencies = set() # TODO: base snap support core_path = common.get_core_path() # Reset to take into account new data inside prime provided by other # parts. self._soname_cache.reset() for elf_file in elf_files: all_dependencies.update( elf_file.load_dependencies(root_path=self.primedir, core_base_path=core_path, soname_cache=self._soname_cache)) dependency_paths = self._handle_dependencies(all_dependencies) if not self._build_attributes.keep_execstack(): clear_execstack(elf_files=elf_files) # TODO revisit if we need to support variations and permutations # of this staged_patchelf_path = os.path.join(self.stagedir, 'bin', 'patchelf') if not os.path.exists(staged_patchelf_path): staged_patchelf_path = None # 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. linker_incompat = dict() # type: Dict[str, str] for elf_file in elf_files: if not elf_file.is_linker_compatible(linker='ld-2.23.so'): linker_incompat[elf_file.path] = elf_file.get_required_glibc() # If libc6 is staged, to avoid symbol mixups we will resort to # glibc mangling. libc6_staged = 'libc6' in self._part_properties.get( 'stage-packages', []) is_classic = self._confinement == 'classic' # classic confined snaps built on anything but a host supporting the # the target base will require glibc mangling. classic_mangling_needed = ( is_classic and not self._project_options.is_host_compatible_with_base) if linker_incompat: formatted_items = [ '- {} (requires GLIBC {})'.format(k, v) for k, v in linker_incompat.items() ] logger.warning( 'The GLIBC version of the targeted core is 2.23. A newer ' 'libc will be required for the following files:\n{}'.format( '\n'.join(formatted_items))) if (linker_incompat or libc6_staged or classic_mangling_needed): if not libc6_staged: raise errors.StagePackageMissingError(package='libc6') handle_glibc_mismatch(elf_files=elf_files, root_path=self.primedir, snap_base_path=self._snap_base_path, core_base_path=core_path, preferred_patchelf_path=staged_patchelf_path, soname_cache=self._soname_cache) elif is_classic: dynamic_linker = self._project_options.get_core_dynamic_linker() elf_patcher = elf.Patcher( dynamic_linker=dynamic_linker, root_path=self.primedir, preferred_patchelf_path=staged_patchelf_path) for elf_file in elf_files: elf_patcher.patch(elf_file=elf_file) self.mark_prime_done(snap_files, snap_dirs, dependency_paths)
def prime(self, force=False) -> None: self.makedirs() self.notify_part_progress('Priming') snap_files, snap_dirs = self.migratable_fileset_for('prime') _migrate_files(snap_files, snap_dirs, self.stagedir, self.primedir) elf_files = elf.get_elf_files(self.primedir, snap_files) all_dependencies = set() # TODO: base snap support core_path = common.get_core_path() for elf_file in elf_files: all_dependencies.update( elf_file.load_dependencies(root_path=self.primedir, core_base_path=core_path)) # Split the necessary dependencies into their corresponding location. # We'll both migrate and track the system dependencies, but we'll only # track the part and staged dependencies, since they should have # already been primed by other means, and migrating them again could # potentially override the `stage` or `snap` filtering. (in_part, staged, primed, system) = _split_dependencies(all_dependencies, self.installdir, self.stagedir, self.primedir) part_dependency_paths = {os.path.dirname(d) for d in in_part} staged_dependency_paths = {os.path.dirname(d) for d in staged} dependency_paths = part_dependency_paths | staged_dependency_paths if not self._build_attributes.no_system_libraries(): system_dependency_paths = {os.path.dirname(d) for d in system} dependency_paths.update(system_dependency_paths) if system: # Lots of dependencies are linked with a symlink, so we need to # make sure we follow those symlinks when we migrate the # dependencies. _migrate_files(system, system_dependency_paths, '/', self.primedir, follow_symlinks=True) formatted_system = '\n'.join(sorted(system)) logger.warning( 'Files from the build host were migrated into the snap to ' 'satisfy dependencies that would otherwise not be met. ' 'This feature will be removed in a future release. If ' 'these libraries are needed in the final snap, ensure ' 'that the following are either satisfied by a ' 'stage-packages entry or through a part:\n{}'.format( formatted_system)) # TODO revisit if we need to support variations and permutations # of this staged_patchelf_path = os.path.join(self.stagedir, 'bin', 'patchelf') if not os.path.exists(staged_patchelf_path): staged_patchelf_path = None # 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. linker_compatible = (e.is_linker_compatible(linker='ld-2.23.so') for e in elf_files) if not all((x for x in linker_compatible)): handle_glibc_mismatch(elf_files=elf_files, root_path=self.primedir, snap_base_path=self._snap_base_path, core_base_path=core_path, preferred_patchelf_path=staged_patchelf_path) elif self._confinement == 'classic': dynamic_linker = self._project_options.get_core_dynamic_linker() elf_patcher = elf.Patcher( dynamic_linker=dynamic_linker, root_path=self.primedir, preferred_patchelf_path=staged_patchelf_path) for elf_file in elf_files: elf_patcher.patch(elf_file=elf_file) self.mark_prime_done(snap_files, snap_dirs, dependency_paths)