windows.configure (22192B)
1 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- 2 # vim: set filetype=python: 3 # This Source Code Form is subject to the terms of the Mozilla Public 4 # License, v. 2.0. If a copy of the MPL was not distributed with this 5 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 7 option( 8 "--with-windows-version", 9 nargs=1, 10 default="603", 11 help="Windows SDK version to target. Win 8.1 (603) is currently " 12 "the minimum supported version", 13 ) 14 15 16 @depends("--with-windows-version") 17 @imports(_from="__builtin__", _import="ValueError") 18 def valid_windows_version(value): 19 if not value: 20 die("Cannot build with --without-windows-version") 21 try: 22 version = int(value[0], 16) 23 if version in (0x603,): 24 return version 25 except ValueError: 26 pass 27 28 die("Invalid value for --with-windows-version (%s)", value[0]) 29 30 31 option(env="WINDOWSSDKDIR", nargs=1, help="Directory containing the Windows SDK") 32 33 34 @depends( 35 "WINDOWSSDKDIR", "WINSYSROOT", winsysroot, target_windows_abi, host_windows_abi 36 ) 37 def windows_sdk_dir( 38 value, winsysroot_env, winsysroot, target_windows_abi, host_windows_abi 39 ): 40 if value: 41 if winsysroot_env: 42 die("WINDOWSSDKDIR and WINSYSROOT cannot be set together.") 43 if winsysroot: 44 die("WINDOWSSDKDIR cannot be set when using the bootstrapped WINSYSROOT") 45 return value 46 if target_windows_abi != "msvc" and host_windows_abi != "msvc": 47 return () 48 49 if winsysroot: 50 return [os.path.join(winsysroot, "Windows Kits", "10")] 51 52 return set( 53 normalize_path(x[1]) 54 for x in get_registry_values( 55 r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots" 56 r"\KitsRoot*", 57 get_32_and_64_bit=True, 58 ) 59 ) 60 61 62 @imports("glob") 63 def get_sdk_dirs(sdk, subdir): 64 def get_dirs_containing(sdk, stem, subdir): 65 return [ 66 os.path.dirname(p) for p in glob.glob(os.path.join(sdk, stem, "*", subdir)) 67 ] 68 69 def categorize(dirs): 70 return {os.path.basename(d): d for d in dirs} 71 72 include_dirs = categorize(get_dirs_containing(sdk, "Include", subdir)) 73 lib_dirs = categorize(get_dirs_containing(sdk, "Lib", subdir)) 74 75 valid_versions = sorted(set(include_dirs) & set(lib_dirs), reverse=True) 76 return [ 77 namespace( 78 path=sdk, 79 lib=lib_dirs[vv], 80 include=include_dirs[vv], 81 ) 82 for vv in valid_versions 83 ] 84 85 86 @imports(_from="mozshellutil", _import="quote") 87 def valid_windows_sdk_dir_result(value): 88 if value: 89 return "0x%04x in %s" % (value.version, quote(value.path)) 90 91 92 @depends( 93 target_windows_abi, 94 host_windows_abi, 95 windows_sdk_dir, 96 valid_windows_version, 97 "WINDOWSSDKDIR", 98 ) 99 @checking("for Windows SDK", valid_windows_sdk_dir_result) 100 @imports(_from="__builtin__", _import="Exception") 101 @imports(_from="__builtin__", _import="open") 102 def valid_windows_sdk_dir( 103 target_windows_abi, 104 host_windows_abi, 105 windows_sdk_dir, 106 target_version, 107 windows_sdk_dir_env, 108 ): 109 if target_windows_abi != "msvc" and host_windows_abi != "msvc": 110 return None 111 if windows_sdk_dir_env: 112 windows_sdk_dir_env = windows_sdk_dir_env[0] 113 sdks = {} 114 for d in windows_sdk_dir: 115 sdklist = get_sdk_dirs(d, "um") 116 for sdk in sdklist: 117 maxver = None 118 winsdkver = os.path.join(sdk.include, "um", "winsdkver.h") 119 with open(winsdkver) as fh: 120 for line in fh.readlines(): 121 if line.startswith("#define"): 122 line = line.split(None, 2) 123 assert line.pop(0) == "#define" 124 if line.pop(0) == "WINVER_MAXVER": 125 maxver = line.pop(0) 126 break 127 128 if maxver: 129 try: 130 maxver = int(maxver, 0) 131 except Exception: 132 pass 133 else: 134 sdks[d] = maxver, sdk 135 break 136 137 if d == windows_sdk_dir_env and d not in sdks: 138 raise FatalCheckError( 139 "Error while checking the version of the SDK in " 140 "WINDOWSSDKDIR (%s). Please verify it contains a valid and " 141 "complete SDK installation." % windows_sdk_dir_env 142 ) 143 144 valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True) 145 if valid_sdks: 146 biggest_version, sdk = sdks[valid_sdks[0]] 147 if not valid_sdks or biggest_version < target_version: 148 if windows_sdk_dir_env: 149 raise FatalCheckError( 150 "You are targeting Windows version 0x%04x, but your SDK only " 151 "supports up to version 0x%04x. Install and use an updated SDK, " 152 "or target a lower version using --with-windows-version. " 153 "Alternatively, try running the Windows SDK Configuration Tool " 154 "and selecting a newer SDK. See " 155 "https://developer.mozilla.org/En/Windows_SDK_versions for " 156 "details on fixing this." % (target_version, biggest_version) 157 ) 158 159 raise FatalCheckError( 160 "Cannot find a Windows SDK for version >= 0x%04x." % target_version 161 ) 162 163 return namespace( 164 path=sdk.path, 165 include=sdk.include, 166 lib=sdk.lib, 167 version=biggest_version, 168 ) 169 170 171 @imports(_from="mozshellutil", _import="quote") 172 def valid_ucrt_sdk_dir_result(value): 173 if value: 174 return "%s in %s" % (value.version, quote(value.path)) 175 176 177 @depends(windows_sdk_dir, "WINDOWSSDKDIR", target_windows_abi, host_windows_abi) 178 @checking("for Universal CRT SDK", valid_ucrt_sdk_dir_result) 179 @imports("os") 180 @imports(_import="mozpack.path", _as="mozpath") 181 def valid_ucrt_sdk_dir( 182 windows_sdk_dir, windows_sdk_dir_env, target_windows_abi, host_windows_abi 183 ): 184 if target_windows_abi != "msvc" and host_windows_abi != "msvc": 185 return None 186 if windows_sdk_dir_env: 187 windows_sdk_dir_env = windows_sdk_dir_env[0] 188 sdks = {} 189 for d in windows_sdk_dir: 190 sdklist = get_sdk_dirs(d, "ucrt") 191 for sdk in sdklist: 192 version = os.path.basename(sdk.include) 193 # We're supposed to always find a version in the directory, because 194 # the 8.1 SDK, which doesn't have a version in the directory, doesn't 195 # contain the Universal CRT SDK. When the main SDK is 8.1, there 196 # is, however, supposed to be a reduced install of the SDK 10 197 # with the UCRT. 198 if version != "include": 199 sdks[d] = Version(version), sdk 200 break 201 202 if d == windows_sdk_dir_env and d not in sdks: 203 # When WINDOWSSDKDIR is set in the environment and we can't find the 204 # Universal CRT SDK, chances are this is a start-shell-msvc*.bat 205 # setup, where INCLUDE and LIB already contain the UCRT paths. 206 ucrt_includes = [ 207 p 208 for p in os.environ.get("INCLUDE", "").split(";") 209 if os.path.basename(p).lower() == "ucrt" 210 ] 211 ucrt_libs = [ 212 p 213 for p in os.environ.get("LIB", "").split(";") 214 if os.path.basename(os.path.dirname(p)).lower() == "ucrt" 215 ] 216 if ucrt_includes and ucrt_libs: 217 # Pick the first of each, since they are the ones that the 218 # compiler would look first. Assume they contain the SDK files. 219 include = os.path.dirname(ucrt_includes[0]) 220 lib = os.path.dirname(os.path.dirname(ucrt_libs[0])) 221 path = os.path.dirname(os.path.dirname(include)) 222 version = os.path.basename(include) 223 if version != "include" and mozpath.basedir(lib, [path]): 224 sdks[d] = ( 225 Version(version), 226 namespace( 227 path=path, 228 include=include, 229 lib=lib, 230 ), 231 ) 232 continue 233 raise FatalCheckError( 234 "The SDK in WINDOWSSDKDIR (%s) does not contain the Universal " 235 "CRT." % windows_sdk_dir_env 236 ) 237 238 valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True) 239 if not valid_sdks: 240 raise FatalCheckError( 241 "Cannot find the Universal CRT SDK. " "Please install it." 242 ) 243 244 version, sdk = sdks[valid_sdks[0]] 245 minimum_ucrt_version = Version("10.0.17134.0") 246 if version < minimum_ucrt_version: 247 raise FatalCheckError( 248 "Latest Universal CRT SDK version found %s" 249 " and minimum required is %s. This or a later" 250 " version can be installed using the Visual" 251 " Studio installer." % (version, minimum_ucrt_version) 252 ) 253 254 return namespace( 255 path=sdk.path, 256 include=sdk.include, 257 lib=sdk.lib, 258 version=version, 259 ) 260 261 262 @depends(target_windows_abi, host_windows_abi, vc_toolchain_search_path) 263 @imports("os") 264 def vc_path(target_windows_abi, host_windows_abi, vc_toolchain_search_path): 265 if target_windows_abi != "msvc" and host_windows_abi != "msvc": 266 return 267 268 # In clang-cl builds, we need the headers and libraries from an MSVC installation. 269 vc_program = find_program("cl.exe", paths=vc_toolchain_search_path) 270 if not vc_program: 271 die("Cannot find a Visual C++ install for e.g. ATL headers.") 272 273 result = os.path.dirname(vc_program) 274 while True: 275 next, p = os.path.split(result) 276 if next == result: 277 die( 278 "Cannot determine the Visual C++ directory the compiler (%s) " 279 "is in" % vc_program 280 ) 281 result = next 282 if p.lower() == "bin": 283 break 284 return os.path.normpath(result) 285 286 287 @depends(vc_path, valid_windows_sdk_dir, valid_ucrt_sdk_dir) 288 @imports("os") 289 def include_path(vc_path, windows_sdk_dir, ucrt_sdk_dir): 290 if not vc_path: 291 return 292 atlmfc_dir = os.path.join(vc_path, "atlmfc", "include") 293 if not os.path.isdir(atlmfc_dir): 294 die( 295 "Cannot find the ATL/MFC headers in the Visual C++ directory (%s). " 296 "Please install them." % vc_path 297 ) 298 299 winrt_dir = os.path.join(windows_sdk_dir.include, "winrt") 300 if not os.path.isdir(winrt_dir): 301 die( 302 "Cannot find the WinRT headers in the Windows SDK directory (%s). " 303 "Please install them." % windows_sdk_dir.path 304 ) 305 306 cppwinrt_dir = os.path.join(windows_sdk_dir.include, "cppwinrt") 307 if not os.path.isdir(cppwinrt_dir): 308 die( 309 "Cannot find the C++/WinRT headers in the Windows SDK directory (%s). " 310 "Please install them." % windows_sdk_dir.path 311 ) 312 313 includes = [] 314 include_env = os.environ.get("INCLUDE") 315 if include_env: 316 includes.append(include_env) 317 includes.extend( 318 ( 319 os.path.join(vc_path, "include"), 320 atlmfc_dir, 321 os.path.join(windows_sdk_dir.include, "shared"), 322 os.path.join(windows_sdk_dir.include, "um"), 323 winrt_dir, 324 cppwinrt_dir, 325 os.path.join(ucrt_sdk_dir.include, "ucrt"), 326 ) 327 ) 328 # Set in the environment for old-configure 329 includes = ";".join(includes) 330 os.environ["INCLUDE"] = includes 331 return includes 332 333 334 set_config("INCLUDE", include_path) 335 336 # cppwinrt requires this on clang because of no coroutine support, which is okay 337 set_define("_SILENCE_CLANG_COROUTINE_MESSAGE", "") 338 339 340 @template 341 def lib_path_for(host_or_target): 342 @depends( 343 host_or_target, 344 dependable(host_or_target is host), 345 vc_path, 346 valid_windows_sdk_dir, 347 valid_ucrt_sdk_dir, 348 c_compiler, 349 ) 350 @imports("os") 351 def lib_path(target, is_host, vc_path, windows_sdk_dir, ucrt_sdk_dir, compiler): 352 if not vc_path or target.os != "WINNT": 353 return 354 sdk_target = { 355 "x86": "x86", 356 "x86_64": "x64", 357 "arm": "arm", 358 "aarch64": "arm64", 359 }.get(target.cpu) 360 361 # MSVC2017 switched to use the same target naming as the sdk. 362 atlmfc_dir = os.path.join(vc_path, "atlmfc", "lib", sdk_target) 363 if not os.path.isdir(atlmfc_dir): 364 die( 365 "Cannot find the ATL/MFC libraries in the Visual C++ directory " 366 "(%s). Please install them." % vc_path 367 ) 368 369 libs = [] 370 lib_env = os.environ.get("LIB") 371 if lib_env and not is_host: 372 libs.extend(lib_env.split(";")) 373 libs.extend( 374 ( 375 os.path.join(vc_path, "lib", sdk_target), 376 atlmfc_dir, 377 os.path.join(windows_sdk_dir.lib, "um", sdk_target), 378 os.path.join(ucrt_sdk_dir.lib, "ucrt", sdk_target), 379 ) 380 ) 381 if compiler.type == "clang-cl": 382 runtime_dir = check_cmd_output( 383 compiler.compiler, 384 "/clang:--print-runtime-dir", 385 *compiler.flags, 386 onerror=lambda: None, 387 ).strip() 388 if runtime_dir and os.path.exists(runtime_dir): 389 # Put the clang runtime directory first, in case there is 390 # a different version in some of the other directories (notably, 391 # some versions of MSVC come with clang runtimes) 392 libs.insert(0, runtime_dir) 393 return libs 394 395 return lib_path 396 397 398 @depends_if(lib_path_for(target), when=target_is_windows) 399 @imports("os") 400 def lib_path(libs): 401 # Set in the environment for old-configure 402 libs = ";".join(libs) 403 os.environ["LIB"] = libs 404 return libs 405 406 407 set_config("LIB", lib_path) 408 409 410 lib_path_for_host = lib_path_for(host) 411 412 413 @depends_if(lib_path_for_host, when=host_is_windows) 414 @imports(_from="mozshellutil", _import="quote") 415 def host_linker_libpaths(libs): 416 return ["-LIBPATH:%s" % quote(l) for l in libs] 417 418 419 @depends_if(lib_path_for_host, when=host_is_windows) 420 @imports(_from="mozshellutil", _import="quote") 421 def host_linker_libpaths_bat(libs): 422 # .bat files need a different style of quoting. Batch quoting is actually 423 # not defined, and up to applications to handle, so it's not really clear 424 # what should be escaped and what not, but most paths should work just 425 # fine without escaping. And we don't care about double-quotes possibly 426 # having to be escaped because they're not allowed in file names on 427 # Windows. 428 return ['"-LIBPATH:%s"' % l for l in libs] 429 430 431 set_config("HOST_LINKER_LIBPATHS", host_linker_libpaths) 432 set_config("HOST_LINKER_LIBPATHS_BAT", host_linker_libpaths_bat) 433 434 435 @depends(valid_windows_sdk_dir, valid_ucrt_sdk_dir, host) 436 @imports(_from="os", _import="environ") 437 def sdk_bin_path(valid_windows_sdk_dir, valid_ucrt_sdk_dir, host): 438 if not valid_windows_sdk_dir: 439 return 440 441 vc_host = { 442 "x86": "x86", 443 "x86_64": "x64", 444 "aarch64": "arm64", 445 }.get(host.cpu) 446 447 # From version 10.0.15063.0 onwards the bin path contains the version number. 448 versioned_bin = ( 449 "bin" 450 if valid_ucrt_sdk_dir.version < "10.0.15063.0" 451 else os.path.join("bin", str(valid_ucrt_sdk_dir.version)) 452 ) 453 result = [ 454 environ["PATH"], 455 os.path.join(valid_windows_sdk_dir.path, versioned_bin, vc_host), 456 ] 457 if vc_host == "x64": 458 result.append(os.path.join(valid_windows_sdk_dir.path, versioned_bin, "x86")) 459 return result 460 461 462 option(env="LINKER", nargs=1, when=target_is_windows, help="Path to the linker") 463 464 link = check_prog( 465 "LINKER", 466 ("lld-link",), 467 input="LINKER", 468 when=target_is_windows, 469 paths=clang_search_path, 470 ) 471 472 option(env="HOST_LINKER", nargs=1, when=host_is_windows, help="Path to the host linker") 473 474 host_link = check_prog( 475 "HOST_LINKER", 476 ("lld-link",), 477 input="HOST_LINKER", 478 when=host_is_windows, 479 paths=clang_search_path, 480 ) 481 482 483 option( 484 "--with-redist", 485 env="WIN32_REDIST_DIR", 486 nargs="?", 487 help="{Package|Don't package} redistributable MSVCRT", 488 ) 489 490 491 @depends("--with-redist", moz_automation, c_compiler, vc_path, target) 492 @imports("os") 493 def win32_redist_dir(redist, automation, c_compiler, vc_path, target): 494 if len(redist): 495 if os.path.isdir(redist[0]): 496 return redist[0] 497 configure_error(f"Invalid Win32 Redist directory: {redist[0]}") 498 if redist or ( 499 automation and redist.origin == "default" and c_compiler.type == "clang-cl" 500 ): 501 if not vc_path: 502 configure_error("Cannot ship redistributable MSVCRT without MSVC") 503 # It would be too simple if the Redist dir had the same version number as 504 # the MSVC one. 505 base_redist_path = os.path.join( 506 os.path.dirname(os.path.dirname(os.path.dirname(vc_path))), "Redist", "MSVC" 507 ) 508 redist_target = { 509 "x86": "x86", 510 "x86_64": "x64", 511 "aarch64": "arm64", 512 }.get(target.cpu) 513 if redist_target and os.path.isdir(base_redist_path): 514 versions = [Version(v) for v in os.listdir(base_redist_path)] 515 redist_path = os.path.join( 516 base_redist_path, 517 str(max(v for v in versions if v.major)), 518 redist_target, 519 ) 520 if os.path.isdir(redist_path): 521 crt_path = max(p for p in os.listdir(redist_path) if p.endswith("CRT")) 522 if crt_path: 523 return os.path.join(redist_path, crt_path) 524 configure_error("Could not find redistributable MSVCRT files") 525 526 527 set_config("WIN32_REDIST_DIR", win32_redist_dir) 528 529 530 @template 531 def check_w32API_version(): 532 # minimum version of toolkit libs used by mozilla 533 W32API_VERSION = Version("3.14") 534 # Check w32api version 535 try_compile( 536 includes=["w32api.h"], 537 check_msg=f"for w32api version >= {W32API_VERSION}", 538 body=f""" 539 #if (__W32API_MAJOR_VERSION < {W32API_VERSION.major}) || \ 540 (__W32API_MAJOR_VERSION == {W32API_VERSION.major} && \ 541 __W32API_MINOR_VERSION < {W32API_VERSION.minor}) 542 #error "test failed." 543 #endif 544 """, 545 onerror=lambda: die(f"w32api version {W32API_VERSION} or higher required."), 546 when=target_is_windows & building_with_gnu_compatible_cc, 547 ) 548 549 550 @depends(target) 551 def cpu_arch_macro_definition(target): 552 if target.cpu == "x86": 553 return "_X86_" 554 elif target.cpu == "x86_64": 555 return "_AMD64_" 556 elif target.cpu == "aarch64": 557 return "_ARM64_" 558 else: 559 return "_CPU_ARCH_NOT_DEFINED" 560 561 562 set_define(cpu_arch_macro_definition, True) 563 564 565 check_w32API_version() 566 567 568 # -Zc:sizedDealloc- disables C++14 global sized deallocation (see bug 1160146) 569 add_flag( 570 "-Zc:sizedDealloc-", compiler=cxx_compiler, when=~building_with_gnu_compatible_cc 571 ) 572 573 574 set_define("_USE_MATH_DEFINES", True) # Otherwise Windows' math.h doesn't #define M_PI. 575 576 WINVER = dependable("0A00") 577 set_define("WINVER", depends(WINVER)(lambda WINVER: f"0x{WINVER}")) 578 set_define("_WIN32_WINNT", depends(WINVER)(lambda WINVER: f"0x{WINVER}")) 579 set_define("_WIN32_IE", "0x0A00") 580 581 with only_when(depends(c_compiler)(lambda c: c.type == "clang-cl")): 582 set_config("MSVC_C_RUNTIME_DLL", "vcruntime140.dll") 583 set_config( 584 "MSVC_C_RUNTIME_1_DLL", 585 "vcruntime140_1.dll", 586 when=depends(target)(lambda target: target.cpu != "x86"), 587 ) 588 set_config("MSVC_CXX_RUNTIME_DLL", "msvcp140.dll") 589 set_config("MSVC_CXX_RUNTIME_ATOMIC_WAIT_DLL", "msvcp140_atomic_wait.dll") 590 591 # Disable warnings about using MSVC-specific secure CRT functions. 592 set_define("_CRT_SECURE_NO_WARNINGS", True) 593 set_define("_CRT_NONSTDC_NO_WARNINGS", True) 594 595 # Work around issues with newer STL and older runtimes, see bug 1994496. 596 set_define("_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR", True) 597 598 set_define("HAVE_SEH_EXCEPTIONS", True) 599 set_define("HAVE_IO_H", True) 600 601 def win32_subsystem_version(): 602 return "10.0" 603 604 set_config( 605 "WIN32_CONSOLE_EXE_LDFLAGS", 606 ["-SUBSYSTEM:CONSOLE,{}".format(win32_subsystem_version())], 607 ) 608 set_config( 609 "WIN32_GUI_EXE_LDFLAGS", 610 ["-SUBSYSTEM:WINDOWS,{}".format(win32_subsystem_version())], 611 ) 612 613 add_flag("-Gy") 614 add_flag("-Zc:inline") 615 616 # VS2013+ supports -Gw for better linker optimizations. 617 # http://blogs.msdn.com/b/vcblog/archive/2013/09/11/introducing-gw-compiler-switch.aspx 618 # Disabled on ASan because it causes false-positive ODR violations. 619 add_flag("-Gw", when=~asan) 620 621 # String tail merging doesn't play nice with ASan's ODR checker. 622 add_linker_flag("-opt:nolldtailmerge", when=asan) 623 624 # Silence VS2017 15.5+ TR1 deprecation warnings hit by older gtest versions 625 add_flag("-D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING", compiler=cxx_compiler) 626 627 with only_when(depends(target.cpu)(lambda cpu: cpu == "x86")): 628 add_linker_flag("-LARGEADDRESSAWARE") 629 add_linker_flag("-SAFESEH") 630 631 632 set_define("WIN32_LEAN_AND_MEAN", True) 633 634 with only_when(depends(c_compiler)(lambda c: c.type == "clang-cl")): 635 # See http://support.microsoft.com/kb/143208 to use STL 636 set_define("NOMINMAX", True) 637 638 639 with only_when(target_is_windows & depends(c_compiler)(lambda c: c.type != "clang-cl")): 640 # strsafe.h on mingw uses macros for function deprecation that pollutes namespace 641 # causing problems with local implementations with the same name. 642 set_define("STRSAFE_NO_DEPRECATE", True) 643 644 set_config("WIN32_CONSOLE_EXE_LDFLAGS", ["-mconsole"]) 645 set_config("WIN32_GUI_EXE_LDFLAGS", ["-mwindows"]) 646 647 # Silence problematic clang warnings 648 add_flag("-Wno-incompatible-ms-struct", compiler=cxx_compiler) 649 add_linker_flag("-Wl,--no-insert-timestamp") 650 651 with only_when(depends(target.cpu)(lambda cpu: cpu == "x86")): 652 add_flag("-mstackrealign") 653 add_linker_flag("-Wl,--large-address-aware")