|  | @@ -0,0 +1,135 @@
 | 
	
		
			
				|  |  | +#!/usr/bin/env python3
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +# Copyright 2020 gRPC authors.
 | 
	
		
			
				|  |  | +#
 | 
	
		
			
				|  |  | +# Licensed 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.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +# This is based on the script on the Envoy project
 | 
	
		
			
				|  |  | +# https://github.com/envoyproxy/envoy/blob/master/tools/gen_compilation_database.py
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import argparse
 | 
	
		
			
				|  |  | +import glob
 | 
	
		
			
				|  |  | +import json
 | 
	
		
			
				|  |  | +import logging
 | 
	
		
			
				|  |  | +import os
 | 
	
		
			
				|  |  | +import re
 | 
	
		
			
				|  |  | +import shlex
 | 
	
		
			
				|  |  | +import subprocess
 | 
	
		
			
				|  |  | +from pathlib import Path
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +RE_INCLUDE_SYSTEM = re.compile("\s*-I\s+/usr/[^ ]+")
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +# This method is equivalent to https://github.com/grailbio/bazel-compilation-database/blob/master/generate.sh
 | 
	
		
			
				|  |  | +def generateCompilationDatabase(args):
 | 
	
		
			
				|  |  | +    # We need to download all remote outputs for generated source code.
 | 
	
		
			
				|  |  | +    # This option lives here to override those specified in bazelrc.
 | 
	
		
			
				|  |  | +    bazel_options = shlex.split(os.environ.get("BAZEL_BUILD_OPTIONS", "")) + [
 | 
	
		
			
				|  |  | +        "--config=compdb",
 | 
	
		
			
				|  |  | +        "--remote_download_outputs=all",
 | 
	
		
			
				|  |  | +    ]
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    subprocess.check_call(["bazel", "build"] + bazel_options + [
 | 
	
		
			
				|  |  | +        "--aspects=@bazel_compdb//:aspects.bzl%compilation_database_aspect",
 | 
	
		
			
				|  |  | +        "--output_groups=compdb_files,header_files"
 | 
	
		
			
				|  |  | +    ] + args.bazel_targets)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    execroot = subprocess.check_output(["bazel", "info", "execution_root"] +
 | 
	
		
			
				|  |  | +                                       bazel_options).decode().strip()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    compdb = []
 | 
	
		
			
				|  |  | +    for compdb_file in Path(execroot).glob("**/*.compile_commands.json"):
 | 
	
		
			
				|  |  | +        compdb.extend(
 | 
	
		
			
				|  |  | +            json.loads(
 | 
	
		
			
				|  |  | +                "[" +
 | 
	
		
			
				|  |  | +                compdb_file.read_text().replace("__EXEC_ROOT__", execroot) +
 | 
	
		
			
				|  |  | +                "]"))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if args.dedup_targets:
 | 
	
		
			
				|  |  | +        compdb_map = {target["file"]: target for target in compdb}
 | 
	
		
			
				|  |  | +        compdb = list(compdb_map.values())
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return compdb
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def isHeader(filename):
 | 
	
		
			
				|  |  | +    for ext in (".h", ".hh", ".hpp", ".hxx"):
 | 
	
		
			
				|  |  | +        if filename.endswith(ext):
 | 
	
		
			
				|  |  | +            return True
 | 
	
		
			
				|  |  | +    return False
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def isCompileTarget(target, args):
 | 
	
		
			
				|  |  | +    filename = target["file"]
 | 
	
		
			
				|  |  | +    if not args.include_headers and isHeader(filename):
 | 
	
		
			
				|  |  | +        return False
 | 
	
		
			
				|  |  | +    if not args.include_genfiles:
 | 
	
		
			
				|  |  | +        if filename.startswith("bazel-out/"):
 | 
	
		
			
				|  |  | +            return False
 | 
	
		
			
				|  |  | +    if not args.include_external:
 | 
	
		
			
				|  |  | +        if filename.startswith("external/"):
 | 
	
		
			
				|  |  | +            return False
 | 
	
		
			
				|  |  | +    return True
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def modifyCompileCommand(target, args):
 | 
	
		
			
				|  |  | +    cc, options = target["command"].split(" ", 1)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    # Workaround for bazel added C++11 options, those doesn't affect build itself but
 | 
	
		
			
				|  |  | +    # clang-tidy will misinterpret them.
 | 
	
		
			
				|  |  | +    options = options.replace("-std=c++0x ", "")
 | 
	
		
			
				|  |  | +    options = options.replace("-std=c++11 ", "")
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if args.vscode:
 | 
	
		
			
				|  |  | +        # Visual Studio Code doesn't seem to like "-iquote". Replace it with
 | 
	
		
			
				|  |  | +        # old-style "-I".
 | 
	
		
			
				|  |  | +        options = options.replace("-iquote ", "-I ")
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if args.ignore_system_headers:
 | 
	
		
			
				|  |  | +        # Remove all include options for /usr/* directories
 | 
	
		
			
				|  |  | +        options = RE_INCLUDE_SYSTEM.sub("", options)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if isHeader(target["file"]):
 | 
	
		
			
				|  |  | +        options += " -Wno-pragma-once-outside-header -Wno-unused-const-variable"
 | 
	
		
			
				|  |  | +        options += " -Wno-unused-function"
 | 
	
		
			
				|  |  | +        if not target["file"].startswith("external/"):
 | 
	
		
			
				|  |  | +            # *.h file is treated as C header by default while our headers files are all C++11.
 | 
	
		
			
				|  |  | +            options = "-x c++ -std=c++11 -fexceptions " + options
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    target["command"] = " ".join([cc, options])
 | 
	
		
			
				|  |  | +    return target
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def fixCompilationDatabase(args, db):
 | 
	
		
			
				|  |  | +    db = [
 | 
	
		
			
				|  |  | +        modifyCompileCommand(target, args)
 | 
	
		
			
				|  |  | +        for target in db
 | 
	
		
			
				|  |  | +        if isCompileTarget(target, args)
 | 
	
		
			
				|  |  | +    ]
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    with open("compile_commands.json", "w") as db_file:
 | 
	
		
			
				|  |  | +        json.dump(db, db_file, indent=2)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +if __name__ == "__main__":
 | 
	
		
			
				|  |  | +    parser = argparse.ArgumentParser(
 | 
	
		
			
				|  |  | +        description='Generate JSON compilation database')
 | 
	
		
			
				|  |  | +    parser.add_argument('--include_external', action='store_true')
 | 
	
		
			
				|  |  | +    parser.add_argument('--include_genfiles', action='store_true')
 | 
	
		
			
				|  |  | +    parser.add_argument('--include_headers', action='store_true')
 | 
	
		
			
				|  |  | +    parser.add_argument('--vscode', action='store_true')
 | 
	
		
			
				|  |  | +    parser.add_argument('--ignore_system_headers', action='store_true')
 | 
	
		
			
				|  |  | +    parser.add_argument('--dedup_targets', action='store_true')
 | 
	
		
			
				|  |  | +    parser.add_argument('bazel_targets', nargs='*', default=["//..."])
 | 
	
		
			
				|  |  | +    args = parser.parse_args()
 | 
	
		
			
				|  |  | +    fixCompilationDatabase(args, generateCompilationDatabase(args))
 |