getHeader and supporting procs
This commit is contained in:
parent
5b0a5ab146
commit
7ef73147a6
2 changed files with 210 additions and 29 deletions
|
|
@ -1,4 +1,6 @@
|
|||
import os, osproc, strformat, strutils
|
||||
import macros, osproc, sequtils, strformat, strutils
|
||||
|
||||
import os except findExe
|
||||
|
||||
import "."/[compat]
|
||||
|
||||
|
|
@ -292,3 +294,196 @@ 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
|
||||
|
||||
proc getGccPaths*(mode = "c"): seq[string] =
|
||||
var
|
||||
nul = when defined(Windows): "nul" else: "/dev/null"
|
||||
mmode = if mode == "cpp": "c++" else: mode
|
||||
inc = false
|
||||
|
||||
(outp, ret) = gorgeEx(&"""{getEnv("CC", "gcc")} -Wp,-v -x{mmode} {nul}""")
|
||||
|
||||
for line in outp.splitLines():
|
||||
if "#include <...> search starts here" in line:
|
||||
inc = true
|
||||
continue
|
||||
elif "End of search list" in line:
|
||||
break
|
||||
if inc:
|
||||
result.add line.strip()
|
||||
|
||||
proc getStdPath(header: string): string =
|
||||
for inc in getGccPaths():
|
||||
result = findFile(header, inc)
|
||||
if result.len != 0:
|
||||
break
|
||||
|
||||
proc getGitPath(header, url, outdir, version: string): string =
|
||||
doAssert url.len != 0, "No git url setup for " & header
|
||||
doAssert findExe("git").len != 0, "git executable missing"
|
||||
|
||||
gitPull(url, outdir, checkout = version)
|
||||
|
||||
result = findFile(header, outdir)
|
||||
|
||||
proc getDlPath(header, url, outdir, version: string): string =
|
||||
doAssert url.len != 0, "No download url setup for " & header
|
||||
|
||||
var
|
||||
dlurl = url
|
||||
if "$#" in url or "$1" in url:
|
||||
doAssert version.len != 0, "Need version for download url"
|
||||
dlurl = url % version
|
||||
else:
|
||||
doAssert version.len == 0, "Download url does not contain version"
|
||||
|
||||
downloadUrl(dlurl, outdir)
|
||||
|
||||
var
|
||||
dirname = ""
|
||||
for kind, path in walkDir(outdir, relative = true):
|
||||
if kind == pcFile and path != dlurl.extractFilename():
|
||||
dirname = ""
|
||||
break
|
||||
elif kind == pcDir:
|
||||
if dirname.len == 0:
|
||||
dirname = path
|
||||
else:
|
||||
dirname = ""
|
||||
break
|
||||
|
||||
if dirname.len != 0:
|
||||
for kind, path in walkDir(outdir / dirname, relative = true):
|
||||
mvFile(outdir / dirname / path, outdir / path)
|
||||
|
||||
result = findFile(header, outdir)
|
||||
|
||||
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) =
|
||||
var
|
||||
conDeps = false
|
||||
conDepStr = ""
|
||||
cmakeDeps = false
|
||||
cmakeDepStr = ""
|
||||
|
||||
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"
|
||||
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)
|
||||
else:
|
||||
cmakeDepStr &= "cmakeStatibLib / cmakeDynLib not specified"
|
||||
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"
|
||||
|
||||
if not cmakeDeps:
|
||||
if not fileExists(outdir / "configure"):
|
||||
if fileExists(outdir / "autogen.sh") or fileExists(outdir / "build" / "autogen.sh"):
|
||||
if findExe("aclocal").len != 0:
|
||||
if findExe("autoconf").len != 0:
|
||||
if findExe("libtoolize").len != 0:
|
||||
cfgCommon()
|
||||
else:
|
||||
conDepStr &= "libtoolize executable missing"
|
||||
else:
|
||||
conDepStr &= "autoconf executable missing"
|
||||
else:
|
||||
conDepStr &= "aclocal executable missing"
|
||||
else:
|
||||
if findExe("bash").len != 0:
|
||||
cfgCommon()
|
||||
else:
|
||||
conDepStr &= "bash executable missing"
|
||||
|
||||
var
|
||||
error = ""
|
||||
if not cmakeDeps and cmakeDepStr.len != 0:
|
||||
error &= &"cmake capable but {cmakeDepStr}\n"
|
||||
if not conDeps and conDepStr.len != 0:
|
||||
error &= &"configure capable but {conDepStr}\n"
|
||||
if error.len == 0:
|
||||
error = "No build files found in " & outdir
|
||||
doAssert cmakeDeps or conDeps, &"\n# Build configuration failed - {error}\n"
|
||||
|
||||
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 =
|
||||
## 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.
|
||||
##
|
||||
## ``-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.
|
||||
##
|
||||
## The library is then configured (either with cmake or autotools if possible) and then built
|
||||
## using make.
|
||||
var
|
||||
name = header.split(".")[0]
|
||||
|
||||
stdName = newIdentNode(name & "Std")
|
||||
gitName = newIdentNode(name & "Git")
|
||||
dlName = newIdentNode(name & "DL")
|
||||
|
||||
path = newIdentNode(name & "Path")
|
||||
version = newIdentNode(name & "Version")
|
||||
|
||||
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`)
|
||||
else:
|
||||
getLocalPath(`header`, `outdir`)
|
||||
|
||||
static:
|
||||
doAssert `path`.len != 0, "\nHeader " & `header` & " not found - " & "missing/empty outdir or -d:$1Std -d:$1Git or -d:$1DL not specified" % `name`
|
||||
|
||||
buildLibrary(`outdir`, `conFlags`, `conStaticLib`, `conDynLib`, `cmakeFlags`, `cmakeStaticLib`, `cmakeDynLib`, `makeFlags`)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -168,14 +168,6 @@ proc getToast(fullpath: string, recurse: bool = false, dynlib: string = "",
|
|||
(result, ret) = gorgeEx(cmd, cache=getCacheValue(fullpath))
|
||||
doAssert ret == 0, getToastError(result)
|
||||
|
||||
proc getGccPaths(mode = "c"): string =
|
||||
var
|
||||
ret = 0
|
||||
nul = when defined(Windows): "nul" else: "/dev/null"
|
||||
mmode = if mode == "cpp": "c++" else: mode
|
||||
|
||||
(result, ret) = gorgeEx(&"""{getEnv("CC", "gcc")} -Wp,-v -x{mmode} {nul}""")
|
||||
|
||||
macro cOverride*(body): untyped =
|
||||
## When the wrapper code generated by nimterop is missing certain symbols or not
|
||||
## accurate, it may be required to hand wrap them. Define them in a
|
||||
|
|
@ -236,7 +228,8 @@ proc cSkipSymbol*(skips: seq[string]) {.compileTime.} =
|
|||
gStateCT.symOverride.add skips
|
||||
|
||||
macro cPlugin*(body): untyped =
|
||||
## When `cOverride() <cimport.html#cOverride.m>`_ and `cSkipSymbol() <cimport.html#cSkipSymbol%2Cseq[T][string]>`_
|
||||
## When `cOverride() <cimport.html#cOverride.m>`_ and
|
||||
## `cSkipSymbol() <cimport.html#cSkipSymbol%2Cseq[T][string]>`_
|
||||
## are not adequate, the `cPlugin() <cimport.html#cPlugin.m>`_ macro can be used
|
||||
## to customize the generated Nim output. The following callbacks are available at
|
||||
## this time.
|
||||
|
|
@ -322,7 +315,8 @@ proc cDebug*() {.compileTime.} =
|
|||
proc cDisableCaching*() {.compileTime.} =
|
||||
## Disable caching of generated Nim code - useful during wrapper development
|
||||
##
|
||||
## If files included by header being processed by `cImport() <cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring>`_
|
||||
## If files included by header being processed by
|
||||
## `cImport() <cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring>`_
|
||||
## change and affect the generated content, they will be ignored and the cached
|
||||
## value will continue to be used . Use `cDisableCaching() <cimport.html#cDisableCaching>`_
|
||||
## to avoid this scenario during development.
|
||||
|
|
@ -334,8 +328,8 @@ proc cDisableCaching*() {.compileTime.} =
|
|||
macro cDefine*(name: static string, val: static string = ""): untyped =
|
||||
## ``#define`` an identifer that is forwarded to the C/C++ preprocessor if
|
||||
## called within `cImport() <cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring>`_
|
||||
## or `c2nImport() <cimport.html#c2nImport.m%2C%2Cstring%2Cstring%2Cstring>`_ as well as to the
|
||||
## C/C++ compiler during Nim compilation using ``{.passC: "-DXXX".}``
|
||||
## or `c2nImport() <cimport.html#c2nImport.m%2C%2Cstring%2Cstring%2Cstring>`_
|
||||
## as well as to the C/C++ compiler during Nim compilation using ``{.passC: "-DXXX".}``
|
||||
|
||||
result = newNimNode(nnkStmtList)
|
||||
|
||||
|
|
@ -370,8 +364,8 @@ proc cAddSearchDir*(dir: string) {.compileTime.} =
|
|||
macro cIncludeDir*(dir: static string): untyped =
|
||||
## Add an include directory that is forwarded to the C/C++ preprocessor if
|
||||
## called within `cImport() <cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring>`_
|
||||
## or `c2nImport() <cimport.html#c2nImport.m%2C%2Cstring%2Cstring%2Cstring>`_ as well as to the
|
||||
## C/C++ compiler during Nim compilation using ``{.passC: "-IXXX".}``.
|
||||
## or `c2nImport() <cimport.html#c2nImport.m%2C%2Cstring%2Cstring%2Cstring>`_
|
||||
## as well as to the C/C++ compiler during Nim compilation using ``{.passC: "-IXXX".}``.
|
||||
|
||||
var dir = interpPath(dir)
|
||||
result = newNimNode(nnkStmtList)
|
||||
|
|
@ -392,16 +386,8 @@ proc cAddStdDir*(mode = "c") {.compileTime.} =
|
|||
static: cAddStdDir()
|
||||
import os
|
||||
doAssert cSearchPath("math.h").existsFile
|
||||
var
|
||||
inc = false
|
||||
for line in getGccPaths(mode).splitLines():
|
||||
if "#include <...> search starts here" in line:
|
||||
inc = true
|
||||
continue
|
||||
elif "End of search list" in line:
|
||||
break
|
||||
if inc:
|
||||
cAddSearchDir line.strip()
|
||||
for inc in getGccPaths(mode):
|
||||
cAddSearchDir inc
|
||||
|
||||
macro cCompile*(path: static string, mode = "c", exclude = ""): untyped =
|
||||
## Compile and link C/C++ implementation into resulting binary using ``{.compile.}``
|
||||
|
|
@ -528,8 +514,8 @@ macro cImport*(filename: static string, recurse: static bool = false, dynlib: st
|
|||
## cImport("pcre.h", dynlib="dynpcre")
|
||||
##
|
||||
## If ``dynlib`` is not specified, the C/C++ implementation files can be compiled in
|
||||
## with `cCompile() <cimport.html#cCompile.m%2C%2Cstring%2Cstring>`_, or the ``{.passL.}`` pragma
|
||||
## can be used to specify the static lib to link.
|
||||
## with `cCompile() <cimport.html#cCompile.m%2C%2Cstring%2Cstring>`_, or the
|
||||
## ``{.passL.}`` pragma can be used to specify the static lib to link.
|
||||
##
|
||||
## ``mode`` is purely for forward compatibility when toast adds C++ support. It can
|
||||
## be ignored for the foreseeable future.
|
||||
|
|
@ -562,8 +548,8 @@ macro c2nImport*(filename: static string, recurse: static bool = false, dynlib:
|
|||
mode: static string = "c", flags: static string = ""): untyped =
|
||||
## Import all supported definitions from specified header file using ``c2nim``
|
||||
##
|
||||
## Similar to `cImport() <cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring>`_ but uses ``c2nim`` to generate
|
||||
## the Nim wrapper instead of ``toast``. Note that neither
|
||||
## Similar to `cImport() <cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring>`_
|
||||
## but uses ``c2nim`` to generate the Nim wrapper instead of ``toast``. Note that neither
|
||||
## `cOverride() <cimport.html#cOverride.m>`_, `cSkipSymbol() <cimport.html#cSkipSymbol%2Cseq[T][string]>`_
|
||||
## nor `cPlugin() <cimport.html#cPlugin.m>`_ have any impact on ``c2nim``.
|
||||
##
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue