def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None, env=None, writemsg_level=None, vardbapi=None): """ Parse /etc/env.d and use it to generate /etc/profile.env, csh.env, ld.so.conf, and prelink.conf. Finally, run ldconfig. When ldconfig is called, its -X option will be used in order to avoid potential interference with installed soname symlinks that are required for correct operation of FEATURES=preserve-libs for downgrade operations. It's not necessary for ldconfig to create soname symlinks, since portage will use NEEDED.ELF.2 data to automatically create them after src_install if they happen to be missing. @param makelinks: True if ldconfig should be called, False otherwise @param target_root: root that is passed to the ldconfig -r option, defaults to portage.settings["ROOT"]. @type target_root: String (Path) """ settings = getattr(portage, 'settings', None) if settings is None: settings = config(config_root=target_root, target_root=target_root) if 'no-env-update' in settings.features: return if vardbapi is None: if isinstance(env, config): vardbapi = vartree(settings=env).dbapi else: if target_root is None: eprefix = portage.settings["EPREFIX"] target_root = portage.settings["ROOT"] target_eroot = portage.settings['EROOT'] else: eprefix = portage.const.EPREFIX target_eroot = os.path.join(target_root, eprefix.lstrip(os.sep)) target_eroot = target_eroot.rstrip(os.sep) + os.sep if hasattr(portage, "db") and target_eroot in portage.db: vardbapi = portage.db[target_eroot]["vartree"].dbapi else: settings = config(config_root=target_root, target_root=target_root, eprefix=eprefix) target_root = settings["ROOT"] if env is None: env = settings vardbapi = vartree(settings=settings).dbapi # Lock the config memory file to prevent symlink creation # in merge_contents from overlapping with env-update. vardbapi._fs_lock() try: return _env_update(makelinks, target_root, prev_mtimes, contents, env, writemsg_level) finally: vardbapi._fs_unlock()
def testFeaturesMutation(self): """ Test whether mutation of config.features updates the FEATURES variable and persists through config.regenerate() calls. """ playground = ResolverPlayground() try: settings = config(clone=playground.settings) settings.features.add('noclean') self.assertEqual('noclean' in settings['FEATURES'].split(), True) settings.regenerate() self.assertEqual('noclean' in settings['FEATURES'].split(),True) settings.features.discard('noclean') self.assertEqual('noclean' in settings['FEATURES'].split(), False) settings.regenerate() self.assertEqual('noclean' in settings['FEATURES'].split(), False) settings.features.add('noclean') self.assertEqual('noclean' in settings['FEATURES'].split(), True) settings.regenerate() self.assertEqual('noclean' in settings['FEATURES'].split(),True) finally: playground.cleanup()
def testSetCpv(self): """ Test the clone via constructor. """ ebuilds = { "dev-libs/A-1": {"IUSE": "static-libs"}, "dev-libs/B-1": {"IUSE": "static-libs"}, } env_files = { "A" : ("USE=\"static-libs\"",) } package_env = ( "dev-libs/A A", ) eprefix = normalize_path(tempfile.mkdtemp()) playground = None try: user_config_dir = os.path.join(eprefix, USER_CONFIG_PATH) os.makedirs(user_config_dir) with io.open(os.path.join(user_config_dir, "package.env"), mode='w', encoding=_encodings['content']) as f: for line in package_env: f.write(line + "\n") env_dir = os.path.join(user_config_dir, "env") os.makedirs(env_dir) for k, v in env_files.items(): with io.open(os.path.join(env_dir, k), mode='w', encoding=_encodings['content']) as f: for line in v: f.write(line + "\n") playground = ResolverPlayground(eprefix=eprefix, ebuilds=ebuilds) settings = config(clone=playground.settings) result = playground.run(["=dev-libs/A-1"]) pkg, existing_node = result.depgraph._select_package( playground.eroot, Atom("=dev-libs/A-1")) settings.setcpv(pkg) self.assertTrue("static-libs" in settings["PORTAGE_USE"].split()) # Test bug #522362, where a USE=static-libs package.env # setting leaked from one setcpv call to the next. pkg, existing_node = result.depgraph._select_package( playground.eroot, Atom("=dev-libs/B-1")) settings.setcpv(pkg) self.assertTrue("static-libs" not in settings["PORTAGE_USE"].split()) finally: if playground is None: shutil.rmtree(eprefix) else: playground.cleanup()
def testFeaturesMutation(self): """ Test whether mutation of config.features updates the FEATURES variable and persists through config.regenerate() calls. """ playground = ResolverPlayground() try: settings = config(clone=playground.settings) settings.features.add('noclean') self.assertEqual('noclean' in settings['FEATURES'].split(), True) settings.regenerate() self.assertEqual('noclean' in settings['FEATURES'].split(), True) settings.features.discard('noclean') self.assertEqual('noclean' in settings['FEATURES'].split(), False) settings.regenerate() self.assertEqual('noclean' in settings['FEATURES'].split(), False) settings.features.add('noclean') self.assertEqual('noclean' in settings['FEATURES'].split(), True) settings.regenerate() self.assertEqual('noclean' in settings['FEATURES'].split(), True) finally: playground.cleanup()
def _load_config(self): env = { "ACCEPT_KEYWORDS": "x86", "PORTDIR": self.portdir, 'PORTAGE_TMPDIR' : os.path.join(self.eroot, 'var/tmp'), } # Pass along PORTAGE_USERNAME and PORTAGE_GRPNAME since they # need to be inherited by ebuild subprocesses. if 'PORTAGE_USERNAME' in os.environ: env['PORTAGE_USERNAME'] = os.environ['PORTAGE_USERNAME'] if 'PORTAGE_GRPNAME' in os.environ: env['PORTAGE_GRPNAME'] = os.environ['PORTAGE_GRPNAME'] settings = config(_eprefix=self.eprefix, env=env) settings.lock() trees = { self.root: { "vartree": vartree(settings=settings), "porttree": portagetree(self.root, settings=settings), "bintree": binarytree(self.root, os.path.join(self.eroot, "usr/portage/packages"), settings=settings) } } for root, root_trees in trees.items(): settings = root_trees["vartree"].settings settings._init_dirs() setconfig = load_default_config(settings, root_trees) root_trees["root_config"] = RootConfig(settings, root_trees, setconfig) return settings, trees
def getmaskingstatus(mycpv, settings=None, portdb=None, myrepo=None): if settings is None: settings = config(clone=portage.settings) if portdb is None: portdb = portage.portdb return [mreason.message for \ mreason in _getmaskingstatus(mycpv, settings, portdb,myrepo)]
def testDoebuildSpawn(self): playground = ResolverPlayground() try: settings = config(clone=playground.settings) cpv = 'sys-apps/portage-2.1' metadata = { 'EAPI' : '2', 'INHERITED' : 'python eutils', 'IUSE' : 'build doc epydoc python3 selinux', 'LICENSE' : 'GPL-2', 'PROVIDE' : 'virtual/portage', 'RDEPEND' : '>=app-shells/bash-3.2_p17 >=dev-lang/python-2.6', 'SLOT' : '0', } root_config = playground.trees[playground.root]['root_config'] pkg = Package(built=False, cpv=cpv, installed=False, metadata=metadata, root_config=root_config, type_name='ebuild') settings.setcpv(pkg) settings['PORTAGE_PYTHON'] = _python_interpreter settings['PORTAGE_BUILDDIR'] = os.path.join( settings['PORTAGE_TMPDIR'], cpv) settings['T'] = os.path.join( settings['PORTAGE_BUILDDIR'], 'temp') for x in ('PORTAGE_BUILDDIR', 'T'): os.makedirs(settings[x]) # Create a fake environment, to pretend as if the ebuild # has been sourced already. open(os.path.join(settings['T'], 'environment'), 'wb') task_scheduler = TaskScheduler() for phase in ('_internal_test',): # Test EbuildSpawnProcess by calling doebuild.spawn() with # returnpid=False. This case is no longer used by portage # internals since EbuildPhase is used instead and that passes # returnpid=True to doebuild.spawn(). rval = doebuild_spawn("%s %s" % (_shell_quote( os.path.join(settings["PORTAGE_BIN_PATH"], os.path.basename(EBUILD_SH_BINARY))), phase), settings, free=1) self.assertEqual(rval, os.EX_OK) ebuild_phase = EbuildPhase(background=False, phase=phase, scheduler=task_scheduler.sched_iface, settings=settings) task_scheduler.add(ebuild_phase) task_scheduler.run() self.assertEqual(ebuild_phase.returncode, os.EX_OK) ebuild_phase = MiscFunctionsProcess(background=False, commands=['success_hooks'], scheduler=task_scheduler.sched_iface, settings=settings) task_scheduler.add(ebuild_phase) task_scheduler.run() self.assertEqual(ebuild_phase.returncode, os.EX_OK) finally: playground.cleanup()
def testDoebuildSpawn(self): playground = ResolverPlayground() try: settings = config(clone=playground.settings) cpv = 'sys-apps/portage-2.1' metadata = { 'EAPI' : '2', 'INHERITED' : 'python eutils', 'IUSE' : 'build doc epydoc python3 selinux', 'LICENSE' : 'GPL-2', 'PROVIDE' : 'virtual/portage', 'RDEPEND' : '>=app-shells/bash-3.2_p17 >=dev-lang/python-2.6', 'SLOT' : '0', } root_config = playground.trees[playground.eroot]['root_config'] pkg = Package(built=False, cpv=cpv, installed=False, metadata=metadata, root_config=root_config, type_name='ebuild') settings.setcpv(pkg) settings['PORTAGE_PYTHON'] = _python_interpreter settings['PORTAGE_BUILDDIR'] = os.path.join( settings['PORTAGE_TMPDIR'], cpv) settings['T'] = os.path.join( settings['PORTAGE_BUILDDIR'], 'temp') for x in ('PORTAGE_BUILDDIR', 'T'): os.makedirs(settings[x]) # Create a fake environment, to pretend as if the ebuild # has been sourced already. open(os.path.join(settings['T'], 'environment'), 'wb').close() scheduler = PollScheduler().sched_iface for phase in ('_internal_test',): # Test EbuildSpawnProcess by calling doebuild.spawn() with # returnpid=False. This case is no longer used by portage # internals since EbuildPhase is used instead and that passes # returnpid=True to doebuild.spawn(). rval = doebuild_spawn("%s %s" % (_shell_quote( os.path.join(settings["PORTAGE_BIN_PATH"], os.path.basename(EBUILD_SH_BINARY))), phase), settings, free=1) self.assertEqual(rval, os.EX_OK) ebuild_phase = EbuildPhase(background=False, phase=phase, scheduler=scheduler, settings=settings) ebuild_phase.start() ebuild_phase.wait() self.assertEqual(ebuild_phase.returncode, os.EX_OK) ebuild_phase = MiscFunctionsProcess(background=False, commands=['success_hooks'], scheduler=scheduler, settings=settings) ebuild_phase.start() ebuild_phase.wait() self.assertEqual(ebuild_phase.returncode, os.EX_OK) finally: playground.cleanup()
def testFakedbapi(self): packages = ( ("sys-apps/portage-2.1.10", { "EAPI" : "2", "IUSE" : "ipc doc", "repository" : "gentoo", "SLOT" : "0", "USE" : "ipc missing-iuse", }), ("virtual/package-manager-0", { "EAPI" : "0", "repository" : "gentoo", "SLOT" : "0", }), ) match_tests = ( ("sys-apps/portage:0[ipc]", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0[-ipc]", []), ("sys-apps/portage:0[doc]", []), ("sys-apps/portage:0[-doc]", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0[missing-iuse]", []), ("sys-apps/portage:0[-missing-iuse]", []), ("sys-apps/portage:0::gentoo[ipc]", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0::multilib[ipc]", []), ("virtual/package-manager", ["virtual/package-manager-0"]), ) tempdir = tempfile.mkdtemp() try: test_repo = os.path.join(tempdir, "var", "repositories", "test_repo") os.makedirs(os.path.join(test_repo, "profiles")) with open(os.path.join(test_repo, "profiles", "repo_name"), "w") as f: f.write("test_repo") env = { "PORTAGE_REPOSITORIES": "[DEFAULT]\nmain-repo = test_repo\n[test_repo]\nlocation = %s" % test_repo } # Tests may override portage.const.EPREFIX in order to # simulate a prefix installation. It's reasonable to do # this because tests should be self-contained such that # the "real" value of portage.const.EPREFIX is entirely # irrelevant (see bug #492932). portage.const.EPREFIX = tempdir fakedb = fakedbapi(settings=config(config_profile_path="", env=env, eprefix=tempdir)) for cpv, metadata in packages: fakedb.cpv_inject(cpv, metadata=metadata) for atom, expected_result in match_tests: result = fakedb.match(atom) self.assertEqual(fakedb.match(atom), expected_result, "fakedb.match('%s') = %s != %s" % (atom, result, expected_result)) finally: shutil.rmtree(tempdir)
def testClone(self): """ Test the clone via constructor. """ ebuilds = {"dev-libs/A-1": {}} playground = ResolverPlayground(ebuilds=ebuilds) try: settings = config(clone=playground.settings) result = playground.run(["=dev-libs/A-1"]) pkg, existing_node = result.depgraph._select_package(playground.eroot, "=dev-libs/A-1") settings.setcpv(pkg) # clone after setcpv tests deepcopy of LazyItemsDict settings2 = config(clone=settings) finally: playground.cleanup()
def _start(self): settings = self.settings if settings is None: settings = self.portdb.settings if "PORTAGE_PARALLEL_FETCHONLY" in settings: # parallel-fetch mode self.returncode = os.EX_OK self._async_wait() return # Prevent temporary config changes from interfering # with config instances that are reused. settings = self.settings = config(clone=settings) # We must create our private PORTAGE_TMPDIR before calling # doebuild_environment(), since lots of variables such # as PORTAGE_BUILDDIR refer to paths inside PORTAGE_TMPDIR. portage_tmpdir = settings.get("PORTAGE_TMPDIR") if not portage_tmpdir or not os.access(portage_tmpdir, os.W_OK): portage_tmpdir = None private_tmpdir = self._private_tmpdir = tempfile.mkdtemp( dir=portage_tmpdir) settings["PORTAGE_TMPDIR"] = private_tmpdir settings.backup_changes("PORTAGE_TMPDIR") # private temp dir was just created, so it's not locked yet settings.pop("PORTAGE_BUILDDIR_LOCKED", None) doebuild_environment(self.ebuild_path, "nofetch", settings=settings, db=self.portdb) restrict = settings["PORTAGE_RESTRICT"].split() defined_phases = settings["DEFINED_PHASES"].split() if not defined_phases: # When DEFINED_PHASES is undefined, assume all # phases are defined. defined_phases = EBUILD_PHASES if "fetch" not in restrict and "nofetch" not in defined_phases: self.returncode = os.EX_OK self._async_wait() return prepare_build_dirs(settings=settings) ebuild_phase = EbuildPhase( background=self.background, phase="nofetch", scheduler=self.scheduler, fd_pipes=self.fd_pipes, settings=settings, ) self._start_task(ebuild_phase, self._nofetch_exit)
def _create_ebuild_manifests(self, ebuilds): for cpv in ebuilds: a = Atom("=" + cpv) ebuild_dir = os.path.join(self.portdir, a.cp) ebuild_path = os.path.join(ebuild_dir, a.cpv.split("/")[1] + ".ebuild") portage.util.noiselimit = -1 tmpsettings = config(clone=self.settings) portdb = self.trees[self.root]["porttree"].dbapi portage.doebuild(ebuild_path, "digest", self.root, tmpsettings, tree="porttree", mydbapi=portdb) portage.util.noiselimit = 0
def testClone(self): """ Test the clone via constructor. """ ebuilds = { "dev-libs/A-1": {}, } playground = ResolverPlayground(ebuilds=ebuilds) try: settings = config(clone=playground.settings) result = playground.run(["=dev-libs/A-1"]) pkg, existing_node = result.depgraph._select_package( playground.eroot, Atom("=dev-libs/A-1")) settings.setcpv(pkg) # clone after setcpv tests deepcopy of LazyItemsDict settings2 = config(clone=settings) finally: playground.cleanup()
def _start(self): settings = self.settings if settings is None: settings = self.portdb.settings if 'PORTAGE_PARALLEL_FETCHONLY' in settings: # parallel-fetch mode self.returncode = os.EX_OK self._async_wait() return # Prevent temporary config changes from interfering # with config instances that are reused. settings = self.settings = config(clone=settings) # We must create our private PORTAGE_TMPDIR before calling # doebuild_environment(), since lots of variables such # as PORTAGE_BUILDDIR refer to paths inside PORTAGE_TMPDIR. portage_tmpdir = settings.get('PORTAGE_TMPDIR') if not portage_tmpdir or not os.access(portage_tmpdir, os.W_OK): portage_tmpdir = None private_tmpdir = self._private_tmpdir = tempfile.mkdtemp( dir=portage_tmpdir) settings['PORTAGE_TMPDIR'] = private_tmpdir settings.backup_changes('PORTAGE_TMPDIR') # private temp dir was just created, so it's not locked yet settings.pop('PORTAGE_BUILDDIR_LOCKED', None) doebuild_environment(self.ebuild_path, 'nofetch', settings=settings, db=self.portdb) restrict = settings['PORTAGE_RESTRICT'].split() defined_phases = settings['DEFINED_PHASES'].split() if not defined_phases: # When DEFINED_PHASES is undefined, assume all # phases are defined. defined_phases = EBUILD_PHASES if 'fetch' not in restrict and \ 'nofetch' not in defined_phases: self.returncode = os.EX_OK self._async_wait() return prepare_build_dirs(settings=settings) ebuild_phase = EbuildPhase(background=self.background, phase='nofetch', scheduler=self.scheduler, fd_pipes=self.fd_pipes, settings=settings) self._start_task(ebuild_phase, self._nofetch_exit)
def testFakedbapi(self): packages = ( ("sys-apps/portage-2.1.10", { "EAPI" : "2", "IUSE" : "ipc doc", "repository" : "gentoo", "SLOT" : "0", "USE" : "ipc missing-iuse", }), ("virtual/package-manager-0", { "EAPI" : "0", "repository" : "gentoo", "SLOT" : "0", }), ) match_tests = ( ("sys-apps/portage:0[ipc]", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0[-ipc]", []), ("sys-apps/portage:0[doc]", []), ("sys-apps/portage:0[-doc]", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0[missing-iuse]", []), ("sys-apps/portage:0[-missing-iuse]", []), ("sys-apps/portage:0::gentoo[ipc]", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0::multilib[ipc]", []), ("virtual/package-manager", ["virtual/package-manager-0"]), ) tempdir = tempfile.mkdtemp() try: portdir = os.path.join(tempdir, "usr/portage") os.makedirs(portdir) env = { "PORTDIR": portdir, } fakedb = fakedbapi(settings=config(config_profile_path="", env=env, eprefix=tempdir)) for cpv, metadata in packages: fakedb.cpv_inject(cpv, metadata=metadata) for atom, expected_result in match_tests: result = fakedb.match(atom) self.assertEqual(fakedb.match(atom), expected_result, "fakedb.match('%s') = %s != %s" % (atom, result, expected_result)) finally: shutil.rmtree(tempdir)
def testHookDirectory(self): """ Tests to be sure a hook loads and reads the right settings Based on test_PackageKeywordsFile.py """ self.tmp_dir_path = self.BuildTmp("/etc/portage/hooks/test.d") try: settings = config() settings["PORTAGE_CONFIGROOT"] = self.tmp_dir_path settings["FEATURES"] += " hooks" hooks = HookDirectory(phase="test", settings=settings) hooks.execute() self.assert_(settings["hookonlytest"] == "") finally: rmtree(self.tmp_dir_path)
def testHookDirectory(self): """ Tests to be sure a hook loads and reads the right settings Based on test_PackageKeywordsFile.py """ self.tmp_dir_path = self.BuildTmp('/etc/portage/hooks/test.d') try: settings = config() settings["PORTAGE_CONFIGROOT"] = self.tmp_dir_path settings["FEATURES"] += " hooks" hooks = HookDirectory(phase='test', settings=settings) hooks.execute() self.assert_(settings["hookonlytest"] == "") finally: rmtree(self.tmp_dir_path)
def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None, env=None, writemsg_level=None, vardbapi=None): """ Parse /etc/env.d and use it to generate /etc/profile.env, csh.env, ld.so.conf, and prelink.conf. Finally, run ldconfig. When ldconfig is called, its -X option will be used in order to avoid potential interference with installed soname symlinks that are required for correct operation of FEATURES=preserve-libs for downgrade operations. It's not necessary for ldconfig to create soname symlinks, since portage will use NEEDED.ELF.2 data to automatically create them after src_install if they happen to be missing. @param makelinks: True if ldconfig should be called, False otherwise @param target_root: root that is passed to the ldconfig -r option, defaults to portage.settings["ROOT"]. @type target_root: String (Path) """ if vardbapi is None: if isinstance(env, config): vardbapi = vartree(settings=env).dbapi else: if target_root is None: eprefix = portage.settings["EPREFIX"] target_root = portage.settings["ROOT"] target_eroot = portage.settings['EROOT'] else: eprefix = portage.const.EPREFIX target_eroot = os.path.join(target_root, eprefix.lstrip(os.sep)) target_eroot = target_eroot.rstrip(os.sep) + os.sep if hasattr(portage, "db") and target_eroot in portage.db: vardbapi = portage.db[target_eroot]["vartree"].dbapi else: settings = config(config_root=target_root, target_root=target_root, eprefix=eprefix) target_root = settings["ROOT"] if env is None: env = settings vardbapi = vartree(settings=settings).dbapi # Lock the config memory file to prevent symlink creation # in merge_contents from overlapping with env-update. vardbapi._fs_lock() try: return _env_update(makelinks, target_root, prev_mtimes, contents, env, writemsg_level) finally: vardbapi._fs_unlock()
def _create_ebuild_manifests(self, ebuilds): tmpsettings = config(clone=self.settings) tmpsettings['PORTAGE_QUIET'] = '1' for cpv in ebuilds: a = Atom("=" + cpv, allow_repo=True) repo = a.repo if repo is None: repo = "test_repo" repo_dir = self._get_repo_dir(repo) ebuild_dir = os.path.join(repo_dir, a.cp) ebuild_path = os.path.join(ebuild_dir, a.cpv.split("/")[1] + ".ebuild") portdb = self.trees[self.eroot]["porttree"].dbapi tmpsettings['O'] = ebuild_dir if not digestgen(mysettings=tmpsettings, myportdb=portdb): raise AssertionError('digest creation failed for %s' % ebuild_path)
def testFakedbapi(self): packages = ( ("sys-apps/portage-2.1.10", { "EAPI": "2", "IUSE": "ipc doc", "repository": "gentoo", "SLOT": "0", "USE": "ipc missing-iuse", }), ("virtual/package-manager-0", { "EAPI": "0", "repository": "gentoo", "SLOT": "0", }), ) match_tests = ( ("sys-apps/portage:0[ipc]", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0[-ipc]", []), ("sys-apps/portage:0[doc]", []), ("sys-apps/portage:0[-doc]", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0[missing-iuse]", []), ("sys-apps/portage:0[-missing-iuse]", []), ("sys-apps/portage:0::gentoo[ipc]", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0::multilib[ipc]", []), ("virtual/package-manager", ["virtual/package-manager-0"]), ) tempdir = tempfile.mkdtemp() try: portdir = os.path.join(tempdir, "usr/portage") os.makedirs(portdir) env = { "PORTDIR": portdir, } fakedb = fakedbapi(settings=config( config_profile_path="", env=env, eprefix=tempdir)) for cpv, metadata in packages: fakedb.cpv_inject(cpv, metadata=metadata) for atom, expected_result in match_tests: self.assertEqual(fakedb.match(atom), expected_result) finally: shutil.rmtree(tempdir)
def testFakedbapi(self): packages = ( ( "sys-apps/portage-2.1.10", {"EAPI": "2", "IUSE": "ipc doc", "repository": "gentoo", "SLOT": "0", "USE": "ipc missing-iuse"}, ), ("virtual/package-manager-0", {"EAPI": "0", "repository": "gentoo", "SLOT": "0"}), ) match_tests = ( ("sys-apps/portage:0[ipc]", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0[-ipc]", []), ("sys-apps/portage:0[doc]", []), ("sys-apps/portage:0[-doc]", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0[missing-iuse]", []), ("sys-apps/portage:0[-missing-iuse]", []), ("sys-apps/portage:0::gentoo[ipc]", ["sys-apps/portage-2.1.10"]), ("sys-apps/portage:0::multilib[ipc]", []), ("virtual/package-manager", ["virtual/package-manager-0"]), ) tempdir = tempfile.mkdtemp() try: test_repo = os.path.join(tempdir, "var", "repositories", "test_repo") os.makedirs(os.path.join(test_repo, "profiles")) with open(os.path.join(test_repo, "profiles", "repo_name"), "w") as f: f.write("test_repo") env = {"PORTAGE_REPOSITORIES": "[DEFAULT]\nmain-repo = test_repo\n[test_repo]\nlocation = %s" % test_repo} fakedb = fakedbapi(settings=config(config_profile_path="", env=env, eprefix=tempdir)) for cpv, metadata in packages: fakedb.cpv_inject(cpv, metadata=metadata) for atom, expected_result in match_tests: result = fakedb.match(atom) self.assertEqual( fakedb.match(atom), expected_result, "fakedb.match('%s') = %s != %s" % (atom, result, expected_result), ) finally: shutil.rmtree(tempdir)
def __init__(self, root = None, portage_arch = None): """Constructor. str root Portage root directory; defaults to Portage’s ${ROOT}. str portage_arch Portage architecture; defaults to Portage’s ${ARCH}. """ if root: # Set this now to override Portage’s default root. os.environ['ROOT'] = root self._category = None # Set by make_package_name() self._portage_config = portage_config.config() if not root: # Set this now to override the null root with Portage’s default root. os.environ['ROOT'] = root = self._portage_config['ROOT'] self._cross_compiler_prefix = None self._dev_null = open(os.devnull, 'w') self._ebuild_file_path = None self._ebuild_pkg_root = None self._indent = '' self._irf_compressor = None self._irf_archive_path = None self._irf_source_path = None self._kernel_release = None # Set by set_sources() self._kernel_version = None # Set by set_sources() self._kmake_args = ['make'] self._kmake_args.extend(shlex.split(self._portage_config['MAKEOPTS'])) self._kmake_env = dict(os.environ) if not portage_arch: portage_arch = self._portage_config['ARCH'] self._kmake_env['ARCH'] = self._portage_arch_to_kernel_arch.get( portage_arch, portage_arch ) self._module_packages = None # Set by build_kernel() self._package_name = None # Set by make_package_name() self._package_version = None # Set by make_package_name() self._root = root self._source_path = None self._src_config_path = None self._src_image_path = None
def __init__(self, root=None, portage_arch=None): """Constructor. str root Portage root directory; defaults to Portage’s ${ROOT}. str portage_arch Portage architecture; defaults to Portage’s ${ARCH}. """ if root: # Set this now to override Portage’s default root. os.environ['ROOT'] = root self._category = None # Set by make_package_name() self._portage_config = portage_config.config() if not root: # Set this now to override the null root with Portage’s default root. os.environ['ROOT'] = root = self._portage_config['ROOT'] self._cross_compiler_prefix = None self._dev_null = open(os.devnull, 'w') self._ebuild_file_path = None self._ebuild_pkg_root = None self._indent = '' self._irf_compressor = None self._irf_archive_path = None self._irf_source_path = None self._kernel_release = None # Set by set_sources() self._kernel_version = None # Set by set_sources() self._kmake_args = ['make'] self._kmake_args.extend(shlex.split(self._portage_config['MAKEOPTS'])) self._kmake_env = dict(os.environ) if not portage_arch: portage_arch = self._portage_config['ARCH'] self._kmake_env['ARCH'] = self._portage_arch_to_kernel_arch.get( portage_arch, portage_arch) self._module_packages = None # Set by build_kernel() self._package_name = None # Set by make_package_name() self._package_version = None # Set by make_package_name() self._root = root self._source_path = None self._src_config_path = None self._src_image_path = None
def __init__(self, sRoot = None, sPArch = None): """Constructor. str sRoot Portage root directory; defaults to Portage’s ${ROOT}. str sPArch Portage architecture; defaults to Portage’s ${ARCH}. """ if sRoot: # Set this now to override Portage’s default root. os.environ['ROOT'] = sRoot self._m_sCategory = None # Set by make_package_name() self._m_pconfig = portage_config.config() if not sRoot: # Set this now to override the null sRoot with Portage’s default root. os.environ['ROOT'] = sRoot = self._m_pconfig['ROOT'] self._m_sCrossCompiler = None self._m_sEbuildFilePath = None self._m_sEbuildPkgRoot = None self._m_sIndent = '' self._m_comprIrf = None self._m_sIrfArchivePath = None self._m_sIrfSourcePath = None self._m_sKernelRelease = None # Set by set_sources() self._m_sKernelVersion = None # Set by set_sources() self._m_listKMakeArgs = ['make'] self._m_listKMakeArgs.extend(shlex.split(self._m_pconfig['MAKEOPTS'])) self._m_dictKMakeEnv = dict(os.environ) if not sPArch: sPArch = self._m_pconfig['ARCH'] self._m_dictKMakeEnv['ARCH'] = self._smc_dictPArchToKArch.get(sPArch, sPArch) self._m_tplModulePackages = None # Set by build_kernel() self._m_fileNullOut = open(os.devnull, 'w') self._m_sPackageName = None # Set by make_package_name() self._m_sPackageVersion = None # Set by make_package_name() self._m_sRoot = sRoot self._m_sSourcePath = None self._m_sSrcConfigPath = None self._m_sSrcImagePath = None
def testFeaturesMutation(self): """ Test whether mutation of config.features updates the FEATURES variable and persists through config.regenerate() calls. Also verify that features_set._prune_overrides() works correctly. """ playground = ResolverPlayground() try: settings = config(clone=playground.settings) settings.features.add('noclean') self.assertEqual('noclean' in settings['FEATURES'].split(), True) settings.regenerate() self.assertEqual('noclean' in settings['FEATURES'].split(), True) settings.features.discard('noclean') self.assertEqual('noclean' in settings['FEATURES'].split(), False) settings.regenerate() self.assertEqual('noclean' in settings['FEATURES'].split(), False) settings.features.add('noclean') self.assertEqual('noclean' in settings['FEATURES'].split(), True) settings.regenerate() self.assertEqual('noclean' in settings['FEATURES'].split(), True) # before: ['noclean', '-noclean', 'noclean'] settings.features._prune_overrides() # after: ['noclean'] self.assertEqual(settings._features_overrides.count('noclean'), 1) self.assertEqual(settings._features_overrides.count('-noclean'), 0) settings.features.remove('noclean') # before: ['noclean', '-noclean'] settings.features._prune_overrides() # after: ['-noclean'] self.assertEqual(settings._features_overrides.count('noclean'), 0) self.assertEqual(settings._features_overrides.count('-noclean'), 1) finally: playground.cleanup()
def _load_config(self): env = { "ACCEPT_KEYWORDS": "x86", "PORTDIR": self.portdir, 'PORTAGE_TMPDIR': os.path.join(self.eroot, 'var/tmp'), } # Pass along PORTAGE_USERNAME and PORTAGE_GRPNAME since they # need to be inherited by ebuild subprocesses. if 'PORTAGE_USERNAME' in os.environ: env['PORTAGE_USERNAME'] = os.environ['PORTAGE_USERNAME'] if 'PORTAGE_GRPNAME' in os.environ: env['PORTAGE_GRPNAME'] = os.environ['PORTAGE_GRPNAME'] settings = config(_eprefix=self.eprefix, env=env) settings.lock() trees = { self.root: { "vartree": vartree(settings=settings), "porttree": portagetree(self.root, settings=settings), "bintree": binarytree(self.root, os.path.join(self.eroot, "usr/portage/packages"), settings=settings) } } for root, root_trees in trees.items(): settings = root_trees["vartree"].settings settings._init_dirs() setconfig = load_default_config(settings, root_trees) root_trees["root_config"] = RootConfig(settings, root_trees, setconfig) return settings, trees
def digestgen(myarchives=None, mysettings=None, overwrite=None, manifestonly=None, myportdb=None): """ Generates a digest file if missing. Fetches files if necessary. NOTE: myarchives and mysettings used to be positional arguments, so their order must be preserved for backward compatibility. @param mysettings: the ebuild config (mysettings["O"] must correspond to the ebuild's parent directory) @type mysettings: config @param myportdb: a portdbapi instance @type myportdb: portdbapi @rtype: int @returns: 1 on success and 0 on failure """ if mysettings is None: raise TypeError("portage.digestgen(): missing" + \ " required 'mysettings' parameter") if myportdb is None: warnings.warn("portage.digestgen() called without 'myportdb' parameter", DeprecationWarning, stacklevel=2) myportdb = portage.portdb if overwrite is not None: warnings.warn("portage.digestgen() called with " + \ "deprecated 'overwrite' parameter", DeprecationWarning, stacklevel=2) if manifestonly is not None: warnings.warn("portage.digestgen() called with " + \ "deprecated 'manifestonly' parameter", DeprecationWarning, stacklevel=2) try: portage._doebuild_manifest_exempt_depend += 1 distfiles_map = {} fetchlist_dict = FetchlistDict(mysettings["O"], mysettings, myportdb) for cpv in fetchlist_dict: try: for myfile in fetchlist_dict[cpv]: distfiles_map.setdefault(myfile, []).append(cpv) except InvalidDependString as e: writemsg("!!! %s\n" % str(e), noiselevel=-1) del e return 0 mytree = os.path.dirname(os.path.dirname(mysettings["O"])) manifest1_compat = False mf = Manifest(mysettings["O"], mysettings["DISTDIR"], fetchlist_dict=fetchlist_dict, manifest1_compat=manifest1_compat) # Don't require all hashes since that can trigger excessive # fetches when sufficient digests already exist. To ease transition # while Manifest 1 is being removed, only require hashes that will # exist before and after the transition. required_hash_types = set() required_hash_types.add("size") required_hash_types.add(MANIFEST2_REQUIRED_HASH) dist_hashes = mf.fhashdict.get("DIST", {}) # To avoid accidental regeneration of digests with the incorrect # files (such as partially downloaded files), trigger the fetch # code if the file exists and it's size doesn't match the current # manifest entry. If there really is a legitimate reason for the # digest to change, `ebuild --force digest` can be used to avoid # triggering this code (or else the old digests can be manually # removed from the Manifest). missing_files = [] for myfile in distfiles_map: myhashes = dist_hashes.get(myfile) if not myhashes: try: st = os.stat(os.path.join(mysettings["DISTDIR"], myfile)) except OSError: st = None if st is None or st.st_size == 0: missing_files.append(myfile) continue size = myhashes.get("size") try: st = os.stat(os.path.join(mysettings["DISTDIR"], myfile)) except OSError as e: if e.errno != errno.ENOENT: raise del e if size == 0: missing_files.append(myfile) continue if required_hash_types.difference(myhashes): missing_files.append(myfile) continue else: if st.st_size == 0 or size is not None and size != st.st_size: missing_files.append(myfile) continue if missing_files: mytree = os.path.realpath(os.path.dirname( os.path.dirname(mysettings["O"]))) fetch_settings = config(clone=mysettings) debug = mysettings.get("PORTAGE_DEBUG") == "1" for myfile in missing_files: uris = set() for cpv in distfiles_map[myfile]: myebuild = os.path.join(mysettings["O"], catsplit(cpv)[1] + ".ebuild") # for RESTRICT=fetch, mirror, etc... doebuild_environment(myebuild, "fetch", mysettings["ROOT"], fetch_settings, debug, 1, myportdb) uris.update(myportdb.getFetchMap( cpv, mytree=mytree)[myfile]) fetch_settings["A"] = myfile # for use by pkg_nofetch() try: st = os.stat(os.path.join( mysettings["DISTDIR"],myfile)) except OSError: st = None if not fetch({myfile : uris}, fetch_settings): writemsg(_("!!! Fetch failed for %s, can't update " "Manifest\n") % myfile, noiselevel=-1) if myfile in dist_hashes and \ st is not None and st.st_size > 0: # stat result is obtained before calling fetch(), # since fetch may rename the existing file if the # digest does not match. writemsg(_("!!! If you would like to " "forcefully replace the existing " "Manifest entry\n!!! for %s, use " "the following command:\n") % myfile + \ "!!! " + colorize("INFORM", "ebuild --force %s manifest" % \ os.path.basename(myebuild)) + "\n", noiselevel=-1) return 0 writemsg_stdout(_(">>> Creating Manifest for %s\n") % mysettings["O"]) try: mf.create(assumeDistHashesSometimes=True, assumeDistHashesAlways=( "assume-digests" in mysettings.features)) except FileNotFound as e: writemsg(_("!!! File %s doesn't exist, can't update " "Manifest\n") % e, noiselevel=-1) return 0 except PortagePackageException as e: writemsg(("!!! %s\n") % (e,), noiselevel=-1) return 0 try: mf.write(sign=False) except PermissionDenied as e: writemsg(_("!!! Permission Denied: %s\n") % (e,), noiselevel=-1) return 0 if "assume-digests" not in mysettings.features: distlist = list(mf.fhashdict.get("DIST", {})) distlist.sort() auto_assumed = [] for filename in distlist: if not os.path.exists( os.path.join(mysettings["DISTDIR"], filename)): auto_assumed.append(filename) if auto_assumed: mytree = os.path.realpath( os.path.dirname(os.path.dirname(mysettings["O"]))) cp = os.path.sep.join(mysettings["O"].split(os.path.sep)[-2:]) pkgs = myportdb.cp_list(cp, mytree=mytree) pkgs.sort() writemsg_stdout(" digest.assumed" + colorize("WARN", str(len(auto_assumed)).rjust(18)) + "\n") for pkg_key in pkgs: fetchlist = myportdb.getFetchMap(pkg_key, mytree=mytree) pv = pkg_key.split("/")[1] for filename in auto_assumed: if filename in fetchlist: writemsg_stdout( " %s::%s\n" % (pv, filename)) return 1 finally: portage._doebuild_manifest_exempt_depend -= 1
def testEbuildFetch(self): distfiles = { 'bar': b'bar\n', 'foo': b'foo\n', } ebuilds = { 'dev-libs/A-1': { 'EAPI': '7', 'RESTRICT': 'primaryuri', 'SRC_URI': '''{scheme}://{host}:{port}/distfiles/bar.txt -> bar {scheme}://{host}:{port}/distfiles/foo.txt -> foo''', }, } loop = SchedulerInterface(global_event_loop()) scheme = 'http' host = '127.0.0.1' content = {} for k, v in distfiles.items(): content['/distfiles/{}.txt'.format(k)] = v with AsyncHTTPServer(host, content, loop) as server: ebuilds_subst = {} for cpv, metadata in ebuilds.items(): metadata = metadata.copy() metadata['SRC_URI'] = metadata['SRC_URI'].format( scheme=scheme, host=host, port=server.server_port) ebuilds_subst[cpv] = metadata playground = ResolverPlayground(ebuilds=ebuilds_subst, distfiles=distfiles) ro_distdir = tempfile.mkdtemp() try: fetchcommand = portage.util.shlex_split(playground.settings['FETCHCOMMAND']) fetch_bin = portage.process.find_binary(fetchcommand[0]) if fetch_bin is None: self.skipTest('FETCHCOMMAND not found: {}'.format(playground.settings['FETCHCOMMAND'])) resumecommand = portage.util.shlex_split(playground.settings['RESUMECOMMAND']) resume_bin = portage.process.find_binary(resumecommand[0]) if resume_bin is None: self.skipTest('RESUMECOMMAND not found: {}'.format(playground.settings['RESUMECOMMAND'])) root_config = playground.trees[playground.eroot]['root_config'] portdb = root_config.trees["porttree"].dbapi settings = config(clone=playground.settings) # Tests only work with one ebuild at a time, so the config # pool only needs a single config instance. class config_pool: @staticmethod def allocate(): return settings @staticmethod def deallocate(settings): pass def async_fetch(pkg, ebuild_path): fetcher = EbuildFetcher(config_pool=config_pool, ebuild_path=ebuild_path, fetchonly=False, fetchall=True, pkg=pkg, scheduler=loop) fetcher.start() return fetcher.async_wait() for cpv in ebuilds: metadata = dict(zip(Package.metadata_keys, portdb.aux_get(cpv, Package.metadata_keys))) pkg = Package(built=False, cpv=cpv, installed=False, metadata=metadata, root_config=root_config, type_name='ebuild') settings.setcpv(pkg) ebuild_path = portdb.findname(pkg.cpv) portage.doebuild_environment(ebuild_path, 'fetch', settings=settings, db=portdb) # Test good files in DISTDIR for k in settings['AA'].split(): os.stat(os.path.join(settings['DISTDIR'], k)) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) # Test digestgen with fetch os.unlink(os.path.join(os.path.dirname(ebuild_path), 'Manifest')) for k in settings['AA'].split(): os.unlink(os.path.join(settings['DISTDIR'], k)) with ForkExecutor(loop=loop) as executor: self.assertTrue(bool(loop.run_until_complete( loop.run_in_executor(executor, functools.partial( digestgen, mysettings=settings, myportdb=portdb))))) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) # Test missing files in DISTDIR for k in settings['AA'].split(): os.unlink(os.path.join(settings['DISTDIR'], k)) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) # Test empty files in DISTDIR for k in settings['AA'].split(): file_path = os.path.join(settings['DISTDIR'], k) with open(file_path, 'wb') as f: pass self.assertEqual(os.stat(file_path).st_size, 0) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) # Test non-empty files containing null bytes in DISTDIR for k in settings['AA'].split(): file_path = os.path.join(settings['DISTDIR'], k) with open(file_path, 'wb') as f: f.write(len(distfiles[k]) * b'\0') self.assertEqual(os.stat(file_path).st_size, len(distfiles[k])) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) # Test PORTAGE_RO_DISTDIRS settings['PORTAGE_RO_DISTDIRS'] = '"{}"'.format(ro_distdir) orig_fetchcommand = settings['FETCHCOMMAND'] orig_resumecommand = settings['RESUMECOMMAND'] try: settings['FETCHCOMMAND'] = settings['RESUMECOMMAND'] = '' for k in settings['AA'].split(): file_path = os.path.join(settings['DISTDIR'], k) os.rename(file_path, os.path.join(ro_distdir, k)) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): file_path = os.path.join(settings['DISTDIR'], k) self.assertTrue(os.path.islink(file_path)) with open(file_path, 'rb') as f: self.assertEqual(f.read(), distfiles[k]) os.unlink(file_path) finally: settings.pop('PORTAGE_RO_DISTDIRS') settings['FETCHCOMMAND'] = orig_fetchcommand settings['RESUMECOMMAND'] = orig_resumecommand # Test local filesystem in GENTOO_MIRRORS orig_mirrors = settings['GENTOO_MIRRORS'] orig_fetchcommand = settings['FETCHCOMMAND'] try: settings['GENTOO_MIRRORS'] = ro_distdir settings['FETCHCOMMAND'] = settings['RESUMECOMMAND'] = '' self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) finally: settings['GENTOO_MIRRORS'] = orig_mirrors settings['FETCHCOMMAND'] = orig_fetchcommand settings['RESUMECOMMAND'] = orig_resumecommand # Test readonly DISTDIR orig_distdir_mode = os.stat(settings['DISTDIR']).st_mode try: os.chmod(settings['DISTDIR'], 0o555) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) finally: os.chmod(settings['DISTDIR'], orig_distdir_mode) # Test parallel-fetch mode settings['PORTAGE_PARALLEL_FETCHONLY'] = '1' try: self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) for k in settings['AA'].split(): os.unlink(os.path.join(settings['DISTDIR'], k)) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) finally: settings.pop('PORTAGE_PARALLEL_FETCHONLY') # Test RESUMECOMMAND orig_resume_min_size = settings['PORTAGE_FETCH_RESUME_MIN_SIZE'] try: settings['PORTAGE_FETCH_RESUME_MIN_SIZE'] = '2' for k in settings['AA'].split(): file_path = os.path.join(settings['DISTDIR'], k) os.unlink(file_path) with open(file_path + _download_suffix, 'wb') as f: f.write(distfiles[k][:2]) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) finally: settings['PORTAGE_FETCH_RESUME_MIN_SIZE'] = orig_resume_min_size finally: shutil.rmtree(ro_distdir) playground.cleanup()
def testDoebuildSpawn(self): ebuild_body = textwrap.dedent(""" pkg_nofetch() { : ; } """) ebuilds = { "sys-apps/portage-2.1": { "EAPI": "2", "IUSE": "build doc epydoc python3 selinux", "KEYWORDS": "x86", "LICENSE": "GPL-2", "RDEPEND": ">=app-shells/bash-3.2_p17 >=dev-lang/python-2.6", "SLOT": "0", "MISC_CONTENT": ebuild_body, } } playground = ResolverPlayground(ebuilds=ebuilds) try: root_config = playground.trees[playground.eroot]["root_config"] portdb = root_config.trees["porttree"].dbapi settings = config(clone=playground.settings) if "__PORTAGE_TEST_HARDLINK_LOCKS" in os.environ: settings["__PORTAGE_TEST_HARDLINK_LOCKS"] = os.environ[ "__PORTAGE_TEST_HARDLINK_LOCKS"] settings.backup_changes("__PORTAGE_TEST_HARDLINK_LOCKS") cpv = "sys-apps/portage-2.1" metadata = dict( zip(Package.metadata_keys, portdb.aux_get(cpv, Package.metadata_keys))) pkg = Package( built=False, cpv=cpv, installed=False, metadata=metadata, root_config=root_config, type_name="ebuild", ) settings.setcpv(pkg) settings["PORTAGE_PYTHON"] = _python_interpreter settings["PORTAGE_BUILDDIR"] = os.path.join( settings["PORTAGE_TMPDIR"], cpv) settings["PYTHONDONTWRITEBYTECODE"] = os.environ.get( "PYTHONDONTWRITEBYTECODE", "") settings["HOME"] = os.path.join(settings["PORTAGE_BUILDDIR"], "homedir") settings["T"] = os.path.join(settings["PORTAGE_BUILDDIR"], "temp") for x in ("PORTAGE_BUILDDIR", "HOME", "T"): os.makedirs(settings[x]) # Create a fake environment, to pretend as if the ebuild # has been sourced already. open(os.path.join(settings["T"], "environment"), "wb").close() scheduler = SchedulerInterface(global_event_loop()) for phase in ("_internal_test", ): # Test EbuildSpawnProcess by calling doebuild.spawn() with # returnpid=False. This case is no longer used by portage # internals since EbuildPhase is used instead and that passes # returnpid=True to doebuild.spawn(). rval = doebuild_spawn( "%s %s" % ( _shell_quote( os.path.join( settings["PORTAGE_BIN_PATH"], os.path.basename(EBUILD_SH_BINARY), )), phase, ), settings, free=1, ) self.assertEqual(rval, os.EX_OK) ebuild_phase = EbuildPhase( background=False, phase=phase, scheduler=scheduler, settings=settings, ) ebuild_phase.start() ebuild_phase.wait() self.assertEqual(ebuild_phase.returncode, os.EX_OK) ebuild_phase = MiscFunctionsProcess( background=False, commands=["success_hooks"], scheduler=scheduler, settings=settings, ) ebuild_phase.start() ebuild_phase.wait() self.assertEqual(ebuild_phase.returncode, os.EX_OK) spawn_nofetch(portdb, portdb.findname(cpv), settings=settings) finally: playground.cleanup()
def getmaskingstatus(mycpv, settings=None, portdb=None): if settings is None: settings = config(clone=portage.settings) if portdb is None: portdb = portage.portdb metadata = None installed = False if not isinstance(mycpv, basestring): # emerge passed in a Package instance pkg = mycpv mycpv = pkg.cpv metadata = pkg.metadata installed = pkg.installed mysplit = catpkgsplit(mycpv) if not mysplit: raise ValueError(_("invalid CPV: %s") % mycpv) if metadata is None: db_keys = list(portdb._aux_cache_keys) try: metadata = dict(zip(db_keys, portdb.aux_get(mycpv, db_keys))) except KeyError: if not portdb.cpv_exists(mycpv): raise return ["corruption"] if "?" in metadata["LICENSE"]: settings.setcpv(mycpv, mydb=metadata) metadata["USE"] = settings["PORTAGE_USE"] else: metadata["USE"] = "" rValue = [] # profile checking if settings._getProfileMaskAtom(mycpv, metadata): rValue.append("profile") # package.mask checking if settings._getMaskAtom(mycpv, metadata): rValue.append("package.mask") # keywords checking eapi = metadata["EAPI"] mygroups = settings._getKeywords(mycpv, metadata) licenses = metadata["LICENSE"] properties = metadata["PROPERTIES"] if eapi.startswith("-"): eapi = eapi[1:] if not eapi_is_supported(eapi): return ["EAPI %s" % eapi] elif _eapi_is_deprecated(eapi) and not installed: return ["EAPI %s" % eapi] egroups = settings.configdict["backupenv"].get("ACCEPT_KEYWORDS", "").split() pgroups = settings["ACCEPT_KEYWORDS"].split() myarch = settings["ARCH"] if pgroups and myarch not in pgroups: """For operating systems other than Linux, ARCH is not necessarily a valid keyword.""" myarch = pgroups[0].lstrip("~") cp = cpv_getkey(mycpv) pkgdict = settings.pkeywordsdict.get(cp) matches = False if pkgdict: cpv_slot_list = ["%s:%s" % (mycpv, metadata["SLOT"])] for atom, pkgkeywords in pkgdict.items(): if match_from_list(atom, cpv_slot_list): matches = True pgroups.extend(pkgkeywords) if matches or egroups: pgroups.extend(egroups) inc_pgroups = set() for x in pgroups: if x.startswith("-"): if x == "-*": inc_pgroups.clear() else: inc_pgroups.discard(x[1:]) else: inc_pgroups.add(x) pgroups = inc_pgroups del inc_pgroups kmask = "missing" if '**' in pgroups: kmask = None else: for keyword in pgroups: if keyword in mygroups: kmask = None break if kmask: for gp in mygroups: if gp == "*": kmask = None break elif gp == "-" + myarch and myarch in pgroups: kmask = "-" + myarch break elif gp == "~" + myarch and myarch in pgroups: kmask = "~" + myarch break try: missing_licenses = settings._getMissingLicenses(mycpv, metadata) if missing_licenses: allowed_tokens = set(["||", "(", ")"]) allowed_tokens.update(missing_licenses) license_split = licenses.split() license_split = [x for x in license_split \ if x in allowed_tokens] msg = license_split[:] msg.append("license(s)") rValue.append(" ".join(msg)) except portage.exception.InvalidDependString as e: rValue.append("LICENSE: " + str(e)) try: missing_properties = settings._getMissingProperties(mycpv, metadata) if missing_properties: allowed_tokens = set(["||", "(", ")"]) allowed_tokens.update(missing_properties) properties_split = properties.split() properties_split = [x for x in properties_split \ if x in allowed_tokens] msg = properties_split[:] msg.append("properties") rValue.append(" ".join(msg)) except portage.exception.InvalidDependString as e: rValue.append("PROPERTIES: " + str(e)) # Only show KEYWORDS masks for installed packages # if they're not masked for any other reason. if kmask and (not installed or not rValue): rValue.append(kmask + " keyword") return rValue
def testEbuildFetch(self): user_config = { "make.conf": ( 'GENTOO_MIRRORS="{scheme}://{host}:{port}"', ), } distfiles = { 'bar': b'bar\n', 'foo': b'foo\n', } ebuilds = { 'dev-libs/A-1': { 'EAPI': '7', 'SRC_URI': '''{scheme}://{host}:{port}/distfiles/bar.txt -> bar {scheme}://{host}:{port}/distfiles/foo.txt -> foo''', }, } loop = SchedulerInterface(global_event_loop()) def run_async(func, *args, **kwargs): with ForkExecutor(loop=loop) as executor: return loop.run_until_complete(loop.run_in_executor(executor, functools.partial(func, *args, **kwargs))) scheme = 'http' host = '127.0.0.1' content = {} content['/distfiles/layout.conf'] = b'[structure]\n0=flat\n' for k, v in distfiles.items(): # mirror path content['/distfiles/{}'.format(k)] = v # upstream path content['/distfiles/{}.txt'.format(k)] = v with AsyncHTTPServer(host, content, loop) as server: ebuilds_subst = {} for cpv, metadata in ebuilds.items(): metadata = metadata.copy() metadata['SRC_URI'] = metadata['SRC_URI'].format( scheme=scheme, host=host, port=server.server_port) ebuilds_subst[cpv] = metadata user_config_subst = user_config.copy() for configname, configdata in user_config.items(): configdata_sub = [] for line in configdata: configdata_sub.append(line.format( scheme=scheme, host=host, port=server.server_port)) user_config_subst[configname] = tuple(configdata_sub) playground = ResolverPlayground(ebuilds=ebuilds_subst, distfiles=distfiles, user_config=user_config_subst) ro_distdir = tempfile.mkdtemp() eubin = os.path.join(playground.eprefix, "usr", "bin") try: fetchcommand = portage.util.shlex_split(playground.settings['FETCHCOMMAND']) fetch_bin = portage.process.find_binary(fetchcommand[0]) if fetch_bin is None: self.skipTest('FETCHCOMMAND not found: {}'.format(playground.settings['FETCHCOMMAND'])) os.symlink(fetch_bin, os.path.join(eubin, os.path.basename(fetch_bin))) resumecommand = portage.util.shlex_split(playground.settings['RESUMECOMMAND']) resume_bin = portage.process.find_binary(resumecommand[0]) if resume_bin is None: self.skipTest('RESUMECOMMAND not found: {}'.format(playground.settings['RESUMECOMMAND'])) if resume_bin != fetch_bin: os.symlink(resume_bin, os.path.join(eubin, os.path.basename(resume_bin))) root_config = playground.trees[playground.eroot]['root_config'] portdb = root_config.trees["porttree"].dbapi settings = config(clone=playground.settings) # Demonstrate that fetch preserves a stale file in DISTDIR when no digests are given. foo_uri = {'foo': ('{scheme}://{host}:{port}/distfiles/foo'.format(scheme=scheme, host=host, port=server.server_port),)} foo_path = os.path.join(settings['DISTDIR'], 'foo') foo_stale_content = b'stale content\n' with open(foo_path, 'wb') as f: f.write(b'stale content\n') self.assertTrue(bool(run_async(fetch, foo_uri, settings, try_mirrors=False))) with open(foo_path, 'rb') as f: self.assertEqual(f.read(), foo_stale_content) with open(foo_path, 'rb') as f: self.assertNotEqual(f.read(), distfiles['foo']) # Use force=True to update the stale file. self.assertTrue(bool(run_async(fetch, foo_uri, settings, try_mirrors=False, force=True))) with open(foo_path, 'rb') as f: self.assertEqual(f.read(), distfiles['foo']) # Test force=True with FEATURES=skiprocheck, using read-only DISTDIR. # FETCHCOMMAND is set to temporarily chmod +w DISTDIR. Note that # FETCHCOMMAND must perform atomic rename itself due to read-only # DISTDIR. with open(foo_path, 'wb') as f: f.write(b'stale content\n') orig_fetchcommand = settings['FETCHCOMMAND'] orig_distdir_mode = os.stat(settings['DISTDIR']).st_mode temp_fetchcommand = os.path.join(eubin, 'fetchcommand') with open(temp_fetchcommand, 'w') as f: f.write(""" set -e URI=$1 DISTDIR=$2 FILE=$3 trap 'chmod a-w "${DISTDIR}"' EXIT chmod ug+w "${DISTDIR}" %s mv -f "${DISTDIR}/${FILE}.__download__" "${DISTDIR}/${FILE}" """ % orig_fetchcommand.replace('${FILE}', '${FILE}.__download__')) settings['FETCHCOMMAND'] = '"%s" "%s" "${URI}" "${DISTDIR}" "${FILE}"' % (BASH_BINARY, temp_fetchcommand) settings.features.add('skiprocheck') settings.features.remove('distlocks') os.chmod(settings['DISTDIR'], 0o555) try: self.assertTrue(bool(run_async(fetch, foo_uri, settings, try_mirrors=False, force=True))) finally: settings['FETCHCOMMAND'] = orig_fetchcommand os.chmod(settings['DISTDIR'], orig_distdir_mode) settings.features.remove('skiprocheck') settings.features.add('distlocks') os.unlink(temp_fetchcommand) with open(foo_path, 'rb') as f: self.assertEqual(f.read(), distfiles['foo']) # Test emirrordist invocation. emirrordist_cmd = (portage._python_interpreter, '-b', '-Wd', os.path.join(self.bindir, 'emirrordist'), '--distfiles', settings['DISTDIR'], '--config-root', settings['EPREFIX'], '--repositories-configuration', settings.repositories.config_string(), '--repo', 'test_repo', '--mirror') env = settings.environ() env['PYTHONPATH'] = ':'.join( filter(None, [PORTAGE_PYM_PATH] + os.environ.get('PYTHONPATH', '').split(':'))) for k in distfiles: os.unlink(os.path.join(settings['DISTDIR'], k)) proc = loop.run_until_complete(asyncio.create_subprocess_exec(*emirrordist_cmd, env=env)) self.assertEqual(loop.run_until_complete(proc.wait()), 0) for k in distfiles: with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) # Tests only work with one ebuild at a time, so the config # pool only needs a single config instance. class config_pool: @staticmethod def allocate(): return settings @staticmethod def deallocate(settings): pass def async_fetch(pkg, ebuild_path): fetcher = EbuildFetcher(config_pool=config_pool, ebuild_path=ebuild_path, fetchonly=False, fetchall=True, pkg=pkg, scheduler=loop) fetcher.start() return fetcher.async_wait() for cpv in ebuilds: metadata = dict(zip(Package.metadata_keys, portdb.aux_get(cpv, Package.metadata_keys))) pkg = Package(built=False, cpv=cpv, installed=False, metadata=metadata, root_config=root_config, type_name='ebuild') settings.setcpv(pkg) ebuild_path = portdb.findname(pkg.cpv) portage.doebuild_environment(ebuild_path, 'fetch', settings=settings, db=portdb) # Test good files in DISTDIR for k in settings['AA'].split(): os.stat(os.path.join(settings['DISTDIR'], k)) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) # Test digestgen with fetch os.unlink(os.path.join(os.path.dirname(ebuild_path), 'Manifest')) for k in settings['AA'].split(): os.unlink(os.path.join(settings['DISTDIR'], k)) with ForkExecutor(loop=loop) as executor: self.assertTrue(bool(loop.run_until_complete( loop.run_in_executor(executor, functools.partial( digestgen, mysettings=settings, myportdb=portdb))))) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) # Test missing files in DISTDIR for k in settings['AA'].split(): os.unlink(os.path.join(settings['DISTDIR'], k)) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) # Test empty files in DISTDIR for k in settings['AA'].split(): file_path = os.path.join(settings['DISTDIR'], k) with open(file_path, 'wb') as f: pass self.assertEqual(os.stat(file_path).st_size, 0) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) # Test non-empty files containing null bytes in DISTDIR for k in settings['AA'].split(): file_path = os.path.join(settings['DISTDIR'], k) with open(file_path, 'wb') as f: f.write(len(distfiles[k]) * b'\0') self.assertEqual(os.stat(file_path).st_size, len(distfiles[k])) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) # Test PORTAGE_RO_DISTDIRS settings['PORTAGE_RO_DISTDIRS'] = '"{}"'.format(ro_distdir) orig_fetchcommand = settings['FETCHCOMMAND'] orig_resumecommand = settings['RESUMECOMMAND'] try: settings['FETCHCOMMAND'] = settings['RESUMECOMMAND'] = '' for k in settings['AA'].split(): file_path = os.path.join(settings['DISTDIR'], k) os.rename(file_path, os.path.join(ro_distdir, k)) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): file_path = os.path.join(settings['DISTDIR'], k) self.assertTrue(os.path.islink(file_path)) with open(file_path, 'rb') as f: self.assertEqual(f.read(), distfiles[k]) os.unlink(file_path) finally: settings.pop('PORTAGE_RO_DISTDIRS') settings['FETCHCOMMAND'] = orig_fetchcommand settings['RESUMECOMMAND'] = orig_resumecommand # Test local filesystem in GENTOO_MIRRORS orig_mirrors = settings['GENTOO_MIRRORS'] orig_fetchcommand = settings['FETCHCOMMAND'] try: settings['GENTOO_MIRRORS'] = ro_distdir settings['FETCHCOMMAND'] = settings['RESUMECOMMAND'] = '' self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) finally: settings['GENTOO_MIRRORS'] = orig_mirrors settings['FETCHCOMMAND'] = orig_fetchcommand settings['RESUMECOMMAND'] = orig_resumecommand # Test readonly DISTDIR orig_distdir_mode = os.stat(settings['DISTDIR']).st_mode try: os.chmod(settings['DISTDIR'], 0o555) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) finally: os.chmod(settings['DISTDIR'], orig_distdir_mode) # Test parallel-fetch mode settings['PORTAGE_PARALLEL_FETCHONLY'] = '1' try: self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) for k in settings['AA'].split(): os.unlink(os.path.join(settings['DISTDIR'], k)) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) finally: settings.pop('PORTAGE_PARALLEL_FETCHONLY') # Test RESUMECOMMAND orig_resume_min_size = settings['PORTAGE_FETCH_RESUME_MIN_SIZE'] try: settings['PORTAGE_FETCH_RESUME_MIN_SIZE'] = '2' for k in settings['AA'].split(): file_path = os.path.join(settings['DISTDIR'], k) os.unlink(file_path) with open(file_path + _download_suffix, 'wb') as f: f.write(distfiles[k][:2]) self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings['AA'].split(): with open(os.path.join(settings['DISTDIR'], k), 'rb') as f: self.assertEqual(f.read(), distfiles[k]) finally: settings['PORTAGE_FETCH_RESUME_MIN_SIZE'] = orig_resume_min_size # Test readonly DISTDIR + skiprocheck, with FETCHCOMMAND set to temporarily chmod DISTDIR orig_fetchcommand = settings['FETCHCOMMAND'] orig_distdir_mode = os.stat(settings['DISTDIR']).st_mode for k in settings['AA'].split(): os.unlink(os.path.join(settings['DISTDIR'], k)) try: os.chmod(settings['DISTDIR'], 0o555) settings['FETCHCOMMAND'] = '"%s" -c "chmod ug+w \\"${DISTDIR}\\"; %s; status=\\$?; chmod a-w \\"${DISTDIR}\\"; exit \\$status"' % (BASH_BINARY, orig_fetchcommand.replace('"', '\\"')) settings.features.add('skiprocheck') settings.features.remove('distlocks') self.assertEqual(loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) finally: settings['FETCHCOMMAND'] = orig_fetchcommand os.chmod(settings['DISTDIR'], orig_distdir_mode) settings.features.remove('skiprocheck') settings.features.add('distlocks') finally: shutil.rmtree(ro_distdir) playground.cleanup()
def testUseExpandIncremental(self): profiles = ( ( "base", { "eapi": ("5", ), "parent": ("..", ), "make.defaults": ( 'INPUT_DEVICES="keyboard mouse"', 'PYTHON_TARGETS="python2_7 python3_3"', ('USE_EXPAND="INPUT_DEVICES PYTHON_TARGETS ' 'VIDEO_CARDS"'), ), }, ), ( "default/linux", { "eapi": ("5", ), "make.defaults": ('VIDEO_CARDS="dummy fbdev v4l"', ) }, ), ( "default/linux/x86", { "eapi": ("5", ), "make.defaults": ( # Test negative incremental for bug 530222. 'PYTHON_TARGETS="-python3_3"', ), "parent": ("../../../base", "../../../mixins/python/3.4", ".."), }, ), ( "mixins/python/3.4", { "eapi": ("5", ), "make.defaults": ('PYTHON_TARGETS="python3_4"', ) }, ), ) # USE_EXPAND variable settings in make.conf will cause # profile settings for the same variable to be discarded # (non-incremental behavior). PMS does not govern make.conf # behavior. user_config = {"make.conf": ('VIDEO_CARDS="intel"', )} ebuilds = { "x11-base/xorg-drivers-1.15": { "EAPI": "5", "IUSE": ("input_devices_keyboard input_devices_mouse " "videos_cards_dummy video_cards_fbdev " "video_cards_v4l video_cards_intel"), }, "sys-apps/portage-2.2.14": { "EAPI": "5", "IUSE": ("python_targets_python2_7 " "python_targets_python3_3 python_targets_python3_4"), }, } package_expected_use = ( ( "x11-base/xorg-drivers-1.15", ( "input_devices_keyboard", "input_devices_mouse", "video_cards_intel", ), ), ( "sys-apps/portage-2.2.14", ("python_targets_python2_7", "python_targets_python3_4"), ), ) playground = ResolverPlayground(debug=False, ebuilds=ebuilds, user_config=user_config) try: repo_dir = playground.settings.repositories.get_location_for_name( "test_repo") profile_root = os.path.join(repo_dir, "profiles") for p, data in profiles: prof_path = os.path.join(profile_root, p) ensure_dirs(prof_path) for k, v in data.items(): with io.open( os.path.join(prof_path, k), mode="w", encoding=_encodings["repo.content"], ) as f: for line in v: f.write("%s\n" % line) # The config must be reloaded in order to account # for the above profile customizations. playground.reload_config() depgraph = playground.run(["=x11-base/xorg-drivers-1.15"]).depgraph settings = config(clone=playground.settings) for cpv, expected_use in package_expected_use: pkg, existing_node = depgraph._select_package( playground.eroot, Atom("=" + cpv)) settings.setcpv(pkg) expected = frozenset(expected_use) got = frozenset(settings["PORTAGE_USE"].split()) self.assertEqual(got, expected, "%s != %s" % (got, expected)) finally: playground.cleanup()
def _testEbuildFetch( self, loop, scheme, host, orig_distfiles, ebuilds, content, server, playground, ro_distdir, ): mirror_layouts = ( ( "[structure]", "0=filename-hash BLAKE2B 8", "1=flat", ), ( "[structure]", "1=filename-hash BLAKE2B 8", "0=flat", ), ( "[structure]", "0=content-hash SHA512 8:8:8", "1=flat", ), ) fetchcommand = portage.util.shlex_split( playground.settings["FETCHCOMMAND"]) fetch_bin = portage.process.find_binary(fetchcommand[0]) if fetch_bin is None: self.skipTest("FETCHCOMMAND not found: {}".format( playground.settings["FETCHCOMMAND"])) eubin = os.path.join(playground.eprefix, "usr", "bin") os.symlink(fetch_bin, os.path.join(eubin, os.path.basename(fetch_bin))) resumecommand = portage.util.shlex_split( playground.settings["RESUMECOMMAND"]) resume_bin = portage.process.find_binary(resumecommand[0]) if resume_bin is None: self.skipTest("RESUMECOMMAND not found: {}".format( playground.settings["RESUMECOMMAND"])) if resume_bin != fetch_bin: os.symlink(resume_bin, os.path.join(eubin, os.path.basename(resume_bin))) root_config = playground.trees[playground.eroot]["root_config"] portdb = root_config.trees["porttree"].dbapi def run_async(func, *args, **kwargs): with ForkExecutor(loop=loop) as executor: return loop.run_until_complete( loop.run_in_executor( executor, functools.partial(func, *args, **kwargs))) for layout_lines in mirror_layouts: settings = config(clone=playground.settings) layout_data = "".join("{}\n".format(line) for line in layout_lines) mirror_conf = MirrorLayoutConfig() mirror_conf.read_from_file(io.StringIO(layout_data)) layouts = mirror_conf.get_all_layouts() content["/distfiles/layout.conf"] = layout_data.encode("utf8") distfiles = {} for k, v in orig_distfiles.items(): filename = DistfileName( k, digests=dict((algo, checksum_str(v, hashname=algo)) for algo in MANIFEST2_HASH_DEFAULTS), ) distfiles[filename] = v # mirror path for layout in layouts: content["/distfiles/" + layout.get_path(filename)] = v # upstream path content["/distfiles/{}.txt".format(k)] = v shutil.rmtree(settings["DISTDIR"]) os.makedirs(settings["DISTDIR"]) with open(os.path.join(settings["DISTDIR"], "layout.conf"), "wt") as f: f.write(layout_data) if any( isinstance(layout, ContentHashLayout) for layout in layouts): content_db = os.path.join(playground.eprefix, "var/db/emirrordist/content.db") os.makedirs(os.path.dirname(content_db), exist_ok=True) try: os.unlink(content_db) except OSError: pass else: content_db = None # Demonstrate that fetch preserves a stale file in DISTDIR when no digests are given. foo_uri = { "foo": ("{scheme}://{host}:{port}/distfiles/foo".format( scheme=scheme, host=host, port=server.server_port), ) } foo_path = os.path.join(settings["DISTDIR"], "foo") foo_stale_content = b"stale content\n" with open(foo_path, "wb") as f: f.write(b"stale content\n") self.assertTrue( bool(run_async(fetch, foo_uri, settings, try_mirrors=False))) with open(foo_path, "rb") as f: self.assertEqual(f.read(), foo_stale_content) with open(foo_path, "rb") as f: self.assertNotEqual(f.read(), distfiles["foo"]) # Use force=True to update the stale file. self.assertTrue( bool( run_async(fetch, foo_uri, settings, try_mirrors=False, force=True))) with open(foo_path, "rb") as f: self.assertEqual(f.read(), distfiles["foo"]) # Test force=True with FEATURES=skiprocheck, using read-only DISTDIR. # FETCHCOMMAND is set to temporarily chmod +w DISTDIR. Note that # FETCHCOMMAND must perform atomic rename itself due to read-only # DISTDIR. with open(foo_path, "wb") as f: f.write(b"stale content\n") orig_fetchcommand = settings["FETCHCOMMAND"] orig_distdir_mode = os.stat(settings["DISTDIR"]).st_mode temp_fetchcommand = os.path.join(eubin, "fetchcommand") with open(temp_fetchcommand, "w") as f: f.write(""" set -e URI=$1 DISTDIR=$2 FILE=$3 trap 'chmod a-w "${DISTDIR}"' EXIT chmod ug+w "${DISTDIR}" %s mv -f "${DISTDIR}/${FILE}.__download__" "${DISTDIR}/${FILE}" """ % orig_fetchcommand.replace("${FILE}", "${FILE}.__download__")) settings[ "FETCHCOMMAND"] = '"%s" "%s" "${URI}" "${DISTDIR}" "${FILE}"' % ( BASH_BINARY, temp_fetchcommand, ) settings.features.add("skiprocheck") settings.features.remove("distlocks") os.chmod(settings["DISTDIR"], 0o555) try: self.assertTrue( bool( run_async(fetch, foo_uri, settings, try_mirrors=False, force=True))) finally: settings["FETCHCOMMAND"] = orig_fetchcommand os.chmod(settings["DISTDIR"], orig_distdir_mode) settings.features.remove("skiprocheck") settings.features.add("distlocks") os.unlink(temp_fetchcommand) with open(foo_path, "rb") as f: self.assertEqual(f.read(), distfiles["foo"]) # Test emirrordist invocation. emirrordist_cmd = ( portage._python_interpreter, "-b", "-Wd", os.path.join(self.bindir, "emirrordist"), "--distfiles", settings["DISTDIR"], "--config-root", settings["EPREFIX"], "--delete", "--repositories-configuration", settings.repositories.config_string(), "--repo", "test_repo", "--mirror", ) if content_db is not None: emirrordist_cmd = emirrordist_cmd + ( "--content-db", content_db, ) env = settings.environ() env["PYTHONPATH"] = ":".join( filter( None, [PORTAGE_PYM_PATH] + os.environ.get("PYTHONPATH", "").split(":"), )) for k in distfiles: try: os.unlink(os.path.join(settings["DISTDIR"], k)) except OSError: pass proc = loop.run_until_complete( asyncio.create_subprocess_exec(*emirrordist_cmd, env=env)) self.assertEqual(loop.run_until_complete(proc.wait()), 0) for k in distfiles: with open( os.path.join(settings["DISTDIR"], layouts[0].get_path(k)), "rb") as f: self.assertEqual(f.read(), distfiles[k]) if content_db is not None: loop.run_until_complete( self._test_content_db( emirrordist_cmd, env, layouts, content_db, distfiles, settings, portdb, )) # Tests only work with one ebuild at a time, so the config # pool only needs a single config instance. class config_pool: @staticmethod def allocate(): return settings @staticmethod def deallocate(settings): pass def async_fetch(pkg, ebuild_path): fetcher = EbuildFetcher( config_pool=config_pool, ebuild_path=ebuild_path, fetchonly=False, fetchall=True, pkg=pkg, scheduler=loop, ) fetcher.start() return fetcher.async_wait() for cpv in ebuilds: metadata = dict( zip( Package.metadata_keys, portdb.aux_get(cpv, Package.metadata_keys), )) pkg = Package( built=False, cpv=cpv, installed=False, metadata=metadata, root_config=root_config, type_name="ebuild", ) settings.setcpv(pkg) ebuild_path = portdb.findname(pkg.cpv) portage.doebuild_environment(ebuild_path, "fetch", settings=settings, db=portdb) # Test good files in DISTDIR for k in settings["AA"].split(): os.stat(os.path.join(settings["DISTDIR"], k)) self.assertEqual( loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings["AA"].split(): with open(os.path.join(settings["DISTDIR"], k), "rb") as f: self.assertEqual(f.read(), distfiles[k]) # Test digestgen with fetch os.unlink( os.path.join(os.path.dirname(ebuild_path), "Manifest")) for k in settings["AA"].split(): os.unlink(os.path.join(settings["DISTDIR"], k)) with ForkExecutor(loop=loop) as executor: self.assertTrue( bool( loop.run_until_complete( loop.run_in_executor( executor, functools.partial(digestgen, mysettings=settings, myportdb=portdb), )))) for k in settings["AA"].split(): with open(os.path.join(settings["DISTDIR"], k), "rb") as f: self.assertEqual(f.read(), distfiles[k]) # Test missing files in DISTDIR for k in settings["AA"].split(): os.unlink(os.path.join(settings["DISTDIR"], k)) self.assertEqual( loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings["AA"].split(): with open(os.path.join(settings["DISTDIR"], k), "rb") as f: self.assertEqual(f.read(), distfiles[k]) # Test empty files in DISTDIR for k in settings["AA"].split(): file_path = os.path.join(settings["DISTDIR"], k) with open(file_path, "wb") as f: pass self.assertEqual(os.stat(file_path).st_size, 0) self.assertEqual( loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings["AA"].split(): with open(os.path.join(settings["DISTDIR"], k), "rb") as f: self.assertEqual(f.read(), distfiles[k]) # Test non-empty files containing null bytes in DISTDIR for k in settings["AA"].split(): file_path = os.path.join(settings["DISTDIR"], k) with open(file_path, "wb") as f: f.write(len(distfiles[k]) * b"\0") self.assertEqual( os.stat(file_path).st_size, len(distfiles[k])) self.assertEqual( loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings["AA"].split(): with open(os.path.join(settings["DISTDIR"], k), "rb") as f: self.assertEqual(f.read(), distfiles[k]) # Test PORTAGE_RO_DISTDIRS settings["PORTAGE_RO_DISTDIRS"] = '"{}"'.format(ro_distdir) orig_fetchcommand = settings["FETCHCOMMAND"] orig_resumecommand = settings["RESUMECOMMAND"] try: settings["FETCHCOMMAND"] = settings["RESUMECOMMAND"] = "" for k in settings["AA"].split(): file_path = os.path.join(settings["DISTDIR"], k) os.rename(file_path, os.path.join(ro_distdir, k)) self.assertEqual( loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings["AA"].split(): file_path = os.path.join(settings["DISTDIR"], k) self.assertTrue(os.path.islink(file_path)) with open(file_path, "rb") as f: self.assertEqual(f.read(), distfiles[k]) os.unlink(file_path) finally: settings.pop("PORTAGE_RO_DISTDIRS") settings["FETCHCOMMAND"] = orig_fetchcommand settings["RESUMECOMMAND"] = orig_resumecommand # Test local filesystem in GENTOO_MIRRORS orig_mirrors = settings["GENTOO_MIRRORS"] orig_fetchcommand = settings["FETCHCOMMAND"] try: settings["GENTOO_MIRRORS"] = ro_distdir settings["FETCHCOMMAND"] = settings["RESUMECOMMAND"] = "" self.assertEqual( loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings["AA"].split(): with open(os.path.join(settings["DISTDIR"], k), "rb") as f: self.assertEqual(f.read(), distfiles[k]) finally: settings["GENTOO_MIRRORS"] = orig_mirrors settings["FETCHCOMMAND"] = orig_fetchcommand settings["RESUMECOMMAND"] = orig_resumecommand # Test readonly DISTDIR orig_distdir_mode = os.stat(settings["DISTDIR"]).st_mode try: os.chmod(settings["DISTDIR"], 0o555) self.assertEqual( loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings["AA"].split(): with open(os.path.join(settings["DISTDIR"], k), "rb") as f: self.assertEqual(f.read(), distfiles[k]) finally: os.chmod(settings["DISTDIR"], orig_distdir_mode) # Test parallel-fetch mode settings["PORTAGE_PARALLEL_FETCHONLY"] = "1" try: self.assertEqual( loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings["AA"].split(): with open(os.path.join(settings["DISTDIR"], k), "rb") as f: self.assertEqual(f.read(), distfiles[k]) for k in settings["AA"].split(): os.unlink(os.path.join(settings["DISTDIR"], k)) self.assertEqual( loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings["AA"].split(): with open(os.path.join(settings["DISTDIR"], k), "rb") as f: self.assertEqual(f.read(), distfiles[k]) finally: settings.pop("PORTAGE_PARALLEL_FETCHONLY") # Test RESUMECOMMAND orig_resume_min_size = settings[ "PORTAGE_FETCH_RESUME_MIN_SIZE"] try: settings["PORTAGE_FETCH_RESUME_MIN_SIZE"] = "2" for k in settings["AA"].split(): file_path = os.path.join(settings["DISTDIR"], k) os.unlink(file_path) with open(file_path + _download_suffix, "wb") as f: f.write(distfiles[k][:2]) self.assertEqual( loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) for k in settings["AA"].split(): with open(os.path.join(settings["DISTDIR"], k), "rb") as f: self.assertEqual(f.read(), distfiles[k]) finally: settings[ "PORTAGE_FETCH_RESUME_MIN_SIZE"] = orig_resume_min_size # Test readonly DISTDIR + skiprocheck, with FETCHCOMMAND set to temporarily chmod DISTDIR orig_fetchcommand = settings["FETCHCOMMAND"] orig_distdir_mode = os.stat(settings["DISTDIR"]).st_mode for k in settings["AA"].split(): os.unlink(os.path.join(settings["DISTDIR"], k)) try: os.chmod(settings["DISTDIR"], 0o555) settings["FETCHCOMMAND"] = ( '"%s" -c "chmod ug+w \\"${DISTDIR}\\"; %s; status=\\$?; chmod a-w \\"${DISTDIR}\\"; exit \\$status"' % (BASH_BINARY, orig_fetchcommand.replace('"', '\\"'))) settings.features.add("skiprocheck") settings.features.remove("distlocks") self.assertEqual( loop.run_until_complete(async_fetch(pkg, ebuild_path)), 0) finally: settings["FETCHCOMMAND"] = orig_fetchcommand os.chmod(settings["DISTDIR"], orig_distdir_mode) settings.features.remove("skiprocheck") settings.features.add("distlocks")
def testDoebuildSpawn(self): ebuild_body = textwrap.dedent(""" pkg_nofetch() { : ; } """) ebuilds = { 'sys-apps/portage-2.1': { 'EAPI' : '2', 'IUSE' : 'build doc epydoc python3 selinux', 'KEYWORDS' : 'x86', 'LICENSE' : 'GPL-2', 'RDEPEND' : '>=app-shells/bash-3.2_p17 >=dev-lang/python-2.6', 'SLOT' : '0', "MISC_CONTENT": ebuild_body, } } playground = ResolverPlayground(ebuilds=ebuilds) try: root_config = playground.trees[playground.eroot]['root_config'] portdb = root_config.trees["porttree"].dbapi settings = config(clone=playground.settings) if "__PORTAGE_TEST_HARDLINK_LOCKS" in os.environ: settings["__PORTAGE_TEST_HARDLINK_LOCKS"] = \ os.environ["__PORTAGE_TEST_HARDLINK_LOCKS"] settings.backup_changes("__PORTAGE_TEST_HARDLINK_LOCKS") cpv = 'sys-apps/portage-2.1' metadata = dict(zip(Package.metadata_keys, portdb.aux_get(cpv, Package.metadata_keys))) pkg = Package(built=False, cpv=cpv, installed=False, metadata=metadata, root_config=root_config, type_name='ebuild') settings.setcpv(pkg) settings['PORTAGE_PYTHON'] = _python_interpreter settings['PORTAGE_BUILDDIR'] = os.path.join( settings['PORTAGE_TMPDIR'], cpv) settings['PYTHONDONTWRITEBYTECODE'] = os.environ.get('PYTHONDONTWRITEBYTECODE', '') settings['T'] = os.path.join( settings['PORTAGE_BUILDDIR'], 'temp') for x in ('PORTAGE_BUILDDIR', 'T'): os.makedirs(settings[x]) # Create a fake environment, to pretend as if the ebuild # has been sourced already. open(os.path.join(settings['T'], 'environment'), 'wb').close() scheduler = SchedulerInterface(global_event_loop()) for phase in ('_internal_test',): # Test EbuildSpawnProcess by calling doebuild.spawn() with # returnpid=False. This case is no longer used by portage # internals since EbuildPhase is used instead and that passes # returnpid=True to doebuild.spawn(). rval = doebuild_spawn("%s %s" % (_shell_quote( os.path.join(settings["PORTAGE_BIN_PATH"], os.path.basename(EBUILD_SH_BINARY))), phase), settings, free=1) self.assertEqual(rval, os.EX_OK) ebuild_phase = EbuildPhase(background=False, phase=phase, scheduler=scheduler, settings=settings) ebuild_phase.start() ebuild_phase.wait() self.assertEqual(ebuild_phase.returncode, os.EX_OK) ebuild_phase = MiscFunctionsProcess(background=False, commands=['success_hooks'], scheduler=scheduler, settings=settings) ebuild_phase.start() ebuild_phase.wait() self.assertEqual(ebuild_phase.returncode, os.EX_OK) spawn_nofetch(portdb, portdb.findname(cpv), settings=settings) finally: playground.cleanup()
def digestgen(myarchives=None, mysettings=None, overwrite=None, manifestonly=None, myportdb=None): """ Generates a digest file if missing. Fetches files if necessary. NOTE: myarchives and mysettings used to be positional arguments, so their order must be preserved for backward compatibility. @param mysettings: the ebuild config (mysettings["O"] must correspond to the ebuild's parent directory) @type mysettings: config @param myportdb: a portdbapi instance @type myportdb: portdbapi @rtype: int @returns: 1 on success and 0 on failure """ if mysettings is None: raise TypeError("portage.digestgen(): missing" + \ " required 'mysettings' parameter") if myportdb is None: warnings.warn( "portage.digestgen() called without 'myportdb' parameter", DeprecationWarning, stacklevel=2) myportdb = portage.portdb if overwrite is not None: warnings.warn("portage.digestgen() called with " + \ "deprecated 'overwrite' parameter", DeprecationWarning, stacklevel=2) if manifestonly is not None: warnings.warn("portage.digestgen() called with " + \ "deprecated 'manifestonly' parameter", DeprecationWarning, stacklevel=2) try: portage._doebuild_manifest_exempt_depend += 1 distfiles_map = {} fetchlist_dict = FetchlistDict(mysettings["O"], mysettings, myportdb) for cpv in fetchlist_dict: try: for myfile in fetchlist_dict[cpv]: distfiles_map.setdefault(myfile, []).append(cpv) except InvalidDependString as e: writemsg("!!! %s\n" % str(e), noiselevel=-1) del e return 0 mytree = os.path.dirname(os.path.dirname(mysettings["O"])) manifest1_compat = False mf = Manifest(mysettings["O"], mysettings["DISTDIR"], fetchlist_dict=fetchlist_dict, manifest1_compat=manifest1_compat) # Don't require all hashes since that can trigger excessive # fetches when sufficient digests already exist. To ease transition # while Manifest 1 is being removed, only require hashes that will # exist before and after the transition. required_hash_types = set() required_hash_types.add("size") required_hash_types.add(MANIFEST2_REQUIRED_HASH) dist_hashes = mf.fhashdict.get("DIST", {}) # To avoid accidental regeneration of digests with the incorrect # files (such as partially downloaded files), trigger the fetch # code if the file exists and it's size doesn't match the current # manifest entry. If there really is a legitimate reason for the # digest to change, `ebuild --force digest` can be used to avoid # triggering this code (or else the old digests can be manually # removed from the Manifest). missing_files = [] for myfile in distfiles_map: myhashes = dist_hashes.get(myfile) if not myhashes: try: st = os.stat(os.path.join(mysettings["DISTDIR"], myfile)) except OSError: st = None if st is None or st.st_size == 0: missing_files.append(myfile) continue size = myhashes.get("size") try: st = os.stat(os.path.join(mysettings["DISTDIR"], myfile)) except OSError as e: if e.errno != errno.ENOENT: raise del e if size == 0: missing_files.append(myfile) continue if required_hash_types.difference(myhashes): missing_files.append(myfile) continue else: if st.st_size == 0 or size is not None and size != st.st_size: missing_files.append(myfile) continue if missing_files: mytree = os.path.realpath( os.path.dirname(os.path.dirname(mysettings["O"]))) fetch_settings = config(clone=mysettings) debug = mysettings.get("PORTAGE_DEBUG") == "1" for myfile in missing_files: uris = set() for cpv in distfiles_map[myfile]: myebuild = os.path.join(mysettings["O"], catsplit(cpv)[1] + ".ebuild") # for RESTRICT=fetch, mirror, etc... doebuild_environment(myebuild, "fetch", mysettings["ROOT"], fetch_settings, debug, 1, myportdb) uris.update( myportdb.getFetchMap(cpv, mytree=mytree)[myfile]) fetch_settings["A"] = myfile # for use by pkg_nofetch() try: st = os.stat(os.path.join(mysettings["DISTDIR"], myfile)) except OSError: st = None if not fetch({myfile: uris}, fetch_settings): writemsg(_("!!! Fetch failed for %s, can't update " "Manifest\n") % myfile, noiselevel=-1) if myfile in dist_hashes and \ st is not None and st.st_size > 0: # stat result is obtained before calling fetch(), # since fetch may rename the existing file if the # digest does not match. writemsg(_("!!! If you would like to " "forcefully replace the existing " "Manifest entry\n!!! for %s, use " "the following command:\n") % myfile + \ "!!! " + colorize("INFORM", "ebuild --force %s manifest" % \ os.path.basename(myebuild)) + "\n", noiselevel=-1) return 0 writemsg_stdout(_(">>> Creating Manifest for %s\n") % mysettings["O"]) try: mf.create(assumeDistHashesSometimes=True, assumeDistHashesAlways=("assume-digests" in mysettings.features)) except FileNotFound as e: writemsg(_("!!! File %s doesn't exist, can't update " "Manifest\n") % e, noiselevel=-1) return 0 except PortagePackageException as e: writemsg(("!!! %s\n") % (e, ), noiselevel=-1) return 0 try: mf.write(sign=False) except PermissionDenied as e: writemsg(_("!!! Permission Denied: %s\n") % (e, ), noiselevel=-1) return 0 if "assume-digests" not in mysettings.features: distlist = list(mf.fhashdict.get("DIST", {})) distlist.sort() auto_assumed = [] for filename in distlist: if not os.path.exists( os.path.join(mysettings["DISTDIR"], filename)): auto_assumed.append(filename) if auto_assumed: mytree = os.path.realpath( os.path.dirname(os.path.dirname(mysettings["O"]))) cp = os.path.sep.join(mysettings["O"].split(os.path.sep)[-2:]) pkgs = myportdb.cp_list(cp, mytree=mytree) pkgs.sort() writemsg_stdout(" digest.assumed" + colorize("WARN", str(len(auto_assumed)).rjust(18)) + "\n") for pkg_key in pkgs: fetchlist = myportdb.getFetchMap(pkg_key, mytree=mytree) pv = pkg_key.split("/")[1] for filename in auto_assumed: if filename in fetchlist: writemsg_stdout(" %s::%s\n" % (pv, filename)) return 1 finally: portage._doebuild_manifest_exempt_depend -= 1
def testDoebuildSpawn(self): ebuild_body = textwrap.dedent(""" pkg_nofetch() { : ; } """) ebuilds = { 'sys-apps/portage-2.1': { 'EAPI': '2', 'IUSE': 'build doc epydoc python3 selinux', 'KEYWORDS': 'x86', 'LICENSE': 'GPL-2', 'RDEPEND': '>=app-shells/bash-3.2_p17 >=dev-lang/python-2.6', 'SLOT': '0', "MISC_CONTENT": ebuild_body, } } playground = ResolverPlayground(ebuilds=ebuilds) try: root_config = playground.trees[playground.eroot]['root_config'] portdb = root_config.trees["porttree"].dbapi settings = config(clone=playground.settings) if "__PORTAGE_TEST_HARDLINK_LOCKS" in os.environ: settings["__PORTAGE_TEST_HARDLINK_LOCKS"] = \ os.environ["__PORTAGE_TEST_HARDLINK_LOCKS"] settings.backup_changes("__PORTAGE_TEST_HARDLINK_LOCKS") cpv = 'sys-apps/portage-2.1' metadata = dict( zip(Package.metadata_keys, portdb.aux_get(cpv, Package.metadata_keys))) pkg = Package(built=False, cpv=cpv, installed=False, metadata=metadata, root_config=root_config, type_name='ebuild') settings.setcpv(pkg) settings['PORTAGE_PYTHON'] = _python_interpreter settings['PORTAGE_BUILDDIR'] = os.path.join( settings['PORTAGE_TMPDIR'], cpv) settings['T'] = os.path.join(settings['PORTAGE_BUILDDIR'], 'temp') for x in ('PORTAGE_BUILDDIR', 'T'): os.makedirs(settings[x]) # Create a fake environment, to pretend as if the ebuild # has been sourced already. open(os.path.join(settings['T'], 'environment'), 'wb').close() scheduler = SchedulerInterface(global_event_loop()) for phase in ('_internal_test', ): # Test EbuildSpawnProcess by calling doebuild.spawn() with # returnpid=False. This case is no longer used by portage # internals since EbuildPhase is used instead and that passes # returnpid=True to doebuild.spawn(). rval = doebuild_spawn("%s %s" % (_shell_quote( os.path.join(settings["PORTAGE_BIN_PATH"], os.path.basename(EBUILD_SH_BINARY))), phase), settings, free=1) self.assertEqual(rval, os.EX_OK) ebuild_phase = EbuildPhase(background=False, phase=phase, scheduler=scheduler, settings=settings) ebuild_phase.start() ebuild_phase.wait() self.assertEqual(ebuild_phase.returncode, os.EX_OK) ebuild_phase = MiscFunctionsProcess(background=False, commands=['success_hooks'], scheduler=scheduler, settings=settings) ebuild_phase.start() ebuild_phase.wait() self.assertEqual(ebuild_phase.returncode, os.EX_OK) spawn_nofetch(portdb, portdb.findname(cpv), settings=settings) finally: playground.cleanup()
def __init__( self, args: MkosiArgs, root: Path, do_run_build_script: bool, ) -> None: ret = self.try_import_portage() from portage.package.ebuild.config import config # type: ignore self.portage_cfg = config(config_root=str(root), target_root=str(root), sysroot=str(root), eprefix=None) PORTAGE_MISCONFIGURED_MSG = "You have portage(5) installed but it's probably missing defaults, bailing out" # we check for PORTDIR, but we could check for any other one if self.portage_cfg['PORTDIR'] is None: die(PORTAGE_MISCONFIGURED_MSG) self.profile_path = root / ret["profile_path"] self.custom_profile_path = root / ret["custom_profile_path"] self.ebuild_sh_env_dir = root / ret["ebuild_sh_env_dir"] self.portage_cfg_dir = root / ret["portage_cfg_dir"] self.portage_cfg_dir.mkdir(parents=True, exist_ok=True) self.DEFAULT_NSPAWN_PARAMS = [ "--capability=CAP_SYS_ADMIN,CAP_MKNOD", f"--bind={self.portage_cfg['PORTDIR']}", f"--bind={self.portage_cfg['DISTDIR']}", f"--bind={self.portage_cfg['PKGDIR']}", ] jobs = os.cpu_count() or 1 self.emerge_default_opts = [ "--buildpkg=y", "--usepkg=y", "--keep-going=y", f"--jobs={jobs}", f"--load-average={jobs-1}", "--nospinner", ] if "build-script" in ARG_DEBUG: self.emerge_default_opts += [ "--verbose", "--quiet=n", "--quiet-fail=n" ] else: self.emerge_default_opts += ["--quiet-build", "--quiet"] self.arch, _ = ARCHITECTURES[args.architecture or "x86_64"] ####################################################################### # GENTOO_UPSTREAM : we only support systemd profiles! and only the # no-multilib flavour of those, for now; # GENTOO_UPSTREAM : wait for fix upstream: # https://bugs.gentoo.org/792081 ####################################################################### # GENTOO_DONTMOVE : could be done inside set_profile, however # stage3_fetch() will be needing this if we want to allow users to pick # profile ####################################################################### self.arch_profile = Path( f"profiles/default/linux/{self.arch}/{args.release}/systemd") self.pkgs_sys = ["@world"] self.pkgs_fs = ["sys-fs/dosfstools"] if args.output_format in (OutputFormat.subvolume, OutputFormat.gpt_btrfs): self.pkgs_fs += ["sys-fs/btrfs-progs"] elif args.output_format == OutputFormat.gpt_xfs: self.pkgs_fs += ["sys-fs/xfsprogs"] elif args.output_format == OutputFormat.gpt_squashfs: self.pkgs_fs += ["sys-fs/squashfs-tools"] if args.encrypt: self.pkgs_fs += ["cryptsetup", "device-mapper"] self.grub_platforms = [] if not do_run_build_script and args.bootable: if args.get_partition(PartitionIdentifier.esp): self.pkgs_boot = ["sys-kernel/installkernel-systemd-boot"] elif args.get_partition(PartitionIdentifier.bios): self.pkgs_boot = ["sys-boot/grub"] self.grub_platforms = ["coreboot", "qemu", "pc"] self.pkgs_boot += [ "sys-kernel/gentoo-kernel-bin", "sys-firmware/edk2-ovmf" ] # GENTOO_DONTMOVE: self.grub_platforms, for instance, must be set self.emerge_vars = { "BOOTSTRAP_USE": " ".join(self.portage_use_flags), "FEATURES": " ".join(self.portage_features), "GRUB_PLATFORMS": " ".join(self.grub_platforms), "UNINSTALL_IGNORE": " ".join(self.UNINSTALL_IGNORE), "USE": " ".join(self.portage_use_flags), } self.sync_portage_tree(args, root) self.set_profile(args) self.set_default_repo() self.unmask_arch() self.provide_patches() self.set_useflags() self.mkosi_conf() self.baselayout(args, root) self.fetch_fix_stage3(args, root) self.update_stage3(args, root) self.depclean(args, root)
def getmaskingstatus(mycpv, settings=None, portdb=None): if settings is None: settings = config(clone=portage.settings) if portdb is None: portdb = portage.portdb metadata = None installed = False if not isinstance(mycpv, basestring): # emerge passed in a Package instance pkg = mycpv mycpv = pkg.cpv metadata = pkg.metadata installed = pkg.installed mysplit = catpkgsplit(mycpv) if not mysplit: raise ValueError(_("invalid CPV: %s") % mycpv) if metadata is None: db_keys = list(portdb._aux_cache_keys) try: metadata = dict(zip(db_keys, portdb.aux_get(mycpv, db_keys))) except KeyError: if not portdb.cpv_exists(mycpv): raise return ["corruption"] if "?" in metadata["LICENSE"]: settings.setcpv(mycpv, mydb=metadata) metadata["USE"] = settings["PORTAGE_USE"] else: metadata["USE"] = "" rValue = [] # profile checking if settings._getProfileMaskAtom(mycpv, metadata): rValue.append("profile") # package.mask checking if settings._getMaskAtom(mycpv, metadata): rValue.append("package.mask") # keywords checking eapi = metadata["EAPI"] mygroups = settings._getKeywords(mycpv, metadata) licenses = metadata["LICENSE"] properties = metadata["PROPERTIES"] if eapi.startswith("-"): eapi = eapi[1:] if not eapi_is_supported(eapi): return ["EAPI %s" % eapi] elif _eapi_is_deprecated(eapi) and not installed: return ["EAPI %s" % eapi] egroups = settings.configdict["backupenv"].get( "ACCEPT_KEYWORDS", "").split() pgroups = settings["ACCEPT_KEYWORDS"].split() myarch = settings["ARCH"] if pgroups and myarch not in pgroups: """For operating systems other than Linux, ARCH is not necessarily a valid keyword.""" myarch = pgroups[0].lstrip("~") cp = cpv_getkey(mycpv) pkgdict = settings.pkeywordsdict.get(cp) matches = False if pkgdict: cpv_slot_list = ["%s:%s" % (mycpv, metadata["SLOT"])] for atom, pkgkeywords in pkgdict.items(): if match_from_list(atom, cpv_slot_list): matches = True pgroups.extend(pkgkeywords) if matches or egroups: pgroups.extend(egroups) inc_pgroups = set() for x in pgroups: if x.startswith("-"): if x == "-*": inc_pgroups.clear() else: inc_pgroups.discard(x[1:]) else: inc_pgroups.add(x) pgroups = inc_pgroups del inc_pgroups kmask = "missing" if '**' in pgroups: kmask = None else: for keyword in pgroups: if keyword in mygroups: kmask = None break if kmask: for gp in mygroups: if gp=="*": kmask=None break elif gp=="-"+myarch and myarch in pgroups: kmask="-"+myarch break elif gp=="~"+myarch and myarch in pgroups: kmask="~"+myarch break try: missing_licenses = settings._getMissingLicenses(mycpv, metadata) if missing_licenses: allowed_tokens = set(["||", "(", ")"]) allowed_tokens.update(missing_licenses) license_split = licenses.split() license_split = [x for x in license_split \ if x in allowed_tokens] msg = license_split[:] msg.append("license(s)") rValue.append(" ".join(msg)) except portage.exception.InvalidDependString as e: rValue.append("LICENSE: "+str(e)) try: missing_properties = settings._getMissingProperties(mycpv, metadata) if missing_properties: allowed_tokens = set(["||", "(", ")"]) allowed_tokens.update(missing_properties) properties_split = properties.split() properties_split = [x for x in properties_split \ if x in allowed_tokens] msg = properties_split[:] msg.append("properties") rValue.append(" ".join(msg)) except portage.exception.InvalidDependString as e: rValue.append("PROPERTIES: "+str(e)) # Only show KEYWORDS masks for installed packages # if they're not masked for any other reason. if kmask and (not installed or not rValue): rValue.append(kmask+" keyword") return rValue
def spawn_nofetch(portdb, ebuild_path, settings=None, fd_pipes=None): """ This spawns pkg_nofetch if appropriate. The settings parameter is useful only if setcpv has already been called in order to cache metadata. It will be cloned internally, in order to prevent any changes from interfering with the calling code. If settings is None then a suitable config instance will be acquired from the given portdbapi instance. Do not use the settings parameter unless setcpv has been called on the given instance, since otherwise it's possible to trigger issues like bug #408817 due to fragile assumptions involving the config state inside doebuild_environment(). A private PORTAGE_BUILDDIR will be created and cleaned up, in order to avoid any interference with any other processes. If PORTAGE_TMPDIR is writable, that will be used, otherwise the default directory for the tempfile module will be used. We only call the pkg_nofetch phase if either RESTRICT=fetch is set or the package has explicitly overridden the default pkg_nofetch implementation. This allows specialized messages to be displayed for problematic packages even though they do not set RESTRICT=fetch (bug #336499). This function does nothing if the PORTAGE_PARALLEL_FETCHONLY variable is set in the config instance. """ if settings is None: settings = config(clone=portdb.settings) else: settings = config(clone=settings) if 'PORTAGE_PARALLEL_FETCHONLY' in settings: return os.EX_OK # We must create our private PORTAGE_TMPDIR before calling # doebuild_environment(), since lots of variables such # as PORTAGE_BUILDDIR refer to paths inside PORTAGE_TMPDIR. portage_tmpdir = settings.get('PORTAGE_TMPDIR') if not portage_tmpdir or not os.access(portage_tmpdir, os.W_OK): portage_tmpdir = None private_tmpdir = tempfile.mkdtemp(dir=portage_tmpdir) settings['PORTAGE_TMPDIR'] = private_tmpdir settings.backup_changes('PORTAGE_TMPDIR') # private temp dir was just created, so it's not locked yet settings.pop('PORTAGE_BUILDDIR_LOCKED', None) try: doebuild_environment(ebuild_path, 'nofetch', settings=settings, db=portdb) restrict = settings['PORTAGE_RESTRICT'].split() defined_phases = settings['DEFINED_PHASES'].split() if not defined_phases: # When DEFINED_PHASES is undefined, assume all # phases are defined. defined_phases = EBUILD_PHASES if 'fetch' not in restrict and \ 'nofetch' not in defined_phases: return os.EX_OK prepare_build_dirs(settings=settings) ebuild_phase = EbuildPhase( background=False, phase='nofetch', scheduler=SchedulerInterface( portage._internal_caller and global_event_loop() or EventLoop(main=False)), fd_pipes=fd_pipes, settings=settings) ebuild_phase.start() ebuild_phase.wait() elog_process(settings.mycpv, settings) finally: shutil.rmtree(private_tmpdir) return ebuild_phase.returncode
def spawn_nofetch(portdb, ebuild_path, settings=None): """ This spawns pkg_nofetch if appropriate. The settings parameter is useful only if setcpv has already been called in order to cache metadata. It will be cloned internally, in order to prevent any changes from interfering with the calling code. If settings is None then a suitable config instance will be acquired from the given portdbapi instance. A private PORTAGE_BUILDDIR will be created and cleaned up, in order to avoid any interference with any other processes. If PORTAGE_TMPDIR is writable, that will be used, otherwise the default directory for the tempfile module will be used. We only call the pkg_nofetch phase if either RESTRICT=fetch is set or the package has explicitly overridden the default pkg_nofetch implementation. This allows specialized messages to be displayed for problematic packages even though they do not set RESTRICT=fetch (bug #336499). This function does nothing if the PORTAGE_PARALLEL_FETCHONLY variable is set in the config instance. """ if settings is None: settings = config(clone=portdb.settings) else: settings = config(clone=settings) if 'PORTAGE_PARALLEL_FETCHONLY' in settings: return # We must create our private PORTAGE_TMPDIR before calling # doebuild_environment(), since lots of variables such # as PORTAGE_BUILDDIR refer to paths inside PORTAGE_TMPDIR. portage_tmpdir = settings.get('PORTAGE_TMPDIR') if not portage_tmpdir or not os.access(portage_tmpdir, os.W_OK): portage_tmpdir = None private_tmpdir = tempfile.mkdtemp(dir=portage_tmpdir) settings['PORTAGE_TMPDIR'] = private_tmpdir settings.backup_changes('PORTAGE_TMPDIR') # private temp dir was just created, so it's not locked yet settings.pop('PORTAGE_BUILDIR_LOCKED', None) try: doebuild_environment(ebuild_path, 'nofetch', settings=settings, db=portdb) restrict = settings['PORTAGE_RESTRICT'].split() defined_phases = settings['DEFINED_PHASES'].split() if not defined_phases: # When DEFINED_PHASES is undefined, assume all # phases are defined. defined_phases = EBUILD_PHASES if 'fetch' not in restrict and \ 'nofetch' not in defined_phases: return prepare_build_dirs(settings=settings) ebuild_phase = EbuildPhase(background=False, phase='nofetch', scheduler=PollScheduler().sched_iface, settings=settings) ebuild_phase.start() ebuild_phase.wait() elog_process(settings.mycpv, settings) finally: shutil.rmtree(private_tmpdir)
def testUseExpandIncremental(self): profiles = ( ( 'base', { "eapi": ("5",), "parent": ("..",), "make.defaults": ( "INPUT_DEVICES=\"keyboard mouse\"", "PYTHON_TARGETS=\"python2_7 python3_3\"", ("USE_EXPAND=\"INPUT_DEVICES PYTHON_TARGETS " "VIDEO_CARDS\""), ) } ), ( 'default/linux', { "eapi": ("5",), "make.defaults": ( "VIDEO_CARDS=\"dummy fbdev v4l\"", ) } ), ( 'default/linux/x86', { "eapi": ("5",), "make.defaults": ( # Test negative incremental for bug 530222. "PYTHON_TARGETS=\"-python3_3\"", ), "parent": ("../../../base", "../../../mixins/python/3.4", ".." ) } ), ( 'mixins/python/3.4', { "eapi": ("5",), "make.defaults": ( "PYTHON_TARGETS=\"python3_4\"", ) } ), ) # USE_EXPAND variable settings in make.conf will cause # profile settings for the same variable to be discarded # (non-incremental behavior). PMS does not govern make.conf # behavior. user_config = { "make.conf" : ( "VIDEO_CARDS=\"intel\"", ) } ebuilds = { "x11-base/xorg-drivers-1.15": { "EAPI": "5", "IUSE": ("input_devices_keyboard input_devices_mouse " "videos_cards_dummy video_cards_fbdev " "video_cards_v4l video_cards_intel") }, "sys-apps/portage-2.2.14": { "EAPI": "5", "IUSE": ("python_targets_python2_7 " "python_targets_python3_3 python_targets_python3_4") }, } package_expected_use = ( ("x11-base/xorg-drivers-1.15", ("input_devices_keyboard", "input_devices_mouse", "video_cards_intel",)), ("sys-apps/portage-2.2.14", ("python_targets_python2_7", "python_targets_python3_4")) ) playground = ResolverPlayground(debug=False, ebuilds=ebuilds, user_config=user_config) try: repo_dir = (playground.settings.repositories. get_location_for_name("test_repo")) profile_root = os.path.join(repo_dir, "profiles") for p, data in profiles: prof_path = os.path.join(profile_root, p) ensure_dirs(prof_path) for k, v in data.items(): with io.open(os.path.join(prof_path, k), mode="w", encoding=_encodings["repo.content"]) as f: for line in v: f.write("%s\n" % line) # The config must be reloaded in order to account # for the above profile customizations. playground.reload_config() depgraph = playground.run( ["=x11-base/xorg-drivers-1.15"]).depgraph settings = config(clone=playground.settings) for cpv, expected_use in package_expected_use: pkg, existing_node = depgraph._select_package( playground.eroot, Atom("=" + cpv)) settings.setcpv(pkg) expected = frozenset(expected_use) got = frozenset(settings["PORTAGE_USE"].split()) self.assertEqual(got, expected, "%s != %s" % (got, expected)) finally: playground.cleanup()