|
dvander@65
|
1 |
# vim: set ts=8 sts=2 sw=2 tw=99 et: |
|
dvander@72
|
2 |
# |
|
dvander@72
|
3 |
# This file is part of AMBuild. |
|
dvander@72
|
4 |
# |
|
dvander@72
|
5 |
# AMBuild is free software: you can redistribute it and/or modify |
|
dvander@72
|
6 |
# it under the terms of the GNU General Public License as published by |
|
dvander@72
|
7 |
# the Free Software Foundation, either version 3 of the License, or |
|
dvander@72
|
8 |
# (at your option) any later version. |
|
dvander@72
|
9 |
# |
|
dvander@72
|
10 |
# AMBuild is distributed in the hope that it will be useful, |
|
dvander@72
|
11 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
dvander@72
|
12 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
dvander@72
|
13 |
# GNU General Public License for more details. |
|
dvander@72
|
14 |
# |
|
dvander@72
|
15 |
# You should have received a copy of the GNU General Public License |
|
dvander@72
|
16 |
# along with AMBuild. If not, see <http://www.gnu.org/licenses/>. |
|
dvander@65
|
17 |
from __future__ import print_function |
|
dvander@65
|
18 |
import subprocess |
|
dvander@65
|
19 |
import re, os, copy |
|
dvander@144
|
20 |
from ambuild2 import util |
|
dvander@65
|
21 |
|
|
dvander@65
|
22 |
class Vendor(object): |
|
dvander@67
|
23 |
def __init__(self, name, version, behavior, command, objSuffix): |
|
dvander@65
|
24 |
self.name = name |
|
dvander@65
|
25 |
self.version = version |
|
dvander@67
|
26 |
self.behavior = behavior |
|
dvander@65
|
27 |
self.command = command |
|
dvander@65
|
28 |
self.objSuffix = objSuffix |
|
danderson@220
|
29 |
self.debuginfo_argv = [] |
|
dvander@65
|
30 |
|
|
dvander@65
|
31 |
class MSVC(Vendor): |
|
dvander@65
|
32 |
def __init__(self, command, version): |
|
dvander@107
|
33 |
super(MSVC, self).__init__('msvc', version, 'msvc', command, '.obj') |
|
dvander@65
|
34 |
self.definePrefix = '/D' |
|
danderson@220
|
35 |
self.debuginfo_argv = ['/Zi'] |
|
dvander@239
|
36 |
if int(self.version) >= 1800: |
|
dvander@238
|
37 |
self.debuginfo_argv += ['/FS'] |
|
dvander@65
|
38 |
|
|
dvander@190
|
39 |
@staticmethod |
|
dvander@190
|
40 |
def IncludePath(outputPath, includePath): |
|
dvander@210
|
41 |
# Hack - try and get a relative path because CL, with either |
|
dvander@210
|
42 |
# /Zi or /ZI, combined with subprocess, apparently tries and |
|
dvander@210
|
43 |
# looks for paths like c:\bleh\"c:\bleh" <-- wtf |
|
dvander@210
|
44 |
# .. this according to Process Monitor |
|
dvander@65
|
45 |
outputPath = os.path.normcase(outputPath) |
|
dvander@65
|
46 |
includePath = os.path.normcase(includePath) |
|
dvander@65
|
47 |
outputDrive = os.path.splitdrive(outputPath)[0] |
|
dvander@65
|
48 |
includeDrive = os.path.splitdrive(includePath)[0] |
|
dvander@65
|
49 |
if outputDrive == includeDrive: |
|
dvander@190
|
50 |
return os.path.relpath(includePath, outputPath) |
|
dvander@190
|
51 |
return includePath |
|
dvander@190
|
52 |
|
|
dvander@190
|
53 |
def formatInclude(self, outputPath, includePath): |
|
dvander@190
|
54 |
return ['/I', self.IncludePath(outputPath, includePath)] |
|
dvander@190
|
55 |
|
|
dvander@190
|
56 |
def preprocessArgs(self, sourceFile): |
|
dvander@204
|
57 |
return ['/showIncludes', '/nologo', '/P', '/c', sourceFile] |
|
dvander@65
|
58 |
|
|
dvander@65
|
59 |
def objectArgs(self, sourceFile, objFile): |
|
dvander@128
|
60 |
return ['/showIncludes', '/nologo', '/c', sourceFile, '/Fo' + objFile] |
|
dvander@65
|
61 |
|
|
dvander@65
|
62 |
class CompatGCC(Vendor): |
|
dvander@65
|
63 |
def __init__(self, name, command, version): |
|
dvander@67
|
64 |
super(CompatGCC, self).__init__(name, version, 'gcc', command, '.o') |
|
dvander@65
|
65 |
parts = version.split('.') |
|
dvander@65
|
66 |
self.majorVersion = int(parts[0]) |
|
dvander@65
|
67 |
self.minorVersion = int(parts[1]) |
|
dvander@65
|
68 |
self.definePrefix = '-D' |
|
dvander@65
|
69 |
|
|
dvander@65
|
70 |
def formatInclude(self, outputPath, includePath): |
|
dvander@65
|
71 |
return ['-I', os.path.normpath(includePath)] |
|
dvander@65
|
72 |
|
|
dvander@65
|
73 |
def objectArgs(self, sourceFile, objFile): |
|
dvander@65
|
74 |
return ['-H', '-c', sourceFile, '-o', objFile] |
|
dvander@65
|
75 |
|
|
dvander@65
|
76 |
class GCC(CompatGCC): |
|
dvander@65
|
77 |
def __init__(self, command, version): |
|
dvander@65
|
78 |
super(GCC, self).__init__('gcc', command, version) |
|
danderson@220
|
79 |
self.debuginfo_argv = ['-g3', '-ggdb3'] |
|
dvander@65
|
80 |
|
|
dvander@65
|
81 |
class Clang(CompatGCC): |
|
dvander@65
|
82 |
def __init__(self, command, version): |
|
dvander@65
|
83 |
super(Clang, self).__init__('clang', command, version) |
|
danderson@220
|
84 |
self.debuginfo_argv = ['-g3'] |
|
dvander@65
|
85 |
|
|
dvander@183
|
86 |
class SunPro(Vendor): |
|
dvander@183
|
87 |
def __init__(self, command, version): |
|
dvander@183
|
88 |
super(SunPro, self).__init__('sun', version, 'sun', command, '.o') |
|
dvander@183
|
89 |
self.definePrefix = '-D' |
|
danderson@220
|
90 |
self.debuginfo_argv = ['-g3'] |
|
dvander@183
|
91 |
|
|
dvander@183
|
92 |
def formatInclude(self, outputPath, includePath): |
|
dvander@183
|
93 |
return ['-I', os.path.normpath(includePath)] |
|
dvander@183
|
94 |
|
|
dvander@183
|
95 |
def objectArgs(self, sourceFile, objFile): |
|
dvander@183
|
96 |
return ['-H', '-c', sourceFile, '-o', objFile] |
|
dvander@183
|
97 |
|
|
dvander@65
|
98 |
def TryVerifyCompiler(cx, env, mode, cmd): |
|
dvander@65
|
99 |
if util.IsWindows(): |
|
dvander@65
|
100 |
cc = VerifyCompiler(cx, env, mode, cmd, 'msvc') |
|
dvander@65
|
101 |
if cc: |
|
dvander@65
|
102 |
return cc |
|
dvander@65
|
103 |
return VerifyCompiler(cx, env, mode, cmd, 'gcc') |
|
dvander@65
|
104 |
|
|
dvander@65
|
105 |
CompilerSearch = { |
|
dvander@65
|
106 |
'CC': { |
|
dvander@65
|
107 |
'mac': ['cc', 'clang', 'gcc', 'icc'], |
|
dvander@65
|
108 |
'windows': ['cl'], |
|
dvander@65
|
109 |
'default': ['cc', 'gcc', 'clang', 'icc'] |
|
dvander@210
|
110 |
}, |
|
dvander@65
|
111 |
'CXX': { |
|
dvander@65
|
112 |
'mac': ['c++', 'clang++', 'g++', 'icc'], |
|
dvander@65
|
113 |
'windows': ['cl'], |
|
dvander@65
|
114 |
'default': ['c++', 'g++', 'clang++', 'icc'] |
|
dvander@65
|
115 |
} |
|
dvander@65
|
116 |
} |
|
dvander@65
|
117 |
|
|
dvander@65
|
118 |
def DetectCompiler(cx, env, var): |
|
dvander@65
|
119 |
if var in env: |
|
dvander@65
|
120 |
trys = [env[var]] |
|
dvander@65
|
121 |
else: |
|
dvander@65
|
122 |
if util.Platform() in CompilerSearch[var]: |
|
dvander@65
|
123 |
trys = CompilerSearch[var][util.Platform()] |
|
dvander@65
|
124 |
else: |
|
dvander@65
|
125 |
trys = CompilerSearch[var]['default'] |
|
dvander@65
|
126 |
for i in trys: |
|
dvander@65
|
127 |
cc = TryVerifyCompiler(cx, env, var, i) |
|
dvander@65
|
128 |
if cc: |
|
dvander@65
|
129 |
return cc |
|
dvander@65
|
130 |
raise Exception('Unable to find a suitable ' + var + ' compiler') |
|
dvander@65
|
131 |
|
|
dvander@65
|
132 |
def VerifyCompiler(cx, env, mode, cmd, vendor): |
|
dvander@65
|
133 |
args = cmd.split(' ') |
|
dvander@65
|
134 |
if 'CXXFLAGS' in env: |
|
dvander@65
|
135 |
args.extend(env['CXXFLAGS']) |
|
dvander@65
|
136 |
if 'CFLAGS' in env: |
|
dvander@65
|
137 |
args.extend(env['CFLAGS']) |
|
dvander@65
|
138 |
if mode == 'CXX' and 'CXXFLAGS' in env: |
|
dvander@65
|
139 |
args.extend(env['CXXFLAGS']) |
|
dvander@65
|
140 |
if mode == 'CXX': |
|
dvander@65
|
141 |
filename = 'test.cpp' |
|
dvander@65
|
142 |
else: |
|
dvander@65
|
143 |
filename = 'test.c' |
|
dvander@65
|
144 |
file = open(filename, 'w') |
|
dvander@65
|
145 |
file.write(""" |
|
dvander@65
|
146 |
#include <stdio.h> |
|
dvander@65
|
147 |
#include <stdlib.h> |
|
dvander@65
|
148 |
|
|
dvander@65
|
149 |
int main() |
|
dvander@65
|
150 |
{ |
|
dvander@65
|
151 |
#if defined __ICC |
|
dvander@65
|
152 |
printf("icc %d\\n", __ICC); |
|
dvander@65
|
153 |
#elif defined __clang__ |
|
dvander@65
|
154 |
# if defined(__clang_major__) && defined(__clang_minor__) |
|
dvander@65
|
155 |
printf("clang %d.%d\\n", __clang_major__, __clang_minor__); |
|
dvander@65
|
156 |
# else |
|
dvander@65
|
157 |
printf("clang 1.%d\\n", __GNUC_MINOR__); |
|
dvander@65
|
158 |
# endif |
|
dvander@65
|
159 |
#elif defined __GNUC__ |
|
dvander@65
|
160 |
printf("gcc %d.%d\\n", __GNUC__, __GNUC_MINOR__); |
|
dvander@65
|
161 |
#elif defined _MSC_VER |
|
dvander@65
|
162 |
printf("msvc %d\\n", _MSC_VER); |
|
dvander@65
|
163 |
#elif defined __TenDRA__ |
|
dvander@65
|
164 |
printf("tendra 0\\n"); |
|
dvander@183
|
165 |
#elif defined __SUNPRO_C |
|
dvander@183
|
166 |
printf("sun %x\\n", __SUNPRO_C); |
|
dvander@183
|
167 |
#elif defined __SUNPRO_CC |
|
dvander@183
|
168 |
printf("sun %x\\n", __SUNPRO_CC); |
|
dvander@65
|
169 |
#else |
|
dvander@65
|
170 |
#error "Unrecognized compiler!" |
|
dvander@65
|
171 |
#endif |
|
dvander@65
|
172 |
#if defined __cplusplus |
|
dvander@65
|
173 |
printf("CXX\\n"); |
|
dvander@65
|
174 |
#else |
|
dvander@65
|
175 |
printf("CC\\n"); |
|
dvander@65
|
176 |
#endif |
|
dvander@65
|
177 |
exit(0); |
|
dvander@65
|
178 |
} |
|
dvander@65
|
179 |
""") |
|
dvander@65
|
180 |
file.close() |
|
dvander@65
|
181 |
if mode == 'CC': |
|
dvander@65
|
182 |
executable = 'test' + util.ExecutableSuffix() |
|
dvander@65
|
183 |
elif mode == 'CXX': |
|
dvander@65
|
184 |
executable = 'testp' + util.ExecutableSuffix() |
|
dvander@65
|
185 |
|
|
dvander@65
|
186 |
# Make sure the exe is gone. |
|
dvander@65
|
187 |
if os.path.exists(executable): |
|
dvander@65
|
188 |
os.unlink(executable) |
|
dvander@65
|
189 |
|
|
dvander@183
|
190 |
# Until we can better detect vendors, don't do this. |
|
dvander@183
|
191 |
# if vendor == 'gcc' and mode == 'CXX': |
|
dvander@183
|
192 |
# args.extend(['-fno-exceptions', '-fno-rtti']) |
|
dvander@65
|
193 |
args.extend([filename, '-o', executable]) |
|
dvander@163
|
194 |
util.con_out( |
|
dvander@163
|
195 |
util.ConsoleHeader, |
|
dvander@163
|
196 |
'Checking {0} compiler (vendor test {1})... '.format(mode, vendor), |
|
dvander@163
|
197 |
util.ConsoleBlue, |
|
dvander@163
|
198 |
'{0}'.format(args), |
|
dvander@163
|
199 |
util.ConsoleNormal |
|
dvander@163
|
200 |
) |
|
dvander@65
|
201 |
p = util.CreateProcess(args) |
|
dvander@65
|
202 |
if p == None: |
|
dvander@65
|
203 |
print('not found') |
|
dvander@65
|
204 |
return False |
|
dvander@65
|
205 |
if util.WaitForProcess(p) != 0: |
|
dvander@65
|
206 |
print('failed with return code {0}'.format(p.returncode)) |
|
dvander@65
|
207 |
return False |
|
dvander@65
|
208 |
|
|
dvander@65
|
209 |
exe = util.MakePath('.', executable) |
|
dvander@65
|
210 |
p = util.CreateProcess([executable], executable = exe) |
|
dvander@65
|
211 |
if p == None: |
|
dvander@65
|
212 |
print('failed to create executable') |
|
dvander@65
|
213 |
return False |
|
dvander@65
|
214 |
if util.WaitForProcess(p) != 0: |
|
dvander@65
|
215 |
print('executable failed with return code {0}'.format(p.returncode)) |
|
dvander@65
|
216 |
return False |
|
dvander@65
|
217 |
lines = p.stdoutText.splitlines() |
|
dvander@65
|
218 |
if len(lines) != 2: |
|
dvander@65
|
219 |
print('invalid executable output') |
|
dvander@65
|
220 |
return False |
|
dvander@65
|
221 |
if lines[1] != mode: |
|
dvander@65
|
222 |
print('requested {0} compiler, found {1}'.format(mode, lines[1])) |
|
dvander@65
|
223 |
return False |
|
dvander@65
|
224 |
|
|
dvander@65
|
225 |
vendor, version = lines[0].split(' ') |
|
dvander@65
|
226 |
if vendor == 'gcc': |
|
dvander@65
|
227 |
v = GCC(cmd, version) |
|
dvander@65
|
228 |
elif vendor == 'clang': |
|
dvander@65
|
229 |
v = Clang(cmd, version) |
|
dvander@65
|
230 |
elif vendor == 'msvc': |
|
dvander@65
|
231 |
v = MSVC(cmd, version) |
|
dvander@183
|
232 |
elif vendor == 'sun': |
|
dvander@183
|
233 |
v = SunPro(cmd, version) |
|
dvander@65
|
234 |
else: |
|
dvander@65
|
235 |
print('Unknown vendor {0}'.format(vendor)) |
|
dvander@65
|
236 |
return False |
|
dvander@65
|
237 |
|
|
dvander@163
|
238 |
util.con_out( |
|
dvander@163
|
239 |
util.ConsoleHeader, |
|
dvander@163
|
240 |
'found {0} version {1}'.format(vendor, version), |
|
dvander@163
|
241 |
util.ConsoleNormal |
|
dvander@163
|
242 |
) |
|
dvander@65
|
243 |
return v |
|
dvander@65
|
244 |
|
|
danderson@175
|
245 |
class Dep(object): |
|
danderson@175
|
246 |
def __init__(self, text, node): |
|
danderson@175
|
247 |
self.text = text |
|
danderson@175
|
248 |
self.node = node |
|
danderson@175
|
249 |
|
|
dvander@65
|
250 |
class Compiler(object): |
|
dvander@114
|
251 |
attrs = [ |
|
dvander@114
|
252 |
'includes', # C and C++ include paths |
|
dvander@114
|
253 |
'cxxincludes', # C++-only include paths |
|
dvander@114
|
254 |
'cflags', # C and C++ compiler flags |
|
dvander@114
|
255 |
'cxxflags', # C++-only compiler flags |
|
dvander@114
|
256 |
'defines', # C and C++ #defines |
|
dvander@114
|
257 |
'cxxdefines', # C++-only #defines |
|
dvander@114
|
258 |
|
|
dvander@190
|
259 |
'rcdefines', # Resource Compiler (RC) defines |
|
dvander@190
|
260 |
|
|
dvander@115
|
261 |
# Link flags. If any members are not strings, they will be interpreted as |
|
dvander@115
|
262 |
# Dep entries created from BinaryBuilder. |
|
dvander@115
|
263 |
'linkflags', |
|
dvander@115
|
264 |
|
|
dvander@114
|
265 |
# An array of objects to link, after all link flags have been specified. |
|
dvander@115
|
266 |
# Entries may either be strings containing a path, or Dep entries created |
|
dvander@115
|
267 |
# from BinaryBuilder. |
|
dvander@114
|
268 |
'postlink', |
|
dvander@134
|
269 |
|
|
dvander@134
|
270 |
# An array of nodes which should be weak dependencies on each source |
|
dvander@134
|
271 |
# compilation command. |
|
dvander@136
|
272 |
'sourcedeps', |
|
dvander@114
|
273 |
] |
|
dvander@65
|
274 |
|
|
dvander@65
|
275 |
def __init__(self, cc, cxx): |
|
dvander@65
|
276 |
self.cc = cc |
|
dvander@65
|
277 |
self.cxx = cxx |
|
danderson@220
|
278 |
self.debuginfo = True |
|
dvander@65
|
279 |
for attr in Compiler.attrs: |
|
dvander@65
|
280 |
setattr(self, attr, []) |
|
dvander@65
|
281 |
|
|
dvander@65
|
282 |
def clone(self): |
|
dvander@65
|
283 |
cc = Compiler(self.cc, self.cxx) |
|
dvander@65
|
284 |
cc.cc = self.cc |
|
dvander@65
|
285 |
cc.cxx = self.cxx |
|
danderson@220
|
286 |
cc.debuginfo = self.debuginfo |
|
dvander@65
|
287 |
for attr in Compiler.attrs: |
|
dvander@110
|
288 |
setattr(cc, attr, copy.copy(getattr(self, attr))) |
|
dvander@65
|
289 |
return cc |
|
dvander@65
|
290 |
|
|
dvander@65
|
291 |
def Program(self, name): |
|
dvander@65
|
292 |
return Program(self, name) |
|
dvander@65
|
293 |
|
|
dvander@107
|
294 |
def Library(self, name): |
|
dvander@107
|
295 |
return Library(self, name) |
|
dvander@107
|
296 |
|
|
dvander@207
|
297 |
def StaticLibrary(self, name): |
|
dvander@207
|
298 |
return StaticLibrary(self, name) |
|
dvander@207
|
299 |
|
|
dvander@186
|
300 |
@staticmethod |
|
dvander@186
|
301 |
def Dep(text, node=None): |
|
dvander@186
|
302 |
return Dep(text, node) |
|
dvander@186
|
303 |
|
|
dvander@65
|
304 |
# Environment representing a C/C++ compiler invocation. Encapsulates most |
|
dvander@65
|
305 |
# arguments. |
|
dvander@65
|
306 |
class CCommandEnv(object): |
|
dvander@65
|
307 |
def __init__(self, outputPath, config, compiler): |
|
dvander@65
|
308 |
args = compiler.command.split(' ') |
|
dvander@65
|
309 |
args += config.cflags |
|
danderson@220
|
310 |
if config.debuginfo: |
|
danderson@220
|
311 |
args += compiler.debuginfo_argv |
|
dvander@65
|
312 |
if compiler == config.cxx: |
|
dvander@65
|
313 |
args += config.cxxflags |
|
dvander@65
|
314 |
args += [compiler.definePrefix + define for define in config.defines] |
|
dvander@65
|
315 |
if compiler == config.cxx: |
|
dvander@65
|
316 |
args += [compiler.definePrefix + define for define in config.cxxdefines] |
|
dvander@65
|
317 |
for include in config.includes: |
|
dvander@65
|
318 |
args += compiler.formatInclude(outputPath, include) |
|
dvander@65
|
319 |
if compiler == config.cxx: |
|
dvander@65
|
320 |
for include in config.cxxincludes: |
|
dvander@65
|
321 |
args += compiler.formatInclude(outputPath, include) |
|
dvander@190
|
322 |
self.argv = args |
|
dvander@65
|
323 |
self.compiler = compiler |
|
dvander@65
|
324 |
|
|
dvander@65
|
325 |
def NameForObjectFile(file): |
|
dvander@65
|
326 |
return re.sub('[^a-zA-Z0-9_]+', '_', os.path.splitext(file)[0]); |
|
dvander@65
|
327 |
|
|
dvander@65
|
328 |
class ObjectFile(object): |
|
dvander@227
|
329 |
def __init__(self, sourceFile, outputFile, argv, sharedOutputs): |
|
dvander@65
|
330 |
self.sourceFile = sourceFile |
|
dvander@65
|
331 |
self.outputFile = outputFile |
|
dvander@65
|
332 |
self.argv = argv |
|
dvander@227
|
333 |
self.sharedOutputs = sharedOutputs |
|
dvander@65
|
334 |
|
|
dvander@190
|
335 |
class RCFile(object): |
|
dvander@199
|
336 |
def __init__(self, sourceFile, preprocFile, outputFile, cl_argv, rc_argv): |
|
dvander@190
|
337 |
self.sourceFile = sourceFile |
|
dvander@199
|
338 |
self.preprocFile = preprocFile |
|
dvander@190
|
339 |
self.outputFile = outputFile |
|
dvander@190
|
340 |
self.cl_argv = cl_argv |
|
dvander@190
|
341 |
self.rc_argv = rc_argv |
|
dvander@190
|
342 |
|
|
dvander@65
|
343 |
class BinaryBuilder(object): |
|
dvander@107
|
344 |
def __init__(self, compiler, name): |
|
dvander@65
|
345 |
super(BinaryBuilder, self).__init__() |
|
dvander@107
|
346 |
self.compiler = compiler.clone() |
|
dvander@65
|
347 |
self.sources = [] |
|
danderson@175
|
348 |
self.name_ = name |
|
danderson@175
|
349 |
self.used_cxx_ = False |
|
danderson@175
|
350 |
self.linker_ = None |
|
dvander@65
|
351 |
|
|
dvander@107
|
352 |
def generate(self, generator, cx): |
|
dvander@118
|
353 |
return generator.addCxxTasks(cx, self) |
|
dvander@107
|
354 |
|
|
dvander@115
|
355 |
# Make an item that can be passed into linkflags/postlink but has an attached |
|
dvander@115
|
356 |
# dependency. |
|
dvander@186
|
357 |
def Dep(self, text, node=None): |
|
dvander@115
|
358 |
return Dep(text, node) |
|
dvander@115
|
359 |
|
|
dvander@115
|
360 |
# The folder we'll be in, relative to our build context. |
|
dvander@115
|
361 |
@property |
|
dvander@115
|
362 |
def localFolder(self): |
|
danderson@175
|
363 |
return self.name_ |
|
dvander@115
|
364 |
|
|
dvander@176
|
365 |
# Exposed only for frontends. |
|
dvander@176
|
366 |
@property |
|
dvander@176
|
367 |
def linker(self): |
|
dvander@176
|
368 |
return self.linker_ |
|
dvander@115
|
369 |
|
|
dvander@115
|
370 |
# Compute the build folder. |
|
dvander@115
|
371 |
def getBuildFolder(self, builder): |
|
dvander@115
|
372 |
return os.path.join(builder.buildFolder, self.localFolder) |
|
dvander@115
|
373 |
|
|
dvander@210
|
374 |
def linkFlag(self, cx, item): |
|
dvander@210
|
375 |
if type(item) is Dep: |
|
dvander@237
|
376 |
# If the dep is a file dependency (no node attached), and has a relative |
|
dvander@237
|
377 |
# path, make it absolute so the linker knows where to look. |
|
dvander@237
|
378 |
if item.node is None and not os.path.isabs(item.text): |
|
dvander@237
|
379 |
return os.path.join(cx.currentSourcePath, item.text) |
|
dvander@210
|
380 |
return item.text |
|
dvander@210
|
381 |
|
|
dvander@210
|
382 |
if hasattr(item, 'path'): |
|
dvander@210
|
383 |
if os.path.isabs(item.path): |
|
dvander@210
|
384 |
return item.path |
|
dvander@210
|
385 |
|
|
dvander@210
|
386 |
local_path = os.path.join(cx.buildFolder, self.localFolder) |
|
dvander@210
|
387 |
return os.path.relpath(item.path, local_path) |
|
dvander@210
|
388 |
|
|
dvander@210
|
389 |
return item |
|
dvander@210
|
390 |
|
|
dvander@210
|
391 |
def linkFlags(self, cx): |
|
dvander@210
|
392 |
argv = [self.linkFlag(cx, item) for item in self.compiler.linkflags] |
|
dvander@210
|
393 |
argv += [self.linkFlag(cx, item) for item in self.compiler.postlink] |
|
dvander@210
|
394 |
return argv |
|
dvander@210
|
395 |
|
|
dvander@65
|
396 |
def finish(self, cx): |
|
dvander@65
|
397 |
# Because we want to compute relative include folders for MSVC (see its |
|
dvander@65
|
398 |
# vendor object), we need to compute an absolute path to the build folder. |
|
dvander@115
|
399 |
self.outputFolder = self.getBuildFolder(cx) |
|
dvander@107
|
400 |
self.outputPath = os.path.join(cx.buildPath, self.outputFolder) |
|
dvander@107
|
401 |
self.default_c_env = CCommandEnv(self.outputPath, self.compiler, self.compiler.cc) |
|
dvander@107
|
402 |
self.default_cxx_env = CCommandEnv(self.outputPath, self.compiler, self.compiler.cxx) |
|
dvander@65
|
403 |
|
|
dvander@227
|
404 |
shared_cc_outputs = [] |
|
dvander@231
|
405 |
if self.compiler.debuginfo and self.compiler.cc.behavior == 'msvc': |
|
dvander@227
|
406 |
cl_version = int(self.compiler.cc.version) - 600 |
|
dvander@227
|
407 |
shared_pdb = 'vc{0}.pdb'.format(int(cl_version / 10)) |
|
dvander@227
|
408 |
shared_cc_outputs += [shared_pdb] |
|
dvander@227
|
409 |
|
|
dvander@190
|
410 |
self.objects = [] |
|
dvander@190
|
411 |
self.resources = [] |
|
dvander@190
|
412 |
for item in self.sources: |
|
dvander@199
|
413 |
if os.path.isabs(item): |
|
dvander@199
|
414 |
sourceFile = item |
|
dvander@199
|
415 |
else: |
|
dvander@199
|
416 |
sourceFile = os.path.join(cx.currentSourcePath, item) |
|
dvander@199
|
417 |
sourceFile = os.path.normpath(sourceFile) |
|
dvander@200
|
418 |
|
|
dvander@200
|
419 |
filename, extension = os.path.splitext(item) |
|
dvander@199
|
420 |
encname = NameForObjectFile(filename) |
|
dvander@199
|
421 |
|
|
dvander@190
|
422 |
if extension == '.rc': |
|
dvander@190
|
423 |
cenv = self.default_c_env |
|
dvander@199
|
424 |
objectFile = encname + '.res' |
|
dvander@190
|
425 |
else: |
|
dvander@190
|
426 |
if extension == '.c': |
|
dvander@190
|
427 |
cenv = self.default_c_env |
|
dvander@190
|
428 |
else: |
|
dvander@190
|
429 |
cenv = self.default_cxx_env |
|
dvander@190
|
430 |
self.used_cxx_ = True |
|
dvander@199
|
431 |
objectFile = encname + cenv.compiler.objSuffix |
|
dvander@190
|
432 |
|
|
dvander@190
|
433 |
if extension == '.rc': |
|
dvander@190
|
434 |
# This is only relevant on Windows. |
|
dvander@190
|
435 |
vendor = cenv.compiler |
|
dvander@190
|
436 |
defines = self.compiler.defines + self.compiler.cxxdefines + self.compiler.rcdefines |
|
dvander@190
|
437 |
cl_argv = vendor.command.split(' ') |
|
dvander@190
|
438 |
cl_argv += [vendor.definePrefix + define for define in defines] |
|
dvander@190
|
439 |
for include in (self.compiler.includes + self.compiler.cxxincludes): |
|
dvander@190
|
440 |
cl_argv += vendor.formatInclude(objectFile, include) |
|
dvander@190
|
441 |
cl_argv += vendor.preprocessArgs(sourceFile) |
|
dvander@190
|
442 |
|
|
dvander@190
|
443 |
rc_argv = ['rc', '/nologo'] |
|
dvander@190
|
444 |
for define in defines: |
|
dvander@190
|
445 |
rc_argv.extend(['/d', define]) |
|
dvander@190
|
446 |
for include in (self.compiler.includes + self.compiler.cxxincludes): |
|
dvander@190
|
447 |
rc_argv.extend(['/i', MSVC.IncludePath(objectFile, include)]) |
|
dvander@190
|
448 |
rc_argv.append('/fo' + objectFile) |
|
dvander@190
|
449 |
rc_argv.append(sourceFile) |
|
dvander@190
|
450 |
|
|
dvander@199
|
451 |
self.resources.append(RCFile(sourceFile, encname + '.i', objectFile, cl_argv, rc_argv)) |
|
dvander@190
|
452 |
else: |
|
dvander@190
|
453 |
argv = cenv.argv + cenv.compiler.objectArgs(sourceFile, objectFile) |
|
dvander@227
|
454 |
obj = ObjectFile(sourceFile, objectFile, argv, shared_cc_outputs) |
|
dvander@227
|
455 |
self.objects.append(obj) |
|
dvander@65
|
456 |
|
|
danderson@175
|
457 |
if not self.linker_: |
|
danderson@175
|
458 |
if self.used_cxx_: |
|
danderson@175
|
459 |
self.linker_ = self.compiler.cxx |
|
dvander@65
|
460 |
else: |
|
danderson@175
|
461 |
self.linker_ = self.compiler.cc |
|
dvander@65
|
462 |
|
|
dvander@207
|
463 |
files = [out.outputFile for out in self.objects + self.resources] |
|
danderson@220
|
464 |
self.argv = self.generateBinary(cx, files) |
|
danderson@220
|
465 |
self.linker_outputs = [self.outputFile] |
|
danderson@220
|
466 |
self.debug_entry = None |
|
danderson@220
|
467 |
|
|
dvander@232
|
468 |
if self.linker_.behavior == 'msvc': |
|
dvander@232
|
469 |
if isinstance(self, Library): |
|
dvander@232
|
470 |
# In theory, .dlls should have exports, so MSVC will generate these |
|
dvander@232
|
471 |
# files. If this turns out not to be true, we may have to get fancier. |
|
dvander@232
|
472 |
self.linker_outputs += [self.name_ + '.lib'] |
|
dvander@232
|
473 |
self.linker_outputs += [self.name_ + '.exp'] |
|
dvander@227
|
474 |
|
|
dvander@221
|
475 |
if self.compiler.debuginfo: |
|
dvander@221
|
476 |
self.perform_symbol_steps(cx) |
|
dvander@221
|
477 |
|
|
dvander@221
|
478 |
def perform_symbol_steps(self, cx): |
|
dvander@221
|
479 |
if isinstance(self.linker_, MSVC): |
|
dvander@227
|
480 |
# Note, pdb is last since we read the pdb as outputs[-1]. |
|
dvander@221
|
481 |
self.linker_outputs += [self.name_ + '.pdb'] |
|
dvander@221
|
482 |
elif cx.target_platform is 'mac': |
|
dvander@221
|
483 |
bundle_folder = os.path.join(self.localFolder, self.outputFile + '.dSYM') |
|
dvander@221
|
484 |
bundle_entry = cx.AddFolder(bundle_folder) |
|
dvander@221
|
485 |
bundle_layout = [ |
|
dvander@221
|
486 |
'Contents', |
|
dvander@221
|
487 |
'Contents/Resources', |
|
dvander@221
|
488 |
'Contents/Resources/DWARF', |
|
dvander@221
|
489 |
] |
|
dvander@221
|
490 |
for folder in bundle_layout: |
|
dvander@221
|
491 |
cx.AddFolder(os.path.join(bundle_folder, folder)) |
|
dvander@221
|
492 |
self.linker_outputs += [ |
|
dvander@221
|
493 |
self.outputFile + '.dSYM/Contents/Info.plist', |
|
dvander@221
|
494 |
self.outputFile + '.dSYM/Contents/Resources/DWARF/' + self.outputFile |
|
dvander@221
|
495 |
] |
|
dvander@221
|
496 |
self.debug_entry = bundle_entry |
|
dvander@221
|
497 |
self.argv = ['ambuild_dsymutil_wrapper.sh', self.outputFile] + self.argv |
|
dvander@221
|
498 |
elif cx.target_platform is 'linux': |
|
dvander@221
|
499 |
self.linker_outputs += [ |
|
dvander@221
|
500 |
self.outputFile + '.dbg' |
|
dvander@221
|
501 |
] |
|
dvander@221
|
502 |
self.argv = ['ambuild_objcopy_wrapper.sh', self.outputFile] + self.argv |
|
danderson@220
|
503 |
|
|
danderson@220
|
504 |
def link(self, context, folder, inputs): |
|
dvander@232
|
505 |
# The existence of .ilk files on Windows does not seem reliable, so we |
|
dvander@232
|
506 |
# treat it as "shared" which does not participate in the DAG (yet). |
|
dvander@232
|
507 |
shared_outputs = [] |
|
dvander@232
|
508 |
if self.linker_.behavior == 'msvc': |
|
dvander@232
|
509 |
if not isinstance(self, StaticLibrary) and '/INCREMENTAL:NO' not in self.argv: |
|
dvander@232
|
510 |
shared_outputs += [self.name_ + '.ilk'] |
|
dvander@232
|
511 |
|
|
danderson@220
|
512 |
ignore, outputs = context.AddCommand( |
|
danderson@220
|
513 |
inputs = inputs, |
|
danderson@220
|
514 |
argv = self.argv, |
|
danderson@220
|
515 |
outputs = self.linker_outputs, |
|
dvander@232
|
516 |
folder = folder, |
|
dvander@232
|
517 |
shared_outputs = shared_outputs |
|
danderson@220
|
518 |
) |
|
danderson@220
|
519 |
if not self.debug_entry and self.compiler.debuginfo: |
|
danderson@220
|
520 |
self.debug_entry = outputs[-1] |
|
danderson@220
|
521 |
return outputs[0], self.debug_entry |
|
dvander@65
|
522 |
|
|
dvander@65
|
523 |
class Program(BinaryBuilder): |
|
dvander@107
|
524 |
def __init__(self, compiler, name): |
|
dvander@107
|
525 |
super(Program, self).__init__(compiler, name) |
|
dvander@190
|
526 |
self.outputFile = name + util.ExecutableSuffix() |
|
dvander@65
|
527 |
|
|
dvander@207
|
528 |
def generateBinary(self, cx, files): |
|
dvander@207
|
529 |
argv = self.linker_.command.split(' ') |
|
dvander@207
|
530 |
argv += files |
|
dvander@207
|
531 |
|
|
danderson@175
|
532 |
if isinstance(self.linker_, MSVC): |
|
dvander@65
|
533 |
argv.append('/link') |
|
danderson@220
|
534 |
argv.extend(self.linkFlags(cx)) |
|
dvander@128
|
535 |
argv.append('/nologo') |
|
danderson@220
|
536 |
argv += [ |
|
danderson@220
|
537 |
'/OUT:' + self.outputFile, |
|
danderson@220
|
538 |
'/nologo', |
|
danderson@220
|
539 |
] |
|
danderson@220
|
540 |
if self.compiler.debuginfo: |
|
danderson@220
|
541 |
argv += ['/DEBUG', '/PDB:"' + self.name_ + '.pdb"'] |
|
dvander@65
|
542 |
else: |
|
danderson@220
|
543 |
argv.extend(self.linkFlags(cx)) |
|
dvander@191
|
544 |
argv.extend(['-o', self.outputFile]) |
|
dvander@65
|
545 |
|
|
danderson@220
|
546 |
return argv |
|
dvander@65
|
547 |
|
|
dvander@107
|
548 |
class Library(BinaryBuilder): |
|
dvander@107
|
549 |
def __init__(self, compiler, name): |
|
dvander@107
|
550 |
super(Library, self).__init__(compiler, name) |
|
dvander@190
|
551 |
self.outputFile = name + util.SharedLibSuffix() |
|
dvander@107
|
552 |
|
|
dvander@207
|
553 |
def generateBinary(self, cx, files): |
|
dvander@207
|
554 |
argv = self.linker_.command.split(' ') |
|
dvander@207
|
555 |
argv += files |
|
dvander@207
|
556 |
|
|
danderson@175
|
557 |
if isinstance(self.linker_, MSVC): |
|
dvander@151
|
558 |
argv.append('/link') |
|
danderson@220
|
559 |
argv.extend(self.linkFlags(cx)) |
|
danderson@220
|
560 |
argv += [ |
|
danderson@220
|
561 |
'/OUT:' + self.outputFile, |
|
danderson@220
|
562 |
'/DEBUG', |
|
danderson@220
|
563 |
'/nologo', |
|
danderson@220
|
564 |
'/DLL', |
|
danderson@220
|
565 |
] |
|
danderson@220
|
566 |
if self.compiler.debuginfo: |
|
danderson@220
|
567 |
argv += ['/DEBUG', '/PDB:"' + self.name_ + '.pdb"'] |
|
danderson@175
|
568 |
elif isinstance(self.linker_, CompatGCC): |
|
danderson@220
|
569 |
argv.extend(self.linkFlags(cx)) |
|
dvander@107
|
570 |
if util.IsMac(): |
|
dvander@107
|
571 |
argv.append('-dynamiclib') |
|
dvander@107
|
572 |
else: |
|
dvander@107
|
573 |
argv.append('-shared') |
|
dvander@191
|
574 |
argv.extend(['-o', self.outputFile]) |
|
dvander@107
|
575 |
|
|
danderson@220
|
576 |
return argv |
|
dvander@207
|
577 |
|
|
dvander@207
|
578 |
class StaticLibrary(BinaryBuilder): |
|
dvander@207
|
579 |
def __init__(self, compiler, name): |
|
dvander@207
|
580 |
super(StaticLibrary, self).__init__(compiler, name) |
|
dvander@207
|
581 |
self.outputFile = util.StaticLibPrefix() + name + util.StaticLibSuffix() |
|
dvander@207
|
582 |
|
|
dvander@207
|
583 |
def generateBinary(self, cx, files): |
|
dvander@207
|
584 |
if isinstance(self.linker_, MSVC): |
|
dvander@207
|
585 |
argv = ['lib.exe', '/OUT:' + self.outputFile] |
|
dvander@207
|
586 |
else: |
|
dvander@207
|
587 |
argv = ['ar', 'rcs', self.outputFile] |
|
dvander@207
|
588 |
argv += files |
|
danderson@220
|
589 |
return argv |
|
dvander@221
|
590 |
|
|
dvander@221
|
591 |
def perform_symbol_steps(self, cx): |
|
dvander@221
|
592 |
pass |