def test_prime_state_with_dependencies(self, mock_migrate_files, mock_load_dependencies, mock_get_symbols): mock_load_dependencies.return_value = { "/foo/bar/baz", "{}/lib1/installed".format(self.handler.part_install_dir), "{}/lib2/staged".format(self.handler._project.stage_dir), "{}/lib3/primed".format(self.handler._project.prime_dir), } self.get_elf_files_mock.return_value = frozenset([ elf.ElfFile(path=os.path.join(self.handler._project.prime_dir, "bin", "1")), elf.ElfFile(path=os.path.join(self.handler._project.prime_dir, "bin", "2")), ]) self.assertRaises(errors.NoLatestStepError, self.handler.latest_step) self.assertThat(self.handler.next_step(), Equals(steps.PULL)) bindir = os.path.join(self.handler.part_install_dir, "bin") os.makedirs(bindir) open(os.path.join(bindir, "1"), "w").close() open(os.path.join(bindir, "2"), "w").close() self.handler.mark_done(steps.BUILD) self.handler.stage() # Resetting for test clarity mock_migrate_files.reset_mock() self.handler.prime() self.assertThat(self.handler.latest_step(), Equals(steps.PRIME)) self.assertRaises(errors.NoNextStepError, self.handler.next_step) self.get_elf_files_mock.assert_called_once_with( self.handler._project.prime_dir, {"bin/1", "bin/2"}) mock_migrate_files.assert_has_calls([ call( {"bin/1", "bin/2"}, {"bin"}, self.handler._project.stage_dir, self.handler._project.prime_dir, ) ]) state = self.handler.get_prime_state() self.assertTrue(type(state) is states.PrimeState) self.assertTrue(type(state.files) is set) self.assertTrue(type(state.directories) is set) self.assertTrue(type(state.properties) is OrderedDict) self.assertThat(len(state.files), Equals(2)) self.assertThat(state.dependency_paths, Equals({"lib3"})) self.assertTrue("bin/1" in state.files) self.assertTrue("bin/2" in state.files) self.assertThat(len(state.directories), Equals(1)) self.assertTrue("bin" in state.directories) self.assertTrue("prime" in state.properties) self.assertThat(state.properties["prime"], Equals(["*"])) self.assertTrue(type(state.project_options) is OrderedDict) self.assertThat(len(state.project_options), Equals(0))
def _build(self, *, package: str = "") -> None: build_cmd = ["go", "build"] if self.options.go_buildtags: build_cmd.extend(["-tags={}".format(",".join(self.options.go_buildtags))]) relink_cmd = build_cmd + ["-ldflags", "-linkmode=external"] if self._is_using_go_mod(self.builddir) and not package: work_dir = self.builddir build_cmd.extend(["-o", self._install_bin_dir]) relink_cmd.extend(["-o", self._install_bin_dir]) else: work_dir = self._install_bin_dir build_cmd.append(package) relink_cmd.append(package) pre_build_files = os.listdir(self._install_bin_dir) self._run(build_cmd, cwd=work_dir) post_build_files = os.listdir(self._install_bin_dir) new_files = set(post_build_files) - set(pre_build_files) if len(new_files) != 1: raise RuntimeError(f"Expected one binary to be built, found: {new_files!r}") binary_path = os.path.join(self._install_bin_dir, new_files.pop()) # Relink with system linker if executable is dynamic in order to be # able to set rpath later on. This workaround can be removed after # https://github.com/NixOS/patchelf/issues/146 is fixed. if self._is_classic and elf.ElfFile(path=binary_path).is_dynamic: self._run(relink_cmd, cwd=work_dir)
def test_bin_echo(self): # Try parsing a file without the pyelftools logic mocked out elf_file = elf.ElfFile(path=sys.executable) self.assertThat(elf_file.path, Equals(sys.executable)) # The arch attribute will be a tuple of three strings self.assertTrue(isinstance(elf_file.arch, tuple)) self.assertThat(len(elf_file.arch), Equals(3)) self.assertThat(elf_file.arch[0], StartsWith('ELFCLASS')) self.assertThat(elf_file.arch[1], StartsWith('ELFDATA')) self.assertThat(elf_file.arch[2], StartsWith('EM_')) # We expect Python to be a dynamic linked executable with an # ELF interpreter. self.assertTrue(isinstance(elf_file.interp, str)) self.assertThat(elf_file.interp, NotEquals('')) # Python is not a shared library, so has no soname self.assertThat(elf_file.soname, Equals('')) # We expect that Python will be linked to libc for lib in elf_file.needed.values(): if lib.name.startswith('libc.so'): break else: self.fail("Expected to find libc in needed library list") self.assertTrue(isinstance(lib.name, str)) for version in lib.versions: self.assertTrue(isinstance(version, str), "expected {!r} to be a string".format(version))
def test_fail_gracefully_if_system_libs_not_found(self): stub_magic = ('ELF 64-bit LSB executable, x86-64, version 1 (SYSV), ' 'dynamically linked, interpreter ' '/lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32') self.assertThat( elf.ElfFile(path='foo', magic=stub_magic).load_dependencies(), Equals(frozenset()))
def test_patch_fails_raises_patcherror_exception(self, check_call_mock): elf_file = elf.ElfFile(path='/fake-elf', is_executable=True) elf_patcher = elf.Patcher(dynamic_linker='/lib/fake-ld') self.assertRaises(errors.PatcherError, elf_patcher.patch, elf_file=elf_file)
def build(self): super().build() tags = [] if self.options.go_buildtags: tags = ["-tags={}".format(",".join(self.options.go_buildtags))] packages = self.options.go_packages if not packages: packages = self._get_local_main_packages() for package in packages: binary = os.path.join(self._gopath_bin, self._binary_name(package)) self._run(["go", "build", "-o", binary] + tags + [package]) # Relink with system linker if executable is dynamic in order to be # able to set rpath later on. This workaround can be removed after # https://github.com/NixOS/patchelf/issues/146 is fixed. if self._is_classic and elf.ElfFile(path=binary).is_dynamic: self._run([ "go", "build", "-ldflags", "-linkmode=external", "-o", binary ] + tags + [package]) install_bin_path = os.path.join(self.installdir, "bin") os.makedirs(install_bin_path, exist_ok=True) for binary in os.listdir(self._gopath_bin): binary_path = os.path.join(self._gopath_bin, binary) shutil.copy2(binary_path, install_bin_path)
def test_get_libraries_filtered_by_system_libraries(self): self.get_system_libs_mock.return_value = frozenset(['foo.so.1']) elf_file = elf.ElfFile(path='foo', magic=self.stub_magic) libs = elf_file.load_dependencies(root_path='/', core_base_path='/snap/core/current') self.assertThat(libs, Equals(frozenset(['/usr/lib/bar.so.2'])))
def test_patch_does_nothing_if_no_interpreter(self, check_call_mock): stub_magic = ('ELF 64-bit LSB shared object, x86-64, ' 'version 1 (SYSV), dynamically linked') elf_file = elf.ElfFile(path='/fake-elf', magic=stub_magic) elf_patcher = elf.Patcher(dynamic_linker='/lib/fake-ld') elf_patcher.patch(elf_file=elf_file) self.assertFalse(check_call_mock.called)
def test_non_elf_primed_sonames_matches_are_ignored(self): primed_foo = os.path.join(self.prime_dir, 'foo.so.1') open(primed_foo, 'w').close() elf_file = elf.ElfFile(path='foobar', magic=self.stub_magic) libs = elf_file.load_dependencies(root_path=self.prime_dir, core_base_path=self.core_base_path) self.assertThat(libs, Equals(frozenset( ['/lib/foo.so.1', '/usr/lib/bar.so.2'])))
def test_patch(self, check_call_mock): elf_file = elf.ElfFile(path='/fake-elf', is_executable=True) elf_patcher = elf.Patcher(dynamic_linker='/lib/fake-ld') elf_patcher.patch(elf_file=elf_file) check_call_mock.assert_called_once_with([ self.expected_patchelf, '--set-interpreter', '/lib/fake-ld', '/fake-elf' ])
def test_patch_fails_raises_patcherror_exception(self, check_call_mock): stub_magic = ('ELF 64-bit LSB executable, x86-64, version 1 (SYSV), ' 'dynamically linked, interpreter ' '/lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32') elf_file = elf.ElfFile(path='/fake-elf', magic=stub_magic) elf_patcher = elf.Patcher(dynamic_linker='/lib/fake-ld') self.assertRaises(errors.PatcherError, elf_patcher.patch, elf_file=elf_file)
def test_get_libraries_ldd_failure_logs_warning(self): self.run_output_mock.side_effect = subprocess.CalledProcessError( 1, 'foo', b'bar') dependencies = elf.ElfFile(path='foo', magic=self.stub_magic).load_dependencies() self.assertThat(dependencies, Equals(set())) self.assertThat( self.fake_logger.output, Equals("Unable to determine library dependencies for 'foo'\n"))
def test_patch_fails_raises_patcherror_exception(self): elf_file = elf.ElfFile(path='/fake-elf', magic=self.stub_magic) # The base_path does not matter here as there are not files to # be crawled for. elf_patcher = elf.Patcher(dynamic_linker='/lib/fake-ld', root_path='/fake') self.assertRaises(errors.PatcherError, elf_patcher.patch, elf_file=elf_file)
def test_prime_state_missing_libraries( self, mock_migrate_files, mock_load_dependencies, mock_get_symbols ): self.handler = self.load_part("test_part") self.get_elf_files_mock.return_value = frozenset( [ elf.ElfFile( path=os.path.join(self.handler._project.prime_dir, "bin", "file") ) ] ) # Pretend we found a system dependency, as well as a part and stage # dependency. mock_load_dependencies.return_value = set( [ "/foo/bar/baz", "{}/lib1/installed".format(self.handler.part_install_dir), "{}/lib2/staged".format(self.handler._project.stage_dir), "{}/lib3/primed".format(self.handler._project.prime_dir), ] ) self.assertRaises(errors.NoLatestStepError, self.handler.latest_step) self.assertThat(self.handler.next_step(), Equals(steps.PULL)) bindir = os.path.join(self.handler.part_install_dir, "bin") os.makedirs(bindir) open(os.path.join(bindir, "file"), "w").close() self.handler.mark_done(steps.BUILD) self.handler.stage() mock_migrate_files.reset_mock() self.handler.prime() self.assertThat(self.handler.latest_step(), Equals(steps.PRIME)) self.assertRaises(errors.NoNextStepError, self.handler.next_step) self.get_elf_files_mock.assert_called_once_with( self.handler._project.prime_dir, {"bin/file"} ) # Verify that only the part's files were migrated-- not the system # dependency. mock_migrate_files.assert_called_once_with( {"bin/file"}, {"bin"}, self.handler._project.stage_dir, self.handler._project.prime_dir, ) state = self.handler.get_prime_state() # Verify that only the primed paths were captured. # The rest should be considered missing. self.assertThat(state.dependency_paths, Equals({"lib3"}))
def test_symbols_no_match(self): self.check_output_mock.return_value = dedent("""\ Symbol table '.dynsym' contains 2281 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 0000000000565f20 """).encode() # noqa elf_file = elf.ElfFile(path='/fake-elf', magic=self.stub_magic) self.assertThat(len(elf_file.symbols), Equals(0))
def test_patch_does_nothing_if_no_interpreter(self, check_call_mock): stub_magic = ('ELF 64-bit LSB shared object, x86-64, ' 'version 1 (SYSV), dynamically linked') elf_file = elf.ElfFile(path='/fake-elf', magic=stub_magic) # The base_path does not matter here as there are not files to # be crawled for. elf_patcher = elf.Patcher(dynamic_linker='/lib/fake-ld', root_path='/fake') elf_patcher.patch(elf_file=elf_file) self.assertFalse(check_call_mock.called)
def test_get_libraries_ldd_failure_logs_warning(self): self.run_output_mock.side_effect = subprocess.CalledProcessError( 1, 'foo', b'bar') elf_file = elf.ElfFile(path='foo', magic=self.stub_magic) libs = elf_file.load_dependencies(root_path='/', core_base_path='/snap/core/current') self.assertThat(libs, Equals(set())) self.assertThat( self.fake_logger.output, Equals("Unable to determine library dependencies for 'foo'\n"))
def test_patch(self): elf_file = elf.ElfFile(path='/fake-elf', magic=self.stub_magic) # The base_path does not matter here as there are not files to # be crawled for. elf_patcher = elf.Patcher(dynamic_linker='/lib/fake-ld', root_path='/fake') elf_patcher.patch(elf_file=elf_file) self.check_call_mock.assert_called_once_with([ self.expected_patchelf, '--set-interpreter', '/lib/fake-ld', '/fake-elf' ])
def test_patch(self, check_call_mock): stub_magic = ('ELF 64-bit LSB executable, x86-64, version 1 (SYSV), ' 'dynamically linked, interpreter ' '/lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32') elf_file = elf.ElfFile(path='/fake-elf', magic=stub_magic) elf_patcher = elf.Patcher(dynamic_linker='/lib/fake-ld') elf_patcher.patch(elf_file=elf_file) check_call_mock.assert_called_once_with([ self.expected_patchelf, '--set-interpreter', '/lib/fake-ld', '/fake-elf' ])
def _setUp(self): super()._setUp() self.core_base_path = self.useFixture(fixtures.TempDir()).path binaries_path = os.path.abspath( os.path.join(__file__, '..', 'bin', 'readelf')) new_binaries_path = self.useFixture(fixtures.TempDir()).path current_path = os.environ.get('PATH') new_path = '{}:{}'.format(new_binaries_path, current_path) self.useFixture(fixtures.EnvironmentVariable('PATH', new_path)) # Copy readelf shutil.copy(os.path.join(binaries_path, 'readelf'), os.path.join(new_binaries_path, 'readelf')) os.chmod(os.path.join(new_binaries_path, 'readelf'), 0o755) # Some values in ldd need to be set with core_path with open(os.path.join(binaries_path, 'ldd')) as rf: with open(os.path.join(new_binaries_path, 'ldd'), 'w') as wf: for line in rf.readlines(): wf.write(line.replace('{CORE_PATH}', self.core_base_path)) os.chmod(os.path.join(new_binaries_path, 'ldd'), 0o755) self._elf_files = { 'fake_elf-2.26': elf.ElfFile(path=os.path.join(self.root_path, 'fake_elf-2.26')), 'fake_elf-2.23': elf.ElfFile(path=os.path.join(self.root_path, 'fake_elf-2.23')), 'fake_elf-1.1': elf.ElfFile(path=os.path.join(self.root_path, 'fake_elf-1.1')), 'fake_elf-static': elf.ElfFile(path=os.path.join(self.root_path, 'fake_elf-static')), 'fake_elf-shared-object': elf.ElfFile( path=os.path.join(self.root_path, 'fake_elf-shared-object')), 'fake_elf-bad-ldd': elf.ElfFile(path=os.path.join(self.root_path, 'fake_elf-bad-ldd')), 'fake_elf-with-core-libs': elf.ElfFile( path=os.path.join(self.root_path, 'fake_elf-with-core-libs')), } for elf_file in self._elf_files.values(): with open(elf_file.path, 'wb') as f: f.write(b'\x7fELF') self.root_libraries = { 'foo.so.1': os.path.join(self.root_path, 'foo.so.1'), } for root_library in self.root_libraries.values(): with open(root_library, 'wb') as f: f.write(b'\x7fELF')
def test_get_libraries_excludes_slash_snap(self): lines = [ 'foo.so.1 => /lib/foo.so.1 (0xdead)', 'bar.so.2 => /usr/lib/bar.so.2 (0xbeef)', 'barsnap.so.2 => /snap/snapcraft/current/bar.so.2 (0xbeef)', '/lib/baz.so.2 (0x1234)', ] self.run_output_mock.return_value = '\t' + '\n\t'.join(lines) + '\n' libs = elf.ElfFile(path='foo', magic=self.stub_magic).load_dependencies() self.assertThat( libs, Equals(frozenset(['/lib/foo.so.1', '/usr/lib/bar.so.2'])))
def _setUp(self): super()._setUp() readelf_path = os.path.abspath(os.path.join( __file__, '..', '..', 'bin', 'readelf')) current_path = os.environ.get('PATH') new_path = '{}:{}'.format(readelf_path, current_path) self.useFixture(fixtures.EnvironmentVariable('PATH', new_path)) stub_magic = ('ELF 64-bit LSB executable, x86-64, version 1 (SYSV), ' 'dynamically linked, interpreter ' '/lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32') self.elf_files = [ elf.ElfFile(path=os.path.join(self.root_path, 'fake_elf-2.26'), magic=stub_magic), elf.ElfFile(path=os.path.join(self.root_path, 'fake_elf-2.23'), magic=stub_magic), elf.ElfFile(path=os.path.join(self.root_path, 'fake_elf-1.1'), magic=stub_magic), ] for elf_file in self.elf_files: open(elf_file.path, 'w').close()
def test_get_libraries_excludes_slash_snap(self): lines = [ 'foo.so.1 => /lib/foo.so.1 (0xdead)', 'bar.so.2 => /usr/lib/bar.so.2 (0xbeef)', 'barsnap.so.2 => {}/barsnap.so.2 (0xbeef)'.format( self.core_base_path), '/lib/baz.so.2 (0x1234)', ] self.run_output_mock.return_value = '\t' + '\n\t'.join(lines) + '\n' elf_file = elf.ElfFile(path='foo', magic=self.stub_magic) libs = elf_file.load_dependencies(root_path=self.prime_dir, core_base_path=self.core_base_path) self.assertThat(libs, Equals( frozenset(['/lib/foo.so.1', '/usr/lib/bar.so.2'])))
def test_symbols(self): elf_file = elf.ElfFile(path='/fake-elf', magic=self.stub_magic) self.assertThat(len(elf_file.symbols), Equals(3)) self.assertThat(elf_file.symbols[0].name, Equals('endgrent')) self.assertThat(elf_file.symbols[0].version, Equals('GLIBC_2.2.5')) self.assertThat(elf_file.symbols[0].section, Equals('UND')) self.assertThat(elf_file.symbols[1].name, Equals('__ctype_toupper_loc')) self.assertThat(elf_file.symbols[1].version, Equals('GLIBC_2.3')) self.assertThat(elf_file.symbols[1].section, Equals('UND')) self.assertThat(elf_file.symbols[2].name, Equals('PyCodec_Register')) self.assertThat(elf_file.symbols[2].version, Equals('')) self.assertThat(elf_file.symbols[2].section, Equals('13'))
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)
def test_primed_libraries_are_preferred(self): primed_foo = os.path.join(self.prime_dir, 'foo.so.1') open(primed_foo, 'w').close() self.ms_mock = mock.Mock() self.ms_mock.load.return_value = 0 self.ms_mock.file.return_value = self.stub_magic patcher = mock.patch('magic.open') self.magic_mock = patcher.start() self.magic_mock.return_value = self.ms_mock self.addCleanup(patcher.stop) elf_file = elf.ElfFile(path='foo', magic=self.stub_magic) libs = elf_file.load_dependencies(root_path=self.prime_dir, core_base_path=self.core_base_path) self.assertThat(libs, Equals(frozenset([primed_foo, '/usr/lib/bar.so.2'])))
def test_bin_echo(self): # Try parsing a file without the pyelftools logic mocked out elf_file = elf.ElfFile(path=sys.executable) self.assertThat(elf_file.path, Equals(sys.executable)) # The arch attribute will be a tuple of three strings self.assertTrue(isinstance(elf_file.arch, tuple)) self.assertThat(len(elf_file.arch), Equals(3)) self.assertThat(elf_file.arch[0], StartsWith("ELFCLASS")) self.assertThat(elf_file.arch[1], StartsWith("ELFDATA")) self.assertThat(elf_file.arch[2], StartsWith("EM_")) # We expect Python to be a dynamic linked executable with an # ELF interpreter. self.assertTrue(isinstance(elf_file.interp, str)) self.assertThat(elf_file.interp, NotEquals("")) # Python is not a shared library, so has no soname or defined versions self.assertThat(elf_file.soname, Equals("")) self.assertThat(elf_file.versions, Equals(set())) # We expect that Python will be linked to libc for lib in elf_file.needed.values(): if lib.name.startswith("libc.so"): break else: self.fail("Expected to find libc in needed library list") self.assertTrue(isinstance(lib.name, str)) for version in lib.versions: self.assertTrue(isinstance(version, str), "expected {!r} to be a string".format(version)) # GCC adds a build ID to executables self.assertThat(elf_file.build_id, NotEquals("")) # If the Python interpreter is distro packaged, it probably # doesn't have debug info, but we don't know for sure. # Instead just check that it is a boolean. self.assertTrue(isinstance(elf_file.has_debug_info, bool)) # Ensure type is detered as executable. self.assertThat(elf_file.elf_type, Equals("ET_EXEC"))
def _build(self, *, package: str = "") -> None: build_cmd = ["go", "build"] if self.options.go_buildtags: build_cmd.extend(["-tags={}".format(",".join(self.options.go_buildtags))]) relink_cmd = build_cmd + ["-ldflags", "-linkmode=external"] if self._is_using_go_mod(self.builddir) and not package: work_dir = self.builddir build_type_args = ["-o"] # go build ./... is not supported in go 1.11 or 1.12. # This will only install the main module. if self._get_parsed_go_version() < parse_version( _GO_MOD_ENV_FLAG_REQUIRED_GO_VERSION ): build_type_args.append( os.path.join(self._install_bin_dir, self._get_module()) ) else: build_type_args.extend([self._install_bin_dir, "./..."]) else: work_dir = self._install_bin_dir build_type_args = [package] pre_build_files = os.listdir(self._install_bin_dir) self._run(build_cmd + build_type_args, cwd=work_dir) post_build_files = os.listdir(self._install_bin_dir) new_files = set(post_build_files) - set(pre_build_files) if len(new_files) == 0: logger.warning(f"no binaries found from {build_cmd!r}") for new_file in new_files: binary_path = os.path.join(self._install_bin_dir, new_file) # Relink with system linker if executable is dynamic in order to be # able to set rpath later on. This workaround can be removed after # https://github.com/NixOS/patchelf/issues/146 is fixed. if self._is_classic and elf.ElfFile(path=binary_path).is_dynamic: self._run(relink_cmd + build_type_args, cwd=work_dir)
def test_prime_state_with_shadowed_dependencies(self, mock_migrate_files, mock_load_dependencies, mock_get_symbols): self.get_elf_files_mock.return_value = frozenset( [elf.ElfFile(path="bin/1")]) mock_load_dependencies.return_value = { f"{self.handler._project.prime_dir}/foo/bar/baz" } self.assertRaises(errors.NoLatestStepError, self.handler.latest_step) self.assertThat(self.handler.next_step(), Equals(steps.PULL)) bindir = os.path.join(self.handler.part_install_dir, "bin") foobardir = os.path.join(self.handler.part_install_dir, "foo", "bar") os.makedirs(bindir) os.makedirs(foobardir) # Make a "binary" as well as a "library" at the same path as the one on # the system open(os.path.join(bindir, "1"), "w").close() open(os.path.join(foobardir, "baz"), "w").close() self.handler.mark_done(steps.BUILD) self.handler.stage() mock_migrate_files.reset_mock() self.handler.prime() self.assertThat(self.handler.latest_step(), Equals(steps.PRIME)) self.assertRaises(errors.NoNextStepError, self.handler.next_step) self.get_elf_files_mock.assert_called_once_with( self.handler._project.prime_dir, {"bin/1", "foo/bar/baz"}) mock_migrate_files.assert_called_once_with( {"bin/1", "foo/bar/baz"}, {"bin", "foo", "foo/bar"}, self.handler._project.stage_dir, self.handler._project.prime_dir, ) state = self.handler.get_prime_state() self.assertTrue(type(state) is states.PrimeState) self.assertThat(state.dependency_paths, Equals({"foo/bar"}))
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.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)