nimble/src/nimblepkg/tools.nim

132 lines
4.2 KiB
Nim

# Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info.
#
# Various miscellaneous utility functions reside here.
import osproc, pegs, strutils, os, uri, sets, json, parseutils
import version, common, cli
proc extractBin(cmd: string): string =
if cmd[0] == '"':
return cmd.captureBetween('"')
else:
return cmd.split(' ')[0]
proc doCmd*(cmd: string, showOutput = false) =
let bin = extractBin(cmd)
if findExe(bin) == "":
raise newException(NimbleError, "'" & bin & "' not in PATH.")
# To keep output in sequence
stdout.flushFile()
stderr.flushFile()
displayDebug("Executing", cmd)
let (output, exitCode) = execCmdEx(cmd)
displayDebug("Finished", "with exit code " & $exitCode)
# TODO: Improve to show output in real-time.
if showOutput:
display("Output:", output, priority = HighPriority)
else:
displayDebug("Output", output)
if exitCode != QuitSuccess:
raise newException(NimbleError,
"Execution failed with exit code $1\nCommand: $2\nOutput: $3" %
[$exitCode, cmd, output])
proc doCmdEx*(cmd: string): tuple[output: TaintedString, exitCode: int] =
let bin = extractBin(cmd)
if findExe(bin) == "":
raise newException(NimbleError, "'" & bin & "' not in PATH.")
return execCmdEx(cmd)
template cd*(dir: string, body: untyped) =
## Sets the current dir to ``dir``, executes ``body`` and restores the
## previous working dir.
let lastDir = getCurrentDir()
setCurrentDir(dir)
body
setCurrentDir(lastDir)
proc getNimBin*: string =
result = "nim"
if findExe("nim") != "": result = findExe("nim")
elif findExe("nimrod") != "": result = findExe("nimrod")
proc getNimrodVersion*: Version =
let nimBin = getNimBin()
let vOutput = doCmdEx('"' & nimBin & "\" -v").output
var matches: array[0..MaxSubpatterns, string]
if vOutput.find(peg"'Version'\s{(\d+\.)+\d}", matches) == -1:
raise newException(NimbleError, "Couldn't find Nim version.")
newVersion(matches[0])
proc samePaths*(p1, p2: string): bool =
## Normalizes path (by adding a trailing slash) and compares.
var cp1 = if not p1.endsWith("/"): p1 & "/" else: p1
var cp2 = if not p2.endsWith("/"): p2 & "/" else: p2
cp1 = cp1.replace('/', DirSep).replace('\\', DirSep)
cp2 = cp2.replace('/', DirSep).replace('\\', DirSep)
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 .. path.len-1]
else:
raise newException(ValueError,
"Cannot change root of path: Path does not begin with original root.")
proc copyFileD*(fro, to: string): string =
## Returns the destination (``to``).
display("Copying", "file $# to $#" % [fro, to], priority = LowPriority)
copyFileWithPermissions(fro, to)
result = to
proc copyDirD*(fro, to: string): seq[string] =
## Returns the filenames of the files in the directory that were copied.
result = @[]
display("Copying", "directory $# to $#" % [fro, to], priority = LowPriority)
for path in walkDirRec(fro):
createDir(changeRoot(fro, to, path.splitFile.dir))
result.add copyFileD(path, changeRoot(fro, to, path))
proc getDownloadDirName*(uri: string, verRange: VersionRange): string =
## Creates a directory name based on the specified ``uri`` (url)
result = ""
let puri = parseUri(uri)
for i in puri.hostname:
case i
of strutils.Letters, strutils.Digits:
result.add i
else: discard
result.add "_"
for i in puri.path:
case i
of strutils.Letters, strutils.Digits:
result.add i
else: discard
let verSimple = getSimpleString(verRange)
if verSimple != "":
result.add "_"
result.add verSimple
proc incl*(s: var HashSet[string], v: seq[string] | HashSet[string]) =
for i in v:
s.incl i
when not declared(json.contains):
proc contains*(j: JsonNode, elem: JsonNode): bool =
for i in j:
if i == elem:
return true
proc contains*(j: JsonNode, elem: tuple[key: string, val: JsonNode]): bool =
for key, val in pairs(j):
if key == elem.key and val == elem.val:
return true