diff --git a/CMakeLists.txt b/CMakeLists.txt index 28e27a4..481ddc0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,7 @@ target_sources(cargoxx 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 diff --git a/Cargoxx.toml b/Cargoxx.toml index b658418..002ca15 100644 --- a/Cargoxx.toml +++ b/Cargoxx.toml @@ -9,7 +9,8 @@ sqlite3 = "*" reproc = "*" [dev-dependencies] -catch2 = "*" +catch2_3 = "*" [build] warnings_as_errors = false +include_dirs = ["third_party"] diff --git a/src/cli/cli.cppm b/src/cli/cli.cppm index d32b390..410e1b4 100644 --- a/src/cli/cli.cppm +++ b/src/cli/cli.cppm @@ -53,6 +53,17 @@ auto cmd_add(const std::filesystem::path& project_root, const std::string& name, auto cmd_remove(const std::filesystem::path& project_root, const std::string& name) -> util::Result; +// Inserts a manual recipe into the SQLite linkdb overlay. Equivalent to +// the auto-discover path's confirmed row, but user-provided. Use it +// when nixpkgs ships a CMake FindModule (no Config.cmake) or when the +// scanner can't pick the right target automatically. +auto cmd_linkdb_add(const std::string& package, const std::string& version_range, + const std::string& find_package_text, + std::vector targets, + const std::string& nixpkgs_attr, + std::optional overlay_path = std::nullopt) + -> util::Result; + auto run(int argc, char** argv) -> int; } // namespace cargoxx::cli diff --git a/src/cli/cmd_linkdb_add.cpp b/src/cli/cmd_linkdb_add.cpp new file mode 100644 index 0000000..63aac0f --- /dev/null +++ b/src/cli/cmd_linkdb_add.cpp @@ -0,0 +1,31 @@ +module cargoxx.cli; + +import std; +import cargoxx.linkdb; +import cargoxx.util; + +namespace cargoxx::cli { + +auto cmd_linkdb_add(const std::string& package, const std::string& version_range, + const std::string& find_package_text, + std::vector targets, + const std::string& nixpkgs_attr, + std::optional overlay_path) + -> util::Result { + auto db = linkdb::Database::open(std::move(overlay_path)); + if (!db) { + return std::unexpected(db.error()); + } + if (auto r = db->evict_auto_recipes(package); !r) { + return std::unexpected(r.error()); + } + linkdb::Recipe r{ + .nixpkgs_attr = nixpkgs_attr, + .find_package = find_package_text, + .targets = std::move(targets), + .source = "manual", + }; + return db->add_manual(package, version_range, r); +} + +} // namespace cargoxx::cli diff --git a/src/cli/run.cpp b/src/cli/run.cpp index ba87274..1ed5903 100644 --- a/src/cli/run.cpp +++ b/src/cli/run.cpp @@ -58,6 +58,28 @@ auto run(int argc, char** argv) -> int { std::string remove_name; remove_cmd->add_option("name", remove_name, "Package name to remove")->required(); + auto* linkdb_cmd = + app.add_subcommand("linkdb", "Manage the link database"); + linkdb_cmd->require_subcommand(1); + auto* linkdb_add_cmd = + linkdb_cmd->add_subcommand("add", "Insert a manual recipe"); + std::string ldb_package; + std::string ldb_version = "*"; + std::string ldb_find_package; + std::string ldb_targets; + std::string ldb_nixpkgs_attr; + linkdb_add_cmd->add_option("package", ldb_package, "Package name")->required(); + linkdb_add_cmd->add_option("--version", ldb_version, "Version range (default: *)"); + linkdb_add_cmd->add_option("--find-package", ldb_find_package, + "Body of the find_package(...) call (e.g. \"fmt CONFIG REQUIRED\")") + ->required(); + linkdb_add_cmd->add_option("--targets", ldb_targets, + "Comma-separated CMake targets (e.g. fmt::fmt)") + ->required(); + linkdb_add_cmd->add_option("--nixpkgs-attr", ldb_nixpkgs_attr, + "nixpkgs attribute name (e.g. fmt_10)") + ->required(); + try { app.parse(argc, argv); } catch (const CLI::ParseError& e) { @@ -175,6 +197,31 @@ auto run(int argc, char** argv) -> int { return 0; } + if (*linkdb_add_cmd) { + std::vector targets; + std::size_t pos = 0; + while (pos <= ldb_targets.size()) { + auto comma = ldb_targets.find(',', pos); + auto piece = ldb_targets.substr( + pos, comma == std::string::npos ? ldb_targets.size() - pos : comma - pos); + if (!piece.empty()) { + targets.push_back(std::move(piece)); + } + if (comma == std::string::npos) { + break; + } + pos = comma + 1; + } + auto r = cmd_linkdb_add(ldb_package, ldb_version, ldb_find_package, + std::move(targets), ldb_nixpkgs_attr); + if (!r) { + std::cerr << util::format(r.error()); + return 1; + } + std::cout << std::format(" Added manual recipe for {}\n", ldb_package); + return 0; + } + return 0; } diff --git a/src/codegen/cmake.cpp b/src/codegen/cmake.cpp index 1712c9f..b5ff00a 100644 --- a/src/codegen/cmake.cpp +++ b/src/codegen/cmake.cpp @@ -111,7 +111,7 @@ auto emit_library(const layout::Target& lib, const std::string& package_name, out += std::format("add_library({} STATIC)\n", package_name); out += std::format("target_sources({}\n", package_name); out += " PUBLIC\n"; - out += " FILE_SET CXX_MODULES FILES\n"; + out += " FILE_SET CXX_MODULES BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/.. FILES\n"; for (const auto& m : lib.module_units) { out += std::format(" {}\n", rel_to_build(m, project_root)); } diff --git a/src/codegen/flake.cpp b/src/codegen/flake.cpp index 6e00772..30d7e38 100644 --- a/src/codegen/flake.cpp +++ b/src/codegen/flake.cpp @@ -59,25 +59,24 @@ auto find_lockfile_ref(const lockfile::Lockfile& lock, const std::string& name, auto build_bindings(const GenerateInputs& in) -> std::vector { std::vector out; - out.reserve(in.manifest.dependencies.size()); - for (std::size_t i = 0; i < in.manifest.dependencies.size(); ++i) { - const auto& dep = in.manifest.dependencies[i]; - const auto& rec = in.recipes[i]; + out.reserve(in.manifest.dependencies.size() + in.manifest.dev_dependencies.size()); + auto push = [&](const manifest::Dependency& dep, const linkdb::Recipe& rec) { auto ref = find_lockfile_ref(in.lock, dep.name, dep.version_spec); - // For pinned deps the lockfile's nixpkgs_attr is authoritative - // (it came from devbox's attr_paths for this specific rev). The - // curated recipe's attr only applies to nixos-unstable, so it's - // wrong when the dep pulls from a different rev. std::string attr = (ref.attr && !ref.attr->empty()) ? *ref.attr : rec.nixpkgs_attr; - DepBinding b{ + out.push_back(DepBinding{ .name = dep.name, .version = dep.version_spec, .nixpkgs_attr = std::move(attr), .sanitized = sanitize_input_attr(dep.name, dep.version_spec), .rev = std::move(ref.rev), - }; - out.push_back(std::move(b)); + }); + }; + for (std::size_t i = 0; i < in.manifest.dependencies.size(); ++i) { + push(in.manifest.dependencies[i], in.recipes[i]); + } + for (std::size_t i = 0; i < in.manifest.dev_dependencies.size(); ++i) { + push(in.manifest.dev_dependencies[i], in.dev_recipes[i]); } return out; } diff --git a/tests/codegen_cmake.cpp b/tests/codegen_cmake.cpp index 73c3f73..8cf6ff6 100644 --- a/tests/codegen_cmake.cpp +++ b/tests/codegen_cmake.cpp @@ -95,7 +95,7 @@ TEST_CASE("cmake_lists for a library-only project", "[codegen][cmake]") { auto out = cmake_lists(in); REQUIRE(out.find("add_library(widget STATIC)") != std::string::npos); - REQUIRE(out.find("FILE_SET CXX_MODULES FILES") != std::string::npos); + REQUIRE(out.find("FILE_SET CXX_MODULES") != std::string::npos); REQUIRE(out.find("../src/lib.cppm") != std::string::npos); REQUIRE(out.find("add_executable") == std::string::npos); }