blob: 65b0e438d0a69ba538fbcdcfa268748a4f2cca2f [file] [log] [blame]
[email protected]a18130a2012-01-03 17:52:081# Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]ca8d1982009-02-19 16:33:122# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Top-level presubmit script for Chromium.
6
[email protected]f1293792009-07-31 18:09:567See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
tfarina78bb92f42015-01-31 00:20:488for more details about the presubmit API built into depot_tools.
[email protected]ca8d1982009-02-19 16:33:129"""
10
[email protected]eea609a2011-11-18 13:10:1211
[email protected]379e7dd2010-01-28 17:39:2112_EXCLUDED_PATHS = (
Egor Paskoce145c42018-09-28 19:31:0413 r"^native_client_sdk[\\/]src[\\/]build_tools[\\/]make_rules.py",
14 r"^native_client_sdk[\\/]src[\\/]build_tools[\\/]make_simple.py",
15 r"^native_client_sdk[\\/]src[\\/]tools[\\/].*.mk",
16 r"^net[\\/]tools[\\/]spdyshark[\\/].*",
17 r"^skia[\\/].*",
Kent Tamura32dbbcb2018-11-30 12:28:4918 r"^third_party[\\/]blink[\\/].*",
Egor Paskoce145c42018-09-28 19:31:0419 r"^third_party[\\/]breakpad[\\/].*",
20 r"^v8[\\/].*",
[email protected]3e4eb112011-01-18 03:29:5421 r".*MakeFile$",
[email protected]1084ccc2012-03-14 03:22:5322 r".+_autogen\.h$",
Egor Paskoce145c42018-09-28 19:31:0423 r".+[\\/]pnacl_shim\.c$",
24 r"^gpu[\\/]config[\\/].*_list_json\.cc$",
25 r"^chrome[\\/]browser[\\/]resources[\\/]pdf[\\/]index.js",
26 r"tools[\\/]md_browser[\\/].*\.css$",
Kenneth Russell077c8d92017-12-16 02:52:1427 # Test pages for Maps telemetry tests.
Egor Paskoce145c42018-09-28 19:31:0428 r"tools[\\/]perf[\\/]page_sets[\\/]maps_perf_test.*",
ehmaldonado78eee2ed2017-03-28 13:16:5429 # Test pages for WebRTC telemetry tests.
Egor Paskoce145c42018-09-28 19:31:0430 r"tools[\\/]perf[\\/]page_sets[\\/]webrtc_cases.*",
[email protected]4306417642009-06-11 00:33:4031)
[email protected]ca8d1982009-02-19 16:33:1232
wnwenbdc444e2016-05-25 13:44:1533
[email protected]06e6d0ff2012-12-11 01:36:4434# Fragment of a regular expression that matches C++ and Objective-C++
35# implementation files.
36_IMPLEMENTATION_EXTENSIONS = r'\.(cc|cpp|cxx|mm)$'
37
wnwenbdc444e2016-05-25 13:44:1538
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:1939# Fragment of a regular expression that matches C++ and Objective-C++
40# header files.
41_HEADER_EXTENSIONS = r'\.(h|hpp|hxx)$'
42
43
[email protected]06e6d0ff2012-12-11 01:36:4444# Regular expression that matches code only used for test binaries
45# (best effort).
46_TEST_CODE_EXCLUDED_PATHS = (
Egor Paskoce145c42018-09-28 19:31:0447 r'.*[\\/](fake_|test_|mock_).+%s' % _IMPLEMENTATION_EXTENSIONS,
[email protected]06e6d0ff2012-12-11 01:36:4448 r'.+_test_(base|support|util)%s' % _IMPLEMENTATION_EXTENSIONS,
Steven Holte27008b7422018-01-29 20:55:4449 r'.+_(api|browser|eg|int|perf|pixel|unit|ui)?test(_[a-z]+)?%s' %
[email protected]e2d7e6f2013-04-23 12:57:1250 _IMPLEMENTATION_EXTENSIONS,
[email protected]06e6d0ff2012-12-11 01:36:4451 r'.+profile_sync_service_harness%s' % _IMPLEMENTATION_EXTENSIONS,
Egor Paskoce145c42018-09-28 19:31:0452 r'.*[\\/](test|tool(s)?)[\\/].*',
[email protected]ef070cc2013-05-03 11:53:0553 # content_shell is used for running layout tests.
Egor Paskoce145c42018-09-28 19:31:0454 r'content[\\/]shell[\\/].*',
[email protected]7b054982013-11-27 00:44:4755 # Non-production example code.
Egor Paskoce145c42018-09-28 19:31:0456 r'mojo[\\/]examples[\\/].*',
[email protected]8176de12014-06-20 19:07:0857 # Launcher for running iOS tests on the simulator.
Egor Paskoce145c42018-09-28 19:31:0458 r'testing[\\/]iossim[\\/]iossim\.mm$',
[email protected]06e6d0ff2012-12-11 01:36:4459)
[email protected]ca8d1982009-02-19 16:33:1260
wnwenbdc444e2016-05-25 13:44:1561
[email protected]eea609a2011-11-18 13:10:1262_TEST_ONLY_WARNING = (
63 'You might be calling functions intended only for testing from\n'
64 'production code. It is OK to ignore this warning if you know what\n'
65 'you are doing, as the heuristics used to detect the situation are\n'
[email protected]b0149772014-03-27 16:47:5866 'not perfect. The commit queue will not block on this warning.')
[email protected]eea609a2011-11-18 13:10:1267
68
[email protected]cf9b78f2012-11-14 11:40:2869_INCLUDE_ORDER_WARNING = (
marjaa017dc482015-03-09 17:13:4070 'Your #include order seems to be broken. Remember to use the right '
avice9a8982015-11-24 20:36:2171 'collation (LC_COLLATE=C) and check\nhttps://google.github.io/styleguide/'
72 'cppguide.html#Names_and_Order_of_Includes')
[email protected]cf9b78f2012-11-14 11:40:2873
wnwenbdc444e2016-05-25 13:44:1574
Eric Stevensona9a980972017-09-23 00:04:4175_BANNED_JAVA_FUNCTIONS = (
76 (
77 'StrictMode.allowThreadDiskReads()',
78 (
79 'Prefer using StrictModeContext.allowDiskReads() to using StrictMode '
80 'directly.',
81 ),
82 False,
83 ),
84 (
85 'StrictMode.allowThreadDiskWrites()',
86 (
87 'Prefer using StrictModeContext.allowDiskWrites() to using StrictMode '
88 'directly.',
89 ),
90 False,
91 ),
92)
93
[email protected]127f18ec2012-06-16 05:05:5994_BANNED_OBJC_FUNCTIONS = (
95 (
96 'addTrackingRect:',
[email protected]23e6cbc2012-06-16 18:51:2097 (
98 'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
[email protected]127f18ec2012-06-16 05:05:5999 'prohibited. Please use CrTrackingArea instead.',
100 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
101 ),
102 False,
103 ),
104 (
[email protected]eaae1972014-04-16 04:17:26105 r'/NSTrackingArea\W',
[email protected]23e6cbc2012-06-16 18:51:20106 (
107 'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
[email protected]127f18ec2012-06-16 05:05:59108 'instead.',
109 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
110 ),
111 False,
112 ),
113 (
114 'convertPointFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20115 (
116 'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59117 'Please use |convertPoint:(point) fromView:nil| instead.',
118 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
119 ),
120 True,
121 ),
122 (
123 'convertPointToBase:',
[email protected]23e6cbc2012-06-16 18:51:20124 (
125 'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59126 'Please use |convertPoint:(point) toView:nil| instead.',
127 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
128 ),
129 True,
130 ),
131 (
132 'convertRectFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20133 (
134 'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59135 'Please use |convertRect:(point) fromView:nil| instead.',
136 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
137 ),
138 True,
139 ),
140 (
141 'convertRectToBase:',
[email protected]23e6cbc2012-06-16 18:51:20142 (
143 'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59144 'Please use |convertRect:(point) toView:nil| instead.',
145 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
146 ),
147 True,
148 ),
149 (
150 'convertSizeFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20151 (
152 'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59153 'Please use |convertSize:(point) fromView:nil| instead.',
154 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
155 ),
156 True,
157 ),
158 (
159 'convertSizeToBase:',
[email protected]23e6cbc2012-06-16 18:51:20160 (
161 'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59162 'Please use |convertSize:(point) toView:nil| instead.',
163 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
164 ),
165 True,
166 ),
jif65398702016-10-27 10:19:48167 (
168 r"/\s+UTF8String\s*]",
169 (
170 'The use of -[NSString UTF8String] is dangerous as it can return null',
171 'even if |canBeConvertedToEncoding:NSUTF8StringEncoding| returns YES.',
172 'Please use |SysNSStringToUTF8| instead.',
173 ),
174 True,
175 ),
Sylvain Defresne4cf1d182017-09-18 14:16:34176 (
177 r'__unsafe_unretained',
178 (
179 'The use of __unsafe_unretained is almost certainly wrong, unless',
180 'when interacting with NSFastEnumeration or NSInvocation.',
181 'Please use __weak in files build with ARC, nothing otherwise.',
182 ),
183 False,
184 ),
[email protected]127f18ec2012-06-16 05:05:59185)
186
Sylvain Defresnea8b73d252018-02-28 15:45:54187_BANNED_IOS_OBJC_FUNCTIONS = (
188 (
189 r'/\bTEST[(]',
190 (
191 'TEST() macro should not be used in Objective-C++ code as it does not ',
192 'drain the autorelease pool at the end of the test. Use TEST_F() ',
193 'macro instead with a fixture inheriting from PlatformTest (or a ',
194 'typedef).'
195 ),
196 True,
197 ),
198 (
199 r'/\btesting::Test\b',
200 (
201 'testing::Test should not be used in Objective-C++ code as it does ',
202 'not drain the autorelease pool at the end of the test. Use ',
203 'PlatformTest instead.'
204 ),
205 True,
206 ),
207)
208
[email protected]127f18ec2012-06-16 05:05:59209
210_BANNED_CPP_FUNCTIONS = (
[email protected]23e6cbc2012-06-16 18:51:20211 # Make sure that gtest's FRIEND_TEST() macro is not used; the
212 # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
[email protected]e00ccc92012-11-01 17:32:30213 # used instead since that allows for FLAKY_ and DISABLED_ prefixes.
[email protected]23e6cbc2012-06-16 18:51:20214 (
thomasandersone7caaa9b2017-03-29 19:22:53215 r'\bNULL\b',
216 (
217 'New code should not use NULL. Use nullptr instead.',
218 ),
219 True,
220 (),
221 ),
222 (
[email protected]23e6cbc2012-06-16 18:51:20223 'FRIEND_TEST(',
224 (
[email protected]e3c945502012-06-26 20:01:49225 'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
[email protected]23e6cbc2012-06-16 18:51:20226 'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
227 ),
228 False,
[email protected]7345da02012-11-27 14:31:49229 (),
[email protected]23e6cbc2012-06-16 18:51:20230 ),
231 (
thomasanderson4b569052016-09-14 20:15:53232 r'XSelectInput|CWEventMask|XCB_CW_EVENT_MASK',
233 (
234 'Chrome clients wishing to select events on X windows should use',
235 'ui::XScopedEventSelector. It is safe to ignore this warning only if',
236 'you are selecting events from the GPU process, or if you are using',
237 'an XDisplay other than gfx::GetXDisplay().',
238 ),
239 True,
240 (
Egor Paskoce145c42018-09-28 19:31:04241 r"^ui[\\/]gl[\\/].*\.cc$",
242 r"^media[\\/]gpu[\\/].*\.cc$",
243 r"^gpu[\\/].*\.cc$",
thomasanderson4b569052016-09-14 20:15:53244 ),
245 ),
246 (
thomasandersone043e3ce2017-06-08 00:43:20247 r'XInternAtom|xcb_intern_atom',
248 (
thomasanderson11aa41d2017-06-08 22:22:38249 'Use gfx::GetAtom() instead of interning atoms directly.',
thomasandersone043e3ce2017-06-08 00:43:20250 ),
251 True,
252 (
Egor Paskoce145c42018-09-28 19:31:04253 r"^gpu[\\/]ipc[\\/]service[\\/]gpu_watchdog_thread\.cc$",
254 r"^remoting[\\/]host[\\/]linux[\\/]x_server_clipboard\.cc$",
255 r"^ui[\\/]gfx[\\/]x[\\/]x11_atom_cache\.cc$",
thomasandersone043e3ce2017-06-08 00:43:20256 ),
257 ),
258 (
tomhudsone2c14d552016-05-26 17:07:46259 'setMatrixClip',
260 (
261 'Overriding setMatrixClip() is prohibited; ',
262 'the base function is deprecated. ',
263 ),
264 True,
265 (),
266 ),
267 (
[email protected]52657f62013-05-20 05:30:31268 'SkRefPtr',
269 (
270 'The use of SkRefPtr is prohibited. ',
tomhudson7e6e0512016-04-19 19:27:22271 'Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31272 ),
273 True,
274 (),
275 ),
276 (
277 'SkAutoRef',
278 (
279 'The indirect use of SkRefPtr via SkAutoRef is prohibited. ',
tomhudson7e6e0512016-04-19 19:27:22280 'Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31281 ),
282 True,
283 (),
284 ),
285 (
286 'SkAutoTUnref',
287 (
288 'The use of SkAutoTUnref is dangerous because it implicitly ',
tomhudson7e6e0512016-04-19 19:27:22289 'converts to a raw pointer. Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31290 ),
291 True,
292 (),
293 ),
294 (
295 'SkAutoUnref',
296 (
297 'The indirect use of SkAutoTUnref through SkAutoUnref is dangerous ',
298 'because it implicitly converts to a raw pointer. ',
tomhudson7e6e0512016-04-19 19:27:22299 'Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31300 ),
301 True,
302 (),
303 ),
[email protected]d89eec82013-12-03 14:10:59304 (
305 r'/HANDLE_EINTR\(.*close',
306 (
307 'HANDLE_EINTR(close) is invalid. If close fails with EINTR, the file',
308 'descriptor will be closed, and it is incorrect to retry the close.',
309 'Either call close directly and ignore its return value, or wrap close',
310 'in IGNORE_EINTR to use its return value. See http://crbug.com/269623'
311 ),
312 True,
313 (),
314 ),
315 (
316 r'/IGNORE_EINTR\((?!.*close)',
317 (
318 'IGNORE_EINTR is only valid when wrapping close. To wrap other system',
319 'calls, use HANDLE_EINTR. See http://crbug.com/269623',
320 ),
321 True,
322 (
323 # Files that #define IGNORE_EINTR.
Egor Paskoce145c42018-09-28 19:31:04324 r'^base[\\/]posix[\\/]eintr_wrapper\.h$',
325 r'^ppapi[\\/]tests[\\/]test_broker\.cc$',
[email protected]d89eec82013-12-03 14:10:59326 ),
327 ),
[email protected]ec5b3f02014-04-04 18:43:43328 (
329 r'/v8::Extension\(',
330 (
331 'Do not introduce new v8::Extensions into the code base, use',
332 'gin::Wrappable instead. See http://crbug.com/334679',
333 ),
334 True,
[email protected]f55c90ee62014-04-12 00:50:03335 (
Egor Paskoce145c42018-09-28 19:31:04336 r'extensions[\\/]renderer[\\/]safe_builtins\.*',
[email protected]f55c90ee62014-04-12 00:50:03337 ),
[email protected]ec5b3f02014-04-04 18:43:43338 ),
skyostilf9469f72015-04-20 10:38:52339 (
jame2d1a952016-04-02 00:27:10340 '#pragma comment(lib,',
341 (
342 'Specify libraries to link with in build files and not in the source.',
343 ),
344 True,
Mirko Bonadeif4f0f0e2018-04-12 09:29:41345 (
tzik3f295992018-12-04 20:32:23346 r'^base[\\/]third_party[\\/]symbolize[\\/].*',
Egor Paskoce145c42018-09-28 19:31:04347 r'^third_party[\\/]abseil-cpp[\\/].*',
Mirko Bonadeif4f0f0e2018-04-12 09:29:41348 ),
jame2d1a952016-04-02 00:27:10349 ),
fdorayc4ac18d2017-05-01 21:39:59350 (
Gabriel Charette7cc6c432018-04-25 20:52:02351 r'/base::SequenceChecker\b',
gabd52c912a2017-05-11 04:15:59352 (
353 'Consider using SEQUENCE_CHECKER macros instead of the class directly.',
354 ),
355 False,
356 (),
357 ),
358 (
Gabriel Charette7cc6c432018-04-25 20:52:02359 r'/base::ThreadChecker\b',
gabd52c912a2017-05-11 04:15:59360 (
361 'Consider using THREAD_CHECKER macros instead of the class directly.',
362 ),
363 False,
364 (),
365 ),
dbeamb6f4fde2017-06-15 04:03:06366 (
Yuri Wiitala2f8de5c2017-07-21 00:11:06367 r'/(Time(|Delta|Ticks)|ThreadTicks)::FromInternalValue|ToInternalValue',
368 (
369 'base::TimeXXX::FromInternalValue() and ToInternalValue() are',
370 'deprecated (http://crbug.com/634507). Please avoid converting away',
371 'from the Time types in Chromium code, especially if any math is',
372 'being done on time values. For interfacing with platform/library',
373 'APIs, use FromMicroseconds() or InMicroseconds(), or one of the other',
374 'type converter methods instead. For faking TimeXXX values (for unit',
375 'testing only), use TimeXXX() + TimeDelta::FromMicroseconds(N). For',
376 'other use cases, please contact base/time/OWNERS.',
377 ),
378 False,
379 (),
380 ),
381 (
dbeamb6f4fde2017-06-15 04:03:06382 'CallJavascriptFunctionUnsafe',
383 (
384 "Don't use CallJavascriptFunctionUnsafe() in new code. Instead, use",
385 'AllowJavascript(), OnJavascriptAllowed()/OnJavascriptDisallowed(),',
386 'and CallJavascriptFunction(). See https://goo.gl/qivavq.',
387 ),
388 False,
389 (
Egor Paskoce145c42018-09-28 19:31:04390 r'^content[\\/]browser[\\/]webui[\\/]web_ui_impl\.(cc|h)$',
391 r'^content[\\/]public[\\/]browser[\\/]web_ui\.h$',
392 r'^content[\\/]public[\\/]test[\\/]test_web_ui\.(cc|h)$',
dbeamb6f4fde2017-06-15 04:03:06393 ),
394 ),
dskiba1474c2bfd62017-07-20 02:19:24395 (
396 'leveldb::DB::Open',
397 (
398 'Instead of leveldb::DB::Open() use leveldb_env::OpenDB() from',
399 'third_party/leveldatabase/env_chromium.h. It exposes databases to',
400 "Chrome's tracing, making their memory usage visible.",
401 ),
402 True,
403 (
404 r'^third_party/leveldatabase/.*\.(cc|h)$',
405 ),
Gabriel Charette0592c3a2017-07-26 12:02:04406 ),
407 (
Chris Mumfordc38afb62017-10-09 17:55:08408 'leveldb::NewMemEnv',
409 (
410 'Instead of leveldb::NewMemEnv() use leveldb_chrome::NewMemEnv() from',
Chris Mumford8d26d10a2018-04-20 17:07:58411 'third_party/leveldatabase/leveldb_chrome.h. It exposes environments',
412 "to Chrome's tracing, making their memory usage visible.",
Chris Mumfordc38afb62017-10-09 17:55:08413 ),
414 True,
415 (
416 r'^third_party/leveldatabase/.*\.(cc|h)$',
417 ),
418 ),
419 (
Gabriel Charetted9839bc2017-07-29 14:17:47420 'RunLoop::QuitCurrent',
421 (
Robert Liao64b7ab22017-08-04 23:03:43422 'Please migrate away from RunLoop::QuitCurrent*() methods. Use member',
423 'methods of a specific RunLoop instance instead.',
Gabriel Charetted9839bc2017-07-29 14:17:47424 ),
Gabriel Charettec0a8f3ee2018-04-25 20:49:41425 False,
Gabriel Charetted9839bc2017-07-29 14:17:47426 (),
Gabriel Charettea44975052017-08-21 23:14:04427 ),
428 (
429 'base::ScopedMockTimeMessageLoopTaskRunner',
430 (
Gabriel Charette87cc1af2018-04-25 20:52:51431 'ScopedMockTimeMessageLoopTaskRunner is deprecated. Prefer',
432 'ScopedTaskEnvironment::MainThreadType::MOCK_TIME. There are still a',
433 'few cases that may require a ScopedMockTimeMessageLoopTaskRunner',
434 '(i.e. mocking the main MessageLoopForUI in browser_tests), but check',
435 'with gab@ first if you think you need it)',
Gabriel Charettea44975052017-08-21 23:14:04436 ),
Gabriel Charette87cc1af2018-04-25 20:52:51437 False,
Gabriel Charettea44975052017-08-21 23:14:04438 (),
Eric Stevenson6b47b44c2017-08-30 20:41:57439 ),
440 (
441 r'std::regex',
442 (
443 'Using std::regex adds unnecessary binary size to Chrome. Please use',
Mostyn Bramley-Moore6b427322017-12-21 22:11:02444 're2::RE2 instead (crbug.com/755321)',
Eric Stevenson6b47b44c2017-08-30 20:41:57445 ),
446 True,
447 (),
Francois Doray43670e32017-09-27 12:40:38448 ),
449 (
450 (r'/base::ThreadRestrictions::(ScopedAllowIO|AssertIOAllowed|'
451 r'DisallowWaiting|AssertWaitAllowed|SetWaitAllowed|ScopedAllowWait)'),
452 (
453 'Use the new API in base/threading/thread_restrictions.h.',
454 ),
Gabriel Charette04b138f2018-08-06 00:03:22455 False,
Francois Doray43670e32017-09-27 12:40:38456 (),
457 ),
Luis Hector Chavez9bbaed532017-11-30 18:25:38458 (
459 r'/\bbase::Bind\(',
460 (
Gabriel Charette147335ea2018-03-22 15:59:19461 'Please consider using base::Bind{Once,Repeating} instead',
Mostyn Bramley-Moore6b427322017-12-21 22:11:02462 'of base::Bind. (crbug.com/714018)',
Luis Hector Chavez9bbaed532017-11-30 18:25:38463 ),
464 False,
465 (),
466 ),
467 (
468 r'/\bbase::Callback<',
469 (
Gabriel Charette147335ea2018-03-22 15:59:19470 'Please consider using base::{Once,Repeating}Callback instead',
Mostyn Bramley-Moore6b427322017-12-21 22:11:02471 'of base::Callback. (crbug.com/714018)',
Luis Hector Chavez9bbaed532017-11-30 18:25:38472 ),
473 False,
474 (),
475 ),
476 (
477 r'/\bbase::Closure\b',
478 (
Gabriel Charette147335ea2018-03-22 15:59:19479 'Please consider using base::{Once,Repeating}Closure instead',
Mostyn Bramley-Moore6b427322017-12-21 22:11:02480 'of base::Closure. (crbug.com/714018)',
Luis Hector Chavez9bbaed532017-11-30 18:25:38481 ),
482 False,
483 (),
484 ),
Victor Costan3653df62018-02-08 21:38:16485 (
Gabriel Charette147335ea2018-03-22 15:59:19486 r'RunMessageLoop',
487 (
488 'RunMessageLoop is deprecated, use RunLoop instead.',
489 ),
490 False,
491 (),
492 ),
493 (
494 r'RunThisRunLoop',
495 (
496 'RunThisRunLoop is deprecated, use RunLoop directly instead.',
497 ),
498 False,
499 (),
500 ),
501 (
502 r'RunAllPendingInMessageLoop()',
503 (
504 "Prefer RunLoop over RunAllPendingInMessageLoop, please contact gab@",
505 "if you're convinced you need this.",
506 ),
507 False,
508 (),
509 ),
510 (
511 r'RunAllPendingInMessageLoop(BrowserThread',
512 (
513 'RunAllPendingInMessageLoop is deprecated. Use RunLoop for',
514 'BrowserThread::UI, TestBrowserThreadBundle::RunIOThreadUntilIdle',
515 'for BrowserThread::IO, and prefer RunLoop::QuitClosure to observe',
516 'async events instead of flushing threads.',
517 ),
518 False,
519 (),
520 ),
521 (
522 r'MessageLoopRunner',
523 (
524 'MessageLoopRunner is deprecated, use RunLoop instead.',
525 ),
526 False,
527 (),
528 ),
529 (
530 r'GetDeferredQuitTaskForRunLoop',
531 (
532 "GetDeferredQuitTaskForRunLoop shouldn't be needed, please contact",
533 "gab@ if you found a use case where this is the only solution.",
534 ),
535 False,
536 (),
537 ),
538 (
Victor Costan3653df62018-02-08 21:38:16539 'sqlite3_initialize',
540 (
541 'Instead of sqlite3_initialize, depend on //sql, ',
542 '#include "sql/initialize.h" and use sql::EnsureSqliteInitialized().',
543 ),
544 True,
545 (
546 r'^sql/initialization\.(cc|h)$',
547 r'^third_party/sqlite/.*\.(c|cc|h)$',
548 ),
549 ),
Matt Menke7f520a82018-03-28 21:38:37550 (
551 'net::URLFetcher',
552 (
553 'net::URLFetcher should no longer be used in content embedders. ',
554 'Instead, use network::SimpleURLLoader instead, which supports ',
555 'an out-of-process network stack. ',
556 'net::URLFetcher may still be used in binaries that do not embed',
557 'content.',
558 ),
Matt Menke59716d02018-04-05 12:45:53559 False,
Matt Menke7f520a82018-03-28 21:38:37560 (
Egor Paskoce145c42018-09-28 19:31:04561 r'^ios[\\/].*\.(cc|h)$',
562 r'.*[\\/]ios[\\/].*\.(cc|h)$',
Matt Menke7f520a82018-03-28 21:38:37563 r'.*_ios\.(cc|h)$',
Egor Paskoce145c42018-09-28 19:31:04564 r'^net[\\/].*\.(cc|h)$',
565 r'.*[\\/]tools[\\/].*\.(cc|h)$',
Matt Menke7f520a82018-03-28 21:38:37566 ),
567 ),
jdoerried7d10ab2018-04-27 10:46:13568 (
569 r'/\barraysize\b',
570 (
571 "arraysize is deprecated, please use base::size(array) instead ",
572 "(https://crbug.com/837308). ",
573 ),
574 False,
575 (),
576 ),
tzik5de2157f2018-05-08 03:42:47577 (
578 r'std::random_shuffle',
579 (
580 'std::random_shuffle is deprecated in C++14, and removed in C++17. Use',
581 'base::RandomShuffle instead.'
582 ),
583 True,
584 (),
585 ),
Javier Ernesto Flores Robles749e6c22018-10-08 09:36:24586 (
587 'ios/web/public/test/http_server',
588 (
589 'web::HTTPserver is deprecated use net::EmbeddedTestServer instead.',
590 ),
591 False,
592 (),
593 ),
[email protected]127f18ec2012-06-16 05:05:59594)
595
wnwenbdc444e2016-05-25 13:44:15596
mlamouria82272622014-09-16 18:45:04597_IPC_ENUM_TRAITS_DEPRECATED = (
598 'You are using IPC_ENUM_TRAITS() in your code. It has been deprecated.\n'
Vaclav Brozekd5de76a2018-03-17 07:57:50599 'See http://www.chromium.org/Home/chromium-security/education/'
600 'security-tips-for-ipc')
mlamouria82272622014-09-16 18:45:04601
Stephen Martinis97a394142018-06-07 23:06:05602_LONG_PATH_ERROR = (
603 'Some files included in this CL have file names that are too long (> 200'
604 ' characters). If committed, these files will cause issues on Windows. See'
605 ' https://crbug.com/612667 for more details.'
606)
607
Shenghua Zhangbfaa38b82017-11-16 21:58:02608_JAVA_MULTIPLE_DEFINITION_EXCLUDED_PATHS = [
Egor Paskoce145c42018-09-28 19:31:04609 r".*[\\/]BuildHooksAndroidImpl\.java",
610 r".*[\\/]LicenseContentProvider\.java",
611 r".*[\\/]PlatformServiceBridgeImpl.java",
Patrick Noland5475bc0d2018-10-01 20:04:28612 r".*chrome[\\\/]android[\\\/]feed[\\\/]dummy[\\\/].*\.java",
Shenghua Zhangbfaa38b82017-11-16 21:58:02613]
[email protected]127f18ec2012-06-16 05:05:59614
Sean Kau46e29bc2017-08-28 16:31:16615# These paths contain test data and other known invalid JSON files.
616_KNOWN_INVALID_JSON_FILE_PATTERNS = [
Egor Paskoce145c42018-09-28 19:31:04617 r'test[\\/]data[\\/]',
618 r'^components[\\/]policy[\\/]resources[\\/]policy_templates\.json$',
619 r'^third_party[\\/]protobuf[\\/]',
Egor Paskoce145c42018-09-28 19:31:04620 r'^third_party[\\/]blink[\\/]renderer[\\/]devtools[\\/]protocol\.json$',
Kent Tamura77578cc2018-11-25 22:33:43621 r'^third_party[\\/]blink[\\/]web_tests[\\/]external[\\/]wpt[\\/]',
Sean Kau46e29bc2017-08-28 16:31:16622]
623
624
[email protected]b00342e7f2013-03-26 16:21:54625_VALID_OS_MACROS = (
626 # Please keep sorted.
rayb0088ee52017-04-26 22:35:08627 'OS_AIX',
[email protected]b00342e7f2013-03-26 16:21:54628 'OS_ANDROID',
Henrique Nakashimaafff0502018-01-24 17:14:12629 'OS_ASMJS',
[email protected]b00342e7f2013-03-26 16:21:54630 'OS_BSD',
631 'OS_CAT', # For testing.
632 'OS_CHROMEOS',
Eugene Kliuchnikovb99125c2018-11-26 17:33:04633 'OS_CYGWIN', # third_party code.
[email protected]b00342e7f2013-03-26 16:21:54634 'OS_FREEBSD',
scottmg2f97ee122017-05-12 17:50:37635 'OS_FUCHSIA',
[email protected]b00342e7f2013-03-26 16:21:54636 'OS_IOS',
637 'OS_LINUX',
638 'OS_MACOSX',
639 'OS_NACL',
hidehikof7295f22014-10-28 11:57:21640 'OS_NACL_NONSFI',
641 'OS_NACL_SFI',
krytarowski969759f2016-07-31 23:55:12642 'OS_NETBSD',
[email protected]b00342e7f2013-03-26 16:21:54643 'OS_OPENBSD',
644 'OS_POSIX',
[email protected]eda7afa12014-02-06 12:27:37645 'OS_QNX',
[email protected]b00342e7f2013-03-26 16:21:54646 'OS_SOLARIS',
[email protected]b00342e7f2013-03-26 16:21:54647 'OS_WIN',
648)
649
650
agrievef32bcc72016-04-04 14:57:40651_ANDROID_SPECIFIC_PYDEPS_FILES = [
Andrew Luob2e4b342018-09-20 19:32:39652 'android_webview/tools/run_cts.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:36653 'base/android/jni_generator/jni_generator.pydeps',
654 'base/android/jni_generator/jni_registration_generator.pydeps',
655 'build/android/gyp/aar.pydeps',
656 'build/android/gyp/aidl.pydeps',
657 'build/android/gyp/apkbuilder.pydeps',
658 'build/android/gyp/app_bundle_to_apks.pydeps',
659 'build/android/gyp/bytecode_processor.pydeps',
660 'build/android/gyp/compile_resources.pydeps',
661 'build/android/gyp/create_bundle_wrapper_script.pydeps',
662 'build/android/gyp/copy_ex.pydeps',
663 'build/android/gyp/create_app_bundle.pydeps',
664 'build/android/gyp/create_apk_operations_script.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:36665 'build/android/gyp/create_java_binary_script.pydeps',
666 'build/android/gyp/create_stack_script.pydeps',
667 'build/android/gyp/create_test_runner_script.pydeps',
668 'build/android/gyp/create_tool_wrapper.pydeps',
669 'build/android/gyp/desugar.pydeps',
Sam Maier3599daa2018-11-26 18:02:59670 'build/android/gyp/dexsplitter.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:36671 'build/android/gyp/dex.pydeps',
672 'build/android/gyp/dist_aar.pydeps',
673 'build/android/gyp/emma_instr.pydeps',
674 'build/android/gyp/filter_zip.pydeps',
675 'build/android/gyp/gcc_preprocess.pydeps',
Christopher Grant99e0e20062018-11-21 21:22:36676 'build/android/gyp/generate_linker_version_script.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:36677 'build/android/gyp/ijar.pydeps',
678 'build/android/gyp/java_cpp_enum.pydeps',
679 'build/android/gyp/javac.pydeps',
680 'build/android/gyp/jinja_template.pydeps',
681 'build/android/gyp/lint.pydeps',
682 'build/android/gyp/main_dex_list.pydeps',
683 'build/android/gyp/merge_jar_info_files.pydeps',
684 'build/android/gyp/merge_manifest.pydeps',
685 'build/android/gyp/prepare_resources.pydeps',
686 'build/android/gyp/proguard.pydeps',
687 'build/android/gyp/write_build_config.pydeps',
688 'build/android/gyp/write_ordered_libraries.pydeps',
Andrew Grieve9ff17792018-11-30 04:55:56689 'build/android/gyp/zip.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:36690 'build/android/incremental_install/generate_android_manifest.pydeps',
691 'build/android/incremental_install/write_installer_json.pydeps',
Andrew Grievea7f1ee902018-05-18 16:17:22692 'build/android/resource_sizes.pydeps',
agrievef32bcc72016-04-04 14:57:40693 'build/android/test_runner.pydeps',
hzl9b15df52017-03-23 23:43:04694 'build/android/test_wrapper/logdog_wrapper.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:36695 'build/protoc_java.pydeps',
Sam Maier3599daa2018-11-26 18:02:59696 ('build/secondary/third_party/android_platform/'
697 'development/scripts/stack.pydeps'),
agrieve732db3a2016-04-26 19:18:19698 'net/tools/testserver/testserver.pydeps',
agrievef32bcc72016-04-04 14:57:40699]
700
wnwenbdc444e2016-05-25 13:44:15701
agrievef32bcc72016-04-04 14:57:40702_GENERIC_PYDEPS_FILES = [
John Chencde89192018-01-27 21:18:40703 'chrome/test/chromedriver/test/run_py_tests.pydeps',
Cole Winstanley7045a1b2018-08-27 23:37:29704 'chrome/test/chromedriver/log_replay/client_replay_unittest.pydeps',
Andrew Grievea7f1ee902018-05-18 16:17:22705 'tools/binary_size/supersize.pydeps',
agrievef32bcc72016-04-04 14:57:40706]
707
wnwenbdc444e2016-05-25 13:44:15708
agrievef32bcc72016-04-04 14:57:40709_ALL_PYDEPS_FILES = _ANDROID_SPECIFIC_PYDEPS_FILES + _GENERIC_PYDEPS_FILES
710
711
Eric Boren6fd2b932018-01-25 15:05:08712# Bypass the AUTHORS check for these accounts.
713_KNOWN_ROBOTS = set(
Chan52654f52018-03-21 21:02:29714 '%s-chromium-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com' % s
715 for s in ('afdo', 'angle', 'catapult', 'chromite', 'depot-tools',
Eric Boren36af476a2018-06-08 16:21:08716 'fuchsia-sdk', 'nacl', 'pdfium', 'perfetto', 'skia',
Eric Boren57cc805b2018-08-20 17:28:32717 'spirv', 'src-internal', 'webrtc')
Sergiy Byelozyorov47158a52018-06-13 22:38:59718 ) | set('%[email protected]' % s for s in ('findit-for-me',)
Achuith Bhandarkar35905562018-07-25 19:28:45719 ) | set('%[email protected]' % s for s in ('3su6n15k.default',)
Sergiy Byelozyorov47158a52018-06-13 22:38:59720 ) | set('%[email protected]' % s
Robert Ma7f024172018-11-01 20:59:22721 for s in ('v8-ci-autoroll-builder', 'wpt-autoroller',)
Eric Boren835d71f2018-09-07 21:09:04722 ) | set('%[email protected]' % s
723 for s in ('chromium-autoroll',)
724 ) | set('%[email protected]' % s
Eric Boren2b7e3c3c2018-09-13 18:14:30725 for s in ('chromium-internal-autoroll',))
Eric Boren6fd2b932018-01-25 15:05:08726
727
[email protected]55459852011-08-10 15:17:19728def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
729 """Attempts to prevent use of functions intended only for testing in
730 non-testing code. For now this is just a best-effort implementation
731 that ignores header files and may have some false positives. A
732 better implementation would probably need a proper C++ parser.
733 """
734 # We only scan .cc files and the like, as the declaration of
735 # for-testing functions in header files are hard to distinguish from
736 # calls to such functions without a proper C++ parser.
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:49737 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
[email protected]55459852011-08-10 15:17:19738
jochenc0d4808c2015-07-27 09:25:42739 base_function_pattern = r'[ :]test::[^\s]+|ForTest(s|ing)?|for_test(s|ing)?'
[email protected]55459852011-08-10 15:17:19740 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
[email protected]23501822014-05-14 02:06:09741 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_function_pattern)
[email protected]55459852011-08-10 15:17:19742 exclusion_pattern = input_api.re.compile(
743 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
744 base_function_pattern, base_function_pattern))
745
746 def FilterFile(affected_file):
[email protected]06e6d0ff2012-12-11 01:36:44747 black_list = (_EXCLUDED_PATHS +
748 _TEST_CODE_EXCLUDED_PATHS +
749 input_api.DEFAULT_BLACK_LIST)
[email protected]55459852011-08-10 15:17:19750 return input_api.FilterSourceFile(
751 affected_file,
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:49752 white_list=file_inclusion_pattern,
[email protected]55459852011-08-10 15:17:19753 black_list=black_list)
754
755 problems = []
756 for f in input_api.AffectedSourceFiles(FilterFile):
757 local_path = f.LocalPath()
[email protected]825d27182014-01-02 21:24:24758 for line_number, line in f.ChangedContents():
[email protected]2fdd1f362013-01-16 03:56:03759 if (inclusion_pattern.search(line) and
[email protected]de4f7d22013-05-23 14:27:46760 not comment_pattern.search(line) and
[email protected]2fdd1f362013-01-16 03:56:03761 not exclusion_pattern.search(line)):
[email protected]55459852011-08-10 15:17:19762 problems.append(
[email protected]2fdd1f362013-01-16 03:56:03763 '%s:%d\n %s' % (local_path, line_number, line.strip()))
[email protected]55459852011-08-10 15:17:19764
765 if problems:
[email protected]f7051d52013-04-02 18:31:42766 return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
[email protected]2fdd1f362013-01-16 03:56:03767 else:
768 return []
[email protected]55459852011-08-10 15:17:19769
770
Vaclav Brozek7dbc28c2018-03-27 08:35:23771def _CheckNoProductionCodeUsingTestOnlyFunctionsJava(input_api, output_api):
772 """This is a simplified version of
773 _CheckNoProductionCodeUsingTestOnlyFunctions for Java files.
774 """
775 javadoc_start_re = input_api.re.compile(r'^\s*/\*\*')
776 javadoc_end_re = input_api.re.compile(r'^\s*\*/')
777 name_pattern = r'ForTest(s|ing)?'
778 # Describes an occurrence of "ForTest*" inside a // comment.
779 comment_re = input_api.re.compile(r'//.*%s' % name_pattern)
780 # Catch calls.
781 inclusion_re = input_api.re.compile(r'(%s)\s*\(' % name_pattern)
782 # Ignore definitions. (Comments are ignored separately.)
783 exclusion_re = input_api.re.compile(r'(%s)[^;]+\{' % name_pattern)
784
785 problems = []
786 sources = lambda x: input_api.FilterSourceFile(
787 x,
788 black_list=(('(?i).*test', r'.*\/junit\/')
789 + input_api.DEFAULT_BLACK_LIST),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:49790 white_list=[r'.*\.java$']
Vaclav Brozek7dbc28c2018-03-27 08:35:23791 )
792 for f in input_api.AffectedFiles(include_deletes=False, file_filter=sources):
793 local_path = f.LocalPath()
794 is_inside_javadoc = False
795 for line_number, line in f.ChangedContents():
796 if is_inside_javadoc and javadoc_end_re.search(line):
797 is_inside_javadoc = False
798 if not is_inside_javadoc and javadoc_start_re.search(line):
799 is_inside_javadoc = True
800 if is_inside_javadoc:
801 continue
802 if (inclusion_re.search(line) and
803 not comment_re.search(line) and
804 not exclusion_re.search(line)):
805 problems.append(
806 '%s:%d\n %s' % (local_path, line_number, line.strip()))
807
808 if problems:
809 return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
810 else:
811 return []
812
813
[email protected]10689ca2011-09-02 02:31:54814def _CheckNoIOStreamInHeaders(input_api, output_api):
815 """Checks to make sure no .h files include <iostream>."""
816 files = []
817 pattern = input_api.re.compile(r'^#include\s*<iostream>',
818 input_api.re.MULTILINE)
819 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
820 if not f.LocalPath().endswith('.h'):
821 continue
822 contents = input_api.ReadFile(f)
823 if pattern.search(contents):
824 files.append(f)
825
826 if len(files):
yolandyandaabc6d2016-04-18 18:29:39827 return [output_api.PresubmitError(
[email protected]6c063c62012-07-11 19:11:06828 'Do not #include <iostream> in header files, since it inserts static '
829 'initialization into every file including the header. Instead, '
[email protected]10689ca2011-09-02 02:31:54830 '#include <ostream>. See http://crbug.com/94794',
831 files) ]
832 return []
833
Danil Chapovalov3518f362018-08-11 16:13:43834def _CheckNoStrCatRedefines(input_api, output_api):
835 """Checks no windows headers with StrCat redefined are included directly."""
836 files = []
837 pattern_deny = input_api.re.compile(
838 r'^#include\s*[<"](shlwapi|atlbase|propvarutil|sphelper).h[">]',
839 input_api.re.MULTILINE)
840 pattern_allow = input_api.re.compile(
841 r'^#include\s"base/win/windows_defines.inc"',
842 input_api.re.MULTILINE)
843 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
844 contents = input_api.ReadFile(f)
845 if pattern_deny.search(contents) and not pattern_allow.search(contents):
846 files.append(f.LocalPath())
847
848 if len(files):
849 return [output_api.PresubmitError(
850 'Do not #include shlwapi.h, atlbase.h, propvarutil.h or sphelper.h '
851 'directly since they pollute code with StrCat macro. Instead, '
852 'include matching header from base/win. See http://crbug.com/856536',
853 files) ]
854 return []
855
[email protected]10689ca2011-09-02 02:31:54856
[email protected]72df4e782012-06-21 16:28:18857def _CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
danakj61c1aa22015-10-26 19:55:52858 """Checks to make sure no source files use UNIT_TEST."""
[email protected]72df4e782012-06-21 16:28:18859 problems = []
860 for f in input_api.AffectedFiles():
861 if (not f.LocalPath().endswith(('.cc', '.mm'))):
862 continue
863
864 for line_num, line in f.ChangedContents():
[email protected]549f86a2013-11-19 13:00:04865 if 'UNIT_TEST ' in line or line.endswith('UNIT_TEST'):
[email protected]72df4e782012-06-21 16:28:18866 problems.append(' %s:%d' % (f.LocalPath(), line_num))
867
868 if not problems:
869 return []
870 return [output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
871 '\n'.join(problems))]
872
Dominic Battre033531052018-09-24 15:45:34873def _CheckNoDISABLETypoInTests(input_api, output_api):
874 """Checks to prevent attempts to disable tests with DISABLE_ prefix.
875
876 This test warns if somebody tries to disable a test with the DISABLE_ prefix
877 instead of DISABLED_. To filter false positives, reports are only generated
878 if a corresponding MAYBE_ line exists.
879 """
880 problems = []
881
882 # The following two patterns are looked for in tandem - is a test labeled
883 # as MAYBE_ followed by a DISABLE_ (instead of the correct DISABLED)
884 maybe_pattern = input_api.re.compile(r'MAYBE_([a-zA-Z0-9_]+)')
885 disable_pattern = input_api.re.compile(r'DISABLE_([a-zA-Z0-9_]+)')
886
887 # This is for the case that a test is disabled on all platforms.
888 full_disable_pattern = input_api.re.compile(
889 r'^\s*TEST[^(]*\([a-zA-Z0-9_]+,\s*DISABLE_[a-zA-Z0-9_]+\)',
890 input_api.re.MULTILINE)
891
Katie Df13948e2018-09-25 07:33:44892 for f in input_api.AffectedFiles(False):
Dominic Battre033531052018-09-24 15:45:34893 if not 'test' in f.LocalPath() or not f.LocalPath().endswith('.cc'):
894 continue
895
896 # Search for MABYE_, DISABLE_ pairs.
897 disable_lines = {} # Maps of test name to line number.
898 maybe_lines = {}
899 for line_num, line in f.ChangedContents():
900 disable_match = disable_pattern.search(line)
901 if disable_match:
902 disable_lines[disable_match.group(1)] = line_num
903 maybe_match = maybe_pattern.search(line)
904 if maybe_match:
905 maybe_lines[maybe_match.group(1)] = line_num
906
907 # Search for DISABLE_ occurrences within a TEST() macro.
908 disable_tests = set(disable_lines.keys())
909 maybe_tests = set(maybe_lines.keys())
910 for test in disable_tests.intersection(maybe_tests):
911 problems.append(' %s:%d' % (f.LocalPath(), disable_lines[test]))
912
913 contents = input_api.ReadFile(f)
914 full_disable_match = full_disable_pattern.search(contents)
915 if full_disable_match:
916 problems.append(' %s' % f.LocalPath())
917
918 if not problems:
919 return []
920 return [
921 output_api.PresubmitPromptWarning(
922 'Attempt to disable a test with DISABLE_ instead of DISABLED_?\n' +
923 '\n'.join(problems))
924 ]
925
[email protected]72df4e782012-06-21 16:28:18926
danakj61c1aa22015-10-26 19:55:52927def _CheckDCHECK_IS_ONHasBraces(input_api, output_api):
kjellanderaee306632017-02-22 19:26:57928 """Checks to make sure DCHECK_IS_ON() does not skip the parentheses."""
danakj61c1aa22015-10-26 19:55:52929 errors = []
930 pattern = input_api.re.compile(r'DCHECK_IS_ON(?!\(\))',
931 input_api.re.MULTILINE)
932 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
933 if (not f.LocalPath().endswith(('.cc', '.mm', '.h'))):
934 continue
935 for lnum, line in f.ChangedContents():
936 if input_api.re.search(pattern, line):
dchenge07de812016-06-20 19:27:17937 errors.append(output_api.PresubmitError(
938 ('%s:%d: Use of DCHECK_IS_ON() must be written as "#if ' +
kjellanderaee306632017-02-22 19:26:57939 'DCHECK_IS_ON()", not forgetting the parentheses.')
dchenge07de812016-06-20 19:27:17940 % (f.LocalPath(), lnum)))
danakj61c1aa22015-10-26 19:55:52941 return errors
942
943
mcasasb7440c282015-02-04 14:52:19944def _FindHistogramNameInLine(histogram_name, line):
945 """Tries to find a histogram name or prefix in a line."""
946 if not "affected-histogram" in line:
947 return histogram_name in line
948 # A histogram_suffixes tag type has an affected-histogram name as a prefix of
949 # the histogram_name.
950 if not '"' in line:
951 return False
952 histogram_prefix = line.split('\"')[1]
953 return histogram_prefix in histogram_name
954
955
956def _CheckUmaHistogramChanges(input_api, output_api):
957 """Check that UMA histogram names in touched lines can still be found in other
958 lines of the patch or in histograms.xml. Note that this check would not catch
959 the reverse: changes in histograms.xml not matched in the code itself."""
960 touched_histograms = []
961 histograms_xml_modifications = []
Vaclav Brozekbdac817c2018-03-24 06:30:47962 call_pattern_c = r'\bUMA_HISTOGRAM.*\('
963 call_pattern_java = r'\bRecordHistogram\.record[a-zA-Z]+Histogram\('
964 name_pattern = r'"(.*?)"'
965 single_line_c_re = input_api.re.compile(call_pattern_c + name_pattern)
966 single_line_java_re = input_api.re.compile(call_pattern_java + name_pattern)
967 split_line_c_prefix_re = input_api.re.compile(call_pattern_c)
968 split_line_java_prefix_re = input_api.re.compile(call_pattern_java)
969 split_line_suffix_re = input_api.re.compile(r'^\s*' + name_pattern)
Vaclav Brozek0e730cbd2018-03-24 06:18:17970 last_line_matched_prefix = False
mcasasb7440c282015-02-04 14:52:19971 for f in input_api.AffectedFiles():
972 # If histograms.xml itself is modified, keep the modified lines for later.
973 if f.LocalPath().endswith(('histograms.xml')):
974 histograms_xml_modifications = f.ChangedContents()
975 continue
Vaclav Brozekbdac817c2018-03-24 06:30:47976 if f.LocalPath().endswith(('cc', 'mm', 'cpp')):
977 single_line_re = single_line_c_re
978 split_line_prefix_re = split_line_c_prefix_re
979 elif f.LocalPath().endswith(('java')):
980 single_line_re = single_line_java_re
981 split_line_prefix_re = split_line_java_prefix_re
982 else:
mcasasb7440c282015-02-04 14:52:19983 continue
984 for line_num, line in f.ChangedContents():
Vaclav Brozek0e730cbd2018-03-24 06:18:17985 if last_line_matched_prefix:
986 suffix_found = split_line_suffix_re.search(line)
987 if suffix_found :
988 touched_histograms.append([suffix_found.group(1), f, line_num])
989 last_line_matched_prefix = False
990 continue
Vaclav Brozek8a8e2e202018-03-23 22:01:06991 found = single_line_re.search(line)
mcasasb7440c282015-02-04 14:52:19992 if found:
993 touched_histograms.append([found.group(1), f, line_num])
Vaclav Brozek0e730cbd2018-03-24 06:18:17994 continue
995 last_line_matched_prefix = split_line_prefix_re.search(line)
mcasasb7440c282015-02-04 14:52:19996
997 # Search for the touched histogram names in the local modifications to
998 # histograms.xml, and, if not found, on the base histograms.xml file.
999 unmatched_histograms = []
1000 for histogram_info in touched_histograms:
1001 histogram_name_found = False
1002 for line_num, line in histograms_xml_modifications:
1003 histogram_name_found = _FindHistogramNameInLine(histogram_info[0], line)
1004 if histogram_name_found:
1005 break
1006 if not histogram_name_found:
1007 unmatched_histograms.append(histogram_info)
1008
eromanb90c82e7e32015-04-01 15:13:491009 histograms_xml_path = 'tools/metrics/histograms/histograms.xml'
mcasasb7440c282015-02-04 14:52:191010 problems = []
1011 if unmatched_histograms:
eromanb90c82e7e32015-04-01 15:13:491012 with open(histograms_xml_path) as histograms_xml:
mcasasb7440c282015-02-04 14:52:191013 for histogram_name, f, line_num in unmatched_histograms:
mcasas39c1b8b2015-02-25 15:33:451014 histograms_xml.seek(0)
mcasasb7440c282015-02-04 14:52:191015 histogram_name_found = False
1016 for line in histograms_xml:
1017 histogram_name_found = _FindHistogramNameInLine(histogram_name, line)
1018 if histogram_name_found:
1019 break
1020 if not histogram_name_found:
1021 problems.append(' [%s:%d] %s' %
1022 (f.LocalPath(), line_num, histogram_name))
1023
1024 if not problems:
1025 return []
1026 return [output_api.PresubmitPromptWarning('Some UMA_HISTOGRAM lines have '
1027 'been modified and the associated histogram name has no match in either '
eromanb90c82e7e32015-04-01 15:13:491028 '%s or the modifications of it:' % (histograms_xml_path), problems)]
mcasasb7440c282015-02-04 14:52:191029
wnwenbdc444e2016-05-25 13:44:151030
yolandyandaabc6d2016-04-18 18:29:391031def _CheckFlakyTestUsage(input_api, output_api):
1032 """Check that FlakyTest annotation is our own instead of the android one"""
1033 pattern = input_api.re.compile(r'import android.test.FlakyTest;')
1034 files = []
1035 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1036 if f.LocalPath().endswith('Test.java'):
1037 if pattern.search(input_api.ReadFile(f)):
1038 files.append(f)
1039 if len(files):
1040 return [output_api.PresubmitError(
1041 'Use org.chromium.base.test.util.FlakyTest instead of '
1042 'android.test.FlakyTest',
1043 files)]
1044 return []
mcasasb7440c282015-02-04 14:52:191045
wnwenbdc444e2016-05-25 13:44:151046
[email protected]8ea5d4b2011-09-13 21:49:221047def _CheckNoNewWStrings(input_api, output_api):
1048 """Checks to make sure we don't introduce use of wstrings."""
[email protected]55463aa62011-10-12 00:48:271049 problems = []
[email protected]8ea5d4b2011-09-13 21:49:221050 for f in input_api.AffectedFiles():
[email protected]b5c24292011-11-28 14:38:201051 if (not f.LocalPath().endswith(('.cc', '.h')) or
scottmge6f04402014-11-05 01:59:571052 f.LocalPath().endswith(('test.cc', '_win.cc', '_win.h')) or
pennymac84fd6692016-07-13 22:35:341053 '/win/' in f.LocalPath() or
1054 'chrome_elf' in f.LocalPath() or
1055 'install_static' in f.LocalPath()):
[email protected]b5c24292011-11-28 14:38:201056 continue
[email protected]8ea5d4b2011-09-13 21:49:221057
[email protected]a11dbe9b2012-08-07 01:32:581058 allowWString = False
[email protected]b5c24292011-11-28 14:38:201059 for line_num, line in f.ChangedContents():
[email protected]a11dbe9b2012-08-07 01:32:581060 if 'presubmit: allow wstring' in line:
1061 allowWString = True
1062 elif not allowWString and 'wstring' in line:
[email protected]55463aa62011-10-12 00:48:271063 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]a11dbe9b2012-08-07 01:32:581064 allowWString = False
1065 else:
1066 allowWString = False
[email protected]8ea5d4b2011-09-13 21:49:221067
[email protected]55463aa62011-10-12 00:48:271068 if not problems:
1069 return []
1070 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
[email protected]a11dbe9b2012-08-07 01:32:581071 ' If you are calling a cross-platform API that accepts a wstring, '
1072 'fix the API.\n' +
[email protected]55463aa62011-10-12 00:48:271073 '\n'.join(problems))]
[email protected]8ea5d4b2011-09-13 21:49:221074
1075
[email protected]2a8ac9c2011-10-19 17:20:441076def _CheckNoDEPSGIT(input_api, output_api):
1077 """Make sure .DEPS.git is never modified manually."""
1078 if any(f.LocalPath().endswith('.DEPS.git') for f in
1079 input_api.AffectedFiles()):
1080 return [output_api.PresubmitError(
1081 'Never commit changes to .DEPS.git. This file is maintained by an\n'
1082 'automated system based on what\'s in DEPS and your changes will be\n'
1083 'overwritten.\n'
Vaclav Brozekd5de76a2018-03-17 07:57:501084 'See https://sites.google.com/a/chromium.org/dev/developers/how-tos/'
1085 'get-the-code#Rolling_DEPS\n'
[email protected]2a8ac9c2011-10-19 17:20:441086 'for more information')]
1087 return []
1088
1089
tandriief664692014-09-23 14:51:471090def _CheckValidHostsInDEPS(input_api, output_api):
1091 """Checks that DEPS file deps are from allowed_hosts."""
1092 # Run only if DEPS file has been modified to annoy fewer bystanders.
1093 if all(f.LocalPath() != 'DEPS' for f in input_api.AffectedFiles()):
1094 return []
1095 # Outsource work to gclient verify
1096 try:
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:201097 input_api.subprocess.check_output(['gclient', 'verify'],
1098 stderr=input_api.subprocess.STDOUT)
tandriief664692014-09-23 14:51:471099 return []
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:201100 except input_api.subprocess.CalledProcessError as error:
tandriief664692014-09-23 14:51:471101 return [output_api.PresubmitError(
1102 'DEPS file must have only git dependencies.',
1103 long_text=error.output)]
1104
1105
[email protected]127f18ec2012-06-16 05:05:591106def _CheckNoBannedFunctions(input_api, output_api):
1107 """Make sure that banned functions are not used."""
1108 warnings = []
1109 errors = []
1110
wnwenbdc444e2016-05-25 13:44:151111 def IsBlacklisted(affected_file, blacklist):
1112 local_path = affected_file.LocalPath()
1113 for item in blacklist:
1114 if input_api.re.match(item, local_path):
1115 return True
1116 return False
1117
Sylvain Defresnea8b73d252018-02-28 15:45:541118 def IsIosObcjFile(affected_file):
1119 local_path = affected_file.LocalPath()
1120 if input_api.os_path.splitext(local_path)[-1] not in ('.mm', '.m', '.h'):
1121 return False
1122 basename = input_api.os_path.basename(local_path)
1123 if 'ios' in basename.split('_'):
1124 return True
1125 for sep in (input_api.os_path.sep, input_api.os_path.altsep):
1126 if sep and 'ios' in local_path.split(sep):
1127 return True
1128 return False
1129
wnwenbdc444e2016-05-25 13:44:151130 def CheckForMatch(affected_file, line_num, line, func_name, message, error):
1131 matched = False
1132 if func_name[0:1] == '/':
1133 regex = func_name[1:]
1134 if input_api.re.search(regex, line):
1135 matched = True
1136 elif func_name in line:
dchenge07de812016-06-20 19:27:171137 matched = True
wnwenbdc444e2016-05-25 13:44:151138 if matched:
dchenge07de812016-06-20 19:27:171139 problems = warnings
wnwenbdc444e2016-05-25 13:44:151140 if error:
dchenge07de812016-06-20 19:27:171141 problems = errors
wnwenbdc444e2016-05-25 13:44:151142 problems.append(' %s:%d:' % (affected_file.LocalPath(), line_num))
1143 for message_line in message:
1144 problems.append(' %s' % message_line)
1145
Eric Stevensona9a980972017-09-23 00:04:411146 file_filter = lambda f: f.LocalPath().endswith(('.java'))
1147 for f in input_api.AffectedFiles(file_filter=file_filter):
1148 for line_num, line in f.ChangedContents():
1149 for func_name, message, error in _BANNED_JAVA_FUNCTIONS:
1150 CheckForMatch(f, line_num, line, func_name, message, error)
1151
[email protected]127f18ec2012-06-16 05:05:591152 file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
1153 for f in input_api.AffectedFiles(file_filter=file_filter):
1154 for line_num, line in f.ChangedContents():
1155 for func_name, message, error in _BANNED_OBJC_FUNCTIONS:
wnwenbdc444e2016-05-25 13:44:151156 CheckForMatch(f, line_num, line, func_name, message, error)
[email protected]127f18ec2012-06-16 05:05:591157
Sylvain Defresnea8b73d252018-02-28 15:45:541158 for f in input_api.AffectedFiles(file_filter=IsIosObcjFile):
1159 for line_num, line in f.ChangedContents():
1160 for func_name, message, error in _BANNED_IOS_OBJC_FUNCTIONS:
1161 CheckForMatch(f, line_num, line, func_name, message, error)
1162
[email protected]127f18ec2012-06-16 05:05:591163 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
1164 for f in input_api.AffectedFiles(file_filter=file_filter):
1165 for line_num, line in f.ChangedContents():
[email protected]7345da02012-11-27 14:31:491166 for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS:
[email protected]7345da02012-11-27 14:31:491167 if IsBlacklisted(f, excluded_paths):
1168 continue
wnwenbdc444e2016-05-25 13:44:151169 CheckForMatch(f, line_num, line, func_name, message, error)
[email protected]127f18ec2012-06-16 05:05:591170
1171 result = []
1172 if (warnings):
1173 result.append(output_api.PresubmitPromptWarning(
1174 'Banned functions were used.\n' + '\n'.join(warnings)))
1175 if (errors):
1176 result.append(output_api.PresubmitError(
1177 'Banned functions were used.\n' + '\n'.join(errors)))
1178 return result
1179
1180
[email protected]6c063c62012-07-11 19:11:061181def _CheckNoPragmaOnce(input_api, output_api):
1182 """Make sure that banned functions are not used."""
1183 files = []
1184 pattern = input_api.re.compile(r'^#pragma\s+once',
1185 input_api.re.MULTILINE)
1186 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1187 if not f.LocalPath().endswith('.h'):
1188 continue
1189 contents = input_api.ReadFile(f)
1190 if pattern.search(contents):
1191 files.append(f)
1192
1193 if files:
1194 return [output_api.PresubmitError(
1195 'Do not use #pragma once in header files.\n'
1196 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
1197 files)]
1198 return []
1199
[email protected]127f18ec2012-06-16 05:05:591200
[email protected]e7479052012-09-19 00:26:121201def _CheckNoTrinaryTrueFalse(input_api, output_api):
1202 """Checks to make sure we don't introduce use of foo ? true : false."""
1203 problems = []
1204 pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)')
1205 for f in input_api.AffectedFiles():
1206 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
1207 continue
1208
1209 for line_num, line in f.ChangedContents():
1210 if pattern.match(line):
1211 problems.append(' %s:%d' % (f.LocalPath(), line_num))
1212
1213 if not problems:
1214 return []
1215 return [output_api.PresubmitPromptWarning(
1216 'Please consider avoiding the "? true : false" pattern if possible.\n' +
1217 '\n'.join(problems))]
1218
1219
[email protected]55f9f382012-07-31 11:02:181220def _CheckUnwantedDependencies(input_api, output_api):
rhalavati08acd232017-04-03 07:23:281221 """Runs checkdeps on #include and import statements added in this
[email protected]55f9f382012-07-31 11:02:181222 change. Breaking - rules is an error, breaking ! rules is a
1223 warning.
1224 """
mohan.reddyf21db962014-10-16 12:26:471225 import sys
[email protected]55f9f382012-07-31 11:02:181226 # We need to wait until we have an input_api object and use this
1227 # roundabout construct to import checkdeps because this file is
1228 # eval-ed and thus doesn't have __file__.
1229 original_sys_path = sys.path
1230 try:
1231 sys.path = sys.path + [input_api.os_path.join(
[email protected]5298cc982014-05-29 20:53:471232 input_api.PresubmitLocalPath(), 'buildtools', 'checkdeps')]
[email protected]55f9f382012-07-31 11:02:181233 import checkdeps
1234 from cpp_checker import CppChecker
Jinsuk Kim5a092672017-10-24 22:42:241235 from java_checker import JavaChecker
rhalavati08acd232017-04-03 07:23:281236 from proto_checker import ProtoChecker
[email protected]55f9f382012-07-31 11:02:181237 from rules import Rule
1238 finally:
1239 # Restore sys.path to what it was before.
1240 sys.path = original_sys_path
1241
1242 added_includes = []
rhalavati08acd232017-04-03 07:23:281243 added_imports = []
Jinsuk Kim5a092672017-10-24 22:42:241244 added_java_imports = []
[email protected]55f9f382012-07-31 11:02:181245 for f in input_api.AffectedFiles():
rhalavati08acd232017-04-03 07:23:281246 if CppChecker.IsCppFile(f.LocalPath()):
Vaclav Brozekd5de76a2018-03-17 07:57:501247 changed_lines = [line for _, line in f.ChangedContents()]
Andrew Grieve085f29f2017-11-02 09:14:081248 added_includes.append([f.AbsoluteLocalPath(), changed_lines])
rhalavati08acd232017-04-03 07:23:281249 elif ProtoChecker.IsProtoFile(f.LocalPath()):
Vaclav Brozekd5de76a2018-03-17 07:57:501250 changed_lines = [line for _, line in f.ChangedContents()]
Andrew Grieve085f29f2017-11-02 09:14:081251 added_imports.append([f.AbsoluteLocalPath(), changed_lines])
Jinsuk Kim5a092672017-10-24 22:42:241252 elif JavaChecker.IsJavaFile(f.LocalPath()):
Vaclav Brozekd5de76a2018-03-17 07:57:501253 changed_lines = [line for _, line in f.ChangedContents()]
Andrew Grieve085f29f2017-11-02 09:14:081254 added_java_imports.append([f.AbsoluteLocalPath(), changed_lines])
[email protected]55f9f382012-07-31 11:02:181255
[email protected]26385172013-05-09 23:11:351256 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
[email protected]55f9f382012-07-31 11:02:181257
1258 error_descriptions = []
1259 warning_descriptions = []
rhalavati08acd232017-04-03 07:23:281260 error_subjects = set()
1261 warning_subjects = set()
[email protected]55f9f382012-07-31 11:02:181262 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
1263 added_includes):
Andrew Grieve085f29f2017-11-02 09:14:081264 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
[email protected]55f9f382012-07-31 11:02:181265 description_with_path = '%s\n %s' % (path, rule_description)
1266 if rule_type == Rule.DISALLOW:
1267 error_descriptions.append(description_with_path)
rhalavati08acd232017-04-03 07:23:281268 error_subjects.add("#includes")
[email protected]55f9f382012-07-31 11:02:181269 else:
1270 warning_descriptions.append(description_with_path)
rhalavati08acd232017-04-03 07:23:281271 warning_subjects.add("#includes")
1272
1273 for path, rule_type, rule_description in deps_checker.CheckAddedProtoImports(
1274 added_imports):
Andrew Grieve085f29f2017-11-02 09:14:081275 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
rhalavati08acd232017-04-03 07:23:281276 description_with_path = '%s\n %s' % (path, rule_description)
1277 if rule_type == Rule.DISALLOW:
1278 error_descriptions.append(description_with_path)
1279 error_subjects.add("imports")
1280 else:
1281 warning_descriptions.append(description_with_path)
1282 warning_subjects.add("imports")
[email protected]55f9f382012-07-31 11:02:181283
Jinsuk Kim5a092672017-10-24 22:42:241284 for path, rule_type, rule_description in deps_checker.CheckAddedJavaImports(
Shenghua Zhangbfaa38b82017-11-16 21:58:021285 added_java_imports, _JAVA_MULTIPLE_DEFINITION_EXCLUDED_PATHS):
Andrew Grieve085f29f2017-11-02 09:14:081286 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
Jinsuk Kim5a092672017-10-24 22:42:241287 description_with_path = '%s\n %s' % (path, rule_description)
1288 if rule_type == Rule.DISALLOW:
1289 error_descriptions.append(description_with_path)
1290 error_subjects.add("imports")
1291 else:
1292 warning_descriptions.append(description_with_path)
1293 warning_subjects.add("imports")
1294
[email protected]55f9f382012-07-31 11:02:181295 results = []
1296 if error_descriptions:
1297 results.append(output_api.PresubmitError(
rhalavati08acd232017-04-03 07:23:281298 'You added one or more %s that violate checkdeps rules.'
1299 % " and ".join(error_subjects),
[email protected]55f9f382012-07-31 11:02:181300 error_descriptions))
1301 if warning_descriptions:
[email protected]f7051d52013-04-02 18:31:421302 results.append(output_api.PresubmitPromptOrNotify(
rhalavati08acd232017-04-03 07:23:281303 'You added one or more %s of files that are temporarily\n'
[email protected]55f9f382012-07-31 11:02:181304 'allowed but being removed. Can you avoid introducing the\n'
rhalavati08acd232017-04-03 07:23:281305 '%s? See relevant DEPS file(s) for details and contacts.' %
1306 (" and ".join(warning_subjects), "/".join(warning_subjects)),
[email protected]55f9f382012-07-31 11:02:181307 warning_descriptions))
1308 return results
1309
1310
[email protected]fbcafe5a2012-08-08 15:31:221311def _CheckFilePermissions(input_api, output_api):
1312 """Check that all files have their permissions properly set."""
[email protected]791507202014-02-03 23:19:151313 if input_api.platform == 'win32':
1314 return []
raphael.kubo.da.costac1d13e60b2016-04-01 11:49:291315 checkperms_tool = input_api.os_path.join(
1316 input_api.PresubmitLocalPath(),
1317 'tools', 'checkperms', 'checkperms.py')
1318 args = [input_api.python_executable, checkperms_tool,
mohan.reddyf21db962014-10-16 12:26:471319 '--root', input_api.change.RepositoryRoot()]
Raphael Kubo da Costa6ff391d2017-11-13 16:43:391320 with input_api.CreateTemporaryFile() as file_list:
1321 for f in input_api.AffectedFiles():
1322 # checkperms.py file/directory arguments must be relative to the
1323 # repository.
1324 file_list.write(f.LocalPath() + '\n')
1325 file_list.close()
1326 args += ['--file-list', file_list.name]
1327 try:
1328 input_api.subprocess.check_output(args)
1329 return []
1330 except input_api.subprocess.CalledProcessError as error:
1331 return [output_api.PresubmitError(
1332 'checkperms.py failed:',
1333 long_text=error.output)]
[email protected]fbcafe5a2012-08-08 15:31:221334
1335
robertocn832f5992017-01-04 19:01:301336def _CheckTeamTags(input_api, output_api):
1337 """Checks that OWNERS files have consistent TEAM and COMPONENT tags."""
1338 checkteamtags_tool = input_api.os_path.join(
1339 input_api.PresubmitLocalPath(),
1340 'tools', 'checkteamtags', 'checkteamtags.py')
1341 args = [input_api.python_executable, checkteamtags_tool,
1342 '--root', input_api.change.RepositoryRoot()]
robertocn5eb82312017-01-09 20:27:221343 files = [f.LocalPath() for f in input_api.AffectedFiles(include_deletes=False)
robertocn832f5992017-01-04 19:01:301344 if input_api.os_path.basename(f.AbsoluteLocalPath()).upper() ==
1345 'OWNERS']
1346 try:
1347 if files:
1348 input_api.subprocess.check_output(args + files)
1349 return []
1350 except input_api.subprocess.CalledProcessError as error:
1351 return [output_api.PresubmitError(
1352 'checkteamtags.py failed:',
1353 long_text=error.output)]
1354
1355
[email protected]c8278b32012-10-30 20:35:491356def _CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
1357 """Makes sure we don't include ui/aura/window_property.h
1358 in header files.
1359 """
1360 pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
1361 errors = []
1362 for f in input_api.AffectedFiles():
1363 if not f.LocalPath().endswith('.h'):
1364 continue
1365 for line_num, line in f.ChangedContents():
1366 if pattern.match(line):
1367 errors.append(' %s:%d' % (f.LocalPath(), line_num))
1368
1369 results = []
1370 if errors:
1371 results.append(output_api.PresubmitError(
1372 'Header files should not include ui/aura/window_property.h', errors))
1373 return results
1374
1375
[email protected]70ca77752012-11-20 03:45:031376def _CheckForVersionControlConflictsInFile(input_api, f):
1377 pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
1378 errors = []
1379 for line_num, line in f.ChangedContents():
dbeam95c35a2f2015-06-02 01:40:231380 if f.LocalPath().endswith('.md'):
1381 # First-level headers in markdown look a lot like version control
1382 # conflict markers. http://daringfireball.net/projects/markdown/basics
1383 continue
[email protected]70ca77752012-11-20 03:45:031384 if pattern.match(line):
1385 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
1386 return errors
1387
1388
1389def _CheckForVersionControlConflicts(input_api, output_api):
1390 """Usually this is not intentional and will cause a compile failure."""
1391 errors = []
1392 for f in input_api.AffectedFiles():
1393 errors.extend(_CheckForVersionControlConflictsInFile(input_api, f))
1394
1395 results = []
1396 if errors:
1397 results.append(output_api.PresubmitError(
1398 'Version control conflict markers found, please resolve.', errors))
1399 return results
1400
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:201401
estadee17314a02017-01-12 16:22:161402def _CheckGoogleSupportAnswerUrl(input_api, output_api):
1403 pattern = input_api.re.compile('support\.google\.com\/chrome.*/answer')
1404 errors = []
1405 for f in input_api.AffectedFiles():
1406 for line_num, line in f.ChangedContents():
1407 if pattern.search(line):
1408 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
1409
1410 results = []
1411 if errors:
1412 results.append(output_api.PresubmitPromptWarning(
Vaclav Brozekd5de76a2018-03-17 07:57:501413 'Found Google support URL addressed by answer number. Please replace '
1414 'with a p= identifier instead. See crbug.com/679462\n', errors))
estadee17314a02017-01-12 16:22:161415 return results
1416
[email protected]70ca77752012-11-20 03:45:031417
[email protected]06e6d0ff2012-12-11 01:36:441418def _CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api):
1419 def FilterFile(affected_file):
1420 """Filter function for use with input_api.AffectedSourceFiles,
1421 below. This filters out everything except non-test files from
1422 top-level directories that generally speaking should not hard-code
1423 service URLs (e.g. src/android_webview/, src/content/ and others).
1424 """
1425 return input_api.FilterSourceFile(
1426 affected_file,
Egor Paskoce145c42018-09-28 19:31:041427 white_list=[r'^(android_webview|base|content|net)[\\/].*'],
[email protected]06e6d0ff2012-12-11 01:36:441428 black_list=(_EXCLUDED_PATHS +
1429 _TEST_CODE_EXCLUDED_PATHS +
1430 input_api.DEFAULT_BLACK_LIST))
1431
reillyi38965732015-11-16 18:27:331432 base_pattern = ('"[^"]*(google|googleapis|googlezip|googledrive|appspot)'
1433 '\.(com|net)[^"]*"')
[email protected]de4f7d22013-05-23 14:27:461434 comment_pattern = input_api.re.compile('//.*%s' % base_pattern)
1435 pattern = input_api.re.compile(base_pattern)
[email protected]06e6d0ff2012-12-11 01:36:441436 problems = [] # items are (filename, line_number, line)
1437 for f in input_api.AffectedSourceFiles(FilterFile):
1438 for line_num, line in f.ChangedContents():
[email protected]de4f7d22013-05-23 14:27:461439 if not comment_pattern.search(line) and pattern.search(line):
[email protected]06e6d0ff2012-12-11 01:36:441440 problems.append((f.LocalPath(), line_num, line))
1441
1442 if problems:
[email protected]f7051d52013-04-02 18:31:421443 return [output_api.PresubmitPromptOrNotify(
[email protected]06e6d0ff2012-12-11 01:36:441444 'Most layers below src/chrome/ should not hardcode service URLs.\n'
[email protected]b0149772014-03-27 16:47:581445 'Are you sure this is correct?',
[email protected]06e6d0ff2012-12-11 01:36:441446 [' %s:%d: %s' % (
1447 problem[0], problem[1], problem[2]) for problem in problems])]
[email protected]2fdd1f362013-01-16 03:56:031448 else:
1449 return []
[email protected]06e6d0ff2012-12-11 01:36:441450
1451
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491452# TODO: add unit tests.
[email protected]d2530012013-01-25 16:39:271453def _CheckNoAbbreviationInPngFileName(input_api, output_api):
1454 """Makes sure there are no abbreviations in the name of PNG files.
binji0dcdf342014-12-12 18:32:311455 The native_client_sdk directory is excluded because it has auto-generated PNG
1456 files for documentation.
[email protected]d2530012013-01-25 16:39:271457 """
[email protected]d2530012013-01-25 16:39:271458 errors = []
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491459 white_list = [r'.*_[a-z]_.*\.png$|.*_[a-z]\.png$']
Egor Paskoce145c42018-09-28 19:31:041460 black_list = [r'^native_client_sdk[\\/]']
binji0dcdf342014-12-12 18:32:311461 file_filter = lambda f: input_api.FilterSourceFile(
1462 f, white_list=white_list, black_list=black_list)
1463 for f in input_api.AffectedFiles(include_deletes=False,
1464 file_filter=file_filter):
1465 errors.append(' %s' % f.LocalPath())
[email protected]d2530012013-01-25 16:39:271466
1467 results = []
1468 if errors:
1469 results.append(output_api.PresubmitError(
1470 'The name of PNG files should not have abbreviations. \n'
1471 'Use _hover.png, _center.png, instead of _h.png, _c.png.\n'
1472 'Contact [email protected] if you have questions.', errors))
1473 return results
1474
1475
Daniel Cheng4dcdb6b2017-04-13 08:30:171476def _ExtractAddRulesFromParsedDeps(parsed_deps):
1477 """Extract the rules that add dependencies from a parsed DEPS file.
1478
1479 Args:
1480 parsed_deps: the locals dictionary from evaluating the DEPS file."""
1481 add_rules = set()
1482 add_rules.update([
1483 rule[1:] for rule in parsed_deps.get('include_rules', [])
1484 if rule.startswith('+') or rule.startswith('!')
1485 ])
Vaclav Brozekd5de76a2018-03-17 07:57:501486 for _, rules in parsed_deps.get('specific_include_rules',
Daniel Cheng4dcdb6b2017-04-13 08:30:171487 {}).iteritems():
1488 add_rules.update([
1489 rule[1:] for rule in rules
1490 if rule.startswith('+') or rule.startswith('!')
1491 ])
1492 return add_rules
1493
1494
1495def _ParseDeps(contents):
1496 """Simple helper for parsing DEPS files."""
1497 # Stubs for handling special syntax in the root DEPS file.
Daniel Cheng4dcdb6b2017-04-13 08:30:171498 class _VarImpl:
1499
1500 def __init__(self, local_scope):
1501 self._local_scope = local_scope
1502
1503 def Lookup(self, var_name):
1504 """Implements the Var syntax."""
1505 try:
1506 return self._local_scope['vars'][var_name]
1507 except KeyError:
1508 raise Exception('Var is not defined: %s' % var_name)
1509
1510 local_scope = {}
1511 global_scope = {
Daniel Cheng4dcdb6b2017-04-13 08:30:171512 'Var': _VarImpl(local_scope).Lookup,
1513 }
1514 exec contents in global_scope, local_scope
1515 return local_scope
1516
1517
1518def _CalculateAddedDeps(os_path, old_contents, new_contents):
[email protected]f32e2d1e2013-07-26 21:39:081519 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
[email protected]14a6131c2014-01-08 01:15:411520 a set of DEPS entries that we should look up.
1521
1522 For a directory (rather than a specific filename) we fake a path to
1523 a specific filename by adding /DEPS. This is chosen as a file that
1524 will seldom or never be subject to per-file include_rules.
1525 """
[email protected]2b438d62013-11-14 17:54:141526 # We ignore deps entries on auto-generated directories.
1527 AUTO_GENERATED_DIRS = ['grit', 'jni']
[email protected]f32e2d1e2013-07-26 21:39:081528
Daniel Cheng4dcdb6b2017-04-13 08:30:171529 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1530 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
1531
1532 added_deps = new_deps.difference(old_deps)
1533
[email protected]2b438d62013-11-14 17:54:141534 results = set()
Daniel Cheng4dcdb6b2017-04-13 08:30:171535 for added_dep in added_deps:
1536 if added_dep.split('/')[0] in AUTO_GENERATED_DIRS:
1537 continue
1538 # Assume that a rule that ends in .h is a rule for a specific file.
1539 if added_dep.endswith('.h'):
1540 results.add(added_dep)
1541 else:
1542 results.add(os_path.join(added_dep, 'DEPS'))
[email protected]f32e2d1e2013-07-26 21:39:081543 return results
1544
1545
[email protected]e871964c2013-05-13 14:14:551546def _CheckAddedDepsHaveTargetApprovals(input_api, output_api):
1547 """When a dependency prefixed with + is added to a DEPS file, we
1548 want to make sure that the change is reviewed by an OWNER of the
1549 target file or directory, to avoid layering violations from being
1550 introduced. This check verifies that this happens.
1551 """
Daniel Cheng4dcdb6b2017-04-13 08:30:171552 virtual_depended_on_files = set()
jochen53efcdd2016-01-29 05:09:241553
1554 file_filter = lambda f: not input_api.re.match(
Kent Tamura32dbbcb2018-11-30 12:28:491555 r"^third_party[\\/]blink[\\/].*", f.LocalPath())
jochen53efcdd2016-01-29 05:09:241556 for f in input_api.AffectedFiles(include_deletes=False,
1557 file_filter=file_filter):
[email protected]e871964c2013-05-13 14:14:551558 filename = input_api.os_path.basename(f.LocalPath())
1559 if filename == 'DEPS':
Daniel Cheng4dcdb6b2017-04-13 08:30:171560 virtual_depended_on_files.update(_CalculateAddedDeps(
1561 input_api.os_path,
1562 '\n'.join(f.OldContents()),
1563 '\n'.join(f.NewContents())))
[email protected]e871964c2013-05-13 14:14:551564
[email protected]e871964c2013-05-13 14:14:551565 if not virtual_depended_on_files:
1566 return []
1567
1568 if input_api.is_committing:
1569 if input_api.tbr:
1570 return [output_api.PresubmitNotifyResult(
1571 '--tbr was specified, skipping OWNERS check for DEPS additions')]
Paweł Hajdan, Jrbe6739ea2016-04-28 15:07:271572 if input_api.dry_run:
1573 return [output_api.PresubmitNotifyResult(
1574 'This is a dry run, skipping OWNERS check for DEPS additions')]
[email protected]e871964c2013-05-13 14:14:551575 if not input_api.change.issue:
1576 return [output_api.PresubmitError(
1577 "DEPS approval by OWNERS check failed: this change has "
Aaron Gable65a99d92017-10-09 19:17:401578 "no change number, so we can't check it for approvals.")]
[email protected]e871964c2013-05-13 14:14:551579 output = output_api.PresubmitError
1580 else:
1581 output = output_api.PresubmitNotifyResult
1582
1583 owners_db = input_api.owners_db
tandriied3b7e12016-05-12 14:38:501584 owner_email, reviewers = (
1585 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1586 input_api,
1587 owners_db.email_regexp,
1588 approval_needed=input_api.is_committing))
[email protected]e871964c2013-05-13 14:14:551589
1590 owner_email = owner_email or input_api.change.author_email
1591
[email protected]de4f7d22013-05-23 14:27:461592 reviewers_plus_owner = set(reviewers)
[email protected]e71c6082013-05-22 02:28:511593 if owner_email:
[email protected]de4f7d22013-05-23 14:27:461594 reviewers_plus_owner.add(owner_email)
[email protected]e871964c2013-05-13 14:14:551595 missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
1596 reviewers_plus_owner)
[email protected]14a6131c2014-01-08 01:15:411597
1598 # We strip the /DEPS part that was added by
1599 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1600 # directory.
1601 def StripDeps(path):
1602 start_deps = path.rfind('/DEPS')
1603 if start_deps != -1:
1604 return path[:start_deps]
1605 else:
1606 return path
1607 unapproved_dependencies = ["'+%s'," % StripDeps(path)
[email protected]e871964c2013-05-13 14:14:551608 for path in missing_files]
1609
1610 if unapproved_dependencies:
1611 output_list = [
Paweł Hajdan, Jrec17f882016-07-04 14:16:151612 output('You need LGTM from owners of depends-on paths in DEPS that were '
1613 'modified in this CL:\n %s' %
1614 '\n '.join(sorted(unapproved_dependencies)))]
1615 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
1616 output_list.append(output(
1617 'Suggested missing target path OWNERS:\n %s' %
1618 '\n '.join(suggested_owners or [])))
[email protected]e871964c2013-05-13 14:14:551619 return output_list
1620
1621 return []
1622
1623
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491624# TODO: add unit tests.
[email protected]85218562013-11-22 07:41:401625def _CheckSpamLogging(input_api, output_api):
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491626 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
[email protected]85218562013-11-22 07:41:401627 black_list = (_EXCLUDED_PATHS +
1628 _TEST_CODE_EXCLUDED_PATHS +
1629 input_api.DEFAULT_BLACK_LIST +
Egor Paskoce145c42018-09-28 19:31:041630 (r"^base[\\/]logging\.h$",
1631 r"^base[\\/]logging\.cc$",
1632 r"^chrome[\\/]app[\\/]chrome_main_delegate\.cc$",
1633 r"^chrome[\\/]browser[\\/]chrome_browser_main\.cc$",
1634 r"^chrome[\\/]browser[\\/]ui[\\/]startup[\\/]"
[email protected]4de75262013-12-18 23:16:121635 r"startup_browser_creator\.cc$",
Egor Paskoce145c42018-09-28 19:31:041636 r"^chrome[\\/]installer[\\/]setup[\\/].*",
1637 r"^chrome[\\/]chrome_cleaner[\\/].*",
1638 r"chrome[\\/]browser[\\/]diagnostics[\\/]" +
[email protected]f5b9a3f342014-08-08 22:06:031639 r"diagnostics_writer\.cc$",
Egor Paskoce145c42018-09-28 19:31:041640 r"^chrome_elf[\\/]dll_hash[\\/]dll_hash_main\.cc$",
1641 r"^chromecast[\\/]",
1642 r"^cloud_print[\\/]",
1643 r"^components[\\/]browser_watcher[\\/]"
manzagop85e629e2017-05-09 22:11:481644 r"dump_stability_report_main_win.cc$",
Egor Paskoce145c42018-09-28 19:31:041645 r"^components[\\/]html_viewer[\\/]"
jochen34415e52015-07-10 08:34:311646 r"web_test_delegate_impl\.cc$",
Egor Paskoce145c42018-09-28 19:31:041647 r"^components[\\/]zucchini[\\/].*",
peter80739bb2015-10-20 11:17:461648 # TODO(peter): Remove this exception. https://crbug.com/534537
Egor Paskoce145c42018-09-28 19:31:041649 r"^content[\\/]browser[\\/]notifications[\\/]"
peter80739bb2015-10-20 11:17:461650 r"notification_event_dispatcher_impl\.cc$",
Egor Paskoce145c42018-09-28 19:31:041651 r"^content[\\/]common[\\/]gpu[\\/]client[\\/]"
[email protected]9056e732014-01-08 06:25:251652 r"gl_helper_benchmark\.cc$",
Egor Paskoce145c42018-09-28 19:31:041653 r"^courgette[\\/]courgette_minimal_tool\.cc$",
1654 r"^courgette[\\/]courgette_tool\.cc$",
1655 r"^extensions[\\/]renderer[\\/]logging_native_handler\.cc$",
1656 r"^ipc[\\/]ipc_logging\.cc$",
1657 r"^native_client_sdk[\\/]",
1658 r"^remoting[\\/]base[\\/]logging\.h$",
1659 r"^remoting[\\/]host[\\/].*",
1660 r"^sandbox[\\/]linux[\\/].*",
1661 r"^tools[\\/]",
1662 r"^ui[\\/]base[\\/]resource[\\/]data_pack.cc$",
1663 r"^ui[\\/]aura[\\/]bench[\\/]bench_main\.cc$",
1664 r"^ui[\\/]ozone[\\/]platform[\\/]cast[\\/]",
Kevin Marshalla139bcc2018-11-09 02:26:361665 r"^webrunner[\\/]browser[\\/]frame_impl.cc$",
Egor Paskoce145c42018-09-28 19:31:041666 r"^storage[\\/]browser[\\/]fileapi[\\/]" +
skyostil87681be82016-12-19 12:46:351667 r"dump_file_system.cc$",
Egor Paskoce145c42018-09-28 19:31:041668 r"^headless[\\/]app[\\/]headless_shell\.cc$"))
[email protected]85218562013-11-22 07:41:401669 source_file_filter = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491670 x, white_list=file_inclusion_pattern, black_list=black_list)
[email protected]85218562013-11-22 07:41:401671
thomasanderson625d3932017-03-29 07:16:581672 log_info = set([])
1673 printf = set([])
[email protected]85218562013-11-22 07:41:401674
1675 for f in input_api.AffectedSourceFiles(source_file_filter):
thomasanderson625d3932017-03-29 07:16:581676 for _, line in f.ChangedContents():
1677 if input_api.re.search(r"\bD?LOG\s*\(\s*INFO\s*\)", line):
1678 log_info.add(f.LocalPath())
1679 elif input_api.re.search(r"\bD?LOG_IF\s*\(\s*INFO\s*,", line):
1680 log_info.add(f.LocalPath())
[email protected]18b466b2013-12-02 22:01:371681
thomasanderson625d3932017-03-29 07:16:581682 if input_api.re.search(r"\bprintf\(", line):
1683 printf.add(f.LocalPath())
1684 elif input_api.re.search(r"\bfprintf\((stdout|stderr)", line):
1685 printf.add(f.LocalPath())
[email protected]85218562013-11-22 07:41:401686
1687 if log_info:
1688 return [output_api.PresubmitError(
1689 'These files spam the console log with LOG(INFO):',
1690 items=log_info)]
1691 if printf:
1692 return [output_api.PresubmitError(
1693 'These files spam the console log with printf/fprintf:',
1694 items=printf)]
1695 return []
1696
1697
[email protected]49aa76a2013-12-04 06:59:161698def _CheckForAnonymousVariables(input_api, output_api):
1699 """These types are all expected to hold locks while in scope and
1700 so should never be anonymous (which causes them to be immediately
1701 destroyed)."""
1702 they_who_must_be_named = [
1703 'base::AutoLock',
1704 'base::AutoReset',
1705 'base::AutoUnlock',
1706 'SkAutoAlphaRestore',
1707 'SkAutoBitmapShaderInstall',
1708 'SkAutoBlitterChoose',
1709 'SkAutoBounderCommit',
1710 'SkAutoCallProc',
1711 'SkAutoCanvasRestore',
1712 'SkAutoCommentBlock',
1713 'SkAutoDescriptor',
1714 'SkAutoDisableDirectionCheck',
1715 'SkAutoDisableOvalCheck',
1716 'SkAutoFree',
1717 'SkAutoGlyphCache',
1718 'SkAutoHDC',
1719 'SkAutoLockColors',
1720 'SkAutoLockPixels',
1721 'SkAutoMalloc',
1722 'SkAutoMaskFreeImage',
1723 'SkAutoMutexAcquire',
1724 'SkAutoPathBoundsUpdate',
1725 'SkAutoPDFRelease',
1726 'SkAutoRasterClipValidate',
1727 'SkAutoRef',
1728 'SkAutoTime',
1729 'SkAutoTrace',
1730 'SkAutoUnref',
1731 ]
1732 anonymous = r'(%s)\s*[({]' % '|'.join(they_who_must_be_named)
1733 # bad: base::AutoLock(lock.get());
1734 # not bad: base::AutoLock lock(lock.get());
1735 bad_pattern = input_api.re.compile(anonymous)
1736 # good: new base::AutoLock(lock.get())
1737 good_pattern = input_api.re.compile(r'\bnew\s*' + anonymous)
1738 errors = []
1739
1740 for f in input_api.AffectedFiles():
1741 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
1742 continue
1743 for linenum, line in f.ChangedContents():
1744 if bad_pattern.search(line) and not good_pattern.search(line):
1745 errors.append('%s:%d' % (f.LocalPath(), linenum))
1746
1747 if errors:
1748 return [output_api.PresubmitError(
1749 'These lines create anonymous variables that need to be named:',
1750 items=errors)]
1751 return []
1752
1753
Peter Kasting4844e46e2018-02-23 07:27:101754def _CheckUniquePtr(input_api, output_api):
Vaclav Brozekb7fadb692018-08-30 06:39:531755 # Returns whether |template_str| is of the form <T, U...> for some types T
1756 # and U. Assumes that |template_str| is already in the form <...>.
1757 def HasMoreThanOneArg(template_str):
1758 # Level of <...> nesting.
1759 nesting = 0
1760 for c in template_str:
1761 if c == '<':
1762 nesting += 1
1763 elif c == '>':
1764 nesting -= 1
1765 elif c == ',' and nesting == 1:
1766 return True
1767 return False
1768
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491769 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
Peter Kasting4844e46e2018-02-23 07:27:101770 sources = lambda affected_file: input_api.FilterSourceFile(
1771 affected_file,
1772 black_list=(_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
1773 input_api.DEFAULT_BLACK_LIST),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491774 white_list=file_inclusion_pattern)
Vaclav Brozeka54c528b2018-04-06 19:23:551775
1776 # Pattern to capture a single "<...>" block of template arguments. It can
1777 # handle linearly nested blocks, such as "<std::vector<std::set<T>>>", but
1778 # cannot handle branching structures, such as "<pair<set<T>,set<U>>". The
1779 # latter would likely require counting that < and > match, which is not
1780 # expressible in regular languages. Should the need arise, one can introduce
1781 # limited counting (matching up to a total number of nesting depth), which
1782 # should cover all practical cases for already a low nesting limit.
1783 template_arg_pattern = (
1784 r'<[^>]*' # Opening block of <.
1785 r'>([^<]*>)?') # Closing block of >.
1786 # Prefix expressing that whatever follows is not already inside a <...>
1787 # block.
1788 not_inside_template_arg_pattern = r'(^|[^<,\s]\s*)'
Peter Kasting4844e46e2018-02-23 07:27:101789 null_construct_pattern = input_api.re.compile(
Vaclav Brozeka54c528b2018-04-06 19:23:551790 not_inside_template_arg_pattern
1791 + r'\bstd::unique_ptr'
1792 + template_arg_pattern
1793 + r'\(\)')
1794
1795 # Same as template_arg_pattern, but excluding type arrays, e.g., <T[]>.
1796 template_arg_no_array_pattern = (
1797 r'<[^>]*[^]]' # Opening block of <.
1798 r'>([^(<]*[^]]>)?') # Closing block of >.
1799 # Prefix saying that what follows is the start of an expression.
1800 start_of_expr_pattern = r'(=|\breturn|^)\s*'
1801 # Suffix saying that what follows are call parentheses with a non-empty list
1802 # of arguments.
1803 nonempty_arg_list_pattern = r'\(([^)]|$)'
Vaclav Brozekb7fadb692018-08-30 06:39:531804 # Put the template argument into a capture group for deeper examination later.
Vaclav Brozeka54c528b2018-04-06 19:23:551805 return_construct_pattern = input_api.re.compile(
1806 start_of_expr_pattern
1807 + r'std::unique_ptr'
Vaclav Brozekb7fadb692018-08-30 06:39:531808 + '(?P<template_arg>'
Vaclav Brozeka54c528b2018-04-06 19:23:551809 + template_arg_no_array_pattern
Vaclav Brozekb7fadb692018-08-30 06:39:531810 + ')'
Vaclav Brozeka54c528b2018-04-06 19:23:551811 + nonempty_arg_list_pattern)
1812
Vaclav Brozek851d9602018-04-04 16:13:051813 problems_constructor = []
1814 problems_nullptr = []
Peter Kasting4844e46e2018-02-23 07:27:101815 for f in input_api.AffectedSourceFiles(sources):
1816 for line_number, line in f.ChangedContents():
1817 # Disallow:
1818 # return std::unique_ptr<T>(foo);
1819 # bar = std::unique_ptr<T>(foo);
1820 # But allow:
1821 # return std::unique_ptr<T[]>(foo);
1822 # bar = std::unique_ptr<T[]>(foo);
Vaclav Brozekb7fadb692018-08-30 06:39:531823 # And also allow cases when the second template argument is present. Those
1824 # cases cannot be handled by std::make_unique:
1825 # return std::unique_ptr<T, U>(foo);
1826 # bar = std::unique_ptr<T, U>(foo);
Vaclav Brozek851d9602018-04-04 16:13:051827 local_path = f.LocalPath()
Vaclav Brozekb7fadb692018-08-30 06:39:531828 return_construct_result = return_construct_pattern.search(line)
1829 if return_construct_result and not HasMoreThanOneArg(
1830 return_construct_result.group('template_arg')):
Vaclav Brozek851d9602018-04-04 16:13:051831 problems_constructor.append(
1832 '%s:%d\n %s' % (local_path, line_number, line.strip()))
Peter Kasting4844e46e2018-02-23 07:27:101833 # Disallow:
1834 # std::unique_ptr<T>()
1835 if null_construct_pattern.search(line):
Vaclav Brozek851d9602018-04-04 16:13:051836 problems_nullptr.append(
1837 '%s:%d\n %s' % (local_path, line_number, line.strip()))
1838
1839 errors = []
Vaclav Brozekc2fecf42018-04-06 16:40:161840 if problems_nullptr:
Vaclav Brozek851d9602018-04-04 16:13:051841 errors.append(output_api.PresubmitError(
1842 'The following files use std::unique_ptr<T>(). Use nullptr instead.',
Vaclav Brozekc2fecf42018-04-06 16:40:161843 problems_nullptr))
1844 if problems_constructor:
Vaclav Brozek851d9602018-04-04 16:13:051845 errors.append(output_api.PresubmitError(
1846 'The following files use explicit std::unique_ptr constructor.'
1847 'Use std::make_unique<T>() instead.',
Vaclav Brozekc2fecf42018-04-06 16:40:161848 problems_constructor))
Peter Kasting4844e46e2018-02-23 07:27:101849 return errors
1850
1851
[email protected]999261d2014-03-03 20:08:081852def _CheckUserActionUpdate(input_api, output_api):
1853 """Checks if any new user action has been added."""
[email protected]2f92dec2014-03-07 19:21:521854 if any('actions.xml' == input_api.os_path.basename(f) for f in
[email protected]999261d2014-03-03 20:08:081855 input_api.LocalPaths()):
[email protected]2f92dec2014-03-07 19:21:521856 # If actions.xml is already included in the changelist, the PRESUBMIT
1857 # for actions.xml will do a more complete presubmit check.
[email protected]999261d2014-03-03 20:08:081858 return []
1859
[email protected]999261d2014-03-03 20:08:081860 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm'))
1861 action_re = r'[^a-zA-Z]UserMetricsAction\("([^"]*)'
[email protected]2f92dec2014-03-07 19:21:521862 current_actions = None
[email protected]999261d2014-03-03 20:08:081863 for f in input_api.AffectedFiles(file_filter=file_filter):
1864 for line_num, line in f.ChangedContents():
1865 match = input_api.re.search(action_re, line)
1866 if match:
[email protected]2f92dec2014-03-07 19:21:521867 # Loads contents in tools/metrics/actions/actions.xml to memory. It's
1868 # loaded only once.
1869 if not current_actions:
1870 with open('tools/metrics/actions/actions.xml') as actions_f:
1871 current_actions = actions_f.read()
1872 # Search for the matched user action name in |current_actions|.
[email protected]999261d2014-03-03 20:08:081873 for action_name in match.groups():
[email protected]2f92dec2014-03-07 19:21:521874 action = 'name="{0}"'.format(action_name)
1875 if action not in current_actions:
[email protected]999261d2014-03-03 20:08:081876 return [output_api.PresubmitPromptWarning(
1877 'File %s line %d: %s is missing in '
[email protected]2f92dec2014-03-07 19:21:521878 'tools/metrics/actions/actions.xml. Please run '
1879 'tools/metrics/actions/extract_actions.py to update.'
[email protected]999261d2014-03-03 20:08:081880 % (f.LocalPath(), line_num, action_name))]
1881 return []
1882
1883
Daniel Cheng13ca61a882017-08-25 15:11:251884def _ImportJSONCommentEater(input_api):
1885 import sys
1886 sys.path = sys.path + [input_api.os_path.join(
1887 input_api.PresubmitLocalPath(),
1888 'tools', 'json_comment_eater')]
1889 import json_comment_eater
1890 return json_comment_eater
1891
1892
[email protected]99171a92014-06-03 08:44:471893def _GetJSONParseError(input_api, filename, eat_comments=True):
1894 try:
1895 contents = input_api.ReadFile(filename)
1896 if eat_comments:
Daniel Cheng13ca61a882017-08-25 15:11:251897 json_comment_eater = _ImportJSONCommentEater(input_api)
plundblad1f5a4509f2015-07-23 11:31:131898 contents = json_comment_eater.Nom(contents)
[email protected]99171a92014-06-03 08:44:471899
1900 input_api.json.loads(contents)
1901 except ValueError as e:
1902 return e
1903 return None
1904
1905
1906def _GetIDLParseError(input_api, filename):
1907 try:
1908 contents = input_api.ReadFile(filename)
1909 idl_schema = input_api.os_path.join(
1910 input_api.PresubmitLocalPath(),
1911 'tools', 'json_schema_compiler', 'idl_schema.py')
1912 process = input_api.subprocess.Popen(
1913 [input_api.python_executable, idl_schema],
1914 stdin=input_api.subprocess.PIPE,
1915 stdout=input_api.subprocess.PIPE,
1916 stderr=input_api.subprocess.PIPE,
1917 universal_newlines=True)
1918 (_, error) = process.communicate(input=contents)
1919 return error or None
1920 except ValueError as e:
1921 return e
1922
1923
1924def _CheckParseErrors(input_api, output_api):
1925 """Check that IDL and JSON files do not contain syntax errors."""
1926 actions = {
1927 '.idl': _GetIDLParseError,
1928 '.json': _GetJSONParseError,
1929 }
[email protected]99171a92014-06-03 08:44:471930 # Most JSON files are preprocessed and support comments, but these do not.
1931 json_no_comments_patterns = [
Egor Paskoce145c42018-09-28 19:31:041932 r'^testing[\\/]',
[email protected]99171a92014-06-03 08:44:471933 ]
1934 # Only run IDL checker on files in these directories.
1935 idl_included_patterns = [
Egor Paskoce145c42018-09-28 19:31:041936 r'^chrome[\\/]common[\\/]extensions[\\/]api[\\/]',
1937 r'^extensions[\\/]common[\\/]api[\\/]',
[email protected]99171a92014-06-03 08:44:471938 ]
1939
1940 def get_action(affected_file):
1941 filename = affected_file.LocalPath()
1942 return actions.get(input_api.os_path.splitext(filename)[1])
1943
[email protected]99171a92014-06-03 08:44:471944 def FilterFile(affected_file):
1945 action = get_action(affected_file)
1946 if not action:
1947 return False
1948 path = affected_file.LocalPath()
1949
Sean Kau46e29bc2017-08-28 16:31:161950 if _MatchesFile(input_api, _KNOWN_INVALID_JSON_FILE_PATTERNS, path):
[email protected]99171a92014-06-03 08:44:471951 return False
1952
1953 if (action == _GetIDLParseError and
Sean Kau46e29bc2017-08-28 16:31:161954 not _MatchesFile(input_api, idl_included_patterns, path)):
[email protected]99171a92014-06-03 08:44:471955 return False
1956 return True
1957
1958 results = []
1959 for affected_file in input_api.AffectedFiles(
1960 file_filter=FilterFile, include_deletes=False):
1961 action = get_action(affected_file)
1962 kwargs = {}
1963 if (action == _GetJSONParseError and
Sean Kau46e29bc2017-08-28 16:31:161964 _MatchesFile(input_api, json_no_comments_patterns,
1965 affected_file.LocalPath())):
[email protected]99171a92014-06-03 08:44:471966 kwargs['eat_comments'] = False
1967 parse_error = action(input_api,
1968 affected_file.AbsoluteLocalPath(),
1969 **kwargs)
1970 if parse_error:
1971 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
1972 (affected_file.LocalPath(), parse_error)))
1973 return results
1974
1975
[email protected]760deea2013-12-10 19:33:491976def _CheckJavaStyle(input_api, output_api):
1977 """Runs checkstyle on changed java files and returns errors if any exist."""
mohan.reddyf21db962014-10-16 12:26:471978 import sys
[email protected]760deea2013-12-10 19:33:491979 original_sys_path = sys.path
1980 try:
1981 sys.path = sys.path + [input_api.os_path.join(
1982 input_api.PresubmitLocalPath(), 'tools', 'android', 'checkstyle')]
1983 import checkstyle
1984 finally:
1985 # Restore sys.path to what it was before.
1986 sys.path = original_sys_path
1987
1988 return checkstyle.RunCheckstyle(
davileen72d76532015-01-20 22:30:091989 input_api, output_api, 'tools/android/checkstyle/chromium-style-5.0.xml',
newtd8b7d30e92015-01-23 18:10:511990 black_list=_EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
[email protected]760deea2013-12-10 19:33:491991
1992
Sean Kau46e29bc2017-08-28 16:31:161993def _MatchesFile(input_api, patterns, path):
1994 for pattern in patterns:
1995 if input_api.re.search(pattern, path):
1996 return True
1997 return False
1998
1999
Daniel Cheng7052cdf2017-11-21 19:23:292000def _GetOwnersFilesToCheckForIpcOwners(input_api):
2001 """Gets a list of OWNERS files to check for correct security owners.
dchenge07de812016-06-20 19:27:172002
Daniel Cheng7052cdf2017-11-21 19:23:292003 Returns:
2004 A dictionary mapping an OWNER file to the list of OWNERS rules it must
2005 contain to cover IPC-related files with noparent reviewer rules.
2006 """
2007 # Whether or not a file affects IPC is (mostly) determined by a simple list
2008 # of filename patterns.
dchenge07de812016-06-20 19:27:172009 file_patterns = [
palmerb19a0932017-01-24 04:00:312010 # Legacy IPC:
dchenge07de812016-06-20 19:27:172011 '*_messages.cc',
2012 '*_messages*.h',
2013 '*_param_traits*.*',
palmerb19a0932017-01-24 04:00:312014 # Mojo IPC:
dchenge07de812016-06-20 19:27:172015 '*.mojom',
Daniel Cheng1f386932018-01-29 19:56:472016 '*_mojom_traits*.*',
dchenge07de812016-06-20 19:27:172017 '*_struct_traits*.*',
2018 '*_type_converter*.*',
palmerb19a0932017-01-24 04:00:312019 '*.typemap',
2020 # Android native IPC:
2021 '*.aidl',
2022 # Blink uses a different file naming convention:
2023 '*EnumTraits*.*',
Daniel Chenge0bf3f62018-01-30 01:56:472024 "*MojomTraits*.*",
dchenge07de812016-06-20 19:27:172025 '*StructTraits*.*',
2026 '*TypeConverter*.*',
2027 ]
2028
scottmg7a6ed5ba2016-11-04 18:22:042029 # These third_party directories do not contain IPCs, but contain files
2030 # matching the above patterns, which trigger false positives.
2031 exclude_paths = [
2032 'third_party/crashpad/*',
Andres Medinae684cf42018-08-27 18:48:232033 'third_party/protobuf/benchmarks/python/*',
Daniel Chengebe635e2018-07-13 12:36:062034 'third_party/third_party/blink/renderer/platform/bindings/*',
Nico Weberee3dc9b2017-08-31 17:09:292035 'third_party/win_build_output/*',
scottmg7a6ed5ba2016-11-04 18:22:042036 ]
2037
dchenge07de812016-06-20 19:27:172038 # Dictionary mapping an OWNERS file path to Patterns.
2039 # Patterns is a dictionary mapping glob patterns (suitable for use in per-file
2040 # rules ) to a PatternEntry.
2041 # PatternEntry is a dictionary with two keys:
2042 # - 'files': the files that are matched by this pattern
2043 # - 'rules': the per-file rules needed for this pattern
2044 # For example, if we expect OWNERS file to contain rules for *.mojom and
2045 # *_struct_traits*.*, Patterns might look like this:
2046 # {
2047 # '*.mojom': {
2048 # 'files': ...,
2049 # 'rules': [
2050 # 'per-file *.mojom=set noparent',
2051 # 'per-file *.mojom=file://ipc/SECURITY_OWNERS',
2052 # ],
2053 # },
2054 # '*_struct_traits*.*': {
2055 # 'files': ...,
2056 # 'rules': [
2057 # 'per-file *_struct_traits*.*=set noparent',
2058 # 'per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS',
2059 # ],
2060 # },
2061 # }
2062 to_check = {}
2063
Daniel Cheng13ca61a882017-08-25 15:11:252064 def AddPatternToCheck(input_file, pattern):
2065 owners_file = input_api.os_path.join(
2066 input_api.os_path.dirname(input_file.LocalPath()), 'OWNERS')
2067 if owners_file not in to_check:
2068 to_check[owners_file] = {}
2069 if pattern not in to_check[owners_file]:
2070 to_check[owners_file][pattern] = {
2071 'files': [],
2072 'rules': [
2073 'per-file %s=set noparent' % pattern,
2074 'per-file %s=file://ipc/SECURITY_OWNERS' % pattern,
2075 ]
2076 }
Vaclav Brozekd5de76a2018-03-17 07:57:502077 to_check[owners_file][pattern]['files'].append(input_file)
Daniel Cheng13ca61a882017-08-25 15:11:252078
dchenge07de812016-06-20 19:27:172079 # Iterate through the affected files to see what we actually need to check
2080 # for. We should only nag patch authors about per-file rules if a file in that
2081 # directory would match that pattern. If a directory only contains *.mojom
2082 # files and no *_messages*.h files, we should only nag about rules for
2083 # *.mojom files.
Daniel Cheng13ca61a882017-08-25 15:11:252084 for f in input_api.AffectedFiles(include_deletes=False):
2085 # Manifest files don't have a strong naming convention. Instead, scan
2086 # affected files for .json files and see if they look like a manifest.
Sean Kau46e29bc2017-08-28 16:31:162087 if (f.LocalPath().endswith('.json') and
2088 not _MatchesFile(input_api, _KNOWN_INVALID_JSON_FILE_PATTERNS,
2089 f.LocalPath())):
Daniel Cheng13ca61a882017-08-25 15:11:252090 json_comment_eater = _ImportJSONCommentEater(input_api)
2091 mostly_json_lines = '\n'.join(f.NewContents())
2092 # Comments aren't allowed in strict JSON, so filter them out.
2093 json_lines = json_comment_eater.Nom(mostly_json_lines)
Daniel Chenge8efd092018-03-23 23:57:432094 try:
2095 json_content = input_api.json.loads(json_lines)
2096 except:
2097 # There's another PRESUBMIT check that already verifies that JSON files
2098 # are not invalid, so no need to emit another warning here.
2099 continue
Daniel Cheng13ca61a882017-08-25 15:11:252100 if 'interface_provider_specs' in json_content:
2101 AddPatternToCheck(f, input_api.os_path.basename(f.LocalPath()))
dchenge07de812016-06-20 19:27:172102 for pattern in file_patterns:
2103 if input_api.fnmatch.fnmatch(
2104 input_api.os_path.basename(f.LocalPath()), pattern):
scottmg7a6ed5ba2016-11-04 18:22:042105 skip = False
2106 for exclude in exclude_paths:
2107 if input_api.fnmatch.fnmatch(f.LocalPath(), exclude):
2108 skip = True
2109 break
2110 if skip:
2111 continue
Daniel Cheng13ca61a882017-08-25 15:11:252112 AddPatternToCheck(f, pattern)
dchenge07de812016-06-20 19:27:172113 break
2114
Daniel Cheng7052cdf2017-11-21 19:23:292115 return to_check
2116
2117
2118def _CheckIpcOwners(input_api, output_api):
2119 """Checks that affected files involving IPC have an IPC OWNERS rule."""
2120 to_check = _GetOwnersFilesToCheckForIpcOwners(input_api)
2121
2122 if to_check:
2123 # If there are any OWNERS files to check, there are IPC-related changes in
2124 # this CL. Auto-CC the review list.
2125 output_api.AppendCC('[email protected]')
2126
2127 # Go through the OWNERS files to check, filtering out rules that are already
2128 # present in that OWNERS file.
dchenge07de812016-06-20 19:27:172129 for owners_file, patterns in to_check.iteritems():
2130 try:
2131 with file(owners_file) as f:
2132 lines = set(f.read().splitlines())
2133 for entry in patterns.itervalues():
2134 entry['rules'] = [rule for rule in entry['rules'] if rule not in lines
2135 ]
2136 except IOError:
2137 # No OWNERS file, so all the rules are definitely missing.
2138 continue
2139
2140 # All the remaining lines weren't found in OWNERS files, so emit an error.
2141 errors = []
2142 for owners_file, patterns in to_check.iteritems():
2143 missing_lines = []
2144 files = []
Vaclav Brozekd5de76a2018-03-17 07:57:502145 for _, entry in patterns.iteritems():
dchenge07de812016-06-20 19:27:172146 missing_lines.extend(entry['rules'])
2147 files.extend([' %s' % f.LocalPath() for f in entry['files']])
2148 if missing_lines:
2149 errors.append(
Vaclav Brozek1893a972018-04-25 05:48:052150 'Because of the presence of files:\n%s\n\n'
2151 '%s needs the following %d lines added:\n\n%s' %
2152 ('\n'.join(files), owners_file, len(missing_lines),
2153 '\n'.join(missing_lines)))
dchenge07de812016-06-20 19:27:172154
2155 results = []
2156 if errors:
vabrf5ce3bf92016-07-11 14:52:412157 if input_api.is_committing:
2158 output = output_api.PresubmitError
2159 else:
2160 output = output_api.PresubmitPromptWarning
2161 results.append(output(
Daniel Cheng52111692017-06-14 08:00:592162 'Found OWNERS files that need to be updated for IPC security ' +
2163 'review coverage.\nPlease update the OWNERS files below:',
dchenge07de812016-06-20 19:27:172164 long_text='\n\n'.join(errors)))
2165
2166 return results
2167
2168
jbriance9e12f162016-11-25 07:57:502169def _CheckUselessForwardDeclarations(input_api, output_api):
jbriance2c51e821a2016-12-12 08:24:312170 """Checks that added or removed lines in non third party affected
2171 header files do not lead to new useless class or struct forward
2172 declaration.
jbriance9e12f162016-11-25 07:57:502173 """
2174 results = []
2175 class_pattern = input_api.re.compile(r'^class\s+(\w+);$',
2176 input_api.re.MULTILINE)
2177 struct_pattern = input_api.re.compile(r'^struct\s+(\w+);$',
2178 input_api.re.MULTILINE)
2179 for f in input_api.AffectedFiles(include_deletes=False):
jbriance2c51e821a2016-12-12 08:24:312180 if (f.LocalPath().startswith('third_party') and
Kent Tamurae9b3a9ec2017-08-31 02:20:192181 not f.LocalPath().startswith('third_party/blink') and
Kent Tamura32dbbcb2018-11-30 12:28:492182 not f.LocalPath().startswith('third_party\\blink')):
jbriance2c51e821a2016-12-12 08:24:312183 continue
2184
jbriance9e12f162016-11-25 07:57:502185 if not f.LocalPath().endswith('.h'):
2186 continue
2187
2188 contents = input_api.ReadFile(f)
2189 fwd_decls = input_api.re.findall(class_pattern, contents)
2190 fwd_decls.extend(input_api.re.findall(struct_pattern, contents))
2191
2192 useless_fwd_decls = []
2193 for decl in fwd_decls:
2194 count = sum(1 for _ in input_api.re.finditer(
2195 r'\b%s\b' % input_api.re.escape(decl), contents))
2196 if count == 1:
2197 useless_fwd_decls.append(decl)
2198
2199 if not useless_fwd_decls:
2200 continue
2201
2202 for line in f.GenerateScmDiff().splitlines():
2203 if (line.startswith('-') and not line.startswith('--') or
2204 line.startswith('+') and not line.startswith('++')):
2205 for decl in useless_fwd_decls:
2206 if input_api.re.search(r'\b%s\b' % decl, line[1:]):
2207 results.append(output_api.PresubmitPromptWarning(
ricea6416dea2017-05-19 12:39:242208 '%s: %s forward declaration is no longer needed' %
jbriance9e12f162016-11-25 07:57:502209 (f.LocalPath(), decl)))
2210 useless_fwd_decls.remove(decl)
2211
2212 return results
2213
2214
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492215# TODO: add unit tests
dskiba88634f4e2015-08-14 23:03:292216def _CheckAndroidToastUsage(input_api, output_api):
2217 """Checks that code uses org.chromium.ui.widget.Toast instead of
2218 android.widget.Toast (Chromium Toast doesn't force hardware
2219 acceleration on low-end devices, saving memory).
2220 """
2221 toast_import_pattern = input_api.re.compile(
2222 r'^import android\.widget\.Toast;$')
2223
2224 errors = []
2225
2226 sources = lambda affected_file: input_api.FilterSourceFile(
2227 affected_file,
2228 black_list=(_EXCLUDED_PATHS +
2229 _TEST_CODE_EXCLUDED_PATHS +
2230 input_api.DEFAULT_BLACK_LIST +
Egor Paskoce145c42018-09-28 19:31:042231 (r'^chromecast[\\/].*',
2232 r'^remoting[\\/].*')),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492233 white_list=[r'.*\.java$'])
dskiba88634f4e2015-08-14 23:03:292234
2235 for f in input_api.AffectedSourceFiles(sources):
2236 for line_num, line in f.ChangedContents():
2237 if toast_import_pattern.search(line):
2238 errors.append("%s:%d" % (f.LocalPath(), line_num))
2239
2240 results = []
2241
2242 if errors:
2243 results.append(output_api.PresubmitError(
2244 'android.widget.Toast usage is detected. Android toasts use hardware'
2245 ' acceleration, and can be\ncostly on low-end devices. Please use'
2246 ' org.chromium.ui.widget.Toast instead.\n'
2247 'Contact [email protected] if you have any questions.',
2248 errors))
2249
2250 return results
2251
2252
dgnaa68d5e2015-06-10 10:08:222253def _CheckAndroidCrLogUsage(input_api, output_api):
2254 """Checks that new logs using org.chromium.base.Log:
2255 - Are using 'TAG' as variable name for the tags (warn)
dgn38736db2015-09-18 19:20:512256 - Are using a tag that is shorter than 20 characters (error)
dgnaa68d5e2015-06-10 10:08:222257 """
pkotwicza1dd0b002016-05-16 14:41:042258
torne89540622017-03-24 19:41:302259 # Do not check format of logs in the given files
pkotwicza1dd0b002016-05-16 14:41:042260 cr_log_check_excluded_paths = [
torne89540622017-03-24 19:41:302261 # //chrome/android/webapk cannot depend on //base
Egor Paskoce145c42018-09-28 19:31:042262 r"^chrome[\\/]android[\\/]webapk[\\/].*",
torne89540622017-03-24 19:41:302263 # WebView license viewer code cannot depend on //base; used in stub APK.
Egor Paskoce145c42018-09-28 19:31:042264 r"^android_webview[\\/]glue[\\/]java[\\/]src[\\/]com[\\/]android[\\/]"
2265 r"webview[\\/]chromium[\\/]License.*",
Egor Paskoa5c05b02018-09-28 16:04:092266 # The customtabs_benchmark is a small app that does not depend on Chromium
2267 # java pieces.
Egor Paskoce145c42018-09-28 19:31:042268 r"tools[\\/]android[\\/]customtabs_benchmark[\\/].*",
pkotwicza1dd0b002016-05-16 14:41:042269 ]
2270
dgnaa68d5e2015-06-10 10:08:222271 cr_log_import_pattern = input_api.re.compile(
dgn87d9fb62015-06-12 09:15:122272 r'^import org\.chromium\.base\.Log;$', input_api.re.MULTILINE)
2273 class_in_base_pattern = input_api.re.compile(
2274 r'^package org\.chromium\.base;$', input_api.re.MULTILINE)
2275 has_some_log_import_pattern = input_api.re.compile(
2276 r'^import .*\.Log;$', input_api.re.MULTILINE)
dgnaa68d5e2015-06-10 10:08:222277 # Extract the tag from lines like `Log.d(TAG, "*");` or `Log.d("TAG", "*");`
dgn87d9fb62015-06-12 09:15:122278 log_call_pattern = input_api.re.compile(r'^\s*Log\.\w\((?P<tag>\"?\w+\"?)\,')
dgnaa68d5e2015-06-10 10:08:222279 log_decl_pattern = input_api.re.compile(
dgn38736db2015-09-18 19:20:512280 r'^\s*private static final String TAG = "(?P<name>(.*))";',
dgnaa68d5e2015-06-10 10:08:222281 input_api.re.MULTILINE)
dgnaa68d5e2015-06-10 10:08:222282
Vincent Scheib16d7b272015-09-15 18:09:072283 REF_MSG = ('See docs/android_logging.md '
dgnaa68d5e2015-06-10 10:08:222284 'or contact [email protected] for more info.')
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492285 sources = lambda x: input_api.FilterSourceFile(x, white_list=[r'.*\.java$'],
pkotwicza1dd0b002016-05-16 14:41:042286 black_list=cr_log_check_excluded_paths)
dgn87d9fb62015-06-12 09:15:122287
dgnaa68d5e2015-06-10 10:08:222288 tag_decl_errors = []
2289 tag_length_errors = []
dgn87d9fb62015-06-12 09:15:122290 tag_errors = []
dgn38736db2015-09-18 19:20:512291 tag_with_dot_errors = []
dgn87d9fb62015-06-12 09:15:122292 util_log_errors = []
dgnaa68d5e2015-06-10 10:08:222293
2294 for f in input_api.AffectedSourceFiles(sources):
2295 file_content = input_api.ReadFile(f)
2296 has_modified_logs = False
2297
2298 # Per line checks
dgn87d9fb62015-06-12 09:15:122299 if (cr_log_import_pattern.search(file_content) or
2300 (class_in_base_pattern.search(file_content) and
2301 not has_some_log_import_pattern.search(file_content))):
2302 # Checks to run for files using cr log
dgnaa68d5e2015-06-10 10:08:222303 for line_num, line in f.ChangedContents():
2304
2305 # Check if the new line is doing some logging
dgn87d9fb62015-06-12 09:15:122306 match = log_call_pattern.search(line)
dgnaa68d5e2015-06-10 10:08:222307 if match:
2308 has_modified_logs = True
2309
2310 # Make sure it uses "TAG"
2311 if not match.group('tag') == 'TAG':
2312 tag_errors.append("%s:%d" % (f.LocalPath(), line_num))
dgn87d9fb62015-06-12 09:15:122313 else:
2314 # Report non cr Log function calls in changed lines
2315 for line_num, line in f.ChangedContents():
2316 if log_call_pattern.search(line):
2317 util_log_errors.append("%s:%d" % (f.LocalPath(), line_num))
dgnaa68d5e2015-06-10 10:08:222318
2319 # Per file checks
2320 if has_modified_logs:
2321 # Make sure the tag is using the "cr" prefix and is not too long
2322 match = log_decl_pattern.search(file_content)
dgn38736db2015-09-18 19:20:512323 tag_name = match.group('name') if match else None
2324 if not tag_name:
dgnaa68d5e2015-06-10 10:08:222325 tag_decl_errors.append(f.LocalPath())
dgn38736db2015-09-18 19:20:512326 elif len(tag_name) > 20:
dgnaa68d5e2015-06-10 10:08:222327 tag_length_errors.append(f.LocalPath())
dgn38736db2015-09-18 19:20:512328 elif '.' in tag_name:
2329 tag_with_dot_errors.append(f.LocalPath())
dgnaa68d5e2015-06-10 10:08:222330
2331 results = []
2332 if tag_decl_errors:
2333 results.append(output_api.PresubmitPromptWarning(
2334 'Please define your tags using the suggested format: .\n'
dgn38736db2015-09-18 19:20:512335 '"private static final String TAG = "<package tag>".\n'
2336 'They will be prepended with "cr_" automatically.\n' + REF_MSG,
dgnaa68d5e2015-06-10 10:08:222337 tag_decl_errors))
2338
2339 if tag_length_errors:
2340 results.append(output_api.PresubmitError(
2341 'The tag length is restricted by the system to be at most '
dgn38736db2015-09-18 19:20:512342 '20 characters.\n' + REF_MSG,
dgnaa68d5e2015-06-10 10:08:222343 tag_length_errors))
2344
2345 if tag_errors:
2346 results.append(output_api.PresubmitPromptWarning(
2347 'Please use a variable named "TAG" for your log tags.\n' + REF_MSG,
2348 tag_errors))
2349
dgn87d9fb62015-06-12 09:15:122350 if util_log_errors:
dgn4401aa52015-04-29 16:26:172351 results.append(output_api.PresubmitPromptWarning(
dgn87d9fb62015-06-12 09:15:122352 'Please use org.chromium.base.Log for new logs.\n' + REF_MSG,
2353 util_log_errors))
2354
dgn38736db2015-09-18 19:20:512355 if tag_with_dot_errors:
2356 results.append(output_api.PresubmitPromptWarning(
2357 'Dot in log tags cause them to be elided in crash reports.\n' + REF_MSG,
2358 tag_with_dot_errors))
2359
dgn4401aa52015-04-29 16:26:172360 return results
2361
2362
Yoland Yanb92fa522017-08-28 17:37:062363def _CheckAndroidTestJUnitFrameworkImport(input_api, output_api):
2364 """Checks that junit.framework.* is no longer used."""
2365 deprecated_junit_framework_pattern = input_api.re.compile(
2366 r'^import junit\.framework\..*;',
2367 input_api.re.MULTILINE)
2368 sources = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492369 x, white_list=[r'.*\.java$'], black_list=None)
Yoland Yanb92fa522017-08-28 17:37:062370 errors = []
2371 for f in input_api.AffectedFiles(sources):
2372 for line_num, line in f.ChangedContents():
2373 if deprecated_junit_framework_pattern.search(line):
2374 errors.append("%s:%d" % (f.LocalPath(), line_num))
2375
2376 results = []
2377 if errors:
2378 results.append(output_api.PresubmitError(
2379 'APIs from junit.framework.* are deprecated, please use JUnit4 framework'
2380 '(org.junit.*) from //third_party/junit. Contact [email protected]'
2381 ' if you have any question.', errors))
2382 return results
2383
2384
2385def _CheckAndroidTestJUnitInheritance(input_api, output_api):
2386 """Checks that if new Java test classes have inheritance.
2387 Either the new test class is JUnit3 test or it is a JUnit4 test class
2388 with a base class, either case is undesirable.
2389 """
2390 class_declaration_pattern = input_api.re.compile(r'^public class \w*Test ')
2391
2392 sources = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492393 x, white_list=[r'.*Test\.java$'], black_list=None)
Yoland Yanb92fa522017-08-28 17:37:062394 errors = []
2395 for f in input_api.AffectedFiles(sources):
2396 if not f.OldContents():
2397 class_declaration_start_flag = False
2398 for line_num, line in f.ChangedContents():
2399 if class_declaration_pattern.search(line):
2400 class_declaration_start_flag = True
2401 if class_declaration_start_flag and ' extends ' in line:
2402 errors.append('%s:%d' % (f.LocalPath(), line_num))
2403 if '{' in line:
2404 class_declaration_start_flag = False
2405
2406 results = []
2407 if errors:
2408 results.append(output_api.PresubmitPromptWarning(
2409 'The newly created files include Test classes that inherits from base'
2410 ' class. Please do not use inheritance in JUnit4 tests or add new'
2411 ' JUnit3 tests. Contact [email protected] if you have any'
2412 ' questions.', errors))
2413 return results
2414
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202415
yolandyan45001472016-12-21 21:12:422416def _CheckAndroidTestAnnotationUsage(input_api, output_api):
2417 """Checks that android.test.suitebuilder.annotation.* is no longer used."""
2418 deprecated_annotation_import_pattern = input_api.re.compile(
2419 r'^import android\.test\.suitebuilder\.annotation\..*;',
2420 input_api.re.MULTILINE)
2421 sources = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492422 x, white_list=[r'.*\.java$'], black_list=None)
yolandyan45001472016-12-21 21:12:422423 errors = []
2424 for f in input_api.AffectedFiles(sources):
2425 for line_num, line in f.ChangedContents():
2426 if deprecated_annotation_import_pattern.search(line):
2427 errors.append("%s:%d" % (f.LocalPath(), line_num))
2428
2429 results = []
2430 if errors:
2431 results.append(output_api.PresubmitError(
2432 'Annotations in android.test.suitebuilder.annotation have been'
2433 ' deprecated since API level 24. Please use android.support.test.filters'
2434 ' from //third_party/android_support_test_runner:runner_java instead.'
2435 ' Contact [email protected] if you have any questions.', errors))
2436 return results
2437
2438
agrieve7b6479d82015-10-07 14:24:222439def _CheckAndroidNewMdpiAssetLocation(input_api, output_api):
2440 """Checks if MDPI assets are placed in a correct directory."""
2441 file_filter = lambda f: (f.LocalPath().endswith('.png') and
2442 ('/res/drawable/' in f.LocalPath() or
2443 '/res/drawable-ldrtl/' in f.LocalPath()))
2444 errors = []
2445 for f in input_api.AffectedFiles(include_deletes=False,
2446 file_filter=file_filter):
2447 errors.append(' %s' % f.LocalPath())
2448
2449 results = []
2450 if errors:
2451 results.append(output_api.PresubmitError(
2452 'MDPI assets should be placed in /res/drawable-mdpi/ or '
2453 '/res/drawable-ldrtl-mdpi/\ninstead of /res/drawable/ and'
2454 '/res/drawable-ldrtl/.\n'
2455 'Contact [email protected] if you have questions.', errors))
2456 return results
2457
2458
Nate Fischer535972b2017-09-16 01:06:182459def _CheckAndroidWebkitImports(input_api, output_api):
2460 """Checks that code uses org.chromium.base.Callback instead of
2461 android.widget.ValueCallback except in the WebView glue layer.
2462 """
2463 valuecallback_import_pattern = input_api.re.compile(
2464 r'^import android\.webkit\.ValueCallback;$')
2465
2466 errors = []
2467
2468 sources = lambda affected_file: input_api.FilterSourceFile(
2469 affected_file,
2470 black_list=(_EXCLUDED_PATHS +
2471 _TEST_CODE_EXCLUDED_PATHS +
2472 input_api.DEFAULT_BLACK_LIST +
Egor Paskoce145c42018-09-28 19:31:042473 (r'^android_webview[\\/]glue[\\/].*',)),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492474 white_list=[r'.*\.java$'])
Nate Fischer535972b2017-09-16 01:06:182475
2476 for f in input_api.AffectedSourceFiles(sources):
2477 for line_num, line in f.ChangedContents():
2478 if valuecallback_import_pattern.search(line):
2479 errors.append("%s:%d" % (f.LocalPath(), line_num))
2480
2481 results = []
2482
2483 if errors:
2484 results.append(output_api.PresubmitError(
2485 'android.webkit.ValueCallback usage is detected outside of the glue'
2486 ' layer. To stay compatible with the support library, android.webkit.*'
2487 ' classes should only be used inside the glue layer and'
2488 ' org.chromium.base.Callback should be used instead.',
2489 errors))
2490
2491 return results
2492
2493
agrievef32bcc72016-04-04 14:57:402494class PydepsChecker(object):
2495 def __init__(self, input_api, pydeps_files):
2496 self._file_cache = {}
2497 self._input_api = input_api
2498 self._pydeps_files = pydeps_files
2499
2500 def _LoadFile(self, path):
2501 """Returns the list of paths within a .pydeps file relative to //."""
2502 if path not in self._file_cache:
2503 with open(path) as f:
2504 self._file_cache[path] = f.read()
2505 return self._file_cache[path]
2506
2507 def _ComputeNormalizedPydepsEntries(self, pydeps_path):
2508 """Returns an interable of paths within the .pydep, relativized to //."""
2509 os_path = self._input_api.os_path
2510 pydeps_dir = os_path.dirname(pydeps_path)
2511 entries = (l.rstrip() for l in self._LoadFile(pydeps_path).splitlines()
2512 if not l.startswith('*'))
2513 return (os_path.normpath(os_path.join(pydeps_dir, e)) for e in entries)
2514
2515 def _CreateFilesToPydepsMap(self):
2516 """Returns a map of local_path -> list_of_pydeps."""
2517 ret = {}
2518 for pydep_local_path in self._pydeps_files:
2519 for path in self._ComputeNormalizedPydepsEntries(pydep_local_path):
2520 ret.setdefault(path, []).append(pydep_local_path)
2521 return ret
2522
2523 def ComputeAffectedPydeps(self):
2524 """Returns an iterable of .pydeps files that might need regenerating."""
2525 affected_pydeps = set()
2526 file_to_pydeps_map = None
2527 for f in self._input_api.AffectedFiles(include_deletes=True):
2528 local_path = f.LocalPath()
2529 if local_path == 'DEPS':
2530 return self._pydeps_files
2531 elif local_path.endswith('.pydeps'):
2532 if local_path in self._pydeps_files:
2533 affected_pydeps.add(local_path)
2534 elif local_path.endswith('.py'):
2535 if file_to_pydeps_map is None:
2536 file_to_pydeps_map = self._CreateFilesToPydepsMap()
2537 affected_pydeps.update(file_to_pydeps_map.get(local_path, ()))
2538 return affected_pydeps
2539
2540 def DetermineIfStale(self, pydeps_path):
2541 """Runs print_python_deps.py to see if the files is stale."""
phajdan.jr0d9878552016-11-04 10:49:412542 import difflib
John Budorick47ca3fe2018-02-10 00:53:102543 import os
2544
agrievef32bcc72016-04-04 14:57:402545 old_pydeps_data = self._LoadFile(pydeps_path).splitlines()
2546 cmd = old_pydeps_data[1][1:].strip()
John Budorick47ca3fe2018-02-10 00:53:102547 env = dict(os.environ)
2548 env['PYTHONDONTWRITEBYTECODE'] = '1'
agrievef32bcc72016-04-04 14:57:402549 new_pydeps_data = self._input_api.subprocess.check_output(
John Budorick47ca3fe2018-02-10 00:53:102550 cmd + ' --output ""', shell=True, env=env)
phajdan.jr0d9878552016-11-04 10:49:412551 old_contents = old_pydeps_data[2:]
2552 new_contents = new_pydeps_data.splitlines()[2:]
agrievef32bcc72016-04-04 14:57:402553 if old_pydeps_data[2:] != new_pydeps_data.splitlines()[2:]:
phajdan.jr0d9878552016-11-04 10:49:412554 return cmd, '\n'.join(difflib.context_diff(old_contents, new_contents))
agrievef32bcc72016-04-04 14:57:402555
2556
2557def _CheckPydepsNeedsUpdating(input_api, output_api, checker_for_tests=None):
2558 """Checks if a .pydeps file needs to be regenerated."""
John Chencde89192018-01-27 21:18:402559 # This check is for Python dependency lists (.pydeps files), and involves
2560 # paths not only in the PRESUBMIT.py, but also in the .pydeps files. It
2561 # doesn't work on Windows and Mac, so skip it on other platforms.
agrieve9bc4200b2016-05-04 16:33:282562 if input_api.platform != 'linux2':
agrievebb9c5b472016-04-22 15:13:002563 return []
Mostyn Bramley-Moore6b427322017-12-21 22:11:022564 # TODO(agrieve): Update when there's a better way to detect
2565 # this: crbug.com/570091
agrievef32bcc72016-04-04 14:57:402566 is_android = input_api.os_path.exists('third_party/android_tools')
2567 pydeps_files = _ALL_PYDEPS_FILES if is_android else _GENERIC_PYDEPS_FILES
2568 results = []
2569 # First, check for new / deleted .pydeps.
2570 for f in input_api.AffectedFiles(include_deletes=True):
Zhiling Huang45cabf32018-03-10 00:50:032571 # Check whether we are running the presubmit check for a file in src.
2572 # f.LocalPath is relative to repo (src, or internal repo).
2573 # os_path.exists is relative to src repo.
2574 # Therefore if os_path.exists is true, it means f.LocalPath is relative
2575 # to src and we can conclude that the pydeps is in src.
2576 if input_api.os_path.exists(f.LocalPath()):
2577 if f.LocalPath().endswith('.pydeps'):
2578 if f.Action() == 'D' and f.LocalPath() in _ALL_PYDEPS_FILES:
2579 results.append(output_api.PresubmitError(
2580 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to '
2581 'remove %s' % f.LocalPath()))
2582 elif f.Action() != 'D' and f.LocalPath() not in _ALL_PYDEPS_FILES:
2583 results.append(output_api.PresubmitError(
2584 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to '
2585 'include %s' % f.LocalPath()))
agrievef32bcc72016-04-04 14:57:402586
2587 if results:
2588 return results
2589
2590 checker = checker_for_tests or PydepsChecker(input_api, pydeps_files)
2591
2592 for pydep_path in checker.ComputeAffectedPydeps():
2593 try:
phajdan.jr0d9878552016-11-04 10:49:412594 result = checker.DetermineIfStale(pydep_path)
2595 if result:
2596 cmd, diff = result
agrievef32bcc72016-04-04 14:57:402597 results.append(output_api.PresubmitError(
phajdan.jr0d9878552016-11-04 10:49:412598 'File is stale: %s\nDiff (apply to fix):\n%s\n'
2599 'To regenerate, run:\n\n %s' %
2600 (pydep_path, diff, cmd)))
agrievef32bcc72016-04-04 14:57:402601 except input_api.subprocess.CalledProcessError as error:
2602 return [output_api.PresubmitError('Error running: %s' % error.cmd,
2603 long_text=error.output)]
2604
2605 return results
2606
2607
glidere61efad2015-02-18 17:39:432608def _CheckSingletonInHeaders(input_api, output_api):
2609 """Checks to make sure no header files have |Singleton<|."""
2610 def FileFilter(affected_file):
2611 # It's ok for base/memory/singleton.h to have |Singleton<|.
2612 black_list = (_EXCLUDED_PATHS +
2613 input_api.DEFAULT_BLACK_LIST +
Egor Paskoce145c42018-09-28 19:31:042614 (r"^base[\\/]memory[\\/]singleton\.h$",
2615 r"^net[\\/]quic[\\/]platform[\\/]impl[\\/]"
Michael Warrese4451492018-03-07 04:42:472616 r"quic_singleton_impl\.h$"))
glidere61efad2015-02-18 17:39:432617 return input_api.FilterSourceFile(affected_file, black_list=black_list)
2618
sergeyu34d21222015-09-16 00:11:442619 pattern = input_api.re.compile(r'(?<!class\sbase::)Singleton\s*<')
glidere61efad2015-02-18 17:39:432620 files = []
2621 for f in input_api.AffectedSourceFiles(FileFilter):
2622 if (f.LocalPath().endswith('.h') or f.LocalPath().endswith('.hxx') or
2623 f.LocalPath().endswith('.hpp') or f.LocalPath().endswith('.inl')):
2624 contents = input_api.ReadFile(f)
2625 for line in contents.splitlines(False):
oysteinec430ad42015-10-22 20:55:242626 if (not line.lstrip().startswith('//') and # Strip C++ comment.
glidere61efad2015-02-18 17:39:432627 pattern.search(line)):
2628 files.append(f)
2629 break
2630
2631 if files:
yolandyandaabc6d2016-04-18 18:29:392632 return [output_api.PresubmitError(
sergeyu34d21222015-09-16 00:11:442633 'Found base::Singleton<T> in the following header files.\n' +
glidere61efad2015-02-18 17:39:432634 'Please move them to an appropriate source file so that the ' +
2635 'template gets instantiated in a single compilation unit.',
2636 files) ]
2637 return []
2638
2639
[email protected]fd20b902014-05-09 02:14:532640_DEPRECATED_CSS = [
2641 # Values
2642 ( "-webkit-box", "flex" ),
2643 ( "-webkit-inline-box", "inline-flex" ),
2644 ( "-webkit-flex", "flex" ),
2645 ( "-webkit-inline-flex", "inline-flex" ),
2646 ( "-webkit-min-content", "min-content" ),
2647 ( "-webkit-max-content", "max-content" ),
2648
2649 # Properties
2650 ( "-webkit-background-clip", "background-clip" ),
2651 ( "-webkit-background-origin", "background-origin" ),
2652 ( "-webkit-background-size", "background-size" ),
2653 ( "-webkit-box-shadow", "box-shadow" ),
dbeam6936c67f2017-01-19 01:51:442654 ( "-webkit-user-select", "user-select" ),
[email protected]fd20b902014-05-09 02:14:532655
2656 # Functions
2657 ( "-webkit-gradient", "gradient" ),
2658 ( "-webkit-repeating-gradient", "repeating-gradient" ),
2659 ( "-webkit-linear-gradient", "linear-gradient" ),
2660 ( "-webkit-repeating-linear-gradient", "repeating-linear-gradient" ),
2661 ( "-webkit-radial-gradient", "radial-gradient" ),
2662 ( "-webkit-repeating-radial-gradient", "repeating-radial-gradient" ),
2663]
2664
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202665
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492666# TODO: add unit tests
dbeam1ec68ac2016-12-15 05:22:242667def _CheckNoDeprecatedCss(input_api, output_api):
[email protected]fd20b902014-05-09 02:14:532668 """ Make sure that we don't use deprecated CSS
[email protected]9a48e3f82014-05-22 00:06:252669 properties, functions or values. Our external
mdjonesae0286c32015-06-10 18:10:342670 documentation and iOS CSS for dom distiller
2671 (reader mode) are ignored by the hooks as it
[email protected]9a48e3f82014-05-22 00:06:252672 needs to be consumed by WebKit. """
[email protected]fd20b902014-05-09 02:14:532673 results = []
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492674 file_inclusion_pattern = [r".+\.css$"]
[email protected]9a48e3f82014-05-22 00:06:252675 black_list = (_EXCLUDED_PATHS +
2676 _TEST_CODE_EXCLUDED_PATHS +
2677 input_api.DEFAULT_BLACK_LIST +
2678 (r"^chrome/common/extensions/docs",
2679 r"^chrome/docs",
mdjonesae0286c32015-06-10 18:10:342680 r"^components/dom_distiller/core/css/distilledpage_ios.css",
sdefresne6308d7f2016-02-15 09:38:442681 r"^components/neterror/resources/neterror.css",
[email protected]9a48e3f82014-05-22 00:06:252682 r"^native_client_sdk"))
2683 file_filter = lambda f: input_api.FilterSourceFile(
2684 f, white_list=file_inclusion_pattern, black_list=black_list)
[email protected]fd20b902014-05-09 02:14:532685 for fpath in input_api.AffectedFiles(file_filter=file_filter):
2686 for line_num, line in fpath.ChangedContents():
2687 for (deprecated_value, value) in _DEPRECATED_CSS:
dbeam070cfe62014-10-22 06:44:022688 if deprecated_value in line:
[email protected]fd20b902014-05-09 02:14:532689 results.append(output_api.PresubmitError(
2690 "%s:%d: Use of deprecated CSS %s, use %s instead" %
2691 (fpath.LocalPath(), line_num, deprecated_value, value)))
2692 return results
2693
mohan.reddyf21db962014-10-16 12:26:472694
dbeam070cfe62014-10-22 06:44:022695_DEPRECATED_JS = [
2696 ( "__lookupGetter__", "Object.getOwnPropertyDescriptor" ),
2697 ( "__defineGetter__", "Object.defineProperty" ),
2698 ( "__defineSetter__", "Object.defineProperty" ),
2699]
2700
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202701
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492702# TODO: add unit tests
dbeam1ec68ac2016-12-15 05:22:242703def _CheckNoDeprecatedJs(input_api, output_api):
dbeam070cfe62014-10-22 06:44:022704 """Make sure that we don't use deprecated JS in Chrome code."""
2705 results = []
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492706 file_inclusion_pattern = [r".+\.js$"] # TODO(dbeam): .html?
dbeam070cfe62014-10-22 06:44:022707 black_list = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
2708 input_api.DEFAULT_BLACK_LIST)
2709 file_filter = lambda f: input_api.FilterSourceFile(
2710 f, white_list=file_inclusion_pattern, black_list=black_list)
2711 for fpath in input_api.AffectedFiles(file_filter=file_filter):
2712 for lnum, line in fpath.ChangedContents():
2713 for (deprecated, replacement) in _DEPRECATED_JS:
2714 if deprecated in line:
2715 results.append(output_api.PresubmitError(
2716 "%s:%d: Use of deprecated JS %s, use %s instead" %
2717 (fpath.LocalPath(), lnum, deprecated, replacement)))
2718 return results
2719
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202720
rlanday6802cf632017-05-30 17:48:362721def _CheckForRelativeIncludes(input_api, output_api):
2722 # Need to set the sys.path so PRESUBMIT_test.py runs properly
2723 import sys
2724 original_sys_path = sys.path
2725 try:
2726 sys.path = sys.path + [input_api.os_path.join(
2727 input_api.PresubmitLocalPath(), 'buildtools', 'checkdeps')]
2728 from cpp_checker import CppChecker
2729 finally:
2730 # Restore sys.path to what it was before.
2731 sys.path = original_sys_path
2732
2733 bad_files = {}
2734 for f in input_api.AffectedFiles(include_deletes=False):
2735 if (f.LocalPath().startswith('third_party') and
Kent Tamura32dbbcb2018-11-30 12:28:492736 not f.LocalPath().startswith('third_party/blink') and
2737 not f.LocalPath().startswith('third_party\\blink')):
rlanday6802cf632017-05-30 17:48:362738 continue
2739
2740 if not CppChecker.IsCppFile(f.LocalPath()):
2741 continue
2742
Vaclav Brozekd5de76a2018-03-17 07:57:502743 relative_includes = [line for _, line in f.ChangedContents()
rlanday6802cf632017-05-30 17:48:362744 if "#include" in line and "../" in line]
2745 if not relative_includes:
2746 continue
2747 bad_files[f.LocalPath()] = relative_includes
2748
2749 if not bad_files:
2750 return []
2751
2752 error_descriptions = []
2753 for file_path, bad_lines in bad_files.iteritems():
2754 error_description = file_path
2755 for line in bad_lines:
2756 error_description += '\n ' + line
2757 error_descriptions.append(error_description)
2758
2759 results = []
2760 results.append(output_api.PresubmitError(
2761 'You added one or more relative #include paths (including "../").\n'
2762 'These shouldn\'t be used because they can be used to include headers\n'
2763 'from code that\'s not correctly specified as a dependency in the\n'
2764 'relevant BUILD.gn file(s).',
2765 error_descriptions))
2766
2767 return results
2768
Takeshi Yoshinoe387aa32017-08-02 13:16:132769
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202770def _CheckWatchlistDefinitionsEntrySyntax(key, value, ast):
2771 if not isinstance(key, ast.Str):
2772 return 'Key at line %d must be a string literal' % key.lineno
2773 if not isinstance(value, ast.Dict):
2774 return 'Value at line %d must be a dict' % value.lineno
2775 if len(value.keys) != 1:
2776 return 'Dict at line %d must have single entry' % value.lineno
2777 if not isinstance(value.keys[0], ast.Str) or value.keys[0].s != 'filepath':
2778 return (
2779 'Entry at line %d must have a string literal \'filepath\' as key' %
2780 value.lineno)
2781 return None
Takeshi Yoshinoe387aa32017-08-02 13:16:132782
Takeshi Yoshinoe387aa32017-08-02 13:16:132783
Sergey Ulanov4af16052018-11-08 02:41:462784def _CheckWatchlistsEntrySyntax(key, value, ast, email_regex):
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202785 if not isinstance(key, ast.Str):
2786 return 'Key at line %d must be a string literal' % key.lineno
2787 if not isinstance(value, ast.List):
2788 return 'Value at line %d must be a list' % value.lineno
Sergey Ulanov4af16052018-11-08 02:41:462789 for element in value.elts:
2790 if not isinstance(element, ast.Str):
2791 return 'Watchlist elements on line %d is not a string' % key.lineno
2792 if not email_regex.match(element.s):
2793 return ('Watchlist element on line %d doesn\'t look like a valid ' +
2794 'email: %s') % (key.lineno, element.s)
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202795 return None
Takeshi Yoshinoe387aa32017-08-02 13:16:132796
Takeshi Yoshinoe387aa32017-08-02 13:16:132797
Sergey Ulanov4af16052018-11-08 02:41:462798def _CheckWATCHLISTSEntries(wd_dict, w_dict, input_api):
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202799 mismatch_template = (
2800 'Mismatch between WATCHLIST_DEFINITIONS entry (%s) and WATCHLISTS '
2801 'entry (%s)')
Takeshi Yoshinoe387aa32017-08-02 13:16:132802
Sergey Ulanov4af16052018-11-08 02:41:462803 email_regex = input_api.re.compile(
2804 r"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+$")
2805
2806 ast = input_api.ast
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202807 i = 0
2808 last_key = ''
2809 while True:
2810 if i >= len(wd_dict.keys):
2811 if i >= len(w_dict.keys):
2812 return None
2813 return mismatch_template % ('missing', 'line %d' % w_dict.keys[i].lineno)
2814 elif i >= len(w_dict.keys):
2815 return (
2816 mismatch_template % ('line %d' % wd_dict.keys[i].lineno, 'missing'))
Takeshi Yoshinoe387aa32017-08-02 13:16:132817
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202818 wd_key = wd_dict.keys[i]
2819 w_key = w_dict.keys[i]
Takeshi Yoshinoe387aa32017-08-02 13:16:132820
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202821 result = _CheckWatchlistDefinitionsEntrySyntax(
2822 wd_key, wd_dict.values[i], ast)
2823 if result is not None:
2824 return 'Bad entry in WATCHLIST_DEFINITIONS dict: %s' % result
Takeshi Yoshinoe387aa32017-08-02 13:16:132825
Sergey Ulanov4af16052018-11-08 02:41:462826 result = _CheckWatchlistsEntrySyntax(
2827 w_key, w_dict.values[i], ast, email_regex)
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202828 if result is not None:
2829 return 'Bad entry in WATCHLISTS dict: %s' % result
2830
2831 if wd_key.s != w_key.s:
2832 return mismatch_template % (
2833 '%s at line %d' % (wd_key.s, wd_key.lineno),
2834 '%s at line %d' % (w_key.s, w_key.lineno))
2835
2836 if wd_key.s < last_key:
2837 return (
2838 'WATCHLISTS dict is not sorted lexicographically at line %d and %d' %
2839 (wd_key.lineno, w_key.lineno))
2840 last_key = wd_key.s
2841
2842 i = i + 1
2843
2844
Sergey Ulanov4af16052018-11-08 02:41:462845def _CheckWATCHLISTSSyntax(expression, input_api):
2846 ast = input_api.ast
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202847 if not isinstance(expression, ast.Expression):
2848 return 'WATCHLISTS file must contain a valid expression'
2849 dictionary = expression.body
2850 if not isinstance(dictionary, ast.Dict) or len(dictionary.keys) != 2:
2851 return 'WATCHLISTS file must have single dict with exactly two entries'
2852
2853 first_key = dictionary.keys[0]
2854 first_value = dictionary.values[0]
2855 second_key = dictionary.keys[1]
2856 second_value = dictionary.values[1]
2857
2858 if (not isinstance(first_key, ast.Str) or
2859 first_key.s != 'WATCHLIST_DEFINITIONS' or
2860 not isinstance(first_value, ast.Dict)):
2861 return (
2862 'The first entry of the dict in WATCHLISTS file must be '
2863 'WATCHLIST_DEFINITIONS dict')
2864
2865 if (not isinstance(second_key, ast.Str) or
2866 second_key.s != 'WATCHLISTS' or
2867 not isinstance(second_value, ast.Dict)):
2868 return (
2869 'The second entry of the dict in WATCHLISTS file must be '
2870 'WATCHLISTS dict')
2871
Sergey Ulanov4af16052018-11-08 02:41:462872 return _CheckWATCHLISTSEntries(first_value, second_value, input_api)
Takeshi Yoshinoe387aa32017-08-02 13:16:132873
2874
2875def _CheckWATCHLISTS(input_api, output_api):
2876 for f in input_api.AffectedFiles(include_deletes=False):
2877 if f.LocalPath() == 'WATCHLISTS':
2878 contents = input_api.ReadFile(f, 'r')
2879
2880 try:
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202881 # First, make sure that it can be evaluated.
Takeshi Yoshinoe387aa32017-08-02 13:16:132882 input_api.ast.literal_eval(contents)
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202883 # Get an AST tree for it and scan the tree for detailed style checking.
2884 expression = input_api.ast.parse(
2885 contents, filename='WATCHLISTS', mode='eval')
2886 except ValueError as e:
2887 return [output_api.PresubmitError(
2888 'Cannot parse WATCHLISTS file', long_text=repr(e))]
2889 except SyntaxError as e:
2890 return [output_api.PresubmitError(
2891 'Cannot parse WATCHLISTS file', long_text=repr(e))]
2892 except TypeError as e:
2893 return [output_api.PresubmitError(
2894 'Cannot parse WATCHLISTS file', long_text=repr(e))]
Takeshi Yoshinoe387aa32017-08-02 13:16:132895
Sergey Ulanov4af16052018-11-08 02:41:462896 result = _CheckWATCHLISTSSyntax(expression, input_api)
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202897 if result is not None:
2898 return [output_api.PresubmitError(result)]
2899 break
Takeshi Yoshinoe387aa32017-08-02 13:16:132900
2901 return []
2902
2903
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:192904def _CheckNewHeaderWithoutGnChange(input_api, output_api):
2905 """Checks that newly added header files have corresponding GN changes.
2906 Note that this is only a heuristic. To be precise, run script:
2907 build/check_gn_headers.py.
2908 """
2909
2910 def headers(f):
2911 return input_api.FilterSourceFile(
2912 f, white_list=(r'.+%s' % _HEADER_EXTENSIONS, ))
2913
2914 new_headers = []
2915 for f in input_api.AffectedSourceFiles(headers):
2916 if f.Action() != 'A':
2917 continue
2918 new_headers.append(f.LocalPath())
2919
2920 def gn_files(f):
2921 return input_api.FilterSourceFile(f, white_list=(r'.+\.gn', ))
2922
2923 all_gn_changed_contents = ''
2924 for f in input_api.AffectedSourceFiles(gn_files):
2925 for _, line in f.ChangedContents():
2926 all_gn_changed_contents += line
2927
2928 problems = []
2929 for header in new_headers:
2930 basename = input_api.os_path.basename(header)
2931 if basename not in all_gn_changed_contents:
2932 problems.append(header)
2933
2934 if problems:
2935 return [output_api.PresubmitPromptWarning(
2936 'Missing GN changes for new header files', items=sorted(problems),
2937 long_text='Please double check whether newly added header files need '
2938 'corresponding changes in gn or gni files.\nThis checking is only a '
2939 'heuristic. Run build/check_gn_headers.py to be precise.\n'
2940 'Read https://crbug.com/661774 for more info.')]
2941 return []
2942
2943
Michael Giuffridad3bc8672018-10-25 22:48:022944def _CheckCorrectProductNameInMessages(input_api, output_api):
2945 """Check that Chromium-branded strings don't include "Chrome" or vice versa.
2946
2947 This assumes we won't intentionally reference one product from the other
2948 product.
2949 """
2950 all_problems = []
2951 test_cases = [{
2952 "filename_postfix": "google_chrome_strings.grd",
2953 "correct_name": "Chrome",
2954 "incorrect_name": "Chromium",
2955 }, {
2956 "filename_postfix": "chromium_strings.grd",
2957 "correct_name": "Chromium",
2958 "incorrect_name": "Chrome",
2959 }]
2960
2961 for test_case in test_cases:
2962 problems = []
2963 filename_filter = lambda x: x.LocalPath().endswith(
2964 test_case["filename_postfix"])
2965
2966 # Check each new line. Can yield false positives in multiline comments, but
2967 # easier than trying to parse the XML because messages can have nested
2968 # children, and associating message elements with affected lines is hard.
2969 for f in input_api.AffectedSourceFiles(filename_filter):
2970 for line_num, line in f.ChangedContents():
2971 if "<message" in line or "<!--" in line or "-->" in line:
2972 continue
2973 if test_case["incorrect_name"] in line:
2974 problems.append(
2975 "Incorrect product name in %s:%d" % (f.LocalPath(), line_num))
2976
2977 if problems:
2978 message = (
2979 "Strings in %s-branded string files should reference \"%s\", not \"%s\""
2980 % (test_case["correct_name"], test_case["correct_name"],
2981 test_case["incorrect_name"]))
2982 all_problems.append(
2983 output_api.PresubmitPromptWarning(message, items=problems))
2984
2985 return all_problems
2986
2987
dgnaa68d5e2015-06-10 10:08:222988def _AndroidSpecificOnUploadChecks(input_api, output_api):
2989 """Groups checks that target android code."""
2990 results = []
dgnaa68d5e2015-06-10 10:08:222991 results.extend(_CheckAndroidCrLogUsage(input_api, output_api))
agrieve7b6479d82015-10-07 14:24:222992 results.extend(_CheckAndroidNewMdpiAssetLocation(input_api, output_api))
dskiba88634f4e2015-08-14 23:03:292993 results.extend(_CheckAndroidToastUsage(input_api, output_api))
Yoland Yanb92fa522017-08-28 17:37:062994 results.extend(_CheckAndroidTestJUnitInheritance(input_api, output_api))
2995 results.extend(_CheckAndroidTestJUnitFrameworkImport(input_api, output_api))
yolandyan45001472016-12-21 21:12:422996 results.extend(_CheckAndroidTestAnnotationUsage(input_api, output_api))
Nate Fischer535972b2017-09-16 01:06:182997 results.extend(_CheckAndroidWebkitImports(input_api, output_api))
dgnaa68d5e2015-06-10 10:08:222998 return results
2999
3000
[email protected]22c9bd72011-03-27 16:47:393001def _CommonChecks(input_api, output_api):
3002 """Checks common to both upload and commit."""
3003 results = []
3004 results.extend(input_api.canned_checks.PanProjectChecks(
[email protected]3de922f2013-12-20 13:27:383005 input_api, output_api,
qyearsleyfa2cfcf82016-12-15 18:03:543006 excluded_paths=_EXCLUDED_PATHS))
Eric Boren6fd2b932018-01-25 15:05:083007
3008 author = input_api.change.author_email
3009 if author and author not in _KNOWN_ROBOTS:
3010 results.extend(
3011 input_api.canned_checks.CheckAuthorizedAuthor(input_api, output_api))
3012
[email protected]55459852011-08-10 15:17:193013 results.extend(
[email protected]760deea2013-12-10 19:33:493014 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
Vaclav Brozek7dbc28c2018-03-27 08:35:233015 results.extend(
3016 _CheckNoProductionCodeUsingTestOnlyFunctionsJava(input_api, output_api))
[email protected]10689ca2011-09-02 02:31:543017 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
[email protected]72df4e782012-06-21 16:28:183018 results.extend(_CheckNoUNIT_TESTInSourceFiles(input_api, output_api))
Dominic Battre033531052018-09-24 15:45:343019 results.extend(_CheckNoDISABLETypoInTests(input_api, output_api))
danakj61c1aa22015-10-26 19:55:523020 results.extend(_CheckDCHECK_IS_ONHasBraces(input_api, output_api))
[email protected]8ea5d4b2011-09-13 21:49:223021 results.extend(_CheckNoNewWStrings(input_api, output_api))
[email protected]2a8ac9c2011-10-19 17:20:443022 results.extend(_CheckNoDEPSGIT(input_api, output_api))
[email protected]127f18ec2012-06-16 05:05:593023 results.extend(_CheckNoBannedFunctions(input_api, output_api))
[email protected]6c063c62012-07-11 19:11:063024 results.extend(_CheckNoPragmaOnce(input_api, output_api))
[email protected]e7479052012-09-19 00:26:123025 results.extend(_CheckNoTrinaryTrueFalse(input_api, output_api))
[email protected]55f9f382012-07-31 11:02:183026 results.extend(_CheckUnwantedDependencies(input_api, output_api))
[email protected]fbcafe5a2012-08-08 15:31:223027 results.extend(_CheckFilePermissions(input_api, output_api))
robertocn832f5992017-01-04 19:01:303028 results.extend(_CheckTeamTags(input_api, output_api))
[email protected]c8278b32012-10-30 20:35:493029 results.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api, output_api))
[email protected]70ca77752012-11-20 03:45:033030 results.extend(_CheckForVersionControlConflicts(input_api, output_api))
[email protected]b8079ae4a2012-12-05 19:56:493031 results.extend(_CheckPatchFiles(input_api, output_api))
[email protected]06e6d0ff2012-12-11 01:36:443032 results.extend(_CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api))
[email protected]d2530012013-01-25 16:39:273033 results.extend(_CheckNoAbbreviationInPngFileName(input_api, output_api))
Kent Tamura5a8755d2017-06-29 23:37:073034 results.extend(_CheckBuildConfigMacrosWithoutInclude(input_api, output_api))
[email protected]b00342e7f2013-03-26 16:21:543035 results.extend(_CheckForInvalidOSMacros(input_api, output_api))
lliabraa35bab3932014-10-01 12:16:443036 results.extend(_CheckForInvalidIfDefinedMacros(input_api, output_api))
yolandyandaabc6d2016-04-18 18:29:393037 results.extend(_CheckFlakyTestUsage(input_api, output_api))
[email protected]e871964c2013-05-13 14:14:553038 results.extend(_CheckAddedDepsHaveTargetApprovals(input_api, output_api))
[email protected]9f919cc2013-07-31 03:04:043039 results.extend(
3040 input_api.canned_checks.CheckChangeHasNoTabs(
3041 input_api,
3042 output_api,
3043 source_file_filter=lambda x: x.LocalPath().endswith('.grd')))
[email protected]85218562013-11-22 07:41:403044 results.extend(_CheckSpamLogging(input_api, output_api))
[email protected]49aa76a2013-12-04 06:59:163045 results.extend(_CheckForAnonymousVariables(input_api, output_api))
[email protected]999261d2014-03-03 20:08:083046 results.extend(_CheckUserActionUpdate(input_api, output_api))
dbeam1ec68ac2016-12-15 05:22:243047 results.extend(_CheckNoDeprecatedCss(input_api, output_api))
3048 results.extend(_CheckNoDeprecatedJs(input_api, output_api))
[email protected]99171a92014-06-03 08:44:473049 results.extend(_CheckParseErrors(input_api, output_api))
mlamouria82272622014-09-16 18:45:043050 results.extend(_CheckForIPCRules(input_api, output_api))
Stephen Martinis97a394142018-06-07 23:06:053051 results.extend(_CheckForLongPathnames(input_api, output_api))
Daniel Bratell8ba52722018-03-02 16:06:143052 results.extend(_CheckForIncludeGuards(input_api, output_api))
mostynbb639aca52015-01-07 20:31:233053 results.extend(_CheckForWindowsLineEndings(input_api, output_api))
glidere61efad2015-02-18 17:39:433054 results.extend(_CheckSingletonInHeaders(input_api, output_api))
agrievef32bcc72016-04-04 14:57:403055 results.extend(_CheckPydepsNeedsUpdating(input_api, output_api))
wnwenbdc444e2016-05-25 13:44:153056 results.extend(_CheckJavaStyle(input_api, output_api))
dchenge07de812016-06-20 19:27:173057 results.extend(_CheckIpcOwners(input_api, output_api))
jbriance9e12f162016-11-25 07:57:503058 results.extend(_CheckUselessForwardDeclarations(input_api, output_api))
rlanday6802cf632017-05-30 17:48:363059 results.extend(_CheckForRelativeIncludes(input_api, output_api))
Takeshi Yoshinoe387aa32017-08-02 13:16:133060 results.extend(_CheckWATCHLISTS(input_api, output_api))
Sergiy Byelozyorov366b6482017-11-06 18:20:433061 results.extend(input_api.RunTests(
3062 input_api.canned_checks.CheckVPythonSpec(input_api, output_api)))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143063 results.extend(_CheckTranslationScreenshots(input_api, output_api))
Michael Giuffridad3bc8672018-10-25 22:48:023064 results.extend(_CheckCorrectProductNameInMessages(input_api, output_api))
[email protected]2299dcf2012-11-15 19:56:243065
Vaclav Brozekcdc7defb2018-03-20 09:54:353066 for f in input_api.AffectedFiles():
3067 path, name = input_api.os_path.split(f.LocalPath())
3068 if name == 'PRESUBMIT.py':
3069 full_path = input_api.os_path.join(input_api.PresubmitLocalPath(), path)
Caleb Rouleaua6117be2018-05-11 20:10:003070 test_file = input_api.os_path.join(path, 'PRESUBMIT_test.py')
3071 if f.Action() != 'D' and input_api.os_path.exists(test_file):
Dirk Pranke38557312018-04-18 00:53:073072 # The PRESUBMIT.py file (and the directory containing it) might
3073 # have been affected by being moved or removed, so only try to
3074 # run the tests if they still exist.
3075 results.extend(input_api.canned_checks.RunUnitTestsInDirectory(
3076 input_api, output_api, full_path,
3077 whitelist=[r'^PRESUBMIT_test\.py$']))
[email protected]22c9bd72011-03-27 16:47:393078 return results
[email protected]1f7b4172010-01-28 01:17:343079
[email protected]b337cb5b2011-01-23 21:24:053080
[email protected]b8079ae4a2012-12-05 19:56:493081def _CheckPatchFiles(input_api, output_api):
3082 problems = [f.LocalPath() for f in input_api.AffectedFiles()
3083 if f.LocalPath().endswith(('.orig', '.rej'))]
3084 if problems:
3085 return [output_api.PresubmitError(
3086 "Don't commit .rej and .orig files.", problems)]
[email protected]2fdd1f362013-01-16 03:56:033087 else:
3088 return []
[email protected]b8079ae4a2012-12-05 19:56:493089
3090
Kent Tamura5a8755d2017-06-29 23:37:073091def _CheckBuildConfigMacrosWithoutInclude(input_api, output_api):
Kent Tamura79ef8f82017-07-18 00:00:213092 # Excludes OS_CHROMEOS, which is not defined in build_config.h.
3093 macro_re = input_api.re.compile(r'^\s*#(el)?if.*\bdefined\(((OS_(?!CHROMEOS)|'
3094 'COMPILER_|ARCH_CPU_|WCHAR_T_IS_)[^)]*)')
Kent Tamura5a8755d2017-06-29 23:37:073095 include_re = input_api.re.compile(
3096 r'^#include\s+"build/build_config.h"', input_api.re.MULTILINE)
3097 extension_re = input_api.re.compile(r'\.[a-z]+$')
3098 errors = []
3099 for f in input_api.AffectedFiles():
3100 if not f.LocalPath().endswith(('.h', '.c', '.cc', '.cpp', '.m', '.mm')):
3101 continue
3102 found_line_number = None
3103 found_macro = None
3104 for line_num, line in f.ChangedContents():
3105 match = macro_re.search(line)
3106 if match:
3107 found_line_number = line_num
3108 found_macro = match.group(2)
3109 break
3110 if not found_line_number:
3111 continue
3112
3113 found_include = False
3114 for line in f.NewContents():
3115 if include_re.search(line):
3116 found_include = True
3117 break
3118 if found_include:
3119 continue
3120
3121 if not f.LocalPath().endswith('.h'):
3122 primary_header_path = extension_re.sub('.h', f.AbsoluteLocalPath())
3123 try:
3124 content = input_api.ReadFile(primary_header_path, 'r')
3125 if include_re.search(content):
3126 continue
3127 except IOError:
3128 pass
3129 errors.append('%s:%d %s macro is used without including build/'
3130 'build_config.h.'
3131 % (f.LocalPath(), found_line_number, found_macro))
3132 if errors:
3133 return [output_api.PresubmitPromptWarning('\n'.join(errors))]
3134 return []
3135
3136
[email protected]b00342e7f2013-03-26 16:21:543137def _DidYouMeanOSMacro(bad_macro):
3138 try:
3139 return {'A': 'OS_ANDROID',
3140 'B': 'OS_BSD',
3141 'C': 'OS_CHROMEOS',
3142 'F': 'OS_FREEBSD',
3143 'L': 'OS_LINUX',
3144 'M': 'OS_MACOSX',
3145 'N': 'OS_NACL',
3146 'O': 'OS_OPENBSD',
3147 'P': 'OS_POSIX',
3148 'S': 'OS_SOLARIS',
3149 'W': 'OS_WIN'}[bad_macro[3].upper()]
3150 except KeyError:
3151 return ''
3152
3153
3154def _CheckForInvalidOSMacrosInFile(input_api, f):
3155 """Check for sensible looking, totally invalid OS macros."""
3156 preprocessor_statement = input_api.re.compile(r'^\s*#')
3157 os_macro = input_api.re.compile(r'defined\((OS_[^)]+)\)')
3158 results = []
3159 for lnum, line in f.ChangedContents():
3160 if preprocessor_statement.search(line):
3161 for match in os_macro.finditer(line):
3162 if not match.group(1) in _VALID_OS_MACROS:
3163 good = _DidYouMeanOSMacro(match.group(1))
3164 did_you_mean = ' (did you mean %s?)' % good if good else ''
3165 results.append(' %s:%d %s%s' % (f.LocalPath(),
3166 lnum,
3167 match.group(1),
3168 did_you_mean))
3169 return results
3170
3171
3172def _CheckForInvalidOSMacros(input_api, output_api):
3173 """Check all affected files for invalid OS macros."""
3174 bad_macros = []
tzik3f295992018-12-04 20:32:233175 for f in input_api.AffectedSourceFiles(None):
ellyjones47654342016-05-06 15:50:473176 if not f.LocalPath().endswith(('.py', '.js', '.html', '.css', '.md')):
[email protected]b00342e7f2013-03-26 16:21:543177 bad_macros.extend(_CheckForInvalidOSMacrosInFile(input_api, f))
3178
3179 if not bad_macros:
3180 return []
3181
3182 return [output_api.PresubmitError(
3183 'Possibly invalid OS macro[s] found. Please fix your code\n'
3184 'or add your macro to src/PRESUBMIT.py.', bad_macros)]
3185
lliabraa35bab3932014-10-01 12:16:443186
3187def _CheckForInvalidIfDefinedMacrosInFile(input_api, f):
3188 """Check all affected files for invalid "if defined" macros."""
3189 ALWAYS_DEFINED_MACROS = (
3190 "TARGET_CPU_PPC",
3191 "TARGET_CPU_PPC64",
3192 "TARGET_CPU_68K",
3193 "TARGET_CPU_X86",
3194 "TARGET_CPU_ARM",
3195 "TARGET_CPU_MIPS",
3196 "TARGET_CPU_SPARC",
3197 "TARGET_CPU_ALPHA",
3198 "TARGET_IPHONE_SIMULATOR",
3199 "TARGET_OS_EMBEDDED",
3200 "TARGET_OS_IPHONE",
3201 "TARGET_OS_MAC",
3202 "TARGET_OS_UNIX",
3203 "TARGET_OS_WIN32",
3204 )
3205 ifdef_macro = input_api.re.compile(r'^\s*#.*(?:ifdef\s|defined\()([^\s\)]+)')
3206 results = []
3207 for lnum, line in f.ChangedContents():
3208 for match in ifdef_macro.finditer(line):
3209 if match.group(1) in ALWAYS_DEFINED_MACROS:
3210 always_defined = ' %s is always defined. ' % match.group(1)
3211 did_you_mean = 'Did you mean \'#if %s\'?' % match.group(1)
3212 results.append(' %s:%d %s\n\t%s' % (f.LocalPath(),
3213 lnum,
3214 always_defined,
3215 did_you_mean))
3216 return results
3217
3218
3219def _CheckForInvalidIfDefinedMacros(input_api, output_api):
3220 """Check all affected files for invalid "if defined" macros."""
3221 bad_macros = []
3222 for f in input_api.AffectedFiles():
sdefresne4e1eccb32017-05-24 08:45:213223 if f.LocalPath().startswith('third_party/sqlite/'):
3224 continue
lliabraa35bab3932014-10-01 12:16:443225 if f.LocalPath().endswith(('.h', '.c', '.cc', '.m', '.mm')):
3226 bad_macros.extend(_CheckForInvalidIfDefinedMacrosInFile(input_api, f))
3227
3228 if not bad_macros:
3229 return []
3230
3231 return [output_api.PresubmitError(
3232 'Found ifdef check on always-defined macro[s]. Please fix your code\n'
3233 'or check the list of ALWAYS_DEFINED_MACROS in src/PRESUBMIT.py.',
3234 bad_macros)]
3235
3236
mlamouria82272622014-09-16 18:45:043237def _CheckForIPCRules(input_api, output_api):
3238 """Check for same IPC rules described in
3239 http://www.chromium.org/Home/chromium-security/education/security-tips-for-ipc
3240 """
3241 base_pattern = r'IPC_ENUM_TRAITS\('
3242 inclusion_pattern = input_api.re.compile(r'(%s)' % base_pattern)
3243 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_pattern)
3244
3245 problems = []
3246 for f in input_api.AffectedSourceFiles(None):
3247 local_path = f.LocalPath()
3248 if not local_path.endswith('.h'):
3249 continue
3250 for line_number, line in f.ChangedContents():
3251 if inclusion_pattern.search(line) and not comment_pattern.search(line):
3252 problems.append(
3253 '%s:%d\n %s' % (local_path, line_number, line.strip()))
3254
3255 if problems:
3256 return [output_api.PresubmitPromptWarning(
3257 _IPC_ENUM_TRAITS_DEPRECATED, problems)]
3258 else:
3259 return []
3260
[email protected]b00342e7f2013-03-26 16:21:543261
Stephen Martinis97a394142018-06-07 23:06:053262def _CheckForLongPathnames(input_api, output_api):
3263 """Check to make sure no files being submitted have long paths.
3264 This causes issues on Windows.
3265 """
3266 problems = []
3267 for f in input_api.AffectedSourceFiles(None):
3268 local_path = f.LocalPath()
3269 # Windows has a path limit of 260 characters. Limit path length to 200 so
3270 # that we have some extra for the prefix on dev machines and the bots.
3271 if len(local_path) > 200:
3272 problems.append(local_path)
3273
3274 if problems:
3275 return [output_api.PresubmitError(_LONG_PATH_ERROR, problems)]
3276 else:
3277 return []
3278
3279
Daniel Bratell8ba52722018-03-02 16:06:143280def _CheckForIncludeGuards(input_api, output_api):
3281 """Check that header files have proper guards against multiple inclusion.
3282 If a file should not have such guards (and it probably should) then it
3283 should include the string "no-include-guard-because-multiply-included".
3284 """
Daniel Bratell6a75baef62018-06-04 10:04:453285 def is_chromium_header_file(f):
3286 # We only check header files under the control of the Chromium
3287 # project. That is, those outside third_party apart from
3288 # third_party/blink.
3289 file_with_path = input_api.os_path.normpath(f.LocalPath())
3290 return (file_with_path.endswith('.h') and
3291 (not file_with_path.startswith('third_party') or
3292 file_with_path.startswith(
3293 input_api.os_path.join('third_party', 'blink'))))
Daniel Bratell8ba52722018-03-02 16:06:143294
3295 def replace_special_with_underscore(string):
Olivier Robinbba137492018-07-30 11:31:343296 return input_api.re.sub(r'[+\\/.-]', '_', string)
Daniel Bratell8ba52722018-03-02 16:06:143297
3298 errors = []
3299
Daniel Bratell6a75baef62018-06-04 10:04:453300 for f in input_api.AffectedSourceFiles(is_chromium_header_file):
Daniel Bratell8ba52722018-03-02 16:06:143301 guard_name = None
3302 guard_line_number = None
3303 seen_guard_end = False
3304
3305 file_with_path = input_api.os_path.normpath(f.LocalPath())
3306 base_file_name = input_api.os_path.splitext(
3307 input_api.os_path.basename(file_with_path))[0]
3308 upper_base_file_name = base_file_name.upper()
3309
3310 expected_guard = replace_special_with_underscore(
3311 file_with_path.upper() + '_')
Daniel Bratell8ba52722018-03-02 16:06:143312
3313 # For "path/elem/file_name.h" we should really only accept
Daniel Bratell39b5b062018-05-16 18:09:573314 # PATH_ELEM_FILE_NAME_H_ per coding style. Unfortunately there
3315 # are too many (1000+) files with slight deviations from the
3316 # coding style. The most important part is that the include guard
3317 # is there, and that it's unique, not the name so this check is
3318 # forgiving for existing files.
Daniel Bratell8ba52722018-03-02 16:06:143319 #
3320 # As code becomes more uniform, this could be made stricter.
3321
3322 guard_name_pattern_list = [
3323 # Anything with the right suffix (maybe with an extra _).
3324 r'\w+_H__?',
3325
Daniel Bratell39b5b062018-05-16 18:09:573326 # To cover include guards with old Blink style.
Daniel Bratell8ba52722018-03-02 16:06:143327 r'\w+_h',
3328
3329 # Anything including the uppercase name of the file.
3330 r'\w*' + input_api.re.escape(replace_special_with_underscore(
3331 upper_base_file_name)) + r'\w*',
3332 ]
3333 guard_name_pattern = '|'.join(guard_name_pattern_list)
3334 guard_pattern = input_api.re.compile(
3335 r'#ifndef\s+(' + guard_name_pattern + ')')
3336
3337 for line_number, line in enumerate(f.NewContents()):
3338 if 'no-include-guard-because-multiply-included' in line:
3339 guard_name = 'DUMMY' # To not trigger check outside the loop.
3340 break
3341
3342 if guard_name is None:
3343 match = guard_pattern.match(line)
3344 if match:
3345 guard_name = match.group(1)
3346 guard_line_number = line_number
3347
Daniel Bratell39b5b062018-05-16 18:09:573348 # We allow existing files to use include guards whose names
Daniel Bratell6a75baef62018-06-04 10:04:453349 # don't match the chromium style guide, but new files should
3350 # get it right.
3351 if not f.OldContents():
Daniel Bratell39b5b062018-05-16 18:09:573352 if guard_name != expected_guard:
Daniel Bratell8ba52722018-03-02 16:06:143353 errors.append(output_api.PresubmitPromptWarning(
3354 'Header using the wrong include guard name %s' % guard_name,
3355 ['%s:%d' % (f.LocalPath(), line_number + 1)],
Daniel Bratell39b5b062018-05-16 18:09:573356 'Expected: %r\nFound: %r' % (expected_guard, guard_name)))
Daniel Bratell8ba52722018-03-02 16:06:143357 else:
3358 # The line after #ifndef should have a #define of the same name.
3359 if line_number == guard_line_number + 1:
3360 expected_line = '#define %s' % guard_name
3361 if line != expected_line:
3362 errors.append(output_api.PresubmitPromptWarning(
3363 'Missing "%s" for include guard' % expected_line,
3364 ['%s:%d' % (f.LocalPath(), line_number + 1)],
3365 'Expected: %r\nGot: %r' % (expected_line, line)))
3366
3367 if not seen_guard_end and line == '#endif // %s' % guard_name:
3368 seen_guard_end = True
3369 elif seen_guard_end:
3370 if line.strip() != '':
3371 errors.append(output_api.PresubmitPromptWarning(
3372 'Include guard %s not covering the whole file' % (
3373 guard_name), [f.LocalPath()]))
3374 break # Nothing else to check and enough to warn once.
3375
3376 if guard_name is None:
3377 errors.append(output_api.PresubmitPromptWarning(
3378 'Missing include guard %s' % expected_guard,
3379 [f.LocalPath()],
3380 'Missing include guard in %s\n'
3381 'Recommended name: %s\n'
3382 'This check can be disabled by having the string\n'
3383 'no-include-guard-because-multiply-included in the header.' %
3384 (f.LocalPath(), expected_guard)))
3385
3386 return errors
3387
3388
mostynbb639aca52015-01-07 20:31:233389def _CheckForWindowsLineEndings(input_api, output_api):
3390 """Check source code and known ascii text files for Windows style line
3391 endings.
3392 """
earthdok1b5e0ee2015-03-10 15:19:103393 known_text_files = r'.*\.(txt|html|htm|mhtml|py|gyp|gypi|gn|isolate)$'
mostynbb639aca52015-01-07 20:31:233394
3395 file_inclusion_pattern = (
3396 known_text_files,
3397 r'.+%s' % _IMPLEMENTATION_EXTENSIONS
3398 )
3399
mostynbb639aca52015-01-07 20:31:233400 problems = []
Andrew Grieve933d12e2017-10-30 20:22:533401 source_file_filter = lambda f: input_api.FilterSourceFile(
3402 f, white_list=file_inclusion_pattern, black_list=None)
3403 for f in input_api.AffectedSourceFiles(source_file_filter):
Vaclav Brozekd5de76a2018-03-17 07:57:503404 include_file = False
3405 for _, line in f.ChangedContents():
mostynbb639aca52015-01-07 20:31:233406 if line.endswith('\r\n'):
Vaclav Brozekd5de76a2018-03-17 07:57:503407 include_file = True
3408 if include_file:
3409 problems.append(f.LocalPath())
mostynbb639aca52015-01-07 20:31:233410
3411 if problems:
3412 return [output_api.PresubmitPromptWarning('Are you sure that you want '
3413 'these files to contain Windows style line endings?\n' +
3414 '\n'.join(problems))]
3415
3416 return []
3417
3418
Vaclav Brozekd5de76a2018-03-17 07:57:503419def _CheckSyslogUseWarning(input_api, output_api, source_file_filter=None):
pastarmovj89f7ee12016-09-20 14:58:133420 """Checks that all source files use SYSLOG properly."""
3421 syslog_files = []
3422 for f in input_api.AffectedSourceFiles(source_file_filter):
pastarmovj032ba5bc2017-01-12 10:41:563423 for line_number, line in f.ChangedContents():
3424 if 'SYSLOG' in line:
3425 syslog_files.append(f.LocalPath() + ':' + str(line_number))
3426
pastarmovj89f7ee12016-09-20 14:58:133427 if syslog_files:
3428 return [output_api.PresubmitPromptWarning(
3429 'Please make sure there are no privacy sensitive bits of data in SYSLOG'
3430 ' calls.\nFiles to check:\n', items=syslog_files)]
3431 return []
3432
3433
[email protected]1f7b4172010-01-28 01:17:343434def CheckChangeOnUpload(input_api, output_api):
3435 results = []
3436 results.extend(_CommonChecks(input_api, output_api))
tandriief664692014-09-23 14:51:473437 results.extend(_CheckValidHostsInDEPS(input_api, output_api))
scottmg39b29952014-12-08 18:31:283438 results.extend(
jam93a6ee792017-02-08 23:59:223439 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
mcasasb7440c282015-02-04 14:52:193440 results.extend(_CheckUmaHistogramChanges(input_api, output_api))
dgnaa68d5e2015-06-10 10:08:223441 results.extend(_AndroidSpecificOnUploadChecks(input_api, output_api))
pastarmovj89f7ee12016-09-20 14:58:133442 results.extend(_CheckSyslogUseWarning(input_api, output_api))
estadee17314a02017-01-12 16:22:163443 results.extend(_CheckGoogleSupportAnswerUrl(input_api, output_api))
Vaclav Brozekea41ab22018-04-06 13:21:533444 results.extend(_CheckUniquePtr(input_api, output_api))
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:193445 results.extend(_CheckNewHeaderWithoutGnChange(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:543446 return results
[email protected]ca8d1982009-02-19 16:33:123447
3448
[email protected]1bfb8322014-04-23 01:02:413449def GetTryServerMasterForBot(bot):
3450 """Returns the Try Server master for the given bot.
3451
[email protected]0bb112362014-07-26 04:38:323452 It tries to guess the master from the bot name, but may still fail
3453 and return None. There is no longer a default master.
3454 """
3455 # Potentially ambiguous bot names are listed explicitly.
3456 master_map = {
tandriie5587792016-07-14 00:34:503457 'chromium_presubmit': 'master.tryserver.chromium.linux',
3458 'tools_build_presubmit': 'master.tryserver.chromium.linux',
[email protected]1bfb8322014-04-23 01:02:413459 }
[email protected]0bb112362014-07-26 04:38:323460 master = master_map.get(bot)
3461 if not master:
wnwen4fbaab82016-05-25 12:54:363462 if 'android' in bot:
tandriie5587792016-07-14 00:34:503463 master = 'master.tryserver.chromium.android'
wnwen4fbaab82016-05-25 12:54:363464 elif 'linux' in bot or 'presubmit' in bot:
tandriie5587792016-07-14 00:34:503465 master = 'master.tryserver.chromium.linux'
[email protected]0bb112362014-07-26 04:38:323466 elif 'win' in bot:
tandriie5587792016-07-14 00:34:503467 master = 'master.tryserver.chromium.win'
[email protected]0bb112362014-07-26 04:38:323468 elif 'mac' in bot or 'ios' in bot:
tandriie5587792016-07-14 00:34:503469 master = 'master.tryserver.chromium.mac'
[email protected]0bb112362014-07-26 04:38:323470 return master
[email protected]1bfb8322014-04-23 01:02:413471
3472
[email protected]ca8d1982009-02-19 16:33:123473def CheckChangeOnCommit(input_api, output_api):
[email protected]fe5f57c52009-06-05 14:25:543474 results = []
[email protected]1f7b4172010-01-28 01:17:343475 results.extend(_CommonChecks(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:543476 # Make sure the tree is 'open'.
[email protected]806e98e2010-03-19 17:49:273477 results.extend(input_api.canned_checks.CheckTreeIsOpen(
[email protected]7f238152009-08-12 19:00:343478 input_api,
3479 output_api,
[email protected]2fdd1f362013-01-16 03:56:033480 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:273481
jam93a6ee792017-02-08 23:59:223482 results.extend(
3483 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
[email protected]3e4eb112011-01-18 03:29:543484 results.extend(input_api.canned_checks.CheckChangeHasBugField(
3485 input_api, output_api))
[email protected]c4b47562011-12-05 23:39:413486 results.extend(input_api.canned_checks.CheckChangeHasDescription(
3487 input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:543488 return results
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143489
3490
3491def _CheckTranslationScreenshots(input_api, output_api):
3492 PART_FILE_TAG = "part"
3493 import os
3494 import sys
3495 from io import StringIO
3496
3497 try:
3498 old_sys_path = sys.path
3499 sys.path = sys.path + [input_api.os_path.join(
3500 input_api.PresubmitLocalPath(), 'tools', 'grit')]
3501 import grit.grd_reader
3502 import grit.node.message
3503 import grit.util
3504 finally:
3505 sys.path = old_sys_path
3506
3507 def _GetGrdMessages(grd_path_or_string, dir_path='.'):
3508 """Load the grd file and return a dict of message ids to messages.
3509
3510 Ignores any nested grdp files pointed by <part> tag.
3511 """
3512 doc = grit.grd_reader.Parse(grd_path_or_string, dir_path,
3513 stop_after=None, first_ids_file=None,
3514 debug=False, defines=None,
3515 tags_to_ignore=set([PART_FILE_TAG]))
3516 return {
3517 msg.attrs['name']:msg for msg in doc.GetChildrenOfType(
3518 grit.node.message.MessageNode)
3519 }
3520
3521 def _GetGrdpMessagesFromString(grdp_string):
3522 """Parses the contents of a grdp file given in grdp_string.
3523
3524 grd_reader can't parse grdp files directly. Instead, this creates a
3525 temporary directory with a grd file pointing to the grdp file, and loads the
3526 grd from there. Any nested grdp files (pointed by <part> tag) are ignored.
3527 """
3528 WRAPPER = """<?xml version="1.0" encoding="utf-8"?>
3529 <grit latest_public_release="1" current_release="1">
3530 <release seq="1">
3531 <messages>
3532 <part file="sub.grdp" />
3533 </messages>
3534 </release>
3535 </grit>
3536 """
3537 with grit.util.TempDir({'main.grd': WRAPPER,
3538 'sub.grdp': grdp_string}) as temp_dir:
3539 return _GetGrdMessages(temp_dir.GetPath('main.grd'), temp_dir.GetPath())
3540
3541 new_or_added_paths = set(f.LocalPath()
3542 for f in input_api.AffectedFiles()
3543 if (f.Action() == 'A' or f.Action() == 'M'))
3544 removed_paths = set(f.LocalPath()
3545 for f in input_api.AffectedFiles(include_deletes=True)
3546 if f.Action() == 'D')
3547
3548 affected_grds = [f for f in input_api.AffectedFiles()
3549 if (f.LocalPath().endswith('.grd') or
3550 f.LocalPath().endswith('.grdp'))]
3551 affected_png_paths = [f.AbsoluteLocalPath()
3552 for f in input_api.AffectedFiles()
3553 if (f.LocalPath().endswith('.png'))]
3554
3555 # Check for screenshots. Developers can upload screenshots using
3556 # tools/translation/upload_screenshots.py which finds and uploads
3557 # images associated with .grd files (e.g. test_grd/IDS_STRING.png for the
3558 # message named IDS_STRING in test.grd) and produces a .sha1 file (e.g.
3559 # test_grd/IDS_STRING.png.sha1) for each png when the upload is successful.
3560 #
3561 # The logic here is as follows:
3562 #
3563 # - If the CL has a .png file under the screenshots directory for a grd
3564 # file, warn the developer. Actual images should never be checked into the
3565 # Chrome repo.
3566 #
3567 # - If the CL contains modified or new messages in grd files and doesn't
3568 # contain the corresponding .sha1 files, warn the developer to add images
3569 # and upload them via tools/translation/upload_screenshots.py.
3570 #
3571 # - If the CL contains modified or new messages in grd files and the
3572 # corresponding .sha1 files, everything looks good.
3573 #
3574 # - If the CL contains removed messages in grd files but the corresponding
3575 # .sha1 files aren't removed, warn the developer to remove them.
3576 unnecessary_screenshots = []
3577 missing_sha1 = []
3578 unnecessary_sha1_files = []
3579
3580
3581 def _CheckScreenshotAdded(screenshots_dir, message_id):
3582 sha1_path = input_api.os_path.join(
3583 screenshots_dir, message_id + '.png.sha1')
3584 if sha1_path not in new_or_added_paths:
3585 missing_sha1.append(sha1_path)
3586
3587
3588 def _CheckScreenshotRemoved(screenshots_dir, message_id):
3589 sha1_path = input_api.os_path.join(
3590 screenshots_dir, message_id + '.png.sha1')
3591 if sha1_path not in removed_paths:
3592 unnecessary_sha1_files.append(sha1_path)
3593
3594
3595 for f in affected_grds:
3596 file_path = f.LocalPath()
3597 old_id_to_msg_map = {}
3598 new_id_to_msg_map = {}
3599 if file_path.endswith('.grdp'):
3600 if f.OldContents():
3601 old_id_to_msg_map = _GetGrdpMessagesFromString(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393602 unicode('\n'.join(f.OldContents())))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143603 if f.NewContents():
3604 new_id_to_msg_map = _GetGrdpMessagesFromString(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393605 unicode('\n'.join(f.NewContents())))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143606 else:
3607 if f.OldContents():
3608 old_id_to_msg_map = _GetGrdMessages(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393609 StringIO(unicode('\n'.join(f.OldContents()))))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143610 if f.NewContents():
3611 new_id_to_msg_map = _GetGrdMessages(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393612 StringIO(unicode('\n'.join(f.NewContents()))))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143613
3614 # Compute added, removed and modified message IDs.
3615 old_ids = set(old_id_to_msg_map)
3616 new_ids = set(new_id_to_msg_map)
3617 added_ids = new_ids - old_ids
3618 removed_ids = old_ids - new_ids
3619 modified_ids = set([])
3620 for key in old_ids.intersection(new_ids):
3621 if (old_id_to_msg_map[key].FormatXml()
3622 != new_id_to_msg_map[key].FormatXml()):
3623 modified_ids.add(key)
3624
3625 grd_name, ext = input_api.os_path.splitext(
3626 input_api.os_path.basename(file_path))
3627 screenshots_dir = input_api.os_path.join(
3628 input_api.os_path.dirname(file_path), grd_name + ext.replace('.', '_'))
3629
3630 # Check the screenshot directory for .png files. Warn if there is any.
3631 for png_path in affected_png_paths:
3632 if png_path.startswith(screenshots_dir):
3633 unnecessary_screenshots.append(png_path)
3634
3635 for added_id in added_ids:
3636 _CheckScreenshotAdded(screenshots_dir, added_id)
3637
3638 for modified_id in modified_ids:
3639 _CheckScreenshotAdded(screenshots_dir, modified_id)
3640
3641 for removed_id in removed_ids:
3642 _CheckScreenshotRemoved(screenshots_dir, removed_id)
3643
3644 results = []
3645 if unnecessary_screenshots:
3646 results.append(output_api.PresubmitNotifyResult(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393647 'Do not include actual screenshots in the changelist. Run '
3648 'tools/translate/upload_screenshots.py to upload them instead:',
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143649 sorted(unnecessary_screenshots)))
3650
3651 if missing_sha1:
3652 results.append(output_api.PresubmitNotifyResult(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393653 'You are adding or modifying UI strings.\n'
3654 'To ensure the best translations, take screenshots of the relevant UI '
3655 '(https://g.co/chrome/translation) and add these files to your '
3656 'changelist:', sorted(missing_sha1)))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143657
3658 if unnecessary_sha1_files:
3659 results.append(output_api.PresubmitNotifyResult(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393660 'You removed strings associated with these files. Remove:',
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143661 sorted(unnecessary_sha1_files)))
3662
3663 return results