blob: a356d765c012695cdb78196968ed557e51c0a218 [file] [log] [blame]
andrew@webrtc.org2442de12012-01-23 17:45:41 +00001# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
2#
3# Use of this source code is governed by a BSD-style license
4# that can be found in the LICENSE file in the root of the source
5# tree. An additional intellectual property rights grant can be found
6# in the file PATENTS. All contributing project authors may
7# be found in the AUTHORS file in the root of the source tree.
niklase@google.comda159d62011-05-30 11:51:34 +00008
kjellander7439f972016-12-05 22:47:46 -08009import json
kjellander@webrtc.orgaefe61a2014-12-08 13:00:30 +000010import os
kjellander@webrtc.org85759802013-10-22 16:47:40 +000011import re
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +000012import sys
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020013from collections import defaultdict
Oleh Prypin2f33a562017-10-04 20:17:54 +020014from contextlib import contextmanager
kjellander@webrtc.org85759802013-10-22 16:47:40 +000015
16
oprypin2aa463f2017-03-23 03:17:02 -070017# Files and directories that are *skipped* by cpplint in the presubmit script.
18CPPLINT_BLACKLIST = [
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019 'api/video_codecs/video_decoder.h',
20 'common_types.cc',
21 'common_types.h',
22 'examples/objc',
23 'media',
24 'modules/audio_coding',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020025 'modules/audio_device',
26 'modules/audio_processing',
27 'modules/desktop_capture',
28 'modules/include/module_common_types.h',
29 'modules/media_file',
30 'modules/utility',
31 'modules/video_capture',
32 'p2p',
33 'pc',
34 'rtc_base',
35 'sdk/android/src/jni',
36 'sdk/objc',
37 'system_wrappers',
38 'test',
Henrik Kjellander90fd7d82017-05-09 08:30:10 +020039 'tools_webrtc',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020040 'voice_engine',
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +010041]
42
jbauchc4e3ead2016-02-19 00:25:55 -080043# These filters will always be removed, even if the caller specifies a filter
44# set, as they are problematic or broken in some way.
45#
46# Justifications for each filter:
47# - build/c++11 : Rvalue ref checks are unreliable (false positives),
48# include file and feature blacklists are
49# google3-specific.
kjellandere5a87a52016-04-27 02:32:12 -070050# - whitespace/operators: Same as above (doesn't seem sufficient to eliminate
51# all move-related errors).
jbauchc4e3ead2016-02-19 00:25:55 -080052BLACKLIST_LINT_FILTERS = [
53 '-build/c++11',
kjellandere5a87a52016-04-27 02:32:12 -070054 '-whitespace/operators',
jbauchc4e3ead2016-02-19 00:25:55 -080055]
56
kjellanderfd595232015-12-04 02:44:09 -080057# List of directories of "supported" native APIs. That means changes to headers
58# will be done in a compatible way following this scheme:
59# 1. Non-breaking changes are made.
60# 2. The old APIs as marked as deprecated (with comments).
61# 3. Deprecation is announced to discuss-webrtc@googlegroups.com and
62# webrtc-users@google.com (internal list).
63# 4. (later) The deprecated APIs are removed.
kjellander53047c92015-12-02 23:56:14 -080064NATIVE_API_DIRS = (
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020065 'api',
66 'media',
67 'modules/audio_device/include',
68 'pc',
kjellanderdd705472016-06-09 11:17:27 -070069)
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020070
kjellanderdd705472016-06-09 11:17:27 -070071# These directories should not be used but are maintained only to avoid breaking
72# some legacy downstream code.
73LEGACY_API_DIRS = (
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020074 'common_audio/include',
75 'modules/audio_coding/include',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020076 'modules/audio_processing/include',
77 'modules/bitrate_controller/include',
78 'modules/congestion_controller/include',
79 'modules/include',
80 'modules/remote_bitrate_estimator/include',
81 'modules/rtp_rtcp/include',
82 'modules/rtp_rtcp/source',
83 'modules/utility/include',
84 'modules/video_coding/codecs/h264/include',
85 'modules/video_coding/codecs/i420/include',
86 'modules/video_coding/codecs/vp8/include',
87 'modules/video_coding/codecs/vp9/include',
88 'modules/video_coding/include',
89 'rtc_base',
90 'system_wrappers/include',
91 'voice_engine/include',
kjellander53047c92015-12-02 23:56:14 -080092)
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020093
kjellanderdd705472016-06-09 11:17:27 -070094API_DIRS = NATIVE_API_DIRS[:] + LEGACY_API_DIRS[:]
kjellander53047c92015-12-02 23:56:14 -080095
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020096# TARGET_RE matches a GN target, and extracts the target name and the contents.
97TARGET_RE = re.compile(r'(?P<indent>\s*)\w+\("(?P<target_name>\w+)"\) {'
98 r'(?P<target_contents>.*?)'
99 r'(?P=indent)}',
100 re.MULTILINE | re.DOTALL)
101
102# SOURCES_RE matches a block of sources inside a GN target.
103SOURCES_RE = re.compile(r'sources \+?= \[(?P<sources>.*?)\]',
104 re.MULTILINE | re.DOTALL)
105
106# FILE_PATH_RE matchies a file path.
107FILE_PATH_RE = re.compile(r'"(?P<file_path>(\w|\/)+)(?P<extension>\.\w+)"')
108
kjellander53047c92015-12-02 23:56:14 -0800109
Oleh Prypin2f33a562017-10-04 20:17:54 +0200110@contextmanager
111def _AddToPath(*paths):
112 original_sys_path = sys.path
113 sys.path.extend(paths)
114 try:
115 yield
116 finally:
117 # Restore sys.path to what it was before.
118 sys.path = original_sys_path
ehmaldonado4fb97462017-01-30 05:27:22 -0800119
120
charujain9893e252017-09-14 13:33:22 +0200121def VerifyNativeApiHeadersListIsValid(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800122 """Ensures the list of native API header directories is up to date."""
123 non_existing_paths = []
124 native_api_full_paths = [
125 input_api.os_path.join(input_api.PresubmitLocalPath(),
kjellanderdd705472016-06-09 11:17:27 -0700126 *path.split('/')) for path in API_DIRS]
kjellander53047c92015-12-02 23:56:14 -0800127 for path in native_api_full_paths:
128 if not os.path.isdir(path):
129 non_existing_paths.append(path)
130 if non_existing_paths:
131 return [output_api.PresubmitError(
132 'Directories to native API headers have changed which has made the '
133 'list in PRESUBMIT.py outdated.\nPlease update it to the current '
134 'location of our native APIs.',
135 non_existing_paths)]
136 return []
137
kjellanderc88b5d52017-04-05 06:42:43 -0700138API_CHANGE_MSG = """
kwibergeb133022016-04-07 07:41:48 -0700139You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 04:13:23 -0800140 1. Make compatible changes that don't break existing clients. Usually
141 this is done by keeping the existing method signatures unchanged.
142 2. Mark the old stuff as deprecated (see RTC_DEPRECATED macro).
kwibergeb133022016-04-07 07:41:48 -0700143 3. Create a timeline and plan for when the deprecated stuff will be
144 removed. (The amount of time we give users to change their code
145 should be informed by how much work it is for them. If they just
146 need to replace one name with another or something equally
147 simple, 1-2 weeks might be good; if they need to do serious work,
148 up to 3 months may be called for.)
149 4. Update/inform existing downstream code owners to stop using the
150 deprecated stuff. (Send announcements to
151 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
152 5. Remove the deprecated stuff, once the agreed-upon amount of time
153 has passed.
154Related files:
155"""
kjellander53047c92015-12-02 23:56:14 -0800156
charujain9893e252017-09-14 13:33:22 +0200157def CheckNativeApiHeaderChanges(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800158 """Checks to remind proper changing of native APIs."""
159 files = []
160 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
161 if f.LocalPath().endswith('.h'):
kjellanderdd705472016-06-09 11:17:27 -0700162 for path in API_DIRS:
kjellander53047c92015-12-02 23:56:14 -0800163 if os.path.dirname(f.LocalPath()) == path:
164 files.append(f)
165
166 if files:
kjellanderc88b5d52017-04-05 06:42:43 -0700167 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
kjellander53047c92015-12-02 23:56:14 -0800168 return []
169
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100170
charujain9893e252017-09-14 13:33:22 +0200171def CheckNoIOStreamInHeaders(input_api, output_api):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000172 """Checks to make sure no .h files include <iostream>."""
173 files = []
174 pattern = input_api.re.compile(r'^#include\s*<iostream>',
175 input_api.re.MULTILINE)
176 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
177 if not f.LocalPath().endswith('.h'):
178 continue
179 contents = input_api.ReadFile(f)
180 if pattern.search(contents):
181 files.append(f)
182
183 if len(files):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200184 return [output_api.PresubmitError(
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000185 'Do not #include <iostream> in header files, since it inserts static ' +
186 'initialization into every file including the header. Instead, ' +
187 '#include <ostream>. See http://crbug.com/94794',
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200188 files)]
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000189 return []
190
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000191
charujain9893e252017-09-14 13:33:22 +0200192def CheckNoPragmaOnce(input_api, output_api):
kjellander6aeef742017-02-20 01:13:18 -0800193 """Make sure that banned functions are not used."""
194 files = []
195 pattern = input_api.re.compile(r'^#pragma\s+once',
196 input_api.re.MULTILINE)
197 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
198 if not f.LocalPath().endswith('.h'):
199 continue
200 contents = input_api.ReadFile(f)
201 if pattern.search(contents):
202 files.append(f)
203
204 if files:
205 return [output_api.PresubmitError(
206 'Do not use #pragma once in header files.\n'
207 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
208 files)]
209 return []
210
211
charujain9893e252017-09-14 13:33:22 +0200212def CheckNoFRIEND_TEST(input_api, output_api): # pylint: disable=invalid-name
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000213 """Make sure that gtest's FRIEND_TEST() macro is not used, the
214 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
215 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
216 problems = []
217
218 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h'))
219 for f in input_api.AffectedFiles(file_filter=file_filter):
220 for line_num, line in f.ChangedContents():
221 if 'FRIEND_TEST(' in line:
222 problems.append(' %s:%d' % (f.LocalPath(), line_num))
223
224 if not problems:
225 return []
226 return [output_api.PresubmitPromptWarning('WebRTC\'s code should not use '
227 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and '
228 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
229
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000230
charujain9893e252017-09-14 13:33:22 +0200231def IsLintBlacklisted(blacklist_paths, file_path):
oprypin2aa463f2017-03-23 03:17:02 -0700232 """ Checks if a file is blacklisted for lint check."""
233 for path in blacklist_paths:
234 if file_path == path or os.path.dirname(file_path).startswith(path):
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100235 return True
236 return False
237
238
charujain9893e252017-09-14 13:33:22 +0200239def CheckApprovedFilesLintClean(input_api, output_api,
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000240 source_file_filter=None):
oprypin2aa463f2017-03-23 03:17:02 -0700241 """Checks that all new or non-blacklisted .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 13:33:22 +0200242 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000243 depot_tools/presubmit_canned_checks.py but has less filters and only checks
244 added files."""
245 result = []
246
247 # Initialize cpplint.
248 import cpplint
249 # Access to a protected member _XX of a client class
250 # pylint: disable=W0212
251 cpplint._cpplint_state.ResetErrorCounts()
252
jbauchc4e3ead2016-02-19 00:25:55 -0800253 lint_filters = cpplint._Filters()
254 lint_filters.extend(BLACKLIST_LINT_FILTERS)
255 cpplint._SetFilters(','.join(lint_filters))
256
oprypin2aa463f2017-03-23 03:17:02 -0700257 # Create a platform independent blacklist for cpplint.
258 blacklist_paths = [input_api.os_path.join(*path.split('/'))
259 for path in CPPLINT_BLACKLIST]
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100260
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000261 # Use the strictest verbosity level for cpplint.py (level 1) which is the
oprypin2aa463f2017-03-23 03:17:02 -0700262 # default when running cpplint.py from command line. To make it possible to
263 # work with not-yet-converted code, we're only applying it to new (or
264 # moved/renamed) files and files not listed in CPPLINT_BLACKLIST.
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000265 verbosity_level = 1
266 files = []
267 for f in input_api.AffectedSourceFiles(source_file_filter):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200268 # Note that moved/renamed files also count as added.
charujain9893e252017-09-14 13:33:22 +0200269 if f.Action() == 'A' or not IsLintBlacklisted(blacklist_paths,
oprypin2aa463f2017-03-23 03:17:02 -0700270 f.LocalPath()):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000271 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000272
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000273 for file_name in files:
274 cpplint.ProcessFile(file_name, verbosity_level)
275
276 if cpplint._cpplint_state.error_count > 0:
277 if input_api.is_committing:
oprypin8e58d652017-03-21 07:52:41 -0700278 res_type = output_api.PresubmitError
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000279 else:
280 res_type = output_api.PresubmitPromptWarning
281 result = [res_type('Changelist failed cpplint.py check.')]
282
283 return result
284
charujain9893e252017-09-14 13:33:22 +0200285def CheckNoSourcesAbove(input_api, gn_files, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700286 # Disallow referencing source files with paths above the GN file location.
287 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
288 re.MULTILINE | re.DOTALL)
289 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
290 violating_gn_files = set()
291 violating_source_entries = []
292 for gn_file in gn_files:
293 contents = input_api.ReadFile(gn_file)
294 for source_block_match in source_pattern.finditer(contents):
295 # Find all source list entries starting with ../ in the source block
296 # (exclude overrides entries).
297 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
298 source_file = file_list_match.group(1)
299 if 'overrides/' not in source_file:
300 violating_source_entries.append(source_file)
301 violating_gn_files.add(gn_file)
302 if violating_gn_files:
303 return [output_api.PresubmitError(
304 'Referencing source files above the directory of the GN file is not '
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100305 'allowed. Please introduce new GN targets in the proper location '
306 'instead.\n'
ehmaldonado5b1ba082016-09-02 05:51:08 -0700307 'Invalid source entries:\n'
308 '%s\n'
309 'Violating GN files:' % '\n'.join(violating_source_entries),
310 items=violating_gn_files)]
311 return []
312
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200313def CheckNoMixingSources(input_api, gn_files, output_api):
314 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
315
316 See bugs.webrtc.org/7743 for more context.
317 """
318 def _MoreThanOneSourceUsed(*sources_lists):
319 sources_used = 0
320 for source_list in sources_lists:
321 if len(source_list):
322 sources_used += 1
323 return sources_used > 1
324
325 errors = defaultdict(lambda: [])
kjellander7439f972016-12-05 22:47:46 -0800326 for gn_file in gn_files:
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200327 gn_file_content = input_api.ReadFile(gn_file)
328 for target_match in TARGET_RE.finditer(gn_file_content):
329 # list_of_sources is a list of tuples of the form
330 # (c_files, cc_files, objc_files) that keeps track of all the sources
331 # defined in a target. A GN target can have more that on definition of
332 # sources (since it supports if/else statements).
333 # E.g.:
334 # rtc_static_library("foo") {
335 # if (is_win) {
336 # sources = [ "foo.cc" ]
337 # } else {
338 # sources = [ "foo.mm" ]
339 # }
340 # }
341 # This is allowed and the presubmit check should support this case.
342 list_of_sources = []
kjellander7439f972016-12-05 22:47:46 -0800343 c_files = []
344 cc_files = []
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200345 objc_files = []
346 target_name = target_match.group('target_name')
347 target_contents = target_match.group('target_contents')
348 for sources_match in SOURCES_RE.finditer(target_contents):
349 if '+=' not in sources_match.group(0):
350 if c_files or cc_files or objc_files:
351 list_of_sources.append((c_files, cc_files, objc_files))
352 c_files = []
353 cc_files = []
354 objc_files = []
355 for file_match in FILE_PATH_RE.finditer(sources_match.group(1)):
356 file_path = file_match.group('file_path')
357 extension = file_match.group('extension')
358 if extension == '.c':
359 c_files.append(file_path + extension)
360 if extension == '.cc':
361 cc_files.append(file_path + extension)
362 if extension in ['.m', '.mm']:
363 objc_files.append(file_path + extension)
364 list_of_sources.append((c_files, cc_files, objc_files))
365 for c_files_list, cc_files_list, objc_files_list in list_of_sources:
366 if _MoreThanOneSourceUsed(c_files_list, cc_files_list, objc_files_list):
367 all_sources = sorted(c_files_list + cc_files_list + objc_files_list)
368 errors[gn_file.LocalPath()].append((target_name, all_sources))
369 if errors:
kjellander7439f972016-12-05 22:47:46 -0800370 return [output_api.PresubmitError(
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200371 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n'
372 'Please create a separate target for each collection of sources.\n'
kjellander7439f972016-12-05 22:47:46 -0800373 'Mixed sources: \n'
374 '%s\n'
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200375 'Violating GN files:\n%s\n' % (json.dumps(errors, indent=2),
376 '\n'.join(errors.keys())))]
kjellander7439f972016-12-05 22:47:46 -0800377 return []
378
charujain9893e252017-09-14 13:33:22 +0200379def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
ehmaldonado4fb97462017-01-30 05:27:22 -0800380 cwd = input_api.PresubmitLocalPath()
Oleh Prypin2f33a562017-10-04 20:17:54 +0200381 with _AddToPath(input_api.os_path.join(
382 cwd, 'tools_webrtc', 'presubmit_checks_lib')):
383 from check_package_boundaries import CheckPackageBoundaries
384 build_files = [os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files]
385 errors = CheckPackageBoundaries(cwd, build_files)[:5]
386 if errors:
ehmaldonado4fb97462017-01-30 05:27:22 -0800387 return [output_api.PresubmitError(
Oleh Prypin2f33a562017-10-04 20:17:54 +0200388 'There are package boundary violations in the following GN files:',
389 long_text='\n\n'.join(str(err) for err in errors))]
ehmaldonado4fb97462017-01-30 05:27:22 -0800390 return []
391
charujain9893e252017-09-14 13:33:22 +0200392def CheckGnChanges(input_api, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700393 source_file_filter = lambda x: input_api.FilterSourceFile(
Oleh Prypinafe01652017-10-04 15:56:08 +0200394 x, white_list=(r'.+\.(gn|gni)$',),
395 black_list=r'.*/presubmit_checks_lib/testdata/.*')
ehmaldonado5b1ba082016-09-02 05:51:08 -0700396
397 gn_files = []
398 for f in input_api.AffectedSourceFiles(source_file_filter):
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200399 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 05:51:08 -0700400
401 result = []
402 if gn_files:
charujain9893e252017-09-14 13:33:22 +0200403 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200404 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
405 result.extend(CheckNoPackageBoundaryViolations(input_api, gn_files,
406 output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700407 return result
408
Oleh Prypin920b6532017-10-05 11:28:51 +0200409def CheckGnGen(input_api, output_api):
410 """Runs `gn gen --check` with default args to detect mismatches between
411 #includes and dependencies in the BUILD.gn files, as well as general build
412 errors.
413 """
414 with _AddToPath(input_api.os_path.join(
415 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
416 from gn_check import RunGnCheck
417 errors = RunGnCheck(input_api.PresubmitLocalPath())[:5]
418 if errors:
419 return [output_api.PresubmitPromptWarning(
420 'Some #includes do not match the build dependency graph. Please run:\n'
421 ' gn gen --check <out_dir>',
422 long_text='\n\n'.join(errors))]
423 return []
424
charujain9893e252017-09-14 13:33:22 +0200425def CheckUnwantedDependencies(input_api, output_api):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000426 """Runs checkdeps on #include statements added in this
427 change. Breaking - rules is an error, breaking ! rules is a
428 warning.
429 """
430 # Copied from Chromium's src/PRESUBMIT.py.
431
432 # We need to wait until we have an input_api object and use this
433 # roundabout construct to import checkdeps because this file is
434 # eval-ed and thus doesn't have __file__.
Oleh Prypin2f33a562017-10-04 20:17:54 +0200435 checkdeps_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
436 'buildtools', 'checkdeps')
437 if not os.path.exists(checkdeps_path):
438 return [output_api.PresubmitError(
439 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
440 'download all the DEPS entries?' % checkdeps_path)]
441 with _AddToPath(checkdeps_path):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000442 import checkdeps
443 from cpp_checker import CppChecker
444 from rules import Rule
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000445
446 added_includes = []
447 for f in input_api.AffectedFiles():
448 if not CppChecker.IsCppFile(f.LocalPath()):
449 continue
450
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200451 changed_lines = [line for _, line in f.ChangedContents()]
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000452 added_includes.append([f.LocalPath(), changed_lines])
453
454 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
455
456 error_descriptions = []
457 warning_descriptions = []
458 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
459 added_includes):
460 description_with_path = '%s\n %s' % (path, rule_description)
461 if rule_type == Rule.DISALLOW:
462 error_descriptions.append(description_with_path)
463 else:
464 warning_descriptions.append(description_with_path)
465
466 results = []
467 if error_descriptions:
468 results.append(output_api.PresubmitError(
kjellandera7066a32017-03-23 03:47:05 -0700469 'You added one or more #includes that violate checkdeps rules.\n'
470 'Check that the DEPS files in these locations contain valid rules.\n'
471 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
472 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000473 error_descriptions))
474 if warning_descriptions:
475 results.append(output_api.PresubmitPromptOrNotify(
476 'You added one or more #includes of files that are temporarily\n'
477 'allowed but being removed. Can you avoid introducing the\n'
kjellandera7066a32017-03-23 03:47:05 -0700478 '#include? See relevant DEPS file(s) for details and contacts.\n'
479 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
480 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000481 warning_descriptions))
482 return results
483
charujain9893e252017-09-14 13:33:22 +0200484def CheckCommitMessageBugEntry(input_api, output_api):
485 """Check that bug entries are well-formed in commit message."""
486 bogus_bug_msg = (
487 'Bogus BUG entry: %s. Please specify the issue tracker prefix and the '
488 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.')
489 results = []
490 for bug in (input_api.change.BUG or '').split(','):
491 bug = bug.strip()
492 if bug.lower() == 'none':
493 continue
charujain81a58c72017-09-25 13:25:45 +0200494 if 'b/' not in bug and ':' not in bug:
charujain9893e252017-09-14 13:33:22 +0200495 try:
496 if int(bug) > 100000:
497 # Rough indicator for current chromium bugs.
498 prefix_guess = 'chromium'
499 else:
500 prefix_guess = 'webrtc'
501 results.append('BUG entry requires issue tracker prefix, e.g. %s:%s' %
502 (prefix_guess, bug))
503 except ValueError:
504 results.append(bogus_bug_msg % bug)
charujain81a58c72017-09-25 13:25:45 +0200505 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
charujain9893e252017-09-14 13:33:22 +0200506 results.append(bogus_bug_msg % bug)
507 return [output_api.PresubmitError(r) for r in results]
508
509def CheckChangeHasBugField(input_api, output_api):
kjellanderd1e26a92016-09-19 08:11:16 -0700510 """Requires that the changelist have a BUG= field.
511
512 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
513 since it fails the presubmit if the BUG= field is missing or doesn't contain
514 a bug reference.
515 """
516 if input_api.change.BUG:
517 return []
518 else:
519 return [output_api.PresubmitError(
520 'The BUG=[bug number] field is mandatory. Please create a bug and '
521 'reference it using either of:\n'
522 ' * https://bugs.webrtc.org - reference it using BUG=webrtc:XXXX\n'
523 ' * https://crbug.com - reference it using BUG=chromium:XXXXXX')]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000524
charujain9893e252017-09-14 13:33:22 +0200525def CheckJSONParseErrors(input_api, output_api):
kjellander569cf942016-02-11 05:02:59 -0800526 """Check that JSON files do not contain syntax errors."""
527
528 def FilterFile(affected_file):
529 return input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
530
531 def GetJSONParseError(input_api, filename):
532 try:
533 contents = input_api.ReadFile(filename)
534 input_api.json.loads(contents)
535 except ValueError as e:
536 return e
537 return None
538
539 results = []
540 for affected_file in input_api.AffectedFiles(
541 file_filter=FilterFile, include_deletes=False):
542 parse_error = GetJSONParseError(input_api,
543 affected_file.AbsoluteLocalPath())
544 if parse_error:
545 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
546 (affected_file.LocalPath(), parse_error)))
547 return results
548
549
charujain9893e252017-09-14 13:33:22 +0200550def RunPythonTests(input_api, output_api):
kjellanderc88b5d52017-04-05 06:42:43 -0700551 def Join(*args):
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200552 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
553
554 test_directories = [
Edward Lemur6d01f6d2017-09-14 17:02:01 +0200555 input_api.PresubmitLocalPath(),
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200556 Join('rtc_tools', 'py_event_log_analyzer'),
557 Join('rtc_tools'),
558 Join('audio', 'test', 'unittests'),
ehmaldonado4fb97462017-01-30 05:27:22 -0800559 ] + [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200560 root for root, _, files in os.walk(Join('tools_webrtc'))
ehmaldonado4fb97462017-01-30 05:27:22 -0800561 if any(f.endswith('_test.py') for f in files)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200562 ]
563
564 tests = []
565 for directory in test_directories:
566 tests.extend(
567 input_api.canned_checks.GetUnitTestsInDirectory(
568 input_api,
569 output_api,
570 directory,
571 whitelist=[r'.+_test\.py$']))
572 return input_api.RunTests(tests, parallel=True)
573
574
charujain9893e252017-09-14 13:33:22 +0200575def CheckUsageOfGoogleProtobufNamespace(input_api, output_api):
mbonadei38415b22017-04-07 05:38:01 -0700576 """Checks that the namespace google::protobuf has not been used."""
577 files = []
578 pattern = input_api.re.compile(r'google::protobuf')
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200579 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
mbonadei38415b22017-04-07 05:38:01 -0700580 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
581 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
582 continue
583 contents = input_api.ReadFile(f)
584 if pattern.search(contents):
585 files.append(f)
586
587 if files:
588 return [output_api.PresubmitError(
589 'Please avoid to use namespace `google::protobuf` directly.\n'
590 'Add a using directive in `%s` and include that header instead.'
591 % proto_utils_path, files)]
592 return []
593
594
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200595def _LicenseHeader(input_api):
596 """Returns the license header regexp."""
597 # Accept any year number from 2003 to the current year
598 current_year = int(input_api.time.strftime('%Y'))
599 allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1)))
600 years_re = '(' + '|'.join(allowed_years) + ')'
601 license_header = (
602 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
603 r'All [Rr]ights [Rr]eserved\.\n'
604 r'.*?\n'
605 r'.*? Use of this source code is governed by a BSD-style license\n'
606 r'.*? that can be found in the LICENSE file in the root of the source\n'
607 r'.*? tree\. An additional intellectual property rights grant can be '
608 r'found\n'
609 r'.*? in the file PATENTS\. All contributing project authors may\n'
610 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
611 ) % {
612 'year': years_re,
613 }
614 return license_header
615
616
charujain9893e252017-09-14 13:33:22 +0200617def CommonChecks(input_api, output_api):
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000618 """Checks common to both upload and commit."""
niklase@google.comda159d62011-05-30 11:51:34 +0000619 results = []
tkchin42f580e2015-11-26 23:18:23 -0800620 # Filter out files that are in objc or ios dirs from being cpplint-ed since
621 # they do not follow C++ lint rules.
622 black_list = input_api.DEFAULT_BLACK_LIST + (
623 r".*\bobjc[\\\/].*",
Kári Tristan Helgason3fa35172016-09-09 08:55:05 +0000624 r".*objc\.[hcm]+$",
tkchin42f580e2015-11-26 23:18:23 -0800625 )
626 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list)
charujain9893e252017-09-14 13:33:22 +0200627 results.extend(CheckApprovedFilesLintClean(
tkchin42f580e2015-11-26 23:18:23 -0800628 input_api, output_api, source_file_filter))
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200629 results.extend(input_api.canned_checks.CheckLicense(
630 input_api, output_api, _LicenseHeader(input_api)))
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000631 results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100632 black_list=(r'^base[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200633 r'^build[\\\/].*\.py$',
634 r'^buildtools[\\\/].*\.py$',
kjellander38c65c82017-04-12 22:43:38 -0700635 r'^infra[\\\/].*\.py$',
Henrik Kjellander0779e8f2016-12-22 12:01:17 +0100636 r'^ios[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200637 r'^out.*[\\\/].*\.py$',
638 r'^testing[\\\/].*\.py$',
639 r'^third_party[\\\/].*\.py$',
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100640 r'^tools[\\\/].*\.py$',
kjellanderafd54942016-12-17 12:21:39 -0800641 # TODO(phoglund): should arguably be checked.
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200642 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
643 r'^tools_webrtc[\\\/]valgrind[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200644 r'^xcodebuild.*[\\\/].*\.py$',),
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200645 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800646
nisse3d21e232016-09-02 03:07:06 -0700647 # TODO(nisse): talk/ is no more, so make below checks simpler?
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200648 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
649 # we need to have different license checks in talk/ and webrtc/ directories.
650 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200651
tkchin3cd9a302016-06-08 12:40:28 -0700652 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
653 # ObjC subdirectories ObjC headers.
654 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100655 # Skip long-lines check for DEPS and GN files.
656 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
tkchin3cd9a302016-06-08 12:40:28 -0700657 eighty_char_sources = lambda x: input_api.FilterSourceFile(x,
658 black_list=build_file_filter_list + objc_filter_list)
659 hundred_char_sources = lambda x: input_api.FilterSourceFile(x,
660 white_list=objc_filter_list)
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000661 results.extend(input_api.canned_checks.CheckLongLines(
tkchin3cd9a302016-06-08 12:40:28 -0700662 input_api, output_api, maxlen=80, source_file_filter=eighty_char_sources))
663 results.extend(input_api.canned_checks.CheckLongLines(
664 input_api, output_api, maxlen=100,
665 source_file_filter=hundred_char_sources))
666
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000667 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
668 input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000669 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
670 input_api, output_api))
kjellandere5dc62a2016-12-14 00:16:21 -0800671 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
672 input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000673 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
674 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200675 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
676 results.extend(CheckNoIOStreamInHeaders(input_api, output_api))
677 results.extend(CheckNoPragmaOnce(input_api, output_api))
678 results.extend(CheckNoFRIEND_TEST(input_api, output_api))
679 results.extend(CheckGnChanges(input_api, output_api))
680 results.extend(CheckUnwantedDependencies(input_api, output_api))
681 results.extend(CheckJSONParseErrors(input_api, output_api))
682 results.extend(RunPythonTests(input_api, output_api))
683 results.extend(CheckUsageOfGoogleProtobufNamespace(input_api, output_api))
Mirko Bonadei866d3372017-09-15 12:35:26 +0200684 results.extend(CheckOrphanHeaders(input_api, output_api))
Mirko Bonadeia730c1c2017-09-18 11:33:13 +0200685 results.extend(CheckNewlineAtTheEndOfProtoFiles(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000686 return results
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000687
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000688
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000689def CheckChangeOnUpload(input_api, output_api):
690 results = []
charujain9893e252017-09-14 13:33:22 +0200691 results.extend(CommonChecks(input_api, output_api))
Oleh Prypin920b6532017-10-05 11:28:51 +0200692 results.extend(CheckGnGen(input_api, output_api))
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200693 results.extend(
694 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
niklase@google.comda159d62011-05-30 11:51:34 +0000695 return results
696
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000697
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000698def CheckChangeOnCommit(input_api, output_api):
niklase@google.com1198db92011-06-09 07:07:24 +0000699 results = []
charujain9893e252017-09-14 13:33:22 +0200700 results.extend(CommonChecks(input_api, output_api))
701 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
niklase@google.com1198db92011-06-09 07:07:24 +0000702 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000703 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
704 input_api, output_api))
705 results.extend(input_api.canned_checks.CheckChangeHasDescription(
706 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200707 results.extend(CheckChangeHasBugField(input_api, output_api))
708 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
kjellander@webrtc.org12cb88c2014-02-13 11:53:43 +0000709 results.extend(input_api.canned_checks.CheckTreeIsOpen(
710 input_api, output_api,
711 json_url='http://webrtc-status.appspot.com/current?format=json'))
niklase@google.com1198db92011-06-09 07:07:24 +0000712 return results
mbonadei74973ed2017-05-09 07:58:05 -0700713
714
charujain9893e252017-09-14 13:33:22 +0200715def CheckOrphanHeaders(input_api, output_api):
mbonadei74973ed2017-05-09 07:58:05 -0700716 # We need to wait until we have an input_api object and use this
717 # roundabout construct to import prebubmit_checks_lib because this file is
718 # eval-ed and thus doesn't have __file__.
719 error_msg = """Header file {} is not listed in any GN target.
720 Please create a target or add it to an existing one in {}"""
721 results = []
Oleh Prypin2f33a562017-10-04 20:17:54 +0200722 with _AddToPath(input_api.os_path.join(
723 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
mbonadei74973ed2017-05-09 07:58:05 -0700724 from check_orphan_headers import GetBuildGnPathFromFilePath
725 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 07:58:05 -0700726
727 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
mbonadeia644ad32017-05-10 05:21:55 -0700728 if f.LocalPath().endswith('.h') and f.Action() == 'A':
mbonadei74973ed2017-05-09 07:58:05 -0700729 file_path = os.path.abspath(f.LocalPath())
730 root_dir = os.getcwd()
731 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
732 root_dir)
733 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
734 if not in_build_gn:
735 results.append(output_api.PresubmitError(error_msg.format(
736 file_path, gn_file_path)))
737 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200738
739
Mirko Bonadeia730c1c2017-09-18 11:33:13 +0200740def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200741 """Checks that all .proto files are terminated with a newline."""
742 error_msg = 'File {} must end with exactly one newline.'
743 results = []
744 source_file_filter = lambda x: input_api.FilterSourceFile(
745 x, white_list=(r'.+\.proto$',))
746 for f in input_api.AffectedSourceFiles(source_file_filter):
747 file_path = f.LocalPath()
748 with open(file_path) as f:
749 lines = f.readlines()
Mirko Bonadeia730c1c2017-09-18 11:33:13 +0200750 if len(lines) > 0 and not lines[-1].endswith('\n'):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200751 results.append(output_api.PresubmitError(error_msg.format(file_path)))
752 return results