Getting Started

Home > Getting Started


Requirements

  • Python 3.12 or later

  • libclang-ng ≥ 19 (Python wrapper around libclang; supports Clang 19–22)


Installation

# From PyPI
pip install tsujikiri

# With uv
uv add tsujikiri

Pinning a Clang version

By default tsujikiri pulls in libclang-ng>=19, which resolves to the latest available Clang release. To pin a specific version use one of the provided extras:

# pip — pick one
pip install "tsujikiri[clang19]"
pip install "tsujikiri[clang20]"
pip install "tsujikiri[clang21]"
pip install "tsujikiri[clang22]"

# uv — pick one
uv add "tsujikiri[clang21]"

Only one clangXX extra may be active at a time; they are mutually exclusive.

Verify the installation:

tsujikiri --list-formats

Expected output:

luabridge3
luals
pybind11
pyi

Your First Binding

This walkthrough takes you from a C++ header to working LuaBridge3 registration code in three steps.

Step 1 — Write your C++ header

// vec3.hpp
#pragma once
#include <cmath>

namespace myproject {

class Vec3 {
public:
    Vec3() = default;
    Vec3(float x, float y, float z) : x_(x), y_(y), z_(z) {}

    float length() const { return std::sqrt(x_*x_ + y_*y_ + z_*z_); }
    float dot(const Vec3& other) const { return x_*other.x_ + y_*other.y_ + z_*other.z_; }
    Vec3  normalized() const { float l = length(); return {x_/l, y_/l, z_/l}; }

    void setX(float v) { x_ = v; }
    void setY(float v) { y_ = v; }
    void setZ(float v) { z_ = v; }

public:
    float x_ = 0.0f;
    float y_ = 0.0f;
    float z_ = 0.0f;
};

} // namespace myproject

Step 2 — Write the input config

# myproject.input.yml
source:
  path: vec3.hpp
  parse_args: ["-std=c++17"]

filters:
  namespaces: ["myproject"]
  classes:
    whitelist: ["Vec3"]
  constructors:
    include: true
  methods:
    global_blacklist:
      - pattern: "operator.*"
        is_regex: true

transforms:
  - stage: modify_field
    class: Vec3
    field: x_
    rename: x
  - stage: modify_field
    class: Vec3
    field: y_
    rename: y
  - stage: modify_field
    class: Vec3
    field: z_
    rename: z

generation:
  includes: ['"vec3.hpp"']

Step 3 — Generate the bindings

tsujikiri -i myproject.input.yml --target luabridge3 bindings.cpp

Output (bindings.cpp):

// DO NOT EDIT - Auto-generated LuaBridge3 bindings for myproject by tsujikiri
extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
} // extern "C"

#include <LuaBridge/LuaBridge.h>

#include <utility>
#include "vec3.hpp"

void register_myproject(lua_State* L)
{
  luabridge::getGlobalNamespace(L)
    .beginNamespace("myproject")
      .beginClass<myproject::Vec3>("Vec3")
        .addConstructor<void (*)(float, float, float)>()
        .addFunction("length", &myproject::Vec3::length)
        .addFunction("dot", &myproject::Vec3::dot)
        .addFunction("normalized", &myproject::Vec3::normalized)
        .addFunction("set_x", &myproject::Vec3::setX)
        .addFunction("set_y", &myproject::Vec3::setY)
        .addFunction("set_z", &myproject::Vec3::setZ)
        .addProperty("x", &myproject::Vec3::x_)
        .addProperty("y", &myproject::Vec3::y_)
        .addProperty("z", &myproject::Vec3::z_)
      .endClass()
    .endNamespace();
}

Notice that setX, setY, setZ are rendered as set_x, set_y, set_z — tsujikiri automatically converts camelCase method names to snake_case in the output.


Pipeline Overview

C++ Header (.hpp)
       │
       ▼  libclang parse
Intermediate Representation (IR)
       │
       ├──▶  FilterEngine          suppress by namespace / class / method / field
       │     (see Filtering)
       │
       ├──▶  AttributeProcessor    read [[tsujikiri::skip]] etc. from C++ source
       │     (see Attributes)
       │
       ├──▶  TransformPipeline     rename, suppress, inject, remap types
       │     (see Transforms)
       │
       ▼  Jinja2 template render
Target Binding Code (.cpp / .lua)

Each phase is independently configurable per input file and per output format. The Input File Reference documents every key. Detailed pages cover Filtering, Transforms, Output Formats, and Attributes.


Dry-Run Workflow

Before generating code, use --dry-run to inspect what will be emitted:

tsujikiri -i myproject.input.yml --target luabridge3 - --dry-run
Format  : luabridge3 1.0
Sources : 1
Classes : 1 — Vec3
Functions: 0 — (none)
Enums   : 0 — (none)
Version : 3a8f...e291

This parses and filters the header without rendering any output. Use it to iterate on your filter and transform config before writing files.


CLI Reference

tsujikiri [OPTIONS]

Flag

Short

Argument

Description

--input

-i

FILE

Input config YAML (required)

--target

-t

FORMAT FILE

Built-in format name or path to .output.yml, plus output file path (- for stdout). Repeatable.

--formats-dir

-f

DIR

Additional directory to search for .output.yml files (repeatable)

--list-formats

Print available formats and exit

--dry-run

Parse and filter only; print IR summary without generating code

--manifest-file

-m

FILE

Write API manifest JSON to FILE; compare if FILE already exists

--check-compat

Exit 1 if manifest shows breaking API changes

--embed-version

Embed the API version hash string in the generated code

--trace-transforms

Print which transform stages ran and on what entities to stderr

--dump-ir

[FILE]

Dump the post-transform IR as JSON to FILE (default: stdout)

--validate-config

Validate input config (regex patterns, stage names) and exit

--verbose

-v

Enable verbose Clang diagnostic output during parsing

--api-version

VERSION

Override the API version string used for --embed-version and api_since/api_until semver filtering

--pretty

[FORMAT...]

Enable pretty printing. No FORMAT args = all targets; with FORMAT names = only those targets. Overrides pretty in input YAML.

--help

-h

Show help and exit

Common Patterns

Print to stdout (quick inspection):

tsujikiri -i project.input.yml --target luabridge3 -

Write to file:

tsujikiri -i project.input.yml --target luabridge3 src/bindings.cpp

Generate multiple outputs in one pass:

tsujikiri -i project.input.yml \
  --target luabridge3 src/bindings.cpp \
  --target luals      types/myproject.lua

Single class (useful during development):

tsujikiri -i project.input.yml --target luabridge3 - -c Vec3

Custom format from a local directory:

tsujikiri -i project.input.yml --target myfmt out/bindings.cpp -f ./my_formats/

API versioning — save manifest and fail on breaking changes:

tsujikiri -i project.input.yml --target luabridge3 src/bindings.cpp \
          -m api.manifest.json --check-compat

Debug transforms — see which stages apply to what:

tsujikiri -i project.input.yml --target luabridge3 - --trace-transforms 2>transforms.log

Dump IR as JSON for inspection:

tsujikiri -i project.input.yml --target luabridge3 - --dump-ir ir.json

Validate config without generating:

tsujikiri -i project.input.yml --validate-config

Enable pretty printing for all targets (overrides YAML):

tsujikiri -i project.input.yml --target luabridge3 src/bindings.cpp --pretty

Enable pretty printing for specific targets only:

tsujikiri -i project.input.yml \
  --target luabridge3 src/bindings.cpp \
  --target luals      types/myproject.lua \
  --pretty luabridge3

List all known formats (including custom directories):

tsujikiri --list-formats -f ./my_formats/

See Also