Skip to content

Instantly share code, notes, and snippets.

@miguelmartin75
Created March 23, 2025 00:00
Show Gist options
  • Save miguelmartin75/298a8b39664a89a69abd8b23a5a805a8 to your computer and use it in GitHub Desktop.
Save miguelmartin75/298a8b39664a89a69abd8b23a5a805a8 to your computer and use it in GitHub Desktop.
compile commands json generation for nim in nimscript
import std/[
os,
json,
tables,
options,
strformat,
strutils,
sugar,
sequtils,
]
const compileCommandIndent = 2
proc nimCompileJsonToCompileCommands(compileJson: JsonNode, buildDir: string): JsonNode =
# see https://clang.llvm.org/docs/JSONCompilationDatabase.html
result = %*[]
# assert absDir.isAbsolute
for x in compileJson["compile"]:
assert x.len == 2
result.add %*{
"file": x[0].getStr,
"command": x[1].getStr,
"directory": buildDir,
}
proc srcAbsDir(path: string): string =
if path.startsWith("/"):
path
else:
&"{getCurrentDir()}/{path}"
proc mergeCompileCommands(ccs: seq[JsonNode]): JsonNode =
var cmdByFilename: Table[string, JsonNode]
for xs in ccs:
assert xs.kind == JArray
for x in xs:
let fn = x["file"].getStr.srcAbsDir
if fn notin cmdByFilename:
cmdByFilename[fn] = x
elif "IMPL" in x["command"].getStr:
cmdByFilename[fn] = x
result = %*[]
for x in cmdByFilename.values:
result.add x
proc createMergedCompileCommands*(
paths: seq[string],
outPath: string,
debugInfo: bool = false,
) =
let
ccs = collect:
for x in paths:
if x.fileExists:
x.readFile.parseJson
cc = mergeCompileCommands(ccs)
outputJson = cc.pretty(indent=compileCommandIndent)
writeFile(outPath, outputJson)
proc createCompileCommands(
inPath: string,
buildDir: string,
outPath: string,
existingCompileCommands: seq[string],
) =
let compileJson = inPath.readFile.parseJson
var
compileCommands = nimCompileJsonToCompileCommands(compileJson, buildDir)
existingCompileCommands = existingCompileCommands.map(x => x.readFile.parseJson)
if outPath.fileExists:
echo "combining outPath: ", outPath
let oldCmds = outPath.readFile.parseJson
existingCompileCommands.add(oldCmds)
existingCompileCommands.add(compileCommands)
compileCommands = mergeCompileCommands(existingCompileCommands)
writeFile(outPath, compileCommands.pretty(indent=compileCommandIndent))
proc createCompileCommands*(
projectName: string,
nimcachedir: string,
outPath: Option[string] = string.none,
existingCompileCommands: seq[string] = @[],
debugInfo: bool = true,
) =
let
compileJsonPath = &"{nimcachedir}/{projectName}.json"
outPath = if outPath.isSome:
outPath.get
else:
&"{nimcachedir}/compile_commands.json"
if debugInfo:
echo &"Converting {compileJsonPath} -> {outPath}"
createCompileCommands(
inPath = compileJsonPath,
buildDir = nimcachedir,
outPath = outPath,
existingCompileCommands = existingCompileCommands,
)
if debugInfo:
echo "Done"
const
pwd = getCurrentDir()
outdir = &"{pwd}/build"
nimcachedir = &"{pwd}/build/cache"
buildDir = outdir
import src/[buildsupport]
proc build*(
module: string,
srcDir: string = "src",
backend: string = "c",
run: bool = false,
compileOpts: string = "",
runOpts: string = "",
compileCommandPath: Option[string] = string.none,
withCompileCommands: bool = true,
debugInfo: bool = hasDebugInfo,
) =
let
runStr = if run:
"-r"
else:
""
allCmdParams: seq[string] = commandLineParams()[1..^1].join(" ").split(" -- ")
cmdParams = allCmdParams[0] & " " & compileOpts
runParams = if allCmdParams.len >= 2:
assert allCmdParams.len == 2, "-- cannot be supplied twice!"
allCmdParams[1] & runOpts
else:
runOpts
cmd = &"{backend} {runStr} {cmdParams} {srcDir}/{module}.nim {runParams}"
projectName = block:
var projectName = module
for cmd in cmdParams.split(" "):
let cmd = cmd.strip
if cmd.startsWith("--out:") or cmd.startsWith("-o:"):
projectName = cmd.split(":")[1].splitFile.name
# assert projectName.fileExists, &"{projectName} does not exist"
projectName
compileCommandJsonPath = &"{nimcachedir}/{projectName}_compile_commands.json"
if debugInfo:
echo &"executing: nim {cmd}"
echo "projectName= ", projectName
selfExec cmd
if withCompileCommands:
echo "-- createCompileCommands"
createCompileCommands(
projectName=projectName,
nimcachedir=nimcachedir,
outPath=compileCommandJsonPath.some,
debugInfo=debugInfo,
)
# create a merged compile_commands.json
createMergedCompileCommands(
@[
&"{buildDir}/compile_commands.json",
&"{buildDir}/combined_compile_commands.json",
compileCommandJsonPath,
],
&"{buildDir}/combined_compile_commands.json",
debugInfo=true,
)
task app, "Build app":
build(
"app",
srcDir="src",
backend="c",
withCompileCommands=true,
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment