Repository: kudu Updated Branches: refs/heads/master bf378b4bd -> 68962df0e
Add script to run clang-tidy against a patch This tooling runs clang-tidy against a given patch, and formats the resulting warnings in a form that can be consumed by the gerrit API[1] This has been running out of a private branch for many months and proven generally useful. This patch also updates .clang-tidy based on the configuration that has been running. [1] https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#set-review Change-Id: I6c6246f4691b0ca143eea4aa8fb6a4b23099ce4e Reviewed-on: http://gerrit.cloudera.org:8080/4381 Reviewed-by: Adar Dembo <[email protected]> Tested-by: Kudu Jenkins Project: http://git-wip-us.apache.org/repos/asf/kudu/repo Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/68962df0 Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/68962df0 Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/68962df0 Branch: refs/heads/master Commit: 68962df0e58fd9ff96212b7265f8a943fd24e792 Parents: bf378b4 Author: Todd Lipcon <[email protected]> Authored: Sun Sep 11 21:37:12 2016 -0700 Committer: Todd Lipcon <[email protected]> Committed: Tue Jun 6 05:55:23 2017 +0000 ---------------------------------------------------------------------- .ycm_extra_conf.py | 36 +------ build-support/clang_tidy_gerrit.py | 175 ++++++++++++++++++++++++++++++++ build-support/compile_flags.py | 60 +++++++++++ src/kudu/.clang-tidy | 3 +- 4 files changed, 241 insertions(+), 33 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/kudu/blob/68962df0/.ycm_extra_conf.py ---------------------------------------------------------------------- diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py index 7d4b69b..2c1efbb 100644 --- a/.ycm_extra_conf.py +++ b/.ycm_extra_conf.py @@ -34,41 +34,15 @@ # This file is based on the example configuration file from YouCompleteMe. import os +import sys import ycm_core +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "build-support")) +from compile_flags import get_flags + # These are the compilation flags that will be used in case there's no # compilation database set (by default, one is not set). -# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR. -flags = [ -'-x', -'c++', -'-DKUDU_HEADERS_NO_STUBS=1', -'-DKUDU_HEADERS_USE_RICH_SLICE=1', -'-DKUDU_HEADERS_USE_SHORT_STATUS_MACROS=1', -'-DKUDU_STATIC_DEFINE', -'-Dintegration_tests_EXPORTS', -'-D__STDC_FORMAT_MACROS', -'-fno-strict-aliasing', -'-msse4.2', -'-Wall', -'-Wno-sign-compare', -'-Wno-deprecated', -'-pthread', -'-ggdb', -'-Qunused-arguments', -'-Wno-ambiguous-member-template', -'-std=c++11', -'-g', -'-fPIC', -'-I', -'src', -'-I', -'build/latest/src', -'-isystem', -'thirdparty/installed/common/include', -'-isystem', -'thirdparty/installed/uninstrumented/include', -] +flags = get_flags() # Set this to the absolute path to the folder (NOT the file!) containing the # compile_commands.json file to use that instead of 'flags'. See here for http://git-wip-us.apache.org/repos/asf/kudu/blob/68962df0/build-support/clang_tidy_gerrit.py ---------------------------------------------------------------------- diff --git a/build-support/clang_tidy_gerrit.py b/build-support/clang_tidy_gerrit.py new file mode 100755 index 0000000..5e38f52 --- /dev/null +++ b/build-support/clang_tidy_gerrit.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import collections +import compile_flags +import json +import logging +import os +import re +import requests +import subprocess +import sys +import unittest +import tempfile + +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + +CLANG_TIDY_DIFF = os.path.join( + ROOT, "thirdparty/installed/uninstrumented/share/clang/clang-tidy-diff.py") +CLANG_TIDY = os.path.join( + ROOT, "thirdparty/clang-toolchain/bin/clang-tidy") + +GERRIT_USER = 'tidybot' +GERRIT_PASSWORD = os.getenv('TIDYBOT_PASSWORD') + +GERRIT_URL = "https://gerrit.cloudera.org" + +def run_tidy(sha="HEAD"): + patch_file = tempfile.NamedTemporaryFile() + cmd = [ + "git", "show", sha, + "--src-prefix=%s/" % ROOT, + "--dst-prefix=%s/" % ROOT] + subprocess.check_call(cmd, + stdout=patch_file) + return subprocess.check_output( + [CLANG_TIDY_DIFF, + "-clang-tidy-binary", CLANG_TIDY, + "-p0", + "--"] + compile_flags.get_flags(), + stdin=file(patch_file.name)) + + +def split_warnings(clang_output): + accumulated = "" + for l in clang_output.splitlines(): + if l == "": + continue + if l.startswith(ROOT) and re.search(r'(warning|error): ', l): + if accumulated: + yield accumulated + accumulated = "" + accumulated += l + "\n" + if accumulated: + yield accumulated + + +def parse_clang_output(clang_output): + ret = [] + for w in split_warnings(clang_output): + m = re.match(r"^(.+?):(\d+):\d+: ((?:warning|error): .+)$", w, re.MULTILINE | re.DOTALL) + if not m: + raise Exception("bad warning: " + w) + path, line, warning = m.groups() + ret.append(dict( + path=os.path.relpath(path, ROOT), + line=int(line), + warning=warning.strip())) + return ret + +def create_gerrit_json_obj(parsed_warnings): + comments = collections.defaultdict(lambda: []) + for warning in parsed_warnings: + comments[warning['path']].append({ + 'line': warning['line'], + 'message': warning['warning'] + }) + return {"comments": comments, + "notify": "OWNER", + "omit_duplicate_comments": "true"} + + +def get_gerrit_revision_url(git_ref): + sha = subprocess.check_output(["git", "rev-parse", git_ref]).strip() + + commit_msg = subprocess.check_output( + ["git", "show", sha]) + matches = re.findall(r'^\s+Change-Id: (I.+)$', commit_msg, re.MULTILINE) + if not matches: + raise Exception("Could not find gerrit Change-Id for commit %s" % sha) + if len(matches) != 1: + raise Exception("Found multiple gerrit Change-Ids for commit %s" % sha) + change_id = matches[0] + return "%s/a/changes/%s/revisions/%s" % (GERRIT_URL, change_id, sha) + + +def post_comments(revision_url_base, gerrit_json_obj): + r = requests.post(revision_url_base + "/review", + auth=(GERRIT_USER, GERRIT_PASSWORD), + data=json.dumps(gerrit_json_obj), + headers={'Content-Type': 'application/json'}) + print "Response:" + print r.headers + print r.status_code + print r.text + + +class TestClangTidyGerrit(unittest.TestCase): + TEST_INPUT = \ +""" +/home/todd/git/kudu/src/kudu/integration-tests/tablet_history_gc-itest.cc:579:55: warning: some warning [warning-name] + some example line of code + +/home/todd/git/kudu/foo/../src/kudu/blah.cc:123:55: error: some error + blah blah +""" + + def test_parse_clang_output(self): + global ROOT + save_root = ROOT + try: + ROOT = "/home/todd/git/kudu" + parsed = parse_clang_output(self.TEST_INPUT) + finally: + ROOT = save_root + self.assertEqual(2, len(parsed)) + + self.assertEqual("src/kudu/integration-tests/tablet_history_gc-itest.cc", parsed[0]['path']) + self.assertEqual(579, parsed[0]['line']) + self.assertEqual("warning: some warning [warning-name]\n" + + " some example line of code", + parsed[0]['warning']) + + self.assertEqual("src/kudu/blah.cc", parsed[1]['path']) + + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + + rev = sys.argv[1] + revision_url = get_gerrit_revision_url(rev) + print revision_url + clang_output = run_tidy(rev) + logging.info("Clang output") + logging.info(clang_output) + logging.info("=" * 80) + parsed = parse_clang_output(clang_output) + if not parsed: + print >>sys.stderr, "No warnings" + sys.exit(0) + print "Parsed clang warnings:" + print json.dumps(parsed, indent=4) + + gerrit_json_obj = create_gerrit_json_obj(parsed) + print "Will post to gerrit:" + print json.dumps(gerrit_json_obj, indent=4) + + post_comments(revision_url, gerrit_json_obj) http://git-wip-us.apache.org/repos/asf/kudu/blob/68962df0/build-support/compile_flags.py ---------------------------------------------------------------------- diff --git a/build-support/compile_flags.py b/build-support/compile_flags.py new file mode 100644 index 0000000..67022a6 --- /dev/null +++ b/build-support/compile_flags.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import os +from os.path import join + +ROOT = join(os.path.dirname(__file__), "..") + +def get_flags(): + """ + Return the set of flags that are used during compilation. + + TODO(todd) it would be nicer to somehow grab these from CMake, but it's + not clear how to do so. + """ + return [ + '-x', + 'c++', + '-DKUDU_HEADERS_NO_STUBS=1', + '-DKUDU_HEADERS_USE_RICH_SLICE=1', + '-DKUDU_HEADERS_USE_SHORT_STATUS_MACROS=1', + '-DKUDU_STATIC_DEFINE', + '-D__STDC_FORMAT_MACROS', + '-fno-strict-aliasing', + '-msse4.2', + '-Wall', + '-Wno-sign-compare', + '-Wno-deprecated', + '-pthread', + '-ggdb', + '-Qunused-arguments', + '-Wno-ambiguous-member-template', + '-std=c++11', + '-g', + '-fPIC', + '-I', + join(ROOT, 'src'), + '-I', + join(ROOT, 'build/latest/src'), + '-isystem', + join(ROOT, 'thirdparty/installed/common/include'), + '-isystem', + join(ROOT, 'thirdparty/installed/uninstrumented/include'), + ] http://git-wip-us.apache.org/repos/asf/kudu/blob/68962df0/src/kudu/.clang-tidy ---------------------------------------------------------------------- diff --git a/src/kudu/.clang-tidy b/src/kudu/.clang-tidy index c00344a..d8a072d 100644 --- a/src/kudu/.clang-tidy +++ b/src/kudu/.clang-tidy @@ -14,6 +14,5 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. ---- -Checks: '-*,clang-diagnostic-*,-clang-diagnostic-unused-const-variable,readability-*,-readability-implicit-bool-cast,-readability-braces-around-statements,-readability-redundant-string-init,-readability-inconsistent-declaration-parameter-name,performance-*,google-*,-google-readability-todo,-google-readability-braces-around-statements,misc-*,-misc-unused-parameters' +Checks: 'clang-diagnostic-*,clang-analyzer-*,-clang-analyzer-alpha*,-*,readability-*,-readability-braces-around-statements,llvm-include-order,misc-*,-modernize-*,performance-*,-readability-implicit-bool-cast,google-*,-google-readability-braces-around-statements,-readability-redundant-string-init' HeaderFilterRegex: '.*,-*.pb.h'
