blob: aebadb5ae2c3169cabf0db662dd12b2684f41a59 [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
ehmaldonado4fb97462017-01-30 05:27:22 -080012import subprocess
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +000013import sys
Mirko Bonadei0c15c532017-09-19 10:41:02 +020014from collections import defaultdict
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',
25 'modules/audio_conference_mixer',
26 'modules/audio_device',
27 'modules/audio_processing',
28 'modules/desktop_capture',
29 'modules/include/module_common_types.h',
30 'modules/media_file',
31 'modules/utility',
32 'modules/video_capture',
33 'p2p',
34 'pc',
35 'rtc_base',
36 'sdk/android/src/jni',
37 'sdk/objc',
38 'system_wrappers',
39 'test',
Henrik Kjellander90fd7d82017-05-09 08:30:10 +020040 'tools_webrtc',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020041 'voice_engine',
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +010042]
43
jbauchc4e3ead2016-02-19 00:25:55 -080044# These filters will always be removed, even if the caller specifies a filter
45# set, as they are problematic or broken in some way.
46#
47# Justifications for each filter:
48# - build/c++11 : Rvalue ref checks are unreliable (false positives),
49# include file and feature blacklists are
50# google3-specific.
kjellandere5a87a52016-04-27 02:32:12 -070051# - whitespace/operators: Same as above (doesn't seem sufficient to eliminate
52# all move-related errors).
jbauchc4e3ead2016-02-19 00:25:55 -080053BLACKLIST_LINT_FILTERS = [
54 '-build/c++11',
kjellandere5a87a52016-04-27 02:32:12 -070055 '-whitespace/operators',
jbauchc4e3ead2016-02-19 00:25:55 -080056]
57
kjellanderfd595232015-12-04 02:44:09 -080058# List of directories of "supported" native APIs. That means changes to headers
59# will be done in a compatible way following this scheme:
60# 1. Non-breaking changes are made.
61# 2. The old APIs as marked as deprecated (with comments).
62# 3. Deprecation is announced to discuss-webrtc@googlegroups.com and
63# webrtc-users@google.com (internal list).
64# 4. (later) The deprecated APIs are removed.
kjellander53047c92015-12-02 23:56:14 -080065NATIVE_API_DIRS = (
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020066 'api',
67 'media',
68 'modules/audio_device/include',
69 'pc',
kjellanderdd705472016-06-09 11:17:27 -070070)
Mirko Bonadei0c15c532017-09-19 10:41:02 +020071
kjellanderdd705472016-06-09 11:17:27 -070072# These directories should not be used but are maintained only to avoid breaking
73# some legacy downstream code.
74LEGACY_API_DIRS = (
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020075 'common_audio/include',
76 'modules/audio_coding/include',
77 'modules/audio_conference_mixer/include',
78 'modules/audio_processing/include',
79 'modules/bitrate_controller/include',
80 'modules/congestion_controller/include',
81 'modules/include',
82 'modules/remote_bitrate_estimator/include',
83 'modules/rtp_rtcp/include',
84 'modules/rtp_rtcp/source',
85 'modules/utility/include',
86 'modules/video_coding/codecs/h264/include',
87 'modules/video_coding/codecs/i420/include',
88 'modules/video_coding/codecs/vp8/include',
89 'modules/video_coding/codecs/vp9/include',
90 'modules/video_coding/include',
91 'rtc_base',
92 'system_wrappers/include',
93 'voice_engine/include',
kjellander53047c92015-12-02 23:56:14 -080094)
Mirko Bonadei0c15c532017-09-19 10:41:02 +020095
kjellanderdd705472016-06-09 11:17:27 -070096API_DIRS = NATIVE_API_DIRS[:] + LEGACY_API_DIRS[:]
kjellander53047c92015-12-02 23:56:14 -080097
Mirko Bonadei0c15c532017-09-19 10:41:02 +020098# TARGET_RE matches a GN target, and extracts the target name and the contents.
99TARGET_RE = re.compile(r'(?P<indent>\s*)\w+\("(?P<target_name>\w+)"\) {'
100 r'(?P<target_contents>.*?)'
101 r'(?P=indent)}',
102 re.MULTILINE | re.DOTALL)
103
104# SOURCES_RE matches a block of sources inside a GN target.
105SOURCES_RE = re.compile(r'sources \+?= \[(?P<sources>.*?)\]',
106 re.MULTILINE | re.DOTALL)
107
108# FILE_PATH_RE matchies a file path.
109FILE_PATH_RE = re.compile(r'"(?P<file_path>(\w|\/)+)(?P<extension>\.\w+)"')
110
kjellander53047c92015-12-02 23:56:14 -0800111
ehmaldonado4fb97462017-01-30 05:27:22 -0800112def _RunCommand(command, cwd):
113 """Runs a command and returns the output from that command."""
114 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
115 cwd=cwd)
116 stdout = p.stdout.read()
117 stderr = p.stderr.read()
118 p.wait()
119 p.stdout.close()
120 p.stderr.close()
121 return p.returncode, stdout, stderr
122
123
charujain9893e252017-09-14 13:33:22 +0200124def VerifyNativeApiHeadersListIsValid(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800125 """Ensures the list of native API header directories is up to date."""
126 non_existing_paths = []
127 native_api_full_paths = [
128 input_api.os_path.join(input_api.PresubmitLocalPath(),
kjellanderdd705472016-06-09 11:17:27 -0700129 *path.split('/')) for path in API_DIRS]
kjellander53047c92015-12-02 23:56:14 -0800130 for path in native_api_full_paths:
131 if not os.path.isdir(path):
132 non_existing_paths.append(path)
133 if non_existing_paths:
134 return [output_api.PresubmitError(
135 'Directories to native API headers have changed which has made the '
136 'list in PRESUBMIT.py outdated.\nPlease update it to the current '
137 'location of our native APIs.',
138 non_existing_paths)]
139 return []
140
kjellanderc88b5d52017-04-05 06:42:43 -0700141API_CHANGE_MSG = """
kwibergeb133022016-04-07 07:41:48 -0700142You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 04:13:23 -0800143 1. Make compatible changes that don't break existing clients. Usually
144 this is done by keeping the existing method signatures unchanged.
145 2. Mark the old stuff as deprecated (see RTC_DEPRECATED macro).
kwibergeb133022016-04-07 07:41:48 -0700146 3. Create a timeline and plan for when the deprecated stuff will be
147 removed. (The amount of time we give users to change their code
148 should be informed by how much work it is for them. If they just
149 need to replace one name with another or something equally
150 simple, 1-2 weeks might be good; if they need to do serious work,
151 up to 3 months may be called for.)
152 4. Update/inform existing downstream code owners to stop using the
153 deprecated stuff. (Send announcements to
154 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
155 5. Remove the deprecated stuff, once the agreed-upon amount of time
156 has passed.
157Related files:
158"""
kjellander53047c92015-12-02 23:56:14 -0800159
charujain9893e252017-09-14 13:33:22 +0200160def CheckNativeApiHeaderChanges(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800161 """Checks to remind proper changing of native APIs."""
162 files = []
163 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
164 if f.LocalPath().endswith('.h'):
kjellanderdd705472016-06-09 11:17:27 -0700165 for path in API_DIRS:
kjellander53047c92015-12-02 23:56:14 -0800166 if os.path.dirname(f.LocalPath()) == path:
167 files.append(f)
168
169 if files:
kjellanderc88b5d52017-04-05 06:42:43 -0700170 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
kjellander53047c92015-12-02 23:56:14 -0800171 return []
172
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100173
charujain9893e252017-09-14 13:33:22 +0200174def CheckNoIOStreamInHeaders(input_api, output_api):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000175 """Checks to make sure no .h files include <iostream>."""
176 files = []
177 pattern = input_api.re.compile(r'^#include\s*<iostream>',
178 input_api.re.MULTILINE)
179 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
180 if not f.LocalPath().endswith('.h'):
181 continue
182 contents = input_api.ReadFile(f)
183 if pattern.search(contents):
184 files.append(f)
185
186 if len(files):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200187 return [output_api.PresubmitError(
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000188 'Do not #include <iostream> in header files, since it inserts static ' +
189 'initialization into every file including the header. Instead, ' +
190 '#include <ostream>. See http://crbug.com/94794',
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200191 files)]
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000192 return []
193
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000194
charujain9893e252017-09-14 13:33:22 +0200195def CheckNoPragmaOnce(input_api, output_api):
kjellander6aeef742017-02-20 01:13:18 -0800196 """Make sure that banned functions are not used."""
197 files = []
198 pattern = input_api.re.compile(r'^#pragma\s+once',
199 input_api.re.MULTILINE)
200 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
201 if not f.LocalPath().endswith('.h'):
202 continue
203 contents = input_api.ReadFile(f)
204 if pattern.search(contents):
205 files.append(f)
206
207 if files:
208 return [output_api.PresubmitError(
209 'Do not use #pragma once in header files.\n'
210 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
211 files)]
212 return []
213
214
charujain9893e252017-09-14 13:33:22 +0200215def CheckNoFRIEND_TEST(input_api, output_api): # pylint: disable=invalid-name
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000216 """Make sure that gtest's FRIEND_TEST() macro is not used, the
217 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
218 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
219 problems = []
220
221 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h'))
222 for f in input_api.AffectedFiles(file_filter=file_filter):
223 for line_num, line in f.ChangedContents():
224 if 'FRIEND_TEST(' in line:
225 problems.append(' %s:%d' % (f.LocalPath(), line_num))
226
227 if not problems:
228 return []
229 return [output_api.PresubmitPromptWarning('WebRTC\'s code should not use '
230 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and '
231 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
232
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000233
charujain9893e252017-09-14 13:33:22 +0200234def IsLintBlacklisted(blacklist_paths, file_path):
oprypin2aa463f2017-03-23 03:17:02 -0700235 """ Checks if a file is blacklisted for lint check."""
236 for path in blacklist_paths:
237 if file_path == path or os.path.dirname(file_path).startswith(path):
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100238 return True
239 return False
240
241
charujain9893e252017-09-14 13:33:22 +0200242def CheckApprovedFilesLintClean(input_api, output_api,
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000243 source_file_filter=None):
oprypin2aa463f2017-03-23 03:17:02 -0700244 """Checks that all new or non-blacklisted .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 13:33:22 +0200245 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000246 depot_tools/presubmit_canned_checks.py but has less filters and only checks
247 added files."""
248 result = []
249
250 # Initialize cpplint.
251 import cpplint
252 # Access to a protected member _XX of a client class
253 # pylint: disable=W0212
254 cpplint._cpplint_state.ResetErrorCounts()
255
jbauchc4e3ead2016-02-19 00:25:55 -0800256 lint_filters = cpplint._Filters()
257 lint_filters.extend(BLACKLIST_LINT_FILTERS)
258 cpplint._SetFilters(','.join(lint_filters))
259
oprypin2aa463f2017-03-23 03:17:02 -0700260 # Create a platform independent blacklist for cpplint.
261 blacklist_paths = [input_api.os_path.join(*path.split('/'))
262 for path in CPPLINT_BLACKLIST]
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100263
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000264 # Use the strictest verbosity level for cpplint.py (level 1) which is the
oprypin2aa463f2017-03-23 03:17:02 -0700265 # default when running cpplint.py from command line. To make it possible to
266 # work with not-yet-converted code, we're only applying it to new (or
267 # moved/renamed) files and files not listed in CPPLINT_BLACKLIST.
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000268 verbosity_level = 1
269 files = []
270 for f in input_api.AffectedSourceFiles(source_file_filter):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200271 # Note that moved/renamed files also count as added.
charujain9893e252017-09-14 13:33:22 +0200272 if f.Action() == 'A' or not IsLintBlacklisted(blacklist_paths,
oprypin2aa463f2017-03-23 03:17:02 -0700273 f.LocalPath()):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000274 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000275
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000276 for file_name in files:
277 cpplint.ProcessFile(file_name, verbosity_level)
278
279 if cpplint._cpplint_state.error_count > 0:
280 if input_api.is_committing:
oprypin8e58d652017-03-21 07:52:41 -0700281 res_type = output_api.PresubmitError
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000282 else:
283 res_type = output_api.PresubmitPromptWarning
284 result = [res_type('Changelist failed cpplint.py check.')]
285
286 return result
287
charujain9893e252017-09-14 13:33:22 +0200288def CheckNoSourcesAbove(input_api, gn_files, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700289 # Disallow referencing source files with paths above the GN file location.
290 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
291 re.MULTILINE | re.DOTALL)
292 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
293 violating_gn_files = set()
294 violating_source_entries = []
295 for gn_file in gn_files:
296 contents = input_api.ReadFile(gn_file)
297 for source_block_match in source_pattern.finditer(contents):
298 # Find all source list entries starting with ../ in the source block
299 # (exclude overrides entries).
300 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
301 source_file = file_list_match.group(1)
302 if 'overrides/' not in source_file:
303 violating_source_entries.append(source_file)
304 violating_gn_files.add(gn_file)
305 if violating_gn_files:
306 return [output_api.PresubmitError(
307 'Referencing source files above the directory of the GN file is not '
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100308 'allowed. Please introduce new GN targets in the proper location '
309 'instead.\n'
ehmaldonado5b1ba082016-09-02 05:51:08 -0700310 'Invalid source entries:\n'
311 '%s\n'
312 'Violating GN files:' % '\n'.join(violating_source_entries),
313 items=violating_gn_files)]
314 return []
315
Mirko Bonadei0c15c532017-09-19 10:41:02 +0200316def CheckNoMixingSources(input_api, gn_files, output_api):
317 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
318
319 See bugs.webrtc.org/7743 for more context.
320 """
321 def _MoreThanOneSourceUsed(*sources_lists):
322 sources_used = 0
323 for source_list in sources_lists:
324 if len(source_list):
325 sources_used += 1
326 return sources_used > 1
327
328 errors = defaultdict(lambda: [])
kjellander7439f972016-12-05 22:47:46 -0800329 for gn_file in gn_files:
Mirko Bonadei0c15c532017-09-19 10:41:02 +0200330 gn_file_content = input_api.ReadFile(gn_file)
331 for target_match in TARGET_RE.finditer(gn_file_content):
kjellander7439f972016-12-05 22:47:46 -0800332 c_files = []
333 cc_files = []
Mirko Bonadei0c15c532017-09-19 10:41:02 +0200334 objc_files = []
335 target_name = target_match.group('target_name')
336 target_contents = target_match.group('target_contents')
337 for sources_match in SOURCES_RE.finditer(target_contents):
338 for file_match in FILE_PATH_RE.finditer(sources_match.group(1)):
339 file_path = file_match.group('file_path')
340 extension = file_match.group('extension')
341 if extension == '.c':
342 c_files.append(file_path + extension)
343 if extension == '.cc':
344 cc_files.append(file_path + extension)
345 if extension in ['.m', '.mm']:
346 objc_files.append(file_path + extension)
347 if _MoreThanOneSourceUsed(c_files, cc_files, objc_files):
348 all_sources = sorted(c_files + cc_files + objc_files)
349 errors[gn_file.LocalPath()].append((target_name, all_sources))
350 if errors:
kjellander7439f972016-12-05 22:47:46 -0800351 return [output_api.PresubmitError(
Mirko Bonadei0c15c532017-09-19 10:41:02 +0200352 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n'
353 'Please create a separate target for each collection of sources.\n'
kjellander7439f972016-12-05 22:47:46 -0800354 'Mixed sources: \n'
355 '%s\n'
Mirko Bonadei0c15c532017-09-19 10:41:02 +0200356 'Violating GN files:\n%s\n' % (json.dumps(errors, indent=2),
357 '\n'.join(errors.keys())))]
kjellander7439f972016-12-05 22:47:46 -0800358 return []
359
charujain9893e252017-09-14 13:33:22 +0200360def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
ehmaldonado4fb97462017-01-30 05:27:22 -0800361 cwd = input_api.PresubmitLocalPath()
mbonadeiab587dc2017-05-12 04:13:31 -0700362 script_path = os.path.join('tools_webrtc', 'presubmit_checks_lib',
363 'check_package_boundaries.py')
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200364 command = [sys.executable, script_path]
ehmaldonado4fb97462017-01-30 05:27:22 -0800365 command += [gn_file.LocalPath() for gn_file in gn_files]
366 returncode, _, stderr = _RunCommand(command, cwd)
367 if returncode:
368 return [output_api.PresubmitError(
369 'There are package boundary violations in the following GN files:\n\n'
370 '%s' % stderr)]
371 return []
372
charujain9893e252017-09-14 13:33:22 +0200373def CheckGnChanges(input_api, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700374 source_file_filter = lambda x: input_api.FilterSourceFile(
375 x, white_list=(r'.+\.(gn|gni)$',))
376
377 gn_files = []
378 for f in input_api.AffectedSourceFiles(source_file_filter):
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200379 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 05:51:08 -0700380
381 result = []
382 if gn_files:
charujain9893e252017-09-14 13:33:22 +0200383 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
Mirko Bonadei0c15c532017-09-19 10:41:02 +0200384 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
385 result.extend(CheckNoPackageBoundaryViolations(input_api, gn_files,
386 output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700387 return result
388
charujain9893e252017-09-14 13:33:22 +0200389def CheckUnwantedDependencies(input_api, output_api):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000390 """Runs checkdeps on #include statements added in this
391 change. Breaking - rules is an error, breaking ! rules is a
392 warning.
393 """
394 # Copied from Chromium's src/PRESUBMIT.py.
395
396 # We need to wait until we have an input_api object and use this
397 # roundabout construct to import checkdeps because this file is
398 # eval-ed and thus doesn't have __file__.
399 original_sys_path = sys.path
400 try:
kjellander@webrtc.orgaefe61a2014-12-08 13:00:30 +0000401 checkdeps_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
402 'buildtools', 'checkdeps')
403 if not os.path.exists(checkdeps_path):
404 return [output_api.PresubmitError(
405 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
406 'download Chromium and setup the symlinks?' % checkdeps_path)]
407 sys.path.append(checkdeps_path)
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000408 import checkdeps
409 from cpp_checker import CppChecker
410 from rules import Rule
411 finally:
412 # Restore sys.path to what it was before.
413 sys.path = original_sys_path
414
415 added_includes = []
416 for f in input_api.AffectedFiles():
417 if not CppChecker.IsCppFile(f.LocalPath()):
418 continue
419
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200420 changed_lines = [line for _, line in f.ChangedContents()]
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000421 added_includes.append([f.LocalPath(), changed_lines])
422
423 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
424
425 error_descriptions = []
426 warning_descriptions = []
427 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
428 added_includes):
429 description_with_path = '%s\n %s' % (path, rule_description)
430 if rule_type == Rule.DISALLOW:
431 error_descriptions.append(description_with_path)
432 else:
433 warning_descriptions.append(description_with_path)
434
435 results = []
436 if error_descriptions:
437 results.append(output_api.PresubmitError(
kjellandera7066a32017-03-23 03:47:05 -0700438 'You added one or more #includes that violate checkdeps rules.\n'
439 'Check that the DEPS files in these locations contain valid rules.\n'
440 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
441 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000442 error_descriptions))
443 if warning_descriptions:
444 results.append(output_api.PresubmitPromptOrNotify(
445 'You added one or more #includes of files that are temporarily\n'
446 'allowed but being removed. Can you avoid introducing the\n'
kjellandera7066a32017-03-23 03:47:05 -0700447 '#include? See relevant DEPS file(s) for details and contacts.\n'
448 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
449 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000450 warning_descriptions))
451 return results
452
charujain9893e252017-09-14 13:33:22 +0200453def CheckCommitMessageBugEntry(input_api, output_api):
454 """Check that bug entries are well-formed in commit message."""
455 bogus_bug_msg = (
456 'Bogus BUG entry: %s. Please specify the issue tracker prefix and the '
457 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.')
458 results = []
459 for bug in (input_api.change.BUG or '').split(','):
460 bug = bug.strip()
461 if bug.lower() == 'none':
462 continue
463 if ':' not in bug:
464 try:
465 if int(bug) > 100000:
466 # Rough indicator for current chromium bugs.
467 prefix_guess = 'chromium'
468 else:
469 prefix_guess = 'webrtc'
470 results.append('BUG entry requires issue tracker prefix, e.g. %s:%s' %
471 (prefix_guess, bug))
472 except ValueError:
473 results.append(bogus_bug_msg % bug)
474 elif not re.match(r'\w+:\d+', bug):
475 results.append(bogus_bug_msg % bug)
476 return [output_api.PresubmitError(r) for r in results]
477
478def CheckChangeHasBugField(input_api, output_api):
kjellanderd1e26a92016-09-19 08:11:16 -0700479 """Requires that the changelist have a BUG= field.
480
481 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
482 since it fails the presubmit if the BUG= field is missing or doesn't contain
483 a bug reference.
484 """
485 if input_api.change.BUG:
486 return []
487 else:
488 return [output_api.PresubmitError(
489 'The BUG=[bug number] field is mandatory. Please create a bug and '
490 'reference it using either of:\n'
491 ' * https://bugs.webrtc.org - reference it using BUG=webrtc:XXXX\n'
492 ' * https://crbug.com - reference it using BUG=chromium:XXXXXX')]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000493
charujain9893e252017-09-14 13:33:22 +0200494def CheckJSONParseErrors(input_api, output_api):
kjellander569cf942016-02-11 05:02:59 -0800495 """Check that JSON files do not contain syntax errors."""
496
497 def FilterFile(affected_file):
498 return input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
499
500 def GetJSONParseError(input_api, filename):
501 try:
502 contents = input_api.ReadFile(filename)
503 input_api.json.loads(contents)
504 except ValueError as e:
505 return e
506 return None
507
508 results = []
509 for affected_file in input_api.AffectedFiles(
510 file_filter=FilterFile, include_deletes=False):
511 parse_error = GetJSONParseError(input_api,
512 affected_file.AbsoluteLocalPath())
513 if parse_error:
514 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
515 (affected_file.LocalPath(), parse_error)))
516 return results
517
518
charujain9893e252017-09-14 13:33:22 +0200519def RunPythonTests(input_api, output_api):
kjellanderc88b5d52017-04-05 06:42:43 -0700520 def Join(*args):
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200521 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
522
523 test_directories = [
Edward Lemur6d01f6d2017-09-14 17:02:01 +0200524 input_api.PresubmitLocalPath(),
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200525 Join('rtc_tools', 'py_event_log_analyzer'),
526 Join('rtc_tools'),
527 Join('audio', 'test', 'unittests'),
ehmaldonado4fb97462017-01-30 05:27:22 -0800528 ] + [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200529 root for root, _, files in os.walk(Join('tools_webrtc'))
ehmaldonado4fb97462017-01-30 05:27:22 -0800530 if any(f.endswith('_test.py') for f in files)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200531 ]
532
533 tests = []
534 for directory in test_directories:
535 tests.extend(
536 input_api.canned_checks.GetUnitTestsInDirectory(
537 input_api,
538 output_api,
539 directory,
540 whitelist=[r'.+_test\.py$']))
541 return input_api.RunTests(tests, parallel=True)
542
543
charujain9893e252017-09-14 13:33:22 +0200544def CheckUsageOfGoogleProtobufNamespace(input_api, output_api):
mbonadei38415b22017-04-07 05:38:01 -0700545 """Checks that the namespace google::protobuf has not been used."""
546 files = []
547 pattern = input_api.re.compile(r'google::protobuf')
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200548 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
mbonadei38415b22017-04-07 05:38:01 -0700549 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
550 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
551 continue
552 contents = input_api.ReadFile(f)
553 if pattern.search(contents):
554 files.append(f)
555
556 if files:
557 return [output_api.PresubmitError(
558 'Please avoid to use namespace `google::protobuf` directly.\n'
559 'Add a using directive in `%s` and include that header instead.'
560 % proto_utils_path, files)]
561 return []
562
563
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200564def _LicenseHeader(input_api):
565 """Returns the license header regexp."""
566 # Accept any year number from 2003 to the current year
567 current_year = int(input_api.time.strftime('%Y'))
568 allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1)))
569 years_re = '(' + '|'.join(allowed_years) + ')'
570 license_header = (
571 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
572 r'All [Rr]ights [Rr]eserved\.\n'
573 r'.*?\n'
574 r'.*? Use of this source code is governed by a BSD-style license\n'
575 r'.*? that can be found in the LICENSE file in the root of the source\n'
576 r'.*? tree\. An additional intellectual property rights grant can be '
577 r'found\n'
578 r'.*? in the file PATENTS\. All contributing project authors may\n'
579 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
580 ) % {
581 'year': years_re,
582 }
583 return license_header
584
585
charujain9893e252017-09-14 13:33:22 +0200586def CommonChecks(input_api, output_api):
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000587 """Checks common to both upload and commit."""
niklase@google.comda159d62011-05-30 11:51:34 +0000588 results = []
tkchin42f580e2015-11-26 23:18:23 -0800589 # Filter out files that are in objc or ios dirs from being cpplint-ed since
590 # they do not follow C++ lint rules.
591 black_list = input_api.DEFAULT_BLACK_LIST + (
592 r".*\bobjc[\\\/].*",
Kári Tristan Helgason3fa35172016-09-09 08:55:05 +0000593 r".*objc\.[hcm]+$",
tkchin42f580e2015-11-26 23:18:23 -0800594 )
595 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list)
charujain9893e252017-09-14 13:33:22 +0200596 results.extend(CheckApprovedFilesLintClean(
tkchin42f580e2015-11-26 23:18:23 -0800597 input_api, output_api, source_file_filter))
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200598 results.extend(input_api.canned_checks.CheckLicense(
599 input_api, output_api, _LicenseHeader(input_api)))
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000600 results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100601 black_list=(r'^base[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200602 r'^build[\\\/].*\.py$',
603 r'^buildtools[\\\/].*\.py$',
kjellander38c65c82017-04-12 22:43:38 -0700604 r'^infra[\\\/].*\.py$',
Henrik Kjellander0779e8f2016-12-22 12:01:17 +0100605 r'^ios[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200606 r'^out.*[\\\/].*\.py$',
607 r'^testing[\\\/].*\.py$',
608 r'^third_party[\\\/].*\.py$',
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100609 r'^tools[\\\/].*\.py$',
kjellanderafd54942016-12-17 12:21:39 -0800610 # TODO(phoglund): should arguably be checked.
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200611 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
612 r'^tools_webrtc[\\\/]valgrind[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200613 r'^xcodebuild.*[\\\/].*\.py$',),
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200614 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800615
nisse3d21e232016-09-02 03:07:06 -0700616 # TODO(nisse): talk/ is no more, so make below checks simpler?
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200617 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
618 # we need to have different license checks in talk/ and webrtc/ directories.
619 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200620
tkchin3cd9a302016-06-08 12:40:28 -0700621 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
622 # ObjC subdirectories ObjC headers.
623 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100624 # Skip long-lines check for DEPS and GN files.
625 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
tkchin3cd9a302016-06-08 12:40:28 -0700626 eighty_char_sources = lambda x: input_api.FilterSourceFile(x,
627 black_list=build_file_filter_list + objc_filter_list)
628 hundred_char_sources = lambda x: input_api.FilterSourceFile(x,
629 white_list=objc_filter_list)
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000630 results.extend(input_api.canned_checks.CheckLongLines(
tkchin3cd9a302016-06-08 12:40:28 -0700631 input_api, output_api, maxlen=80, source_file_filter=eighty_char_sources))
632 results.extend(input_api.canned_checks.CheckLongLines(
633 input_api, output_api, maxlen=100,
634 source_file_filter=hundred_char_sources))
635
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000636 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
637 input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000638 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
639 input_api, output_api))
kjellandere5dc62a2016-12-14 00:16:21 -0800640 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
641 input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000642 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
643 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200644 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
645 results.extend(CheckNoIOStreamInHeaders(input_api, output_api))
646 results.extend(CheckNoPragmaOnce(input_api, output_api))
647 results.extend(CheckNoFRIEND_TEST(input_api, output_api))
648 results.extend(CheckGnChanges(input_api, output_api))
649 results.extend(CheckUnwantedDependencies(input_api, output_api))
650 results.extend(CheckJSONParseErrors(input_api, output_api))
651 results.extend(RunPythonTests(input_api, output_api))
652 results.extend(CheckUsageOfGoogleProtobufNamespace(input_api, output_api))
Mirko Bonadei866d3372017-09-15 12:35:26 +0200653 results.extend(CheckOrphanHeaders(input_api, output_api))
Mirko Bonadeia730c1c2017-09-18 11:33:13 +0200654 results.extend(CheckNewlineAtTheEndOfProtoFiles(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000655 return results
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000656
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000657
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000658def CheckChangeOnUpload(input_api, output_api):
659 results = []
charujain9893e252017-09-14 13:33:22 +0200660 results.extend(CommonChecks(input_api, output_api))
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200661 results.extend(
662 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
niklase@google.comda159d62011-05-30 11:51:34 +0000663 return results
664
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000665
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000666def CheckChangeOnCommit(input_api, output_api):
niklase@google.com1198db92011-06-09 07:07:24 +0000667 results = []
charujain9893e252017-09-14 13:33:22 +0200668 results.extend(CommonChecks(input_api, output_api))
669 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
niklase@google.com1198db92011-06-09 07:07:24 +0000670 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000671 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
672 input_api, output_api))
673 results.extend(input_api.canned_checks.CheckChangeHasDescription(
674 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200675 results.extend(CheckChangeHasBugField(input_api, output_api))
676 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
kjellander@webrtc.org12cb88c2014-02-13 11:53:43 +0000677 results.extend(input_api.canned_checks.CheckTreeIsOpen(
678 input_api, output_api,
679 json_url='http://webrtc-status.appspot.com/current?format=json'))
niklase@google.com1198db92011-06-09 07:07:24 +0000680 return results
mbonadei74973ed2017-05-09 07:58:05 -0700681
682
charujain9893e252017-09-14 13:33:22 +0200683def CheckOrphanHeaders(input_api, output_api):
mbonadei74973ed2017-05-09 07:58:05 -0700684 # We need to wait until we have an input_api object and use this
685 # roundabout construct to import prebubmit_checks_lib because this file is
686 # eval-ed and thus doesn't have __file__.
687 error_msg = """Header file {} is not listed in any GN target.
688 Please create a target or add it to an existing one in {}"""
689 results = []
690 original_sys_path = sys.path
691 try:
692 sys.path = sys.path + [input_api.os_path.join(
693 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')]
694 from check_orphan_headers import GetBuildGnPathFromFilePath
695 from check_orphan_headers import IsHeaderInBuildGn
696 finally:
697 # Restore sys.path to what it was before.
698 sys.path = original_sys_path
699
700 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
mbonadeia644ad32017-05-10 05:21:55 -0700701 if f.LocalPath().endswith('.h') and f.Action() == 'A':
mbonadei74973ed2017-05-09 07:58:05 -0700702 file_path = os.path.abspath(f.LocalPath())
703 root_dir = os.getcwd()
704 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
705 root_dir)
706 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
707 if not in_build_gn:
708 results.append(output_api.PresubmitError(error_msg.format(
709 file_path, gn_file_path)))
710 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200711
712
Mirko Bonadeia730c1c2017-09-18 11:33:13 +0200713def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200714 """Checks that all .proto files are terminated with a newline."""
715 error_msg = 'File {} must end with exactly one newline.'
716 results = []
717 source_file_filter = lambda x: input_api.FilterSourceFile(
718 x, white_list=(r'.+\.proto$',))
719 for f in input_api.AffectedSourceFiles(source_file_filter):
720 file_path = f.LocalPath()
721 with open(file_path) as f:
722 lines = f.readlines()
Mirko Bonadeia730c1c2017-09-18 11:33:13 +0200723 if len(lines) > 0 and not lines[-1].endswith('\n'):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200724 results.append(output_api.PresubmitError(error_msg.format(file_path)))
725 return results