Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands")
load(
"@proxy_wasm_cpp_host//bazel:select.bzl",
"proxy_wasm_select_engine_null",
Expand All @@ -33,6 +34,21 @@ filegroup(
data = [".clang-tidy"],
)

# Convenience target to refresh `compile_commands.json` for the workspace.
# Adjust `targets` to what you work on frequently. Using `//:lib` here makes
# the extractor gather commands for the main library; change to `//...` or a
# dict of targets with flags if you prefer.
refresh_compile_commands(
name = "refresh_compile_commands",
# Align with DEVELOPMENT.md example: include tests and the main lib.
# Accepts a string, list, or dict (target->flags). A list will be converted
# to a dict by the macro.
targets = [
"//test/...",
"//:lib",
],
)

cc_library(
name = "wasm_vm_headers",
hdrs = [
Expand Down
38 changes: 34 additions & 4 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,41 @@
# Development guidelines

## Generate compilation database
## Generating compilation database

[JSON Compilation Database](https://clang.llvm.org/docs/JSONCompilationDatabase.html) files can be used by [clangd](https://clangd.llvm.org/) or similar tools to add source code cross-references and code completion functionality to editors.
Use the Python wrapper `tools/gen_compilation_database.py` to produce a
`compile_commands.json` file for `clangd` or similar tools. This is the
recommended and reproducible approach.

The following command can be used to generate the `compile_commands.json` file:
See also:

- JSON Compilation Database: https://clang.llvm.org/docs/JSONCompilationDatabase.html
- clangd: https://clangd.llvm.org/

Generate for the whole workspace (recommended):

```bash
BAZEL_BUILD_OPTION_LIST="--define=engine=multi" python3 tools/gen_compilation_database.py
```
BAZEL_BUILD_OPTION_LIST="--define=engine=multi" ./tools/gen_compilation_database.py --include_all //test/... //:lib

Generate for specific Bazel targets:

```bash
python3 tools/gen_compilation_database.py //test/... //:lib
```

Supported flags:

- `--include_external`
- `--include_genfiles`
- `--include_headers`
- `--include_all`
- `--system-clang`

Notes:

- The Python wrapper calls the external Hedron extractor (`@hedron_compile_commands//:refresh_all`) by
default.
- If you pass positional targets the script creates a temporary Bazel package that defines a `refresh_compile_commands` target with those targets and runs it. This avoids forwarding targets directly to the extractor at runtime (which can cause `bazel aquery` parsing errors).
- If you prefer a workspace-local target, create it in your root `BUILD` and run it directly with `bazel run :refresh_compile_commands`.
- The extractor writes `compile_commands.json` at the repository root.

12 changes: 7 additions & 5 deletions bazel/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,15 @@ def proxy_wasm_cpp_host_repositories():
urls = ["https://github.com/proxy-wasm/proxy-wasm-cpp-sdk/archive/7465dee8b2953beebff99f6dc3720ad0c79bab99.tar.gz"],
)

# Compile DB dependencies.
maybe(
http_archive,
name = "bazel_compdb",
sha256 = "acd2a9eaf49272bb1480c67d99b82662f005b596a8c11739046a4220ec73c4da",
strip_prefix = "bazel-compilation-database-40864791135333e1446a04553b63cbe744d358d0",
url = "https://github.com/grailbio/bazel-compilation-database/archive/40864791135333e1446a04553b63cbe744d358d0.tar.gz",
name = "hedron_compile_commands",
urls = [
"https://github.com/hedronvision/bazel-compile-commands-extractor/archive/0e990032f3c5a866e72615cf67e5ce22186dcb97.tar.gz",
],
strip_prefix = "bazel-compile-commands-extractor-0e990032f3c5a866e72615cf67e5ce22186dcb97",
# SHA256 of the tarball (computed from upstream tag/commit).
sha256 = "2b3ee8bba2df4542a508b0289727b031427162b4cd381850f89b406445c17578",
)

# Test dependencies.
Expand Down
48 changes: 39 additions & 9 deletions tools/gen_compilation_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,48 @@ def generate_compilation_database(args):

source_dir_targets = args.bazel_targets

subprocess.check_call(["bazel", *bazel_startup_options, "build"] + bazel_options + [
"--aspects=@bazel_compdb//:aspects.bzl%compilation_database_aspect",
"--output_groups=compdb_files,header_files"
] + source_dir_targets)
# Build the list of runtime flags (those accepted by the extractor script)
runtime_flags = []
if args.include_external:
runtime_flags.append("--include_external")
if args.include_genfiles:
runtime_flags.append("--include_genfiles")
if args.include_headers:
runtime_flags.append("--include_headers")
if args.include_all:
runtime_flags.append("--include_all")
if args.system_clang:
runtime_flags.append("--system-clang")

execroot = subprocess.check_output(
["bazel", *bazel_startup_options, "info", *bazel_options,
"execution_root"]).decode().strip()
# Invoke the external Hedron extractor directly and forward both runtime
# flags and any positional Bazel targets to it. The extractor is expected
# to accept these arguments; this keeps the wrapper simple and makes the
# user-visible behavior consistent: calling the Python script is equivalent
# to running the external extractor with the same arguments.
try:
subprocess.check_call([
"bazel", *bazel_startup_options, "run", *bazel_options,
"@hedron_compile_commands//:refresh_all", "--"
] + runtime_flags + source_dir_targets)
except subprocess.CalledProcessError as e:
print("ERROR: external Hedron extractor failed to run; please ensure @hedron_compile_commands is available and the provided arguments are valid for the extractor.")
raise e

# The extractor writes a single `compile_commands.json` into the workspace
# root. Prefer that file; fall back to searching the execroot for legacy
# `*.compile_commands.json` files if the extractor didn't produce one.
db_entries = []
for db in Path(execroot).glob('**/*.compile_commands.json'):
db_entries.extend(json.loads(db.read_text()))
# Ensure we always have execroot available for any `__EXEC_ROOT__` markers.
execroot = subprocess.check_output(
["bazel", *bazel_startup_options, "info", *bazel_options, "execution_root"]
).decode().strip()

cc_path = Path("compile_commands.json")
if cc_path.exists():
db_entries = json.loads(cc_path.read_text())
else:
for db in Path(execroot).glob('**/*.compile_commands.json'):
db_entries.extend(json.loads(db.read_text()))

def replace_execroot_marker(db_entry):
if 'directory' in db_entry and db_entry['directory'] == '__EXEC_ROOT__':
Expand Down