aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGanesh Viswanathan <dev@genotrance.com>2018-07-12 01:36:15 -0500
committerGanesh Viswanathan <dev@genotrance.com>2018-07-12 01:36:15 -0500
commitcf629494a40fa569ab4316b417dfb27856ea2034 (patch)
tree798ee0237d0035a1dae2f30f98cd7e5436126a2c /src
parent293e8cca850be17b1a27c47daedfbbc322c2f2d5 (diff)
downloadnimgen-cf629494a40fa569ab4316b417dfb27856ea2034.tar.gz
nimgen-cf629494a40fa569ab4316b417dfb27856ea2034.zip
Split into multiple files
Diffstat (limited to 'src')
-rw-r--r--src/nimgen.nim7
-rw-r--r--src/nimgen/config.nim229
-rw-r--r--src/nimgen/file.nim100
-rw-r--r--src/nimgen/fileops.nim110
-rw-r--r--src/nimgen/gencore.nim295
-rw-r--r--src/nimgen/globals.nim40
-rw-r--r--src/nimgen/prepare.nim117
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]