Implements #41.

Also, building of binary packages now takes place before any old packages
are removed. This is to prevent the case where the old package is removed
and a new version of the same package fails to build so the user is left
with no package at all.
This commit is contained in:
Dominik Picheta 2014-06-20 20:10:49 +01:00
commit 10de991d3d
5 changed files with 95 additions and 56 deletions

View file

@ -2,9 +2,9 @@
# BSD License. Look at license.txt for more info. # BSD License. Look at license.txt for more info.
import httpclient, parseopt, os, strutils, osproc, pegs, tables, parseutils, import httpclient, parseopt, os, strutils, osproc, pegs, tables, parseutils,
strtabs, json, algorithm strtabs, json, algorithm, sets
import babelpkg/packageinfo, babelpkg/version, babelpkg/common, babelpkg/tools, babelpkg/download import babelpkg/packageinfo, babelpkg/version, babelpkg/tools, babelpkg/download
type type
TOptions = object TOptions = object
@ -180,20 +180,26 @@ proc checkInstallDir(pkgInfo: TPackageInfo,
if thisDir[0] == '.': result = true if thisDir[0] == '.': result = true
if thisDir == "nimcache": result = true if thisDir == "nimcache": result = true
proc copyWithExt(origDir, currentDir, dest: string, pkgInfo: TPackageInfo) = proc copyWithExt(origDir, currentDir, dest: string,
pkgInfo: TPackageInfo): seq[string] =
## Returns the filenames of the files that have been copied
## (their destination).
result = @[]
for kind, path in walkDir(currentDir): for kind, path in walkDir(currentDir):
if kind == pcDir: if kind == pcDir:
copyWithExt(origDir, path, dest, pkgInfo) result.add copyWithExt(origDir, path, dest, pkgInfo)
else: else:
for iExt in pkgInfo.installExt: for iExt in pkgInfo.installExt:
if path.splitFile.ext == ('.' & iExt): if path.splitFile.ext == ('.' & iExt):
createDir(changeRoot(origDir, dest, path).splitFile.dir) createDir(changeRoot(origDir, dest, path).splitFile.dir)
copyFileD(path, changeRoot(origDir, dest, path)) result.add copyFileD(path, changeRoot(origDir, dest, path))
proc copyFilesRec(origDir, currentDir, dest: string, proc copyFilesRec(origDir, currentDir, dest: string,
options: TOptions, pkgInfo: TPackageInfo) = options: TOptions, pkgInfo: TPackageInfo): TSet[string] =
## Copies all the required files, skips files specified in the .babel file ## Copies all the required files, skips files specified in the .babel file
## (TPackageInfo). ## (TPackageInfo).
## Returns a list of filepaths to files which have been installed.
result = initSet[string]()
let whitelistMode = let whitelistMode =
pkgInfo.installDirs.len != 0 or pkgInfo.installDirs.len != 0 or
pkgInfo.installFiles.len != 0 or pkgInfo.installFiles.len != 0 or
@ -207,7 +213,7 @@ proc copyFilesRec(origDir, currentDir, dest: string,
else: else:
quit(QuitSuccess) quit(QuitSuccess)
createDir(dest / file.splitFile.dir) createDir(dest / file.splitFile.dir)
copyFileD(src, dest / file) result.incl copyFileD(src, dest / file)
for dir in pkgInfo.installDirs: for dir in pkgInfo.installDirs:
# TODO: Allow skipping files inside dirs? # TODO: Allow skipping files inside dirs?
@ -217,9 +223,9 @@ proc copyFilesRec(origDir, currentDir, dest: string,
continue continue
else: else:
quit(QuitSuccess) quit(QuitSuccess)
copyDirD(origDir / dir, dest / dir) result.incl copyDirD(origDir / dir, dest / dir)
copyWithExt(origDir, currentDir, dest, pkgInfo) result.incl copyWithExt(origDir, currentDir, dest, pkgInfo)
else: else:
for kind, file in walkDir(currentDir): for kind, file in walkDir(currentDir):
if kind == pcDir: if kind == pcDir:
@ -229,15 +235,15 @@ proc copyFilesRec(origDir, currentDir, dest: string,
# Create the dir. # Create the dir.
createDir(changeRoot(origDir, dest, file)) createDir(changeRoot(origDir, dest, file))
copyFilesRec(origDir, file, dest, options, pkgInfo) result.incl copyFilesRec(origDir, file, dest, options, pkgInfo)
else: else:
let skip = pkgInfo.checkInstallFile(origDir, file) let skip = pkgInfo.checkInstallFile(origDir, file)
if skip: continue if skip: continue
copyFileD(file, changeRoot(origDir, dest, file)) result.incl copyFileD(file, changeRoot(origDir, dest, file))
copyFileD(pkgInfo.mypath, result.incl copyFileD(pkgInfo.mypath,
changeRoot(pkgInfo.mypath.splitFile.dir, dest, pkgInfo.mypath)) changeRoot(pkgInfo.mypath.splitFile.dir, dest, pkgInfo.mypath))
proc install(packages: seq[tuple[name: string, verRange: PVersionRange]], proc install(packages: seq[tuple[name: string, verRange: PVersionRange]],
@ -286,19 +292,52 @@ proc buildFromDir(pkgInfo: TPackageInfo, paths: seq[string]) =
doCmd("nimrod $# -d:release --noBabelPath $# \"$#\"" % doCmd("nimrod $# -d:release --noBabelPath $# \"$#\"" %
[pkgInfo.backend, args, realDir / bin.changeFileExt("nim")]) [pkgInfo.backend, args, realDir / bin.changeFileExt("nim")])
proc installFromDir(dir: string, latest: bool, options: TOptions, url: string): seq[string] = proc saveBabelMeta(pkgDestDir, url: string, filesInstalled: TSet[string]) =
## Returns where package has been installed to. var babelmeta = %{"url": %url}
babelmeta["files"] = newJArray()
for file in filesInstalled:
babelmeta["files"].add(%changeRoot(pkgDestDir, "", file))
writeFile(pkgDestDir / "babelmeta.json", $babelmeta)
proc removePkgDir(dir: string, options: TOptions) =
## Removes files belonging to the package in ``dir``.
try:
var babelmeta = parseFile(dir / "babelmeta.json")
if not babelmeta.hasKey("files"):
raise newException(EJsonParsingError,
"Meta data does not contain required info.")
for file in babelmeta["files"]:
removeFile(dir / file.str)
except EOS, EJsonParsingError:
echo("Error: Unable to read babelmeta.json: ", getCurrentExceptionMsg())
if not options.prompt("Would you like to COMPLETELY overwrite ALL files " &
"in " & dir & "?"):
quit(QuitSuccess)
removeDir(dir)
proc installFromDir(dir: string, latest: bool, options: TOptions,
url: string): seq[string] =
## 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 ## The return value of this function is used by
## ``processDeps`` to gather a list of paths to pass to the nimrod compiler. ## ``processDeps`` to gather a list of paths to pass to the nimrod compiler.
var pkgInfo = getPkgInfo(dir) var pkgInfo = getPkgInfo(dir)
let realDir = pkgInfo.getRealDir() let realDir = pkgInfo.getRealDir()
# Dependencies need to be processed before the creation of the pkg dir.
let paths = processDeps(pkginfo, options)
echo("Installing ", pkginfo.name, "-", pkginfo.version)
# Build before removing an existing package (if one exists). This way
# if the build fails then the old package will still be installed.
if pkgInfo.bin.len > 0: buildFromDir(pkgInfo, paths)
let versionStr = (if latest: "" else: '-' & pkgInfo.version) let versionStr = (if latest: "" else: '-' & pkgInfo.version)
let pkgDestDir = pkgsDir / (pkgInfo.name & versionStr) let pkgDestDir = pkgsDir / (pkgInfo.name & versionStr)
if existsDir(pkgDestDir): if existsDir(pkgDestDir):
if not options.prompt(pkgInfo.name & versionStr & " already exists. Overwrite?"): if not options.prompt(pkgInfo.name & versionStr & " already exists. Overwrite?"):
quit(QuitSuccess) quit(QuitSuccess)
removeDir(pkgDestDir) removePkgDir(pkgDestDir, options)
# Remove any symlinked binaries # Remove any symlinked binaries
for bin in pkgInfo.bin: for bin in pkgInfo.bin:
# TODO: Check that this binary belongs to the package being installed. # TODO: Check that this binary belongs to the package being installed.
@ -306,24 +345,21 @@ proc installFromDir(dir: string, latest: bool, options: TOptions, url: string):
removeFile(binDir / bin.changeFileExt("bat")) removeFile(binDir / bin.changeFileExt("bat"))
else: else:
removeFile(binDir / bin) removeFile(binDir / bin)
echo("Installing ", pkginfo.name, "-", pkginfo.version) ## Will contain a list of files which have been installed.
var filesInstalled: TSet[string]
# Dependencies need to be processed before the creation of the pkg dir.
let paths = processDeps(pkginfo, options)
if pkgInfo.bin.len > 0: buildFromDir(pkgInfo, paths)
createDir(pkgDestDir) createDir(pkgDestDir)
if pkgInfo.bin.len > 0: if pkgInfo.bin.len > 0:
createDir(binDir) createDir(binDir)
# Copy all binaries and files that are not skipped # Copy all binaries and files that are not skipped
copyFilesRec(realDir, realDir, pkgDestDir, options, pkgInfo) filesInstalled = copyFilesRec(realDir, realDir, pkgDestDir, options,
pkgInfo)
# Set file permissions to +x for all binaries built, # Set file permissions to +x for all binaries built,
# and symlink them on *nix OS' to $babelDir/bin/ # and symlink them on *nix OS' to $babelDir/bin/
for bin in pkgInfo.bin: for bin in pkgInfo.bin:
if not existsFile(pkgDestDir / bin): if not existsFile(pkgDestDir / bin):
copyFileD(realDir / bin, pkgDestDir / bin) filesInstalled.incl copyFileD(realDir / bin, pkgDestDir / bin)
let currentPerms = getFilePermissions(pkgDestDir / bin) let currentPerms = getFilePermissions(pkgDestDir / bin)
setFilePermissions(pkgDestDir / bin, currentPerms + {fpUserExec}) setFilePermissions(pkgDestDir / bin, currentPerms + {fpUserExec})
@ -341,11 +377,11 @@ proc installFromDir(dir: string, latest: bool, options: TOptions, url: string):
else: else:
{.error: "Sorry, your platform is not supported.".} {.error: "Sorry, your platform is not supported.".}
else: else:
copyFilesRec(realDir, realDir, pkgDestDir, options, pkgInfo) filesInstalled = copyFilesRec(realDir, realDir, pkgDestDir, options,
pkgInfo)
# Save a babelmeta.json file. # Save a babelmeta.json file.
var babelmeta = %{"url": %url} saveBabelMeta(pkgDestDir, url, filesInstalled)
writeFile(pkgDestDir / "babelmeta.json", $babelmeta)
result = paths # Return the paths to the dependencies of this package. result = paths # Return the paths to the dependencies of this package.
result.add pkgDestDir result.add pkgDestDir

View file

@ -1,15 +0,0 @@
# Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info.
import os, osproc
type
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)

View file

@ -3,7 +3,7 @@
import parseutils, os, osproc, strutils, tables import parseutils, os, osproc, strutils, tables
import packageinfo, common, version, tools import packageinfo, version, tools
type type
TDownloadMethod* {.pure.} = enum TDownloadMethod* {.pure.} = enum
@ -210,4 +210,4 @@ proc echoPackageVersions*(pkg: TPackage) =
except EOS: except EOS:
echo(getCurrentExceptionMsg()) echo(getCurrentExceptionMsg())
of TDownloadMethod.Hg: of TDownloadMethod.Hg:
echo(" versions: (Remote tag retrieval not supported by " & pkg.downloadMethod & ")") echo(" versions: (Remote tag retrieval not supported by " & pkg.downloadMethod & ")")

View file

@ -1,7 +1,7 @@
# Copyright (C) Dominik Picheta. All rights reserved. # Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info. # BSD License. Look at license.txt for more info.
import parsecfg, json, streams, strutils, parseutils, os import parsecfg, json, streams, strutils, parseutils, os
import version, common import version, tools
type type
TPackageInfo* = object TPackageInfo* = object
mypath*: string ## The path of this .babel file mypath*: string ## The path of this .babel file
@ -323,6 +323,13 @@ proc echoPackage*(pkg: TPackage) =
if pkg.web.len > 0: if pkg.web.len > 0:
echo(" website: " & pkg.web) echo(" website: " & pkg.web)
proc getDownloadDirName*(pkg: TPackage, verRange: PVersionRange): string =
result = pkg.name
let verSimple = getSimpleString(verRange)
if verSimple != "":
result.add "_"
result.add verSimple
when isMainModule: when isMainModule:
doAssert getNameVersion("/home/user/.babel/libs/packagea-0.1") == ("packagea", "0.1") doAssert getNameVersion("/home/user/.babel/libs/packagea-0.1") == ("packagea", "0.1")
doAssert getNameVersion("/home/user/.babel/libs/package-a-0.1") == ("package-a", "0.1") doAssert getNameVersion("/home/user/.babel/libs/package-a-0.1") == ("package-a", "0.1")

View file

@ -2,10 +2,11 @@
# BSD License. Look at license.txt for more info. # BSD License. Look at license.txt for more info.
# #
# Various miscellaneous utility functions reside here. # Various miscellaneous utility functions reside here.
import osproc, pegs, strutils, os, parseurl import osproc, pegs, strutils, os, parseurl, sets
import version, common, packageinfo import version, packageinfo
# TODO: Merge with common.nim? type
EBabel* = object of EBase
proc doCmd*(cmd: string) = proc doCmd*(cmd: string) =
let exitCode = execCmd(cmd) let exitCode = execCmd(cmd)
@ -47,6 +48,19 @@ proc changeRoot*(origRoot, newRoot, path: string): string =
raise newException(EInvalidValue, raise newException(EInvalidValue,
"Cannot change root of path: Path does not begin with original root.") "Cannot change root of path: Path does not begin with original root.")
proc copyFileD*(fro, to: string): string =
## Returns the destination (``to``).
echo(fro, " -> ", to)
copyFile(fro, to)
result = to
proc copyDirD*(fro, to: string): seq[string] =
## Returns the filenames of the files in the directory that were copied.
result = @[]
echo("Copying directory: ", fro, " -> ", to)
for path in walkDirRec(fro):
result.add copyFileD(path, changeRoot(fro, to, path))
proc getDownloadDirName*(url: string, verRange: PVersionRange): string = proc getDownloadDirName*(url: string, verRange: PVersionRange): string =
## Creates a directory name based on the specified ``url`` ## Creates a directory name based on the specified ``url``
result = "" result = ""
@ -68,9 +82,6 @@ proc getDownloadDirName*(url: string, verRange: PVersionRange): string =
result.add "_" result.add "_"
result.add verSimple result.add verSimple
proc getDownloadDirName*(pkg: TPackage, verRange: PVersionRange): string = proc incl*(s: var TSet[string], v: seq[string] | TSet[string]) =
result = pkg.name for i in v:
let verSimple = getSimpleString(verRange) s.incl i
if verSimple != "":
result.add "_"
result.add verSimple