-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathbuilder.lua
323 lines (301 loc) · 10.6 KB
/
builder.lua
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
-- 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, TBOOX Open Source Group.
--
import("core.base.graph")
import("core.base.hashset")
import("core.project.config")
import("core.platform.platform")
-- source: ://github.com/xmake-io/xmake/blob/master/xmake/core/tool/builder.lua
-- builder: get the extra configuration from value
function _extraconf(extras, value)
local extra = extras
if extra then
if type(value) == "table" then
extra = extra[table.concat(value, "_")]
else
extra = extra[value]
end
end
return extra
end
-- builder: add items from config
function _add_items_from_config(items, name, opt)
local values = config.get(name)
if values and name:endswith("dirs") then
values = path.splitenv(values)
end
if values then
table.insert(items, {
name = name,
values = table.wrap(values),
check = opt.check,
multival = opt.multival,
mapper = opt.mapper})
end
end
-- builder: add items from toolchain
function _add_items_from_toolchain(items, name, opt)
local values
local target = opt.target
if target and target:type() == "target" then
values = target:toolconfig(name)
else
values = platform.toolconfig(name)
end
if values then
table.insert(items, {
name = name,
values = table.wrap(values),
check = opt.check,
multival = opt.multival,
mapper = opt.mapper})
end
end
-- builder: add items from option
function _add_items_from_option(items, name, opt)
local values
local target = opt.target
if target then
values = target:get(name)
end
if values then
table.insert(items, {
name = name,
values = table.wrap(values),
check = opt.check,
multival = opt.multival,
mapper = opt.mapper})
end
end
-- builder: add items from target
function _add_items_from_target(items, name, opt)
local target = opt.target
if target then
local result, sources = target:get_from(name, "*")
if result then
for idx, values in ipairs(result) do
local source = sources[idx]
local extras = target:extraconf_from(name, source)
values = table.wrap(values)
if values and #values > 0 then
table.insert(items, {
name = name,
values = values,
extras = extras,
check = opt.check,
multival = opt.multival,
mapper = opt.mapper})
end
end
end
end
end
-- builder: sort links of items
function _sort_links_of_items(items, opt)
opt = opt or {}
local sortlinks = false
local makegroups = false
local linkorders = table.wrap(opt.linkorders)
if #linkorders > 0 then
sortlinks = true
end
local linkgroups = table.wrap(opt.linkgroups)
local linkgroups_set = hashset.new()
if #linkgroups > 0 then
makegroups = true
for _, linkgroup in ipairs(linkgroups) do
for _, link in ipairs(linkgroup) do
linkgroups_set:insert(link)
end
end
end
-- get all links
local links = {}
local linkgroups_map = {}
local extras_map = {}
local link_mapper
local framework_mapper
local linkgroup_mapper
if sortlinks or makegroups then
local linkitems = {}
table.remove_if(items, function (_, item)
local name = item.name
local removed = false
if name == "links" or name == "syslinks" then
link_mapper = item.mapper
removed = true
table.insert(linkitems, item)
elseif name == "frameworks" then
framework_mapper = item.mapper
removed = true
table.insert(linkitems, item)
elseif name == "linkgroups" then
linkgroup_mapper = item.mapper
removed = true
table.insert(linkitems, item)
end
return removed
end)
-- @note table.remove_if will traverse backwards,
-- we need to fix the initial link order first to make sure the syslinks are in the correct order
linkitems = table.reverse(linkitems)
for _, item in ipairs(linkitems) do
local name = item.name
for _, value in ipairs(item.values) do
if name == "links" or name == "syslinks" then
if not linkgroups_set:has(value) then
table.insert(links, value)
end
elseif name == "frameworks" then
table.insert(links, "framework::" .. value)
elseif name == "linkgroups" then
local extras = item.extras
local extra = _extraconf(extras, value)
local key = extra and extra.name or tostring(value)
table.insert(links, "linkgroup::" .. key)
linkgroups_map[key] = value
extras_map[key] = extras
end
end
end
links = table.reverse_unique(links)
end
-- sort sublinks
if sortlinks then
local gh = graph.new(true)
local from
local original_deps = {}
for _, link in ipairs(links) do
local to = link
if from and to then
original_deps[from] = to
end
from = to
end
-- we need remove cycle in original links
-- e.g.
--
-- case1:
-- original_deps: a -> b -> c -> d -> e
-- new deps: e -> b
-- graph: a -> b -> c -> d e (remove d -> e, add d -> nil)
-- /|\ |
-- --------------
--
-- case2:
-- original_deps: a -> b -> c -> d -> e
-- new deps: b -> a
--
-- ---------
-- | \|/
-- graph: a b -> c -> d -> e (remove a -> b, add a -> c)
-- /|\ |
-- ----
--
local function remove_cycle_in_original_deps(f, t)
local k
local v = t
while v ~= f do
k = v
v = original_deps[v]
if v == nil then
break
end
end
if v == f and k ~= nil then
-- break the original from node, link to next node
-- e.g.
-- case1: d -x-> e, d -> nil, k: d, f: e
-- case2: a -x-> b, a -> c, k: a, f: b
original_deps[k] = original_deps[f]
end
end
local links_set = hashset.from(links)
for _, linkorder in ipairs(linkorders) do
local from
for _, link in ipairs(linkorder) do
if links_set:has(link) then
local to = link
if from and to then
remove_cycle_in_original_deps(from, to)
gh:add_edge(from, to)
end
from = to
end
end
end
for k, v in pairs(original_deps) do
gh:add_edge(k, v)
end
if not gh:empty() then
local cycle = gh:find_cycle()
if cycle then
utils.warning("cycle links found in add_linkorders(): %s", table.concat(cycle, " -> "))
end
links = gh:topological_sort()
end
end
-- re-generate links to items list
if sortlinks or makegroups then
for _, link in ipairs(links) do
if link:startswith("framework::") then
link = link:sub(12)
table.insert(items, {name = "frameworks", values = table.wrap(link), check = false, multival = false, mapper = framework_mapper})
elseif link:startswith("linkgroup::") then
local key = link:sub(12)
local values = linkgroups_map[key]
local extras = extras_map[key]
table.insert(items, {name = "linkgroups", values = table.wrap(values), extras = extras, check = false, multival = false, mapper = linkgroup_mapper})
else
table.insert(items, {name = "links", values = table.wrap(link), check = false, multival = false, mapper = link_mapper})
end
end
end
end
-- get the links in the correct order
function orderlinks(target)
assert(target:is_binary(), "linkorders() requires a binary target")
local linkorders = {}
local linkgroups = {}
local values = target:get_from("linkorders", "*")
if values then
for _, value in ipairs(values) do
table.join2(linkorders, value)
end
end
values = target:get_from("linkgroups", "*")
if values then
for _, value in ipairs(values) do
table.join2(linkgroups, value)
end
end
local items = {}
_add_items_from_target(items, "linkgroups", {target = target})
_add_items_from_config(items, "links", {target = target})
_add_items_from_target(items, "links", {target = target})
_add_items_from_option(items, "links", {target = target})
_add_items_from_toolchain(items, "links", {target = target})
_add_items_from_config(items, "frameworks", {target = target})
_add_items_from_target(items, "frameworks", {target = target})
_add_items_from_option(items, "frameworks", {target = target})
_add_items_from_toolchain(items, "frameworks", {target = target})
_add_items_from_config(items, "syslinks", {target = target})
_add_items_from_target(items, "syslinks", {target = target})
_add_items_from_option(items, "syslinks", {target = target})
_add_items_from_toolchain(items, "syslinks", {target = target})
if #linkorders > 0 or #linkgroups > 0 then
_sort_links_of_items(items, {linkorders = linkorders, linkgroups = linkgroups})
end
return items
end