Merge branch 'nimscript-support'
This commit is contained in:
commit
d94501234a
10 changed files with 747 additions and 180 deletions
|
|
@ -1,6 +1,6 @@
|
|||
[Package]
|
||||
name = "nimble"
|
||||
version = "0.6.2"
|
||||
version = "0.7.0"
|
||||
author = "Dominik Picheta"
|
||||
description = "Nim package manager."
|
||||
license = "BSD"
|
||||
|
|
|
|||
|
|
@ -230,6 +230,28 @@ which can be useful to read the bundled documentation. Example:
|
|||
The nimble ``init`` command will start a simple wizard which will create
|
||||
a quick ``.nimble`` file for your project.
|
||||
|
||||
As of version 0.7.0, the ``.nimble`` file that this command creates will
|
||||
use the new NimScript format.
|
||||
Check out the [Creating Packages](#creating-packages) section for more info.
|
||||
|
||||
### nimble publish
|
||||
|
||||
Publishes your Nimble package to the official Nimble package repository.
|
||||
|
||||
**Note:** Requires a valid Github account.
|
||||
|
||||
### nimble tasks
|
||||
|
||||
For a nimble package in the current working directory, list the tasks which that
|
||||
package defines. This is only supported for packages utilising the new
|
||||
nimscript .nimble files.
|
||||
|
||||
### nimble dump
|
||||
|
||||
Outputs information about the package in the current working directory in
|
||||
an ini-compatible format. Useful for tools wishing to read metadata about
|
||||
Nimble packages who do not want to use the NimScript evaluator.
|
||||
|
||||
## Configuration
|
||||
|
||||
At startup Nimble will attempt to read ``~/.config/nimble/nimble.ini`` on Linux
|
||||
|
|
@ -265,7 +287,17 @@ named "foobar" should have a corresponding ``foobar.nimble`` file.
|
|||
|
||||
These files specify information about the package including its name, author,
|
||||
license, dependencies and more. Without one Nimble is not able to install
|
||||
a package. A bare minimum .nimble file follows:
|
||||
a package.
|
||||
|
||||
A .nimble file can be created easily using Nimble's ``init`` command. This
|
||||
command will ask you a bunch of questions about your package, then generate a
|
||||
.nimble file for you.
|
||||
|
||||
**Note:** As of version 0.7.0, the ``init`` command generates .nimble files
|
||||
using the new NimScript format. Take a look at the next section for more
|
||||
information.
|
||||
|
||||
A bare minimum .nimble file follows:
|
||||
|
||||
```ini
|
||||
[Package]
|
||||
|
|
@ -286,6 +318,62 @@ Nimble currently supports installation of packages from a local directory, a
|
|||
git repository and a mercurial repository. The .nimble file must be present in
|
||||
the root of the directory or repository being installed.
|
||||
|
||||
### The new NimScript format
|
||||
|
||||
**Warning:** This features is still very experimental. You are encouraged to
|
||||
try it, but be aware that it may change significantly in the future or
|
||||
may even be removed completely!
|
||||
|
||||
Version 0.7.0 of Nimble introduces support for evaluating .nimble files
|
||||
as Nim code. This means that you can define metadata about your package
|
||||
using the Nim programming language.
|
||||
|
||||
Because of Nim's flexibility the definitions remain declarative. With the added
|
||||
ability of using the Nim language to enrich your package specification.
|
||||
For example, you can define dependencies for specific platforms using Nim's
|
||||
``when`` statement.
|
||||
|
||||
The ini example above looks like this in the NimScript format:
|
||||
|
||||
```nim
|
||||
# Package
|
||||
|
||||
version = "0.1.0"
|
||||
author = "Your Name"
|
||||
description = "Example .nimble file."
|
||||
license = "MIT"
|
||||
|
||||
# Deps
|
||||
|
||||
requires "nim >= 0.10.0"
|
||||
```
|
||||
|
||||
The format is indeed very similar to the ini format. Another great feature
|
||||
is the ability to define custom Nimble package-specific commands. These are
|
||||
defined in the .nimble files of course.
|
||||
|
||||
```nim
|
||||
task hello, "This is a hello task":
|
||||
echo("Hello World!")
|
||||
```
|
||||
|
||||
You can then execute ``nimble hello``, which will result in the following
|
||||
output:
|
||||
|
||||
```
|
||||
Executing task hello in /Users/user/projects/pkg/pkg.nimble
|
||||
Hello World!
|
||||
```
|
||||
|
||||
You can place any Nim code inside these tasks. As long as that code does not
|
||||
access the FFI. The ``nimscript``
|
||||
[module](http://nim-lang.org/docs/nimscript.html) in Nim's standard library defines
|
||||
additional functionality such as the ability to execute external processes
|
||||
which makes this feature very powerful.
|
||||
|
||||
You can also check what tasks are supported by the package in the current
|
||||
directory by using the ``tasks`` command.
|
||||
|
||||
### Libraries
|
||||
|
||||
Library packages are likely the most popular form of Nimble packages. They are
|
||||
|
|
@ -431,7 +519,7 @@ their own packages to it! Take a look at
|
|||
|
||||
#### Required
|
||||
|
||||
* ``name`` - The name of the package.
|
||||
* ``name`` - The name of the package. *(This is not required in the new NimScript format)*
|
||||
* ``version`` - The *current* version of this package. This should be incremented
|
||||
**after** tagging the current version using ``git tag`` or ``hg tag``.
|
||||
* ``author`` - The name of the author of this package.
|
||||
|
|
|
|||
336
src/nimble.nim
336
src/nimble.nim
|
|
@ -10,6 +10,8 @@ import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools,
|
|||
nimblepkg/download, nimblepkg/config, nimblepkg/nimbletypes,
|
||||
nimblepkg/publish
|
||||
|
||||
import nimblepkg/nimscriptsupport
|
||||
|
||||
when not defined(windows):
|
||||
from posix import getpid
|
||||
else:
|
||||
|
|
@ -38,13 +40,14 @@ type
|
|||
nimbleData: JsonNode ## Nimbledata.json
|
||||
|
||||
ActionType = enum
|
||||
actionNil, actionUpdate, actionInit, actionPublish,
|
||||
actionNil, actionUpdate, actionInit, actionDump, actionPublish,
|
||||
actionInstall, actionSearch,
|
||||
actionList, actionBuild, actionPath, actionUninstall, actionCompile
|
||||
actionList, actionBuild, actionPath, actionUninstall, actionCompile,
|
||||
actionCustom, actionTasks
|
||||
|
||||
Action = object
|
||||
case typ: ActionType
|
||||
of actionNil, actionList, actionBuild, actionPublish: nil
|
||||
of actionNil, actionList, actionBuild, actionPublish, actionTasks: nil
|
||||
of actionUpdate:
|
||||
optionalURL: string # Overrides default package list.
|
||||
of actionInstall, actionPath, actionUninstall:
|
||||
|
|
@ -53,7 +56,7 @@ type
|
|||
packages: seq[PkgTuple] # Optional only for actionInstall.
|
||||
of actionSearch:
|
||||
search: seq[string] # Search string.
|
||||
of actionInit:
|
||||
of actionInit, actionDump:
|
||||
projName: string
|
||||
of actionCompile:
|
||||
file: string
|
||||
|
|
@ -84,8 +87,12 @@ Commands:
|
|||
performed by tag and by name.
|
||||
list [--ver] Lists all packages.
|
||||
[-i, --installed] Lists all installed packages.
|
||||
tasks Lists the tasks specified in the Nimble
|
||||
package's Nimble file.
|
||||
path pkgname ... Shows absolute path to the installed packages
|
||||
specified.
|
||||
dump [pkgname] Outputs Nimble package information for
|
||||
external tools.
|
||||
|
||||
Options:
|
||||
-h, --help Print this help message.
|
||||
|
|
@ -99,7 +106,7 @@ Options:
|
|||
For more information read the Github readme:
|
||||
https://github.com/nim-lang/nimble#readme
|
||||
"""
|
||||
nimbleVersion = "0.6.2"
|
||||
nimbleVersion = "0.7.0"
|
||||
defaultPackageURL =
|
||||
"https://github.com/nim-lang/packages/raw/master/packages.json"
|
||||
|
||||
|
|
@ -144,6 +151,18 @@ proc prompt(options: Options, question: string): bool =
|
|||
else:
|
||||
return false
|
||||
|
||||
proc promptCustom(question, default: string): string =
|
||||
if default == "":
|
||||
stdout.write(question, ": ")
|
||||
let user = stdin.readLine()
|
||||
if user.len == 0: return promptCustom(question, default)
|
||||
else: return user
|
||||
else:
|
||||
stdout.write(question, " [", default, "]: ")
|
||||
let user = stdin.readLine()
|
||||
if user == "": return default
|
||||
else: return user
|
||||
|
||||
proc renameBabelToNimble(options: Options) {.deprecated.} =
|
||||
let babelDir = getHomeDir() / ".babel"
|
||||
let nimbleDir = getHomeDir() / ".nimble"
|
||||
|
|
@ -156,6 +175,64 @@ proc renameBabelToNimble(options: Options) {.deprecated.} =
|
|||
removeDir(babelDir)
|
||||
removeFile(nimbleDir / "babeldata.json")
|
||||
|
||||
proc parseActionType(action: string): ActionType =
|
||||
case action.normalize()
|
||||
of "install", "path":
|
||||
case action.normalize()
|
||||
of "install":
|
||||
result = actionInstall
|
||||
of "path":
|
||||
result = actionPath
|
||||
else:
|
||||
discard
|
||||
of "build":
|
||||
result = actionBuild
|
||||
of "c", "compile", "js", "cpp", "cc":
|
||||
result = actionCompile
|
||||
of "init":
|
||||
result = actionInit
|
||||
of "dump":
|
||||
result = actionDump
|
||||
of "update", "refresh":
|
||||
result = actionUpdate
|
||||
of "search":
|
||||
result = actionSearch
|
||||
of "list":
|
||||
result = actionList
|
||||
of "uninstall", "remove", "delete", "del", "rm":
|
||||
result = actionUninstall
|
||||
of "publish":
|
||||
result = actionPublish
|
||||
of "tasks":
|
||||
result = actionTasks
|
||||
else:
|
||||
result = actionCustom
|
||||
|
||||
proc initAction(options: var Options, key: string) =
|
||||
## Intialises `options.actions` fields based on `options.actions.typ` and
|
||||
## `key`.
|
||||
let keyNorm = key.normalize()
|
||||
case options.action.typ
|
||||
of actionInstall, actionPath:
|
||||
options.action.packages = @[]
|
||||
of actionCompile:
|
||||
options.action.compileOptions = @[]
|
||||
options.action.file = ""
|
||||
if keyNorm == "c" or keyNorm == "compile": options.action.backend = ""
|
||||
else: options.action.backend = keyNorm
|
||||
of actionInit:
|
||||
options.action.projName = ""
|
||||
of actionDump:
|
||||
options.action.projName = ""
|
||||
of actionUpdate:
|
||||
options.action.optionalURL = ""
|
||||
of actionSearch:
|
||||
options.action.search = @[]
|
||||
of actionUninstall:
|
||||
options.action.packages = @[]
|
||||
of actionBuild, actionPublish, actionCustom, actionList, actionTasks,
|
||||
actionNil: discard
|
||||
|
||||
proc parseCmdLine(): Options =
|
||||
result.action.typ = actionNil
|
||||
result.config = parseConfig()
|
||||
|
|
@ -163,41 +240,9 @@ proc parseCmdLine(): Options =
|
|||
case kind
|
||||
of cmdArgument:
|
||||
if result.action.typ == actionNil:
|
||||
case key
|
||||
of "install", "path":
|
||||
case key
|
||||
of "install":
|
||||
result.action.typ = actionInstall
|
||||
of "path":
|
||||
result.action.typ = actionPath
|
||||
else:
|
||||
discard
|
||||
result.action.packages = @[]
|
||||
of "build":
|
||||
result.action.typ = actionBuild
|
||||
of "c", "compile", "js", "cpp", "cc":
|
||||
result.action.typ = actionCompile
|
||||
result.action.compileOptions = @[]
|
||||
result.action.file = ""
|
||||
if key == "c" or key == "compile": result.action.backend = ""
|
||||
else: result.action.backend = key
|
||||
of "init":
|
||||
result.action.typ = actionInit
|
||||
result.action.projName = ""
|
||||
of "update", "refresh":
|
||||
result.action.typ = actionUpdate
|
||||
result.action.optionalURL = ""
|
||||
of "search":
|
||||
result.action.typ = actionSearch
|
||||
result.action.search = @[]
|
||||
of "list":
|
||||
result.action.typ = actionList
|
||||
of "uninstall", "remove", "delete", "del", "rm":
|
||||
result.action.typ = actionUninstall
|
||||
result.action.packages = @[]
|
||||
of "publish":
|
||||
result.action.typ = actionPublish
|
||||
else: writeHelp()
|
||||
setNimScriptCommand(key)
|
||||
result.action.typ = parseActionType(key)
|
||||
initAction(result, key)
|
||||
else:
|
||||
case result.action.typ
|
||||
of actionNil:
|
||||
|
|
@ -215,7 +260,7 @@ proc parseCmdLine(): Options =
|
|||
result.action.optionalURL = key
|
||||
of actionSearch:
|
||||
result.action.search.add(key)
|
||||
of actionInit:
|
||||
of actionInit, actionDump:
|
||||
if result.action.projName != "":
|
||||
raise newException(NimbleError,
|
||||
"Can only initialize one package at a time.")
|
||||
|
|
@ -721,11 +766,13 @@ proc install(packages: seq[PkgTuple],
|
|||
|
||||
proc build(options: Options) =
|
||||
var pkgInfo = getPkgInfo(getCurrentDir())
|
||||
nimScriptHint(pkgInfo)
|
||||
let paths = processDeps(pkginfo, options)
|
||||
buildFromDir(pkgInfo, paths, false)
|
||||
|
||||
proc compile(options: Options) =
|
||||
var pkgInfo = getPkgInfo(getCurrentDir())
|
||||
nimScriptHint(pkgInfo)
|
||||
let paths = processDeps(pkginfo, options)
|
||||
let realDir = pkgInfo.getRealDir()
|
||||
|
||||
|
|
@ -827,9 +874,11 @@ proc listPaths(options: Options) =
|
|||
continue
|
||||
|
||||
let
|
||||
nimScriptFile = path / name.addFileExt("nims")
|
||||
babelFile = path / name.addFileExt("babel")
|
||||
nimbleFile = path / name.addFileExt("nimble")
|
||||
hasSpec = nimbleFile.existsFile or babelFile.existsFile
|
||||
hasSpec = nimScriptFile.existsFile or
|
||||
nimbleFile.existsFile or babelFile.existsFile
|
||||
if hasSpec:
|
||||
var pkgInfo = getPkgInfo(path)
|
||||
var v: VersionAndPath
|
||||
|
|
@ -849,44 +898,105 @@ proc listPaths(options: Options) =
|
|||
raise newException(NimbleError,
|
||||
"At least one of the specified packages was not found")
|
||||
|
||||
proc join(x: seq[PkgTuple]; y: string): string =
|
||||
if x.len == 0: return ""
|
||||
result = x[0][0] & " " & $x[0][1]
|
||||
for i in 1 ..< x.len:
|
||||
result.add y
|
||||
result.add x[i][0] & " " & $x[i][1]
|
||||
|
||||
proc dump(options: Options) =
|
||||
let proj = addFileExt(options.action.projName, "nimble")
|
||||
let p = if fileExists(proj): readPackageInfo(proj)
|
||||
else: getPkgInfo(os.getCurrentDir())
|
||||
echo "name: ", p.name.escape
|
||||
echo "version: ", p.version.escape
|
||||
echo "author: ", p.author.escape
|
||||
echo "desc: ", p.description.escape
|
||||
echo "license: ", p.license.escape
|
||||
echo "skipDirs: ", p.skipDirs.join(", ").escape
|
||||
echo "skipFiles: ", p.skipFiles.join(", ").escape
|
||||
echo "skipExt: ", p.skipExt.join(", ").escape
|
||||
echo "installDirs: ", p.installDirs.join(", ").escape
|
||||
echo "installFiles: ", p.installFiles.join(", ").escape
|
||||
echo "installExt: ", p.installExt.join(", ").escape
|
||||
echo "requires: ", p.requires.join(", ").escape
|
||||
echo "bin: ", p.bin.join(", ").escape
|
||||
echo "binDir: ", p.binDir.escape
|
||||
echo "srcDir: ", p.srcDir.escape
|
||||
echo "backend: ", p.backend.escape
|
||||
|
||||
proc init(options: Options) =
|
||||
echo("Initializing new Nimble project!")
|
||||
var
|
||||
pkgName, fName: string = ""
|
||||
outFile: File
|
||||
var nimbleFile: string = ""
|
||||
|
||||
if (options.action.projName != ""):
|
||||
pkgName = options.action.projName
|
||||
fName = pkgName & ".nimble"
|
||||
if (existsFile(os.getCurrentDir() / fName)):
|
||||
raise newException(NimbleError, "Already have a nimble file.")
|
||||
echo("In order to initialise a new Nimble package, I will need to ask you\n" &
|
||||
"some questions. Default values are shown in square brackets, press\n" &
|
||||
"enter to use them.")
|
||||
|
||||
# Ask for package name.
|
||||
if options.action.projName != "":
|
||||
let pkgName = options.action.projName
|
||||
nimbleFile = pkgName.changeFileExt("nimble")
|
||||
else:
|
||||
echo("Enter a project name for this (blank to use working directory), " &
|
||||
"Ctrl-C to abort:")
|
||||
pkgName = readline(stdin)
|
||||
if (pkgName == ""):
|
||||
pkgName = os.getCurrentDir().splitPath.tail
|
||||
if (pkgName == ""):
|
||||
raise newException(NimbleError, "Could not get default file path.")
|
||||
fName = pkgName & ".nimble"
|
||||
var pkgName = os.getCurrentDir().splitPath.tail.toValidPackageName()
|
||||
pkgName = promptCustom("Enter package name", pkgName)
|
||||
nimbleFile = pkgName.changeFileExt("nimble")
|
||||
|
||||
# Now need to write out .nimble file with projName and other details
|
||||
validatePackageName(nimbleFile.changeFileExt(""))
|
||||
|
||||
if (not existsFile(os.getCurrentDir() / fName) and
|
||||
open(f=outFile, filename = fName, mode = fmWrite)):
|
||||
outFile.writeln("[Package]")
|
||||
outFile.writeln("name = \"" & pkgName & "\"")
|
||||
outFile.writeln("version = \"0.1.0\"")
|
||||
outFile.writeln("author = \"Anonymous\"")
|
||||
outFile.writeln("description = \"New Nimble project for Nim\"")
|
||||
outFile.writeln("license = \"MIT\"")
|
||||
outFile.writeln("")
|
||||
outFile.writeln("[Deps]")
|
||||
outFile.writeln("Requires: \"nim >= 0.10.0\"")
|
||||
if existsFile(os.getCurrentDir() / nimbleFile):
|
||||
raise newException(NimbleError, "Nimble file already exists.")
|
||||
|
||||
# Ask for package version.
|
||||
let pkgVersion = promptCustom("Enter intial version of package", "0.1.0")
|
||||
validateVersion(pkgVersion)
|
||||
|
||||
# Ask for package author
|
||||
var defaultAuthor = "Anonymous"
|
||||
if findExe("git") != "":
|
||||
let (name, exitCode) = doCmdEx("git config --global user.name")
|
||||
if exitCode == QuitSuccess and name.len > 0:
|
||||
defaultAuthor = name.strip()
|
||||
elif defaultAuthor == "Anonymous" and findExe("hg") != "":
|
||||
let (name, exitCode) = doCmdEx("hg config ui.username")
|
||||
if exitCode == QuitSuccess and name.len > 0:
|
||||
defaultAuthor = name.strip()
|
||||
let pkgAuthor = promptCustom("Enter your name", defaultAuthor)
|
||||
|
||||
# Ask for description
|
||||
let pkgDesc = promptCustom("Enter package description", "")
|
||||
|
||||
# Ask for license
|
||||
# TODO: Provide selection of licenses, or select random default license.
|
||||
let pkgLicense = promptCustom("Enter package license", "MIT")
|
||||
|
||||
# Ask for Nim dependency
|
||||
let nimDepDef = getNimrodVersion()
|
||||
let pkgNimDep = promptCustom("Enter lowest supported Nim version", $nimDepDef)
|
||||
validateVersion(pkgNimDep)
|
||||
|
||||
# Now generate the .nimble file.
|
||||
if existsFile(os.getCurrentDir() / nimbleFile):
|
||||
raise newException(NimbleError,
|
||||
"Looks like a Nimble file has already been created.")
|
||||
|
||||
var outFile: File
|
||||
if open(f = outFile, filename = nimbleFile, mode = fmWrite):
|
||||
outFile.writeLine """# Package
|
||||
|
||||
version = $#
|
||||
author = $#
|
||||
description = $#
|
||||
license = $#
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= $#"
|
||||
""" % [pkgVersion.escape(), pkgAuthor.escape(), pkgDesc.escape(),
|
||||
pkgLicense.escape(), pkgNimDep]
|
||||
close(outFile)
|
||||
|
||||
else:
|
||||
raise newException(NimbleError, "Unable to open file " & fName &
|
||||
raise newException(NimbleError, "Unable to open file " & nimbleFile &
|
||||
" for writing: " & osErrorMsg(osLastError()))
|
||||
|
||||
proc uninstall(options: Options) =
|
||||
|
|
@ -946,37 +1056,67 @@ proc uninstall(options: Options) =
|
|||
removePkgDir(options.getPkgsDir / (pkg.name & '-' & pkg.version), options)
|
||||
echo("Removed ", pkg.name, " (", $pkg.version, ")")
|
||||
|
||||
proc listTasks(options: Options) =
|
||||
let nimbleFile = findNimbleFile(getCurrentDir(), true)
|
||||
nimscriptsupport.listTasks(nimbleFile)
|
||||
|
||||
proc doAction(options: Options) =
|
||||
if not existsDir(options.getNimbleDir()):
|
||||
createDir(options.getNimbleDir())
|
||||
if not existsDir(options.getPkgsDir):
|
||||
createDir(options.getPkgsDir)
|
||||
|
||||
case options.action.typ
|
||||
of actionUpdate:
|
||||
update(options)
|
||||
of actionInstall:
|
||||
discard install(options.action.packages, options)
|
||||
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 actionNil:
|
||||
assert false
|
||||
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 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()
|
||||
|
||||
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()
|
||||
|
||||
when isMainModule:
|
||||
when defined(release):
|
||||
|
|
|
|||
|
|
@ -1 +1,4 @@
|
|||
-d:ssl
|
||||
--path:"$lib/packages/docutils"
|
||||
--noNimblePath
|
||||
--path:"$nim/"
|
||||
2
src/nimblepkg/nim.cfg
Normal file
2
src/nimblepkg/nim.cfg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
--path:"$nim/"
|
||||
--path:"$lib/packages/docutils"
|
||||
|
|
@ -4,13 +4,15 @@
|
|||
# recursive imports
|
||||
|
||||
import version
|
||||
export version.NimbleError
|
||||
|
||||
type
|
||||
NimbleError* = object of Exception
|
||||
BuildFailed* = object of NimbleError
|
||||
|
||||
PackageInfo* = object
|
||||
mypath*: string ## The path of this .nimble file
|
||||
isNimScript*: bool ## Determines if this pkg info was read from a nims file
|
||||
isMinimal*: bool
|
||||
name*: string
|
||||
version*: string
|
||||
author*: string
|
||||
|
|
@ -27,4 +29,3 @@ type
|
|||
binDir*: string
|
||||
srcDir*: string
|
||||
backend*: string
|
||||
|
||||
|
|
|
|||
185
src/nimblepkg/nimscriptsupport.nim
Normal file
185
src/nimblepkg/nimscriptsupport.nim
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
# 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
|
||||
compiler/ast, compiler/modules, compiler/passes, compiler/passaux,
|
||||
compiler/condsyms, compiler/options, compiler/sem, compiler/semdata,
|
||||
compiler/llstream, compiler/vm, compiler/vmdef, compiler/commands,
|
||||
compiler/msgs, compiler/magicsys, compiler/lists
|
||||
|
||||
from compiler/scriptconfig import setupVM
|
||||
from compiler/idents import getIdent
|
||||
from compiler/astalgo import strTableGet, `$`
|
||||
|
||||
import nimbletypes, version
|
||||
import os, strutils
|
||||
|
||||
const
|
||||
internalCmd = "NimbleInternal"
|
||||
|
||||
proc raiseVariableError(ident, typ: string) {.noinline.} =
|
||||
raise newException(NimbleError,
|
||||
"NimScript's variable '" & ident & "' needs a value of type '" & typ & "'.")
|
||||
|
||||
proc isStrLit(n: PNode): bool = n.kind in {nkStrLit..nkTripleStrLit}
|
||||
|
||||
proc getGlobal(ident: string): string =
|
||||
let n = vm.globalCtx.getGlobalValue(getSysSym ident)
|
||||
if n.isStrLit:
|
||||
result = if n.strVal.isNil: "" else: n.strVal
|
||||
else:
|
||||
raiseVariableError(ident, "string")
|
||||
|
||||
proc getGlobalAsSeq(ident: string): seq[string] =
|
||||
let n = vm.globalCtx.getGlobalValue(getSysSym ident)
|
||||
result = @[]
|
||||
if n.kind == nkBracket:
|
||||
for x in n:
|
||||
if x.isStrLit:
|
||||
result.add x.strVal
|
||||
else:
|
||||
raiseVariableError(ident, "seq[string]")
|
||||
else:
|
||||
raiseVariableError(ident, "seq[string]")
|
||||
|
||||
proc extractRequires(result: var seq[PkgTuple]) =
|
||||
let n = vm.globalCtx.getGlobalValue(getSysSym "requiresData")
|
||||
if n.kind == nkBracket:
|
||||
for x in n:
|
||||
if x.kind == nkPar and x.len == 2 and x[0].isStrLit and x[1].isStrLit:
|
||||
result.add(parseRequires(x[0].strVal & x[1].strVal))
|
||||
elif x.isStrLit:
|
||||
result.add(parseRequires(x.strVal))
|
||||
else:
|
||||
raiseVariableError("requiresData", "seq[(string, VersionReq)]")
|
||||
else:
|
||||
raiseVariableError("requiresData", "seq[(string, VersionReq)]")
|
||||
|
||||
proc execScript(scriptName: string) =
|
||||
## Executes the specified script.
|
||||
##
|
||||
## No clean up is performed and must be done manually!
|
||||
setDefaultLibpath()
|
||||
passes.gIncludeFile = includeModule
|
||||
passes.gImportModule = importModule
|
||||
initDefines()
|
||||
|
||||
defineSymbol("nimscript")
|
||||
defineSymbol("nimconfig")
|
||||
defineSymbol("nimble")
|
||||
registerPass(semPass)
|
||||
registerPass(evalPass)
|
||||
|
||||
appendStr(searchPaths, options.libpath)
|
||||
|
||||
var m = makeModule(scriptName)
|
||||
incl(m.flags, sfMainModule)
|
||||
vm.globalCtx = setupVM(m, scriptName)
|
||||
|
||||
compileSystemModule()
|
||||
processModule(m, llStreamOpen(scriptName, fmRead), nil)
|
||||
|
||||
proc cleanup() =
|
||||
# ensure everything can be called again:
|
||||
resetAllModulesHard()
|
||||
clearPasses()
|
||||
msgs.gErrorMax = 1
|
||||
msgs.writeLnHook = nil
|
||||
vm.globalCtx = nil
|
||||
initDefines()
|
||||
|
||||
proc readPackageInfoFromNims*(scriptName: string; result: var PackageInfo) =
|
||||
## Executes the `scriptName` nimscript file. Reads the package information
|
||||
## that it populates.
|
||||
|
||||
# Setup custom error handling.
|
||||
msgs.gErrorMax = high(int)
|
||||
var previousMsg = ""
|
||||
msgs.writeLnHook =
|
||||
proc (output: string) =
|
||||
# The error counter is incremented after the writeLnHook is invoked.
|
||||
if msgs.gErrorCounter > 0:
|
||||
raise newException(NimbleError, previousMsg)
|
||||
previousMsg = output
|
||||
|
||||
# Execute the nimscript file.
|
||||
execScript(scriptName)
|
||||
|
||||
# Extract all the necessary fields populated by the nimscript file.
|
||||
template trivialField(field) =
|
||||
result.field = getGlobal(astToStr field)
|
||||
|
||||
template trivialFieldSeq(field) =
|
||||
result.field.add getGlobalAsSeq(astToStr field)
|
||||
|
||||
# keep reasonable default:
|
||||
let name = getGlobal"packageName"
|
||||
if name.len > 0: result.name = name
|
||||
|
||||
trivialField version
|
||||
trivialField author
|
||||
trivialField description
|
||||
trivialField license
|
||||
trivialField srcdir
|
||||
trivialField bindir
|
||||
trivialFieldSeq skipDirs
|
||||
trivialFieldSeq skipFiles
|
||||
trivialFieldSeq skipExt
|
||||
trivialFieldSeq installDirs
|
||||
trivialFieldSeq installFiles
|
||||
trivialFieldSeq installExt
|
||||
|
||||
extractRequires result.requires
|
||||
|
||||
let binSeq = getGlobalAsSeq("bin")
|
||||
for i in binSeq:
|
||||
result.bin.add(i.addFileExt(ExeExt))
|
||||
|
||||
let backend = getGlobal("backend")
|
||||
if backend.len == 0:
|
||||
result.backend = "c"
|
||||
elif cmpIgnoreStyle(backend, "javascript") == 0:
|
||||
result.backend = "js"
|
||||
else:
|
||||
result.backend = backend.toLower()
|
||||
|
||||
cleanup()
|
||||
|
||||
proc execTask*(scriptName, taskName: string): bool =
|
||||
## Executes the specified task in the specified script.
|
||||
##
|
||||
## `scriptName` should be a filename pointing to the nimscript file.
|
||||
result = true
|
||||
options.command = internalCmd
|
||||
echo("Executing task ", taskName, " in ", scriptName)
|
||||
|
||||
execScript(scriptName)
|
||||
# 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
|
||||
discard vm.globalCtx.execProc(prc, [])
|
||||
cleanup()
|
||||
|
||||
proc getNimScriptCommand*(): string =
|
||||
options.command
|
||||
|
||||
proc setNimScriptCommand*(command: string) =
|
||||
options.command = command
|
||||
|
||||
proc hasTaskRequestedCommand*(): bool =
|
||||
## Determines whether the last executed task used ``setCommand``
|
||||
return getNimScriptCommand() != internalCmd
|
||||
|
||||
proc listTasks*(scriptName: string) =
|
||||
setNimScriptCommand("help")
|
||||
|
||||
execScript(scriptName)
|
||||
# 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 parsecfg, json, streams, strutils, parseutils, os
|
||||
import version, tools, nimbletypes
|
||||
import version, tools, nimbletypes, nimscriptsupport
|
||||
|
||||
when not declared(system.map):
|
||||
from sequtils import map
|
||||
|
|
@ -15,7 +15,7 @@ type
|
|||
downloadMethod*: string
|
||||
description*: string
|
||||
tags*: seq[string] # Even if empty, always a valid non nil seq. \
|
||||
# From here on, optional fields set to the emtpy string if not available.
|
||||
# From here on, optional fields set to the empty string if not available.
|
||||
version*: string
|
||||
dvcsTag*: string
|
||||
web*: string # Info url for humans.
|
||||
|
|
@ -23,9 +23,12 @@ type
|
|||
MetaData* = object
|
||||
url*: string
|
||||
|
||||
proc initPackageInfo(): PackageInfo =
|
||||
result.mypath = ""
|
||||
result.name = ""
|
||||
NimbleFile* = string
|
||||
|
||||
proc initPackageInfo(path: string): PackageInfo =
|
||||
result.mypath = path
|
||||
# reasonable default:
|
||||
result.name = path.splitFile.name
|
||||
result.version = ""
|
||||
result.author = ""
|
||||
result.description = ""
|
||||
|
|
@ -42,6 +45,47 @@ proc initPackageInfo(): PackageInfo =
|
|||
result.binDir = ""
|
||||
result.backend = "c"
|
||||
|
||||
proc validatePackageName*(name: string) =
|
||||
## Raises an error if specified package name contains invalid characters.
|
||||
##
|
||||
## A valid package name is one which is a valid nim module name. So only
|
||||
## underscores, letters and numbers allowed.
|
||||
if name.len == 0: return
|
||||
|
||||
if name[0] in {'0'..'9'}:
|
||||
raise newException(NimbleError,
|
||||
"Invalid package name: cannot beging with " & name[0])
|
||||
|
||||
var prevWasUnderscore = false
|
||||
for c in name:
|
||||
case c
|
||||
of '_':
|
||||
if prevWasUnderscore:
|
||||
raise newException(NimbleError,
|
||||
"Invalid package name: cannot contain \"__\"")
|
||||
prevWasUnderscore = true
|
||||
of AllChars - IdentChars:
|
||||
raise newException(NimbleError,
|
||||
"Invalid package name: cannot contain '$1'" % $c)
|
||||
else:
|
||||
prevWasUnderscore = false
|
||||
|
||||
proc toValidPackageName*(name: string): string =
|
||||
result = ""
|
||||
for c in name:
|
||||
case c
|
||||
of '_', '-':
|
||||
if result[^1] != '_': result.add('_')
|
||||
of AllChars - IdentChars - {'-'}: discard
|
||||
else: result.add(c)
|
||||
|
||||
proc validateVersion*(ver: string) =
|
||||
for c in ver:
|
||||
if c notin ({'.'} + Digits):
|
||||
raise newException(NimbleError,
|
||||
"Version may only consist of numbers and the '.' character " &
|
||||
"but found '" & c & "'.")
|
||||
|
||||
proc validatePackageInfo(pkgInfo: PackageInfo, path: string) =
|
||||
if pkgInfo.name == "":
|
||||
raise newException(NimbleError, "Incorrect .nimble file: " & path &
|
||||
|
|
@ -49,40 +93,29 @@ proc validatePackageInfo(pkgInfo: PackageInfo, path: string) =
|
|||
if pkgInfo.version == "":
|
||||
raise newException(NimbleError, "Incorrect .nimble file: " & path &
|
||||
" does not contain a version field.")
|
||||
if pkgInfo.author == "":
|
||||
raise newException(NimbleError, "Incorrect .nimble file: " & path &
|
||||
" does not contain an author field.")
|
||||
if pkgInfo.description == "":
|
||||
raise newException(NimbleError, "Incorrect .nimble file: " & path &
|
||||
" does not contain a description field.")
|
||||
if pkgInfo.license == "":
|
||||
raise newException(NimbleError, "Incorrect .nimble file: " & path &
|
||||
" does not contain a license field.")
|
||||
if pkgInfo.backend notin ["c", "cc", "objc", "cpp", "js"]:
|
||||
raise newException(NimbleError, "'" & pkgInfo.backend &
|
||||
"' is an invalid backend.")
|
||||
for c in pkgInfo.version:
|
||||
if c notin ({'.'} + Digits):
|
||||
raise newException(NimbleError,
|
||||
"Version may only consist of numbers and the '.' character " &
|
||||
"but found '" & c & "'.")
|
||||
|
||||
proc parseRequires(req: string): PkgTuple =
|
||||
try:
|
||||
if ' ' in req:
|
||||
var i = skipUntil(req, Whitespace)
|
||||
result.name = req[0 .. i].strip
|
||||
result.ver = parseVersionRange(req[i .. req.len-1])
|
||||
elif '#' in req:
|
||||
var i = skipUntil(req, {'#'})
|
||||
result.name = req[0 .. i-1]
|
||||
result.ver = parseVersionRange(req[i .. req.len-1])
|
||||
else:
|
||||
result.name = req.strip
|
||||
result.ver = VersionRange(kind: verAny)
|
||||
except ParseVersionError:
|
||||
raise newException(NimbleError,
|
||||
"Unable to parse dependency version range: " & getCurrentExceptionMsg())
|
||||
if not pkgInfo.isMinimal:
|
||||
if pkgInfo.author == "":
|
||||
raise newException(NimbleError, "Incorrect .nimble file: " & path &
|
||||
" does not contain an author field.")
|
||||
if pkgInfo.description == "":
|
||||
raise newException(NimbleError, "Incorrect .nimble file: " & path &
|
||||
" does not contain a description field.")
|
||||
if pkgInfo.license == "":
|
||||
raise newException(NimbleError, "Incorrect .nimble file: " & path &
|
||||
" does not contain a license field.")
|
||||
if pkgInfo.backend notin ["c", "cc", "objc", "cpp", "js"]:
|
||||
raise newException(NimbleError, "'" & pkgInfo.backend &
|
||||
"' is an invalid backend.")
|
||||
|
||||
validateVersion(pkgInfo.version)
|
||||
|
||||
proc nimScriptHint*(pkgInfo: PackageInfo) =
|
||||
if not pkgInfo.isNimScript:
|
||||
# TODO: Turn this into a warning.
|
||||
# TODO: Add a URL explaining more.
|
||||
echo("NOTE: The .nimble file for this project could make use of " &
|
||||
"additional features, if converted into the new NimScript format.")
|
||||
|
||||
proc multiSplit(s: string): seq[string] =
|
||||
## Returns ``s`` split by newline and comma characters.
|
||||
|
|
@ -100,9 +133,7 @@ proc multiSplit(s: string): seq[string] =
|
|||
if len(result) < 1:
|
||||
return @[s]
|
||||
|
||||
proc readPackageInfo*(path: string): PackageInfo =
|
||||
result = initPackageInfo()
|
||||
result.mypath = path
|
||||
proc readPackageInfoFromNimble(path: string; result: var PackageInfo) =
|
||||
var fs = newFileStream(path, fmRead)
|
||||
if fs != nil:
|
||||
var p: CfgParser
|
||||
|
|
@ -164,7 +195,72 @@ proc readPackageInfo*(path: string): PackageInfo =
|
|||
close(p)
|
||||
else:
|
||||
raise newException(ValueError, "Cannot open package info: " & path)
|
||||
validatePackageInfo(result, path)
|
||||
|
||||
proc getNameVersion*(pkgpath: string): tuple[name, version: string] =
|
||||
## Splits ``pkgpath`` in the format ``/home/user/.nimble/pkgs/package-0.1``
|
||||
## into ``(packagea, 0.1)``
|
||||
##
|
||||
## Also works for file paths like:
|
||||
## ``/home/user/.nimble/pkgs/package-0.1/package.nimble``
|
||||
|
||||
if pkgPath.splitFile.ext == ".nimble":
|
||||
return getNameVersion(pkgPath.splitPath.head)
|
||||
|
||||
result.name = ""
|
||||
result.version = ""
|
||||
let tail = pkgpath.splitPath.tail
|
||||
if '-' notin tail:
|
||||
result.name = tail
|
||||
return
|
||||
|
||||
for i in countdown(tail.len-1, 0):
|
||||
if tail[i] == '-':
|
||||
result.name = tail[0 .. i-1]
|
||||
result.version = tail[i+1 .. tail.len-1]
|
||||
break
|
||||
|
||||
proc readPackageInfo*(nf: NimbleFile; onlyMinimalInfo=false): PackageInfo =
|
||||
## Reads package info from the specifed Nimble file.
|
||||
##
|
||||
## Attempts to read it using the "old" Nimble ini format first, if that
|
||||
## fails attempts to evaluate it as a nimscript file.
|
||||
##
|
||||
## If both fail then returns an error.
|
||||
##
|
||||
## When ``onlyMinimalInfo`` is true, only the `name` and `version` fields are
|
||||
## populated. The isNimScript field can also be relied on.
|
||||
result = initPackageInfo(nf)
|
||||
|
||||
var success = false
|
||||
var iniError: ref NimbleError
|
||||
# Attempt ini-format first.
|
||||
try:
|
||||
readPackageInfoFromNimble(nf, result)
|
||||
success = true
|
||||
result.isNimScript = false
|
||||
except NimbleError:
|
||||
iniError = (ref NimbleError)(getCurrentException())
|
||||
|
||||
if not success:
|
||||
if onlyMinimalInfo:
|
||||
let tmp = getNameVersion(nf)
|
||||
result.name = tmp.name
|
||||
result.version = tmp.version
|
||||
result.isNimScript = true
|
||||
result.isMinimal = true
|
||||
else:
|
||||
try:
|
||||
readPackageInfoFromNims(nf, result)
|
||||
result.isNimScript = true
|
||||
except NimbleError:
|
||||
let msg = "Could not read package info file in " & nf & ";\n" &
|
||||
" Reading as ini file failed with: \n" &
|
||||
" " & iniError.msg & ".\n" &
|
||||
" Evaluating as NimScript file failed with: \n" &
|
||||
" " & getCurrentExceptionMsg() & "."
|
||||
raise newException(NimbleError, msg)
|
||||
|
||||
validatePackageInfo(result, nf)
|
||||
|
||||
proc optionalField(obj: JsonNode, name: string, default = ""): string =
|
||||
## Queries ``obj`` for the optional ``name`` string.
|
||||
|
|
@ -241,21 +337,31 @@ proc getPackageList*(packagesPath: string): seq[Package] =
|
|||
let pkg: Package = p.fromJson()
|
||||
result.add(pkg)
|
||||
|
||||
proc findNimbleFile*(dir: string): string =
|
||||
proc findNimbleFile*(dir: string; error: bool): NimbleFile =
|
||||
result = ""
|
||||
var hits = 0
|
||||
for kind, path in walkDir(dir):
|
||||
if kind == pcFile and path.splitFile.ext in [".babel", ".nimble"]:
|
||||
if result != "":
|
||||
raise newException(NimbleError,
|
||||
"Only one .nimble file should be present in " & dir)
|
||||
result = path
|
||||
if kind == pcFile:
|
||||
let ext = path.splitFile.ext
|
||||
case ext
|
||||
of ".babel", ".nimble":
|
||||
result = path
|
||||
inc hits
|
||||
else: discard
|
||||
if hits >= 2:
|
||||
raise newException(NimbleError,
|
||||
"Only one .nimble file should be present in " & dir)
|
||||
elif hits == 0:
|
||||
if error:
|
||||
raise newException(NimbleError,
|
||||
"Specified directory does not contain a .nimble file.")
|
||||
else:
|
||||
# TODO: Abstract logging.
|
||||
echo("WARNING: No .nimble file found for ", dir)
|
||||
|
||||
proc getPkgInfo*(dir: string): PackageInfo =
|
||||
## Find the .nimble file in ``dir`` and parses it, returning a PackageInfo.
|
||||
let nimbleFile = findNimbleFile(dir)
|
||||
if nimbleFile == "":
|
||||
raise newException(NimbleError,
|
||||
"Specified directory does not contain a .nimble file.")
|
||||
let nimbleFile = findNimbleFile(dir, true)
|
||||
result = readPackageInfo(nimbleFile)
|
||||
|
||||
proc getInstalledPkgs*(libsDir: string):
|
||||
|
|
@ -266,13 +372,10 @@ proc getInstalledPkgs*(libsDir: string):
|
|||
result = @[]
|
||||
for kind, path in walkDir(libsDir):
|
||||
if kind == pcDir:
|
||||
let nimbleFile = findNimbleFile(path)
|
||||
let nimbleFile = findNimbleFile(path, false)
|
||||
if nimbleFile != "":
|
||||
let meta = readMetaData(path)
|
||||
result.add((readPackageInfo(nimbleFile), meta))
|
||||
else:
|
||||
# TODO: Abstract logging.
|
||||
echo("WARNING: No .nimble file found for ", path)
|
||||
result.add((readPackageInfo(nimbleFile, true), meta))
|
||||
|
||||
proc findPkg*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]],
|
||||
dep: PkgTuple,
|
||||
|
|
@ -284,8 +387,8 @@ proc findPkg*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]],
|
|||
##
|
||||
## **Note**: dep.name here could be a URL, hence the need for pkglist.meta.
|
||||
for pkg in pkglist:
|
||||
if pkg.pkginfo.name.normalize != dep.name.normalize and
|
||||
pkg.meta.url.normalize != dep.name.normalize: continue
|
||||
if cmpIgnoreStyle(pkg.pkginfo.name, dep.name) != 0 and
|
||||
cmpIgnoreStyle(pkg.meta.url, dep.name) != 0: continue
|
||||
if withinRange(newVersion(pkg.pkginfo.version), dep.ver):
|
||||
if not result or newVersion(r.version) < newVersion(pkg.pkginfo.version):
|
||||
r = pkg.pkginfo
|
||||
|
|
@ -298,8 +401,8 @@ proc findAllPkgs*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]],
|
|||
## packages if multiple are found.
|
||||
result = @[]
|
||||
for pkg in pkglist:
|
||||
if pkg.pkginfo.name.normalize != dep.name.normalize and
|
||||
pkg.meta.url.normalize != dep.name.normalize: continue
|
||||
if cmpIgnoreStyle(pkg.pkginfo.name, dep.name) != 0 and
|
||||
cmpIgnoreStyle(pkg.meta.url, dep.name) != 0: continue
|
||||
if withinRange(newVersion(pkg.pkginfo.version), dep.ver):
|
||||
result.add pkg.pkginfo
|
||||
|
||||
|
|
@ -318,22 +421,6 @@ proc getOutputDir*(pkgInfo: PackageInfo, bin: string): string =
|
|||
else:
|
||||
result = pkgInfo.mypath.splitFile.dir / bin
|
||||
|
||||
proc getNameVersion*(pkgpath: string): tuple[name, version: string] =
|
||||
## Splits ``pkgpath`` in the format ``/home/user/.nimble/pkgs/package-0.1``
|
||||
## into ``(packagea, 0.1)``
|
||||
result.name = ""
|
||||
result.version = ""
|
||||
let tail = pkgpath.splitPath.tail
|
||||
if '-' notin tail:
|
||||
result.name = tail
|
||||
return
|
||||
|
||||
for i in countdown(tail.len-1, 0):
|
||||
if tail[i] == '-':
|
||||
result.name = tail[0 .. i-1]
|
||||
result.version = tail[i+1 .. tail.len-1]
|
||||
break
|
||||
|
||||
proc echoPackage*(pkg: Package) =
|
||||
echo(pkg.name & ":")
|
||||
echo(" url: " & pkg.url & " (" & pkg.downloadMethod & ")")
|
||||
|
|
@ -350,8 +437,26 @@ proc getDownloadDirName*(pkg: Package, verRange: VersionRange): string =
|
|||
result.add "_"
|
||||
result.add verSimple
|
||||
|
||||
proc isNimScript*(nf: NimbleFile): bool =
|
||||
readPackageInfo(nf).isNimScript
|
||||
|
||||
when isMainModule:
|
||||
doAssert getNameVersion("/home/user/.nimble/libs/packagea-0.1") ==
|
||||
("packagea", "0.1")
|
||||
doAssert getNameVersion("/home/user/.nimble/libs/package-a-0.1") ==
|
||||
("package-a", "0.1")
|
||||
doAssert getNameVersion("/home/user/.nimble/libs/package-a-0.1/package.nimble") ==
|
||||
("package-a", "0.1")
|
||||
|
||||
validatePackageName("foo_bar")
|
||||
validatePackageName("f_oo_b_a_r")
|
||||
try:
|
||||
validatePackageName("foo__bar")
|
||||
assert false
|
||||
except NimbleError:
|
||||
assert true
|
||||
|
||||
doAssert toValidPackageName("foo__bar") == "foo_bar"
|
||||
doAssert toValidPackageName("jhbasdh!£$@%#^_&*_()qwe") == "jhbasdh_qwe"
|
||||
|
||||
echo("All tests passed!")
|
||||
|
|
@ -33,6 +33,7 @@ type
|
|||
PkgTuple* = tuple[name: string, ver: VersionRange]
|
||||
|
||||
ParseVersionError* = object of ValueError
|
||||
NimbleError* = object of Exception
|
||||
|
||||
proc newVersion*(ver: string): Version = return Version(ver)
|
||||
proc newSpecial*(spe: string): Special = return Special(spe)
|
||||
|
|
@ -198,6 +199,24 @@ proc parseVersionRange*(s: string): VersionRange =
|
|||
"Unexpected char in version range: " & s[i])
|
||||
inc(i)
|
||||
|
||||
|
||||
proc parseRequires*(req: string): PkgTuple =
|
||||
try:
|
||||
if ' ' in req:
|
||||
var i = skipUntil(req, Whitespace)
|
||||
result.name = req[0 .. i].strip
|
||||
result.ver = parseVersionRange(req[i .. req.len-1])
|
||||
elif '#' in req:
|
||||
var i = skipUntil(req, {'#'})
|
||||
result.name = req[0 .. i-1]
|
||||
result.ver = parseVersionRange(req[i .. req.len-1])
|
||||
else:
|
||||
result.name = req.strip
|
||||
result.ver = VersionRange(kind: verAny)
|
||||
except ParseVersionError:
|
||||
raise newException(NimbleError,
|
||||
"Unable to parse dependency version range: " & getCurrentExceptionMsg())
|
||||
|
||||
proc `$`*(verRange: VersionRange): string =
|
||||
case verRange.kind
|
||||
of verLater:
|
||||
|
|
|
|||
|
|
@ -18,6 +18,23 @@ template cd*(dir: string, body: stmt) =
|
|||
proc processOutput(output: string): seq[string] =
|
||||
output.strip.splitLines().filter((x: string) => (x.len > 0))
|
||||
|
||||
test "can install nimscript package":
|
||||
cd "nimscript":
|
||||
check execCmdEx("../" & path & " install -y").exitCode == QuitSuccess
|
||||
|
||||
test "can execute nimscript tasks":
|
||||
cd "nimscript":
|
||||
let (output, exitCode) = execCmdEx("../" & path & " test")
|
||||
let lines = output.strip.splitLines()
|
||||
check exitCode == QuitSuccess
|
||||
check lines[^1] == "10"
|
||||
|
||||
test "can list nimscript tasks":
|
||||
cd "nimscript":
|
||||
let (output, exitCode) = execCmdEx("../" & path & " tasks")
|
||||
check output.strip == "test test description"
|
||||
check exitCode == QuitSuccess
|
||||
|
||||
test "can install packagebin2":
|
||||
check execCmdEx(path &
|
||||
" install -y https://github.com/nimble-test/packagebin2.git").exitCode ==
|
||||
|
|
@ -48,6 +65,11 @@ test "issue #27":
|
|||
cd "issue27":
|
||||
check execCmdEx("../" & path & " install -y").exitCode == QuitSuccess
|
||||
|
||||
test "can list":
|
||||
check execCmdEx(path & " list").exitCode == QuitSuccess
|
||||
|
||||
check execCmdEx(path & " list -i").exitCode == QuitSuccess
|
||||
|
||||
test "can uninstall":
|
||||
block:
|
||||
let (outp, exitCode) = execCmdEx(path & " uninstall -y issue27b")
|
||||
|
|
@ -80,3 +102,5 @@ test "can uninstall":
|
|||
check execCmdEx(path & " uninstall -y PackageA@0.2 issue27b").exitCode ==
|
||||
QuitSuccess
|
||||
check (not dirExists(getHomeDir() / ".nimble" / "pkgs" / "PackageA-0.2.0"))
|
||||
|
||||
check execCmdEx(path & " uninstall -y nimscript").exitCode == QuitSuccess
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue