1*4882a593Smuzhiyun# Populates LICENSE_DIRECTORY as set in distro config with the license files as set by 2*4882a593Smuzhiyun# LIC_FILES_CHKSUM. 3*4882a593Smuzhiyun# TODO: 4*4882a593Smuzhiyun# - There is a real issue revolving around license naming standards. 5*4882a593Smuzhiyun 6*4882a593SmuzhiyunLICENSE_DIRECTORY ??= "${DEPLOY_DIR}/licenses" 7*4882a593SmuzhiyunLICSSTATEDIR = "${WORKDIR}/license-destdir/" 8*4882a593Smuzhiyun 9*4882a593Smuzhiyun# Create extra package with license texts and add it to RRECOMMENDS:${PN} 10*4882a593SmuzhiyunLICENSE_CREATE_PACKAGE[type] = "boolean" 11*4882a593SmuzhiyunLICENSE_CREATE_PACKAGE ??= "0" 12*4882a593SmuzhiyunLICENSE_PACKAGE_SUFFIX ??= "-lic" 13*4882a593SmuzhiyunLICENSE_FILES_DIRECTORY ??= "${datadir}/licenses/" 14*4882a593Smuzhiyun 15*4882a593Smuzhiyunaddtask populate_lic after do_patch before do_build 16*4882a593Smuzhiyundo_populate_lic[dirs] = "${LICSSTATEDIR}/${PN}" 17*4882a593Smuzhiyundo_populate_lic[cleandirs] = "${LICSSTATEDIR}" 18*4882a593Smuzhiyun 19*4882a593Smuzhiyunpython do_populate_lic() { 20*4882a593Smuzhiyun """ 21*4882a593Smuzhiyun Populate LICENSE_DIRECTORY with licenses. 22*4882a593Smuzhiyun """ 23*4882a593Smuzhiyun lic_files_paths = find_license_files(d) 24*4882a593Smuzhiyun 25*4882a593Smuzhiyun # The base directory we wrangle licenses to 26*4882a593Smuzhiyun destdir = os.path.join(d.getVar('LICSSTATEDIR'), d.getVar('PN')) 27*4882a593Smuzhiyun copy_license_files(lic_files_paths, destdir) 28*4882a593Smuzhiyun info = get_recipe_info(d) 29*4882a593Smuzhiyun with open(os.path.join(destdir, "recipeinfo"), "w") as f: 30*4882a593Smuzhiyun for key in sorted(info.keys()): 31*4882a593Smuzhiyun f.write("%s: %s\n" % (key, info[key])) 32*4882a593Smuzhiyun oe.qa.exit_if_errors(d) 33*4882a593Smuzhiyun} 34*4882a593Smuzhiyun 35*4882a593SmuzhiyunPSEUDO_IGNORE_PATHS .= ",${@','.join(((d.getVar('COMMON_LICENSE_DIR') or '') + ' ' + (d.getVar('LICENSE_PATH') or '') + ' ' + d.getVar('COREBASE') + '/meta/COPYING').split())}" 36*4882a593Smuzhiyun# it would be better to copy them in do_install:append, but find_license_filesa is python 37*4882a593Smuzhiyunpython perform_packagecopy:prepend () { 38*4882a593Smuzhiyun enabled = oe.data.typed_value('LICENSE_CREATE_PACKAGE', d) 39*4882a593Smuzhiyun if d.getVar('CLASSOVERRIDE') == 'class-target' and enabled: 40*4882a593Smuzhiyun lic_files_paths = find_license_files(d) 41*4882a593Smuzhiyun 42*4882a593Smuzhiyun # LICENSE_FILES_DIRECTORY starts with '/' so os.path.join cannot be used to join D and LICENSE_FILES_DIRECTORY 43*4882a593Smuzhiyun destdir = d.getVar('D') + os.path.join(d.getVar('LICENSE_FILES_DIRECTORY'), d.getVar('PN')) 44*4882a593Smuzhiyun copy_license_files(lic_files_paths, destdir) 45*4882a593Smuzhiyun add_package_and_files(d) 46*4882a593Smuzhiyun} 47*4882a593Smuzhiyunperform_packagecopy[vardeps] += "LICENSE_CREATE_PACKAGE" 48*4882a593Smuzhiyun 49*4882a593Smuzhiyundef get_recipe_info(d): 50*4882a593Smuzhiyun info = {} 51*4882a593Smuzhiyun info["PV"] = d.getVar("PV") 52*4882a593Smuzhiyun info["PR"] = d.getVar("PR") 53*4882a593Smuzhiyun info["LICENSE"] = d.getVar("LICENSE") 54*4882a593Smuzhiyun return info 55*4882a593Smuzhiyun 56*4882a593Smuzhiyundef add_package_and_files(d): 57*4882a593Smuzhiyun packages = d.getVar('PACKAGES') 58*4882a593Smuzhiyun files = d.getVar('LICENSE_FILES_DIRECTORY') 59*4882a593Smuzhiyun pn = d.getVar('PN') 60*4882a593Smuzhiyun pn_lic = "%s%s" % (pn, d.getVar('LICENSE_PACKAGE_SUFFIX', False)) 61*4882a593Smuzhiyun if pn_lic in packages.split(): 62*4882a593Smuzhiyun bb.warn("%s package already existed in %s." % (pn_lic, pn)) 63*4882a593Smuzhiyun else: 64*4882a593Smuzhiyun # first in PACKAGES to be sure that nothing else gets LICENSE_FILES_DIRECTORY 65*4882a593Smuzhiyun d.setVar('PACKAGES', "%s %s" % (pn_lic, packages)) 66*4882a593Smuzhiyun d.setVar('FILES:' + pn_lic, files) 67*4882a593Smuzhiyun 68*4882a593Smuzhiyundef copy_license_files(lic_files_paths, destdir): 69*4882a593Smuzhiyun import shutil 70*4882a593Smuzhiyun import errno 71*4882a593Smuzhiyun 72*4882a593Smuzhiyun bb.utils.mkdirhier(destdir) 73*4882a593Smuzhiyun for (basename, path, beginline, endline) in lic_files_paths: 74*4882a593Smuzhiyun try: 75*4882a593Smuzhiyun src = path 76*4882a593Smuzhiyun dst = os.path.join(destdir, basename) 77*4882a593Smuzhiyun if os.path.exists(dst): 78*4882a593Smuzhiyun os.remove(dst) 79*4882a593Smuzhiyun if os.path.islink(src): 80*4882a593Smuzhiyun src = os.path.realpath(src) 81*4882a593Smuzhiyun canlink = os.access(src, os.W_OK) and (os.stat(src).st_dev == os.stat(destdir).st_dev) and beginline is None and endline is None 82*4882a593Smuzhiyun if canlink: 83*4882a593Smuzhiyun try: 84*4882a593Smuzhiyun os.link(src, dst) 85*4882a593Smuzhiyun except OSError as err: 86*4882a593Smuzhiyun if err.errno == errno.EXDEV: 87*4882a593Smuzhiyun # Copy license files if hardlink is not possible even if st_dev is the 88*4882a593Smuzhiyun # same on source and destination (docker container with device-mapper?) 89*4882a593Smuzhiyun canlink = False 90*4882a593Smuzhiyun else: 91*4882a593Smuzhiyun raise 92*4882a593Smuzhiyun # Only chown if we did hardlink and we're running under pseudo 93*4882a593Smuzhiyun if canlink and os.environ.get('PSEUDO_DISABLED') == '0': 94*4882a593Smuzhiyun os.chown(dst,0,0) 95*4882a593Smuzhiyun if not canlink: 96*4882a593Smuzhiyun begin_idx = max(0, int(beginline) - 1) if beginline is not None else None 97*4882a593Smuzhiyun end_idx = max(0, int(endline)) if endline is not None else None 98*4882a593Smuzhiyun if begin_idx is None and end_idx is None: 99*4882a593Smuzhiyun shutil.copyfile(src, dst) 100*4882a593Smuzhiyun else: 101*4882a593Smuzhiyun with open(src, 'rb') as src_f: 102*4882a593Smuzhiyun with open(dst, 'wb') as dst_f: 103*4882a593Smuzhiyun dst_f.write(b''.join(src_f.readlines()[begin_idx:end_idx])) 104*4882a593Smuzhiyun 105*4882a593Smuzhiyun except Exception as e: 106*4882a593Smuzhiyun bb.warn("Could not copy license file %s to %s: %s" % (src, dst, e)) 107*4882a593Smuzhiyun 108*4882a593Smuzhiyundef find_license_files(d): 109*4882a593Smuzhiyun """ 110*4882a593Smuzhiyun Creates list of files used in LIC_FILES_CHKSUM and generic LICENSE files. 111*4882a593Smuzhiyun """ 112*4882a593Smuzhiyun import shutil 113*4882a593Smuzhiyun import oe.license 114*4882a593Smuzhiyun from collections import defaultdict, OrderedDict 115*4882a593Smuzhiyun 116*4882a593Smuzhiyun # All the license files for the package 117*4882a593Smuzhiyun lic_files = d.getVar('LIC_FILES_CHKSUM') or "" 118*4882a593Smuzhiyun pn = d.getVar('PN') 119*4882a593Smuzhiyun # The license files are located in S/LIC_FILE_CHECKSUM. 120*4882a593Smuzhiyun srcdir = d.getVar('S') 121*4882a593Smuzhiyun # Directory we store the generic licenses as set in the distro configuration 122*4882a593Smuzhiyun generic_directory = d.getVar('COMMON_LICENSE_DIR') 123*4882a593Smuzhiyun # List of basename, path tuples 124*4882a593Smuzhiyun lic_files_paths = [] 125*4882a593Smuzhiyun # hash for keep track generic lics mappings 126*4882a593Smuzhiyun non_generic_lics = {} 127*4882a593Smuzhiyun # Entries from LIC_FILES_CHKSUM 128*4882a593Smuzhiyun lic_chksums = {} 129*4882a593Smuzhiyun license_source_dirs = [] 130*4882a593Smuzhiyun license_source_dirs.append(generic_directory) 131*4882a593Smuzhiyun try: 132*4882a593Smuzhiyun additional_lic_dirs = d.getVar('LICENSE_PATH').split() 133*4882a593Smuzhiyun for lic_dir in additional_lic_dirs: 134*4882a593Smuzhiyun license_source_dirs.append(lic_dir) 135*4882a593Smuzhiyun except: 136*4882a593Smuzhiyun pass 137*4882a593Smuzhiyun 138*4882a593Smuzhiyun class FindVisitor(oe.license.LicenseVisitor): 139*4882a593Smuzhiyun def visit_Str(self, node): 140*4882a593Smuzhiyun # 141*4882a593Smuzhiyun # Until I figure out what to do with 142*4882a593Smuzhiyun # the two modifiers I support (or greater = + 143*4882a593Smuzhiyun # and "with exceptions" being * 144*4882a593Smuzhiyun # we'll just strip out the modifier and put 145*4882a593Smuzhiyun # the base license. 146*4882a593Smuzhiyun find_license(node.s.replace("+", "").replace("*", "")) 147*4882a593Smuzhiyun self.generic_visit(node) 148*4882a593Smuzhiyun 149*4882a593Smuzhiyun def visit_Constant(self, node): 150*4882a593Smuzhiyun find_license(node.value.replace("+", "").replace("*", "")) 151*4882a593Smuzhiyun self.generic_visit(node) 152*4882a593Smuzhiyun 153*4882a593Smuzhiyun def find_license(license_type): 154*4882a593Smuzhiyun try: 155*4882a593Smuzhiyun bb.utils.mkdirhier(gen_lic_dest) 156*4882a593Smuzhiyun except: 157*4882a593Smuzhiyun pass 158*4882a593Smuzhiyun spdx_generic = None 159*4882a593Smuzhiyun license_source = None 160*4882a593Smuzhiyun # If the generic does not exist we need to check to see if there is an SPDX mapping to it, 161*4882a593Smuzhiyun # unless NO_GENERIC_LICENSE is set. 162*4882a593Smuzhiyun for lic_dir in license_source_dirs: 163*4882a593Smuzhiyun if not os.path.isfile(os.path.join(lic_dir, license_type)): 164*4882a593Smuzhiyun if d.getVarFlag('SPDXLICENSEMAP', license_type) != None: 165*4882a593Smuzhiyun # Great, there is an SPDXLICENSEMAP. We can copy! 166*4882a593Smuzhiyun bb.debug(1, "We need to use a SPDXLICENSEMAP for %s" % (license_type)) 167*4882a593Smuzhiyun spdx_generic = d.getVarFlag('SPDXLICENSEMAP', license_type) 168*4882a593Smuzhiyun license_source = lic_dir 169*4882a593Smuzhiyun break 170*4882a593Smuzhiyun elif os.path.isfile(os.path.join(lic_dir, license_type)): 171*4882a593Smuzhiyun spdx_generic = license_type 172*4882a593Smuzhiyun license_source = lic_dir 173*4882a593Smuzhiyun break 174*4882a593Smuzhiyun 175*4882a593Smuzhiyun non_generic_lic = d.getVarFlag('NO_GENERIC_LICENSE', license_type) 176*4882a593Smuzhiyun if spdx_generic and license_source: 177*4882a593Smuzhiyun # we really should copy to generic_ + spdx_generic, however, that ends up messing the manifest 178*4882a593Smuzhiyun # audit up. This should be fixed in emit_pkgdata (or, we actually got and fix all the recipes) 179*4882a593Smuzhiyun 180*4882a593Smuzhiyun lic_files_paths.append(("generic_" + license_type, os.path.join(license_source, spdx_generic), 181*4882a593Smuzhiyun None, None)) 182*4882a593Smuzhiyun 183*4882a593Smuzhiyun # The user may attempt to use NO_GENERIC_LICENSE for a generic license which doesn't make sense 184*4882a593Smuzhiyun # and should not be allowed, warn the user in this case. 185*4882a593Smuzhiyun if d.getVarFlag('NO_GENERIC_LICENSE', license_type): 186*4882a593Smuzhiyun oe.qa.handle_error("license-no-generic", 187*4882a593Smuzhiyun "%s: %s is a generic license, please don't use NO_GENERIC_LICENSE for it." % (pn, license_type), d) 188*4882a593Smuzhiyun 189*4882a593Smuzhiyun elif non_generic_lic and non_generic_lic in lic_chksums: 190*4882a593Smuzhiyun # if NO_GENERIC_LICENSE is set, we copy the license files from the fetched source 191*4882a593Smuzhiyun # of the package rather than the license_source_dirs. 192*4882a593Smuzhiyun lic_files_paths.append(("generic_" + license_type, 193*4882a593Smuzhiyun os.path.join(srcdir, non_generic_lic), None, None)) 194*4882a593Smuzhiyun non_generic_lics[non_generic_lic] = license_type 195*4882a593Smuzhiyun else: 196*4882a593Smuzhiyun # Explicitly avoid the CLOSED license because this isn't generic 197*4882a593Smuzhiyun if license_type != 'CLOSED': 198*4882a593Smuzhiyun # And here is where we warn people that their licenses are lousy 199*4882a593Smuzhiyun oe.qa.handle_error("license-exists", 200*4882a593Smuzhiyun "%s: No generic license file exists for: %s in any provider" % (pn, license_type), d) 201*4882a593Smuzhiyun pass 202*4882a593Smuzhiyun 203*4882a593Smuzhiyun if not generic_directory: 204*4882a593Smuzhiyun bb.fatal("COMMON_LICENSE_DIR is unset. Please set this in your distro config") 205*4882a593Smuzhiyun 206*4882a593Smuzhiyun for url in lic_files.split(): 207*4882a593Smuzhiyun try: 208*4882a593Smuzhiyun (method, host, path, user, pswd, parm) = bb.fetch.decodeurl(url) 209*4882a593Smuzhiyun if method != "file" or not path: 210*4882a593Smuzhiyun raise bb.fetch.MalformedUrl() 211*4882a593Smuzhiyun except bb.fetch.MalformedUrl: 212*4882a593Smuzhiyun bb.fatal("%s: LIC_FILES_CHKSUM contains an invalid URL: %s" % (d.getVar('PF'), url)) 213*4882a593Smuzhiyun # We want the license filename and path 214*4882a593Smuzhiyun chksum = parm.get('md5', None) 215*4882a593Smuzhiyun beginline = parm.get('beginline') 216*4882a593Smuzhiyun endline = parm.get('endline') 217*4882a593Smuzhiyun lic_chksums[path] = (chksum, beginline, endline) 218*4882a593Smuzhiyun 219*4882a593Smuzhiyun v = FindVisitor() 220*4882a593Smuzhiyun try: 221*4882a593Smuzhiyun v.visit_string(d.getVar('LICENSE')) 222*4882a593Smuzhiyun except oe.license.InvalidLicense as exc: 223*4882a593Smuzhiyun bb.fatal('%s: %s' % (d.getVar('PF'), exc)) 224*4882a593Smuzhiyun except SyntaxError: 225*4882a593Smuzhiyun oe.qa.handle_error("license-syntax", 226*4882a593Smuzhiyun "%s: Failed to parse it's LICENSE field." % (d.getVar('PF')), d) 227*4882a593Smuzhiyun # Add files from LIC_FILES_CHKSUM to list of license files 228*4882a593Smuzhiyun lic_chksum_paths = defaultdict(OrderedDict) 229*4882a593Smuzhiyun for path, data in sorted(lic_chksums.items()): 230*4882a593Smuzhiyun lic_chksum_paths[os.path.basename(path)][data] = (os.path.join(srcdir, path), data[1], data[2]) 231*4882a593Smuzhiyun for basename, files in lic_chksum_paths.items(): 232*4882a593Smuzhiyun if len(files) == 1: 233*4882a593Smuzhiyun # Don't copy again a LICENSE already handled as non-generic 234*4882a593Smuzhiyun if basename in non_generic_lics: 235*4882a593Smuzhiyun continue 236*4882a593Smuzhiyun data = list(files.values())[0] 237*4882a593Smuzhiyun lic_files_paths.append(tuple([basename] + list(data))) 238*4882a593Smuzhiyun else: 239*4882a593Smuzhiyun # If there are multiple different license files with identical 240*4882a593Smuzhiyun # basenames we rename them to <file>.0, <file>.1, ... 241*4882a593Smuzhiyun for i, data in enumerate(files.values()): 242*4882a593Smuzhiyun lic_files_paths.append(tuple(["%s.%d" % (basename, i)] + list(data))) 243*4882a593Smuzhiyun 244*4882a593Smuzhiyun return lic_files_paths 245*4882a593Smuzhiyun 246*4882a593Smuzhiyundef return_spdx(d, license): 247*4882a593Smuzhiyun """ 248*4882a593Smuzhiyun This function returns the spdx mapping of a license if it exists. 249*4882a593Smuzhiyun """ 250*4882a593Smuzhiyun return d.getVarFlag('SPDXLICENSEMAP', license) 251*4882a593Smuzhiyun 252*4882a593Smuzhiyundef canonical_license(d, license): 253*4882a593Smuzhiyun """ 254*4882a593Smuzhiyun Return the canonical (SPDX) form of the license if available (so GPLv3 255*4882a593Smuzhiyun becomes GPL-3.0-only) or the passed license if there is no canonical form. 256*4882a593Smuzhiyun """ 257*4882a593Smuzhiyun return d.getVarFlag('SPDXLICENSEMAP', license) or license 258*4882a593Smuzhiyun 259*4882a593Smuzhiyundef expand_wildcard_licenses(d, wildcard_licenses): 260*4882a593Smuzhiyun """ 261*4882a593Smuzhiyun There are some common wildcard values users may want to use. Support them 262*4882a593Smuzhiyun here. 263*4882a593Smuzhiyun """ 264*4882a593Smuzhiyun licenses = set(wildcard_licenses) 265*4882a593Smuzhiyun mapping = { 266*4882a593Smuzhiyun "AGPL-3.0*" : ["AGPL-3.0-only", "AGPL-3.0-or-later"], 267*4882a593Smuzhiyun "GPL-3.0*" : ["GPL-3.0-only", "GPL-3.0-or-later"], 268*4882a593Smuzhiyun "LGPL-3.0*" : ["LGPL-3.0-only", "LGPL-3.0-or-later"], 269*4882a593Smuzhiyun } 270*4882a593Smuzhiyun for k in mapping: 271*4882a593Smuzhiyun if k in wildcard_licenses: 272*4882a593Smuzhiyun licenses.remove(k) 273*4882a593Smuzhiyun for item in mapping[k]: 274*4882a593Smuzhiyun licenses.add(item) 275*4882a593Smuzhiyun 276*4882a593Smuzhiyun for l in licenses: 277*4882a593Smuzhiyun if l in oe.license.obsolete_license_list(): 278*4882a593Smuzhiyun bb.fatal("Error, %s is an obsolete license, please use an SPDX reference in INCOMPATIBLE_LICENSE" % l) 279*4882a593Smuzhiyun if "*" in l: 280*4882a593Smuzhiyun bb.fatal("Error, %s is an invalid license wildcard entry" % l) 281*4882a593Smuzhiyun 282*4882a593Smuzhiyun return list(licenses) 283*4882a593Smuzhiyun 284*4882a593Smuzhiyundef incompatible_license_contains(license, truevalue, falsevalue, d): 285*4882a593Smuzhiyun license = canonical_license(d, license) 286*4882a593Smuzhiyun bad_licenses = (d.getVar('INCOMPATIBLE_LICENSE') or "").split() 287*4882a593Smuzhiyun bad_licenses = expand_wildcard_licenses(d, bad_licenses) 288*4882a593Smuzhiyun return truevalue if license in bad_licenses else falsevalue 289*4882a593Smuzhiyun 290*4882a593Smuzhiyundef incompatible_pkg_license(d, dont_want_licenses, license): 291*4882a593Smuzhiyun # Handles an "or" or two license sets provided by 292*4882a593Smuzhiyun # flattened_licenses(), pick one that works if possible. 293*4882a593Smuzhiyun def choose_lic_set(a, b): 294*4882a593Smuzhiyun return a if all(oe.license.license_ok(canonical_license(d, lic), 295*4882a593Smuzhiyun dont_want_licenses) for lic in a) else b 296*4882a593Smuzhiyun 297*4882a593Smuzhiyun try: 298*4882a593Smuzhiyun licenses = oe.license.flattened_licenses(license, choose_lic_set) 299*4882a593Smuzhiyun except oe.license.LicenseError as exc: 300*4882a593Smuzhiyun bb.fatal('%s: %s' % (d.getVar('P'), exc)) 301*4882a593Smuzhiyun 302*4882a593Smuzhiyun incompatible_lic = [] 303*4882a593Smuzhiyun for l in licenses: 304*4882a593Smuzhiyun license = canonical_license(d, l) 305*4882a593Smuzhiyun if not oe.license.license_ok(license, dont_want_licenses): 306*4882a593Smuzhiyun incompatible_lic.append(license) 307*4882a593Smuzhiyun 308*4882a593Smuzhiyun return sorted(incompatible_lic) 309*4882a593Smuzhiyun 310*4882a593Smuzhiyundef incompatible_license(d, dont_want_licenses, package=None): 311*4882a593Smuzhiyun """ 312*4882a593Smuzhiyun This function checks if a recipe has only incompatible licenses. It also 313*4882a593Smuzhiyun take into consideration 'or' operand. dont_want_licenses should be passed 314*4882a593Smuzhiyun as canonical (SPDX) names. 315*4882a593Smuzhiyun """ 316*4882a593Smuzhiyun import oe.license 317*4882a593Smuzhiyun license = d.getVar("LICENSE:%s" % package) if package else None 318*4882a593Smuzhiyun if not license: 319*4882a593Smuzhiyun license = d.getVar('LICENSE') 320*4882a593Smuzhiyun 321*4882a593Smuzhiyun return incompatible_pkg_license(d, dont_want_licenses, license) 322*4882a593Smuzhiyun 323*4882a593Smuzhiyundef check_license_flags(d): 324*4882a593Smuzhiyun """ 325*4882a593Smuzhiyun This function checks if a recipe has any LICENSE_FLAGS that 326*4882a593Smuzhiyun aren't acceptable. 327*4882a593Smuzhiyun 328*4882a593Smuzhiyun If it does, it returns the all LICENSE_FLAGS missing from the list 329*4882a593Smuzhiyun of acceptable license flags, or all of the LICENSE_FLAGS if there 330*4882a593Smuzhiyun is no list of acceptable flags. 331*4882a593Smuzhiyun 332*4882a593Smuzhiyun If everything is is acceptable, it returns None. 333*4882a593Smuzhiyun """ 334*4882a593Smuzhiyun 335*4882a593Smuzhiyun def license_flag_matches(flag, acceptlist, pn): 336*4882a593Smuzhiyun """ 337*4882a593Smuzhiyun Return True if flag matches something in acceptlist, None if not. 338*4882a593Smuzhiyun 339*4882a593Smuzhiyun Before we test a flag against the acceptlist, we append _${PN} 340*4882a593Smuzhiyun to it. We then try to match that string against the 341*4882a593Smuzhiyun acceptlist. This covers the normal case, where we expect 342*4882a593Smuzhiyun LICENSE_FLAGS to be a simple string like 'commercial', which 343*4882a593Smuzhiyun the user typically matches exactly in the acceptlist by 344*4882a593Smuzhiyun explicitly appending the package name e.g 'commercial_foo'. 345*4882a593Smuzhiyun If we fail the match however, we then split the flag across 346*4882a593Smuzhiyun '_' and append each fragment and test until we either match or 347*4882a593Smuzhiyun run out of fragments. 348*4882a593Smuzhiyun """ 349*4882a593Smuzhiyun flag_pn = ("%s_%s" % (flag, pn)) 350*4882a593Smuzhiyun for candidate in acceptlist: 351*4882a593Smuzhiyun if flag_pn == candidate: 352*4882a593Smuzhiyun return True 353*4882a593Smuzhiyun 354*4882a593Smuzhiyun flag_cur = "" 355*4882a593Smuzhiyun flagments = flag_pn.split("_") 356*4882a593Smuzhiyun flagments.pop() # we've already tested the full string 357*4882a593Smuzhiyun for flagment in flagments: 358*4882a593Smuzhiyun if flag_cur: 359*4882a593Smuzhiyun flag_cur += "_" 360*4882a593Smuzhiyun flag_cur += flagment 361*4882a593Smuzhiyun for candidate in acceptlist: 362*4882a593Smuzhiyun if flag_cur == candidate: 363*4882a593Smuzhiyun return True 364*4882a593Smuzhiyun return False 365*4882a593Smuzhiyun 366*4882a593Smuzhiyun def all_license_flags_match(license_flags, acceptlist): 367*4882a593Smuzhiyun """ Return all unmatched flags, None if all flags match """ 368*4882a593Smuzhiyun pn = d.getVar('PN') 369*4882a593Smuzhiyun split_acceptlist = acceptlist.split() 370*4882a593Smuzhiyun flags = [] 371*4882a593Smuzhiyun for flag in license_flags.split(): 372*4882a593Smuzhiyun if not license_flag_matches(flag, split_acceptlist, pn): 373*4882a593Smuzhiyun flags.append(flag) 374*4882a593Smuzhiyun return flags if flags else None 375*4882a593Smuzhiyun 376*4882a593Smuzhiyun license_flags = d.getVar('LICENSE_FLAGS') 377*4882a593Smuzhiyun if license_flags: 378*4882a593Smuzhiyun acceptlist = d.getVar('LICENSE_FLAGS_ACCEPTED') 379*4882a593Smuzhiyun if not acceptlist: 380*4882a593Smuzhiyun return license_flags.split() 381*4882a593Smuzhiyun unmatched_flags = all_license_flags_match(license_flags, acceptlist) 382*4882a593Smuzhiyun if unmatched_flags: 383*4882a593Smuzhiyun return unmatched_flags 384*4882a593Smuzhiyun return None 385*4882a593Smuzhiyun 386*4882a593Smuzhiyundef check_license_format(d): 387*4882a593Smuzhiyun """ 388*4882a593Smuzhiyun This function checks if LICENSE is well defined, 389*4882a593Smuzhiyun Validate operators in LICENSES. 390*4882a593Smuzhiyun No spaces are allowed between LICENSES. 391*4882a593Smuzhiyun """ 392*4882a593Smuzhiyun pn = d.getVar('PN') 393*4882a593Smuzhiyun licenses = d.getVar('LICENSE') 394*4882a593Smuzhiyun from oe.license import license_operator, license_operator_chars, license_pattern 395*4882a593Smuzhiyun 396*4882a593Smuzhiyun elements = list(filter(lambda x: x.strip(), license_operator.split(licenses))) 397*4882a593Smuzhiyun for pos, element in enumerate(elements): 398*4882a593Smuzhiyun if license_pattern.match(element): 399*4882a593Smuzhiyun if pos > 0 and license_pattern.match(elements[pos - 1]): 400*4882a593Smuzhiyun oe.qa.handle_error('license-format', 401*4882a593Smuzhiyun '%s: LICENSE value "%s" has an invalid format - license names ' \ 402*4882a593Smuzhiyun 'must be separated by the following characters to indicate ' \ 403*4882a593Smuzhiyun 'the license selection: %s' % 404*4882a593Smuzhiyun (pn, licenses, license_operator_chars), d) 405*4882a593Smuzhiyun elif not license_operator.match(element): 406*4882a593Smuzhiyun oe.qa.handle_error('license-format', 407*4882a593Smuzhiyun '%s: LICENSE value "%s" has an invalid separator "%s" that is not ' \ 408*4882a593Smuzhiyun 'in the valid list of separators (%s)' % 409*4882a593Smuzhiyun (pn, licenses, element, license_operator_chars), d) 410*4882a593Smuzhiyun 411*4882a593SmuzhiyunSSTATETASKS += "do_populate_lic" 412*4882a593Smuzhiyundo_populate_lic[sstate-inputdirs] = "${LICSSTATEDIR}" 413*4882a593Smuzhiyundo_populate_lic[sstate-outputdirs] = "${LICENSE_DIRECTORY}/" 414*4882a593Smuzhiyun 415*4882a593SmuzhiyunIMAGE_CLASSES:append = " license_image" 416*4882a593Smuzhiyun 417*4882a593Smuzhiyunpython do_populate_lic_setscene () { 418*4882a593Smuzhiyun sstate_setscene(d) 419*4882a593Smuzhiyun} 420*4882a593Smuzhiyunaddtask do_populate_lic_setscene 421