diff --git a/.gitignore b/.gitignore index cf6e331..fc2f0ca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Build outputs -/build/ +/build/* +!/build/CMakeLists.txt /result /result-* diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index bb147a0..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,96 +0,0 @@ -cmake_minimum_required(VERSION 3.30) - -# Opt into experimental C++ modules dyndep + `import std;` support. -# Required until CMake declares these stable; see CMake docs for the current UUID. -set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) -set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444") -set(CMAKE_CXX_MODULE_STD ON) - -project(cargoxx LANGUAGES CXX VERSION 0.1.0) - -# Phase 0: hand-written CMakeLists.txt. Replaced by generated build/CMakeLists.txt -# at milestone M3 once cargoxx can build itself. See TECH_SPEC.md §15. - -set(CMAKE_CXX_STANDARD 23) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS ON) -set(CMAKE_CXX_SCAN_FOR_MODULES ON) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type" FORCE) -endif() - -# TECH_SPEC.md §17: -Wall -Wextra -Wpedantic -Wconversion, -Werror in CI. -add_compile_options(-Wall -Wextra -Wpedantic -Wconversion) - -option(CARGOXX_WERROR "Treat warnings as errors" OFF) -if(CARGOXX_WERROR) - add_compile_options(-Werror) -endif() - -find_package(SQLite3 REQUIRED) -find_package(reproc REQUIRED) - -# ----- cargoxx library: module units + implementation units ----- -add_library(cargoxx STATIC) -target_include_directories(cargoxx SYSTEM PRIVATE third_party) -target_sources(cargoxx - PRIVATE - src/util/error.cpp - src/util/semver.cpp - src/manifest/parser.cpp - src/manifest/writer.cpp - src/layout/layout.cpp - src/lockfile/lockfile.cpp - src/linkdb/database.cpp - src/linkdb/overlay.cpp - src/codegen/flake.cpp - src/codegen/cmake.cpp - src/exec/subprocess.cpp - src/resolver/nixpkgs_probe.cpp - src/resolver/nix_cmake_scan.cpp - src/resolver/pc_scan.cpp - src/resolver/conan_probe.cpp - src/resolver/vcpkg_probe.cpp - src/resolver/verify_link.cpp - src/resolver/discover.cpp - src/resolver/search_devbox.cpp - src/resolver/nixpkgs_git.cpp - src/resolver/version_resolve.cpp - src/cli/cmd_new.cpp - src/cli/cmd_build.cpp - src/cli/cmd_run.cpp - src/cli/cmd_test.cpp - src/cli/cmd_clean.cpp - src/cli/cmd_add.cpp - src/cli/cmd_remove.cpp - src/cli/cmd_linkdb_add.cpp - src/cli/run.cpp - PUBLIC - FILE_SET CXX_MODULES FILES - src/lib.cppm - src/util/util.cppm - src/exec/exec.cppm - src/manifest/manifest.cppm - src/lockfile/lockfile.cppm - src/layout/layout.cppm - src/linkdb/linkdb.cppm - src/resolver/resolver.cppm - src/codegen/codegen.cppm - src/cli/cli.cppm -) - -target_link_libraries(cargoxx PRIVATE SQLite::SQLite3 reproc) - -# ----- cargoxx binary ----- -add_executable(cargoxx_bin src/main.cpp) -set_target_properties(cargoxx_bin PROPERTIES OUTPUT_NAME cargoxx) -target_link_libraries(cargoxx_bin PRIVATE cargoxx) - -# ----- tests ----- -option(CARGOXX_BUILD_TESTS "Build cargoxx tests" ON) -if(CARGOXX_BUILD_TESTS) - enable_testing() - add_subdirectory(tests) -endif() diff --git a/Cargoxx.lock b/Cargoxx.lock new file mode 100644 index 0000000..629f3d9 --- /dev/null +++ b/Cargoxx.lock @@ -0,0 +1,24 @@ +version = 1 + +[[package]] +dependencies = [ 'reproc *', 'sqlite *', 'catch2_3 *' ] +name = 'cargoxx' +version = '0.1.0' + +[[package]] +linkdb_source = 'nix-probe' +name = 'reproc' +nixpkgs_attr = 'reproc' +version = '*' + +[[package]] +linkdb_source = 'pkg-config' +name = 'sqlite' +nixpkgs_attr = 'sqlite' +version = '*' + +[[package]] +linkdb_source = 'nix-probe' +name = 'catch2_3' +nixpkgs_attr = 'catch2_3' +version = '*' diff --git a/TECH_SPEC.md b/TECH_SPEC.md index 1978d6a..437d905 100644 --- a/TECH_SPEC.md +++ b/TECH_SPEC.md @@ -619,18 +619,17 @@ User-facing errors are formatted via `util::format(Error)` and printed to stderr ## 15. Bootstrap and self-hosting -Three phases. +**Phase 0 (historical) — hand-written `CMakeLists.txt` and `flake.nix` at the repo root.** -**Phase 0 — hand-written CMake (commits before milestone M3).** -`CMakeLists.txt` and `flake.nix` at the repo root are written by humans. `cargoxx` builds `cargoxx`. +**Phase 2 (current, since M6) — fully self-hosted.** +`Cargoxx.toml` describes cargoxx's own deps with nixpkgs names: `sqlite`, `reproc`, `catch2_3`. `cargoxx build` runs the auto-resolver chain (nixpkgs probe → realize → nix_cmake_scan → pc_scan), confirms each recipe via verify_link, and generates `build/CMakeLists.txt` and the root `flake.nix`. Both files are committed (tracked) so the build is reproducible without first building cargoxx, and `[build].include_dirs = ["third_party"]` keeps the vendored headers on the include path. -**Phase 1 — generated CMake, hand-written flake.** -At milestone M3 (codegen complete), commit a populated `Cargoxx.toml`. Generate `build/CMakeLists.txt` from it. Delete the root `CMakeLists.txt`. The root `flake.nix` stays hand-written because cargoxx doesn't know about its own host-language deps yet. +Bootstrap path: +``` +pre-built cargoxx → cargoxx build → next cargoxx +``` -**Phase 2 — fully self-hosted.** -At milestone M5, vendor all third-party headers into `third_party/` and have cargoxx generate the flake too. The bootstrap path becomes: pre-built cargoxx binary → run `cargoxx build` → produce next cargoxx. - -For continuity, ship a known-good cargoxx binary as a release artifact. Anyone bootstrapping from source clones the repo, downloads the latest release binary, and runs `./bootstrap-cargoxx build`. If we ever want to bootstrap from absolute zero, `scripts/bootstrap-build.sh` does it with a hand-written CMake invocation. +A clean clone with an empty `~/.cache/cargoxx/linkdb.sqlite` auto-resolves all three deps on first `cargoxx build` (sqlite goes through pkg-config because nixpkgs ships no SQLite3Config.cmake; reproc/catch2_3 go through nix_cmake_scan). For continuity, a known-good cargoxx binary is shipped as a release artifact; from-scratch bootstrap is not in v0.1 scope. --- diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt new file mode 100644 index 0000000..66b9160 --- /dev/null +++ b/build/CMakeLists.txt @@ -0,0 +1,351 @@ +cmake_minimum_required(VERSION 3.30) + +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) +set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444") +set(CMAKE_CXX_MODULE_STD ON) + +project(cargoxx LANGUAGES CXX) + +# Generated by cargoxx — do not edit. +# Source of truth: ../Cargoxx.toml + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS ON) +set(CMAKE_CXX_SCAN_FOR_MODULES ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +add_compile_options(-Wall -Wextra -Wpedantic -Wconversion) + +# ----- dependencies ----- +find_package(reproc CONFIG REQUIRED) +find_package(PkgConfig REQUIRED) +pkg_check_modules(SQLITE3 REQUIRED IMPORTED_TARGET sqlite3) +find_package(Catch2 CONFIG REQUIRED) + +# ----- library target ----- +add_library(cargoxx STATIC) +target_sources(cargoxx + PUBLIC + FILE_SET CXX_MODULES BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/.. FILES + ../src/cli/cli.cppm + ../src/codegen/codegen.cppm + ../src/exec/exec.cppm + ../src/layout/layout.cppm + ../src/lib.cppm + ../src/linkdb/linkdb.cppm + ../src/lockfile/lockfile.cppm + ../src/manifest/manifest.cppm + ../src/resolver/resolver.cppm + ../src/util/util.cppm + PRIVATE + ../src/cli/cmd_add.cpp + ../src/cli/cmd_build.cpp + ../src/cli/cmd_clean.cpp + ../src/cli/cmd_linkdb_add.cpp + ../src/cli/cmd_new.cpp + ../src/cli/cmd_remove.cpp + ../src/cli/cmd_run.cpp + ../src/cli/cmd_test.cpp + ../src/cli/run.cpp + ../src/codegen/cmake.cpp + ../src/codegen/flake.cpp + ../src/exec/subprocess.cpp + ../src/layout/layout.cpp + ../src/linkdb/database.cpp + ../src/linkdb/overlay.cpp + ../src/lockfile/lockfile.cpp + ../src/manifest/parser.cpp + ../src/manifest/writer.cpp + ../src/resolver/conan_probe.cpp + ../src/resolver/discover.cpp + ../src/resolver/nix_cmake_scan.cpp + ../src/resolver/nixpkgs_git.cpp + ../src/resolver/nixpkgs_probe.cpp + ../src/resolver/pc_scan.cpp + ../src/resolver/search_devbox.cpp + ../src/resolver/vcpkg_probe.cpp + ../src/resolver/verify_link.cpp + ../src/resolver/version_resolve.cpp + ../src/util/error.cpp + ../src/util/semver.cpp +) +target_include_directories(cargoxx SYSTEM PRIVATE ../third_party) +target_link_libraries(cargoxx PUBLIC + reproc + PkgConfig::SQLITE3 +) + +# ----- binary target ----- +add_executable(cargoxx_bin ../src/main.cpp) +set_target_properties(cargoxx_bin PROPERTIES OUTPUT_NAME cargoxx) +target_link_libraries(cargoxx_bin PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 +) + +# ----- tests ----- +enable_testing() +include(Catch) +add_executable(test_cmd_add ../tests/cmd_add.cpp) +target_link_libraries(test_cmd_add PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_cmd_add) +add_executable(test_cmd_build ../tests/cmd_build.cpp) +target_link_libraries(test_cmd_build PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_cmd_build) +add_executable(test_cmd_clean ../tests/cmd_clean.cpp) +target_link_libraries(test_cmd_clean PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_cmd_clean) +add_executable(test_cmd_new ../tests/cmd_new.cpp) +target_link_libraries(test_cmd_new PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_cmd_new) +add_executable(test_cmd_remove ../tests/cmd_remove.cpp) +target_link_libraries(test_cmd_remove PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_cmd_remove) +add_executable(test_cmd_run ../tests/cmd_run.cpp) +target_link_libraries(test_cmd_run PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_cmd_run) +add_executable(test_codegen_cmake ../tests/codegen_cmake.cpp) +target_link_libraries(test_codegen_cmake PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_codegen_cmake) +add_executable(test_codegen_flake ../tests/codegen_flake.cpp) +target_link_libraries(test_codegen_flake PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_codegen_flake) +add_executable(test_conan_probe_live ../tests/conan_probe_live.cpp) +target_link_libraries(test_conan_probe_live PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_conan_probe_live) +add_executable(test_conan_probe_parse ../tests/conan_probe_parse.cpp) +target_link_libraries(test_conan_probe_parse PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_conan_probe_parse) +add_executable(test_devbox_resolve_live ../tests/devbox_resolve_live.cpp) +target_link_libraries(test_devbox_resolve_live PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_devbox_resolve_live) +add_executable(test_devbox_resolve_parse ../tests/devbox_resolve_parse.cpp) +target_link_libraries(test_devbox_resolve_parse PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_devbox_resolve_parse) +add_executable(test_exec_run ../tests/exec_run.cpp) +target_link_libraries(test_exec_run PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_exec_run) +add_executable(test_layout_discovery ../tests/layout_discovery.cpp) +target_link_libraries(test_layout_discovery PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_layout_discovery) +add_executable(test_linkdb_lookup ../tests/linkdb_lookup.cpp) +target_link_libraries(test_linkdb_lookup PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_linkdb_lookup) +add_executable(test_linkdb_overlay ../tests/linkdb_overlay.cpp) +target_link_libraries(test_linkdb_overlay PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_linkdb_overlay) +add_executable(test_lockfile_round_trip ../tests/lockfile_round_trip.cpp) +target_link_libraries(test_lockfile_round_trip PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_lockfile_round_trip) +add_executable(test_manifest_parse ../tests/manifest_parse.cpp) +target_link_libraries(test_manifest_parse PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_manifest_parse) +add_executable(test_manifest_write ../tests/manifest_write.cpp) +target_link_libraries(test_manifest_write PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_manifest_write) +add_executable(test_nix_cmake_scan_live ../tests/nix_cmake_scan_live.cpp) +target_link_libraries(test_nix_cmake_scan_live PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_nix_cmake_scan_live) +add_executable(test_nix_cmake_scan_parse ../tests/nix_cmake_scan_parse.cpp) +target_link_libraries(test_nix_cmake_scan_parse PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_nix_cmake_scan_parse) +add_executable(test_nixpkgs_git_resolve ../tests/nixpkgs_git_resolve.cpp) +target_link_libraries(test_nixpkgs_git_resolve PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_nixpkgs_git_resolve) +add_executable(test_nixpkgs_probe_live ../tests/nixpkgs_probe_live.cpp) +target_link_libraries(test_nixpkgs_probe_live PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_nixpkgs_probe_live) +add_executable(test_nixpkgs_probe_parse ../tests/nixpkgs_probe_parse.cpp) +target_link_libraries(test_nixpkgs_probe_parse PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_nixpkgs_probe_parse) +add_executable(test_semver_satisfies ../tests/semver_satisfies.cpp) +target_link_libraries(test_semver_satisfies PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_semver_satisfies) +add_executable(test_util_error ../tests/util_error.cpp) +target_link_libraries(test_util_error PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_util_error) +add_executable(test_vcpkg_probe_live ../tests/vcpkg_probe_live.cpp) +target_link_libraries(test_vcpkg_probe_live PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_vcpkg_probe_live) +add_executable(test_vcpkg_probe_parse ../tests/vcpkg_probe_parse.cpp) +target_link_libraries(test_vcpkg_probe_parse PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_vcpkg_probe_parse) +add_executable(test_verify_link_unit ../tests/verify_link_unit.cpp) +target_link_libraries(test_verify_link_unit PRIVATE + cargoxx + reproc + PkgConfig::SQLITE3 + Catch2::Catch2 + Catch2::Catch2WithMain +) +catch_discover_tests(test_verify_link_unit) diff --git a/flake.nix b/flake.nix index 5bdc15d..8574da1 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "cargoxx — Cargo-style frontend for modern C++"; + description = "cargoxx"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; @@ -11,21 +11,22 @@ let pkgs = import nixpkgs { inherit system; }; in { - devShells.default = pkgs.gcc15Stdenv.mkDerivation { - name = "cargoxx-dev"; - version = "0.1.0"; + devShell = pkgs.gcc15Stdenv.mkDerivation { + name = "shell"; + version = "1.0"; nativeBuildInputs = [ - pkgs.cmake pkgs.ninja - pkgs.git + pkgs.cmake pkgs.pkg-config ]; buildInputs = [ - pkgs.sqlite pkgs.reproc + pkgs.sqlite pkgs.catch2_3 ]; - hardeningDisable = [ "all" ]; + hardeningDisable = [ + "all" + ]; }; }); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index c27ada8..0000000 --- a/tests/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -find_package(Catch2 3 REQUIRED CONFIG) -include(Catch) - -function(cargoxx_add_test name) - add_executable(${name} ${name}.cpp) - target_link_libraries(${name} PRIVATE cargoxx Catch2::Catch2WithMain) - catch_discover_tests(${name}) -endfunction() - -cargoxx_add_test(util_error) -cargoxx_add_test(semver_satisfies) -cargoxx_add_test(exec_run) -cargoxx_add_test(manifest_parse) -cargoxx_add_test(manifest_write) -cargoxx_add_test(layout_discovery) -cargoxx_add_test(lockfile_round_trip) -cargoxx_add_test(linkdb_lookup) -cargoxx_add_test(linkdb_overlay) -cargoxx_add_test(codegen_flake) -cargoxx_add_test(codegen_cmake) -cargoxx_add_test(cmd_new) -cargoxx_add_test(cmd_build) -cargoxx_add_test(cmd_run) -cargoxx_add_test(cmd_clean) -cargoxx_add_test(cmd_add) -cargoxx_add_test(cmd_remove) -cargoxx_add_test(nixpkgs_probe_parse) -cargoxx_add_test(nixpkgs_probe_live) -cargoxx_add_test(nix_cmake_scan_parse) -cargoxx_add_test(nix_cmake_scan_live) -cargoxx_add_test(conan_probe_parse) -cargoxx_add_test(conan_probe_live) -cargoxx_add_test(vcpkg_probe_parse) -cargoxx_add_test(vcpkg_probe_live) -cargoxx_add_test(verify_link_unit) -cargoxx_add_test(devbox_resolve_parse) -cargoxx_add_test(devbox_resolve_live) -cargoxx_add_test(nixpkgs_git_resolve)