summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md10
-rw-r--r--compiler/cmdlinehelper.nim8
-rw-r--r--compiler/commands.nim4
-rw-r--r--compiler/docgen.nim21
-rw-r--r--compiler/extccomp.nim15
-rw-r--r--compiler/main.nim77
-rw-r--r--compiler/options.nim4
-rw-r--r--lib/packages/docutils/rstgen.nim2
-rw-r--r--tests/misc/mbackend.nim31
-rw-r--r--tests/nimdoc/m13129.nim2
-rw-r--r--tests/trunner.nim10
11 files changed, 125 insertions, 59 deletions
diff --git a/changelog.md b/changelog.md
index a93a7e651..986186356 100644
--- a/changelog.md
+++ b/changelog.md
@@ -160,8 +160,8 @@ proc mydiv(a, b): int {.raises: [].} =
 
 - 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
+- New command: `nim r main.nim [args...]` which compiles and runs main.nim, and implies `--usenimcache`
+  so that output is saved 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:
@@ -178,6 +178,12 @@ proc mydiv(a, b): int {.raises: [].} =
 - new hint: `--hint:msgOrigin` will show where a compiler msg (hint|warning|error) was generated; this
   helps in particular when it's non obvious where it came from either because multiple locations generate
   the same message, or because the message involves runtime formatting.
