module cargoxx.resolver; import std; import cargoxx.util; import cargoxx.linkdb; namespace cargoxx::resolver { namespace fs = std::filesystem; namespace { auto error(util::ErrorCode code, std::string msg) -> util::Error { return util::Error{code, std::move(msg), "", std::nullopt, std::nullopt}; } // Build a linkdb::Recipe out of one of the probe outputs. The // nixpkgs_attr is always the package name (the attr we used to query // nixpkgs in step 1); find_package + targets come from the probe. auto recipe_from_conan(const ConanRecipe& c, const std::string& nixpkgs_attr, const std::string& source) -> linkdb::Recipe { return linkdb::Recipe{ .nixpkgs_attr = nixpkgs_attr, .find_package = c.find_package, .targets = c.targets, .source = source, }; } auto recipe_from_vcpkg(const VcpkgRecipe& v, const std::string& nixpkgs_attr, const std::string& source) -> linkdb::Recipe { return linkdb::Recipe{ .nixpkgs_attr = nixpkgs_attr, .find_package = v.find_package, .targets = v.targets, .source = source, }; } auto recipe_from_nix_scan(const NixCmakeCandidate& n, const std::string& nixpkgs_attr, const std::string& source) -> linkdb::Recipe { return linkdb::Recipe{ .nixpkgs_attr = nixpkgs_attr, .find_package = n.find_package, .targets = n.targets, .source = source, }; } struct Candidate { std::string source; linkdb::Recipe recipe; }; auto try_verify(const Candidate& cand, const std::string& name, const std::string& version_spec, const std::vector& components, const fs::path& overlay_path, const fs::path& scratch_root, const BuildFn& build_fn) -> util::Result { VerifyLinkRequest req{ .candidate = cand.recipe, .source = cand.source, .package_name = name, .version_spec = version_spec, .components = components, .overlay_path = overlay_path, .scratch_root = scratch_root, }; return verify_link(req, build_fn); } } // namespace auto discover(const std::string& name, const std::string& version_spec, const std::vector& components, const fs::path& overlay_path, const fs::path& scratch_root, const BuildFn& build_fn) -> util::Result { auto info = nixpkgs_probe(name); if (!info) { return std::unexpected(info.error()); } std::vector candidates; if (auto c = conan_probe(name); c) { candidates.push_back({"conan", recipe_from_conan(*c, name, "conan")}); } if (auto v = vcpkg_probe(name); v) { candidates.push_back({"vcpkg", recipe_from_vcpkg(*v, name, "vcpkg")}); } if (auto n = nix_cmake_scan(info->out_path, name); n) { candidates.push_back( {"nix-probe", recipe_from_nix_scan(*n, name, "nix-probe")}); } if (candidates.empty()) { return std::unexpected(error( util::ErrorCode::ResolutionUnsatisfiable, std::format("no recipe candidates for '{}' (Conan/vcpkg/nix-scan all empty)", name))); } util::Error last_error{ util::ErrorCode::ResolutionUnsatisfiable, std::format("no candidate for '{}' verified", name), "", std::nullopt, std::nullopt, }; for (auto& cand : candidates) { auto verified = try_verify(cand, name, version_spec, components, overlay_path, scratch_root, build_fn); if (verified) { return Discovered{ .recipe = std::move(cand.recipe), .source = std::move(cand.source), }; } last_error = verified.error(); } return std::unexpected(last_error); } } // namespace cargoxx::resolver