aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGanesh Viswanathan <dev@genotrance.com>2019-08-23 16:00:43 -0500
committerGanesh Viswanathan <dev@genotrance.com>2019-10-02 15:30:49 -0500
commit182d473973294585a5348d6fc1d01dd209136a4c (patch)
treedf2099f9b460b4d88b12c6f07adde3e01cc3b622
parent7ef73147a69f224f3df36081afcc7a235cd70b33 (diff)
downloadnimterop-182d473973294585a5348d6fc1d01dd209136a4c.tar.gz
nimterop-182d473973294585a5348d6fc1d01dd209136a4c.zip
findFile w/regex, make -j, lib support for getHeader, noexcept / throw
-rw-r--r--nimterop.nimble6
-rw-r--r--nimterop/build.nim208
-rw-r--r--nimterop/grammar.nim1
3 files changed, 152 insertions, 63 deletions
diff --git a/nimterop.nimble b/nimterop.nimble
index 9910444..1014e66 100644
--- a/nimterop.nimble
+++ b/nimterop.nimble
@@ -36,12 +36,16 @@ proc testAll() =
execCmd "nim cpp -r tests/tnimterop_cpp.nim"
execTest "tests/tpcre.nim"
- ## platform specific tests
+ # platform specific tests
when defined(Windows):
execTest "tests/tmath.nim"
if defined(OSX) or defined(Windows) or not existsEnv("TRAVIS"):
tsoloud() # requires some libraries on linux, need them installed in TRAVIS
+ # getHeader tests
+ withDir("tests"):
+ execCmd("nim e getheader.nims")
+
const htmldocsDir = "build/htmldocs"
when (NimMajor, NimMinor, NimPatch) >= (0, 19, 9):
diff --git a/nimterop/build.nim b/nimterop/build.nim
index 342e6bc..53237c0 100644
--- a/nimterop/build.nim
+++ b/nimterop/build.nim
@@ -1,4 +1,4 @@
-import macros, osproc, sequtils, strformat, strutils
+import macros, osproc, regex, sequtils, strformat, strutils
import os except findExe
@@ -194,6 +194,29 @@ proc gitPull*(url: string, outdir = "", plist = "", checkout = "") =
echo "# Pulling repository"
discard execAction(&"cd {outdirQ} && git pull --depth=1 origin master")
+proc findFile*(file: string|Regex, dir: string, recurse = true, first = false): string =
+ ## Find the file in the specified directory
+ ##
+ ## ``file`` can be a string or a regex object
+ ##
+ ## Turn off recursive search with ``recurse`` and stop on first match with
+ ## ``first``. Without it, the shortest match is returned.
+ when file is Regex:
+ var
+ rm: RegexMatch
+
+ for f in walkDirRec(dir, followFilter = if recurse: {pcDir} else: {}):
+ let
+ fn = f.extractFilename()
+ when file is string:
+ if (result.len == 0 or result.len > f.len) and fn == file:
+ result = f
+ if first: break
+ else:
+ if (result.len == 0 or result.len > f.len) and fn.match(file, rm):
+ result = f
+ if first: break
+
proc configure*(path, check: string, flags = "") =
## Run the GNU `configure` command to generate all Makefiles or other
## build scripts in the specified path
@@ -261,7 +284,7 @@ proc cmake*(path, check, flags: string) =
doAssert (path / check).fileExists(), "# cmake failed"
-proc make*(path, check: string, flags = "") =
+proc make*(path, check: string|Regex, flags = "") =
## Run the `make` command to build all binaries in the specified path
##
## `check` is a file that will be generated by the `make` command.
@@ -272,7 +295,7 @@ proc make*(path, check: string, flags = "") =
##
## If make.exe is missing and mingw32-make.exe is available, it will
## be copied over to make.exe in the same location.
- if (path / check).fileExists():
+ if findFile(check, path).len != 0:
return
echo "# Running make " & flags
@@ -293,14 +316,7 @@ proc make*(path, check: string, flags = "") =
echo execAction(cmd)
- doAssert (path / check).fileExists(), "# make failed"
-
-proc findFile*(file, dir: string): string =
- ## Find the file in the specified directory
- for f in walkDirRec(dir):
- if f.extractFilename() == file:
- if result.len == 0 or result.len > f.len:
- result = f
+ doAssert findFile(check, path).len != 0, "# make failed"
proc getGccPaths*(mode = "c"): seq[string] =
var
@@ -308,7 +324,7 @@ proc getGccPaths*(mode = "c"): seq[string] =
mmode = if mode == "cpp": "c++" else: mode
inc = false
- (outp, ret) = gorgeEx(&"""{getEnv("CC", "gcc")} -Wp,-v -x{mmode} {nul}""")
+ (outp, _) = gorgeEx(&"""{getEnv("CC", "gcc")} -Wp,-v -x{mmode} {nul}""")
for line in outp.splitLines():
if "#include <...> search starts here" in line:
@@ -317,11 +333,38 @@ proc getGccPaths*(mode = "c"): seq[string] =
elif "End of search list" in line:
break
if inc:
- result.add line.strip()
+ var
+ path = line.strip()
+ path.normalizePath()
+ if path notin result:
+ result.add path
+
+proc getGccLibPaths*(mode = "c"): seq[string] =
+ var
+ nul = when defined(Windows): "nul" else: "/dev/null"
+ mmode = if mode == "cpp": "c++" else: mode
+
+ (outp, _) = gorgeEx(&"""{getEnv("CC", "gcc")} -v -x{mmode} {nul}""")
+
+ for line in outp.splitLines():
+ if "LIBRARY_PATH=" in line:
+ for path in line[13 .. ^1].split(PathSep):
+ var
+ path = path.strip()
+ path.normalizePath()
+ if path notin result:
+ result.add path
+ break
proc getStdPath(header: string): string =
for inc in getGccPaths():
- result = findFile(header, inc)
+ result = findFile(header, inc, recurse = false, first = true)
+ if result.len != 0:
+ break
+
+proc getStdLibPath(lname: string): string =
+ for lib in getGccLibPaths():
+ result = findFile(re(lname), lib, recurse = false, first = true)
if result.len != 0:
break
@@ -369,44 +412,49 @@ proc getLocalPath(header, outdir: string): string =
if outdir.len != 0:
result = findFile(header, outdir)
-proc buildLibrary(outdir, conFlags, conStaticLib, conDynLib, cmakeFlags, cmakeStaticLib, cmakeDynLib, makeFlags: string) =
+proc getNumProcs(): string =
+ when defined(windows):
+ getEnv("NUMBER_OF_PROCESSORS").strip()
+ elif defined(linux):
+ execAction("nproc").strip()
+ elif defined(macosx):
+ execAction("sysctl -n hw.ncpu").strip()
+ else:
+ "1"
+
+proc buildLibrary(lname, outdir, conFlags, cmakeFlags, makeFlags: string): string =
var
conDeps = false
conDepStr = ""
cmakeDeps = false
cmakeDepStr = ""
+ lpath = findFile(re(lname), outdir)
+ makeFlagsProc = &"-j {getNumProcs()} {makeFlags}"
+
+ if lpath.len != 0:
+ return lpath
if fileExists(outdir / "CMakeLists.txt"):
if findExe("cmake").len != 0:
- if cmakeStaticLib.len != 0 or cmakeDynLib.len != 0:
- var
- gen = ""
- when defined(windows):
- if findExe("sh").len != 0:
- gen = "MSYS Makefiles"
- else:
- gen = "MinGW Makefiles"
+ var
+ gen = ""
+ when defined(windows):
+ if findExe("sh").len != 0:
+ gen = "MSYS Makefiles"
else:
- gen = "Unix Makefiles"
- cmake(outdir / "build", "Makefile", &".. -G {gen.quoteShell} {cmakeFlags}")
- cmakeDeps = true
- let
- check = if cmakeStaticLib.len != 0: cmakeStaticLib else: cmakeDynLib
- make(outdir / "build", check, makeFlags)
+ gen = "MinGW Makefiles"
else:
- cmakeDepStr &= "cmakeStatibLib / cmakeDynLib not specified"
+ gen = "Unix Makefiles"
+ cmake(outdir / "build", "Makefile", &".. -G {gen.quoteShell} {cmakeFlags}")
+ cmakeDeps = true
+ make(outdir / "build", re(lname), makeFlagsProc)
else:
cmakeDepStr &= "cmake executable missing"
template cfgCommon() {.dirty.} =
- if (conStaticLib.len != 0 or conDynLib.len != 0):
- configure(outdir, "Makefile", conFlags)
- conDeps = true
- let
- check = if conStaticLib.len != 0: conStaticLib else: conDynLib
- make(outdir, check, makeFlags)
- else:
- conDepStr &= "conStaticLib / conDynLib not specified"
+ configure(outdir, "Makefile", conFlags)
+ conDeps = true
+ make(outdir, re(lname), makeFlagsProc)
if not cmakeDeps:
if not fileExists(outdir / "configure"):
@@ -437,53 +485,89 @@ proc buildLibrary(outdir, conFlags, conStaticLib, conDynLib, cmakeFlags, cmakeSt
error = "No build files found in " & outdir
doAssert cmakeDeps or conDeps, &"\n# Build configuration failed - {error}\n"
+ result = findFile(re(lname), outdir)
+
+proc getDynlibExt(): string =
+ when defined(windows):
+ result = ".dll"
+ elif defined(linux):
+ result = ".so[0-9.]*"
+ elif defined(macosx):
+ result = ".dylib[0-9.]*"
+
macro getHeader*(header: static[string], giturl: static[string] = "", dlurl: static[string] = "", outdir: static[string] = "",
- conFlags: static[string] = "", conStaticLib: static[string] = "", conDynLib: static[string] = "",
- cmakeFlags: static[string] = "", cmakeStaticLib: static[string] = "", cmakeDynLib: static[string] = "",
- makeFlags: static[string] = ""): untyped =
+ conFlags: static[string] = "", cmakeFlags: static[string] = "", makeFlags: static[string] = ""): untyped =
## Get the path to a header file for wrapping with
## `cImport() <cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring>`_ or
## `c2nImport() <cimport.html#c2nImport.m%2C%2Cstring%2Cstring%2Cstring>`_.
##
- ## Checks defines based on the header name (e.g. lzma from lzma.h), to use different
- ## ways to obtain the source.
+ ## This proc checks -d:xxx defines based on the header name (e.g. lzma from lzma.h),
+ ## and accordingly employs different ways to obtain the source.
##
## ``-d:xxxStd`` - search standard system paths. E.g. ``/usr/include`` and ``/usr/lib`` on Linux
## ``-d:xxxGit`` - clone source from a git repo specified in ``giturl``
## ``-d:xxxDL`` - download source from ``dlurl`` and extract if required
##
## This allows a single wrapper to be used in different ways depending on the user's needs.
- ## If no defines are specified, ``outdir`` will be searched for the header.
+ ## If no -d:xxx defines are specified, ``outdir`` will be searched for the header.
+ ##
+ ## The library is then configured (with cmake or autotools if possible) and built
+ ## using make, unless using ``-d:xxxStd`` which presumes that the system package
+ ## manager was used to install prebuilt headers and binaries.
##
- ## The library is then configured (either with cmake or autotools if possible) and then built
- ## using make.
+ ## The header path is stored in ``const xxxPath`` and can be used in a ``cImport()`` call
+ ## in the calling wrapper. The dynamic library path is stored in ``const xxxLPath`` and can
+ ## be used for the ``dynlib`` parameter (within quotes).
+ ##
+ ## ``-d:xxxStatic`` can be specified to statically link with the library instead. This
+ ## will automatically add a ``{.passL.}`` call to the static library for convenience.
var
name = header.split(".")[0]
- stdName = newIdentNode(name & "Std")
- gitName = newIdentNode(name & "Git")
- dlName = newIdentNode(name & "DL")
+ nameStd = newIdentNode(name & "Std")
+ nameGit = newIdentNode(name & "Git")
+ nameDL = newIdentNode(name & "DL")
+
+ nameStatic = newIdentNode(name & "Static")
path = newIdentNode(name & "Path")
+ lpath = newIdentNode(name & "LPath")
version = newIdentNode(name & "Version")
+ lname = newIdentNode(name & "LName")
+
+ lre = "(lib)?$1[0-9.\\-]*\\" % name
result = newNimNode(nnkStmtList)
result.add(quote do:
- const `version`* {.strdefine.} = ""
+ const
+ `version`* {.strdefine.} = ""
+ `lname` =
+ when defined(`nameStatic`):
+ `lre` & ".a"
+ else:
+ `lre` & getDynlibExt()
- when defined(`stdName`):
- const `path`* = getStdPath(`header`)
+ when defined(`nameStd`):
+ const
+ `path`* = getStdPath(`header`)
+ `lpath`* = getStdLibPath(`lname`)
else:
- const `path`* =
- when defined(`gitName`):
- getGitPath(`header`, `giturl`, `outdir`, `version`)
- elif defined(`dlName`):
- getDlPath(`header`, `dlurl`, `outdir`, `version`)
- else:
- getLocalPath(`header`, `outdir`)
+ const
+ `path`* =
+ when defined(`nameGit`):
+ getGitPath(`header`, `giturl`, `outdir`, `version`)
+ elif defined(`nameDL`):
+ getDlPath(`header`, `dlurl`, `outdir`, `version`)
+ else:
+ getLocalPath(`header`, `outdir`)
+
+ `lpath`* = buildLibrary(`lname`, `outdir`, `conFlags`, `cmakeFlags`, `makeFlags`)
- static:
- doAssert `path`.len != 0, "\nHeader " & `header` & " not found - " & "missing/empty outdir or -d:$1Std -d:$1Git or -d:$1DL not specified" % `name`
+ static:
+ doAssert `path`.len != 0, "\nHeader " & `header` & " not found - " & "missing/empty outdir or -d:$1Std -d:$1Git or -d:$1DL not specified" % `name`
+ doAssert `lpath`.len != 0, "\nLibrary " & `lname` & " not found"
+ echo "# Including library " & `lpath`
- buildLibrary(`outdir`, `conFlags`, `conStaticLib`, `conDynLib`, `cmakeFlags`, `cmakeStaticLib`, `cmakeDynLib`, `makeFlags`)
+ when defined(`nameStatic`):
+ {.passL: `lpath`.}
)
diff --git a/nimterop/grammar.nim b/nimterop/grammar.nim
index a66442f..6e2db16 100644
--- a/nimterop/grammar.nim
+++ b/nimterop/grammar.nim
@@ -73,6 +73,7 @@ proc initGrammar(): Grammar =
(type_identifier)
)
{paramListGrammar}
+ (noexcept|throw_specifier?)
)
"""