Add MPL support to add3prf.py
This adds the ability to detect and generate the correct METADATA file for MPL licensed crates. Bug: 308477037 Test: atest --host add3prf_test; Ran against https://crates.io/crates/fdt Change-Id: I2b1765a08b68c5eb59e4f52b33c1df337e8c148b
This commit is contained in:
@@ -49,6 +49,8 @@ MIT_PATTERN = r"^.*MIT License.*$"
|
|||||||
MIT_MATCHER = re.compile(MIT_PATTERN)
|
MIT_MATCHER = re.compile(MIT_PATTERN)
|
||||||
BSD_PATTERN = r"^.*BSD .*License.*$"
|
BSD_PATTERN = r"^.*BSD .*License.*$"
|
||||||
BSD_MATCHER = re.compile(BSD_PATTERN)
|
BSD_MATCHER = re.compile(BSD_PATTERN)
|
||||||
|
MPL_PATTERN = r"^.Mozilla Public License.*$"
|
||||||
|
MPL_MATCHER = re.compile(MPL_PATTERN)
|
||||||
MULTI_LICENSE_COMMENT = ("# Dual-licensed, using the least restrictive "
|
MULTI_LICENSE_COMMENT = ("# Dual-licensed, using the least restrictive "
|
||||||
"per go/thirdpartylicenses#same.\n ")
|
"per go/thirdpartylicenses#same.\n ")
|
||||||
|
|
||||||
@@ -71,7 +73,7 @@ third_party {{
|
|||||||
value: "https://static.crates.io/crates/{name}/{name}-{version}.crate"
|
value: "https://static.crates.io/crates/{name}/{name}-{version}.crate"
|
||||||
}}
|
}}
|
||||||
version: "{version}"
|
version: "{version}"
|
||||||
{license_comment}license_type: NOTICE
|
{license_comment}license_type: {license_type}
|
||||||
last_upgrade_date {{
|
last_upgrade_date {{
|
||||||
year: {year}
|
year: {year}
|
||||||
month: {month}
|
month: {month}
|
||||||
@@ -108,7 +110,7 @@ def get_metadata_date():
|
|||||||
return today.year, today.month, today.day
|
return today.year, today.month, today.day
|
||||||
|
|
||||||
|
|
||||||
def add_metadata(name, version, description, multi_license):
|
def add_metadata(name, version, description, license_group, multi_license):
|
||||||
"""Update or add METADATA file."""
|
"""Update or add METADATA file."""
|
||||||
if os.path.exists("METADATA"):
|
if os.path.exists("METADATA"):
|
||||||
print("### Updating METADATA")
|
print("### Updating METADATA")
|
||||||
@@ -121,7 +123,7 @@ def add_metadata(name, version, description, multi_license):
|
|||||||
with open("METADATA", "w") as outf:
|
with open("METADATA", "w") as outf:
|
||||||
outf.write(METADATA_CONTENT.format(
|
outf.write(METADATA_CONTENT.format(
|
||||||
name=name, description=description, version=version,
|
name=name, description=description, version=version,
|
||||||
license_comment=license_comment, year=year, month=month, day=day))
|
license_comment=license_comment, license_type=license_group, year=year, month=month, day=day))
|
||||||
|
|
||||||
|
|
||||||
def grep_license_keyword(license_file):
|
def grep_license_keyword(license_file):
|
||||||
@@ -129,14 +131,16 @@ def grep_license_keyword(license_file):
|
|||||||
with open(license_file, "r") as input_file:
|
with open(license_file, "r") as input_file:
|
||||||
for line in input_file:
|
for line in input_file:
|
||||||
if APACHE_MATCHER.match(line):
|
if APACHE_MATCHER.match(line):
|
||||||
return License(LicenseType.APACHE2, license_file)
|
return License(LicenseType.APACHE2, LicenseGroup.NOTICE, license_file)
|
||||||
if MIT_MATCHER.match(line):
|
if MIT_MATCHER.match(line):
|
||||||
return License(LicenseType.MIT, license_file)
|
return License(LicenseType.MIT, LicenseGroup.NOTICE, license_file)
|
||||||
if BSD_MATCHER.match(line):
|
if BSD_MATCHER.match(line):
|
||||||
return License(LicenseType.BSD_LIKE, license_file)
|
return License(LicenseType.BSD_LIKE, LicenseGroup.NOTICE, license_file)
|
||||||
|
if MPL_MATCHER(LicenseType.MPL, license_file):
|
||||||
|
return License(LicenseType.MPL, LicenseGroup.RECIPROCAL, license_file)
|
||||||
print("ERROR: cannot decide license type in", license_file,
|
print("ERROR: cannot decide license type in", license_file,
|
||||||
"assume BSD_LIKE")
|
"assume BSD_LIKE")
|
||||||
return License(LicenseType.BSD_LIKE, license_file)
|
return License(LicenseType.BSD_LIKE, LicenseGroup.NOTICE, license_file)
|
||||||
|
|
||||||
|
|
||||||
class LicenseType(enum.IntEnum):
|
class LicenseType(enum.IntEnum):
|
||||||
@@ -150,9 +154,23 @@ class LicenseType(enum.IntEnum):
|
|||||||
MIT = 2
|
MIT = 2
|
||||||
BSD_LIKE = 3
|
BSD_LIKE = 3
|
||||||
ISC = 4
|
ISC = 4
|
||||||
|
MPL = 5
|
||||||
|
|
||||||
|
class LicenseGroup(enum.Enum):
|
||||||
|
"""A group of license as defined by go/thirdpartylicenses#types
|
||||||
|
|
||||||
|
Note, go/thirdpartylicenses#types calls them "types". But LicenseType was
|
||||||
|
already taken so this script calls them groups.
|
||||||
|
"""
|
||||||
|
RESTRICTED = 1
|
||||||
|
RESTRICTED_IF_STATICALLY_LINKED = 2
|
||||||
|
RECIPROCAL = 3
|
||||||
|
NOTICE = 4
|
||||||
|
PERMISSIVE = 5
|
||||||
|
BY_EXCEPTION_ONLY = 6
|
||||||
|
|
||||||
|
|
||||||
License = collections.namedtuple('License', ['type', 'filename'])
|
License = collections.namedtuple('License', ['type', 'group', 'filename'])
|
||||||
|
|
||||||
|
|
||||||
def decide_license_type(cargo_license):
|
def decide_license_type(cargo_license):
|
||||||
@@ -167,9 +185,9 @@ def decide_license_type(cargo_license):
|
|||||||
for license_file in glob.glob("LICENSE*") + glob.glob("COPYING*"):
|
for license_file in glob.glob("LICENSE*") + glob.glob("COPYING*"):
|
||||||
lowered_name = license_file.lower()
|
lowered_name = license_file.lower()
|
||||||
if lowered_name == "license-apache":
|
if lowered_name == "license-apache":
|
||||||
licenses.append(License(LicenseType.APACHE2, license_file))
|
licenses.append(License(LicenseType.APACHE2, LicenseGroup.NOTICE, license_file))
|
||||||
elif lowered_name == "license-mit":
|
elif lowered_name == "license-mit":
|
||||||
licenses.append(License(LicenseType.MIT, license_file))
|
licenses.append(License(LicenseType.MIT, LicenseGroup.NOTICE, license_file))
|
||||||
if licenses:
|
if licenses:
|
||||||
licenses.sort(key=lambda l: l.type)
|
licenses.sort(key=lambda l: l.type)
|
||||||
return licenses
|
return licenses
|
||||||
@@ -178,13 +196,15 @@ def decide_license_type(cargo_license):
|
|||||||
# There is a LICENSE* or COPYING* file, use cargo_license found in
|
# There is a LICENSE* or COPYING* file, use cargo_license found in
|
||||||
# Cargo.toml.
|
# Cargo.toml.
|
||||||
if "Apache" in cargo_license:
|
if "Apache" in cargo_license:
|
||||||
return [License(LicenseType.APACHE2, license_file)]
|
return [License(LicenseType.APACHE2, LicenseGroup.NOTICE, license_file)]
|
||||||
if "MIT" in cargo_license:
|
if "MIT" in cargo_license:
|
||||||
return [License(LicenseType.MIT, license_file)]
|
return [License(LicenseType.MIT, LicenseGroup.NOTICE, license_file)]
|
||||||
if "BSD" in cargo_license:
|
if "BSD" in cargo_license:
|
||||||
return [License(LicenseType.BSD_LIKE, license_file)]
|
return [License(LicenseType.BSD_LIKE, LicenseGroup.NOTICE, license_file)]
|
||||||
if "ISC" in cargo_license:
|
if "ISC" in cargo_license:
|
||||||
return [License(LicenseType.ISC, license_file)]
|
return [License(LicenseType.ISC, LicenseGroup.NOTICE, license_file)]
|
||||||
|
if "MPL" in cargo_license:
|
||||||
|
return [License(LicenseType.MPL, LicenseGroup.RECIPROCAL, license_file)]
|
||||||
return [grep_license_keyword(license_file)]
|
return [grep_license_keyword(license_file)]
|
||||||
|
|
||||||
|
|
||||||
@@ -223,7 +243,7 @@ def add_license(target):
|
|||||||
def add_module_license(license_type):
|
def add_module_license(license_type):
|
||||||
"""Touch MODULE_LICENSE_type file."""
|
"""Touch MODULE_LICENSE_type file."""
|
||||||
# Do not change existing MODULE_* files.
|
# Do not change existing MODULE_* files.
|
||||||
for suffix in ["MIT", "APACHE", "APACHE2", "BSD_LIKE"]:
|
for suffix in ["MIT", "APACHE", "APACHE2", "BSD_LIKE", "MPL"]:
|
||||||
module_file = "MODULE_LICENSE_" + suffix
|
module_file = "MODULE_LICENSE_" + suffix
|
||||||
if os.path.exists(module_file):
|
if os.path.exists(module_file):
|
||||||
if license_type.name != suffix:
|
if license_type.name != suffix:
|
||||||
@@ -309,7 +329,7 @@ def main():
|
|||||||
print("### Cargo.toml license:", cargo_license)
|
print("### Cargo.toml license:", cargo_license)
|
||||||
licenses = decide_license_type(cargo_license)
|
licenses = decide_license_type(cargo_license)
|
||||||
preferred_license = licenses[0]
|
preferred_license = licenses[0]
|
||||||
add_metadata(name, version, description, len(licenses) > 1)
|
add_metadata(name, version, description, preferred_license.group.name, len(licenses) > 1)
|
||||||
add_owners()
|
add_owners()
|
||||||
add_license(preferred_license.filename)
|
add_license(preferred_license.filename)
|
||||||
add_module_license(preferred_license.type)
|
add_module_license(preferred_license.type)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class LicenseDetectionTestCase(fake_filesystem_unittest.TestCase):
|
|||||||
self.assertEqual(len(licenses), 2)
|
self.assertEqual(len(licenses), 2)
|
||||||
preferred_license = licenses[0]
|
preferred_license = licenses[0]
|
||||||
self.assertEqual(preferred_license.type, add3prf.LicenseType.APACHE2)
|
self.assertEqual(preferred_license.type, add3prf.LicenseType.APACHE2)
|
||||||
|
self.assertEqual(preferred_license.group, add3prf.LicenseGroup.NOTICE)
|
||||||
self.assertEqual(preferred_license.filename, "LICENSE-APACHE")
|
self.assertEqual(preferred_license.filename, "LICENSE-APACHE")
|
||||||
|
|
||||||
def test_mit_license(self):
|
def test_mit_license(self):
|
||||||
@@ -38,6 +39,7 @@ class LicenseDetectionTestCase(fake_filesystem_unittest.TestCase):
|
|||||||
self.assertEqual(len(licenses), 1)
|
self.assertEqual(len(licenses), 1)
|
||||||
preferred_license = licenses[0]
|
preferred_license = licenses[0]
|
||||||
self.assertEqual(preferred_license.type, add3prf.LicenseType.MIT)
|
self.assertEqual(preferred_license.type, add3prf.LicenseType.MIT)
|
||||||
|
self.assertEqual(preferred_license.group, add3prf.LicenseGroup.NOTICE)
|
||||||
self.assertEqual(preferred_license.filename, "LICENSE")
|
self.assertEqual(preferred_license.filename, "LICENSE")
|
||||||
|
|
||||||
def test_misc_license(self):
|
def test_misc_license(self):
|
||||||
@@ -46,12 +48,22 @@ class LicenseDetectionTestCase(fake_filesystem_unittest.TestCase):
|
|||||||
self.assertEqual(len(licenses), 1)
|
self.assertEqual(len(licenses), 1)
|
||||||
preferred_license = licenses[0]
|
preferred_license = licenses[0]
|
||||||
self.assertEqual(preferred_license.type, add3prf.LicenseType.BSD_LIKE)
|
self.assertEqual(preferred_license.type, add3prf.LicenseType.BSD_LIKE)
|
||||||
|
self.assertEqual(preferred_license.group, add3prf.LicenseGroup.NOTICE)
|
||||||
self.assertEqual(preferred_license.filename, "LICENSE.txt")
|
self.assertEqual(preferred_license.filename, "LICENSE.txt")
|
||||||
|
|
||||||
def test_missing_license_file(self):
|
def test_missing_license_file(self):
|
||||||
with self.assertRaises(FileNotFoundError):
|
with self.assertRaises(FileNotFoundError):
|
||||||
add3prf.decide_license_type("MIT OR Apache-2.0")
|
add3prf.decide_license_type("MIT OR Apache-2.0")
|
||||||
|
|
||||||
|
def test_mpl_license(self):
|
||||||
|
self.fs.create_file("LICENSE")
|
||||||
|
licenses = add3prf.decide_license_type("MPL")
|
||||||
|
self.assertEqual(len(licenses), 1)
|
||||||
|
preferred_license = licenses[0]
|
||||||
|
self.assertEqual(preferred_license.type, add3prf.LicenseType.MPL)
|
||||||
|
self.assertEqual(preferred_license.group, add3prf.LicenseGroup.RECIPROCAL)
|
||||||
|
self.assertEqual(preferred_license.filename, "LICENSE")
|
||||||
|
|
||||||
|
|
||||||
class AddModuleLicenseTestCase(fake_filesystem_unittest.TestCase):
|
class AddModuleLicenseTestCase(fake_filesystem_unittest.TestCase):
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user