-
Notifications
You must be signed in to change notification settings - Fork 8
/
compilationunit.lua
341 lines (271 loc) · 9.12 KB
/
compilationunit.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
--
-- always include _preload so that the module works even when not embedded.
--
if premake.extensions == nil or premake.extensions.compilationunit == nil then
include ( "_preload.lua" )
end
--
-- define the extension
--
premake.extensions.compilationunit = {
--
-- these are private, do not touch
--
compilationunitname = "__compilation_unit__",
numcompilationunits = 8,
compilationunits = {}
}
--
-- This method overrides premake.oven.bakeFiles method. We use it to add the compilation units
-- to the project, and then generate and fill them.
--
function premake.extensions.compilationunit.customBakeFiles(base, prj)
-- do nothing for external projects
if prj.external == true then
return base(prj)
end
local project = premake.project
local cu = premake.extensions.compilationunit
-- first step, gather compilation units.
-- this needs to take care of the case where the compilation units are generated in a folder from
-- the project (we need to skip them in this case to avoid recursively adding them each time we
-- run Premake.
for cfg in project.eachconfig(prj) do
-- remove the previous compilation units
for i = #cfg.files, 1, -1 do
if cu.isCompilationUnit(cfg, cfg.files[i]) then
table.remove(cfg.files, i)
end
end
-- stop there when compilation units are disabled
if prj.compilationunitenabled == true then
-- initialize the compilation unit structure for this config
cu.compilationunits[cfg] = {}
-- the indices of the files that must be included in the compilation units
local sourceindexforcu = {}
-- store the list of files for a later building of the actual compilation unit files
for i = #cfg.files, 1, -1 do
local filename = cfg.files[i]
if cu.isIncludedInCompilationUnit(cfg, filename) == true then
table.insert(cu.compilationunits[cfg], filename)
table.insert(sourceindexforcu, i)
end
end
-- remove the original source files from the project
if cfg.compilationunitsonly then
table.foreachi(sourceindexforcu, function(i)
table.remove(cfg.files, i)
end)
end
-- store the compilation unit folder in the config
if cfg._compilationUnitDir == nil then
cfg._compilationUnitDir = cu.getCompilationUnitDir(cfg)
end
-- add the compilation units for premake.
-- note: avoid generating empty compilation units if we have less files in the project
-- than the number of compilation units.
local count = math.min(#cu.compilationunits[cfg], cu.numcompilationunits)
for i = 1, count do
table.insert(cfg.files, path.join(cfg._compilationUnitDir, cu.getCompilationUnitName(cfg, i)))
end
end
end
-- we removed any potential previous compilation units, early out if compilation units are not enabled
if prj.compilationunitenabled ~= true then
return base(prj)
end
-- second step loop through the configs and generate the compilation units
for config, files in pairs(cu.compilationunits) do
-- create the units
local units = {}
for i = 1, cu.numcompilationunits do
local content = ""
-- add header if needed
if config.compilationunitheader ~= nil then
content = content .. config.compilationunitheader .. "\n\n"
end
-- add pch if needed
if config.pchheader ~= nil then
content = content .. "#include \"" .. config.pchheader .. "\"\n\n"
end
-- add the unit
table.insert(units, {
filename = path.join(config._compilationUnitDir, cu.getCompilationUnitName(config, i)),
content = content
})
end
-- add files in the cpp unit
local index = 1
for _, filename in ipairs(files) do
-- compute the relative path of the original file, to add the #include statement
-- in the compilation unit
local relativefilename = path.getrelative(path.getdirectory(units[index].filename), path.getdirectory(filename))
relativefilename = path.join(relativefilename, path.getname(filename))
units[index].content = units[index].content .. "#include \"" .. relativefilename .. "\"\n"
index = (index % cu.numcompilationunits) + 1
end
-- write units
for _, unit in ipairs(units) do
-- get the content of the file, if it already exists
local file = io.open(unit.filename, "r")
local content = ""
if file ~= nil then
content = file:read("*all")
file:close()
end
-- overwrite only if the content changed
if content ~= unit.content then
file = assert(io.open(unit.filename, "w"))
file:write(unit.content)
file:close()
end
end
end
return base(prj)
end
--
-- This method overrides premake.fileconfig.addconfig and adds a file configuration object
-- for each file, on each configuration. We use it to disable compilation of non-compilation
-- units files.
--
function premake.extensions.compilationunit.customAddFileConfig(base, fcfg, cfg)
-- get the addon
local cu = premake.extensions.compilationunit
-- call the base method to add the file config
base(fcfg, cfg)
-- do nothing else if the compilation units are not enabled for this project
if cfg.compilationunitenabled == nil or cu.compilationunits[cfg] == nil then
return
end
-- get file name and config
local filename = fcfg.abspath
local config = premake.fileconfig.getconfig(fcfg, cfg)
-- if the compilation units were explicitely disabled for this file, remove it
-- from the compilation units and stop here
if config.compilationunitenabled == false then
local i = table.indexof(cu.compilationunits[cfg], filename)
if i ~= nil then
table.remove(cu.compilationunits[cfg], i)
end
return
end
-- if a file will be included in the compilation units, disable it
if cu.isIncludedInCompilationUnit(cfg, filename) == true and cu.isCompilationUnit(cfg, filename) == false then
config.flags.ExcludeFromBuild = true
end
end
--
-- Checks if a file should be included in the compulation units.
--
-- @param cfg
-- The active configuration
-- @param filename
-- The filename
-- @return
-- true if the file should be included in compilation units, false otherwise
--
function premake.extensions.compilationunit.isIncludedInCompilationUnit(cfg, filename)
-- only handle source files
if path.iscfile(filename) == false and path.iscppfile(filename) == false then
return false
end
local cu = premake.extensions.compilationunit
-- ignore PCH files
if cu.isPCHSource(cfg, filename) == true then
return false
end
-- it's ok !
return true
end
--
-- Get the compilation unit output directory
--
-- @param cfg
-- The input configuration
--
function premake.extensions.compilationunit.getCompilationUnitDir(cfg)
-- in this order:
-- - check if compilationunitdir is used
-- - if not, if we have an objdir set, use it
-- - if not, re-create the obj dir like the default Premake one.
local dir = ""
if cfg.compilationunitdir then
dir = cfg.compilationunitdir
else
if cfg.objdir then
return cfg.objdir
else
dir = path.join(cfg.project.location, "obj")
end
end
if cfg.platform then
dir = path.join(dir, cfg.platform)
end
dir = path.join(dir, cfg.buildcfg)
dir = path.join(dir, cfg.project.name)
return path.getabsolute(dir)
end
--
-- Get the name of a compilation unit
--
-- @param cfg
-- The configuration for which we want the compilation unit's filename
-- @param index
-- The index of the compilation unit
-- @return
-- The name of the file.
--
function premake.extensions.compilationunit.getCompilationUnitName(cfg, index, shortName)
local language = cfg.language
local extension = nil
if cfg.compilationunitextensions ~= nil then
extension = cfg.compilationunitextensions[language]
end
if extension == nil then
extension = iif(language == "C", ".c", ".cpp")
end
return premake.extensions.compilationunit.compilationunitname .. index .. extension
end
--
-- Checks if an absolute filename is a compilation unit..
--
-- @param cfg
-- The current configuration
-- @param absfilename
-- The absolute filename of the file to check
-- @return
-- true if the file is a compilation unit, false otherwise
--
function premake.extensions.compilationunit.isCompilationUnit(cfg, absfilename)
return path.getname(absfilename):startswith(premake.extensions.compilationunit.compilationunitname)
end
--
-- Checks if a file is the PCH source.
--
-- @param cfg
-- The current configuration
-- @param absfilename
-- The absolute filename of the file to check
-- @return
-- true if the file is the PCH source, false otherwise
--
function premake.extensions.compilationunit.isPCHSource(cfg, absfilename)
return cfg.pchsource ~= nil and cfg.pchsource:lower() == absfilename:lower()
end
--
-- If the compilationunit option was used, activate the addon
--
if _OPTIONS["compilationunit"] ~= nil then
local cu = premake.extensions.compilationunit
-- store the number of compilation units
cu.numcompilationunits = tonumber(_OPTIONS["compilationunit"])
if cu.numcompilationunits == nil then
error("value for option 'compilationunit' must be a valid number")
end
-- setup the overrides
premake.override(premake.oven, "bakeFiles", cu.customBakeFiles)
premake.override(premake.fileconfig, "addconfig", cu.customAddFileConfig)
else
-- still need this to avoid including compilation units of a previous build
premake.override(premake.oven, "bakeFiles", premake.extensions.compilationunit.customBakeFiles)
end