diff options
| author | Ganesh Viswanathan <dev@genotrance.com> | 2018-07-12 01:36:15 -0500 |
|---|---|---|
| committer | Ganesh Viswanathan <dev@genotrance.com> | 2018-07-12 01:36:15 -0500 |
| commit | cf629494a40fa569ab4316b417dfb27856ea2034 (patch) | |
| tree | 798ee0237d0035a1dae2f30f98cd7e5436126a2c /src | |
| parent | 293e8cca850be17b1a27c47daedfbbc322c2f2d5 (diff) | |
| download | nimgen-cf629494a40fa569ab4316b417dfb27856ea2034.tar.gz nimgen-cf629494a40fa569ab4316b417dfb27856ea2034.zip | |
Split into multiple files
Diffstat (limited to 'src')
| -rw-r--r-- | src/nimgen.nim | 7 | ||||
| -rw-r--r-- | src/nimgen/config.nim | 229 | ||||
| -rw-r--r-- | src/nimgen/file.nim | 100 | ||||
| -rw-r--r-- | src/nimgen/fileops.nim | 110 | ||||
| -rw-r--r-- | src/nimgen/gencore.nim | 295 | ||||
| -rw-r--r-- | src/nimgen/globals.nim | 40 | ||||
| -rw-r--r-- | src/nimgen/prepare.nim | 117 |
7 files changed, 898 insertions, 0 deletions
diff --git a/src/nimgen.nim b/src/nimgen.nim new file mode 100644 index 0000000..44ee88b --- /dev/null +++ b/src/nimgen.nim @@ -0,0 +1,7 @@ +import os + +import nimgen/config + +for i in commandLineParams(): + if i != "-f": + runCfg(i) diff --git a/src/nimgen/config.nim b/src/nimgen/config.nim new file mode 100644 index 0000000..3a0f3b0 --- /dev/null +++ b/src/nimgen/config.nim @@ -0,0 +1,229 @@ +import os, parsecfg, regex, strutils, tables + +import file, fileops, gencore, globals, prepare + +proc `[]`*(table: OrderedTableRef[string, string], key: string): string = + ## Gets table values with env vars inserted + tables.`[]`(table, key).addEnv + +proc getKey(ukey: string): tuple[key: string, val: bool] = + var kv = ukey.replace(re"\..*", "").split("-", 1) + if kv.len() == 1: + kv.add("") + + if (kv[1] == "") or + (kv[1] == "win" and defined(Windows)) or + (kv[1] == "lin" and defined(Linux)) or + (kv[1] == "osx" and defined(MacOSX)): + return (kv[0], true) + + return (kv[0], false) + +proc runFile*(file: string, cfgin: OrderedTableRef) = + var + cfg = cfgin + sfile = search(file) + + for pattern in gWildcards.keys(): + var match: RegexMatch + let pat = pattern.replace(".", "\\.").replace("*", ".*").replace("?", ".?") + if file.find(toPattern(pat), match): + echo "Appending " & file & " " & pattern + for key in gWildcards[pattern].keys(): + cfg[key & "." & pattern] = gWildcards[pattern][key] + + var + srch = "" + + c2nimConfig = c2nimConfigObj( + flags: "--stdcall", ppflags: "", + recurse: false, inline: false, preprocess: false, ctags: false, defines: false, + dynlib: @[], compile: @[], pragma: @[] + ) + + for act in cfg.keys(): + let (action, val) = getKey(act) + if val == true: + if action == "create": + createDir(file.splitPath().head) + writeFile(file, cfg[act]) + elif action in @["prepend", "append", "replace", "comment", + "rename", "compile", "dynlib", "pragma", + "pipe"] and sfile != "": + if action == "prepend": + if srch != "": + prepend(sfile, cfg[act], cfg[srch]) + else: + prepend(sfile, cfg[act]) + elif action == "append": + if srch != "": + append(sfile, cfg[act], cfg[srch]) + else: + append(sfile, cfg[act]) + elif action == "replace": + if srch != "": + freplace(sfile, cfg[srch], cfg[act]) + elif action == "comment": + if srch != "": + comment(sfile, cfg[srch], cfg[act]) + elif action == "rename": + rename(sfile, cfg[act]) + elif action == "compile": + c2nimConfig.compile.add(cfg[act]) + elif action == "dynlib": + c2nimConfig.dynlib.add(cfg[act]) + elif action == "pragma": + c2nimConfig.pragma.add(cfg[act]) + elif action == "pipe": + pipe(sfile, cfg[act]) + srch = "" + elif action == "search": + srch = act + + if file.splitFile().ext != ".nim": + var noprocess = false + + for act in cfg.keys(): + let (action, val) = getKey(act) + if val == true: + if cfg[act] == "true": + if action == "recurse": + c2nimConfig.recurse = true + elif action == "inline": + c2nimConfig.inline = true + elif action == "preprocess": + c2nimConfig.preprocess = true + elif action == "ctags": + c2nimConfig.ctags = true + elif action == "defines": + c2nimConfig.defines = true + elif action == "noprocess": + noprocess = true + elif action == "flags": + c2nimConfig.flags = cfg[act] + elif action == "ppflags": + c2nimConfig.ppflags = cfg[act] + + if c2nimConfig.recurse and c2nimConfig.inline: + echo "Cannot use recurse and inline simultaneously" + quit(1) + + if not noprocess: + var incls = c2nim(file, getNimout(sfile), c2nimConfig) + if c2nimConfig.recurse and incls.len() != 0: + var + cfg = newOrderedTable[string, string]() + + for name, value in c2nimConfig.fieldPairs: + when value is string: + cfg[name] = value + when value is bool: + cfg[name] = $value + + for i in c2nimConfig.dynlib: + cfg["dynlib." & i] = i + + for inc in incls: + runFile(inc, cfg) + +proc runCfg*(cfg: string) = + if not fileExists(cfg): + echo "Config doesn't exist: " & cfg + quit(1) + + gProjectDir = parentDir(cfg.expandFilename()) + + gConfig = loadConfig(cfg) + + if gConfig.hasKey("n.global"): + if gConfig["n.global"].hasKey("output"): + gOutput = gConfig["n.global"]["output"] + if dirExists(gOutput): + if "-f" in commandLineParams(): + try: + removeDir(gOutput) + except OSError: + echo "Directory in use: " & gOutput + quit(1) + else: + for f in walkFiles(gOutput/"*.nim"): + try: + removeFile(f) + except OSError: + echo "Unable to delete: " & f + quit(1) + createDir(gOutput) + + if gConfig["n.global"].hasKey("cpp_compiler"): + gCppCompiler = gConfig["n.global"]["cpp_compiler"] + else: + # Reset on a per project basis + gCppCompiler = getEnv(cppCompilerEnv, defaultCppCompiler) + + if gConfig["n.global"].hasKey("c_compiler"): + gCCompiler = gConfig["n.global"]["c_compiler"] + else: + # Reset on a per project basis + gCCompiler = getEnv(cCompilerEnv, defaultCCompiler) + + if gConfig["n.global"].hasKey("filter"): + gFilter = gConfig["n.global"]["filter"] + if gConfig["n.global"].hasKey("quotes"): + if gConfig["n.global"]["quotes"] == "false": + gQuotes = false + + if gConfig.hasKey("n.include"): + for inc in gConfig["n.include"].keys(): + gIncludes.add(inc.addEnv()) + + if gConfig.hasKey("n.exclude"): + for excl in gConfig["n.exclude"].keys(): + gExcludes.add(excl.addEnv()) + + if gConfig.hasKey("n.prepare"): + for prep in gConfig["n.prepare"].keys(): + let (key, val) = getKey(prep) + if val == true: + let prepVal = gConfig["n.prepare"][prep] + if key == "download": + downloadUrl(prepVal) + elif key == "extract": + extractZip(prepVal) + elif key == "git": + gitRemotePull(prepVal) + elif key == "gitremote": + gitRemotePull(prepVal, false) + elif key == "gitsparse": + gitSparseCheckout(prepVal) + elif key == "execute": + discard execProc(prepVal) + elif key == "copy": + doCopy(prepVal) + + if gConfig.hasKey("n.wildcard"): + var wildcard = "" + for wild in gConfig["n.wildcard"].keys(): + let (key, val) = getKey(wild) + if val == true: + if key == "wildcard": + wildcard = gConfig["n.wildcard"][wild] + else: + gWildcards.setSectionKey(wildcard, wild, + gConfig["n.wildcard"][wild]) + + for file in gConfig.keys(): + if file in @["n.global", "n.include", "n.exclude", + "n.prepare", "n.wildcard", "n.post"]: + continue + + runFile(file, gConfig[file]) + + if gConfig.hasKey("n.post"): + for post in gConfig["n.post"].keys(): + let (key, val) = getKey(post) + if val == true: + let postVal = gConfig["n.post"][post] + if key == "reset": + gitReset() + elif key == "execute": + discard execProc(postVal) diff --git a/src/nimgen/file.nim b/src/nimgen/file.nim new file mode 100644 index 0000000..27296d2 --- /dev/null +++ b/src/nimgen/file.nim @@ -0,0 +1,100 @@ +import os, ospaths, pegs, regex, strutils, tables + +import globals + +# ### +# File loction + +proc getNimout*(file: string, rename=true): string = + result = file.splitFile().name.replace(re"[\-\.]", "_") & ".nim" + if gOutput != "": + result = gOutput/result + + if not rename: + return + + if gRenames.hasKey(file): + result = gRenames[file] + + if not dirExists(parentDir(result)): + createDir(parentDir(result)) + +proc exclude*(file: string): bool = + for excl in gExcludes: + if excl in file: + return true + return false + +proc search*(file: string): string = + if exclude(file): + return "" + + result = file + if file.splitFile().ext == ".nim": + result = getNimout(file) + elif not fileExists(result) and not dirExists(result): + var found = false + for inc in gIncludes: + result = inc/file + if fileExists(result) or dirExists(result): + found = true + break + if not found: + echo "File doesn't exist: " & file + quit(1) + + # Only keep relative directory + return result.multiReplace([("\\", $DirSep), ("//", $DirSep), (gProjectDir & $DirSep, "")]) + +# ### +# Loading / unloading + +proc openRetry*(file: string, mode: FileMode = fmRead): File = + while true: + try: + result = open(file, mode) + break + except IOError: + sleep(100) + +template withFile*(file: string, body: untyped): untyped = + if fileExists(file): + var f = openRetry(file) + + var contentOrig = f.readAll() + f.close() + var content {.inject.} = contentOrig + + body + + if content != contentOrig: + f = openRetry(file, fmWrite) + write(f, content) + f.close() + else: + echo "Missing file " & file + +proc rename*(file: string, renfile: string) = + if file.splitFile().ext == ".nim": + return + + var + nimout = getNimout(file, false) + newname = renfile.replace("$nimout", extractFilename(nimout)) + + if newname =~ peg"(!\$.)*{'$replace'\s*'('\s*{(!\)\S)+}')'}": + var final = nimout.extractFilename() + for entry in matches[1].split(","): + let spl = entry.split("=") + if spl.len() != 2: + echo "Bad replace syntax: " & renfile + quit(1) + + let + srch = spl[0].strip() + repl = spl[1].strip() + + final = final.replace(srch, repl) + newname = newname.replace(matches[0], final) + + gRenames[file] = gOutput/newname diff --git a/src/nimgen/fileops.nim b/src/nimgen/fileops.nim new file mode 100644 index 0000000..d7ecd62 --- /dev/null +++ b/src/nimgen/fileops.nim @@ -0,0 +1,110 @@ +import os, regex, strutils + +import file, prepare + +# ### +# Manipulating content + +proc prepend*(file: string, data: string, search="") = + withFile(file): + if search == "": + content = data & content + else: + let idx = content.find(search) + if idx != -1: + content = content[0..<idx] & data & content[idx..<content.len()] + +proc pipe*(file: string, command: string) = + let cmd = command % ["file", file] + let commandResult = execProc(cmd).strip() + if commandResult != "": + withFile(file): + content = commandResult + +proc append*(file: string, data: string, search="") = + withFile(file): + if search == "": + content &= data + else: + let idx = content.find(search) + let idy = idx + search.len() + if idx != -1: + content = content[0..<idy] & data & content[idy..<content.len()] + +proc freplace*(file: string, pattern: string, repl="") = + withFile(file): + if pattern in content: + content = content.replace(pattern, repl) + +proc freplace*(file: string, pattern: Regex, repl="") = + withFile(file): + var m: RegexMatch + if content.find(pattern, m): + if "$#" in repl: + content = content.replace(pattern, + proc (m: RegexMatch, s: string): string = repl % s[m.group(0)[0]]) + else: + content = content.replace(pattern, repl) + +proc comment*(file: string, pattern: string, numlines: string) = + let + ext = file.splitFile().ext.toLowerAscii() + cmtchar = if ext == ".nim": "#" else: "//" + + withFile(file): + var + idx = content.find(pattern) + num = 0 + + try: + num = numlines.parseInt() + except ValueError: + echo "Bad comment value, should be integer: " & numlines + if idx != -1: + for i in 0 .. num-1: + if idx >= content.len(): + break + content = content[0..<idx] & cmtchar & content[idx..<content.len()] + while idx < content.len(): + idx += 1 + if content[idx] == '\L': + idx += 1 + break + +proc removeStatic*(filename: string) = + ## Replace static function bodies with a semicolon and commented + ## out body + withFile(filename): + content = content.replace( + re"(?m)(static inline.*?\))(\s*\{(\s*?.*?$)*[\n\r]\})", + proc (m: RegexMatch, s: string): string = + let funcDecl = s[m.group(0)[0]] + let body = s[m.group(1)[0]].strip() + result = "" + + result.add("$#;" % [funcDecl]) + result.add(body.replace(re"(?m)^", "//")) + ) + +proc reAddStatic*(filename: string) = + ## Uncomment out the body and remove the semicolon. Undoes + ## removeStatic + withFile(filename): + content = content.replace( + re"(?m)(static inline.*?\));(\/\/\s*\{(\s*?.*?$)*[\n\r]\/\/\})", + proc (m: RegexMatch, s: string): string = + let funcDecl = s[m.group(0)[0]] + let body = s[m.group(1)[0]].strip() + result = "" + + result.add("$# " % [funcDecl]) + result.add(body.replace(re"(?m)^\/\/", "")) + ) + +proc fixFuncProtos*(file: string) = + withFile(file): + content = content.replace(re"(?m)(^.*?)[ ]*\(\*(.*?)\((.*?)\)\)[ \r\n]*\((.*?[\r\n]*.*?)\);", + proc (m: RegexMatch, s: string): string = + (("typedef $# (*type_$#)($#);\n" % [s[m.group(0)[0]], s[m.group(1)[0]], s[m.group(3)[0]]]) & + ("type_$# $#($#);" % [s[m.group(1)[0]], s[m.group(1)[0]], s[m.group(2)[0]]])) + ) diff --git a/src/nimgen/gencore.nim b/src/nimgen/gencore.nim new file mode 100644 index 0000000..59e86fe --- /dev/null +++ b/src/nimgen/gencore.nim @@ -0,0 +1,295 @@ +import os, regex, ropes, sequtils, strutils, tables + +import file, fileops, globals, prepare + +proc addEnv*(str: string): string = + var newStr = str + for pair in envPairs(): + try: + newStr = newStr % [pair.key, pair.value.string] + except ValueError: + # Ignore if there are no values to replace. We + # want to continue anyway + discard + + try: + newStr = newStr % ["output", gOutput] + except ValueError: + # Ignore if there are no values to replace. We + # want to continue anyway + discard + + # if there are still format args, print a warning + if newStr.contains("${"): + echo "WARNING: \"", newStr, "\" still contains an uninterpolated value!" + + return newStr + +proc compile*(dir="", file=""): string = + proc fcompile(file: string): string = + return "{.compile: \"$#\".}" % file.replace("\\", "/") + + var data = "" + if dir != "" and dirExists(dir): + for f in walkFiles(dir / "*.c"): + data &= fcompile(f) & "\n" + + if file != "" and fileExists(file): + data &= fcompile(file) & "\n" + + return data + +proc getIncls*(file: string, inline=false): seq[string] = + result = @[] + if inline and file in gDoneInline: + return + + let curPath = splitFile(expandFileName(file)).dir + withFile(file): + for f in content.findAll(re"(?m)^\s*#\s*include\s+(.*?)$"): + var inc = content[f.group(0)[0]].strip() + if ((gQuotes and inc.contains("\"")) or (gFilter != "" and gFilter in inc)) and (not exclude(inc)): + let addInc = inc.replace(re"""[<>"]""", "").replace(re"\/[\*\/].*$", "").strip() + try: + # Try searching for a local library. expandFilename will throw + # OSError if the file does not exist + let + finc = expandFileName(curPath / addInc) + fname = finc.replace(curPath & $DirSep, "") + + if fname.len() > 0: + # only add if the file is non-empty + result.add(fname.search()) + except OSError: + # If it's a system library + result.add(addInc) + + result = result.deduplicate() + + gDoneInline.add(file) + + if inline: + var sres = newSeq[string]() + for incl in result: + let sincl = search(incl) + if sincl == "": + continue + + sres.add(getIncls(sincl, inline)) + result.add(sres) + + result = result.deduplicate() + +proc getDefines*(file: string, inline=false): string = + result = "" + if inline: + var incls = getIncls(file, inline) + for incl in incls: + let sincl = search(incl) + if sincl != "": + echo "Inlining " & sincl + result &= getDefines(sincl) + withFile(file): + for def in content.findAll(re"(?m)^(\s*#\s*define\s+[\w\d_]+\s+[\d\-.xf]+)(?:\r|//|/*).*?$"): + result &= content[def.group(0)[0]] & "\n" + +proc runPreprocess*(file, ppflags, flags: string, inline: bool): string = + var + pproc = if flags.contains("cpp"): gCppCompiler else: gCCompiler + cmd = "$# -E $# $#" % [pproc, ppflags, file] + + for inc in gIncludes: + cmd &= " -I " & inc + + # Run preprocessor + var data = execProc(cmd) + + # Include content only from file + var + rdata: Rope + start = false + sfile = file.replace("\\", "/") + + if inline: + sfile = sfile.parentDir() + for line in data.splitLines(): + if line.strip() != "": + if line[0] == '#' and not line.contains("#pragma"): + start = false + if sfile in line.replace("\\", "/").replace("//", "/"): + start = true + if not ("\\" in line) and not ("/" in line) and extractFilename(sfile) in line: + start = true + else: + if start: + rdata.add( + line.replace("_Noreturn", "") + .replace("(())", "") + .replace("WINAPI", "") + .replace("__attribute__", "") + .replace("extern \"C\"", "") + .replace(re"\(\([_a-z]+?\)\)", "") + .replace(re"\(\(__format__[\s]*\(__[gnu_]*printf__, [\d]+, [\d]+\)\)\);", ";") & "\n" + ) + return $rdata + +proc runCtags*(file: string): string = + var + cmd = "ctags -o - --fields=+S+K --c-kinds=+p --file-scope=no " & file + fps = execProc(cmd) + fdata = "" + + for line in fps.splitLines(): + var spl = line.split(re"\t") + if spl.len() > 4: + if spl[0] != "main" and spl[3] != "member": + var fn = "" + var match: RegexMatch + if spl[2].find(re"/\^(.*?)\(", match): + fn = spl[2][match.group(0)[0]] + fn &= spl[4].replace("signature:", "") & ";" + fdata &= fn & "\n" + + return fdata + +template relativePath(path: untyped): untyped = + path.multiReplace([(gOutput, ""), ("\\", "/"), ("//", "/")]) + +proc c2nim*(fl, outfile: string, c2nimConfig: c2nimConfigObj): seq[string] = + var + incls: seq[string] = @[] + incout = "" + file = search(fl) + + if file == "": + return + + if file in gDoneRecursive: + return + + echo "Processing $# => $#" % [file, outfile] + gDoneRecursive.add(file) + + # Remove static inline function bodies + removeStatic(file) + + fixFuncProtos(file) + + if c2nimConfig.recurse: + incls = getIncls(file) + for inc in incls: + incout &= "import $#\n" % inc.search().getNimout()[0 .. ^5] + + var cfile = file + if c2nimConfig.preprocess: + cfile = "temp-$#.c" % [outfile.extractFilename()] + writeFile(cfile, runPreprocess(file, c2nimConfig.ppflags, c2nimConfig.flags, c2nimConfig.inline)) + elif c2nimConfig.ctags: + cfile = "temp-$#.c" % [outfile.extractFilename()] + writeFile(cfile, runCtags(file)) + + if c2nimConfig.defines and (c2nimConfig.preprocess or c2nimConfig.ctags): + prepend(cfile, getDefines(file, c2nimConfig.inline)) + + var + extflags = "" + passC = "" + outlib = "" + outpragma = "" + + passC = "import ospaths, strutils\n" + + for inc in gIncludes: + if inc.isAbsolute(): + passC &= ("""{.passC: "-I\"$#\"".}""" % [inc]) & "\n" + else: + passC &= ( + """{.passC: "-I\"" & currentSourcePath().splitPath().head & "$#\"".}""" % + inc.relativePath() + ) & "\n" + + for prag in c2nimConfig.pragma: + outpragma &= "{." & prag & ".}\n" + + let fname = file.splitFile().name.replace(re"[\.\-]", "_") + + if c2nimConfig.dynlib.len() != 0: + let + win = "when defined(Windows):\n" + lin = "when defined(Linux):\n" + osx = "when defined(MacOSX):\n" + + var winlib, linlib, osxlib: string = "" + for dl in c2nimConfig.dynlib: + let + lib = " const dynlib$# = \"$#\"\n" % [fname, dl] + ext = dl.splitFile().ext + + if ext == ".dll": + winlib &= lib + elif ext == ".so": + linlib &= lib + elif ext == ".dylib": + osxlib &= lib + + if winlib != "": + outlib &= win & winlib & "\n" + if linlib != "": + outlib &= lin & linlib & "\n" + if osxlib != "": + outlib &= osx & osxlib & "\n" + + if outlib != "": + extflags &= " --dynlib:dynlib$#" % fname + else: + if file.isAbsolute(): + passC &= "const header$# = \"$#\"\n" % [fname, file] + else: + passC &= "const header$# = currentSourcePath().splitPath().head & \"$#\"\n" % + [fname, file.relativePath()] + extflags = "--header:header$#" % fname + + # Run c2nim on generated file + var cmd = "c2nim $# $# --out:$# $#" % [c2nimConfig.flags, extflags, outfile, cfile] + when defined(windows): + cmd = "cmd /c " & cmd + discard execProc(cmd) + + if c2nimConfig.preprocess or c2nimConfig.ctags: + try: + removeFile(cfile) + except: + discard + + # Import nim modules + if c2nimConfig.recurse: + prepend(outfile, incout) + + # Nim doesn't like {.cdecl.} for type proc() + freplace(outfile, re"(?m)(.*? = proc.*?)\{.cdecl.\}", "$#") + freplace(outfile, " {.cdecl.})", ")") + + # Include {.compile.} directives + for cpl in c2nimConfig.compile: + let fcpl = search(cpl) + if getFileInfo(fcpl).kind == pcFile: + prepend(outfile, compile(file=fcpl)) + else: + prepend(outfile, compile(dir=fcpl)) + + # Add any pragmas + if outpragma != "": + prepend(outfile, outpragma) + + # Add header file and include paths + if passC != "": + prepend(outfile, passC) + + # Add dynamic library + if outlib != "": + prepend(outfile, outlib) + + # Add back static functions for compilation + reAddStatic(file) + + return incls diff --git a/src/nimgen/globals.nim b/src/nimgen/globals.nim new file mode 100644 index 0000000..c371fe2 --- /dev/null +++ b/src/nimgen/globals.nim @@ -0,0 +1,40 @@ +import os, parsecfg, tables + +const + cCompilerEnv* = "CC" + cppCompilerEnv* = "CPP" + defaultCCompiler* = "gcc" + defaultCppCompiler* = "g++" + +var + gDoneRecursive*: seq[string] = @[] + gDoneInline*: seq[string] = @[] + + gProjectDir* = "" + gConfig*: Config + gFilter* = "" + gQuotes* = true + gCppCompiler* = getEnv(cppCompilerEnv, defaultCCompiler) + gCCompiler* = getEnv(cCompilerEnv, defaultCppCompiler) + gOutput* = "" + gIncludes*: seq[string] = @[] + gExcludes*: seq[string] = @[] + gRenames* = initTable[string, string]() + gWildcards* = newConfig() + +type + c2nimConfigObj* = object + flags*, ppflags*: string + recurse*, inline*, preprocess*, ctags*, defines*: bool + dynlib*, compile*, pragma*: seq[string] + +const gDoc* = """ +Nimgen is a helper for c2nim to simpilfy and automate the wrapping of C libraries + +Usage: + nimgen [options] <file.cfg>... + +Options: + -f delete all artifacts and regenerate +""" + diff --git a/src/nimgen/prepare.nim b/src/nimgen/prepare.nim new file mode 100644 index 0000000..a1857a5 --- /dev/null +++ b/src/nimgen/prepare.nim @@ -0,0 +1,117 @@ +import os, osproc, streams, strutils + +import globals + +proc execProc*(cmd: string): string = + result = "" + var + p = startProcess(cmd, options = {poStdErrToStdOut, poUsePath, poEvalCommand}) + + outp = outputStream(p) + line = newStringOfCap(120).TaintedString + + while true: + if outp.readLine(line): + result.add(line) + result.add("\n") + elif not running(p): break + + var x = p.peekExitCode() + if x != 0: + echo "Command failed: " & $x + echo cmd + echo result + quit(1) + +proc extractZip*(zipfile: string) = + var cmd = "unzip -o $#" + if defined(Windows): + cmd = "powershell -nologo -noprofile -command \"& { Add-Type -A " & + "'System.IO.Compression.FileSystem'; " & + "[IO.Compression.ZipFile]::ExtractToDirectory('$#', '.'); }\"" + + setCurrentDir(gOutput) + defer: setCurrentDir(gProjectDir) + + echo "Extracting " & zipfile + discard execProc(cmd % zipfile) + +proc downloadUrl*(url: string) = + let + file = url.extractFilename() + ext = file.splitFile().ext.toLowerAscii() + + var cmd = "curl $# -o $#" + if defined(Windows): + cmd = "powershell wget $# -OutFile $#" + + if not (ext == ".zip" and fileExists(gOutput/file)): + echo "Downloading " & file + discard execProc(cmd % [url, gOutput/file]) + + if ext == ".zip": + extractZip(file) + +proc gitReset*() = + echo "Resetting Git repo" + + setCurrentDir(gOutput) + defer: setCurrentDir(gProjectDir) + + discard execProc("git reset --hard HEAD") + +proc gitCheckout*(filename: string) {.used.} = + echo "Resetting file: $#" % [filename] + + setCurrentDir(gOutput) + defer: setCurrentDir(gProjectDir) + + let adjustedFile = filename.replace(gOutput & $DirSep, "") + + discard execProc("git checkout $#" % [adjustedFile]) + +proc gitRemotePull*(url: string, pull=true) = + if dirExists(gOutput/".git"): + if pull: + gitReset() + return + + setCurrentDir(gOutput) + defer: setCurrentDir(gProjectDir) + + echo "Setting up Git repo" + discard execProc("git init .") + discard execProc("git remote add origin " & url) + + if pull: + echo "Checking out artifacts" + discard execProc("git pull --depth=1 origin master") + +proc gitSparseCheckout*(plist: string) = + let sparsefile = ".git/info/sparse-checkout" + if fileExists(gOutput/sparsefile): + gitReset() + return + + setCurrentDir(gOutput) + defer: setCurrentDir(gProjectDir) + + discard execProc("git config core.sparsecheckout true") + writeFile(sparsefile, plist) + + echo "Checking out artifacts" + discard execProc("git pull --depth=1 origin master") + +proc doCopy*(flist: string) = + for pair in flist.split(","): + let spl = pair.split("=") + if spl.len() != 2: + echo "Bad copy syntax: " & flist + quit(1) + + let + lfile = spl[0].strip() + rfile = spl[1].strip() + + copyFile(lfile, rfile) + echo "Copied $# to $#" % [lfile, rfile] |
