~bigbes/tarantool

tarantool-protobuf

ref: ee07048d3ab9678553493c643fb74328e7d5e908 tarantool-protobuf/docs/specs/c_accel_build_packaging.md -rw-r--r-- 8.2 KiB
ee07048d — Eugene Blikh beads: close 74c 2 months ago

#Spec: C-acceleration build and packaging

Status: scaffolding landed, C module pending. This spec records how the C-accelerated runtime is built, installed, and exercised in CI. The opt-in hook in runtime/pb/init.lua, the just build-c recipe, and the CI manifests under .builds/ land alongside this doc; the actual C source they refer to arrives with bd-ra6.

Closes bd-wky.

#Where the C module lives

runtime/pb/
├── c/                       (NEW — C runtime sources)
│   ├── Makefile             (mirrors bench/c_accel/Makefile)
│   ├── c_runtime.c          (entry points + plan struct)
│   ├── plan.c               (descriptor -> plan compiler, bd-mq7)
│   ├── encode.c             (bd-3b)
│   ├── decode.c             (bd-3g)
│   ├── buf.c                (4 KB stack-backed buffer)
│   ├── wkt.c                (override dispatch, bd-3k)
│   └── ...
├── c_runtime.so             (built output — gitignored)
├── c_runtime.dylib          (macOS build output — gitignored)
├── init.lua
├── codec.lua
└── ...

The build output sits alongside the Lua files at runtime/pb/c_runtime.{so,dylib} so require('pb.c_runtime') resolves it via Lua's package.cpath once the test runner adds ./runtime/?.so to LUA_CPATH (see Test runner).

#Build entry points

#just build-c

$ just build-c
make -C runtime/pb/c
cc -O2 -fPIC -Wall -Wextra -std=c99 -I/usr/include/tarantool ... \
    -o runtime/pb/c_runtime.dylib runtime/pb/c/c_runtime.c ...

Mirrors bench/c_accel/Makefile: same TT_INC auto-detection (env override, brew prefix, common system dirs), same -fPIC -O2, same platform-aware extension (.dylib bundle on macOS, .so on Linux).

The recipe is a stub today — it runs make -C runtime/pb/c and errors out cleanly with "no C runtime source yet" until bd-mq7 lands the first source files.

#just clean-c

Removes runtime/pb/c_runtime.so and runtime/pb/c_runtime.dylib. Folded into just clean.

#just build and just test

Unchanged. Building the C module is not part of the default flow. Users who care about C must invoke just build-c explicitly.

This keeps the default contributor experience identical for anyone not working on the C path — just test continues to work without a C compiler.

#Activation

The single switch is the environment variable PB_ENABLE_C. See c_accel_compat.md § Activation for the full contract.

runtime/pb/init.lua does:

local c_runtime
if os.getenv('PB_ENABLE_C') == '1' then
    local ok, mod = pcall(require, 'pb.c_runtime')
    if ok then c_runtime = mod end
end

When c_runtime is non-nil, pb.finalize_message(desc) calls into it to compile a desc.c_plan userdata. When c_runtime is nil, nothing changes — the pure-Lua path runs unchanged.

This hook is wired today; the finalize-time compile call is deferred to bd-mq7.

#Test runner changes

The Justfile's lua_path constant covers Lua module resolution. For the C module, the test recipe also sets LUA_CPATH:

lua_cpath := "./runtime/?.so;./runtime/?.dylib;./runtime/?/init.so;;"

test: gen
    LUA_PATH="{{lua_path}}" LUA_CPATH="{{lua_cpath}}" {{luatest}} -v test/

This is added by the wky landing PR. With runtime/pb/c_runtime.so built, require('pb.c_runtime') resolves to that path.

tt rocks install puts compiled modules under .rocks/lib/tarantool/, which is already on the default cpath; the wky changes only affect the local dev workflow, not installed rocks.

#Rockspec

The current rockspec uses build.type = "builtin" which copies Lua files only. To optionally build the C module, the rockspec gains:

