From f62cff49c609387e88e29639dad9f8f2379d2bca Mon Sep 17 00:00:00 2001 From: Amadey Vorontsov Date: Fri, 15 May 2026 23:05:40 +0000 Subject: [PATCH] [M7] lockfile carries full recipe (find_package, targets, pkg_config_module, brute_force_*) --- src/cli/cmd_build.cpp | 46 ++++++++++++++++++++++++----- src/lockfile/lockfile.cpp | 54 +++++++++++++++++++++++++++++++++++ src/lockfile/lockfile.cppm | 7 ++++- tests/lockfile_round_trip.cpp | 48 +++++++++++++++++++++++++++++++ 4 files changed, 147 insertions(+), 8 deletions(-) diff --git a/src/cli/cmd_build.cpp b/src/cli/cmd_build.cpp index acede92..c5e4d0a 100644 --- a/src/cli/cmd_build.cpp +++ b/src/cli/cmd_build.cpp @@ -92,6 +92,11 @@ auto merge_lockfile(const manifest::Manifest& m, .nixpkgs_attr = std::move(attr), .nixpkgs_rev = std::move(rev), .linkdb_source = rec.source, + .find_package = rec.find_package, + .targets = rec.targets, + .pkg_config_module = rec.pkg_config_module, + .brute_force_libs = rec.brute_force_libs, + .brute_force_includes = rec.brute_force_includes, }); }; for (std::size_t i = 0; i < m.dependencies.size(); ++i) { @@ -176,11 +181,45 @@ auto cmd_build(const fs::path& project_root, bool no_build, bool release, return {}; }; + lockfile::Lockfile prior; + if (std::error_code ec; std::filesystem::exists(project_root / "Cargoxx.lock", ec)) { + if (auto r = lockfile::parse(project_root / "Cargoxx.lock"); r) { + prior = std::move(*r); + } + } + auto recipe_from_lock = [&](const std::string& name, const std::string& version) + -> std::optional { + for (const auto& p : prior.packages) { + if (p.name != name || p.version != version) { + continue; + } + if (!p.find_package || p.targets.empty()) { + if (p.brute_force_libs.empty() && p.brute_force_includes.empty()) { + return std::nullopt; + } + } + return linkdb::Recipe{ + .nixpkgs_attr = p.nixpkgs_attr.value_or(""), + .find_package = p.find_package.value_or(""), + .targets = p.targets, + .source = p.linkdb_source.value_or(""), + .pkg_config_module = p.pkg_config_module, + .brute_force_libs = p.brute_force_libs, + .brute_force_includes = p.brute_force_includes, + }; + } + return std::nullopt; + }; + auto resolve_list = [&](const std::vector& deps) -> util::Result> { std::vector out; out.reserve(deps.size()); for (const auto& dep : deps) { + if (auto cached = recipe_from_lock(dep.name, dep.version_spec); cached) { + out.push_back(std::move(*cached)); + continue; + } auto r = db->resolve(dep.name, dep.version_spec, dep.components); if (!r && r.error().code == util::ErrorCode::LinkdbUnknownPackage) { if (auto resolved = auto_resolve(dep.name, dep.version_spec, @@ -205,13 +244,6 @@ auto cmd_build(const fs::path& project_root, bool no_build, bool release, if (!dev_recipes) { return std::unexpected(dev_recipes.error()); } - - lockfile::Lockfile prior; - if (std::error_code ec; std::filesystem::exists(project_root / "Cargoxx.lock", ec)) { - if (auto r = lockfile::parse(project_root / "Cargoxx.lock"); r) { - prior = std::move(*r); - } - } auto lock = merge_lockfile(*m, *recipes, *dev_recipes, prior); codegen::GenerateInputs in{ diff --git a/src/lockfile/lockfile.cpp b/src/lockfile/lockfile.cpp index 7d68aba..889c67d 100644 --- a/src/lockfile/lockfile.cpp +++ b/src/lockfile/lockfile.cpp @@ -79,6 +79,33 @@ auto parse_package(const toml::table& tbl, const std::filesystem::path& path) if (auto v = tbl["linkdb_source"].value()) { pkg.linkdb_source = *v; } + if (auto v = tbl["find_package"].value()) { + pkg.find_package = *v; + } + if (const auto* arr = tbl["targets"].as_array()) { + auto r = extract_string_array(*arr, "targets", path); + if (!r) { + return std::unexpected(r.error()); + } + pkg.targets = std::move(*r); + } + if (auto v = tbl["pkg_config_module"].value()) { + pkg.pkg_config_module = *v; + } + if (const auto* arr = tbl["brute_force_libs"].as_array()) { + auto r = extract_string_array(*arr, "brute_force_libs", path); + if (!r) { + return std::unexpected(r.error()); + } + pkg.brute_force_libs = std::move(*r); + } + if (const auto* arr = tbl["brute_force_includes"].as_array()) { + auto r = extract_string_array(*arr, "brute_force_includes", path); + if (!r) { + return std::unexpected(r.error()); + } + pkg.brute_force_includes = std::move(*r); + } return pkg; } @@ -149,6 +176,33 @@ auto write(const Lockfile& lock, const std::filesystem::path& path) -> util::Res if (p.linkdb_source) { tbl.insert_or_assign("linkdb_source", *p.linkdb_source); } + if (p.find_package) { + tbl.insert_or_assign("find_package", *p.find_package); + } + if (!p.targets.empty()) { + toml::array arr; + for (const auto& t : p.targets) { + arr.push_back(t); + } + tbl.insert_or_assign("targets", std::move(arr)); + } + if (p.pkg_config_module) { + tbl.insert_or_assign("pkg_config_module", *p.pkg_config_module); + } + if (!p.brute_force_libs.empty()) { + toml::array arr; + for (const auto& l : p.brute_force_libs) { + arr.push_back(l); + } + tbl.insert_or_assign("brute_force_libs", std::move(arr)); + } + if (!p.brute_force_includes.empty()) { + toml::array arr; + for (const auto& i : p.brute_force_includes) { + arr.push_back(i); + } + tbl.insert_or_assign("brute_force_includes", std::move(arr)); + } packages.push_back(std::move(tbl)); } root.insert_or_assign("package", std::move(packages)); diff --git a/src/lockfile/lockfile.cppm b/src/lockfile/lockfile.cppm index 7345aee..863000c 100644 --- a/src/lockfile/lockfile.cppm +++ b/src/lockfile/lockfile.cppm @@ -9,10 +9,15 @@ export namespace cargoxx::lockfile { struct LockfilePackage { std::string name; std::string version; - std::vector dependencies; // " " entries; non-empty for the root + std::vector dependencies; std::optional nixpkgs_attr; std::optional nixpkgs_rev; std::optional linkdb_source; + std::optional find_package; + std::vector targets; + std::optional pkg_config_module; + std::vector brute_force_libs; + std::vector brute_force_includes; bool operator==(const LockfilePackage&) const = default; }; diff --git a/tests/lockfile_round_trip.cpp b/tests/lockfile_round_trip.cpp index aff64f8..1de3335 100644 --- a/tests/lockfile_round_trip.cpp +++ b/tests/lockfile_round_trip.cpp @@ -70,6 +70,54 @@ TEST_CASE("write round-trips a lockfile with deps", "[lockfile]") { REQUIRE(round_trip(l) == l); } +TEST_CASE("write round-trips lockfile recipe fields", "[lockfile]") { + Lockfile l{ + 1, + { + LockfilePackage{ + .name = "fmt", + .version = "10.2.1", + .dependencies = {}, + .nixpkgs_attr = "fmt_10", + .nixpkgs_rev = std::nullopt, + .linkdb_source = "conan", + .find_package = "fmt CONFIG REQUIRED", + .targets = {"fmt::fmt"}, + .pkg_config_module = std::nullopt, + .brute_force_libs = {}, + .brute_force_includes = {}, + }, + LockfilePackage{ + .name = "sqlite", + .version = "*", + .dependencies = {}, + .nixpkgs_attr = "sqlite", + .nixpkgs_rev = std::nullopt, + .linkdb_source = "pkg-config", + .find_package = "PkgConfig REQUIRED", + .targets = {"PkgConfig::SQLITE3"}, + .pkg_config_module = "sqlite3", + .brute_force_libs = {}, + .brute_force_includes = {}, + }, + LockfilePackage{ + .name = "obscure", + .version = "*", + .dependencies = {}, + .nixpkgs_attr = "obscure", + .nixpkgs_rev = std::nullopt, + .linkdb_source = "brute-force", + .find_package = "", + .targets = {"obscure::obscure"}, + .pkg_config_module = std::nullopt, + .brute_force_libs = {"/nix/store/abc/lib/libobscure.a"}, + .brute_force_includes = {"/nix/store/abc/include"}, + }, + }, + }; + REQUIRE(round_trip(l) == l); +} + TEST_CASE("Lockfile::nixpkgs_rev returns the shared rev", "[lockfile]") { Lockfile l{ 1,