[yocto] [license][PATCH 1/2] license: Add support for SPDX 2.0 operators

Sergei Miroshnichenko sergeimir at emcraft.com
Tue Jun 21 06:09:36 PDT 2016


Composite SPDX license expressions can be constructed using "OR",
"AND" and "WITH" operators: map them to "|" and "&" Yocto operators
accordingly.

Recognize long form license identifiers: "[DocumentRef-XXX:][LicenseRef-]YYY".

Tested on the following expressions:
LICENSE = "Artistic-1.0"
LICENSE = "LicenseRef-Artistic-1.0 AND NASA-1.3"
LICENSE = "(LicenseRef-Artistic-1.0 WITH Classpath-exception-2.0)
           OR (DocumentRef-spdx-tool-1.2:LicenseRef-NASA-1.3 AND GPL-2.0+)"

Reference:
https://spdx.org/sites/spdx/files/SPDX-2.0.pdf Appendix IV: SPDX License Expression

Signed-off-by: Sergei Miroshnichenko <sergeimir at emcraft.com>
---
 meta/classes/license.bbclass | 17 ++++++++++-------
 meta/lib/oe/license.py       | 21 ++++++++++++++++-----
 2 files changed, 26 insertions(+), 12 deletions(-)

diff --git a/meta/classes/license.bbclass b/meta/classes/license.bbclass
index 69335d6..7f7f7dd 100644
--- a/meta/classes/license.bbclass
+++ b/meta/classes/license.bbclass
@@ -633,20 +633,23 @@ def check_license_format(d):
     """
     pn = d.getVar('PN', True)
     licenses = d.getVar('LICENSE', True)
-    from oe.license import license_operator, license_operator_chars, license_pattern
+    from oe.license import license_operator, license_pattern
 
     elements = filter(lambda x: x.strip(), license_operator.split(licenses))
     for pos, element in enumerate(elements):
-        if license_pattern.match(element):
-            if pos > 0 and license_pattern.match(elements[pos - 1]):
+        operator_match = license_operator.match(element)
+        license_match = license_pattern.match(element)
+
+        if license_match and not operator_match:
+            if pos > 0 and license_pattern.match(elements[pos - 1]) and not license_operator.match(elements[pos - 1]):
                 bb.warn('%s: LICENSE value "%s" has an invalid format - license names ' \
-                        'must be separated by the following characters to indicate ' \
+                        'must be separated by the following operators to indicate ' \
                         'the license selection: %s' %
-                        (pn, licenses, license_operator_chars))
-        elif not license_operator.match(element):
+                        (pn, licenses, license_operator.pattern))
+        elif not operator_match:
             bb.warn('%s: LICENSE value "%s" has an invalid separator "%s" that is not ' \
                     'in the valid list of separators (%s)' %
-                    (pn, licenses, element, license_operator_chars))
+                    (pn, licenses, element, license_operator.pattern))
 
 SSTATETASKS += "do_populate_lic"
 do_populate_lic[sstate-inputdirs] = "${LICSSTATEDIR}"
diff --git a/meta/lib/oe/license.py b/meta/lib/oe/license.py
index f0f661c..4f42a9e 100644
--- a/meta/lib/oe/license.py
+++ b/meta/lib/oe/license.py
@@ -40,8 +40,13 @@ 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('([' + license_operator_chars + ']|AND|OR|WITH)')
+license_pattern = re.compile('^(?:DocumentRef-[a-zA-Z0-9.\-]+:)?(?:LicenseRef-)?([a-zA-Z0-9_.+\-]+)$')
+
+license_operator_map = {}
+license_operator_map["OR"] = '|'
+license_operator_map["AND"] = '&'
+license_operator_map["WITH"] = '&'
 
 class LicenseVisitor(ast.NodeVisitor):
     """Get elements based on OpenEmbedded license strings"""
@@ -49,11 +54,17 @@ class LicenseVisitor(ast.NodeVisitor):
         new_elements = []
         elements = filter(lambda x: x.strip(), license_operator.split(licensestr))
         for pos, element in enumerate(elements):
-            if license_pattern.match(element):
-                if pos > 0 and license_pattern.match(elements[pos-1]):
+            operator_match = license_operator.match(element)
+            license_match = license_pattern.match(element)
+            if operator_match:
+                if license_operator_map.get(element, None) != None:
+                    element = license_operator_map.get(element)
+            elif license_match:
+                if pos > 0 and license_pattern.match(elements[pos-1]) and not license_operator.match(elements[pos-1]):
                     new_elements.append('&')
+                element = license_match.group(1)
                 element = '"' + element + '"'
-            elif not license_operator.match(element):
+            else:
                 raise InvalidLicense(element)
             new_elements.append(element)
 
-- 
2.1.0




More information about the yocto mailing list