blob: 9a76412e0c57c04276b54ff8ca5a055a9c33adf2 [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',
oprypin2aa463f2017-03-23 03:17:02 -070020 'webrtc/examples/objc',
21 'webrtc/media',
22 'webrtc/modules/audio_coding',
23 'webrtc/modules/audio_conference_mixer',
24 'webrtc/modules/audio_device',
25 'webrtc/modules/audio_processing',
26 'webrtc/modules/desktop_capture',
27 'webrtc/modules/include/module_common_types.h',
28 'webrtc/modules/media_file',
29 'webrtc/modules/utility',
30 'webrtc/modules/video_capture',
31 'webrtc/p2p',
32 'webrtc/pc',
Henrik Kjellanderc0362762017-06-29 08:03:04 +020033 'webrtc/rtc_base',
oprypin2aa463f2017-03-23 03:17:02 -070034 'webrtc/sdk/android/src/jni',
35 'webrtc/sdk/objc',
36 'webrtc/system_wrappers',
37 'webrtc/test',
38 'webrtc/voice_engine',
oprypin2aa463f2017-03-23 03:17:02 -070039 'webrtc/common_types.h',
40 'webrtc/common_types.cc',
oprypin2aa463f2017-03-23 03:17:02 -070041 'webrtc/video_send_stream.h',
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 = (
kjellander53047c92015-12-02 23:56:14 -080066 'webrtc',
kjellanderdd705472016-06-09 11:17:27 -070067 'webrtc/api',
68 'webrtc/media',
kjellander53047c92015-12-02 23:56:14 -080069 'webrtc/modules/audio_device/include',
kjellanderdd705472016-06-09 11:17:27 -070070 'webrtc/pc',
71)
72# These directories should not be used but are maintained only to avoid breaking
73# some legacy downstream code.
74LEGACY_API_DIRS = (
kjellanderdd705472016-06-09 11:17:27 -070075 'webrtc/common_audio/include',
76 'webrtc/modules/audio_coding/include',
77 'webrtc/modules/audio_conference_mixer/include',
kjellander53047c92015-12-02 23:56:14 -080078 'webrtc/modules/audio_processing/include',
79 'webrtc/modules/bitrate_controller/include',
Stefan Holmer80e12072016-02-23 13:30:42 +010080 'webrtc/modules/congestion_controller/include',
kjellander53047c92015-12-02 23:56:14 -080081 'webrtc/modules/include',
82 'webrtc/modules/remote_bitrate_estimator/include',
83 'webrtc/modules/rtp_rtcp/include',
kjellanderdd705472016-06-09 11:17:27 -070084 'webrtc/modules/rtp_rtcp/source',
kjellander53047c92015-12-02 23:56:14 -080085 'webrtc/modules/utility/include',
86 'webrtc/modules/video_coding/codecs/h264/include',
87 'webrtc/modules/video_coding/codecs/i420/include',
88 'webrtc/modules/video_coding/codecs/vp8/include',
89 'webrtc/modules/video_coding/codecs/vp9/include',
90 'webrtc/modules/video_coding/include',
ehmaldonadof6a861a2017-07-19 10:40:47 -070091 'webrtc/rtc_base',
kjellanderdd705472016-06-09 11:17:27 -070092 'webrtc/system_wrappers/include',
kjellander53047c92015-12-02 23:56:14 -080093 'webrtc/voice_engine/include',
94)
kjellanderdd705472016-06-09 11:17:27 -070095API_DIRS = NATIVE_API_DIRS[:] + LEGACY_API_DIRS[:]
kjellander53047c92015-12-02 23:56:14 -080096
97
ehmaldonado4fb97462017-01-30 05:27:22 -080098def _RunCommand(command, cwd):
99 """Runs a command and returns the output from that command."""
100 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
101 cwd=cwd)
102 stdout = p.stdout.read()
103 stderr = p.stderr.read()
104 p.wait()
105 p.stdout.close()
106 p.stderr.close()
107 return p.returncode, stdout, stderr
108
109
kjellander53047c92015-12-02 23:56:14 -0800110def _VerifyNativeApiHeadersListIsValid(input_api, output_api):
111 """Ensures the list of native API header directories is up to date."""
112 non_existing_paths = []
113 native_api_full_paths = [
114 input_api.os_path.join(input_api.PresubmitLocalPath(),
kjellanderdd705472016-06-09 11:17:27 -0700115 *path.split('/')) for path in API_DIRS]
kjellander53047c92015-12-02 23:56:14 -0800116 for path in native_api_full_paths:
117 if not os.path.isdir(path):
118 non_existing_paths.append(path)
119 if non_existing_paths:
120 return [output_api.PresubmitError(
121 'Directories to native API headers have changed which has made the '
122 'list in PRESUBMIT.py outdated.\nPlease update it to the current '
123 'location of our native APIs.',
124 non_existing_paths)]
125 return []
126
kjellanderc88b5d52017-04-05 06:42:43 -0700127API_CHANGE_MSG = """
kwibergeb133022016-04-07 07:41:48 -0700128You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 04:13:23 -0800129 1. Make compatible changes that don't break existing clients. Usually
130 this is done by keeping the existing method signatures unchanged.
131 2. Mark the old stuff as deprecated (see RTC_DEPRECATED macro).
kwibergeb133022016-04-07 07:41:48 -0700132 3. Create a timeline and plan for when the deprecated stuff will be
133 removed. (The amount of time we give users to change their code
134 should be informed by how much work it is for them. If they just
135 need to replace one name with another or something equally
136 simple, 1-2 weeks might be good; if they need to do serious work,
137 up to 3 months may be called for.)
138 4. Update/inform existing downstream code owners to stop using the
139 deprecated stuff. (Send announcements to
140 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
141 5. Remove the deprecated stuff, once the agreed-upon amount of time
142 has passed.
143Related files:
144"""
kjellander53047c92015-12-02 23:56:14 -0800145
146def _CheckNativeApiHeaderChanges(input_api, output_api):
147 """Checks to remind proper changing of native APIs."""
148 files = []
149 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
150 if f.LocalPath().endswith('.h'):
kjellanderdd705472016-06-09 11:17:27 -0700151 for path in API_DIRS:
kjellander53047c92015-12-02 23:56:14 -0800152 if os.path.dirname(f.LocalPath()) == path:
153 files.append(f)
154
155 if files:
kjellanderc88b5d52017-04-05 06:42:43 -0700156 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
kjellander53047c92015-12-02 23:56:14 -0800157 return []
158
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100159
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000160def _CheckNoIOStreamInHeaders(input_api, output_api):
161 """Checks to make sure no .h files include <iostream>."""
162 files = []
163 pattern = input_api.re.compile(r'^#include\s*<iostream>',
164 input_api.re.MULTILINE)
165 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
166 if not f.LocalPath().endswith('.h'):
167 continue
168 contents = input_api.ReadFile(f)
169 if pattern.search(contents):
170 files.append(f)
171
172 if len(files):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200173 return [output_api.PresubmitError(
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000174 'Do not #include <iostream> in header files, since it inserts static ' +
175 'initialization into every file including the header. Instead, ' +
176 '#include <ostream>. See http://crbug.com/94794',
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200177 files)]
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000178 return []
179
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000180
kjellander6aeef742017-02-20 01:13:18 -0800181def _CheckNoPragmaOnce(input_api, output_api):
182 """Make sure that banned functions are not used."""
183 files = []
184 pattern = input_api.re.compile(r'^#pragma\s+once',
185 input_api.re.MULTILINE)
186 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
187 if not f.LocalPath().endswith('.h'):
188 continue
189 contents = input_api.ReadFile(f)
190 if pattern.search(contents):
191 files.append(f)
192
193 if files:
194 return [output_api.PresubmitError(
195 'Do not use #pragma once in header files.\n'
196 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
197 files)]
198 return []
199
200
kjellanderc88b5d52017-04-05 06:42:43 -0700201def _CheckNoFRIEND_TEST(input_api, output_api): # pylint: disable=invalid-name
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000202 """Make sure that gtest's FRIEND_TEST() macro is not used, the
203 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
204 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
205 problems = []
206
207 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h'))
208 for f in input_api.AffectedFiles(file_filter=file_filter):
209 for line_num, line in f.ChangedContents():
210 if 'FRIEND_TEST(' in line:
211 problems.append(' %s:%d' % (f.LocalPath(), line_num))
212
213 if not problems:
214 return []
215 return [output_api.PresubmitPromptWarning('WebRTC\'s code should not use '
216 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and '
217 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
218
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000219
oprypin2aa463f2017-03-23 03:17:02 -0700220def _IsLintBlacklisted(blacklist_paths, file_path):
221 """ Checks if a file is blacklisted for lint check."""
222 for path in blacklist_paths:
223 if file_path == path or os.path.dirname(file_path).startswith(path):
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100224 return True
225 return False
226
227
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000228def _CheckApprovedFilesLintClean(input_api, output_api,
229 source_file_filter=None):
oprypin2aa463f2017-03-23 03:17:02 -0700230 """Checks that all new or non-blacklisted .cc and .h files pass cpplint.py.
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000231 This check is based on _CheckChangeLintsClean in
232 depot_tools/presubmit_canned_checks.py but has less filters and only checks
233 added files."""
234 result = []
235
236 # Initialize cpplint.
237 import cpplint
238 # Access to a protected member _XX of a client class
239 # pylint: disable=W0212
240 cpplint._cpplint_state.ResetErrorCounts()
241
jbauchc4e3ead2016-02-19 00:25:55 -0800242 lint_filters = cpplint._Filters()
243 lint_filters.extend(BLACKLIST_LINT_FILTERS)
244 cpplint._SetFilters(','.join(lint_filters))
245
oprypin2aa463f2017-03-23 03:17:02 -0700246 # Create a platform independent blacklist for cpplint.
247 blacklist_paths = [input_api.os_path.join(*path.split('/'))
248 for path in CPPLINT_BLACKLIST]
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100249
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000250 # Use the strictest verbosity level for cpplint.py (level 1) which is the
oprypin2aa463f2017-03-23 03:17:02 -0700251 # default when running cpplint.py from command line. To make it possible to
252 # work with not-yet-converted code, we're only applying it to new (or
253 # moved/renamed) files and files not listed in CPPLINT_BLACKLIST.
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000254 verbosity_level = 1
255 files = []
256 for f in input_api.AffectedSourceFiles(source_file_filter):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200257 # Note that moved/renamed files also count as added.
oprypin2aa463f2017-03-23 03:17:02 -0700258 if f.Action() == 'A' or not _IsLintBlacklisted(blacklist_paths,
259 f.LocalPath()):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000260 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000261
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000262 for file_name in files:
263 cpplint.ProcessFile(file_name, verbosity_level)
264
265 if cpplint._cpplint_state.error_count > 0:
266 if input_api.is_committing:
oprypin8e58d652017-03-21 07:52:41 -0700267 res_type = output_api.PresubmitError
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000268 else:
269 res_type = output_api.PresubmitPromptWarning
270 result = [res_type('Changelist failed cpplint.py check.')]
271
272 return result
273
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100274def _CheckNoSourcesAbove(input_api, gn_files, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700275 # Disallow referencing source files with paths above the GN file location.
276 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
277 re.MULTILINE | re.DOTALL)
278 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
279 violating_gn_files = set()
280 violating_source_entries = []
281 for gn_file in gn_files:
282 contents = input_api.ReadFile(gn_file)
283 for source_block_match in source_pattern.finditer(contents):
284 # Find all source list entries starting with ../ in the source block
285 # (exclude overrides entries).
286 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
287 source_file = file_list_match.group(1)
288 if 'overrides/' not in source_file:
289 violating_source_entries.append(source_file)
290 violating_gn_files.add(gn_file)
291 if violating_gn_files:
292 return [output_api.PresubmitError(
293 'Referencing source files above the directory of the GN file is not '
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100294 'allowed. Please introduce new GN targets in the proper location '
295 'instead.\n'
ehmaldonado5b1ba082016-09-02 05:51:08 -0700296 'Invalid source entries:\n'
297 '%s\n'
298 'Violating GN files:' % '\n'.join(violating_source_entries),
299 items=violating_gn_files)]
300 return []
301
kjellander7439f972016-12-05 22:47:46 -0800302def _CheckNoMixingCAndCCSources(input_api, gn_files, output_api):
303 # Disallow mixing .c and .cc source files in the same target.
304 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
305 re.MULTILINE | re.DOTALL)
306 file_pattern = input_api.re.compile(r'"(.*)"')
307 violating_gn_files = dict()
308 for gn_file in gn_files:
309 contents = input_api.ReadFile(gn_file)
310 for source_block_match in source_pattern.finditer(contents):
311 c_files = []
312 cc_files = []
313 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
314 source_file = file_list_match.group(1)
315 if source_file.endswith('.c'):
316 c_files.append(source_file)
317 if source_file.endswith('.cc'):
318 cc_files.append(source_file)
319 if c_files and cc_files:
320 violating_gn_files[gn_file.LocalPath()] = sorted(c_files + cc_files)
321 if violating_gn_files:
322 return [output_api.PresubmitError(
323 'GN targets cannot mix .cc and .c source files. Please create a '
324 'separate target for each collection of sources.\n'
325 'Mixed sources: \n'
326 '%s\n'
327 'Violating GN files:' % json.dumps(violating_gn_files, indent=2),
328 items=violating_gn_files.keys())]
329 return []
330
ehmaldonado4fb97462017-01-30 05:27:22 -0800331def _CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
332 cwd = input_api.PresubmitLocalPath()
mbonadeiab587dc2017-05-12 04:13:31 -0700333 script_path = os.path.join('tools_webrtc', 'presubmit_checks_lib',
334 'check_package_boundaries.py')
ehmaldonado4fb97462017-01-30 05:27:22 -0800335 webrtc_path = os.path.join('webrtc')
336 command = [sys.executable, script_path, webrtc_path]
337 command += [gn_file.LocalPath() for gn_file in gn_files]
338 returncode, _, stderr = _RunCommand(command, cwd)
339 if returncode:
340 return [output_api.PresubmitError(
341 'There are package boundary violations in the following GN files:\n\n'
342 '%s' % stderr)]
343 return []
344
ehmaldonado5b1ba082016-09-02 05:51:08 -0700345def _CheckGnChanges(input_api, output_api):
346 source_file_filter = lambda x: input_api.FilterSourceFile(
347 x, white_list=(r'.+\.(gn|gni)$',))
348
349 gn_files = []
350 for f in input_api.AffectedSourceFiles(source_file_filter):
351 if f.LocalPath().startswith('webrtc'):
352 gn_files.append(f)
353
354 result = []
355 if gn_files:
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100356 result.extend(_CheckNoSourcesAbove(input_api, gn_files, output_api))
kjellander7439f972016-12-05 22:47:46 -0800357 result.extend(_CheckNoMixingCAndCCSources(input_api, gn_files, output_api))
ehmaldonado4fb97462017-01-30 05:27:22 -0800358 result.extend(_CheckNoPackageBoundaryViolations(
359 input_api, gn_files, output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700360 return result
361
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000362def _CheckUnwantedDependencies(input_api, output_api):
363 """Runs checkdeps on #include statements added in this
364 change. Breaking - rules is an error, breaking ! rules is a
365 warning.
366 """
367 # Copied from Chromium's src/PRESUBMIT.py.
368
369 # We need to wait until we have an input_api object and use this
370 # roundabout construct to import checkdeps because this file is
371 # eval-ed and thus doesn't have __file__.
372 original_sys_path = sys.path
373 try:
kjellander@webrtc.orgaefe61a2014-12-08 13:00:30 +0000374 checkdeps_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
375 'buildtools', 'checkdeps')
376 if not os.path.exists(checkdeps_path):
377 return [output_api.PresubmitError(
378 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
379 'download Chromium and setup the symlinks?' % checkdeps_path)]
380 sys.path.append(checkdeps_path)
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000381 import checkdeps
382 from cpp_checker import CppChecker
383 from rules import Rule
384 finally:
385 # Restore sys.path to what it was before.
386 sys.path = original_sys_path
387
388 added_includes = []
389 for f in input_api.AffectedFiles():
390 if not CppChecker.IsCppFile(f.LocalPath()):
391 continue
392
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200393 changed_lines = [line for _, line in f.ChangedContents()]
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000394 added_includes.append([f.LocalPath(), changed_lines])
395
396 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
397
398 error_descriptions = []
399 warning_descriptions = []
400 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
401 added_includes):
402 description_with_path = '%s\n %s' % (path, rule_description)
403 if rule_type == Rule.DISALLOW:
404 error_descriptions.append(description_with_path)
405 else:
406 warning_descriptions.append(description_with_path)
407
408 results = []
409 if error_descriptions:
410 results.append(output_api.PresubmitError(
kjellandera7066a32017-03-23 03:47:05 -0700411 'You added one or more #includes that violate checkdeps rules.\n'
412 'Check that the DEPS files in these locations contain valid rules.\n'
413 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
414 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000415 error_descriptions))
416 if warning_descriptions:
417 results.append(output_api.PresubmitPromptOrNotify(
418 'You added one or more #includes of files that are temporarily\n'
419 'allowed but being removed. Can you avoid introducing the\n'
kjellandera7066a32017-03-23 03:47:05 -0700420 '#include? See relevant DEPS file(s) for details and contacts.\n'
421 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
422 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000423 warning_descriptions))
424 return results
425
kjellanderd1e26a92016-09-19 08:11:16 -0700426def _CheckChangeHasBugField(input_api, output_api):
427 """Requires that the changelist have a BUG= field.
428
429 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
430 since it fails the presubmit if the BUG= field is missing or doesn't contain
431 a bug reference.
432 """
433 if input_api.change.BUG:
434 return []
435 else:
436 return [output_api.PresubmitError(
437 'The BUG=[bug number] field is mandatory. Please create a bug and '
438 'reference it using either of:\n'
439 ' * https://bugs.webrtc.org - reference it using BUG=webrtc:XXXX\n'
440 ' * https://crbug.com - reference it using BUG=chromium:XXXXXX')]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000441
kjellander569cf942016-02-11 05:02:59 -0800442def _CheckJSONParseErrors(input_api, output_api):
443 """Check that JSON files do not contain syntax errors."""
444
445 def FilterFile(affected_file):
446 return input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
447
448 def GetJSONParseError(input_api, filename):
449 try:
450 contents = input_api.ReadFile(filename)
451 input_api.json.loads(contents)
452 except ValueError as e:
453 return e
454 return None
455
456 results = []
457 for affected_file in input_api.AffectedFiles(
458 file_filter=FilterFile, include_deletes=False):
459 parse_error = GetJSONParseError(input_api,
460 affected_file.AbsoluteLocalPath())
461 if parse_error:
462 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
463 (affected_file.LocalPath(), parse_error)))
464 return results
465
466
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200467def _RunPythonTests(input_api, output_api):
kjellanderc88b5d52017-04-05 06:42:43 -0700468 def Join(*args):
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200469 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
470
471 test_directories = [
kjellander41bafb22017-06-30 04:14:54 -0700472 Join('webrtc', 'rtc_tools', 'py_event_log_analyzer'),
473 Join('webrtc', 'rtc_tools'),
oprypinabd101b2017-04-06 23:21:30 -0700474 Join('webrtc', 'audio', 'test', 'unittests'),
ehmaldonado4fb97462017-01-30 05:27:22 -0800475 ] + [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200476 root for root, _, files in os.walk(Join('tools_webrtc'))
ehmaldonado4fb97462017-01-30 05:27:22 -0800477 if any(f.endswith('_test.py') for f in files)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200478 ]
479
480 tests = []
481 for directory in test_directories:
482 tests.extend(
483 input_api.canned_checks.GetUnitTestsInDirectory(
484 input_api,
485 output_api,
486 directory,
487 whitelist=[r'.+_test\.py$']))
488 return input_api.RunTests(tests, parallel=True)
489
490
mbonadei38415b22017-04-07 05:38:01 -0700491def _CheckUsageOfGoogleProtobufNamespace(input_api, output_api):
492 """Checks that the namespace google::protobuf has not been used."""
493 files = []
494 pattern = input_api.re.compile(r'google::protobuf')
495 proto_utils_path = os.path.join('webrtc', 'base', 'protobuf_utils.h')
496 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
497 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
498 continue
499 contents = input_api.ReadFile(f)
500 if pattern.search(contents):
501 files.append(f)
502
503 if files:
504 return [output_api.PresubmitError(
505 'Please avoid to use namespace `google::protobuf` directly.\n'
506 'Add a using directive in `%s` and include that header instead.'
507 % proto_utils_path, files)]
508 return []
509
510
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000511def _CommonChecks(input_api, output_api):
512 """Checks common to both upload and commit."""
niklase@google.comda159d62011-05-30 11:51:34 +0000513 results = []
tkchin42f580e2015-11-26 23:18:23 -0800514 # Filter out files that are in objc or ios dirs from being cpplint-ed since
515 # they do not follow C++ lint rules.
516 black_list = input_api.DEFAULT_BLACK_LIST + (
517 r".*\bobjc[\\\/].*",
Kári Tristan Helgason3fa35172016-09-09 08:55:05 +0000518 r".*objc\.[hcm]+$",
hjon65ae2d82016-08-02 23:55:44 -0700519 r"webrtc\/build\/ios\/SDK\/.*",
tkchin42f580e2015-11-26 23:18:23 -0800520 )
521 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list)
522 results.extend(_CheckApprovedFilesLintClean(
523 input_api, output_api, source_file_filter))
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000524 results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100525 black_list=(r'^base[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200526 r'^build[\\\/].*\.py$',
527 r'^buildtools[\\\/].*\.py$',
kjellander38c65c82017-04-12 22:43:38 -0700528 r'^infra[\\\/].*\.py$',
Henrik Kjellander0779e8f2016-12-22 12:01:17 +0100529 r'^ios[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200530 r'^out.*[\\\/].*\.py$',
531 r'^testing[\\\/].*\.py$',
532 r'^third_party[\\\/].*\.py$',
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100533 r'^tools[\\\/].*\.py$',
kjellanderafd54942016-12-17 12:21:39 -0800534 # TODO(phoglund): should arguably be checked.
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200535 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
536 r'^tools_webrtc[\\\/]valgrind[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200537 r'^xcodebuild.*[\\\/].*\.py$',),
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200538 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800539
nisse3d21e232016-09-02 03:07:06 -0700540 # TODO(nisse): talk/ is no more, so make below checks simpler?
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200541 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
542 # we need to have different license checks in talk/ and webrtc/ directories.
543 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200544
tkchin3cd9a302016-06-08 12:40:28 -0700545 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
546 # ObjC subdirectories ObjC headers.
547 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100548 # Skip long-lines check for DEPS and GN files.
549 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
tkchin3cd9a302016-06-08 12:40:28 -0700550 eighty_char_sources = lambda x: input_api.FilterSourceFile(x,
551 black_list=build_file_filter_list + objc_filter_list)
552 hundred_char_sources = lambda x: input_api.FilterSourceFile(x,
553 white_list=objc_filter_list)
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000554 results.extend(input_api.canned_checks.CheckLongLines(
tkchin3cd9a302016-06-08 12:40:28 -0700555 input_api, output_api, maxlen=80, source_file_filter=eighty_char_sources))
556 results.extend(input_api.canned_checks.CheckLongLines(
557 input_api, output_api, maxlen=100,
558 source_file_filter=hundred_char_sources))
559
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000560 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
561 input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000562 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
563 input_api, output_api))
kjellandere5dc62a2016-12-14 00:16:21 -0800564 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
565 input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000566 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
567 input_api, output_api))
kjellander53047c92015-12-02 23:56:14 -0800568 results.extend(_CheckNativeApiHeaderChanges(input_api, output_api))
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000569 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
kjellander6aeef742017-02-20 01:13:18 -0800570 results.extend(_CheckNoPragmaOnce(input_api, output_api))
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000571 results.extend(_CheckNoFRIEND_TEST(input_api, output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700572 results.extend(_CheckGnChanges(input_api, output_api))
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000573 results.extend(_CheckUnwantedDependencies(input_api, output_api))
kjellander569cf942016-02-11 05:02:59 -0800574 results.extend(_CheckJSONParseErrors(input_api, output_api))
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200575 results.extend(_RunPythonTests(input_api, output_api))
mbonadei38415b22017-04-07 05:38:01 -0700576 results.extend(_CheckUsageOfGoogleProtobufNamespace(input_api, output_api))
mbonadei26c26342017-05-12 02:06:16 -0700577 results.extend(_CheckOrphanHeaders(input_api, output_api))
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200578 results.extend(_CheckNewLineAtTheEndOfProtoFiles(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000579 return results
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000580
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000581
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000582def CheckChangeOnUpload(input_api, output_api):
583 results = []
584 results.extend(_CommonChecks(input_api, output_api))
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200585 results.extend(
586 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
niklase@google.comda159d62011-05-30 11:51:34 +0000587 return results
588
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000589
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000590def CheckChangeOnCommit(input_api, output_api):
niklase@google.com1198db92011-06-09 07:07:24 +0000591 results = []
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000592 results.extend(_CommonChecks(input_api, output_api))
kjellander53047c92015-12-02 23:56:14 -0800593 results.extend(_VerifyNativeApiHeadersListIsValid(input_api, output_api))
niklase@google.com1198db92011-06-09 07:07:24 +0000594 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000595 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
596 input_api, output_api))
597 results.extend(input_api.canned_checks.CheckChangeHasDescription(
598 input_api, output_api))
kjellanderd1e26a92016-09-19 08:11:16 -0700599 results.extend(_CheckChangeHasBugField(input_api, output_api))
kjellander@webrtc.org12cb88c2014-02-13 11:53:43 +0000600 results.extend(input_api.canned_checks.CheckTreeIsOpen(
601 input_api, output_api,
602 json_url='http://webrtc-status.appspot.com/current?format=json'))
niklase@google.com1198db92011-06-09 07:07:24 +0000603 return results
mbonadei74973ed2017-05-09 07:58:05 -0700604
605
606def _CheckOrphanHeaders(input_api, output_api):
607 # We need to wait until we have an input_api object and use this
608 # roundabout construct to import prebubmit_checks_lib because this file is
609 # eval-ed and thus doesn't have __file__.
610 error_msg = """Header file {} is not listed in any GN target.
611 Please create a target or add it to an existing one in {}"""
612 results = []
613 original_sys_path = sys.path
614 try:
615 sys.path = sys.path + [input_api.os_path.join(
616 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')]
617 from check_orphan_headers import GetBuildGnPathFromFilePath
618 from check_orphan_headers import IsHeaderInBuildGn
619 finally:
620 # Restore sys.path to what it was before.
621 sys.path = original_sys_path
622
623 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
mbonadeia644ad32017-05-10 05:21:55 -0700624 if f.LocalPath().endswith('.h') and f.Action() == 'A':
mbonadei74973ed2017-05-09 07:58:05 -0700625 file_path = os.path.abspath(f.LocalPath())
626 root_dir = os.getcwd()
627 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
628 root_dir)
629 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
630 if not in_build_gn:
631 results.append(output_api.PresubmitError(error_msg.format(
632 file_path, gn_file_path)))
633 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200634
635
636def _CheckNewLineAtTheEndOfProtoFiles(input_api, output_api):
637 """Checks that all .proto files are terminated with a newline."""
638 error_msg = 'File {} must end with exactly one newline.'
639 results = []
640 source_file_filter = lambda x: input_api.FilterSourceFile(
641 x, white_list=(r'.+\.proto$',))
642 for f in input_api.AffectedSourceFiles(source_file_filter):
643 file_path = f.LocalPath()
644 with open(file_path) as f:
645 lines = f.readlines()
646 if lines[-1] != '\n' or lines[-2] == '\n':
647 results.append(output_api.PresubmitError(error_msg.format(file_path)))
648 return results