summary refs log tree commit diff stats
path: root/compiler/scriptconfig.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/scriptconfig.nim')
-rw-r--r--compiler/scriptconfig.nim254
1 files changed, 254 insertions, 0 deletions
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
new file mode 100644
index 000000000..e3d2bcd45
--- /dev/null
+++ b/compiler/scriptconfig.nim
@@ -0,0 +1,254 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Implements the new configuration system for Nim. Uses Nim as a scripting
+## language.
+
+import
+  ast, modules, idents, condsyms,
+  options, llstream, vm, vmdef, commands,
+  wordrecg, modulegraphs,
+  pathutils, pipelines
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
+
+import std/[strtabs, os, times, osproc]
+
+# we support 'cmpIgnoreStyle' natively for efficiency:
+from std/strutils import cmpIgnoreStyle, contains
+
+proc listDirs(a: VmArgs, filter: set[PathComponent]) =
+  let dir = getString(a, 0)
+  var result: seq[string] = @[]
+  for kind, path in walkDir(dir):
+    if kind in filter: result.add path
+  setResult(a, result)
+
+proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
+              graph: ModuleGraph; idgen: IdGenerator): PEvalContext =
+  # For Nimble we need to export 'setupVM'.
+  result = newCtx(module, cache, graph, idgen)
+  result.mode = emRepl
+  registerAdditionalOps(result)
+  let conf = graph.config
+
+  # captured vars:
+  var errorMsg: string
+  var vthisDir = scriptName.splitFile.dir
+
+  template cbconf(name, body) {.dirty.} =
+    result.registerCallback "stdlib.system." & astToStr(name),
+      proc (a: VmArgs) =
+        body
+
+  template cbexc(name, exc, body) {.dirty.} =
+    result.registerCallback "stdlib.system." & astToStr(name),
+      proc (a: VmArgs) =
+        errorMsg = ""
+        try:
+          body
+        except exc:
+          errorMsg = getCurrentExceptionMsg()
+
+  template cbos(name, body) {.dirty.} =
+    cbexc(name, OSError, body)
+
+  # Idea: Treat link to file as a file, but ignore link to directory to prevent
+  # endless recursions out of the box.
+  cbos listFilesImpl:
+    listDirs(a, {pcFile, pcLinkToFile})
+  cbos listDirsImpl:
+    listDirs(a, {pcDir})
+  cbos removeDir:
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      discard
+    else:
+      os.removeDir(getString(a, 0), getBool(a, 1))
+  cbos removeFile:
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      discard
+    else:
+      os.removeFile getString(a, 0)
+  cbos createDir:
+    os.createDir getString(a, 0)
+
+  result.registerCallback "stdlib.system.getError",
+    proc (a: VmArgs) = setResult(a, errorMsg)
+
+  cbos setCurrentDir:
+    os.setCurrentDir getString(a, 0)
+  cbos getCurrentDir:
+    setResult(a, os.getCurrentDir())
+  cbos moveFile:
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      discard
+    else:
+      os.moveFile(getString(a, 0), getString(a, 1))
+  cbos moveDir:
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      discard
+    else:
+      os.moveDir(getString(a, 0), getString(a, 1))
+  cbos copyFile:
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      discard
+    else:
+      os.copyFile(getString(a, 0), getString(a, 1))
+  cbos copyDir:
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      discard
+    else:
+      os.copyDir(getString(a, 0), getString(a, 1))
+  cbos getLastModificationTime:
+    setResult(a, getLastModificationTime(getString(a, 0)).toUnix)
+  cbos findExe:
+    setResult(a, os.findExe(getString(a, 0)))
+
+  cbos rawExec:
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      discard
+    else:
+      setResult(a, osproc.execCmd getString(a, 0))
+
+  cbconf getEnv:
+    setResult(a, os.getEnv(a.getString 0, a.getString 1))
+  cbconf existsEnv:
+    setResult(a, os.existsEnv(a.getString 0))
+  cbconf putEnv:
+    os.putEnv(a.getString 0, a.getString 1)
+  cbconf delEnv:
+    os.delEnv(a.getString 0)
+  cbconf dirExists:
+    setResult(a, os.dirExists(a.getString 0))
+  cbconf fileExists:
+    setResult(a, os.fileExists(a.getString 0))
+
+  cbconf projectName:
+    setResult(a, conf.projectName)
+  cbconf projectDir:
+    setResult(a, conf.projectPath.string)
+  cbconf projectPath:
+    setResult(a, conf.projectFull.string)
+  cbconf thisDir:
+    setResult(a, vthisDir)
+  cbconf put:
+    options.setConfigVar(conf, getString(a, 0), getString(a, 1))
+  cbconf get:
+    setResult(a, options.getConfigVar(conf, a.getString 0))
+  cbconf exists:
+    setResult(a, options.existsConfigVar(conf, a.getString 0))
+  cbconf nimcacheDir:
+    setResult(a, options.getNimcacheDir(conf).string)
+  cbconf paramStr:
+    setResult(a, os.paramStr(int a.getInt 0))
+  cbconf paramCount:
+    setResult(a, os.paramCount())
+  cbconf cmpIgnoreStyle:
+    setResult(a, strutils.cmpIgnoreStyle(a.getString 0, a.getString 1))
+  cbconf cmpIgnoreCase:
+    setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1))
+  cbconf setCommand:
+    conf.setCommandEarly(a.getString 0)
+    let arg = a.getString 1
+    incl(conf.globalOptions, optWasNimscript)
+    if arg.len > 0: setFromProjectName(conf, arg)
+  cbconf getCommand:
+    setResult(a, conf.command)
+  cbconf switch:
+    conf.currentConfigDir = vthisDir
+    processSwitch(a.getString 0, a.getString 1, passPP, module.info, conf)
+  cbconf hintImpl:
+    processSpecificNote(a.getString 0, wHint, passPP, module.info,
+      a.getString 1, conf)
+  cbconf warningImpl:
+    processSpecificNote(a.getString 0, wWarning, passPP, module.info,
+      a.getString 1, conf)
+  cbconf patchFile:
+    let key = a.getString(0) & "_" & a.getString(1)
+    var val = a.getString(2).addFileExt(NimExt)
+    if {'$', '~'} in val:
+      val = pathSubs(conf, val, vthisDir)
+    elif not isAbsolute(val):
+      val = vthisDir / val
+    conf.moduleOverrides[key] = val
+  cbconf selfExe:
+    setResult(a, os.getAppFilename())
+  cbconf cppDefine:
+    options.cppDefine(conf, a.getString(0))
+  cbexc stdinReadLine, EOFError:
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      setResult(a, "")
+    else:
+      setResult(a, stdin.readLine())
+  cbexc stdinReadAll, EOFError:
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      setResult(a, "")
+    else:
+      setResult(a, stdin.readAll())
+
+proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
+                   idgen: IdGenerator;
+                   freshDefines=true; conf: ConfigRef, stream: PLLStream) =
+  let oldSymbolFiles = conf.symbolFiles
+  conf.symbolFiles = disabledSf
+
+  let graph = newModuleGraph(cache, conf)
+  connectPipelineCallbacks(graph)
+  if freshDefines: initDefines(conf.symbols)
+
+  defineSymbol(conf.symbols, "nimscript")
+  defineSymbol(conf.symbols, "nimconfig")
+
+  conf.searchPaths.add(conf.libpath)
+
+  let oldGlobalOptions = conf.globalOptions
+  let oldSelectedGC = conf.selectedGC
+  unregisterArcOrc(conf)
+  conf.globalOptions.excl optOwnedRefs
+  conf.selectedGC = gcUnselected
+
+  var m = graph.makeModule(scriptName)
+  incl(m.flags, sfMainModule)
+  var vm = setupVM(m, cache, scriptName.string, graph, idgen)
+  graph.vm = vm
+
+  graph.setPipeLinePass(EvalPass)
+  graph.compilePipelineSystemModule()
+  discard graph.processPipelineModule(m, vm.idgen, stream)
+
+  # watch out, "newruntime" can be set within NimScript itself and then we need
+  # to remember this:
+  if conf.selectedGC == gcUnselected:
+    conf.selectedGC = oldSelectedGC
+  if optOwnedRefs in oldGlobalOptions:
+    conf.globalOptions.incl {optTinyRtti, optOwnedRefs, optSeqDestructors}
+    defineSymbol(conf.symbols, "nimv2")
+  if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
+    conf.globalOptions.incl {optTinyRtti, optSeqDestructors}
+    defineSymbol(conf.symbols, "nimv2")
+    defineSymbol(conf.symbols, "gcdestructors")
+    defineSymbol(conf.symbols, "nimSeqsV2")
+    case conf.selectedGC
+    of gcArc:
+      defineSymbol(conf.symbols, "gcarc")
+    of gcOrc:
+      defineSymbol(conf.symbols, "gcorc")
+    of gcAtomicArc:
+      defineSymbol(conf.symbols, "gcatomicarc")
+    else:
+      raiseAssert "unreachable"
+
+  # ensure we load 'system.nim' again for the real non-config stuff!
+  resetSystemArtifacts(graph)
+  # do not remove the defined symbols
+  #initDefines()
+  undefSymbol(conf.symbols, "nimscript")
+  undefSymbol(conf.symbols, "nimconfig")
+  conf.symbolFiles = oldSymbolFiles