diff options
| author | Richard Purdie <richard.purdie@linuxfoundation.org> | 2025-06-10 15:53:27 +0100 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2025-08-12 10:05:11 +0100 |
| commit | bdb5e99854c76f31d5463f55a657d40098a0743a (patch) | |
| tree | 1b77d9e688cc1ab0002695b84a50e80ceda37494 /meta/lib | |
| parent | 7c9a1f20f1f07d8a946f300878e680103d70ac84 (diff) | |
| download | poky-bdb5e99854c76f31d5463f55a657d40098a0743a.tar.gz | |
classes/lib: Switch classextend to use new filter API
Currently, class extensions are implmented using shadow variables and
access indirection which is horribly ineffient and ugly.
Switch to using the new bitbake filter API, which allows a translation
of the variable before the expanded value is returned. This allows us
to drop the shadow variable accesses. It also avoids the need to iterate
PACKAGES and make many variable changes since a filter against RDEPENDS
applies to RDEPENDS:${PN} and all of it's other overridden values.
Since data expansion happens at access, it also avoids many of the race
conditions this code has tranditionally been plagued with.
(From OE-Core rev: 24a9858a8927e91d499ee342ed93a0dbb44d83bc)
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib')
| -rw-r--r-- | meta/lib/oe/__init__.py | 2 | ||||
| -rw-r--r-- | meta/lib/oe/classextend.py | 255 |
2 files changed, 119 insertions, 138 deletions
diff --git a/meta/lib/oe/__init__.py b/meta/lib/oe/__init__.py index 73de774266..9e4134c483 100644 --- a/meta/lib/oe/__init__.py +++ b/meta/lib/oe/__init__.py | |||
| @@ -12,4 +12,4 @@ __path__ = extend_path(__path__, __name__) | |||
| 12 | BBIMPORTS = ["qa", "data", "path", "utils", "types", "package", "packagedata", \ | 12 | BBIMPORTS = ["qa", "data", "path", "utils", "types", "package", "packagedata", \ |
| 13 | "packagegroup", "sstatesig", "lsb", "cachedpath", "license", "qemu", \ | 13 | "packagegroup", "sstatesig", "lsb", "cachedpath", "license", "qemu", \ |
| 14 | "reproducible", "rust", "buildcfg", "go", "spdx30_tasks", "spdx_common", \ | 14 | "reproducible", "rust", "buildcfg", "go", "spdx30_tasks", "spdx_common", \ |
| 15 | "cve_check", "tune"] | 15 | "cve_check", "tune", "classextend"] |
diff --git a/meta/lib/oe/classextend.py b/meta/lib/oe/classextend.py index 8ae5d3b715..ddca10dee5 100644 --- a/meta/lib/oe/classextend.py +++ b/meta/lib/oe/classextend.py | |||
| @@ -5,155 +5,136 @@ | |||
| 5 | # | 5 | # |
| 6 | 6 | ||
| 7 | import collections | 7 | import collections |
| 8 | import bb.filter | ||
| 9 | |||
| 10 | @bb.filter.filter_proc() | ||
| 11 | def native_filter(val, pn, bpn, regex=False, selfref=True): | ||
| 12 | deps = val | ||
| 13 | if not deps: | ||
| 14 | return | ||
| 15 | deps = bb.utils.explode_deps(deps) | ||
| 16 | newdeps = [] | ||
| 17 | for dep in deps: | ||
| 18 | if regex and dep.startswith("^") and dep.endswith("$"): | ||
| 19 | if not dep.endswith("-native$"): | ||
| 20 | newdeps.append(dep[:-1].replace(pn, bpn) + "-native$") | ||
| 21 | else: | ||
| 22 | newdeps.append(dep) | ||
| 23 | elif dep == pn: | ||
| 24 | if not selfref: | ||
| 25 | continue | ||
| 26 | newdeps.append(dep) | ||
| 27 | elif "-cross-" in dep: | ||
| 28 | newdeps.append(dep.replace("-cross", "-native")) | ||
| 29 | elif not dep.endswith("-native"): | ||
| 30 | # Replace ${PN} with ${BPN} in the dependency to make sure | ||
| 31 | # dependencies on, e.g., ${PN}-foo become ${BPN}-foo-native | ||
| 32 | # rather than ${BPN}-native-foo-native. | ||
| 33 | newdeps.append(dep.replace(pn, bpn) + "-native") | ||
| 34 | else: | ||
| 35 | newdeps.append(dep) | ||
| 36 | return " ".join(newdeps) | ||
| 37 | |||
| 38 | def add_suffix(val, extname, prefixes): | ||
| 39 | if val.startswith(extname + "-"): | ||
| 40 | return val | ||
| 41 | if val.endswith(("-native", "-native-runtime")) or ('nativesdk-' in val) or ('-cross-' in val) or ('-crosssdk-' in val): | ||
| 42 | return val | ||
| 43 | # If it starts with a known prefix (e.g. multilibs), just pass it through | ||
| 44 | for prefix in prefixes: | ||
| 45 | if val.startswith(prefix + "-"): | ||
| 46 | return val | ||
| 47 | if val.startswith("kernel-") or val == "virtual/kernel": | ||
| 48 | return val | ||
| 49 | if val.startswith("rtld"): | ||
| 50 | return val | ||
| 51 | if val.endswith("-crosssdk"): | ||
| 52 | return val | ||
| 53 | if val.endswith("-" + extname): | ||
| 54 | val = val.replace("-" + extname, "") | ||
| 55 | if val.startswith("virtual/"): | ||
| 56 | # Assume large numbers of dashes means a triplet is present and we don't need to convert | ||
| 57 | if val.count("-") >= 3 and val.endswith(("-go",)): | ||
| 58 | return val | ||
| 59 | subs = val.split("/", 1)[1] | ||
| 60 | if not subs.startswith(extname): | ||
| 61 | return "virtual/" + extname + "-" + subs | ||
| 62 | return val | ||
| 63 | if val.startswith("/") or (val.startswith("${") and val.endswith("}")): | ||
| 64 | return val | ||
| 65 | if not val.startswith(extname): | ||
| 66 | return extname + "-" + val | ||
| 67 | return val | ||
| 68 | |||
| 69 | def get_package_mappings(packages, extname): | ||
| 70 | pkgs_mapping = [] | ||
| 71 | for pkg in packages.split(): | ||
| 72 | if pkg.startswith(extname): | ||
| 73 | pkgs_mapping.append([pkg.split(extname + "-")[1], pkg]) | ||
| 74 | continue | ||
| 75 | pkgs_mapping.append([pkg, add_suffix(pkg, extname, [])]) | ||
| 76 | return pkgs_mapping | ||
| 77 | |||
| 78 | @bb.filter.filter_proc() | ||
| 79 | def package_suffix_filter(val, extname): | ||
| 80 | pkgs_mapping = get_package_mappings(val, extname) | ||
| 81 | return " ".join([row[1] for row in pkgs_mapping]) | ||
| 82 | |||
| 83 | @bb.filter.filter_proc() | ||
| 84 | def suffix_filter(val, extname, prefixes): | ||
| 85 | newdeps = [] | ||
| 86 | for dep in val.split(): | ||
| 87 | newdeps.append(add_suffix(dep, extname, prefixes)) | ||
| 88 | return " ".join(newdeps) | ||
| 89 | |||
| 90 | @bb.filter.filter_proc() | ||
| 91 | def suffix_filter_regex(val, extname, prefixes): | ||
| 92 | newvar = [] | ||
| 93 | for v in val.split(): | ||
| 94 | if v.startswith("^" + extname): | ||
| 95 | newvar.append(v) | ||
| 96 | elif v.startswith("^"): | ||
| 97 | newvar.append("^" + extname + "-" + v[1:]) | ||
| 98 | else: | ||
| 99 | newvar.append(add_suffix(v, extname, prefixes)) | ||
| 100 | return " ".join(newvar) | ||
| 8 | 101 | ||
| 9 | def get_packages(d): | 102 | @bb.filter.filter_proc() |
| 10 | pkgs = d.getVar("PACKAGES_NONML") | 103 | def suffix_filter_deps(val, extname, prefixes): |
| 11 | extcls = d.getVar("EXTENDERCLASS") | 104 | deps = bb.utils.explode_dep_versions2(val) |
| 12 | return extcls.rename_packages_internal(pkgs) | 105 | newdeps = collections.OrderedDict() |
| 13 | 106 | for dep in deps: | |
| 14 | def get_depends(varprefix, d): | 107 | newdeps[add_suffix(dep, extname, prefixes)] = deps[dep] |
| 15 | extcls = d.getVar("EXTENDERCLASS") | 108 | return bb.utils.join_deps(newdeps, False) |
| 16 | return extcls.map_depends_variable(varprefix + "_NONML") | ||
| 17 | 109 | ||
| 18 | class ClassExtender(object): | 110 | class ClassExtender(object): |
| 19 | def __init__(self, extname, d): | 111 | def __init__(self, extname, prefixes, d): |
| 20 | self.extname = extname | 112 | self.extname = extname |
| 21 | self.d = d | 113 | self.d = d |
| 22 | self.pkgs_mapping = [] | 114 | self.prefixes = prefixes |
| 23 | self.d.setVar("EXTENDERCLASS", self) | ||
| 24 | |||
| 25 | def extend_name(self, name): | ||
| 26 | if name.startswith("kernel-") or name == "virtual/kernel": | ||
| 27 | return name | ||
| 28 | if name.startswith("rtld"): | ||
| 29 | return name | ||
| 30 | if name.endswith("-crosssdk"): | ||
| 31 | return name | ||
| 32 | if name.endswith("-" + self.extname): | ||
| 33 | name = name.replace("-" + self.extname, "") | ||
| 34 | if name.startswith("virtual/"): | ||
| 35 | # Assume large numbers of dashes means a triplet is present and we don't need to convert | ||
| 36 | if name.count("-") >= 3 and name.endswith(("-go",)): | ||
| 37 | return name | ||
| 38 | subs = name.split("/", 1)[1] | ||
| 39 | if not subs.startswith(self.extname): | ||
| 40 | return "virtual/" + self.extname + "-" + subs | ||
| 41 | return name | ||
| 42 | if name.startswith("/") or (name.startswith("${") and name.endswith("}")): | ||
| 43 | return name | ||
| 44 | if not name.startswith(self.extname): | ||
| 45 | return self.extname + "-" + name | ||
| 46 | return name | ||
| 47 | |||
| 48 | def map_variable(self, varname, setvar = True): | ||
| 49 | var = self.d.getVar(varname) | ||
| 50 | if not var: | ||
| 51 | return "" | ||
| 52 | var = var.split() | ||
| 53 | newvar = [] | ||
| 54 | for v in var: | ||
| 55 | newvar.append(self.extend_name(v)) | ||
| 56 | newdata = " ".join(newvar) | ||
| 57 | if setvar: | ||
| 58 | self.d.setVar(varname, newdata) | ||
| 59 | return newdata | ||
| 60 | |||
| 61 | def map_regexp_variable(self, varname, setvar = True): | ||
| 62 | var = self.d.getVar(varname) | ||
| 63 | if not var: | ||
| 64 | return "" | ||
| 65 | var = var.split() | ||
| 66 | newvar = [] | ||
| 67 | for v in var: | ||
| 68 | if v.startswith("^" + self.extname): | ||
| 69 | newvar.append(v) | ||
| 70 | elif v.startswith("^"): | ||
| 71 | newvar.append("^" + self.extname + "-" + v[1:]) | ||
| 72 | else: | ||
| 73 | newvar.append(self.extend_name(v)) | ||
| 74 | newdata = " ".join(newvar) | ||
| 75 | if setvar: | ||
| 76 | self.d.setVar(varname, newdata) | ||
| 77 | return newdata | ||
| 78 | 115 | ||
| 79 | def map_depends(self, dep): | 116 | def set_filter(self, var, deps): |
| 80 | if dep.endswith(("-native", "-native-runtime")) or ('nativesdk-' in dep) or ('cross-canadian' in dep) or ('-crosssdk-' in dep): | 117 | if deps: |
| 81 | return dep | 118 | self.d.setVarFilter(var, "suffix_filter_deps(val, '" + self.extname + "', " + str(self.prefixes) + ")") |
| 82 | else: | 119 | else: |
| 83 | # Do not extend for that already have multilib prefix | 120 | self.d.setVarFilter(var, "suffix_filter(val, '" + self.extname + "', " + str(self.prefixes) + ")") |
| 84 | var = self.d.getVar("MULTILIB_VARIANTS") | ||
| 85 | if var: | ||
| 86 | var = var.split() | ||
| 87 | for v in var: | ||
| 88 | if dep.startswith(v): | ||
| 89 | return dep | ||
| 90 | return self.extend_name(dep) | ||
| 91 | |||
| 92 | def map_depends_variable(self, varname, suffix = ""): | ||
| 93 | # We need to preserve EXTENDPKGV so it can be expanded correctly later | ||
| 94 | if suffix: | ||
| 95 | varname = varname + ":" + suffix | ||
| 96 | orig = self.d.getVar("EXTENDPKGV", False) | ||
| 97 | self.d.setVar("EXTENDPKGV", "EXTENDPKGV") | ||
| 98 | deps = self.d.getVar(varname) | ||
| 99 | if not deps: | ||
| 100 | self.d.setVar("EXTENDPKGV", orig) | ||
| 101 | return | ||
| 102 | deps = bb.utils.explode_dep_versions2(deps) | ||
| 103 | newdeps = collections.OrderedDict() | ||
| 104 | for dep in deps: | ||
| 105 | newdeps[self.map_depends(dep)] = deps[dep] | ||
| 106 | |||
| 107 | if not varname.endswith("_NONML"): | ||
| 108 | self.d.renameVar(varname, varname + "_NONML") | ||
| 109 | self.d.setVar(varname, "${@oe.classextend.get_depends('%s', d)}" % varname) | ||
| 110 | self.d.appendVarFlag(varname, "vardeps", " " + varname + "_NONML") | ||
| 111 | ret = bb.utils.join_deps(newdeps, False).replace("EXTENDPKGV", "${EXTENDPKGV}") | ||
| 112 | self.d.setVar("EXTENDPKGV", orig) | ||
| 113 | return ret | ||
| 114 | 121 | ||
| 115 | def map_packagevars(self): | 122 | def map_packagevars(self): |
| 116 | for pkg in (self.d.getVar("PACKAGES").split() + [""]): | 123 | self.set_filter("RDEPENDS", deps=True) |
| 117 | self.map_depends_variable("RDEPENDS", pkg) | 124 | self.set_filter("RRECOMMENDS", deps=True) |
| 118 | self.map_depends_variable("RRECOMMENDS", pkg) | 125 | self.set_filter("RSUGGESTS", deps=True) |
| 119 | self.map_depends_variable("RSUGGESTS", pkg) | 126 | self.set_filter("RPROVIDES", deps=True) |
| 120 | self.map_depends_variable("RPROVIDES", pkg) | 127 | self.set_filter("RREPLACES", deps=True) |
| 121 | self.map_depends_variable("RREPLACES", pkg) | 128 | self.set_filter("RCONFLICTS", deps=True) |
| 122 | self.map_depends_variable("RCONFLICTS", pkg) | 129 | self.set_filter("PKG", deps=True) |
| 123 | self.map_depends_variable("PKG", pkg) | ||
| 124 | |||
| 125 | def rename_packages(self): | ||
| 126 | for pkg in (self.d.getVar("PACKAGES") or "").split(): | ||
| 127 | if pkg.startswith(self.extname): | ||
| 128 | self.pkgs_mapping.append([pkg.split(self.extname + "-")[1], pkg]) | ||
| 129 | continue | ||
| 130 | self.pkgs_mapping.append([pkg, self.extend_name(pkg)]) | ||
| 131 | |||
| 132 | self.d.renameVar("PACKAGES", "PACKAGES_NONML") | ||
| 133 | self.d.setVar("PACKAGES", "${@oe.classextend.get_packages(d)}") | ||
| 134 | |||
| 135 | def rename_packages_internal(self, pkgs): | ||
| 136 | self.pkgs_mapping = [] | ||
| 137 | for pkg in (self.d.expand(pkgs) or "").split(): | ||
| 138 | if pkg.startswith(self.extname): | ||
| 139 | self.pkgs_mapping.append([pkg.split(self.extname + "-")[1], pkg]) | ||
| 140 | continue | ||
| 141 | self.pkgs_mapping.append([pkg, self.extend_name(pkg)]) | ||
| 142 | |||
| 143 | return " ".join([row[1] for row in self.pkgs_mapping]) | ||
| 144 | 130 | ||
| 145 | def rename_package_variables(self, variables): | 131 | def rename_package_variables(self, variables): |
| 146 | for pkg_mapping in self.pkgs_mapping: | 132 | pkgs_mapping = get_package_mappings(self.d.getVar('PACKAGES'), self.extname) |
| 133 | self.d.setVarFilter('PACKAGES', "package_suffix_filter(val, '" + self.extname + "')") | ||
| 134 | self.d.setVarFilter('PACKAGES_DYNAMIC', "suffix_filter_regex(val, '" + self.extname + "', " + str(self.prefixes) + ")") | ||
| 135 | |||
| 136 | for pkg_mapping in pkgs_mapping: | ||
| 147 | if pkg_mapping[0].startswith("${") and pkg_mapping[0].endswith("}"): | 137 | if pkg_mapping[0].startswith("${") and pkg_mapping[0].endswith("}"): |
| 148 | continue | 138 | continue |
| 149 | for subs in variables: | 139 | for subs in variables: |
| 150 | self.d.renameVar("%s:%s" % (subs, pkg_mapping[0]), "%s:%s" % (subs, pkg_mapping[1])) | 140 | self.d.renameVar("%s:%s" % (subs, pkg_mapping[0]), "%s:%s" % (subs, pkg_mapping[1])) |
| 151 | |||
| 152 | class NativesdkClassExtender(ClassExtender): | ||
| 153 | def map_depends(self, dep): | ||
| 154 | if dep.startswith(self.extname): | ||
| 155 | return dep | ||
| 156 | if dep.endswith(("-native", "-native-runtime")) or ('nativesdk-' in dep) or ('-cross-' in dep) or ('-crosssdk-' in dep): | ||
| 157 | return dep | ||
| 158 | else: | ||
| 159 | return self.extend_name(dep) | ||
