blob: ca6a70a19eb3f42a516326adbf7d2a52c979cd23 [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
kjellander@webrtc.org85759802013-10-22 16:47:40 +000014
15
oprypin2aa463f2017-03-23 03:17:02 -070016# Files and directories that are *skipped* by cpplint in the presubmit script.
17CPPLINT_BLACKLIST = [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +020018 'tools_webrtc',
ilnikd60d06a2017-04-05 03:02:20 -070019 'webrtc/api/video_codecs/video_decoder.h',
20 'webrtc/api/video_codecs/video_encoder.h',
ehmaldonado370dd472017-07-10 05:58:42 -070021 'webrtc/base',
oprypin2aa463f2017-03-23 03:17:02 -070022 'webrtc/examples/objc',
23 'webrtc/media',
24 'webrtc/modules/audio_coding',
25 'webrtc/modules/audio_conference_mixer',
26 'webrtc/modules/audio_device',
27 'webrtc/modules/audio_processing',
28 'webrtc/modules/desktop_capture',
29 'webrtc/modules/include/module_common_types.h',
30 'webrtc/modules/media_file',
31 'webrtc/modules/utility',
32 'webrtc/modules/video_capture',
33 'webrtc/p2p',
34 'webrtc/pc',
Henrik Kjellanderc0362762017-06-29 08:03:04 +020035 'webrtc/rtc_base',
oprypin2aa463f2017-03-23 03:17:02 -070036 'webrtc/sdk/android/src/jni',
37 'webrtc/sdk/objc',
38 'webrtc/system_wrappers',
39 'webrtc/test',
40 'webrtc/voice_engine',
oprypin2aa463f2017-03-23 03:17:02 -070041 'webrtc/common_types.h',
42 'webrtc/common_types.cc',
oprypin2aa463f2017-03-23 03:17:02 -070043 'webrtc/video_send_stream.h',
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +010044]
45
jbauchc4e3ead2016-02-19 00:25:55 -080046# These filters will always be removed, even if the caller specifies a filter
47# set, as they are problematic or broken in some way.
48#
49# Justifications for each filter:
50# - build/c++11 : Rvalue ref checks are unreliable (false positives),
51# include file and feature blacklists are
52# google3-specific.
kjellandere5a87a52016-04-27 02:32:12 -070053# - whitespace/operators: Same as above (doesn't seem sufficient to eliminate
54# all move-related errors).
jbauchc4e3ead2016-02-19 00:25:55 -080055BLACKLIST_LINT_FILTERS = [
56 '-build/c++11',
kjellandere5a87a52016-04-27 02:32:12 -070057 '-whitespace/operators',
jbauchc4e3ead2016-02-19 00:25:55 -080058]
59
kjellanderfd595232015-12-04 02:44:09 -080060# List of directories of "supported" native APIs. That means changes to headers
61# will be done in a compatible way following this scheme:
62# 1. Non-breaking changes are made.
63# 2. The old APIs as marked as deprecated (with comments).
64# 3. Deprecation is announced to discuss-webrtc@googlegroups.com and
65# webrtc-users@google.com (internal list).
66# 4. (later) The deprecated APIs are removed.
kjellander53047c92015-12-02 23:56:14 -080067NATIVE_API_DIRS = (
kjellander53047c92015-12-02 23:56:14 -080068 'webrtc',
kjellanderdd705472016-06-09 11:17:27 -070069 'webrtc/api',
70 'webrtc/media',
kjellander53047c92015-12-02 23:56:14 -080071 'webrtc/modules/audio_device/include',
kjellanderdd705472016-06-09 11:17:27 -070072 'webrtc/pc',
73)
74# These directories should not be used but are maintained only to avoid breaking
75# some legacy downstream code.
76LEGACY_API_DIRS = (
ehmaldonado370dd472017-07-10 05:58:42 -070077 'webrtc/base',
kjellanderdd705472016-06-09 11:17:27 -070078 'webrtc/common_audio/include',
79 'webrtc/modules/audio_coding/include',
80 'webrtc/modules/audio_conference_mixer/include',
kjellander53047c92015-12-02 23:56:14 -080081 'webrtc/modules/audio_processing/include',
82 'webrtc/modules/bitrate_controller/include',
Stefan Holmer80e12072016-02-23 13:30:42 +010083 'webrtc/modules/congestion_controller/include',
kjellander53047c92015-12-02 23:56:14 -080084 'webrtc/modules/include',
85 'webrtc/modules/remote_bitrate_estimator/include',
86 'webrtc/modules/rtp_rtcp/include',
kjellanderdd705472016-06-09 11:17:27 -070087 'webrtc/modules/rtp_rtcp/source',
kjellander53047c92015-12-02 23:56:14 -080088 'webrtc/modules/utility/include',
89 'webrtc/modules/video_coding/codecs/h264/include',
90 'webrtc/modules/video_coding/codecs/i420/include',
91 'webrtc/modules/video_coding/codecs/vp8/include',
92 'webrtc/modules/video_coding/codecs/vp9/include',
93 'webrtc/modules/video_coding/include',
kjellanderdd705472016-06-09 11:17:27 -070094 'webrtc/system_wrappers/include',
kjellander53047c92015-12-02 23:56:14 -080095 'webrtc/voice_engine/include',
96)
kjellanderdd705472016-06-09 11:17:27 -070097API_DIRS = NATIVE_API_DIRS[:] + LEGACY_API_DIRS[:]
kjellander53047c92015-12-02 23:56:14 -080098
99
ehmaldonado4fb97462017-01-30 05:27:22 -0800100def _RunCommand(command, cwd):
101 """Runs a command and returns the output from that command."""
102 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
103 cwd=cwd)
104 stdout = p.stdout.read()
105 stderr = p.stderr.read()
106 p.wait()
107 p.stdout.close()
108 p.stderr.close()
109 return p.returncode, stdout, stderr
110
111
kjellander53047c92015-12-02 23:56:14 -0800112def _VerifyNativeApiHeadersListIsValid(input_api, output_api):
113 """Ensures the list of native API header directories is up to date."""
114 non_existing_paths = []
115 native_api_full_paths = [
116 input_api.os_path.join(input_api.PresubmitLocalPath(),
kjellanderdd705472016-06-09 11:17:27 -0700117 *path.split('/')) for path in API_DIRS]
kjellander53047c92015-12-02 23:56:14 -0800118 for path in native_api_full_paths:
119 if not os.path.isdir(path):
120 non_existing_paths.append(path)
121 if non_existing_paths:
122 return [output_api.PresubmitError(
123 'Directories to native API headers have changed which has made the '
124 'list in PRESUBMIT.py outdated.\nPlease update it to the current '
125 'location of our native APIs.',
126 non_existing_paths)]
127 return []
128
kjellanderc88b5d52017-04-05 06:42:43 -0700129API_CHANGE_MSG = """
kwibergeb133022016-04-07 07:41:48 -0700130You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 04:13:23 -0800131 1. Make compatible changes that don't break existing clients. Usually
132 this is done by keeping the existing method signatures unchanged.
133 2. Mark the old stuff as deprecated (see RTC_DEPRECATED macro).
kwibergeb133022016-04-07 07:41:48 -0700134 3. Create a timeline and plan for when the deprecated stuff will be
135 removed. (The amount of time we give users to change their code
136 should be informed by how much work it is for them. If they just
137 need to replace one name with another or something equally
138 simple, 1-2 weeks might be good; if they need to do serious work,
139 up to 3 months may be called for.)
140 4. Update/inform existing downstream code owners to stop using the
141 deprecated stuff. (Send announcements to
142 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
143 5. Remove the deprecated stuff, once the agreed-upon amount of time
144 has passed.
145Related files:
146"""
kjellander53047c92015-12-02 23:56:14 -0800147
148def _CheckNativeApiHeaderChanges(input_api, output_api):
149 """Checks to remind proper changing of native APIs."""
150 files = []
151 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
152 if f.LocalPath().endswith('.h'):
kjellanderdd705472016-06-09 11:17:27 -0700153 for path in API_DIRS:
kjellander53047c92015-12-02 23:56:14 -0800154 if os.path.dirname(f.LocalPath()) == path:
155 files.append(f)
156
157 if files:
kjellanderc88b5d52017-04-05 06:42:43 -0700158 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
kjellander53047c92015-12-02 23:56:14 -0800159 return []
160
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100161
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000162def _CheckNoIOStreamInHeaders(input_api, output_api):
163 """Checks to make sure no .h files include <iostream>."""
164 files = []
165 pattern = input_api.re.compile(r'^#include\s*<iostream>',
166 input_api.re.MULTILINE)
167 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
168 if not f.LocalPath().endswith('.h'):
169 continue
170 contents = input_api.ReadFile(f)
171 if pattern.search(contents):
172 files.append(f)
173
174 if len(files):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200175 return [output_api.PresubmitError(
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000176 'Do not #include <iostream> in header files, since it inserts static ' +
177 'initialization into every file including the header. Instead, ' +
178 '#include <ostream>. See http://crbug.com/94794',
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200179 files)]
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000180 return []
181
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000182
kjellander6aeef742017-02-20 01:13:18 -0800183def _CheckNoPragmaOnce(input_api, output_api):
184 """Make sure that banned functions are not used."""
185 files = []
186 pattern = input_api.re.compile(r'^#pragma\s+once',
187 input_api.re.MULTILINE)
188 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
189 if not f.LocalPath().endswith('.h'):
190 continue
191 contents = input_api.ReadFile(f)
192 if pattern.search(contents):
193 files.append(f)
194
195 if files:
196 return [output_api.PresubmitError(
197 'Do not use #pragma once in header files.\n'
198 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
199 files)]
200 return []
201
202
kjellanderc88b5d52017-04-05 06:42:43 -0700203def _CheckNoFRIEND_TEST(input_api, output_api): # pylint: disable=invalid-name
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000204 """Make sure that gtest's FRIEND_TEST() macro is not used, the
205 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
206 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
207 problems = []
208
209 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h'))
210 for f in input_api.AffectedFiles(file_filter=file_filter):
211 for line_num, line in f.ChangedContents():
212 if 'FRIEND_TEST(' in line:
213 problems.append(' %s:%d' % (f.LocalPath(), line_num))
214
215 if not problems:
216 return []
217 return [output_api.PresubmitPromptWarning('WebRTC\'s code should not use '
218 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and '
219 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
220
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000221
oprypin2aa463f2017-03-23 03:17:02 -0700222def _IsLintBlacklisted(blacklist_paths, file_path):
223 """ Checks if a file is blacklisted for lint check."""
224 for path in blacklist_paths:
225 if file_path == path or os.path.dirname(file_path).startswith(path):
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100226 return True
227 return False
228
229
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000230def _CheckApprovedFilesLintClean(input_api, output_api,
231 source_file_filter=None):
oprypin2aa463f2017-03-23 03:17:02 -0700232 """Checks that all new or non-blacklisted .cc and .h files pass cpplint.py.
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000233 This check is based on _CheckChangeLintsClean in
234 depot_tools/presubmit_canned_checks.py but has less filters and only checks
235 added files."""
236 result = []
237
238 # Initialize cpplint.
239 import cpplint
240 # Access to a protected member _XX of a client class
241 # pylint: disable=W0212
242 cpplint._cpplint_state.ResetErrorCounts()
243
jbauchc4e3ead2016-02-19 00:25:55 -0800244 lint_filters = cpplint._Filters()
245 lint_filters.extend(BLACKLIST_LINT_FILTERS)
246 cpplint._SetFilters(','.join(lint_filters))
247
oprypin2aa463f2017-03-23 03:17:02 -0700248 # Create a platform independent blacklist for cpplint.
249 blacklist_paths = [input_api.os_path.join(*path.split('/'))
250 for path in CPPLINT_BLACKLIST]
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100251
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000252 # Use the strictest verbosity level for cpplint.py (level 1) which is the
oprypin2aa463f2017-03-23 03:17:02 -0700253 # default when running cpplint.py from command line. To make it possible to
254 # work with not-yet-converted code, we're only applying it to new (or
255 # moved/renamed) files and files not listed in CPPLINT_BLACKLIST.
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000256 verbosity_level = 1
257 files = []
258 for f in input_api.AffectedSourceFiles(source_file_filter):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200259 # Note that moved/renamed files also count as added.
oprypin2aa463f2017-03-23 03:17:02 -0700260 if f.Action() == 'A' or not _IsLintBlacklisted(blacklist_paths,
261 f.LocalPath()):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000262 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000263
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000264 for file_name in files:
265 cpplint.ProcessFile(file_name, verbosity_level)
266
267 if cpplint._cpplint_state.error_count > 0:
268 if input_api.is_committing:
oprypin8e58d652017-03-21 07:52:41 -0700269 res_type = output_api.PresubmitError
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000270 else:
271 res_type = output_api.PresubmitPromptWarning
272 result = [res_type('Changelist failed cpplint.py check.')]
273
274 return result
275
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100276def _CheckNoSourcesAbove(input_api, gn_files, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700277 # Disallow referencing source files with paths above the GN file location.
278 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
279 re.MULTILINE | re.DOTALL)
280 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
281 violating_gn_files = set()
282 violating_source_entries = []
283 for gn_file in gn_files:
284 contents = input_api.ReadFile(gn_file)
285 for source_block_match in source_pattern.finditer(contents):
286 # Find all source list entries starting with ../ in the source block
287 # (exclude overrides entries).
288 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
289 source_file = file_list_match.group(1)
290 if 'overrides/' not in source_file:
291 violating_source_entries.append(source_file)
292 violating_gn_files.add(gn_file)
293 if violating_gn_files:
294 return [output_api.PresubmitError(
295 'Referencing source files above the directory of the GN file is not '
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100296 'allowed. Please introduce new GN targets in the proper location '
297 'instead.\n'
ehmaldonado5b1ba082016-09-02 05:51:08 -0700298 'Invalid source entries:\n'
299 '%s\n'
300 'Violating GN files:' % '\n'.join(violating_source_entries),
301 items=violating_gn_files)]
302 return []
303
kjellander7439f972016-12-05 22:47:46 -0800304def _CheckNoMixingCAndCCSources(input_api, gn_files, output_api):
305 # Disallow mixing .c and .cc source files in the same target.
306 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
307 re.MULTILINE | re.DOTALL)
308 file_pattern = input_api.re.compile(r'"(.*)"')
309 violating_gn_files = dict()
310 for gn_file in gn_files:
311 contents = input_api.ReadFile(gn_file)
312 for source_block_match in source_pattern.finditer(contents):
313 c_files = []
314 cc_files = []
315 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
316 source_file = file_list_match.group(1)
317 if source_file.endswith('.c'):
318 c_files.append(source_file)
319 if source_file.endswith('.cc'):
320 cc_files.append(source_file)
321 if c_files and cc_files:
322 violating_gn_files[gn_file.LocalPath()] = sorted(c_files + cc_files)
323 if violating_gn_files:
324 return [output_api.PresubmitError(
325 'GN targets cannot mix .cc and .c source files. Please create a '
326 'separate target for each collection of sources.\n'
327 'Mixed sources: \n'
328 '%s\n'
329 'Violating GN files:' % json.dumps(violating_gn_files, indent=2),
330 items=violating_gn_files.keys())]
331 return []
332
ehmaldonado4fb97462017-01-30 05:27:22 -0800333def _CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
334 cwd = input_api.PresubmitLocalPath()
mbonadeiab587dc2017-05-12 04:13:31 -0700335 script_path = os.path.join('tools_webrtc', 'presubmit_checks_lib',
336 'check_package_boundaries.py')
ehmaldonado4fb97462017-01-30 05:27:22 -0800337 webrtc_path = os.path.join('webrtc')
338 command = [sys.executable, script_path, webrtc_path]
339 command += [gn_file.LocalPath() for gn_file in gn_files]
340 returncode, _, stderr = _RunCommand(command, cwd)
341 if returncode:
342 return [output_api.PresubmitError(
343 'There are package boundary violations in the following GN files:\n\n'
344 '%s' % stderr)]
345 return []
346
ehmaldonado5b1ba082016-09-02 05:51:08 -0700347def _CheckGnChanges(input_api, output_api):
348 source_file_filter = lambda x: input_api.FilterSourceFile(
349 x, white_list=(r'.+\.(gn|gni)$',))
350
351 gn_files = []
352 for f in input_api.AffectedSourceFiles(source_file_filter):
353 if f.LocalPath().startswith('webrtc'):
354 gn_files.append(f)
355
356 result = []
357 if gn_files:
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100358 result.extend(_CheckNoSourcesAbove(input_api, gn_files, output_api))
kjellander7439f972016-12-05 22:47:46 -0800359 result.extend(_CheckNoMixingCAndCCSources(input_api, gn_files, output_api))
ehmaldonado4fb97462017-01-30 05:27:22 -0800360 result.extend(_CheckNoPackageBoundaryViolations(
361 input_api, gn_files, output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700362 return result
363
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000364def _CheckUnwantedDependencies(input_api, output_api):
365 """Runs checkdeps on #include statements added in this
366 change. Breaking - rules is an error, breaking ! rules is a
367 warning.
368 """
369 # Copied from Chromium's src/PRESUBMIT.py.
370
371 # We need to wait until we have an input_api object and use this
372 # roundabout construct to import checkdeps because this file is
373 # eval-ed and thus doesn't have __file__.
374 original_sys_path = sys.path
375 try:
kjellander@webrtc.orgaefe61a2014-12-08 13:00:30 +0000376 checkdeps_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
377 'buildtools', 'checkdeps')
378 if not os.path.exists(checkdeps_path):
379 return [output_api.PresubmitError(
380 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
381 'download Chromium and setup the symlinks?' % checkdeps_path)]
382 sys.path.append(checkdeps_path)
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000383 import checkdeps
384 from cpp_checker import CppChecker
385 from rules import Rule
386 finally:
387 # Restore sys.path to what it was before.
388 sys.path = original_sys_path
389
390 added_includes = []
391 for f in input_api.AffectedFiles():
392 if not CppChecker.IsCppFile(f.LocalPath()):
393 continue
394
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200395 changed_lines = [line for _, line in f.ChangedContents()]
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000396 added_includes.append([f.LocalPath(), changed_lines])
397
398 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
399
400 error_descriptions = []
401 warning_descriptions = []
402 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
403 added_includes):
404 description_with_path = '%s\n %s' % (path, rule_description)
405 if rule_type == Rule.DISALLOW:
406 error_descriptions.append(description_with_path)
407 else:
408 warning_descriptions.append(description_with_path)
409
410 results = []
411 if error_descriptions:
412 results.append(output_api.PresubmitError(
kjellandera7066a32017-03-23 03:47:05 -0700413 'You added one or more #includes that violate checkdeps rules.\n'
414 'Check that the DEPS files in these locations contain valid rules.\n'
415 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
416 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000417 error_descriptions))
418 if warning_descriptions:
419 results.append(output_api.PresubmitPromptOrNotify(
420 'You added one or more #includes of files that are temporarily\n'
421 'allowed but being removed. Can you avoid introducing the\n'
kjellandera7066a32017-03-23 03:47:05 -0700422 '#include? See relevant DEPS file(s) for details and contacts.\n'
423 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
424 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000425 warning_descriptions))
426 return results
427
kjellanderd1e26a92016-09-19 08:11:16 -0700428def _CheckChangeHasBugField(input_api, output_api):
429 """Requires that the changelist have a BUG= field.
430
431 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
432 since it fails the presubmit if the BUG= field is missing or doesn't contain
433 a bug reference.
434 """
435 if input_api.change.BUG:
436 return []
437 else:
438 return [output_api.PresubmitError(
439 'The BUG=[bug number] field is mandatory. Please create a bug and '
440 'reference it using either of:\n'
441 ' * https://bugs.webrtc.org - reference it using BUG=webrtc:XXXX\n'
442 ' * https://crbug.com - reference it using BUG=chromium:XXXXXX')]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000443
kjellander569cf942016-02-11 05:02:59 -0800444def _CheckJSONParseErrors(input_api, output_api):
445 """Check that JSON files do not contain syntax errors."""
446
447 def FilterFile(affected_file):
448 return input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
449
450 def GetJSONParseError(input_api, filename):
451 try:
452 contents = input_api.ReadFile(filename)
453 input_api.json.loads(contents)
454 except ValueError as e:
455 return e
456 return None
457
458 results = []
459 for affected_file in input_api.AffectedFiles(
460 file_filter=FilterFile, include_deletes=False):
461 parse_error = GetJSONParseError(input_api,
462 affected_file.AbsoluteLocalPath())
463 if parse_error:
464 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
465 (affected_file.LocalPath(), parse_error)))
466 return results
467
468
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200469def _RunPythonTests(input_api, output_api):
kjellanderc88b5d52017-04-05 06:42:43 -0700470 def Join(*args):
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200471 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
472
473 test_directories = [
kjellander41bafb22017-06-30 04:14:54 -0700474 Join('webrtc', 'rtc_tools', 'py_event_log_analyzer'),
475 Join('webrtc', 'rtc_tools'),
oprypinabd101b2017-04-06 23:21:30 -0700476 Join('webrtc', 'audio', 'test', 'unittests'),
ehmaldonado4fb97462017-01-30 05:27:22 -0800477 ] + [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200478 root for root, _, files in os.walk(Join('tools_webrtc'))
ehmaldonado4fb97462017-01-30 05:27:22 -0800479 if any(f.endswith('_test.py') for f in files)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200480 ]
481
482 tests = []
483 for directory in test_directories:
484 tests.extend(
485 input_api.canned_checks.GetUnitTestsInDirectory(
486 input_api,
487 output_api,
488 directory,
489 whitelist=[r'.+_test\.py$']))
490 return input_api.RunTests(tests, parallel=True)
491
492
mbonadei38415b22017-04-07 05:38:01 -0700493def _CheckUsageOfGoogleProtobufNamespace(input_api, output_api):
494 """Checks that the namespace google::protobuf has not been used."""
495 files = []
496 pattern = input_api.re.compile(r'google::protobuf')
497 proto_utils_path = os.path.join('webrtc', 'base', 'protobuf_utils.h')
498 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
499 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
500 continue
501 contents = input_api.ReadFile(f)
502 if pattern.search(contents):
503 files.append(f)
504
505 if files:
506 return [output_api.PresubmitError(
507 'Please avoid to use namespace `google::protobuf` directly.\n'
508 'Add a using directive in `%s` and include that header instead.'
509 % proto_utils_path, files)]
510 return []
511
512
ehmaldonadoea0e0842017-07-10 06:44:09 -0700513def _CheckNoChangesToWebRTCBase(input_api, output_api):
514 """Checks that no changes refer to webrtc/base."""
515 problems = []
516
517 for f in input_api.AffectedFiles():
518 if os.path.join('webrtc', 'base') in f.LocalPath():
519 problems.append(' ' + f.LocalPath())
520 continue
521 for line_num, line in f.ChangedContents():
522 if 'webrtc/base' in line:
523 problems.append(' %s: %s' % (f.LocalPath(), line_num))
524
ehmaldonadofe533552017-07-10 07:04:10 -0700525 if problems:
526 return [output_api.PresubmitPromptWarning(
527 'webrtc/base is being moved to webrtc/rtc_base (See '
528 'bugs.webrtc.org/7634). Please refer to webrtc/rtc_base instead in the '
529 'following files:\n' + '\n'.join(problems))]
530 return []
ehmaldonadoea0e0842017-07-10 06:44:09 -0700531
532
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000533def _CommonChecks(input_api, output_api):
534 """Checks common to both upload and commit."""
niklase@google.comda159d62011-05-30 11:51:34 +0000535 results = []
tkchin42f580e2015-11-26 23:18:23 -0800536 # Filter out files that are in objc or ios dirs from being cpplint-ed since
537 # they do not follow C++ lint rules.
538 black_list = input_api.DEFAULT_BLACK_LIST + (
539 r".*\bobjc[\\\/].*",
Kári Tristan Helgason3fa35172016-09-09 08:55:05 +0000540 r".*objc\.[hcm]+$",
hjon65ae2d82016-08-02 23:55:44 -0700541 r"webrtc\/build\/ios\/SDK\/.*",
tkchin42f580e2015-11-26 23:18:23 -0800542 )
543 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list)
544 results.extend(_CheckApprovedFilesLintClean(
545 input_api, output_api, source_file_filter))
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000546 results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100547 black_list=(r'^base[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200548 r'^build[\\\/].*\.py$',
549 r'^buildtools[\\\/].*\.py$',
kjellander38c65c82017-04-12 22:43:38 -0700550 r'^infra[\\\/].*\.py$',
Henrik Kjellander0779e8f2016-12-22 12:01:17 +0100551 r'^ios[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200552 r'^out.*[\\\/].*\.py$',
553 r'^testing[\\\/].*\.py$',
554 r'^third_party[\\\/].*\.py$',
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100555 r'^tools[\\\/].*\.py$',
kjellanderafd54942016-12-17 12:21:39 -0800556 # TODO(phoglund): should arguably be checked.
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200557 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
558 r'^tools_webrtc[\\\/]valgrind[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200559 r'^xcodebuild.*[\\\/].*\.py$',),
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200560 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800561
nisse3d21e232016-09-02 03:07:06 -0700562 # TODO(nisse): talk/ is no more, so make below checks simpler?
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200563 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
564 # we need to have different license checks in talk/ and webrtc/ directories.
565 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200566
tkchin3cd9a302016-06-08 12:40:28 -0700567 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
568 # ObjC subdirectories ObjC headers.
569 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100570 # Skip long-lines check for DEPS and GN files.
571 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
tkchin3cd9a302016-06-08 12:40:28 -0700572 eighty_char_sources = lambda x: input_api.FilterSourceFile(x,
573 black_list=build_file_filter_list + objc_filter_list)
574 hundred_char_sources = lambda x: input_api.FilterSourceFile(x,
575 white_list=objc_filter_list)
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000576 results.extend(input_api.canned_checks.CheckLongLines(
tkchin3cd9a302016-06-08 12:40:28 -0700577 input_api, output_api, maxlen=80, source_file_filter=eighty_char_sources))
578 results.extend(input_api.canned_checks.CheckLongLines(
579 input_api, output_api, maxlen=100,
580 source_file_filter=hundred_char_sources))
581
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000582 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
583 input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000584 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
585 input_api, output_api))
kjellandere5dc62a2016-12-14 00:16:21 -0800586 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
587 input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000588 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
589 input_api, output_api))
kjellander53047c92015-12-02 23:56:14 -0800590 results.extend(_CheckNativeApiHeaderChanges(input_api, output_api))
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000591 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
kjellander6aeef742017-02-20 01:13:18 -0800592 results.extend(_CheckNoPragmaOnce(input_api, output_api))
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000593 results.extend(_CheckNoFRIEND_TEST(input_api, output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700594 results.extend(_CheckGnChanges(input_api, output_api))
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000595 results.extend(_CheckUnwantedDependencies(input_api, output_api))
kjellander569cf942016-02-11 05:02:59 -0800596 results.extend(_CheckJSONParseErrors(input_api, output_api))
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200597 results.extend(_RunPythonTests(input_api, output_api))
mbonadei38415b22017-04-07 05:38:01 -0700598 results.extend(_CheckUsageOfGoogleProtobufNamespace(input_api, output_api))
mbonadei26c26342017-05-12 02:06:16 -0700599 results.extend(_CheckOrphanHeaders(input_api, output_api))
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200600 results.extend(_CheckNewLineAtTheEndOfProtoFiles(input_api, output_api))
ehmaldonadoea0e0842017-07-10 06:44:09 -0700601 results.extend(_CheckNoChangesToWebRTCBase(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000602 return results
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000603
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000604
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000605def CheckChangeOnUpload(input_api, output_api):
606 results = []
607 results.extend(_CommonChecks(input_api, output_api))
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200608 results.extend(
609 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
niklase@google.comda159d62011-05-30 11:51:34 +0000610 return results
611
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000612
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000613def CheckChangeOnCommit(input_api, output_api):
niklase@google.com1198db92011-06-09 07:07:24 +0000614 results = []
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000615 results.extend(_CommonChecks(input_api, output_api))
kjellander53047c92015-12-02 23:56:14 -0800616 results.extend(_VerifyNativeApiHeadersListIsValid(input_api, output_api))
niklase@google.com1198db92011-06-09 07:07:24 +0000617 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000618 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
619 input_api, output_api))
620 results.extend(input_api.canned_checks.CheckChangeHasDescription(
621 input_api, output_api))
kjellanderd1e26a92016-09-19 08:11:16 -0700622 results.extend(_CheckChangeHasBugField(input_api, output_api))
kjellander@webrtc.org12cb88c2014-02-13 11:53:43 +0000623 results.extend(input_api.canned_checks.CheckTreeIsOpen(
624 input_api, output_api,
625 json_url='http://webrtc-status.appspot.com/current?format=json'))
niklase@google.com1198db92011-06-09 07:07:24 +0000626 return results
mbonadei74973ed2017-05-09 07:58:05 -0700627
628
629def _CheckOrphanHeaders(input_api, output_api):
630 # We need to wait until we have an input_api object and use this
631 # roundabout construct to import prebubmit_checks_lib because this file is
632 # eval-ed and thus doesn't have __file__.
633 error_msg = """Header file {} is not listed in any GN target.
634 Please create a target or add it to an existing one in {}"""
635 results = []
636 original_sys_path = sys.path
637 try:
638 sys.path = sys.path + [input_api.os_path.join(
639 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')]
640 from check_orphan_headers import GetBuildGnPathFromFilePath
641 from check_orphan_headers import IsHeaderInBuildGn
642 finally:
643 # Restore sys.path to what it was before.
644 sys.path = original_sys_path
645
646 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
mbonadeia644ad32017-05-10 05:21:55 -0700647 if f.LocalPath().endswith('.h') and f.Action() == 'A':
mbonadei74973ed2017-05-09 07:58:05 -0700648 file_path = os.path.abspath(f.LocalPath())
649 root_dir = os.getcwd()
650 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
651 root_dir)
652 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
653 if not in_build_gn:
654 results.append(output_api.PresubmitError(error_msg.format(
655 file_path, gn_file_path)))
656 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200657
658
659def _CheckNewLineAtTheEndOfProtoFiles(input_api, output_api):
660 """Checks that all .proto files are terminated with a newline."""
661 error_msg = 'File {} must end with exactly one newline.'
662 results = []
663 source_file_filter = lambda x: input_api.FilterSourceFile(
664 x, white_list=(r'.+\.proto$',))
665 for f in input_api.AffectedSourceFiles(source_file_filter):
666 file_path = f.LocalPath()
667 with open(file_path) as f:
668 lines = f.readlines()
669 if lines[-1] != '\n' or lines[-2] == '\n':
670 results.append(output_api.PresubmitError(error_msg.format(file_path)))
671 return results