First commit
This commit is contained in:
commit
77fff838cc
5 changed files with 393 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
*~
|
||||||
15
babel.babel
Normal file
15
babel.babel
Normal 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
101
installer.nim
Normal 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
145
parser.nim
Normal 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
131
version.nim
Normal 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...")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue