commit 77fff838cc2815e354b7814a90e7fdff83f0623c Author: Dominik Picheta Date: Thu Jan 27 15:13:39 2011 +0000 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b25c15b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*~ diff --git a/babel.babel b/babel.babel new file mode 100644 index 0000000..7c37281 --- /dev/null +++ b/babel.babel @@ -0,0 +1,15 @@ +; Babel library +name = "babel" +version = "0.1.0" +author = "Dominik Picheta" +category = "Distribution" +description = """Babel framework: Specifies a common interface for programmers + to more easily build their applications in portable way.""" + +[Library] +Depends = "nimrod >= 0.8.10" +ExposedModules = "parser, installer, version" ; No need for .nim + +;[Bin babel] +;Depends = "nimrod >= 0.8.11" +; diff --git a/installer.nim b/installer.nim new file mode 100644 index 0000000..36858db --- /dev/null +++ b/installer.nim @@ -0,0 +1,101 @@ +import parser, version, osproc, strutils, re, os + +type + EInstall = object of EBase + + TDepend = tuple[name: String, verRange: PVersionRange] + +proc getNimVersion(cmd: string = "nimrod"): String = + var output = execProcess(cmd & " -v") + # TODO: Fix this. Don't know why it doesn't work. + ##echo(splitlines(output)[0]) + # :\ + if splitlines(output)[0] =~ re"(Version\s.+?\s)": + echo(matches[0]) + for i in items(matches): + echo(i) + else: + nil + #echo(":(") + + return "0.8.10" + +proc join(s: seq[string], sep: char = ' '): string = + result = "" + for i in 0..len(s)-1: + if i < len(s)-1: + result.add($sep) + result.add(s[i]) + +proc dependExists(name: string, verRange: PVersionRange): Bool = + if name == "nimrod": + var nimVer = getNimVersion() + if not withinRange(newVersion(nimVer), verRange): + raise newException(EInstall, "Nimrod version(" & + nimVer & ") doesn't satisfy dependency") + else: return True + else: + # TODO: Figure out how to check whether a package has been installed... + # ... Perhaps a list of all the packages that have been installed? + # ... or just look for the package in PATH + $nimrod/lib/babel/packageName + assert(False) + +proc verifyDepends*(proj: TProject): seq[TDepend] = + result = @[] + for i in items(proj.depends): + var spl = i.split() + var nameStr = "" + var verStr = "" + if spl.len == 1: + nameStr = spl[0] + elif spl.len > 1: + nameStr = spl[0] + spl.del(0) + verStr = join(spl, ' ') + else: + raise newException(EInstall, "Incorrect dependency got: " & i) + + var verRange: PVersionRange + if verStr == "": + new(verRange) + verRange.kind = verAny + else: + verRange = parseVersionRange(verStr) + + if not dependExists(nameStr, verRange): + result.add((nameStr, verRange)) + +proc install*(name: string, filename: string = "") = + ## Install package by the name of ``name``, filename specifies where to look for it + ## if left as "", the current working directory will be assumed. + # TODO: Add a `debug` variable? If true the status messages get echo-ed, + # vice-versa if false? + var babelFile: TProject = initProj() + var path = "" + if filename == "": + path = name & ".babel" + else: + path = filename / name & ".babel" + + echo("Reading ", path, "...") + babelFile = parseBabel(path) + + var ret = babelFile.verify() + if not ret.b: + raise newException(EInstall, "Verifying the .babel file failed: " & ret.reason) + + if babelFile.depends.len == 1: + echo("Verifying 1 dependency...") + else: + echo("Verifying ", babelFile.depends.len(), " dependencies...") + var dependsNeeded = babelFile.verifyDepends() + if dependsNeeded.len() > 0: + raise newException(EInstall, "TODO: Download & Install dependencies.") + else: + echo("All dependencies verified!") + + echo("Installing " & name) + # TODO: Install. + +when isMainModule: + install("babel") diff --git a/parser.nim b/parser.nim new file mode 100644 index 0000000..35e8a8b --- /dev/null +++ b/parser.nim @@ -0,0 +1,145 @@ +import parsecfg, streams, strutils, version + +type + TProject* = object + name*: String # Req + version*: String # Req + author*: String # Req + category*: String # Req + desc*: String # Req + license*: String + homepage*: String + + library*: bool + depends*: seq[string] # Dependencies + modules*: seq[string] # ExtraModules + files*: seq[string] # files + + executable*: bool + + unknownFields*: seq[string] # TODO: + + EParseErr* = object of EIO + +proc initProj*(): TProject = + result.name = "" + result.version = "" + result.author = "" + result.category = "" + result.desc = "" + result.license = "" + result.homepage = "" + + result.library = False + result.executable = False + result.depends = @[] + result.modules = @[] + result.files = @[] + + result.unknownFields = @[] + + +proc parseList(s: string): seq[string] = + result = @[] + var many = s.split({',', ';'}) + for i in items(many): + result.add(i.strip()) + +proc parseErr(p: TCfgParser, msg: string) = + raise newException(EParseErr, "(" & $p.getLine() & ", " & + $p.getColumn() & ") " & msg) + +proc parseBabel*(file: string): TProject = + var f = newFileStream(file, fmRead) + if f != nil: + var p: TCfgParser + open(p, f, file) + + var section: String = "" + while true: + var e = next(p) + case e.kind + of cfgEof: + break + of cfgKeyValuePair: + case section + of "": + case normalize(e.key): + of "name": + result.name = e.value + of "version": + result.version = e.value + of "author": + result.author = e.value + of "category": + result.category = e.value + of "description": + result.desc = e.value + of "homepage": + result.homepage = e.value + of "license": + result.license = e.value + else: + p.parseErr("Unknown key: " & e.key) + of "library": + case normalize(e.key) + of "depends": + result.depends = e.value.parseList() + of "files": + result.files = e.value.parseList() + of "exposedmodules": + result.modules = e.value.parseList() + else: + p.parseErr("Unknown key: " & e.key) + of "executable": + case normalize(e.key) + of "depends": + result.depends = e.value.parseList() + of "extrafiles": + result.files = e.value.parseList() + else: + p.parseErr("Unknown key: " & e.key) + + else: + p.parseErr("Unknown section: " & section) + + of cfgSectionStart: + section = normalize(e.section) + case normalize(e.section): + of "library": + result.library = True + of "bin": + result.executable = True + else: + p.parseErr("Unknown section: " & section) + + of cfgError: + p.parseErr(e.msg) + + of cfgOption: + p.parseErr("Unknown option: " & e.key) + + close(p) + else: + raise newException(EIO, "Cannot open " & file) + +proc isEmpty*(s: string): Bool = return s == "" + +proc verify*(proj: TProject): tuple[b: Bool, reason: string] = + ## Checks whether the required fields have been specified. + if isEmpty(proj.name) or isEmpty(proj.version) or isEmpty(proj.author) or + isEmpty(proj.category) or isEmpty(proj.desc): + return (False, "Missing required fields.") + elif proj.library == false and proj.executable == false: + return (False, "Either a valid Library needs to be specified or a valid Bin.") + elif proj.library == true and proj.modules.len() == 0: + return (False, "A valid library needs at least one ExposedModule listed.") + # TODO: Rules for Bin. + + return (True, "") + +when isMainModule: + for i in items(parseList("test, asdasd >sda; jsj, kk >>, sd")): + echo(i) + var project = parseBabel("babel.babel") + echo project.library diff --git a/version.nim b/version.nim new file mode 100644 index 0000000..465b0fc --- /dev/null +++ b/version.nim @@ -0,0 +1,131 @@ +## 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 laterparseInt(sVer[i]) + verEqEarlier, # <= V -- Equal or earlier + verIntersect, # > V & < V + verAny # * + + PVersionRange* = ref TVersionRange + TVersionRange* = object + case kind*: TVersionRangeEnum + of verLater, verEarlier, verEqLater, verEqEarlier: + ver*: TVersion + of verIntersect: + verI*: tuple[left: PVersionRange, right: PVersionRange] + of verAny: + nil + + EParseVersion = object of EBase + +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 verIntersect: + return withinRange(ver, ran.verI.left) and withinRange(ver, ran.verI.right) + of verAny: + return True + + +proc makeRange*(version: string, op: string): PVersionRange = + new(result) + case op + of ">": + result.kind = verLater + of "<": + result.kind = verEarlier + of ">=": + result.kind = verEqLater + of "<=": + result.kind = verEqEarlier + 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 '&': + var left = makeRange(version, op) + + # Parse everything after & + # Recursion <3 + var right = parseVersionRange(copy(s, i + 1)) + + result.kind = verIntersect + result.verI = (left, right) + break + + of '0'..'9', '.': + version.add(s[i]) + + of '\0': + result = makeRange(version, op) + break + + of ' ': + nil # Ignore whitespace + + else: + raise newException(EParseVersion, "Unexpected char in version range: " & s[i]) + inc(i) + +when isMainModule: + assert(newVersion("1.0") < newVersion("1.4")) + assert(newVersion("1.0.1") > newVersion("1.0")) + assert(newVersion("1.0.6") <= newVersion("1.0.6")) + + var inter1 = parseVersionRange(">= 1.0 & <= 1.5") + + assert(not withinRange(newVersion("1.5.1"), inter1)) + assert(withinRange(newVersion("1.2.3.4.5.6.7.8.9.10.11.12"), inter1)) + + assert(newVersion("1") == newVersion("1")) + + echo("Everything works! Assuming that you didn't compile without assertions...")