blob: c8ac1656a5db2069f8494d6257bb88175d0340db [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 Bonadei4dc4e252017-09-19 13:49:16 +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',
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
ehmaldonado4fb97462017-01-30 05:27:22 -0800110def _RunCommand(command, cwd):
111 """Runs a command and returns the output from that command."""
112 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
113 cwd=cwd)
114 stdout = p.stdout.read()
115 stderr = p.stderr.read()
116 p.wait()
117 p.stdout.close()
118 p.stderr.close()
119 return p.returncode, stdout, stderr
120
121
charujain9893e252017-09-14 13:33:22 +0200122def VerifyNativeApiHeadersListIsValid(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800123 """Ensures the list of native API header directories is up to date."""
124 non_existing_paths = []
125 native_api_full_paths = [
126 input_api.os_path.join(input_api.PresubmitLocalPath(),
kjellanderdd705472016-06-09 11:17:27 -0700127 *path.split('/')) for path in API_DIRS]
kjellander53047c92015-12-02 23:56:14 -0800128 for path in native_api_full_paths:
129 if not os.path.isdir(path):
130 non_existing_paths.append(path)
131 if non_existing_paths:
132 return [output_api.PresubmitError(
133 'Directories to native API headers have changed which has made the '
134 'list in PRESUBMIT.py outdated.\nPlease update it to the current '
135 'location of our native APIs.',
136 non_existing_paths)]
137 return []
138
kjellanderc88b5d52017-04-05 06:42:43 -0700139API_CHANGE_MSG = """
kwibergeb133022016-04-07 07:41:48 -0700140You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 04:13:23 -0800141 1. Make compatible changes that don't break existing clients. Usually
142 this is done by keeping the existing method signatures unchanged.
143 2. Mark the old stuff as deprecated (see RTC_DEPRECATED macro).
kwibergeb133022016-04-07 07:41:48 -0700144 3. Create a timeline and plan for when the deprecated stuff will be
145 removed. (The amount of time we give users to change their code
146 should be informed by how much work it is for them. If they just
147 need to replace one name with another or something equally
148 simple, 1-2 weeks might be good; if they need to do serious work,
149 up to 3 months may be called for.)
150 4. Update/inform existing downstream code owners to stop using the
151 deprecated stuff. (Send announcements to
152 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
153 5. Remove the deprecated stuff, once the agreed-upon amount of time
154 has passed.
155Related files:
156"""
kjellander53047c92015-12-02 23:56:14 -0800157
charujain9893e252017-09-14 13:33:22 +0200158def CheckNativeApiHeaderChanges(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800159 """Checks to remind proper changing of native APIs."""
160 files = []
161 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
162 if f.LocalPath().endswith('.h'):
kjellanderdd705472016-06-09 11:17:27 -0700163 for path in API_DIRS:
kjellander53047c92015-12-02 23:56:14 -0800164 if os.path.dirname(f.LocalPath()) == path:
165 files.append(f)
166
167 if files:
kjellanderc88b5d52017-04-05 06:42:43 -0700168 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
kjellander53047c92015-12-02 23:56:14 -0800169 return []
170
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100171
charujain9893e252017-09-14 13:33:22 +0200172def CheckNoIOStreamInHeaders(input_api, output_api):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000173 """Checks to make sure no .h files include <iostream>."""
174 files = []
175 pattern = input_api.re.compile(r'^#include\s*<iostream>',
176 input_api.re.MULTILINE)
177 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
178 if not f.LocalPath().endswith('.h'):
179 continue
180 contents = input_api.ReadFile(f)
181 if pattern.search(contents):
182 files.append(f)
183
184 if len(files):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200185 return [output_api.PresubmitError(
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000186 'Do not #include <iostream> in header files, since it inserts static ' +
187 'initialization into every file including the header. Instead, ' +
188 '#include <ostream>. See http://crbug.com/94794',
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200189 files)]
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000190 return []
191
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000192
charujain9893e252017-09-14 13:33:22 +0200193def CheckNoPragmaOnce(input_api, output_api):
kjellander6aeef742017-02-20 01:13:18 -0800194 """Make sure that banned functions are not used."""
195 files = []
196 pattern = input_api.re.compile(r'^#pragma\s+once',
197 input_api.re.MULTILINE)
198 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
199 if not f.LocalPath().endswith('.h'):
200 continue
201 contents = input_api.ReadFile(f)
202 if pattern.search(contents):
203 files.append(f)
204
205 if files:
206 return [output_api.PresubmitError(
207 'Do not use #pragma once in header files.\n'
208 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
209 files)]
210 return []
211
212
charujain9893e252017-09-14 13:33:22 +0200213def CheckNoFRIEND_TEST(input_api, output_api): # pylint: disable=invalid-name
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000214 """Make sure that gtest's FRIEND_TEST() macro is not used, the
215 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
216 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
217 problems = []
218
219 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h'))
220 for f in input_api.AffectedFiles(file_filter=file_filter):
221 for line_num, line in f.ChangedContents():
222 if 'FRIEND_TEST(' in line:
223 problems.append(' %s:%d' % (f.LocalPath(), line_num))
224
225 if not problems:
226 return []
227 return [output_api.PresubmitPromptWarning('WebRTC\'s code should not use '
228 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and '
229 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
230
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000231
charujain9893e252017-09-14 13:33:22 +0200232def IsLintBlacklisted(blacklist_paths, file_path):
oprypin2aa463f2017-03-23 03:17:02 -0700233 """ Checks if a file is blacklisted for lint check."""
234 for path in blacklist_paths:
235 if file_path == path or os.path.dirname(file_path).startswith(path):
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100236 return True
237 return False
238
239
charujain9893e252017-09-14 13:33:22 +0200240def CheckApprovedFilesLintClean(input_api, output_api,
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000241 source_file_filter=None):
oprypin2aa463f2017-03-23 03:17:02 -0700242 """Checks that all new or non-blacklisted .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 13:33:22 +0200243 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000244 depot_tools/presubmit_canned_checks.py but has less filters and only checks
245 added files."""
246 result = []
247
248 # Initialize cpplint.
249 import cpplint
250 # Access to a protected member _XX of a client class
251 # pylint: disable=W0212
252 cpplint._cpplint_state.ResetErrorCounts()
253
jbauchc4e3ead2016-02-19 00:25:55 -0800254 lint_filters = cpplint._Filters()
255 lint_filters.extend(BLACKLIST_LINT_FILTERS)
256 cpplint._SetFilters(','.join(lint_filters))
257
oprypin2aa463f2017-03-23 03:17:02 -0700258 # Create a platform independent blacklist for cpplint.
259 blacklist_paths = [input_api.os_path.join(*path.split('/'))
260 for path in CPPLINT_BLACKLIST]
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100261
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000262 # Use the strictest verbosity level for cpplint.py (level 1) which is the
oprypin2aa463f2017-03-23 03:17:02 -0700263 # default when running cpplint.py from command line. To make it possible to
264 # work with not-yet-converted code, we're only applying it to new (or
265 # moved/renamed) files and files not listed in CPPLINT_BLACKLIST.
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000266 verbosity_level = 1
267 files = []
268 for f in input_api.AffectedSourceFiles(source_file_filter):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200269 # Note that moved/renamed files also count as added.
charujain9893e252017-09-14 13:33:22 +0200270 if f.Action() == 'A' or not IsLintBlacklisted(blacklist_paths,
oprypin2aa463f2017-03-23 03:17:02 -0700271 f.LocalPath()):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000272 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000273
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000274 for file_name in files:
275 cpplint.ProcessFile(file_name, verbosity_level)
276
277 if cpplint._cpplint_state.error_count > 0:
278 if input_api.is_committing:
oprypin8e58d652017-03-21 07:52:41 -0700279 res_type = output_api.PresubmitError
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000280 else:
281 res_type = output_api.PresubmitPromptWarning
282 result = [res_type('Changelist failed cpplint.py check.')]
283
284 return result
285
charujain9893e252017-09-14 13:33:22 +0200286def CheckNoSourcesAbove(input_api, gn_files, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700287 # Disallow referencing source files with paths above the GN file location.
288 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
289 re.MULTILINE | re.DOTALL)
290 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
291 violating_gn_files = set()
292 violating_source_entries = []
293 for gn_file in gn_files:
294 contents = input_api.ReadFile(gn_file)
295 for source_block_match in source_pattern.finditer(contents):
296 # Find all source list entries starting with ../ in the source block
297 # (exclude overrides entries).
298 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
299 source_file = file_list_match.group(1)
300 if 'overrides/' not in source_file:
301 violating_source_entries.append(source_file)
302 violating_gn_files.add(gn_file)
303 if violating_gn_files:
304 return [output_api.PresubmitError(
305 'Referencing source files above the directory of the GN file is not '
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100306 'allowed. Please introduce new GN targets in the proper location '
307 'instead.\n'
ehmaldonado5b1ba082016-09-02 05:51:08 -0700308 'Invalid source entries:\n'
309 '%s\n'
310 'Violating GN files:' % '\n'.join(violating_source_entries),
311 items=violating_gn_files)]
312 return []
313
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200314def CheckNoMixingSources(input_api, gn_files, output_api):
315 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
316
317 See bugs.webrtc.org/7743 for more context.
318 """
319 def _MoreThanOneSourceUsed(*sources_lists):
320 sources_used = 0
321 for source_list in sources_lists:
322 if len(source_list):
323 sources_used += 1
324 return sources_used > 1
325
326 errors = defaultdict(lambda: [])
kjellander7439f972016-12-05 22:47:46 -0800327 for gn_file in gn_files:
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200328 gn_file_content = input_api.ReadFile(gn_file)
329 for target_match in TARGET_RE.finditer(gn_file_content):
330 # list_of_sources is a list of tuples of the form
331 # (c_files, cc_files, objc_files) that keeps track of all the sources
332 # defined in a target. A GN target can have more that on definition of
333 # sources (since it supports if/else statements).
334 # E.g.:
335 # rtc_static_library("foo") {
336 # if (is_win) {
337 # sources = [ "foo.cc" ]
338 # } else {
339 # sources = [ "foo.mm" ]
340 # }
341 # }
342 # This is allowed and the presubmit check should support this case.
343 list_of_sources = []
kjellander7439f972016-12-05 22:47:46 -0800344 c_files = []
345 cc_files = []
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200346 objc_files = []
347 target_name = target_match.group('target_name')
348 target_contents = target_match.group('target_contents')
349 for sources_match in SOURCES_RE.finditer(target_contents):
350 if '+=' not in sources_match.group(0):
351 if c_files or cc_files or objc_files:
352 list_of_sources.append((c_files, cc_files, objc_files))
353 c_files = []
354 cc_files = []
355 objc_files = []
356 for file_match in FILE_PATH_RE.finditer(sources_match.group(1)):
357 file_path = file_match.group('file_path')
358 extension = file_match.group('extension')
359 if extension == '.c':
360 c_files.append(file_path + extension)
361 if extension == '.cc':
362 cc_files.append(file_path + extension)
363 if extension in ['.m', '.mm']:
364 objc_files.append(file_path + extension)
365 list_of_sources.append((c_files, cc_files, objc_files))
366 for c_files_list, cc_files_list, objc_files_list in list_of_sources:
367 if _MoreThanOneSourceUsed(c_files_list, cc_files_list, objc_files_list):
368 all_sources = sorted(c_files_list + cc_files_list + objc_files_list)
369 errors[gn_file.LocalPath()].append((target_name, all_sources))
370 if errors:
kjellander7439f972016-12-05 22:47:46 -0800371 return [output_api.PresubmitError(
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200372 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n'
373 'Please create a separate target for each collection of sources.\n'
kjellander7439f972016-12-05 22:47:46 -0800374 'Mixed sources: \n'
375 '%s\n'
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200376 'Violating GN files:\n%s\n' % (json.dumps(errors, indent=2),
377 '\n'.join(errors.keys())))]
kjellander7439f972016-12-05 22:47:46 -0800378 return []
379
charujain9893e252017-09-14 13:33:22 +0200380def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
ehmaldonado4fb97462017-01-30 05:27:22 -0800381 cwd = input_api.PresubmitLocalPath()
mbonadeiab587dc2017-05-12 04:13:31 -0700382 script_path = os.path.join('tools_webrtc', 'presubmit_checks_lib',
383 'check_package_boundaries.py')
Oleh Prypinafe01652017-10-04 15:56:08 +0200384 command = [sys.executable, script_path, cwd]
385 command += [os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files]
ehmaldonado4fb97462017-01-30 05:27:22 -0800386 returncode, _, stderr = _RunCommand(command, cwd)
387 if returncode:
388 return [output_api.PresubmitError(
389 'There are package boundary violations in the following GN files:\n\n'
390 '%s' % stderr)]
391 return []
392
charujain9893e252017-09-14 13:33:22 +0200393def CheckGnChanges(input_api, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700394 source_file_filter = lambda x: input_api.FilterSourceFile(
Oleh Prypinafe01652017-10-04 15:56:08 +0200395 x, white_list=(r'.+\.(gn|gni)$',),
396 black_list=r'.*/presubmit_checks_lib/testdata/.*')
ehmaldonado5b1ba082016-09-02 05:51:08 -0700397
398 gn_files = []
399 for f in input_api.AffectedSourceFiles(source_file_filter):
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200400 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 05:51:08 -0700401
402 result = []
403 if gn_files:
charujain9893e252017-09-14 13:33:22 +0200404 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200405 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
406 result.extend(CheckNoPackageBoundaryViolations(input_api, gn_files,
407 output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700408 return result
409
charujain9893e252017-09-14 13:33:22 +0200410def CheckUnwantedDependencies(input_api, output_api):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000411 """Runs checkdeps on #include statements added in this
412 change. Breaking - rules is an error, breaking ! rules is a
413 warning.
414 """
415 # Copied from Chromium's src/PRESUBMIT.py.
416
417 # We need to wait until we have an input_api object and use this
418 # roundabout construct to import checkdeps because this file is
419 # eval-ed and thus doesn't have __file__.
420 original_sys_path = sys.path
421 try:
kjellander@webrtc.orgaefe61a2014-12-08 13:00:30 +0000422 checkdeps_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
423 'buildtools', 'checkdeps')
424 if not os.path.exists(checkdeps_path):
425 return [output_api.PresubmitError(
426 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
427 'download Chromium and setup the symlinks?' % checkdeps_path)]
428 sys.path.append(checkdeps_path)
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000429 import checkdeps
430 from cpp_checker import CppChecker
431 from rules import Rule
432 finally:
433 # Restore sys.path to what it was before.
434 sys.path = original_sys_path
435
436 added_includes = []
437 for f in input_api.AffectedFiles():
438 if not CppChecker.IsCppFile(f.LocalPath()):
439 continue
440
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200441 changed_lines = [line for _, line in f.ChangedContents()]
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000442 added_includes.append([f.LocalPath(), changed_lines])
443
444 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
445
446 error_descriptions = []
447 warning_descriptions = []
448 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
449 added_includes):
450 description_with_path = '%s\n %s' % (path, rule_description)
451 if rule_type == Rule.DISALLOW:
452 error_descriptions.append(description_with_path)
453 else:
454 warning_descriptions.append(description_with_path)
455
456 results = []
457 if error_descriptions:
458 results.append(output_api.PresubmitError(
kjellandera7066a32017-03-23 03:47:05 -0700459 'You added one or more #includes that violate checkdeps rules.\n'
460 'Check that the DEPS files in these locations contain valid rules.\n'
461 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
462 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000463 error_descriptions))
464 if warning_descriptions:
465 results.append(output_api.PresubmitPromptOrNotify(
466 'You added one or more #includes of files that are temporarily\n'
467 'allowed but being removed. Can you avoid introducing the\n'
kjellandera7066a32017-03-23 03:47:05 -0700468 '#include? See relevant DEPS file(s) for details and contacts.\n'
469 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
470 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000471 warning_descriptions))
472 return results
473
charujain9893e252017-09-14 13:33:22 +0200474def CheckCommitMessageBugEntry(input_api, output_api):
475 """Check that bug entries are well-formed in commit message."""
476 bogus_bug_msg = (
477 'Bogus BUG entry: %s. Please specify the issue tracker prefix and the '
478 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.')
479 results = []
480 for bug in (input_api.change.BUG or '').split(','):
481 bug = bug.strip()
482 if bug.lower() == 'none':
483 continue
charujain81a58c72017-09-25 13:25:45 +0200484 if 'b/' not in bug and ':' not in bug:
charujain9893e252017-09-14 13:33:22 +0200485 try:
486 if int(bug) > 100000:
487 # Rough indicator for current chromium bugs.
488 prefix_guess = 'chromium'
489 else:
490 prefix_guess = 'webrtc'
491 results.append('BUG entry requires issue tracker prefix, e.g. %s:%s' %
492 (prefix_guess, bug))
493 except ValueError:
494 results.append(bogus_bug_msg % bug)
charujain81a58c72017-09-25 13:25:45 +0200495 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
charujain9893e252017-09-14 13:33:22 +0200496 results.append(bogus_bug_msg % bug)
497 return [output_api.PresubmitError(r) for r in results]
498
499def CheckChangeHasBugField(input_api, output_api):
kjellanderd1e26a92016-09-19 08:11:16 -0700500 """Requires that the changelist have a BUG= field.
501
502 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
503 since it fails the presubmit if the BUG= field is missing or doesn't contain
504 a bug reference.
505 """
506 if input_api.change.BUG:
507 return []
508 else:
509 return [output_api.PresubmitError(
510 'The BUG=[bug number] field is mandatory. Please create a bug and '
511 'reference it using either of:\n'
512 ' * https://bugs.webrtc.org - reference it using BUG=webrtc:XXXX\n'
513 ' * https://crbug.com - reference it using BUG=chromium:XXXXXX')]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000514
charujain9893e252017-09-14 13:33:22 +0200515def CheckJSONParseErrors(input_api, output_api):
kjellander569cf942016-02-11 05:02:59 -0800516 """Check that JSON files do not contain syntax errors."""
517
518 def FilterFile(affected_file):
519 return input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
520
521 def GetJSONParseError(input_api, filename):
522 try:
523 contents = input_api.ReadFile(filename)
524 input_api.json.loads(contents)
525 except ValueError as e:
526 return e
527 return None
528
529 results = []
530 for affected_file in input_api.AffectedFiles(
531 file_filter=FilterFile, include_deletes=False):
532 parse_error = GetJSONParseError(input_api,
533 affected_file.AbsoluteLocalPath())
534 if parse_error:
535 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
536 (affected_file.LocalPath(), parse_error)))
537 return results
538
539
charujain9893e252017-09-14 13:33:22 +0200540def RunPythonTests(input_api, output_api):
kjellanderc88b5d52017-04-05 06:42:43 -0700541 def Join(*args):
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200542 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
543
544 test_directories = [
Edward Lemur6d01f6d2017-09-14 17:02:01 +0200545 input_api.PresubmitLocalPath(),
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200546 Join('rtc_tools', 'py_event_log_analyzer'),
547 Join('rtc_tools'),
548 Join('audio', 'test', 'unittests'),
ehmaldonado4fb97462017-01-30 05:27:22 -0800549 ] + [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200550 root for root, _, files in os.walk(Join('tools_webrtc'))
ehmaldonado4fb97462017-01-30 05:27:22 -0800551 if any(f.endswith('_test.py') for f in files)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200552 ]
553
554 tests = []
555 for directory in test_directories:
556 tests.extend(
557 input_api.canned_checks.GetUnitTestsInDirectory(
558 input_api,
559 output_api,
560 directory,
561 whitelist=[r'.+_test\.py$']))
562 return input_api.RunTests(tests, parallel=True)
563
564
charujain9893e252017-09-14 13:33:22 +0200565def CheckUsageOfGoogleProtobufNamespace(input_api, output_api):
mbonadei38415b22017-04-07 05:38:01 -0700566 """Checks that the namespace google::protobuf has not been used."""
567 files = []
568 pattern = input_api.re.compile(r'google::protobuf')
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200569 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
mbonadei38415b22017-04-07 05:38:01 -0700570 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
571 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
572 continue
573 contents = input_api.ReadFile(f)
574 if pattern.search(contents):
575 files.append(f)
576
577 if files:
578 return [output_api.PresubmitError(
579 'Please avoid to use namespace `google::protobuf` directly.\n'
580 'Add a using directive in `%s` and include that header instead.'
581 % proto_utils_path, files)]
582 return []
583
584
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200585def _LicenseHeader(input_api):
586 """Returns the license header regexp."""
587 # Accept any year number from 2003 to the current year
588 current_year = int(input_api.time.strftime('%Y'))
589 allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1)))
590 years_re = '(' + '|'.join(allowed_years) + ')'
591 license_header = (
592 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
593 r'All [Rr]ights [Rr]eserved\.\n'
594 r'.*?\n'
595 r'.*? Use of this source code is governed by a BSD-style license\n'
596 r'.*? that can be found in the LICENSE file in the root of the source\n'
597 r'.*? tree\. An additional intellectual property rights grant can be '
598 r'found\n'
599 r'.*? in the file PATENTS\. All contributing project authors may\n'
600 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
601 ) % {
602 'year': years_re,
603 }
604 return license_header
605
606
charujain9893e252017-09-14 13:33:22 +0200607def CommonChecks(input_api, output_api):
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000608 """Checks common to both upload and commit."""
niklase@google.comda159d62011-05-30 11:51:34 +0000609 results = []
tkchin42f580e2015-11-26 23:18:23 -0800610 # Filter out files that are in objc or ios dirs from being cpplint-ed since
611 # they do not follow C++ lint rules.
612 black_list = input_api.DEFAULT_BLACK_LIST + (
613 r".*\bobjc[\\\/].*",
Kári Tristan Helgason3fa35172016-09-09 08:55:05 +0000614 r".*objc\.[hcm]+$",
tkchin42f580e2015-11-26 23:18:23 -0800615 )
616 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list)
charujain9893e252017-09-14 13:33:22 +0200617 results.extend(CheckApprovedFilesLintClean(
tkchin42f580e2015-11-26 23:18:23 -0800618 input_api, output_api, source_file_filter))
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200619 results.extend(input_api.canned_checks.CheckLicense(
620 input_api, output_api, _LicenseHeader(input_api)))
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000621 results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100622 black_list=(r'^base[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200623 r'^build[\\\/].*\.py$',
624 r'^buildtools[\\\/].*\.py$',
kjellander38c65c82017-04-12 22:43:38 -0700625 r'^infra[\\\/].*\.py$',
Henrik Kjellander0779e8f2016-12-22 12:01:17 +0100626 r'^ios[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200627 r'^out.*[\\\/].*\.py$',
628 r'^testing[\\\/].*\.py$',
629 r'^third_party[\\\/].*\.py$',
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100630 r'^tools[\\\/].*\.py$',
kjellanderafd54942016-12-17 12:21:39 -0800631 # TODO(phoglund): should arguably be checked.
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200632 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
633 r'^tools_webrtc[\\\/]valgrind[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200634 r'^xcodebuild.*[\\\/].*\.py$',),
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200635 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800636
nisse3d21e232016-09-02 03:07:06 -0700637 # TODO(nisse): talk/ is no more, so make below checks simpler?
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200638 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
639 # we need to have different license checks in talk/ and webrtc/ directories.
640 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200641
tkchin3cd9a302016-06-08 12:40:28 -0700642 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
643 # ObjC subdirectories ObjC headers.
644 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100645 # Skip long-lines check for DEPS and GN files.
646 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
tkchin3cd9a302016-06-08 12:40:28 -0700647 eighty_char_sources = lambda x: input_api.FilterSourceFile(x,
648 black_list=build_file_filter_list + objc_filter_list)
649 hundred_char_sources = lambda x: input_api.FilterSourceFile(x,
650 white_list=objc_filter_list)
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000651 results.extend(input_api.canned_checks.CheckLongLines(
tkchin3cd9a302016-06-08 12:40:28 -0700652 input_api, output_api, maxlen=80, source_file_filter=eighty_char_sources))
653 results.extend(input_api.canned_checks.CheckLongLines(
654 input_api, output_api, maxlen=100,
655 source_file_filter=hundred_char_sources))
656
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000657 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
658 input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000659 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
660 input_api, output_api))
kjellandere5dc62a2016-12-14 00:16:21 -0800661 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
662 input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000663 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
664 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200665 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
666 results.extend(CheckNoIOStreamInHeaders(input_api, output_api))
667 results.extend(CheckNoPragmaOnce(input_api, output_api))
668 results.extend(CheckNoFRIEND_TEST(input_api, output_api))
669 results.extend(CheckGnChanges(input_api, output_api))
670 results.extend(CheckUnwantedDependencies(input_api, output_api))
671 results.extend(CheckJSONParseErrors(input_api, output_api))
672 results.extend(RunPythonTests(input_api, output_api))
673 results.extend(CheckUsageOfGoogleProtobufNamespace(input_api, output_api))
Mirko Bonadei866d3372017-09-15 12:35:26 +0200674 results.extend(CheckOrphanHeaders(input_api, output_api))
Mirko Bonadeia730c1c2017-09-18 11:33:13 +0200675 results.extend(CheckNewlineAtTheEndOfProtoFiles(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000676 return results
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000677
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000678
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000679def CheckChangeOnUpload(input_api, output_api):
680 results = []
charujain9893e252017-09-14 13:33:22 +0200681 results.extend(CommonChecks(input_api, output_api))
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200682 results.extend(
683 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
niklase@google.comda159d62011-05-30 11:51:34 +0000684 return results
685
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000686
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000687def CheckChangeOnCommit(input_api, output_api):
niklase@google.com1198db92011-06-09 07:07:24 +0000688 results = []
charujain9893e252017-09-14 13:33:22 +0200689 results.extend(CommonChecks(input_api, output_api))
690 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
niklase@google.com1198db92011-06-09 07:07:24 +0000691 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000692 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
693 input_api, output_api))
694 results.extend(input_api.canned_checks.CheckChangeHasDescription(
695 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200696 results.extend(CheckChangeHasBugField(input_api, output_api))
697 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
kjellander@webrtc.org12cb88c2014-02-13 11:53:43 +0000698 results.extend(input_api.canned_checks.CheckTreeIsOpen(
699 input_api, output_api,
700 json_url='http://webrtc-status.appspot.com/current?format=json'))
niklase@google.com1198db92011-06-09 07:07:24 +0000701 return results
mbonadei74973ed2017-05-09 07:58:05 -0700702
703
charujain9893e252017-09-14 13:33:22 +0200704def CheckOrphanHeaders(input_api, output_api):
mbonadei74973ed2017-05-09 07:58:05 -0700705 # We need to wait until we have an input_api object and use this
706 # roundabout construct to import prebubmit_checks_lib because this file is
707 # eval-ed and thus doesn't have __file__.
708 error_msg = """Header file {} is not listed in any GN target.
709 Please create a target or add it to an existing one in {}"""
710 results = []
711 original_sys_path = sys.path
712 try:
713 sys.path = sys.path + [input_api.os_path.join(
714 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')]
715 from check_orphan_headers import GetBuildGnPathFromFilePath
716 from check_orphan_headers import IsHeaderInBuildGn
717 finally:
718 # Restore sys.path to what it was before.
719 sys.path = original_sys_path
720
721 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
mbonadeia644ad32017-05-10 05:21:55 -0700722 if f.LocalPath().endswith('.h') and f.Action() == 'A':
mbonadei74973ed2017-05-09 07:58:05 -0700723 file_path = os.path.abspath(f.LocalPath())
724 root_dir = os.getcwd()
725 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
726 root_dir)
727 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
728 if not in_build_gn:
729 results.append(output_api.PresubmitError(error_msg.format(
730 file_path, gn_file_path)))
731 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200732
733
Mirko Bonadeia730c1c2017-09-18 11:33:13 +0200734def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200735 """Checks that all .proto files are terminated with a newline."""
736 error_msg = 'File {} must end with exactly one newline.'
737 results = []
738 source_file_filter = lambda x: input_api.FilterSourceFile(
739 x, white_list=(r'.+\.proto$',))
740 for f in input_api.AffectedSourceFiles(source_file_filter):
741 file_path = f.LocalPath()
742 with open(file_path) as f:
743 lines = f.readlines()
Mirko Bonadeia730c1c2017-09-18 11:33:13 +0200744 if len(lines) > 0 and not lines[-1].endswith('\n'):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200745 results.append(output_api.PresubmitError(error_msg.format(file_path)))
746 return results