From ff731e998a19c3384234fc7709b46ca372dacbfd Mon Sep 17 00:00:00 2001 From: Miguel Martin Date: Sat, 22 Mar 2025 16:33:18 -0700 Subject: [PATCH 1/2] compile_commands.json support --- compiler/commands.nim | 6 +++++ compiler/extccomp.nim | 33 ++++++++++++++++++++++++++++ compiler/options.nim | 4 ++++ tests/compile_commands_json/foo.cpp | 7 ++++++ tests/compile_commands_json/foo.nim | 6 +++++ tests/compile_commands_json/foo.nims | 1 + 6 files changed, 57 insertions(+) create mode 100644 tests/compile_commands_json/foo.cpp create mode 100644 tests/compile_commands_json/foo.nim create mode 100644 tests/compile_commands_json/foo.nims diff --git a/compiler/commands.nim b/compiler/commands.nim index 879a995882b6e..2f0993b8810c0 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -1154,6 +1154,12 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; handleStdinInput(conf) of "nilseqs", "nilchecks", "symbol", "taintmode", "cs", "deadcodeelim": warningOptionNoop(switch) of "nimmainprefix": conf.nimMainPrefix = arg + of "compilecmdjson": + conf.compileCommandsJson = true + of "projectcompilecmdjson": + conf.projectCompileCommandJsons = true + of "copycommandjson": + conf.copyCompileCommandsJsonToCurDir = true else: if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg) else: invalidCmdLineOption(conf, pass, switch, info) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 4bae400dc013f..c98a136fa944c 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -1003,6 +1003,22 @@ proc callCCompiler*(conf: ConfigRef) = template hashNimExe(): string = $secureHashFile(os.getAppFilename()) +iterator compileCommandsJsonFiles*(conf: ConfigRef): AbsoluteFile = + # `outFile` is better than `projectName`, as it allows having different json + # files for a given source file compiled with different options; it also + # works out of the box with `hashMainCompilationParams`. + var rf = if conf.projectCompileCommandJsons: + ("compile_commands_" & conf.outFile.changeFileExt("json").string).RelativeFile + else: + "compile_commands".RelativeFile + + yield getNimcacheDir(conf) / rf + + if conf.copyCompileCommandsJsonToCurDir: + # TODO + yield getNimcacheDir(conf) / rf + + proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile = # `outFile` is better than `projectName`, as it allows having different json # files for a given source file compiled with different options; it also @@ -1027,6 +1043,16 @@ type BuildCache = object depfiles: seq[(string, string)] nimexe: string +proc jsonCompileCommands*(conf: ConfigRef): JsonNode = + # ref: https://clang.llvm.org/docs/JSONCompilationDatabase.html + result = %*[] + for it in conf.toCompile: + result.add %*{ + "file": it.cname.string, + "command": getCompileCFileCmd(conf, it), + "directory": conf.outDir.string, + } + proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) = var linkFiles = collect(for it in conf.externalToLink: var it = it @@ -1060,8 +1086,15 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) = if fileExists(bcache.outputFile): bcache.outputLastModificationTime = $getLastModificationTime(bcache.outputFile) conf.jsonBuildFile = conf.jsonBuildInstructionsFile + conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty) + # NOTE: compile_commands.json uses indent of 2 by convention + if conf.compileCommandsJson: + let compileCmds = conf.jsonCompileCommands.pretty(2) + for p in conf.compileCommandsJsonFiles: + p.writeFile(compileCmds) + proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile): bool = result = false if not fileExists(jsonFile) or not fileExists(conf.absOutFile): return true diff --git a/compiler/options.nim b/compiler/options.nim index ea75a68487568..4abd5aa100845 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -448,6 +448,10 @@ type currentConfigDir*: string # used for passPP only; absolute dir clientProcessId*: int + compileCommandsJson*: bool + projectCompileCommandJsons*: bool + copyCompileCommandsJsonToCurDir*: bool + proc assignIfDefault*[T](result: var T, val: T, def = default(T)) = diff --git a/tests/compile_commands_json/foo.cpp b/tests/compile_commands_json/foo.cpp new file mode 100644 index 0000000000000..a65c8a70ea58a --- /dev/null +++ b/tests/compile_commands_json/foo.cpp @@ -0,0 +1,7 @@ +#include + +#ifdef ENABLE_FOO +extern "C" void foo() { + std::cout << "hello world\n"; +} +#endif diff --git a/tests/compile_commands_json/foo.nim b/tests/compile_commands_json/foo.nim new file mode 100644 index 0000000000000..fd71a8b57db75 --- /dev/null +++ b/tests/compile_commands_json/foo.nim @@ -0,0 +1,6 @@ +{.compile("foo.cpp", "-DENABLE_FOO").} + +proc foo() {.importc.} + +when isMainModule: + foo() diff --git a/tests/compile_commands_json/foo.nims b/tests/compile_commands_json/foo.nims new file mode 100644 index 0000000000000..dedb7cc4b84b3 --- /dev/null +++ b/tests/compile_commands_json/foo.nims @@ -0,0 +1 @@ +--nimcache:"./nimcache" From 818d016b37393ead35b7ca42be84bbeb7e1a3aa6 Mon Sep 17 00:00:00 2001 From: Miguel Martin Date: Sat, 22 Mar 2025 17:19:27 -0700 Subject: [PATCH 2/2] add TODOs --- compiler/commands.nim | 10 ++++++++-- compiler/extccomp.nim | 8 ++------ tests/compile_commands_json/foo.nims | 1 + 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 2f0993b8810c0..bdac041e2fce1 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -1154,11 +1154,17 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; handleStdinInput(conf) of "nilseqs", "nilchecks", "symbol", "taintmode", "cs", "deadcodeelim": warningOptionNoop(switch) of "nimmainprefix": conf.nimMainPrefix = arg - of "compilecmdjson": + of "compilecmdsjson": + # TODO + # processOnOffSwitch(conf, {optCursorInference}, arg, pass, info) conf.compileCommandsJson = true of "projectcompilecmdjson": + # TODO + # processOnOffSwitch(conf, {optCursorInference}, arg, pass, info) conf.projectCompileCommandJsons = true - of "copycommandjson": + of "rootcompilecmdsjson": + # TODO + # processOnOffSwitch(conf, {optCursorInference}, arg, pass, info) conf.copyCompileCommandsJsonToCurDir = true else: if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index c98a136fa944c..78ad245857ef3 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -1004,9 +1004,6 @@ proc callCCompiler*(conf: ConfigRef) = template hashNimExe(): string = $secureHashFile(os.getAppFilename()) iterator compileCommandsJsonFiles*(conf: ConfigRef): AbsoluteFile = - # `outFile` is better than `projectName`, as it allows having different json - # files for a given source file compiled with different options; it also - # works out of the box with `hashMainCompilationParams`. var rf = if conf.projectCompileCommandJsons: ("compile_commands_" & conf.outFile.changeFileExt("json").string).RelativeFile else: @@ -1015,8 +1012,7 @@ iterator compileCommandsJsonFiles*(conf: ConfigRef): AbsoluteFile = yield getNimcacheDir(conf) / rf if conf.copyCompileCommandsJsonToCurDir: - # TODO - yield getNimcacheDir(conf) / rf + yield conf.projectPath / rf proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile = @@ -1089,8 +1085,8 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) = conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty) - # NOTE: compile_commands.json uses indent of 2 by convention if conf.compileCommandsJson: + # NOTE: compile_commands.json uses indent of 2 by convention let compileCmds = conf.jsonCompileCommands.pretty(2) for p in conf.compileCommandsJsonFiles: p.writeFile(compileCmds) diff --git a/tests/compile_commands_json/foo.nims b/tests/compile_commands_json/foo.nims index dedb7cc4b84b3..550313a81f172 100644 --- a/tests/compile_commands_json/foo.nims +++ b/tests/compile_commands_json/foo.nims @@ -1 +1,2 @@ --nimcache:"./nimcache" +--compileCmdsJson:on