Skip to content

Instantly share code, notes, and snippets.

@ichizok
Last active April 12, 2026 10:20
Show Gist options
  • Select an option

  • Save ichizok/9e34f02780f9837e6f0b0166367985a5 to your computer and use it in GitHub Desktop.

Select an option

Save ichizok/9e34f02780f9837e6f0b0166367985a5 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import json
import subprocess
import sys
from argparse import ArgumentParser
from os import path
from tempfile import TemporaryDirectory
def json_dump(obj, filename):
with open(filename, "w") as f:
json.dump(obj, f, indent=2)
f.write("\n")
def compiledb(build_log, work_dir):
compile_commands_json = path.join(work_dir, "compile_commands.c.json")
with open(build_log) as f:
subprocess.run(["compiledb", "-o", compile_commands_json], stdin=f, check=True)
with open(compile_commands_json) as f:
return json.load(f)
def xccompiledb(build_log, work_dir):
compile_commands_json = path.join(work_dir, "compile_commands.m.json")
r = []
with open(build_log) as f:
compile_c = False
dirname = ""
for line in f:
if line.startswith("CompileC "):
compile_c = True
elif line == "\n":
compile_c = False
elif compile_c and not line.isspace():
items = line.split()
if items[0] == "cd":
dirname = items[1]
elif items[0].endswith("/bin/clang"):
filename = items[items.index("-c") + 1]
obj = {
"directory": dirname,
"arguments": items,
"file": path.relpath(filename, dirname),
}
r += [obj]
json_dump(r, compile_commands_json)
return r
def separate_build_log(build_log, build_c_log, build_m_log):
with (
open(build_log) as f,
open(build_c_log, "w") as fc,
open(build_m_log, "w") as fm,
):
xcodebuild = False
for line in f:
if xcodebuild or line.startswith("xcodebuild"):
xcodebuild = True
fm.write(line)
else:
fc.write(line)
fc.write("\n")
fm.write("\n")
def main():
parser = ArgumentParser()
parser.add_argument("build_log", nargs="?", type=str)
args = parser.parse_args()
with TemporaryDirectory() as work_dir:
if args.build_log is None or args.build_log == "-":
build_log = path.join(work_dir, "build.log")
with open(build_log, "w") as f:
for line in sys.stdin:
f.write(line)
sys.stdout.write(line)
else:
build_log = args.build_log
if not path.isfile(build_log):
raise FileNotFoundError(f"File not found: {build_log}")
build_c_log = path.join(work_dir, "build.c.log")
build_m_log = path.join(work_dir, "build.m.log")
separate_build_log(build_log, build_c_log, build_m_log)
r1 = compiledb(build_c_log, work_dir)
r2 = xccompiledb(build_m_log, work_dir)
json_dump(r1 + r2, "compile_commands.json")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment