From 10de991d3d97d271513334c935ca15b9d9ac1714 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Fri, 20 Jun 2014 20:10:49 +0100 Subject: [PATCH] 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. --- src/babel.nim | 92 +++++++++++++++++++++++++----------- src/babelpkg/common.nim | 15 ------ src/babelpkg/download.nim | 4 +- src/babelpkg/packageinfo.nim | 11 ++++- src/babelpkg/tools.nim | 29 ++++++++---- 5 files changed, 95 insertions(+), 56 deletions(-) delete mode 100644 src/babelpkg/common.nim diff --git a/src/babel.nim b/src/babel.nim index 647acba7..f5ff8230 100644 --- a/src/babel.nim +++ b/src/babel.nim @@ -2,9 +2,9 @@ # BSD License. Look at license.txt for more info. 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 TOptions = object @@ -180,20 +180,26 @@ proc checkInstallDir(pkgInfo: TPackageInfo, if thisDir[0] == '.': 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): if kind == pcDir: - copyWithExt(origDir, path, dest, pkgInfo) + result.add copyWithExt(origDir, path, dest, pkgInfo) else: for iExt in pkgInfo.installExt: if path.splitFile.ext == ('.' & iExt): 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, - options: TOptions, pkgInfo: TPackageInfo) = + options: TOptions, pkgInfo: TPackageInfo): TSet[string] = ## Copies all the required files, skips files specified in the .babel file ## (TPackageInfo). + ## Returns a list of filepaths to files which have been installed. + result = initSet[string]() let whitelistMode = pkgInfo.installDirs.len != 0 or pkgInfo.installFiles.len != 0 or @@ -207,7 +213,7 @@ proc copyFilesRec(origDir, currentDir, dest: string, else: quit(QuitSuccess) createDir(dest / file.splitFile.dir) - copyFileD(src, dest / file) + result.incl copyFileD(src, dest / file) for dir in pkgInfo.installDirs: # TODO: Allow skipping files inside dirs? @@ -217,9 +223,9 @@ proc copyFilesRec(origDir, currentDir, dest: string, continue else: 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: for kind, file in walkDir(currentDir): if kind == pcDir: @@ -229,15 +235,15 @@ proc copyFilesRec(origDir, currentDir, dest: string, # Create the dir. createDir(changeRoot(origDir, dest, file)) - copyFilesRec(origDir, file, dest, options, pkgInfo) + result.incl copyFilesRec(origDir, file, dest, options, pkgInfo) else: let skip = pkgInfo.checkInstallFile(origDir, file) 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)) 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 $# \"$#\"" % [pkgInfo.backend, args, realDir / bin.changeFileExt("nim")]) -proc installFromDir(dir: string, latest: bool, options: TOptions, url: string): seq[string] = - ## Returns where package has been installed to. +proc saveBabelMeta(pkgDestDir, url: string, filesInstalled: TSet[string]) = + 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 ## ``processDeps`` to gather a list of paths to pass to the nimrod compiler. var pkgInfo = getPkgInfo(dir) 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 pkgDestDir = pkgsDir / (pkgInfo.name & versionStr) if existsDir(pkgDestDir): if not options.prompt(pkgInfo.name & versionStr & " already exists. Overwrite?"): quit(QuitSuccess) - removeDir(pkgDestDir) + removePkgDir(pkgDestDir, options) # Remove any symlinked binaries for bin in pkgInfo.bin: # 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")) else: removeFile(binDir / bin) - - echo("Installing ", pkginfo.name, "-", pkginfo.version) - - # 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) + + ## Will contain a list of files which have been installed. + var filesInstalled: TSet[string] createDir(pkgDestDir) if pkgInfo.bin.len > 0: createDir(binDir) # 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, # and symlink them on *nix OS' to $babelDir/bin/ for bin in pkgInfo.bin: if not existsFile(pkgDestDir / bin): - copyFileD(realDir / bin, pkgDestDir / bin) + filesInstalled.incl copyFileD(realDir / bin, pkgDestDir / bin) let currentPerms = getFilePermissions(pkgDestDir / bin) setFilePermissions(pkgDestDir / bin, currentPerms + {fpUserExec}) @@ -341,11 +377,11 @@ proc installFromDir(dir: string, latest: bool, options: TOptions, url: string): else: {.error: "Sorry, your platform is not supported.".} else: - copyFilesRec(realDir, realDir, pkgDestDir, options, pkgInfo) + filesInstalled = copyFilesRec(realDir, realDir, pkgDestDir, options, + pkgInfo) # Save a babelmeta.json file. - var babelmeta = %{"url": %url} - writeFile(pkgDestDir / "babelmeta.json", $babelmeta) + saveBabelMeta(pkgDestDir, url, filesInstalled) result = paths # Return the paths to the dependencies of this package. result.add pkgDestDir diff --git a/src/babelpkg/common.nim b/src/babelpkg/common.nim deleted file mode 100644 index e7d8d9a5..00000000 --- a/src/babelpkg/common.nim +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/babelpkg/download.nim b/src/babelpkg/download.nim index 15681eba..c20865a6 100644 --- a/src/babelpkg/download.nim +++ b/src/babelpkg/download.nim @@ -3,7 +3,7 @@ import parseutils, os, osproc, strutils, tables -import packageinfo, common, version, tools +import packageinfo, version, tools type TDownloadMethod* {.pure.} = enum @@ -210,4 +210,4 @@ proc echoPackageVersions*(pkg: TPackage) = except EOS: echo(getCurrentExceptionMsg()) of TDownloadMethod.Hg: - echo(" versions: (Remote tag retrieval not supported by " & pkg.downloadMethod & ")") \ No newline at end of file + echo(" versions: (Remote tag retrieval not supported by " & pkg.downloadMethod & ")") diff --git a/src/babelpkg/packageinfo.nim b/src/babelpkg/packageinfo.nim index b2877a93..b0c1f32c 100644 --- a/src/babelpkg/packageinfo.nim +++ b/src/babelpkg/packageinfo.nim @@ -1,7 +1,7 @@ # 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, common +import version, tools type TPackageInfo* = object mypath*: string ## The path of this .babel file @@ -323,6 +323,13 @@ proc echoPackage*(pkg: TPackage) = if pkg.web.len > 0: 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: 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") \ No newline at end of file + doAssert getNameVersion("/home/user/.babel/libs/package-a-0.1") == ("package-a", "0.1") diff --git a/src/babelpkg/tools.nim b/src/babelpkg/tools.nim index 77dedcbb..08ea08b9 100644 --- a/src/babelpkg/tools.nim +++ b/src/babelpkg/tools.nim @@ -2,10 +2,11 @@ # BSD License. Look at license.txt for more info. # # Various miscellaneous utility functions reside here. -import osproc, pegs, strutils, os, parseurl -import version, common, packageinfo +import osproc, pegs, strutils, os, parseurl, sets +import version, packageinfo -# TODO: Merge with common.nim? +type + EBabel* = object of EBase proc doCmd*(cmd: string) = let exitCode = execCmd(cmd) @@ -47,6 +48,19 @@ proc changeRoot*(origRoot, newRoot, path: string): string = raise newException(EInvalidValue, "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 = ## Creates a directory name based on the specified ``url`` result = "" @@ -68,9 +82,6 @@ proc getDownloadDirName*(url: string, verRange: PVersionRange): string = result.add "_" result.add verSimple -proc getDownloadDirName*(pkg: TPackage, verRange: PVersionRange): string = - result = pkg.name - let verSimple = getSimpleString(verRange) - if verSimple != "": - result.add "_" - result.add verSimple \ No newline at end of file +proc incl*(s: var TSet[string], v: seq[string] | TSet[string]) = + for i in v: + s.incl i