external_dependencies = {
    TARANTOOL = { header = "module.h" },
}

build = {
    type = "builtin",
    modules = {
        ["pb"]               = "runtime/pb/init.lua",
        -- ... existing Lua modules ...
        -- The C module is listed as a buildable source:
        ["pb.c_runtime"] = {
            sources = {
                "runtime/pb/c/c_runtime.c",
                "runtime/pb/c/plan.c",
                "runtime/pb/c/encode.c",
                "runtime/pb/c/decode.c",
            },
            incdirs = { "$(TARANTOOL_INCDIR)" },
        },
    },
}

Behavior:

  • tt rocks install (or luarocks install) detects the C compiler and module.h via external_dependencies. If both are present, the C module compiles. If module.h is missing (older Tarantool, no -dev package), the install fails because the rockspec declares it as a hard dependency.
  • To make the C build truly optional (install succeeds on hosts without module.h), we'd need to use luarocks-specific build.platforms or two separate rockspec variants. Deferred: for now, if the user installs and doesn't have tarantool-dev, installation fails with a clear error message — acceptable because every supported Tarantool environment ships module.h.

The rockspec change lands when the first C sources exist (bd-mq7 or bd-ra6). The wky scaffolding leaves the rockspec at "builtin Lua only" for now and documents the change here.

#CI

Two manifests under .builds/:

  • .builds/pure-lua.yml — full test suite with PB_ENABLE_C unset. Validates the pure-Lua path and the silent-fallback case (env var unset means C is dormant even when present).
  • .builds/c-enabled.yml — same test suite plus just build-c and PB_ENABLE_C=1. Validates the C path end-to-end.

Both run on ubuntu/noble. Sourcehut submits both per push (no matrix; each .builds/*.yml is an independent job).

Until the repo is pushed to a sourcehut-compatible host (or a webhook from sourcecraft.dev fires hut builds submit), the manifests sit idle. They start producing value the day the remote lands. No CI-side configuration is required beyond the manifests themselves.

A third manifest at .sourcehut/conformance.yml (deliberately outside .builds/ so it doesn't auto-submit) runs the Google proto3 conformance suite inside the Docker harness. Submit it manually with hut builds submit .sourcehut/conformance.yml before a release or after wire-format changes. Skipping it from every-push CI is a deliberate cost-control choice — the suite pulls a multi-GB Docker image and runs ~7000 tests; it doesn't belong on the fast-feedback path.

#Platform support

The bench/c_accel/Makefile patterns we mirror in runtime/pb/c/Makefile:

  • Linux x86_64 / aarch64-shared, output is .so.
  • macOS arm64 / x86_64-bundle -undefined dynamic_lookup, output is .dylib. The dynamic_lookup flag is what lets the module use lua_* symbols without linking against a specific Lua/Tarantool binary.
  • Windows / WSL / FreeBSD — out of scope for the initial ship. Tarantool's primary deployment targets are Linux servers and macOS dev hosts; FreeBSD ports exist but aren't on the CI matrix.

#What ships with bd-wky today

  • runtime/pb/init.luaPB_ENABLE_C pcall hook (lines added at module load; exposes c_runtime field on the returned table for introspection).
  • Justfilebuild-c, clean-c recipes; updated lua_cpath variable; test recipe sources LUA_CPATH.
  • .builds/pure-lua.yml, .builds/c-enabled.yml — CI matrix.
  • .sourcehut/conformance.yml — manual conformance trigger.
  • .gitignoreruntime/pb/c_runtime.{so,dylib} ignored.
  • This spec.

#What does NOT ship with bd-wky

  • No actual C source. runtime/pb/c/ doesn't exist; just build-c errors with a clear message until bd-ra6 populates the directory.
  • No rockspec changes (still pure-Lua install). The rockspec changes land with the first C source files (likely bd-mq7 or bd-ra6).
  • No finalize_message C-plan compile call. Lives with bd-mq7.

#References