diff --git a/CHANGES.md b/CHANGES.md index 70e1f76..23aa2bb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -41,12 +41,16 @@ https://github.com/nimterop/nimterop/compare/v0.5.9...v0.6.3 - `xxxJBB` now allows for customizing the base location to search packages with the `jbbFlags` param to `getHeader()`. Specifying `giturl=xxx` where `xxx` could be a full Git URL or just the username for Github.com allows changing the default Git repo. In addition, `url=xxx` is also supported to download project info and binaries compiled with BinaryBuilder.org but hosted at another non-Git location. (since v0.6.3) +- It is now possible to exclude the contents of specific files or entire directories from the wrapped output using `--exclude | -X` with `toast` or `cExclude()` from a wrapper. This might be required when a header uses `#include` to pull in external dependencies. E.g. `sciter` has a `#include ` which pulls in the entire GTK ecosystem which is needed for successful preprocessing but we do not want to include those headers in the wrapped output when using `--recurse | -r`. + ### Other improvements - Generated wrappers no longer depend on nimterop being present - no more `import nimterop/types`. Supporting code is directly included in the wrapper output and only when required. E.g. enum macro is only included if wrapper contains enums. [#125][i125] (since v0.6.1) - `cImport()` now includes wrapper output from a file rather than inline. Errors in generated wrappers will no longer point to a line in `macros.nim` making debugging easier. (since v0.6.1) +- `cIncludeDir()` can now accept a `seq[string]` of directories and an optional `exclude` param which sets those include directories to not be included in the wrapped output. + ## Version 0.5.0 diff --git a/README.md b/README.md index f9e9307..0e79a6f 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,7 @@ cDefine("HAS_ABC") # Set #defines for preprocessor and compiler cDefine("HAS_ABC", "DEF") cIncludeDir("clib/include") # Setup any include directories +cExclude("clib/file.h") # Exclude file from wrapped output cImport("clib.h") # Generate wrappers for header specified @@ -219,6 +220,7 @@ Options: -d, --debug bool false enable debug output -D=, --defines= strings {} definitions to pass to preprocessor -l=, --dynlib= string "" {.dynlib.} pragma to import symbols - Nim const string or file path + -X=, --exclude= strings {} files or directories to exclude from the wrapped output -f=, --feature= Features {} flags to enable experimental features -I=, --includeDirs= strings {} include directory to pass to preprocessor -m=, --mode= string "" language parser: c or cpp diff --git a/nimterop/cimport.nim b/nimterop/cimport.nim index cedd903..95004ad 100644 --- a/nimterop/cimport.nim +++ b/nimterop/cimport.nim @@ -120,6 +120,9 @@ proc getToast(fullpaths: seq[string], recurse: bool = false, dynlib: string = "" for i in gStateCT.includeDirs: cmd.add &" --includeDirs+={i.sanitizePath}" + for i in gStateCT.exclude: + cmd.add &" --exclude+={i.sanitizePath}" + if not noNimout: cmd.add &" --pnim" @@ -415,21 +418,52 @@ proc cAddSearchDir*(dir: string) {.compileTime.} = if dir notin gStateCT.searchDirs: gStateCT.searchDirs.add(dir) -macro cIncludeDir*(dir: static string): untyped = +macro cIncludeDir*(dirs: static seq[string], exclude: static[bool] = false): untyped = + ## Add include directories 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: "-IXXX".}`. + ## + ## 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 ## called within `cImport()` or `c2nImport()` as well as to the C/C++ ## compiler during Nim compilation using `{.passC: "-IXXX".}`. - var dir = interpPath(dir) - result = newNimNode(nnkStmtList) + ## + ## Set `exclude = true` if the contents of this include directory should + ## not be included in the wrapped output. + return quote do: + cIncludeDir(@[`dir`], `exclude` == 1) - let fullpath = findPath(dir) - if fullpath notin gStateCT.includeDirs: - gStateCT.includeDirs.add(fullpath) - let str = &"-I{fullpath.quoteShell}" - result.add quote do: - {.passC: `str`.} - if gStateCT.debug: - gecho result.repr +macro cExclude*(paths: static seq[string]): untyped = + ## Exclude specified paths - files or directories from the wrapped output + ## + ## Full path to file or directory is required. + result = newNimNode(nnkStmtList) + for path in paths: + gStateCT.exclude.add path + +macro cExclude*(path: static string): untyped = + ## Exclude specified path - file or directory from the wrapped output. + ## + ## Full path to file or directory is required. + return quote do: + cExclude(@[`path`]) proc cAddStdDir*(mode = "c") {.compileTime.} = ## Add the standard `c` [default] or `cpp` include paths to search diff --git a/nimterop/globals.nim b/nimterop/globals.nim index 17b9518..89e403f 100644 --- a/nimterop/globals.nim +++ b/nimterop/globals.nim @@ -19,6 +19,7 @@ type 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 dynlib*: string # `cImport(dynlib)` or `--dynlib | -l` to specify variable containing library name + exclude*: seq[string] # files or directories to exclude from the wrapped output feature*: seq[Feature] # `--feature | -f` feature flags enabled includeDirs*: seq[string] # Paths added by `cIncludeDir()` and `--includeDirs | -I` for C/C++ preprocessor/compiler mode*: string # `cImport(mode)` or `--mode | -m` to override detected compiler mode - c or cpp diff --git a/nimterop/toast.nim b/nimterop/toast.nim index 1cda239..20dc79e 100644 --- a/nimterop/toast.nim +++ b/nimterop/toast.nim @@ -13,7 +13,7 @@ var preMainOut = "" proc process(gState: State, path: string) = - doAssert existsFile(path), &"Invalid path {path}" + doAssert fileExists(path), &"Invalid path {path}" if gState.mode.Bl: gState.mode = getCompilerMode(path) @@ -38,6 +38,7 @@ proc main( debug = false, defines: seq[string] = @[], dynlib: string = "", + exclude: seq[string] = @[], feature: seq[Feature] = @[], includeDirs: seq[string] = @[], mode = "", @@ -65,6 +66,7 @@ proc main( debug: debug, defines: defines, dynlib: dynlib, + exclude: exclude, feature: feature, includeDirs: includeDirs, mode: mode, @@ -222,6 +224,7 @@ when isMainModule: "debug": "enable debug output", "defines": "definitions to pass to preprocessor", "dynlib": "{.dynlib.} pragma to import symbols - Nim const string or file path", + "exclude": "files or directories to exclude from the wrapped output", "feature": "flags to enable experimental features", "includeDirs": "include directory to pass to preprocessor", "mode": "language parser: c or cpp", @@ -247,6 +250,7 @@ when isMainModule: "debug": 'd', "defines": 'D', "dynlib": 'l', + "exclude": 'X', "feature": 'f', "includeDirs": 'I', "noComments": 'c', diff --git a/nimterop/toastlib/getters.nim b/nimterop/toastlib/getters.nim index 1e70d59..8c6abe1 100644 --- a/nimterop/toastlib/getters.nim +++ b/nimterop/toastlib/getters.nim @@ -286,7 +286,16 @@ proc getKeyword*(kind: NimSymKind): string = proc getCurrentHeader*(fullpath: string): string = ("header" & fullpath.splitFile().name.multiReplace([(".", ""), ("-", "")])) +proc isIncluded(gState: State, file: string): bool {.inline.} = + # Check if the specified file should be excluded from wrapped output + if gState.exclude.nBl: + for excl in gState.exclude: + if file.startsWith(excl): + return + result = true + proc getPreprocessor*(gState: State, fullpath: string) = + # Get preprocessed output from the C/C++ compiler var args: seq[string] start = false @@ -332,14 +341,15 @@ proc getPreprocessor*(gState: State, fullpath: string) = start = true elif gState.recurse: if (pDir.Bl or pDir in line) and line notin gState.headersProcessed: - start = true newHeaders.incl line + start = gState.isIncluded(line) else: for inc in includeDirs: if line.startsWith(inc) and line notin gState.headersProcessed: - start = true newHeaders.incl line - break + start = gState.isIncluded(line) + if start: + break elif ": fatal error:" in line: doAssert false, "\n\nFailed in preprocessing, check if `cIncludeDir()` is needed or compiler `mode` is correct (c/cpp)" &