ambuild2/frontend/cpp.py
author David Anderson <dvander@alliedmods.net>
Sat Feb 08 20:40:05 2014 -0800 (2014-02-08)
changeset 238 2e3728b08095
parent 237 a3ab09e0556b
child 239 b7f3bcd06c99
permissions -rw-r--r--
Add /FS for /Zi builds on Visual Studio 2013.
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@238
    36
    if 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