diff --git a/vndk/snapshot/collect_licenses.py b/vndk/snapshot/collect_licenses.py new file mode 100644 index 000000000..b5cc17d35 --- /dev/null +++ b/vndk/snapshot/collect_licenses.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import argparse +import glob +import logging +import os + +import utils + +LICENSE_KINDS_PREFIX = 'SPDX-license-identifier-' +LICENSE_KEYWORDS = { + 'Apache-2.0': ('Apache License', 'Version 2.0',), + 'BSD': ('BSD ',), + 'CC0-1.0': ('CC0 Public Domain Dedication license',), + 'FTL': ('FreeType Project LICENSE',), + 'ISC': ('Internet Systems Consortium',), + 'ISC': ('ISC license',), + 'MIT': (' MIT ',), + 'MPL-2.0': ('Mozilla Public License Version 2.0',), + 'MPL': ('Mozilla Public License',), + 'NCSA': ('University of Illinois', 'NCSA',), + 'OpenSSL': ('The OpenSSL Project',), + 'Zlib': ('zlib License',), +} +RESTRICTED_LICENSE_KEYWORDS = { + 'LGPL-3.0': ('LESSER GENERAL PUBLIC LICENSE', 'Version 3,',), + 'LGPL-2.1': ('LESSER GENERAL PUBLIC LICENSE', 'Version 2.1',), + 'LGPL-2.0': ('GNU LIBRARY GENERAL PUBLIC LICENSE', 'Version 2,',), + 'LGPL': ('LESSER GENERAL PUBLIC LICENSE',), + 'GPL-2.0': ('GNU GENERAL PUBLIC LICENSE', 'Version 2,',), + 'GPL': ('GNU GENERAL PUBLIC LICENSE',), +} + +LICENSE_INCLUDE = ['legacy_permissive', 'legacy_unencumbered'] + +class LicenseCollector(object): + """ Collect licenses from a VNDK snapshot directory + + This is to collect the license_kinds to be used in license modules. + It also lists the modules with the restricted licenses. + + Initialize the LicenseCollector with a vndk snapshot directory. + After run() is called, 'license_kinds' will include the licenses found from + the snapshot directory. + 'restricted' will have the files that have the restricted licenses. + """ + def __init__(self, install_dir): + self._install_dir = install_dir + self._paths_to_check = [os.path.join(install_dir, + utils.NOTICE_FILES_DIR_PATH),] + self._paths_to_check = self._paths_to_check + glob.glob(os.path.join(self._install_dir, '*/include')) + + self.license_kinds = set() + self.restricted = set() + + def read_and_check_licenses(self, license_text, license_keywords): + """ Read the license keywords and check if all keywords are in the file. + + The found licenses will be added to license_kinds set. This function will + return True if any licenses are found, False otherwise. + """ + found = False + for lic, patterns in license_keywords.items(): + for pattern in patterns: + if pattern not in license_text: + break + else: + self.license_kinds.add(LICENSE_KINDS_PREFIX + lic) + found = True + return found + + def read_and_check_dir_for_licenses(self, path): + """ Check licenses for all files under the directory + """ + for (root, _, files) in os.walk(path): + for f in files: + with open(os.path.join(root, f), 'r') as file_to_check: + file_string = file_to_check.read() + self.read_and_check_licenses(file_string, LICENSE_KEYWORDS) + if self.read_and_check_licenses(file_string, RESTRICTED_LICENSE_KEYWORDS): + self.restricted.add(f) + + def run(self): + """ search licenses in vndk snapshots + """ + for path in self._paths_to_check: + logging.info('Reading {}'.format(path)) + self.read_and_check_dir_for_licenses(path) + self.license_kinds.update(LICENSE_INCLUDE) + +def get_args(): + parser = argparse.ArgumentParser() + parser.add_argument( + 'vndk_version', + type=utils.vndk_version_int, + help='VNDK snapshot version to check, e.g. "{}".'.format( + utils.MINIMUM_VNDK_VERSION)) + parser.add_argument( + '-v', + '--verbose', + action='count', + default=0, + help='Increase output verbosity, e.g. "-v", "-vv".') + return parser.parse_args() + +def main(): + """ For the local testing purpose. + """ + ANDROID_BUILD_TOP = utils.get_android_build_top() + PREBUILTS_VNDK_DIR = utils.join_realpath(ANDROID_BUILD_TOP, + 'prebuilts/vndk') + args = get_args() + vndk_version = args.vndk_version + install_dir = os.path.join(PREBUILTS_VNDK_DIR, 'v{}'.format(vndk_version)) + utils.set_logging_config(args.verbose) + + license_collector = LicenseCollector(install_dir) + license_collector.run() + print(sorted(license_collector.license_kinds)) + print(sorted(license_collector.restricted)) + +if __name__ == '__main__': + main() diff --git a/vndk/snapshot/gen_buildfiles.py b/vndk/snapshot/gen_buildfiles.py index d6afa2e93..dcb8b3fd9 100644 --- a/vndk/snapshot/gen_buildfiles.py +++ b/vndk/snapshot/gen_buildfiles.py @@ -22,6 +22,7 @@ import logging import os import sys +import collect_licenses import utils @@ -300,18 +301,33 @@ class GenBuildFile(object): ind=self.INDENT, version=self._vndk_version)) + def _get_license_kinds(self): + """ Returns a set of license kinds """ + license_collector = collect_licenses.LicenseCollector(self._install_dir) + license_collector.run() + return license_collector.license_kinds + def _gen_license(self): """ Generates license module. It uses license files for all VNDK snapshot libraries in common/NOTICE_FILES directory. """ + license_kinds = self._get_license_kinds() + license_kinds_string = '' + for license_kind in sorted(license_kinds): + license_kinds_string += '{ind}{ind}"{license_kind}",\n'.format( + ind=self.INDENT, license_kind=license_kind) return ('license {{\n' '{ind}name: "prebuilts_vndk_v{version}_license",\n' '{ind}visibility: [":__subpackages__"],\n' + '{ind}license_kinds: [\n' + '{license_kinds}' + '{ind}],\n' '{ind}license_text: ["{notice_files}"],\n' '}}\n'.format( ind=self.INDENT, version=self._vndk_version, + license_kinds=license_kinds_string, notice_files=os.path.join(utils.NOTICE_FILES_DIR_PATH, '*.txt'))) def _get_versioned_name(self,