Moved Nimble-specific Nimscript definitions to nimscriptapi module.
* Moved package info reading procedures to a packageparser module. This is to prevent recu rsive dependencies between nimscriptsupport and packageinfo modules. * Passed the Options object to all procedures which read package info (necessary for nimscriptsupport module, to find the path of the nimscriptapi module) * Introduced an ``isInstalled`` field to the ``PackageInfo`` type. This is a bug fix for g etRealDir: it no longer uses the ``src`` field in the path it returns for installed packag es' PackageInfo objects. * Improved error reporting from NimScript evaluator. * Renamed compiler/options import to ``compiler_options`` in ``nimscriptsupport`` module. * Backward compatibility for .babel packages in getNameVersion proc. * Introduced a getInstalledPkgsMin procedure which does not read package info, but only pr ovides minimal info instead.
This commit is contained in:
parent
84f371982b
commit
ca99ad7d21
9 changed files with 426 additions and 300 deletions
|
|
@ -8,7 +8,7 @@ from sequtils import toSeq
|
||||||
|
|
||||||
import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools,
|
import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools,
|
||||||
nimblepkg/download, nimblepkg/config, nimblepkg/nimbletypes,
|
nimblepkg/download, nimblepkg/config, nimblepkg/nimbletypes,
|
||||||
nimblepkg/publish, nimblepkg/options
|
nimblepkg/publish, nimblepkg/options, nimblepkg/packageparser
|
||||||
|
|
||||||
import nimblepkg/nimscriptsupport
|
import nimblepkg/nimscriptsupport
|
||||||
|
|
||||||
|
|
@ -227,7 +227,7 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[string] =
|
||||||
##
|
##
|
||||||
## Returns the list of paths to pass to the compiler during build phase.
|
## Returns the list of paths to pass to the compiler during build phase.
|
||||||
result = @[]
|
result = @[]
|
||||||
let pkglist = getInstalledPkgs(options.getPkgsDir())
|
let pkglist = getInstalledPkgs(options.getPkgsDir(), options)
|
||||||
var reverseDeps: seq[tuple[name, version: string]] = @[]
|
var reverseDeps: seq[tuple[name, version: string]] = @[]
|
||||||
for dep in pkginfo.requires:
|
for dep in pkginfo.requires:
|
||||||
if dep.name == "nimrod" or dep.name == "nim":
|
if dep.name == "nimrod" or dep.name == "nim":
|
||||||
|
|
@ -328,7 +328,7 @@ proc installFromDir(dir: string, latest: bool, options: Options,
|
||||||
## to the packages this package depends on.
|
## to the packages this package depends on.
|
||||||
## The return value of this function is used by
|
## The return value of this function is used by
|
||||||
## ``processDeps`` to gather a list of paths to pass to the nim compiler.
|
## ``processDeps`` to gather a list of paths to pass to the nim compiler.
|
||||||
var pkgInfo = getPkgInfo(dir)
|
var pkgInfo = getPkgInfo(dir, options)
|
||||||
let realDir = pkgInfo.getRealDir()
|
let realDir = pkgInfo.getRealDir()
|
||||||
let binDir = options.getBinDir()
|
let binDir = options.getBinDir()
|
||||||
let pkgsDir = options.getPkgsDir()
|
let pkgsDir = options.getPkgsDir()
|
||||||
|
|
@ -440,7 +440,8 @@ proc getNimbleTempDir(): string =
|
||||||
result.add($getpid())
|
result.add($getpid())
|
||||||
|
|
||||||
proc downloadPkg(url: string, verRange: VersionRange,
|
proc downloadPkg(url: string, verRange: VersionRange,
|
||||||
downMethod: DownloadMethod): (string, VersionRange) =
|
downMethod: DownloadMethod,
|
||||||
|
options: Options): (string, VersionRange) =
|
||||||
## Downloads the repository as specified by ``url`` and ``verRange`` using
|
## Downloads the repository as specified by ``url`` and ``verRange`` using
|
||||||
## the download method specified.
|
## the download method specified.
|
||||||
##
|
##
|
||||||
|
|
@ -449,7 +450,10 @@ proc downloadPkg(url: string, verRange: VersionRange,
|
||||||
let downloadDir = (getNimbleTempDir() / getDownloadDirName(url, verRange))
|
let downloadDir = (getNimbleTempDir() / getDownloadDirName(url, verRange))
|
||||||
createDir(downloadDir)
|
createDir(downloadDir)
|
||||||
echo("Downloading ", url, " into ", downloadDir, " using ", downMethod, "...")
|
echo("Downloading ", url, " into ", downloadDir, " using ", downMethod, "...")
|
||||||
result = (downloadDir, doDownload(url, downloadDir, verRange, downMethod))
|
result = (
|
||||||
|
downloadDir,
|
||||||
|
doDownload(url, downloadDir, verRange, downMethod, options)
|
||||||
|
)
|
||||||
|
|
||||||
proc getDownloadInfo*(pv: PkgTuple, options: Options,
|
proc getDownloadInfo*(pv: PkgTuple, options: Options,
|
||||||
doPrompt: bool): (DownloadMethod, string) =
|
doPrompt: bool): (DownloadMethod, string) =
|
||||||
|
|
@ -488,7 +492,8 @@ proc install(packages: seq[PkgTuple],
|
||||||
# Install each package.
|
# Install each package.
|
||||||
for pv in packages:
|
for pv in packages:
|
||||||
let (meth, url) = getDownloadInfo(pv, options, doPrompt)
|
let (meth, url) = getDownloadInfo(pv, options, doPrompt)
|
||||||
let (downloadDir, downloadVersion) = downloadPkg(url, pv.ver, meth)
|
let (downloadDir, downloadVersion) =
|
||||||
|
downloadPkg(url, pv.ver, meth, options)
|
||||||
try:
|
try:
|
||||||
result = installFromDir(downloadDir, false, options, url)
|
result = installFromDir(downloadDir, false, options, url)
|
||||||
except BuildFailed:
|
except BuildFailed:
|
||||||
|
|
@ -512,13 +517,13 @@ proc install(packages: seq[PkgTuple],
|
||||||
raise
|
raise
|
||||||
|
|
||||||
proc build(options: Options) =
|
proc build(options: Options) =
|
||||||
var pkgInfo = getPkgInfo(getCurrentDir())
|
var pkgInfo = getPkgInfo(getCurrentDir(), options)
|
||||||
nimScriptHint(pkgInfo)
|
nimScriptHint(pkgInfo)
|
||||||
let paths = processDeps(pkginfo, options)
|
let paths = processDeps(pkginfo, options)
|
||||||
buildFromDir(pkgInfo, paths, false)
|
buildFromDir(pkgInfo, paths, false)
|
||||||
|
|
||||||
proc compile(options: Options) =
|
proc compile(options: Options) =
|
||||||
var pkgInfo = getPkgInfo(getCurrentDir())
|
var pkgInfo = getPkgInfo(getCurrentDir(), options)
|
||||||
nimScriptHint(pkgInfo)
|
nimScriptHint(pkgInfo)
|
||||||
let paths = processDeps(pkginfo, options)
|
let paths = processDeps(pkginfo, options)
|
||||||
let realDir = pkgInfo.getRealDir()
|
let realDir = pkgInfo.getRealDir()
|
||||||
|
|
@ -587,7 +592,7 @@ proc list(options: Options) =
|
||||||
|
|
||||||
proc listInstalled(options: Options) =
|
proc listInstalled(options: Options) =
|
||||||
var h = initTable[string, seq[string]]()
|
var h = initTable[string, seq[string]]()
|
||||||
let pkgs = getInstalledPkgs(options.getPkgsDir())
|
let pkgs = getInstalledPkgs(options.getPkgsDir(), options)
|
||||||
for x in pkgs.items():
|
for x in pkgs.items():
|
||||||
let
|
let
|
||||||
pName = x.pkginfo.name
|
pName = x.pkginfo.name
|
||||||
|
|
@ -627,7 +632,7 @@ proc listPaths(options: Options) =
|
||||||
hasSpec = nimScriptFile.existsFile or
|
hasSpec = nimScriptFile.existsFile or
|
||||||
nimbleFile.existsFile or babelFile.existsFile
|
nimbleFile.existsFile or babelFile.existsFile
|
||||||
if hasSpec:
|
if hasSpec:
|
||||||
var pkgInfo = getPkgInfo(path)
|
var pkgInfo = getPkgInfo(path, options)
|
||||||
var v: VersionAndPath
|
var v: VersionAndPath
|
||||||
v.version = newVersion(pkgInfo.version)
|
v.version = newVersion(pkgInfo.version)
|
||||||
v.path = options.getPkgsDir / (pkgInfo.name & '-' & pkgInfo.version)
|
v.path = options.getPkgsDir / (pkgInfo.name & '-' & pkgInfo.version)
|
||||||
|
|
@ -654,8 +659,8 @@ proc join(x: seq[PkgTuple]; y: string): string =
|
||||||
|
|
||||||
proc dump(options: Options) =
|
proc dump(options: Options) =
|
||||||
let proj = addFileExt(options.action.projName, "nimble")
|
let proj = addFileExt(options.action.projName, "nimble")
|
||||||
let p = if fileExists(proj): readPackageInfo(proj)
|
let p = if fileExists(proj): readPackageInfo(proj, options)
|
||||||
else: getPkgInfo(os.getCurrentDir())
|
else: getPkgInfo(os.getCurrentDir(), options)
|
||||||
echo "name: ", p.name.escape
|
echo "name: ", p.name.escape
|
||||||
echo "version: ", p.version.escape
|
echo "version: ", p.version.escape
|
||||||
echo "author: ", p.author.escape
|
echo "author: ", p.author.escape
|
||||||
|
|
@ -755,7 +760,7 @@ proc uninstall(options: Options) =
|
||||||
# Do some verification.
|
# Do some verification.
|
||||||
for pkgTup in options.action.packages:
|
for pkgTup in options.action.packages:
|
||||||
echo("Looking for ", pkgTup.name, " (", $pkgTup.ver, ")...")
|
echo("Looking for ", pkgTup.name, " (", $pkgTup.ver, ")...")
|
||||||
let installedPkgs = getInstalledPkgs(options.getPkgsDir())
|
let installedPkgs = getInstalledPkgs(options.getPkgsDir(), options)
|
||||||
var pkgList = findAllPkgs(installedPkgs, pkgTup)
|
var pkgList = findAllPkgs(installedPkgs, pkgTup)
|
||||||
if pkgList.len == 0:
|
if pkgList.len == 0:
|
||||||
raise newException(NimbleError, "Package not found")
|
raise newException(NimbleError, "Package not found")
|
||||||
|
|
@ -805,7 +810,7 @@ proc uninstall(options: Options) =
|
||||||
|
|
||||||
proc listTasks(options: Options) =
|
proc listTasks(options: Options) =
|
||||||
let nimbleFile = findNimbleFile(getCurrentDir(), true)
|
let nimbleFile = findNimbleFile(getCurrentDir(), true)
|
||||||
nimscriptsupport.listTasks(nimbleFile)
|
nimscriptsupport.listTasks(nimbleFile, options)
|
||||||
|
|
||||||
proc doAction(options: Options) =
|
proc doAction(options: Options) =
|
||||||
if not existsDir(options.getNimbleDir()):
|
if not existsDir(options.getNimbleDir()):
|
||||||
|
|
@ -836,7 +841,7 @@ proc doAction(options: Options) =
|
||||||
of actionInit:
|
of actionInit:
|
||||||
init(options)
|
init(options)
|
||||||
of actionPublish:
|
of actionPublish:
|
||||||
var pkgInfo = getPkgInfo(getCurrentDir())
|
var pkgInfo = getPkgInfo(getCurrentDir(), options)
|
||||||
publish(pkgInfo)
|
publish(pkgInfo)
|
||||||
of actionDump:
|
of actionDump:
|
||||||
dump(options)
|
dump(options)
|
||||||
|
|
@ -849,10 +854,11 @@ proc doAction(options: Options) =
|
||||||
of actionCustom:
|
of actionCustom:
|
||||||
# Custom command. Attempt to call a NimScript task.
|
# Custom command. Attempt to call a NimScript task.
|
||||||
let nimbleFile = findNimbleFile(getCurrentDir(), true)
|
let nimbleFile = findNimbleFile(getCurrentDir(), true)
|
||||||
if not nimbleFile.isNimScript():
|
## TODO: Optimise this, there are two calls to readPackageInfo here.
|
||||||
|
if not nimbleFile.isNimScript(options):
|
||||||
writeHelp()
|
writeHelp()
|
||||||
|
|
||||||
let execResult = execTask(nimbleFile, options.action.command)
|
let execResult = execTask(nimbleFile, options.action.command, options)
|
||||||
if not execResult.success:
|
if not execResult.success:
|
||||||
echo("FAILURE: Could not find task ", options.action.command, " in ",
|
echo("FAILURE: Could not find task ", options.action.command, " in ",
|
||||||
nimbleFile)
|
nimbleFile)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
import parseutils, os, osproc, strutils, tables, pegs
|
import parseutils, os, osproc, strutils, tables, pegs
|
||||||
|
|
||||||
import packageinfo, version, tools, nimbletypes
|
import packageinfo, packageparser, version, tools, nimbletypes, options
|
||||||
|
|
||||||
type
|
type
|
||||||
DownloadMethod* {.pure.} = enum
|
DownloadMethod* {.pure.} = enum
|
||||||
|
|
@ -136,7 +136,7 @@ proc isURL*(name: string): bool =
|
||||||
name.startsWith(peg" @'://' ")
|
name.startsWith(peg" @'://' ")
|
||||||
|
|
||||||
proc doDownload*(url: string, downloadDir: string, verRange: VersionRange,
|
proc doDownload*(url: string, downloadDir: string, verRange: VersionRange,
|
||||||
downMethod: DownloadMethod): VersionRange =
|
downMethod: DownloadMethod, options: Options): VersionRange =
|
||||||
## Downloads the repository specified by ``url`` using the specified download
|
## Downloads the repository specified by ``url`` using the specified download
|
||||||
## method.
|
## method.
|
||||||
##
|
##
|
||||||
|
|
@ -161,7 +161,7 @@ proc doDownload*(url: string, downloadDir: string, verRange: VersionRange,
|
||||||
proc verifyClone() =
|
proc verifyClone() =
|
||||||
## Makes sure that the downloaded package's version satisfies the requested
|
## Makes sure that the downloaded package's version satisfies the requested
|
||||||
## version range.
|
## version range.
|
||||||
let pkginfo = getPkgInfo(downloadDir)
|
let pkginfo = getPkgInfo(downloadDir, options)
|
||||||
if pkginfo.version.newVersion notin verRange:
|
if pkginfo.version.newVersion notin verRange:
|
||||||
raise newException(NimbleError,
|
raise newException(NimbleError,
|
||||||
"Downloaded package's version does not satisfy requested version " &
|
"Downloaded package's version does not satisfy requested version " &
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ type
|
||||||
mypath*: string ## The path of this .nimble file
|
mypath*: string ## The path of this .nimble file
|
||||||
isNimScript*: bool ## Determines if this pkg info was read from a nims file
|
isNimScript*: bool ## Determines if this pkg info was read from a nims file
|
||||||
isMinimal*: bool
|
isMinimal*: bool
|
||||||
|
isInstalled*: bool ## Determines if the pkg this info belongs to is installed
|
||||||
name*: string
|
name*: string
|
||||||
version*: string
|
version*: string
|
||||||
author*: string
|
author*: string
|
||||||
|
|
|
||||||
30
src/nimblepkg/nimscriptapi.nim
Normal file
30
src/nimblepkg/nimscriptapi.nim
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Copyright (C) Dominik Picheta. All rights reserved.
|
||||||
|
# BSD License. Look at license.txt for more info.
|
||||||
|
|
||||||
|
## This module is implicitly imported in NimScript .nimble files.
|
||||||
|
|
||||||
|
var
|
||||||
|
packageName* = "" ## Set this to the package name. It
|
||||||
|
## is usually not required to do that, nims' filename is
|
||||||
|
## the default.
|
||||||
|
version*: string ## The package's version.
|
||||||
|
author*: string ## The package's author.
|
||||||
|
description*: string ## The package's description.
|
||||||
|
license*: string ## The package's license.
|
||||||
|
srcdir*: string ## The package's source directory.
|
||||||
|
binDir*: string ## The package's binary directory.
|
||||||
|
backend*: string ## The package's backend.
|
||||||
|
|
||||||
|
skipDirs*, skipFiles*, skipExt*, installDirs*, installFiles*,
|
||||||
|
installExt*, bin*: seq[string] = @[] ## Nimble metadata.
|
||||||
|
requiresData*: seq[string] = @[] ## The package's dependencies.
|
||||||
|
|
||||||
|
proc requires*(deps: varargs[string]) =
|
||||||
|
## Call this to set the list of requirements of your Nimble
|
||||||
|
## package.
|
||||||
|
for d in deps: requiresData.add(d)
|
||||||
|
|
||||||
|
template builtin = discard
|
||||||
|
|
||||||
|
proc getPkgDir*(): string =
|
||||||
|
builtin
|
||||||
|
|
@ -6,15 +6,16 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
compiler/ast, compiler/modules, compiler/passes, compiler/passaux,
|
compiler/ast, compiler/modules, compiler/passes, compiler/passaux,
|
||||||
compiler/condsyms, compiler/options, compiler/sem, compiler/semdata,
|
compiler/condsyms, compiler/sem, compiler/semdata,
|
||||||
compiler/llstream, compiler/vm, compiler/vmdef, compiler/commands,
|
compiler/llstream, compiler/vm, compiler/vmdef, compiler/commands,
|
||||||
compiler/msgs, compiler/magicsys, compiler/lists
|
compiler/msgs, compiler/magicsys, compiler/lists
|
||||||
|
|
||||||
from compiler/scriptconfig import setupVM
|
from compiler/scriptconfig import setupVM
|
||||||
from compiler/idents import getIdent
|
from compiler/idents import getIdent
|
||||||
from compiler/astalgo import strTableGet
|
from compiler/astalgo import strTableGet
|
||||||
|
import compiler/options as compiler_options
|
||||||
|
|
||||||
import nimbletypes, version
|
import nimbletypes, version, options, packageinfo
|
||||||
import os, strutils, strtabs, times, osproc
|
import os, strutils, strtabs, times, osproc
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -33,27 +34,27 @@ proc raiseVariableError(ident, typ: string) {.noinline.} =
|
||||||
|
|
||||||
proc isStrLit(n: PNode): bool = n.kind in {nkStrLit..nkTripleStrLit}
|
proc isStrLit(n: PNode): bool = n.kind in {nkStrLit..nkTripleStrLit}
|
||||||
|
|
||||||
proc getGlobal(ident: string): string =
|
proc getGlobal(ident: PSym): string =
|
||||||
let n = vm.globalCtx.getGlobalValue(getSysSym ident)
|
let n = vm.globalCtx.getGlobalValue(ident)
|
||||||
if n.isStrLit:
|
if n.isStrLit:
|
||||||
result = if n.strVal.isNil: "" else: n.strVal
|
result = if n.strVal.isNil: "" else: n.strVal
|
||||||
else:
|
else:
|
||||||
raiseVariableError(ident, "string")
|
raiseVariableError(ident.name.s, "string")
|
||||||
|
|
||||||
proc getGlobalAsSeq(ident: string): seq[string] =
|
proc getGlobalAsSeq(ident: PSym): seq[string] =
|
||||||
let n = vm.globalCtx.getGlobalValue(getSysSym ident)
|
let n = vm.globalCtx.getGlobalValue(ident)
|
||||||
result = @[]
|
result = @[]
|
||||||
if n.kind == nkBracket:
|
if n.kind == nkBracket:
|
||||||
for x in n:
|
for x in n:
|
||||||
if x.isStrLit:
|
if x.isStrLit:
|
||||||
result.add x.strVal
|
result.add x.strVal
|
||||||
else:
|
else:
|
||||||
raiseVariableError(ident, "seq[string]")
|
raiseVariableError(ident.name.s, "seq[string]")
|
||||||
else:
|
else:
|
||||||
raiseVariableError(ident, "seq[string]")
|
raiseVariableError(ident.name.s, "seq[string]")
|
||||||
|
|
||||||
proc extractRequires(result: var seq[PkgTuple]) =
|
proc extractRequires(ident: PSym, result: var seq[PkgTuple]) =
|
||||||
let n = vm.globalCtx.getGlobalValue(getSysSym "requiresData")
|
let n = vm.globalCtx.getGlobalValue(ident)
|
||||||
if n.kind == nkBracket:
|
if n.kind == nkBracket:
|
||||||
for x in n:
|
for x in n:
|
||||||
if x.kind == nkPar and x.len == 2 and x[0].isStrLit and x[1].isStrLit:
|
if x.kind == nkPar and x.len == 2 and x[0].isStrLit and x[1].isStrLit:
|
||||||
|
|
@ -140,13 +141,13 @@ proc setupVM(module: PSym; scriptName: string,
|
||||||
cbconf thisDir:
|
cbconf thisDir:
|
||||||
setResult(a, vthisDir)
|
setResult(a, vthisDir)
|
||||||
cbconf put:
|
cbconf put:
|
||||||
options.setConfigVar(getString(a, 0), getString(a, 1))
|
compiler_options.setConfigVar(getString(a, 0), getString(a, 1))
|
||||||
cbconf get:
|
cbconf get:
|
||||||
setResult(a, options.getConfigVar(a.getString 0))
|
setResult(a, compiler_options.getConfigVar(a.getString 0))
|
||||||
cbconf exists:
|
cbconf exists:
|
||||||
setResult(a, options.existsConfigVar(a.getString 0))
|
setResult(a, compiler_options.existsConfigVar(a.getString 0))
|
||||||
cbconf nimcacheDir:
|
cbconf nimcacheDir:
|
||||||
setResult(a, options.getNimcacheDir())
|
setResult(a, compiler_options.getNimcacheDir())
|
||||||
cbconf paramStr:
|
cbconf paramStr:
|
||||||
setResult(a, os.paramStr(int a.getInt 0))
|
setResult(a, os.paramStr(int a.getInt 0))
|
||||||
cbconf paramCount:
|
cbconf paramCount:
|
||||||
|
|
@ -156,7 +157,7 @@ proc setupVM(module: PSym; scriptName: string,
|
||||||
cbconf cmpIgnoreCase:
|
cbconf cmpIgnoreCase:
|
||||||
setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1))
|
setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1))
|
||||||
cbconf setCommand:
|
cbconf setCommand:
|
||||||
options.command = a.getString 0
|
compiler_options.command = a.getString 0
|
||||||
let arg = a.getString 1
|
let arg = a.getString 1
|
||||||
if arg.len > 0:
|
if arg.len > 0:
|
||||||
gProjectName = arg
|
gProjectName = arg
|
||||||
|
|
@ -165,15 +166,40 @@ proc setupVM(module: PSym; scriptName: string,
|
||||||
except OSError:
|
except OSError:
|
||||||
gProjectFull = gProjectName
|
gProjectFull = gProjectName
|
||||||
cbconf getCommand:
|
cbconf getCommand:
|
||||||
setResult(a, options.command)
|
setResult(a, compiler_options.command)
|
||||||
cbconf switch:
|
cbconf switch:
|
||||||
if not flags.isNil:
|
if not flags.isNil:
|
||||||
flags[a.getString 0] = a.getString 1
|
flags[a.getString 0] = a.getString 1
|
||||||
|
|
||||||
proc execScript(scriptName: string, flags: StringTableRef) =
|
proc findNimscriptApi(options: Options): string =
|
||||||
|
## Returns the directory containing ``nimscriptapi.nim``
|
||||||
|
var inPath = false
|
||||||
|
let pkgs = getInstalledPkgsMin(options.getPkgsDir(), options)
|
||||||
|
var pkg: PackageInfo
|
||||||
|
if pkgs.findPkg(("nimble", newVRAny()), pkg):
|
||||||
|
let pkgDir = pkg.getRealDir()
|
||||||
|
if fileExists(pkgDir / "nimblepkg" / "nimscriptapi.nim"):
|
||||||
|
result = pkgDir
|
||||||
|
inPath = true
|
||||||
|
if not inPath:
|
||||||
|
# Try finding it in exe's path
|
||||||
|
if fileExists(getAppDir() / "nimblepkg" / "nimscriptapi.nim"):
|
||||||
|
result = getAppDir()
|
||||||
|
inPath = true
|
||||||
|
if not inPath:
|
||||||
|
raise newException(NimbleError, "Cannot find nimscriptapi.nim")
|
||||||
|
|
||||||
|
proc execScript(scriptName: string, flags: StringTableRef, options: Options) =
|
||||||
## Executes the specified script.
|
## Executes the specified script.
|
||||||
##
|
##
|
||||||
## No clean up is performed and must be done manually!
|
## No clean up is performed and must be done manually!
|
||||||
|
if "nimblepkg/nimscriptapi" notin compiler_options.implicitIncludes:
|
||||||
|
compiler_options.implicitIncludes.add("nimblepkg/nimscriptapi")
|
||||||
|
|
||||||
|
# Ensure that "nimblepkg/nimscriptapi" is in the PATH.
|
||||||
|
let nimscriptApiPath = findNimscriptApi(options)
|
||||||
|
appendStr(searchPaths, nimscriptApiPath)
|
||||||
|
|
||||||
setDefaultLibpath()
|
setDefaultLibpath()
|
||||||
passes.gIncludeFile = includeModule
|
passes.gIncludeFile = includeModule
|
||||||
passes.gImportModule = importModule
|
passes.gImportModule = importModule
|
||||||
|
|
@ -185,19 +211,28 @@ proc execScript(scriptName: string, flags: StringTableRef) =
|
||||||
registerPass(semPass)
|
registerPass(semPass)
|
||||||
registerPass(evalPass)
|
registerPass(evalPass)
|
||||||
|
|
||||||
appendStr(searchPaths, options.libpath)
|
appendStr(searchPaths, compiler_options.libpath)
|
||||||
|
|
||||||
var m = makeModule(scriptName)
|
var m = makeModule(scriptName)
|
||||||
incl(m.flags, sfMainModule)
|
incl(m.flags, sfMainModule)
|
||||||
vm.globalCtx = setupVM(m, scriptName, flags)
|
vm.globalCtx = setupVM(m, scriptName, flags)
|
||||||
|
|
||||||
|
# Setup builtins defined in nimscriptapi.nim
|
||||||
|
template cbApi(name, body) {.dirty.} =
|
||||||
|
vm.globalCtx.registerCallback "stdlib.system." & astToStr(name),
|
||||||
|
proc (a: VmArgs) =
|
||||||
|
body
|
||||||
|
# TODO: This doesn't work.
|
||||||
|
cbApi getPkgDir:
|
||||||
|
setResult(a, "FOOBAR")
|
||||||
|
|
||||||
compileSystemModule()
|
compileSystemModule()
|
||||||
processModule(m, llStreamOpen(scriptName, fmRead), nil)
|
processModule(m, llStreamOpen(scriptName, fmRead), nil)
|
||||||
|
|
||||||
proc cleanup() =
|
proc cleanup() =
|
||||||
# ensure everything can be called again:
|
# ensure everything can be called again:
|
||||||
options.gProjectName = ""
|
compiler_options.gProjectName = ""
|
||||||
options.command = ""
|
compiler_options.command = ""
|
||||||
resetAllModulesHard()
|
resetAllModulesHard()
|
||||||
clearPasses()
|
clearPasses()
|
||||||
msgs.gErrorMax = 1
|
msgs.gErrorMax = 1
|
||||||
|
|
@ -205,7 +240,8 @@ proc cleanup() =
|
||||||
vm.globalCtx = nil
|
vm.globalCtx = nil
|
||||||
initDefines()
|
initDefines()
|
||||||
|
|
||||||
proc readPackageInfoFromNims*(scriptName: string; result: var PackageInfo) =
|
proc readPackageInfoFromNims*(scriptName: string, options: Options,
|
||||||
|
result: var PackageInfo) =
|
||||||
## Executes the `scriptName` nimscript file. Reads the package information
|
## Executes the `scriptName` nimscript file. Reads the package information
|
||||||
## that it populates.
|
## that it populates.
|
||||||
|
|
||||||
|
|
@ -217,20 +253,35 @@ proc readPackageInfoFromNims*(scriptName: string; result: var PackageInfo) =
|
||||||
# The error counter is incremented after the writeLnHook is invoked.
|
# The error counter is incremented after the writeLnHook is invoked.
|
||||||
if msgs.gErrorCounter > 0:
|
if msgs.gErrorCounter > 0:
|
||||||
raise newException(NimbleError, previousMsg)
|
raise newException(NimbleError, previousMsg)
|
||||||
|
elif previousMsg.len > 0:
|
||||||
|
echo(previousMsg)
|
||||||
|
if output.normalize.startsWith("error"):
|
||||||
|
raise newException(NimbleError, output)
|
||||||
previousMsg = output
|
previousMsg = output
|
||||||
|
|
||||||
|
compiler_options.command = internalCmd
|
||||||
|
|
||||||
# Execute the nimscript file.
|
# Execute the nimscript file.
|
||||||
execScript(scriptName, nil)
|
execScript(scriptName, nil, options)
|
||||||
|
|
||||||
# Extract all the necessary fields populated by the nimscript file.
|
# Extract all the necessary fields populated by the nimscript file.
|
||||||
|
proc getSym(thisModule: PSym, ident: string): PSym =
|
||||||
|
thisModule.tab.strTableGet(getIdent(ident))
|
||||||
|
|
||||||
template trivialField(field) =
|
template trivialField(field) =
|
||||||
result.field = getGlobal(astToStr field)
|
result.field = getGlobal(getSym(thisModule, astToStr field))
|
||||||
|
|
||||||
template trivialFieldSeq(field) =
|
template trivialFieldSeq(field) =
|
||||||
result.field.add getGlobalAsSeq(astToStr field)
|
result.field.add getGlobalAsSeq(getSym(thisModule, astToStr field))
|
||||||
|
|
||||||
|
# Grab the module Sym for .nimble file (nimscriptapi is included in it).
|
||||||
|
let idx = fileInfoIdx(scriptName)
|
||||||
|
let thisModule = getModule(idx)
|
||||||
|
assert(not thisModule.isNil)
|
||||||
|
assert thisModule.kind == skModule
|
||||||
|
|
||||||
# keep reasonable default:
|
# keep reasonable default:
|
||||||
let name = getGlobal"packageName"
|
let name = getGlobal(thisModule.tab.strTableGet(getIdent"packageName"))
|
||||||
if name.len > 0: result.name = name
|
if name.len > 0: result.name = name
|
||||||
|
|
||||||
trivialField version
|
trivialField version
|
||||||
|
|
@ -246,13 +297,13 @@ proc readPackageInfoFromNims*(scriptName: string; result: var PackageInfo) =
|
||||||
trivialFieldSeq installFiles
|
trivialFieldSeq installFiles
|
||||||
trivialFieldSeq installExt
|
trivialFieldSeq installExt
|
||||||
|
|
||||||
extractRequires result.requires
|
extractRequires(getSym(thisModule, "requiresData"), result.requires)
|
||||||
|
|
||||||
let binSeq = getGlobalAsSeq("bin")
|
let binSeq = getGlobalAsSeq(getSym(thisModule, "bin"))
|
||||||
for i in binSeq:
|
for i in binSeq:
|
||||||
result.bin.add(i.addFileExt(ExeExt))
|
result.bin.add(i.addFileExt(ExeExt))
|
||||||
|
|
||||||
let backend = getGlobal("backend")
|
let backend = getGlobal(getSym(thisModule, "backend"))
|
||||||
if backend.len == 0:
|
if backend.len == 0:
|
||||||
result.backend = "c"
|
result.backend = "c"
|
||||||
elif cmpIgnoreStyle(backend, "javascript") == 0:
|
elif cmpIgnoreStyle(backend, "javascript") == 0:
|
||||||
|
|
@ -262,19 +313,22 @@ proc readPackageInfoFromNims*(scriptName: string; result: var PackageInfo) =
|
||||||
|
|
||||||
cleanup()
|
cleanup()
|
||||||
|
|
||||||
proc execTask*(scriptName, taskName: string): ExecutionResult =
|
proc execTask*(scriptName, taskName: string,
|
||||||
|
options: Options): ExecutionResult =
|
||||||
## Executes the specified task in the specified script.
|
## Executes the specified task in the specified script.
|
||||||
##
|
##
|
||||||
## `scriptName` should be a filename pointing to the nimscript file.
|
## `scriptName` should be a filename pointing to the nimscript file.
|
||||||
result.success = true
|
result.success = true
|
||||||
result.flags = newStringTable()
|
result.flags = newStringTable()
|
||||||
options.command = internalCmd
|
compiler_options.command = internalCmd
|
||||||
echo("Executing task ", taskName, " in ", scriptName)
|
echo("Executing task ", taskName, " in ", scriptName)
|
||||||
|
|
||||||
execScript(scriptName, result.flags)
|
execScript(scriptName, result.flags, options)
|
||||||
# Explicitly execute the task procedure, instead of relying on hack.
|
# Explicitly execute the task procedure, instead of relying on hack.
|
||||||
assert vm.globalCtx.module.kind == skModule
|
let idx = fileInfoIdx(scriptName)
|
||||||
let prc = vm.globalCtx.module.tab.strTableGet(getIdent(taskName & "Task"))
|
let thisModule = getModule(idx)
|
||||||
|
assert thisModule.kind == skModule
|
||||||
|
let prc = thisModule.tab.strTableGet(getIdent(taskName & "Task"))
|
||||||
if prc.isNil:
|
if prc.isNil:
|
||||||
# Procedure not defined in the NimScript module.
|
# Procedure not defined in the NimScript module.
|
||||||
result.success = false
|
result.success = false
|
||||||
|
|
@ -282,27 +336,27 @@ proc execTask*(scriptName, taskName: string): ExecutionResult =
|
||||||
discard vm.globalCtx.execProc(prc, [])
|
discard vm.globalCtx.execProc(prc, [])
|
||||||
|
|
||||||
# Read the command, arguments and flags set by the executed task.
|
# Read the command, arguments and flags set by the executed task.
|
||||||
result.command = options.command
|
result.command = compiler_options.command
|
||||||
result.arguments = @[]
|
result.arguments = @[]
|
||||||
for arg in options.gProjectName.split():
|
for arg in compiler_options.gProjectName.split():
|
||||||
result.arguments.add(arg)
|
result.arguments.add(arg)
|
||||||
|
|
||||||
cleanup()
|
cleanup()
|
||||||
|
|
||||||
proc getNimScriptCommand(): string =
|
proc getNimScriptCommand(): string =
|
||||||
options.command
|
compiler_options.command
|
||||||
|
|
||||||
proc setNimScriptCommand(command: string) =
|
proc setNimScriptCommand(command: string) =
|
||||||
options.command = command
|
compiler_options.command = command
|
||||||
|
|
||||||
proc hasTaskRequestedCommand*(execResult: ExecutionResult): bool =
|
proc hasTaskRequestedCommand*(execResult: ExecutionResult): bool =
|
||||||
## Determines whether the last executed task used ``setCommand``
|
## Determines whether the last executed task used ``setCommand``
|
||||||
return execResult.command != internalCmd
|
return execResult.command != internalCmd
|
||||||
|
|
||||||
proc listTasks*(scriptName: string) =
|
proc listTasks*(scriptName: string, options: Options) =
|
||||||
setNimScriptCommand("help")
|
setNimScriptCommand("help")
|
||||||
|
|
||||||
execScript(scriptName, nil)
|
execScript(scriptName, nil, options)
|
||||||
# TODO: Make the 'task' template generate explicit data structure containing
|
# TODO: Make the 'task' template generate explicit data structure containing
|
||||||
# all the task names + descriptions.
|
# all the task names + descriptions.
|
||||||
cleanup()
|
cleanup()
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
import json, strutils, os, parseopt, strtabs
|
import json, strutils, os, parseopt, strtabs
|
||||||
|
|
||||||
import nimblepkg/config, nimblepkg/version, nimblepkg/nimscriptsupport,
|
import nimblepkg/config, nimblepkg/version,
|
||||||
nimblepkg/tools
|
nimblepkg/tools
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
# Copyright (C) Dominik Picheta. All rights reserved.
|
# Copyright (C) Dominik Picheta. All rights reserved.
|
||||||
# BSD License. Look at license.txt for more info.
|
# BSD License. Look at license.txt for more info.
|
||||||
import parsecfg, json, streams, strutils, parseutils, os
|
import parsecfg, json, streams, strutils, parseutils, os
|
||||||
import version, tools, nimbletypes, nimscriptsupport
|
import version, tools, nimbletypes, options
|
||||||
|
|
||||||
when not declared(system.map):
|
|
||||||
from sequtils import map
|
|
||||||
|
|
||||||
type
|
type
|
||||||
Package* = object
|
Package* = object
|
||||||
|
|
@ -23,12 +20,7 @@ type
|
||||||
MetaData* = object
|
MetaData* = object
|
||||||
url*: string
|
url*: string
|
||||||
|
|
||||||
NimbleFile* = string
|
proc initPackageInfo*(path: string): PackageInfo =
|
||||||
|
|
||||||
ValidationError* = object of NimbleError
|
|
||||||
warnInstalled*: bool # Determines whether to show a warning for installed pkgs
|
|
||||||
|
|
||||||
proc initPackageInfo(path: string): PackageInfo =
|
|
||||||
result.mypath = path
|
result.mypath = path
|
||||||
# reasonable default:
|
# reasonable default:
|
||||||
result.name = path.splitFile.name
|
result.name = path.splitFile.name
|
||||||
|
|
@ -48,37 +40,6 @@ proc initPackageInfo(path: string): PackageInfo =
|
||||||
result.binDir = ""
|
result.binDir = ""
|
||||||
result.backend = "c"
|
result.backend = "c"
|
||||||
|
|
||||||
proc newValidationError(msg: string, warnInstalled: bool): ref ValidationError =
|
|
||||||
result = newException(ValidationError, msg)
|
|
||||||
result.warnInstalled = warnInstalled
|
|
||||||
|
|
||||||
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 newValidationError(name &
|
|
||||||
"\"$1\" is an invalid package name: cannot begin with $2" %
|
|
||||||
[name, $name[0]], true)
|
|
||||||
|
|
||||||
var prevWasUnderscore = false
|
|
||||||
for c in name:
|
|
||||||
case c
|
|
||||||
of '_':
|
|
||||||
if prevWasUnderscore:
|
|
||||||
raise newValidationError(
|
|
||||||
"$1 is an invalid package name: cannot contain \"__\"" % name, true)
|
|
||||||
prevWasUnderscore = true
|
|
||||||
of AllChars - IdentChars:
|
|
||||||
raise newValidationError(
|
|
||||||
"$1 is an invalid package name: cannot contain '$2'" % [name, $c],
|
|
||||||
true)
|
|
||||||
else:
|
|
||||||
prevWasUnderscore = false
|
|
||||||
|
|
||||||
proc toValidPackageName*(name: string): string =
|
proc toValidPackageName*(name: string): string =
|
||||||
result = ""
|
result = ""
|
||||||
for c in name:
|
for c in name:
|
||||||
|
|
@ -88,128 +49,6 @@ proc toValidPackageName*(name: string): string =
|
||||||
of AllChars - IdentChars - {'-'}: discard
|
of AllChars - IdentChars - {'-'}: discard
|
||||||
else: result.add(c)
|
else: result.add(c)
|
||||||
|
|
||||||
proc validateVersion*(ver: string) =
|
|
||||||
for c in ver:
|
|
||||||
if c notin ({'.'} + Digits):
|
|
||||||
raise newValidationError(
|
|
||||||
"Version may only consist of numbers and the '.' character " &
|
|
||||||
"but found '" & c & "'.", false)
|
|
||||||
|
|
||||||
proc validatePackageInfo(pkgInfo: PackageInfo, path: string) =
|
|
||||||
if pkgInfo.name == "":
|
|
||||||
raise newValidationError("Incorrect .nimble file: " & path &
|
|
||||||
" does not contain a name field.", false)
|
|
||||||
|
|
||||||
if pkgInfo.name.normalize != path.splitFile.name.normalize:
|
|
||||||
raise newValidationError(
|
|
||||||
"The .nimble file name must match name specified inside " & path, true)
|
|
||||||
|
|
||||||
if pkgInfo.version == "":
|
|
||||||
raise newValidationError("Incorrect .nimble file: " & path &
|
|
||||||
" does not contain a version field.", false)
|
|
||||||
|
|
||||||
if not pkgInfo.isMinimal:
|
|
||||||
if pkgInfo.author == "":
|
|
||||||
raise newValidationError("Incorrect .nimble file: " & path &
|
|
||||||
" does not contain an author field.", false)
|
|
||||||
if pkgInfo.description == "":
|
|
||||||
raise newValidationError("Incorrect .nimble file: " & path &
|
|
||||||
" does not contain a description field.", false)
|
|
||||||
if pkgInfo.license == "":
|
|
||||||
raise newValidationError("Incorrect .nimble file: " & path &
|
|
||||||
" does not contain a license field.", false)
|
|
||||||
if pkgInfo.backend notin ["c", "cc", "objc", "cpp", "js"]:
|
|
||||||
raise newValidationError("'" & pkgInfo.backend &
|
|
||||||
"' is an invalid backend.", false)
|
|
||||||
|
|
||||||
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.
|
|
||||||
##
|
|
||||||
## Before returning, all individual entries are stripped of whitespace and
|
|
||||||
## also empty entries are purged from the list. If after all the cleanups are
|
|
||||||
## done no entries are found in the list, the proc returns a sequence with
|
|
||||||
## the original string as the only entry.
|
|
||||||
result = split(s, {char(0x0A), char(0x0D), ','})
|
|
||||||
map(result, proc(x: var string) = x = x.strip())
|
|
||||||
for i in countdown(result.len()-1, 0):
|
|
||||||
if len(result[i]) < 1:
|
|
||||||
result.del(i)
|
|
||||||
# Huh, nothing to return? Return given input.
|
|
||||||
if len(result) < 1:
|
|
||||||
return @[s]
|
|
||||||
|
|
||||||
proc readPackageInfoFromNimble(path: string; result: var PackageInfo) =
|
|
||||||
var fs = newFileStream(path, fmRead)
|
|
||||||
if fs != nil:
|
|
||||||
var p: CfgParser
|
|
||||||
open(p, fs, path)
|
|
||||||
var currentSection = ""
|
|
||||||
while true:
|
|
||||||
var ev = next(p)
|
|
||||||
case ev.kind
|
|
||||||
of cfgEof:
|
|
||||||
break
|
|
||||||
of cfgSectionStart:
|
|
||||||
currentSection = ev.section
|
|
||||||
of cfgKeyValuePair:
|
|
||||||
case currentSection.normalize
|
|
||||||
of "package":
|
|
||||||
case ev.key.normalize
|
|
||||||
of "name": result.name = ev.value
|
|
||||||
of "version": result.version = ev.value
|
|
||||||
of "author": result.author = ev.value
|
|
||||||
of "description": result.description = ev.value
|
|
||||||
of "license": result.license = ev.value
|
|
||||||
of "srcdir": result.srcDir = ev.value
|
|
||||||
of "bindir": result.binDir = ev.value
|
|
||||||
of "skipdirs":
|
|
||||||
result.skipDirs.add(ev.value.multiSplit)
|
|
||||||
of "skipfiles":
|
|
||||||
result.skipFiles.add(ev.value.multiSplit)
|
|
||||||
of "skipext":
|
|
||||||
result.skipExt.add(ev.value.multiSplit)
|
|
||||||
of "installdirs":
|
|
||||||
result.installDirs.add(ev.value.multiSplit)
|
|
||||||
of "installfiles":
|
|
||||||
result.installFiles.add(ev.value.multiSplit)
|
|
||||||
of "installext":
|
|
||||||
result.installExt.add(ev.value.multiSplit)
|
|
||||||
of "bin":
|
|
||||||
for i in ev.value.multiSplit:
|
|
||||||
result.bin.add(i.addFileExt(ExeExt))
|
|
||||||
of "backend":
|
|
||||||
result.backend = ev.value.toLower()
|
|
||||||
case result.backend.normalize
|
|
||||||
of "javascript": result.backend = "js"
|
|
||||||
else: discard
|
|
||||||
else:
|
|
||||||
raise newException(NimbleError, "Invalid field: " & ev.key)
|
|
||||||
of "deps", "dependencies":
|
|
||||||
case ev.key.normalize
|
|
||||||
of "requires":
|
|
||||||
for v in ev.value.multiSplit:
|
|
||||||
result.requires.add(parseRequires(v.strip))
|
|
||||||
else:
|
|
||||||
raise newException(NimbleError, "Invalid field: " & ev.key)
|
|
||||||
else: raise newException(NimbleError,
|
|
||||||
"Invalid section: " & currentSection)
|
|
||||||
of cfgOption: raise newException(NimbleError,
|
|
||||||
"Invalid package info, should not contain --" & ev.value)
|
|
||||||
of cfgError:
|
|
||||||
raise newException(NimbleError, "Error parsing .nimble file: " & ev.msg)
|
|
||||||
close(p)
|
|
||||||
else:
|
|
||||||
raise newException(ValueError, "Cannot open package info: " & path)
|
|
||||||
|
|
||||||
proc getNameVersion*(pkgpath: string): tuple[name, version: string] =
|
proc getNameVersion*(pkgpath: string): tuple[name, version: string] =
|
||||||
## Splits ``pkgpath`` in the format ``/home/user/.nimble/pkgs/package-0.1``
|
## Splits ``pkgpath`` in the format ``/home/user/.nimble/pkgs/package-0.1``
|
||||||
## into ``(packagea, 0.1)``
|
## into ``(packagea, 0.1)``
|
||||||
|
|
@ -217,7 +56,7 @@ proc getNameVersion*(pkgpath: string): tuple[name, version: string] =
|
||||||
## Also works for file paths like:
|
## Also works for file paths like:
|
||||||
## ``/home/user/.nimble/pkgs/package-0.1/package.nimble``
|
## ``/home/user/.nimble/pkgs/package-0.1/package.nimble``
|
||||||
|
|
||||||
if pkgPath.splitFile.ext == ".nimble":
|
if pkgPath.splitFile.ext == ".nimble" or pkgPath.splitFile.ext == ".babel":
|
||||||
return getNameVersion(pkgPath.splitPath.head)
|
return getNameVersion(pkgPath.splitPath.head)
|
||||||
|
|
||||||
result.name = ""
|
result.name = ""
|
||||||
|
|
@ -233,51 +72,6 @@ proc getNameVersion*(pkgpath: string): tuple[name, version: string] =
|
||||||
result.version = tail[i+1 .. tail.len-1]
|
result.version = tail[i+1 .. tail.len-1]
|
||||||
break
|
break
|
||||||
|
|
||||||
proc readPackageInfo*(nf: NimbleFile; onlyMinimalInfo=false): PackageInfo =
|
|
||||||
## Reads package info from the specified 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)
|
|
||||||
|
|
||||||
validatePackageName(nf.splitFile.name)
|
|
||||||
|
|
||||||
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 =
|
proc optionalField(obj: JsonNode, name: string, default = ""): string =
|
||||||
## Queries ``obj`` for the optional ``name`` string.
|
## Queries ``obj`` for the optional ``name`` string.
|
||||||
##
|
##
|
||||||
|
|
@ -353,7 +147,7 @@ proc getPackageList*(packagesPath: string): seq[Package] =
|
||||||
let pkg: Package = p.fromJson()
|
let pkg: Package = p.fromJson()
|
||||||
result.add(pkg)
|
result.add(pkg)
|
||||||
|
|
||||||
proc findNimbleFile*(dir: string; error: bool): NimbleFile =
|
proc findNimbleFile*(dir: string; error: bool): string =
|
||||||
result = ""
|
result = ""
|
||||||
var hits = 0
|
var hits = 0
|
||||||
for kind, path in walkDir(dir):
|
for kind, path in walkDir(dir):
|
||||||
|
|
@ -375,14 +169,11 @@ proc findNimbleFile*(dir: string; error: bool): NimbleFile =
|
||||||
# TODO: Abstract logging.
|
# TODO: Abstract logging.
|
||||||
echo("WARNING: No .nimble file found for ", dir)
|
echo("WARNING: No .nimble file found for ", dir)
|
||||||
|
|
||||||
proc getPkgInfo*(dir: string): PackageInfo =
|
proc getInstalledPkgsMin*(libsDir: string, options: Options):
|
||||||
## Find the .nimble file in ``dir`` and parses it, returning a PackageInfo.
|
|
||||||
let nimbleFile = findNimbleFile(dir, true)
|
|
||||||
result = readPackageInfo(nimbleFile)
|
|
||||||
|
|
||||||
proc getInstalledPkgs*(libsDir: string):
|
|
||||||
seq[tuple[pkginfo: PackageInfo, meta: MetaData]] =
|
seq[tuple[pkginfo: PackageInfo, meta: MetaData]] =
|
||||||
## Gets a list of installed packages.
|
## 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``.
|
||||||
##
|
##
|
||||||
## ``libsDir`` is in most cases: ~/.nimble/pkgs/
|
## ``libsDir`` is in most cases: ~/.nimble/pkgs/
|
||||||
result = @[]
|
result = @[]
|
||||||
|
|
@ -391,23 +182,13 @@ proc getInstalledPkgs*(libsDir: string):
|
||||||
let nimbleFile = findNimbleFile(path, false)
|
let nimbleFile = findNimbleFile(path, false)
|
||||||
if nimbleFile != "":
|
if nimbleFile != "":
|
||||||
let meta = readMetaData(path)
|
let meta = readMetaData(path)
|
||||||
try:
|
let (name, version) = getNameVersion(nimbleFile)
|
||||||
result.add((readPackageInfo(nimbleFile, true), meta))
|
var pkg = initPackageInfo(nimbleFile)
|
||||||
except ValidationError:
|
pkg.name = name
|
||||||
let exc = (ref ValidationError)(getCurrentException())
|
pkg.version = version
|
||||||
if exc.warnInstalled:
|
pkg.isMinimal = true
|
||||||
echo("WARNING: Unable to read package info for " & path & "\n" &
|
pkg.isInstalled = true
|
||||||
" Package did not pass validation: " & exc.msg)
|
result.add((pkg, meta))
|
||||||
else:
|
|
||||||
exc.msg = "Unable to read package info for " & path & "\n" &
|
|
||||||
" Package did not pass validation: " & exc.msg
|
|
||||||
raise exc
|
|
||||||
except:
|
|
||||||
let exc = getCurrentException()
|
|
||||||
exc.msg = "Unable to read package info for " & path & "\n" &
|
|
||||||
" Error: " & exc.msg
|
|
||||||
raise exc
|
|
||||||
|
|
||||||
|
|
||||||
proc findPkg*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]],
|
proc findPkg*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]],
|
||||||
dep: PkgTuple,
|
dep: PkgTuple,
|
||||||
|
|
@ -441,7 +222,7 @@ proc findAllPkgs*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]],
|
||||||
proc getRealDir*(pkgInfo: PackageInfo): string =
|
proc getRealDir*(pkgInfo: PackageInfo): string =
|
||||||
## Returns the ``pkgInfo.srcDir`` or the .mypath directory if package does
|
## Returns the ``pkgInfo.srcDir`` or the .mypath directory if package does
|
||||||
## not specify the src dir.
|
## not specify the src dir.
|
||||||
if pkgInfo.srcDir != "":
|
if pkgInfo.srcDir != "" and not pkgInfo.isInstalled:
|
||||||
result = pkgInfo.mypath.splitFile.dir / pkgInfo.srcDir
|
result = pkgInfo.mypath.splitFile.dir / pkgInfo.srcDir
|
||||||
else:
|
else:
|
||||||
result = pkgInfo.mypath.splitFile.dir
|
result = pkgInfo.mypath.splitFile.dir
|
||||||
|
|
@ -469,9 +250,6 @@ proc getDownloadDirName*(pkg: Package, verRange: VersionRange): string =
|
||||||
result.add "_"
|
result.add "_"
|
||||||
result.add verSimple
|
result.add verSimple
|
||||||
|
|
||||||
proc isNimScript*(nf: NimbleFile): bool =
|
|
||||||
result = readPackageInfo(nf).isNimScript
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
doAssert getNameVersion("/home/user/.nimble/libs/packagea-0.1") ==
|
doAssert getNameVersion("/home/user/.nimble/libs/packagea-0.1") ==
|
||||||
("packagea", "0.1")
|
("packagea", "0.1")
|
||||||
|
|
|
||||||
254
src/nimblepkg/packageparser.nim
Normal file
254
src/nimblepkg/packageparser.nim
Normal file
|
|
@ -0,0 +1,254 @@
|
||||||
|
# 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, nimscriptsupport, options, packageinfo
|
||||||
|
|
||||||
|
## Contains procedures for parsing .nimble files. Moved here from ``packageinfo``
|
||||||
|
## because it depends on ``nimscriptsupport`` (``nimscriptsupport`` also
|
||||||
|
## depends on other procedures in ``packageinfo``.
|
||||||
|
|
||||||
|
when not declared(system.map):
|
||||||
|
from sequtils import map
|
||||||
|
|
||||||
|
type
|
||||||
|
NimbleFile* = string
|
||||||
|
|
||||||
|
ValidationError* = object of NimbleError
|
||||||
|
warnInstalled*: bool # Determines whether to show a warning for installed pkgs
|
||||||
|
|
||||||
|
proc newValidationError(msg: string, warnInstalled: bool): ref ValidationError =
|
||||||
|
result = newException(ValidationError, msg)
|
||||||
|
result.warnInstalled = warnInstalled
|
||||||
|
|
||||||
|
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 newValidationError(name &
|
||||||
|
"\"$1\" is an invalid package name: cannot begin with $2" %
|
||||||
|
[name, $name[0]], true)
|
||||||
|
|
||||||
|
var prevWasUnderscore = false
|
||||||
|
for c in name:
|
||||||
|
case c
|
||||||
|
of '_':
|
||||||
|
if prevWasUnderscore:
|
||||||
|
raise newValidationError(
|
||||||
|
"$1 is an invalid package name: cannot contain \"__\"" % name, true)
|
||||||
|
prevWasUnderscore = true
|
||||||
|
of AllChars - IdentChars:
|
||||||
|
raise newValidationError(
|
||||||
|
"$1 is an invalid package name: cannot contain '$2'" % [name, $c],
|
||||||
|
true)
|
||||||
|
else:
|
||||||
|
prevWasUnderscore = false
|
||||||
|
|
||||||
|
proc validateVersion*(ver: string) =
|
||||||
|
for c in ver:
|
||||||
|
if c notin ({'.'} + Digits):
|
||||||
|
raise newValidationError(
|
||||||
|
"Version may only consist of numbers and the '.' character " &
|
||||||
|
"but found '" & c & "'.", false)
|
||||||
|
|
||||||
|
proc validatePackageInfo(pkgInfo: PackageInfo, path: string) =
|
||||||
|
if pkgInfo.name == "":
|
||||||
|
raise newValidationError("Incorrect .nimble file: " & path &
|
||||||
|
" does not contain a name field.", false)
|
||||||
|
|
||||||
|
if pkgInfo.name.normalize != path.splitFile.name.normalize:
|
||||||
|
raise newValidationError(
|
||||||
|
"The .nimble file name must match name specified inside " & path, true)
|
||||||
|
|
||||||
|
if pkgInfo.version == "":
|
||||||
|
raise newValidationError("Incorrect .nimble file: " & path &
|
||||||
|
" does not contain a version field.", false)
|
||||||
|
|
||||||
|
if not pkgInfo.isMinimal:
|
||||||
|
if pkgInfo.author == "":
|
||||||
|
raise newValidationError("Incorrect .nimble file: " & path &
|
||||||
|
" does not contain an author field.", false)
|
||||||
|
if pkgInfo.description == "":
|
||||||
|
raise newValidationError("Incorrect .nimble file: " & path &
|
||||||
|
" does not contain a description field.", false)
|
||||||
|
if pkgInfo.license == "":
|
||||||
|
raise newValidationError("Incorrect .nimble file: " & path &
|
||||||
|
" does not contain a license field.", false)
|
||||||
|
if pkgInfo.backend notin ["c", "cc", "objc", "cpp", "js"]:
|
||||||
|
raise newValidationError("'" & pkgInfo.backend &
|
||||||
|
"' is an invalid backend.", false)
|
||||||
|
|
||||||
|
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.
|
||||||
|
##
|
||||||
|
## Before returning, all individual entries are stripped of whitespace and
|
||||||
|
## also empty entries are purged from the list. If after all the cleanups are
|
||||||
|
## done no entries are found in the list, the proc returns a sequence with
|
||||||
|
## the original string as the only entry.
|
||||||
|
result = split(s, {char(0x0A), char(0x0D), ','})
|
||||||
|
map(result, proc(x: var string) = x = x.strip())
|
||||||
|
for i in countdown(result.len()-1, 0):
|
||||||
|
if len(result[i]) < 1:
|
||||||
|
result.del(i)
|
||||||
|
# Huh, nothing to return? Return given input.
|
||||||
|
if len(result) < 1:
|
||||||
|
return @[s]
|
||||||
|
|
||||||
|
proc readPackageInfoFromNimble(path: string; result: var PackageInfo) =
|
||||||
|
var fs = newFileStream(path, fmRead)
|
||||||
|
if fs != nil:
|
||||||
|
var p: CfgParser
|
||||||
|
open(p, fs, path)
|
||||||
|
var currentSection = ""
|
||||||
|
while true:
|
||||||
|
var ev = next(p)
|
||||||
|
case ev.kind
|
||||||
|
of cfgEof:
|
||||||
|
break
|
||||||
|
of cfgSectionStart:
|
||||||
|
currentSection = ev.section
|
||||||
|
of cfgKeyValuePair:
|
||||||
|
case currentSection.normalize
|
||||||
|
of "package":
|
||||||
|
case ev.key.normalize
|
||||||
|
of "name": result.name = ev.value
|
||||||
|
of "version": result.version = ev.value
|
||||||
|
of "author": result.author = ev.value
|
||||||
|
of "description": result.description = ev.value
|
||||||
|
of "license": result.license = ev.value
|
||||||
|
of "srcdir": result.srcDir = ev.value
|
||||||
|
of "bindir": result.binDir = ev.value
|
||||||
|
of "skipdirs":
|
||||||
|
result.skipDirs.add(ev.value.multiSplit)
|
||||||
|
of "skipfiles":
|
||||||
|
result.skipFiles.add(ev.value.multiSplit)
|
||||||
|
of "skipext":
|
||||||
|
result.skipExt.add(ev.value.multiSplit)
|
||||||
|
of "installdirs":
|
||||||
|
result.installDirs.add(ev.value.multiSplit)
|
||||||
|
of "installfiles":
|
||||||
|
result.installFiles.add(ev.value.multiSplit)
|
||||||
|
of "installext":
|
||||||
|
result.installExt.add(ev.value.multiSplit)
|
||||||
|
of "bin":
|
||||||
|
for i in ev.value.multiSplit:
|
||||||
|
result.bin.add(i.addFileExt(ExeExt))
|
||||||
|
of "backend":
|
||||||
|
result.backend = ev.value.toLower()
|
||||||
|
case result.backend.normalize
|
||||||
|
of "javascript": result.backend = "js"
|
||||||
|
else: discard
|
||||||
|
else:
|
||||||
|
raise newException(NimbleError, "Invalid field: " & ev.key)
|
||||||
|
of "deps", "dependencies":
|
||||||
|
case ev.key.normalize
|
||||||
|
of "requires":
|
||||||
|
for v in ev.value.multiSplit:
|
||||||
|
result.requires.add(parseRequires(v.strip))
|
||||||
|
else:
|
||||||
|
raise newException(NimbleError, "Invalid field: " & ev.key)
|
||||||
|
else: raise newException(NimbleError,
|
||||||
|
"Invalid section: " & currentSection)
|
||||||
|
of cfgOption: raise newException(NimbleError,
|
||||||
|
"Invalid package info, should not contain --" & ev.value)
|
||||||
|
of cfgError:
|
||||||
|
raise newException(NimbleError, "Error parsing .nimble file: " & ev.msg)
|
||||||
|
close(p)
|
||||||
|
else:
|
||||||
|
raise newException(ValueError, "Cannot open package info: " & path)
|
||||||
|
|
||||||
|
proc readPackageInfo*(nf: NimbleFile, options: Options,
|
||||||
|
onlyMinimalInfo=false): PackageInfo =
|
||||||
|
## Reads package info from the specified 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)
|
||||||
|
|
||||||
|
validatePackageName(nf.splitFile.name)
|
||||||
|
|
||||||
|
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, options, 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 getPkgInfo*(dir: string, options: Options): PackageInfo =
|
||||||
|
## Find the .nimble file in ``dir`` and parses it, returning a PackageInfo.
|
||||||
|
let nimbleFile = findNimbleFile(dir, true)
|
||||||
|
result = readPackageInfo(nimbleFile, options)
|
||||||
|
|
||||||
|
proc getInstalledPkgs*(libsDir: string, options: Options):
|
||||||
|
seq[tuple[pkginfo: PackageInfo, meta: MetaData]] =
|
||||||
|
## Gets a list of installed packages.
|
||||||
|
##
|
||||||
|
## ``libsDir`` is in most cases: ~/.nimble/pkgs/
|
||||||
|
result = @[]
|
||||||
|
for kind, path in walkDir(libsDir):
|
||||||
|
if kind == pcDir:
|
||||||
|
let nimbleFile = findNimbleFile(path, false)
|
||||||
|
if nimbleFile != "":
|
||||||
|
let meta = readMetaData(path)
|
||||||
|
try:
|
||||||
|
var pkg = readPackageInfo(nimbleFile, options, true)
|
||||||
|
pkg.isInstalled = true
|
||||||
|
result.add((pkg, meta))
|
||||||
|
except ValidationError:
|
||||||
|
let exc = (ref ValidationError)(getCurrentException())
|
||||||
|
if exc.warnInstalled:
|
||||||
|
echo("WARNING: Unable to read package info for " & path & "\n" &
|
||||||
|
" Package did not pass validation: " & exc.msg)
|
||||||
|
else:
|
||||||
|
exc.msg = "Unable to read package info for " & path & "\n" &
|
||||||
|
" Package did not pass validation: " & exc.msg
|
||||||
|
raise exc
|
||||||
|
except:
|
||||||
|
let exc = getCurrentException()
|
||||||
|
exc.msg = "Unable to read package info for " & path & "\n" &
|
||||||
|
" Error: " & exc.msg
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
proc isNimScript*(nf: string, options: Options): bool =
|
||||||
|
result = readPackageInfo(nf, options).isNimScript
|
||||||
|
|
@ -19,4 +19,7 @@ task c_test, "Testing `setCommand \"c\", \"nimscript.nim\"`":
|
||||||
|
|
||||||
task cr, "Testing `nimble c -r nimscript.nim` via setCommand":
|
task cr, "Testing `nimble c -r nimscript.nim` via setCommand":
|
||||||
--r
|
--r
|
||||||
setCommand "c", "nimscript.nim"
|
setCommand "c", "nimscript.nim"
|
||||||
|
|
||||||
|
task api, "Testing nimscriptapi module functionality":
|
||||||
|
echo(getPkgDir())
|
||||||
Loading…
Add table
Add a link
Reference in a new issue