From 9502e39b634eea8e04f07ddc110b466387f42322 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 11 May 2020 03:01:18 -0700 Subject: [PATCH] `nim doc --backend:js`, `nim doc --doccmd:-d:foo`, `nim r --backend:js`, `--doccmd:skip` + other improvements (#14278) * `nim doc --backend:js|cpp...` `nim doc --doccmd:'-d:foo --threads:on'` `nim r --backend:cpp...` (implies --run --usenimcache) * --usenimcache works with all targets * --docCmd:skip now skips compiling snippets; 50X speedup for doc/manual.rst --- compiler/ccgthreadvars.nim | 2 +- compiler/cgen.nim | 8 ++-- compiler/cmdlinehelper.nim | 3 +- compiler/commands.nim | 11 ++++- compiler/docgen.nim | 29 +++++++++----- compiler/extccomp.nim | 24 +++++------ compiler/lambdalifting.nim | 6 +-- compiler/main.nim | 69 ++++++++++++++++++-------------- compiler/nim.nim | 12 +++--- compiler/options.nim | 32 ++++++++++++--- compiler/pragmas.nim | 8 ++-- compiler/semexprs.nim | 2 +- compiler/sizealignoffsetimpl.nim | 2 +- compiler/transf.nim | 4 +- compiler/vmops.nim | 5 ++- doc/advopt.txt | 11 ++++- doc/basicopt.txt | 6 ++- lib/std/compilesettings.nim | 2 + tests/nimdoc/m13129.nim | 36 +++++++++++++++++ tests/nimdoc/readme.md | 2 + tests/trunner.nim | 37 ++++++++++++----- tests/vm/tcompilesetting.nim | 1 + 22 files changed, 213 insertions(+), 99 deletions(-) create mode 100644 tests/nimdoc/m13129.nim create mode 100644 tests/nimdoc/readme.md diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim index 3701f337d2dd..8bf5e573f981 100644 --- a/compiler/ccgthreadvars.nim +++ b/compiler/ccgthreadvars.nim @@ -46,7 +46,7 @@ proc generateThreadLocalStorage(m: BModule) = proc generateThreadVarsSize(m: BModule) = if m.g.nimtv != nil: - let externc = if m.config.cmd == cmdCompileToCpp or + let externc = if m.config.backend == backendCpp or sfCompileToCpp in m.module.flags: "extern \"C\" " else: "" m.s[cfsProcs].addf( diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 9e356206f07c..c32e86a8894d 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -279,7 +279,7 @@ proc genProc(m: BModule, prc: PSym) proc raiseInstr(p: BProc): Rope template compileToCpp(m: BModule): untyped = - m.config.cmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags + m.config.backend == backendCpp or sfCompileToCpp in m.module.flags proc getTempName(m: BModule): Rope = result = m.tmpBase & rope(m.labels) @@ -1066,11 +1066,11 @@ proc genProcAux(m: BModule, prc: PSym) = proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} = result = (sfCompileToCpp in m.module.flags and sfCompileToCpp notin sym.getModule().flags and - m.config.cmd != cmdCompileToCpp) or ( + m.config.backend != backendCpp) or ( sym.flags * {sfInfixCall, sfCompilerProc, sfMangleCpp} == {} and sym.flags * {sfImportc, sfExportc} != {} and sym.magic == mNone and - m.config.cmd == cmdCompileToCpp) + m.config.backend == backendCpp) proc genProcPrototype(m: BModule, sym: PSym) = useHeader(m, sym) @@ -1867,7 +1867,7 @@ proc writeHeader(m: BModule) = proc getCFile(m: BModule): AbsoluteFile = let ext = if m.compileToCpp: ".nim.cpp" - elif m.config.cmd == cmdCompileToOC or sfCompileToObjc in m.module.flags: ".nim.m" + elif m.config.backend == backendObjc or sfCompileToObjc in m.module.flags: ".nim.m" else: ".nim.c" result = changeFileExt(completeCfilePath(m.config, withPackageName(m.config, m.cfilename)), ext) diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim index d5c743fc6883..dd74f54dc4b6 100644 --- a/compiler/cmdlinehelper.nim +++ b/compiler/cmdlinehelper.nim @@ -78,7 +78,8 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi # XXX This is hacky. We need to find a better way. case conf.command of "cpp", "compiletocpp": - conf.cmd = cmdCompileToCpp + conf.backend = backendCpp + conf.cmd = cmdCompileToBackend else: discard diff --git a/compiler/commands.nim b/compiler/commands.nim index e980d64fc60c..2ab79f14aefa 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -434,11 +434,18 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "outdir": expectArg(conf, switch, arg, pass, info) conf.outDir = processPath(conf, arg, info, notRelativeToProj=true) + of "usenimcache": + processOnOffSwitchG(conf, {optUseNimcache}, arg, pass, info) of "docseesrcurl": expectArg(conf, switch, arg, pass, info) conf.docSeeSrcUrl = arg of "docroot": conf.docRoot = if arg.len == 0: "@default" else: arg + of "backend", "b": + let backend = parseEnum(arg.normalize, TBackend.default) + if backend == TBackend.default: localError(conf, info, "invalid backend: '$1'" % arg) + conf.backend = backend + of "doccmd": conf.docCmd = arg of "mainmodule", "m": discard "allow for backwards compatibility, but don't do anything" of "define", "d": @@ -495,7 +502,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; defineSymbol(conf.symbols, "gcmarkandsweep") of "destructors", "arc": conf.selectedGC = gcArc - if conf.cmd != cmdCompileToCpp: + if conf.backend != backendCpp: conf.exc = excGoto defineSymbol(conf.symbols, "gcdestructors") defineSymbol(conf.symbols, "gcarc") @@ -506,7 +513,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; defineSymbol(conf.symbols, "nimV2") of "orc": conf.selectedGC = gcOrc - if conf.cmd != cmdCompileToCpp: + if conf.backend != backendCpp: conf.exc = excGoto defineSymbol(conf.symbols, "gcdestructors") defineSymbol(conf.symbols, "gcorc") diff --git a/compiler/docgen.nim b/compiler/docgen.nim index a42be9a9cffb..c01d5633f883 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -22,6 +22,7 @@ import const exportSection = skField htmldocsDir = RelativeDir"htmldocs" + docCmdSkip = "skip" type TSections = array[TSymKind, Rope] @@ -196,6 +197,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, initStrTable result.types result.onTestSnippet = proc (gen: var RstGenerator; filename, cmd: string; status: int; content: string) = + if conf.docCmd == docCmdSkip: return inc(gen.id) var d = TDocumentor(gen) var outp: AbsoluteFile @@ -444,21 +446,26 @@ proc testExample(d: PDoc; ex: PNode) = d.examples.add "import r\"" & outp.string & "\"\n" proc runAllExamples(d: PDoc) = - if d.examples.len == 0: return + let docCmd = d.conf.docCmd + let backend = d.conf.backend + # This used to be: `let backend = if isDefined(d.conf, "js"): "js"` (etc), however + # using `-d:js` (etc) cannot work properly, eg would fail with `importjs` + # since semantics are affected by `config.backend`, not by isDefined(d.conf, "js") + if d.examples.len == 0 or docCmd == docCmdSkip: return let outputDir = d.conf.getNimcacheDir / RelativeDir"runnableExamples" let outp = outputDir / RelativeFile(extractFilename(d.filename.changeFileExt"" & "_examples.nim")) writeFile(outp, d.examples) - let backend = if isDefined(d.conf, "js"): "js" - elif isDefined(d.conf, "cpp"): "cpp" - elif isDefined(d.conf, "objc"): "objc" - else: "c" - if os.execShellCmd(os.getAppFilename() & " " & backend & - " --warning[UnusedImport]:off" & - " --path:" & quoteShell(d.conf.projectPath) & - " --nimcache:" & quoteShell(outputDir) & - " -r " & quoteShell(outp)) != 0: - quit "[Examples] failed: see " & outp.string + let cmd = "$nim $backend -r --warning:UnusedImport:off --path:$path --nimcache:$nimcache $docCmd $file" % [ + "nim", os.getAppFilename(), + "backend", $d.conf.backend, + "path", quoteShell(d.conf.projectPath), + "nimcache", quoteShell(outputDir), + "file", quoteShell(outp), + "docCmd", docCmd, + ] + if os.execShellCmd(cmd) != 0: + quit "[runnableExamples] failed: generated file: '$1' cmd: $2" % [outp.string, cmd] else: # keep generated source file `outp` to allow inspection. rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string]) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 7c255484d5ad..3c2435beca8e 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -307,14 +307,12 @@ 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 = - if conf.cmd == cmdCompileToCpp: - ".cpp" & suffix - elif conf.cmd == cmdCompileToOC: - ".objc" & suffix - elif conf.cmd == cmdCompileToJS: - ".js" & suffix + case conf.backend + of backendCpp, backendJs, backendObjc: "." & $conf.backend & suffix + of backendC: suffix else: - suffix + doAssert false + "" if (conf.target.hostOS != conf.target.targetOS or conf.target.hostCPU != conf.target.targetCPU) and optCompileOnly notin conf.globalOptions: @@ -481,10 +479,10 @@ proc needsExeExt(conf: ConfigRef): bool {.inline.} = (conf.target.hostOS == osWindows) proc useCpp(conf: ConfigRef; cfile: AbsoluteFile): bool = - conf.cmd == cmdCompileToCpp and not cfile.string.endsWith(".c") + conf.backend == backendCpp and not cfile.string.endsWith(".c") proc envFlags(conf: ConfigRef): string = - result = if conf.cmd == cmdCompileToCpp: + result = if conf.backend == backendCpp: getEnv("CXXFLAGS") else: getEnv("CFLAGS") @@ -535,7 +533,7 @@ proc ccHasSaneOverflow*(conf: ConfigRef): bool = proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string = result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe - elif optMixedMode in conf.globalOptions and conf.cmd != cmdCompileToCpp: CC[compiler].cppCompiler + elif optMixedMode in conf.globalOptions and conf.backend != backendCpp: CC[compiler].cppCompiler else: getCompilerExe(conf, compiler, AbsoluteFile"") proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, @@ -626,8 +624,10 @@ proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash = getCompileCFileCmd(conf, cfile)) proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool = - if conf.cmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM, cmdNone}: - return false + case conf.backend + of backendInvalid: doAssert false + of backendJs: return false # pre-existing behavior, but not sure it's good + else: discard var hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1") var currentHash = footprint(conf, cfile) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 01def12ada4d..fc1ad5e4033a 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -235,7 +235,7 @@ template isIterator*(owner: PSym): bool = proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} = ## lambda lifting can be harmful for JS-like code generators. let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro - result = conf.cmd == cmdCompileToJS and not isCompileTime + result = conf.backend == backendJs and not isCompileTime proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; owner: PSym) = createTypeBoundOps(g, nil, refType.lastSon, info) @@ -846,14 +846,14 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNo fn.typ.callConv = oldCC proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PNode = - # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs + # XXX backend == backendJs does not suffice! The compiletime stuff needs # the transformation even when compiling to JS ... # However we can do lifting for the stuff which is *only* compiletime. let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro if body.kind == nkEmpty or ( - g.config.cmd == cmdCompileToJS and not isCompileTime) or + g.config.backend == backendJs and not isCompileTime) or fn.skipGenericOwner.kind != skModule: # ignore forward declaration: diff --git a/compiler/main.nim b/compiler/main.nim index c94af24d5bcb..00bc579df7de 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -119,8 +119,7 @@ when not defined(leanCompiler): let conf = graph.config conf.exc = excCpp - if conf.outDir.isEmpty: - conf.outDir = conf.projectPath + setOutDir(conf) if conf.outFile.isEmpty: conf.outFile = RelativeFile(conf.projectName & ".js") @@ -128,7 +127,6 @@ when not defined(leanCompiler): setTarget(graph.config.target, osJS, cpuJS) #initDefines() defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility - defineSymbol(graph.config.symbols, "js") semanticPasses(graph) registerPass(graph, JSgenPass) compileProject(graph) @@ -190,25 +188,46 @@ proc mainCommand*(graph: ModuleGraph) = conf.lastCmdTime = epochTime() conf.searchPaths.add(conf.libpath) setId(100) - template handleC() = - conf.cmd = cmdCompileToC - if conf.exc == excNone: conf.exc = excSetjmp - defineSymbol(graph.config.symbols, "c") - commandCompileToC(graph) + + ## Calling `setOutDir(conf)` unconditionally would fix regression + ## https://github.com/nim-lang/Nim/issues/6583#issuecomment-625711125 + 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 + 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 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) + of backendInvalid: doAssert false + 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 - defineSymbol(graph.config.symbols, "cpp") - commandCompileToC(graph) - of "objc", "compiletooc": - conf.cmd = cmdCompileToOC - defineSymbol(graph.config.symbols, "objc") - commandCompileToC(graph) + 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 "r": # different from `"run"`! conf.globalOptions.incl {optRun, optUseNimcache} - handleC() + handleBackend(conf.backend) of "run": conf.cmd = cmdRun when hasTinyCBackend: @@ -216,18 +235,6 @@ proc mainCommand*(graph: ModuleGraph) = commandCompileToC(graph) else: rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc") - of "js", "compiletojs": - when defined(leanCompiler): - quit "compiler wasn't built with JS code generator" - else: - conf.cmd = cmdCompileToJS - 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) of "doc0": when defined(leanCompiler): quit "compiler wasn't built with documentation generator" diff --git a/compiler/nim.nim b/compiler/nim.nim index dba5b8bc3cfb..b48fce0b8edb 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -88,17 +88,19 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = tccgen.run(conf, conf.arguments) if optRun in conf.globalOptions: let output = conf.absOutFile - let ex = quoteShell output case conf.cmd - of cmdCompileToJS: - execExternalProgram(conf, findNodeJs() & " " & ex & ' ' & conf.arguments) + of cmdCompileToBackend: + var cmdPrefix = "" + case conf.backend + of backendC, backendCpp, backendObjc: discard + of backendJs: cmdPrefix = findNodeJs() & " " + else: doAssert false, $conf.backend + execExternalProgram(conf, cmdPrefix & output.quoteShell & ' ' & conf.arguments) of cmdDoc, cmdRst2html: if conf.arguments.len > 0: # reserved for future use rawMessage(conf, errGenerated, "'$1 cannot handle arguments" % [$conf.cmd]) openDefaultBrowser($output) - of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC: - execExternalProgram(conf, ex & ' ' & conf.arguments) else: # support as needed rawMessage(conf, errGenerated, "'$1 cannot handle --run" % [$conf.cmd]) diff --git a/compiler/options.nim b/compiler/options.nim index 5475bc07bdc6..3e6c7989fee2 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -104,12 +104,26 @@ const harmlessOptions* = {optForceFullMake, optNoLinking, optRun, optUseColors, optStdout} +type + TBackend* = enum + backendInvalid = "" # for parseEnum + backendC = "c" + backendCpp = "cpp" # was cmdCompileToCpp + backendJs = "js" # was cmdCompileToJS + backendObjc = "objc" # was cmdCompileToOC + # backendNimscript = "nimscript" # this could actually work + # backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM + type TCommands* = enum # Nim's commands # **keep binary compatible** - cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, - cmdCompileToJS, - cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc, + cmdNone, + cmdCompileToC, # deadcode + cmdCompileToCpp, # deadcode + cmdCompileToOC, # deadcode + cmdCompileToJS, # deadcode + cmdCompileToLLVM, # deadcode + cmdInterpret, cmdPretty, cmdDoc, cmdGenDepend, cmdDump, cmdCheck, # semantic checking for whole project cmdParse, # parse a single file (for debugging) @@ -121,6 +135,7 @@ type cmdInteractive, # start interactive session cmdRun, # run the project via TCC backend cmdJsonScript # compile a .json build file + cmdCompileToBackend, # compile to backend in TBackend TStringSeq* = seq[string] TGCMode* = enum # the selected GC gcUnselected, gcNone, gcBoehm, gcRegions, gcMarkAndSweep, gcArc, gcOrc, @@ -208,6 +223,7 @@ type ## fields marked with '*' are subject to ## the incremental compilation mechanisms ## (+) means "part of the dependency" + backend*: TBackend target*: Target # (+) linesCompiled*: int # all lines that have been compiled options*: TOptions # (+) @@ -274,6 +290,7 @@ type docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \ # The string uses the formatting variables `path` and `line`. docRoot*: string ## see nim --fullhelp for --docRoot + docCmd*: string ## see nim --fullhelp for --docCmd # the used compiler cIncludes*: seq[AbsoluteDir] # directories to search for included files @@ -402,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, externalToLink: @[], linkOptionsCmd: "", compileOptionsCmd: @[], @@ -522,7 +539,10 @@ proc setConfigVar*(conf: ConfigRef; key, val: string) = conf.configVars[key] = val proc getOutFile*(conf: ConfigRef; filename: RelativeFile, ext: string): AbsoluteFile = - conf.outDir / changeFileExt(filename, ext) + # explains regression https://github.com/nim-lang/Nim/issues/6583#issuecomment-625711125 + # Yet another reason why "" should not mean "."; `""/something` should raise + # instead of implying "" == "." as it's bug prone. + result = conf.outDir / changeFileExt(filename, ext) proc absOutFile*(conf: ConfigRef): AbsoluteFile = if false: @@ -615,7 +635,7 @@ proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir = # XXX projectName should always be without a file extension! result = if not conf.nimcacheDir.isEmpty: conf.nimcacheDir - elif conf.cmd == cmdCompileToJS: + elif conf.backend == backendJs: conf.projectPath / genSubDir else: AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name & diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 465608b102cf..021a8b339616 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -179,7 +179,7 @@ proc processImportCpp(c: PContext; s: PSym, extname: string, info: TLineInfo) = incl(s.flags, sfImportc) incl(s.flags, sfInfixCall) excl(s.flags, sfForward) - if c.config.cmd == cmdCompileToC: + if c.config.backend == backendC: let m = s.getModule() incl(m.flags, sfCompileToCpp) incl c.config.globalOptions, optMixedMode @@ -797,8 +797,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wExportc, wExportCpp: makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info) if k == wExportCpp: - if c.config.cmd != cmdCompileToCpp: - localError(c.config, it.info, "exportcpp requires `nim cpp`, got " & $c.config.cmd) + if c.config.backend != backendCpp: + localError(c.config, it.info, "exportcpp requires `cpp` backend, got " & $c.config.backend) else: incl(sym.flags, sfMangleCpp) incl(sym.flags, sfUsed) # avoid wrong hints @@ -819,7 +819,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wImportCpp: processImportCpp(c, sym, getOptionalStr(c, it, "$1"), it.info) of wImportJs: - if c.config.cmd != cmdCompileToJS: + if c.config.backend != backendJs: localError(c.config, it.info, "`importjs` pragma requires the JavaScript target") let name = getOptionalStr(c, it, "$1") incl(sym.flags, sfImportc) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 56b6076d5efe..43fd1b69c17f 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -660,7 +660,7 @@ proc hasUnresolvedArgs(c: PContext, n: PNode): bool = return false proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = - if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or + if n.kind == nkHiddenDeref and not (c.config.backend == backendCpp or sfCompileToCpp in c.module.flags): checkSonsLen(n, 1, c.config) result = n[0] diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index dc3fefeeacf5..9d12297eac49 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -346,7 +346,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = while st.kind in skipPtrs: st = st[^1] computeSizeAlign(conf, st) - if conf.cmd == cmdCompileToCpp: + if conf.backend == backendCpp: OffsetAccum( offset: int(st.size) - int(st.paddingAtEnd), maxAlign: st.align diff --git a/compiler/transf.nim b/compiler/transf.nim index cbf904255cb8..bd9f567ed3f8 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -411,7 +411,7 @@ proc transformYield(c: PTransf, n: PNode): PNode = proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode = result = transformSons(c, n) - if c.graph.config.cmd == cmdCompileToCpp or sfCompileToCpp in c.module.flags: return + if c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags: return var n = result case n[0].kind of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: @@ -447,7 +447,7 @@ proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode = # we cannot generate a proper thunk here for GC-safety reasons # (see internal documentation): - if c.graph.config.cmd == cmdCompileToJS: return prc + if c.graph.config.backend == backendJs: return prc result = newNodeIT(nkClosure, prc.info, dest) var conv = newNodeIT(nkHiddenSubConv, prc.info, dest) conv.add(newNodeI(nkEmpty, prc.info)) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 54b2877e72af..27d1ef479964 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -121,6 +121,7 @@ when defined(nimHasInvariant): of linkOptions: result = conf.linkOptions of compileOptions: result = conf.compileOptions of ccompilerPath: result = conf.cCompilerPath + of backend: result = $conf.backend proc querySettingSeqImpl(conf: ConfigRef, switch: BiggestInt): seq[string] = template copySeq(field: untyped): untyped = @@ -215,7 +216,7 @@ proc registerAdditionalOps*(c: PCtx) = proc hashVmImpl(a: VmArgs) = var res = hashes.hash(a.getString(0), a.getInt(1).int, a.getInt(2).int) - if c.config.cmd == cmdCompileToJS: + if c.config.backend == backendJs: # emulate JS's terrible integers: res = cast[int32](res) setResult(a, res) @@ -232,7 +233,7 @@ proc registerAdditionalOps*(c: PCtx) = bytes[i] = byte(arr[i].intVal and 0xff) var res = hashes.hash(bytes, sPos, ePos) - if c.config.cmd == cmdCompileToJS: + if c.config.backend == backendJs: # emulate JS's terrible integers: res = cast[int32](res) setResult(a, res) diff --git a/doc/advopt.txt b/doc/advopt.txt index c877b02e9263..a668be5c023b 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -5,6 +5,7 @@ Advanced commands: //js compile project to Javascript //e run a Nimscript file //rst2html convert a reStructuredText file to HTML + use `--docCmd:skip` to skip compiling snippets //rst2tex convert a reStructuredText file to TeX //jsondoc extract the documentation to a json file //ctags create a tags file @@ -29,6 +30,8 @@ Runtime checks (see -x): Advanced options: -o:FILE, --out:FILE set the output filename --outdir:DIR set the path where the output file will be written + --usenimcache will use `outdir=$$nimcache`, whichever it resolves + to after all options have been processed --stdout:on|off output to stdout --colors:on|off turn compiler messages coloring on|off --listFullPaths:on|off list full paths in messages @@ -69,13 +72,17 @@ Advanced options: --clib:LIBNAME link an additional C library (you should omit platform-specific extensions) --project document the whole project (doc2) - --docRoot:path nim doc --docRoot:/foo --project --outdir:docs /foo/sub/main.nim + --docRoot:path `nim doc --docRoot:/foo --project --outdir:docs /foo/sub/main.nim` generates: docs/sub/main.html if path == @pkg, will use nimble file enclosing dir - if path == @path, will use first matching dir in --path + if path == @path, will use first matching dir in `--path` if path == @default (the default and most useful), will use best match among @pkg,@path. if these are nonexistant, will use project path + -b, --backend:c|cpp|js|objc sets backend to use with commands like `nim doc` or `nim r` + --docCmd:cmd if `cmd == skip`, skips runnableExamples + else, runs runnableExamples with given options, eg: + `--docCmd:"-d:foo --threads:on"` --docSeeSrcUrl:url activate 'see source' for doc and doc2 commands (see doc.item.seesrc in config/nimdoc.cfg) --docInternal also generate documentation for non-exported symbols diff --git a/doc/basicopt.txt b/doc/basicopt.txt index cc808a077aa1..cdd8ab9be071 100644 --- a/doc/basicopt.txt +++ b/doc/basicopt.txt @@ -4,8 +4,10 @@ Command: //compile, c compile project with default code generator (C) - //r compile & run $nimcach/projname [arguments] - //doc generate the documentation for inputfile + //r compile to $nimcache/projname, run with [arguments] + using backend specified by `--backend` (default: c) + //doc generate the documentation for inputfile for + backend specified by `--backend` (default: c) Arguments: arguments are passed to the program being run (if --run option is selected) diff --git a/lib/std/compilesettings.nim b/lib/std/compilesettings.nim index 12204658d45c..b9b13175d569 100644 --- a/lib/std/compilesettings.nim +++ b/lib/std/compilesettings.nim @@ -29,6 +29,8 @@ type linkOptions, ## additional options passed to the linker compileOptions, ## additional options passed to the C/C++ compiler ccompilerPath ## the path to the C/C++ compiler + backend ## the backend (eg: c|cpp|objc|js); both `nim doc --backend:js` + ## and `nim js` would imply backend=js MultipleValueSetting* {.pure.} = enum ## \ ## settings resulting in a seq of string values diff --git a/tests/nimdoc/m13129.nim b/tests/nimdoc/m13129.nim new file mode 100644 index 000000000000..df4b5a3f58de --- /dev/null +++ b/tests/nimdoc/m13129.nim @@ -0,0 +1,36 @@ +when defined(cpp): + {.push header: "".} + type + Vector[T] {.importcpp: "std::vector".} = object +elif defined(js): + proc endsWith*(s, suffix: cstring): bool {.noSideEffect,importjs: "#.endsWith(#)".} +elif defined(c): + proc c_printf*(frmt: cstring): cint {. + importc: "printf", header: "", varargs, discardable.} + +proc main*() = + runnableExamples: + import std/compilesettings + doAssert not defined(m13129Foo1) + doAssert defined(m13129Foo2) + doAssert not defined(nimdoc) + echo "ok2: backend: " & querySetting(backend) + # echo defined(c), defined(js), + +import std/compilesettings +when defined nimdoc: + # import std/compilesettings + static: + doAssert defined(m13129Foo1) + doAssert not defined(m13129Foo2) + echo "ok1:" & querySetting(backend) + +when isMainModule: + when not defined(js): + import std/os + let cache = querySetting(nimcacheDir) + doAssert cache.len > 0 + let app = getAppFilename() + doAssert app.isRelativeTo(cache) + doAssert querySetting(projectFull) == currentSourcePath + echo "ok3" diff --git a/tests/nimdoc/readme.md b/tests/nimdoc/readme.md new file mode 100644 index 000000000000..64b16c2a5c0b --- /dev/null +++ b/tests/nimdoc/readme.md @@ -0,0 +1,2 @@ +the html validation is tested by nimdoc/tester.nim +the runnableExamples + nim doc logic (across backend) is tested here diff --git a/tests/trunner.nim b/tests/trunner.nim index f71c324a2390..e6a8a2077f70 100644 --- a/tests/trunner.nim +++ b/tests/trunner.nim @@ -6,7 +6,7 @@ discard """ ## tests that don't quite fit the mold and are easier to handle via `execCmdEx` ## A few others could be added to here to simplify code. -import std/[strformat,os,osproc] +import std/[strformat,os,osproc,unittest] const nim = getCurrentCompilerExe() @@ -15,8 +15,9 @@ const mode = elif defined(cpp): "cpp" else: static: doAssert false +const testsDir = currentSourcePath().parentDir + proc runCmd(file, options = ""): auto = - const testsDir = currentSourcePath().parentDir let fileabs = testsDir / file.unixToNativePath doAssert fileabs.existsFile, fileabs let cmd = fmt"{nim} {mode} {options} --hints:off {fileabs}" @@ -55,11 +56,11 @@ ret=[s1:foobar s2:foobar age:25 pi:3.14] else: # don't run twice the same test import std/[strutils] - template check(msg) = doAssert msg in output, output + template check2(msg) = doAssert msg in output, output block: # mstatic_assert let (output, exitCode) = runCmd("ccgbugs/mstatic_assert.nim", "-d:caseBad") - check "sizeof(bool) == 2" + check2 "sizeof(bool) == 2" doAssert exitCode != 0 block: # ABI checks @@ -72,11 +73,11 @@ else: # don't run twice the same test # on platforms that support _StaticAssert natively, errors will show full context, eg: # error: static_assert failed due to requirement 'sizeof(unsigned char) == 8' # "backend & Nim disagree on size for: BadImportcType{int64} [declared in mabi_check.nim(1, 6)]" - check "sizeof(unsigned char) == 8" - check "sizeof(struct Foo2) == 1" - check "sizeof(Foo5) == 16" - check "sizeof(Foo5) == 3" - check "sizeof(struct Foo6) == " + check2 "sizeof(unsigned char) == 8" + check2 "sizeof(struct Foo2) == 1" + check2 "sizeof(Foo5) == 16" + check2 "sizeof(Foo5) == 3" + check2 "sizeof(struct Foo6) == " doAssert exitCode != 0 import streams @@ -103,3 +104,21 @@ else: # don't run twice the same test var (output, exitCode) = execCmdEx(cmd) output.stripLineEnd doAssert output == expected + + block: # nim doc --backend:$backend --doccmd:$cmd + # test for https://github.com/nim-lang/Nim/issues/13129 + # test for https://github.com/nim-lang/Nim/issues/13891 + const buildDir = testsDir.parentDir / "build" + const nimcache = buildDir / "nimcacheTrunner" + # `querySetting(nimcacheDir)` would also be possible, but we thus + # avoid stomping on other parallel tests + let file = testsDir / "nimdoc/m13129.nim" + for backend in fmt"{mode} js".split: + let cmd = fmt"{nim} doc -b:{backend} --nimcache:{nimcache} -d:m13129Foo1 --doccmd:'-d:m13129Foo2 --hints:off' --usenimcache --hints:off {file}" + check execCmdEx(cmd) == (&"ok1:{backend}\nok2: backend: {backend}\n", 0) + # checks that --usenimcache works with `nim doc` + check fileExists(nimcache / "m13129.html") + + block: # mak sure --backend works with `nim r` + let cmd = fmt"{nim} r --backend:{mode} --hints:off --nimcache:{nimcache} {file}" + check execCmdEx(cmd) == ("ok3\n", 0) diff --git a/tests/vm/tcompilesetting.nim b/tests/vm/tcompilesetting.nim index 79527d58415f..98565ab94f2c 100644 --- a/tests/vm/tcompilesetting.nim +++ b/tests/vm/tcompilesetting.nim @@ -17,3 +17,4 @@ static: doAssert "myNimCache" in nc doAssert "myNimblePath" in np[0] +doAssert querySetting(backend) == "c"