Implemented flags and arguments for NimScript's setCommand.

This commit is contained in:
Dominik Picheta 2015-12-25 16:32:46 +00:00
commit 84f371982b
6 changed files with 249 additions and 82 deletions

View file

@ -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):

View file

@ -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()

View file

@ -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()}

View file

@ -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") ==

View file

@ -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"

View file

@ -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":