1 # Copyright 2011 The Chromium Authors
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
7 PRESUBMIT_VERSION = '2.0.0'
9 ANDROID_ALLOWED_LICENSES = [
10 'A(pple )?PSL 2(\.0)?',
11 'Android Software Development Kit License',
12 'Apache( License)?,?( Version)? 2(\.0)?',
13 '(New )?([23]-Clause )?BSD( [23]-Clause)?( with advertising clause)?',
14 'GNU Lesser Public License',
15 'L?GPL ?v?2(\.[01])?( or later)?( with the classpath exception)?',
16 '(The )?MIT(/X11)?(-like)?( License)?',
17 'MPL 1\.1 ?/ ?GPL 2(\.0)? ?/ ?LGPL 2\.1',
19 'Microsoft Limited Public License',
20 'Microsoft Permissive License',
23 'SIL Open Font License, Version 1.1',
24 'SGI Free Software License B',
25 'Unicode, Inc. License',
26 'University of Illinois\/NCSA Open Source',
32 def LicenseIsCompatibleWithAndroid(input_api, license):
33 regex = '^(%s)$' % '|'.join(ANDROID_ALLOWED_LICENSES)
35 [x.strip() for x in input_api.re.split(' and |,', license) if len(x) > 0]
36 has_compatible_license = False
38 if input_api.re.match(regex, token, input_api.re.IGNORECASE):
39 has_compatible_license = True
41 return has_compatible_license
44 def CheckThirdPartyMetadataFiles(input_api, output_api):
45 """Checks that third party metadata files are correctly formatted
49 local_path = f.LocalPath()
51 # Limit to README.chromium files within //third_party/.
52 if (not local_path.endswith('README.chromium')
53 or not local_path.startswith('third_party' + input_api.os_path.sep)):
56 # Some folders are currently exempt from being checked.
58 ('third_party', 'blink'),
59 ('third_party', 'boringssl'),
60 ('third_party', 'closure_compiler', 'externs'),
61 ('third_party', 'closure_compiler', 'interfaces'),
62 ('third_party', 'feed_library'),
63 ('third_party', 'ipcz'),
64 # TODO(danakj): We should look for the README.chromium file in
65 # third_party/rust/CRATE_NAME/vVERSION/.
66 ('third_party', 'rust'),
67 ('third_party', 'webxr_test_pages'),
69 for path in skip_dirs:
70 prefix = ''.join([dir_name + input_api.os_path.sep for dir_name in path])
71 if local_path.startswith(prefix):
76 return input_api.canned_checks.CheckChromiumDependencyMetadata(
77 input_api, output_api, file_filter=readme_filter)
80 def CheckThirdPartyReadmesUpdated(input_api, output_api):
82 Checks to make sure that README.chromium files are properly updated
83 when dependencies in third_party are modified.
88 for f in input_api.AffectedFiles():
89 local_path = f.LocalPath()
90 if input_api.os_path.dirname(local_path) == 'third_party':
92 if (local_path.startswith('third_party' + input_api.os_path.sep) and
93 not local_path.startswith('third_party' + input_api.os_path.sep +
94 'blink' + input_api.os_path.sep) and
95 not local_path.startswith('third_party' + input_api.os_path.sep +
96 'boringssl' + input_api.os_path.sep) and
97 not local_path.startswith('third_party' + input_api.os_path.sep +
98 'closure_compiler' + input_api.os_path.sep +
99 'externs' + input_api.os_path.sep) and
100 not local_path.startswith('third_party' + input_api.os_path.sep +
101 'closure_compiler' + input_api.os_path.sep +
102 'interfaces' + input_api.os_path.sep) and
103 not local_path.startswith('third_party' + input_api.os_path.sep +
104 'feed_library' + input_api.os_path.sep) and
105 not local_path.startswith('third_party' + input_api.os_path.sep +
106 'ipcz' + input_api.os_path.sep) and
107 # TODO(danakj): We should look for the README.chromium file in
108 # third_party/rust/CRATE_NAME/vVERSION/.
109 not local_path.startswith('third_party' + input_api.os_path.sep +
110 'rust' + input_api.os_path.sep) and
111 not local_path.startswith('third_party' + input_api.os_path.sep +
112 'webxr_test_pages' + input_api.os_path.sep)):
114 if local_path.endswith("README.chromium"):
116 if files and not readmes:
117 errors.append(output_api.PresubmitPromptWarning(
118 'When updating or adding third party code the appropriate\n'
119 '\'README.chromium\' file should also be updated with the correct\n'
120 'version and package information.', files))
124 name_pattern = input_api.re.compile(
125 r'^Name: [a-zA-Z0-9_\-\. \(\)]+\r?$',
126 input_api.re.IGNORECASE | input_api.re.MULTILINE)
127 shortname_pattern = input_api.re.compile(
128 r'^Short Name: [a-zA-Z0-9_\-\.]+\r?$',
129 input_api.re.IGNORECASE | input_api.re.MULTILINE)
130 version_pattern = input_api.re.compile(
131 r'^Version: [a-zA-Z0-9_\-\+\.:/]+\r?$',
132 input_api.re.IGNORECASE | input_api.re.MULTILINE)
133 release_pattern = input_api.re.compile(
134 r'^Security Critical: (yes|no)\r?$',
135 input_api.re.IGNORECASE | input_api.re.MULTILINE)
136 license_pattern = input_api.re.compile(
137 r'^License: (.+)\r?$',
138 input_api.re.IGNORECASE | input_api.re.MULTILINE)
139 # Using NOT_SHIPPED in a License File field is deprecated.
140 # Please use the 'Shipped' field instead.
141 old_shipped_pattern = input_api.re.compile(
142 r'^License File: NOT_SHIPPED\r?$',
143 input_api.re.IGNORECASE | input_api.re.MULTILINE)
144 license_file_pattern = input_api.re.compile(
145 r'^License File: (.+)\r?$',
146 input_api.re.IGNORECASE | input_api.re.MULTILINE)
147 # Matches both 'Shipped' (preferred) and
148 # 'Shipped in Chromium' (for deps that are also standalone projects).
149 shipped_pattern = input_api.re.compile(
150 r'^Shipped(?: in Chromium)?: (yes|no)\r?$',
151 input_api.re.IGNORECASE | input_api.re.MULTILINE)
153 if 'D' in f.Action():
154 _IgnoreIfDeleting(input_api, output_api, f, errors)
157 # TODO(aredulla): Delete this rudimentary README.chromium checking
158 # once full metadata validation is enforced. The presubmit check
159 # above (CheckThirdPartyMetadataFiles) will return only warnings
160 # until all existing issues have been addressed. To prevent third
161 # party metadata degrading while the data quality is being uplifted,
162 # the below content checking for README.chromium files that return
163 # presubmit errors will be retained.
165 # Note: This may result in a presubmit error from below being
166 # repeated as a presubmit warning.
167 contents = input_api.ReadFile(f)
168 if (not shortname_pattern.search(contents)
169 and not name_pattern.search(contents)):
170 errors.append(output_api.PresubmitError(
171 'Third party README files should contain either a \'Short Name\' or\n'
172 'a \'Name\' which is the name under which the package is\n'
173 'distributed. Check README.chromium.template for details.',
175 if not version_pattern.search(contents):
176 errors.append(output_api.PresubmitError(
177 'Third party README files should contain a \'Version\' field.\n'
178 'If the package is not versioned, list the version as \'N/A\'\n'
179 'and provide at least the revision or package update date.\n'
180 'Check README.chromium.template for details.',
182 if not release_pattern.search(contents):
183 errors.append(output_api.PresubmitError(
184 'Third party README files should contain a \'Security Critical\'\n'
185 'field. This field specifies the security impact of vulnerabilities.\n'
186 'Check README.chromium.template for details.',
188 license_match = license_pattern.search(contents)
189 if not license_match:
190 errors.append(output_api.PresubmitError(
191 'Third party README files should contain a \'License\' field.\n'
192 'This field specifies the license used by the package. Check\n'
193 'README.chromium.template for details.',
196 shipped_match = shipped_pattern.search(contents)
197 # Default to Shipped if not specified. This will flag a license check or
198 # be overwritten if the README is still using the deprecated NOT_SHIPPED
200 is_shipped = (shipped_match is None) or ("yes" in shipped_match.group(1))
201 # Check for dependencies using the old NOT_SHIPPED pattern and issue a warning
202 deprecated_not_shipped = old_shipped_pattern.search(contents)
203 if deprecated_not_shipped:
205 license_file_match = license_file_pattern.search(contents)
206 if is_shipped and not license_file_match:
207 errors.append(output_api.PresubmitError(
208 'Packages marked as shipped must provide a path to a license file.\n'
209 'Check README.chromium.template for details.',
214 def _IgnoreIfDeleting(input_api, output_api, affected_file, errors):
215 third_party_dir = input_api.os_path.dirname(affected_file.LocalPath()) + \
217 for f in input_api.AffectedFiles():
218 if f.LocalPath().startswith(third_party_dir):
219 if 'D' not in f.Action():
220 errors.append(output_api.PresubmitError(
221 'Third party README should only be removed when the whole\n'
222 'directory is being removed.\n', [f, affected_file]))