[M5+] realize candidate output before nix-cmake-scan
This commit is contained in:
@@ -89,21 +89,31 @@ auto discover(const std::string& name, const std::string& version_spec,
|
||||
if (auto v = vcpkg_probe(name); v) {
|
||||
candidates.push_back({"vcpkg", recipe_from_vcpkg(*v, name, "vcpkg")});
|
||||
}
|
||||
// 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)
|
||||
// Multi-output nix packages keep CMake configs in the `dev` output.
|
||||
// The probe above only computes paths via `nix eval`; for packages
|
||||
// not yet present in the store, those paths don't exist on disk
|
||||
// and the scan would fail on its `fs::exists` check. Realize each
|
||||
// candidate output (downloads from the binary cache) before
|
||||
// scanning. dev wins when available; fall back to the default out.
|
||||
auto realize_and_scan = [&](std::string_view sub_attr)
|
||||
-> std::optional<NixCmakeCandidate> {
|
||||
if (root.empty()) {
|
||||
auto attr =
|
||||
sub_attr.empty() ? name : std::format("{}.{}", name, sub_attr);
|
||||
auto realized = realize_path(attr);
|
||||
if (!realized) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (auto r = nix_cmake_scan(root, name); r) {
|
||||
if (auto r = nix_cmake_scan(fs::path{*realized}, name); r) {
|
||||
return std::move(*r);
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
auto scan_hit = try_scan(info->dev_path);
|
||||
std::optional<NixCmakeCandidate> scan_hit;
|
||||
if (!info->dev_path.empty()) {
|
||||
scan_hit = realize_and_scan("dev");
|
||||
}
|
||||
if (!scan_hit) {
|
||||
scan_hit = try_scan(info->out_path);
|
||||
scan_hit = realize_and_scan("");
|
||||
}
|
||||
if (scan_hit) {
|
||||
candidates.push_back(
|
||||
|
||||
@@ -115,4 +115,51 @@ auto nixpkgs_probe(const std::string& attr) -> util::Result<NixpkgsInfo> {
|
||||
return parse_nix_eval_json(attr, r->stdout_text);
|
||||
}
|
||||
|
||||
auto realize_path(const std::string& flake_attr) -> util::Result<std::string> {
|
||||
if (flake_attr.empty()) {
|
||||
return std::unexpected(make_error(util::ErrorCode::ResolutionUnknownPackage,
|
||||
"realize_path: flake_attr is empty"));
|
||||
}
|
||||
|
||||
std::vector<std::string> args{
|
||||
"--extra-experimental-features", "nix-command flakes",
|
||||
"build", std::format("nixpkgs#{}", flake_attr),
|
||||
"--no-link", "--print-out-paths",
|
||||
};
|
||||
|
||||
auto r = exec::run("nix", args,
|
||||
exec::ExecOptions{
|
||||
.cwd = {},
|
||||
.env_overrides = {},
|
||||
// Long enough for first-time fetch from the binary
|
||||
// cache; multi-output llvm tarballs are ~hundreds of MB.
|
||||
.timeout = std::chrono::seconds{600},
|
||||
.inherit_stdio = false,
|
||||
});
|
||||
if (!r) {
|
||||
return std::unexpected(r.error());
|
||||
}
|
||||
if (r->exit_code != 0) {
|
||||
if (looks_like_missing_attribute(r->stderr_text)) {
|
||||
return std::unexpected(make_error(
|
||||
util::ErrorCode::ResolutionUnknownPackage,
|
||||
std::format("nixpkgs has no attribute '{}'", flake_attr)));
|
||||
}
|
||||
return std::unexpected(make_error(
|
||||
util::ErrorCode::ResolutionNetworkError,
|
||||
std::format("nix build failed (exit {}): {}", r->exit_code,
|
||||
r->stderr_text)));
|
||||
}
|
||||
auto path = r->stdout_text;
|
||||
while (!path.empty() && (path.back() == '\n' || path.back() == ' ')) {
|
||||
path.pop_back();
|
||||
}
|
||||
if (path.empty()) {
|
||||
return std::unexpected(make_error(
|
||||
util::ErrorCode::ResolutionNetworkError,
|
||||
std::format("nix build emitted no path for '{}'", flake_attr)));
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
} // namespace cargoxx::resolver
|
||||
|
||||
@@ -33,6 +33,18 @@ auto parse_nix_eval_json(std::string_view attr, std::string_view json)
|
||||
// `ResolutionNetworkError` on timeout or evaluator errors.
|
||||
auto nixpkgs_probe(const std::string& attr) -> util::Result<NixpkgsInfo>;
|
||||
|
||||
// Materializes a flake attribute on disk by shelling out to
|
||||
// nix build --no-link --print-out-paths nixpkgs#<flake_attr>
|
||||
// and returns the absolute store path. The eval-only `nixpkgs_probe`
|
||||
// computes the path the output *would* have, but it isn't actually
|
||||
// present on disk until it's been built / fetched from the binary
|
||||
// cache; nix_cmake_scan needs it on disk to walk lib/cmake. Use
|
||||
// `realize_path("libclang.dev")` to scan a dev output, etc.
|
||||
//
|
||||
// `ResolutionUnknownPackage` for unknown attrs, `ResolutionNetworkError`
|
||||
// for build / network errors.
|
||||
auto realize_path(const std::string& flake_attr) -> util::Result<std::string>;
|
||||
|
||||
// One CMake config-file's IMPORTED targets together with the find_package
|
||||
// expression derived from its filename stem.
|
||||
struct NixCmakeCandidate {
|
||||
|
||||
Reference in New Issue
Block a user