Completed
Branch rebuild (e7a2d2)
by Glenn
08:26
created

MsvsSettings.GetLdflags()   F

Complexity

Conditions 14

Size

Total Lines 102

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 102
c 0
b 0
f 0
rs 2
cc 14

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like MsvsSettings.GetLdflags() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# Copyright (c) 2012 Google Inc. All rights reserved.
2
# Use of this source code is governed by a BSD-style license that can be
3
# found in the LICENSE file.
4
5
"""
6
This module helps emulate Visual Studio 2008 behavior on top of other
7
build systems, primarily ninja.
8
"""
9
10
import os
11
import re
12
import subprocess
13
import sys
14
15
from gyp.common import OrderedSet
16
import gyp.MSVSUtil
17
import gyp.MSVSVersion
18
19
20
windows_quoter_regex = re.compile(r'(\\*)"')
21
22
23
def QuoteForRspFile(arg):
24
  """Quote a command line argument so that it appears as one argument when
25
  processed via cmd.exe and parsed by CommandLineToArgvW (as is typical for
26
  Windows programs)."""
27
  # See http://goo.gl/cuFbX and http://goo.gl/dhPnp including the comment
28
  # threads. This is actually the quoting rules for CommandLineToArgvW, not
29
  # for the shell, because the shell doesn't do anything in Windows. This
30
  # works more or less because most programs (including the compiler, etc.)
31
  # use that function to handle command line arguments.
32
33
  # For a literal quote, CommandLineToArgvW requires 2n+1 backslashes
34
  # preceding it, and results in n backslashes + the quote. So we substitute
35
  # in 2* what we match, +1 more, plus the quote.
36
  arg = windows_quoter_regex.sub(lambda mo: 2 * mo.group(1) + '\\"', arg)
37
38
  # %'s also need to be doubled otherwise they're interpreted as batch
39
  # positional arguments. Also make sure to escape the % so that they're
40
  # passed literally through escaping so they can be singled to just the
41
  # original %. Otherwise, trying to pass the literal representation that
42
  # looks like an environment variable to the shell (e.g. %PATH%) would fail.
43
  arg = arg.replace('%', '%%')
44
45
  # These commands are used in rsp files, so no escaping for the shell (via ^)
46
  # is necessary.
47
48
  # Finally, wrap the whole thing in quotes so that the above quote rule
49
  # applies and whitespace isn't a word break.
50
  return '"' + arg + '"'
51
52
53
def EncodeRspFileList(args):
54
  """Process a list of arguments using QuoteCmdExeArgument."""
55
  # Note that the first argument is assumed to be the command. Don't add
56
  # quotes around it because then built-ins like 'echo', etc. won't work.
57
  # Take care to normpath only the path in the case of 'call ../x.bat' because
58
  # otherwise the whole thing is incorrectly interpreted as a path and not
59
  # normalized correctly.
60
  if not args: return ''
61
  if args[0].startswith('call '):
62
    call, program = args[0].split(' ', 1)
63
    program = call + ' ' + os.path.normpath(program)
64
  else:
65
    program = os.path.normpath(args[0])
66
  return program + ' ' + ' '.join(QuoteForRspFile(arg) for arg in args[1:])
67
68
69
def _GenericRetrieve(root, default, path):
70
  """Given a list of dictionary keys |path| and a tree of dicts |root|, find
71
  value at path, or return |default| if any of the path doesn't exist."""
72
  if not root:
73
    return default
74
  if not path:
75
    return root
76
  return _GenericRetrieve(root.get(path[0]), default, path[1:])
77
78
79
def _AddPrefix(element, prefix):
80
  """Add |prefix| to |element| or each subelement if element is iterable."""
81
  if element is None:
82
    return element
83
  # Note, not Iterable because we don't want to handle strings like that.
84
  if isinstance(element, list) or isinstance(element, tuple):
85
    return [prefix + e for e in element]
86
  else:
87
    return prefix + element
88
89
90
def _DoRemapping(element, map):
91
  """If |element| then remap it through |map|. If |element| is iterable then
92
  each item will be remapped. Any elements not found will be removed."""
93
  if map is not None and element is not None:
94
    if not callable(map):
95
      map = map.get # Assume it's a dict, otherwise a callable to do the remap.
96
    if isinstance(element, list) or isinstance(element, tuple):
97
      element = filter(None, [map(elem) for elem in element])
98
    else:
99
      element = map(element)
100
  return element
101
102
103
def _AppendOrReturn(append, element):
104
  """If |append| is None, simply return |element|. If |append| is not None,
105
  then add |element| to it, adding each item in |element| if it's a list or
106
  tuple."""
107
  if append is not None and element is not None:
108
    if isinstance(element, list) or isinstance(element, tuple):
109
      append.extend(element)
110
    else:
111
      append.append(element)
112
  else:
113
    return element
114
115
116
def _FindDirectXInstallation():
117
  """Try to find an installation location for the DirectX SDK. Check for the
118
  standard environment variable, and if that doesn't exist, try to find
119
  via the registry. May return None if not found in either location."""
120
  # Return previously calculated value, if there is one
121
  if hasattr(_FindDirectXInstallation, 'dxsdk_dir'):
122
    return _FindDirectXInstallation.dxsdk_dir
123
124
  dxsdk_dir = os.environ.get('DXSDK_DIR')
125
  if not dxsdk_dir:
126
    # Setup params to pass to and attempt to launch reg.exe.
127
    cmd = ['reg.exe', 'query', r'HKLM\Software\Microsoft\DirectX', '/s']
128
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
129
    for line in p.communicate()[0].splitlines():
130
      if 'InstallPath' in line:
131
        dxsdk_dir = line.split('    ')[3] + "\\"
132
133
  # Cache return value
134
  _FindDirectXInstallation.dxsdk_dir = dxsdk_dir
135
  return dxsdk_dir
136
137
138
def GetGlobalVSMacroEnv(vs_version):
139
  """Get a dict of variables mapping internal VS macro names to their gyp
140
  equivalents. Returns all variables that are independent of the target."""
141
  env = {}
142
  # '$(VSInstallDir)' and '$(VCInstallDir)' are available when and only when
143
  # Visual Studio is actually installed.
144
  if vs_version.Path():
145
    env['$(VSInstallDir)'] = vs_version.Path()
146
    env['$(VCInstallDir)'] = os.path.join(vs_version.Path(), 'VC') + '\\'
147
  # Chromium uses DXSDK_DIR in include/lib paths, but it may or may not be
148
  # set. This happens when the SDK is sync'd via src-internal, rather than
149
  # by typical end-user installation of the SDK. If it's not set, we don't
150
  # want to leave the unexpanded variable in the path, so simply strip it.
