"Fossies" - the Fresh Open Source Software Archive

Member "scons-3.0.5/engine/SCons/Tool/MSCommon/vc.py" (26 Mar 2019, 30395 Bytes) of package /linux/misc/scons-3.0.5.tar.gz:


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. For more information about "vc.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.0.4_vs_3.0.5.

    1 #
    2 # Copyright (c) 2001 - 2019 The SCons Foundation
    3 #
    4 # Permission is hereby granted, free of charge, to any person obtaining
    5 # a copy of this software and associated documentation files (the
    6 # "Software"), to deal in the Software without restriction, including
    7 # without limitation the rights to use, copy, modify, merge, publish,
    8 # distribute, sublicense, and/or sell copies of the Software, and to
    9 # permit persons to whom the Software is furnished to do so, subject to
   10 # the following conditions:
   11 #
   12 # The above copyright notice and this permission notice shall be included
   13 # in all copies or substantial portions of the Software.
   14 #
   15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
   16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
   17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
   19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
   21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   22 #
   23 
   24 # TODO:
   25 #   * supported arch for versions: for old versions of batch file without
   26 #     argument, giving bogus argument cannot be detected, so we have to hardcode
   27 #     this here
   28 #   * print warning when msvc version specified but not found
   29 #   * find out why warning do not print
   30 #   * test on 64 bits XP +  VS 2005 (and VS 6 if possible)
   31 #   * SDK
   32 #   * Assembly
   33 __revision__ = "src/engine/SCons/Tool/MSCommon/vc.py a56bbd8c09fb219ab8a9673330ffcd55279219d0 2019-03-26 23:16:31 bdeegan"
   34 
   35 __doc__ = """Module for Visual C/C++ detection and configuration.
   36 """
   37 import SCons.compat
   38 import SCons.Util
   39 
   40 import subprocess
   41 import os
   42 import platform
   43 from string import digits as string_digits
   44 
   45 import SCons.Warnings
   46 from SCons.Tool import find_program_path
   47 
   48 from . import common
   49 
   50 debug = common.debug
   51 
   52 from . import sdk
   53 
   54 get_installed_sdks = sdk.get_installed_sdks
   55 
   56 
   57 class VisualCException(Exception):
   58     pass
   59 
   60 class UnsupportedVersion(VisualCException):
   61     pass
   62 
   63 class UnsupportedArch(VisualCException):
   64     pass
   65 
   66 class MissingConfiguration(VisualCException):
   67     pass
   68 
   69 class NoVersionFound(VisualCException):
   70     pass
   71 
   72 class BatchFileExecutionError(VisualCException):
   73     pass
   74 
   75 # Dict to 'canonalize' the arch
   76 _ARCH_TO_CANONICAL = {
   77     "amd64"     : "amd64",
   78     "emt64"     : "amd64",
   79     "i386"      : "x86",
   80     "i486"      : "x86",
   81     "i586"      : "x86",
   82     "i686"      : "x86",
   83     "ia64"      : "ia64",      # deprecated
   84     "itanium"   : "ia64",      # deprecated
   85     "x86"       : "x86",
   86     "x86_64"    : "amd64",
   87     "arm"       : "arm",
   88     "arm64"     : "arm64",
   89     "aarch64"   : "arm64",
   90 }
   91 
   92 # get path to the cl.exe dir for newer VS versions
   93 # based off a tuple of (host, target) platforms
   94 _HOST_TARGET_TO_CL_DIR_GREATER_THAN_14 = {
   95     ("amd64","amd64")  : "Hostx64\\x64",
   96     ("amd64","x86")    : "Hostx64\\x86",
   97     ("amd64","arm")    : "Hostx64\\arm",
   98     ("amd64","arm64")  : "Hostx64\\arm64",
   99     ("x86","amd64")    : "Hostx86\\x64",
  100     ("x86","x86")      : "Hostx86\\x86",
  101     ("x86","arm")      : "Hostx86\\arm",
  102     ("x86","arm64")    : "Hostx86\\arm64",
  103 }
  104 
  105 # get path to the cl.exe dir for older VS versions
  106 # based off a tuple of (host, target) platforms
  107 _HOST_TARGET_TO_CL_DIR = {
  108     ("amd64","amd64")  : "amd64",
  109     ("amd64","x86")    : "amd64_x86",
  110     ("amd64","arm")    : "amd64_arm",
  111     ("amd64","arm64")  : "amd64_arm64",
  112     ("x86","amd64")    : "x86_amd64",
  113     ("x86","x86")      : "",
  114     ("x86","arm")      : "x86_arm",
  115     ("x86","arm64")    : "x86_arm64",
  116 }
  117 
  118 # Given a (host, target) tuple, return the argument for the bat file.
  119 # Both host and targets should be canonalized.
  120 _HOST_TARGET_ARCH_TO_BAT_ARCH = {
  121     ("x86", "x86"): "x86",
  122     ("x86", "amd64"): "x86_amd64",
  123     ("x86", "x86_amd64"): "x86_amd64",
  124     ("amd64", "x86_amd64"): "x86_amd64", # This is present in (at least) VS2012 express
  125     ("amd64", "amd64"): "amd64",
  126     ("amd64", "x86"): "x86",
  127     ("x86", "ia64"): "x86_ia64",         # gone since 14.0
  128     ("arm", "arm"): "arm",              # since 14.0, maybe gone 14.1?
  129     ("x86", "arm"): "x86_arm",          # since 14.0
  130     ("x86", "arm64"): "x86_arm64",      # since 14.1
  131     ("amd64", "arm"): "amd64_arm",      # since 14.0
  132     ("amd64", "arm64"): "amd64_arm64",  # since 14.1
  133 }
  134 
  135 _CL_EXE_NAME = 'cl.exe'
  136 
  137 def get_msvc_version_numeric(msvc_version):
  138     """Get the raw version numbers from a MSVC_VERSION string, so it
  139     could be cast to float or other numeric values. For example, '14.0Exp'
  140     would get converted to '14.0'.
  141 
  142     Args:
  143         msvc_version: str
  144             string representing the version number, could contain non
  145             digit characters
  146 
  147     Returns:
  148         str: the value converted to a numeric only string
  149 
  150     """
  151     return ''.join([x for  x in msvc_version if x in string_digits + '.'])
  152 
  153 def get_host_target(env):
  154     debug('vc.py:get_host_target()')
  155 
  156     host_platform = env.get('HOST_ARCH')
  157     if not host_platform:
  158         host_platform = platform.machine()
  159         # TODO(2.5):  the native Python platform.machine() function returns
  160         # '' on all Python versions before 2.6, after which it also uses
  161         # PROCESSOR_ARCHITECTURE.
  162         if not host_platform:
  163             host_platform = os.environ.get('PROCESSOR_ARCHITECTURE', '')
  164 
  165     # Retain user requested TARGET_ARCH
  166     req_target_platform = env.get('TARGET_ARCH')
  167     debug('vc.py:get_host_target() req_target_platform:%s'%req_target_platform)
  168 
  169     if  req_target_platform:
  170         # If user requested a specific platform then only try that one.
  171         target_platform = req_target_platform
  172     else:
  173         target_platform = host_platform
  174 
  175     try:
  176         host = _ARCH_TO_CANONICAL[host_platform.lower()]
  177     except KeyError as e:
  178         msg = "Unrecognized host architecture %s"
  179         raise ValueError(msg % repr(host_platform))
  180 
  181     try:
  182         target = _ARCH_TO_CANONICAL[target_platform.lower()]
  183     except KeyError as e:
  184         all_archs = str(list(_ARCH_TO_CANONICAL.keys()))
  185         raise ValueError("Unrecognized target architecture %s\n\tValid architectures: %s" % (target_platform, all_archs))
  186 
  187     return (host, target,req_target_platform)
  188 
  189 # If you update this, update SupportedVSList in Tool/MSCommon/vs.py, and the
  190 # MSVC_VERSION documentation in Tool/msvc.xml.
  191 _VCVER = ["14.1", "14.0", "14.0Exp", "12.0", "12.0Exp", "11.0", "11.0Exp", "10.0", "10.0Exp", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"]
  192 
  193 _VCVER_TO_PRODUCT_DIR = {
  194     '14.1' : [
  195         (SCons.Util.HKEY_LOCAL_MACHINE, r'')], # Visual Studio 2017 doesn't set this registry key anymore
  196     '14.0' : [
  197         (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir')],
  198     '14.0Exp' : [
  199         (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\14.0\Setup\VC\ProductDir')],
  200     '12.0' : [
  201         (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\12.0\Setup\VC\ProductDir'),
  202         ],
  203     '12.0Exp' : [
  204         (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\12.0\Setup\VC\ProductDir'),
  205         ],
  206     '11.0': [
  207         (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\11.0\Setup\VC\ProductDir'),
  208         ],
  209     '11.0Exp' : [
  210         (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\11.0\Setup\VC\ProductDir'),
  211         ],
  212     '10.0': [
  213         (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\10.0\Setup\VC\ProductDir'),
  214         ],
  215     '10.0Exp' : [
  216         (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\10.0\Setup\VC\ProductDir'),
  217         ],
  218     '9.0': [
  219         (SCons.Util.HKEY_CURRENT_USER, r'Microsoft\DevDiv\VCForPython\9.0\installdir',),
  220         (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir',),
  221         ],
  222     '9.0Exp' : [
  223         (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\9.0\Setup\VC\ProductDir'),
  224         ],
  225     '8.0': [
  226         (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\8.0\Setup\VC\ProductDir'),
  227         ],
  228     '8.0Exp': [
  229         (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'),
  230         ],
  231     '7.1': [
  232         (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\7.1\Setup\VC\ProductDir'),
  233         ],
  234     '7.0': [
  235         (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\7.0\Setup\VC\ProductDir'),
  236         ],
  237     '6.0': [
  238         (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++\ProductDir'),
  239         ]
  240 }
  241 
  242 def msvc_version_to_maj_min(msvc_version):
  243     msvc_version_numeric = get_msvc_version_numeric(msvc_version)
  244 
  245     t = msvc_version_numeric.split(".")
  246     if not len(t) == 2:
  247         raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric))
  248     try:
  249         maj = int(t[0])
  250         min = int(t[1])
  251         return maj, min
  252     except ValueError as e:
  253         raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric))
  254 
  255 def is_host_target_supported(host_target, msvc_version):
  256     """Check if the given (host, target) tuple is supported for given version.
  257 
  258     Args:
  259         host_target: tuple
  260             tuple of (canonalized) host-targets, e.g. ("x86", "amd64")
  261             for cross compilation from 32 bit Windows to 64 bits.
  262         msvc_version: str
  263             msvc version (major.minor, e.g. 10.0)
  264 
  265     Returns:
  266         bool:
  267 
  268     Note:
  269         This only checks whether a given version *may* support the given (host,
  270         target), not that the toolchain is actually present on the machine.
  271     """
  272     # We assume that any Visual Studio version supports x86 as a target
  273     if host_target[1] != "x86":
  274         maj, min = msvc_version_to_maj_min(msvc_version)
  275         if maj < 8:
  276             return False
  277 
  278     return True
  279 
  280 
  281 def find_vc_pdir_vswhere(msvc_version):
  282     """
  283     Find the MSVC product directory using vswhere.exe.
  284 
  285     Run it asking for specified version and get MSVS  install location
  286     :param msvc_version:
  287     :return: MSVC install dir or None
  288     """
  289 
  290     # For bug 3333 - support default location of vswhere for both 64 and 32 bit windows
  291     # installs. 
  292     for pf in ['Program Files (x86)', 'Program Files']:
  293         vswhere_path = os.path.join(
  294             'C:\\',
  295             pf,
  296             'Microsoft Visual Studio',
  297             'Installer',
  298             'vswhere.exe'
  299         )
  300         if os.path.exists(vswhere_path):
  301             # If we found vswhere, then use it.
  302             break
  303 
  304     vswhere_cmd = [vswhere_path, '-products', '*', '-version', msvc_version, '-property', 'installationPath']
  305 
  306     if os.path.exists(vswhere_path):
  307         #TODO PY27 cannot use Popen as context manager
  308         # try putting it back to the old way for now
  309         sp = subprocess.Popen(vswhere_cmd,
  310                               stdout=subprocess.PIPE,
  311                               stderr=subprocess.PIPE)
  312         vsdir, err = sp.communicate()
  313         if vsdir:
  314             vsdir = vsdir.decode("mbcs").splitlines()
  315             # vswhere could easily return multiple lines
  316             # we could define a way to pick the one we prefer, but since
  317             # this data is currently only used to make a check for existence,
  318             # returning the first hit should be good enough for now.
  319             vc_pdir = os.path.join(vsdir[0], 'VC')
  320             return vc_pdir
  321     else:
  322         # No vswhere on system, no install info available
  323         return None
  324 
  325 
  326 def find_vc_pdir(msvc_version):
  327     """Find the product directory for the given version.
  328 
  329     Tries to look up the path using a registry key from the table
  330     _VCVER_TO_PRODUCT_DIR; if there is no key, calls find_vc_pdir_wshere
  331     for help instead.
  332 
  333     Args:
  334         msvc_version: str
  335             msvc version (major.minor, e.g. 10.0)
  336 
  337     Returns:
  338         str: Path found in registry, or None
  339 
  340     Raises:
  341         UnsupportedVersion: if the version is not known by this file.
  342         MissingConfiguration: found version but the directory is missing.
  343 
  344         Both exceptions inherit from VisualCException.
  345     """
  346     root = 'Software\\'
  347     try:
  348         hkeys = _VCVER_TO_PRODUCT_DIR[msvc_version]
  349     except KeyError:
  350         debug("Unknown version of MSVC: %s" % msvc_version)
  351         raise UnsupportedVersion("Unknown version %s" % msvc_version)
  352 
  353     for hkroot, key in hkeys:
  354         try:
  355             comps = None
  356             if not key:
  357                 comps = find_vc_pdir_vswhere(msvc_version)
  358                 if not comps:
  359                     debug('find_vc_pdir_vswhere(): no VC found for version {}'.format(repr(msvc_version)))
  360                     raise SCons.Util.WinError
  361                 debug('find_vc_pdir_vswhere(): VC found: {}'.format(repr(msvc_version)))
  362                 return comps
  363             else:
  364                 if common.is_win64():
  365                     try:
  366                         # ordinally at win64, try Wow6432Node first.
  367                         comps = common.read_reg(root + 'Wow6432Node\\' + key, hkroot)
  368                     except SCons.Util.WinError as e:
  369                         # at Microsoft Visual Studio for Python 2.7, value is not in Wow6432Node
  370                         pass
  371                 if not comps:
  372                     # not Win64, or Microsoft Visual Studio for Python 2.7
  373                     comps = common.read_reg(root + key, hkroot)
  374         except SCons.Util.WinError as e:
  375             debug('find_vc_dir(): no VC registry key {}'.format(repr(key)))
  376         else:
  377             debug('find_vc_dir(): found VC in registry: {}'.format(comps))
  378             if os.path.exists(comps):
  379                 return comps
  380             else:
  381                 debug('find_vc_dir(): reg says dir is {}, but it does not exist. (ignoring)'.format(comps))
  382                 raise MissingConfiguration("registry dir {} not found on the filesystem".format(comps))
  383     return None
  384 
  385 def find_batch_file(env,msvc_version,host_arch,target_arch):
  386     """
  387     Find the location of the batch script which should set up the compiler
  388     for any TARGET_ARCH whose compilers were installed by Visual Studio/VCExpress
  389     """
  390     pdir = find_vc_pdir(msvc_version)
  391     if pdir is None:
  392         raise NoVersionFound("No version of Visual Studio found")
  393 
  394     debug('vc.py: find_batch_file() in {}'.format(pdir))
  395 
  396     # filter out e.g. "Exp" from the version name
  397     msvc_ver_numeric = get_msvc_version_numeric(msvc_version)
  398     vernum = float(msvc_ver_numeric)
  399     if 7 <= vernum < 8:
  400         pdir = os.path.join(pdir, os.pardir, "Common7", "Tools")
  401         batfilename = os.path.join(pdir, "vsvars32.bat")
  402     elif vernum < 7:
  403         pdir = os.path.join(pdir, "Bin")
  404         batfilename = os.path.join(pdir, "vcvars32.bat")
  405     elif 8 <= vernum <= 14:
  406         batfilename = os.path.join(pdir, "vcvarsall.bat")
  407     else:  # vernum >= 14.1  VS2017 and above
  408         batfilename = os.path.join(pdir, "Auxiliary", "Build", "vcvarsall.bat")
  409 
  410     if not os.path.exists(batfilename):
  411         debug("Not found: %s" % batfilename)
  412         batfilename = None
  413 
  414     installed_sdks=get_installed_sdks()
  415     for _sdk in installed_sdks:
  416         sdk_bat_file = _sdk.get_sdk_vc_script(host_arch,target_arch)
  417         if not sdk_bat_file:
  418             debug("vc.py:find_batch_file() not found:%s"%_sdk)
  419         else:
  420             sdk_bat_file_path = os.path.join(pdir,sdk_bat_file)
  421             if os.path.exists(sdk_bat_file_path):
  422                 debug('vc.py:find_batch_file() sdk_bat_file_path:%s'%sdk_bat_file_path)
  423                 return (batfilename,sdk_bat_file_path)
  424     return (batfilename,None)
  425 
  426 
  427 __INSTALLED_VCS_RUN = None
  428 
  429 def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version):
  430     """Find the cl.exe on the filesystem in the vc_dir depending on
  431     TARGET_ARCH, HOST_ARCH and the msvc version. TARGET_ARCH and
  432     HOST_ARCH can be extracted from the passed env, unless its None,
  433     which then the native platform is assumed the host and target.
  434 
  435     Args:
  436         env: Environment
  437             a construction environment, usually if this is passed its
  438             because there is a desired TARGET_ARCH to be used when searching
  439             for a cl.exe
  440         vc_dir: str
  441             the path to the VC dir in the MSVC installation
  442         msvc_version: str
  443             msvc version (major.minor, e.g. 10.0)
  444 
  445     Returns:
  446         bool:
  447 
  448     """
  449 
  450     # determine if there is a specific target platform we want to build for and
  451     # use that to find a list of valid VCs, default is host platform == target platform
  452     # and same for if no env is specified to extract target platform from
  453     if env:
  454         (host_platform, target_platform, req_target_platform) = get_host_target(env)
  455     else:
  456         host_platform = platform.machine().lower()
  457         target_platform = host_platform
  458 
  459     host_platform = _ARCH_TO_CANONICAL[host_platform]
  460     target_platform = _ARCH_TO_CANONICAL[target_platform]
  461 
  462     debug('_check_cl_exists_in_vc_dir(): host platform %s, target platform %s' % (host_platform, target_platform))
  463 
  464     ver_num = float(get_msvc_version_numeric(msvc_version))
  465 
  466     # make sure the cl.exe exists meaning the tool is installed
  467     if ver_num > 14:
  468         # 2017 and newer allowed multiple versions of the VC toolset to be installed at the same time.
  469         # Just get the default tool version for now
  470         #TODO: support setting a specific minor VC version
  471         default_toolset_file = os.path.join(vc_dir, r'Auxiliary\Build\Microsoft.VCToolsVersion.default.txt')
  472         try:
  473             with open(default_toolset_file) as f:
  474                 vc_specific_version = f.readlines()[0].strip()
  475         except IOError:
  476             debug('_check_cl_exists_in_vc_dir(): failed to read ' + default_toolset_file)
  477             return False
  478         except IndexError:
  479             debug('_check_cl_exists_in_vc_dir(): failed to find MSVC version in ' + default_toolset_file)
  480             return False
  481 
  482         host_trgt_dir = _HOST_TARGET_TO_CL_DIR_GREATER_THAN_14.get((host_platform, target_platform), None)
  483         if not host_trgt_dir:
  484             debug('_check_cl_exists_in_vc_dir(): unsupported host/target platform combo')
  485             return False
  486 
  487         cl_path = os.path.join(vc_dir, r'Tools\MSVC', vc_specific_version, 'bin', host_trgt_dir, _CL_EXE_NAME)
  488         debug('_check_cl_exists_in_vc_dir(): checking for ' + _CL_EXE_NAME + ' at ' + cl_path)
  489         if os.path.exists(cl_path):
  490             debug('_check_cl_exists_in_vc_dir(): found ' + _CL_EXE_NAME + '!')
  491             return True
  492 
  493     elif ver_num <= 14 and ver_num >= 8:
  494 
  495         host_trgt_dir = _HOST_TARGET_TO_CL_DIR.get((host_platform, target_platform), None)
  496         if not host_trgt_dir:
  497             debug('_check_cl_exists_in_vc_dir(): unsupported host/target platform combo')
  498             return False
  499 
  500         cl_path = os.path.join(vc_dir, 'bin',  host_trgt_dir, _CL_EXE_NAME)
  501         debug('_check_cl_exists_in_vc_dir(): checking for ' + _CL_EXE_NAME + ' at ' + cl_path)
  502 
  503         cl_path_exists = os.path.exists(cl_path)
  504         if not cl_path_exists and host_platform == 'amd64':
  505             # older versions of visual studio only had x86 binaries,
  506             # so if the host platform is amd64, we need to check cross
  507             # compile options (x86 binary compiles some other target on a 64 bit os)
  508             host_trgt_dir = _HOST_TARGET_TO_CL_DIR.get(('x86', target_platform), None)
  509             if not host_trgt_dir:
  510                 return False
  511 
  512             cl_path = os.path.join(vc_dir, 'bin', host_trgt_dir, _CL_EXE_NAME)
  513             debug('_check_cl_exists_in_vc_dir(): checking for ' + _CL_EXE_NAME + ' at ' + cl_path)
  514             cl_path_exists = os.path.exists(cl_path)
  515 
  516         if cl_path_exists:
  517             debug('_check_cl_exists_in_vc_dir(): found ' + _CL_EXE_NAME + '!')
  518             return True
  519 
  520     elif ver_num < 8 and ver_num >= 6:
  521         # not sure about these versions so if a walk the VC dir (could be slow)
  522         for root, _, files in os.walk(vc_dir):
  523             if _CL_EXE_NAME in files:
  524                 debug('get_installed_vcs ' + _CL_EXE_NAME + ' found %s' % os.path.join(root, _CL_EXE_NAME))
  525                 return True
  526         return False
  527     else:
  528         # version not support return false
  529         debug('_check_cl_exists_in_vc_dir(): unsupported MSVC version: ' + str(ver_num))
  530 
  531     return False
  532 
  533 def cached_get_installed_vcs(env=None):
  534     global __INSTALLED_VCS_RUN
  535 
  536     if __INSTALLED_VCS_RUN is None:
  537         ret = get_installed_vcs(env)
  538         __INSTALLED_VCS_RUN = ret
  539 
  540     return __INSTALLED_VCS_RUN
  541 
  542 def get_installed_vcs(env=None):
  543     installed_versions = []
  544 
  545     for ver in _VCVER:
  546         debug('trying to find VC %s' % ver)
  547         try:
  548             VC_DIR = find_vc_pdir(ver)
  549             if VC_DIR:
  550                 debug('found VC %s' % ver)
  551                 if _check_cl_exists_in_vc_dir(env, VC_DIR, ver):
  552                     installed_versions.append(ver)
  553                 else:
  554                     debug('find_vc_pdir no compiler found %s' % ver)
  555             else:
  556                 debug('find_vc_pdir return None for ver %s' % ver)
  557         except VisualCException as e:
  558             debug('did not find VC %s: caught exception %s' % (ver, str(e)))
  559     return installed_versions
  560 
  561 def reset_installed_vcs():
  562     """Make it try again to find VC.  This is just for the tests."""
  563     __INSTALLED_VCS_RUN = None
  564 
  565 # Running these batch files isn't cheap: most of the time spent in
  566 # msvs.generate() is due to vcvars*.bat.  In a build that uses "tools='msvs'"
  567 # in multiple environments, for example:
  568 #    env1 = Environment(tools='msvs')
  569 #    env2 = Environment(tools='msvs')
  570 # we can greatly improve the speed of the second and subsequent Environment
  571 # (or Clone) calls by memoizing the environment variables set by vcvars*.bat.
  572 script_env_stdout_cache = {}
  573 def script_env(script, args=None):
  574     cache_key = (script, args)
  575     stdout = script_env_stdout_cache.get(cache_key, None)
  576     if stdout is None:
  577         stdout = common.get_output(script, args)
  578         script_env_stdout_cache[cache_key] = stdout
  579 
  580     # Stupid batch files do not set return code: we take a look at the
  581     # beginning of the output for an error message instead
  582     olines = stdout.splitlines()
  583     if olines[0].startswith("The specified configuration type is missing"):
  584         raise BatchFileExecutionError("\n".join(olines[:2]))
  585 
  586     return common.parse_output(stdout)
  587 
  588 def get_default_version(env):
  589     debug('get_default_version()')
  590 
  591     msvc_version = env.get('MSVC_VERSION')
  592     msvs_version = env.get('MSVS_VERSION')
  593 
  594     debug('get_default_version(): msvc_version:%s msvs_version:%s'%(msvc_version,msvs_version))
  595 
  596     if msvs_version and not msvc_version:
  597         SCons.Warnings.warn(
  598                 SCons.Warnings.DeprecatedWarning,
  599                 "MSVS_VERSION is deprecated: please use MSVC_VERSION instead ")
  600         return msvs_version
  601     elif msvc_version and msvs_version:
  602         if not msvc_version == msvs_version:
  603             SCons.Warnings.warn(
  604                     SCons.Warnings.VisualVersionMismatch,
  605                     "Requested msvc version (%s) and msvs version (%s) do " \
  606                     "not match: please use MSVC_VERSION only to request a " \
  607                     "visual studio version, MSVS_VERSION is deprecated" \
  608                     % (msvc_version, msvs_version))
  609         return msvs_version
  610     if not msvc_version:
  611         installed_vcs = cached_get_installed_vcs(env)
  612         debug('installed_vcs:%s' % installed_vcs)
  613         if not installed_vcs:
  614             #msg = 'No installed VCs'
  615             #debug('msv %s\n' % repr(msg))
  616             #SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
  617             debug('msvc_setup_env: No installed VCs')
  618             return None
  619         msvc_version = installed_vcs[0]
  620         debug('msvc_setup_env: using default installed MSVC version %s\n' % repr(msvc_version))
  621 
  622     return msvc_version
  623 
  624 def msvc_setup_env_once(env):
  625     try:
  626         has_run  = env["MSVC_SETUP_RUN"]
  627     except KeyError:
  628         has_run = False
  629 
  630     if not has_run:
  631         msvc_setup_env(env)
  632         env["MSVC_SETUP_RUN"] = True
  633 
  634 def msvc_find_valid_batch_script(env,version):
  635     debug('vc.py:msvc_find_valid_batch_script()')
  636     # Find the host platform, target platform, and if present the requested
  637     # target platform
  638     platforms = get_host_target(env)
  639     debug("vc.py: msvs_find_valid_batch_script(): host_platform %s, target_platform %s req_target_platform:%s" % platforms)
  640 
  641     host_platform, target_platform, req_target_platform = platforms
  642     try_target_archs = [target_platform]
  643 
  644     # VS2012 has a "cross compile" environment to build 64 bit
  645     # with x86_amd64 as the argument to the batch setup script
  646     if req_target_platform in ('amd64', 'x86_64'):
  647         try_target_archs.append('x86_amd64')
  648     elif not req_target_platform and target_platform in ['amd64', 'x86_64']:
  649         # There may not be "native" amd64, but maybe "cross" x86_amd64 tools
  650         try_target_archs.append('x86_amd64')
  651         # If the user hasn't specifically requested a TARGET_ARCH, and
  652         # The TARGET_ARCH is amd64 then also try 32 bits if there are no viable
  653         # 64 bit tools installed
  654         try_target_archs.append('x86')
  655 
  656     debug("msvs_find_valid_batch_script(): host_platform: %s try_target_archs:%s"%(host_platform, try_target_archs))
  657 
  658     d = None
  659     for tp in try_target_archs:
  660         # Set to current arch.
  661         env['TARGET_ARCH']=tp
  662 
  663         debug("vc.py:msvc_find_valid_batch_script() trying target_platform:%s"%tp)
  664         host_target = (host_platform, tp)
  665         if not is_host_target_supported(host_target, version):
  666             warn_msg = "host, target = %s not supported for MSVC version %s" % \
  667                 (host_target, version)
  668             SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
  669         arg = _HOST_TARGET_ARCH_TO_BAT_ARCH[host_target]
  670 
  671         # Get just version numbers
  672         maj, min = msvc_version_to_maj_min(version)
  673         # VS2015+
  674         if maj >= 14:
  675             if env.get('MSVC_UWP_APP') == '1':
  676                 # Initialize environment variables with store/universal paths
  677                 arg += ' store'
  678 
  679         # Try to locate a batch file for this host/target platform combo
  680         try:
  681             (vc_script,sdk_script) = find_batch_file(env,version,host_platform,tp)
  682             debug('vc.py:msvc_find_valid_batch_script() vc_script:%s sdk_script:%s'%(vc_script,sdk_script))
  683         except VisualCException as e:
  684             msg = str(e)
  685             debug('Caught exception while looking for batch file (%s)' % msg)
  686             warn_msg = "VC version %s not installed.  " + \
  687                        "C/C++ compilers are most likely not set correctly.\n" + \
  688                        " Installed versions are: %s"
  689             warn_msg = warn_msg % (version, cached_get_installed_vcs(env))
  690             SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
  691             continue
  692 
  693         # Try to use the located batch file for this host/target platform combo
  694         debug('vc.py:msvc_find_valid_batch_script() use_script 2 %s, args:%s\n' % (repr(vc_script), arg))
  695         found = None
  696         if vc_script:
  697             try:
  698                 d = script_env(vc_script, args=arg)
  699                 found = vc_script
  700             except BatchFileExecutionError as e:
  701                 debug('vc.py:msvc_find_valid_batch_script() use_script 3: failed running VC script %s: %s: Error:%s'%(repr(vc_script),arg,e))
  702                 vc_script=None
  703                 continue
  704         if not vc_script and sdk_script:
  705             debug('vc.py:msvc_find_valid_batch_script() use_script 4: trying sdk script: %s'%(sdk_script))
  706             try:
  707                 d = script_env(sdk_script)
  708                 found = sdk_script
  709             except BatchFileExecutionError as e:
  710                 debug('vc.py:msvc_find_valid_batch_script() use_script 5: failed running SDK script %s: Error:%s'%(repr(sdk_script),e))
  711                 continue
  712         elif not vc_script and not sdk_script:
  713             debug('vc.py:msvc_find_valid_batch_script() use_script 6: Neither VC script nor SDK script found')
  714             continue
  715 
  716         debug("vc.py:msvc_find_valid_batch_script() Found a working script/target: %s/%s"%(repr(found),arg))
  717         break # We've found a working target_platform, so stop looking
  718 
  719     # If we cannot find a viable installed compiler, reset the TARGET_ARCH
  720     # To it's initial value
  721     if not d:
  722         env['TARGET_ARCH']=req_target_platform
  723 
  724     return d
  725 
  726 
  727 def msvc_setup_env(env):
  728     debug('msvc_setup_env()')
  729 
  730     version = get_default_version(env)
  731     if version is None:
  732         warn_msg = "No version of Visual Studio compiler found - C/C++ " \
  733                    "compilers most likely not set correctly"
  734         SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
  735         return None
  736     debug('msvc_setup_env: using specified MSVC version %s\n' % repr(version))
  737 
  738     # XXX: we set-up both MSVS version for backward
  739     # compatibility with the msvs tool
  740     env['MSVC_VERSION'] = version
  741     env['MSVS_VERSION'] = version
  742     env['MSVS'] = {}
  743 
  744 
  745     use_script = env.get('MSVC_USE_SCRIPT', True)
  746     if SCons.Util.is_String(use_script):
  747         debug('vc.py:msvc_setup_env() use_script 1 %s\n' % repr(use_script))
  748         d = script_env(use_script)
  749     elif use_script:
  750         d = msvc_find_valid_batch_script(env,version)
  751         debug('vc.py:msvc_setup_env() use_script 2 %s\n' % d)
  752         if not d:
  753             return d
  754     else:
  755         debug('MSVC_USE_SCRIPT set to False')
  756         warn_msg = "MSVC_USE_SCRIPT set to False, assuming environment " \
  757                    "set correctly."
  758         SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
  759         return None
  760 
  761     for k, v in d.items():
  762         debug('vc.py:msvc_setup_env() env:%s -> %s'%(k,v))
  763         env.PrependENVPath(k, v, delete_existing=True)
  764 
  765     # final check to issue a warning if the compiler is not present
  766     msvc_cl = find_program_path(env, 'cl')
  767     if not msvc_cl:
  768         SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning,
  769             "Could not find MSVC compiler 'cl', it may need to be installed separately with Visual Studio")
  770 
  771 def msvc_exists(env=None, version=None):
  772     vcs = cached_get_installed_vcs(env)
  773     if version is None:
  774         return len(vcs) > 0
  775     return version in vcs