blob: c3db09847f859a78035d69f47f422f1df504158a [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[\\/].*",
18 r"^third_party[\\/](WebKit|blink)[\\/].*",
19 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 (
Egor Paskoce145c42018-09-28 19:31:04346 r'^third_party[\\/]abseil-cpp[\\/].*',
Mirko Bonadeif4f0f0e2018-04-12 09:29:41347 ),
jame2d1a952016-04-02 00:27:10348 ),
fdorayc4ac18d2017-05-01 21:39:59349 (
Gabriel Charette7cc6c432018-04-25 20:52:02350 r'/base::SequenceChecker\b',
gabd52c912a2017-05-11 04:15:59351 (
352 'Consider using SEQUENCE_CHECKER macros instead of the class directly.',
353 ),
354 False,
355 (),
356 ),
357 (
Gabriel Charette7cc6c432018-04-25 20:52:02358 r'/base::ThreadChecker\b',
gabd52c912a2017-05-11 04:15:59359 (
360 'Consider using THREAD_CHECKER macros instead of the class directly.',
361 ),
362 False,
363 (),
364 ),
dbeamb6f4fde2017-06-15 04:03:06365 (
Yuri Wiitala2f8de5c2017-07-21 00:11:06366 r'/(Time(|Delta|Ticks)|ThreadTicks)::FromInternalValue|ToInternalValue',
367 (
368 'base::TimeXXX::FromInternalValue() and ToInternalValue() are',
369 'deprecated (http://crbug.com/634507). Please avoid converting away',
370 'from the Time types in Chromium code, especially if any math is',
371 'being done on time values. For interfacing with platform/library',
372 'APIs, use FromMicroseconds() or InMicroseconds(), or one of the other',
373 'type converter methods instead. For faking TimeXXX values (for unit',
374 'testing only), use TimeXXX() + TimeDelta::FromMicroseconds(N). For',
375 'other use cases, please contact base/time/OWNERS.',
376 ),
377 False,
378 (),
379 ),
380 (
dbeamb6f4fde2017-06-15 04:03:06381 'CallJavascriptFunctionUnsafe',
382 (
383 "Don't use CallJavascriptFunctionUnsafe() in new code. Instead, use",
384 'AllowJavascript(), OnJavascriptAllowed()/OnJavascriptDisallowed(),',
385 'and CallJavascriptFunction(). See https://goo.gl/qivavq.',
386 ),
387 False,
388 (
Egor Paskoce145c42018-09-28 19:31:04389 r'^content[\\/]browser[\\/]webui[\\/]web_ui_impl\.(cc|h)$',
390 r'^content[\\/]public[\\/]browser[\\/]web_ui\.h$',
391 r'^content[\\/]public[\\/]test[\\/]test_web_ui\.(cc|h)$',
dbeamb6f4fde2017-06-15 04:03:06392 ),
393 ),
dskiba1474c2bfd62017-07-20 02:19:24394 (
395 'leveldb::DB::Open',
396 (
397 'Instead of leveldb::DB::Open() use leveldb_env::OpenDB() from',
398 'third_party/leveldatabase/env_chromium.h. It exposes databases to',
399 "Chrome's tracing, making their memory usage visible.",
400 ),
401 True,
402 (
403 r'^third_party/leveldatabase/.*\.(cc|h)$',
404 ),
Gabriel Charette0592c3a2017-07-26 12:02:04405 ),
406 (
Chris Mumfordc38afb62017-10-09 17:55:08407 'leveldb::NewMemEnv',
408 (
409 'Instead of leveldb::NewMemEnv() use leveldb_chrome::NewMemEnv() from',
Chris Mumford8d26d10a2018-04-20 17:07:58410 'third_party/leveldatabase/leveldb_chrome.h. It exposes environments',
411 "to Chrome's tracing, making their memory usage visible.",
Chris Mumfordc38afb62017-10-09 17:55:08412 ),
413 True,
414 (
415 r'^third_party/leveldatabase/.*\.(cc|h)$',
416 ),
417 ),
418 (
Gabriel Charetted9839bc2017-07-29 14:17:47419 'RunLoop::QuitCurrent',
420 (
Robert Liao64b7ab22017-08-04 23:03:43421 'Please migrate away from RunLoop::QuitCurrent*() methods. Use member',
422 'methods of a specific RunLoop instance instead.',
Gabriel Charetted9839bc2017-07-29 14:17:47423 ),
Gabriel Charettec0a8f3ee2018-04-25 20:49:41424 False,
Gabriel Charetted9839bc2017-07-29 14:17:47425 (),
Gabriel Charettea44975052017-08-21 23:14:04426 ),
427 (
428 'base::ScopedMockTimeMessageLoopTaskRunner',
429 (
Gabriel Charette87cc1af2018-04-25 20:52:51430 'ScopedMockTimeMessageLoopTaskRunner is deprecated. Prefer',
431 'ScopedTaskEnvironment::MainThreadType::MOCK_TIME. There are still a',
432 'few cases that may require a ScopedMockTimeMessageLoopTaskRunner',
433 '(i.e. mocking the main MessageLoopForUI in browser_tests), but check',
434 'with gab@ first if you think you need it)',
Gabriel Charettea44975052017-08-21 23:14:04435 ),
Gabriel Charette87cc1af2018-04-25 20:52:51436 False,
Gabriel Charettea44975052017-08-21 23:14:04437 (),
Eric Stevenson6b47b44c2017-08-30 20:41:57438 ),
439 (
440 r'std::regex',
441 (
442 'Using std::regex adds unnecessary binary size to Chrome. Please use',
Mostyn Bramley-Moore6b427322017-12-21 22:11:02443 're2::RE2 instead (crbug.com/755321)',
Eric Stevenson6b47b44c2017-08-30 20:41:57444 ),
445 True,
446 (),
Francois Doray43670e32017-09-27 12:40:38447 ),
448 (
449 (r'/base::ThreadRestrictions::(ScopedAllowIO|AssertIOAllowed|'
450 r'DisallowWaiting|AssertWaitAllowed|SetWaitAllowed|ScopedAllowWait)'),
451 (
452 'Use the new API in base/threading/thread_restrictions.h.',
453 ),
Gabriel Charette04b138f2018-08-06 00:03:22454 False,
Francois Doray43670e32017-09-27 12:40:38455 (),
456 ),
Luis Hector Chavez9bbaed532017-11-30 18:25:38457 (
458 r'/\bbase::Bind\(',
459 (
Gabriel Charette147335ea2018-03-22 15:59:19460 'Please consider using base::Bind{Once,Repeating} instead',
Mostyn Bramley-Moore6b427322017-12-21 22:11:02461 'of base::Bind. (crbug.com/714018)',
Luis Hector Chavez9bbaed532017-11-30 18:25:38462 ),
463 False,
464 (),
465 ),
466 (
467 r'/\bbase::Callback<',
468 (
Gabriel Charette147335ea2018-03-22 15:59:19469 'Please consider using base::{Once,Repeating}Callback instead',
Mostyn Bramley-Moore6b427322017-12-21 22:11:02470 'of base::Callback. (crbug.com/714018)',
Luis Hector Chavez9bbaed532017-11-30 18:25:38471 ),
472 False,
473 (),
474 ),
475 (
476 r'/\bbase::Closure\b',
477 (
Gabriel Charette147335ea2018-03-22 15:59:19478 'Please consider using base::{Once,Repeating}Closure instead',
Mostyn Bramley-Moore6b427322017-12-21 22:11:02479 'of base::Closure. (crbug.com/714018)',
Luis Hector Chavez9bbaed532017-11-30 18:25:38480 ),
481 False,
482 (),
483 ),
Victor Costan3653df62018-02-08 21:38:16484 (
Gabriel Charette147335ea2018-03-22 15:59:19485 r'RunMessageLoop',
486 (
487 'RunMessageLoop is deprecated, use RunLoop instead.',
488 ),
489 False,
490 (),
491 ),
492 (
493 r'RunThisRunLoop',
494 (
495 'RunThisRunLoop is deprecated, use RunLoop directly instead.',
496 ),
497 False,
498 (),
499 ),
500 (
501 r'RunAllPendingInMessageLoop()',
502 (
503 "Prefer RunLoop over RunAllPendingInMessageLoop, please contact gab@",
504 "if you're convinced you need this.",
505 ),
506 False,
507 (),
508 ),
509 (
510 r'RunAllPendingInMessageLoop(BrowserThread',
511 (
512 'RunAllPendingInMessageLoop is deprecated. Use RunLoop for',
513 'BrowserThread::UI, TestBrowserThreadBundle::RunIOThreadUntilIdle',
514 'for BrowserThread::IO, and prefer RunLoop::QuitClosure to observe',
515 'async events instead of flushing threads.',
516 ),
517 False,
518 (),
519 ),
520 (
521 r'MessageLoopRunner',
522 (
523 'MessageLoopRunner is deprecated, use RunLoop instead.',
524 ),
525 False,
526 (),
527 ),
528 (
529 r'GetDeferredQuitTaskForRunLoop',
530 (
531 "GetDeferredQuitTaskForRunLoop shouldn't be needed, please contact",
532 "gab@ if you found a use case where this is the only solution.",
533 ),
534 False,
535 (),
536 ),
537 (
Victor Costan3653df62018-02-08 21:38:16538 'sqlite3_initialize',
539 (
540 'Instead of sqlite3_initialize, depend on //sql, ',
541 '#include "sql/initialize.h" and use sql::EnsureSqliteInitialized().',
542 ),
543 True,
544 (
545 r'^sql/initialization\.(cc|h)$',
546 r'^third_party/sqlite/.*\.(c|cc|h)$',
547 ),
548 ),
Matt Menke7f520a82018-03-28 21:38:37549 (
550 'net::URLFetcher',
551 (
552 'net::URLFetcher should no longer be used in content embedders. ',
553 'Instead, use network::SimpleURLLoader instead, which supports ',
554 'an out-of-process network stack. ',
555 'net::URLFetcher may still be used in binaries that do not embed',
556 'content.',
557 ),
Matt Menke59716d02018-04-05 12:45:53558 False,
Matt Menke7f520a82018-03-28 21:38:37559 (
Egor Paskoce145c42018-09-28 19:31:04560 r'^ios[\\/].*\.(cc|h)$',
561 r'.*[\\/]ios[\\/].*\.(cc|h)$',
Matt Menke7f520a82018-03-28 21:38:37562 r'.*_ios\.(cc|h)$',
Egor Paskoce145c42018-09-28 19:31:04563 r'^net[\\/].*\.(cc|h)$',
564 r'.*[\\/]tools[\\/].*\.(cc|h)$',
Matt Menke7f520a82018-03-28 21:38:37565 ),
566 ),
jdoerried7d10ab2018-04-27 10:46:13567 (
568 r'/\barraysize\b',
569 (
570 "arraysize is deprecated, please use base::size(array) instead ",
571 "(https://crbug.com/837308). ",
572 ),
573 False,
574 (),
575 ),
tzik5de2157f2018-05-08 03:42:47576 (
577 r'std::random_shuffle',
578 (
579 'std::random_shuffle is deprecated in C++14, and removed in C++17. Use',
580 'base::RandomShuffle instead.'
581 ),
582 True,
583 (),
584 ),
Javier Ernesto Flores Robles749e6c22018-10-08 09:36:24585 (
586 'ios/web/public/test/http_server',
587 (
588 'web::HTTPserver is deprecated use net::EmbeddedTestServer instead.',
589 ),
590 False,
591 (),
592 ),
[email protected]127f18ec2012-06-16 05:05:59593)
594
wnwenbdc444e2016-05-25 13:44:15595
mlamouria82272622014-09-16 18:45:04596_IPC_ENUM_TRAITS_DEPRECATED = (
597 'You are using IPC_ENUM_TRAITS() in your code. It has been deprecated.\n'
Vaclav Brozekd5de76a2018-03-17 07:57:50598 'See http://www.chromium.org/Home/chromium-security/education/'
599 'security-tips-for-ipc')
mlamouria82272622014-09-16 18:45:04600
Stephen Martinis97a394142018-06-07 23:06:05601_LONG_PATH_ERROR = (
602 'Some files included in this CL have file names that are too long (> 200'
603 ' characters). If committed, these files will cause issues on Windows. See'
604 ' https://crbug.com/612667 for more details.'
605)
606
Shenghua Zhangbfaa38b82017-11-16 21:58:02607_JAVA_MULTIPLE_DEFINITION_EXCLUDED_PATHS = [
Egor Paskoce145c42018-09-28 19:31:04608 r".*[\\/]BuildHooksAndroidImpl\.java",
609 r".*[\\/]LicenseContentProvider\.java",
610 r".*[\\/]PlatformServiceBridgeImpl.java",
Patrick Noland5475bc0d2018-10-01 20:04:28611 r".*chrome[\\\/]android[\\\/]feed[\\\/]dummy[\\\/].*\.java",
Shenghua Zhangbfaa38b82017-11-16 21:58:02612]
[email protected]127f18ec2012-06-16 05:05:59613
Sean Kau46e29bc2017-08-28 16:31:16614# These paths contain test data and other known invalid JSON files.
615_KNOWN_INVALID_JSON_FILE_PATTERNS = [
Egor Paskoce145c42018-09-28 19:31:04616 r'test[\\/]data[\\/]',
617 r'^components[\\/]policy[\\/]resources[\\/]policy_templates\.json$',
618 r'^third_party[\\/]protobuf[\\/]',
Egor Paskoce145c42018-09-28 19:31:04619 r'^third_party[\\/]blink[\\/]renderer[\\/]devtools[\\/]protocol\.json$',
Kent Tamura77578cc2018-11-25 22:33:43620 r'^third_party[\\/]blink[\\/]web_tests[\\/]external[\\/]wpt[\\/]',
Sean Kau46e29bc2017-08-28 16:31:16621]
622
623
[email protected]b00342e7f2013-03-26 16:21:54624_VALID_OS_MACROS = (
625 # Please keep sorted.
rayb0088ee52017-04-26 22:35:08626 'OS_AIX',
[email protected]b00342e7f2013-03-26 16:21:54627 'OS_ANDROID',
Henrique Nakashimaafff0502018-01-24 17:14:12628 'OS_ASMJS',
[email protected]b00342e7f2013-03-26 16:21:54629 'OS_BSD',
630 'OS_CAT', # For testing.
631 'OS_CHROMEOS',
Eugene Kliuchnikovb99125c2018-11-26 17:33:04632 'OS_CYGWIN', # third_party code.
[email protected]b00342e7f2013-03-26 16:21:54633 'OS_FREEBSD',
scottmg2f97ee122017-05-12 17:50:37634 'OS_FUCHSIA',
[email protected]b00342e7f2013-03-26 16:21:54635 'OS_IOS',
636 'OS_LINUX',
637 'OS_MACOSX',
638 'OS_NACL',
hidehikof7295f22014-10-28 11:57:21639 'OS_NACL_NONSFI',
640 'OS_NACL_SFI',
krytarowski969759f2016-07-31 23:55:12641 'OS_NETBSD',
[email protected]b00342e7f2013-03-26 16:21:54642 'OS_OPENBSD',
643 'OS_POSIX',
[email protected]eda7afa12014-02-06 12:27:37644 'OS_QNX',
[email protected]b00342e7f2013-03-26 16:21:54645 'OS_SOLARIS',
[email protected]b00342e7f2013-03-26 16:21:54646 'OS_WIN',
647)
648
649
agrievef32bcc72016-04-04 14:57:40650_ANDROID_SPECIFIC_PYDEPS_FILES = [
Andrew Luob2e4b342018-09-20 19:32:39651 'android_webview/tools/run_cts.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:36652 'base/android/jni_generator/jni_generator.pydeps',
653 'base/android/jni_generator/jni_registration_generator.pydeps',
654 'build/android/gyp/aar.pydeps',
655 'build/android/gyp/aidl.pydeps',
656 'build/android/gyp/apkbuilder.pydeps',
657 'build/android/gyp/app_bundle_to_apks.pydeps',
658 'build/android/gyp/bytecode_processor.pydeps',
659 'build/android/gyp/compile_resources.pydeps',
660 'build/android/gyp/create_bundle_wrapper_script.pydeps',
661 'build/android/gyp/copy_ex.pydeps',
662 'build/android/gyp/create_app_bundle.pydeps',
663 'build/android/gyp/create_apk_operations_script.pydeps',
664 'build/android/gyp/create_dist_jar.pydeps',
665 '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',
689 'build/android/incremental_install/generate_android_manifest.pydeps',
690 'build/android/incremental_install/write_installer_json.pydeps',
Andrew Grievea7f1ee902018-05-18 16:17:22691 'build/android/resource_sizes.pydeps',
agrievef32bcc72016-04-04 14:57:40692 'build/android/test_runner.pydeps',
hzl9b15df52017-03-23 23:43:04693 'build/android/test_wrapper/logdog_wrapper.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:36694 'build/protoc_java.pydeps',
Sam Maier3599daa2018-11-26 18:02:59695 ('build/secondary/third_party/android_platform/'
696 'development/scripts/stack.pydeps'),
agrieve732db3a2016-04-26 19:18:19697 'net/tools/testserver/testserver.pydeps',
agrievef32bcc72016-04-04 14:57:40698]
699
wnwenbdc444e2016-05-25 13:44:15700
agrievef32bcc72016-04-04 14:57:40701_GENERIC_PYDEPS_FILES = [
John Chencde89192018-01-27 21:18:40702 'chrome/test/chromedriver/test/run_py_tests.pydeps',
Cole Winstanley7045a1b2018-08-27 23:37:29703 'chrome/test/chromedriver/log_replay/client_replay_unittest.pydeps',
Andrew Grievea7f1ee902018-05-18 16:17:22704 'tools/binary_size/supersize.pydeps',
agrievef32bcc72016-04-04 14:57:40705]
706
wnwenbdc444e2016-05-25 13:44:15707
agrievef32bcc72016-04-04 14:57:40708_ALL_PYDEPS_FILES = _ANDROID_SPECIFIC_PYDEPS_FILES + _GENERIC_PYDEPS_FILES
709
710
Eric Boren6fd2b932018-01-25 15:05:08711# Bypass the AUTHORS check for these accounts.
712_KNOWN_ROBOTS = set(
Chan52654f52018-03-21 21:02:29713 '%s-chromium-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com' % s
714 for s in ('afdo', 'angle', 'catapult', 'chromite', 'depot-tools',
Eric Boren36af476a2018-06-08 16:21:08715 'fuchsia-sdk', 'nacl', 'pdfium', 'perfetto', 'skia',
Eric Boren57cc805b2018-08-20 17:28:32716 'spirv', 'src-internal', 'webrtc')
Sergiy Byelozyorov47158a52018-06-13 22:38:59717 ) | set('%[email protected]' % s for s in ('findit-for-me',)
Achuith Bhandarkar35905562018-07-25 19:28:45718 ) | set('%[email protected]' % s for s in ('3su6n15k.default',)
Sergiy Byelozyorov47158a52018-06-13 22:38:59719 ) | set('%[email protected]' % s
Robert Ma7f024172018-11-01 20:59:22720 for s in ('v8-ci-autoroll-builder', 'wpt-autoroller',)
Eric Boren835d71f2018-09-07 21:09:04721 ) | set('%[email protected]' % s
722 for s in ('chromium-autoroll',)
723 ) | set('%[email protected]' % s
Eric Boren2b7e3c3c2018-09-13 18:14:30724 for s in ('chromium-internal-autoroll',))
Eric Boren6fd2b932018-01-25 15:05:08725
726
[email protected]55459852011-08-10 15:17:19727def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
728 """Attempts to prevent use of functions intended only for testing in
729 non-testing code. For now this is just a best-effort implementation
730 that ignores header files and may have some false positives. A
731 better implementation would probably need a proper C++ parser.
732 """
733 # We only scan .cc files and the like, as the declaration of
734 # for-testing functions in header files are hard to distinguish from
735 # calls to such functions without a proper C++ parser.
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:49736 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
[email protected]55459852011-08-10 15:17:19737
jochenc0d4808c2015-07-27 09:25:42738 base_function_pattern = r'[ :]test::[^\s]+|ForTest(s|ing)?|for_test(s|ing)?'
[email protected]55459852011-08-10 15:17:19739 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
[email protected]23501822014-05-14 02:06:09740 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_function_pattern)
[email protected]55459852011-08-10 15:17:19741 exclusion_pattern = input_api.re.compile(
742 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
743 base_function_pattern, base_function_pattern))
744
745 def FilterFile(affected_file):
[email protected]06e6d0ff2012-12-11 01:36:44746 black_list = (_EXCLUDED_PATHS +
747 _TEST_CODE_EXCLUDED_PATHS +
748 input_api.DEFAULT_BLACK_LIST)
[email protected]55459852011-08-10 15:17:19749 return input_api.FilterSourceFile(
750 affected_file,
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:49751 white_list=file_inclusion_pattern,
[email protected]55459852011-08-10 15:17:19752 black_list=black_list)
753
754 problems = []
755 for f in input_api.AffectedSourceFiles(FilterFile):
756 local_path = f.LocalPath()
[email protected]825d27182014-01-02 21:24:24757 for line_number, line in f.ChangedContents():
[email protected]2fdd1f362013-01-16 03:56:03758 if (inclusion_pattern.search(line) and
[email protected]de4f7d22013-05-23 14:27:46759 not comment_pattern.search(line) and
[email protected]2fdd1f362013-01-16 03:56:03760 not exclusion_pattern.search(line)):
[email protected]55459852011-08-10 15:17:19761 problems.append(
[email protected]2fdd1f362013-01-16 03:56:03762 '%s:%d\n %s' % (local_path, line_number, line.strip()))
[email protected]55459852011-08-10 15:17:19763
764 if problems:
[email protected]f7051d52013-04-02 18:31:42765 return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
[email protected]2fdd1f362013-01-16 03:56:03766 else:
767 return []
[email protected]55459852011-08-10 15:17:19768
769
Vaclav Brozek7dbc28c2018-03-27 08:35:23770def _CheckNoProductionCodeUsingTestOnlyFunctionsJava(input_api, output_api):
771 """This is a simplified version of
772 _CheckNoProductionCodeUsingTestOnlyFunctions for Java files.
773 """
774 javadoc_start_re = input_api.re.compile(r'^\s*/\*\*')
775 javadoc_end_re = input_api.re.compile(r'^\s*\*/')
776 name_pattern = r'ForTest(s|ing)?'
777 # Describes an occurrence of "ForTest*" inside a // comment.
778 comment_re = input_api.re.compile(r'//.*%s' % name_pattern)
779 # Catch calls.
780 inclusion_re = input_api.re.compile(r'(%s)\s*\(' % name_pattern)
781 # Ignore definitions. (Comments are ignored separately.)
782 exclusion_re = input_api.re.compile(r'(%s)[^;]+\{' % name_pattern)
783
784 problems = []
785 sources = lambda x: input_api.FilterSourceFile(
786 x,
787 black_list=(('(?i).*test', r'.*\/junit\/')
788 + input_api.DEFAULT_BLACK_LIST),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:49789 white_list=[r'.*\.java$']
Vaclav Brozek7dbc28c2018-03-27 08:35:23790 )
791 for f in input_api.AffectedFiles(include_deletes=False, file_filter=sources):
792 local_path = f.LocalPath()
793 is_inside_javadoc = False
794 for line_number, line in f.ChangedContents():
795 if is_inside_javadoc and javadoc_end_re.search(line):
796 is_inside_javadoc = False
797 if not is_inside_javadoc and javadoc_start_re.search(line):
798 is_inside_javadoc = True
799 if is_inside_javadoc:
800 continue
801 if (inclusion_re.search(line) and
802 not comment_re.search(line) and
803 not exclusion_re.search(line)):
804 problems.append(
805 '%s:%d\n %s' % (local_path, line_number, line.strip()))
806
807 if problems:
808 return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
809 else:
810 return []
811
812
[email protected]10689ca2011-09-02 02:31:54813def _CheckNoIOStreamInHeaders(input_api, output_api):
814 """Checks to make sure no .h files include <iostream>."""
815 files = []
816 pattern = input_api.re.compile(r'^#include\s*<iostream>',
817 input_api.re.MULTILINE)
818 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
819 if not f.LocalPath().endswith('.h'):
820 continue
821 contents = input_api.ReadFile(f)
822 if pattern.search(contents):
823 files.append(f)
824
825 if len(files):
yolandyandaabc6d2016-04-18 18:29:39826 return [output_api.PresubmitError(
[email protected]6c063c62012-07-11 19:11:06827 'Do not #include <iostream> in header files, since it inserts static '
828 'initialization into every file including the header. Instead, '
[email protected]10689ca2011-09-02 02:31:54829 '#include <ostream>. See http://crbug.com/94794',
830 files) ]
831 return []
832
Danil Chapovalov3518f362018-08-11 16:13:43833def _CheckNoStrCatRedefines(input_api, output_api):
834 """Checks no windows headers with StrCat redefined are included directly."""
835 files = []
836 pattern_deny = input_api.re.compile(
837 r'^#include\s*[<"](shlwapi|atlbase|propvarutil|sphelper).h[">]',
838 input_api.re.MULTILINE)
839 pattern_allow = input_api.re.compile(
840 r'^#include\s"base/win/windows_defines.inc"',
841 input_api.re.MULTILINE)
842 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
843 contents = input_api.ReadFile(f)
844 if pattern_deny.search(contents) and not pattern_allow.search(contents):
845 files.append(f.LocalPath())
846
847 if len(files):
848 return [output_api.PresubmitError(
849 'Do not #include shlwapi.h, atlbase.h, propvarutil.h or sphelper.h '
850 'directly since they pollute code with StrCat macro. Instead, '
851 'include matching header from base/win. See http://crbug.com/856536',
852 files) ]
853 return []
854
[email protected]10689ca2011-09-02 02:31:54855
[email protected]72df4e782012-06-21 16:28:18856def _CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
danakj61c1aa22015-10-26 19:55:52857 """Checks to make sure no source files use UNIT_TEST."""
[email protected]72df4e782012-06-21 16:28:18858 problems = []
859 for f in input_api.AffectedFiles():
860 if (not f.LocalPath().endswith(('.cc', '.mm'))):
861 continue
862
863 for line_num, line in f.ChangedContents():
[email protected]549f86a2013-11-19 13:00:04864 if 'UNIT_TEST ' in line or line.endswith('UNIT_TEST'):
[email protected]72df4e782012-06-21 16:28:18865 problems.append(' %s:%d' % (f.LocalPath(), line_num))
866
867 if not problems:
868 return []
869 return [output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
870 '\n'.join(problems))]
871
Dominic Battre033531052018-09-24 15:45:34872def _CheckNoDISABLETypoInTests(input_api, output_api):
873 """Checks to prevent attempts to disable tests with DISABLE_ prefix.
874
875 This test warns if somebody tries to disable a test with the DISABLE_ prefix
876 instead of DISABLED_. To filter false positives, reports are only generated
877 if a corresponding MAYBE_ line exists.
878 """
879 problems = []
880
881 # The following two patterns are looked for in tandem - is a test labeled
882 # as MAYBE_ followed by a DISABLE_ (instead of the correct DISABLED)
883 maybe_pattern = input_api.re.compile(r'MAYBE_([a-zA-Z0-9_]+)')
884 disable_pattern = input_api.re.compile(r'DISABLE_([a-zA-Z0-9_]+)')
885
886 # This is for the case that a test is disabled on all platforms.
887 full_disable_pattern = input_api.re.compile(
888 r'^\s*TEST[^(]*\([a-zA-Z0-9_]+,\s*DISABLE_[a-zA-Z0-9_]+\)',
889 input_api.re.MULTILINE)
890
Katie Df13948e2018-09-25 07:33:44891 for f in input_api.AffectedFiles(False):
Dominic Battre033531052018-09-24 15:45:34892 if not 'test' in f.LocalPath() or not f.LocalPath().endswith('.cc'):
893 continue
894
895 # Search for MABYE_, DISABLE_ pairs.
896 disable_lines = {} # Maps of test name to line number.
897 maybe_lines = {}
898 for line_num, line in f.ChangedContents():
899 disable_match = disable_pattern.search(line)
900 if disable_match:
901 disable_lines[disable_match.group(1)] = line_num
902 maybe_match = maybe_pattern.search(line)
903 if maybe_match:
904 maybe_lines[maybe_match.group(1)] = line_num
905
906 # Search for DISABLE_ occurrences within a TEST() macro.
907 disable_tests = set(disable_lines.keys())
908 maybe_tests = set(maybe_lines.keys())
909 for test in disable_tests.intersection(maybe_tests):
910 problems.append(' %s:%d' % (f.LocalPath(), disable_lines[test]))
911
912 contents = input_api.ReadFile(f)
913 full_disable_match = full_disable_pattern.search(contents)
914 if full_disable_match:
915 problems.append(' %s' % f.LocalPath())
916
917 if not problems:
918 return []
919 return [
920 output_api.PresubmitPromptWarning(
921 'Attempt to disable a test with DISABLE_ instead of DISABLED_?\n' +
922 '\n'.join(problems))
923 ]
924
[email protected]72df4e782012-06-21 16:28:18925
danakj61c1aa22015-10-26 19:55:52926def _CheckDCHECK_IS_ONHasBraces(input_api, output_api):
kjellanderaee306632017-02-22 19:26:57927 """Checks to make sure DCHECK_IS_ON() does not skip the parentheses."""
danakj61c1aa22015-10-26 19:55:52928 errors = []
929 pattern = input_api.re.compile(r'DCHECK_IS_ON(?!\(\))',
930 input_api.re.MULTILINE)
931 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
932 if (not f.LocalPath().endswith(('.cc', '.mm', '.h'))):
933 continue
934 for lnum, line in f.ChangedContents():
935 if input_api.re.search(pattern, line):
dchenge07de812016-06-20 19:27:17936 errors.append(output_api.PresubmitError(
937 ('%s:%d: Use of DCHECK_IS_ON() must be written as "#if ' +
kjellanderaee306632017-02-22 19:26:57938 'DCHECK_IS_ON()", not forgetting the parentheses.')
dchenge07de812016-06-20 19:27:17939 % (f.LocalPath(), lnum)))
danakj61c1aa22015-10-26 19:55:52940 return errors
941
942
mcasasb7440c282015-02-04 14:52:19943def _FindHistogramNameInLine(histogram_name, line):
944 """Tries to find a histogram name or prefix in a line."""
945 if not "affected-histogram" in line:
946 return histogram_name in line
947 # A histogram_suffixes tag type has an affected-histogram name as a prefix of
948 # the histogram_name.
949 if not '"' in line:
950 return False
951 histogram_prefix = line.split('\"')[1]
952 return histogram_prefix in histogram_name
953
954
955def _CheckUmaHistogramChanges(input_api, output_api):
956 """Check that UMA histogram names in touched lines can still be found in other
957 lines of the patch or in histograms.xml. Note that this check would not catch
958 the reverse: changes in histograms.xml not matched in the code itself."""
959 touched_histograms = []
960 histograms_xml_modifications = []
Vaclav Brozekbdac817c2018-03-24 06:30:47961 call_pattern_c = r'\bUMA_HISTOGRAM.*\('
962 call_pattern_java = r'\bRecordHistogram\.record[a-zA-Z]+Histogram\('
963 name_pattern = r'"(.*?)"'
964 single_line_c_re = input_api.re.compile(call_pattern_c + name_pattern)
965 single_line_java_re = input_api.re.compile(call_pattern_java + name_pattern)
966 split_line_c_prefix_re = input_api.re.compile(call_pattern_c)
967 split_line_java_prefix_re = input_api.re.compile(call_pattern_java)
968 split_line_suffix_re = input_api.re.compile(r'^\s*' + name_pattern)
Vaclav Brozek0e730cbd2018-03-24 06:18:17969 last_line_matched_prefix = False
mcasasb7440c282015-02-04 14:52:19970 for f in input_api.AffectedFiles():
971 # If histograms.xml itself is modified, keep the modified lines for later.
972 if f.LocalPath().endswith(('histograms.xml')):
973 histograms_xml_modifications = f.ChangedContents()
974 continue
Vaclav Brozekbdac817c2018-03-24 06:30:47975 if f.LocalPath().endswith(('cc', 'mm', 'cpp')):
976 single_line_re = single_line_c_re
977 split_line_prefix_re = split_line_c_prefix_re
978 elif f.LocalPath().endswith(('java')):
979 single_line_re = single_line_java_re
980 split_line_prefix_re = split_line_java_prefix_re
981 else:
mcasasb7440c282015-02-04 14:52:19982 continue
983 for line_num, line in f.ChangedContents():
Vaclav Brozek0e730cbd2018-03-24 06:18:17984 if last_line_matched_prefix:
985 suffix_found = split_line_suffix_re.search(line)
986 if suffix_found :
987 touched_histograms.append([suffix_found.group(1), f, line_num])
988 last_line_matched_prefix = False
989 continue
Vaclav Brozek8a8e2e202018-03-23 22:01:06990 found = single_line_re.search(line)
mcasasb7440c282015-02-04 14:52:19991 if found:
992 touched_histograms.append([found.group(1), f, line_num])
Vaclav Brozek0e730cbd2018-03-24 06:18:17993 continue
994 last_line_matched_prefix = split_line_prefix_re.search(line)
mcasasb7440c282015-02-04 14:52:19995
996 # Search for the touched histogram names in the local modifications to
997 # histograms.xml, and, if not found, on the base histograms.xml file.
998 unmatched_histograms = []
999 for histogram_info in touched_histograms:
1000 histogram_name_found = False
1001 for line_num, line in histograms_xml_modifications:
1002 histogram_name_found = _FindHistogramNameInLine(histogram_info[0], line)
1003 if histogram_name_found:
1004 break
1005 if not histogram_name_found:
1006 unmatched_histograms.append(histogram_info)
1007
eromanb90c82e7e32015-04-01 15:13:491008 histograms_xml_path = 'tools/metrics/histograms/histograms.xml'
mcasasb7440c282015-02-04 14:52:191009 problems = []
1010 if unmatched_histograms:
eromanb90c82e7e32015-04-01 15:13:491011 with open(histograms_xml_path) as histograms_xml:
mcasasb7440c282015-02-04 14:52:191012 for histogram_name, f, line_num in unmatched_histograms:
mcasas39c1b8b2015-02-25 15:33:451013 histograms_xml.seek(0)
mcasasb7440c282015-02-04 14:52:191014 histogram_name_found = False
1015 for line in histograms_xml:
1016 histogram_name_found = _FindHistogramNameInLine(histogram_name, line)
1017 if histogram_name_found:
1018 break
1019 if not histogram_name_found:
1020 problems.append(' [%s:%d] %s' %
1021 (f.LocalPath(), line_num, histogram_name))
1022
1023 if not problems:
1024 return []
1025 return [output_api.PresubmitPromptWarning('Some UMA_HISTOGRAM lines have '
1026 'been modified and the associated histogram name has no match in either '
eromanb90c82e7e32015-04-01 15:13:491027 '%s or the modifications of it:' % (histograms_xml_path), problems)]
mcasasb7440c282015-02-04 14:52:191028
wnwenbdc444e2016-05-25 13:44:151029
yolandyandaabc6d2016-04-18 18:29:391030def _CheckFlakyTestUsage(input_api, output_api):
1031 """Check that FlakyTest annotation is our own instead of the android one"""
1032 pattern = input_api.re.compile(r'import android.test.FlakyTest;')
1033 files = []
1034 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1035 if f.LocalPath().endswith('Test.java'):
1036 if pattern.search(input_api.ReadFile(f)):
1037 files.append(f)
1038 if len(files):
1039 return [output_api.PresubmitError(
1040 'Use org.chromium.base.test.util.FlakyTest instead of '
1041 'android.test.FlakyTest',
1042 files)]
1043 return []
mcasasb7440c282015-02-04 14:52:191044
wnwenbdc444e2016-05-25 13:44:151045
[email protected]8ea5d4b2011-09-13 21:49:221046def _CheckNoNewWStrings(input_api, output_api):
1047 """Checks to make sure we don't introduce use of wstrings."""
[email protected]55463aa62011-10-12 00:48:271048 problems = []
[email protected]8ea5d4b2011-09-13 21:49:221049 for f in input_api.AffectedFiles():
[email protected]b5c24292011-11-28 14:38:201050 if (not f.LocalPath().endswith(('.cc', '.h')) or
scottmge6f04402014-11-05 01:59:571051 f.LocalPath().endswith(('test.cc', '_win.cc', '_win.h')) or
pennymac84fd6692016-07-13 22:35:341052 '/win/' in f.LocalPath() or
1053 'chrome_elf' in f.LocalPath() or
1054 'install_static' in f.LocalPath()):
[email protected]b5c24292011-11-28 14:38:201055 continue
[email protected]8ea5d4b2011-09-13 21:49:221056
[email protected]a11dbe9b2012-08-07 01:32:581057 allowWString = False
[email protected]b5c24292011-11-28 14:38:201058 for line_num, line in f.ChangedContents():
[email protected]a11dbe9b2012-08-07 01:32:581059 if 'presubmit: allow wstring' in line:
1060 allowWString = True
1061 elif not allowWString and 'wstring' in line:
[email protected]55463aa62011-10-12 00:48:271062 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]a11dbe9b2012-08-07 01:32:581063 allowWString = False
1064 else:
1065 allowWString = False
[email protected]8ea5d4b2011-09-13 21:49:221066
[email protected]55463aa62011-10-12 00:48:271067 if not problems:
1068 return []
1069 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
[email protected]a11dbe9b2012-08-07 01:32:581070 ' If you are calling a cross-platform API that accepts a wstring, '
1071 'fix the API.\n' +
[email protected]55463aa62011-10-12 00:48:271072 '\n'.join(problems))]
[email protected]8ea5d4b2011-09-13 21:49:221073
1074
[email protected]2a8ac9c2011-10-19 17:20:441075def _CheckNoDEPSGIT(input_api, output_api):
1076 """Make sure .DEPS.git is never modified manually."""
1077 if any(f.LocalPath().endswith('.DEPS.git') for f in
1078 input_api.AffectedFiles()):
1079 return [output_api.PresubmitError(
1080 'Never commit changes to .DEPS.git. This file is maintained by an\n'
1081 'automated system based on what\'s in DEPS and your changes will be\n'
1082 'overwritten.\n'
Vaclav Brozekd5de76a2018-03-17 07:57:501083 'See https://sites.google.com/a/chromium.org/dev/developers/how-tos/'
1084 'get-the-code#Rolling_DEPS\n'
[email protected]2a8ac9c2011-10-19 17:20:441085 'for more information')]
1086 return []
1087
1088
tandriief664692014-09-23 14:51:471089def _CheckValidHostsInDEPS(input_api, output_api):
1090 """Checks that DEPS file deps are from allowed_hosts."""
1091 # Run only if DEPS file has been modified to annoy fewer bystanders.
1092 if all(f.LocalPath() != 'DEPS' for f in input_api.AffectedFiles()):
1093 return []
1094 # Outsource work to gclient verify
1095 try:
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:201096 input_api.subprocess.check_output(['gclient', 'verify'],
1097 stderr=input_api.subprocess.STDOUT)
tandriief664692014-09-23 14:51:471098 return []
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:201099 except input_api.subprocess.CalledProcessError as error:
tandriief664692014-09-23 14:51:471100 return [output_api.PresubmitError(
1101 'DEPS file must have only git dependencies.',
1102 long_text=error.output)]
1103
1104
[email protected]127f18ec2012-06-16 05:05:591105def _CheckNoBannedFunctions(input_api, output_api):
1106 """Make sure that banned functions are not used."""
1107 warnings = []
1108 errors = []
1109
wnwenbdc444e2016-05-25 13:44:151110 def IsBlacklisted(affected_file, blacklist):
1111 local_path = affected_file.LocalPath()
1112 for item in blacklist:
1113 if input_api.re.match(item, local_path):
1114 return True
1115 return False
1116
Sylvain Defresnea8b73d252018-02-28 15:45:541117 def IsIosObcjFile(affected_file):
1118 local_path = affected_file.LocalPath()
1119 if input_api.os_path.splitext(local_path)[-1] not in ('.mm', '.m', '.h'):
1120 return False
1121 basename = input_api.os_path.basename(local_path)
1122 if 'ios' in basename.split('_'):
1123 return True
1124 for sep in (input_api.os_path.sep, input_api.os_path.altsep):
1125 if sep and 'ios' in local_path.split(sep):
1126 return True
1127 return False
1128
wnwenbdc444e2016-05-25 13:44:151129 def CheckForMatch(affected_file, line_num, line, func_name, message, error):
1130 matched = False
1131 if func_name[0:1] == '/':
1132 regex = func_name[1:]
1133 if input_api.re.search(regex, line):
1134 matched = True
1135 elif func_name in line:
dchenge07de812016-06-20 19:27:171136 matched = True
wnwenbdc444e2016-05-25 13:44:151137 if matched:
dchenge07de812016-06-20 19:27:171138 problems = warnings
wnwenbdc444e2016-05-25 13:44:151139 if error:
dchenge07de812016-06-20 19:27:171140 problems = errors
wnwenbdc444e2016-05-25 13:44:151141 problems.append(' %s:%d:' % (affected_file.LocalPath(), line_num))
1142 for message_line in message:
1143 problems.append(' %s' % message_line)
1144
Eric Stevensona9a980972017-09-23 00:04:411145 file_filter = lambda f: f.LocalPath().endswith(('.java'))
1146 for f in input_api.AffectedFiles(file_filter=file_filter):
1147 for line_num, line in f.ChangedContents():
1148 for func_name, message, error in _BANNED_JAVA_FUNCTIONS:
1149 CheckForMatch(f, line_num, line, func_name, message, error)
1150
[email protected]127f18ec2012-06-16 05:05:591151 file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
1152 for f in input_api.AffectedFiles(file_filter=file_filter):
1153 for line_num, line in f.ChangedContents():
1154 for func_name, message, error in _BANNED_OBJC_FUNCTIONS:
wnwenbdc444e2016-05-25 13:44:151155 CheckForMatch(f, line_num, line, func_name, message, error)
[email protected]127f18ec2012-06-16 05:05:591156
Sylvain Defresnea8b73d252018-02-28 15:45:541157 for f in input_api.AffectedFiles(file_filter=IsIosObcjFile):
1158 for line_num, line in f.ChangedContents():
1159 for func_name, message, error in _BANNED_IOS_OBJC_FUNCTIONS:
1160 CheckForMatch(f, line_num, line, func_name, message, error)
1161
[email protected]127f18ec2012-06-16 05:05:591162 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
1163 for f in input_api.AffectedFiles(file_filter=file_filter):
1164 for line_num, line in f.ChangedContents():
[email protected]7345da02012-11-27 14:31:491165 for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS:
[email protected]7345da02012-11-27 14:31:491166 if IsBlacklisted(f, excluded_paths):
1167 continue
wnwenbdc444e2016-05-25 13:44:151168 CheckForMatch(f, line_num, line, func_name, message, error)
[email protected]127f18ec2012-06-16 05:05:591169
1170 result = []
1171 if (warnings):
1172 result.append(output_api.PresubmitPromptWarning(
1173 'Banned functions were used.\n' + '\n'.join(warnings)))
1174 if (errors):
1175 result.append(output_api.PresubmitError(
1176 'Banned functions were used.\n' + '\n'.join(errors)))
1177 return result
1178
1179
[email protected]6c063c62012-07-11 19:11:061180def _CheckNoPragmaOnce(input_api, output_api):
1181 """Make sure that banned functions are not used."""
1182 files = []
1183 pattern = input_api.re.compile(r'^#pragma\s+once',
1184 input_api.re.MULTILINE)
1185 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1186 if not f.LocalPath().endswith('.h'):
1187 continue
1188 contents = input_api.ReadFile(f)
1189 if pattern.search(contents):
1190 files.append(f)
1191
1192 if files:
1193 return [output_api.PresubmitError(
1194 'Do not use #pragma once in header files.\n'
1195 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
1196 files)]
1197 return []
1198
[email protected]127f18ec2012-06-16 05:05:591199
[email protected]e7479052012-09-19 00:26:121200def _CheckNoTrinaryTrueFalse(input_api, output_api):
1201 """Checks to make sure we don't introduce use of foo ? true : false."""
1202 problems = []
1203 pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)')
1204 for f in input_api.AffectedFiles():
1205 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
1206 continue
1207
1208 for line_num, line in f.ChangedContents():
1209 if pattern.match(line):
1210 problems.append(' %s:%d' % (f.LocalPath(), line_num))
1211
1212 if not problems:
1213 return []
1214 return [output_api.PresubmitPromptWarning(
1215 'Please consider avoiding the "? true : false" pattern if possible.\n' +
1216 '\n'.join(problems))]
1217
1218
[email protected]55f9f382012-07-31 11:02:181219def _CheckUnwantedDependencies(input_api, output_api):
rhalavati08acd232017-04-03 07:23:281220 """Runs checkdeps on #include and import statements added in this
[email protected]55f9f382012-07-31 11:02:181221 change. Breaking - rules is an error, breaking ! rules is a
1222 warning.
1223 """
mohan.reddyf21db962014-10-16 12:26:471224 import sys
[email protected]55f9f382012-07-31 11:02:181225 # We need to wait until we have an input_api object and use this
1226 # roundabout construct to import checkdeps because this file is
1227 # eval-ed and thus doesn't have __file__.
1228 original_sys_path = sys.path
1229 try:
1230 sys.path = sys.path + [input_api.os_path.join(
[email protected]5298cc982014-05-29 20:53:471231 input_api.PresubmitLocalPath(), 'buildtools', 'checkdeps')]
[email protected]55f9f382012-07-31 11:02:181232 import checkdeps
1233 from cpp_checker import CppChecker
Jinsuk Kim5a092672017-10-24 22:42:241234 from java_checker import JavaChecker
rhalavati08acd232017-04-03 07:23:281235 from proto_checker import ProtoChecker
[email protected]55f9f382012-07-31 11:02:181236 from rules import Rule
1237 finally:
1238 # Restore sys.path to what it was before.
1239 sys.path = original_sys_path
1240
1241 added_includes = []
rhalavati08acd232017-04-03 07:23:281242 added_imports = []
Jinsuk Kim5a092672017-10-24 22:42:241243 added_java_imports = []
[email protected]55f9f382012-07-31 11:02:181244 for f in input_api.AffectedFiles():
rhalavati08acd232017-04-03 07:23:281245 if CppChecker.IsCppFile(f.LocalPath()):
Vaclav Brozekd5de76a2018-03-17 07:57:501246 changed_lines = [line for _, line in f.ChangedContents()]
Andrew Grieve085f29f2017-11-02 09:14:081247 added_includes.append([f.AbsoluteLocalPath(), changed_lines])
rhalavati08acd232017-04-03 07:23:281248 elif ProtoChecker.IsProtoFile(f.LocalPath()):
Vaclav Brozekd5de76a2018-03-17 07:57:501249 changed_lines = [line for _, line in f.ChangedContents()]
Andrew Grieve085f29f2017-11-02 09:14:081250 added_imports.append([f.AbsoluteLocalPath(), changed_lines])
Jinsuk Kim5a092672017-10-24 22:42:241251 elif JavaChecker.IsJavaFile(f.LocalPath()):
Vaclav Brozekd5de76a2018-03-17 07:57:501252 changed_lines = [line for _, line in f.ChangedContents()]
Andrew Grieve085f29f2017-11-02 09:14:081253 added_java_imports.append([f.AbsoluteLocalPath(), changed_lines])
[email protected]55f9f382012-07-31 11:02:181254
[email protected]26385172013-05-09 23:11:351255 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
[email protected]55f9f382012-07-31 11:02:181256
1257 error_descriptions = []
1258 warning_descriptions = []
rhalavati08acd232017-04-03 07:23:281259 error_subjects = set()
1260 warning_subjects = set()
[email protected]55f9f382012-07-31 11:02:181261 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
1262 added_includes):
Andrew Grieve085f29f2017-11-02 09:14:081263 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
[email protected]55f9f382012-07-31 11:02:181264 description_with_path = '%s\n %s' % (path, rule_description)
1265 if rule_type == Rule.DISALLOW:
1266 error_descriptions.append(description_with_path)
rhalavati08acd232017-04-03 07:23:281267 error_subjects.add("#includes")
[email protected]55f9f382012-07-31 11:02:181268 else:
1269 warning_descriptions.append(description_with_path)
rhalavati08acd232017-04-03 07:23:281270 warning_subjects.add("#includes")
1271
1272 for path, rule_type, rule_description in deps_checker.CheckAddedProtoImports(
1273 added_imports):
Andrew Grieve085f29f2017-11-02 09:14:081274 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
rhalavati08acd232017-04-03 07:23:281275 description_with_path = '%s\n %s' % (path, rule_description)
1276 if rule_type == Rule.DISALLOW:
1277 error_descriptions.append(description_with_path)
1278 error_subjects.add("imports")
1279 else:
1280 warning_descriptions.append(description_with_path)
1281 warning_subjects.add("imports")
[email protected]55f9f382012-07-31 11:02:181282
Jinsuk Kim5a092672017-10-24 22:42:241283 for path, rule_type, rule_description in deps_checker.CheckAddedJavaImports(
Shenghua Zhangbfaa38b82017-11-16 21:58:021284 added_java_imports, _JAVA_MULTIPLE_DEFINITION_EXCLUDED_PATHS):
Andrew Grieve085f29f2017-11-02 09:14:081285 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
Jinsuk Kim5a092672017-10-24 22:42:241286 description_with_path = '%s\n %s' % (path, rule_description)
1287 if rule_type == Rule.DISALLOW:
1288 error_descriptions.append(description_with_path)
1289 error_subjects.add("imports")
1290 else:
1291 warning_descriptions.append(description_with_path)
1292 warning_subjects.add("imports")
1293
[email protected]55f9f382012-07-31 11:02:181294 results = []
1295 if error_descriptions:
1296 results.append(output_api.PresubmitError(
rhalavati08acd232017-04-03 07:23:281297 'You added one or more %s that violate checkdeps rules.'
1298 % " and ".join(error_subjects),
[email protected]55f9f382012-07-31 11:02:181299 error_descriptions))
1300 if warning_descriptions:
[email protected]f7051d52013-04-02 18:31:421301 results.append(output_api.PresubmitPromptOrNotify(
rhalavati08acd232017-04-03 07:23:281302 'You added one or more %s of files that are temporarily\n'
[email protected]55f9f382012-07-31 11:02:181303 'allowed but being removed. Can you avoid introducing the\n'
rhalavati08acd232017-04-03 07:23:281304 '%s? See relevant DEPS file(s) for details and contacts.' %
1305 (" and ".join(warning_subjects), "/".join(warning_subjects)),
[email protected]55f9f382012-07-31 11:02:181306 warning_descriptions))
1307 return results
1308
1309
[email protected]fbcafe5a2012-08-08 15:31:221310def _CheckFilePermissions(input_api, output_api):
1311 """Check that all files have their permissions properly set."""
[email protected]791507202014-02-03 23:19:151312 if input_api.platform == 'win32':
1313 return []
raphael.kubo.da.costac1d13e60b2016-04-01 11:49:291314 checkperms_tool = input_api.os_path.join(
1315 input_api.PresubmitLocalPath(),
1316 'tools', 'checkperms', 'checkperms.py')
1317 args = [input_api.python_executable, checkperms_tool,
mohan.reddyf21db962014-10-16 12:26:471318 '--root', input_api.change.RepositoryRoot()]
Raphael Kubo da Costa6ff391d2017-11-13 16:43:391319 with input_api.CreateTemporaryFile() as file_list:
1320 for f in input_api.AffectedFiles():
1321 # checkperms.py file/directory arguments must be relative to the
1322 # repository.
1323 file_list.write(f.LocalPath() + '\n')
1324 file_list.close()
1325 args += ['--file-list', file_list.name]
1326 try:
1327 input_api.subprocess.check_output(args)
1328 return []
1329 except input_api.subprocess.CalledProcessError as error:
1330 return [output_api.PresubmitError(
1331 'checkperms.py failed:',
1332 long_text=error.output)]
[email protected]fbcafe5a2012-08-08 15:31:221333
1334
robertocn832f5992017-01-04 19:01:301335def _CheckTeamTags(input_api, output_api):
1336 """Checks that OWNERS files have consistent TEAM and COMPONENT tags."""
1337 checkteamtags_tool = input_api.os_path.join(
1338 input_api.PresubmitLocalPath(),
1339 'tools', 'checkteamtags', 'checkteamtags.py')
1340 args = [input_api.python_executable, checkteamtags_tool,
1341 '--root', input_api.change.RepositoryRoot()]
robertocn5eb82312017-01-09 20:27:221342 files = [f.LocalPath() for f in input_api.AffectedFiles(include_deletes=False)
robertocn832f5992017-01-04 19:01:301343 if input_api.os_path.basename(f.AbsoluteLocalPath()).upper() ==
1344 'OWNERS']
1345 try:
1346 if files:
1347 input_api.subprocess.check_output(args + files)
1348 return []
1349 except input_api.subprocess.CalledProcessError as error:
1350 return [output_api.PresubmitError(
1351 'checkteamtags.py failed:',
1352 long_text=error.output)]
1353
1354
[email protected]c8278b32012-10-30 20:35:491355def _CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
1356 """Makes sure we don't include ui/aura/window_property.h
1357 in header files.
1358 """
1359 pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
1360 errors = []
1361 for f in input_api.AffectedFiles():
1362 if not f.LocalPath().endswith('.h'):
1363 continue
1364 for line_num, line in f.ChangedContents():
1365 if pattern.match(line):
1366 errors.append(' %s:%d' % (f.LocalPath(), line_num))
1367
1368 results = []
1369 if errors:
1370 results.append(output_api.PresubmitError(
1371 'Header files should not include ui/aura/window_property.h', errors))
1372 return results
1373
1374
[email protected]70ca77752012-11-20 03:45:031375def _CheckForVersionControlConflictsInFile(input_api, f):
1376 pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
1377 errors = []
1378 for line_num, line in f.ChangedContents():
dbeam95c35a2f2015-06-02 01:40:231379 if f.LocalPath().endswith('.md'):
1380 # First-level headers in markdown look a lot like version control
1381 # conflict markers. http://daringfireball.net/projects/markdown/basics
1382 continue
[email protected]70ca77752012-11-20 03:45:031383 if pattern.match(line):
1384 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
1385 return errors
1386
1387
1388def _CheckForVersionControlConflicts(input_api, output_api):
1389 """Usually this is not intentional and will cause a compile failure."""
1390 errors = []
1391 for f in input_api.AffectedFiles():
1392 errors.extend(_CheckForVersionControlConflictsInFile(input_api, f))
1393
1394 results = []
1395 if errors:
1396 results.append(output_api.PresubmitError(
1397 'Version control conflict markers found, please resolve.', errors))
1398 return results
1399
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:201400
estadee17314a02017-01-12 16:22:161401def _CheckGoogleSupportAnswerUrl(input_api, output_api):
1402 pattern = input_api.re.compile('support\.google\.com\/chrome.*/answer')
1403 errors = []
1404 for f in input_api.AffectedFiles():
1405 for line_num, line in f.ChangedContents():
1406 if pattern.search(line):
1407 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
1408
1409 results = []
1410 if errors:
1411 results.append(output_api.PresubmitPromptWarning(
Vaclav Brozekd5de76a2018-03-17 07:57:501412 'Found Google support URL addressed by answer number. Please replace '
1413 'with a p= identifier instead. See crbug.com/679462\n', errors))
estadee17314a02017-01-12 16:22:161414 return results
1415
[email protected]70ca77752012-11-20 03:45:031416
[email protected]06e6d0ff2012-12-11 01:36:441417def _CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api):
1418 def FilterFile(affected_file):
1419 """Filter function for use with input_api.AffectedSourceFiles,
1420 below. This filters out everything except non-test files from
1421 top-level directories that generally speaking should not hard-code
1422 service URLs (e.g. src/android_webview/, src/content/ and others).
1423 """
1424 return input_api.FilterSourceFile(
1425 affected_file,
Egor Paskoce145c42018-09-28 19:31:041426 white_list=[r'^(android_webview|base|content|net)[\\/].*'],
[email protected]06e6d0ff2012-12-11 01:36:441427 black_list=(_EXCLUDED_PATHS +
1428 _TEST_CODE_EXCLUDED_PATHS +
1429 input_api.DEFAULT_BLACK_LIST))
1430
reillyi38965732015-11-16 18:27:331431 base_pattern = ('"[^"]*(google|googleapis|googlezip|googledrive|appspot)'
1432 '\.(com|net)[^"]*"')
[email protected]de4f7d22013-05-23 14:27:461433 comment_pattern = input_api.re.compile('//.*%s' % base_pattern)
1434 pattern = input_api.re.compile(base_pattern)
[email protected]06e6d0ff2012-12-11 01:36:441435 problems = [] # items are (filename, line_number, line)
1436 for f in input_api.AffectedSourceFiles(FilterFile):
1437 for line_num, line in f.ChangedContents():
[email protected]de4f7d22013-05-23 14:27:461438 if not comment_pattern.search(line) and pattern.search(line):
[email protected]06e6d0ff2012-12-11 01:36:441439 problems.append((f.LocalPath(), line_num, line))
1440
1441 if problems:
[email protected]f7051d52013-04-02 18:31:421442 return [output_api.PresubmitPromptOrNotify(
[email protected]06e6d0ff2012-12-11 01:36:441443 'Most layers below src/chrome/ should not hardcode service URLs.\n'
[email protected]b0149772014-03-27 16:47:581444 'Are you sure this is correct?',
[email protected]06e6d0ff2012-12-11 01:36:441445 [' %s:%d: %s' % (
1446 problem[0], problem[1], problem[2]) for problem in problems])]
[email protected]2fdd1f362013-01-16 03:56:031447 else:
1448 return []
[email protected]06e6d0ff2012-12-11 01:36:441449
1450
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491451# TODO: add unit tests.
[email protected]d2530012013-01-25 16:39:271452def _CheckNoAbbreviationInPngFileName(input_api, output_api):
1453 """Makes sure there are no abbreviations in the name of PNG files.
binji0dcdf342014-12-12 18:32:311454 The native_client_sdk directory is excluded because it has auto-generated PNG
1455 files for documentation.
[email protected]d2530012013-01-25 16:39:271456 """
[email protected]d2530012013-01-25 16:39:271457 errors = []
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491458 white_list = [r'.*_[a-z]_.*\.png$|.*_[a-z]\.png$']
Egor Paskoce145c42018-09-28 19:31:041459 black_list = [r'^native_client_sdk[\\/]']
binji0dcdf342014-12-12 18:32:311460 file_filter = lambda f: input_api.FilterSourceFile(
1461 f, white_list=white_list, black_list=black_list)
1462 for f in input_api.AffectedFiles(include_deletes=False,
1463 file_filter=file_filter):
1464 errors.append(' %s' % f.LocalPath())
[email protected]d2530012013-01-25 16:39:271465
1466 results = []
1467 if errors:
1468 results.append(output_api.PresubmitError(
1469 'The name of PNG files should not have abbreviations. \n'
1470 'Use _hover.png, _center.png, instead of _h.png, _c.png.\n'
1471 'Contact [email protected] if you have questions.', errors))
1472 return results
1473
1474
Daniel Cheng4dcdb6b2017-04-13 08:30:171475def _ExtractAddRulesFromParsedDeps(parsed_deps):
1476 """Extract the rules that add dependencies from a parsed DEPS file.
1477
1478 Args:
1479 parsed_deps: the locals dictionary from evaluating the DEPS file."""
1480 add_rules = set()
1481 add_rules.update([
1482 rule[1:] for rule in parsed_deps.get('include_rules', [])
1483 if rule.startswith('+') or rule.startswith('!')
1484 ])
Vaclav Brozekd5de76a2018-03-17 07:57:501485 for _, rules in parsed_deps.get('specific_include_rules',
Daniel Cheng4dcdb6b2017-04-13 08:30:171486 {}).iteritems():
1487 add_rules.update([
1488 rule[1:] for rule in rules
1489 if rule.startswith('+') or rule.startswith('!')
1490 ])
1491 return add_rules
1492
1493
1494def _ParseDeps(contents):
1495 """Simple helper for parsing DEPS files."""
1496 # Stubs for handling special syntax in the root DEPS file.
Daniel Cheng4dcdb6b2017-04-13 08:30:171497 class _VarImpl:
1498
1499 def __init__(self, local_scope):
1500 self._local_scope = local_scope
1501
1502 def Lookup(self, var_name):
1503 """Implements the Var syntax."""
1504 try:
1505 return self._local_scope['vars'][var_name]
1506 except KeyError:
1507 raise Exception('Var is not defined: %s' % var_name)
1508
1509 local_scope = {}
1510 global_scope = {
Daniel Cheng4dcdb6b2017-04-13 08:30:171511 'Var': _VarImpl(local_scope).Lookup,
1512 }
1513 exec contents in global_scope, local_scope
1514 return local_scope
1515
1516
1517def _CalculateAddedDeps(os_path, old_contents, new_contents):
[email protected]f32e2d1e2013-07-26 21:39:081518 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
[email protected]14a6131c2014-01-08 01:15:411519 a set of DEPS entries that we should look up.
1520
1521 For a directory (rather than a specific filename) we fake a path to
1522 a specific filename by adding /DEPS. This is chosen as a file that
1523 will seldom or never be subject to per-file include_rules.
1524 """
[email protected]2b438d62013-11-14 17:54:141525 # We ignore deps entries on auto-generated directories.
1526 AUTO_GENERATED_DIRS = ['grit', 'jni']
[email protected]f32e2d1e2013-07-26 21:39:081527
Daniel Cheng4dcdb6b2017-04-13 08:30:171528 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1529 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
1530
1531 added_deps = new_deps.difference(old_deps)
1532
[email protected]2b438d62013-11-14 17:54:141533 results = set()
Daniel Cheng4dcdb6b2017-04-13 08:30:171534 for added_dep in added_deps:
1535 if added_dep.split('/')[0] in AUTO_GENERATED_DIRS:
1536 continue
1537 # Assume that a rule that ends in .h is a rule for a specific file.
1538 if added_dep.endswith('.h'):
1539 results.add(added_dep)
1540 else:
1541 results.add(os_path.join(added_dep, 'DEPS'))
[email protected]f32e2d1e2013-07-26 21:39:081542 return results
1543
1544
[email protected]e871964c2013-05-13 14:14:551545def _CheckAddedDepsHaveTargetApprovals(input_api, output_api):
1546 """When a dependency prefixed with + is added to a DEPS file, we
1547 want to make sure that the change is reviewed by an OWNER of the
1548 target file or directory, to avoid layering violations from being
1549 introduced. This check verifies that this happens.
1550 """
Daniel Cheng4dcdb6b2017-04-13 08:30:171551 virtual_depended_on_files = set()
jochen53efcdd2016-01-29 05:09:241552
1553 file_filter = lambda f: not input_api.re.match(
Egor Paskoce145c42018-09-28 19:31:041554 r"^third_party[\\/](WebKit|blink)[\\/].*", f.LocalPath())
jochen53efcdd2016-01-29 05:09:241555 for f in input_api.AffectedFiles(include_deletes=False,
1556 file_filter=file_filter):
[email protected]e871964c2013-05-13 14:14:551557 filename = input_api.os_path.basename(f.LocalPath())
1558 if filename == 'DEPS':
Daniel Cheng4dcdb6b2017-04-13 08:30:171559 virtual_depended_on_files.update(_CalculateAddedDeps(
1560 input_api.os_path,
1561 '\n'.join(f.OldContents()),
1562 '\n'.join(f.NewContents())))
[email protected]e871964c2013-05-13 14:14:551563
[email protected]e871964c2013-05-13 14:14:551564 if not virtual_depended_on_files:
1565 return []
1566
1567 if input_api.is_committing:
1568 if input_api.tbr:
1569 return [output_api.PresubmitNotifyResult(
1570 '--tbr was specified, skipping OWNERS check for DEPS additions')]
Paweł Hajdan, Jrbe6739ea2016-04-28 15:07:271571 if input_api.dry_run:
1572 return [output_api.PresubmitNotifyResult(
1573 'This is a dry run, skipping OWNERS check for DEPS additions')]
[email protected]e871964c2013-05-13 14:14:551574 if not input_api.change.issue:
1575 return [output_api.PresubmitError(
1576 "DEPS approval by OWNERS check failed: this change has "
Aaron Gable65a99d92017-10-09 19:17:401577 "no change number, so we can't check it for approvals.")]
[email protected]e871964c2013-05-13 14:14:551578 output = output_api.PresubmitError
1579 else:
1580 output = output_api.PresubmitNotifyResult
1581
1582 owners_db = input_api.owners_db
tandriied3b7e12016-05-12 14:38:501583 owner_email, reviewers = (
1584 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1585 input_api,
1586 owners_db.email_regexp,
1587 approval_needed=input_api.is_committing))
[email protected]e871964c2013-05-13 14:14:551588
1589 owner_email = owner_email or input_api.change.author_email
1590
[email protected]de4f7d22013-05-23 14:27:461591 reviewers_plus_owner = set(reviewers)
[email protected]e71c6082013-05-22 02:28:511592 if owner_email:
[email protected]de4f7d22013-05-23 14:27:461593 reviewers_plus_owner.add(owner_email)
[email protected]e871964c2013-05-13 14:14:551594 missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
1595 reviewers_plus_owner)
[email protected]14a6131c2014-01-08 01:15:411596
1597 # We strip the /DEPS part that was added by
1598 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1599 # directory.
1600 def StripDeps(path):
1601 start_deps = path.rfind('/DEPS')
1602 if start_deps != -1:
1603 return path[:start_deps]
1604 else:
1605 return path
1606 unapproved_dependencies = ["'+%s'," % StripDeps(path)
[email protected]e871964c2013-05-13 14:14:551607 for path in missing_files]
1608
1609 if unapproved_dependencies:
1610 output_list = [
Paweł Hajdan, Jrec17f882016-07-04 14:16:151611 output('You need LGTM from owners of depends-on paths in DEPS that were '
1612 'modified in this CL:\n %s' %
1613 '\n '.join(sorted(unapproved_dependencies)))]
1614 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
1615 output_list.append(output(
1616 'Suggested missing target path OWNERS:\n %s' %
1617 '\n '.join(suggested_owners or [])))
[email protected]e871964c2013-05-13 14:14:551618 return output_list
1619
1620 return []
1621
1622
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491623# TODO: add unit tests.
[email protected]85218562013-11-22 07:41:401624def _CheckSpamLogging(input_api, output_api):
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491625 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
[email protected]85218562013-11-22 07:41:401626 black_list = (_EXCLUDED_PATHS +
1627 _TEST_CODE_EXCLUDED_PATHS +
1628 input_api.DEFAULT_BLACK_LIST +
Egor Paskoce145c42018-09-28 19:31:041629 (r"^base[\\/]logging\.h$",
1630 r"^base[\\/]logging\.cc$",
1631 r"^chrome[\\/]app[\\/]chrome_main_delegate\.cc$",
1632 r"^chrome[\\/]browser[\\/]chrome_browser_main\.cc$",
1633 r"^chrome[\\/]browser[\\/]ui[\\/]startup[\\/]"
[email protected]4de75262013-12-18 23:16:121634 r"startup_browser_creator\.cc$",
Egor Paskoce145c42018-09-28 19:31:041635 r"^chrome[\\/]installer[\\/]setup[\\/].*",
1636 r"^chrome[\\/]chrome_cleaner[\\/].*",
1637 r"chrome[\\/]browser[\\/]diagnostics[\\/]" +
[email protected]f5b9a3f342014-08-08 22:06:031638 r"diagnostics_writer\.cc$",
Egor Paskoce145c42018-09-28 19:31:041639 r"^chrome_elf[\\/]dll_hash[\\/]dll_hash_main\.cc$",
1640 r"^chromecast[\\/]",
1641 r"^cloud_print[\\/]",
1642 r"^components[\\/]browser_watcher[\\/]"
manzagop85e629e2017-05-09 22:11:481643 r"dump_stability_report_main_win.cc$",
Egor Paskoce145c42018-09-28 19:31:041644 r"^components[\\/]html_viewer[\\/]"
jochen34415e52015-07-10 08:34:311645 r"web_test_delegate_impl\.cc$",
Egor Paskoce145c42018-09-28 19:31:041646 r"^components[\\/]zucchini[\\/].*",
peter80739bb2015-10-20 11:17:461647 # TODO(peter): Remove this exception. https://crbug.com/534537
Egor Paskoce145c42018-09-28 19:31:041648 r"^content[\\/]browser[\\/]notifications[\\/]"
peter80739bb2015-10-20 11:17:461649 r"notification_event_dispatcher_impl\.cc$",
Egor Paskoce145c42018-09-28 19:31:041650 r"^content[\\/]common[\\/]gpu[\\/]client[\\/]"
[email protected]9056e732014-01-08 06:25:251651 r"gl_helper_benchmark\.cc$",
Egor Paskoce145c42018-09-28 19:31:041652 r"^courgette[\\/]courgette_minimal_tool\.cc$",
1653 r"^courgette[\\/]courgette_tool\.cc$",
1654 r"^extensions[\\/]renderer[\\/]logging_native_handler\.cc$",
1655 r"^ipc[\\/]ipc_logging\.cc$",
1656 r"^native_client_sdk[\\/]",
1657 r"^remoting[\\/]base[\\/]logging\.h$",
1658 r"^remoting[\\/]host[\\/].*",
1659 r"^sandbox[\\/]linux[\\/].*",
1660 r"^tools[\\/]",
1661 r"^ui[\\/]base[\\/]resource[\\/]data_pack.cc$",
1662 r"^ui[\\/]aura[\\/]bench[\\/]bench_main\.cc$",
1663 r"^ui[\\/]ozone[\\/]platform[\\/]cast[\\/]",
Kevin Marshalla139bcc2018-11-09 02:26:361664 r"^webrunner[\\/]browser[\\/]frame_impl.cc$",
Egor Paskoce145c42018-09-28 19:31:041665 r"^storage[\\/]browser[\\/]fileapi[\\/]" +
skyostil87681be82016-12-19 12:46:351666 r"dump_file_system.cc$",
Egor Paskoce145c42018-09-28 19:31:041667 r"^headless[\\/]app[\\/]headless_shell\.cc$"))
[email protected]85218562013-11-22 07:41:401668 source_file_filter = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491669 x, white_list=file_inclusion_pattern, black_list=black_list)
[email protected]85218562013-11-22 07:41:401670
thomasanderson625d3932017-03-29 07:16:581671 log_info = set([])
1672 printf = set([])
[email protected]85218562013-11-22 07:41:401673
1674 for f in input_api.AffectedSourceFiles(source_file_filter):
thomasanderson625d3932017-03-29 07:16:581675 for _, line in f.ChangedContents():
1676 if input_api.re.search(r"\bD?LOG\s*\(\s*INFO\s*\)", line):
1677 log_info.add(f.LocalPath())
1678 elif input_api.re.search(r"\bD?LOG_IF\s*\(\s*INFO\s*,", line):
1679 log_info.add(f.LocalPath())
[email protected]18b466b2013-12-02 22:01:371680
thomasanderson625d3932017-03-29 07:16:581681 if input_api.re.search(r"\bprintf\(", line):
1682 printf.add(f.LocalPath())
1683 elif input_api.re.search(r"\bfprintf\((stdout|stderr)", line):
1684 printf.add(f.LocalPath())
[email protected]85218562013-11-22 07:41:401685
1686 if log_info:
1687 return [output_api.PresubmitError(
1688 'These files spam the console log with LOG(INFO):',
1689 items=log_info)]
1690 if printf:
1691 return [output_api.PresubmitError(
1692 'These files spam the console log with printf/fprintf:',
1693 items=printf)]
1694 return []
1695
1696
[email protected]49aa76a2013-12-04 06:59:161697def _CheckForAnonymousVariables(input_api, output_api):
1698 """These types are all expected to hold locks while in scope and
1699 so should never be anonymous (which causes them to be immediately
1700 destroyed)."""
1701 they_who_must_be_named = [
1702 'base::AutoLock',
1703 'base::AutoReset',
1704 'base::AutoUnlock',
1705 'SkAutoAlphaRestore',
1706 'SkAutoBitmapShaderInstall',
1707 'SkAutoBlitterChoose',
1708 'SkAutoBounderCommit',
1709 'SkAutoCallProc',
1710 'SkAutoCanvasRestore',
1711 'SkAutoCommentBlock',
1712 'SkAutoDescriptor',
1713 'SkAutoDisableDirectionCheck',
1714 'SkAutoDisableOvalCheck',
1715 'SkAutoFree',
1716 'SkAutoGlyphCache',
1717 'SkAutoHDC',
1718 'SkAutoLockColors',
1719 'SkAutoLockPixels',
1720 'SkAutoMalloc',
1721 'SkAutoMaskFreeImage',
1722 'SkAutoMutexAcquire',
1723 'SkAutoPathBoundsUpdate',
1724 'SkAutoPDFRelease',
1725 'SkAutoRasterClipValidate',
1726 'SkAutoRef',
1727 'SkAutoTime',
1728 'SkAutoTrace',
1729 'SkAutoUnref',
1730 ]
1731 anonymous = r'(%s)\s*[({]' % '|'.join(they_who_must_be_named)
1732 # bad: base::AutoLock(lock.get());
1733 # not bad: base::AutoLock lock(lock.get());
1734 bad_pattern = input_api.re.compile(anonymous)
1735 # good: new base::AutoLock(lock.get())
1736 good_pattern = input_api.re.compile(r'\bnew\s*' + anonymous)
1737 errors = []
1738
1739 for f in input_api.AffectedFiles():
1740 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
1741 continue
1742 for linenum, line in f.ChangedContents():
1743 if bad_pattern.search(line) and not good_pattern.search(line):
1744 errors.append('%s:%d' % (f.LocalPath(), linenum))
1745
1746 if errors:
1747 return [output_api.PresubmitError(
1748 'These lines create anonymous variables that need to be named:',
1749 items=errors)]
1750 return []
1751
1752
Peter Kasting4844e46e2018-02-23 07:27:101753def _CheckUniquePtr(input_api, output_api):
Vaclav Brozekb7fadb692018-08-30 06:39:531754 # Returns whether |template_str| is of the form <T, U...> for some types T
1755 # and U. Assumes that |template_str| is already in the form <...>.
1756 def HasMoreThanOneArg(template_str):
1757 # Level of <...> nesting.
1758 nesting = 0
1759 for c in template_str:
1760 if c == '<':
1761 nesting += 1
1762 elif c == '>':
1763 nesting -= 1
1764 elif c == ',' and nesting == 1:
1765 return True
1766 return False
1767
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491768 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
Peter Kasting4844e46e2018-02-23 07:27:101769 sources = lambda affected_file: input_api.FilterSourceFile(
1770 affected_file,
1771 black_list=(_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
1772 input_api.DEFAULT_BLACK_LIST),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491773 white_list=file_inclusion_pattern)
Vaclav Brozeka54c528b2018-04-06 19:23:551774
1775 # Pattern to capture a single "<...>" block of template arguments. It can
1776 # handle linearly nested blocks, such as "<std::vector<std::set<T>>>", but
1777 # cannot handle branching structures, such as "<pair<set<T>,set<U>>". The
1778 # latter would likely require counting that < and > match, which is not
1779 # expressible in regular languages. Should the need arise, one can introduce
1780 # limited counting (matching up to a total number of nesting depth), which
1781 # should cover all practical cases for already a low nesting limit.
1782 template_arg_pattern = (
1783 r'<[^>]*' # Opening block of <.
1784 r'>([^<]*>)?') # Closing block of >.
1785 # Prefix expressing that whatever follows is not already inside a <...>
1786 # block.
1787 not_inside_template_arg_pattern = r'(^|[^<,\s]\s*)'
Peter Kasting4844e46e2018-02-23 07:27:101788 null_construct_pattern = input_api.re.compile(
Vaclav Brozeka54c528b2018-04-06 19:23:551789 not_inside_template_arg_pattern
1790 + r'\bstd::unique_ptr'
1791 + template_arg_pattern
1792 + r'\(\)')
1793
1794 # Same as template_arg_pattern, but excluding type arrays, e.g., <T[]>.
1795 template_arg_no_array_pattern = (
1796 r'<[^>]*[^]]' # Opening block of <.
1797 r'>([^(<]*[^]]>)?') # Closing block of >.
1798 # Prefix saying that what follows is the start of an expression.
1799 start_of_expr_pattern = r'(=|\breturn|^)\s*'
1800 # Suffix saying that what follows are call parentheses with a non-empty list
1801 # of arguments.
1802 nonempty_arg_list_pattern = r'\(([^)]|$)'
Vaclav Brozekb7fadb692018-08-30 06:39:531803 # Put the template argument into a capture group for deeper examination later.
Vaclav Brozeka54c528b2018-04-06 19:23:551804 return_construct_pattern = input_api.re.compile(
1805 start_of_expr_pattern
1806 + r'std::unique_ptr'
Vaclav Brozekb7fadb692018-08-30 06:39:531807 + '(?P<template_arg>'
Vaclav Brozeka54c528b2018-04-06 19:23:551808 + template_arg_no_array_pattern
Vaclav Brozekb7fadb692018-08-30 06:39:531809 + ')'
Vaclav Brozeka54c528b2018-04-06 19:23:551810 + nonempty_arg_list_pattern)
1811
Vaclav Brozek851d9602018-04-04 16:13:051812 problems_constructor = []
1813 problems_nullptr = []
Peter Kasting4844e46e2018-02-23 07:27:101814 for f in input_api.AffectedSourceFiles(sources):
1815 for line_number, line in f.ChangedContents():
1816 # Disallow:
1817 # return std::unique_ptr<T>(foo);
1818 # bar = std::unique_ptr<T>(foo);
1819 # But allow:
1820 # return std::unique_ptr<T[]>(foo);
1821 # bar = std::unique_ptr<T[]>(foo);
Vaclav Brozekb7fadb692018-08-30 06:39:531822 # And also allow cases when the second template argument is present. Those
1823 # cases cannot be handled by std::make_unique:
1824 # return std::unique_ptr<T, U>(foo);
1825 # bar = std::unique_ptr<T, U>(foo);
Vaclav Brozek851d9602018-04-04 16:13:051826 local_path = f.LocalPath()
Vaclav Brozekb7fadb692018-08-30 06:39:531827 return_construct_result = return_construct_pattern.search(line)
1828 if return_construct_result and not HasMoreThanOneArg(
1829 return_construct_result.group('template_arg')):
Vaclav Brozek851d9602018-04-04 16:13:051830 problems_constructor.append(
1831 '%s:%d\n %s' % (local_path, line_number, line.strip()))
Peter Kasting4844e46e2018-02-23 07:27:101832 # Disallow:
1833 # std::unique_ptr<T>()
1834 if null_construct_pattern.search(line):
Vaclav Brozek851d9602018-04-04 16:13:051835 problems_nullptr.append(
1836 '%s:%d\n %s' % (local_path, line_number, line.strip()))
1837
1838 errors = []
Vaclav Brozekc2fecf42018-04-06 16:40:161839 if problems_nullptr:
Vaclav Brozek851d9602018-04-04 16:13:051840 errors.append(output_api.PresubmitError(
1841 'The following files use std::unique_ptr<T>(). Use nullptr instead.',
Vaclav Brozekc2fecf42018-04-06 16:40:161842 problems_nullptr))
1843 if problems_constructor:
Vaclav Brozek851d9602018-04-04 16:13:051844 errors.append(output_api.PresubmitError(
1845 'The following files use explicit std::unique_ptr constructor.'
1846 'Use std::make_unique<T>() instead.',
Vaclav Brozekc2fecf42018-04-06 16:40:161847 problems_constructor))
Peter Kasting4844e46e2018-02-23 07:27:101848 return errors
1849
1850
[email protected]999261d2014-03-03 20:08:081851def _CheckUserActionUpdate(input_api, output_api):
1852 """Checks if any new user action has been added."""
[email protected]2f92dec2014-03-07 19:21:521853 if any('actions.xml' == input_api.os_path.basename(f) for f in
[email protected]999261d2014-03-03 20:08:081854 input_api.LocalPaths()):
[email protected]2f92dec2014-03-07 19:21:521855 # If actions.xml is already included in the changelist, the PRESUBMIT
1856 # for actions.xml will do a more complete presubmit check.
[email protected]999261d2014-03-03 20:08:081857 return []
1858
[email protected]999261d2014-03-03 20:08:081859 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm'))
1860 action_re = r'[^a-zA-Z]UserMetricsAction\("([^"]*)'
[email protected]2f92dec2014-03-07 19:21:521861 current_actions = None
[email protected]999261d2014-03-03 20:08:081862 for f in input_api.AffectedFiles(file_filter=file_filter):
1863 for line_num, line in f.ChangedContents():
1864 match = input_api.re.search(action_re, line)
1865 if match:
[email protected]2f92dec2014-03-07 19:21:521866 # Loads contents in tools/metrics/actions/actions.xml to memory. It's
1867 # loaded only once.
1868 if not current_actions:
1869 with open('tools/metrics/actions/actions.xml') as actions_f:
1870 current_actions = actions_f.read()
1871 # Search for the matched user action name in |current_actions|.
[email protected]999261d2014-03-03 20:08:081872 for action_name in match.groups():
[email protected]2f92dec2014-03-07 19:21:521873 action = 'name="{0}"'.format(action_name)
1874 if action not in current_actions:
[email protected]999261d2014-03-03 20:08:081875 return [output_api.PresubmitPromptWarning(
1876 'File %s line %d: %s is missing in '
[email protected]2f92dec2014-03-07 19:21:521877 'tools/metrics/actions/actions.xml. Please run '
1878 'tools/metrics/actions/extract_actions.py to update.'
[email protected]999261d2014-03-03 20:08:081879 % (f.LocalPath(), line_num, action_name))]
1880 return []
1881
1882
Daniel Cheng13ca61a882017-08-25 15:11:251883def _ImportJSONCommentEater(input_api):
1884 import sys
1885 sys.path = sys.path + [input_api.os_path.join(
1886 input_api.PresubmitLocalPath(),
1887 'tools', 'json_comment_eater')]
1888 import json_comment_eater
1889 return json_comment_eater
1890
1891
[email protected]99171a92014-06-03 08:44:471892def _GetJSONParseError(input_api, filename, eat_comments=True):
1893 try:
1894 contents = input_api.ReadFile(filename)
1895 if eat_comments:
Daniel Cheng13ca61a882017-08-25 15:11:251896 json_comment_eater = _ImportJSONCommentEater(input_api)
plundblad1f5a4509f2015-07-23 11:31:131897 contents = json_comment_eater.Nom(contents)
[email protected]99171a92014-06-03 08:44:471898
1899 input_api.json.loads(contents)
1900 except ValueError as e:
1901 return e
1902 return None
1903
1904
1905def _GetIDLParseError(input_api, filename):
1906 try:
1907 contents = input_api.ReadFile(filename)
1908 idl_schema = input_api.os_path.join(
1909 input_api.PresubmitLocalPath(),
1910 'tools', 'json_schema_compiler', 'idl_schema.py')
1911 process = input_api.subprocess.Popen(
1912 [input_api.python_executable, idl_schema],
1913 stdin=input_api.subprocess.PIPE,
1914 stdout=input_api.subprocess.PIPE,
1915 stderr=input_api.subprocess.PIPE,
1916 universal_newlines=True)
1917 (_, error) = process.communicate(input=contents)
1918 return error or None
1919 except ValueError as e:
1920 return e
1921
1922
1923def _CheckParseErrors(input_api, output_api):
1924 """Check that IDL and JSON files do not contain syntax errors."""
1925 actions = {
1926 '.idl': _GetIDLParseError,
1927 '.json': _GetJSONParseError,
1928 }
[email protected]99171a92014-06-03 08:44:471929 # Most JSON files are preprocessed and support comments, but these do not.
1930 json_no_comments_patterns = [
Egor Paskoce145c42018-09-28 19:31:041931 r'^testing[\\/]',
[email protected]99171a92014-06-03 08:44:471932 ]
1933 # Only run IDL checker on files in these directories.
1934 idl_included_patterns = [
Egor Paskoce145c42018-09-28 19:31:041935 r'^chrome[\\/]common[\\/]extensions[\\/]api[\\/]',
1936 r'^extensions[\\/]common[\\/]api[\\/]',
[email protected]99171a92014-06-03 08:44:471937 ]
1938
1939 def get_action(affected_file):
1940 filename = affected_file.LocalPath()
1941 return actions.get(input_api.os_path.splitext(filename)[1])
1942
[email protected]99171a92014-06-03 08:44:471943 def FilterFile(affected_file):
1944 action = get_action(affected_file)
1945 if not action:
1946 return False
1947 path = affected_file.LocalPath()
1948
Sean Kau46e29bc2017-08-28 16:31:161949 if _MatchesFile(input_api, _KNOWN_INVALID_JSON_FILE_PATTERNS, path):
[email protected]99171a92014-06-03 08:44:471950 return False
1951
1952 if (action == _GetIDLParseError and
Sean Kau46e29bc2017-08-28 16:31:161953 not _MatchesFile(input_api, idl_included_patterns, path)):
[email protected]99171a92014-06-03 08:44:471954 return False
1955 return True
1956
1957 results = []
1958 for affected_file in input_api.AffectedFiles(
1959 file_filter=FilterFile, include_deletes=False):
1960 action = get_action(affected_file)
1961 kwargs = {}
1962 if (action == _GetJSONParseError and
Sean Kau46e29bc2017-08-28 16:31:161963 _MatchesFile(input_api, json_no_comments_patterns,
1964 affected_file.LocalPath())):
[email protected]99171a92014-06-03 08:44:471965 kwargs['eat_comments'] = False
1966 parse_error = action(input_api,
1967 affected_file.AbsoluteLocalPath(),
1968 **kwargs)
1969 if parse_error:
1970 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
1971 (affected_file.LocalPath(), parse_error)))
1972 return results
1973
1974
[email protected]760deea2013-12-10 19:33:491975def _CheckJavaStyle(input_api, output_api):
1976 """Runs checkstyle on changed java files and returns errors if any exist."""
mohan.reddyf21db962014-10-16 12:26:471977 import sys
[email protected]760deea2013-12-10 19:33:491978 original_sys_path = sys.path
1979 try:
1980 sys.path = sys.path + [input_api.os_path.join(
1981 input_api.PresubmitLocalPath(), 'tools', 'android', 'checkstyle')]
1982 import checkstyle
1983 finally:
1984 # Restore sys.path to what it was before.
1985 sys.path = original_sys_path
1986
1987 return checkstyle.RunCheckstyle(
davileen72d76532015-01-20 22:30:091988 input_api, output_api, 'tools/android/checkstyle/chromium-style-5.0.xml',
newtd8b7d30e92015-01-23 18:10:511989 black_list=_EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
[email protected]760deea2013-12-10 19:33:491990
1991
Sean Kau46e29bc2017-08-28 16:31:161992def _MatchesFile(input_api, patterns, path):
1993 for pattern in patterns:
1994 if input_api.re.search(pattern, path):
1995 return True
1996 return False
1997
1998
Daniel Cheng7052cdf2017-11-21 19:23:291999def _GetOwnersFilesToCheckForIpcOwners(input_api):
2000 """Gets a list of OWNERS files to check for correct security owners.
dchenge07de812016-06-20 19:27:172001
Daniel Cheng7052cdf2017-11-21 19:23:292002 Returns:
2003 A dictionary mapping an OWNER file to the list of OWNERS rules it must
2004 contain to cover IPC-related files with noparent reviewer rules.
2005 """
2006 # Whether or not a file affects IPC is (mostly) determined by a simple list
2007 # of filename patterns.
dchenge07de812016-06-20 19:27:172008 file_patterns = [
palmerb19a0932017-01-24 04:00:312009 # Legacy IPC:
dchenge07de812016-06-20 19:27:172010 '*_messages.cc',
2011 '*_messages*.h',
2012 '*_param_traits*.*',
palmerb19a0932017-01-24 04:00:312013 # Mojo IPC:
dchenge07de812016-06-20 19:27:172014 '*.mojom',
Daniel Cheng1f386932018-01-29 19:56:472015 '*_mojom_traits*.*',
dchenge07de812016-06-20 19:27:172016 '*_struct_traits*.*',
2017 '*_type_converter*.*',
palmerb19a0932017-01-24 04:00:312018 '*.typemap',
2019 # Android native IPC:
2020 '*.aidl',
2021 # Blink uses a different file naming convention:
2022 '*EnumTraits*.*',
Daniel Chenge0bf3f62018-01-30 01:56:472023 "*MojomTraits*.*",
dchenge07de812016-06-20 19:27:172024 '*StructTraits*.*',
2025 '*TypeConverter*.*',
2026 ]
2027
scottmg7a6ed5ba2016-11-04 18:22:042028 # These third_party directories do not contain IPCs, but contain files
2029 # matching the above patterns, which trigger false positives.
2030 exclude_paths = [
2031 'third_party/crashpad/*',
Andres Medinae684cf42018-08-27 18:48:232032 'third_party/protobuf/benchmarks/python/*',
Daniel Chengebe635e2018-07-13 12:36:062033 'third_party/third_party/blink/renderer/platform/bindings/*',
Nico Weberee3dc9b2017-08-31 17:09:292034 'third_party/win_build_output/*',
scottmg7a6ed5ba2016-11-04 18:22:042035 ]
2036
dchenge07de812016-06-20 19:27:172037 # Dictionary mapping an OWNERS file path to Patterns.
2038 # Patterns is a dictionary mapping glob patterns (suitable for use in per-file
2039 # rules ) to a PatternEntry.
2040 # PatternEntry is a dictionary with two keys:
2041 # - 'files': the files that are matched by this pattern
2042 # - 'rules': the per-file rules needed for this pattern
2043 # For example, if we expect OWNERS file to contain rules for *.mojom and
2044 # *_struct_traits*.*, Patterns might look like this:
2045 # {
2046 # '*.mojom': {
2047 # 'files': ...,
2048 # 'rules': [
2049 # 'per-file *.mojom=set noparent',
2050 # 'per-file *.mojom=file://ipc/SECURITY_OWNERS',
2051 # ],
2052 # },
2053 # '*_struct_traits*.*': {
2054 # 'files': ...,
2055 # 'rules': [
2056 # 'per-file *_struct_traits*.*=set noparent',
2057 # 'per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS',
2058 # ],
2059 # },
2060 # }
2061 to_check = {}
2062
Daniel Cheng13ca61a882017-08-25 15:11:252063 def AddPatternToCheck(input_file, pattern):
2064 owners_file = input_api.os_path.join(
2065 input_api.os_path.dirname(input_file.LocalPath()), 'OWNERS')
2066 if owners_file not in to_check:
2067 to_check[owners_file] = {}
2068 if pattern not in to_check[owners_file]:
2069 to_check[owners_file][pattern] = {
2070 'files': [],
2071 'rules': [
2072 'per-file %s=set noparent' % pattern,
2073 'per-file %s=file://ipc/SECURITY_OWNERS' % pattern,
2074 ]
2075 }
Vaclav Brozekd5de76a2018-03-17 07:57:502076 to_check[owners_file][pattern]['files'].append(input_file)
Daniel Cheng13ca61a882017-08-25 15:11:252077
dchenge07de812016-06-20 19:27:172078 # Iterate through the affected files to see what we actually need to check
2079 # for. We should only nag patch authors about per-file rules if a file in that
2080 # directory would match that pattern. If a directory only contains *.mojom
2081 # files and no *_messages*.h files, we should only nag about rules for
2082 # *.mojom files.
Daniel Cheng13ca61a882017-08-25 15:11:252083 for f in input_api.AffectedFiles(include_deletes=False):
2084 # Manifest files don't have a strong naming convention. Instead, scan
2085 # affected files for .json files and see if they look like a manifest.
Sean Kau46e29bc2017-08-28 16:31:162086 if (f.LocalPath().endswith('.json') and
2087 not _MatchesFile(input_api, _KNOWN_INVALID_JSON_FILE_PATTERNS,
2088 f.LocalPath())):
Daniel Cheng13ca61a882017-08-25 15:11:252089 json_comment_eater = _ImportJSONCommentEater(input_api)
2090 mostly_json_lines = '\n'.join(f.NewContents())
2091 # Comments aren't allowed in strict JSON, so filter them out.
2092 json_lines = json_comment_eater.Nom(mostly_json_lines)
Daniel Chenge8efd092018-03-23 23:57:432093 try:
2094 json_content = input_api.json.loads(json_lines)
2095 except:
2096 # There's another PRESUBMIT check that already verifies that JSON files
2097 # are not invalid, so no need to emit another warning here.
2098 continue
Daniel Cheng13ca61a882017-08-25 15:11:252099 if 'interface_provider_specs' in json_content:
2100 AddPatternToCheck(f, input_api.os_path.basename(f.LocalPath()))
dchenge07de812016-06-20 19:27:172101 for pattern in file_patterns:
2102 if input_api.fnmatch.fnmatch(
2103 input_api.os_path.basename(f.LocalPath()), pattern):
scottmg7a6ed5ba2016-11-04 18:22:042104 skip = False
2105 for exclude in exclude_paths:
2106 if input_api.fnmatch.fnmatch(f.LocalPath(), exclude):
2107 skip = True
2108 break
2109 if skip:
2110 continue
Daniel Cheng13ca61a882017-08-25 15:11:252111 AddPatternToCheck(f, pattern)
dchenge07de812016-06-20 19:27:172112 break
2113
Daniel Cheng7052cdf2017-11-21 19:23:292114 return to_check
2115
2116
2117def _CheckIpcOwners(input_api, output_api):
2118 """Checks that affected files involving IPC have an IPC OWNERS rule."""
2119 to_check = _GetOwnersFilesToCheckForIpcOwners(input_api)
2120
2121 if to_check:
2122 # If there are any OWNERS files to check, there are IPC-related changes in
2123 # this CL. Auto-CC the review list.
2124 output_api.AppendCC('[email protected]')
2125
2126 # Go through the OWNERS files to check, filtering out rules that are already
2127 # present in that OWNERS file.
dchenge07de812016-06-20 19:27:172128 for owners_file, patterns in to_check.iteritems():
2129 try:
2130 with file(owners_file) as f:
2131 lines = set(f.read().splitlines())
2132 for entry in patterns.itervalues():
2133 entry['rules'] = [rule for rule in entry['rules'] if rule not in lines
2134 ]
2135 except IOError:
2136 # No OWNERS file, so all the rules are definitely missing.
2137 continue
2138
2139 # All the remaining lines weren't found in OWNERS files, so emit an error.
2140 errors = []
2141 for owners_file, patterns in to_check.iteritems():
2142 missing_lines = []
2143 files = []
Vaclav Brozekd5de76a2018-03-17 07:57:502144 for _, entry in patterns.iteritems():
dchenge07de812016-06-20 19:27:172145 missing_lines.extend(entry['rules'])
2146 files.extend([' %s' % f.LocalPath() for f in entry['files']])
2147 if missing_lines:
2148 errors.append(
Vaclav Brozek1893a972018-04-25 05:48:052149 'Because of the presence of files:\n%s\n\n'
2150 '%s needs the following %d lines added:\n\n%s' %
2151 ('\n'.join(files), owners_file, len(missing_lines),
2152 '\n'.join(missing_lines)))
dchenge07de812016-06-20 19:27:172153
2154 results = []
2155 if errors:
vabrf5ce3bf92016-07-11 14:52:412156 if input_api.is_committing:
2157 output = output_api.PresubmitError
2158 else:
2159 output = output_api.PresubmitPromptWarning
2160 results.append(output(
Daniel Cheng52111692017-06-14 08:00:592161 'Found OWNERS files that need to be updated for IPC security ' +
2162 'review coverage.\nPlease update the OWNERS files below:',
dchenge07de812016-06-20 19:27:172163 long_text='\n\n'.join(errors)))
2164
2165 return results
2166
2167
jbriance9e12f162016-11-25 07:57:502168def _CheckUselessForwardDeclarations(input_api, output_api):
jbriance2c51e821a2016-12-12 08:24:312169 """Checks that added or removed lines in non third party affected
2170 header files do not lead to new useless class or struct forward
2171 declaration.
jbriance9e12f162016-11-25 07:57:502172 """
2173 results = []
2174 class_pattern = input_api.re.compile(r'^class\s+(\w+);$',
2175 input_api.re.MULTILINE)
2176 struct_pattern = input_api.re.compile(r'^struct\s+(\w+);$',
2177 input_api.re.MULTILINE)
2178 for f in input_api.AffectedFiles(include_deletes=False):
jbriance2c51e821a2016-12-12 08:24:312179 if (f.LocalPath().startswith('third_party') and
Kent Tamurae9b3a9ec2017-08-31 02:20:192180 not f.LocalPath().startswith('third_party/blink') and
2181 not f.LocalPath().startswith('third_party\\blink') and
jbriance2c51e821a2016-12-12 08:24:312182 not f.LocalPath().startswith('third_party/WebKit') and
2183 not f.LocalPath().startswith('third_party\\WebKit')):
2184 continue
2185
jbriance9e12f162016-11-25 07:57:502186 if not f.LocalPath().endswith('.h'):
2187 continue
2188
2189 contents = input_api.ReadFile(f)
2190 fwd_decls = input_api.re.findall(class_pattern, contents)
2191 fwd_decls.extend(input_api.re.findall(struct_pattern, contents))
2192
2193 useless_fwd_decls = []
2194 for decl in fwd_decls:
2195 count = sum(1 for _ in input_api.re.finditer(
2196 r'\b%s\b' % input_api.re.escape(decl), contents))
2197 if count == 1:
2198 useless_fwd_decls.append(decl)
2199
2200 if not useless_fwd_decls:
2201 continue
2202
2203 for line in f.GenerateScmDiff().splitlines():
2204 if (line.startswith('-') and not line.startswith('--') or
2205 line.startswith('+') and not line.startswith('++')):
2206 for decl in useless_fwd_decls:
2207 if input_api.re.search(r'\b%s\b' % decl, line[1:]):
2208 results.append(output_api.PresubmitPromptWarning(
ricea6416dea2017-05-19 12:39:242209 '%s: %s forward declaration is no longer needed' %
jbriance9e12f162016-11-25 07:57:502210 (f.LocalPath(), decl)))
2211 useless_fwd_decls.remove(decl)
2212
2213 return results
2214
2215
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492216# TODO: add unit tests
dskiba88634f4e2015-08-14 23:03:292217def _CheckAndroidToastUsage(input_api, output_api):
2218 """Checks that code uses org.chromium.ui.widget.Toast instead of
2219 android.widget.Toast (Chromium Toast doesn't force hardware
2220 acceleration on low-end devices, saving memory).
2221 """
2222 toast_import_pattern = input_api.re.compile(
2223 r'^import android\.widget\.Toast;$')
2224
2225 errors = []
2226
2227 sources = lambda affected_file: input_api.FilterSourceFile(
2228 affected_file,
2229 black_list=(_EXCLUDED_PATHS +
2230 _TEST_CODE_EXCLUDED_PATHS +
2231 input_api.DEFAULT_BLACK_LIST +
Egor Paskoce145c42018-09-28 19:31:042232 (r'^chromecast[\\/].*',
2233 r'^remoting[\\/].*')),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492234 white_list=[r'.*\.java$'])
dskiba88634f4e2015-08-14 23:03:292235
2236 for f in input_api.AffectedSourceFiles(sources):
2237 for line_num, line in f.ChangedContents():
2238 if toast_import_pattern.search(line):
2239 errors.append("%s:%d" % (f.LocalPath(), line_num))
2240
2241 results = []
2242
2243 if errors:
2244 results.append(output_api.PresubmitError(
2245 'android.widget.Toast usage is detected. Android toasts use hardware'
2246 ' acceleration, and can be\ncostly on low-end devices. Please use'
2247 ' org.chromium.ui.widget.Toast instead.\n'
2248 'Contact [email protected] if you have any questions.',
2249 errors))
2250
2251 return results
2252
2253
dgnaa68d5e2015-06-10 10:08:222254def _CheckAndroidCrLogUsage(input_api, output_api):
2255 """Checks that new logs using org.chromium.base.Log:
2256 - Are using 'TAG' as variable name for the tags (warn)
dgn38736db2015-09-18 19:20:512257 - Are using a tag that is shorter than 20 characters (error)
dgnaa68d5e2015-06-10 10:08:222258 """
pkotwicza1dd0b002016-05-16 14:41:042259
torne89540622017-03-24 19:41:302260 # Do not check format of logs in the given files
pkotwicza1dd0b002016-05-16 14:41:042261 cr_log_check_excluded_paths = [
torne89540622017-03-24 19:41:302262 # //chrome/android/webapk cannot depend on //base
Egor Paskoce145c42018-09-28 19:31:042263 r"^chrome[\\/]android[\\/]webapk[\\/].*",
torne89540622017-03-24 19:41:302264 # WebView license viewer code cannot depend on //base; used in stub APK.
Egor Paskoce145c42018-09-28 19:31:042265 r"^android_webview[\\/]glue[\\/]java[\\/]src[\\/]com[\\/]android[\\/]"
2266 r"webview[\\/]chromium[\\/]License.*",
Egor Paskoa5c05b02018-09-28 16:04:092267 # The customtabs_benchmark is a small app that does not depend on Chromium
2268 # java pieces.
Egor Paskoce145c42018-09-28 19:31:042269 r"tools[\\/]android[\\/]customtabs_benchmark[\\/].*",
pkotwicza1dd0b002016-05-16 14:41:042270 ]
2271
dgnaa68d5e2015-06-10 10:08:222272 cr_log_import_pattern = input_api.re.compile(
dgn87d9fb62015-06-12 09:15:122273 r'^import org\.chromium\.base\.Log;$', input_api.re.MULTILINE)
2274 class_in_base_pattern = input_api.re.compile(
2275 r'^package org\.chromium\.base;$', input_api.re.MULTILINE)
2276 has_some_log_import_pattern = input_api.re.compile(
2277 r'^import .*\.Log;$', input_api.re.MULTILINE)
dgnaa68d5e2015-06-10 10:08:222278 # Extract the tag from lines like `Log.d(TAG, "*");` or `Log.d("TAG", "*");`
dgn87d9fb62015-06-12 09:15:122279 log_call_pattern = input_api.re.compile(r'^\s*Log\.\w\((?P<tag>\"?\w+\"?)\,')
dgnaa68d5e2015-06-10 10:08:222280 log_decl_pattern = input_api.re.compile(
dgn38736db2015-09-18 19:20:512281 r'^\s*private static final String TAG = "(?P<name>(.*))";',
dgnaa68d5e2015-06-10 10:08:222282 input_api.re.MULTILINE)
dgnaa68d5e2015-06-10 10:08:222283
Vincent Scheib16d7b272015-09-15 18:09:072284 REF_MSG = ('See docs/android_logging.md '
dgnaa68d5e2015-06-10 10:08:222285 'or contact [email protected] for more info.')
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492286 sources = lambda x: input_api.FilterSourceFile(x, white_list=[r'.*\.java$'],
pkotwicza1dd0b002016-05-16 14:41:042287 black_list=cr_log_check_excluded_paths)
dgn87d9fb62015-06-12 09:15:122288
dgnaa68d5e2015-06-10 10:08:222289 tag_decl_errors = []
2290 tag_length_errors = []
dgn87d9fb62015-06-12 09:15:122291 tag_errors = []
dgn38736db2015-09-18 19:20:512292 tag_with_dot_errors = []
dgn87d9fb62015-06-12 09:15:122293 util_log_errors = []
dgnaa68d5e2015-06-10 10:08:222294
2295 for f in input_api.AffectedSourceFiles(sources):
2296 file_content = input_api.ReadFile(f)
2297 has_modified_logs = False
2298
2299 # Per line checks
dgn87d9fb62015-06-12 09:15:122300 if (cr_log_import_pattern.search(file_content) or
2301 (class_in_base_pattern.search(file_content) and
2302 not has_some_log_import_pattern.search(file_content))):
2303 # Checks to run for files using cr log
dgnaa68d5e2015-06-10 10:08:222304 for line_num, line in f.ChangedContents():
2305
2306 # Check if the new line is doing some logging
dgn87d9fb62015-06-12 09:15:122307 match = log_call_pattern.search(line)
dgnaa68d5e2015-06-10 10:08:222308 if match:
2309 has_modified_logs = True
2310
2311 # Make sure it uses "TAG"
2312 if not match.group('tag') == 'TAG':
2313 tag_errors.append("%s:%d" % (f.LocalPath(), line_num))
dgn87d9fb62015-06-12 09:15:122314 else:
2315 # Report non cr Log function calls in changed lines
2316 for line_num, line in f.ChangedContents():
2317 if log_call_pattern.search(line):
2318 util_log_errors.append("%s:%d" % (f.LocalPath(), line_num))
dgnaa68d5e2015-06-10 10:08:222319
2320 # Per file checks
2321 if has_modified_logs:
2322 # Make sure the tag is using the "cr" prefix and is not too long
2323 match = log_decl_pattern.search(file_content)
dgn38736db2015-09-18 19:20:512324 tag_name = match.group('name') if match else None
2325 if not tag_name:
dgnaa68d5e2015-06-10 10:08:222326 tag_decl_errors.append(f.LocalPath())
dgn38736db2015-09-18 19:20:512327 elif len(tag_name) > 20:
dgnaa68d5e2015-06-10 10:08:222328 tag_length_errors.append(f.LocalPath())
dgn38736db2015-09-18 19:20:512329 elif '.' in tag_name:
2330 tag_with_dot_errors.append(f.LocalPath())
dgnaa68d5e2015-06-10 10:08:222331
2332 results = []
2333 if tag_decl_errors:
2334 results.append(output_api.PresubmitPromptWarning(
2335 'Please define your tags using the suggested format: .\n'
dgn38736db2015-09-18 19:20:512336 '"private static final String TAG = "<package tag>".\n'
2337 'They will be prepended with "cr_" automatically.\n' + REF_MSG,
dgnaa68d5e2015-06-10 10:08:222338 tag_decl_errors))
2339
2340 if tag_length_errors:
2341 results.append(output_api.PresubmitError(
2342 'The tag length is restricted by the system to be at most '
dgn38736db2015-09-18 19:20:512343 '20 characters.\n' + REF_MSG,
dgnaa68d5e2015-06-10 10:08:222344 tag_length_errors))
2345
2346 if tag_errors:
2347 results.append(output_api.PresubmitPromptWarning(
2348 'Please use a variable named "TAG" for your log tags.\n' + REF_MSG,
2349 tag_errors))
2350
dgn87d9fb62015-06-12 09:15:122351 if util_log_errors:
dgn4401aa52015-04-29 16:26:172352 results.append(output_api.PresubmitPromptWarning(
dgn87d9fb62015-06-12 09:15:122353 'Please use org.chromium.base.Log for new logs.\n' + REF_MSG,
2354 util_log_errors))
2355
dgn38736db2015-09-18 19:20:512356 if tag_with_dot_errors:
2357 results.append(output_api.PresubmitPromptWarning(
2358 'Dot in log tags cause them to be elided in crash reports.\n' + REF_MSG,
2359 tag_with_dot_errors))
2360
dgn4401aa52015-04-29 16:26:172361 return results
2362
2363
Yoland Yanb92fa522017-08-28 17:37:062364def _CheckAndroidTestJUnitFrameworkImport(input_api, output_api):
2365 """Checks that junit.framework.* is no longer used."""
2366 deprecated_junit_framework_pattern = input_api.re.compile(
2367 r'^import junit\.framework\..*;',
2368 input_api.re.MULTILINE)
2369 sources = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492370 x, white_list=[r'.*\.java$'], black_list=None)
Yoland Yanb92fa522017-08-28 17:37:062371 errors = []
2372 for f in input_api.AffectedFiles(sources):
2373 for line_num, line in f.ChangedContents():
2374 if deprecated_junit_framework_pattern.search(line):
2375 errors.append("%s:%d" % (f.LocalPath(), line_num))
2376
2377 results = []
2378 if errors:
2379 results.append(output_api.PresubmitError(
2380 'APIs from junit.framework.* are deprecated, please use JUnit4 framework'
2381 '(org.junit.*) from //third_party/junit. Contact [email protected]'
2382 ' if you have any question.', errors))
2383 return results
2384
2385
2386def _CheckAndroidTestJUnitInheritance(input_api, output_api):
2387 """Checks that if new Java test classes have inheritance.
2388 Either the new test class is JUnit3 test or it is a JUnit4 test class
2389 with a base class, either case is undesirable.
2390 """
2391 class_declaration_pattern = input_api.re.compile(r'^public class \w*Test ')
2392
2393 sources = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492394 x, white_list=[r'.*Test\.java$'], black_list=None)
Yoland Yanb92fa522017-08-28 17:37:062395 errors = []
2396 for f in input_api.AffectedFiles(sources):
2397 if not f.OldContents():
2398 class_declaration_start_flag = False
2399 for line_num, line in f.ChangedContents():
2400 if class_declaration_pattern.search(line):
2401 class_declaration_start_flag = True
2402 if class_declaration_start_flag and ' extends ' in line:
2403 errors.append('%s:%d' % (f.LocalPath(), line_num))
2404 if '{' in line:
2405 class_declaration_start_flag = False
2406
2407 results = []
2408 if errors:
2409 results.append(output_api.PresubmitPromptWarning(
2410 'The newly created files include Test classes that inherits from base'
2411 ' class. Please do not use inheritance in JUnit4 tests or add new'
2412 ' JUnit3 tests. Contact [email protected] if you have any'
2413 ' questions.', errors))
2414 return results
2415
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202416
yolandyan45001472016-12-21 21:12:422417def _CheckAndroidTestAnnotationUsage(input_api, output_api):
2418 """Checks that android.test.suitebuilder.annotation.* is no longer used."""
2419 deprecated_annotation_import_pattern = input_api.re.compile(
2420 r'^import android\.test\.suitebuilder\.annotation\..*;',
2421 input_api.re.MULTILINE)
2422 sources = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492423 x, white_list=[r'.*\.java$'], black_list=None)
yolandyan45001472016-12-21 21:12:422424 errors = []
2425 for f in input_api.AffectedFiles(sources):
2426 for line_num, line in f.ChangedContents():
2427 if deprecated_annotation_import_pattern.search(line):
2428 errors.append("%s:%d" % (f.LocalPath(), line_num))
2429
2430 results = []
2431 if errors:
2432 results.append(output_api.PresubmitError(
2433 'Annotations in android.test.suitebuilder.annotation have been'
2434 ' deprecated since API level 24. Please use android.support.test.filters'
2435 ' from //third_party/android_support_test_runner:runner_java instead.'
2436 ' Contact [email protected] if you have any questions.', errors))
2437 return results
2438
2439
agrieve7b6479d82015-10-07 14:24:222440def _CheckAndroidNewMdpiAssetLocation(input_api, output_api):
2441 """Checks if MDPI assets are placed in a correct directory."""
2442 file_filter = lambda f: (f.LocalPath().endswith('.png') and
2443 ('/res/drawable/' in f.LocalPath() or
2444 '/res/drawable-ldrtl/' in f.LocalPath()))
2445 errors = []
2446 for f in input_api.AffectedFiles(include_deletes=False,
2447 file_filter=file_filter):
2448 errors.append(' %s' % f.LocalPath())
2449
2450 results = []
2451 if errors:
2452 results.append(output_api.PresubmitError(
2453 'MDPI assets should be placed in /res/drawable-mdpi/ or '
2454 '/res/drawable-ldrtl-mdpi/\ninstead of /res/drawable/ and'
2455 '/res/drawable-ldrtl/.\n'
2456 'Contact [email protected] if you have questions.', errors))
2457 return results
2458
2459
Nate Fischer535972b2017-09-16 01:06:182460def _CheckAndroidWebkitImports(input_api, output_api):
2461 """Checks that code uses org.chromium.base.Callback instead of
2462 android.widget.ValueCallback except in the WebView glue layer.
2463 """
2464 valuecallback_import_pattern = input_api.re.compile(
2465 r'^import android\.webkit\.ValueCallback;$')
2466
2467 errors = []
2468
2469 sources = lambda affected_file: input_api.FilterSourceFile(
2470 affected_file,
2471 black_list=(_EXCLUDED_PATHS +
2472 _TEST_CODE_EXCLUDED_PATHS +
2473 input_api.DEFAULT_BLACK_LIST +
Egor Paskoce145c42018-09-28 19:31:042474 (r'^android_webview[\\/]glue[\\/].*',)),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492475 white_list=[r'.*\.java$'])
Nate Fischer535972b2017-09-16 01:06:182476
2477 for f in input_api.AffectedSourceFiles(sources):
2478 for line_num, line in f.ChangedContents():
2479 if valuecallback_import_pattern.search(line):
2480 errors.append("%s:%d" % (f.LocalPath(), line_num))
2481
2482 results = []
2483
2484 if errors:
2485 results.append(output_api.PresubmitError(
2486 'android.webkit.ValueCallback usage is detected outside of the glue'
2487 ' layer. To stay compatible with the support library, android.webkit.*'
2488 ' classes should only be used inside the glue layer and'
2489 ' org.chromium.base.Callback should be used instead.',
2490 errors))
2491
2492 return results
2493
2494
agrievef32bcc72016-04-04 14:57:402495class PydepsChecker(object):
2496 def __init__(self, input_api, pydeps_files):
2497 self._file_cache = {}
2498 self._input_api = input_api
2499 self._pydeps_files = pydeps_files
2500
2501 def _LoadFile(self, path):
2502 """Returns the list of paths within a .pydeps file relative to //."""
2503 if path not in self._file_cache:
2504 with open(path) as f:
2505 self._file_cache[path] = f.read()
2506 return self._file_cache[path]
2507
2508 def _ComputeNormalizedPydepsEntries(self, pydeps_path):
2509 """Returns an interable of paths within the .pydep, relativized to //."""
2510 os_path = self._input_api.os_path
2511 pydeps_dir = os_path.dirname(pydeps_path)
2512 entries = (l.rstrip() for l in self._LoadFile(pydeps_path).splitlines()
2513 if not l.startswith('*'))
2514 return (os_path.normpath(os_path.join(pydeps_dir, e)) for e in entries)
2515
2516 def _CreateFilesToPydepsMap(self):
2517 """Returns a map of local_path -> list_of_pydeps."""
2518 ret = {}
2519 for pydep_local_path in self._pydeps_files:
2520 for path in self._ComputeNormalizedPydepsEntries(pydep_local_path):
2521 ret.setdefault(path, []).append(pydep_local_path)
2522 return ret
2523
2524 def ComputeAffectedPydeps(self):
2525 """Returns an iterable of .pydeps files that might need regenerating."""
2526 affected_pydeps = set()
2527 file_to_pydeps_map = None
2528 for f in self._input_api.AffectedFiles(include_deletes=True):
2529 local_path = f.LocalPath()
2530 if local_path == 'DEPS':
2531 return self._pydeps_files
2532 elif local_path.endswith('.pydeps'):
2533 if local_path in self._pydeps_files:
2534 affected_pydeps.add(local_path)
2535 elif local_path.endswith('.py'):
2536 if file_to_pydeps_map is None:
2537 file_to_pydeps_map = self._CreateFilesToPydepsMap()
2538 affected_pydeps.update(file_to_pydeps_map.get(local_path, ()))
2539 return affected_pydeps
2540
2541 def DetermineIfStale(self, pydeps_path):
2542 """Runs print_python_deps.py to see if the files is stale."""
phajdan.jr0d9878552016-11-04 10:49:412543 import difflib
John Budorick47ca3fe2018-02-10 00:53:102544 import os
2545
agrievef32bcc72016-04-04 14:57:402546 old_pydeps_data = self._LoadFile(pydeps_path).splitlines()
2547 cmd = old_pydeps_data[1][1:].strip()
John Budorick47ca3fe2018-02-10 00:53:102548 env = dict(os.environ)
2549 env['PYTHONDONTWRITEBYTECODE'] = '1'
agrievef32bcc72016-04-04 14:57:402550 new_pydeps_data = self._input_api.subprocess.check_output(
John Budorick47ca3fe2018-02-10 00:53:102551 cmd + ' --output ""', shell=True, env=env)
phajdan.jr0d9878552016-11-04 10:49:412552 old_contents = old_pydeps_data[2:]
2553 new_contents = new_pydeps_data.splitlines()[2:]
agrievef32bcc72016-04-04 14:57:402554 if old_pydeps_data[2:] != new_pydeps_data.splitlines()[2:]:
phajdan.jr0d9878552016-11-04 10:49:412555 return cmd, '\n'.join(difflib.context_diff(old_contents, new_contents))
agrievef32bcc72016-04-04 14:57:402556
2557
2558def _CheckPydepsNeedsUpdating(input_api, output_api, checker_for_tests=None):
2559 """Checks if a .pydeps file needs to be regenerated."""
John Chencde89192018-01-27 21:18:402560 # This check is for Python dependency lists (.pydeps files), and involves
2561 # paths not only in the PRESUBMIT.py, but also in the .pydeps files. It
2562 # doesn't work on Windows and Mac, so skip it on other platforms.
agrieve9bc4200b2016-05-04 16:33:282563 if input_api.platform != 'linux2':
agrievebb9c5b472016-04-22 15:13:002564 return []
Mostyn Bramley-Moore6b427322017-12-21 22:11:022565 # TODO(agrieve): Update when there's a better way to detect
2566 # this: crbug.com/570091
agrievef32bcc72016-04-04 14:57:402567 is_android = input_api.os_path.exists('third_party/android_tools')
2568 pydeps_files = _ALL_PYDEPS_FILES if is_android else _GENERIC_PYDEPS_FILES
2569 results = []
2570 # First, check for new / deleted .pydeps.
2571 for f in input_api.AffectedFiles(include_deletes=True):
Zhiling Huang45cabf32018-03-10 00:50:032572 # Check whether we are running the presubmit check for a file in src.
2573 # f.LocalPath is relative to repo (src, or internal repo).
2574 # os_path.exists is relative to src repo.
2575 # Therefore if os_path.exists is true, it means f.LocalPath is relative
2576 # to src and we can conclude that the pydeps is in src.
2577 if input_api.os_path.exists(f.LocalPath()):
2578 if f.LocalPath().endswith('.pydeps'):
2579 if f.Action() == 'D' and f.LocalPath() in _ALL_PYDEPS_FILES:
2580 results.append(output_api.PresubmitError(
2581 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to '
2582 'remove %s' % f.LocalPath()))
2583 elif f.Action() != 'D' and f.LocalPath() not in _ALL_PYDEPS_FILES:
2584 results.append(output_api.PresubmitError(
2585 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to '
2586 'include %s' % f.LocalPath()))
agrievef32bcc72016-04-04 14:57:402587
2588 if results:
2589 return results
2590
2591 checker = checker_for_tests or PydepsChecker(input_api, pydeps_files)
2592
2593 for pydep_path in checker.ComputeAffectedPydeps():
2594 try:
phajdan.jr0d9878552016-11-04 10:49:412595 result = checker.DetermineIfStale(pydep_path)
2596 if result:
2597 cmd, diff = result
agrievef32bcc72016-04-04 14:57:402598 results.append(output_api.PresubmitError(
phajdan.jr0d9878552016-11-04 10:49:412599 'File is stale: %s\nDiff (apply to fix):\n%s\n'
2600 'To regenerate, run:\n\n %s' %
2601 (pydep_path, diff, cmd)))
agrievef32bcc72016-04-04 14:57:402602 except input_api.subprocess.CalledProcessError as error:
2603 return [output_api.PresubmitError('Error running: %s' % error.cmd,
2604 long_text=error.output)]
2605
2606 return results
2607
2608
glidere61efad2015-02-18 17:39:432609def _CheckSingletonInHeaders(input_api, output_api):
2610 """Checks to make sure no header files have |Singleton<|."""
2611 def FileFilter(affected_file):
2612 # It's ok for base/memory/singleton.h to have |Singleton<|.
2613 black_list = (_EXCLUDED_PATHS +
2614 input_api.DEFAULT_BLACK_LIST +
Egor Paskoce145c42018-09-28 19:31:042615 (r"^base[\\/]memory[\\/]singleton\.h$",
2616 r"^net[\\/]quic[\\/]platform[\\/]impl[\\/]"
Michael Warrese4451492018-03-07 04:42:472617 r"quic_singleton_impl\.h$"))
glidere61efad2015-02-18 17:39:432618 return input_api.FilterSourceFile(affected_file, black_list=black_list)
2619
sergeyu34d21222015-09-16 00:11:442620 pattern = input_api.re.compile(r'(?<!class\sbase::)Singleton\s*<')
glidere61efad2015-02-18 17:39:432621 files = []
2622 for f in input_api.AffectedSourceFiles(FileFilter):
2623 if (f.LocalPath().endswith('.h') or f.LocalPath().endswith('.hxx') or
2624 f.LocalPath().endswith('.hpp') or f.LocalPath().endswith('.inl')):
2625 contents = input_api.ReadFile(f)
2626 for line in contents.splitlines(False):
oysteinec430ad42015-10-22 20:55:242627 if (not line.lstrip().startswith('//') and # Strip C++ comment.
glidere61efad2015-02-18 17:39:432628 pattern.search(line)):
2629 files.append(f)
2630 break
2631
2632 if files:
yolandyandaabc6d2016-04-18 18:29:392633 return [output_api.PresubmitError(
sergeyu34d21222015-09-16 00:11:442634 'Found base::Singleton<T> in the following header files.\n' +
glidere61efad2015-02-18 17:39:432635 'Please move them to an appropriate source file so that the ' +
2636 'template gets instantiated in a single compilation unit.',
2637 files) ]
2638 return []
2639
2640
[email protected]fd20b902014-05-09 02:14:532641_DEPRECATED_CSS = [
2642 # Values
2643 ( "-webkit-box", "flex" ),
2644 ( "-webkit-inline-box", "inline-flex" ),
2645 ( "-webkit-flex", "flex" ),
2646 ( "-webkit-inline-flex", "inline-flex" ),
2647 ( "-webkit-min-content", "min-content" ),
2648 ( "-webkit-max-content", "max-content" ),
2649
2650 # Properties
2651 ( "-webkit-background-clip", "background-clip" ),
2652 ( "-webkit-background-origin", "background-origin" ),
2653 ( "-webkit-background-size", "background-size" ),
2654 ( "-webkit-box-shadow", "box-shadow" ),
dbeam6936c67f2017-01-19 01:51:442655 ( "-webkit-user-select", "user-select" ),
[email protected]fd20b902014-05-09 02:14:532656
2657 # Functions
2658 ( "-webkit-gradient", "gradient" ),
2659 ( "-webkit-repeating-gradient", "repeating-gradient" ),
2660 ( "-webkit-linear-gradient", "linear-gradient" ),
2661 ( "-webkit-repeating-linear-gradient", "repeating-linear-gradient" ),
2662 ( "-webkit-radial-gradient", "radial-gradient" ),
2663 ( "-webkit-repeating-radial-gradient", "repeating-radial-gradient" ),
2664]
2665
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202666
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492667# TODO: add unit tests
dbeam1ec68ac2016-12-15 05:22:242668def _CheckNoDeprecatedCss(input_api, output_api):
[email protected]fd20b902014-05-09 02:14:532669 """ Make sure that we don't use deprecated CSS
[email protected]9a48e3f82014-05-22 00:06:252670 properties, functions or values. Our external
mdjonesae0286c32015-06-10 18:10:342671 documentation and iOS CSS for dom distiller
2672 (reader mode) are ignored by the hooks as it
[email protected]9a48e3f82014-05-22 00:06:252673 needs to be consumed by WebKit. """
[email protected]fd20b902014-05-09 02:14:532674 results = []
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492675 file_inclusion_pattern = [r".+\.css$"]
[email protected]9a48e3f82014-05-22 00:06:252676 black_list = (_EXCLUDED_PATHS +
2677 _TEST_CODE_EXCLUDED_PATHS +
2678 input_api.DEFAULT_BLACK_LIST +
2679 (r"^chrome/common/extensions/docs",
2680 r"^chrome/docs",
mdjonesae0286c32015-06-10 18:10:342681 r"^components/dom_distiller/core/css/distilledpage_ios.css",
sdefresne6308d7f2016-02-15 09:38:442682 r"^components/neterror/resources/neterror.css",
[email protected]9a48e3f82014-05-22 00:06:252683 r"^native_client_sdk"))
2684 file_filter = lambda f: input_api.FilterSourceFile(
2685 f, white_list=file_inclusion_pattern, black_list=black_list)
[email protected]fd20b902014-05-09 02:14:532686 for fpath in input_api.AffectedFiles(file_filter=file_filter):
2687 for line_num, line in fpath.ChangedContents():
2688 for (deprecated_value, value) in _DEPRECATED_CSS:
dbeam070cfe62014-10-22 06:44:022689 if deprecated_value in line:
[email protected]fd20b902014-05-09 02:14:532690 results.append(output_api.PresubmitError(
2691 "%s:%d: Use of deprecated CSS %s, use %s instead" %
2692 (fpath.LocalPath(), line_num, deprecated_value, value)))
2693 return results
2694
mohan.reddyf21db962014-10-16 12:26:472695
dbeam070cfe62014-10-22 06:44:022696_DEPRECATED_JS = [
2697 ( "__lookupGetter__", "Object.getOwnPropertyDescriptor" ),
2698 ( "__defineGetter__", "Object.defineProperty" ),
2699 ( "__defineSetter__", "Object.defineProperty" ),
2700]
2701
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202702
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492703# TODO: add unit tests
dbeam1ec68ac2016-12-15 05:22:242704def _CheckNoDeprecatedJs(input_api, output_api):
dbeam070cfe62014-10-22 06:44:022705 """Make sure that we don't use deprecated JS in Chrome code."""
2706 results = []
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492707 file_inclusion_pattern = [r".+\.js$"] # TODO(dbeam): .html?
dbeam070cfe62014-10-22 06:44:022708 black_list = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
2709 input_api.DEFAULT_BLACK_LIST)
2710 file_filter = lambda f: input_api.FilterSourceFile(
2711 f, white_list=file_inclusion_pattern, black_list=black_list)
2712 for fpath in input_api.AffectedFiles(file_filter=file_filter):
2713 for lnum, line in fpath.ChangedContents():
2714 for (deprecated, replacement) in _DEPRECATED_JS:
2715 if deprecated in line:
2716 results.append(output_api.PresubmitError(
2717 "%s:%d: Use of deprecated JS %s, use %s instead" %
2718 (fpath.LocalPath(), lnum, deprecated, replacement)))
2719 return results
2720
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202721
rlanday6802cf632017-05-30 17:48:362722def _CheckForRelativeIncludes(input_api, output_api):
2723 # Need to set the sys.path so PRESUBMIT_test.py runs properly
2724 import sys
2725 original_sys_path = sys.path
2726 try:
2727 sys.path = sys.path + [input_api.os_path.join(
2728 input_api.PresubmitLocalPath(), 'buildtools', 'checkdeps')]
2729 from cpp_checker import CppChecker
2730 finally:
2731 # Restore sys.path to what it was before.
2732 sys.path = original_sys_path
2733
2734 bad_files = {}
2735 for f in input_api.AffectedFiles(include_deletes=False):
2736 if (f.LocalPath().startswith('third_party') and
2737 not f.LocalPath().startswith('third_party/WebKit') and
2738 not f.LocalPath().startswith('third_party\\WebKit')):
2739 continue
2740
2741 if not CppChecker.IsCppFile(f.LocalPath()):
2742 continue
2743
Vaclav Brozekd5de76a2018-03-17 07:57:502744 relative_includes = [line for _, line in f.ChangedContents()
rlanday6802cf632017-05-30 17:48:362745 if "#include" in line and "../" in line]
2746 if not relative_includes:
2747 continue
2748 bad_files[f.LocalPath()] = relative_includes
2749
2750 if not bad_files:
2751 return []
2752
2753 error_descriptions = []
2754 for file_path, bad_lines in bad_files.iteritems():
2755 error_description = file_path
2756 for line in bad_lines:
2757 error_description += '\n ' + line
2758 error_descriptions.append(error_description)
2759
2760 results = []
2761 results.append(output_api.PresubmitError(
2762 'You added one or more relative #include paths (including "../").\n'
2763 'These shouldn\'t be used because they can be used to include headers\n'
2764 'from code that\'s not correctly specified as a dependency in the\n'
2765 'relevant BUILD.gn file(s).',
2766 error_descriptions))
2767
2768 return results
2769
Takeshi Yoshinoe387aa32017-08-02 13:16:132770
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202771def _CheckWatchlistDefinitionsEntrySyntax(key, value, ast):
2772 if not isinstance(key, ast.Str):
2773 return 'Key at line %d must be a string literal' % key.lineno
2774 if not isinstance(value, ast.Dict):
2775 return 'Value at line %d must be a dict' % value.lineno
2776 if len(value.keys) != 1:
2777 return 'Dict at line %d must have single entry' % value.lineno
2778 if not isinstance(value.keys[0], ast.Str) or value.keys[0].s != 'filepath':
2779 return (
2780 'Entry at line %d must have a string literal \'filepath\' as key' %
2781 value.lineno)
2782 return None
Takeshi Yoshinoe387aa32017-08-02 13:16:132783
Takeshi Yoshinoe387aa32017-08-02 13:16:132784
Sergey Ulanov4af16052018-11-08 02:41:462785def _CheckWatchlistsEntrySyntax(key, value, ast, email_regex):
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202786 if not isinstance(key, ast.Str):
2787 return 'Key at line %d must be a string literal' % key.lineno
2788 if not isinstance(value, ast.List):
2789 return 'Value at line %d must be a list' % value.lineno
Sergey Ulanov4af16052018-11-08 02:41:462790 for element in value.elts:
2791 if not isinstance(element, ast.Str):
2792 return 'Watchlist elements on line %d is not a string' % key.lineno
2793 if not email_regex.match(element.s):
2794 return ('Watchlist element on line %d doesn\'t look like a valid ' +
2795 'email: %s') % (key.lineno, element.s)
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202796 return None
Takeshi Yoshinoe387aa32017-08-02 13:16:132797
Takeshi Yoshinoe387aa32017-08-02 13:16:132798
Sergey Ulanov4af16052018-11-08 02:41:462799def _CheckWATCHLISTSEntries(wd_dict, w_dict, input_api):
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202800 mismatch_template = (
2801 'Mismatch between WATCHLIST_DEFINITIONS entry (%s) and WATCHLISTS '
2802 'entry (%s)')
Takeshi Yoshinoe387aa32017-08-02 13:16:132803
Sergey Ulanov4af16052018-11-08 02:41:462804 email_regex = input_api.re.compile(
2805 r"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+$")
2806
2807 ast = input_api.ast
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202808 i = 0
2809 last_key = ''
2810 while True:
2811 if i >= len(wd_dict.keys):
2812 if i >= len(w_dict.keys):
2813 return None
2814 return mismatch_template % ('missing', 'line %d' % w_dict.keys[i].lineno)
2815 elif i >= len(w_dict.keys):
2816 return (
2817 mismatch_template % ('line %d' % wd_dict.keys[i].lineno, 'missing'))
Takeshi Yoshinoe387aa32017-08-02 13:16:132818
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202819 wd_key = wd_dict.keys[i]
2820 w_key = w_dict.keys[i]
Takeshi Yoshinoe387aa32017-08-02 13:16:132821
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202822 result = _CheckWatchlistDefinitionsEntrySyntax(
2823 wd_key, wd_dict.values[i], ast)
2824 if result is not None:
2825 return 'Bad entry in WATCHLIST_DEFINITIONS dict: %s' % result
Takeshi Yoshinoe387aa32017-08-02 13:16:132826
Sergey Ulanov4af16052018-11-08 02:41:462827 result = _CheckWatchlistsEntrySyntax(
2828 w_key, w_dict.values[i], ast, email_regex)
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202829 if result is not None:
2830 return 'Bad entry in WATCHLISTS dict: %s' % result
2831
2832 if wd_key.s != w_key.s:
2833 return mismatch_template % (
2834 '%s at line %d' % (wd_key.s, wd_key.lineno),
2835 '%s at line %d' % (w_key.s, w_key.lineno))
2836
2837 if wd_key.s < last_key:
2838 return (
2839 'WATCHLISTS dict is not sorted lexicographically at line %d and %d' %
2840 (wd_key.lineno, w_key.lineno))
2841 last_key = wd_key.s
2842
2843 i = i + 1
2844
2845
Sergey Ulanov4af16052018-11-08 02:41:462846def _CheckWATCHLISTSSyntax(expression, input_api):
2847 ast = input_api.ast
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202848 if not isinstance(expression, ast.Expression):
2849 return 'WATCHLISTS file must contain a valid expression'
2850 dictionary = expression.body
2851 if not isinstance(dictionary, ast.Dict) or len(dictionary.keys) != 2:
2852 return 'WATCHLISTS file must have single dict with exactly two entries'
2853
2854 first_key = dictionary.keys[0]
2855 first_value = dictionary.values[0]
2856 second_key = dictionary.keys[1]
2857 second_value = dictionary.values[1]
2858
2859 if (not isinstance(first_key, ast.Str) or
2860 first_key.s != 'WATCHLIST_DEFINITIONS' or
2861 not isinstance(first_value, ast.Dict)):
2862 return (
2863 'The first entry of the dict in WATCHLISTS file must be '
2864 'WATCHLIST_DEFINITIONS dict')
2865
2866 if (not isinstance(second_key, ast.Str) or
2867 second_key.s != 'WATCHLISTS' or
2868 not isinstance(second_value, ast.Dict)):
2869 return (
2870 'The second entry of the dict in WATCHLISTS file must be '
2871 'WATCHLISTS dict')
2872
Sergey Ulanov4af16052018-11-08 02:41:462873 return _CheckWATCHLISTSEntries(first_value, second_value, input_api)
Takeshi Yoshinoe387aa32017-08-02 13:16:132874
2875
2876def _CheckWATCHLISTS(input_api, output_api):
2877 for f in input_api.AffectedFiles(include_deletes=False):
2878 if f.LocalPath() == 'WATCHLISTS':
2879 contents = input_api.ReadFile(f, 'r')
2880
2881 try:
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202882 # First, make sure that it can be evaluated.
Takeshi Yoshinoe387aa32017-08-02 13:16:132883 input_api.ast.literal_eval(contents)
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202884 # Get an AST tree for it and scan the tree for detailed style checking.
2885 expression = input_api.ast.parse(
2886 contents, filename='WATCHLISTS', mode='eval')
2887 except ValueError as e:
2888 return [output_api.PresubmitError(
2889 'Cannot parse WATCHLISTS file', long_text=repr(e))]
2890 except SyntaxError as e:
2891 return [output_api.PresubmitError(
2892 'Cannot parse WATCHLISTS file', long_text=repr(e))]
2893 except TypeError as e:
2894 return [output_api.PresubmitError(
2895 'Cannot parse WATCHLISTS file', long_text=repr(e))]
Takeshi Yoshinoe387aa32017-08-02 13:16:132896
Sergey Ulanov4af16052018-11-08 02:41:462897 result = _CheckWATCHLISTSSyntax(expression, input_api)
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202898 if result is not None:
2899 return [output_api.PresubmitError(result)]
2900 break
Takeshi Yoshinoe387aa32017-08-02 13:16:132901
2902 return []
2903
2904
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:192905def _CheckNewHeaderWithoutGnChange(input_api, output_api):
2906 """Checks that newly added header files have corresponding GN changes.
2907 Note that this is only a heuristic. To be precise, run script:
2908 build/check_gn_headers.py.
2909 """
2910
2911 def headers(f):
2912 return input_api.FilterSourceFile(
2913 f, white_list=(r'.+%s' % _HEADER_EXTENSIONS, ))
2914
2915 new_headers = []
2916 for f in input_api.AffectedSourceFiles(headers):
2917 if f.Action() != 'A':
2918 continue
2919 new_headers.append(f.LocalPath())
2920
2921 def gn_files(f):
2922 return input_api.FilterSourceFile(f, white_list=(r'.+\.gn', ))
2923
2924 all_gn_changed_contents = ''
2925 for f in input_api.AffectedSourceFiles(gn_files):
2926 for _, line in f.ChangedContents():
2927 all_gn_changed_contents += line
2928
2929 problems = []
2930 for header in new_headers:
2931 basename = input_api.os_path.basename(header)
2932 if basename not in all_gn_changed_contents:
2933 problems.append(header)
2934
2935 if problems:
2936 return [output_api.PresubmitPromptWarning(
2937 'Missing GN changes for new header files', items=sorted(problems),
2938 long_text='Please double check whether newly added header files need '
2939 'corresponding changes in gn or gni files.\nThis checking is only a '
2940 'heuristic. Run build/check_gn_headers.py to be precise.\n'
2941 'Read https://crbug.com/661774 for more info.')]
2942 return []
2943
2944
Michael Giuffridad3bc8672018-10-25 22:48:022945def _CheckCorrectProductNameInMessages(input_api, output_api):
2946 """Check that Chromium-branded strings don't include "Chrome" or vice versa.
2947
2948 This assumes we won't intentionally reference one product from the other
2949 product.
2950 """
2951 all_problems = []
2952 test_cases = [{
2953 "filename_postfix": "google_chrome_strings.grd",
2954 "correct_name": "Chrome",
2955 "incorrect_name": "Chromium",
2956 }, {
2957 "filename_postfix": "chromium_strings.grd",
2958 "correct_name": "Chromium",
2959 "incorrect_name": "Chrome",
2960 }]
2961
2962 for test_case in test_cases:
2963 problems = []
2964 filename_filter = lambda x: x.LocalPath().endswith(
2965 test_case["filename_postfix"])
2966
2967 # Check each new line. Can yield false positives in multiline comments, but
2968 # easier than trying to parse the XML because messages can have nested
2969 # children, and associating message elements with affected lines is hard.
2970 for f in input_api.AffectedSourceFiles(filename_filter):
2971 for line_num, line in f.ChangedContents():
2972 if "<message" in line or "<!--" in line or "-->" in line:
2973 continue
2974 if test_case["incorrect_name"] in line:
2975 problems.append(
2976 "Incorrect product name in %s:%d" % (f.LocalPath(), line_num))
2977
2978 if problems:
2979 message = (
2980 "Strings in %s-branded string files should reference \"%s\", not \"%s\""
2981 % (test_case["correct_name"], test_case["correct_name"],
2982 test_case["incorrect_name"]))
2983 all_problems.append(
2984 output_api.PresubmitPromptWarning(message, items=problems))
2985
2986 return all_problems
2987
2988
dgnaa68d5e2015-06-10 10:08:222989def _AndroidSpecificOnUploadChecks(input_api, output_api):
2990 """Groups checks that target android code."""
2991 results = []
dgnaa68d5e2015-06-10 10:08:222992 results.extend(_CheckAndroidCrLogUsage(input_api, output_api))
agrieve7b6479d82015-10-07 14:24:222993 results.extend(_CheckAndroidNewMdpiAssetLocation(input_api, output_api))
dskiba88634f4e2015-08-14 23:03:292994 results.extend(_CheckAndroidToastUsage(input_api, output_api))
Yoland Yanb92fa522017-08-28 17:37:062995 results.extend(_CheckAndroidTestJUnitInheritance(input_api, output_api))
2996 results.extend(_CheckAndroidTestJUnitFrameworkImport(input_api, output_api))
yolandyan45001472016-12-21 21:12:422997 results.extend(_CheckAndroidTestAnnotationUsage(input_api, output_api))
Nate Fischer535972b2017-09-16 01:06:182998 results.extend(_CheckAndroidWebkitImports(input_api, output_api))
dgnaa68d5e2015-06-10 10:08:222999 return results
3000
3001
[email protected]22c9bd72011-03-27 16:47:393002def _CommonChecks(input_api, output_api):
3003 """Checks common to both upload and commit."""
3004 results = []
3005 results.extend(input_api.canned_checks.PanProjectChecks(
[email protected]3de922f2013-12-20 13:27:383006 input_api, output_api,
qyearsleyfa2cfcf82016-12-15 18:03:543007 excluded_paths=_EXCLUDED_PATHS))
Eric Boren6fd2b932018-01-25 15:05:083008
3009 author = input_api.change.author_email
3010 if author and author not in _KNOWN_ROBOTS:
3011 results.extend(
3012 input_api.canned_checks.CheckAuthorizedAuthor(input_api, output_api))
3013
[email protected]55459852011-08-10 15:17:193014 results.extend(
[email protected]760deea2013-12-10 19:33:493015 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
Vaclav Brozek7dbc28c2018-03-27 08:35:233016 results.extend(
3017 _CheckNoProductionCodeUsingTestOnlyFunctionsJava(input_api, output_api))
[email protected]10689ca2011-09-02 02:31:543018 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
[email protected]72df4e782012-06-21 16:28:183019 results.extend(_CheckNoUNIT_TESTInSourceFiles(input_api, output_api))
Dominic Battre033531052018-09-24 15:45:343020 results.extend(_CheckNoDISABLETypoInTests(input_api, output_api))
danakj61c1aa22015-10-26 19:55:523021 results.extend(_CheckDCHECK_IS_ONHasBraces(input_api, output_api))
[email protected]8ea5d4b2011-09-13 21:49:223022 results.extend(_CheckNoNewWStrings(input_api, output_api))
[email protected]2a8ac9c2011-10-19 17:20:443023 results.extend(_CheckNoDEPSGIT(input_api, output_api))
[email protected]127f18ec2012-06-16 05:05:593024 results.extend(_CheckNoBannedFunctions(input_api, output_api))
[email protected]6c063c62012-07-11 19:11:063025 results.extend(_CheckNoPragmaOnce(input_api, output_api))
[email protected]e7479052012-09-19 00:26:123026 results.extend(_CheckNoTrinaryTrueFalse(input_api, output_api))
[email protected]55f9f382012-07-31 11:02:183027 results.extend(_CheckUnwantedDependencies(input_api, output_api))
[email protected]fbcafe5a2012-08-08 15:31:223028 results.extend(_CheckFilePermissions(input_api, output_api))
robertocn832f5992017-01-04 19:01:303029 results.extend(_CheckTeamTags(input_api, output_api))
[email protected]c8278b32012-10-30 20:35:493030 results.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api, output_api))
[email protected]70ca77752012-11-20 03:45:033031 results.extend(_CheckForVersionControlConflicts(input_api, output_api))
[email protected]b8079ae4a2012-12-05 19:56:493032 results.extend(_CheckPatchFiles(input_api, output_api))
[email protected]06e6d0ff2012-12-11 01:36:443033 results.extend(_CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api))
[email protected]d2530012013-01-25 16:39:273034 results.extend(_CheckNoAbbreviationInPngFileName(input_api, output_api))
Kent Tamura5a8755d2017-06-29 23:37:073035 results.extend(_CheckBuildConfigMacrosWithoutInclude(input_api, output_api))
[email protected]b00342e7f2013-03-26 16:21:543036 results.extend(_CheckForInvalidOSMacros(input_api, output_api))
lliabraa35bab3932014-10-01 12:16:443037 results.extend(_CheckForInvalidIfDefinedMacros(input_api, output_api))
yolandyandaabc6d2016-04-18 18:29:393038 results.extend(_CheckFlakyTestUsage(input_api, output_api))
[email protected]e871964c2013-05-13 14:14:553039 results.extend(_CheckAddedDepsHaveTargetApprovals(input_api, output_api))
[email protected]9f919cc2013-07-31 03:04:043040 results.extend(
3041 input_api.canned_checks.CheckChangeHasNoTabs(
3042 input_api,
3043 output_api,
3044 source_file_filter=lambda x: x.LocalPath().endswith('.grd')))
[email protected]85218562013-11-22 07:41:403045 results.extend(_CheckSpamLogging(input_api, output_api))
[email protected]49aa76a2013-12-04 06:59:163046 results.extend(_CheckForAnonymousVariables(input_api, output_api))
[email protected]999261d2014-03-03 20:08:083047 results.extend(_CheckUserActionUpdate(input_api, output_api))
dbeam1ec68ac2016-12-15 05:22:243048 results.extend(_CheckNoDeprecatedCss(input_api, output_api))
3049 results.extend(_CheckNoDeprecatedJs(input_api, output_api))
[email protected]99171a92014-06-03 08:44:473050 results.extend(_CheckParseErrors(input_api, output_api))
mlamouria82272622014-09-16 18:45:043051 results.extend(_CheckForIPCRules(input_api, output_api))
Stephen Martinis97a394142018-06-07 23:06:053052 results.extend(_CheckForLongPathnames(input_api, output_api))
Daniel Bratell8ba52722018-03-02 16:06:143053 results.extend(_CheckForIncludeGuards(input_api, output_api))
mostynbb639aca52015-01-07 20:31:233054 results.extend(_CheckForWindowsLineEndings(input_api, output_api))
glidere61efad2015-02-18 17:39:433055 results.extend(_CheckSingletonInHeaders(input_api, output_api))
agrievef32bcc72016-04-04 14:57:403056 results.extend(_CheckPydepsNeedsUpdating(input_api, output_api))
wnwenbdc444e2016-05-25 13:44:153057 results.extend(_CheckJavaStyle(input_api, output_api))
dchenge07de812016-06-20 19:27:173058 results.extend(_CheckIpcOwners(input_api, output_api))
jbriance9e12f162016-11-25 07:57:503059 results.extend(_CheckUselessForwardDeclarations(input_api, output_api))
rlanday6802cf632017-05-30 17:48:363060 results.extend(_CheckForRelativeIncludes(input_api, output_api))
Takeshi Yoshinoe387aa32017-08-02 13:16:133061 results.extend(_CheckWATCHLISTS(input_api, output_api))
Sergiy Byelozyorov366b6482017-11-06 18:20:433062 results.extend(input_api.RunTests(
3063 input_api.canned_checks.CheckVPythonSpec(input_api, output_api)))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143064 results.extend(_CheckTranslationScreenshots(input_api, output_api))
Michael Giuffridad3bc8672018-10-25 22:48:023065 results.extend(_CheckCorrectProductNameInMessages(input_api, output_api))
[email protected]2299dcf2012-11-15 19:56:243066
Vaclav Brozekcdc7defb2018-03-20 09:54:353067 for f in input_api.AffectedFiles():
3068 path, name = input_api.os_path.split(f.LocalPath())
3069 if name == 'PRESUBMIT.py':
3070 full_path = input_api.os_path.join(input_api.PresubmitLocalPath(), path)
Caleb Rouleaua6117be2018-05-11 20:10:003071 test_file = input_api.os_path.join(path, 'PRESUBMIT_test.py')
3072 if f.Action() != 'D' and input_api.os_path.exists(test_file):
Dirk Pranke38557312018-04-18 00:53:073073 # The PRESUBMIT.py file (and the directory containing it) might
3074 # have been affected by being moved or removed, so only try to
3075 # run the tests if they still exist.
3076 results.extend(input_api.canned_checks.RunUnitTestsInDirectory(
3077 input_api, output_api, full_path,
3078 whitelist=[r'^PRESUBMIT_test\.py$']))
[email protected]22c9bd72011-03-27 16:47:393079 return results
[email protected]1f7b4172010-01-28 01:17:343080
[email protected]b337cb5b2011-01-23 21:24:053081
[email protected]b8079ae4a2012-12-05 19:56:493082def _CheckPatchFiles(input_api, output_api):
3083 problems = [f.LocalPath() for f in input_api.AffectedFiles()
3084 if f.LocalPath().endswith(('.orig', '.rej'))]
3085 if problems:
3086 return [output_api.PresubmitError(
3087 "Don't commit .rej and .orig files.", problems)]
[email protected]2fdd1f362013-01-16 03:56:033088 else:
3089 return []
[email protected]b8079ae4a2012-12-05 19:56:493090
3091
Kent Tamura5a8755d2017-06-29 23:37:073092def _CheckBuildConfigMacrosWithoutInclude(input_api, output_api):
Kent Tamura79ef8f82017-07-18 00:00:213093 # Excludes OS_CHROMEOS, which is not defined in build_config.h.
3094 macro_re = input_api.re.compile(r'^\s*#(el)?if.*\bdefined\(((OS_(?!CHROMEOS)|'
3095 'COMPILER_|ARCH_CPU_|WCHAR_T_IS_)[^)]*)')
Kent Tamura5a8755d2017-06-29 23:37:073096 include_re = input_api.re.compile(
3097 r'^#include\s+"build/build_config.h"', input_api.re.MULTILINE)
3098 extension_re = input_api.re.compile(r'\.[a-z]+$')
3099 errors = []
3100 for f in input_api.AffectedFiles():
3101 if not f.LocalPath().endswith(('.h', '.c', '.cc', '.cpp', '.m', '.mm')):
3102 continue
3103 found_line_number = None
3104 found_macro = None
3105 for line_num, line in f.ChangedContents():
3106 match = macro_re.search(line)
3107 if match:
3108 found_line_number = line_num
3109 found_macro = match.group(2)
3110 break
3111 if not found_line_number:
3112 continue
3113
3114 found_include = False
3115 for line in f.NewContents():
3116 if include_re.search(line):
3117 found_include = True
3118 break
3119 if found_include:
3120 continue
3121
3122 if not f.LocalPath().endswith('.h'):
3123 primary_header_path = extension_re.sub('.h', f.AbsoluteLocalPath())
3124 try:
3125 content = input_api.ReadFile(primary_header_path, 'r')
3126 if include_re.search(content):
3127 continue
3128 except IOError:
3129 pass
3130 errors.append('%s:%d %s macro is used without including build/'
3131 'build_config.h.'
3132 % (f.LocalPath(), found_line_number, found_macro))
3133 if errors:
3134 return [output_api.PresubmitPromptWarning('\n'.join(errors))]
3135 return []
3136
3137
[email protected]b00342e7f2013-03-26 16:21:543138def _DidYouMeanOSMacro(bad_macro):
3139 try:
3140 return {'A': 'OS_ANDROID',
3141 'B': 'OS_BSD',
3142 'C': 'OS_CHROMEOS',
3143 'F': 'OS_FREEBSD',
3144 'L': 'OS_LINUX',
3145 'M': 'OS_MACOSX',
3146 'N': 'OS_NACL',
3147 'O': 'OS_OPENBSD',
3148 'P': 'OS_POSIX',
3149 'S': 'OS_SOLARIS',
3150 'W': 'OS_WIN'}[bad_macro[3].upper()]
3151 except KeyError:
3152 return ''
3153
3154
3155def _CheckForInvalidOSMacrosInFile(input_api, f):
3156 """Check for sensible looking, totally invalid OS macros."""
3157 preprocessor_statement = input_api.re.compile(r'^\s*#')
3158 os_macro = input_api.re.compile(r'defined\((OS_[^)]+)\)')
3159 results = []
3160 for lnum, line in f.ChangedContents():
3161 if preprocessor_statement.search(line):
3162 for match in os_macro.finditer(line):
3163 if not match.group(1) in _VALID_OS_MACROS:
3164 good = _DidYouMeanOSMacro(match.group(1))
3165 did_you_mean = ' (did you mean %s?)' % good if good else ''
3166 results.append(' %s:%d %s%s' % (f.LocalPath(),
3167 lnum,
3168 match.group(1),
3169 did_you_mean))
3170 return results
3171
3172
3173def _CheckForInvalidOSMacros(input_api, output_api):
3174 """Check all affected files for invalid OS macros."""
3175 bad_macros = []
3176 for f in input_api.AffectedFiles():
ellyjones47654342016-05-06 15:50:473177 if not f.LocalPath().endswith(('.py', '.js', '.html', '.css', '.md')):
[email protected]b00342e7f2013-03-26 16:21:543178 bad_macros.extend(_CheckForInvalidOSMacrosInFile(input_api, f))
3179
3180 if not bad_macros:
3181 return []
3182
3183 return [output_api.PresubmitError(
3184 'Possibly invalid OS macro[s] found. Please fix your code\n'
3185 'or add your macro to src/PRESUBMIT.py.', bad_macros)]
3186
lliabraa35bab3932014-10-01 12:16:443187
3188def _CheckForInvalidIfDefinedMacrosInFile(input_api, f):
3189 """Check all affected files for invalid "if defined" macros."""
3190 ALWAYS_DEFINED_MACROS = (
3191 "TARGET_CPU_PPC",
3192 "TARGET_CPU_PPC64",
3193 "TARGET_CPU_68K",
3194 "TARGET_CPU_X86",
3195 "TARGET_CPU_ARM",
3196 "TARGET_CPU_MIPS",
3197 "TARGET_CPU_SPARC",
3198 "TARGET_CPU_ALPHA",
3199 "TARGET_IPHONE_SIMULATOR",
3200 "TARGET_OS_EMBEDDED",
3201 "TARGET_OS_IPHONE",
3202 "TARGET_OS_MAC",
3203 "TARGET_OS_UNIX",
3204 "TARGET_OS_WIN32",
3205 )
3206 ifdef_macro = input_api.re.compile(r'^\s*#.*(?:ifdef\s|defined\()([^\s\)]+)')
3207 results = []
3208 for lnum, line in f.ChangedContents():
3209 for match in ifdef_macro.finditer(line):
3210 if match.group(1) in ALWAYS_DEFINED_MACROS:
3211 always_defined = ' %s is always defined. ' % match.group(1)
3212 did_you_mean = 'Did you mean \'#if %s\'?' % match.group(1)
3213 results.append(' %s:%d %s\n\t%s' % (f.LocalPath(),
3214 lnum,
3215 always_defined,
3216 did_you_mean))
3217 return results
3218
3219
3220def _CheckForInvalidIfDefinedMacros(input_api, output_api):
3221 """Check all affected files for invalid "if defined" macros."""
3222 bad_macros = []
3223 for f in input_api.AffectedFiles():
sdefresne4e1eccb32017-05-24 08:45:213224 if f.LocalPath().startswith('third_party/sqlite/'):
3225 continue
lliabraa35bab3932014-10-01 12:16:443226 if f.LocalPath().endswith(('.h', '.c', '.cc', '.m', '.mm')):
3227 bad_macros.extend(_CheckForInvalidIfDefinedMacrosInFile(input_api, f))
3228
3229 if not bad_macros:
3230 return []
3231
3232 return [output_api.PresubmitError(
3233 'Found ifdef check on always-defined macro[s]. Please fix your code\n'
3234 'or check the list of ALWAYS_DEFINED_MACROS in src/PRESUBMIT.py.',
3235 bad_macros)]
3236
3237
mlamouria82272622014-09-16 18:45:043238def _CheckForIPCRules(input_api, output_api):
3239 """Check for same IPC rules described in
3240 http://www.chromium.org/Home/chromium-security/education/security-tips-for-ipc
3241 """
3242 base_pattern = r'IPC_ENUM_TRAITS\('
3243 inclusion_pattern = input_api.re.compile(r'(%s)' % base_pattern)
3244 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_pattern)
3245
3246 problems = []
3247 for f in input_api.AffectedSourceFiles(None):
3248 local_path = f.LocalPath()
3249 if not local_path.endswith('.h'):
3250 continue
3251 for line_number, line in f.ChangedContents():
3252 if inclusion_pattern.search(line) and not comment_pattern.search(line):
3253 problems.append(
3254 '%s:%d\n %s' % (local_path, line_number, line.strip()))
3255
3256 if problems:
3257 return [output_api.PresubmitPromptWarning(
3258 _IPC_ENUM_TRAITS_DEPRECATED, problems)]
3259 else:
3260 return []
3261
[email protected]b00342e7f2013-03-26 16:21:543262
Stephen Martinis97a394142018-06-07 23:06:053263def _CheckForLongPathnames(input_api, output_api):
3264 """Check to make sure no files being submitted have long paths.
3265 This causes issues on Windows.
3266 """
3267 problems = []
3268 for f in input_api.AffectedSourceFiles(None):
3269 local_path = f.LocalPath()
3270 # Windows has a path limit of 260 characters. Limit path length to 200 so
3271 # that we have some extra for the prefix on dev machines and the bots.
3272 if len(local_path) > 200:
3273 problems.append(local_path)
3274
3275 if problems:
3276 return [output_api.PresubmitError(_LONG_PATH_ERROR, problems)]
3277 else:
3278 return []
3279
3280
Daniel Bratell8ba52722018-03-02 16:06:143281def _CheckForIncludeGuards(input_api, output_api):
3282 """Check that header files have proper guards against multiple inclusion.
3283 If a file should not have such guards (and it probably should) then it
3284 should include the string "no-include-guard-because-multiply-included".
3285 """
Daniel Bratell6a75baef62018-06-04 10:04:453286 def is_chromium_header_file(f):
3287 # We only check header files under the control of the Chromium
3288 # project. That is, those outside third_party apart from
3289 # third_party/blink.
3290 file_with_path = input_api.os_path.normpath(f.LocalPath())
3291 return (file_with_path.endswith('.h') and
3292 (not file_with_path.startswith('third_party') or
3293 file_with_path.startswith(
3294 input_api.os_path.join('third_party', 'blink'))))
Daniel Bratell8ba52722018-03-02 16:06:143295
3296 def replace_special_with_underscore(string):
Olivier Robinbba137492018-07-30 11:31:343297 return input_api.re.sub(r'[+\\/.-]', '_', string)
Daniel Bratell8ba52722018-03-02 16:06:143298
3299 errors = []
3300
Daniel Bratell6a75baef62018-06-04 10:04:453301 for f in input_api.AffectedSourceFiles(is_chromium_header_file):
Daniel Bratell8ba52722018-03-02 16:06:143302 guard_name = None
3303 guard_line_number = None
3304 seen_guard_end = False
3305
3306 file_with_path = input_api.os_path.normpath(f.LocalPath())
3307 base_file_name = input_api.os_path.splitext(
3308 input_api.os_path.basename(file_with_path))[0]
3309 upper_base_file_name = base_file_name.upper()
3310
3311 expected_guard = replace_special_with_underscore(
3312 file_with_path.upper() + '_')
Daniel Bratell8ba52722018-03-02 16:06:143313
3314 # For "path/elem/file_name.h" we should really only accept
Daniel Bratell39b5b062018-05-16 18:09:573315 # PATH_ELEM_FILE_NAME_H_ per coding style. Unfortunately there
3316 # are too many (1000+) files with slight deviations from the
3317 # coding style. The most important part is that the include guard
3318 # is there, and that it's unique, not the name so this check is
3319 # forgiving for existing files.
Daniel Bratell8ba52722018-03-02 16:06:143320 #
3321 # As code becomes more uniform, this could be made stricter.
3322
3323 guard_name_pattern_list = [
3324 # Anything with the right suffix (maybe with an extra _).
3325 r'\w+_H__?',
3326
Daniel Bratell39b5b062018-05-16 18:09:573327 # To cover include guards with old Blink style.
Daniel Bratell8ba52722018-03-02 16:06:143328 r'\w+_h',
3329
3330 # Anything including the uppercase name of the file.
3331 r'\w*' + input_api.re.escape(replace_special_with_underscore(
3332 upper_base_file_name)) + r'\w*',
3333 ]
3334 guard_name_pattern = '|'.join(guard_name_pattern_list)
3335 guard_pattern = input_api.re.compile(
3336 r'#ifndef\s+(' + guard_name_pattern + ')')
3337
3338 for line_number, line in enumerate(f.NewContents()):
3339 if 'no-include-guard-because-multiply-included' in line:
3340 guard_name = 'DUMMY' # To not trigger check outside the loop.
3341 break
3342
3343 if guard_name is None:
3344 match = guard_pattern.match(line)
3345 if match:
3346 guard_name = match.group(1)
3347 guard_line_number = line_number
3348
Daniel Bratell39b5b062018-05-16 18:09:573349 # We allow existing files to use include guards whose names
Daniel Bratell6a75baef62018-06-04 10:04:453350 # don't match the chromium style guide, but new files should
3351 # get it right.
3352 if not f.OldContents():
Daniel Bratell39b5b062018-05-16 18:09:573353 if guard_name != expected_guard:
Daniel Bratell8ba52722018-03-02 16:06:143354 errors.append(output_api.PresubmitPromptWarning(
3355 'Header using the wrong include guard name %s' % guard_name,
3356 ['%s:%d' % (f.LocalPath(), line_number + 1)],
Daniel Bratell39b5b062018-05-16 18:09:573357 'Expected: %r\nFound: %r' % (expected_guard, guard_name)))
Daniel Bratell8ba52722018-03-02 16:06:143358 else:
3359 # The line after #ifndef should have a #define of the same name.
3360 if line_number == guard_line_number + 1:
3361 expected_line = '#define %s' % guard_name
3362 if line != expected_line:
3363 errors.append(output_api.PresubmitPromptWarning(
3364 'Missing "%s" for include guard' % expected_line,
3365 ['%s:%d' % (f.LocalPath(), line_number + 1)],
3366 'Expected: %r\nGot: %r' % (expected_line, line)))
3367
3368 if not seen_guard_end and line == '#endif // %s' % guard_name:
3369 seen_guard_end = True
3370 elif seen_guard_end:
3371 if line.strip() != '':
3372 errors.append(output_api.PresubmitPromptWarning(
3373 'Include guard %s not covering the whole file' % (
3374 guard_name), [f.LocalPath()]))
3375 break # Nothing else to check and enough to warn once.
3376
3377 if guard_name is None:
3378 errors.append(output_api.PresubmitPromptWarning(
3379 'Missing include guard %s' % expected_guard,
3380 [f.LocalPath()],
3381 'Missing include guard in %s\n'
3382 'Recommended name: %s\n'
3383 'This check can be disabled by having the string\n'
3384 'no-include-guard-because-multiply-included in the header.' %
3385 (f.LocalPath(), expected_guard)))
3386
3387 return errors
3388
3389
mostynbb639aca52015-01-07 20:31:233390def _CheckForWindowsLineEndings(input_api, output_api):
3391 """Check source code and known ascii text files for Windows style line
3392 endings.
3393 """
earthdok1b5e0ee2015-03-10 15:19:103394 known_text_files = r'.*\.(txt|html|htm|mhtml|py|gyp|gypi|gn|isolate)$'
mostynbb639aca52015-01-07 20:31:233395
3396 file_inclusion_pattern = (
3397 known_text_files,
3398 r'.+%s' % _IMPLEMENTATION_EXTENSIONS
3399 )
3400
mostynbb639aca52015-01-07 20:31:233401 problems = []
Andrew Grieve933d12e2017-10-30 20:22:533402 source_file_filter = lambda f: input_api.FilterSourceFile(
3403 f, white_list=file_inclusion_pattern, black_list=None)
3404 for f in input_api.AffectedSourceFiles(source_file_filter):
Vaclav Brozekd5de76a2018-03-17 07:57:503405 include_file = False
3406 for _, line in f.ChangedContents():
mostynbb639aca52015-01-07 20:31:233407 if line.endswith('\r\n'):
Vaclav Brozekd5de76a2018-03-17 07:57:503408 include_file = True
3409 if include_file:
3410 problems.append(f.LocalPath())
mostynbb639aca52015-01-07 20:31:233411
3412 if problems:
3413 return [output_api.PresubmitPromptWarning('Are you sure that you want '
3414 'these files to contain Windows style line endings?\n' +
3415 '\n'.join(problems))]
3416
3417 return []
3418
3419
Vaclav Brozekd5de76a2018-03-17 07:57:503420def _CheckSyslogUseWarning(input_api, output_api, source_file_filter=None):
pastarmovj89f7ee12016-09-20 14:58:133421 """Checks that all source files use SYSLOG properly."""
3422 syslog_files = []
3423 for f in input_api.AffectedSourceFiles(source_file_filter):
pastarmovj032ba5bc2017-01-12 10:41:563424 for line_number, line in f.ChangedContents():
3425 if 'SYSLOG' in line:
3426 syslog_files.append(f.LocalPath() + ':' + str(line_number))
3427
pastarmovj89f7ee12016-09-20 14:58:133428 if syslog_files:
3429 return [output_api.PresubmitPromptWarning(
3430 'Please make sure there are no privacy sensitive bits of data in SYSLOG'
3431 ' calls.\nFiles to check:\n', items=syslog_files)]
3432 return []
3433
3434
[email protected]1f7b4172010-01-28 01:17:343435def CheckChangeOnUpload(input_api, output_api):
3436 results = []
3437 results.extend(_CommonChecks(input_api, output_api))
tandriief664692014-09-23 14:51:473438 results.extend(_CheckValidHostsInDEPS(input_api, output_api))
scottmg39b29952014-12-08 18:31:283439 results.extend(
jam93a6ee792017-02-08 23:59:223440 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
mcasasb7440c282015-02-04 14:52:193441 results.extend(_CheckUmaHistogramChanges(input_api, output_api))
dgnaa68d5e2015-06-10 10:08:223442 results.extend(_AndroidSpecificOnUploadChecks(input_api, output_api))
pastarmovj89f7ee12016-09-20 14:58:133443 results.extend(_CheckSyslogUseWarning(input_api, output_api))
estadee17314a02017-01-12 16:22:163444 results.extend(_CheckGoogleSupportAnswerUrl(input_api, output_api))
Vaclav Brozekea41ab22018-04-06 13:21:533445 results.extend(_CheckUniquePtr(input_api, output_api))
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:193446 results.extend(_CheckNewHeaderWithoutGnChange(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:543447 return results
[email protected]ca8d1982009-02-19 16:33:123448
3449
[email protected]1bfb8322014-04-23 01:02:413450def GetTryServerMasterForBot(bot):
3451 """Returns the Try Server master for the given bot.
3452
[email protected]0bb112362014-07-26 04:38:323453 It tries to guess the master from the bot name, but may still fail
3454 and return None. There is no longer a default master.
3455 """
3456 # Potentially ambiguous bot names are listed explicitly.
3457 master_map = {
tandriie5587792016-07-14 00:34:503458 'chromium_presubmit': 'master.tryserver.chromium.linux',
3459 'tools_build_presubmit': 'master.tryserver.chromium.linux',
[email protected]1bfb8322014-04-23 01:02:413460 }
[email protected]0bb112362014-07-26 04:38:323461 master = master_map.get(bot)
3462 if not master:
wnwen4fbaab82016-05-25 12:54:363463 if 'android' in bot:
tandriie5587792016-07-14 00:34:503464 master = 'master.tryserver.chromium.android'
wnwen4fbaab82016-05-25 12:54:363465 elif 'linux' in bot or 'presubmit' in bot:
tandriie5587792016-07-14 00:34:503466 master = 'master.tryserver.chromium.linux'
[email protected]0bb112362014-07-26 04:38:323467 elif 'win' in bot:
tandriie5587792016-07-14 00:34:503468 master = 'master.tryserver.chromium.win'
[email protected]0bb112362014-07-26 04:38:323469 elif 'mac' in bot or 'ios' in bot:
tandriie5587792016-07-14 00:34:503470 master = 'master.tryserver.chromium.mac'
[email protected]0bb112362014-07-26 04:38:323471 return master
[email protected]1bfb8322014-04-23 01:02:413472
3473
[email protected]ca8d1982009-02-19 16:33:123474def CheckChangeOnCommit(input_api, output_api):
[email protected]fe5f57c52009-06-05 14:25:543475 results = []
[email protected]1f7b4172010-01-28 01:17:343476 results.extend(_CommonChecks(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:543477 # Make sure the tree is 'open'.
[email protected]806e98e2010-03-19 17:49:273478 results.extend(input_api.canned_checks.CheckTreeIsOpen(
[email protected]7f238152009-08-12 19:00:343479 input_api,
3480 output_api,
[email protected]2fdd1f362013-01-16 03:56:033481 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:273482
jam93a6ee792017-02-08 23:59:223483 results.extend(
3484 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
[email protected]3e4eb112011-01-18 03:29:543485 results.extend(input_api.canned_checks.CheckChangeHasBugField(
3486 input_api, output_api))
[email protected]c4b47562011-12-05 23:39:413487 results.extend(input_api.canned_checks.CheckChangeHasDescription(
3488 input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:543489 return results
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143490
3491
3492def _CheckTranslationScreenshots(input_api, output_api):
3493 PART_FILE_TAG = "part"
3494 import os
3495 import sys
3496 from io import StringIO
3497
3498 try:
3499 old_sys_path = sys.path
3500 sys.path = sys.path + [input_api.os_path.join(
3501 input_api.PresubmitLocalPath(), 'tools', 'grit')]
3502 import grit.grd_reader
3503 import grit.node.message
3504 import grit.util
3505 finally:
3506 sys.path = old_sys_path
3507
3508 def _GetGrdMessages(grd_path_or_string, dir_path='.'):
3509 """Load the grd file and return a dict of message ids to messages.
3510
3511 Ignores any nested grdp files pointed by <part> tag.
3512 """
3513 doc = grit.grd_reader.Parse(grd_path_or_string, dir_path,
3514 stop_after=None, first_ids_file=None,
3515 debug=False, defines=None,
3516 tags_to_ignore=set([PART_FILE_TAG]))
3517 return {
3518 msg.attrs['name']:msg for msg in doc.GetChildrenOfType(
3519 grit.node.message.MessageNode)
3520 }
3521
3522 def _GetGrdpMessagesFromString(grdp_string):
3523 """Parses the contents of a grdp file given in grdp_string.
3524
3525 grd_reader can't parse grdp files directly. Instead, this creates a
3526 temporary directory with a grd file pointing to the grdp file, and loads the
3527 grd from there. Any nested grdp files (pointed by <part> tag) are ignored.
3528 """
3529 WRAPPER = """<?xml version="1.0" encoding="utf-8"?>
3530 <grit latest_public_release="1" current_release="1">
3531 <release seq="1">
3532 <messages>
3533 <part file="sub.grdp" />
3534 </messages>
3535 </release>
3536 </grit>
3537 """
3538 with grit.util.TempDir({'main.grd': WRAPPER,
3539 'sub.grdp': grdp_string}) as temp_dir:
3540 return _GetGrdMessages(temp_dir.GetPath('main.grd'), temp_dir.GetPath())
3541
3542 new_or_added_paths = set(f.LocalPath()
3543 for f in input_api.AffectedFiles()
3544 if (f.Action() == 'A' or f.Action() == 'M'))
3545 removed_paths = set(f.LocalPath()
3546 for f in input_api.AffectedFiles(include_deletes=True)
3547 if f.Action() == 'D')
3548
3549 affected_grds = [f for f in input_api.AffectedFiles()
3550 if (f.LocalPath().endswith('.grd') or
3551 f.LocalPath().endswith('.grdp'))]
3552 affected_png_paths = [f.AbsoluteLocalPath()
3553 for f in input_api.AffectedFiles()
3554 if (f.LocalPath().endswith('.png'))]
3555
3556 # Check for screenshots. Developers can upload screenshots using
3557 # tools/translation/upload_screenshots.py which finds and uploads
3558 # images associated with .grd files (e.g. test_grd/IDS_STRING.png for the
3559 # message named IDS_STRING in test.grd) and produces a .sha1 file (e.g.
3560 # test_grd/IDS_STRING.png.sha1) for each png when the upload is successful.
3561 #
3562 # The logic here is as follows:
3563 #
3564 # - If the CL has a .png file under the screenshots directory for a grd
3565 # file, warn the developer. Actual images should never be checked into the
3566 # Chrome repo.
3567 #
3568 # - If the CL contains modified or new messages in grd files and doesn't
3569 # contain the corresponding .sha1 files, warn the developer to add images
3570 # and upload them via tools/translation/upload_screenshots.py.
3571 #
3572 # - If the CL contains modified or new messages in grd files and the
3573 # corresponding .sha1 files, everything looks good.
3574 #
3575 # - If the CL contains removed messages in grd files but the corresponding
3576 # .sha1 files aren't removed, warn the developer to remove them.
3577 unnecessary_screenshots = []
3578 missing_sha1 = []
3579 unnecessary_sha1_files = []
3580
3581
3582 def _CheckScreenshotAdded(screenshots_dir, message_id):
3583 sha1_path = input_api.os_path.join(
3584 screenshots_dir, message_id + '.png.sha1')
3585 if sha1_path not in new_or_added_paths:
3586 missing_sha1.append(sha1_path)
3587
3588
3589 def _CheckScreenshotRemoved(screenshots_dir, message_id):
3590 sha1_path = input_api.os_path.join(
3591 screenshots_dir, message_id + '.png.sha1')
3592 if sha1_path not in removed_paths:
3593 unnecessary_sha1_files.append(sha1_path)
3594
3595
3596 for f in affected_grds:
3597 file_path = f.LocalPath()
3598 old_id_to_msg_map = {}
3599 new_id_to_msg_map = {}
3600 if file_path.endswith('.grdp'):
3601 if f.OldContents():
3602 old_id_to_msg_map = _GetGrdpMessagesFromString(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393603 unicode('\n'.join(f.OldContents())))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143604 if f.NewContents():
3605 new_id_to_msg_map = _GetGrdpMessagesFromString(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393606 unicode('\n'.join(f.NewContents())))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143607 else:
3608 if f.OldContents():
3609 old_id_to_msg_map = _GetGrdMessages(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393610 StringIO(unicode('\n'.join(f.OldContents()))))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143611 if f.NewContents():
3612 new_id_to_msg_map = _GetGrdMessages(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393613 StringIO(unicode('\n'.join(f.NewContents()))))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143614
3615 # Compute added, removed and modified message IDs.
3616 old_ids = set(old_id_to_msg_map)
3617 new_ids = set(new_id_to_msg_map)
3618 added_ids = new_ids - old_ids
3619 removed_ids = old_ids - new_ids
3620 modified_ids = set([])
3621 for key in old_ids.intersection(new_ids):
3622 if (old_id_to_msg_map[key].FormatXml()
3623 != new_id_to_msg_map[key].FormatXml()):
3624 modified_ids.add(key)
3625
3626 grd_name, ext = input_api.os_path.splitext(
3627 input_api.os_path.basename(file_path))
3628 screenshots_dir = input_api.os_path.join(
3629 input_api.os_path.dirname(file_path), grd_name + ext.replace('.', '_'))
3630
3631 # Check the screenshot directory for .png files. Warn if there is any.
3632 for png_path in affected_png_paths:
3633 if png_path.startswith(screenshots_dir):
3634 unnecessary_screenshots.append(png_path)
3635
3636 for added_id in added_ids:
3637 _CheckScreenshotAdded(screenshots_dir, added_id)
3638
3639 for modified_id in modified_ids:
3640 _CheckScreenshotAdded(screenshots_dir, modified_id)
3641
3642 for removed_id in removed_ids:
3643 _CheckScreenshotRemoved(screenshots_dir, removed_id)
3644
3645 results = []
3646 if unnecessary_screenshots:
3647 results.append(output_api.PresubmitNotifyResult(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393648 'Do not include actual screenshots in the changelist. Run '
3649 'tools/translate/upload_screenshots.py to upload them instead:',
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143650 sorted(unnecessary_screenshots)))
3651
3652 if missing_sha1:
3653 results.append(output_api.PresubmitNotifyResult(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393654 'You are adding or modifying UI strings.\n'
3655 'To ensure the best translations, take screenshots of the relevant UI '
3656 '(https://g.co/chrome/translation) and add these files to your '
3657 'changelist:', sorted(missing_sha1)))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143658
3659 if unnecessary_sha1_files:
3660 results.append(output_api.PresubmitNotifyResult(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393661 'You removed strings associated with these files. Remove:',
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143662 sorted(unnecessary_sha1_files)))
3663
3664 return results