commit: 3fa274977405f73221055c7287880a9dc3038765
Author: Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
AuthorDate: Fri Oct 7 18:52:15 2022 +0000
Commit: Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Sat Oct 8 09:58:19 2022 +0000
URL:
https://gitweb.gentoo.org/proj/pkgcore/pkgcore.git/commit/?id=3fa27497
profiles: support package.bashrc files
Add support for `profile-bashrcs` profile format, which adds support for
per-profile bashrc mechanism `package.bashrc`, which enables to specify
per restriction the extra bashrc files to run.
See portage(5) for format details.
Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org>
src/pkgcore/ebuild/domain.py | 9 +++---
src/pkgcore/ebuild/inspect_profile.py | 9 ++++++
src/pkgcore/ebuild/profiles.py | 26 ++++++++++++++++
src/pkgcore/ebuild/repo_objs.py | 2 +-
tests/ebuild/test_profiles.py | 56 +++++++++++++++++++++++++++++++++++
5 files changed, 97 insertions(+), 5 deletions(-)
diff --git a/src/pkgcore/ebuild/domain.py b/src/pkgcore/ebuild/domain.py
index 47f31ef2f..a1f231e9f 100644
--- a/src/pkgcore/ebuild/domain.py
+++ b/src/pkgcore/ebuild/domain.py
@@ -596,10 +596,11 @@ class domain(config_domain):
return self
def get_package_bashrcs(self, pkg):
- for source in self.profile.bashrcs:
- yield source
- for source in self.bashrcs:
- yield source
+ yield from self.profile.bashrcs
+ for restrict, bashrcs in self.profile.pkg_bashrcs:
+ if restrict.match(pkg):
+ yield from bashrcs
+ yield from self.bashrcs
if not os.path.exists(self.ebuild_hook_dir):
return
# matching portage behavior... it's whacked.
diff --git a/src/pkgcore/ebuild/inspect_profile.py
b/src/pkgcore/ebuild/inspect_profile.py
index a0be246c1..dc8c5942d 100644
--- a/src/pkgcore/ebuild/inspect_profile.py
+++ b/src/pkgcore/ebuild/inspect_profile.py
@@ -187,6 +187,15 @@ class bashrcs(_base, metaclass=_register_command):
out.write(bashrc.path)
+class package_bashrc(_base, metaclass=_register_command):
+ """inspect package.bashrc"""
+
+ def __call__(self, namespace, out, err):
+ for package, bashrcs in namespace.profile.pkg_bashrcs:
+ bashrcs = ", ".join(s.path for s in bashrcs)
+ out.write(f'{package}: {bashrcs}')
+
+
class keywords(_base, metaclass=_register_command):
"""inspect package.keywords"""
diff --git a/src/pkgcore/ebuild/profiles.py b/src/pkgcore/ebuild/profiles.py
index 4607639cb..34e839e9b 100644
--- a/src/pkgcore/ebuild/profiles.py
+++ b/src/pkgcore/ebuild/profiles.py
@@ -452,6 +452,27 @@ class ProfileNode(metaclass=caching.WeakInstMeta):
return local_source(path)
return None
+ @load_property("package.bashrc", allow_recurse=True)
+ def pkg_bashrc(self, data):
+ repo_config = self.repoconfig
+ if repo_config is None or 'profile-bashrcs' not in
repo_config.profile_formats:
+ return ()
+
+ d = defaultdict(list)
+ for line, lineno, relpath in data:
+ l = line.split()
+ try:
+ a = self.eapi_atom(l[0])
+ except ebuild_errors.MalformedAtom as exc:
+ logger.error(f'{relpath!r}, line {lineno}: parsing error:
{exc}')
+ continue
+ if len(l) == 1:
+ logger.error(f'{relpath!r}, line {lineno}: missing bashrc
files: {line!r}')
+ continue
+ for filename in l[1:]:
+ d[a].append(local_source(pjoin(self.path, 'bashrc', filename)))
+ return tuple((k, tuple(v)) for k, v in d.items())
+
@load_property('eapi', fallback='0')
def eapi(self, data):
# handle fallback
@@ -522,6 +543,7 @@ class EmptyRootNode(ProfileNode):
deprecated = None
pkg_use = masked_use = stable_masked_use = forced_use = stable_forced_use
= misc.ChunkedDataDict()
forced_use.freeze()
+ pkg_bashrc = ()
pkg_use_force = pkg_use_mask = ImmutableDict()
pkg_provided = system = profile_set = ((), ())
@@ -747,6 +769,10 @@ class ProfileStack:
def bashrcs(self):
return tuple(x.bashrc for x in self.stack if x.bashrc is not None)
+ @klass.jit_attr
+ def pkg_bashrcs(self):
+ return tuple(chain.from_iterable(x.pkg_bashrc for x in self.stack))
+
bashrc = klass.alias_attr("bashrcs")
path = klass.alias_attr("node.path")
diff --git a/src/pkgcore/ebuild/repo_objs.py b/src/pkgcore/ebuild/repo_objs.py
index 633c858e3..78371e29a 100644
--- a/src/pkgcore/ebuild/repo_objs.py
+++ b/src/pkgcore/ebuild/repo_objs.py
@@ -682,7 +682,7 @@ class RepoConfig(syncable.tree, klass.ImmutableInstance,
metaclass=WeakInstMeta)
default_hashes = ('size', 'blake2b', 'sha512')
default_required_hashes = ('size', 'blake2b')
- supported_profile_formats = ('pms', 'portage-1', 'portage-2',
'profile-set')
+ supported_profile_formats = ('pms', 'portage-1', 'portage-2',
'profile-bashrcs', 'profile-set')
supported_cache_formats = ('md5-dict', 'pms')
__inst_caching__ = True
diff --git a/tests/ebuild/test_profiles.py b/tests/ebuild/test_profiles.py
index 8f920825c..a7abfe9f2 100644
--- a/tests/ebuild/test_profiles.py
+++ b/tests/ebuild/test_profiles.py
@@ -545,6 +545,17 @@ class TestPmsProfileNode(profile_mixin):
self.write_file(tmp_path, "profile.bashrc", '')
assert self.klass(path).bashrc is not None
+ def test_pkg_bashrc(self, tmp_path, caplog):
+ path = tmp_path / self.profile
+ assert not self.klass(path).pkg_bashrc
+ self.write_file(tmp_path, "package.bashrc", "@dsfg",
profile=self.profile)
+ assert not self.klass(path).pkg_bashrc
+ self.write_file(tmp_path, "package.bashrc", "dev-util/foo",
profile=self.profile)
+ assert not self.klass(path).pkg_bashrc
+ self.write_file(tmp_path, "package.bashrc", "dev-util/foo file1
file2\ndev-util/bar file3", profile=self.profile)
+ assert not self.klass(path).pkg_bashrc
+ assert not caplog.text
+
class TestPortage1ProfileNode(TestPmsProfileNode):
@@ -593,6 +604,51 @@ class TestPortage2ProfileNode(TestPortage1ProfileNode):
(tmp_path / "metadata" / "layout.conf").write_text("masters =
''\nprofile-formats = portage-2")
+class TestProfileBashrcProfileNode(TestPmsProfileNode):
+
+ profile = os.path.join("profiles", "default")
+
+ def assert_pkg_bashrc(self, actual, expected):
+ assert expected == {
+ str(k): [s.path for s in v]
+ for k, v in actual
+ }
+
+ def setup_repo(self, tmp_path):
+ (tmp_path / "profiles" /
"repo_name").write_bytes(binascii.b2a_hex(os.urandom(10)))
+ (tmp_path / "metadata").mkdir()
+ (tmp_path / "metadata" / "layout.conf").write_text("masters =
''\nprofile-formats = profile-bashrcs")
+
+ def test_pkg_bashrc(self, tmp_path, caplog):
+ path = tmp_path / self.profile
+ assert not self.klass(path).pkg_bashrc
+ self.parsing_checks(tmp_path, "package.bashrc", "pkg_bashrc")
+ assert not caplog.text
+
+ caplog.clear()
+ self.write_file(tmp_path, "package.bashrc", "@dsfg",
profile=self.profile)
+ assert not self.klass(path).pkg_bashrc
+ assert "line 1: parsing error: invalid package atom: '@dsfg'" in
caplog.text
+
+ caplog.clear()
+ self.write_file(tmp_path, "package.bashrc", "dev-util/foo",
profile=self.profile)
+ assert not self.klass(path).pkg_bashrc
+ assert "line 1: missing bashrc files: 'dev-util/foo'" in caplog.text
+
+ caplog.clear()
+ self.write_file(tmp_path, "package.bashrc", "dev-util/foo file1",
profile=self.profile)
+ self.assert_pkg_bashrc(self.klass(path).pkg_bashrc, {"dev-util/foo":
[str(path / "bashrc/file1")]})
+ assert not caplog.text
+
+ caplog.clear()
+ self.write_file(tmp_path, "package.bashrc", "dev-util/foo file1
file2\ndev-util/bar file3", profile=self.profile)
+ self.assert_pkg_bashrc(self.klass(path).pkg_bashrc, {
+ "dev-util/foo": [str(path / "bashrc/file1"), str(path /
"bashrc/file2")],
+ "dev-util/bar": [str(path / "bashrc/file3")],
+ })
+ assert not caplog.text
+
+
class TestProfileSetProfileNode(TestPmsProfileNode):
profile = os.path.join("profiles", "default")