nimble/src/nimblepkg/nimscriptwrapper.nim
Ivan Bobev bb1dd21224 Add an unit test for compilation without warnings
An unit test which checks whether Nimble compiles without warnings is
added. Checking for three warning types cleaned in previous commits is
implemented:

 - [UnusedImport] cleaned in e8c7d5c

 - [Deprecated] cleaned in 3d6172e

 - [XDeclaredButNotUsed] cleaned in 7df2ef3

Other types of warnings easily can be added to the test by extending the
warnings list.

Related to #680
2019-09-07 12:08:15 +01:00

181 lines
5.9 KiB
Nim

# Copyright (C) Andreas Rumpf. All rights reserved.
# BSD License. Look at license.txt for more info.
## Implements the new configuration system for Nimble. Uses Nim as a
## scripting language.
import version, options, cli, tools
import hashes, json, os, strutils, tables, times, osproc, strtabs
type
Flags = TableRef[string, seq[string]]
ExecutionResult*[T] = object
success*: bool
command*: string
arguments*: seq[string]
flags*: Flags
retVal*: T
const
internalCmd = "e"
nimscriptApi = staticRead("nimscriptapi.nim")
proc execNimscript(nimsFile, projectDir, actionName: string, options: Options):
tuple[output: string, exitCode: int] =
let
nimsFileCopied = projectDir / nimsFile.splitFile().name & "_" & getProcessId() & ".nims"
outFile = getNimbleTempDir() & ".out"
let
isScriptResultCopied =
nimsFileCopied.fileExists() and
nimsFileCopied.getLastModificationTime() >= nimsFile.getLastModificationTime()
if not isScriptResultCopied:
nimsFile.copyFile(nimsFileCopied)
defer:
# Only if copied in this invocation, allows recursive calls of nimble
if not isScriptResultCopied:
nimsFileCopied.removeFile()
var
cmd = ("nim e --hints:off --verbosity:0 -p:" & (getTempDir() / "nimblecache").quoteShell &
" " & nimsFileCopied.quoteShell & " " & outFile.quoteShell & " " & actionName).strip()
if options.action.typ == actionCustom and actionName != "printPkgInfo":
for i in options.action.arguments:
cmd &= " " & i.quoteShell()
for key, val in options.action.flags.pairs():
cmd &= " $#$#" % [if key.len == 1: "-" else: "--", key]
if val.len != 0:
cmd &= ":" & val.quoteShell()
displayDebug("Executing " & cmd)
result.exitCode = execCmd(cmd)
if outFile.fileExists():
result.output = outFile.readFile()
discard outFile.tryRemoveFile()
proc getNimsFile(scriptName: string, options: Options): string =
let
cacheDir = getTempDir() / "nimblecache"
shash = $scriptName.parentDir().hash().abs()
prjCacheDir = cacheDir / scriptName.splitFile().name & "_" & shash
nimscriptApiFile = cacheDir / "nimscriptapi.nim"
result = prjCacheDir / scriptName.extractFilename().changeFileExt ".nims"
let
iniFile = result.changeFileExt(".ini")
isNimscriptApiCached =
nimscriptApiFile.fileExists() and nimscriptApiFile.getLastModificationTime() >
getAppFilename().getLastModificationTime()
isScriptResultCached =
isNimscriptApiCached and result.fileExists() and result.getLastModificationTime() >
scriptName.getLastModificationTime()
if not isNimscriptApiCached:
createDir(cacheDir)
writeFile(nimscriptApiFile, nimscriptApi)
if not isScriptResultCached:
createDir(result.parentDir())
writeFile(result, """
import system except getCommand, setCommand, switch, `--`,
packageName, version, author, description, license, srcDir, binDir, backend,
skipDirs, skipFiles, skipExt, installDirs, installFiles, installExt, bin, foreignDeps,
requires, task, packageName
""" &
"import nimscriptapi, strutils\n" & scriptName.readFile() & "\nonExit()\n")
discard tryRemoveFile(iniFile)
proc getIniFile*(scriptName: string, options: Options): string =
let
nimsFile = getNimsFile(scriptName, options)
result = nimsFile.changeFileExt(".ini")
let
isIniResultCached =
result.fileExists() and result.getLastModificationTime() >
scriptName.getLastModificationTime()
if not isIniResultCached:
let
(output, exitCode) =
execNimscript(nimsFile, scriptName.parentDir(), "printPkgInfo", options)
if exitCode == 0 and output.len != 0:
result.writeFile(output)
else:
raise newException(NimbleError, output & "\nprintPkgInfo() failed")
proc execScript(scriptName, actionName: string, options: Options):
ExecutionResult[bool] =
let
nimsFile = getNimsFile(scriptName, options)
let
(output, exitCode) = execNimscript(nimsFile, scriptName.parentDir(), actionName, options)
if exitCode != 0:
let errMsg =
if output.len != 0:
output
else:
"Exception raised during nimble script execution"
raise newException(NimbleError, errMsg)
let
j =
if output.len != 0:
parseJson(output)
else:
parseJson("{}")
result.flags = newTable[string, seq[string]]()
result.success = j{"success"}.getBool()
result.command = j{"command"}.getStr()
if "project" in j:
result.arguments.add j["project"].getStr()
if "flags" in j:
for flag, vals in j["flags"].pairs:
result.flags[flag] = @[]
for val in vals.items():
result.flags[flag].add val.getStr()
result.retVal = j{"retVal"}.getBool()
proc execTask*(scriptName, taskName: string,
options: Options): ExecutionResult[bool] =
## Executes the specified task in the specified script.
##
## `scriptName` should be a filename pointing to the nimscript file.
display("Executing", "task $# in $#" % [taskName, scriptName],
priority = HighPriority)
result = execScript(scriptName, taskName, options)
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.
let hookName =
if before: actionName.toLowerAscii & "Before"
else: actionName.toLowerAscii & "After"
display("Attempting", "to execute hook $# in $#" % [hookName, scriptName],
priority = MediumPriority)
result = execScript(scriptName, hookName, options)
proc hasTaskRequestedCommand*(execResult: ExecutionResult): bool =
## Determines whether the last executed task used ``setCommand``
return execResult.command != internalCmd
proc listTasks*(scriptName: string, options: Options) =
discard execScript(scriptName, "", options)