Files
cargoxx/SPEC.md

26 KiB
Raw Blame History

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 cargoxx with new, build, add, run, clean commands.
  • 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.rs equivalent)
  • Features / conditional compilation flags
  • Dev-dependencies separate from regular dependencies
  • Profiles other than debug and release

2. Glossary

  • Manifest — the Cargoxx.toml file 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.sqlite plus a curated JSON file shipped with cargoxx.
  • Module unit — a .cppm file containing export module foo;.
  • Implementation unit — a .cpp file containing module foo; (no export).
  • Crate root — Cargo terminology for the entry source file. We use the same convention: src/main.cpp is the binary crate root, src/lib.cppm is 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.cppm exists, src/main.cpp (when present) must import <package-name>; — cargoxx does not enforce this with a parser, but documents it.
  • If neither src/main.cpp nor src/lib.cppm exists, cargoxx build errors 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:

  1. Parse the package spec.
  2. If @<version> is omitted, query the resolver for the latest version available in Nixpkgs and use that.
  3. Resolve the link recipe (see §9).
  4. Update Cargoxx.toml [dependencies] table, preserving formatting where possible.
  5. Update Cargoxx.lock.
  6. Regenerate flake.nix.
  7. 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>]

  1. Validate manifest and project layout.
  2. Resolve dependencies if Cargoxx.lock is missing or stale.
  3. Generate build/CMakeLists.txt from manifest and source tree.
  4. Generate / update flake.nix if stale.
  5. 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.
  6. 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:

  • cpp20set(CMAKE_CXX_STANDARD 20)
  • cpp23set(CMAKE_CXX_STANDARD 23)
  • cpp26set(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/.


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:

  1. Query user overlay SQLite. If a row matches and verified_at is recent (<30 days for nix-probe, never expires for curated / manual), return it.
  2. Query the embedded curated JSON. If a range matches, return it.
  3. Return Error::Unknown { package }.

In v0.2, after step 2 the algorithm continues:

  1. Try conan-center-index GitHub raw lookup for recipes/<package>/all/conanfile.py. Parse cmake_target_name, cmake_file_name. Convert to a recipe.
  2. Try microsoft/vcpkg/ports/<package>/usage. Parse the literal CMake snippet.
  3. Run nix-build for the package, then scan the output for lib/cmake/*/*Config.cmake and grep add_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:

  1. Primary — devbox API. GET https://search.devbox.sh/v1/resolve?name=<package>&version=<version>. The JSON response contains commit_hash. Same Jetify backend that powers nixhub.io, but the API is documented and returns the commit directly. (See devbox/internal/searcher/client.go.) 10-second timeout.
  2. Fallback — local nixpkgs git clone. Lazy clone of https://github.com/NixOS/nixpkgs.git at ~/.cache/cargoxx/nixpkgs/ (or $XDG_CACHE_HOME/cargoxx/nixpkgs/). On a miss, run git -C <repo> log --all -S 'version = "<version>"' --pretty='%H %ct' -- pkgs/ and pick the youngest matching commit.
  3. 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 usage files 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].edition is 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.nix and CMakeLists.txt are 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.