From 2b28c91e3878ac3c58ff11f892ed2a035a7423ea Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 8 Dec 2012 22:13:00 +0000 Subject: [PATCH] Dependencies can now be specified. --- packageinfo.nim | 23 +++++- readme.markdown | 7 +- version.nim | 183 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 version.nim diff --git a/packageinfo.nim b/packageinfo.nim index 964e80c..92d7181 100644 --- a/packageinfo.nim +++ b/packageinfo.nim @@ -1,4 +1,5 @@ -import parsecfg, json, streams, strutils +import parsecfg, json, streams, strutils, parseutils +import version type TPackageInfo* = object name*: string @@ -8,6 +9,7 @@ type license*: string skipDirs*: seq[string] skipFiles*: seq[string] + requires*: seq[tuple[name: string, ver: PVersionRange]] TPackage* = object name*: string @@ -27,6 +29,7 @@ proc initPackageInfo(): TPackageInfo = result.license = "" result.skipDirs = @[] result.skipFiles = @[] + result.requires = @[] proc validatePackageInfo(pkgInfo: TPackageInfo, path: string) = if pkgInfo.name == "": @@ -40,6 +43,14 @@ proc validatePackageInfo(pkgInfo: TPackageInfo, path: string) = if pkgInfo.license == "": quit("Incorrect .babel file: " & path & " does not contain a license field.") +proc parseRequires(req: string): tuple[name: string, ver: PVersionRange] = + try: + var i = skipUntil(req, whitespace) + result.name = req[0 .. i] + result.ver = parseVersionRange(req[i .. -1]) + except EParseVersion: + quit("Unable to parse dependency version range: " & getCurrentExceptionMsg()) + proc readPackageInfo*(path: string): TPackageInfo = result = initPackageInfo() var fs = newFileStream(path, fmRead) @@ -63,12 +74,18 @@ proc readPackageInfo*(path: string): TPackageInfo = of "author": result.author = ev.value of "description": result.description = ev.value of "license": result.license = ev.value - of "library": - case ev.key.normalize of "skipdirs": result.skipDirs.add(ev.value.split(',')) of "skipfiles": result.skipFiles.add(ev.value.split(',')) + else: + quit("Invalid field: " & ev.key, QuitFailure) + of "deps", "dependencies": + case ev.key.normalize + of "requires": + result.requires.add(parseRequires(ev.value)) + else: + quit("Invalid field: " & ev.key, QuitFailure) else: quit("Invalid section: " & currentSection, QuitFailure) of cfgOption: quit("Invalid package info, should not contain --" & ev.value, QuitFailure) of cfgError: diff --git a/readme.markdown b/readme.markdown index 7ea327f..8795254 100644 --- a/readme.markdown +++ b/readme.markdown @@ -40,12 +40,15 @@ author = "Dominik Picheta" description = """Example .babel file.""" license = "MIT" -[Library] SkipDirs = "SomeDir" ; ./ProjectName/SomeDir will be skipped. SkipFiles = "file.txt,file2.txt" ; ./ProjectName/{file.txt, file2.txt} will be skipped. + +[Deps] +Requires: "nimrod >= 0.8.0" ``` -All the fields under ``[Package]`` are required. +All the fields (except ``SkipDirs`` and ``SkipFiles``) under ``[Package]`` are +required. ## Submitting your package to the package list. Babel's packages list is stored on github and everyone is encouraged to add diff --git a/version.nim b/version.nim new file mode 100644 index 0000000..0c7a19c --- /dev/null +++ b/version.nim @@ -0,0 +1,183 @@ +## Module for handling versions and version ranges such as ``>= 1.0 & <= 1.5`` +import strutils +type + TVersion* = distinct string + + TVersionRangeEnum* = enum + verLater, # > V + verEarlier, # < V + verEqLater, # >= V -- Equal or later + verEqEarlier, # <= V -- Equal or earlier + verIntersect, # > V & < V + verEq, # V + verAny # * + + PVersionRange* = ref TVersionRange + TVersionRange* = object + case kind*: TVersionRangeEnum + of verLater, verEarlier, verEqLater, verEqEarlier, verEq: + ver*: TVersion + of verIntersect: + verILeft, verIRight: PVersionRange + of verAny: + nil + + EParseVersion* = object of EInvalidValue + +proc newVersion*(ver: string): TVersion = return TVersion(ver) + +proc `$`*(ver: TVersion): String {.borrow.} + +proc `<`*(ver: TVersion, ver2: TVersion): Bool = + var sVer = string(ver).split('.') + var sVer2 = string(ver2).split('.') + for i in 0..max(sVer.len, sVer2.len)-1: + if i > sVer.len-1: + return True + elif i > sVer2.len-1: + return False + + var sVerI = parseInt(sVer[i]) + var sVerI2 = parseInt(sVer2[i]) + if sVerI < sVerI2: + return True + elif sVerI == sVerI2: + nil + else: + return False + +proc `==`*(ver: TVersion, ver2: TVersion): Bool {.borrow.} + +proc `<=`*(ver: TVersion, ver2: TVersion): Bool = + return (ver == ver2) or (ver < ver2) + +proc withinRange*(ver: TVersion, ran: PVersionRange): Bool = + case ran.kind + of verLater: + return ver > ran.ver + of verEarlier: + return ver < ran.ver + of verEqLater: + return ver >= ran.ver + of verEqEarlier: + return ver <= ran.ver + of verEq: + return ver == ran.ver + of verIntersect: + return withinRange(ver, ran.verILeft) and withinRange(ver, ran.verIRight) + of verAny: + return True + +proc makeRange*(version: string, op: string): PVersionRange = + new(result) + if version == "": + raise newException(EParseVersion, "A version needs to accompany the operator.") + case op + of ">": + result.kind = verLater + of "<": + result.kind = verEarlier + of ">=": + result.kind = verEqLater + of "<=": + result.kind = verEqEarlier + of "": + result.kind = verEq + else: + raise newException(EParseVersion, "Invalid operator: " & op) + result.ver = TVersion(version) + +proc parseVersionRange*(s: string): PVersionRange = + # >= 1.5 & <= 1.8 + new(result) + + var i = 0 + var op = "" + var version = "" + while True: + case s[i] + of '>', '<', '=': + op.add(s[i]) + of '&': + result.kind = verIntersect + result.verILeft = makeRange(version, op) + + # Parse everything after & + # Recursion <3 + result.verIRight = parseVersionRange(substr(s, i + 1)) + + # Disallow more than one verIntersect. It's pointless and could lead to + # major unknown mistakes. + if result.verIRight.kind == verIntersect: + raise newException(EParseVersion, + "Having more than one `&` in a version range is pointless") + + break + + of '0'..'9', '.': + version.add(s[i]) + + of '\0': + result = makeRange(version, op) + break + + of ' ': + # 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.") + + else: + raise newException(EParseVersion, "Unexpected char in version range: " & s[i]) + inc(i) + +proc `$`*(verRange: PVersionRange): String = + echo(verRange.repr()) + case verRange.kind + of verLater: + result = "> " + of verEarlier: + result = "< " + of verEqLater: + result = ">= " + of verEqEarlier: + result = "<= " + of verEq: + result = "" + of verIntersect: + return $verRange.verILeft & " & " & $verRange.verIRight + of verAny: + return "Any" + + result.add(string(verRange.ver)) + +proc newVRAny*(): PVersionRange = + new(result) + result.kind = verAny + +proc newVREarlier*(ver: String): PVersionRange = + new(result) + result.kind = verEarlier + result.ver = newVersion(ver) + +proc newVREq*(ver: string): PVersionRange = + new(result) + result.kind = verEq + result.ver = newVersion(ver) + +when isMainModule: + doAssert(newVersion("1.0") < newVersion("1.4")) + doAssert(newVersion("1.0.1") > newVersion("1.0")) + doAssert(newVersion("1.0.6") <= newVersion("1.0.6")) + + var inter1 = parseVersionRange(">= 1.0 & <= 1.5") + var inter2 = parseVersionRange("1.0") + doAssert(inter2.kind == verEq) + #echo(parseVersionRange(">= 0.8 0.9")) + + doAssert(not withinRange(newVersion("1.5.1"), inter1)) + doAssert(withinRange(newVersion("1.0.2.3.4.5.6.7.8.9.10.11.12"), inter1)) + + doAssert(newVersion("1") == newVersion("1")) + + echo("Everything works!") \ No newline at end of file