summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzzbaron <[email protected]>2025-09-16 12:27:02 -0400
committerzzbaron <[email protected]>2025-09-16 12:27:02 -0400
commitc6946e8f3dae13c3d8a94dfaea850e4ea38a075e (patch)
tree1cd54976928e4b23213e45f020dd14672610cf61
parente6c092279a2306dc56567d034013c5f0ff4e4b15 (diff)
added nix devshell support with package validation
-rw-r--r--xmake/modules/package/manager/nix/find_package.lua216
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