Skip to content

Project model

besa.toml is the authoritative, build-system-neutral declaration of a BESA C++ project. It says what the project can contain. A backend supplies the requested configuration, BESA resolves it against the model, and the backend realizes the resulting targets and build actions.

The generated CMakeLists.txt is therefore a CMake backend bootstrap rather than a second project description. In particular, project identity, features, test modes, API profiles, dependencies, source roots, and conditional directories are not duplicated there.

Every selectable project capability is a feature. kind is metadata used for presentation and analysis; it does not create separate feature namespaces:

schema = 1

[project]
name = "example"
version = "0.1.0"

[features.build-source]
default = true
kind = "build"

[features.toolchain-cpp]
default = true
kind = "toolchain"

[features.user-docs]
default = false
kind = "documentation"

Conditions are structural and may nest all, any, and not:

when = { all = ["project-foo", { any = ["toolchain-cuda", "toolchain-hip"] }] }

API profiles describe compilation contexts independently of ordinary project-feature selection:

[api.profiles.cpu]
features = ["build-source", "toolchain-cpp"]

[api.profiles.cuda]
features = ["build-source", "toolchain-cpp", "toolchain-cuda"]
predefined = ["__CUDACC__=1"]

Dependencies retain BESA's existing dependency semantics without trying to unify them with an external package manager:

[[dependencies]]
name = "Catch2"
version = "3"
kind = "dev"
provider = "cmake"
when = { all = ["build-testing"] }

Public source roots and conditional project directories are also declared in the model:

[[sources]]
name = "cpp"
path = "src/cpp"
language = "CXX"
api = "public"
when = { all = ["build-source", "toolchain-cpp"] }

[[directories]]
name = "showcase-hello"
path = "showcases/hello"
api = "none"
when = { all = ["showcase-hello"] }

BESA owns the source-prefix conventions below a registered source root. The backend discovers the appropriate contents instead of requiring an exhaustive source-file list in TOML.

Portable feature constraints

Feature logic which cannot be represented by a structural condition can be implemented by a small Python callback. The declaration names the complete feature domain observed by the callback:

[[constraints]]
name = "accelerator-mode"
features = ["toolchain-cuda", "toolchain-hip"]
callback = "tools/constraints.py:accelerator_mode"

The callback receives a dictionary and returns either a boolean or a result dictionary containing success, boolean result, and optional reason. BESA evaluates only the callback's declared finite feature domain and caches the truth table. The cache fingerprint includes the declaration and callback implementation, so changing the function invalidates its cache independently.

Derived configuration space

For API discovery, BESA finds the features actually referenced by public registrations and closes that set over any overlapping constraint domains. It varies only that reduced feature space and crosses it with the declared API profiles. Unrelated features therefore do not cause a global power-set expansion.

The normalized model and derived API configuration space are generated under the workspace's configure_cache/ directory for diagnostics and downstream tooling. They are derived artifacts; besa.toml remains the source of truth.

Workspace

A BESA workspace contains backend build state, generated code, documentation products, and reusable configuration analysis as siblings:

<workspace>/
├── build/
├── codegen/
├── docs/
└── configure_cache/

BESA_WORKSPACE selects the workspace root for the CMake backend. When it is not supplied, BESA uses the parent of PROJECT_BINARY_DIR.

Portable generators write one prefix below codegen/. A generated prefix contains bin/, include/, lib/, and optionally mod/; BESA consumes those directories using the same conventions as ordinary project inputs. Generator context values may be marked as paths, in which case the contents reachable through the path participate in cache invalidation.