text: # Copyright 2014 Vinnie Falco (vinnie.falco@gmail.com)
  # Portions Copyright The SCons Foundation
  # Portions Copyright Google, Inc.
  # This file is part of beast
  
  """
  A SCons tool to provide a family of scons builders that
  generate Visual Studio project files
  """
  
  import collections
  import hashlib
  import io
  import itertools
  import ntpath
  import os
  import pprint
  import random
  import re
  
  import SCons.Builder
  import SCons.Node.FS
  import SCons.Node
  import SCons.Script.Main
  import SCons.Util
  
  #-------------------------------------------------------------------------------
  
  # Adapted from msvs.py
  
  UnicodeByteMarker = '\xEF\xBB\xBF'
  
  V12DSPHeader = """\
  <?xml version="1.0" encoding="%(encoding)s"?>\r
  <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
  """
  
  V12DSPProjectConfiguration = """\
      <ProjectConfiguration Include="%(variant)s|%(platform)s">\r
        <Configuration>%(variant)s</Configuration>\r
        <Platform>%(platform)s</Platform>\r
      </ProjectConfiguration>\r
  """
  
  V12DSPGlobals = """\
    <PropertyGroup Label="Globals">\r
      <ProjectGuid>%(project_guid)s</ProjectGuid>\r
      <Keyword>Win32Proj</Keyword>\r
      <RootNamespace>%(name)s</RootNamespace>\r
      <IgnoreWarnCompileDuplicatedFilename>true</IgnoreWarnCompileDuplicatedFilename>\r
    </PropertyGroup>\r
  """
  
  V12DSPPropertyGroup = """\
    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='%(variant)s|%(platform)s'" Label="Configuration">\r
      <CharacterSet>MultiByte</CharacterSet>\r
      <ConfigurationType>Application</ConfigurationType>\r
      <PlatformToolset>v120</PlatformToolset>\r
      <LinkIncremental>False</LinkIncremental>\r
      <UseDebugLibraries>%(use_debug_libs)s</UseDebugLibraries>\r
      <UseOfMfc>False</UseOfMfc>\r
      <WholeProgramOptimization>false</WholeProgramOptimization>\r
      <IntDir>%(int_dir)s</IntDir>\r
      <OutDir>%(out_dir)s</OutDir>\r
    </PropertyGroup>\r
  """
  
  V12DSPImportGroup= """\
    <ImportGroup Condition="'$(Configuration)|$(Platform)'=='%(variant)s|%(platform)s'" Label="PropertySheets">\r
      <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
    </ImportGroup>\r
  """
  
  V12DSPItemDefinitionGroup= """\
    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='%(variant)s|%(platform)s'">\r
  """
  
  V12CustomBuildProtoc= """\
        <FileType>Document</FileType>\r
        <Command Condition="'$(Configuration)|$(Platform)'=='%(name)s'">protoc --cpp_out=%(cpp_out)s --proto_path=%%(RelativeDir) %%(Identity)</Command>\r
        <Outputs Condition="'$(Configuration)|$(Platform)'=='%(name)s'">%(base_out)s.pb.h;%(base_out)s.pb.cc</Outputs>\r
        <Message Condition="'$(Configuration)|$(Platform)'=='%(name)s'">protoc --cpp_out=%(cpp_out)s --proto_path=%%(RelativeDir) %%(Identity)</Message>\r
        <LinkObjects Condition="'$(Configuration)|$(Platform)'=='%(name)s'">false</LinkObjects>\r
  """
  
  V12DSPFiltersHeader = (
  '''<?xml version="1.0" encoding="utf-8"?>\r
  <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
  ''')
  
  #-------------------------------------------------------------------------------
  
  def is_subdir(child, parent):
      '''Determine if child is a subdirectory of parent'''
      return os.path.commonprefix([parent, child]) == parent
  
  def _key(item):
      if isinstance(item, (str, unicode)):
          return ('s', item.upper(), item)
      elif isinstance(item, (int, long, float)):
          return ('n', item)
      elif isinstance(item, (list, tuple)):
          return ('l', map(_key, item))
      elif isinstance(item, dict):
          return ('d', xsorted(item.keys()), xsorted(item.values()))
      elif isinstance(item, Configuration):
          return ('c', _key(item.name), _key(item.target), _key(item.variant), _key(item.platform))
      elif isinstance(item, Item):
          return ('i', _key(winpath(item.path())), _key(item.is_compiled()), _key(item.builder()), _key(item.tag()), _key(item.is_excluded()))
      elif isinstance(item, SCons.Node.FS.File):
          return ('f', _key(item.name), _key(item.suffix))
      else:
          return ('x', item)
  
  def xsorted(tosort, **kwargs):
      '''Performs sorted in a deterministic manner.'''
      if 'key' in kwargs:
          map(kwargs['key'], tosort)
      kwargs['key'] = _key
      return sorted(tosort, **kwargs)
  
  def itemList(items, sep):
      if type(items) == str:  # Won't work in Python 3.
          return items
      def gen():
          for item in xsorted(items):
              if isinstance(item, dict):
                  for k, v in xsorted(item.items()):
                      yield k + '=' + v
              elif isinstance(item, (tuple, list)):
                  assert len(item) == 2, "Item shoud have exactly two elements: " + str(item)
                  yield '%s=%s' % tuple(item)
              else:
                  yield item
              yield sep
      return ''.join(gen())
  
  #-------------------------------------------------------------------------------
  
  class SwitchConverter(object):
      '''Converts command line switches to MSBuild XML, using tables'''
  
      def __init__(self, table, booltable, retable=None):
          self.table = {}
          for key in table:
              self.table[key] = table[key]
          for key in booltable:
              value = booltable[key]
              self.table[key] = [value[0], 'True']
              self.table[key + '-'] = [value[0], 'False']
          if retable != None:
              self.retable = retable
          else:
              self.retable = []
  
      def getXml(self, switches, prefix = ''):
          switches = list(set(switches))      # Filter dupes because on windows platforms, /nologo is added automatically to the environment.
          xml = []
          for regex, tag in self.retable:
              matches = []
              for switch in switches[:]:
                  match = regex.match(switch)
                  if None != match:
                      matches.append(match.group(1))
                      switches.remove(switch)
              if len(matches) > 0:
                  xml.append (
                      '%s<%s>%s</%s>\r\n' % (
                          prefix, tag, ';'.join(matches), tag))
          unknown = []
          for switch in switches:
              try:
                  value = self.table[switch]
                  xml.append (
                      '%s<%s>%s</%s>\r\n' % (
                          prefix, value[0], value[1], value[0]))
              except:
                  unknown.append(switch)
          if unknown:
              s = itemList(unknown, ' ')
              tag = 'AdditionalOptions'
              xml.append('%(prefix)s<%(tag)s>%(s)s%%(%(tag)s)</%(tag)s>\r\n' % locals())
          if xml:
              return ''.join(xml)
          return ''
  
  class ClSwitchConverter(SwitchConverter):
      def __init__(self):
          booltable = {
              '/C'            : ['KeepComments'],
              '/doc'          : ['GenerateXMLDocumentationFiles'],
              '/FAu'          : ['UseUnicodeForAssemblerListing'],
              '/FC'           : ['UseFullPaths'],
              '/FR'           : ['BrowseInformation'],
              '/Fr'           : ['BrowseInformation'],
              '/Fx'           : ['ExpandAttributedSource'],
              '/GF'           : ['StringPooling'],
              '/GL'           : ['WholeProgramOptimization'],
              '/Gm'           : ['MinimalRebuild'],
              '/GR'           : ['RuntimeTypeInfo'],
              '/GS'           : ['BufferSecurityCheck'],
              '/GT'           : ['EnableFiberSafeOptimizations'],
              '/Gy'           : ['FunctionLevelLinking'],
              '/MP'           : ['MultiProcessorCompilation'],
              '/Oi'           : ['IntrinsicFunctions'],
              '/Oy'           : ['OmitFramePointers'],
              '/RTCc'         : ['SmallerTypeCheck'],
              '/u'            : ['UndefineAllPreprocessorDefinitions'],
              '/X'            : ['IgnoreStandardIncludePath'],
              '/WX'           : ['TreatWarningAsError'],
              '/Za'           : ['DisableLanguageExtensions'],
              '/Zl'           : ['OmitDefaultLibName'],
              '/fp:except'    : ['FloatingPointExceptions'],
              '/hotpatch'     : ['CreateHotpatchableImage'],
              '/nologo'       : ['SuppressStartupBanner'],
              '/openmp'       : ['OpenMPSupport'],
              '/showIncludes' : ['ShowIncludes'],
              '/Zc:forScope'  : ['ForceConformanceInForLoopScope'],
              '/Zc:wchar_t'   : ['TreatWChar_tAsBuiltInType'],
          }
          table = {
              '/EHsc' : ['ExceptionHandling', 'Sync'],
              '/EHa'  : ['ExceptionHandling', 'Async'],
              '/EHs'  : ['ExceptionHandling', 'SyncCThrow'],
              '/FA'   : ['AssemblerOutput', 'AssemblyCode'],
              '/FAcs' : ['AssemblerOutput', 'All'],
              '/FAc'  : ['AssemblerOutput', 'AssemblyAndMachineCode'],
              '/FAs'  : ['AssemblerOutput', 'AssemblyAndSourceCode'],
              '/Gd'   : ['CallingConvention', 'Cdecl'],
              '/Gr'   : ['CallingConvention', 'FastCall'],
              '/Gz'   : ['CallingConvention', 'StdCall'],
              '/MT'   : ['RuntimeLibrary', 'MultiThreaded'],
              '/MTd'  : ['RuntimeLibrary', 'MultiThreadedDebug'],
              '/MD'   : ['RuntimeLibrary', 'MultiThreadedDLL'],
              '/MDd'  : ['RuntimeLibrary', 'MultiThreadedDebugDLL'],
              '/Od'   : ['Optimization', 'Disabled'],
              '/O1'   : ['Optimization', 'MinSpace'],
              '/O2'   : ['Optimization', 'MaxSpeed'],
              '/Ox'   : ['Optimization', 'Full'],
              '/Ob1'  : ['InlineFunctionExpansion', 'OnlyExplicitInline'],
              '/Ob2'  : ['InlineFunctionExpansion', 'AnySuitable'],
              '/Ot'   : ['FavorSizeOrSpeed', 'Speed'],
              '/Os'   : ['FavorSizeOrSpeed', 'Size'],
              '/RTCs' : ['BasicRuntimeChecks', 'StackFrameRuntimeCheck'],
              '/RTCu' : ['BasicRuntimeChecks', 'UninitializedLocalUsageCheck'],
              '/RTC1' : ['BasicRuntimeChecks', 'EnableFastChecks'],
              '/TC'   : ['CompileAs', 'CompileAsC'],
              '/TP'   : ['CompileAs', 'CompileAsCpp'],
              '/W0'   : [ 'WarningLevel', 'TurnOffAllWarnings'],
              '/W1'   : [ 'WarningLevel', 'Level1'],
              '/W2'   : [ 'WarningLevel', 'Level2'],
              '/W3'   : [ 'WarningLevel', 'Level3'],
              '/W4'   : [ 'WarningLevel', 'Level4'],
              '/Wall' : [ 'WarningLevel', 'EnableAllWarnings'],
              '/Yc'   : ['PrecompiledHeader', 'Create'],
              '/Yu'   : ['PrecompiledHeader', 'Use'],
              '/Z7'   : ['DebugInformationFormat', 'OldStyle'],
              '/Zi'   : ['DebugInformationFormat', 'ProgramDatabase'],
              '/ZI'   : ['DebugInformationFormat', 'EditAndContinue'],
              '/Zp1'  : ['StructMemberAlignment', '1Byte'],
              '/Zp2'  : ['StructMemberAlignment', '2Bytes'],
              '/Zp4'  : ['StructMemberAlignment', '4Bytes'],
              '/Zp8'  : ['StructMemberAlignment', '8Bytes'],
              '/Zp16'         : ['StructMemberAlignment', '16Bytes'],
              '/arch:IA32'     : ['EnableEnhancedInstructionSet', 'NoExtensions'],
              '/arch:SSE'      : ['EnableEnhancedInstructionSet', 'StreamingSIMDExtensions'],
              '/arch:SSE2'     : ['EnableEnhancedInstructionSet', 'StreamingSIMDExtensions2'],
              '/arch:AVX'      : ['EnableEnhancedInstructionSet', 'AdvancedVectorExtensions'],
              '/clr'           : ['CompileAsManaged', 'True'],
              '/clr:pure'      : ['CompileAsManaged', 'Pure'],
              '/clr:safe'      : ['CompileAsManaged', 'Safe'],
              '/clr:oldSyntax' : ['CompileAsManaged', 'OldSyntax'],
              '/fp:fast'       : ['FloatingPointModel', 'Fast'],
              '/fp:precise'    : ['FloatingPointModel', 'Precise'],
              '/fp:strict'     : ['FloatingPointModel', 'Strict'],
              '/errorReport:none'   : ['ErrorReporting', 'None'],
              '/errorReport:prompt' : ['ErrorReporting', 'Prompt'],
              '/errorReport:queue'  : ['ErrorReporting', 'Queue'],
              '/errorReport:send'   : ['ErrorReporting', 'Send'],
          }
          retable = [
              (re.compile(r'/wd\"(\d+)\"'), 'DisableSpecificWarnings'),
          ]
          # Ideas from Google's Generate Your Project
          '''
          _Same(_compile, 'AdditionalIncludeDirectories', _folder_list)  # /I
  
          _Same(_compile, 'PreprocessorDefinitions', _string_list)  # /D
          _Same(_compile, 'ProgramDataBaseFileName', _file_name)  # /Fd
  
          _Same(_compile, 'AdditionalOptions', _string_list)
          _Same(_compile, 'AdditionalUsingDirectories', _folder_list)  # /AI
          _Same(_compile, 'AssemblerListingLocation', _file_name)  # /Fa
          _Same(_compile, 'BrowseInformationFile', _file_name)
          _Same(_compile, 'ForcedIncludeFiles', _file_list)  # /FI
          _Same(_compile, 'ForcedUsingFiles', _file_list)  # /FU
          _Same(_compile, 'UndefinePreprocessorDefinitions', _string_list)  # /U
          _Same(_compile, 'XMLDocumentationFileName', _file_name)
             ''    : ['EnablePREfast', _boolean)  # /analyze Visible='false'
          _Renamed(_compile, 'ObjectFile', 'ObjectFileName', _file_name)  # /Fo
          _Renamed(_compile, 'PrecompiledHeaderThrough', 'PrecompiledHeaderFile',
                   _file_name)  # Used with /Yc and /Yu
          _Renamed(_compile, 'PrecompiledHeaderFile', 'PrecompiledHeaderOutputFile',
                   _file_name)  # /Fp
          _ConvertedToAdditionalOption(_compile, 'DefaultCharIsUnsigned', '/J')
          _MSBuildOnly(_compile, 'ProcessorNumber', _integer)  # the number of processors
          _MSBuildOnly(_compile, 'TrackerLogDirectory', _folder_name)
          _MSBuildOnly(_compile, 'TreatSpecificWarningsAsErrors', _string_list)  # /we
          _MSBuildOnly(_compile, 'PreprocessOutputPath', _string)  # /Fi
          '''
          SwitchConverter.__init__(self, table, booltable, retable)
  
  class LinkSwitchConverter(SwitchConverter):
      def __init__(self):
          # Based on code in Generate Your Project
          booltable = {
              '/DEBUG'                : ['GenerateDebugInformation'],
              '/DYNAMICBASE'          : ['RandomizedBaseAddress'],
              '/NOLOGO'               : ['SuppressStartupBanner'],
              '/nologo'               : ['SuppressStartupBanner'],
          }
          table = {
              '/ERRORREPORT:NONE'     : ['ErrorReporting', 'NoErrorReport'],
              '/ERRORREPORT:PROMPT'   : ['ErrorReporting', 'PromptImmediately'],
              '/ERRORREPORT:QUEUE'    : ['ErrorReporting', 'QueueForNextLogin'],
              '/ERRORREPORT:SEND'     : ['ErrorReporting', 'SendErrorReport'],
              '/MACHINE:X86'          : ['TargetMachine', 'MachineX86'],
              '/MACHINE:ARM'          : ['TargetMachine', 'MachineARM'],
              '/MACHINE:EBC'          : ['TargetMachine', 'MachineEBC'],
              '/MACHINE:IA64'         : ['TargetMachine', 'MachineIA64'],
              '/MACHINE:MIPS'         : ['TargetMachine', 'MachineMIPS'],
              '/MACHINE:MIPS16'       : ['TargetMachine', 'MachineMIPS16'],
              '/MACHINE:MIPSFPU'      : ['TargetMachine', 'MachineMIPSFPU'],
              '/MACHINE:MIPSFPU16'    : ['TargetMachine', 'MachineMIPSFPU16'],
              '/MACHINE:SH4'          : ['TargetMachine', 'MachineSH4'],
              '/MACHINE:THUMB'        : ['TargetMachine', 'MachineTHUMB'],
              '/MACHINE:X64'          : ['TargetMachine', 'MachineX64'],
              '/NXCOMPAT'             : ['DataExecutionPrevention', 'true'],
              '/NXCOMPAT:NO'          : ['DataExecutionPrevention', 'false'],
              '/SUBSYSTEM:CONSOLE'                    : ['SubSystem', 'Console'],
              '/SUBSYSTEM:WINDOWS'                    : ['SubSystem', 'Windows'],
              '/SUBSYSTEM:NATIVE'                     : ['SubSystem', 'Native'],
              '/SUBSYSTEM:EFI_APPLICATION'            : ['SubSystem', 'EFI Application'],
              '/SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER'    : ['SubSystem', 'EFI Boot Service Driver'],
              '/SUBSYSTEM:EFI_ROM'                    : ['SubSystem', 'EFI ROM'],
              '/SUBSYSTEM:EFI_RUNTIME_DRIVER'         : ['SubSystem', 'EFI Runtime'],
              '/SUBSYSTEM:WINDOWSCE'                  : ['SubSystem', 'WindowsCE'],
              '/SUBSYSTEM:POSIX'                      : ['SubSystem', 'POSIX'],
          }
          '''
          /TLBID:1 /MANIFEST /MANIFESTUAC:level='asInvoker' uiAccess='false'
  
          _Same(_link, 'AllowIsolation', _boolean)  # /ALLOWISOLATION
          _Same(_link, 'CLRUnmanagedCodeCheck', _boolean)  # /CLRUNMANAGEDCODECHECK
          _Same(_link, 'DelaySign', _boolean)  # /DELAYSIGN
          _Same(_link, 'EnableUAC', _boolean)  # /MANIFESTUAC
          _Same(_link, 'GenerateMapFile', _boolean)  # /MAP
          _Same(_link, 'IgnoreAllDefaultLibraries', _boolean)  # /NODEFAULTLIB
          _Same(_link, 'IgnoreEmbeddedIDL', _boolean)  # /IGNOREIDL
          _Same(_link, 'MapExports', _boolean)  # /MAPINFO:EXPORTS
          _Same(_link, 'StripPrivateSymbols', _file_name)  # /PDBSTRIPPED
          _Same(_link, 'PerUserRedirection', _boolean)
          _Same(_link, 'Profile', _boolean)  # /PROFILE
          _Same(_link, 'RegisterOutput', _boolean)
          _Same(_link, 'SetChecksum', _boolean)  # /RELEASE
          _Same(_link, 'SupportUnloadOfDelayLoadedDLL', _boolean)  # /DELAY:UNLOAD
          
          _Same(_link, 'SwapRunFromCD', _boolean)  # /SWAPRUN:CD
          _Same(_link, 'TurnOffAssemblyGeneration', _boolean)  # /NOASSEMBLY
          _Same(_link, 'UACUIAccess', _boolean)  # /uiAccess='true'
          _Same(_link, 'EnableCOMDATFolding', _newly_boolean)  # /OPT:ICF
          _Same(_link, 'FixedBaseAddress', _newly_boolean)  # /FIXED
          _Same(_link, 'LargeAddressAware', _newly_boolean)  # /LARGEADDRESSAWARE
          _Same(_link, 'OptimizeReferences', _newly_boolean)  # /OPT:REF
          _Same(_link, 'TerminalServerAware', _newly_boolean)  # /TSAWARE
  
          _Same(_link, 'AdditionalDependencies', _file_list)
          _Same(_link, 'AdditionalLibraryDirectories', _folder_list)  # /LIBPATH 
          _Same(_link, 'AdditionalManifestDependencies', _file_list)  # /MANIFESTDEPENDENCY:
          _Same(_link, 'AdditionalOptions', _string_list)
          _Same(_link, 'AddModuleNamesToAssembly', _file_list)  # /ASSEMBLYMODULE
          _Same(_link, 'AssemblyLinkResource', _file_list)  # /ASSEMBLYLINKRESOURCE
          _Same(_link, 'BaseAddress', _string)  # /BASE
          _Same(_link, 'DelayLoadDLLs', _file_list)  # /DELAYLOAD
          _Same(_link, 'EmbedManagedResourceFile', _file_list)  # /ASSEMBLYRESOURCE
          _Same(_link, 'EntryPointSymbol', _string)  # /ENTRY
          _Same(_link, 'ForceSymbolReferences', _file_list)  # /INCLUDE
          _Same(_link, 'FunctionOrder', _file_name)  # /ORDER
          _Same(_link, 'HeapCommitSize', _string)
          _Same(_link, 'HeapReserveSize', _string)  # /HEAP
          _Same(_link, 'ImportLibrary', _file_name)  # /IMPLIB
          _Same(_link, 'KeyContainer', _file_name)  # /KEYCONTAINER
          _Same(_link, 'KeyFile', _file_name)  # /KEYFILE
          _Same(_link, 'ManifestFile', _file_name)  # /ManifestFile
          _Same(_link, 'MapFileName', _file_name)
          _Same(_link, 'MergedIDLBaseFileName', _file_name)  # /IDLOUT
          _Same(_link, 'MergeSections', _string)  # /MERGE
          _Same(_link, 'MidlCommandFile', _file_name)  # /MIDL
          _Same(_link, 'ModuleDefinitionFile', _file_name)  # /DEF
          _Same(_link, 'OutputFile', _file_name)  # /OUT
          _Same(_link, 'ProfileGuidedDatabase', _file_name)  # /PGD
          _Same(_link, 'ProgramDatabaseFile', _file_name)  # /PDB
          _Same(_link, 'StackCommitSize', _string)
          _Same(_link, 'StackReserveSize', _string)  # /STACK
          _Same(_link, 'TypeLibraryFile', _file_name)  # /TLBOUT
          _Same(_link, 'TypeLibraryResourceID', _integer)  # /TLBID
          _Same(_link, 'Version', _string)  # /VERSION
  
  
          _Same(_link, 'AssemblyDebug',
                _Enumeration(['',
                              'true',  # /ASSEMBLYDEBUG
                              'false']))  # /ASSEMBLYDEBUG:DISABLE
          _Same(_link, 'CLRImageType',
                _Enumeration(['Default',
                              'ForceIJWImage',  # /CLRIMAGETYPE:IJW
                              'ForcePureILImage',  # /Switch="CLRIMAGETYPE:PURE
                              'ForceSafeILImage']))  # /Switch="CLRIMAGETYPE:SAFE
          _Same(_link, 'CLRThreadAttribute',
                _Enumeration(['DefaultThreadingAttribute',  # /CLRTHREADATTRIBUTE:NONE
                              'MTAThreadingAttribute',  # /CLRTHREADATTRIBUTE:MTA
                              'STAThreadingAttribute']))  # /CLRTHREADATTRIBUTE:STA
          _Same(_link, 'Driver',
                _Enumeration(['NotSet',
                              'Driver',  # /Driver
                              'UpOnly',  # /DRIVER:UPONLY
                              'WDM']))  # /DRIVER:WDM
          _Same(_link, 'LinkTimeCodeGeneration',
                _Enumeration(['Default',
                              'UseLinkTimeCodeGeneration',  # /LTCG
                              'PGInstrument',  # /LTCG:PGInstrument
                              'PGOptimization',  # /LTCG:PGOptimize
                              'PGUpdate']))  # /LTCG:PGUpdate
          _Same(_link, 'ShowProgress',
                _Enumeration(['NotSet',
                              'LinkVerbose',  # /VERBOSE
                              'LinkVerboseLib'],  # /VERBOSE:Lib
                             new=['LinkVerboseICF',  # /VERBOSE:ICF
                                  'LinkVerboseREF',  # /VERBOSE:REF
                                  'LinkVerboseSAFESEH',  # /VERBOSE:SAFESEH
                                  'LinkVerboseCLR']))  # /VERBOSE:CLR
          _Same(_link, 'UACExecutionLevel',
                _Enumeration(['AsInvoker',  # /level='asInvoker'
                              'HighestAvailable',  # /level='highestAvailable'
                              'RequireAdministrator']))  # /level='requireAdministrator'
          _Same(_link, 'MinimumRequiredVersion', _string)
          _Same(_link, 'TreatLinkerWarningAsErrors', _boolean)  # /WX
  
  
          # Options found in MSVS that have been renamed in MSBuild.
          _Renamed(_link, 'IgnoreDefaultLibraryNames', 'IgnoreSpecificDefaultLibraries',
                   _file_list)  # /NODEFAULTLIB
          _Renamed(_link, 'ResourceOnlyDLL', 'NoEntryPoint', _boolean)  # /NOENTRY
          _Renamed(_link, 'SwapRunFromNet', 'SwapRunFromNET', _boolean)  # /SWAPRUN:NET
  
          _Moved(_link, 'GenerateManifest', '', _boolean)
          _Moved(_link, 'IgnoreImportLibrary', '', _boolean)
          _Moved(_link, 'LinkIncremental', '', _newly_boolean)
          _Moved(_link, 'LinkLibraryDependencies', 'ProjectReference', _boolean)
          _Moved(_link, 'UseLibraryDependencyInputs', 'ProjectReference', _boolean)
  
          # MSVS options not found in MSBuild.
          _MSVSOnly(_link, 'OptimizeForWindows98', _newly_boolean)
          _MSVSOnly(_link, 'UseUnicodeResponseFiles', _boolean)
  
          # MSBuild options not found in MSVS.
          _MSBuildOnly(_link, 'BuildingInIDE', _boolean)
          _MSBuildOnly(_link, 'ImageHasSafeExceptionHandlers', _boolean)  # /SAFESEH
          _MSBuildOnly(_link, 'LinkDLL', _boolean)  # /DLL Visible='false'
          _MSBuildOnly(_link, 'LinkStatus', _boolean)  # /LTCG:STATUS
          _MSBuildOnly(_link, 'PreventDllBinding', _boolean)  # /ALLOWBIND
          _MSBuildOnly(_link, 'SupportNobindOfDelayLoadedDLL', _boolean)  # /DELAY:NOBIND
          _MSBuildOnly(_link, 'TrackerLogDirectory', _folder_name)
          _MSBuildOnly(_link, 'MSDOSStubFileName', _file_name)  # /STUB Visible='false'
          _MSBuildOnly(_link, 'SectionAlignment', _integer)  # /ALIGN
          _MSBuildOnly(_link, 'SpecifySectionAttributes', _string)  # /SECTION
          _MSBuildOnly(_link, 'ForceFileOutput',
                       _Enumeration([], new=['Enabled',  # /FORCE
                                             # /FORCE:MULTIPLE
                                             'MultiplyDefinedSymbolOnly',
                                             'UndefinedSymbolOnly']))  # /FORCE:UNRESOLVED
          _MSBuildOnly(_link, 'CreateHotPatchableImage',
                       _Enumeration([], new=['Enabled',  # /FUNCTIONPADMIN
                                             'X86Image',  # /FUNCTIONPADMIN:5
                                             'X64Image',  # /FUNCTIONPADMIN:6
                                             'ItaniumImage']))  # /FUNCTIONPADMIN:16
          _MSBuildOnly(_link, 'CLRSupportLastError',
                       _Enumeration([], new=['Enabled',  # /CLRSupportLastError
                                             'Disabled',  # /CLRSupportLastError:NO
                                             # /CLRSupportLastError:SYSTEMDLL
                                             'SystemDlls']))
  
          '''
          SwitchConverter.__init__(self, table, booltable)
  
  CLSWITCHES = ClSwitchConverter()
  LINKSWITCHES = LinkSwitchConverter()
  
  #-------------------------------------------------------------------------------
  
  # Return a Windows path from a native path
  def winpath(path):
      drive, rest = ntpath.splitdrive(path)
      result = []
      while rest and rest != ntpath.sep:
          rest, part = ntpath.split(rest)
          result.insert(0, part)
      if rest:
          result.insert(0, rest)
      return ntpath.join(drive.upper(), *result)
  
  def makeList(x):
      if not x:
          return []
      if type(x) is not list:
          return [x]
      return x
  
  #-------------------------------------------------------------------------------
  
  class Configuration(object):
      def __init__(self, variant, platform, target, env):
          self.name = '%s|%s' % (variant, platform)
          self.variant = variant
          self.platform = platform
          self.target = target
          self.env = env
  
  #-------------------------------------------------------------------------------
  
  class Item(object):
      '''Represents a file item in the Solution Explorer'''
      def __init__(self, path, builder):
          self._path = path
          self._builder = builder
          self.node = dict()
  
          if builder == 'Object':
              self._tag = 'ClCompile'
              self._excluded = False
          elif builder == 'Protoc':
              self._tag = 'CustomBuild'
              self._excluded = False
          else:
              ext = os.path.splitext(self._path)[1]
              if ext in ['.c', '.cc', '.cpp']:
                  self._tag = 'ClCompile'
                  self._excluded = True
              else:
                  if ext in ['.h', '.hpp', '.hxx', '.inl', '.inc']:
                      self._tag = 'ClInclude'
                  else:
                      self._tag = 'None'
                  self._excluded = False;
  
      def __repr__(self):
          return '<VSProject.Item "%s" %s>' % (
              self.path, self.tag, str(self.node))
  
      def path(self):
          return self._path
  
      def tag(self):
          return self._tag
  
      def builder(self):
          return self._builder
  
      def is_compiled(self):
          return self._builder == 'Object'
  
      def is_excluded(self):
          return self._excluded
  
  #-------------------------------------------------------------------------------
  
  def _guid(seed, name = None):
      m = hashlib.md5()
      m.update(seed)
      if name:
          m.update(name)
      d = m.hexdigest().upper()
      guid = "{%s-%s-%s-%s-%s}" % (d[:8], d[8:12], d[12:16], d[16:20], d[20:32])
      return guid
  
  class _ProjectGenerator(object):
      '''Generates a project file for Visual Studio 2013'''
  
      def __init__(self, project_node, filters_node, env):
          try:
              self.configs = xsorted(env['VSPROJECT_CONFIGS'])
          except KeyError:
              raise ValueError ('Missing VSPROJECT_CONFIGS')
          self.root_dir = os.getcwd()
          self.root_dirs = [os.path.abspath(x) for x in makeList(env['VSPROJECT_ROOT_DIRS'])]
          self.project_dir = os.path.dirname(os.path.abspath(str(project_node)))
          self.project_node = project_node
          self.project_file = None
          self.filters_node = filters_node
          self.filters_file = None
          self.guid = _guid(os.path.basename(str(self.project_node)))
          self.buildItemList(env)
  
      def buildItemList(self, env):
          '''Build the Item set associated with the configurations'''
          items = {}
          def _walk(target, items, prefix=''):
              if os.path.isabs(str(target)):
                  return
              if target.has_builder():
                  builder = target.get_builder().get_name(env)
                  bsources = target.get_binfo().bsources
                  if builder == 'Program':
                      for child in bsources:
                          _walk(child, items, prefix+'  ')
                  else:
                      for child in bsources:
                          item = items.setdefault(str(child), Item(str(child), builder=builder))
                          item.node[config] = target
                          _walk(child, items, prefix+'  ')
                      for child in target.children(scan=1):
                          if not os.path.isabs(str(child)):
                              item = items.setdefault(str(child), Item(str(child), builder=None))
                              _walk(child, items, prefix+'  ')
          for config in self.configs:
              targets = config.target
              for target in targets:
                  _walk(target, items)
          self.items = xsorted(items.values())
  
      def makeListTag(self, items, prefix, tag, attrs, inherit=True):
          '''Builds an XML tag string from a list of items. If items is
          empty, then the returned string is empty.'''
          if not items:
              return ''
          s = '%(prefix)s<%(tag)s%(attrs)s>' % locals()
          s += ';'.join(items)
          if inherit:
              s += ';%%(%(tag)s)' % locals()
          s += '</%(tag)s>\r\n' % locals()
          return s
  
      def relPaths(self, paths):
          items = []
          for path in paths:
              if not os.path.isabs(path):
                  items.append(winpath(os.path.relpath(path, self.project_dir)))
          return items
  
      def extraRelPaths(self, paths, base):
          extras = []
          for path in paths:
              if not path in base:
                  extras.append(path)
          return self.relPaths(extras)
  
      def writeHeader(self):
          global clSwitches
  
          encoding = 'utf-8'
          project_guid = self.guid
          name = os.path.splitext(os.path.basename(str(self.project_node)))[0]
  
          f = self.project_file
          f.write(UnicodeByteMarker)
          f.write(V12DSPHeader % locals())
          f.write(V12DSPGlobals % locals())
          f.write('  <ItemGroup Label="ProjectConfigurations">\r\n')
          for config in self.configs:
              variant = config.variant
              platform = config.platform           
              f.write(V12DSPProjectConfiguration % locals())
          f.write('  </ItemGroup>\r\n')
  
          f.write('  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r\n')
          for config in self.configs:
              variant = config.variant
              platform = config.platform
              use_debug_libs = variant == 'Debug'
              variant_dir = os.path.relpath(os.path.dirname(
                  config.target[0].get_abspath()), self.project_dir)
              out_dir = winpath(variant_dir) + ntpath.sep
              int_dir = winpath(ntpath.join(variant_dir, 'src')) + ntpath.sep
              f.write(V12DSPPropertyGroup % locals())
  
          f.write('  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r\n')
          f.write('  <ImportGroup Label="ExtensionSettings" />\r\n')
          for config in self.configs:
              variant = config.variant
              platform = config.platform
              f.write(V12DSPImportGroup % locals())
  
          f.write('  <PropertyGroup Label="UserMacros" />\r\n')
          for config in self.configs:
              variant = config.variant
              platform = config.platform
              f.write(V12DSPItemDefinitionGroup % locals())
              # Cl options
              f.write('    <ClCompile>\r\n')
              f.write(
                  '      <PreprocessorDefinitions>%s%%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n' % (
                      itemList(config.env['CPPDEFINES'], ';')))
              props = ''
              props += self.makeListTag(self.relPaths(xsorted(config.env['CPPPATH'])),
                  '      ', 'AdditionalIncludeDirectories', '', True)
              f.write(props)
              f.write(CLSWITCHES.getXml(xsorted(config.env['CCFLAGS']), '      '))
              f.write('    </ClCompile>\r\n')
  
              f.write('    <Link>\r\n')
              props = ''
              props += self.makeListTag(xsorted(config.env['LIBS']),
                  '      ', 'AdditionalDependencies', '', True)
              try:
                  props += self.makeListTag(self.relPaths(xsorted(config.env['LIBPATH'])),
                      '      ', 'AdditionalLibraryDirectories', '', True)
              except:
                  pass
              f.write(props)
              f.write(LINKSWITCHES.getXml(xsorted(config.env['LINKFLAGS']), '      '))
              f.write('    </Link>\r\n')
  
              f.write('  </ItemDefinitionGroup>\r\n')
  
      def writeProject(self):
          self.writeHeader()
  
          f = self.project_file
          self.project_file.write('  <ItemGroup>\r\n')
          for item in self.items:
              path = winpath(os.path.relpath(item.path(), self.project_dir))
              tag = item.tag()
              props = ''
              if item.builder() == 'Object':
                  props = ''
                  for config in self.configs:
                      name = config.name
                      variant = config.variant
                      platform = config.platform
                      if not config in item.node:
                          props += \
                              '''      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='%(variant)s|%(platform)s'">True</ExcludedFromBuild>\r\n''' % locals()
                  for config, output in xsorted(item.node.items()):
                      name = config.name
                      env = output.get_build_env()
                      variant = config.variant
                      platform = config.platform
                      props += self.makeListTag(self.extraRelPaths(xsorted(env['CPPPATH']), config.env['CPPPATH']),
                          '      ', 'AdditionalIncludeDirectories',
                          ''' Condition="'$(Configuration)|$(Platform)'=='%(variant)s|%(platform)s'"''' % locals(),
                          True)
              elif item.is_excluded():
                  props = '      <ExcludedFromBuild>True</ExcludedFromBuild>\r\n'
              elif item.builder() == 'Protoc':
                  for config, output in xsorted(item.node.items()):
                      name = config.name
                      out_dir = os.path.relpath(os.path.dirname(str(output)), self.project_dir)
                      cpp_out = winpath(out_dir)
                      out_parts = out_dir.split(os.sep)
                      out_parts.append(os.path.splitext(os.path.basename(item.path()))[0])
                      base_out = ntpath.join(*out_parts)
                      props += V12CustomBuildProtoc % locals()
   
              f.write('    <%(tag)s Include="%(path)s">\r\n' % locals())
              f.write(props)
              f.write('    </%(tag)s>\r\n' % locals())
          f.write('  </ItemGroup>\r\n')
  
          f.write(
              '  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r\n'
              '  <ImportGroup Label="ExtensionTargets">\r\n'
              '  </ImportGroup>\r\n'
              '</Project>\r\n')
  
      def writeFilters(self):
          def getGroup(abspath):
              abspath = os.path.dirname(abspath)
              for d in self.root_dirs:
                  common = os.path.commonprefix([abspath, d])
                  if common == d:
                      return winpath(os.path.relpath(abspath, common))
              return winpath(os.path.split(abspath)[1])
  
          f = self.filters_file
          f.write(UnicodeByteMarker)
          f.write(V12DSPFiltersHeader)
  
          f.write('  <ItemGroup>\r\n')
          groups = set()
          for item in self.items:
              group = getGroup(os.path.abspath(item.path()))
              while group != '':
                  groups.add(group)
                  group = ntpath.split(group)[0]
          for group in xsorted(groups):
              guid = _guid(self.guid, group)
              f.write(
                  '    <Filter Include="%(group)s">\r\n'
                  '      <UniqueIdentifier>%(guid)s</UniqueIdentifier>\r\n'
                  '    </Filter>\r\n' % locals())
          f.write('  </ItemGroup>\r\n')
  
          f.write('  <ItemGroup>\r\n')
          for item in self.items:
              path = os.path.abspath(item.path())
              group = getGroup(path)
              path = winpath(os.path.relpath(path, self.project_dir))
              tag = item.tag()
              f.write (
                  '    <%(tag)s Include="%(path)s">\r\n'
                  '      <Filter>%(group)s</Filter>\r\n'
                  '    </%(tag)s>\r\n' % locals())
          f.write('  </ItemGroup>\r\n')
          f.write('</Project>\r\n')
  
      def build(self):
          try:
              self.project_file = open(str(self.project_node), 'wb')
          except IOError, detail:
              raise SCons.Errors.InternalError('Unable to open "' +
                  str(self.project_node) + '" for writing:' + str(detail))
          try:
              self.filters_file = open(str(self.filters_node), 'wb')
          except IOError, detail:
              raise SCons.Errors.InternalError('Unable to open "' +
                  str(self.filters_node) + '" for writing:' + str(detail))
          self.writeProject()
          self.writeFilters()
          self.project_file.close()
          self.filters_file.close()
  
  #-------------------------------------------------------------------------------
  
  class _SolutionGenerator(object):
      def __init__(self, slnfile, projfile, env):
          pass
  
      def build(self):
          pass
  
  #-------------------------------------------------------------------------------
  
  # Generate the VS2013 project
  def buildProject(target, source, env):
      if env.get('auto_build_solution', 1):
          if len(target) != 3:
              raise ValueError ("Unexpected len(target) != 3")
      if not env.get('auto_build_solution', 1):
          if len(target) != 2:
              raise ValueError ("Unexpected len(target) != 2")
  
      g = _ProjectGenerator (target[0], target[1], env)
      g.build()
  
      if env.get('auto_build_solution', 1):
          g = _SolutionGenerator (target[2], target[0], env)
          g.build()
  
  def projectEmitter(target, source, env):
      if len(target) != 1:
          raise ValueError ("Exactly one target must be specified")
  
      # If source is unspecified this condition will be true
      if not source or source[0] == target[0]:
          source = []
  
      outputs = []
      for node in list(target):
          path = env.GetBuildPath(node)
          outputs.extend([
              path + '.vcxproj',
              path + '.vcxproj.filters'])
          if env.get('auto_build_solution', 1):
              outputs.append(path + '.sln')
      return outputs, source
  
  projectBuilder = SCons.Builder.Builder(
      action = SCons.Action.Action(buildProject, "Building ${TARGET}"),
      emitter = projectEmitter)
  
  def createConfig(self, variant, platform, target, env):
      return Configuration(variant, platform, target, env)
  
  def generate(env):
      '''Add Builders and construction variables for Microsoft Visual
      Studio project files to an Environment.'''
      try:
        env['BUILDERS']['VSProject']
      except KeyError:
        env['BUILDERS']['VSProject'] = projectBuilder
      env.AddMethod(createConfig, 'VSProjectConfig')
  
  def exists(env):
      return True
  