├── third_party ├── BUILD.bazel ├── tools │ ├── clang_tidy.sh │ ├── generate_compilation_database.sh │ └── generate_compile_commands │ │ ├── BUILD.bazel │ │ └── extract_compile_command.cc ├── bazel │ ├── BUILD.bazel │ └── src │ │ └── main │ │ └── protobuf │ │ └── extra_actions_base.proto └── rapidjson.BUILD ├── README.md └── WORKSPACE /third_party/BUILD.bazel: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /third_party/tools/clang_tidy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ -z "${CLANG_TIDY:=$(which clang-tidy)}" ]; then 5 | echo "Unable to find clang-tidy" 1>&2 6 | exit 1 7 | fi 8 | 9 | $(dirname "${BASH_SOURCE[0]}")/generate_compilation_database.sh 10 | 11 | "$CLANG_TIDY" -p "$(bazel info execution_root)" "$@" 12 | -------------------------------------------------------------------------------- /third_party/bazel/BUILD.bazel: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | licenses(["notice"]) # Apache 2.0 4 | 5 | proto_library( 6 | name = "extra_actions_base_proto", 7 | srcs = ["src/main/protobuf/extra_actions_base.proto"], 8 | ) 9 | 10 | cc_proto_library( 11 | name = "extra_actions_base_cc_proto", 12 | deps = [":extra_actions_base_proto"], 13 | ) 14 | -------------------------------------------------------------------------------- /third_party/rapidjson.BUILD: -------------------------------------------------------------------------------- 1 | licenses(["notice"]) 2 | 3 | package(default_visibility = ["//visibility:public"]) 4 | 5 | filegroup( 6 | name = "license", 7 | srcs = ["license.txt"], 8 | ) 9 | 10 | cc_library( 11 | name = "rapidjson", 12 | hdrs = glob([ 13 | "include/rapidjson/*.h", 14 | "include/rapidjson/*/*.h", 15 | ]), 16 | copts = [ 17 | "-Wno-non-virtual-dtor", 18 | "-Wno-unused-variable", 19 | "-Wno-implicit-fallthrough", 20 | ], 21 | includes = ["include"], 22 | ) 23 | -------------------------------------------------------------------------------- /third_party/tools/generate_compilation_database.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Generates a compile_commands.json file at $(bazel info execution_root) for 4 | # your Clang tooling needs. 5 | 6 | set -e 7 | 8 | bazel build \ 9 | --experimental_action_listener=//third_party/tools/generate_compile_commands:extract_json \ 10 | --noshow_progress \ 11 | --noshow_loading_progress \ 12 | $(bazel query 'kind(cc_.*, //...)') > /dev/null 13 | 14 | pushd $(bazel info execution_root) > /dev/null 15 | echo "[" > compile_commands.json 16 | find . -name '*.compile_command.json' -exec bash -c 'cat {} && echo ,' \; >> compile_commands.json 17 | sed -i '$s/,$//' compile_commands.json 18 | echo "]" >> compile_commands.json 19 | popd > /dev/null 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bazel-compilation-db 2 | Generate `compile_commands.json` and run clang-tidy with [Bazel][bazel]. 3 | This is an adaptation of [Kythe][kythe]'s approach with a small patch to support the latest version of Bazel. 4 | 5 | 1. Append `WORKSPACE` to your repository's `WORKSPACE` file. 6 | 2. Copy `third_party` to your repository directory. 7 | 3. Optionally replace `'kind(cc_.*, //...)'` in `third_party/tools/generate_compilation_database.sh`. 8 | 9 | To generate a `compile_commands.json` file in `$(bazel info execution_root)`, run `third_party/tools/generate_compilation_database.sh`. 10 | To use `clang-tidy`, run `third_party/tools/clang_tidy.sh`. 11 | 12 | Tested with Bazel 0.27.0. 13 | 14 | [bazel]: https://bazel.build/ 15 | [kythe]: https://github.com/google/kythe 16 | -------------------------------------------------------------------------------- /third_party/tools/generate_compile_commands/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # Build file for tools integrating Bazel with clang tooling. 2 | 3 | licenses(["notice"]) # Apache 2.0 4 | 5 | # Extracts a single compile command from an extra action. 6 | cc_binary( 7 | name = "extract_compile_command", 8 | srcs = ["extract_compile_command.cc"], 9 | deps = [ 10 | "//third_party/bazel:extra_actions_base_cc_proto", 11 | "@com_github_tencent_rapidjson//:rapidjson", 12 | ], 13 | ) 14 | 15 | action_listener( 16 | name = "extract_json", 17 | extra_actions = [":extra_action"], 18 | mnemonics = ["CppCompile"], 19 | visibility = ["//visibility:public"], 20 | ) 21 | 22 | extra_action( 23 | name = "extra_action", 24 | cmd = "$(location :extract_compile_command) \ 25 | $(EXTRA_ACTION_FILE) \ 26 | $(output $(ACTION_ID).compile_command.json)", 27 | out_templates = ["$(ACTION_ID).compile_command.json"], 28 | tools = [":extract_compile_command"], 29 | ) 30 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 2 | 3 | http_archive( 4 | name = "bazel_skylib", 5 | sha256 = "2ef429f5d7ce7111263289644d233707dba35e39696377ebab8b0bc701f7818e", 6 | urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/0.8.0/bazel-skylib.0.8.0.tar.gz"], 7 | ) 8 | 9 | # proto_library, cc_proto_library, and java_proto_library rules implicitly 10 | # depend on @com_google_protobuf for protoc and proto runtimes. 11 | # This statement defines the @com_google_protobuf repo. 12 | http_archive( 13 | name = "com_google_protobuf", 14 | sha256 = "1e622ce4b84b88b6d2cdf1db38d1a634fe2392d74f0b7b74ff98f3a51838ee53", 15 | strip_prefix = "protobuf-3.8.0", 16 | urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.8.0.zip"], 17 | ) 18 | 19 | load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") 20 | protobuf_deps() 21 | 22 | http_archive( 23 | name = "com_github_tencent_rapidjson", 24 | sha256 = "8e00c38829d6785a2dfb951bb87c6974fa07dfe488aa5b25deec4b8bc0f6a3ab", 25 | strip_prefix = "rapidjson-1.1.0", 26 | url = "https://github.com/Tencent/rapidjson/archive/v1.1.0.zip", 27 | build_file = "@//third_party:rapidjson.BUILD", 28 | ) 29 | -------------------------------------------------------------------------------- /third_party/tools/generate_compile_commands/extract_compile_command.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "google/protobuf/io/coded_stream.h" 26 | #include "google/protobuf/io/zero_copy_stream.h" 27 | #include "google/protobuf/io/zero_copy_stream_impl.h" 28 | #include "google/protobuf/stubs/common.h" 29 | #include "rapidjson/stringbuffer.h" 30 | #include "rapidjson/writer.h" 31 | #include "third_party/bazel/src/main/protobuf/extra_actions_base.pb.h" 32 | 33 | namespace { 34 | using ::google::protobuf::io::FileInputStream; 35 | using ::google::protobuf::io::CodedInputStream; 36 | 37 | bool ReadExtraAction(const std::string& path, blaze::ExtraActionInfo* info, 38 | blaze::CppCompileInfo* cpp_info) { 39 | int fd = ::open(path.c_str(), O_RDONLY, S_IREAD | S_IWRITE); 40 | if (fd < 0) { 41 | perror("Failed to open input: "); 42 | return false; 43 | } 44 | FileInputStream file_input(fd); 45 | file_input.SetCloseOnDelete(true); 46 | 47 | CodedInputStream input(&file_input); 48 | if (!info->ParseFromCodedStream(&input)) return false; 49 | if (!info->HasExtension(blaze::CppCompileInfo::cpp_compile_info)) 50 | return false; 51 | *cpp_info = info->GetExtension(blaze::CppCompileInfo::cpp_compile_info); 52 | return true; 53 | } 54 | 55 | std::string JoinCommand(const std::vector& command) { 56 | std::string output; 57 | if (command.empty()) return output; 58 | 59 | // TODO(shahms): Deal with embedded spaces and quotes. 60 | auto iter = command.begin(); 61 | output = *iter++; 62 | for (; iter != command.end(); ++iter) { 63 | output += " " + *iter; 64 | } 65 | return output; 66 | } 67 | 68 | std::string GetBuildDirectory() { 69 | std::string build_dir; 70 | { 71 | char cwd[MAXPATHLEN]; 72 | build_dir = getcwd(cwd, MAXPATHLEN); 73 | } 74 | const char* sandbox_str = "/sandbox/"; 75 | auto sandbox_start = build_dir.find(sandbox_str); 76 | if (sandbox_start == std::string::npos) return build_dir; 77 | auto sandbox_end = 78 | build_dir.find("/execroot", sandbox_start + strlen(sandbox_str)); 79 | 80 | return build_dir.erase(sandbox_start, sandbox_end - sandbox_start); 81 | } 82 | 83 | std::string FormatCompilationCommand(const std::string& source_file, 84 | const std::vector& command) { 85 | rapidjson::StringBuffer buffer; 86 | rapidjson::Writer writer(buffer); 87 | writer.StartObject(); 88 | writer.Key("file"); 89 | writer.String(source_file.c_str()); 90 | writer.Key("directory"); 91 | writer.String(GetBuildDirectory().c_str()); 92 | writer.Key("command"); 93 | writer.String(JoinCommand(command).c_str()); 94 | writer.EndObject(); 95 | return buffer.GetString(); 96 | } 97 | } // namespace 98 | 99 | int main(int argc, char** argv) { 100 | GOOGLE_PROTOBUF_VERIFY_VERSION; 101 | if (argc != 3) { 102 | fprintf(stderr, "usage: %s extra-action-file output-file\n", argv[0]); 103 | return 1; 104 | } 105 | std::string extra_action_file = argv[1]; 106 | std::string output_file = argv[2]; 107 | blaze::ExtraActionInfo info; 108 | blaze::CppCompileInfo cpp_info; 109 | if (!ReadExtraAction(extra_action_file, &info, &cpp_info)) return 1; 110 | 111 | std::vector args; 112 | args.push_back(cpp_info.tool()); 113 | args.insert(args.end(), cpp_info.compiler_option().begin(), 114 | cpp_info.compiler_option().end()); 115 | if (std::find(args.begin(), args.end(), "-c") == args.end()) { 116 | args.push_back("-c"); 117 | args.push_back(cpp_info.source_file()); 118 | } 119 | if (std::find(args.begin(), args.end(), "-o") == args.end()) { 120 | args.push_back("-o"); 121 | args.push_back(cpp_info.output_file()); 122 | } 123 | 124 | FILE* output = ::fopen(output_file.c_str(), "w"); 125 | if (output == nullptr) { 126 | perror("Unable to open file for writing: "); 127 | return 1; 128 | } 129 | ::fputs(FormatCompilationCommand(cpp_info.source_file(), args).c_str(), 130 | output); 131 | ::fclose(output); 132 | 133 | google::protobuf::ShutdownProtobufLibrary(); 134 | return 0; 135 | } 136 | -------------------------------------------------------------------------------- /third_party/bazel/src/main/protobuf/extra_actions_base.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // proto definitions for the blaze extra_action feature. 16 | 17 | syntax = "proto2"; 18 | 19 | package blaze; 20 | 21 | option java_multiple_files = true; 22 | option java_package = "com.google.devtools.build.lib.actions.extra"; 23 | 24 | // A list of extra actions and metadata for the print_action command. 25 | message ExtraActionSummary { 26 | repeated DetailedExtraActionInfo action = 1; 27 | } 28 | 29 | // An individual action printed by the print_action command. 30 | message DetailedExtraActionInfo { 31 | // If the given action was included in the output due to a request for a 32 | // specific file, then this field contains the name of that file so that the 33 | // caller can correctly associate the extra action with that file. 34 | // 35 | // The data in this message is currently not sufficient to run the action on a 36 | // production machine, because not all necessary input files are identified, 37 | // especially for C++. 38 | // 39 | // There is no easy way to fix this; we could require that all header files 40 | // are declared and then add all of them here (which would be a huge superset 41 | // of the files that are actually required), or we could run the include 42 | // scanner and add those files here. 43 | optional string triggering_file = 1; 44 | // The actual action. 45 | required ExtraActionInfo action = 2; 46 | } 47 | 48 | // Provides information to an extra_action on the original action it is 49 | // shadowing. 50 | message ExtraActionInfo { 51 | extensions 1000 to max; 52 | 53 | // The label of the ActionOwner of the shadowed action. 54 | optional string owner = 1; 55 | 56 | // Only set if the owner is an Aspect. 57 | // Corresponds to AspectValue.AspectKey.getAspectClass.getName() 58 | // This field is deprecated as there might now be 59 | // multiple aspects applied to the same target. 60 | // This is the aspect name of the last aspect 61 | // in 'aspects' (8) field. 62 | optional string aspect_name = 6 [deprecated = true]; 63 | 64 | // Only set if the owner is an Aspect. 65 | // Corresponds to AspectValue.AspectKey.getParameters() 66 | // This field is deprecated as there might now be 67 | // multiple aspects applied to the same target. 68 | // These are the aspect parameters of the last aspect 69 | // in 'aspects' (8) field. 70 | map aspect_parameters = 7 [deprecated = true]; 71 | message StringList { 72 | option deprecated = true; 73 | repeated string value = 1; 74 | } 75 | 76 | message AspectDescriptor { 77 | // Corresponds to AspectDescriptor.getName() 78 | optional string aspect_name = 1; 79 | // Corresponds to AspectDescriptor.getParameters() 80 | map aspect_parameters = 2; 81 | message StringList { 82 | repeated string value = 1; 83 | } 84 | } 85 | 86 | // If the owner is an aspect, all aspects applied to the target 87 | repeated AspectDescriptor aspects = 8; 88 | 89 | // An id uniquely describing the shadowed action at the ActionOwner level. 90 | optional string id = 2; 91 | 92 | // The mnemonic of the shadowed action. Used to distinguish actions with the 93 | // same ActionType. 94 | optional string mnemonic = 5; 95 | } 96 | 97 | message EnvironmentVariable { 98 | // It is possible that this name is not a valid variable identifier. 99 | required string name = 1; 100 | // The value is unescaped and unquoted. 101 | required string value = 2; 102 | } 103 | 104 | // Provides access to data that is specific to spawn actions. 105 | // Usually provided by actions using the "Spawn" & "Genrule" Mnemonics. 106 | message SpawnInfo { 107 | extend ExtraActionInfo { 108 | optional SpawnInfo spawn_info = 1003; 109 | } 110 | 111 | repeated string argument = 1; 112 | // A list of environment variables and their values. No order is enforced. 113 | repeated EnvironmentVariable variable = 2; 114 | repeated string input_file = 4; 115 | repeated string output_file = 5; 116 | } 117 | 118 | // Provides access to data that is specific to C++ compile actions. 119 | // Usually provided by actions using the "CppCompile" Mnemonic. 120 | message CppCompileInfo { 121 | extend ExtraActionInfo { 122 | optional CppCompileInfo cpp_compile_info = 1001; 123 | } 124 | 125 | optional string tool = 1; 126 | repeated string compiler_option = 2; 127 | optional string source_file = 3; 128 | optional string output_file = 4; 129 | // Due to header discovery, this won't include headers unless the build is 130 | // actually performed. If set, this field will include the value of 131 | // "source_file" in addition to the headers. 132 | repeated string sources_and_headers = 5; 133 | // A list of environment variables and their values. No order is enforced. 134 | repeated EnvironmentVariable variable = 6; 135 | } 136 | 137 | // Provides access to data that is specific to C++ link actions. 138 | // Usually provided by actions using the "CppLink" Mnemonic. 139 | message CppLinkInfo { 140 | extend ExtraActionInfo { 141 | optional CppLinkInfo cpp_link_info = 1002; 142 | } 143 | 144 | repeated string input_file = 1; 145 | optional string output_file = 2; 146 | optional string interface_output_file = 3; 147 | optional string link_target_type = 4; 148 | optional string link_staticness = 5; 149 | repeated string link_stamp = 6; 150 | repeated string build_info_header_artifact = 7; 151 | // The list of command line options used for running the linking tool. 152 | repeated string link_opt = 8; 153 | } 154 | 155 | // Provides access to data that is specific to java compile actions. 156 | // Usually provided by actions using the "Javac" Mnemonic. 157 | message JavaCompileInfo { 158 | extend ExtraActionInfo { 159 | optional JavaCompileInfo java_compile_info = 1000; 160 | } 161 | 162 | optional string outputjar = 1; 163 | repeated string classpath = 2; 164 | repeated string sourcepath = 3; 165 | repeated string source_file = 4; 166 | repeated string javac_opt = 5; 167 | repeated string processor = 6; 168 | repeated string processorpath = 7; 169 | repeated string bootclasspath = 8; 170 | } 171 | 172 | // Provides access to data that is specific to python rules. 173 | // Usually provided by actions using the "Python" Mnemonic. 174 | message PythonInfo { 175 | extend ExtraActionInfo { 176 | optional PythonInfo python_info = 1005; 177 | } 178 | 179 | repeated string source_file = 1; 180 | repeated string dep_file = 2; 181 | } 182 | --------------------------------------------------------------------------------