This is the flow of the compiler detection logic: External to MSCommon: The Tool init modules, in their exists() routines, call -> msvc_exists(env) At the moment, those modules are: SCons/Tool/midl.py SCons/Tool/mslib.py SCons/Tool/mslink.py SCons/Tool/msvc.py SCons/Tool/msvs.py env may contain a version request in MSVC_VERSION, but this is not used in the detection that follows from msvc_exists(), only in the later batch that starts with a call to msvc_setup_env(). Internal to MSCommon/vc.py: + MSCommon/vc.py:msvc_exists: | vcs = cached_get_installed_vcs(env) | returns True if vcs > 0 | +-> MSCommon/vc.py:cached_get_installed_vcs: | checks global if we've run previously, if so return it | populate the global from -> get_installed_vcs(env) | +-> MSCommon/vc.py:get_installed_vcs: | loop through "known" versions of msvc, granularity is maj.min | check for product dir -> find_vc_pdir(env, ver) | +-> MSCommon/vc.py:find_vc_pdir: | From the msvc-version to pdir mapping dict, get reg key base and value | If value is none -> find_vc_pdir_vswhere(ver, env) | +-> MSCommon/vc.py:find_vc_pdir_vswhere: | From the vc-version to VS-version mapping table get string | Figure out where vswhere is -> msvc_find_vswhere() | Use subprocess to call vswhere, return first line of match / | else get product directory from registry (<= 14.0) / | if we found one -> _check_cl_exists_in_vc_dir(env, pdir, ver) | +-> MSCommon/vc.py:_check_cl_exists_in_vc_dir: | Figure out host/target pair | if version > 14.0 get specific version by looking in | pdir + Auxiliary/Build/Microsoft/VCToolsVersion/default.txt | look for pdir + Tools/MSVC/{specver}/bin/host/target/cl.exe | if 14.0 or less, "do older stuff" All of this just got us a yes-no answer on whether /some/ msvc version exists, but does populate __INSTALLED_VCS_RUN with all of the top-level versions as noted for get_installed_vcs Externally: Once a module's exists() has been called (or, in the case of clang/clangxx, after the compiler has been detected by other means - those still expect the rest of the msvc chain but not cl.exe) the module's generate() function calls -> msvc_setup_env_once(env) Internally: + MSCommon/vc.py:msvc_setup_env_once: | checks for environment flag MSVC_SETUP_RUN | if not, -> msvc_setup_env(env) and set flag | +-+ MSCommon/vc.py:msvc_setup_env: | set ver from -> get_default_version(env) | +-+ MSCommon/vc.py:get_default_version: | if no version specified in env.MSVC_VERSION: | return first entry from -> cached_get_installed_vcs(env) | else return requested version / | get script from MSVC_USE_SCRIPT if set to a filename | -> script_env(script) | +-+ MSCommon/vc.py:script_env: | return (possibly cached) script variables matching script arg / | else -> msvc_find_valid_batch_script(env, version) | +-+ MSCommon/vc.py:msvc_find_valid_batch_script: | Build a list of plausible target values, and loop through | look for host + target -> find_batch_file(env, ver, host, target) | +-+ MSCommon/vc.py:find_batch_file: | call -> find_vc_pdir (see above) | use the return to construct a version-biased batfile path, check / | if not found, try sdk scripts (unknown if this is still useful) Problems: - For VS >= 2017, VS and VS are not 1:1, there can be many VC for one VS - For vswhere-ready versions, detection does not proceed beyond the product level ("2019") into individual "features" (individual msvc) - As documented for MSVC_VERSION, compilers can only be requested if versions are from the set in _VCVER, so 14.1 but not 14.16 or 14.16.27023 - Information found in the first pass (msvs_exists) isn't really available anywhere except the cached version list, since we just return true/false. - Since msvc_exists chain of calls does not look at version, we can proceed to compiler setup if *any* msvc was found, even if the one requested wasn't found.