aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Sachau <tommy@gentoo.org>2021-04-03 10:28:06 +0200
committerThomas Sachau <tommy@gentoo.org>2021-04-03 10:28:06 +0200
commit639064263dbd0d1a0753d8e754c53ac0ff10768b (patch)
tree3b3647688fd13842e3fa8a4289b8bddb3b44469d
parentMerge tag 'portage-3.0.17' into multilib (diff)
parentUpdates for portage-3.0.18 release (diff)
downloadportage-multilib.tar.gz
portage-multilib.tar.bz2
portage-multilib.zip
Merge tag 'portage-3.0.18' into multilibmultilib
portage-3.0.18 Signed-off-by: Zac Medico <zmedico@gentoo.org> Signed-off-by: Thomas Sachau <tommy@gentoo.org>
-rw-r--r--NEWS4
-rw-r--r--RELEASE-NOTES7
-rwxr-xr-xbin/glsa-check3
-rw-r--r--lib/_emerge/BlockerCache.py6
-rw-r--r--lib/_emerge/help.py2
-rw-r--r--lib/_emerge/main.py2
-rw-r--r--lib/portage/_sets/ProfilePackageSet.py9
-rw-r--r--lib/portage/_sets/profiles.py10
-rw-r--r--lib/portage/dbapi/_VdbMetadataDelta.py11
-rw-r--r--lib/portage/dbapi/vartree.py6
-rw-r--r--lib/portage/dep/__init__.py7
-rw-r--r--lib/portage/eapi.py107
-rw-r--r--lib/portage/emaint/modules/sync/sync.py40
-rw-r--r--lib/portage/locks.py4
-rw-r--r--lib/portage/package/ebuild/_config/KeywordsManager.py7
-rw-r--r--lib/portage/package/ebuild/_config/LocationsManager.py11
-rw-r--r--lib/portage/package/ebuild/_config/MaskManager.py7
-rw-r--r--lib/portage/package/ebuild/_config/UseManager.py12
-rw-r--r--lib/portage/package/ebuild/config.py11
-rw-r--r--lib/portage/repository/config.py34
-rw-r--r--lib/portage/tests/dep/test_isvalidatom.py26
-rw-r--r--lib/portage/tests/resolver/ResolverPlayground.py10
-rw-r--r--lib/portage/tests/resolver/binpkg_multi_instance/test_build_id_profile_format.py14
-rw-r--r--lib/portage/tests/sync/test_sync_local.py15
-rw-r--r--man/emerge.12
-rw-r--r--man/portage.56
-rwxr-xr-xrepoman/bin/repoman4
-rw-r--r--repoman/cnf/qa_data/qa_data.yaml1
-rw-r--r--repoman/cnf/repository/qa_data.yaml1
-rw-r--r--repoman/cnf/repository/repository.yaml1
-rw-r--r--repoman/lib/repoman/actions.py1
-rwxr-xr-xrepoman/lib/repoman/main.py43
-rw-r--r--repoman/lib/repoman/modules/linechecks/phases/__init__.py6
-rw-r--r--repoman/lib/repoman/modules/linechecks/phases/phase.py132
-rw-r--r--repoman/lib/repoman/repos.py8
-rw-r--r--repoman/lib/repoman/tests/simple/test_simple.py203
-rw-r--r--repoman/man/repoman.15
-rwxr-xr-xsetup.py11
38 files changed, 647 insertions, 142 deletions
diff --git a/NEWS b/NEWS
index 84299e0a2..2812f8a18 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,9 @@
News (mainly features/major bug fixes)
+portage-3.0.18
+--------------
+* profile-repo-deps is supported in layout.conf profile-formats.
+
portage-3.0.16
--------------
* emirrordist supports the content-hash distfiles mirror layout.
diff --git a/RELEASE-NOTES b/RELEASE-NOTES
index 6fd9e2922..8c74f266c 100644
--- a/RELEASE-NOTES
+++ b/RELEASE-NOTES
@@ -1,6 +1,13 @@
Release Notes; upgrade information mainly.
Features/major bugfixes are listed in NEWS
+portage-3.0.18
+==================================
+* Bug Fixes:
+ - Bug 651208 profile-repo-deps profile-formats support
+ - Bug 719260 remove glsa --quiet blank line
+ - Bug 722748 suppress emerge --oneshot portage suggestion
+
portage-3.0.17
==================================
* Bug Fixes:
diff --git a/bin/glsa-check b/bin/glsa-check
index a3e7aa043..c8610f7df 100755
--- a/bin/glsa-check
+++ b/bin/glsa-check
@@ -290,7 +290,8 @@ if mode in ["dump", "fix", "inject", "pretend"]:
elif mode == "inject":
sys.stdout.write("injecting " + myid + "\n")
myglsa.inject()
- sys.stdout.write("\n")
+ if not quiet:
+ sys.stdout.write("\n")
sys.exit(0)
# test is a bit different as Glsa.test() produces no output
diff --git a/lib/_emerge/BlockerCache.py b/lib/_emerge/BlockerCache.py
index 8154d9ade..035f2212d 100644
--- a/lib/_emerge/BlockerCache.py
+++ b/lib/_emerge/BlockerCache.py
@@ -133,9 +133,9 @@ class BlockerCache(portage.cache.mappings.MutableMapping):
if len(self._modified) >= self._cache_threshold and \
secpass >= 2:
try:
- f = portage.util.atomic_ofstream(self._cache_filename, mode='wb')
- pickle.dump(self._cache_data, f, protocol=2)
- f.close()
+ with portage.util.atomic_ofstream(self._cache_filename, mode='wb') as f:
+ pickle.dump(self._cache_data, f, protocol=2)
+
portage.util.apply_secpass_permissions(
self._cache_filename, gid=portage.portage_gid, mode=0o644)
except (IOError, OSError):
diff --git a/lib/_emerge/help.py b/lib/_emerge/help.py
index 15e1941ca..d9faf0c4b 100644
--- a/lib/_emerge/help.py
+++ b/lib/_emerge/help.py
@@ -11,7 +11,7 @@ def emerge_help():
print(" "+turquoise("emerge")+" < "+turquoise("--sync")+" | "+turquoise("--metadata")+" | "+turquoise("--info")+" >")
print(" "+turquoise("emerge")+" "+turquoise("--resume")+" [ "+green("--pretend")+" | "+green("--ask")+" | "+green("--skipfirst")+" ]")
print(" "+turquoise("emerge")+" "+turquoise("--help"))
- print(bold("Options:")+" "+green("-")+"["+green("abBcCdDefgGhjkKlnNoOpPqrsStuUvVw")+"]")
+ print(bold("Options:")+" "+green("-")+"["+green("abBcCdDefgGhjkKlnNoOpPqrsStuUvVwW")+"]")
print(" [ " + green("--color")+" < " + turquoise("y") + " | "+ turquoise("n")+" > ] [ "+green("--columns")+" ]")
print(" [ "+green("--complete-graph")+" ] [ "+green("--deep")+" ]")
print(" [ "+green("--jobs") + " " + turquoise("JOBS")+" ] [ "+green("--keep-going")+" ] [ " + green("--load-average")+" " + turquoise("LOAD") + " ]")
diff --git a/lib/_emerge/main.py b/lib/_emerge/main.py
index 0ac25ea36..31e690584 100644
--- a/lib/_emerge/main.py
+++ b/lib/_emerge/main.py
@@ -187,6 +187,7 @@ def insert_optional_args(args):
'q' : y_or_n,
'v' : y_or_n,
'w' : y_or_n,
+ 'W' : y_or_n,
}
arg_stack = args[:]
@@ -470,6 +471,7 @@ def parse_opts(tmpcmdline, silent=False):
},
"--deselect": {
+ "shortopt" : "-W",
"help" : "remove atoms/sets from the world file",
"choices" : true_y_or_n
},
diff --git a/lib/portage/_sets/ProfilePackageSet.py b/lib/portage/_sets/ProfilePackageSet.py
index fec937391..7a304c578 100644
--- a/lib/portage/_sets/ProfilePackageSet.py
+++ b/lib/portage/_sets/ProfilePackageSet.py
@@ -1,7 +1,8 @@
-# Copyright 2014 Gentoo Foundation
+# Copyright 2014-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
from portage import os
+from portage.repository.config import allow_profile_repo_deps
from portage.util import grabfile_package, stack_lists
from portage._sets.base import PackageSet
@@ -9,7 +10,9 @@ class ProfilePackageSet(PackageSet):
_operations = ["merge"]
def __init__(self, profiles, debug=False):
- super(ProfilePackageSet, self).__init__()
+ super(ProfilePackageSet, self).__init__(
+ allow_repo=any(allow_profile_repo_deps(y) for y in profiles)
+ )
self._profiles = profiles
if profiles:
desc_profile = profiles[-1]
@@ -24,7 +27,7 @@ class ProfilePackageSet(PackageSet):
self._setAtoms(x for x in stack_lists(
[grabfile_package(os.path.join(y.location, "packages"),
verify_eapi=True, eapi=y.eapi, eapi_default=None,
- allow_build_id=y.allow_build_id)
+ allow_build_id=y.allow_build_id, allow_repo=allow_profile_repo_deps(y))
for y in self._profiles
if "profile-set" in y.profile_formats],
incremental=1) if x[:1] != "*")
diff --git a/lib/portage/_sets/profiles.py b/lib/portage/_sets/profiles.py
index bccc02e7c..95831f705 100644
--- a/lib/portage/_sets/profiles.py
+++ b/lib/portage/_sets/profiles.py
@@ -1,9 +1,10 @@
-# Copyright 2007-2014 Gentoo Foundation
+# Copyright 2007-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
import logging
from portage import os
+from portage.repository.config import allow_profile_repo_deps
from portage.util import grabfile_package, stack_lists
from portage._sets.base import PackageSet
from portage._sets import get_boolean
@@ -15,7 +16,9 @@ class PackagesSystemSet(PackageSet):
_operations = ["merge"]
def __init__(self, profiles, debug=False):
- super(PackagesSystemSet, self).__init__()
+ super(PackagesSystemSet, self).__init__(
+ allow_repo=any(allow_profile_repo_deps(x) for x in profiles)
+ )
self._profiles = profiles
self._debug = debug
if profiles:
@@ -35,7 +38,8 @@ class PackagesSystemSet(PackageSet):
mylist = [grabfile_package(os.path.join(x.location, "packages"),
verify_eapi=True, eapi=x.eapi, eapi_default=None,
- allow_build_id=x.allow_build_id)
+ allow_build_id=x.allow_build_id,
+ allow_repo=allow_profile_repo_deps(x))
for x in self._profiles]
if debug:
diff --git a/lib/portage/dbapi/_VdbMetadataDelta.py b/lib/portage/dbapi/_VdbMetadataDelta.py
index ffdc0b361..568e1964a 100644
--- a/lib/portage/dbapi/_VdbMetadataDelta.py
+++ b/lib/portage/dbapi/_VdbMetadataDelta.py
@@ -18,13 +18,12 @@ class VdbMetadataDelta:
self._vardb = vardb
def initialize(self, timestamp):
- f = atomic_ofstream(self._vardb._cache_delta_filename, 'w',
- encoding=_encodings['repo.content'], errors='strict')
- json.dump({
- "version": self._format_version,
- "timestamp": timestamp
+ with atomic_ofstream(self._vardb._cache_delta_filename, 'w',
+ encoding=_encodings['repo.content'], errors='strict') as f:
+ json.dump({
+ "version": self._format_version,
+ "timestamp": timestamp
}, f, ensure_ascii=False)
- f.close()
def load(self):
diff --git a/lib/portage/dbapi/vartree.py b/lib/portage/dbapi/vartree.py
index 826083eae..5ae035baf 100644
--- a/lib/portage/dbapi/vartree.py
+++ b/lib/portage/dbapi/vartree.py
@@ -614,9 +614,9 @@ class vardbapi(dbapi):
timestamp = time.time()
self._aux_cache["timestamp"] = timestamp
- f = atomic_ofstream(self._aux_cache_filename, 'wb')
- pickle.dump(self._aux_cache, f, protocol=2)
- f.close()
+ with atomic_ofstream(self._aux_cache_filename, 'wb') as f:
+ pickle.dump(self._aux_cache, f, protocol=2)
+
apply_secpass_permissions(
self._aux_cache_filename, mode=0o644)
diff --git a/lib/portage/dep/__init__.py b/lib/portage/dep/__init__.py
index 767c24ede..94c848387 100644
--- a/lib/portage/dep/__init__.py
+++ b/lib/portage/dep/__init__.py
@@ -1,5 +1,5 @@
# deps.py -- Portage dependency resolution functions
-# Copyright 2003-2020 Gentoo Authors
+# Copyright 2003-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
__all__ = [
@@ -1260,8 +1260,9 @@ class Atom(str):
self.__dict__['eapi'] = eapi
if eapi is not None:
- # Ignore allow_repo when eapi is specified.
- allow_repo = eapi_attrs.repo_deps
+ # If allow_repo is not set, use default from eapi
+ if allow_repo is None:
+ allow_repo = eapi_attrs.repo_deps
else:
# These parameters have "smart" defaults that are only
# applied when the caller does not explicitly pass in a
diff --git a/lib/portage/eapi.py b/lib/portage/eapi.py
index aca571ebd..796184644 100644
--- a/lib/portage/eapi.py
+++ b/lib/portage/eapi.py
@@ -1,7 +1,9 @@
-# Copyright 2010-2018 Gentoo Foundation
+# Copyright 2010-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
import collections
+import operator
+import types
from portage import eapi_is_supported
@@ -127,15 +129,63 @@ def eapi_has_sysroot(eapi):
_eapi_attrs = collections.namedtuple('_eapi_attrs',
'allows_package_provided '
- 'bdepend broot dots_in_PN dots_in_use_flags exports_EBUILD_PHASE_FUNC '
- 'exports_PORTDIR exports_ECLASSDIR '
+ 'bdepend '
+ 'broot '
+ 'dots_in_PN dots_in_use_flags '
+ 'exports_AA '
+ 'exports_EBUILD_PHASE_FUNC '
+ 'exports_ECLASSDIR '
+ 'exports_KV '
+ 'exports_merge_type '
+ 'exports_PORTDIR '
+ 'exports_replace_vars '
'feature_flag_test '
'iuse_defaults iuse_effective posixish_locale '
'path_variables_end_with_trailing_slash '
+ 'prefix '
'repo_deps required_use required_use_at_most_one_of slot_operator slot_deps '
'src_uri_arrows strong_blocks use_deps use_dep_defaults '
'empty_groups_always_true sysroot')
+
+_eapi_attr_func_prefixes = (
+ 'eapi_allows_',
+ 'eapi_has_',
+ 'eapi_requires_',
+ 'eapi_supports_',
+ 'eapi_',
+)
+
+
+def _eapi_func_decorator(func, attr_getter):
+ def wrapper(eapi):
+ return attr_getter(_get_eapi_attrs(eapi))
+ wrapper.func = func
+ wrapper.__doc__ = func.__doc__
+ return wrapper
+
+
+def _decorate_eapi_funcs():
+ """
+ Decorate eapi_* functions so that they use _get_eapi_attrs(eapi)
+ to cache results.
+ """
+ decorated = {}
+ for k, v in globals().items():
+ if not (isinstance(v, types.FunctionType) and k.startswith(_eapi_attr_func_prefixes)):
+ continue
+ for prefix in _eapi_attr_func_prefixes:
+ if k.startswith(prefix):
+ attr_name = k[len(prefix):]
+ if hasattr(_eapi_attrs, attr_name):
+ decorated[k] = _eapi_func_decorator(v, operator.attrgetter(attr_name))
+ break
+ globals().update(decorated)
+
+
+_decorate_eapi_funcs()
+
+
_eapi_attrs_cache = {}
def _get_eapi_attrs(eapi):
@@ -155,31 +205,36 @@ def _get_eapi_attrs(eapi):
eapi = None
eapi_attrs = _eapi_attrs(
- allows_package_provided=(eapi is None or eapi_allows_package_provided(eapi)),
- bdepend = (eapi is not None and eapi_has_bdepend(eapi)),
- broot = (eapi is None or eapi_has_broot(eapi)),
- dots_in_PN = (eapi is None or eapi_allows_dots_in_PN(eapi)),
- dots_in_use_flags = (eapi is None or eapi_allows_dots_in_use_flags(eapi)),
- empty_groups_always_true = (eapi is not None and eapi_empty_groups_always_true(eapi)),
- exports_EBUILD_PHASE_FUNC = (eapi is None or eapi_exports_EBUILD_PHASE_FUNC(eapi)),
- exports_PORTDIR = (eapi is None or eapi_exports_PORTDIR(eapi)),
- exports_ECLASSDIR = (eapi is not None and eapi_exports_ECLASSDIR(eapi)),
+ allows_package_provided=(eapi is None or eapi_allows_package_provided.func(eapi)),
+ bdepend = (eapi is not None and eapi_has_bdepend.func(eapi)),
+ broot = (eapi is None or eapi_has_broot.func(eapi)),
+ dots_in_PN = (eapi is None or eapi_allows_dots_in_PN.func(eapi)),
+ dots_in_use_flags = (eapi is None or eapi_allows_dots_in_use_flags.func(eapi)),
+ empty_groups_always_true = (eapi is not None and eapi_empty_groups_always_true.func(eapi)),
+ exports_AA = (eapi is not None and eapi_exports_AA.func(eapi)),
+ exports_EBUILD_PHASE_FUNC = (eapi is None or eapi_exports_EBUILD_PHASE_FUNC.func(eapi)),
+ exports_ECLASSDIR = (eapi is not None and eapi_exports_ECLASSDIR.func(eapi)),
+ exports_KV = (eapi is not None and eapi_exports_KV.func(eapi)),
+ exports_merge_type = (eapi is None or eapi_exports_merge_type.func(eapi)),
+ exports_PORTDIR = (eapi is None or eapi_exports_PORTDIR.func(eapi)),
+ exports_replace_vars = (eapi is None or eapi_exports_replace_vars.func(eapi)),
feature_flag_test = False,
- iuse_defaults = (eapi is None or eapi_has_iuse_defaults(eapi)),
- iuse_effective = (eapi is not None and eapi_has_iuse_effective(eapi)),
+ iuse_defaults = (eapi is None or eapi_has_iuse_defaults.func(eapi)),
+ iuse_effective = (eapi is not None and eapi_has_iuse_effective.func(eapi)),
path_variables_end_with_trailing_slash = (eapi is not None and
- eapi_path_variables_end_with_trailing_slash(eapi)),
- posixish_locale = (eapi is not None and eapi_requires_posixish_locale(eapi)),
- repo_deps = (eapi is None or eapi_has_repo_deps(eapi)),
- required_use = (eapi is None or eapi_has_required_use(eapi)),
- required_use_at_most_one_of = (eapi is None or eapi_has_required_use_at_most_one_of(eapi)),
- slot_deps = (eapi is None or eapi_has_slot_deps(eapi)),
- slot_operator = (eapi is None or eapi_has_slot_operator(eapi)),
- src_uri_arrows = (eapi is None or eapi_has_src_uri_arrows(eapi)),
- strong_blocks = (eapi is None or eapi_has_strong_blocks(eapi)),
- sysroot = (eapi is None or eapi_has_sysroot(eapi)),
- use_deps = (eapi is None or eapi_has_use_deps(eapi)),
- use_dep_defaults = (eapi is None or eapi_has_use_dep_defaults(eapi))
+ eapi_path_variables_end_with_trailing_slash.func(eapi)),
+ posixish_locale = (eapi is not None and eapi_requires_posixish_locale.func(eapi)),
+ prefix = (eapi is None or eapi_supports_prefix.func(eapi)),
+ repo_deps = (eapi is None or eapi_has_repo_deps.func(eapi)),
+ required_use = (eapi is None or eapi_has_required_use.func(eapi)),
+ required_use_at_most_one_of = (eapi is None or eapi_has_required_use_at_most_one_of.func(eapi)),
+ slot_deps = (eapi is None or eapi_has_slot_deps.func(eapi)),
+ slot_operator = (eapi is None or eapi_has_slot_operator.func(eapi)),
+ src_uri_arrows = (eapi is None or eapi_has_src_uri_arrows.func(eapi)),
+ strong_blocks = (eapi is None or eapi_has_strong_blocks.func(eapi)),
+ sysroot = (eapi is None or eapi_has_sysroot.func(eapi)),
+ use_deps = (eapi is None or eapi_has_use_deps.func(eapi)),
+ use_dep_defaults = (eapi is None or eapi_has_use_dep_defaults.func(eapi))
)
_eapi_attrs_cache[orig_eapi] = eapi_attrs
diff --git a/lib/portage/emaint/modules/sync/sync.py b/lib/portage/emaint/modules/sync/sync.py
index ce9c0da39..442973142 100644
--- a/lib/portage/emaint/modules/sync/sync.py
+++ b/lib/portage/emaint/modules/sync/sync.py
@@ -277,13 +277,45 @@ class SyncRepos:
mypvs = portage.best(
self.emerge_config.target_config.trees['vartree'].dbapi.match(
portage.const.PORTAGE_PACKAGE_ATOM))
-
- chk_updated_cfg_files(self.emerge_config.target_config.root,
+ try:
+ old_use = (
+ self.emerge_config.target_config.trees["vartree"]
+ .dbapi.aux_get(mypvs, ["USE"])[0]
+ .split()
+ )
+ except KeyError:
+ old_use = ()
+
+ chk_updated_cfg_files(
+ self.emerge_config.target_config.root,
portage.util.shlex_split(
- self.emerge_config.target_config.settings.get("CONFIG_PROTECT", "")))
+ self.emerge_config.target_config.settings.get("CONFIG_PROTECT", "")
+ ),
+ )
msgs = []
- if mybestpv != mypvs and "--quiet" not in self.emerge_config.opts:
+ if not (mybestpv and mypvs) or mybestpv == mypvs or "--quiet" in self.emerge_config.opts:
+ return msgs
+
+ # Suggest to update to the latest available version of portage.
+ # Since changes to PYTHON_TARGETS cause complications, this message
+ # is suppressed if the new version has different PYTHON_TARGETS enabled
+ # than previous version.
+ portdb = self.emerge_config.target_config.trees["porttree"].dbapi
+ portdb.doebuild_settings.setcpv(mybestpv, mydb=portdb)
+ usemask = portdb.doebuild_settings.usemask
+ useforce = portdb.doebuild_settings.useforce
+ new_use = (
+ frozenset(portdb.doebuild_settings["PORTAGE_USE"].split()) | useforce
+ ) - usemask
+ new_python_targets = frozenset(
+ x for x in new_use if x.startswith("python_targets_")
+ )
+ old_python_targets = frozenset(
+ x for x in old_use if x.startswith("python_targets_")
+ )
+
+ if new_python_targets == old_python_targets:
msgs.append('')
msgs.append(warn(" * ")+bold("An update to portage is available.")+" It is _highly_ recommended")
msgs.append(warn(" * ")+"that you update portage now, before any other packages are updated.")
diff --git a/lib/portage/locks.py b/lib/portage/locks.py
index ddce72f62..d0218d7bc 100644
--- a/lib/portage/locks.py
+++ b/lib/portage/locks.py
@@ -113,6 +113,10 @@ def _test_lock_fn(lock_fn: typing.Callable[[str, int, int], typing.Callable[[],
# the test passed
return True
finally:
+ try:
+ os.unlink(lock_path)
+ except OSError:
+ pass
if unlock_fn is not None:
unlock_fn()
return False
diff --git a/lib/portage/package/ebuild/_config/KeywordsManager.py b/lib/portage/package/ebuild/_config/KeywordsManager.py
index bf68a88ac..8dcaee0d9 100644
--- a/lib/portage/package/ebuild/_config/KeywordsManager.py
+++ b/lib/portage/package/ebuild/_config/KeywordsManager.py
@@ -1,4 +1,4 @@
-# Copyright 2010-2020 Gentoo Authors
+# Copyright 2010-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
__all__ = (
@@ -12,6 +12,7 @@ from portage import os
from portage.dep import ExtendedAtomDict
from portage.localization import _
from portage.package.ebuild._config.helper import ordered_by_atom_specificity
+from portage.repository.config import allow_profile_repo_deps
from portage.util import grabdict_package, stack_lists
from portage.versions import _pkg_str
@@ -25,6 +26,7 @@ class KeywordsManager:
os.path.join(x.location, "package.keywords"),
recursive=x.portage1_directories,
verify_eapi=True, eapi=x.eapi, eapi_default=None,
+ allow_repo=allow_profile_repo_deps(x),
allow_build_id=x.allow_build_id)
for x in profiles]
for pkeyworddict in rawpkeywords:
@@ -41,7 +43,8 @@ class KeywordsManager:
raw_p_accept_keywords = [grabdict_package(
os.path.join(x.location, "package.accept_keywords"),
recursive=x.portage1_directories,
- verify_eapi=True, eapi=x.eapi, eapi_default=None)
+ verify_eapi=True, eapi=x.eapi, eapi_default=None,
+ allow_repo=allow_profile_repo_deps(x))
for x in profiles]
for d in raw_p_accept_keywords:
if not d:
diff --git a/lib/portage/package/ebuild/_config/LocationsManager.py b/lib/portage/package/ebuild/_config/LocationsManager.py
index 327400ad6..433232a7a 100644
--- a/lib/portage/package/ebuild/_config/LocationsManager.py
+++ b/lib/portage/package/ebuild/_config/LocationsManager.py
@@ -1,11 +1,10 @@
-# Copyright 2010-2018 Gentoo Foundation
+# Copyright 2010-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
__all__ = (
'LocationsManager',
)
-import collections
import io
import warnings
@@ -20,7 +19,7 @@ from portage.util import ensure_dirs, grabfile, \
normalize_path, read_corresponding_eapi_file, shlex_split, writemsg
from portage.util._path import exists_raise_eaccess, isdir_raise_eaccess
from portage.repository.config import parse_layout_conf, \
- _portage1_profiles_allow_directories
+ _portage1_profiles_allow_directories, _profile_node
_PORTAGE1_DIRECTORIES = frozenset([
@@ -28,12 +27,6 @@ _PORTAGE1_DIRECTORIES = frozenset([
'package.use', 'package.use.mask', 'package.use.force',
'use.mask', 'use.force'])
-_profile_node = collections.namedtuple('_profile_node',
- ('location', 'portage1_directories', 'user_config',
- 'profile_formats', 'eapi', 'allow_build_id',
- 'show_deprecated_warning',
-))
-
_allow_parent_colon = frozenset(
["portage-2"])
diff --git a/lib/portage/package/ebuild/_config/MaskManager.py b/lib/portage/package/ebuild/_config/MaskManager.py
index 7714456e1..b0c2b55da 100644
--- a/lib/portage/package/ebuild/_config/MaskManager.py
+++ b/lib/portage/package/ebuild/_config/MaskManager.py
@@ -1,4 +1,4 @@
-# Copyright 2010-2018 Gentoo Foundation
+# Copyright 2010-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
__all__ = (
@@ -10,6 +10,7 @@ import warnings
from portage import os
from portage.dep import ExtendedAtomDict, match_from_list
from portage.localization import _
+from portage.repository.config import allow_profile_repo_deps
from portage.util import append_repo, grabfile_package, stack_lists, writemsg
from portage.versions import _pkg_str
@@ -41,6 +42,7 @@ class MaskManager:
recursive=repo_config.portage1_profiles,
remember_source_file=True, verify_eapi=True,
eapi_default=repo_config.eapi,
+ allow_repo=allow_profile_repo_deps(repo_config),
allow_build_id=("build-id"
in repo_config.profile_formats))
if repo_config.portage1_profiles_compat and os.path.isdir(path):
@@ -110,6 +112,7 @@ class MaskManager:
repo_lines = grabfile_package(os.path.join(repo.location, "profiles", "package.unmask"), \
recursive=1, remember_source_file=True,
verify_eapi=True, eapi_default=repo.eapi,
+ allow_repo=allow_profile_repo_deps(repo),
allow_build_id=("build-id" in repo.profile_formats))
lines = stack_lists([repo_lines], incremental=1, \
remember_source_file=True, warn_for_unmatched_removal=True,
@@ -126,6 +129,7 @@ class MaskManager:
recursive=x.portage1_directories,
remember_source_file=True, verify_eapi=True,
eapi=x.eapi, eapi_default=None,
+ allow_repo=allow_profile_repo_deps(x),
allow_build_id=x.allow_build_id))
if x.portage1_directories:
profile_pkgunmasklines.append(grabfile_package(
@@ -133,6 +137,7 @@ class MaskManager:
recursive=x.portage1_directories,
remember_source_file=True, verify_eapi=True,
eapi=x.eapi, eapi_default=None,
+ allow_repo=allow_profile_repo_deps(x),
allow_build_id=x.allow_build_id))
profile_pkgmasklines = stack_lists(profile_pkgmasklines, incremental=1, \
remember_source_file=True, warn_for_unmatched_removal=True,
diff --git a/lib/portage/package/ebuild/_config/UseManager.py b/lib/portage/package/ebuild/_config/UseManager.py
index 882b0efa9..656c6199b 100644
--- a/lib/portage/package/ebuild/_config/UseManager.py
+++ b/lib/portage/package/ebuild/_config/UseManager.py
@@ -1,4 +1,4 @@
-# Copyright 2010-2014 Gentoo Foundation
+# Copyright 2010-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
__all__ = (
@@ -11,6 +11,7 @@ from portage.dep import Atom, dep_getrepo, dep_getslot, ExtendedAtomDict, remove
from portage.eapi import eapi_has_use_aliases, eapi_supports_stable_use_forcing_and_masking
from portage.exception import InvalidAtom
from portage.localization import _
+from portage.repository.config import allow_profile_repo_deps
from portage.util import grabfile, grabdict, grabdict_package, read_corresponding_eapi_file, stack_lists, writemsg
from portage.versions import _pkg_str
@@ -154,7 +155,7 @@ class UseManager:
def _parse_file_to_dict(self, file_name, juststrings=False, recursive=True,
eapi_filter=None, user_config=False, eapi=None, eapi_default="0",
- allow_build_id=False):
+ allow_repo=False, allow_build_id=False):
"""
@param file_name: input file name
@type file_name: str
@@ -193,8 +194,9 @@ class UseManager:
ret = ExtendedAtomDict(dict)
else:
ret = {}
+ allow_repo = allow_repo or extended_syntax
file_dict = grabdict_package(file_name, recursive=recursive,
- allow_wildcard=extended_syntax, allow_repo=extended_syntax,
+ allow_wildcard=extended_syntax, allow_repo=allow_repo,
verify_eapi=(not extended_syntax), eapi=eapi,
eapi_default=eapi_default, allow_build_id=allow_build_id,
allow_use=False)
@@ -277,6 +279,7 @@ class UseManager:
ret[repo.name] = self._parse_file_to_dict(
os.path.join(repo.location, "profiles", file_name),
eapi_filter=eapi_filter, eapi_default=repo.eapi,
+ allow_repo=allow_profile_repo_deps(repo),
allow_build_id=("build-id" in repo.profile_formats))
return ret
@@ -294,7 +297,8 @@ class UseManager:
os.path.join(profile.location, file_name), juststrings,
recursive=profile.portage1_directories, eapi_filter=eapi_filter,
user_config=profile.user_config, eapi=profile.eapi,
- eapi_default=None, allow_build_id=profile.allow_build_id)
+ eapi_default=None, allow_build_id=profile.allow_build_id,
+ allow_repo=allow_profile_repo_deps(profile))
for profile in locations)
def _parse_repository_usealiases(self, repositories):
diff --git a/lib/portage/package/ebuild/config.py b/lib/portage/package/ebuild/config.py
index a13ec24e1..8046ea141 100644
--- a/lib/portage/package/ebuild/config.py
+++ b/lib/portage/package/ebuild/config.py
@@ -1,4 +1,4 @@
-# Copyright 2010-2020 Gentoo Authors
+# Copyright 2010-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
__all__ = [
@@ -42,7 +42,10 @@ from portage.exception import InvalidDependString, PortageException
from portage.localization import _
from portage.output import colorize
from portage.process import fakeroot_capable, sandbox_capable
-from portage.repository.config import load_repository_config
+from portage.repository.config import (
+ allow_profile_repo_deps,
+ load_repository_config,
+)
from portage.util import ensure_dirs, getconfig, grabdict, \
grabdict_package, grabfile, grabfile_package, LazyItemsDict, \
normalize_path, shlex_split, stack_dictlist, stack_dicts, stack_lists, \
@@ -593,6 +596,7 @@ class config:
packages_list = [grabfile_package(
os.path.join(x.location, "packages"),
verify_eapi=True, eapi=x.eapi, eapi_default=None,
+ allow_repo=allow_profile_repo_deps(x),
allow_build_id=x.allow_build_id)
for x in profiles_complex]
except EnvironmentError as e:
@@ -812,7 +816,8 @@ class config:
portage.dep.ExtendedAtomDict(dict)
bashrc = grabdict_package(os.path.join(profile.location,
"package.bashrc"), recursive=1, allow_wildcard=True,
- allow_repo=True, verify_eapi=True,
+ allow_repo=allow_profile_repo_deps(profile),
+ verify_eapi=True,
eapi=profile.eapi, eapi_default=None,
allow_build_id=profile.allow_build_id)
if not bashrc:
diff --git a/lib/portage/repository/config.py b/lib/portage/repository/config.py
index 83e9746d1..d81559e3a 100644
--- a/lib/portage/repository/config.py
+++ b/lib/portage/repository/config.py
@@ -1,16 +1,21 @@
-# Copyright 2010-2020 Gentoo Authors
+# Copyright 2010-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
+import collections
import io
import logging
import warnings
import re
+import typing
import portage
from portage import eclass_cache, os
from portage.checksum import get_valid_checksum_keys
from portage.const import (PORTAGE_BASE_PATH, REPO_NAME_LOC, USER_CONFIG_PATH)
-from portage.eapi import eapi_allows_directories_on_profile_level_and_repository_level
+from portage.eapi import (
+ eapi_allows_directories_on_profile_level_and_repository_level,
+ eapi_has_repo_deps,
+)
from portage.env.loaders import KeyValuePairFileLoader
from portage.util import (normalize_path, read_corresponding_eapi_file, shlex_split,
stack_lists, writemsg, writemsg_level, _recursive_file_list)
@@ -25,13 +30,25 @@ from portage import _encodings
from portage import manifest
import portage.sync
+_profile_node = collections.namedtuple(
+ "_profile_node",
+ (
+ "location",
+ "portage1_directories",
+ "user_config",
+ "profile_formats",
+ "eapi",
+ "allow_build_id",
+ "show_deprecated_warning",
+ ),
+)
# Characters prohibited by repoman's file.name check.
_invalid_path_char_re = re.compile(r'[^a-zA-Z0-9._\-+/]')
_valid_profile_formats = frozenset(
['pms', 'portage-1', 'portage-2', 'profile-bashrcs', 'profile-set',
- 'profile-default-eapi', 'build-id'])
+ 'profile-default-eapi', 'build-id', 'profile-repo-deps'])
_portage1_profiles_allow_directories = frozenset(
["portage-1-compat", "portage-1", 'portage-2'])
@@ -1096,6 +1113,17 @@ class RepoConfigLoader:
config_string += "%s = %s\n" % (o, v)
return config_string.lstrip("\n")
+def allow_profile_repo_deps(
+ repo: typing.Union[RepoConfig, _profile_node],
+) -> bool:
+ if eapi_has_repo_deps(repo.eapi):
+ return True
+
+ if 'profile-repo-deps' in repo.profile_formats:
+ return True
+
+ return False
+
def load_repository_config(settings, extra_files=None):
repoconfigpaths = []
if "PORTAGE_REPOSITORIES" in settings:
diff --git a/lib/portage/tests/dep/test_isvalidatom.py b/lib/portage/tests/dep/test_isvalidatom.py
index 58d999646..4203be07b 100644
--- a/lib/portage/tests/dep/test_isvalidatom.py
+++ b/lib/portage/tests/dep/test_isvalidatom.py
@@ -1,4 +1,4 @@
-# Copyright 2006-2013 Gentoo Foundation
+# Copyright 2006-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
from portage.tests import TestCase
@@ -6,12 +6,13 @@ from portage.dep import isvalidatom
class IsValidAtomTestCase:
def __init__(self, atom, expected, allow_wildcard=False,
- allow_repo=False, allow_build_id=False):
+ allow_repo=False, allow_build_id=False, eapi=None):
self.atom = atom
self.expected = expected
self.allow_wildcard = allow_wildcard
self.allow_repo = allow_repo
self.allow_build_id = allow_build_id
+ self.eapi = eapi
class IsValidAtom(TestCase):
@@ -137,6 +138,24 @@ class IsValidAtom(TestCase):
IsValidAtomTestCase("=sys-apps/portage-2.2*:foo::repo[doc?]", False, allow_repo=False),
IsValidAtomTestCase("null/portage::repo", False, allow_repo=False),
+ # Testing repo atoms with eapi
+
+ # If allow_repo is None, it should be overwritten by eapi
+ IsValidAtomTestCase("sys-apps/portage::repo", True, allow_repo=None),
+ IsValidAtomTestCase("sys-apps/portage::repo", False, allow_repo=None, eapi="5"),
+ IsValidAtomTestCase("sys-apps/portage::repo", True, allow_repo=None, eapi="5-progress"),
+ IsValidAtomTestCase("sys-apps/portage::repo", False, allow_repo=None, eapi="7"),
+
+ # If allow_repo is not None, it should not be overwritten by eapi
+ IsValidAtomTestCase("sys-apps/portage::repo", False, allow_repo=False),
+ IsValidAtomTestCase("sys-apps/portage::repo", False, allow_repo=False, eapi="5"),
+ IsValidAtomTestCase("sys-apps/portage::repo", False, allow_repo=False, eapi="5-progress"),
+ IsValidAtomTestCase("sys-apps/portage::repo", False, allow_repo=False, eapi="7"),
+ IsValidAtomTestCase("sys-apps/portage::repo", True, allow_repo=True),
+ IsValidAtomTestCase("sys-apps/portage::repo", True, allow_repo=True, eapi="5"),
+ IsValidAtomTestCase("sys-apps/portage::repo", True, allow_repo=True, eapi="5-progress"),
+ IsValidAtomTestCase("sys-apps/portage::repo", True, allow_repo=True, eapi="7"),
+
IsValidAtomTestCase("virtual/ffmpeg:0/53", True),
IsValidAtomTestCase("virtual/ffmpeg:0/53=", True),
IsValidAtomTestCase("virtual/ffmpeg:0/53*", False),
@@ -157,6 +176,7 @@ class IsValidAtom(TestCase):
atom_type = "invalid"
self.assertEqual(bool(isvalidatom(test_case.atom, allow_wildcard=test_case.allow_wildcard,
allow_repo=test_case.allow_repo,
- allow_build_id=test_case.allow_build_id)),
+ allow_build_id=test_case.allow_build_id,
+ eapi=test_case.eapi)),
test_case.expected,
msg="isvalidatom(%s) != %s" % (test_case.atom, test_case.expected))
diff --git a/lib/portage/tests/resolver/ResolverPlayground.py b/lib/portage/tests/resolver/ResolverPlayground.py
index 89feea47a..4575156f2 100644
--- a/lib/portage/tests/resolver/ResolverPlayground.py
+++ b/lib/portage/tests/resolver/ResolverPlayground.py
@@ -1,4 +1,4 @@
-# Copyright 2010-2020 Gentoo Authors
+# Copyright 2010-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
import bz2
@@ -44,7 +44,13 @@ class ResolverPlayground:
config_files = frozenset(("eapi", "layout.conf", "make.conf", "modules", "package.accept_keywords",
"package.keywords", "package.license", "package.mask", "package.properties",
"package.provided", "packages",
- "package.unmask", "package.use", "package.use.aliases", "package.use.stable.mask",
+ "package.unmask",
+ "package.use",
+ "package.use.aliases",
+ "package.use.force",
+ "package.use.mask",
+ "package.use.stable.force",
+ "package.use.stable.mask",
"soname.provided",
"unpack_dependencies", "use.aliases", "use.force", "use.mask", "layout.conf"))
diff --git a/lib/portage/tests/resolver/binpkg_multi_instance/test_build_id_profile_format.py b/lib/portage/tests/resolver/binpkg_multi_instance/test_build_id_profile_format.py
index 0397509f8..f9b1abb35 100644
--- a/lib/portage/tests/resolver/binpkg_multi_instance/test_build_id_profile_format.py
+++ b/lib/portage/tests/resolver/binpkg_multi_instance/test_build_id_profile_format.py
@@ -1,4 +1,4 @@
-# Copyright 2015 Gentoo Foundation
+# Copyright 2015-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
from portage.tests import TestCase
@@ -10,14 +10,22 @@ class BuildIdProfileFormatTestCase(TestCase):
def testBuildIdProfileFormat(self):
profile = {
- "packages": ("=app-misc/A-1-2",),
+ "packages": ("=app-misc/A-1-2::test_repo",),
+ "package.mask": ("<app-misc/A-1::test_repo",),
+ "package.keywords": ("app-misc/A-1::test_repo x86",),
+ "package.unmask": (">=app-misc/A-1::test_repo",),
+ "package.use": ("app-misc/A-1::test_repo foo",),
+ "package.use.mask": ("app-misc/A-1::test_repo -foo",),
+ "package.use.stable.mask": ("app-misc/A-1::test_repo -foo",),
+ "package.use.force": ("app-misc/A-1::test_repo foo",),
+ "package.use.stable.force": ("app-misc/A-1::test_repo foo",),
"package.provided": ("sys-libs/zlib-1.2.8-r1",),
}
repo_configs = {
"test_repo": {
"layout.conf": (
- "profile-formats = build-id profile-set",
+ "profile-formats = build-id profile-repo-deps profile-set",
),
}
}
diff --git a/lib/portage/tests/sync/test_sync_local.py b/lib/portage/tests/sync/test_sync_local.py
index 21c03a98b..02a8b2958 100644
--- a/lib/portage/tests/sync/test_sync_local.py
+++ b/lib/portage/tests/sync/test_sync_local.py
@@ -1,4 +1,4 @@
-# Copyright 2014-2020 Gentoo Authors
+# Copyright 2014-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
import datetime
@@ -55,14 +55,23 @@ class SyncLocalTestCase(TestCase):
}
ebuilds = {
- "dev-libs/A-0": {}
+ "dev-libs/A-0": {},
+ "sys-apps/portage-3.0": {"IUSE": "+python_targets_python3_8"},
+ }
+
+ installed = {
+ "sys-apps/portage-2.3.99": {
+ "EAPI": "7",
+ "IUSE": "+python_targets_python3_8",
+ "USE": "python_targets_python3_8",
+ },
}
user_config = {
'make.conf': ('FEATURES="metadata-transfer"',)
}
- playground = ResolverPlayground(ebuilds=ebuilds,
+ playground = ResolverPlayground(ebuilds=ebuilds, installed=installed,
profile=profile, user_config=user_config, debug=debug)
settings = playground.settings
eprefix = settings["EPREFIX"]
diff --git a/man/emerge.1 b/man/emerge.1
index ad7f81ae7..33416d986 100644
--- a/man/emerge.1
+++ b/man/emerge.1
@@ -155,7 +155,7 @@ unmerge matched packages that have no reverse dependencies. Use
\fB\-\-depclean\fR together with \fB\-\-verbose\fR to show reverse
dependencies.
.TP
-.BR "\-\-deselect [ y | n ]"
+.BR "\-\-deselect [ y | n ]", " \-W
Remove atoms and/or sets from the world file. This action is implied
by uninstall actions, including \fB-\-depclean\fR,
\fB-\-prune\fR and \fB-\-unmerge\fR. Use \fB-\-deselect=n\fR
diff --git a/man/portage.5 b/man/portage.5
index f6ec1b0fa..247ec5ab0 100644
--- a/man/portage.5
+++ b/man/portage.5
@@ -1,4 +1,4 @@
-.TH "PORTAGE" "5" "Sep 2020" "Portage VERSION" "Portage"
+.TH "PORTAGE" "5" "Mar 2021" "Portage VERSION" "Portage"
.SH NAME
portage \- the heart of Gentoo
.SH "DESCRIPTION"
@@ -1466,7 +1466,7 @@ The EAPI to use for profiles when unspecified. This attribute is
supported only if profile-default-eapi is included in profile-formats.
.TP
.BR profile\-formats " = [pms] [portage-1] [portage-2] [profile-bashrcs] \
-[profile-set] [profile-default-eapi] [build-id]"
+[profile-set] [profile-default-eapi] [build-id] [profile-repo-deps]"
Control functionality available to profiles in this repo such as which files
may be dirs, or the syntax available in parent files. Use "portage-2" if you're
unsure. The default is "portage-1-compat" mode which is meant to be compatible
@@ -1481,6 +1481,8 @@ dependency atoms in the profile to refer to specific builds (see the
binpkg\-multi\-instance FEATURES setting in \fBmake.conf\fR(5)). A
build\-id atom is identical to a version-specific atom, except that the
version is followed by a hyphen and an integer build\-id.
+Setting profile\-repo\-deps allows dependency atoms in the profile to
+refer to specific repositories.
.RE
.RE
diff --git a/repoman/bin/repoman b/repoman/bin/repoman
index 29c630772..c52ab15b7 100755
--- a/repoman/bin/repoman
+++ b/repoman/bin/repoman
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-# Copyright 1999-2020 Gentoo Authors
+# Copyright 1999-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
"""Ebuild and tree health checks and maintenance utilities.
@@ -41,7 +41,7 @@ from portage.util._eventloop.global_event_loop import global_event_loop
from repoman.main import repoman_main
try:
- sys.exit(repoman_main(sys.argv[1:]))
+ sys.exit(repoman_main(sys.argv))
except IOError as e:
if e.errno == errno.EACCES:
print("\nRepoman: Need user access")
diff --git a/repoman/cnf/qa_data/qa_data.yaml b/repoman/cnf/qa_data/qa_data.yaml
index 29a3d6e9f..530c8c806 100644
--- a/repoman/cnf/qa_data/qa_data.yaml
+++ b/repoman/cnf/qa_data/qa_data.yaml
@@ -129,6 +129,7 @@ qahelp:
obsolete: "The ebuild makes use of an obsolete construct"
variable:
invalidchar: "A variable contains an invalid character that is not part of the ASCII character set"
+ phase: "Variable referenced found within scope of incorrect ebuild phase as specified by PMS"
readonly: "Assigning a readonly variable"
usedwithhelpers: "Ebuild uses D, ROOT, BROOT, ED, EROOT or EPREFIX with helpers"
virtual:
diff --git a/repoman/cnf/repository/qa_data.yaml b/repoman/cnf/repository/qa_data.yaml
index 3fe6b53d5..2249000c3 100644
--- a/repoman/cnf/repository/qa_data.yaml
+++ b/repoman/cnf/repository/qa_data.yaml
@@ -80,6 +80,7 @@ qawarnings:
- usage.obsolete
- upstream.workaround
- uri.https
+ - variable.phase
- virtual.suspect
- wxwidgets.eclassnotused
diff --git a/repoman/cnf/repository/repository.yaml b/repoman/cnf/repository/repository.yaml
index ad00d18c1..dbc1decaa 100644
--- a/repoman/cnf/repository/repository.yaml
+++ b/repoman/cnf/repository/repository.yaml
@@ -61,6 +61,7 @@ linechecks_modules:
emakeparallel
srccompileeconf
srcunpackpatches
+ pmsvariablerefphasescope
portageinternal
portageinternalvariableassignment
quote
diff --git a/repoman/lib/repoman/actions.py b/repoman/lib/repoman/actions.py
index 0f89572b9..351df07be 100644
--- a/repoman/lib/repoman/actions.py
+++ b/repoman/lib/repoman/actions.py
@@ -307,7 +307,6 @@ the whole commit message to abort.
utilities.repoman_sez(
"\"Make your QA payment on time"
" and you'll never see the likes of me.\"\n")
- sys.exit(1)
def _fail(self, result, can_force):
diff --git a/repoman/lib/repoman/main.py b/repoman/lib/repoman/main.py
index 50b99c21f..78be31df3 100755
--- a/repoman/lib/repoman/main.py
+++ b/repoman/lib/repoman/main.py
@@ -3,6 +3,7 @@
# Copyright 1999-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
+import collections
import io
import logging
import sys
@@ -47,7 +48,26 @@ portage.util.initialize_logger(LOGLEVEL)
VALID_VERSIONS = [1,]
+_repoman_main_vars = collections.namedtuple("_repoman_main_vars", (
+ "can_force",
+ "exitcode",
+ "options",
+ "qadata",
+ "repo_settings",
+ "scanner",
+ "vcs_settings",
+))
+
+
def repoman_main(argv):
+ repoman_vars = _repoman_init(argv)
+ if repoman_vars.exitcode is not None:
+ return repoman_vars.exitcode
+ result = _repoman_scan(*repoman_vars)
+ return _handle_result(*repoman_vars, result)
+
+
+def _repoman_init(argv):
config_root = os.environ.get("PORTAGE_CONFIGROOT")
repoman_settings = portage.config(config_root=config_root, local_config=False)
repoman_settings.valid_versions = VALID_VERSIONS
@@ -58,11 +78,11 @@ def repoman_main(argv):
nocolor()
options, arguments = parse_args(
- sys.argv, repoman_settings.get("REPOMAN_DEFAULT_OPTS", ""))
+ argv, repoman_settings.get("REPOMAN_DEFAULT_OPTS", ""))
if options.version:
print("Repoman", VERSION, "(portage-%s)" % portage.VERSION)
- sys.exit(0)
+ return _repoman_main_vars(exitcode=0)
logger = logging.getLogger()
@@ -75,10 +95,15 @@ def repoman_main(argv):
# something other than a QA issue) makes it impossible to
# commit (like if Manifest generation fails).
can_force = ExtendedFuture(True)
+ repo_settings, vcs_settings, scanner, qadata = _create_scanner(options, can_force, config_root, repoman_settings)
+ return _repoman_main_vars(can_force, None, options, qadata, repo_settings, scanner, vcs_settings)
+
+
+def _create_scanner(options, can_force, config_root, repoman_settings):
portdir, portdir_overlay, mydir = utilities.FindPortdir(repoman_settings)
if portdir is None:
- sys.exit(1)
+ return (None, None, None, None)
myreporoot = os.path.basename(portdir_overlay)
myreporoot += mydir[len(portdir_overlay):]
@@ -117,6 +142,10 @@ def repoman_main(argv):
# Perform the main checks
scanner = Scanner(repo_settings, myreporoot, config_root, options,
vcs_settings, mydir, env)
+ return repo_settings, vcs_settings, scanner, qadata
+
+
+def _repoman_scan(can_force, exitcode, options, qadata, repo_settings, scanner, vcs_settings):
scanner.scan_pkgs(can_force)
if options.if_modified == "y" and len(scanner.effective_scanlist) < 1:
@@ -142,6 +171,10 @@ def repoman_main(argv):
(result['warn'] and not (options.quiet or options.mode == "scan")):
result['full'] = 0
+ return result
+
+
+def _handle_result(can_force, exitcode, options, qadata, repo_settings, scanner, vcs_settings, result):
commitmessage = None
if options.commitmsg:
commitmessage = options.commitmsg
@@ -189,5 +222,7 @@ def repoman_main(argv):
if actions.inform(can_force.get(), result):
# perform any other actions
actions.perform(qa_output)
+ elif result['fail']:
+ return 1
- sys.exit(0)
+ return 0
diff --git a/repoman/lib/repoman/modules/linechecks/phases/__init__.py b/repoman/lib/repoman/modules/linechecks/phases/__init__.py
index 686c675d2..e166b31a3 100644
--- a/repoman/lib/repoman/modules/linechecks/phases/__init__.py
+++ b/repoman/lib/repoman/modules/linechecks/phases/__init__.py
@@ -29,6 +29,12 @@ module_spec = {
'class': "SrcUnpackPatches",
'description': doc,
},
+ 'pmsvariablerefphasescope-check': {
+ 'name': "pmsvariablerefphasescope",
+ 'sourcefile': "phase",
+ 'class': "PMSVariableReference",
+ 'description': doc,
+ },
},
'version': 1,
}
diff --git a/repoman/lib/repoman/modules/linechecks/phases/phase.py b/repoman/lib/repoman/modules/linechecks/phases/phase.py
index 74cf4608f..433e93601 100644
--- a/repoman/lib/repoman/modules/linechecks/phases/phase.py
+++ b/repoman/lib/repoman/modules/linechecks/phases/phase.py
@@ -1,7 +1,19 @@
+import fnmatch
import re
-
-from portage.eapi import eapi_has_src_prepare_and_src_configure
+import types
+
+from portage.eapi import (
+ eapi_has_broot,
+ eapi_has_sysroot,
+ eapi_has_src_prepare_and_src_configure,
+ eapi_exports_AA,
+ eapi_exports_replace_vars,
+ eapi_exports_ECLASSDIR,
+ eapi_exports_PORTDIR,
+ eapi_supports_prefix,
+ eapi_exports_merge_type,
+)
from repoman.modules.linechecks.base import LineCheck
@@ -9,11 +21,22 @@ class PhaseCheck(LineCheck):
""" basic class for function detection """
func_end_re = re.compile(r'^\}$')
- phases_re = re.compile('(%s)' % '|'.join((
- 'pkg_pretend', 'pkg_setup', 'src_unpack', 'src_prepare',
- 'src_configure', 'src_compile', 'src_test', 'src_install',
- 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm',
- 'pkg_config')))
+ phase_funcs = (
+ 'pkg_pretend',
+ 'pkg_setup',
+ 'src_unpack',
+ 'src_prepare',
+ 'src_configure',
+ 'src_compile',
+ 'src_test',
+ 'src_install',
+ 'pkg_preinst',
+ 'pkg_postinst',
+ 'pkg_prerm',
+ 'pkg_postrm',
+ 'pkg_config',
+ )
+ phases_re = re.compile('(%s)' % '|'.join(phase_funcs))
in_phase = ''
def check(self, num, line):
@@ -69,3 +92,98 @@ class SrcUnpackPatches(PhaseCheck):
if m is not None:
return ("'%s'" % m.group(1)) + \
" call should be moved to src_prepare"
+
+# Refererences
+# - https://projects.gentoo.org/pms/7/pms.html#x1-10900011.1
+# - https://pkgcore.github.io/pkgcheck/_modules/pkgcheck/checks/codingstyle.html#VariableScopeCheck
+_pms_vars = (
+ ("A", None, ("src_*", "pkg_nofetch")),
+ ("AA", eapi_exports_AA, ("src_*", "pkg_nofetch")),
+ ("FILESDIR", None, ("src_*",)),
+ ("DISTDIR", None, ("src_*",)),
+ ("WORKDIR", None, ("src_*",)),
+ ("S", None, ("src_*",)),
+ ("PORTDIR", eapi_exports_PORTDIR, ("src_*",)),
+ ("ECLASSDIR", eapi_exports_ECLASSDIR, ("src_*",)),
+ ("ROOT", None, ("pkg_*",)),
+ ("EROOT", eapi_supports_prefix, ("pkg_*",)),
+ ("SYSROOT", eapi_has_sysroot, ("src_*", "pkg_setup")),
+ ("ESYSROOT", eapi_has_sysroot, ("src_*", "pkg_setup")),
+ ("BROOT", eapi_has_broot, ("src_*", "pkg_setup")),
+ ("D", None, ("src_install", "pkg_preinst", "pkg_postint")),
+ ("ED", eapi_supports_prefix, ("src_install", "pkg_preinst", "pkg_postint")),
+ ("DESTTREE", None, ("src_install",)),
+ ("INSDESTTREE", None, ("src_install",)),
+ ("MERGE_TYPE", eapi_exports_merge_type, ("pkg_*",)),
+ ("REPLACING_VERSIONS", eapi_exports_replace_vars, ("pkg_*",)),
+ ("REPLACED_BY_VERSION", eapi_exports_replace_vars, ("pkg_prerm", "pkg_postrm")),
+)
+
+
+def _compile_phases():
+ phase_vars = {}
+ for phase_func in PhaseCheck.phase_funcs:
+ for variable, eapi_filter, allowed_scopes in _pms_vars:
+ allowed = False
+ for scope in allowed_scopes:
+ if fnmatch.fnmatch(phase_func, scope):
+ allowed = True
+ break
+
+ if not allowed:
+ phase_vars.setdefault(phase_func, []).append((variable, eapi_filter))
+
+ phase_info = {}
+ for phase_func, prohibited_vars in phase_vars.items():
+ phase_func_vars = []
+ for variable, eapi_filter in prohibited_vars:
+ phase_func_vars.append(variable)
+ phase_obj = phase_info[phase_func] = types.SimpleNamespace()
+ phase_obj.prohibited_vars = dict(prohibited_vars)
+ phase_obj.var_names = "(%s)" % "|".join(
+ variable for variable, eapi_filter in prohibited_vars
+ )
+ phase_obj.var_reference = re.compile(
+ r"\$(\{|)%s(\}|\W)" % (phase_obj.var_names,)
+ )
+
+ return phase_info
+
+
+class PMSVariableReference(PhaseCheck):
+ """Check phase scope for references to variables specified by PMS"""
+
+ repoman_check_name = "variable.phase"
+ phase_info = _compile_phases()
+
+ def new(self, pkg):
+ self._eapi = pkg.eapi
+
+ def end(self):
+ self._eapi = None
+
+ def phase_check(self, num, line):
+ try:
+ phase_info = self.phase_info[self.in_phase]
+ except KeyError:
+ return
+
+ eapi = self._eapi
+ issues = []
+ for m in phase_info.var_reference.finditer(line):
+ open_brace = m.group(1)
+ var_name = m.group(2)
+ close_brace = m.group(3)
+ # discard \W if matched by (\}|\W)
+ close_brace = close_brace if close_brace == "}" else ""
+ if bool(open_brace) != bool(close_brace):
+ continue
+ var_name = m.group(2)
+ eapi_filter = phase_info.prohibited_vars[var_name]
+ if eapi_filter is not None and not eapi_filter(eapi):
+ continue
+ issues.append(
+ "phase %s: EAPI %s: variable %s: Forbidden reference to variable specified by PMS"
+ % (self.in_phase, eapi, var_name)
+ )
+ return issues
diff --git a/repoman/lib/repoman/repos.py b/repoman/lib/repoman/repos.py
index 31cb82caf..6df984a28 100644
--- a/repoman/lib/repoman/repos.py
+++ b/repoman/lib/repoman/repos.py
@@ -14,6 +14,7 @@ from portage import os
from portage import _encodings
from portage import _unicode_encode
from portage.checksum import get_valid_checksum_keys
+from portage.repository.config import allow_profile_repo_deps
# pylint: disable=ungrouped-imports
from repoman.errors import err
@@ -233,7 +234,9 @@ def repo_metadata(portdb, repoman_settings):
profile_list = []
global_pmasklines = []
- for path in portdb.porttrees:
+ for repo in portdb.repositories:
+ path = repo.location
+
try:
liclist.update(os.listdir(os.path.join(path, "licenses")))
except OSError:
@@ -265,7 +268,8 @@ def repo_metadata(portdb, repoman_settings):
global_pmasklines.append(
portage.util.grabfile_package(
os.path.join(path, 'profiles', 'package.mask'),
- recursive=1, verify_eapi=True))
+ recursive=1, verify_eapi=True,
+ allow_repo=allow_profile_repo_deps(repo)))
desc_path = os.path.join(path, 'profiles', 'profiles.desc')
try:
diff --git a/repoman/lib/repoman/tests/simple/test_simple.py b/repoman/lib/repoman/tests/simple/test_simple.py
index 2448bb117..3a699a708 100644
--- a/repoman/lib/repoman/tests/simple/test_simple.py
+++ b/repoman/lib/repoman/tests/simple/test_simple.py
@@ -1,23 +1,88 @@
# Copyright 2011-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
+import collections
import subprocess
import sys
import time
+import types
from repoman._portage import portage
from portage import os
-from portage import _unicode_decode
from portage.process import find_binary
from portage.tests.resolver.ResolverPlayground import ResolverPlayground
from portage.util import ensure_dirs
+from portage.util.futures import asyncio
+from portage.util.futures._asyncio.streams import _reader
+from portage.util._async.AsyncFunction import AsyncFunction
# pylint: disable=ungrouped-imports
from repoman import REPOMAN_BASE_PATH
from repoman.copyrights import update_copyright_year
+from repoman.main import _repoman_init, _repoman_scan, _handle_result
from repoman.tests import TestCase
+class RepomanRun(types.SimpleNamespace):
+ async def run(self):
+ self.expected = getattr(self, "expected", None) or {"returncode": 0}
+ if self.debug:
+ fd_pipes = {}
+ pr = None
+ pw = None
+ else:
+ pr, pw = os.pipe()
+ fd_pipes = {1: pw, 2: pw}
+ pr = open(pr, "rb", 0)
+
+ proc = AsyncFunction(
+ scheduler=asyncio.get_event_loop(),
+ target=self._subprocess,
+ args=(self.args, self.cwd, self.env, self.expected, self.debug),
+ fd_pipes=fd_pipes,
+ )
+
+ proc.start()
+ if pw is not None:
+ os.close(pw)
+
+ await proc.async_wait()
+
+ if pr is None:
+ stdio = None
+ else:
+ stdio = await _reader(pr)
+
+ self.result = {
+ "stdio": stdio,
+ "result": proc.result,
+ }
+
+ @staticmethod
+ def _subprocess(args, cwd, env, expected, debug):
+ os.chdir(cwd)
+ os.environ.update(env)
+ repoman_vars = _repoman_init(["repoman"] + args)
+ if repoman_vars.exitcode is not None:
+ return repoman_vars.exitcode
+ result = _repoman_scan(*repoman_vars)
+ returncode = _handle_result(*repoman_vars, result)
+ qawarnings = repoman_vars.vcs_settings.qatracker.qawarnings
+ warns = collections.defaultdict(list)
+ fails = collections.defaultdict(list)
+ for qacat, issues in repoman_vars.vcs_settings.qatracker.fails.items():
+ if qacat in qawarnings:
+ warns[qacat].extend(issues)
+ else:
+ fails[qacat].extend(issues)
+ result = {"returncode": returncode}
+ if fails:
+ result["fails"] = fails
+ if warns:
+ result["warns"] = warns
+ return result
+
+
class SimpleRepomanTestCase(TestCase):
def testCopyrightUpdate(self):
@@ -72,11 +137,17 @@ class SimpleRepomanTestCase(TestCase):
self.assertFalse(True, skip_reason)
return
- copyright_header = """# Copyright 1999-%s Gentoo Foundation
+ copyright_header = """# Copyright 1999-%s Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
-# $Header: $
+
""" % time.gmtime().tm_year
+ pkg_preinst_references_forbidden_var = """
+pkg_preinst() {
+ echo "This ${A} reference is not allowed. Neither is this $BROOT reference."
+}
+"""
+
repo_configs = {
"test_repo": {
"layout.conf":
@@ -130,13 +201,14 @@ class SimpleRepomanTestCase(TestCase):
"dev-libs/C-0": {
"COPYRIGHT_HEADER" : copyright_header,
"DESCRIPTION" : "Desc goes here",
- "EAPI" : "4",
+ "EAPI" : "7",
"HOMEPAGE" : "https://example.com",
"IUSE" : "flag",
# must be unstable, since dev-libs/A[flag] is stable masked
"KEYWORDS": "~x86",
"LICENSE": "GPL-2",
"RDEPEND": "flag? ( dev-libs/A[flag] )",
+ "MISC_CONTENT": pkg_preinst_references_forbidden_var,
},
}
licenses = ["GPL-2"]
@@ -169,6 +241,39 @@ class SimpleRepomanTestCase(TestCase):
playground = ResolverPlayground(ebuilds=ebuilds,
profile=profile, repo_configs=repo_configs, debug=debug)
+
+ loop = asyncio._wrap_loop()
+ loop.run_until_complete(
+ asyncio.ensure_future(
+ self._async_test_simple(
+ playground,
+ metadata_xml_files,
+ profiles,
+ profile,
+ licenses,
+ arch_list,
+ use_desc,
+ metadata_xsd,
+ copyright_header,
+ debug,
+ ),
+ loop=loop,
+ )
+ )
+
+ async def _async_test_simple(
+ self,
+ playground,
+ metadata_xml_files,
+ profiles,
+ profile,
+ licenses,
+ arch_list,
+ use_desc,
+ metadata_xsd,
+ copyright_header,
+ debug,
+ ):
settings = playground.settings
eprefix = settings["EPREFIX"]
eroot = settings["EROOT"]
@@ -195,25 +300,34 @@ class SimpleRepomanTestCase(TestCase):
committer_name = "Gentoo Dev"
committer_email = "gentoo-dev@gentoo.org"
+ expected_warnings = {
+ "returncode": 0,
+ "warns": {
+ "variable.phase": [
+ "dev-libs/C/C-0.ebuild: line 15: phase pkg_preinst: EAPI 7: variable A: Forbidden reference to variable specified by PMS",
+ "dev-libs/C/C-0.ebuild: line 15: phase pkg_preinst: EAPI 7: variable BROOT: Forbidden reference to variable specified by PMS",
+ ]
+ },
+ }
git_test = (
- ("", repoman_cmd + ("manifest",)),
+ ("", RepomanRun(args=["manifest"])),
("", git_cmd + ("config", "--global", "user.name", committer_name,)),
("", git_cmd + ("config", "--global", "user.email", committer_email,)),
("", git_cmd + ("init-db",)),
("", git_cmd + ("add", ".")),
("", git_cmd + ("commit", "-a", "-m", "add whole repo")),
- ("", repoman_cmd + ("full", "-d")),
- ("", repoman_cmd + ("full", "--include-profiles", "default/linux/x86/test_profile")),
+ ("", RepomanRun(args=["full", "-d"], expected=expected_warnings)),
+ ("", RepomanRun(args=["full", "--include-profiles", "default/linux/x86/test_profile"], expected=expected_warnings)),
("", cp_cmd + (test_ebuild, test_ebuild[:-8] + "2.ebuild")),
("", git_cmd + ("add", test_ebuild[:-8] + "2.ebuild")),
- ("", repoman_cmd + ("commit", "-m", "cat/pkg: bump to version 2")),
+ ("", RepomanRun(args=["commit", "-m", "cat/pkg: bump to version 2"], expected=expected_warnings)),
("", cp_cmd + (test_ebuild, test_ebuild[:-8] + "3.ebuild")),
("", git_cmd + ("add", test_ebuild[:-8] + "3.ebuild")),
- ("dev-libs", repoman_cmd + ("commit", "-m", "cat/pkg: bump to version 3")),
+ ("dev-libs", RepomanRun(args=["commit", "-m", "cat/pkg: bump to version 3"], expected=expected_warnings)),
("", cp_cmd + (test_ebuild, test_ebuild[:-8] + "4.ebuild")),
("", git_cmd + ("add", test_ebuild[:-8] + "4.ebuild")),
- ("dev-libs/A", repoman_cmd + ("commit", "-m", "cat/pkg: bump to version 4")),
+ ("dev-libs/A", RepomanRun(args=["commit", "-m", "cat/pkg: bump to version 4"])),
)
env = {
@@ -282,41 +396,66 @@ class SimpleRepomanTestCase(TestCase):
# triggered by python -Wd will be visible.
stdout = subprocess.PIPE
- for cwd in ("", "dev-libs", "dev-libs/A", "dev-libs/B"):
+ for cwd in ("", "dev-libs", "dev-libs/A", "dev-libs/B", "dev-libs/C"):
abs_cwd = os.path.join(test_repo_symlink, cwd)
- proc = subprocess.Popen(repoman_cmd + ("full",),
- cwd=abs_cwd, env=env, stdout=stdout)
+
+ proc = await asyncio.create_subprocess_exec(
+ *(repoman_cmd + ("full",)),
+ env=env,
+ stderr=None,
+ stdout=stdout,
+ cwd=abs_cwd
+ )
if debug:
- proc.wait()
+ await proc.wait()
else:
- output = proc.stdout.readlines()
- proc.wait()
- proc.stdout.close()
+ output, _err = await proc.communicate()
+ await proc.wait()
if proc.returncode != os.EX_OK:
- for line in output:
- sys.stderr.write(_unicode_decode(line))
+ portage.writemsg(output)
- self.assertEqual(os.EX_OK, proc.returncode,
- "repoman failed in %s" % (cwd,))
+ self.assertEqual(
+ os.EX_OK, proc.returncode, "repoman failed in %s" % (cwd,)
+ )
if git_binary is not None:
for cwd, cmd in git_test:
abs_cwd = os.path.join(test_repo_symlink, cwd)
- proc = subprocess.Popen(cmd,
- cwd=abs_cwd, env=env, stdout=stdout)
+ if isinstance(cmd, RepomanRun):
+ cmd.cwd = abs_cwd
+ cmd.env = env
+ cmd.debug = debug
+ await cmd.run()
+ if cmd.result["result"] != cmd.expected and cmd.result.get("stdio"):
+ portage.writemsg(cmd.result["stdio"])
+ try:
+ self.assertEqual(cmd.result["result"], cmd.expected)
+ except Exception:
+ print(cmd.result["result"], file=sys.stderr, flush=True)
+ raise
+ continue
+
+ proc = await asyncio.create_subprocess_exec(
+ *cmd, env=env, stderr=None, stdout=stdout, cwd=abs_cwd
+ )
if debug:
- proc.wait()
+ await proc.wait()
else:
- output = proc.stdout.readlines()
- proc.wait()
- proc.stdout.close()
+ output, _err = await proc.communicate()
+ await proc.wait()
if proc.returncode != os.EX_OK:
- for line in output:
- sys.stderr.write(_unicode_decode(line))
-
- self.assertEqual(os.EX_OK, proc.returncode,
- "%s failed in %s" % (cmd, cwd,))
+ portage.writemsg(output)
+
+ self.assertEqual(
+ os.EX_OK,
+ proc.returncode,
+ "%s failed in %s"
+ % (
+ cmd,
+ cwd,
+ ),
+ )
finally:
playground.cleanup()
diff --git a/repoman/man/repoman.1 b/repoman/man/repoman.1
index 0926e806c..5dbc41560 100644
--- a/repoman/man/repoman.1
+++ b/repoman/man/repoman.1
@@ -1,4 +1,4 @@
-.TH "REPOMAN" "1" "Aug 2020" "Repoman VERSION" "Repoman"
+.TH "REPOMAN" "1" "March 2021" "Repoman VERSION" "Repoman"
.SH NAME
repoman \- Gentoo's program to enforce a minimal level of quality assurance in
packages added to the ebuild repository
@@ -445,6 +445,9 @@ The ebuild makes use of an obsolete construct
A variable contains an invalid character that is not part of the ASCII
character set.
.TP
+.B variable.phase
+Variable referenced found within scope of incorrect ebuild phase as specified by PMS.
+.TP
.B variable.readonly
Assigning a readonly variable
.TP
diff --git a/setup.py b/setup.py
index e473e7db5..06e67f369 100755
--- a/setup.py
+++ b/setup.py
@@ -502,10 +502,13 @@ class x_install_lib(install_lib):
with codecs.open(path, "w", "utf-8") as f:
f.write(data)
- val_dict = {
- "GLOBAL_CONFIG_PATH": self.portage_confdir,
- }
+ val_dict = {}
if create_entry_points:
+ val_dict.update(
+ {
+ "GLOBAL_CONFIG_PATH": self.portage_confdir,
+ }
+ )
re_sub_file(
"portage/const.py",
(
@@ -716,7 +719,7 @@ class build_ext(_build_ext):
setup(
name = 'portage',
- version = '3.0.17',
+ version = '3.0.18',
url = 'https://wiki.gentoo.org/wiki/Project:Portage',
project_urls = {
'Release Notes': 'https://gitweb.gentoo.org/proj/portage.git/plain/RELEASE-NOTES',