feat(tooling): wire consumers in through Debug::Use and a launcher
A consumer loads Crafter.Debug.Build through
#pragma crafter dependency "<Crafter.Debug>" project "tooling/project.cpp"
and calls Debug::Use in its own CrafterBuildProject, the way
Crafter.LintRules gets in. With the consumer's flag on, Use adds the
library from the checkout the pragma loaded, and writes
build/crafter-debug: a POSIX sh launcher for the client that library
builds. The client is one of the library's dependencies, so a debug
build compiles it in the same pass as the app, from the same sources.
Why a launcher and not a script: every `crafter-build run` loads the
whole project first, which costs 13-28 s per call on 3DForts (a git
pull per unpinned dependency plus rescanning sources), while the
launcher is one sandbox start, 0.08 s. A client built in the Flatpak
links the Flatpak's libc++ against the runtime's glibc, so the launcher
starts it through `flatpak run` and never on the host itself; inside a
CrafterBuild sandbox it runs it directly.
The library, the client and examples/Basic are placeholders that prove
the wiring: the debug build writes a launcher whose client exists, a
plain build has no Crafter::Debug symbols and keeps the notes from
tooling/AGENTS.consumer.md, and a fresh plain build writes a stub that
names the flag.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 01:18:07 +01:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
|
|
|
|
|
|
|
|
|
import std;
|
|
|
|
|
import Crafter.Build;
|
|
|
|
|
// This checkout's own build helper. A consumer elsewhere names the repository:
|
|
|
|
|
// #pragma crafter dependency "https://forgejo.catcrafts.net/Catcrafts/Crafter.Debug.git" project "tooling/project.cpp"
|
|
|
|
|
#pragma crafter dependency "../.." project "tooling/project.cpp"
|
|
|
|
|
import Crafter.Debug.Build;
|
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
|
using namespace Crafter;
|
|
|
|
|
|
|
|
|
|
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> args) {
|
|
|
|
|
Configuration cfg;
|
|
|
|
|
cfg.path = "./";
|
|
|
|
|
cfg.name = "Basic";
|
|
|
|
|
cfg.outputName = "basic";
|
|
|
|
|
cfg.type = ConfigurationType::Executable;
|
|
|
|
|
const ArgQuery opts = ApplyStandardArgs(cfg, args);
|
|
|
|
|
|
|
|
|
|
// What every dependency gets: this project's args minus the flags only it
|
|
|
|
|
// reads. A dependency that two paths share must get one list on both.
|
|
|
|
|
std::vector<std::string> depArgs;
|
|
|
|
|
for (const std::string_view arg : args) {
|
|
|
|
|
if (arg != "--debug-api") depArgs.emplace_back(arg);
|
|
|
|
|
}
|
feat(examples): Basic, a ball simulation with a bug to find
The example was a placeholder that printed which build it was. It is
now a headless simulation of five balls dropped onto a 10 cm floor
slab at 60 ticks a second, with a bug planted the way such bugs are
written: collision is checked only where each step lands, so the ball
dropped from 60 m, at 34 m/s, steps over the slab and falls forever.
It shows the consumer side end to end:
- Basic.DebugShim forwards Pump, Advance, QuitRequested, Log, Check,
Emit and Sample to Crafter.Debug in debug builds and does nothing in
plain ones, so the simulation and main have no #ifdef but the
session's.
- Basic.DebugCommands, compiled only when Debug::Use returns true,
registers World_Spawn, World_Reset, World_Clear and Ball_Get, and
watches for the physics, the floor and the balls.
- The simulation checks each ball stays above the floor, emits Bounce
and samples the fastest ball, so `pause --on-check`, `wait --event`
and `trace --history` have something to show.
Driven over the real transport, it shows the whole loop: `log --level
warn` names the ball, `pause --on-check` stops the frame it fails,
`set physics.substeps 4` and `call World_Reset` show the fix holds
before any code changes. The plain build simulates ten seconds and
prints that one ball fell through. Its agent notes describe the app
without the bug.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 03:22:17 +01:00
|
|
|
const bool debugApi = Debug::Use(cfg, { .enabled = opts.Has("--debug-api"), .flag = "--debug-api", .define = "BASIC_DEBUG_API", .args = depArgs });
|
feat(tooling): wire consumers in through Debug::Use and a launcher
A consumer loads Crafter.Debug.Build through
#pragma crafter dependency "<Crafter.Debug>" project "tooling/project.cpp"
and calls Debug::Use in its own CrafterBuildProject, the way
Crafter.LintRules gets in. With the consumer's flag on, Use adds the
library from the checkout the pragma loaded, and writes
build/crafter-debug: a POSIX sh launcher for the client that library
builds. The client is one of the library's dependencies, so a debug
build compiles it in the same pass as the app, from the same sources.
Why a launcher and not a script: every `crafter-build run` loads the
whole project first, which costs 13-28 s per call on 3DForts (a git
pull per unpinned dependency plus rescanning sources), while the
launcher is one sandbox start, 0.08 s. A client built in the Flatpak
links the Flatpak's libc++ against the runtime's glibc, so the launcher
starts it through `flatpak run` and never on the host itself; inside a
CrafterBuild sandbox it runs it directly.
The library, the client and examples/Basic are placeholders that prove
the wiring: the debug build writes a launcher whose client exists, a
plain build has no Crafter::Debug symbols and keeps the notes from
tooling/AGENTS.consumer.md, and a fresh plain build writes a stub that
names the flag.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 01:18:07 +01:00
|
|
|
|
feat(examples): Basic, a ball simulation with a bug to find
The example was a placeholder that printed which build it was. It is
now a headless simulation of five balls dropped onto a 10 cm floor
slab at 60 ticks a second, with a bug planted the way such bugs are
written: collision is checked only where each step lands, so the ball
dropped from 60 m, at 34 m/s, steps over the slab and falls forever.
It shows the consumer side end to end:
- Basic.DebugShim forwards Pump, Advance, QuitRequested, Log, Check,
Emit and Sample to Crafter.Debug in debug builds and does nothing in
plain ones, so the simulation and main have no #ifdef but the
session's.
- Basic.DebugCommands, compiled only when Debug::Use returns true,
registers World_Spawn, World_Reset, World_Clear and Ball_Get, and
watches for the physics, the floor and the balls.
- The simulation checks each ball stays above the floor, emits Bounce
and samples the fastest ball, so `pause --on-check`, `wait --event`
and `trace --history` have something to show.
Driven over the real transport, it shows the whole loop: `log --level
warn` names the ball, `pause --on-check` stops the frame it fails,
`set physics.substeps 4` and `call World_Reset` show the fix holds
before any code changes. The plain build simulates ten seconds and
prints that one ball fell through. Its agent notes describe the app
without the bug.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 03:22:17 +01:00
|
|
|
std::vector<fs::path> interfaces = { "interfaces/Basic.DebugShim", "interfaces/Basic.World" };
|
|
|
|
|
std::vector<fs::path> implementations = { "implementations/Basic.World", "implementations/main" };
|
|
|
|
|
// The agent's commands and watches, in debug builds only.
|
|
|
|
|
if (debugApi) {
|
|
|
|
|
interfaces.emplace_back("interfaces/Basic.DebugCommands");
|
|
|
|
|
implementations.emplace_back("implementations/Basic.DebugCommands");
|
|
|
|
|
}
|
feat(tooling): wire consumers in through Debug::Use and a launcher
A consumer loads Crafter.Debug.Build through
#pragma crafter dependency "<Crafter.Debug>" project "tooling/project.cpp"
and calls Debug::Use in its own CrafterBuildProject, the way
Crafter.LintRules gets in. With the consumer's flag on, Use adds the
library from the checkout the pragma loaded, and writes
build/crafter-debug: a POSIX sh launcher for the client that library
builds. The client is one of the library's dependencies, so a debug
build compiles it in the same pass as the app, from the same sources.
Why a launcher and not a script: every `crafter-build run` loads the
whole project first, which costs 13-28 s per call on 3DForts (a git
pull per unpinned dependency plus rescanning sources), while the
launcher is one sandbox start, 0.08 s. A client built in the Flatpak
links the Flatpak's libc++ against the runtime's glibc, so the launcher
starts it through `flatpak run` and never on the host itself; inside a
CrafterBuild sandbox it runs it directly.
The library, the client and examples/Basic are placeholders that prove
the wiring: the debug build writes a launcher whose client exists, a
plain build has no Crafter::Debug symbols and keeps the notes from
tooling/AGENTS.consumer.md, and a fresh plain build writes a stub that
names the flag.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 01:18:07 +01:00
|
|
|
cfg.GetInterfacesAndImplementations(interfaces, implementations);
|
|
|
|
|
return cfg;
|
|
|
|
|
}
|