diff options
| author | zzbaron <[email protected]> | 2025-09-21 23:48:21 -0400 |
|---|---|---|
| committer | zzbaron <[email protected]> | 2025-09-21 23:48:21 -0400 |
| commit | f1a64ad50e22c55a08a2553669aba152f70d9eb6 (patch) | |
| tree | 9a66ee1533ad6c91fddec50b9871048d6e0f2698 | |
| parent | 32468fdcccd6c29cfc4ac29e078da14bd27fc2d2 (diff) | |
nix: improve package filtering and search accuracy
| -rw-r--r-- | xmake/modules/package/manager/nix/find_package.lua | 243 |
1 files changed, 171 insertions, 72 deletions
diff --git a/xmake/modules/package/manager/nix/find_package.lua b/xmake/modules/package/manager/nix/find_package.lua index 3c9c961d5..fb14a6b90 100644 --- a/xmake/modules/package/manager/nix/find_package.lua +++ b/xmake/modules/package/manager/nix/find_package.lua @@ -115,6 +115,31 @@ function _in_nix_shell() return in_nix_shell == "pure" or in_nix_shell == "impure" end +-- check if a store path likely contains the requested package +function _path_matches_package(store_path, package_name, opt) + local path_name = path.basename(store_path) + local package_name_lower = package_name:lower() + + -- Extract package name from store path + -- Format: hash-packagename-version[-output] + local package_base = path_name:match("^[^%-]+-([^%-]+)") + if package_base then + local package_base_lower = package_base:lower() + + -- Exact match + if package_base_lower == package_name_lower then + return true + end + + -- Partial match (package name contains or is contained in the base name) + if package_base_lower:find(package_name_lower, 1, true) or package_name_lower:find(package_base_lower, 1, true) then + return true + end + end + + return false +end + -- group store paths by package base name function _group_store_paths_by_package(store_paths, opt) local packages = {} @@ -140,15 +165,24 @@ end -- pkg-config search that handles all outputs function _find_with_pkgconfig(package_name, store_paths, opt) - if opt and (opt.verbose or option.get("verbose")) then - print("Nix: pkg-config search for " .. package_name) + + -- Only use store paths that might contain the requested package for pkg-config + local relevant_paths = {} + for _, store_path in ipairs(store_paths) do + if _path_matches_package(store_path, package_name, opt) then + table.insert(relevant_paths, store_path) + end + end + + if #relevant_paths == 0 then + return nil end - -- Collect all pkg-config directories from all store paths + -- Collect all pkg-config directories from relevant store paths local all_pkgconfig_dirs = {} local pkgconfig_env_additions = {} - for _, store_path in ipairs(store_paths) do + for _, store_path in ipairs(relevant_paths) do local pkgconfig_dirs = { path.join(store_path, "lib", "pkgconfig"), path.join(store_path, "share", "pkgconfig") @@ -182,6 +216,10 @@ function _find_with_pkgconfig(package_name, store_paths, opt) -- First try direct package name result = find_package_from_pkgconfig(package_name) + if result then + return result + end + if not result then -- Try alternative names - check what .pc files actually exist for _, pkgconfig_dir in ipairs(all_pkgconfig_dirs) do @@ -199,9 +237,6 @@ function _find_with_pkgconfig(package_name, store_paths, opt) if pc_lower:find(name_lower, 1, true) or name_lower:find(pc_lower, 1, true) then result = find_package_from_pkgconfig(pc_name) if result then - if opt and (opt.verbose or option.get("verbose")) then - print("Nix: Found via pkg-config: " .. pc_name) - end break end end @@ -214,8 +249,9 @@ function _find_with_pkgconfig(package_name, store_paths, opt) return result end --- extract package info from all outputs of a package +-- extract package info from relevant store paths only function _extract_package_info(store_paths, package_name, opt) + local result = { includedirs = {}, bindirs = {}, @@ -224,19 +260,43 @@ function _extract_package_info(store_paths, package_name, opt) libfiles = {} } - -- Process store paths, not just the ones that match the package name - -- This ensures we get include directories from propagated dependencies + -- First, try pkg-config search with only relevant paths + local pkgconfig_result = _find_with_pkgconfig(package_name, store_paths, opt) + if pkgconfig_result then + return pkgconfig_result + end + + -- Find paths that match the requested package + local main_package_paths = {} + local dependency_paths = {} + local found_main_package = false + for _, store_path in ipairs(store_paths) do - -- Add include directories from ALL store paths + if _path_matches_package(store_path, package_name, opt) then + table.insert(main_package_paths, store_path) + found_main_package = true + else + table.insert(dependency_paths, store_path) + end + end + + if not found_main_package then + return nil + end + + -- Process main package paths first (for bins and primary libs) + for _, store_path in ipairs(main_package_paths) do + + -- Add include directories local includedir = path.join(store_path, "include") if os.isdir(includedir) then table.insert(result.includedirs, includedir) if opt and (opt.verbose or option.get("verbose")) then - print("Nix: Found include dir: " .. includedir) + print("Nix: Found main package include dir: " .. includedir) end -- Recursively add one level of subdirectories to handle cases where - -- headers are organized in subdirectories + -- headers are organized in subdirectories (openexr, etc.) local subdirs = try {function() return os.dirs(path.join(includedir, "*")) end} or {} @@ -245,22 +305,22 @@ function _extract_package_info(store_paths, package_name, opt) if os.isdir(subdir) then table.insert(result.includedirs, subdir) if opt and (opt.verbose or option.get("verbose")) then - print("Nix: Found include subdir: " .. subdir) + print("Nix: Found main package include subdir: " .. subdir) end end end end - -- Add bin directories from any output that has them + -- Add bin directories local bindir = path.join(store_path, "bin") if os.isdir(bindir) then table.insert(result.bindirs, bindir) if opt and (opt.verbose or option.get("verbose")) then - print("Nix: Found bin dir: " .. bindir) + print("Nix: Found main package bin dir: " .. bindir) end end - -- Add lib directories and scan for libraries from any output that has them + -- Add lib directories and scan for libraries local libdir = path.join(store_path, "lib") if os.isdir(libdir) then -- Check if this lib dir actually contains libraries (not just cmake/pkgconfig) @@ -278,7 +338,7 @@ function _extract_package_info(store_paths, package_name, opt) if #libfiles > 0 then table.insert(result.linkdirs, libdir) if opt and (opt.verbose or option.get("verbose")) then - print("Nix: Found lib dir: " .. libdir .. " (" .. #libfiles .. " libraries)") + print("Nix: Found main package lib dir: " .. libdir .. " (" .. #libfiles .. " libraries)") end for _, libfile in ipairs(libfiles) do @@ -290,6 +350,9 @@ function _extract_package_info(store_paths, package_name, opt) if linkname then table.insert(result.links, linkname) table.insert(result.libfiles, libfile) + if opt and (opt.verbose or option.get("verbose")) then + print("Nix: Found main package library: " .. linkname .. " -> " .. libfile) + end end end else @@ -300,65 +363,88 @@ function _extract_package_info(store_paths, package_name, opt) if has_cmake or has_pkgconfig then table.insert(result.linkdirs, libdir) if opt and (opt.verbose or option.get("verbose")) then - print("Nix: Found lib dir: " .. libdir .. " (cmake/pkgconfig only)") + print("Nix: Found main package lib dir: " .. libdir .. " (cmake/pkgconfig only)") end end end end end - -- Try pkg-config search with all store paths - local pkgconfig_result = _find_with_pkgconfig(package_name, store_paths, opt) - - -- Merge pkg-config results if available (prioritize pkg-config results) - if pkgconfig_result then - -- Create new result starting with pkg-config data (highest priority) - local merged_result = { - includedirs = {}, - bindirs = result.bindirs, -- Keep discovered bin dirs - linkdirs = {}, - links = {}, - libfiles = result.libfiles -- Keep discovered lib files - } - - -- Add pkg-config include dirs first (highest priority) - for _, incdir in ipairs(pkgconfig_result.includedirs or {}) do - table.insert(merged_result.includedirs, incdir) - end - - -- Add our discovered include dirs after pkg-config ones - for _, incdir in ipairs(result.includedirs) do - table.insert(merged_result.includedirs, incdir) - end - - -- Add pkg-config link dirs first - for _, linkdir in ipairs(pkgconfig_result.linkdirs or {}) do - table.insert(merged_result.linkdirs, linkdir) - end + -- Process dependency paths (include dirs and libs only, no bins) + for _, store_path in ipairs(dependency_paths) do - -- Add our discovered link dirs after pkg-config ones - for _, linkdir in ipairs(result.linkdirs) do - table.insert(merged_result.linkdirs, linkdir) - end - - -- Add pkg-config links first - for _, link in ipairs(pkgconfig_result.links or {}) do - table.insert(merged_result.links, link) - end - - -- Add our discovered links after pkg-config ones - for _, link in ipairs(result.links) do - table.insert(merged_result.links, link) + -- Add include directories from dependencies + local includedir = path.join(store_path, "include") + if os.isdir(includedir) then + table.insert(result.includedirs, includedir) + if opt and (opt.verbose or option.get("verbose")) then + print("Nix: Found dependency include dir: " .. includedir) + end + + -- Recursively add one level of subdirectories to handle cases where + -- headers are organized in subdirectories + local subdirs = try {function() + return os.dirs(path.join(includedir, "*")) + end} or {} + + for _, subdir in ipairs(subdirs) do + if os.isdir(subdir) then + table.insert(result.includedirs, subdir) + if opt and (opt.verbose or option.get("verbose")) then + print("Nix: Found dependency include subdir: " .. subdir) + end + end + end end - -- Add any system links from pkg-config - if pkgconfig_result.syslinks then - for _, link in ipairs(pkgconfig_result.syslinks) do - table.insert(merged_result.links, link) + -- Add lib directories and scan for libraries from dependencies + local libdir = path.join(store_path, "lib") + if os.isdir(libdir) then + -- Check if this lib dir actually contains libraries (not just cmake/pkgconfig) + local libfiles = try {function() + local files = {} + local so_files = os.files(path.join(libdir, "*.so*")) or {} + local a_files = os.files(path.join(libdir, "*.a")) or {} + local dylib_files = os.files(path.join(libdir, "*.dylib*")) or {} + for _, f in ipairs(so_files) do table.insert(files, f) end + for _, f in ipairs(a_files) do table.insert(files, f) end + for _, f in ipairs(dylib_files) do table.insert(files, f) end + return files + end} or {} + + if #libfiles > 0 then + table.insert(result.linkdirs, libdir) + if opt and (opt.verbose or option.get("verbose")) then + print("Nix: Found dependency lib dir: " .. libdir .. " (" .. #libfiles .. " libraries)") + end + + for _, libfile in ipairs(libfiles) do + local filename = path.filename(libfile) + local linkname = filename:match("^lib(.+)%.so") or + filename:match("^lib(.+)%.a") or + filename:match("^lib(.+)%.dylib") + + if linkname then + table.insert(result.links, linkname) + table.insert(result.libfiles, libfile) + if opt and (opt.verbose or option.get("verbose")) then + print("Nix: Found dependency library: " .. linkname .. " -> " .. libfile) + end + end + end + else + -- If no actual libraries but has cmake/pkgconfig, still add for potential cmake usage + local has_cmake = os.isdir(path.join(libdir, "cmake")) + local has_pkgconfig = os.isdir(path.join(libdir, "pkgconfig")) + + if has_cmake or has_pkgconfig then + table.insert(result.linkdirs, libdir) + if opt and (opt.verbose or option.get("verbose")) then + print("Nix: Found dependency lib dir: " .. libdir .. " (cmake/pkgconfig only)") + end + end end end - - result = merged_result end -- Remove duplicates @@ -380,12 +466,22 @@ function _extract_package_info(store_paths, package_name, opt) result.links = remove_duplicates(result.links) result.libfiles = remove_duplicates(result.libfiles) - -- Return result if we found anything useful + -- Return result only if we found the main package AND have useful information if (#result.includedirs > 0) or (#result.bindirs > 0) or (#result.links > 0) or (#result.linkdirs > 0) then + if opt and (opt.verbose or option.get("verbose")) then + print("Nix: DEBUG: Package info extraction succeeded for '" .. package_name .. "'") + print("Nix: DEBUG: Found " .. #result.includedirs .. " include dirs, " .. + #result.bindirs .. " bin dirs, " .. #result.linkdirs .. " link dirs, " .. + #result.links .. " links") + print("Nix: DEBUG: Main package paths: " .. #main_package_paths .. ", Dependency paths: " .. #dependency_paths) + end return result + else + if opt and (opt.verbose or option.get("verbose")) then + print("Nix: DEBUG: Package info extraction found no useful information for '" .. package_name .. "'") + end + return nil end - - return nil end -- priority 1: nix shell (flake or legacy) @@ -404,10 +500,14 @@ function _find_in_nix_shell(package_name, opt) local store_paths = _parse_store_paths_from_env(build_env_vars, opt) if #store_paths > 0 then + if opt and (opt.verbose or option.get("verbose")) then + print("Nix: Found " .. #store_paths .. " total store paths in nix-shell") + end + local result = _extract_package_info(store_paths, package_name, opt) if result then if opt and (opt.verbose or option.get("verbose")) then - print("Found " .. package_name .. " in nix-shell environment with " .. #store_paths .. " paths") + print("Nix: Found " .. package_name .. " in nix-shell environment") end return result end @@ -628,11 +728,10 @@ function _find_in_nixos_current_system(package_name, opt) end end end - + return nil end - -- main find function function main(name, opt) opt = opt or {} |
