Development setup
Start with a checkout of the
repository you want to change.
Run every command from that repository's root, where MODULE.bazel selects the
published Toolchain and SDK versions used by the build.
Build tools
Install Git, Python 3
and Bazelisk as the bazel command.
Bazelisk selects the Bazel version recorded in the checkout's .bazelversion.
Toolchain acquires the matching Clang, platform SDKs, formatter and Web runtime;
do not configure a second compiler inside the repository.
From your checkout, check that the tools are available:
git --version
python3 --version
bazel --version
The first command may download substantial compiler and SDK archives. Keep the dependency versions selected by the checkout. Another copy of TTX, Perimortem or Toolchain beside the repository does not replace its declared dependency.
The shell examples here use Linux. Target platform settings are separate from the host running Bazel. Use the configurations provided by the repository when cross-compiling, and run its tests on a host that can execute the resulting binaries.
Toolchain project setup
Toolchain is consumed as a versioned Bazel module. A new project needs
.bazelversion, .bazelrc, MODULE.bazel, a root BUILD, and a source/BUILD
that describes the library. Toolchain 0.4.1 uses Bazel 9.2.0.
Toolchain release and targets
Pin the released source archive and register the target environments in
MODULE.bazel:
module(
name = "example",
version = "0.1.0",
)
bazel_dep(name = "rules_cc", version = "0.2.17")
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "rules_pkg", version = "1.3.0")
bazel_dep(name = "tetro_toolchain", version = "0.4.1")
archive_override(
module_name = "tetro_toolchain",
sha256 = "9b501a36786fdb96f0708135cff7a62a1178fef66d245a4acae22dd2dd657ae6",
urls = ["https://github.com/tetrodotoxin-dev/Toolchain/releases/download/v0.4.1/tetro_toolchain-0.4.1-source.tar.gz"],
)
register_toolchains(
"@tetro_toolchain//:cc_toolchain_for_linux_x86_64",
"@tetro_toolchain//:cc_toolchain_for_wasm32",
"@tetro_toolchain//:cc_toolchain_for_wasm32_runner",
"@tetro_toolchain//:cc_toolchain_for_windows_x64",
)
Set .bazelversion to 9.2.0. A minimal .bazelrc keeps outputs inside the
checkout and names the supported target configurations:
common --symlink_prefix=.bin/
run --run_in_cwd
build --compilation_mode=dbg
build:debug --compilation_mode=dbg
build:release --compilation_mode=opt
build:release --strip=always
build:linux --platforms=@tetro_toolchain//:linux_x86_64
build:windows --platforms=@tetro_toolchain//:windows_x64
build:web --platforms=@tetro_toolchain//:wasm32
build:web_tests --platforms=@tetro_toolchain//:wasm32_runner
try-import %workspace%/.bazelrc.local
Project layout
Toolchain's library and validation macros give the repository one predictable
shape. Authored library code lives under source/, while executable correctness
and performance checks live under validation/. A small project named example
can begin with this tree:
example/
├── .bazelrc
├── .bazelversion
├── BUILD
├── LICENSE
├── MODULE.bazel
├── source/
│ ├── BUILD
│ └── example/
│ ├── example.cpp
│ └── example.hpp
└── validation/
├── benchmarks/
│ └── example.cpp
├── data/
└── tests/
└── example.cpp
The example directory matches the name in MODULE.bazel. That lets the default
library, package and validation declarations agree on one project identity.
Projects can add subdirectories freely beneath it. Public component boundaries
become useful only when the SDK needs independently consumable header groups;
they are not required to bootstrap one library.
Single-library SDK
Load library() from source/BUILD and give it the implementation and public
headers. The first version can glob the complete source tree:
# source/BUILD
load("@tetro_toolchain//:defs.bzl", "library")
library(
srcs = glob(["example/**/*.cpp"]),
hdrs = glob([
"example/**/*.h",
"example/**/*.hpp",
]),
includes = ["."],
)
With no explicit name or module, library() uses the Bazel module name. This
creates the static //source:example target, publishes headers as
example/example.hpp, and supplies the EXAMPLE export identity used by shared
and static builds. A project can later declare a shared variant or component map
without changing its authored include paths.
Project operations
The root BUILD turns that compiled library into the repository's public and
development surface:
load(
"@tetro_toolchain//:defs.bzl",
"format",
"package",
"snapshot",
"validation",
"vscode",
)
package()
format()
snapshot()
validation()
vscode()
package() publishes //:example and builds the versioned :sdk archives.
validation() recursively compiles the C and C++ files under
validation/tests/ and validation/benchmarks/ into standalone :tests and
:benchmarks programs; both link the project library automatically. Files under
validation/data/ are available to those programs using the same relative paths
under Bazel and direct execution.
format(), snapshot() and vscode() add the repository-owned quality check,
clean-commit source archive and generated editor environment. Add the project
license at LICENSE, then ignore .bin/, .vscode/ and Bazel's generated
workspace links. Projects with public components or external SDK dependencies
extend these declarations rather than replacing the root workflow.
VS Code environment
Repositories on Toolchain 0.4 declare a root :vscode target. Run it before
opening the checkout:
bazel run :vscode
code .
The target creates the complete .vscode/ environment for the selected
Toolchain release:
- C23 and C++26 source paths for the repository and Bazel outputs;
- the pinned Clang formatter, generated include ordering and format-on-save;
- Debug and Release build, test, benchmark and format tasks;
- CodeLLDB launch configurations for the standalone test and benchmark programs;
- extension recommendations for C/C++ editing and native debugging.
Rerunning bazel run :vscode replaces those shared files, including the
formatter path and style, after a Toolchain upgrade. The generated directory is
ignored by repositories using this workflow. Avoid hand-maintaining parallel
tasks or IntelliSense include lists that can drift from the selected build.
Install the recommended extensions when VS Code prompts. For a workspace with
several repositories, add each repository as its own workspace folder and run
:vscode from each root; every folder retains its own dependency pins and Bazel
root.
Project targets
Toolchain 0.4 projects expose one root workflow:
bazel run :format -- --check
bazel build //source/...
bazel run :tests
bazel run :benchmarks --config=release
bazel build :sdk --config=release
format checks canonical formatting, source structure and comment quality.
tests and benchmarks are standalone executables assembled from
validation/tests/ and validation/benchmarks/. sdk builds the public headers,
libraries, runtime dependencies and metadata for every configured target.
Projects that support hosted Web validation can run the same test program through the pinned Emscripten and Node environment:
bazel run :tests --config=web_tests
Web and Windows cross-builds establish compilation and packaging only. Run the produced program on its target before claiming runtime behavior there.
GitHub release
Update the module version and dependency pins before validating the release. Commit that exact state, then run the applicable native and hosted checks from a clean worktree:
bazel run :format -- --check
bazel run :tests --config=release
bazel run :benchmarks --config=release
bazel run :tests --config=web_tests
bazel build :sdk --config=release
bazel run :snapshot
The SDK build writes versioned header, platform and checksum archives under
.bin/bin/. snapshot refuses a dirty worktree and writes the committed source
archive, its SHA-256 file and exact commit under .bin/release/. Review the
generated asset list before publication; a project that omits a linkage variant
or target should not advertise that archive.
Create and push an annotated version tag, then upload those exact artifacts with the GitHub CLI:
git tag -a v0.1.0 -m "Example 0.1.0"
git push origin main v0.1.0
gh release create v0.1.0 --verify-tag \
.bin/bin/example-0.1.0-*.zip \
.bin/bin/example-0.1.0-sha256.txt \
.bin/release/example-0.1.0-source.tar.gz \
.bin/release/example-0.1.0-source.tar.gz.sha256 \
.bin/release/example-0.1.0-source.tar.gz.commit \
--title "Example 0.1.0" \
--notes-file /path/to/release-notes.md
The uploaded snapshot, not GitHub's automatically generated source archive, is
the source product consumed by downstream archive_override declarations. After
publication, download the public assets by URL and prove an independent consumer
against them before updating dependent repositories.
Build outputs and source
Toolchain configures Bazel's output prefix as .bin/. Generated libraries,
standalone validation programs and imported SDK include trees are visible under
.bin/bin/; they remain Bazel outputs rather than authored source.
The common layout is:
source/owns the implementation and public headers;validation/tests/owns correctness cases;validation/benchmarks/owns measured workloads;validation/data/contains runtime fixtures;MODULE.bazelselects released dependencies;- the root
BUILDdeclares formatting, validation, packaging, snapshots and editor generation.
Read the root BUILD before assuming every repository enables every optional
target. Godot and Compute also carry integration-specific targets; continue with the
Godot setup guide or
Compute build instructions
for those workflows.
Tetrodotoxin 0.4 migration
Tetrodotoxin is moving its Source and Puffer work onto the Toolchain 0.4 frontend
for its first release. Until that migration lands, its checked-in .vscode/
configuration and repository-specific //tests:all targets remain authoritative.
Do not replace them with the generated library-project setup or infer target names
from Perimortem and TTX.
The repository's current BUILD.bazel, DEVELOPMENT.md and .vscode/README.md
record which Source, module and Puffer checks are active at that checkpoint.
Once the environment is ready, continue with development and review for ownership, interface comments and checks across a foreign consumer boundary.