findFile w/regex, make -j, lib support for getHeader, noexcept / throw
This commit is contained in:
parent
7ef73147a6
commit
182d473973
3 changed files with 153 additions and 64 deletions
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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 (either with cmake or autotools if possible) and then built
|
||||
## using make.
|
||||
## 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 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.} = ""
|
||||
|
||||
when defined(`stdName`):
|
||||
const `path`* = getStdPath(`header`)
|
||||
else:
|
||||
const `path`* =
|
||||
when defined(`gitName`):
|
||||
getGitPath(`header`, `giturl`, `outdir`, `version`)
|
||||
elif defined(`dlName`):
|
||||
getDlPath(`header`, `dlurl`, `outdir`, `version`)
|
||||
const
|
||||
`version`* {.strdefine.} = ""
|
||||
`lname` =
|
||||
when defined(`nameStatic`):
|
||||
`lre` & ".a"
|
||||
else:
|
||||
getLocalPath(`header`, `outdir`)
|
||||
`lre` & getDynlibExt()
|
||||
|
||||
static:
|
||||
doAssert `path`.len != 0, "\nHeader " & `header` & " not found - " & "missing/empty outdir or -d:$1Std -d:$1Git or -d:$1DL not specified" % `name`
|
||||
when defined(`nameStd`):
|
||||
const
|
||||
`path`* = getStdPath(`header`)
|
||||
`lpath`* = getStdLibPath(`lname`)
|
||||
else:
|
||||
const
|
||||
`path`* =
|
||||
when defined(`nameGit`):
|
||||
getGitPath(`header`, `giturl`, `outdir`, `version`)
|
||||
elif defined(`nameDL`):
|
||||
getDlPath(`header`, `dlurl`, `outdir`, `version`)
|
||||
else:
|
||||
getLocalPath(`header`, `outdir`)
|
||||
|
||||
buildLibrary(`outdir`, `conFlags`, `conStaticLib`, `conDynLib`, `cmakeFlags`, `cmakeStaticLib`, `cmakeDynLib`, `makeFlags`)
|
||||
`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`
|
||||
doAssert `lpath`.len != 0, "\nLibrary " & `lname` & " not found"
|
||||
echo "# Including library " & `lpath`
|
||||
|
||||
when defined(`nameStatic`):
|
||||
{.passL: `lpath`.}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ proc initGrammar(): Grammar =
|
|||
(type_identifier)
|
||||
)
|
||||
{paramListGrammar}
|
||||
(noexcept|throw_specifier?)
|
||||
)
|
||||
"""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue