Add cImport for multiple headers

This commit is contained in:
Ganesh Viswanathan 2020-04-22 22:11:30 -05:00
commit c1f46680f7
3 changed files with 99 additions and 37 deletions

View file

@ -51,6 +51,9 @@ task test, "Test":
execTest "tests/tpcre.nim"
execTest "tests/tpcre.nim", "-d:FLAGS=\"-f:ast2\""
execTest "tests/rsa.nim"
execTest "tests/rsa.nim", "-d:FLAGS=\"-H\""
# Platform specific tests
when defined(Windows):
execTest "tests/tmath.nim"

View file

@ -91,6 +91,11 @@ proc getCacheValue(fullpath: string): string =
if not gStateCT.nocache:
result = fullpath.getFileDate()
proc getCacheValue(fullpaths: seq[string]): string =
if not gStateCT.nocache:
for fullpath in fullpaths:
result &= getCacheValue(fullpath)
proc getToastError(output: string): string =
# Filter out preprocessor errors
for line in output.splitLines():
@ -121,7 +126,7 @@ proc getNimCheckError(output: string): tuple[tmpFile, errors: string] =
result.errors = "\n\n" & check
proc getToast(fullpath: string, recurse: bool = false, dynlib: string = "",
proc getToast(fullpaths: seq[string], recurse: bool = false, dynlib: string = "",
mode = "c", flags = "", noNimout = false): string =
var
ret = 0
@ -158,11 +163,12 @@ proc getToast(fullpath: string, recurse: bool = false, dynlib: string = "",
if gStateCT.pluginSourcePath.nBl:
cmd.add &" --pluginSourcePath={gStateCT.pluginSourcePath.sanitizePath}"
cmd.add &" {fullpath.sanitizePath}"
for fullpath in fullpaths:
cmd.add &" {fullpath.sanitizePath}"
# see https://github.com/nimterop/nimterop/issues/69
(result, ret) = execAction(cmd, die = false, cache = (not gStateCT.nocache),
cacheKey = getCacheValue(fullpath))
cacheKey = getCacheValue(fullpaths))
doAssert ret == 0, getToastError(result)
macro cOverride*(body): untyped =
@ -555,6 +561,46 @@ macro cCompile*(path: static string, mode = "c", exclude = ""): untyped =
if gStateCT.debug:
echo result.repr
macro cImport*(filenames: static seq[string], recurse: static bool = false, dynlib: static string = "",
mode: static string = "c", flags: static string = ""): untyped =
## Import multiple headers in one shot
##
## This macro is preferable over multiple individual `cImport()` calls, especially
## when the headers might `#include` the same headers and result in duplicate symbols.
result = newNimNode(nnkStmtList)
var
fullpaths: seq[string]
for filename in filenames:
fullpaths.add findPath(filename)
# In case cOverride called after cPlugin
if gStateCT.pluginSourcePath.Bl:
cPluginHelper(gStateCT.pluginSource)
echo "# Importing " & fullpaths.join(", ").sanitizePath
let
output = getToast(fullpaths, recurse, dynlib, mode, flags)
# Reset plugin and overrides for next cImport
if gStateCT.overrides.nBl:
gStateCT.pluginSourcePath = ""
gStateCT.overrides = ""
if gStateCT.debug:
echo output
try:
let body = parseStmt(output)
result.add body
except:
let
(tmpFile, errors) = getNimCheckError(output)
doAssert false, errors & "\n\nNimterop codegen limitation or error - review 'nim check' output above generated for " & tmpFile
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
@ -590,8 +636,8 @@ macro cImport*(filename: static string, recurse: static bool = false, dynlib: st
## 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.
## `mode` selects the preprocessor and tree-sitter parser to be used to process
## the header.
##
## `flags` can be used to pass any other command line arguments to `toast`. A
## good example would be `--prefix` and `--suffix` which strip leading and
@ -600,37 +646,8 @@ macro cImport*(filename: static string, recurse: static bool = false, dynlib: st
## `cImport()` consumes and resets preceding `cOverride()` calls. `cPlugin()`
## is retained for the next `cImport()` call unless a new `cPlugin()` call is
## defined.
result = newNimNode(nnkStmtList)
let
fullpath = findPath(filename)
# In case cOverride called after cPlugin
if gStateCT.pluginSourcePath.Bl:
cPluginHelper(gStateCT.pluginSource)
echo "# Importing " & fullpath.sanitizePath
let
output = getToast(fullpath, recurse, dynlib, mode, flags)
# Reset plugin and overrides for next cImport
if gStateCT.overrides.nBl:
gStateCT.pluginSourcePath = ""
gStateCT.overrides = ""
if gStateCT.debug:
echo output
try:
let body = parseStmt(output)
result.add body
except:
let
(tmpFile, errors) = getNimCheckError(output)
doAssert false, errors & "\n\nNimterop codegen limitation or error - review 'nim check' output above generated for " & tmpFile
return quote do:
cImport(@[`filename`], bool(`recurse`), `dynlib`, `mode`, `flags`)
macro c2nImport*(filename: static string, recurse: static bool = false, dynlib: static string = "",
mode: static string = "c", flags: static string = ""): untyped =
@ -661,7 +678,7 @@ macro c2nImport*(filename: static string, recurse: static bool = false, dynlib:
echo "# Importing " & fullpath & " with c2nim"
let
output = getToast(fullpath, recurse, dynlib, noNimout = true)
output = getToast(@[fullpath], recurse, dynlib, noNimout = true)
hash = output.hash().abs()
hpath = getProjectCacheDir("c2nimCache", forceClean = false) / "nimterop_" & $hash & ".h"
npath = hpath[0 .. hpath.rfind('.')] & "nim"

42
tests/rsa.nim Normal file
View file

@ -0,0 +1,42 @@
import os
import strutils
import nimterop/[build, cimport]
setDefines(@["cryptoStd"])
getHeader("openssl/crypto.h")
const
basePath = cryptoPath.parentDir
FLAGS {.strdefine.} = ""
cPlugin:
import strutils
proc onSymbol*(sym: var Symbol) {.exportc, dynlib.} =
sym.name = sym.name.strip(chars = {'_'}).replace("__", "_")
if sym.name in [
"AES_ENCRYPT", "AES_DECRYPT",
"BIO_CTRL_PENDING", "BIO_CTRL_WPENDING",
"BN_F_BNRAND", "BN_F_BNRAND_RANGE",
"CRYPTO_THREADID",
"EVP_CIPHER",
"OPENSSL_VERSION",
"PKCS7_ENCRYPT", "PKCS7_STREAM",
"SSL_TXT_ADH", "SSL_TXT_AECDH", "SSL_TXT_kECDHE"
]:
sym.name = "C_" & sym.name
cOverride:
proc OPENSSL_die*(assertion: cstring; file: cstring; line: cint) {.importc.}
cImport(@[
basePath / "rsa.h",
basePath / "err.h",
], recurse = true, flags = "-f:ast2 -s " & FLAGS)
{.passL: cryptoLPath.}
OpensslInit()
echo $OPENSSL_VERSION_TEXT