From 6ffacf92fc19d2cd0754e2409868a2dca4501e93 Mon Sep 17 00:00:00 2001 From: Ganesh Viswanathan Date: Tue, 14 Jul 2020 18:08:59 -0500 Subject: [PATCH] Forward pragmas to toast --- README.md | 2 + nimterop/build/conan.nim | 20 ++-- nimterop/build/jbb.nim | 17 ++- nimterop/build/nimconf.nim | 4 + nimterop/cimport.nim | 212 ++++++++++++++++--------------------- nimterop/globals.nim | 5 +- nimterop/template.nim | 111 ------------------- nimterop/templite.nim | 36 ------- nimterop/toast.nim | 9 ++ nimterop/toastlib/ast2.nim | 24 ++++- tests/libssh2.nim | 4 +- tests/rsa.nim | 4 +- tests/tast2.nim | 2 +- tests/tsoloud.nim | 8 +- tests/zlib.nim | 10 +- 15 files changed, 162 insertions(+), 306 deletions(-) delete mode 100644 nimterop/template.nim delete mode 100644 nimterop/templite.nim diff --git a/README.md b/README.md index 0e79a6f..cda3b5e 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/nimterop/build/conan.nim b/nimterop/build/conan.nim index 792de01..dd43dc9 100644 --- a/nimterop/build/conan.nim +++ b/nimterop/build/conan.nim @@ -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 diff --git a/nimterop/build/jbb.nim b/nimterop/build/jbb.nim index b9272c7..a430a55 100644 --- a/nimterop/build/jbb.nim +++ b/nimterop/build/jbb.nim @@ -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] diff --git a/nimterop/build/nimconf.nim b/nimterop/build/nimconf.nim index 3bbe521..7624bf2 100644 --- a/nimterop/build/nimconf.nim +++ b/nimterop/build/nimconf.nim @@ -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 \ No newline at end of file diff --git a/nimterop/cimport.nim b/nimterop/cimport.nim index aa7895a..6120cb5 100644 --- a/nimterop/cimport.nim +++ b/nimterop/cimport.nim @@ -1,78 +1,17 @@ -##[ -This is the main nimterop import file to help with wrapping C/C++ source code. - -Check out `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 `_ 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) diff --git a/nimterop/globals.nim b/nimterop/globals.nim index 89e403f..2900272 100644 --- a/nimterop/globals.nim +++ b/nimterop/globals.nim @@ -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 diff --git a/nimterop/template.nim b/nimterop/template.nim deleted file mode 100644 index c6e846f..0000000 --- a/nimterop/template.nim +++ /dev/null @@ -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") diff --git a/nimterop/templite.nim b/nimterop/templite.nim deleted file mode 100644 index 7d8eb5b..0000000 --- a/nimterop/templite.nim +++ /dev/null @@ -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) diff --git a/nimterop/toast.nim b/nimterop/toast.nim index 20dc79e..8695216 100644 --- a/nimterop/toast.nim +++ b/nimterop/toast.nim @@ -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", diff --git a/nimterop/toastlib/ast2.nim b/nimterop/toastlib/ast2.nim index 083d4b2..7f2b4ad 100644 --- a/nimterop/toastlib/ast2.nim +++ b/nimterop/toastlib/ast2.nim @@ -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" diff --git a/tests/libssh2.nim b/tests/libssh2.nim index 09343bc..940b2df 100644 --- a/tests/libssh2.nim +++ b/tests/libssh2.nim @@ -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 diff --git a/tests/rsa.nim b/tests/rsa.nim index f5c343c..ee36286 100644 --- a/tests/rsa.nim +++ b/tests/rsa.nim @@ -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 diff --git a/tests/tast2.nim b/tests/tast2.nim index 17edfb0..a410de4 100644 --- a/tests/tast2.nim +++ b/tests/tast2.nim @@ -2,7 +2,7 @@ import macros, os, sets, strutils import nimterop/[cimport] -{.passC: "-DNIMTEROP".} +cPassC("-DNIMTEROP") static: # Skip casting on lower nim compilers because diff --git a/tests/tsoloud.nim b/tests/tsoloud.nim index 4d9a28b..d594516 100644 --- a/tests/tsoloud.nim +++ b/tests/tsoloud.nim @@ -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: diff --git a/tests/zlib.nim b/tests/zlib.nim index 53384c3..d4744eb 100644 --- a/tests/zlib.nim +++ b/tests/zlib.nim @@ -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() \ No newline at end of file