summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SConstruct5
-rw-r--r--site_scons/site_tools/vcredist.py199
-rw-r--r--src/mongo/installer/msi/SConscript37
-rw-r--r--src/mongo/installer/msi/wxs/BinaryFragment.wxs2
4 files changed, 207 insertions, 36 deletions
diff --git a/SConstruct b/SConstruct
index 8fc25fbadc4..67325c76c8e 100644
--- a/SConstruct
+++ b/SConstruct
@@ -624,7 +624,7 @@ def variable_arch_converter(val):
def decide_platform_tools():
if mongo_platform.is_running_os('windows'):
# we only support MS toolchain on windows
- return ['msvc', 'mslink', 'mslib', 'masm']
+ return ['msvc', 'mslink', 'mslib', 'masm', 'vcredist']
elif mongo_platform.is_running_os('linux', 'solaris'):
return ['gcc', 'g++', 'gnulink', 'ar', 'gas']
elif mongo_platform.is_running_os('darwin'):
@@ -846,7 +846,8 @@ env_vars.Add('MSVC_USE_SCRIPT',
help='Sets the script used to setup Visual Studio.')
env_vars.Add('MSVC_VERSION',
- help='Sets the version of Visual Studio to use (e.g. 12.0, 11.0, 10.0)')
+ help='Sets the version of Visual C++ to use (e.g. 14.1 for VS2017, 14.2 for VS2019)',
+ default="14.1")
env_vars.Add('NINJA_SUFFIX',
help="""A suffix to add to the end of generated build.ninja
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
diff --git a/src/mongo/installer/msi/SConscript b/src/mongo/installer/msi/SConscript
index 099d29432c2..b8db8f409fa 100644
--- a/src/mongo/installer/msi/SConscript
+++ b/src/mongo/installer/msi/SConscript
@@ -32,40 +32,10 @@ env['WIXLIGHT'] = r'$WIXPATH\light.exe'
env['WIXUIEXT'] = r'$WIXPATH\WixUIExtension.dll'
env['WIXUTILEXT'] = r'$WIXPATH\WixUtilExtension.dll'
-# How to locate the Merge Modules path is described in https://docs.microsoft.com/en-us/cpp/ide/redistributing-visual-cpp-files?view=vs-2017
-
-# When we run this on evergreen, the environment is broken, fall back to hard coded C:\Program Files (x86)
-programfilesx86 = os.environ.get('ProgramFiles(x86)')
-if programfilesx86 is None:
- programfilesx86 = "C:\\Program Files (x86)"
-
-# Use vswhere (it has a fixed stable path) to query where Visual Studio is installed.
-vsinstall_path = subprocess.check_output([os.path.join(programfilesx86, "Microsoft Visual Studio", "Installer", "vswhere.exe"), "-version", "[15.0,16.0)", "-property", "installationPath", "-nologo"]).decode('utf-8').strip()
-
-# Check the registry key that has the runtime lib version
-try:
- vsruntime_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64")
- vslib_version,vslib_version_type = winreg.QueryValueEx(vsruntime_key, "Version")
-except WindowsError:
- print("Visual Studio Runtime not found in registry, disabling msi installer")
+if not 'VCREDISTMERGEMODULEPATH' in env['MSVS']:
+ print("SCons tool setup did not configure the path to the vcredist merge modules, disabling MSI installer")
Return()
-# Combine and set the full merge module path
-redist_root = os.path.join(vsinstall_path, "VC", "Redist", "MSVC")
-redist_path = os.path.join(redist_root, re.match("v(\d+\.\d+\.\d+)\.\d+", vslib_version).group(1))
-
-# Fallback to directory search if we don't find the expected version
-if not os.path.isdir(redist_path):
- 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
-
-env['MERGEMODULESBASEPATH'] = os.path.join(redist_path, "MergeModules")
-
sourcesList = [ "BinaryFragment.wxs",
"FeatureFragment.wxs",
"LicensingFragment.wxs",
@@ -154,7 +124,8 @@ env.Command(objects,
' -dBinarySource=' + buildDir + r'\mongo'
' -dToolBinarySource=' + toolBuildDir +
' -dEnterpriseToolBinarySource=' + enterpriseToolBuildDir +
- ' -dMergeModulesBasePath="$MERGEMODULESBASEPATH"'
+ ' -dMergeModulesBasePath=' + "\"${MSVS['VCREDISTMERGEMODULEPATH']}\"" +
+ ' -dMergeModuleFileCRT=' + env.GetMergeModuleNameForFeature('CRT') +
' -dEdition=' + msi_edition +
' -d"ProductId=*\"'
' -dUpgradeCode=' + upgrade_code +
diff --git a/src/mongo/installer/msi/wxs/BinaryFragment.wxs b/src/mongo/installer/msi/wxs/BinaryFragment.wxs
index 2a03699b614..06b0c8465dd 100644
--- a/src/mongo/installer/msi/wxs/BinaryFragment.wxs
+++ b/src/mongo/installer/msi/wxs/BinaryFragment.wxs
@@ -164,7 +164,7 @@
<?if $(var.Edition) = Enterprise or $(var.Edition) = SSL ?>
<DirectoryRef Id="TARGETDIR">
- <Merge Id="m_vcredist" SourceFile="$(var.MergeModulesBasePath)\Microsoft_VC141_CRT_x64.msm" DiskId="1" Language="0"/>
+ <Merge Id="m_vcredist" SourceFile="$(var.MergeModulesBasePath)\$(var.MergeModuleFileCRT)" DiskId="1" Language="0"/>
</DirectoryRef>
<?endif ?>