diff --git a/Cargoxx.lock b/Cargoxx.lock index 629f3d9..86fa894 100644 --- a/Cargoxx.lock +++ b/Cargoxx.lock @@ -12,7 +12,7 @@ nixpkgs_attr = 'reproc' version = '*' [[package]] -linkdb_source = 'pkg-config' +linkdb_source = 'cmake-findmodule' name = 'sqlite' nixpkgs_attr = 'sqlite' version = '*' diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index c20fd4d..f294595 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -19,8 +19,7 @@ add_compile_options(-Wall -Wextra -Wpedantic -Wconversion) # ----- dependencies ----- find_package(reproc CONFIG REQUIRED) -find_package(PkgConfig REQUIRED) -pkg_check_modules(SQLITE3 REQUIRED IMPORTED_TARGET sqlite3) +find_package(SQLite3 REQUIRED) find_package(Catch2 CONFIG REQUIRED) # ----- library target ----- @@ -57,6 +56,7 @@ target_sources(cargoxx ../src/lockfile/lockfile.cpp ../src/manifest/parser.cpp ../src/manifest/writer.cpp + ../src/resolver/brute_scan.cpp ../src/resolver/conan_probe.cpp ../src/resolver/discover.cpp ../src/resolver/findmodule_scan.cpp @@ -76,7 +76,7 @@ target_sources(cargoxx target_include_directories(cargoxx SYSTEM PRIVATE ../third_party) target_link_libraries(cargoxx PUBLIC reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 ) # ----- binary target ----- @@ -85,7 +85,7 @@ set_target_properties(cargoxx_bin PROPERTIES OUTPUT_NAME cargoxx) target_link_libraries(cargoxx_bin PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 ) # ----- tests ----- @@ -95,7 +95,7 @@ add_executable(test_cmd_add ../tests/cmd_add.cpp) target_link_libraries(test_cmd_add PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -104,7 +104,7 @@ add_executable(test_cmd_build ../tests/cmd_build.cpp) target_link_libraries(test_cmd_build PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -113,7 +113,7 @@ add_executable(test_cmd_clean ../tests/cmd_clean.cpp) target_link_libraries(test_cmd_clean PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -122,7 +122,7 @@ add_executable(test_cmd_new ../tests/cmd_new.cpp) target_link_libraries(test_cmd_new PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -131,7 +131,7 @@ add_executable(test_cmd_remove ../tests/cmd_remove.cpp) target_link_libraries(test_cmd_remove PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -140,7 +140,7 @@ add_executable(test_cmd_run ../tests/cmd_run.cpp) target_link_libraries(test_cmd_run PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -149,7 +149,7 @@ add_executable(test_codegen_cmake ../tests/codegen_cmake.cpp) target_link_libraries(test_codegen_cmake PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -158,7 +158,7 @@ add_executable(test_codegen_flake ../tests/codegen_flake.cpp) target_link_libraries(test_codegen_flake PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -167,7 +167,7 @@ add_executable(test_conan_probe_live ../tests/conan_probe_live.cpp) target_link_libraries(test_conan_probe_live PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -176,7 +176,7 @@ add_executable(test_conan_probe_parse ../tests/conan_probe_parse.cpp) target_link_libraries(test_conan_probe_parse PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -185,7 +185,7 @@ add_executable(test_devbox_resolve_live ../tests/devbox_resolve_live.cpp) target_link_libraries(test_devbox_resolve_live PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -194,7 +194,7 @@ add_executable(test_devbox_resolve_parse ../tests/devbox_resolve_parse.cpp) target_link_libraries(test_devbox_resolve_parse PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -203,7 +203,7 @@ add_executable(test_exec_run ../tests/exec_run.cpp) target_link_libraries(test_exec_run PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -212,7 +212,7 @@ add_executable(test_layout_discovery ../tests/layout_discovery.cpp) target_link_libraries(test_layout_discovery PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -221,7 +221,7 @@ add_executable(test_linkdb_lookup ../tests/linkdb_lookup.cpp) target_link_libraries(test_linkdb_lookup PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -230,7 +230,7 @@ add_executable(test_linkdb_overlay ../tests/linkdb_overlay.cpp) target_link_libraries(test_linkdb_overlay PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -239,7 +239,7 @@ add_executable(test_lockfile_round_trip ../tests/lockfile_round_trip.cpp) target_link_libraries(test_lockfile_round_trip PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -248,7 +248,7 @@ add_executable(test_manifest_parse ../tests/manifest_parse.cpp) target_link_libraries(test_manifest_parse PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -257,7 +257,7 @@ add_executable(test_manifest_write ../tests/manifest_write.cpp) target_link_libraries(test_manifest_write PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -266,7 +266,7 @@ add_executable(test_nix_cmake_scan_live ../tests/nix_cmake_scan_live.cpp) target_link_libraries(test_nix_cmake_scan_live PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -275,7 +275,7 @@ add_executable(test_nix_cmake_scan_parse ../tests/nix_cmake_scan_parse.cpp) target_link_libraries(test_nix_cmake_scan_parse PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -284,7 +284,7 @@ add_executable(test_nixpkgs_git_resolve ../tests/nixpkgs_git_resolve.cpp) target_link_libraries(test_nixpkgs_git_resolve PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -293,7 +293,7 @@ add_executable(test_nixpkgs_probe_live ../tests/nixpkgs_probe_live.cpp) target_link_libraries(test_nixpkgs_probe_live PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -302,7 +302,7 @@ add_executable(test_nixpkgs_probe_parse ../tests/nixpkgs_probe_parse.cpp) target_link_libraries(test_nixpkgs_probe_parse PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -311,7 +311,7 @@ add_executable(test_semver_satisfies ../tests/semver_satisfies.cpp) target_link_libraries(test_semver_satisfies PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -320,7 +320,7 @@ add_executable(test_util_error ../tests/util_error.cpp) target_link_libraries(test_util_error PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -329,7 +329,7 @@ add_executable(test_vcpkg_probe_live ../tests/vcpkg_probe_live.cpp) target_link_libraries(test_vcpkg_probe_live PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -338,7 +338,7 @@ add_executable(test_vcpkg_probe_parse ../tests/vcpkg_probe_parse.cpp) target_link_libraries(test_vcpkg_probe_parse PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) @@ -347,7 +347,7 @@ add_executable(test_verify_link_unit ../tests/verify_link_unit.cpp) target_link_libraries(test_verify_link_unit PRIVATE cargoxx reproc - PkgConfig::SQLITE3 + SQLite::SQLite3 Catch2::Catch2 Catch2::Catch2WithMain ) diff --git a/flake.nix b/flake.nix index 8574da1..205e09c 100644 --- a/flake.nix +++ b/flake.nix @@ -17,7 +17,6 @@ nativeBuildInputs = [ pkgs.ninja pkgs.cmake - pkgs.pkg-config ]; buildInputs = [ pkgs.reproc diff --git a/src/codegen/cmake.cpp b/src/codegen/cmake.cpp index a353a62..921b227 100644 --- a/src/codegen/cmake.cpp +++ b/src/codegen/cmake.cpp @@ -89,7 +89,34 @@ auto emit_find_packages(const std::vector& recipes, bool pkgconfig_emitted = false; auto emit_one = [&](const linkdb::Recipe& r) { - if (r.pkg_config_module && !r.pkg_config_module->empty()) { + if (!r.brute_force_libs.empty() || !r.brute_force_includes.empty()) { + // Synthesize a single INTERFACE IMPORTED target named after + // the first entry in `targets` (e.g. `::`). No + // find_package — every artifact path is baked in. + if (r.targets.empty()) { + return; + } + const auto& target = r.targets.front(); + out += std::format("add_library({} INTERFACE IMPORTED)\n", target); + if (!r.brute_force_libs.empty()) { + out += std::format("set_property(TARGET {} APPEND PROPERTY " + "INTERFACE_LINK_LIBRARIES", + target); + for (const auto& l : r.brute_force_libs) { + out += std::format("\n \"{}\"", l); + } + out += ")\n"; + } + if (!r.brute_force_includes.empty()) { + out += std::format("set_property(TARGET {} APPEND PROPERTY " + "INTERFACE_INCLUDE_DIRECTORIES", + target); + for (const auto& i : r.brute_force_includes) { + out += std::format("\n \"{}\"", i); + } + out += ")\n"; + } + } else if (r.pkg_config_module && !r.pkg_config_module->empty()) { if (!pkgconfig_emitted) { out += "find_package(PkgConfig REQUIRED)\n"; pkgconfig_emitted = true; diff --git a/src/linkdb/database.cpp b/src/linkdb/database.cpp index 38e8e65..9ffd42e 100644 --- a/src/linkdb/database.cpp +++ b/src/linkdb/database.cpp @@ -65,6 +65,8 @@ auto Database::resolve(const std::string& package, const std::string& version, .targets = row.targets, .source = row.source, .pkg_config_module = row.pkg_config_module, + .brute_force_libs = row.brute_force_libs, + .brute_force_includes = row.brute_force_includes, }; } return std::unexpected(util::Error{ diff --git a/src/linkdb/linkdb.cppm b/src/linkdb/linkdb.cppm index 3d5f185..c455ba2 100644 --- a/src/linkdb/linkdb.cppm +++ b/src/linkdb/linkdb.cppm @@ -25,6 +25,14 @@ struct Recipe { // overlay rows don't need a sentinel. std::optional pkg_config_module; + // Set by the brute-force probe (the last-resort discover stage). + // When non-empty, codegen skips `find_package(...)` and instead + // synthesizes an INTERFACE IMPORTED target named in `targets[0]` + // (which is `::`) with these absolute lib paths + + // include dirs. + std::vector brute_force_libs; + std::vector brute_force_includes; + bool operator==(const Recipe&) const = default; }; @@ -40,6 +48,8 @@ struct OverlayRow { std::string source; std::int64_t verified_at = 0; std::optional pkg_config_module; + std::vector brute_force_libs; + std::vector brute_force_includes; }; // RAII wrapper for an open sqlite3 connection used by the overlay database. diff --git a/src/linkdb/overlay.cpp b/src/linkdb/overlay.cpp index 0f3482b..573978d 100644 --- a/src/linkdb/overlay.cpp +++ b/src/linkdb/overlay.cpp @@ -90,24 +90,40 @@ auto overlay_open(const std::filesystem::path& path) }); } - // Schema migration: legacy overlays predate pkg_config_module. - // SQLite ADD COLUMN errors when the column already exists; treat - // "duplicate column" as success. - constexpr const char* MIGRATE_PC = - "ALTER TABLE recipes ADD COLUMN pkg_config_module TEXT"; - char* mig_err = nullptr; - if (sqlite3_exec(state->handle(), MIGRATE_PC, nullptr, nullptr, &mig_err) != - SQLITE_OK) { - if (mig_err && std::string_view{mig_err}.find("duplicate column") == - std::string_view::npos) { - std::string msg = std::format("cannot migrate overlay schema: {}", - mig_err ? mig_err : "?"); + // Schema migrations. SQLite ADD COLUMN errors when the column + // already exists; treat "duplicate column" as success. + auto add_column = [&](const char* sql) -> util::Result { + char* mig_err = nullptr; + if (sqlite3_exec(state->handle(), sql, nullptr, nullptr, &mig_err) != + SQLITE_OK) { + if (mig_err && std::string_view{mig_err}.find("duplicate column") == + std::string_view::npos) { + std::string msg = std::format("cannot migrate overlay schema: {}", + mig_err ? mig_err : "?"); + sqlite3_free(mig_err); + return std::unexpected(util::Error{ + util::ErrorCode::LinkdbCorrupt, std::move(msg), "", path, + std::nullopt, + }); + } sqlite3_free(mig_err); - return std::unexpected(util::Error{ - util::ErrorCode::LinkdbCorrupt, std::move(msg), "", path, std::nullopt, - }); } - sqlite3_free(mig_err); + return {}; + }; + if (auto r = add_column( + "ALTER TABLE recipes ADD COLUMN pkg_config_module TEXT"); + !r) { + return std::unexpected(r.error()); + } + if (auto r = add_column( + "ALTER TABLE recipes ADD COLUMN brute_force_libs TEXT"); + !r) { + return std::unexpected(r.error()); + } + if (auto r = add_column( + "ALTER TABLE recipes ADD COLUMN brute_force_includes TEXT"); + !r) { + return std::unexpected(r.error()); } return state; @@ -119,8 +135,8 @@ auto overlay_insert_manual(OverlayState& state, const std::string& package, constexpr const char* SQL = "INSERT OR REPLACE INTO recipes " "(package, version_range, nixpkgs_attr, find_package, targets, components, source, " - " verified_at, pkg_config_module) " - "VALUES (?, ?, ?, ?, ?, NULL, 'manual', ?, ?)"; + " verified_at, pkg_config_module, brute_force_libs, brute_force_includes) " + "VALUES (?, ?, ?, ?, ?, NULL, 'manual', ?, ?, ?, ?)"; sqlite3* db = state.handle(); sqlite3_stmt* stmt = nullptr; @@ -129,6 +145,8 @@ auto overlay_insert_manual(OverlayState& state, const std::string& package, } auto targets_str = nlohmann::json(r.targets).dump(); + auto libs_str = nlohmann::json(r.brute_force_libs).dump(); + auto incs_str = nlohmann::json(r.brute_force_includes).dump(); auto now = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count(); @@ -144,6 +162,8 @@ auto overlay_insert_manual(OverlayState& state, const std::string& package, } else { sqlite3_bind_null(stmt, 7); } + sqlite3_bind_text(stmt, 8, libs_str.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 9, incs_str.c_str(), -1, SQLITE_TRANSIENT); auto rc = sqlite3_step(stmt); sqlite3_finalize(stmt); @@ -162,8 +182,8 @@ auto overlay_insert(OverlayState& state, const std::string& package, constexpr const char* SQL = "INSERT OR REPLACE INTO recipes " "(package, version_range, nixpkgs_attr, find_package, targets, components, source, " - " verified_at, pkg_config_module) " - "VALUES (?, ?, ?, ?, ?, NULL, ?, ?, ?)"; + " verified_at, pkg_config_module, brute_force_libs, brute_force_includes) " + "VALUES (?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?)"; sqlite3* db = state.handle(); sqlite3_stmt* stmt = nullptr; @@ -172,6 +192,8 @@ auto overlay_insert(OverlayState& state, const std::string& package, } auto targets_str = nlohmann::json(r.targets).dump(); + auto libs_str = nlohmann::json(r.brute_force_libs).dump(); + auto incs_str = nlohmann::json(r.brute_force_includes).dump(); sqlite3_bind_text(stmt, 1, package.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 2, version_range.c_str(), -1, SQLITE_TRANSIENT); @@ -185,6 +207,8 @@ auto overlay_insert(OverlayState& state, const std::string& package, } else { sqlite3_bind_null(stmt, 8); } + sqlite3_bind_text(stmt, 9, libs_str.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 10, incs_str.c_str(), -1, SQLITE_TRANSIENT); auto rc = sqlite3_step(stmt); sqlite3_finalize(stmt); @@ -276,7 +300,7 @@ auto overlay_query(OverlayState& state, const std::string& package) -> util::Result> { constexpr const char* SQL = "SELECT version_range, nixpkgs_attr, find_package, targets, source, verified_at, " - " pkg_config_module " + " pkg_config_module, brute_force_libs, brute_force_includes " "FROM recipes WHERE package = ?"; sqlite3* db = state.handle(); @@ -310,6 +334,23 @@ auto overlay_query(OverlayState& state, const std::string& package) if (sqlite3_column_type(stmt, 6) != SQLITE_NULL) { row.pkg_config_module = column_text(stmt, 6); } + auto parse_str_array = [&](int col, std::vector& out_arr) { + if (sqlite3_column_type(stmt, col) == SQLITE_NULL) { + return; + } + try { + auto txt = column_text(stmt, col); + if (txt.empty()) { + return; + } + out_arr = + nlohmann::json::parse(txt).get>(); + } catch (const nlohmann::json::exception&) { + // legacy/manual rows may have stored garbage; ignore + } + }; + parse_str_array(7, row.brute_force_libs); + parse_str_array(8, row.brute_force_includes); out.push_back(std::move(row)); } sqlite3_finalize(stmt); diff --git a/src/resolver/brute_scan.cpp b/src/resolver/brute_scan.cpp new file mode 100644 index 0000000..8567340 --- /dev/null +++ b/src/resolver/brute_scan.cpp @@ -0,0 +1,81 @@ +module cargoxx.resolver; + +import std; +import cargoxx.util; + +namespace cargoxx::resolver { + +namespace fs = std::filesystem; + +namespace { + +auto is_lib_filename(const fs::path& p) -> bool { + auto name = p.filename().string(); + if (!name.starts_with("lib")) { + return false; + } + auto ext = p.extension().string(); + if (ext == ".a") { + return true; + } + if (ext == ".so" || ext == ".dylib") { + return true; + } + // .so., .so.., ... — common shared-lib versioning. Use a + // looser check: if the name contains ".so." or ".dylib." anywhere + // after the lib prefix, accept it. + return name.find(".so.") != std::string::npos || + name.find(".dylib.") != std::string::npos; +} + +} // namespace + +auto brute_scan(const fs::path& store_path, const std::string& package_name) + -> util::Result { + if (package_name.empty()) { + return std::unexpected(util::Error{ + util::ErrorCode::ResolutionUnknownPackage, + "brute_scan: package name is empty", + "", std::nullopt, std::nullopt, + }); + } + const auto lib_dir = store_path / "lib"; + const auto include_dir = store_path / "include"; + + BruteCandidate out; + std::error_code ec; + + if (fs::exists(lib_dir, ec) && !ec) { + for (const auto& entry : fs::directory_iterator{ + lib_dir, fs::directory_options::skip_permission_denied, ec}) { + if (!entry.is_regular_file() && !entry.is_symlink()) { + continue; + } + if (!is_lib_filename(entry.path())) { + continue; + } + out.lib_files.push_back(entry.path().string()); + } + std::ranges::sort(out.lib_files); + } + + if (fs::exists(include_dir, ec) && !ec) { + // For include/, expose the top-level directory itself (e.g. + // `/include`) — that's what `#include ` + // expects. Adding every subdir would also work, but is noisier + // and provokes name collisions across deps. + out.include_dirs.push_back(include_dir.string()); + } + + if (out.lib_files.empty() && out.include_dirs.empty()) { + return std::unexpected(util::Error{ + util::ErrorCode::ResolutionUnknownPackage, + std::format("no libs or headers under '{}'", store_path.string()), + "", store_path, std::nullopt, + }); + } + + return out; +} + +} // namespace cargoxx::resolver diff --git a/src/resolver/discover.cpp b/src/resolver/discover.cpp index d2020bb..8648075 100644 --- a/src/resolver/discover.cpp +++ b/src/resolver/discover.cpp @@ -89,6 +89,20 @@ auto recipe_from_findmodule(const FindModuleCandidate& fm, }; } +auto recipe_from_brute(const BruteCandidate& b, const std::string& name, + const std::string& nixpkgs_attr, const std::string& source) + -> linkdb::Recipe { + return linkdb::Recipe{ + .nixpkgs_attr = nixpkgs_attr, + // No find_package — codegen synthesizes the target directly. + .find_package = "", + .targets = {std::format("{}::{}", name, name)}, + .source = source, + .brute_force_libs = b.lib_files, + .brute_force_includes = b.include_dirs, + }; +} + struct Candidate { std::string source; linkdb::Recipe recipe; @@ -183,6 +197,12 @@ auto discover(const std::string& name, const std::string& version_spec, candidates.push_back( {"pkg-config", recipe_from_pc(*pc_hit, name, "pkg-config")}); } + if (!realized_dev_path.empty()) { + if (auto b = brute_scan(fs::path{realized_dev_path}, name); b) { + candidates.push_back( + {"brute-force", recipe_from_brute(*b, name, name, "brute-force")}); + } + } if (candidates.empty()) { return std::unexpected(error( diff --git a/src/resolver/resolver.cppm b/src/resolver/resolver.cppm index 4e75b99..abf5b16 100644 --- a/src/resolver/resolver.cppm +++ b/src/resolver/resolver.cppm @@ -110,6 +110,19 @@ auto pc_scan(const std::filesystem::path& store_path, const std::string& package_name) -> util::Result; +// Last-resort brute-force: every shared/static lib + every include +// directory under the store path is wrapped in a synthetic +// `::` INTERFACE IMPORTED target. Used when nothing more +// structured matched. +struct BruteCandidate { + std::vector lib_files; // abs paths to lib*.{a,so,dylib} + std::vector include_dirs; // abs paths under include/ +}; + +auto brute_scan(const std::filesystem::path& store_path, + const std::string& package_name) + -> util::Result; + // Output of a conan-center-index recipe scrape. struct ConanRecipe { std::string find_package; // e.g. "fmt CONFIG REQUIRED"