151
  dxsdk_dir = _FindDirectXInstallation()
152
  env['$(DXSDK_DIR)'] = dxsdk_dir if dxsdk_dir else ''
153
  # Try to find an installation location for the Windows DDK by checking
154
  # the WDK_DIR environment variable, may be None.
155
  env['$(WDK_DIR)'] = os.environ.get('WDK_DIR', '')
156
  return env
157
158
def ExtractSharedMSVSSystemIncludes(configs, generator_flags):
159
  """Finds msvs_system_include_dirs that are common to all targets, removes
160
  them from all targets, and returns an OrderedSet containing them."""
161
  all_system_includes = OrderedSet(
162
      configs[0].get('msvs_system_include_dirs', []))
163
  for config in configs[1:]:
164
    system_includes = config.get('msvs_system_include_dirs', [])
165
    all_system_includes = all_system_includes & OrderedSet(system_includes)
166
  if not all_system_includes:
167
    return None
168
  # Expand macros in all_system_includes.
169
  env = GetGlobalVSMacroEnv(GetVSVersion(generator_flags))
170
  expanded_system_includes = OrderedSet([ExpandMacros(include, env)
171
                                         for include in all_system_includes])
172
  if any(['$' in include for include in expanded_system_includes]):
173
    # Some path relies on target-specific variables, bail.
174
    return None
175
176
  # Remove system includes shared by all targets from the targets.
177
  for config in configs:
178
    includes = config.get('msvs_system_include_dirs', [])
179
    if includes:  # Don't insert a msvs_system_include_dirs key if not needed.
180
      # This must check the unexpanded includes list:
181
      new_includes = [i for i in includes if i not in all_system_includes]
182
      config['msvs_system_include_dirs'] = new_includes
183
  return expanded_system_includes
184
185
186
class MsvsSettings(object):
187
  """A class that understands the gyp 'msvs_...' values (especially the
188
  msvs_settings field). They largely correpond to the VS2008 IDE DOM. This
189
  class helps map those settings to command line options."""
190
191
  def __init__(self, spec, generator_flags):
192
    self.spec = spec
193
    self.vs_version = GetVSVersion(generator_flags)
194
195
    supported_fields = [
196
        ('msvs_configuration_attributes', dict),
197
        ('msvs_settings', dict),
198
        ('msvs_system_include_dirs', list),
199
        ('msvs_disabled_warnings', list),
200
        ('msvs_precompiled_header', str),
201
        ('msvs_precompiled_source', str),
202
        ('msvs_configuration_platform', str),
203
        ('msvs_target_platform', str),
204
        ]
205
    configs = spec['configurations']
206
    for field, default in supported_fields:
207
      setattr(self, field, {})
208
      for configname, config in configs.iteritems():
209
        getattr(self, field)[configname] = config.get(field, default())
210
211
    self.msvs_cygwin_dirs = spec.get('msvs_cygwin_dirs', ['.'])
212
213
    unsupported_fields = [
214
        'msvs_prebuild',
215
        'msvs_postbuild',
216
    ]
217
    unsupported = []
218
    for field in unsupported_fields:
219
      for config in configs.values():
220
        if field in config:
221
          unsupported += ["%s not supported (target %s)." %
222
                          (field, spec['target_name'])]
223
    if unsupported:
224
      raise Exception('\n'.join(unsupported))
225
226
  def GetExtension(self):
227
    """Returns the extension for the target, with no leading dot.
228
229
    Uses 'product_extension' if specified, otherwise uses MSVS defaults based on
230
    the target type.
231
    """
232
    ext = self.spec.get('product_extension', None)
233
    if ext:
234
      return ext
235
    return gyp.MSVSUtil.TARGET_TYPE_EXT.get(self.spec['type'], '')
236
237
  def GetVSMacroEnv(self, base_to_build=None, config=None):
238
    """Get a dict of variables mapping internal VS macro names to their gyp
239
    equivalents."""
240
    target_platform = 'Win32' if self.GetArch(config) == 'x86' else 'x64'
241
    target_name = self.spec.get('product_prefix', '') + \
242
        self.spec.get('product_name', self.spec['target_name'])
243
    target_dir = base_to_build + '\\' if base_to_build else ''
244
    target_ext = '.' + self.GetExtension()
245
    target_file_name = target_name + target_ext
246
247
    replacements = {
248
        '$(InputName)': '${root}',
249
        '$(InputPath)': '${source}',
250
        '$(IntDir)': '$!INTERMEDIATE_DIR',
251
        '$(OutDir)\\': target_dir,
252
        '$(PlatformName)': target_platform,
253
        '$(ProjectDir)\\': '',
254
        '$(ProjectName)': self.spec['target_name'],
255
        '$(TargetDir)\\': target_dir,
256
        '$(TargetExt)': target_ext,
257
        '$(TargetFileName)': target_file_name,
258
        '$(TargetName)': target_name,
259
        '$(TargetPath)': os.path.join(target_dir, target_file_name),
260
    }
261
    replacements.update(GetGlobalVSMacroEnv(self.vs_version))
262
    return replacements
263
264
  def ConvertVSMacros(self, s, base_to_build=None, config=None):
265
    """Convert from VS macro names to something equivalent."""
266
    env = self.GetVSMacroEnv(base_to_build, config=config)
267
    return ExpandMacros(s, env)
268
269
  def AdjustLibraries(self, libraries):
270
    """Strip -l from library if it's specified with that."""
271
    libs = [lib[2:] if lib.startswith('-l') else lib for lib in libraries]
272
    return [lib + '.lib' if not lib.endswith('.lib') else lib for lib in libs]
273
274
  def _GetAndMunge(self, field, path, default, prefix, append, map):
275
    """Retrieve a value from |field| at |path| or return |default|. If
276
    |append| is specified, and the item is found, it will be appended to that
277
    object instead of returned. If |map| is specified, results will be
278
    remapped through |map| before being returned or appended."""
279
    result = _GenericRetrieve(field, default, path)
280
    result = _DoRemapping(result, map)
281
    result = _AddPrefix(result, prefix)
282
    return _AppendOrReturn(append, result)
283
284
  class _GetWrapper(object):
285
    def __init__(self, parent, field, base_path, append=None):
286
      self.parent = parent
287
      self.field = field
288
      self.base_path = [base_path]
289
      self.append = append
290
    def __call__(self, name, map=None, prefix='', default=None):
291
      return self.parent._GetAndMunge(self.field, self.base_path + [name],
292
          default=default, prefix=prefix, append=self.append, map=map)
293
294
  def GetArch(self, config):
295
    """Get architecture based on msvs_configuration_platform and
296
    msvs_target_platform. Returns either 'x86' or 'x64'."""
