aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGanesh Viswanathan <dev@genotrance.com>2019-09-09 15:33:44 -0700
committerGanesh Viswanathan <dev@genotrance.com>2019-10-02 15:31:32 -0500
commitb0447677f1e02ec7d6b070e374e5e92b3ad0ce51 (patch)
tree068c469d468e2096f945f3ed04c4e33f5d019eb1
parent3a2395360712d2c6f27221e0887b7e3cad0be7a1 (diff)
downloadnimterop-b0447677f1e02ec7d6b070e374e5e92b3ad0ce51.tar.gz
nimterop-b0447677f1e02ec7d6b070e374e5e92b3ad0ce51.zip
Build pre-hook, altNames, uInt, getType() calls, add zlib test
Fix subdir header, static lib name, [u]int[\d]+
-rw-r--r--.travis.yml2
-rw-r--r--README.md2
-rw-r--r--nimterop/build.nim163
-rw-r--r--nimterop/cimport.nim68
-rw-r--r--nimterop/getters.nim12
-rw-r--r--nimterop/grammar.nim8
-rw-r--r--tests/getheader.nims46
-rw-r--r--tests/zlib.nim68
8 files changed, 279 insertions, 90 deletions
diff --git a/.travis.yml b/.travis.yml
index d866260..d48afd0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,7 +20,6 @@ cache:
- "$HOME/.choosenim/toolchains/nim-0.20.2"
install:
- - set -e
- export CHOOSENIM_CHOOSE_VERSION=$BRANCH
- |
curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh
@@ -28,7 +27,6 @@ install:
- export PATH="$HOME/.nimble/bin:/usr/local/opt/gettext/bin:$PATH"
script:
- - set -e
- nimble --verbose install -y
- nimble --verbose test
- nimble --verbose --nimbleDir:`pwd`/build/fakenimble install nimterop -y
diff --git a/README.md b/README.md
index 982a2e7..fcab924 100644
--- a/README.md
+++ b/README.md
@@ -62,7 +62,7 @@ when not defined(headerStatic):
else:
cImport(headerPath, recurse = true)
```
-This allows the user to control how the wrapper works - either pass `-d:headerStd` to search for `header.h` in the standard system path, `-d:headerGit` to clone the source from the specified git URL or `-d:headerDL` to get the source from download URL. Further, the `-d:headerVersion=X.Y.Z` flag can be used to specify which version to use. It is used as the tag name for Git whereas for DL, it replaces `$1` in the URL defined.
+This allows the user to control how the wrapper works - either pass `-d:headerStd` to search for `header.h` in the standard system path, `-d:headerGit` to clone the source from the specified git URL or `-d:headerDL` to get the source from download URL. Further, the `-d:headerSetVer=X.Y.Z` flag can be used to specify which version to use. It is used as the tag name for Git whereas for DL, it replaces `$1` in the URL defined.
The `-d:headerStatic` attempts to statically link the library. If it is omitted, the library is dynamically linked instead.
diff --git a/nimterop/build.nim b/nimterop/build.nim
index 7a1aaf9..2563bb8 100644
--- a/nimterop/build.nim
+++ b/nimterop/build.nim
@@ -32,7 +32,7 @@ proc execAction*(cmd: string, nostderr=false): string =
doAssert ret == 0, "Command failed: " & $(ret, nostderr) & "\nccmd: " & ccmd & "\nresult:\n" & result
proc findExe*(exe: string): string =
- ## Find the specified executable using the which/where command - supported
+ ## Find the specified executable using the `which`/`where` command - supported
## at compile time
var
cmd =
@@ -47,7 +47,7 @@ proc findExe*(exe: string): string =
return oup.splitLines()[0].strip()
proc mkDir*(dir: string) =
- ## Create a directory at cmopile time
+ ## Create a directory at compile time
##
## The `os` module is not available at compile time so a few
## crucial helper functions are included with nimterop.
@@ -57,7 +57,7 @@ proc mkDir*(dir: string) =
discard execAction(&"mkdir {flag} {dir.sanitizePath}")
proc cpFile*(source, dest: string, move=false) =
- ## Copy a file from source to destination at compile time
+ ## Copy a file from `source` to `dest` at compile time
let
source = source.replace("/", $DirSep)
dest = dest.replace("/", $DirSep)
@@ -76,7 +76,7 @@ proc cpFile*(source, dest: string, move=false) =
discard execAction(&"{cmd} {source.sanitizePath} {dest.sanitizePath}")
proc mvFile*(source, dest: string) =
- ## Move a file from source to destination at compile time
+ ## Move a file from `source` to `dest` at compile time
cpFile(source, dest, move=true)
proc rmFile*(source: string, dir = false) =
@@ -99,7 +99,7 @@ proc rmDir*(source: string) =
rmFile(source, dir = true)
proc extractZip*(zipfile, outdir: string) =
- ## Extract a zip file using powershell on Windows and unzip on other
+ ## Extract a zip file using `powershell` on Windows and `unzip` on other
## systems to the specified output directory
var cmd = "unzip -o $#"
if defined(Windows):
@@ -111,7 +111,7 @@ proc extractZip*(zipfile, outdir: string) =
discard execAction(&"cd {outdir.sanitizePath} && {cmd % zipfile}")
proc extractTar*(tarfile, outdir: string) =
- ## Extract a tar file using tar, 7z or 7za to the specified output directory
+ ## Extract a tar file using `tar`, `7z` or `7za` to the specified output directory
var
cmd = ""
name = ""
@@ -146,9 +146,9 @@ proc extractTar*(tarfile, outdir: string) =
rmFile(outdir / name)
proc downloadUrl*(url, outdir: string) =
- ## Download a file using curl or wget (or powershell on Windows) to the specified directory
+ ## Download a file using `curl` or `wget` (or `powershell` on Windows) to the specified directory
##
- ## If a zip file, it is automatically extracted after download.
+ ## If an archive file, it is automatically extracted after download.
let
file = url.extractFilename()
ext = file.splitFile().ext.toLowerAscii()
@@ -185,7 +185,7 @@ proc gitReset*(outdir: string) =
echo "# Retrying ..."
proc gitCheckout*(file, outdir: string) =
- ## Checkout the specified file in the git repository specified
+ ## Checkout the specified `file` in the git repository at `outdir`
##
## This effectively resets all changes in the file and can be
## used to undo any changes that were made to source files to enable
@@ -240,13 +240,17 @@ proc gitPull*(url: string, outdir = "", plist = "", checkout = "") =
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
+ ## `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.
+ ## 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
+ else:
+ let
+ dir = dir / file.parentDir()
+ file = file.extractFilename
for f in walkDirRec(dir, yieldFilter = {pcFile, pcLinkToFile},
followFilter = if recurse: {pcDir} else: {}):
@@ -311,6 +315,52 @@ proc configure*(path, check: string, flags = "") =
doAssert (path / check).fileExists(), "# Configure failed"
+proc getCmakePropertyStr(name, property, value: string): string =
+ &"\nset_target_properties({name} PROPERTIES {property} \"{value}\")\n"
+
+proc setCmakeProperty*(outdir, name, property, value: string) =
+ ## Set a `cmake` property in `outdir / CMakeLists.txt` - usable in the `xxxPreBuild` hook
+ ## for `getHeader()`
+ ##
+ ## `set_target_properties(name PROPERTIES property "value")`
+ let
+ cm = outdir / "CMakeLists.txt"
+ if cm.fileExists():
+ cm.writeFile(
+ cm.readFile() & getCmakePropertyStr(name, property, value)
+ )
+
+proc setCmakeLibName*(outdir, name, prefix = "", oname = "", suffix = "") =
+ ## Set a `cmake` property in `outdir / CMakeLists.txt` to specify a custom library output
+ ## name - usable in the `xxxPreBuild` hook for `getHeader()`
+ ##
+ ## `prefix` is typically `lib`
+ ## `oname` is the library name
+ ## `suffix` is typically `.a`
+ ##
+ ## Sometimes, `cmake` generates non-standard library names - e.g. zlib compiles to
+ ## `libzlibstatic.a` on Windows. This proc can help rename it to `libzlib.a` so that `getHeader()`
+ ## can find it after the library is compiled.
+ ##
+ ## ```
+ ## set_target_properties(name PROPERTIES PREFIX "prefix")
+ ## set_target_properties(name PROPERTIES OUTPUT_NAME "oname")
+ ## set_target_properties(name PROPERTIES SUFFIX "suffix")
+ ## ```
+ let
+ cm = outdir / "CMakeLists.txt"
+ if cm.fileExists():
+ var
+ str = ""
+ if prefix.len != 0:
+ str &= getCmakePropertyStr(name, "PREFIX", prefix)
+ if oname.len != 0:
+ str &= getCmakePropertyStr(name, "OUTPUT_NAME", oname)
+ if suffix.len != 0:
+ str &= getCmakePropertyStr(name, "SUFFIX", suffix)
+ if str.len != 0:
+ cm.writeFile(cm.readFile() & str)
+
proc cmake*(path, check, flags: string) =
## Run the `cmake` command to generate all Makefiles or other
## build scripts in the specified path
@@ -349,7 +399,7 @@ proc make*(path, check: string|Regex, flags = "") =
##
## `flags` are any flags that should be passed to the `make` command.
##
- ## If make.exe is missing and mingw32-make.exe is available, it will
+ ## If `make.exe` is missing and `mingw32-make.exe` is available, it will
## be copied over to make.exe in the same location.
if findFile(check, path).len != 0:
return
@@ -394,6 +444,9 @@ proc getGccPaths*(mode = "c"): seq[string] =
if path notin result:
result.add path
+ when defined(osx):
+ result.add execAction("xcrun --show-sdk-path").strip() & "/usr/include"
+
proc getGccLibPaths*(mode = "c"): seq[string] =
var
nul = when defined(Windows): "nul" else: "/dev/null"
@@ -416,6 +469,9 @@ proc getGccLibPaths*(mode = "c"): seq[string] =
if path notin result:
result.add path
+ when defined(osx):
+ result.add "/usr/lib"
+
proc getStdPath(header: string): string =
for inc in getGccPaths():
result = findFile(header, inc, recurse = false, first = true)
@@ -503,12 +559,19 @@ proc buildLibrary(lname, outdir, conFlags, cmakeFlags, makeFlags: string): strin
gen = ""
when defined(windows):
if findExe("sh").len != 0:
- gen = "MSYS Makefiles"
+ let
+ uname = execAction("sh -c uname -a").toLowerAscii()
+ if uname.contains("msys"):
+ gen = "MSYS Makefiles".quoteShell
+ elif uname.contains("mingw"):
+ gen = "MinGW Makefiles".quoteShell & " -DCMAKE_SH=\"CMAKE_SH-NOTFOUND\""
+ else:
+ echo "Unsupported system: " & uname
else:
- gen = "MinGW Makefiles"
+ gen = "MinGW Makefiles".quoteShell
else:
- gen = "Unix Makefiles"
- cmake(outdir / "build", "Makefile", &".. -G {gen.sanitizePath} {cmakeFlags}")
+ gen = "Unix Makefiles".quoteShell
+ cmake(outdir / "build", "Makefile", &".. -G {gen} {cmakeFlags}")
cmakeDeps = true
makePath = outdir / "build"
else:
@@ -550,33 +613,54 @@ proc getDynlibExt(): string =
result = ".dylib[0-9.]*"
macro getHeader*(header: static[string], giturl: static[string] = "", dlurl: static[string] = "", outdir: static[string] = "",
- conFlags: static[string] = "", cmakeFlags: static[string] = "", makeFlags: static[string] = ""): untyped =
+ conFlags: static[string] = "", cmakeFlags: static[string] = "", makeFlags: static[string] = "",
+ altNames: 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>`_.
##
- ## This proc checks -d:xxx defines based on the header name (e.g. lzma from lzma.h),
+ ## 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
+ ## `-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 -d:xxx defines are specified, ``outdir`` will be searched for the header.
+ ## If no `-d:xxx` defines are specified, `outdir` will be searched for the header as is.
+ ##
+ ## `-d:xxxSetVer=x.y.z` can be used to specify which version to use. It is used as a tag
+ ## name for Git whereas for DL, it replaces `$1` in the URL defined.
##
- ## 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
+ ## 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).
+ ## 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) or with `{.passL.}`.
+ ##
+ ## `-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.
+ ##
+ ## `conFlags`, `cmakeFlags` and `makeFlags` allow sending custom parameters to `configure`,
+ ## `cmake` and `make` in case additional configuration is required as part of the build process.
+ ##
+ ## `altNames` is a list of alternate names for the library - e.g. zlib uses `zlib.h` for the header but
+ ## the typical lib name is `libz.so` and not `libzlib.so`. In this case, `altNames = "z"`. Comma
+ ## separate for multiple alternate names.
+ ##
+ ## `xxxPreBuild` is a hook that is called after the source code is pulled from Git or downloaded but
+ ## before the library is built. This might be needed if some initial prep needs to be done before
+ ## compilation. A few values are provided to the hook to help provide context:
##
- ## ``-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.
+ ## `outdir` is the same `outdir` passed in and `header` is the discovered header path in the
+ ## downloaded source code.
+ ##
+ ## Simply define `proc xxxPreBuild(outdir, header: string)` in the wrapper and it will get called
+ ## prior to the build process.
var
- name = header.split(".")[0]
+ name = header.extractFilename().split(".")[0]
nameStd = newIdentNode(name & "Std")
nameGit = newIdentNode(name & "Git")
@@ -586,10 +670,18 @@ macro getHeader*(header: static[string], giturl: static[string] = "", dlurl: sta
path = newIdentNode(name & "Path")
lpath = newIdentNode(name & "LPath")
- version = newIdentNode(name & "Version")
+ version = newIdentNode(name & "SetVer")
lname = newIdentNode(name & "LName")
+ preBuild = newIdentNode(name & "PreBuild")
+
+ lre = "(lib)?$1[_]?(static)?[0-9.\\-]*\\"
- lre = "(lib)?$1[0-9.\\-]*\\" % name
+ if altNames.len != 0:
+ let
+ names = "(" & name & "|" & altNames.replace(",", "|") & ")"
+ lre = lre % names
+ else:
+ lre = lre % name
result = newNimNode(nnkStmtList)
result.add(quote do:
@@ -615,6 +707,11 @@ macro getHeader*(header: static[string], giturl: static[string] = "", dlurl: sta
else:
getLocalPath(`header`, `outdir`)
+ when declared(`preBuild`):
+ static:
+ `preBuild`(`outdir`, `path`)
+
+ const
`lpath`* = buildLibrary(`lname`, `outdir`, `conFlags`, `cmakeFlags`, `makeFlags`)
static:
diff --git a/nimterop/cimport.nim b/nimterop/cimport.nim
index 3c99c81..b295344 100644
--- a/nimterop/cimport.nim
+++ b/nimterop/cimport.nim
@@ -6,7 +6,7 @@ as a starting point for wrapping a new library. The template can be copied and
trimmed down and modified as required. `templite.nim <https://github.com/nimterop/nimterop/blob/master/nimterop/templite.nim>`_ is a shorter
version for more experienced users.
-All ``{.compileTime.}`` procs must be used in a compile time context, e.g. using:
+All `{.compileTime.}` procs must be used in a compile time context, e.g. using:
.. code-block:: c
@@ -194,7 +194,7 @@ macro cOverride*(body): untyped =
## proc svGetCallerInfo(fileName: var cstring; lineNumber: var cint)
##
## Using the `cOverride() <cimport.html#cOverride.m>`_ block, nimterop
- ## can be instructed to skip over ``svGetCallerInfo()``. This works for procs,
+ ## can be instructed to skip over `svGetCallerInfo()`. This works for procs,
## consts and types.
##
## `cOverride() <cimport.html#cOverride.m>`_ only affects calls to
@@ -254,7 +254,7 @@ macro cPlugin*(body): untyped =
## - `nskEnumField` for enum (field) names, though they are in the global namespace as `nskConst`
## - `nskProc` - for proc names
##
- ## ``nimterop/plugins`` is implicitly imported to provide access to standard plugin facilities.
+ ## `nimterop/plugins` is implicitly imported to provide access to standard plugin facilities.
##
## `cPlugin() <cimport.html#cPlugin.m>`_ only affects calls to
## `cImport() <cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring>`_ that follow it.
@@ -288,7 +288,7 @@ macro cPlugin*(body): untyped =
gStateCT.pluginSourcePath = path
proc cSearchPath*(path: string): string {.compileTime.}=
- ## Get full path to file or directory ``path`` in search path configured
+ ## Get full path to file or directory `path` in search path configured
## using `cAddSearchDir() <cimport.html#cAddSearchDir%2Cstring>`_ and
## `cAddStdDir() <cimport.html#cAddStdDir,string>`_.
##
@@ -321,15 +321,15 @@ proc cDisableCaching*() {.compileTime.} =
## value will continue to be used . Use `cDisableCaching() <cimport.html#cDisableCaching>`_
## to avoid this scenario during development.
##
- ## ``nim -f`` was broken prior to 0.19.4 but can also be used to flush the cached content.
+ ## `nim -f` was broken prior to 0.19.4 but can also be used to flush the cached content.
gStateCT.nocache = true
macro cDefine*(name: static string, val: static string = ""): untyped =
- ## ``#define`` an identifer that is forwarded to the C/C++ preprocessor if
+ ## `#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".}``
+ ## as well as to the C/C++ compiler during Nim compilation using `{.passC: "-DXXX".}`
result = newNimNode(nnkStmtList)
@@ -350,7 +350,7 @@ macro cDefine*(name: static string, val: static string = ""): untyped =
echo result.repr & "\n"
proc cAddSearchDir*(dir: string) {.compileTime.} =
- ## Add directory ``dir`` to the search path used in calls to
+ ## Add directory `dir` to the search path used in calls to
## `cSearchPath() <cimport.html#cSearchPath,string>`_.
runnableExamples:
import paths, os
@@ -365,7 +365,7 @@ 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".}``.
+ ## as well as to the C/C++ compiler during Nim compilation using `{.passC: "-IXXX".}`.
var dir = interpPath(dir)
result = newNimNode(nnkStmtList)
@@ -380,7 +380,7 @@ macro cIncludeDir*(dir: static string): untyped =
echo result.repr
proc cAddStdDir*(mode = "c") {.compileTime.} =
- ## Add the standard ``c`` [default] or ``cpp`` include paths to search
+ ## Add the standard `c` [default] or `cpp` include paths to search
## path used in calls to `cSearchPath() <cimport.html#cSearchPath,string>`_
runnableExamples:
static: cAddStdDir()
@@ -390,24 +390,24 @@ proc cAddStdDir*(mode = "c") {.compileTime.} =
cAddSearchDir inc
macro cCompile*(path: static string, mode = "c", exclude = ""): untyped =
- ## Compile and link C/C++ implementation into resulting binary using ``{.compile.}``
+ ## Compile and link C/C++ implementation into resulting binary using `{.compile.}`
##
- ## ``path`` can be a specific file or contain wildcards:
+ ## `path` can be a specific file or contain wildcards:
##
## .. code-block:: nim
##
## cCompile("file.c")
## cCompile("path/to/*.c")
##
- ## ``mode`` recursively searches for code files in ``path``.
+ ## `mode` recursively searches for code files in `path`.
##
- ## ``c`` searches for ``*.c`` whereas ``cpp`` searches for ``*.C *.cpp *.c++ *.cc *.cxx``
+ ## `c` searches for `*.c` whereas `cpp` searches for `*.C *.cpp *.c++ *.cc *.cxx`
##
## .. code-block:: nim
##
## cCompile("path/to/dir", "cpp")
##
- ## ``exclude`` can be used to exclude files by partial string match. Comma separated to
+ ## `exclude` can be used to exclude files by partial string match. Comma separated to
## specify multiple exclude strings
##
## .. code-block:: nim
@@ -485,16 +485,16 @@ macro cCompile*(path: static string, mode = "c", exclude = ""): untyped =
macro cImport*(filename: static string, recurse: static bool = false, dynlib: static string = "",
mode: static string = "c", flags: static string = ""): untyped =
## Import all supported definitions from specified header file. Generated
- ## content is cached in ``nimcache`` until ``filename`` changes unless
- ## `cDisableCaching() <cimport.html#cDisableCaching>`_ is set. ``nim -f``
+ ## content is cached in `nimcache` until `filename` changes unless
+ ## `cDisableCaching() <cimport.html#cDisableCaching>`_ is set. `nim -f`
## can also be used after Nim v0.19.4 to flush the cache.
##
- ## ``recurse`` can be used to generate Nim wrappers from ``#include`` files
- ## referenced in ``filename``. This is only done for files in the same
- ## directory as ``filename`` or in a directory added using
+ ## `recurse` can be used to generate Nim wrappers from `#include` files
+ ## referenced in `filename`. This is only done for files in the same
+ ## directory as `filename` or in a directory added using
## `cIncludeDir() <cimport.html#cIncludeDir.m>`_
##
- ## ``dynlib`` can be used to specify the Nim string to use to specify the dynamic
+ ## `dynlib` can be used to specify the Nim string to use to specify the dynamic
## library to load the imported symbols from. For example:
##
## .. code-block:: nim
@@ -513,14 +513,14 @@ 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
+ ## 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.
+ ## `{.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
+ ## `mode` is purely for forward compatibility when toast adds C++ support. It can
## be ignored for the foreseeable future.
##
- ## ``flags`` can be used to pass any other command line arguments to ``toast``.
+ ## `flags` can be used to pass any other command line arguments to `toast`.
result = newNimNode(nnkStmtList)
@@ -546,24 +546,24 @@ macro cImport*(filename: static string, recurse: static bool = false, dynlib: st
macro c2nImport*(filename: static string, recurse: static bool = false, dynlib: static string = "",
mode: static string = "c", flags: static string = ""): untyped =
- ## Import all supported definitions from specified header file using ``c2nim``
+ ## 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
+ ## 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``.
+ ## nor `cPlugin() <cimport.html#cPlugin.m>`_ have any impact on `c2nim`.
##
- ## ``toast`` is only used to preprocess the header file and recurse
+ ## `toast` is only used to preprocess the header file and recurse
## if specified.
##
- ## ``mode`` should be set to ``cpp`` for c2nim to wrap C++ headers.
+ ## `mode` should be set to `cpp` for c2nim to wrap C++ headers.
##
- ## ``flags`` can be used to pass other command line arguments to ``c2nim``.
+ ## `flags` can be used to pass other command line arguments to `c2nim`.
##
- ## ``nimterop`` does not depend on ``c2nim`` as a ``nimble`` dependency so it
+ ## `nimterop` does not depend on `c2nim` as a `nimble` dependency so it
## does not get installed automatically. Any wrapper or library that requires this proc
- ## needs to install ``c2nim`` with ``nimble install c2nim`` or add it as a dependency in
- ## its own ``.nimble`` file.
+ ## needs to install `c2nim` with `nimble install c2nim` or add it as a dependency in
+ ## its own `.nimble` file.
result = newNimNode(nnkStmtList)
diff --git a/nimterop/getters.nim b/nimterop/getters.nim
index 54040c0..20fd8b3 100644
--- a/nimterop/getters.nim
+++ b/nimterop/getters.nim
@@ -26,7 +26,7 @@ when while
xor
yield""".split(Whitespace).toSet()
-const gTypeMap = {
+const gTypeMap* = {
# char
"char": "cchar",
"signed char": "cschar",
@@ -39,6 +39,8 @@ const gTypeMap = {
"signed short int": "cshort",
"unsigned short": "cushort",
"unsigned short int": "cushort",
+ "uShort": "cushort",
+ "u_short": "cushort",
# int
"int": "cint",
@@ -47,6 +49,8 @@ const gTypeMap = {
"ssize_t": "cint",
"unsigned": "cuint",
"unsigned int": "cuint",
+ "uInt": "cuint",
+ "u_int": "cuint",
"size_t": "cuint",
# long
@@ -57,6 +61,8 @@ const gTypeMap = {
"off_t": "clong",
"unsigned long": "culong",
"unsigned long int": "culong",
+ "uLong": "culong",
+ "u_long": "culong",
# long long
"long long": "clonglong",
@@ -79,8 +85,8 @@ proc getType*(str: string): string =
result = str.strip(chars={'_'}).
replace(re"\s+", " ").
- replace(re"([u]?int[\d]+)_t", "$1").
- replace(re"([u]?int)ptr_t", "ptr $1")
+ replace(re"^([u]?int[\d]+)_t$", "$1").
+ replace(re"^([u]?int)ptr_t$", "ptr $1")
if gTypeMap.hasKey(result):
result = gTypeMap[result]
diff --git a/nimterop/grammar.nim b/nimterop/grammar.nim
index 6e2db16..bfc5def 100644
--- a/nimterop/grammar.nim
+++ b/nimterop/grammar.nim
@@ -91,7 +91,7 @@ proc initGrammar(): Grammar =
"""
template funcParamCommon(fname, pname, ptyp, pptr, pout, count, i: untyped): untyped =
- ptyp = nimState.getIdentifier(nimState.data[i].val, nskType, fname)
+ ptyp = nimState.getIdentifier(nimState.data[i].val, nskType, fname).getType()
pptr = ""
while i+1 < nimState.data.len and nimState.data[i+1].name == "pointer_declarator":
@@ -137,7 +137,7 @@ proc initGrammar(): Grammar =
var
i = 0
- typ = nimState.getIdentifier(nimState.data[i].val, nskType)
+ typ = nimState.getIdentifier(nimState.data[i].val, nskType).getType()
name = ""
nname = ""
tptr = ""
@@ -161,7 +161,7 @@ proc initGrammar(): Grammar =
let
pragma = nimState.getPragma(nimState.getImportC(name, nname))
- if typ.nBl and nname.nBl and nimState.addNewIdentifer(nname):
+ if nname notin gTypeMap and typ.nBl and nname.nBl and nimState.addNewIdentifer(nname):
if i < nimState.data.len and nimState.data[^1].name == "function_declarator":
var
fname = nname
@@ -575,7 +575,7 @@ proc initGrammar(): Grammar =
if fnname.nBl and nimState.addNewIdentifer(fnname):
let
- ftyp = nimState.getIdentifier(nimState.data[0].val, nskType, fnname)
+ ftyp = nimState.getIdentifier(nimState.data[0].val, nskType, fnname).getType()
pragma = nimState.getPragma(nimState.getImportC(fname, fnname), "cdecl")
if fptr.len != 0 or ftyp != "object":
diff --git a/tests/getheader.nims b/tests/getheader.nims
index 840a114..e489380 100644
--- a/tests/getheader.nims
+++ b/tests/getheader.nims
@@ -3,6 +3,7 @@ import strutils
proc testCall(cmd, output: string, exitCode: int, delete = true) =
if delete:
rmDir("build/liblzma")
+ rmDir("build/zlib")
echo cmd
var
ccmd =
@@ -17,26 +18,45 @@ proc testCall(cmd, output: string, exitCode: int, delete = true) =
var
cmd = "nim c -f"
- rcmd = " -r lzma.nim"
- exp = "liblzma version = "
+ lrcmd = " -r lzma.nim"
+ zrcmd = " -r zlib.nim"
+ lexp = "liblzma version = "
+ zexp = "zlib version = "
-when defined(posix):
- testCall(cmd & rcmd, "No build files found", 1)
+testCall(cmd & lrcmd, "No build files found", 1)
+when defined(posix):
# stdlib
- testCall(cmd & " -d:lzmaStd" & rcmd, exp, 0)
- testCall(cmd & " -d:lzmaStd -d:lzmaStatic" & rcmd, exp, 0)
+ testCall(cmd & " -d:lzmaStd" & lrcmd, lexp, 0)
+ testCall(cmd & " -d:lzmaStd -d:lzmaStatic" & lrcmd, lexp, 0)
+
+ when not defined(osx):
+ testCall(cmd & " -d:zlibStd" & zrcmd, zexp, 0)
+ testCall(cmd & " -d:zlibStd -d:zlibStatic" & zrcmd, zexp, 0)
# git
- testCall(cmd & " -d:lzmaGit" & rcmd, exp, 0)
- testCall(cmd & " -d:lzmaGit -d:lzmaStatic" & rcmd, exp, 0, delete = false)
+ testCall(cmd & " -d:lzmaGit" & lrcmd, lexp, 0)
+ testCall(cmd & " -d:lzmaGit -d:lzmaStatic" & lrcmd, lexp, 0, delete = false)
# git tag
- testCall(cmd & " -d:lzmaGit -d:lzmaVersion=v5.2.0" & rcmd, exp & "5.2.0", 0)
- testCall(cmd & " -d:lzmaGit -d:lzmaStatic -d:lzmaVersion=v5.2.0" & rcmd, exp & "5.2.0", 0, delete = false)
+ testCall(cmd & " -d:lzmaGit -d:lzmaSetVer=v5.2.0" & lrcmd, lexp & "5.2.0", 0)
+ testCall(cmd & " -d:lzmaGit -d:lzmaStatic -d:lzmaSetVer=v5.2.0" & lrcmd, lexp & "5.2.0", 0, delete = false)
testCall("cd build/liblzma && git branch", "v5.2.0", 0, delete = false)
+# git
+testCall(cmd & " -d:zlibGit" & zrcmd, zexp, 0)
+testCall(cmd & " -d:zlibGit -d:zlibStatic" & zrcmd, zexp, 0, delete = false)
+
+# git tag
+testCall(cmd & " -d:zlibGit -d:zlibSetVer=v1.2.10" & zrcmd, zexp & "1.2.10", 0)
+testCall(cmd & " -d:zlibGit -d:zlibStatic -d:zlibSetVer=v1.2.10" & zrcmd, zexp & "1.2.10", 0, delete = false)
+testCall("cd build/zlib && git branch", "v1.2.10", 0, delete = false)
+
+# dl
+testCall(cmd & " -d:lzmaDL" & lrcmd, "Need version", 1)
+testCall(cmd & " -d:lzmaDL -d:lzmaSetVer=5.2.4" & lrcmd, lexp & "5.2.4", 0)
+testCall(cmd & " -d:lzmaDL -d:lzmaStatic -d:lzmaSetVer=5.2.4" & lrcmd, lexp & "5.2.4", 0, delete = false)
+
# dl
-testCall(cmd & " -d:lzmaDL" & rcmd, "Need version", 1)
-testCall(cmd & " -d:lzmaDL -d:lzmaVersion=5.2.4" & rcmd, exp & "5.2.4", 0)
-testCall(cmd & " -d:lzmaDL -d:lzmaStatic -d:lzmaVersion=5.2.4" & rcmd, exp & "5.2.4", 0, delete = false)
+testCall(cmd & " -d:zlibDL -d:zlibSetVer=1.2.11" & zrcmd, zexp & "1.2.11", 0)
+testCall(cmd & " -d:zlibDL -d:zlibStatic -d:zlibSetVer=1.2.11" & zrcmd, zexp & "1.2.11", 0, delete = false)
diff --git a/tests/zlib.nim b/tests/zlib.nim
new file mode 100644
index 0000000..f2cc16a
--- /dev/null
+++ b/tests/zlib.nim
@@ -0,0 +1,68 @@
+import os, strutils
+
+import nimterop/[build, cimport]
+
+const
+ baseDir = currentSourcePath.parentDir()/"build"/"zlib"
+
+static:
+ cDebug()
+
+proc zlibPreBuild(outdir, path: string) =
+ let
+ mf = outdir / "Makefile"
+ if mf.fileExists():
+ # Delete default Makefile
+ if mf.readFile().contains("configure first"):
+ mf.rmFile()
+ when defined(windows):
+ # Fix static lib name on Windows
+ setCmakeLibName(outdir, "zlibstatic", prefix = "lib", oname = "zlib", suffix = ".a")
+
+getHeader(
+ "zlib.h",
+ giturl = "https://github.com/madler/zlib",
+ dlurl = "http://zlib.net/zlib-$1.tar.gz",
+ outdir = baseDir,
+ altNames = "z"
+)
+
+cPlugin:
+ import regex, strutils
+
+ proc onSymbol*(sym: var Symbol) {.exportc, dynlib.} =
+ sym.name = sym.name.replace(re"_[_]+", "_").strip(chars = {'_'})
+
+cOverride:
+ type
+ voidpf = ptr object
+ voidpc = ptr object
+ voidp = ptr object
+ uLongf = culong
+ z_size_t = culong
+ z_crc_t = culong
+ alloc_func* {.importc.} = proc(opaque: voidpf, items, size: uint) {.cdecl.}
+ Bytef {.importc.} = object
+
+ when defined(posix):
+ type
+ pthread_mutex_s = object
+ pthread_cond_s = object
+ pthread_rwlock_arch_t = object
+ extension = object
+ fd_set = object
+
+when defined(posix):
+ static:
+ cSkipSymbol(@["u_int8_t", "u_int16_t", "u_int32_t", "u_int64_t"])
+
+when defined(zlibGit) or defined(zlibDL):
+ when dirExists(baseDir / "build"):
+ cIncludeDir(baseDir / "build")
+
+when not defined(zlibStatic):
+ cImport(zlibPath, recurse = true, dynlib = "zlibLPath")
+else:
+ cImport(zlibPath, recurse = true)
+
+echo "zlib version = " & $zlibVersion()