license.py: Correct selection of licenses in is_included()

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().

(From OE-Core rev: 312b4d6175e189852c0787ca2fe99b99ce92d1bd)

Signed-off-by: Peter Kjellerstedt <peter.kjellerstedt@axis.com>
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
Peter Kjellerstedt 2017-09-29 17:52:34 +02:00 committed by Richard Purdie
parent 03839a7ffa
commit 16f1a14f83
2 changed files with 44 additions and 5 deletions

View 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

View 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])