summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzzbaron <[email protected]>2025-09-21 23:48:21 -0400
committerzzbaron <[email protected]>2025-09-21 23:48:21 -0400
commitf1a64ad50e22c55a08a2553669aba152f70d9eb6 (patch)
tree9a66ee1533ad6c91fddec50b9871048d6e0f2698
parent32468fdcccd6c29cfc4ac29e078da14bd27fc2d2 (diff)
nix: improve package filtering and search accuracy
-rw-r--r--xmake/modules/package/manager/nix/find_package.lua243
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 {}