Input File Reference
Home > Input File Reference
An input file (conventionally named *.input.yml) is the primary configuration artefact for tsujikiri. It tells the tool which C++ headers to parse, what to include or exclude, how to transform the result, and what output to produce.
Top-Level Keys
Key |
Type |
Required |
Default |
Purpose |
|---|---|---|---|---|
|
list of strings |
no |
|
Other input config files to merge in as defaults (resolved relative to this file) |
|
string |
no |
|
Base directory prepended to every relative |
|
mapping |
one of |
— |
Single C++ source to parse |
|
list |
one of |
— |
Multiple C++ sources to parse |
|
list |
no |
— |
Named output groups; produces one file per group per target format |
|
list of strings |
no |
|
Clang flags prepended to every source’s |
|
list of strings |
no |
|
|
|
list of strings |
no |
|
Preprocessor definitions prepended to every source’s |
|
mapping |
no |
(all included) |
Default filtering rules |
|
list |
no |
|
Default transform pipeline |
|
mapping |
no |
(empty) |
Output generation settings |
|
mapping |
no |
(built-ins only) |
Custom C++ attribute handlers |
|
mapping |
no |
|
Legacy per-class overrides |
|
mapping or list |
no |
|
Per-format filter/transform/generation overrides |
|
bool |
no |
|
Run the language-appropriate pretty printer on generated output |
|
list of strings |
no |
|
Extra arguments forwarded to the pretty printer CLI |
|
mapping |
no |
(empty) |
Type system declarations: primitive types, typedefs, custom types, containers, smart pointers, conversion rules, and declared functions |
|
mapping |
no |
|
Arbitrary key-value data passed verbatim into the template context as |
The module name used in the generated output (e.g. register_myproject) is derived from the input file name: myproject.input.yml → myproject.
loads — Configuration Loads
loads lets you split shared configuration into reusable files and compose them. Each path is resolved relative to the file declaring it.
loads:
- ./common/base.input.yml
- ../shared/typesystem.input.yml
Merge semantics
Loaded files are processed first and provide defaults. The current file’s values extend or override them:
Value type |
Behaviour |
|---|---|
Scalar ( |
Current file wins on conflict; loaded value used when key is absent |
List ( |
Loaded entries come first, then current file’s entries |
Mapping ( |
Deep-merged; current file wins on scalar-leaf conflicts |
When multiple paths are listed under loads, they are merged in order — earlier entries are treated as deeper defaults, later entries extend them.
For format_overrides, both mapping form and list form are supported in any combination across loaded and loading files — they are normalised to list form during the merge. Output-scoped entries are merged over matching global entries after loading.
Cycle detection
If file A loads B and B loads A (or A loads itself), the cycle is silently broken — each file is loaded at most once per resolution chain.
Nested loads
Loaded files may themselves declare loads. Resolution is recursive: the full merged result of a loaded file (including its own loads) is merged into the parent before the parent’s own values are applied.
Example
# common/filters.input.yml
filters:
classes:
blacklist:
- pattern: ".*Impl$"
is_regex: true
constructors:
include: true
transforms:
- stage: add_type_mapping
from: "engine::String"
to: "std::string"
# game_engine.input.yml
loads:
- common/filters.input.yml
sources:
- path: engine/physics.hpp
- path: engine/audio.hpp
generation:
embed_version: true
The resulting config has the class blacklist and add_type_mapping transform from filters.input.yml, with both sources and embed_version: true from game_engine.input.yml. Transforms from the loaded file appear before any transforms declared in game_engine.input.yml.
basepath — Source Base Directory
basepath is a shorthand for the common directory prefix shared by all your source files. When set, it is prepended to every relative path value under source and sources. Absolute path values are left unchanged.
basepath: "../../engine/include" # relative to this input.yml
sources:
- path: "physics.hpp" # resolves to ../../engine/include/physics.hpp
- path: "audio.hpp" # resolves to ../../engine/include/audio.hpp
Without basepath you would have to repeat the long relative prefix on each entry:
sources:
- path: "../../engine/include/physics.hpp"
- path: "../../engine/include/audio.hpp"
basepath itself is resolved relative to the input file’s directory when it is a relative path, so the two examples above are equivalent. An absolute basepath is used as-is.
Global Source Config
parse_args, include_paths, and defines can be set at the top level to apply the same compiler flags or include directories to every source without repeating them on each entry.
Note:
system_include_pathshas no global counterpart — it remains per-source only. Use per-sourcesystem_include_pathsfor-isystempaths that should suppress warnings from specific directories.
Global values are prepended to each source’s own values, so per-source entries always win for last-flag-wins settings like -std=.
parse_args: ["-std=c++17", "-fno-exceptions"]
include_paths:
- /opt/sdk/include
defines:
- NDEBUG
sources:
- path: engine/physics.hpp
defines: ["ENGINE_PHYSICS=1"] # effective: NDEBUG, ENGINE_PHYSICS=1
- path: engine/audio.hpp
parse_args: ["-std=c++20"] # effective: -std=c++17, -fno-exceptions, -std=c++20
Interaction with loads:
Global source config fields are plain lists, so loads: merge semantics apply: loaded-file values are prepended before the loading file’s values.
# base.input.yml
parse_args: ["-std=c++17"]
# project.input.yml
loads:
- base.input.yml
parse_args: ["-fno-exceptions"]
# effective parse_args: ["-std=c++17", "-fno-exceptions"]
source — Single Source Entry
Use source when parsing a single C++ header.
source:
path: "myproject.hpp" # Path to the C++ header (relative to the input YAML)
parse_args: ["-std=c++17"] # Extra flags forwarded to libclang
include_paths: # Additional include directories
- "/usr/local/include"
- "third_party/include"
defines: # Preprocessor definitions (equivalent to -D flags)
- "NDEBUG"
- "MY_FEATURE=1"
Field |
Type |
Required |
Default |
Notes |
|---|---|---|---|---|
|
string |
yes |
— |
Relative to the input YAML’s directory |
|
list of strings |
no |
|
Any clang flag: |
|
list of strings |
no |
|
Equivalent to |
|
list of strings |
no |
|
Equivalent to |
|
list of strings |
no |
|
Preprocessor definitions; equivalent to |
sources — Multiple Source Entries
Use sources when you need to parse several headers into a single binding module. Each entry may override the top-level filters, transforms, and generation.
sources:
- path: "core/types.hpp"
parse_args: ["-std=c++17"]
filters:
namespaces: ["core"]
generation:
includes: ['"core/types.hpp"']
- path: "ui/widgets.hpp"
parse_args: ["-std=c++20"]
defines: ["HAS_WIDGETS=1"]
transforms:
- stage: suppress_class
pattern: ".*Internal.*"
is_regex: true
generation:
includes: ['"ui/widgets.hpp"']
Per-Source Override Semantics
When a source entry provides filters, transforms, or generation, these replace (not extend) the top-level defaults for that source:
filtersin a source entry: completely replaces the top-levelfiltersfor that sourcetransformsin a source entry: completely replaces the top-leveltransformsfor that sourcegeneration.includesin a source entry: appended to the top-level includes (not replaced)
If a source entry omits a key, the top-level value is used.
Tip: Use per-source overrides when headers from different subsystems need different namespace restrictions or clang flags. Use top-level filters for policies that apply to all sources.
outputs — Multi-Output Groups
Use outputs when you need to produce one output file per group per target format. This is an alternative to source/sources — they are mutually exclusive.
Each group names a subset of source files. When every --target path ends with /, the CLI writes {outdir}/{group.name}{ext} for every group × target combination. The directory is created automatically if it doesn’t exist.
sources:
- path: "math/matrix.hpp"
parse_args: ["-std=c++17"]
- path: "math/vector.hpp"
parse_args: ["-std=c++17"]
- path: "types.hpp"
parse_args: ["-std=c++20"]
filters:
namespaces: ["myproject"]
outputs:
- name: math_bindings
sources: ["matrix.hpp", "vector.hpp"]
- name: types_bindings
sources: ["types.hpp"]
With --target luabridge3 out/ the above produces:
out/math_bindings.cpp— contains onlymatrix.hppandvector.hppsymbolsout/types_bindings.cpp— contains onlytypes.hppsymbols
Each group entry has two fields:
Field |
Type |
Required |
Notes |
|---|---|---|---|
|
string |
yes |
Output filename stem — written as |
|
list of strings |
yes |
Filenames, relative path suffixes, or absolute paths referencing source files; matched against the top-level |
Matching sources to groups
When both a top-level sources: list and outputs: are present, each path in a group’s sources: list is looked up in the global sources table. Absolute paths match exactly. Relative paths match unique trailing path suffixes, so source.h, to/source.h, and path/to/source.h can all resolve to a configured source such as ../../path/to/source.h. The match inherits the full SourceEntry configuration (including parse_args, include_paths, defines, per-source filters, and transforms). If a relative path matches more than one configured source, tsujikiri reports an ambiguity error.
If no matching global source entry is found, a bare source entry (path only) is created for that path. This lets you use outputs: without a top-level sources: key when you only need simple path-based groups with no extra clang flags.
Manifest
When --manifest-file is used, the API manifest is computed from the merged IR of all groups combined. Version tracking stays unified across all output files, regardless of how sources are split into groups.
Requirements
Every
--targetpath must end with/(a directory, not a file).All targets must be directory paths — mixing file and directory targets with
outputs:is an error.
filters — Filtering Rules
The filters section controls which classes, methods, fields, constructors, functions, and enums are included in the binding output.
filters:
namespaces: ["myproject"]
sources:
exclude_patterns: ["*.mm"]
classes:
whitelist: []
blacklist: []
internal: []
methods:
global_blacklist: []
per_class: {}
fields:
global_blacklist: []
per_class: {}
constructors:
include: true
signatures: []
functions:
whitelist: []
blacklist: []
enums:
whitelist: []
blacklist: []
All sub-keys are optional. Omitted keys use their defaults shown above. Filter patterns are plain strings (exact match) or objects with is_regex: true for Python re.fullmatch regex.
See Filtering for complete mechanics and examples for every filter type.
transforms — Transform Pipeline
A list of transform stages applied in order after filtering and attribute processing. Each entry is a mapping with a stage key identifying the operation.
transforms:
- stage: rename_method
class: Vec3
from: getLength
to: length
- stage: suppress_method
class: "*"
pattern: "operator.*"
is_regex: true
- stage: add_type_mapping
from: "juce::String"
to: "std::string"
Transforms mutate the Intermediate Representation in-place. The pipeline runs in list order — earlier stages affect what later stages see.
See Transforms for all 32 built-in stages with full reference and examples.
generation — Output Generation Settings
Controls headers, prefix text, suffix text, and version embedding in the generated output.
generation:
includes:
- "<myproject.hpp>" # Angle-bracket include
- '"relative/path.hpp"' # Quote include (note inner quotes)
prefix: |
// Copyright 2024 My Company
// SPDX-License-Identifier: MIT
postfix: |
// End of generated bindings
embed_version: false
Field |
Type |
Default |
Notes |
|---|---|---|---|
|
list of strings |
|
Each string is emitted as |
|
string |
|
Written verbatim before the template output |
|
string |
|
Written verbatim after the template output |
|
bool |
|
When |
Note on includes format: Write the string exactly as it should appear inside
#include. Use"<vector>"for system headers and'"myfile.hpp"'(YAML string containing C++ quotes) for local headers.
attributes — Custom C++ Attribute Handlers
Maps C++ attribute names to actions. tsujikiri supports several built-in attributes automatically:
[[tsujikiri::skip]]— suppress a node[[tsujikiri::keep]]— force-include a node (overrides filters)[[tsujikiri::rename("name")]]— rename a node[[tsujikiri::readonly]]— mark a field read-only[[tsujikiri::thread_safe]]— mark a method/function as thread-safe[[tsujikiri::doc("text")]]— attach a documentation string[[tsujikiri::rename_argument("old", "new")]]— rename a parameter[[tsujikiri::type_map("CppType", "Target")]]— override a type for one declaration
This section registers additional attribute names for your project:
attributes:
handlers:
"mygame::no_export": skip # [[mygame::no_export]] → set emit=False
"mygame::force_export": keep # [[mygame::force_export]] → set emit=True
"mygame::bind_as": rename # [[mygame::bind_as("newName")]] → set rename
Value |
Effect |
|---|---|
|
Sets |
|
Sets |
|
Sets |
See Attributes for full documentation on how attributes are detected and processed.
tweaks — Legacy Per-Class Overrides
tweaks provides simple per-class overrides. It predates the transforms system and is kept for backward compatibility.
tweaks:
Vec3:
rename: "Vector3" # Rename the class in the output
skip_methods: ["legacy"] # Suppress specific methods by exact name
Prefer transforms for new projects.
rename_classandsuppress_methodtransform stages are more flexible and composable.tweakswill remain supported but receives no new features.
format_overrides — Per-Format Customisation
The format_overrides section customises behaviour for specific output formats. In the mapping form, the key is the format name (e.g. luabridge3, luals, pybind11, pyi) and the override applies to every output.
format_overrides:
luabridge3:
template_extends: |
{% extends "luabridge3.tpl" %}
{% block prologue %}
// Custom file header
{{ super() }}
{% endblock %}
unsupported_types:
- "MyOpaqueHandle"
filters:
namespaces: ["lua_api"]
classes:
blacklist:
- pattern: ".*Internal"
is_regex: true
transforms:
- stage: suppress_class
pattern: "LuaUnused"
generation:
includes: ['"luabridge_extras.hpp"']
prefix: "// LuaBridge3 custom header\n"
postfix: "// LuaBridge3 custom footer\n"
typesystem:
primitive_types:
- { cpp_name: "juce::String", target_name: "str" }
pretty: true
pretty_options:
- "--style=LLVM"
luals:
generation:
includes: []
typesystem_file: luals_types.input.yml # load typesystem from an external file
pretty: false # disable even if top-level pretty: true
When outputs: groups are used, list entries can scope overrides to one output group with output. Entries without output are global; output-scoped entries are deep-merged on top of the matching global format override. Nested mappings merge, lists append, and scalar values from the output-scoped entry win.
outputs:
- name: gameplay_bindings
sources: ["gameplay.hpp"]
- name: editor_bindings
sources: ["editor.hpp"]
format_overrides:
- luabridge3:
generation:
includes: ['"lua_common.hpp"']
prefix: "// Lua bindings\n"
- output: gameplay_bindings
luabridge3:
generation:
includes: ['"gameplay_lua.hpp"'] # appended after lua_common.hpp
prefix: "// Gameplay Lua bindings\n" # replaces the global prefix
Field |
Type |
Default |
Override Behaviour |
|---|---|---|---|
|
string |
|
Inline Jinja2 child template; |
|
list of strings |
|
Appended to the format’s built-in unsupported types list |
|
mapping |
(use effective filters) |
Replaces the effective filter set entirely for this format |
|
list |
|
Appended after all other transforms for this format |
|
list |
|
Appended to the merged includes from top-level and per-source entries |
|
string |
|
Replaces the top-level prefix when non-empty |
|
string |
|
Replaces the top-level postfix when non-empty |
|
bool |
|
OR’d with top-level |
|
mapping |
(absent) |
Inline typesystem declarations; entries take priority over the top-level |
|
string |
|
Path to a YAML file whose |
|
bool or absent |
(inherit) |
|
|
list of strings or absent |
(inherit) |
Override args for the pretty printer; absent = inherit top-level |
|
mapping |
|
Deep-merged over the top-level |
Override Precedence
When a value can come from multiple places, this is the resolution order (highest priority wins):
format_overrides.<format>.filters ← replaces entirely
↑
sources[N].filters ← replaces top-level for that source
↑
filters ← top-level default
For transforms:
[top-level transforms] + [per-source transforms override top-level]
then:
+ [format_overrides.<format>.transforms] ← appended last
For generation includes:
top-level includes
+ per-source includes (for each source entry)
+ format_overrides includes
= final includes list (additive, no deduplication)
For generation prefix/postfix:
format_overrides prefix/postfix replaces top-level when non-empty
top-level prefix/postfix used when format_overrides omits or leaves empty
For pretty printing (highest priority wins):
CLI --pretty [FORMAT...] ← overrides all YAML settings
↑
format_overrides.<format>.pretty ← per-format YAML override
↑
pretty ← top-level YAML default
pretty_options follows the same priority chain; the first non-absent level wins.
Full Example
This configuration combines multi-source, top-level filters, format-specific overrides, and generation settings:
# game_engine.input.yml
sources:
- path: "engine/physics.hpp"
parse_args: ["-std=c++17"]
defines: ["ENGINE_PHYSICS=1"]
filters:
namespaces: ["physics"]
generation:
includes: ['"engine/physics.hpp"']
- path: "engine/audio.hpp"
parse_args: ["-std=c++17"]
defines: ["ENGINE_AUDIO=1"]
filters:
namespaces: ["audio"]
generation:
includes: ['"engine/audio.hpp"']
filters:
classes:
blacklist:
- pattern: ".*Impl$"
is_regex: true
- pattern: ".*Detail$"
is_regex: true
methods:
global_blacklist:
- pattern: "operator.*"
is_regex: true
constructors:
include: true
transforms:
- stage: add_type_mapping
from: "engine::String"
to: "std::string"
generation:
embed_version: true
attributes:
handlers:
"engine::no_script": skip
"engine::script_name": rename
format_overrides:
luabridge3:
generation:
includes: ['"engine/lua_compat.hpp"']
transforms:
- stage: suppress_class
pattern: ".*LuaInternal.*"
is_regex: true
luals:
generation:
prefix: "-- LuaLS type stubs for My Game Engine\n"
pretty / pretty_options — Post-Generation Pretty Printing
When pretty: true, tsujikiri pipes the generated output through the language-appropriate pretty printer before writing it. The pretty printer is determined by the output config’s language field.
Currently registered pretty printers:
Language |
Command |
Used by formats |
|---|---|---|
|
|
|
|
|
|
pretty: true
pretty_options:
- "--style=Google"
- "--sort-includes"
Field |
Type |
Default |
Notes |
|---|---|---|---|
|
bool |
|
Run the pretty printer on generated output |
|
list of strings |
|
Extra CLI arguments forwarded to the pretty printer |
The pretty printer is invoked with - as the filename so it reads from stdin and writes to stdout — no temporary file is created. If the pretty printer binary is not on PATH, tsujikiri raises FileNotFoundError. If the pretty printer exits non-zero, subprocess.CalledProcessError is raised.
Tip: When
languagehas no registered pretty printer (e.g.luals),pretty: trueis silently ignored.
Per-Format and CLI Overrides
pretty can also be controlled per-format via format_overrides or overridden entirely from the CLI:
# Enable globally but disable for the luals format (no registered printer)
pretty: true
pretty_options:
- "--style=Google"
format_overrides:
luals:
pretty: false
pybind11:
pretty: true # force-enable even if top-level were false
pretty_options: # use different options for this format
- "--style=LLVM"
From the CLI, --pretty overrides the YAML setting for all targets or specific ones:
# Enable for all targets (overrides pretty: false in YAML)
tsujikiri -i project.input.yml --target luabridge3 out.cpp --pretty
# Enable only for luabridge3 (disables for all other targets even if YAML says true)
tsujikiri -i project.input.yml \
--target luabridge3 out.cpp \
--target luals out.lua \
--pretty luabridge3
See Override Precedence for the full resolution order.
typesystem — Type System Declarations
The typesystem section provides first-class type metadata to the generator. It lets you declare how C++ types map to target-language types, define container and smart-pointer wrappers, supply conversion rules, and declare functions that the parser cannot see (e.g. templates or generated wrappers).
typesystem:
primitive_types:
- { cpp_name: "int32_t", target_name: "int" }
- { cpp_name: "float", target_name: "float" }
- { cpp_name: "std::string", target_name: "str" }
typedef_types:
- { cpp_name: "EntityId", target_name: "int32_t" }
custom_types:
- { cpp_name: "lua_State" }
container_types:
- { cpp_name: "std::vector", kind: "list" }
- { cpp_name: "std::map", kind: "map" }
- { cpp_name: "std::set", kind: "set" }
- { cpp_name: "std::pair", kind: "pair" }
smart_pointer_types:
- { cpp_name: "std::shared_ptr", kind: "shared", getter: "get" }
- { cpp_name: "std::unique_ptr", kind: "unique", getter: "get" }
declared_functions:
- name: makeCircle
namespace: mylib
return_type: "mylib::Circle*"
parameters:
- { name: radius, type: double }
wrapper_code: "+[](double r) { return mylib::Circle::create(r); }"
doc: "Factory helper for Circle objects"
conversion_rules:
- cpp_type: "mylib::Color"
native_to_target: "%%in.toInt()"
target_to_native: "mylib::Color::fromInt(%%in)"
Sub-keys
Key |
Type |
Purpose |
|---|---|---|
|
list |
Map a C++ type name to a target-language primitive name |
|
list |
Declare a C++ typedef as an alias to another known type |
|
list |
Declare types that exist externally — no binding is generated, but the type is recognised |
|
list |
Declare C++ container templates and their sequence protocol kind |
|
list |
Declare smart pointer templates with their kind and inner-object accessor |
|
list |
Manually declare functions the parser cannot see (templates, wrappers) |
|
list |
Provide native ↔ target conversion code for a C++ type |
primitive_types
Each entry maps one C++ type spelling to a target-language name:
Field |
Type |
Notes |
|---|---|---|
|
string |
Exact C++ type spelling (e.g. |
|
string |
Target type name (e.g. |
typedef_types
Field |
Type |
Notes |
|---|---|---|
|
string |
Typedef name (e.g. |
|
string |
Underlying type (e.g. |
custom_types
Field |
Type |
Notes |
|---|---|---|
|
string |
The C++ type name — recognised as a known external type |
container_types
Field |
Type |
Notes |
|---|---|---|
|
string |
Template name (e.g. |
|
string |
|
smart_pointer_types
Field |
Type |
Default |
Notes |
|---|---|---|---|
|
string |
— |
Template name (e.g. |
|
string |
— |
|
|
string |
|
Member function returning the raw pointer |
declared_functions
Allows the generator to include functions the libclang parser cannot see (template wrappers, factory helpers, etc.).
Field |
Type |
Default |
Notes |
|---|---|---|---|
|
string |
— |
Function name (required) |
|
string |
|
C++ namespace (qualified name becomes |
|
string |
|
C++ return type spelling |
|
list |
|
Each item: |
|
string |
— |
Lambda or callable to emit instead of |
|
string |
— |
Documentation string attached to the function |
conversion_rules
Provides native-to-target and target-to-native conversion expressions for a C++ type. Templates use these to emit appropriate conversion code.
Field |
Type |
Notes |
|---|---|---|
|
string |
C++ type spelling (required) |
|
string |
Expression converting C++ value to target; |
|
string |
Expression converting target value to C++; |
custom_data — User-Defined Template Variables
custom_data is an arbitrary YAML mapping whose contents are passed verbatim into the Jinja2 template context under the key custom_data. Values may be any YAML scalar (string, int, float, bool) or nested mappings and lists.
custom_data:
xyz: 1
abc:
- "a"
- "b"
- "c"
something_else: true
something_new: 42.1337
Inside any template, reference these values with normal Jinja2 syntax:
// scalar
{{ custom_data.xyz }}
// list index
{{ custom_data.abc[1] }}
// list element piped through a filter
{{ custom_data.abc[1] | camel_to_snake }}
// nested mapping
{{ custom_data.nested.key }}
custom_data is available alongside all other top-level template variables (module_name, classes, enums, etc.). When the key is absent or set to null, custom_data is an empty dict {}.
Per-Format custom_data
custom_data can also be declared inside format_overrides to supply format-specific or output+format-specific values. These are deep-merged over the top-level custom_data when generating for that format, so global keys remain available and format keys extend or override them.
custom_data:
env: production # available in every format
format_overrides:
- luabridge3:
custom_data:
backend: lua # adds/overrides only for luabridge3
- output: client
luabridge3:
custom_data:
module: client # adds/overrides only for client output in luabridge3
Deep merge behaviour mirrors the top-level custom_data merge: scalars from the format override win on conflict; lists are appended after the base list; nested mappings are merged recursively.
See Also
Filtering — complete mechanics for every filter type
Transforms — all 32 transform stages with examples
Output Formats — format files, custom formats, template_extends
Attributes — C++ attribute system