scriptconfig.nim 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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 new configuration system for Nim. Uses Nim as a scripting
  10. ## language.
  11. import
  12. ast, modules, idents, passes, condsyms,
  13. options, sem, llstream, vm, vmdef, commands, msgs,
  14. os, times, osproc, wordrecg, strtabs, modulegraphs,
  15. lineinfos, pathutils
  16. # we support 'cmpIgnoreStyle' natively for efficiency:
  17. from strutils import cmpIgnoreStyle, contains
  18. proc listDirs(a: VmArgs, filter: set[PathComponent]) =
  19. let dir = getString(a, 0)
  20. var result: seq[string] = @[]
  21. for kind, path in walkDir(dir):
  22. if kind in filter: result.add path
  23. setResult(a, result)
  24. proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
  25. graph: ModuleGraph): PEvalContext =
  26. # For Nimble we need to export 'setupVM'.
  27. result = newCtx(module, cache, graph)
  28. result.mode = emRepl
  29. registerAdditionalOps(result)
  30. let conf = graph.config
  31. # captured vars:
  32. var errorMsg: string
  33. var vthisDir = scriptName.splitFile.dir
  34. template cbconf(name, body) {.dirty.} =
  35. result.registerCallback "stdlib.system." & astToStr(name),
  36. proc (a: VmArgs) =
  37. body
  38. template cbexc(name, exc, body) {.dirty.} =
  39. result.registerCallback "stdlib.system." & astToStr(name),
  40. proc (a: VmArgs) =
  41. errorMsg = ""
  42. try:
  43. body
  44. except exc:
  45. errorMsg = getCurrentExceptionMsg()
  46. template cbos(name, body) {.dirty.} =
  47. cbexc(name, OSError, body)
  48. # Idea: Treat link to file as a file, but ignore link to directory to prevent
  49. # endless recursions out of the box.
  50. cbos listFiles:
  51. listDirs(a, {pcFile, pcLinkToFile})
  52. cbos listDirs:
  53. listDirs(a, {pcDir})
  54. cbos removeDir:
  55. os.removeDir getString(a, 0)
  56. cbos removeFile:
  57. os.removeFile getString(a, 0)
  58. cbos createDir:
  59. os.createDir getString(a, 0)
  60. result.registerCallback "stdlib.system.getError",
  61. proc (a: VmArgs) = setResult(a, errorMsg)
  62. cbos setCurrentDir:
  63. os.setCurrentDir getString(a, 0)
  64. cbos getCurrentDir:
  65. setResult(a, os.getCurrentDir())
  66. cbos moveFile:
  67. os.moveFile(getString(a, 0), getString(a, 1))
  68. cbos moveDir:
  69. os.moveDir(getString(a, 0), getString(a, 1))
  70. cbos copyFile:
  71. os.copyFile(getString(a, 0), getString(a, 1))
  72. cbos copyDir:
  73. os.copyDir(getString(a, 0), getString(a, 1))
  74. cbos getLastModificationTime:
  75. setResult(a, getLastModificationTime(getString(a, 0)).toUnix)
  76. cbos findExe:
  77. setResult(a, os.findExe(getString(a, 0)))
  78. cbos rawExec:
  79. setResult(a, osproc.execCmd getString(a, 0))
  80. cbconf getEnv:
  81. setResult(a, os.getEnv(a.getString 0, a.getString 1))
  82. cbconf existsEnv:
  83. setResult(a, os.existsEnv(a.getString 0))
  84. cbconf putEnv:
  85. os.putEnv(a.getString 0, a.getString 1)
  86. cbconf dirExists:
  87. setResult(a, os.dirExists(a.getString 0))
  88. cbconf fileExists:
  89. setResult(a, os.fileExists(a.getString 0))
  90. cbconf projectName:
  91. setResult(a, conf.projectName)
  92. cbconf projectDir:
  93. setResult(a, conf.projectPath.string)
  94. cbconf projectPath:
  95. setResult(a, conf.projectFull.string)
  96. cbconf thisDir:
  97. setResult(a, vthisDir)
  98. cbconf put:
  99. options.setConfigVar(conf, getString(a, 0), getString(a, 1))
  100. cbconf get:
  101. setResult(a, options.getConfigVar(conf, a.getString 0))
  102. cbconf exists:
  103. setResult(a, options.existsConfigVar(conf, a.getString 0))
  104. cbconf nimcacheDir:
  105. setResult(a, options.getNimcacheDir(conf).string)
  106. cbconf paramStr:
  107. setResult(a, os.paramStr(int a.getInt 0))
  108. cbconf paramCount:
  109. setResult(a, os.paramCount())
  110. cbconf cmpIgnoreStyle:
  111. setResult(a, strutils.cmpIgnoreStyle(a.getString 0, a.getString 1))
  112. cbconf cmpIgnoreCase:
  113. setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1))
  114. cbconf setCommand:
  115. conf.command = a.getString 0
  116. let arg = a.getString 1
  117. incl(conf.globalOptions, optWasNimscript)
  118. if arg.len > 0:
  119. conf.projectName = arg
  120. let path =
  121. if conf.projectName.isAbsolute: AbsoluteFile(conf.projectName)
  122. else: conf.projectPath / RelativeFile(conf.projectName)
  123. try:
  124. conf.projectFull = canonicalizePath(conf, path)
  125. except OSError:
  126. conf.projectFull = path
  127. cbconf getCommand:
  128. setResult(a, conf.command)
  129. cbconf switch:
  130. processSwitch(a.getString 0, a.getString 1, passPP, module.info, conf)
  131. cbconf hintImpl:
  132. processSpecificNote(a.getString 0, wHint, passPP, module.info,
  133. a.getString 1, conf)
  134. cbconf warningImpl:
  135. processSpecificNote(a.getString 0, wWarning, passPP, module.info,
  136. a.getString 1, conf)
  137. cbconf patchFile:
  138. let key = a.getString(0) & "_" & a.getString(1)
  139. var val = a.getString(2).addFileExt(NimExt)
  140. if {'$', '~'} in val:
  141. val = pathSubs(conf, val, vthisDir)
  142. elif not isAbsolute(val):
  143. val = vthisDir / val
  144. conf.moduleOverrides[key] = val
  145. cbconf selfExe:
  146. setResult(a, os.getAppFilename())
  147. cbconf cppDefine:
  148. options.cppDefine(conf, a.getString(0))
  149. cbexc stdinReadLine, EOFError:
  150. setResult(a, "")
  151. setResult(a, stdin.readLine())
  152. cbexc stdinReadAll, EOFError:
  153. setResult(a, "")
  154. setResult(a, stdin.readAll())
  155. proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
  156. freshDefines=true; conf: ConfigRef) =
  157. rawMessage(conf, hintConf, scriptName.string)
  158. let oldSymbolFiles = conf.symbolFiles
  159. conf.symbolFiles = disabledSf
  160. let graph = newModuleGraph(cache, conf)
  161. connectCallbacks(graph)
  162. if freshDefines: initDefines(conf.symbols)
  163. defineSymbol(conf.symbols, "nimscript")
  164. defineSymbol(conf.symbols, "nimconfig")
  165. registerPass(graph, semPass)
  166. registerPass(graph, evalPass)
  167. conf.searchPaths.add(conf.libpath)
  168. var m = graph.makeModule(scriptName)
  169. incl(m.flags, sfMainModule)
  170. graph.vm = setupVM(m, cache, scriptName.string, graph)
  171. graph.compileSystemModule() # TODO: see why this unsets hintConf in conf.notes
  172. discard graph.processModule(m, llStreamOpen(scriptName, fmRead))
  173. # ensure we load 'system.nim' again for the real non-config stuff!
  174. resetSystemArtifacts(graph)
  175. # do not remove the defined symbols
  176. #initDefines()
  177. undefSymbol(conf.symbols, "nimscript")
  178. undefSymbol(conf.symbols, "nimconfig")
  179. conf.symbolFiles = oldSymbolFiles