diff options
| author | Ganesh Viswanathan <dev@genotrance.com> | 2019-08-23 16:00:43 -0500 |
|---|---|---|
| committer | Ganesh Viswanathan <dev@genotrance.com> | 2019-10-02 15:30:49 -0500 |
| commit | 182d473973294585a5348d6fc1d01dd209136a4c (patch) | |
| tree | df2099f9b460b4d88b12c6f07adde3e01cc3b622 | |
| parent | 7ef73147a69f224f3df36081afcc7a235cd70b33 (diff) | |
| download | nimterop-182d473973294585a5348d6fc1d01dd209136a4c.tar.gz nimterop-182d473973294585a5348d6fc1d01dd209136a4c.zip | |
findFile w/regex, make -j, lib support for getHeader, noexcept / throw
| -rw-r--r-- | nimterop.nimble | 6 | ||||
| -rw-r--r-- | nimterop/build.nim | 208 | ||||
| -rw-r--r-- | nimterop/grammar.nim | 1 |
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?) ) """ |
