main.nim 13 KB

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