main.nim 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  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. compileProject(graph)
  79. if graph.config.errorCounter > 0:
  80. return # issue #9933
  81. cgenWriteModules(graph.backend, conf)
  82. if conf.cmd != cmdRun:
  83. extccomp.callCCompiler(conf)
  84. # for now we do not support writing out a .json file with the build instructions when HCR is on
  85. if not conf.hcrOn:
  86. extccomp.writeJsonBuildInstructions(conf)
  87. if optGenScript in graph.config.globalOptions:
  88. writeDepsFile(graph)
  89. proc commandJsonScript(graph: ModuleGraph) =
  90. let proj = changeFileExt(graph.config.projectFull, "")
  91. extccomp.runJsonBuildInstructions(graph.config, proj)
  92. when not defined(leanCompiler):
  93. proc commandCompileToJS(graph: ModuleGraph) =
  94. let conf = graph.config
  95. if conf.outDir.isEmpty:
  96. conf.outDir = conf.projectPath
  97. if conf.outFile.isEmpty:
  98. conf.outFile = RelativeFile(conf.projectName & ".js")
  99. #incl(gGlobalOptions, optSafeCode)
  100. setTarget(graph.config.target, osJS, cpuJS)
  101. #initDefines()
  102. defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility
  103. defineSymbol(graph.config.symbols, "js")
  104. semanticPasses(graph)
  105. registerPass(graph, JSgenPass)
  106. compileProject(graph)
  107. if optGenScript in graph.config.globalOptions:
  108. writeDepsFile(graph)
  109. proc interactivePasses(graph: ModuleGraph) =
  110. initDefines(graph.config.symbols)
  111. defineSymbol(graph.config.symbols, "nimscript")
  112. # note: seems redundant with -d:nimHasLibFFI
  113. when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
  114. registerPass(graph, verbosePass)
  115. registerPass(graph, semPass)
  116. registerPass(graph, evalPass)
  117. proc commandInteractive(graph: ModuleGraph) =
  118. graph.config.errorMax = high(int) # do not stop after first error
  119. interactivePasses(graph)
  120. compileSystemModule(graph)
  121. if graph.config.commandArgs.len > 0:
  122. discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
  123. else:
  124. var m = graph.makeStdinModule()
  125. incl(m.flags, sfMainModule)
  126. processModule(graph, m, llStreamOpenStdIn())
  127. const evalPasses = [verbosePass, semPass, evalPass]
  128. proc evalNim(graph: ModuleGraph; nodes: PNode, module: PSym) =
  129. carryPasses(graph, nodes, module, evalPasses)
  130. proc commandScan(cache: IdentCache, config: ConfigRef) =
  131. var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt)
  132. var stream = llStreamOpen(f, fmRead)
  133. if stream != nil:
  134. var
  135. L: TLexer
  136. tok: TToken
  137. initToken(tok)
  138. openLexer(L, f, stream, cache, config)
  139. while true:
  140. rawGetTok(L, tok)
  141. printTok(config, tok)
  142. if tok.tokType == tkEof: break
  143. closeLexer(L)
  144. else:
  145. rawMessage(config, errGenerated, "cannot open file: " & f.string)
  146. const
  147. PrintRopeCacheStats = false
  148. proc mainCommand*(graph: ModuleGraph) =
  149. let conf = graph.config
  150. let cache = graph.cache
  151. setupModuleCache(graph)
  152. # In "nim serve" scenario, each command must reset the registered passes
  153. clearPasses(graph)
  154. conf.lastCmdTime = epochTime()
  155. conf.searchPaths.add(conf.libpath)
  156. setId(100)
  157. case conf.command.normalize
  158. of "c", "cc", "compile", "compiletoc":
  159. # compile means compileToC currently
  160. conf.cmd = cmdCompileToC
  161. defineSymbol(graph.config.symbols, "c")
  162. commandCompileToC(graph)
  163. of "cpp", "compiletocpp":
  164. conf.cmd = cmdCompileToCpp
  165. defineSymbol(graph.config.symbols, "cpp")
  166. commandCompileToC(graph)
  167. of "objc", "compiletooc":
  168. conf.cmd = cmdCompileToOC
  169. defineSymbol(graph.config.symbols, "objc")
  170. commandCompileToC(graph)
  171. of "run":
  172. conf.cmd = cmdRun
  173. when hasTinyCBackend:
  174. extccomp.setCC("tcc")
  175. commandCompileToC(graph)
  176. else:
  177. rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc")
  178. of "js", "compiletojs":
  179. when defined(leanCompiler):
  180. quit "compiler wasn't built with JS code generator"
  181. else:
  182. conf.cmd = cmdCompileToJS
  183. if conf.hcrOn:
  184. # XXX: At the moment, system.nim cannot be compiled in JS mode
  185. # with "-d:useNimRtl". The HCR option has been processed earlier
  186. # and it has added this define implictly, so we must undo that here.
  187. # A better solution might be to fix system.nim
  188. undefSymbol(conf.symbols, "useNimRtl")
  189. commandCompileToJS(graph)
  190. of "doc0":
  191. when defined(leanCompiler):
  192. quit "compiler wasn't built with documentation generator"
  193. else:
  194. wantMainModule(conf)
  195. conf.cmd = cmdDoc
  196. loadConfigs(DocConfig, cache, conf)
  197. commandDoc(cache, conf)
  198. of "doc2", "doc":
  199. when defined(leanCompiler):
  200. quit "compiler wasn't built with documentation generator"
  201. else:
  202. conf.cmd = cmdDoc
  203. loadConfigs(DocConfig, cache, conf)
  204. defineSymbol(conf.symbols, "nimdoc")
  205. commandDoc2(graph, false)
  206. of "rst2html":
  207. when defined(leanCompiler):
  208. quit "compiler wasn't built with documentation generator"
  209. else:
  210. conf.cmd = cmdRst2html
  211. loadConfigs(DocConfig, cache, conf)
  212. commandRst2Html(cache, conf)
  213. of "rst2tex":
  214. when defined(leanCompiler):
  215. quit "compiler wasn't built with documentation generator"
  216. else:
  217. conf.cmd = cmdRst2tex
  218. loadConfigs(DocTexConfig, cache, conf)
  219. commandRst2TeX(cache, conf)
  220. of "jsondoc0":
  221. when defined(leanCompiler):
  222. quit "compiler wasn't built with documentation generator"
  223. else:
  224. wantMainModule(conf)
  225. conf.cmd = cmdDoc
  226. loadConfigs(DocConfig, cache, conf)
  227. wantMainModule(conf)
  228. defineSymbol(conf.symbols, "nimdoc")
  229. commandJson(cache, conf)
  230. of "jsondoc2", "jsondoc":
  231. when defined(leanCompiler):
  232. quit "compiler wasn't built with documentation generator"
  233. else:
  234. conf.cmd = cmdDoc
  235. loadConfigs(DocConfig, cache, conf)
  236. wantMainModule(conf)
  237. defineSymbol(conf.symbols, "nimdoc")
  238. commandDoc2(graph, true)
  239. of "ctags":
  240. when defined(leanCompiler):
  241. quit "compiler wasn't built with documentation generator"
  242. else:
  243. wantMainModule(conf)
  244. conf.cmd = cmdDoc
  245. loadConfigs(DocConfig, cache, conf)
  246. defineSymbol(conf.symbols, "nimdoc")
  247. commandTags(cache, conf)
  248. of "buildindex":
  249. when defined(leanCompiler):
  250. quit "compiler wasn't built with documentation generator"
  251. else:
  252. conf.cmd = cmdDoc
  253. loadConfigs(DocConfig, cache, conf)
  254. commandBuildIndex(cache, conf)
  255. of "gendepend":
  256. conf.cmd = cmdGenDepend
  257. commandGenDepend(graph)
  258. of "dump":
  259. conf.cmd = cmdDump
  260. if getConfigVar(conf, "dump.format") == "json":
  261. wantMainModule(conf)
  262. var definedSymbols = newJArray()
  263. for s in definedSymbolNames(conf.symbols): definedSymbols.elems.add(%s)
  264. var libpaths = newJArray()
  265. for dir in conf.searchPaths: libpaths.elems.add(%dir.string)
  266. var hints = newJObject() # consider factoring with `listHints`
  267. for a in hintMin..hintMax:
  268. let key = lineinfos.HintsToStr[ord(a) - ord(hintMin)]
  269. hints[key] = %(a in conf.notes)
  270. var warnings = newJObject()
  271. for a in warnMin..warnMax:
  272. let key = lineinfos.WarningsToStr[ord(a) - ord(warnMin)]
  273. warnings[key] = %(a in conf.notes)
  274. var dumpdata = %[
  275. (key: "version", val: %VersionAsString),
  276. (key: "nimExe", val: %(getAppFilename())),
  277. (key: "prefixdir", val: %conf.getPrefixDir().string),
  278. (key: "project_path", val: %conf.projectFull.string),
  279. (key: "defined_symbols", val: definedSymbols),
  280. (key: "lib_paths", val: %libpaths),
  281. (key: "outdir", val: %conf.outDir.string),
  282. (key: "out", val: %conf.outFile.string),
  283. (key: "nimcache", val: %getNimcacheDir(conf).string),
  284. (key: "hints", val: hints),
  285. (key: "warnings", val: warnings),
  286. ]
  287. msgWriteln(conf, $dumpdata, {msgStdout, msgSkipHook})
  288. else:
  289. msgWriteln(conf, "-- list of currently defined symbols --",
  290. {msgStdout, msgSkipHook})
  291. for s in definedSymbolNames(conf.symbols): msgWriteln(conf, s, {msgStdout, msgSkipHook})
  292. msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook})
  293. for it in conf.searchPaths: msgWriteln(conf, it.string)
  294. of "check":
  295. conf.cmd = cmdCheck
  296. commandCheck(graph)
  297. of "parse":
  298. conf.cmd = cmdParse
  299. wantMainModule(conf)
  300. discard parseFile(conf.projectMainIdx, cache, conf)
  301. of "scan":
  302. conf.cmd = cmdScan
  303. wantMainModule(conf)
  304. commandScan(cache, conf)
  305. msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
  306. of "secret":
  307. conf.cmd = cmdInteractive
  308. commandInteractive(graph)
  309. of "e":
  310. if not fileExists(conf.projectFull):
  311. rawMessage(conf, errGenerated, "NimScript file does not exist: " & conf.projectFull.string)
  312. elif not conf.projectFull.string.endsWith(".nims"):
  313. rawMessage(conf, errGenerated, "not a NimScript file: " & conf.projectFull.string)
  314. # main NimScript logic handled in cmdlinehelper.nim.
  315. of "nop", "help":
  316. # prevent the "success" message:
  317. conf.cmd = cmdDump
  318. of "jsonscript":
  319. conf.cmd = cmdJsonScript
  320. commandJsonScript(graph)
  321. else:
  322. rawMessage(conf, errGenerated, "invalid command: " & conf.command)
  323. if conf.errorCounter == 0 and
  324. conf.cmd notin {cmdInterpret, cmdRun, cmdDump}:
  325. when declared(system.getMaxMem):
  326. let usedMem = formatSize(getMaxMem()) & " peakmem"
  327. else:
  328. let usedMem = formatSize(getTotalMem())
  329. rawMessage(conf, hintSuccessX, [$conf.linesCompiled,
  330. formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3),
  331. usedMem,
  332. if isDefined(conf, "danger"): "Dangerous Release Build"
  333. elif isDefined(conf, "release"): "Release Build"
  334. else: "Debug Build"])
  335. when PrintRopeCacheStats:
  336. echo "rope cache stats: "
  337. echo " tries : ", gCacheTries
  338. echo " misses: ", gCacheMisses
  339. echo " int tries: ", gCacheIntTries
  340. echo " efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float),
  341. ffDecimal, 3)
  342. resetAttributes(conf)