1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
--!A cross-platform build utility based on Lua
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-- Copyright (C) 2015-present, Xmake Open Source Community.
--
-- @author ruki
-- @file merge_staticlib.lua
--
-- imports
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
-- convert all library files to absolute paths before changing directory
local libraryfiles_abs = {}
for _, libraryfile in ipairs(libraryfiles) do
if os.isfile(libraryfile) then
table.insert(libraryfiles_abs, path.absolute(libraryfile))
end
end
if #libraryfiles_abs == 0 then
return
end
local tmpdir = os.tmpfile() .. ".dir"
os.mkdir(tmpdir)
-- check for duplicate object file names and warn
for idx, libraryfile_abs in ipairs(libraryfiles_abs) do
local list = os.iorunv(program, {"-t", libraryfile_abs}, {curdir = tmpdir})
if list then
local seen_files = {}
local duplicates = {}
for _, line in ipairs(list:split("\n")) do
line = line:trim()
if line:endswith(".o") then
if seen_files[line] then
if not duplicates[line] then
duplicates[line] = {}
end
table.insert(duplicates[line], libraryfile_abs)
else
seen_files[line] = true
end
end
end
if not table.empty(duplicates) then
local dup_names = table.keys(duplicates)
wprint("duplicate object file names found in %s: %s (some files may be lost during merge)",
path.filename(libraryfile_abs), table.concat(dup_names, ", "))
end
end
end
-- extract and merge all archives
local objectfiles = {}
for idx, libraryfile_abs in ipairs(libraryfiles_abs) do
-- extract all files from this archive
os.vrunv(program, {"-x", libraryfile_abs}, {curdir = tmpdir})
-- collect extracted object files (duplicate names will be overwritten)
for _, objfile in ipairs(os.files(path.join(tmpdir, "*.o"))) do
-- use relative path (filename only) since ar will run with curdir = tmpdir
table.insert(objectfiles, path.filename(objfile))
end
end
-- create new archive with all object files
if #objectfiles > 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)
-- use relative paths for object files since curdir = tmpdir
os.vrunv(program, table.join("-cr", outputfile_abs, objectfiles), {curdir = tmpdir})
end
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
_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
_merge_for_ar_gnu(target, program, outputfile, libraryfiles, opt)
end
end
-- merge *.a archive libraries for msvc/lib.exe
function _merge_for_msvclib(target, program, outputfile, libraryfiles, opt)
opt = opt or {}
vstool.runv(program, table.join("-nologo", "-out:" .. outputfile, libraryfiles), {envs = opt.runenvs})
end
-- merge *.a archive libraries, @note target may be package.
function main(target, outputfile, libraryfiles)
local program, toolname = target:tool("ar")
if program and toolname then
if toolname:find("ar") then
_merge_for_ar(target, program, outputfile, libraryfiles)
elseif toolname == "link" and target:is_plat("windows") then
local msvc
for _, toolchain_inst in ipairs(target:toolchains()) do
if toolchain_inst:name() == "msvc" then
msvc = toolchain_inst
break
end
end
_merge_for_msvclib(target, (program:gsub("link%.exe", "lib.exe")), outputfile, libraryfiles, {runenvs = msvc and msvc:runenvs()})
else
raise("cannot merge (%s): unknown ar tool %s!", table.concat(libraryfiles, ", "), toolname)
end
else
raise("cannot merge (%s): ar not found!", table.concat(libraryfiles, ", "))
end
end
|