297
    configuration_platform = self.msvs_configuration_platform.get(config, '')
298
    platform = self.msvs_target_platform.get(config, '')
299
    if not platform: # If no specific override, use the configuration's.
300
      platform = configuration_platform
301
    # Map from platform to architecture.
302
    return {'Win32': 'x86', 'x64': 'x64'}.get(platform, 'x86')
303
304
  def _TargetConfig(self, config):
305
    """Returns the target-specific configuration."""
306
    # There's two levels of architecture/platform specification in VS. The
307
    # first level is globally for the configuration (this is what we consider
308
    # "the" config at the gyp level, which will be something like 'Debug' or
309
    # 'Release_x64'), and a second target-specific configuration, which is an
310
    # override for the global one. |config| is remapped here to take into
311
    # account the local target-specific overrides to the global configuration.
312
    arch = self.GetArch(config)
313
    if arch == 'x64' and not config.endswith('_x64'):
314
      config += '_x64'
315
    if arch == 'x86' and config.endswith('_x64'):
316
      config = config.rsplit('_', 1)[0]
317
    return config
318
319
  def _Setting(self, path, config,
320
              default=None, prefix='', append=None, map=None):
321
    """_GetAndMunge for msvs_settings."""
322
    return self._GetAndMunge(
323
        self.msvs_settings[config], path, default, prefix, append, map)
324
325
  def _ConfigAttrib(self, path, config,
326
                   default=None, prefix='', append=None, map=None):
327
    """_GetAndMunge for msvs_configuration_attributes."""
328
    return self._GetAndMunge(
329
        self.msvs_configuration_attributes[config],
330
        path, default, prefix, append, map)
331
332
  def AdjustIncludeDirs(self, include_dirs, config):
333
    """Updates include_dirs to expand VS specific paths, and adds the system
334
    include dirs used for platform SDK and similar."""
335
    config = self._TargetConfig(config)
336
    includes = include_dirs + self.msvs_system_include_dirs[config]
337
    includes.extend(self._Setting(
338
      ('VCCLCompilerTool', 'AdditionalIncludeDirectories'), config, default=[]))
339
    return [self.ConvertVSMacros(p, config=config) for p in includes]
340
341
  def AdjustMidlIncludeDirs(self, midl_include_dirs, config):
342
    """Updates midl_include_dirs to expand VS specific paths, and adds the
343
    system include dirs used for platform SDK and similar."""
344
    config = self._TargetConfig(config)
345
    includes = midl_include_dirs + self.msvs_system_include_dirs[config]
346
    includes.extend(self._Setting(
347
      ('VCMIDLTool', 'AdditionalIncludeDirectories'), config, default=[]))
348
    return [self.ConvertVSMacros(p, config=config) for p in includes]
349
350
  def GetComputedDefines(self, config):
351
    """Returns the set of defines that are injected to the defines list based
352
    on other VS settings."""
353
    config = self._TargetConfig(config)
354
    defines = []
355
    if self._ConfigAttrib(['CharacterSet'], config) == '1':
356
      defines.extend(('_UNICODE', 'UNICODE'))
357
    if self._ConfigAttrib(['CharacterSet'], config) == '2':
358
      defines.append('_MBCS')
359
    defines.extend(self._Setting(
360
        ('VCCLCompilerTool', 'PreprocessorDefinitions'), config, default=[]))
361
    return defines
362
363
  def GetCompilerPdbName(self, config, expand_special):
364
    """Get the pdb file name that should be used for compiler invocations, or
365
    None if there's no explicit name specified."""
366
    config = self._TargetConfig(config)
367
    pdbname = self._Setting(
368
        ('VCCLCompilerTool', 'ProgramDataBaseFileName'), config)
369
    if pdbname:
370
      pdbname = expand_special(self.ConvertVSMacros(pdbname))
371
    return pdbname
372
373
  def GetMapFileName(self, config, expand_special):
374
    """Gets the explicitly overriden map file name for a target or returns None
375
    if it's not set."""
376
    config = self._TargetConfig(config)
377
    map_file = self._Setting(('VCLinkerTool', 'MapFileName'), config)
378
    if map_file:
379
      map_file = expand_special(self.ConvertVSMacros(map_file, config=config))
380
    return map_file
381
382
  def GetOutputName(self, config, expand_special):
383
    """Gets the explicitly overridden output name for a target or returns None
384
    if it's not overridden."""
385
    config = self._TargetConfig(config)
386
    type = self.spec['type']
387
    root = 'VCLibrarianTool' if type == 'static_library' else 'VCLinkerTool'
388
    # TODO(scottmg): Handle OutputDirectory without OutputFile.
389
    output_file = self._Setting((root, 'OutputFile'), config)
390
    if output_file:
391
      output_file = expand_special(self.ConvertVSMacros(
392
          output_file, config=config))
393
    return output_file
394
395
  def GetPDBName(self, config, expand_special, default):
396
    """Gets the explicitly overridden pdb name for a target or returns
397
    default if it's not overridden, or if no pdb will be generated."""
398
    config = self._TargetConfig(config)
399
    output_file = self._Setting(('VCLinkerTool', 'ProgramDatabaseFile'), config)
400
    generate_debug_info = self._Setting(
401
        ('VCLinkerTool', 'GenerateDebugInformation'), config)
402
    if generate_debug_info == 'true':
403
      if output_file:
404
        return expand_special(self.ConvertVSMacros(output_file, config=config))
405
      else:
406
        return default
407
    else:
408
      return None
409
410
  def GetNoImportLibrary(self, config):
411
    """If NoImportLibrary: true, ninja will not expect the output to include
412
    an import library."""
413
    config = self._TargetConfig(config)
414
    noimplib = self._Setting(('NoImportLibrary',), config)
415
    return noimplib == 'true'
416
417
  def GetAsmflags(self, config):
418
    """Returns the flags that need to be added to ml invocations."""
419
    config = self._TargetConfig(config)
420
    asmflags = []
421
    safeseh = self._Setting(('MASM', 'UseSafeExceptionHandlers'), config)
422
    if safeseh == 'true':
423
      asmflags.append('/safeseh')
424
    return asmflags
425
426
  def GetCflags(self, config):
427
    """Returns the flags that need to be added to .c and .cc compilations."""
428
    config = self._TargetConfig(config)
429
    cflags = []
430
    cflags.extend(['/wd' + w for w in self.msvs_disabled_warnings[config]])
431
    cl = self._GetWrapper(self, self.msvs_settings[config],
432
                          'VCCLCompilerTool', append=cflags)
433
    cl('Optimization',
434
       map={'0': 'd', '1': '1', '2': '2', '3': 'x'}, prefix='/O', default='2')
