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