aboutsummaryrefslogtreecommitdiff
path: root/PRESUBMIT.py
diff options
context:
space:
mode:
Diffstat (limited to 'PRESUBMIT.py')
-rwxr-xr-x[-rw-r--r--]PRESUBMIT.py280
1 files changed, 242 insertions, 38 deletions
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 6eaa4083b8..e7ceac94cf 100644..100755
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -6,32 +6,13 @@
# in the file PATENTS. All contributing project authors may
# be found in the AUTHORS file in the root of the source tree.
-import os.path
-
-# All folders in LINT_FOLDERS will be scanned by cpplint by the presubmit
-# script. Note that subfolders are not included.
-LINT_FOLDERS = ['src/video_engine']
-
-def _LicenseHeader(input_api):
- """Returns the license header regexp."""
- # Accept any year number from 2011 to the current year
- current_year = int(input_api.time.strftime('%Y'))
- allowed_years = (str(s) for s in reversed(xrange(2011, current_year + 1)))
- years_re = '(' + '|'.join(allowed_years) + ')'
- license_header = (
- r'.*? Copyright \(c\) %(year)s The WebRTC project authors\. '
- r'All Rights Reserved\.\n'
- r'.*?\n'
- r'.*? Use of this source code is governed by a BSD-style license\n'
- r'.*? that can be found in the LICENSE file in the root of the source\n'
- r'.*? tree\. An additional intellectual property rights grant can be '
- r'found\n'
- r'.*? in the file PATENTS\. All contributing project authors may\n'
- r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
- ) % {
- 'year': years_re,
- }
- return license_header
+import json
+import os
+import platform
+import re
+import subprocess
+import sys
+
def _CheckNoIOStreamInHeaders(input_api, output_api):
"""Checks to make sure no .h files include <iostream>."""
@@ -46,13 +27,14 @@ def _CheckNoIOStreamInHeaders(input_api, output_api):
files.append(f)
if len(files):
- return [ output_api.PresubmitError(
+ return [output_api.PresubmitError(
'Do not #include <iostream> in header files, since it inserts static ' +
'initialization into every file including the header. Instead, ' +
'#include <ostream>. See http://crbug.com/94794',
- files) ]
+ files)]
return []
+
def _CheckNoFRIEND_TEST(input_api, output_api):
"""Make sure that gtest's FRIEND_TEST() macro is not used, the
FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
@@ -71,10 +53,6 @@ def _CheckNoFRIEND_TEST(input_api, output_api):
'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and '
'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
-def _IsLintWhitelisted(file_name):
- """ Checks if a file is whitelisted for lint check."""
- # TODO(mflodman) Include subfolders in the check.
- return (os.path.dirname(file_name) in LINT_FOLDERS)
def _CheckApprovedFilesLintClean(input_api, output_api,
source_file_filter=None):
@@ -104,8 +82,8 @@ def _CheckApprovedFilesLintClean(input_api, output_api,
verbosity_level = 1
files = []
for f in input_api.AffectedSourceFiles(source_file_filter):
- # Note that moved/renamed files also count as added for svn.
- if (f.Action() == 'A' or _IsLintWhitelisted(f.LocalPath())):
+ # Note that moved/renamed files also count as added.
+ if f.Action() == 'A':
files.append(f.AbsoluteLocalPath())
for file_name in files:
@@ -122,12 +100,205 @@ def _CheckApprovedFilesLintClean(input_api, output_api,
return result
+def _CheckNoRtcBaseDeps(input_api, gyp_files, output_api):
+ pattern = input_api.re.compile(r"base.gyp:rtc_base\s*'")
+ violating_files = []
+ for f in gyp_files:
+ gyp_exceptions = (
+ 'base_tests.gyp',
+ 'desktop_capture.gypi',
+ 'libjingle.gyp',
+ 'libjingle_tests.gyp',
+ 'p2p.gyp',
+ 'sound.gyp',
+ 'webrtc_test_common.gyp',
+ 'webrtc_tests.gypi',
+ )
+ if f.LocalPath().endswith(gyp_exceptions):
+ continue
+ contents = input_api.ReadFile(f)
+ if pattern.search(contents):
+ violating_files.append(f)
+ if violating_files:
+ return [output_api.PresubmitError(
+ 'Depending on rtc_base is not allowed. Change your dependency to '
+ 'rtc_base_approved and possibly sanitize and move the desired source '
+ 'file(s) to rtc_base_approved.\nChanged GYP files:',
+ items=violating_files)]
+ return []
+
+def _CheckNoSourcesAboveGyp(input_api, gyp_files, output_api):
+ # Disallow referencing source files with paths above the GYP file location.
+ source_pattern = input_api.re.compile(r'sources.*?\[(.*?)\]',
+ re.MULTILINE | re.DOTALL)
+ file_pattern = input_api.re.compile(r"'((\.\./.*?)|(<\(webrtc_root\).*?))'")
+ violating_gyp_files = set()
+ violating_source_entries = []
+ for gyp_file in gyp_files:
+ contents = input_api.ReadFile(gyp_file)
+ for source_block_match in source_pattern.finditer(contents):
+ # Find all source list entries starting with ../ in the source block
+ # (exclude overrides entries).
+ for file_list_match in file_pattern.finditer(source_block_match.group(0)):
+ source_file = file_list_match.group(0)
+ if 'overrides/' not in source_file:
+ violating_source_entries.append(source_file)
+ violating_gyp_files.add(gyp_file)
+ if violating_gyp_files:
+ return [output_api.PresubmitError(
+ 'Referencing source files above the directory of the GYP file is not '
+ 'allowed. Please introduce new GYP targets and/or GYP files in the '
+ 'proper location instead.\n'
+ 'Invalid source entries:\n'
+ '%s\n'
+ 'Violating GYP files:' % '\n'.join(violating_source_entries),
+ items=violating_gyp_files)]
+ return []
+
+def _CheckGypChanges(input_api, output_api):
+ source_file_filter = lambda x: input_api.FilterSourceFile(
+ x, white_list=(r'.+\.(gyp|gypi)$',))
+
+ gyp_files = []
+ for f in input_api.AffectedSourceFiles(source_file_filter):
+ if f.LocalPath().startswith('webrtc'):
+ gyp_files.append(f)
+
+ result = []
+ if gyp_files:
+ result.append(output_api.PresubmitNotifyResult(
+ 'As you\'re changing GYP files: please make sure corresponding '
+ 'BUILD.gn files are also updated.\nChanged GYP files:',
+ items=gyp_files))
+ result.extend(_CheckNoRtcBaseDeps(input_api, gyp_files, output_api))
+ result.extend(_CheckNoSourcesAboveGyp(input_api, gyp_files, output_api))
+ return result
+
+def _CheckUnwantedDependencies(input_api, output_api):
+ """Runs checkdeps on #include statements added in this
+ change. Breaking - rules is an error, breaking ! rules is a
+ warning.
+ """
+ # Copied from Chromium's src/PRESUBMIT.py.
+
+ # We need to wait until we have an input_api object and use this
+ # roundabout construct to import checkdeps because this file is
+ # eval-ed and thus doesn't have __file__.
+ original_sys_path = sys.path
+ try:
+ checkdeps_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
+ 'buildtools', 'checkdeps')
+ if not os.path.exists(checkdeps_path):
+ return [output_api.PresubmitError(
+ 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
+ 'download Chromium and setup the symlinks?' % checkdeps_path)]
+ sys.path.append(checkdeps_path)
+ import checkdeps
+ from cpp_checker import CppChecker
+ from rules import Rule
+ finally:
+ # Restore sys.path to what it was before.
+ sys.path = original_sys_path
+
+ added_includes = []
+ for f in input_api.AffectedFiles():
+ if not CppChecker.IsCppFile(f.LocalPath()):
+ continue
+
+ changed_lines = [line for _, line in f.ChangedContents()]
+ added_includes.append([f.LocalPath(), changed_lines])
+
+ deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
+
+ error_descriptions = []
+ warning_descriptions = []
+ for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
+ added_includes):
+ description_with_path = '%s\n %s' % (path, rule_description)
+ if rule_type == Rule.DISALLOW:
+ error_descriptions.append(description_with_path)
+ else:
+ warning_descriptions.append(description_with_path)
+
+ results = []
+ if error_descriptions:
+ results.append(output_api.PresubmitError(
+ 'You added one or more #includes that violate checkdeps rules.',
+ error_descriptions))
+ if warning_descriptions:
+ results.append(output_api.PresubmitPromptOrNotify(
+ 'You added one or more #includes of files that are temporarily\n'
+ 'allowed but being removed. Can you avoid introducing the\n'
+ '#include? See relevant DEPS file(s) for details and contacts.',
+ warning_descriptions))
+ return results
+
+
+def _RunPythonTests(input_api, output_api):
+ def join(*args):
+ return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
+
+ test_directories = [
+ join('tools', 'autoroller', 'unittests'),
+ ]
+
+ tests = []
+ for directory in test_directories:
+ tests.extend(
+ input_api.canned_checks.GetUnitTestsInDirectory(
+ input_api,
+ output_api,
+ directory,
+ whitelist=[r'.+_test\.py$']))
+ return input_api.RunTests(tests, parallel=True)
+
+
def _CommonChecks(input_api, output_api):
"""Checks common to both upload and commit."""
- # TODO(kjellander): Use presubmit_canned_checks.PanProjectChecks too.
results = []
+ results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
+ black_list=(r'^.*gviz_api\.py$',
+ r'^.*gaeunit\.py$',
+ # Embedded shell-script fakes out pylint.
+ r'^build[\\\/].*\.py$',
+ r'^buildtools[\\\/].*\.py$',
+ r'^chromium[\\\/].*\.py$',
+ r'^google_apis[\\\/].*\.py$',
+ r'^net.*[\\\/].*\.py$',
+ r'^out.*[\\\/].*\.py$',
+ r'^testing[\\\/].*\.py$',
+ r'^third_party[\\\/].*\.py$',
+ r'^tools[\\\/]find_depot_tools.py$',
+ r'^tools[\\\/]clang[\\\/].*\.py$',
+ r'^tools[\\\/]generate_library_loader[\\\/].*\.py$',
+ r'^tools[\\\/]gn[\\\/].*\.py$',
+ r'^tools[\\\/]gyp[\\\/].*\.py$',
+ r'^tools[\\\/]isolate_driver.py$',
+ r'^tools[\\\/]protoc_wrapper[\\\/].*\.py$',
+ r'^tools[\\\/]python[\\\/].*\.py$',
+ r'^tools[\\\/]python_charts[\\\/]data[\\\/].*\.py$',
+ r'^tools[\\\/]refactoring[\\\/].*\.py$',
+ r'^tools[\\\/]swarming_client[\\\/].*\.py$',
+ r'^tools[\\\/]vim[\\\/].*\.py$',
+ # TODO(phoglund): should arguably be checked.
+ r'^tools[\\\/]valgrind-webrtc[\\\/].*\.py$',
+ r'^tools[\\\/]valgrind[\\\/].*\.py$',
+ r'^tools[\\\/]win[\\\/].*\.py$',
+ r'^xcodebuild.*[\\\/].*\.py$',),
+ disabled_warnings=['F0401', # Failed to import x
+ 'E0611', # No package y in x
+ 'W0232', # Class has no __init__ method
+ ],
+ pylintrc='pylintrc'))
+ # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
+ # we need to have different license checks in talk/ and webrtc/ directories.
+ # Instead, hand-picked checks are included below.
+
+ # Skip long-lines check for DEPS, GN and GYP files.
+ long_lines_sources = lambda x: input_api.FilterSourceFile(x,
+ black_list=(r'.+\.gyp$', r'.+\.gypi$', r'.+\.gn$', r'.+\.gni$', 'DEPS'))
results.extend(input_api.canned_checks.CheckLongLines(
- input_api, output_api))
+ input_api, output_api, maxlen=80, source_file_filter=long_lines_sources))
results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
input_api, output_api))
results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
@@ -135,17 +306,22 @@ def _CommonChecks(input_api, output_api):
results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
input_api, output_api))
results.extend(_CheckApprovedFilesLintClean(input_api, output_api))
- results.extend(input_api.canned_checks.CheckLicense(
- input_api, output_api, _LicenseHeader(input_api)))
results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
results.extend(_CheckNoFRIEND_TEST(input_api, output_api))
+ results.extend(_CheckGypChanges(input_api, output_api))
+ results.extend(_CheckUnwantedDependencies(input_api, output_api))
+ results.extend(_RunPythonTests(input_api, output_api))
return results
+
def CheckChangeOnUpload(input_api, output_api):
results = []
results.extend(_CommonChecks(input_api, output_api))
+ results.extend(
+ input_api.canned_checks.CheckGNFormatted(input_api, output_api))
return results
+
def CheckChangeOnCommit(input_api, output_api):
results = []
results.extend(_CommonChecks(input_api, output_api))
@@ -158,4 +334,32 @@ def CheckChangeOnCommit(input_api, output_api):
input_api, output_api))
results.extend(input_api.canned_checks.CheckChangeHasTestField(
input_api, output_api))
+ results.extend(input_api.canned_checks.CheckTreeIsOpen(
+ input_api, output_api,
+ json_url='http://webrtc-status.appspot.com/current?format=json'))
return results
+
+
+# pylint: disable=W0613
+def GetPreferredTryMasters(project, change):
+ cq_config_path = os.path.join(
+ change.RepositoryRoot(), 'infra', 'config', 'cq.cfg')
+ # commit_queue.py below is a script in depot_tools directory, which has a
+ # 'builders' command to retrieve a list of CQ builders from the CQ config.
+ is_win = platform.system() == 'Windows'
+ masters = json.loads(subprocess.check_output(
+ ['commit_queue', 'builders', cq_config_path], shell=is_win))
+
+ try_config = {}
+ for master in masters:
+ try_config.setdefault(master, {})
+ for builder in masters[master]:
+ if 'presubmit' in builder:
+ # Do not trigger presubmit builders, since they're likely to fail
+ # (e.g. OWNERS checks before finished code review), and we're running
+ # local presubmit anyway.
+ pass
+ else:
+ try_config[master][builder] = ['defaulttests']
+
+ return try_config