"Fossies" - the Fresh Open Source Software Archive

Member "node-v12.18.4-win-x86/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/generator/msvs.py" (22 Jul 2020, 137168 Bytes) of package /windows/www/node-v12.18.4-win-x86.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

    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 from __future__ import print_function
    6 
    7 import copy
    8 import ntpath
    9 import os
   10 import posixpath
   11 import re
   12 import subprocess
   13 import sys
   14 
   15 from collections import OrderedDict
   16 
   17 import gyp.common
   18 import gyp.easy_xml as easy_xml
   19 import gyp.generator.ninja as ninja_generator
   20 import gyp.MSVSNew as MSVSNew
   21 import gyp.MSVSProject as MSVSProject
   22 import gyp.MSVSSettings as MSVSSettings
   23 import gyp.MSVSToolFile as MSVSToolFile
   24 import gyp.MSVSUserFile as MSVSUserFile
   25 import gyp.MSVSUtil as MSVSUtil
   26 import gyp.MSVSVersion as MSVSVersion
   27 from gyp.common import GypError
   28 from gyp.common import OrderedSet
   29 
   30 PY3 = bytes != str
   31 
   32 
   33 # Regular expression for validating Visual Studio GUIDs.  If the GUID
   34 # contains lowercase hex letters, MSVS will be fine. However,
   35 # IncrediBuild BuildConsole will parse the solution file, but then
   36 # silently skip building the target causing hard to track down errors.
   37 # Note that this only happens with the BuildConsole, and does not occur
   38 # if IncrediBuild is executed from inside Visual Studio.  This regex
   39 # validates that the string looks like a GUID with all uppercase hex
   40 # letters.
   41 VALID_MSVS_GUID_CHARS = re.compile(r'^[A-F0-9\-]+$')
   42 
   43 
   44 generator_default_variables = {
   45     'EXECUTABLE_PREFIX': '',
   46     'EXECUTABLE_SUFFIX': '.exe',
   47     'STATIC_LIB_PREFIX': '',
   48     'SHARED_LIB_PREFIX': '',
   49     'STATIC_LIB_SUFFIX': '.lib',
   50     'SHARED_LIB_SUFFIX': '.dll',
   51     'INTERMEDIATE_DIR': '$(IntDir)',
   52     'SHARED_INTERMEDIATE_DIR': '$(OutDir)obj/global_intermediate',
   53     'OS': 'win',
   54     'PRODUCT_DIR': '$(OutDir)',
   55     'LIB_DIR': '$(OutDir)lib',
   56     'RULE_INPUT_ROOT': '$(InputName)',
   57     'RULE_INPUT_DIRNAME': '$(InputDir)',
   58     'RULE_INPUT_EXT': '$(InputExt)',
   59     'RULE_INPUT_NAME': '$(InputFileName)',
   60     'RULE_INPUT_PATH': '$(InputPath)',
   61     'CONFIGURATION_NAME': '$(ConfigurationName)',
   62 }
   63 
   64 
   65 # The msvs specific sections that hold paths
   66 generator_additional_path_sections = [
   67     'msvs_cygwin_dirs',
   68     'msvs_props',
   69 ]
   70 
   71 
   72 generator_additional_non_configuration_keys = [
   73     'msvs_cygwin_dirs',
   74     'msvs_cygwin_shell',
   75     'msvs_large_pdb',
   76     'msvs_shard',
   77     'msvs_external_builder',
   78     'msvs_external_builder_out_dir',
   79     'msvs_external_builder_build_cmd',
   80     'msvs_external_builder_clean_cmd',
   81     'msvs_external_builder_clcompile_cmd',
   82     'msvs_enable_winrt',
   83     'msvs_requires_importlibrary',
   84     'msvs_enable_winphone',
   85     'msvs_enable_marmasm',
   86     'msvs_application_type_revision',
   87     'msvs_target_platform_version',
   88     'msvs_target_platform_minversion',
   89 ]
   90 
   91 
   92 # List of precompiled header related keys.
   93 precomp_keys = [
   94     'msvs_precompiled_header',
   95     'msvs_precompiled_source',
   96 ]
   97 
   98 
   99 cached_username = None
  100 
  101 
  102 cached_domain = None
  103 
  104 
  105 # TODO(gspencer): Switch the os.environ calls to be
  106 # win32api.GetDomainName() and win32api.GetUserName() once the
  107 # python version in depot_tools has been updated to work on Vista
  108 # 64-bit.
  109 def _GetDomainAndUserName():
  110   if sys.platform not in ('win32', 'cygwin'):
  111     return ('DOMAIN', 'USERNAME')
  112   global cached_username
  113   global cached_domain
  114   if not cached_domain or not cached_username:
  115     domain = os.environ.get('USERDOMAIN')
  116     username = os.environ.get('USERNAME')
  117     if not domain or not username:
  118       call = subprocess.Popen(['net', 'config', 'Workstation'],
  119                               stdout=subprocess.PIPE)
  120       config = call.communicate()[0]
  121       if PY3:
  122         config = config.decode('utf-8')
  123       username_re = re.compile(r'^User name\s+(\S+)', re.MULTILINE)
  124       username_match = username_re.search(config)
  125       if username_match:
  126         username = username_match.group(1)
  127       domain_re = re.compile(r'^Logon domain\s+(\S+)', re.MULTILINE)
  128       domain_match = domain_re.search(config)
  129       if domain_match:
  130         domain = domain_match.group(1)
  131     cached_domain = domain
  132     cached_username = username
  133   return (cached_domain, cached_username)
  134 
  135 fixpath_prefix = None
  136 
  137 
  138 def _NormalizedSource(source):
  139   """Normalize the path.
  140 
  141   But not if that gets rid of a variable, as this may expand to something
  142   larger than one directory.
  143 
  144   Arguments:
  145       source: The path to be normalize.d
  146 
  147   Returns:
  148       The normalized path.
  149   """
  150   normalized = os.path.normpath(source)
  151   if source.count('$') == normalized.count('$'):
  152     source = normalized
  153   return source
  154 
  155 
  156 def _FixPath(path):
  157   """Convert paths to a form that will make sense in a vcproj file.
  158 
  159   Arguments:
  160     path: The path to convert, may contain / etc.
  161   Returns:
  162     The path with all slashes made into backslashes.
  163   """
  164   if fixpath_prefix and path and not os.path.isabs(path) and not path[0] == '$' and not _IsWindowsAbsPath(path):
  165     path = os.path.join(fixpath_prefix, path)
  166   path = path.replace('/', '\\')
  167   path = _NormalizedSource(path)
  168   if path and path[-1] == '\\':
  169     path = path[:-1]
  170   return path
  171 
  172 
  173 def _IsWindowsAbsPath(path):
  174   r"""
  175   On Cygwin systems Python needs a little help determining if a path is an absolute Windows path or not, so that
  176   it does not treat those as relative, which results in bad paths like:
  177 
  178   '..\C:\<some path>\some_source_code_file.cc'
  179   """
  180   return path.startswith('c:') or path.startswith('C:')
  181 
  182 
  183 def _FixPaths(paths):
  184   """Fix each of the paths of the list."""
  185   return [_FixPath(i) for i in paths]
  186 
  187 
  188 def _ConvertSourcesToFilterHierarchy(sources, prefix=None, excluded=None,
  189                                      list_excluded=True, msvs_version=None):
  190   """Converts a list split source file paths into a vcproj folder hierarchy.
  191 
  192   Arguments:
  193     sources: A list of source file paths split.
  194     prefix: A list of source file path layers meant to apply to each of sources.
  195     excluded: A set of excluded files.
  196     msvs_version: A MSVSVersion object.
  197 
  198   Returns:
  199     A hierarchy of filenames and MSVSProject.Filter objects that matches the
  200     layout of the source tree.
  201     For example:
  202     _ConvertSourcesToFilterHierarchy([['a', 'bob1.c'], ['b', 'bob2.c']],
  203                                      prefix=['joe'])
  204     -->
  205     [MSVSProject.Filter('a', contents=['joe\\a\\bob1.c']),
  206      MSVSProject.Filter('b', contents=['joe\\b\\bob2.c'])]
  207   """
  208   if not prefix: prefix = []
  209   result = []
  210   excluded_result = []
  211   folders = OrderedDict()
  212   # Gather files into the final result, excluded, or folders.
  213   for s in sources:
  214     if len(s) == 1:
  215       filename = _NormalizedSource('\\'.join(prefix + s))
  216       if filename in excluded:
  217         excluded_result.append(filename)
  218       else:
  219         result.append(filename)
  220     elif msvs_version and not msvs_version.UsesVcxproj():
  221       # For MSVS 2008 and earlier, we need to process all files before walking
  222       # the sub folders.
  223       if not folders.get(s[0]):
  224         folders[s[0]] = []
  225       folders[s[0]].append(s[1:])
  226     else:
  227       contents = _ConvertSourcesToFilterHierarchy([s[1:]], prefix + [s[0]],
  228                                                   excluded=excluded,
  229                                                   list_excluded=list_excluded,
  230                                                   msvs_version=msvs_version)
  231       contents = MSVSProject.Filter(s[0], contents=contents)
  232       result.append(contents)
  233   # Add a folder for excluded files.
  234   if excluded_result and list_excluded:
  235     excluded_folder = MSVSProject.Filter('_excluded_files',
  236                                          contents=excluded_result)
  237     result.append(excluded_folder)
  238 
  239   if msvs_version and msvs_version.UsesVcxproj():
  240     return result
  241 
  242   # Populate all the folders.
  243   for f in folders:
  244     contents = _ConvertSourcesToFilterHierarchy(folders[f], prefix=prefix + [f],
  245                                                 excluded=excluded,
  246                                                 list_excluded=list_excluded,
  247                                                 msvs_version=msvs_version)
  248     contents = MSVSProject.Filter(f, contents=contents)
  249     result.append(contents)
  250   return result
  251 
  252 
  253 def _ToolAppend(tools, tool_name, setting, value, only_if_unset=False):
  254   if not value: return
  255   _ToolSetOrAppend(tools, tool_name, setting, value, only_if_unset)
  256 
  257 
  258 def _ToolSetOrAppend(tools, tool_name, setting, value, only_if_unset=False):
  259   # TODO(bradnelson): ugly hack, fix this more generally!!!
  260   if 'Directories' in setting or 'Dependencies' in setting:
  261     if type(value) == str:
  262       value = value.replace('/', '\\')
  263     else:
  264       value = [i.replace('/', '\\') for i in value]
  265   if not tools.get(tool_name):
  266     tools[tool_name] = dict()
  267   tool = tools[tool_name]
  268   if tool.get(setting):
  269     if only_if_unset: return
  270     if type(tool[setting]) == list and type(value) == list:
  271       tool[setting] += value
  272     else:
  273       raise TypeError(
  274           'Appending "%s" to a non-list setting "%s" for tool "%s" is '
  275           'not allowed, previous value: %s' % (
  276               value, setting, tool_name, str(tool[setting])))
  277   else:
  278     tool[setting] = value
  279 
  280 
  281 def _ConfigPlatform(config_data):
  282   return config_data.get('msvs_configuration_platform', 'Win32')
  283 
  284 
  285 def _ConfigBaseName(config_name, platform_name):
  286   if config_name.endswith('_' + platform_name):
  287     return config_name[0:-len(platform_name) - 1]
  288   else:
  289     return config_name
  290 
  291 
  292 def _ConfigFullName(config_name, config_data):
  293   platform_name = _ConfigPlatform(config_data)
  294   return '%s|%s' % (_ConfigBaseName(config_name, platform_name), platform_name)
  295 
  296 
  297 def _ConfigWindowsTargetPlatformVersion(config_data):
  298   ver = config_data.get('msvs_windows_target_platform_version')
  299   if not ver or re.match(r'^\d+', ver):
  300     return ver
  301   for key in [r'HKLM\Software\Microsoft\Microsoft SDKs\Windows\%s',
  302               r'HKLM\Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows\%s']:
  303     sdkdir = MSVSVersion._RegistryGetValue(key % ver, 'InstallationFolder')
  304     if not sdkdir:
  305       continue
  306     version = MSVSVersion._RegistryGetValue(key % ver, 'ProductVersion') or ''
  307     # find a matching entry in sdkdir\include
  308     names = sorted([x for x in os.listdir(r'%s\include' % sdkdir) \
  309                     if x.startswith(version)], reverse = True)
  310     return names[0]
  311 
  312 
  313 def _BuildCommandLineForRuleRaw(spec, cmd, cygwin_shell, has_input_path,
  314                                 quote_cmd, do_setup_env):
  315 
  316   if [x for x in cmd if '$(InputDir)' in x]:
  317     input_dir_preamble = (
  318       'set INPUTDIR=$(InputDir)\n'
  319       'if NOT DEFINED INPUTDIR set INPUTDIR=.\\\n'
  320       'set INPUTDIR=%INPUTDIR:~0,-1%\n'
  321       )
  322   else:
  323     input_dir_preamble = ''
  324 
  325   if cygwin_shell:
  326     # Find path to cygwin.
  327     cygwin_dir = _FixPath(spec.get('msvs_cygwin_dirs', ['.'])[0])
  328     # Prepare command.
  329     direct_cmd = cmd
  330     direct_cmd = [i.replace('$(IntDir)',
  331                             '`cygpath -m "${INTDIR}"`') for i in direct_cmd]
  332     direct_cmd = [i.replace('$(OutDir)',
  333                             '`cygpath -m "${OUTDIR}"`') for i in direct_cmd]
  334     direct_cmd = [i.replace('$(InputDir)',
  335                             '`cygpath -m "${INPUTDIR}"`') for i in direct_cmd]
  336     if has_input_path:
  337       direct_cmd = [i.replace('$(InputPath)',
  338                               '`cygpath -m "${INPUTPATH}"`')
  339                     for i in direct_cmd]
  340     direct_cmd = ['\\"%s\\"' % i.replace('"', '\\\\\\"') for i in direct_cmd]
  341     # direct_cmd = gyp.common.EncodePOSIXShellList(direct_cmd)
  342     direct_cmd = ' '.join(direct_cmd)
  343     # TODO(quote):  regularize quoting path names throughout the module
  344     cmd = ''
  345     if do_setup_env:
  346       cmd += 'call "$(ProjectDir)%(cygwin_dir)s\\setup_env.bat" && '
  347     cmd += 'set CYGWIN=nontsec&& '
  348     if direct_cmd.find('NUMBER_OF_PROCESSORS') >= 0:
  349       cmd += 'set /a NUMBER_OF_PROCESSORS_PLUS_1=%%NUMBER_OF_PROCESSORS%%+1&& '
  350     if direct_cmd.find('INTDIR') >= 0:
  351       cmd += 'set INTDIR=$(IntDir)&& '
  352     if direct_cmd.find('OUTDIR') >= 0:
  353       cmd += 'set OUTDIR=$(OutDir)&& '
  354     if has_input_path and direct_cmd.find('INPUTPATH') >= 0:
  355       cmd += 'set INPUTPATH=$(InputPath) && '
  356     cmd += 'bash -c "%(cmd)s"'
  357     cmd = cmd % {'cygwin_dir': cygwin_dir,
  358                  'cmd': direct_cmd}
  359     return input_dir_preamble + cmd
  360   else:
  361     # Convert cat --> type to mimic unix.
  362     if cmd[0] == 'cat':
  363       command = ['type']
  364     else:
  365       command = [cmd[0].replace('/', '\\')]
  366     # Add call before command to ensure that commands can be tied together one
  367     # after the other without aborting in Incredibuild, since IB makes a bat
  368     # file out of the raw command string, and some commands (like python) are
  369     # actually batch files themselves.
  370     command.insert(0, 'call')
  371     # Fix the paths
  372     # TODO(quote): This is a really ugly heuristic, and will miss path fixing
  373     #              for arguments like "--arg=path" or "/opt:path".
  374     # If the argument starts with a slash or dash, it's probably a command line
  375     # switch
  376     arguments = [i if (i[:1] in "/-") else _FixPath(i) for i in cmd[1:]]
  377     arguments = [i.replace('$(InputDir)', '%INPUTDIR%') for i in arguments]
  378     arguments = [MSVSSettings.FixVCMacroSlashes(i) for i in arguments]
  379     if quote_cmd:
  380       # Support a mode for using cmd directly.
  381       # Convert any paths to native form (first element is used directly).
  382       # TODO(quote):  regularize quoting path names throughout the module
  383       arguments = ['"%s"' % i for i in arguments]
  384     # Collapse into a single command.
  385     return input_dir_preamble + ' '.join(command + arguments)
  386 
  387 
  388 def _BuildCommandLineForRule(spec, rule, has_input_path, do_setup_env):
  389   # Currently this weird argument munging is used to duplicate the way a
  390   # python script would need to be run as part of the chrome tree.
  391   # Eventually we should add some sort of rule_default option to set this
  392   # per project. For now the behavior chrome needs is the default.
  393   mcs = rule.get('msvs_cygwin_shell')
  394   if mcs is None:
  395     mcs = int(spec.get('msvs_cygwin_shell', 1))
  396   elif isinstance(mcs, str):
  397     mcs = int(mcs)
  398   quote_cmd = int(rule.get('msvs_quote_cmd', 1))
  399   return _BuildCommandLineForRuleRaw(spec, rule['action'], mcs, has_input_path,
  400                                      quote_cmd, do_setup_env=do_setup_env)
  401 
  402 
  403 def _AddActionStep(actions_dict, inputs, outputs, description, command):
  404   """Merge action into an existing list of actions.
  405 
  406   Care must be taken so that actions which have overlapping inputs either don't
  407   get assigned to the same input, or get collapsed into one.
  408 
  409   Arguments:
  410     actions_dict: dictionary keyed on input name, which maps to a list of
  411       dicts describing the actions attached to that input file.
  412     inputs: list of inputs
  413     outputs: list of outputs
  414     description: description of the action
  415     command: command line to execute
  416   """
  417   # Require there to be at least one input (call sites will ensure this).
  418   assert inputs
  419 
  420   action = {
  421       'inputs': inputs,
  422       'outputs': outputs,
  423       'description': description,
  424       'command': command,
  425   }
  426 
  427   # Pick where to stick this action.
  428   # While less than optimal in terms of build time, attach them to the first
  429   # input for now.
  430   chosen_input = inputs[0]
  431 
  432   # Add it there.
  433   if chosen_input not in actions_dict:
  434     actions_dict[chosen_input] = []
  435   actions_dict[chosen_input].append(action)
  436 
  437 
  438 def _AddCustomBuildToolForMSVS(p, spec, primary_input,
  439                                inputs, outputs, description, cmd):
  440   """Add a custom build tool to execute something.
  441 
  442   Arguments:
  443     p: the target project
  444     spec: the target project dict
  445     primary_input: input file to attach the build tool to
  446     inputs: list of inputs
  447     outputs: list of outputs
  448     description: description of the action
  449     cmd: command line to execute
  450   """
  451   inputs = _FixPaths(inputs)
  452   outputs = _FixPaths(outputs)
  453   tool = MSVSProject.Tool(
  454       'VCCustomBuildTool',
  455       {'Description': description,
  456        'AdditionalDependencies': ';'.join(inputs),
  457        'Outputs': ';'.join(outputs),
  458        'CommandLine': cmd,
  459       })
  460   # Add to the properties of primary input for each config.
  461   for config_name, c_data in spec['configurations'].items():
  462     p.AddFileConfig(_FixPath(primary_input),
  463                     _ConfigFullName(config_name, c_data), tools=[tool])
  464 
  465 
  466 def _AddAccumulatedActionsToMSVS(p, spec, actions_dict):
  467   """Add actions accumulated into an actions_dict, merging as needed.
  468 
  469   Arguments:
  470     p: the target project
  471     spec: the target project dict
  472     actions_dict: dictionary keyed on input name, which maps to a list of
  473         dicts describing the actions attached to that input file.
  474   """
  475   for primary_input in actions_dict:
  476     inputs = OrderedSet()
  477     outputs = OrderedSet()
  478     descriptions = []
  479     commands = []
  480     for action in actions_dict[primary_input]:
  481       inputs.update(OrderedSet(action['inputs']))
  482       outputs.update(OrderedSet(action['outputs']))
  483       descriptions.append(action['description'])
  484       commands.append(action['command'])
  485     # Add the custom build step for one input file.
  486     description = ', and also '.join(descriptions)
  487     command = '\r\n'.join(commands)
  488     _AddCustomBuildToolForMSVS(p, spec,
  489                                primary_input=primary_input,
  490                                inputs=inputs,
  491                                outputs=outputs,
  492                                description=description,
  493                                cmd=command)
  494 
  495 
  496 def _RuleExpandPath(path, input_file):
  497   """Given the input file to which a rule applied, string substitute a path.
  498 
  499   Arguments:
  500     path: a path to string expand
  501     input_file: the file to which the rule applied.
  502   Returns:
  503     The string substituted path.
  504   """
  505   path = path.replace('$(InputName)',
  506                       os.path.splitext(os.path.split(input_file)[1])[0])
  507   path = path.replace('$(InputDir)', os.path.dirname(input_file))
  508   path = path.replace('$(InputExt)',
  509                       os.path.splitext(os.path.split(input_file)[1])[1])
  510   path = path.replace('$(InputFileName)', os.path.split(input_file)[1])
  511   path = path.replace('$(InputPath)', input_file)
  512   return path
  513 
  514 
  515 def _FindRuleTriggerFiles(rule, sources):
  516   """Find the list of files which a particular rule applies to.
  517 
  518   Arguments:
  519     rule: the rule in question
  520     sources: the set of all known source files for this project
  521   Returns:
  522     The list of sources that trigger a particular rule.
  523   """
  524   return rule.get('rule_sources', [])
  525 
  526 
  527 def _RuleInputsAndOutputs(rule, trigger_file):
  528   """Find the inputs and outputs generated by a rule.
  529 
  530   Arguments:
  531     rule: the rule in question.
  532     trigger_file: the main trigger for this rule.
  533   Returns:
  534     The pair of (inputs, outputs) involved in this rule.
  535   """
  536   raw_inputs = _FixPaths(rule.get('inputs', []))
  537   raw_outputs = _FixPaths(rule.get('outputs', []))
  538   inputs = OrderedSet()
  539   outputs = OrderedSet()
  540   inputs.add(trigger_file)
  541   for i in raw_inputs:
  542     inputs.add(_RuleExpandPath(i, trigger_file))
  543   for o in raw_outputs:
  544     outputs.add(_RuleExpandPath(o, trigger_file))
  545   return (inputs, outputs)
  546 
  547 
  548 def _GenerateNativeRulesForMSVS(p, rules, output_dir, spec, options):
  549   """Generate a native rules file.
  550 
  551   Arguments:
  552     p: the target project
  553     rules: the set of rules to include
  554     output_dir: the directory in which the project/gyp resides
  555     spec: the project dict
  556     options: global generator options
  557   """
  558   rules_filename = '%s%s.rules' % (spec['target_name'],
  559                                    options.suffix)
  560   rules_file = MSVSToolFile.Writer(os.path.join(output_dir, rules_filename),
  561                                    spec['target_name'])
  562   # Add each rule.
  563   for r in rules:
  564     rule_name = r['rule_name']
  565     rule_ext = r['extension']
  566     inputs = _FixPaths(r.get('inputs', []))
  567     outputs = _FixPaths(r.get('outputs', []))
  568     # Skip a rule with no action and no inputs.
  569     if 'action' not in r and not r.get('rule_sources', []):
  570       continue
  571     cmd = _BuildCommandLineForRule(spec, r, has_input_path=True,
  572                                    do_setup_env=True)
  573     rules_file.AddCustomBuildRule(name=rule_name,
  574                                   description=r.get('message', rule_name),
  575                                   extensions=[rule_ext],
  576                                   additional_dependencies=inputs,
  577                                   outputs=outputs,
  578                                   cmd=cmd)
  579   # Write out rules file.
  580   rules_file.WriteIfChanged()
  581 
  582   # Add rules file to project.
  583   p.AddToolFile(rules_filename)
  584 
  585 
  586 def _Cygwinify(path):
  587   path = path.replace('$(OutDir)', '$(OutDirCygwin)')
  588   path = path.replace('$(IntDir)', '$(IntDirCygwin)')
  589   return path
  590 
  591 
  592 def _GenerateExternalRules(rules, output_dir, spec,
  593                            sources, options, actions_to_add):
  594   """Generate an external makefile to do a set of rules.
  595 
  596   Arguments:
  597     rules: the list of rules to include
  598     output_dir: path containing project and gyp files
  599     spec: project specification data
  600     sources: set of sources known
  601     options: global generator options
  602     actions_to_add: The list of actions we will add to.
  603   """
  604   filename = '%s_rules%s.mk' % (spec['target_name'], options.suffix)
  605   mk_file = gyp.common.WriteOnDiff(os.path.join(output_dir, filename))
  606   # Find cygwin style versions of some paths.
  607   mk_file.write('OutDirCygwin:=$(shell cygpath -u "$(OutDir)")\n')
  608   mk_file.write('IntDirCygwin:=$(shell cygpath -u "$(IntDir)")\n')
  609   # Gather stuff needed to emit all: target.
  610   all_inputs = OrderedSet()
  611   all_outputs = OrderedSet()
  612   all_output_dirs = OrderedSet()
  613   first_outputs = []
  614   for rule in rules:
  615     trigger_files = _FindRuleTriggerFiles(rule, sources)
  616     for tf in trigger_files:
  617       inputs, outputs = _RuleInputsAndOutputs(rule, tf)
  618       all_inputs.update(OrderedSet(inputs))
  619       all_outputs.update(OrderedSet(outputs))
  620       # Only use one target from each rule as the dependency for
  621       # 'all' so we don't try to build each rule multiple times.
  622       first_outputs.append(list(outputs)[0])
  623       # Get the unique output directories for this rule.
  624       output_dirs = [os.path.split(i)[0] for i in outputs]
  625       for od in output_dirs:
  626         all_output_dirs.add(od)
  627   first_outputs_cyg = [_Cygwinify(i) for i in first_outputs]
  628   # Write out all: target, including mkdir for each output directory.
  629   mk_file.write('all: %s\n' % ' '.join(first_outputs_cyg))
  630   for od in all_output_dirs:
  631     if od:
  632       mk_file.write('\tmkdir -p `cygpath -u "%s"`\n' % od)
  633   mk_file.write('\n')
  634   # Define how each output is generated.
  635   for rule in rules:
  636     trigger_files = _FindRuleTriggerFiles(rule, sources)
  637     for tf in trigger_files:
  638       # Get all the inputs and outputs for this rule for this trigger file.
  639       inputs, outputs = _RuleInputsAndOutputs(rule, tf)
  640       inputs = [_Cygwinify(i) for i in inputs]
  641       outputs = [_Cygwinify(i) for i in outputs]
  642       # Prepare the command line for this rule.
  643       cmd = [_RuleExpandPath(c, tf) for c in rule['action']]
  644       cmd = ['"%s"' % i for i in cmd]
  645       cmd = ' '.join(cmd)
  646       # Add it to the makefile.
  647       mk_file.write('%s: %s\n' % (' '.join(outputs), ' '.join(inputs)))
  648       mk_file.write('\t%s\n\n' % cmd)
  649   # Close up the file.
  650   mk_file.close()
  651 
  652   # Add makefile to list of sources.
  653   sources.add(filename)
  654   # Add a build action to call makefile.
  655   cmd = ['make',
  656          'OutDir=$(OutDir)',
  657          'IntDir=$(IntDir)',
  658          '-j', '${NUMBER_OF_PROCESSORS_PLUS_1}',
  659          '-f', filename]
  660   cmd = _BuildCommandLineForRuleRaw(spec, cmd, True, False, True, True)
  661   # Insert makefile as 0'th input, so it gets the action attached there,
  662   # as this is easier to understand from in the IDE.
  663   all_inputs = list(all_inputs)
  664   all_inputs.insert(0, filename)
  665   _AddActionStep(actions_to_add,
  666                  inputs=_FixPaths(all_inputs),
  667                  outputs=_FixPaths(all_outputs),
  668                  description='Running external rules for %s' %
  669                      spec['target_name'],
  670                  command=cmd)
  671 
  672 
  673 def _EscapeEnvironmentVariableExpansion(s):
  674   """Escapes % characters.
  675 
  676   Escapes any % characters so that Windows-style environment variable
  677   expansions will leave them alone.
  678   See http://connect.microsoft.com/VisualStudio/feedback/details/106127/cl-d-name-text-containing-percentage-characters-doesnt-compile
  679   to understand why we have to do this.
  680 
  681   Args:
  682       s: The string to be escaped.
  683 
  684   Returns:
  685       The escaped string.
  686   """
  687   s = s.replace('%', '%%')
  688   return s
  689 
  690 
  691 quote_replacer_regex = re.compile(r'(\\*)"')
  692 
  693 
  694 def _EscapeCommandLineArgumentForMSVS(s):
  695   """Escapes a Windows command-line argument.
  696 
  697   So that the Win32 CommandLineToArgv function will turn the escaped result back
  698   into the original string.
  699   See http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
  700   ("Parsing C++ Command-Line Arguments") to understand why we have to do
  701   this.
  702 
  703   Args:
  704       s: the string to be escaped.
  705   Returns:
  706       the escaped string.
  707   """
  708 
  709   def _Replace(match):
  710     # For a literal quote, CommandLineToArgv requires an odd number of
  711     # backslashes preceding it, and it produces half as many literal backslashes
  712     # (rounded down). So we need to produce 2n+1 backslashes.
  713     return 2 * match.group(1) + '\\"'
  714 
  715   # Escape all quotes so that they are interpreted literally.
  716   s = quote_replacer_regex.sub(_Replace, s)
  717   # Now add unescaped quotes so that any whitespace is interpreted literally.
  718   s = '"' + s + '"'
  719   return s
  720 
  721 
  722 delimiters_replacer_regex = re.compile(r'(\\*)([,;]+)')
  723 
  724 
  725 def _EscapeVCProjCommandLineArgListItem(s):
  726   """Escapes command line arguments for MSVS.
  727 
  728   The VCProj format stores string lists in a single string using commas and
  729   semi-colons as separators, which must be quoted if they are to be
  730   interpreted literally. However, command-line arguments may already have
  731   quotes, and the VCProj parser is ignorant of the backslash escaping
  732   convention used by CommandLineToArgv, so the command-line quotes and the
  733   VCProj quotes may not be the same quotes. So to store a general
  734   command-line argument in a VCProj list, we need to parse the existing
  735   quoting according to VCProj's convention and quote any delimiters that are
  736   not already quoted by that convention. The quotes that we add will also be
  737   seen by CommandLineToArgv, so if backslashes precede them then we also have
  738   to escape those backslashes according to the CommandLineToArgv
  739   convention.
  740 
  741   Args:
  742       s: the string to be escaped.
  743   Returns:
  744       the escaped string.
  745   """
  746 
  747   def _Replace(match):
  748     # For a non-literal quote, CommandLineToArgv requires an even number of
  749     # backslashes preceding it, and it produces half as many literal
  750     # backslashes. So we need to produce 2n backslashes.
  751     return 2 * match.group(1) + '"' + match.group(2) + '"'
  752 
  753   segments = s.split('"')
  754   # The unquoted segments are at the even-numbered indices.
  755   for i in range(0, len(segments), 2):
  756     segments[i] = delimiters_replacer_regex.sub(_Replace, segments[i])
  757   # Concatenate back into a single string
  758   s = '"'.join(segments)
  759   if len(segments) % 2 == 0:
  760     # String ends while still quoted according to VCProj's convention. This
  761     # means the delimiter and the next list item that follow this one in the
  762     # .vcproj file will be misinterpreted as part of this item. There is nothing
  763     # we can do about this. Adding an extra quote would correct the problem in
  764     # the VCProj but cause the same problem on the final command-line. Moving
  765     # the item to the end of the list does works, but that's only possible if
  766     # there's only one such item. Let's just warn the user.
  767     print('Warning: MSVS may misinterpret the odd number of ' +
  768                           'quotes in ' + s, file=sys.stderr)
  769   return s
  770 
  771 
  772 def _EscapeCppDefineForMSVS(s):
  773   """Escapes a CPP define so that it will reach the compiler unaltered."""
  774   s = _EscapeEnvironmentVariableExpansion(s)
  775   s = _EscapeCommandLineArgumentForMSVS(s)
  776   s = _EscapeVCProjCommandLineArgListItem(s)
  777   # cl.exe replaces literal # characters with = in preprocesor definitions for
  778   # some reason. Octal-encode to work around that.
  779   s = s.replace('#', '\\%03o' % ord('#'))
  780   return s
  781 
  782 
  783 quote_replacer_regex2 = re.compile(r'(\\+)"')
  784 
  785 
  786 def _EscapeCommandLineArgumentForMSBuild(s):
  787   """Escapes a Windows command-line argument for use by MSBuild."""
  788 
  789   def _Replace(match):
  790     return (len(match.group(1)) / 2 * 4) * '\\' + '\\"'
  791 
  792   # Escape all quotes so that they are interpreted literally.
  793   s = quote_replacer_regex2.sub(_Replace, s)
  794   return s
  795 
  796 
  797 def _EscapeMSBuildSpecialCharacters(s):
  798   escape_dictionary = {
  799       '%': '%25',
  800       '$': '%24',
  801       '@': '%40',
  802       "'": '%27',
  803       ';': '%3B',
  804       '?': '%3F',
  805       '*': '%2A'
  806       }
  807   result = ''.join([escape_dictionary.get(c, c) for c in s])
  808   return result
  809 
  810 
  811 def _EscapeCppDefineForMSBuild(s):
  812   """Escapes a CPP define so that it will reach the compiler unaltered."""
  813   s = _EscapeEnvironmentVariableExpansion(s)
  814   s = _EscapeCommandLineArgumentForMSBuild(s)
  815   s = _EscapeMSBuildSpecialCharacters(s)
  816   # cl.exe replaces literal # characters with = in preprocesor definitions for
  817   # some reason. Octal-encode to work around that.
  818   s = s.replace('#', '\\%03o' % ord('#'))
  819   return s
  820 
  821 
  822 def _GenerateRulesForMSVS(p, output_dir, options, spec,
  823                           sources, excluded_sources,
  824                           actions_to_add):
  825   """Generate all the rules for a particular project.
  826 
  827   Arguments:
  828     p: the project
  829     output_dir: directory to emit rules to
  830     options: global options passed to the generator
  831     spec: the specification for this project
  832     sources: the set of all known source files in this project
  833     excluded_sources: the set of sources excluded from normal processing
  834     actions_to_add: deferred list of actions to add in
  835   """
  836   rules = spec.get('rules', [])
  837   rules_native = [r for r in rules if not int(r.get('msvs_external_rule', 0))]
  838   rules_external = [r for r in rules if int(r.get('msvs_external_rule', 0))]
  839 
  840   # Handle rules that use a native rules file.
  841   if rules_native:
  842     _GenerateNativeRulesForMSVS(p, rules_native, output_dir, spec, options)
  843 
  844   # Handle external rules (non-native rules).
  845   if rules_external:
  846     _GenerateExternalRules(rules_external, output_dir, spec,
  847                            sources, options, actions_to_add)
  848   _AdjustSourcesForRules(rules, sources, excluded_sources, False)
  849 
  850 
  851 def _AdjustSourcesForRules(rules, sources, excluded_sources, is_msbuild):
  852   # Add outputs generated by each rule (if applicable).
  853   for rule in rules:
  854     # Add in the outputs from this rule.
  855     trigger_files = _FindRuleTriggerFiles(rule, sources)
  856     for trigger_file in trigger_files:
  857       # Remove trigger_file from excluded_sources to let the rule be triggered
  858       # (e.g. rule trigger ax_enums.idl is added to excluded_sources
  859       # because it's also in an action's inputs in the same project)
  860       excluded_sources.discard(_FixPath(trigger_file))
  861       # Done if not processing outputs as sources.
  862       if int(rule.get('process_outputs_as_sources', False)):
  863         inputs, outputs = _RuleInputsAndOutputs(rule, trigger_file)
  864         inputs = OrderedSet(_FixPaths(inputs))
  865         outputs = OrderedSet(_FixPaths(outputs))
  866         inputs.remove(_FixPath(trigger_file))
  867         sources.update(inputs)
  868         if not is_msbuild:
  869           excluded_sources.update(inputs)
  870         sources.update(outputs)
  871 
  872 
  873 def _FilterActionsFromExcluded(excluded_sources, actions_to_add):
  874   """Take inputs with actions attached out of the list of exclusions.
  875 
  876   Arguments:
  877     excluded_sources: list of source files not to be built.
  878     actions_to_add: dict of actions keyed on source file they're attached to.
  879   Returns:
  880     excluded_sources with files that have actions attached removed.
  881   """
  882   must_keep = OrderedSet(_FixPaths(actions_to_add.keys()))
  883   return [s for s in excluded_sources if s not in must_keep]
  884 
  885 
  886 def _GetDefaultConfiguration(spec):
  887   return spec['configurations'][spec['default_configuration']]
  888 
  889 
  890 def _GetGuidOfProject(proj_path, spec):
  891   """Get the guid for the project.
  892 
  893   Arguments:
  894     proj_path: Path of the vcproj or vcxproj file to generate.
  895     spec: The target dictionary containing the properties of the target.
  896   Returns:
  897     the guid.
  898   Raises:
  899     ValueError: if the specified GUID is invalid.
  900   """
  901   # Pluck out the default configuration.
  902   default_config = _GetDefaultConfiguration(spec)
  903   # Decide the guid of the project.
  904   guid = default_config.get('msvs_guid')
  905   if guid:
  906     if VALID_MSVS_GUID_CHARS.match(guid) is None:
  907       raise ValueError('Invalid MSVS guid: "%s".  Must match regex: "%s".' %
  908                        (guid, VALID_MSVS_GUID_CHARS.pattern))
  909     guid = '{%s}' % guid
  910   guid = guid or MSVSNew.MakeGuid(proj_path)
  911   return guid
  912 
  913 
  914 def _GetMsbuildToolsetOfProject(proj_path, spec, version):
  915   """Get the platform toolset for the project.
  916 
  917   Arguments:
  918     proj_path: Path of the vcproj or vcxproj file to generate.
  919     spec: The target dictionary containing the properties of the target.
  920     version: The MSVSVersion object.
  921   Returns:
  922     the platform toolset string or None.
  923   """
  924   # Pluck out the default configuration.
  925   default_config = _GetDefaultConfiguration(spec)
  926   toolset = default_config.get('msbuild_toolset')
  927   if not toolset and version.DefaultToolset():
  928     toolset = version.DefaultToolset()
  929   return toolset
  930 
  931 
  932 def _GenerateProject(project, options, version, generator_flags):
  933   """Generates a vcproj file.
  934 
  935   Arguments:
  936     project: the MSVSProject object.
  937     options: global generator options.
  938     version: the MSVSVersion object.
  939     generator_flags: dict of generator-specific flags.
  940   Returns:
  941     A list of source files that cannot be found on disk.
  942   """
  943   default_config = _GetDefaultConfiguration(project.spec)
  944 
  945   # Skip emitting anything if told to with msvs_existing_vcproj option.
  946   if default_config.get('msvs_existing_vcproj'):
  947     return []
  948 
  949   if version.UsesVcxproj():
  950     return _GenerateMSBuildProject(project, options, version, generator_flags)
  951   else:
  952     return _GenerateMSVSProject(project, options, version, generator_flags)
  953 
  954 
  955 # TODO: Avoid code duplication with _ValidateSourcesForOSX in make.py.
  956 def _ValidateSourcesForMSVSProject(spec, version):
  957   """Makes sure if duplicate basenames are not specified in the source list.
  958 
  959   Arguments:
  960     spec: The target dictionary containing the properties of the target.
  961     version: The VisualStudioVersion object.
  962   """
  963   # This validation should not be applied to MSVC2010 and later.
  964   assert not version.UsesVcxproj()
  965 
  966   # TODO: Check if MSVC allows this for loadable_module targets.
  967   if spec.get('type', None) not in ('static_library', 'shared_library'):
  968     return
  969   sources = spec.get('sources', [])
  970   basenames = {}
  971   for source in sources:
  972     name, ext = os.path.splitext(source)
  973     is_compiled_file = ext in [
  974         '.c', '.cc', '.cpp', '.cxx', '.m', '.mm', '.s', '.S']
  975     if not is_compiled_file:
  976       continue
  977     basename = os.path.basename(name)  # Don't include extension.
  978     basenames.setdefault(basename, []).append(source)
  979 
  980   error = ''
  981   for basename, files in basenames.items():
  982     if len(files) > 1:
  983       error += '  %s: %s\n' % (basename, ' '.join(files))
  984 
  985   if error:
  986     print('static library %s has several files with the same basename:\n' % spec['target_name']
  987           + error + 'MSVC08 cannot handle that.')
  988     raise GypError('Duplicate basenames in sources section, see list above')
  989 
  990 
  991 def _GenerateMSVSProject(project, options, version, generator_flags):
  992   """Generates a .vcproj file.  It may create .rules and .user files too.
  993 
  994   Arguments:
  995     project: The project object we will generate the file for.
  996     options: Global options passed to the generator.
  997     version: The VisualStudioVersion object.
  998     generator_flags: dict of generator-specific flags.
  999   """
 1000   spec = project.spec
 1001   gyp.common.EnsureDirExists(project.path)
 1002 
 1003   platforms = _GetUniquePlatforms(spec)
 1004   p = MSVSProject.Writer(project.path, version, spec['target_name'],
 1005                          project.guid, platforms)
 1006 
 1007   # Get directory project file is in.
 1008   project_dir = os.path.split(project.path)[0]
 1009   gyp_path = _NormalizedSource(project.build_file)
 1010   relative_path_of_gyp_file = gyp.common.RelativePath(gyp_path, project_dir)
 1011 
 1012   config_type = _GetMSVSConfigurationType(spec, project.build_file)
 1013   for config_name, config in spec['configurations'].items():
 1014     _AddConfigurationToMSVSProject(p, spec, config_type, config_name, config)
 1015 
 1016   # MSVC08 and prior version cannot handle duplicate basenames in the same
 1017   # target.
 1018   # TODO: Take excluded sources into consideration if possible.
 1019   _ValidateSourcesForMSVSProject(spec, version)
 1020 
 1021   # Prepare list of sources and excluded sources.
 1022   gyp_file = os.path.split(project.build_file)[1]
 1023   sources, excluded_sources = _PrepareListOfSources(spec, generator_flags,
 1024                                                     gyp_file)
 1025 
 1026   # Add rules.
 1027   actions_to_add = {}
 1028   _GenerateRulesForMSVS(p, project_dir, options, spec,
 1029                         sources, excluded_sources,
 1030                         actions_to_add)
 1031   list_excluded = generator_flags.get('msvs_list_excluded_files', True)
 1032   sources, excluded_sources, excluded_idl = (
 1033       _AdjustSourcesAndConvertToFilterHierarchy(spec, options, project_dir,
 1034                                                 sources, excluded_sources,
 1035                                                 list_excluded, version))
 1036 
 1037   # Add in files.
 1038   missing_sources = _VerifySourcesExist(sources, project_dir)
 1039   p.AddFiles(sources)
 1040 
 1041   _AddToolFilesToMSVS(p, spec)
 1042   _HandlePreCompiledHeaders(p, sources, spec)
 1043   _AddActions(actions_to_add, spec, relative_path_of_gyp_file)
 1044   _AddCopies(actions_to_add, spec)
 1045   _WriteMSVSUserFile(project.path, version, spec)
 1046 
 1047   # NOTE: this stanza must appear after all actions have been decided.
 1048   # Don't excluded sources with actions attached, or they won't run.
 1049   excluded_sources = _FilterActionsFromExcluded(
 1050       excluded_sources, actions_to_add)
 1051   _ExcludeFilesFromBeingBuilt(p, spec, excluded_sources, excluded_idl,
 1052                               list_excluded)
 1053   _AddAccumulatedActionsToMSVS(p, spec, actions_to_add)
 1054 
 1055   # Write it out.
 1056   p.WriteIfChanged()
 1057 
 1058   return missing_sources
 1059 
 1060 
 1061 def _GetUniquePlatforms(spec):
 1062   """Returns the list of unique platforms for this spec, e.g ['win32', ...].
 1063 
 1064   Arguments:
 1065     spec: The target dictionary containing the properties of the target.
 1066   Returns:
 1067     The MSVSUserFile object created.
 1068   """
 1069   # Gather list of unique platforms.
 1070   platforms = OrderedSet()
 1071   for configuration in spec['configurations']:
 1072     platforms.add(_ConfigPlatform(spec['configurations'][configuration]))
 1073   platforms = list(platforms)
 1074   return platforms
 1075 
 1076 
 1077 def _CreateMSVSUserFile(proj_path, version, spec):
 1078   """Generates a .user file for the user running this Gyp program.
 1079 
 1080   Arguments:
 1081     proj_path: The path of the project file being created.  The .user file
 1082                shares the same path (with an appropriate suffix).
 1083     version: The VisualStudioVersion object.
 1084     spec: The target dictionary containing the properties of the target.
 1085   Returns:
 1086     The MSVSUserFile object created.
 1087   """
 1088   (domain, username) = _GetDomainAndUserName()
 1089   vcuser_filename = '.'.join([proj_path, domain, username, 'user'])
 1090   user_file = MSVSUserFile.Writer(vcuser_filename, version,
 1091                                   spec['target_name'])
 1092   return user_file
 1093 
 1094 
 1095 def _GetMSVSConfigurationType(spec, build_file):
 1096   """Returns the configuration type for this project.
 1097 
 1098   It's a number defined by Microsoft.  May raise an exception.
 1099 
 1100   Args:
 1101       spec: The target dictionary containing the properties of the target.
 1102       build_file: The path of the gyp file.
 1103   Returns:
 1104       An integer, the configuration type.
 1105   """
 1106   try:
 1107     config_type = {
 1108         'executable': '1',  # .exe
 1109         'shared_library': '2',  # .dll
 1110         'loadable_module': '2',  # .dll
 1111         'static_library': '4',  # .lib
 1112         'none': '10',  # Utility type
 1113         }[spec['type']]
 1114   except KeyError:
 1115     if spec.get('type'):
 1116       raise GypError('Target type %s is not a valid target type for '
 1117                      'target %s in %s.' %
 1118                      (spec['type'], spec['target_name'], build_file))
 1119     else:
 1120       raise GypError('Missing type field for target %s in %s.' %
 1121                      (spec['target_name'], build_file))
 1122   return config_type
 1123 
 1124 
 1125 def _AddConfigurationToMSVSProject(p, spec, config_type, config_name, config):
 1126   """Adds a configuration to the MSVS project.
 1127 
 1128   Many settings in a vcproj file are specific to a configuration.  This
 1129   function the main part of the vcproj file that's configuration specific.
 1130 
 1131   Arguments:
 1132     p: The target project being generated.
 1133     spec: The target dictionary containing the properties of the target.
 1134     config_type: The configuration type, a number as defined by Microsoft.
 1135     config_name: The name of the configuration.
 1136     config: The dictionary that defines the special processing to be done
 1137             for this configuration.
 1138   """
 1139   # Get the information for this configuration
 1140   include_dirs, midl_include_dirs, resource_include_dirs = \
 1141       _GetIncludeDirs(config)
 1142   libraries = _GetLibraries(spec)
 1143   library_dirs = _GetLibraryDirs(config)
 1144   out_file, vc_tool, _ = _GetOutputFilePathAndTool(spec, msbuild=False)
 1145   defines = _GetDefines(config)
 1146   defines = [_EscapeCppDefineForMSVS(d) for d in defines]
 1147   disabled_warnings = _GetDisabledWarnings(config)
 1148   prebuild = config.get('msvs_prebuild')
 1149   postbuild = config.get('msvs_postbuild')
 1150   def_file = _GetModuleDefinition(spec)
 1151   precompiled_header = config.get('msvs_precompiled_header')
 1152 
 1153   # Prepare the list of tools as a dictionary.
 1154   tools = dict()
 1155   # Add in user specified msvs_settings.
 1156   msvs_settings = config.get('msvs_settings', {})
 1157   MSVSSettings.ValidateMSVSSettings(msvs_settings)
 1158 
 1159   # Prevent default library inheritance from the environment.
 1160   _ToolAppend(tools, 'VCLinkerTool', 'AdditionalDependencies', ['$(NOINHERIT)'])
 1161 
 1162   for tool in msvs_settings:
 1163     settings = config['msvs_settings'][tool]
 1164     for setting in settings:
 1165       _ToolAppend(tools, tool, setting, settings[setting])
 1166   # Add the information to the appropriate tool
 1167   _ToolAppend(tools, 'VCCLCompilerTool',
 1168               'AdditionalIncludeDirectories', include_dirs)
 1169   _ToolAppend(tools, 'VCMIDLTool',
 1170               'AdditionalIncludeDirectories', midl_include_dirs)
 1171   _ToolAppend(tools, 'VCResourceCompilerTool',
 1172               'AdditionalIncludeDirectories', resource_include_dirs)
 1173   # Add in libraries.
 1174   _ToolAppend(tools, 'VCLinkerTool', 'AdditionalDependencies', libraries)
 1175   _ToolAppend(tools, 'VCLinkerTool', 'AdditionalLibraryDirectories',
 1176               library_dirs)
 1177   if out_file:
 1178     _ToolAppend(tools, vc_tool, 'OutputFile', out_file, only_if_unset=True)
 1179   # Add defines.
 1180   _ToolAppend(tools, 'VCCLCompilerTool', 'PreprocessorDefinitions', defines)
 1181   _ToolAppend(tools, 'VCResourceCompilerTool', 'PreprocessorDefinitions',
 1182               defines)
 1183   # Change program database directory to prevent collisions.
 1184   _ToolAppend(tools, 'VCCLCompilerTool', 'ProgramDataBaseFileName',
 1185               '$(IntDir)$(ProjectName)\\vc80.pdb', only_if_unset=True)
 1186   # Add disabled warnings.
 1187   _ToolAppend(tools, 'VCCLCompilerTool',
 1188               'DisableSpecificWarnings', disabled_warnings)
 1189   # Add Pre-build.
 1190   _ToolAppend(tools, 'VCPreBuildEventTool', 'CommandLine', prebuild)
 1191   # Add Post-build.
 1192   _ToolAppend(tools, 'VCPostBuildEventTool', 'CommandLine', postbuild)
 1193   # Turn on precompiled headers if appropriate.
 1194   if precompiled_header:
 1195     precompiled_header = os.path.split(precompiled_header)[1]
 1196     _ToolAppend(tools, 'VCCLCompilerTool', 'UsePrecompiledHeader', '2')
 1197     _ToolAppend(tools, 'VCCLCompilerTool',
 1198                 'PrecompiledHeaderThrough', precompiled_header)
 1199     _ToolAppend(tools, 'VCCLCompilerTool',
 1200                 'ForcedIncludeFiles', precompiled_header)
 1201   # Loadable modules don't generate import libraries;
 1202   # tell dependent projects to not expect one.
 1203   if spec['type'] == 'loadable_module':
 1204     _ToolAppend(tools, 'VCLinkerTool', 'IgnoreImportLibrary', 'true')
 1205   # Set the module definition file if any.
 1206   if def_file:
 1207     _ToolAppend(tools, 'VCLinkerTool', 'ModuleDefinitionFile', def_file)
 1208 
 1209   _AddConfigurationToMSVS(p, spec, tools, config, config_type, config_name)
 1210 
 1211 
 1212 def _GetIncludeDirs(config):
 1213   """Returns the list of directories to be used for #include directives.
 1214 
 1215   Arguments:
 1216     config: The dictionary that defines the special processing to be done
 1217             for this configuration.
 1218   Returns:
 1219     The list of directory paths.
 1220   """
 1221   # TODO(bradnelson): include_dirs should really be flexible enough not to
 1222   #                   require this sort of thing.
 1223   include_dirs = (
 1224       config.get('include_dirs', []) +
 1225       config.get('msvs_system_include_dirs', []))
 1226   midl_include_dirs = (
 1227       config.get('midl_include_dirs', []) +
 1228       config.get('msvs_system_include_dirs', []))
 1229   resource_include_dirs = config.get('resource_include_dirs', include_dirs)
 1230   include_dirs = _FixPaths(include_dirs)
 1231   midl_include_dirs = _FixPaths(midl_include_dirs)
 1232   resource_include_dirs = _FixPaths(resource_include_dirs)
 1233   return include_dirs, midl_include_dirs, resource_include_dirs
 1234 
 1235 
 1236 def _GetLibraryDirs(config):
 1237   """Returns the list of directories to be used for library search paths.
 1238 
 1239   Arguments:
 1240     config: The dictionary that defines the special processing to be done
 1241             for this configuration.
 1242   Returns:
 1243     The list of directory paths.
 1244   """
 1245 
 1246   library_dirs = config.get('library_dirs', [])
 1247   library_dirs = _FixPaths(library_dirs)
 1248   return library_dirs
 1249 
 1250 
 1251 def _GetLibraries(spec):
 1252   """Returns the list of libraries for this configuration.
 1253 
 1254   Arguments:
 1255     spec: The target dictionary containing the properties of the target.
 1256   Returns:
 1257     The list of directory paths.
 1258   """
 1259   libraries = spec.get('libraries', [])
 1260   # Strip out -l, as it is not used on windows (but is needed so we can pass
 1261   # in libraries that are assumed to be in the default library path).
 1262   # Also remove duplicate entries, leaving only the last duplicate, while
 1263   # preserving order.
 1264   found = OrderedSet()
 1265   unique_libraries_list = []
 1266   for entry in reversed(libraries):
 1267     library = re.sub(r'^\-l', '', entry)
 1268     if not os.path.splitext(library)[1]:
 1269       library += '.lib'
 1270     if library not in found:
 1271       found.add(library)
 1272       unique_libraries_list.append(library)
 1273   unique_libraries_list.reverse()
 1274   return unique_libraries_list
 1275 
 1276 
 1277 def _GetOutputFilePathAndTool(spec, msbuild):
 1278   """Returns the path and tool to use for this target.
 1279 
 1280   Figures out the path of the file this spec will create and the name of
 1281   the VC tool that will create it.
 1282 
 1283   Arguments:
 1284     spec: The target dictionary containing the properties of the target.
 1285   Returns:
 1286     A triple of (file path, name of the vc tool, name of the msbuild tool)
 1287   """
 1288   # Select a name for the output file.
 1289   out_file = ''
 1290   vc_tool = ''
 1291   msbuild_tool = ''
 1292   output_file_map = {
 1293       'executable': ('VCLinkerTool', 'Link', '$(OutDir)', '.exe'),
 1294       'shared_library': ('VCLinkerTool', 'Link', '$(OutDir)', '.dll'),
 1295       'loadable_module': ('VCLinkerTool', 'Link', '$(OutDir)', '.dll'),
 1296       'static_library': ('VCLibrarianTool', 'Lib', '$(OutDir)lib\\', '.lib'),
 1297   }
 1298   output_file_props = output_file_map.get(spec['type'])
 1299   if output_file_props and int(spec.get('msvs_auto_output_file', 1)):
 1300     vc_tool, msbuild_tool, out_dir, suffix = output_file_props
 1301     if spec.get('standalone_static_library', 0):
 1302       out_dir = '$(OutDir)'
 1303     out_dir = spec.get('product_dir', out_dir)
 1304     product_extension = spec.get('product_extension')
 1305     if product_extension:
 1306       suffix = '.' + product_extension
 1307     elif msbuild:
 1308       suffix = '$(TargetExt)'
 1309     prefix = spec.get('product_prefix', '')
 1310     product_name = spec.get('product_name', '$(ProjectName)')
 1311     out_file = ntpath.join(out_dir, prefix + product_name + suffix)
 1312   return out_file, vc_tool, msbuild_tool
 1313 
 1314 
 1315 def _GetOutputTargetExt(spec):
 1316   """Returns the extension for this target, including the dot
 1317 
 1318   If product_extension is specified, set target_extension to this to avoid
 1319   MSB8012, returns None otherwise. Ignores any target_extension settings in
 1320   the input files.
 1321 
 1322   Arguments:
 1323     spec: The target dictionary containing the properties of the target.
 1324   Returns:
 1325     A string with the extension, or None
 1326   """
 1327   target_extension = spec.get('product_extension')
 1328   if target_extension:
 1329     return '.' + target_extension
 1330   return None
 1331 
 1332 
 1333 def _GetDefines(config):
 1334   """Returns the list of preprocessor definitions for this configuation.
 1335 
 1336   Arguments:
 1337     config: The dictionary that defines the special processing to be done
 1338             for this configuration.
 1339   Returns:
 1340     The list of preprocessor definitions.
 1341   """
 1342   defines = []
 1343   for d in config.get('defines', []):
 1344     if type(d) == list:
 1345       fd = '='.join([str(dpart) for dpart in d])
 1346     else:
 1347       fd = str(d)
 1348     defines.append(fd)
 1349   return defines
 1350 
 1351 
 1352 def _GetDisabledWarnings(config):
 1353   return [str(i) for i in config.get('msvs_disabled_warnings', [])]
 1354 
 1355 
 1356 def _GetModuleDefinition(spec):
 1357   def_file = ''
 1358   if spec['type'] in ['shared_library', 'loadable_module', 'executable']:
 1359     def_files = [s for s in spec.get('sources', []) if s.endswith('.def')]
 1360     if len(def_files) == 1:
 1361       def_file = _FixPath(def_files[0])
 1362     elif def_files:
 1363       raise ValueError(
 1364           'Multiple module definition files in one target, target %s lists '
 1365           'multiple .def files: %s' % (
 1366               spec['target_name'], ' '.join(def_files)))
 1367   return def_file
 1368 
 1369 
 1370 def _ConvertToolsToExpectedForm(tools):
 1371   """Convert tools to a form expected by Visual Studio.
 1372 
 1373   Arguments:
 1374     tools: A dictionary of settings; the tool name is the key.
 1375   Returns:
 1376     A list of Tool objects.
 1377   """
 1378   tool_list = []
 1379   for tool, settings in tools.items():
 1380     # Collapse settings with lists.
 1381     settings_fixed = {}
 1382     for setting, value in settings.items():
 1383       if type(value) == list:
 1384         if ((tool == 'VCLinkerTool' and
 1385              setting == 'AdditionalDependencies') or
 1386             setting == 'AdditionalOptions'):
 1387           settings_fixed[setting] = ' '.join(value)
 1388         else:
 1389           settings_fixed[setting] = ';'.join(value)
 1390       else:
 1391         settings_fixed[setting] = value
 1392     # Add in this tool.
 1393     tool_list.append(MSVSProject.Tool(tool, settings_fixed))
 1394   return tool_list
 1395 
 1396 
 1397 def _AddConfigurationToMSVS(p, spec, tools, config, config_type, config_name):
 1398   """Add to the project file the configuration specified by config.
 1399 
 1400   Arguments:
 1401     p: The target project being generated.
 1402     spec: the target project dict.
 1403     tools: A dictionary of settings; the tool name is the key.
 1404     config: The dictionary that defines the special processing to be done
 1405             for this configuration.
 1406     config_type: The configuration type, a number as defined by Microsoft.
 1407     config_name: The name of the configuration.
 1408   """
 1409   attributes = _GetMSVSAttributes(spec, config, config_type)
 1410   # Add in this configuration.
 1411   tool_list = _ConvertToolsToExpectedForm(tools)
 1412   p.AddConfig(_ConfigFullName(config_name, config),
 1413               attrs=attributes, tools=tool_list)
 1414 
 1415 
 1416 def _GetMSVSAttributes(spec, config, config_type):
 1417   # Prepare configuration attributes.
 1418   prepared_attrs = {}
 1419   source_attrs = config.get('msvs_configuration_attributes', {})
 1420   for a in source_attrs:
 1421     prepared_attrs[a] = source_attrs[a]
 1422   # Add props files.
 1423   vsprops_dirs = config.get('msvs_props', [])
 1424   vsprops_dirs = _FixPaths(vsprops_dirs)
 1425   if vsprops_dirs:
 1426     prepared_attrs['InheritedPropertySheets'] = ';'.join(vsprops_dirs)
 1427   # Set configuration type.
 1428   prepared_attrs['ConfigurationType'] = config_type
 1429   output_dir = prepared_attrs.get('OutputDirectory',
 1430                                   '$(SolutionDir)$(ConfigurationName)')
 1431   prepared_attrs['OutputDirectory'] = _FixPath(output_dir) + '\\'
 1432   if 'IntermediateDirectory' not in prepared_attrs:
 1433     intermediate = '$(ConfigurationName)\\obj\\$(ProjectName)'
 1434     prepared_attrs['IntermediateDirectory'] = _FixPath(intermediate) + '\\'
 1435   else:
 1436     intermediate = _FixPath(prepared_attrs['IntermediateDirectory']) + '\\'
 1437     intermediate = MSVSSettings.FixVCMacroSlashes(intermediate)
 1438     prepared_attrs['IntermediateDirectory'] = intermediate
 1439   return prepared_attrs
 1440 
 1441 
 1442 def _AddNormalizedSources(sources_set, sources_array):
 1443   sources_set.update(_NormalizedSource(s) for s in sources_array)
 1444 
 1445 
 1446 def _PrepareListOfSources(spec, generator_flags, gyp_file):
 1447   """Prepare list of sources and excluded sources.
 1448 
 1449   Besides the sources specified directly in the spec, adds the gyp file so
 1450   that a change to it will cause a re-compile. Also adds appropriate sources
 1451   for actions and copies. Assumes later stage will un-exclude files which
 1452   have custom build steps attached.
 1453 
 1454   Arguments:
 1455     spec: The target dictionary containing the properties of the target.
 1456     gyp_file: The name of the gyp file.
 1457   Returns:
 1458     A pair of (list of sources, list of excluded sources).
 1459     The sources will be relative to the gyp file.
 1460   """
 1461   sources = OrderedSet()
 1462   _AddNormalizedSources(sources, spec.get('sources', []))
 1463   excluded_sources = OrderedSet()
 1464   # Add in the gyp file.
 1465   if not generator_flags.get('standalone'):
 1466     sources.add(gyp_file)
 1467 
 1468   # Add in 'action' inputs and outputs.
 1469   for a in spec.get('actions', []):
 1470     inputs = a['inputs']
 1471     inputs = [_NormalizedSource(i) for i in inputs]
 1472     # Add all inputs to sources and excluded sources.
 1473     inputs = OrderedSet(inputs)
 1474     sources.update(inputs)
 1475     if not spec.get('msvs_external_builder'):
 1476       excluded_sources.update(inputs)
 1477     if int(a.get('process_outputs_as_sources', False)):
 1478       _AddNormalizedSources(sources, a.get('outputs', []))
 1479   # Add in 'copies' inputs and outputs.
 1480   for cpy in spec.get('copies', []):
 1481     _AddNormalizedSources(sources, cpy.get('files', []))
 1482   return (sources, excluded_sources)
 1483 
 1484 
 1485 def _AdjustSourcesAndConvertToFilterHierarchy(
 1486     spec, options, gyp_dir, sources, excluded_sources, list_excluded, version):
 1487   """Adjusts the list of sources and excluded sources.
 1488 
 1489   Also converts the sets to lists.
 1490 
 1491   Arguments:
 1492     spec: The target dictionary containing the properties of the target.
 1493     options: Global generator options.
 1494     gyp_dir: The path to the gyp file being processed.
 1495     sources: A set of sources to be included for this project.
 1496     excluded_sources: A set of sources to be excluded for this project.
 1497     version: A MSVSVersion object.
 1498   Returns:
 1499     A trio of (list of sources, list of excluded sources,
 1500                path of excluded IDL file)
 1501   """
 1502   # Exclude excluded sources coming into the generator.
 1503   excluded_sources.update(OrderedSet(spec.get('sources_excluded', [])))
 1504   # Add excluded sources into sources for good measure.
 1505   sources.update(excluded_sources)
 1506   # Convert to proper windows form.
 1507   # NOTE: sources goes from being a set to a list here.
 1508   # NOTE: excluded_sources goes from being a set to a list here.
 1509   sources = _FixPaths(sources)
 1510   # Convert to proper windows form.
 1511   excluded_sources = _FixPaths(excluded_sources)
 1512 
 1513   excluded_idl = _IdlFilesHandledNonNatively(spec, sources)
 1514 
 1515   precompiled_related = _GetPrecompileRelatedFiles(spec)
 1516   # Find the excluded ones, minus the precompiled header related ones.
 1517   fully_excluded = [i for i in excluded_sources if i not in precompiled_related]
 1518 
 1519   # Convert to folders and the right slashes.
 1520   sources = [i.split('\\') for i in sources]
 1521   sources = _ConvertSourcesToFilterHierarchy(sources, excluded=fully_excluded,
 1522                                              list_excluded=list_excluded,
 1523                                              msvs_version=version)
 1524 
 1525   # Prune filters with a single child to flatten ugly directory structures
 1526   # such as ../../src/modules/module1 etc.
 1527   if version.UsesVcxproj():
 1528     while all([isinstance(s, MSVSProject.Filter) for s in sources]) \
 1529         and len(set([s.name for s in sources])) == 1:
 1530       assert all([len(s.contents) == 1 for s in sources])
 1531       sources = [s.contents[0] for s in sources]
 1532   else:
 1533     while len(sources) == 1 and isinstance(sources[0], MSVSProject.Filter):
 1534       sources = sources[0].contents
 1535 
 1536   return sources, excluded_sources, excluded_idl
 1537 
 1538 
 1539 def _IdlFilesHandledNonNatively(spec, sources):
 1540   # If any non-native rules use 'idl' as an extension exclude idl files.
 1541   # Gather a list here to use later.
 1542   using_idl = False
 1543   for rule in spec.get('rules', []):
 1544     if rule['extension'] == 'idl' and int(rule.get('msvs_external_rule', 0)):
 1545       using_idl = True
 1546       break
 1547   if using_idl:
 1548     excluded_idl = [i for i in sources if i.endswith('.idl')]
 1549   else:
 1550     excluded_idl = []
 1551   return excluded_idl
 1552 
 1553 
 1554 def _GetPrecompileRelatedFiles(spec):
 1555   # Gather a list of precompiled header related sources.
 1556   precompiled_related = []
 1557   for _, config in spec['configurations'].items():
 1558     for k in precomp_keys:
 1559       f = config.get(k)
 1560       if f:
 1561         precompiled_related.append(_FixPath(f))
 1562   return precompiled_related
 1563 
 1564 
 1565 def _ExcludeFilesFromBeingBuilt(p, spec, excluded_sources, excluded_idl,
 1566                                 list_excluded):
 1567   exclusions = _GetExcludedFilesFromBuild(spec, excluded_sources, excluded_idl)
 1568   for file_name, excluded_configs in exclusions.items():
 1569     if (not list_excluded and
 1570             len(excluded_configs) == len(spec['configurations'])):
 1571       # If we're not listing excluded files, then they won't appear in the
 1572       # project, so don't try to configure them to be excluded.
 1573       pass
 1574     else:
 1575       for config_name, config in excluded_configs:
 1576         p.AddFileConfig(file_name, _ConfigFullName(config_name, config),
 1577                         {'ExcludedFromBuild': 'true'})
 1578 
 1579 
 1580 def _GetExcludedFilesFromBuild(spec, excluded_sources, excluded_idl):
 1581   exclusions = {}
 1582   # Exclude excluded sources from being built.
 1583   for f in excluded_sources:
 1584     excluded_configs = []
 1585     for config_name, config in spec['configurations'].items():
 1586       precomped = [_FixPath(config.get(i, '')) for i in precomp_keys]
 1587       # Don't do this for ones that are precompiled header related.
 1588       if f not in precomped:
 1589         excluded_configs.append((config_name, config))
 1590     exclusions[f] = excluded_configs
 1591   # If any non-native rules use 'idl' as an extension exclude idl files.
 1592   # Exclude them now.
 1593   for f in excluded_idl:
 1594     excluded_configs = []
 1595     for config_name, config in spec['configurations'].items():
 1596       excluded_configs.append((config_name, config))
 1597     exclusions[f] = excluded_configs
 1598   return exclusions
 1599 
 1600 
 1601 def _AddToolFilesToMSVS(p, spec):
 1602   # Add in tool files (rules).
 1603   tool_files = OrderedSet()
 1604   for _, config in spec['configurations'].items():
 1605     for f in config.get('msvs_tool_files', []):
 1606       tool_files.add(f)
 1607   for f in tool_files:
 1608     p.AddToolFile(f)
 1609 
 1610 
 1611 def _HandlePreCompiledHeaders(p, sources, spec):
 1612   # Pre-compiled header source stubs need a different compiler flag
 1613   # (generate precompiled header) and any source file not of the same
 1614   # kind (i.e. C vs. C++) as the precompiled header source stub needs
 1615   # to have use of precompiled headers disabled.
 1616   extensions_excluded_from_precompile = []
 1617   for config_name, config in spec['configurations'].items():
 1618     source = config.get('msvs_precompiled_source')
 1619     if source:
 1620       source = _FixPath(source)
 1621       # UsePrecompiledHeader=1 for if using precompiled headers.
 1622       tool = MSVSProject.Tool('VCCLCompilerTool',
 1623                               {'UsePrecompiledHeader': '1'})
 1624       p.AddFileConfig(source, _ConfigFullName(config_name, config),
 1625                       {}, tools=[tool])
 1626       basename, extension = os.path.splitext(source)
 1627       if extension == '.c':
 1628         extensions_excluded_from_precompile = ['.cc', '.cpp', '.cxx']
 1629       else:
 1630         extensions_excluded_from_precompile = ['.c']
 1631   def DisableForSourceTree(source_tree):
 1632     for source in source_tree:
 1633       if isinstance(source, MSVSProject.Filter):
 1634         DisableForSourceTree(source.contents)
 1635       else:
 1636         basename, extension = os.path.splitext(source)
 1637         if extension in extensions_excluded_from_precompile:
 1638           for config_name, config in spec['configurations'].items():
 1639             tool = MSVSProject.Tool('VCCLCompilerTool',
 1640                                     {'UsePrecompiledHeader': '0',
 1641                                      'ForcedIncludeFiles': '$(NOINHERIT)'})
 1642             p.AddFileConfig(_FixPath(source),
 1643                             _ConfigFullName(config_name, config),
 1644                             {}, tools=[tool])
 1645   # Do nothing if there was no precompiled source.
 1646   if extensions_excluded_from_precompile:
 1647     DisableForSourceTree(sources)
 1648 
 1649 
 1650 def _AddActions(actions_to_add, spec, relative_path_of_gyp_file):
 1651   # Add actions.
 1652   actions = spec.get('actions', [])
 1653   # Don't setup_env every time. When all the actions are run together in one
 1654   # batch file in VS, the PATH will grow too long.
 1655   # Membership in this set means that the cygwin environment has been set up,
 1656   # and does not need to be set up again.
 1657   have_setup_env = set()
 1658   for a in actions:
 1659     # Attach actions to the gyp file if nothing else is there.
 1660     inputs = a.get('inputs') or [relative_path_of_gyp_file]
 1661     attached_to = inputs[0]
 1662     need_setup_env = attached_to not in have_setup_env
 1663     cmd = _BuildCommandLineForRule(spec, a, has_input_path=False,
 1664                                    do_setup_env=need_setup_env)
 1665     have_setup_env.add(attached_to)
 1666     # Add the action.
 1667     _AddActionStep(actions_to_add,
 1668                    inputs=inputs,
 1669                    outputs=a.get('outputs', []),
 1670                    description=a.get('message', a['action_name']),
 1671                    command=cmd)
 1672 
 1673 
 1674 def _WriteMSVSUserFile(project_path, version, spec):
 1675   # Add run_as and test targets.
 1676   if 'run_as' in spec:
 1677     run_as = spec['run_as']
 1678     action = run_as.get('action', [])
 1679     environment = run_as.get('environment', [])
 1680     working_directory = run_as.get('working_directory', '.')
 1681   elif int(spec.get('test', 0)):
 1682     action = ['$(TargetPath)', '--gtest_print_time']
 1683     environment = []
 1684     working_directory = '.'
 1685   else:
 1686     return  # Nothing to add
 1687   # Write out the user file.
 1688   user_file = _CreateMSVSUserFile(project_path, version, spec)
 1689   for config_name, c_data in spec['configurations'].items():
 1690     user_file.AddDebugSettings(_ConfigFullName(config_name, c_data),
 1691                                action, environment, working_directory)
 1692   user_file.WriteIfChanged()
 1693 
 1694 
 1695 def _AddCopies(actions_to_add, spec):
 1696   copies = _GetCopies(spec)
 1697   for inputs, outputs, cmd, description in copies:
 1698     _AddActionStep(actions_to_add, inputs=inputs, outputs=outputs,
 1699                    description=description, command=cmd)
 1700 
 1701 
 1702 def _GetCopies(spec):
 1703   copies = []
 1704   # Add copies.
 1705   for cpy in spec.get('copies', []):
 1706     for src in cpy.get('files', []):
 1707       dst = os.path.join(cpy['destination'], os.path.basename(src))
 1708       # _AddCustomBuildToolForMSVS() will call _FixPath() on the inputs and
 1709       # outputs, so do the same for our generated command line.
 1710       if src.endswith('/'):
 1711         src_bare = src[:-1]
 1712         base_dir = posixpath.split(src_bare)[0]
 1713         outer_dir = posixpath.split(src_bare)[1]
 1714         cmd = 'cd "%s" && xcopy /e /f /y "%s" "%s\\%s\\"' % (
 1715             _FixPath(base_dir), outer_dir, _FixPath(dst), outer_dir)
 1716         copies.append(([src], ['dummy_copies', dst], cmd,
 1717                        'Copying %s to %s' % (src, dst)))
 1718       else:
 1719         cmd = 'mkdir "%s" 2>nul & set ERRORLEVEL=0 & copy /Y "%s" "%s"' % (
 1720             _FixPath(cpy['destination']), _FixPath(src), _FixPath(dst))
 1721         copies.append(([src], [dst], cmd, 'Copying %s to %s' % (src, dst)))
 1722   return copies
 1723 
 1724 
 1725 def _GetPathDict(root, path):
 1726   # |path| will eventually be empty (in the recursive calls) if it was initially
 1727   # relative; otherwise it will eventually end up as '\', 'D:\', etc.
 1728   if not path or path.endswith(os.sep):
 1729     return root
 1730   parent, folder = os.path.split(path)
 1731   parent_dict = _GetPathDict(root, parent)
 1732   if folder not in parent_dict:
 1733     parent_dict[folder] = dict()
 1734   return parent_dict[folder]
 1735 
 1736 
 1737 def _DictsToFolders(base_path, bucket, flat):
 1738   # Convert to folders recursively.
 1739   children = []
 1740   for folder, contents in bucket.items():
 1741     if type(contents) == dict:
 1742       folder_children = _DictsToFolders(os.path.join(base_path, folder),
 1743                                         contents, flat)
 1744       if flat:
 1745         children += folder_children
 1746       else:
 1747         folder_children = MSVSNew.MSVSFolder(os.path.join(base_path, folder),
 1748                                              name='(' + folder + ')',
 1749                                              entries=folder_children)
 1750         children.append(folder_children)
 1751     else:
 1752       children.append(contents)
 1753   return children
 1754 
 1755 
 1756 def _CollapseSingles(parent, node):
 1757   # Recursively explorer the tree of dicts looking for projects which are
 1758   # the sole item in a folder which has the same name as the project. Bring
 1759   # such projects up one level.
 1760   if (type(node) == dict and
 1761       len(node) == 1 and
 1762       list(node)[0] == parent + '.vcproj'):
 1763     return node[list(node)[0]]
 1764   if type(node) != dict:
 1765     return node
 1766   for child in node:
 1767     node[child] = _CollapseSingles(child, node[child])
 1768   return node
 1769 
 1770 
 1771 def _GatherSolutionFolders(sln_projects, project_objects, flat):
 1772   root = {}
 1773   # Convert into a tree of dicts on path.
 1774   for p in sln_projects:
 1775     gyp_file, target = gyp.common.ParseQualifiedTarget(p)[0:2]
 1776     gyp_dir = os.path.dirname(gyp_file)
 1777     path_dict = _GetPathDict(root, gyp_dir)
 1778     path_dict[target + '.vcproj'] = project_objects[p]
 1779   # Walk down from the top until we hit a folder that has more than one entry.
 1780   # In practice, this strips the top-level "src/" dir from the hierarchy in
 1781   # the solution.
 1782   while len(root) == 1 and type(root[list(root)[0]]) == dict:
 1783     root = root[list(root)[0]]
 1784   # Collapse singles.
 1785   root = _CollapseSingles('', root)
 1786   # Merge buckets until everything is a root entry.
 1787   return _DictsToFolders('', root, flat)
 1788 
 1789 
 1790 def _GetPathOfProject(qualified_target, spec, options, msvs_version):
 1791   default_config = _GetDefaultConfiguration(spec)
 1792   proj_filename = default_config.get('msvs_existing_vcproj')
 1793   if not proj_filename:
 1794     proj_filename = (spec['target_name'] + options.suffix +
 1795                      msvs_version.ProjectExtension())
 1796 
 1797   build_file = gyp.common.BuildFile(qualified_target)
 1798   proj_path = os.path.join(os.path.dirname(build_file), proj_filename)
 1799   fix_prefix = None
 1800   if options.generator_output:
 1801     project_dir_path = os.path.dirname(os.path.abspath(proj_path))
 1802     proj_path = os.path.join(options.generator_output, proj_path)
 1803     fix_prefix = gyp.common.RelativePath(project_dir_path,
 1804                                          os.path.dirname(proj_path))
 1805   return proj_path, fix_prefix
 1806 
 1807 
 1808 def _GetPlatformOverridesOfProject(spec):
 1809   # Prepare a dict indicating which project configurations are used for which
 1810   # solution configurations for this target.
 1811   config_platform_overrides = {}
 1812   for config_name, c in spec['configurations'].items():
 1813     config_fullname = _ConfigFullName(config_name, c)
 1814     platform = c.get('msvs_target_platform', _ConfigPlatform(c))
 1815     fixed_config_fullname = '%s|%s' % (
 1816         _ConfigBaseName(config_name, _ConfigPlatform(c)), platform)
 1817     config_platform_overrides[config_fullname] = fixed_config_fullname
 1818   return config_platform_overrides
 1819 
 1820 
 1821 def _CreateProjectObjects(target_list, target_dicts, options, msvs_version):
 1822   """Create a MSVSProject object for the targets found in target list.
 1823 
 1824   Arguments:
 1825     target_list: the list of targets to generate project objects for.
 1826     target_dicts: the dictionary of specifications.
 1827     options: global generator options.
 1828     msvs_version: the MSVSVersion object.
 1829   Returns:
 1830     A set of created projects, keyed by target.
 1831   """
 1832   global fixpath_prefix
 1833   # Generate each project.
 1834   projects = {}
 1835   for qualified_target in target_list:
 1836     spec = target_dicts[qualified_target]
 1837     if spec['toolset'] != 'target':
 1838       raise GypError(
 1839           'Multiple toolsets not supported in msvs build (target %s)' %
 1840           qualified_target)
 1841     proj_path, fixpath_prefix = _GetPathOfProject(qualified_target, spec,
 1842                                                   options, msvs_version)
 1843     guid = _GetGuidOfProject(proj_path, spec)
 1844     overrides = _GetPlatformOverridesOfProject(spec)
 1845     build_file = gyp.common.BuildFile(qualified_target)
 1846     # Create object for this project.
 1847     obj = MSVSNew.MSVSProject(
 1848         proj_path,
 1849         name=spec['target_name'],
 1850         guid=guid,
 1851         spec=spec,
 1852         build_file=build_file,
 1853         config_platform_overrides=overrides,
 1854         fixpath_prefix=fixpath_prefix)
 1855     # Set project toolset if any (MS build only)
 1856     if msvs_version.UsesVcxproj():
 1857       obj.set_msbuild_toolset(
 1858           _GetMsbuildToolsetOfProject(proj_path, spec, msvs_version))
 1859     projects[qualified_target] = obj
 1860   # Set all the dependencies, but not if we are using an external builder like
 1861   # ninja
 1862   for project in projects.values():
 1863     if not project.spec.get('msvs_external_builder'):
 1864       deps = project.spec.get('dependencies', [])
 1865       deps = [projects[d] for d in deps]
 1866       project.set_dependencies(deps)
 1867   return projects
 1868 
 1869 
 1870 def _InitNinjaFlavor(params, target_list, target_dicts):
 1871   """Initialize targets for the ninja flavor.
 1872 
 1873   This sets up the necessary variables in the targets to generate msvs projects
 1874   that use ninja as an external builder. The variables in the spec are only set
 1875   if they have not been set. This allows individual specs to override the
 1876   default values initialized here.
 1877   Arguments:
 1878     params: Params provided to the generator.
 1879     target_list: List of target pairs: 'base/base.gyp:base'.
 1880     target_dicts: Dict of target properties keyed on target pair.
 1881   """
 1882   for qualified_target in target_list:
 1883     spec = target_dicts[qualified_target]
 1884     if spec.get('msvs_external_builder'):
 1885       # The spec explicitly defined an external builder, so don't change it.
 1886       continue
 1887 
 1888     path_to_ninja = spec.get('msvs_path_to_ninja', 'ninja.exe')
 1889 
 1890     spec['msvs_external_builder'] = 'ninja'
 1891     if not spec.get('msvs_external_builder_out_dir'):
 1892       gyp_file, _, _ = gyp.common.ParseQualifiedTarget(qualified_target)
 1893       gyp_dir = os.path.dirname(gyp_file)
 1894       configuration = '$(Configuration)'
 1895       if params.get('target_arch') == 'x64':
 1896         configuration += '_x64'
 1897       if params.get('target_arch') == 'arm64':
 1898         configuration += '_arm64'
 1899       spec['msvs_external_builder_out_dir'] = os.path.join(
 1900           gyp.common.RelativePath(params['options'].toplevel_dir, gyp_dir),
 1901           ninja_generator.ComputeOutputDir(params),
 1902           configuration)
 1903     if not spec.get('msvs_external_builder_build_cmd'):
 1904       spec['msvs_external_builder_build_cmd'] = [
 1905         path_to_ninja,
 1906         '-C',
 1907         '$(OutDir)',
 1908         '$(ProjectName)',
 1909       ]
 1910     if not spec.get('msvs_external_builder_clean_cmd'):
 1911       spec['msvs_external_builder_clean_cmd'] = [
 1912         path_to_ninja,
 1913         '-C',
 1914         '$(OutDir)',
 1915         '-tclean',
 1916         '$(ProjectName)',
 1917       ]
 1918 
 1919 
 1920 def CalculateVariables(default_variables, params):
 1921   """Generated variables that require params to be known."""
 1922 
 1923   generator_flags = params.get('generator_flags', {})
 1924 
 1925   # Select project file format version (if unset, default to auto detecting).
 1926   msvs_version = MSVSVersion.SelectVisualStudioVersion(
 1927       generator_flags.get('msvs_version', 'auto'))
 1928   # Stash msvs_version for later (so we don't have to probe the system twice).
 1929   params['msvs_version'] = msvs_version
 1930 
 1931   # Set a variable so conditions can be based on msvs_version.
 1932   default_variables['MSVS_VERSION'] = msvs_version.ShortName()
 1933 
 1934   # To determine processor word size on Windows, in addition to checking
 1935   # PROCESSOR_ARCHITECTURE (which reflects the word size of the current
 1936   # process), it is also necessary to check PROCESSOR_ARCITEW6432 (which
 1937   # contains the actual word size of the system when running thru WOW64).
 1938   if (os.environ.get('PROCESSOR_ARCHITECTURE', '').find('64') >= 0 or
 1939       os.environ.get('PROCESSOR_ARCHITEW6432', '').find('64') >= 0):
 1940     default_variables['MSVS_OS_BITS'] = 64
 1941   else:
 1942     default_variables['MSVS_OS_BITS'] = 32
 1943 
 1944   if gyp.common.GetFlavor(params) == 'ninja':
 1945     default_variables['SHARED_INTERMEDIATE_DIR'] = '$(OutDir)gen'
 1946 
 1947 
 1948 def PerformBuild(data, configurations, params):
 1949   options = params['options']
 1950   msvs_version = params['msvs_version']
 1951   devenv = os.path.join(msvs_version.path, 'Common7', 'IDE', 'devenv.com')
 1952 
 1953   for build_file, build_file_dict in data.items():
 1954     (build_file_root, build_file_ext) = os.path.splitext(build_file)
 1955     if build_file_ext != '.gyp':
 1956       continue
 1957     sln_path = build_file_root + options.suffix + '.sln'
 1958     if options.generator_output:
 1959       sln_path = os.path.join(options.generator_output, sln_path)
 1960 
 1961   for config in configurations:
 1962     arguments = [devenv, sln_path, '/Build', config]
 1963     print('Building [%s]: %s' % (config, arguments))
 1964     rtn = subprocess.check_call(arguments)
 1965 
 1966 
 1967 def GenerateOutput(target_list, target_dicts, data, params):
 1968   """Generate .sln and .vcproj files.
 1969 
 1970   This is the entry point for this generator.
 1971   Arguments:
 1972     target_list: List of target pairs: 'base/base.gyp:base'.
 1973     target_dicts: Dict of target properties keyed on target pair.
 1974     data: Dictionary containing per .gyp data.
 1975   """
 1976   global fixpath_prefix
 1977 
 1978   options = params['options']
 1979 
 1980   # Get the project file format version back out of where we stashed it in
 1981   # GeneratorCalculatedVariables.
 1982   msvs_version = params['msvs_version']
 1983 
 1984   generator_flags = params.get('generator_flags', {})
 1985 
 1986   # Optionally shard targets marked with 'msvs_shard': SHARD_COUNT.
 1987   (target_list, target_dicts) = MSVSUtil.ShardTargets(target_list, target_dicts)
 1988 
 1989   # Optionally use the large PDB workaround for targets marked with
 1990   # 'msvs_large_pdb': 1.
 1991   (target_list, target_dicts) = MSVSUtil.InsertLargePdbShims(
 1992         target_list, target_dicts, generator_default_variables)
 1993 
 1994   # Optionally configure each spec to use ninja as the external builder.
 1995   if params.get('flavor') == 'ninja':
 1996     _InitNinjaFlavor(params, target_list, target_dicts)
 1997 
 1998   # Prepare the set of configurations.
 1999   configs = set()
 2000   for qualified_target in target_list:
 2001     spec = target_dicts[qualified_target]
 2002     for config_name, config in spec['configurations'].items():
 2003       configs.add(_ConfigFullName(config_name, config))
 2004   configs = list(configs)
 2005 
 2006   # Figure out all the projects that will be generated and their guids
 2007   project_objects = _CreateProjectObjects(target_list, target_dicts, options,
 2008                                           msvs_version)
 2009 
 2010   # Generate each project.
 2011   missing_sources = []
 2012   for project in project_objects.values():
 2013     fixpath_prefix = project.fixpath_prefix
 2014     missing_sources.extend(_GenerateProject(project, options, msvs_version,
 2015                                             generator_flags))
 2016   fixpath_prefix = None
 2017 
 2018   for build_file in data:
 2019     # Validate build_file extension
 2020     if not build_file.endswith('.gyp'):
 2021       continue
 2022     sln_path = os.path.splitext(build_file)[0] + options.suffix + '.sln'
 2023     if options.generator_output:
 2024       sln_path = os.path.join(options.generator_output, sln_path)
 2025     # Get projects in the solution, and their dependents.
 2026     sln_projects = gyp.common.BuildFileTargets(target_list, build_file)
 2027     sln_projects += gyp.common.DeepDependencyTargets(target_dicts, sln_projects)
 2028     # Create folder hierarchy.
 2029     root_entries = _GatherSolutionFolders(
 2030         sln_projects, project_objects, flat=msvs_version.FlatSolution())
 2031     # Create solution.
 2032     sln = MSVSNew.MSVSSolution(sln_path,
 2033                                entries=root_entries,
 2034                                variants=configs,
 2035                                websiteProperties=False,
 2036                                version=msvs_version)
 2037     sln.Write()
 2038 
 2039   if missing_sources:
 2040     error_message = "Missing input files:\n" + \
 2041                     '\n'.join(set(missing_sources))
 2042     if generator_flags.get('msvs_error_on_missing_sources', False):
 2043       raise GypError(error_message)
 2044     else:
 2045       print("Warning: " + error_message, file=sys.stdout)
 2046 
 2047 
 2048 def _GenerateMSBuildFiltersFile(filters_path, source_files,
 2049                                 rule_dependencies, extension_to_rule_name,
 2050                                 platforms):
 2051   """Generate the filters file.
 2052 
 2053   This file is used by Visual Studio to organize the presentation of source
 2054   files into folders.
 2055 
 2056   Arguments:
 2057       filters_path: The path of the file to be created.
 2058       source_files: The hierarchical structure of all the sources.
 2059       extension_to_rule_name: A dictionary mapping file extensions to rules.
 2060   """
 2061   filter_group = []
 2062   source_group = []
 2063   _AppendFiltersForMSBuild('', source_files, rule_dependencies,
 2064                            extension_to_rule_name, platforms,
 2065                            filter_group, source_group)
 2066   if filter_group:
 2067     content = ['Project',
 2068                {'ToolsVersion': '4.0',
 2069                 'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'
 2070                },
 2071                ['ItemGroup'] + filter_group,
 2072                ['ItemGroup'] + source_group
 2073               ]
 2074     easy_xml.WriteXmlIfChanged(content, filters_path, pretty=True, win32=True)
 2075   elif os.path.exists(filters_path):
 2076     # We don't need this filter anymore.  Delete the old filter file.
 2077     os.unlink(filters_path)
 2078 
 2079 
 2080 def _AppendFiltersForMSBuild(parent_filter_name, sources, rule_dependencies,
 2081                              extension_to_rule_name, platforms,
 2082                              filter_group, source_group):
 2083   """Creates the list of filters and sources to be added in the filter file.
 2084 
 2085   Args:
 2086       parent_filter_name: The name of the filter under which the sources are
 2087           found.
 2088       sources: The hierarchy of filters and sources to process.
 2089       extension_to_rule_name: A dictionary mapping file extensions to rules.
 2090       filter_group: The list to which filter entries will be appended.
 2091       source_group: The list to which source entries will be appeneded.
 2092   """
 2093   for source in sources:
 2094     if isinstance(source, MSVSProject.Filter):
 2095       # We have a sub-filter.  Create the name of that sub-filter.
 2096       if not parent_filter_name:
 2097         filter_name = source.name
 2098       else:
 2099         filter_name = '%s\\%s' % (parent_filter_name, source.name)
 2100       # Add the filter to the group.
 2101       filter_group.append(
 2102           ['Filter', {'Include': filter_name},
 2103            ['UniqueIdentifier', MSVSNew.MakeGuid(source.name)]])
 2104       # Recurse and add its dependents.
 2105       _AppendFiltersForMSBuild(filter_name, source.contents,
 2106                                rule_dependencies, extension_to_rule_name,
 2107                                platforms, filter_group, source_group)
 2108     else:
 2109       # It's a source.  Create a source entry.
 2110       _, element = _MapFileToMsBuildSourceType(source, rule_dependencies,
 2111                                                extension_to_rule_name,
 2112                                                platforms)
 2113       source_entry = [element, {'Include': source}]
 2114       # Specify the filter it is part of, if any.
 2115       if parent_filter_name:
 2116         source_entry.append(['Filter', parent_filter_name])
 2117       source_group.append(source_entry)
 2118 
 2119 
 2120 def _MapFileToMsBuildSourceType(source, rule_dependencies,
 2121                                 extension_to_rule_name, platforms):
 2122   """Returns the group and element type of the source file.
 2123 
 2124   Arguments:
 2125       source: The source file name.
 2126       extension_to_rule_name: A dictionary mapping file extensions to rules.
 2127 
 2128   Returns:
 2129       A pair of (group this file should be part of, the label of element)
 2130   """
 2131   _, ext = os.path.splitext(source)
 2132   if ext in extension_to_rule_name:
 2133     group = 'rule'
 2134     element = extension_to_rule_name[ext]
 2135   elif ext in ['.cc', '.cpp', '.c', '.cxx', '.mm']:
 2136     group = 'compile'
 2137     element = 'ClCompile'
 2138   elif ext in ['.h', '.hxx']:
 2139     group = 'include'
 2140     element = 'ClInclude'
 2141   elif ext == '.rc':
 2142     group = 'resource'
 2143     element = 'ResourceCompile'
 2144   elif ext == '.asm':
 2145     group = 'masm'
 2146     element = 'MASM'
 2147     for platform in platforms:
 2148       if platform.lower() in ['arm', 'arm64']:
 2149        element = 'MARMASM'
 2150   elif ext == '.idl':
 2151     group = 'midl'
 2152     element = 'Midl'
 2153   elif source in rule_dependencies:
 2154     group = 'rule_dependency'
 2155     element = 'CustomBuild'
 2156   else:
 2157     group = 'none'
 2158     element = 'None'
 2159   return (group, element)
 2160 
 2161 
 2162 def _GenerateRulesForMSBuild(output_dir, options, spec,
 2163                              sources, excluded_sources,
 2164                              props_files_of_rules, targets_files_of_rules,
 2165                              actions_to_add, rule_dependencies,
 2166                              extension_to_rule_name):
 2167   # MSBuild rules are implemented using three files: an XML file, a .targets
 2168   # file and a .props file.
 2169   # See http://blogs.msdn.com/b/vcblog/archive/2010/04/21/quick-help-on-vs2010-custom-build-rule.aspx
 2170   # for more details.
 2171   rules = spec.get('rules', [])
 2172   rules_native = [r for r in rules if not int(r.get('msvs_external_rule', 0))]
 2173   rules_external = [r for r in rules if int(r.get('msvs_external_rule', 0))]
 2174 
 2175   msbuild_rules = []
 2176   for rule in rules_native:
 2177     # Skip a rule with no action and no inputs.
 2178     if 'action' not in rule and not rule.get('rule_sources', []):
 2179       continue
 2180     msbuild_rule = MSBuildRule(rule, spec)
 2181     msbuild_rules.append(msbuild_rule)
 2182     rule_dependencies.update(msbuild_rule.additional_dependencies.split(';'))
 2183     extension_to_rule_name[msbuild_rule.extension] = msbuild_rule.rule_name
 2184   if msbuild_rules:
 2185     base = spec['target_name'] + options.suffix
 2186     props_name = base + '.props'
 2187     targets_name = base + '.targets'
 2188     xml_name = base + '.xml'
 2189 
 2190     props_files_of_rules.add(props_name)
 2191     targets_files_of_rules.add(targets_name)
 2192 
 2193     props_path = os.path.join(output_dir, props_name)
 2194     targets_path = os.path.join(output_dir, targets_name)
 2195     xml_path = os.path.join(output_dir, xml_name)
 2196 
 2197     _GenerateMSBuildRulePropsFile(props_path, msbuild_rules)
 2198     _GenerateMSBuildRuleTargetsFile(targets_path, msbuild_rules)
 2199     _GenerateMSBuildRuleXmlFile(xml_path, msbuild_rules)
 2200 
 2201   if rules_external:
 2202     _GenerateExternalRules(rules_external, output_dir, spec,
 2203                            sources, options, actions_to_add)
 2204   _AdjustSourcesForRules(rules, sources, excluded_sources, True)
 2205 
 2206 
 2207 class MSBuildRule(object):
 2208   """Used to store information used to generate an MSBuild rule.
 2209 
 2210   Attributes:
 2211     rule_name: The rule name, sanitized to use in XML.
 2212     target_name: The name of the target.
 2213     after_targets: The name of the AfterTargets element.
 2214     before_targets: The name of the BeforeTargets element.
 2215     depends_on: The name of the DependsOn element.
 2216     compute_output: The name of the ComputeOutput element.
 2217     dirs_to_make: The name of the DirsToMake element.
 2218     inputs: The name of the _inputs element.
 2219     tlog: The name of the _tlog element.
 2220     extension: The extension this rule applies to.
 2221     description: The message displayed when this rule is invoked.
 2222     additional_dependencies: A string listing additional dependencies.
 2223     outputs: The outputs of this rule.
 2224     command: The command used to run the rule.
 2225   """
 2226 
 2227   def __init__(self, rule, spec):
 2228     self.display_name = rule['rule_name']
 2229     # Assure that the rule name is only characters and numbers
 2230     self.rule_name = re.sub(r'\W', '_', self.display_name)
 2231     # Create the various element names, following the example set by the
 2232     # Visual Studio 2008 to 2010 conversion.  I don't know if VS2010
 2233     # is sensitive to the exact names.
 2234     self.target_name = '_' + self.rule_name
 2235     self.after_targets = self.rule_name + 'AfterTargets'
 2236     self.before_targets = self.rule_name + 'BeforeTargets'
 2237     self.depends_on = self.rule_name + 'DependsOn'
 2238     self.compute_output = 'Compute%sOutput' % self.rule_name
 2239     self.dirs_to_make = self.rule_name + 'DirsToMake'
 2240     self.inputs = self.rule_name + '_inputs'
 2241     self.tlog = self.rule_name + '_tlog'
 2242     self.extension = rule['extension']
 2243     if not self.extension.startswith('.'):
 2244       self.extension = '.' + self.extension
 2245 
 2246     self.description = MSVSSettings.ConvertVCMacrosToMSBuild(
 2247         rule.get('message', self.rule_name))
 2248     old_additional_dependencies = _FixPaths(rule.get('inputs', []))
 2249     self.additional_dependencies = (
 2250         ';'.join([MSVSSettings.ConvertVCMacrosToMSBuild(i)
 2251                   for i in old_additional_dependencies]))
 2252     old_outputs = _FixPaths(rule.get('outputs', []))
 2253     self.outputs = ';'.join([MSVSSettings.ConvertVCMacrosToMSBuild(i)
 2254                              for i in old_outputs])
 2255     old_command = _BuildCommandLineForRule(spec, rule, has_input_path=True,
 2256                                            do_setup_env=True)
 2257     self.command = MSVSSettings.ConvertVCMacrosToMSBuild(old_command)
 2258 
 2259 
 2260 def _GenerateMSBuildRulePropsFile(props_path, msbuild_rules):
 2261   """Generate the .props file."""
 2262   content = ['Project',
 2263              {'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'}]
 2264   for rule in msbuild_rules:
 2265     content.extend([
 2266         ['PropertyGroup',
 2267          {'Condition': "'$(%s)' == '' and '$(%s)' == '' and "
 2268           "'$(ConfigurationType)' != 'Makefile'" % (rule.before_targets,
 2269                                                     rule.after_targets)
 2270          },
 2271          [rule.before_targets, 'Midl'],
 2272          [rule.after_targets, 'CustomBuild'],
 2273         ],
 2274         ['PropertyGroup',
 2275          [rule.depends_on,
 2276           {'Condition': "'$(ConfigurationType)' != 'Makefile'"},
 2277           '_SelectedFiles;$(%s)' % rule.depends_on
 2278          ],
 2279         ],
 2280         ['ItemDefinitionGroup',
 2281          [rule.rule_name,
 2282           ['CommandLineTemplate', rule.command],
 2283           ['Outputs', rule.outputs],
 2284           ['ExecutionDescription', rule.description],
 2285           ['AdditionalDependencies', rule.additional_dependencies],
 2286          ],
 2287         ]
 2288     ])
 2289   easy_xml.WriteXmlIfChanged(content, props_path, pretty=True, win32=True)
 2290 
 2291 
 2292 def _GenerateMSBuildRuleTargetsFile(targets_path, msbuild_rules):
 2293   """Generate the .targets file."""
 2294   content = ['Project',
 2295              {'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'
 2296              }
 2297             ]
 2298   item_group = [
 2299       'ItemGroup',
 2300       ['PropertyPageSchema',
 2301        {'Include': '$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml'}
 2302       ]
 2303     ]
 2304   for rule in msbuild_rules:
 2305     item_group.append(
 2306         ['AvailableItemName',
 2307          {'Include': rule.rule_name},
 2308          ['Targets', rule.target_name],
 2309         ])
 2310   content.append(item_group)
 2311 
 2312   for rule in msbuild_rules:
 2313     content.append(
 2314         ['UsingTask',
 2315          {'TaskName': rule.rule_name,
 2316           'TaskFactory': 'XamlTaskFactory',
 2317           'AssemblyName': 'Microsoft.Build.Tasks.v4.0'
 2318          },
 2319          ['Task', '$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml'],
 2320         ])
 2321   for rule in msbuild_rules:
 2322     rule_name = rule.rule_name
 2323     target_outputs = '%%(%s.Outputs)' % rule_name
 2324     target_inputs = ('%%(%s.Identity);%%(%s.AdditionalDependencies);'
 2325                      '$(MSBuildProjectFile)') % (rule_name, rule_name)
 2326     rule_inputs = '%%(%s.Identity)' % rule_name
 2327     extension_condition = ("'%(Extension)'=='.obj' or "
 2328                            "'%(Extension)'=='.res' or "
 2329                            "'%(Extension)'=='.rsc' or "
 2330                            "'%(Extension)'=='.lib'")
 2331     remove_section = [
 2332         'ItemGroup',
 2333         {'Condition': "'@(SelectedFiles)' != ''"},
 2334         [rule_name,
 2335          {'Remove': '@(%s)' % rule_name,
 2336           'Condition': "'%(Identity)' != '@(SelectedFiles)'"
 2337          }
 2338         ]
 2339     ]
 2340     inputs_section = [
 2341         'ItemGroup',
 2342         [rule.inputs, {'Include': '%%(%s.AdditionalDependencies)' % rule_name}]
 2343     ]
 2344     logging_section = [
 2345         'ItemGroup',
 2346         [rule.tlog,
 2347          {'Include': '%%(%s.Outputs)' % rule_name,
 2348           'Condition': ("'%%(%s.Outputs)' != '' and "
 2349                         "'%%(%s.ExcludedFromBuild)' != 'true'" %
 2350                         (rule_name, rule_name))
 2351          },
 2352          ['Source', "@(%s, '|')" % rule_name],
 2353          ['Inputs', "@(%s -> '%%(Fullpath)', ';')" % rule.inputs],
 2354         ],
 2355     ]
 2356     message_section = [
 2357         'Message',
 2358         {'Importance': 'High',
 2359          'Text': '%%(%s.ExecutionDescription)' % rule_name
 2360         }
 2361     ]
 2362     write_tlog_section = [
 2363         'WriteLinesToFile',
 2364         {'Condition': "'@(%s)' != '' and '%%(%s.ExcludedFromBuild)' != "
 2365          "'true'" % (rule.tlog, rule.tlog),
 2366          'File': '$(IntDir)$(ProjectName).write.1.tlog',
 2367          'Lines': "^%%(%s.Source);@(%s->'%%(Fullpath)')" % (rule.tlog,
 2368                                                             rule.tlog)
 2369         }
 2370     ]
 2371     read_tlog_section = [
 2372         'WriteLinesToFile',
 2373         {'Condition': "'@(%s)' != '' and '%%(%s.ExcludedFromBuild)' != "
 2374          "'true'" % (rule.tlog, rule.tlog),
 2375          'File': '$(IntDir)$(ProjectName).read.1.tlog',
 2376          'Lines': "^%%(%s.Source);%%(%s.Inputs)" % (rule.tlog, rule.tlog)
 2377         }
 2378     ]
 2379     command_and_input_section = [
 2380         rule_name,
 2381         {'Condition': "'@(%s)' != '' and '%%(%s.ExcludedFromBuild)' != "
 2382          "'true'" % (rule_name, rule_name),
 2383          'EchoOff': 'true',
 2384          'StandardOutputImportance': 'High',
 2385          'StandardErrorImportance': 'High',
 2386          'CommandLineTemplate': '%%(%s.CommandLineTemplate)' % rule_name,
 2387          'AdditionalOptions': '%%(%s.AdditionalOptions)' % rule_name,
 2388          'Inputs': rule_inputs
 2389         }
 2390     ]
 2391     content.extend([
 2392         ['Target',
 2393          {'Name': rule.target_name,
 2394           'BeforeTargets': '$(%s)' % rule.before_targets,
 2395           'AfterTargets': '$(%s)' % rule.after_targets,
 2396           'Condition': "'@(%s)' != ''" % rule_name,
 2397           'DependsOnTargets': '$(%s);%s' % (rule.depends_on,
 2398                                             rule.compute_output),
 2399           'Outputs': target_outputs,
 2400           'Inputs': target_inputs
 2401          },
 2402          remove_section,
 2403          inputs_section,
 2404          logging_section,
 2405          message_section,
 2406          write_tlog_section,
 2407          read_tlog_section,
 2408          command_and_input_section,
 2409         ],
 2410         ['PropertyGroup',
 2411          ['ComputeLinkInputsTargets',
 2412           '$(ComputeLinkInputsTargets);',
 2413           '%s;' % rule.compute_output
 2414          ],
 2415          ['ComputeLibInputsTargets',
 2416           '$(ComputeLibInputsTargets);',
 2417           '%s;' % rule.compute_output
 2418          ],
 2419         ],
 2420         ['Target',
 2421          {'Name': rule.compute_output,
 2422           'Condition': "'@(%s)' != ''" % rule_name
 2423          },
 2424          ['ItemGroup',
 2425           [rule.dirs_to_make,
 2426            {'Condition': "'@(%s)' != '' and "
 2427             "'%%(%s.ExcludedFromBuild)' != 'true'" % (rule_name, rule_name),
 2428             'Include': '%%(%s.Outputs)' % rule_name
 2429            }
 2430           ],
 2431           ['Link',
 2432            {'Include': '%%(%s.Identity)' % rule.dirs_to_make,
 2433             'Condition': extension_condition
 2434            }
 2435           ],
 2436           ['Lib',
 2437            {'Include': '%%(%s.Identity)' % rule.dirs_to_make,
 2438             'Condition': extension_condition
 2439            }
 2440           ],
 2441           ['ImpLib',
 2442            {'Include': '%%(%s.Identity)' % rule.dirs_to_make,
 2443             'Condition': extension_condition
 2444            }
 2445           ],
 2446          ],
 2447          ['MakeDir',
 2448           {'Directories': ("@(%s->'%%(RootDir)%%(Directory)')" %
 2449                            rule.dirs_to_make)
 2450           }
 2451          ]
 2452         ],
 2453     ])
 2454   easy_xml.WriteXmlIfChanged(content, targets_path, pretty=True, win32=True)
 2455 
 2456 
 2457 def _GenerateMSBuildRuleXmlFile(xml_path, msbuild_rules):
 2458   # Generate the .xml file
 2459   content = [
 2460       'ProjectSchemaDefinitions',
 2461       {'xmlns': ('clr-namespace:Microsoft.Build.Framework.XamlTypes;'
 2462                  'assembly=Microsoft.Build.Framework'),
 2463        'xmlns:x': 'http://schemas.microsoft.com/winfx/2006/xaml',
 2464        'xmlns:sys': 'clr-namespace:System;assembly=mscorlib',
 2465        'xmlns:transformCallback':
 2466        'Microsoft.Cpp.Dev10.ConvertPropertyCallback'
 2467       }
 2468   ]
 2469   for rule in msbuild_rules:
 2470     content.extend([
 2471         ['Rule',
 2472          {'Name': rule.rule_name,
 2473           'PageTemplate': 'tool',
 2474           'DisplayName': rule.display_name,
 2475           'Order': '200'
 2476          },
 2477          ['Rule.DataSource',
 2478           ['DataSource',
 2479            {'Persistence': 'ProjectFile',
 2480             'ItemType': rule.rule_name
 2481            }
 2482           ]
 2483          ],
 2484          ['Rule.Categories',
 2485           ['Category',
 2486            {'Name': 'General'},
 2487            ['Category.DisplayName',
 2488             ['sys:String', 'General'],
 2489            ],
 2490           ],
 2491           ['Category',
 2492            {'Name': 'Command Line',
 2493             'Subtype': 'CommandLine'
 2494            },
 2495            ['Category.DisplayName',
 2496             ['sys:String', 'Command Line'],
 2497            ],
 2498           ],
 2499          ],
 2500          ['StringListProperty',
 2501           {'Name': 'Inputs',
 2502            'Category': 'Command Line',
 2503            'IsRequired': 'true',
 2504            'Switch': ' '
 2505           },
 2506           ['StringListProperty.DataSource',
 2507            ['DataSource',
 2508             {'Persistence': 'ProjectFile',
 2509              'ItemType': rule.rule_name,
 2510              'SourceType': 'Item'
 2511             }
 2512            ]
 2513           ],
 2514          ],
 2515          ['StringProperty',
 2516           {'Name': 'CommandLineTemplate',
 2517            'DisplayName': 'Command Line',
 2518            'Visible': 'False',
 2519            'IncludeInCommandLine': 'False'
 2520           }
 2521          ],
 2522          ['DynamicEnumProperty',
 2523           {'Name': rule.before_targets,
 2524            'Category': 'General',
 2525            'EnumProvider': 'Targets',
 2526            'IncludeInCommandLine': 'False'
 2527           },
 2528           ['DynamicEnumProperty.DisplayName',
 2529            ['sys:String', 'Execute Before'],
 2530           ],
 2531           ['DynamicEnumProperty.Description',
 2532            ['sys:String', 'Specifies the targets for the build customization'
 2533             ' to run before.'
 2534            ],
 2535           ],
 2536           ['DynamicEnumProperty.ProviderSettings',
 2537            ['NameValuePair',
 2538             {'Name': 'Exclude',
 2539              'Value': '^%s|^Compute' % rule.before_targets
 2540             }
 2541            ]
 2542           ],
 2543           ['DynamicEnumProperty.DataSource',
 2544            ['DataSource',
 2545             {'Persistence': 'ProjectFile',
 2546              'HasConfigurationCondition': 'true'
 2547             }
 2548            ]
 2549           ],
 2550          ],
 2551          ['DynamicEnumProperty',
 2552           {'Name': rule.after_targets,
 2553            'Category': 'General',
 2554            'EnumProvider': 'Targets',
 2555            'IncludeInCommandLine': 'False'
 2556           },
 2557           ['DynamicEnumProperty.DisplayName',
 2558            ['sys:String', 'Execute After'],
 2559           ],
 2560           ['DynamicEnumProperty.Description',
 2561            ['sys:String', ('Specifies the targets for the build customization'
 2562                            ' to run after.')
 2563            ],
 2564           ],
 2565           ['DynamicEnumProperty.ProviderSettings',
 2566            ['NameValuePair',
 2567             {'Name': 'Exclude',
 2568              'Value': '^%s|^Compute' % rule.after_targets
 2569             }
 2570            ]
 2571           ],
 2572           ['DynamicEnumProperty.DataSource',
 2573            ['DataSource',
 2574             {'Persistence': 'ProjectFile',
 2575              'ItemType': '',
 2576              'HasConfigurationCondition': 'true'
 2577             }
 2578            ]
 2579           ],
 2580          ],
 2581          ['StringListProperty',
 2582           {'Name': 'Outputs',
 2583            'DisplayName': 'Outputs',
 2584            'Visible': 'False',
 2585            'IncludeInCommandLine': 'False'
 2586           }
 2587          ],
 2588          ['StringProperty',
 2589           {'Name': 'ExecutionDescription',
 2590            'DisplayName': 'Execution Description',
 2591            'Visible': 'False',
 2592            'IncludeInCommandLine': 'False'
 2593           }
 2594          ],
 2595          ['StringListProperty',
 2596           {'Name': 'AdditionalDependencies',
 2597            'DisplayName': 'Additional Dependencies',
 2598            'IncludeInCommandLine': 'False',
 2599            'Visible': 'false'
 2600           }
 2601          ],
 2602          ['StringProperty',
 2603           {'Subtype': 'AdditionalOptions',
 2604            'Name': 'AdditionalOptions',
 2605            'Category': 'Command Line'
 2606           },
 2607           ['StringProperty.DisplayName',
 2608            ['sys:String', 'Additional Options'],
 2609           ],
 2610           ['StringProperty.Description',
 2611            ['sys:String', 'Additional Options'],
 2612           ],
 2613          ],
 2614         ],
 2615         ['ItemType',
 2616          {'Name': rule.rule_name,
 2617           'DisplayName': rule.display_name
 2618          }
 2619         ],
 2620         ['FileExtension',
 2621          {'Name': '*' + rule.extension,
 2622           'ContentType': rule.rule_name
 2623          }
 2624         ],
 2625         ['ContentType',
 2626          {'Name': rule.rule_name,
 2627           'DisplayName': '',
 2628           'ItemType': rule.rule_name
 2629          }
 2630         ]
 2631     ])
 2632   easy_xml.WriteXmlIfChanged(content, xml_path, pretty=True, win32=True)
 2633 
 2634 
 2635 def _GetConfigurationAndPlatform(name, settings):
 2636   configuration = name.rsplit('_', 1)[0]
 2637   platform = settings.get('msvs_configuration_platform', 'Win32')
 2638   return (configuration, platform)
 2639 
 2640 
 2641 def _GetConfigurationCondition(name, settings):
 2642   return (r"'$(Configuration)|$(Platform)'=='%s|%s'" %
 2643           _GetConfigurationAndPlatform(name, settings))
 2644 
 2645 
 2646 def _GetMSBuildProjectConfigurations(configurations):
 2647   group = ['ItemGroup', {'Label': 'ProjectConfigurations'}]
 2648   for (name, settings) in sorted(configurations.items()):
 2649     configuration, platform = _GetConfigurationAndPlatform(name, settings)
 2650     designation = '%s|%s' % (configuration, platform)
 2651     group.append(
 2652         ['ProjectConfiguration', {'Include': designation},
 2653          ['Configuration', configuration],
 2654          ['Platform', platform]])
 2655   return [group]
 2656 
 2657 
 2658 def _GetMSBuildGlobalProperties(spec, guid, gyp_file_name):
 2659   namespace = os.path.splitext(gyp_file_name)[0]
 2660   properties = [
 2661       ['PropertyGroup', {'Label': 'Globals'},
 2662         ['ProjectGuid', guid],
 2663         ['Keyword', 'Win32Proj'],
 2664         ['RootNamespace', namespace],
 2665         ['IgnoreWarnCompileDuplicatedFilename', 'true'],
 2666       ]
 2667     ]
 2668 
 2669   if os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or \
 2670      os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64':
 2671     properties[0].append(['PreferredToolArchitecture', 'x64'])
 2672 
 2673   if spec.get('msvs_enable_winrt'):
 2674     properties[0].append(['DefaultLanguage', 'en-US'])
 2675     properties[0].append(['AppContainerApplication', 'true'])
 2676     if spec.get('msvs_application_type_revision'):
 2677       app_type_revision = spec.get('msvs_application_type_revision')
 2678       properties[0].append(['ApplicationTypeRevision', app_type_revision])
 2679     else:
 2680       properties[0].append(['ApplicationTypeRevision', '8.1'])
 2681 
 2682     if spec.get('msvs_target_platform_version'):
 2683       target_platform_version = spec.get('msvs_target_platform_version')
 2684       properties[0].append(['WindowsTargetPlatformVersion',
 2685                             target_platform_version])
 2686       if spec.get('msvs_target_platform_minversion'):
 2687         target_platform_minversion = spec.get('msvs_target_platform_minversion')
 2688         properties[0].append(['WindowsTargetPlatformMinVersion',
 2689                               target_platform_minversion])
 2690       else:
 2691         properties[0].append(['WindowsTargetPlatformMinVersion',
 2692                               target_platform_version])
 2693     if spec.get('msvs_enable_winphone'):
 2694       properties[0].append(['ApplicationType', 'Windows Phone'])
 2695     else:
 2696       properties[0].append(['ApplicationType', 'Windows Store'])
 2697 
 2698   platform_name = None
 2699   msvs_windows_target_platform_version = None
 2700   for configuration in spec['configurations'].values():
 2701     platform_name = platform_name or _ConfigPlatform(configuration)
 2702     msvs_windows_target_platform_version = \
 2703                     msvs_windows_target_platform_version or \
 2704                     _ConfigWindowsTargetPlatformVersion(configuration)
 2705     if platform_name and msvs_windows_target_platform_version:
 2706       break
 2707 
 2708   if platform_name == 'ARM':
 2709     properties[0].append(['WindowsSDKDesktopARMSupport', 'true'])
 2710   if msvs_windows_target_platform_version:
 2711     properties[0].append(['WindowsTargetPlatformVersion', \
 2712                           str(msvs_windows_target_platform_version)])
 2713 
 2714   return properties
 2715 
 2716 def _GetMSBuildConfigurationDetails(spec, build_file):
 2717   properties = {}
 2718   for name, settings in spec['configurations'].items():
 2719     msbuild_attributes = _GetMSBuildAttributes(spec, settings, build_file)
 2720     condition = _GetConfigurationCondition(name, settings)
 2721     character_set = msbuild_attributes.get('CharacterSet')
 2722     _AddConditionalProperty(properties, condition, 'ConfigurationType',
 2723                             msbuild_attributes['ConfigurationType'])
 2724     if character_set:
 2725       if 'msvs_enable_winrt' not in spec :
 2726         _AddConditionalProperty(properties, condition, 'CharacterSet',
 2727                                 character_set)
 2728   return _GetMSBuildPropertyGroup(spec, 'Configuration', properties)
 2729 
 2730 
 2731 def _GetMSBuildLocalProperties(msbuild_toolset):
 2732   # Currently the only local property we support is PlatformToolset
 2733   properties = {}
 2734   if msbuild_toolset:
 2735     properties = [
 2736         ['PropertyGroup', {'Label': 'Locals'},
 2737           ['PlatformToolset', msbuild_toolset],
 2738         ]
 2739       ]
 2740   return properties
 2741 
 2742 
 2743 def _GetMSBuildPropertySheets(configurations):
 2744   user_props = r'$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props'
 2745   additional_props = {}
 2746   props_specified = False
 2747   for name, settings in sorted(configurations.items()):
 2748     configuration = _GetConfigurationCondition(name, settings)
 2749     if 'msbuild_props' in settings:
 2750       additional_props[configuration] = _FixPaths(settings['msbuild_props'])
 2751       props_specified = True
 2752     else:
 2753      additional_props[configuration] = ''
 2754 
 2755   if not props_specified:
 2756     return [
 2757         ['ImportGroup',
 2758          {'Label': 'PropertySheets'},
 2759          ['Import',
 2760           {'Project': user_props,
 2761            'Condition': "exists('%s')" % user_props,
 2762            'Label': 'LocalAppDataPlatform'
 2763           }
 2764          ]
 2765         ]
 2766     ]
 2767   else:
 2768     sheets = []
 2769     for condition, props in additional_props.items():
 2770       import_group = [
 2771         'ImportGroup',
 2772         {'Label': 'PropertySheets',
 2773          'Condition': condition
 2774         },
 2775         ['Import',
 2776          {'Project': user_props,
 2777           'Condition': "exists('%s')" % user_props,
 2778           'Label': 'LocalAppDataPlatform'
 2779          }
 2780         ]
 2781       ]
 2782       for props_file in props:
 2783         import_group.append(['Import', {'Project':props_file}])
 2784       sheets.append(import_group)
 2785     return sheets
 2786 
 2787 def _ConvertMSVSBuildAttributes(spec, config, build_file):
 2788   config_type = _GetMSVSConfigurationType(spec, build_file)
 2789   msvs_attributes = _GetMSVSAttributes(spec, config, config_type)
 2790   msbuild_attributes = {}
 2791   for a in msvs_attributes:
 2792     if a in ['IntermediateDirectory', 'OutputDirectory']:
 2793       directory = MSVSSettings.ConvertVCMacrosToMSBuild(msvs_attributes[a])
 2794       if not directory.endswith('\\'):
 2795         directory += '\\'
 2796       msbuild_attributes[a] = directory
 2797     elif a == 'CharacterSet':
 2798       msbuild_attributes[a] = _ConvertMSVSCharacterSet(msvs_attributes[a])
 2799     elif a == 'ConfigurationType':
 2800       msbuild_attributes[a] = _ConvertMSVSConfigurationType(msvs_attributes[a])
 2801     else:
 2802       print('Warning: Do not know how to convert MSVS attribute ' + a)
 2803   return msbuild_attributes
 2804 
 2805 
 2806 def _ConvertMSVSCharacterSet(char_set):
 2807   if char_set.isdigit():
 2808     char_set = {
 2809         '0': 'MultiByte',
 2810         '1': 'Unicode',
 2811         '2': 'MultiByte',
 2812     }[char_set]
 2813   return char_set
 2814 
 2815 
 2816 def _ConvertMSVSConfigurationType(config_type):
 2817   if config_type.isdigit():
 2818     config_type = {
 2819         '1': 'Application',
 2820         '2': 'DynamicLibrary',
 2821         '4': 'StaticLibrary',
 2822         '10': 'Utility'
 2823     }[config_type]
 2824   return config_type
 2825 
 2826 
 2827 def _GetMSBuildAttributes(spec, config, build_file):
 2828   if 'msbuild_configuration_attributes' not in config:
 2829     msbuild_attributes = _ConvertMSVSBuildAttributes(spec, config, build_file)
 2830 
 2831   else:
 2832     config_type = _GetMSVSConfigurationType(spec, build_file)
 2833     config_type = _ConvertMSVSConfigurationType(config_type)
 2834     msbuild_attributes = config.get('msbuild_configuration_attributes', {})
 2835     msbuild_attributes.setdefault('ConfigurationType', config_type)
 2836     output_dir = msbuild_attributes.get('OutputDirectory',
 2837                                       '$(SolutionDir)$(Configuration)')
 2838     msbuild_attributes['OutputDirectory'] = _FixPath(output_dir) + '\\'
 2839     if 'IntermediateDirectory' not in msbuild_attributes:
 2840       intermediate = _FixPath('$(Configuration)') + '\\'
 2841       msbuild_attributes['IntermediateDirectory'] = intermediate
 2842     if 'CharacterSet' in msbuild_attributes:
 2843       msbuild_attributes['CharacterSet'] = _ConvertMSVSCharacterSet(
 2844           msbuild_attributes['CharacterSet'])
 2845   if 'TargetName' not in msbuild_attributes:
 2846     prefix = spec.get('product_prefix', '')
 2847     product_name = spec.get('product_name', '$(ProjectName)')
 2848     target_name = prefix + product_name
 2849     msbuild_attributes['TargetName'] = target_name
 2850   if 'TargetExt' not in msbuild_attributes and 'product_extension' in spec:
 2851     ext = spec.get('product_extension')
 2852     msbuild_attributes['TargetExt'] = '.' + ext
 2853 
 2854   if spec.get('msvs_external_builder'):
 2855     external_out_dir = spec.get('msvs_external_builder_out_dir', '.')
 2856     msbuild_attributes['OutputDirectory'] = _FixPath(external_out_dir) + '\\'
 2857 
 2858   # Make sure that 'TargetPath' matches 'Lib.OutputFile' or 'Link.OutputFile'
 2859   # (depending on the tool used) to avoid MSB8012 warning.
 2860   msbuild_tool_map = {
 2861       'executable': 'Link',
 2862       'shared_library': 'Link',
 2863       'loadable_module': 'Link',
 2864       'static_library': 'Lib',
 2865   }
 2866   msbuild_tool = msbuild_tool_map.get(spec['type'])
 2867   if msbuild_tool:
 2868     msbuild_settings = config['finalized_msbuild_settings']
 2869     out_file = msbuild_settings[msbuild_tool].get('OutputFile')
 2870     if out_file:
 2871       msbuild_attributes['TargetPath'] = _FixPath(out_file)
 2872     target_ext = msbuild_settings[msbuild_tool].get('TargetExt')
 2873     if target_ext:
 2874       msbuild_attributes['TargetExt'] = target_ext
 2875 
 2876   return msbuild_attributes
 2877 
 2878 
 2879 def _GetMSBuildConfigurationGlobalProperties(spec, configurations, build_file):
 2880   # TODO(jeanluc) We could optimize out the following and do it only if
 2881   # there are actions.
 2882   # TODO(jeanluc) Handle the equivalent of setting 'CYGWIN=nontsec'.
 2883   new_paths = []
 2884   cygwin_dirs = spec.get('msvs_cygwin_dirs', ['.'])[0]
 2885   if cygwin_dirs:
 2886     cyg_path = '$(MSBuildProjectDirectory)\\%s\\bin\\' % _FixPath(cygwin_dirs)
 2887     new_paths.append(cyg_path)
 2888     # TODO(jeanluc) Change the convention to have both a cygwin_dir and a
 2889     # python_dir.
 2890     python_path = cyg_path.replace('cygwin\\bin', 'python_26')
 2891     new_paths.append(python_path)
 2892     if new_paths:
 2893       new_paths = '$(ExecutablePath);' + ';'.join(new_paths)
 2894 
 2895   properties = {}
 2896   for (name, configuration) in sorted(configurations.items()):
 2897     condition = _GetConfigurationCondition(name, configuration)
 2898     attributes = _GetMSBuildAttributes(spec, configuration, build_file)
 2899     msbuild_settings = configuration['finalized_msbuild_settings']
 2900     _AddConditionalProperty(properties, condition, 'IntDir',
 2901                             attributes['IntermediateDirectory'])
 2902     _AddConditionalProperty(properties, condition, 'OutDir',
 2903                             attributes['OutputDirectory'])
 2904     _AddConditionalProperty(properties, condition, 'TargetName',
 2905                             attributes['TargetName'])
 2906     if 'TargetExt' in attributes:
 2907       _AddConditionalProperty(properties, condition, 'TargetExt',
 2908                               attributes['TargetExt'])
 2909 
 2910     if attributes.get('TargetPath'):
 2911       _AddConditionalProperty(properties, condition, 'TargetPath',
 2912                               attributes['TargetPath'])
 2913     if attributes.get('TargetExt'):
 2914       _AddConditionalProperty(properties, condition, 'TargetExt',
 2915                               attributes['TargetExt'])
 2916 
 2917     if new_paths:
 2918       _AddConditionalProperty(properties, condition, 'ExecutablePath',
 2919                               new_paths)
 2920     tool_settings = msbuild_settings.get('', {})
 2921     for name, value in sorted(tool_settings.items()):
 2922       formatted_value = _GetValueFormattedForMSBuild('', name, value)
 2923       _AddConditionalProperty(properties, condition, name, formatted_value)
 2924   return _GetMSBuildPropertyGroup(spec, None, properties)
 2925 
 2926 
 2927 def _AddConditionalProperty(properties, condition, name, value):
 2928   """Adds a property / conditional value pair to a dictionary.
 2929 
 2930   Arguments:
 2931     properties: The dictionary to be modified.  The key is the name of the
 2932         property.  The value is itself a dictionary; its key is the value and
 2933         the value a list of condition for which this value is true.
 2934     condition: The condition under which the named property has the value.
 2935     name: The name of the property.
 2936     value: The value of the property.
 2937   """
 2938   if name not in properties:
 2939     properties[name] = {}
 2940   values = properties[name]
 2941   if value not in values:
 2942     values[value] = []
 2943   conditions = values[value]
 2944   conditions.append(condition)
 2945 
 2946 
 2947 # Regex for msvs variable references ( i.e. $(FOO) ).
 2948 MSVS_VARIABLE_REFERENCE = re.compile(r'\$\(([a-zA-Z_][a-zA-Z0-9_]*)\)')
 2949 
 2950 
 2951 def _GetMSBuildPropertyGroup(spec, label, properties):
 2952   """Returns a PropertyGroup definition for the specified properties.
 2953 
 2954   Arguments:
 2955     spec: The target project dict.
 2956     label: An optional label for the PropertyGroup.
 2957     properties: The dictionary to be converted.  The key is the name of the
 2958         property.  The value is itself a dictionary; its key is the value and
 2959         the value a list of condition for which this value is true.
 2960   """
 2961   group = ['PropertyGroup']
 2962   if label:
 2963     group.append({'Label': label})
 2964   num_configurations = len(spec['configurations'])
 2965   def GetEdges(node):
 2966     # Use a definition of edges such that user_of_variable -> used_varible.
 2967     # This happens to be easier in this case, since a variable's
 2968     # definition contains all variables it references in a single string.
 2969     edges = set()
 2970     for value in sorted(properties[node].keys()):
 2971       # Add to edges all $(...) references to variables.
 2972       #
 2973       # Variable references that refer to names not in properties are excluded
 2974       # These can exist for instance to refer built in definitions like
 2975       # $(SolutionDir).
 2976       #
 2977       # Self references are ignored. Self reference is used in a few places to
 2978       # append to the default value. I.e. PATH=$(PATH);other_path
 2979       edges.update(set([v for v in MSVS_VARIABLE_REFERENCE.findall(value)
 2980                         if v in properties and v != node]))
 2981     return edges
 2982   properties_ordered = gyp.common.TopologicallySorted(
 2983       properties.keys(), GetEdges)
 2984   # Walk properties in the reverse of a topological sort on
 2985   # user_of_variable -> used_variable as this ensures variables are
 2986   # defined before they are used.
 2987   # NOTE: reverse(topsort(DAG)) = topsort(reverse_edges(DAG))
 2988   for name in reversed(properties_ordered):
 2989     values = properties[name]
 2990     for value, conditions in sorted(values.items()):
 2991       if len(conditions) == num_configurations:
 2992         # If the value is the same all configurations,
 2993         # just add one unconditional entry.
 2994         group.append([name, value])
 2995       else:
 2996         for condition in conditions:
 2997           group.append([name, {'Condition': condition}, value])
 2998   return [group]
 2999 
 3000 
 3001 def _GetMSBuildToolSettingsSections(spec, configurations):
 3002   groups = []
 3003   for (name, configuration) in sorted(configurations.items()):
 3004     msbuild_settings = configuration['finalized_msbuild_settings']
 3005     group = ['ItemDefinitionGroup',
 3006              {'Condition': _GetConfigurationCondition(name, configuration)}
 3007             ]
 3008     for tool_name, tool_settings in sorted(msbuild_settings.items()):
 3009       # Skip the tool named '' which is a holder of global settings handled
 3010       # by _GetMSBuildConfigurationGlobalProperties.
 3011       if tool_name:
 3012         if tool_settings:
 3013           tool = [tool_name]
 3014           for name, value in sorted(tool_settings.items()):
 3015             formatted_value = _GetValueFormattedForMSBuild(tool_name, name,
 3016                                                            value)
 3017             tool.append([name, formatted_value])
 3018           group.append(tool)
 3019     groups.append(group)
 3020   return groups
 3021 
 3022 
 3023 def _FinalizeMSBuildSettings(spec, configuration):
 3024   if 'msbuild_settings' in configuration:
 3025     converted = False
 3026     msbuild_settings = configuration['msbuild_settings']
 3027     MSVSSettings.ValidateMSBuildSettings(msbuild_settings)
 3028   else:
 3029     converted = True
 3030     msvs_settings = configuration.get('msvs_settings', {})
 3031     msbuild_settings = MSVSSettings.ConvertToMSBuildSettings(msvs_settings)
 3032   include_dirs, midl_include_dirs, resource_include_dirs = \
 3033       _GetIncludeDirs(configuration)
 3034   libraries = _GetLibraries(spec)
 3035   library_dirs = _GetLibraryDirs(configuration)
 3036   out_file, _, msbuild_tool = _GetOutputFilePathAndTool(spec, msbuild=True)
 3037   target_ext = _GetOutputTargetExt(spec)
 3038   defines = _GetDefines(configuration)
 3039   if converted:
 3040     # Visual Studio 2010 has TR1
 3041     defines = [d for d in defines if d != '_HAS_TR1=0']
 3042     # Warn of ignored settings
 3043     ignored_settings = ['msvs_tool_files']
 3044     for ignored_setting in ignored_settings:
 3045       value = configuration.get(ignored_setting)
 3046       if value:
 3047         print('Warning: The automatic conversion to MSBuild does not handle '
 3048                '%s.  Ignoring setting of %s' % (ignored_setting, str(value)))
 3049 
 3050   defines = [_EscapeCppDefineForMSBuild(d) for d in defines]
 3051   disabled_warnings = _GetDisabledWarnings(configuration)
 3052   prebuild = configuration.get('msvs_prebuild')
 3053   postbuild = configuration.get('msvs_postbuild')
 3054   def_file = _GetModuleDefinition(spec)
 3055   precompiled_header = configuration.get('msvs_precompiled_header')
 3056 
 3057   # Add the information to the appropriate tool
 3058   # TODO(jeanluc) We could optimize and generate these settings only if
 3059   # the corresponding files are found, e.g. don't generate ResourceCompile
 3060   # if you don't have any resources.
 3061   _ToolAppend(msbuild_settings, 'ClCompile',
 3062               'AdditionalIncludeDirectories', include_dirs)
 3063   _ToolAppend(msbuild_settings, 'Midl',
 3064               'AdditionalIncludeDirectories', midl_include_dirs)
 3065   _ToolAppend(msbuild_settings, 'ResourceCompile',
 3066               'AdditionalIncludeDirectories', resource_include_dirs)
 3067   # Add in libraries, note that even for empty libraries, we want this
 3068   # set, to prevent inheriting default libraries from the environment.
 3069   _ToolSetOrAppend(msbuild_settings, 'Link', 'AdditionalDependencies',
 3070                   libraries)
 3071   _ToolAppend(msbuild_settings, 'Link', 'AdditionalLibraryDirectories',
 3072               library_dirs)
 3073   if out_file:
 3074     _ToolAppend(msbuild_settings, msbuild_tool, 'OutputFile', out_file,
 3075                 only_if_unset=True)
 3076   if target_ext:
 3077     _ToolAppend(msbuild_settings, msbuild_tool, 'TargetExt', target_ext,
 3078                 only_if_unset=True)
 3079   # Add defines.
 3080   _ToolAppend(msbuild_settings, 'ClCompile',
 3081               'PreprocessorDefinitions', defines)
 3082   _ToolAppend(msbuild_settings, 'ResourceCompile',
 3083               'PreprocessorDefinitions', defines)
 3084   # Add disabled warnings.
 3085   _ToolAppend(msbuild_settings, 'ClCompile',
 3086               'DisableSpecificWarnings', disabled_warnings)
 3087   # Turn on precompiled headers if appropriate.
 3088   if precompiled_header:
 3089     precompiled_header = os.path.split(precompiled_header)[1]
 3090     _ToolAppend(msbuild_settings, 'ClCompile', 'PrecompiledHeader', 'Use')
 3091     _ToolAppend(msbuild_settings, 'ClCompile',
 3092                 'PrecompiledHeaderFile', precompiled_header)
 3093     _ToolAppend(msbuild_settings, 'ClCompile',
 3094                 'ForcedIncludeFiles', [precompiled_header])
 3095   else:
 3096     _ToolAppend(msbuild_settings, 'ClCompile', 'PrecompiledHeader', 'NotUsing')
 3097   # Turn off WinRT compilation
 3098   _ToolAppend(msbuild_settings, 'ClCompile', 'CompileAsWinRT', 'false')
 3099   # Turn on import libraries if appropriate
 3100   if spec.get('msvs_requires_importlibrary'):
 3101    _ToolAppend(msbuild_settings, '', 'IgnoreImportLibrary', 'false')
 3102   # Loadable modules don't generate import libraries;
 3103   # tell dependent projects to not expect one.
 3104   if spec['type'] == 'loadable_module':
 3105     _ToolAppend(msbuild_settings, '', 'IgnoreImportLibrary', 'true')
 3106   # Set the module definition file if any.
 3107   if def_file:
 3108     _ToolAppend(msbuild_settings, 'Link', 'ModuleDefinitionFile', def_file)
 3109   configuration['finalized_msbuild_settings'] = msbuild_settings
 3110   if prebuild:
 3111     _ToolAppend(msbuild_settings, 'PreBuildEvent', 'Command', prebuild)
 3112   if postbuild:
 3113     _ToolAppend(msbuild_settings, 'PostBuildEvent', 'Command', postbuild)
 3114 
 3115 
 3116 def _GetValueFormattedForMSBuild(tool_name, name, value):
 3117   if type(value) == list:
 3118     # For some settings, VS2010 does not automatically extends the settings
 3119     # TODO(jeanluc) Is this what we want?
 3120     if name in ['AdditionalIncludeDirectories',
 3121                 'AdditionalLibraryDirectories',
 3122                 'AdditionalOptions',
 3123                 'DelayLoadDLLs',
 3124                 'DisableSpecificWarnings',
 3125                 'PreprocessorDefinitions']:
 3126       value.append('%%(%s)' % name)
 3127     # For most tools, entries in a list should be separated with ';' but some
 3128     # settings use a space.  Check for those first.
 3129     exceptions = {
 3130         'ClCompile': ['AdditionalOptions'],
 3131         'Link': ['AdditionalOptions'],
 3132         'Lib': ['AdditionalOptions']}
 3133     if tool_name in exceptions and name in exceptions[tool_name]:
 3134       char = ' '
 3135     else:
 3136       char = ';'
 3137     formatted_value = char.join(
 3138         [MSVSSettings.ConvertVCMacrosToMSBuild(i) for i in value])
 3139   else:
 3140     formatted_value = MSVSSettings.ConvertVCMacrosToMSBuild(value)
 3141   return formatted_value
 3142 
 3143 
 3144 def _VerifySourcesExist(sources, root_dir):
 3145   """Verifies that all source files exist on disk.
 3146 
 3147   Checks that all regular source files, i.e. not created at run time,
 3148   exist on disk.  Missing files cause needless recompilation but no otherwise
 3149   visible errors.
 3150 
 3151   Arguments:
 3152     sources: A recursive list of Filter/file names.
 3153     root_dir: The root directory for the relative path names.
 3154   Returns:
 3155     A list of source files that cannot be found on disk.
 3156   """
 3157   missing_sources = []
 3158   for source in sources:
 3159     if isinstance(source, MSVSProject.Filter):
 3160       missing_sources.extend(_VerifySourcesExist(source.contents, root_dir))
 3161     else:
 3162       if '$' not in source:
 3163         full_path = os.path.join(root_dir, source)
 3164         if not os.path.exists(full_path):
 3165           missing_sources.append(full_path)
 3166   return missing_sources
 3167 
 3168 
 3169 def _GetMSBuildSources(spec, sources, exclusions, rule_dependencies,
 3170                        extension_to_rule_name, actions_spec,
 3171                        sources_handled_by_action, list_excluded):
 3172   groups = ['none', 'masm', 'midl', 'include', 'compile', 'resource', 'rule',
 3173             'rule_dependency']
 3174   grouped_sources = {}
 3175   for g in groups:
 3176     grouped_sources[g] = []
 3177 
 3178   _AddSources2(spec, sources, exclusions, grouped_sources,
 3179                rule_dependencies, extension_to_rule_name,
 3180                sources_handled_by_action, list_excluded)
 3181   sources = []
 3182   for g in groups:
 3183     if grouped_sources[g]:
 3184       sources.append(['ItemGroup'] + grouped_sources[g])
 3185   if actions_spec:
 3186     sources.append(['ItemGroup'] + actions_spec)
 3187   return sources
 3188 
 3189 
 3190 def _AddSources2(spec, sources, exclusions, grouped_sources,
 3191                  rule_dependencies, extension_to_rule_name,
 3192                  sources_handled_by_action,
 3193                  list_excluded):
 3194   extensions_excluded_from_precompile = []
 3195   for source in sources:
 3196     if isinstance(source, MSVSProject.Filter):
 3197       _AddSources2(spec, source.contents, exclusions, grouped_sources,
 3198                    rule_dependencies, extension_to_rule_name,
 3199                    sources_handled_by_action,
 3200                    list_excluded)
 3201     else:
 3202       if not source in sources_handled_by_action:
 3203         detail = []
 3204         excluded_configurations = exclusions.get(source, [])
 3205         if len(excluded_configurations) == len(spec['configurations']):
 3206           detail.append(['ExcludedFromBuild', 'true'])
 3207         else:
 3208           for config_name, configuration in sorted(excluded_configurations):
 3209             condition = _GetConfigurationCondition(config_name, configuration)
 3210             detail.append(['ExcludedFromBuild',
 3211                            {'Condition': condition},
 3212                            'true'])
 3213         # Add precompile if needed
 3214         for config_name, configuration in spec['configurations'].items():
 3215           precompiled_source = configuration.get('msvs_precompiled_source', '')
 3216           if precompiled_source != '':
 3217             precompiled_source = _FixPath(precompiled_source)
 3218             if not extensions_excluded_from_precompile:
 3219               # If the precompiled header is generated by a C source, we must
 3220               # not try to use it for C++ sources, and vice versa.
 3221               basename, extension = os.path.splitext(precompiled_source)
 3222               if extension == '.c':
 3223                 extensions_excluded_from_precompile = ['.cc', '.cpp', '.cxx']
 3224               else:
 3225                 extensions_excluded_from_precompile = ['.c']
 3226 
 3227           if precompiled_source == source:
 3228             condition = _GetConfigurationCondition(config_name, configuration)
 3229             detail.append(['PrecompiledHeader',
 3230                            {'Condition': condition},
 3231                            'Create'
 3232                           ])
 3233           else:
 3234             # Turn off precompiled header usage for source files of a
 3235             # different type than the file that generated the
 3236             # precompiled header.
 3237             for extension in extensions_excluded_from_precompile:
 3238               if source.endswith(extension):
 3239                 detail.append(['PrecompiledHeader', ''])
 3240                 detail.append(['ForcedIncludeFiles', ''])
 3241 
 3242         group, element = _MapFileToMsBuildSourceType(source, rule_dependencies,
 3243                                                      extension_to_rule_name,
 3244                                                      _GetUniquePlatforms(spec))
 3245         grouped_sources[group].append([element, {'Include': source}] + detail)
 3246 
 3247 
 3248 def _GetMSBuildProjectReferences(project):
 3249   references = []
 3250   if project.dependencies:
 3251     group = ['ItemGroup']
 3252     for dependency in project.dependencies:
 3253       guid = dependency.guid
 3254       project_dir = os.path.split(project.path)[0]
 3255       relative_path = gyp.common.RelativePath(dependency.path, project_dir)
 3256       project_ref = ['ProjectReference',
 3257           {'Include': relative_path},
 3258           ['Project', guid],
 3259           ['ReferenceOutputAssembly', 'false']
 3260           ]
 3261       for config in dependency.spec.get('configurations', {}).values():
 3262         if config.get('msvs_use_library_dependency_inputs', 0):
 3263           project_ref.append(['UseLibraryDependencyInputs', 'true'])
 3264           break
 3265         # If it's disabled in any config, turn it off in the reference.
 3266         if config.get('msvs_2010_disable_uldi_when_referenced', 0):
 3267           project_ref.append(['UseLibraryDependencyInputs', 'false'])
 3268           break
 3269       group.append(project_ref)
 3270     references.append(group)
 3271   return references
 3272 
 3273 
 3274 def _GenerateMSBuildProject(project, options, version, generator_flags):
 3275   spec = project.spec
 3276   configurations = spec['configurations']
 3277   project_dir, project_file_name = os.path.split(project.path)
 3278   gyp.common.EnsureDirExists(project.path)
 3279   # Prepare list of sources and excluded sources.
 3280   gyp_path = _NormalizedSource(project.build_file)
 3281   relative_path_of_gyp_file = gyp.common.RelativePath(gyp_path, project_dir)
 3282 
 3283   gyp_file = os.path.split(project.build_file)[1]
 3284   sources, excluded_sources = _PrepareListOfSources(spec, generator_flags,
 3285                                                     gyp_file)
 3286   # Add rules.
 3287   actions_to_add = {}
 3288   props_files_of_rules = set()
 3289   targets_files_of_rules = set()
 3290   rule_dependencies = set()
 3291   extension_to_rule_name = {}
 3292   list_excluded = generator_flags.get('msvs_list_excluded_files', True)
 3293 
 3294   # Don't generate rules if we are using an external builder like ninja.
 3295   if not spec.get('msvs_external_builder'):
 3296     _GenerateRulesForMSBuild(project_dir, options, spec,
 3297                              sources, excluded_sources,
 3298                              props_files_of_rules, targets_files_of_rules,
 3299                              actions_to_add, rule_dependencies,
 3300                              extension_to_rule_name)
 3301   else:
 3302     rules = spec.get('rules', [])
 3303     _AdjustSourcesForRules(rules, sources, excluded_sources, True)
 3304 
 3305   sources, excluded_sources, excluded_idl = (
 3306       _AdjustSourcesAndConvertToFilterHierarchy(spec, options,
 3307                                                 project_dir, sources,
 3308                                                 excluded_sources,
 3309                                                 list_excluded, version))
 3310 
 3311   # Don't add actions if we are using an external builder like ninja.
 3312   if not spec.get('msvs_external_builder'):
 3313     _AddActions(actions_to_add, spec, project.build_file)
 3314     _AddCopies(actions_to_add, spec)
 3315 
 3316     # NOTE: this stanza must appear after all actions have been decided.
 3317     # Don't excluded sources with actions attached, or they won't run.
 3318     excluded_sources = _FilterActionsFromExcluded(
 3319         excluded_sources, actions_to_add)
 3320 
 3321   exclusions = _GetExcludedFilesFromBuild(spec, excluded_sources, excluded_idl)
 3322   actions_spec, sources_handled_by_action = _GenerateActionsForMSBuild(
 3323       spec, actions_to_add)
 3324 
 3325   _GenerateMSBuildFiltersFile(project.path + '.filters', sources,
 3326                               rule_dependencies,
 3327                               extension_to_rule_name, _GetUniquePlatforms(spec))
 3328   missing_sources = _VerifySourcesExist(sources, project_dir)
 3329 
 3330   for configuration in configurations.values():
 3331     _FinalizeMSBuildSettings(spec, configuration)
 3332 
 3333   # Add attributes to root element
 3334 
 3335   import_default_section = [
 3336       ['Import', {'Project': r'$(VCTargetsPath)\Microsoft.Cpp.Default.props'}]]
 3337   import_cpp_props_section = [
 3338       ['Import', {'Project': r'$(VCTargetsPath)\Microsoft.Cpp.props'}]]
 3339   import_cpp_targets_section = [
 3340       ['Import', {'Project': r'$(VCTargetsPath)\Microsoft.Cpp.targets'}]]
 3341   import_masm_props_section = [
 3342       ['Import',
 3343         {'Project': r'$(VCTargetsPath)\BuildCustomizations\masm.props'}]]
 3344   import_masm_targets_section = [
 3345       ['Import',
 3346         {'Project': r'$(VCTargetsPath)\BuildCustomizations\masm.targets'}]]
 3347   import_marmasm_props_section = [
 3348       ['Import',
 3349         {'Project': r'$(VCTargetsPath)\BuildCustomizations\marmasm.props'}]]
 3350   import_marmasm_targets_section = [
 3351       ['Import',
 3352         {'Project': r'$(VCTargetsPath)\BuildCustomizations\marmasm.targets'}]]
 3353   macro_section = [['PropertyGroup', {'Label': 'UserMacros'}]]
 3354 
 3355   content = [
 3356       'Project',
 3357       {'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003',
 3358        'ToolsVersion': version.ProjectVersion(),
 3359        'DefaultTargets': 'Build'
 3360       }]
 3361 
 3362   content += _GetMSBuildProjectConfigurations(configurations)
 3363   content += _GetMSBuildGlobalProperties(spec, project.guid, project_file_name)
 3364   content += import_default_section
 3365   content += _GetMSBuildConfigurationDetails(spec, project.build_file)
 3366   if spec.get('msvs_enable_winphone'):
 3367    content += _GetMSBuildLocalProperties('v120_wp81')
 3368   else:
 3369    content += _GetMSBuildLocalProperties(project.msbuild_toolset)
 3370   content += import_cpp_props_section
 3371   content += import_masm_props_section
 3372   if spec.get('msvs_enable_marmasm'):
 3373     content += import_marmasm_props_section
 3374   content += _GetMSBuildExtensions(props_files_of_rules)
 3375   content += _GetMSBuildPropertySheets(configurations)
 3376   content += macro_section
 3377   content += _GetMSBuildConfigurationGlobalProperties(spec, configurations,
 3378                                                       project.build_file)
 3379   content += _GetMSBuildToolSettingsSections(spec, configurations)
 3380   content += _GetMSBuildSources(
 3381       spec, sources, exclusions, rule_dependencies, extension_to_rule_name,
 3382       actions_spec, sources_handled_by_action, list_excluded)
 3383   content += _GetMSBuildProjectReferences(project)
 3384   content += import_cpp_targets_section
 3385   content += import_masm_targets_section
 3386   if spec.get('msvs_enable_marmasm'):
 3387     content += import_marmasm_targets_section
 3388   content += _GetMSBuildExtensionTargets(targets_files_of_rules)
 3389 
 3390   if spec.get('msvs_external_builder'):
 3391     content += _GetMSBuildExternalBuilderTargets(spec)
 3392 
 3393   # TODO(jeanluc) File a bug to get rid of runas.  We had in MSVS:
 3394   # has_run_as = _WriteMSVSUserFile(project.path, version, spec)
 3395 
 3396   easy_xml.WriteXmlIfChanged(content, project.path, pretty=True, win32=True)
 3397 
 3398   return missing_sources
 3399 
 3400 
 3401 def _GetMSBuildExternalBuilderTargets(spec):
 3402   """Return a list of MSBuild targets for external builders.
 3403 
 3404   The "Build" and "Clean" targets are always generated.  If the spec contains
 3405   'msvs_external_builder_clcompile_cmd', then the "ClCompile" target will also
 3406   be generated, to support building selected C/C++ files.
 3407 
 3408   Arguments:
 3409     spec: The gyp target spec.
 3410   Returns:
 3411     List of MSBuild 'Target' specs.
 3412   """
 3413   build_cmd = _BuildCommandLineForRuleRaw(
 3414       spec, spec['msvs_external_builder_build_cmd'],
 3415       False, False, False, False)
 3416   build_target = ['Target', {'Name': 'Build'}]
 3417   build_target.append(['Exec', {'Command': build_cmd}])
 3418 
 3419   clean_cmd = _BuildCommandLineForRuleRaw(
 3420       spec, spec['msvs_external_builder_clean_cmd'],
 3421       False, False, False, False)
 3422   clean_target = ['Target', {'Name': 'Clean'}]
 3423   clean_target.append(['Exec', {'Command': clean_cmd}])
 3424 
 3425   targets = [build_target, clean_target]
 3426 
 3427   if spec.get('msvs_external_builder_clcompile_cmd'):
 3428     clcompile_cmd = _BuildCommandLineForRuleRaw(
 3429         spec, spec['msvs_external_builder_clcompile_cmd'],
 3430         False, False, False, False)
 3431     clcompile_target = ['Target', {'Name': 'ClCompile'}]
 3432     clcompile_target.append(['Exec', {'Command': clcompile_cmd}])
 3433     targets.append(clcompile_target)
 3434 
 3435   return targets
 3436 
 3437 
 3438 def _GetMSBuildExtensions(props_files_of_rules):
 3439   extensions = ['ImportGroup', {'Label': 'ExtensionSettings'}]
 3440   for props_file in props_files_of_rules:
 3441     extensions.append(['Import', {'Project': props_file}])
 3442   return [extensions]
 3443 
 3444 
 3445 def _GetMSBuildExtensionTargets(targets_files_of_rules):
 3446   targets_node = ['ImportGroup', {'Label': 'ExtensionTargets'}]
 3447   for targets_file in sorted(targets_files_of_rules):
 3448     targets_node.append(['Import', {'Project': targets_file}])
 3449   return [targets_node]
 3450 
 3451 
 3452 def _GenerateActionsForMSBuild(spec, actions_to_add):
 3453   """Add actions accumulated into an actions_to_add, merging as needed.
 3454 
 3455   Arguments:
 3456     spec: the target project dict
 3457     actions_to_add: dictionary keyed on input name, which maps to a list of
 3458         dicts describing the actions attached to that input file.
 3459 
 3460   Returns:
 3461     A pair of (action specification, the sources handled by this action).
 3462   """
 3463   sources_handled_by_action = OrderedSet()
 3464   actions_spec = []
 3465   for primary_input, actions in actions_to_add.items():
 3466     inputs = OrderedSet()
 3467     outputs = OrderedSet()
 3468     descriptions = []
 3469     commands = []
 3470     for action in actions:
 3471       inputs.update(OrderedSet(action['inputs']))
 3472       outputs.update(OrderedSet(action['outputs']))
 3473       descriptions.append(action['description'])
 3474       cmd = action['command']
 3475       # For most actions, add 'call' so that actions that invoke batch files
 3476       # return and continue executing.  msbuild_use_call provides a way to
 3477       # disable this but I have not seen any adverse effect from doing that
 3478       # for everything.
 3479       if action.get('msbuild_use_call', True):
 3480         cmd = 'call ' + cmd
 3481       commands.append(cmd)
 3482     # Add the custom build action for one input file.
 3483     description = ', and also '.join(descriptions)
 3484 
 3485     # We can't join the commands simply with && because the command line will
 3486     # get too long. See also _AddActions: cygwin's setup_env mustn't be called
 3487     # for every invocation or the command that sets the PATH will grow too
 3488     # long.
 3489     command = '\r\n'.join([c + '\r\nif %errorlevel% neq 0 exit /b %errorlevel%'
 3490                            for c in commands])
 3491     _AddMSBuildAction(spec,
 3492                       primary_input,
 3493                       inputs,
 3494                       outputs,
 3495                       command,
 3496                       description,
 3497                       sources_handled_by_action,
 3498                       actions_spec)
 3499   return actions_spec, sources_handled_by_action
 3500 
 3501 
 3502 def _AddMSBuildAction(spec, primary_input, inputs, outputs, cmd, description,
 3503                       sources_handled_by_action, actions_spec):
 3504   command = MSVSSettings.ConvertVCMacrosToMSBuild(cmd)
 3505   primary_input = _FixPath(primary_input)
 3506   inputs_array = _FixPaths(inputs)
 3507   outputs_array = _FixPaths(outputs)
 3508   additional_inputs = ';'.join([i for i in inputs_array
 3509                                 if i != primary_input])
 3510   outputs = ';'.join(outputs_array)
 3511   sources_handled_by_action.add(primary_input)
 3512   action_spec = ['CustomBuild', {'Include': primary_input}]
 3513   action_spec.extend(
 3514       # TODO(jeanluc) 'Document' for all or just if as_sources?
 3515       [['FileType', 'Document'],
 3516        ['Command', command],
 3517        ['Message', description],
 3518        ['Outputs', outputs]
 3519       ])
 3520   if additional_inputs:
 3521     action_spec.append(['AdditionalInputs', additional_inputs])
 3522   actions_spec.append(action_spec)