diff --git a/PRESUBMIT.py b/PRESUBMIT.py index ff46605230..a356d765c0 100755 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py @@ -406,6 +406,22 @@ def CheckGnChanges(input_api, output_api): output_api)) return result +def CheckGnGen(input_api, output_api): + """Runs `gn gen --check` with default args to detect mismatches between + #includes and dependencies in the BUILD.gn files, as well as general build + errors. + """ + with _AddToPath(input_api.os_path.join( + input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')): + from gn_check import RunGnCheck + errors = RunGnCheck(input_api.PresubmitLocalPath())[:5] + if errors: + return [output_api.PresubmitPromptWarning( + 'Some #includes do not match the build dependency graph. Please run:\n' + ' gn gen --check ', + long_text='\n\n'.join(errors))] + return [] + def CheckUnwantedDependencies(input_api, output_api): """Runs checkdeps on #include statements added in this change. Breaking - rules is an error, breaking ! rules is a @@ -673,6 +689,7 @@ def CommonChecks(input_api, output_api): def CheckChangeOnUpload(input_api, output_api): results = [] results.extend(CommonChecks(input_api, output_api)) + results.extend(CheckGnGen(input_api, output_api)) results.extend( input_api.canned_checks.CheckGNFormatted(input_api, output_api)) return results diff --git a/tools_webrtc/presubmit_checks_lib/gn_check.py b/tools_webrtc/presubmit_checks_lib/gn_check.py new file mode 100644 index 0000000000..7270bddb23 --- /dev/null +++ b/tools_webrtc/presubmit_checks_lib/gn_check.py @@ -0,0 +1,37 @@ +# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import re +import shutil +import subprocess +import tempfile + + +# GN_ERROR_RE matches the summary of an error output by `gn check`. +# Matches "ERROR" and following lines until it sees an empty line or a line +# containing just underscores. +GN_ERROR_RE = re.compile(r'^ERROR .+(?:\n.*[^_\n].*$)+', re.MULTILINE) + + +def RunGnCheck(root_dir=None): + """Runs `gn gen --check` with default args to detect mismatches between + #includes and dependencies in the BUILD.gn files, as well as general build + errors. + + Returns a list of error summary strings. + """ + out_dir = tempfile.mkdtemp('gn') + try: + command = ['gn', 'gen', '--check', out_dir] + subprocess.check_output(command, cwd=root_dir) + except subprocess.CalledProcessError as err: + return GN_ERROR_RE.findall(err.output) + else: + return [] + finally: + shutil.rmtree(out_dir, ignore_errors=True) diff --git a/tools_webrtc/presubmit_checks_lib/gn_check_test.py b/tools_webrtc/presubmit_checks_lib/gn_check_test.py new file mode 100755 index 0000000000..f7e158cd9b --- /dev/null +++ b/tools_webrtc/presubmit_checks_lib/gn_check_test.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import os +import unittest + +from gn_check import RunGnCheck + + +TESTDATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'testdata') + + +class GnCheckTest(unittest.TestCase): + def testCircularDependencyError(self): + test_dir = os.path.join(TESTDATA_DIR, 'circular_dependency') + expected_errors = ['ERROR Dependency cycle:\n' + ' //:bar ->\n //:foo ->\n //:bar'] + self.assertListEqual(expected_errors, RunGnCheck(test_dir)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tools_webrtc/presubmit_checks_lib/testdata/circular_dependency/.gn b/tools_webrtc/presubmit_checks_lib/testdata/circular_dependency/.gn new file mode 100644 index 0000000000..9fe0b4226c --- /dev/null +++ b/tools_webrtc/presubmit_checks_lib/testdata/circular_dependency/.gn @@ -0,0 +1 @@ +buildconfig = "//BUILDCONFIG.gn" diff --git a/tools_webrtc/presubmit_checks_lib/testdata/circular_dependency/BUILD.gn b/tools_webrtc/presubmit_checks_lib/testdata/circular_dependency/BUILD.gn new file mode 100644 index 0000000000..cf17887e59 --- /dev/null +++ b/tools_webrtc/presubmit_checks_lib/testdata/circular_dependency/BUILD.gn @@ -0,0 +1,14 @@ +toolchain("toolchain") { +} + +static_library("foo") { + deps = [ + ":bar", + ] +} + +static_library("bar") { + deps = [ + ":foo", + ] +} diff --git a/tools_webrtc/presubmit_checks_lib/testdata/circular_dependency/BUILDCONFIG.gn b/tools_webrtc/presubmit_checks_lib/testdata/circular_dependency/BUILDCONFIG.gn new file mode 100644 index 0000000000..48c2a464b2 --- /dev/null +++ b/tools_webrtc/presubmit_checks_lib/testdata/circular_dependency/BUILDCONFIG.gn @@ -0,0 +1 @@ +set_default_toolchain(":toolchain")