Bug 1290026 - Automatically set LIB from configure. r?chmanchester draft
authorMike Hommey <mh+mozilla@glandium.org>
Wed, 27 Jul 2016 18:01:04 +0900
changeset 393709 241a93277adb2e57e3524e14b586a0a1442cf376
parent 393708 b1b0cc0dedd5e7720949992fb92c21951b3a14de
child 393712 e9289c45e69061f264227bc5ad3e509004435dbe
push id24389
push userbmo:mh+mozilla@glandium.org
push dateThu, 28 Jul 2016 09:16:10 +0000
reviewerschmanchester
bugs1290026
milestone50.0a1
Bug 1290026 - Automatically set LIB from configure. r?chmanchester
build/moz.configure/windows.configure
config/config.mk
--- a/build/moz.configure/windows.configure
+++ b/build/moz.configure/windows.configure
@@ -45,41 +45,58 @@ def windows_sdk_dir(value, host):
 
     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.
+# 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_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
+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,
@@ -88,61 +105,64 @@ def valid_windows_sdk_dir_result(value):
 @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:
+        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
+                    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], reverse=True)
+    valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True)
     if valid_sdks:
-        biggest_version = sdks[valid_sdks[0]]
+        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=valid_sdks[0],
+        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(
@@ -159,40 +179,44 @@ def valid_ucrt_sdk_dir_result(value):
 @depends_win(windows_sdk_dir, 'WINDOWSSDKDIR')
 @checking('for Universal CRT SDK', valid_ucrt_sdk_dir_result)
 @imports(_from='__builtin__', _import='sorted')
 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:
-        ucrt_dir = get_include_dir(d, 'ucrt')
-        if ucrt_dir:
-            version = os.path.basename(os.path.dirname(ucrt_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)
+                sdks[d] = Version(version), sdk
                 continue
         if d == windows_sdk_dir_env:
             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], reverse=True)
+    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=valid_sdks[0],
-        version=sdks[valid_sdks[0]],
+        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
@@ -226,43 +250,92 @@ def dia_sdk_dir(vc_path):
 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 = get_include_dir(windows_sdk_dir.path, 'winrt')
+    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,
-        get_include_dir(windows_sdk_dir.path, 'shared'),
-        get_include_dir(windows_sdk_dir.path, 'um'),
+        os.path.join(windows_sdk_dir.include, 'shared'),
+        os.path.join(windows_sdk_dir.include, 'um'),
         winrt_dir,
-        get_include_dir(ucrt_sdk_dir.path, 'ucrt'),
+        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
--- a/config/config.mk
+++ b/config/config.mk
@@ -359,16 +359,22 @@ export MACOSX_DEPLOYMENT_TARGET
 endif # MACOSX_DEPLOYMENT_TARGET
 
 # Export to propagate to cl and submake for third-party code.
 # Eventually, we'll want to just use -I.
 ifdef INCLUDE
 export INCLUDE
 endif
 
+# Export to propagate to link.exe and submake for third-party code.
+# Eventually, we'll want to just use -LIBPATH.
+ifdef LIB
+export LIB
+endif
+
 ifdef MOZ_USING_CCACHE
 ifdef CLANG_CXX
 export CCACHE_CPP2=1
 endif
 endif
 
 # Set link flags according to whether we want a console.
 ifeq ($(OS_ARCH),WINNT)