main.nim 12 KB

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