435
    cl('InlineFunctionExpansion', prefix='/Ob')
436
    cl('DisableSpecificWarnings', prefix='/wd')
437
    cl('StringPooling', map={'true': '/GF'})
438
    cl('EnableFiberSafeOptimizations', map={'true': '/GT'})
439
    cl('OmitFramePointers', map={'false': '-', 'true': ''}, prefix='/Oy')
440
    cl('EnableIntrinsicFunctions', map={'false': '-', 'true': ''}, prefix='/Oi')
441
    cl('FavorSizeOrSpeed', map={'1': 't', '2': 's'}, prefix='/O')
442
    cl('FloatingPointModel',
443
        map={'0': 'precise', '1': 'strict', '2': 'fast'}, prefix='/fp:',
444
        default='0')
445
    cl('CompileAsManaged', map={'false': '', 'true': '/clr'})
446
    cl('WholeProgramOptimization', map={'true': '/GL'})
447
    cl('WarningLevel', prefix='/W')
448
    cl('WarnAsError', map={'true': '/WX'})
449
    cl('CallingConvention',
450
        map={'0': 'd', '1': 'r', '2': 'z', '3': 'v'}, prefix='/G')
451
    cl('DebugInformationFormat',
452
        map={'1': '7', '3': 'i', '4': 'I'}, prefix='/Z')
453
    cl('RuntimeTypeInfo', map={'true': '/GR', 'false': '/GR-'})
454
    cl('EnableFunctionLevelLinking', map={'true': '/Gy', 'false': '/Gy-'})
455
    cl('MinimalRebuild', map={'true': '/Gm'})
456
    cl('BufferSecurityCheck', map={'true': '/GS', 'false': '/GS-'})
457
    cl('BasicRuntimeChecks', map={'1': 's', '2': 'u', '3': '1'}, prefix='/RTC')
458
    cl('RuntimeLibrary',
459
        map={'0': 'T', '1': 'Td', '2': 'D', '3': 'Dd'}, prefix='/M')
460
    cl('ExceptionHandling', map={'1': 'sc','2': 'a'}, prefix='/EH')
461
    cl('DefaultCharIsUnsigned', map={'true': '/J'})
462
    cl('TreatWChar_tAsBuiltInType',
463
        map={'false': '-', 'true': ''}, prefix='/Zc:wchar_t')
464
    cl('EnablePREfast', map={'true': '/analyze'})
465
    cl('AdditionalOptions', prefix='')
466
    cl('EnableEnhancedInstructionSet',
467
        map={'1': 'SSE', '2': 'SSE2', '3': 'AVX', '4': 'IA32', '5': 'AVX2'},
468
        prefix='/arch:')
469
    cflags.extend(['/FI' + f for f in self._Setting(
470
        ('VCCLCompilerTool', 'ForcedIncludeFiles'), config, default=[])])
471
    if self.vs_version.short_name in ('2013', '2013e', '2015'):
472
      # New flag required in 2013 to maintain previous PDB behavior.
473
      cflags.append('/FS')
474
    # ninja handles parallelism by itself, don't have the compiler do it too.
475
    cflags = filter(lambda x: not x.startswith('/MP'), cflags)
476
    return cflags
477
478
  def _GetPchFlags(self, config, extension):
479
    """Get the flags to be added to the cflags for precompiled header support.
480
    """
481
    config = self._TargetConfig(config)
482
    # The PCH is only built once by a particular source file. Usage of PCH must
483
    # only be for the same language (i.e. C vs. C++), so only include the pch
484
    # flags when the language matches.
485
    if self.msvs_precompiled_header[config]:
486
      source_ext = os.path.splitext(self.msvs_precompiled_source[config])[1]
487
      if _LanguageMatchesForPch(source_ext, extension):
488
        pch = os.path.split(self.msvs_precompiled_header[config])[1]
489
        return ['/Yu' + pch, '/FI' + pch, '/Fp${pchprefix}.' + pch + '.pch']
490
    return  []
491
492
  def GetCflagsC(self, config):
493
    """Returns the flags that need to be added to .c compilations."""
494
    config = self._TargetConfig(config)
495
    return self._GetPchFlags(config, '.c')
496
497
  def GetCflagsCC(self, config):
498
    """Returns the flags that need to be added to .cc compilations."""
499
    config = self._TargetConfig(config)
500
    return ['/TP'] + self._GetPchFlags(config, '.cc')
501
502
  def _GetAdditionalLibraryDirectories(self, root, config, gyp_to_build_path):
503
    """Get and normalize the list of paths in AdditionalLibraryDirectories
504
    setting."""
505
    config = self._TargetConfig(config)
506
    libpaths = self._Setting((root, 'AdditionalLibraryDirectories'),
507
                             config, default=[])
508
    libpaths = [os.path.normpath(
509
                    gyp_to_build_path(self.ConvertVSMacros(p, config=config)))
510
                for p in libpaths]
511
    return ['/LIBPATH:"' + p + '"' for p in libpaths]
512
513
  def GetLibFlags(self, config, gyp_to_build_path):
514
    """Returns the flags that need to be added to lib commands."""
515
    config = self._TargetConfig(config)
516
    libflags = []
517
    lib = self._GetWrapper(self, self.msvs_settings[config],
518
                          'VCLibrarianTool', append=libflags)
519
    libflags.extend(self._GetAdditionalLibraryDirectories(
520
        'VCLibrarianTool', config, gyp_to_build_path))
521
    lib('LinkTimeCodeGeneration', map={'true': '/LTCG'})
522
    lib('TargetMachine', map={'1': 'X86', '17': 'X64', '3': 'ARM'},
523
        prefix='/MACHINE:')
524
    lib('AdditionalOptions')
525
    return libflags
526
527
  def GetDefFile(self, gyp_to_build_path):
528
    """Returns the .def file from sources, if any.  Otherwise returns None."""
529
    spec = self.spec
530
    if spec['type'] in ('shared_library', 'loadable_module', 'executable'):
531
      def_files = [s for s in spec.get('sources', []) if s.endswith('.def')]
532
      if len(def_files) == 1:
533
        return gyp_to_build_path(def_files[0])
534
      elif len(def_files) > 1:
535
        raise Exception("Multiple .def files")
536
    return None
537
538
  def _GetDefFileAsLdflags(self, ldflags, gyp_to_build_path):
539
    """.def files get implicitly converted to a ModuleDefinitionFile for the
540
    linker in the VS generator. Emulate that behaviour here."""
541
    def_file = self.GetDefFile(gyp_to_build_path)
542
    if def_file:
543
      ldflags.append('/DEF:"%s"' % def_file)
544
545
  def GetPGDName(self, config, expand_special):
