diff --git a/nimble.nimble b/nimble.nimble index 3170eb2..a7b6e75 100644 --- a/nimble.nimble +++ b/nimble.nimble @@ -12,6 +12,13 @@ srcDir = "src" requires "nim >= 0.11.2" +before tasks: + echo("About to list tasks!") + return true + +after tasks: + echo("Listed tasks!") + task tests, "Run the Nimble tester!": withDir "tests": exec "nim c -r tester" \ No newline at end of file diff --git a/src/nimble.nim b/src/nimble.nim index 97e3027..e501b05 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -856,12 +856,23 @@ proc listTasks(options: Options) = let nimbleFile = findNimbleFile(getCurrentDir(), true) nimscriptsupport.listTasks(nimbleFile, options) +proc execHook(options: Options, before: bool): bool = + ## Returns whether to continue. + result = true + let nimbleFile = findNimbleFile(getCurrentDir(), true) + # TODO: Optimise this, there are two (three?) calls to readPackageInfo. + if nimbleFile.isNimScript(options): + let actionName = ($options.action.typ)[6 .. ^1] + let res = execHook(nimbleFile, actionName, before, options) + result = res.retVal + proc doAction(options: Options) = if not existsDir(options.getNimbleDir()): createDir(options.getNimbleDir()) if not existsDir(options.getPkgsDir): createDir(options.getPkgsDir) + if not execHook(options, true): return case options.action.typ of actionUpdate: update(options) @@ -898,7 +909,6 @@ proc doAction(options: Options) = of actionCustom: # Custom command. Attempt to call a NimScript task. let nimbleFile = findNimbleFile(getCurrentDir(), true) - ## TODO: Optimise this, there are two calls to readPackageInfo here. if not nimbleFile.isNimScript(options): writeHelp() @@ -910,6 +920,8 @@ proc doAction(options: Options) = if execResult.command.normalize == "nop": echo("WARNING: Using `setCommand 'nop'` is not necessary.") return + if not execHook(options, false): + return if execResult.hasTaskRequestedCommand(): var newOptions = initOptions() newOptions.config = options.config @@ -921,6 +933,9 @@ proc doAction(options: Options) = parseFlag(flag, val, newOptions) doAction(newOptions) + if options.action.typ != actionCustom: + discard execHook(options, false) + when isMainModule: when defined(release): try: diff --git a/src/nimblepkg/nimscriptapi.nim b/src/nimblepkg/nimscriptapi.nim index c996e1a..af5171d 100644 --- a/src/nimblepkg/nimscriptapi.nim +++ b/src/nimblepkg/nimscriptapi.nim @@ -24,6 +24,17 @@ proc requires*(deps: varargs[string]) = ## package. for d in deps: requiresData.add(d) +template before*(action: untyped, body: untyped): untyped = + ## Defines a block of code which is evaluated before ``action`` is executed. + proc `action Before`*(): bool = + result = false + body + +template after*(action: untyped, body: untyped): untyped = + ## Defines a block of code which is evaluated after ``action`` is executed. + proc `action After`*(): bool = + body + template builtin = discard proc getPkgDir*(): string = diff --git a/src/nimblepkg/nimscriptsupport.nim b/src/nimblepkg/nimscriptsupport.nim index b74da46..4622c62 100644 --- a/src/nimblepkg/nimscriptsupport.nim +++ b/src/nimblepkg/nimscriptsupport.nim @@ -12,18 +12,21 @@ import from compiler/scriptconfig import setupVM from compiler/idents import getIdent -from compiler/astalgo import strTableGet +from compiler/astalgo import strTableGet, `$` import compiler/options as compiler_options +import compiler/renderer + import nimbletypes, version, options, packageinfo import os, strutils, strtabs, times, osproc type - ExecutionResult* = object + ExecutionResult*[T] = object success*: bool command*: string arguments*: seq[string] flags*: StringTableRef + retVal*: T const internalCmd = "NimbleInternal" @@ -174,18 +177,20 @@ proc setupVM(module: PSym; scriptName: string, proc findNimscriptApi(options: Options): string = ## Returns the directory containing ``nimscriptapi.nim`` var inPath = false - let pkgs = getInstalledPkgsMin(options.getPkgsDir(), options) - var pkg: PackageInfo - if pkgs.findPkg(("nimble", newVRAny()), pkg): - let pkgDir = pkg.getRealDir() - if fileExists(pkgDir / "nimblepkg" / "nimscriptapi.nim"): - result = pkgDir - inPath = true + # Try finding it in exe's path + if fileExists(getAppDir() / "nimblepkg" / "nimscriptapi.nim"): + result = getAppDir() + inPath = true + if not inPath: - # Try finding it in exe's path - if fileExists(getAppDir() / "nimblepkg" / "nimscriptapi.nim"): - result = getAppDir() - inPath = true + let pkgs = getInstalledPkgsMin(options.getPkgsDir(), options) + var pkg: PackageInfo + if pkgs.findPkg(("nimble", newVRAny()), pkg): + let pkgDir = pkg.getRealDir() + if fileExists(pkgDir / "nimblepkg" / "nimscriptapi.nim"): + result = pkgDir + inPath = true + if not inPath: raise newException(NimbleError, "Cannot find nimscriptapi.nim") @@ -196,6 +201,8 @@ proc execScript(scriptName: string, flags: StringTableRef, options: Options) = if "nimblepkg/nimscriptapi" notin compiler_options.implicitIncludes: compiler_options.implicitIncludes.add("nimblepkg/nimscriptapi") + let pkgName = scriptName.splitFile.name + # Ensure that "nimblepkg/nimscriptapi" is in the PATH. let nimscriptApiPath = findNimscriptApi(options) appendStr(searchPaths, nimscriptApiPath) @@ -219,10 +226,10 @@ proc execScript(scriptName: string, flags: StringTableRef, options: Options) = # Setup builtins defined in nimscriptapi.nim template cbApi(name, body) {.dirty.} = - vm.globalCtx.registerCallback "stdlib.system." & astToStr(name), + vm.globalCtx.registerCallback pkgName & "." & astToStr(name), proc (a: VmArgs) = body - # TODO: This doesn't work. + cbApi getPkgDir: setResult(a, "FOOBAR") @@ -318,7 +325,7 @@ proc readPackageInfoFromNims*(scriptName: string, options: Options, cleanup() proc execTask*(scriptName, taskName: string, - options: Options): ExecutionResult = + options: Options): ExecutionResult[void] = ## Executes the specified task in the specified script. ## ## `scriptName` should be a filename pointing to the nimscript file. @@ -347,6 +354,44 @@ proc execTask*(scriptName, taskName: string, cleanup() +proc execHook*(scriptName, actionName: string, before: bool, + options: Options): ExecutionResult[bool] = + ## Executes the specified action's hook. Depending on ``before``, either + ## the "before" or the "after" hook. + ## + ## `scriptName` should be a filename pointing to the nimscript file. + result.success = true + result.flags = newStringTable() + compiler_options.command = internalCmd + let hookName = + if before: actionName.toLower & "Before" + else: actionName.toLower & "After" + echo("Attempting to execute hook ", hookName, " in ", scriptName) + + execScript(scriptName, result.flags, options) + # Explicitly execute the task procedure, instead of relying on hack. + let idx = fileInfoIdx(scriptName) + let thisModule = getModule(idx) + assert thisModule.kind == skModule + let prc = thisModule.tab.strTableGet(getIdent(hookName)) + if prc.isNil: + # Procedure not defined in the NimScript module. + result.success = false + return + let returnVal = vm.globalCtx.execProc(prc, []) + case returnVal.kind + of nkCharLit..nkUInt64Lit: + result.retVal = returnVal.intVal == 1 + else: assert false + + # Read the command, arguments and flags set by the executed task. + result.command = compiler_options.command + result.arguments = @[] + for arg in compiler_options.gProjectName.split(): + result.arguments.add(arg) + + cleanup() + proc getNimScriptCommand(): string = compiler_options.command diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim index a7950c0..3af5817 100644 --- a/src/nimblepkg/options.nim +++ b/src/nimblepkg/options.nim @@ -29,8 +29,6 @@ type of actionUpdate: optionalURL*: string # Overrides default package list. of actionInstall, actionPath, actionUninstall: - optionalName*: seq[string] # \ - # When this is @[], installs package from current dir. packages*: seq[PkgTuple] # Optional only for actionInstall. of actionSearch: search*: seq[string] # Search string.