VCS commit hash, branch or tag can now be specified when installing
packages.
This commit is contained in:
parent
c3cf9d5b5d
commit
b6bde8b0e7
4 changed files with 138 additions and 67 deletions
36
babel.nim
36
babel.nim
|
|
@ -136,7 +136,6 @@ proc prompt(options: TOptions, question: string): bool =
|
|||
let babelDir = getHomeDir() / ".babel"
|
||||
let pkgsDir = babelDir / "pkgs"
|
||||
let binDir = babelDir / "bin"
|
||||
let nimVer = getNimrodVersion()
|
||||
|
||||
proc update(url: string = defaultPackageURL) =
|
||||
## Downloads the package list from the specified URL.
|
||||
|
|
@ -227,8 +226,8 @@ proc copyFilesRec(origDir, currentDir, dest: string, pkgInfo: TPackageInfo) =
|
|||
copyFileD(pkgInfo.mypath,
|
||||
changeRoot(pkgInfo.mypath.splitFile.dir, dest, pkgInfo.mypath))
|
||||
|
||||
proc install(packages: seq[String], verRange: PVersionRange, options: TOptions,
|
||||
doPrompt = true): string {.discardable.}
|
||||
proc install(packages: seq[tuple[name: string, verRange: PVersionRange]],
|
||||
options: TOptions, doPrompt = true): string {.discardable.}
|
||||
proc processDeps(pkginfo: TPackageInfo, options: TOptions): seq[string] =
|
||||
## Verifies and installs dependencies.
|
||||
##
|
||||
|
|
@ -237,6 +236,7 @@ proc processDeps(pkginfo: TPackageInfo, options: TOptions): seq[string] =
|
|||
let pkglist = getInstalledPkgs(pkgsDir)
|
||||
for dep in pkginfo.requires:
|
||||
if dep.name == "nimrod":
|
||||
let nimVer = getNimrodVersion()
|
||||
if not withinRange(nimVer, dep.ver):
|
||||
quit("Unsatisfied dependency: " & dep.name & " (" & $dep.ver & ")")
|
||||
else:
|
||||
|
|
@ -244,7 +244,7 @@ proc processDeps(pkginfo: TPackageInfo, options: TOptions): seq[string] =
|
|||
var pkg: TPackageInfo
|
||||
if not findPkg(pkglist, dep, pkg):
|
||||
echo("None found, installing...")
|
||||
let dest = install(@[dep.name], dep.ver, options)
|
||||
let dest = install(@[(dep.name, dep.ver)], options)
|
||||
result.add(dest)
|
||||
else:
|
||||
echo("Dependency already satisfied.")
|
||||
|
|
@ -328,8 +328,8 @@ proc downloadPkg(pkg: TPackage, verRange: PVersionRange): string =
|
|||
doDownload(pkg, downloadDir, verRange)
|
||||
result = downloadDir
|
||||
|
||||
proc install(packages: seq[String], verRange: PVersionRange, options: TOptions,
|
||||
doPrompt = true): string =
|
||||
proc install(packages: seq[tuple[name: string, verRange: PVersionRange]],
|
||||
options: TOptions, doPrompt = true): string =
|
||||
if packages == @[]:
|
||||
result = installFromDir(getCurrentDir(), false, options)
|
||||
else:
|
||||
|
|
@ -337,19 +337,20 @@ proc install(packages: seq[String], verRange: PVersionRange, options: TOptions,
|
|||
if doPrompt and
|
||||
options.prompt("Local packages.json not found, download it from internet?"):
|
||||
update()
|
||||
install(packages, verRange, options, false)
|
||||
install(packages, options, false)
|
||||
else:
|
||||
quit("Please run babel update.", QuitFailure)
|
||||
for p in packages:
|
||||
|
||||
for pv in packages:
|
||||
var pkg: TPackage
|
||||
if getPackage(p, babelDir / "packages.json", pkg):
|
||||
let downloadDir = downloadPkg(pkg, verRange)
|
||||
if getPackage(pv.name, babelDir / "packages.json", pkg):
|
||||
let downloadDir = downloadPkg(pkg, pv.verRange)
|
||||
result = installFromDir(downloadDir, false, options)
|
||||
else:
|
||||
if doPrompt and
|
||||
options.prompt(p & " not found in local packages.json, check internet for updated packages?"):
|
||||
options.prompt(pv.name & " not found in local packages.json, check internet for updated packages?"):
|
||||
update()
|
||||
install(@[p], verRange, options, false)
|
||||
install(@[pv], options, false)
|
||||
else:
|
||||
raise newException(EBabel, "Package not found.")
|
||||
|
||||
|
|
@ -437,8 +438,15 @@ proc doAction(options: TOptions) =
|
|||
else:
|
||||
update()
|
||||
of ActionInstall:
|
||||
# TODO: Allow user to specify version.
|
||||
install(options.action.optionalName, PVersionRange(kind: verAny), options)
|
||||
var installList: seq[tuple[name: string, verRange: PVersionRange]] = @[]
|
||||
for name in options.action.optionalName:
|
||||
if '#' in name:
|
||||
let i = find(name, '#')
|
||||
installList.add((name[0 .. i-1], name[i .. -1].parseVersionRange()))
|
||||
else:
|
||||
installList.add((name, PVersionRange(kind: verAny)))
|
||||
|
||||
install(installList, options)
|
||||
of ActionSearch:
|
||||
search(options)
|
||||
of ActionList:
|
||||
|
|
|
|||
115
download.nim
115
download.nim
|
|
@ -36,14 +36,16 @@ proc doPull(meth: TDownloadMethod, downloadDir: string) =
|
|||
cd downloadDir:
|
||||
doCmd("hg pull")
|
||||
|
||||
proc doClone(meth: TDownloadMethod, url, downloadDir: string, branch = "") =
|
||||
proc doClone(meth: TDownloadMethod, url, downloadDir: string, branch = "", tip = true) =
|
||||
let branchArg = if branch == "": "" else: "-b " & branch & " "
|
||||
case meth
|
||||
of TDownloadMethod.Git:
|
||||
let depthArg = if tip: "--depth 1 " else: ""
|
||||
# TODO: Get rid of the annoying 'detached HEAD' message somehow?
|
||||
doCmd("git clone --depth 1 " & branchArg & url & " " & downloadDir)
|
||||
doCmd("git clone " & depthArg & branchArg & url & " " & downloadDir)
|
||||
of TDownloadMethod.Hg:
|
||||
doCmd("hg clone -r tip " & branchArg & url & " " & downloadDir)
|
||||
let tipArg = if tip: "-r tip " else: ""
|
||||
doCmd("hg clone " & tipArg & branchArg & url & " " & downloadDir)
|
||||
|
||||
proc getTagsList(dir: string, meth: TDownloadMethod): seq[string] =
|
||||
cd dir:
|
||||
|
|
@ -100,57 +102,70 @@ proc getDownloadMethod*(meth: string): TDownloadMethod =
|
|||
else:
|
||||
raise newException(EBabel, "Invalid download method: " & meth)
|
||||
|
||||
proc getHeadName*(meth: TDownloadMethod): 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"
|
||||
|
||||
proc doDownload*(pkg: TPackage, downloadDir: string, verRange: PVersionRange) =
|
||||
template getLatestByTag(meth: stmt): stmt {.dirty, immediate.} =
|
||||
echo("Found tags...")
|
||||
# Find latest version that fits our ``verRange``.
|
||||
var latest = findLatest(verRange, versions)
|
||||
## Note: HEAD is not used when verRange.kind is verAny. This is
|
||||
## intended behaviour, the latest tagged version will be used in this case.
|
||||
if latest.tag != "":
|
||||
meth
|
||||
|
||||
proc verifyHead() =
|
||||
## Makes sure that HEAD satisfies the requested version range.
|
||||
let pkginfo = getPkgInfo(downloadDir)
|
||||
if pkginfo.version.newVersion notin verRange:
|
||||
raise newException(EBabel,
|
||||
"No versions of " & pkg.name &
|
||||
" exist (this usually means that `git tag` returned nothing)." &
|
||||
"Git HEAD also does not satisfy version range: " & $verRange)
|
||||
|
||||
let downMethod = pkg.downloadMethod.getDownloadMethod()
|
||||
echo "Downloading ", pkg.name, " using ", downMethod, "..."
|
||||
|
||||
case downMethod
|
||||
of TDownloadMethod.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(pkg.url, downMethod).getVersionList()
|
||||
if versions.len > 0:
|
||||
echo("Found tags...")
|
||||
var latest = findLatest(verRange, versions)
|
||||
## Note: HEAD is not used when verRange.kind is verAny. This is
|
||||
## intended behaviour, the latest tagged version will be used in this case.
|
||||
if latest.tag != "":
|
||||
echo("Cloning latest tagged version: ", latest.tag)
|
||||
removeDir(downloadDir)
|
||||
doClone(downMethod, pkg.url, downloadDir, latest.tag)
|
||||
else:
|
||||
# If no commits have been tagged on the repo we just clone HEAD.
|
||||
removeDir(downloadDir)
|
||||
removeDir(downloadDir)
|
||||
if verRange.kind == verSpecial:
|
||||
# We want a specific commit/branch/tag here.
|
||||
if verRange.spe == newSpecial(getHeadName(downMethod)):
|
||||
doClone(downMethod, pkg.url, downloadDir) # Grab HEAD.
|
||||
if verRange.kind != verAny:
|
||||
# Make sure that HEAD satisfies the requested version range.
|
||||
let pkginfo = getPkgInfo(downloadDir)
|
||||
if pkginfo.version.newVersion notin verRange:
|
||||
raise newException(EBabel,
|
||||
"No versions of " & pkg.name &
|
||||
" exist (this usually means that `git tag` returned nothing)." &
|
||||
"Git HEAD also does not satisfy version range: " & $verRange)
|
||||
of TDownloadMethod.Hg:
|
||||
removeDir(downloadDir)
|
||||
doClone(downMethod, pkg.url, downloadDir)
|
||||
let versions = getTagsList(downloadDir, downMethod).getVersionList()
|
||||
|
||||
if versions.len > 0:
|
||||
echo("Found tags...")
|
||||
var latest = findLatest(verRange, versions)
|
||||
## Note: HEAD is not used when verRange.kind is verAny. This is
|
||||
## intended behaviour, the latest tagged version will be used in this case.
|
||||
if latest.tag != "":
|
||||
echo("Switching to latest tagged version: ", latest.tag)
|
||||
doCheckout(downMethod, downloadDir, latest.tag)
|
||||
elif verRange.kind != verAny:
|
||||
let pkginfo = getPkgInfo(downloadDir)
|
||||
if pkginfo.version.newVersion notin verRange:
|
||||
raise newException(EBabel,
|
||||
"No versions of " & pkg.name &
|
||||
" exist (this usually means that `git tag` returned nothing)." &
|
||||
"Git HEAD also does not satisfy version range: " & $verRange)
|
||||
# We use GIT HEAD if it satisfies our ver range
|
||||
else:
|
||||
# We don't know if we got a commit hash or a branch here, and
|
||||
# we can't clone a specific commit (with depth 1) according to:
|
||||
# http://stackoverflow.com/a/7198956/492186
|
||||
doClone(downMethod, pkg.url, downloadDir, tip = false)
|
||||
doCheckout(downMethod, downloadDir, $verRange.spe)
|
||||
else:
|
||||
case downMethod
|
||||
of TDownloadMethod.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(pkg.url, downMethod).getVersionList()
|
||||
if versions.len > 0:
|
||||
getLatestByTag:
|
||||
echo("Cloning latest tagged version: ", latest.tag)
|
||||
doClone(downMethod, pkg.url, downloadDir, latest.tag)
|
||||
else:
|
||||
# If no commits have been tagged on the repo we just clone HEAD.
|
||||
doClone(downMethod, pkg.url, downloadDir) # Grab HEAD.
|
||||
if verRange.kind != verAny:
|
||||
verifyHead()
|
||||
of TDownloadMethod.Hg:
|
||||
doClone(downMethod, pkg.url, downloadDir)
|
||||
let versions = getTagsList(downloadDir, downMethod).getVersionList()
|
||||
|
||||
if versions.len > 0:
|
||||
getLatestByTag:
|
||||
echo("Switching to latest tagged version: ", latest.tag)
|
||||
doCheckout(downMethod, downloadDir, latest.tag)
|
||||
elif verRange.kind != verAny:
|
||||
verifyHead()
|
||||
|
||||
proc echoPackageVersions*(pkg: TPackage) =
|
||||
let downMethod = pkg.downloadMethod.getDownloadMethod()
|
||||
|
|
|
|||
|
|
@ -72,6 +72,10 @@ proc parseRequires(req: string): tuple[name: string, ver: PVersionRange] =
|
|||
var i = skipUntil(req, whitespace)
|
||||
result.name = req[0 .. i].strip
|
||||
result.ver = parseVersionRange(req[i .. -1])
|
||||
elif '#' in req:
|
||||
var i = skipUntil(req, {'#'})
|
||||
result.name = req[0 .. i-1]
|
||||
result.ver = parseVersionRange(req[i .. -1])
|
||||
else:
|
||||
result.name = req.strip
|
||||
result.ver = PVersionRange(kind: verAny)
|
||||
|
|
|
|||
50
version.nim
50
version.nim
|
|
@ -5,6 +5,7 @@
|
|||
import strutils, tables, hashes, parseutils
|
||||
type
|
||||
TVersion* = distinct string
|
||||
TSpecial* = distinct string
|
||||
|
||||
TVersionRangeEnum* = enum
|
||||
verLater, # > V
|
||||
|
|
@ -13,13 +14,16 @@ type
|
|||
verEqEarlier, # <= V -- Equal or earlier
|
||||
verIntersect, # > V & < V
|
||||
verEq, # V
|
||||
verAny # *
|
||||
verAny, # *
|
||||
verSpecial # #head
|
||||
|
||||
PVersionRange* = ref TVersionRange
|
||||
TVersionRange* = object
|
||||
case kind*: TVersionRangeEnum
|
||||
of verLater, verEarlier, verEqLater, verEqEarlier, verEq:
|
||||
ver*: TVersion
|
||||
of verSpecial:
|
||||
spe*: TSpecial
|
||||
of verIntersect:
|
||||
verILeft, verIRight: PVersionRange
|
||||
of verAny:
|
||||
|
|
@ -28,11 +32,16 @@ type
|
|||
EParseVersion* = object of EInvalidValue
|
||||
|
||||
proc newVersion*(ver: string): TVersion = return TVersion(ver)
|
||||
proc newSpecial*(spe: string): TSpecial = return TSpecial(spe)
|
||||
|
||||
proc `$`*(ver: TVersion): String {.borrow.}
|
||||
|
||||
proc hash*(ver: TVersion): THash {.borrow.}
|
||||
|
||||
proc `$`*(ver: TSpecial): String {.borrow.}
|
||||
|
||||
proc hash*(ver: TSpecial): THash {.borrow.}
|
||||
|
||||
proc `<`*(ver: TVersion, ver2: TVersion): Bool =
|
||||
var sVer = string(ver).split('.')
|
||||
var sVer2 = string(ver2).split('.')
|
||||
|
|
@ -65,6 +74,9 @@ proc `==`*(ver: TVersion, ver2: TVersion): Bool =
|
|||
else:
|
||||
return False
|
||||
|
||||
proc `==`*(spe: TSpecial, spe2: TSpecial): bool =
|
||||
return ($spe).toLower() == ($spe2).toLower()
|
||||
|
||||
proc `<=`*(ver: TVersion, ver2: TVersion): Bool =
|
||||
return (ver == ver2) or (ver < ver2)
|
||||
|
||||
|
|
@ -80,14 +92,28 @@ proc withinRange*(ver: TVersion, ran: PVersionRange): Bool =
|
|||
return ver <= ran.ver
|
||||
of verEq:
|
||||
return ver == ran.ver
|
||||
of verSpecial:
|
||||
return False
|
||||
of verIntersect:
|
||||
return withinRange(ver, ran.verILeft) and withinRange(ver, ran.verIRight)
|
||||
of verAny:
|
||||
return True
|
||||
|
||||
proc withinRange*(spe: TSpecial, ran: PVersionRange): Bool =
|
||||
case ran.kind
|
||||
of verLater, verEarlier, verEqLater, verEqEarlier, verEq, verIntersect:
|
||||
return False
|
||||
of verSpecial:
|
||||
return spe == ran.spe
|
||||
of verAny:
|
||||
return True
|
||||
|
||||
proc contains*(ran: PVersionRange, ver: TVersion): bool =
|
||||
return withinRange(ver, ran)
|
||||
|
||||
proc contains*(ran: PVersionRange, spe: TSpecial): bool =
|
||||
return withinRange(spe, ran)
|
||||
|
||||
proc makeRange*(version: string, op: string): PVersionRange =
|
||||
new(result)
|
||||
if version == "":
|
||||
|
|
@ -110,6 +136,10 @@ proc makeRange*(version: string, op: string): PVersionRange =
|
|||
proc parseVersionRange*(s: string): PVersionRange =
|
||||
# >= 1.5 & <= 1.8
|
||||
new(result)
|
||||
if s[0] == '#':
|
||||
result.kind = verSpecial
|
||||
result.spe = s[1 .. -1].TSpecial
|
||||
return
|
||||
|
||||
var i = 0
|
||||
var op = ""
|
||||
|
|
@ -163,6 +193,8 @@ proc `$`*(verRange: PVersionRange): String =
|
|||
result = "<= "
|
||||
of verEq:
|
||||
result = ""
|
||||
of verSpecial:
|
||||
return "#" & $verRange.spe
|
||||
of verIntersect:
|
||||
return $verRange.verILeft & " & " & $verRange.verIRight
|
||||
of verAny:
|
||||
|
|
@ -212,6 +244,7 @@ when isMainModule:
|
|||
doAssert(newVersion("1") == newVersion("1"))
|
||||
doAssert(newVersion("1.0.2.4.6.1.2.123") == newVersion("1.0.2.4.6.1.2.123"))
|
||||
doAssert(newVersion("1.0.2") != newVersion("1.0.2.4.6.1.2.123"))
|
||||
doAssert(newVersion("1.0.3") != newVersion("1.0.2"))
|
||||
|
||||
doAssert(not (newVersion("") < newVersion("0.0.0")))
|
||||
doAssert(newVersion("") < newVersion("1.0.0"))
|
||||
|
|
@ -220,8 +253,19 @@ when isMainModule:
|
|||
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")
|
||||
|
||||
# TODO: These fail.
|
||||
#doAssert newVersion("0.1-rc1") < newVersion("0.2")
|
||||
#doAssert newVersion("0.1-rc1") < newVersion("0.1")
|
||||
|
||||
# Special tests
|
||||
doAssert newSpecial("ab26sgdt362") != newSpecial("ab26saggdt362")
|
||||
doAssert newSpecial("ab26saggdt362") == newSpecial("ab26saggdt362")
|
||||
doAssert newSpecial("head") == newSpecial("HEAD")
|
||||
doAssert newSpecial("head") == newSpecial("head")
|
||||
|
||||
var sp = parseVersionRange("#ab26sgdt362")
|
||||
doAssert newSpecial("ab26sgdt362") in sp
|
||||
doAssert newSpecial("ab26saggdt362") notin sp
|
||||
|
||||
doAssert newVersion("0.1-rc1") < newVersion("0.2")
|
||||
doAssert newVersion("0.1-rc1") < newVersion("0.1")
|
||||
|
||||
echo("Everything works!")
|
||||
Loading…
Add table
Add a link
Reference in a new issue