Skip to content

Contributing

Build through make, never through a bare cargo

This is the trap that costs the most time

The Makefile resolves the toolchain directory from rust-toolchain.toml via rustup which and puts it first on PATH. A bare cargo may be an older Homebrew one, and then "green on my machine, red in CI" has an explanation nobody enjoys finding.

rustup run 1.92.0 rustc --version
rustup run 1.92.0 cargo --version

Targets

Command What it does
make build build the workspace and bugsaurtarget/debug/bugsaur
make check type-check only; binaries are not runnable afterwards
make fmt check formatting; changes nothing
make clippy clippy across the workspace with -D warnings
make test the offline test suite, --no-fail-fast
make test-one one target: PKG=<crate> [TEST=<file>] [NAME=<filter>]
make fixture-rust build the Rust fixture
make fixture-c / make fixture-cpp build the C and C++ fixtures
make gui / make gui-rust build and launch the GUI on a fixture
make gui-c / make gui-cpp build and launch the GUI on the C or C++ fixture
make serve SOCKET=… run the backend without a window
make clean remove Cargo build artifacts
make help list every target

Documentation site targets:

Command What it does
make docs-deps create the docs virtualenv and install pinned dependencies
make docs-build build the site with --strict
make docs-serve serve the site locally with live reload
make docs-check verify the EN and RU page sets match

Running one test

Whole-workspace runs are slow, and a hanging test makes them useless:

make test-one PKG=php-dbgp-adapter TEST=protocol
make test-one PKG=bugsaur NAME=every_entry_point

Keeping the Cargo cache under control

target/ is disposable. In this workspace it can grow to tens of gigabytes: debug symbols, incremental state and a separate set of artifacts for every binary, integration test and feature combination accumulate there. Cargo does not necessarily remove variants that are no longer useful.

After make clean, do not try to prewarm the cache with --all, --workspace or --all-targets. In particular, do not run these merely to populate target/:

cargo build --all
cargo test --workspace
cargo clippy --workspace --all-targets

Those commands compile targets that ordinary development may never use, including the GUI test harnesses and every integration and E2E test. They quickly recreate the disk usage that cleaning was meant to remove. Full workspace commands are still appropriate when their result is required, for example before a pull request or in CI; they are not cache-warming commands.

Let normal work rebuild only what it needs. To prepare the main application, use the pinned toolchain and build just bugsaur:

RUSTC="$(rustup which rustc --toolchain 1.92.0)" \
  rustup run 1.92.0 cargo build -p bugsaur --locked --offline

For type-checking or tests, keep the same narrow scope:

RUSTC="$(rustup which rustc --toolchain 1.92.0)" \
  rustup run 1.92.0 cargo check -p bugsaur --locked --offline
make test-one PKG=bugsaur NAME=<test-filter>

cargo clean removes generated build artifacts, not downloaded crates in the global Cargo cache. The first build is cold and may take a while; subsequent builds recreate the useful incremental cache automatically.

Live adapter tests

Ignored by default because they need real toolchains installed:

make live-codelldb      # real codelldb over TCP DAP
make live-codelldb-c    # C, the same adapter, its own fixture
make live-codelldb-cpp  # C++, STL containers in the variables pane
make live-dlv-100k      # 100k-element collection pagination through Delve
make live-debugpy       # Python, needs debugpy in the fixture venv
make live-php-docker    # PHP/Xdebug in Docker
make live-test-rust     # test-under-cursor, Rust
make live-test-php      # test-under-cursor, PHP

For live-debugpy, the interpreter defaults to the fixture's venv, because debugpy installs into an environment rather than into the system:

python3 -m venv fixtures/python-hello/.venv
fixtures/python-hello/.venv/bin/pip install debugpy

An existing environment can be pointed at with BUGSAUR_PYTHON.

Acceptance scripts:

make test-m8     # workspace gate, fake adapter, fixtures, live scenarios
make test-m10    # PHP / Xdebug acceptance

The offline rule

make build, make check and make test run with --locked --offline. A build that silently reaches the network is a build that behaves differently on a machine without one — and in CI.

CI

ci.yml runs the workspace gate on macOS and Linux both. One OS is not enough: Unix sockets, 0600 permissions and connection-close behaviour differ, and the whole IPC layer rests on them.

The toolchain is not installed explicitly — rustup on the runner reads rust-toolchain.toml and installs the pinned version with rustfmt and clippy. Pinning a stable action here caused CI to drift ahead of local builds.

docs.yml is separate, and builds the documentation site. It does not depend on the Rust toolchain and should not lengthen the test matrix.

Every ci.yml run also publishes size-optimised archives for Linux x86-64, macOS Intel and macOS Apple Silicon in the run's Artifacts section. They are short-lived builds for testing a commit, not the installation channel. A pushed v* tag runs release.yml and publishes the same platform set under GitHub Releases.

Before opening a pull request

make fmt
make clippy
make test

If you touched documentation:

make docs-check
make docs-build

Both languages must stay in sync — docs-check fails on a page that exists in one language only.

Where the design rationale lives

Decisions are recorded as ADRs rather than argued again in review. If a change contradicts one, the ADR is what to update.

See Architecture Decision Records.