diff options
author | Andrew Morrow <andrew.morrow@10gen.com> | 2019-12-08 01:30:07 +0000 |
---|---|---|
committer | evergreen <evergreen@mongodb.com> | 2019-12-08 01:30:07 +0000 |
commit | 7c428b6fba15eb2058d52430b8d2c5f4df45d7ad (patch) | |
tree | 093999b7fcc63cc9b185a51a315c428272240c95 /site_scons | |
parent | 72788deb99186ac2628604505147e68ac53cffc5 (diff) | |
download | mongo-7c428b6fba15eb2058d52430b8d2c5f4df45d7ad.tar.gz |
SERVER-44557 Set the default for MSVC_VERSION to VS2017
Diffstat (limited to 'site_scons')
-rw-r--r-- | site_scons/site_tools/vcredist.py | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/site_scons/site_tools/vcredist.py b/site_scons/site_tools/vcredist.py new file mode 100644 index 00000000000..62e14010329 --- /dev/null +++ b/site_scons/site_tools/vcredist.py @@ -0,0 +1,199 @@ +# Copyright 2019 MongoDB Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import re +import subprocess +import winreg + +import SCons + +def exists(env): + result = 'msvc' in env['TOOLS'] + return result + +# How to locate the Merge Modules path is described in: +# +# - VS2019: https://docs.microsoft.com/en-us/visualstudio/releases/2019/redistribution#visual-c-runtime-files +# - VS2017: https://docs.microsoft.com/en-us/visualstudio/productinfo/2017-redistribution-vs#visual-c-runtime-files +# - VS2015: https://docs.microsoft.com/en-us/visualstudio/productinfo/2015-redistribution-vs#visual-c-runtime +# +# However, please note that for VS2017 an VS2019, the documented paths are incorrect, per this +# discussion: +# +# - https://developercommunity.visualstudio.com/content/problem/828060/what-are-the-correct-location-to-search-for-vc-crt.html +# +# This tool uses the currently undocumented but correct paths. + +# The keys are the values SCons accepts for TARGET_ARCH to name +# different windows targets, the values are the tag that VS uses +# for the associated redistributable for that platform. +# +# TODO: Expand this map as needed. +target_arch_expansion_map = { + 'amd64' : 'x64', + 'arm' : None, + 'arm64' : 'arm64', + 'emt64' : 'x64', + 'i386' : 'x86', + 'x86' : 'x86', + 'x86_64' : 'x64', +} + +def _get_programfiles(): + result = os.getenv('ProgramFiles(x86)') + # When we run this under cygwin, the environment is broken, fall + # back to hard coded C:\Program Files (x86) + if result is None: + result = "C:\\Program Files (x86)" + if not os.path.isdir(result): + return None + return result + +def _get_merge_module_name_for_feature(env, feature): + version_components = env['MSVC_VERSION'].split('.') + return "Microsoft_VC{msvc_major}{msvc_minor}_{feature}_{target_arch}.msm".format( + msvc_major=version_components[0], + msvc_minor=version_components[1], + feature=feature, + target_arch=target_arch_expansion_map[env.subst('$TARGET_ARCH')] + ) + +def generate(env): + if not exists(env): + return + + env.Tool('msvc') + + env.AddMethod(_get_merge_module_name_for_feature, "GetMergeModuleNameForFeature") + + # Obtain the major and minor versions of the curently configured MSVC + # and ensure that we are using a VC 14 based toolchain. + # + # Please see + # https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering + # for details on the various version numbers in play for + # the Microsoft toolchain. + msvc_major, msvc_minor = env['MSVC_VERSION'].split('.') + if msvc_major != "14": + return + + # We may or may not need to figure out the path to Program files, + # depending on the various paths we take throught this logic. + programfilesx86 = None + + # TODO: Getting this path is a start, but we should later provide + # an abstraction over the names of the merge modules + # themselves. They seem to have the form + # Microsoft_VC{msvc_major}{msvc_minor}_{Feature}_{target_arch}.msm. It + # would be useful to provide an env.MergeModuleNameFor('feature') + # that consulted the values we have found here and used + # TARGET_ARCH (normalized somehow) to select the right one. + mergemodulepath = None + + # On VS2015 the merge modules are in the program files directory, + # not under the VS install dir. + if msvc_minor == '0': + + if not programfilesx86: + programfilesx86 = _get_programfiles() + if not programfilesx86: + return + + mergemodulepath = os.path.join(programfilesx86, "Common Files", "Merge Modules") + if os.path.isdir(mergemodulepath): + env['MSVS']['VCREDISTMERGEMODULEPATH'] = mergemodulepath + + if not 'VSINSTALLDIR' in env['MSVS']: + + # Compute a VS version based on the VC version. VC 14.0 is VS 2015, VC + # 14.1 is VS 2017. Also compute the next theoretical version by + # incrementing the major version by 1. Then form a range from this + # that we can use as an argument to the -version flag to vswhere. + vs_version = int(msvc_major) + int(msvc_minor) + vs_version_next = vs_version + 1 + vs_version_range = '[{vs_version}.0, {vs_version_next}.0)'.format(vs_version=vs_version, vs_version_next=vs_version_next) + + if not programfilesx86: + programfilesx86 = _get_programfiles() + if not programfilesx86: + return + + # Use vswhere (it has a fixed stable path) to query where Visual Studio is installed. + env['MSVS']['VSINSTALLDIR'] = subprocess.check_output([os.path.join(programfilesx86, "Microsoft Visual Studio", "Installer", "vswhere.exe"), "-version", vs_version_range, "-property", "installationPath", "-nologo"]).decode('utf-8').strip() + + vsinstall_dir = env['MSVS']['VSINSTALLDIR'] + + # Combine and set the full merge module path + redist_root = os.path.join(vsinstall_dir, "VC", "Redist", "MSVC") + if not os.path.isdir(redist_root): + return + env['MSVS']['VCREDISTROOT'] = redist_root + + # Check the registry key that has the runtime lib version + try: + # TOOO: This x64 needs to be abstracted away. Is it the host + # arch, or the target arch? My guess is host. + vsruntime_key_name = "SOFTWARE\\Microsoft\\VisualStudio\\{msvc_major}.0\\VC\\Runtimes\\x64".format(msvc_major=msvc_major) + vsruntime_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, vsruntime_key_name) + vslib_version, vslib_version_type = winreg.QueryValueEx(vsruntime_key, "Version") + except WindowsError: + return + + # Fallback to directory search if we don't find the expected version + redist_path = os.path.join(redist_root, re.match("v(\d+\.\d+\.\d+)\.\d+", vslib_version).group(1)) + if not os.path.isdir(redist_path): + redist_path = None + dirs = os.listdir(redist_root) + dirs.sort() + for dir in reversed(dirs): + candidate = os.path.join(redist_root, dir) + if os.path.isdir(candidate): + redist_path = candidate + break + else: + return + env['MSVS']['VCREDISTPATH'] = redist_path + + if mergemodulepath is None and msvc_minor != "0": + mergemodulepath = os.path.join(redist_path, "MergeModules") + if os.path.isdir(mergemodulepath): + env['MSVS']['VCREDISTMERGEMODULEPATH'] = mergemodulepath + + # Keep these in preference order. The way with the {} in between + # the dots appears to be the more modern form, but we select the + # older one when available to minimize disruption to existing + # automation that expects the redist executable embedded in our + # packages to have this shape. Some architectures, like arm64, + # don't appear to be provided under that syntax though, so we + # include the newer form for that purpose. If Microsoft ever stops + # providing the old form, we will automatically roll forward to + # the new form. + vcredist_search_template_sequence = [ + "vcredist_{}.exe", + "vc_redist.{}.exe", + ] + + expansion = target_arch_expansion_map.get(env.subst('$TARGET_ARCH'), None) + if not expansion: + return + + vcredist_candidates = [c.format(expansion) for c in vcredist_search_template_sequence] + for candidate in vcredist_candidates: + candidate = os.path.join(redist_path, candidate) + if os.path.isfile(candidate): + break + else: + return + env['MSVS']['VCREDISTEXE'] = candidate |