summaryrefslogtreecommitdiffstats
path: root/meta/classes/license_image.bbclass
diff options
context:
space:
mode:
authorRichard Purdie <richard.purdie@linuxfoundation.org>2018-07-23 15:34:31 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2018-07-25 16:48:27 +0100
commita6a865ba933ca7f8a2f93ac4aeaa918ef42d36b1 (patch)
tree2fc1acc178b51458d2e56c5f7bd164b29aabf63a /meta/classes/license_image.bbclass
parent2dda838683bea563624f0588ae14fc778955fe3c (diff)
downloadpoky-a6a865ba933ca7f8a2f93ac4aeaa918ef42d36b1.tar.gz
license: Split image license functions to a separate class
This means the image code is only included in image recipes through the IMAGE_CLASSES variable. This sets things up to allow us to fix image deploy dependency problems. (From OE-Core rev: fd44b8b4b2484f2d35c7a0e749e7dc316d601989) Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/classes/license_image.bbclass')
-rw-r--r--meta/classes/license_image.bbclass284
1 files changed, 284 insertions, 0 deletions
diff --git a/meta/classes/license_image.bbclass b/meta/classes/license_image.bbclass
new file mode 100644
index 0000000000..efeedce519
--- /dev/null
+++ b/meta/classes/license_image.bbclass
@@ -0,0 +1,284 @@
1python write_package_manifest() {
2 # Get list of installed packages
3 license_image_dir = d.expand('${LICENSE_DIRECTORY}/${IMAGE_NAME}')
4 bb.utils.mkdirhier(license_image_dir)
5 from oe.rootfs import image_list_installed_packages
6 from oe.utils import format_pkg_list
7
8 pkgs = image_list_installed_packages(d)
9 output = format_pkg_list(pkgs)
10 open(os.path.join(license_image_dir, 'package.manifest'),
11 'w+').write(output)
12}
13
14python write_deploy_manifest() {
15 license_deployed_manifest(d)
16}
17
18python license_create_manifest() {
19 import oe.packagedata
20 from oe.rootfs import image_list_installed_packages
21
22 build_images_from_feeds = d.getVar('BUILD_IMAGES_FROM_FEEDS')
23 if build_images_from_feeds == "1":
24 return 0
25
26 pkg_dic = {}
27 for pkg in sorted(image_list_installed_packages(d)):
28 pkg_info = os.path.join(d.getVar('PKGDATA_DIR'),
29 'runtime-reverse', pkg)
30 pkg_name = os.path.basename(os.readlink(pkg_info))
31
32 pkg_dic[pkg_name] = oe.packagedata.read_pkgdatafile(pkg_info)
33 if not "LICENSE" in pkg_dic[pkg_name].keys():
34 pkg_lic_name = "LICENSE_" + pkg_name
35 pkg_dic[pkg_name]["LICENSE"] = pkg_dic[pkg_name][pkg_lic_name]
36
37 rootfs_license_manifest = os.path.join(d.getVar('LICENSE_DIRECTORY'),
38 d.getVar('IMAGE_NAME'), 'license.manifest')
39 write_license_files(d, rootfs_license_manifest, pkg_dic)
40}
41
42def write_license_files(d, license_manifest, pkg_dic):
43 import re
44
45 bad_licenses = (d.getVar("INCOMPATIBLE_LICENSE") or "").split()
46 bad_licenses = map(lambda l: canonical_license(d, l), bad_licenses)
47 bad_licenses = expand_wildcard_licenses(d, bad_licenses)
48
49 with open(license_manifest, "w") as license_file:
50 for pkg in sorted(pkg_dic):
51 if bad_licenses:
52 try:
53 (pkg_dic[pkg]["LICENSE"], pkg_dic[pkg]["LICENSES"]) = \
54 oe.license.manifest_licenses(pkg_dic[pkg]["LICENSE"],
55 bad_licenses, canonical_license, d)
56 except oe.license.LicenseError as exc:
57 bb.fatal('%s: %s' % (d.getVar('P'), exc))
58 else:
59 pkg_dic[pkg]["LICENSES"] = re.sub('[|&()*]', ' ', pkg_dic[pkg]["LICENSE"])
60 pkg_dic[pkg]["LICENSES"] = re.sub(' *', ' ', pkg_dic[pkg]["LICENSES"])
61 pkg_dic[pkg]["LICENSES"] = pkg_dic[pkg]["LICENSES"].split()
62
63 if not "IMAGE_MANIFEST" in pkg_dic[pkg]:
64 # Rootfs manifest
65 license_file.write("PACKAGE NAME: %s\n" % pkg)
66 license_file.write("PACKAGE VERSION: %s\n" % pkg_dic[pkg]["PV"])
67 license_file.write("RECIPE NAME: %s\n" % pkg_dic[pkg]["PN"])
68 license_file.write("LICENSE: %s\n\n" % pkg_dic[pkg]["LICENSE"])
69
70 # If the package doesn't contain any file, that is, its size is 0, the license
71 # isn't relevant as far as the final image is concerned. So doing license check
72 # doesn't make much sense, skip it.
73 if pkg_dic[pkg]["PKGSIZE_%s" % pkg] == "0":
74 continue
75 else:
76 # Image manifest
77 license_file.write("RECIPE NAME: %s\n" % pkg_dic[pkg]["PN"])
78 license_file.write("VERSION: %s\n" % pkg_dic[pkg]["PV"])
79 license_file.write("LICENSE: %s\n" % pkg_dic[pkg]["LICENSE"])
80 license_file.write("FILES: %s\n\n" % pkg_dic[pkg]["FILES"])
81
82 for lic in pkg_dic[pkg]["LICENSES"]:
83 lic_file = os.path.join(d.getVar('LICENSE_DIRECTORY'),
84 pkg_dic[pkg]["PN"], "generic_%s" %
85 re.sub('\+', '', lic))
86 # add explicity avoid of CLOSED license because isn't generic
87 if lic == "CLOSED":
88 continue
89
90 if not os.path.exists(lic_file):
91 bb.warn("The license listed %s was not in the "\
92 "licenses collected for recipe %s"
93 % (lic, pkg_dic[pkg]["PN"]))
94
95 # Two options here:
96 # - Just copy the manifest
97 # - Copy the manifest and the license directories
98 # With both options set we see a .5 M increase in core-image-minimal
99 copy_lic_manifest = d.getVar('COPY_LIC_MANIFEST')
100 copy_lic_dirs = d.getVar('COPY_LIC_DIRS')
101 if copy_lic_manifest == "1":
102 rootfs_license_dir = os.path.join(d.getVar('IMAGE_ROOTFS'),
103 'usr', 'share', 'common-licenses')
104 bb.utils.mkdirhier(rootfs_license_dir)
105 rootfs_license_manifest = os.path.join(rootfs_license_dir,
106 os.path.split(license_manifest)[1])
107 if not os.path.exists(rootfs_license_manifest):
108 os.link(license_manifest, rootfs_license_manifest)
109
110 if copy_lic_dirs == "1":
111 for pkg in sorted(pkg_dic):
112 pkg_rootfs_license_dir = os.path.join(rootfs_license_dir, pkg)
113 bb.utils.mkdirhier(pkg_rootfs_license_dir)
114 pkg_license_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'),
115 pkg_dic[pkg]["PN"])
116
117 pkg_manifest_licenses = [canonical_license(d, lic) \
118 for lic in pkg_dic[pkg]["LICENSES"]]
119
120 licenses = os.listdir(pkg_license_dir)
121 for lic in licenses:
122 rootfs_license = os.path.join(rootfs_license_dir, lic)
123 pkg_license = os.path.join(pkg_license_dir, lic)
124 pkg_rootfs_license = os.path.join(pkg_rootfs_license_dir, lic)
125
126 if re.match("^generic_.*$", lic):
127 generic_lic = canonical_license(d,
128 re.search("^generic_(.*)$", lic).group(1))
129
130 # Do not copy generic license into package if isn't
131 # declared into LICENSES of the package.
132 if not re.sub('\+$', '', generic_lic) in \
133 [re.sub('\+', '', lic) for lic in \
134 pkg_manifest_licenses]:
135 continue
136
137 if oe.license.license_ok(generic_lic,
138 bad_licenses) == False:
139 continue
140
141 if not os.path.exists(rootfs_license):
142 os.link(pkg_license, rootfs_license)
143
144 if not os.path.exists(pkg_rootfs_license):
145 os.symlink(os.path.join('..', lic), pkg_rootfs_license)
146 else:
147 if (oe.license.license_ok(canonical_license(d,
148 lic), bad_licenses) == False or
149 os.path.exists(pkg_rootfs_license)):
150 continue
151
152 os.link(pkg_license, pkg_rootfs_license)
153
154
155def license_deployed_manifest(d):
156 """
157 Write the license manifest for the deployed recipes.
158 The deployed recipes usually includes the bootloader
159 and extra files to boot the target.
160 """
161
162 dep_dic = {}
163 man_dic = {}
164 lic_dir = d.getVar("LICENSE_DIRECTORY")
165
166 dep_dic = get_deployed_dependencies(d)
167 for dep in dep_dic.keys():
168 man_dic[dep] = {}
169 # It is necessary to mark this will be used for image manifest
170 man_dic[dep]["IMAGE_MANIFEST"] = True
171 man_dic[dep]["PN"] = dep
172 man_dic[dep]["FILES"] = \
173 " ".join(get_deployed_files(dep_dic[dep]))
174 with open(os.path.join(lic_dir, dep, "recipeinfo"), "r") as f:
175 for line in f.readlines():
176 key,val = line.split(": ", 1)
177 man_dic[dep][key] = val[:-1]
178
179 lic_manifest_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'),
180 d.getVar('IMAGE_NAME'))
181 bb.utils.mkdirhier(lic_manifest_dir)
182 image_license_manifest = os.path.join(lic_manifest_dir, 'image_license.manifest')
183 write_license_files(d, image_license_manifest, man_dic)
184
185def get_deployed_dependencies(d):
186 """
187 Get all the deployed dependencies of an image
188 """
189
190 deploy = {}
191 # Get all the dependencies for the current task (rootfs).
192 # Also get EXTRA_IMAGEDEPENDS because the bootloader is
193 # usually in this var and not listed in rootfs.
194 # At last, get the dependencies from boot classes because
195 # it might contain the bootloader.
196 taskdata = d.getVar("BB_TASKDEPDATA", False)
197 depends = list(set([dep[0] for dep
198 in list(taskdata.values())
199 if not dep[0].endswith("-native")]))
200 extra_depends = d.getVar("EXTRA_IMAGEDEPENDS")
201 boot_depends = get_boot_dependencies(d)
202 depends.extend(extra_depends.split())
203 depends.extend(boot_depends)
204 depends = list(set(depends))
205
206 # To verify what was deployed it checks the rootfs dependencies against
207 # the SSTATE_MANIFESTS for "deploy" task.
208 # The manifest file name contains the arch. Because we are not running
209 # in the recipe context it is necessary to check every arch used.
210 sstate_manifest_dir = d.getVar("SSTATE_MANIFESTS")
211 archs = list(set(d.getVar("SSTATE_ARCHS").split()))
212 for dep in depends:
213 # Some recipes have an arch on their own, so we try that first.
214 special_arch = d.getVar("PACKAGE_ARCH_pn-%s" % dep)
215 if special_arch:
216 sstate_manifest_file = os.path.join(sstate_manifest_dir,
217 "manifest-%s-%s.deploy" % (special_arch, dep))
218 if os.path.exists(sstate_manifest_file):
219 deploy[dep] = sstate_manifest_file
220 continue
221
222 for arch in archs:
223 sstate_manifest_file = os.path.join(sstate_manifest_dir,
224 "manifest-%s-%s.deploy" % (arch, dep))
225 if os.path.exists(sstate_manifest_file):
226 deploy[dep] = sstate_manifest_file
227 break
228
229 return deploy
230get_deployed_dependencies[vardepsexclude] = "BB_TASKDEPDATA"
231
232def get_boot_dependencies(d):
233 """
234 Return the dependencies from boot tasks
235 """
236
237 depends = []
238 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
239 # Only bootimg includes the depends flag
240 boot_depends_string = d.getVarFlag("do_bootimg", "depends") or ""
241 boot_depends = [dep.split(":")[0] for dep
242 in boot_depends_string.split()
243 if not dep.split(":")[0].endswith("-native")]
244 for dep in boot_depends:
245 info_file = os.path.join(d.getVar("LICENSE_DIRECTORY"),
246 dep, "recipeinfo")
247 # If the recipe and dependency name is the same
248 if os.path.exists(info_file):
249 depends.append(dep)
250 # We need to search for the provider of the dependency
251 else:
252 for taskdep in taskdepdata.values():
253 # The fifth field contains what the task provides
254 if dep in taskdep[4]:
255 info_file = os.path.join(
256 d.getVar("LICENSE_DIRECTORY"),
257 taskdep[0], "recipeinfo")
258 if os.path.exists(info_file):
259 depends.append(taskdep[0])
260 break
261 return depends
262get_boot_dependencies[vardepsexclude] = "BB_TASKDEPDATA"
263
264def get_deployed_files(man_file):
265 """
266 Get the files deployed from the sstate manifest
267 """
268
269 dep_files = []
270 excluded_files = []
271 with open(man_file, "r") as manifest:
272 all_files = manifest.read()
273 for f in all_files.splitlines():
274 if ((not (os.path.islink(f) or os.path.isdir(f))) and
275 not os.path.basename(f) in excluded_files):
276 dep_files.append(os.path.basename(f))
277 return dep_files
278
279ROOTFS_POSTPROCESS_COMMAND_prepend = "write_package_manifest; license_create_manifest; "
280do_rootfs[recrdeptask] += "do_populate_lic"
281
282IMAGE_POSTPROCESS_COMMAND_prepend = "write_deploy_manifest; "
283do_image[recrdeptask] += "do_populate_lic"
284