From 5db915e5764454028340bda1e265731f7b2d1c81 Mon Sep 17 00:00:00 2001 From: Amadey Vorontsov Date: Wed, 13 May 2026 23:09:28 +0000 Subject: [PATCH] [M5+] swap toolchain to gcc15Stdenv + libstdc++ --- flake.nix | 17 ++------- src/codegen/flake.cpp | 39 +++------------------ src/linkdb/curated.cpp | 1 - src/linkdb/linkdb.cppm | 19 ---------- src/linkdb/overlay.cpp | 68 +++--------------------------------- src/resolver/verify_link.cpp | 57 ++---------------------------- 6 files changed, 14 insertions(+), 187 deletions(-) diff --git a/flake.nix b/flake.nix index 590c4db..5bdc15d 100644 --- a/flake.nix +++ b/flake.nix @@ -10,33 +10,20 @@ flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; }; - llvmPkgs = pkgs.llvmPackages; - libcxxPkgs = { - catch2_3 = pkgs.catch2_3.override { - stdenv = llvmPkgs.libcxxStdenv; - }; - }; in { - devShells.default = llvmPkgs.libcxxStdenv.mkDerivation { + devShells.default = pkgs.gcc15Stdenv.mkDerivation { name = "cargoxx-dev"; version = "0.1.0"; nativeBuildInputs = [ pkgs.cmake pkgs.ninja - pkgs.clang-tools pkgs.git pkgs.pkg-config ]; buildInputs = [ pkgs.sqlite pkgs.reproc - libcxxPkgs.catch2_3 - ]; - 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" + pkgs.catch2_3 ]; hardeningDisable = [ "all" ]; }; diff --git a/src/codegen/flake.cpp b/src/codegen/flake.cpp index f0de7e5..6e00772 100644 --- a/src/codegen/flake.cpp +++ b/src/codegen/flake.cpp @@ -18,7 +18,6 @@ struct DepBinding { std::string sanitized; // "nixpkgs_fmt_10_2_1" — input attr, // let-binding stem, lambda param std::optional rev; // pinned commit (null → unpinned) - bool libcxx_override = false; // wrap in .override { stdenv = libcxx } }; // Replaces every char outside [a-zA-Z0-9_] with '_'. The result is safe @@ -77,7 +76,6 @@ auto build_bindings(const GenerateInputs& in) -> std::vector { .nixpkgs_attr = std::move(attr), .sanitized = sanitize_input_attr(dep.name, dep.version_spec), .rev = std::move(ref.rev), - .libcxx_override = rec.requires_libcxx_override, }; out.push_back(std::move(b)); } @@ -146,32 +144,13 @@ auto base_expr(const DepBinding& b) -> std::string { : std::format("pkgs.{}", b.nixpkgs_attr); } -auto emit_build_input_line(const DepBinding& b) -> std::string { - auto expr = base_expr(b); - if (b.libcxx_override) { - // Stdenv swap (libc++) plus a `doCheck = false` overrideAttrs - // to skip the package's test suite — without it, the rebuilt - // dep would re-run its full check phase under the new stdenv, - // adding minutes to hours of evaluation/build time. - expr = std::format( - "(({}.override {{ stdenv = llvmPkgs.libcxxStdenv; }})" - ".overrideAttrs (old: {{ doCheck = false; }}))", - expr); - } - return std::format(" {}\n", expr); -} - auto emit_build_inputs(const std::vector& bindings) -> std::string { std::set seen; std::string out; for (const auto& b : bindings) { - // Dedupe by base expression — two deps that resolve to the same - // (set, attr) but differ only in override are considered distinct - // because the dedup key includes the override flag. - auto key = std::format("{}{}", base_expr(b), - b.libcxx_override ? "@libcxx" : ""); - if (seen.insert(key).second) { - out += emit_build_input_line(b); + auto expr = base_expr(b); + if (seen.insert(expr).second) { + out += std::format(" {}\n", expr); } } return out; @@ -197,25 +176,17 @@ auto flake_nix(const GenerateInputs& in) -> std::string { " let\n" " pkgs = import nixpkgs { inherit system; };\n"; out += emit_let_bindings(pinned); - out += " llvmPkgs = pkgs.llvmPackages;\n" - " in {\n" - " devShell = llvmPkgs.libcxxStdenv.mkDerivation {\n" + out += " in {\n" + " devShell = pkgs.gcc15Stdenv.mkDerivation {\n" " name = \"shell\";\n" " version = \"1.0\";\n" " nativeBuildInputs = [\n" " pkgs.ninja\n" " pkgs.cmake\n" - " pkgs.clang-tools\n" " ];\n" " buildInputs = [\n"; out += emit_build_inputs(bindings); out += " ];\n" - " env.NIX_CFLAGS_COMPILE = toString [\n" - " \"-stdlib=libc++\"\n" - " \"-Wno-unused-command-line-argument\"\n" - " \"-B${pkgs.lib.getLib pkgs.libcxx}/lib\"\n" - " \"-isystem ${pkgs.lib.getDev pkgs.libcxx}/include/c++/v1\"\n" - " ];\n" " hardeningDisable = [\n" " \"all\"\n" " ];\n" diff --git a/src/linkdb/curated.cpp b/src/linkdb/curated.cpp index adfb74b..0d3799b 100644 --- a/src/linkdb/curated.cpp +++ b/src/linkdb/curated.cpp @@ -185,7 +185,6 @@ auto Database::resolve(const std::string& package, const std::string& version, .find_package = row.find_package, .targets = row.targets, .source = row.source, - .requires_libcxx_override = row.requires_libcxx_override, }; } } diff --git a/src/linkdb/linkdb.cppm b/src/linkdb/linkdb.cppm index 2e6687c..67a8e30 100644 --- a/src/linkdb/linkdb.cppm +++ b/src/linkdb/linkdb.cppm @@ -14,12 +14,6 @@ struct Recipe { std::string find_package; // post-substitution std::vector targets; // post-substitution std::string source; // 'curated' | 'manual' | etc. - // Set when the dep needs `.override { stdenv = llvmPkgs.libcxxStdenv; }` - // to link against the user's libc++-built project (e.g. nixpkgs ships - // it built against libstdc++ — abseil-cpp, llvm). verify_link toggles - // this on after a libstdc++/libc++ link failure. Codegen wraps the - // buildInputs entry in the override expression when true. - bool requires_libcxx_override = false; bool operator==(const Recipe&) const = default; }; @@ -43,7 +37,6 @@ struct OverlayRow { std::vector targets; std::string source; std::int64_t verified_at = 0; - bool requires_libcxx_override = false; }; // RAII wrapper for an open sqlite3 connection used by the overlay database. @@ -97,13 +90,6 @@ auto overlay_delete_recipe(OverlayState& state, const std::string& package, const std::string& source) -> cargoxx::util::Result; -// Toggle the libcxx-override flag for an existing row. Used by -// `verify_link` after a libstdc++/libc++ link mismatch. -auto overlay_set_libcxx_override(OverlayState& state, const std::string& package, - const std::string& version_range, - const std::string& source, bool value) - -> cargoxx::util::Result; - // Drops every overlay row for `package` whose source is not "manual". // Used by `cargoxx add` to invalidate stale auto-discovered recipes // when the resolver/scanner logic has changed under the user — they @@ -146,11 +132,6 @@ class Database { const std::string& version_range, const std::string& source) -> util::Result; - auto set_libcxx_override(const std::string& package, - const std::string& version_range, - const std::string& source, bool value) - -> util::Result; - // Evict every non-manual overlay row for `package`. Called by // `cargoxx add` so users never have to manually drop stale // auto-discovered recipes when cargoxx's resolver/scanner logic diff --git a/src/linkdb/overlay.cpp b/src/linkdb/overlay.cpp index e50732b..36a5e3f 100644 --- a/src/linkdb/overlay.cpp +++ b/src/linkdb/overlay.cpp @@ -90,25 +90,6 @@ auto overlay_open(const std::filesystem::path& path) }); } - // Add the libcxx-override column to legacy overlay files. SQLite's - // ADD COLUMN errors when the column already exists; treat that as - // success (the column is present, which is all we need). - constexpr const char* MIGRATE = - "ALTER TABLE recipes ADD COLUMN requires_libcxx_override INTEGER NOT NULL DEFAULT 0"; - char* mig_err = nullptr; - if (sqlite3_exec(state->handle(), MIGRATE, nullptr, nullptr, &mig_err) != SQLITE_OK) { - if (mig_err && std::string_view{mig_err}.find("duplicate column") == - std::string_view::npos) { - std::string msg = std::format("cannot migrate overlay schema: {}", - mig_err ? mig_err : "?"); - sqlite3_free(mig_err); - return std::unexpected(util::Error{ - util::ErrorCode::LinkdbCorrupt, std::move(msg), "", path, std::nullopt, - }); - } - sqlite3_free(mig_err); - } - return state; } @@ -118,8 +99,8 @@ auto overlay_insert_manual(OverlayState& state, const std::string& package, constexpr const char* SQL = "INSERT OR REPLACE INTO recipes " "(package, version_range, nixpkgs_attr, find_package, targets, components, source, " - " verified_at, requires_libcxx_override) " - "VALUES (?, ?, ?, ?, ?, NULL, 'manual', ?, ?)"; + " verified_at) " + "VALUES (?, ?, ?, ?, ?, NULL, 'manual', ?)"; sqlite3* db = state.handle(); sqlite3_stmt* stmt = nullptr; @@ -138,7 +119,6 @@ auto overlay_insert_manual(OverlayState& state, const std::string& package, sqlite3_bind_text(stmt, 4, r.find_package.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 5, targets_str.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_int64(stmt, 6, now); - sqlite3_bind_int(stmt, 7, r.requires_libcxx_override ? 1 : 0); auto rc = sqlite3_step(stmt); sqlite3_finalize(stmt); @@ -157,8 +137,8 @@ auto overlay_insert(OverlayState& state, const std::string& package, constexpr const char* SQL = "INSERT OR REPLACE INTO recipes " "(package, version_range, nixpkgs_attr, find_package, targets, components, source, " - " verified_at, requires_libcxx_override) " - "VALUES (?, ?, ?, ?, ?, NULL, ?, ?, ?)"; + " verified_at) " + "VALUES (?, ?, ?, ?, ?, NULL, ?, ?)"; sqlite3* db = state.handle(); sqlite3_stmt* stmt = nullptr; @@ -175,7 +155,6 @@ auto overlay_insert(OverlayState& state, const std::string& package, sqlite3_bind_text(stmt, 5, targets_str.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 6, source.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_int64(stmt, 7, verified_at); - sqlite3_bind_int(stmt, 8, r.requires_libcxx_override ? 1 : 0); auto rc = sqlite3_step(stmt); sqlite3_finalize(stmt); @@ -266,8 +245,7 @@ auto overlay_evict_auto(OverlayState& state, const std::string& package) auto overlay_query(OverlayState& state, const std::string& package) -> util::Result> { constexpr const char* SQL = - "SELECT version_range, nixpkgs_attr, find_package, targets, source, verified_at, " - " requires_libcxx_override " + "SELECT version_range, nixpkgs_attr, find_package, targets, source, verified_at " "FROM recipes WHERE package = ?"; sqlite3* db = state.handle(); @@ -298,7 +276,6 @@ auto overlay_query(OverlayState& state, const std::string& package) } row.source = column_text(stmt, 4); row.verified_at = sqlite3_column_int64(stmt, 5); - row.requires_libcxx_override = sqlite3_column_int(stmt, 6) != 0; out.push_back(std::move(row)); } sqlite3_finalize(stmt); @@ -323,30 +300,6 @@ auto overlay_is_fresh(const OverlayRow& row, std::int64_t now) -> bool { return (now - row.verified_at) < THIRTY_DAYS_SECONDS; } -auto overlay_set_libcxx_override(OverlayState& state, const std::string& package, - const std::string& version_range, - const std::string& source, bool value) - -> util::Result { - constexpr const char* SQL = - "UPDATE recipes SET requires_libcxx_override = ? " - "WHERE package = ? AND version_range = ? AND source = ?"; - sqlite3* db = state.handle(); - sqlite3_stmt* stmt = nullptr; - if (sqlite3_prepare_v2(db, SQL, -1, &stmt, nullptr) != SQLITE_OK) { - return std::unexpected(sqlite_error(db, "prepare update libcxx_override")); - } - sqlite3_bind_int(stmt, 1, value ? 1 : 0); - sqlite3_bind_text(stmt, 2, package.c_str(), -1, SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 3, version_range.c_str(), -1, SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 4, source.c_str(), -1, SQLITE_TRANSIENT); - auto rc = sqlite3_step(stmt); - sqlite3_finalize(stmt); - if (rc != SQLITE_DONE) { - return std::unexpected(sqlite_error(db, "step update libcxx_override")); - } - return {}; -} - } // namespace detail auto Database::add_manual(const std::string& package, const std::string& version_range, @@ -410,15 +363,4 @@ auto Database::evict_auto_recipes(const std::string& package) return detail::overlay_evict_auto(*overlay_, package); } -auto Database::set_libcxx_override(const std::string& package, - const std::string& version_range, - const std::string& source, bool value) - -> util::Result { - if (auto ok = require_overlay(overlay_); !ok) { - return std::unexpected(ok.error()); - } - return detail::overlay_set_libcxx_override(*overlay_, package, version_range, - source, value); -} - } // namespace cargoxx::linkdb diff --git a/src/resolver/verify_link.cpp b/src/resolver/verify_link.cpp index 8f12841..a12991e 100644 --- a/src/resolver/verify_link.cpp +++ b/src/resolver/verify_link.cpp @@ -141,64 +141,11 @@ auto verify_link(const VerifyLinkRequest& req, const BuildFn& build_fn) } auto build_result = build_fn(proj_root); - - // First-pass success — confirm and we're done. - if (build_result) { - auto db = linkdb::Database::open(req.overlay_path); - if (!db) { - return std::unexpected(db.error()); - } - if (auto r = db->confirm_provisional(req.package_name, req.version_spec, - req.source); - !r) { - return std::unexpected(r.error()); - } - return {}; - } - - // First pass failed. Try once more with a libstdc++/libc++ ABI - // workaround: flip `requires_libcxx_override` on the overlay row, - // which makes flake codegen wrap the dep in - // `.override { stdenv = llvmPkgs.libcxxStdenv; }`. cmd_build - // re-resolves and regenerates the flake, so the second build_fn - // call sees the override. - std::cerr << std::format( - "verify_link: first build of '{}' failed; retrying with " - "libc++/doCheck=false override\n", - req.package_name); - { - auto db = linkdb::Database::open(req.overlay_path); - if (!db) { - return std::unexpected(db.error()); - } - if (auto r = db->set_libcxx_override(req.package_name, req.version_spec, - req.source, true); - !r) { - // Fall through to the abort path below — couldn't even mark. - (void)db->abort_provisional(req.package_name, req.version_spec, - req.source); - return std::unexpected(r.error()); - } - } - - // Wipe CMake's per-profile build dirs. The first pass's - // CMakeCache.txt has cached `LLVM_DIR` (and other find_package - // results) at the default-stdenv path; without clearing it, the - // retry's regenerated flake.nix has no effect on what - // find_package sees. - { - std::error_code wipe_ec; - for (auto* sub : {"debug", "release"}) { - std::filesystem::remove_all(proj_root / "build" / sub, wipe_ec); - } - } - - auto retry_result = build_fn(proj_root); auto db = linkdb::Database::open(req.overlay_path); if (!db) { return std::unexpected(db.error()); } - if (retry_result) { + if (build_result) { if (auto r = db->confirm_provisional(req.package_name, req.version_spec, req.source); !r) { @@ -207,7 +154,7 @@ auto verify_link(const VerifyLinkRequest& req, const BuildFn& build_fn) return {}; } (void)db->abort_provisional(req.package_name, req.version_spec, req.source); - return std::unexpected(retry_result.error()); + return std::unexpected(build_result.error()); } } // namespace cargoxx::resolver