|
24 | 24 | """
|
25 | 25 |
|
26 | 26 | import argparse
|
| 27 | +import json |
27 | 28 | import subprocess
|
28 | 29 | import sys
|
| 30 | +from typing import Any, Dict |
29 | 31 |
|
30 | 32 | import scripts_utils
|
31 | 33 |
|
@@ -72,6 +74,60 @@ def _build_generated_files(
|
72 | 74 | )
|
73 | 75 |
|
74 | 76 |
|
| 77 | +def _get_config_for_entry(entry: Dict[str, Any]) -> str: |
| 78 | + """Returns the configuration for a compile command entry.""" |
| 79 | + arguments = entry.get("arguments") |
| 80 | + |
| 81 | + # Only handle files where the object file argument is easily found as |
| 82 | + # the last argument, which matches the expected structure from Bazel. |
| 83 | + if not arguments or len(arguments) < 2 or arguments[-2] != "-o": |
| 84 | + return "unknown" |
| 85 | + obj_file = arguments[-1] |
| 86 | + |
| 87 | + # The configuration is the name of the subdirectory of `bazel-out`. |
| 88 | + if not obj_file.startswith("bazel-out/"): |
| 89 | + return "unknown" |
| 90 | + return str(obj_file.split("/")[1]) |
| 91 | + |
| 92 | + |
| 93 | +def _filter_compilation_database(file_path: str) -> None: |
| 94 | + """Filters out duplicate exec-config entries from the database.""" |
| 95 | + print("Filtering out duplicate exec-configuration entries...") |
| 96 | + try: |
| 97 | + with open(file_path, "r") as f: |
| 98 | + commands = json.load(f) |
| 99 | + except FileNotFoundError: |
| 100 | + print(f"Error: The file '{file_path}' was not found.") |
| 101 | + sys.exit(1) |
| 102 | + except json.JSONDecodeError: |
| 103 | + print(f"Error: The file '{file_path}' is not a valid JSON file.") |
| 104 | + sys.exit(1) |
| 105 | + |
| 106 | + # We want to skip compiles that were in the "exec" configuration for tools. |
| 107 | + # Because we generate compile commands for every bazel cc_* target in the |
| 108 | + # main configuration, even if only used by tools, their sources should be |
| 109 | + # covered and the exec configuration would simply be a duplicate. |
| 110 | + # |
| 111 | + # Detecting this based on the `-exec-` string in the configuration name of |
| 112 | + # the directory is a bit of a hack, but even using the `--notool_deps` |
| 113 | + # argument, Bazel seems to sometimes include this configuration in the query |
| 114 | + # that produces the compilation database. |
| 115 | + filtered_commands = [ |
| 116 | + entry |
| 117 | + for entry in commands |
| 118 | + if "-exec-" not in _get_config_for_entry(entry) |
| 119 | + ] |
| 120 | + |
| 121 | + with open(file_path, "w") as f: |
| 122 | + # Use indent=4 for a human-readable, pretty-printed output file |
| 123 | + json.dump(filtered_commands, f, indent=4) |
| 124 | + print( |
| 125 | + "Filtered out " |
| 126 | + f"{len(commands) - len(filtered_commands)} " |
| 127 | + "duplicate entries..." |
| 128 | + ) |
| 129 | + |
| 130 | + |
75 | 131 | def main() -> None:
|
76 | 132 | parser = argparse.ArgumentParser(
|
77 | 133 | description=__doc__,
|
@@ -108,6 +164,8 @@ def main() -> None:
|
108 | 164 | ]
|
109 | 165 | )
|
110 | 166 |
|
| 167 | + _filter_compilation_database("compile_commands.json") |
| 168 | + |
111 | 169 |
|
112 | 170 | if __name__ == "__main__":
|
113 | 171 | main()
|
0 commit comments