diff options
Diffstat (limited to 'meta/lib/oe/license.py')
-rw-r--r-- | meta/lib/oe/license.py | 85 |
1 files changed, 56 insertions, 29 deletions
diff --git a/meta/lib/oe/license.py b/meta/lib/oe/license.py index f0f661c3ba..8955cbdeb2 100644 --- a/meta/lib/oe/license.py +++ b/meta/lib/oe/license.py @@ -1,4 +1,6 @@ -# vi:sts=4:sw=4:et +# +# SPDX-License-Identifier: GPL-2.0-only +# """Code for parsing OpenEmbedded license strings""" import ast @@ -8,14 +10,7 @@ from fnmatch import fnmatchcase as fnmatch def license_ok(license, dont_want_licenses): """ Return False if License exist in dont_want_licenses else True """ for dwl in dont_want_licenses: - # If you want to exclude license named generically 'X', we - # surely want to exclude 'X+' as well. In consequence, we - # will exclude a trailing '+' character from LICENSE in - # case INCOMPATIBLE_LICENSE is not a 'X+' license. - lic = license - if not re.search('\+$', dwl): - lic = re.sub('\+', '', license) - if fnmatch(lic, dwl): + if fnmatch(license, dwl): return False return True @@ -40,14 +35,14 @@ class InvalidLicense(LicenseError): return "invalid characters in license '%s'" % self.license license_operator_chars = '&|() ' -license_operator = re.compile('([' + license_operator_chars + '])') -license_pattern = re.compile('[a-zA-Z0-9.+_\-]+$') +license_operator = re.compile(r'([' + license_operator_chars + '])') +license_pattern = re.compile(r'[a-zA-Z0-9.+_\-]+$') class LicenseVisitor(ast.NodeVisitor): """Get elements based on OpenEmbedded license strings""" def get_elements(self, licensestr): new_elements = [] - elements = filter(lambda x: x.strip(), license_operator.split(licensestr)) + elements = list([x for x in license_operator.split(licensestr) if x.strip()]) for pos, element in enumerate(elements): if license_pattern.match(element): if pos > 0 and license_pattern.match(elements[pos-1]): @@ -79,6 +74,9 @@ class FlattenVisitor(LicenseVisitor): def visit_Str(self, node): self.licenses.append(node.s) + def visit_Constant(self, node): + self.licenses.append(node.value) + def visit_BinOp(self, node): if isinstance(node.op, ast.BitOr): left = FlattenVisitor(self.choose_licenses) @@ -101,39 +99,47 @@ def flattened_licenses(licensestr, choose_licenses): raise LicenseSyntaxError(licensestr, exc) return flatten.licenses -def is_included(licensestr, whitelist=None, blacklist=None): - """Given a license string and whitelist and blacklist, determine if the - license string matches the whitelist and does not match the blacklist. +def is_included(licensestr, include_licenses=None, exclude_licenses=None): + """Given a license string, a list of licenses to include and a list of + licenses to exclude, determine if the license string matches the include + list and does not match the exclude list. Returns a tuple holding the boolean state and a list of the applicable - licenses which were excluded (or None, if the state is True) - """ + licenses that were excluded if state is False, or the licenses that were + included if the state is True.""" def include_license(license): - return any(fnmatch(license, pattern) for pattern in whitelist) + return any(fnmatch(license, pattern) for pattern in include_licenses) def exclude_license(license): - return any(fnmatch(license, pattern) for pattern in blacklist) + return any(fnmatch(license, pattern) for pattern in exclude_licenses) def choose_licenses(alpha, beta): """Select the option in an OR which is the 'best' (has the most - included licenses).""" - alpha_weight = len(filter(include_license, alpha)) - beta_weight = len(filter(include_license, beta)) - if alpha_weight > beta_weight: + included licenses and no excluded licenses).""" + # The factor 1000 below is arbitrary, just expected to be much larger + # than the number of licenses actually specified. That way the weight + # will be negative if the list of licenses contains an excluded license, + # but still gives a higher weight to the list with the most included + # licenses. + alpha_weight = (len(list(filter(include_license, alpha))) - + 1000 * (len(list(filter(exclude_license, alpha))) > 0)) + beta_weight = (len(list(filter(include_license, beta))) - + 1000 * (len(list(filter(exclude_license, beta))) > 0)) + if alpha_weight >= beta_weight: return alpha else: return beta - if not whitelist: - whitelist = ['*'] + if not include_licenses: + include_licenses = ['*'] - if not blacklist: - blacklist = [] + if not exclude_licenses: + exclude_licenses = [] licenses = flattened_licenses(licensestr, choose_licenses) - excluded = filter(lambda lic: exclude_license(lic), licenses) - included = filter(lambda lic: include_license(lic), licenses) + excluded = [lic for lic in licenses if exclude_license(lic)] + included = [lic for lic in licenses if include_license(lic)] if excluded: return False, excluded else: @@ -215,3 +221,24 @@ def manifest_licenses(licensestr, dont_want_licenses, canonical_license, d): manifest.licensestr = manifest.licensestr.replace('[', '(').replace(']', ')') return (manifest.licensestr, manifest.licenses) + +class ListVisitor(LicenseVisitor): + """Record all different licenses found in the license string""" + def __init__(self): + self.licenses = set() + + def visit_Str(self, node): + self.licenses.add(node.s) + + def visit_Constant(self, node): + self.licenses.add(node.value) + +def list_licenses(licensestr): + """Simply get a list of all licenses mentioned in a license string. + Binary operators are not applied or taken into account in any way""" + visitor = ListVisitor() + try: + visitor.visit_string(licensestr) + except SyntaxError as exc: + raise LicenseSyntaxError(licensestr, exc) + return visitor.licenses |