aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGanesh Viswanathan <dev@genotrance.com>2018-07-11 22:59:16 -0500
committerGanesh Viswanathan <dev@genotrance.com>2018-07-11 22:59:16 -0500
commit2766607277b3d86935f082d6f9afb016c302b661 (patch)
tree66cf875e4940d12d24d5b2664b0db0f1b52d1551
parente32acea78e3a1c30e73059cd131129ea8706b4f4 (diff)
parent6aca40808b7afadb9051d3bffa173ee4ffbfcdd0 (diff)
downloadnimgen-2766607277b3d86935f082d6f9afb016c302b661.tar.gz
nimgen-2766607277b3d86935f082d6f9afb016c302b661.zip
Merge branch 'master' of https://github.com/genotrance/nimgen into regex-1
-rw-r--r--README.md23
-rw-r--r--nimgen.nim158
2 files changed, 164 insertions, 17 deletions
diff --git a/README.md b/README.md
index 1e95d0a..6bc1cd1 100644
--- a/README.md
+++ b/README.md
@@ -65,6 +65,17 @@ Nimgen only supports the ```gcc``` preprocessor at this time. Support for detect
__Config file__
+In all sections below, environment variables are supported via Nim's string interpolation `%` symbol imported from the `strutils` module. Simply use double quotes to enclose any value and put `$` or `${}` around the environment variable name. In addition, the `output` var from the n.global section is available as ${output}. For example:
+
+ [n.global]
+ c_compiler="$CC"
+ cpp_compiler="${CPP}-arm"
+ output="src/path"
+
+ [n.include]
+ "${output}/library/include"
+ "${MY_INCLUDE_PATH}/include"
+
_[n.global]_
```output``` = name of the Nimble project once installed, also location to place generated .nim files
@@ -73,6 +84,10 @@ _[n.global]_
```filter``` = string to identify and recurse into library .h files in #include statements and exclude standard headers
+```cpp_compiler``` = string to specify a CPP compiler executable. [default: g++]
+
+```c_compiler``` = string to specify a C compiler executable. [default: gcc]
+
_[n.include]_
List of all directories, one per line, to include in the search path. This is used by:-
@@ -103,6 +118,14 @@ The following keys can be used to prepare dependencies such as downloading ZIP f
```copy``` = copy a file to another location. Preferred over moving to preserve original. Comma separate for multiple entries. E.g. copy = "output/config.h.in=output/config.h"
+_[n.post]_
+
+This section is the same as the prepare section, but for performing actions after the project has been processed.
+
+```reset``` = whether or not to perform a git reset on all files after processing [default: false]
+
+```execute``` = command to run after processing
+
_[n.wildcard]_
File wildcards such as *.nim, ssl*.h, etc. can be used to perform tasks across a group of files. This is useful to define common operations such as global text replacements without having to specify an explicit section for every single file. These operations will be performed on every matching file that is defined as a _sourcefile_ or recursed files. Only applies on source files following the wildcard declarations.
diff --git a/nimgen.nim b/nimgen.nim
index 12380d5..4e540d8 100644
--- a/nimgen.nim
+++ b/nimgen.nim
@@ -1,5 +1,11 @@
import os, ospaths, osproc, parsecfg, pegs, regex, ropes, sequtils, streams, strutils, tables
+const
+ cCompilerEnv = "CC"
+ cppCompilerEnv = "CPP"
+ defaultCCompiler = "gcc"
+ defaultCppCompiler = "g++"
+
var
gDoneRecursive: seq[string] = @[]
gDoneInline: seq[string] = @[]
@@ -8,6 +14,8 @@ var
gConfig: Config
gFilter = ""
gQuotes = true
+ gCppCompiler = getEnv(cppCompilerEnv, defaultCCompiler)
+ gCCompiler = getEnv(cCompilerEnv, defaultCppCompiler)
gOutput = ""
gIncludes: seq[string] = @[]
gExcludes: seq[string] = @[]
@@ -33,6 +41,33 @@ Options:
# ###
# Helpers
+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 `[]`(table: OrderedTableRef[string, string], key: string): string =
+ ## Gets table values with env vars inserted
+ tables.`[]`(table, key).addEnv
+
proc execProc(cmd: string): string =
result = ""
var
@@ -57,7 +92,9 @@ proc execProc(cmd: string): string =
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('$#', '.'); }\""
+ cmd = "powershell -nologo -noprofile -command \"& { Add-Type -A " &
+ "'System.IO.Compression.FileSystem'; " &
+ "[IO.Compression.ZipFile]::ExtractToDirectory('$#', '.'); }\""
setCurrentDir(gOutput)
defer: setCurrentDir(gProjectDir)
@@ -89,6 +126,16 @@ proc gitReset() =
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:
@@ -189,7 +236,8 @@ proc search(file: string): string =
echo "File doesn't exist: " & file
quit(1)
- return result.replace(re"[\\/]", $DirSep)
+ # Only keep relative directory
+ return result.multiReplace([("\\", $DirSep), ("//", $DirSep), (gProjectDir & $DirSep, "")])
# ###
# Loading / unloading
@@ -288,6 +336,36 @@ proc comment(file: string, pattern: string, numlines: string) =
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 (match: RegexMatch): string =
+ let funcDecl = match.captures[0]
+ let body = match.captures[1].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 (match: RegexMatch): string =
+ let funcDecl = match.captures[0]
+ let body = match.captures[1].strip()
+ result = ""
+
+ result.add("$# " % [funcDecl])
+ result.add(body.replace(re"(?m)^\/\/", ""))
+ )
+
proc rename(file: string, renfile: string) =
if file.splitFile().ext == ".nim":
return
@@ -343,12 +421,25 @@ proc getIncls(file: string, inline=false): seq[string] =
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)):
- result.add(
- inc.replace(re"""[<>"]""", "").replace(re"\/[\*\/].*$", "").strip())
+ 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()
@@ -381,7 +472,7 @@ proc getDefines(file: string, inline=false): string =
proc runPreprocess(file, ppflags, flags: string, inline: bool): string =
var
- pproc = if flags.contains("cpp"): "g++" else: "gcc"
+ pproc = if flags.contains("cpp"): gCppCompiler else: gCCompiler
cmd = "$# -E $# $#" % [pproc, ppflags, file]
for inc in gIncludes:
@@ -615,7 +706,9 @@ proc runFile(file: string, cfgin: OrderedTableRef) =
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 != "":
+ 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])
@@ -674,9 +767,15 @@ proc runFile(file: string, cfgin: OrderedTableRef) =
echo "Cannot use recurse and inline simultaneously"
quit(1)
+ # Remove static inline function bodies
+ removeStatic(sfile)
+
if not noprocess:
c2nim(file, getNimout(sfile), c2nimConfig)
+ # Add them back for compilation
+ reAddStatic(sfile)
+
proc runCfg(cfg: string) =
if not fileExists(cfg):
echo "Config doesn't exist: " & cfg
@@ -705,6 +804,18 @@ proc runCfg(cfg: string) =
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"):
@@ -713,30 +824,31 @@ proc runCfg(cfg: string) =
if gConfig.hasKey("n.include"):
for inc in gConfig["n.include"].keys():
- gIncludes.add(inc)
+ gIncludes.add(inc.addEnv())
if gConfig.hasKey("n.exclude"):
for excl in gConfig["n.exclude"].keys():
- gExcludes.add(excl)
+ 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(gConfig["n.prepare"][prep])
+ downloadUrl(prepVal)
elif key == "extract":
- extractZip(gConfig["n.prepare"][prep])
+ extractZip(prepVal)
elif key == "git":
- gitRemotePull(gConfig["n.prepare"][prep])
+ gitRemotePull(prepVal)
elif key == "gitremote":
- gitRemotePull(gConfig["n.prepare"][prep], false)
+ gitRemotePull(prepVal, false)
elif key == "gitsparse":
- gitSparseCheckout(gConfig["n.prepare"][prep])
+ gitSparseCheckout(prepVal)
elif key == "execute":
- discard execProc(gConfig["n.prepare"][prep])
+ discard execProc(prepVal)
elif key == "copy":
- doCopy(gConfig["n.prepare"][prep])
+ doCopy(prepVal)
if gConfig.hasKey("n.wildcard"):
var wildcard = ""
@@ -746,14 +858,26 @@ proc runCfg(cfg: string) =
if key == "wildcard":
wildcard = gConfig["n.wildcard"][wild]
else:
- gWildcards.setSectionKey(wildcard, wild, gConfig["n.wildcard"][wild])
+ 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"]:
+ 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)
+
# ###
# Main loop