From 470a95bfb2d106ed821408641bf2b6fc983c1ba4 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 22 Apr 2020 16:23:37 -0700 Subject: [PATCH 1/2] implement `nim r main` to compile and run, saving binary to $nimcache --- changelog.md | 11 +++++++++++ compiler/main.nim | 25 +++++++++++++++++-------- compiler/nim.nim | 2 +- compiler/options.nim | 23 +++++++++++++---------- doc/basicopt.txt | 1 + 5 files changed, 43 insertions(+), 19 deletions(-) diff --git a/changelog.md b/changelog.md index 86d2e181c7481..35da0381d70f3 100644 --- a/changelog.md +++ b/changelog.md @@ -75,6 +75,17 @@ - Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`. - The `define` and `undef` pragmas have been de-deprecated. +- New command: `nim r main.nim [args...]` which compiles and runs main.nim, saving + the binary to $nimcache/main$exeExt, using the same logic as `nim c -r` to + avoid recompiling when sources don't change. This is now the preferred way to + run tests, avoiding the usual pain of clobbering your repo with binaries or + using tricky gitignore rules on posix. Example: + ```nim + nim r compiler/nim.nim --help # only compiled the first time + echo 'import os; echo getCurrentCompilerExe()' | nim r - # this works too + nim r compiler/nim.nim --fullhelp # no recompilation + nim r --nimcache:/tmp main # binary saved to /tmp/main + ``` ## Tool changes diff --git a/compiler/main.nim b/compiler/main.nim index 1a776d51606e6..66ae830d2f668 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -66,16 +66,22 @@ when not defined(leanCompiler): compileProject(graph) finishDoc2Pass(graph.config.projectName) +proc setOutDir(conf: ConfigRef) = + if conf.outDir.isEmpty: + if optUseNimcache in conf.globalOptions: + conf.outDir = getNimcacheDir(conf) + else: + conf.outDir = conf.projectPath + proc commandCompileToC(graph: ModuleGraph) = let conf = graph.config - - if conf.outDir.isEmpty: - conf.outDir = conf.projectPath + setOutDir(conf) if conf.outFile.isEmpty: + let base = conf.projectName let targetName = if optGenDynLib in conf.globalOptions: - platform.OS[conf.target.targetOS].dllFrmt % conf.projectName + platform.OS[conf.target.targetOS].dllFrmt % base else: - conf.projectName & platform.OS[conf.target.targetOS].exeExt + base & platform.OS[conf.target.targetOS].exeExt conf.outFile = RelativeFile targetName extccomp.initVars(conf) @@ -181,13 +187,13 @@ proc mainCommand*(graph: ModuleGraph) = conf.lastCmdTime = epochTime() conf.searchPaths.add(conf.libpath) setId(100) - case conf.command.normalize - of "c", "cc", "compile", "compiletoc": - # compile means compileToC currently + template handleC() = conf.cmd = cmdCompileToC if conf.exc == excNone: conf.exc = excSetjmp defineSymbol(graph.config.symbols, "c") commandCompileToC(graph) + case conf.command.normalize + of "c", "cc", "compile", "compiletoc": handleC() # compile means compileToC currently of "cpp", "compiletocpp": conf.cmd = cmdCompileToCpp if conf.exc == excNone: conf.exc = excCpp @@ -197,6 +203,9 @@ proc mainCommand*(graph: ModuleGraph) = conf.cmd = cmdCompileToOC defineSymbol(graph.config.symbols, "objc") commandCompileToC(graph) + of "r": # different from `"run"`! + conf.globalOptions.incl {optRun, optUseNimcache} + handleC() of "run": conf.cmd = cmdRun when hasTinyCBackend: diff --git a/compiler/nim.nim b/compiler/nim.nim index 997888cb884bc..7fe0db5cf154f 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -63,7 +63,7 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = if processArgument(pass, p, argsCount, config): break if pass == passCmd2: if {optRun, optWasNimscript} * config.globalOptions == {} and - config.arguments.len > 0 and config.command.normalize notin ["run", "e"]: + config.arguments.len > 0 and config.command.normalize notin ["run", "e", "r"]: rawMessage(config, errGenerated, errArgsNeedRunOption) proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = diff --git a/compiler/options.nim b/compiler/options.nim index 6105fcdb3d6d7..70ffb576e2bd5 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -57,6 +57,7 @@ type # please make sure we have under 32 options optGenScript, # generate a script file to compile the *.c files optGenMapping, # generate a mapping file optRun, # run the compiled project + optUseNimcache, # save artifacts (including binary) in $nimcache optStyleHint, # check that the names adhere to NEP-1 optStyleError, # enforce that the names adhere to NEP-1 optSkipSystemConfigFile, # skip the system's cfg/nims config file @@ -254,7 +255,8 @@ type nimblePaths*: seq[AbsoluteDir] searchPaths*: seq[AbsoluteDir] lazyPaths*: seq[AbsoluteDir] - outFile*: RelativeFile + outFileAbs*: AbsoluteFile + outFile*: RelativeFile # used if outFileAbs is empty outDir*: AbsoluteDir prefixDir*, libpath*, nimcacheDir*: AbsoluteDir dllOverrides, moduleOverrides*, cfileSpecificOptions*: StringTableRef @@ -524,18 +526,19 @@ proc getOutFile*(conf: ConfigRef; filename: RelativeFile, ext: string): Absolute conf.outDir / changeFileExt(filename, ext) proc absOutFile*(conf: ConfigRef): AbsoluteFile = - result = conf.outDir / conf.outFile - when defined(posix): - if dirExists(result.string): - result.string.add ".out" + result = conf.outFileAbs + if result.isEmpty: + doAssert not conf.outDir.isEmpty + doAssert not conf.outFile.isEmpty + result = conf.outDir / conf.outFile + when defined(posix): + if dirExists(result.string): result.string.add ".out" + conf.outFileAbs = result proc prepareToWriteOutput*(conf: ConfigRef): AbsoluteFile = ## Create the output directory and returns a full path to the output file - createDir conf.outDir - result = conf.outDir / conf.outFile - when defined(posix): - if dirExists(result.string): - result.string.add ".out" + result = conf.absOutFile + createDir result.string.parentDir proc getPrefixDir*(conf: ConfigRef): AbsoluteDir = ## Gets the prefix dir, usually the parent directory where the binary resides. diff --git a/doc/basicopt.txt b/doc/basicopt.txt index 1405e7b01b8dd..cc808a077aa1d 100644 --- a/doc/basicopt.txt +++ b/doc/basicopt.txt @@ -4,6 +4,7 @@ Command: //compile, c compile project with default code generator (C) + //r compile & run $nimcach/projname [arguments] //doc generate the documentation for inputfile Arguments: From 0e7eb8f06f3821803cc521a18507ec9dcd10eda8 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 22 Apr 2020 16:56:01 -0700 Subject: [PATCH 2/2] remove outFileAbs for now --- compiler/options.nim | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index 70ffb576e2bd5..5f4372177e5b6 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -255,8 +255,7 @@ type nimblePaths*: seq[AbsoluteDir] searchPaths*: seq[AbsoluteDir] lazyPaths*: seq[AbsoluteDir] - outFileAbs*: AbsoluteFile - outFile*: RelativeFile # used if outFileAbs is empty + outFile*: RelativeFile outDir*: AbsoluteDir prefixDir*, libpath*, nimcacheDir*: AbsoluteDir dllOverrides, moduleOverrides*, cfileSpecificOptions*: StringTableRef @@ -526,14 +525,14 @@ proc getOutFile*(conf: ConfigRef; filename: RelativeFile, ext: string): Absolute conf.outDir / changeFileExt(filename, ext) proc absOutFile*(conf: ConfigRef): AbsoluteFile = - result = conf.outFileAbs - if result.isEmpty: + if false: doAssert not conf.outDir.isEmpty doAssert not conf.outFile.isEmpty - result = conf.outDir / conf.outFile - when defined(posix): - if dirExists(result.string): result.string.add ".out" - conf.outFileAbs = result + # xxx: fix this pre-existing bug causing `SuccessX` error messages to lie + # for `jsonscript` + result = conf.outDir / conf.outFile + when defined(posix): + if dirExists(result.string): result.string.add ".out" proc prepareToWriteOutput*(conf: ConfigRef): AbsoluteFile = ## Create the output directory and returns a full path to the output file