Implemented download of packages residing in mercurial repos.

* Moved download handling code into a download module.
* Documented how versioning works for repos (via tags).
* More info during install about what tag is picked.
* Refactored download code to be more generic.
This commit is contained in:
Dominik Picheta 2013-07-26 23:58:25 +01:00
commit 07b7f46fd6
6 changed files with 162 additions and 77 deletions

View file

@ -3,7 +3,7 @@
import httpclient, parseopt, os, strutils, osproc, pegs, tables, parseutils
import packageinfo, version, common, tools
import packageinfo, version, common, tools, download
type
TActionType = enum
@ -103,29 +103,6 @@ proc update(url: string = defaultPackageURL) =
downloadFile(url, babelDir / "packages.json")
echo("Done.")
proc copyFileD(fro, to: string) =
echo(fro, " -> ", to)
copyFile(fro, to)
proc copyDirD(fro, to: string) =
echo(fro, " -> ", to)
copyDir(fro, to)
proc doCmd(cmd: string) =
let exitCode = execCmd(cmd)
if exitCode != QuitSuccess:
quit("Execution failed with exit code " & $exitCode, QuitFailure)
template cd(dir: string, body: stmt) =
## Sets the current dir to ``dir``, executes ``body`` and restores the
## previous working dir.
let lastDir = getCurrentDir()
echo("cd ", dir)
setCurrentDir(dir)
echo(getCurrentDir())
body
setCurrentDir(lastDir)
proc checkInstallFile(pkgInfo: TPackageInfo,
origDir, file: string): bool =
## Checks whether ``file`` should be installed.
@ -292,61 +269,12 @@ proc installFromDir(dir: string, latest: bool): string =
echo(pkgInfo.name & " installed successfully.")
proc getTagsList(dir: string): seq[string] =
cd dir:
let output = execProcess("git tag")
if output.len > 0:
result = output.splitLines()
else:
result = @[]
proc getVersionList(dir: string): TTable[TVersion, string] =
# Returns: TTable of version -> git tag name
result = initTable[TVersion, string]()
let tags = getTagsList(dir)
for tag in tags:
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 downloadPkg(pkg: TPackage, verRange: PVersionRange): string =
let downloadDir = (getTempDir() / "babel" / pkg.name)
if not existsDir(getTempDir() / "babel"): createDir(getTempDir() / "babel")
echo("Downloading ", pkg.name, " into ", downloadDir, "...")
case pkg.downloadMethod
of "git":
echo("Executing git...")
if existsDir(downloadDir / ".git"):
cd downloadDir:
doCmd("git checkout master")
doCmd("git pull")
else:
removeDir(downloadDir)
doCmd("git clone --depth 1 " & pkg.url & " " & downloadDir)
doDownload(pkg, downloadDir, verRange)
# TODO: Determine if version is a commit hash, if it is. Move the
# git repo to ``babelDir/pkgs``, then babel can simply checkout
# the correct hash instead of constantly cloning and copying.
# N.B. This may still partly be requires, as one lib may require hash A
# whereas another lib requires hash B and they are both required by the
# project you want to build.
let versions = getVersionList(downloadDir)
if versions.len > 0:
let latest = findLatest(verRange, versions)
if latest.tag != "":
cd downloadDir:
doCmd("git checkout " & 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: raise newException(EBabel, "Unknown download method: " & pkg.downloadMethod)
result = downloadDir
proc install(packages: seq[String], verRange: PVersionRange): string =

View file

@ -1,4 +1,15 @@
# Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info.
import os, osproc
type
EBabel* = object of EBase
EBabel* = object of EBase
proc copyFileD*(fro, to: string) =
echo(fro, " -> ", to)
copyFile(fro, to)
proc copyDirD*(fro, to: string) =
echo(fro, " -> ", to)
copyDir(fro, to)

117
download.nim Normal file
View file

@ -0,0 +1,117 @@
# Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info.
import parseutils, os, osproc, strutils, tables
import packageinfo, common, version, tools
type
TDownloadMethod {.pure.} = enum
Git = "git", Hg = "hg"
proc getSpecificDir(meth: TDownloadMethod): string =
case meth
of TDownloadMethod.Git:
".git"
of TDownloadMethod.Hg:
".hg"
proc doCheckout(meth: TDownloadMethod, downloadDir, branch: string) =
case meth
of TDownloadMethod.Git:
cd downloadDir:
doCmd("git checkout " & branch)
of TDownloadMethod.Hg:
cd downloadDir:
doCmd("hg checkout " & branch)
proc doPull(meth: TDownloadMethod, downloadDir: string) =
case meth
of TDownloadMethod.Git:
doCheckout(meth, downloadDir, "master")
cd downloadDir:
doCmd("git pull")
of TDownloadMethod.Hg:
doCheckout(meth, downloadDir, "default")
cd downloadDir:
doCmd("hg pull")
proc doClone(meth: TDownloadMethod, url, downloadDir: string) =
case meth
of TDownloadMethod.Git:
doCmd("git clone --depth 1 " & url & " " & downloadDir)
of TDownloadMethod.Hg:
doCmd("hg clone " & url & " " & downloadDir)
proc getTagsList(dir: string, meth: TDownloadMethod): seq[string] =
cd dir:
var output = execProcess("git tag")
case meth
of TDownloadMethod.Git:
output = execProcess("git tag")
of TDownloadMethod.Hg:
output = execProcess("hg tags")
if output.len > 0:
case meth
of TDownloadMethod.Git:
result = output.splitLines()
of TDownloadMethod.Hg:
result = @[]
for i in output.splitLines():
var tag = ""
discard parseUntil(i, tag, ' ')
if tag != "tip":
result.add(tag)
else:
result = @[]
proc getVersionList(dir: string,
meth: TDownloadMethod): TTable[TVersion, string] =
# Returns: TTable of version -> git tag name
result = initTable[TVersion, string]()
let tags = getTagsList(dir, meth)
for tag in tags:
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 =
case meth
of "git": return TDownloadMethod.Git
of "hg", "mercurial": return TDownloadMethod.Hg
else:
raise newException(EBabel, "Invalid download method: " & meth)
proc doDownload*(pkg: TPackage, downloadDir: string, verRange: PVersionRange) =
let downMethod = pkg.downloadMethod.getDownloadMethod()
echo "Executing ", downMethod, "..."
if existsDir(downloadDir / getSpecificDir(downMethod)):
doPull(downMethod, downloadDir)
else:
removeDir(downloadDir)
doClone(downMethod, pkg.url, downloadDir)
# TODO: Determine if version is a commit hash, if it is. Move the
# git repo to ``babelDir/pkgs``, then babel can simply checkout
# the correct hash instead of constantly cloning and copying.
# N.B. This may still partly be required, as one lib may require hash A
# whereas another lib requires hash B and they are both required by the
# project you want to build.
let versions = getVersionList(downloadDir, downMethod)
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

View file

@ -118,6 +118,19 @@ greater-than (``>``), less-than-or-equal-to (``<=``) and greater-than-or-equal-t
``> 0.2 & < 1.0`` which will install a package with the version greater than 0.2
and less than 1.0.
## Versions
Versions of cloned packages via git or mercurial are determined through
the repo's *tags*.
When installing a package which needs to be downloaded, after the
download is complete and if the package is distributed through a VCS, babel
will check the cloned repo's tags list. If no tags exist, babel will simply
install the HEAD (or tip in mercurial) of the repo. If tags exist, babel will
attempt to look for tags which resemble versions (e.g. v0.1) and will then
find the latest version out of the available tags, once it does so it will install
the package after checking out the latest version.
## .babel reference
### [Package]
@ -126,7 +139,7 @@ and less than 1.0.
* ``name`` - The name of the package.
* ``version`` - The *current* version of this package. This should be incremented
after tagging the current version using ``git tag``.
**after** tagging the current version using ``git tag`` or ``hg tag``.
* ``author`` - The name of the author of this package.
* ``description`` - A string describing the package.
* ``license`` - The name of the license in which this package is licensed under.

View file

@ -9,4 +9,5 @@
* more package download methods
* Allow for proper versions of packages to download. Reuse 'version' field
in packages.json.
* Install only .nim files when installing library packages?
* Install only .nim files when installing library packages?
* Force disable --babelPath when building binary packages?

View file

@ -5,6 +5,21 @@
import osproc, pegs, strutils, os
import version
# TODO: Merge with common.nim?
proc doCmd*(cmd: string) =
let exitCode = execCmd(cmd)
if exitCode != QuitSuccess:
quit("Execution failed with exit code " & $exitCode, QuitFailure)
template cd*(dir: string, body: stmt) =
## Sets the current dir to ``dir``, executes ``body`` and restores the
## previous working dir.
let lastDir = getCurrentDir()
setCurrentDir(dir)
body
setCurrentDir(lastDir)
proc getNimrodVersion*: TVersion =
let vOutput = execProcess("nimrod -v")
var matches: array[0..MaxSubpatterns, string]