blob: 0a5ea0a2fbc745ac3c3e8baa3d5bbb2b7f23cb6b [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)
22 open(os.path.join(license_image_dir, 'package.manifest'),
23 'w+').write(output)
24}
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)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800107 # Two options here:
108 # - Just copy the manifest
109 # - Copy the manifest and the license directories
110 # With both options set we see a .5 M increase in core-image-minimal
111 copy_lic_manifest = d.getVar('COPY_LIC_MANIFEST')
112 copy_lic_dirs = d.getVar('COPY_LIC_DIRS')
Brad Bishop19323692019-04-05 15:28:33 -0400113 if rootfs and copy_lic_manifest == "1":
Andrew Geisslerc926e172021-05-07 16:11:35 -0500114 rootfs_license_dir = d.getVar('ROOTFS_LICENSE_DIR')
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800115 bb.utils.mkdirhier(rootfs_license_dir)
116 rootfs_license_manifest = os.path.join(rootfs_license_dir,
117 os.path.split(license_manifest)[1])
118 if not os.path.exists(rootfs_license_manifest):
Brad Bishopc342db32019-05-15 21:57:59 -0400119 oe.path.copyhardlink(license_manifest, rootfs_license_manifest)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800120
121 if copy_lic_dirs == "1":
122 for pkg in sorted(pkg_dic):
123 pkg_rootfs_license_dir = os.path.join(rootfs_license_dir, pkg)
124 bb.utils.mkdirhier(pkg_rootfs_license_dir)
125 pkg_license_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'),
126 pkg_dic[pkg]["PN"])
127
128 pkg_manifest_licenses = [canonical_license(d, lic) \
129 for lic in pkg_dic[pkg]["LICENSES"]]
130
131 licenses = os.listdir(pkg_license_dir)
132 for lic in licenses:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800133 pkg_license = os.path.join(pkg_license_dir, lic)
134 pkg_rootfs_license = os.path.join(pkg_rootfs_license_dir, lic)
135
Brad Bishop977dc1a2019-02-06 16:01:43 -0500136 if re.match(r"^generic_.*$", lic):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800137 generic_lic = canonical_license(d,
Brad Bishop977dc1a2019-02-06 16:01:43 -0500138 re.search(r"^generic_(.*)$", lic).group(1))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800139
140 # Do not copy generic license into package if isn't
141 # declared into LICENSES of the package.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500142 if not re.sub(r'\+$', '', generic_lic) in \
143 [re.sub(r'\+', '', lic) for lic in \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800144 pkg_manifest_licenses]:
145 continue
146
147 if oe.license.license_ok(generic_lic,
148 bad_licenses) == False:
149 continue
150
Andrew Geissler6ce62a22020-11-30 19:58:47 -0600151 # Make sure we use only canonical name for the license file
Andrew Geisslerc926e172021-05-07 16:11:35 -0500152 generic_lic_file = "generic_%s" % generic_lic
153 rootfs_license = os.path.join(rootfs_license_dir, generic_lic_file)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800154 if not os.path.exists(rootfs_license):
Brad Bishopc342db32019-05-15 21:57:59 -0400155 oe.path.copyhardlink(pkg_license, rootfs_license)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800156
157 if not os.path.exists(pkg_rootfs_license):
Andrew Geisslerc926e172021-05-07 16:11:35 -0500158 os.symlink(os.path.join('..', generic_lic_file), pkg_rootfs_license)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800159 else:
160 if (oe.license.license_ok(canonical_license(d,
161 lic), bad_licenses) == False or
162 os.path.exists(pkg_rootfs_license)):
163 continue
164
Brad Bishopc342db32019-05-15 21:57:59 -0400165 oe.path.copyhardlink(pkg_license, pkg_rootfs_license)
Brad Bishop19323692019-04-05 15:28:33 -0400166 # Fixup file ownership and permissions
167 for walkroot, dirs, files in os.walk(rootfs_license_dir):
168 for f in files:
169 p = os.path.join(walkroot, f)
170 os.lchown(p, 0, 0)
171 if not os.path.islink(p):
172 os.chmod(p, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
173 for dir in dirs:
174 p = os.path.join(walkroot, dir)
175 os.lchown(p, 0, 0)
176 os.chmod(p, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
177
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800178
179
180def license_deployed_manifest(d):
181 """
182 Write the license manifest for the deployed recipes.
183 The deployed recipes usually includes the bootloader
184 and extra files to boot the target.
185 """
186
187 dep_dic = {}
188 man_dic = {}
189 lic_dir = d.getVar("LICENSE_DIRECTORY")
190
191 dep_dic = get_deployed_dependencies(d)
192 for dep in dep_dic.keys():
193 man_dic[dep] = {}
194 # It is necessary to mark this will be used for image manifest
195 man_dic[dep]["IMAGE_MANIFEST"] = True
196 man_dic[dep]["PN"] = dep
197 man_dic[dep]["FILES"] = \
198 " ".join(get_deployed_files(dep_dic[dep]))
199 with open(os.path.join(lic_dir, dep, "recipeinfo"), "r") as f:
200 for line in f.readlines():
201 key,val = line.split(": ", 1)
202 man_dic[dep][key] = val[:-1]
203
204 lic_manifest_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'),
205 d.getVar('IMAGE_NAME'))
206 bb.utils.mkdirhier(lic_manifest_dir)
207 image_license_manifest = os.path.join(lic_manifest_dir, 'image_license.manifest')
Brad Bishop19323692019-04-05 15:28:33 -0400208 write_license_files(d, image_license_manifest, man_dic, rootfs=False)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800209
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500210 link_name = d.getVar('IMAGE_LINK_NAME')
211 if link_name:
212 lic_manifest_symlink_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'),
213 link_name)
214 # remove old symlink
215 if os.path.islink(lic_manifest_symlink_dir):
216 os.unlink(lic_manifest_symlink_dir)
217
218 # create the image dir symlink
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600219 if lic_manifest_dir != lic_manifest_symlink_dir:
220 os.symlink(lic_manifest_dir, lic_manifest_symlink_dir)
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500221
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800222def get_deployed_dependencies(d):
223 """
224 Get all the deployed dependencies of an image
225 """
226
227 deploy = {}
228 # Get all the dependencies for the current task (rootfs).
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800229 taskdata = d.getVar("BB_TASKDEPDATA", False)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600230 pn = d.getVar("PN", True)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800231 depends = list(set([dep[0] for dep
232 in list(taskdata.values())
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600233 if not dep[0].endswith("-native") and not dep[0] == pn]))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800234
235 # To verify what was deployed it checks the rootfs dependencies against
236 # the SSTATE_MANIFESTS for "deploy" task.
237 # The manifest file name contains the arch. Because we are not running
238 # in the recipe context it is necessary to check every arch used.
239 sstate_manifest_dir = d.getVar("SSTATE_MANIFESTS")
240 archs = list(set(d.getVar("SSTATE_ARCHS").split()))
241 for dep in depends:
242 for arch in archs:
243 sstate_manifest_file = os.path.join(sstate_manifest_dir,
244 "manifest-%s-%s.deploy" % (arch, dep))
245 if os.path.exists(sstate_manifest_file):
246 deploy[dep] = sstate_manifest_file
247 break
248
249 return deploy
250get_deployed_dependencies[vardepsexclude] = "BB_TASKDEPDATA"
251
252def get_deployed_files(man_file):
253 """
254 Get the files deployed from the sstate manifest
255 """
256
257 dep_files = []
258 excluded_files = []
259 with open(man_file, "r") as manifest:
260 all_files = manifest.read()
261 for f in all_files.splitlines():
262 if ((not (os.path.islink(f) or os.path.isdir(f))) and
263 not os.path.basename(f) in excluded_files):
264 dep_files.append(os.path.basename(f))
265 return dep_files
266
Patrick Williams213cb262021-08-07 19:21:33 -0500267ROOTFS_POSTPROCESS_COMMAND:prepend = "write_package_manifest; license_create_manifest; "
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800268do_rootfs[recrdeptask] += "do_populate_lic"
269
270python do_populate_lic_deploy() {
271 license_deployed_manifest(d)
Andrew Geisslereff27472021-10-29 15:35:00 -0500272 oe.qa.exit_if_errors(d)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800273}
274
275addtask populate_lic_deploy before do_build after do_image_complete
276do_populate_lic_deploy[recrdeptask] += "do_populate_lic do_deploy"
277
Andrew Geisslerc926e172021-05-07 16:11:35 -0500278python license_qa_dead_symlink() {
279 import os
280
281 for root, dirs, files in os.walk(d.getVar('ROOTFS_LICENSE_DIR')):
282 for file in files:
283 full_path = root + "/" + file
284 if os.path.islink(full_path) and not os.path.exists(full_path):
285 bb.error("broken symlink: " + full_path)
286}
287IMAGE_QA_COMMANDS += "license_qa_dead_symlink"