build/moz.configure/windows.configure
author Robert Strong <robert.bugzilla@gmail.com>
Wed, 19 Oct 2016 13:27:01 -0700
changeset 348675 4d6033fadbc589a5f6170803e6af65ed8de5c96d
parent 347535 a9d6bdcdb18d3aeb4e2bb967064dd2a215493f1b
child 348979 880f40a0a8327f3cff5c79966300052cdd4eb59a
permissions -rw-r--r--
Client code for Bug 1309125 - Allow configuration of the download throttle via the update.xml. r=mhowell, a=rkothari

# -*- 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.
@imports('os')
@imports('re')
@imports(_from='__builtin__', _import='sorted')
@imports(_from='__builtin__', _import='WindowsError')
def get_include_dir(sdk, subdir):
    base = os.path.join(sdk, 'include')
    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 None
    if subdir in subdirs:
        return os.path.join(base, subdir)
    # At this point, either we have an incomplete or invalid SDK directory,
    # or we exclusively have version numbers in subdirs.
    versions = sorted((Version(d) for d in subdirs), reverse=True)
    # Version('non-number').major is 0, so if the biggest version we have is
    # 0, we have a problem.
    if versions[0].major == 0:
        return None
    path = os.path.join(base, str(versions[0]), subdir)
    return path if os.path.isdir(path) else None


@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:
        um_dir = get_include_dir(d, 'um')
        shared_dir = get_include_dir(d, 'shared')
        if um_dir and shared_dir:
            check = dedent('''\
            #include <winsdkver.h>
            WINVER_MAXVER
            ''')
            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
                    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], reverse=True)
    if valid_sdks:
        biggest_version = 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=valid_sdks[0],
        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))


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


# Normally, we'd use `MT` instead of `_MT`, but for now, we want MT to only contain
# mt.exe.
mt = check_prog('_MT', depends_win()(lambda: ('mt.exe',)), what='mt',
                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('MT', depends_if(valid_mt)(lambda x: os.path.basename(x)))
set_config('MSMANIFEST_TOOL', depends(valid_mt)(lambda x: bool(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)