]> code.ossystems Code Review - openembedded-core.git/commitdiff
license.py: Correct selection of licenses in is_included()
authorPeter Kjellerstedt <peter.kjellerstedt@axis.com>
Fri, 29 Sep 2017 15:52:34 +0000 (17:52 +0200)
committerRichard Purdie <richard.purdie@linuxfoundation.org>
Fri, 6 Oct 2017 11:03:33 +0000 (12:03 +0100)
When faced with multiple sets of licenses combined with | (OR), it was
possible for oe.license.is_included() to choose a set of licenses with
a blacklisted license and then report failure, even if choosing
another set of licenses would have resulted in a successful
result. This happened when the chosen set still contained more
whitelisted licenses than the other set.

This change makes sure a set with any blacklisted license is always
considered with a lower weight than a set with only whitelisted
licenses.

Example: Faced with the license string "GPL-3.0 & GPL-2.0 & LGPL-2.1 |
Proprietary" and with "GPL-3.0" being blacklisted, the old code would
report a failure since "GPL-3.0 & GPL-2.0 & LGPL-2.1" still contains
more whitelisted licenses than "Proprietary" does.

This change also adds a unit test for oe.license.is_included().

Signed-off-by: Peter Kjellerstedt <peter.kjellerstedt@axis.com>
Signed-off-by: Ross Burton <ross.burton@intel.com>
meta/lib/oe/license.py
meta/lib/oeqa/selftest/cases/oelib/license.py

index 8d2fd1709cf12f2b4c12d2793e2c6545898b54b2..ca385d5187a0bdde73a8b6f72d29e74c8a569b6a 100644 (file)
@@ -106,7 +106,8 @@ def is_included(licensestr, whitelist=None, blacklist=None):
     license string matches the whitelist and does not match the blacklist.
 
     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):
@@ -117,10 +118,17 @@ def is_included(licensestr, whitelist=None, blacklist=None):
 
     def choose_licenses(alpha, beta):
         """Select the option in an OR which is the 'best' (has the most
-        included licenses)."""
-        alpha_weight = len(list(filter(include_license, alpha)))
-        beta_weight = len(list(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
+        # that 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
index bfd9ed9c29bebfe719c98bb64b7a776ee9c667e4..d7f91fb2f4d2908691d605d1edb47f7cdf1a7493 100644 (file)
@@ -66,3 +66,34 @@ class TestComplexCombinations(TestSimpleCombinations):
         "(GPL-2.0|Proprietary)&BSD-4-clause&MIT": ["GPL-2.0", "BSD-4-clause", "MIT"],
     }
     preferred = ["BAR", "OMEGA", "BETA", "GPL-2.0"]
+
+class TestIsIncluded(TestCase):
+    tests = {
+        ("FOO | BAR", None, None):
+            [True, ["FOO"]],
+        ("FOO | BAR", None, "FOO"):
+            [True, ["BAR"]],
+        ("FOO | BAR", "BAR", None):
+            [True, ["BAR"]],
+        ("FOO | BAR & FOOBAR", "*BAR", None):
+            [True, ["BAR", "FOOBAR"]],
+        ("FOO | BAR & FOOBAR", None, "FOO*"):
+            [False, ["FOOBAR"]],
+        ("(FOO | BAR) & FOOBAR | BARFOO", None, "FOO"):
+            [True, ["BAR", "FOOBAR"]],
+        ("(FOO | BAR) & FOOBAR | BAZ & MOO & BARFOO", None, "FOO"):
+            [True, ["BAZ", "MOO", "BARFOO"]],
+        ("GPL-3.0 & GPL-2.0 & LGPL-2.1 | Proprietary", None, None):
+            [True, ["GPL-3.0", "GPL-2.0", "LGPL-2.1"]],
+        ("GPL-3.0 & GPL-2.0 & LGPL-2.1 | Proprietary", None, "GPL-3.0"):
+            [True, ["Proprietary"]],
+        ("GPL-3.0 & GPL-2.0 & LGPL-2.1 | Proprietary", None, "GPL-3.0 Proprietary"):
+            [False, ["GPL-3.0"]]
+    }
+
+    def test_tests(self):
+        for args, expected in self.tests.items():
+            is_included, licenses = oe.license.is_included(
+                args[0], (args[1] or '').split(), (args[2] or '').split())
+            self.assertEqual(is_included, expected[0])
+            self.assertListEqual(licenses, expected[1])