Dynlib support

This commit is contained in:
genotrance 2019-05-08 23:56:30 -05:00 committed by GitHub
commit 2cdde94635
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 126 additions and 41 deletions

View file

@ -34,6 +34,7 @@ task buildToast, "build toast":
proc testAll() =
execTest "tests/tnimterop_c.nim"
execCmd "nim cpp -r tests/tnimterop_cpp.nim"
execTest "tests/tpcre.nim"
## platform specific tests
when defined(Windows):

View file

@ -151,14 +151,16 @@ proc printNim*(gState: State, fullpath: string, root: TSNode, astTable: AstTable
var
nimState = new(NimState)
fp = fullpath.replace("\\", "/")
nimState.identifiers = newTable[string, string]()
nimState.currentHeader = getCurrentHeader(fullpath)
nimState.impHeader = nimState.currentHeader.replace("header", "imp")
nimState.sourceFile = fullpath
nimState.constStr &= &"\n {nimState.currentHeader} {{.used.}} = \"{fp}\""
nimState.gState = gState
nimState.currentHeader = getCurrentHeader(fullpath)
nimState.impShort = nimState.currentHeader.replace("header", "imp")
nimState.sourceFile = fullpath
if nimState.gState.dynlib.len == 0:
nimState.constStr &= &"\n {nimState.currentHeader} {{.used.}} = \"{fp}\""
root.searchAst(astTable, nimState)
@ -169,8 +171,8 @@ proc printNim*(gState: State, fullpath: string, root: TSNode, astTable: AstTable
echo &"const{nimState.constStr}\n"
echo &"""
{{.pragma: {nimState.impHeader}, importc, header: {nimState.currentHeader}.}}
{{.pragma: {nimState.impHeader}C, {nimState.impHeader}, cdecl.}}
{{.pragma: {nimState.impShort}, importc{nimState.getHeader()}.}}
{{.pragma: {nimState.impShort}C, {nimState.impShort}, cdecl{nimState.getDynlib()}.}}
"""
if nimState.typeStr.nBl:

View file

@ -112,7 +112,7 @@ proc getNimCheckError(output: string): tuple[tmpFile, errors: string] =
result.errors = "\n\n" & check
proc getToast(fullpath: string, recurse: bool = false): string =
proc getToast(fullpath: string, recurse: bool = false, dynlib: string = ""): string =
var
ret = 0
cmd = when defined(Windows): "cmd /c " else: ""
@ -131,6 +131,9 @@ proc getToast(fullpath: string, recurse: bool = false): string =
for i in gStateCT.includeDirs:
cmd.add &" --includeDirs+={i.quoteShell}"
if dynlib.len != 0:
cmd.add &" --dynlib={dynlib}"
if gStateCT.symOverride.len != 0:
cmd.add &" --symOverride={gStateCT.symOverride.join(\",\")}"
@ -297,9 +300,6 @@ proc cDisableCaching*() {.compileTime.} =
gStateCT.nocache = true
# TODO: `passC` should be delayed and inserted inside `cImport`, `cCompile`
# and this should be made a proc:
# proc cDefine*(name: string, val = "") {.compileTime.} =
macro cDefine*(name: static string, val: static string = ""): untyped =
## ``#define`` an identifer that is forwarded to the C/C++ compiler
## using ``{.passC: "-DXXX".}``
@ -462,7 +462,7 @@ macro cCompile*(path: static string, mode = "c", exclude = ""): untyped =
if gStateCT.debug:
echo result.repr
macro cImport*(filename: static string, recurse: static bool = false): untyped =
macro cImport*(filename: static string, recurse: static bool = false, dynlib: 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``
@ -472,6 +472,29 @@ macro cImport*(filename: static string, recurse: static bool = false): untyped =
## 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
## library to load the imported symbols from. For example:
##
## .. code-block:: nim
##
## const
## dynpcre =
## when defined(windows):
## when defined(cpu64):
## "pcre64.dll"
## else:
## "pcre32.dll"
## elif hostOS == "macosx":
## "libpcre(.3|.1|).dylib"
## else:
## "libpcre.so(.3|.1|)"
##
## 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,,string>`_, or the `{.passL.}` pragma
## can be used to specify the static lib to link.
result = newNimNode(nnkStmtList)
@ -481,7 +504,7 @@ macro cImport*(filename: static string, recurse: static bool = false): untyped =
echo "# Importing " & fullpath
let
output = getToast(fullpath, recurse)
output = getToast(fullpath, recurse, dynlib)
if gStateCT.debug:
echo output

View file

@ -338,6 +338,47 @@ proc getSplitComma*(joined: seq[string]): seq[string] =
for i in joined:
result = result.concat(i.split(","))
proc getHeader*(nimState: NimState): string =
result =
if nimState.gState.dynlib.len == 0:
&", header: {nimState.currentHeader}"
else:
""
proc getDynlib*(nimState: NimState): string =
result =
if nimState.gState.dynlib.len != 0:
&", dynlib: {nimState.gState.dynlib}"
else:
""
proc getImportC*(nimState: NimState, origName, nimName: string): string =
if nimName != origName:
result = &"importc: \"{origName}\"{nimState.getHeader()}"
else:
result = nimState.impShort
proc getPragma*(nimState: NimState, pragmas: varargs[string]): string =
result = ""
for pragma in pragmas.items():
if pragma.len != 0:
result &= pragma & ", "
if result.len != 0:
result = " {." & result[0 .. ^3] & ".}"
result = result.replace(nimState.impShort & ", cdecl", nimState.impShort & "C")
let
dy = nimState.getDynlib()
if ", cdecl" in result and dy.len != 0:
result = result.replace(".}", dy & ".}")
proc getComments*(nimState: NimState): string =
if not nimState.gState.nocomments and nimState.commentStr.len != 0:
result = "\n" & nimState.commentStr
nimState.commentStr = ""
proc dll*(path: string): string =
let
(dir, name, _) = path.splitFile()

View file

@ -68,7 +68,7 @@ proc downloadUrl*(url, outdir: string) =
var cmd = if defined(Windows):
"powershell [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; wget $# -OutFile $#"
else:
"curl $# -o $#"
"curl -L $# -o $#"
discard execAction(cmd % [url, (outdir/file).quoteShell])
if ext == ".zip":

View file

@ -57,7 +57,7 @@ type
nocache*, nocomments*, debug*, past*, preprocess*, pnim*, pretty*, recurse*: bool
code*, mode*, pluginSourcePath*: string
code*, dynlib*, mode*, pluginSourcePath*: string
onSymbol*: OnSymbol
@ -68,7 +68,7 @@ type
gState*: State
currentHeader*, impHeader*, sourceFile*: string
currentHeader*, impShort*, sourceFile*: string
data*: seq[tuple[name, val: string]]

View file

@ -7,27 +7,6 @@ import "."/[getters, globals, lisp, treesitter/api]
type
Grammar = seq[tuple[grammar: string, call: proc(ast: ref Ast, node: TSNode, nimState: NimState) {.nimcall.}]]
proc genImportC(nimState: NimState, origName, nimName: string): string =
if nimName != origName:
result = &"importc: \"{origName}\", header: {nimState.currentHeader}"
else:
result = nimState.impHeader
proc genPragma(nimState: NimState, pragmas: varargs[string]): string =
result = ""
for pragma in pragmas.items():
if pragma.len != 0:
result &= pragma & ", "
if result.len != 0:
result = " {." & result[0 .. ^2] & ".}"
result = result.replace(nimState.impHeader & ", cdecl", nimState.impHeader & "C")
proc getComments(nimState: NimState): string =
if not nimState.gState.nocomments and nimState.commentStr.len != 0:
result = "\n" & nimState.commentStr
nimState.commentStr = ""
proc initGrammar(): Grammar =
# #define X Y
result.add(("""
@ -177,7 +156,7 @@ proc initGrammar(): Grammar =
i += 1
let
pragma = nimState.genPragma(nimState.genImportC(name, nname))
pragma = nimState.getPragma(nimState.getImportC(name, nname))
if typ.nBl and nname.nBl and nimState.addNewIdentifer(nname):
if i < nimState.data.len and nimState.data[^1].name == "function_declarator":
@ -234,7 +213,7 @@ proc initGrammar(): Grammar =
else:
if nimState.addNewIdentifer(ndname):
let
pragma = nimState.genPragma(nimState.genImportc(dname, ndname), "bycopy")
pragma = nimState.getPragma(nimState.getImportc(dname, ndname), "bycopy")
nimState.typeStr &=
&"{nimState.getComments()}\n {ndname}*{pragma} = {dptr}{nname}"
@ -274,7 +253,7 @@ proc initGrammar(): Grammar =
nimState.typeStr &= &"{nimState.getComments()}\n {nname}* {{.bycopy.}} = object{union}"
else:
let
pragma = nimState.genPragma(nimState.genImportC(prefix & name, nname), "bycopy")
pragma = nimState.getPragma(nimState.getImportC(prefix & name, nname), "bycopy")
nimState.typeStr &= &"{nimState.getComments()}\n {nname}*{pragma} = object{union}"
var
@ -586,7 +565,7 @@ proc initGrammar(): Grammar =
if fnname.nBl and nimState.addNewIdentifer(fnname):
let
ftyp = nimState.getIdentifier(nimState.data[0].val, nskType, fnname)
pragma = nimState.genPragma(nimState.genImportC(fname, fnname), "cdecl")
pragma = nimState.getPragma(nimState.getImportC(fname, fnname), "cdecl")
if fptr.len != 0 or ftyp != "object":
nimState.procStr &= &"{nimState.getComments()}\nproc {fnname}*({pout}): {getPtrType(fptr&ftyp)}{pragma}"

View file

@ -105,6 +105,7 @@ proc main(
nocomments = false,
defines: seq[string] = @[],
includeDirs: seq[string] = @[],
dynlib: string = "",
symOverride: seq[string] = @[],
pluginSourcePath: string = "",
debug = false,
@ -121,6 +122,7 @@ proc main(
nocomments: nocomments,
defines: defines,
includeDirs: includeDirs,
dynlib: dynlib,
symOverride: symOverride,
pluginSourcePath: pluginSourcePath,
debug: debug,
@ -153,6 +155,7 @@ when isMainModule:
"nocomments": "exclude top-level comments from output",
"defines": "definitions to pass to preprocessor",
"includeDirs": "include directory to pass to preprocessor",
"dynlib": "Import symbols from library in specified Nim string",
"symOverride": "skip generating specified symbols",
"pluginSourcePath": "Nim file to build and load as a plugin",
"debug": "enable debug output",
@ -167,6 +170,7 @@ when isMainModule:
"nocomments": 'c',
"defines": 'D',
"includeDirs": 'I',
"dynlib": 'l',
"symOverride": 'O',
"debug": 'd',
"pgrammar": 'g'

35
tests/tpcre.nim Normal file
View file

@ -0,0 +1,35 @@
import os
import nimterop/[cimport, git, paths]
const
baseDir = nimteropBuildDir()/"pcre"
pcreH = baseDir/"pcre.h.in"
static:
if not pcreH.fileExists():
downloadUrl("https://github.com/svn2github/pcre/raw/master/pcre.h.in", baseDir)
cDebug()
cDisableCaching()
const
dynpcre =
when defined(windows):
when defined(cpu64):
"pcre64.dll"
else:
"pcre32.dll"
elif hostOS == "macosx":
"libpcre(.3|.1|).dylib"
else:
"libpcre.so(.3|.1|)"
cPlugin:
import strutils
proc onSymbol*(sym: var Symbol) {.exportc, dynlib.} =
sym.name = sym.name.replace("pcre_", "")
cImport(pcreH, dynlib="dynpcre")
echo version()