26 KiB
cargoxx — project specification
A Cargo-style frontend for modern C++ that uses Nix as the source of truth for dependencies and generates CMake for the build. Users author projects with C++23 modules and never touch CMake or flake.nix by hand.
This document defines the user-facing contract: project layout, CLI commands, manifest schema, generated files, and the algorithms that drive code generation. It is the source of truth for what cargoxx must do. Implementation details are in TECH_SPEC.md.
1. Goals and non-goals
Goals (v0.1)
- Single binary CLI
cargoxxwithnew,build,add,run,cleancommands. - Cargo-compatible project layout. A user who knows Cargo recognizes everything.
- Module-first authoring: every C++ source unit is a module interface (
.cppm) or a module implementation (.cpp). Headers are consumed via the global module fragment, not authored. - Reproducible builds via
flake.nix+flake.lock. The Nix store is the dependency cache. - Generated CMake is hidden in
build/and treated as a build artifact. Users never edit it. - Curated link database covering ~25 popular libraries, shipped with the tool.
Non-goals (v0.1)
- Not a workspace tool. No multi-package repos in v0.1.
- Not a publisher. No
cargoxx publish, no central registry. - No support for non-Nix package managers as primary (Conan / vcpkg recipes are read-only data sources for the link database).
- No Windows support in v0.1. Linux + macOS via Nix.
- No automatic resolution of arbitrary packages from Conan / vcpkg recipes. v0.1 ships a hand-curated database; v0.2 adds automatic resolution.
Explicit scope cuts that may be requested but are out
- Cross-compilation
- Custom build scripts (
build.rsequivalent) - Features / conditional compilation flags
- Dev-dependencies separate from regular dependencies
- Profiles other than
debugandrelease
2. Glossary
- Manifest — the
Cargoxx.tomlfile at the project root. - Link recipe — the CMake snippet (
find_package(...)+ target names) needed to consume a package. Stored per(package, version)in the link database. - Link database — SQLite database at
~/.cache/cargoxx/linkdb.sqliteplus a curated JSON file shipped with cargoxx. - Module unit — a
.cppmfile containingexport module foo;. - Implementation unit — a
.cppfile containingmodule foo;(noexport). - Crate root — Cargo terminology for the entry source file. We use the same convention:
src/main.cppis the binary crate root,src/lib.cppmis the library crate root.
3. User-facing project layout
The layout is fixed. cargoxx infers targets from file paths; it does not read globs from the manifest.
my-project/
├── Cargoxx.toml # manifest (committed)
├── Cargoxx.lock # resolved dep versions (committed)
├── flake.nix # generated from manifest (committed)
├── flake.lock # nix lockfile (committed)
├── .gitignore # cargoxx-managed entries
├── src/
│ ├── main.cpp # → binary target named after [package].name
│ ├── lib.cppm # → library target named after [package].name
│ └── bin/
│ └── tool.cpp # → additional binary target "tool"
├── tests/
│ └── basic.cpp # → test target "basic"
├── examples/
│ └── demo.cpp # → example target "demo"
└── build/ # gitignored
├── CMakeLists.txt # generated
├── debug/ # cmake binary dir for debug profile
└── release/ # cmake binary dir for release profile
Target inference rules
Applied in order, each rule produces zero or more CMake targets.
| Path | Target kind | Target name |
|---|---|---|
src/lib.cppm |
static library | <package-name> |
src/main.cpp |
executable | <package-name> |
src/bin/<name>.cpp |
executable | <name> |
src/<dir>/*.cppm |
module units belonging to src/lib.cppm |
(none — added to library) |
src/<dir>/*.cpp |
implementation units belonging to src/lib.cppm |
(none — added to library) |
tests/<name>.cpp |
executable + add_test |
test_<name> |
examples/<name>.cpp |
executable | example_<name> |
Rules are deliberately rigid. If a user wants exotic layouts, they can drop down to raw CMake — but cargoxx will refuse to generate from a layout it doesn't recognize.
Conflict and validation
- If
src/lib.cppmexists,src/main.cpp(when present) mustimport <package-name>;— cargoxx does not enforce this with a parser, but documents it. - If neither
src/main.cppnorsrc/lib.cppmexists,cargoxx builderrors with:no target found: expected src/main.cpp or src/lib.cppm. - File names other than the listed ones in
src/,src/bin/,tests/,examples/are ignored (not an error). This allows users to keep auxiliary scripts in those directories. - Subdirectories under
src/bin/,tests/,examples/are not walked. Each top-level file is one target.
4. Manifest format — Cargoxx.toml
[package]
name = "my-project"
version = "0.1.0"
edition = "cpp23" # one of: cpp20, cpp23, cpp26
authors = ["Name <email>"] # optional
license = "MIT" # optional, SPDX expression
[dependencies]
fmt = "10.2"
spdlog = "1.13"
boost = { version = "1.84", components = ["filesystem", "system"] }
nlohmann_json = "3.11"
[build]
warnings_as_errors = true # optional, default false
sanitizers = ["address"] # optional, list of: address, undefined, thread, leak
Dependency syntax
Two forms allowed:
fmt = "10.2" # version string only
boost = { version = "1.84", components = ["filesystem"] } # table form
Version strings follow Cargo semver semantics ("10.2" means >=10.2.0, <11.0.0). cargoxx resolves to the highest matching version available in the configured Nixpkgs revision.
components is meaningful only for packages whose link recipe declares component support (Boost, Qt, abseil). Specified components map directly to CMake targets.
Reserved fields
The following keys are parsed and stored but not yet acted on. They MUST be accepted without error so manifests written for v0.1 stay valid in later versions:
[dev-dependencies][features][workspace][package].repository[package].description
5. Lockfile — Cargoxx.lock
Generated and updated by cargoxx. Format is TOML, committed to version control.
version = 1
[[package]]
name = "my-project"
version = "0.1.0"
dependencies = [
"fmt 10.2.1",
"spdlog 1.13.0",
]
[[package]]
name = "fmt"
version = "10.2.1"
nixpkgs_attr = "fmt_10"
nixpkgs_rev = "8a3f...c2d1"
linkdb_source = "curated"
[[package]]
name = "spdlog"
version = "1.13.0"
nixpkgs_attr = "spdlog"
nixpkgs_rev = "8a3f...c2d1"
linkdb_source = "curated"
nixpkgs_rev is the same for all entries in a single resolution — cargoxx pins one Nixpkgs revision per project, picked to satisfy all [dependencies] simultaneously. This is the simplest model and matches what Nix flakes do natively.
6. CLI commands
cargoxx new <name> and cargoxx new --lib <name>
Creates a new project directory.
Default (cargoxx new foo) creates a binary project:
foo/
├── Cargoxx.toml
├── flake.nix
├── flake.lock
├── .gitignore
└── src/
└── main.cpp
With --lib:
foo/
├── Cargoxx.toml
├── flake.nix
├── flake.lock
├── .gitignore
└── src/
└── lib.cppm
src/main.cpp template:
import std;
int main() {
std::println("Hello from {}!", "foo");
return 0;
}
src/lib.cppm template:
export module foo;
import std;
export namespace foo {
auto greeting() -> std::string_view {
return "Hello from foo!";
}
}
After scaffolding, runs cargoxx build --no-build (generate flake.nix and CMakeLists.txt, do not invoke nix/cmake) so the project opens correctly in IDEs immediately.
cargoxx add <pkg>[@<version>] and cargoxx add <pkg> --components <a,b>
Adds a dependency.
Algorithm:
- Parse the package spec.
- If
@<version>is omitted, query the resolver for the latest version available in Nixpkgs and use that. - Resolve the link recipe (see §9).
- Update
Cargoxx.toml[dependencies]table, preserving formatting where possible. - Update
Cargoxx.lock. - Regenerate
flake.nix. - Print the chosen version and link recipe source for transparency:
Added fmt 10.2.1 (linkdb: curated).
If the link recipe cannot be resolved, the command fails with instructions for the user to file an issue or supply a manual recipe via Cargoxx.toml.
cargoxx remove <pkg>
Inverse of add. Removes from manifest, lockfile, and regenerates flake.nix.
cargoxx build [--release] [--target <name>]
- Validate manifest and project layout.
- Resolve dependencies if
Cargoxx.lockis missing or stale. - Generate
build/CMakeLists.txtfrom manifest and source tree. - Generate / update
flake.nixif stale. - Run
nix develop --command cmake -B build/<profile> -S build -G Ninja -DCMAKE_BUILD_TYPE=<Profile>if the binary dir doesn't exist or is stale. - Run
nix develop --command cmake --build build/<profile> [--target <name>].
<profile> is debug (default) or release (with --release). Capitalized <Profile> is Debug or Release.
cargoxx run [--release] [--bin <name>] [-- <args>...]
build then exec the chosen binary. If the project has multiple binary targets and --bin is omitted, fail with a list. If exactly one binary exists, run it.
cargoxx test [--release]
Build all tests/*.cpp targets, then run them via ctest --output-on-failure inside build/<profile>.
cargoxx clean
Removes build/. Does not touch Cargoxx.lock or flake.lock.
cargoxx fmt and cargoxx check
Stubs in v0.1 — accepted but only print a deprecation-style message: cargoxx fmt: not implemented in v0.1, run clang-format directly. This reserves the command names.
7. Generated flake.nix
The shared nixpkgs input always tracks nixos-unstable and provides the
toolchain (clang, cmake, ninja, libc++). Each pinned manifest dep
(<pkg>@<concrete-version>) gets its own additional nixpkgs_<...>
input pointing at a specific commit; the dep is built from that
flake's pkgs set instead of the shared one. Wildcard deps stay on
the shared nixpkgs.
{
description = "<<package.name>>";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
# one line per pinned dep, in manifest order:
nixpkgs_<<sanitized_dep_a>>.url = "github:NixOS/nixpkgs/<<rev_a>>";
nixpkgs_<<sanitized_dep_b>>.url = "github:NixOS/nixpkgs/<<rev_b>>";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, nixpkgs_<<sanitized_dep_a>>,
nixpkgs_<<sanitized_dep_b>>, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
pkgs_nixpkgs_<<sanitized_dep_a>> =
import nixpkgs_<<sanitized_dep_a>> { inherit system; };
pkgs_nixpkgs_<<sanitized_dep_b>> =
import nixpkgs_<<sanitized_dep_b>> { inherit system; };
llvmPkgs = pkgs.llvmPackages;
in {
devShell = llvmPkgs.libcxxStdenv.mkDerivation {
name = "shell";
version = "1.0";
nativeBuildInputs = [
pkgs.ninja
pkgs.cmake
pkgs.clang-tools
];
buildInputs = [
# pinned dep: from the per-dep nixpkgs set
pkgs_nixpkgs_<<sanitized_dep_a>>.<<dep_a_attr>>
# wildcard / unpinned dep: from the shared `pkgs`
pkgs.<<dep_c_attr>>
];
env.NIX_CFLAGS_COMPILE = toString [
"-stdlib=libc++"
"-Wno-unused-command-line-argument"
"-B${pkgs.lib.getLib pkgs.libcxx}/lib"
"-isystem ${pkgs.lib.getDev pkgs.libcxx}/include/c++/v1"
];
hardeningDisable = [
"all"
];
};
});
}
<<sanitized_dep>> is <dep-name>_<dep-version> with every char
outside [a-zA-Z0-9_] replaced by _. The same identifier is reused
in three places: the input attribute, the outputs lambda parameter,
and the pkgs_... let binding — keeping it a valid Nix identifier
in all three contexts.
<<rev_a>> etc. come from Cargoxx.lock's per-dep nixpkgs_rev
field. Wildcard pins (pkg = "*") leave the field null and emit no
extra input.
The toolchain (clang_21, cmake, ninja) is fixed in v0.1 — Clang
because module support is most mature there.
Regeneration is idempotent: cargoxx writes the file only if its
content would change. This avoids spurious flake.lock updates.
8. Generated build/CMakeLists.txt
cmake_minimum_required(VERSION 3.30)
project(<<package.name>> LANGUAGES CXX)
# ----- toolchain configuration -----
set(CMAKE_CXX_STANDARD <<edition-numeric>>)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Generated by cargoxx — do not edit.
# Source of truth: ../Cargoxx.toml
# ----- dependencies -----
<<one find_package line per dependency, from link recipe>>
# ----- library target (if present) -----
<<if src/lib.cppm exists>>
add_library(<<package.name>> STATIC)
target_sources(<<package.name>>
PUBLIC
FILE_SET CXX_MODULES FILES
../src/lib.cppm
<<each additional .cppm under src/, recursive>>
PRIVATE
<<each .cpp under src/ except src/main.cpp and src/bin/*, recursive>>
)
target_link_libraries(<<package.name>> PUBLIC
<<each dep's CMake target name from link recipe>>
)
<<endif>>
# ----- binary target (if src/main.cpp exists) -----
<<if src/main.cpp exists>>
add_executable(<<package.name>>_bin ../src/main.cpp)
set_target_properties(<<package.name>>_bin PROPERTIES OUTPUT_NAME <<package.name>>)
target_link_libraries(<<package.name>>_bin PRIVATE
<<package.name>> # only if library target also exists
<<each dep's CMake target name>>
)
<<endif>>
# ----- additional binaries (src/bin/*.cpp) -----
<<for each src/bin/<name>.cpp>>
add_executable(<<name>> ../src/bin/<<name>>.cpp)
target_link_libraries(<<name>> PRIVATE
<<package.name>> # only if library target also exists
<<each dep's CMake target name>>
)
<<endfor>>
# ----- tests -----
<<if any tests/*.cpp>>
enable_testing()
<<for each tests/<name>.cpp>>
add_executable(test_<<name>> ../tests/<<name>>.cpp)
target_link_libraries(test_<<name>> PRIVATE
<<package.name>> # only if library target also exists
<<each dep's CMake target name>>
)
add_test(NAME <<name>> COMMAND test_<<name>>)
<<endfor>>
<<endif>>
# ----- examples -----
<<for each examples/<name>.cpp>>
add_executable(example_<<name>> ../examples/<<name>>.cpp)
target_link_libraries(example_<<name>> PRIVATE
<<package.name>> # only if library target also exists
<<each dep's CMake target name>>
)
<<endfor>>
# ----- warnings and sanitizers from [build] section -----
<<if build.warnings_as_errors>>
foreach(target_name IN ITEMS <<all_targets>>)
target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wpedantic -Werror)
endforeach()
<<endif>>
<<if build.sanitizers non-empty>>
<<emit -fsanitize=... flags via target_compile_options and target_link_options>>
<<endif>>
Edition mapping:
cpp20→set(CMAKE_CXX_STANDARD 20)cpp23→set(CMAKE_CXX_STANDARD 23)cpp26→set(CMAKE_CXX_STANDARD 26)(CMake 3.30 supports the value as experimental)
Source paths are written relative to build/ (i.e. prefixed with ../) because the file lives in build/CMakeLists.txt but sources live in ../src/.
9. Link database — schema and resolution
On-disk format
Curated database shipped with cargoxx, embedded as a resource (compiled in or read from ${prefix}/share/cargoxx/linkdb.json):
{
"version": 1,
"packages": {
"fmt": [
{
"version": ">=10.0.0",
"nixpkgs_attr": "fmt_10",
"find_package": "fmt CONFIG REQUIRED",
"targets": ["fmt::fmt"]
},
{
"version": ">=8.0.0,<10.0.0",
"nixpkgs_attr": "fmt_8",
"find_package": "fmt CONFIG REQUIRED",
"targets": ["fmt::fmt"]
}
],
"boost": [
{
"version": ">=1.70.0",
"nixpkgs_attr": "boost",
"find_package": "Boost REQUIRED COMPONENTS {{components}}",
"targets": ["Boost::{{component}}"],
"components": "supported"
}
],
"openssl": [
{
"version": "*",
"nixpkgs_attr": "openssl",
"find_package": "OpenSSL REQUIRED",
"targets": ["OpenSSL::SSL", "OpenSSL::Crypto"]
}
]
}
}
The version field is a Cargo-style range. cargoxx picks the first entry whose range matches the resolved version.
{{components}} and {{component}} are Mustache-style placeholders substituted at codegen time.
User-overlay database
~/.cache/cargoxx/linkdb.sqlite:
CREATE TABLE recipes (
package TEXT NOT NULL,
version_range TEXT NOT NULL,
nixpkgs_attr TEXT NOT NULL,
find_package TEXT NOT NULL,
targets TEXT NOT NULL, -- JSON array
components TEXT, -- 'supported' | NULL
source TEXT NOT NULL, -- 'curated' | 'conan' | 'vcpkg' | 'nix-probe' | 'manual'
verified_at INTEGER NOT NULL, -- unix epoch
PRIMARY KEY (package, version_range, source)
);
CREATE TABLE resolution_failures (
package TEXT NOT NULL,
version TEXT NOT NULL,
last_attempt INTEGER NOT NULL,
error TEXT NOT NULL,
PRIMARY KEY (package, version)
);
Lookup precedence: user overlay (any source) → curated (shipped JSON) → automatic resolution (v0.2+) → fail.
Resolution algorithm (v0.1)
resolve_link_recipe(package, version) -> Recipe | Error:
- Query user overlay SQLite. If a row matches and
verified_atis recent (<30 days fornix-probe, never expires forcurated/manual), return it. - Query the embedded curated JSON. If a range matches, return it.
- Return
Error::Unknown { package }.
In v0.2, after step 2 the algorithm continues:
- Try
conan-center-indexGitHub raw lookup forrecipes/<package>/all/conanfile.py. Parsecmake_target_name,cmake_file_name. Convert to a recipe. - Try
microsoft/vcpkg/ports/<package>/usage. Parse the literal CMake snippet. - Run
nix-buildfor the package, then scan the output forlib/cmake/*/*Config.cmakeand grepadd_library(... IMPORTED)lines.
Each successful step writes the result to the user overlay before returning.
10. Version resolution algorithm
cargoxx pins per dep: each <pkg>@<concrete-version> resolves to
its own nixpkgs revision and lands as a separate flake input. The
shared nixpkgs input always tracks nixos-unstable and provides
the toolchain.
resolve_version(package, version) -> nixpkgs_rev:
- Primary — devbox API.
GET https://search.devbox.sh/v1/resolve?name=<package>&version=<version>. The JSON response containscommit_hash. Same Jetify backend that powers nixhub.io, but the API is documented and returns the commit directly. (Seedevbox/internal/searcher/client.go.) 10-second timeout. - Fallback — local nixpkgs git clone. Lazy clone of
https://github.com/NixOS/nixpkgs.gitat~/.cache/cargoxx/nixpkgs/(or$XDG_CACHE_HOME/cargoxx/nixpkgs/). On a miss, rungit -C <repo> log --all -S 'version = "<version>"' --pretty='%H %ct' -- pkgs/and pick the youngest matching commit. - If both fail, return
ResolutionVersionNotFound.
When cargoxx add <pkg>@<ver> succeeds, the rev is written to Cargoxx.lock next to the dep entry (nixpkgs_rev). cargoxx build then merges the lockfile rather than overwriting it — pins survive arbitrary rebuilds. Wildcard pins (<pkg> with no @<ver>, or <pkg>@*) skip resolution entirely; the dep tracks the shared nixos-unstable input.
There is no whole-project rev solver. Two pinned deps may pull from different nixpkgs commits, with the ABI-mismatch risk that implies (different glibc / libc++ majors). cargoxx accepts that trade-off in exchange for fine-grained control; a future cargoxx update may surface compatibility warnings.
11. Curated package list (v0.1)
cargoxx ships with link recipes for these 25 packages. They cover the common cases. The list is fixed for v0.1; any package outside it requires a v0.2 automatic resolver or a manual user-overlay entry.
| Package | Nixpkgs attr | CMake find_package |
CMake targets |
|---|---|---|---|
| fmt | fmt |
fmt CONFIG REQUIRED |
fmt::fmt |
| spdlog | spdlog |
spdlog CONFIG REQUIRED |
spdlog::spdlog |
| nlohmann_json | nlohmann_json |
nlohmann_json CONFIG REQUIRED |
nlohmann_json::nlohmann_json |
| boost | boost |
Boost REQUIRED COMPONENTS {{c}} |
Boost::{{c}} |
| openssl | openssl |
OpenSSL REQUIRED |
OpenSSL::SSL, OpenSSL::Crypto |
| zlib | zlib |
ZLIB REQUIRED |
ZLIB::ZLIB |
| sqlite3 | sqlite |
SQLite3 REQUIRED |
SQLite::SQLite3 |
| curl | curl |
CURL REQUIRED |
CURL::libcurl |
| protobuf | protobuf |
Protobuf REQUIRED |
protobuf::libprotobuf |
| grpc | grpc |
gRPC CONFIG REQUIRED |
gRPC::grpc++ |
| abseil-cpp | abseil-cpp |
absl CONFIG REQUIRED |
absl::{{c}} |
| gtest | gtest |
GTest CONFIG REQUIRED |
GTest::gtest, GTest::gtest_main |
| catch2 | catch2_3 |
Catch2 CONFIG REQUIRED |
Catch2::Catch2WithMain |
| eigen | eigen |
Eigen3 CONFIG REQUIRED |
Eigen3::Eigen |
| tbb | tbb |
TBB CONFIG REQUIRED |
TBB::tbb |
| libpng | libpng |
PNG REQUIRED |
PNG::PNG |
| libjpeg | libjpeg |
JPEG REQUIRED |
JPEG::JPEG |
| freetype | freetype |
Freetype REQUIRED |
Freetype::Freetype |
| glfw | glfw |
glfw3 CONFIG REQUIRED |
glfw |
| glm | glm |
glm CONFIG REQUIRED |
glm::glm |
| sdl2 | SDL2 |
SDL2 CONFIG REQUIRED |
SDL2::SDL2 |
| cli11 | cli11 |
CLI11 CONFIG REQUIRED |
CLI11::CLI11 |
| cxxopts | cxxopts |
cxxopts CONFIG REQUIRED |
cxxopts::cxxopts |
| range-v3 | range-v3 |
range-v3 CONFIG REQUIRED |
range-v3::range-v3 |
| magic_enum | magic-enum |
magic_enum CONFIG REQUIRED |
magic_enum::magic_enum |
Verification of these entries is part of acceptance for v0.1 — see §13 of TECH_SPEC.md.
12. Error model
User-facing errors must be actionable. Each error has a code, a one-line message, and a "what to do" hint.
Examples:
error[E0001]: no target found
--> ./
expected one of: src/main.cpp, src/lib.cppm
hint: run `cargoxx new --lib <name>` to create a library project
error[E0042]: package not in link database
--> Cargoxx.toml:7:1
package "obscurelib" has no known CMake link recipe
hint: file an issue at <repo>/issues/new, or add a manual recipe via:
cargoxx linkdb add obscurelib --find-package "obscurelib CONFIG REQUIRED" --targets "obscurelib::obscurelib"
error[E0010]: unsatisfiable version constraint
fmt = "11.0" is not available in any Nixpkgs revision that also has spdlog = "1.13"
available revisions for fmt 11.0: 8a3f...c2d1, 7e21...b09a
available revisions for spdlog 1.13: 4d22...e1f8
hint: relax one constraint and re-run `cargoxx add`
The full list of error codes is in TECH_SPEC.md §6.
13. Tools and dependencies (host project)
cargoxx itself is a C++23 application. It is a single static binary at install time.
| Concern | Library | Why |
|---|---|---|
| TOML parsing | toml++ (header-only) |
Best C++ TOML library, supports comment-preserving writes via toml::table |
| JSON parsing | nlohmann/json |
Familiar, header-only, fine for our scale |
| HTTP client | cpp-httplib (header-only) |
No OpenSSL hard dep at build time, bundled TLS via system OpenSSL |
| SQLite | sqlite3 C API directly |
One translation unit, no abstraction tax |
| Git operations | shell out to git |
libgit2 is a heavy dep for what we need |
| Subprocess | reproc (or boost::process) |
Cross-platform, capture stdout/stderr cleanly |
| CLI parsing | CLI11 |
Subcommand support, good UX |
| Logging | spdlog |
Everyone has it |
| Filesystem | std::filesystem |
Built in |
| Testing | Catch2 v3 |
Module support, modern |
Bootstrap toolchain: clang_21, cmake_3_30+, ninja. Provided by a bootstrap flake.nix shipped in cargoxx's own repo.
14. Security considerations
- cargoxx never runs commands from network responses. Conan recipes are Python files; v0.2's automatic resolver MUST parse them as text (regex / minimal AST), never execute them.
- vcpkg
usagefiles are plain text and safe to read. - nixhub.io and lazamar are trusted only as version directories. The actual fetch happens through Nix, which verifies hashes via
flake.lock. - The user overlay SQLite is per-user with default permissions (0600).
- cargoxx never writes outside the project directory and
~/.cache/cargoxx/.
15. Versioning and stability
v0.1 is explicitly experimental. Manifest format may break at v0.2. After v1.0:
- Bumping
[package].editionis the only way to opt into breaking C++ language changes. - The manifest format is stable across minor versions.
- The link database format is independent of the manifest version.
- Generated
flake.nixandCMakeLists.txtare not part of the stability contract — they're build artifacts.
Cargoxx.lock format version is bumped on incompatible changes; cargoxx refuses to read a newer lockfile than it understands.