[M5+] nix-cmake-scan walks the dev output for multi-output packages
This commit is contained in:
12
CHANGELOG.md
12
CHANGELOG.md
@@ -91,6 +91,18 @@ All notable changes to cargoxx will be documented in this file.
|
|||||||
6 cases against fixtures derived from a real fmt 10.2.1 response;
|
6 cases against fixtures derived from a real fmt 10.2.1 response;
|
||||||
`tests/devbox_resolve_live.cpp` (gated by `CARGOXX_NETWORK_TESTS=1`)
|
`tests/devbox_resolve_live.cpp` (gated by `CARGOXX_NETWORK_TESTS=1`)
|
||||||
hits the live API.
|
hits the live API.
|
||||||
|
- Fix: `nix_cmake_scan` walked only the package's default output. For
|
||||||
|
multi-output Nix packages — `boost`, `openssl`, `libllvm`,
|
||||||
|
`libclang`, glib, … — CMake configs live in the `dev` output
|
||||||
|
(`<store>/lib/cmake/llvm/LLVMConfig.cmake`), not under the default
|
||||||
|
output, so `cargoxx add libllvm` (or any other multi-output dep)
|
||||||
|
failed with "Conan/vcpkg/nix-scan all empty". `NixpkgsInfo` now
|
||||||
|
carries an optional `dev_path`; the `nix eval` apply expression
|
||||||
|
reports `if p ? dev then p.dev.outPath else ""`. `discover` scans
|
||||||
|
the dev output preferentially and falls back to `out`. Live
|
||||||
|
verified: `cargoxx add libllvm` now reaches the verify-link step
|
||||||
|
(the linker-time libstdc++/libc++ ABI mismatch is a separate
|
||||||
|
documented limit and applies to abseil-cpp too).
|
||||||
- Fix: pinned-dep `buildInputs` line used the curated linkdb's
|
- Fix: pinned-dep `buildInputs` line used the curated linkdb's
|
||||||
`nixpkgs_attr` (e.g. `fmt_10`) regardless of the pinned rev, so
|
`nixpkgs_attr` (e.g. `fmt_10`) regardless of the pinned rev, so
|
||||||
`cargoxx add fmt@12.1.0` emitted `pkgs_nixpkgs_fmt_12_1_0.fmt_10`
|
`cargoxx add fmt@12.1.0` emitted `pkgs_nixpkgs_fmt_12_1_0.fmt_10`
|
||||||
|
|||||||
@@ -89,9 +89,25 @@ auto discover(const std::string& name, const std::string& version_spec,
|
|||||||
if (auto v = vcpkg_probe(name); v) {
|
if (auto v = vcpkg_probe(name); v) {
|
||||||
candidates.push_back({"vcpkg", recipe_from_vcpkg(*v, name, "vcpkg")});
|
candidates.push_back({"vcpkg", recipe_from_vcpkg(*v, name, "vcpkg")});
|
||||||
}
|
}
|
||||||
if (auto n = nix_cmake_scan(info->out_path, name); n) {
|
// Multi-output nix packages keep CMake configs in the `dev` output;
|
||||||
|
// scan it first when available, fall back to the default `out`.
|
||||||
|
auto try_scan = [&](const fs::path& root)
|
||||||
|
-> std::optional<NixCmakeCandidate> {
|
||||||
|
if (root.empty()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
if (auto r = nix_cmake_scan(root, name); r) {
|
||||||
|
return std::move(*r);
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
};
|
||||||
|
auto scan_hit = try_scan(info->dev_path);
|
||||||
|
if (!scan_hit) {
|
||||||
|
scan_hit = try_scan(info->out_path);
|
||||||
|
}
|
||||||
|
if (scan_hit) {
|
||||||
candidates.push_back(
|
candidates.push_back(
|
||||||
{"nix-probe", recipe_from_nix_scan(*n, name, "nix-probe")});
|
{"nix-probe", recipe_from_nix_scan(*scan_hit, name, "nix-probe")});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (candidates.empty()) {
|
if (candidates.empty()) {
|
||||||
|
|||||||
@@ -15,8 +15,14 @@ namespace {
|
|||||||
// `outPath` is a magic attribute name that triggers nix's derivation
|
// `outPath` is a magic attribute name that triggers nix's derivation
|
||||||
// coercion in --json mode (the attrset gets serialized as a bare path
|
// coercion in --json mode (the attrset gets serialized as a bare path
|
||||||
// string). Renaming the field to `path` keeps it a proper JSON object.
|
// string). Renaming the field to `path` keeps it a proper JSON object.
|
||||||
|
//
|
||||||
|
// `dev_path` is captured for multi-output packages (boost, openssl,
|
||||||
|
// llvm, …) where CMake configs live under the `dev` output rather
|
||||||
|
// than `out`. `p ? dev` is false for single-output packages, leaving
|
||||||
|
// the field as the empty string.
|
||||||
constexpr std::string_view APPLY_FN =
|
constexpr std::string_view APPLY_FN =
|
||||||
"p: { version = p.version or \"\"; path = p.outPath; }";
|
"p: { version = p.version or \"\"; path = p.outPath; "
|
||||||
|
"dev_path = if p ? dev then p.dev.outPath else \"\"; }";
|
||||||
|
|
||||||
auto make_error(util::ErrorCode code, std::string msg) -> util::Error {
|
auto make_error(util::ErrorCode code, std::string msg) -> util::Error {
|
||||||
return util::Error{code, std::move(msg), "", std::nullopt, std::nullopt};
|
return util::Error{code, std::move(msg), "", std::nullopt, std::nullopt};
|
||||||
@@ -48,7 +54,9 @@ auto parse_nix_eval_json(std::string_view attr, std::string_view json)
|
|||||||
"nix eval JSON is not an object"));
|
"nix eval JSON is not an object"));
|
||||||
}
|
}
|
||||||
|
|
||||||
NixpkgsInfo info{.attr = std::string{attr}, .version = {}, .out_path = {}};
|
NixpkgsInfo info{
|
||||||
|
.attr = std::string{attr}, .version = {}, .out_path = {}, .dev_path = {},
|
||||||
|
};
|
||||||
|
|
||||||
if (j.contains("path") && j["path"].is_string()) {
|
if (j.contains("path") && j["path"].is_string()) {
|
||||||
info.out_path = j["path"].get<std::string>();
|
info.out_path = j["path"].get<std::string>();
|
||||||
@@ -62,6 +70,10 @@ auto parse_nix_eval_json(std::string_view attr, std::string_view json)
|
|||||||
info.version = j["version"].get<std::string>();
|
info.version = j["version"].get<std::string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (j.contains("dev_path") && j["dev_path"].is_string()) {
|
||||||
|
info.dev_path = j["dev_path"].get<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,17 @@ export namespace cargoxx::resolver {
|
|||||||
|
|
||||||
// What `nix eval nixpkgs#<pkg>` reports for a package: a confirmation that
|
// What `nix eval nixpkgs#<pkg>` reports for a package: a confirmation that
|
||||||
// the attribute exists, a best-effort version string, and the realized
|
// the attribute exists, a best-effort version string, and the realized
|
||||||
// nix-store path so later probes can scan its installed CMake configs.
|
// nix-store path(s) so later probes can scan its installed CMake configs.
|
||||||
|
//
|
||||||
|
// Multi-output packages (boost, openssl, llvm, ...) expose CMake configs
|
||||||
|
// in their `dev` output, not in the default `out`. When the package has
|
||||||
|
// a separate dev output its store path is captured here so callers can
|
||||||
|
// scan it preferentially.
|
||||||
struct NixpkgsInfo {
|
struct NixpkgsInfo {
|
||||||
std::string attr; // the queried name, e.g. "simdjson"
|
std::string attr; // the queried name, e.g. "simdjson"
|
||||||
std::string version; // empty when the derivation has no version
|
std::string version; // empty when the derivation has no version
|
||||||
std::string out_path; // absolute /nix/store/... path
|
std::string out_path; // default output's absolute /nix/store/... path
|
||||||
|
std::string dev_path; // dev output's path; empty when no dev output
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pure parser exposed for unit testing. Accepts the raw JSON returned by
|
// Pure parser exposed for unit testing. Accepts the raw JSON returned by
|
||||||
|
|||||||
@@ -53,3 +53,29 @@ TEST_CASE("parse_nix_eval_json fails on a non-object root",
|
|||||||
REQUIRE_FALSE(r.has_value());
|
REQUIRE_FALSE(r.has_value());
|
||||||
REQUIRE(r.error().code == ErrorCode::ResolutionNetworkError);
|
REQUIRE(r.error().code == ErrorCode::ResolutionNetworkError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("parse_nix_eval_json captures dev_path when present",
|
||||||
|
"[resolver][nixpkgs]") {
|
||||||
|
constexpr std::string_view input = R"({
|
||||||
|
"version": "21.1.8",
|
||||||
|
"path": "/nix/store/abc-llvm-21.1.8",
|
||||||
|
"dev_path": "/nix/store/def-llvm-21.1.8-dev"
|
||||||
|
})";
|
||||||
|
auto r = parse_nix_eval_json("libllvm", input);
|
||||||
|
REQUIRE(r.has_value());
|
||||||
|
REQUIRE(r->out_path == "/nix/store/abc-llvm-21.1.8");
|
||||||
|
REQUIRE(r->dev_path == "/nix/store/def-llvm-21.1.8-dev");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("parse_nix_eval_json leaves dev_path empty for single-output packages",
|
||||||
|
"[resolver][nixpkgs]") {
|
||||||
|
constexpr std::string_view input = R"({
|
||||||
|
"version": "2.12.2",
|
||||||
|
"path": "/nix/store/xyz-hello-2.12.2",
|
||||||
|
"dev_path": ""
|
||||||
|
})";
|
||||||
|
auto r = parse_nix_eval_json("hello", input);
|
||||||
|
REQUIRE(r.has_value());
|
||||||
|
REQUIRE(r->out_path == "/nix/store/xyz-hello-2.12.2");
|
||||||
|
REQUIRE(r->dev_path.empty());
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user