1*4882a593SmuzhiyunROOTFS_LICENSE_DIR = "${IMAGE_ROOTFS}/usr/share/common-licenses" 2*4882a593Smuzhiyun 3*4882a593Smuzhiyun# This requires LICENSE_CREATE_PACKAGE=1 to work too 4*4882a593SmuzhiyunCOMPLEMENTARY_GLOB[lic-pkgs] = "*-lic" 5*4882a593Smuzhiyun 6*4882a593Smuzhiyunpython() { 7*4882a593Smuzhiyun if not oe.data.typed_value('LICENSE_CREATE_PACKAGE', d): 8*4882a593Smuzhiyun features = set(oe.data.typed_value('IMAGE_FEATURES', d)) 9*4882a593Smuzhiyun if 'lic-pkgs' in features: 10*4882a593Smuzhiyun bb.error("'lic-pkgs' in IMAGE_FEATURES but LICENSE_CREATE_PACKAGE not enabled to generate -lic packages") 11*4882a593Smuzhiyun} 12*4882a593Smuzhiyun 13*4882a593Smuzhiyunpython write_package_manifest() { 14*4882a593Smuzhiyun # Get list of installed packages 15*4882a593Smuzhiyun license_image_dir = d.expand('${LICENSE_DIRECTORY}/${IMAGE_NAME}') 16*4882a593Smuzhiyun bb.utils.mkdirhier(license_image_dir) 17*4882a593Smuzhiyun from oe.rootfs import image_list_installed_packages 18*4882a593Smuzhiyun from oe.utils import format_pkg_list 19*4882a593Smuzhiyun 20*4882a593Smuzhiyun pkgs = image_list_installed_packages(d) 21*4882a593Smuzhiyun output = format_pkg_list(pkgs) 22*4882a593Smuzhiyun with open(os.path.join(license_image_dir, 'package.manifest'), "w+") as package_manifest: 23*4882a593Smuzhiyun package_manifest.write(output) 24*4882a593Smuzhiyun} 25*4882a593Smuzhiyun 26*4882a593Smuzhiyunpython license_create_manifest() { 27*4882a593Smuzhiyun import oe.packagedata 28*4882a593Smuzhiyun from oe.rootfs import image_list_installed_packages 29*4882a593Smuzhiyun 30*4882a593Smuzhiyun build_images_from_feeds = d.getVar('BUILD_IMAGES_FROM_FEEDS') 31*4882a593Smuzhiyun if build_images_from_feeds == "1": 32*4882a593Smuzhiyun return 0 33*4882a593Smuzhiyun 34*4882a593Smuzhiyun pkg_dic = {} 35*4882a593Smuzhiyun for pkg in sorted(image_list_installed_packages(d)): 36*4882a593Smuzhiyun pkg_info = os.path.join(d.getVar('PKGDATA_DIR'), 37*4882a593Smuzhiyun 'runtime-reverse', pkg) 38*4882a593Smuzhiyun pkg_name = os.path.basename(os.readlink(pkg_info)) 39*4882a593Smuzhiyun 40*4882a593Smuzhiyun pkg_dic[pkg_name] = oe.packagedata.read_pkgdatafile(pkg_info) 41*4882a593Smuzhiyun if not "LICENSE" in pkg_dic[pkg_name].keys(): 42*4882a593Smuzhiyun pkg_lic_name = "LICENSE:" + pkg_name 43*4882a593Smuzhiyun pkg_dic[pkg_name]["LICENSE"] = pkg_dic[pkg_name][pkg_lic_name] 44*4882a593Smuzhiyun 45*4882a593Smuzhiyun rootfs_license_manifest = os.path.join(d.getVar('LICENSE_DIRECTORY'), 46*4882a593Smuzhiyun d.getVar('IMAGE_NAME'), 'license.manifest') 47*4882a593Smuzhiyun write_license_files(d, rootfs_license_manifest, pkg_dic, rootfs=True) 48*4882a593Smuzhiyun} 49*4882a593Smuzhiyun 50*4882a593Smuzhiyundef write_license_files(d, license_manifest, pkg_dic, rootfs=True): 51*4882a593Smuzhiyun import re 52*4882a593Smuzhiyun import stat 53*4882a593Smuzhiyun 54*4882a593Smuzhiyun bad_licenses = (d.getVar("INCOMPATIBLE_LICENSE") or "").split() 55*4882a593Smuzhiyun bad_licenses = expand_wildcard_licenses(d, bad_licenses) 56*4882a593Smuzhiyun 57*4882a593Smuzhiyun exceptions = (d.getVar("INCOMPATIBLE_LICENSE_EXCEPTIONS") or "").split() 58*4882a593Smuzhiyun with open(license_manifest, "w") as license_file: 59*4882a593Smuzhiyun for pkg in sorted(pkg_dic): 60*4882a593Smuzhiyun remaining_bad_licenses = oe.license.apply_pkg_license_exception(pkg, bad_licenses, exceptions) 61*4882a593Smuzhiyun incompatible_licenses = incompatible_pkg_license(d, remaining_bad_licenses, pkg_dic[pkg]["LICENSE"]) 62*4882a593Smuzhiyun if incompatible_licenses: 63*4882a593Smuzhiyun bb.fatal("Package %s cannot be installed into the image because it has incompatible license(s): %s" %(pkg, ' '.join(incompatible_licenses))) 64*4882a593Smuzhiyun else: 65*4882a593Smuzhiyun incompatible_licenses = incompatible_pkg_license(d, bad_licenses, pkg_dic[pkg]["LICENSE"]) 66*4882a593Smuzhiyun if incompatible_licenses: 67*4882a593Smuzhiyun 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*4882a593Smuzhiyun try: 69*4882a593Smuzhiyun (pkg_dic[pkg]["LICENSE"], pkg_dic[pkg]["LICENSES"]) = \ 70*4882a593Smuzhiyun oe.license.manifest_licenses(pkg_dic[pkg]["LICENSE"], 71*4882a593Smuzhiyun remaining_bad_licenses, canonical_license, d) 72*4882a593Smuzhiyun except oe.license.LicenseError as exc: 73*4882a593Smuzhiyun bb.fatal('%s: %s' % (d.getVar('P'), exc)) 74*4882a593Smuzhiyun 75*4882a593Smuzhiyun if not "IMAGE_MANIFEST" in pkg_dic[pkg]: 76*4882a593Smuzhiyun # Rootfs manifest 77*4882a593Smuzhiyun license_file.write("PACKAGE NAME: %s\n" % pkg) 78*4882a593Smuzhiyun license_file.write("PACKAGE VERSION: %s\n" % pkg_dic[pkg]["PV"]) 79*4882a593Smuzhiyun license_file.write("RECIPE NAME: %s\n" % pkg_dic[pkg]["PN"]) 80*4882a593Smuzhiyun license_file.write("LICENSE: %s\n\n" % pkg_dic[pkg]["LICENSE"]) 81*4882a593Smuzhiyun 82*4882a593Smuzhiyun # If the package doesn't contain any file, that is, its size is 0, the license 83*4882a593Smuzhiyun # isn't relevant as far as the final image is concerned. So doing license check 84*4882a593Smuzhiyun # doesn't make much sense, skip it. 85*4882a593Smuzhiyun if pkg_dic[pkg]["PKGSIZE:%s" % pkg] == "0": 86*4882a593Smuzhiyun continue 87*4882a593Smuzhiyun else: 88*4882a593Smuzhiyun # Image manifest 89*4882a593Smuzhiyun license_file.write("RECIPE NAME: %s\n" % pkg_dic[pkg]["PN"]) 90*4882a593Smuzhiyun license_file.write("VERSION: %s\n" % pkg_dic[pkg]["PV"]) 91*4882a593Smuzhiyun license_file.write("LICENSE: %s\n" % pkg_dic[pkg]["LICENSE"]) 92*4882a593Smuzhiyun license_file.write("FILES: %s\n\n" % pkg_dic[pkg]["FILES"]) 93*4882a593Smuzhiyun 94*4882a593Smuzhiyun for lic in pkg_dic[pkg]["LICENSES"]: 95*4882a593Smuzhiyun lic_file = os.path.join(d.getVar('LICENSE_DIRECTORY'), 96*4882a593Smuzhiyun pkg_dic[pkg]["PN"], "generic_%s" % 97*4882a593Smuzhiyun re.sub(r'\+', '', lic)) 98*4882a593Smuzhiyun # add explicity avoid of CLOSED license because isn't generic 99*4882a593Smuzhiyun if lic == "CLOSED": 100*4882a593Smuzhiyun continue 101*4882a593Smuzhiyun 102*4882a593Smuzhiyun if not os.path.exists(lic_file): 103*4882a593Smuzhiyun oe.qa.handle_error('license-file-missing', 104*4882a593Smuzhiyun "The license listed %s was not in the "\ 105*4882a593Smuzhiyun "licenses collected for recipe %s" 106*4882a593Smuzhiyun % (lic, pkg_dic[pkg]["PN"]), d) 107*4882a593Smuzhiyun oe.qa.exit_if_errors(d) 108*4882a593Smuzhiyun 109*4882a593Smuzhiyun # Two options here: 110*4882a593Smuzhiyun # - Just copy the manifest 111*4882a593Smuzhiyun # - Copy the manifest and the license directories 112*4882a593Smuzhiyun # With both options set we see a .5 M increase in core-image-minimal 113*4882a593Smuzhiyun copy_lic_manifest = d.getVar('COPY_LIC_MANIFEST') 114*4882a593Smuzhiyun copy_lic_dirs = d.getVar('COPY_LIC_DIRS') 115*4882a593Smuzhiyun if rootfs and copy_lic_manifest == "1": 116*4882a593Smuzhiyun rootfs_license_dir = d.getVar('ROOTFS_LICENSE_DIR') 117*4882a593Smuzhiyun bb.utils.mkdirhier(rootfs_license_dir) 118*4882a593Smuzhiyun rootfs_license_manifest = os.path.join(rootfs_license_dir, 119*4882a593Smuzhiyun os.path.split(license_manifest)[1]) 120*4882a593Smuzhiyun if not os.path.exists(rootfs_license_manifest): 121*4882a593Smuzhiyun oe.path.copyhardlink(license_manifest, rootfs_license_manifest) 122*4882a593Smuzhiyun 123*4882a593Smuzhiyun if copy_lic_dirs == "1": 124*4882a593Smuzhiyun for pkg in sorted(pkg_dic): 125*4882a593Smuzhiyun pkg_rootfs_license_dir = os.path.join(rootfs_license_dir, pkg) 126*4882a593Smuzhiyun bb.utils.mkdirhier(pkg_rootfs_license_dir) 127*4882a593Smuzhiyun pkg_license_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'), 128*4882a593Smuzhiyun pkg_dic[pkg]["PN"]) 129*4882a593Smuzhiyun 130*4882a593Smuzhiyun pkg_manifest_licenses = [canonical_license(d, lic) \ 131*4882a593Smuzhiyun for lic in pkg_dic[pkg]["LICENSES"]] 132*4882a593Smuzhiyun 133*4882a593Smuzhiyun licenses = os.listdir(pkg_license_dir) 134*4882a593Smuzhiyun for lic in licenses: 135*4882a593Smuzhiyun pkg_license = os.path.join(pkg_license_dir, lic) 136*4882a593Smuzhiyun pkg_rootfs_license = os.path.join(pkg_rootfs_license_dir, lic) 137*4882a593Smuzhiyun 138*4882a593Smuzhiyun if re.match(r"^generic_.*$", lic): 139*4882a593Smuzhiyun generic_lic = canonical_license(d, 140*4882a593Smuzhiyun re.search(r"^generic_(.*)$", lic).group(1)) 141*4882a593Smuzhiyun 142*4882a593Smuzhiyun # Do not copy generic license into package if isn't 143*4882a593Smuzhiyun # declared into LICENSES of the package. 144*4882a593Smuzhiyun if not re.sub(r'\+$', '', generic_lic) in \ 145*4882a593Smuzhiyun [re.sub(r'\+', '', lic) for lic in \ 146*4882a593Smuzhiyun pkg_manifest_licenses]: 147*4882a593Smuzhiyun continue 148*4882a593Smuzhiyun 149*4882a593Smuzhiyun if oe.license.license_ok(generic_lic, 150*4882a593Smuzhiyun bad_licenses) == False: 151*4882a593Smuzhiyun continue 152*4882a593Smuzhiyun 153*4882a593Smuzhiyun # Make sure we use only canonical name for the license file 154*4882a593Smuzhiyun generic_lic_file = "generic_%s" % generic_lic 155*4882a593Smuzhiyun rootfs_license = os.path.join(rootfs_license_dir, generic_lic_file) 156*4882a593Smuzhiyun if not os.path.exists(rootfs_license): 157*4882a593Smuzhiyun oe.path.copyhardlink(pkg_license, rootfs_license) 158*4882a593Smuzhiyun 159*4882a593Smuzhiyun if not os.path.exists(pkg_rootfs_license): 160*4882a593Smuzhiyun os.symlink(os.path.join('..', generic_lic_file), pkg_rootfs_license) 161*4882a593Smuzhiyun else: 162*4882a593Smuzhiyun if (oe.license.license_ok(canonical_license(d, 163*4882a593Smuzhiyun lic), bad_licenses) == False or 164*4882a593Smuzhiyun os.path.exists(pkg_rootfs_license)): 165*4882a593Smuzhiyun continue 166*4882a593Smuzhiyun 167*4882a593Smuzhiyun oe.path.copyhardlink(pkg_license, pkg_rootfs_license) 168*4882a593Smuzhiyun # Fixup file ownership and permissions 169*4882a593Smuzhiyun for walkroot, dirs, files in os.walk(rootfs_license_dir): 170*4882a593Smuzhiyun for f in files: 171*4882a593Smuzhiyun p = os.path.join(walkroot, f) 172*4882a593Smuzhiyun os.lchown(p, 0, 0) 173*4882a593Smuzhiyun if not os.path.islink(p): 174*4882a593Smuzhiyun os.chmod(p, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) 175*4882a593Smuzhiyun for dir in dirs: 176*4882a593Smuzhiyun p = os.path.join(walkroot, dir) 177*4882a593Smuzhiyun os.lchown(p, 0, 0) 178*4882a593Smuzhiyun os.chmod(p, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) 179*4882a593Smuzhiyun 180*4882a593Smuzhiyun 181*4882a593Smuzhiyun 182*4882a593Smuzhiyundef license_deployed_manifest(d): 183*4882a593Smuzhiyun """ 184*4882a593Smuzhiyun Write the license manifest for the deployed recipes. 185*4882a593Smuzhiyun The deployed recipes usually includes the bootloader 186*4882a593Smuzhiyun and extra files to boot the target. 187*4882a593Smuzhiyun """ 188*4882a593Smuzhiyun 189*4882a593Smuzhiyun dep_dic = {} 190*4882a593Smuzhiyun man_dic = {} 191*4882a593Smuzhiyun lic_dir = d.getVar("LICENSE_DIRECTORY") 192*4882a593Smuzhiyun 193*4882a593Smuzhiyun dep_dic = get_deployed_dependencies(d) 194*4882a593Smuzhiyun for dep in dep_dic.keys(): 195*4882a593Smuzhiyun man_dic[dep] = {} 196*4882a593Smuzhiyun # It is necessary to mark this will be used for image manifest 197*4882a593Smuzhiyun man_dic[dep]["IMAGE_MANIFEST"] = True 198*4882a593Smuzhiyun man_dic[dep]["PN"] = dep 199*4882a593Smuzhiyun man_dic[dep]["FILES"] = \ 200*4882a593Smuzhiyun " ".join(get_deployed_files(dep_dic[dep])) 201*4882a593Smuzhiyun with open(os.path.join(lic_dir, dep, "recipeinfo"), "r") as f: 202*4882a593Smuzhiyun for line in f.readlines(): 203*4882a593Smuzhiyun key,val = line.split(": ", 1) 204*4882a593Smuzhiyun man_dic[dep][key] = val[:-1] 205*4882a593Smuzhiyun 206*4882a593Smuzhiyun lic_manifest_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'), 207*4882a593Smuzhiyun d.getVar('IMAGE_NAME')) 208*4882a593Smuzhiyun bb.utils.mkdirhier(lic_manifest_dir) 209*4882a593Smuzhiyun image_license_manifest = os.path.join(lic_manifest_dir, 'image_license.manifest') 210*4882a593Smuzhiyun write_license_files(d, image_license_manifest, man_dic, rootfs=False) 211*4882a593Smuzhiyun 212*4882a593Smuzhiyun link_name = d.getVar('IMAGE_LINK_NAME') 213*4882a593Smuzhiyun if link_name: 214*4882a593Smuzhiyun lic_manifest_symlink_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'), 215*4882a593Smuzhiyun link_name) 216*4882a593Smuzhiyun # remove old symlink 217*4882a593Smuzhiyun if os.path.islink(lic_manifest_symlink_dir): 218*4882a593Smuzhiyun os.unlink(lic_manifest_symlink_dir) 219*4882a593Smuzhiyun 220*4882a593Smuzhiyun # create the image dir symlink 221*4882a593Smuzhiyun if lic_manifest_dir != lic_manifest_symlink_dir: 222*4882a593Smuzhiyun os.symlink(lic_manifest_dir, lic_manifest_symlink_dir) 223*4882a593Smuzhiyun 224*4882a593Smuzhiyundef get_deployed_dependencies(d): 225*4882a593Smuzhiyun """ 226*4882a593Smuzhiyun Get all the deployed dependencies of an image 227*4882a593Smuzhiyun """ 228*4882a593Smuzhiyun 229*4882a593Smuzhiyun deploy = {} 230*4882a593Smuzhiyun # Get all the dependencies for the current task (rootfs). 231*4882a593Smuzhiyun taskdata = d.getVar("BB_TASKDEPDATA", False) 232*4882a593Smuzhiyun pn = d.getVar("PN") 233*4882a593Smuzhiyun depends = list(set([dep[0] for dep 234*4882a593Smuzhiyun in list(taskdata.values()) 235*4882a593Smuzhiyun if not dep[0].endswith("-native") and not dep[0] == pn])) 236*4882a593Smuzhiyun 237*4882a593Smuzhiyun # To verify what was deployed it checks the rootfs dependencies against 238*4882a593Smuzhiyun # the SSTATE_MANIFESTS for "deploy" task. 239*4882a593Smuzhiyun # The manifest file name contains the arch. Because we are not running 240*4882a593Smuzhiyun # in the recipe context it is necessary to check every arch used. 241*4882a593Smuzhiyun sstate_manifest_dir = d.getVar("SSTATE_MANIFESTS") 242*4882a593Smuzhiyun archs = list(set(d.getVar("SSTATE_ARCHS").split())) 243*4882a593Smuzhiyun for dep in depends: 244*4882a593Smuzhiyun for arch in archs: 245*4882a593Smuzhiyun sstate_manifest_file = os.path.join(sstate_manifest_dir, 246*4882a593Smuzhiyun "manifest-%s-%s.deploy" % (arch, dep)) 247*4882a593Smuzhiyun if os.path.exists(sstate_manifest_file): 248*4882a593Smuzhiyun deploy[dep] = sstate_manifest_file 249*4882a593Smuzhiyun break 250*4882a593Smuzhiyun 251*4882a593Smuzhiyun return deploy 252*4882a593Smuzhiyunget_deployed_dependencies[vardepsexclude] = "BB_TASKDEPDATA" 253*4882a593Smuzhiyun 254*4882a593Smuzhiyundef get_deployed_files(man_file): 255*4882a593Smuzhiyun """ 256*4882a593Smuzhiyun Get the files deployed from the sstate manifest 257*4882a593Smuzhiyun """ 258*4882a593Smuzhiyun 259*4882a593Smuzhiyun dep_files = [] 260*4882a593Smuzhiyun excluded_files = [] 261*4882a593Smuzhiyun with open(man_file, "r") as manifest: 262*4882a593Smuzhiyun all_files = manifest.read() 263*4882a593Smuzhiyun for f in all_files.splitlines(): 264*4882a593Smuzhiyun if ((not (os.path.islink(f) or os.path.isdir(f))) and 265*4882a593Smuzhiyun not os.path.basename(f) in excluded_files): 266*4882a593Smuzhiyun dep_files.append(os.path.basename(f)) 267*4882a593Smuzhiyun return dep_files 268*4882a593Smuzhiyun 269*4882a593SmuzhiyunROOTFS_POSTPROCESS_COMMAND:prepend = "write_package_manifest; license_create_manifest; " 270*4882a593Smuzhiyundo_rootfs[recrdeptask] += "do_populate_lic" 271*4882a593Smuzhiyun 272*4882a593Smuzhiyunpython do_populate_lic_deploy() { 273*4882a593Smuzhiyun license_deployed_manifest(d) 274*4882a593Smuzhiyun oe.qa.exit_if_errors(d) 275*4882a593Smuzhiyun} 276*4882a593Smuzhiyun 277*4882a593Smuzhiyunaddtask populate_lic_deploy before do_build after do_image_complete 278*4882a593Smuzhiyundo_populate_lic_deploy[recrdeptask] += "do_populate_lic do_deploy" 279*4882a593Smuzhiyun 280*4882a593Smuzhiyunpython license_qa_dead_symlink() { 281*4882a593Smuzhiyun import os 282*4882a593Smuzhiyun 283*4882a593Smuzhiyun for root, dirs, files in os.walk(d.getVar('ROOTFS_LICENSE_DIR')): 284*4882a593Smuzhiyun for file in files: 285*4882a593Smuzhiyun full_path = root + "/" + file 286*4882a593Smuzhiyun if os.path.islink(full_path) and not os.path.exists(full_path): 287*4882a593Smuzhiyun bb.error("broken symlink: " + full_path) 288*4882a593Smuzhiyun} 289*4882a593SmuzhiyunIMAGE_QA_COMMANDS += "license_qa_dead_symlink" 290