Dynlib support
This commit is contained in:
commit
2cdde94635
9 changed files with 126 additions and 41 deletions
|
|
@ -34,6 +34,7 @@ task buildToast, "build toast":
|
||||||
proc testAll() =
|
proc testAll() =
|
||||||
execTest "tests/tnimterop_c.nim"
|
execTest "tests/tnimterop_c.nim"
|
||||||
execCmd "nim cpp -r tests/tnimterop_cpp.nim"
|
execCmd "nim cpp -r tests/tnimterop_cpp.nim"
|
||||||
|
execTest "tests/tpcre.nim"
|
||||||
|
|
||||||
## platform specific tests
|
## platform specific tests
|
||||||
when defined(Windows):
|
when defined(Windows):
|
||||||
|
|
|
||||||
|
|
@ -151,14 +151,16 @@ proc printNim*(gState: State, fullpath: string, root: TSNode, astTable: AstTable
|
||||||
var
|
var
|
||||||
nimState = new(NimState)
|
nimState = new(NimState)
|
||||||
fp = fullpath.replace("\\", "/")
|
fp = fullpath.replace("\\", "/")
|
||||||
|
|
||||||
nimState.identifiers = newTable[string, string]()
|
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.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)
|
root.searchAst(astTable, nimState)
|
||||||
|
|
||||||
|
|
@ -169,8 +171,8 @@ proc printNim*(gState: State, fullpath: string, root: TSNode, astTable: AstTable
|
||||||
echo &"const{nimState.constStr}\n"
|
echo &"const{nimState.constStr}\n"
|
||||||
|
|
||||||
echo &"""
|
echo &"""
|
||||||
{{.pragma: {nimState.impHeader}, importc, header: {nimState.currentHeader}.}}
|
{{.pragma: {nimState.impShort}, importc{nimState.getHeader()}.}}
|
||||||
{{.pragma: {nimState.impHeader}C, {nimState.impHeader}, cdecl.}}
|
{{.pragma: {nimState.impShort}C, {nimState.impShort}, cdecl{nimState.getDynlib()}.}}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if nimState.typeStr.nBl:
|
if nimState.typeStr.nBl:
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ proc getNimCheckError(output: string): tuple[tmpFile, errors: string] =
|
||||||
|
|
||||||
result.errors = "\n\n" & check
|
result.errors = "\n\n" & check
|
||||||
|
|
||||||
proc getToast(fullpath: string, recurse: bool = false): string =
|
proc getToast(fullpath: string, recurse: bool = false, dynlib: string = ""): string =
|
||||||
var
|
var
|
||||||
ret = 0
|
ret = 0
|
||||||
cmd = when defined(Windows): "cmd /c " else: ""
|
cmd = when defined(Windows): "cmd /c " else: ""
|
||||||
|
|
@ -131,6 +131,9 @@ proc getToast(fullpath: string, recurse: bool = false): string =
|
||||||
for i in gStateCT.includeDirs:
|
for i in gStateCT.includeDirs:
|
||||||
cmd.add &" --includeDirs+={i.quoteShell}"
|
cmd.add &" --includeDirs+={i.quoteShell}"
|
||||||
|
|
||||||
|
if dynlib.len != 0:
|
||||||
|
cmd.add &" --dynlib={dynlib}"
|
||||||
|
|
||||||
if gStateCT.symOverride.len != 0:
|
if gStateCT.symOverride.len != 0:
|
||||||
cmd.add &" --symOverride={gStateCT.symOverride.join(\",\")}"
|
cmd.add &" --symOverride={gStateCT.symOverride.join(\",\")}"
|
||||||
|
|
||||||
|
|
@ -297,9 +300,6 @@ proc cDisableCaching*() {.compileTime.} =
|
||||||
|
|
||||||
gStateCT.nocache = true
|
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 =
|
macro cDefine*(name: static string, val: static string = ""): untyped =
|
||||||
## ``#define`` an identifer that is forwarded to the C/C++ compiler
|
## ``#define`` an identifer that is forwarded to the C/C++ compiler
|
||||||
## using ``{.passC: "-DXXX".}``
|
## using ``{.passC: "-DXXX".}``
|
||||||
|
|
@ -462,7 +462,7 @@ macro cCompile*(path: static string, mode = "c", exclude = ""): untyped =
|
||||||
if gStateCT.debug:
|
if gStateCT.debug:
|
||||||
echo result.repr
|
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
|
## Import all supported definitions from specified header file. Generated
|
||||||
## content is cached in ``nimcache`` until ``filename`` changes unless
|
## content is cached in ``nimcache`` until ``filename`` changes unless
|
||||||
## `cDisableCaching() <cimport.html#cDisableCaching,>`_ is set. ``nim -f``
|
## `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
|
## referenced in ``filename``. This is only done for files in the same
|
||||||
## directory as ``filename`` or in a directory added using
|
## directory as ``filename`` or in a directory added using
|
||||||
## `cIncludeDir() <cimport.html#cIncludeDir.m,>`_
|
## `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)
|
result = newNimNode(nnkStmtList)
|
||||||
|
|
||||||
|
|
@ -481,7 +504,7 @@ macro cImport*(filename: static string, recurse: static bool = false): untyped =
|
||||||
echo "# Importing " & fullpath
|
echo "# Importing " & fullpath
|
||||||
|
|
||||||
let
|
let
|
||||||
output = getToast(fullpath, recurse)
|
output = getToast(fullpath, recurse, dynlib)
|
||||||
|
|
||||||
if gStateCT.debug:
|
if gStateCT.debug:
|
||||||
echo output
|
echo output
|
||||||
|
|
|
||||||
|
|
@ -338,6 +338,47 @@ proc getSplitComma*(joined: seq[string]): seq[string] =
|
||||||
for i in joined:
|
for i in joined:
|
||||||
result = result.concat(i.split(","))
|
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 =
|
proc dll*(path: string): string =
|
||||||
let
|
let
|
||||||
(dir, name, _) = path.splitFile()
|
(dir, name, _) = path.splitFile()
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ proc downloadUrl*(url, outdir: string) =
|
||||||
var cmd = if defined(Windows):
|
var cmd = if defined(Windows):
|
||||||
"powershell [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; wget $# -OutFile $#"
|
"powershell [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; wget $# -OutFile $#"
|
||||||
else:
|
else:
|
||||||
"curl $# -o $#"
|
"curl -L $# -o $#"
|
||||||
discard execAction(cmd % [url, (outdir/file).quoteShell])
|
discard execAction(cmd % [url, (outdir/file).quoteShell])
|
||||||
|
|
||||||
if ext == ".zip":
|
if ext == ".zip":
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ type
|
||||||
|
|
||||||
nocache*, nocomments*, debug*, past*, preprocess*, pnim*, pretty*, recurse*: bool
|
nocache*, nocomments*, debug*, past*, preprocess*, pnim*, pretty*, recurse*: bool
|
||||||
|
|
||||||
code*, mode*, pluginSourcePath*: string
|
code*, dynlib*, mode*, pluginSourcePath*: string
|
||||||
|
|
||||||
onSymbol*: OnSymbol
|
onSymbol*: OnSymbol
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@ type
|
||||||
|
|
||||||
gState*: State
|
gState*: State
|
||||||
|
|
||||||
currentHeader*, impHeader*, sourceFile*: string
|
currentHeader*, impShort*, sourceFile*: string
|
||||||
|
|
||||||
data*: seq[tuple[name, val: string]]
|
data*: seq[tuple[name, val: string]]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,27 +7,6 @@ import "."/[getters, globals, lisp, treesitter/api]
|
||||||
type
|
type
|
||||||
Grammar = seq[tuple[grammar: string, call: proc(ast: ref Ast, node: TSNode, nimState: NimState) {.nimcall.}]]
|
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 =
|
proc initGrammar(): Grammar =
|
||||||
# #define X Y
|
# #define X Y
|
||||||
result.add(("""
|
result.add(("""
|
||||||
|
|
@ -177,7 +156,7 @@ proc initGrammar(): Grammar =
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
let
|
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 typ.nBl and nname.nBl and nimState.addNewIdentifer(nname):
|
||||||
if i < nimState.data.len and nimState.data[^1].name == "function_declarator":
|
if i < nimState.data.len and nimState.data[^1].name == "function_declarator":
|
||||||
|
|
@ -234,7 +213,7 @@ proc initGrammar(): Grammar =
|
||||||
else:
|
else:
|
||||||
if nimState.addNewIdentifer(ndname):
|
if nimState.addNewIdentifer(ndname):
|
||||||
let
|
let
|
||||||
pragma = nimState.genPragma(nimState.genImportc(dname, ndname), "bycopy")
|
pragma = nimState.getPragma(nimState.getImportc(dname, ndname), "bycopy")
|
||||||
nimState.typeStr &=
|
nimState.typeStr &=
|
||||||
&"{nimState.getComments()}\n {ndname}*{pragma} = {dptr}{nname}"
|
&"{nimState.getComments()}\n {ndname}*{pragma} = {dptr}{nname}"
|
||||||
|
|
||||||
|
|
@ -274,7 +253,7 @@ proc initGrammar(): Grammar =
|
||||||
nimState.typeStr &= &"{nimState.getComments()}\n {nname}* {{.bycopy.}} = object{union}"
|
nimState.typeStr &= &"{nimState.getComments()}\n {nname}* {{.bycopy.}} = object{union}"
|
||||||
else:
|
else:
|
||||||
let
|
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}"
|
nimState.typeStr &= &"{nimState.getComments()}\n {nname}*{pragma} = object{union}"
|
||||||
|
|
||||||
var
|
var
|
||||||
|
|
@ -586,7 +565,7 @@ proc initGrammar(): Grammar =
|
||||||
if fnname.nBl and nimState.addNewIdentifer(fnname):
|
if fnname.nBl and nimState.addNewIdentifer(fnname):
|
||||||
let
|
let
|
||||||
ftyp = nimState.getIdentifier(nimState.data[0].val, nskType, fnname)
|
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":
|
if fptr.len != 0 or ftyp != "object":
|
||||||
nimState.procStr &= &"{nimState.getComments()}\nproc {fnname}*({pout}): {getPtrType(fptr&ftyp)}{pragma}"
|
nimState.procStr &= &"{nimState.getComments()}\nproc {fnname}*({pout}): {getPtrType(fptr&ftyp)}{pragma}"
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@ proc main(
|
||||||
nocomments = false,
|
nocomments = false,
|
||||||
defines: seq[string] = @[],
|
defines: seq[string] = @[],
|
||||||
includeDirs: seq[string] = @[],
|
includeDirs: seq[string] = @[],
|
||||||
|
dynlib: string = "",
|
||||||
symOverride: seq[string] = @[],
|
symOverride: seq[string] = @[],
|
||||||
pluginSourcePath: string = "",
|
pluginSourcePath: string = "",
|
||||||
debug = false,
|
debug = false,
|
||||||
|
|
@ -121,6 +122,7 @@ proc main(
|
||||||
nocomments: nocomments,
|
nocomments: nocomments,
|
||||||
defines: defines,
|
defines: defines,
|
||||||
includeDirs: includeDirs,
|
includeDirs: includeDirs,
|
||||||
|
dynlib: dynlib,
|
||||||
symOverride: symOverride,
|
symOverride: symOverride,
|
||||||
pluginSourcePath: pluginSourcePath,
|
pluginSourcePath: pluginSourcePath,
|
||||||
debug: debug,
|
debug: debug,
|
||||||
|
|
@ -153,6 +155,7 @@ when isMainModule:
|
||||||
"nocomments": "exclude top-level comments from output",
|
"nocomments": "exclude top-level comments from output",
|
||||||
"defines": "definitions to pass to preprocessor",
|
"defines": "definitions to pass to preprocessor",
|
||||||
"includeDirs": "include directory 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",
|
"symOverride": "skip generating specified symbols",
|
||||||
"pluginSourcePath": "Nim file to build and load as a plugin",
|
"pluginSourcePath": "Nim file to build and load as a plugin",
|
||||||
"debug": "enable debug output",
|
"debug": "enable debug output",
|
||||||
|
|
@ -167,6 +170,7 @@ when isMainModule:
|
||||||
"nocomments": 'c',
|
"nocomments": 'c',
|
||||||
"defines": 'D',
|
"defines": 'D',
|
||||||
"includeDirs": 'I',
|
"includeDirs": 'I',
|
||||||
|
"dynlib": 'l',
|
||||||
"symOverride": 'O',
|
"symOverride": 'O',
|
||||||
"debug": 'd',
|
"debug": 'd',
|
||||||
"pgrammar": 'g'
|
"pgrammar": 'g'
|
||||||
|
|
|
||||||
35
tests/tpcre.nim
Normal file
35
tests/tpcre.nim
Normal 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()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue