summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2025-11-24 23:08:17 +0800
committerruki <[email protected]>2025-11-24 23:08:17 +0800
commit4198e9bb7a885fb8d18915533b26434bd01e2f67 (patch)
treef95e810b7ee03e641c3eb52799f21c30673a237f
parent49a1737db8d36a9715f71d9cb1acbffe8e7e7faf (diff)
improve to merge archive libs
-rw-r--r--xmake/modules/utils/archive/merge_staticlib.lua113
1 files changed, 93 insertions, 20 deletions
diff --git a/xmake/modules/utils/archive/merge_staticlib.lua b/xmake/modules/utils/archive/merge_staticlib.lua
index 41f256740..bf5c52b19 100644
--- a/xmake/modules/utils/archive/merge_staticlib.lua
+++ b/xmake/modules/utils/archive/merge_staticlib.lua
@@ -22,31 +22,104 @@
import("core.base.option")
import("private.tools.vstool")
+-- merge *.a archive libraries using libtool
+function _merge_for_ar_libtool(target, program, outputfile, libraryfiles, opt)
+ os.vrunv("libtool", table.join("-static", "-o", outputfile, libraryfiles))
+end
+
+-- merge *.a archive libraries using fallback method (extract and repack)
+-- Used for platforms where ar does not support -M option (e.g., Solaris)
+function _merge_for_ar_fallback(target, program, outputfile, libraryfiles, opt)
+ -- we need to handle duplicate object file names by adding prefixes
+ local tmpdir = os.tmpfile() .. ".dir"
+ os.mkdir(tmpdir)
+ local oldir = os.cd(tmpdir)
+ local objfiles = {}
+ local objfile_set = {}
+ for idx, libraryfile in ipairs(libraryfiles) do
+ if os.isfile(libraryfile) then
+ -- list all files in the archive first to avoid overwriting during extraction
+ local ok, list = os.iorunv(program, {"-t", path.absolute(libraryfile)})
+ if ok and list then
+ local file_counter = {}
+ for line in list:gmatch("[^\r\n]+") do
+ line = line:trim()
+ if line:endswith(".o") then
+ local basename = path.basename(line, path.extension(line))
+ local ext = path.extension(line)
+ -- count occurrences of this basename in current library
+ file_counter[basename] = (file_counter[basename] or 0) + 1
+ local counter = file_counter[basename] - 1
+ -- generate unique name with library index prefix to avoid conflicts
+ local unique_name
+ if counter == 0 then
+ unique_name = string.format("lib%d_%s%s", idx, basename, ext)
+ else
+ unique_name = string.format("lib%d_%s_%d%s", idx, basename, counter, ext)
+ end
+ -- ensure global uniqueness by adding counter if needed
+ local global_counter = 0
+ while objfile_set[unique_name] do
+ global_counter = global_counter + 1
+ unique_name = string.format("lib%d_%s_%d_%d%s", idx, basename, counter, global_counter, ext)
+ end
+ objfile_set[unique_name] = true
+ -- extract single file and rename immediately to avoid overwriting
+ os.vrunv(program, {"-x", path.absolute(libraryfile), line})
+ if os.isfile(line) then
+ os.mv(line, unique_name)
+ table.insert(objfiles, unique_name)
+ end
+ end
+ end
+ end
+ end
+ end
+ -- create new archive with all object files
+ if #objfiles > 0 then
+ os.mkdir(path.directory(outputfile))
+ local outputfile_abs = path.absolute(outputfile)
+ -- remove output file if exists to avoid appending
+ os.tryrm(outputfile_abs)
+ -- create new archive with -c (create) and -r (replace/insert)
+ os.vrunv(program, table.join("-cr", outputfile_abs, objfiles))
+ end
+ os.cd(oldir)
+ os.rm(tmpdir)
+end
+
+-- merge *.a archive libraries for GNU ar (using -M interactive mode)
+function _merge_for_ar_gnu(target, program, outputfile, libraryfiles, opt)
+ -- we can't generate directly to outputfile,
+ -- because on windows/ndk, llvm-ar.exe may fail to write with no permission, even though it's a writable temp file.
+ --
+ -- @see https://github.com/xmake-io/xmake/issues/1973
+ local archivefile = target.autogenfile and target:autogenfile((hash.uuid(outputfile):gsub("%-", ""))) .. ".a" or (os.tmpfile() .. ".a")
+ os.mkdir(path.directory(archivefile))
+ local tmpfile = os.tmpfile()
+ local mrifile = io.open(tmpfile, "w")
+ mrifile:print("create %s", archivefile)
+ for _, libraryfile in ipairs(libraryfiles) do
+ mrifile:print("addlib %s", libraryfile)
+ end
+ mrifile:print("save")
+ mrifile:print("end")
+ mrifile:close()
+ os.vrunv(program, {"-M"}, {stdin = tmpfile})
+ os.cp(archivefile, outputfile)
+ os.rm(tmpfile)
+ os.rm(archivefile)
+end
+
-- merge *.a archive libraries for ar
function _merge_for_ar(target, program, outputfile, libraryfiles, opt)
opt = opt or {}
if target:is_plat("macosx", "iphoneos", "watchos", "appletvos") then
- os.vrunv("libtool", table.join("-static", "-o", outputfile, libraryfiles))
+ _merge_for_ar_libtool(target, program, outputfile, libraryfiles, opt)
+ elseif target:is_plat("solaris") then
+ _merge_for_ar_fallback(target, program, outputfile, libraryfiles, opt)
else
- -- we can't generate directly to outputfile,
- -- because on windows/ndk, llvm-ar.exe may fail to write with no permission, even though it's a writable temp file.
- --
- -- @see https://github.com/xmake-io/xmake/issues/1973
- local archivefile = target.autogenfile and target:autogenfile((hash.uuid(outputfile):gsub("%-", ""))) .. ".a" or (os.tmpfile() .. ".a")
- os.mkdir(path.directory(archivefile))
- local tmpfile = os.tmpfile()
- local mrifile = io.open(tmpfile, "w")
- mrifile:print("create %s", archivefile)
- for _, libraryfile in ipairs(libraryfiles) do
- mrifile:print("addlib %s", libraryfile)
- end
- mrifile:print("save")
- mrifile:print("end")
- mrifile:close()
- os.vrunv(program, {"-M"}, {stdin = tmpfile})
- os.cp(archivefile, outputfile)
- os.rm(tmpfile)
- os.rm(archivefile)
+ _merge_for_ar_gnu(target, program, outputfile, libraryfiles, opt)
end
end