223 lines
No EOL
6.6 KiB
Nim
223 lines
No EOL
6.6 KiB
Nim
import httpclient, parseopt, os, strutils, osproc
|
|
|
|
import packageinfo
|
|
|
|
type
|
|
TActionType = enum
|
|
ActionNil, ActionUpdate, ActionInstall
|
|
|
|
TAction = object
|
|
case typ: TActionType
|
|
of ActionNil: nil
|
|
of ActionUpdate:
|
|
optionalURL: string # Overrides default package list.
|
|
of ActionInstall:
|
|
optionalName: seq[string] # When this is @[], installs package from current dir.
|
|
|
|
const
|
|
help = """
|
|
Usage: babel COMMAND
|
|
|
|
Commands:
|
|
install Installs a list of packages.
|
|
update Updates package list. A package list URL can be optionally specificed.
|
|
"""
|
|
babelVersion = "0.1.0"
|
|
defaultPackageURL = "https://github.com/nimrod-code/packages/raw/master/packages.json"
|
|
|
|
proc writeHelp() =
|
|
echo(help)
|
|
quit(QuitSuccess)
|
|
|
|
proc writeVersion() =
|
|
echo(babelVersion)
|
|
quit(QuitSuccess)
|
|
|
|
proc parseCmdLine(): TAction =
|
|
result.typ = ActionNil
|
|
for kind, key, val in getOpt():
|
|
case kind
|
|
of cmdArgument:
|
|
if result.typ == ActionNil:
|
|
case key
|
|
of "install":
|
|
result.typ = ActionInstall
|
|
result.optionalName = @[]
|
|
of "update":
|
|
result.typ = ActionUpdate
|
|
result.optionalURL = ""
|
|
else: writeHelp()
|
|
else:
|
|
case result.typ
|
|
of ActionNil:
|
|
assert false
|
|
of ActionInstall:
|
|
result.optionalName.add(key)
|
|
of ActionUpdate:
|
|
result.optionalURL = key
|
|
of cmdLongOption, cmdShortOption:
|
|
case key
|
|
of "help", "h": writeHelp()
|
|
of "version", "v": writeVersion()
|
|
of cmdEnd: assert(false) # cannot happen
|
|
if result.typ == ActionNil:
|
|
writeHelp()
|
|
|
|
proc prompt(question: string): bool =
|
|
echo(question & " [y/N]")
|
|
let yn = stdin.readLine()
|
|
case yn.normalize
|
|
of "y", "yes":
|
|
return true
|
|
of "n", "no":
|
|
return false
|
|
else:
|
|
return false
|
|
|
|
proc update(url: string = defaultPackageURL) =
|
|
echo("Downloading package list from " & url)
|
|
downloadFile(url, getHomeDir() / ".babel" / "packages.json")
|
|
echo("Done.")
|
|
|
|
proc findBabelFile(dir: string): string =
|
|
for kind, path in walkDir(dir):
|
|
if kind == pcFile and path.splitFile.ext == ".babel":
|
|
return path
|
|
return ""
|
|
|
|
proc copyFileD(fro, to: string) =
|
|
echo(fro, " -> ", to)
|
|
copyFile(fro, to)
|
|
|
|
proc getBabelDir: string = return getHomeDir() / ".babel"
|
|
|
|
proc getLibsDir: string = return getBabelDir() / "libs"
|
|
|
|
proc samePaths(p1, p2: string): bool =
|
|
## Normalizes path (by adding a trailing slash) and compares.
|
|
let cp1 = if not p1.endsWith("/"): p1 & "/" else: p1
|
|
let cp2 = if not p2.endsWith("/"): p2 & "/" else: p2
|
|
return cmpPaths(cp1, cp2) == 0
|
|
|
|
proc changeRoot(origRoot, newRoot, path: string): string =
|
|
## origRoot: /home/dom/
|
|
## newRoot: /home/test/
|
|
## path: /home/dom/bar/blah/2/foo.txt
|
|
## Return value -> /home/test/bar/blah/2/foo.txt
|
|
if path.startsWith(origRoot):
|
|
return newRoot / path[origRoot.len .. -1]
|
|
else:
|
|
raise newException(EInvalidValue,
|
|
"Cannot change root of path: Path does not begin with original root.")
|
|
|
|
proc copyFilesRec(origDir, currentDir: string, pkgInfo: TPackageInfo) =
|
|
for kind, file in walkDir(currentDir):
|
|
if kind == pcDir:
|
|
var skip = false
|
|
for ignoreDir in pkgInfo.skipDirs:
|
|
if samePaths(file, origDir / ignoreDir):
|
|
skip = true
|
|
break
|
|
let thisDir = splitPath(file).tail
|
|
assert thisDir != ""
|
|
if thisDir[0] == '.': skip = true
|
|
if thisDir == "nimcache": skip = true
|
|
|
|
if skip: continue
|
|
# Create the dir.
|
|
createDir(changeRoot(origDir, getLibsDir() / pkgInfo.name, file))
|
|
|
|
copyFilesRec(origDir, file, pkgInfo)
|
|
else:
|
|
var skip = false
|
|
if file.splitFile().name[0] == '.': skip = true
|
|
if file.splitFile().ext == "": skip = true
|
|
for ignoreFile in pkgInfo.skipFiles:
|
|
if samePaths(file, origDir / ignoreFile):
|
|
skip = true
|
|
break
|
|
|
|
if not skip:
|
|
copyFileD(file, changeRoot(origDir, getLibsDir() / pkgInfo.name, file))
|
|
|
|
proc installFromDir(dir: string) =
|
|
let babelFile = findBabelFile(dir)
|
|
if babelFile == "":
|
|
quit("Specified directory does not contain a .babel file.", QuitFailure)
|
|
var pkgInfo = readPackageInfo(babelFile)
|
|
|
|
if not existsDir(dir / pkgInfo.name):
|
|
quit("Package modules should be placed in a " & pkgInfo.name & dirSep &
|
|
" directory.", QuitFailure)
|
|
|
|
if not existsDir(getLibsDir() / pkgInfo.name):
|
|
createDir(getLibsDir() / pkgInfo.name)
|
|
else:
|
|
if not prompt("Package already exists. Overwrite?"):
|
|
quit(QuitSuccess)
|
|
removeDir(getLibsDir() / pkgInfo.name)
|
|
createDir(getLibsDir() / pkgInfo.name)
|
|
|
|
# Find main project file.
|
|
let nimFile = dir / pkgInfo.name.addFileExt("nim")
|
|
let nimrodFile = dir / pkgInfo.name.addFileExt("nimrod")
|
|
if existsFile(nimFile) or existsFile(nimrodFile):
|
|
if existsFile(nimFile):
|
|
copyFileD(nimFile, changeRoot(dir, getLibsDir(), nimFile))
|
|
pkgInfo.skipFiles.add(changeRoot(dir, "", nimFile))
|
|
elif existsFile(nimrodFile):
|
|
copyFileD(nimrodFile, changeRoot(dir, getLibsDir(), nimrodFile))
|
|
pkgInfo.skipFiles.add(changeRoot(dir, "", nimrodFile))
|
|
else:
|
|
# TODO: Make this an error? Which can be overriden in .babel file?
|
|
echo("Warning: Could not find main package file.")
|
|
|
|
copyFilesRec(dir / pkgInfo.name, dir / pkgInfo.name, pkgInfo)
|
|
echo(pkgInfo.name & " installed successfully.")
|
|
|
|
proc install(packages: seq[String]) =
|
|
if packages == @[]:
|
|
installFromDir(getCurrentDir())
|
|
else:
|
|
if not existsFile(getBabelDir() / "packages.json"):
|
|
quit("Please run babel update.", QuitFailure)
|
|
for p in packages:
|
|
var pkg: TPackage
|
|
if getPackage(p, getBabelDir() / "packages.json", pkg):
|
|
let downloadDir = (getTempDir() / "babel" / pkg.name)
|
|
case pkg.downloadMethod
|
|
of "git":
|
|
echo("Executing git...")
|
|
removeDir(downloadDir)
|
|
let exitCode = execCmd("git clone " & pkg.url & " " & downloadDir)
|
|
if exitCode != QuitSuccess:
|
|
quit("Execution of git failed.", QuitFailure)
|
|
else: quit("Unknown download method: " & pkg.downloadMethod, QuitFailure)
|
|
installFromDir(downloadDir)
|
|
else:
|
|
quit("Package not found.", QuitFailure)
|
|
|
|
proc doAction(action: TAction) =
|
|
case action.typ
|
|
of ActionUpdate:
|
|
if action.optionalURL != "":
|
|
update(action.optionalURL)
|
|
else:
|
|
update()
|
|
of ActionInstall:
|
|
install(action.optionalName)
|
|
of ActionNil:
|
|
assert false
|
|
|
|
when isMainModule:
|
|
if not existsDir(getHomeDir() / ".babel"):
|
|
createDir(getHomeDir() / ".babel")
|
|
if not existsDir(getHomeDir() / ".babel" / "libs"):
|
|
createDir(getHomeDir() / ".babel" / "libs")
|
|
|
|
parseCmdLine().doAction()
|
|
|
|
|
|
|
|
|
|
|