Skip to content

Commit

Permalink
Initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
genotrance committed Apr 13, 2019
1 parent b9ef88b commit ab38b81
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 29 deletions.
4 changes: 2 additions & 2 deletions src/nimble.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools,
nimblepkg/cli, nimblepkg/packageinstaller, nimblepkg/reversedeps,
nimblepkg/nimscriptexecutor, nimblepkg/init

import nimblepkg/nimscriptsupport
import nimblepkg/nimscriptwrapper

proc refresh(options: Options) =
## Downloads the package list from the specified URL.
Expand Down Expand Up @@ -896,7 +896,7 @@ proc uninstall(options: Options) =

proc listTasks(options: Options) =
let nimbleFile = findNimbleFile(getCurrentDir(), true)
nimscriptsupport.listTasks(nimbleFile, options)
nimscriptwrapper.listTasks(nimbleFile, options)

proc developFromDir(dir: string, options: Options) =
if options.depsOnly:
Expand Down
116 changes: 95 additions & 21 deletions src/nimblepkg/nimscriptapi.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

## This module is implicitly imported in NimScript .nimble files.

import system except getCommand, setCommand
import strformat, strutils

var
packageName* = "" ## Set this to the package name. It
## is usually not required to do that, nims' filename is
Expand All @@ -22,43 +25,114 @@ var
foreignDeps*: seq[string] = @[] ## The foreign dependencies. Only
## exported for 'distros.nim'.

startCommand = "e"
endCommand = startCommand
endProject = ""

proc requires*(deps: varargs[string]) =
## Call this to set the list of requirements of your Nimble
## package.
for d in deps: requiresData.add(d)

proc getParams(): seq[string] =
for i in 4 .. paramCount():
result.add paramStr(i)

proc getCommand(): string =
return endCommand

proc setCommand(cmd: string, project = "") =
endCommand = cmd
if project.len != 0:
endProject = project

template printIfLen(varName) =
if varName.len != 0:
iniOut &= astToStr(varName) & ": \"" & varName & "\"\n"

template printSeqIfLen(varName) =
if varName.len != 0:
iniOut &= astToStr(varName) & ": \"" & varName.join(", ") & "\"\n"

proc printPkgInfo() =
var
iniOut = "[Package]\n"
printIfLen packageName
printIfLen version
printIfLen author
printIfLen description
printIfLen license
printIfLen srcdir
printIfLen binDir
printIfLen backend

printSeqIfLen skipDirs
printSeqIfLen skipFiles
printSeqIfLen skipExt
printSeqIfLen installDirs
printSeqIfLen installFiles
printSeqIfLen installExt
printSeqIfLen bin

if requiresData.len != 0:
iniOut &= "\n[Deps]\n"
iniOut &= &"requires: \"{requiresData.join(\", \")}\"\n"
echo iniOut
proc onExit() =
let
params = getParams()
if "printPkgInfo" in params:
printPkgInfo()
else:
var
output = ""
if endCommand != startCommand:
output &= "\"command\": \"" & endCommand & "\", "
if endProject.len != 0:
output &= "\"project\": \"" & endProject & "\", "
if output.len != 0:
echo "{" & output[0 .. ^3] & "}"
else:
echo "{}"
# TODO: New release of Nim will move this `task` template under a
# `when not defined(nimble)`. This will allow us to override it in the future.
when not declared(task):
template task*(name: untyped; description: string; body: untyped): untyped =
## Defines a task. Hidden tasks are supported via an empty description.
## Example:
##
## .. code-block:: nim
## task build, "default build is via the C backend":
## setCommand "c"
proc `name Task`*() = body

let cmd = getCommand()
if cmd.len == 0 or cmd == "help":
setCommand "help"
echo(astToStr(name), " ", description)
elif cmd == astToStr(name):
setCommand "nop"
`name Task`()
template task*(name: untyped; description: string; body: untyped): untyped =
## Defines a task. Hidden tasks are supported via an empty description.
## Example:
##
## .. code-block:: nim
## task build, "default build is via the C backend":
## setCommand "c"
proc `name Task`*() = body
let params = getParams()
if params.len == 0 or "help" in params:
echo(astToStr(name), " ", description)
elif astToStr(name) in params:
`name Task`()
template before*(action: untyped, body: untyped): untyped =
## Defines a block of code which is evaluated before ``action`` is executed.
proc `action Before`*(): bool =
result = true
proc `action Before`*() =
body
let params = getParams()
if astToStr(action) & "Before" in params:
`action Before`()
template after*(action: untyped, body: untyped): untyped =
## Defines a block of code which is evaluated after ``action`` is executed.
proc `action After`*(): bool =
result = true
proc `action After`*() =
body
let params = getParams()
if astToStr(action) & "After" in params:
`action After`()
template builtin = discard
proc getPkgDir*(): string =
Expand Down
4 changes: 1 addition & 3 deletions src/nimblepkg/nimscriptexecutor.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import os, tables, strutils, sets

import packageparser, common, packageinfo, options, nimscriptsupport, cli
import packageparser, common, packageinfo, options, nimscriptwrapper, cli

proc execHook*(options: Options, before: bool): bool =
## Returns whether to continue.
Expand All @@ -27,8 +27,6 @@ proc execHook*(options: Options, before: bool): bool =
else: actionName.normalize in pkgInfo.postHooks
if pkgInfo.isNimScript and hookExists:
let res = execHook(nimbleFile, actionName, before, options)
if res.success:
result = res.retVal

proc execCustom*(options: Options,
execResult: var ExecutionResult[void],
Expand Down
108 changes: 108 additions & 0 deletions src/nimblepkg/nimscriptwrapper.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# 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 common, version, options, packageinfo, cli
import hashes, json, os, strutils, strtabs, tables, times, osproc, sets, pegs

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, actionName: string, options: Options): tuple[output: string, exitCode: int] =
let
cmd = "nim e --verbosity:0 " & nimsFile.quoteShell & " " & actionName

result = execCmdEx(cmd)

proc setupNimscript*(scriptName: string, options: Options): tuple[nimsFile, iniFile: string] =
let
cacheDir = getTempDir() / "nimblecache"
shash = $scriptName.hash().abs()
prjCacheDir = cacheDir / scriptName.splitFile().name & "_" & shash

result.nimsFile = scriptName.parentDir() / scriptName.splitFile().name & "_" & shash & ".nims"
result.iniFile = prjCacheDir / scriptName.extractFilename().changeFileExt ".ini"

if not prjCacheDir.dirExists() or not result.nimsFile.fileExists() or not result.iniFile.fileExists() or
scriptName.getLastModificationTime() > result.nimsFile.getLastModificationTime():
createDir(prjCacheDir)
writeFile(result.nimsFile, nimscriptApi & scriptName.readFile() & "\nonExit()\n")
discard tryRemoveFile(result.iniFile)

let
(output, exitCode) = result.nimsFile.execNimscript("printPkgInfo", options)

if exitCode == 0 and output.len != 0:
result.iniFile.writeFile(output)
else:
raise newException(NimbleError, "printPkgInfo() failed")

proc execScript*(scriptName, actionName: string, options: Options): ExecutionResult[void] =
let
(nimsFile, iniFile) = setupNimscript(scriptName, options)

(output, exitCode) = nimsFile.execNimscript(actionName, options)

if exitCode != 0:
raise newException(NimbleError, output)

let
lines = output.strip().splitLines()
j =
if lines.len != 0:
parseJson(lines[^1])
else:
parseJson("{}")

result.success = true
if "command" in j:
result.command = $j["command"]
if "project" in j:
result.arguments.add $j["project"]
result.flags = newTable[string, seq[string]]()

if lines.len > 1:
stdout.writeLine lines[0 .. ^2].join("\n")

proc execTask*(scriptName, taskName: string,
options: Options): ExecutionResult[void] =
## 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[void] =
## 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)
2 changes: 1 addition & 1 deletion src/nimblepkg/packageinfo.nim
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ proc getInstalledPkgsMin*(libsDir: string, options: Options):
seq[tuple[pkginfo: PackageInfo, meta: MetaData]] =
## Gets a list of installed packages. The resulting package info is
## minimal. This has the advantage that it does not depend on the
## ``packageparser`` module, and so can be used by ``nimscriptsupport``.
## ``packageparser`` module, and so can be used by ``nimscriptwrapper``.
##
## ``libsDir`` is in most cases: ~/.nimble/pkgs/ (options.getPkgsDir)
result = @[]
Expand Down
12 changes: 10 additions & 2 deletions src/nimblepkg/packageparser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import parsecfg, json, streams, strutils, parseutils, os, tables, sugar
from sequtils import apply, map

import version, tools, common, nimscriptsupport, options, packageinfo, cli
import version, tools, common, nimscriptwrapper, options, packageinfo, cli

## Contains procedures for parsing .nimble files. Moved here from ``packageinfo``
## because it depends on ``nimscriptsupport`` (``nimscriptsupport`` also
## because it depends on ``nimscriptwrapper`` (``nimscriptwrapper`` also
## depends on other procedures in ``packageinfo``.

type
Expand Down Expand Up @@ -277,6 +277,14 @@ proc readPackageInfoFromNimble(path: string; result: var PackageInfo) =
else:
raise newException(ValueError, "Cannot open package info: " & path)

proc readPackageInfoFromNims(scriptName: string, options: Options,
result: var PackageInfo) =
let
(nimsFile, iniFile) = setupNimscript(scriptName, options)

if iniFile.fileExists():
readPackageInfoFromNimble(iniFile, result)

proc inferInstallRules(pkgInfo: var PackageInfo, options: Options) =
# Binary packages shouldn't install .nim files by default.
# (As long as the package info doesn't explicitly specify what should be
Expand Down

0 comments on commit ab38b81

Please sign in to comment.