546
    """Gets the explicitly overridden pgd name for a target or returns None
547
    if it's not overridden."""
548
    config = self._TargetConfig(config)
549
    output_file = self._Setting(
550
        ('VCLinkerTool', 'ProfileGuidedDatabase'), config)
551
    if output_file:
552
      output_file = expand_special(self.ConvertVSMacros(
553
          output_file, config=config))
554
    return output_file
555
556
  def GetLdflags(self, config, gyp_to_build_path, expand_special,
557
                 manifest_base_name, output_name, is_executable, build_dir):
558
    """Returns the flags that need to be added to link commands, and the
559
    manifest files."""
560
    config = self._TargetConfig(config)
561
    ldflags = []
562
    ld = self._GetWrapper(self, self.msvs_settings[config],
563
                          'VCLinkerTool', append=ldflags)
564
    self._GetDefFileAsLdflags(ldflags, gyp_to_build_path)
565
    ld('GenerateDebugInformation', map={'true': '/DEBUG'})
566
    ld('TargetMachine', map={'1': 'X86', '17': 'X64', '3': 'ARM'},
567
       prefix='/MACHINE:')
568
    ldflags.extend(self._GetAdditionalLibraryDirectories(
569
        'VCLinkerTool', config, gyp_to_build_path))
570
    ld('DelayLoadDLLs', prefix='/DELAYLOAD:')
571
    ld('TreatLinkerWarningAsErrors', prefix='/WX',
572
       map={'true': '', 'false': ':NO'})
573
    out = self.GetOutputName(config, expand_special)
574
    if out:
575
      ldflags.append('/OUT:' + out)
576
    pdb = self.GetPDBName(config, expand_special, output_name + '.pdb')
577
    if pdb:
578
      ldflags.append('/PDB:' + pdb)
579
    pgd = self.GetPGDName(config, expand_special)
580
    if pgd:
581
      ldflags.append('/PGD:' + pgd)
582
    map_file = self.GetMapFileName(config, expand_special)
583
    ld('GenerateMapFile', map={'true': '/MAP:' + map_file if map_file
584
        else '/MAP'})
585
    ld('MapExports', map={'true': '/MAPINFO:EXPORTS'})
586
    ld('AdditionalOptions', prefix='')
587
588
    minimum_required_version = self._Setting(
589
        ('VCLinkerTool', 'MinimumRequiredVersion'), config, default='')
590
    if minimum_required_version:
591
      minimum_required_version = ',' + minimum_required_version
592
    ld('SubSystem',
593
       map={'1': 'CONSOLE%s' % minimum_required_version,
594
            '2': 'WINDOWS%s' % minimum_required_version},
595
       prefix='/SUBSYSTEM:')
596
597
    stack_reserve_size = self._Setting(
598
        ('VCLinkerTool', 'StackReserveSize'), config, default='')
599
    if stack_reserve_size:
600
      stack_commit_size = self._Setting(
601
          ('VCLinkerTool', 'StackCommitSize'), config, default='')
602
      if stack_commit_size:
603
        stack_commit_size = ',' + stack_commit_size
604
      ldflags.append('/STACK:%s%s' % (stack_reserve_size, stack_commit_size))
605
606
    ld('TerminalServerAware', map={'1': ':NO', '2': ''}, prefix='/TSAWARE')
607
    ld('LinkIncremental', map={'1': ':NO', '2': ''}, prefix='/INCREMENTAL')
608
    ld('BaseAddress', prefix='/BASE:')
609
    ld('FixedBaseAddress', map={'1': ':NO', '2': ''}, prefix='/FIXED')
610
    ld('RandomizedBaseAddress',
611
        map={'1': ':NO', '2': ''}, prefix='/DYNAMICBASE')
612
    ld('DataExecutionPrevention',
613
        map={'1': ':NO', '2': ''}, prefix='/NXCOMPAT')
614
    ld('OptimizeReferences', map={'1': 'NOREF', '2': 'REF'}, prefix='/OPT:')
615
    ld('ForceSymbolReferences', prefix='/INCLUDE:')
616
    ld('EnableCOMDATFolding', map={'1': 'NOICF', '2': 'ICF'}, prefix='/OPT:')
617
    ld('LinkTimeCodeGeneration',
618
        map={'1': '', '2': ':PGINSTRUMENT', '3': ':PGOPTIMIZE',
619
             '4': ':PGUPDATE'},
620
        prefix='/LTCG')
621
    ld('IgnoreDefaultLibraryNames', prefix='/NODEFAULTLIB:')
622
    ld('ResourceOnlyDLL', map={'true': '/NOENTRY'})
623
    ld('EntryPointSymbol', prefix='/ENTRY:')
624
    ld('Profile', map={'true': '/PROFILE'})
625
    ld('LargeAddressAware',
626
        map={'1': ':NO', '2': ''}, prefix='/LARGEADDRESSAWARE')
627
    # TODO(scottmg): This should sort of be somewhere else (not really a flag).
628
    ld('AdditionalDependencies', prefix='')
629
630
    if self.GetArch(config) == 'x86':
631
      safeseh_default = 'true'
632
    else:
633
      safeseh_default = None
634
    ld('ImageHasSafeExceptionHandlers',
635
        map={'false': ':NO', 'true': ''}, prefix='/SAFESEH',
636
        default=safeseh_default)
637
638
    # If the base address is not specifically controlled, DYNAMICBASE should
639
    # be on by default.
640
    base_flags = filter(lambda x: 'DYNAMICBASE' in x or x == '/FIXED',
641
                        ldflags)
642
    if not base_flags:
643
      ldflags.append('/DYNAMICBASE')
644
645
    # If the NXCOMPAT flag has not been specified, default to on. Despite the
646
    # documentation that says this only defaults to on when the subsystem is
647
    # Vista or greater (which applies to the linker), the IDE defaults it on
648
    # unless it's explicitly off.
649
    if not filter(lambda x: 'NXCOMPAT' in x, ldflags):
650
      ldflags.append('/NXCOMPAT')
651
652
    have_def_file = filter(lambda x: x.startswith('/DEF:'), ldflags)
653
    manifest_flags, intermediate_manifest, manifest_files = \
654
        self._GetLdManifestFlags(config, manifest_base_name, gyp_to_build_path,
655
                                 is_executable and not have_def_file, build_dir)
656
    ldflags.extend(manifest_flags)
657
    return ldflags, intermediate_manifest, manifest_files
658
659
  def _GetLdManifestFlags(self, config, name, gyp_to_build_path,
660
                          allow_isolation, build_dir):
661
    """Returns a 3-tuple:
662
    - the set of flags that need to be added to the link to generate
663
      a default manifest
664
    - the intermediate manifest that the linker will generate that should be
665
      used to assert it doesn't add anything to the merged one.
666
    - the list of all the manifest files to be merged by the manifest tool and
667
      included into the link."""
