From 383e0f2a4c003cdb03d7483e5618203aa2510274 Mon Sep 17 00:00:00 2001 From: Louis Berube Date: Mon, 29 Dec 2014 15:38:16 -0500 Subject: [PATCH 1/3] Updated source to eliminate stuff deprecated by nim 0.10.2 --- src/nimble.nim | 30 ++++++++++++++++++------------ src/nimblepkg/config.nim | 4 ++-- src/nimblepkg/download.nim | 10 +++++----- src/nimblepkg/nimbletypes.nim | 7 +++++++ src/nimblepkg/packageinfo.nim | 15 ++++++++------- src/nimblepkg/tools.nim | 29 +++++++++++++---------------- src/nimblepkg/version.nim | 6 +++--- 7 files changed, 56 insertions(+), 45 deletions(-) create mode 100644 src/nimblepkg/nimbletypes.nim diff --git a/src/nimble.nim b/src/nimble.nim index 6bb23c81..3a5737aa 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -7,7 +7,8 @@ import httpclient, parseopt, os, strutils, osproc, pegs, tables, parseutils, from sequtils import toSeq import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools, - nimblepkg/download, nimblepkg/config, nimblepkg/compat + nimblepkg/download, nimblepkg/config, nimblepkg/compat, + nimblepkg/nimbletypes when not defined(windows): from posix import getpid @@ -18,7 +19,7 @@ type queryVersions: bool action: TAction config: TConfig - nimbleData: PJsonNode ## Nimbledata.json + nimbleData: JsonNode ## Nimbledata.json TActionType = enum ActionNil, ActionUpdate, ActionInit, ActionInstall, ActionSearch, @@ -137,6 +138,8 @@ proc parseCmdLine(): TOptions = result.action.typ = ActionInstall of "path": result.action.typ = ActionPath + else: + discard result.action.packages = @[] of "build": result.action.typ = ActionBuild @@ -177,6 +180,8 @@ proc parseCmdLine(): TOptions = result.action.projName = key of ActionList, ActionBuild: writeHelp() + else: + discard of cmdLongOption, cmdShortOption: case key of "help", "h": writeHelp() @@ -184,6 +189,7 @@ proc parseCmdLine(): TOptions = of "accept", "y": result.forcePrompts = ForcePromptYes of "reject", "n": result.forcePrompts = ForcePromptNo of "ver": result.queryVersions = true + else: discard of cmdEnd: assert(false) # cannot happen if result.action.typ == ActionNil: writeHelp() @@ -266,7 +272,7 @@ proc copyWithExt(origDir, currentDir, dest: string, result.add copyFileD(path, changeRoot(origDir, dest, path)) proc copyFilesRec(origDir, currentDir, dest: string, - options: TOptions, pkgInfo: TPackageInfo): TSet[string] = + options: TOptions, pkgInfo: TPackageInfo): HashSet[string] = ## Copies all the required files, skips files specified in the .nimble file ## (TPackageInfo). ## Returns a list of filepaths to files which have been installed. @@ -323,7 +329,7 @@ proc saveNimbleData(options: TOptions) = proc addRevDep(options: TOptions, dep: tuple[name, version: string], pkg: TPackageInfo) = - let depNameVer = dep.name & '-' & dep.version + # let depNameVer = dep.name & '-' & dep.version if not options.nimbleData["reverseDeps"].hasKey(dep.name): options.nimbleData["reverseDeps"][dep.name] = newJObject() if not options.nimbleData["reverseDeps"][dep.name].hasKey(dep.version): @@ -336,7 +342,7 @@ proc addRevDep(options: TOptions, dep: tuple[name, version: string], proc removeRevDep(options: TOptions, pkg: TPackageInfo) = ## Removes ``pkg`` from the reverse dependencies of every package. proc remove(options: TOptions, pkg: TPackageInfo, depTup: TPkgTuple, - thisDep: PJsonNode) = + thisDep: JsonNode) = for ver, val in thisDep: if ver.newVersion in depTup.ver: var newVal = newJArray() @@ -403,7 +409,7 @@ proc processDeps(pkginfo: TPackageInfo, options: TOptions): seq[string] = # Check if two packages of the same name (but different version) are listed # in the path. - var pkgsInPath: PStringTable = newStringTable(modeCaseSensitive) + var pkgsInPath: StringTableRef = newStringTable(modeCaseSensitive) for p in result: let (name, version) = getNameVersion(p) if pkgsInPath.hasKey(name) and pkgsInPath[name] != version: @@ -430,7 +436,7 @@ proc buildFromDir(pkgInfo: TPackageInfo, paths: seq[string]) = doCmd(getNimBin() & " $# -d:release --noBabelPath $# \"$#\"" % [pkgInfo.backend, args, realDir / bin.changeFileExt("nim")]) -proc saveNimbleMeta(pkgDestDir, url: string, filesInstalled: TSet[string]) = +proc saveNimbleMeta(pkgDestDir, url: string, filesInstalled: HashSet[string]) = var nimblemeta = %{"url": %url} nimblemeta["files"] = newJArray() for file in filesInstalled: @@ -442,7 +448,7 @@ proc removePkgDir(dir: string, options: TOptions) = try: var nimblemeta = parseFile(dir / "nimblemeta.json") if not nimblemeta.hasKey("files"): - raise newException(EJsonParsingError, + raise newException(JsonParsingError, "Meta data does not contain required info.") for file in nimblemeta["files"]: removeFile(dir / file.str) @@ -455,7 +461,7 @@ proc removePkgDir(dir: string, options: TOptions) = else: echo("WARNING: Cannot completely remove " & dir & ". Files not installed by nimble are present.") - except EOS, EJsonParsingError: + except OSError, JsonParsingError: echo("Error: Unable to read nimblemeta.json: ", getCurrentExceptionMsg()) if not options.prompt("Would you like to COMPLETELY remove ALL files " & "in " & dir & "?"): @@ -500,7 +506,7 @@ proc installFromDir(dir: string, latest: bool, options: TOptions, removeFile(binDir / bin) ## Will contain a list of files which have been installed. - var filesInstalled: TSet[string] + var filesInstalled: HashSet[string] createDir(pkgDestDir) if pkgInfo.bin.len > 0: @@ -710,7 +716,7 @@ proc init(options: TOptions) = echo("Initializing new Nimble project!") var pkgName, fName: string = "" - outFile: TFile + outFile: File if (options.action.projName != ""): pkgName = options.action.projName @@ -743,7 +749,7 @@ proc init(options: TOptions) = else: raise newException(ENimble, "Unable to open file " & fName & - " for writing: " & osErrorMsg()) + " for writing: " & osErrorMsg(osLastError())) proc uninstall(options: TOptions) = var pkgsToDelete: seq[TPackageInfo] = @[] diff --git a/src/nimblepkg/config.nim b/src/nimblepkg/config.nim index d8d68f17..9458c550 100644 --- a/src/nimblepkg/config.nim +++ b/src/nimblepkg/config.nim @@ -2,7 +2,7 @@ # BSD License. Look at license.txt for more info. import parsecfg, streams, strutils, os -import tools, version +import tools, version, nimbletypes type TConfig* = object @@ -32,7 +32,7 @@ proc parseConfig*(): TConfig = if f != nil: echo("Reading from config file at ", confFile) - var p: TCfgParser + var p: CfgParser open(p, f, confFile) while true: var e = next(p) diff --git a/src/nimblepkg/download.nim b/src/nimblepkg/download.nim index e69d0d1e..73f1fe41 100644 --- a/src/nimblepkg/download.nim +++ b/src/nimblepkg/download.nim @@ -3,7 +3,7 @@ import parseutils, os, osproc, strutils, tables, pegs -import packageinfo, version, tools +import packageinfo, version, tools, nimbletypes type TDownloadMethod* {.pure.} = enum @@ -98,7 +98,7 @@ proc getTagsListRemote*(url: string, meth: TDownloadMethod): seq[string] = of TDownloadMethod.Git: var (output, exitCode) = doCmdEx("git ls-remote --tags " & url) if exitCode != QuitSuccess: - raise newException(EOS, "Unable to query remote tags for " & url & + raise newException(OSError, "Unable to query remote tags for " & url & ". Git returned: " & output) for i in output.splitLines(): if i == "": continue @@ -108,9 +108,9 @@ proc getTagsListRemote*(url: string, meth: TDownloadMethod): seq[string] = of TDownloadMethod.Hg: # http://stackoverflow.com/questions/2039150/show-tags-for-remote-hg-repository - raise newException(EInvalidValue, "Hg doesn't support remote tag querying.") + raise newException(ValueError, "Hg doesn't support remote tag querying.") -proc getVersionList*(tags: seq[string]): TTable[TVersion, string] = +proc getVersionList*(tags: seq[string]): Table[TVersion, string] = # Returns: TTable of version -> git tag name result = initTable[TVersion, string]() for tag in tags: @@ -225,7 +225,7 @@ proc echoPackageVersions*(pkg: TPackage) = echo(" versions: " & vstr) else: echo(" versions: (No versions tagged in the remote repository)") - except EOS: + except OSError: echo(getCurrentExceptionMsg()) of TDownloadMethod.Hg: echo(" versions: (Remote tag retrieval not supported by " & pkg.downloadMethod & ")") diff --git a/src/nimblepkg/nimbletypes.nim b/src/nimblepkg/nimbletypes.nim new file mode 100644 index 00000000..544c2944 --- /dev/null +++ b/src/nimblepkg/nimbletypes.nim @@ -0,0 +1,7 @@ +# BSD License. Look at license.txt for more info. +# +# Various miscellaneous common types reside here, to avoid problems with +# recursive imports + +type + ENimble* = object of Exception diff --git a/src/nimblepkg/packageinfo.nim b/src/nimblepkg/packageinfo.nim index 8b3056e6..c0c8085f 100644 --- a/src/nimblepkg/packageinfo.nim +++ b/src/nimblepkg/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, tools +import version, tools, nimbletypes type ## Tuple containing package name and version range. TPkgTuple* = tuple[name: string, ver: PVersionRange] @@ -120,7 +120,7 @@ proc readPackageInfo*(path: string): TPackageInfo = result.mypath = path var fs = newFileStream(path, fmRead) if fs != nil: - var p: TCfgParser + var p: CfgParser open(p, fs, path) var currentSection = "" while true: @@ -159,6 +159,7 @@ proc readPackageInfo*(path: string): TPackageInfo = result.backend = ev.value.toLower() case result.backend.normalize of "javascript": result.backend = "js" + else: discard else: raise newException(ENimble, "Invalid field: " & ev.key) of "deps", "dependencies": @@ -174,23 +175,23 @@ proc readPackageInfo*(path: string): TPackageInfo = raise newException(ENimble, "Error parsing .nimble file: " & ev.msg) close(p) else: - raise newException(EInvalidValue, "Cannot open package info: " & path) + raise newException(ValueError, "Cannot open package info: " & path) validatePackageInfo(result, path) -proc optionalField(obj: PJsonNode, name: string, default = ""): string = +proc optionalField(obj: JsonNode, name: string, default = ""): string = ## Queries ``obj`` for the optional ``name`` string. ## ## Returns the value of ``name`` if it is a valid string, or aborts execution ## if the field exists but is not of string type. If ``name`` is not present, ## returns ``default``. - if existsKey(obj, name): + if hasKey(obj, name): if obj[name].kind == JString: return obj[name].str else: raise newException(ENimble, "Corrupted packages.json file. " & name & " field is of unexpected type.") else: return default -proc requiredField(obj: PJsonNode, name: string): string = +proc requiredField(obj: JsonNode, name: string): string = ## Queries ``obj`` for the required ``name`` string. ## ## Aborts execution if the field does not exist or is of invalid json type. @@ -199,7 +200,7 @@ proc requiredField(obj: PJsonNode, name: string): string = raise newException(ENimble, "Package in packages.json file does not contain a " & name & " field.") -proc fromJson(obj: PJSonNode): TPackage = +proc fromJson(obj: JSonNode): TPackage = ## Constructs a TPackage object from a JSON node. ## ## Aborts execution if the JSON node doesn't contain the required fields. diff --git a/src/nimblepkg/tools.nim b/src/nimblepkg/tools.nim index 295c0bb6..a7adfb08 100644 --- a/src/nimblepkg/tools.nim +++ b/src/nimblepkg/tools.nim @@ -2,11 +2,8 @@ # BSD License. Look at license.txt for more info. # # Various miscellaneous utility functions reside here. -import osproc, pegs, strutils, os, parseurl, sets, json -import version, packageinfo - -type - ENimble* = object of EBase +import osproc, pegs, strutils, os, uri, sets, json +import version, packageinfo, nimbletypes proc doCmd*(cmd: string) = let bin = cmd.split(' ')[0] @@ -61,7 +58,7 @@ proc changeRoot*(origRoot, newRoot, path: string): string = if path.startsWith(origRoot): return newRoot / path[origRoot.len .. -1] else: - raise newException(EInvalidValue, + raise newException(ValueError, "Cannot change root of path: Path does not begin with original root.") proc copyFileD*(fro, to: string): string = @@ -78,37 +75,37 @@ proc copyDirD*(fro, to: string): seq[string] = createDir(changeRoot(fro, to, path.splitFile.dir)) result.add copyFileD(path, changeRoot(fro, to, path)) -proc getDownloadDirName*(url: string, verRange: PVersionRange): string = - ## Creates a directory name based on the specified ``url`` +proc getDownloadDirName*(uri: string, verRange: PVersionRange): string = + ## Creates a directory name based on the specified ``uri`` (url) result = "" - let purl = parseUrl(url) - for i in purl.hostname: + let puri = parseUri(uri) + for i in puri.hostname: case i of strutils.Letters, strutils.Digits: result.add i - else: nil + else: discard result.add "_" - for i in purl.path: + for i in puri.path: case i of strutils.Letters, strutils.Digits: result.add i - else: nil + else: discard let verSimple = getSimpleString(verRange) if verSimple != "": result.add "_" result.add verSimple -proc incl*(s: var TSet[string], v: seq[string] | TSet[string]) = +proc incl*(s: var HashSet[string], v: seq[string] | HashSet[string]) = for i in v: s.incl i -proc contains*(j: PJsonNode, elem: PJsonNode): bool = +proc contains*(j: JsonNode, elem: JsonNode): bool = for i in j: if i == elem: return true -proc contains*(j: PJsonNode, elem: tuple[key: string, val: PJsonNode]): bool = +proc contains*(j: JsonNode, elem: tuple[key: string, val: JsonNode]): bool = for key, val in pairs(j): if key == elem.key and val == elem.val: return true diff --git a/src/nimblepkg/version.nim b/src/nimblepkg/version.nim index 8cc4cd0d..46190163 100644 --- a/src/nimblepkg/version.nim +++ b/src/nimblepkg/version.nim @@ -29,7 +29,7 @@ type of verAny: nil - EParseVersion* = object of EInvalidValue + EParseVersion* = object of ValueError proc newVersion*(ver: string): TVersion = return TVersion(ver) proc newSpecial*(spe: string): TSpecial = return TSpecial(spe) @@ -55,7 +55,7 @@ proc `<`*(ver: TVersion, ver2: TVersion): bool = if sVerI < sVerI2: return true elif sVerI == sVerI2: - nil + discard else: return false @@ -229,7 +229,7 @@ proc newVREq*(ver: string): PVersionRange = result.kind = verEq result.ver = newVersion(ver) -proc findLatest*(verRange: PVersionRange, versions: TTable[TVersion, string]): tuple[ver: TVersion, tag: string] = +proc findLatest*(verRange: PVersionRange, versions: Table[TVersion, string]): tuple[ver: TVersion, tag: string] = result = (newVersion(""), "") for ver, tag in versions: if not withinRange(ver, verRange): continue From 521ee1f6964c3557100c11c3ca3735c6bc3dd070 Mon Sep 17 00:00:00 2001 From: Louis Berube Date: Wed, 31 Dec 2014 10:13:23 -0500 Subject: [PATCH 2/3] - Converted types from the old T/P convention - Deleted compat.nim --- src/nimble.nim | 252 ++++++++++++++++++---------------- src/nimblepkg/compat.nim | 29 ---- src/nimblepkg/config.nim | 10 +- src/nimblepkg/download.nim | 92 +++++++------ src/nimblepkg/nimbletypes.nim | 2 +- src/nimblepkg/packageinfo.nim | 107 ++++++++------- src/nimblepkg/tools.nim | 11 +- src/nimblepkg/version.nim | 95 +++++++------ tests/tester.nim | 7 +- 9 files changed, 303 insertions(+), 302 deletions(-) delete mode 100644 src/nimblepkg/compat.nim diff --git a/src/nimble.nim b/src/nimble.nim index 3a5737aa..bc934561 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -7,40 +7,39 @@ import httpclient, parseopt, os, strutils, osproc, pegs, tables, parseutils, from sequtils import toSeq import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools, - nimblepkg/download, nimblepkg/config, nimblepkg/compat, - nimblepkg/nimbletypes + nimblepkg/download, nimblepkg/config, nimblepkg/nimbletypes when not defined(windows): from posix import getpid type - TOptions = object - forcePrompts: TForcePrompt + Options = object + forcePrompts: ForcePrompt queryVersions: bool - action: TAction - config: TConfig + action: Action + config: Config nimbleData: JsonNode ## Nimbledata.json - TActionType = enum - ActionNil, ActionUpdate, ActionInit, ActionInstall, ActionSearch, - ActionList, ActionBuild, ActionPath, ActionUninstall + ActionType = enum + actionNil, actionUpdate, actionInit, actionInstall, actionSearch, + actionList, actionBuild, actionPath, actionUninstall - TAction = object - case typ: TActionType - of ActionNil, ActionList, ActionBuild: nil - of ActionUpdate: + Action = object + case typ: ActionType + of actionNil, actionList, actionBuild: nil + of actionUpdate: optionalURL: string # Overrides default package list. - of ActionInstall, ActionPath, ActionUninstall: + of actionInstall, actionPath, actionUninstall: optionalName: seq[string] # \ # When this is @[], installs package from current dir. - packages: seq[TPkgTuple] # Optional only for ActionInstall. - of ActionSearch: + packages: seq[PkgTuple] # Optional only for actionInstall. + of actionSearch: search: seq[string] # Search string. - of ActionInit: + of actionInit: projName: string - TForcePrompt = enum - DontForcePrompt, ForcePromptYes, ForcePromptNo + ForcePrompt = enum + dontForcePrompt, forcePromptYes, forcePromptNo const help = """ @@ -71,38 +70,40 @@ For more information read the Github readme: https://github.com/nim-lang/nimble#readme """ nimbleVersion = "0.6.0" - defaultPackageURL = "https://github.com/nim-lang/packages/raw/master/packages.json" + defaultPackageURL = + "https://github.com/nim-lang/packages/raw/master/packages.json" proc writeHelp() = echo(help) quit(QuitSuccess) proc writeVersion() = - echo("nimble v$# compiled at $# $#" % [nimbleVersion, CompileDate, CompileTime]) + echo("nimble v$# compiled at $# $#" % + [nimbleVersion, CompileDate, CompileTime]) quit(QuitSuccess) -proc getNimbleDir(options: TOptions): string = +proc getNimbleDir(options: Options): string = options.config.nimbleDir -proc getPkgsDir(options: TOptions): string = +proc getPkgsDir(options: Options): string = options.config.nimbleDir / "pkgs" -proc getBinDir(options: TOptions): string = +proc getBinDir(options: Options): string = options.config.nimbleDir / "bin" -proc prompt(options: TOptions, question: string): bool = +proc prompt(options: Options, question: string): bool = ## Asks an interactive question and returns the result. ## ## The proc will return immediately without asking the user if the global - ## forcePrompts has a value different than DontForcePrompt. + ## forcePrompts has a value different than dontForcePrompt. case options.forcePrompts - of ForcePromptYes: + of forcePromptYes: echo(question & " -> [forced yes]") return true - of ForcePromptNo: + of forcePromptNo: echo(question & " -> [forced no]") return false - of DontForcePrompt: + of dontForcePrompt: echo(question & " [y/N]") let yn = stdin.readLine() case yn.normalize @@ -113,72 +114,74 @@ proc prompt(options: TOptions, question: string): bool = else: return false -proc renameBabelToNimble(options: TOptions) {.deprecated.} = +proc renameBabelToNimble(options: Options) {.deprecated.} = let babelDir = getHomeDir() / ".babel" let nimbleDir = getHomeDir() / ".nimble" if dirExists(babelDir): - if options.prompt("Found deprecated babel package directory, would you like to rename it to nimble?"): + if options.prompt("Found deprecated babel package directory, would you " & + "like to rename it to nimble?"): copyDir(babelDir, nimbleDir) removeDir(babelDir) copyFile(babelDir / "babeldata.json", nimbleDir / "nimbledata.json") removeFile(nimbleDir / "babeldata.json") -proc parseCmdLine(): TOptions = - result.action.typ = ActionNil +proc parseCmdLine(): Options = + result.action.typ = actionNil result.config = parseConfig() for kind, key, val in getOpt(): case kind of cmdArgument: - if result.action.typ == ActionNil: + if result.action.typ == actionNil: case key of "install", "path": case key of "install": - result.action.typ = ActionInstall + result.action.typ = actionInstall of "path": - result.action.typ = ActionPath + result.action.typ = actionPath else: discard result.action.packages = @[] of "build": - result.action.typ = ActionBuild + result.action.typ = actionBuild of "init": - result.action.typ = ActionInit + result.action.typ = actionInit result.action.projName = "" of "update": - result.action.typ = ActionUpdate + result.action.typ = actionUpdate result.action.optionalURL = "" of "search": - result.action.typ = ActionSearch + result.action.typ = actionSearch result.action.search = @[] of "list": - result.action.typ = ActionList + result.action.typ = actionList of "uninstall", "remove", "delete", "del", "rm": - result.action.typ = ActionUninstall + result.action.typ = actionUninstall result.action.packages = @[] else: writeHelp() else: case result.action.typ - of ActionNil: + of actionNil: assert false - of ActionInstall, ActionPath, ActionUninstall: + of actionInstall, actionPath, actionUninstall: # Parse pkg@verRange if '@' in key: let i = find(key, '@') let pkgTup = (key[0 .. i-1], key[i+1 .. -1].parseVersionRange()) result.action.packages.add(pkgTup) else: - result.action.packages.add((key, PVersionRange(kind: verAny))) - of ActionUpdate: + result.action.packages.add((key, VersionRangeRef(kind: verAny))) + of actionUpdate: result.action.optionalURL = key - of ActionSearch: + of actionSearch: result.action.search.add(key) - of ActionInit: + of actionInit: if result.action.projName != "": - raise newException(ENimble, "Can only initialize one package at a time.") + raise newException(NimbleError, + "Can only initialize one package at a time.") result.action.projName = key - of ActionList, ActionBuild: + of actionList, actionBuild: writeHelp() else: discard @@ -186,12 +189,12 @@ proc parseCmdLine(): TOptions = case key of "help", "h": writeHelp() of "version", "v": writeVersion() - of "accept", "y": result.forcePrompts = ForcePromptYes - of "reject", "n": result.forcePrompts = ForcePromptNo + of "accept", "y": result.forcePrompts = forcePromptYes + of "reject", "n": result.forcePrompts = forcePromptNo of "ver": result.queryVersions = true else: discard of cmdEnd: assert(false) # cannot happen - if result.action.typ == ActionNil: + if result.action.typ == actionNil: writeHelp() # TODO: Remove this after a couple of versions. @@ -205,18 +208,18 @@ proc parseCmdLine(): TOptions = try: result.nimbleData = parseFile(nimbledataFilename) except: - raise newException(ENimble, "Couldn't parse nimbledata.json file " & + raise newException(NimbleError, "Couldn't parse nimbledata.json file " & "located at " & nimbledataFilename) else: result.nimbleData = %{"reverseDeps": newJObject()} -proc update(options: TOptions) = +proc update(options: Options) = ## Downloads the package list from the specified URL. ## ## If the download is successful, the global didUpdatePackages is set to ## true. Otherwise an exception is raised on error. let url = - if options.action.typ == ActionUpdate and options.action.optionalURL != "": + if options.action.typ == actionUpdate and options.action.optionalURL != "": options.action.optionalURL else: defaultPackageURL @@ -224,14 +227,14 @@ proc update(options: TOptions) = downloadFile(url, options.getNimbleDir() / "packages.json") echo("Done.") -proc checkInstallFile(pkgInfo: TPackageInfo, +proc checkInstallFile(pkgInfo: PackageInfo, origDir, file: string): bool = ## Checks whether ``file`` should be installed. ## ``True`` means file should be skipped. for ignoreFile in pkgInfo.skipFiles: if ignoreFile.endswith("nimble"): - raise newException(ENimble, ignoreFile & " must be installed.") + raise newException(NimbleError, ignoreFile & " must be installed.") if samePaths(file, origDir / ignoreFile): result = true break @@ -243,7 +246,7 @@ proc checkInstallFile(pkgInfo: TPackageInfo, if file.splitFile().name[0] == '.': result = true -proc checkInstallDir(pkgInfo: TPackageInfo, +proc checkInstallDir(pkgInfo: PackageInfo, origDir, dir: string): bool = ## Determines whether ``dir`` should be installed. ## ``True`` means dir should be skipped. @@ -258,7 +261,7 @@ proc checkInstallDir(pkgInfo: TPackageInfo, if thisDir == "nimcache": result = true proc copyWithExt(origDir, currentDir, dest: string, - pkgInfo: TPackageInfo): seq[string] = + pkgInfo: PackageInfo): seq[string] = ## Returns the filenames of the files that have been copied ## (their destination). result = @[] @@ -272,9 +275,9 @@ proc copyWithExt(origDir, currentDir, dest: string, result.add copyFileD(path, changeRoot(origDir, dest, path)) proc copyFilesRec(origDir, currentDir, dest: string, - options: TOptions, pkgInfo: TPackageInfo): HashSet[string] = + options: Options, pkgInfo: PackageInfo): HashSet[string] = ## Copies all the required files, skips files specified in the .nimble file - ## (TPackageInfo). + ## (PackageInfo). ## Returns a list of filepaths to files which have been installed. result = initSet[string]() let whitelistMode = @@ -323,12 +326,13 @@ proc copyFilesRec(origDir, currentDir, dest: string, result.incl copyFileD(pkgInfo.mypath, changeRoot(pkgInfo.mypath.splitFile.dir, dest, pkgInfo.mypath)) -proc saveNimbleData(options: TOptions) = +proc saveNimbleData(options: Options) = # TODO: This file should probably be locked. - writeFile(options.getNimbleDir() / "nimbledata.json", pretty(options.nimbleData)) + writeFile(options.getNimbleDir() / "nimbledata.json", + pretty(options.nimbleData)) -proc addRevDep(options: TOptions, dep: tuple[name, version: string], - pkg: TPackageInfo) = +proc addRevDep(options: Options, dep: tuple[name, version: string], + pkg: PackageInfo) = # let depNameVer = dep.name & '-' & dep.version if not options.nimbleData["reverseDeps"].hasKey(dep.name): options.nimbleData["reverseDeps"][dep.name] = newJObject() @@ -339,9 +343,9 @@ proc addRevDep(options: TOptions, dep: tuple[name, version: string], if revDep notin thisDep: thisDep.add revDep -proc removeRevDep(options: TOptions, pkg: TPackageInfo) = +proc removeRevDep(options: Options, pkg: PackageInfo) = ## Removes ``pkg`` from the reverse dependencies of every package. - proc remove(options: TOptions, pkg: TPackageInfo, depTup: TPkgTuple, + proc remove(options: Options, pkg: PackageInfo, depTup: PkgTuple, thisDep: JsonNode) = for ver, val in thisDep: if ver.newVersion in depTup.ver: @@ -376,10 +380,10 @@ proc removeRevDep(options: TOptions, pkg: TPackageInfo) = saveNimbleData(options) -proc install(packages: seq[TPkgTuple], - options: TOptions, - doPrompt = true): tuple[paths: seq[string], pkg: TPackageInfo] -proc processDeps(pkginfo: TPackageInfo, options: TOptions): seq[string] = +proc install(packages: seq[PkgTuple], + options: Options, + doPrompt = true): tuple[paths: seq[string], pkg: PackageInfo] +proc processDeps(pkginfo: PackageInfo, options: Options): seq[string] = ## Verifies and installs dependencies. ## ## Returns the list of paths to pass to the compiler during build phase. @@ -393,7 +397,7 @@ proc processDeps(pkginfo: TPackageInfo, options: TOptions): seq[string] = quit("Unsatisfied dependency: " & dep.name & " (" & $dep.ver & ")") else: echo("Looking for ", dep.name, " (", $dep.ver, ")...") - var pkg: TPackageInfo + var pkg: PackageInfo if not findPkg(pkglist, dep, pkg): echo("None found, installing...") let (paths, installedPkg) = install(@[(dep.name, dep.ver)], options) @@ -413,7 +417,7 @@ proc processDeps(pkginfo: TPackageInfo, options: TOptions): seq[string] = for p in result: let (name, version) = getNameVersion(p) if pkgsInPath.hasKey(name) and pkgsInPath[name] != version: - raise newException(ENimble, + raise newException(NimbleError, "Cannot satisfy the dependency on $1 $2 and $1 $3" % [name, version, pkgsInPath[name]]) pkgsInPath[name] = version @@ -425,7 +429,7 @@ proc processDeps(pkginfo: TPackageInfo, options: TOptions): seq[string] = addRevDep(options, i, pkginfo) saveNimbleData(options) -proc buildFromDir(pkgInfo: TPackageInfo, paths: seq[string]) = +proc buildFromDir(pkgInfo: PackageInfo, paths: seq[string]) = ## Builds a package as specified by ``pkgInfo``. let realDir = pkgInfo.getRealDir() var args = "" @@ -443,7 +447,7 @@ proc saveNimbleMeta(pkgDestDir, url: string, filesInstalled: HashSet[string]) = nimblemeta["files"].add(%changeRoot(pkgDestDir, "", file)) writeFile(pkgDestDir / "nimblemeta.json", $nimblemeta) -proc removePkgDir(dir: string, options: TOptions) = +proc removePkgDir(dir: string, options: Options) = ## Removes files belonging to the package in ``dir``. try: var nimblemeta = parseFile(dir / "nimblemeta.json") @@ -468,8 +472,8 @@ proc removePkgDir(dir: string, options: TOptions) = quit(QuitSuccess) removeDir(dir) -proc installFromDir(dir: string, latest: bool, options: TOptions, - url: string): tuple[paths: seq[string], pkg: TPackageInfo] = +proc installFromDir(dir: string, latest: bool, options: Options, + url: string): tuple[paths: seq[string], pkg: PackageInfo] = ## 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 @@ -490,7 +494,8 @@ proc installFromDir(dir: string, latest: bool, options: TOptions, let versionStr = (if latest: "" else: '-' & pkgInfo.version) let pkgDestDir = pkgsDir / (pkgInfo.name & versionStr) if existsDir(pkgDestDir) and existsFile(pkgDestDir / "nimblemeta.json"): - if not options.prompt(pkgInfo.name & versionStr & " already exists. Overwrite?"): + if not options.prompt(pkgInfo.name & versionStr & + " already exists. Overwrite?"): quit(QuitSuccess) removePkgDir(pkgDestDir, options) # Remove any symlinked binaries @@ -571,32 +576,34 @@ proc getNimbleTempDir(): string = else: result.add($getpid()) -proc downloadPkg(url: string, verRange: PVersionRange, - downMethod: TDownloadMethod): string = +proc downloadPkg(url: string, verRange: VersionRangeRef, + downMethod: DownloadMethod): string = let downloadDir = (getNimbleTempDir() / getDownloadDirName(url, verRange)) createDir(downloadDir) echo("Downloading ", url, " into ", downloadDir, " using ", downMethod, "...") doDownload(url, downloadDir, verRange, downMethod) result = downloadDir -proc downloadPkg(pkg: TPackage, verRange: PVersionRange): string = +proc downloadPkg(pkg: Package, verRange: VersionRangeRef): string = let downloadDir = (getNimbleTempDir() / getDownloadDirName(pkg, verRange)) let downMethod = pkg.downloadMethod.getDownloadMethod() createDir(downloadDir) - echo("Downloading ", pkg.name, " into ", downloadDir, " using ", downMethod, "...") + echo("Downloading ", pkg.name, " into ", downloadDir, " using ", downMethod, + "...") doDownload(pkg.url, downloadDir, verRange, downMethod) result = downloadDir -proc install(packages: seq[TPkgTuple], - options: TOptions, - doPrompt = true): tuple[paths: seq[string], pkg: TPackageInfo] = +proc install(packages: seq[PkgTuple], + options: Options, + doPrompt = true): tuple[paths: seq[string], pkg: PackageInfo] = if packages == @[]: result = installFromDir(getCurrentDir(), false, options, "") else: # If packages.json is not present ask the user if they want to download it. if not existsFile(options.getNimbleDir / "packages.json"): if doPrompt and - options.prompt("Local packages.json not found, download it from internet?"): + options.prompt("Local packages.json not found, download it from " & + "internet?"): update(options) else: quit("Please run nimble update.", QuitFailure) @@ -608,34 +615,35 @@ proc install(packages: seq[TPkgTuple], let downloadDir = downloadPkg(pv.name, pv.ver, meth) result = installFromDir(downloadDir, false, options, pv.name) else: - var pkg: TPackage + var pkg: Package if getPackage(pv.name, options.getNimbleDir() / "packages.json", pkg): let downloadDir = downloadPkg(pkg, pv.ver) result = installFromDir(downloadDir, false, options, pkg.url) else: - # If package is not found give the user a chance to update package.json + # If package is not found give the user a chance to update + # package.json if doPrompt and options.prompt(pv.name & " not found in local packages.json, " & "check internet for updated packages?"): update(options) result = install(@[pv], options, false) else: - raise newException(ENimble, "Package not found.") + raise newException(NimbleError, "Package not found.") -proc build(options: TOptions) = +proc build(options: Options) = var pkgInfo = getPkgInfo(getCurrentDir()) let paths = processDeps(pkginfo, options) buildFromDir(pkgInfo, paths) -proc search(options: TOptions) = +proc search(options: Options) = ## Searches for matches in ``options.action.search``. ## ## Searches are done in a case insensitive way making all strings lower case. - assert options.action.typ == ActionSearch + assert options.action.typ == actionSearch if options.action.search == @[]: - raise newException(ENimble, "Please specify a search string.") + raise newException(NimbleError, "Please specify a search string.") if not existsFile(options.getNimbleDir() / "packages.json"): - raise newException(ENimble, "Please run nimble update.") + raise newException(NimbleError, "Please run nimble update.") let pkgList = getPackageList(options.getNimbleDir() / "packages.json") var found = false template onFound: stmt = @@ -659,9 +667,9 @@ proc search(options: TOptions) = if not found: echo("No package found.") -proc list(options: TOptions) = +proc list(options: Options) = if not existsFile(options.getNimbleDir() / "packages.json"): - raise newException(ENimble, "Please run nimble update.") + raise newException(NimbleError, "Please run nimble update.") let pkgList = getPackageList(options.getNimbleDir() / "packages.json") for pkg in pkgList: echoPackage(pkg) @@ -669,9 +677,9 @@ proc list(options: TOptions) = echoPackageVersions(pkg) echo(" ") -type VersionAndPath = tuple[version: TVersion, path: string] +type VersionAndPath = tuple[version: Version, path: string] -proc listPaths(options: TOptions) = +proc listPaths(options: Options) = ## Loops over installing packages displaying their installed paths. ## ## If there are several packages installed, only the last one (the version @@ -680,7 +688,7 @@ proc listPaths(options: TOptions) = ## but at the end quits with a non zero exit error. ## ## On success the proc returns normally. - assert options.action.typ == ActionPath + assert options.action.typ == actionPath assert(not options.action.packages.isNil) var errors = 0 for name, version in options.action.packages.items: @@ -710,9 +718,10 @@ proc listPaths(options: TOptions) = echo "Warning: Package '" & name & "' not installed" errors += 1 if errors > 0: - raise newException(ENimble, "At least one of the specified packages was not found") + raise newException(NimbleError, + "At least one of the specified packages was not found") -proc init(options: TOptions) = +proc init(options: Options) = echo("Initializing new Nimble project!") var pkgName, fName: string = "" @@ -722,14 +731,15 @@ proc init(options: TOptions) = pkgName = options.action.projName fName = pkgName & ".nimble" if (existsFile(os.getCurrentDir() / fName)): - raise newException(ENimble, "Already have a nimble file.") + raise newException(NimbleError, "Already have a nimble file.") else: - echo("Enter a project name for this (blank to use working directory), Ctrl-C to abort:") + echo("Enter a project name for this (blank to use working directory), " & + "Ctrl-C to abort:") pkgName = readline(stdin) if (pkgName == ""): pkgName = os.getCurrentDir().splitPath.tail if (pkgName == ""): - raise newException(ENimble, "Could not get default file path.") + raise newException(NimbleError, "Could not get default file path.") fName = pkgName & ".nimble" # Now need to write out .nimble file with projName and other details @@ -748,18 +758,18 @@ proc init(options: TOptions) = close(outFile) else: - raise newException(ENimble, "Unable to open file " & fName & + raise newException(NimbleError, "Unable to open file " & fName & " for writing: " & osErrorMsg(osLastError())) -proc uninstall(options: TOptions) = - var pkgsToDelete: seq[TPackageInfo] = @[] +proc uninstall(options: Options) = + var pkgsToDelete: seq[PackageInfo] = @[] # Do some verification. for pkgTup in options.action.packages: echo("Looking for ", pkgTup.name, " (", $pkgTup.ver, ")...") let installedPkgs = getInstalledPkgs(options.getPkgsDir()) var pkgList = findAllPkgs(installedPkgs, pkgTup) if pkgList.len == 0: - raise newException(ENimble, "Package not found") + raise newException(NimbleError, "Package not found") echo("Checking reverse dependencies...") var errors: seq[string] = @[] @@ -785,7 +795,7 @@ proc uninstall(options: TOptions) = pkgsToDelete.add pkg if pkgsToDelete.len == 0: - raise newException(ENimble, "\n " & errors.join("\n ")) + raise newException(NimbleError, "\n " & errors.join("\n ")) var pkgNames = "" for i in 0 .. = 5): - when not defined(`{}`): - proc `{}`*(node: PJsonNode, key: string): PJsonNode = - ## Transverses the node and gets the given value. If any of the - ## names does not exist, returns nil - result = node - if isNil(node): return nil - result = result[key] - - when not defined(`{}=`): - proc `{}=`*(node: PJsonNode, names: varargs[string], value: PJsonNode) = - ## Transverses the node and tries to set the value at the given location - ## to `value` If any of the names are missing, they are added - var node = node - for i in 0..(names.len-2): - if isNil(node[names[i]]): - node[names[i]] = newJObject() - node = node[names[i]] - node[names[names.len-1]] = value diff --git a/src/nimblepkg/config.nim b/src/nimblepkg/config.nim index 9458c550..244509c5 100644 --- a/src/nimblepkg/config.nim +++ b/src/nimblepkg/config.nim @@ -5,12 +5,12 @@ import parsecfg, streams, strutils, os import tools, version, nimbletypes type - TConfig* = object + Config* = object nimbleDir*: string chcp*: bool # Whether to change the code page in .cmd files on Win. -proc initConfig(): TConfig = +proc initConfig(): Config = if getNimrodVersion() > newVersion("0.9.6"): result.nimbleDir = getHomeDir() / ".nimble" else: @@ -18,7 +18,7 @@ proc initConfig(): TConfig = result.chcp = true -proc parseConfig*(): TConfig = +proc parseConfig*(): Config = result = initConfig() var confFile = getConfigDir() / "nimble" / "nimble.ini" @@ -49,8 +49,8 @@ proc parseConfig*(): TConfig = of "chcp": result.chcp = parseBool(e.value) else: - raise newException(ENimble, "Unable to parse config file:" & + raise newException(NimbleError, "Unable to parse config file:" & " Unknown key: " & e.key) of cfgError: - raise newException(ENimble, "Unable to parse config file: " & e.msg) + raise newException(NimbleError, "Unable to parse config file: " & e.msg) close(p) diff --git a/src/nimblepkg/download.nim b/src/nimblepkg/download.nim index 73f1fe41..c79c69bb 100644 --- a/src/nimblepkg/download.nim +++ b/src/nimblepkg/download.nim @@ -6,44 +6,45 @@ import parseutils, os, osproc, strutils, tables, pegs import packageinfo, version, tools, nimbletypes type - TDownloadMethod* {.pure.} = enum - Git = "git", Hg = "hg" + DownloadMethod* {.pure.} = enum + git = "git", hg = "hg" -proc getSpecificDir(meth: TDownloadMethod): string = +proc getSpecificDir(meth: DownloadMethod): string = case meth - of TDownloadMethod.Git: + of DownloadMethod.git: ".git" - of TDownloadMethod.Hg: + of DownloadMethod.hg: ".hg" -proc doCheckout(meth: TDownloadMethod, downloadDir, branch: string) = +proc doCheckout(meth: DownloadMethod, downloadDir, branch: string) = case meth - of TDownloadMethod.Git: + of DownloadMethod.git: cd downloadDir: # Force is used here because local changes may appear straight after a # clone has happened. Like in the case of git on Windows where it # messes up the damn line endings. doCmd("git checkout --force " & branch) - of TDownloadMethod.Hg: + of DownloadMethod.hg: cd downloadDir: doCmd("hg checkout " & branch) -proc doPull(meth: TDownloadMethod, downloadDir: string) = +proc doPull(meth: DownloadMethod, downloadDir: string) = case meth - of TDownloadMethod.Git: + of DownloadMethod.git: doCheckout(meth, downloadDir, "master") cd downloadDir: doCmd("git pull") if existsFile(".gitmodules"): doCmd("git submodule update") - of TDownloadMethod.Hg: + of DownloadMethod.hg: doCheckout(meth, downloadDir, "default") cd downloadDir: doCmd("hg pull") -proc doClone(meth: TDownloadMethod, url, downloadDir: string, branch = "", tip = true) = +proc doClone(meth: DownloadMethod, url, downloadDir: string, branch = "", + tip = true) = case meth - of TDownloadMethod.Git: + of DownloadMethod.git: let depthArg = if tip: "--depth 1 " else: "" branchArg = if branch == "": "-b origin/master" else: "-b " & branch & " " @@ -60,28 +61,28 @@ proc doClone(meth: TDownloadMethod, url, downloadDir: string, branch = "", tip = doCmd("git reset --hard FETCH_HEAD") doCmd("git checkout --force " & branchArg) doCmd("git submodule update --init --recursive") - of TDownloadMethod.Hg: + of DownloadMethod.hg: let tipArg = if tip: "-r tip " else: "" branchArg = if branch == "": "" else: "-b " & branch & " " doCmd("hg clone " & tipArg & branchArg & url & " " & downloadDir) -proc getTagsList(dir: string, meth: TDownloadMethod): seq[string] = +proc getTagsList(dir: string, meth: DownloadMethod): seq[string] = cd dir: var output = execProcess("git tag") case meth - of TDownloadMethod.Git: + of DownloadMethod.git: output = execProcess("git tag") - of TDownloadMethod.Hg: + of DownloadMethod.hg: output = execProcess("hg tags") if output.len > 0: case meth - of TDownloadMethod.Git: + of DownloadMethod.git: result = @[] for i in output.splitLines(): if i == "": continue result.add(i) - of TDownloadMethod.Hg: + of DownloadMethod.hg: result = @[] for i in output.splitLines(): if i == "": continue @@ -92,10 +93,10 @@ proc getTagsList(dir: string, meth: TDownloadMethod): seq[string] = else: result = @[] -proc getTagsListRemote*(url: string, meth: TDownloadMethod): seq[string] = +proc getTagsListRemote*(url: string, meth: DownloadMethod): seq[string] = result = @[] case meth - of TDownloadMethod.Git: + of DownloadMethod.git: var (output, exitCode) = doCmdEx("git ls-remote --tags " & url) if exitCode != QuitSuccess: raise newException(OSError, "Unable to query remote tags for " & url & @@ -106,47 +107,47 @@ proc getTagsListRemote*(url: string, meth: TDownloadMethod): seq[string] = let tag = i[start .. -1] if not tag.endswith("^{}"): result.add(tag) - of TDownloadMethod.Hg: + of DownloadMethod.hg: # http://stackoverflow.com/questions/2039150/show-tags-for-remote-hg-repository raise newException(ValueError, "Hg doesn't support remote tag querying.") -proc getVersionList*(tags: seq[string]): Table[TVersion, string] = +proc getVersionList*(tags: seq[string]): Table[Version, string] = # Returns: TTable of version -> git tag name - result = initTable[TVersion, string]() + result = initTable[Version, string]() for tag in tags: if tag != "": 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 = +proc getDownloadMethod*(meth: string): DownloadMethod = case meth - of "git": return TDownloadMethod.Git - of "hg", "mercurial": return TDownloadMethod.Hg + of "git": return DownloadMethod.git + of "hg", "mercurial": return DownloadMethod.hg else: - raise newException(ENimble, "Invalid download method: " & meth) + raise newException(NimbleError, "Invalid download method: " & meth) -proc getHeadName*(meth: TDownloadMethod): string = +proc getHeadName*(meth: DownloadMethod): 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" + of DownloadMethod.git: "head" + of DownloadMethod.hg: "tip" -proc checkUrlType*(url: string): TDownloadMethod = +proc checkUrlType*(url: string): DownloadMethod = ## Determines the download method based on the URL. if doCmdEx("git ls-remote " & url).exitCode == QuitSuccess: - return TDownloadMethod.Git + return DownloadMethod.git elif doCmdEx("hg identify " & url).exitCode == QuitSuccess: - return TDownloadMethod.Hg + return DownloadMethod.hg else: - raise newException(ENimble, "Unable to identify url.") + raise newException(NimbleError, "Unable to identify url.") proc isURL*(name: string): bool = name.startsWith(peg" @'://' ") -proc doDownload*(url: string, downloadDir: string, verRange: PVersionRange, - downMethod: TDownloadMethod) = +proc doDownload*(url: string, downloadDir: string, verRange: VersionRangeRef, + downMethod: DownloadMethod) = template getLatestByTag(meth: stmt): stmt {.dirty, immediate.} = echo("Found tags...") # Find latest version that fits our ``verRange``. @@ -164,7 +165,7 @@ proc doDownload*(url: string, downloadDir: string, verRange: PVersionRange, ## version range. let pkginfo = getPkgInfo(downloadDir) if pkginfo.version.newVersion notin verRange: - raise newException(ENimble, + raise newException(NimbleError, "Downloaded package's version does not satisfy requested version " & "range: wanted $1 got $2." % [$verRange, $pkginfo.version]) @@ -177,14 +178,14 @@ proc doDownload*(url: string, downloadDir: string, verRange: PVersionRange, else: # Mercurial requies a clone and checkout. The git clone operation is # already fragmented into multiple steps so we just call doClone(). - if downMethod == TDownloadMethod.Git: + if downMethod == DownloadMethod.git: doClone(downMethod, url, downloadDir, $verRange.spe) else: doClone(downMethod, url, downloadDir, tip = false) doCheckout(downMethod, downloadDir, $verRange.spe) else: case downMethod - of TDownloadMethod.Git: + of DownloadMethod.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(url, downMethod).getVersionList() @@ -197,7 +198,7 @@ proc doDownload*(url: string, downloadDir: string, verRange: PVersionRange, doClone(downMethod, url, downloadDir) # Grab HEAD. verifyClone() - of TDownloadMethod.Hg: + of DownloadMethod.hg: doClone(downMethod, url, downloadDir) let versions = getTagsList(downloadDir, downMethod).getVersionList() @@ -208,10 +209,10 @@ proc doDownload*(url: string, downloadDir: string, verRange: PVersionRange, verifyClone() -proc echoPackageVersions*(pkg: TPackage) = +proc echoPackageVersions*(pkg: Package) = let downMethod = pkg.downloadMethod.getDownloadMethod() case downMethod - of TDownloadMethod.Git: + of DownloadMethod.git: try: let versions = getTagsListRemote(pkg.url, downMethod).getVersionList() if versions.len > 0: @@ -227,5 +228,6 @@ proc echoPackageVersions*(pkg: TPackage) = echo(" versions: (No versions tagged in the remote repository)") except OSError: echo(getCurrentExceptionMsg()) - of TDownloadMethod.Hg: - echo(" versions: (Remote tag retrieval not supported by " & pkg.downloadMethod & ")") + of DownloadMethod.hg: + echo(" versions: (Remote tag retrieval not supported by " & + pkg.downloadMethod & ")") diff --git a/src/nimblepkg/nimbletypes.nim b/src/nimblepkg/nimbletypes.nim index 544c2944..cdfc6469 100644 --- a/src/nimblepkg/nimbletypes.nim +++ b/src/nimblepkg/nimbletypes.nim @@ -4,4 +4,4 @@ # recursive imports type - ENimble* = object of Exception + NimbleError* = object of Exception diff --git a/src/nimblepkg/packageinfo.nim b/src/nimblepkg/packageinfo.nim index c0c8085f..7b1cec8e 100644 --- a/src/nimblepkg/packageinfo.nim +++ b/src/nimblepkg/packageinfo.nim @@ -4,9 +4,9 @@ import parsecfg, json, streams, strutils, parseutils, os import version, tools, nimbletypes type ## Tuple containing package name and version range. - TPkgTuple* = tuple[name: string, ver: PVersionRange] + PkgTuple* = tuple[name: string, ver: VersionRangeRef] - TPackageInfo* = object + PackageInfo* = object mypath*: string ## The path of this .nimble file name*: string version*: string @@ -19,12 +19,12 @@ type installDirs*: seq[string] installFiles*: seq[string] installExt*: seq[string] - requires*: seq[TPkgTuple] + requires*: seq[PkgTuple] bin*: seq[string] srcDir*: string backend*: string - TPackage* = object + Package* = object # Required fields in a package. name*: string url*: string # Download location. @@ -37,10 +37,10 @@ type dvcsTag*: string web*: string # Info url for humans. - TMetadata* = object + MetaData* = object url*: string -proc initPackageInfo(): TPackageInfo = +proc initPackageInfo(): PackageInfo = result.mypath = "" result.name = "" result.version = "" @@ -58,31 +58,32 @@ proc initPackageInfo(): TPackageInfo = result.srcDir = "" result.backend = "c" -proc validatePackageInfo(pkgInfo: TPackageInfo, path: string) = +proc validatePackageInfo(pkgInfo: PackageInfo, path: string) = if pkgInfo.name == "": - raise newException(ENimble, "Incorrect .nimble file: " & path & + raise newException(NimbleError, "Incorrect .nimble file: " & path & " does not contain a name field.") if pkgInfo.version == "": - raise newException(ENimble, "Incorrect .nimble file: " & path & + raise newException(NimbleError, "Incorrect .nimble file: " & path & " does not contain a version field.") if pkgInfo.author == "": - raise newException(ENimble, "Incorrect .nimble file: " & path & + raise newException(NimbleError, "Incorrect .nimble file: " & path & " does not contain an author field.") if pkgInfo.description == "": - raise newException(ENimble, "Incorrect .nimble file: " & path & + raise newException(NimbleError, "Incorrect .nimble file: " & path & " does not contain a description field.") if pkgInfo.license == "": - raise newException(ENimble, "Incorrect .nimble file: " & path & + raise newException(NimbleError, "Incorrect .nimble file: " & path & " does not contain a license field.") if pkgInfo.backend notin ["c", "cc", "objc", "cpp", "js"]: - raise newException(ENimble, "'" & pkgInfo.backend & "' is an invalid backend.") + raise newException(NimbleError, "'" & pkgInfo.backend & + "' is an invalid backend.") for c in pkgInfo.version: if c notin ({'.'} + Digits): - raise newException(ENimble, + raise newException(NimbleError, "Version may only consist of numbers and the '.' character " & "but found '" & c & "'.") -proc parseRequires(req: string): TPkgTuple = +proc parseRequires(req: string): PkgTuple = try: if ' ' in req: var i = skipUntil(req, Whitespace) @@ -94,10 +95,10 @@ proc parseRequires(req: string): TPkgTuple = result.ver = parseVersionRange(req[i .. -1]) else: result.name = req.strip - result.ver = PVersionRange(kind: verAny) - except EParseVersion: - raise newException(ENimble, "Unable to parse dependency version range: " & - getCurrentExceptionMsg()) + result.ver = VersionRangeRef(kind: verAny) + except ParseVersionError: + raise newException(NimbleError, + "Unable to parse dependency version range: " & getCurrentExceptionMsg()) proc multiSplit(s: string): seq[string] = ## Returns ``s`` split by newline and comma characters. @@ -115,7 +116,7 @@ proc multiSplit(s: string): seq[string] = if len(result) < 1: return @[s] -proc readPackageInfo*(path: string): TPackageInfo = +proc readPackageInfo*(path: string): PackageInfo = result = initPackageInfo() result.mypath = path var fs = newFileStream(path, fmRead) @@ -161,18 +162,20 @@ proc readPackageInfo*(path: string): TPackageInfo = of "javascript": result.backend = "js" else: discard else: - raise newException(ENimble, "Invalid field: " & ev.key) + raise newException(NimbleError, "Invalid field: " & ev.key) of "deps", "dependencies": case ev.key.normalize of "requires": for v in ev.value.multiSplit: result.requires.add(parseRequires(v.strip)) else: - raise newException(ENimble, "Invalid field: " & ev.key) - else: raise newException(ENimble, "Invalid section: " & currentSection) - of cfgOption: raise newException(ENimble, "Invalid package info, should not contain --" & ev.value) + raise newException(NimbleError, "Invalid field: " & ev.key) + else: raise newException(NimbleError, + "Invalid section: " & currentSection) + of cfgOption: raise newException(NimbleError, + "Invalid package info, should not contain --" & ev.value) of cfgError: - raise newException(ENimble, "Error parsing .nimble file: " & ev.msg) + raise newException(NimbleError, "Error parsing .nimble file: " & ev.msg) close(p) else: raise newException(ValueError, "Cannot open package info: " & path) @@ -188,7 +191,8 @@ proc optionalField(obj: JsonNode, name: string, default = ""): string = if obj[name].kind == JString: return obj[name].str else: - raise newException(ENimble, "Corrupted packages.json file. " & name & " field is of unexpected type.") + raise newException(NimbleError, "Corrupted packages.json file. " & name & + " field is of unexpected type.") else: return default proc requiredField(obj: JsonNode, name: string): string = @@ -197,11 +201,11 @@ proc requiredField(obj: JsonNode, name: string): string = ## Aborts execution if the field does not exist or is of invalid json type. result = optionalField(obj, name, nil) if result == nil: - raise newException(ENimble, + raise newException(NimbleError, "Package in packages.json file does not contain a " & name & " field.") -proc fromJson(obj: JSonNode): TPackage = - ## Constructs a TPackage object from a JSON node. +proc fromJson(obj: JSonNode): Package = + ## Constructs a Package object from a JSON node. ## ## Aborts execution if the JSON node doesn't contain the required fields. result.name = obj.requiredField("name") @@ -216,7 +220,7 @@ proc fromJson(obj: JSonNode): TPackage = result.description = obj.requiredField("description") result.web = obj.optionalField("web") -proc readMetadata*(path: string): TMetadata = +proc readMetaData*(path: string): MetaData = ## Reads the metadata present in ``~/.nimble/pkgs/pkg-0.1/nimblemeta.json`` var bmeta = path / "nimblemeta.json" if not existsFile(bmeta): @@ -232,7 +236,7 @@ proc readMetadata*(path: string): TMetadata = let jsonmeta = parseJson(cont) result.url = jsonmeta["url"].str -proc getPackage*(pkg: string, packagesPath: string, resPkg: var TPackage): bool = +proc getPackage*(pkg: string, packagesPath: string, resPkg: var Package): bool = ## Searches ``packagesPath`` file saving into ``resPkg`` the found package. ## ## Pass in ``pkg`` the name of the package you are searching for. As @@ -244,12 +248,12 @@ proc getPackage*(pkg: string, packagesPath: string, resPkg: var TPackage): bool resPkg = p.fromJson() return true -proc getPackageList*(packagesPath: string): seq[TPackage] = +proc getPackageList*(packagesPath: string): seq[Package] = ## Returns the list of packages found at the specified path. result = @[] let packages = parseFile(packagesPath) for p in packages: - let pkg: TPackage = p.fromJson() + let pkg: Package = p.fromJson() result.add(pkg) proc findNimbleFile*(dir: string): string = @@ -257,17 +261,20 @@ proc findNimbleFile*(dir: string): string = for kind, path in walkDir(dir): if kind == pcFile and path.splitFile.ext in [".babel", ".nimble"]: if result != "": - raise newException(ENimble, "Only one .nimble file should be present in " & dir) + raise newException(NimbleError, + "Only one .nimble file should be present in " & dir) result = path -proc getPkgInfo*(dir: string): TPackageInfo = - ## Find the .nimble file in ``dir`` and parses it, returning a TPackageInfo. +proc getPkgInfo*(dir: string): PackageInfo = + ## Find the .nimble file in ``dir`` and parses it, returning a PackageInfo. let nimbleFile = findNimbleFile(dir) if nimbleFile == "": - raise newException(ENimble, "Specified directory does not contain a .nimble file.") + raise newException(NimbleError, + "Specified directory does not contain a .nimble file.") result = readPackageInfo(nimbleFile) -proc getInstalledPkgs*(libsDir: string): seq[tuple[pkginfo: TPackageInfo, meta: TMetaData]] = +proc getInstalledPkgs*(libsDir: string): + seq[tuple[pkginfo: PackageInfo, meta: MetaData]] = ## Gets a list of installed packages. ## ## ``libsDir`` is in most cases: ~/.nimble/pkgs/ @@ -276,15 +283,15 @@ proc getInstalledPkgs*(libsDir: string): seq[tuple[pkginfo: TPackageInfo, meta: if kind == pcDir: let nimbleFile = findNimbleFile(path) if nimbleFile != "": - let meta = readMetadata(path) + let meta = readMetaData(path) result.add((readPackageInfo(nimbleFile), meta)) else: # TODO: Abstract logging. echo("WARNING: No .nimble file found for ", path) -proc findPkg*(pkglist: seq[tuple[pkginfo: TPackageInfo, meta: TMetaData]], - dep: TPkgTuple, - r: var TPackageInfo): bool = +proc findPkg*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]], + dep: PkgTuple, + r: var PackageInfo): bool = ## Searches ``pkglist`` for a package of which version is within 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 @@ -299,8 +306,8 @@ proc findPkg*(pkglist: seq[tuple[pkginfo: TPackageInfo, meta: TMetaData]], r = pkg.pkginfo result = true -proc findAllPkgs*(pkglist: seq[tuple[pkginfo: TPackageInfo, meta: TMetaData]], - dep: TPkgTuple): seq[TPackageInfo] = +proc findAllPkgs*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]], + dep: PkgTuple): seq[PackageInfo] = ## Searches ``pkglist`` for packages of which version is within the range ## of ``dep.ver``. This is similar to ``findPkg`` but returns multiple ## packages if multiple are found. @@ -311,7 +318,7 @@ proc findAllPkgs*(pkglist: seq[tuple[pkginfo: TPackageInfo, meta: TMetaData]], if withinRange(newVersion(pkg.pkginfo.version), dep.ver): result.add pkg.pkginfo -proc getRealDir*(pkgInfo: TPackageInfo): string = +proc getRealDir*(pkgInfo: PackageInfo): string = ## Returns the ``pkgInfo.srcDir`` or the .mypath directory if package does ## not specify the src dir. if pkgInfo.srcDir != "": @@ -335,7 +342,7 @@ proc getNameVersion*(pkgpath: string): tuple[name, version: string] = result.version = tail[i+1 .. -1] break -proc echoPackage*(pkg: TPackage) = +proc echoPackage*(pkg: Package) = echo(pkg.name & ":") echo(" url: " & pkg.url & " (" & pkg.downloadMethod & ")") echo(" tags: " & pkg.tags.join(", ")) @@ -344,7 +351,7 @@ proc echoPackage*(pkg: TPackage) = if pkg.web.len > 0: echo(" website: " & pkg.web) -proc getDownloadDirName*(pkg: TPackage, verRange: PVersionRange): string = +proc getDownloadDirName*(pkg: Package, verRange: VersionRangeRef): string = result = pkg.name let verSimple = getSimpleString(verRange) if verSimple != "": @@ -352,5 +359,7 @@ proc getDownloadDirName*(pkg: TPackage, verRange: PVersionRange): string = result.add verSimple when isMainModule: - doAssert getNameVersion("/home/user/.nimble/libs/packagea-0.1") == ("packagea", "0.1") - doAssert getNameVersion("/home/user/.nimble/libs/package-a-0.1") == ("package-a", "0.1") + doAssert getNameVersion("/home/user/.nimble/libs/packagea-0.1") == + ("packagea", "0.1") + doAssert getNameVersion("/home/user/.nimble/libs/package-a-0.1") == + ("package-a", "0.1") diff --git a/src/nimblepkg/tools.nim b/src/nimblepkg/tools.nim index a7adfb08..f642ebcb 100644 --- a/src/nimblepkg/tools.nim +++ b/src/nimblepkg/tools.nim @@ -8,16 +8,17 @@ import version, packageinfo, nimbletypes proc doCmd*(cmd: string) = let bin = cmd.split(' ')[0] if findExe(bin) == "": - raise newException(ENimble, "'" & bin & "' not in PATH.") + raise newException(NimbleError, "'" & bin & "' not in PATH.") let exitCode = execCmd(cmd) if exitCode != QuitSuccess: - raise newException(ENimble, "Execution failed with exit code " & $exitCode) + raise newException(NimbleError, + "Execution failed with exit code " & $exitCode) proc doCmdEx*(cmd: string): tuple[output: TaintedString, exitCode: int] = let bin = cmd.split(' ')[0] if findExe(bin) == "": - raise newException(ENimble, "'" & bin & "' not in PATH.") + raise newException(NimbleError, "'" & bin & "' not in PATH.") return execCmdEx(cmd) template cd*(dir: string, body: stmt) = @@ -33,7 +34,7 @@ proc getNimBin*: string = if findExe("nim") != "": result = "nim" elif findExe("nimrod") != "": result = "nimrod" -proc getNimrodVersion*: TVersion = +proc getNimrodVersion*: Version = let nimBin = getNimBin() let vOutput = doCmdEx(nimBin & " -v").output var matches: array[0..MaxSubpatterns, string] @@ -75,7 +76,7 @@ proc copyDirD*(fro, to: string): seq[string] = createDir(changeRoot(fro, to, path.splitFile.dir)) result.add copyFileD(path, changeRoot(fro, to, path)) -proc getDownloadDirName*(uri: string, verRange: PVersionRange): string = +proc getDownloadDirName*(uri: string, verRange: VersionRangeRef): string = ## Creates a directory name based on the specified ``uri`` (url) result = "" let puri = parseUri(uri) diff --git a/src/nimblepkg/version.nim b/src/nimblepkg/version.nim index 46190163..358a8763 100644 --- a/src/nimblepkg/version.nim +++ b/src/nimblepkg/version.nim @@ -4,10 +4,10 @@ ## Module for handling versions and version ranges such as ``>= 1.0 & <= 1.5`` import strutils, tables, hashes, parseutils type - TVersion* = distinct string - TSpecial* = distinct string + Version* = distinct string + Special* = distinct string - TVersionRangeEnum* = enum + VersionRangeEnum* = enum verLater, # > V verEarlier, # < V verEqLater, # >= V -- Equal or later @@ -17,32 +17,32 @@ type verAny, # * verSpecial # #head - PVersionRange* = ref TVersionRange - TVersionRange* = object - case kind*: TVersionRangeEnum + VersionRangeRef* = ref VersionRange + VersionRange* = object + case kind*: VersionRangeEnum of verLater, verEarlier, verEqLater, verEqEarlier, verEq: - ver*: TVersion + ver*: Version of verSpecial: - spe*: TSpecial + spe*: Special of verIntersect: - verILeft, verIRight: PVersionRange + verILeft, verIRight: VersionRangeRef of verAny: nil - EParseVersion* = object of ValueError + ParseVersionError* = object of ValueError -proc newVersion*(ver: string): TVersion = return TVersion(ver) -proc newSpecial*(spe: string): TSpecial = return TSpecial(spe) +proc newVersion*(ver: string): Version = return Version(ver) +proc newSpecial*(spe: string): Special = return Special(spe) -proc `$`*(ver: TVersion): string {.borrow.} +proc `$`*(ver: Version): string {.borrow.} -proc hash*(ver: TVersion): THash {.borrow.} +proc hash*(ver: Version): THash {.borrow.} -proc `$`*(ver: TSpecial): string {.borrow.} +proc `$`*(ver: Special): string {.borrow.} -proc hash*(ver: TSpecial): THash {.borrow.} +proc hash*(ver: Special): THash {.borrow.} -proc `<`*(ver: TVersion, ver2: TVersion): bool = +proc `<`*(ver: Version, ver2: Version): bool = var sVer = string(ver).split('.') var sVer2 = string(ver2).split('.') for i in 0..max(sVer.len, sVer2.len)-1: @@ -59,7 +59,7 @@ proc `<`*(ver: TVersion, ver2: TVersion): bool = else: return false -proc `==`*(ver: TVersion, ver2: TVersion): bool = +proc `==`*(ver: Version, ver2: Version): bool = var sVer = string(ver).split('.') var sVer2 = string(ver2).split('.') for i in 0..max(sVer.len, sVer2.len)-1: @@ -74,13 +74,13 @@ proc `==`*(ver: TVersion, ver2: TVersion): bool = else: return false -proc `==`*(spe: TSpecial, spe2: TSpecial): bool = +proc `==`*(spe: Special, spe2: Special): bool = return ($spe).toLower() == ($spe2).toLower() -proc `<=`*(ver: TVersion, ver2: TVersion): bool = +proc `<=`*(ver: Version, ver2: Version): bool = return (ver == ver2) or (ver < ver2) -proc withinRange*(ver: TVersion, ran: PVersionRange): bool = +proc withinRange*(ver: Version, ran: VersionRangeRef): bool = case ran.kind of verLater: return ver > ran.ver @@ -99,7 +99,7 @@ proc withinRange*(ver: TVersion, ran: PVersionRange): bool = of verAny: return true -proc withinRange*(spe: TSpecial, ran: PVersionRange): bool = +proc withinRange*(spe: Special, ran: VersionRangeRef): bool = case ran.kind of verLater, verEarlier, verEqLater, verEqEarlier, verEq, verIntersect: return false @@ -108,16 +108,17 @@ proc withinRange*(spe: TSpecial, ran: PVersionRange): bool = of verAny: return true -proc contains*(ran: PVersionRange, ver: TVersion): bool = +proc contains*(ran: VersionRangeRef, ver: Version): bool = return withinRange(ver, ran) -proc contains*(ran: PVersionRange, spe: TSpecial): bool = +proc contains*(ran: VersionRangeRef, spe: Special): bool = return withinRange(spe, ran) -proc makeRange*(version: string, op: string): PVersionRange = +proc makeRange*(version: string, op: string): VersionRangeRef = new(result) if version == "": - raise newException(EParseVersion, "A version needs to accompany the operator.") + raise newException(ParseVersionError, + "A version needs to accompany the operator.") case op of ">": result.kind = verLater @@ -130,15 +131,15 @@ proc makeRange*(version: string, op: string): PVersionRange = of "": result.kind = verEq else: - raise newException(EParseVersion, "Invalid operator: " & op) - result.ver = TVersion(version) + raise newException(ParseVersionError, "Invalid operator: " & op) + result.ver = Version(version) -proc parseVersionRange*(s: string): PVersionRange = +proc parseVersionRange*(s: string): VersionRangeRef = # >= 1.5 & <= 1.8 new(result) if s[0] == '#': result.kind = verSpecial - result.spe = s[1 .. -1].TSpecial + result.spe = s[1 .. -1].Special return var i = 0 @@ -159,7 +160,7 @@ proc parseVersionRange*(s: string): PVersionRange = # Disallow more than one verIntersect. It's pointless and could lead to # major unpredictable mistakes. if result.verIRight.kind == verIntersect: - raise newException(EParseVersion, + raise newException(ParseVersionError, "Having more than one `&` in a version range is pointless") break @@ -175,13 +176,15 @@ proc parseVersionRange*(s: string): PVersionRange = # Make sure '0.9 8.03' is not allowed. if version != "" and i < s.len: if s[i+1] in {'0'..'9', '.'}: - raise newException(EParseVersion, "Whitespace is not allowed in a version literal.") + raise newException(ParseVersionError, + "Whitespace is not allowed in a version literal.") else: - raise newException(EParseVersion, "Unexpected char in version range: " & s[i]) + raise newException(ParseVersionError, + "Unexpected char in version range: " & s[i]) inc(i) -proc `$`*(verRange: PVersionRange): string = +proc `$`*(verRange: VersionRangeRef): string = case verRange.kind of verLater: result = "> " @@ -202,34 +205,36 @@ proc `$`*(verRange: PVersionRange): string = result.add(string(verRange.ver)) -proc getSimpleString*(verRange: PVersionRange): string = - ## Gets a string with no special symbols and spaces. Used for dir name creation - ## in tools.nim +proc getSimpleString*(verRange: VersionRangeRef): string = + ## Gets a string with no special symbols and spaces. Used for dir name + ## creation in tools.nim case verRange.kind of verSpecial: result = $verRange.spe of verLater, verEarlier, verEqLater, verEqEarlier, verEq: result = $verRange.ver of verIntersect: - result = getSimpleString(verRange.verILeft) & "_" & getSimpleString(verRange.verIRight) + result = getSimpleString(verRange.verILeft) & "_" & + getSimpleString(verRange.verIRight) of verAny: result = "" -proc newVRAny*(): PVersionRange = +proc newVRAny*(): VersionRangeRef = new(result) result.kind = verAny -proc newVREarlier*(ver: string): PVersionRange = +proc newVREarlier*(ver: string): VersionRangeRef = new(result) result.kind = verEarlier result.ver = newVersion(ver) -proc newVREq*(ver: string): PVersionRange = +proc newVREq*(ver: string): VersionRangeRef = new(result) result.kind = verEq result.ver = newVersion(ver) -proc findLatest*(verRange: PVersionRange, versions: Table[TVersion, string]): tuple[ver: TVersion, tag: string] = +proc findLatest*(verRange: VersionRangeRef, + versions: Table[Version, string]): tuple[ver: Version, tag: string] = result = (newVersion(""), "") for ver, tag in versions: if not withinRange(ver, verRange): continue @@ -263,8 +268,10 @@ when isMainModule: doAssert(newVersion("") < newVersion("1.0.0")) doAssert(newVersion("") < newVersion("0.1.0")) - 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") + var versions = toTable[Version, 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: Allow these in later versions? #doAssert newVersion("0.1-rc1") < newVersion("0.2") diff --git a/tests/tester.nim b/tests/tester.nim index 3ad86140..7fedb0ba 100644 --- a/tests/tester.nim +++ b/tests/tester.nim @@ -31,7 +31,7 @@ test "can reject same version dependencies": let ls = outp.strip.splitLines() check exitCode != QuitSuccess check ls[ls.len-1] == "Error: unhandled exception: Cannot satisfy the " & - "dependency on PackageA 0.2.0 and PackageA 0.5.0 [ENimble]" + "dependency on PackageA 0.2.0 and PackageA 0.5.0 [NimbleError]" test "can update": check execCmdEx(path & " update").exitCode == QuitSuccess @@ -54,7 +54,7 @@ test "can uninstall": let ls = outp.processOutput() check exitCode != QuitSuccess check ls[ls.len-1] == " Cannot uninstall issue27b (0.1.0) because " & - "issue27a (0.1.0) depends on it [ENimble]" + "issue27a (0.1.0) depends on it [NimbleError]" check execCmdEx(path & " uninstall -y issue27").exitCode == QuitSuccess check execCmdEx(path & " uninstall -y issue27a").exitCode == QuitSuccess @@ -76,5 +76,6 @@ test "can uninstall": # Remove the rest of the installed packages. check execCmdEx(path & " uninstall -y PackageB").exitCode == QuitSuccess - check execCmdEx(path & " uninstall -y PackageA@0.2 issue27b").exitCode == QuitSuccess + check execCmdEx(path & " uninstall -y PackageA@0.2 issue27b").exitCode == + QuitSuccess check (not dirExists(getHomeDir() / ".nimble" / "pkgs" / "PackageA-0.2.0")) From f4c052eaca78b3689d445c7875d22638442ae6ea Mon Sep 17 00:00:00 2001 From: Louis Berube Date: Fri, 2 Jan 2015 16:04:46 -0500 Subject: [PATCH 3/3] Fixed out-of-order problems in the automated unit tests --- src/nimble.nim | 4 ++-- src/nimblepkg/tools.nim | 5 +++++ tests/tester.nim | 48 +++++++++++++++++++++++++++++++++-------- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/nimble.nim b/src/nimble.nim index 18ec04f2..c3091371 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -596,9 +596,9 @@ proc downloadPkg(pkg: Package, verRange: VersionRangeRef): string = proc install(packages: seq[PkgTuple], options: Options, doPrompt = true): tuple[paths: seq[string], pkg: PackageInfo] = - if packages == @[]: + if packages == @[]: result = installFromDir(getCurrentDir(), false, options, "") - else: + else: # If packages.json is not present ask the user if they want to download it. if not existsFile(options.getNimbleDir / "packages.json"): if doPrompt and diff --git a/src/nimblepkg/tools.nim b/src/nimblepkg/tools.nim index f642ebcb..f0e7d3a1 100644 --- a/src/nimblepkg/tools.nim +++ b/src/nimblepkg/tools.nim @@ -9,8 +9,13 @@ proc doCmd*(cmd: string) = let bin = cmd.split(' ')[0] if findExe(bin) == "": raise newException(NimbleError, "'" & bin & "' not in PATH.") + + # To keep output in sequence + stdout.flushFile() + stderr.flushFile() let exitCode = execCmd(cmd) + if exitCode != QuitSuccess: raise newException(NimbleError, "Execution failed with exit code " & $exitCode) diff --git a/tests/tester.nim b/tests/tester.nim index 7fedb0ba..f95e76f4 100644 --- a/tests/tester.nim +++ b/tests/tester.nim @@ -1,6 +1,6 @@ # Copyright (C) Dominik Picheta. All rights reserved. # BSD License. Look at license.txt for more info. -import osproc, unittest, strutils, os, sequtils, future +import osproc, streams, unittest, strutils, os, sequtils, future const path = "../src/nimble" @@ -15,6 +15,35 @@ template cd*(dir: string, body: stmt) = body setCurrentDir(lastDir) +proc execCmdEx2*(command: string, options: set[ProcessOption] = { + poUsePath}): tuple[ + output: TaintedString, + error: TaintedString, + exitCode: int] {.tags: [ExecIOEffect, ReadIOEffect], gcsafe.} = + ## A slightly altered version of osproc.execCmdEx + ## Runs the `command` and returns the standard output, error output, and + ## exit code. + var p = startProcess(command = command, options = options + {poEvalCommand}) + var outp = outputStream(p) + var errp = errorStream(p) + result = (TaintedString"", TaintedString"", -1) + var outLine = newStringOfCap(120).TaintedString + var errLine = newStringOfCap(120).TaintedString + while true: + var checkForExit = true + if outp.readLine(outLine): + result[0].string.add(outLine.string) + result[0].string.add("\n") + checkForExit = false + if errp.readLine(errLine): + result[1].string.add(errLine.string) + result[1].string.add("\n") + checkForExit = false + if checkForExit: + result[2] = peekExitCode(p) + if result[2] != -1: break + close(p) + proc processOutput(output: string): seq[string] = output.strip.splitLines().filter((x: string) => (x.len > 0)) @@ -24,11 +53,11 @@ test "can install packagebin2": QuitSuccess test "can reject same version dependencies": - let (outp, exitCode) = execCmdEx(path & + let (outp, errp, exitCode) = execCmdEx2(path & " install -y https://github.com/nimble-test/packagebin.git") - #echo outp - # TODO: outp is not in the correct order. - let ls = outp.strip.splitLines() + # We look at the error output here to avoid out-of-order problems caused by + # stderr output being generated and flushed without first flushing stdout + let ls = errp.strip.splitLines() check exitCode != QuitSuccess check ls[ls.len-1] == "Error: unhandled exception: Cannot satisfy the " & "dependency on PackageA 0.2.0 and PackageA 0.5.0 [NimbleError]" @@ -50,8 +79,9 @@ test "issue #27": test "can uninstall": block: - let (outp, exitCode) = execCmdEx(path & " uninstall -y issue27b") - let ls = outp.processOutput() + let (outp, errp, exitCode) = execCmdEx2(path & " uninstall -y issue27b") + # let ls = outp.processOutput() + let ls = errp.strip.splitLines() check exitCode != QuitSuccess check ls[ls.len-1] == " Cannot uninstall issue27b (0.1.0) because " & "issue27a (0.1.0) depends on it [NimbleError]" @@ -62,9 +92,9 @@ test "can uninstall": # Remove Package* check execCmdEx(path & " uninstall -y PackageA@0.5").exitCode == QuitSuccess - let (outp, exitCode) = execCmdEx(path & " uninstall -y PackageA") + let (outp, errp, exitCode) = execCmdEx2(path & " uninstall -y PackageA") check exitCode != QuitSuccess - let ls = outp.processOutput() + let ls = errp.processOutput() check ls[ls.len-2].startsWith(" Cannot uninstall PackageA ") check ls[ls.len-1].startsWith(" Cannot uninstall PackageA ") check execCmdEx(path & " uninstall -y PackageBin2").exitCode == QuitSuccess