From f46fcaf6d3d3d7d7b59ae900dfc7d5e7d14a7c80 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 2 Jun 2013 22:38:13 +0100 Subject: [PATCH] Refactored a lot of code -- split many functions into separate module. --- babel.nim | 164 +++++++++++++++--------------------------------- common.nim | 4 ++ packageinfo.nim | 50 ++++++++++++++- tools.nim | 30 +++++++++ 4 files changed, 134 insertions(+), 114 deletions(-) create mode 100644 common.nim create mode 100644 tools.nim diff --git a/babel.nim b/babel.nim index 86174b6..feaac4c 100644 --- a/babel.nim +++ b/babel.nim @@ -3,7 +3,7 @@ import httpclient, parseopt, os, strutils, osproc, pegs, tables, parseutils -import packageinfo, version +import packageinfo, version, common, tools type TActionType = enum @@ -18,19 +18,17 @@ type optionalName: seq[string] # When this is @[], installs package from current dir. of ActionSearch: search: seq[string] # Search string. - - EBabel = object of EBase const help = """ Usage: babel COMMAND [opts] Commands: - install Installs a list of packages. - build Builds a package. - update Updates package list. A package list URL can be optionally specificed. - search Searches for a specified package. - list Lists all packages. + install [pkgname, ...] Installs a list of packages. + build [pkgname] Builds a package. + update [url] Updates package list. A package list URL can be optionally specified. + search pkg/tag Searches for a specified package. Search is performed by tag and by name. + list Lists all packages. """ babelVersion = "0.1.0" defaultPackageURL = "https://github.com/nimrod-code/packages/raw/master/packages.json" @@ -95,13 +93,6 @@ proc prompt(question: string): bool = else: return false -proc getNimrodVersion: TVersion = - let vOutput = execProcess("nimrod -v") - var matches: array[0..MaxSubpatterns, string] - if vOutput.find(peg"'Version'\s{(\d\.)+\d}", matches) == -1: - quit("Couldn't find Nimrod version.", QuitFailure) - newVersion(matches[0]) - let babelDir = getHomeDir() / ".babel" let libsDir = babelDir / "libs" let binDir = babelDir / "bin" @@ -112,40 +103,18 @@ proc update(url: string = defaultPackageURL) = downloadFile(url, babelDir / "packages.json") echo("Done.") -proc findBabelFile(dir: string): string = - result = "" - for kind, path in walkDir(dir): - if kind == pcFile and path.splitFile.ext == ".babel": - if result != "": quit("Only one .babel file should be present in " & dir) - result = path - proc copyFileD(fro, to: string) = echo(fro, " -> ", to) copyFile(fro, to) -proc samePaths(p1, p2: string): bool = - ## Normalizes path (by adding a trailing slash) and compares. - let cp1 = if not p1.endsWith("/"): p1 & "/" else: p1 - let cp2 = if not p2.endsWith("/"): p2 & "/" else: p2 - return cmpPaths(cp1, cp2) == 0 - -proc changeRoot(origRoot, newRoot, path: string): string = - ## origRoot: /home/dom/ - ## newRoot: /home/test/ - ## path: /home/dom/bar/blah/2/foo.txt - ## Return value -> /home/test/bar/blah/2/foo.txt - if path.startsWith(origRoot): - return newRoot / path[origRoot.len .. -1] - else: - raise newException(EInvalidValue, - "Cannot change root of path: Path does not begin with original root.") - proc doCmd(cmd: string) = let exitCode = execCmd(cmd) if exitCode != QuitSuccess: quit("Execution failed with exit code " & $exitCode, QuitFailure) proc copyFilesRec(origDir, currentDir, dest: string, pkgInfo: TPackageInfo) = + ## Used for library-only packages. Copies all the required files, skips + ## files specified in the .babel file. for kind, file in walkDir(currentDir): if kind == pcDir: var skip = false @@ -168,7 +137,7 @@ proc copyFilesRec(origDir, currentDir, dest: string, pkgInfo: TPackageInfo) = if file.splitFile().name[0] == '.': skip = true for ignoreFile in pkgInfo.skipFiles: if ignoreFile.endswith("babel"): - quit(ignoreFile & " must be installed.") + raise newException(EBabel, ignoreFile & " must be installed.") if samePaths(file, origDir / ignoreFile): skip = true break @@ -176,59 +145,30 @@ proc copyFilesRec(origDir, currentDir, dest: string, pkgInfo: TPackageInfo) = if not skip: copyFileD(file, changeRoot(origDir, dest, file)) -proc getPkgInfo(dir: string): TPackageInfo = - let babelFile = findBabelFile(dir) - if babelFile == "": - quit("Specified directory does not contain a .babel file.", QuitFailure) - result = readPackageInfo(babelFile) - -# TODO: Move to packageinfo.nim - -proc getInstalledPkgs(): seq[tuple[path: string, info: TPackageInfo]] = - ## Gets a list of installed packages - result = @[] - for kind, path in walkDir(libsDir): - if kind == pcDir: - let babelFile = findBabelFile(path) - if babelFile != "": - result.add((path, readPackageInfo(babelFile))) - else: - # TODO: Abstract logging. - echo("WARNING: No .babel file found for ", path) - -proc findPkg(pkglist: seq[tuple[path: string, info: TPackageInfo]], - dep: tuple[name: string, ver: PVersionRange], - r: var tuple[path: string, info: TPackageInfo]): bool = - for pkg in pkglist: - if pkg.info.name != dep.name: continue - if withinRange(newVersion(pkg.info.version), dep.ver): - if not result or newVersion(r.info.version) < newVersion(pkg.info.version): - r = pkg - result = true - proc install(packages: seq[String], verRange: PVersionRange): string {.discardable.} proc processDeps(pkginfo: TPackageInfo): seq[string] = ## Verifies and installs dependencies. ## ## Returns the list of paths to pass to the compiler during build phase. result = @[] - let pkglist = getInstalledPkgs() + let pkglist = getInstalledPkgs(libsDir) for dep in pkginfo.requires: if dep.name == "nimrod": if not withinRange(nimVer, dep.ver): quit("Unsatisfied dependency: " & dep.name & " (" & $dep.ver & ")") else: echo("Looking for ", dep.name, " (", $dep.ver, ")...") - var pkg: tuple[path: string, info: TPackageInfo] + var pkg: TPackageInfo if not findPkg(pkglist, dep, pkg): + echo("None found, installing...") let dest = install(@[dep.name], dep.ver) if dest != "": # only add if not a binary package result.add(dest) else: echo("Dependency already satisfied.") - if pkg.info.bin.len == 0: - result.add(pkg.path) + if pkg.bin.len == 0: + result.add(pkg.mypath.splitFile.dir) proc buildFromDir(dir: string, paths: seq[string]) = ## Builds a package which resides in ``dir`` @@ -237,7 +177,6 @@ proc buildFromDir(dir: string, paths: seq[string]) = for path in paths: args.add("--path:" & path & " ") for bin in pkgInfo.bin: echo("Building ", pkginfo.name, "/", bin, "...") - echo(args) doCmd("nimrod c -d:release " & args & dir / bin) proc installFromDir(dir: string, latest: bool): string = @@ -273,11 +212,6 @@ proc installFromDir(dir: string, latest: bool): string = echo(pkgInfo.name & " installed successfully.") result = pkgDestDir -proc getDVCSTag(pkg: TPackage): string = - result = pkg.dvcsTag - if result == "": - result = pkg.version - proc getTagsList(dir: string): seq[string] = let output = execProcess("cd \"" & dir & "\" && git tag") if output.len > 0: @@ -294,6 +228,42 @@ proc getVersionList(dir: string): TTable[TVersion, string] = # 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) + echo("Downloading ", pkg.name, " into ", downloadDir, "...") + case pkg.downloadMethod + of "git": + echo("Executing git...") + if existsDir(downloadDir / ".git"): + doCmd("cd " & downloadDir & " && git pull") + else: + removeDir(downloadDir) + doCmd("git clone --depth 1 " & pkg.url & " " & downloadDir) + + # TODO: Determine if version is a commit hash, if it is. Move the + # git repo to ``babelDir/libs``, 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 != "": + doCmd("cd \"" & downloadDir & "\" && 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 = if packages == @[]: result = installFromDir(getCurrentDir(), false) @@ -303,37 +273,7 @@ proc install(packages: seq[String], verRange: PVersionRange): string = for p in packages: var pkg: TPackage if getPackage(p, babelDir / "packages.json", pkg): - let downloadDir = (getTempDir() / "babel" / pkg.name) - #let dvcsTag = getDVCSTag(pkg) - case pkg.downloadMethod - of "git": - echo("Executing git...") - if existsDir(downloadDir / ".git"): - doCmd("cd " & downloadDir & " && git pull") - else: - removeDir(downloadDir) - doCmd("git clone --depth 1 " & pkg.url & " " & downloadDir) - - # TODO: Determine if version is a commit hash, if it is. Move the - # git repo to ``babelDir/libs``, then babel can simply checkout - # the correct hash instead of constantly cloning and copying. - let versions = getVersionList(downloadDir) - if versions.len > 0: - let latest = findLatest(verRange, versions) - - if latest.tag != "": - doCmd("cd \"" & downloadDir & "\" && 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: quit("Unknown download method: " & pkg.downloadMethod, QuitFailure) - + let downloadDir = downloadPkg(pkg, verRange) result = installFromDir(downloadDir, false) else: raise newException(EBabel, "Package not found.") @@ -359,7 +299,7 @@ proc search(action: TAction) = notFound = false break if notFound: - # Search by tag. + # Search by name. for pkg in pkgList: if pkg.name in action.search: echoPackage(pkg) diff --git a/common.nim b/common.nim new file mode 100644 index 0000000..15e2a02 --- /dev/null +++ b/common.nim @@ -0,0 +1,4 @@ +# Copyright (C) Dominik Picheta. All rights reserved. +# BSD License. Look at license.txt for more info. +type + EBabel* = object of EBase \ No newline at end of file diff --git a/packageinfo.nim b/packageinfo.nim index 2eecbee..f8018c9 100644 --- a/packageinfo.nim +++ b/packageinfo.nim @@ -1,9 +1,10 @@ # Copyright (C) Dominik Picheta. All rights reserved. # BSD License. Look at license.txt for more info. -import parsecfg, json, streams, strutils, parseutils -import version +import parsecfg, json, streams, strutils, parseutils, os +import version, common type TPackageInfo* = object + mypath*: string ## The path of this .babel file name*: string version*: string author*: string @@ -25,6 +26,7 @@ type description*: string proc initPackageInfo(): TPackageInfo = + result.mypath = "" result.name = "" result.version = "" result.author = "" @@ -61,6 +63,7 @@ proc parseRequires(req: string): tuple[name: string, ver: PVersionRange] = proc readPackageInfo*(path: string): TPackageInfo = result = initPackageInfo() + result.mypath = path var fs = newFileStream(path, fmRead) if fs != nil: var p: TCfgParser @@ -157,6 +160,49 @@ proc getPackageList*(packagesPath: string): seq[TPackage] = pkg.description = p.requiredField("description") result.add(pkg) +proc findBabelFile*(dir: string): string = + result = "" + for kind, path in walkDir(dir): + if kind == pcFile and path.splitFile.ext == ".babel": + if result != "": + raise newException(EBabel, "Only one .babel file should be present in " & dir) + result = path + +proc getPkgInfo*(dir: string): TPackageInfo = + ## Find the .babel file in ``dir`` and parses it, returning a TPackageInfo. + let babelFile = findBabelFile(dir) + if babelFile == "": + quit("Specified directory does not contain a .babel file.", QuitFailure) + result = readPackageInfo(babelFile) + +proc getInstalledPkgs*(libsDir: string): seq[TPackageInfo] = + ## Gets a list of installed packages. + ## + ## ``libsDir`` is in most cases: ~/.babel/libs/ + result = @[] + for kind, path in walkDir(libsDir): + if kind == pcDir: + let babelFile = findBabelFile(path) + if babelFile != "": + result.add(readPackageInfo(babelFile)) + else: + # TODO: Abstract logging. + echo("WARNING: No .babel file found for ", path) + +proc findPkg*(pkglist: seq[TPackageInfo], + dep: tuple[name: string, ver: PVersionRange], + r: var TPackageInfo): bool = + ## Searches ``pkglist`` for a package of which version is withing 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 + ## version number) + for pkg in pkglist: + if pkg.name != dep.name: continue + if withinRange(newVersion(pkg.version), dep.ver): + if not result or newVersion(r.version) < newVersion(pkg.version): + r = pkg + result = true + proc echoPackage*(pkg: TPackage) = echo(pkg.name & ":") if pkg.version != "": diff --git a/tools.nim b/tools.nim new file mode 100644 index 0000000..2bb7f14 --- /dev/null +++ b/tools.nim @@ -0,0 +1,30 @@ +# Copyright (C) Dominik Picheta. All rights reserved. +# BSD License. Look at license.txt for more info. +# +# Various miscellaneous utility functions reside here. +import osproc, pegs, strutils, os +import version + +proc getNimrodVersion*: TVersion = + let vOutput = execProcess("nimrod -v") + var matches: array[0..MaxSubpatterns, string] + if vOutput.find(peg"'Version'\s{(\d\.)+\d}", matches) == -1: + quit("Couldn't find Nimrod version.", QuitFailure) + newVersion(matches[0]) + +proc samePaths*(p1, p2: string): bool = + ## Normalizes path (by adding a trailing slash) and compares. + let cp1 = if not p1.endsWith("/"): p1 & "/" else: p1 + let cp2 = if not p2.endsWith("/"): p2 & "/" else: p2 + return cmpPaths(cp1, cp2) == 0 + +proc changeRoot*(origRoot, newRoot, path: string): string = + ## origRoot: /home/dom/ + ## newRoot: /home/test/ + ## path: /home/dom/bar/blah/2/foo.txt + ## Return value -> /home/test/bar/blah/2/foo.txt + if path.startsWith(origRoot): + return newRoot / path[origRoot.len .. -1] + else: + raise newException(EInvalidValue, + "Cannot change root of path: Path does not begin with original root.") \ No newline at end of file