main.nim 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # implements the command dispatcher and several commands
  10. when not defined(nimcore):
  11. {.error: "nimcore MUST be defined for Nim's core tooling".}
  12. import
  13. llstream, strutils, os, ast, lexer, syntaxes, options, msgs,
  14. condsyms, times,
  15. sem, idents, passes, extccomp,
  16. cgen, json, nversion,
  17. platform, nimconf, passaux, depends, vm, idgen,
  18. modules,
  19. modulegraphs, tables, rod, lineinfos, pathutils
  20. when not defined(leanCompiler):
  21. import jsgen, docgen, docgen2
  22. proc semanticPasses(g: ModuleGraph) =
  23. registerPass g, verbosePass
  24. registerPass g, semPass
  25. proc writeDepsFile(g: ModuleGraph) =
  26. let fname = g.config.nimcacheDir / RelativeFile(g.config.projectName & ".deps")
  27. let f = open(fname.string, fmWrite)
  28. for m in g.modules:
  29. if m != nil:
  30. f.writeLine(toFullPath(g.config, m.position.FileIndex))
  31. for k in g.inclToMod.keys:
  32. if g.getModule(k).isNil: # don't repeat includes which are also modules
  33. f.writeLine(toFullPath(g.config, k))
  34. f.close()
  35. proc commandGenDepend(graph: ModuleGraph) =
  36. semanticPasses(graph)
  37. registerPass(graph, gendependPass)
  38. compileProject(graph)
  39. let project = graph.config.projectFull
  40. writeDepsFile(graph)
  41. generateDot(graph, project)
  42. execExternalProgram(graph.config, "dot -Tpng -o" &
  43. changeFileExt(project, "png").string &
  44. ' ' & changeFileExt(project, "dot").string)
  45. proc commandCheck(graph: ModuleGraph) =
  46. graph.config.errorMax = high(int) # do not stop after first error
  47. defineSymbol(graph.config.symbols, "nimcheck")
  48. semanticPasses(graph) # use an empty backend for semantic checking only
  49. compileProject(graph)
  50. when not defined(leanCompiler):
  51. proc commandDoc2(graph: ModuleGraph; json: bool) =
  52. handleDocOutputOptions graph.config
  53. graph.config.errorMax = high(int) # do not stop after first error
  54. semanticPasses(graph)
  55. if json: registerPass(graph, docgen2JsonPass)
  56. else: registerPass(graph, docgen2Pass)
  57. compileProject(graph)
  58. finishDoc2Pass(graph.config.projectName)
  59. proc commandCompileToC(graph: ModuleGraph) =
  60. let conf = graph.config
  61. if conf.outDir.isEmpty:
  62. conf.outDir = conf.projectPath
  63. if conf.outFile.isEmpty:
  64. let targetName = if optGenDynLib in conf.globalOptions:
  65. platform.OS[conf.target.targetOS].dllFrmt % conf.projectName
  66. else:
  67. conf.projectName & platform.OS[conf.target.targetOS].exeExt
  68. conf.outFile = RelativeFile targetName
  69. extccomp.initVars(conf)
  70. semanticPasses(graph)
  71. registerPass(graph, cgenPass)
  72. if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"):
  73. let proj = changeFileExt(conf.projectFull, "")
  74. if not changeDetectedViaJsonBuildInstructions(conf, proj):
  75. # nothing changed
  76. graph.config.notes = graph.config.mainPackageNotes
  77. return
  78. if not extccomp.ccHasSaneOverflow(conf):
  79. conf.symbols.defineSymbol("nimEmulateOverflowChecks")
  80. compileProject(graph)
  81. if graph.config.errorCounter > 0:
  82. return # issue #9933
  83. cgenWriteModules(graph.backend, conf)
  84. if conf.cmd != cmdRun:
  85. extccomp.callCCompiler(conf)
  86. # for now we do not support writing out a .json file with the build instructions when HCR is on
  87. if not conf.hcrOn:
  88. extccomp.writeJsonBuildInstructions(conf)
  89. if optGenScript in graph.config.globalOptions:
  90. writeDepsFile(graph)
  91. proc commandJsonScript(graph: ModuleGraph) =
  92. let proj = changeFileExt(graph.config.projectFull, "")
  93. extccomp.runJsonBuildInstructions(graph.config, proj)
  94. when not defined(leanCompiler):
  95. proc commandCompileToJS(graph: ModuleGraph) =
  96. let conf = graph.config
  97. conf.exc = excCpp
  98. if conf.outDir.isEmpty:
  99. conf.outDir = conf.projectPath
  100. if conf.outFile.isEmpty:
  101. conf.outFile = RelativeFile(conf.projectName & ".js")
  102. #incl(gGlobalOptions, optSafeCode)
  103. setTarget(graph.config.target, osJS, cpuJS)
  104. #initDefines()
  105. defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility
  106. defineSymbol(graph.config.symbols, "js")
  107. semanticPasses(graph)
  108. registerPass(graph, JSgenPass)
  109. compileProject(graph)
  110. if optGenScript in graph.config.globalOptions:
  111. writeDepsFile(graph)
  112. proc interactivePasses(graph: ModuleGraph) =
  113. initDefines(graph.config.symbols)
  114. defineSymbol(graph.config.symbols, "nimscript")
  115. # note: seems redundant with -d:nimHasLibFFI
  116. when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
  117. registerPass(graph, verbosePass)
  118. registerPass(graph, semPass)
  119. registerPass(graph, evalPass)
  120. proc commandInteractive(graph: ModuleGraph) =
  121. graph.config.errorMax = high(int) # do not stop after first error
  122. interactivePasses(graph)
  123. compileSystemModule(graph)
  124. if graph.config.commandArgs.len > 0:
  125. discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
  126. else:
  127. var m = graph.makeStdinModule()
  128. incl(m.flags, sfMainModule)
  129. processModule(graph, m, llStreamOpenStdIn())
  130. const evalPasses = [verbosePass, semPass, evalPass]
  131. proc evalNim(graph: ModuleGraph; nodes: PNode, module: PSym) =
  132. carryPasses(graph, nodes, module, evalPasses)
  133. proc commandScan(cache: IdentCache, config: ConfigRef) =
  134. var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt)
  135. var stream = llStreamOpen(f, fmRead)
  136. if stream != nil:
  137. var
  138. L: TLexer
  139. tok: TToken
  140. initToken(tok)
  141. openLexer(L, f, stream, cache, config)
  142. while true:
  143. rawGetTok(L, tok)
  144. printTok(config, tok)
  145. if tok.tokType == tkEof: break
  146. closeLexer(L)
  147. else:
  148. rawMessage(config, errGenerated, "cannot open file: " & f.string)
  149. const
  150. PrintRopeCacheStats = false
  151. proc mainCommand*(graph: ModuleGraph) =
  152. let conf = graph.config
  153. let cache = graph.cache
  154. setupModuleCache(graph)
  155. # In "nim serve" scenario, each command must reset the registered passes
  156. clearPasses(graph)
  157. conf.lastCmdTime = epochTime()
  158. conf.searchPaths.add(conf.libpath)
  159. setId(100)
  160. case conf.command.normalize
  161. of "c", "cc", "compile", "compiletoc":
  162. # compile means compileToC currently
  163. conf.cmd = cmdCompileToC
  164. if conf.exc == excNone: conf.exc = excSetjmp
  165. defineSymbol(graph.config.symbols, "c")
  166. commandCompileToC(graph)
  167. of "cpp", "compiletocpp":
  168. conf.cmd = cmdCompileToCpp
  169. if conf.exc == excNone: conf.exc = excCpp
  170. defineSymbol(graph.config.symbols, "cpp")
  171. commandCompileToC(graph)
  172. of "objc", "compiletooc":
  173. conf.cmd = cmdCompileToOC
  174. defineSymbol(graph.config.symbols, "objc")
  175. commandCompileToC(graph)
  176. of "run":
  177. conf.cmd = cmdRun
  178. when hasTinyCBackend:
  179. extccomp.setCC(conf, "tcc", unknownLineInfo)
  180. commandCompileToC(graph)
  181. else:
  182. rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc")
  183. of "js", "compiletojs":
  184. when defined(leanCompiler):
  185. quit "compiler wasn't built with JS code generator"
  186. else:
  187. conf.cmd = cmdCompileToJS
  188. if conf.hcrOn:
  189. # XXX: At the moment, system.nim cannot be compiled in JS mode
  190. # with "-d:useNimRtl". The HCR option has been processed earlier
  191. # and it has added this define implictly, so we must undo that here.
  192. # A better solution might be to fix system.nim
  193. undefSymbol(conf.symbols, "useNimRtl")
  194. commandCompileToJS(graph)
  195. of "doc0":
  196. when defined(leanCompiler):
  197. quit "compiler wasn't built with documentation generator"
  198. else:
  199. wantMainModule(conf)
  200. conf.cmd = cmdDoc
  201. loadConfigs(DocConfig, cache, conf)
  202. commandDoc(cache, conf)
  203. of "doc2", "doc":
  204. conf.setNoteDefaults(warnLockLevel, false) # issue #13218
  205. conf.setNoteDefaults(warnRedefinitionOfLabel, false) # issue #13218
  206. # because currently generates lots of false positives due to conflation
  207. # of labels links in doc comments, eg for random.rand:
  208. # ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
  209. # ## * `rand proc<#rand,Rand,range[]>`_ that returns a float
  210. when defined(leanCompiler):
  211. quit "compiler wasn't built with documentation generator"
  212. else:
  213. conf.cmd = cmdDoc
  214. loadConfigs(DocConfig, cache, conf)
  215. defineSymbol(conf.symbols, "nimdoc")
  216. commandDoc2(graph, false)
  217. of "rst2html":
  218. conf.setNoteDefaults(warnRedefinitionOfLabel, false) # similar to issue #13218
  219. when defined(leanCompiler):
  220. quit "compiler wasn't built with documentation generator"
  221. else:
  222. conf.cmd = cmdRst2html
  223. loadConfigs(DocConfig, cache, conf)
  224. commandRst2Html(cache, conf)
  225. of "rst2tex":
  226. when defined(leanCompiler):
  227. quit "compiler wasn't built with documentation generator"
  228. else:
  229. conf.cmd = cmdRst2tex
  230. loadConfigs(DocTexConfig, cache, conf)
  231. commandRst2TeX(cache, conf)
  232. of "jsondoc0":
  233. when defined(leanCompiler):
  234. quit "compiler wasn't built with documentation generator"
  235. else:
  236. wantMainModule(conf)
  237. conf.cmd = cmdDoc
  238. loadConfigs(DocConfig, cache, conf)
  239. wantMainModule(conf)
  240. defineSymbol(conf.symbols, "nimdoc")
  241. commandJson(cache, conf)
  242. of "jsondoc2", "jsondoc":
  243. when defined(leanCompiler):
  244. quit "compiler wasn't built with documentation generator"
  245. else:
  246. conf.cmd = cmdDoc
  247. loadConfigs(DocConfig, cache, conf)
  248. wantMainModule(conf)
  249. defineSymbol(conf.symbols, "nimdoc")
  250. commandDoc2(graph, true)
  251. of "ctags":
  252. when defined(leanCompiler):
  253. quit "compiler wasn't built with documentation generator"
  254. else:
  255. wantMainModule(conf)
  256. conf.cmd = cmdDoc
  257. loadConfigs(DocConfig, cache, conf)
  258. defineSymbol(conf.symbols, "nimdoc")
  259. commandTags(cache, conf)
  260. of "buildindex":
  261. when defined(leanCompiler):
  262. quit "compiler wasn't built with documentation generator"
  263. else:
  264. conf.cmd = cmdDoc
  265. loadConfigs(DocConfig, cache, conf)
  266. commandBuildIndex(cache, conf)
  267. of "gendepend":
  268. conf.cmd = cmdGenDepend
  269. commandGenDepend(graph)
  270. of "dump":
  271. conf.cmd = cmdDump
  272. if getConfigVar(conf, "dump.format") == "json":
  273. wantMainModule(conf)
  274. var definedSymbols = newJArray()
  275. for s in definedSymbolNames(conf.symbols): definedSymbols.elems.add(%s)
  276. var libpaths = newJArray()
  277. var lazyPaths = newJArray()
  278. for dir in conf.searchPaths: libpaths.elems.add(%dir.string)
  279. for dir in conf.lazyPaths: lazyPaths.elems.add(%dir.string)
  280. var hints = newJObject() # consider factoring with `listHints`
  281. for a in hintMin..hintMax:
  282. let key = lineinfos.HintsToStr[ord(a) - ord(hintMin)]
  283. hints[key] = %(a in conf.notes)
  284. var warnings = newJObject()
  285. for a in warnMin..warnMax:
  286. let key = lineinfos.WarningsToStr[ord(a) - ord(warnMin)]
  287. warnings[key] = %(a in conf.notes)
  288. var dumpdata = %[
  289. (key: "version", val: %VersionAsString),
  290. (key: "nimExe", val: %(getAppFilename())),
  291. (key: "prefixdir", val: %conf.getPrefixDir().string),
  292. (key: "libpath", val: %conf.libpath.string),
  293. (key: "project_path", val: %conf.projectFull.string),
  294. (key: "defined_symbols", val: definedSymbols),
  295. (key: "lib_paths", val: %libpaths),
  296. (key: "lazyPaths", val: %lazyPaths),
  297. (key: "outdir", val: %conf.outDir.string),
  298. (key: "out", val: %conf.outFile.string),
  299. (key: "nimcache", val: %getNimcacheDir(conf).string),
  300. (key: "hints", val: hints),
  301. (key: "warnings", val: warnings),
  302. ]
  303. msgWriteln(conf, $dumpdata, {msgStdout, msgSkipHook})
  304. else:
  305. msgWriteln(conf, "-- list of currently defined symbols --",
  306. {msgStdout, msgSkipHook})
  307. for s in definedSymbolNames(conf.symbols): msgWriteln(conf, s, {msgStdout, msgSkipHook})
  308. msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook})
  309. for it in conf.searchPaths: msgWriteln(conf, it.string)
  310. of "check":
  311. conf.cmd = cmdCheck
  312. commandCheck(graph)
  313. of "parse":
  314. conf.cmd = cmdParse
  315. wantMainModule(conf)
  316. discard parseFile(conf.projectMainIdx, cache, conf)
  317. of "scan":
  318. conf.cmd = cmdScan
  319. wantMainModule(conf)
  320. commandScan(cache, conf)
  321. msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
  322. of "secret":
  323. conf.cmd = cmdInteractive
  324. commandInteractive(graph)
  325. of "e":
  326. if not fileExists(conf.projectFull):
  327. rawMessage(conf, errGenerated, "NimScript file does not exist: " & conf.projectFull.string)
  328. elif not conf.projectFull.string.endsWith(".nims"):
  329. rawMessage(conf, errGenerated, "not a NimScript file: " & conf.projectFull.string)
  330. # main NimScript logic handled in cmdlinehelper.nim.
  331. of "nop", "help":
  332. # prevent the "success" message:
  333. conf.cmd = cmdDump
  334. of "jsonscript":
  335. conf.cmd = cmdJsonScript
  336. commandJsonScript(graph)
  337. else:
  338. rawMessage(conf, errGenerated, "invalid command: " & conf.command)
  339. if conf.errorCounter == 0 and
  340. conf.cmd notin {cmdInterpret, cmdRun, cmdDump}:
  341. let mem =
  342. when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem"
  343. else: formatSize(getTotalMem()) & " totmem"
  344. let loc = $conf.linesCompiled
  345. let build = if isDefined(conf, "danger"): "Dangerous Release"
  346. elif isDefined(conf, "release"): "Release"
  347. else: "Debug"
  348. let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3)
  349. let project = if optListFullPaths in conf.globalOptions: $conf.projectFull else: $conf.projectName
  350. var output = $conf.absOutFile
  351. if optListFullPaths notin conf.globalOptions: output = output.AbsoluteFile.extractFilename
  352. rawMessage(conf, hintSuccessX, [
  353. "loc", loc,
  354. "sec", sec,
  355. "mem", mem,
  356. "build", build,
  357. "project", project,
  358. "output", output,
  359. ])
  360. when PrintRopeCacheStats:
  361. echo "rope cache stats: "
  362. echo " tries : ", gCacheTries
  363. echo " misses: ", gCacheMisses
  364. echo " int tries: ", gCacheIntTries
  365. echo " efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float),
  366. ffDecimal, 3)
  367. resetAttributes(conf)