summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher Larson <kergoth@gmail.com>2011-12-04 20:03:37 -0500
committerRichard Purdie <richard.purdie@linuxfoundation.org>2011-12-08 15:24:29 +0000
commita57de1ac9de96eef13f1fd6d37f3f73808d075ac (patch)
tree44812ce1150046b3f83d3b8668b5f5fc7ce55a26
parent91378835c6d857ce74dd51a242ad27e73e628a72 (diff)
downloadpoky-a57de1ac9de96eef13f1fd6d37f3f73808d075ac.tar.gz
license: split license parsing into oe.license
In addition to moving this functionality to oe.license, makes the string preparation more picky before passing it off to the ast compilation. This ensures that LICENSE entries like 'GPL/BSD' are seen as invalid (due to the presence of the unsupported '/'). (From OE-Core rev: 20d4068045c76e9dc2aff0c152dd02d6a109c9dd) Signed-off-by: Christopher Larson <kergoth@gmail.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--meta/classes/license.bbclass59
-rw-r--r--meta/lib/oe/license.py32
-rw-r--r--meta/lib/oe/tests/test_license.py38
3 files changed, 90 insertions, 39 deletions
diff --git a/meta/classes/license.bbclass b/meta/classes/license.bbclass
index 4d036b171e..8c6e2d2c9b 100644
--- a/meta/classes/license.bbclass
+++ b/meta/classes/license.bbclass
@@ -1,17 +1,17 @@
1# Populates LICENSE_DIRECTORY as set in distro config with the license files as set by 1# Populates LICENSE_DIRECTORY as set in distro config with the license files as set by
2# LIC_FILES_CHKSUM. 2# LIC_FILES_CHKSUM.
3# TODO: 3# TODO:
4# - We should also enable the ability to put the generated license directory onto the 4# - We should also enable the ability to put the generated license directory onto the
5# rootfs 5# rootfs
6# - Gather up more generic licenses 6# - Gather up more generic licenses
7# - There is a real issue revolving around license naming standards. See license names 7# - There is a real issue revolving around license naming standards. See license names
8# licenses.conf and compare them to the license names in the recipes. You'll see some 8# licenses.conf and compare them to the license names in the recipes. You'll see some
9# differences and that should be corrected. 9# differences and that should be corrected.
10 10
11LICENSE_DIRECTORY ??= "${DEPLOY_DIR}/licenses" 11LICENSE_DIRECTORY ??= "${DEPLOY_DIR}/licenses"
12LICSSTATEDIR = "${WORKDIR}/license-destdir/" 12LICSSTATEDIR = "${WORKDIR}/license-destdir/"
13 13
14addtask populate_lic after do_patch before do_package 14addtask populate_lic after do_patch before do_package
15do_populate_lic[dirs] = "${LICSSTATEDIR}/${PN}" 15do_populate_lic[dirs] = "${LICSSTATEDIR}/${PN}"
16do_populate_lic[cleandirs] = "${LICSSTATEDIR}" 16do_populate_lic[cleandirs] = "${LICSSTATEDIR}"
17 17
@@ -20,7 +20,7 @@ do_populate_lic[cleandirs] = "${LICSSTATEDIR}"
20# break the non-standardized license names that we find in LICENSE, we'll set 20# break the non-standardized license names that we find in LICENSE, we'll set
21# up a bunch of VarFlags to accomodate non-SPDX license names. 21# up a bunch of VarFlags to accomodate non-SPDX license names.
22# 22#
23# We should really discuss standardizing this field, but that's a longer term goal. 23# We should really discuss standardizing this field, but that's a longer term goal.
24# For now, we can do this and it should grab the most common LICENSE naming variations. 24# For now, we can do this and it should grab the most common LICENSE naming variations.
25 25
26#GPL variations 26#GPL variations
@@ -57,37 +57,25 @@ python do_populate_lic() {
57 import os 57 import os
58 import bb 58 import bb
59 import shutil 59 import shutil
60 import ast 60 import oe.license
61
62 class LicenseVisitor(ast.NodeVisitor):
63 def generic_visit(self, node):
64 ast.NodeVisitor.generic_visit(self, node)
65 61
62 class FindVisitor(oe.license.LicenseVisitor):
66 def visit_Str(self, node): 63 def visit_Str(self, node):
67 # 64 #
68 # Until I figure out what to do with 65 # Until I figure out what to do with
69 # the two modifiers I support (or greater = + 66 # the two modifiers I support (or greater = +
70 # and "with exceptions" being * 67 # and "with exceptions" being *
71 # we'll just strip out the modifier and put 68 # we'll just strip out the modifier and put
72 # the base license. 69 # the base license.
73 find_license(node.s.replace("+", "").replace("*", "")) 70 find_license(node.s.replace("+", "").replace("*", ""))
74 ast.NodeVisitor.generic_visit(self, node) 71 self.generic_visit(node)
75
76 def visit_BinOp(self, node):
77 op = node.op
78 if isinstance(op, ast.BitOr):
79 x = LicenseVisitor()
80 x.visit(node.left)
81 x.visit(node.right)
82 else:
83 ast.NodeVisitor.generic_visit(self, node)
84 72
85 def copy_license(source, destination, file_name): 73 def copy_license(source, destination, file_name):
86 try: 74 try:
87 bb.copyfile(os.path.join(source, file_name), os.path.join(destination, file_name)) 75 bb.copyfile(os.path.join(source, file_name), os.path.join(destination, file_name))
88 except: 76 except:
89 bb.warn("%s: No generic license file exists for: %s at %s" % (pn, file_name, source)) 77 bb.warn("%s: No generic license file exists for: %s at %s" % (pn, file_name, source))
90 pass 78 pass
91 79
92 def link_license(source, destination, file_name): 80 def link_license(source, destination, file_name):
93 try: 81 try:
@@ -108,8 +96,8 @@ python do_populate_lic() {
108 # Great, there is an SPDXLICENSEMAP. We can copy! 96 # Great, there is an SPDXLICENSEMAP. We can copy!
109 bb.note("We need to use a SPDXLICENSEMAP for %s" % (license_type)) 97 bb.note("We need to use a SPDXLICENSEMAP for %s" % (license_type))
110 spdx_generic = d.getVarFlag('SPDXLICENSEMAP', license_type) 98 spdx_generic = d.getVarFlag('SPDXLICENSEMAP', license_type)
111 copy_license(generic_directory, gen_lic_dest, spdx_generic) 99 copy_license(generic_directory, gen_lic_dest, spdx_generic)
112 link_license(gen_lic_dest, destdir, spdx_generic) 100 link_license(gen_lic_dest, destdir, spdx_generic)
113 else: 101 else:
114 # And here is where we warn people that their licenses are lousy 102 # And here is where we warn people that their licenses are lousy
115 bb.warn("%s: No generic license file exists for: %s at %s" % (pn, license_type, generic_directory)) 103 bb.warn("%s: No generic license file exists for: %s at %s" % (pn, license_type, generic_directory))
@@ -117,7 +105,7 @@ python do_populate_lic() {
117 pass 105 pass
118 elif os.path.isfile(os.path.join(generic_directory, license_type)): 106 elif os.path.isfile(os.path.join(generic_directory, license_type)):
119 copy_license(generic_directory, gen_lic_dest, license_type) 107 copy_license(generic_directory, gen_lic_dest, license_type)
120 link_license(gen_lic_dest, destdir, license_type) 108 link_license(gen_lic_dest, destdir, license_type)
121 109
122 # All the license types for the package 110 # All the license types for the package
123 license_types = d.getVar('LICENSE', True) 111 license_types = d.getVar('LICENSE', True)
@@ -130,7 +118,7 @@ python do_populate_lic() {
130 srcdir = d.getVar('S', True) 118 srcdir = d.getVar('S', True)
131 # Directory we store the generic licenses as set in the distro configuration 119 # Directory we store the generic licenses as set in the distro configuration
132 generic_directory = d.getVar('COMMON_LICENSE_DIR', True) 120 generic_directory = d.getVar('COMMON_LICENSE_DIR', True)
133 121
134 try: 122 try:
135 bb.mkdirhier(destdir) 123 bb.mkdirhier(destdir)
136 except: 124 except:
@@ -153,21 +141,14 @@ python do_populate_lic() {
153 # If the copy didn't occur, something horrible went wrong and we fail out 141 # If the copy didn't occur, something horrible went wrong and we fail out
154 if ret is False or ret == 0: 142 if ret is False or ret == 0:
155 bb.warn("%s could not be copied for some reason. It may not exist. WARN for now." % srclicfile) 143 bb.warn("%s could not be copied for some reason. It may not exist. WARN for now." % srclicfile)
156 144
157 gen_lic_dest = os.path.join(d.getVar('LICENSE_DIRECTORY', True), "common-licenses") 145 gen_lic_dest = os.path.join(d.getVar('LICENSE_DIRECTORY', True), "common-licenses")
158 146
159 clean_licenses = "" 147 v = FindVisitor()
160 148 try:
161 for x in license_types.replace("(", " ( ").replace(")", " ) ").split(): 149 v.visit_string(license_types)
162 if ((x != "(") and (x != ")") and (x != "&") and (x != "|")): 150 except oe.license.InvalidLicense as exc:
163 clean_licenses += "'" + x + "'" 151 bb.fatal("%s: %s" % (d.getVar('PF', True), exc))
164 else:
165 clean_licenses += " " + x + " "
166
167 # lstrip any possible indents, since ast needs python syntax.
168 node = ast.parse(clean_licenses.lstrip())
169 v = LicenseVisitor()
170 v.visit(node)
171} 152}
172 153
173SSTATETASKS += "do_populate_lic" 154SSTATETASKS += "do_populate_lic"
diff --git a/meta/lib/oe/license.py b/meta/lib/oe/license.py
new file mode 100644
index 0000000000..b230d3ef45
--- /dev/null
+++ b/meta/lib/oe/license.py
@@ -0,0 +1,32 @@
1# vi:sts=4:sw=4:et
2"""Code for parsing OpenEmbedded license strings"""
3
4import ast
5import re
6
7class InvalidLicense(StandardError):
8 def __init__(self, license):
9 self.license = license
10 StandardError.__init__(self)
11
12 def __str__(self):
13 return "invalid license '%s'" % self.license
14
15license_operator = re.compile('([&|() ])')
16license_pattern = re.compile('[a-zA-Z0-9.+_\-]+$')
17
18class LicenseVisitor(ast.NodeVisitor):
19 """Syntax tree visitor which can accept OpenEmbedded license strings"""
20 def visit_string(self, licensestr):
21 new_elements = []
22 elements = filter(lambda x: x.strip(), license_operator.split(licensestr))
23 for pos, element in enumerate(elements):
24 if license_pattern.match(element):
25 if pos > 0 and license_pattern.match(elements[pos-1]):
26 new_elements.append('&')
27 element = '"' + element + '"'
28 elif not license_operator.match(element):
29 raise InvalidLicense(element)
30 new_elements.append(element)
31
32 self.visit(ast.parse(' '.join(new_elements)))
diff --git a/meta/lib/oe/tests/test_license.py b/meta/lib/oe/tests/test_license.py
new file mode 100644
index 0000000000..cb949fc76f
--- /dev/null
+++ b/meta/lib/oe/tests/test_license.py
@@ -0,0 +1,38 @@
1import unittest
2import oe.license
3
4class SeenVisitor(oe.license.LicenseVisitor):
5 def __init__(self):
6 self.seen = []
7 oe.license.LicenseVisitor.__init__(self)
8
9 def visit_Str(self, node):
10 self.seen.append(node.s)
11
12class TestSingleLicense(unittest.TestCase):
13 licenses = [
14 "GPLv2",
15 "LGPL-2.0",
16 "Artistic",
17 "MIT",
18 "GPLv3+",
19 "FOO_BAR",
20 ]
21 invalid_licenses = ["GPL/BSD"]
22
23 @staticmethod
24 def parse(licensestr):
25 visitor = SeenVisitor()
26 visitor.visit_string(licensestr)
27 return visitor.seen
28
29 def test_single_licenses(self):
30 for license in self.licenses:
31 licenses = self.parse(license)
32 self.assertListEqual(licenses, [license])
33
34 def test_invalid_licenses(self):
35 for license in self.invalid_licenses:
36 with self.assertRaises(oe.license.InvalidLicense) as cm:
37 self.parse(license)
38 self.assertEqual(cm.exception.license, license)