Implemented flags and arguments for NimScript's setCommand.
This commit is contained in:
parent
5eaee85825
commit
84f371982b
6 changed files with 249 additions and 82 deletions
108
src/nimble.nim
108
src/nimble.nim
|
|
@ -813,59 +813,63 @@ proc doAction(options: Options) =
|
|||
if not existsDir(options.getPkgsDir):
|
||||
createDir(options.getPkgsDir)
|
||||
|
||||
var command = getNimScriptCommand().parseActionType()
|
||||
# The loop is necessary to support tasks using `setCommand`.
|
||||
var moreCommands = true
|
||||
while moreCommands:
|
||||
moreCommands = false
|
||||
case command
|
||||
of actionUpdate:
|
||||
update(options)
|
||||
of actionInstall:
|
||||
let (_, pkgInfo) = install(options.action.packages, options)
|
||||
if options.action.packages.len == 0:
|
||||
nimScriptHint(pkgInfo)
|
||||
of actionUninstall:
|
||||
uninstall(options)
|
||||
of actionSearch:
|
||||
search(options)
|
||||
of actionList:
|
||||
if options.queryInstalled: listInstalled(options)
|
||||
else: list(options)
|
||||
of actionPath:
|
||||
listPaths(options)
|
||||
of actionBuild:
|
||||
build(options)
|
||||
of actionCompile:
|
||||
compile(options)
|
||||
of actionInit:
|
||||
init(options)
|
||||
of actionPublish:
|
||||
var pkgInfo = getPkgInfo(getCurrentDir())
|
||||
publish(pkgInfo)
|
||||
of actionDump:
|
||||
dump(options)
|
||||
of actionTasks:
|
||||
listTasks(options)
|
||||
of actionVersion:
|
||||
writeVersion()
|
||||
of actionNil:
|
||||
assert false
|
||||
of actionCustom:
|
||||
# Custom command. Attempt to call a NimScript task.
|
||||
let nimbleFile = findNimbleFile(getCurrentDir(), true)
|
||||
let oldCmd = getNimScriptCommand()
|
||||
if not nimbleFile.isNimScript():
|
||||
writeHelp()
|
||||
case options.action.typ
|
||||
of actionUpdate:
|
||||
update(options)
|
||||
of actionInstall:
|
||||
let (_, pkgInfo) = install(options.action.packages, options)
|
||||
if options.action.packages.len == 0:
|
||||
nimScriptHint(pkgInfo)
|
||||
of actionUninstall:
|
||||
uninstall(options)
|
||||
of actionSearch:
|
||||
search(options)
|
||||
of actionList:
|
||||
if options.queryInstalled: listInstalled(options)
|
||||
else: list(options)
|
||||
of actionPath:
|
||||
listPaths(options)
|
||||
of actionBuild:
|
||||
build(options)
|
||||
of actionCompile:
|
||||
compile(options)
|
||||
of actionInit:
|
||||
init(options)
|
||||
of actionPublish:
|
||||
var pkgInfo = getPkgInfo(getCurrentDir())
|
||||
publish(pkgInfo)
|
||||
of actionDump:
|
||||
dump(options)
|
||||
of actionTasks:
|
||||
listTasks(options)
|
||||
of actionVersion:
|
||||
writeVersion()
|
||||
of actionNil:
|
||||
assert false
|
||||
of actionCustom:
|
||||
# Custom command. Attempt to call a NimScript task.
|
||||
let nimbleFile = findNimbleFile(getCurrentDir(), true)
|
||||
if not nimbleFile.isNimScript():
|
||||
writeHelp()
|
||||
|
||||
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()
|
||||
moreCommands = hasTaskRequestedCommand()
|
||||
let execResult = execTask(nimbleFile, options.action.command)
|
||||
if not execResult.success:
|
||||
echo("FAILURE: Could not find task ", options.action.command, " in ",
|
||||
nimbleFile)
|
||||
writeHelp()
|
||||
if execResult.command.normalize == "nop":
|
||||
echo("WARNING: Using `setCommand 'nop'` is not necessary.")
|
||||
return
|
||||
if execResult.hasTaskRequestedCommand():
|
||||
var newOptions = initOptions()
|
||||
newOptions.config = options.config
|
||||
newOptions.nimbleData = options.nimbleData
|
||||
parseCommand(execResult.command, newOptions)
|
||||
for arg in execResult.arguments:
|
||||
parseArgument(arg, newOptions)
|
||||
for flag, val in execResult.flags:
|
||||
parseFlag(flag, val, newOptions)
|
||||
doAction(newOptions)
|
||||
|
||||
when isMainModule:
|
||||
when defined(release):
|
||||
|
|
|
|||
|
|
@ -15,7 +15,14 @@ from compiler/idents import getIdent
|
|||
from compiler/astalgo import strTableGet
|
||||
|
||||
import nimbletypes, version
|
||||
import os, strutils
|
||||
import os, strutils, strtabs, times, osproc
|
||||
|
||||
type
|
||||
ExecutionResult* = object
|
||||
success*: bool
|
||||
command*: string
|
||||
arguments*: seq[string]
|
||||
flags*: StringTableRef
|
||||
|
||||
const
|
||||
internalCmd = "NimbleInternal"
|
||||
|
|
@ -58,7 +65,112 @@ proc extractRequires(result: var seq[PkgTuple]) =
|
|||
else:
|
||||
raiseVariableError("requiresData", "seq[(string, VersionReq)]")
|
||||
|
||||
proc execScript(scriptName: string) =
|
||||
proc setupVM(module: PSym; scriptName: string,
|
||||
flags: StringTableRef): PEvalContext =
|
||||
## This procedure is exported in the compiler sources, but its implementation
|
||||
## is too Nim-specific to be used by Nimble.
|
||||
## Specifically, the implementation of ``switch`` is problematic. Sooo
|
||||
## I simply copied it here and edited it :)
|
||||
|
||||
result = newCtx(module)
|
||||
result.mode = emRepl
|
||||
registerAdditionalOps(result)
|
||||
|
||||
# captured vars:
|
||||
var errorMsg: string
|
||||
var vthisDir = scriptName.splitFile.dir
|
||||
|
||||
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)
|
||||
|
||||
template cbconf(name, body) {.dirty.} =
|
||||
result.registerCallback "stdlib.system." & astToStr(name),
|
||||
proc (a: VmArgs) =
|
||||
body
|
||||
|
||||
template cbos(name, body) {.dirty.} =
|
||||
result.registerCallback "stdlib.system." & astToStr(name),
|
||||
proc (a: VmArgs) =
|
||||
try:
|
||||
body
|
||||
except OSError:
|
||||
errorMsg = getCurrentExceptionMsg()
|
||||
|
||||
# Idea: Treat link to file as a file, but ignore link to directory to prevent
|
||||
# endless recursions out of the box.
|
||||
cbos listFiles:
|
||||
listDirs(a, {pcFile, pcLinkToFile})
|
||||
cbos listDirs:
|
||||
listDirs(a, {pcDir})
|
||||
cbos removeDir:
|
||||
os.removeDir getString(a, 0)
|
||||
cbos removeFile:
|
||||
os.removeFile getString(a, 0)
|
||||
cbos createDir:
|
||||
os.createDir getString(a, 0)
|
||||
cbos getOsError:
|
||||
setResult(a, errorMsg)
|
||||
cbos setCurrentDir:
|
||||
os.setCurrentDir getString(a, 0)
|
||||
cbos getCurrentDir:
|
||||
setResult(a, os.getCurrentDir())
|
||||
cbos moveFile:
|
||||
os.moveFile(getString(a, 0), getString(a, 1))
|
||||
cbos copyFile:
|
||||
os.copyFile(getString(a, 0), getString(a, 1))
|
||||
cbos getLastModificationTime:
|
||||
setResult(a, toSeconds(getLastModificationTime(getString(a, 0))))
|
||||
|
||||
cbos rawExec:
|
||||
setResult(a, osproc.execCmd getString(a, 0))
|
||||
|
||||
cbconf getEnv:
|
||||
setResult(a, os.getEnv(a.getString 0))
|
||||
cbconf existsEnv:
|
||||
setResult(a, os.existsEnv(a.getString 0))
|
||||
cbconf dirExists:
|
||||
setResult(a, os.dirExists(a.getString 0))
|
||||
cbconf fileExists:
|
||||
setResult(a, os.fileExists(a.getString 0))
|
||||
|
||||
cbconf thisDir:
|
||||
setResult(a, vthisDir)
|
||||
cbconf put:
|
||||
options.setConfigVar(getString(a, 0), getString(a, 1))
|
||||
cbconf get:
|
||||
setResult(a, options.getConfigVar(a.getString 0))
|
||||
cbconf exists:
|
||||
setResult(a, options.existsConfigVar(a.getString 0))
|
||||
cbconf nimcacheDir:
|
||||
setResult(a, options.getNimcacheDir())
|
||||
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:
|
||||
options.command = a.getString 0
|
||||
let arg = a.getString 1
|
||||
if arg.len > 0:
|
||||
gProjectName = arg
|
||||
try:
|
||||
gProjectFull = canonicalizePath(gProjectPath / gProjectName)
|
||||
except OSError:
|
||||
gProjectFull = gProjectName
|
||||
cbconf getCommand:
|
||||
setResult(a, options.command)
|
||||
cbconf switch:
|
||||
if not flags.isNil:
|
||||
flags[a.getString 0] = a.getString 1
|
||||
|
||||
proc execScript(scriptName: string, flags: StringTableRef) =
|
||||
## Executes the specified script.
|
||||
##
|
||||
## No clean up is performed and must be done manually!
|
||||
|
|
@ -77,13 +189,15 @@ proc execScript(scriptName: string) =
|
|||
|
||||
var m = makeModule(scriptName)
|
||||
incl(m.flags, sfMainModule)
|
||||
vm.globalCtx = setupVM(m, scriptName)
|
||||
vm.globalCtx = setupVM(m, scriptName, flags)
|
||||
|
||||
compileSystemModule()
|
||||
processModule(m, llStreamOpen(scriptName, fmRead), nil)
|
||||
|
||||
proc cleanup() =
|
||||
# ensure everything can be called again:
|
||||
options.gProjectName = ""
|
||||
options.command = ""
|
||||
resetAllModulesHard()
|
||||
clearPasses()
|
||||
msgs.gErrorMax = 1
|
||||
|
|
@ -106,7 +220,7 @@ proc readPackageInfoFromNims*(scriptName: string; result: var PackageInfo) =
|
|||
previousMsg = output
|
||||
|
||||
# Execute the nimscript file.
|
||||
execScript(scriptName)
|
||||
execScript(scriptName, nil)
|
||||
|
||||
# Extract all the necessary fields populated by the nimscript file.
|
||||
template trivialField(field) =
|
||||
|
|
@ -148,38 +262,47 @@ proc readPackageInfoFromNims*(scriptName: string; result: var PackageInfo) =
|
|||
|
||||
cleanup()
|
||||
|
||||
proc execTask*(scriptName, taskName: string): bool =
|
||||
proc execTask*(scriptName, taskName: string): ExecutionResult =
|
||||
## Executes the specified task in the specified script.
|
||||
##
|
||||
## `scriptName` should be a filename pointing to the nimscript file.
|
||||
result = true
|
||||
result.success = true
|
||||
result.flags = newStringTable()
|
||||
options.command = internalCmd
|
||||
echo("Executing task ", taskName, " in ", scriptName)
|
||||
|
||||
execScript(scriptName)
|
||||
execScript(scriptName, result.flags)
|
||||
# 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
|
||||
result.success = false
|
||||
return
|
||||
discard vm.globalCtx.execProc(prc, [])
|
||||
|
||||
# Read the command, arguments and flags set by the executed task.
|
||||
result.command = options.command
|
||||
result.arguments = @[]
|
||||
for arg in options.gProjectName.split():
|
||||
result.arguments.add(arg)
|
||||
|
||||
cleanup()
|
||||
|
||||
proc getNimScriptCommand*(): string =
|
||||
proc getNimScriptCommand(): string =
|
||||
options.command
|
||||
|
||||
proc setNimScriptCommand*(command: string) =
|
||||
proc setNimScriptCommand(command: string) =
|
||||
options.command = command
|
||||
|
||||
proc hasTaskRequestedCommand*(): bool =
|
||||
proc hasTaskRequestedCommand*(execResult: ExecutionResult): bool =
|
||||
## Determines whether the last executed task used ``setCommand``
|
||||
return getNimScriptCommand() != internalCmd
|
||||
return execResult.command != internalCmd
|
||||
|
||||
proc listTasks*(scriptName: string) =
|
||||
setNimScriptCommand("help")
|
||||
|
||||
execScript(scriptName)
|
||||
execScript(scriptName, nil)
|
||||
# TODO: Make the 'task' template generate explicit data structure containing
|
||||
# all the task names + descriptions.
|
||||
cleanup()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (C) Dominik Picheta. All rights reserved.
|
||||
# BSD License. Look at license.txt for more info.
|
||||
|
||||
import json, strutils, os, parseopt
|
||||
import json, strutils, os, parseopt, strtabs
|
||||
|
||||
import nimblepkg/config, nimblepkg/version, nimblepkg/nimscriptsupport,
|
||||
nimblepkg/tools
|
||||
|
|
@ -39,7 +39,10 @@ type
|
|||
file*: string
|
||||
backend*: string
|
||||
compileOptions*: seq[string]
|
||||
else: nil
|
||||
of actionCustom:
|
||||
command*: string
|
||||
arguments*: seq[string]
|
||||
flags*: StringTableRef
|
||||
|
||||
ForcePrompt* = enum
|
||||
dontForcePrompt, forcePromptYes, forcePromptNo
|
||||
|
|
@ -144,7 +147,11 @@ proc initAction*(options: var Options, key: string) =
|
|||
options.action.search = @[]
|
||||
of actionUninstall:
|
||||
options.action.packages = @[]
|
||||
of actionBuild, actionPublish, actionCustom, actionList, actionTasks,
|
||||
of actionCustom:
|
||||
options.action.command = key
|
||||
options.action.arguments = @[]
|
||||
options.action.flags = newStringTable()
|
||||
of actionBuild, actionPublish, actionList, actionTasks,
|
||||
actionNil, actionVersion: discard
|
||||
|
||||
proc prompt*(options: Options, question: string): bool =
|
||||
|
|
@ -192,7 +199,6 @@ proc getBinDir*(options: Options): string =
|
|||
options.config.nimbleDir / "bin"
|
||||
|
||||
proc parseCommand*(key: string, result: var Options) =
|
||||
setNimScriptCommand(key)
|
||||
result.action.typ = parseActionType(key)
|
||||
initAction(result, key)
|
||||
|
||||
|
|
@ -222,6 +228,8 @@ proc parseArgument*(key: string, result: var Options) =
|
|||
result.action.file = key
|
||||
of actionList, actionBuild, actionPublish:
|
||||
writeHelp()
|
||||
of actionCustom:
|
||||
result.action.arguments.add(key)
|
||||
else:
|
||||
discard
|
||||
|
||||
|
|
@ -232,7 +240,10 @@ proc parseFlag*(flag, val: string, result: var Options) =
|
|||
result.action.compileOptions.add("--" & flag)
|
||||
else:
|
||||
result.action.compileOptions.add("--" & flag & ":" & val)
|
||||
of actionCustom:
|
||||
result.action.flags[flag] = val
|
||||
else:
|
||||
# TODO: These should not be global.
|
||||
case flag.normalize()
|
||||
of "help", "h": writeHelp()
|
||||
of "version", "v":
|
||||
|
|
@ -246,9 +257,26 @@ proc parseFlag*(flag, val: string, result: var Options) =
|
|||
else:
|
||||
raise newException(NimbleError, "Unknown option: --" & flag)
|
||||
|
||||
proc parseCmdLine*(): Options =
|
||||
proc initOptions*(): Options =
|
||||
result.action.typ = actionNil
|
||||
|
||||
proc parseMisc(): Options =
|
||||
result = initOptions()
|
||||
result.config = parseConfig()
|
||||
|
||||
# Load nimbledata.json
|
||||
let nimbledataFilename = result.getNimbleDir() / "nimbledata.json"
|
||||
if fileExists(nimbledataFilename):
|
||||
try:
|
||||
result.nimbleData = parseFile(nimbledataFilename)
|
||||
except:
|
||||
raise newException(NimbleError, "Couldn't parse nimbledata.json file " &
|
||||
"located at " & nimbledataFilename)
|
||||
else:
|
||||
result.nimbleData = %{"reverseDeps": newJObject()}
|
||||
|
||||
proc parseCmdLine*(): Options =
|
||||
result = parseMisc()
|
||||
for kind, key, val in getOpt():
|
||||
case kind
|
||||
of cmdArgument:
|
||||
|
|
@ -267,13 +295,3 @@ proc parseCmdLine*(): Options =
|
|||
# Rename deprecated babel dir.
|
||||
renameBabelToNimble(result)
|
||||
|
||||
# Load nimbledata.json
|
||||
let nimbledataFilename = result.getNimbleDir() / "nimbledata.json"
|
||||
if fileExists(nimbledataFilename):
|
||||
try:
|
||||
result.nimbleData = parseFile(nimbledataFilename)
|
||||
except:
|
||||
raise newException(NimbleError, "Couldn't parse nimbledata.json file " &
|
||||
"located at " & nimbledataFilename)
|
||||
else:
|
||||
result.nimbleData = %{"reverseDeps": newJObject()}
|
||||
|
|
|
|||
|
|
@ -470,7 +470,7 @@ proc getDownloadDirName*(pkg: Package, verRange: VersionRange): string =
|
|||
result.add verSimple
|
||||
|
||||
proc isNimScript*(nf: NimbleFile): bool =
|
||||
readPackageInfo(nf).isNimScript
|
||||
result = readPackageInfo(nf).isNimScript
|
||||
|
||||
when isMainModule:
|
||||
doAssert getNameVersion("/home/user/.nimble/libs/packagea-0.1") ==
|
||||
|
|
|
|||
|
|
@ -13,3 +13,10 @@ requires "nim >= 0.12.1"
|
|||
|
||||
task test, "test description":
|
||||
echo(5+5)
|
||||
|
||||
task c_test, "Testing `setCommand \"c\", \"nimscript.nim\"`":
|
||||
setCommand "c", "nimscript.nim"
|
||||
|
||||
task cr, "Testing `nimble c -r nimscript.nim` via setCommand":
|
||||
--r
|
||||
setCommand "c", "nimscript.nim"
|
||||
|
|
@ -29,10 +29,25 @@ test "can execute nimscript tasks":
|
|||
check exitCode == QuitSuccess
|
||||
check lines[^1] == "10"
|
||||
|
||||
test "can use nimscript's setCommand":
|
||||
cd "nimscript":
|
||||
let (output, exitCode) = execCmdEx("../" & path & " cTest")
|
||||
let lines = output.strip.splitLines()
|
||||
check exitCode == QuitSuccess
|
||||
check "Hint: operation successful".normalize in lines[^1].normalize
|
||||
|
||||
test "can use nimscript's setCommand with flags":
|
||||
cd "nimscript":
|
||||
let (output, exitCode) = execCmdEx("../" & path & " cr")
|
||||
let lines = output.strip.splitLines()
|
||||
check exitCode == QuitSuccess
|
||||
check "Hint: operation successful".normalize in lines[^2].normalize
|
||||
check "Hello World".normalize in lines[^1].normalize
|
||||
|
||||
test "can list nimscript tasks":
|
||||
cd "nimscript":
|
||||
let (output, exitCode) = execCmdEx("../" & path & " tasks")
|
||||
check output.strip == "test test description"
|
||||
check "test test description".normalize in output.normalize
|
||||
check exitCode == QuitSuccess
|
||||
|
||||
test "can install packagebin2":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue