diff options
| author | Ganesh Viswanathan <dev@genotrance.com> | 2019-09-09 15:33:44 -0700 |
|---|---|---|
| committer | Ganesh Viswanathan <dev@genotrance.com> | 2019-10-02 15:31:32 -0500 |
| commit | b0447677f1e02ec7d6b070e374e5e92b3ad0ce51 (patch) | |
| tree | 068c469d468e2096f945f3ed04c4e33f5d019eb1 | |
| parent | 3a2395360712d2c6f27221e0887b7e3cad0be7a1 (diff) | |
| download | nimterop-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.yml | 2 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | nimterop/build.nim | 163 | ||||
| -rw-r--r-- | nimterop/cimport.nim | 68 | ||||
| -rw-r--r-- | nimterop/getters.nim | 12 | ||||
| -rw-r--r-- | nimterop/grammar.nim | 8 | ||||
| -rw-r--r-- | tests/getheader.nims | 46 | ||||
| -rw-r--r-- | tests/zlib.nim | 68 |
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 @@ -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() |