668
    generate_manifest = self._Setting(('VCLinkerTool', 'GenerateManifest'),
669
                                      config,
670
                                      default='true')
671
    if generate_manifest != 'true':
672
      # This means not only that the linker should not generate the intermediate
673
      # manifest but also that the manifest tool should do nothing even when
674
      # additional manifests are specified.
675
      return ['/MANIFEST:NO'], [], []
676
677
    output_name = name + '.intermediate.manifest'
678
    flags = [
679
      '/MANIFEST',
680
      '/ManifestFile:' + output_name,
681
    ]
682
683
    # Instead of using the MANIFESTUAC flags, we generate a .manifest to
684
    # include into the list of manifests. This allows us to avoid the need to
685
    # do two passes during linking. The /MANIFEST flag and /ManifestFile are
686
    # still used, and the intermediate manifest is used to assert that the
687
    # final manifest we get from merging all the additional manifest files
688
    # (plus the one we generate here) isn't modified by merging the
689
    # intermediate into it.
690
691
    # Always NO, because we generate a manifest file that has what we want.
692
    flags.append('/MANIFESTUAC:NO')
693
694
    config = self._TargetConfig(config)
695
    enable_uac = self._Setting(('VCLinkerTool', 'EnableUAC'), config,
696
                               default='true')
697
    manifest_files = []
698
    generated_manifest_outer = \
699
"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>" \
700
"<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>%s" \
701
"</assembly>"
702
    if enable_uac == 'true':
703
      execution_level = self._Setting(('VCLinkerTool', 'UACExecutionLevel'),
704
                                      config, default='0')
705
      execution_level_map = {
706
        '0': 'asInvoker',
707
        '1': 'highestAvailable',
708
        '2': 'requireAdministrator'
709
      }
710
711
      ui_access = self._Setting(('VCLinkerTool', 'UACUIAccess'), config,
712
                                default='false')
713
714
      inner = '''
715
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
716
  <security>
717
    <requestedPrivileges>
718
      <requestedExecutionLevel level='%s' uiAccess='%s' />
719
    </requestedPrivileges>
720
  </security>
721
</trustInfo>''' % (execution_level_map[execution_level], ui_access)
722
    else:
723
      inner = ''
724
725
    generated_manifest_contents = generated_manifest_outer % inner
726
    generated_name = name + '.generated.manifest'
727
    # Need to join with the build_dir here as we're writing it during
728
    # generation time, but we return the un-joined version because the build
729
    # will occur in that directory. We only write the file if the contents
730
    # have changed so that simply regenerating the project files doesn't
731
    # cause a relink.
732
    build_dir_generated_name = os.path.join(build_dir, generated_name)
733
    gyp.common.EnsureDirExists(build_dir_generated_name)
734
    f = gyp.common.WriteOnDiff(build_dir_generated_name)
735
    f.write(generated_manifest_contents)
736
    f.close()
737
    manifest_files = [generated_name]
738
739
    if allow_isolation:
740
      flags.append('/ALLOWISOLATION')
741
742
    manifest_files += self._GetAdditionalManifestFiles(config,
743
                                                       gyp_to_build_path)
744
    return flags, output_name, manifest_files
745
746
  def _GetAdditionalManifestFiles(self, config, gyp_to_build_path):
747
    """Gets additional manifest files that are added to the default one
748
    generated by the linker."""
749
    files = self._Setting(('VCManifestTool', 'AdditionalManifestFiles'), config,
750
                          default=[])
751
    if isinstance(files, str):
752
      files = files.split(';')
753
    return [os.path.normpath(
754
                gyp_to_build_path(self.ConvertVSMacros(f, config=config)))
755
            for f in files]
756
757
  def IsUseLibraryDependencyInputs(self, config):
758
    """Returns whether the target should be linked via Use Library Dependency
759
    Inputs (using component .objs of a given .lib)."""
760
    config = self._TargetConfig(config)
761
    uldi = self._Setting(('VCLinkerTool', 'UseLibraryDependencyInputs'), config)
762
    return uldi == 'true'
763
764
  def IsEmbedManifest(self, config):
765
    """Returns whether manifest should be linked into binary."""
766
    config = self._TargetConfig(config)
767
    embed = self._Setting(('VCManifestTool', 'EmbedManifest'), config,
768
                          default='true')
769
    return embed == 'true'
770
771
  def IsLinkIncremental(self, config):
772
    """Returns whether the target should be linked incrementally."""
773
    config = self._TargetConfig(config)
774
    link_inc = self._Setting(('VCLinkerTool', 'LinkIncremental'), config)
775
    return link_inc != '1'
776
777
  def GetRcflags(self, config, gyp_to_ninja_path):
778
    """Returns the flags that need to be added to invocations of the resource
779
    compiler."""
780
    config = self._TargetConfig(config)
781
    rcflags = []
782
    rc = self._GetWrapper(self, self.msvs_settings[config],
783
        'VCResourceCompilerTool', append=rcflags)
784
    rc('AdditionalIncludeDirectories', map=gyp_to_ninja_path, prefix='/I')
785
    rcflags.append('/I' + gyp_to_ninja_path('.'))
786
    rc('PreprocessorDefinitions', prefix='/d')
787
    # /l arg must be in hex without leading '0x'
788
    rc('Culture', prefix='/l', map=lambda x: hex(int(x))[2:])
789
    return rcflags
790
791
  def BuildCygwinBashCommandLine(self, args, path_to_base):
792
    """Build a command line that runs args via cygwin bash. We assume that all
793
    incoming paths are in Windows normpath'd form, so they need to be
794
    converted to posix style for the part of the command line that's passed to
795
    bash. We also have to do some Visual Studio macro emulation here because
796
    various rules use magic VS names for things. Also note that rules that
797
    contain ninja variables cannot be fixed here (for example ${source}), so
798
    the outer generator needs to make sure that the paths that are written out
799
    are in posix style, if the command line will be used here."""
800
    cygwin_dir = os.path.normpath(
801
        os.path.join(path_to_base, self.msvs_cygwin_dirs[0]))
802
    cd = ('cd %s' % path_to_base).replace('\\', '/')
803
    args = [a.replace('\\', '/').replace('"', '\\"') for a in args]
804
    args = ["'%s'" % a.replace("'", "'\\''") for a in args]
805
    bash_cmd = ' '.join(args)
806
    cmd = (
807
        'call "%s\\setup_env.bat" && set CYGWIN=nontsec && ' % cygwin_dir +
808
        'bash -c "%s ; %s"' % (cd, bash_cmd))
809
    return cmd
