Merge branch 'mybr'
- merged changes from my working branch into master
This commit is contained in:
commit
1ba199a1f6
9 changed files with 350 additions and 338 deletions
276
src/nimble.nim
276
src/nimble.nim
|
|
@ -7,39 +7,39 @@ import httpclient, parseopt, os, strutils, osproc, pegs, tables, parseutils,
|
|||
from sequtils import toSeq
|
||||
|
||||
import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools,
|
||||
nimblepkg/download, nimblepkg/config, nimblepkg/compat
|
||||
nimblepkg/download, nimblepkg/config, nimblepkg/nimbletypes
|
||||
|
||||
when not defined(windows):
|
||||
from posix import getpid
|
||||
|
||||
type
|
||||
TOptions = object
|
||||
forcePrompts: TForcePrompt
|
||||
Options = object
|
||||
forcePrompts: ForcePrompt
|
||||
queryVersions: bool
|
||||
action: TAction
|
||||
config: TConfig
|
||||
nimbleData: PJsonNode ## Nimbledata.json
|
||||
action: Action
|
||||
config: Config
|
||||
nimbleData: JsonNode ## Nimbledata.json
|
||||
|
||||
TActionType = enum
|
||||
ActionNil, ActionUpdate, ActionInit, ActionInstall, ActionSearch,
|
||||
ActionList, ActionBuild, ActionPath, ActionUninstall
|
||||
ActionType = enum
|
||||
actionNil, actionUpdate, actionInit, actionInstall, actionSearch,
|
||||
actionList, actionBuild, actionPath, actionUninstall
|
||||
|
||||
TAction = object
|
||||
case typ: TActionType
|
||||
of ActionNil, ActionList, ActionBuild: nil
|
||||
of ActionUpdate:
|
||||
Action = object
|
||||
case typ: ActionType
|
||||
of actionNil, actionList, actionBuild: nil
|
||||
of actionUpdate:
|
||||
optionalURL: string # Overrides default package list.
|
||||
of ActionInstall, ActionPath, ActionUninstall:
|
||||
of actionInstall, actionPath, actionUninstall:
|
||||
optionalName: seq[string] # \
|
||||
# When this is @[], installs package from current dir.
|
||||
packages: seq[TPkgTuple] # Optional only for ActionInstall.
|
||||
of ActionSearch:
|
||||
packages: seq[PkgTuple] # Optional only for actionInstall.
|
||||
of actionSearch:
|
||||
search: seq[string] # Search string.
|
||||
of ActionInit:
|
||||
of actionInit:
|
||||
projName: string
|
||||
|
||||
TForcePrompt = enum
|
||||
DontForcePrompt, ForcePromptYes, ForcePromptNo
|
||||
ForcePrompt = enum
|
||||
dontForcePrompt, forcePromptYes, forcePromptNo
|
||||
|
||||
const
|
||||
help = """
|
||||
|
|
@ -70,38 +70,40 @@ For more information read the Github readme:
|
|||
https://github.com/nim-lang/nimble#readme
|
||||
"""
|
||||
nimbleVersion = "0.6.0"
|
||||
defaultPackageURL = "https://github.com/nim-lang/packages/raw/master/packages.json"
|
||||
defaultPackageURL =
|
||||
"https://github.com/nim-lang/packages/raw/master/packages.json"
|
||||
|
||||
proc writeHelp() =
|
||||
echo(help)
|
||||
quit(QuitSuccess)
|
||||
|
||||
proc writeVersion() =
|
||||
echo("nimble v$# compiled at $# $#" % [nimbleVersion, CompileDate, CompileTime])
|
||||
echo("nimble v$# compiled at $# $#" %
|
||||
[nimbleVersion, CompileDate, CompileTime])
|
||||
quit(QuitSuccess)
|
||||
|
||||
proc getNimbleDir(options: TOptions): string =
|
||||
proc getNimbleDir(options: Options): string =
|
||||
options.config.nimbleDir
|
||||
|
||||
proc getPkgsDir(options: TOptions): string =
|
||||
proc getPkgsDir(options: Options): string =
|
||||
options.config.nimbleDir / "pkgs"
|
||||
|
||||
proc getBinDir(options: TOptions): string =
|
||||
proc getBinDir(options: Options): string =
|
||||
options.config.nimbleDir / "bin"
|
||||
|
||||
proc prompt(options: TOptions, question: string): bool =
|
||||
proc prompt(options: Options, question: string): bool =
|
||||
## Asks an interactive question and returns the result.
|
||||
##
|
||||
## The proc will return immediately without asking the user if the global
|
||||
## forcePrompts has a value different than DontForcePrompt.
|
||||
## forcePrompts has a value different than dontForcePrompt.
|
||||
case options.forcePrompts
|
||||
of ForcePromptYes:
|
||||
of forcePromptYes:
|
||||
echo(question & " -> [forced yes]")
|
||||
return true
|
||||
of ForcePromptNo:
|
||||
of forcePromptNo:
|
||||
echo(question & " -> [forced no]")
|
||||
return false
|
||||
of DontForcePrompt:
|
||||
of dontForcePrompt:
|
||||
echo(question & " [y/N]")
|
||||
let yn = stdin.readLine()
|
||||
case yn.normalize
|
||||
|
|
@ -112,80 +114,87 @@ proc prompt(options: TOptions, question: string): bool =
|
|||
else:
|
||||
return false
|
||||
|
||||
proc renameBabelToNimble(options: TOptions) {.deprecated.} =
|
||||
proc renameBabelToNimble(options: Options) {.deprecated.} =
|
||||
let babelDir = getHomeDir() / ".babel"
|
||||
let nimbleDir = getHomeDir() / ".nimble"
|
||||
if dirExists(babelDir):
|
||||
if options.prompt("Found deprecated babel package directory, would you like to rename it to nimble?"):
|
||||
if options.prompt("Found deprecated babel package directory, would you " &
|
||||
"like to rename it to nimble?"):
|
||||
copyDir(babelDir, nimbleDir)
|
||||
copyFile(babelDir / "babeldata.json", nimbleDir / "nimbledata.json")
|
||||
|
||||
removeDir(babelDir)
|
||||
removeFile(nimbleDir / "babeldata.json")
|
||||
|
||||
proc parseCmdLine(): TOptions =
|
||||
result.action.typ = ActionNil
|
||||
proc parseCmdLine(): Options =
|
||||
result.action.typ = actionNil
|
||||
result.config = parseConfig()
|
||||
for kind, key, val in getOpt():
|
||||
case kind
|
||||
of cmdArgument:
|
||||
if result.action.typ == ActionNil:
|
||||
if result.action.typ == actionNil:
|
||||
case key
|
||||
of "install", "path":
|
||||
case key
|
||||
of "install":
|
||||
result.action.typ = ActionInstall
|
||||
result.action.typ = actionInstall
|
||||
of "path":
|
||||
result.action.typ = ActionPath
|
||||
result.action.typ = actionPath
|
||||
else:
|
||||
discard
|
||||
result.action.packages = @[]
|
||||
of "build":
|
||||
result.action.typ = ActionBuild
|
||||
result.action.typ = actionBuild
|
||||
of "init":
|
||||
result.action.typ = ActionInit
|
||||
result.action.typ = actionInit
|
||||
result.action.projName = ""
|
||||
of "update":
|
||||
result.action.typ = ActionUpdate
|
||||
result.action.typ = actionUpdate
|
||||
result.action.optionalURL = ""
|
||||
of "search":
|
||||
result.action.typ = ActionSearch
|
||||
result.action.typ = actionSearch
|
||||
result.action.search = @[]
|
||||
of "list":
|
||||
result.action.typ = ActionList
|
||||
result.action.typ = actionList
|
||||
of "uninstall", "remove", "delete", "del", "rm":
|
||||
result.action.typ = ActionUninstall
|
||||
result.action.typ = actionUninstall
|
||||
result.action.packages = @[]
|
||||
else: writeHelp()
|
||||
else:
|
||||
case result.action.typ
|
||||
of ActionNil:
|
||||
of actionNil:
|
||||
assert false
|
||||
of ActionInstall, ActionPath, ActionUninstall:
|
||||
of actionInstall, actionPath, actionUninstall:
|
||||
# Parse pkg@verRange
|
||||
if '@' in key:
|
||||
let i = find(key, '@')
|
||||
let pkgTup = (key[0 .. i-1], key[i+1 .. -1].parseVersionRange())
|
||||
result.action.packages.add(pkgTup)
|
||||
else:
|
||||
result.action.packages.add((key, PVersionRange(kind: verAny)))
|
||||
of ActionUpdate:
|
||||
result.action.packages.add((key, VersionRangeRef(kind: verAny)))
|
||||
of actionUpdate:
|
||||
result.action.optionalURL = key
|
||||
of ActionSearch:
|
||||
of actionSearch:
|
||||
result.action.search.add(key)
|
||||
of ActionInit:
|
||||
of actionInit:
|
||||
if result.action.projName != "":
|
||||
raise newException(ENimble, "Can only initialize one package at a time.")
|
||||
raise newException(NimbleError,
|
||||
"Can only initialize one package at a time.")
|
||||
result.action.projName = key
|
||||
of ActionList, ActionBuild:
|
||||
of actionList, actionBuild:
|
||||
writeHelp()
|
||||
else:
|
||||
discard
|
||||
of cmdLongOption, cmdShortOption:
|
||||
case key
|
||||
of "help", "h": writeHelp()
|
||||
of "version", "v": writeVersion()
|
||||
of "accept", "y": result.forcePrompts = ForcePromptYes
|
||||
of "reject", "n": result.forcePrompts = ForcePromptNo
|
||||
of "accept", "y": result.forcePrompts = forcePromptYes
|
||||
of "reject", "n": result.forcePrompts = forcePromptNo
|
||||
of "ver": result.queryVersions = true
|
||||
else: discard
|
||||
of cmdEnd: assert(false) # cannot happen
|
||||
if result.action.typ == ActionNil:
|
||||
if result.action.typ == actionNil:
|
||||
writeHelp()
|
||||
|
||||
# TODO: Remove this after a couple of versions.
|
||||
|
|
@ -199,18 +208,18 @@ proc parseCmdLine(): TOptions =
|
|||
try:
|
||||
result.nimbleData = parseFile(nimbledataFilename)
|
||||
except:
|
||||
raise newException(ENimble, "Couldn't parse nimbledata.json file " &
|
||||
raise newException(NimbleError, "Couldn't parse nimbledata.json file " &
|
||||
"located at " & nimbledataFilename)
|
||||
else:
|
||||
result.nimbleData = %{"reverseDeps": newJObject()}
|
||||
|
||||
proc update(options: TOptions) =
|
||||
proc update(options: Options) =
|
||||
## Downloads the package list from the specified URL.
|
||||
##
|
||||
## If the download is successful, the global didUpdatePackages is set to
|
||||
## true. Otherwise an exception is raised on error.
|
||||
let url =
|
||||
if options.action.typ == ActionUpdate and options.action.optionalURL != "":
|
||||
if options.action.typ == actionUpdate and options.action.optionalURL != "":
|
||||
options.action.optionalURL
|
||||
else:
|
||||
defaultPackageURL
|
||||
|
|
@ -218,14 +227,14 @@ proc update(options: TOptions) =
|
|||
downloadFile(url, options.getNimbleDir() / "packages.json")
|
||||
echo("Done.")
|
||||
|
||||
proc checkInstallFile(pkgInfo: TPackageInfo,
|
||||
proc checkInstallFile(pkgInfo: PackageInfo,
|
||||
origDir, file: string): bool =
|
||||
## Checks whether ``file`` should be installed.
|
||||
## ``True`` means file should be skipped.
|
||||
|
||||
for ignoreFile in pkgInfo.skipFiles:
|
||||
if ignoreFile.endswith("nimble"):
|
||||
raise newException(ENimble, ignoreFile & " must be installed.")
|
||||
raise newException(NimbleError, ignoreFile & " must be installed.")
|
||||
if samePaths(file, origDir / ignoreFile):
|
||||
result = true
|
||||
break
|
||||
|
|
@ -237,7 +246,7 @@ proc checkInstallFile(pkgInfo: TPackageInfo,
|
|||
|
||||
if file.splitFile().name[0] == '.': result = true
|
||||
|
||||
proc checkInstallDir(pkgInfo: TPackageInfo,
|
||||
proc checkInstallDir(pkgInfo: PackageInfo,
|
||||
origDir, dir: string): bool =
|
||||
## Determines whether ``dir`` should be installed.
|
||||
## ``True`` means dir should be skipped.
|
||||
|
|
@ -252,7 +261,7 @@ proc checkInstallDir(pkgInfo: TPackageInfo,
|
|||
if thisDir == "nimcache": result = true
|
||||
|
||||
proc copyWithExt(origDir, currentDir, dest: string,
|
||||
pkgInfo: TPackageInfo): seq[string] =
|
||||
pkgInfo: PackageInfo): seq[string] =
|
||||
## Returns the filenames of the files that have been copied
|
||||
## (their destination).
|
||||
result = @[]
|
||||
|
|
@ -266,9 +275,9 @@ proc copyWithExt(origDir, currentDir, dest: string,
|
|||
result.add copyFileD(path, changeRoot(origDir, dest, path))
|
||||
|
||||
proc copyFilesRec(origDir, currentDir, dest: string,
|
||||
options: TOptions, pkgInfo: TPackageInfo): TSet[string] =
|
||||
options: Options, pkgInfo: PackageInfo): HashSet[string] =
|
||||
## Copies all the required files, skips files specified in the .nimble file
|
||||
## (TPackageInfo).
|
||||
## (PackageInfo).
|
||||
## Returns a list of filepaths to files which have been installed.
|
||||
result = initSet[string]()
|
||||
let whitelistMode =
|
||||
|
|
@ -317,13 +326,14 @@ proc copyFilesRec(origDir, currentDir, dest: string,
|
|||
result.incl copyFileD(pkgInfo.mypath,
|
||||
changeRoot(pkgInfo.mypath.splitFile.dir, dest, pkgInfo.mypath))
|
||||
|
||||
proc saveNimbleData(options: TOptions) =
|
||||
proc saveNimbleData(options: Options) =
|
||||
# TODO: This file should probably be locked.
|
||||
writeFile(options.getNimbleDir() / "nimbledata.json", pretty(options.nimbleData))
|
||||
writeFile(options.getNimbleDir() / "nimbledata.json",
|
||||
pretty(options.nimbleData))
|
||||
|
||||
proc addRevDep(options: TOptions, dep: tuple[name, version: string],
|
||||
pkg: TPackageInfo) =
|
||||
let depNameVer = dep.name & '-' & dep.version
|
||||
proc addRevDep(options: Options, dep: tuple[name, version: string],
|
||||
pkg: PackageInfo) =
|
||||
# let depNameVer = dep.name & '-' & dep.version
|
||||
if not options.nimbleData["reverseDeps"].hasKey(dep.name):
|
||||
options.nimbleData["reverseDeps"][dep.name] = newJObject()
|
||||
if not options.nimbleData["reverseDeps"][dep.name].hasKey(dep.version):
|
||||
|
|
@ -333,10 +343,10 @@ proc addRevDep(options: TOptions, dep: tuple[name, version: string],
|
|||
if revDep notin thisDep:
|
||||
thisDep.add revDep
|
||||
|
||||
proc removeRevDep(options: TOptions, pkg: TPackageInfo) =
|
||||
proc removeRevDep(options: Options, pkg: PackageInfo) =
|
||||
## Removes ``pkg`` from the reverse dependencies of every package.
|
||||
proc remove(options: TOptions, pkg: TPackageInfo, depTup: TPkgTuple,
|
||||
thisDep: PJsonNode) =
|
||||
proc remove(options: Options, pkg: PackageInfo, depTup: PkgTuple,
|
||||
thisDep: JsonNode) =
|
||||
for ver, val in thisDep:
|
||||
if ver.newVersion in depTup.ver:
|
||||
var newVal = newJArray()
|
||||
|
|
@ -370,10 +380,10 @@ proc removeRevDep(options: TOptions, pkg: TPackageInfo) =
|
|||
|
||||
saveNimbleData(options)
|
||||
|
||||
proc install(packages: seq[TPkgTuple],
|
||||
options: TOptions,
|
||||
doPrompt = true): tuple[paths: seq[string], pkg: TPackageInfo]
|
||||
proc processDeps(pkginfo: TPackageInfo, options: TOptions): seq[string] =
|
||||
proc install(packages: seq[PkgTuple],
|
||||
options: Options,
|
||||
doPrompt = true): tuple[paths: seq[string], pkg: PackageInfo]
|
||||
proc processDeps(pkginfo: PackageInfo, options: Options): seq[string] =
|
||||
## Verifies and installs dependencies.
|
||||
##
|
||||
## Returns the list of paths to pass to the compiler during build phase.
|
||||
|
|
@ -387,7 +397,7 @@ proc processDeps(pkginfo: TPackageInfo, options: TOptions): seq[string] =
|
|||
quit("Unsatisfied dependency: " & dep.name & " (" & $dep.ver & ")")
|
||||
else:
|
||||
echo("Looking for ", dep.name, " (", $dep.ver, ")...")
|
||||
var pkg: TPackageInfo
|
||||
var pkg: PackageInfo
|
||||
if not findPkg(pkglist, dep, pkg):
|
||||
echo("None found, installing...")
|
||||
let (paths, installedPkg) = install(@[(dep.name, dep.ver)], options)
|
||||
|
|
@ -403,11 +413,11 @@ proc processDeps(pkginfo: TPackageInfo, options: TOptions): seq[string] =
|
|||
|
||||
# Check if two packages of the same name (but different version) are listed
|
||||
# in the path.
|
||||
var pkgsInPath: PStringTable = newStringTable(modeCaseSensitive)
|
||||
var pkgsInPath: StringTableRef = newStringTable(modeCaseSensitive)
|
||||
for p in result:
|
||||
let (name, version) = getNameVersion(p)
|
||||
if pkgsInPath.hasKey(name) and pkgsInPath[name] != version:
|
||||
raise newException(ENimble,
|
||||
raise newException(NimbleError,
|
||||
"Cannot satisfy the dependency on $1 $2 and $1 $3" %
|
||||
[name, version, pkgsInPath[name]])
|
||||
pkgsInPath[name] = version
|
||||
|
|
@ -419,7 +429,7 @@ proc processDeps(pkginfo: TPackageInfo, options: TOptions): seq[string] =
|
|||
addRevDep(options, i, pkginfo)
|
||||
saveNimbleData(options)
|
||||
|
||||
proc buildFromDir(pkgInfo: TPackageInfo, paths: seq[string]) =
|
||||
proc buildFromDir(pkgInfo: PackageInfo, paths: seq[string]) =
|
||||
## Builds a package as specified by ``pkgInfo``.
|
||||
let realDir = pkgInfo.getRealDir()
|
||||
var args = ""
|
||||
|
|
@ -430,19 +440,19 @@ proc buildFromDir(pkgInfo: TPackageInfo, paths: seq[string]) =
|
|||
doCmd(getNimBin() & " $# -d:release --noBabelPath $# \"$#\"" %
|
||||
[pkgInfo.backend, args, realDir / bin.changeFileExt("nim")])
|
||||
|
||||
proc saveNimbleMeta(pkgDestDir, url: string, filesInstalled: TSet[string]) =
|
||||
proc saveNimbleMeta(pkgDestDir, url: string, filesInstalled: HashSet[string]) =
|
||||
var nimblemeta = %{"url": %url}
|
||||
nimblemeta["files"] = newJArray()
|
||||
for file in filesInstalled:
|
||||
nimblemeta["files"].add(%changeRoot(pkgDestDir, "", file))
|
||||
writeFile(pkgDestDir / "nimblemeta.json", $nimblemeta)
|
||||
|
||||
proc removePkgDir(dir: string, options: TOptions) =
|
||||
proc removePkgDir(dir: string, options: Options) =
|
||||
## Removes files belonging to the package in ``dir``.
|
||||
try:
|
||||
var nimblemeta = parseFile(dir / "nimblemeta.json")
|
||||
if not nimblemeta.hasKey("files"):
|
||||
raise newException(EJsonParsingError,
|
||||
raise newException(JsonParsingError,
|
||||
"Meta data does not contain required info.")
|
||||
for file in nimblemeta["files"]:
|
||||
removeFile(dir / file.str)
|
||||
|
|
@ -455,15 +465,15 @@ proc removePkgDir(dir: string, options: TOptions) =
|
|||
else:
|
||||
echo("WARNING: Cannot completely remove " & dir &
|
||||
". Files not installed by nimble are present.")
|
||||
except EOS, EJsonParsingError:
|
||||
except OSError, JsonParsingError:
|
||||
echo("Error: Unable to read nimblemeta.json: ", getCurrentExceptionMsg())
|
||||
if not options.prompt("Would you like to COMPLETELY remove ALL files " &
|
||||
"in " & dir & "?"):
|
||||
quit(QuitSuccess)
|
||||
removeDir(dir)
|
||||
|
||||
proc installFromDir(dir: string, latest: bool, options: TOptions,
|
||||
url: string): tuple[paths: seq[string], pkg: TPackageInfo] =
|
||||
proc installFromDir(dir: string, latest: bool, options: Options,
|
||||
url: string): tuple[paths: seq[string], pkg: PackageInfo] =
|
||||
## Returns where package has been installed to, together with paths
|
||||
## to the packages this package depends on.
|
||||
## The return value of this function is used by
|
||||
|
|
@ -484,7 +494,8 @@ proc installFromDir(dir: string, latest: bool, options: TOptions,
|
|||
let versionStr = (if latest: "" else: '-' & pkgInfo.version)
|
||||
let pkgDestDir = pkgsDir / (pkgInfo.name & versionStr)
|
||||
if existsDir(pkgDestDir) and existsFile(pkgDestDir / "nimblemeta.json"):
|
||||
if not options.prompt(pkgInfo.name & versionStr & " already exists. Overwrite?"):
|
||||
if not options.prompt(pkgInfo.name & versionStr &
|
||||
" already exists. Overwrite?"):
|
||||
quit(QuitSuccess)
|
||||
removePkgDir(pkgDestDir, options)
|
||||
# Remove any symlinked binaries
|
||||
|
|
@ -500,7 +511,7 @@ proc installFromDir(dir: string, latest: bool, options: TOptions,
|
|||
removeFile(binDir / bin)
|
||||
|
||||
## Will contain a list of files which have been installed.
|
||||
var filesInstalled: TSet[string]
|
||||
var filesInstalled: HashSet[string]
|
||||
|
||||
createDir(pkgDestDir)
|
||||
if pkgInfo.bin.len > 0:
|
||||
|
|
@ -565,32 +576,34 @@ proc getNimbleTempDir(): string =
|
|||
else:
|
||||
result.add($getpid())
|
||||
|
||||
proc downloadPkg(url: string, verRange: PVersionRange,
|
||||
downMethod: TDownloadMethod): string =
|
||||
proc downloadPkg(url: string, verRange: VersionRangeRef,
|
||||
downMethod: DownloadMethod): string =
|
||||
let downloadDir = (getNimbleTempDir() / getDownloadDirName(url, verRange))
|
||||
createDir(downloadDir)
|
||||
echo("Downloading ", url, " into ", downloadDir, " using ", downMethod, "...")
|
||||
doDownload(url, downloadDir, verRange, downMethod)
|
||||
result = downloadDir
|
||||
|
||||
proc downloadPkg(pkg: TPackage, verRange: PVersionRange): string =
|
||||
proc downloadPkg(pkg: Package, verRange: VersionRangeRef): string =
|
||||
let downloadDir = (getNimbleTempDir() / getDownloadDirName(pkg, verRange))
|
||||
let downMethod = pkg.downloadMethod.getDownloadMethod()
|
||||
createDir(downloadDir)
|
||||
echo("Downloading ", pkg.name, " into ", downloadDir, " using ", downMethod, "...")
|
||||
echo("Downloading ", pkg.name, " into ", downloadDir, " using ", downMethod,
|
||||
"...")
|
||||
doDownload(pkg.url, downloadDir, verRange, downMethod)
|
||||
result = downloadDir
|
||||
|
||||
proc install(packages: seq[TPkgTuple],
|
||||
options: TOptions,
|
||||
doPrompt = true): tuple[paths: seq[string], pkg: TPackageInfo] =
|
||||
proc install(packages: seq[PkgTuple],
|
||||
options: Options,
|
||||
doPrompt = true): tuple[paths: seq[string], pkg: PackageInfo] =
|
||||
if packages == @[]:
|
||||
result = installFromDir(getCurrentDir(), false, options, "")
|
||||
else:
|
||||
# If packages.json is not present ask the user if they want to download it.
|
||||
if not existsFile(options.getNimbleDir / "packages.json"):
|
||||
if doPrompt and
|
||||
options.prompt("Local packages.json not found, download it from internet?"):
|
||||
options.prompt("Local packages.json not found, download it from " &
|
||||
"internet?"):
|
||||
update(options)
|
||||
else:
|
||||
quit("Please run nimble update.", QuitFailure)
|
||||
|
|
@ -602,34 +615,35 @@ proc install(packages: seq[TPkgTuple],
|
|||
let downloadDir = downloadPkg(pv.name, pv.ver, meth)
|
||||
result = installFromDir(downloadDir, false, options, pv.name)
|
||||
else:
|
||||
var pkg: TPackage
|
||||
var pkg: Package
|
||||
if getPackage(pv.name, options.getNimbleDir() / "packages.json", pkg):
|
||||
let downloadDir = downloadPkg(pkg, pv.ver)
|
||||
result = installFromDir(downloadDir, false, options, pkg.url)
|
||||
else:
|
||||
# If package is not found give the user a chance to update package.json
|
||||
# If package is not found give the user a chance to update
|
||||
# package.json
|
||||
if doPrompt and
|
||||
options.prompt(pv.name & " not found in local packages.json, " &
|
||||
"check internet for updated packages?"):
|
||||
update(options)
|
||||
result = install(@[pv], options, false)
|
||||
else:
|
||||
raise newException(ENimble, "Package not found.")
|
||||
raise newException(NimbleError, "Package not found.")
|
||||
|
||||
proc build(options: TOptions) =
|
||||
proc build(options: Options) =
|
||||
var pkgInfo = getPkgInfo(getCurrentDir())
|
||||
let paths = processDeps(pkginfo, options)
|
||||
buildFromDir(pkgInfo, paths)
|
||||
|
||||
proc search(options: TOptions) =
|
||||
proc search(options: Options) =
|
||||
## Searches for matches in ``options.action.search``.
|
||||
##
|
||||
## Searches are done in a case insensitive way making all strings lower case.
|
||||
assert options.action.typ == ActionSearch
|
||||
assert options.action.typ == actionSearch
|
||||
if options.action.search == @[]:
|
||||
raise newException(ENimble, "Please specify a search string.")
|
||||
raise newException(NimbleError, "Please specify a search string.")
|
||||
if not existsFile(options.getNimbleDir() / "packages.json"):
|
||||
raise newException(ENimble, "Please run nimble update.")
|
||||
raise newException(NimbleError, "Please run nimble update.")
|
||||
let pkgList = getPackageList(options.getNimbleDir() / "packages.json")
|
||||
var found = false
|
||||
template onFound: stmt =
|
||||
|
|
@ -653,9 +667,9 @@ proc search(options: TOptions) =
|
|||
if not found:
|
||||
echo("No package found.")
|
||||
|
||||
proc list(options: TOptions) =
|
||||
proc list(options: Options) =
|
||||
if not existsFile(options.getNimbleDir() / "packages.json"):
|
||||
raise newException(ENimble, "Please run nimble update.")
|
||||
raise newException(NimbleError, "Please run nimble update.")
|
||||
let pkgList = getPackageList(options.getNimbleDir() / "packages.json")
|
||||
for pkg in pkgList:
|
||||
echoPackage(pkg)
|
||||
|
|
@ -663,9 +677,9 @@ proc list(options: TOptions) =
|
|||
echoPackageVersions(pkg)
|
||||
echo(" ")
|
||||
|
||||
type VersionAndPath = tuple[version: TVersion, path: string]
|
||||
type VersionAndPath = tuple[version: Version, path: string]
|
||||
|
||||
proc listPaths(options: TOptions) =
|
||||
proc listPaths(options: Options) =
|
||||
## Loops over installing packages displaying their installed paths.
|
||||
##
|
||||
## If there are several packages installed, only the last one (the version
|
||||
|
|
@ -674,7 +688,7 @@ proc listPaths(options: TOptions) =
|
|||
## but at the end quits with a non zero exit error.
|
||||
##
|
||||
## On success the proc returns normally.
|
||||
assert options.action.typ == ActionPath
|
||||
assert options.action.typ == actionPath
|
||||
assert(not options.action.packages.isNil)
|
||||
var errors = 0
|
||||
for name, version in options.action.packages.items:
|
||||
|
|
@ -704,26 +718,28 @@ proc listPaths(options: TOptions) =
|
|||
echo "Warning: Package '" & name & "' not installed"
|
||||
errors += 1
|
||||
if errors > 0:
|
||||
raise newException(ENimble, "At least one of the specified packages was not found")
|
||||
raise newException(NimbleError,
|
||||
"At least one of the specified packages was not found")
|
||||
|
||||
proc init(options: TOptions) =
|
||||
proc init(options: Options) =
|
||||
echo("Initializing new Nimble project!")
|
||||
var
|
||||
pkgName, fName: string = ""
|
||||
outFile: TFile
|
||||
outFile: File
|
||||
|
||||
if (options.action.projName != ""):
|
||||
pkgName = options.action.projName
|
||||
fName = pkgName & ".nimble"
|
||||
if (existsFile(os.getCurrentDir() / fName)):
|
||||
raise newException(ENimble, "Already have a nimble file.")
|
||||
raise newException(NimbleError, "Already have a nimble file.")
|
||||
else:
|
||||
echo("Enter a project name for this (blank to use working directory), Ctrl-C to abort:")
|
||||
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(ENimble, "Could not get default file path.")
|
||||
raise newException(NimbleError, "Could not get default file path.")
|
||||
fName = pkgName & ".nimble"
|
||||
|
||||
# Now need to write out .nimble file with projName and other details
|
||||
|
|
@ -742,18 +758,18 @@ proc init(options: TOptions) =
|
|||
close(outFile)
|
||||
|
||||
else:
|
||||
raise newException(ENimble, "Unable to open file " & fName &
|
||||
" for writing: " & osErrorMsg())
|
||||
raise newException(NimbleError, "Unable to open file " & fName &
|
||||
" for writing: " & osErrorMsg(osLastError()))
|
||||
|
||||
proc uninstall(options: TOptions) =
|
||||
var pkgsToDelete: seq[TPackageInfo] = @[]
|
||||
proc uninstall(options: Options) =
|
||||
var pkgsToDelete: seq[PackageInfo] = @[]
|
||||
# Do some verification.
|
||||
for pkgTup in options.action.packages:
|
||||
echo("Looking for ", pkgTup.name, " (", $pkgTup.ver, ")...")
|
||||
let installedPkgs = getInstalledPkgs(options.getPkgsDir())
|
||||
var pkgList = findAllPkgs(installedPkgs, pkgTup)
|
||||
if pkgList.len == 0:
|
||||
raise newException(ENimble, "Package not found")
|
||||
raise newException(NimbleError, "Package not found")
|
||||
|
||||
echo("Checking reverse dependencies...")
|
||||
var errors: seq[string] = @[]
|
||||
|
|
@ -779,7 +795,7 @@ proc uninstall(options: TOptions) =
|
|||
pkgsToDelete.add pkg
|
||||
|
||||
if pkgsToDelete.len == 0:
|
||||
raise newException(ENimble, "\n " & errors.join("\n "))
|
||||
raise newException(NimbleError, "\n " & errors.join("\n "))
|
||||
|
||||
var pkgNames = ""
|
||||
for i in 0 .. <pkgsToDelete.len:
|
||||
|
|
@ -798,37 +814,37 @@ proc uninstall(options: TOptions) =
|
|||
removePkgDir(options.getPkgsDir / (pkg.name & '-' & pkg.version), options)
|
||||
echo("Removed ", pkg.name, " (", $pkg.version, ")")
|
||||
|
||||
proc doAction(options: TOptions) =
|
||||
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:
|
||||
of actionUpdate:
|
||||
update(options)
|
||||
of ActionInstall:
|
||||
of actionInstall:
|
||||
discard install(options.action.packages, options)
|
||||
of ActionUninstall:
|
||||
of actionUninstall:
|
||||
uninstall(options)
|
||||
of ActionSearch:
|
||||
of actionSearch:
|
||||
search(options)
|
||||
of ActionList:
|
||||
of actionList:
|
||||
list(options)
|
||||
of ActionPath:
|
||||
of actionPath:
|
||||
listPaths(options)
|
||||
of ActionBuild:
|
||||
of actionBuild:
|
||||
build(options)
|
||||
of ActionInit:
|
||||
of actionInit:
|
||||
init(options)
|
||||
of ActionNil:
|
||||
of actionNil:
|
||||
assert false
|
||||
|
||||
when isMainModule:
|
||||
when defined(release):
|
||||
try:
|
||||
parseCmdLine().doAction()
|
||||
except ENimble:
|
||||
except NimbleError:
|
||||
quit("FAILURE: " & getCurrentExceptionMsg())
|
||||
finally:
|
||||
removeDir(getNimbleTempDir())
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
# Copyright (C) Dominik Picheta. All rights reserved.
|
||||
# BSD License. Look at license.txt for more info.
|
||||
|
||||
## This module contains additional code from the development version of
|
||||
## Nimrod's standard library. These procs are required to be able to compile
|
||||
## against the last stable release 0.9.4. Once 0.9.6 is release these procs
|
||||
## will disappear.
|
||||
|
||||
import json
|
||||
when false:
|
||||
when not (NimrodPatch >= 5):
|
||||
when not defined(`{}`):
|
||||
proc `{}`*(node: PJsonNode, key: string): PJsonNode =
|
||||
## Transverses the node and gets the given value. If any of the
|
||||
## names does not exist, returns nil
|
||||
result = node
|
||||
if isNil(node): return nil
|
||||
result = result[key]
|
||||
|
||||
when not defined(`{}=`):
|
||||
proc `{}=`*(node: PJsonNode, names: varargs[string], value: PJsonNode) =
|
||||
## Transverses the node and tries to set the value at the given location
|
||||
## to `value` If any of the names are missing, they are added
|
||||
var node = node
|
||||
for i in 0..(names.len-2):
|
||||
if isNil(node[names[i]]):
|
||||
node[names[i]] = newJObject()
|
||||
node = node[names[i]]
|
||||
node[names[names.len-1]] = value
|
||||
|
|
@ -2,15 +2,15 @@
|
|||
# BSD License. Look at license.txt for more info.
|
||||
import parsecfg, streams, strutils, os
|
||||
|
||||
import tools, version
|
||||
import tools, version, nimbletypes
|
||||
|
||||
type
|
||||
TConfig* = object
|
||||
Config* = object
|
||||
nimbleDir*: string
|
||||
chcp*: bool # Whether to change the code page in .cmd files on Win.
|
||||
|
||||
|
||||
proc initConfig(): TConfig =
|
||||
proc initConfig(): Config =
|
||||
if getNimrodVersion() > newVersion("0.9.6"):
|
||||
result.nimbleDir = getHomeDir() / ".nimble"
|
||||
else:
|
||||
|
|
@ -18,7 +18,7 @@ proc initConfig(): TConfig =
|
|||
|
||||
result.chcp = true
|
||||
|
||||
proc parseConfig*(): TConfig =
|
||||
proc parseConfig*(): Config =
|
||||
result = initConfig()
|
||||
var confFile = getConfigDir() / "nimble" / "nimble.ini"
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ proc parseConfig*(): TConfig =
|
|||
|
||||
if f != nil:
|
||||
echo("Reading from config file at ", confFile)
|
||||
var p: TCfgParser
|
||||
var p: CfgParser
|
||||
open(p, f, confFile)
|
||||
while true:
|
||||
var e = next(p)
|
||||
|
|
@ -49,8 +49,8 @@ proc parseConfig*(): TConfig =
|
|||
of "chcp":
|
||||
result.chcp = parseBool(e.value)
|
||||
else:
|
||||
raise newException(ENimble, "Unable to parse config file:" &
|
||||
raise newException(NimbleError, "Unable to parse config file:" &
|
||||
" Unknown key: " & e.key)
|
||||
of cfgError:
|
||||
raise newException(ENimble, "Unable to parse config file: " & e.msg)
|
||||
raise newException(NimbleError, "Unable to parse config file: " & e.msg)
|
||||
close(p)
|
||||
|
|
|
|||
|
|
@ -3,47 +3,48 @@
|
|||
|
||||
import parseutils, os, osproc, strutils, tables, pegs
|
||||
|
||||
import packageinfo, version, tools
|
||||
import packageinfo, version, tools, nimbletypes
|
||||
|
||||
type
|
||||
TDownloadMethod* {.pure.} = enum
|
||||
Git = "git", Hg = "hg"
|
||||
DownloadMethod* {.pure.} = enum
|
||||
git = "git", hg = "hg"
|
||||
|
||||
proc getSpecificDir(meth: TDownloadMethod): string =
|
||||
proc getSpecificDir(meth: DownloadMethod): string =
|
||||
case meth
|
||||
of TDownloadMethod.Git:
|
||||
of DownloadMethod.git:
|
||||
".git"
|
||||
of TDownloadMethod.Hg:
|
||||
of DownloadMethod.hg:
|
||||
".hg"
|
||||
|
||||
proc doCheckout(meth: TDownloadMethod, downloadDir, branch: string) =
|
||||
proc doCheckout(meth: DownloadMethod, downloadDir, branch: string) =
|
||||
case meth
|
||||
of TDownloadMethod.Git:
|
||||
of DownloadMethod.git:
|
||||
cd downloadDir:
|
||||
# Force is used here because local changes may appear straight after a
|
||||
# clone has happened. Like in the case of git on Windows where it
|
||||
# messes up the damn line endings.
|
||||
doCmd("git checkout --force " & branch)
|
||||
of TDownloadMethod.Hg:
|
||||
of DownloadMethod.hg:
|
||||
cd downloadDir:
|
||||
doCmd("hg checkout " & branch)
|
||||
|
||||
proc doPull(meth: TDownloadMethod, downloadDir: string) =
|
||||
proc doPull(meth: DownloadMethod, downloadDir: string) =
|
||||
case meth
|
||||
of TDownloadMethod.Git:
|
||||
of DownloadMethod.git:
|
||||
doCheckout(meth, downloadDir, "master")
|
||||
cd downloadDir:
|
||||
doCmd("git pull")
|
||||
if existsFile(".gitmodules"):
|
||||
doCmd("git submodule update")
|
||||
of TDownloadMethod.Hg:
|
||||
of DownloadMethod.hg:
|
||||
doCheckout(meth, downloadDir, "default")
|
||||
cd downloadDir:
|
||||
doCmd("hg pull")
|
||||
|
||||
proc doClone(meth: TDownloadMethod, url, downloadDir: string, branch = "", tip = true) =
|
||||
proc doClone(meth: DownloadMethod, url, downloadDir: string, branch = "",
|
||||
tip = true) =
|
||||
case meth
|
||||
of TDownloadMethod.Git:
|
||||
of DownloadMethod.git:
|
||||
let
|
||||
depthArg = if tip: "--depth 1 " else: ""
|
||||
branchArg = if branch == "": "-b origin/master" else: "-b " & branch & " "
|
||||
|
|
@ -60,28 +61,28 @@ proc doClone(meth: TDownloadMethod, url, downloadDir: string, branch = "", tip =
|
|||
doCmd("git reset --hard FETCH_HEAD")
|
||||
doCmd("git checkout --force " & branchArg)
|
||||
doCmd("git submodule update --init --recursive")
|
||||
of TDownloadMethod.Hg:
|
||||
of DownloadMethod.hg:
|
||||
let
|
||||
tipArg = if tip: "-r tip " else: ""
|
||||
branchArg = if branch == "": "" else: "-b " & branch & " "
|
||||
doCmd("hg clone " & tipArg & branchArg & url & " " & downloadDir)
|
||||
|
||||
proc getTagsList(dir: string, meth: TDownloadMethod): seq[string] =
|
||||
proc getTagsList(dir: string, meth: DownloadMethod): seq[string] =
|
||||
cd dir:
|
||||
var output = execProcess("git tag")
|
||||
case meth
|
||||
of TDownloadMethod.Git:
|
||||
of DownloadMethod.git:
|
||||
output = execProcess("git tag")
|
||||
of TDownloadMethod.Hg:
|
||||
of DownloadMethod.hg:
|
||||
output = execProcess("hg tags")
|
||||
if output.len > 0:
|
||||
case meth
|
||||
of TDownloadMethod.Git:
|
||||
of DownloadMethod.git:
|
||||
result = @[]
|
||||
for i in output.splitLines():
|
||||
if i == "": continue
|
||||
result.add(i)
|
||||
of TDownloadMethod.Hg:
|
||||
of DownloadMethod.hg:
|
||||
result = @[]
|
||||
for i in output.splitLines():
|
||||
if i == "": continue
|
||||
|
|
@ -92,13 +93,13 @@ proc getTagsList(dir: string, meth: TDownloadMethod): seq[string] =
|
|||
else:
|
||||
result = @[]
|
||||
|
||||
proc getTagsListRemote*(url: string, meth: TDownloadMethod): seq[string] =
|
||||
proc getTagsListRemote*(url: string, meth: DownloadMethod): seq[string] =
|
||||
result = @[]
|
||||
case meth
|
||||
of TDownloadMethod.Git:
|
||||
of DownloadMethod.git:
|
||||
var (output, exitCode) = doCmdEx("git ls-remote --tags " & url)
|
||||
if exitCode != QuitSuccess:
|
||||
raise newException(EOS, "Unable to query remote tags for " & url &
|
||||
raise newException(OSError, "Unable to query remote tags for " & url &
|
||||
". Git returned: " & output)
|
||||
for i in output.splitLines():
|
||||
if i == "": continue
|
||||
|
|
@ -106,47 +107,47 @@ proc getTagsListRemote*(url: string, meth: TDownloadMethod): seq[string] =
|
|||
let tag = i[start .. -1]
|
||||
if not tag.endswith("^{}"): result.add(tag)
|
||||
|
||||
of TDownloadMethod.Hg:
|
||||
of DownloadMethod.hg:
|
||||
# http://stackoverflow.com/questions/2039150/show-tags-for-remote-hg-repository
|
||||
raise newException(EInvalidValue, "Hg doesn't support remote tag querying.")
|
||||
raise newException(ValueError, "Hg doesn't support remote tag querying.")
|
||||
|
||||
proc getVersionList*(tags: seq[string]): TTable[TVersion, string] =
|
||||
proc getVersionList*(tags: seq[string]): Table[Version, string] =
|
||||
# Returns: TTable of version -> git tag name
|
||||
result = initTable[TVersion, string]()
|
||||
result = initTable[Version, string]()
|
||||
for tag in tags:
|
||||
if tag != "":
|
||||
let i = skipUntil(tag, Digits) # skip any chars before the version
|
||||
# TODO: Better checking, tags can have any names. Add warnings and such.
|
||||
result[newVersion(tag[i .. -1])] = tag
|
||||
|
||||
proc getDownloadMethod*(meth: string): TDownloadMethod =
|
||||
proc getDownloadMethod*(meth: string): DownloadMethod =
|
||||
case meth
|
||||
of "git": return TDownloadMethod.Git
|
||||
of "hg", "mercurial": return TDownloadMethod.Hg
|
||||
of "git": return DownloadMethod.git
|
||||
of "hg", "mercurial": return DownloadMethod.hg
|
||||
else:
|
||||
raise newException(ENimble, "Invalid download method: " & meth)
|
||||
raise newException(NimbleError, "Invalid download method: " & meth)
|
||||
|
||||
proc getHeadName*(meth: TDownloadMethod): string =
|
||||
proc getHeadName*(meth: DownloadMethod): string =
|
||||
## Returns the name of the download method specific head. i.e. for git
|
||||
## it's ``head`` for hg it's ``tip``.
|
||||
case meth
|
||||
of TDownloadMethod.Git: "head"
|
||||
of TDownloadMethod.Hg: "tip"
|
||||
of DownloadMethod.git: "head"
|
||||
of DownloadMethod.hg: "tip"
|
||||
|
||||
proc checkUrlType*(url: string): TDownloadMethod =
|
||||
proc checkUrlType*(url: string): DownloadMethod =
|
||||
## Determines the download method based on the URL.
|
||||
if doCmdEx("git ls-remote " & url).exitCode == QuitSuccess:
|
||||
return TDownloadMethod.Git
|
||||
return DownloadMethod.git
|
||||
elif doCmdEx("hg identify " & url).exitCode == QuitSuccess:
|
||||
return TDownloadMethod.Hg
|
||||
return DownloadMethod.hg
|
||||
else:
|
||||
raise newException(ENimble, "Unable to identify url.")
|
||||
raise newException(NimbleError, "Unable to identify url.")
|
||||
|
||||
proc isURL*(name: string): bool =
|
||||
name.startsWith(peg" @'://' ")
|
||||
|
||||
proc doDownload*(url: string, downloadDir: string, verRange: PVersionRange,
|
||||
downMethod: TDownloadMethod) =
|
||||
proc doDownload*(url: string, downloadDir: string, verRange: VersionRangeRef,
|
||||
downMethod: DownloadMethod) =
|
||||
template getLatestByTag(meth: stmt): stmt {.dirty, immediate.} =
|
||||
echo("Found tags...")
|
||||
# Find latest version that fits our ``verRange``.
|
||||
|
|
@ -164,7 +165,7 @@ proc doDownload*(url: string, downloadDir: string, verRange: PVersionRange,
|
|||
## version range.
|
||||
let pkginfo = getPkgInfo(downloadDir)
|
||||
if pkginfo.version.newVersion notin verRange:
|
||||
raise newException(ENimble,
|
||||
raise newException(NimbleError,
|
||||
"Downloaded package's version does not satisfy requested version " &
|
||||
"range: wanted $1 got $2." %
|
||||
[$verRange, $pkginfo.version])
|
||||
|
|
@ -177,14 +178,14 @@ proc doDownload*(url: string, downloadDir: string, verRange: PVersionRange,
|
|||
else:
|
||||
# Mercurial requies a clone and checkout. The git clone operation is
|
||||
# already fragmented into multiple steps so we just call doClone().
|
||||
if downMethod == TDownloadMethod.Git:
|
||||
if downMethod == DownloadMethod.git:
|
||||
doClone(downMethod, url, downloadDir, $verRange.spe)
|
||||
else:
|
||||
doClone(downMethod, url, downloadDir, tip = false)
|
||||
doCheckout(downMethod, downloadDir, $verRange.spe)
|
||||
else:
|
||||
case downMethod
|
||||
of TDownloadMethod.Git:
|
||||
of DownloadMethod.git:
|
||||
# For Git we have to query the repo remotely for its tags. This is
|
||||
# necessary as cloning with a --depth of 1 removes all tag info.
|
||||
let versions = getTagsListRemote(url, downMethod).getVersionList()
|
||||
|
|
@ -197,7 +198,7 @@ proc doDownload*(url: string, downloadDir: string, verRange: PVersionRange,
|
|||
doClone(downMethod, url, downloadDir) # Grab HEAD.
|
||||
|
||||
verifyClone()
|
||||
of TDownloadMethod.Hg:
|
||||
of DownloadMethod.hg:
|
||||
doClone(downMethod, url, downloadDir)
|
||||
let versions = getTagsList(downloadDir, downMethod).getVersionList()
|
||||
|
||||
|
|
@ -208,10 +209,10 @@ proc doDownload*(url: string, downloadDir: string, verRange: PVersionRange,
|
|||
|
||||
verifyClone()
|
||||
|
||||
proc echoPackageVersions*(pkg: TPackage) =
|
||||
proc echoPackageVersions*(pkg: Package) =
|
||||
let downMethod = pkg.downloadMethod.getDownloadMethod()
|
||||
case downMethod
|
||||
of TDownloadMethod.Git:
|
||||
of DownloadMethod.git:
|
||||
try:
|
||||
let versions = getTagsListRemote(pkg.url, downMethod).getVersionList()
|
||||
if versions.len > 0:
|
||||
|
|
@ -225,7 +226,8 @@ proc echoPackageVersions*(pkg: TPackage) =
|
|||
echo(" versions: " & vstr)
|
||||
else:
|
||||
echo(" versions: (No versions tagged in the remote repository)")
|
||||
except EOS:
|
||||
except OSError:
|
||||
echo(getCurrentExceptionMsg())
|
||||
of TDownloadMethod.Hg:
|
||||
echo(" versions: (Remote tag retrieval not supported by " & pkg.downloadMethod & ")")
|
||||
of DownloadMethod.hg:
|
||||
echo(" versions: (Remote tag retrieval not supported by " &
|
||||
pkg.downloadMethod & ")")
|
||||
|
|
|
|||
7
src/nimblepkg/nimbletypes.nim
Normal file
7
src/nimblepkg/nimbletypes.nim
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# BSD License. Look at license.txt for more info.
|
||||
#
|
||||
# Various miscellaneous common types reside here, to avoid problems with
|
||||
# recursive imports
|
||||
|
||||
type
|
||||
NimbleError* = object of Exception
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
# 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
|
||||
import version, tools, nimbletypes
|
||||
type
|
||||
## Tuple containing package name and version range.
|
||||
TPkgTuple* = tuple[name: string, ver: PVersionRange]
|
||||
PkgTuple* = tuple[name: string, ver: VersionRangeRef]
|
||||
|
||||
TPackageInfo* = object
|
||||
PackageInfo* = object
|
||||
mypath*: string ## The path of this .nimble file
|
||||
name*: string
|
||||
version*: string
|
||||
|
|
@ -19,12 +19,12 @@ type
|
|||
installDirs*: seq[string]
|
||||
installFiles*: seq[string]
|
||||
installExt*: seq[string]
|
||||
requires*: seq[TPkgTuple]
|
||||
requires*: seq[PkgTuple]
|
||||
bin*: seq[string]
|
||||
srcDir*: string
|
||||
backend*: string
|
||||
|
||||
TPackage* = object
|
||||
Package* = object
|
||||
# Required fields in a package.
|
||||
name*: string
|
||||
url*: string # Download location.
|
||||
|
|
@ -37,10 +37,10 @@ type
|
|||
dvcsTag*: string
|
||||
web*: string # Info url for humans.
|
||||
|
||||
TMetadata* = object
|
||||
MetaData* = object
|
||||
url*: string
|
||||
|
||||
proc initPackageInfo(): TPackageInfo =
|
||||
proc initPackageInfo(): PackageInfo =
|
||||
result.mypath = ""
|
||||
result.name = ""
|
||||
result.version = ""
|
||||
|
|
@ -58,31 +58,32 @@ proc initPackageInfo(): TPackageInfo =
|
|||
result.srcDir = ""
|
||||
result.backend = "c"
|
||||
|
||||
proc validatePackageInfo(pkgInfo: TPackageInfo, path: string) =
|
||||
proc validatePackageInfo(pkgInfo: PackageInfo, path: string) =
|
||||
if pkgInfo.name == "":
|
||||
raise newException(ENimble, "Incorrect .nimble file: " & path &
|
||||
raise newException(NimbleError, "Incorrect .nimble file: " & path &
|
||||
" does not contain a name field.")
|
||||
if pkgInfo.version == "":
|
||||
raise newException(ENimble, "Incorrect .nimble file: " & path &
|
||||
raise newException(NimbleError, "Incorrect .nimble file: " & path &
|
||||
" does not contain a version field.")
|
||||
if pkgInfo.author == "":
|
||||
raise newException(ENimble, "Incorrect .nimble file: " & path &
|
||||
raise newException(NimbleError, "Incorrect .nimble file: " & path &
|
||||
" does not contain an author field.")
|
||||
if pkgInfo.description == "":
|
||||
raise newException(ENimble, "Incorrect .nimble file: " & path &
|
||||
raise newException(NimbleError, "Incorrect .nimble file: " & path &
|
||||
" does not contain a description field.")
|
||||
if pkgInfo.license == "":
|
||||
raise newException(ENimble, "Incorrect .nimble file: " & path &
|
||||
raise newException(NimbleError, "Incorrect .nimble file: " & path &
|
||||
" does not contain a license field.")
|
||||
if pkgInfo.backend notin ["c", "cc", "objc", "cpp", "js"]:
|
||||
raise newException(ENimble, "'" & pkgInfo.backend & "' is an invalid backend.")
|
||||
raise newException(NimbleError, "'" & pkgInfo.backend &
|
||||
"' is an invalid backend.")
|
||||
for c in pkgInfo.version:
|
||||
if c notin ({'.'} + Digits):
|
||||
raise newException(ENimble,
|
||||
raise newException(NimbleError,
|
||||
"Version may only consist of numbers and the '.' character " &
|
||||
"but found '" & c & "'.")
|
||||
|
||||
proc parseRequires(req: string): TPkgTuple =
|
||||
proc parseRequires(req: string): PkgTuple =
|
||||
try:
|
||||
if ' ' in req:
|
||||
var i = skipUntil(req, Whitespace)
|
||||
|
|
@ -94,10 +95,10 @@ proc parseRequires(req: string): TPkgTuple =
|
|||
result.ver = parseVersionRange(req[i .. -1])
|
||||
else:
|
||||
result.name = req.strip
|
||||
result.ver = PVersionRange(kind: verAny)
|
||||
except EParseVersion:
|
||||
raise newException(ENimble, "Unable to parse dependency version range: " &
|
||||
getCurrentExceptionMsg())
|
||||
result.ver = VersionRangeRef(kind: verAny)
|
||||
except ParseVersionError:
|
||||
raise newException(NimbleError,
|
||||
"Unable to parse dependency version range: " & getCurrentExceptionMsg())
|
||||
|
||||
proc multiSplit(s: string): seq[string] =
|
||||
## Returns ``s`` split by newline and comma characters.
|
||||
|
|
@ -115,12 +116,12 @@ proc multiSplit(s: string): seq[string] =
|
|||
if len(result) < 1:
|
||||
return @[s]
|
||||
|
||||
proc readPackageInfo*(path: string): TPackageInfo =
|
||||
proc readPackageInfo*(path: string): PackageInfo =
|
||||
result = initPackageInfo()
|
||||
result.mypath = path
|
||||
var fs = newFileStream(path, fmRead)
|
||||
if fs != nil:
|
||||
var p: TCfgParser
|
||||
var p: CfgParser
|
||||
open(p, fs, path)
|
||||
var currentSection = ""
|
||||
while true:
|
||||
|
|
@ -159,48 +160,52 @@ proc readPackageInfo*(path: string): TPackageInfo =
|
|||
result.backend = ev.value.toLower()
|
||||
case result.backend.normalize
|
||||
of "javascript": result.backend = "js"
|
||||
else: discard
|
||||
else:
|
||||
raise newException(ENimble, "Invalid field: " & ev.key)
|
||||
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(ENimble, "Invalid field: " & ev.key)
|
||||
else: raise newException(ENimble, "Invalid section: " & currentSection)
|
||||
of cfgOption: raise newException(ENimble, "Invalid package info, should not contain --" & ev.value)
|
||||
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(ENimble, "Error parsing .nimble file: " & ev.msg)
|
||||
raise newException(NimbleError, "Error parsing .nimble file: " & ev.msg)
|
||||
close(p)
|
||||
else:
|
||||
raise newException(EInvalidValue, "Cannot open package info: " & path)
|
||||
raise newException(ValueError, "Cannot open package info: " & path)
|
||||
validatePackageInfo(result, path)
|
||||
|
||||
proc optionalField(obj: PJsonNode, name: string, default = ""): string =
|
||||
proc optionalField(obj: JsonNode, name: string, default = ""): string =
|
||||
## Queries ``obj`` for the optional ``name`` string.
|
||||
##
|
||||
## Returns the value of ``name`` if it is a valid string, or aborts execution
|
||||
## if the field exists but is not of string type. If ``name`` is not present,
|
||||
## returns ``default``.
|
||||
if existsKey(obj, name):
|
||||
if hasKey(obj, name):
|
||||
if obj[name].kind == JString:
|
||||
return obj[name].str
|
||||
else:
|
||||
raise newException(ENimble, "Corrupted packages.json file. " & name & " field is of unexpected type.")
|
||||
raise newException(NimbleError, "Corrupted packages.json file. " & name &
|
||||
" field is of unexpected type.")
|
||||
else: return default
|
||||
|
||||
proc requiredField(obj: PJsonNode, name: string): string =
|
||||
proc requiredField(obj: JsonNode, name: string): string =
|
||||
## Queries ``obj`` for the required ``name`` string.
|
||||
##
|
||||
## Aborts execution if the field does not exist or is of invalid json type.
|
||||
result = optionalField(obj, name, nil)
|
||||
if result == nil:
|
||||
raise newException(ENimble,
|
||||
raise newException(NimbleError,
|
||||
"Package in packages.json file does not contain a " & name & " field.")
|
||||
|
||||
proc fromJson(obj: PJSonNode): TPackage =
|
||||
## Constructs a TPackage object from a JSON node.
|
||||
proc fromJson(obj: JSonNode): Package =
|
||||
## Constructs a Package object from a JSON node.
|
||||
##
|
||||
## Aborts execution if the JSON node doesn't contain the required fields.
|
||||
result.name = obj.requiredField("name")
|
||||
|
|
@ -215,7 +220,7 @@ proc fromJson(obj: PJSonNode): TPackage =
|
|||
result.description = obj.requiredField("description")
|
||||
result.web = obj.optionalField("web")
|
||||
|
||||
proc readMetadata*(path: string): TMetadata =
|
||||
proc readMetaData*(path: string): MetaData =
|
||||
## Reads the metadata present in ``~/.nimble/pkgs/pkg-0.1/nimblemeta.json``
|
||||
var bmeta = path / "nimblemeta.json"
|
||||
if not existsFile(bmeta):
|
||||
|
|
@ -231,7 +236,7 @@ proc readMetadata*(path: string): TMetadata =
|
|||
let jsonmeta = parseJson(cont)
|
||||
result.url = jsonmeta["url"].str
|
||||
|
||||
proc getPackage*(pkg: string, packagesPath: string, resPkg: var TPackage): bool =
|
||||
proc getPackage*(pkg: string, packagesPath: string, resPkg: var Package): bool =
|
||||
## Searches ``packagesPath`` file saving into ``resPkg`` the found package.
|
||||
##
|
||||
## Pass in ``pkg`` the name of the package you are searching for. As
|
||||
|
|
@ -243,12 +248,12 @@ proc getPackage*(pkg: string, packagesPath: string, resPkg: var TPackage): bool
|
|||
resPkg = p.fromJson()
|
||||
return true
|
||||
|
||||
proc getPackageList*(packagesPath: string): seq[TPackage] =
|
||||
proc getPackageList*(packagesPath: string): seq[Package] =
|
||||
## Returns the list of packages found at the specified path.
|
||||
result = @[]
|
||||
let packages = parseFile(packagesPath)
|
||||
for p in packages:
|
||||
let pkg: TPackage = p.fromJson()
|
||||
let pkg: Package = p.fromJson()
|
||||
result.add(pkg)
|
||||
|
||||
proc findNimbleFile*(dir: string): string =
|
||||
|
|
@ -256,17 +261,20 @@ proc findNimbleFile*(dir: string): string =
|
|||
for kind, path in walkDir(dir):
|
||||
if kind == pcFile and path.splitFile.ext in [".babel", ".nimble"]:
|
||||
if result != "":
|
||||
raise newException(ENimble, "Only one .nimble file should be present in " & dir)
|
||||
raise newException(NimbleError,
|
||||
"Only one .nimble file should be present in " & dir)
|
||||
result = path
|
||||
|
||||
proc getPkgInfo*(dir: string): TPackageInfo =
|
||||
## Find the .nimble file in ``dir`` and parses it, returning a TPackageInfo.
|
||||
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(ENimble, "Specified directory does not contain a .nimble file.")
|
||||
raise newException(NimbleError,
|
||||
"Specified directory does not contain a .nimble file.")
|
||||
result = readPackageInfo(nimbleFile)
|
||||
|
||||
proc getInstalledPkgs*(libsDir: string): seq[tuple[pkginfo: TPackageInfo, meta: TMetaData]] =
|
||||
proc getInstalledPkgs*(libsDir: string):
|
||||
seq[tuple[pkginfo: PackageInfo, meta: MetaData]] =
|
||||
## Gets a list of installed packages.
|
||||
##
|
||||
## ``libsDir`` is in most cases: ~/.nimble/pkgs/
|
||||
|
|
@ -275,15 +283,15 @@ proc getInstalledPkgs*(libsDir: string): seq[tuple[pkginfo: TPackageInfo, meta:
|
|||
if kind == pcDir:
|
||||
let nimbleFile = findNimbleFile(path)
|
||||
if nimbleFile != "":
|
||||
let meta = readMetadata(path)
|
||||
let meta = readMetaData(path)
|
||||
result.add((readPackageInfo(nimbleFile), meta))
|
||||
else:
|
||||
# TODO: Abstract logging.
|
||||
echo("WARNING: No .nimble file found for ", path)
|
||||
|
||||
proc findPkg*(pkglist: seq[tuple[pkginfo: TPackageInfo, meta: TMetaData]],
|
||||
dep: TPkgTuple,
|
||||
r: var TPackageInfo): bool =
|
||||
proc findPkg*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]],
|
||||
dep: PkgTuple,
|
||||
r: var PackageInfo): bool =
|
||||
## Searches ``pkglist`` for a package of which version is within the range
|
||||
## of ``dep.ver``. ``True`` is returned if a package is found. If multiple
|
||||
## packages are found the newest one is returned (the one with the highest
|
||||
|
|
@ -298,8 +306,8 @@ proc findPkg*(pkglist: seq[tuple[pkginfo: TPackageInfo, meta: TMetaData]],
|
|||
r = pkg.pkginfo
|
||||
result = true
|
||||
|
||||
proc findAllPkgs*(pkglist: seq[tuple[pkginfo: TPackageInfo, meta: TMetaData]],
|
||||
dep: TPkgTuple): seq[TPackageInfo] =
|
||||
proc findAllPkgs*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]],
|
||||
dep: PkgTuple): seq[PackageInfo] =
|
||||
## Searches ``pkglist`` for packages of which version is within the range
|
||||
## of ``dep.ver``. This is similar to ``findPkg`` but returns multiple
|
||||
## packages if multiple are found.
|
||||
|
|
@ -310,7 +318,7 @@ proc findAllPkgs*(pkglist: seq[tuple[pkginfo: TPackageInfo, meta: TMetaData]],
|
|||
if withinRange(newVersion(pkg.pkginfo.version), dep.ver):
|
||||
result.add pkg.pkginfo
|
||||
|
||||
proc getRealDir*(pkgInfo: TPackageInfo): string =
|
||||
proc getRealDir*(pkgInfo: PackageInfo): string =
|
||||
## Returns the ``pkgInfo.srcDir`` or the .mypath directory if package does
|
||||
## not specify the src dir.
|
||||
if pkgInfo.srcDir != "":
|
||||
|
|
@ -334,7 +342,7 @@ proc getNameVersion*(pkgpath: string): tuple[name, version: string] =
|
|||
result.version = tail[i+1 .. -1]
|
||||
break
|
||||
|
||||
proc echoPackage*(pkg: TPackage) =
|
||||
proc echoPackage*(pkg: Package) =
|
||||
echo(pkg.name & ":")
|
||||
echo(" url: " & pkg.url & " (" & pkg.downloadMethod & ")")
|
||||
echo(" tags: " & pkg.tags.join(", "))
|
||||
|
|
@ -343,7 +351,7 @@ proc echoPackage*(pkg: TPackage) =
|
|||
if pkg.web.len > 0:
|
||||
echo(" website: " & pkg.web)
|
||||
|
||||
proc getDownloadDirName*(pkg: TPackage, verRange: PVersionRange): string =
|
||||
proc getDownloadDirName*(pkg: Package, verRange: VersionRangeRef): string =
|
||||
result = pkg.name
|
||||
let verSimple = getSimpleString(verRange)
|
||||
if verSimple != "":
|
||||
|
|
@ -351,5 +359,7 @@ proc getDownloadDirName*(pkg: TPackage, verRange: PVersionRange): string =
|
|||
result.add verSimple
|
||||
|
||||
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/packagea-0.1") ==
|
||||
("packagea", "0.1")
|
||||
doAssert getNameVersion("/home/user/.nimble/libs/package-a-0.1") ==
|
||||
("package-a", "0.1")
|
||||
|
|
|
|||
|
|
@ -2,25 +2,23 @@
|
|||
# BSD License. Look at license.txt for more info.
|
||||
#
|
||||
# Various miscellaneous utility functions reside here.
|
||||
import osproc, pegs, strutils, os, parseurl, sets, json
|
||||
import version, packageinfo
|
||||
|
||||
type
|
||||
ENimble* = object of EBase
|
||||
import osproc, pegs, strutils, os, uri, sets, json
|
||||
import version, packageinfo, nimbletypes
|
||||
|
||||
proc doCmd*(cmd: string) =
|
||||
let bin = cmd.split(' ')[0]
|
||||
if findExe(bin) == "":
|
||||
raise newException(ENimble, "'" & bin & "' not in PATH.")
|
||||
raise newException(NimbleError, "'" & bin & "' not in PATH.")
|
||||
|
||||
let exitCode = execCmd(cmd)
|
||||
if exitCode != QuitSuccess:
|
||||
raise newException(ENimble, "Execution failed with exit code " & $exitCode)
|
||||
raise newException(NimbleError,
|
||||
"Execution failed with exit code " & $exitCode)
|
||||
|
||||
proc doCmdEx*(cmd: string): tuple[output: TaintedString, exitCode: int] =
|
||||
let bin = cmd.split(' ')[0]
|
||||
if findExe(bin) == "":
|
||||
raise newException(ENimble, "'" & bin & "' not in PATH.")
|
||||
raise newException(NimbleError, "'" & bin & "' not in PATH.")
|
||||
return execCmdEx(cmd)
|
||||
|
||||
template cd*(dir: string, body: stmt) =
|
||||
|
|
@ -36,7 +34,7 @@ proc getNimBin*: string =
|
|||
if findExe("nim") != "": result = "nim"
|
||||
elif findExe("nimrod") != "": result = "nimrod"
|
||||
|
||||
proc getNimrodVersion*: TVersion =
|
||||
proc getNimrodVersion*: Version =
|
||||
let nimBin = getNimBin()
|
||||
let vOutput = doCmdEx(nimBin & " -v").output
|
||||
var matches: array[0..MaxSubpatterns, string]
|
||||
|
|
@ -61,7 +59,7 @@ proc changeRoot*(origRoot, newRoot, path: string): string =
|
|||
if path.startsWith(origRoot):
|
||||
return newRoot / path[origRoot.len .. -1]
|
||||
else:
|
||||
raise newException(EInvalidValue,
|
||||
raise newException(ValueError,
|
||||
"Cannot change root of path: Path does not begin with original root.")
|
||||
|
||||
proc copyFileD*(fro, to: string): string =
|
||||
|
|
@ -78,37 +76,37 @@ proc copyDirD*(fro, to: string): seq[string] =
|
|||
createDir(changeRoot(fro, to, path.splitFile.dir))
|
||||
result.add copyFileD(path, changeRoot(fro, to, path))
|
||||
|
||||
proc getDownloadDirName*(url: string, verRange: PVersionRange): string =
|
||||
## Creates a directory name based on the specified ``url``
|
||||
proc getDownloadDirName*(uri: string, verRange: VersionRangeRef): string =
|
||||
## Creates a directory name based on the specified ``uri`` (url)
|
||||
result = ""
|
||||
let purl = parseUrl(url)
|
||||
for i in purl.hostname:
|
||||
let puri = parseUri(uri)
|
||||
for i in puri.hostname:
|
||||
case i
|
||||
of strutils.Letters, strutils.Digits:
|
||||
result.add i
|
||||
else: nil
|
||||
else: discard
|
||||
result.add "_"
|
||||
for i in purl.path:
|
||||
for i in puri.path:
|
||||
case i
|
||||
of strutils.Letters, strutils.Digits:
|
||||
result.add i
|
||||
else: nil
|
||||
else: discard
|
||||
|
||||
let verSimple = getSimpleString(verRange)
|
||||
if verSimple != "":
|
||||
result.add "_"
|
||||
result.add verSimple
|
||||
|
||||
proc incl*(s: var TSet[string], v: seq[string] | TSet[string]) =
|
||||
proc incl*(s: var HashSet[string], v: seq[string] | HashSet[string]) =
|
||||
for i in v:
|
||||
s.incl i
|
||||
|
||||
proc contains*(j: PJsonNode, elem: PJsonNode): bool =
|
||||
proc contains*(j: JsonNode, elem: JsonNode): bool =
|
||||
for i in j:
|
||||
if i == elem:
|
||||
return true
|
||||
|
||||
proc contains*(j: PJsonNode, elem: tuple[key: string, val: PJsonNode]): bool =
|
||||
proc contains*(j: JsonNode, elem: tuple[key: string, val: JsonNode]): bool =
|
||||
for key, val in pairs(j):
|
||||
if key == elem.key and val == elem.val:
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
## Module for handling versions and version ranges such as ``>= 1.0 & <= 1.5``
|
||||
import strutils, tables, hashes, parseutils
|
||||
type
|
||||
TVersion* = distinct string
|
||||
TSpecial* = distinct string
|
||||
Version* = distinct string
|
||||
Special* = distinct string
|
||||
|
||||
TVersionRangeEnum* = enum
|
||||
VersionRangeEnum* = enum
|
||||
verLater, # > V
|
||||
verEarlier, # < V
|
||||
verEqLater, # >= V -- Equal or later
|
||||
|
|
@ -17,32 +17,32 @@ type
|
|||
verAny, # *
|
||||
verSpecial # #head
|
||||
|
||||
PVersionRange* = ref TVersionRange
|
||||
TVersionRange* = object
|
||||
case kind*: TVersionRangeEnum
|
||||
VersionRangeRef* = ref VersionRange
|
||||
VersionRange* = object
|
||||
case kind*: VersionRangeEnum
|
||||
of verLater, verEarlier, verEqLater, verEqEarlier, verEq:
|
||||
ver*: TVersion
|
||||
ver*: Version
|
||||
of verSpecial:
|
||||
spe*: TSpecial
|
||||
spe*: Special
|
||||
of verIntersect:
|
||||
verILeft, verIRight: PVersionRange
|
||||
verILeft, verIRight: VersionRangeRef
|
||||
of verAny:
|
||||
nil
|
||||
|
||||
EParseVersion* = object of EInvalidValue
|
||||
ParseVersionError* = object of ValueError
|
||||
|
||||
proc newVersion*(ver: string): TVersion = return TVersion(ver)
|
||||
proc newSpecial*(spe: string): TSpecial = return TSpecial(spe)
|
||||
proc newVersion*(ver: string): Version = return Version(ver)
|
||||
proc newSpecial*(spe: string): Special = return Special(spe)
|
||||
|
||||
proc `$`*(ver: TVersion): string {.borrow.}
|
||||
proc `$`*(ver: Version): string {.borrow.}
|
||||
|
||||
proc hash*(ver: TVersion): THash {.borrow.}
|
||||
proc hash*(ver: Version): THash {.borrow.}
|
||||
|
||||
proc `$`*(ver: TSpecial): string {.borrow.}
|
||||
proc `$`*(ver: Special): string {.borrow.}
|
||||
|
||||
proc hash*(ver: TSpecial): THash {.borrow.}
|
||||
proc hash*(ver: Special): THash {.borrow.}
|
||||
|
||||
proc `<`*(ver: TVersion, ver2: TVersion): bool =
|
||||
proc `<`*(ver: Version, ver2: Version): bool =
|
||||
var sVer = string(ver).split('.')
|
||||
var sVer2 = string(ver2).split('.')
|
||||
for i in 0..max(sVer.len, sVer2.len)-1:
|
||||
|
|
@ -55,11 +55,11 @@ proc `<`*(ver: TVersion, ver2: TVersion): bool =
|
|||
if sVerI < sVerI2:
|
||||
return true
|
||||
elif sVerI == sVerI2:
|
||||
nil
|
||||
discard
|
||||
else:
|
||||
return false
|
||||
|
||||
proc `==`*(ver: TVersion, ver2: TVersion): bool =
|
||||
proc `==`*(ver: Version, ver2: Version): bool =
|
||||
var sVer = string(ver).split('.')
|
||||
var sVer2 = string(ver2).split('.')
|
||||
for i in 0..max(sVer.len, sVer2.len)-1:
|
||||
|
|
@ -74,13 +74,13 @@ proc `==`*(ver: TVersion, ver2: TVersion): bool =
|
|||
else:
|
||||
return false
|
||||
|
||||
proc `==`*(spe: TSpecial, spe2: TSpecial): bool =
|
||||
proc `==`*(spe: Special, spe2: Special): bool =
|
||||
return ($spe).toLower() == ($spe2).toLower()
|
||||
|
||||
proc `<=`*(ver: TVersion, ver2: TVersion): bool =
|
||||
proc `<=`*(ver: Version, ver2: Version): bool =
|
||||
return (ver == ver2) or (ver < ver2)
|
||||
|
||||
proc withinRange*(ver: TVersion, ran: PVersionRange): bool =
|
||||
proc withinRange*(ver: Version, ran: VersionRangeRef): bool =
|
||||
case ran.kind
|
||||
of verLater:
|
||||
return ver > ran.ver
|
||||
|
|
@ -99,7 +99,7 @@ proc withinRange*(ver: TVersion, ran: PVersionRange): bool =
|
|||
of verAny:
|
||||
return true
|
||||
|
||||
proc withinRange*(spe: TSpecial, ran: PVersionRange): bool =
|
||||
proc withinRange*(spe: Special, ran: VersionRangeRef): bool =
|
||||
case ran.kind
|
||||
of verLater, verEarlier, verEqLater, verEqEarlier, verEq, verIntersect:
|
||||
return false
|
||||
|
|
@ -108,16 +108,17 @@ proc withinRange*(spe: TSpecial, ran: PVersionRange): bool =
|
|||
of verAny:
|
||||
return true
|
||||
|
||||
proc contains*(ran: PVersionRange, ver: TVersion): bool =
|
||||
proc contains*(ran: VersionRangeRef, ver: Version): bool =
|
||||
return withinRange(ver, ran)
|
||||
|
||||
proc contains*(ran: PVersionRange, spe: TSpecial): bool =
|
||||
proc contains*(ran: VersionRangeRef, spe: Special): bool =
|
||||
return withinRange(spe, ran)
|
||||
|
||||
proc makeRange*(version: string, op: string): PVersionRange =
|
||||
proc makeRange*(version: string, op: string): VersionRangeRef =
|
||||
new(result)
|
||||
if version == "":
|
||||
raise newException(EParseVersion, "A version needs to accompany the operator.")
|
||||
raise newException(ParseVersionError,
|
||||
"A version needs to accompany the operator.")
|
||||
case op
|
||||
of ">":
|
||||
result.kind = verLater
|
||||
|
|
@ -130,15 +131,15 @@ proc makeRange*(version: string, op: string): PVersionRange =
|
|||
of "":
|
||||
result.kind = verEq
|
||||
else:
|
||||
raise newException(EParseVersion, "Invalid operator: " & op)
|
||||
result.ver = TVersion(version)
|
||||
raise newException(ParseVersionError, "Invalid operator: " & op)
|
||||
result.ver = Version(version)
|
||||
|
||||
proc parseVersionRange*(s: string): PVersionRange =
|
||||
proc parseVersionRange*(s: string): VersionRangeRef =
|
||||
# >= 1.5 & <= 1.8
|
||||
new(result)
|
||||
if s[0] == '#':
|
||||
result.kind = verSpecial
|
||||
result.spe = s[1 .. -1].TSpecial
|
||||
result.spe = s[1 .. -1].Special
|
||||
return
|
||||
|
||||
var i = 0
|
||||
|
|
@ -159,7 +160,7 @@ proc parseVersionRange*(s: string): PVersionRange =
|
|||
# Disallow more than one verIntersect. It's pointless and could lead to
|
||||
# major unpredictable mistakes.
|
||||
if result.verIRight.kind == verIntersect:
|
||||
raise newException(EParseVersion,
|
||||
raise newException(ParseVersionError,
|
||||
"Having more than one `&` in a version range is pointless")
|
||||
|
||||
break
|
||||
|
|
@ -175,13 +176,15 @@ proc parseVersionRange*(s: string): PVersionRange =
|
|||
# Make sure '0.9 8.03' is not allowed.
|
||||
if version != "" and i < s.len:
|
||||
if s[i+1] in {'0'..'9', '.'}:
|
||||
raise newException(EParseVersion, "Whitespace is not allowed in a version literal.")
|
||||
raise newException(ParseVersionError,
|
||||
"Whitespace is not allowed in a version literal.")
|
||||
|
||||
else:
|
||||
raise newException(EParseVersion, "Unexpected char in version range: " & s[i])
|
||||
raise newException(ParseVersionError,
|
||||
"Unexpected char in version range: " & s[i])
|
||||
inc(i)
|
||||
|
||||
proc `$`*(verRange: PVersionRange): string =
|
||||
proc `$`*(verRange: VersionRangeRef): string =
|
||||
case verRange.kind
|
||||
of verLater:
|
||||
result = "> "
|
||||
|
|
@ -202,34 +205,36 @@ proc `$`*(verRange: PVersionRange): string =
|
|||
|
||||
result.add(string(verRange.ver))
|
||||
|
||||
proc getSimpleString*(verRange: PVersionRange): string =
|
||||
## Gets a string with no special symbols and spaces. Used for dir name creation
|
||||
## in tools.nim
|
||||
proc getSimpleString*(verRange: VersionRangeRef): string =
|
||||
## Gets a string with no special symbols and spaces. Used for dir name
|
||||
## creation in tools.nim
|
||||
case verRange.kind
|
||||
of verSpecial:
|
||||
result = $verRange.spe
|
||||
of verLater, verEarlier, verEqLater, verEqEarlier, verEq:
|
||||
result = $verRange.ver
|
||||
of verIntersect:
|
||||
result = getSimpleString(verRange.verILeft) & "_" & getSimpleString(verRange.verIRight)
|
||||
result = getSimpleString(verRange.verILeft) & "_" &
|
||||
getSimpleString(verRange.verIRight)
|
||||
of verAny:
|
||||
result = ""
|
||||
|
||||
proc newVRAny*(): PVersionRange =
|
||||
proc newVRAny*(): VersionRangeRef =
|
||||
new(result)
|
||||
result.kind = verAny
|
||||
|
||||
proc newVREarlier*(ver: string): PVersionRange =
|
||||
proc newVREarlier*(ver: string): VersionRangeRef =
|
||||
new(result)
|
||||
result.kind = verEarlier
|
||||
result.ver = newVersion(ver)
|
||||
|
||||
proc newVREq*(ver: string): PVersionRange =
|
||||
proc newVREq*(ver: string): VersionRangeRef =
|
||||
new(result)
|
||||
result.kind = verEq
|
||||
result.ver = newVersion(ver)
|
||||
|
||||
proc findLatest*(verRange: PVersionRange, versions: TTable[TVersion, string]): tuple[ver: TVersion, tag: string] =
|
||||
proc findLatest*(verRange: VersionRangeRef,
|
||||
versions: Table[Version, string]): tuple[ver: Version, tag: string] =
|
||||
result = (newVersion(""), "")
|
||||
for ver, tag in versions:
|
||||
if not withinRange(ver, verRange): continue
|
||||
|
|
@ -263,8 +268,10 @@ when isMainModule:
|
|||
doAssert(newVersion("") < newVersion("1.0.0"))
|
||||
doAssert(newVersion("") < newVersion("0.1.0"))
|
||||
|
||||
var versions = toTable[TVersion, string]({newVersion("0.1.1"): "v0.1.1", newVersion("0.2.3"): "v0.2.3", newVersion("0.5"): "v0.5"})
|
||||
doAssert findLatest(parseVersionRange(">= 0.1 & <= 0.4"), versions) == (newVersion("0.2.3"), "v0.2.3")
|
||||
var versions = toTable[Version, string]({newVersion("0.1.1"): "v0.1.1",
|
||||
newVersion("0.2.3"): "v0.2.3", newVersion("0.5"): "v0.5"})
|
||||
doAssert findLatest(parseVersionRange(">= 0.1 & <= 0.4"), versions) ==
|
||||
(newVersion("0.2.3"), "v0.2.3")
|
||||
|
||||
# TODO: Allow these in later versions?
|
||||
#doAssert newVersion("0.1-rc1") < newVersion("0.2")
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ test "can reject same version dependencies":
|
|||
let ls = outp.strip.splitLines()
|
||||
check exitCode != QuitSuccess
|
||||
check ls[ls.len-1] == "Error: unhandled exception: Cannot satisfy the " &
|
||||
"dependency on PackageA 0.2.0 and PackageA 0.5.0 [ENimble]"
|
||||
"dependency on PackageA 0.2.0 and PackageA 0.5.0 [NimbleError]"
|
||||
|
||||
test "can update":
|
||||
check execCmdEx(path & " update").exitCode == QuitSuccess
|
||||
|
|
@ -54,7 +54,7 @@ test "can uninstall":
|
|||
let ls = outp.processOutput()
|
||||
check exitCode != QuitSuccess
|
||||
check ls[ls.len-1] == " Cannot uninstall issue27b (0.1.0) because " &
|
||||
"issue27a (0.1.0) depends on it [ENimble]"
|
||||
"issue27a (0.1.0) depends on it [NimbleError]"
|
||||
|
||||
check execCmdEx(path & " uninstall -y issue27").exitCode == QuitSuccess
|
||||
check execCmdEx(path & " uninstall -y issue27a").exitCode == QuitSuccess
|
||||
|
|
@ -76,5 +76,6 @@ test "can uninstall":
|
|||
# Remove the rest of the installed packages.
|
||||
check execCmdEx(path & " uninstall -y PackageB").exitCode == QuitSuccess
|
||||
|
||||
check execCmdEx(path & " uninstall -y PackageA@0.2 issue27b").exitCode == QuitSuccess
|
||||
check execCmdEx(path & " uninstall -y PackageA@0.2 issue27b").exitCode ==
|
||||
QuitSuccess
|
||||
check (not dirExists(getHomeDir() / ".nimble" / "pkgs" / "PackageA-0.2.0"))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue