1#!/usr/bin/env python3 2# 3# Copyright (C) 2024 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"""Check if the given files in a given commit has an AOSP license.""" 18 19import argparse 20import os 21import re 22import sys 23 24_path = os.path.realpath(__file__ + '/../..') 25if sys.path[0] != _path: 26 sys.path.insert(0, _path) 27del _path 28 29# We have to import our local modules after the sys.path tweak. We can't use 30# relative imports because this is an executable program, not a module. 31# pylint: disable=import-error,wrong-import-position 32import rh.git 33 34 35# AOSP uses the Apache2 License: https://source.android.com/source/licenses.html 36# Spaces and comment identifiers in different languages are allowed at the 37# beginning of each line. 38AOSP_LICENSE_HEADER = ( 39 r"""[ #/\*]*Copyright \(C\) 20\d\d The Android Open Source Project 40[ #/\*]*\n?[ #/\*]*Licensed under the Apache License, Version 2.0 """ 41 r"""\(the "License"\); 42[ #/\*]*you may not use this file except in compliance with the License\. 43[ #/\*]*You may obtain a copy of the License at 44[ #/\*]* 45[ #/\*]*http://www\.apache\.org/licenses/LICENSE-2\.0 46[ #/\*]* 47[ #/\*]*Unless required by applicable law or agreed to in writing, software 48[ #/\*]*distributed under the License is distributed on an "AS IS" BASIS, 49[ #/\*]*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or """ 50 r"""implied\. 51[ #/\*]*See the License for the specific language governing permissions and 52[ #/\*]*limitations under the License\. 53""" 54) 55 56 57license_re = re.compile(AOSP_LICENSE_HEADER, re.MULTILINE) 58 59 60AOSP_LICENSE_SUBSTR = 'Licensed under the Apache License' 61 62 63def check_license(contents: str) -> bool: 64 """Verifies the AOSP license/copyright header.""" 65 return license_re.search(contents) is not None 66 67 68def get_parser() -> argparse.ArgumentParser: 69 parser = argparse.ArgumentParser( 70 description=( 71 'Check if the given files in a given commit has an AOSP license.' 72 ) 73 ) 74 parser.add_argument( 75 'file_paths', 76 nargs='+', 77 help='The file paths to check.', 78 ) 79 parser.add_argument( 80 '--commit_hash', 81 '-c', 82 help='The commit hash to check.', 83 # TODO(b/370907797): Read the contents on the file system by default 84 # instead. 85 default='HEAD', 86 ) 87 return parser 88 89 90def main(argv: list[str]): 91 """The main entry.""" 92 parser = get_parser() 93 args = parser.parse_args(argv) 94 commit_hash = args.commit_hash 95 file_paths = args.file_paths 96 97 all_passed = True 98 for file_path in file_paths: 99 contents = rh.git.get_file_content(commit_hash, file_path) 100 if not check_license(contents): 101 has_pattern = contents.find(AOSP_LICENSE_SUBSTR) != -1 102 if has_pattern: 103 print(f'Malformed AOSP license in {file_path}') 104 else: 105 print(f'Missing AOSP license in {file_path}') 106 all_passed = False 107 if not all_passed: 108 return 1 109 return 0 110 111 112if __name__ == '__main__': 113 sys.exit(main(sys.argv[1:])) 114