Forward pragmas to toast
This commit is contained in:
parent
27fcecaa26
commit
6ffacf92fc
15 changed files with 162 additions and 306 deletions
|
|
@ -139,6 +139,8 @@ cImport("clib.h") # Generate wrappers for header specified
|
|||
cCompile("clib/src/*.c") # Compile in any implementation source files
|
||||
```
|
||||
|
||||
All `{.compileTime.}` procs must be used in a compile time context, like `cDebug()` and `cDisableCaching()` above.
|
||||
|
||||
Module documentation for the wrapper API can be found [here](https://nimterop.github.io/nimterop/cimport.html).
|
||||
|
||||
#### Preprocessing
|
||||
|
|
|
|||
|
|
@ -79,10 +79,6 @@ proc jsonGet(url: string): JsonNode =
|
|||
discard
|
||||
rmFile(file)
|
||||
|
||||
template fixOutDir() {.dirty.} =
|
||||
let
|
||||
outdir = if outdir.isAbsolute(): outdir else: getProjectDir() / outdir
|
||||
|
||||
proc `==`*(pkg1, pkg2: ConanPackage): bool =
|
||||
## Check if two ConanPackage objects are equal
|
||||
(not pkg1.isNil and not pkg2.isNil and
|
||||
|
|
@ -282,9 +278,8 @@ proc getConanRevisions*(pkg: ConanPackage, bld: ConanBuild) =
|
|||
|
||||
proc loadConanInfo*(outdir: string): ConanPackage =
|
||||
## Load cached package info from `outdir/conaninfo.json`
|
||||
fixOutDir()
|
||||
let
|
||||
file = outdir / conanInfo
|
||||
file = fixRelPath(outdir) / conanInfo
|
||||
|
||||
if fileExists(file):
|
||||
when (NimMajor, NimMinor, NimPatch) < (1, 2, 0):
|
||||
|
|
@ -297,9 +292,8 @@ proc loadConanInfo*(outdir: string): ConanPackage =
|
|||
|
||||
proc saveConanInfo*(pkg: ConanPackage, outdir: string) =
|
||||
## Save downloaded package info to `outdir/conaninfo.json`
|
||||
fixOutDir()
|
||||
let
|
||||
file = outdir / conanInfo
|
||||
file = fixRelPath(outdir) / conanInfo
|
||||
|
||||
when (NimMajor, NimMinor, NimPatch) < (1, 2, 0):
|
||||
writeFile(file, $$pkg)
|
||||
|
|
@ -329,11 +323,11 @@ proc dlConanBuild*(pkg: ConanPackage, bld: ConanBuild, outdir: string, revision
|
|||
## Download specific `revision` of `bld` to `outdir`
|
||||
##
|
||||
## If omitted, the latest revision (first) is downloaded
|
||||
fixOutDir()
|
||||
|
||||
doAssert bld.revisions.len != 0, "No build revisions found for Conan.io package " & pkg.getUriFromConanPackage()
|
||||
|
||||
let
|
||||
outdir = fixRelPath(outdir)
|
||||
|
||||
revision =
|
||||
if revision.len != 0:
|
||||
revision
|
||||
|
|
@ -376,8 +370,9 @@ proc downloadConan*(pkg: ConanPackage, outdir: string, main = true) =
|
|||
##
|
||||
## High-level API that handles the end to end Conan process flow to find
|
||||
## latest package binary and downloads and extracts it to `outdir`.
|
||||
fixOutDir()
|
||||
let
|
||||
outdir = fixRelPath(outdir)
|
||||
|
||||
pkg =
|
||||
if pkg.version.len == 0:
|
||||
searchConan(pkg)
|
||||
|
|
@ -415,7 +410,8 @@ proc dlConanRequires*(pkg: ConanPackage, bld: ConanBuild, outdir: string) =
|
|||
##
|
||||
## This is not required for shared libs since conan builds them
|
||||
## with all dependencies statically linked in
|
||||
fixOutDir()
|
||||
let
|
||||
outdir = fixRelPath(outdir)
|
||||
if bld.options["shared"] == "False":
|
||||
for req in bld.requires:
|
||||
let
|
||||
|
|
|
|||
|
|
@ -33,10 +33,6 @@ var
|
|||
# Reuse dependencies already downloaded
|
||||
gJBBRequires {.compileTime.}: Table[string, JBBPackage]
|
||||
|
||||
template fixOutDir() {.dirty.} =
|
||||
let
|
||||
outdir = if outdir.isAbsolute(): outdir else: getProjectDir() / outdir
|
||||
|
||||
proc `==`*(pkg1, pkg2: JBBPackage): bool =
|
||||
## Check if two JBBPackage objects are equal
|
||||
(not pkg1.isNil and not pkg2.isNil and
|
||||
|
|
@ -164,9 +160,8 @@ proc getJBBRepo*(pkg: JBBPackage, outdir: string) =
|
|||
|
||||
proc loadJBBInfo*(outdir: string): JBBPackage =
|
||||
## Load cached package info from `outdir/jbbinfo.json`
|
||||
fixOutDir()
|
||||
let
|
||||
file = outdir / jbbInfo
|
||||
file = fixRelPath(outdir) / jbbInfo
|
||||
|
||||
if fileExists(file):
|
||||
when (NimMajor, NimMinor, NimPatch) < (1, 2, 0):
|
||||
|
|
@ -179,9 +174,8 @@ proc loadJBBInfo*(outdir: string): JBBPackage =
|
|||
|
||||
proc saveJBBInfo*(pkg: JBBPackage, outdir: string) =
|
||||
## Save downloaded package info to `outdir/jbbinfo.json`
|
||||
fixOutDir()
|
||||
let
|
||||
file = outdir / jbbInfo
|
||||
file = fixRelPath(outdir) / jbbInfo
|
||||
|
||||
when (NimMajor, NimMinor, NimPatch) < (1, 2, 0):
|
||||
writeFile(file, $$pkg)
|
||||
|
|
@ -194,7 +188,9 @@ proc downloadJBB*(pkg: JBBPackage, outdir: string, main = true) =
|
|||
##
|
||||
## High-level API that handles the end to end JBB process flow to find
|
||||
## latest package binary and downloads and extracts it to `outdir`.
|
||||
fixOutDir()
|
||||
let
|
||||
outdir = fixRelPath(outdir)
|
||||
|
||||
if main:
|
||||
let
|
||||
cpkg = loadJBBInfo(outdir)
|
||||
|
|
@ -226,7 +222,8 @@ proc downloadJBB*(pkg: JBBPackage, outdir: string, main = true) =
|
|||
|
||||
proc dlJBBRequires*(pkg: JBBPackage, outdir: string) =
|
||||
## Download all required dependencies of this `pkg`
|
||||
fixOutDir()
|
||||
let
|
||||
outdir = fixRelPath(outdir)
|
||||
for i in 0 ..< pkg.requires.len:
|
||||
let
|
||||
rpkg = pkg.requires[i]
|
||||
|
|
|
|||
|
|
@ -234,3 +234,7 @@ proc getOutDir*(projectDir = ""): string =
|
|||
proc getNimteropCacheDir*(): string =
|
||||
## Get location to cache all nimterop artifacts
|
||||
result = getNimcacheDir() / "nimterop"
|
||||
|
||||
proc fixRelPath*(path: string): string =
|
||||
## If `path` is relative, consider relative to `projectPath`
|
||||
if path.isAbsolute: path else: getProjectDir() / path
|
||||
|
|
@ -1,78 +1,17 @@
|
|||
##[
|
||||
This is the main nimterop import file to help with wrapping C/C++ source code.
|
||||
|
||||
Check out `template.nim <https://github.com/nimterop/nimterop/blob/master/nimterop/template.nim>`_
|
||||
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:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static:
|
||||
cAddStdDir()
|
||||
|
||||
]##
|
||||
|
||||
import hashes, macros, os, strformat, strutils
|
||||
|
||||
import "."/[globals, paths]
|
||||
import "."/build/[ccompiler, misc, nimconf, shell]
|
||||
|
||||
proc interpPath(dir: string): string=
|
||||
# TODO: more robust: needs a DirSep after "$projpath"
|
||||
# disabling this interpolation as this is error prone, but other less
|
||||
# interpolations can be added, eg see https://github.com/nim-lang/Nim/pull/10530
|
||||
# result = dir.replace("$projpath", getProjectPath())
|
||||
result = dir
|
||||
|
||||
proc joinPathIfRel(path1: string, path2: string): string =
|
||||
if path2.isAbsolute:
|
||||
result = path2
|
||||
else:
|
||||
result = joinPath(path1, path2)
|
||||
|
||||
proc findPath(path: string, fail = true): string =
|
||||
# Relative to project path
|
||||
result = joinPathIfRel(getProjectPath(), path).replace("\\", "/")
|
||||
let
|
||||
path = fixRelPath(path)
|
||||
result = path.replace("\\", "/")
|
||||
if not fileExists(result) and not dirExists(result):
|
||||
doAssert (not fail), "File or directory not found: " & path
|
||||
result = ""
|
||||
|
||||
proc walkDirImpl(indir, inext: string, file=true): seq[string] =
|
||||
let
|
||||
dir = joinPathIfRel(getProjectPath(), indir)
|
||||
ext =
|
||||
if inext.nBl:
|
||||
when not defined(Windows):
|
||||
"-name " & inext
|
||||
else:
|
||||
"\\" & inext
|
||||
else:
|
||||
""
|
||||
|
||||
let
|
||||
cmd =
|
||||
when defined(Windows):
|
||||
if file:
|
||||
"cmd /c dir /s/b/a-d " & dir.replace("/", "\\") & ext
|
||||
else:
|
||||
"cmd /c dir /s/b/ad " & dir.replace("/", "\\")
|
||||
else:
|
||||
if file:
|
||||
"find $1 -type f $2" % [dir, ext]
|
||||
else:
|
||||
"find $1 -type d" % dir
|
||||
|
||||
(output, ret) = execAction(cmd, die = false)
|
||||
|
||||
if ret == 0:
|
||||
result = output.splitLines()
|
||||
|
||||
proc fixRelFile(file: string): string =
|
||||
if file.isAbsolute(): file else: getProjectDir() / file
|
||||
|
||||
proc getCacheValue(fullpath: string): string =
|
||||
if not gStateCT.nocache:
|
||||
result = fullpath.getFileDate()
|
||||
|
|
@ -123,6 +62,15 @@ proc getToast(fullpaths: seq[string], recurse: bool = false, dynlib: string = ""
|
|||
for i in gStateCT.exclude:
|
||||
cmd.add &" --exclude+={i.sanitizePath}"
|
||||
|
||||
for i in gStateCT.passC:
|
||||
cmd.add &" --passC+={i.quoteShell}"
|
||||
|
||||
for i in gStateCT.passL:
|
||||
cmd.add &" --passL+={i.quoteShell}"
|
||||
|
||||
for i in gStateCT.compile:
|
||||
cmd.add &" --compile+={i.sanitizePath}"
|
||||
|
||||
if not noNimout:
|
||||
cmd.add &" --pnim"
|
||||
|
||||
|
|
@ -147,7 +95,7 @@ proc getToast(fullpaths: seq[string], recurse: bool = false, dynlib: string = ""
|
|||
($(cmd & cacheKey).hash().abs()).addFileExt(ext)
|
||||
|
||||
if outFile.nBl:
|
||||
result = fixRelFile(outFile)
|
||||
result = fixRelPath(outFile)
|
||||
else:
|
||||
result = cacheFile
|
||||
|
||||
|
|
@ -384,7 +332,7 @@ macro cPluginPath*(path: static[string]): untyped =
|
|||
doAssert fileExists(path), "Plugin file not found: " & path
|
||||
cPluginHelper(readFile(path), imports = "")
|
||||
|
||||
proc cSearchPath*(path: string): string {.compileTime.}=
|
||||
proc cSearchPath*(path: string): string {.compileTime.} =
|
||||
## Get full path to file or directory `path` in search path configured
|
||||
## using `cAddSearchDir()` and `cAddStdDir()`.
|
||||
##
|
||||
|
|
@ -416,27 +364,38 @@ proc cDisableCaching*() {.compileTime.} =
|
|||
## `nim -f` can also be used to flush the cached content.
|
||||
gStateCT.nocache = true
|
||||
|
||||
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++ preprocessor if
|
||||
## called within `cImport()` or `c2nImport()` as well as to the C/C++
|
||||
## compiler during Nim compilation using `{.passC: "-DXXX".}`
|
||||
result = newNimNode(nnkStmtList)
|
||||
|
||||
var str = name
|
||||
# todo: see https://github.com/nimterop/nimterop/issues/100 for
|
||||
# edge case of empty strings
|
||||
if val.nBl:
|
||||
str &= &"={val.quoteShell}"
|
||||
|
||||
if str notin gStateCT.defines:
|
||||
gStateCT.defines.add(str)
|
||||
str = "-D" & str
|
||||
|
||||
result.add quote do:
|
||||
{.passC: `str`.}
|
||||
macro cDefine*(values: static seq[string]): untyped =
|
||||
## `#define` multiple identifers that are forwarded to the C/C++ preprocessor
|
||||
## if called within `cImport()` or `c2nImport()` as well as to the C/C++
|
||||
## compiler during Nim compilation using `{.passC: "-DXXX".}`
|
||||
for value in values:
|
||||
let
|
||||
spl = value.split("=", maxsplit = 1)
|
||||
name = spl[0]
|
||||
val = if spl.len == 2: spl[1] else: ""
|
||||
discard quote do:
|
||||
cDefine(`name`, `val`)
|
||||
|
||||
if gStateCT.debug:
|
||||
gecho result.repr & "\n"
|
||||
macro cPassC*(value: static string): untyped =
|
||||
## Create a `{.passC.}` entry that gets forwarded to the C/C++ compiler
|
||||
## during Nim compilation.
|
||||
gStateCT.passC.add value
|
||||
|
||||
macro cPassL*(value: static string): untyped =
|
||||
## Create a `{.passL.}` entry that gets forwarded to the C/C++ compiler
|
||||
## during Nim compilation.
|
||||
gStateCT.passL.add value
|
||||
|
||||
proc cAddSearchDir*(dir: string) {.compileTime.} =
|
||||
## Add directory `dir` to the search path used in calls to
|
||||
|
|
@ -445,8 +404,8 @@ proc cAddSearchDir*(dir: string) {.compileTime.} =
|
|||
import nimterop/paths, os
|
||||
static:
|
||||
cAddSearchDir testsIncludeDir()
|
||||
doAssert cSearchPath("test.h").existsFile
|
||||
var dir = interpPath(dir)
|
||||
doAssert cSearchPath("test.h").fileExists
|
||||
|
||||
if dir notin gStateCT.searchDirs:
|
||||
gStateCT.searchDirs.add(dir)
|
||||
|
||||
|
|
@ -458,19 +417,11 @@ macro cIncludeDir*(dirs: static seq[string], exclude: static[bool] = false): unt
|
|||
## Set `exclude = true` if the contents of these include directories should
|
||||
## not be included in the wrapped output.
|
||||
for dir in dirs:
|
||||
var dir = interpPath(dir)
|
||||
result = newNimNode(nnkStmtList)
|
||||
|
||||
let fullpath = findPath(dir)
|
||||
if fullpath notin gStateCT.includeDirs:
|
||||
gStateCT.includeDirs.add(fullpath)
|
||||
if exclude:
|
||||
gStateCT.exclude.add(fullpath)
|
||||
let str = &"-I{fullpath.quoteShell}"
|
||||
result.add quote do:
|
||||
{.passC: `str`.}
|
||||
if gStateCT.debug:
|
||||
gecho result.repr
|
||||
|
||||
macro cIncludeDir*(dir: static[string], exclude: static[bool] = false): untyped =
|
||||
## Add an include directory that is forwarded to the C/C++ preprocessor if
|
||||
|
|
@ -501,16 +452,17 @@ proc cAddStdDir*(mode = "c") {.compileTime.} =
|
|||
## Add the standard `c` [default] or `cpp` include paths to search
|
||||
## path used in calls to `cSearchPath()`.
|
||||
runnableExamples:
|
||||
static: cAddStdDir()
|
||||
import os
|
||||
doAssert cSearchPath("math.h").existsFile
|
||||
static:
|
||||
cAddStdDir()
|
||||
doAssert cSearchPath("math.h").fileExists
|
||||
for inc in getGccPaths(mode):
|
||||
cAddSearchDir inc
|
||||
|
||||
macro cCompile*(path: static string, mode = "c", exclude = ""): untyped =
|
||||
macro cCompile*(path: static string, mode: static[string] = "c", exclude: static[string] = ""): untyped =
|
||||
## 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 `*` wildcard for filename:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
|
|
@ -532,26 +484,21 @@ macro cCompile*(path: static string, mode = "c", exclude = ""): untyped =
|
|||
##
|
||||
## cCompile("path/to/dir", exclude="test2.c")
|
||||
|
||||
result = newNimNode(nnkStmtList)
|
||||
|
||||
var
|
||||
stmt = ""
|
||||
|
||||
proc fcompile(file: string): string =
|
||||
proc fcompile(file: string) =
|
||||
let
|
||||
(_, fn, ext) = file.splitFile()
|
||||
var
|
||||
ufn = fn
|
||||
uniq = 1
|
||||
while ufn in gStateCT.compile:
|
||||
while ufn in gStateCT.compcache:
|
||||
ufn = fn & $uniq
|
||||
uniq += 1
|
||||
|
||||
# - https://github.com/nim-lang/Nim/issues/10299
|
||||
# - https://github.com/nim-lang/Nim/issues/10486
|
||||
gStateCT.compile.add(ufn)
|
||||
gStateCT.compcache.add(ufn)
|
||||
if fn == ufn:
|
||||
return "{.compile: \"$#\".}\n" % file.replace("\\", "/")
|
||||
gStateCT.compile.add file.replace("\\", "/")
|
||||
else:
|
||||
# - https://github.com/nim-lang/Nim/issues/9370
|
||||
let
|
||||
|
|
@ -559,7 +506,7 @@ macro cCompile*(path: static string, mode = "c", exclude = ""): untyped =
|
|||
tmpFile = file.parentDir() / &"_nimterop_{$hash}_{ufn}{ext}"
|
||||
if not tmpFile.fileExists() or file.getFileDate() > tmpFile.getFileDate():
|
||||
cpFile(file, tmpFile)
|
||||
return "{.compile: \"$#\".}\n" % tmpFile.replace("\\", "/")
|
||||
gStateCT.compile.add tmpFile.replace("\\", "/")
|
||||
|
||||
# Due to https://github.com/nim-lang/Nim/issues/9863
|
||||
# cannot use seq[string] for excludes
|
||||
|
|
@ -572,33 +519,37 @@ macro cCompile*(path: static string, mode = "c", exclude = ""): untyped =
|
|||
if excl in file:
|
||||
result = false
|
||||
|
||||
proc dcompile(dir, exclude: string, ext=""): string =
|
||||
proc dcompile(dir, exclude: string, ext="") =
|
||||
let
|
||||
files = walkDirImpl(dir, ext)
|
||||
(dir, pat) =
|
||||
if "*" in dir:
|
||||
dir.splitPath()
|
||||
else:
|
||||
(dir, "")
|
||||
|
||||
for f in files:
|
||||
if f.nBl and f.notExcluded(exclude):
|
||||
result &= fcompile(f)
|
||||
for file in walkDirRec(dir):
|
||||
if ext.nBl or pat.nBl:
|
||||
let
|
||||
fext = file.splitFile().ext
|
||||
if (ext.nBl and fext != ext) or (pat.nBl and fext != pat[1 .. ^1]):
|
||||
continue
|
||||
if file.notExcluded(exclude):
|
||||
fcompile(file)
|
||||
|
||||
if path.contains("*") or path.contains("?"):
|
||||
stmt &= dcompile(path, exclude.strVal())
|
||||
if "*" in path:
|
||||
dcompile(path, exclude)
|
||||
else:
|
||||
let fpath = findPath(path)
|
||||
if fileExists(fpath) and fpath.notExcluded(exclude.strVal()):
|
||||
stmt &= fcompile(fpath)
|
||||
if fileExists(fpath) and fpath.notExcluded(exclude):
|
||||
fcompile(fpath)
|
||||
elif dirExists(fpath):
|
||||
if mode.strVal().contains("cpp"):
|
||||
for i in @["*.cpp", "*.c++", "*.cc", "*.cxx"]:
|
||||
stmt &= dcompile(fpath, exclude.strVal(), i)
|
||||
if mode.contains("cpp"):
|
||||
for i in @[".cpp", ".c++", ".cc", ".cxx"]:
|
||||
dcompile(fpath, exclude, i)
|
||||
when not defined(Windows):
|
||||
stmt &= dcompile(fpath, exclude.strVal(), "*.C")
|
||||
dcompile(fpath, exclude, ".C")
|
||||
else:
|
||||
stmt &= dcompile(fpath, exclude.strVal(), "*.c")
|
||||
|
||||
result.add stmt.parseStmt()
|
||||
|
||||
if gStateCT.debug:
|
||||
gecho result.repr
|
||||
dcompile(fpath, exclude, ".c")
|
||||
|
||||
macro cImport*(filenames: static seq[string], recurse: static bool = false, dynlib: static string = "",
|
||||
mode: static string = "c", flags: static string = "", nimFile: static string = ""): untyped =
|
||||
|
|
@ -726,7 +677,7 @@ macro c2nImport*(filename: static string, recurse: static bool = false, dynlib:
|
|||
|
||||
let
|
||||
hFile = getToast(@[fullpath], recurse, dynlib, mode, noNimout = true)
|
||||
nimFile = if nimFile.nBl: fixRelFile(nimFile) else: hFile.changeFileExt("nim")
|
||||
nimFile = if nimFile.nBl: fixRelPath(nimFile) else: hFile.changeFileExt("nim")
|
||||
header = "header" & fullpath.splitFile().name.split(seps = {'-', '.'}).join()
|
||||
|
||||
if not fileExists(nimFile) or gStateCT.nocache or compileOption("forceBuild"):
|
||||
|
|
@ -741,8 +692,29 @@ macro c2nImport*(filename: static string, recurse: static bool = false, dynlib:
|
|||
if flags.nBl:
|
||||
cmd.add &" {flags}"
|
||||
|
||||
# Have to create pragmas for c2nim since toast handles this at runtime
|
||||
for i in gStateCT.defines:
|
||||
cmd.add &" --assumedef:{i.quoteShell}"
|
||||
let str = "-D" & i
|
||||
result.add quote do:
|
||||
{.passC: `str`.}
|
||||
|
||||
for i in gStateCT.includeDirs:
|
||||
let str = &"-I{i.quoteShell}"
|
||||
result.add quote do:
|
||||
{.passC: `str`.}
|
||||
|
||||
for i in gStateCT.passC:
|
||||
result.add quote do:
|
||||
{.passC: `i`.}
|
||||
|
||||
for i in gStateCT.passL:
|
||||
result.add quote do:
|
||||
{.passL: `i`.}
|
||||
|
||||
for i in gStateCT.compile:
|
||||
result.add quote do:
|
||||
{.compile: `i`.}
|
||||
|
||||
let
|
||||
(c2nimout, ret) = execAction(cmd)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ type
|
|||
|
||||
State* = ref object
|
||||
# Command line arguments to toast - some forwarded from cimport.nim
|
||||
compile*: seq[string] # `--compile` to create `{.compile.}` entries in generated wrapper
|
||||
convention*: string # `--convention | -C` to change calling convention from cdecl default
|
||||
debug*: bool # `cDebug()` or `--debug | -d` to enable debug mode
|
||||
defines*: seq[string] # Symbols added by `cDefine()` and `--define | -D` for C/C++ preprocessor/compiler
|
||||
|
|
@ -26,6 +27,8 @@ type
|
|||
nim*: string # `--nim` to specify full path to Nim compiler
|
||||
noComments*: bool # `--noComments | -c` to disable rendering comments in wrappers
|
||||
noHeader*: bool # `--noHeader | -H` to skip {.header.} pragma in wrapper
|
||||
passC*: seq[string] # `--passC` to create `{.passC.}` entries in the generated wrapper
|
||||
passL*: seq[string] # `--passL` to create `{.passL.}` entries in the generated wrapper
|
||||
past*: bool # `--past | -a` to print tree-sitter AST of code
|
||||
pluginSourcePath*: string # `--pluginSourcePath` specified path to plugin file to compile and load
|
||||
pnim*: bool # `--pnim | -n` to render Nim wrapper for header
|
||||
|
|
@ -77,7 +80,7 @@ type
|
|||
wrapperHeader*: string
|
||||
else:
|
||||
# cimport.nim specific
|
||||
compile*: seq[string] # `cCompile()` list of files already processed
|
||||
compcache*: seq[string] # `cCompile()` list of files already processed
|
||||
nocache*: bool # `cDisableCaching()` to disable caching of artifacts
|
||||
overrides*: string # `cOverride()` code which gets added to `cPlugin()` output
|
||||
pluginSource*: string # `cPlugin()` generated code to write to plugin file from
|
||||
|
|
|
|||
|
|
@ -1,111 +0,0 @@
|
|||
import os, strutils
|
||||
|
||||
import nimterop/[cimport, build, paths]
|
||||
|
||||
# Documentation:
|
||||
# https://github.com/nimterop/nimterop
|
||||
# https://nimterop.github.io/nimterop/cimport.html
|
||||
|
||||
const
|
||||
# Location where any sources should get downloaded. Adjust depending on
|
||||
# actual location of wrapper file relative to project.
|
||||
baseDir = currentSourcePath.parentDir()/"build"
|
||||
|
||||
# All files and dirs should be inside to baseDir
|
||||
srcDir = baseDir/"project"
|
||||
|
||||
static:
|
||||
# Print generated Nim to output
|
||||
cDebug()
|
||||
|
||||
# Disable caching so that wrapper is generated every time. Useful during
|
||||
# development. Remove once wrapper is working as expected.
|
||||
cDisableCaching()
|
||||
|
||||
# Download C/C++ source code from a git repository
|
||||
gitPull("https://github.com/user/project", outdir = srcDir, plist = """
|
||||
include/*.h
|
||||
src/*.c
|
||||
""", checkout = "tag/branch/hash")
|
||||
|
||||
# Download source from the web - zip files are auto extracted
|
||||
downloadUrl("https://hostname.com/file.h", outdir = srcDir)
|
||||
|
||||
# Run GNU configure on the source
|
||||
when defined(posix):
|
||||
configure(srcDir, fileThatShouldGetGenerated, flagsToConfigure)
|
||||
|
||||
# Run cmake on the source
|
||||
cmake(srcDir/"build", fileThatShouldGetGenerated, flagsToCmake)
|
||||
|
||||
# Run standard file/directory operations with mkDir(), cpFile(), mvFile()
|
||||
|
||||
# Edit file contents if required with readFile(), writeFile() and standard
|
||||
# string operations
|
||||
|
||||
# Run any other external commands with execAction()
|
||||
|
||||
# Skip any symbols from being wrapped
|
||||
cSkipSymbol(@["type1", "proc2"])
|
||||
|
||||
# Manually wrap any symbols since nimterop cannot or incorrectly wraps them
|
||||
cOverride:
|
||||
# Standard Nim code to wrap types, consts, procs, etc.
|
||||
type
|
||||
symbol = object
|
||||
|
||||
# Specify include directories for gcc and Nim
|
||||
cIncludeDir(srcDir/"include")
|
||||
|
||||
# Define global symbols
|
||||
cDefine("SYMBOL", "value")
|
||||
|
||||
# Any global compiler options
|
||||
{.passC: "flags".}
|
||||
|
||||
# Any global linker options
|
||||
{.passL: "flags".}
|
||||
|
||||
# Compile in any common source code
|
||||
cCompile(srcDir/"file.c")
|
||||
|
||||
# Perform OS specific tasks
|
||||
when defined(Windows):
|
||||
# Windows specific symbols, options and files
|
||||
|
||||
# Dynamic library to link against
|
||||
const dynlibFile =
|
||||
when defined(cpu64):
|
||||
"xyz64.dll"
|
||||
else:
|
||||
"xyz32.dll"
|
||||
elif defined(posix):
|
||||
# Common posix symbols, options and files
|
||||
|
||||
when defined(linux):
|
||||
# Linux specific
|
||||
const dynlibFile = "libxyz.so(.2|.1|)"
|
||||
elif defined(osx):
|
||||
# MacOSX specific
|
||||
const dynlibFile = "libxyz(.2|.1|).dylib"
|
||||
else:
|
||||
static: doAssert false
|
||||
else:
|
||||
static: doAssert false
|
||||
|
||||
# Use cPlugin() to make any symbol changes
|
||||
cPlugin:
|
||||
import strutils
|
||||
|
||||
# Symbol renaming examples
|
||||
proc onSymbol*(sym: var Symbol) {.exportc, dynlib.} =
|
||||
# Get rid of leading and trailing underscores
|
||||
sym.name = sym.name.strip(chars = {'_'})
|
||||
|
||||
# Remove prefixes or suffixes from procs
|
||||
if sym.kind == nskProc and sym.name.contains("SDL_"):
|
||||
sym.name = sym.name.replace("SDL_", "")
|
||||
|
||||
# Finally import wrapped header file. Recurse if #include files should also
|
||||
# be wrapped. Set dynlib if binding to dynamic library.
|
||||
cImport(srcDir/"include/file.h", recurse = true, dynlib="dynlibFile")
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
import os, strutils
|
||||
|
||||
import nimterop/[cimport, build, paths]
|
||||
|
||||
const
|
||||
baseDir = currentSourcePath.parentDir()/"build"
|
||||
|
||||
srcDir = baseDir/"project"
|
||||
|
||||
static:
|
||||
cDebug()
|
||||
cDisableCaching()
|
||||
|
||||
gitPull("https://github.com/user/project", outdir = srcDir, plist = """
|
||||
include/*.h
|
||||
src/*.c
|
||||
""", checkout = "tag/branch/hash")
|
||||
|
||||
downloadUrl("https://hostname.com/file.h", outdir = srcDir)
|
||||
|
||||
cIncludeDir(srcDir/"include")
|
||||
|
||||
cDefine("SYMBOL", "value")
|
||||
|
||||
{.passC: "flags".}
|
||||
{.passL: "flags".}
|
||||
|
||||
cCompile(srcDir/"file.c")
|
||||
|
||||
cPlugin:
|
||||
import strutils
|
||||
|
||||
proc onSymbol*(sym: var Symbol) {.exportc, dynlib.} =
|
||||
sym.name = sym.name.strip(chars = {'_'})
|
||||
|
||||
cImport(srcDir/"include/file.h", recurse = true)
|
||||
|
|
@ -34,6 +34,7 @@ proc process(gState: State, path: string) =
|
|||
# CLI processing with default values
|
||||
proc main(
|
||||
check = false,
|
||||
compile: seq[string] = @[],
|
||||
convention = "cdecl",
|
||||
debug = false,
|
||||
defines: seq[string] = @[],
|
||||
|
|
@ -46,6 +47,8 @@ proc main(
|
|||
noComments = false,
|
||||
noHeader = false,
|
||||
output = "",
|
||||
passC: seq[string] = @[],
|
||||
passL: seq[string] = @[],
|
||||
past = false,
|
||||
pluginSourcePath: string = "",
|
||||
pnim = false,
|
||||
|
|
@ -62,6 +65,7 @@ proc main(
|
|||
|
||||
# Setup global state with arguments
|
||||
gState = State(
|
||||
compile: compile,
|
||||
convention: convention,
|
||||
debug: debug,
|
||||
defines: defines,
|
||||
|
|
@ -73,6 +77,8 @@ proc main(
|
|||
nim: nim.sanitizePath,
|
||||
noComments: noComments,
|
||||
noHeader: noHeader,
|
||||
passC: passC,
|
||||
passL: passL,
|
||||
past: past,
|
||||
pluginSourcePath: pluginSourcePath,
|
||||
pnim: pnim,
|
||||
|
|
@ -220,6 +226,7 @@ when isMainModule:
|
|||
import cligen
|
||||
dispatch(main, help = {
|
||||
"check": "check generated wrapper with compiler",
|
||||
"compile": "create {.compile.} entries in generated wrapper",
|
||||
"convention": "calling convention for wrapped procs",
|
||||
"debug": "enable debug output",
|
||||
"defines": "definitions to pass to preprocessor",
|
||||
|
|
@ -232,6 +239,8 @@ when isMainModule:
|
|||
"noComments": "exclude top-level comments from output",
|
||||
"noHeader": "skip {.header.} pragma in wrapper",
|
||||
"output": "file to output content - default: stdout",
|
||||
"passC": "create {.passC.} entries in generated wrapper",
|
||||
"passL": "create {.passL.} entries in generated wrapper",
|
||||
"past": "print AST output",
|
||||
"pluginSourcePath": "nim file to build and load as a plugin",
|
||||
"pnim": "print Nim output",
|
||||
|
|
|
|||
|
|
@ -1881,11 +1881,31 @@ proc setupPragmas(gState: State, root: TSNode, fullpath: string) =
|
|||
gState.pragmaSection.add dynPragma
|
||||
count += 1
|
||||
|
||||
# Add `{.experimental: "codeReordering".} for #206
|
||||
# Only if not already done
|
||||
if gState.pragmaSection.len == count:
|
||||
# Only if not already done
|
||||
# Add `{.experimental: "codeReordering".} for #206
|
||||
gState.pragmaSection.add gState.newPragma(root, "experimental", newStrNode(nkStrLit, "codeReordering"))
|
||||
|
||||
# Create `{.passC.}` from defines
|
||||
for define in gState.defines:
|
||||
gState.pragmaSection.add gState.newPragma(root, "passC", newStrNode(nkStrLit, "-D" & define))
|
||||
|
||||
# Create `{.passC.}` from include directories
|
||||
for inc in gState.includeDirs:
|
||||
gState.pragmaSection.add gState.newPragma(root, "passC", newStrNode(nkStrLit, "-I" & inc.quoteShell))
|
||||
|
||||
# Create `{.passC.}` from passC
|
||||
for passC in gState.passC:
|
||||
gState.pragmaSection.add gState.newPragma(root, "passC", newStrNode(nkStrLit, passC))
|
||||
|
||||
# Create `{.passL.}` from passL
|
||||
for passL in gState.passL:
|
||||
gState.pragmaSection.add gState.newPragma(root, "passL", newStrNode(nkStrLit, passL))
|
||||
|
||||
# Create `{.compile.}` for specified files
|
||||
for file in gState.compile:
|
||||
gState.pragmaSection.add gState.newPragma(root, "compile", newStrNode(nkStrLit, file))
|
||||
|
||||
proc initNim*(gState: State) =
|
||||
# Initialize for parseNim() one time
|
||||
gState.wrapperHeader = "{.push hint[ConvFromXtoItselfNotNeeded]: off.}\n"
|
||||
|
|
|
|||
|
|
@ -22,13 +22,13 @@ when not libssh2Static:
|
|||
when not defined(Windows) and not isDefined(libssh2JBB):
|
||||
proc zlibVersion(): cstring {.importc, dynlib: libssh2LPath.}
|
||||
else:
|
||||
cPassL("-lpthread")
|
||||
|
||||
cImport(libssh2Path, recurse = true, flags = "-c -E_ -F_")
|
||||
|
||||
when not defined(Windows) and not isDefined(libssh2JBB):
|
||||
proc zlibVersion(): cstring {.importc.}
|
||||
|
||||
{.passL: "-lpthread".}
|
||||
|
||||
assert libssh2_init(0) == 0
|
||||
|
||||
let
|
||||
|
|
|
|||
|
|
@ -35,13 +35,13 @@ cPlugin:
|
|||
cOverride:
|
||||
proc OPENSSL_die*(assertion: cstring; file: cstring; line: cint) {.importc.}
|
||||
|
||||
cPassL(cryptoLPath)
|
||||
|
||||
# Skip comments for https://github.com/tree-sitter/tree-sitter-c/issues/44
|
||||
cImport(@[
|
||||
basePath / "rsa.h",
|
||||
basePath / "err.h",
|
||||
], recurse = true, flags = "-s -c " & FLAGS)
|
||||
|
||||
{.passL: cryptoLPath.}
|
||||
|
||||
OpensslInit()
|
||||
echo $OPENSSL_VERSION_TEXT
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import macros, os, sets, strutils
|
|||
|
||||
import nimterop/[cimport]
|
||||
|
||||
{.passC: "-DNIMTEROP".}
|
||||
cPassC("-DNIMTEROP")
|
||||
|
||||
static:
|
||||
# Skip casting on lower nim compilers because
|
||||
|
|
|
|||
|
|
@ -24,15 +24,15 @@ cIncludeDir(incl)
|
|||
|
||||
when defined(osx):
|
||||
cDefine("WITH_COREAUDIO")
|
||||
{.passL: "-framework CoreAudio -framework AudioToolbox".}
|
||||
cPassL("-framework CoreAudio -framework AudioToolbox")
|
||||
cCompile(src/"backend/coreaudio/*.cpp")
|
||||
elif defined(Linux):
|
||||
{.passL: "-lpthread".}
|
||||
cPassL("-lpthread")
|
||||
cDefine("WITH_OSS")
|
||||
cCompile(src/"backend/oss/*.cpp")
|
||||
elif defined(Windows):
|
||||
{.passC: "-msse".}
|
||||
{.passL: "-lwinmm".}
|
||||
cPassC("-msse")
|
||||
cPassL("-lwinmm")
|
||||
cDefine("WITH_WINMM")
|
||||
cCompile(src/"backend/winmm/*.cpp")
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -65,12 +65,12 @@ when zlibGit or zlibDL:
|
|||
when dirExists(baseDir / "buildcache"):
|
||||
cIncludeDir(baseDir / "buildcache")
|
||||
|
||||
when not zlibStatic:
|
||||
when not isDefined(zlibStatic):
|
||||
cImport(zlibPath, recurse = true, dynlib = "zlibLPath", flags = FLAGS)
|
||||
else:
|
||||
when isDefined(zlibJBB):
|
||||
cPassL("-no-pie")
|
||||
|
||||
cImport(zlibPath, recurse = true, flags = FLAGS)
|
||||
|
||||
echo "zlib version = " & $zlibVersion()
|
||||
|
||||
when isDefined(zlibJBB) and isDefined(zlibStatic):
|
||||
{.passL: "-no-pie".}
|
||||
echo "zlib version = " & $zlibVersion()
|
||||
Loading…
Add table
Add a link
Reference in a new issue