Compare commits

..

No commits in common. "gh-pages" and "master" have entirely different histories.

79 changed files with 10930 additions and 6119 deletions

19
.gitignore vendored Normal file
View file

@ -0,0 +1,19 @@
## ignore all files wo extension
*
!/**/
!*.*
# Notes:
# all generated files should go inside `build/`
# use absolute paths to refer to a path assumed to be at a fixed level in the hierarchy
/build
nimcache
.DS_Store
*.exe
*.swp

View file

59
.travis.yml Normal file
View file

@ -0,0 +1,59 @@
os: linux
dist: bionic
language: c
addons:
apt:
packages:
- autopoint
matrix:
include:
# Linux - amd64
- env: BRANCH=0.20.2
- env: BRANCH=1.0.8
- env: BRANCH=1.2.4
- env: BRANCH=devel
# Linux - arm64
# - arch: arm64
# env: BRANCH=1.2.4
# Linux - ppc64
# - arch: ppc64le
# env: BRANCH=1.2.4
# macOS - amd64
- os: osx
env: BRANCH=0.20.2
- os: osx
env: BRANCH=1.0.8
- os: osx
env: BRANCH=1.2.4
- os: osx
env: BRANCH=devel
# windows - amd64
- os: windows
env: BRANCH=0.20.2
- os: windows
env: BRANCH=1.0.8
- os: windows
env: BRANCH=1.2.4
- os: windows
env: BRANCH=devel
cache:
directories:
- "$HOME/.choosenim"
install:
- export PATH="/usr/local/opt/gettext/bin:$PATH"
- curl https://gist.github.com/genotrance/fb53504a4fba88bc5201d3783df5c522/raw/travis.sh -LsSf -o travis.sh
- source travis.sh
script:
- set -e
- nimble develop -y
- nimble test
- nimble --verbose --nimbleDir:`pwd`/build/fakenimble install nimterop@#head -y

158
CHANGES.md Normal file
View file

@ -0,0 +1,158 @@
# Nimterop Change History
## Version 0.6.0
This release adds the ability to download precompiled binaries from [Conan.io](https://conan.io/center) and Julia's [BinaryBuilder.org](https://binarybuilder.org). This alleviates the headache of searching and downloading libraries manually both for wrapper writers as well as end users. There are some known limitations but it should prove to become more useful as these sites expand their capabilities.
Conan.io shared builds tend to have all dependencies statically linked into the binary so a single so/dll/dylib has everything. For Conan.io static builds and all libraries on BinaryBuilder.org, dependencies are also downloaded and linked as needed. They are returned in the new `const xxxLDeps` in case wrapper writers need it for some reason.
Known concerns:
- Conan.io only compiles Windows builds with Microsoft's VC++ compiler so static .lib files may not always work with MinGW on Windows.
- Conan.io compiles all Mac builds on OSX 10.14 so older versions of the OS will grumble when statically linking these libraries.
- BinaryBuilder.org does not include static libs for all their projects.
Refer to the documentation for `getHeader()` for details on how to use this new capability.
See the full list of changes here:
https://github.com/nimterop/nimterop/compare/v0.5.9...v0.6.5
### Breaking changes
- The legacy algorithm has been removed as promised. `ast2` is now the default and wrappers no longer need to explicitly specify `-f:ast2` in order to use it.
- All shared libraries installed by `getHeader()` will now get copied into the `libdir` parameter specified. If left blank, `libdir` will default to the directory where the executable binary gets created (outdir). While this is not really a breaking change, it is a change in behavior compared to older versions of nimterop. Note that `Std` libraries are not copied over. [#154][i154]
- `git.nim` has been removed. This module was an artifact from the early days and was renamed to `build.nim` back in v0.2.0.
- Nameless enum values are no longer typed to the made-up enum type name, they are instead typed as `cint` to match the underlying type. This allows using such enums without having to depend on the made-up name which could change if enum ordering changes upstream. [#236][i236] (since v0.6.1)
- Static libraries installed and linked with `getHeader()` now have their `{.passL.}` pragmas forwarded to the generated wrapper. This might lead to link errors in existing wrappers if other dependencies are specified with `{.passL.}` calls and the order of linking is wrong. This can be fixed by changing such explicit `{.passL.}` calls with `cPassL()` which will forward the link call to the generated wrapper as well. (since v0.6.5)
### New functionality
- `getHeader()` now detects and links against `.lib` files as part of enabling Conan.io. Not all `.lib` files are compatible with MinGW as already stated above but for those that work, this is a required capability.
- The `dynlib` command line parameter to `toast` and `cImport()` can also be the path to a shared library (dll|so|dylib) in place of a Nim const string containing the path. This allows for the traditional use case of passing `"xxxLPath"` to `cImport()` as well as simply passing the path to the library on the command line as is. This allows the creation of standalone cached wrappers as well as the usage of the `--check` and the `--stub` functionality that `toast` provides via `cImport()`.
- `gitPull()` now checks if an existing repository is at the `checkout` value specified. If not, it will pull the latest changes and checkout the specified commit, tag or branch.
- `cImport()` can now write the generated wrapper output to a user-defined file with the `nimFile` param. [#127][i127] (since v0.6.1)
- Nimterop now supports anonymous nested structs/unions but it only works correctly for unions when `noHeader` is turned off (the default). This is because Nim does not support nested structs/unions and is unaware of the underlying memory structure. [#237][i237] (since v0.6.1)
- `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 <gtk/gtk.h>` 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`. (since v0.6.4)
- All `cDefine()`, `cIncludeDir()` and `cCompile()` calls now forward relevant pragmas into the generated wrapper further enabling standalone wrappers. [#239][i239]
- Added `cPassC()` and `cPassL()` to forward C/C++ compilation pragmas into the generated wrapper. These should be used in place of `{.passC.}` and `{.passL.}` and need to be called before `cImport()` to take effect. (since v0.6.5)
- Added `--compile`, `--passC` and `--passL` flags to `toast` to enable the previous two improvements. (since v0.6.5)
- Added `renderPragma()` to create pragmas inline in case `cImport()` is not being used. (since v0.6.5)
- `xxxConan` and `xxxJBB` now allow skipping required dependencies by specifying `skip=pkg1,pkg2` to the `conanFlags` and `jbbFlags` params to `getHeader()`. (since v0.6.6)
### 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. (since v0.6.4)
- `cDefine()` can now accept a `seq[string]` of values. (since v0.6.5)
## Version 0.5.0
This release introduces a new backend for wrapper generation dubbed `ast2` that leverages the Nim compiler AST and renderer. The new design simplifies feature development and already includes all the functionality of the legacy algorithm plus fixes for several open issues.
The new backend can be leveraged with the `-f:ast2` flag to `toast` or `flags = "-f:ast2"` to `cImport()`. The legacy algorithm will be the default backend for this release but no new functionality or bugfixes are expected going forward. Usage of the legacy algorithm will display a *deprecated* hint to encourage users to test their wrappers with `-f:ast2` and remove any overrides that the new algorithm supports.
Version 0.6.0 of Nimterop will make `ast2` the default backend and the legacy algorithm will be removed altogether.
See the full list of changes here:
https://github.com/nimterop/nimterop/compare/v0.4.4...v0.5.4
### Breaking changes
- Nimterop used to default to C++ mode for preprocessing and tree-sitter parsing in all cases unless explicitly informed to use C mode. This has been changed and is now detected based on the file extension. This means some existing wrappers could break since they might contain C++ code or include C++ headers like `#include <string>` which will not work in C mode. Explicitly setting `mode = "cpp"` or `-mcpp` should fix such issues. [#176][i176]
- Enums were originally being mapped to `distint int` - this has been changed to `distinct cint` since the sizes are incorrect on 64-bit and is especially noticeable when types or unions have enum fields.
- `static inline` functions are no longer wrapped by the legacy backend. The `ast2` backend correctly generates wrappers for such functions but they are only generated when `--noHeader | -H` is not in effect. This is because such functions do not exist in the binary and can only be referenced when the header is compiled in.
- Support for Nim v0.19.6 has been dropped and the test matrix now covers v0.20.2, v1.0.6, v1.2.0 and devel.
### New functionality
- Nimterop can now skip generating the `{.header.}` pragma when the `--noHeader | -H` flag is used. This skips the header file `#include` in the generated code and allows creation of wrappers that do not require presence of the header during compile time. Note that `static inline` functions will only be wrapped when the header is compiled in. This change applies to both `ast2` and the legacy backend, although `ast2` can also generate wrappers with both `{.header.}` and `{.dynlib.}` in effect enabling type size checking with `-d:checkAbi`. More information is available in the [README.md](README.md). [#169][i169]
- `ast2` includes support for various C constructs that were issues with the legacy backend. These changes should reduce the reliance on `cOverride()` and existing wrappers should attempt to clean up such sections where possible.
- N-dimensional arrays and pointers - [#54][i54]
- Synomyms for types - [#74][i74]
- Varargs support - [#76][i76]
- Nested structs, unions and enums - [#137][i137] [#147][i147]
- Forward declarations of types - [#148][i148]
- Nested function pointers - [#155][i155] [#156][i156]
- Various enum fixes - [#159][i159] [#171][i171]
- Map `int arr[]` to `arr: UncheckedArray[cint]` - [#174][i174]
- Global variables including arrays and procs (since v0.5.4)
- `ast2` also includes an advanced expression parser that can reliably handle constructs typically seen with `#define` statements and enumeration values:
- Integers + integer like expressions (hex, octal, suffixes)
- Floating point expressions
- Strings and character literals, including C's escape characters
- Math operators `+ - / *`
- Some Unary operators `- ! ~`
- Any identifiers
- C type descriptors `int char` etc
- Boolean values `true false`
- Shift, cast, math or sizeof expressions
- Most type coercions
- Wrappers can now point to an external plugin file with `cPluginPath()` instead of having to declaring plugins inline with `cPlugin()`. This allows multiple wrappers to share the same plugin. [#181][i181]
- `cImport()` adds support for importing multiple headers in a single call - this enables support for libraries that have many header files that include shared headers and typically cannot be imported in multiple `cImport()` calls since it results in duplicate symbols. Calling `toast` with multiple headers uses the same algorithm.
- `ast2` now creates Nim doc comments instead of reqular comments which get rendered when the wrapper is run through `nim doc` or the `buildDocs()` API. [#197][i197]
- `toast` now includes `--replace | -G` to manipulate identifier names beyond `--prefix` and `--suffix`. `-G:X=Y` replaces X with Y and `-G:@_[_]+=_` replaces multiple `_` with a single instance using the `@` prefix to enable regular expressions.
- `toast` also includes `--typeMap | -T` to map C types to another type. E.g. `--typeMap:GLint64=int64` generates a wrapper where all instances of `GLint64` are remapped to the Nim type `int64` and `GLint64` is not defined. (since v0.5.2)
- CLI flags can now be specified one or more per line in a file and path provided to `toast`. They will be expanded in place. [#196][i196] (since v0.5.3)
- Nimterop is now able to detect Nim configuration of projects and can better handle cases where defaults such as `nimcacheDir` or `nimblePath` are overridden. This especially enables better interop with workflows that do not depend on Nimble. [#151][i151] [#153][i153]
- Nimterop defaults to `cmake`, followed by `autoconf` for building libraries with `getHeader()`. It is now possible to change the order of discovery with the `buildType` value. [#200][i200]
[i54]: https://github.com/nimterop/nimterop/issues/54
[i74]: https://github.com/nimterop/nimterop/issues/74
[i76]: https://github.com/nimterop/nimterop/issues/76
[i125]: https://github.com/nimterop/nimterop/issues/125
[i127]: https://github.com/nimterop/nimterop/issues/127
[i137]: https://github.com/nimterop/nimterop/issues/137
[i147]: https://github.com/nimterop/nimterop/issues/147
[i148]: https://github.com/nimterop/nimterop/issues/148
[i151]: https://github.com/nimterop/nimterop/issues/151
[i153]: https://github.com/nimterop/nimterop/issues/153
[i154]: https://github.com/nimterop/nimterop/issues/154
[i155]: https://github.com/nimterop/nimterop/issues/155
[i156]: https://github.com/nimterop/nimterop/issues/156
[i159]: https://github.com/nimterop/nimterop/issues/159
[i169]: https://github.com/nimterop/nimterop/issues/169
[i171]: https://github.com/nimterop/nimterop/issues/171
[i174]: https://github.com/nimterop/nimterop/issues/174
[i176]: https://github.com/nimterop/nimterop/issues/176
[i181]: https://github.com/nimterop/nimterop/issues/181
[i196]: https://github.com/nimterop/nimterop/issues/196
[i197]: https://github.com/nimterop/nimterop/issues/197
[i200]: https://github.com/nimterop/nimterop/issues/200
[i236]: https://github.com/nimterop/nimterop/issues/236
[i237]: https://github.com/nimterop/nimterop/issues/237
[i239]: https://github.com/nimterop/nimterop/issues/239

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Ganesh Viswanathan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

280
README.md Normal file
View file

@ -0,0 +1,280 @@
[![Chat on Gitter](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/nimterop/Lobby)
[![Build status](https://ci.appveyor.com/api/projects/status/hol1yvqbp6hq4ao8/branch/master?svg=true)](https://ci.appveyor.com/project/genotrance/nimterop-8jcj7/branch/master)
[![Build Status](https://travis-ci.org/nimterop/nimterop.svg?branch=master)](https://travis-ci.org/nimterop/nimterop)
Nimterop is a [Nim](https://nim-lang.org/) package that aims to make C/C++ interop seamless
Most of the wrapping functionality is contained within the `toast` binary that is built when nimterop is installed and can be used standalone similar to how `c2nim` can be used today. In addition, nimterop also offers an API to pull in the generated Nim content directly into an application and other functionality that helps in automating the wrapping process. There is also support to statically or dynamically link to system installed libraries or downloading and building them with `autoconf` or `cmake` from a Git repo or source archive.
The nimterop wrapping functionality is still limited to C but is constantly expanding. C++ support will be added once most popular C libraries can be wrapped seamlessly. Meanwhile, `c2nim` can also be used in place of `toast` with the `c2nImport()` API call.
The goal is to make interop seamless so nimterop will focus on wrapping headers and not the outright conversion of C/C++ implementation.
## Installation
Nimterop can be installed via [Nimble](https://github.com/nim-lang/nimble):
```bash
nimble install nimterop -y
```
or:
```bash
git clone http://github.com/nimterop/nimterop && cd nimterop
nimble develop -y
nimble build -d:danger
```
This will download and install nimterop in the standard Nimble package location, typically `~/.nimble`. Once installed, it can be imported into any Nim program.
## Usage
Nimterop can be used in three ways:
- Creating a wrapper file - a `.nim` file that contains calls to the high-level API that can download and build the C library as well as generate the required Nim code to interface with the library. This wrapper file can then be imported into Nim code like any other module and it will be processed at compile time.
- Same as the first option except using the `nimFile` param to `cImport()` to write the generated wrapper to a file during build time just once and then importing that generated wrapper into the application like any other Nim module.
- Using the command line `toast` tool to generate the Nim code which can then be stored into a file and imported separately.
Any combination of the above is possible - only download, build or wrapping and nimterop avoids imposing any particular workflow.
Refer to [CHANGES.md](CHANGES.md) for history and information around breaking changes.
### Build API
Creating a wrapper has two parts, the first is to setup the C library. This includes downloading it or finding it if already installed, and building it if applicable. The `getHeader()` high-level API provides all of this functionality as a convenience. The following `.nim` wrapper file is an example of using the high-level `getHeader()` API to perform all building, wrapping and linking automatically:
```nim
import nimterop/[build, cimport]
static:
cDebug() # Print wrapper to stdout
const
baseDir = getProjectCacheDir("testwrapper") # Download library within nimcache
getHeader(
"header.h", # The header file to wrap, full path is returned in `headerPath`
giturl = "https://github.com/username/repo", # Git repo URL
dlurl = "https://website.org/download/repo-$1.tar.gz", # Download URL for archive or raw file
conanuri = "repo/$1", # Conan.io URI
jbburi = "repo/$1", # BinaryBuilder.org URI
outdir = baseDir, # Where to download/build/search
conFlags = "--disable-comp --enable-feature", # Flags to pass configure script
cmakeFlags = "-DENABLE_STATIC_LIB=ON" # Flags to pass to Cmake
altNames = "hdr" # Alterate names of the library binary, full path returned in `headerLPath`
)
# Wrap headerPath as returned from getHeader() and link statically
# or dynamically depending on user input
when not isDefined(headerStatic):
cImport(headerPath, recurse = true, dynlib = "headerLPath") # Pass dynlib if not static link
else:
cImport(headerPath, recurse = true)
```
Module documentation for the build API can be found [here](https://nimterop.github.io/nimterop/build.html). Refer to the ```tests``` directory for additional examples on how the library can be used. Also, check out the [wiki](https://github.com/nimterop/nimterop/wiki/Wrappers) for a list of all known wrappers that have been created using nimterop. They will provide real world examples of how to wrap libraries. Please do add your project once you are done so that others can benefit from your work.
#### Download / Search
The above wrapper is generic and allows the end user to control how it works. Note that `headerPath` is derived from `header.h` so if you have `SDL.h` as the argument to `getHeader()`, it generates `SDLPath` and `SDLLPath` and is controlled by `-d:SDLStatic`, `-d:SDLGit` and so forth.
- If the library is already installed in `/usr/include` then the `-d:headerStd` define to Nim can be used to instruct `getHeader()` to search for `header.h` in the standard system path.
- If the library needs to be downloaded, the user can use `-d:headerGit` to clone the source from the specified git URL, `-d:headerDL` to get the source from download URL, `-d:headerConan` to download from https://conan.io/center or `-d:headerJBB` to download from https://binarybuilder.org.
- The `-d:headerSetVer=X.Y.Z` flag can be used to specify which version to download. It is used as the tag name for Git and for DL, Conan and JBB, it replaces `$1` in the URL if specified.
- If no flag is provided, `getHeader()` simply looks for the library in `outdir`. The user could use Git submodules or manually download or check-in the library to that directory and `getHeader()` will use it directly.
#### Pre build
`getHeader()` provides a `headerPreBuild()` hook that gets called after the library is downloaded but before it is built. This allows for any manipulations of the source files or build scripts before build. [archive](https://github.com/genotrance/nimarchive/blob/master/nimarchive/archive.nim) has such an example.
The build API also includes various compile time helper procs that aid in file manipulation, Cmake shortcuts, library linking, etc. Refer to [build](https://nimterop.github.io/nimterop/build.html) for more details.
#### Build
Nimterop currently supports `configure` and `cmake` based building of libraries, with `cmake` taking precedence if a project supports both. Nimterop verifies that the tool selected is available and notifies the user if any issues are found. Bash is required on Windows for `configure` and the binary shipped with Git has been tested.
Flags can be specified to these tools via `getHeader()` or directly via the underlying `configure()` and `cmake()` calls. Once the build scripts are ready, `getHeader()` then calls `make()`. At every step, `getHeader()` checks for the presence of created artifacts and does not redo steps that have been successfully completed.
#### Linking
If `-d:headerStatic` is specified, `getHeader()` will return the static library path in `headerLPath`. The wrapper writer can check for this and call `cImport()` accordingly as in the example above. If `-d:headerStatic` is omitted, the dynamic library is returned in `headerLPath`.
All dependency libraries (supported by Conan and JBB) will be returned in `headerLDeps`. Static libraries and dependencies are automatically linked using `cPassL()`. Conan shared libs typically include dependencies compiled in whereas JBB shared libs expect the required dependencies to be in the same location or in `LD_LIBRARY_PATH`. `conanFlags` and `jbbFlags` can be used to skip required dependencies from being downloaded in case another source is preferred. This can be done with `skip=pkg1,pkg2` to these flags.
`getHeader()` searches for libraries based on the header name by default:
- `libheader.so` or `libheader.a` on Linux
- `libheader.dylib` on OSX
- `header.dll`, `header.a` or `header.lib` on Windows
If a library has a different header and library binary name, `altNames` can be used to configure an alternate name of library binary.
- For example, Bzip2 has `bzlib.h` but the library is `libbz2.so` so `altNames = "bz2"`.
- In the example above, `altNames = "hdr"` so `getHeader()` will look for `libhdr.so`, `hdr.dll`, etc.
- See [bzlib.nim](https://github.com/genotrance/nimarchive/blob/master/nimarchive/bzlib.nim) for an example.
[lzma.nim](https://github.com/nimterop/nimterop/blob/master/tests/lzma.nim) is an example of a library that allows both static and dynamic linking.
#### User control
The `-d:xxxYYY` Nim define flags have already been described above and can be specified on the command line or in a nim.cfg file. It is also possible to specify them within the wrapper itself using `setDefines()` if required. Further, all defines, regardless of how they are specified, can be generically checked using `isDefined()`.
If more fine-tuned control is desired over the build process, it is possible to manually control all steps that `getHeader()` performs by directly using the API provided by [build](https://nimterop.github.io/nimterop/build.html). Note also that there is no requirement to use these APIs to setup the library. Any other established mechanisms can be used to do so any limitations imposed by Nimterop are unintentional and feedback is most welcome.
### Wrapper API
Once the C library is setup, the next step is to generate code that inform Nim of all the types and functions that are available. Following is a simple example covering the API:
```nim
import nimterop/cimport
static:
cDebug()
cDisableCaching() # Regenerate Nim wrapper every time
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
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
In order to leverage the preprocessor, certain projects might need `cDefine()` calls to set `#define` values. Simpler library may have documentation that cover this but larger ones will rely on build tools that discover and set values in a `config.h` which is loaded with `#include`. Projects might also require some `cIncludeDir()` calls to specify paths to directories that contain other headers. This might be within the library or refer to another library.
The wrapper API always runs headers through the C preprocessor before wrapping. Details on why are discussed further down.
By default, the `$CC` environment variable is used for the compiler path. If not found, `toast` defaults to `gcc`.
#### Wrapping
The `cImport()` call invokes the `toast` binary with appropriate command line flags including any `cDefine()` and `cInclude()` parameters configured. The output of `toast` is then pulled into the module as Nim code and printed if `cDebug()` is specified. This allows for an end user to simply import the wrapper into their code and access the library API as Nim types and procs. Output is cached to save time on subsequent runs. It is also possible to just redirect the output to a file and import that instead if preferred.
The `recurse` flag can be set to enable the recursion capability which runs through all #include files in the header. If the library needs to be dyamically linked using Nim's `dynlib` pragma, the `dynlib = "constName"` attribute can be set to generate wrappers that load the DLL automatically. Without `dynlib`, static link is assumed so it is the user's responsibility to link the library.
There may be cases where the wrapper generated by `toast` for certain types or procs is not preferred, or may be skipped or altogether wrong due to limitations or bugs. In these instances, the `cOverride()` macro can be used to define consts, types or procs to use in place of the wrapper generated output. `cImport()` will forward this information to `toast` and the values will be inserted in context in the generated wrapper. This allows wrapper authors to work around tool limitations or to improve the wrapper output - say change `ptr X` to `var X` or to create more Nim friendly types or proc signatures.
Several C libraries also use leading and/or trailing `_` in identifiers and since Nim does not allow this, the `cPlugin()` macro can be used to modify such symbols or `cSkipSymbol()` them altogether. Instead of a full `cPlugin()` section, it might also be preferred to set `flags = "-E_ -F_"` to the `cImport()` call to trim out such characters. These features can also be used to remove common prefixes like `SDL_` to generate a cleaner wrapper. The `--replace | -G` flag can be used for replacements. `cPlugin()` is real Nim code though so anything Nim allows is fair game. Note that `cPlugin()` overrides any `-E -F -G` flags. Also, behind the scenes, `cOverride()` is communicated to `toast` via `cPlugin()`.
If the same `cPlugin()` is needed in multiple wrapper files, the code can be moved into a standalone file and be used with the `cPluginPath()` call.
Lastly, `c2nImport()` provides access to calling `c2nim` from the wrapper instead of `toast`. Note that `c2nImport()` does not use any of the above described features like `cPlugin()` and needs to be controlled with `c2nim` specific flags via the `flags` param.
#### Header vs. Dynlib
Nim provides some flexibility when it comes to using C/C++ libraries. In order to understand this better, some Nim pragmas need to be introduced. The main one is `{.importc.}` which informs Nim to use a symbol defined in a C library. This applies to both types and procs but how Nim should find the symbol is slightly different for each.
For types, `{.header: "header.h".}` informs Nim that `header.h` has the symbol and to `#include "header.h"` in the generated code. However, types can be mostly recreated in pure Nim so it is also possible to omit both `{.importc.}` and `{.header}` and it will work just fine except with a different name in the generated C code. This allows the user to compile the wrapper without requiring `header.h` to be present.
For functions, `{.header.}` works the same as types and can be omitted if preferred. The `{.importc.}` pragma is still required, unlike types since functions need to be linked to the implementation in the library. The user will need to provide this information at link time with `cPassL()` and linking to a library with `-lheader` or `path/to/libheader.a`. It is also possible to just use `cCompile()` or `{.compile.}` to compile some C source files which contain the implementation.
While `{.header.}` can be omitted for convenience, it does prevent wrapping of `static inline` functions as well as type checking of the wrapper ABI with `-d:checkAbi` at compile time. Further, anonymous nested structs/unions within unions will be rendered incorrectly by Nim since it is unaware of the true memory structure of the type. The user will need to choose based on the library in question.
Going further, the `{.dynlib: "path/to/libheader.so".}` pragma can be used to inform Nim to load the library at runtime and link the function instead of linking at compile time. This enables creation of a wrapper that does not need the library present at compile time.
Now that this is understood, a user might want any combination of the above in the wrapper rendered by Nimterop. This can be controlled with various flags to `cImport()` and `toast`.
- By default, generated wrappers will include the `{.header, importc.}` pragmas for types and procs. This can be disabled with the `--noHeader | -H` flag to `toast` or `flags = "-H"` param to `cImport()` which will remove `{.header}` for both and `{.importc.}` for types only.
- By default, generated wrappers will assume that the user will link the library implementation themselves. The `--dynlib | -l` flag to `toast` or `dynlib = "headerLPath"` param to `cImport()` will configure the wrapper to generate `{.dynlib.}` pragmas for procs.
This results in four supported cases:
1. Default: `{.header, importc.}` for both types and procs
2. With `--noHeader`, types will be pure Nim and procs will be just `{.importc.}`
3. With `--dynlib`, types will still be `{.header, importc.}` but procs will be `{.dynlib, importc.}`
4. With `--dynlib` and `--noHeader`, types will be pure Nim, procs will be `{.dynlib, importc.}`
Creation of a standalone wrapper (case 4) which does not require the header or library at compile time will require an explicit `--noHeader` and `--dynlib`.
More documentation on on these pragmas can be found in the Nim manual:
- [{.importc.}](https://nim-lang.org/docs/manual.html#foreign-function-interface-importc-pragma)
- [{.header.}](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-header-pragma)
- [{.dynlib.}](https://nim-lang.org/docs/manual.html#foreign-function-interface-dynlib-pragma-for-import)
- [{.passL.}](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-passl-pragma)
- [{.compile.}](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-compile-pragma)
#### Compiling the source
The job of building and compiling the underlying C library is best left to the build mechanism selected by the library author so using `getHeader()` is recommended. For simpler projects with a few `.c` files though, `cCompile()` should be more than enough. It is not recommended for larger projects which heavily rely on functionality offered by build tools. Recreating reliable logic in Nim can be tedious and one can expect minimal support from that author if their tested build mechanism is not used.
### Docs API
Nimterop also provides a [docs](https://nimterop.github.io/nimterop/docs.html) API which can be used to generate documentation from the generated wrappers. This can be added as a task in the `.nimble` or `.nims` file for convenience. See [nimarchive.nimble](https://github.com/genotrance/nimarchive/blob/master/nimarchive.nimble) for an example.
### Command line API
The `toast` binary can also be used directly on the CLI, similar to `c2nim`. These flags can be specified on the command line or via a file, one or more flags per line, and the path provided to `toast` instead, or a combination. The file contents will be expanded in place.
Note: unlike the wrapper API, the `-p | --preprocess` flag is not enabled by default but is *highly* recommended.
```
> toast -h
Usage:
main [optional-params] C/C++ source/header(s) and command line file(s)
Options:
-h, --help print this cligen-erated help
--help-syntax advanced: prepend,plurals,..
-k, --check bool false check generated wrapper with compiler
--compile= strings {} create {.compile.} entries in generated wrapper
-C=, --convention= string "cdecl" calling convention for wrapped procs
-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
--nim= string "nim" use a particular Nim executable
-c, --noComments bool false exclude top-level comments from output
-H, --noHeader bool false skip {.header.} pragma in wrapper
-o=, --output= string "" file to output content - default: stdout
--passC= strings {} create {.passC.} entries in generated wrapper
--passL= strings {} create {.passL.} entries in generated wrapper
-a, --past bool false print AST output
--pluginSourcePath= string "" nim file to build and load as a plugin
-n, --pnim bool false print Nim output
-E=, --prefix= strings {} strip prefix from identifiers
-p, --preprocess bool false run preprocessor on header
-r, --recurse bool false process #include files - implies --preprocess
-G=, --replace= strings {} replace X with Y in identifiers, X1=Y1,X2=Y2, @X for regex
-s, --stub bool false stub out undefined type references as objects
-F=, --suffix= strings {} strip suffix from identifiers
-O=, --symOverride= strings {} skip generating specified symbols
-T=, --typeMap= strings {} map instances of type X to Y - e.g. ABC=cint
```
## Why nimterop
Nim has one of the best FFI you can find - importing C/C++ is supported out of the box. All you need to provide is type and proc definitions for Nim to interop with C/C++ binaries. Generation of these wrappers is easy for simple libraries but can quickly get out of hand. [c2nim](https://github.com/nim-lang/c2nim) greatly helps here by parsing and converting C/C++ into Nim but is limited due to the complex and constantly evolving C/C++ grammar. [nimgen](https://github.com/genotrance/nimgen) mainly focused on automating the wrapping process with `c2nim` and filled some holes but is again limited to `c2nim` capabilities.
The goal of nimterop is to leverage the [tree-sitter](http://tree-sitter.github.io/tree-sitter/) engine to parse C/C++ code and then convert relevant portions of the AST into Nim definitions. [tree-sitter](https://github.com/tree-sitter) is a Github sponsored project that can parse a variety of languages into an AST which is then leveraged by the [Atom](https://atom.io/) editor for syntax highlighting and code folding. The advantages of this approach are multifold:
- Benefit from the tree-sitter community's ongoing investment into language parsing
- Wrap what is recognized in the AST rather than completely failing due to parsing errors
The tree-sitter library is limited though - it may fail on some advanced language constructs but is designed to handle them gracefully since it is expected to have bad code while actively typing in an editor. When an error is detected, tree-sitter includes an ERROR node at that location in the AST. At this time, `cImport()` will complain and continue if it encounters any errors. Depending on how severe the errors are, compilation may succeed or fail. Glaring issues will be communicated to the tree-sitter team but their goals may not always align with those of this project.
It is debatable whether a syntax highlighting engine like `tree-sitter` is the most reliable method to convert C code into AST. However, it is lightweight, cross-platform with no dependencies and handles error conditions gracefully. It has produced usable wrappers for C libraries though things could get murky when considering C++ but that will be a topic for another day. Nimterop relies heavily on the preprocessor, as discussed next, so having an engine which can run anywhere has been worth the compromise. Only time will tell though.
### Preprocessing
The wrapper API always runs headers through the C preprocessor before wrapping, unlike the command line interface where the `-p | --preprocess` flag is not set by default but *highly* recommended. This is because almost all platform, compiler and package discovery is handled by build tools like `configure` and `cmake` which then use preprocessor `#define` values to tweak what C code is applicable for that platform. While parsing preprocessor macros is possible in tools like `toast`, given how dependent the `#ifdef` branches are on values provided by these and many other build tools, preprocessing seems is best left to them than attempting to self-discover or intercept that information.
Nimterop is still able to wrap most relevant `#define` like numbers and strings thanks to `gcc -E` providing the sufficient detail in its output. Many C libraries also use `#define` templates for some of their user facing API and providing that functionality in Nim is on the Nimterop roadmap.
The con of this approach of delegating to the preprocessor is that the Nim wrapper generated by Nimterop is no longer portable despite being Nim code. A wrapper rendered on Linux might not work on Windows since some APIs may not be available or inappropriate, integer sizes might be wrong, types could be missing and many other possible issues. But none of this is easily or accurately known at the Nim level since it would require input from the build tools which already work well with the preprocessor or have to be completely reimplemented within Nim. Neither approach that bypasses such build tools would be supported by the library author.
This is part of the reason why Nimterop provides a wrapper API so that the generation of wrappers is Nim code that can be rendered as part of the build process on the target platform. It helps to think of Nimterop as a build time tool like `cmake` that renders artifacts on the target rather than a tool whose generated artifacts should be checked into source control. Regardless, both the wrapper API and the `toast` command line still allow saving the wrapper output to a file to be stored in source control since it might work well enough for many projects.
## Credits
Nimterop depends on [tree-sitter](http://tree-sitter.github.io/tree-sitter/) and all licensing terms of [tree-sitter](https://github.com/tree-sitter/tree-sitter/blob/master/LICENSE) apply to the usage of this package. The tree-sitter functionality is pulled and wrapped using nimterop itself.
Thank you to all the [contributors](https://github.com/nimterop/nimterop/graphs/contributors), issue submitters, various people in [#nim](irc://freenode.net/nim) and users for helping improve Nimterop over the years.
## Feedback
Nimterop is a work in progress and any feedback or suggestions are welcome. It is hosted on [GitHub](https://github.com/nimterop/nimterop) with an MIT license so issues, forks and PRs are most appreciated.

134
all.html
View file

@ -1,134 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
<!-- Google fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
<!-- CSS -->
<title>all</title>
<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
<script type="text/javascript" src="dochack.js"></script>
<script type="text/javascript">
function main() {
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
toggleSwitch.addEventListener('change', switchTheme, false);
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-theme', "dark");
toggleSwitch.checked = true;
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
document.documentElement.setAttribute('data-theme', "light");
toggleSwitch.checked = false;
} else {
const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null;
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
if (currentTheme === 'dark') {
toggleSwitch.checked = true;
}
}
}
}
</script>
</head>
<body onload="main()">
<div class="document" id="documentId">
<div class="container">
<h1 class="title">all</h1>
<div class="row">
<div class="three columns">
<div class="theme-switch-wrapper">
<label class="theme-switch" for="checkbox">
<input type="checkbox" id="checkbox" />
<div class="slider round"></div>
</label>
&nbsp;&nbsp;&nbsp; <em>Dark Mode</em>
</div>
<div id="global-links">
<ul class="simple">
</ul>
</div>
<div id="searchInputDiv">
Search: <input type="text" id="searchInput"
onkeyup="search()" />
</div>
<div>
Group by:
<select onchange="groupBy(this.value)">
<option value="section">Section</option>
<option value="type">Type</option>
</select>
</div>
<ul class="simple simple-toc" id="toc-list">
<li>
<a class="reference reference-toplevel" href="#6" id="56">Imports</a>
<ul class="simple simple-toc-section">
</ul>
</li>
</ul>
</div>
<div class="nine columns" id="content">
<div id="tocRoot"></div>
<p class="module-desc">Module that should import everything so that <tt class="docutils literal"><span class="pre">nim doc --project nimtero/all</span></tt> runs docs on everything. </p>
<div class="section" id="6">
<h1><a class="toc-backref" href="#6">Imports</a></h1>
<dl class="item">
<a class="reference external" href="docs.html">docs</a>, <a class="reference external" href="cimport.html">cimport</a>, <a class="reference external" href="build.html">build</a>, <a class="reference external" href="types.html">types</a>, <a class="reference external" href="plugin.html">plugin</a>
</dl></div>
</div>
</div>
<div class="row">
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small style="color: var(--hint);">Made with Nim. Generated: 2020-03-24 20:55:42 UTC</small>
</div>
</div>
</div>
</div>
</body>
</html>

93
appveyor.yml Normal file
View file

@ -0,0 +1,93 @@
version: '{build}'
image:
- Visual Studio 2015
- Ubuntu
matrix:
fast_finish: true
environment:
matrix:
- NIM_VERSION: 0.20.2
- NIM_VERSION: 1.0.8
- NIM_VERSION: 1.2.4
for:
-
matrix:
only:
- image: Visual Studio 2015
environment:
ARCH: 64
GIT_URL: https://github.com/git-for-windows/git/releases/download/v2.23.0.windows.1/
GIT_ARCHIVE: PortableGit-2.23.0-64-bit.7z.exe
install:
- CD c:\
- IF not exist "binaries" (
echo %NIM_VERSION% &&
MKDIR binaries &&
CD binaries &&
MKDIR git &&
CD git &&
appveyor DownloadFile "%GIT_URL%/%GIT_ARCHIVE%" -FileName "%GIT_ARCHIVE%" &&
7z x -y -bd "%GIT_ARCHIVE%"> nul &&
del "%GIT_ARCHIVE%" &&
CD .. &&
appveyor DownloadFile "https://nim-lang.org/download/nim-%NIM_VERSION%_x%ARCH%.zip" -FileName "nim-%NIM_VERSION%_x%ARCH%.zip" &&
7z x -y "nim-%NIM_VERSION%_x%ARCH%.zip"> nul &&
del "nim-%NIM_VERSION%_x%ARCH%.zip")
- SET PATH=c:\binaries\git\bin;C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;c:\binaries\nim-%NIM_VERSION%\bin;%USERPROFILE%\.nimble\bin;%PATH%
- CD %APPVEYOR_BUILD_FOLDER%
# on_finish:
# - 7z a -r buildlogs-win-pkgs.zip %USERPROFILE%\.nimble\pkgs
# - appveyor PushArtifact buildlogs-win-pkgs.zip
# - 7z a -r buildlogs-win-projects.zip c:\projects\*
# - appveyor PushArtifact buildlogs-win-projects.zip
cache:
- c:\binaries
-
matrix:
only:
- image: Ubuntu
install:
- sudo apt-get update
- sudo apt-get --yes --force-yes install liblzma-dev liblzma5 autopoint
- if [ ! -e /home/appveyor/binaries ]; then
echo $NIM_VERSION &&
mkdir /home/appveyor/binaries &&
cd /home/appveyor/binaries &&
curl -s -o nim-$NIM_VERSION.tar.xz https://nim-lang.org/download/nim-$NIM_VERSION.tar.xz &&
tar xJf nim-$NIM_VERSION.tar.xz &&
cd nim-$NIM_VERSION &&
sh build.sh &&
bin/nim c -d:release koch &&
./koch boot -d:release &&
./koch nimble -d:release;
fi
- export PATH=/home/appveyor/binaries/nim-$NIM_VERSION/bin:~/.nimble/bin:$PATH
- cd $APPVEYOR_BUILD_FOLDER
# on_finish:
# - zip -r -q buildlogs-lin-pkgs.zip ~/.nimble/pkgs
# - appveyor PushArtifact buildlogs-lin-pkgs.zip
# - zip -r -q buildlogs-lin-projects.zip /home/appveyor/projects
# - appveyor PushArtifact buildlogs-lin-projects.zip
cache:
- /home/appveyor/binaries
build_script:
- nimble --verbose develop -y
test_script:
- nimble --verbose test
- nimble --verbose --nimbleDir:test install nimterop -y
deploy: off

View file

@ -1,589 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
<!-- Google fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
<!-- CSS -->
<title>build</title>
<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
<script type="text/javascript" src="dochack.js"></script>
<script type="text/javascript">
function main() {
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
toggleSwitch.addEventListener('change', switchTheme, false);
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-theme', "dark");
toggleSwitch.checked = true;
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
document.documentElement.setAttribute('data-theme', "light");
toggleSwitch.checked = false;
} else {
const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null;
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
if (currentTheme === 'dark') {
toggleSwitch.checked = true;
}
}
}
}
</script>
</head>
<body onload="main()">
<div class="document" id="documentId">
<div class="container">
<h1 class="title">build</h1>
<div class="row">
<div class="three columns">
<div class="theme-switch-wrapper">
<label class="theme-switch" for="checkbox">
<input type="checkbox" id="checkbox" />
<div class="slider round"></div>
</label>
&nbsp;&nbsp;&nbsp; <em>Dark Mode</em>
</div>
<div id="global-links">
<ul class="simple">
</ul>
</div>
<div id="searchInputDiv">
Search: <input type="text" id="searchInput"
onkeyup="search()" />
</div>
<div>
Group by:
<select onchange="groupBy(this.value)">
<option value="section">Section</option>
<option value="type">Type</option>
</select>
</div>
<ul class="simple simple-toc" id="toc-list">
<li>
<a class="reference reference-toplevel" href="#12" id="62">Procs</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#sanitizePath%2Cstring"
title="sanitizePath(path: string; noQuote = false; sep = $&apos;/&apos;): string"><wbr />sanitize<wbr />Path<span class="attachedType"></span></a></li>
<li><a class="reference" href="#sleep%2Cint"
title="sleep(milsecs: int)"><wbr />sleep<span class="attachedType"></span></a></li>
<li><a class="reference" href="#getCurrentNimCompiler"
title="getCurrentNimCompiler(): string"><wbr />get<wbr />Current<wbr />Nim<wbr />Compiler<span class="attachedType"></span></a></li>
<li><a class="reference" href="#execAction%2Cstring%2Cint%2Cstring"
title="execAction(cmd: string; retry = 0; die = true; cache = false; cacheKey = &quot;&quot;): tuple[
output: string, ret: int]"><wbr />exec<wbr />Action<span class="attachedType"></span></a></li>
<li><a class="reference" href="#findExe%2Cstring"
title="findExe(exe: string): string"><wbr />find<wbr />Exe<span class="attachedType"></span></a></li>
<li><a class="reference" href="#mkDir%2Cstring"
title="mkDir(dir: string)"><wbr />mk<wbr />Dir<span class="attachedType"></span></a></li>
<li><a class="reference" href="#cpFile%2Cstring%2Cstring"
title="cpFile(source, dest: string; move = false)"><wbr />cp<wbr />File<span class="attachedType"></span></a></li>
<li><a class="reference" href="#mvFile%2Cstring%2Cstring"
title="mvFile(source, dest: string)"><wbr />mv<wbr />File<span class="attachedType"></span></a></li>
<li><a class="reference" href="#rmFile%2Cstring"
title="rmFile(source: string; dir = false)"><wbr />rm<wbr />File<span class="attachedType"></span></a></li>
<li><a class="reference" href="#rmDir%2Cstring"
title="rmDir(dir: string)"><wbr />rm<wbr />Dir<span class="attachedType"></span></a></li>
<li><a class="reference" href="#getProjectCacheDir%2Cstring"
title="getProjectCacheDir(name: string; forceClean = true): string"><wbr />get<wbr />Project<wbr />Cache<wbr />Dir<span class="attachedType"></span></a></li>
<li><a class="reference" href="#extractZip%2Cstring%2Cstring"
title="extractZip(zipfile, outdir: string)"><wbr />extract<wbr />Zip<span class="attachedType"></span></a></li>
<li><a class="reference" href="#extractTar%2Cstring%2Cstring"
title="extractTar(tarfile, outdir: string)"><wbr />extract<wbr />Tar<span class="attachedType"></span></a></li>
<li><a class="reference" href="#downloadUrl%2Cstring%2Cstring"
title="downloadUrl(url, outdir: string)"><wbr />download<wbr />Url<span class="attachedType"></span></a></li>
<li><a class="reference" href="#gitReset%2Cstring"
title="gitReset(outdir: string)"><wbr />git<wbr />Reset<span class="attachedType"></span></a></li>
<li><a class="reference" href="#gitCheckout%2Cstring%2Cstring"
title="gitCheckout(file, outdir: string)"><wbr />git<wbr />Checkout<span class="attachedType"></span></a></li>
<li><a class="reference" href="#gitPull%2Cstring%2Cstring%2Cstring%2Cstring"
title="gitPull(url: string; outdir = &quot;&quot;; plist = &quot;&quot;; checkout = &quot;&quot;)"><wbr />git<wbr />Pull<span class="attachedType"></span></a></li>
<li><a class="reference" href="#findFile%2Cstring%2Cstring"
title="findFile(file: string; dir: string; recurse = true; first = false; regex = false): string"><wbr />find<wbr />File<span class="attachedType"></span></a></li>
<li><a class="reference" href="#flagBuild%2Cstring%2CopenArray%5Bstring%5D"
title="flagBuild(base: string; flags: openArray[string]): string"><wbr />flag<wbr />Build<span class="attachedType"></span></a></li>
<li><a class="reference" href="#linkLibs%2CopenArray%5Bstring%5D"
title="linkLibs(names: openArray[string]; staticLink = true): string"><wbr />link<wbr />Libs<span class="attachedType"></span></a></li>
<li><a class="reference" href="#configure%2Cstring%2Cstring%2Cstring"
title="configure(path, check: string; flags = &quot;&quot;)"><wbr />configure<span class="attachedType"></span></a></li>
<li><a class="reference" href="#getCmakeIncludePath%2CopenArray%5Bstring%5D"
title="getCmakeIncludePath(paths: openArray[string]): string"><wbr />get<wbr />Cmake<wbr />Include<wbr />Path<span class="attachedType"></span></a></li>
<li><a class="reference" href="#setCmakeProperty%2Cstring%2Cstring%2Cstring%2Cstring"
title="setCmakeProperty(outdir, name, property, value: string)"><wbr />set<wbr />Cmake<wbr />Property<span class="attachedType"></span></a></li>
<li><a class="reference" href="#setCmakeLibName%2Cstring%2Cstring%2Cstring%2Cstring%2Cstring"
title="setCmakeLibName(outdir, name, prefix = &quot;&quot;; oname = &quot;&quot;; suffix = &quot;&quot;)"><wbr />set<wbr />Cmake<wbr />Lib<wbr />Name<span class="attachedType"></span></a></li>
<li><a class="reference" href="#setCmakePositionIndependentCode%2Cstring"
title="setCmakePositionIndependentCode(outdir: string)"><wbr />set<wbr />Cmake<wbr />Position<wbr />Independent<wbr />Code<span class="attachedType"></span></a></li>
<li><a class="reference" href="#cmake%2Cstring%2Cstring%2Cstring"
title="cmake(path, check, flags: string)"><wbr />cmake<span class="attachedType"></span></a></li>
<li><a class="reference" href="#make%2Cstring%2Cstring%2Cstring"
title="make(path, check: string; flags = &quot;&quot;; regex = false)"><wbr />make<span class="attachedType"></span></a></li>
<li><a class="reference" href="#getCompiler"
title="getCompiler(): string"><wbr />get<wbr />Compiler<span class="attachedType"></span></a></li>
<li><a class="reference" href="#getGccPaths%2Cstring"
title="getGccPaths(mode = &quot;c&quot;): seq[string]"><wbr />get<wbr />Gcc<wbr />Paths<span class="attachedType"></span></a></li>
<li><a class="reference" href="#getGccLibPaths%2Cstring"
title="getGccLibPaths(mode = &quot;c&quot;): seq[string]"><wbr />get<wbr />Gcc<wbr />Lib<wbr />Paths<span class="attachedType"></span></a></li>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#17" id="67">Macros</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#setDefines.m"
title="setDefines(defs: static openArray[string]): untyped"><wbr />set<wbr />Defines<span class="attachedType"></span></a></li>
<li><a class="reference" href="#clearDefines.m"
title="clearDefines(): untyped"><wbr />clear<wbr />Defines<span class="attachedType"></span></a></li>
<li><a class="reference" href="#isDefined.m%2Cuntyped"
title="isDefined(def: untyped): untyped"><wbr />is<wbr />Defined<span class="attachedType"></span></a></li>
<li><a class="reference" href="#getHeader.m%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D"
title="getHeader(header: static[string]; giturl: static[string] = &quot;&quot;;
dlurl: static[string] = &quot;&quot;; outdir: static[string] = &quot;&quot;;
conFlags: static[string] = &quot;&quot;; cmakeFlags: static[string] = &quot;&quot;;
makeFlags: static[string] = &quot;&quot;; altNames: static[string] = &quot;&quot;): untyped"><wbr />get<wbr />Header<span class="attachedType"></span></a></li>
</ul>
</li>
</ul>
</div>
<div class="nine columns" id="content">
<div id="tocRoot"></div>
<p class="module-desc"></p>
<div class="section" id="12">
<h1><a class="toc-backref" href="#12">Procs</a></h1>
<dl class="item">
<a id="sanitizePath,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#sanitizePath%2Cstring"><span class="Identifier">sanitizePath</span></a><span class="Other">(</span><span class="Identifier">path</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">noQuote</span> <span class="Other">=</span> <span class="Identifier">false</span><span class="Other">;</span> <span class="Identifier">sep</span> <span class="Other">=</span> <span class="Operator">$</span><span class="CharLit">'/'</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
</dd>
<a id="sleep,int"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#sleep%2Cint"><span class="Identifier">sleep</span></a><span class="Other">(</span><span class="Identifier">milsecs</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
Sleep at compile time
</dd>
<a id="getCurrentNimCompiler"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#getCurrentNimCompiler"><span class="Identifier">getCurrentNimCompiler</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
</dd>
<a id="execAction,string,int,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#execAction%2Cstring%2Cint%2Cstring"><span class="Identifier">execAction</span></a><span class="Other">(</span><span class="Identifier">cmd</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">retry</span> <span class="Other">=</span> <span class="DecNumber">0</span><span class="Other">;</span> <span class="Identifier">die</span> <span class="Other">=</span> <span class="Identifier">true</span><span class="Other">;</span> <span class="Identifier">cache</span> <span class="Other">=</span> <span class="Identifier">false</span><span class="Other">;</span> <span class="Identifier">cacheKey</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">)</span><span class="Other">:</span> <span class="Keyword">tuple</span><span class="Other">[</span>
<span class="Identifier">output</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">,</span> <span class="Identifier">ret</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">]</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span>
<span class="Identifier">ReadEnvEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span>
<span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Execute an external command - supported at compile time</p>
<p>Checks if command exits successfully before returning. If not, an error is raised. Always caches results to be used in nimsuggest or nimcheck mode.</p>
<p><tt class="docutils literal"><span class="pre">retry</span></tt> - number of times command should be retried before error <tt class="docutils literal"><span class="pre">die = false</span></tt> - return on errors <tt class="docutils literal"><span class="pre">cache = true</span></tt> - cache results unless cleared with -f <tt class="docutils literal"><span class="pre">cacheKey</span></tt> - key to create unique cache entry</p>
</dd>
<a id="findExe,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#findExe%2Cstring"><span class="Identifier">findExe</span></a><span class="Other">(</span><span class="Identifier">exe</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span>
<span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadEnvEffect</span><span class="Other">,</span>
<span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
Find the specified executable using the <tt class="docutils literal"><span class="pre">which</span></tt>/<tt class="docutils literal"><span class="pre">where</span></tt> command - supported at compile time
</dd>
<a id="mkDir,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#mkDir%2Cstring"><span class="Identifier">mkDir</span></a><span class="Other">(</span><span class="Identifier">dir</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span>
<span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">ReadEnvEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span>
<span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Create a directory at compile time</p>
<p>The <tt class="docutils literal"><span class="pre">os</span></tt> module is not available at compile time so a few crucial helper functions are included with nimterop.</p>
</dd>
<a id="cpFile,string,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#cpFile%2Cstring%2Cstring"><span class="Identifier">cpFile</span></a><span class="Other">(</span><span class="Identifier">source</span><span class="Other">,</span> <span class="Identifier">dest</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">move</span> <span class="Other">=</span> <span class="Identifier">false</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma">
<span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadEnvEffect</span><span class="Other">,</span>
<span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
Copy a file from <tt class="docutils literal"><span class="pre">source</span></tt> to <tt class="docutils literal"><span class="pre">dest</span></tt> at compile time
</dd>
<a id="mvFile,string,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#mvFile%2Cstring%2Cstring"><span class="Identifier">mvFile</span></a><span class="Other">(</span><span class="Identifier">source</span><span class="Other">,</span> <span class="Identifier">dest</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span>
<span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadEnvEffect</span><span class="Other">,</span>
<span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
Move a file from <tt class="docutils literal"><span class="pre">source</span></tt> to <tt class="docutils literal"><span class="pre">dest</span></tt> at compile time
</dd>
<a id="rmFile,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#rmFile%2Cstring"><span class="Identifier">rmFile</span></a><span class="Other">(</span><span class="Identifier">source</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">dir</span> <span class="Other">=</span> <span class="Identifier">false</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span>
<span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">ReadEnvEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">,</span>
<span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
Remove a file or pattern at compile time
</dd>
<a id="rmDir,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#rmDir%2Cstring"><span class="Identifier">rmDir</span></a><span class="Other">(</span><span class="Identifier">dir</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span>
<span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">ReadEnvEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span>
<span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
Remove a directory or pattern at compile time
</dd>
<a id="getProjectCacheDir,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#getProjectCacheDir%2Cstring"><span class="Identifier">getProjectCacheDir</span></a><span class="Other">(</span><span class="Identifier">name</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">forceClean</span> <span class="Other">=</span> <span class="Identifier">true</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma">
<span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadEnvEffect</span><span class="Other">,</span>
<span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Get a cache directory where all nimterop artifacts can be stored</p>
<p>Projects can use this location to download source code and build binaries that can be then accessed by multiple apps. This is created under the per-user Nim cache directory.</p>
<p>Use <tt class="docutils literal"><span class="pre">name</span></tt> to specify the subdirectory name for a project.</p>
<p><tt class="docutils literal"><span class="pre">forceClean</span></tt> is enabled by default and effectively deletes the folder if Nim is compiled with the <tt class="docutils literal"><span class="pre">-f</span></tt> or <tt class="docutils literal"><span class="pre">--forceBuild</span></tt> flag. This allows any project to start out with a clean cache dir on a forced build.</p>
<p>NOTE: avoid calling <tt class="docutils literal"><span class="pre">getProjectCacheDir()</span></tt> multiple times on the same <tt class="docutils literal"><span class="pre">name</span></tt> when <tt class="docutils literal"><span class="pre">forceClean = true</span></tt> else checked out source might get deleted at the wrong time during build.</p>
<dl class="docutils"><dt>E.g.</dt>
<dd><p><tt class="docutils literal"><span class="pre">nimgit2</span></tt> downloads <tt class="docutils literal"><span class="pre">libgit2</span></tt> source so <tt class="docutils literal"><span class="pre">name = &quot;libgit2&quot;</span></tt></p>
<p><tt class="docutils literal"><span class="pre">nimarchive</span></tt> downloads <tt class="docutils literal"><span class="pre">libarchive</span></tt>, <tt class="docutils literal"><span class="pre">bzlib</span></tt>, <tt class="docutils literal"><span class="pre">liblzma</span></tt> and <tt class="docutils literal"><span class="pre">zlib</span></tt> so <tt class="docutils literal"><span class="pre">name = &quot;nimarchive&quot; / &quot;libarchive&quot;</span></tt> for <tt class="docutils literal"><span class="pre">libarchive</span></tt>, etc.</p>
</dd>
</dl>
</dd>
<a id="extractZip,string,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#extractZip%2Cstring%2Cstring"><span class="Identifier">extractZip</span></a><span class="Other">(</span><span class="Identifier">zipfile</span><span class="Other">,</span> <span class="Identifier">outdir</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span>
<span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadEnvEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadDirEffect</span><span class="Other">,</span>
<span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
Extract a zip file using <tt class="docutils literal"><span class="pre">powershell</span></tt> on Windows and <tt class="docutils literal"><span class="pre">unzip</span></tt> on other systems to the specified output directory
</dd>
<a id="extractTar,string,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#extractTar%2Cstring%2Cstring"><span class="Identifier">extractTar</span></a><span class="Other">(</span><span class="Identifier">tarfile</span><span class="Other">,</span> <span class="Identifier">outdir</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span>
<span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadEnvEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadDirEffect</span><span class="Other">,</span>
<span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
Extract a tar file using <tt class="docutils literal"><span class="pre">tar</span></tt>, <tt class="docutils literal"><span class="pre">7z</span></tt> or <tt class="docutils literal"><span class="pre">7za</span></tt> to the specified output directory
</dd>
<a id="downloadUrl,string,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#downloadUrl%2Cstring%2Cstring"><span class="Identifier">downloadUrl</span></a><span class="Other">(</span><span class="Identifier">url</span><span class="Other">,</span> <span class="Identifier">outdir</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span>
<span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">ReadEnvEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">,</span>
<span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Download a file using <tt class="docutils literal"><span class="pre">curl</span></tt> or <tt class="docutils literal"><span class="pre">wget</span></tt> (or <tt class="docutils literal"><span class="pre">powershell</span></tt> on Windows) to the specified directory</p>
<p>If an archive file, it is automatically extracted after download.</p>
</dd>
<a id="gitReset,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#gitReset%2Cstring"><span class="Identifier">gitReset</span></a><span class="Other">(</span><span class="Identifier">outdir</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span>
<span class="Identifier">ReadEnvEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span>
<span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
Hard reset the git repository at the specified directory
</dd>
<a id="gitCheckout,string,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#gitCheckout%2Cstring%2Cstring"><span class="Identifier">gitCheckout</span></a><span class="Other">(</span><span class="Identifier">file</span><span class="Other">,</span> <span class="Identifier">outdir</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span>
<span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">,</span> <span class="Identifier">ReadEnvEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadDirEffect</span><span class="Other">,</span>
<span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Checkout the specified <tt class="docutils literal"><span class="pre">file</span></tt> in the git repository at <tt class="docutils literal"><span class="pre">outdir</span></tt></p>
<p>This effectively resets all changes in the file and can be used to undo any changes that were made to source files to enable successful wrapping with <tt class="docutils literal"><span class="pre">cImport()</span></tt> or <tt class="docutils literal"><span class="pre">c2nImport()</span></tt>.</p>
</dd>
<a id="gitPull,string,string,string,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#gitPull%2Cstring%2Cstring%2Cstring%2Cstring"><span class="Identifier">gitPull</span></a><span class="Other">(</span><span class="Identifier">url</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">outdir</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">;</span> <span class="Identifier">plist</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">;</span> <span class="Identifier">checkout</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma">
<span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadDirEffect</span><span class="Other">,</span>
<span class="Identifier">ReadEnvEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Pull the specified git repository to the output directory</p>
<p><tt class="docutils literal"><span class="pre">plist</span></tt> is the list of specific files and directories or wildcards to sparsely checkout. Multiple values can be specified one entry per line. It is optional and if omitted, the entire repository will be checked out.</p>
<p><tt class="docutils literal"><span class="pre">checkout</span></tt> is the git tag, branch or commit hash to checkout once the repository is downloaded. This allows for pinning to a specific version of the code.</p>
</dd>
<a id="findFile,string,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#findFile%2Cstring%2Cstring"><span class="Identifier">findFile</span></a><span class="Other">(</span><span class="Identifier">file</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">dir</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">recurse</span> <span class="Other">=</span> <span class="Identifier">true</span><span class="Other">;</span> <span class="Identifier">first</span> <span class="Other">=</span> <span class="Identifier">false</span><span class="Other">;</span> <span class="Identifier">regex</span> <span class="Other">=</span> <span class="Identifier">false</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma">
<span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadEnvEffect</span><span class="Other">,</span>
<span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Find the file in the specified directory</p>
<p><tt class="docutils literal"><span class="pre">file</span></tt> is a regular expression if <tt class="docutils literal"><span class="pre">regex</span></tt> is true</p>
<p>Turn off recursive search with <tt class="docutils literal"><span class="pre">recurse</span></tt> and stop on first match with <tt class="docutils literal"><span class="pre">first</span></tt>. Without it, the shortest match is returned.</p>
</dd>
<a id="flagBuild,string,openArray[string]"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#flagBuild%2Cstring%2CopenArray%5Bstring%5D"><span class="Identifier">flagBuild</span></a><span class="Other">(</span><span class="Identifier">base</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">flags</span><span class="Other">:</span> <span class="Identifier">openArray</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">]</span><span class="Other">,</span>
<span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Simple helper proc to generate flags for <tt class="docutils literal"><span class="pre">configure</span></tt>, <tt class="docutils literal"><span class="pre">cmake</span></tt>, etc.</p>
<p>Every entry in <tt class="docutils literal"><span class="pre">flags</span></tt> is replaced into the <tt class="docutils literal"><span class="pre">base</span></tt> string and concatenated to the result.</p>
<dl class="docutils"><dt>E.g.</dt>
<dd><tt class="docutils literal"><span class="pre">base = &quot;--disable-$#&quot;</span></tt> <tt class="docutils literal"><span class="pre">flags = @[&quot;one&quot;, &quot;two&quot;]</span></tt></dd>
</dl>
<p><tt class="docutils literal"><span class="pre">flagBuild(base, flags) =&gt; &quot; --disable-one --disable-two&quot;</span></tt></p>
</dd>
<a id="linkLibs,openArray[string]"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#linkLibs%2CopenArray%5Bstring%5D"><span class="Identifier">linkLibs</span></a><span class="Other">(</span><span class="Identifier">names</span><span class="Other">:</span> <span class="Identifier">openArray</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">;</span> <span class="Identifier">staticLink</span> <span class="Other">=</span> <span class="Identifier">true</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma">
<span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadEnvEffect</span><span class="Other">,</span>
<span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Create linker flags for specified libraries</p>
<p>Prepends <tt class="docutils literal"><span class="pre">lib</span></tt> to the name so you only need <tt class="docutils literal"><span class="pre">ssl</span></tt> for <tt class="docutils literal"><span class="pre">libssl</span></tt>.</p>
</dd>
<a id="configure,string,string,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#configure%2Cstring%2Cstring%2Cstring"><span class="Identifier">configure</span></a><span class="Other">(</span><span class="Identifier">path</span><span class="Other">,</span> <span class="Identifier">check</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">flags</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma">
<span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadDirEffect</span><span class="Other">,</span>
<span class="Identifier">ReadEnvEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Run the GNU <tt class="docutils literal"><span class="pre">configure</span></tt> command to generate all Makefiles or other build scripts in the specified path</p>
<p>If a <tt class="docutils literal"><span class="pre">configure</span></tt> script is not present and an <tt class="docutils literal"><span class="pre">autogen.sh</span></tt> script is present, it will be run before attempting <tt class="docutils literal"><span class="pre">configure</span></tt>.</p>
<p>Next, if <tt class="docutils literal"><span class="pre">configure.ac</span></tt> or <tt class="docutils literal"><span class="pre">configure.in</span></tt> exist, <tt class="docutils literal"><span class="pre">autoreconf</span></tt> will be executed.</p>
<p><tt class="docutils literal"><span class="pre">check</span></tt> is a file that will be generated by the <tt class="docutils literal"><span class="pre">configure</span></tt> command. This is required to prevent configure from running on every build. It is relative to the <tt class="docutils literal"><span class="pre">path</span></tt> and should not be an absolute path.</p>
<p><tt class="docutils literal"><span class="pre">flags</span></tt> are any flags that should be passed to the <tt class="docutils literal"><span class="pre">configure</span></tt> command.</p>
</dd>
<a id="getCmakeIncludePath,openArray[string]"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#getCmakeIncludePath%2CopenArray%5Bstring%5D"><span class="Identifier">getCmakeIncludePath</span></a><span class="Other">(</span><span class="Identifier">paths</span><span class="Other">:</span> <span class="Identifier">openArray</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Create a <tt class="docutils literal"><span class="pre">cmake</span></tt> flag to specify custom include paths</p>
<p>Result can be included in the <tt class="docutils literal"><span class="pre">flag</span></tt> parameter for <tt class="docutils literal"><span class="pre">cmake()</span></tt> or the <tt class="docutils literal"><span class="pre">cmakeFlags</span></tt> parameter for <tt class="docutils literal"><span class="pre">getHeader()</span></tt>.</p>
</dd>
<a id="setCmakeProperty,string,string,string,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#setCmakeProperty%2Cstring%2Cstring%2Cstring%2Cstring"><span class="Identifier">setCmakeProperty</span></a><span class="Other">(</span><span class="Identifier">outdir</span><span class="Other">,</span> <span class="Identifier">name</span><span class="Other">,</span> <span class="Identifier">property</span><span class="Other">,</span> <span class="Identifier">value</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma">
<span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">]</span><span class="Other">,</span>
<span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Set a <tt class="docutils literal"><span class="pre">cmake</span></tt> property in <tt class="docutils literal"><span class="pre">outdir / CMakeLists.txt</span></tt> - usable in the <tt class="docutils literal"><span class="pre">xxxPreBuild</span></tt> hook for <tt class="docutils literal"><span class="pre">getHeader()</span></tt></p>
<p><tt class="docutils literal"><span class="pre">set_target_properties(name PROPERTIES property &quot;value&quot;)</span></tt></p>
</dd>
<a id="setCmakeLibName,string,string,string,string,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#setCmakeLibName%2Cstring%2Cstring%2Cstring%2Cstring%2Cstring"><span class="Identifier">setCmakeLibName</span></a><span class="Other">(</span><span class="Identifier">outdir</span><span class="Other">,</span> <span class="Identifier">name</span><span class="Other">,</span> <span class="Identifier">prefix</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">;</span> <span class="Identifier">oname</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">;</span> <span class="Identifier">suffix</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma">
<span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">IOError</span><span class="Other">]</span><span class="Other">,</span>
<span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Set a <tt class="docutils literal"><span class="pre">cmake</span></tt> property in <tt class="docutils literal"><span class="pre">outdir / CMakeLists.txt</span></tt> to specify a custom library output name - usable in the <tt class="docutils literal"><span class="pre">xxxPreBuild</span></tt> hook for <tt class="docutils literal"><span class="pre">getHeader()</span></tt></p>
<p><tt class="docutils literal"><span class="pre">prefix</span></tt> is typically <tt class="docutils literal"><span class="pre">lib</span></tt> <tt class="docutils literal"><span class="pre">oname</span></tt> is the library name <tt class="docutils literal"><span class="pre">suffix</span></tt> is typically <tt class="docutils literal"><span class="pre">.a</span></tt></p>
<p>Sometimes, <tt class="docutils literal"><span class="pre">cmake</span></tt> generates non-standard library names - e.g. zlib compiles to <tt class="docutils literal"><span class="pre">libzlibstatic.a</span></tt> on Windows. This proc can help rename it to <tt class="docutils literal"><span class="pre">libzlib.a</span></tt> so that <tt class="docutils literal"><span class="pre">getHeader()</span></tt> can find it after the library is compiled.</p>
<p><pre class="listing">
set_target_properties(name PROPERTIES PREFIX &quot;prefix&quot;)
set_target_properties(name PROPERTIES OUTPUT_NAME &quot;oname&quot;)
set_target_properties(name PROPERTIES SUFFIX &quot;suffix&quot;)
</pre></p>
</dd>
<a id="setCmakePositionIndependentCode,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#setCmakePositionIndependentCode%2Cstring"><span class="Identifier">setCmakePositionIndependentCode</span></a><span class="Other">(</span><span class="Identifier">outdir</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">]</span><span class="Other">,</span>
<span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
Set a <tt class="docutils literal"><span class="pre">cmake</span></tt> directive to create libraries with -fPIC enabled
</dd>
<a id="cmake,string,string,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#cmake%2Cstring%2Cstring%2Cstring"><span class="Identifier">cmake</span></a><span class="Other">(</span><span class="Identifier">path</span><span class="Other">,</span> <span class="Identifier">check</span><span class="Other">,</span> <span class="Identifier">flags</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span>
<span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">ReadEnvEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">,</span>
<span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Run the <tt class="docutils literal"><span class="pre">cmake</span></tt> command to generate all Makefiles or other build scripts in the specified path</p>
<p><tt class="docutils literal"><span class="pre">path</span></tt> will be created since typically <tt class="docutils literal"><span class="pre">cmake</span></tt> is run in an empty directory.</p>
<p><tt class="docutils literal"><span class="pre">check</span></tt> is a file that will be generated by the <tt class="docutils literal"><span class="pre">cmake</span></tt> command. This is required to prevent <tt class="docutils literal"><span class="pre">cmake</span></tt> from running on every build. It is relative to the <tt class="docutils literal"><span class="pre">path</span></tt> and should not be an absolute path.</p>
<p><tt class="docutils literal"><span class="pre">flags</span></tt> are any flags that should be passed to the <tt class="docutils literal"><span class="pre">cmake</span></tt> command. Unlike <tt class="docutils literal"><span class="pre">configure</span></tt>, it is required since typically it will be the path to the repository, typically <tt class="docutils literal"><span class="pre">..</span></tt> when <tt class="docutils literal"><span class="pre">path</span></tt> is a subdir.</p>
</dd>
<a id="make,string,string,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#make%2Cstring%2Cstring%2Cstring"><span class="Identifier">make</span></a><span class="Other">(</span><span class="Identifier">path</span><span class="Other">,</span> <span class="Identifier">check</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">flags</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">;</span> <span class="Identifier">regex</span> <span class="Other">=</span> <span class="Identifier">false</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma">
<span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadEnvEffect</span><span class="Other">,</span>
<span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Run the <tt class="docutils literal"><span class="pre">make</span></tt> command to build all binaries in the specified path</p>
<p><tt class="docutils literal"><span class="pre">check</span></tt> is a file that will be generated by the <tt class="docutils literal"><span class="pre">make</span></tt> command. This is required to prevent <tt class="docutils literal"><span class="pre">make</span></tt> from running on every build. It is relative to the <tt class="docutils literal"><span class="pre">path</span></tt> and should not be an absolute path.</p>
<p><tt class="docutils literal"><span class="pre">flags</span></tt> are any flags that should be passed to the <tt class="docutils literal"><span class="pre">make</span></tt> command.</p>
<p><tt class="docutils literal"><span class="pre">regex</span></tt> can be set to true if <tt class="docutils literal"><span class="pre">check</span></tt> is a regular expression.</p>
<p>If <tt class="docutils literal"><span class="pre">make.exe</span></tt> is missing and <tt class="docutils literal"><span class="pre">mingw32-make.exe</span></tt> is available, it will be copied over to make.exe in the same location.</p>
</dd>
<a id="getCompiler"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#getCompiler"><span class="Identifier">getCompiler</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadEnvEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
</dd>
<a id="getGccPaths,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#getGccPaths%2Cstring"><span class="Identifier">getGccPaths</span></a><span class="Other">(</span><span class="Identifier">mode</span> <span class="Other">=</span> <span class="StringLit">&quot;c&quot;</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span>
<span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadEnvEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadDirEffect</span><span class="Other">,</span>
<span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
</dd>
<a id="getGccLibPaths,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#getGccLibPaths%2Cstring"><span class="Identifier">getGccLibPaths</span></a><span class="Other">(</span><span class="Identifier">mode</span> <span class="Other">=</span> <span class="StringLit">&quot;c&quot;</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma">
<span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadEnvEffect</span><span class="Other">,</span>
<span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadDirEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
</dd>
</dl></div>
<div class="section" id="17">
<h1><a class="toc-backref" href="#17">Macros</a></h1>
<dl class="item">
<a id="setDefines.m"></a>
<dt><pre><span class="Keyword">macro</span> <a href="#setDefines.m"><span class="Identifier">setDefines</span></a><span class="Other">(</span><span class="Identifier">defs</span><span class="Other">:</span> <span class="Identifier">static</span> <span class="Identifier">openArray</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
<p>Specify <tt class="docutils literal"><span class="pre">-d:xxx</span></tt> values in code instead of having to rely on the command line or <tt class="docutils literal"><span class="pre">cfg</span></tt> or <tt class="docutils literal"><span class="pre">nims</span></tt> files.</p>
<p>At this time, Nim does not allow creation of <tt class="docutils literal"><span class="pre">-d:xxx</span></tt> defines in code. In addition, Nim only loads config files for the module being compiled but not for imported packages. This becomes a challenge when wanting to ship a wrapper library that wants to control <tt class="docutils literal"><span class="pre">getHeader()</span></tt> for an underlying package.</p>
<blockquote><p>E.g. nimarchive wanting to set <tt class="docutils literal"><span class="pre">-d:lzmaStatic</span></tt></p></blockquote>
<p>The consumer of nimarchive would need to set such defines as part of their project, making it inconvenient.</p>
<p>By calling this proc with the defines preferred before importing such a module, the caller can set the behavior in code instead.</p>
<pre class="listing"><span class="Identifier">setDefines</span><span class="Punctuation">(</span><span class="Operator">@</span><span class="Punctuation">[</span><span class="StringLit">&quot;lzmaStatic&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;lzmaDL&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;lzmaSetVer=5.2.4&quot;</span><span class="Punctuation">]</span><span class="Punctuation">)</span>
<span class="Keyword">import</span> <span class="Identifier">lzma</span></pre>
</dd>
<a id="clearDefines.m"></a>
<dt><pre><span class="Keyword">macro</span> <a href="#clearDefines.m"><span class="Identifier">clearDefines</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Clear all defines set using <tt class="docutils literal"><span class="pre">setDefines()</span></tt>.
</dd>
<a id="isDefined.m,untyped"></a>
<dt><pre><span class="Keyword">macro</span> <a href="#isDefined.m%2Cuntyped"><span class="Identifier">isDefined</span></a><span class="Other">(</span><span class="Identifier">def</span><span class="Other">:</span> <span class="Identifier">untyped</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Check if <tt class="docutils literal"><span class="pre">-d:xxx</span></tt> is set globally or via <tt class="docutils literal"><span class="pre">setDefines()</span></tt>
</dd>
<a id="getHeader.m,static[string],static[string],static[string],static[string],static[string],static[string],static[string],static[string]"></a>
<dt><pre><span class="Keyword">macro</span> <a href="#getHeader.m%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D"><span class="Identifier">getHeader</span></a><span class="Other">(</span><span class="Identifier">header</span><span class="Other">:</span> <span class="Identifier">static</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">;</span> <span class="Identifier">giturl</span><span class="Other">:</span> <span class="Identifier">static</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">;</span>
<span class="Identifier">dlurl</span><span class="Other">:</span> <span class="Identifier">static</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">;</span> <span class="Identifier">outdir</span><span class="Other">:</span> <span class="Identifier">static</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">;</span>
<span class="Identifier">conFlags</span><span class="Other">:</span> <span class="Identifier">static</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">;</span> <span class="Identifier">cmakeFlags</span><span class="Other">:</span> <span class="Identifier">static</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">;</span>
<span class="Identifier">makeFlags</span><span class="Other">:</span> <span class="Identifier">static</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">;</span> <span class="Identifier">altNames</span><span class="Other">:</span> <span class="Identifier">static</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
<p>Get the path to a header file for wrapping with <a class="reference external" href="cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring">cImport()</a> or <a class="reference external" href="cimport.html#c2nImport.m%2C%2Cstring%2Cstring%2Cstring">c2nImport()</a>.</p>
<p>This proc checks <tt class="docutils literal"><span class="pre">-d:xxx</span></tt> defines based on the header name (e.g. lzma from lzma.h), and accordingly employs different ways to obtain the source.</p>
<p><tt class="docutils literal"><span class="pre">-d:xxxStd</span></tt> - search standard system paths. E.g. <tt class="docutils literal"><span class="pre">/usr/include</span></tt> and <tt class="docutils literal"><span class="pre">/usr/lib</span></tt> on Linux <tt class="docutils literal"><span class="pre">-d:xxxGit</span></tt> - clone source from a git repo specified in <tt class="docutils literal"><span class="pre">giturl</span></tt> <tt class="docutils literal"><span class="pre">-d:xxxDL</span></tt> - download source from <tt class="docutils literal"><span class="pre">dlurl</span></tt> and extract if required</p>
<p>This allows a single wrapper to be used in different ways depending on the user's needs. If no <tt class="docutils literal"><span class="pre">-d:xxx</span></tt> defines are specified, <tt class="docutils literal"><span class="pre">outdir</span></tt> will be searched for the header as is.</p>
<p>If multiple <tt class="docutils literal"><span class="pre">-d:xxx</span></tt> defines are specified, precedence is <tt class="docutils literal"><span class="pre">Std</span></tt> and then <tt class="docutils literal"><span class="pre">Git</span></tt> or <tt class="docutils literal"><span class="pre">DL</span></tt>. This allows using a system installed library if available before falling back to manual building.</p>
<p><tt class="docutils literal"><span class="pre">-d:xxxSetVer=x.y.z</span></tt> can be used to specify which version to use. It is used as a tag name for Git whereas for DL, it replaces <tt class="docutils literal"><span class="pre">$1</span></tt> in the URL defined.</p>
<p>All defines can also be set in code using <tt class="docutils literal"><span class="pre">setDefines()</span></tt>.</p>
<p>The library is then configured (with <tt class="docutils literal"><span class="pre">cmake</span></tt> or <tt class="docutils literal"><span class="pre">autotools</span></tt> if possible) and built using <tt class="docutils literal"><span class="pre">make</span></tt>, unless using <tt class="docutils literal"><span class="pre">-d:xxxStd</span></tt> which presumes that the system package manager was used to install prebuilt headers and binaries.</p>
<p>The header path is stored in <tt class="docutils literal"><span class="pre">const xxxPath</span></tt> and can be used in a <tt class="docutils literal"><span class="pre">cImport()</span></tt> call in the calling wrapper. The dynamic library path is stored in <tt class="docutils literal"><span class="pre">const xxxLPath</span></tt> and can be used for the <tt class="docutils literal"><span class="pre">dynlib</span></tt> parameter (within quotes) or with <tt class="docutils literal"><span class="pre">{.passL.}</span></tt>.</p>
<p><tt class="docutils literal"><span class="pre">-d:xxxStatic</span></tt> can be specified to statically link with the library instead. This will automatically add a <tt class="docutils literal"><span class="pre">{.passL.}</span></tt> call to the static library for convenience.</p>
<p><tt class="docutils literal"><span class="pre">conFlags</span></tt>, <tt class="docutils literal"><span class="pre">cmakeFlags</span></tt> and <tt class="docutils literal"><span class="pre">makeFlags</span></tt> allow sending custom parameters to <tt class="docutils literal"><span class="pre">configure</span></tt>, <tt class="docutils literal"><span class="pre">cmake</span></tt> and <tt class="docutils literal"><span class="pre">make</span></tt> in case additional configuration is required as part of the build process.</p>
<p><tt class="docutils literal"><span class="pre">altNames</span></tt> is a list of alternate names for the library - e.g. zlib uses <tt class="docutils literal"><span class="pre">zlib.h</span></tt> for the header but the typical lib name is <tt class="docutils literal"><span class="pre">libz.so</span></tt> and not <tt class="docutils literal"><span class="pre">libzlib.so</span></tt>. However, it is libzlib.dll on Windows if built with cmake. In this case, <tt class="docutils literal"><span class="pre">altNames = &quot;z,zlib&quot;</span></tt>. Comma separate for multiple alternate names without spaces.</p>
<p>The original header name is not included by default if <tt class="docutils literal"><span class="pre">altNames</span></tt> is set since it could cause the wrong lib to be selected. E.g. <tt class="docutils literal"><span class="pre">SDL2/SDL.h</span></tt> could pick <tt class="docutils literal"><span class="pre">libSDL.so</span></tt> even if <tt class="docutils literal"><span class="pre">altNames = &quot;SDL2&quot;</span></tt>. Explicitly include it in <tt class="docutils literal"><span class="pre">altNames</span></tt> like the <tt class="docutils literal"><span class="pre">zlib</span></tt> example when required.</p>
<p><tt class="docutils literal"><span class="pre">xxxPreBuild</span></tt> is a hook that is called after the source code is pulled from Git or downloaded but before the library is built. This might be needed if some initial prep needs to be done before compilation. A few values are provided to the hook to help provide context:</p>
<p><tt class="docutils literal"><span class="pre">outdir</span></tt> is the same <tt class="docutils literal"><span class="pre">outdir</span></tt> passed in and <tt class="docutils literal"><span class="pre">header</span></tt> is the discovered header path in the downloaded source code.</p>
<p>Simply define <tt class="docutils literal"><span class="pre">proc xxxPreBuild(outdir, header: string)</span></tt> in the wrapper and it will get called prior to the build process.</p>
</dd>
</dl></div>
</div>
</div>
<div class="row">
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small style="color: var(--hint);">Made with Nim. Generated: 2020-03-24 20:55:39 UTC</small>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -1,34 +0,0 @@
sanitizePath build.html#sanitizePath,string build: sanitizePath(path: string; noQuote = false; sep = $&apos;/&apos;): string
sleep build.html#sleep,int build: sleep(milsecs: int)
getCurrentNimCompiler build.html#getCurrentNimCompiler build: getCurrentNimCompiler(): string
execAction build.html#execAction,string,int,string build: execAction(cmd: string; retry = 0; die = true; cache = false; cacheKey = &quot;&quot;): tuple[\n output: string, ret: int]
findExe build.html#findExe,string build: findExe(exe: string): string
mkDir build.html#mkDir,string build: mkDir(dir: string)
cpFile build.html#cpFile,string,string build: cpFile(source, dest: string; move = false)
mvFile build.html#mvFile,string,string build: mvFile(source, dest: string)
rmFile build.html#rmFile,string build: rmFile(source: string; dir = false)
rmDir build.html#rmDir,string build: rmDir(dir: string)
getProjectCacheDir build.html#getProjectCacheDir,string build: getProjectCacheDir(name: string; forceClean = true): string
extractZip build.html#extractZip,string,string build: extractZip(zipfile, outdir: string)
extractTar build.html#extractTar,string,string build: extractTar(tarfile, outdir: string)
downloadUrl build.html#downloadUrl,string,string build: downloadUrl(url, outdir: string)
gitReset build.html#gitReset,string build: gitReset(outdir: string)
gitCheckout build.html#gitCheckout,string,string build: gitCheckout(file, outdir: string)
gitPull build.html#gitPull,string,string,string,string build: gitPull(url: string; outdir = &quot;&quot;; plist = &quot;&quot;; checkout = &quot;&quot;)
findFile build.html#findFile,string,string build: findFile(file: string; dir: string; recurse = true; first = false; regex = false): string
flagBuild build.html#flagBuild,string,openArray[string] build: flagBuild(base: string; flags: openArray[string]): string
linkLibs build.html#linkLibs,openArray[string] build: linkLibs(names: openArray[string]; staticLink = true): string
configure build.html#configure,string,string,string build: configure(path, check: string; flags = &quot;&quot;)
getCmakeIncludePath build.html#getCmakeIncludePath,openArray[string] build: getCmakeIncludePath(paths: openArray[string]): string
setCmakeProperty build.html#setCmakeProperty,string,string,string,string build: setCmakeProperty(outdir, name, property, value: string)
setCmakeLibName build.html#setCmakeLibName,string,string,string,string,string build: setCmakeLibName(outdir, name, prefix = &quot;&quot;; oname = &quot;&quot;; suffix = &quot;&quot;)
setCmakePositionIndependentCode build.html#setCmakePositionIndependentCode,string build: setCmakePositionIndependentCode(outdir: string)
cmake build.html#cmake,string,string,string build: cmake(path, check, flags: string)
make build.html#make,string,string,string build: make(path, check: string; flags = &quot;&quot;; regex = false)
getCompiler build.html#getCompiler build: getCompiler(): string
getGccPaths build.html#getGccPaths,string build: getGccPaths(mode = &quot;c&quot;): seq[string]
getGccLibPaths build.html#getGccLibPaths,string build: getGccLibPaths(mode = &quot;c&quot;): seq[string]
setDefines build.html#setDefines.m build: setDefines(defs: static openArray[string]): untyped
clearDefines build.html#clearDefines.m build: clearDefines(): untyped
isDefined build.html#isDefined.m,untyped build: isDefined(def: untyped): untyped
getHeader build.html#getHeader.m,static[string],static[string],static[string],static[string],static[string],static[string],static[string],static[string] build: getHeader(header: static[string]; giturl: static[string] = &quot;&quot;;\n dlurl: static[string] = &quot;&quot;; outdir: static[string] = &quot;&quot;;\n conFlags: static[string] = &quot;&quot;; cmakeFlags: static[string] = &quot;&quot;;\n makeFlags: static[string] = &quot;&quot;; altNames: static[string] = &quot;&quot;): untyped

View file

@ -1,410 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
<!-- Google fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
<!-- CSS -->
<title>cimport</title>
<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
<script type="text/javascript" src="dochack.js"></script>
<script type="text/javascript">
function main() {
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
toggleSwitch.addEventListener('change', switchTheme, false);
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-theme', "dark");
toggleSwitch.checked = true;
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
document.documentElement.setAttribute('data-theme', "light");
toggleSwitch.checked = false;
} else {
const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null;
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
if (currentTheme === 'dark') {
toggleSwitch.checked = true;
}
}
}
}
</script>
</head>
<body onload="main()">
<div class="document" id="documentId">
<div class="container">
<h1 class="title">cimport</h1>
<div class="row">
<div class="three columns">
<div class="theme-switch-wrapper">
<label class="theme-switch" for="checkbox">
<input type="checkbox" id="checkbox" />
<div class="slider round"></div>
</label>
&nbsp;&nbsp;&nbsp; <em>Dark Mode</em>
</div>
<div id="global-links">
<ul class="simple">
</ul>
</div>
<div id="searchInputDiv">
Search: <input type="text" id="searchInput"
onkeyup="search()" />
</div>
<div>
Group by:
<select onchange="groupBy(this.value)">
<option value="section">Section</option>
<option value="type">Type</option>
</select>
</div>
<ul class="simple simple-toc" id="toc-list">
<li>
<a class="reference reference-toplevel" href="#6" id="56">Imports</a>
<ul class="simple simple-toc-section">
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#7" id="57">Types</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#Feature"
title="Feature = enum
ast2"><wbr />Feature<span class="attachedType"></span></a></li>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#12" id="62">Procs</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#cSkipSymbol%2Cseq%5BT%5D%5Bstring%5D"
title="cSkipSymbol(skips: seq[string])"><wbr />c<wbr />Skip<wbr />Symbol<span class="attachedType"></span></a></li>
<li><a class="reference" href="#cSearchPath%2Cstring"
title="cSearchPath(path: string): string"><wbr />c<wbr />Search<wbr />Path<span class="attachedType"></span></a></li>
<li><a class="reference" href="#cDebug"
title="cDebug()"><wbr />c<wbr />Debug<span class="attachedType"></span></a></li>
<li><a class="reference" href="#cDisableCaching"
title="cDisableCaching()"><wbr />c<wbr />Disable<wbr />Caching<span class="attachedType"></span></a></li>
<li><a class="reference" href="#cAddSearchDir%2Cstring"
title="cAddSearchDir(dir: string)"><wbr />c<wbr />Add<wbr />Search<wbr />Dir<span class="attachedType"></span></a></li>
<li><a class="reference" href="#cAddStdDir%2Cstring"
title="cAddStdDir(mode = &quot;c&quot;)"><wbr />c<wbr />Add<wbr />Std<wbr />Dir<span class="attachedType"></span></a></li>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#17" id="67">Macros</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#cOverride.m"
title="cOverride(body): untyped"><wbr />c<wbr />Override<span class="attachedType"></span></a></li>
<li><a class="reference" href="#cPlugin.m"
title="cPlugin(body): untyped"><wbr />c<wbr />Plugin<span class="attachedType"></span></a></li>
<li><a class="reference" href="#cDefine.m%2C%2Cstring"
title="cDefine(name: static string; val: static string = &quot;&quot;): untyped"><wbr />c<wbr />Define<span class="attachedType"></span></a></li>
<li><a class="reference" href="#cIncludeDir.m"
title="cIncludeDir(dir: static string): untyped"><wbr />c<wbr />Include<wbr />Dir<span class="attachedType"></span></a></li>
<li><a class="reference" href="#cCompile.m%2C%2Cstring%2Cstring"
title="cCompile(path: static string; mode = &quot;c&quot;; exclude = &quot;&quot;): untyped"><wbr />c<wbr />Compile<span class="attachedType"></span></a></li>
<li><a class="reference" href="#cImport.m%2C%2Cstring%2Cstring%2Cstring"
title="cImport(filename: static string; recurse: static bool = false;
dynlib: static string = &quot;&quot;; mode: static string = &quot;c&quot;; flags: static string = &quot;&quot;): untyped"><wbr />c<wbr />Import<span class="attachedType"></span></a></li>
<li><a class="reference" href="#c2nImport.m%2C%2Cstring%2Cstring%2Cstring"
title="c2nImport(filename: static string; recurse: static bool = false;
dynlib: static string = &quot;&quot;; mode: static string = &quot;c&quot;; flags: static string = &quot;&quot;): untyped"><wbr />c2n<wbr />Import<span class="attachedType"></span></a></li>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#19" id="69">Exports</a>
<ul class="simple simple-toc-section">
</ul>
</li>
</ul>
</div>
<div class="nine columns" id="content">
<div id="tocRoot"></div>
<p class="module-desc"><p>This is the main nimterop import file to help with wrapping C/C++ source code.</p>
<p>Check out <a class="reference external" href="https://github.com/nimterop/nimterop/blob/master/nimterop/template.nim">template.nim</a> as a starting point for wrapping a new library. The template can be copied and trimmed down and modified as required. <a class="reference external" href="https://github.com/nimterop/nimterop/blob/master/nimterop/templite.nim">templite.nim</a> is a shorter version for more experienced users.</p>
<p>All <tt class="docutils literal"><span class="pre">{.compileTime.}</span></tt> procs must be used in a compile time context, e.g. using:</p>
<pre class="listing"><span class="Keyword">static</span><span class="Punctuation">:</span>
<span class="Identifier">cAddStdDir</span><span class="Punctuation">(</span><span class="Punctuation">)</span></pre></p>
<div class="section" id="6">
<h1><a class="toc-backref" href="#6">Imports</a></h1>
<dl class="item">
<a class="reference external" href="plugin.html">plugin</a>, <a class="reference external" href="build.html">build</a>, <a class="reference external" href="paths.html">paths</a>, <a class="reference external" href="types.html">types</a>
</dl></div>
<div class="section" id="7">
<h1><a class="toc-backref" href="#7">Types</a></h1>
<dl class="item">
<a id="Feature"></a>
<dt><pre><a href="cimport.html#Feature"><span class="Identifier">Feature</span></a> <span class="Other">=</span> <span class="Keyword">enum</span>
<span class="Identifier">ast2</span></pre></dt>
<dd>
</dd>
</dl></div>
<div class="section" id="12">
<h1><a class="toc-backref" href="#12">Procs</a></h1>
<dl class="item">
<a id="cSkipSymbol,seq[T][string]"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#cSkipSymbol%2Cseq%5BT%5D%5Bstring%5D"><span class="Identifier">cSkipSymbol</span></a><span class="Other">(</span><span class="Identifier">skips</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">compileTime</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Similar to <a class="reference external" href="cimport.html#cOverride.m">cOverride()</a>, this macro allows filtering out symbols not of interest from the generated output.</p>
<p><a class="reference external" href="cimport.html#cSkipSymbol%2Cseq[T][string]">cSkipSymbol()</a> only affects calls to <a class="reference external" href="cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring">cImport()</a> that follow it.</p>
<p><strong class="examples_text">Examples:</strong></p>
<pre class="listing"><span class="Keyword">static</span> <span class="Other">:</span>
<span class="Identifier">cSkipSymbol</span> <span class="Operator">@</span><span class="Other">[</span><span class="StringLit">&quot;proc1&quot;</span><span class="Other">,</span> <span class="StringLit">&quot;Type2&quot;</span><span class="Other">]</span></pre>
</dd>
<a id="cSearchPath,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#cSearchPath%2Cstring"><span class="Identifier">cSearchPath</span></a><span class="Other">(</span><span class="Identifier">path</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">compileTime</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadDirEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Get full path to file or directory <tt class="docutils literal"><span class="pre">path</span></tt> in search path configured using <a class="reference external" href="cimport.html#cAddSearchDir%2Cstring">cAddSearchDir()</a> and <a class="reference external" href="cimport.html#cAddStdDir,string">cAddStdDir()</a>.</p>
<p>This can be used to locate files or directories that can be passed onto <a class="reference external" href="cimport.html#cCompile.m%2C%2Cstring%2Cstring">cCompile()</a>, <a class="reference external" href="cimport.html#cIncludeDir.m">cIncludeDir()</a> and <a class="reference external" href="cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring">cImport()</a>.</p>
</dd>
<a id="cDebug"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#cDebug"><span class="Identifier">cDebug</span></a><span class="Other">(</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">compileTime</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
Enable debug messages and display the generated Nim code
</dd>
<a id="cDisableCaching"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#cDisableCaching"><span class="Identifier">cDisableCaching</span></a><span class="Other">(</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">compileTime</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Disable caching of generated Nim code - useful during wrapper development</p>
<p>If files included by header being processed by <a class="reference external" href="cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring">cImport()</a> change and affect the generated content, they will be ignored and the cached value will continue to be used . Use <a class="reference external" href="cimport.html#cDisableCaching">cDisableCaching()</a> to avoid this scenario during development.</p>
<p><tt class="docutils literal"><span class="pre">nim -f</span></tt> was broken prior to 0.19.4 but can also be used to flush the cached content.</p>
</dd>
<a id="cAddSearchDir,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#cAddSearchDir%2Cstring"><span class="Identifier">cAddSearchDir</span></a><span class="Other">(</span><span class="Identifier">dir</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">compileTime</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
Add directory <tt class="docutils literal"><span class="pre">dir</span></tt> to the search path used in calls to <a class="reference external" href="cimport.html#cSearchPath,string">cSearchPath()</a>.
<p><strong class="examples_text">Examples:</strong></p>
<pre class="listing"><span class="Keyword">import</span>
<span class="Identifier">paths</span><span class="Other">,</span> <span class="Identifier">os</span>
<span class="Keyword">static</span> <span class="Other">:</span>
<span class="Identifier">cAddSearchDir</span> <span class="Identifier">testsIncludeDir</span><span class="Other">(</span><span class="Other">)</span>
<span class="Identifier">doAssert</span> <span class="Identifier">cSearchPath</span><span class="Other">(</span><span class="StringLit">&quot;test.h&quot;</span><span class="Other">)</span><span class="Other">.</span><span class="Identifier">existsFile</span></pre>
</dd>
<a id="cAddStdDir,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#cAddStdDir%2Cstring"><span class="Identifier">cAddStdDir</span></a><span class="Other">(</span><span class="Identifier">mode</span> <span class="Other">=</span> <span class="StringLit">&quot;c&quot;</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">compileTime</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">IOError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span>
<span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">Defect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadEnvEffect</span><span class="Other">,</span> <span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">ReadDirEffect</span><span class="Other">,</span>
<span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">ExecIOEffect</span><span class="Other">,</span> <span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
Add the standard <tt class="docutils literal"><span class="pre">c</span></tt> [default] or <tt class="docutils literal"><span class="pre">cpp</span></tt> include paths to search path used in calls to <a class="reference external" href="cimport.html#cSearchPath,string">cSearchPath()</a>
<p><strong class="examples_text">Examples:</strong></p>
<pre class="listing"><span class="Keyword">static</span> <span class="Other">:</span>
<span class="Identifier">cAddStdDir</span><span class="Other">(</span><span class="Other">)</span>
<span class="Keyword">import</span>
<span class="Identifier">os</span>
<span class="Identifier">doAssert</span> <span class="Identifier">cSearchPath</span><span class="Other">(</span><span class="StringLit">&quot;math.h&quot;</span><span class="Other">)</span><span class="Other">.</span><span class="Identifier">existsFile</span></pre>
</dd>
</dl></div>
<div class="section" id="17">
<h1><a class="toc-backref" href="#17">Macros</a></h1>
<dl class="item">
<a id="cOverride.m"></a>
<dt><pre><span class="Keyword">macro</span> <a href="#cOverride.m"><span class="Identifier">cOverride</span></a><span class="Other">(</span><span class="Identifier">body</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
<p>When the wrapper code generated by nimterop is missing certain symbols or not accurate, it may be required to hand wrap them. Define them in a <a class="reference external" href="cimport.html#cOverride.m">cOverride()</a> macro block so that Nimterop uses these definitions instead.</p>
<p>For example:</p>
<pre class="listing"><span class="Keyword">int</span> <span class="Identifier">svGetCallerInfo</span><span class="Punctuation">(</span><span class="Keyword">const</span> <span class="Keyword">char</span><span class="Operator">**</span> <span class="Identifier">fileName</span><span class="Punctuation">,</span> <span class="Keyword">int</span> <span class="Operator">*</span><span class="Identifier">lineNumber</span><span class="Punctuation">)</span><span class="Punctuation">;</span></pre><p>This might map to:</p>
<pre class="listing"><span class="Keyword">proc</span> <span class="Identifier">svGetCallerInfo</span><span class="Punctuation">(</span><span class="Identifier">fileName</span><span class="Punctuation">:</span> <span class="Keyword">ptr</span> <span class="Identifier">cstring</span><span class="Punctuation">;</span> <span class="Identifier">lineNumber</span><span class="Punctuation">:</span> <span class="Keyword">var</span> <span class="Identifier">cint</span><span class="Punctuation">)</span></pre><p>Whereas it might mean:</p>
<pre class="listing"><span class="Identifier">cOverride</span><span class="Punctuation">:</span>
<span class="Keyword">proc</span> <span class="Identifier">svGetCallerInfo</span><span class="Punctuation">(</span><span class="Identifier">fileName</span><span class="Punctuation">:</span> <span class="Keyword">var</span> <span class="Identifier">cstring</span><span class="Punctuation">;</span> <span class="Identifier">lineNumber</span><span class="Punctuation">:</span> <span class="Keyword">var</span> <span class="Identifier">cint</span><span class="Punctuation">)</span></pre><p>Using the <a class="reference external" href="cimport.html#cOverride.m">cOverride()</a> block, nimterop can be instructed to use this definition of <tt class="docutils literal"><span class="pre">svGetCallerInfo()</span></tt> instead. This works for procs, consts and types.</p>
<p><tt class="docutils literal"><span class="pre">cOverride()</span></tt> only affects the next <tt class="docutils literal"><span class="pre">cImport()</span></tt> call. This is because any recognized symbols get overridden in place and any remaining symbols get added to the top. If reused, the next <tt class="docutils literal"><span class="pre">cImport()</span></tt> would add those symbols again leading to redefinition errors.</p>
</dd>
<a id="cPlugin.m"></a>
<dt><pre><span class="Keyword">macro</span> <a href="#cPlugin.m"><span class="Identifier">cPlugin</span></a><span class="Other">(</span><span class="Identifier">body</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
When <a class="reference external" href="cimport.html#cOverride.m">cOverride()</a> and <a class="reference external" href="cimport.html#cSkipSymbol%2Cseq[T][string]">cSkipSymbol()</a> are not adequate, the <a class="reference external" href="cimport.html#cPlugin.m">cPlugin()</a> macro can be used to customize the generated Nim output. The following callbacks are available at this time.<pre class="listing"><span class="Keyword">proc</span> <span class="Identifier">onSymbol</span><span class="Punctuation">(</span><span class="Identifier">sym</span><span class="Punctuation">:</span> <span class="Keyword">var</span> <span class="Identifier">Symbol</span><span class="Punctuation">)</span> <span class="Punctuation">{</span><span class="Operator">.</span><span class="Identifier">exportc</span><span class="Punctuation">,</span> <span class="Identifier">dynlib</span><span class="Operator">.</span><span class="Punctuation">}</span></pre><p><tt class="docutils literal"><span class="pre">onSymbol()</span></tt> can be used to handle symbol name modifications required due to invalid characters in identifiers or to rename symbols that would clash due to Nim's style insensitivity. The symbol name and type is provided to the callback and the name can be modified.</p>
<p>While <tt class="docutils literal"><span class="pre">cPlugin</span></tt> can easily remove leading/trailing <tt class="docutils literal"><span class="pre">_</span></tt> or prefixes and suffixes like <tt class="docutils literal"><span class="pre">SDL_</span></tt>, passing <tt class="docutils literal"><span class="pre">--prefix</span></tt> or <tt class="docutils literal"><span class="pre">--suffix</span></tt> flags to <tt class="docutils literal"><span class="pre">cImport</span></tt> in the <tt class="docutils literal"><span class="pre">flags</span></tt> parameter is much easier. However, these flags will only be considered when no <tt class="docutils literal"><span class="pre">cPlugin</span></tt> is specified.</p>
<p>Returning a blank name will result in the symbol being skipped. This will fail for <tt class="docutils literal"><span class="pre">nskParam</span></tt> and <tt class="docutils literal"><span class="pre">nskField</span></tt> since the generated Nim code will be wrong.</p>
<p>Symbol types can be any of the following:</p>
<ul class="simple"><li><tt class="docutils literal"><span class="pre">nskConst</span></tt> for constants</li>
<li><tt class="docutils literal"><span class="pre">nskType</span></tt> for type identifiers, including primitive</li>
<li><tt class="docutils literal"><span class="pre">nskParam</span></tt> for param names</li>
<li><tt class="docutils literal"><span class="pre">nskField</span></tt> for struct field names</li>
<li><tt class="docutils literal"><span class="pre">nskEnumField</span></tt> for enum (field) names, though they are in the global namespace as <tt class="docutils literal"><span class="pre">nskConst</span></tt></li>
<li><tt class="docutils literal"><span class="pre">nskProc</span></tt> - for proc names</li>
</ul>
<p><tt class="docutils literal"><span class="pre">nimterop/plugins</span></tt> is implicitly imported to provide access to standard plugin facilities.</p>
<p><a class="reference external" href="cimport.html#cPlugin.m">cPlugin()</a> only affects calls to <a class="reference external" href="cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring">cImport()</a> that follow it.</p>
<p><strong class="examples_text">Examples:</strong></p>
<pre class="listing"><span class="Identifier">cPlugin</span><span class="Other">:</span>
<span class="Keyword">import</span>
<span class="Identifier">strutils</span>
<span class="Keyword">proc</span> <span class="Identifier">onSymbol</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">sym</span><span class="Other">:</span> <span class="Keyword">var</span> <span class="Identifier">Symbol</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">exportc</span><span class="Other">,</span> <span class="Identifier">dynlib</span></span><span class="Other">.}</span></span> <span class="Other">=</span>
<span class="Identifier">sym</span><span class="Other">.</span><span class="Identifier">name</span> <span class="Other">=</span> <span class="Identifier">sym</span><span class="Other">.</span><span class="Identifier">name</span><span class="Other">.</span><span class="Identifier">strip</span><span class="Other">(</span><span class="Identifier">chars</span> <span class="Other">=</span> <span class="Other">{</span><span class="CharLit">'_'</span><span class="Other">}</span><span class="Other">)</span>
</pre>
<p><strong class="examples_text">Examples:</strong></p>
<pre class="listing"><span class="Identifier">cPlugin</span><span class="Other">:</span>
<span class="Keyword">import</span>
<span class="Identifier">strutils</span>
<span class="Keyword">proc</span> <span class="Identifier">onSymbol</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">sym</span><span class="Other">:</span> <span class="Keyword">var</span> <span class="Identifier">Symbol</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">exportc</span><span class="Other">,</span> <span class="Identifier">dynlib</span></span><span class="Other">.}</span></span> <span class="Other">=</span>
<span class="Keyword">if</span> <span class="Identifier">sym</span><span class="Other">.</span><span class="Identifier">kind</span> <span class="Operator">==</span> <span class="Identifier">nskProc</span> <span class="Keyword">and</span> <span class="Identifier">sym</span><span class="Other">.</span><span class="Identifier">name</span><span class="Other">.</span><span class="Identifier">contains</span><span class="Other">(</span><span class="StringLit">&quot;SDL_&quot;</span><span class="Other">)</span><span class="Other">:</span>
<span class="Identifier">sym</span><span class="Other">.</span><span class="Identifier">name</span> <span class="Other">=</span> <span class="Identifier">sym</span><span class="Other">.</span><span class="Identifier">name</span><span class="Other">.</span><span class="Identifier">replace</span><span class="Other">(</span><span class="StringLit">&quot;SDL_&quot;</span><span class="Other">,</span> <span class="StringLit">&quot;&quot;</span><span class="Other">)</span>
</pre>
</dd>
<a id="cDefine.m,,string"></a>
<dt><pre><span class="Keyword">macro</span> <a href="#cDefine.m%2C%2Cstring"><span class="Identifier">cDefine</span></a><span class="Other">(</span><span class="Identifier">name</span><span class="Other">:</span> <span class="Identifier">static</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">val</span><span class="Other">:</span> <span class="Identifier">static</span> <span class="Identifier">string</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
<tt class="docutils literal"><span class="pre">#define</span></tt> an identifer that is forwarded to the C/C++ preprocessor if called within <a class="reference external" href="cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring">cImport()</a> or <a class="reference external" href="cimport.html#c2nImport.m%2C%2Cstring%2Cstring%2Cstring">c2nImport()</a> as well as to the C/C++ compiler during Nim compilation using <tt class="docutils literal"><span class="pre">{.passC: &quot;-DXXX&quot;.}</span></tt>
</dd>
<a id="cIncludeDir.m"></a>
<dt><pre><span class="Keyword">macro</span> <a href="#cIncludeDir.m"><span class="Identifier">cIncludeDir</span></a><span class="Other">(</span><span class="Identifier">dir</span><span class="Other">:</span> <span class="Identifier">static</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Add an include directory that is forwarded to the C/C++ preprocessor if called within <a class="reference external" href="cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring">cImport()</a> or <a class="reference external" href="cimport.html#c2nImport.m%2C%2Cstring%2Cstring%2Cstring">c2nImport()</a> as well as to the C/C++ compiler during Nim compilation using <tt class="docutils literal"><span class="pre">{.passC: &quot;-IXXX&quot;.}</span></tt>.
</dd>
<a id="cCompile.m,,string,string"></a>
<dt><pre><span class="Keyword">macro</span> <a href="#cCompile.m%2C%2Cstring%2Cstring"><span class="Identifier">cCompile</span></a><span class="Other">(</span><span class="Identifier">path</span><span class="Other">:</span> <span class="Identifier">static</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">mode</span> <span class="Other">=</span> <span class="StringLit">&quot;c&quot;</span><span class="Other">;</span> <span class="Identifier">exclude</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
<p>Compile and link C/C++ implementation into resulting binary using <tt class="docutils literal"><span class="pre">{.compile.}</span></tt></p>
<p><tt class="docutils literal"><span class="pre">path</span></tt> can be a specific file or contain wildcards:</p>
<pre class="listing"><span class="Identifier">cCompile</span><span class="Punctuation">(</span><span class="StringLit">&quot;file.c&quot;</span><span class="Punctuation">)</span>
<span class="Identifier">cCompile</span><span class="Punctuation">(</span><span class="StringLit">&quot;path/to/*.c&quot;</span><span class="Punctuation">)</span></pre><p><tt class="docutils literal"><span class="pre">mode</span></tt> recursively searches for code files in <tt class="docutils literal"><span class="pre">path</span></tt>.</p>
<p><tt class="docutils literal"><span class="pre">c</span></tt> searches for <tt class="docutils literal"><span class="pre">*.c</span></tt> whereas <tt class="docutils literal"><span class="pre">cpp</span></tt> searches for <tt class="docutils literal"><span class="pre">*.C *.cpp *.c++ *.cc *.cxx</span></tt></p>
<pre class="listing"><span class="Identifier">cCompile</span><span class="Punctuation">(</span><span class="StringLit">&quot;path/to/dir&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;cpp&quot;</span><span class="Punctuation">)</span></pre><p><tt class="docutils literal"><span class="pre">exclude</span></tt> can be used to exclude files by partial string match. Comma separated to specify multiple exclude strings</p>
<pre class="listing"><span class="Identifier">cCompile</span><span class="Punctuation">(</span><span class="StringLit">&quot;path/to/dir&quot;</span><span class="Punctuation">,</span> <span class="Identifier">exclude</span><span class="Operator">=</span><span class="StringLit">&quot;test2.c&quot;</span><span class="Punctuation">)</span></pre>
</dd>
<a id="cImport.m,,string,string,string"></a>
<dt><pre><span class="Keyword">macro</span> <a href="#cImport.m%2C%2Cstring%2Cstring%2Cstring"><span class="Identifier">cImport</span></a><span class="Other">(</span><span class="Identifier">filename</span><span class="Other">:</span> <span class="Identifier">static</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">recurse</span><span class="Other">:</span> <span class="Identifier">static</span> <span class="Identifier">bool</span> <span class="Other">=</span> <span class="Identifier">false</span><span class="Other">;</span>
<span class="Identifier">dynlib</span><span class="Other">:</span> <span class="Identifier">static</span> <span class="Identifier">string</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">;</span> <span class="Identifier">mode</span><span class="Other">:</span> <span class="Identifier">static</span> <span class="Identifier">string</span> <span class="Other">=</span> <span class="StringLit">&quot;c&quot;</span><span class="Other">;</span>
<span class="Identifier">flags</span><span class="Other">:</span> <span class="Identifier">static</span> <span class="Identifier">string</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
<p>Import all supported definitions from specified header file. Generated content is cached in <tt class="docutils literal"><span class="pre">nimcache</span></tt> until <tt class="docutils literal"><span class="pre">filename</span></tt> changes unless <a class="reference external" href="cimport.html#cDisableCaching">cDisableCaching()</a> is set. <tt class="docutils literal"><span class="pre">nim -f</span></tt> can also be used after Nim v0.19.4 to flush the cache.</p>
<p><tt class="docutils literal"><span class="pre">recurse</span></tt> can be used to generate Nim wrappers from <tt class="docutils literal"><span class="pre">#include</span></tt> files referenced in <tt class="docutils literal"><span class="pre">filename</span></tt>. This is only done for files in the same directory as <tt class="docutils literal"><span class="pre">filename</span></tt> or in a directory added using <a class="reference external" href="cimport.html#cIncludeDir.m">cIncludeDir()</a></p>
<p><tt class="docutils literal"><span class="pre">dynlib</span></tt> can be used to specify the Nim string to use to specify the dynamic library to load the imported symbols from. For example:</p>
<pre class="listing"><span class="Keyword">const</span>
<span class="Identifier">dynpcre</span> <span class="Operator">=</span>
<span class="Keyword">when</span> <span class="Identifier">defined</span><span class="Punctuation">(</span><span class="Identifier">windows</span><span class="Punctuation">)</span><span class="Punctuation">:</span>
<span class="Keyword">when</span> <span class="Identifier">defined</span><span class="Punctuation">(</span><span class="Identifier">cpu64</span><span class="Punctuation">)</span><span class="Punctuation">:</span>
<span class="StringLit">&quot;pcre64.dll&quot;</span>
<span class="Keyword">else</span><span class="Punctuation">:</span>
<span class="StringLit">&quot;pcre32.dll&quot;</span>
<span class="Keyword">elif</span> <span class="Identifier">hostOS</span> <span class="Operator">==</span> <span class="StringLit">&quot;macosx&quot;</span><span class="Punctuation">:</span>
<span class="StringLit">&quot;libpcre(.3|.1|).dylib&quot;</span>
<span class="Keyword">else</span><span class="Punctuation">:</span>
<span class="StringLit">&quot;libpcre.so(.3|.1|)&quot;</span>
<span class="Identifier">cImport</span><span class="Punctuation">(</span><span class="StringLit">&quot;pcre.h&quot;</span><span class="Punctuation">,</span> <span class="Identifier">dynlib</span><span class="Operator">=</span><span class="StringLit">&quot;dynpcre&quot;</span><span class="Punctuation">)</span></pre><p>If <tt class="docutils literal"><span class="pre">dynlib</span></tt> is not specified, the C/C++ implementation files can be compiled in with <a class="reference external" href="cimport.html#cCompile.m%2C%2Cstring%2Cstring">cCompile()</a>, or the <tt class="docutils literal"><span class="pre">{.passL.}</span></tt> pragma can be used to specify the static lib to link.</p>
<p><tt class="docutils literal"><span class="pre">mode</span></tt> is purely for forward compatibility when toast adds C++ support. It can be ignored for the foreseeable future.</p>
<p><tt class="docutils literal"><span class="pre">flags</span></tt> can be used to pass any other command line arguments to <tt class="docutils literal"><span class="pre">toast</span></tt>. A good example would be <tt class="docutils literal"><span class="pre">--prefix</span></tt> and <tt class="docutils literal"><span class="pre">--suffix</span></tt> which strip leading and trailing strings from identifiers, <tt class="docutils literal"><span class="pre">_</span></tt> being quite common.</p>
<p><tt class="docutils literal"><span class="pre">cImport()</span></tt> consumes and resets preceding <tt class="docutils literal"><span class="pre">cOverride()</span></tt> calls. <tt class="docutils literal"><span class="pre">cPlugin()</span></tt> is retained for the next <tt class="docutils literal"><span class="pre">cImport()</span></tt> call unless a new <tt class="docutils literal"><span class="pre">cPlugin()</span></tt> call is defined.</p>
</dd>
<a id="c2nImport.m,,string,string,string"></a>
<dt><pre><span class="Keyword">macro</span> <a href="#c2nImport.m%2C%2Cstring%2Cstring%2Cstring"><span class="Identifier">c2nImport</span></a><span class="Other">(</span><span class="Identifier">filename</span><span class="Other">:</span> <span class="Identifier">static</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">recurse</span><span class="Other">:</span> <span class="Identifier">static</span> <span class="Identifier">bool</span> <span class="Other">=</span> <span class="Identifier">false</span><span class="Other">;</span>
<span class="Identifier">dynlib</span><span class="Other">:</span> <span class="Identifier">static</span> <span class="Identifier">string</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">;</span> <span class="Identifier">mode</span><span class="Other">:</span> <span class="Identifier">static</span> <span class="Identifier">string</span> <span class="Other">=</span> <span class="StringLit">&quot;c&quot;</span><span class="Other">;</span>
<span class="Identifier">flags</span><span class="Other">:</span> <span class="Identifier">static</span> <span class="Identifier">string</span> <span class="Other">=</span> <span class="StringLit">&quot;&quot;</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
<p>Import all supported definitions from specified header file using <tt class="docutils literal"><span class="pre">c2nim</span></tt></p>
<p>Similar to <a class="reference external" href="cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring">cImport()</a> but uses <tt class="docutils literal"><span class="pre">c2nim</span></tt> to generate the Nim wrapper instead of <tt class="docutils literal"><span class="pre">toast</span></tt>. Note that neither <a class="reference external" href="cimport.html#cOverride.m">cOverride()</a>, <a class="reference external" href="cimport.html#cSkipSymbol%2Cseq[T][string]">cSkipSymbol()</a> nor <a class="reference external" href="cimport.html#cPlugin.m">cPlugin()</a> have any impact on <tt class="docutils literal"><span class="pre">c2nim</span></tt>.</p>
<p><tt class="docutils literal"><span class="pre">toast</span></tt> is only used to preprocess the header file and recurse if specified.</p>
<p><tt class="docutils literal"><span class="pre">mode</span></tt> should be set to <tt class="docutils literal"><span class="pre">cpp</span></tt> for c2nim to wrap C++ headers.</p>
<p><tt class="docutils literal"><span class="pre">flags</span></tt> can be used to pass other command line arguments to <tt class="docutils literal"><span class="pre">c2nim</span></tt>.</p>
<p><tt class="docutils literal"><span class="pre">nimterop</span></tt> does not depend on <tt class="docutils literal"><span class="pre">c2nim</span></tt> as a <tt class="docutils literal"><span class="pre">nimble</span></tt> dependency so it does not get installed automatically. Any wrapper or library that requires this proc needs to install <tt class="docutils literal"><span class="pre">c2nim</span></tt> with <tt class="docutils literal"><span class="pre">nimble install c2nim</span></tt> or add it as a dependency in its own <tt class="docutils literal"><span class="pre">.nimble</span></tt> file.</p>
</dd>
</dl></div>
<div class="section" id="19">
<h1><a class="toc-backref" href="#19">Exports</a></h1>
<dl class="item">
<a href="types.html#va_list"><span class="Identifier">va_list</span></a>, <a href="types.html#defineEnum"><span class="Identifier">defineEnum</span></a>, <a href="types.html#wchar_t"><span class="Identifier">wchar_t</span></a>, <a href="types.html#time_t"><span class="Identifier">time_t</span></a>, <a href="types.html#time64_t"><span class="Identifier">time64_t</span></a>, <a class="reference external" href="types.html">types</a>, <a href="types.html#enumOp"><span class="Identifier">enumOp</span></a>, <a href="types.html#ptrdiff_t"><span class="Identifier">ptrdiff_t</span></a>
</dl></div>
</div>
</div>
<div class="row">
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small style="color: var(--hint);">Made with Nim. Generated: 2020-03-24 20:55:42 UTC</small>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -1,15 +0,0 @@
ast2 cimport.html#ast2 Feature.ast2
Feature cimport.html#Feature cimport: Feature
cOverride cimport.html#cOverride.m cimport: cOverride(body): untyped
cSkipSymbol cimport.html#cSkipSymbol,seq[T][string] cimport: cSkipSymbol(skips: seq[string])
cPlugin cimport.html#cPlugin.m cimport: cPlugin(body): untyped
cSearchPath cimport.html#cSearchPath,string cimport: cSearchPath(path: string): string
cDebug cimport.html#cDebug cimport: cDebug()
cDisableCaching cimport.html#cDisableCaching cimport: cDisableCaching()
cDefine cimport.html#cDefine.m,,string cimport: cDefine(name: static string; val: static string = &quot;&quot;): untyped
cAddSearchDir cimport.html#cAddSearchDir,string cimport: cAddSearchDir(dir: string)
cIncludeDir cimport.html#cIncludeDir.m cimport: cIncludeDir(dir: static string): untyped
cAddStdDir cimport.html#cAddStdDir,string cimport: cAddStdDir(mode = &quot;c&quot;)
cCompile cimport.html#cCompile.m,,string,string cimport: cCompile(path: static string; mode = &quot;c&quot;; exclude = &quot;&quot;): untyped
cImport cimport.html#cImport.m,,string,string,string cimport: cImport(filename: static string; recurse: static bool = false;\n dynlib: static string = &quot;&quot;; mode: static string = &quot;c&quot;; flags: static string = &quot;&quot;): untyped
c2nImport cimport.html#c2nImport.m,,string,string,string cimport: c2nImport(filename: static string; recurse: static bool = false;\n dynlib: static string = &quot;&quot;; mode: static string = &quot;c&quot;; flags: static string = &quot;&quot;): untyped

View file

@ -1,870 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
<!-- Google fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
<!-- CSS -->
<title>compat</title>
<style type="text/css" >
/*
Stylesheet for use with Docutils/rst2html.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
Modified from Chad Skeeters' rst2html-style
https://bitbucket.org/cskeeters/rst2html-style/
Modified by Boyd Greenfield and narimiran
*/
html {
font-size: 100%;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%; }
body {
font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
font-weight: 400;
font-size: 1.125em;
line-height: 1.5;
color: #222;
background-color: #FCFCFC; }
/* Skeleton grid */
.container {
position: relative;
width: 100%;
max-width: 1050px;
margin: 0 auto;
padding: 0;
box-sizing: border-box; }
.column,
.columns {
width: 100%;
float: left;
box-sizing: border-box;
margin-left: 1%;
}
.column:first-child,
.columns:first-child {
margin-left: 0; }
.three.columns {
width: 19%; }
.nine.columns {
width: 80.0%; }
.twelve.columns {
width: 100%;
margin-left: 0; }
@media screen and (max-width: 860px) {
.three.columns {
display: none;
}
.nine.columns {
width: 98.0%;
}
body {
font-size: 1em;
line-height: 1.35;
}
}
cite {
font-style: italic !important; }
/* Nim search input */
div#searchInputDiv {
margin-bottom: 1em;
}
input#searchInput {
width: 80%;
}
/*
* Some custom formatting for input forms.
* This also fixes input form colors on Firefox with a dark system theme on Linux.
*/
input {
-moz-appearance: none;
color: #333;
background-color: #f8f8f8;
border: 1px solid #aaa;
font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
font-size: 0.9em;
padding: 6px;
}
input:focus {
border: 1px solid #1fa0eb;
box-shadow: 0 0 2px #1fa0eb;
}
/* Docgen styles */
/* Links */
a {
color: #07b;
text-decoration: none;
}
a span.Identifier {
text-decoration: underline;
text-decoration-color: #aab;
}
a.reference-toplevel {
font-weight: bold;
}
a.toc-backref {
text-decoration: none;
color: #222; }
a.link-seesrc {
color: #607c9f;
font-size: 0.9em;
font-style: italic; }
a:hover,
a:focus {
color: #607c9f;
text-decoration: underline; }
a:hover span.Identifier {
color: #607c9f;
}
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline; }
sup {
top: -0.5em; }
sub {
bottom: -0.25em; }
img {
width: auto;
height: auto;
max-width: 100%;
vertical-align: middle;
border: 0;
-ms-interpolation-mode: bicubic; }
@media print {
* {
color: black !important;
text-shadow: none !important;
background: transparent !important;
box-shadow: none !important; }
a,
a:visited {
text-decoration: underline; }
a[href]:after {
content: " (" attr(href) ")"; }
abbr[title]:after {
content: " (" attr(title) ")"; }
.ir a:after,
a[href^="javascript:"]:after,
a[href^="#"]:after {
content: ""; }
pre,
blockquote {
border: 1px solid #999;
page-break-inside: avoid; }
thead {
display: table-header-group; }
tr,
img {
page-break-inside: avoid; }
img {
max-width: 100% !important; }
@page {
margin: 0.5cm; }
h1 {
page-break-before: always; }
h1.title {
page-break-before: avoid; }
p,
h2,
h3 {
orphans: 3;
widows: 3; }
h2,
h3 {
page-break-after: avoid; }
}
p {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
small {
font-size: 85%; }
strong {
font-weight: 600;
font-size: 0.95em;
color: #3c3c3c;
}
em {
font-style: italic; }
h1 {
font-size: 1.8em;
font-weight: 400;
padding-bottom: .25em;
border-bottom: 1px solid #aaa;
margin-top: 2.5em;
margin-bottom: 1em;
line-height: 1.2em; }
h1.title {
padding-bottom: 1em;
border-bottom: 0px;
font-size: 2.5em;
text-align: center;
font-weight: 900;
margin-top: 0.75em;
margin-bottom: 0em;
}
h2 {
font-size: 1.3em;
margin-top: 2em; }
h2.subtitle {
text-align: center; }
h3 {
font-size: 1.125em;
font-style: italic;
margin-top: 1.5em; }
h4 {
font-size: 1.125em;
margin-top: 1em; }
h5 {
font-size: 1.125em;
margin-top: 0.75em; }
h6 {
font-size: 1.1em; }
ul,
ol {
padding: 0;
margin-top: 0.5em;
margin-left: 0.75em; }
ul ul,
ul ol,
ol ol,
ol ul {
margin-bottom: 0;
margin-left: 1.25em; }
li {
list-style-type: circle;
}
ul.simple-boot li {
list-style-type: none;
margin-left: 0em;
margin-bottom: 0.5em;
}
ol.simple > li, ul.simple > li {
margin-bottom: 0.25em;
margin-left: 0.4em }
ul.simple.simple-toc > li {
margin-top: 1em;
}
ul.simple-toc {
list-style: none;
font-size: 0.9em;
margin-left: -0.3em;
margin-top: 1em; }
ul.simple-toc > li {
list-style-type: none;
}
ul.simple-toc-section {
list-style-type: circle;
margin-left: 1em;
color: #6c9aae; }
ol.arabic {
list-style: decimal; }
ol.loweralpha {
list-style: lower-alpha; }
ol.upperalpha {
list-style: upper-alpha; }
ol.lowerroman {
list-style: lower-roman; }
ol.upperroman {
list-style: upper-roman; }
ul.auto-toc {
list-style-type: none; }
dl {
margin-bottom: 1.5em; }
dt {
margin-bottom: -0.5em;
margin-left: 0.0em; }
dd {
margin-left: 2.0em;
margin-bottom: 3.0em;
margin-top: 0.5em; }
hr {
margin: 2em 0;
border: 0;
border-top: 1px solid #aaa; }
blockquote {
font-size: 0.9em;
font-style: italic;
padding-left: 0.5em;
margin-left: 0;
border-left: 5px solid #bbc;
}
.pre {
font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace;
font-weight: 500;
font-size: 0.85em;
background-color: #f0f3ff;
padding-left: 3px;
padding-right: 3px;
border-radius: 4px;
}
pre {
font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace;
color: #222;
font-weight: 500;
display: inline-block;
box-sizing: border-box;
min-width: 100%;
padding: 0.5em;
margin-top: 0.5em;
margin-bottom: 0.5em;
font-size: 0.85em;
white-space: pre !important;
overflow-y: hidden;
overflow-x: visible;
background-color: ghostwhite;
border: 1px solid #dde;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px; }
.pre-scrollable {
max-height: 340px;
overflow-y: scroll; }
/* Nim line-numbered tables */
.line-nums-table {
width: 100%;
table-layout: fixed; }
table.line-nums-table {
border-radius: 4px;
border: 1px solid #cccccc;
background-color: ghostwhite;
border-collapse: separate;
margin-top: 15px;
margin-bottom: 25px; }
.line-nums-table tbody {
border: none; }
.line-nums-table td pre {
border: none;
background-color: transparent; }
.line-nums-table td.blob-line-nums {
width: 28px; }
.line-nums-table td.blob-line-nums pre {
color: #b0b0b0;
-webkit-filter: opacity(75%);
text-align: right;
border-color: transparent;
background-color: transparent;
padding-left: 0px;
margin-left: 0px;
padding-right: 0px;
margin-right: 0px; }
table {
max-width: 100%;
background-color: transparent;
margin-top: 0.5em;
margin-bottom: 1.5em;
border-collapse: collapse;
border-color: #ccc;
border-spacing: 0;
font-size: 0.9em;
}
table th, table td {
padding: 0px 0.5em 0px;
}
table th {
background-color: #e8e8e8;
font-weight: bold; }
table th.docinfo-name {
background-color: transparent;
}
table tr:hover {
background-color: ghostwhite; }
/* rst2html default used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0; }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 !important; }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 !important; }
.last, .with-subtitle {
margin-bottom: 0 !important; }
.hidden {
display: none; }
blockquote.epigraph {
margin: 2em 5em; }
dl.docutils dd {
margin-bottom: 0.5em; }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden; }
div.figure {
margin-left: 2em;
margin-right: 2em; }
div.footer, div.header {
clear: both;
text-align: center;
color: #666;
font-size: smaller; }
div.footer {
padding-top: 5em;
}
div.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em; }
div.line-block div.line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em; }
div.topic {
margin: 2em; }
div.search_results {
background-color: antiquewhite;
margin: 3em;
padding: 1em;
border: 1px solid #4d4d4d;
}
div#global-links ul {
margin-left: 0;
list-style-type: none;
}
div#global-links > simple-boot {
margin-left: 3em;
}
hr.docutils {
width: 75%; }
img.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em; }
img.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em; }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto; }
.align-left {
text-align: left; }
.align-center {
clear: both;
text-align: center; }
.align-right {
text-align: right; }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit; }
p.attribution {
text-align: right;
margin-left: 50%; }
p.caption {
font-style: italic; }
p.credits {
font-style: italic;
font-size: smaller; }
p.label {
white-space: nowrap; }
p.rubric {
font-weight: bold;
font-size: larger;
color: maroon;
text-align: center; }
p.topic-title {
font-weight: bold; }
pre.address {
margin-bottom: 0;
margin-top: 0;
font: inherit; }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em;
margin-right: 2em; }
pre.code .ln {
color: grey; }
/* line numbers */
pre.code, code {
background-color: #eeeeee; }
pre.code .comment, code .comment {
color: #5c6576; }
pre.code .keyword, code .keyword {
color: #3B0D06;
font-weight: bold; }
pre.code .literal.string, code .literal.string {
color: #0c5404; }
pre.code .name.builtin, code .name.builtin {
color: #352b84; }
pre.code .deleted, code .deleted {
background-color: #DEB0A1; }
pre.code .inserted, code .inserted {
background-color: #A3D289; }
span.classifier {
font-style: oblique; }
span.classifier-delimiter {
font-weight: bold; }
span.option {
white-space: nowrap; }
span.problematic {
color: #b30000; }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80%; }
span.DecNumber {
color: #252dbe; }
span.BinNumber {
color: #252dbe; }
span.HexNumber {
color: #252dbe; }
span.OctNumber {
color: #252dbe; }
span.FloatNumber {
color: #252dbe; }
span.Identifier {
color: #222; }
span.Keyword {
font-weight: 600;
color: #5e8f60; }
span.StringLit {
color: #a4255b; }
span.LongStringLit {
color: #a4255b; }
span.CharLit {
color: #a4255b; }
span.EscapeSequence {
color: black; }
span.Operator {
color: black; }
span.Punctuation {
color: black; }
span.Comment, span.LongComment {
font-style: italic;
font-weight: 400;
color: #484a86; }
span.RegularExpression {
color: darkviolet; }
span.TagStart {
color: darkviolet; }
span.TagEnd {
color: darkviolet; }
span.Key {
color: #252dbe; }
span.Value {
color: #252dbe; }
span.RawData {
color: #a4255b; }
span.Assembler {
color: #252dbe; }
span.Preprocessor {
color: #252dbe; }
span.Directive {
color: #252dbe; }
span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference,
span.Other {
color: black; }
/* Pop type, const, proc, and iterator defs in nim def blocks */
dt pre > span.Identifier, dt pre > span.Operator {
color: #155da4;
font-weight: 700; }
dt pre > span.Keyword ~ span.Identifier, dt pre > span.Identifier ~ span.Identifier,
dt pre > span.Operator ~ span.Identifier, dt pre > span.Other ~ span.Identifier {
color: inherit;
font-weight: inherit; }
/* Nim sprite for the footer (taken from main page favicon) */
.nim-sprite {
display: inline-block;
height: 16px;
width: 16px;
background-position: 0 0;
background-size: 16px 16px;
-webkit-filter: opacity(50%);
background-repeat: no-repeat;
background-image: url("data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA==");
margin-bottom: -5px; }
span.pragmadots {
/* Position: relative frees us up to make the dots
look really nice without fucking up the layout and
causing bulging in the parent container */
position: relative;
/* 1px down looks slightly nicer */
top: 1px;
padding: 2px;
background-color: #e8e8e8;
border-radius: 4px;
margin: 0 2px;
cursor: pointer;
font-size: 0.8em;
}
span.pragmadots:hover {
background-color: #DBDBDB;
}
span.pragmawrap {
display: none;
}
span.attachedType {
display: none;
visibility: hidden;
}
</style>
<script type="text/javascript" src="dochack.js"></script>
<script type="text/javascript">
function main() {
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
}
</script>
</head>
<body onload="main()">
<div class="document" id="documentId">
<div class="container">
<h1 class="title">compat</h1>
<div class="row">
<div class="three columns">
<div id="global-links">
<ul class="simple">
</ul>
</div>
<div id="searchInputDiv">
Search: <input type="text" id="searchInput"
onkeyup="search()" />
</div>
<div>
Group by:
<select onchange="groupBy(this.value)">
<option value="section">Section</option>
<option value="type">Type</option>
</select>
</div>
<ul class="simple simple-toc" id="toc-list">
<li>
<a class="reference reference-toplevel" href="#12" id="62">Procs</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#myNormalizedPath%2Cstring"
title="myNormalizedPath(path: string): string"><wbr />my<wbr />Normalized<wbr />Path<span class="attachedType"></span></a></li>
</ul>
</li>
</ul>
</div>
<div class="nine columns" id="content">
<div id="tocRoot"></div>
<p class="module-desc"></p>
<div class="section" id="12">
<h1><a class="toc-backref" href="#12">Procs</a></h1>
<dl class="item">
<a id="myNormalizedPath,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#myNormalizedPath%2Cstring"><span class="Identifier">myNormalizedPath</span></a><span class="Other">(</span><span class="Identifier">path</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
</dd>
</dl></div>
</div>
</div>
<div class="row">
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small>Made with Nim. Generated: 2020-03-10 02:10:43 UTC</small>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -1 +0,0 @@
myNormalizedPath compat.html#myNormalizedPath,string compat: myNormalizedPath(path: string): string

2011
dochack.js

File diff suppressed because it is too large Load diff

152
docs.html
View file

@ -1,152 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
<!-- Google fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
<!-- CSS -->
<title>docs</title>
<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
<script type="text/javascript" src="dochack.js"></script>
<script type="text/javascript">
function main() {
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
toggleSwitch.addEventListener('change', switchTheme, false);
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-theme', "dark");
toggleSwitch.checked = true;
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
document.documentElement.setAttribute('data-theme', "light");
toggleSwitch.checked = false;
} else {
const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null;
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
if (currentTheme === 'dark') {
toggleSwitch.checked = true;
}
}
}
}
</script>
</head>
<body onload="main()">
<div class="document" id="documentId">
<div class="container">
<h1 class="title">docs</h1>
<div class="row">
<div class="three columns">
<div class="theme-switch-wrapper">
<label class="theme-switch" for="checkbox">
<input type="checkbox" id="checkbox" />
<div class="slider round"></div>
</label>
&nbsp;&nbsp;&nbsp; <em>Dark Mode</em>
</div>
<div id="global-links">
<ul class="simple">
</ul>
</div>
<div id="searchInputDiv">
Search: <input type="text" id="searchInput"
onkeyup="search()" />
</div>
<div>
Group by:
<select onchange="groupBy(this.value)">
<option value="section">Section</option>
<option value="type">Type</option>
</select>
</div>
<ul class="simple simple-toc" id="toc-list">
<li>
<a class="reference reference-toplevel" href="#12" id="62">Procs</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#buildDocs%2CopenArray%5Bstring%5D%2Cstring%2CopenArray%5Bstring%5D"
title="buildDocs(files: openArray[string]; path: string; baseDir = getProjectPath() &amp; &quot;/&quot;;
defines: openArray[string] = @[])"><wbr />build<wbr />Docs<span class="attachedType"></span></a></li>
</ul>
</li>
</ul>
</div>
<div class="nine columns" id="content">
<div id="tocRoot"></div>
<p class="module-desc"></p>
<div class="section" id="12">
<h1><a class="toc-backref" href="#12">Procs</a></h1>
<dl class="item">
<a id="buildDocs,openArray[string],string,openArray[string]"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#buildDocs%2CopenArray%5Bstring%5D%2Cstring%2CopenArray%5Bstring%5D"><span class="Identifier">buildDocs</span></a><span class="Other">(</span><span class="Identifier">files</span><span class="Other">:</span> <span class="Identifier">openArray</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">;</span> <span class="Identifier">path</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">;</span>
<span class="Identifier">baseDir</span> <span class="Other">=</span> <span class="Identifier">getProjectPath</span><span class="Other">(</span><span class="Other">)</span> <span class="Operator">&amp;</span> <span class="StringLit">&quot;/&quot;</span><span class="Other">;</span> <span class="Identifier">defines</span><span class="Other">:</span> <span class="Identifier">openArray</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span> <span class="Other">=</span> <span class="Operator">@</span><span class="Other">[</span><span class="Other">]</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma">
<span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadIOEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
<p>Generate docs for all specified nim <tt class="docutils literal"><span class="pre">files</span></tt> to the specified <tt class="docutils literal"><span class="pre">path</span></tt></p>
<p><tt class="docutils literal"><span class="pre">baseDir</span></tt> is the project path by default and <tt class="docutils literal"><span class="pre">files</span></tt> and <tt class="docutils literal"><span class="pre">path</span></tt> are relative to that directory. Set to &quot;&quot; if using absolute paths.</p>
<p><tt class="docutils literal"><span class="pre">defines</span></tt> is a list of <tt class="docutils literal"><span class="pre">-d:xxx</span></tt> define flags (the <tt class="docutils literal"><span class="pre">xxx</span></tt> part) that should be passed to <tt class="docutils literal"><span class="pre">nim doc</span></tt> so that <tt class="docutils literal"><span class="pre">getHeader()</span></tt> is invoked correctly.</p>
<p>Use the <tt class="docutils literal"><span class="pre">--publish</span></tt> flag with nimble to publish docs contained in <tt class="docutils literal"><span class="pre">path</span></tt> to Github in the <tt class="docutils literal"><span class="pre">gh-pages</span></tt> branch. This requires the ghp-import package for Python: <tt class="docutils literal"><span class="pre">pip install ghp-import</span></tt></p>
<p>WARNING: <tt class="docutils literal"><span class="pre">--publish</span></tt> will destroy any existing content in this branch.</p>
<p>NOTE: <tt class="docutils literal"><span class="pre">buildDocs()</span></tt> only works correctly on Windows with Nim 1.0+ since <a class="reference external" href="https://github.com/nim-lang/Nim/pull/11814">https://github.com/nim-lang/Nim/pull/11814</a> is required.</p>
</dd>
</dl></div>
</div>
</div>
<div class="row">
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small style="color: var(--hint);">Made with Nim. Generated: 2020-03-24 20:55:37 UTC</small>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -1 +0,0 @@
buildDocs docs.html#buildDocs,openArray[string],string,openArray[string] docs: buildDocs(files: openArray[string]; path: string; baseDir = getProjectPath() &amp; &quot;/&quot;;\n defines: openArray[string] = @[])

File diff suppressed because one or more lines are too long

109
nimterop.nimble Normal file
View file

@ -0,0 +1,109 @@
# Package
version = "0.6.13"
author = "genotrance"
description = "C/C++ interop for Nim"
license = "MIT"
bin = @["nimterop/toast", "nimterop/loaf"]
installDirs = @["nimterop"]
# Dependencies
requires "nim >= 0.20.2", "regex >= 0.15.0", "cligen >= 1.5.3"
import nimterop/docs
import os
proc execCmd(cmd: string) =
exec "tests/timeit " & cmd
proc execTest(test: string, flags = "", runDocs = true) =
execCmd "nim c --hints:off -f -d:checkAbi " & flags & " -r " & test
let
# -d:checkAbi broken in cpp mode until post 1.2.0
cppAbi = when (NimMajor, NimMinor) >= (1, 3): "-d:checkAbi " else: ""
execCmd "nim cpp --hints:off " & cppAbi & flags & " -r " & test
if runDocs:
let docPath = "build/html_" & test.extractFileName.changeFileExt("") & "_docs"
rmDir docPath
mkDir docPath
buildDocs(@[test], docPath, nimArgs = "--hints:off " & flags)
task buildTimeit, "build timer":
exec "nim c --hints:off -d:danger tests/timeit"
task buildLoaf, "build loaf":
execCmd("nim c --hints:off -d:danger nimterop/loaf.nim")
task buildToast, "build toast":
execCmd("nim c --hints:off -d:danger nimterop/toast.nim")
task bt, "build toast":
buildToastTask()
task btd, "build toast":
execCmd("nim c -g nimterop/toast.nim")
task docs, "Generate docs":
buildDocs(@["nimterop/all.nim"], "build/htmldocs")
task minitest, "Test for Nim CI":
exec "nim c -f -d:danger nimterop/loaf.nim"
exec "nim c -f -d:danger nimterop/toast"
exec "nim c -f -d:checkAbi -r tests/tast2.nim"
exec "nim c -f -d:checkAbi -d:zlibJBB -d:zlibSetVer=1.2.11 -r tests/zlib.nim"
task basic, "Basic tests":
execTest "tests/tast2.nim"
execTest "tests/tast2.nim", "-d:NOHEADER"
execTest "tests/tast2.nim", "-d:NOHEADER -d:WRAPPED"
execTest "tests/tnimterop_c.nim"
execTest "tests/tnimterop_c.nim", "-d:FLAGS=\"-H\""
execCmd "nim cpp --hints:off -f -r tests/tnimterop_cpp.nim"
execCmd "./nimterop/toast tests/toast.cfg tests/include/toast.h"
task wrapper, "Wrapper tests":
execTest "tests/tpcre.nim"
when defined(Linux):
execTest "tests/rsa.nim"
execTest "tests/rsa.nim", "-d:FLAGS=\"-H\""
# Platform specific tests
when defined(Windows):
execTest "tests/tmath.nim"
execTest "tests/tmath.nim", "-d:FLAGS=\"-H\""
if defined(OSX) or defined(Windows) or not existsEnv("TRAVIS"):
execTest "tests/tsoloud.nim"
execTest "tests/tsoloud.nim", "-d:FLAGS=\"-H\""
task getheader, "getHeader tests":
withDir("tests"):
exec "nim e getheader.nims"
task package, "Wrapper package tests":
if not existsEnv("APPVEYOR"):
withDir("tests"):
exec "nim e wrappers.nims"
task test, "Test":
rmFile("tests/timeit.txt")
buildTimeitTask()
buildLoafTask()
buildToastTask()
basicTask()
wrapperTask()
getheaderTask()
packageTask()
docsTask()
echo readFile("tests/timeit.txt")

5
nimterop/all.nim Normal file
View file

@ -0,0 +1,5 @@
##[
The following modules are available to users of Nimterop.
]##
import "."/[build, cimport, docs, plugin]

39
nimterop/build.nim Normal file
View file

@ -0,0 +1,39 @@
when not defined(TOAST):
import os except findExe, sleep
else:
import os
export extractFilename, `/`
# Misc helpers
import "."/build/misc
export misc
# Nim cfg file related functionality
import "."/build/nimconf
export nimconf
# Functionality shelled out to external executables
import "."/build/shell
export shell
# C compiler support
import "."/build/ccompiler
export ccompiler
when not defined(TOAST):
# configure, cmake, make support
import "."/build/tools
export tools
# Conan.io support
import "."/build/conan
export conan
# Julia BinaryBuilder.org support
import "."/build/jbb
export jbb
# getHeader support
import "."/build/getheader
export getheader

View file

@ -0,0 +1,105 @@
import os, strformat, strutils
import "."/shell
proc getCompilerMode*(path: string): string =
## Determines a target language mode from an input filename, if one is not already specified.
let file = path.splitFile()
if file.ext in [".hxx", ".hpp", ".hh", ".H", ".h++", ".cpp", ".cxx", ".cc", ".C", ".c++"]:
result = "cpp"
elif file.ext in [".h", ".c"]:
result = "c"
proc getGccModeArg*(mode: string): string =
## Produces a GCC argument that explicitly sets the language mode to be used by the compiler.
if mode == "cpp":
result = "-xc++"
elif mode == "c":
result = "-xc"
proc getCompiler*(): string =
var
compiler =
when defined(gcc):
"gcc"
elif defined(clang):
"clang"
else:
doAssert false, "Nimterop only supports gcc and clang at this time"
result = getEnv("CC", compiler)
proc getGccPaths*(mode: string): seq[string] =
var
nul = when defined(Windows): "nul" else: "/dev/null"
inc = false
(outp, _) = execAction(&"""{getCompiler()} -Wp,-v {getGccModeArg(mode)} {nul}""", die = false)
for line in outp.splitLines():
if "#include <...> search starts here" in line:
inc = true
continue
elif "End of search list" in line:
break
if inc:
var
path = line.strip().normalizedPath()
if path notin result:
result.add path
when defined(osx):
result.add(execAction("xcrun --show-sdk-path").output.strip() & "/usr/include")
proc getGccLibPaths*(mode: string): seq[string] =
var
nul = when defined(Windows): "nul" else: "/dev/null"
linker = when defined(OSX): "-Xlinker" else: ""
(outp, _) = execAction(&"""{getCompiler()} {linker} -v {getGccModeArg(mode)} {nul}""", die = false)
for line in outp.splitLines():
if "LIBRARY_PATH=" in line:
for path in line[13 .. ^1].split(PathSep):
var
path = path.strip().normalizedPath()
if path notin result:
result.add path
break
elif '\t' in line:
var
path = line.strip().normalizedPath()
if path notin result:
result.add path
when defined(osx):
result.add "/usr/lib"
proc getGccInfo*(): tuple[arch, os, compiler, version, libc: string] =
let
(outp, _) = execAction(&"{getCompiler()} -v")
for line in outp.splitLines():
if line.startsWith("Target: "):
result.arch = line.split(' ')[1].split('-')[0]
result.os =
if "linux" in line:
"linux"
elif "android" in line:
"android"
elif "darwin" in line:
"macos"
elif "w64" in line or "mingw" in line:
"windows"
else:
"unknown"
elif " version " in line:
result.version = line.split(" version ")[1].split(' ')[0]
if "clang" in outp:
if result.os == "macos":
result.compiler = "apple-clang"
else:
result.compiler = "clang"
else:
result.compiler = "gcc"
if "musl" in outp:
result.libc = "musl"

466
nimterop/build/conan.nim Normal file
View file

@ -0,0 +1,466 @@
import json, os, strformat, strutils, tables
import ".."/globals
import "."/[ccompiler, misc, nimconf, shell]
when (NimMajor, NimMinor, NimPatch) < (1, 2, 0):
import marshal
type
ConanPackage* = ref object
## ConanPackage type that stores conan uri and recipes/builds/revisions
name*: string
version*: string
user*: string
channel*: string
recipes*: OrderedTableRef[string, seq[ConanBuild]]
arch*, os*, compiler*, compversion*: string
bhash*: string
shared*: bool
sharedLibs*: seq[string]
staticLibs*: seq[string]
requires*: seq[ConanPackage]
skipRequires*: seq[string]
ConanBuild* = ref object
## Build type that stores build specific info and revisions
bhash*: string
settings*: TableRef[string, string]
options*: TableRef[string, string]
requires*: seq[string]
recipe_hash*: string
revisions*: seq[string]
const
# Conan API urls
conanBaseUrl = "https://conan.bintray.com/v2/conans"
conanSearchUrl = conanBaseUrl & "/search?q=$query"
conanPkgUrl = conanBaseUrl & "/$name/$version/$user/$channel/search$query"
conanCfgUrl = conanBaseUrl & "/$name/$version/$user/$channel/revisions/$recipe/packages/$build/revisions"
conanDlUrl = conanBaseUrl & "/$name/$version/$user/$channel/revisions/$recipe/packages/$build/revisions/$revision/files/$file"
# Bintray download sub-URL for explicit `user/channel` (not _/_)
conanDlAltUrl = "/download_file?file_path=$user%2F$name%2F$version%2F$channel%2F0%2Fpackage%2F$build%2F0%2F$file"
# Strings
conanInfo = "conaninfo.json"
conanPackage = "conan_package.tgz"
conanManifest = "conanmanifest.txt"
var
# Bintray download URL for explicit `user/channel`
conanBaseAltUrl {.compileTime.} = {
"bincrafters": "https://bintray.com/bincrafters/public-conan",
"conan": "https://bintray.com/conan-community/conan"
}.toTable()
# Reuse dependencies already downloaded
gConanRequires {.compileTime.}: Table[string, ConanPackage]
proc addAltConanBaseUrl*(name, url: string) =
# Add an alternate base URL for a custom conan repo on bintray
conanBaseAltUrl[name] = url
proc jsonGet(url: string): JsonNode =
# Make HTTP call and return content as JSON
let
temp = getTempDir()
file = block:
var
file = temp / url.extractFilename()
when defined(Windows):
file = file.replace('?', '_')
file
downloadUrl(url, temp, quiet = true)
try:
result = readFile(file).parseJson()
except JsonParsingError:
discard
rmFile(file)
proc `==`*(pkg1, pkg2: ConanPackage): bool =
## Check if two ConanPackage objects are equal
(not pkg1.isNil and not pkg2.isNil and
pkg1.name == pkg2.name and
pkg1.version == pkg2.version and
pkg1.user == pkg2.user and
pkg1.channel == pkg2.channel and
pkg1.arch == pkg2.arch and
pkg1.os == pkg2.os and
pkg1.compiler == pkg2.compiler and
pkg1.compversion == pkg2.compversion and
pkg1.bhash == pkg2.bhash and
pkg1.shared == pkg2.shared)
proc newConanPackage*(name, version, user = "_", channel = "_", bhash = "", shared = true): ConanPackage =
## Create a new ConanPackage with specified name and version
result = new(ConanPackage)
result.name = name
result.version = version
result.user = user
result.channel = channel
result.recipes = newOrderedTable[string, seq[ConanBuild]](2)
let
(arch, os, compiler, compversion, libc) = getGccInfo()
doAssert libc != "musl", "Conan does not provide precompiled binaries using musl"
result.arch = arch
result.os = os
result.compiler = compiler
result.compversion = compversion
result.bhash = bhash
result.shared = shared
proc newConanPackageFromUri*(uri: string, shared = true): ConanPackage =
## Create a new ConanPackage from a conan uri typically formatted as name/version[@user/channel][:bhash]
var
name, version, user, channel, bhash: string
spl = uri.split(":")
if spl.len > 1:
bhash = spl[1]
spl = spl[0].split('/')
name = spl[0]
user = "_"
channel = "_"
if spl.len > 2:
channel = spl[2]
if spl.len > 1:
spl = spl[1].split('@')
version = spl[0]
if spl.len > 1:
user = spl[1]
result = newConanPackage(name, version, user, channel, bhash, shared)
proc getUriFromConanPackage*(pkg: ConanPackage): string =
## Convert a ConanPackage to a conan uri
result = pkg.name
if pkg.version.nBl:
result &= "/" & pkg.version
if pkg.user.nBl:
result &= "@" & pkg.user
if pkg.channel.nBl:
result &= "/" & pkg.channel
if pkg.bhash.nBl:
result &= ":" & pkg.bhash
proc searchConan*(name: string, version = "", user = "", channel = ""): ConanPackage =
## Search for package by `name` and optional `version`, `user` and `channel`
##
## Search is quite slow so it is preferable to specify a version and use `getConanBuilds()`
var
query = name
if version.nBl:
query &= "/" & version
if user.nBl:
query &= "@" & user
if channel.nBl:
query &= "/" & channel
gecho &"# Searching Conan.io for latest version of {name}"
let
j1 = jsonGet(conanSearchUrl % ["query", query])
res = j1.getOrDefault("results").getElems()
# Return latest comparing versions - prefer @_/_
var
latest = ""
latestv = ""
for i in 0 ..< res.len:
let
str = res[i].getStr()
if "@_/_" in str:
let
ver = str.split('/')[1].split('@')[0]
if latestv.Bl or compareVersions(ver, latestv) > 0:
latestv = ver
latest = str
if latest.nBl:
result = newConanPackageFromUri(latest)
proc searchConan*(pkg: ConanPackage): ConanPackage =
## Search for latest package based on incomplete package info
result = searchConan(pkg.name, pkg.version, pkg.user, pkg.channel)
proc getConanBuilds*(pkg: ConanPackage, filter = "") =
## Get all builds for a package based on the C compiler's target OS/arch info
##
## `filter` can be used to tweak search terms
## e.g. build_type=Debug&compiler=clang
let
vsplit = pkg.compversion.split('.')
vfilter =
when defined(OSX):
vsplit[0 .. 1].join(".")
else:
vsplit[0]
query =
if pkg.bhash.Bl:
block:
var
query = &"?q=arch={pkg.arch}&os={pkg.os.capitalizeAscii()}"
if "build_type" notin filter:
query &= "&build_type=Release"
if "shared=" notin filter:
query &= &"&options.shared={($pkg.shared).capitalizeAscii()}"
if filter.nBl:
query &= &"&{filter}"
if "compiler=" notin filter and pkg.os != "windows":
query &= &"&compiler={pkg.compiler}&compiler.version=" & vfilter
if "compiler.runtime=" notin filter and pkg.os == "windows":
query &= &"&compiler.runtime=MD"
if "compiler.version=" notin filter and pkg.os == "windows":
query &= &"&compiler.version=14"
query.replace("&", "%20and%20")
else: ""
url = conanPkgUrl % [
"name", pkg.name,
"version", pkg.version,
"user", pkg.user,
"channel", pkg.channel,
"query", query
]
j1 = jsonGet(url)
if not j1.isNil:
for bhash, bdata in j1.getFields():
if pkg.bhash.Bl or pkg.bhash == bhash:
let
bld = new(ConanBuild)
settings = bdata.getOrDefault("settings")
options = bdata.getOrDefault("options")
requires = bdata.getOrDefault("requires")
bld.bhash = bhash
if not settings.isNil:
bld.settings = newTable[string, string](8)
for key, value in settings.getFields():
bld.settings[key] = value.getStr()
if not options.isNil:
bld.options = newTable[string, string](8)
for key, value in options.getFields():
bld.options[key] = value.getStr()
for req in requires.to(seq[string]):
# Filter skipped dependencies
if req.toLowerAscii() notin pkg.skipRequires:
bld.requires.add req
bld.recipe_hash = bdata.getOrDefault("recipe_hash").getStr()
if pkg.recipes.hasKey(bld.recipe_hash):
pkg.recipes[bld.recipe_hash].add bld
else:
pkg.recipes[bld.recipe_hash] = @[bld]
# Only need first or matching build
break
proc getConanRevisions*(pkg: ConanPackage, bld: ConanBuild) =
## Get all revisions of a build
let
url = conanCfgUrl % [
"name", pkg.name,
"version", pkg.version,
"user", pkg.user,
"channel", pkg.channel,
"recipe", bld.recipe_hash,
"build", bld.bhash
]
j1 = jsonGet(url)
if not j1.isNil:
let
revs = j1.getOrDefault("revisions")
for i in revs:
bld.revisions.add i.getOrDefault("revision").getStr()
proc loadConanInfo*(outdir: string): ConanPackage =
## Load cached package info from `outdir/conaninfo.json`
let
file = fixRelPath(outdir) / conanInfo
if fileExists(file):
when (NimMajor, NimMinor, NimPatch) < (1, 2, 0):
result = to[ConanPackage](readFile(file))
else:
try:
result = to(readFile(file).parseJson(), ConanPackage)
except:
discard
proc saveConanInfo*(pkg: ConanPackage, outdir: string) =
## Save downloaded package info to `outdir/conaninfo.json`
let
file = fixRelPath(outdir) / conanInfo
when (NimMajor, NimMinor, NimPatch) < (1, 2, 0):
writeFile(file, $$pkg)
else:
writeFile(file, $(%pkg))
proc parseConanManifest(pkg: ConanPackage, outdir: string) =
# Get all library info from downloaded conan package
let
file = outdir / conanManifest
if fileExists(file):
let
data = readFile(file)
for line in data.splitLines():
let
line = line.split(':')[0]
if line.startsWith("lib/"):
if line.endsWith(".a") or line.endsWith(".lib"):
pkg.staticLibs.add line
elif line.endsWith(".so") or line.endsWith(".dylib"):
pkg.sharedLibs.add line
elif line.startsWith("bin/") and line.endsWith("dll"):
pkg.sharedLibs.add line
proc dlConanBuild*(pkg: ConanPackage, bld: ConanBuild, outdir: string, revision = "") =
## Download specific `revision` of `bld` to `outdir`
##
## If omitted, the latest revision (first) is downloaded
doAssert bld.revisions.nBl, "No build revisions found for Conan.io package " & pkg.getUriFromConanPackage()
let
outdir = fixRelPath(outdir)
revision =
if revision.nBl:
revision
else:
bld.revisions[0]
url =
if pkg.user == "_":
conanDlUrl % [
"name", pkg.name,
"version", pkg.version,
"user", pkg.user,
"channel", pkg.channel,
"recipe", bld.recipe_hash,
"build", bld.bhash,
"revision", revision,
"file", conanPackage
]
else:
conanBaseAltUrl[pkg.user] & conanDlAltUrl % [
"name", pkg.name,
"version", pkg.version,
"user", pkg.user,
"channel", pkg.channel,
"build", bld.bhash,
"file", conanPackage
]
downloadUrl(url, outdir, quiet = true)
downloadUrl(url.replace(conanPackage, conanManifest), outdir, quiet = true)
pkg.parseConanManifest(outdir)
rmFile(outdir / url.extractFilename())
rmFile(outdir / conanManifest)
proc dlConanRequires*(pkg: ConanPackage, bld: ConanBuild, outdir: string)
proc downloadConan*(pkg: ConanPackage, outdir: string, main = true) =
## Download latest recipe/build/revision of `pkg` to `outdir`
##
## High-level API that handles the end to end Conan process flow to find
## latest package binary and downloads and extracts it to `outdir`.
let
outdir = fixRelPath(outdir)
pkg =
if pkg.version.Bl:
searchConan(pkg)
else:
pkg
if main:
let
cpkg = loadConanInfo(outdir)
if cpkg == pkg:
return
cleanDir(outdir)
pkg.getConanBuilds()
doAssert pkg.recipes.nBl, &"Failed to download {pkg.name} v{pkg.version} from Conan - check https://conan.io/center"
gecho &"# Downloading {pkg.name} v{pkg.version} from Conan.io"
for recipe, builds in pkg.recipes:
for build in builds:
if pkg.bhash.Bl or pkg.bhash == build.bhash:
pkg.getConanRevisions(build)
pkg.dlConanBuild(build, outdir)
pkg.dlConanRequires(build, outdir)
break
break
if main:
pkg.saveConanInfo(outdir)
proc dlConanRequires*(pkg: ConanPackage, bld: ConanBuild, outdir: string) =
## Download all required dependencies of this `bld`
##
## This is not required for shared libs since conan builds them
## with all dependencies statically linked in
let
outdir = fixRelPath(outdir)
if bld.options["shared"] == "False":
for req in bld.requires:
let
name = req.split('/')[0]
if gConanRequires.hasKey(name):
# Reuse dep already downloaded
pkg.requires.add gConanRequires[name]
else:
let
rpkg = newConanPackageFromUri(req, shared = false)
rpkg.skipRequires = pkg.skipRequires
downloadConan(rpkg, outdir, main = false)
pkg.requires.add rpkg
gConanRequires[name] = rpkg
proc getConanLDeps*(pkg: ConanPackage, outdir: string, main = true): seq[string] =
## Get all Conan libs - shared (.so|.dll) or static (.a|.lib) in pkg, including deps
## in descending order
##
## `outdir` is prefixed to each entry
let
libs = if pkg.shared: pkg.sharedLibs else: pkg.staticLibs
str = if pkg.shared: "shared" else: "static"
doAssert libs.nBl, &"No {str} libs found for {pkg.name} in {outdir}"
if not main:
for lib in libs:
result.add outdir / lib
for cpkg in pkg.requires:
result.add cpkg.getConanLDeps(outdir, main = false)

View file

@ -0,0 +1,568 @@
import macros, strformat, strutils, tables
import os except findExe
import ".."/globals
import "."/[ccompiler, conan, jbb, nimconf, shell, tools]
var
gDefines {.compileTime.} = initTable[string, string]()
macro setDefines*(defs: static openArray[string]): untyped =
## Specify `-d:xxx` values in code instead of having to rely on the command
## line or `cfg` or `nims` files.
##
## At this time, Nim does not allow creation of `-d:xxx` defines in code. In
## addition, Nim only loads config files for the module being compiled but not
## for imported packages. This becomes a challenge when wanting to ship a wrapper
## library that wants to control `getHeader()` for an underlying package.
##
## E.g. nimarchive wanting to set `-d:lzmaStatic`
##
## The consumer of nimarchive would need to set such defines as part of their
## project, making it inconvenient.
##
## By calling this proc with the defines preferred before importing such a module,
## the caller can set the behavior in code instead.
##
## .. code-block:: nim
##
## setDefines(@["lzmaStatic", "lzmaDL", "lzmaSetVer=5.2.4"])
##
## import lzma
for def in defs:
let
nv = def.strip().split("=", maxsplit = 1)
if nv.nBl:
let
n = nv[0]
v =
if nv.len == 2:
nv[1]
else:
""
gDefines[n] = v
macro clearDefines*(): untyped =
## Clear all defines set using `setDefines()`.
gDefines.clear()
macro isDefined*(def: untyped): untyped =
## Check if `-d:xxx` is set globally or via `setDefines()`
let
sdef = gDefines.hasKey(def.strVal())
result = newNimNode(nnkStmtList)
result.add(quote do:
when defined(`def`) or `sdef` != 0:
true
else:
false
)
macro getDefine*(def: untyped): untyped =
let version = newIdentNode(def.strVal())
let verVal =
if gDefines.hasKey(def.strVal()):
gDefines[def.strVal()]
else:
""
result = quote do:
const `version` {.strdefine.} = `verVal`
`version`
proc getDynlibExt(): string =
when defined(Windows):
result = "[0-9.\\-]*\\.dll"
elif defined(linux) or defined(FreeBSD):
result = "\\.so[0-9.]*"
elif defined(macosx):
result = "[0-9.\\-]*\\.dylib"
proc getStdPath(header, mode: string): string =
for inc in getGccPaths(mode):
result = findFile(header, inc, recurse = false, first = true)
if result.nBl:
break
proc getStdLibPath(lname, mode: string): string =
for lib in getGccLibPaths(mode):
result = findFile(lname, lib, recurse = false, first = true, regex = true)
if result.nBl:
break
proc getGitPath(header, url, outdir, version: string): string =
doAssert url.nBl, "No git url setup for " & header
doAssert findExe("git").nBl, "git executable missing"
gitPull(url, outdir, checkout = version)
result = findFile(header, outdir)
proc getDlPath(header, url, outdir, version: string): string =
doAssert url.nBl, "No download url setup for " & header
var
dlurl = url
if "$#" in url or "$1" in url:
doAssert version.nBl, "Need version for download url"
dlurl = url % version
else:
doAssert version.Bl, "Download url does not contain version"
downloadUrl(dlurl, outdir)
var
dirname = ""
for kind, path in walkDir(outdir, relative = true):
if kind == pcFile and path != dlurl.extractFilename():
dirname = ""
break
elif kind == pcDir:
if dirname.Bl:
dirname = path
else:
dirname = ""
break
if dirname.nBl:
for kind, path in walkDir(outdir / dirname, relative = true):
mvFile(outdir / dirname / path, outdir / path)
result = findFile(header, outdir)
proc getConanPath(header, uri, flags, outdir, version: string, shared: bool): string =
var
uri = uri
if "$#" in uri or "$1" in uri:
doAssert version.nBl, "Need version for Conan.io uri: " & uri
uri = uri % version
elif version.nBl:
uri = uri & "/" & version
let
pkg = newConanPackageFromUri(uri, shared)
# Handle `conanFlags`
if flags.nBl:
for flag in flags.split(" "):
if flag.startsWith("skip="):
for req in flag["skip=".len .. ^1].split(","):
if req.nBl:
pkg.skipRequires.add req.toLowerAscii()
downloadConan(pkg, outdir)
result = findFile(header, outdir)
proc getConanLDeps(outdir: string): seq[string] =
let
pkg = loadConanInfo(outdir)
result = pkg.getConanLDeps(outdir)
proc getJBBPath(header, uri, flags, outdir, version: string): string =
let
spl = uri.split('/', 1)
name = spl[0]
hasVersion = version.nBl
var
ver = if spl.len == 2: spl[1] else: ""
if ver.nBl:
if "$#" in ver or "$1" in ver:
doAssert hasVersion, "Need version for BinaryBuilder.org uri: " & uri
ver = ver % version
elif hasVersion:
doAssert false, "Version in both uri `" & uri & "` and `-d:xxxSetVer=\"" &
version & "\"` for BinaryBuilder.org"
elif hasVersion:
ver = version
let
pkg = newJBBPackage(name, ver)
# Handle `jbbFlags`
if flags.nBl:
for flag in flags.split(" "):
if flag.startsWith("giturl="):
let
val = flag["giturl=".len .. ^1]
if val.contains("://"):
pkg.baseUrl = val
else:
pkg.baseUrl = "https://github.com/" & val
elif flag.startsWith("url="):
pkg.baseUrl = flag["url=".len .. ^1]
pkg.isGit = false
elif flag.startsWith("skip="):
for req in flag["skip=".len .. ^1].split(","):
if req.nBl:
pkg.skipRequires.add req.toLowerAscii()
downloadJBB(pkg, outdir)
result = findFile(header, outdir)
proc getJBBLDeps(outdir: string, shared: bool): seq[string] =
let
pkg = loadJBBInfo(outdir)
result = pkg.getJBBLDeps(outdir, shared)
proc getLocalPath(header, outdir: string): string =
if outdir.nBl:
result = findFile(header, outdir)
proc buildLibrary(lname, outdir, conFlags, cmakeFlags, makeFlags: string, buildTypes: openArray[BuildType]): string =
var
lpath = findFile(lname, outdir, regex = true)
makePath = outdir
buildStatus: BuildStatus
errors: seq[string]
if lpath.nBl:
return lpath
for buildType in buildTypes:
case buildType
of btCmake:
buildStatus = buildWithCmake(makePath, cmakeFlags)
of btAutoconf:
buildStatus = buildWithAutoConf(makePath, conFlags)
if buildStatus.built:
break
elif buildStatus.error.nBl:
errors.add buildStatus.error
if buildStatus.buildPath.len > 0:
let libraryExists = findFile(lname, buildStatus.buildPath, regex = true).len > 0
if not libraryExists and fileExists(buildStatus.buildPath / "Makefile"):
make(buildStatus.buildPath, lname, makeFlags, regex = true)
buildStatus.built = true
let error = if errors.len > 0: errors.join("\n") else: "No build files found in " & outdir
doAssert buildStatus.built, &"\nBuild configuration failed - {error}\n"
result = findFile(lname, outdir, regex = true)
macro getHeader*(
header: static[string], giturl: static[string] = "", dlurl: static[string] = "",
conanuri: static[string] = "", jbburi: static[string] = "",
outdir: static[string] = "", libdir: static[string] = "",
conFlags: static[string] = "", cmakeFlags: static[string] = "", makeFlags: static[string] = "",
conanFlags: static[string] = "", jbbFlags: static[string] = "", altNames: static[string] = "",
buildTypes: static[openArray[BuildType]] = [btCmake, btAutoconf]): untyped =
## Get the path to a header file for wrapping with
## `cImport() <cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring>`_ or
## `c2nImport() <cimport.html#c2nImport.m%2C%2Cstring%2Cstring%2Cstring>`_.
##
## This proc checks `-d:xxx` defines based on the header name (e.g. lzma from lzma.h),
## and accordingly employs different ways to obtain the source.
##
## `-d:xxxStd` - search standard system paths. E.g. `/usr/include` and `/usr/lib` on Linux
## `-d:xxxGit` - clone source from a git repo specified in `giturl`
## `-d:xxxDL` - download source from `dlurl` and extract if required
## `-d:xxxConan` - download headers and binary from Conan.io using `conanuri` with
## format `pkgname[/version[@user/channel][:bhash]]`
## `-d:xxxJBB` - download headers and binary from BinaryBuilder.org using `jbburi` with
## format `pkgname[/version]`
##
## This allows a single wrapper to be used in different ways depending on the user's needs.
## If no `-d:xxx` defines are specified, `outdir` will be searched for the header as is.
## The user can opt to download the sources to `outdir` using any other method such as
## git sub-modules, vendoring or pointing to a repository that was already cloned.
##
## If multiple `-d:xxx` defines are specified, precedence is `Std` and then `Git`, `DL`,
## `Conan` or `JBB`. This allows using a system installed library if available before
## falling back to manual building. The user would need to specify both `-d:xxxStd` and
## one of the other methods.
##
## `-d:xxxSetVer=x.y.z` can be used to specify which version to use. It is used as a tag
## name for `Git` whereas for `DL`, `Conan` and `JBB`, it replaces `$1` in the URL
## if specified. Specifying `-d:xxxSetVer` without a `$1` will download that version for
## `Conan` and `JBB` if available. If no version is specified, the latest release of the
## package is downloaded. For `Conan`, `-d:xxxSetVer` can also be used to set additional
## URI information:
## `-d:xxxSetVer=1.9.0@bincrafters/stable:bhash`
##
## If `conanuri` or `jbburi` are not defined and `Conan` or `JBB` is selected, the `header`
## filename is used instead.
##
## All defines can also be set in code using `setDefines()` and checked for using
## `isDefined()` which checks for defines set from both `-d` and `setDefines()`.
##
## The library is then configured (with `cmake` or `autotools` if possible) and built
## using `make`, unless using `-d:xxxStd` which presumes that the system package
## manager was used to install prebuilt headers and binaries, or using `-d:xxxConan`
## or `-d:xxxJBB` which download pre-built binaries.
##
## The header path is stored in `const xxxPath` and can be used in a `cImport()` call
## in the calling wrapper. The dynamic library path is stored in `const xxxLPath` and can
## be used for the `dynlib` parameter (within quotes) or with `cPassL()`. Any dependency
## libraries downloaded by `Conan` or `JBB` are returned in `const xxxLDeps` as a seq[string].
##
## `libdir` can be used to instruct `getHeader()` to copy shared libraries and their
## dependencies to that directory. This prevents any runtime failures if `outdir` gets
## removed or its contents changed. By default, `libdir` is set to the output directory
## where the program binary will be created. The values of `xxxLPath` and `xxxLDeps` will
## reflect this new location. `libdir` is ignored for `Std` mode.
##
## `-d:xxxStatic` can be specified to statically link with the library instead. This
## will automatically add a `cPassL()` call to the static library for convenience. Note
## that `-d:xxxConan` and `-d:xxxJBB` download all dependency libs as well and the
## `xxxLPath` will include paths to all of them separated by space in the right order for
## linking.
##
## Note also that Conan currently builds all OSX binaries on 10.14 so older versions of
## OSX will complain if statically linking to these binaries. Further, all Conan binaries
## for Windows are built with Visual Studio so static linking the `.lib` files with gcc
## or clang might lead to incompatibility issues if the library uses Visual Studio
## specific compiler features.
##
## `conFlags`, `cmakeFlags` and `makeFlags` allow sending custom parameters to `configure`,
## `cmake` and `make` in case additional configuration is required as part of the build
## process.
##
## `conanFlags` and `jbbFlags` allow changing the Conan.io and BinaryBuilder.org defaults:
## - `skip=pkg1,pkg2` skips the specified packages which are required dependencies of the
## package in question. This enables downloading those dependencies from other sources
## if required.
##
## `jbbFlags` allows two additional customizations:
## - `giturl=customUrl` changes the default `https://github.com/JuliaBinaryWrappers` to
## another Git URL. If no hostname is specified, `https://github.com` is assumed.
## - `url=customUrl` uses regular HTTP instead of Git and looks for `Artifacts.toml` and
## `Project.toml` files at that location. `$1` or `$#` are replaced with the version
## if specified.
##
## `altNames` is a list of alternate names for the library - e.g. zlib uses `zlib.h` for
## the header but the typical lib name is `libz.so` and not `libzlib.so`. However, it is
## libzlib.dll on Windows if built with cmake. In this case, `altNames = "z,zlib"`. Comma
## separate for multiple alternate names without spaces.
##
## The original header name is not included by default if `altNames` is set since it could
## cause the wrong lib to be selected. E.g. `SDL2/SDL.h` could pick `libSDL.so` even if
## `altNames = "SDL2"`. Explicitly include it in `altNames` like the `zlib` example when
## required.
##
## `buildTypes` specifies a list of ordered build strategies to use when building the
## downloaded source files. Default is [btCmake, btAutoconf]
##
## `xxxPreBuild` is a hook that is called after the source code is pulled from Git or
## downloaded but before the library is built. This might be needed if some initial prep
## needs to be done before compilation. A few values are provided to the hook to help
## provide context:
##
## `outdir` is the same `outdir` passed in and `header` is the discovered header path
## in the downloaded source code.
##
## Simply define `proc xxxPreBuild(outdir, header: string)` in the wrapper and it will get
## called prior to the build process.
var
origname = header.extractFilename().split(".")[0]
name = origname.split(seps = AllChars-Letters-Digits).join()
# Default to origname if not specified
conanuri = if conanuri.nBl: conanuri else: origname
jbburi = if jbburi.nBl: jbburi else: origname
# -d:xxx for this header
stdStr = name & "Std"
gitStr = name & "Git"
dlStr = name & "DL"
conanStr = name & "Conan"
jbbStr = name & "JBB"
staticStr = name & "Static"
verStr = name & "SetVer"
getPath = name & "GetPath"
# Ident nodes of the -d:xxx to check in when statements
nameStd = newIdentNode(stdStr)
nameGit = newIdentNode(gitStr)
nameDL = newIdentNode(dlStr)
nameConan = newIdentNode(conanStr)
nameJBB = newIdentNode(jbbStr)
nameStatic = newIdentNode(staticStr)
nameGetPath = newIdentNode(getPath)
# Consts to generate
path = newIdentNode(name & "Path")
lpath = newIdentNode(name & "LPath")
ldeps = newIdentNode(name & "LDeps")
version = newIdentNode(verStr)
lname = newIdentNode(name & "LName")
preBuild = newIdentNode(name & "PreBuild")
# Regex for library search
lre = "(lib)?$1[_-]?(static)?"
# If -d:xxx set with setDefines()
stdVal = gDefines.hasKey(stdStr)
gitVal = gDefines.hasKey(gitStr)
dlVal = gDefines.hasKey(dlStr)
conanVal = gDefines.hasKey(conanStr)
jbbVal = gDefines.hasKey(jbbStr)
staticVal = gDefines.hasKey(staticStr)
verVal =
if gDefines.hasKey(verStr):
gDefines[verStr]
else:
""
mode = getCompilerMode(header)
libdir = if libdir.nBl: libdir else: getOutDir()
# Use alternate library names if specified for regex search
if altNames.nBl:
lre = lre % ("(" & altNames.replace(",", "|") & ")")
else:
lre = lre % origname
result = newNimNode(nnkStmtList)
result.add(quote do:
# Need to check -d:xxx or setDefines()
const
`nameStd`* = when defined(`nameStd`): true else: `stdVal` == 1
`nameGit`* = when defined(`nameGit`): true else: `gitVal` == 1
`nameDL`* = when defined(`nameDL`): true else: `dlVal` == 1
`nameConan`* = when defined(`nameConan`): true else: `conanVal` == 1
`nameJBB`* = when defined(`nameJBB`): true else: `jbbVal` == 1
`nameStatic`* = when defined(`nameStatic`): true else: `staticVal` == 1
# Search for header in outdir (after retrieving code) depending on -d:xxx mode
proc `nameGetPath`(header, giturl, dlurl, conanuri, conanFlags, jbburi, jbbFlags,
outdir, version: string, shared: bool): string =
when `nameGit`:
getGitPath(header, giturl, outdir, version)
elif `nameDL`:
getDlPath(header, dlurl, outdir, version)
elif `nameConan`:
getConanPath(header, conanuri, conanFlags, outdir, version, shared)
elif `nameJBB`:
getJBBPath(header, jbburi, jbbFlags, outdir, version)
else:
getLocalPath(header, outdir)
static:
# Don't delete project
when not `nameStd` and (`nameGit` or `nameDL` or `nameConan` or `nameJBB`):
doAssert `outdir`.len != 0, "getHeader():outdir cannot be blank"
doAssert `outdir` != getProjectPath(), "getHeader():outdir cannot be the project path"
const
`version`* {.strdefine.} = `verVal`
`lname` =
when `nameStatic`:
`lre` & "\\.(a|lib)"
else:
`lre` & getDynlibExt()
# Look in standard path if requested by user
stdPath =
when `nameStd`: getStdPath(`header`, `mode`) else: ""
stdLPath =
when `nameStd`: getStdLibPath(`lname`, `mode`) else: ""
useStd = stdPath.len != 0 and stdLPath.len != 0
# Look elsewhere if requested while prioritizing standard paths
prePath =
when useStd:
stdPath
else:
`nameGetPath`(`header`, `giturl`, `dlurl`, `conanuri`, `conanFlags`, `jbburi`, `jbbFlags`,
`outdir`, `version`, not `nameStatic`)
# Run preBuild hook before building library if not Std, Conan or JBB
when not (useStd or `nameConan` or `nameJBB`) and declared(`preBuild`):
static:
`preBuild`(`outdir`, prePath)
let
# Library binary path - build if not standard / conan / jbb
lpath {.compileTime.} =
when useStd:
stdLPath
elif `nameConan` or `nameJBB`:
findFile(`lname`, `outdir`, regex = true)
else:
buildLibrary(`lname`, `outdir`, `conFlags`, `cmakeFlags`, `makeFlags`, `buildTypes`)
# Library dependecy paths
ldeps {.compileTime.}: seq[string] =
when not useStd:
when `nameConan`:
getConanLDeps(`outdir`)
elif `nameJBB`:
getJBBLDeps(`outdir`, not `nameStatic`)
else:
@[]
else:
@[]
const
# Header path - search again in case header is generated in build
`path`* =
if prePath.len != 0:
prePath
else:
`nameGetPath`(`header`, `giturl`, `dlurl`, `conanuri`, `conanFlags`, `jbburi`, `jbbFlags`,
`outdir`, `version`, not `nameStatic`)
static:
doAssert `path`.len != 0, "\nHeader " & `header` & " not found - " &
"missing/empty outdir or -d:$1Std -d:$1Git -d:$1DL -d:$1Conan or -d:$1JBB not specified" % `name`
doAssert lpath.len != 0, "\nLibrary " & `lname` & " not found"
when `nameStatic`:
const
`lpath`* = lpath
`ldeps`* = ldeps
# Automatically link with static library and dependencies
static:
gecho "# Including library " & lpath
gStateCT.passL.add lpath
if ldeps.len != 0:
gecho "# Including dependencies " & ldeps.join(" ")
gStateCT.passL.add ldeps.join(" ")
else:
const
`lpath`* = when not useStd: `libdir` / lpath.extractFilename() else: lpath
`ldeps`* =
when not useStd:
block:
var
ldeps = ldeps
copied: seq[string]
for i in 0 ..< ldeps.len:
let
lname = ldeps[i].extractFilename()
ldeptgt = `libdir` / lname
if not fileExists(ldeptgt) or getFileDate(ldeps[i]) != getFileDate(ldeptgt):
cpFile(ldeps[i], ldeptgt, psymlink = true)
copied.add lname
ldeps[i] = ldeptgt
# Copy downloaded dependencies to `libdir`
if copied.len != 0:
gecho "# Copying dependencies: " & copied.join(" ") & "\n# to " & `libdir`
ldeps
else:
ldeps
static:
when not useStd:
# Copy downloaded shared libraries to `libdir`
if not fileExists(`lpath`) or getFileDate(lpath) != getFileDate(`lpath`):
gecho "# Copying " & `lpath`.extractFilename() & " to " & `libdir`
cpFile(lpath, `lpath`)
gecho "# Including library " & `lpath`
)

277
nimterop/build/jbb.nim Normal file
View file

@ -0,0 +1,277 @@
import json, os, strformat, strutils, tables
import ".."/globals
import "."/[ccompiler, nimconf, shell]
when (NimMajor, NimMinor, NimPatch) < (1, 2, 0):
import marshal
type
JBBPackage* = ref object
## JBBPackage type that stores package information
name*: string
version*: string
baseUrl*: string # Location to find package
isGit*: bool # Git or HTTP
url*: string # Download URL
arch*, os*, libc*: string # Target
sharedLibs*: seq[string]
staticLibs*: seq[string]
requires*: seq[JBBPackage]
skipRequires*: seq[string]
const
# JBB URLs
jbbBaseUrl = "https://github.com/JuliaBinaryWrappers"
jbbInfo = "jbbinfo.json"
jbbProject = "Project.toml"
jbbArtifacts = "Artifacts.toml"
var
# Reuse dependencies already downloaded
gJBBRequires {.compileTime.}: Table[string, JBBPackage]
proc `==`*(pkg1, pkg2: JBBPackage): bool =
## Check if two JBBPackage objects are equal
(not pkg1.isNil and not pkg2.isNil and
pkg1.name == pkg2.name and
pkg1.version == pkg2.version and
pkg1.arch == pkg2.arch and
pkg1.os == pkg2.os and
pkg1.libc == pkg2.libc)
proc newJBBPackage*(name, version: string): JBBPackage =
## Create a new JBBPackage with specified name and version
result = new(JBBPackage)
result.name = name
result.version = version
result.baseUrl = jbbBaseUrl
result.isGit = true
let
(arch, os, _, _, libc) = getGccInfo()
result.arch = arch
result.os = os
result.libc = libc
proc parseJBBProject(pkg: JBBPackage, outdir: string) =
# Get all dependencies from Project.toml
let
file = outdir / jbbProject
if fileExists(file):
let
data = readFile(file)
var
deps = false
doAssert pkg.version in data, &"{pkg.name} v{pkg.version} not found"
for line in data.splitLines():
let
line = line.strip()
if line.nBl:
if line.startsWith('['):
if line == "[deps]":
deps = true
else:
deps = false
elif deps:
let
name = line.split()[0]
if name.endsWith("_jll"):
# Filter skipped dependencies
let
pname = name[0 .. ^5]
if pname.toLowerAscii() notin pkg.skipRequires:
pkg.requires.add newJBBPackage(pname, "")
pkg.requires[^1].skipRequires = pkg.skipRequires
proc parseJBBArtifacts(pkg: JBBPackage, outdir: string) =
# Get build information from Artifacts.toml
let
file = outdir / jbbArtifacts
if fileExists(file):
let
data = readFile(file)
doAssert pkg.version in data, &"{pkg.name} v{pkg.version} not found"
var
found = false
for line in data.splitLines():
let
line = line.strip()
if line.nBl:
let
spl = line.split(" = ", 1)
name = spl[0]
val = if spl.len == 2: spl[1].strip(chars = {'"', ' '}) else: ""
# Match arch, os and glibc on Linux to find download URL
case name
of "arch":
if val == pkg.arch and not found: found = true
of "os":
if val != pkg.os and found: found = false
of "libc":
when defined(Linux):
if found:
let libc = if pkg.libc.nBl: pkg.libc else: "glibc"
if val != libc: found = false
of "url":
if found:
pkg.url = val
break
else:
discard
proc findJBBLibs(pkg: JBBPackage, outdir: string) =
pkg.sharedLibs = findFiles("(bin|lib)[\\\\/].*\\.(so|dll|dylib)[0-9.]*", outdir)
for lib in findFiles("lib[\\\\/].*\\.(a|lib)", outdir):
if not lib.endsWith(".dll.a"):
pkg.staticLibs.add lib
proc getJBBRepo*(pkg: JBBPackage, outdir: string) =
## Clone JBB package repo and checkout version tag if version is
## specified in package
let
path = outdir / "repos" / pkg.name
if pkg.isGit:
# Get package info using Git
gitPull(
pkg.baseUrl & ("/$1_jll.jl" % pkg.name),
outdir = path,
plist = "*.toml",
"master",
quiet = true
)
if pkg.version.nBl:
# Checkout correct tag
let
tags = gitTags(path)
for i in tags.len - 1 .. 0:
if pkg.version in tags[i] and i != tags.len - 1:
gitCheckout(path, tags[i-1])
else:
# Download package info from HTTP
var
url = pkg.baseUrl
if "$#" in url or "$1" in url:
doAssert pkg.version.nBl, "Need version for custom BinaryBuilder.org url: " & url
url = url % pkg.version
downloadUrl(url & "Artifacts.toml", path, quiet = true)
downloadUrl(url & "Project.toml", path, quiet = true)
pkg.parseJBBProject(path)
pkg.parseJBBArtifacts(path)
proc loadJBBInfo*(outdir: string): JBBPackage =
## Load cached package info from `outdir/jbbinfo.json`
let
file = fixRelPath(outdir) / jbbInfo
if fileExists(file):
when (NimMajor, NimMinor, NimPatch) < (1, 2, 0):
result = to[JBBPackage](readFile(file))
else:
try:
result = to(readFile(file).parseJson(), JBBPackage)
except:
discard
proc saveJBBInfo*(pkg: JBBPackage, outdir: string) =
## Save downloaded package info to `outdir/jbbinfo.json`
let
file = fixRelPath(outdir) / jbbInfo
when (NimMajor, NimMinor, NimPatch) < (1, 2, 0):
writeFile(file, $$pkg)
else:
writeFile(file, $(%pkg))
proc dlJBBRequires*(pkg: JBBPackage, outdir: string)
proc downloadJBB*(pkg: JBBPackage, outdir: string, main = true) =
## Download `pkg` from BinaryBuilder.org to `outdir`
##
## High-level API that handles the end to end JBB process flow to find
## latest package binary and downloads and extracts it to `outdir`.
let
outdir = fixRelPath(outdir)
if main:
let
cpkg = loadJBBInfo(outdir)
if cpkg == pkg:
return
cleanDir(outdir)
pkg.getJBBRepo(outdir)
if pkg.url.Bl:
# No url for deps means no package for that os/arch combo - e.g. Attr
doAssert not main, &"Failed to download {pkg.name} info from BinaryBuilder.org"
return
let
vstr =
if pkg.version.nBl:
&" v{pkg.version}"
else:
""
path = outdir / pkg.name
gecho &"# Downloading {pkg.name}{vstr} from BinaryBuilder.org"
downloadUrl(pkg.url, path, quiet = true)
pkg.findJBBLibs(path)
pkg.dlJBBRequires(outdir)
if main:
pkg.saveJBBInfo(outdir)
proc dlJBBRequires*(pkg: JBBPackage, outdir: string) =
## Download all required dependencies of this `pkg`
let
outdir = fixRelPath(outdir)
for i in 0 ..< pkg.requires.len:
let
rpkg = pkg.requires[i]
if gJBBRequires.hasKey(rpkg.name):
# Reuse dep already downloaded
pkg.requires[i] = gJBBRequires[rpkg.name]
else:
downloadJBB(rpkg, outdir, main = false)
gJBBRequires[rpkg.name] = rpkg
proc getJBBLDeps*(pkg: JBBPackage, outdir: string, shared: bool, main = true): seq[string] =
## Get all BinaryBuilder.org libs - shared (.so|.dll) or static (.a|.lib) in pkg, including deps
## in descending order
##
## `outdir` is prefixed to each entry
let
libs = if shared: pkg.sharedLibs else: pkg.staticLibs
str = if shared: "shared" else: "static"
doAssert libs.nBl, &"No {str} libs found for {pkg.name} in {outdir}"
if not main:
for lib in libs:
result.add lib
for cpkg in pkg.requires:
# No url for deps means no package for that os/arch combo - e.g. Attr
if cpkg.url.nBl:
result.add cpkg.getJBBLDeps(outdir, shared, main = false)

62
nimterop/build/misc.nim Normal file
View file

@ -0,0 +1,62 @@
import os, strutils
when defined(Windows):
import strformat
import ".."/globals
proc sanitizePath*(path: string, noQuote = false, sep = $DirSep): string =
result = path.multiReplace([("\\\\", sep), ("\\", sep), ("/", sep)])
if not noQuote:
result = result.quoteShell
proc getCurrentNimCompiler*(): string =
when nimvm:
result = getCurrentCompilerExe()
when defined(nimsuggest):
result = result.replace("nimsuggest", "nim")
else:
result = gState.nim
proc compareVersions*(ver1, ver2: string): int =
## Compare two version strings x.y.z and return -1, 0, 1
##
## ver1 < ver2 = -1
## ver1 = ver2 = 0
## ver1 > ver2 = 1
let
ver1seq = ver1.replace("-", "").split('.')
ver2seq = ver2.replace("-", "").split('.')
for i in 0 ..< ver1seq.len:
let
p1 = ver1seq[i]
p2 = if i < ver2seq.len: ver2seq[i] else: "0"
try:
let
h1 = p1.parseHexInt()
h2 = p2.parseHexInt()
if h1 < h2: return -1
elif h1 > h2: return 1
except ValueError:
if p1 < p2: return -1
elif p1 > p2: return 1
proc fixCmd*(cmd: string): string =
when defined(Windows):
# Replace 'cd d:\abc' with 'd: && cd d:\abc`
var filteredCmd = cmd
if cmd.toLower().startsWith("cd"):
var
colonIndex = cmd.find(":")
driveLetter = cmd.substr(colonIndex-1, colonIndex)
if (driveLetter[0].isAlphaAscii() and
driveLetter[1] == ':' and
colonIndex == 4):
filteredCmd = &"{driveLetter} && {cmd}"
result = "cmd /c " & filteredCmd
elif defined(posix):
result = cmd
else:
doAssert false

240
nimterop/build/nimconf.nim Normal file
View file

@ -0,0 +1,240 @@
import json, os, osproc, sets, strformat, strutils
import ".."/globals
import "."/misc
when nimvm:
when (NimMajor, NimMinor, NimPatch) >= (1, 2, 0):
import std/compilesettings
else:
import macros
else:
discard
# Config detected with std/compilesettings or `nim dump`
type
Config* = ref object
NimMajor*: int
NimMinor*: int
NimPatch*: int
paths*: OrderedSet[string]
nimblePaths*: OrderedSet[string]
nimcacheDir*: string
outDir*: string
proc getJson(projectDir: string): JsonNode =
# Get `nim dump` json value for `projectDir`
var
cmd = &"{getCurrentNimCompiler()} --hints:off --dump.format:json dump dummy"
dump = ""
ret = 0
if projectDir.len != 0:
# Run `nim dump` in `projectDir` if specified
cmd = &"cd {projectDir.sanitizePath} && " & cmd
cmd = fixCmd(cmd)
when nimvm:
(dump, ret) = gorgeEx(cmd)
else:
(dump, ret) = execCmdEx(cmd)
try:
result = parseJson(dump)
except JsonParsingError as e:
gecho "# Failed to parse `nim dump` output: " & e.msg
proc getOsCacheDir(): string =
# OS default cache directory
when defined(posix):
result = getEnv("XDG_CACHE_HOME", getHomeDir() / ".cache") / "nim"
else:
result = getHomeDir() / "nimcache"
proc getProjectDir*(): string =
## Get project directory for this compilation - returns `""` at runtime
when nimvm:
when (NimMajor, NimMinor, NimPatch) >= (1, 2, 0):
# If nim v1.2.0+, get from `std/compilesettings`
result = querySetting(projectFull).parentDir()
else:
# Get from `macros`
result = getProjectPath()
else:
discard
proc stripName(path, projectName: string): string =
# Remove `pname_d|r` tail from path
let
(head, tail) = path.splitPath()
if projectName in tail:
result = head
else:
result = path
proc jsonToSeq(node: JsonNode, key: string): seq[string] =
# Convert JsonArray to seq[string] for specified `key`
if node.hasKey(key):
for elem in node[key].getElems():
result.add elem.getStr()
proc getAbsoluteDir(projectDir, path: string): string =
# Path is relative to `projectDir` if not absolute
if path.isAbsolute():
result = path
else:
result = (projectDir / path).normalizedPath()
proc getNimConfig*(projectDir = ""): Config =
# Get `paths` - list of paths to be forwarded to Nim
result = new(Config)
var
libPath, version: string
lazyPaths, searchPaths: seq[string]
when nimvm:
result.NimMajor = NimMajor
result.NimMinor = NimMinor
result.NimPatch = NimPatch
when (NimMajor, NimMinor, NimPatch) >= (1, 2, 0):
# Get value at compile time from `std/compilesettings`
libPath = getCurrentCompilerExe().parentDir().parentDir() / "lib"
lazyPaths = querySettingSeq(MultipleValueSetting.lazyPaths)
searchPaths = querySettingSeq(MultipleValueSetting.searchPaths)
result.nimcacheDir = stripName(
querySetting(SingleValueSetting.nimcacheDir),
querySetting(SingleValueSetting.projectName)
)
result.outDir = querySetting(SingleValueSetting.outDir)
else:
discard
let
# Get project directory for < v1.2.0 at compile time
projectDir = if projectDir.len != 0: projectDir else: getProjectDir()
# Not Nim v1.2.0+ or runtime
if libPath.len == 0:
let
dumpJson = getJson(projectDir)
if dumpJson != nil:
if dumpJson.hasKey("version"):
version = dumpJson["version"].getStr()
lazyPaths = jsonToSeq(dumpJson, "lazyPaths")
searchPaths = jsonToSeq(dumpJson, "lib_paths")
if dumpJson.hasKey("libpath"):
libPath = dumpJson["libpath"].getStr()
elif searchPaths.len != 0:
# Usually `libPath` is last entry in `searchPaths`
libPath = searchPaths[^1]
if dumpJson.hasKey("nimcache"):
result.nimcacheDir = stripName(dumpJson["nimcache"].getStr(), "dummy")
if dumpJson.hasKey("outdir"):
result.outDir = dumpJson["outdir"].getStr()
# Parse version
if version.len != 0:
let
splversion = version.split({'.'}, maxsplit = 3)
result.NimMajor = splversion[0].parseInt()
result.NimMinor = splversion[1].parseInt()
result.NimPatch = splversion[2].parseInt()
# Find non standard lib paths added to `searchPath`
for path in searchPaths:
let
path = getAbsoluteDir(projectDir, path)
if libPath notin path:
result.paths.incl path
# Find `nimblePaths` in `lazyPaths`
for path in lazyPaths:
let
path = getAbsoluteDir(projectDir, path)
(_, tail) = path.strip(leading = false, chars = {'/', '\\'}).splitPath()
if tail == "pkgs":
# Nimble path probably
result.nimblePaths.incl path
# Find `paths` in `lazyPaths` that aren't within `nimblePaths`
# Have to do this separately since `nimblePaths` could be after
# packages in `lazyPaths`
for path in lazyPaths:
let
path = getAbsoluteDir(projectDir, path)
var skip = false
for npath in result.nimblePaths:
if npath in path:
skip = true
break
if not skip:
result.paths.incl path
if result.nimcacheDir.len == 0:
result.nimcacheDir = getOsCacheDir()
if result.outDir.len == 0:
result.outDir = projectDir
proc getNimConfigFlags(cfg: Config): string =
# Convert configuration into Nim flags for cfg file or command line
result = &"--nimcache:\"{cfg.nimcacheDir}\"\n"
if (cfg.NimMajor, cfg.NimMinor, cfg.NimPatch) >= (1, 2, 0):
# --clearNimbleCache if Nim v1.2.0+
result &= "--clearNimblePath\n"
# Add `nimblePaths` if detected - v1.2.0+
for path in cfg.nimblePaths:
result &= &"--nimblePath:\"{path}\"\n"
# Add `paths` in all cases if any detected
for path in cfg.paths:
result &= &"--path:\"{path}\"\n"
when defined(Windows):
result = result.replace("\\", "/")
proc getNimConfigFlags*(projectDir = ""): string =
## Get Nim command line configuration flags for `projectDir`
##
## If `projectDir` is not specified, it is detected if compile time or
## current directory is used.
let
cfg = getNimConfig(projectDir)
cfgOut = getNimConfigFlags(cfg)
return cfgOut.replace("\n", " ")
proc writeNimConfig*(cfgFile: string, projectDir = "") =
## Write Nim configuration for `projectDir` to specified `cfgFile`
##
## If `projectDir` is not specified, it is detected if compile time or
## current directory is used.
let
cfg = getNimConfig(projectDir)
cfgOut = getNimConfigFlags(cfg)
writeFile(cfgFile, cfgOut)
proc getNimcacheDir*(projectDir = ""): string =
## Get nimcache directory for current compilation or specified `projectDir`
let
cfg = getNimConfig(projectDir)
result = cfg.nimcacheDir
proc getOutDir*(projectDir = ""): string =
## Get output directory for current compilation or specified `projectDir`
let
cfg = getNimConfig(projectDir)
result = cfg.outDir
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

528
nimterop/build/shell.nim Normal file
View file

@ -0,0 +1,528 @@
import hashes, osproc, sets, strformat, strutils
when not defined(TOAST):
import os except findExe, sleep
else:
import os
import ".."/globals
import "."/[misc, nimconf]
when not defined(TOAST):
proc sleep*(milsecs: int) =
## Sleep at compile time
let
cmd =
when defined(Windows):
"cmd /c timeout "
else:
"sleep "
discard gorgeEx(cmd & $(milsecs / 1000))
else:
export sleep
proc execAction*(cmd: string, retry = 0, die = true, cache = false,
cacheKey = "", onRetry: proc() = nil,
onError: proc(output: string, err: int) = nil): tuple[output: string, ret: int] =
## Execute an external command - supported at compile time
##
## Checks if command exits successfully before returning. If not, an
## error is raised. Always caches results to be used in nimsuggest or nimcheck
## mode.
##
## `retry` - number of times command should be retried before error
## `die = false` - return on errors
## `cache = true` - cache results unless cleared with -f
## `cacheKey` - key to create unique cache entry
## `onRetry()` - proc to call before retrying
## `onError(output, err)` - proc to call on error
let
ccmd = fixCmd(cmd)
when nimvm:
# Cache results for speedup if cache = true
# Else cache for preserving functionality in nimsuggest and nimcheck
let
hash = (ccmd & cacheKey).hash().abs()
cachePath = getNimteropCacheDir() / "execCache" / "nimterop_" & $hash
cacheFile = cachePath & ".txt"
retFile = cachePath & "_ret.txt"
when defined(nimsuggest) or defined(nimcheck):
# Load results from cache file if generated in previous run
if fileExists(cacheFile) and fileExists(retFile):
result.output = cacheFile.readFile()
result.ret = retFile.readFile().parseInt()
elif die:
doAssert false, "Results not cached - run nim c/cpp at least once\n" & ccmd
else:
if cache and fileExists(cacheFile) and fileExists(retFile) and not compileOption("forceBuild"):
# Return from cache when requested
result.output = cacheFile.readFile()
result.ret = retFile.readFile().parseInt()
else:
# Execute command and store results in cache
(result.output, result.ret) = gorgeEx(ccmd)
if result.ret == 0 or die == false:
# mkdir for execCache dir (circular dependency)
let dir = cacheFile.parentDir()
if not dirExists(dir):
let flag = when not defined(Windows): "-p" else: ""
discard execAction(&"mkdir {flag} {dir.sanitizePath}")
cacheFile.writeFile(result.output)
retFile.writeFile($result.ret)
else:
# Used by toast
(result.output, result.ret) = execCmdEx(ccmd)
# On failure, retry or die as requested
if result.ret != 0:
if retry > 0:
if not onRetry.isNil:
onRetry()
sleep(500)
result = execAction(cmd, retry = retry - 1, die, cache, cacheKey)
else:
if not onError.isNil:
onError(result.output, result.ret)
doAssert not die, "Command failed: " & $result.ret & "\ncmd: " & ccmd &
"\nresult:\n" & result.output
when not defined(TOAST):
proc findExe*(exe: string): string =
## Find the specified executable using the `which`/`where` command - supported
## at compile time
var
cmd =
when defined(Windows):
"where " & exe
else:
"which " & exe
(output, ret) = execAction(cmd, die = false)
if ret == 0:
return output.splitLines()[0].strip().sanitizePath
else:
export findExe
proc mkDir*(dir: string) =
## Create a directory at compile time
##
## The `os` module is not available at compile time so a few
## crucial helper functions are included with nimterop.
if not dirExists(dir):
let
flag = when not defined(Windows): "-p" else: ""
discard execAction(&"mkdir {flag} {dir.sanitizePath}", retry = 2)
proc cpFile*(source, dest: string, psymlink = false, move = false) =
## Copy a file from `source` to `dest` at compile time
##
## `psymlink = true` preserves symlinks instead of dereferencing on posix
let
source = source.replace("/", $DirSep)
dest = dest.replace("/", $DirSep)
cmd =
when defined(Windows):
if move:
"move /y"
else:
"copy /y"
else:
if move:
"mv -f"
else:
if psymlink:
"cp -fa"
else:
"cp -f"
discard execAction(&"{cmd} {source.sanitizePath} {dest.sanitizePath}", retry = 2)
proc mvFile*(source, dest: string) =
## Move a file from `source` to `dest` at compile time
cpFile(source, dest, move=true)
proc rmFile*(source: string, dir = false) =
## Remove a file or pattern at compile time
let
source = source.replace("/", $DirSep)
cmd =
when defined(Windows):
if dir:
"rd /s/q"
else:
"del /q/f"
else:
"rm -rf"
exists =
if dir:
dirExists(source)
else:
fileExists(source)
if exists:
discard execAction(&"{cmd} {source.sanitizePath}", retry = 2)
proc rmDir*(dir: string) =
## Remove a directory or pattern at compile time
rmFile(dir, dir = true)
proc cleanDir*(dir: string) =
## Remove all contents of a directory at compile time
for kind, path in walkDir(dir):
if kind == pcDir:
rmDir(path)
else:
rmFile(path)
proc cpTree*(source, dest: string, move = false) =
## Copy contents of source dir to the destination, not the directory itself
for kind, path in walkDir(source, relative = true):
if kind == pcDir:
cpTree(source / path, dest / path, move)
if move:
rmDir(source / path)
else:
if not dirExists(dest):
mkDir(dest)
if move:
mvFile(source / path, dest / path)
else:
cpFile(source / path, dest / path)
proc mvTree*(source, dest: string) =
## Move contents of source dir to the destination, not the directory itself
cpTree(source, dest, move = true)
proc getFileDate*(fullpath: string): string =
## Get file date for `fullpath`
var
ret = 0
cmd =
when defined(Windows):
let
(head, tail) = fullpath.splitPath()
&"forfiles /P {head.sanitizePath()} /M {tail.sanitizePath} /C \"cmd /c echo @fdate @ftime\""
elif defined(Linux):
&"stat -c %Y {fullpath.sanitizePath}"
elif defined(OSX) or defined(FreeBSD):
&"stat -f %m {fullpath.sanitizePath}"
(result, ret) = execAction(cmd, die=false)
proc touchFile*(fullpath: string) =
## Touch file to update modified date
var
cmd =
when defined(Windows):
&"cmd /c copy /b {fullpath.sanitizePath}+"
else:
&"touch {fullpath.sanitizePath}"
discard execAction(cmd)
proc extractZip*(zipfile, outdir: string, quiet = false) =
## Extract a zip file using `powershell` on Windows and `unzip` on other
## systems to the specified output directory
var cmd = "unzip -o $#"
if defined(Windows):
cmd = "powershell -nologo -noprofile -command \"& { Add-Type -A " &
"'System.IO.Compression.FileSystem'; " &
"[IO.Compression.ZipFile]::ExtractToDirectory('$#', '.'); }\""
if not quiet:
gecho "# Extracting " & zipfile
discard execAction(&"cd {outdir.sanitizePath} && {cmd % zipfile}")
proc extractTar*(tarfile, outdir: string, quiet = false) =
## Extract a tar file using `tar`, `7z` or `7za` to the specified output directory
var
cmd = ""
name = ""
if findExe("tar").len != 0:
let
ext = tarfile.splitFile().ext.toLowerAscii()
typ =
case ext
of ".gz", ".tgz": "z"
of ".xz": "J"
of ".bz2": "j"
else: ""
cmd = "tar xvf" & typ & " " & tarfile.sanitizePath
else:
for i in ["7z", "7za"]:
if findExe(i).len != 0:
cmd = i & " x $#" % tarfile.sanitizePath
name = tarfile.splitFile().name
if ".tar" in name.toLowerAscii():
cmd &= " && " & i & " x $#" % name.sanitizePath
break
doAssert cmd.len != 0, "No extraction tool - tar, 7z, 7za - available for " & tarfile.sanitizePath
if not quiet:
gecho "# Extracting " & tarfile
discard execAction(&"cd {outdir.sanitizePath} && {cmd}")
if name.len != 0:
rmFile(outdir / name)
proc downloadUrl*(url, outdir: string, quiet = false, retry = 1) =
## Download a file using `curl` or `wget` (or `powershell` on Windows) to the specified directory
##
## If an archive file, it is automatically extracted after download.
let
file = url.extractFilename()
filePath = outdir / file
ext = file.splitFile().ext.toLowerAscii()
archives = @[".zip", ".xz", ".gz", ".bz2", ".tgz", ".tar"]
if not (ext in archives and fileExists(filePath)):
if not quiet:
gecho "# Downloading " & file
mkDir(outdir)
var cmd = findExe("curl")
if cmd.len != 0:
cmd &= " -Lk $# -o $#"
else:
cmd = findExe("wget")
if cmd.len != 0:
cmd &= " $# -O $#"
elif defined(Windows):
cmd = "powershell [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; wget $# -OutFile $#"
else:
doAssert false, "No download tool available - curl, wget"
discard execAction(cmd % [url.quoteShell, (filePath).sanitizePath], retry = 3,
onRetry = proc() = rmFile(filePath))
if ext == ".zip":
extractZip(file, outdir, quiet)
elif ext in archives:
extractTar(file, outdir, quiet)
proc gitReset*(outdir: string) =
## Hard reset the git repository at the specified directory
gecho "# Resetting " & outdir
let cmd = &"cd {outdir.sanitizePath} && git reset --hard"
while execAction(cmd).output.contains("Permission denied"):
sleep(1000)
gecho "# Retrying ..."
proc gitCheckout*(file, outdir: string) =
## Checkout the specified `file` in the git repository at `outdir`
##
## This effectively resets all changes in the file and can be
## used to undo any changes that were made to source files to enable
## successful wrapping with `cImport()` or `c2nImport()`.
gecho "# Resetting " & file
let file2 = file.relativePath outdir
let cmd = &"cd {outdir.sanitizePath} && git checkout {file2.sanitizePath}"
while execAction(cmd).output.contains("Permission denied"):
sleep(500)
gecho "# Retrying ..."
proc gitAtCheckout*(outdir, checkout: string): bool =
## Check if specified git repository is checked out to the specified
## commit hash, tag or branch
result = checkout in execAction(
&"cd {outdir.sanitizePath} && git log --decorate --no-color -n 1 --format=oneline").output
proc gitDefaultBranch*(outdir: string): string =
## Get the default branch for a git repository before it is pulled
result = "master"
let
output = execAction(
&"cd {outdir.sanitizePath} && git remote show origin"
).output
for line in output.splitLines():
if "HEAD branch: " in line:
result = line.split("branch: ")[1].strip()
proc gitPull*(url: string, outdir = "", plist = "", checkout = "", quiet = false) =
## Pull the specified git repository to the output directory
##
## `plist` is the list of specific files and directories or wildcards
## to sparsely checkout. Multiple values can be specified one entry per
## line. It is optional and if omitted, the entire repository will be
## checked out.
##
## `checkout` is the git tag, branch or commit hash to checkout once
## the repository is downloaded. This allows for pinning to a specific
## version of the code.
let
outdirQ = outdir.sanitizePath
if dirExists(outdir/".git"):
gitReset(outdir)
if checkout.nBl and not gitAtCheckout(outdir, checkout):
gecho &"# Updating repository to checkout {checkout}"
discard execAction(
&"cd {outdirQ} && git clean -fxd && git fetch && git checkout {checkout}", retry = 3)
return
mkDir(outdir)
if not quiet:
gecho "# Setting up Git repo: " & url
discard execAction(&"cd {outdirQ} && git init .")
discard execAction(&"cd {outdirQ} && git remote add origin {url}")
if plist.len != 0:
# If a specific list of files is required, create a sparse checkout
# file for git in its config directory
let sparsefile = outdir / ".git/info/sparse-checkout"
discard execAction(&"cd {outdirQ} && git config core.sparsecheckout true")
writeFile(sparsefile, plist)
# In case directory has old files from another run
discard execAction(&"cd {outdirQ} && git clean -fxd")
# Checkout specified branch/tag/commit or default branch - typically master
let
checkout = if checkout.Bl: gitDefaultBranch(outdir) else: checkout
if not quiet:
gecho "# Checking out " & checkout
discard execAction(&"cd {outdirQ} && git fetch", retry = 3)
discard execAction(&"cd {outdirQ} && git checkout {checkout}")
proc gitTags*(outdir: string): seq[string] =
## Get all the git tags in the specified directory
let
cmd = &"cd {outdir.sanitizePath} && git tag"
tags = execAction(cmd).output.splitLines()
for tag in tags:
let
tag = tag.strip()
if tag.len != 0:
result.add tag
proc loafExePath(): string =
currentSourcePath.parentDir.parentDir / ("loaf".addFileExt ExeExt)
proc findFiles*(file: string, dir: string, recurse = true, regex = false): seq[string] =
## Find all matching files in the specified directory
##
## `file` is a regular expression if `regex` is true
##
## Turn off recursive search with `recurse`
let
loafExe = loafExePath()
doAssert fileExists(loafExe), "loaf not compiled: " & loafExe.sanitizePath &
" make sure 'nimble build' or 'nimble install' built it"
var
cmd = loafExe.quoteShell & " find --rexp $1 \"$2\" $3"
recursive = if recurse: "--recurse" else: ""
var
dir = dir
file = file
# If file = `path/file`, adjust dir = `dir/path` and search for new file
if not (recurse or regex):
let
pdir = file.parentDir()
if pdir.len != 0:
dir = dir / pdir
file = file.extractFilename
cmd = cmd % [recursive, (".*[\\\\/]" & file & "$"), dir.sanitizePath]
let
(files, ret) = execAction(cmd, die = false)
if ret == 0:
for line in files.splitLines():
if line.len != 0:
result.add line
proc findFile*(file: string, dir: string, recurse = true, first = false, regex = false): string =
## Find the file in the specified directory
##
## `file` is a regular expression if `regex` is true
##
## Turn off recursive search with `recurse` and stop on first match with
## `first`. Without it, the shortest match is returned.
let
matches = findFiles(file, dir, recurse, regex)
for match in matches:
if (result.len == 0 or result.len > match.len):
result = match
if first: break
proc linkLibs*(names: openArray[string], staticLink = true): string =
## Create linker flags for specified libraries using pkg-config
##
## Prepends `lib` to the name so you only need `ssl` for `libssl`.
var
stat = if staticLink: "--static" else: ""
resSet: OrderedSet[string]
cmd = &"pkg-config --libs --silence-errors {stat}"
resSet.init()
for name in names:
for n in ["lib" & name, name]:
# Try libname and name - e.g. MagickWand doesn't have lib
let
cmd = &"{cmd} {n}"
(libs, _) = execAction(cmd, die = false)
if libs.len != 0:
for lib in libs.split(" "):
resSet.incl lib
break
if staticLink:
resSet.incl "--static"
for res in resSet:
result &= " " & res
proc getNumProcs*(): string =
## Get number of processors
when defined(Windows):
getEnv("NUMBER_OF_PROCESSORS").strip()
elif defined(linux):
execAction("nproc").output.strip()
elif defined(macosx) or defined(FreeBSD):
execAction("sysctl -n hw.ncpu").output.strip()
else:
"1"
proc getProjectCacheDir*(name: string, forceClean = true): string =
## Get a cache directory where all nimterop artifacts can be stored
##
## Projects can use this location to download source code and build binaries
## that can be then accessed by multiple apps. This is created under the
## per-user Nim cache directory.
##
## Use `name` to specify the subdirectory name for a project.
##
## `forceClean` is enabled by default and effectively deletes the folder
## if Nim is compiled with the `-f` or `--forceBuild` flag. This allows
## any project to start out with a clean cache dir on a forced build.
##
## NOTE: avoid calling `getProjectCacheDir()` multiple times on the same
## `name` when `forceClean = true` else checked out source might get deleted
## at the wrong time during build.
##
## E.g.
## `nimgit2` downloads `libgit2` source so `name = "libgit2"`
##
## `nimarchive` downloads `libarchive`, `bzlib`, `liblzma` and `zlib` so
## `name = "nimarchive" / "libarchive"` for `libarchive`, etc.
result = getNimteropCacheDir() / name
if forceClean and compileOption("forceBuild"):
gecho "# Removing " & result
rmDir(result)

256
nimterop/build/tools.nim Normal file
View file

@ -0,0 +1,256 @@
import strformat, strutils
import os except findExe
import ".."/globals
import "."/[misc, shell]
proc configure*(path, check: string, flags = "") =
## Run the GNU `configure` command to generate all Makefiles or other
## build scripts in the specified path
##
## If a `configure` script is not present and an `autogen.sh` script
## is present, it will be run before attempting `configure`.
##
## Next, if `configure.ac` or `configure.in` exist, `autoreconf` will
## be executed.
##
## `check` is a file that will be generated by the `configure` command.
## This is required to prevent configure from running on every build. It
## is relative to the `path` and should not be an absolute path.
##
## `flags` are any flags that should be passed to the `configure` command.
if (path / check).fileExists():
return
gecho "# Configuring " & path
if not fileExists(path / "configure"):
for i in @["autogen.sh", "build" / "autogen.sh"]:
if fileExists(path / i):
gecho "# Running autogen.sh"
when defined(unix):
decho execAction(
&"cd {(path / i).parentDir().sanitizePath} && ./autogen.sh").output
else:
decho execAction(
&"cd {(path / i).parentDir().sanitizePath} && bash ./autogen.sh").output
break
if not fileExists(path / "configure"):
for i in @["configure.ac", "configure.in"]:
if fileExists(path / i):
gecho "# Running autoreconf"
decho execAction(&"cd {path.sanitizePath} && autoreconf -fi").output
break
if fileExists(path / "configure"):
gecho "# Running configure " & flags
when defined(unix):
var
cmd = &"cd {path.sanitizePath} && ./configure"
else:
var
cmd = &"cd {path.sanitizePath} && bash ./configure"
if flags.len != 0:
cmd &= &" {flags}"
decho execAction(cmd).output
doAssert (path / check).fileExists(), "Configure failed"
proc getCmakePropertyStr(name, property, value: string): string =
&"\nset_target_properties({name} PROPERTIES {property} \"{value}\")\n"
proc getCmakeIncludePath*(paths: openArray[string]): string =
## Create a `cmake` flag to specify custom include paths
##
## Result can be included in the `flag` parameter for `cmake()` or
## the `cmakeFlags` parameter for `getHeader()`.
for path in paths:
result &= path & ";"
result = " -DCMAKE_INCLUDE_PATH=" & result[0 .. ^2].sanitizePath(sep = "/")
proc setCmakeProperty*(outdir, name, property, value: string) =
## Set a `cmake` property in `outdir / CMakeLists.txt` - usable in the `xxxPreBuild` hook
## for `getHeader()`
##
## `set_target_properties(name PROPERTIES property "value")`
let
cm = outdir / "CMakeLists.txt"
if cm.fileExists():
cm.writeFile(
cm.readFile() & getCmakePropertyStr(name, property, value)
)
proc setCmakeLibName*(outdir, name, prefix = "", oname = "", suffix = "") =
## Set a `cmake` property in `outdir / CMakeLists.txt` to specify a custom library output
## name - usable in the `xxxPreBuild` hook for `getHeader()`
##
## `prefix` is typically `lib`
## `oname` is the library name
## `suffix` is typically `.a`
##
## Sometimes, `cmake` generates non-standard library names - e.g. zlib compiles to
## `libzlibstatic.a` on Windows. This proc can help rename it to `libzlib.a` so that `getHeader()`
## can find it after the library is compiled.
##
## ```
## set_target_properties(name PROPERTIES PREFIX "prefix")
## set_target_properties(name PROPERTIES OUTPUT_NAME "oname")
## set_target_properties(name PROPERTIES SUFFIX "suffix")
## ```
let
cm = outdir / "CMakeLists.txt"
if cm.fileExists():
var
str = ""
if prefix.len != 0:
str &= getCmakePropertyStr(name, "PREFIX", prefix)
if oname.len != 0:
str &= getCmakePropertyStr(name, "OUTPUT_NAME", oname)
if suffix.len != 0:
str &= getCmakePropertyStr(name, "SUFFIX", suffix)
if str.len != 0:
cm.writeFile(cm.readFile() & str)
proc setCmakePositionIndependentCode*(outdir: string) =
## Set a `cmake` directive to create libraries with -fPIC enabled
let
cm = outdir / "CMakeLists.txt"
if cm.fileExists():
let
pic = "set(CMAKE_POSITION_INDEPENDENT_CODE ON)"
cmd = cm.readFile()
if not cmd.contains(pic):
cm.writeFile(
pic & "\n" & cmd
)
proc cmake*(path, check, flags: string) =
## Run the `cmake` command to generate all Makefiles or other
## build scripts in the specified path
##
## `path` will be created since typically `cmake` is run in an
## empty directory.
##
## `check` is a file that will be generated by the `cmake` command.
## This is required to prevent `cmake` from running on every build. It
## is relative to the `path` and should not be an absolute path.
##
## `flags` are any flags that should be passed to the `cmake` command.
## Unlike `configure`, it is required since typically it will be the
## path to the repository, typically `..` when `path` is a subdir.
if (path / check).fileExists():
return
gecho "# Running cmake " & flags
gecho "# Path: " & path
mkDir(path)
let
cmd = &"cd {path.sanitizePath} && cmake {flags}"
decho execAction(cmd).output
doAssert (path / check).fileExists(), "cmake failed"
proc make*(path, check: string, flags = "", regex = false) =
## Run the `make` command to build all binaries in the specified path
##
## `check` is a file that will be generated by the `make` command.
## This is required to prevent `make` from running on every build. It
## is relative to the `path` and should not be an absolute path.
##
## `flags` are any flags that should be passed to the `make` command.
##
## `regex` can be set to true if `check` is a regular expression.
##
## If `make.exe` is missing and `mingw32-make.exe` is available, it will
## be copied over to make.exe in the same location.
if findFile(check, path, regex = regex).len != 0:
return
gecho "# Running make " & flags
gecho "# Path: " & path
var
cmd = findExe("make")
if cmd.len == 0:
cmd = findExe("mingw32-make")
if cmd.len != 0:
cpFile(cmd, cmd.replace("mingw32-make", "make"))
doAssert cmd.len != 0, "Make not found"
cmd = &"cd {path.sanitizePath} && make -j {getNumProcs()}"
if flags.len != 0:
cmd &= &" {flags}"
decho execAction(cmd).output
doAssert findFile(check, path, regex = regex).len != 0, "make failed"
proc buildWithCmake*(outdir, flags: string): BuildStatus =
if not fileExists(outdir / "Makefile"):
if fileExists(outdir / "CMakeLists.txt"):
if findExe("cmake").len != 0:
var
gen = ""
when defined(Windows):
if findExe("sh").len != 0:
let
uname = execAction("sh -c uname -a").output.toLowerAscii()
if uname.contains("msys"):
gen = "MSYS Makefiles".quoteShell
elif uname.contains("mingw"):
gen = "MinGW Makefiles".quoteShell & " -DCMAKE_SH=\"CMAKE_SH-NOTFOUND\""
else:
gecho "Unsupported system: " & uname
else:
gen = "MinGW Makefiles".quoteShell
else:
gen = "Unix Makefiles".quoteShell
if findExe("ccache").len != 0:
gen &= " -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache"
result.buildPath = outdir / "buildcache"
cmake(result.buildPath, "Makefile", &".. -G {gen} {flags}")
result.built = true
else:
result.error = "cmake capable but cmake executable missing"
else:
result.buildPath = outdir
proc buildWithAutoConf*(outdir, flags: string): BuildStatus =
if not fileExists(outdir / "Makefile"):
if findExe("bash").len != 0:
for file in @["configure", "configure.ac", "configure.in", "autogen.sh", "build/autogen.sh"]:
if fileExists(outdir / file):
configure(outdir, "Makefile", flags)
result.buildPath = outdir
result.built = true
break
else:
result.error = "configure capable but bash executable missing"
else:
result.buildPath = outdir
proc flagBuild*(base: string, flags: openArray[string]): string =
## Simple helper proc to generate flags for `configure`, `cmake`, etc.
##
## Every entry in `flags` is replaced into the `base` string and
## concatenated to the result.
##
## E.g.
## `base = "--disable-$#"`
## `flags = @["one", "two"]`
##
## `flagBuild(base, flags) => " --disable-one --disable-two"`
for i in flags:
result &= " " & base % i

778
nimterop/cimport.nim Normal file
View file

@ -0,0 +1,778 @@
import hashes, macros, os, strformat, strutils
import "."/[globals, paths]
import "."/build/[ccompiler, misc, nimconf, shell]
proc findPath(path: string, fail = true): string =
# Relative to project path
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 getCacheValue(fullpath: string): string =
if not gStateCT.nocache:
result = fullpath.getFileDate()
proc getCacheValue(fullpaths: seq[string]): string =
if not gStateCT.nocache:
for fullpath in fullpaths:
result &= getCacheValue(fullpath)
proc getNimCheckError(nimFile: string) =
let
(check, _) = execAction(
&"{getCurrentNimCompiler()} check {nimFile.sanitizePath}",
die = false
)
doAssert false, &"\n\n{check}\n\n" &
"Codegen limitation or error - review 'nim check' output above generated for " & nimFile
proc getToast(fullpaths: seq[string], recurse: bool = false, dynlib: string = "",
mode = "c", flags = "", outFile = "", noNimout = false): string =
var
cmd = when defined(Windows): "cmd /c " else: ""
ext = "h"
let
toastExe = toastExePath()
# see https://github.com/nimterop/nimterop/issues/69
cacheKey = getCacheValue(toastExe) & getCacheValue(fullpaths)
doAssert fileExists(toastExe), "toast not compiled: " & toastExe.sanitizePath &
" make sure 'nimble build' or 'nimble install' built it"
cmd &= &"{toastExe} --preprocess -m:{mode}"
if recurse:
cmd.add " --recurse"
if flags.nBl:
cmd.add " " & flags
for i in gStateCT.defines:
cmd.add &" --defines+={i.quoteShell}"
for i in gStateCT.includeDirs:
cmd.add &" --includeDirs+={i.sanitizePath}"
for i in gStateCT.exclude:
cmd.add &" --exclude+={i.sanitizePath}"
for i in gStateCT.passC:
cmd.add &" --passC+={i.quoteShell}"
gStateCT.passC = @[]
for i in gStateCT.passL:
cmd.add &" --passL+={i.quoteShell}"
gStateCT.passL = @[]
for i in gStateCT.compile:
cmd.add &" --compile+={i.sanitizePath}"
gStateCT.compile = @[]
if not noNimout:
cmd.add &" --pnim"
if dynlib.nBl:
cmd.add &" --dynlib={dynlib}"
if gStateCT.symOverride.nBl:
cmd.add &" --symOverride={gStateCT.symOverride.join(\",\")}"
cmd.add &" --nim:{getCurrentNimCompiler().sanitizePath}"
if gStateCT.pluginSourcePath.nBl:
cmd.add &" --pluginSourcePath={gStateCT.pluginSourcePath.sanitizePath}"
ext = "nim"
for fullpath in fullpaths:
cmd.add &" {fullpath.sanitizePath}"
let
cacheFile = getNimteropCacheDir() / "toastCache" / "nimterop_" &
($(cmd & cacheKey).hash().abs()).addFileExt(ext)
if outFile.nBl:
result = fixRelPath(outFile)
else:
result = cacheFile
when defined(Windows):
result = result.replace(DirSep, '/')
let
# When to regenerate the wrapper
regen =
if gStateCT.nocache or compileOption("forceBuild"):
# No caching or forced
true
elif not fileExists(result):
# Cache or outfile doesn't exist
true
elif outFile.nBl and (not fileExists(cacheFile) or
result.getFileDate() > cacheFile.getFileDate()):
# Outfile exists but cache doesn't or outdated
true
else:
false
if regen:
let
dir = result.parentDir()
if not dirExists(dir):
mkDir(dir)
cmd.add &" -o {result.sanitizePath}"
var
(output, ret) = execAction(cmd, die = false)
if ret != 0:
# If toast fails, print failure to output and delete any generated files
let errout = if result.fileExists(): result.readFile() & output else: output
rmFile(result)
doAssert false, "\n\n" & errout & "\n"
# Write empty cache file to track changes when outFile specified
if outFile.nBl:
let dir = cacheFile.parentDir()
if not dirExists(dir):
mkdir(dir)
writeFile(cacheFile, "")
proc cDebug*() {.compileTime.} =
## Enable debug messages and display the generated Nim code
gStateCT.debug = true
proc cDisableCaching*() {.compileTime.} =
## Disable caching of generated Nim code - useful during wrapper development
##
## If files included by header being processed by
## `cImport()` change and affect the generated content, they will be ignored
## and the cached value will continue to be used . Use `cDisableCaching()` to
## avoid this scenario during development.
##
## `nim -f` can also be used to flush the cached content.
gStateCT.nocache = true
proc cSearchPath*(path: string): string {.compileTime.} =
## Get full path to file or directory `path` in search path configured
## using `cAddSearchDir()` and `cAddStdDir()`.
##
## This can be used to locate files or directories that can be passed onto
## `cCompile()`, `cIncludeDir()` and `cImport()`.
result = findPath(path, fail = false)
if result.Bl:
var found = false
for inc in gStateCT.searchDirs:
result = findPath(inc / path, fail = false)
if result.nBl:
found = true
break
doAssert found, "File or directory not found: " & path &
" gStateCT.searchDirs: " & $gStateCT.searchDirs
proc cAddSearchDir*(dir: string) {.compileTime.} =
## Add directory `dir` to the search path used in calls to
## `cSearchPath()`.
runnableExamples:
import nimterop/paths, os
static:
cAddSearchDir testsIncludeDir()
doAssert cSearchPath("test.h").fileExists
if dir notin gStateCT.searchDirs:
gStateCT.searchDirs.add(dir)
proc cAddStdDir*(mode = "c") {.compileTime.} =
## Add the standard `c` [default] or `cpp` include paths to search
## path used in calls to `cSearchPath()`.
runnableExamples:
import os
static:
cAddStdDir()
doAssert cSearchPath("math.h").fileExists
for inc in getGccPaths(mode):
cAddSearchDir inc
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".}`
##
## This needs to be called before `cImport()` to take effect.
var str = name
if val.nBl:
str &= &"={val.quoteShell}"
if str notin gStateCT.defines:
gStateCT.defines.add(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".}`
##
## This needs to be called before `cImport()` to take effect.
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`)
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.
##
## This needs to be called before `cImport()` to take effect.
for dir in dirs:
let fullpath = findPath(dir)
if fullpath notin gStateCT.includeDirs:
gStateCT.includeDirs.add(fullpath)
if exclude:
gStateCT.exclude.add(fullpath)
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".}`.
##
## Set `exclude = true` if the contents of this include directory should
## not be included in the wrapped output.
##
## This needs to be called before `cImport()` to take effect.
return quote do:
cIncludeDir(@[`dir`], `exclude` == 1)
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`])
macro cPassC*(value: static string): untyped =
## Create a `{.passC.}` entry that gets forwarded to the C/C++ compiler
## during Nim compilation.
##
## `cPassC()` needs to be called before `cImport()` to take effect and gets
## consumed and reset so as not to impact subsequent `cImport()` calls.
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.
##
## `cPassL()` needs to be called before `cImport()` to take effect and gets
## consumed and reset so as not to impact subsequent `cImport()` calls.
gStateCT.passL.add value
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 `*` wildcard for filename:
##
## .. code-block:: nim
##
## cCompile("file.c")
## cCompile("path/to/*.c")
##
## `mode` recursively searches for code files in `path`.
##
## `c` searches for `*.c` whereas `cpp` searches for `*.C *.cpp *.c++ *.cc *.cxx`
##
## .. code-block:: nim
##
## cCompile("path/to/dir", "cpp")
##
## `exclude` can be used to exclude files by partial string match. Comma separated to
## specify multiple exclude strings
##
## .. code-block:: nim
##
## cCompile("path/to/dir", exclude="test2.c")
##
## `cCompile()` needs to be called before `cImport()` to take effect and gets
## consumed and reset so as not to impact subsequent `cImport()` calls.
proc fcompile(file: string) =
let
(_, fn, ext) = file.splitFile()
var
ufn = fn
uniq = 1
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.compcache.add(ufn)
if fn == ufn:
gStateCT.compile.add file.replace("\\", "/")
else:
# - https://github.com/nim-lang/Nim/issues/9370
let
hash = file.hash().abs()
tmpFile = file.parentDir() / &"_nimterop_{$hash}_{ufn}{ext}"
if not tmpFile.fileExists() or file.getFileDate() > tmpFile.getFileDate():
cpFile(file, tmpFile)
gStateCT.compile.add tmpFile.replace("\\", "/")
# Due to https://github.com/nim-lang/Nim/issues/9863
# cannot use seq[string] for excludes
proc notExcluded(file, exclude: string): bool =
result = true
if "_nimterop_" in file:
result = false
elif exclude.nBl:
for excl in exclude.split(","):
if excl in file:
result = false
proc dcompile(dir, exclude: string, ext="") =
let
(dir, pat) =
if "*" in dir:
dir.splitPath()
else:
(dir, "")
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 "*" in path:
dcompile(path, exclude)
else:
let fpath = findPath(path)
if fileExists(fpath) and fpath.notExcluded(exclude):
fcompile(fpath)
elif dirExists(fpath):
if mode.contains("cpp"):
for i in @[".cpp", ".c++", ".cc", ".cxx"]:
dcompile(fpath, exclude, i)
when not defined(Windows):
dcompile(fpath, exclude, ".C")
else:
dcompile(fpath, exclude, ".c")
macro renderPragma*(): untyped =
## All `cDefine()`, `cIncludeDir()`, `cCompile()`, `cPassC()` and `cPassL()`
## content typically gets forwarded via `cImport()` to the generated wrapper to be
## rendered as part of the output so as to enable standalone wrappers. If `cImport()`
## is not being used for some reason, `renderPragma()` can create these pragmas
## in the nimterop wrapper itself. A good example is using `getHeader()` without
## calling `cImport()`.
##
## `c2nImport()` already uses this macro so there's no need to use it when typically
## wrapping headers.
result = newNimNode(nnkStmtList)
for i in gStateCT.defines:
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`.}
gStateCT.passC = @[]
for i in gStateCT.passL:
result.add quote do:
{.passL: `i`.}
gStateCT.passL = @[]
for i in gStateCT.compile:
result.add quote do:
{.compile: `i`.}
gStateCT.compile = @[]
proc cSkipSymbol*(skips: seq[string]) {.compileTime.} =
## Similar to `cOverride()`, this macro allows filtering out symbols not of
## interest from the generated output.
##
## `cSkipSymbol()` only affects calls to `cImport()` that follow it.
runnableExamples:
static: cSkipSymbol @["proc1", "Type2"]
gStateCT.symOverride.add skips
macro cOverride*(body): untyped =
## When the wrapper code generated by nimterop is missing certain symbols or not
## accurate, it may be required to hand wrap them. Define them in a `cOverride()`
## macro block so that Nimterop uses these definitions instead.
##
## For example:
##
## .. code-block:: c
##
## int svGetCallerInfo(const char** fileName, int *lineNumber);
##
## This might map to:
##
## .. code-block:: nim
##
## proc svGetCallerInfo(fileName: ptr cstring; lineNumber: var cint)
##
## Whereas it might mean:
##
## .. code-block:: nim
##
## cOverride:
## proc svGetCallerInfo(fileName: var cstring; lineNumber: var cint)
##
## Using the `cOverride()` block, nimterop can be instructed to use this
## definition of `svGetCallerInfo()` instead. This works for procs, consts
## and types.
##
## `cOverride()` only affects the next `cImport()` call. This is because any
## recognized symbols get overridden in place and any remaining symbols get
## added to the top. If reused, the next `cImport()` would add those symbols
## again leading to redefinition errors.
iterator findOverrides(node: NimNode): tuple[name, override: string, kind: NimNodeKind] =
for child in node:
case child.kind
of nnkTypeSection, nnkConstSection:
# Types, const
for inst in child:
let name =
if inst[0].kind == nnkPragmaExpr:
$inst[0][0]
else:
$inst[0]
yield (name.strip(chars={'*'}), inst.repr, child.kind)
of nnkProcDef:
let
name = $child[0]
yield (name.strip(chars={'*'}), child.repr, child.kind)
else:
discard
if gStateCT.overrides.Bl:
gStateCT.overrides = """
import sets, tables
proc onSymbolOverride*(sym: var Symbol) {.exportc, dynlib.} =
"""
# If cPlugin called before cOverride
if gStateCT.pluginSourcePath.nBl:
gStateCT.pluginSourcePath = ""
var
names: seq[string]
for name, override, kind in body.findOverrides():
let
typ =
case kind
of nnkTypeSection: "nskType"
of nnkConstSection: "nskConst"
of nnkProcDef: "nskProc"
else: ""
gStateCT.overrides &= &"""
if sym.name == "{name}" and sym.kind == {typ} and "{name}" in cOverrides["{typ}"]:
sym.override = """ & "\"\"\"" & override & "\"\"\"\n"
gStateCT.overrides &= &" cOverrides[\"{typ}\"].excl \"{name}\"\n"
gStateCT.overrides = gStateCT.overrides.replace("proc onSymbolOverride",
&"cOverrides[\"{typ}\"].incl \"{name}\"\nproc onSymbolOverride")
names.add name
gStateCT.symOverride.add name
if names.nBl:
decho "Overriding " & names.join(" ")
proc cPluginHelper(body: string, imports = "import macros, nimterop/plugin\n\n") =
gStateCT.pluginSource = body
if gStateCT.pluginSource.nBl or gStateCT.overrides.nBl:
let
data = imports & body & "\n\n" & gStateCT.overrides
hash = data.hash().abs()
path = getProjectCacheDir("cPlugins", forceClean = false) / "nimterop_" & $hash & ".nim"
if not fileExists(path) or gStateCT.nocache or compileOption("forceBuild"):
mkDir(path.parentDir())
writeFile(path, data)
writeNimConfig(path & ".cfg")
doAssert fileExists(path), "Unable to write plugin file: " & path
gStateCT.pluginSourcePath = path
macro cPlugin*(body): untyped =
## When `cOverride()` and `cSkipSymbol()` are not adequate, the `cPlugin()`
## macro can be used to customize the generated Nim output. The following
## callbacks are available at this time.
##
## .. code-block:: nim
##
## proc onSymbol(sym: var Symbol) {.exportc, dynlib.}
##
## `onSymbol()` can be used to handle symbol name modifications required due
## to invalid characters in identifiers or to rename symbols that would clash
## due to Nim's style insensitivity. The symbol name and type is provided to
## the callback and the name can be modified.
##
## While `cPlugin` can easily remove leading/trailing `_` or prefixes and
## suffixes like `SDL_`, passing `--prefix` or `--suffix` flags to `cImport`
## in the `flags` parameter is much easier. However, these flags will only be
## considered when no `cPlugin` is specified.
##
## Returning a blank name will result in the symbol being skipped. This will
## fail for `nskParam` and `nskField` since the generated Nim code will be wrong.
##
## Symbol types can be any of the following:
## - `nskConst` for constants
## - `nskType` for type identifiers, including primitive
## - `nskParam` for param names
## - `nskField` for struct field names
## - `nskEnumField` for enum (field) names, though they are in the global namespace as `nskConst`
## - `nskProc` - for proc names
##
## `macros` and `nimterop/plugins` are implicitly imported to provide access to standard
## plugin facilities.
##
## `cPlugin()` only affects calls to `cImport()` that follow it.
runnableExamples:
cPlugin:
import strutils
# Strip leading and trailing underscores
proc onSymbol*(sym: var Symbol) {.exportc, dynlib.} =
sym.name = sym.name.strip(chars={'_'})
runnableExamples:
cPlugin:
import strutils
# Strip prefix from procs
proc onSymbol*(sym: var Symbol) {.exportc, dynlib.} =
if sym.kind == nskProc and sym.name.contains("SDL_"):
sym.name = sym.name.replace("SDL_", "")
cPluginHelper(body.repr)
macro cPluginPath*(path: static[string]): untyped =
## Rather than embedding the `cPlugin()` code within the wrapper, it might be
## preferable to have it stored in a separate source file. This allows for reuse
## across multiple wrappers when applicable.
##
## The `cPluginPath()` macro enables this functionality - specify the path to the
## plugin file and it will be consumed in the same way as `cPlugin()`.
##
## `path` is relative to the current dir and not necessarily relative to the
## location of the wrapper file. Use `currentSourcePath` to specify a path relative
## to the wrapper file.
##
## Unlike `cPlugin()`, this macro also does not implicitly import any other modules
## since the standalone plugin file will need explicit imports for `nim check` and
## suggestions to work. `import nimterop/plugin` is required for all plugins.
doAssert fileExists(path), "Plugin file not found: " & path
cPluginHelper(readFile(path), imports = "")
macro cImport*(filenames: static seq[string], recurse: static bool = false, dynlib: static string = "",
mode: static string = "c", flags: static string = "", nimFile: static string = ""): untyped =
## Import multiple headers in one shot
##
## This macro is preferable over multiple individual `cImport()` calls, especially
## when the headers might `#include` the same headers and result in duplicate symbols.
result = newNimNode(nnkStmtList)
var
fullpaths: seq[string]
for filename in filenames:
fullpaths.add findPath(filename)
# In case cOverride called after cPlugin
if gStateCT.pluginSourcePath.Bl:
cPluginHelper(gStateCT.pluginSource)
gecho "# Importing " & fullpaths.join(", ").sanitizePath
let
nimFile = getToast(fullpaths, recurse, dynlib, mode, flags, nimFile)
# Reset plugin and overrides for next cImport
if gStateCT.overrides.nBl:
gStateCT.pluginSourcePath = ""
gStateCT.overrides = ""
if gStateCT.debug:
gecho nimFile.readFile()
gecho "# Saved to " & nimFile
try:
let
nimFileNode = newStrLitNode(nimFile.changeFileExt(""))
result.add quote do:
include `nimFileNode`
except:
getNimCheckError(nimFile)
macro cImport*(filename: static string, recurse: static bool = false, dynlib: static string = "",
mode: static string = "c", flags: static string = "", nimFile: static string = ""): untyped =
## Import all supported definitions from specified header file. Generated
## content is cached in `nimcache` until `filename` changes unless
## `cDisableCaching()` is set. `nim -f` can also be used to flush the cache.
##
## `recurse` can be used to generate Nim wrappers from `#include` files
## referenced in `filename`. This is only done for files in the same
## directory as `filename` or in a directory added using
## `cIncludeDir()`.
##
## `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()`, or the `{.passL.}` pragma can be used to specify the
## static lib to link.
##
## `mode` selects the preprocessor and tree-sitter parser to be used to process
## the header.
##
## `flags` can be used to pass any other command line arguments to `toast`. A
## good example would be `--prefix` and `--suffix` which strip leading and
## trailing strings from identifiers, `_` being quite common.
##
## `nimFile` is the location where the generated wrapper should get written.
## By default, the generated wrapper is written to `nimcache` and included from
## there. `nimFile` makes it possible to write the wrapper to a predetermined
## location which can then be directly imported into the main application and
## checked into source control if preferred. Importing the nimterop wrapper with
## `nimFile` specified still works per usual. If `nimFile` is not an absolute
## path, it is relative to the project path.
##
## `cImport()` consumes and resets preceding `cOverride()` calls. `cPlugin()`
## is retained for the next `cImport()` call unless a new `cPlugin()` call is
## defined.
return quote do:
cImport(@[`filename`], bool(`recurse`), `dynlib`, `mode`, `flags`, `nimFile`)
macro c2nImport*(filename: static string, recurse: static bool = false, dynlib: static string = "",
mode: static string = "c", flags: static string = "", nimFile: static string = ""): untyped =
## Import all supported definitions from specified header file using `c2nim`
##
## Similar to `cImport()` but uses `c2nim` to generate the Nim wrapper instead
## of `toast`. Note that neither `cOverride()`, `cSkipSymbol()` nor `cPlugin()`
## have any impact on `c2nim`.
##
## `toast` is only used to preprocess the header file and `recurse` if specified.
##
## `mode` should be set to `cpp` for c2nim to wrap C++ headers.
##
## `flags` can be used to pass other command line arguments to `c2nim`.
##
## `nimFile` is the location where the generated wrapper should get written,
## similar to `cImport()`.
##
## `nimterop` does not depend on `c2nim` as a `nimble` dependency so it does not
## get installed automatically. Any wrapper or library that requires this proc
## needs to install `c2nim` with `nimble install c2nim` or add it as a dependency
## in its own `.nimble` file.
result = newNimNode(nnkStmtList)
let
fullpath = findPath(filename)
gecho "# Importing " & fullpath & " with c2nim"
let
hFile = getToast(@[fullpath], recurse, dynlib, mode, noNimout = true)
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"):
var
cmd = when defined(Windows): "cmd /c " else: ""
cmd &= &"c2nim {hFile} --header:{header} --out:{nimFile.sanitizePath}"
if dynlib.nBl:
cmd.add &" --dynlib:{dynlib}"
if mode.contains("cpp"):
cmd.add " --cpp"
if flags.nBl:
cmd.add &" {flags}"
for i in gStateCT.defines:
cmd.add &" --assumedef:{i.quoteShell}"
# Have to create pragmas for c2nim since toast handles this at runtime
result.add quote do:
renderPragma()
let
(c2nimout, ret) = execAction(cmd)
if ret != 0:
rmFile(nimFile)
doAssert false, "\n\nc2nim codegen limitation or error - " & c2nimout
nimFile.writeFile(&"const {header} = \"{fullpath}\"\n\n" & readFile(nimFile))
if gStateCT.debug:
gecho nimFile.readFile()
gecho "# Saved to " & nimFile
try:
let
nimFileNode = newStrLitNode(nimFile.changeFileExt(""))
result.add quote do:
include `nimFileNode`
except:
getNimCheckError(nimFile)

85
nimterop/docs.nim Normal file
View file

@ -0,0 +1,85 @@
import strformat
from os import parentDir, getCurrentCompilerExe, DirSep
when defined(nimdoc):
from os import getCurrentDir, paramCount, paramStr
proc getNimRootDir(): string =
#[
hack, but works
alternatively (but more complex), use (from a nim file, not nims otherwise
you get Error: ambiguous call; both system.fileExists):
import "$nim/testament/lib/stdtest/specialpaths.nim"
nimRootDir
]#
fmt"{currentSourcePath}".parentDir.parentDir.parentDir
const
DirSep = when defined(Windows): '\\' else: '/'
proc execAction(cmd: string): string =
var
ccmd = ""
ret = 0
when defined(Windows):
ccmd = "cmd /c " & cmd
elif defined(posix):
ccmd = cmd
else:
doAssert false
(result, ret) = gorgeEx(ccmd)
doAssert ret == 0, "Command failed: " & $ret & "\ncmd: " & ccmd & "\nresult:\n" & result
proc buildDocs*(files: openArray[string], path: string, baseDir = getCurrentDir() & $DirSep,
defines: openArray[string] = @[], nimArgs = "") =
## Generate docs for all specified nim `files` to the specified `path`
##
## `baseDir` is the project path by default and `files` and `path` are relative
## to that directory. Set to "" if using absolute paths.
##
## `defines` is a list of `-d:xxx` define flags (the `xxx` part) that should be passed
## to `nim doc` so that `getHeader()` is invoked correctly.
##
## `nimArgs` is a string representing extra arguments to send to the `nim doc` call.
##
## Use the `--publish` flag with nimble to publish docs contained in
## `path` to Github in the `gh-pages` branch. This requires the ghp-import
## package for Python: `pip install ghp-import`
##
## WARNING: `--publish` will destroy any existing content in this branch.
##
## NOTE: `buildDocs()` only works correctly on Windows with Nim 1.0+ since
## https://github.com/nim-lang/Nim/pull/11814 is required.
when defined(Windows) and (NimMajor, NimMinor, NimPatch) < (1, 0, 0):
echo "buildDocs() unsupported on Windows for Nim < 1.0 - requires PR #11814"
else:
let
baseDir =
if baseDir == $DirSep:
getCurrentDir() & $DirSep
else:
baseDir
path = baseDir & path
defStr = block:
var defStr = ""
for def in defines:
defStr &= " -d:" & def
defStr
nim = getCurrentCompilerExe()
for file in files:
echo execAction(&"{nim} doc {defStr} {nimArgs} -o:{path} --project --index:on {baseDir & file}")
echo execAction(&"{nim} buildIndex {nimArgs} -o:{path}/theindex.html {path}")
when declared(getNimRootDir):
#[
this enables doc search, works at least locally with:
cd {path} && python -m SimpleHTTPServer 9009
]#
echo execAction(&"{nim} js {nimArgs} -o:{path}/dochack.js {getNimRootDir()}/tools/dochack/dochack.nim")
for i in 0 .. paramCount():
if paramStr(i) == "--publish":
echo execAction(&"cd {path} && ghp-import --no-jekyll -fp {path}")
break

42
nimterop/enumtype.nim Normal file
View file

@ -0,0 +1,42 @@
import macros
macro defineEnum(typ: untyped): untyped =
result = newNimNode(nnkStmtList)
# Enum mapped to distinct cint
result.add quote do:
type `typ`* = distinct cint
for i in ["+", "-", "*", "div", "mod", "shl", "shr", "or", "and", "xor", "<", "<=", "==", ">", ">="]:
let
ni = newIdentNode(i)
typout = if i[0] in "<=>": newIdentNode("bool") else: typ # comparisons return bool
if i[0] == '>': # cannot borrow `>` and `>=` from templates
let
nopp = if i.len == 2: newIdentNode("<=") else: newIdentNode("<")
result.add quote do:
proc `ni`*(x: `typ`, y: cint): `typout` = `nopp`(y, x)
proc `ni`*(x: cint, y: `typ`): `typout` = `nopp`(y, x)
proc `ni`*(x, y: `typ`): `typout` = `nopp`(y, x)
else:
result.add quote do:
proc `ni`*(x: `typ`, y: cint): `typout` {.borrow.}
proc `ni`*(x: cint, y: `typ`): `typout` {.borrow.}
proc `ni`*(x, y: `typ`): `typout` {.borrow.}
result.add quote do:
proc `ni`*(x: `typ`, y: int): `typout` = `ni`(x, y.cint)
proc `ni`*(x: int, y: `typ`): `typout` = `ni`(x.cint, y)
let
divop = newIdentNode("/") # `/`()
dlrop = newIdentNode("$") # `$`()
notop = newIdentNode("not") # `not`()
result.add quote do:
proc `divop`*(x, y: `typ`): `typ` = `typ`((x.float / y.float).cint)
proc `divop`*(x: `typ`, y: cint): `typ` = `divop`(x, `typ`(y))
proc `divop`*(x: cint, y: `typ`): `typ` = `divop`(`typ`(x), y)
proc `divop`*(x: `typ`, y: int): `typ` = `divop`(x, y.cint)
proc `divop`*(x: int, y: `typ`): `typ` = `divop`(x.cint, y)
proc `dlrop`*(x: `typ`): string {.borrow.}
proc `notop`*(x: `typ`): `typ` {.borrow.}

42
nimterop/enumtypepub.nim Normal file
View file

@ -0,0 +1,42 @@
import macros
macro defineEnum*(typ: untyped): untyped =
result = newNimNode(nnkStmtList)
# Enum mapped to distinct cint
result.add quote do:
type `typ`* = distinct cint
for i in ["+", "-", "*", "div", "mod", "shl", "shr", "or", "and", "xor", "<", "<=", "==", ">", ">="]:
let
ni = newIdentNode(i)
typout = if i[0] in "<=>": newIdentNode("bool") else: typ # comparisons return bool
if i[0] == '>': # cannot borrow `>` and `>=` from templates
let
nopp = if i.len == 2: newIdentNode("<=") else: newIdentNode("<")
result.add quote do:
proc `ni`*(x: `typ`, y: cint): `typout` = `nopp`(y, x)
proc `ni`*(x: cint, y: `typ`): `typout` = `nopp`(y, x)
proc `ni`*(x, y: `typ`): `typout` = `nopp`(y, x)
else:
result.add quote do:
proc `ni`*(x: `typ`, y: cint): `typout` {.borrow.}
proc `ni`*(x: cint, y: `typ`): `typout` {.borrow.}
proc `ni`*(x, y: `typ`): `typout` {.borrow.}
result.add quote do:
proc `ni`*(x: `typ`, y: int): `typout` = `ni`(x, y.cint)
proc `ni`*(x: int, y: `typ`): `typout` = `ni`(x.cint, y)
let
divop = newIdentNode("/") # `/`()
dlrop = newIdentNode("$") # `$`()
notop = newIdentNode("not") # `not`()
result.add quote do:
proc `divop`*(x, y: `typ`): `typ` = `typ`((x.float / y.float).cint)
proc `divop`*(x: `typ`, y: cint): `typ` = `divop`(x, `typ`(y))
proc `divop`*(x: cint, y: `typ`): `typ` = `divop`(`typ`(x), y)
proc `divop`*(x: `typ`, y: int): `typ` = `divop`(x, y.cint)
proc `divop`*(x: int, y: `typ`): `typ` = `divop`(x.cint, y)
proc `dlrop`*(x: `typ`): string {.borrow.}
proc `notop`*(x: `typ`): `typ` {.borrow.}

167
nimterop/globals.nim Normal file
View file

@ -0,0 +1,167 @@
import strutils, tables
when defined(TOAST):
import sets, sequtils, strutils
import "."/plugin
import compiler/[ast, idents, modulegraphs, options]
# import "."/treesitter/api
type
Feature* = enum
ast2
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
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
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
preprocess*: bool # `--preprocess | -p` to enable preprocessing of code before wrapping
prefix*: seq[string] # `--prefix` strings to strip from start of identifiers
recurse*: bool # `--recurse | -r` to recurse into #include files in headers specified
replace*: OrderedTableRef[string, string]
# `--replace | -G` replacement rules for identifiers
suffix*: seq[string] # `--suffix` strings to strip from end of identifiers
symOverride*: seq[string] # `cSkipSymbol()`, `cOverride()` and `--symOverride | -O` symbols to skip during wrapping
typeMap*: TableRef[string, string]
# `--typeMap | -T` to map instances of type X to Y - e.g. ABC=cint
when defined(TOAST):
# Data fields
code*: string # Contents of header file currently being processed
currentHeader*: string # Const name of header being currently processed
impShort*: string # Short base name for pragma in output
outputHandle*: File # `--output | -o` open file handle
sourceFile*: string # Full path of header being currently processed
# Plugin callbacks
onSymbol*, onSymbolOverride*: OnSymbol
onSymbolOverrideFinal*: OnSymbolOverrideFinal
# Symbol tables
constIdentifiers*: HashSet[string] # Const names for enum casting
identifiers*: TableRef[string, string] # Symbols that have been declared so far indexed by nimName
skippedSyms*: HashSet[string] # Symbols that have been skipped due to being unwrappable or
# the user provided override is blank
headersProcessed*: HashSet[string] # Headers already processed directly or recursively
# Nim compiler objects
constSection*, enumSection*, pragmaSection*, procSection*, typeSection*, varSection*: PNode
identCache*: IdentCache
config*: ConfigRef
graph*: ModuleGraph
# Table of symbols to generated AST PNode - used to implement forward declarations
identifierNodes*: TableRef[string, PNode]
# Used for the exprparser.nim module
currentExpr*, currentTyCastName*: string
# Controls whether or not the current expression
# should validate idents against currently defined idents
skipIdentValidation*: bool
# Top level header for wrapper output - include imported types, pragmas and other info
wrapperHeader*: string
else:
# cimport.nim specific
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
searchDirs*: seq[string] # `cSearchPath()` added directories for header search
BuildType* = enum
btAutoconf, btCmake
BuildStatus* = object
built*: bool
buildPath*: string
error*: string
when nimvm:
var
gStateCT* {.compileTime, used.} = new(State)
else:
var
gState*: State
when defined(TOAST):
const
gAtoms* {.used.} = @[
"field_identifier",
"identifier",
"number_literal",
"char_literal",
"preproc_arg",
"primitive_type",
"sized_type_specifier",
"type_identifier"
].toHashSet()
gExpressions* {.used.} = @[
"parenthesized_expression",
"bitwise_expression",
"shift_expression",
"math_expression",
"escape_sequence",
"binary_expression",
"unary_expression"
].toHashSet()
gEnumVals* {.used.} = @[
"identifier",
"number_literal",
"char_literal"
].concat(toSeq(gExpressions.items))
type
Status* = enum
success, unknown, error
template getCommented*(str: string): string =
"\n# " & str.strip().replace("\n", "\n# ")
# Redirect output to file when required
template gecho*(args: string) =
when defined(TOAST):
when nimvm:
echo args
else:
if gState.outputHandle.isNil:
echo args
else:
gState.outputHandle.writeLine(args)
else:
echo args
template decho*(args: varargs[string, `$`]): untyped =
let
str = join(args, "")
when defined(TOAST):
if gState.debug:
gecho str.getCommented()
else:
if gStateCT.debug:
echo str.getCommented()
template nBl*(s: typed): untyped {.used.} =
(s.len != 0)
template Bl*(s: typed): untyped {.used.} =
(s.len == 0)

39
nimterop/loaf.nim Normal file
View file

@ -0,0 +1,39 @@
import system except find
import os
import cligen
import strutils except find
import regex except find
proc findRec(dir: string, pattern: string | Regex, recurse: bool) =
for kind, path in walkDir(dir):
if kind in [pcDir, pcLinkToDir]:
if recurse: findRec(path, pattern, recurse)
elif pattern in path:
echo path.absolutePath()
proc find(recurse = false, rexp = false, args: seq[string]) =
var
pat = ""
rpat: Regex
for arg in args:
if not arg.startsWith("-"):
if dirExists(arg):
if rexp:
findRec(arg, rpat, recurse)
else:
findRec(arg, pat, recurse)
else:
pat = arg
if rexp: rpat = re(arg)
when isMainModule:
dispatchMulti([
find, help = {
"recurse": "recursive search",
"rexp": "patterns are regular expressions",
"args": "pattern1 dir1 dir2 pattern2 dir3 ..."
}
])

18
nimterop/paths.nim Normal file
View file

@ -0,0 +1,18 @@
import os
import "."/build/shell
const
cacheDir* = getProjectCacheDir("nimterop", forceClean = false)
proc nimteropRoot*(): string =
currentSourcePath.parentDir.parentDir
proc nimteropSrcDir*(): string =
nimteropRoot() / "nimterop"
proc toastExePath*(): string =
nimteropSrcDir() / ("toast".addFileExt ExeExt)
proc testsIncludeDir*(): string =
nimteropRoot() / "tests" / "include"

22
nimterop/plugin.nim Normal file
View file

@ -0,0 +1,22 @@
import macros, sets, tables
type
Symbol* = object
name*: string
parent*: string
kind*: NimSymKind
override*: string
StringHash = HashSet[string]
OnSymbol* = proc(sym: var Symbol) {.cdecl.}
OnSymbolOverrideFinal* = proc(typ: string): StringHash {.cdecl.}
var
cOverrides*: Table[string, StringHash]
cOverrides = initTable[string, StringHash]()
cOverrides["nskType"] = StringHash()
cOverrides["nskConst"] = StringHash()
cOverrides["nskProc"] = StringHash()
proc onSymbolOverrideFinal*(typ: string): StringHash {.exportc, dynlib.} =
result = cOverrides[typ]

43
nimterop/setup.nim Normal file
View file

@ -0,0 +1,43 @@
import os, strutils
import "."/[paths]
import "."/build/[shell]
proc treesitterSetup*() =
gitPull("https://github.com/tree-sitter/tree-sitter", cacheDir / "treesitter", """
lib/include/*
lib/src/*
""", "0.16.8")
let
tbase = cacheDir / "treesitter" / "lib"
stack = tbase / "src" / "stack.c"
parser = tbase / "include" / "tree_sitter" / "parser.h"
tparser = parser.replace("parser", "tparser")
language = tbase / "src" / "language.h"
lexer = tbase / "src" / "lexer.h"
subtree = tbase / "src" / "subtree.h"
stack.writeFile(stack.readFile().replace("inline Stack", "Stack"))
# parser.h
mvFile(parser, tparser)
language.writeFile(language.readFile().replace("parser.h", "tparser.h"))
lexer.writeFile(lexer.readFile().replace("parser.h", "tparser.h"))
subtree.writeFile(subtree.readFile().replace("parser.h", "tparser.h"))
proc treesitterCSetup*() =
gitPull("https://github.com/tree-sitter/tree-sitter-c", cacheDir / "treesitter_c", """
src/*.h
src/*.c
src/*.cc
src/tree_sitter/parser.h
""", "0.16.1")
proc treesitterCppSetup*() =
gitPull("https://github.com/tree-sitter/tree-sitter-cpp", cacheDir / "treesitter_cpp", """
src/*.h
src/*.c
src/*.cc
src/tree_sitter/parser.h
""", "v0.16.0")

278
nimterop/toast.nim Normal file
View file

@ -0,0 +1,278 @@
import os, osproc, sets, strformat, strutils, tables, times
import "."/treesitter/[api, c, cpp]
import "."/[globals]
import "."/toastlib/[ast2, getters, tshelp]
import "."/build/[ccompiler, misc]
var
# Output generated before main() is called
preMainOut = ""
proc process(gState: State, path: string) =
doAssert fileExists(path), &"Invalid path {path}"
if gState.mode.Bl:
gState.mode = getCompilerMode(path)
if gState.preprocess:
gState.getPreprocessor(path)
else:
gState.code = readFile(path)
withCodeAst(gState.code, gState.mode):
if gState.past:
gecho gState.printLisp(root)
elif gState.pnim:
parseNim(gState, path, root)
elif gState.preprocess:
gecho gState.code
# CLI processing with default values
proc main(
check = false,
compile: seq[string] = @[],
convention = "cdecl",
debug = false,
defines: seq[string] = @[],
dynlib: string = "",
exclude: seq[string] = @[],
feature: seq[Feature] = @[],
includeDirs: seq[string] = @[],
mode = "",
nim: string = "nim",
noComments = false,
noHeader = false,
output = "",
passC: seq[string] = @[],
passL: seq[string] = @[],
past = false,
pluginSourcePath: string = "",
pnim = false,
prefix: seq[string] = @[],
preprocess = false,
recurse = false,
replace: seq[string] = @[],
stub = false,
suffix: seq[string] = @[],
symOverride: seq[string] = @[],
typeMap: seq[string] = @[],
source: seq[string]
) =
# Setup global state with arguments
gState = State(
compile: compile,
convention: convention,
debug: debug,
defines: defines,
dynlib: dynlib,
exclude: exclude,
feature: feature,
includeDirs: includeDirs,
mode: mode,
nim: nim.sanitizePath,
noComments: noComments,
noHeader: noHeader,
passC: passC,
passL: passL,
past: past,
pluginSourcePath: pluginSourcePath,
pnim: pnim,
prefix: prefix,
preprocess: preprocess,
recurse: recurse,
replace: newOrderedTable[string, string](),
suffix: suffix,
symOverride: symOverride
)
# Split some arguments with ,
gState.symOverride = gState.symOverride.getSplitComma()
gState.prefix = gState.prefix.getSplitComma()
gState.suffix = gState.suffix.getSplitComma()
# Replace => Table
for i in replace.getSplitComma():
let
nv = i.split("=", maxsplit = 1)
name = nv[0]
value = if nv.len == 2: nv[1] else: ""
gState.replace[name] = value
# typeMap => getters.gTypeMap
for i in typeMap.getSplitComma():
let
nv = i.split("=", maxsplit = 1)
doAssert nv.len == 2, "`--typeMap` requires X=Y format"
gTypeMap[nv[0]] = nv[1]
gTypeMapValues.incl nv[1]
if pluginSourcePath.nBl:
gState.loadPlugin(pluginSourcePath)
var
outputFile = output
check = check or stub
# Fix output file extention for Nim mode
if outputFile.len != 0 and pnim:
if outputFile.splitFile().ext != ".nim":
outputFile = outputFile & ".nim"
# Check needs a file
if check and outputFile.len == 0:
outputFile = getTempDir() / "toast_" & ($getTime().toUnix()).addFileExt("nim")
# Recurse implies preprocess
if gState.recurse:
gState.preprocess = true
# Redirect output to file
if outputFile.len != 0:
doAssert gState.outputHandle.open(outputFile, fmWrite),
&"Failed to write to {outputFile}"
decho &"# Writing output to {outputFile}\n"
if source.nBl:
# Print source after preprocess or Nim output
if gState.pnim:
gecho preMainOut
gState.initNim()
for src in source:
let
src = src.expandSymlinkAbs()
if src notin gState.headersProcessed:
gState.process(src)
gState.headersProcessed.incl src
if gState.pnim:
printNim(gState)
# Close outputFile
if outputFile.len != 0:
gState.outputHandle.close()
# Check Nim output
if gState.pnim and check:
# Run nim check on generated wrapper
var
(check, err) = execCmdEx(&"{gState.nim} check {outputFile}")
if err != 0:
# Failed check so try stubbing
if stub:
# Find undeclared identifiers in error
var
data = ""
stubData = ""
for line in check.splitLines:
if "undeclared identifier" in line:
try:
# Add stub of object type
stubData &= " " & line.split("'")[1] & " = object\n"
except:
discard
# Include in wrapper file
data = outputFile.readFile()
let
idx = data.find("\ntype\n")
if idx != -1:
# In first existing type block
data = data[0 ..< idx+6] & stubData & data[idx+6 .. ^1]
else:
# At the top if none already
data = "type\n" & stubData & data
outputFile.writeFile(data)
# Rerun nim check on stubbed wrapper
(check, err) = execCmdEx(&"{gState.nim} check {outputFile}")
doAssert err == 0, data & "\n# Nim check with stub failed:\n\n" & check
else:
doAssert err == 0, outputFile.readFile() & "\n# Nim check failed:\n\n" & check
# Print wrapper if temporarily redirected to file
if check and output.len == 0:
stdout.write outputFile.readFile()
proc mergeParams(cmdNames: seq[string], cmdLine = commandLineParams()): seq[string] =
# Load command-line params from `source` if it is a .cfg file
if cmdNames.len != 0:
# https://github.com/c-blake/cligen/issues/149
for param in cmdLine:
if param.fileExists() and param.splitFile().ext == ".cfg":
preMainOut &= &"# Loading flags from '{param}'\n"
for line in param.readFile().splitLines():
let
line = line.strip()
if line.len > 1 and line[0] != '#':
result.add line.parseCmdLine()
else:
result.add param
if result.len != 0 and "-h" notin result and "--help" notin result:
preMainOut &= &"""# Generated @ {$now()}
# Command line:
# {getAppFilename()} {result.join(" ")}
"""
else:
result = cmdLine
when isMainModule:
# Setup cligen command line help and short flags
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",
"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",
"nim": "use a particular Nim executable",
"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",
"prefix": "strip prefix from identifiers",
"preprocess": "run preprocessor on header",
"recurse": "process #include files - implies --preprocess",
"replace": "replace X with Y in identifiers, X1=Y1,X2=Y2, @X for regex",
"source" : "C/C++ source/header(s) and command line file(s)",
"stub": "stub out undefined type references as objects",
"suffix": "strip suffix from identifiers",
"symOverride": "skip generating specified symbols",
"typeMap": "map instances of type X to Y - e.g. ABC=cint"
}, short = {
"check": 'k',
"convention": 'C',
"debug": 'd',
"defines": 'D',
"dynlib": 'l',
"exclude": 'X',
"feature": 'f',
"includeDirs": 'I',
"noComments": 'c',
"noHeader": 'H',
"output": 'o',
"past": 'a',
"pnim": 'n',
"prefix": 'E',
"preprocess": 'p',
"recurse": 'r',
"replace": 'G',
"stub": 's',
"suffix": 'F',
"symOverride": 'O',
"typeMap": 'T'
})

30
nimterop/toast.nims Normal file
View file

@ -0,0 +1,30 @@
import os
# Workaround for C++ scanner.cc causing link error with other C obj files
switch("clang.linkerexe", "clang++")
switch("gcc.linkerexe", "g++")
# Workaround for NilAccessError crash on Windows #98
# Could also help for OSX/Linux crash
switch("gc", "markAndSweep")
# Retain stackTrace for clear errors
switch("stackTrace", "on")
switch("lineTrace", "on")
# Path to compiler
switch("path", "$nim")
# Case objects
when not defined(danger):
switch("define", "nimOldCaseObjects")
# Prevent outdir override
switch("out", currentSourcePath.parentDir() / "toast".addFileExt(ExeExt))
# Define TOAST for globals.nim
switch("define", "TOAST")
# Static for Windows - #248
when defined(Windows):
switch("passL", "-static")

1965
nimterop/toastlib/ast2.nim Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,147 @@
import macros, strutils
import compiler/[ast, idents, lineinfos, msgs, options, parser, pathutils, renderer]
import ".."/[globals, treesitter/api]
import "."/[getters, tshelp]
proc handleError*(conf: ConfigRef, info: TLineInfo, msg: TMsgKind, arg: string) =
# Raise exception in parseString() instead of exiting for errors
if msg < warnMin:
raise newException(Exception, msgKindToString(msg))
proc parseString*(gState: State, str: string): PNode =
# Parse a string into Nim AST - use custom error handler that raises
# an exception rather than exiting on failure
try:
result = parseString(
str, gState.identCache, gState.config, errorHandler = handleError
)
except:
decho getCurrentExceptionMsg()
proc printTree*(gState: State, pnode: PNode, offset = ""): string =
if not pnode.isNil and gState.debug and pnode.kind != nkNone:
result &= "\n# " & offset & $pnode.kind & "("
case pnode.kind
of nkCharLit:
result &= ($pnode.intVal.char).escape & ")"
of nkIntLit..nkUInt64Lit:
result &= $pnode.intVal & ")"
of nkFloatLit..nkFloat128Lit:
result &= $pnode.floatVal & ")"
of nkStrLit..nkTripleStrLit:
result &= pnode.strVal.escape & ")"
of nkSym:
result &= $pnode.sym & ")"
of nkIdent:
result &= "\"" & $pnode.ident.s & "\")"
else:
if pnode.sons.len != 0:
for i in 0 ..< pnode.sons.len:
result &= gState.printTree(pnode.sons[i], offset & " ")
if i != pnode.sons.len - 1:
result &= ","
result &= "\n# " & offset & ")"
else:
result &= ")"
if offset.len == 0:
result &= "\n"
proc printDebug*(gState: State, pnode: PNode) =
if gState.debug and pnode.kind != nkNone:
gecho ("Output => " & $pnode).getCommented()
gecho gState.printTree(pnode)
proc getDefaultLineInfo*(gState: State): TLineInfo =
result = newLineInfo(gState.config, gState.sourceFile.AbsoluteFile, 0, 0)
proc getLineInfo*(gState: State, node: TSNode): TLineInfo =
# Get Nim equivalent line:col info from node
let
(line, col) = gState.getLineCol(node)
result = newLineInfo(gState.config, gState.sourceFile.AbsoluteFile, line, col)
proc getIdent*(gState: State, name: string, info: TLineInfo, exported = true): PNode =
if name.nBl:
# Get ident PNode for name + info
let
exp = getIdent(gState.identCache, "*")
ident = getIdent(gState.identCache, name)
if exported:
result = newNode(nkPostfix)
result.add newIdentNode(exp, info)
result.add newIdentNode(ident, info)
else:
result = newIdentNode(ident, info)
proc getIdent*(gState: State, name: string): PNode =
gState.getIdent(name, gState.getDefaultLineInfo(), exported = false)
proc getIdentName*(node: PNode): string =
if not node.isNil:
for i in 0 ..< node.len:
if node[i].kind == nkIdent and $node[i] != "*":
result = $node[i]
if result.Bl and node.len > 0:
result = node[0].getIdentName()
proc getNameInfo*(gState: State, node: TSNode, kind: NimSymKind, parent = ""):
tuple[name, origname: string, info: TLineInfo] =
# Shortcut to get identifier name and info (node value and line:col)
result.origname = gState.getNodeVal(node)
result.name = gState.getIdentifier(result.origname, kind, parent)
if result.name.nBl:
if kind == nskType:
result.name = gState.getType(result.name, parent)
result.info = gState.getLineInfo(node)
proc getPtrType*(str: string): string =
result = case str:
of "cchar":
"cstring"
of "object":
"pointer"
of "FILE":
"File"
else:
str
proc newPtrTree*(gState: State, count: int, typ: PNode): PNode =
# Create nkPtrTy tree depending on count
#
# Reduce by 1 if Nim type available for ptr X - e.g. ptr cchar = cstring
result = typ
var
count = count
if typ.kind == nkIdent:
let
tname = typ.ident.s
ptname = getPtrType(tname)
if tname != ptname:
# If Nim type available, use that ident
result = gState.getIdent(ptname, typ.info, exported = false)
# One ptr reduced
count -= 1
if count > 0:
# Nested nkPtrTy(typ) depending on count
#
# [ptr ...] typ
#
# nkPtrTy(
# nkPtrTy(
# typ
# )
# )
var
nresult = newNode(nkPtrTy)
parent = nresult
child: PNode
for i in 1 ..< count:
child = newNode(nkPtrTy)
parent.add child
parent = child
parent.add result
result = nresult

View file

@ -0,0 +1,643 @@
import strformat, strutils, macros, sets, sequtils
import regex
import compiler/[ast, renderer]
import ".."/treesitter/[api, c, cpp]
import ".."/globals
import "."/[getters, comphelp, tshelp]
# This version of exprparser should be able to handle:
#
# All integers + integer like expressions (hex, octal, suffixes)
# All floating point expressions (except for C++'s hex floating point stuff)
# Strings and character literals, including C's escape characters (not sure if this is the same as C++'s escape characters or not)
# Math operators (+, -, /, *)
# Some Unary operators (-, !, ~). ++, --, and & are yet to be implemented
# Any identifiers
# C type descriptors (int, char, etc)
# Boolean values (true, false)
# Shift expressions (containing anything in this list)
# Cast expressions (containing anything in this list)
# Math expressions (containing anything in this list)
# Sizeof expressions (containing anything in this list)
# Cast expressions (containing anything in this list)
# Parentheses expressions (containing anything in this list)
# Expressions containing other expressions
#
# In addition to the above, it should also handle most type coercions, except
# for where Nim can't (such as uint + -int)
type
ExprParseError* = object of CatchableError
const
CharRegStr = "(\\\\x[[:xdigit:]]{2}|\\\\\\d{3}|\\\\0|\\\\a|\\\\b|\\\\e|\\\\f|\\\\n|\\\\r|\\\\t|\\\\v|\\\\\\\\|\\\\'|\\\\\"|[[:ascii:]])"
CharRegex = re(CharRegStr)
template val(node: TSNode): string =
gState.currentExpr.getNodeVal(node)
proc printDebugExpr*(gState: State, node: TSNode) =
if gState.debug:
gecho ("Input => " & node.val).getCommented()
gecho gState.currentExpr.printLisp(node).getCommented()
proc getExprIdent*(gState: State, identName: string, kind = nskConst, parent = ""): PNode =
## Gets a cPlugin transformed identifier from `identName`
##
## Returns PNode(nkNone) if the identifier is blank
result = newNode(nkNone)
if gState.skipIdentValidation or identName notin gState.skippedSyms:
var ident = identName
if ident != "_":
# Process the identifier through cPlugin
ident = gState.getIdentifier(ident, kind, parent)
if kind == nskType:
result = gState.getIdent(ident)
elif gState.skipIdentValidation or ident.nBl and ident in gState.constIdentifiers:
if gState.currentTyCastName.nBl:
ident = ident & "." & gState.currentTyCastName
result = gState.getIdent(ident)
proc getExprIdent*(gState: State, node: TSNode, kind = nskConst, parent = ""): PNode =
## Gets a cPlugin transformed identifier from `identName`
##
## Returns PNode(nkNone) if the identifier is blank
gState.getExprIdent(node.val, kind, parent)
proc parseChar(charStr: string): uint8 {.inline.} =
## Parses a character literal out of a string. This is needed
## because treesitter gives unescaped characters when parsing
## strings.
if charStr.len == 1:
return charStr[0].uint8
# Handle octal, hex, unicode?
if charStr.startsWith("\\x"):
result = parseHexInt(charStr.replace("\\x", "0x")).uint8
elif charStr.len == 4: # Octal
result = parseOctInt("0o" & charStr[1 ..< charStr.len]).uint8
if result == 0:
case charStr
of "\\0":
result = ord('\0')
of "\\a":
result = 0x07
of "\\b":
result = 0x08
of "\\e":
result = 0x1B
of "\\f":
result = 0x0C
of "\\n":
result = '\n'.uint8
of "\\r":
result = 0x0D
of "\\t":
result = 0x09
of "\\v":
result = 0x0B
of "\\\\":
result = 0x5C
of "\\'":
result = '\''.uint8
of "\\\"":
result = '\"'.uint8
of "\\?":
result = 0x3F
else:
discard
if result > uint8.high:
result = uint8.high
proc getCharLit(charStr: string): PNode {.inline.} =
## Convert a character string into a proper Nim char lit node
result = newNode(nkCharLit)
result.intVal = parseChar(charStr).int64
proc getFloatNode(number, suffix: string): PNode {.inline.} =
## Get a Nim float node from a C float expression + suffix
let floatSuffix = number[number.len-1]
try:
case floatSuffix
of 'l', 'L':
# TODO: handle long double (128 bits)
# result = newNode(nkFloat128Lit)
result = newFloatNode(nkFloat64Lit, parseFloat(number[0 ..< number.len - 1]))
of 'f', 'F':
result = newFloatNode(nkFloat64Lit, parseFloat(number[0 ..< number.len - 1]))
else:
result = newFloatNode(nkFloatLit, parseFloat(number))
except ValueError:
raise newException(ExprParseError, &"Could not parse float value \"{number}\".")
proc getIntNode(number, suffix: string): PNode {.inline.} =
## Get a Nim int node from a C integer expression + suffix
var
val: BiggestInt
flags: TNodeFlags
if number.len > 1 and number[0] == '0':
if number[1] in ['x', 'X']:
val = parseHexInt(number)
flags = {nfBase16}
elif number[1] in ['b', 'B']:
val = parseBinInt(number)
flags = {nfBase2}
else:
val = parseOctInt(number)
flags = {nfBase8}
else:
val = parseInt(number)
case suffix
of "u", "U":
result = newNode(nkUintLit)
of "l", "L":
# If the value doesn't fit, adjust
if val > int32.high or val < int32.low:
result = newNode(nkInt64Lit)
else:
result = newNode(nkInt32Lit)
of "ul", "UL":
# If the value doesn't fit, adjust
if val > uint32.high.BiggestInt:
result = newNode(nkUInt64Lit)
else:
result = newNode(nkUInt32Lit)
of "ll", "LL":
result = newNode(nkInt64Lit)
of "ull", "ULL":
result = newNode(nkUint64Lit)
else:
result = newNode(nkIntLit)
result.intVal = val
result.flags = flags
proc getNumNode(number, suffix: string): PNode {.inline.} =
## Convert a C number to a Nim number PNode
if number.contains("."):
getFloatNode(number, suffix)
else:
getIntNode(number, suffix)
proc processNumberLiteral(gState: State, node: TSNode): PNode =
## Parse a number literal from a TSNode. Can be a float, hex, long, etc
result = newNode(nkNone)
let nodeVal = node.val
var
prefix: string
number = nodeVal
suffix: string
const
singleEndings = ["u", "l", "U", "L"]
doubleEndings = ["ul", "UL", "ll", "LL"]
tripleEndings = ["ull", "ULL"]
if number.startsWith("-"):
number = number[1 ..< number.len]
prefix = "-"
if tripleEndings.any(proc (s: string): bool = number.endsWith(s)):
suffix = number[^3 .. ^1]
number = number[0 ..< ^3]
elif doubleEndings.any(proc (s: string): bool = number.endsWith(s)):
suffix = number[^2 .. ^1]
number = number[0 ..< ^2]
elif singleEndings.any(proc (s: string): bool = number.endsWith(s)):
suffix = $number[number.len - 1]
number = number[0 ..< ^1]
result = getNumNode(number, suffix)
if result.kind != nkNone and prefix == "-":
result = nkPrefix.newTree(
gState.getIdent("-"),
result
)
proc processCharacterLiteral(gState: State, node: TSNode): PNode =
# Input => 'G'
#
# (char_literal 1 1 3 "'G'")
#
# Output => 'G'
#
# nkCharLit("G")
let val = node.val
result = getCharLit(val[1 ..< val.len - 1])
proc processStringLiteral(gState: State, node: TSNode): PNode =
# Input => "\n\rfoobar\0\'"
#
# (string_literal 1 1 16 ""\n\rfoobar\0\'""
# (escape_sequence 1 2 2 "\n")
# (escape_sequence 1 4 2 "\r")
# (escape_sequence 1 12 2 "\0")
# (escape_sequence 1 14 2 "\'")
# )
#
# Output => "\n\cfoobar\x00\'"
#
# nkStrLit("\x0A\x0Dfoobar\x00\'")
let
nodeVal = node.val
strVal = nodeVal[1 ..< nodeVal.len - 1]
# Convert the c string escape sequences/etc to Nim chars
var nimStr = newStringOfCap(nodeVal.len)
for m in strVal.findAll(CharRegex):
nimStr.add(parseChar(strVal[m.group(0)[0]]).chr)
result = newStrNode(nkStrLit, nimStr)
proc processTSNode(gState: State, node: TSNode, typeofNode: var PNode): PNode
proc processParenthesizedExpr(gState: State, node: TSNode, typeofNode: var PNode): PNode =
# Input => (a + b)
#
# (parenthesized_expression 1 1 7
# (math_expression 1 2 5
# (identifier 1 2 1 "a")
# (identifier 1 6 1 "b")
# )
# )
#
# Output => (typeof(a)(a + typeof(a)(b)))
#
# nkPar(
# nkCall(
# nkCall(
# nkIdent("typeof"),
# nkIdent("a")
# ),
# nkInfix(
# nkIdent("+"),
# nkIdent("a"),
# nkCall(
# nkCall(
# nkIdent("typeof"),
# nkIdent("a")
# ),
# nkIdent("b")
# )
# )
# )
# )
result = newNode(nkPar)
for i in 0 ..< node.len():
result.add(gState.processTSNode(node[i], typeofNode))
proc processCastExpression(gState: State, node: TSNode, typeofNode: var PNode): PNode =
# Input => (int)a
#
# (cast_expression 1 1 6 "(int)a"
# (type_descriptor 1 2 3 "int"
# (primitive_type 1 2 3 "int")
# )
# (identifier 1 6 1 "a")
# )
#
# Output => cast[cint](a)
#
# nkCast(
# nkIdent("cint"),
# nkIdent("a")
# )
result = nkCast.newTree(
gState.processTSNode(node[0], typeofNode),
gState.processTSNode(node[1], typeofNode)
)
proc getNimUnarySym(csymbol: string): string =
## Get the Nim equivalent of a unary C symbol
##
## TODO: Add ++, --,
case csymbol
of "+", "-":
result = csymbol
of "~", "!":
result = "not"
else:
raise newException(ExprParseError, &"Unsupported unary symbol \"{csymbol}\"")
proc getNimBinarySym(csymbol: string): string =
case csymbol
of "|", "||":
result = "or"
of "&", "&&":
result = "and"
of "^":
result = "xor"
of "==", "!=",
"+", "-", "/", "*",
">", "<", ">=", "<=":
result = csymbol
of "%":
result = "mod"
of "<<":
result = "shl"
of ">>":
result = "shr"
else:
raise newException(ExprParseError, &"Unsupported binary symbol \"{csymbol}\"")
proc processBinaryExpression(gState: State, node: TSNode, typeofNode: var PNode): PNode =
# Node has left and right children ie: (2 + 7)
#
# Input => a == b
#
# (equality_expression 1 1 6
# (identifier 1 1 1 "a")
# (identifier 1 6 1 "b")
# )
#
# Output => a == typeof(a)(b)
#
# nkInfix(
# nkIdent("=="),
# nkIdent("a"),
# nkCall(
# nkCall(
# nkIdent("typeof"),
# nkIdent("a")
# ),
# nkIdent("b")
# )
# )
result = newNode(nkInfix)
let
left = node[0]
right = node[1]
binarySym = node.tsNodeChild(1).val.strip()
nimSym = getNimBinarySym(binarySym)
result.add gState.getIdent(nimSym)
let leftNode = gState.processTSNode(left, typeofNode)
var tyNode = typeofNode
if typeofNode.isNil:
typeofNode = nkCall.newTree(
gState.getIdent("typeof"),
leftNode
)
tyNode = typeofNode
# Special case of setting the shift left/right type
# to be the type of the direct left operand
if binarySym in [">>", "<<"]:
tyNode = nkCall.newTree(
gState.getIdent("typeof"),
leftNode
)
let rightNode = gState.processTSNode(right, tyNode)
result.add leftNode
result.add nkCall.newTree(
tyNode,
rightNode
)
if binarySym == "/":
# Special case. Nim's operators generally output
# the same type they take in, except for division.
# So we need to emulate C here and cast the whole
# expression to the type of the first arg
result = nkCall.newTree(
tyNode,
result
)
proc processUnaryExpression(gState: State, node: TSNode, typeofNode: var PNode): PNode =
# Input => !a
#
# (logical_expression 1 1 2 "!a"
# (identifier 1 2 1 "a")
# )
#
# Output => (not a)
#
# nkPar(
# nkPrefix(
# nkIdent("not"),
# nkIdent("a")
# )
# )
result = newNode(nkPar)
let
child = node[0]
unarySym = node.tsNodeChild(0).val.strip()
nimSym = getNimUnarySym(unarySym)
if nimSym == "-":
# Special case. The minus symbol must be in front of an integer,
# so we have to make a gentle cast here to coerce it to one.
# Might be bad because we are overwriting the type
# There's probably a better way of doing this
if typeofNode.isNil:
typeofNode = gState.getIdent("int64")
result.add nkPrefix.newTree(
gState.getIdent(unarySym),
nkPar.newTree(
nkCall.newTree(
gState.getIdent("int64"),
gState.processTSNode(child, typeofNode)
)
)
)
else:
result.add nkPrefix.newTree(
gState.getIdent(nimSym),
gState.processTSNode(child, typeofNode)
)
proc processUnaryOrBinaryExpression(gState: State, node: TSNode, typeofNode: var PNode): PNode =
## Processes both unary (-1, ~true, !something) and binary (a + b, c * d) expressions
if node.len > 1:
# Node has left and right children ie: (2 + 7)
result = processBinaryExpression(gState, node, typeofNode)
elif node.len() == 1:
# Node has only one child, ie -(20 + 7)
result = processUnaryExpression(gState, node, typeofNode)
else:
raise newException(ExprParseError, &"Invalid {node.getName()} \"{node.val}\"")
proc processSizeofExpression(gState: State, node: TSNode, typeofNode: var PNode): PNode =
# Input => sizeof(int)
#
# (sizeof_expression 1 1 11 "sizeof(int)"
# (type_descriptor 1 8 3 "int"
# (primitive_type 1 8 3 "int")
# )
# )
#
# Output => sizeof(cint)
#
# nkCall(
# nkIdent("sizeof"),
# nkIdent("cint")
# )
result = nkCall.newTree(
gState.getIdent("sizeof"),
gState.processTSNode(node[0], typeofNode)
)
proc processTSNode(gState: State, node: TSNode, typeofNode: var PNode): PNode =
## Handle all of the types of expressions here. This proc gets called recursively
## in the processX procs and will drill down to sub nodes.
result = newNode(nkNone)
let nodeName = node.getName()
decho "NODE: ", nodeName, ", VAL: ", node.val
case nodeName
of "number_literal":
# Input -> 0x1234FE, 1231, 123u, 123ul, 123ull, 1.334f
# Output -> 0x1234FE, 1231, 123'u, 123'u32, 123'u64, 1.334
result = gState.processNumberLiteral(node)
of "string_literal":
# Input -> "foo\0\x42"
# Output -> "foo\0"
result = gState.processStringLiteral(node)
of "char_literal":
# Input -> 'F', '\060' // Octal, '\x5A' // Hex, '\r' // escape sequences
# Output -> 'F', '0', 'Z', '\r'
result = gState.processCharacterLiteral(node)
of "expression_statement", "ERROR", "translation_unit":
# Note that we're parsing partial expressions, so the TSNode might contain
# an ERROR node. If that's the case, they usually contain children with
# partial results, which will contain parsed expressions
#
# Input (top level statement) -> ((1 + 3 - IDENT) - (int)400.0)
# Output -> (1 + typeof(1)(3) - typeof(1)(IDENT) - typeof(1)(cast[int](400.0))) # Type casting in case some args differ
if node.len == 1:
result = gState.processTSNode(node[0], typeofNode)
elif node.len > 1:
var nodes: seq[PNode]
for i in 0 ..< node.len:
let subNode = gState.processTSNode(node[i], typeofNode)
if subNode.kind != nkNone:
nodes.add(subNode)
# Multiple nodes can get tricky. Don't support them yet, unless they
# have at most one valid node
if nodes.len > 1:
raise newException(ExprParseError, &"Node type \"{nodeName}\" with val ({node.val}) has more than one non empty node")
if nodes.len == 1:
result = nodes[0]
else:
raise newException(ExprParseError, &"Node type \"{nodeName}\" has no children")
of "parenthesized_expression":
# Input -> (IDENT - OTHERIDENT)
# Output -> (IDENT - typeof(IDENT)(OTHERIDENT)) # Type casting in case OTHERIDENT is a slightly different type (uint vs int)
result = gState.processParenthesizedExpr(node, typeofNode)
of "sizeof_expression":
# Input -> sizeof(char)
# Output -> sizeof(cchar)
result = gState.processSizeofExpression(node, typeofNode)
# binary_expression from the new treesitter upgrade should work here
# once we upgrade
of "math_expression", "logical_expression", "relational_expression",
"bitwise_expression", "equality_expression", "binary_expression",
"shift_expression", "unary_expression":
# Input -> a == b, a != b, !a, ~a, a < b, a > b, a <= b, a >= b, a >> b, a << b
# Output ->
# typeof(a)(a == typeof(a)(b))
# typeof(a)(a != typeof(a)(b))
# (not a)
# (not a)
# typeof(a)(a < typeof(a)(b))
# typeof(a)(a > typeof(a)(b))
# typeof(a)(a <= typeof(a)(b))
# typeof(a)(a >= typeof(a)(b))
# a shr typeof(a)(b)
# a shl typeof(a)(b)
result = gState.processUnaryOrBinaryExpression(node, typeofNode)
of "cast_expression":
# Input -> (int) a
# Output -> cast[cint](a)
result = gState.processCastExpression(node, typeofNode)
# Why are these node types named true/false?
of "true", "false":
# Input -> true, false
# Output -> true, false
result = gState.parseString(node.val)
of "type_descriptor":
# Input => int*
# (type_descriptor 1 2 4 "int*"
# (type_identifier 1 2 3 "int")
# (abstract_pointer_declarator 1 3 1 "*")
# )
#
# Output => ptr int
#
# nkPtrTy(
# nkIdent("int")
# )
let pointerDecl = node.anyChildInTree("abstract_pointer_declarator")
if pointerDecl.isNil:
result = gState.processTSNode(node[0], typeofNode)
else:
let pointerCount = pointerDecl.getXCount("abstract_pointer_declarator")
result = gState.newPtrTree(pointerCount, gState.processTSNode(node[0], typeofNode))
of "sized_type_specifier", "primitive_type", "type_identifier":
# Input -> int, unsigned int, long int, etc
# Output -> cint, cuint, clong, etc
let ty = gState.getType(node.val, parent = node.getName())
if ty.len > 0:
# If ty is not empty, one of C's builtin types has been found
result = gState.getExprIdent(ty, nskType, parent=node.getName())
else:
result = gState.getExprIdent(node.val, nskType, parent=node.getName())
if result.kind == nkNone:
raise newException(ExprParseError, &"Missing type specifier \"{node.val}\"")
of "identifier":
# Input -> IDENT
# Output -> IDENT (if found in sym table, else error)
result = gState.getExprIdent(node, parent=node.getName())
if result.kind == nkNone:
raise newException(ExprParseError, &"Missing identifier \"{node.val}\"")
of "comment":
discard
else:
raise newException(ExprParseError, &"Unsupported node type \"{nodeName}\" for node \"{node.val}\"")
if result.kind != nkNone:
decho "NODE RESULT: ", result
proc parseCExpression*(gState: State, codeRoot: TSNode): PNode =
## Parse a c expression from a root ts node
# This var is used for keeping track of the type of the first
# symbol used for type casting
var tnode: PNode = nil
result = newNode(nkNone)
try:
result = gState.processTSNode(codeRoot, tnode)
except ExprParseError as e:
decho e.msg
result = newNode(nkNone)
except Exception as e:
decho "UNEXPECTED EXCEPTION: ", e.msg
result = newNode(nkNone)
proc parseCExpression*(gState: State, code: string, name = "", skipIdentValidation = false): PNode =
## Convert the C string to a nim PNode tree
gState.currentExpr = code
gState.currentTyCastName = name
gState.skipIdentValidation = skipIdentValidation
withCodeAst(gState.currentExpr, gState.mode):
result = gState.parseCExpression(root)
# Clear the state
gState.currentExpr = ""
gState.currentTyCastName = ""
gState.skipIdentValidation = false

View file

@ -0,0 +1,421 @@
import dynlib, macros, os, osproc, sequtils, sets, streams, strformat, strutils, tables, times
import regex
import ".."/[globals, plugin]
import ".."/build/[ccompiler, misc, nimconf, shell]
const gReserved = """
addr and as asm
bind block break
case cast concept const continue converter
defer discard distinct div do
elif else end enum except export
finally for from func
if import in include interface is isnot iterator
let
macro method mixin mod
nil not notin
of or out
proc ptr
raise ref return
shl shr static
template try tuple type
using
var
when while
xor
yield""".split(Whitespace).toHashSet()
# Types related
const
# Enum macro read from file - written into wrapper when required
gEnumMacroConst = "import nimterop / enumtypepub"
var
gEnumMacro* = gEnumMacroConst
gTypeMap* = {
# char
"char": "cchar",
"signed char": "cschar",
"unsigned char": "cuchar",
# short
"short": "cshort",
"short int": "cshort",
"signed short": "cshort",
"signed short int": "cshort",
"unsigned short": "cushort",
"unsigned short int": "cushort",
"uShort": "cushort",
"u_short": "cushort",
# int
"int": "cint",
"signed": "cint",
"signed int": "cint",
"ssize_t": "int",
"unsigned": "cuint",
"unsigned int": "cuint",
"uInt": "cuint",
"u_int": "cuint",
"size_t": "uint",
"int8_t": "int8",
"int16_t": "int16",
"int32_t": "int32",
"int64_t": "int64",
"intptr_t": "ptr int",
"Int8": "int8",
"Int16": "int16",
"Int32": "int32",
"Int64": "int64",
"uint8_t": "uint8",
"uint16_t": "uint16",
"uint32_t": "uint32",
"uint64_t": "uint64",
"uintptr_t": "ptr uint",
"Uint8": "uint8",
"Uint16": "uint16",
"Uint32": "uint32",
"Uint64": "uint64",
# long
"long": "clong",
"long int": "clong",
"signed long": "clong",
"signed long int": "clong",
"off_t": "clong",
"unsigned long": "culong",
"unsigned long int": "culong",
"uLong": "culong",
"u_long": "culong",
# long long
"long long": "clonglong",
"long long int": "clonglong",
"signed long long": "clonglong",
"signed long long int": "clonglong",
"off64_t": "clonglong",
"unsigned long long": "culonglong",
"unsigned long long int": "culonglong",
# floating point
"float": "cfloat",
"double": "cdouble",
"long double": "clongdouble",
# Misc Nim types
"Bool": "bool",
"ptrdiff_t": "ByteAddress"
}.toTable()
# Nim type names that shouldn't need to be wrapped again
gTypeMapValues* = toSeq(gTypeMap.values).toHashSet()
# Types to import from C/Nim if used in wrapper
gTypeImport* = {
"time_t": """
import std/time_t as std_time_t
type time_t* = std_time_t.Time
""",
"time64_t": """
import std/time_t as std_time64_t
type time64_t* = std_time64_t.Time
""",
"wchar_t": """
when defined(cpp):
# http://www.cplusplus.com/reference/cwchar/wchar_t/
# In C++, wchar_t is a distinct fundamental type (and thus it is
# not defined in <cwchar> nor any other header).
type wchar_t* {.importc.} = object
else:
type wchar_t* {.importc, header:"stddef.h".} = object
""",
"va_list": """
type va_list* {.importc, header:"<stdarg.h>".} = object
"""}.toTable()
proc getType*(gState: State, str, parent: string): string =
if str == "void":
return "object"
result = str.strip(chars={'_'}).splitWhitespace().join(" ")
if gTypeMap.hasKey(result):
result = gTypeMap[result]
elif parent.nBl and gTypeImport.hasKey(result) and not gState.identifierNodes.hasKey(result):
# Include C/Nim type imports once if a field/param and not already declared
gState.wrapperHeader &= "\n" & gTypeImport[result]
gTypeImport.del result
# Identifier related
proc checkIdentifier(name, kind, parent, origName: string) =
let
parentStr = if parent.nBl: parent & ":" else: ""
if name.nBl:
let
origStr = if name != origName: &", originally '{origName}' before 'cPlugin:onSymbol()'," else: ""
errmsg = &"Identifier '{parentStr}{name}' ({kind}){origStr} $1 " &
"which Nim does not allow. Use toast flag '$2' or 'cPlugin()' to modify."
doAssert name[0] != '_' and name[^1] != '_', errmsg % ["has leading/trailing underscores '_'", "--prefix or --suffix"]
doAssert (not name.contains("__")): errmsg % ["has consecutive underscores '_'", "--replace"]
doAssert not name[0].isDigit(), errmsg % [&"starts with a digit '{name[0]}'", "--prefix"]
# Cannot blank out symbols which are fields or params
#
# `IgnoreSkipSymbol` is used to `getIdentifier()` even if symbol is in `symOverride` list
# so that any prefix/suffix/replace or `onSymbol()` processing can occur. This is only used
# for `cOverride()` since it also depends on `symOverride`.
if parent.nBl and parent != "IgnoreSkipSymbol":
doAssert name.nBl, &"Blank identifier, originally '{parentStr}{origName}' ({kind}), cannot be empty"
proc getIdentifier*(gState: State, name: string, kind: NimSymKind, parent=""): string =
doAssert name.nBl, "Blank identifier error"
if name notin gState.symOverride or parent.nBl:
if gState.onSymbol != nil:
# Use onSymbol from plugin provided by user
var
sym = Symbol(name: name, parent: parent, kind: kind)
gState.onSymbol(sym)
result = sym.name
else:
result = name
# Strip out --prefix from CLI if specified
for str in gState.prefix:
if result.startsWith(str):
result = result[str.len .. ^1]
# Strip out --suffix from CLI if specified
for str in gState.suffix:
if result.endsWith(str):
result = result[0 .. ^(str.len+1)]
# --replace from CLI if specified
for name, value in gState.replace.pairs:
if name.len > 1 and name[0] == '@':
result = result.replace(re(name[1 .. ^1]), value)
else:
result = result.replace(name, value)
checkIdentifier(result, $kind, parent, name)
if result in gReserved or (result == "object" and kind != nskType):
# Enclose in backticks since Nim reserved word
result = &"`{result}`"
else:
# Skip identifier since in symOverride
result = ""
proc getUniqueIdentifier*(gState: State, prefix = ""): string =
var
name = prefix & "_" & gState.sourceFile.extractFilename().multiReplace([(".", ""), ("-", "")])
nimName = name[0] & name[1 .. ^1].replace("_", "").toLowerAscii
count = 1
while (nimName & $count) in gState.identifiers:
count += 1
return name & $count
proc addNewIdentifer*(gState: State, name: string, override = false): bool =
if override or name notin gState.symOverride:
let
nimName = name[0] & name[1 .. ^1].replace("_", "").toLowerAscii
if gState.identifiers.hasKey(nimName):
doAssert name == gState.identifiers[nimName],
&"Identifier '{name}' is a stylistic duplicate of identifier " &
&"'{gState.identifiers[nimName]}', use 'cPlugin:onSymbol()' to rename"
result = false
else:
gState.identifiers[nimName] = name
result = true
# Overrides related
proc getOverride*(gState: State, name: string, kind: NimSymKind): string =
# Get cOverride for identifier `name` of `kind` if defined
doAssert name.nBl, "Blank identifier error"
if gState.onSymbolOverride != nil:
var
nname = gState.getIdentifier(name, kind, "IgnoreSkipSymbol")
sym = Symbol(name: nname, kind: kind)
if nname.nBl:
gState.onSymbolOverride(sym)
if sym.override.nBl and gState.addNewIdentifer(nname, override = true):
result = sym.override
if kind != nskProc:
result = " " & result.replace("\n", "\n ")
proc getOverrideFinal*(gState: State, kind: NimSymKind): string =
# Get all unused cOverride symbols of `kind`
let
typ = $kind
if gState.onSymbolOverrideFinal != nil:
for i in gState.onSymbolOverrideFinal(typ):
result &= "\n" & gState.getOverride(i, kind)
proc getKeyword*(kind: NimSymKind): string =
# Convert `kind` into a Nim keyword
# cOverride procs already include `proc` keyword
result = ($kind).replace("nsk", "").toLowerAscii()
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
sfile = fullpath.sanitizePath(noQuote = true)
sfileName = sfile.extractFilename()
pDir = sfile.expandFilename().parentDir()
includeDirs: seq[string]
args.add @["-E", "-dD", getGccModeArg(gState.mode), "-w"]
if not gState.noComments:
args.add "-CC"
for inc in gState.includeDirs:
args.add &"-I{inc.sanitizePath}"
includeDirs.add inc.absolutePath().sanitizePath(noQuote = true)
for def in gState.defines:
args.add &"-D{def}"
# Remove gcc special calls
# https://github.com/tree-sitter/tree-sitter-c/issues/43
args.add @["-D__attribute__(x)=", "-D__restrict=", "-D__restrict__=", "-D__extension__=", "-D__inline__=inline",
"-D__inline=inline", "-D_Noreturn=", &"{fullpath.sanitizePath}"]
# Include content only from file
var
p = startProcess(getCompiler(), args = args, options = {poStdErrToStdOut, poUsePath})
outp = p.outputStream()
line = ""
newHeaders: HashSet[string]
# Include content only from file
gState.code = ""
while true:
if outp.readLine(line):
# We want to keep blank lines here for comment processing
if line.len > 10 and line[0] == '#' and line[1] == ' ' and line.contains('"'):
# # 1 "path/to/file.h" 1
start = false
line = line.split('"')[1].sanitizePath(noQuote = true)
if sfile == line or
(DirSep notin line and sfileName == line):
start = true
elif gState.recurse:
if (pDir.Bl or pDir in line) and line notin gState.headersProcessed:
newHeaders.incl line
start = gState.isIncluded(line)
else:
for inc in includeDirs:
if line.startsWith(inc) and line notin gState.headersProcessed:
newHeaders.incl line
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)" &
"\n\nERROR:$1\n" % line.split(": fatal error:")[1]
else:
if start:
if "#undef" in line:
continue
gState.code.add line & "\n"
elif not p.running(): break
p.close()
assert p.peekExitCode() == 0,
gState.code & "\n\nFailed in preprocessing:\n " &
getCompiler() & " " & args.join(" ")
gState.headersProcessed.incl newHeaders
# Plugin related
proc dll*(path: string): string =
let
(dir, name, _) = path.splitFile()
result = dir / (DynlibFormat % name)
proc loadPlugin*(gState: State, sourcePath: string) =
doAssert fileExists(sourcePath), "Plugin file does not exist: " & sourcePath
let
pdll = sourcePath.dll
if not fileExists(pdll) or
sourcePath.getLastModificationTime() > pdll.getLastModificationTime():
let
# Get Nim configuration flags if not already specified in a .cfg file
flags =
if fileExists(sourcePath & ".cfg"): ""
else: getNimConfigFlags(getCurrentDir())
# Always set output to same directory as source, prevents override
outflags = &"--out:\"{pdll}\""
# Compile plugin as library with `markAndSweep` GC
cmd = &"{gState.nim} c --app:lib --gc:markAndSweep {flags} {outflags} {sourcePath.sanitizePath}"
(output, ret) = execAction(cmd, die = false)
doAssert ret == 0, output & "\nFailed to compile cPlugin()\n\ncmd: " & cmd
doAssert fileExists(pdll), "No plugin binary generated for " & sourcePath
let lib = loadLib(pdll)
doAssert lib != nil, "Plugin $1 compiled to $2 failed to load" % [sourcePath, pdll]
gState.onSymbol = cast[OnSymbol](lib.symAddr("onSymbol"))
gState.onSymbolOverride = cast[OnSymbol](lib.symAddr("onSymbolOverride"))
gState.onSymbolOverrideFinal = cast[OnSymbolOverrideFinal](lib.symAddr("onSymbolOverrideFinal"))
# Misc toast helpers
proc getSplitComma*(joined: seq[string]): seq[string] =
for i in joined:
result = result.concat(i.split(","))
proc expandSymlinkAbs*(path: string): string =
try:
result = path.expandFilename().normalizedPath()
except:
result = path
result = result.sanitizePath(noQuote = true)

View file

@ -0,0 +1,410 @@
import sets, strformat, strutils
import ".."/treesitter/[api, c, cpp]
import ".."/globals
template withCodeAst*(code: string, mode: string, body: untyped): untyped =
## A simple template to inject the TSNode into a body of code
mixin treeSitterC
mixin treeSitterCpp
var parser = tsParserNew()
defer:
parser.tsParserDelete()
doAssert code.nBl, "Empty code or preprocessor error"
if mode == "c":
doAssert parser.tsParserSetLanguage(treeSitterC()), "Failed to load C parser"
elif mode == "cpp":
doAssert parser.tsParserSetLanguage(treeSitterCpp()), "Failed to load C++ parser"
else:
doAssert false, "Invalid parser " & mode
var
tree = parser.tsParserParseString(nil, code.cstring, code.len.uint32)
root {.inject.} = tree.tsTreeRootNode()
body
defer:
tree.tsTreeDelete()
proc isNil*(node: TSNode): bool =
node.tsNodeIsNull()
proc len*(node: TSNode): int =
if not node.isNil:
result = node.tsNodeNamedChildCount().int
proc `[]`*(node: TSNode, i: SomeInteger): TSNode =
if i < type(i)(node.len()):
result = node.tsNodeNamedChild(i.uint32)
proc getName*(node: TSNode): string {.inline.} =
if not node.isNil:
return $node.tsNodeType()
proc getNodeVal*(code: var string, node: TSNode): string =
if not node.isNil:
return code[node.tsNodeStartByte() .. node.tsNodeEndByte()-1]
proc getNodeVal*(gState: State, node: TSNode): string =
gState.code.getNodeVal(node)
proc getAtom*(node: TSNode): TSNode =
if not node.isNil:
# Get child node which is topmost atom
if node.getName() in gAtoms:
return node
elif node.len != 0:
if node[0].getName() in ["type_qualifier", "comment"]:
# Skip const, volatile
if node.len > 1:
return node[1].getAtom()
else:
return
else:
return node[0].getAtom()
proc getStartAtom*(node: TSNode): int =
if not node.isNil:
# Skip const, volatile and other type qualifiers
for i in 0 .. node.len - 1:
if node[i].getAtom().getName() notin gAtoms:
result += 1
else:
break
proc getConstQualifier*(gState: State, node: TSNode): bool =
# Check if node siblings have type_qualifier = `const`
var
curr = node.tsNodePrevNamedSibling()
while not curr.isNil:
# Check previous siblings
if curr.getName() == "type_qualifier" and
gState.getNodeVal(curr) == "const":
return true
curr = curr.tsNodePrevNamedSibling()
# Check immediate next sibling
curr = node.tsNodePrevNamedSibling()
if curr.getName() == "type_qualifier" and
gState.getNodeVal(curr) == "const":
return true
proc getXCount*(node: TSNode, ntype: string, reverse = false): int =
if not node.isNil:
# Get number of ntype nodes nested in tree
var
cnode = node
while ntype in cnode.getName():
result += 1
if reverse:
cnode = cnode.tsNodeParent()
else:
if cnode.len != 0:
if cnode[0].getName() == "type_qualifier":
# Skip const, volatile
if cnode.len > 1:
cnode = cnode[1]
else:
break
else:
cnode = cnode[0]
else:
break
proc getPtrCount*(node: TSNode, reverse = false): int =
node.getXCount("pointer_declarator", reverse)
proc getArrayCount*(node: TSNode, reverse = false): int =
node.getXCount("array_declarator")
proc getDeclarator*(node: TSNode): TSNode =
if not node.isNil:
# Return if child is a function or array declarator
if node.getName() in ["function_declarator", "array_declarator"]:
return node
elif node.len != 0:
return node[0].getDeclarator()
proc getVarargs*(node: TSNode): bool =
# Detect ... and add {.varargs.}
#
# `node` is the param list
#
# ... is an unnamed node, second last node and ) is last node
let
nlen = node.tsNodeChildCount()
if nlen > 1.uint32:
let
nval = node.tsNodeChild(nlen - 2.uint32).getName()
if nval == "...":
result = true
proc firstChildInTree*(node: TSNode, ntype: string): TSNode =
# Search for node type in tree - first children
var
cnode = node
while not cnode.isNil:
if cnode.getName() == ntype:
return cnode
if cnode.len != 0:
for i in 0 ..< cnode.len:
if cnode[i].getName() != "comment":
cnode = cnode[i]
break
else:
cnode = cnode[0]
proc anyChildInTree*(node: TSNode, ntype: string): TSNode =
# Search for node type anywhere in tree - depth first
var
cnode = node
while not cnode.isNil:
if cnode.getName() == ntype:
return cnode
for i in 0 ..< cnode.len:
let
ccnode = cnode[i].anyChildInTree(ntype)
if not ccnode.isNil:
return ccnode
if cnode != node:
cnode = cnode.tsNodeNextNamedSibling()
else:
break
proc mostNestedChildInTree*(node: TSNode): TSNode =
# Search for the most nested child of node's type in tree
var
cnode = node
ntype = cnode.getName()
while not cnode.isNil and cnode.len != 0 and cnode[0].getName() == ntype:
cnode = cnode[0]
result = cnode
proc inChildren*(node: TSNode, ntype: string): bool =
# Search for node type in immediate children
result = false
for i in 0 ..< node.len:
if (node[i]).getName() == ntype:
result = true
break
proc getLineCol*(code: var string, node: TSNode): tuple[line, col: int] =
# Get line number and column info for node
let
point = node.tsNodeStartPoint()
result.line = point.row.int + 1
result.col = point.column.int + 1
proc getLineCol*(gState: State, node: TSNode): tuple[line, col: int] =
getLineCol(gState.code, node)
proc getEndLineCol*(code: var string, node: TSNode): tuple[line, col: int] =
# Get line number and column info for node
let
point = node.tsNodeEndPoint()
result.line = point.row.int + 1
result.col = point.column.int + 1
proc getEndLineCol*(gState: State, node: TSNode): tuple[line, col: int] =
getEndLineCol(gState.code, node)
proc getTSNodeNamedChildCountSansComments*(node: TSNode): int =
for i in 0 ..< node.len:
if node.getName() != "comment":
result += 1
proc getPxName*(node: TSNode, offset: int): string =
# Get the xth (grand)parent of the node
var
np = node
count = 0
while not np.isNil and count < offset:
np = np.tsNodeParent()
count += 1
if count == offset and not np.isNil:
return np.getName()
proc printLisp*(code: var string, root: TSNode): string =
var
node = root
nextnode: TSNode
depth = 0
while true:
if not node.isNil and depth > -1:
result &= spaces(depth)
let
(line, col) = code.getLineCol(node)
result &= &"({$node.tsNodeType()} {line} {col} {node.tsNodeEndByte() - node.tsNodeStartByte()}"
let
val = code.getNodeVal(node)
if "\n" notin val and " " notin val:
result &= &" \"{val}\""
else:
break
if node.len() != 0:
result &= "\n"
nextnode = node[0]
depth += 1
else:
result &= ")\n"
nextnode = node.tsNodeNextNamedSibling()
if nextnode.isNil:
while true:
node = node.tsNodeParent()
depth -= 1
if depth == -1:
break
result &= spaces(depth) & ")\n"
if node == root:
break
if not node.tsNodeNextNamedSibling().isNil:
node = node.tsNodeNextNamedSibling()
break
else:
node = nextnode
if node == root:
break
proc printLisp*(gState: State, root: TSNode): string =
printLisp(gState.code, root)
proc printDebug*(gState: State, node: TSNode) =
if gState.debug:
gecho ("Input => " & gState.getNodeVal(node)).getCommented()
gecho gState.printLisp(node).getCommented()
proc getCommentsStr*(gState: State, commentNodes: seq[TSNode]): string =
## Generate a comment from a set of comment nodes. Comment is guaranteed
## to be able to be rendered using nim doc
if commentNodes.len > 0:
for commentNode in commentNodes:
result &= "\n " & gState.getNodeVal(commentNode).strip()
result = "```\n " & result.multiReplace(
{
"/**": "", "**/": "", "/*": "",
"*/": "", "/*": "", "//": "",
"\n": "\n ", "`": ""
}
# need to replace this last otherwise it supercedes other replacements
).replace(" *", "").strip() & "\n```"
proc getCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] =
## Get a set of comment nodes in order of priority. Will search up to ``maxSearch``
## nodes before and after the current node
##
## Priority is (closest line number) > comment before > comment after.
## This priority might need to be changed based on the project, but
## for now it is good enough
# Skip this if we don't want comments
if gState.noComments:
return
let (line, _) = gState.getLineCol(node)
# Keep track of both directions from a node
var
prevSibling = node.tsNodePrevNamedSibling()
nextSibling = node.tsNodeNextNamedSibling()
nilNode: TSNode
var
i = 0
prevSiblingDistance, nextSiblingDistance: int = int.high
lowestDistance: int
commentsFound = false
while not commentsFound and i < maxSearch:
# Distance from the current node will tell us approximately if the
# comment belongs to the node. The closer it is in terms of line
# numbers, the more we can be sure it's the comment we want
if not prevSibling.isNil:
if prevSibling.getName() == "comment":
prevSiblingDistance = abs(gState.getEndLineCol(prevSibling)[0] - line)
else:
prevSiblingDistance = int.high
if not nextSibling.isNil:
if nextSibling.getName() == "comment":
nextSiblingDistance = abs(gState.getLineCol(nextSibling)[0] - line)
else:
nextSiblingDistance = int.high
lowestDistance = min(prevSiblingDistance, nextSiblingDistance)
if prevSiblingDistance > maxSearch:
# If the line is out of range, skip searching
prevSibling = nilNode # Can't do `= nil`
if nextSiblingDistance > maxSearch:
# If the line is out of range, skip searching
nextSibling = nilNode
# Search above the current line for comments. When one is found
# keep going to retrieve successive comments for cases with multiple
# `//` style comments
while (
not prevSibling.isNil and
prevSibling.getName() == "comment" and
prevSiblingDistance == lowestDistance
):
# Put the previous nodes in reverse order so the comments
# make logical sense
result.insert(prevSibling, 0)
prevSibling = prevSibling.tsNodePrevNamedSibling()
commentsFound = true
# If we've already found comments above the current line, quit
if commentsFound:
break
# Search below or at the current line for comments. When one is found
# keep going to retrieve successive comments for cases with multiple
# `//` style comments
while (
not nextSibling.isNil and
nextSibling.getName() == "comment" and
nextSiblingDistance == lowestDistance
):
result.add(nextSibling)
nextSibling = nextSibling.tsNodeNextNamedSibling()
commentsFound = true
if commentsFound:
break
# Go to next sibling pair
if not prevSibling.isNil:
prevSibling = prevSibling.tsNodePrevNamedSibling()
if not nextSibling.isNil:
nextSibling = nextSibling.tsNodeNextNamedSibling()
i += 1
proc getTSNodeNamedChildNames*(node: TSNode): seq[string] =
if node.tsNodeNamedChildCount() != 0:
for i in 0 .. node.tsNodeNamedChildCount()-1:
let
name = $node.tsNodeNamedChild(i).tsNodeType()
if name != "comment":
result.add(name)
proc getNodeError*(gState: State, node: TSNode): bool =
let
err = node.anyChildInTree("ERROR")
if not err.isNil:
# Bail on errors
gState.printDebug(node)
gecho &"# tree-sitter parse error: '{gState.getNodeVal(node).splitLines()[0]}', skipped"
result = true

259
nimterop/treesitter/api.nim Normal file
View file

@ -0,0 +1,259 @@
{.experimental: "codeReordering".}
import strutils, os
include ".."/enumtype
import ".."/[paths, setup]
static:
treesitterSetup()
const sourcePath = cacheDir / "treesitter" / "lib"
when defined(Linux) and defined(gcc):
{.passC: "-std=c11".}
{.passC: "-I$1" % (sourcePath / "include").}
{.passC: "-I$1" % (sourcePath / "src").}
{.compile: sourcePath / "src" / "lib.c".}
### Generated below
{.push hint[ConvFromXtoItselfNotNeeded]: off.}
{.pragma: impapiHdr, header: sourcePath / "include" / "tree_sitter" / "api.h".}
defineEnum(TSInputEncoding)
defineEnum(TSSymbolType)
defineEnum(TSLogType)
defineEnum(TSQueryPredicateStepType)
defineEnum(TSQueryError)
const
TREE_SITTER_LANGUAGE_VERSION* = 11
TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION* = 9
TSInputEncodingUTF8* = (0).TSInputEncoding
TSInputEncodingUTF16* = (TSInputEncodingUTF8 + 1).TSInputEncoding
TSSymbolTypeRegular* = (0).TSSymbolType
TSSymbolTypeAnonymous* = (TSSymbolTypeRegular + 1).TSSymbolType
TSSymbolTypeAuxiliary* = (TSSymbolTypeAnonymous + 1).TSSymbolType
TSLogTypeParse* = (0).TSLogType
TSLogTypeLex* = (TSLogTypeParse + 1).TSLogType
TSQueryPredicateStepTypeDone* = (0).TSQueryPredicateStepType
TSQueryPredicateStepTypeCapture* = (TSQueryPredicateStepTypeDone + 1).TSQueryPredicateStepType
TSQueryPredicateStepTypeString* = (TSQueryPredicateStepTypeCapture + 1).TSQueryPredicateStepType
TSQueryErrorNone* = (0).TSQueryError
TSQueryErrorSyntax* = (TSQueryErrorNone + 1).TSQueryError
TSQueryErrorNodeType* = (TSQueryErrorSyntax + 1).TSQueryError
TSQueryErrorField* = (TSQueryErrorNodeType + 1).TSQueryError
TSQueryErrorCapture* = (TSQueryErrorField + 1).TSQueryError
type
TSSymbol* {.importc, impapiHdr.} = uint16
TSFieldId* {.importc, impapiHdr.} = uint16
TSLanguage* {.importc, impapiHdr, incompleteStruct.} = object
TSParser* {.importc, impapiHdr, incompleteStruct.} = object
TSTree* {.importc, impapiHdr, incompleteStruct.} = object
TSQuery* {.importc, impapiHdr, incompleteStruct.} = object
TSQueryCursor* {.importc, impapiHdr, incompleteStruct.} = object
TSPoint* {.bycopy, importc, impapiHdr.} = object
row*: uint32
column*: uint32
TSRange* {.bycopy, importc, impapiHdr.} = object
start_point*: TSPoint
end_point*: TSPoint
start_byte*: uint32
end_byte*: uint32
TSInput* {.bycopy, importc, impapiHdr.} = object
payload*: pointer
read*: proc (payload: pointer; byte_index: uint32; position: TSPoint;
bytes_read: ptr uint32): cstring {.cdecl.}
encoding*: TSInputEncoding
TSLogger* {.bycopy, importc, impapiHdr.} = object
payload*: pointer
log*: proc (payload: pointer; a2: TSLogType; a3: cstring) {.cdecl.}
TSInputEdit* {.bycopy, importc, impapiHdr.} = object
start_byte*: uint32
old_end_byte*: uint32
new_end_byte*: uint32
start_point*: TSPoint
old_end_point*: TSPoint
new_end_point*: TSPoint
TSNode* {.bycopy, importc, impapiHdr.} = object
context*: array[4, uint32]
id*: pointer
tree*: ptr TSTree
TSTreeCursor* {.bycopy, importc, impapiHdr.} = object
tree*: pointer
id*: pointer
context*: array[2, uint32]
TSQueryCapture* {.bycopy, importc, impapiHdr.} = object
node*: TSNode
index*: uint32
TSQueryMatch* {.bycopy, importc, impapiHdr.} = object
id*: uint32
pattern_index*: uint16
capture_count*: uint16
captures*: ptr TSQueryCapture
TSQueryPredicateStep* {.bycopy, importc, impapiHdr.} = object
`type`*: TSQueryPredicateStepType
value_id*: uint32
proc ts_parser_new*(): ptr TSParser {.importc, cdecl, impapiHdr.}
proc ts_parser_delete*(parser: ptr TSParser) {.importc, cdecl, impapiHdr.}
proc ts_parser_set_language*(self: ptr TSParser; language: ptr TSLanguage): bool {.
importc, cdecl, impapiHdr.}
proc ts_parser_language*(self: ptr TSParser): ptr TSLanguage {.importc, cdecl, impapiHdr.}
proc ts_parser_set_included_ranges*(self: ptr TSParser; ranges: ptr TSRange;
length: uint32) {.importc, cdecl, impapiHdr.}
proc ts_parser_included_ranges*(self: ptr TSParser; length: ptr uint32): ptr TSRange {.
importc, cdecl, impapiHdr.}
proc ts_parser_parse*(self: ptr TSParser; old_tree: ptr TSTree; input: TSInput): ptr TSTree {.
importc, cdecl, impapiHdr.}
proc ts_parser_parse_string*(self: ptr TSParser; old_tree: ptr TSTree; string: cstring;
length: uint32): ptr TSTree {.importc, cdecl, impapiHdr.}
proc ts_parser_parse_string_encoding*(self: ptr TSParser; old_tree: ptr TSTree;
string: cstring; length: uint32;
encoding: TSInputEncoding): ptr TSTree {.
importc, cdecl, impapiHdr.}
proc ts_parser_reset*(self: ptr TSParser) {.importc, cdecl, impapiHdr.}
proc ts_parser_set_timeout_micros*(self: ptr TSParser; timeout: uint64) {.importc,
cdecl, impapiHdr.}
proc ts_parser_timeout_micros*(self: ptr TSParser): uint64 {.importc, cdecl, impapiHdr.}
proc ts_parser_set_cancellation_flag*(self: ptr TSParser; flag: ptr uint) {.importc,
cdecl, impapiHdr.}
proc ts_parser_cancellation_flag*(self: ptr TSParser): ptr uint {.importc, cdecl,
impapiHdr.}
proc ts_parser_set_logger*(self: ptr TSParser; logger: TSLogger) {.importc, cdecl,
impapiHdr.}
proc ts_parser_logger*(self: ptr TSParser): TSLogger {.importc, cdecl, impapiHdr.}
proc ts_parser_print_dot_graphs*(self: ptr TSParser; file: cint) {.importc, cdecl,
impapiHdr.}
proc ts_parser_halt_on_error*(self: ptr TSParser; halt: bool) {.importc, cdecl,
impapiHdr.}
proc ts_tree_copy*(self: ptr TSTree): ptr TSTree {.importc, cdecl, impapiHdr.}
proc ts_tree_delete*(self: ptr TSTree) {.importc, cdecl, impapiHdr.}
proc ts_tree_root_node*(self: ptr TSTree): TSNode {.importc, cdecl, impapiHdr.}
proc ts_tree_language*(a1: ptr TSTree): ptr TSLanguage {.importc, cdecl, impapiHdr.}
proc ts_tree_edit*(self: ptr TSTree; edit: ptr TSInputEdit) {.importc, cdecl, impapiHdr.}
proc ts_tree_get_changed_ranges*(old_tree: ptr TSTree; new_tree: ptr TSTree;
length: ptr uint32): ptr TSRange {.importc, cdecl,
impapiHdr.}
proc ts_tree_print_dot_graph*(a1: ptr TSTree; a2: File) {.importc, cdecl, impapiHdr.}
proc ts_node_type*(a1: TSNode): cstring {.importc, cdecl, impapiHdr.}
proc ts_node_symbol*(a1: TSNode): TSSymbol {.importc, cdecl, impapiHdr.}
proc ts_node_start_byte*(a1: TSNode): uint32 {.importc, cdecl, impapiHdr.}
proc ts_node_start_point*(a1: TSNode): TSPoint {.importc, cdecl, impapiHdr.}
proc ts_node_end_byte*(a1: TSNode): uint32 {.importc, cdecl, impapiHdr.}
proc ts_node_end_point*(a1: TSNode): TSPoint {.importc, cdecl, impapiHdr.}
proc ts_node_string*(a1: TSNode): cstring {.importc, cdecl, impapiHdr.}
proc ts_node_is_null*(a1: TSNode): bool {.importc, cdecl, impapiHdr.}
proc ts_node_is_named*(a1: TSNode): bool {.importc, cdecl, impapiHdr.}
proc ts_node_is_missing*(a1: TSNode): bool {.importc, cdecl, impapiHdr.}
proc ts_node_is_extra*(a1: TSNode): bool {.importc, cdecl, impapiHdr.}
proc ts_node_has_changes*(a1: TSNode): bool {.importc, cdecl, impapiHdr.}
proc ts_node_has_error*(a1: TSNode): bool {.importc, cdecl, impapiHdr.}
proc ts_node_parent*(a1: TSNode): TSNode {.importc, cdecl, impapiHdr.}
proc ts_node_child*(a1: TSNode; a2: uint32): TSNode {.importc, cdecl, impapiHdr.}
proc ts_node_child_count*(a1: TSNode): uint32 {.importc, cdecl, impapiHdr.}
proc ts_node_named_child*(a1: TSNode; a2: uint32): TSNode {.importc, cdecl, impapiHdr.}
proc ts_node_named_child_count*(a1: TSNode): uint32 {.importc, cdecl, impapiHdr.}
proc ts_node_child_by_field_name*(self: TSNode; field_name: cstring;
field_name_length: uint32): TSNode {.importc,
cdecl, impapiHdr.}
proc ts_node_child_by_field_id*(a1: TSNode; a2: TSFieldId): TSNode {.importc, cdecl,
impapiHdr.}
proc ts_node_next_sibling*(a1: TSNode): TSNode {.importc, cdecl, impapiHdr.}
proc ts_node_prev_sibling*(a1: TSNode): TSNode {.importc, cdecl, impapiHdr.}
proc ts_node_next_named_sibling*(a1: TSNode): TSNode {.importc, cdecl, impapiHdr.}
proc ts_node_prev_named_sibling*(a1: TSNode): TSNode {.importc, cdecl, impapiHdr.}
proc ts_node_first_child_for_byte*(a1: TSNode; a2: uint32): TSNode {.importc, cdecl,
impapiHdr.}
proc ts_node_first_named_child_for_byte*(a1: TSNode; a2: uint32): TSNode {.importc,
cdecl, impapiHdr.}
proc ts_node_descendant_for_byte_range*(a1: TSNode; a2: uint32; a3: uint32): TSNode {.
importc, cdecl, impapiHdr.}
proc ts_node_descendant_for_point_range*(a1: TSNode; a2: TSPoint; a3: TSPoint): TSNode {.
importc, cdecl, impapiHdr.}
proc ts_node_named_descendant_for_byte_range*(a1: TSNode; a2: uint32; a3: uint32): TSNode {.
importc, cdecl, impapiHdr.}
proc ts_node_named_descendant_for_point_range*(a1: TSNode; a2: TSPoint; a3: TSPoint): TSNode {.
importc, cdecl, impapiHdr.}
proc ts_node_edit*(a1: ptr TSNode; a2: ptr TSInputEdit) {.importc, cdecl, impapiHdr.}
proc ts_node_eq*(a1: TSNode; a2: TSNode): bool {.importc, cdecl, impapiHdr.}
proc ts_tree_cursor_new*(a1: TSNode): TSTreeCursor {.importc, cdecl, impapiHdr.}
proc ts_tree_cursor_delete*(a1: ptr TSTreeCursor) {.importc, cdecl, impapiHdr.}
proc ts_tree_cursor_reset*(a1: ptr TSTreeCursor; a2: TSNode) {.importc, cdecl, impapiHdr.}
proc ts_tree_cursor_current_node*(a1: ptr TSTreeCursor): TSNode {.importc, cdecl,
impapiHdr.}
proc ts_tree_cursor_current_field_name*(a1: ptr TSTreeCursor): cstring {.importc,
cdecl, impapiHdr.}
proc ts_tree_cursor_current_field_id*(a1: ptr TSTreeCursor): TSFieldId {.importc,
cdecl, impapiHdr.}
proc ts_tree_cursor_goto_parent*(a1: ptr TSTreeCursor): bool {.importc, cdecl,
impapiHdr.}
proc ts_tree_cursor_goto_next_sibling*(a1: ptr TSTreeCursor): bool {.importc, cdecl,
impapiHdr.}
proc ts_tree_cursor_goto_first_child*(a1: ptr TSTreeCursor): bool {.importc, cdecl,
impapiHdr.}
proc ts_tree_cursor_goto_first_child_for_byte*(a1: ptr TSTreeCursor; a2: uint32): int64 {.
importc, cdecl, impapiHdr.}
proc ts_tree_cursor_copy*(a1: ptr TSTreeCursor): TSTreeCursor {.importc, cdecl,
impapiHdr.}
proc ts_query_new*(language: ptr TSLanguage; source: cstring; source_len: uint32;
error_offset: ptr uint32; error_type: ptr TSQueryError): ptr TSQuery {.
importc, cdecl, impapiHdr.}
proc ts_query_delete*(a1: ptr TSQuery) {.importc, cdecl, impapiHdr.}
proc ts_query_pattern_count*(a1: ptr TSQuery): uint32 {.importc, cdecl, impapiHdr.}
proc ts_query_capture_count*(a1: ptr TSQuery): uint32 {.importc, cdecl, impapiHdr.}
proc ts_query_string_count*(a1: ptr TSQuery): uint32 {.importc, cdecl, impapiHdr.}
proc ts_query_start_byte_for_pattern*(a1: ptr TSQuery; a2: uint32): uint32 {.importc,
cdecl, impapiHdr.}
proc ts_query_predicates_for_pattern*(self: ptr TSQuery; pattern_index: uint32;
length: ptr uint32): ptr TSQueryPredicateStep {.
importc, cdecl, impapiHdr.}
proc ts_query_capture_name_for_id*(a1: ptr TSQuery; id: uint32; length: ptr uint32): cstring {.
importc, cdecl, impapiHdr.}
proc ts_query_string_value_for_id*(a1: ptr TSQuery; id: uint32; length: ptr uint32): cstring {.
importc, cdecl, impapiHdr.}
proc ts_query_disable_capture*(a1: ptr TSQuery; a2: cstring; a3: uint32) {.importc,
cdecl, impapiHdr.}
proc ts_query_cursor_new*(): ptr TSQueryCursor {.importc, cdecl, impapiHdr.}
proc ts_query_cursor_delete*(a1: ptr TSQueryCursor) {.importc, cdecl, impapiHdr.}
proc ts_query_cursor_exec*(a1: ptr TSQueryCursor; a2: ptr TSQuery; a3: TSNode) {.importc,
cdecl, impapiHdr.}
proc ts_query_cursor_set_byte_range*(a1: ptr TSQueryCursor; a2: uint32; a3: uint32) {.
importc, cdecl, impapiHdr.}
proc ts_query_cursor_set_point_range*(a1: ptr TSQueryCursor; a2: TSPoint; a3: TSPoint) {.
importc, cdecl, impapiHdr.}
proc ts_query_cursor_next_match*(a1: ptr TSQueryCursor; match: ptr TSQueryMatch): bool {.
importc, cdecl, impapiHdr.}
proc ts_query_cursor_remove_match*(a1: ptr TSQueryCursor; id: uint32) {.importc, cdecl,
impapiHdr.}
proc ts_query_cursor_next_capture*(a1: ptr TSQueryCursor; match: ptr TSQueryMatch;
capture_index: ptr uint32): bool {.importc, cdecl,
impapiHdr.}
proc ts_language_symbol_count*(a1: ptr TSLanguage): uint32 {.importc, cdecl, impapiHdr.}
proc ts_language_symbol_name*(a1: ptr TSLanguage; a2: TSSymbol): cstring {.importc,
cdecl, impapiHdr.}
proc ts_language_symbol_for_name*(self: ptr TSLanguage; string: cstring;
length: uint32; is_named: bool): TSSymbol {.importc,
cdecl, impapiHdr.}
proc ts_language_field_count*(a1: ptr TSLanguage): uint32 {.importc, cdecl, impapiHdr.}
proc ts_language_field_name_for_id*(a1: ptr TSLanguage; a2: TSFieldId): cstring {.
importc, cdecl, impapiHdr.}
proc ts_language_field_id_for_name*(a1: ptr TSLanguage; a2: cstring; a3: uint32): TSFieldId {.
importc, cdecl, impapiHdr.}
proc ts_language_symbol_type*(a1: ptr TSLanguage; a2: TSSymbol): TSSymbolType {.
importc, cdecl, impapiHdr.}
proc ts_language_version*(a1: ptr TSLanguage): uint32 {.importc, cdecl, impapiHdr.}
{.pop.}

16
nimterop/treesitter/c.nim Normal file
View file

@ -0,0 +1,16 @@
import strutils, os
import ".."/[setup, paths]
static:
treesitterCSetup()
const srcDir = cacheDir / "treesitter_c" / "src"
import "."/api
{.passC: "-I$1" % srcDir.}
{.compile: srcDir / "parser.c".}
proc treeSitterC*(): ptr TSLanguage {.importc: "tree_sitter_c".}

View file

@ -0,0 +1,21 @@
import strutils, os
import ".."/[setup, paths]
import ".."/build/shell
static:
treesitterCppSetup()
const srcDir = cacheDir / "treesitter_cpp" / "src"
{.passC: "-I$1" % srcDir.}
import "."/api
static:
cpFile(srcDir / "parser.c", srcDir / "parser_cpp.c")
{.compile: srcDir / "parser_cpp.c".}
{.compile: srcDir / "scanner.cc".}
proc treeSitterCpp*(): ptr TSLanguage {.importc: "tree_sitter_cpp".}

View file

@ -0,0 +1,11 @@
# nim c tsgen.nim > temp.nim
# Move temp.nim contents to api.nim below generated line + minor adjustments
import os
import nimterop/[cimport, paths]
static:
cDebug()
cImport(cacheDir / "treesitter" / "lib" / "include" / "tree_sitter" / "api.h", flags = "-E_ -c")

View file

@ -1,201 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
<!-- Google fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
<!-- CSS -->
<title>paths</title>
<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
<script type="text/javascript" src="dochack.js"></script>
<script type="text/javascript">
function main() {
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
toggleSwitch.addEventListener('change', switchTheme, false);
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-theme', "dark");
toggleSwitch.checked = true;
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
document.documentElement.setAttribute('data-theme', "light");
toggleSwitch.checked = false;
} else {
const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null;
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
if (currentTheme === 'dark') {
toggleSwitch.checked = true;
}
}
}
}
</script>
</head>
<body onload="main()">
<div class="document" id="documentId">
<div class="container">
<h1 class="title">paths</h1>
<div class="row">
<div class="three columns">
<div class="theme-switch-wrapper">
<label class="theme-switch" for="checkbox">
<input type="checkbox" id="checkbox" />
<div class="slider round"></div>
</label>
&nbsp;&nbsp;&nbsp; <em>Dark Mode</em>
</div>
<div id="global-links">
<ul class="simple">
</ul>
</div>
<div id="searchInputDiv">
Search: <input type="text" id="searchInput"
onkeyup="search()" />
</div>
<div>
Group by:
<select onchange="groupBy(this.value)">
<option value="section">Section</option>
<option value="type">Type</option>
</select>
</div>
<ul class="simple simple-toc" id="toc-list">
<li>
<a class="reference reference-toplevel" href="#6" id="56">Imports</a>
<ul class="simple simple-toc-section">
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#10" id="60">Consts</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#cacheDir"
title="cacheDir = &quot;/home/genotrance/.cache/nim/nimterop/nimterop&quot;"><wbr />cache<wbr />Dir<span class="attachedType"></span></a></li>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#12" id="62">Procs</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#nimteropRoot"
title="nimteropRoot(): string"><wbr />nimterop<wbr />Root<span class="attachedType"></span></a></li>
<li><a class="reference" href="#nimteropSrcDir"
title="nimteropSrcDir(): string"><wbr />nimterop<wbr />Src<wbr />Dir<span class="attachedType"></span></a></li>
<li><a class="reference" href="#toastExePath"
title="toastExePath(): string"><wbr />toast<wbr />Exe<wbr />Path<span class="attachedType"></span></a></li>
<li><a class="reference" href="#testsIncludeDir"
title="testsIncludeDir(): string"><wbr />tests<wbr />Include<wbr />Dir<span class="attachedType"></span></a></li>
</ul>
</li>
</ul>
</div>
<div class="nine columns" id="content">
<div id="tocRoot"></div>
<p class="module-desc"></p>
<div class="section" id="6">
<h1><a class="toc-backref" href="#6">Imports</a></h1>
<dl class="item">
<a class="reference external" href="build.html">build</a>
</dl></div>
<div class="section" id="10">
<h1><a class="toc-backref" href="#10">Consts</a></h1>
<dl class="item">
<a id="cacheDir"></a>
<dt><pre><a href="paths.html#cacheDir"><span class="Identifier">cacheDir</span></a> <span class="Other">=</span> <span class="StringLit">&quot;/home/genotrance/.cache/nim/nimterop/nimterop&quot;</span></pre></dt>
<dd>
</dd>
</dl></div>
<div class="section" id="12">
<h1><a class="toc-backref" href="#12">Procs</a></h1>
<dl class="item">
<a id="nimteropRoot"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#nimteropRoot"><span class="Identifier">nimteropRoot</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
</dd>
<a id="nimteropSrcDir"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#nimteropSrcDir"><span class="Identifier">nimteropSrcDir</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
</dd>
<a id="toastExePath"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#toastExePath"><span class="Identifier">toastExePath</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
</dd>
<a id="testsIncludeDir"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#testsIncludeDir"><span class="Identifier">testsIncludeDir</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
</dd>
</dl></div>
</div>
</div>
<div class="row">
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small style="color: var(--hint);">Made with Nim. Generated: 2020-03-24 20:55:39 UTC</small>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -1,5 +0,0 @@
cacheDir paths.html#cacheDir paths: cacheDir
nimteropRoot paths.html#nimteropRoot paths: nimteropRoot(): string
nimteropSrcDir paths.html#nimteropSrcDir paths: nimteropSrcDir(): string
toastExePath paths.html#toastExePath paths: toastExePath(): string
testsIncludeDir paths.html#testsIncludeDir paths: testsIncludeDir(): string

View file

@ -1,211 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
<!-- Google fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
<!-- CSS -->
<title>plugin</title>
<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
<script type="text/javascript" src="dochack.js"></script>
<script type="text/javascript">
function main() {
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
toggleSwitch.addEventListener('change', switchTheme, false);
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-theme', "dark");
toggleSwitch.checked = true;
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
document.documentElement.setAttribute('data-theme', "light");
toggleSwitch.checked = false;
} else {
const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null;
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
if (currentTheme === 'dark') {
toggleSwitch.checked = true;
}
}
}
}
</script>
</head>
<body onload="main()">
<div class="document" id="documentId">
<div class="container">
<h1 class="title">plugin</h1>
<div class="row">
<div class="three columns">
<div class="theme-switch-wrapper">
<label class="theme-switch" for="checkbox">
<input type="checkbox" id="checkbox" />
<div class="slider round"></div>
</label>
&nbsp;&nbsp;&nbsp; <em>Dark Mode</em>
</div>
<div id="global-links">
<ul class="simple">
</ul>
</div>
<div id="searchInputDiv">
Search: <input type="text" id="searchInput"
onkeyup="search()" />
</div>
<div>
Group by:
<select onchange="groupBy(this.value)">
<option value="section">Section</option>
<option value="type">Type</option>
</select>
</div>
<ul class="simple simple-toc" id="toc-list">
<li>
<a class="reference reference-toplevel" href="#7" id="57">Types</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#Symbol"
title="Symbol = object
name*: string
parent*: string
kind*: NimSymKind
override*: string"><wbr />Symbol<span class="attachedType"></span></a></li>
<li><a class="reference" href="#OnSymbol"
title="OnSymbol = proc (sym: var Symbol) {.cdecl.}"><wbr />On<wbr />Symbol<span class="attachedType"></span></a></li>
<li><a class="reference" href="#OnSymbolOverrideFinal"
title="OnSymbolOverrideFinal = proc (typ: string): StringHash {.cdecl.}"><wbr />On<wbr />Symbol<wbr />Override<wbr />Final<span class="attachedType"></span></a></li>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#8" id="58">Vars</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#cOverrides"
title="cOverrides: Table[string, StringHash]"><wbr />c<wbr />Overrides<span class="attachedType"></span></a></li>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#12" id="62">Procs</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#onSymbolOverrideFinal%2Cstring"
title="onSymbolOverrideFinal(typ: string): StringHash"><wbr />on<wbr />Symbol<wbr />Override<wbr />Final<span class="attachedType"></span></a></li>
</ul>
</li>
</ul>
</div>
<div class="nine columns" id="content">
<div id="tocRoot"></div>
<p class="module-desc"></p>
<div class="section" id="7">
<h1><a class="toc-backref" href="#7">Types</a></h1>
<dl class="item">
<a id="Symbol"></a>
<dt><pre><a href="plugin.html#Symbol"><span class="Identifier">Symbol</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
<span class="Identifier">name</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">string</span>
<span class="Identifier">parent</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">string</span>
<span class="Identifier">kind</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">NimSymKind</span>
<span class="Identifier">override</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">string</span>
</pre></dt>
<dd>
</dd>
<a id="OnSymbol"></a>
<dt><pre><a href="plugin.html#OnSymbol"><span class="Identifier">OnSymbol</span></a> <span class="Other">=</span> <span class="Keyword">proc</span> <span class="Other">(</span><span class="Identifier">sym</span><span class="Other">:</span> <span class="Keyword">var</span> <a href="plugin.html#Symbol"><span class="Identifier">Symbol</span></a><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">cdecl</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
</dd>
<a id="OnSymbolOverrideFinal"></a>
<dt><pre><a href="plugin.html#OnSymbolOverrideFinal"><span class="Identifier">OnSymbolOverrideFinal</span></a> <span class="Other">=</span> <span class="Keyword">proc</span> <span class="Other">(</span><span class="Identifier">typ</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">StringHash</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">cdecl</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
</dd>
</dl></div>
<div class="section" id="8">
<h1><a class="toc-backref" href="#8">Vars</a></h1>
<dl class="item">
<a id="cOverrides"></a>
<dt><pre><a href="plugin.html#cOverrides"><span class="Identifier">cOverrides</span></a><span class="Other">:</span> <span class="Identifier">Table</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">,</span> <span class="Identifier">StringHash</span><span class="Other">]</span></pre></dt>
<dd>
</dd>
</dl></div>
<div class="section" id="12">
<h1><a class="toc-backref" href="#12">Procs</a></h1>
<dl class="item">
<a id="onSymbolOverrideFinal,string"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#onSymbolOverrideFinal%2Cstring"><span class="Identifier">onSymbolOverrideFinal</span></a><span class="Other">(</span><span class="Identifier">typ</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">StringHash</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">exportc</span><span class="Other">,</span> <span class="Identifier">dynlib</span><span class="Other">,</span>
<span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">KeyError</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>
</dd>
</dl></div>
</div>
</div>
<div class="row">
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small style="color: var(--hint);">Made with Nim. Generated: 2020-03-24 20:55:39 UTC</small>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -1,5 +0,0 @@
Symbol plugin.html#Symbol plugin: Symbol
OnSymbol plugin.html#OnSymbol plugin: OnSymbol
OnSymbolOverrideFinal plugin.html#OnSymbolOverrideFinal plugin: OnSymbolOverrideFinal
cOverrides plugin.html#cOverrides plugin: cOverrides
onSymbolOverrideFinal plugin.html#onSymbolOverrideFinal,string plugin: onSymbolOverrideFinal(typ: string): StringHash

75
tests/getheader.nims Normal file
View file

@ -0,0 +1,75 @@
import strutils
proc testCall(cmd, output: string, exitCode: int, delete = true) =
var
ccmd = "../tests/timeit " & cmd
if not delete:
ccmd = ccmd.replace(" -f ", " ")
var
(outp, exitC) = gorgeEx(ccmd)
echo outp
doAssert exitC == exitCode, $exitC
doAssert outp.contains(output), outp
var
cmd = "nim c -f --hints:off -d:checkAbi"
lrcmd = " -r lzma.nim"
zrcmd = " -r zlib.nim"
sshcmd = " -r libssh2.nim"
lexp = "liblzma version = "
zexp = "zlib version = "
when (NimMajor, NimMinor, NimPatch) >= (1, 2, 0):
cmd &= " --gc:arc"
testCall(cmd & lrcmd, "No build files found", 1)
testCall(cmd & " -d:libssh2Conan" & sshcmd, "Need version for Conan.io uri", 1)
testCall(cmd & " -d:libssh2JBB -d:libssh2SetVer=1.9.0" & sshcmd, "Version in both uri", 1)
when defined(posix):
# stdlib
testCall(cmd & " -d:envTest" & lrcmd, lexp, 0)
testCall(cmd & " -d:envTestStatic" & lrcmd, lexp, 0)
when not defined(osx):
testCall(cmd & " -d:zlibStd" & zrcmd, zexp, 0)
testCall(cmd & " -d:zlibStd -d:zlibStatic" & zrcmd, zexp, 0)
# git tag
testCall(cmd & " -d:lzmaGit -d:lzmaSetVer=v5.2.0" & lrcmd, lexp & "5.2.0", 0)
testCall(cmd & " -d:lzmaGit -d:lzmaStatic -d:lzmaSetVer=v5.2.0" & lrcmd, lexp & "5.2.0", 0, delete = false)
# conan static
testCall(cmd & " -d:libssh2Conan -d:libssh2SetVer=1.9.0 -d:libssh2Static" & sshcmd, zexp, 0)
else:
# conan static for Windows
testCall(cmd & " -d:zlibConan -d:zlibSetVer=1.2.11 -d:zlibStatic" & zrcmd, zexp, 0)
# JBB
testCall(cmd & " -d:libssh2JBB" & sshcmd, zexp, 0)
testCall(cmd & " -d:zlibJBB -d:zlibSetVer=1.2.11" & zrcmd, zexp, 0)
testCall(cmd & " -d:zlibJBB -d:zlibSetVer=1.2.11 -d:zlibStatic" & zrcmd, zexp, 0, delete = false)
testCall(cmd & " -d:lzmaJBB -d:lzmaSetVer=5.2.4" & lrcmd, lexp & "5.2.4", 0)
# git
testCall(cmd & " -d:envTest" & zrcmd, zexp, 0)
testCall(cmd & " -d:envTestStatic" & zrcmd, zexp, 0, delete = false)
# git tag
testCall(cmd & " -d:zlibGit -d:zlibSetVer=v1.2.10" & zrcmd, zexp & "1.2.10", 0)
testCall(cmd & " -d:zlibGit -d:zlibStatic -d:zlibSetVer=v1.2.10" & zrcmd, zexp & "1.2.10", 0, delete = false)
# dl
testCall(cmd & " -d:lzmaDL" & lrcmd, "Need version", 1)
testCall(cmd & " -d:lzmaDL -d:lzmaSetVer=5.2.4" & lrcmd, lexp & "5.2.4", 0)
testCall(cmd & " -d:lzmaDL -d:lzmaStatic -d:lzmaSetVer=5.2.4" & lrcmd, lexp & "5.2.4", 0, delete = false)
# dl
testCall(cmd & " -d:zlibDL -d:zlibSetVer=1.2.11" & zrcmd, zexp & "1.2.11", 0)
testCall(cmd & " -d:zlibDL -d:zlibStatic -d:zlibSetVer=1.2.11" & zrcmd, zexp & "1.2.11", 0, delete = false)
# conan
testCall(cmd & " -d:libssh2Conan -d:libssh2SetVer=1.9.0" & sshcmd, zexp, 0)
testCall(cmd & " -d:lzmaConan -d:lzmaSetVer=5.2.4" & lrcmd, lexp & "5.2.4", 0)

603
tests/include/tast2.h Normal file
View file

@ -0,0 +1,603 @@
#ifdef __cplusplus
extern "C" {
#endif
#define A 1
#define B 1.0
#define C 0x10
#define D "hello"
#define E 'c'
#define F 01234
#define UEXPR (1234u << 1)
#define ULEXPR (1234ul << 2)
#define ULLEXPR (1234ull << 3)
#define LEXPR (1234l << 4)
#define LLEXPR (1234ll << 5)
#define SHL1 (1u << 1)
#define SHL2 (1u << 2)
#define SHL3 (1u << 3)
#define COERCE 645635634896ull + 35436
#define COERCE2 645635634896 + 35436ul
#define BINEXPR ~(-(1u << !-1)) ^ (10 >> 1)
#define POINTEREXPR (int*)0
#define POINTERPOINTERPOINTEREXPR (int***)0
#define BOOL true
#define MATHEXPR (1 + 2/3*20 - 100)
#define ANDEXPR (100 & 11000)
#define CASTEXPR (char) 34
#define AVAL 100
#define BVAL 200
#define EQ1 AVAL <= BVAL
#define EQ2 AVAL >= BVAL
#define EQ3 AVAL > BVAL
#define EQ4 AVAL < BVAL
#define EQ5 AVAL != BVAL
#define EQ6 AVAL == BVAL
#define SX_NEAR_ZERO (1.0f / (1 << 28))
// testing integer out of long int range
#define INT_FAST16_MIN (-9223372036854775807L-1)
#define SIZEOF sizeof(char)
#define REG_STR "regular string"
#define NOTSUPPORTEDSTR "not a " REG_STR
#define NULLCHAR '\0'
#define OCTCHAR '\012'
#define HEXCHAR '\xFE'
#define TRICKYSTR "\x4E\034\nfoo\0\'\"\r\v\a\b\e\f\t\\\?bar"
#define ALLSHL (SHL1 | SHL2 | SHL3)
#ifdef NIMTEROP
#define SOME_CONST 8
#endif
struct some_struct_s
{
int x;
};
struct parent_struct_s
{
/* Random comment */
struct some_struct_s s[SOME_CONST];
};
typedef struct some_struct_s SOME_ARRAY[SOME_CONST];
struct A0;
struct A1 {};
typedef struct A2;
typedef struct A3 {};
typedef struct A4 A4, *A4p;
typedef const int A5;
typedef int *A6;
typedef struct A0 **A7;
typedef void *A8;
// Forward declaration
struct A0 {
int f1;
};
struct A4 {
float f1;
};
typedef char *A9p[3], A9[4];
typedef char *A10[3][6];
typedef char *(*A11)[3];
typedef struct A0 *A111[12];
typedef int
**(*A12)(int, int b, int *c, int *, int /*out*/ *count[4], int (*func)(int, int)),
**(*A121)(float, float b, float *c, float *, float *count[4], float (*func)(float, float)),
**(*A122)(char, char b, char *c, char *, char *count[4], char (*func)(char, char));
typedef int (*A13)(int, int, void (*func)(void));
struct A14 { volatile char a1; };
struct A15 { char *a1; const int *a2[1]; };
typedef struct A16 { char f1; };
typedef struct A17 { char *a1; int *a2[1]; } A18, *A18p;
typedef struct { char *a1; int *a2[1]; } A19, *A19p;
typedef struct A20 { char a1; } A20, A21, *A21p;
//Expression
typedef struct A22 { const int **f1; int *f2[123+132]; } A22;
// #231
typedef const char *(*A23)();
//Unions
union U1 {int f1; float f2; };
typedef union U2 { const int **f1; int abc[123+132]; } U2;
// Enums
// Issue #159
#define NK_FLAG(x) (1 << (x))
enum nk_panel_type {
NK_PANEL_NONE = 0,
NK_PANEL_WINDOW = NK_FLAG(0),
NK_PANEL_GROUP = NK_FLAG(1),
NK_PANEL_POPUP = NK_FLAG(2),
NK_PANEL_CONTEXTUAL = NK_FLAG(4),
NK_PANEL_COMBO = NK_FLAG(5),
NK_PANEL_MENU = NK_FLAG(6),
NK_PANEL_TOOLTIP = NK_FLAG(7)
};
enum nk_panel_set {
NK_PANEL_SET_NONBLOCK = NK_PANEL_CONTEXTUAL|NK_PANEL_COMBO|NK_PANEL_MENU|NK_PANEL_TOOLTIP,
NK_PANEL_SET_POPUP = NK_PANEL_SET_NONBLOCK|NK_PANEL_POPUP,
NK_PANEL_SET_SUB = NK_PANEL_SET_POPUP|NK_PANEL_GROUP
};
// Issue #171
typedef enum VSColorFamily {
/* all planar formats */
cmGray = 1000000,
cmRGB = 2000000,
cmYUV = 3000000,
cmYCoCg = 4000000,
/* special for compatibility */
cmCompat = 9000000
} VSColorFamily;
typedef enum VSPresetFormat {
pfNone = 0,
pfGray8 = cmGray + 10,
pfGray16,
pfYUV420P8 = cmYUV + 10,
pfYUV422P8,
pfRGB24 = cmRGB + 10,
pfRGB27,
/* test */
pfCompatBGR32 = cmCompat + 10,
pfCompatYUY2
} VSPresetFormat;
// Anonymous
//typedef struct { char a1; };
//struct A2 test_proc1(struct A0 a);
// Proc vars
void
*(*pcre_malloc)(size_t, ...),
(*pcre_free)(void *),
*(*pcre_stack_malloc)(size_t);
typedef int ImageView, MagickBooleanType;
typedef MagickBooleanType
(*DuplexTransferImageViewMethod)(const ImageView *,const ImageView *,
ImageView *,const size_t,const int,void *),
(*GetImageViewMethod)(const ImageView *,const size_t,const int,void *),
(*SetImageViewMethod)(ImageView *,const size_t,const int,void *),
(*TransferImageViewMethod)(const ImageView *,ImageView *,const size_t,
const int,void *),
(*UpdateImageViewMethod)(ImageView *,const size_t,const int,void *);
// Issue #156, math.h
void
*absfunptr1 (int (*)(struct A0 *)),
**absfunptr2 (int (**)(struct A1 *)),
absfunptr3 (int *(*)(struct A2 *)),
*absfunptr4 (int *(**)(struct A3 *)),
absfunptr5 (int (*a)(A4 *));
int sqlite3_bind_blob(struct A1*, int, const void*, int n, void(*)(void*));
// Issue #174 - type name[] => UncheckedArray[type]
int ucArrFunc1(int text[]);
int ucArrFunc2(int text[][5], int (*func)(int text[]));
typedef int ucArrType1[][5];
struct ucArrType2 {
float f1[5][5];
int *f2[][5];
};
typedef struct fieldfuncfunc {
int *(*func1)(int f1, int *(*sfunc1)(int f1, int *(*ssfunc1)(int f1, ...)));
};
int *func2(int f1, int *(*sfunc2)(int f1, int *(*ssfunc2)(int f1)));
typedef struct {
const char *name; // description
const char *driver; // driver
int flags;
} BASS_DEVICEINFO;
// Issue #183
struct GPU_Target
{
int w, *h;
char *x, y, **z;
};
// Issue #185
struct SDL_AudioCVT;
typedef struct SDL_AudioCVT
{
int needed;
} SDL_AudioCVT;
// Issue #172
typedef struct {
const char* const* x;
} SomeType;
// Nested #137
typedef struct {
struct NT1 { int f1; } f1;
struct { int f1; } f2;
struct NT3 {
/* Random comment */
struct {
int f1;
union NU1 {
float f1;
} f2;
enum { NEV1, NEV2, NEV3 } f3;
} f1;
} f3;
struct { int f1; } f4;
union NU2 { int f1; } f5;
union { int f1; } f6;
enum NE1 { NEV4 = 8, NEV5 } f7;
enum { NEV6 = 8 * 8, NEV7 } f8;
} nested;
static inline int sitest1(int f1) {
return f1 * 2;
}
// Issue #196
typedef int MyInt;
struct TestMyInt {
MyInt f1;
};
// Issue #237
typedef union sx_ivec3 {
struct {
int x;
int y;
struct {
int z;
};
};
int n[3];
} sx_ivec3;
// Issue #236
enum {
SG_INVALID_ID = 0,
SG_NUM_SHADER_STAGES = 2,
SG_MAX_MIPMAPS = 16,
SG_MAX_TEXTUREARRAY_LAYERS = 128
};
struct parenpoin {
void (*gtk_reserved1);
};
// DUPLICATES
#ifdef NOHEADER
#define A 1
#define B 1.0
#define C 0x10
#define D "hello"
#define E 'c'
#define F 01234
#define UEXPR (1234u << 1)
#define ULEXPR (1234ul << 2)
#define ULLEXPR (1234ull << 3)
#define LEXPR (1234l << 4)
#define LLEXPR (1234ll << 5)
#define SHL1 (1u << 1)
#define SHL2 (1u << 2)
#define SHL3 (1u << 3)
#define COERCE 645635634896ull + 35436
#define COERCE2 645635634896 + 35436ul
#define BINEXPR ~(-(1u << !-1)) ^ (10 >> 1)
#define POINTEREXPR (int*)0
#define POINTERPOINTERPOINTEREXPR (int***)0
#define BOOL true
#define MATHEXPR (1 + 2/3*20 - 100)
#define ANDEXPR (100 & 11000)
#define CASTEXPR (char) 34
#define AVAL 100
#define BVAL 200
#define EQ1 AVAL <= BVAL
#define EQ2 AVAL >= BVAL
#define EQ3 AVAL > BVAL
#define EQ4 AVAL < BVAL
#define EQ5 AVAL != BVAL
#define EQ6 AVAL == BVAL
// testing integer out of long int range
#define INT_FAST16_MIN (-9223372036854775807L-1)
#define SIZEOF sizeof(char)
#define REG_STR "regular string"
#define NOTSUPPORTEDSTR "not a " REG_STR
#define NULLCHAR '\0'
#define OCTCHAR '\012'
#define HEXCHAR '\xFE'
#define TRICKYSTR "\x4E\034\nfoo\0\'\"\r\v\a\b\e\f\t\\\?bar"
#define ALLSHL (SHL1 | SHL2 | SHL3)
#ifdef NIMTEROP
#define SOME_CONST 8
#endif
struct some_struct_s
{
int x;
};
struct parent_struct_s
{
/* Random comment */
struct some_struct_s s[SOME_CONST];
};
typedef struct some_struct_s SOME_ARRAY[SOME_CONST];
struct A0;
struct A1 {};
typedef struct A2;
typedef struct A3 {};
typedef struct A4 A4, *A4p;
typedef const int A5;
typedef int *A6;
typedef struct A0 **A7;
typedef void *A8;
// Forward declaration
struct A0 {
int f1;
};
struct A4 {
float f1;
};
typedef char *A9p[3], A9[4];
typedef char *A10[3][6];
typedef char *(*A11)[3];
typedef struct A0 *A111[12];
typedef int
**(*A12)(int, int b, int *c, int *, int /*out*/ *count[4], int (*func)(int, int)),
**(*A121)(float, float b, float *c, float *, float *count[4], float (*func)(float, float)),
**(*A122)(char, char b, char *c, char *, char *count[4], char (*func)(char, char));
typedef int (*A13)(int, int, void (*func)(void));
struct A14 { volatile char a1; };
struct A15 { char *a1; const int *a2[1]; };
typedef struct A16 { char f1; };
typedef struct A17 { char *a1; int *a2[1]; } A18, *A18p;
typedef struct { char *a1; int *a2[1]; } A19, *A19p;
typedef struct A20 { char a1; } A20, A21, *A21p;
//Expression
typedef struct A22 { const int **f1; int *f2[123+132]; } A22;
// #231
typedef const char *(*A23)();
//Unions
union U1 {int f1; float f2; };
typedef union U2 { const int **f1; int abc[123+132]; } U2;
// Enums
// Issue #159
#define NK_FLAG(x) (1 << (x))
enum nk_panel_type {
NK_PANEL_NONE = 0,
NK_PANEL_WINDOW = NK_FLAG(0),
NK_PANEL_GROUP = NK_FLAG(1),
NK_PANEL_POPUP = NK_FLAG(2),
NK_PANEL_CONTEXTUAL = NK_FLAG(4),
NK_PANEL_COMBO = NK_FLAG(5),
NK_PANEL_MENU = NK_FLAG(6),
NK_PANEL_TOOLTIP = NK_FLAG(7)
};
enum nk_panel_set {
NK_PANEL_SET_NONBLOCK = NK_PANEL_CONTEXTUAL|NK_PANEL_COMBO|NK_PANEL_MENU|NK_PANEL_TOOLTIP,
NK_PANEL_SET_POPUP = NK_PANEL_SET_NONBLOCK|NK_PANEL_POPUP,
NK_PANEL_SET_SUB = NK_PANEL_SET_POPUP|NK_PANEL_GROUP
};
// Issue #171
typedef enum VSColorFamily {
/* all planar formats */
cmGray = 1000000,
cmRGB = 2000000,
cmYUV = 3000000,
cmYCoCg = 4000000,
/* special for compatibility */
cmCompat = 9000000
} VSColorFamily;
typedef enum VSPresetFormat {
pfNone = 0,
pfGray8 = cmGray + 10,
pfGray16,
pfYUV420P8 = cmYUV + 10,
pfYUV422P8,
pfRGB24 = cmRGB + 10,
pfRGB27,
/* test */
pfCompatBGR32 = cmCompat + 10,
pfCompatYUY2
} VSPresetFormat;
// Anonymous
//typedef struct { char a1; };
//struct A2 test_proc1(struct A0 a);
// Proc vars
void
*(*pcre_malloc)(size_t, ...),
(*pcre_free)(void *),
*(*pcre_stack_malloc)(size_t);
typedef int ImageView, MagickBooleanType;
typedef MagickBooleanType
(*DuplexTransferImageViewMethod)(const ImageView *,const ImageView *,
ImageView *,const size_t,const int,void *),
(*GetImageViewMethod)(const ImageView *,const size_t,const int,void *),
(*SetImageViewMethod)(ImageView *,const size_t,const int,void *),
(*TransferImageViewMethod)(const ImageView *,ImageView *,const size_t,
const int,void *),
(*UpdateImageViewMethod)(ImageView *,const size_t,const int,void *);
// Issue #156, math.h
void
*absfunptr1 (int (*)(struct A0 *)),
**absfunptr2 (int (**)(struct A1 *)),
absfunptr3 (int *(*)(struct A2 *)),
*absfunptr4 (int *(**)(struct A3 *)),
absfunptr5 (int (*a)(A4 *));
int sqlite3_bind_blob(struct A1*, int, const void*, int n, void(*)(void*));
// Issue #174 - type name[] => UncheckedArray[type]
int ucArrFunc1(int text[]);
int ucArrFunc2(int text[][5], int (*func)(int text[]));
typedef int ucArrType1[][5];
struct ucArrType2 {
float f1[5][5];
int *f2[][5];
};
typedef struct fieldfuncfunc {
int *(*func1)(int f1, int *(*sfunc1)(int f1, int *(*ssfunc1)(int f1, ...)));
};
int *func2(int f1, int *(*sfunc2)(int f1, int *(*ssfunc2)(int f1)));
typedef struct {
const char *name; // description
const char *driver; // driver
int flags;
} BASS_DEVICEINFO;
// Issue #183
struct GPU_Target
{
int w, *h;
char *x, y, **z;
};
// Issue #185
struct SDL_AudioCVT;
typedef struct SDL_AudioCVT
{
int needed;
} SDL_AudioCVT;
// Issue #172
typedef struct {
const char* const* x;
} SomeType;
// Nested #137
typedef struct {
struct NT1 { int f1; } f1;
struct { int f1; } f2;
struct NT3 {
/* Random comment */
struct {
int f1;
union NU1 {
float f1;
} f2;
enum { NEV1, NEV2, NEV3 } f3;
} f1;
} f3;
struct { int f1; } f4;
union NU2 { int f1; } f5;
union { int f1; } f6;
enum NE1 { NEV4 = 8, NEV5 } f7;
enum { NEV6 = 8 * 8, NEV7 } f8;
} nested;
static inline int sitest1(int f1) {
return f1 * 2;
}
// Issue #196
typedef int MyInt;
struct TestMyInt {
MyInt f1;
};
// Issue #237
typedef union sx_ivec3 {
struct {
int x;
int y;
struct z {
int z;
};
};
int n[3];
} sx_ivec3;
// Issue #236
enum {
SG_INVALID_ID = 0,
SG_NUM_SHADER_STAGES = 2,
SG_MAX_MIPMAPS = 16,
SG_MAX_TEXTUREARRAY_LAYERS = 128
};
struct parenpoin {
void (*__gtk_reserved1);
};
#endif
#ifdef __cplusplus
}
#endif

85
tests/include/test.c Normal file
View file

@ -0,0 +1,85 @@
#include "test.h"
int test_call_int() {
return 5;
}
#ifdef FORCE
struct STRUCT1 _test_call_param_(int param1) {
struct STRUCT1 s;
s.field1 = param1;
return s;
}
#endif
STRUCT2 test_call_param2(int param1, STRUCT2 param2) {
STRUCT2 s;
s.field1 = param1 + param2.field1;
return s;
}
STRUCT2 test_call_param3(int param1, struct STRUCT1 param2) {
STRUCT2 s;
s.field1 = param1 + param2.field1;
return s;
}
ENUM2 test_call_param4(enum ENUM param1) {
return enum4;
}
union UNION1 test_call_param5(float param1) {
union UNION1 u;
u.field2 = param1;
return u;
}
unsigned char test_call_param6(UNION2 param1) {
return param1.field2;
}
int test_call_param7(union UNION1 param1) {
return param1.field1;
}
float test_call_param8(int *param1) {
*param1 = 5 * *param1;
return 1.0 * *param1;
}
void *test_call9() {
return NULL;
}
void **test_call10(int **param1) {
return NULL;
}
char *test_array_param(int arr[5]) {
return NULL;
}
void multiline1(void) {}
void *multiline2(void) {
return NULL;
}
void multiline3(void) {}
int weirdfunc(char ***apple) {
return 1;
}
int weirdfunc2(char **apple) {
return 2;
}

205
tests/include/test.h Normal file
View file

@ -0,0 +1,205 @@
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#define TEST_INT 512
#define TEST_FLOAT 5.12
#define TEST_HEX 0x512
#define TEST_CHAR 'a'
#define TEST_STR "hello world"
#ifdef __APPLE__
#define OSDEF 10
#endif
#ifdef _WIN32
#define OSDEF 20
#endif
#ifdef __linux__
#define OSDEF 30
#endif
#define foobar1(x) OSDEF * x
#define foobar2(x) x + 1
typedef uint8_t PRIMTYPE;
typedef PRIMTYPE CUSTTYPE;
typedef CUSTTYPE _CCUSTTYPE_;
struct STRUCT0;
struct STRUCT1 {
int field1;
};
typedef struct STRUCT1 STRUCT2;
typedef struct {
int field1;
} STRUCT3;
enum ENUM {
enum1,
enum2,
enum3
};
typedef enum {
enum4 = 3,
enum5,
enum6,
enum6a = enum5 & enum6,
enum6b = enum5 | enum6
} ENUM2;
enum {
enum7,
enum8,
enum9
};
typedef enum ENUM4 {
enum10,
enum11,
enum12
} ENUM4;
enum ENUM5 {
enum13 = (1 << 2),
enum14 = ((1 << 3) | 1),
enum15 = (1 << (1 & 1))
};
enum ENUM7 {
enum17 = '\0',
enum18 = 'A'
};
typedef void * VOIDPTR;
typedef int * INTPTR;
typedef struct {
struct STRUCT1 *field0;
int *field;
int field2[TEST_INT];
enum ENUM field3[TEST_INT];
int *field4[TEST_INT];
ENUM4 *field5[TEST_INT + TEST_INT];
int field6 : 1;
} STRUCT4;
typedef struct struct5 {
int (*tci)();
struct STRUCT1 (*tcp)(int);
float (*tcp8)(int *i);
void *(*tcv)();
} STRUCT5;
union UNION1 {
int field1;
float field2;
};
typedef union UNION2 {
double field1;
unsigned char field2;
} UNION2;
int test_call_int();
struct STRUCT1 _test_call_param_(int param1);
STRUCT2 test_call_param2(int param1, STRUCT2 param2);
STRUCT2 test_call_param3(int param1, struct STRUCT1 param2);
ENUM2 test_call_param4(enum ENUM param1);
union UNION1 test_call_param5(float param1);
unsigned char test_call_param6(UNION2 param1);
int test_call_param7(union UNION1 param1);
float test_call_param8(int *param1);
void *test_call9();
void **test_call10(int **param1);
char *test_array_param(int arr[5]);
// Issue #58
void
multiline1(void),
*multiline2(void),
multiline3(void);
// Issue #52
typedef struct struct6 { char name; } *STRUCT6;
typedef enum enum6t { enum16 } *ENUM6;
typedef union union3 { char name; } *UNION3;
struct struct6 test_call_stype6();
STRUCT6 test_call_stype_ptr6();
enum enum6t test_call_etype6();
ENUM6 test_call_etype_ptr6();
union union3 test_call_utype3();
UNION3 test_call_etype_ptr3();
typedef struct _Kernel { char name; } *Kernel;
// Double pointers
typedef void **DVOIDPTR;
typedef int **DINTPTR;
struct dstruct {
int **field1;
};
typedef struct dstruct2 {
char **field1;
float **field2;
void **(*tcv)(int **param1);
} DSTRUCT2;
// Issue #131
enum
{
TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024,
TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10,
TDEFL_BOGUS_1 = (1024 * 128) / TDEFL_LZ_CODE_BUF_SIZE,
TDEFL_BOGUS_2 = TDEFL_LZ_CODE_BUF_SIZE / 64,
TDEFL_BOGUS_3 = TDEFL_OUT_BUF_SIZE / TDEFL_BOGUS_1
};
// cOverride
struct foo { int foo[8][1]; };
typedef struct tagBITMAPINFOHEADER{
int biClrImportant;
} BITMAPINFOHEADER, * pBITMAPINFOHEADER;
#define BIT 123u
#define BIT2 123u
#define BIT3 123
typedef int ABC;
typedef int DEF;
typedef struct {
int **f1;
} GHI;
struct JKL {
int **f1;
};
// Issue #178
typedef enum
{
SDLK_UNDERSCORE = '_'
} SDL_KeyCode;
#ifdef __cplusplus
}
#endif

5
tests/include/test2.cpp Normal file
View file

@ -0,0 +1,5 @@
#include "test2.hpp"
int test_call_int() {
return 5;
}

26
tests/include/test2.hpp Normal file
View file

@ -0,0 +1,26 @@
#include <stdint.h>
#define TEST_INT 512
#define TEST_FLOAT 5.12
#define TEST_HEX 0x512
extern "C" {
int test_call_int();
struct Foo{
int bar;
};
}
class Foo1{
int bar1;
};
template<typename T>
struct Foo2{
int bar2;
};
typedef Foo2<int> Foo2_int;

23
tests/include/toast.h Normal file
View file

@ -0,0 +1,23 @@
// #57
enum {
_SG_STRING_SIZE = 16,
_SG_SLOT_SHIFT = 16,
_SG_SLOT_MASK = (1<<_SG_SLOT_SHIFT)-1,
_SG_MAX_POOL_SIZE = (1<<_SG_SLOT_SHIFT),
_SG_DEFAULT_BUFFER_POOL_SIZE = 128,
_SG_DEFAULT_IMAGE_POOL_SIZE = 128,
_SG_DEFAULT_SHADER_POOL_SIZE = 32,
_SG_DEFAULT_PIPELINE_POOL_SIZE = 64,
_SG_DEFAULT_PASS_POOL_SIZE = 16,
_SG_DEFAULT_CONTEXT_POOL_SIZE = 16,
_SG_MTL_DEFAULT_UB_SIZE = 4 * 1024 * 1024,
_SG_MTL_DEFAULT_SAMPLER_CACHE_CAPACITY = 64,
};
// #152
enum {
_ONE,
_TWO,
_MAX = _TWO,
_MORE
};

47
tests/libssh2.nim Normal file
View file

@ -0,0 +1,47 @@
import nimterop/[build, cimport]
const
outdir = getProjectCacheDir("libssh2")
getHeader(
header = "libssh2.h",
conanuri = "libssh2/$1",
jbburi = "libssh2/1.9.0",
outdir = outdir
)
cOverride:
type
stat = object
stat64 = object
SOCKET = object
when not libssh2Static:
cImport(libssh2Path, recurse = true, dynlib = "libssh2LPath", flags = "-c -E_ -F_")
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.}
assert libssh2_init(0) == 0
let
session = libssh2_session_init_ex(nil, nil, nil, nil)
if session == nil:
quit(1)
libssh2_session_set_blocking(session, 0.cint)
echo "zlib version = " & (block:
when not defined(Windows) and not isDefined(libssh2JBB):
$zlibVersion()
else:
""
)

49
tests/lzma.nim Normal file
View file

@ -0,0 +1,49 @@
import os, strutils
import nimterop/[build, cimport]
const
FLAGS {.strdefine.} = ""
baseDir = getProjectCacheDir("nimterop" / "tests" / "liblzma")
tflags = "--prefix=___,__,_ --suffix=__,_ " & FLAGS
static:
cSkipSymbol(@[
"PRIX8", "PRIX16", "PRIX32",
"PRIXLEAST8", "PRIXLEAST16", "PRIXLEAST32",
"PRIXFAST8"
])
when defined(envTest):
setDefines(@["lzmaStd"])
elif defined(envTestStatic):
setDefines(@["lzmaStd", "lzmaStatic"])
getHeader(
"lzma.h",
giturl = "https://github.com/xz-mirror/xz",
dlurl = "https://tukaani.org/xz/xz-$1.tar.gz",
conanuri = "xz_utils",
jbburi = "xz",
outdir = baseDir,
conFlags = "--disable-xz --disable-xzdec --disable-lzmadec --disable-lzmainfo"
)
cOverride:
type
lzma_internal = object
lzma_index = object
lzma_index_hash = object
lzma_options_lzma = object
lzma_stream_flags = object
lzma_block = object
lzma_index_iter = object
when not lzmaStatic:
cImport(lzmaPath, recurse = true, dynlib = "lzmaLPath", flags = tflags)
else:
cImport(lzmaPath, recurse = true, flags = tflags)
echo "liblzma version = " & $lzma_version_string()

1
tests/nim.cfg Normal file
View file

@ -0,0 +1 @@
--path:".."

47
tests/rsa.nim Normal file
View file

@ -0,0 +1,47 @@
import os
import strutils
import nimterop/[build, cimport]
setDefines(@["cryptoStd"])
getHeader("openssl/crypto.h")
const
basePath = cryptoPath.parentDir
FLAGS {.strdefine.} = ""
static:
cSkipSymbol(@["ERR_load_crypto_strings", "OpenSSLDie"])
cPlugin:
import strutils
proc onSymbol*(sym: var Symbol) {.exportc, dynlib.} =
sym.name = sym.name.strip(chars = {'_'}).replace("__", "_")
if sym.name in [
"AES_ENCRYPT", "AES_DECRYPT",
"BIO_CTRL_PENDING", "BIO_CTRL_WPENDING",
"BN_F_BNRAND", "BN_F_BNRAND_RANGE",
"CRYPTO_LOCK", "CRYPTO_NUM_LOCKS", "CRYPTO_THREADID",
"EVP_CIPHER",
"OPENSSL_VERSION",
"PKCS7_ENCRYPT", "PKCS7_STREAM",
"SSLEAY_VERSION",
"SSL_TXT_ADH", "SSL_TXT_AECDH", "SSL_TXT_kECDHE"
]:
sym.name = "C_" & sym.name
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)
OpensslInit()
echo $OPENSSL_VERSION_TEXT

524
tests/tast2.nim Normal file
View file

@ -0,0 +1,524 @@
import macros, os, sets, strutils
import nimterop/[cimport]
cPassC("-DNIMTEROP")
static:
# Skip casting on lower nim compilers because
# the VM does not support it
when (NimMajor, NimMinor, NimPatch) < (1, 0, 0):
cSkipSymbol @["CASTEXPR"]
const
path = currentSourcePath.parentDir() / "include" / "tast2.h"
when defined(NOHEADER):
cDefine("NOHEADER")
const
flags = " -H"
pHeader: seq[string] = @[]
pHeaderImp: seq[string] = @[]
else:
const
flags = ""
pHeader = @["header:" & path.replace("\\", "/")]
pHeaderImp = @["importc"] & pHeader
const
pHeaderImpBy = @["bycopy"] & pHeaderImp
pHeaderBy = @["bycopy"] & pHeader
pHeaderInc = @["incompleteStruct"] & pHeader
cOverride:
const
A* = 2
type
A1* = A0
cDefine("SOME_CONST=100")
when not defined(WRAPPED):
cImport(path, flags="-f:ast2 -ENK_,SDL_ -GVICE=SLICE -TMyInt=cint" & flags, nimFile = "tast2wrapped.nim")
else:
import tast2wrapped
proc getPragmas(n: NimNode): HashSet[string] =
# Find all pragmas in AST, return as "name" or "name:value" in set
for i in 0 ..< n.len:
if n[i].kind == nnkPragma:
for j in 0 ..< n[i].len:
if n[i][j].kind == nnkIdent:
result.incl $n[i][j]
elif n[i][j].kind == nnkExprColonExpr:
result.incl $n[i][j][0] & ":" & $n[i][j][1]
else:
result.incl n[i].getPragmas()
proc getRecList(n: NimNode): NimNode =
# Find nnkRecList in AST
for i in 0 ..< n.len:
if n[i].kind == nnkRecList:
return n[i]
elif n[i].len != 0:
let
rl = getRecList(n[i])
if not rl.isNil:
return rl
macro checkPragmas(t: typed, pragmas: static[seq[string]], istype: static[bool] = true,
origname: static[string] = ""): untyped =
# Verify that type has expected pragmas defined
# `istype` is true when typedef X
var
ast = t.getImpl()
prag = ast.getPragmas()
exprag = pragmas.toHashSet()
when not defined(NOHEADER):
if not istype:
if "union" in exprag:
exprag.incl "importc:union " & $t
else:
exprag.incl "importc:struct " & $t
elif origname.len != 0:
exprag.incl "importc:" & $origname
doAssert symmetricDifference(prag, exprag).len == 0,
"\nWrong number of pragmas in " & $t & "\n" & $prag & " vs " & $exprag
macro testFields(t: typed, fields: static[string] = "") =
# Verify that type has expected fields
var
ast = t.getImpl()
rl = ast.getRecList()
fsplit = fields.split("!")
names = fsplit[0].split("|")
types =
if fsplit.len > 1:
fsplit[1].split("|")
else:
@[]
if not rl.isNil:
for i in 0 ..< rl.len:
let
name = ($rl[i][0]).strip(chars = {'*'})
typ = ($(rl[i][1].repr())).replace("\n", "").replace(" ", "").replace("typeof", "type")
n = names.find(name)
assert n != -1, $t & "." & name & " invalid"
assert types[n].replace("typeof", "type") == typ,
"typeof(" & $t & ":" & name & ") != " & types[n].replace("typeof", "type") & ", is " & typ
assert A == 2
assert B == 1.0
assert C == 0x10
assert D == "hello"
assert E == 'c'
assert F == 0o1234
assert not defined(NOTSUPPORTEDSTR)
assert UEXPR == (1234.uint shl 1)
assert ULEXPR == (1234.uint32 shl 2)
assert ULLEXPR == (1234.uint64 shl 3)
assert LEXPR == (1234.int32 shl 4)
assert LLEXPR == (1234.int64 shl 5)
assert AVAL == 100
assert BVAL == 200
assert EQ1 == (AVAL <= BVAL)
assert EQ2 == (AVAL >= BVAL)
assert EQ3 == (AVAL > BVAL)
assert EQ4 == (AVAL < BVAL)
assert EQ5 == (AVAL != BVAL)
assert EQ6 == (AVAL == BVAL)
assert SX_NEAR_ZERO == 3.725290298461914e-09
assert SIZEOF == 1
assert COERCE == 645635670332'u64
assert COERCE2 == 645635670332'i64
assert INT_FAST16_MIN == -9223372036854775807'i64 - 1
assert BINEXPR == 5
assert BOOL == true
assert MATHEXPR == -99
assert ANDEXPR == 96
when (NimMajor, NimMinor, NimPatch) >= (1, 0, 0):
assert CASTEXPR == 34.chr
assert TRICKYSTR == "N\x1C\nfoo\x00\'\"\c\v\a\b\e\f\t\\\\?bar"
assert NULLCHAR == '\0'
assert OCTCHAR == '\n'
assert HEXCHAR.int == 0xFE
assert SHL1 == (1.uint shl 1)
assert SHL2 == (1.uint shl 2)
assert SHL3 == (1.uint shl 3)
assert typeof(POINTEREXPR) is (ptr cint)
assert typeof(POINTERPOINTERPOINTEREXPR) is (ptr ptr ptr cint)
assert ALLSHL == (SHL1 or SHL2 or SHL3)
assert typeof(parent_struct_s().s) is array[100, some_struct_s]
assert typeof(SOME_ARRAY) is array[100, some_struct_s]
assert A0 is object
testFields(A0, "f1!cint")
checkPragmas(A0, pHeaderBy, istype = false)
var a0: A0
a0.f1 = 1
assert A1 is A0
testFields(A1, "f1!cint")
var a1: A1
a1.f1 = 2
assert A2 is object
testFields(A2)
checkPragmas(A2, pHeaderInc, istype = false)
when defined(NOHEADER):
# typedef struct X; is invalid
var a2: A2
assert A3 is object
testFields(A3)
checkPragmas(A3, pHeaderInc, istype = false)
var a3: A3
assert A4 is object
testFields(A4, "f1!cfloat")
checkPragmas(A4, pHeaderImpBy)
var a4: A4
a4.f1 = 4.1
assert A4p is ptr A4
testFields(A4p, "f1!cfloat")
checkPragmas(A4p, pHeaderImp)
var a4p: A4p
a4p = addr a4
assert A5 is cint
checkPragmas(A5, pHeaderImp)
const a5: A5 = 5
assert A6 is ptr cint
checkPragmas(A6, pHeaderImp)
var
a6: A6
a6i = 6
a6 = cast[A6](addr a6i)
assert A7 is ptr ptr A0
checkPragmas(A7, pHeaderImp)
var
a7: A7
a7a = addr a0
a7 = addr a7a
assert A8 is pointer
checkPragmas(A8, pHeaderImp)
var a8: A8
a8 = nil
assert A9p is array[3, cstring]
checkPragmas(A9p, pHeaderImp)
var a9p: A9p
a9p[1] = nil
a9p[2] = "hello".cstring
assert A9 is array[4, cchar]
checkPragmas(A9, pHeaderImp)
var a9: A9
a9[2] = 'c'
assert A10 is array[3, array[6, cstring]]
checkPragmas(A10, pHeaderImp)
var a10: A10
a10[2][5] = "12345".cstring
assert A11 is ptr array[3, cstring]
checkPragmas(A11, pHeaderImp)
var a11: A11
a11 = addr a9p
assert A111 is array[12, ptr A1]
checkPragmas(A111, pHeaderImp)
var a111: A111
a111[11] = addr a0
assert A12 is proc(a1: cint, b: cint, c: ptr cint, a4: ptr cint, count: array[4, ptr cint], `func`: proc(a1: cint, a2: cint): cint {.cdecl.}): ptr ptr cint {.cdecl.}
checkPragmas(A12, pHeaderImp & "cdecl")
var a12: A12
assert A121 is proc(a1: cfloat, b: cfloat, c: ptr cfloat, a4: ptr cfloat, count: array[4, ptr cfloat], `func`: proc(a1: cfloat, a2: cfloat): cfloat {.cdecl.}): ptr ptr cint {.cdecl.}
checkPragmas(A121, pHeaderImp & "cdecl")
var a121: A121
assert A122 is proc(a1: cchar, b: cchar, c: cstring, a4: cstring, count: array[4, cstring], `func`: proc(a1: cchar, a2: cchar): cchar {.cdecl.}): ptr ptr cint {.cdecl.}
checkPragmas(A122, pHeaderImp & "cdecl")
var a122: A122
assert A13 is proc(a1: cint, a2: cint, `func`: proc() {.cdecl.}): cint {.cdecl.}
checkPragmas(A13, pHeaderImp & "cdecl")
var a13: A13
assert A14 is object
testFields(A14, "a1!cchar")
checkPragmas(A14, pHeaderBy, istype = false)
var a14: A14
a14.a1 = 'a'
assert A15 is object
testFields(A15, "a1|a2!cstring|array[1, ptr cint]")
checkPragmas(A15, pHeaderBy, istype = false)
var
a15: A15
a15i = 15.cint
a15.a1 = "hello".cstring
a15.a2[0] = addr a15i
assert A16 is object
testFields(A16, "f1!cchar")
checkPragmas(A16, pHeaderBy, istype = false)
var a16: A16
a16.f1 = 's'
assert A17 is object
testFields(A17, "a1|a2!cstring|array[1, ptr cint]")
checkPragmas(A17, pHeaderBy, istype = false)
var a17: A17
a17.a1 = "hello".cstring
a17.a2[0] = addr a15i
assert A18 is A17
checkPragmas(A18, pHeaderImp)
var a18: A18
assert A18p is ptr A17
checkPragmas(A18p, pHeaderImp)
var a18p: A18p
a18p = addr a18
assert A19 is object
testFields(A19, "a1|a2!cstring|array[1, ptr cint]")
checkPragmas(A19, pHeaderImpBy)
var a19: A19
a19.a1 = "hello".cstring
a19.a2[0] = addr a15i
assert A19p is ptr A19
checkPragmas(A19p, pHeaderImp)
var a19p: A19p
a19p = addr a19
assert A20 is object
testFields(A20, "a1!cchar")
checkPragmas(A20, pHeaderBy, istype = false)
var a20: A20
a20.a1 = 'a'
assert A21 is A20
checkPragmas(A21, pHeaderImp)
var a21: A21
a21 = a20
assert A21p is ptr A20
checkPragmas(A21p, pHeaderImp)
var a21p: A21p
a21p = addr a20
assert A22 is object
testFields(A22, "f1|f2!ptr ptr cint|array[123 + type(123)(132), ptr cint]")
checkPragmas(A22, pHeaderBy, istype = false)
var a22: A22
a22.f1 = addr a15.a2[0]
assert A23 is proc(): cstring {.cdecl.}
checkPragmas(A23, pHeaderImp & "cdecl")
var a23: A23
assert U1 is object
assert sizeof(U1) == sizeof(cfloat)
checkPragmas(U1, pHeaderBy & @["union"], istype = false)
var u1: U1
u1.f1 = 5
assert U2 is object
assert sizeof(U2) == 256 * sizeof(cint)
checkPragmas(U2, pHeaderBy & @["union"], istype = false)
var u2: U2
u2.f1 = addr a15.a2[0]
assert PANEL_WINDOW is nk_panel_type
assert PANEL_WINDOW == 1
assert PANEL_GROUP == 2
assert PANEL_POPUP == 4
assert PANEL_CONTEXTUAL == 16
assert PANEL_COMBO == 32
assert PANEL_MENU == 64
assert PANEL_TOOLTIP == 128
assert PANEL_SET_NONBLOCK == 240
assert PANEL_SET_POPUP == 244
assert PANEL_SET_SUB == 246
assert cmGray == 1000000
assert pfGray16 == 1000011
assert pfYUV422P8 == pfYUV420P8 + 1
assert pfRGB27 == cmRGB.VSPresetFormat + 11
assert pfCompatYUY2 == pfCompatBGR32 + 1
assert pcre_malloc is proc(a1: uint): pointer {.cdecl, varargs.}
checkPragmas(pcre_malloc, @["importc", "cdecl", "varargs"] & pHeader)
assert pcre_free is proc(a1: pointer) {.cdecl.}
checkPragmas(pcre_free, @["importc", "cdecl"] & pHeader)
assert pcre_stack_malloc is proc(a1: uint): pointer {.cdecl.}
checkPragmas(pcre_stack_malloc, @["importc", "cdecl"] & pHeader)
assert DuplexTransferImageViewMethod is
proc (a1: ptr ImageView; a2: ptr ImageView; a3: ptr ImageView; a4: uint;
a5: cint; a6: pointer): MagickBooleanType {.cdecl.}
assert GetImageViewMethod is
proc (a1: ptr ImageView; a2: uint; a3: cint; a4: pointer): MagickBooleanType {.cdecl.}
assert SetImageViewMethod is
proc (a1: ptr ImageView; a2: uint; a3: cint; a4: pointer): MagickBooleanType {.cdecl.}
assert TransferImageViewMethod is
proc (a1: ptr ImageView; a2: ptr ImageView; a3: uint; a4: cint; a5: pointer): MagickBooleanType {.cdecl.}
assert UpdateImageViewMethod is
proc (a1: ptr ImageView; a2: uint; a3: cint; a4: pointer): MagickBooleanType {.cdecl.}
# Issue #156, math.h
assert absfunptr1 is proc(a1: proc(a1: ptr A0): cint {.cdecl.}): pointer {.cdecl.}
assert absfunptr2 is proc(a1: ptr proc(a1: ptr A1): cint {.cdecl.}): ptr pointer {.cdecl.}
assert absfunptr3 is proc(a1: proc(a1: ptr A2): ptr cint {.cdecl.}) {.cdecl.}
assert absfunptr4 is proc(a1: ptr proc(a1: ptr A3): ptr cint {.cdecl.}): pointer {.cdecl.}
assert absfunptr5 is proc(a1: proc(a1: ptr A4): cint {.cdecl.}) {.cdecl.}
assert sqlite3_bind_blob is
proc(a1: ptr A1, a2: cint, a3: pointer, n: cint, a5: proc(a1: pointer) {.cdecl.}): cint {.cdecl.}
# Issue #174 - type name[] => UncheckedArray[type]
assert ucArrFunc1 is proc(text: UncheckedArray[cint]): cint {.cdecl.}
assert ucArrFunc2 is
proc(text: UncheckedArray[array[5, cint]], `func`: proc(text: UncheckedArray[cint]): cint {.cdecl.}): cint {.cdecl.}
assert ucArrType1 is UncheckedArray[array[5, cint]]
checkPragmas(ucArrType1, pHeaderImp)
assert ucArrType2 is object
testFields(ucArrType2, "f1|f2!array[5, array[5, cfloat]]|UncheckedArray[array[5, ptr cint]]")
checkPragmas(ucArrType2, pHeaderBy, istype = false)
assert fieldfuncfunc is object
testFields(fieldfuncfunc,
"func1!proc (f1: cint; sfunc1: proc (f1: cint; ssfunc1: proc (f1: cint): ptr cint {.cdecl, varargs.}): ptr cint {.cdecl.}): ptr cint {.cdecl.}")
assert func2 is proc (f1: cint; sfunc2: proc (f1: cint; ssfunc2: proc (f1: cint): ptr cint {.cdecl.}): ptr cint {.cdecl.}): ptr cint {.cdecl.}
# Test --replace VICE=SLICE
assert BASS_DESLICEINFO is object
testFields(BASS_DESLICEINFO, "name|driver|flags!cstring|cstring|cint")
checkPragmas(BASS_DESLICEINFO, pHeaderBy, origname = "BASS_DEVICEINFO")
# Issue #183
assert GPU_Target is object
testFields(GPU_Target, "w|h|x|y|z!cint|ptr cint|cstring|cchar|ptr cstring")
checkPragmas(GPU_Target, pHeaderBy, istype = false)
# Issue #185
assert AudioCVT is object
testFields(AudioCVT, "needed!cint")
checkPragmas(AudioCVT, pHeaderBy, origname = "struct SDL_AudioCVT")
# Issue #172
assert SomeType is object
testFields(SomeType, "x!ptr cstring")
checkPragmas(SomeType, pHeaderImpBy)
# Nested #137
assert NT1 is object
testFields(NT1, "f1!cint")
checkPragmas(NT1, pHeaderBy, istype = false)
assert Type_tast2h1 is object
testFields(Type_tast2h1, "f1!cint")
checkPragmas(Type_tast2h1, pHeaderBy, istype = false)
assert NU1 is object
testFields(NU1, "f1!cfloat")
checkPragmas(NU1, pHeaderBy & @["union"], istype = false)
assert NEV1 == 0
assert NEV2 == 1
assert NEV3 == 2
assert Type_tast2h2 is object
testFields(Type_tast2h2, "f1|f2|f3!cint|NU1|Enum_tast2h1")
checkPragmas(Type_tast2h2, pHeaderBy, istype = false)
assert NT3 is object
testFields(NT3, "f1!Type_tast2h2")
checkPragmas(NT3, pHeaderBy, istype = false)
assert Type_tast2h3 is object
testFields(Type_tast2h3, "f1!cint")
checkPragmas(Type_tast2h3, pHeaderBy, istype = false)
assert NU2 is object
testFields(NU2, "f1!cint")
checkPragmas(NU2, pHeaderBy & @["union"], istype = false)
assert Union_tast2h1 is object
testFields(Union_tast2h1, "f1!cint")
checkPragmas(Union_tast2h1, pHeaderBy & @["union"], istype = false)
assert NEV4 == 8
assert NEV5 == 9
assert NEV6 == 64
assert NEV7 == 65
assert nested is object
testFields(nested, "f1|f2|f3|f4|f5|f6|f7|f8!NT1|Type_tast2h1|NT3|Type_tast2h3|NU2|Union_tast2h1|NE1|Enum_tast2h2")
checkPragmas(nested, pHeaderImpBy)
when not defined(NOHEADER):
assert sitest1(5) == 10
assert sitest1(10) == 20
when declared(MyInt):
assert false, "MyInt is defined!"
testFields(TestMyInt, "f1!cint")
checkPragmas(TestMyInt, pHeaderBy, isType = false)
# #237
assert sx_ivec3 is object
testFields(sx_ivec3, "x|y|z|n!cint|cint|cint|array[3, cint]")
checkPragmas(sx_ivec3, pHeaderBy & @["union"], istype = false)
var sx: sx_ivec3
sx.x = 5
assert sx.n[0] == 5
when not defined(NOHEADER):
# Nim doesn't know of the anonymous nested struct so when the header
# isn't present, the test below breaks
sx.n[1] = 4
assert sx.y == 4
# #236
assert SG_MAX_MIPMAPS is cint
assert SG_MAX_MIPMAPS == 16
assert parenpoin is object
var pp: parenpoin
assert pp.gtk_reserved1 is pointer

29
tests/timeit.nim Normal file
View file

@ -0,0 +1,29 @@
import os, osproc, sequtils, strformat, strutils, times
when (NimMajor, NimMinor) >= (1, 0):
import std/monotimes
template getTime(): MonoTime = getMonoTime()
else:
template getTime(): float = epochTime()
when isMainModule:
var params = commandLineParams()
params.apply(quoteShell)
let cmd = params.join(" ")
echo &"================\nRunning: {cmd}\n"
let
start = getTime()
ret = execCmd(cmd)
endt = getTime()
outf = getAppDir() / "timeit.txt"
outd = if fileExists(outf): readFile(outf) else: ""
outp = &"\nRan: {cmd}\nTime taken: {$(endt - start)}\n"
echo outp
writeFile(outf, outd & outp)
quit(ret mod 255)

36
tests/tmath.nim Normal file
View file

@ -0,0 +1,36 @@
import unittest
import nimterop/cimport
cOverride:
type
locale_t = object
mingw_ldbl_type_t = object
mingw_dbl_type_t = object
when defined(Windows):
cOverride:
type
complex = object
static:
when (NimMajor, NimMinor, NimPatch) < (1, 0, 0):
# FP_ILOGB0 and FP_ILOGBNAN are casts that are unsupported
# on lower Nim VMs
cSkipSymbol @["math_errhandling", "FP_ILOGB0", "FP_ILOGBNAN"]
else:
cSkipSymbol @["math_errhandling"]
cDisableCaching()
cAddStdDir()
cPlugin:
import strutils
proc onSymbol*(sym: var Symbol) {.exportc, dynlib.} =
sym.name = sym.name.strip(chars={'_'}).replace("__", "_")
const FLAGS {.strdefine.} = ""
cImport(cSearchPath("math.h"), flags = FLAGS)
check sin(5) == -0.9589242746631385
check abs(-5) == 5
check sqrt(4.00) == 2.0

224
tests/tnimterop_c.nim Normal file
View file

@ -0,0 +1,224 @@
import std/unittest
import os
import macros
import nimterop/cimport
import nimterop/paths
static:
cDisableCaching()
cAddSearchDir testsIncludeDir()
cDefine("FORCE")
cIncludeDir testsIncludeDir()
cCompile cSearchPath("test.c")
cPluginPath(getProjectPath() / "tnimterop_c_plugin.nim")
cOverride:
type
BITMAPINFOHEADER* {.bycopy.} = object
biClrImportant*: int
Random = object
ABC = pointer
GHI = object
f2: ptr ptr cint
JKL = object
f2: ptr ptr cint
const
BIT* = 1
proc weirdfunc(apple: ptr ptr ptr cchar): int {.importc.}
proc weirdfunc2(mango: ptr ptr cchar): int {.importc.}
const FLAGS {.strdefine.} = ""
cImport(cSearchPath("test.h"), flags = FLAGS)
check TEST_INT == 512
check TEST_FLOAT == 5.12
check TEST_HEX == 0x512
check TEST_CHAR == 'a'
check TEST_STR == "hello world"
when defined(osx):
check OSDEF == 10
elif defined(Windows):
check OSDEF == 20
else:
check OSDEF == 30
block:
# workaround for https://github.com/nim-lang/Nim/issues/10129
const ok = OSDEF
var
pt: PRIMTYPE
ct: CUSTTYPE
cct: CCUSTTYPE
s0: ptr STRUCT0
s1: STRUCT1
s2: STRUCT2
s3: STRUCT3
s4: STRUCT4
s5: STRUCT5
s51: struct5
e: ENUM
e2: ENUM2 = enum5
e3 = enum7
e4: ENUM4 = enum11
vptr: VOIDPTR
iptr: INTPTR
u: UNION1
u2: UNION2
i: cint
pt = 3
ct = 4
cct = 5
s1.field1 = 5
s2.field1 = 6
s3.field1 = 7
s4.field2[2] = 5
# note: simplify with `defined(c)` for nim >= 0.19.9
when defined(cpp) and defined(OSX):
discard
else:
s4.field3[3] = enum1
s4.field6 = 1
s4.field6 += 1
check s4.field6 == 0
s5.tci = test_call_int
s5.tcp = test_call_param
s5.tcp8 = test_call_param8
s51.tci = test_call_int
s51.tcv = test_call9
check s5.tci() == 5
check s51.tci() == 5
check s51.tcv() == nil
e = enum1
e2 = enum4
u2.field2 = 'c'
i = 5
check test_call_int() == 5
check test_call_param(5).field1 == 5
check test_call_param2(5, s2).field1 == 11
check test_call_param3(5, s1).field1 == 10
when defined(cpp) and defined(OSX):
# error: assigning to 'enum ENUM' from incompatible type 'NI' (aka 'long long')
discard
else:
check test_call_param4(e) == e2
check test_call_param5(5.0).field2 == 5.0
check test_call_param6(u2) == 'c'
u.field1 = 4
check test_call_param7(u) == 4
when defined(cpp) and defined(OSX):
# note: candidate function not viable: no known conversion from 'NI *' (aka 'long long *') to 'int *' for 1st argument
# check test_call_param8(cast[ptr int](addr i)) == 25.0
discard
else:
check test_call_param8(addr i) == 25.0
check i == 25
check test_call9() == nil
check enum6a == 4
check enum6b == 5
check e3 == enum7
check e4 == enum11
check enum13 == 4
check enum14 == 9
check enum15 == 2
check enum17 == '\0'.ENUM7
check enum18 == 'A'.ENUM7
# Issue #58
multiline1()
let p = multiline2()
multiline3()
# Issue #52
var
s6: struct6
s6p: STRUCT6
e6: enum6t
e6p: ENUM6
u3: union3
u3p: UNION3
k: uKernel
kp: Kernel
## failing tests
when false:
static: # Error: undeclared identifier: 'foobar1'
doAssert foobar1(3) == OSDEF * 3
when false: # Error: undeclared identifier: 'foobar2'
doAssert foobar2(3) == 3 + 1
# Double pointer
var
dv: DVOIDPTR
di: DINTPTR
ds: dstruct
cstr = "Hello".cstring
ds2: DSTRUCT2
dv = addr vptr
di = addr iptr
ds.field1 = di
ds2.field1 = addr cstr
ds2.tcv = test_call10
check ds2.tcv(di) == nil
# Issue #131
check TDEFL_OUT_BUF_SIZE == 85196
check TDEFL_BOGUS_1 == 2
check TDEFL_BOGUS_2 == 1024
check TDEFL_BOGUS_3 == (85196 / 2).int
var
arr: array[5, cint]
check test_array_param(arr) == nil
# cOverride
var
ca = weirdfunc
cb: BITMAPINFOHEADER
cc = weirdfunc2
cd: ABC
ce: DEF
cf: GHI
cg: JKL
cd = nil
ce = 5
cf.f2 = nil
cg.f2 = nil
doAssert BIT == 1
doAssert ca(nil) == 1
doAssert cc(nil) == 2
doAssert SDLK_UNDERSCORE == 95

View file

@ -0,0 +1,9 @@
import nimterop/plugin
import strutils
proc onSymbol*(sym: var Symbol) {.exportc, dynlib.} =
if sym.name == "_Kernel":
sym.name = "uKernel"
else:
sym.name = sym.name.strip(chars={'_'})

19
tests/tnimterop_cpp.nim Normal file
View file

@ -0,0 +1,19 @@
import unittest
import nimterop/cimport
import nimterop/paths
static:
cDisableCaching()
cAddSearchDir testsIncludeDir()
cIncludeDir testsIncludeDir()
cCompile cSearchPath "test2.cpp"
cImport cSearchPath "test2.hpp"
check TEST_INT == 512
check TEST_FLOAT == 5.12
check TEST_HEX == 0x512
check test_call_int() == 5
var foo: Foo
check foo.bar == 0

2
tests/toast.cfg Normal file
View file

@ -0,0 +1,2 @@
--preprocess
-nk -E=_

43
tests/tpcre.nim Normal file
View file

@ -0,0 +1,43 @@
import os
import nimterop/[cimport, build]
const
baseDir = getProjectCacheDir("nimterop" / "tests" / "pcre")
pcreH = baseDir/"pcre.h.in"
static:
if not pcreH.fileExists():
downloadUrl("https://github.com/svn2github/pcre/raw/master/pcre.h.in", baseDir)
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_", "")
if sym.name.startsWith("pcre16_") or sym.name.startsWith("pcre32_"):
sym.name = ""
const FLAGS {.strdefine.} = ""
cImport(pcreH, dynlib="dynpcre", flags="--mode=c " & FLAGS)
echo version()
when FLAGS.len != 0:
# Legacy algorithm is broken - does not convert void * return to pointer
proc my_malloc(a1: uint): pointer {.cdecl.} =
discard
malloc = my_malloc

60
tests/tsoloud.nim Normal file
View file

@ -0,0 +1,60 @@
import os, nimterop/[cimport, build]
const
FLAGS {.strdefine.} = ""
baseDir = getProjectCacheDir("nimterop" / "tests" / "soloud")
incl = baseDir/"include"
src = baseDir/"src"
static:
gitPull("https://github.com/jarikomppa/soloud", baseDir, "include/*\nsrc/*\n", checkout = "RELEASE_20200207")
cDisableCaching()
cOverride:
type
Soloud* = pointer
AlignedFloatBuffer* = pointer
proc Soloud_destroy*(aSoloud: ptr Soloud) {.importc: "Soloud_destroy", header: cSearchPath(incl/"soloud_c.h").}
static: cSkipSymbol @["WavStream_stop", "WavStream_setFilter"]
cIncludeDir(incl)
when defined(osx):
cDefine("WITH_COREAUDIO")
cPassL("-framework CoreAudio -framework AudioToolbox")
cCompile(src/"backend/coreaudio/*.cpp")
elif defined(Linux):
cPassL("-lpthread")
cDefine("WITH_OSS")
cCompile(src/"backend/oss/*.cpp")
elif defined(Windows):
cPassC("-msse")
cPassL("-lwinmm")
cDefine("WITH_WINMM")
cCompile(src/"backend/winmm/*.cpp")
else:
static: doAssert false
cCompile(src/"c_api/soloud_c.cpp")
cCompile(src/"core/*.cpp")
cCompile(src/"audiosource", "cpp", exclude="ay/")
cCompile(src/"audiosource", "c")
cCompile(src/"filter/*.cpp")
cImport(incl/"soloud_c.h", flags = FLAGS)
var
s = Soloud_create()
echo s.Soloud_init()
s.Soloud_destroy()
when declared(WavStream_stop):
assert "WavStream_stop() not skipped"
when declared(WavStream_setFilter):
assert "WavStream_setFilter not skipped"

5
tests/tsoloud.nims Normal file
View file

@ -0,0 +1,5 @@
# Workaround for C++ scanner.cc causing link error with other C obj files
when defined(MacOSX):
switch("clang.linkerexe", "g++")
else:
switch("gcc.linkerexe", "g++")

15
tests/wrappers.nims Normal file
View file

@ -0,0 +1,15 @@
import os
let
wrappers = @["genotrance/nimarchive", "genotrance/nimgit2"]
rmDir("wrappers")
mkDir("wrappers")
withDir("wrappers"):
for wrapper in wrappers:
let
name = wrapper.extractFilename()
exec "../../tests/timeit git clone https://github.com/" & wrapper
withDir(name):
exec "../../../tests/timeit nimble install -d"
exec "../../../tests/timeit nimble test"

76
tests/zlib.nim Normal file
View file

@ -0,0 +1,76 @@
import os, strutils
import nimterop/[build, cimport]
const
FLAGS {.strdefine.} = ""
baseDir = getProjectCacheDir("nimterop" / "tests" / "zlib")
proc zlibPreBuild(outdir, path: string) =
let
mf = outdir / "Makefile"
if mf.fileExists():
# Delete default Makefile
if mf.readFile().contains("configure first"):
mf.rmFile()
when defined(Windows):
# Fix static lib name on Windows
setCmakeLibName(outdir, "zlibstatic", prefix = "lib", oname = "zlib", suffix = ".a")
when defined(envTest):
setDefines(@["zlibGit"])
elif defined(envTestStatic):
setDefines(@["zlibGit", "zlibStatic"])
getHeader(
"zlib.h",
giturl = "https://github.com/madler/zlib",
dlurl = "http://zlib.net/zlib-$1.tar.gz",
outdir = baseDir,
altNames = "z,zlib"
)
cPlugin:
import regex, strutils
proc onSymbol*(sym: var Symbol) {.exportc, dynlib.} =
sym.name = sym.name.replace(re"_[_]+", "_").strip(chars = {'_'})
cOverride:
type
voidpf = ptr object
voidpc = ptr object
voidp = ptr object
uLongf = culong
z_size_t = culong
z_crc_t = culong
alloc_func* {.importc.} = proc(opaque: voidpf, items, size: uint) {.cdecl.}
Bytef {.importc.} = object
when defined(posix):
cOverride:
type
pthread_mutex_s = object
pthread_cond_s = object
pthread_rwlock_arch_t = object
extension = object
fd_set = object
when defined(posix):
static:
cSkipSymbol(@["u_int8_t", "u_int16_t", "u_int32_t", "u_int64_t"])
when zlibGit or zlibDL:
when dirExists(baseDir / "buildcache"):
cIncludeDir(baseDir / "buildcache")
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()

View file

@ -1,374 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
<!-- Google fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
<!-- CSS -->
<title>Index</title>
<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
<script type="text/javascript" src="dochack.js"></script>
<script type="text/javascript">
function main() {
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
toggleSwitch.addEventListener('change', switchTheme, false);
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-theme', "dark");
toggleSwitch.checked = true;
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
document.documentElement.setAttribute('data-theme', "light");
toggleSwitch.checked = false;
} else {
const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null;
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
if (currentTheme === 'dark') {
toggleSwitch.checked = true;
}
}
}
}
</script>
</head>
<body onload="main()">
<div class="document" id="documentId">
<div class="container">
<h1 class="title">Index</h1>
Modules: <a href="build.html">build</a>, <a href="cimport.html">cimport</a>, <a href="compat.html">compat</a>, <a href="docs.html">docs</a>, <a href="paths.html">paths</a>, <a href="plugin.html">plugin</a>, <a href="types.html">types</a>.<br/><p /><h2>API symbols</h2>
<dl><dt><a name="ast2" href="#ast2"><span>ast2:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="Feature.ast2" href="cimport.html#ast2">Feature.ast2</a></li>
</ul></dd>
<dt><a name="buildDocs" href="#buildDocs"><span>buildDocs:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="docs: buildDocs(files: openArray[string]; path: string; baseDir = getProjectPath() &amp; &quot;/&quot;;
defines: openArray[string] = @[])" href="docs.html#buildDocs%2CopenArray%5Bstring%5D%2Cstring%2CopenArray%5Bstring%5D">docs: buildDocs(files: openArray[string]; path: string; baseDir = getProjectPath() &amp; &quot;/&quot;;
defines: openArray[string] = @[])</a></li>
</ul></dd>
<dt><a name="c2nImport" href="#c2nImport"><span>c2nImport:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="cimport: c2nImport(filename: static string; recurse: static bool = false;
dynlib: static string = &quot;&quot;; mode: static string = &quot;c&quot;; flags: static string = &quot;&quot;): untyped" href="cimport.html#c2nImport.m%2C%2Cstring%2Cstring%2Cstring">cimport: c2nImport(filename: static string; recurse: static bool = false;
dynlib: static string = &quot;&quot;; mode: static string = &quot;c&quot;; flags: static string = &quot;&quot;): untyped</a></li>
</ul></dd>
<dt><a name="cacheDir" href="#cacheDir"><span>cacheDir:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="paths: cacheDir" href="paths.html#cacheDir">paths: cacheDir</a></li>
</ul></dd>
<dt><a name="cAddSearchDir" href="#cAddSearchDir"><span>cAddSearchDir:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="cimport: cAddSearchDir(dir: string)" href="cimport.html#cAddSearchDir%2Cstring">cimport: cAddSearchDir(dir: string)</a></li>
</ul></dd>
<dt><a name="cAddStdDir" href="#cAddStdDir"><span>cAddStdDir:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="cimport: cAddStdDir(mode = &quot;c&quot;)" href="cimport.html#cAddStdDir%2Cstring">cimport: cAddStdDir(mode = &quot;c&quot;)</a></li>
</ul></dd>
<dt><a name="cCompile" href="#cCompile"><span>cCompile:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="cimport: cCompile(path: static string; mode = &quot;c&quot;; exclude = &quot;&quot;): untyped" href="cimport.html#cCompile.m%2C%2Cstring%2Cstring">cimport: cCompile(path: static string; mode = &quot;c&quot;; exclude = &quot;&quot;): untyped</a></li>
</ul></dd>
<dt><a name="cDebug" href="#cDebug"><span>cDebug:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="cimport: cDebug()" href="cimport.html#cDebug">cimport: cDebug()</a></li>
</ul></dd>
<dt><a name="cDefine" href="#cDefine"><span>cDefine:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="cimport: cDefine(name: static string; val: static string = &quot;&quot;): untyped" href="cimport.html#cDefine.m%2C%2Cstring">cimport: cDefine(name: static string; val: static string = &quot;&quot;): untyped</a></li>
</ul></dd>
<dt><a name="cDisableCaching" href="#cDisableCaching"><span>cDisableCaching:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="cimport: cDisableCaching()" href="cimport.html#cDisableCaching">cimport: cDisableCaching()</a></li>
</ul></dd>
<dt><a name="cImport" href="#cImport"><span>cImport:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="cimport: cImport(filename: static string; recurse: static bool = false;
dynlib: static string = &quot;&quot;; mode: static string = &quot;c&quot;; flags: static string = &quot;&quot;): untyped" href="cimport.html#cImport.m%2C%2Cstring%2Cstring%2Cstring">cimport: cImport(filename: static string; recurse: static bool = false;
dynlib: static string = &quot;&quot;; mode: static string = &quot;c&quot;; flags: static string = &quot;&quot;): untyped</a></li>
</ul></dd>
<dt><a name="cIncludeDir" href="#cIncludeDir"><span>cIncludeDir:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="cimport: cIncludeDir(dir: static string): untyped" href="cimport.html#cIncludeDir.m">cimport: cIncludeDir(dir: static string): untyped</a></li>
</ul></dd>
<dt><a name="clearDefines" href="#clearDefines"><span>clearDefines:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: clearDefines(): untyped" href="build.html#clearDefines.m">build: clearDefines(): untyped</a></li>
</ul></dd>
<dt><a name="cmake" href="#cmake"><span>cmake:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: cmake(path, check, flags: string)" href="build.html#cmake%2Cstring%2Cstring%2Cstring">build: cmake(path, check, flags: string)</a></li>
</ul></dd>
<dt><a name="configure" href="#configure"><span>configure:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: configure(path, check: string; flags = &quot;&quot;)" href="build.html#configure%2Cstring%2Cstring%2Cstring">build: configure(path, check: string; flags = &quot;&quot;)</a></li>
</ul></dd>
<dt><a name="cOverride" href="#cOverride"><span>cOverride:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="cimport: cOverride(body): untyped" href="cimport.html#cOverride.m">cimport: cOverride(body): untyped</a></li>
</ul></dd>
<dt><a name="cOverrides" href="#cOverrides"><span>cOverrides:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="plugin: cOverrides" href="plugin.html#cOverrides">plugin: cOverrides</a></li>
</ul></dd>
<dt><a name="cpFile" href="#cpFile"><span>cpFile:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: cpFile(source, dest: string; move = false)" href="build.html#cpFile%2Cstring%2Cstring">build: cpFile(source, dest: string; move = false)</a></li>
</ul></dd>
<dt><a name="cPlugin" href="#cPlugin"><span>cPlugin:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="cimport: cPlugin(body): untyped" href="cimport.html#cPlugin.m">cimport: cPlugin(body): untyped</a></li>
</ul></dd>
<dt><a name="cSearchPath" href="#cSearchPath"><span>cSearchPath:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="cimport: cSearchPath(path: string): string" href="cimport.html#cSearchPath%2Cstring">cimport: cSearchPath(path: string): string</a></li>
</ul></dd>
<dt><a name="cSkipSymbol" href="#cSkipSymbol"><span>cSkipSymbol:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="cimport: cSkipSymbol(skips: seq[string])" href="cimport.html#cSkipSymbol%2Cseq%5BT%5D%5Bstring%5D">cimport: cSkipSymbol(skips: seq[string])</a></li>
</ul></dd>
<dt><a name="defineEnum" href="#defineEnum"><span>defineEnum:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="types: defineEnum(typ)" href="types.html#defineEnum.t">types: defineEnum(typ)</a></li>
</ul></dd>
<dt><a name="downloadUrl" href="#downloadUrl"><span>downloadUrl:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: downloadUrl(url, outdir: string)" href="build.html#downloadUrl%2Cstring%2Cstring">build: downloadUrl(url, outdir: string)</a></li>
</ul></dd>
<dt><a name="enumOp" href="#enumOp"><span>enumOp:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="types: enumOp(op, typ, typout)" href="types.html#enumOp.t%2C%2C%2C">types: enumOp(op, typ, typout)</a></li>
</ul></dd>
<dt><a name="execAction" href="#execAction"><span>execAction:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: execAction(cmd: string; retry = 0; die = true; cache = false; cacheKey = &quot;&quot;): tuple[
output: string, ret: int]" href="build.html#execAction%2Cstring%2Cint%2Cstring">build: execAction(cmd: string; retry = 0; die = true; cache = false; cacheKey = &quot;&quot;): tuple[
output: string, ret: int]</a></li>
</ul></dd>
<dt><a name="extractTar" href="#extractTar"><span>extractTar:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: extractTar(tarfile, outdir: string)" href="build.html#extractTar%2Cstring%2Cstring">build: extractTar(tarfile, outdir: string)</a></li>
</ul></dd>
<dt><a name="extractZip" href="#extractZip"><span>extractZip:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: extractZip(zipfile, outdir: string)" href="build.html#extractZip%2Cstring%2Cstring">build: extractZip(zipfile, outdir: string)</a></li>
</ul></dd>
<dt><a name="Feature" href="#Feature"><span>Feature:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="cimport: Feature" href="cimport.html#Feature">cimport: Feature</a></li>
</ul></dd>
<dt><a name="findExe" href="#findExe"><span>findExe:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: findExe(exe: string): string" href="build.html#findExe%2Cstring">build: findExe(exe: string): string</a></li>
</ul></dd>
<dt><a name="findFile" href="#findFile"><span>findFile:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: findFile(file: string; dir: string; recurse = true; first = false; regex = false): string" href="build.html#findFile%2Cstring%2Cstring">build: findFile(file: string; dir: string; recurse = true; first = false; regex = false): string</a></li>
</ul></dd>
<dt><a name="flagBuild" href="#flagBuild"><span>flagBuild:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: flagBuild(base: string; flags: openArray[string]): string" href="build.html#flagBuild%2Cstring%2CopenArray%5Bstring%5D">build: flagBuild(base: string; flags: openArray[string]): string</a></li>
</ul></dd>
<dt><a name="getCmakeIncludePath" href="#getCmakeIncludePath"><span>getCmakeIncludePath:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: getCmakeIncludePath(paths: openArray[string]): string" href="build.html#getCmakeIncludePath%2CopenArray%5Bstring%5D">build: getCmakeIncludePath(paths: openArray[string]): string</a></li>
</ul></dd>
<dt><a name="getCompiler" href="#getCompiler"><span>getCompiler:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: getCompiler(): string" href="build.html#getCompiler">build: getCompiler(): string</a></li>
</ul></dd>
<dt><a name="getCurrentNimCompiler" href="#getCurrentNimCompiler"><span>getCurrentNimCompiler:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: getCurrentNimCompiler(): string" href="build.html#getCurrentNimCompiler">build: getCurrentNimCompiler(): string</a></li>
</ul></dd>
<dt><a name="getGccLibPaths" href="#getGccLibPaths"><span>getGccLibPaths:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: getGccLibPaths(mode = &quot;c&quot;): seq[string]" href="build.html#getGccLibPaths%2Cstring">build: getGccLibPaths(mode = &quot;c&quot;): seq[string]</a></li>
</ul></dd>
<dt><a name="getGccPaths" href="#getGccPaths"><span>getGccPaths:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: getGccPaths(mode = &quot;c&quot;): seq[string]" href="build.html#getGccPaths%2Cstring">build: getGccPaths(mode = &quot;c&quot;): seq[string]</a></li>
</ul></dd>
<dt><a name="getHeader" href="#getHeader"><span>getHeader:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: getHeader(header: static[string]; giturl: static[string] = &quot;&quot;;
dlurl: static[string] = &quot;&quot;; outdir: static[string] = &quot;&quot;;
conFlags: static[string] = &quot;&quot;; cmakeFlags: static[string] = &quot;&quot;;
makeFlags: static[string] = &quot;&quot;; altNames: static[string] = &quot;&quot;): untyped" href="build.html#getHeader.m%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D%2Cstatic%5Bstring%5D">build: getHeader(header: static[string]; giturl: static[string] = &quot;&quot;;
dlurl: static[string] = &quot;&quot;; outdir: static[string] = &quot;&quot;;
conFlags: static[string] = &quot;&quot;; cmakeFlags: static[string] = &quot;&quot;;
makeFlags: static[string] = &quot;&quot;; altNames: static[string] = &quot;&quot;): untyped</a></li>
</ul></dd>
<dt><a name="getProjectCacheDir" href="#getProjectCacheDir"><span>getProjectCacheDir:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: getProjectCacheDir(name: string; forceClean = true): string" href="build.html#getProjectCacheDir%2Cstring">build: getProjectCacheDir(name: string; forceClean = true): string</a></li>
</ul></dd>
<dt><a name="gitCheckout" href="#gitCheckout"><span>gitCheckout:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: gitCheckout(file, outdir: string)" href="build.html#gitCheckout%2Cstring%2Cstring">build: gitCheckout(file, outdir: string)</a></li>
</ul></dd>
<dt><a name="gitPull" href="#gitPull"><span>gitPull:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: gitPull(url: string; outdir = &quot;&quot;; plist = &quot;&quot;; checkout = &quot;&quot;)" href="build.html#gitPull%2Cstring%2Cstring%2Cstring%2Cstring">build: gitPull(url: string; outdir = &quot;&quot;; plist = &quot;&quot;; checkout = &quot;&quot;)</a></li>
</ul></dd>
<dt><a name="gitReset" href="#gitReset"><span>gitReset:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: gitReset(outdir: string)" href="build.html#gitReset%2Cstring">build: gitReset(outdir: string)</a></li>
</ul></dd>
<dt><a name="isDefined" href="#isDefined"><span>isDefined:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: isDefined(def: untyped): untyped" href="build.html#isDefined.m%2Cuntyped">build: isDefined(def: untyped): untyped</a></li>
</ul></dd>
<dt><a name="linkLibs" href="#linkLibs"><span>linkLibs:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: linkLibs(names: openArray[string]; staticLink = true): string" href="build.html#linkLibs%2CopenArray%5Bstring%5D">build: linkLibs(names: openArray[string]; staticLink = true): string</a></li>
</ul></dd>
<dt><a name="make" href="#make"><span>make:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: make(path, check: string; flags = &quot;&quot;; regex = false)" href="build.html#make%2Cstring%2Cstring%2Cstring">build: make(path, check: string; flags = &quot;&quot;; regex = false)</a></li>
</ul></dd>
<dt><a name="mkDir" href="#mkDir"><span>mkDir:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: mkDir(dir: string)" href="build.html#mkDir%2Cstring">build: mkDir(dir: string)</a></li>
</ul></dd>
<dt><a name="mvFile" href="#mvFile"><span>mvFile:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: mvFile(source, dest: string)" href="build.html#mvFile%2Cstring%2Cstring">build: mvFile(source, dest: string)</a></li>
</ul></dd>
<dt><a name="myNormalizedPath" href="#myNormalizedPath"><span>myNormalizedPath:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="compat: myNormalizedPath(path: string): string" href="compat.html#myNormalizedPath%2Cstring">compat: myNormalizedPath(path: string): string</a></li>
</ul></dd>
<dt><a name="nimteropRoot" href="#nimteropRoot"><span>nimteropRoot:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="paths: nimteropRoot(): string" href="paths.html#nimteropRoot">paths: nimteropRoot(): string</a></li>
</ul></dd>
<dt><a name="nimteropSrcDir" href="#nimteropSrcDir"><span>nimteropSrcDir:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="paths: nimteropSrcDir(): string" href="paths.html#nimteropSrcDir">paths: nimteropSrcDir(): string</a></li>
</ul></dd>
<dt><a name="OnSymbol" href="#OnSymbol"><span>OnSymbol:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="plugin: OnSymbol" href="plugin.html#OnSymbol">plugin: OnSymbol</a></li>
</ul></dd>
<dt><a name="OnSymbolOverrideFinal" href="#OnSymbolOverrideFinal"><span>OnSymbolOverrideFinal:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="plugin: OnSymbolOverrideFinal" href="plugin.html#OnSymbolOverrideFinal">plugin: OnSymbolOverrideFinal</a></li>
</ul></dd>
<dt><a name="onSymbolOverrideFinal" href="#onSymbolOverrideFinal"><span>onSymbolOverrideFinal:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="plugin: onSymbolOverrideFinal(typ: string): StringHash" href="plugin.html#onSymbolOverrideFinal%2Cstring">plugin: onSymbolOverrideFinal(typ: string): StringHash</a></li>
</ul></dd>
<dt><a name="ptrdiff_t" href="#ptrdiff_t"><span>ptrdiff_t:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="types: ptrdiff_t" href="types.html#ptrdiff_t">types: ptrdiff_t</a></li>
</ul></dd>
<dt><a name="rmDir" href="#rmDir"><span>rmDir:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: rmDir(dir: string)" href="build.html#rmDir%2Cstring">build: rmDir(dir: string)</a></li>
</ul></dd>
<dt><a name="rmFile" href="#rmFile"><span>rmFile:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: rmFile(source: string; dir = false)" href="build.html#rmFile%2Cstring">build: rmFile(source: string; dir = false)</a></li>
</ul></dd>
<dt><a name="sanitizePath" href="#sanitizePath"><span>sanitizePath:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: sanitizePath(path: string; noQuote = false; sep = $&apos;/&apos;): string" href="build.html#sanitizePath%2Cstring">build: sanitizePath(path: string; noQuote = false; sep = $&apos;/&apos;): string</a></li>
</ul></dd>
<dt><a name="setCmakeLibName" href="#setCmakeLibName"><span>setCmakeLibName:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: setCmakeLibName(outdir, name, prefix = &quot;&quot;; oname = &quot;&quot;; suffix = &quot;&quot;)" href="build.html#setCmakeLibName%2Cstring%2Cstring%2Cstring%2Cstring%2Cstring">build: setCmakeLibName(outdir, name, prefix = &quot;&quot;; oname = &quot;&quot;; suffix = &quot;&quot;)</a></li>
</ul></dd>
<dt><a name="setCmakePositionIndependentCode" href="#setCmakePositionIndependentCode"><span>setCmakePositionIndependentCode:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: setCmakePositionIndependentCode(outdir: string)" href="build.html#setCmakePositionIndependentCode%2Cstring">build: setCmakePositionIndependentCode(outdir: string)</a></li>
</ul></dd>
<dt><a name="setCmakeProperty" href="#setCmakeProperty"><span>setCmakeProperty:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: setCmakeProperty(outdir, name, property, value: string)" href="build.html#setCmakeProperty%2Cstring%2Cstring%2Cstring%2Cstring">build: setCmakeProperty(outdir, name, property, value: string)</a></li>
</ul></dd>
<dt><a name="setDefines" href="#setDefines"><span>setDefines:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: setDefines(defs: static openArray[string]): untyped" href="build.html#setDefines.m">build: setDefines(defs: static openArray[string]): untyped</a></li>
</ul></dd>
<dt><a name="sleep" href="#sleep"><span>sleep:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="build: sleep(milsecs: int)" href="build.html#sleep%2Cint">build: sleep(milsecs: int)</a></li>
</ul></dd>
<dt><a name="Symbol" href="#Symbol"><span>Symbol:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="plugin: Symbol" href="plugin.html#Symbol">plugin: Symbol</a></li>
</ul></dd>
<dt><a name="testsIncludeDir" href="#testsIncludeDir"><span>testsIncludeDir:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="paths: testsIncludeDir(): string" href="paths.html#testsIncludeDir">paths: testsIncludeDir(): string</a></li>
</ul></dd>
<dt><a name="time64_t" href="#time64_t"><span>time64_t:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="types: time64_t" href="types.html#time64_t">types: time64_t</a></li>
</ul></dd>
<dt><a name="time_t" href="#time_t"><span>time_t:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="types: time_t" href="types.html#time_t">types: time_t</a></li>
</ul></dd>
<dt><a name="toastExePath" href="#toastExePath"><span>toastExePath:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="paths: toastExePath(): string" href="paths.html#toastExePath">paths: toastExePath(): string</a></li>
</ul></dd>
<dt><a name="va_list" href="#va_list"><span>va_list:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="types: va_list" href="types.html#va_list">types: va_list</a></li>
</ul></dd>
<dt><a name="wchar_t" href="#wchar_t"><span>wchar_t:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="types: wchar_t" href="types.html#wchar_t">types: wchar_t</a></li>
</ul></dd>
</dl>
<div class="row">
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small style="color: var(--hint);">Made with Nim. Generated: 2020-03-24 20:55:42 UTC</small>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -1,208 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
<!-- Google fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
<!-- CSS -->
<title>types</title>
<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
<script type="text/javascript" src="dochack.js"></script>
<script type="text/javascript">
function main() {
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
toggleSwitch.addEventListener('change', switchTheme, false);
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-theme', "dark");
toggleSwitch.checked = true;
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
document.documentElement.setAttribute('data-theme', "light");
toggleSwitch.checked = false;
} else {
const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null;
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
if (currentTheme === 'dark') {
toggleSwitch.checked = true;
}
}
}
}
</script>
</head>
<body onload="main()">
<div class="document" id="documentId">
<div class="container">
<h1 class="title">types</h1>
<div class="row">
<div class="three columns">
<div class="theme-switch-wrapper">
<label class="theme-switch" for="checkbox">
<input type="checkbox" id="checkbox" />
<div class="slider round"></div>
</label>
&nbsp;&nbsp;&nbsp; <em>Dark Mode</em>
</div>
<div id="global-links">
<ul class="simple">
</ul>
</div>
<div id="searchInputDiv">
Search: <input type="text" id="searchInput"
onkeyup="search()" />
</div>
<div>
Group by:
<select onchange="groupBy(this.value)">
<option value="section">Section</option>
<option value="type">Type</option>
</select>
</div>
<ul class="simple simple-toc" id="toc-list">
<li>
<a class="reference reference-toplevel" href="#7" id="57">Types</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#time_t"
title="time_t = time_t_temp.Time"><wbr />time_<wbr />t<span class="attachedType"></span></a></li>
<li><a class="reference" href="#time64_t"
title="time64_t = time_t_temp.Time"><wbr />time64_<wbr />t<span class="attachedType"></span></a></li>
<li><a class="reference" href="#wchar_t"
title="wchar_t {.importc, header: &quot;&lt;cwchar&gt;&quot;.} = object"><wbr />wchar_<wbr />t<span class="attachedType"></span></a></li>
<li><a class="reference" href="#ptrdiff_t"
title="ptrdiff_t = ByteAddress"><wbr />ptrdiff_<wbr />t<span class="attachedType"></span></a></li>
<li><a class="reference" href="#va_list"
title="va_list {.importc, header: &quot;&lt;stdarg.h&gt;&quot;.} = object"><wbr />va_<wbr />list<span class="attachedType"></span></a></li>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#18" id="68">Templates</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#enumOp.t%2C%2C%2C"
title="enumOp(op, typ, typout)"><wbr />enum<wbr />Op<span class="attachedType"></span></a></li>
<li><a class="reference" href="#defineEnum.t"
title="defineEnum(typ)"><wbr />define<wbr />Enum<span class="attachedType"></span></a></li>
</ul>
</li>
</ul>
</div>
<div class="nine columns" id="content">
<div id="tocRoot"></div>
<p class="module-desc"></p>
<div class="section" id="7">
<h1><a class="toc-backref" href="#7">Types</a></h1>
<dl class="item">
<a id="time_t"></a>
<dt><pre><a href="types.html#time_t"><span class="Identifier">time_t</span></a> <span class="Other">=</span> <span class="Identifier">time_t_temp</span><span class="Other">.</span><span class="Identifier">Time</span></pre></dt>
<dd>
</dd>
<a id="time64_t"></a>
<dt><pre><a href="types.html#time64_t"><span class="Identifier">time64_t</span></a> <span class="Other">=</span> <span class="Identifier">time_t_temp</span><span class="Other">.</span><span class="Identifier">Time</span></pre></dt>
<dd>
</dd>
<a id="wchar_t"></a>
<dt><pre><a href="types.html#wchar_t"><span class="Identifier">wchar_t</span></a> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">importc</span><span class="Other">,</span> <span class="Identifier">header</span><span class="Other">:</span> <span class="StringLit">&quot;&lt;cwchar&gt;&quot;</span></span><span class="Other">.}</span></span> <span class="Other">=</span> <span class="Keyword">object</span></pre></dt>
<dd>
</dd>
<a id="ptrdiff_t"></a>
<dt><pre><a href="types.html#ptrdiff_t"><span class="Identifier">ptrdiff_t</span></a> <span class="Other">=</span> <span class="Identifier">ByteAddress</span></pre></dt>
<dd>
</dd>
<a id="va_list"></a>
<dt><pre><a href="types.html#va_list"><span class="Identifier">va_list</span></a> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">importc</span><span class="Other">,</span> <span class="Identifier">header</span><span class="Other">:</span> <span class="StringLit">&quot;&lt;stdarg.h&gt;&quot;</span></span><span class="Other">.}</span></span> <span class="Other">=</span> <span class="Keyword">object</span></pre></dt>
<dd>
</dd>
</dl></div>
<div class="section" id="18">
<h1><a class="toc-backref" href="#18">Templates</a></h1>
<dl class="item">
<a id="enumOp.t,,,"></a>
<dt><pre><span class="Keyword">template</span> <a href="#enumOp.t%2C%2C%2C"><span class="Identifier">enumOp</span></a><span class="Other">(</span><span class="Identifier">op</span><span class="Other">,</span> <span class="Identifier">typ</span><span class="Other">,</span> <span class="Identifier">typout</span><span class="Other">)</span></pre></dt>
<dd>
</dd>
<a id="defineEnum.t"></a>
<dt><pre><span class="Keyword">template</span> <a href="#defineEnum.t"><span class="Identifier">defineEnum</span></a><span class="Other">(</span><span class="Identifier">typ</span><span class="Other">)</span></pre></dt>
<dd>
</dd>
</dl></div>
</div>
</div>
<div class="row">
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small style="color: var(--hint);">Made with Nim. Generated: 2020-03-24 20:55:39 UTC</small>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -1,7 +0,0 @@
time_t types.html#time_t types: time_t
time64_t types.html#time64_t types: time64_t
wchar_t types.html#wchar_t types: wchar_t
ptrdiff_t types.html#ptrdiff_t types: ptrdiff_t
va_list types.html#va_list types: va_list
enumOp types.html#enumOp.t,,, types: enumOp(op, typ, typout)
defineEnum types.html#defineEnum.t types: defineEnum(typ)