blob: 3213ea758e0f13410fe9e834e04161dd0c63226a [file] [log] [blame]
Andrew Geisslerc926e172021-05-07 16:11:35 -05001ROOTFS_LICENSE_DIR = "${IMAGE_ROOTFS}/usr/share/common-licenses"
2
Andrew Geissler5f350902021-07-23 13:09:54 -04003# This requires LICENSE_CREATE_PACKAGE=1 to work too
4COMPLEMENTARY_GLOB[lic-pkgs] = "*-lic"
5
6python() {
7 if not oe.data.typed_value('LICENSE_CREATE_PACKAGE', d):
8 features = set(oe.data.typed_value('IMAGE_FEATURES', d))
9 if 'lic-pkgs' in features:
10 bb.error("'lic-pkgs' in IMAGE_FEATURES but LICENSE_CREATE_PACKAGE not enabled to generate -lic packages")
11}
12
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080013python write_package_manifest() {
14 # Get list of installed packages
15 license_image_dir = d.expand('${LICENSE_DIRECTORY}/${IMAGE_NAME}')
16 bb.utils.mkdirhier(license_image_dir)
17 from oe.rootfs import image_list_installed_packages
18 from oe.utils import format_pkg_list
19
20 pkgs = image_list_installed_packages(d)
21 output = format_pkg_list(pkgs)
Patrick Williams58776372022-04-13 09:07:35 -050022 with open(os.path.join(license_image_dir, 'package.manifest'), "w+") as package_manifest:
23 package_manifest.write(output)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080024}
25
26python license_create_manifest() {
27 import oe.packagedata
28 from oe.rootfs import image_list_installed_packages
29
30 build_images_from_feeds = d.getVar('BUILD_IMAGES_FROM_FEEDS')
31 if build_images_from_feeds == "1":
32 return 0
33
34 pkg_dic = {}
35 for pkg in sorted(image_list_installed_packages(d)):
36 pkg_info = os.path.join(d.getVar('PKGDATA_DIR'),
37 'runtime-reverse', pkg)
38 pkg_name = os.path.basename(os.readlink(pkg_info))
39
40 pkg_dic[pkg_name] = oe.packagedata.read_pkgdatafile(pkg_info)
41 if not "LICENSE" in pkg_dic[pkg_name].keys():
Patrick Williams213cb262021-08-07 19:21:33 -050042 pkg_lic_name = "LICENSE:" + pkg_name
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080043 pkg_dic[pkg_name]["LICENSE"] = pkg_dic[pkg_name][pkg_lic_name]
44
45 rootfs_license_manifest = os.path.join(d.getVar('LICENSE_DIRECTORY'),
46 d.getVar('IMAGE_NAME'), 'license.manifest')
Brad Bishop19323692019-04-05 15:28:33 -040047 write_license_files(d, rootfs_license_manifest, pkg_dic, rootfs=True)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080048}
49
Brad Bishop19323692019-04-05 15:28:33 -040050def write_license_files(d, license_manifest, pkg_dic, rootfs=True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080051 import re
Brad Bishop19323692019-04-05 15:28:33 -040052 import stat
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080053
54 bad_licenses = (d.getVar("INCOMPATIBLE_LICENSE") or "").split()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080055 bad_licenses = expand_wildcard_licenses(d, bad_licenses)
56
Andrew Geissler9aee5002022-03-30 16:27:02 +000057 exceptions = (d.getVar("INCOMPATIBLE_LICENSE_EXCEPTIONS") or "").split()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080058 with open(license_manifest, "w") as license_file:
59 for pkg in sorted(pkg_dic):
Andrew Geissler9aee5002022-03-30 16:27:02 +000060 remaining_bad_licenses = oe.license.apply_pkg_license_exception(pkg, bad_licenses, exceptions)
61 incompatible_licenses = incompatible_pkg_license(d, remaining_bad_licenses, pkg_dic[pkg]["LICENSE"])
62 if incompatible_licenses:
63 bb.fatal("Package %s cannot be installed into the image because it has incompatible license(s): %s" %(pkg, ' '.join(incompatible_licenses)))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080064 else:
Andrew Geissler9aee5002022-03-30 16:27:02 +000065 incompatible_licenses = incompatible_pkg_license(d, bad_licenses, pkg_dic[pkg]["LICENSE"])
66 if incompatible_licenses:
67 oe.qa.handle_error('license-incompatible', "Including %s with incompatible license(s) %s into the image, because it has been allowed by exception list." %(pkg, ' '.join(incompatible_licenses)), d)
68 try:
69 (pkg_dic[pkg]["LICENSE"], pkg_dic[pkg]["LICENSES"]) = \
70 oe.license.manifest_licenses(pkg_dic[pkg]["LICENSE"],
71 remaining_bad_licenses, canonical_license, d)
72 except oe.license.LicenseError as exc:
73 bb.fatal('%s: %s' % (d.getVar('P'), exc))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080074
75 if not "IMAGE_MANIFEST" in pkg_dic[pkg]:
76 # Rootfs manifest
77 license_file.write("PACKAGE NAME: %s\n" % pkg)
78 license_file.write("PACKAGE VERSION: %s\n" % pkg_dic[pkg]["PV"])
79 license_file.write("RECIPE NAME: %s\n" % pkg_dic[pkg]["PN"])
80 license_file.write("LICENSE: %s\n\n" % pkg_dic[pkg]["LICENSE"])
81
82 # If the package doesn't contain any file, that is, its size is 0, the license
83 # isn't relevant as far as the final image is concerned. So doing license check
84 # doesn't make much sense, skip it.
Andrew Geisslerd159c7f2021-09-02 21:05:58 -050085 if pkg_dic[pkg]["PKGSIZE:%s" % pkg] == "0":
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080086 continue
87 else:
88 # Image manifest
89 license_file.write("RECIPE NAME: %s\n" % pkg_dic[pkg]["PN"])
90 license_file.write("VERSION: %s\n" % pkg_dic[pkg]["PV"])
91 license_file.write("LICENSE: %s\n" % pkg_dic[pkg]["LICENSE"])
92 license_file.write("FILES: %s\n\n" % pkg_dic[pkg]["FILES"])
93
94 for lic in pkg_dic[pkg]["LICENSES"]:
95 lic_file = os.path.join(d.getVar('LICENSE_DIRECTORY'),
96 pkg_dic[pkg]["PN"], "generic_%s" %
Brad Bishop977dc1a2019-02-06 16:01:43 -050097 re.sub(r'\+', '', lic))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080098 # add explicity avoid of CLOSED license because isn't generic
99 if lic == "CLOSED":
100 continue
101
102 if not os.path.exists(lic_file):
Andrew Geisslereff27472021-10-29 15:35:00 -0500103 oe.qa.handle_error('license-file-missing',
104 "The license listed %s was not in the "\
105 "licenses collected for recipe %s"
106 % (lic, pkg_dic[pkg]["PN"]), d)
Patrick Williams03907ee2022-05-01 06:28:52 -0500107 oe.qa.exit_if_errors(d)
108
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800109 # Two options here:
110 # - Just copy the manifest
111 # - Copy the manifest and the license directories
112 # With both options set we see a .5 M increase in core-image-minimal
113 copy_lic_manifest = d.getVar('COPY_LIC_MANIFEST')
114 copy_lic_dirs = d.getVar('COPY_LIC_DIRS')
Brad Bishop19323692019-04-05 15:28:33 -0400115 if rootfs and copy_lic_manifest == "1":
Andrew Geisslerc926e172021-05-07 16:11:35 -0500116 rootfs_license_dir = d.getVar('ROOTFS_LICENSE_DIR')
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800117 bb.utils.mkdirhier(rootfs_license_dir)
118 rootfs_license_manifest = os.path.join(rootfs_license_dir,
119 os.path.split(license_manifest)[1])
120 if not os.path.exists(rootfs_license_manifest):
Brad Bishopc342db32019-05-15 21:57:59 -0400121 oe.path.copyhardlink(license_manifest, rootfs_license_manifest)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800122
123 if copy_lic_dirs == "1":
124 for pkg in sorted(pkg_dic):
125 pkg_rootfs_license_dir = os.path.join(rootfs_license_dir, pkg)
126 bb.utils.mkdirhier(pkg_rootfs_license_dir)
127 pkg_license_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'),
128 pkg_dic[pkg]["PN"])
129
130 pkg_manifest_licenses = [canonical_license(d, lic) \
131 for lic in pkg_dic[pkg]["LICENSES"]]
132
133 licenses = os.listdir(pkg_license_dir)
134 for lic in licenses:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800135 pkg_license = os.path.join(pkg_license_dir, lic)
136 pkg_rootfs_license = os.path.join(pkg_rootfs_license_dir, lic)
137
Brad Bishop977dc1a2019-02-06 16:01:43 -0500138 if re.match(r"^generic_.*$", lic):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800139 generic_lic = canonical_license(d,
Brad Bishop977dc1a2019-02-06 16:01:43 -0500140 re.search(r"^generic_(.*)$", lic).group(1))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800141
142 # Do not copy generic license into package if isn't
143 # declared into LICENSES of the package.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500144 if not re.sub(r'\+$', '', generic_lic) in \
145 [re.sub(r'\+', '', lic) for lic in \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800146 pkg_manifest_licenses]:
147 continue
148
149 if oe.license.license_ok(generic_lic,
150 bad_licenses) == False:
151 continue
152
Andrew Geissler6ce62a22020-11-30 19:58:47 -0600153 # Make sure we use only canonical name for the license file
Andrew Geisslerc926e172021-05-07 16:11:35 -0500154 generic_lic_file = "generic_%s" % generic_lic
155 rootfs_license = os.path.join(rootfs_license_dir, generic_lic_file)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800156 if not os.path.exists(rootfs_license):
Brad Bishopc342db32019-05-15 21:57:59 -0400157 oe.path.copyhardlink(pkg_license, rootfs_license)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800158
159 if not os.path.exists(pkg_rootfs_license):
Andrew Geisslerc926e172021-05-07 16:11:35 -0500160 os.symlink(os.path.join('..', generic_lic_file), pkg_rootfs_license)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800161 else:
162 if (oe.license.license_ok(canonical_license(d,
163 lic), bad_licenses) == False or
164 os.path.exists(pkg_rootfs_license)):
165 continue
166
Brad Bishopc342db32019-05-15 21:57:59 -0400167 oe.path.copyhardlink(pkg_license, pkg_rootfs_license)
Brad Bishop19323692019-04-05 15:28:33 -0400168 # Fixup file ownership and permissions
169 for walkroot, dirs, files in os.walk(rootfs_license_dir):
170 for f in files:
171 p = os.path.join(walkroot, f)
172 os.lchown(p, 0, 0)
173 if not os.path.islink(p):
174 os.chmod(p, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
175 for dir in dirs:
176 p = os.path.join(walkroot, dir)
177 os.lchown(p, 0, 0)
178 os.chmod(p, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
179
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800180
181
182def license_deployed_manifest(d):
183 """
184 Write the license manifest for the deployed recipes.
185 The deployed recipes usually includes the bootloader
186 and extra files to boot the target.
187 """
188
189 dep_dic = {}
190 man_dic = {}
191 lic_dir = d.getVar("LICENSE_DIRECTORY")
192
193 dep_dic = get_deployed_dependencies(d)
194 for dep in dep_dic.keys():
195 man_dic[dep] = {}
196 # It is necessary to mark this will be used for image manifest
197 man_dic[dep]["IMAGE_MANIFEST"] = True
198 man_dic[dep]["PN"] = dep
199 man_dic[dep]["FILES"] = \
200 " ".join(get_deployed_files(dep_dic[dep]))
201 with open(os.path.join(lic_dir, dep, "recipeinfo"), "r") as f:
202 for line in f.readlines():
203 key,val = line.split(": ", 1)
204 man_dic[dep][key] = val[:-1]
205
206 lic_manifest_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'),
207 d.getVar('IMAGE_NAME'))
208 bb.utils.mkdirhier(lic_manifest_dir)
209 image_license_manifest = os.path.join(lic_manifest_dir, 'image_license.manifest')
Brad Bishop19323692019-04-05 15:28:33 -0400210 write_license_files(d, image_license_manifest, man_dic, rootfs=False)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800211
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500212 link_name = d.getVar('IMAGE_LINK_NAME')
213 if link_name:
214 lic_manifest_symlink_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'),
215 link_name)
216 # remove old symlink
217 if os.path.islink(lic_manifest_symlink_dir):
218 os.unlink(lic_manifest_symlink_dir)
219
220 # create the image dir symlink
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600221 if lic_manifest_dir != lic_manifest_symlink_dir:
222 os.symlink(lic_manifest_dir, lic_manifest_symlink_dir)
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500223
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800224def get_deployed_dependencies(d):
225 """
226 Get all the deployed dependencies of an image
227 """
228
229 deploy = {}
230 # Get all the dependencies for the current task (rootfs).
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800231 taskdata = d.getVar("BB_TASKDEPDATA", False)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600232 pn = d.getVar("PN", True)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800233 depends = list(set([dep[0] for dep
234 in list(taskdata.values())
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600235 if not dep[0].endswith("-native") and not dep[0] == pn]))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800236
237 # To verify what was deployed it checks the rootfs dependencies against
238 # the SSTATE_MANIFESTS for "deploy" task.
239 # The manifest file name contains the arch. Because we are not running
240 # in the recipe context it is necessary to check every arch used.
241 sstate_manifest_dir = d.getVar("SSTATE_MANIFESTS")
242 archs = list(set(d.getVar("SSTATE_ARCHS").split()))
243 for dep in depends:
244 for arch in archs:
245 sstate_manifest_file = os.path.join(sstate_manifest_dir,
246 "manifest-%s-%s.deploy" % (arch, dep))
247 if os.path.exists(sstate_manifest_file):
248 deploy[dep] = sstate_manifest_file
249 break
250
251 return deploy
252get_deployed_dependencies[vardepsexclude] = "BB_TASKDEPDATA"
253
254def get_deployed_files(man_file):
255 """
256 Get the files deployed from the sstate manifest
257 """
258
259 dep_files = []
260 excluded_files = []
261 with open(man_file, "r") as manifest:
262 all_files = manifest.read()
263 for f in all_files.splitlines():
264 if ((not (os.path.islink(f) or os.path.isdir(f))) and
265 not os.path.basename(f) in excluded_files):
266 dep_files.append(os.path.basename(f))
267 return dep_files
268
Patrick Williams213cb262021-08-07 19:21:33 -0500269ROOTFS_POSTPROCESS_COMMAND:prepend = "write_package_manifest; license_create_manifest; "
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800270do_rootfs[recrdeptask] += "do_populate_lic"
271
272python do_populate_lic_deploy() {
273 license_deployed_manifest(d)
Andrew Geisslereff27472021-10-29 15:35:00 -0500274 oe.qa.exit_if_errors(d)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800275}
276
277addtask populate_lic_deploy before do_build after do_image_complete
278do_populate_lic_deploy[recrdeptask] += "do_populate_lic do_deploy"
279
Andrew Geisslerc926e172021-05-07 16:11:35 -0500280python license_qa_dead_symlink() {
281 import os
282
283 for root, dirs, files in os.walk(d.getVar('ROOTFS_LICENSE_DIR')):
284 for file in files:
285 full_path = root + "/" + file
286 if os.path.islink(full_path) and not os.path.exists(full_path):
287 bb.error("broken symlink: " + full_path)
288}
289IMAGE_QA_COMMANDS += "license_qa_dead_symlink"