810
811
  def IsRuleRunUnderCygwin(self, rule):
812
    """Determine if an action should be run under cygwin. If the variable is
813
    unset, or set to 1 we use cygwin."""
814
    return int(rule.get('msvs_cygwin_shell',
815
                        self.spec.get('msvs_cygwin_shell', 1))) != 0
816
817
  def _HasExplicitRuleForExtension(self, spec, extension):
818
    """Determine if there's an explicit rule for a particular extension."""
819
    for rule in spec.get('rules', []):
820
      if rule['extension'] == extension:
821
        return True
822
    return False
823
824
  def _HasExplicitIdlActions(self, spec):
825
    """Determine if an action should not run midl for .idl files."""
826
    return any([action.get('explicit_idl_action', 0)
827
                for action in spec.get('actions', [])])
828
829
  def HasExplicitIdlRulesOrActions(self, spec):
830
    """Determine if there's an explicit rule or action for idl files. When
831
    there isn't we need to generate implicit rules to build MIDL .idl files."""
832
    return (self._HasExplicitRuleForExtension(spec, 'idl') or
833
            self._HasExplicitIdlActions(spec))
834
835
  def HasExplicitAsmRules(self, spec):
836
    """Determine if there's an explicit rule for asm files. When there isn't we
837
    need to generate implicit rules to assemble .asm files."""
838
    return self._HasExplicitRuleForExtension(spec, 'asm')
839
840
  def GetIdlBuildData(self, source, config):
841
    """Determine the implicit outputs for an idl file. Returns output
842
    directory, outputs, and variables and flags that are required."""
843
    config = self._TargetConfig(config)
844
    midl_get = self._GetWrapper(self, self.msvs_settings[config], 'VCMIDLTool')
845
    def midl(name, default=None):
846
      return self.ConvertVSMacros(midl_get(name, default=default),
847
                                  config=config)
848
    tlb = midl('TypeLibraryName', default='${root}.tlb')
849
    header = midl('HeaderFileName', default='${root}.h')
850
    dlldata = midl('DLLDataFileName', default='dlldata.c')
851
    iid = midl('InterfaceIdentifierFileName', default='${root}_i.c')
852
    proxy = midl('ProxyFileName', default='${root}_p.c')
853
    # Note that .tlb is not included in the outputs as it is not always
854
    # generated depending on the content of the input idl file.
855
    outdir = midl('OutputDirectory', default='')
856
    output = [header, dlldata, iid, proxy]
857
    variables = [('tlb', tlb),
858
                 ('h', header),
859
                 ('dlldata', dlldata),
860
                 ('iid', iid),
861
                 ('proxy', proxy)]
862
    # TODO(scottmg): Are there configuration settings to set these flags?
863
    target_platform = 'win32' if self.GetArch(config) == 'x86' else 'x64'
864
    flags = ['/char', 'signed', '/env', target_platform, '/Oicf']
865
    return outdir, output, variables, flags
866
867
868
def _LanguageMatchesForPch(source_ext, pch_source_ext):
869
  c_exts = ('.c',)
870
  cc_exts = ('.cc', '.cxx', '.cpp')
871
  return ((source_ext in c_exts and pch_source_ext in c_exts) or
872
          (source_ext in cc_exts and pch_source_ext in cc_exts))
873
874
875
class PrecompiledHeader(object):
876
  """Helper to generate dependencies and build rules to handle generation of
877
  precompiled headers. Interface matches the GCH handler in xcode_emulation.py.
878
  """
879
  def __init__(
880
      self, settings, config, gyp_to_build_path, gyp_to_unique_output, obj_ext):
881
    self.settings = settings
882
    self.config = config
883
    pch_source = self.settings.msvs_precompiled_source[self.config]
884
    self.pch_source = gyp_to_build_path(pch_source)
885
    filename, _ = os.path.splitext(pch_source)
886
    self.output_obj = gyp_to_unique_output(filename + obj_ext).lower()
887
888
  def _PchHeader(self):
889
    """Get the header that will appear in an #include line for all source
890
    files."""
891
    return os.path.split(self.settings.msvs_precompiled_header[self.config])[1]
892
893
  def GetObjDependencies(self, sources, objs, arch):
894
    """Given a list of sources files and the corresponding object files,
895
    returns a list of the pch files that should be depended upon. The
896
    additional wrapping in the return value is for interface compatibility
897
    with make.py on Mac, and xcode_emulation.py."""
898
    assert arch is None
899
    if not self._PchHeader():
900
      return []
901
    pch_ext = os.path.splitext(self.pch_source)[1]
902
    for source in sources:
903
      if _LanguageMatchesForPch(os.path.splitext(source)[1], pch_ext):
904
        return [(None, None, self.output_obj)]
905
    return []
906
907
  def GetPchBuildCommands(self, arch):
908
    """Not used on Windows as there are no additional build steps required
909
    (instead, existing steps are modified in GetFlagsModifications below)."""
910
    return []
911
912
  def GetFlagsModifications(self, input, output, implicit, command,
913
                            cflags_c, cflags_cc, expand_special):
914
    """Get the modified cflags and implicit dependencies that should be used
915
    for the pch compilation step."""
916
    if input == self.pch_source:
917
      pch_output = ['/Yc' + self._PchHeader()]
918
      if command == 'cxx':
919
        return ([('cflags_cc', map(expand_special, cflags_cc + pch_output))],
920
                self.output_obj, [])
921
      elif command == 'cc':
922
        return ([('cflags_c', map(expand_special, cflags_c + pch_output))],
923
                self.output_obj, [])
924
    return [], output, implicit
925
926
927
vs_version = None
928
def GetVSVersion(generator_flags):
929
  global vs_version
930
  if not vs_version:
931
    vs_version = gyp.MSVSVersion.SelectVisualStudioVersion(
932
        generator_flags.get('msvs_version', 'auto'),
933
        allow_fallback=False)
934
  return vs_version
935
936
def _GetVsvarsSetupArgs(generator_flags, arch):
937
  vs = GetVSVersion(generator_flags)
938
  return vs.SetupScript()
939
940
def ExpandMacros(string, expansions):
941
  """Expand $(Variable) per expansions dict. See MsvsSettings.GetVSMacroEnv
942
  for the canonical way to retrieve a suitable dict."""
943
  if '$' in string:
944
    for old, new in expansions.iteritems():
945
      assert '$(' not in new, new
946
      string = string.replace(old, new)
947
  return string
948
949
def _ExtractImportantEnvironment(output_of_set):
950
  """Extracts environment variables required for the toolchain to run from
951
  a textual dump output by the cmd.exe 'set' command."""
