1#!/usr/bin/env python3 2# 3# Copyright (C) 2021 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the 'License'); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an 'AS IS' BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18import argparse 19import glob 20import logging 21import os 22 23import utils 24 25LICENSE_KINDS_PREFIX = 'SPDX-license-identifier-' 26LICENSE_KEYWORDS = { 27 'Apache-2.0': ('Apache License', 'Version 2.0',), 28 'BSD': ('BSD ',), 29 'CC0-1.0': ('CC0 Public Domain Dedication license',), 30 'FTL': ('FreeType Project LICENSE',), 31 'ISC': ('Internet Systems Consortium',), 32 'ISC': ('ISC license',), 33 'MIT': (' MIT ',), 34 'MPL-2.0': ('Mozilla Public License Version 2.0',), 35 'MPL': ('Mozilla Public License',), 36 'NCSA': ('University of Illinois', 'NCSA',), 37 'OpenSSL': ('The OpenSSL Project',), 38 'Zlib': ('zlib License',), 39 'LGPL-3.0': ('LESSER GENERAL PUBLIC LICENSE', 'Version 3,',), 40 'LGPL-2.1': ('LESSER GENERAL PUBLIC LICENSE', 'Version 2.1',), 41 'LGPL-2.0': ('GNU LIBRARY GENERAL PUBLIC LICENSE', 'Version 2,',), 42 'LGPL': ('LESSER GENERAL PUBLIC LICENSE',), 43 'GPL-2.0': ('GNU GENERAL PUBLIC LICENSE', 'Version 2,',), 44 'GPL': ('GNU GENERAL PUBLIC LICENSE',), 45} 46 47LICENSE_INCLUDE = ['legacy_permissive', 'legacy_unencumbered'] 48 49class LicenseCollector(object): 50 """ Collect licenses from a VNDK snapshot directory 51 52 This is to collect the license_kinds to be used in license modules. 53 54 Initialize the LicenseCollector with a vndk snapshot directory. 55 After run() is called, 'license_kinds' will include the licenses found from 56 the snapshot directory. 57 """ 58 def __init__(self, install_dir): 59 self._install_dir = install_dir 60 self._paths_to_check = [os.path.join(install_dir, 61 utils.NOTICE_FILES_DIR_PATH),] 62 self._paths_to_check = self._paths_to_check + glob.glob(os.path.join(self._install_dir, '*/include')) 63 64 self.license_kinds = set() 65 66 def read_and_check_licenses(self, license_text, license_keywords): 67 """ Read the license keywords and check if all keywords are in the file. 68 69 The found licenses will be added to license_kinds set. This function will 70 return True if any licenses are found, False otherwise. 71 """ 72 found = False 73 for lic, patterns in license_keywords.items(): 74 for pattern in patterns: 75 if pattern not in license_text: 76 break 77 else: 78 self.license_kinds.add(LICENSE_KINDS_PREFIX + lic) 79 found = True 80 return found 81 82 def check_licenses(self, filepath): 83 """ Read a license text file and find the license_kinds. 84 """ 85 with open(filepath, 'rt', encoding='utf-8') as file_to_check: 86 try: 87 file_string = file_to_check.read() 88 self.read_and_check_licenses(file_string, LICENSE_KEYWORDS) 89 except UnicodeDecodeError: 90 # Read text files only. 91 return 92 93 def run(self, module=''): 94 """ search licenses in vndk snapshots 95 96 Args: 97 module: module name to find the license kind. 98 If empty, check all license files. 99 """ 100 if module == '': 101 for path in self._paths_to_check: 102 logging.info('Reading {}'.format(path)) 103 for (root, _, files) in os.walk(path): 104 for f in files: 105 self.check_licenses(os.path.join(root, f)) 106 self.license_kinds.update(LICENSE_INCLUDE) 107 else: 108 license_text_path = '{notice_dir}/{module}.txt'.format( 109 notice_dir=utils.NOTICE_FILES_DIR_NAME, 110 module=module) 111 logging.info('Reading {}'.format(license_text_path)) 112 self.check_licenses(os.path.join(self._install_dir, utils.COMMON_DIR_PATH, license_text_path)) 113 if not self.license_kinds: 114 # Add 'legacy_permissive' if no licenses are found for this file. 115 self.license_kinds.add('legacy_permissive') 116 117def get_args(): 118 parser = argparse.ArgumentParser() 119 parser.add_argument( 120 'vndk_version', 121 type=utils.vndk_version_int, 122 help='VNDK snapshot version to check, e.g. "{}".'.format( 123 utils.MINIMUM_VNDK_VERSION)) 124 parser.add_argument( 125 '-v', 126 '--verbose', 127 action='count', 128 default=0, 129 help='Increase output verbosity, e.g. "-v", "-vv".') 130 return parser.parse_args() 131 132def main(): 133 """ For the local testing purpose. 134 """ 135 ANDROID_BUILD_TOP = utils.get_android_build_top() 136 PREBUILTS_VNDK_DIR = utils.join_realpath(ANDROID_BUILD_TOP, 137 'prebuilts/vndk') 138 args = get_args() 139 vndk_version = args.vndk_version 140 install_dir = os.path.join(PREBUILTS_VNDK_DIR, 'v{}'.format(vndk_version)) 141 utils.set_logging_config(args.verbose) 142 143 license_collector = LicenseCollector(install_dir) 144 license_collector.run() 145 print(sorted(license_collector.license_kinds)) 146 147if __name__ == '__main__': 148 main() 149