First commit

This commit is contained in:
Dominik Picheta 2011-01-27 15:13:39 +00:00
commit 77fff838cc
5 changed files with 393 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*~

15
babel.babel Normal file
View file

@ -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"
;

101
installer.nim Normal file
View file

@ -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")

145
parser.nim Normal file
View file

@ -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

131
version.nim Normal file
View file

@ -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...")