Fix rc errors.
1 # vim: set ts=8 sts=2 sw=2 tw=99 et:
3 # This file is part of AMBuild.
5 # AMBuild is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # AMBuild is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with AMBuild. If not, see <http://www.gnu.org/licenses/>.
17 from __future__ import print_function
20 from ambuild2 import util
23 def __init__(self, name, version, behavior, command, objSuffix):
25 self.version = version
26 self.behavior = behavior
27 self.command = command
28 self.objSuffix = objSuffix
31 def __init__(self, command, version):
32 super(MSVC, self).__init__('msvc', version, 'msvc', command, '.obj')
33 self.definePrefix = '/D'
34 self.pdbSuffix = '.pdb'
37 def IncludePath(outputPath, includePath):
38 #Hack - try and get a relative path because CL, with either
39 #/Zi or /ZI, combined with subprocess, apparently tries and
40 #looks for paths like c:\bleh\"c:\bleh" <-- wtf
41 #.. this according to Process Monitor
42 outputPath = os.path.normcase(outputPath)
43 includePath = os.path.normcase(includePath)
44 outputDrive = os.path.splitdrive(outputPath)[0]
45 includeDrive = os.path.splitdrive(includePath)[0]
46 if outputDrive == includeDrive:
47 return os.path.relpath(includePath, outputPath)
50 def formatInclude(self, outputPath, includePath):
51 return ['/I', self.IncludePath(outputPath, includePath)]
53 def preprocessArgs(self, sourceFile):
54 return ['/showIncludes', '/nologo', '/E', '/c', sourceFile]
56 def objectArgs(self, sourceFile, objFile):
57 return ['/showIncludes', '/nologo', '/c', sourceFile, '/Fo' + objFile]
59 class CompatGCC(Vendor):
60 def __init__(self, name, command, version):
61 super(CompatGCC, self).__init__(name, version, 'gcc', command, '.o')
62 parts = version.split('.')
63 self.majorVersion = int(parts[0])
64 self.minorVersion = int(parts[1])
65 self.definePrefix = '-D'
68 def formatInclude(self, outputPath, includePath):
69 return ['-I', os.path.normpath(includePath)]
71 def objectArgs(self, sourceFile, objFile):
72 return ['-H', '-c', sourceFile, '-o', objFile]
75 def __init__(self, command, version):
76 super(GCC, self).__init__('gcc', command, version)
78 class Clang(CompatGCC):
79 def __init__(self, command, version):
80 super(Clang, self).__init__('clang', command, version)
83 def __init__(self, command, version):
84 super(SunPro, self).__init__('sun', version, 'sun', command, '.o')
85 parts = version.split('.')
86 self.definePrefix = '-D'
89 def formatInclude(self, outputPath, includePath):
90 return ['-I', os.path.normpath(includePath)]
92 def objectArgs(self, sourceFile, objFile):
93 return ['-H', '-c', sourceFile, '-o', objFile]
95 def TryVerifyCompiler(cx, env, mode, cmd):
97 cc = VerifyCompiler(cx, env, mode, cmd, 'msvc')
100 return VerifyCompiler(cx, env, mode, cmd, 'gcc')
104 'mac': ['cc', 'clang', 'gcc', 'icc'],
106 'default': ['cc', 'gcc', 'clang', 'icc']
109 'mac': ['c++', 'clang++', 'g++', 'icc'],
111 'default': ['c++', 'g++', 'clang++', 'icc']
115 def DetectCompiler(cx, env, var):
119 if util.Platform() in CompilerSearch[var]:
120 trys = CompilerSearch[var][util.Platform()]
122 trys = CompilerSearch[var]['default']
124 cc = TryVerifyCompiler(cx, env, var, i)
127 raise Exception('Unable to find a suitable ' + var + ' compiler')
129 def VerifyCompiler(cx, env, mode, cmd, vendor):
130 args = cmd.split(' ')
131 if 'CXXFLAGS' in env:
132 args.extend(env['CXXFLAGS'])
134 args.extend(env['CFLAGS'])
135 if mode == 'CXX' and 'CXXFLAGS' in env:
136 args.extend(env['CXXFLAGS'])
138 filename = 'test.cpp'
141 file = open(filename, 'w')
149 printf("icc %d\\n", __ICC);
150 #elif defined __clang__
151 # if defined(__clang_major__) && defined(__clang_minor__)
152 printf("clang %d.%d\\n", __clang_major__, __clang_minor__);
154 printf("clang 1.%d\\n", __GNUC_MINOR__);
156 #elif defined __GNUC__
157 printf("gcc %d.%d\\n", __GNUC__, __GNUC_MINOR__);
158 #elif defined _MSC_VER
159 printf("msvc %d\\n", _MSC_VER);
160 #elif defined __TenDRA__
161 printf("tendra 0\\n");
162 #elif defined __SUNPRO_C
163 printf("sun %x\\n", __SUNPRO_C);
164 #elif defined __SUNPRO_CC
165 printf("sun %x\\n", __SUNPRO_CC);
167 #error "Unrecognized compiler!"
169 #if defined __cplusplus
179 executable = 'test' + util.ExecutableSuffix()
181 executable = 'testp' + util.ExecutableSuffix()
183 # Make sure the exe is gone.
184 if os.path.exists(executable):
185 os.unlink(executable)
187 # Until we can better detect vendors, don't do this.
188 # if vendor == 'gcc' and mode == 'CXX':
189 # args.extend(['-fno-exceptions', '-fno-rtti'])
190 args.extend([filename, '-o', executable])
193 'Checking {0} compiler (vendor test {1})... '.format(mode, vendor),
198 p = util.CreateProcess(args)
202 if util.WaitForProcess(p) != 0:
203 print('failed with return code {0}'.format(p.returncode))
206 exe = util.MakePath('.', executable)
207 p = util.CreateProcess([executable], executable = exe)
209 print('failed to create executable')
211 if util.WaitForProcess(p) != 0:
212 print('executable failed with return code {0}'.format(p.returncode))
214 lines = p.stdoutText.splitlines()
216 print('invalid executable output')
219 print('requested {0} compiler, found {1}'.format(mode, lines[1]))
222 vendor, version = lines[0].split(' ')
224 v = GCC(cmd, version)
225 elif vendor == 'clang':
226 v = Clang(cmd, version)
227 elif vendor == 'msvc':
228 v = MSVC(cmd, version)
229 elif vendor == 'sun':
230 v = SunPro(cmd, version)
232 print('Unknown vendor {0}'.format(vendor))
237 'found {0} version {1}'.format(vendor, version),
243 def __init__(self, text, node):
247 class Compiler(object):
249 'includes', # C and C++ include paths
250 'cxxincludes', # C++-only include paths
251 'cflags', # C and C++ compiler flags
252 'cxxflags', # C++-only compiler flags
253 'defines', # C and C++ #defines
254 'cxxdefines', # C++-only #defines
256 'rcdefines', # Resource Compiler (RC) defines
258 # Link flags. If any members are not strings, they will be interpreted as
259 # Dep entries created from BinaryBuilder.
262 # An array of objects to link, after all link flags have been specified.
263 # Entries may either be strings containing a path, or Dep entries created
264 # from BinaryBuilder.
267 # An array of nodes which should be weak dependencies on each source
268 # compilation command.
272 def __init__(self, cc, cxx):
275 for attr in Compiler.attrs:
276 setattr(self, attr, [])
279 cc = Compiler(self.cc, self.cxx)
282 for attr in Compiler.attrs:
283 setattr(cc, attr, copy.copy(getattr(self, attr)))
286 def Program(self, name):
287 return Program(self, name)
289 def Library(self, name):
290 return Library(self, name)
293 def Dep(text, node=None):
294 return Dep(text, node)
296 # Environment representing a C/C++ compiler invocation. Encapsulates most
298 class CCommandEnv(object):
299 def __init__(self, outputPath, config, compiler):
300 args = compiler.command.split(' ')
301 args += config.cflags
302 if compiler == config.cxx:
303 args += config.cxxflags
304 args += [compiler.definePrefix + define for define in config.defines]
305 if compiler == config.cxx:
306 args += [compiler.definePrefix + define for define in config.cxxdefines]
307 for include in config.includes:
308 args += compiler.formatInclude(outputPath, include)
309 if compiler == config.cxx:
310 for include in config.cxxincludes:
311 args += compiler.formatInclude(outputPath, include)
313 self.compiler = compiler
315 def NameForObjectFile(file):
316 return re.sub('[^a-zA-Z0-9_]+', '_', os.path.splitext(file)[0]);
318 class ObjectFile(object):
319 def __init__(self, sourceFile, outputFile, argv):
320 self.sourceFile = sourceFile
321 self.outputFile = outputFile
324 class RCFile(object):
325 def __init__(self, sourceFile, preprocFile, outputFile, cl_argv, rc_argv):
326 self.sourceFile = sourceFile
327 self.preprocFile = preprocFile
328 self.outputFile = outputFile
329 self.cl_argv = cl_argv
330 self.rc_argv = rc_argv
332 def LinkFlags(compiler):
334 for item in compiler.linkflags:
335 if type(item) is not Dep:
338 argv.append(item.text)
339 for item in compiler.postlink:
340 if type(item) is not Dep:
343 argv.append(item.text)
346 class BinaryBuilder(object):
347 def __init__(self, compiler, name):
348 super(BinaryBuilder, self).__init__()
349 self.compiler = compiler.clone()
352 self.used_cxx_ = False
355 def generate(self, generator, cx):
356 return generator.addCxxTasks(cx, self)
358 # Make an item that can be passed into linkflags/postlink but has an attached
360 def Dep(self, text, node=None):
361 return Dep(text, node)
363 # The folder we'll be in, relative to our build context.
365 def localFolder(self):
368 # Exposed only for frontends.
373 # Compute the build folder.
374 def getBuildFolder(self, builder):
375 return os.path.join(builder.buildFolder, self.localFolder)
377 def finish(self, cx):
378 # Because we want to compute relative include folders for MSVC (see its
379 # vendor object), we need to compute an absolute path to the build folder.
380 self.outputFolder = self.getBuildFolder(cx)
381 self.outputPath = os.path.join(cx.buildPath, self.outputFolder)
382 self.default_c_env = CCommandEnv(self.outputPath, self.compiler, self.compiler.cc)
383 self.default_cxx_env = CCommandEnv(self.outputPath, self.compiler, self.compiler.cxx)
387 for item in self.sources:
388 if os.path.isabs(item):
391 sourceFile = os.path.join(cx.currentSourcePath, item)
392 sourceFile = os.path.normpath(sourceFile)
393 encname = NameForObjectFile(filename)
395 filename, extension = os.path.splitext(item)
396 if extension == '.rc':
397 cenv = self.default_c_env
398 objectFile = encname + '.res'
400 if extension == '.c':
401 cenv = self.default_c_env
403 cenv = self.default_cxx_env
404 self.used_cxx_ = True
405 objectFile = encname + cenv.compiler.objSuffix
407 if extension == '.rc':
408 # This is only relevant on Windows.
409 vendor = cenv.compiler
410 defines = self.compiler.defines + self.compiler.cxxdefines + self.compiler.rcdefines
411 cl_argv = vendor.command.split(' ')
412 cl_argv += [vendor.definePrefix + define for define in defines]
413 for include in (self.compiler.includes + self.compiler.cxxincludes):
414 cl_argv += vendor.formatInclude(objectFile, include)
415 cl_argv += vendor.preprocessArgs(sourceFile)
416 cl_argv += ['/Fo' + encname + '.i']
418 rc_argv = ['rc', '/nologo']
419 for define in defines:
420 rc_argv.extend(['/d', define])
421 for include in (self.compiler.includes + self.compiler.cxxincludes):
422 rc_argv.extend(['/i', MSVC.IncludePath(objectFile, include)])
423 rc_argv.append('/fo' + objectFile)
424 rc_argv.append(sourceFile)
426 self.resources.append(RCFile(sourceFile, encname + '.i', objectFile, cl_argv, rc_argv))
428 argv = cenv.argv + cenv.compiler.objectArgs(sourceFile, objectFile)
429 self.objects.append(ObjectFile(sourceFile, objectFile, argv))
433 self.linker_ = self.compiler.cxx
435 self.linker_ = self.compiler.cc
437 argv = self.linker_.command.split(' ')
438 for objfile in self.objects:
439 argv.append(objfile.outputFile)
440 for rcfile in self.resources:
441 argv.append(rcfile.outputFile)
443 argv = self.generateBinary(cx, argv)
446 if self.linker_.pdbSuffix:
447 self.pdbFile = self.name_ + self.linker_.pdbSuffix
451 class Program(BinaryBuilder):
452 def __init__(self, compiler, name):
453 super(Program, self).__init__(compiler, name)
454 self.outputFile = name + util.ExecutableSuffix()
456 def generateBinary(self, cx, argv):
457 if isinstance(self.linker_, MSVC):
459 argv.extend(LinkFlags(self.compiler))
460 if isinstance(self.linker_, MSVC):
461 argv.append('/OUT:' + self.outputFile)
462 argv.append('/DEBUG')
463 argv.append('/nologo')
464 argv.append('/PDB:"' + self.name_ + '.pdb"')
466 argv.extend(['-o', self.outputFile])
470 class Library(BinaryBuilder):
471 def __init__(self, compiler, name):
472 super(Library, self).__init__(compiler, name)
473 self.outputFile = name + util.SharedLibSuffix()
475 def generateBinary(self, cx, argv):
476 if isinstance(self.linker_, MSVC):
478 argv.extend(LinkFlags(self.compiler))
479 if isinstance(self.linker_, MSVC):
480 argv.append('/OUT:' + self.outputFile)
481 argv.append('/DEBUG')
482 argv.append('/nologo')
484 argv.append('/PDB:"' + self.name_ + '.pdb"')
485 elif isinstance(self.linker_, CompatGCC):
487 argv.append('-dynamiclib')
489 argv.append('-shared')
490 argv.extend(['-o', self.outputFile])