summaryrefslogtreecommitdiffstats
path: root/meta/lib
diff options
context:
space:
mode:
authorRichard Purdie <richard.purdie@linuxfoundation.org>2025-06-10 15:53:27 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2025-08-12 10:05:11 +0100
commitbdb5e99854c76f31d5463f55a657d40098a0743a (patch)
tree1b77d9e688cc1ab0002695b84a50e80ceda37494 /meta/lib
parent7c9a1f20f1f07d8a946f300878e680103d70ac84 (diff)
downloadpoky-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__.py2
-rw-r--r--meta/lib/oe/classextend.py255
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__)
12BBIMPORTS = ["qa", "data", "path", "utils", "types", "package", "packagedata", \ 12BBIMPORTS = ["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
7import collections 7import collections
8import bb.filter
9
10@bb.filter.filter_proc()
11def 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
38def 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
69def 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()
79def 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()
84def 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()
91def 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
9def get_packages(d): 102@bb.filter.filter_proc()
10 pkgs = d.getVar("PACKAGES_NONML") 103def 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:
14def 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
18class ClassExtender(object): 110class 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
152class 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)