diff options
author | Adrian Dudau <adrian.dudau@enea.com> | 2013-12-12 13:38:32 +0100 |
---|---|---|
committer | Adrian Dudau <adrian.dudau@enea.com> | 2013-12-12 13:50:20 +0100 |
commit | e2e6f6fe07049f33cb6348780fa975162752e421 (patch) | |
tree | b1813295411235d1297a0ed642b1346b24fdfb12 /meta/lib/oe/license.py | |
download | poky-e2e6f6fe07049f33cb6348780fa975162752e421.tar.gz |
initial commit of Enea Linux 3.1
Migrated from the internal git server on the dora-enea branch
Signed-off-by: Adrian Dudau <adrian.dudau@enea.com>
Diffstat (limited to 'meta/lib/oe/license.py')
-rw-r--r-- | meta/lib/oe/license.py | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/meta/lib/oe/license.py b/meta/lib/oe/license.py new file mode 100644 index 0000000000..340da61102 --- /dev/null +++ b/meta/lib/oe/license.py | |||
@@ -0,0 +1,116 @@ | |||
1 | # vi:sts=4:sw=4:et | ||
2 | """Code for parsing OpenEmbedded license strings""" | ||
3 | |||
4 | import ast | ||
5 | import re | ||
6 | from fnmatch import fnmatchcase as fnmatch | ||
7 | |||
8 | class LicenseError(Exception): | ||
9 | pass | ||
10 | |||
11 | class LicenseSyntaxError(LicenseError): | ||
12 | def __init__(self, licensestr, exc): | ||
13 | self.licensestr = licensestr | ||
14 | self.exc = exc | ||
15 | LicenseError.__init__(self) | ||
16 | |||
17 | def __str__(self): | ||
18 | return "error in '%s': %s" % (self.licensestr, self.exc) | ||
19 | |||
20 | class InvalidLicense(LicenseError): | ||
21 | def __init__(self, license): | ||
22 | self.license = license | ||
23 | LicenseError.__init__(self) | ||
24 | |||
25 | def __str__(self): | ||
26 | return "invalid characters in license '%s'" % self.license | ||
27 | |||
28 | license_operator = re.compile('([&|() ])') | ||
29 | license_pattern = re.compile('[a-zA-Z0-9.+_\-]+$') | ||
30 | |||
31 | class LicenseVisitor(ast.NodeVisitor): | ||
32 | """Syntax tree visitor which can accept OpenEmbedded license strings""" | ||
33 | def visit_string(self, licensestr): | ||
34 | new_elements = [] | ||
35 | elements = filter(lambda x: x.strip(), license_operator.split(licensestr)) | ||
36 | for pos, element in enumerate(elements): | ||
37 | if license_pattern.match(element): | ||
38 | if pos > 0 and license_pattern.match(elements[pos-1]): | ||
39 | new_elements.append('&') | ||
40 | element = '"' + element + '"' | ||
41 | elif not license_operator.match(element): | ||
42 | raise InvalidLicense(element) | ||
43 | new_elements.append(element) | ||
44 | |||
45 | self.visit(ast.parse(' '.join(new_elements))) | ||
46 | |||
47 | class FlattenVisitor(LicenseVisitor): | ||
48 | """Flatten a license tree (parsed from a string) by selecting one of each | ||
49 | set of OR options, in the way the user specifies""" | ||
50 | def __init__(self, choose_licenses): | ||
51 | self.choose_licenses = choose_licenses | ||
52 | self.licenses = [] | ||
53 | LicenseVisitor.__init__(self) | ||
54 | |||
55 | def visit_Str(self, node): | ||
56 | self.licenses.append(node.s) | ||
57 | |||
58 | def visit_BinOp(self, node): | ||
59 | if isinstance(node.op, ast.BitOr): | ||
60 | left = FlattenVisitor(self.choose_licenses) | ||
61 | left.visit(node.left) | ||
62 | |||
63 | right = FlattenVisitor(self.choose_licenses) | ||
64 | right.visit(node.right) | ||
65 | |||
66 | selected = self.choose_licenses(left.licenses, right.licenses) | ||
67 | self.licenses.extend(selected) | ||
68 | else: | ||
69 | self.generic_visit(node) | ||
70 | |||
71 | def flattened_licenses(licensestr, choose_licenses): | ||
72 | """Given a license string and choose_licenses function, return a flat list of licenses""" | ||
73 | flatten = FlattenVisitor(choose_licenses) | ||
74 | try: | ||
75 | flatten.visit_string(licensestr) | ||
76 | except SyntaxError as exc: | ||
77 | raise LicenseSyntaxError(licensestr, exc) | ||
78 | return flatten.licenses | ||
79 | |||
80 | def is_included(licensestr, whitelist=None, blacklist=None): | ||
81 | """Given a license string and whitelist and blacklist, determine if the | ||
82 | license string matches the whitelist and does not match the blacklist. | ||
83 | |||
84 | Returns a tuple holding the boolean state and a list of the applicable | ||
85 | licenses which were excluded (or None, if the state is True) | ||
86 | """ | ||
87 | |||
88 | def include_license(license): | ||
89 | return any(fnmatch(license, pattern) for pattern in whitelist) | ||
90 | |||
91 | def exclude_license(license): | ||
92 | return any(fnmatch(license, pattern) for pattern in blacklist) | ||
93 | |||
94 | def choose_licenses(alpha, beta): | ||
95 | """Select the option in an OR which is the 'best' (has the most | ||
96 | included licenses).""" | ||
97 | alpha_weight = len(filter(include_license, alpha)) | ||
98 | beta_weight = len(filter(include_license, beta)) | ||
99 | if alpha_weight > beta_weight: | ||
100 | return alpha | ||
101 | else: | ||
102 | return beta | ||
103 | |||
104 | if not whitelist: | ||
105 | whitelist = ['*'] | ||
106 | |||
107 | if not blacklist: | ||
108 | blacklist = [] | ||
109 | |||
110 | licenses = flattened_licenses(licensestr, choose_licenses) | ||
111 | excluded = filter(lambda lic: exclude_license(lic), licenses) | ||
112 | included = filter(lambda lic: include_license(lic), licenses) | ||
113 | if excluded: | ||
114 | return False, excluded | ||
115 | else: | ||
116 | return True, included | ||