diff options
| author | zzbaron <[email protected]> | 2025-09-16 12:27:02 -0400 |
|---|---|---|
| committer | zzbaron <[email protected]> | 2025-09-16 12:27:02 -0400 |
| commit | c6946e8f3dae13c3d8a94dfaea850e4ea38a075e (patch) | |
| tree | 1cd54976928e4b23213e45f020dd14672610cf61 | |
| parent | e6c092279a2306dc56567d034013c5f0ff4e4b15 (diff) | |
added nix devshell support with package validation
| -rw-r--r-- | xmake/modules/package/manager/nix/find_package.lua | 216 |
1 files changed, 183 insertions, 33 deletions
diff --git a/xmake/modules/package/manager/nix/find_package.lua b/xmake/modules/package/manager/nix/find_package.lua index 579156153..7bfd6282d 100644 --- a/xmake/modules/package/manager/nix/find_package.lua +++ b/xmake/modules/package/manager/nix/find_package.lua @@ -24,13 +24,114 @@ import("lib.detect.find_tool") import("private.core.base.is_cross") import("package.manager.pkgconfig.find_package", {alias = "find_package_from_pkgconfig"}) +-- check if we're in a nix-shell environment +function _in_nix_shell() + local in_nix_shell = os.getenv("IN_NIX_SHELL") + return in_nix_shell == "pure" or in_nix_shell == "impure" +end + +-- extract store paths from nix environment variables with better filtering +function _extract_nix_store_paths(env_var_name, env_var_value) + local paths = {} + local seen = {} + + if env_var_value == "" then + return paths + end + + -- Handle different environment variable formats + local separators = { + PATH = ":", + PKG_CONFIG_PATH = ":", + LIBRARY_PATH = ":", + LD_LIBRARY_PATH = ":", + C_INCLUDE_PATH = ":", + CPLUS_INCLUDE_PATH = ":", + NIX_LDFLAGS = "%s", -- space separated, may contain -L flags + NIX_CFLAGS_COMPILE = "%s" -- space separated, may contain -I flags + } + + local separator = separators[env_var_name] or ":" + local pattern = separator == ":" and "[^:]+" or "[^%s]+" + + for item in env_var_value:gmatch(pattern) do + local clean_item = item + + -- Remove flag prefixes for compiler/linker flags + if env_var_name == "NIX_LDFLAGS" then + clean_item = item:gsub("^%-L", "") + elseif env_var_name == "NIX_CFLAGS_COMPILE" then + clean_item = item:gsub("^%-[iI]system%s*", ""):gsub("^%-I", "") + end + + if clean_item:startswith("/nix/store/") then + local store_path = clean_item:match("(/nix/store/[^/]+)") + if store_path and not seen[store_path] then + seen[store_path] = true + table.insert(paths, store_path) + end + end + end + + return paths +end + +-- get current shell buildInputs from environment with improved detection +function _get_shell_build_inputs() + local all_paths = {} + local seen = {} + + if not _in_nix_shell() then + return all_paths + end + + + -- Environment variables to check for nix store paths + local env_vars = { + "PATH", + "PKG_CONFIG_PATH", + "LIBRARY_PATH", + "LD_LIBRARY_PATH", + "C_INCLUDE_PATH", + "CPLUS_INCLUDE_PATH", + "NIX_LDFLAGS", + "NIX_CFLAGS_COMPILE" + } + + for _, env_var in ipairs(env_vars) do + local env_value = os.getenv(env_var) or "" + local paths = _extract_nix_store_paths(env_var, env_value) + + for _, path in ipairs(paths) do + if not seen[path] then + seen[path] = true + table.insert(all_paths, path) + end + end + end + + return all_paths +end + -- get all nix store paths currently available in environment function _get_available_nix_paths() local paths = {} local seen = {} - -- Get paths from environment PATH + -- First, get paths from current shell if we're in nix-shell + if _in_nix_shell() then + local shell_paths = _get_shell_build_inputs() + for _, path in ipairs(shell_paths) do + if not seen[path] then + seen[path] = true + table.insert(paths, path) + end + end + end + + -- Get paths from environment PATH (additional check) local env_path = os.getenv("PATH") or "" + for dir in env_path:gmatch("[^:]+") do if dir:startswith("/nix/store/") then local store_path = dir:match("(/nix/store/[^/]+)") @@ -43,14 +144,13 @@ function _get_available_nix_paths() -- Get paths from common Nix environment locations local env_locations = { - os.getenv("NIX_PROFILES") or "", - (os.getenv("HOME") or "") .. "/.nix-profile", "/nix/var/nix/profiles/default", - "/run/current-system/sw" -- NixOS system packages + (os.getenv("HOME") or "") .. "/.nix-profile" } for _, location in ipairs(env_locations) do if location ~= "" and os.isdir(location) then + -- Check if it's a symlink to store path local target = try {function() return os.iorunv("readlink", {"-f", location}):trim() @@ -81,16 +181,66 @@ function _get_available_nix_paths() end end end - + return paths end --- find package in a specific nix store path +-- check if a store path actually contains the requested package +function _validate_package_in_store_path(store_path, name) + + -- Check if the store path name contains the package name + local store_name = path.basename(store_path):lower() + local search_name = name:lower() + + -- Look for exact match, or package name in the store path + local name_match = store_name:find(search_name, 1, true) or + store_name:find(search_name:gsub("%-", "%%-")) -- handle hyphens + + if name_match then + return true + end + + -- Check for libraries with the package name + local libdir = path.join(store_path, "lib") + if os.isdir(libdir) then + local libfiles = os.files(path.join(libdir, "lib" .. name .. ".*")) + if #libfiles > 0 then + for _, libfile in ipairs(libfiles) do + end + return true + end + end + + -- Check for pkg-config files + local pkgconfigdirs = { + path.join(store_path, "lib", "pkgconfig"), + path.join(store_path, "share", "pkgconfig") + } + + for _, pcdir in ipairs(pkgconfigdirs) do + if os.isdir(pcdir) then + local pcfiles = os.files(path.join(pcdir, name .. ".pc")) + if #pcfiles > 0 then + return true + end + end + end + + return false +end + +-- find package in a specific nix store path with validation function _find_in_store_path(store_path, name) + if not os.isdir(store_path) then return nil end + -- First validate that this store path actually contains our package + if not _validate_package_in_store_path(store_path, name) then + return nil + end + local result = {} -- Find include directories @@ -106,11 +256,12 @@ function _find_in_store_path(store_path, name) result.links = {} result.libfiles = {} - -- Scan for library files + -- Scan for library files related to our package local libfiles = os.files(path.join(libdir, "*.so*"), path.join(libdir, "*.a"), path.join(libdir, "*.dylib*")) + local found_libs = {} for _, libfile in ipairs(libfiles) do local filename = path.filename(libfile) local linkname = filename:match("^lib(.+)%.so") or @@ -118,8 +269,11 @@ function _find_in_store_path(store_path, name) filename:match("^lib(.+)%.dylib") if linkname then - table.insert(result.links, linkname) - table.insert(result.libfiles, libfile) + if linkname == name or linkname:find(name) then + table.insert(result.links, linkname) + table.insert(result.libfiles, libfile) + found_libs[linkname] = true + end if filename:endswith(".a") then result.static = true @@ -151,7 +305,7 @@ function _find_in_store_path(store_path, name) end -- Return result if we found anything useful - if result.includedirs or result.linkdirs then + if result.includedirs or (result.links and #result.links > 0) then return result end @@ -208,37 +362,33 @@ function main(name, opt) -- Get all available Nix store paths local nix_paths = _get_available_nix_paths() - -- Search through available paths first (unless we're forced to build) - if #nix_paths > 0 and not force_nix then - for _, store_path in ipairs(nix_paths) do + -- Search through available paths first (prioritize shell environment) + if #nix_paths > 0 then + for i, store_path in ipairs(nix_paths) do local result = _find_in_store_path(store_path, actual_name) if result then - if opt.verbose or option.get("verbose") then - print("Found " .. actual_name .. " in: " .. store_path) - end return result end end end - -- If not found in available paths or forced to build, try building - local storepath = nil - - -- Try modern nix first - storepath = _try_modern_nix_build(actual_name) - - -- Fallback to legacy nix-build - if not storepath then - storepath = _try_legacy_nix_build(actual_name) - end - - if storepath and os.isdir(storepath) then - local result = _find_in_store_path(storepath, actual_name) - if result then - if opt.verbose or option.get("verbose") then - print("Built and found " .. actual_name .. " in: " .. storepath) + -- If not found in available paths and not in nix-shell, try building + if not _in_nix_shell() or force_nix then + local storepath = nil + + -- Try modern nix first + storepath = _try_modern_nix_build(actual_name) + + -- Fallback to legacy nix-build + if not storepath then + storepath = _try_legacy_nix_build(actual_name) + end + + if storepath and os.isdir(storepath) then + local result = _find_in_store_path(storepath, actual_name) + if result then + return result end - return result end end |
