forked from premake/premake-xcode
-
Notifications
You must be signed in to change notification settings - Fork 1
/
xcode_utils.lua
366 lines (309 loc) · 9.9 KB
/
xcode_utils.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
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
--
-- xcode6_utils.lua
-- Define the Apple XCode action and support functions.
-- Copyright (c) 2015 Blizzard Entertainment
--
local p = premake
local api = p.api
local configset = p.configset
local context = p.context
local detoken = p.detoken
local xcode6 = p.modules.xcode_blizzard
local project = p.project
local workspace = p.workspace
function xcode6.newid(...)
local name = table.concat({...}, ';');
return string.sub(name:sha1(), 1, 24)
end
function xcode6.getBuildCategory(filename)
if not xcode6._buildCategories then
local categories = {
[".a"] = "Frameworks",
[".app"] = "Applications",
[".c"] = "Sources",
[".cc"] = "Sources",
[".cpp"] = "Sources",
[".cxx"] = "Sources",
[".dylib"] = "Frameworks",
[".framework"] = "Frameworks",
[".m"] = "Sources",
[".metal"] = "Sources",
[".mig"] = "Sources",
[".mm"] = "Sources",
[".strings"] = "Resources",
[".nib"] = "Resources",
[".xib"] = "Resources",
[".icns"] = "Resources",
[".s"] = "Sources",
[".storyboard"] = "Resources",
[".txt"] = "Resources"
}
for rule in p.global.eachRule() do
for _, v in ipairs(rule.fileextension) do
categories[v] = "Sources"
end
end
xcode6._buildCategories = categories
end
return xcode6._buildCategories[string.lower(path.getextension(filename))]
end
function xcode6.getProductType(prj)
if prj.kind == "SharedLib" and prj.sharedlibtype then
local types = {
OSXBundle = "com.apple.product-type.bundle",
OSXFramework = "com.apple.product-type.framework",
}
return types[prj.sharedlibtype]
else
local types = {
ConsoleApp = iif(os.istarget("macosx"), "com.apple.product-type.tool", "com.apple.product-type.application"),
WindowedApp = "com.apple.product-type.application",
StaticLib = "com.apple.product-type.library.static",
SharedLib = "com.apple.product-type.library.dynamic",
}
return types[prj.kind]
end
end
function xcode6.getTargetType(prj)
local types = {
ConsoleApp = "\"compiled.mach-o.executable\"",
WindowedApp = "wrapper.application",
StaticLib = "archive.ar",
SharedLib = "\"compiled.mach-o.dylib\"",
}
return types[prj.kind]
end
function xcode6.getTargetName(prj, cfg)
if prj.external then
return cfg.project.name
end
return cfg.buildtarget.bundlename ~= "" and cfg.buildtarget.bundlename or cfg.buildtarget.name;
end
function xcode6.isItemResource(project, node)
local res;
if project and project.xcodebuildresources and type(project.xcodebuildresources) == "table" then
res = project.xcodebuildresources
end
local function checkItemInList(item, list)
if item and list and type(list) == "table" then
for _,v in pairs(list) do
if string.find(item, v) then
return true
end
end
end
return false
end
return checkItemInList(node.path, res);
end
function xcode6.getFrameworkDirs(cfg)
local done = {}
local dirs = {}
if cfg.project then
table.foreachi(cfg.links, function(link)
if link:find('.framework$') and path.isabsolute(link) then
local dir = workspace.getrelative(cfg.workspace, path.getdirectory(link))
if not done[dir] then
table.insert(dirs, dir)
done[dir] = true
end
end
end)
end
local frameworkdirs = xcode6.fetchlocal(cfg, 'xcode_frameworkdirs')
if frameworkdirs then
table.foreachi(frameworkdirs, function(dir)
if path.isabsolute(dir) then
dir = workspace.getrelative(cfg.workspace, dir)
end
if not done[dir] then
table.insert(dirs, dir)
done[dir] = true
end
end)
end
return dirs
end
local escapeSpecialChars = {
['\n'] = '\\n',
['\r'] = '\\r',
['\t'] = '\\t',
}
local function escapeChar(c)
return escapeSpecialChars[c] or '\\'..c
end
local function escapeArg(value)
value = value:gsub('[\'"\\\n\r\t ]', escapeChar)
return value
end
function xcode6.esc(value)
value = value:gsub('["\\\n\r\t]', escapeChar)
return value
end
function xcode6.quoted(value)
value = value..''
if not value:match('^[%a%d_./]+$') then
value = '"' .. xcode6.esc(value) .. '"'
end
return value
end
function xcode6.filterEmpty(dirs)
return table.translate(dirs, function(val)
if val and #val > 0 then
return val
else
return nil
end
end)
end
function xcode6.printSetting(level, name, value)
if type(value) == 'function' then
value(level, name)
elseif type(value) ~= 'table' then
_p(level, '%s = %s;', xcode6.quoted(name), xcode6.quoted(value))
elseif #value == 1 then
_p(level, '%s = %s;', xcode6.quoted(name), xcode6.quoted(value[1]))
elseif #value > 1 then
_p(level, '%s = (', xcode6.quoted(name))
for _, item in ipairs(value) do
_p(level + 1, '%s,', xcode6.quoted(item))
end
_p(level, ');')
end
end
function xcode6.printSettingsTable(level, settings)
-- Maintain alphabetic order to be consistent
local keys = table.keys(settings)
table.sort(keys)
for _, k in ipairs(keys) do
xcode6.printSetting(level, k, settings[k])
end
end
function xcode6.fetchlocal(cfg, key)
-- if cfg is nil just exit.
if cfg == nil then
return nil
end
-- If there's no field definition, just return the raw value.
local field = p.field.get(key)
if not field then
return cfg[key]
end
local wks = cfg.workspace
local prj = cfg.project
-- If it's a workspace config, just fetch the value normally.
if not prj then
local value = cfg[key]
return value, value, { } -- everything is new
end
-- If it's a project config, then we only want values specified at the project or configuration level,
-- or with an explicit filter for the project or configuration. If it's a file config, then we only
-- want values with an explicit filter for the file.
local value = nil
local inserted = nil
local removed = nil
local parentcfg = cfg.buildcfg and table.filter(wks.configs, function(_cfg) return _cfg.name == cfg.name end)[1]
local parent = parentcfg or (cfg.terms.files and prj or wks)
if p.field.removes(field) then
value = cfg[key]
if value then
if cfg.abspath then
-- files don't automatically inherit settings from projects, so handle that here
value = p.field.merge(field, xcode6.fetchfiltered(parent, field, cfg.terms), value)
end
local parentvalue = parent[key]
inserted = { }
removed = { }
for _, v in ipairs(parentvalue) do
if not value[v] then
table.insert(removed, v)
end
end
for _, v in ipairs(value) do
if not parentvalue[v] then
table.insert(inserted, v)
end
end
end
elseif p.field.merges(field) then
value = cfg[key]
if value then
if cfg.abspath then
-- files don't automatically inherit settings from projects, so handle that here
value = p.field.merge(field, xcode6.fetchfiltered(parent, field, cfg.terms), value)
end
local slnvalue = context.fetchvalue(parent, key)
local parentvalue = xcode6.fetchfiltered(cfg, field, cfg.terms)
inserted = { }
for _, v in ipairs(parentvalue) do
if not slnvalue[v] then
table.insert(inserted, v)
end
end
inserted = p.field.merge(field, inserted, context.fetchvalue(prj, key, true))
inserted = p.field.merge(field, inserted, xcode6.fetchfiltered(cfg, field, cfg.terms, prj))
inserted = p.field.merge(field, inserted, context.fetchvalue(cfg, key, true))
removed = { }
end
else
value = context.fetchvalue(cfg, key, true)
if value == nil then
value = xcode6.fetchfiltered(cfg, field, cfg.terms, prj)
if value == nil then
value = context.fetchvalue(prj, key, true)
if value == nil then
local parentvalue = xcode6.fetchfiltered(cfg, field, cfg.terms)
local slnvalue = context.fetchvalue(parent, key)
if slnvalue ~= parentvalue then
value = parentvalue
end
end
end
end
end
return value, inserted, removed
end
function xcode6.fetchfiltered(cfg, field, terms, ctx)
return configset.fetch(cfg._cfgset, field, terms, cfg, ctx and ctx._cfgset)
end
function xcode6.resolveShellScript(wks, prj, cmd)
local userDefinedCommands = os.translateCommandsAndPaths(cmd, prj.basedir, wks.location)
return 'PATH=$EXECUTABLE_PATHS:$PATH\n' .. userDefinedCommands
end
function xcode6.buildOutputsEnvironment(rule)
local pathVars = p.rule.createPathVars(rule, "$(%s)")
pathVars["file.basename"] = { absolute = false, token = "$(INPUT_FILE_BASE)" }
pathVars["file.abspath"] = { absolute = true, token = "$(INPUT_FILE_PATH)" }
pathVars["file.relpath"] = { absolute = true, token = "$(INPUT_FILE_PATH)" }
pathVars["file.path"] = { absolute = true, token = "$(INPUT_FILE_PATH)" }
pathVars["file.directory"] = { absolute = true, token = "$(INPUT_FILE_DIR)" }
pathVars["file.reldirectory"] = { absolute = true, token = "$(INPUT_FILE_DIR)" }
return context.extent(rule, { pathVars = pathVars })
end
function xcode6.buildCommandsEnvironment(rule)
local pathVars = {}
pathVars["file.basename"] = { absolute = false, token = "$INPUT_FILE_BASE" }
pathVars["file.abspath"] = { absolute = true, token = "$INPUT_FILE_PATH" }
pathVars["file.relpath"] = { absolute = true, token = "$INPUT_FILE_PATH" }
pathVars["file.path"] = { absolute = true, token = "$INPUT_FILE_PATH" }
pathVars["file.directory"] = { absolute = true, token = "$INPUT_FILE_DIR" }
pathVars["file.reldirectory"] = { absolute = true, token = "$INPUT_FILE_DIR" }
local environ = p.rule.createEnvironment(rule, "$%s")
environ.pathVars = pathVars
return context.extent(rule, environ)
end
function xcode6.path(wks, prefix, filename)
if type(filename) == "table" then
local result = {}
for i, name in ipairs(filename) do
if name and #name > 0 then
table.insert(result, xcode6.path(wks, prefix, name))
end
end
return result
else
if filename then
return path.join(prefix, path.getrelative(wks.location, filename))
end
end
end