+- new flag `--backend:js|c|cpp|objc (or -b:js etc), to change backend; can be used with any command
+  (eg nim r, doc, check etc); safe to re-assign.
+- new flag `--doccmd:cmd` to pass additional flags for runnableExamples, eg: `--doccmd:-d:foo --threads`
+  use `--doccmd:skip` to skip runnableExamples and rst test snippets.
+- new flag `--usenimcache` to output to nimcache (whatever it resolves to after all commands are processed)
+  and avoids polluting both $pwd and $projectdir. It can be used with any command.
 
 ## Tool changes
 
diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim
index dd74f54dc..4777af8df 100644
--- a/compiler/cmdlinehelper.nim
+++ b/compiler/cmdlinehelper.nim
@@ -75,14 +75,6 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
   # now process command line arguments again, because some options in the
   # command line can overwrite the config file's settings
   extccomp.initVars(conf)
-  # XXX This is hacky. We need to find a better way.
-  case conf.command
-  of "cpp", "compiletocpp":
-    conf.backend = backendCpp
-    conf.cmd = cmdCompileToBackend
-  else:
-    discard
-
   self.processCmdLine(passCmd2, "", conf)
   if conf.command == "":
     rawMessage(conf, errGenerated, "command missing")
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 2ab79f14a..340180378 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -502,8 +502,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
         defineSymbol(conf.symbols, "gcmarkandsweep")
       of "destructors", "arc":
         conf.selectedGC = gcArc
-        if conf.backend != backendCpp:
-          conf.exc = excGoto
         defineSymbol(conf.symbols, "gcdestructors")
         defineSymbol(conf.symbols, "gcarc")
         incl conf.globalOptions, optSeqDestructors
@@ -513,8 +511,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
           defineSymbol(conf.symbols, "nimV2")
       of "orc":
         conf.selectedGC = gcOrc
-        if conf.backend != backendCpp:
-          conf.exc = excGoto
         defineSymbol(conf.symbols, "gcdestructors")
         defineSymbol(conf.symbols, "gcorc")
         incl conf.globalOptions, optSeqDestructors
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index d774fd508..b594921ae 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -216,13 +216,20 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
       # Include the current file if we're parsing a nim file
       let importStmt = if d.isPureRst: "" else: "import \"$1\"\n" % [d.filename.replace("\\", "/")]
       writeFile(outp, importStmt & content)
-      let c = if cmd.startsWith("nim <backend> "): os.getAppFilename() & " " & $conf.backend & cmd.substr("nim <backend>".len)
-              elif cmd.startsWith("nim "): os.getAppFilename() & cmd.substr("nim".len)
-              else: cmd
-      let c2 = c % quoteShell(outp)
-      rawMessage(conf, hintExecuting, c2)
-      if execShellCmd(c2) != status:
-        rawMessage(conf, errGenerated, "executing of external program failed: " & c2)
+
+      proc interpSnippetCmd(cmd: string): string =
+        # backward compatibility hacks; interpolation commands should explicitly use `$`
+        if cmd.startsWith "nim ": result = "$nim " & cmd[4..^1]
+        else: result = cmd
+        result = result.replace("$1", "$options") % [
+          "nim", os.getAppFilename().quoteShell,
+          "backend", $d.conf.backend,
+          "options", outp.quoteShell,
+        ]
+      let cmd = cmd.interpSnippetCmd
+      rawMessage(conf, hintExecuting, cmd)
+      if execShellCmd(cmd) != status:
+        rawMessage(conf, errGenerated, "executing of external program failed: " & cmd)
   result.emitted = initIntSet()
   result.destFile = getOutFile2(conf, presentationPath(conf, filename),
                                 outExt, htmldocsDir, false)
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 3c2435bec..4c5b195ab 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -306,13 +306,14 @@ proc isVSCompatible*(conf: ConfigRef): bool =
 proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
   # use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given
   # for niminst support
-  let fullSuffix =
-    case conf.backend
-    of backendCpp, backendJs, backendObjc: "." & $conf.backend & suffix
-    of backendC: suffix
-    else:
-      doAssert false
-      ""
+  var fullSuffix = suffix
+  case conf.backend
+  of backendCpp, backendJs, backendObjc: fullSuffix = "." & $conf.backend & suffix
+  of backendC: discard
+  of backendInvalid:
+    # during parsing of cfg files; we don't know the backend yet, no point in
+    # guessing wrong thing
+    return ""
 
   if (conf.target.hostOS != conf.target.targetOS or conf.target.hostCPU != conf.target.targetCPU) and
       optCompileOnly notin conf.globalOptions:
diff --git a/compiler/main.nim b/compiler/main.nim
index 00bc579df..21c0695f6 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -114,8 +114,10 @@ proc commandJsonScript(graph: ModuleGraph) =
   let proj = changeFileExt(graph.config.projectFull, "")
   extccomp.runJsonBuildInstructions(graph.config, proj)
 
-when not defined(leanCompiler):
-  proc commandCompileToJS(graph: ModuleGraph) =
+proc commandCompileToJS(graph: ModuleGraph) =
+  when defined(leanCompiler):
+    globalError(graph.config, unknownLineInfo, "compiler wasn't built with JS code generator")
+  else:
     let conf = graph.config
     conf.exc = excCpp
 
@@ -194,47 +196,65 @@ proc mainCommand*(graph: ModuleGraph) =
   when false: setOutDir(conf)
   if optUseNimcache in conf.globalOptions: setOutDir(conf)
 
-  template handleBackend(backend2: TBackend) =
-    conf.backend = backend2
-    conf.cmd = cmdCompileToBackend
-    defineSymbol(graph.config.symbols, $backend2)
-    case backend2
+  proc customizeForBackend(backend: TBackend) =
+    ## Sets backend specific options but don't compile to backend yet in
+    ## case command doesn't require it. This must be called by all commands.
+    if conf.backend == backendInvalid:
+      # only set if wasn't already set, to allow override via `nim c -b:cpp`
+      conf.backend = backend
+
+    defineSymbol(graph.config.symbols, $conf.backend)
+    case conf.backend
     of backendC:
       if conf.exc == excNone: conf.exc = excSetjmp
-      commandCompileToC(graph)
     of backendCpp:
       if conf.exc == excNone: conf.exc = excCpp
-      commandCompileToC(graph)
-    of backendObjc:
-      commandCompileToC(graph)
+    of backendObjc: discard
     of backendJs:
-      when defined(leanCompiler):
-        globalError(conf, unknownLineInfo, "compiler wasn't built with JS code generator")
-      else:
-        if conf.hcrOn:
-          # XXX: At the moment, system.nim cannot be compiled in JS mode
-          # with "-d:useNimRtl". The HCR option has been processed earlier
-          # and it has added this define implictly, so we must undo that here.
-          # A better solution might be to fix system.nim
-          undefSymbol(conf.symbols, "useNimRtl")
-        commandCompileToJS(graph)
+      if conf.hcrOn:
+        # XXX: At the moment, system.nim cannot be compiled in JS mode
+        # with "-d:useNimRtl". The HCR option has been processed earlier
+        # and it has added this define implictly, so we must undo that here.
+        # A better solution might be to fix system.nim
+        undefSymbol(conf.symbols, "useNimRtl")
+    of backendInvalid: doAssert false
+    if conf.selectedGC in {gcArc, gcOrc} and conf.backend != backendCpp:
+      conf.exc = excGoto
+
+  var commandAlreadyProcessed = false
+
+  proc compileToBackend(backend: TBackend, cmd = cmdCompileToBackend) =
+    commandAlreadyProcessed = true
+    conf.cmd = cmd
+    customizeForBackend(backend)
+    case conf.backend
+    of backendC: commandCompileToC(graph)
+    of backendCpp: commandCompileToC(graph)
+    of backendObjc: commandCompileToC(graph)
+    of backendJs: commandCompileToJS(graph)
     of backendInvalid: doAssert false
 
+  ## process all backend commands
   case conf.command.normalize
-  of "c", "cc", "compile", "compiletoc": handleBackend(backendC) # compile means compileToC currently
-  of "cpp", "compiletocpp": handleBackend(backendCpp)
-  of "objc", "compiletooc": handleBackend(backendObjc)
-  of "js", "compiletojs": handleBackend(backendJs)
+  of "c", "cc", "compile", "compiletoc": compileToBackend(backendC) # compile means compileToC currently
+  of "cpp", "compiletocpp": compileToBackend(backendCpp)
+  of "objc", "compiletooc": compileToBackend(backendObjc)
+  of "js", "compiletojs": compileToBackend(backendJs)
   of "r": # different from `"run"`!
     conf.globalOptions.incl {optRun, optUseNimcache}
-    handleBackend(conf.backend)
+    compileToBackend(backendC)
   of "run":
-    conf.cmd = cmdRun
     when hasTinyCBackend:
       extccomp.setCC(conf, "tcc", unknownLineInfo)
-      commandCompileToC(graph)
+      if conf.backend notin {backendC, backendInvalid}:
+        rawMessage(conf, errGenerated, "'run' requires c backend, got: '$1'" % $conf.backend)
+      compileToBackend(backendC, cmd = cmdRun)
     else:
       rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc")
+  else: customizeForBackend(backendC) # fallback for other commands
+
+  ## process all other commands
+  case conf.command.normalize
   of "doc0":
     when defined(leanCompiler):
       quit "compiler wasn't built with documentation generator"
@@ -383,6 +403,7 @@ proc mainCommand*(graph: ModuleGraph) =
   of "jsonscript":
     conf.cmd = cmdJsonScript
     commandJsonScript(graph)
+  elif commandAlreadyProcessed: discard # already handled
   else:
     rawMessage(conf, errGenerated, "invalid command: " & conf.command)
 
diff --git a/compiler/options.nim b/compiler/options.nim
index 3e6c7989f..194d635b1 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -223,7 +223,7 @@ type
                           ## fields marked with '*' are subject to
                           ## the incremental compilation mechanisms
                           ## (+) means "part of the dependency"
-    backend*: TBackend
+    backend*: TBackend # set via `nim x` or `nim --backend:x`
     target*: Target       # (+)
     linesCompiled*: int   # all lines that have been compiled
     options*: TOptions    # (+)
@@ -419,7 +419,7 @@ proc newConfigRef*(): ConfigRef =
     cIncludes: @[],   # directories to search for included files
     cLibs: @[],       # directories to search for lib files
     cLinkedLibs: @[],  # libraries to link
-    backend: backendC,
+    backend: backendInvalid,
     externalToLink: @[],
     linkOptionsCmd: "",
     compileOptionsCmd: @[],
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index edd987a76..816d724ef 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -865,7 +865,7 @@ proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) =
   of "test":
     params.testCmd = n.getFieldValue.strip
     if params.testCmd.len == 0:
-      params.testCmd = "nim <backend> -r $1" # The nim backend is auto-set in docgen.nim
+      params.testCmd = "$nim r --backend:$backend $options" # see `interpSnippetCmd`
     else:
       params.testCmd = unescape(params.testCmd)
   of "status", "exitcode":
diff --git a/tests/misc/mbackend.nim b/tests/misc/mbackend.nim
new file mode 100644
index 000000000..e38eb2d3e
--- /dev/null
+++ b/tests/misc/mbackend.nim
@@ -0,0 +1,31 @@
+#[
+We can't merge this test inside a `when defined(cpp)` because some bug that was
+fixed would not trigger in that case.
+]#
+
+import std/compilesettings
+import std/unittest
+
+static:
+  ## bugfix 1: this used to CT error with: Error: unhandled exception: mimportcpp.nim(6, 18) `defined(cpp)`
+  doAssert defined(cpp)
+  doAssert querySetting(backend) == "cpp"
+
+  ## checks that `--backend:c` has no side effect (ie, can be overridden by subsequent commands)
+  doAssert not defined(c)
+  doAssert not defined(js)
+  doAssert not defined(js)
+
+type
+  std_exception {.importcpp: "std::exception", header: "<exception>".} = object
+proc what(s: std_exception): cstring {.importcpp: "((char *)#.what())".}
+
+var isThrown = false
+try:
+  ## bugfix 2: this used to CT error with: Error: only a 'ref object' can be raised
+  raise std_exception()
+except std_exception as ex:
+  doAssert ex.what().len > 0
+  isThrown = true
+
+doAssert isThrown
diff --git a/tests/nimdoc/m13129.nim b/tests/nimdoc/m13129.nim
index df4b5a3f5..95d072d52 100644
--- a/tests/nimdoc/m13129.nim
+++ b/tests/nimdoc/m13129.nim
@@ -1,3 +1,5 @@
+# issue #13129
+
 when defined(cpp):
   {.push header: "<vector>".}
   type
diff --git a/tests/trunner.nim b/tests/trunner.nim
index bd74a8405..e89606bf7 100644
--- a/tests/trunner.nim
+++ b/tests/trunner.nim
@@ -123,6 +123,16 @@ else: # don't run twice the same test
       let cmd = fmt"{nim} r --backend:{mode} --hints:off --nimcache:{nimcache} {file}"
       check execCmdEx(cmd) == ("ok3\n", 0)
 
+  block: # further issues with `--backend`
+    let file = testsDir / "misc/mbackend.nim"
+    var cmd = fmt"{nim} doc -b:cpp --hints:off --nimcache:{nimcache} {file}"
+    check execCmdEx(cmd) == ("", 0)
+    cmd = fmt"{nim} check -b:c -b:cpp --hints:off --nimcache:{nimcache} {file}"
+    check execCmdEx(cmd) == ("", 0)
+    # issue https://github.com/timotheecour/Nim/issues/175
+    cmd = fmt"{nim} c -b:js -b:cpp --hints:off --nimcache:{nimcache} {file}"
+    check execCmdEx(cmd) == ("", 0)
+
   block: # some importc tests
     # issue #14314
     let file = testsDir / "misc/mimportc.nim"