xref: /OK3568_Linux_fs/yocto/poky/meta/lib/oe/license.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#
2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only
3*4882a593Smuzhiyun#
4*4882a593Smuzhiyun"""Code for parsing OpenEmbedded license strings"""
5*4882a593Smuzhiyun
6*4882a593Smuzhiyunimport ast
7*4882a593Smuzhiyunimport re
8*4882a593Smuzhiyunfrom fnmatch import fnmatchcase as fnmatch
9*4882a593Smuzhiyun
10*4882a593Smuzhiyundef license_ok(license, dont_want_licenses):
11*4882a593Smuzhiyun    """ Return False if License exist in dont_want_licenses else True """
12*4882a593Smuzhiyun    for dwl in dont_want_licenses:
13*4882a593Smuzhiyun        if fnmatch(license, dwl):
14*4882a593Smuzhiyun            return False
15*4882a593Smuzhiyun    return True
16*4882a593Smuzhiyun
17*4882a593Smuzhiyundef obsolete_license_list():
18*4882a593Smuzhiyun    return ["AGPL-3", "AGPL-3+", "AGPLv3", "AGPLv3+", "AGPLv3.0", "AGPLv3.0+", "AGPL-3.0", "AGPL-3.0+", "BSD-0-Clause",
19*4882a593Smuzhiyun            "GPL-1", "GPL-1+", "GPLv1", "GPLv1+", "GPLv1.0", "GPLv1.0+", "GPL-1.0", "GPL-1.0+", "GPL-2", "GPL-2+", "GPLv2",
20*4882a593Smuzhiyun            "GPLv2+", "GPLv2.0", "GPLv2.0+", "GPL-2.0", "GPL-2.0+", "GPL-3", "GPL-3+", "GPLv3", "GPLv3+", "GPLv3.0", "GPLv3.0+",
21*4882a593Smuzhiyun            "GPL-3.0", "GPL-3.0+", "LGPLv2", "LGPLv2+", "LGPLv2.0", "LGPLv2.0+", "LGPL-2.0", "LGPL-2.0+", "LGPL2.1", "LGPL2.1+",
22*4882a593Smuzhiyun            "LGPLv2.1", "LGPLv2.1+", "LGPL-2.1", "LGPL-2.1+", "LGPLv3", "LGPLv3+", "LGPL-3.0", "LGPL-3.0+", "MPL-1", "MPLv1",
23*4882a593Smuzhiyun            "MPLv1.1", "MPLv2", "MIT-X", "MIT-style", "openssl", "PSF", "PSFv2", "Python-2", "Apachev2", "Apache-2", "Artisticv1",
24*4882a593Smuzhiyun            "Artistic-1", "AFL-2", "AFL-1", "AFLv2", "AFLv1", "CDDLv1", "CDDL-1", "EPLv1.0", "FreeType", "Nauman",
25*4882a593Smuzhiyun            "tcl", "vim", "SGIv1"]
26*4882a593Smuzhiyun
27*4882a593Smuzhiyunclass LicenseError(Exception):
28*4882a593Smuzhiyun    pass
29*4882a593Smuzhiyun
30*4882a593Smuzhiyunclass LicenseSyntaxError(LicenseError):
31*4882a593Smuzhiyun    def __init__(self, licensestr, exc):
32*4882a593Smuzhiyun        self.licensestr = licensestr
33*4882a593Smuzhiyun        self.exc = exc
34*4882a593Smuzhiyun        LicenseError.__init__(self)
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun    def __str__(self):
37*4882a593Smuzhiyun        return "error in '%s': %s" % (self.licensestr, self.exc)
38*4882a593Smuzhiyun
39*4882a593Smuzhiyunclass InvalidLicense(LicenseError):
40*4882a593Smuzhiyun    def __init__(self, license):
41*4882a593Smuzhiyun        self.license = license
42*4882a593Smuzhiyun        LicenseError.__init__(self)
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun    def __str__(self):
45*4882a593Smuzhiyun        return "invalid characters in license '%s'" % self.license
46*4882a593Smuzhiyun
47*4882a593Smuzhiyunlicense_operator_chars = '&|() '
48*4882a593Smuzhiyunlicense_operator = re.compile(r'([' + license_operator_chars + '])')
49*4882a593Smuzhiyunlicense_pattern = re.compile(r'[a-zA-Z0-9.+_\-]+$')
50*4882a593Smuzhiyun
51*4882a593Smuzhiyunclass LicenseVisitor(ast.NodeVisitor):
52*4882a593Smuzhiyun    """Get elements based on OpenEmbedded license strings"""
53*4882a593Smuzhiyun    def get_elements(self, licensestr):
54*4882a593Smuzhiyun        new_elements = []
55*4882a593Smuzhiyun        elements = list([x for x in license_operator.split(licensestr) if x.strip()])
56*4882a593Smuzhiyun        for pos, element in enumerate(elements):
57*4882a593Smuzhiyun            if license_pattern.match(element):
58*4882a593Smuzhiyun                if pos > 0 and license_pattern.match(elements[pos-1]):
59*4882a593Smuzhiyun                    new_elements.append('&')
60*4882a593Smuzhiyun                element = '"' + element + '"'
61*4882a593Smuzhiyun            elif not license_operator.match(element):
62*4882a593Smuzhiyun                raise InvalidLicense(element)
63*4882a593Smuzhiyun            new_elements.append(element)
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun        return new_elements
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun    """Syntax tree visitor which can accept elements previously generated with
68*4882a593Smuzhiyun    OpenEmbedded license string"""
69*4882a593Smuzhiyun    def visit_elements(self, elements):
70*4882a593Smuzhiyun        self.visit(ast.parse(' '.join(elements)))
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun    """Syntax tree visitor which can accept OpenEmbedded license strings"""
73*4882a593Smuzhiyun    def visit_string(self, licensestr):
74*4882a593Smuzhiyun        self.visit_elements(self.get_elements(licensestr))
75*4882a593Smuzhiyun
76*4882a593Smuzhiyunclass FlattenVisitor(LicenseVisitor):
77*4882a593Smuzhiyun    """Flatten a license tree (parsed from a string) by selecting one of each
78*4882a593Smuzhiyun    set of OR options, in the way the user specifies"""
79*4882a593Smuzhiyun    def __init__(self, choose_licenses):
80*4882a593Smuzhiyun        self.choose_licenses = choose_licenses
81*4882a593Smuzhiyun        self.licenses = []
82*4882a593Smuzhiyun        LicenseVisitor.__init__(self)
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun    def visit_Str(self, node):
85*4882a593Smuzhiyun        self.licenses.append(node.s)
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun    def visit_Constant(self, node):
88*4882a593Smuzhiyun        self.licenses.append(node.value)
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun    def visit_BinOp(self, node):
91*4882a593Smuzhiyun        if isinstance(node.op, ast.BitOr):
92*4882a593Smuzhiyun            left = FlattenVisitor(self.choose_licenses)
93*4882a593Smuzhiyun            left.visit(node.left)
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun            right = FlattenVisitor(self.choose_licenses)
96*4882a593Smuzhiyun            right.visit(node.right)
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun            selected = self.choose_licenses(left.licenses, right.licenses)
99*4882a593Smuzhiyun            self.licenses.extend(selected)
100*4882a593Smuzhiyun        else:
101*4882a593Smuzhiyun            self.generic_visit(node)
102*4882a593Smuzhiyun
103*4882a593Smuzhiyundef flattened_licenses(licensestr, choose_licenses):
104*4882a593Smuzhiyun    """Given a license string and choose_licenses function, return a flat list of licenses"""
105*4882a593Smuzhiyun    flatten = FlattenVisitor(choose_licenses)
106*4882a593Smuzhiyun    try:
107*4882a593Smuzhiyun        flatten.visit_string(licensestr)
108*4882a593Smuzhiyun    except SyntaxError as exc:
109*4882a593Smuzhiyun        raise LicenseSyntaxError(licensestr, exc)
110*4882a593Smuzhiyun    return flatten.licenses
111*4882a593Smuzhiyun
112*4882a593Smuzhiyundef is_included(licensestr, include_licenses=None, exclude_licenses=None):
113*4882a593Smuzhiyun    """Given a license string, a list of licenses to include and a list of
114*4882a593Smuzhiyun    licenses to exclude, determine if the license string matches the include
115*4882a593Smuzhiyun    list and does not match the exclude list.
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun    Returns a tuple holding the boolean state and a list of the applicable
118*4882a593Smuzhiyun    licenses that were excluded if state is False, or the licenses that were
119*4882a593Smuzhiyun    included if the state is True."""
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun    def include_license(license):
122*4882a593Smuzhiyun        return any(fnmatch(license, pattern) for pattern in include_licenses)
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun    def exclude_license(license):
125*4882a593Smuzhiyun        return any(fnmatch(license, pattern) for pattern in exclude_licenses)
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun    def choose_licenses(alpha, beta):
128*4882a593Smuzhiyun        """Select the option in an OR which is the 'best' (has the most
129*4882a593Smuzhiyun        included licenses and no excluded licenses)."""
130*4882a593Smuzhiyun        # The factor 1000 below is arbitrary, just expected to be much larger
131*4882a593Smuzhiyun        # than the number of licenses actually specified. That way the weight
132*4882a593Smuzhiyun        # will be negative if the list of licenses contains an excluded license,
133*4882a593Smuzhiyun        # but still gives a higher weight to the list with the most included
134*4882a593Smuzhiyun        # licenses.
135*4882a593Smuzhiyun        alpha_weight = (len(list(filter(include_license, alpha))) -
136*4882a593Smuzhiyun                        1000 * (len(list(filter(exclude_license, alpha))) > 0))
137*4882a593Smuzhiyun        beta_weight = (len(list(filter(include_license, beta))) -
138*4882a593Smuzhiyun                       1000 * (len(list(filter(exclude_license, beta))) > 0))
139*4882a593Smuzhiyun        if alpha_weight >= beta_weight:
140*4882a593Smuzhiyun            return alpha
141*4882a593Smuzhiyun        else:
142*4882a593Smuzhiyun            return beta
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun    if not include_licenses:
145*4882a593Smuzhiyun        include_licenses = ['*']
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun    if not exclude_licenses:
148*4882a593Smuzhiyun        exclude_licenses = []
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun    licenses = flattened_licenses(licensestr, choose_licenses)
151*4882a593Smuzhiyun    excluded = [lic for lic in licenses if exclude_license(lic)]
152*4882a593Smuzhiyun    included = [lic for lic in licenses if include_license(lic)]
153*4882a593Smuzhiyun    if excluded:
154*4882a593Smuzhiyun        return False, excluded
155*4882a593Smuzhiyun    else:
156*4882a593Smuzhiyun        return True, included
157*4882a593Smuzhiyun
158*4882a593Smuzhiyunclass ManifestVisitor(LicenseVisitor):
159*4882a593Smuzhiyun    """Walk license tree (parsed from a string) removing the incompatible
160*4882a593Smuzhiyun    licenses specified"""
161*4882a593Smuzhiyun    def __init__(self, dont_want_licenses, canonical_license, d):
162*4882a593Smuzhiyun        self._dont_want_licenses = dont_want_licenses
163*4882a593Smuzhiyun        self._canonical_license = canonical_license
164*4882a593Smuzhiyun        self._d = d
165*4882a593Smuzhiyun        self._operators = []
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun        self.licenses = []
168*4882a593Smuzhiyun        self.licensestr = ''
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun        LicenseVisitor.__init__(self)
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun    def visit(self, node):
173*4882a593Smuzhiyun        if isinstance(node, ast.Str):
174*4882a593Smuzhiyun            lic = node.s
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun            if license_ok(self._canonical_license(self._d, lic),
177*4882a593Smuzhiyun                    self._dont_want_licenses) == True:
178*4882a593Smuzhiyun                if self._operators:
179*4882a593Smuzhiyun                    ops = []
180*4882a593Smuzhiyun                    for op in self._operators:
181*4882a593Smuzhiyun                        if op == '[':
182*4882a593Smuzhiyun                            ops.append(op)
183*4882a593Smuzhiyun                        elif op == ']':
184*4882a593Smuzhiyun                            ops.append(op)
185*4882a593Smuzhiyun                        else:
186*4882a593Smuzhiyun                            if not ops:
187*4882a593Smuzhiyun                                ops.append(op)
188*4882a593Smuzhiyun                            elif ops[-1] in ['[', ']']:
189*4882a593Smuzhiyun                                ops.append(op)
190*4882a593Smuzhiyun                            else:
191*4882a593Smuzhiyun                                ops[-1] = op
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun                    for op in ops:
194*4882a593Smuzhiyun                        if op == '[' or op == ']':
195*4882a593Smuzhiyun                            self.licensestr += op
196*4882a593Smuzhiyun                        elif self.licenses:
197*4882a593Smuzhiyun                            self.licensestr += ' ' + op + ' '
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun                    self._operators = []
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun                self.licensestr += lic
202*4882a593Smuzhiyun                self.licenses.append(lic)
203*4882a593Smuzhiyun        elif isinstance(node, ast.BitAnd):
204*4882a593Smuzhiyun            self._operators.append("&")
205*4882a593Smuzhiyun        elif isinstance(node, ast.BitOr):
206*4882a593Smuzhiyun            self._operators.append("|")
207*4882a593Smuzhiyun        elif isinstance(node, ast.List):
208*4882a593Smuzhiyun            self._operators.append("[")
209*4882a593Smuzhiyun        elif isinstance(node, ast.Load):
210*4882a593Smuzhiyun            self.licensestr += "]"
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun        self.generic_visit(node)
213*4882a593Smuzhiyun
214*4882a593Smuzhiyundef manifest_licenses(licensestr, dont_want_licenses, canonical_license, d):
215*4882a593Smuzhiyun    """Given a license string and dont_want_licenses list,
216*4882a593Smuzhiyun       return license string filtered and a list of licenses"""
217*4882a593Smuzhiyun    manifest = ManifestVisitor(dont_want_licenses, canonical_license, d)
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun    try:
220*4882a593Smuzhiyun        elements = manifest.get_elements(licensestr)
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun        # Replace '()' to '[]' for handle in ast as List and Load types.
223*4882a593Smuzhiyun        elements = ['[' if e == '(' else e for e in elements]
224*4882a593Smuzhiyun        elements = [']' if e == ')' else e for e in elements]
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun        manifest.visit_elements(elements)
227*4882a593Smuzhiyun    except SyntaxError as exc:
228*4882a593Smuzhiyun        raise LicenseSyntaxError(licensestr, exc)
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun    # Replace '[]' to '()' for output correct license.
231*4882a593Smuzhiyun    manifest.licensestr = manifest.licensestr.replace('[', '(').replace(']', ')')
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun    return (manifest.licensestr, manifest.licenses)
234*4882a593Smuzhiyun
235*4882a593Smuzhiyunclass ListVisitor(LicenseVisitor):
236*4882a593Smuzhiyun    """Record all different licenses found in the license string"""
237*4882a593Smuzhiyun    def __init__(self):
238*4882a593Smuzhiyun        self.licenses = set()
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun    def visit_Str(self, node):
241*4882a593Smuzhiyun        self.licenses.add(node.s)
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun    def visit_Constant(self, node):
244*4882a593Smuzhiyun        self.licenses.add(node.value)
245*4882a593Smuzhiyun
246*4882a593Smuzhiyundef list_licenses(licensestr):
247*4882a593Smuzhiyun    """Simply get a list of all licenses mentioned in a license string.
248*4882a593Smuzhiyun       Binary operators are not applied or taken into account in any way"""
249*4882a593Smuzhiyun    visitor = ListVisitor()
250*4882a593Smuzhiyun    try:
251*4882a593Smuzhiyun        visitor.visit_string(licensestr)
252*4882a593Smuzhiyun    except SyntaxError as exc:
253*4882a593Smuzhiyun        raise LicenseSyntaxError(licensestr, exc)
254*4882a593Smuzhiyun    return visitor.licenses
255*4882a593Smuzhiyun
256*4882a593Smuzhiyundef apply_pkg_license_exception(pkg, bad_licenses, exceptions):
257*4882a593Smuzhiyun    """Return remaining bad licenses after removing any package exceptions"""
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun    return [lic for lic in bad_licenses if pkg + ':' + lic not in exceptions]
260