blob: 73cebb4d55e4f27e603b023da906733a8673ef76 [file] [log] [blame]
Andrew Geisslerc926e172021-05-07 16:11:35 -05001ROOTFS_LICENSE_DIR = "${IMAGE_ROOTFS}/usr/share/common-licenses"
2
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08003python write_package_manifest() {
4 # Get list of installed packages
5 license_image_dir = d.expand('${LICENSE_DIRECTORY}/${IMAGE_NAME}')
6 bb.utils.mkdirhier(license_image_dir)
7 from oe.rootfs import image_list_installed_packages
8 from oe.utils import format_pkg_list
9
10 pkgs = image_list_installed_packages(d)
11 output = format_pkg_list(pkgs)
12 open(os.path.join(license_image_dir, 'package.manifest'),
13 'w+').write(output)
14}
15
16python license_create_manifest() {
17 import oe.packagedata
18 from oe.rootfs import image_list_installed_packages
19
20 build_images_from_feeds = d.getVar('BUILD_IMAGES_FROM_FEEDS')
21 if build_images_from_feeds == "1":
22 return 0
23
24 pkg_dic = {}
25 for pkg in sorted(image_list_installed_packages(d)):
26 pkg_info = os.path.join(d.getVar('PKGDATA_DIR'),
27 'runtime-reverse', pkg)
28 pkg_name = os.path.basename(os.readlink(pkg_info))
29
30 pkg_dic[pkg_name] = oe.packagedata.read_pkgdatafile(pkg_info)
31 if not "LICENSE" in pkg_dic[pkg_name].keys():
32 pkg_lic_name = "LICENSE_" + pkg_name
33 pkg_dic[pkg_name]["LICENSE"] = pkg_dic[pkg_name][pkg_lic_name]
34
35 rootfs_license_manifest = os.path.join(d.getVar('LICENSE_DIRECTORY'),
36 d.getVar('IMAGE_NAME'), 'license.manifest')
Brad Bishop19323692019-04-05 15:28:33 -040037 write_license_files(d, rootfs_license_manifest, pkg_dic, rootfs=True)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080038}
39
Brad Bishop19323692019-04-05 15:28:33 -040040def write_license_files(d, license_manifest, pkg_dic, rootfs=True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080041 import re
Brad Bishop19323692019-04-05 15:28:33 -040042 import stat
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080043
44 bad_licenses = (d.getVar("INCOMPATIBLE_LICENSE") or "").split()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080045 bad_licenses = expand_wildcard_licenses(d, bad_licenses)
46
Brad Bishopf3f93bb2019-10-16 14:33:32 -040047 whitelist = []
48 for lic in bad_licenses:
49 whitelist.extend((d.getVar("WHITELIST_" + lic) or "").split())
50
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080051 with open(license_manifest, "w") as license_file:
52 for pkg in sorted(pkg_dic):
Brad Bishopf3f93bb2019-10-16 14:33:32 -040053 if bad_licenses and pkg not in whitelist:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080054 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -050055 licenses = incompatible_pkg_license(d, bad_licenses, pkg_dic[pkg]["LICENSE"])
56 if licenses:
57 bb.fatal("Package %s cannot be installed into the image because it has incompatible license(s): %s" %(pkg, ' '.join(licenses)))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080058 (pkg_dic[pkg]["LICENSE"], pkg_dic[pkg]["LICENSES"]) = \
59 oe.license.manifest_licenses(pkg_dic[pkg]["LICENSE"],
60 bad_licenses, canonical_license, d)
61 except oe.license.LicenseError as exc:
62 bb.fatal('%s: %s' % (d.getVar('P'), exc))
63 else:
Brad Bishop977dc1a2019-02-06 16:01:43 -050064 pkg_dic[pkg]["LICENSES"] = re.sub(r'[|&()*]', ' ', pkg_dic[pkg]["LICENSE"])
65 pkg_dic[pkg]["LICENSES"] = re.sub(r' *', ' ', pkg_dic[pkg]["LICENSES"])
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080066 pkg_dic[pkg]["LICENSES"] = pkg_dic[pkg]["LICENSES"].split()
Brad Bishopf3f93bb2019-10-16 14:33:32 -040067 if pkg in whitelist:
68 bb.warn("Including %s with an incompatible license %s into the image, because it has been whitelisted." %(pkg, pkg_dic[pkg]["LICENSE"]))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080069
70 if not "IMAGE_MANIFEST" in pkg_dic[pkg]:
71 # Rootfs manifest
72 license_file.write("PACKAGE NAME: %s\n" % pkg)
73 license_file.write("PACKAGE VERSION: %s\n" % pkg_dic[pkg]["PV"])
74 license_file.write("RECIPE NAME: %s\n" % pkg_dic[pkg]["PN"])
75 license_file.write("LICENSE: %s\n\n" % pkg_dic[pkg]["LICENSE"])
76
77 # If the package doesn't contain any file, that is, its size is 0, the license
78 # isn't relevant as far as the final image is concerned. So doing license check
79 # doesn't make much sense, skip it.
80 if pkg_dic[pkg]["PKGSIZE_%s" % pkg] == "0":
81 continue
82 else:
83 # Image manifest
84 license_file.write("RECIPE NAME: %s\n" % pkg_dic[pkg]["PN"])
85 license_file.write("VERSION: %s\n" % pkg_dic[pkg]["PV"])
86 license_file.write("LICENSE: %s\n" % pkg_dic[pkg]["LICENSE"])
87 license_file.write("FILES: %s\n\n" % pkg_dic[pkg]["FILES"])
88
89 for lic in pkg_dic[pkg]["LICENSES"]:
90 lic_file = os.path.join(d.getVar('LICENSE_DIRECTORY'),
91 pkg_dic[pkg]["PN"], "generic_%s" %
Brad Bishop977dc1a2019-02-06 16:01:43 -050092 re.sub(r'\+', '', lic))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080093 # add explicity avoid of CLOSED license because isn't generic
94 if lic == "CLOSED":
95 continue
96
97 if not os.path.exists(lic_file):
98 bb.warn("The license listed %s was not in the "\
99 "licenses collected for recipe %s"
100 % (lic, pkg_dic[pkg]["PN"]))
101
102 # Two options here:
103 # - Just copy the manifest
104 # - Copy the manifest and the license directories
105 # With both options set we see a .5 M increase in core-image-minimal
106 copy_lic_manifest = d.getVar('COPY_LIC_MANIFEST')
107 copy_lic_dirs = d.getVar('COPY_LIC_DIRS')
Brad Bishop19323692019-04-05 15:28:33 -0400108 if rootfs and copy_lic_manifest == "1":
Andrew Geisslerc926e172021-05-07 16:11:35 -0500109 rootfs_license_dir = d.getVar('ROOTFS_LICENSE_DIR')
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800110 bb.utils.mkdirhier(rootfs_license_dir)
111 rootfs_license_manifest = os.path.join(rootfs_license_dir,
112 os.path.split(license_manifest)[1])
113 if not os.path.exists(rootfs_license_manifest):
Brad Bishopc342db32019-05-15 21:57:59 -0400114 oe.path.copyhardlink(license_manifest, rootfs_license_manifest)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800115
116 if copy_lic_dirs == "1":
117 for pkg in sorted(pkg_dic):
118 pkg_rootfs_license_dir = os.path.join(rootfs_license_dir, pkg)
119 bb.utils.mkdirhier(pkg_rootfs_license_dir)
120 pkg_license_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'),
121 pkg_dic[pkg]["PN"])
122
123 pkg_manifest_licenses = [canonical_license(d, lic) \
124 for lic in pkg_dic[pkg]["LICENSES"]]
125
126 licenses = os.listdir(pkg_license_dir)
127 for lic in licenses:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800128 pkg_license = os.path.join(pkg_license_dir, lic)
129 pkg_rootfs_license = os.path.join(pkg_rootfs_license_dir, lic)
130
Brad Bishop977dc1a2019-02-06 16:01:43 -0500131 if re.match(r"^generic_.*$", lic):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800132 generic_lic = canonical_license(d,
Brad Bishop977dc1a2019-02-06 16:01:43 -0500133 re.search(r"^generic_(.*)$", lic).group(1))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800134
135 # Do not copy generic license into package if isn't
136 # declared into LICENSES of the package.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500137 if not re.sub(r'\+$', '', generic_lic) in \
138 [re.sub(r'\+', '', lic) for lic in \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800139 pkg_manifest_licenses]:
140 continue
141
142 if oe.license.license_ok(generic_lic,
143 bad_licenses) == False:
144 continue
145
Andrew Geissler6ce62a22020-11-30 19:58:47 -0600146 # Make sure we use only canonical name for the license file
Andrew Geisslerc926e172021-05-07 16:11:35 -0500147 generic_lic_file = "generic_%s" % generic_lic
148 rootfs_license = os.path.join(rootfs_license_dir, generic_lic_file)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800149 if not os.path.exists(rootfs_license):
Brad Bishopc342db32019-05-15 21:57:59 -0400150 oe.path.copyhardlink(pkg_license, rootfs_license)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800151
152 if not os.path.exists(pkg_rootfs_license):
Andrew Geisslerc926e172021-05-07 16:11:35 -0500153 os.symlink(os.path.join('..', generic_lic_file), pkg_rootfs_license)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800154 else:
155 if (oe.license.license_ok(canonical_license(d,
156 lic), bad_licenses) == False or
157 os.path.exists(pkg_rootfs_license)):
158 continue
159
Brad Bishopc342db32019-05-15 21:57:59 -0400160 oe.path.copyhardlink(pkg_license, pkg_rootfs_license)
Brad Bishop19323692019-04-05 15:28:33 -0400161 # Fixup file ownership and permissions
162 for walkroot, dirs, files in os.walk(rootfs_license_dir):
163 for f in files:
164 p = os.path.join(walkroot, f)
165 os.lchown(p, 0, 0)
166 if not os.path.islink(p):
167 os.chmod(p, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
168 for dir in dirs:
169 p = os.path.join(walkroot, dir)
170 os.lchown(p, 0, 0)
171 os.chmod(p, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
172
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800173
174
175def license_deployed_manifest(d):
176 """
177 Write the license manifest for the deployed recipes.
178 The deployed recipes usually includes the bootloader
179 and extra files to boot the target.
180 """
181
182 dep_dic = {}
183 man_dic = {}
184 lic_dir = d.getVar("LICENSE_DIRECTORY")
185
186 dep_dic = get_deployed_dependencies(d)
187 for dep in dep_dic.keys():
188 man_dic[dep] = {}
189 # It is necessary to mark this will be used for image manifest
190 man_dic[dep]["IMAGE_MANIFEST"] = True
191 man_dic[dep]["PN"] = dep
192 man_dic[dep]["FILES"] = \
193 " ".join(get_deployed_files(dep_dic[dep]))
194 with open(os.path.join(lic_dir, dep, "recipeinfo"), "r") as f:
195 for line in f.readlines():
196 key,val = line.split(": ", 1)
197 man_dic[dep][key] = val[:-1]
198
199 lic_manifest_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'),
200 d.getVar('IMAGE_NAME'))
201 bb.utils.mkdirhier(lic_manifest_dir)
202 image_license_manifest = os.path.join(lic_manifest_dir, 'image_license.manifest')
Brad Bishop19323692019-04-05 15:28:33 -0400203 write_license_files(d, image_license_manifest, man_dic, rootfs=False)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800204
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500205 link_name = d.getVar('IMAGE_LINK_NAME')
206 if link_name:
207 lic_manifest_symlink_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'),
208 link_name)
209 # remove old symlink
210 if os.path.islink(lic_manifest_symlink_dir):
211 os.unlink(lic_manifest_symlink_dir)
212
213 # create the image dir symlink
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600214 if lic_manifest_dir != lic_manifest_symlink_dir:
215 os.symlink(lic_manifest_dir, lic_manifest_symlink_dir)
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500216
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800217def get_deployed_dependencies(d):
218 """
219 Get all the deployed dependencies of an image
220 """
221
222 deploy = {}
223 # Get all the dependencies for the current task (rootfs).
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800224 taskdata = d.getVar("BB_TASKDEPDATA", False)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600225 pn = d.getVar("PN", True)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800226 depends = list(set([dep[0] for dep
227 in list(taskdata.values())
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600228 if not dep[0].endswith("-native") and not dep[0] == pn]))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800229
230 # To verify what was deployed it checks the rootfs dependencies against
231 # the SSTATE_MANIFESTS for "deploy" task.
232 # The manifest file name contains the arch. Because we are not running
233 # in the recipe context it is necessary to check every arch used.
234 sstate_manifest_dir = d.getVar("SSTATE_MANIFESTS")
235 archs = list(set(d.getVar("SSTATE_ARCHS").split()))
236 for dep in depends:
237 for arch in archs:
238 sstate_manifest_file = os.path.join(sstate_manifest_dir,
239 "manifest-%s-%s.deploy" % (arch, dep))
240 if os.path.exists(sstate_manifest_file):
241 deploy[dep] = sstate_manifest_file
242 break
243
244 return deploy
245get_deployed_dependencies[vardepsexclude] = "BB_TASKDEPDATA"
246
247def get_deployed_files(man_file):
248 """
249 Get the files deployed from the sstate manifest
250 """
251
252 dep_files = []
253 excluded_files = []
254 with open(man_file, "r") as manifest:
255 all_files = manifest.read()
256 for f in all_files.splitlines():
257 if ((not (os.path.islink(f) or os.path.isdir(f))) and
258 not os.path.basename(f) in excluded_files):
259 dep_files.append(os.path.basename(f))
260 return dep_files
261
262ROOTFS_POSTPROCESS_COMMAND_prepend = "write_package_manifest; license_create_manifest; "
263do_rootfs[recrdeptask] += "do_populate_lic"
264
265python do_populate_lic_deploy() {
266 license_deployed_manifest(d)
267}
268
269addtask populate_lic_deploy before do_build after do_image_complete
270do_populate_lic_deploy[recrdeptask] += "do_populate_lic do_deploy"
271
Andrew Geisslerc926e172021-05-07 16:11:35 -0500272python license_qa_dead_symlink() {
273 import os
274
275 for root, dirs, files in os.walk(d.getVar('ROOTFS_LICENSE_DIR')):
276 for file in files:
277 full_path = root + "/" + file
278 if os.path.islink(full_path) and not os.path.exists(full_path):
279 bb.error("broken symlink: " + full_path)
280}
281IMAGE_QA_COMMANDS += "license_qa_dead_symlink"