Tested: With vcc and tcc on Windows, and The Machinery master branch.
- Modify
tm.nimble
configuration variables section for your needs. - In this repo a
headers
directory is included for wrapping a subset of the headers. - Run
nimble gen
to generate the bindings.
- Run
nimble minimal
to build a minimal sample. More samples are in the samples folder. - Create a plugin scaffold with
nimble new
- Add a new task to
tm.nimble
to build your plugin. See the examples at the bottom of the file.
- Prereqs
- Copy
tcc\intrin.h
to your tcc include folder for win32. It's used bymath.inl
.
- Copy
- Modify tm.nimble to set the compiler to
tcc
- Run
nimble gen
again to regeneratetm/tm_generated.nim
, then build your plugin
- Copy the headers you need from The Machinery to the
headers
dir, or you can try wrapping everything by modifyingglobals.nim
to point to The Machinery SDK headers dir. - Run
nimble gen
The following is a list of common issues and workarounds when adding new headers for the generator. You may need to override the definitions generated by nimterop in tm_gen_override.nim
.
If you get an Error: undeclared identifier
about an opaque type, ending in _o
or _t
, you should add a forward declaration to the C header and push a PR to The Machinery, fix the header locally, or override it in tm_gen_override.nim
, and run nimble gen
again.
TM uses a lot of opaque ptrs, typedefs with _o suffix, and other types that are passed between API calls. If all we have to do is pass these along between calls we can define an empty object
for them. If TM, expects us to define our own data through an opaque type, we can override their definition and add an {.inheritable.}
pragma to them. This makes it possible to create subtypes for opaque types to interop between Nim and C. For example:
# in tm_gen_override.nim
type
tm_simulation_state_o* {.inheritable.} = object
# in our plugin
type
# we can cast
sim_state_o = object of tm_simulation_state_o
# custom data
To handle unions, flatten the structure/union by bringing all the fields into the object. Rely on importc
to handle all the fields correctly.
TM defines some struct
types with another struct
declaration at the top using TM_INHERITS. The workaround is to copy all fields from the referenced struct
into the current struct
.
The Machinery uses lots of function pointers and callbacks. There's a custom pragma tmType
you can attach to a proc to make it easier to interact with the api. Without it you need to cast the proc.
Since TM is written in C we're going to have to manipulate lots of UncheckedArray
s and ptr
/pointer
s. The ptr_math
package is included to make things easier to work with. I've also added iterators for m/items
, m/pairs
that work with UncheckedArray
and ptr T
.
- On Identifier 'Foo' is a stylistic duplicate of identifier 'foo', 'use cPlugin:onSymbol()', modify
tm_gen_onsymbol.nim
to rename the identifier.
In tm_gen.nim
, cImport uses recurse = true
, this preprocesses the headers, flattening them, so we lose track of which file a type actually comes from. For example, if a.h
includes b.h
and b.h
defines foo
, when nimterop creates the header pragma foo
will be marked as coming from a.h
like impaHdr
. If you override something, and reuse a nimterop custom header
pragma you might run into an invalid pragma
error. Replace the nimterop custom header
pragma, with a regular header
pragma that points to the file where the definition exists.
You may run into a situation where after a header file is included, nimterop produces a binding that cannot compile properly. You may see a strange error even though the C code is valid. This may be because a .inl
file is including other files. The .inl
may not be including a header file it has a dependency on, so it can't be preprocessed properly. For example, a define
symbol might be used from api_types.h
, but it is not included in the .inl
.
- TinyC does not recognize
#pragma once
so thetcc_mods.nim
modifies the headers to use#ifndef/#define
to avoid repeated includes. tcc_mods.nim
modifiesfoundation/api_types.h
to check forTCC
so it definesTM_DISABLE_PADDING_WARNINGS
andTM_RESTORE_PADDING_WARNINGS
as nothing. Inside of theif defined(TM_OS_WINDOWS)
, it adds:
#if defined(TCC)
#define TM_DISABLE_PADDING_WARNINGS
#define TM_RESTORE_PADDING_WARNINGS
#else
and an #endif
after the define for TM_RESTORE_PADDING_WARNINGS
.