1 # Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 ANDROID_WHITELISTED_LICENSES = [ 6 'A(pple )?PSL 2(\.0)?', 7 'Apache( Version)? 2(\.0)?', 8 '(New )?([23]-Clause )?BSD( [23]-Clause)?( with advertising clause)?', 9 'L?GPL ?v?2(\.[01])?( or later)?', 10 'MIT(/X11)?(-like)?', 11 'MPL 1\.1 ?/ ?GPL 2(\.0)? ?/ ?LGPL 2\.1', 12 'MPL 2(\.0)?', 13 'Microsoft Limited Public License', 14 'Microsoft Permissive License', 15 'Public Domain', 16 'Python', 17 'SGI Free Software License B', 18 'University of Illinois\/NCSA Open Source', 19 'X11', 20 ] 21 22 def LicenseIsCompatibleWithAndroid(input_api, license): 23 regex = '^(%s)$' % '|'.join(ANDROID_WHITELISTED_LICENSES) 24 tokens = \ 25 [x.strip() for x in input_api.re.split(' and |,', license) if len(x) > 0] 26 has_compatible_license = False 27 for token in tokens: 28 if input_api.re.match(regex, token, input_api.re.IGNORECASE): 29 has_compatible_license = True 30 break 31 return has_compatible_license 32 33 def _CheckThirdPartyReadmesUpdated(input_api, output_api): 34 """ 35 Checks to make sure that README.chromium files are properly updated 36 when dependancies in third_party are modified. 37 """ 38 readmes = [] 39 files = [] 40 errors = [] 41 for f in input_api.AffectedFiles(): 42 local_path = f.LocalPath() 43 if input_api.os_path.dirname(local_path) == 'third_party': 44 continue 45 if local_path.startswith('third_party' + input_api.os_path.sep): 46 files.append(f) 47 if local_path.endswith("README.chromium"): 48 readmes.append(f) 49 if files and not readmes: 50 errors.append(output_api.PresubmitPromptWarning( 51 'When updating or adding third party code the appropriate\n' 52 '\'README.chromium\' file should also be updated with the correct\n' 53 'version and package information.', files)) 54 if not readmes: 55 return errors 56 57 name_pattern = input_api.re.compile( 58 r'^Name: [a-zA-Z0-9_\-\. \(\)]+\r?$', 59 input_api.re.IGNORECASE | input_api.re.MULTILINE) 60 shortname_pattern = input_api.re.compile( 61 r'^Short Name: [a-zA-Z0-9_\-\.]+\r?$', 62 input_api.re.IGNORECASE | input_api.re.MULTILINE) 63 version_pattern = input_api.re.compile( 64 r'^Version: [a-zA-Z0-9_\-\.:]+\r?$', 65 input_api.re.IGNORECASE | input_api.re.MULTILINE) 66 release_pattern = input_api.re.compile( 67 r'^Security Critical: (yes|no)\r?$', 68 input_api.re.IGNORECASE | input_api.re.MULTILINE) 69 license_pattern = input_api.re.compile( 70 r'^License: (.+)\r?$', 71 input_api.re.IGNORECASE | input_api.re.MULTILINE) 72 license_android_compatible_pattern = input_api.re.compile( 73 r'^License Android Compatible: (yes|no)\r?$', 74 input_api.re.IGNORECASE | input_api.re.MULTILINE) 75 76 for f in readmes: 77 if 'D' in f.Action(): 78 _IgnoreIfDeleting(input_api, output_api, f, errors) 79 continue 80 81 contents = input_api.ReadFile(f) 82 if (not shortname_pattern.search(contents) 83 and not name_pattern.search(contents)): 84 errors.append(output_api.PresubmitError( 85 'Third party README files should contain either a \'Short Name\' or\n' 86 'a \'Name\' which is the name under which the package is\n' 87 'distributed. Check README.chromium.template for details.', 88 [f])) 89 if not version_pattern.search(contents): 90 errors.append(output_api.PresubmitError( 91 'Third party README files should contain a \'Version\' field.\n' 92 'If the package is not versioned or the version is not known\n' 93 'list the version as \'unknown\'.\n' 94 'Check README.chromium.template for details.', 95 [f])) 96 if not release_pattern.search(contents): 97 errors.append(output_api.PresubmitError( 98 'Third party README files should contain a \'Security Critical\'\n' 99 'field. This field specifies whether the package is built with\n' 100 'Chromium. Check README.chromium.template for details.', 101 [f])) 102 license_match = license_pattern.search(contents) 103 if not license_match: 104 errors.append(output_api.PresubmitError( 105 'Third party README files should contain a \'License\' field.\n' 106 'This field specifies the license used by the package. Check\n' 107 'README.chromium.template for details.', 108 [f])) 109 elif not LicenseIsCompatibleWithAndroid(input_api, license_match.group(1)) \ 110 and not license_android_compatible_pattern.search(contents): 111 errors.append(output_api.PresubmitPromptWarning( 112 'Cannot determine whether specified license is compatible with\n' + 113 'the Android licensing requirements. Please check that the license\n' + 114 'name is spelled according to third_party/PRESUBMIT.py. Please see\n' + 115 'README.chromium.template for details.', 116 [f])) 117 return errors 118 119 120 def _IgnoreIfDeleting(input_api, output_api, affected_file, errors): 121 third_party_dir = input_api.os_path.dirname(affected_file.LocalPath()) 122 for f in input_api.AffectedFiles(): 123 if f.LocalPath().startswith(third_party_dir): 124 if 'D' not in f.Action(): 125 errors.append(output_api.PresubmitError( 126 'Third party README should only be removed when the whole\n' 127 'directory is being removed.\n', [f, affected_file])) 128 129 130 def CheckChangeOnUpload(input_api, output_api): 131 results = [] 132 results.extend(_CheckThirdPartyReadmesUpdated(input_api, output_api)) 133 return results 134