summaryrefslogtreecommitdiffstats
path: root/build/moz.configure/windows.configure
diff options
context:
space:
mode:
Diffstat (limited to 'build/moz.configure/windows.configure')
-rw-r--r--build/moz.configure/windows.configure431
1 files changed, 431 insertions, 0 deletions
diff --git a/build/moz.configure/windows.configure b/build/moz.configure/windows.configure
new file mode 100644
index 000000000..b9a3898a1
--- /dev/null
+++ b/build/moz.configure/windows.configure
@@ -0,0 +1,431 @@
+# -*- 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(target)
+def is_windows(target):
+ return target.kernel == 'WINNT'
+
+
+@template
+def depends_win(*args):
+ return depends_when(*args, when=is_windows)
+
+
+@depends_win('--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_win('WINDOWSSDKDIR', host)
+def windows_sdk_dir(value, host):
+ if value:
+ return value
+ if host.kernel != 'WINNT':
+ return ()
+
+ return tuple(x[1] for x in get_registry_values(
+ r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots'
+ r'\KitsRoot*'))
+
+# 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_win(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='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:
+ 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,
+ )
+
+
+add_old_configure_assignment(
+ 'WINDOWSSDKDIR',
+ delayed_getattr(valid_windows_sdk_dir, 'path'))
+add_old_configure_assignment(
+ 'MOZ_WINSDK_MAXVER',
+ depends(valid_windows_sdk_dir)(
+ lambda x: '0x%04X0000' % x.version if x else None))
+
+
+@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_win(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]]
+
+ return namespace(
+ path=sdk.path,
+ include=sdk.include,
+ lib=sdk.lib,
+ version=version,
+ )
+
+
+@depends_win(c_compiler)
+@imports('os')
+def vc_path(c_compiler):
+ if c_compiler.type != 'msvc':
+ return
+ # Normally, we'd start from c_compiler.compiler, but for now, it's not the
+ # ideal full path to the compiler. At least, we're guaranteed find_program
+ # will get us the one we found in toolchain.configure.
+ cl = find_program(c_compiler.compiler)
+ result = os.path.dirname(cl)
+ while True:
+ next, p = os.path.split(result)
+ if next == result:
+ die('Cannot determine the Visual C++ directory the compiler (%s) '
+ 'is in' % cl)
+ result = next
+ if p.lower() == 'bin':
+ break
+ return result
+
+
+@depends_win(vc_path)
+@checking('for the Debug Interface Access SDK', lambda x: x or 'not found')
+@imports(_from='os.path', _import='isdir')
+def dia_sdk_dir(vc_path):
+ if vc_path:
+ path = os.path.join(os.path.dirname(vc_path), 'DIA SDK')
+ if isdir(path):
+ return path
+
+
+@depends_win(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)
+
+
+@depends_win(target, vc_path, valid_windows_sdk_dir, valid_ucrt_sdk_dir, dia_sdk_dir)
+@imports('os')
+def lib_path(target, vc_path, windows_sdk_dir, ucrt_sdk_dir, dia_sdk_dir):
+ if not vc_path:
+ return
+ vc_target = {
+ 'x86': '',
+ 'x86_64': 'amd64',
+ 'arm': 'arm',
+ }.get(target.cpu)
+ if vc_target is None:
+ return
+ # As vc_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.
+ vc_target = (vc_target,) if vc_target else ()
+ sdk_target = {
+ 'x86': 'x86',
+ 'x86_64': 'x64',
+ 'arm': 'arm',
+ }.get(target.cpu)
+
+ atlmfc_dir = os.path.join(vc_path, 'atlmfc', 'lib', *vc_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:
+ libs.append(lib_env)
+ libs.extend((
+ os.path.join(vc_path, 'lib', *vc_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_dir:
+ libs.append(os.path.join(dia_sdk_dir, 'lib', *vc_target))
+ # Set in the environment for old-configure
+ libs = os.pathsep.join(libs)
+ os.environ['LIB'] = libs
+ return libs
+
+set_config('LIB', lib_path)
+
+
+option(env='MT', nargs=1, help='Path to the Microsoft Manifest Tool')
+
+@depends_win(valid_windows_sdk_dir)
+@imports(_from='os', _import='environ')
+@imports('platform')
+def sdk_bin_path(valid_windows_sdk_dir):
+ if not valid_windows_sdk_dir:
+ return
+
+ vc_host = {
+ 'x86': 'x86',
+ 'AMD64': 'x64',
+ }.get(platform.machine())
+
+ result = [
+ environ['PATH'],
+ os.path.join(valid_windows_sdk_dir.path, 'bin', vc_host)
+ ]
+ if vc_host == 'x64':
+ result.append(
+ os.path.join(valid_windows_sdk_dir.path, 'bin', 'x86'))
+ return result
+
+
+mt = check_prog('MT', depends_win()(lambda: ('mt.exe',)), input='MT',
+ paths=sdk_bin_path)
+
+
+# Check that MT is not something unexpected like "magnetic tape manipulation
+# utility".
+@depends_win(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)))
+
+
+# Ultimately, this will move to toolchain.configure and be turned into a
+# cross-platform check.
+option(env='LD', nargs=1, help='Path to the linker')
+
+link = check_prog('LINK', depends_win()(lambda: ('link.exe',)), input='LD',
+ paths=vc_compiler_path)
+
+add_old_configure_assignment('LD', depends_win(link)(lambda x: x))
+
+
+# Normally, we'd just have CC, etc. set to absolute paths, but the build system
+# doesn't currently handle properly the case where the paths contain spaces.
+# Additionally, there's the issue described in toolchain.configure, in
+# valid_compiler().
+@depends_win(sdk_bin_path)
+@imports('os')
+def alter_path(sdk_bin_path):
+ path = os.pathsep.join(sdk_bin_path)
+ os.environ['PATH'] = path
+ return path
+
+set_config('PATH', alter_path)