952
  envvars_to_save = (
953
      'goma_.*', # TODO(scottmg): This is ugly, but needed for goma.
954
      'include',
955
      'lib',
956
      'libpath',
957
      'path',
958
      'pathext',
959
      'systemroot',
960
      'temp',
961
      'tmp',
962
      )
963
  env = {}
964
  for line in output_of_set.splitlines():
965
    for envvar in envvars_to_save:
966
      if re.match(envvar + '=', line.lower()):
967
        var, setting = line.split('=', 1)
968
        if envvar == 'path':
969
          # Our own rules (for running gyp-win-tool) and other actions in
970
          # Chromium rely on python being in the path. Add the path to this
971
          # python here so that if it's not in the path when ninja is run
972
          # later, python will still be found.
973
          setting = os.path.dirname(sys.executable) + os.pathsep + setting
974
        env[var.upper()] = setting
975
        break
976
  for required in ('SYSTEMROOT', 'TEMP', 'TMP'):
977
    if required not in env:
978
      raise Exception('Environment variable "%s" '
979
                      'required to be set to valid path' % required)
980
  return env
981
982
def _FormatAsEnvironmentBlock(envvar_dict):
983
  """Format as an 'environment block' directly suitable for CreateProcess.
984
  Briefly this is a list of key=value\0, terminated by an additional \0. See
985
  CreateProcess documentation for more details."""
986
  block = ''
987
  nul = '\0'
988
  for key, value in envvar_dict.iteritems():
989
    block += key + '=' + value + nul
990
  block += nul
991
  return block
992
993
def _ExtractCLPath(output_of_where):
994
  """Gets the path to cl.exe based on the output of calling the environment
995
  setup batch file, followed by the equivalent of `where`."""
996
  # Take the first line, as that's the first found in the PATH.
997
  for line in output_of_where.strip().splitlines():
998
    if line.startswith('LOC:'):
999
      return line[len('LOC:'):].strip()
1000
1001
def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags,
1002
                             system_includes, open_out):
1003
  """It's not sufficient to have the absolute path to the compiler, linker,
1004
  etc. on Windows, as those tools rely on .dlls being in the PATH. We also
1005
  need to support both x86 and x64 compilers within the same build (to support
1006
  msvs_target_platform hackery). Different architectures require a different
1007
  compiler binary, and different supporting environment variables (INCLUDE,
1008
  LIB, LIBPATH). So, we extract the environment here, wrap all invocations
1009
  of compiler tools (cl, link, lib, rc, midl, etc.) via win_tool.py which
1010
  sets up the environment, and then we do not prefix the compiler with
1011
  an absolute path, instead preferring something like "cl.exe" in the rule
1012
  which will then run whichever the environment setup has put in the path.
1013
  When the following procedure to generate environment files does not
1014
  meet your requirement (e.g. for custom toolchains), you can pass
1015
  "-G ninja_use_custom_environment_files" to the gyp to suppress file
1016
  generation and use custom environment files prepared by yourself."""
1017
  archs = ('x86', 'x64')
1018
  if generator_flags.get('ninja_use_custom_environment_files', 0):
1019
    cl_paths = {}
1020
    for arch in archs:
1021
      cl_paths[arch] = 'cl.exe'
1022
    return cl_paths
1023
  vs = GetVSVersion(generator_flags)
1024
  cl_paths = {}
1025
  for arch in archs:
1026
    # Extract environment variables for subprocesses.
1027
    args = vs.SetupScript(arch)
1028
    args.extend(('&&', 'set'))
1029
    popen = subprocess.Popen(
1030
        args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
1031
    variables, _ = popen.communicate()
1032
    env = _ExtractImportantEnvironment(variables)
1033
1034
    # Inject system includes from gyp files into INCLUDE.
1035
    if system_includes:
1036
      system_includes = system_includes | OrderedSet(
1037
                                              env.get('INCLUDE', '').split(';'))
1038
      env['INCLUDE'] = ';'.join(system_includes)
1039
1040
    env_block = _FormatAsEnvironmentBlock(env)
1041
    f = open_out(os.path.join(toplevel_build_dir, 'environment.' + arch), 'wb')
1042
    f.write(env_block)
1043
    f.close()
1044
1045
    # Find cl.exe location for this architecture.
1046
    args = vs.SetupScript(arch)
1047
    args.extend(('&&',
1048
      'for', '%i', 'in', '(cl.exe)', 'do', '@echo', 'LOC:%~$PATH:i'))
1049
    popen = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE)
1050
    output, _ = popen.communicate()
1051
    cl_paths[arch] = _ExtractCLPath(output)
1052
  return cl_paths
1053
1054
def VerifyMissingSources(sources, build_dir, generator_flags, gyp_to_ninja):
1055
  """Emulate behavior of msvs_error_on_missing_sources present in the msvs
1056
  generator: Check that all regular source files, i.e. not created at run time,
1057
  exist on disk. Missing files cause needless recompilation when building via
1058
  VS, and we want this check to match for people/bots that build using ninja,
1059
  so they're not surprised when the VS build fails."""
1060
  if int(generator_flags.get('msvs_error_on_missing_sources', 0)):
1061
    no_specials = filter(lambda x: '$' not in x, sources)
1062
    relative = [os.path.join(build_dir, gyp_to_ninja(s)) for s in no_specials]
1063
    missing = filter(lambda x: not os.path.exists(x), relative)
1064
    if missing:
1065
      # They'll look like out\Release\..\..\stuff\things.cc, so normalize the
1066
      # path for a slightly less crazy looking output.
1067
      cleaned_up = [os.path.normpath(x) for x in missing]
1068
      raise Exception('Missing input files:\n%s' % '\n'.join(cleaned_up))
1069
1070
# Sets some values in default_variables, which are required for many
1071
# generators, run on Windows.
1072
def CalculateCommonVariables(default_variables, params):
1073
  generator_flags = params.get('generator_flags', {})
1074
1075
  # Set a variable so conditions can be based on msvs_version.
1076
  msvs_version = gyp.msvs_emulation.GetVSVersion(generator_flags)
1077
  default_variables['MSVS_VERSION'] = msvs_version.ShortName()
1078
1079
  # To determine processor word size on Windows, in addition to checking
1080
  # PROCESSOR_ARCHITECTURE (which reflects the word size of the current
1081
  # process), it is also necessary to check PROCESSOR_ARCHITEW6432 (which
1082
  # contains the actual word size of the system when running thru WOW64).
1083
  if ('64' in os.environ.get('PROCESSOR_ARCHITECTURE', '') or
1084
      '64' in os.environ.get('PROCESSOR_ARCHITEW6432', '')):
1085
    default_variables['MSVS_OS_BITS'] = 64
1086
  else:
1087
    default_variables['MSVS_OS_BITS'] = 32
1088