From 4b761bebd4528d771cfb4b56d5b2e73f34e9a6d5 Mon Sep 17 00:00:00 2001 From: Ganesh Viswanathan Date: Thu, 12 Jul 2018 14:39:24 -0500 Subject: Fix rename bug, additional cleanup --- src/nimgen.nim | 2 +- src/nimgen/c2nim.nim | 130 ++++++++++++++++++++++++++ src/nimgen/config.nim | 229 ---------------------------------------------- src/nimgen/external.nim | 162 +++++++++++++++++++++++++++++++++ src/nimgen/file.nim | 60 +++++++----- src/nimgen/fileops.nim | 2 +- src/nimgen/gencore.nim | 205 +---------------------------------------- src/nimgen/prepare.nim | 117 ------------------------ src/nimgen/runcfg.nim | 237 ++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 570 insertions(+), 574 deletions(-) create mode 100644 src/nimgen/c2nim.nim delete mode 100644 src/nimgen/config.nim create mode 100644 src/nimgen/external.nim delete mode 100644 src/nimgen/prepare.nim create mode 100644 src/nimgen/runcfg.nim (limited to 'src') diff --git a/src/nimgen.nim b/src/nimgen.nim index 44ee88b..5777398 100644 --- a/src/nimgen.nim +++ b/src/nimgen.nim @@ -1,6 +1,6 @@ import os -import nimgen/config +import nimgen/runcfg for i in commandLineParams(): if i != "-f": diff --git a/src/nimgen/c2nim.nim b/src/nimgen/c2nim.nim new file mode 100644 index 0000000..07b15e0 --- /dev/null +++ b/src/nimgen/c2nim.nim @@ -0,0 +1,130 @@ +import os, ospaths, regex, strutils + +import external, file, fileops, gencore, globals + +template relativePath(path: untyped): untyped = + path.multiReplace([(gOutput, ""), ("\\", "/"), ("//", "/")]) + +proc c2nim*(fl, outfile: string, c2nimConfig: c2nimConfigObj) = + var 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) + + 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 + + # 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) diff --git a/src/nimgen/config.nim b/src/nimgen/config.nim deleted file mode 100644 index 3a0f3b0..0000000 --- a/src/nimgen/config.nim +++ /dev/null @@ -1,229 +0,0 @@ -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/external.nim b/src/nimgen/external.nim new file mode 100644 index 0000000..f642cfb --- /dev/null +++ b/src/nimgen/external.nim @@ -0,0 +1,162 @@ +import os, osproc, regex, ropes, 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 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 diff --git a/src/nimgen/file.nim b/src/nimgen/file.nim index 27296d2..1a316bf 100644 --- a/src/nimgen/file.nim +++ b/src/nimgen/file.nim @@ -46,8 +46,33 @@ proc search*(file: string): string = # Only keep relative directory return result.multiReplace([("\\", $DirSep), ("//", $DirSep), (gProjectDir & $DirSep, "")]) +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 + # ### -# Loading / unloading +# Actions proc openRetry*(file: string, mode: FileMode = fmRead): File = while true: @@ -74,27 +99,16 @@ template withFile*(file: string, body: untyped): untyped = 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() +proc doCopy*(flist: string) = + for pair in flist.split(","): + let spl = pair.split("=") + if spl.len() != 2: + echo "Bad copy syntax: " & flist + quit(1) - final = final.replace(srch, repl) - newname = newname.replace(matches[0], final) + let + lfile = spl[0].strip() + rfile = spl[1].strip() - gRenames[file] = gOutput/newname + copyFile(lfile, rfile) + echo "Copied $# to $#" % [lfile, rfile] diff --git a/src/nimgen/fileops.nim b/src/nimgen/fileops.nim index d7ecd62..2e51d09 100644 --- a/src/nimgen/fileops.nim +++ b/src/nimgen/fileops.nim @@ -1,6 +1,6 @@ import os, regex, strutils -import file, prepare +import external, file # ### # Manipulating content diff --git a/src/nimgen/gencore.nim b/src/nimgen/gencore.nim index 59e86fe..f794991 100644 --- a/src/nimgen/gencore.nim +++ b/src/nimgen/gencore.nim @@ -1,6 +1,6 @@ -import os, regex, ropes, sequtils, strutils, tables +import os, regex, sequtils, strutils -import file, fileops, globals, prepare +import file, globals proc addEnv*(str: string): string = var newStr = str @@ -92,204 +92,3 @@ proc getDefines*(file: string, inline=false): string = 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/prepare.nim b/src/nimgen/prepare.nim deleted file mode 100644 index a1857a5..0000000 --- a/src/nimgen/prepare.nim +++ /dev/null @@ -1,117 +0,0 @@ -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] diff --git a/src/nimgen/runcfg.nim b/src/nimgen/runcfg.nim new file mode 100644 index 0000000..96e067e --- /dev/null +++ b/src/nimgen/runcfg.nim @@ -0,0 +1,237 @@ +import os, parsecfg, regex, strutils, tables + +import c2nim, external, file, fileops, gencore, globals + +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: + let outfile = getNimout(sfile) + c2nim(file, outfile, c2nimConfig) + + if c2nimConfig.recurse: + var + cfg = newOrderedTable[string, string]() + incls = getIncls(sfile) + incout = "" + + 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) + incout &= "import $#\n" % inc.search().getNimout()[0 .. ^5] + + if incout.len() != 0: + prepend(outfile, incout) + +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) -- cgit v1.2.3