1ROOTFS_LICENSE_DIR = "${IMAGE_ROOTFS}/usr/share/common-licenses" 2 3# 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 13python 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 with open(os.path.join(license_image_dir, 'package.manifest'), "w+") as package_manifest: 23 package_manifest.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(): 42 pkg_lic_name = "LICENSE:" + pkg_name 43 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') 47 write_license_files(d, rootfs_license_manifest, pkg_dic, rootfs=True) 48} 49 50def write_license_files(d, license_manifest, pkg_dic, rootfs=True): 51 import re 52 import stat 53 54 bad_licenses = (d.getVar("INCOMPATIBLE_LICENSE") or "").split() 55 bad_licenses = expand_wildcard_licenses(d, bad_licenses) 56 57 exceptions = (d.getVar("INCOMPATIBLE_LICENSE_EXCEPTIONS") or "").split() 58 with open(license_manifest, "w") as license_file: 59 for pkg in sorted(pkg_dic): 60 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))) 64 else: 65 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)) 74 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. 85 if pkg_dic[pkg]["PKGSIZE:%s" % pkg] == "0": 86 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" % 97 re.sub(r'\+', '', lic)) 98 # 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): 103 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) 107 oe.qa.exit_if_errors(d) 108 109 # 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') 115 if rootfs and copy_lic_manifest == "1": 116 rootfs_license_dir = d.getVar('ROOTFS_LICENSE_DIR') 117 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): 121 oe.path.copyhardlink(license_manifest, rootfs_license_manifest) 122 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: 135 pkg_license = os.path.join(pkg_license_dir, lic) 136 pkg_rootfs_license = os.path.join(pkg_rootfs_license_dir, lic) 137 138 if re.match(r"^generic_.*$", lic): 139 generic_lic = canonical_license(d, 140 re.search(r"^generic_(.*)$", lic).group(1)) 141 142 # Do not copy generic license into package if isn't 143 # declared into LICENSES of the package. 144 if not re.sub(r'\+$', '', generic_lic) in \ 145 [re.sub(r'\+', '', lic) for lic in \ 146 pkg_manifest_licenses]: 147 continue 148 149 if oe.license.license_ok(generic_lic, 150 bad_licenses) == False: 151 continue 152 153 # Make sure we use only canonical name for the license file 154 generic_lic_file = "generic_%s" % generic_lic 155 rootfs_license = os.path.join(rootfs_license_dir, generic_lic_file) 156 if not os.path.exists(rootfs_license): 157 oe.path.copyhardlink(pkg_license, rootfs_license) 158 159 if not os.path.exists(pkg_rootfs_license): 160 os.symlink(os.path.join('..', generic_lic_file), pkg_rootfs_license) 161 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 167 oe.path.copyhardlink(pkg_license, pkg_rootfs_license) 168 # 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 180 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') 210 write_license_files(d, image_license_manifest, man_dic, rootfs=False) 211 212 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 221 if lic_manifest_dir != lic_manifest_symlink_dir: 222 os.symlink(lic_manifest_dir, lic_manifest_symlink_dir) 223 224def 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). 231 taskdata = d.getVar("BB_TASKDEPDATA", False) 232 pn = d.getVar("PN") 233 depends = list(set([dep[0] for dep 234 in list(taskdata.values()) 235 if not dep[0].endswith("-native") and not dep[0] == pn])) 236 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 269ROOTFS_POSTPROCESS_COMMAND:prepend = "write_package_manifest; license_create_manifest; " 270do_rootfs[recrdeptask] += "do_populate_lic" 271 272python do_populate_lic_deploy() { 273 license_deployed_manifest(d) 274 oe.qa.exit_if_errors(d) 275} 276 277addtask populate_lic_deploy before do_build after do_image_complete 278do_populate_lic_deploy[recrdeptask] += "do_populate_lic do_deploy" 279 280python 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" 290