diff --git a/src/nimble.nim b/src/nimble.nim index db4f78c..19d669a 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -10,7 +10,7 @@ import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools, nimblepkg/download, nimblepkg/config, nimblepkg/nimbletypes, nimblepkg/publish -import compiler/options as compiler_options +import nimblepkg/nimscriptsupport when not defined(windows): from posix import getpid @@ -223,7 +223,7 @@ proc parseCmdLine(): Options = case kind of cmdArgument: if result.action.typ == actionNil: - compiler_options.command = key + setNimScriptCommand(key) result.action.typ = parseActionType(key) initAction(result, key) else: @@ -1017,14 +1017,9 @@ proc doAction(options: Options) = if not existsDir(options.getPkgsDir): createDir(options.getPkgsDir) - # The nimscript file may use `setCommand` to set the command so we need to - # re-read it from the compiler. - # TODO: It doesn't appear that this actually happens, can we just remove this? - var command = compiler_options.command.parseActionType() - var doLoop = true - while doLoop: - doLoop = false - let cmd = compiler_options.command.normalize + var command = getNimScriptCommand().parseActionType() + # The loop is necessary to support tasks using `setCommand`. + while true: case command of actionUpdate: update(options) @@ -1050,11 +1045,22 @@ proc doAction(options: Options) = publish(pkgInfo) of actionDump: dump(options) - of actionNil: discard "assuming nimscript 'nop' command here" + of actionNil: + assert false of actionCustom: - # calls Nimscript as a side effect :-/ - discard getPkgInfo(getCurrentDir()) - doLoop = cmd != compiler_options.command.normalize + # Custom command. Attempt to call a NimScript task. + let (isNimScript, nimbleFile) = findNimbleFile(getCurrentDir(), true) + if not isNimScript: + writeHelp() + let oldCmd = getNimScriptCommand() + if not execTask(nimbleFile, oldCmd): + echo("FAILURE: Could not find task ", oldCmd, " in ", nimbleFile) + writeHelp() + if getNimScriptCommand().normalize == "nop": + echo("WARNING: Using `setCommand 'nop'` is not necessary.") + break + command = getNimScriptCommand().parseActionType() + if not hasTaskRequestedCommand(): break when isMainModule: when defined(release): diff --git a/src/nimblepkg/nimscriptsupport.nim b/src/nimblepkg/nimscriptsupport.nim index 71e6c53..14710b9 100644 --- a/src/nimblepkg/nimscriptsupport.nim +++ b/src/nimblepkg/nimscriptsupport.nim @@ -11,10 +11,15 @@ import compiler/msgs, compiler/magicsys, compiler/lists from compiler/scriptconfig import setupVM +from compiler/idents import getIdent +from compiler/astalgo import strTableGet, `$` import nimbletypes, version import os, strutils +const + internalCmd = "NimbleInternal" + proc raiseVariableError(ident, typ: string) {.noinline.} = raise newException(NimbleError, "NimScript's variable '" & ident & "' needs a value of type '" & typ & "'.") @@ -53,7 +58,10 @@ proc extractRequires(result: var seq[PkgTuple]) = else: raiseVariableError("requiresData", "seq[(string, VersionReq)]") -proc readPackageInfoFromNims*(scriptName: string; result: var PackageInfo) = +proc execScript(scriptName: string) = + ## Executes the specified script. + ## + ## No clean up is performed and must be done manually! setDefaultLibpath() passes.gIncludeFile = includeModule passes.gImportModule = importModule @@ -74,6 +82,21 @@ proc readPackageInfoFromNims*(scriptName: string; result: var PackageInfo) = compileSystemModule() processModule(m, llStreamOpen(scriptName, fmRead), nil) +proc cleanup() = + # ensure everything can be called again: + resetAllModulesHard() + clearPasses() + vm.globalCtx = nil + initDefines() + +proc readPackageInfoFromNims*(scriptName: string; result: var PackageInfo) = + ## Executes the `scriptName` nimscript file. Reads the package information + ## that it populates. + + # Execute the nimscript file. + execScript(scriptName) + + # Extract all the necessary fields populated by the nimscript file. template trivialField(field) = result.field = getGlobal(astToStr field) @@ -111,7 +134,32 @@ proc readPackageInfoFromNims*(scriptName: string; result: var PackageInfo) = else: result.backend = backend.toLower() - # ensure everything can be called again: - resetAllModulesHard() - vm.globalCtx = nil - initDefines() + cleanup() + +proc execTask*(scriptName, taskName: string): bool = + ## Executes the specified task in the specified script. + ## + ## `scriptName` should be a filename pointing to the nimscript file. + result = true + options.command = internalCmd + echo("Executing task ", taskName, " in ", scriptName) + + execScript(scriptName) + # Explicitly execute the task procedure, instead of relying on hack. + assert vm.globalCtx.module.kind == skModule + let prc = vm.globalCtx.module.tab.strTableGet(getIdent(taskName & "Task")) + if prc.isNil: + # Procedure not defined in the NimScript module. + return false + discard vm.globalCtx.execProc(prc, []) + cleanup() + +proc getNimScriptCommand*(): string = + options.command + +proc setNimScriptCommand*(command: string) = + options.command = command + +proc hasTaskRequestedCommand*(): bool = + ## Determines whether the last executed task used ``setCommand`` + return getNimScriptCommand() != internalCmd \ No newline at end of file