gecko-dev/build/moz.configure/windows.configure
Nathan Froyd e6da99d7d3 Bug 1546491 - raise minimum windows SDK version; r=RyanVM
We've landed some recent changes that implicitly require SDK
10.0.17134.0.  Since we've been building with that version for some time
in automation, let's go ahead and raise the minimum version accordingly.

Differential Revision: https://phabricator.services.mozilla.com/D28671

--HG--
extra : moz-landing-system : lando
2019-04-24 16:15:10 +00:00

489 lines
17 KiB
Python

# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
option('--with-windows-version', nargs=1, default='603',
help='Windows SDK version to target. Win 8.1 (603) is currently'
'the minimum supported version.')
@depends('--with-windows-version')
@imports(_from='__builtin__', _import='ValueError')
def valid_windows_version(value):
if not value:
die('Cannot build with --without-windows-version')
try:
version = int(value[0], 16)
if version in (0x603,):
return version
except ValueError:
pass
die('Invalid value for --with-windows-version (%s)', value[0])
option(env='WINDOWSSDKDIR', nargs=1,
help='Directory containing the Windows SDK')
@depends('WINDOWSSDKDIR', host)
def windows_sdk_dir(value, host):
if value:
return value
if host.kernel != 'WINNT':
return ()
return set(x[1] for x in get_registry_values(
r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots'
r'\KitsRoot*', get_32_and_64_bit=True))
# The Windows SDK 8.1 and 10 have different layouts. The former has
# $SDK/include/$subdir, while the latter has $SDK/include/$version/$subdir.
# The vcvars* scripts don't actually care about the version, they just take
# the last alphanumerically.
# The $SDK/lib directories always have version subdirectories, but while the
# versions match the one in $SDK/include for SDK 10, it's "winv6.3" for SDK
# 8.1.
@imports('os')
@imports('re')
@imports(_from='__builtin__', _import='sorted')
@imports(_from='__builtin__', _import='WindowsError')
def get_sdk_dirs(sdk, subdir):
def get_dirs_containing(sdk, stem, subdir):
base = os.path.join(sdk, stem)
try:
subdirs = [d for d in os.listdir(base)
if os.path.isdir(os.path.join(base, d))]
except WindowsError:
subdirs = []
if not subdirs:
return ()
if subdir in subdirs:
return (base,)
# At this point, either we have an incomplete or invalid SDK directory,
# or we exclusively have version numbers in subdirs.
return tuple(os.path.join(base, s) for s in subdirs
if os.path.isdir(os.path.join(base, s, subdir)))
def categorize(dirs):
return {os.path.basename(d): d for d in dirs}
include_dirs = categorize(get_dirs_containing(sdk, 'include', subdir))
lib_dirs = categorize(get_dirs_containing(sdk, 'lib', subdir))
if 'include' in include_dirs:
include_dirs['winv6.3'] = include_dirs['include']
del include_dirs['include']
valid_versions = sorted(set(include_dirs) & set(lib_dirs), reverse=True)
if valid_versions:
return namespace(
path=sdk,
lib=lib_dirs[valid_versions[0]],
include=include_dirs[valid_versions[0]],
)
@imports(_from='mozbuild.shellutil', _import='quote')
def valid_windows_sdk_dir_result(value):
if value:
return '0x%04x in %s' % (value.version, quote(value.path))
@depends(c_compiler, windows_sdk_dir, valid_windows_version, 'WINDOWSSDKDIR')
@checking('for Windows SDK', valid_windows_sdk_dir_result)
@imports(_from='__builtin__', _import='sorted')
@imports(_from='__builtin__', _import='Exception')
@imports(_from='textwrap', _import='dedent')
def valid_windows_sdk_dir(compiler, windows_sdk_dir, target_version,
windows_sdk_dir_env):
if windows_sdk_dir_env:
windows_sdk_dir_env = windows_sdk_dir_env[0]
sdks = {}
for d in windows_sdk_dir:
sdk = get_sdk_dirs(d, 'um')
if sdk:
check = dedent('''\
#include <winsdkver.h>
WINVER_MAXVER
''')
um_dir = os.path.join(sdk.include, 'um')
shared_dir = os.path.join(sdk.include, 'shared')
result = try_preprocess(compiler.wrapper + [compiler.compiler] +
compiler.flags +
['-I', um_dir, '-I', shared_dir], 'C',
check)
if result:
maxver = result.splitlines()[-1]
try:
maxver = int(maxver, 0)
except Exception:
pass
else:
sdks[d] = maxver, sdk
continue
if d == windows_sdk_dir_env:
raise FatalCheckError(
'Error while checking the version of the SDK in '
'WINDOWSSDKDIR (%s). Please verify it contains a valid and '
'complete SDK installation.' % windows_sdk_dir_env)
valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True)
if valid_sdks:
biggest_version, sdk = sdks[valid_sdks[0]]
if not valid_sdks or biggest_version < target_version:
if windows_sdk_dir_env:
raise FatalCheckError(
'You are targeting Windows version 0x%04x, but your SDK only '
'supports up to version 0x%04x. Install and use an updated SDK, '
'or target a lower version using --with-windows-version. '
'Alternatively, try running the Windows SDK Configuration Tool '
'and selecting a newer SDK. See '
'https://developer.mozilla.org/En/Windows_SDK_versions for '
'details on fixing this.' % (target_version, biggest_version))
raise FatalCheckError(
'Cannot find a Windows SDK for version >= 0x%04x.' % target_version)
return namespace(
path=sdk.path,
include=sdk.include,
lib=sdk.lib,
version=biggest_version,
)
@imports(_from='mozbuild.shellutil', _import='quote')
def valid_ucrt_sdk_dir_result(value):
if value:
return '%s in %s' % (value.version, quote(value.path))
@depends(windows_sdk_dir, 'WINDOWSSDKDIR')
@checking('for Universal CRT SDK', valid_ucrt_sdk_dir_result)
@imports('os')
@imports(_from='__builtin__', _import='sorted')
@imports(_import='mozpack.path', _as='mozpath')
def valid_ucrt_sdk_dir(windows_sdk_dir, windows_sdk_dir_env):
if windows_sdk_dir_env:
windows_sdk_dir_env = windows_sdk_dir_env[0]
sdks = {}
for d in windows_sdk_dir:
sdk = get_sdk_dirs(d, 'ucrt')
if sdk:
version = os.path.basename(sdk.include)
# We're supposed to always find a version in the directory, because
# the 8.1 SDK, which doesn't have a version in the directory, doesn't
# contain the Universal CRT SDK. When the main SDK is 8.1, there
# is, however, supposed to be a reduced install of the SDK 10
# with the UCRT.
if version != 'include':
sdks[d] = Version(version), sdk
continue
if d == windows_sdk_dir_env:
# When WINDOWSSDKDIR is set in the environment and we can't find the
# Universal CRT SDK, chances are this is a start-shell-msvc*.bat
# setup, where INCLUDE and LIB already contain the UCRT paths.
ucrt_includes = [
p for p in os.environ.get('INCLUDE', '').split(os.pathsep)
if os.path.basename(p).lower() == 'ucrt'
]
ucrt_libs = [
p for p in os.environ.get('LIB', '').split(os.pathsep)
if os.path.basename(os.path.dirname(p)).lower() == 'ucrt'
]
if ucrt_includes and ucrt_libs:
# Pick the first of each, since they are the ones that the
# compiler would look first. Assume they contain the SDK files.
include = os.path.dirname(ucrt_includes[0])
lib = os.path.dirname(os.path.dirname(ucrt_libs[0]))
path = os.path.dirname(os.path.dirname(include))
version = os.path.basename(include)
if version != 'include' and mozpath.basedir(lib, [path]):
sdks[d] = Version(version), namespace(
path=path,
include=include,
lib=lib,
)
continue
raise FatalCheckError(
'The SDK in WINDOWSSDKDIR (%s) does not contain the Universal '
'CRT.' % windows_sdk_dir_env)
valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True)
if not valid_sdks:
raise FatalCheckError('Cannot find the Universal CRT SDK. '
'Please install it.')
version, sdk = sdks[valid_sdks[0]]
minimum_ucrt_version = Version('10.0.17134.0')
if version < minimum_ucrt_version:
raise FatalCheckError('Latest Universal CRT SDK version found %s'
' and minimum required is %s. This or a later'
' version can be installed using the Visual'
' Studio installer.'
% (version, minimum_ucrt_version))
return namespace(
path=sdk.path,
include=sdk.include,
lib=sdk.lib,
version=version,
)
@depends(c_compiler, toolchain_search_path)
@imports('os')
def vc_path(c_compiler, toolchain_search_path):
if c_compiler.type != 'clang-cl':
return
# In clang-cl builds, we need the headers and libraries from an MSVC installation.
vc_program = find_program('cl.exe', paths=toolchain_search_path)
if not vc_program:
die('Cannot find a Visual C++ install for e.g. ATL headers.')
result = os.path.dirname(vc_program)
while True:
next, p = os.path.split(result)
if next == result:
die('Cannot determine the Visual C++ directory the compiler (%s) '
'is in' % vc_program)
result = next
if p.lower() == 'bin':
break
return os.path.normpath(result)
option(env='DIA_SDK_PATH', nargs=1,
help='Path to the Debug Interface Access SDK')
@depends(vc_path, 'DIA_SDK_PATH')
@checking('for the Debug Interface Access SDK', lambda x: x or 'not found')
@imports('os')
def dia_sdk_dir(vc_path, dia_sdk_path):
if dia_sdk_path:
path = os.path.normpath(dia_sdk_path[0])
elif vc_path:
# This would be easier if we had the installationPath that
# get_vc_paths works with, since 'DIA SDK' is relative to that.
path = os.path.normpath(os.path.join(
vc_path, r'..\..\..\..\DIA SDK'))
if os.path.exists(os.path.join(path, 'include', 'dia2.h')):
return path
@depends(vc_path, valid_windows_sdk_dir, valid_ucrt_sdk_dir, dia_sdk_dir)
@imports('os')
def include_path(vc_path, windows_sdk_dir, ucrt_sdk_dir, dia_sdk_dir):
if not vc_path:
return
atlmfc_dir = os.path.join(vc_path, 'atlmfc', 'include')
if not os.path.isdir(atlmfc_dir):
die('Cannot find the ATL/MFC headers in the Visual C++ directory (%s). '
'Please install them.' % vc_path)
winrt_dir = os.path.join(windows_sdk_dir.include, 'winrt')
if not os.path.isdir(winrt_dir):
die('Cannot find the WinRT headers in the Windows SDK directory (%s). '
'Please install them.' % windows_sdk_dir.path)
includes = []
include_env = os.environ.get('INCLUDE')
if include_env:
includes.append(include_env)
includes.extend((
os.path.join(vc_path, 'include'),
atlmfc_dir,
os.path.join(windows_sdk_dir.include, 'shared'),
os.path.join(windows_sdk_dir.include, 'um'),
winrt_dir,
os.path.join(ucrt_sdk_dir.include, 'ucrt'),
))
if dia_sdk_dir:
includes.append(os.path.join(dia_sdk_dir, 'include'))
# Set in the environment for old-configure
includes = os.pathsep.join(includes)
os.environ['INCLUDE'] = includes
return includes
set_config('INCLUDE', include_path)
@template
def dia_sdk_subdir(host_or_target, subdir):
@depends(dia_sdk_dir, host_or_target, dependable(subdir))
def dia_sdk_subdir(dia_sdk_dir, target, subdir):
if not dia_sdk_dir:
return
# For some reason the DIA SDK still uses the old-style targets
# even in a newer MSVC.
old_target = {
'x86': '',
'x86_64': 'amd64',
'arm': 'arm',
'aarch64': 'arm64'
}.get(target.cpu)
if old_target is None:
return
# As old_target can be '', and os.path.join will happily use the empty
# string, leading to a string ending with a backslash, that Make will
# interpret as a "string continues on next line" indicator, use variable
# args.
old_target = (old_target,) if old_target else ()
return os.path.join(dia_sdk_dir, subdir, *old_target)
return dia_sdk_subdir
set_config('WIN_DIA_SDK_BIN_DIR', dia_sdk_subdir(host, 'bin'))
@template
def lib_path_for(host_or_target):
@depends(host_or_target, dependable(host_or_target is host), vc_path,
valid_windows_sdk_dir, valid_ucrt_sdk_dir, dia_sdk_subdir(host_or_target, 'lib'))
@imports('os')
def lib_path(target, is_host, vc_path, windows_sdk_dir, ucrt_sdk_dir, dia_sdk_lib_dir):
if not vc_path:
return
sdk_target = {
'x86': 'x86',
'x86_64': 'x64',
'arm': 'arm',
'aarch64': 'arm64',
}.get(target.cpu)
# MSVC2017 switched to use the same target naming as the sdk.
atlmfc_dir = os.path.join(vc_path, 'atlmfc', 'lib', sdk_target)
if not os.path.isdir(atlmfc_dir):
die('Cannot find the ATL/MFC libraries in the Visual C++ directory '
'(%s). Please install them.' % vc_path)
libs = []
lib_env = os.environ.get('LIB')
if lib_env and not is_host:
libs.extend(lib_env.split(os.pathsep))
libs.extend((
os.path.join(vc_path, 'lib', sdk_target),
atlmfc_dir,
os.path.join(windows_sdk_dir.lib, 'um', sdk_target),
os.path.join(ucrt_sdk_dir.lib, 'ucrt', sdk_target),
))
if dia_sdk_lib_dir:
libs.append(dia_sdk_lib_dir)
return libs
return lib_path
@depends(lib_path_for(target))
@imports('os')
def lib_path(libs):
# Set in the environment for old-configure
libs = os.pathsep.join(libs)
os.environ['LIB'] = libs
return libs
set_config('LIB', lib_path)
lib_path_for_host = lib_path_for(host)
@depends(lib_path_for_host)
@imports(_from='mozbuild.shellutil', _import='quote')
def host_linker_libpaths(libs):
return ['-LIBPATH:%s' % quote(l) for l in libs]
@depends(lib_path_for_host)
@imports(_from='mozbuild.shellutil', _import='quote')
def host_linker_libpaths_bat(libs):
# .bat files need a different style of quoting. Batch quoting is actually
# not defined, and up to applications to handle, so it's not really clear
# what should be escaped and what not, but most paths should work just
# fine without escaping. And we don't care about double-quotes possibly
# having to be escaped because they're not allowed in file names on
# Windows.
return ['"-LIBPATH:%s"' % l for l in libs]
set_config('HOST_LINKER_LIBPATHS', host_linker_libpaths)
set_config('HOST_LINKER_LIBPATHS_BAT', host_linker_libpaths_bat)
option(env='MT', nargs=1, help='Path to the Microsoft Manifest Tool')
@depends(valid_windows_sdk_dir, valid_ucrt_sdk_dir)
@imports(_from='os', _import='environ')
@imports('platform')
def sdk_bin_path(valid_windows_sdk_dir, valid_ucrt_sdk_dir):
if not valid_windows_sdk_dir:
return
vc_host = {
'x86': 'x86',
'AMD64': 'x64',
}.get(platform.machine())
# From version 10.0.15063.0 onwards the bin path contains the version number.
versioned_bin = ('bin' if valid_ucrt_sdk_dir.version < '10.0.15063.0'
else os.path.join('bin', str(valid_ucrt_sdk_dir.version)))
result = [
environ['PATH'],
os.path.join(valid_windows_sdk_dir.path, versioned_bin, vc_host)
]
if vc_host == 'x64':
result.append(
os.path.join(valid_windows_sdk_dir.path, versioned_bin, 'x86'))
return result
mt = check_prog('MT', ('mt.exe',), input='MT',
paths=sdk_bin_path)
# Check that MT is not something unexpected like "magnetic tape manipulation
# utility".
@depends(mt)
@checking('whether MT is really Microsoft Manifest Tool', lambda x: bool(x))
@imports('subprocess')
def valid_mt(path):
try:
out = subprocess.check_output([path]).splitlines()
out = '\n'.join(l for l in out
if 'Microsoft (R) Manifest Tool' in l)
if out:
return path
except subprocess.CalledProcessError:
pass
raise FatalCheckError('%s is not Microsoft Manifest Tool')
set_config('MSMANIFEST_TOOL', depends(valid_mt)(lambda x: bool(x)))
js_option(env='LINKER', nargs=1, help='Path to the linker')
link = check_prog('LINKER', ('lld-link',), input='LINKER',
paths=toolchain_search_path)
js_option(env='HOST_LINKER', nargs=1, help='Path to the host linker')
host_link = check_prog('HOST_LINKER', ('lld-link',), input='HOST_LINKER',
paths=host_toolchain_search_path)
add_old_configure_assignment('LINKER', link)
check_prog('MAKECAB', ('makecab.exe',))