Index: libcxx/include/CMakeLists.txt =================================================================== --- libcxx/include/CMakeLists.txt +++ libcxx/include/CMakeLists.txt @@ -237,14 +237,15 @@ add_library(${CXX_HEADER_TARGET} INTERFACE) add_dependencies(${CXX_HEADER_TARGET} generate-cxx-headers ${LIBCXX_CXX_ABI_HEADER_TARGET}) # TODO: Use target_include_directories once we figure out why that breaks the runtimes build - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") + + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC" AND NOT ${CMAKE_CXX_COMPILER_FRONTEND_VARIANT} STREQUAL "GNU") target_compile_options(${CXX_HEADER_TARGET} INTERFACE /I "${output_dir}") else() target_compile_options(${CXX_HEADER_TARGET} INTERFACE -I "${output_dir}") endif() # Make sure the generated __config_site header is included when we build the library. - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC" AND NOT ${CMAKE_CXX_COMPILER_FRONTEND_VARIANT} STREQUAL "GNU") target_compile_options(${CXX_HEADER_TARGET} INTERFACE /FI "${LIBCXX_BINARY_DIR}/__config_site") else() target_compile_options(${CXX_HEADER_TARGET} INTERFACE -include "${LIBCXX_BINARY_DIR}/__config_site") Index: libcxx/include/support/win32/locale_win32.h =================================================================== --- libcxx/include/support/win32/locale_win32.h +++ libcxx/include/support/win32/locale_win32.h @@ -12,9 +12,41 @@ #include <__config> #include -#include // _locale_t +#include // _locale_t #include <__nullptr> +<<>>> + #define LC_COLLATE_MASK _M_COLLATE #define LC_CTYPE_MASK _M_CTYPE #define LC_MONETARY_MASK _M_MONETARY Index: libcxx/src/CMakeLists.txt =================================================================== --- libcxx/src/CMakeLists.txt +++ libcxx/src/CMakeLists.txt @@ -57,11 +57,18 @@ ) if(WIN32) + if(LIBCXX_ENABLE_THREADS) list(APPEND LIBCXX_SOURCES support/win32/locale_win32.cpp support/win32/support.cpp support/win32/thread_win32.cpp ) + else() + list(APPEND LIBCXX_SOURCES + support/win32/locale_win32.cpp + support/win32/support.cpp + ) + endif() elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "SunOS") list(APPEND LIBCXX_SOURCES support/solaris/mbsnrtowcs.inc @@ -192,6 +199,8 @@ if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY) if (APPLE) target_link_libraries(cxx_shared PRIVATE "-Wl,-force_load" "${LIBCXX_CXX_STATIC_ABI_LIBRARY}") + elseif (WIN32) + target_link_libraries(cxx_shared PRIVATE "-Wl,/WHOLEARCHIVE:${LIBCXX_CXX_STATIC_ABI_LIBRARY}${CMAKE_STATIC_LIBRARY_SUFFIX}") else() target_link_libraries(cxx_shared PRIVATE "-Wl,--whole-archive,-Bstatic" "${LIBCXX_CXX_STATIC_ABI_LIBRARY}" "-Wl,-Bdynamic,--no-whole-archive") endif() @@ -277,6 +286,9 @@ endif() if (TARGET "${LIBCXX_CXX_STATIC_ABI_LIBRARY}" OR HAVE_LIBCXXABI) set(MERGE_ARCHIVES_ABI_TARGET "$") + elseif (WIN32) + set(MERGE_ARCHIVES_ABI_TARGET + "${LIBCXX_CXX_STATIC_ABI_LIBRARY}${CMAKE_STATIC_LIBRARY_SUFFIX}") else() set(MERGE_ARCHIVES_ABI_TARGET "${CMAKE_STATIC_LIBRARY_PREFIX}${LIBCXX_CXX_STATIC_ABI_LIBRARY}${CMAKE_STATIC_LIBRARY_SUFFIX}") Index: libcxx/utils/merge_archives.py =================================================================== --- libcxx/utils/merge_archives.py +++ libcxx/utils/merge_archives.py @@ -31,9 +31,6 @@ def find_and_diagnose_missing(lib, search_paths): if os.path.exists(lib): return os.path.abspath(lib) - if not lib.startswith('lib') or not lib.endswith('.a'): - print_and_exit(("input file '%s' not not name a static library. " - "It should start with 'lib' and end with '.a") % lib) for sp in search_paths: assert type(sp) is list and len(sp) == 1 path = os.path.join(sp[0], lib) Index: libcxxabi/CMakeLists.txt =================================================================== --- libcxxabi/CMakeLists.txt +++ libcxxabi/CMakeLists.txt @@ -103,6 +103,8 @@ option(LIBCXXABI_ENABLE_SHARED "Build libc++abi as a shared library." ON) option(LIBCXXABI_ENABLE_STATIC "Build libc++abi as a static library." ON) +option(LIBCXXABI_ENABLE_STATIC_FOR_LIBCXX_SHARED "Build libc++abi as a static library to be linked into a libcxx shared library." ON) + cmake_dependent_option(LIBCXXABI_INSTALL_STATIC_LIBRARY "Install the static libc++abi library." ON "LIBCXXABI_ENABLE_STATIC;LIBCXXABI_INSTALL_LIBRARY" OFF) @@ -257,10 +259,17 @@ add_definitions(-D_LIBCXXABI_BUILDING_LIBRARY) # Disable DLL annotations on Windows for static builds. -if (WIN32 AND LIBCXXABI_ENABLE_STATIC AND NOT LIBCXXABI_ENABLE_SHARED) +if (WIN32 AND LIBCXXABI_ENABLE_STATIC AND NOT LIBCXXABI_ENABLE_SHARED AND NOT LIBCXXABI_ENABLE_STATIC_FOR_LIBCXX_SHARED) add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS) endif() +# We are building libcxxabi as a static library and linking it into libcxx; +# therefore, we need the export annotations to be in place so that the +# required symobls will be exported from the final libcxx dll. +if (LIBCXXABI_ENABLE_STATIC_FOR_LIBCXX_SHARED) + add_definitions(-D_LIBCPP_BUILDING_LIBRARY) +endif() + add_compile_flags_if_supported(-Werror=return-type) # Get warning flags @@ -409,8 +418,20 @@ # pre-C++17. add_definitions(-D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS) -if (MSVC) +if (MSVC OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") add_definitions(-D_CRT_SECURE_NO_WARNINGS) + # There is a symbol dependency between libc++ and libc++abi. To break this dependency + # we build libc++abi as a library and then statically link that library into the + # libc++ dll. In order to make this work I have copied this block of compiler flags from + # libc++'s' CMakeLists.txt. Unless these flags are normalized link errors are emitted + # when trying to link the libc++ dll, for example: + # lld-link: error: /failifmismatch: mismatch detected for '_CRT_STDIO_ISO_WIDE_SPECIFIERS': + # >>> src/CMakeFiles/cxx_shared.dir/support/win32/support.cpp.obj has value 1 + # >>> c++abi.lib(cxa_handlers.cpp.obj) has value 0 + add_definitions(-D_ALLOW_MSC_VER_MISMATCH) + add_definitions(-D_ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH) + add_definitions(-D_CRTBLD) + add_definitions(-D_CRT_STDIO_ISO_WIDE_SPECIFIERS) endif() # Define LIBCXXABI_USE_LLVM_UNWINDER for conditional compilation. Index: libcxxabi/src/cxa_guard_impl.h =================================================================== --- libcxxabi/src/cxa_guard_impl.h +++ libcxxabi/src/cxa_guard_impl.h @@ -39,7 +39,9 @@ #include "__cxxabi_config.h" #include "include/atomic_support.h" -#include +// Avoid dragging in vcruntime headers +// TODO: Control this properly with the preprocessor. +//#include #if defined(__has_include) # if __has_include() # include Index: libunwind/src/CMakeLists.txt =================================================================== --- libunwind/src/CMakeLists.txt +++ libunwind/src/CMakeLists.txt @@ -67,6 +67,7 @@ ${LIBUNWIND_ASM_SOURCES}) # Generate library list. +if (LIBUNWIND_NO_LIBRARIES) add_library_flags_if(LIBUNWIND_HAS_C_LIB c) if (LIBUNWIND_USE_COMPILER_RT) add_library_flags("${LIBUNWIND_BUILTINS_LIBRARY}") @@ -79,6 +80,7 @@ add_library_flags_if(LIBUNWIND_HAS_PTHREAD_LIB pthread) add_compile_flags_if(LIBUNWIND_WEAK_PTHREAD_LIB -DLIBUNWIND_USE_WEAK_PTHREAD=1) endif() +endif() # Setup flags. add_link_flags_if_supported(-nodefaultlibs) Index: wi.patch =================================================================== --- /dev/null +++ wi.patch @@ -0,0 +1,184 @@ +diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt +index be8141c9816..0f9da38a230 100644 +--- a/libcxx/include/CMakeLists.txt ++++ b/libcxx/include/CMakeLists.txt +@@ -237,14 +237,15 @@ if(LIBCXX_HEADER_DIR) + add_library(${CXX_HEADER_TARGET} INTERFACE) + add_dependencies(${CXX_HEADER_TARGET} generate-cxx-headers ${LIBCXX_CXX_ABI_HEADER_TARGET}) + # TODO: Use target_include_directories once we figure out why that breaks the runtimes build +- if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") ++ ++ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC" AND NOT ${CMAKE_CXX_COMPILER_FRONTEND_VARIANT} STREQUAL "GNU") + target_compile_options(${CXX_HEADER_TARGET} INTERFACE /I "${output_dir}") + else() + target_compile_options(${CXX_HEADER_TARGET} INTERFACE -I "${output_dir}") + endif() + + # Make sure the generated __config_site header is included when we build the library. +- if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") ++ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC" AND NOT ${CMAKE_CXX_COMPILER_FRONTEND_VARIANT} STREQUAL "GNU") + target_compile_options(${CXX_HEADER_TARGET} INTERFACE /FI "${LIBCXX_BINARY_DIR}/__config_site") + else() + target_compile_options(${CXX_HEADER_TARGET} INTERFACE -include "${LIBCXX_BINARY_DIR}/__config_site") +diff --git a/libcxx/include/support/win32/locale_win32.h b/libcxx/include/support/win32/locale_win32.h +index 8d7779e0cca..1f3ce9d648d 100644 +--- a/libcxx/include/support/win32/locale_win32.h ++++ b/libcxx/include/support/win32/locale_win32.h +@@ -12,9 +12,41 @@ + + #include <__config> + #include +-#include // _locale_t ++#include // _locale_t + #include <__nullptr> + ++<<>>> ++ + #define LC_COLLATE_MASK _M_COLLATE + #define LC_CTYPE_MASK _M_CTYPE + #define LC_MONETARY_MASK _M_MONETARY +diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt +index 2001c09761d..ddb5b9d0c78 100644 +--- a/libcxx/src/CMakeLists.txt ++++ b/libcxx/src/CMakeLists.txt +@@ -57,11 +57,18 @@ set(LIBCXX_SOURCES + ) + + if(WIN32) ++ if(LIBCXX_ENABLE_THREADS) + list(APPEND LIBCXX_SOURCES + support/win32/locale_win32.cpp + support/win32/support.cpp + support/win32/thread_win32.cpp + ) ++ else() ++ list(APPEND LIBCXX_SOURCES ++ support/win32/locale_win32.cpp ++ support/win32/support.cpp ++ ) ++ endif() + elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "SunOS") + list(APPEND LIBCXX_SOURCES + support/solaris/mbsnrtowcs.inc +@@ -192,6 +199,8 @@ if (LIBCXX_ENABLE_SHARED) + if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY) + if (APPLE) + target_link_libraries(cxx_shared PRIVATE "-Wl,-force_load" "${LIBCXX_CXX_STATIC_ABI_LIBRARY}") ++ elseif (WIN32) ++ target_link_libraries(cxx_shared PRIVATE "-Wl,/WHOLEARCHIVE:${LIBCXX_CXX_STATIC_ABI_LIBRARY}${CMAKE_STATIC_LIBRARY_SUFFIX}") + else() + target_link_libraries(cxx_shared PRIVATE "-Wl,--whole-archive,-Bstatic" "${LIBCXX_CXX_STATIC_ABI_LIBRARY}" "-Wl,-Bdynamic,--no-whole-archive") + endif() +@@ -277,6 +286,9 @@ if (LIBCXX_ENABLE_STATIC) + endif() + if (TARGET "${LIBCXX_CXX_STATIC_ABI_LIBRARY}" OR HAVE_LIBCXXABI) + set(MERGE_ARCHIVES_ABI_TARGET "$") ++ elseif (WIN32) ++ set(MERGE_ARCHIVES_ABI_TARGET ++ "${LIBCXX_CXX_STATIC_ABI_LIBRARY}${CMAKE_STATIC_LIBRARY_SUFFIX}") + else() + set(MERGE_ARCHIVES_ABI_TARGET + "${CMAKE_STATIC_LIBRARY_PREFIX}${LIBCXX_CXX_STATIC_ABI_LIBRARY}${CMAKE_STATIC_LIBRARY_SUFFIX}") +diff --git a/libcxx/utils/merge_archives.py b/libcxx/utils/merge_archives.py +index d751526359b..d93f13bda44 100755 +--- a/libcxx/utils/merge_archives.py ++++ b/libcxx/utils/merge_archives.py +@@ -31,9 +31,6 @@ def print_and_exit(msg): + def find_and_diagnose_missing(lib, search_paths): + if os.path.exists(lib): + return os.path.abspath(lib) +- if not lib.startswith('lib') or not lib.endswith('.a'): +- print_and_exit(("input file '%s' not not name a static library. " +- "It should start with 'lib' and end with '.a") % lib) + for sp in search_paths: + assert type(sp) is list and len(sp) == 1 + path = os.path.join(sp[0], lib) +diff --git a/libcxxabi/CMakeLists.txt b/libcxxabi/CMakeLists.txt +index 96a1c625222..a3a06df4416 100644 +--- a/libcxxabi/CMakeLists.txt ++++ b/libcxxabi/CMakeLists.txt +@@ -409,8 +409,20 @@ add_definitions(-D_LIBCPP_DISABLE_EXTERN_TEMPLATE) + # pre-C++17. + add_definitions(-D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS) + +-if (MSVC) ++if (MSVC OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") + add_definitions(-D_CRT_SECURE_NO_WARNINGS) ++ # There is a symbol dependency between libc++ and libc++abi. To break this dependency ++ # we build libc++abi as a library and then statically link that library into the ++ # libc++ dll. In order to make this work I have copied this block of compiler flags from ++ # libc++'s' CMakeLists.txt. Unless these flags are normalized link errors are emitted ++ # when trying to link the libc++ dll, for example: ++ # lld-link: error: /failifmismatch: mismatch detected for '_CRT_STDIO_ISO_WIDE_SPECIFIERS': ++ # >>> src/CMakeFiles/cxx_shared.dir/support/win32/support.cpp.obj has value 1 ++ # >>> c++abi.lib(cxa_handlers.cpp.obj) has value 0 ++ add_definitions(-D_ALLOW_MSC_VER_MISMATCH) ++ add_definitions(-D_ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH) ++ add_definitions(-D_CRTBLD) ++ add_definitions(-D_CRT_STDIO_ISO_WIDE_SPECIFIERS) + endif() + + # Define LIBCXXABI_USE_LLVM_UNWINDER for conditional compilation. +diff --git a/libcxxabi/src/cxa_guard_impl.h b/libcxxabi/src/cxa_guard_impl.h +index f6a698e23ac..a84e5e83477 100644 +--- a/libcxxabi/src/cxa_guard_impl.h ++++ b/libcxxabi/src/cxa_guard_impl.h +@@ -39,7 +39,7 @@ + + #include "__cxxabi_config.h" + #include "include/atomic_support.h" +-#include ++//#include + #if defined(__has_include) + # if __has_include() + # include +diff --git a/libunwind/src/CMakeLists.txt b/libunwind/src/CMakeLists.txt +index 8f79b1cf874..353b0452602 100644 +--- a/libunwind/src/CMakeLists.txt ++++ b/libunwind/src/CMakeLists.txt +@@ -67,6 +67,7 @@ set(LIBUNWIND_SOURCES + ${LIBUNWIND_ASM_SOURCES}) + + # Generate library list. ++if (LIBUNWIND_NO_LIBRARIES) + add_library_flags_if(LIBUNWIND_HAS_C_LIB c) + if (LIBUNWIND_USE_COMPILER_RT) + add_library_flags("${LIBUNWIND_BUILTINS_LIBRARY}") +@@ -79,6 +80,7 @@ if (LIBUNWIND_ENABLE_THREADS) + add_library_flags_if(LIBUNWIND_HAS_PTHREAD_LIB pthread) + add_compile_flags_if(LIBUNWIND_WEAK_PTHREAD_LIB -DLIBUNWIND_USE_WEAK_PTHREAD=1) + endif() ++endif() + + # Setup flags. + add_link_flags_if_supported(-nodefaultlibs) Index: wi.py =================================================================== --- /dev/null +++ wi.py @@ -0,0 +1,548 @@ +#!/usr/bin/env python3 + +# Build a windows-itanium environment: +# We require libunwind, libcxx-abi, and libcxx and the MS ucrt. +# +# This script is windows specific; however, a similar process +# could be used to cross-compile from linux. +# +# The process *should be* to first build a window-itanium clang +# and then do a stand-alone compilation of each of the +# libraries using that compiler. However, this doesn't +# work (currently) due to the lack of a suitable driver for +# windows-itanium (in the clang driver code). Instead we build +# an msvc targeting clang and then we invoke that clang but force +# the triple to windows-itanium using the -cc1 layer -triple option. +# +# This script automates a build of the required components using cmake +# and ninja. Build products are left in the source tree for simplicity. The +# result is a "wi" directory containing the toolchain binaries, include +# files and libraries. Due to the lack of a suitable windows-itanium +# driver this script creates a .bat file in the toolchain bin directory +# named "wi.bat" which assists in driving the windows-itanium compiler. +# +# usage: +# 1. Ensure pre-requisites are available as described in "pre-requisites:". +# 2. cd into git workspace that contains wi.py. +# 3. Run: "wi.py --patch" to patch the sources. +# 4. Manually fix up "locale_win32.h" (see notes in the patch). +# 5. Run "wi.py" to build. +# +# Note: The "--clean-all" option can be supplied to force a rebuild. +# +# pre-requisites: +# Git must be available on the path to allow for patching the sources +# with the --patch option. +# +# This script assumes it's run from within an llvm git checkout (tested +# using: 4cb016cd2d8467c572b2e5c5d34f376ee79e4ac1) with wi.patch +# available in the same directory. +# +# Ninja and cmake must be on your path. Tested with: +# - Ninga version v1.10.1, binaries from https://github.com/ninja-build/ninja/releases +# - cmake version 3.18.2, binary zip from https://cmake.org/download/ +# +# Note: We need a version of cmake that is high enough to have +# this: https://gitlab.kitware.com/cmake/cmake/-/issues/16439 +# +# The build needs to be able to find the installed windows sdk. I tested +# the script with an sdk installed via VS2017. The following include files +# and library locations were added when running vcvarsall.bat on my system: +# C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Tools\\MSVC\\14.16.27023\\ATLMFC\\include +# C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Tools\\MSVC\\14.16.27023\\include +# C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17763.0\\ucrt +# C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17763.0\\shared +# C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17763.0\\um +# C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17763.0\\winrt +# C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17763.0\\cppwinrt +# +# C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Tools\\MSVC\\14.16.27023\\ATLMFC\\lib\\x64 +# C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Tools\\MSVC\\14.16.27023\\lib\\x64 +# C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.17763.0\\ucrt\\x64 +# C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.17763.0\\um\\x64 +# C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Tools\\MSVC\\14.16.27023\\ATLMFC\\lib\\x64 +# C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Tools\\MSVC\\14.16.27023\\lib\\x64 +# C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Tools\\MSVC\\14.16.27023\\lib\\x86\\store\\references +# C:\\Program Files (x86)\\Windows Kits\\10\\UnionMetadata\\10.0.17763.0 +# C:\\Program Files (x86)\\Windows Kits\\10\\References\\10.0.17763.0 +# C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319 +# +# Notes on inter-dependencies: +# libunwind can be built as a dll and is not dependent on other projects. +# +# In libcxx/include/exception the "class _LIBCPP_TYPE_VIS exception_ptr" is declared. This has these three +# functions +# exception_ptr(const exception_ptr&) _NOEXCEPT; +# exception_ptr& operator=(const exception_ptr&) _NOEXCEPT; +# ~exception_ptr() _NOEXCEPT; +# +# This header is included when building stdlib_exception.cpp in libcxxabi. The definitions of the above three +# functions are in libcxx not libcxxabi. Therefore, there is a circular dependency and you get a link error when +# trying to link libcxxabi into a DLL. Maybe this works with other itanium toolchains as they would generate +# ELF output and the missing symbols would become references to be resolved later by the dynamic linker? We +# tackle this by statically linking libcxxabi into libcxx when building the libcxx DLL. +# + +import os +import shutil +import stat +import subprocess +import sys +import winreg + +curr_dir = os.getcwd() + +def do_exit(exit_code): + print('Build {}'.format('failed' if exit_code != 0 else 'succeeded')) + exit(exit_code) + +def remove_file(file): + if os.path.exists(file): + os.remove(file) + +def remove_readonly(func, path, _): + "Clear the readonly bit and reattempt the removal" + os.chmod(path, stat.S_IWRITE) + func(path) + +def remove_dir(dir): + if os.path.exists(dir): + shutil.rmtree(dir, onerror = remove_readonly) + +def run_cmd(cmds, bat_file, input=None): + with open('{}'.format(bat_file), 'w') as tf: + for cmd in cmds: + tf.write('{}\n@if %ERRORLEVEL% NEQ 0 exit %ERRORLEVEL%\n\n'.format(cmd)); + tf.write('exit %ERRORLEVEL%\n') + tf.flush() + + proc = subprocess.Popen(['cmd', '/c', tf.name], stdin = subprocess.PIPE) + proc.communicate(input.encode() if input else None) + if proc.returncode != 0: + do_exit(proc.returncode) + +vcvars_cmd_line = None +def get_vcvars_cmd(): + global vcvars_cmd_line + if not vcvars_cmd_line: + try: + with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7') as key: + path = winreg.QueryValueEx(key, r'15.0') + vcvars_cmd_line = '@call "' + path[0] + r'VC\Auxiliary\Build\vcvarsall.bat" amd64' + except: + vcvars_cmd_line = r'@echo "Warning: could not find vcvarsall.bat"' + return vcvars_cmd_line + +def patch_sources(): + run_cmd(['git apply wi.patch'], + 'patch.bat') + +build_dirs_name = 'build-wi' + +toolchain_build_path = os.path.join(curr_dir, build_dirs_name) + +install_path = os.path.join(curr_dir, 'wi') + +toolchain_bin_path = os.path.join(install_path, 'bin') + +def build_clang_and_lld(): + ''' + Build a windows-msvc (default triple) toolchain + with support for the windows-itanium triple. See comments + on the lack of a suitable windows-itanium driver elsewhere. + ''' + if not os.path.exists(toolchain_build_path): + os.mkdir(toolchain_build_path) + + cd_build_dir = '@cd {}'.format(toolchain_build_path) + + cmake_cmd = ' cmake' + cmake_cmd += ' -GNinja' + cmake_cmd += ' -DCMAKE_CXX_COMPILER=cl' + cmake_cmd += ' -DCMAKE_C_COMPILER=cl' + cmake_cmd += ' -DCMAKE_MAKE_PROGRAM=ninja' + cmake_cmd += ' -DLLVM_TARGETS_TO_BUILD:STRING=X86' + cmake_cmd += ' -DLLVM_OPTIMIZED_TABLEGEN:BOOL=ON' + cmake_cmd += ' -DLLVM_ENABLE_ASSERTIONS=ON' + cmake_cmd += ' -DLLVM_INCLUDE_EXAMPLES=OFF' + cmake_cmd += ' -DLLVM_ENABLE_TIMESTAMPS=OFF' + cmake_cmd += ' -DLLVM_VERSION_SUFFIX=' + cmake_cmd += ' -DLLVM_BUILD_RUNTIME=OFF' + cmake_cmd += ' -DLLVM_APPEND_VC_REV=OFF' + cmake_cmd += ' -DLLVM_ENABLE_PROJECTS="clang;lld"' + cmake_cmd += ' -DCMAKE_BUILD_TYPE=Release' + cmake_cmd += ' -DCLANG_ENABLE_STATIC_ANALYZER=OFF' + cmake_cmd += ' -DCLANG_ENABLE_ARCMT=OFF' + cmake_cmd += ' ../llvm' + cmake_cmd += ' -DCMAKE_INSTALL_PREFIX=\"{}\"'.format(install_path) + + run_cmd([get_vcvars_cmd(), + cd_build_dir, + cmake_cmd, + 'ninja -v', + 'ninja install'], + 'toolchain.bat') + +ar_bin = os.path.join(toolchain_bin_path, 'llvm-ar') +ranlib_bin = os.path.join(toolchain_bin_path, 'llvm-ranlib') + +unwind_build_dir = os.path.join('libunwind', build_dirs_name) + +llvm_dir = os.path.join(curr_dir, 'llvm') + +install_libs_path = os.path.join(install_path, 'lib') +install_bin_path = os.path.join(install_path, 'bin') + +# EXPLAIN: Use -Xclang -triple rather than -target (see other comments about the lack of a suitable windows-itanium driver.) +common_clang_options = ' -Xclang -triple -Xclang x86_64-unknown-windows-itanium' + +# EXPLAIN: When -windows-itanium was being actively developed lld wasn't ready to replace link.exe but now we need it to as +# link.exe doesn't like the COMDATs in the generated COFF files. +common_clang_options += ' --ld-path='.format(os.path.join(toolchain_bin_path, 'lld-link')) + +# EXPLAIN: My understanding is that we shouldn't need to set any _LIBCPP_* flags when +# building libcxxabi. Unfortunately, I found that if you don't set this then you get +# compiler errors for "missing refstring" because refstring is declared in libcxx/include/stdexcept +# but the declaration is guarded by: #ifndef _LIBCPP_ABI_VCRUNTIME and _LIBCPP_ABI_VCRUNTIME is on. This +# seemed the best way to force it off. It also seems right as it makes sense to set this for windows-itanium. +common_clang_options += ' -D_LIBCPP_ABI_FORCE_ITANIUM' + +# EXPLAIN: We rather crudely specify linker options even when doing compilation. +# suppress warnings about this. +common_clang_options += ' -Qunused-arguments' + +# EXPLAIN: Use -fsjlj-exceptions for now! +# TODO: Get DWARF zero cost exceptions working properly! +common_clang_options += ' -fsjlj-exceptions' + +clang_builtins_path = os.path.join(toolchain_build_path, 'lib', 'clang', '12.0.0', 'include') + +def build_unwind(): + if not os.path.exists(unwind_build_dir): + os.mkdir(unwind_build_dir) + + if not os.path.exists(install_path): + os.mkdir(install_path) + + cd_build_dir = '@cd {}'.format(unwind_build_dir) + + run_cmake = 'cmake' + run_cmake += ' --debug-trycompile' + run_cmake += ' -G \"Ninja\"' + run_cmake += ' -DCMAKE_MAKE_PROGRAM=ninja' + run_cmake += ' -DCMAKE_BUILD_TYPE=Release' + run_cmake += ' -DCMAKE_INSTALL_PREFIX=\"{}\"'.format(install_path) + run_cmake += ' -DCMAKE_C_COMPILER=clang' + run_cmake += ' -DCMAKE_CXX_COMPILER=clang' + run_cmake += ' -DCMAKE_CROSSCOMPILING=ON' + run_cmake += ' -DCMAKE_SYSTEM_NAME=Windows' + run_cmake += ' -DCMAKE_C_COMPILER_WORKS=ON' + run_cmake += ' -DCMAKE_CXX_COMPILER_WORKS=ON' + run_cmake += ' -DLLVM_PATH=\"{}\"'.format(llvm_dir) + run_cmake += ' -DLLVM_COMPILER_CHECKED=ON' + run_cmake += ' -DCMAKE_AR=\"{}\"'.format(ar_bin) + run_cmake += ' -DCMAKE_RANLIB=\"{}\"'.format(ranlib_bin) + run_cmake += ' -DCXX_SUPPORTS_CXX11=ON' + run_cmake += ' -DCXX_SUPPORTS_CXX_STD=ON' + run_cmake += ' -DLIBUNWIND_USE_COMPILER_RT=OFF' + run_cmake += ' -DLIBUNWIND_ENABLE_THREADS=OFF' + run_cmake += ' -DLIBUNWIND_ENABLE_SHARED=ON' + run_cmake += ' -DLIBUNWIND_ENABLE_STATIC=OFF' + run_cmake += ' -DLIBUNWIND_ENABLE_CROSS_UNWINDING=OFF' + run_cmake += ' -DLIBUNWIND_INSTALL_LIBRARY=ON' + + # EXPLAN: Adding these two libraries fixes the following error during linking: + # lld-link: warning: undefined symbol: fprintf + # >>> referenced by src/CMakeFiles/unwind_shared.dir/Unwind-sjlj.c.obj:(_Unwind_SjLj_RaiseException) + # >>> referenced 33 more times + # However it introduces this: + # lld-link: warning: duplicate symbol: _snprintf + # >>> defined at src/CMakeFiles/unwind_shared.dir/UnwindLevel1.c.obj + # >>> defined at legacy_stdio_definitions.lib(legacy_stdio_definitions.obj) + libunwind_c_flags = ' -lmsvcrt -llegacy_stdio_definitions' + + # EXPLAN: This works-around the multiply defined error messages when linking, e.g: + # lld-link: warning: duplicate symbol: sprintf + # >>> defined at src/CMakeFiles/unwind_shared.dir/UnwindLevel1.c.obj + # >>> defined at src/CMakeFiles/unwind_shared.dir/UnwindLevel1-gcc-ext.c.obj + # I have set it on the basis that the mingw build set this flag to work around COMDAT + # problems and we see the same comdat errors from lld when linking. + # TODO: work out if this is the correct thing to be doing here! + libunwind_c_flags += ' -Xlinker /force:multiple' + + run_cmake += ' -DCMAKE_CXX_FLAGS=\"-Wno-dll-attribute-on-redeclaration {} {}\"'.format(common_clang_options, libunwind_c_flags) + run_cmake += ' -DCMAKE_C_FLAGS=\"-Wno-dll-attribute-on-redeclaration {} {}\"'.format(common_clang_options, libunwind_c_flags) + + # EXPLAIN: Don't run the linker on compiler check + run_cmake += ' -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY' + + # EXPLAIN: I added this flag to the cmake files to prevent cmake adding + # the following libraries: -lc.lib -lgcc_s.lib -lgcc.lib -ldl.lib. I presume + # that building for windows is just not a thing that libunwind really supports? + # Can we upstream something similar? + run_cmake += ' -LIBUNWIND_NO_LIBRARIES' + + run_cmake += ' ..' + + run_cmd([get_vcvars_cmd(), + 'set PATH={};%PATH%'.format(toolchain_bin_path), + cd_build_dir, + run_cmake, + 'ninja -v', + 'ninja install'], + 'libunwind.bat') + + # TODO: Why doesn't "ninja install" put the dll + # into the correct install location? Currently, it + # doesn't so we manually do this here: + if not os.path.exists(install_bin_path): + os.mkdir(install_bin_path) + built_dll = os.path.join(unwind_build_dir, 'lib', 'unwind.dll') + installed_dll = os.path.join(install_bin_path, 'unwind.dll') + shutil.copyfile(built_dll, installed_dll) + +cxx_build_dir = os.path.join('libcxx', build_dirs_name) + +def build_cxx(): + if not os.path.exists(cxx_build_dir): + os.mkdir(cxx_build_dir) + + cd_build_dir = '@cd {}'.format(cxx_build_dir) + + run_cmake = 'cmake' + # EXPLAIN: --debug-trycompile leaves around the files and dirs that cmake + # uses to decide which features are supported. This process can easily go + # awry due to setting incorrect flags here and leaving this stuff around + # allows for investigation. + run_cmake += ' --debug-trycompile' + run_cmake += ' -G \"Ninja\"' + run_cmake += ' -DCMAKE_MAKE_PROGRAM=ninja' + run_cmake += ' -DCMAKE_BUILD_TYPE=Release' + run_cmake += ' -DCMAKE_INSTALL_PREFIX=\"{}\"'.format(install_path) + run_cmake += ' -DCMAKE_C_COMPILER=clang' + run_cmake += ' -DCMAKE_CXX_COMPILER=clang' + run_cmake += ' -DCMAKE_CROSSCOMPILING=ON' + run_cmake += ' -DCMAKE_SYSTEM_NAME=Windows' + run_cmake += ' -DCMAKE_C_COMPILER_WORKS=ON' + run_cmake += ' -DCMAKE_CXX_COMPILER_WORKS=ON' + run_cmake += ' -DLLVM_PATH=\"{}\"'.format(llvm_dir) + run_cmake += ' -DLLVM_COMPILER_CHECKED=ON' + run_cmake += ' -DCMAKE_AR=\"{}\"'.format(ar_bin) + run_cmake += ' -DCMAKE_RANLIB=\"{}\"'.format(ranlib_bin) + run_cmake += ' -DLIBCXX_INSTALL_HEADERS=ON' + run_cmake += ' -DLIBCXX_ENABLE_SHARED=ON' + run_cmake += ' -DLIBCXX_ENABLE_STATIC=OFF' + run_cmake += ' -DLIBCXX_ENABLE_EXCEPTIONS=ON' + + # EXPLAIN: for windows-itanium we want to use the MS supplied runtime. + run_cmake += ' -DLIBCXX_USE_COMPILER_RT=OFF' + + # EXPLAIN: turn threads off for now - hopefully simplifies the build. + # TODO: enable threads + run_cmake += ' -DLIBCXX_ENABLE_THREADS=OFF' + + # EXPLAIN: _LIBCPP_HAS_THREAD_API_WIN32 can't be set if -DLIBCXXABI_ENABLE_THREADS" is "OFF" + # run_cmake += ' -DLIBCXX_HAS_WIN32_THREAD_API=ON' + + run_cmake += ' -DLIBCXX_ENABLE_MONOTONIC_CLOCK=ON' + run_cmake += ' -DLIBCXX_SUPPORTS_STD_EQ_CXX11_FLAG=ON' + run_cmake += ' -DLIBCXX_HAVE_CXX_ATOMICS_WITHOUT_LIB=ON' + run_cmake += ' -DLIBCXX_ENABLE_EXPERIMENTAL_LIBRARY=OFF' + run_cmake += ' -DLIBCXX_ENABLE_FILESYSTEM=OFF' + run_cmake += ' -DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON' + run_cmake += ' -DLIBCXX_CXX_ABI=libcxxabi' + run_cmake += ' -DLIBCXX_CXX_ABI_INCLUDE_PATHS=../../libcxxabi/include' + run_cmake += ' -DLIBCXX_CXX_ABI_LIBRARY_PATH=../../libcxxabi/{}/lib'.format(build_dirs_name) + run_cmake += ' -DLIBCXX_LIBDIR_SUFFIX=""' + run_cmake += ' -DLIBCXX_INCLUDE_TESTS=OFF' + run_cmake += ' -DLIBCXX_ENABLE_ABI_LINKER_SCRIPT=OFF' + + # EXPLAIN: Try to remove any dependency on the VC runtime - we + # need libc++abi to supply the C++ runtime. + run_cmake += ' -DLIBCXX_NO_VCRUNTIME=ON' + + # EXPLAIN: As we are statically linking against libcxxabi we need + # to link against the unwind import library to resolve + # unwind references from the libcxxabi objects. + libcxx_c_flags = ' {}'.format(os.path.join(install_path, 'lib', 'unwind.lib')) + + # EXPLAIN: force this to be undefined to prevent the inclusion of sys/time that MS doesn't provide. + libcxx_c_flags += ' -UCLOCK_REALTIME' + + # EXPLAN: I have set it on the basis that the mingw build sets this flag to work around COMDAT + # problems and we see the same comdat errors from lld when linking. + # TODO: work out if this is the correct thing to be doing here! + libcxx_c_flags += ' -Xlinker /force:multiple' + + run_cmake += ' -DCMAKE_CXX_FLAGS=\"{} {}\"'.format(common_clang_options, libcxx_c_flags) + run_cmake += ' -DCMAKE_C_FLAGS=\"{} {}\"'.format(common_clang_options, libcxx_c_flags) + + # EXPLAIN: Don't run the linker on CMAKE compiler check. + run_cmake += ' -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY' + + run_cmake += ' ..' + + run_cmd(['set PATH={};%PATH%'.format(toolchain_bin_path), + cd_build_dir, + run_cmake, + 'ninja -v', + 'ninja install'], + 'libcxx.bat') + +cxxabi_build_dir = os.path.join('libcxxabi', build_dirs_name) + +def build_cxxabi(): + if not os.path.exists(cxxabi_build_dir): + os.mkdir(cxxabi_build_dir) + + cd_build_dir = '@cd {}'.format(cxxabi_build_dir) + + run_cmake = 'cmake' + run_cmake += ' --debug-trycompile' + run_cmake += ' -G \"Ninja\"' + run_cmake += ' -DCMAKE_MAKE_PROGRAM=ninja' + run_cmake += ' -DCMAKE_BUILD_TYPE=Release' + run_cmake += ' -DCMAKE_INSTALL_PREFIX=\"{}\"'.format(install_path) + run_cmake += ' -DCMAKE_C_COMPILER=clang' + run_cmake += ' -DCMAKE_CXX_COMPILER=clang' + run_cmake += ' -DCMAKE_CROSSCOMPILING=ON' + run_cmake += ' -DCMAKE_SYSTEM_NAME=Windows' + run_cmake += ' -DCMAKE_C_COMPILER_WORKS=ON' + run_cmake += ' -DCMAKE_CXX_COMPILER_WORKS=ON' + run_cmake += ' -DLLVM_PATH=\"{}\"'.format(llvm_dir) + run_cmake += ' -DLLVM_COMPILER_CHECKED=ON' + run_cmake += ' -DCMAKE_AR=\"{}\"'.format(ar_bin) + run_cmake += ' -DCMAKE_RANLIB=\"{}\"'.format(ranlib_bin) + run_cmake += ' -DLIBCXXABI_ENABLE_EXCEPTIONS=ON' + + # EXPLAIN: we want to use the MS CRT. + run_cmake += ' -DLIBUNWIND_USE_COMPILER_RT=OFF' + + # EXPLAIN: turn threads off - hopefully simplifies the build. + # TODO: enable threads + run_cmake += ' -DLIBCXXABI_ENABLE_THREADS=OFF' + + run_cmake += ' -DLIBCXXABI_ENABLE_SHARED=OFF' + run_cmake += ' -DLIBCXXABI_ENABLE_STATIC=ON' + + # EXPLAIN: Add visibility annotations as if building a dll. + # This options does not exist upstream - see "wi.patch" + # for details. + run_cmake += ' -LIBCXXABI_ENABLE_STATIC_FOR_LIBCXX_SHARED=ON' + + run_cmake += ' -DLIBCXXABI_LIBCXX_INCLUDES=../../libcxx/include' + run_cmake += ' -DLIBCXXABI_LIBDIR_SUFFIX=""' + run_cmake += ' -DLIBCXXABI_ENABLE_NEW_DELETE_DEFINITIONS=ON' + run_cmake += ' -DCXX_SUPPORTS_CXX_STD=ON' + + # EXPLAIN: Unless we force this off then cxxabi will contain + # references to threading symbols that will not be defined + # by libc++ e.g: + # lld-link: error: undefined symbol: std::__1::__libcpp_thread_yield() + # >>> referenced by c++abi.lib(cxa_default_handlers.cpp.obj):(std::__1::__libcpp_timed_backoff_policy::operator()(std::__1::chrono::duration >) const) + # >>> referenced by c++abi.lib(cxa_demangle.cpp.obj) + # >>> referenced by c++abi.lib(cxa_exception_storage.cpp.obj) + libcxxabi_c_flags = ' -D_LIBCPP_HAS_NO_THREADS' + + # EXPLAIN: Use -Xclang -triple rather than -target to avoid: clang: error: invalid linker name in argument '-fuse-ld=lld-link' + run_cmake += ' -DCMAKE_CXX_FLAGS=\"{} {}\"'.format(libcxxabi_c_flags, common_clang_options) + run_cmake += ' -DCMAKE_C_FLAGS=\"{} {}\"'.format(libcxxabi_c_flags, common_clang_options) + + run_cmake += ' ..' + + run_cmd(['set PATH={};%PATH%'.format(toolchain_bin_path), + cd_build_dir, + run_cmake, + 'ninja -v'], + 'libcxxabi.bat') + +def write_driver_script(): + script = get_vcvars_cmd() + script += '\nset PATH={};%PATH%\n'.format(os.path.join(install_path, 'bin')) + script += '{} ^\n'.format(os.path.join(toolchain_bin_path, 'clang.exe')) + script += ' -fuse-ld=lld-link ^\n' + script += ' -Xclang -triple -Xclang x86_64-unknown-windows-itanium ^\n' + script += ' -fsjlj-exceptions ^\n' + script += ' -isystem {} ^\n'.format(os.path.join(install_path, 'include')) + script += ' -isystem {} ^\n'.format(os.path.join(install_path, 'include', 'c++', 'v1')) + script += ' -lkernel32 ^\n' + script += ' -luser32 ^\n' + script += ' -lgdi32 ^\n' + script += ' -lwinspool ^\n' + script += ' -lshell32 ^\n' + script += ' -lole32 ^\n' + script += ' -loleaut32 ^\n' + script += ' -luuid ^\n' + script += ' -lcomdlg32 ^\n' + script += ' -ladvapi32 ^\n' + script += ' -loldnames ^\n' + script += ' {} ^\n'.format(os.path.join(install_path, 'lib', 'c++.lib')) + script += ' {} ^\n'.format(os.path.join(install_path, 'lib', 'unwind.lib')) + script += ' -D_LIBCPP_HAS_NO_THREADS ^\n' + script += ' -Wl,/nodefaultlib ^\n' + script += ' -lmsvcrt ^\n' + script += ' -lucrt ^\n' + script += ' -lvcruntime ^\n' + script += ' %*' + + with open('{}'.format(os.path.join(install_path, 'bin', 'clang-wi.bat')), 'w') as file: + file.write(script) + +def test_hello_world(): + test_path = os.path.join(install_path, 'test') + if not os.path.exists(test_path): + os.mkdir(test_path) + with open('{}'.format(os.path.join(test_path, 'hello.cpp')), 'w') as file: + file.write('#include \nint main() {std::cout << "Hello!";}') + + run_cmd(['@set PATH={};%PATH%'.format(toolchain_bin_path), + '@cd {}'.format(test_path), + 'call {} hello.cpp -o hello.exe'.format(os.path.join(install_path, 'bin', 'clang-wi.bat')), + 'hello.exe >NUL', + '@hello.exe | findstr "Hello!" || (echo "hello.exe UNSUCCESSFUL: did not print: Hello!" && exit 1)', + '@echo "hello.exe SUCCESSFUL!'], + 'test.bat') + +if '--patch' in sys.argv: + patch_sources() + exit(0) + +if '--clean-libs' in sys.argv or '--clean-all' in sys.argv: + remove_file('toolchain.bat') + remove_file('libunwind.bat') + remove_file('libcxxabi.bat') + remove_file('libcxx.bat') + remove_file('patch.bat') + remove_file('test.bat') + remove_file(os.path.join(install_libs_path, 'unwind.lib')) + remove_file(os.path.join(install_bin_path, 'unwind.dll')) + remove_file(os.path.join(install_libs_path, 'c++.lib')) + remove_file(os.path.join(install_bin_path, 'c++.dll')) + remove_dir(unwind_build_dir) + remove_dir(cxxabi_build_dir) + remove_dir(cxx_build_dir) + +if '--clean-all' in sys.argv: + remove_dir(toolchain_build_path) + remove_dir(install_path) + +if '--debug' in sys.argv: + # EXPLAIN: produce more debugging output from the linker in the log files. + common_clang_options += ' -Xlinker /verbose' + + # EXPLAIN: Log which include files were pulled in. + common_clang_options += ' -H' + + # EXPLAIN: Save the preprocessed output files next to the object files + # Note: I have seen this option cause COMDAT errors to be reported. + # It seems that source -> assembly -> obj may report comdat + # errors (when assembling) that are not reported doing source-> obj. + # TODO: Investigate these comdat errors. + common_clang_options += ' -save-temps=obj' + +build_clang_and_lld() +build_unwind() +build_cxxabi() +build_cxx() +write_driver_script() +test_hello_world() \ No newline at end of file