1 # Cross toolchain configuration for using clang-cl on non-Windows hosts to
6 # -DCMAKE_TOOLCHAIN_FILE=/path/to/this/file
7 # -DHOST_ARCH=[aarch64|arm64|armv7|arm|i686|x86|x86_64|x64]
8 # -DLLVM_NATIVE_TOOLCHAIN=/path/to/llvm/installation
9 # -DLLVM_WINSYSROOT=/path/to/win/sysroot
10 # -DMSVC_VER=vc tools version folder name
11 # -DWINSDK_VER=windows sdk version folder name
14 # The architecture to build for.
16 # LLVM_NATIVE_TOOLCHAIN:
17 # *Absolute path* to a folder containing the toolchain which will be used to
18 # build. At a minimum, this folder should have a bin directory with a
19 # copy of clang-cl, clang, clang++, and lld-link, as well as a lib directory
20 # containing clang's system resource directory.
22 # MSVC_VER/WINSDK_VER:
23 # (Optional) if not specified, highest version number is used if any.
25 # LLVM_WINSYSROOT and MSVC_VER work together to define a folder layout that
26 # containing MSVC headers and system libraries. The layout of the folder
27 # matches that which is intalled by MSVC 2017 on Windows, and should look like
30 # ${LLVM_WINSYSROOT}/VC/Tools/MSVC/${MSVC_VER}/
45 # For versions of MSVC < 2017, or where you have a hermetic toolchain in a
46 # custom format, you must use symlinks or restructure it to look like the above.
48 # LLVM_WINSYSROOT and WINSDK_VER work together to define a folder layout that
49 # matches that of the Windows SDK installation on a standard Windows machine.
50 # It should match the layout described below.
52 # Note that if you install Windows SDK to a windows machine and simply copy the
53 # files, it will already be in the correct layout.
55 # ${LLVM_WINSYSROOT}/Windows Kits/10/
76 # IMPORTANT: In order for this to work, you will need a valid copy of the Windows
77 # SDK and C++ STL headers and libraries on your host. Additionally, since the
78 # Windows libraries and headers are not case-correct, this toolchain file sets
79 # up a VFS overlay for the SDK headers and case-correcting symlinks for the
80 # libraries when running on a case-sensitive filesystem.
84 # When configuring CMake with a toolchain file against a top-level CMakeLists.txt,
85 # it will actually run CMake many times, once for each small test program used to
86 # determine what features a compiler supports. By default, none of these
87 # invocations share a CMakeCache.txt with the top-level invocation, meaning they
88 # won't see the value of any arguments the user passed via -D. Since these are
89 # necessary to properly configure MSVC in both the top-level configuration as well as
90 # all feature-test invocations, we include them in CMAKE_TRY_COMPILE_PLATFORM_VARIABLES,
91 # so that they get inherited by child invocations.
92 list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
98 winsdk_lib_symlinks_dir
99 winsdk_vfs_overlay_path
102 function(generate_winsdk_vfs_overlay winsdk_include_dir output_path)
104 file(GLOB_RECURSE entries LIST_DIRECTORIES true "${winsdk_include_dir}/*")
105 foreach(entry ${entries})
106 if(IS_DIRECTORY "${entry}")
107 list(APPEND include_dirs "${entry}")
111 file(WRITE "${output_path}" "version: 0\n")
112 file(APPEND "${output_path}" "case-sensitive: false\n")
113 file(APPEND "${output_path}" "roots:\n")
115 foreach(dir ${include_dirs})
116 file(GLOB headers RELATIVE "${dir}" "${dir}/*.h")
121 file(APPEND "${output_path}" " - name: \"${dir}\"\n")
122 file(APPEND "${output_path}" " type: directory\n")
123 file(APPEND "${output_path}" " contents:\n")
125 foreach(header ${headers})
126 file(APPEND "${output_path}" " - name: \"${header}\"\n")
127 file(APPEND "${output_path}" " type: file\n")
128 file(APPEND "${output_path}" " external-contents: \"${dir}/${header}\"\n")
133 function(generate_winsdk_lib_symlinks winsdk_um_lib_dir output_dir)
134 execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${output_dir}")
135 file(GLOB libraries RELATIVE "${winsdk_um_lib_dir}" "${winsdk_um_lib_dir}/*")
136 foreach(library ${libraries})
137 string(TOLOWER "${library}" all_lowercase_symlink_name)
138 if(NOT library STREQUAL all_lowercase_symlink_name)
139 execute_process(COMMAND "${CMAKE_COMMAND}"
141 "${winsdk_um_lib_dir}/${library}"
142 "${output_dir}/${all_lowercase_symlink_name}")
145 get_filename_component(name_we "${library}" NAME_WE)
146 get_filename_component(ext "${library}" EXT)
147 string(TOLOWER "${ext}" lowercase_ext)
148 set(lowercase_ext_symlink_name "${name_we}${lowercase_ext}")
149 if(NOT library STREQUAL lowercase_ext_symlink_name AND
150 NOT all_lowercase_symlink_name STREQUAL lowercase_ext_symlink_name)
151 execute_process(COMMAND "${CMAKE_COMMAND}"
153 "${winsdk_um_lib_dir}/${library}"
154 "${output_dir}/${lowercase_ext_symlink_name}")
159 function(get_highest_version the_dir the_ver)
160 file(GLOB entries LIST_DIRECTORIES true RELATIVE "${the_dir}" "${the_dir}/[0-9.]*")
161 foreach(entry ${entries})
162 if(IS_DIRECTORY "${the_dir}/${entry}")
163 set(${the_ver} "${entry}" PARENT_SCOPE)
168 set(CMAKE_SYSTEM_NAME Windows)
169 set(CMAKE_SYSTEM_VERSION 10.0)
170 set(CMAKE_SYSTEM_PROCESSOR AMD64)
173 set(HOST_ARCH x86_64)
175 if(HOST_ARCH STREQUAL "aarch64" OR HOST_ARCH STREQUAL "arm64")
176 set(TRIPLE_ARCH "aarch64")
177 set(WINSDK_ARCH "arm64")
178 elseif(HOST_ARCH STREQUAL "armv7" OR HOST_ARCH STREQUAL "arm")
179 set(TRIPLE_ARCH "armv7")
180 set(WINSDK_ARCH "arm")
181 elseif(HOST_ARCH STREQUAL "i686" OR HOST_ARCH STREQUAL "x86")
182 set(TRIPLE_ARCH "i686")
183 set(WINSDK_ARCH "x86")
184 elseif(HOST_ARCH STREQUAL "x86_64" OR HOST_ARCH STREQUAL "x64")
185 set(TRIPLE_ARCH "x86_64")
186 set(WINSDK_ARCH "x64")
188 message(SEND_ERROR "Unknown host architecture ${HOST_ARCH}. Must be aarch64 (or arm64), armv7 (or arm), i686 (or x86), or x86_64 (or x64).")
191 # Do some sanity checking to make sure we can find a native toolchain and
192 # that the Windows SDK / MSVC STL directories look kosher.
193 if(NOT EXISTS "${LLVM_NATIVE_TOOLCHAIN}/bin/clang-cl" OR
194 NOT EXISTS "${LLVM_NATIVE_TOOLCHAIN}/bin/lld-link")
196 "LLVM_NATIVE_TOOLCHAIN folder '${LLVM_NATIVE_TOOLCHAIN}' does not "
197 "point to a valid directory containing bin/clang-cl and bin/lld-link "
202 get_highest_version("${LLVM_WINSYSROOT}/VC/Tools/MSVC" MSVC_VER)
206 get_highest_version("${LLVM_WINSYSROOT}/Windows Kits/10/Include" WINSDK_VER)
209 if (NOT LLVM_WINSYSROOT OR NOT MSVC_VER OR NOT WINSDK_VER)
211 "Must specify CMake variable LLVM_WINSYSROOT, MSVC_VER and WINSDK_VER")
214 set(ATLMFC_LIB "${LLVM_WINSYSROOT}/VC/Tools/MSVC/${MSVC_VER}/atlmfc/lib")
215 set(MSVC_INCLUDE "${LLVM_WINSYSROOT}/VC/Tools/MSVC/${MSVC_VER}/include")
216 set(MSVC_LIB "${LLVM_WINSYSROOT}/VC/Tools/MSVC/${MSVC_VER}/lib")
217 set(WINSDK_INCLUDE "${LLVM_WINSYSROOT}/Windows Kits/10/Include/${WINSDK_VER}")
218 set(WINSDK_LIB "${LLVM_WINSYSROOT}/Windows Kits/10/Lib/${WINSDK_VER}")
220 if (NOT EXISTS "${MSVC_INCLUDE}" OR NOT EXISTS "${MSVC_LIB}")
222 "CMake variable LLVM_WINSYSROOT and MSVC_VER must point to a folder "
223 "containing MSVC system headers and libraries")
226 if(NOT EXISTS "${WINSDK_INCLUDE}" OR NOT EXISTS "${WINSDK_LIB}")
228 "CMake variable LLVM_WINSYSROOT and WINSDK_VER must resolve to a "
229 "valid Windows SDK installation")
232 if(NOT EXISTS "${WINSDK_INCLUDE}/um/Windows.h")
233 message(SEND_ERROR "Cannot find Windows.h")
235 if(NOT EXISTS "${WINSDK_INCLUDE}/um/WINDOWS.H")
236 set(case_sensitive_filesystem TRUE)
239 set(CMAKE_C_COMPILER "${LLVM_NATIVE_TOOLCHAIN}/bin/clang-cl" CACHE FILEPATH "")
240 set(CMAKE_CXX_COMPILER "${LLVM_NATIVE_TOOLCHAIN}/bin/clang-cl" CACHE FILEPATH "")
241 set(CMAKE_LINKER "${LLVM_NATIVE_TOOLCHAIN}/bin/lld-link" CACHE FILEPATH "")
242 set(CMAKE_AR "${LLVM_NATIVE_TOOLCHAIN}/bin/llvm-lib" CACHE FILEPATH "")
244 # Even though we're cross-compiling, we need some native tools (e.g. llvm-tblgen), and those
245 # native tools have to be built before we can start doing the cross-build. LLVM supports
246 # a CROSS_TOOLCHAIN_FLAGS_NATIVE argument which consists of a list of flags to pass to CMake
247 # when configuring the NATIVE portion of the cross-build. By default we construct this so
248 # that it points to the tools in the same location as the native clang-cl that we're using.
249 list(APPEND _CTF_NATIVE_DEFAULT "-DCMAKE_ASM_COMPILER=${LLVM_NATIVE_TOOLCHAIN}/bin/clang")
250 list(APPEND _CTF_NATIVE_DEFAULT "-DCMAKE_C_COMPILER=${LLVM_NATIVE_TOOLCHAIN}/bin/clang")
251 list(APPEND _CTF_NATIVE_DEFAULT "-DCMAKE_CXX_COMPILER=${LLVM_NATIVE_TOOLCHAIN}/bin/clang++")
253 # These flags are used during build time. So if CFLAGS/CXXFLAGS/LDFLAGS is set
254 # for the target, makes sure these are unset during build time.
255 set(CROSS_TOOLCHAIN_FLAGS_NATIVE "${_CTF_NATIVE_DEFAULT}" CACHE STRING "")
258 -D_CRT_SECURE_NO_WARNINGS
259 --target=${TRIPLE_ARCH}-windows-msvc
260 -fms-compatibility-version=19.27
261 -vctoolsversion ${MSVC_VER}
262 -winsdkversion ${WINSDK_VER}
263 -winsysroot ${LLVM_WINSYSROOT})
265 if(case_sensitive_filesystem)
266 # Ensure all sub-configures use the top-level VFS overlay instead of generating their own.
267 if(NOT winsdk_vfs_overlay_path)
268 set(winsdk_vfs_overlay_path "${CMAKE_BINARY_DIR}/winsdk_vfs_overlay.yaml")
269 generate_winsdk_vfs_overlay("${WINSDK_INCLUDE}" "${winsdk_vfs_overlay_path}")
271 list(APPEND COMPILE_FLAGS
272 -Xclang -ivfsoverlay -Xclang "${winsdk_vfs_overlay_path}")
275 string(REPLACE ";" " " COMPILE_FLAGS "${COMPILE_FLAGS}")
276 string(APPEND CMAKE_C_FLAGS_INIT " ${COMPILE_FLAGS}")
277 string(APPEND CMAKE_CXX_FLAGS_INIT " ${COMPILE_FLAGS}")
280 # Prevent CMake from attempting to invoke mt.exe. It only recognizes the slashed form and not the dashed form.
283 -libpath:"${ATLMFC_LIB}/${WINSDK_ARCH}"
284 -libpath:"${MSVC_LIB}/${WINSDK_ARCH}"
285 -libpath:"${WINSDK_LIB}/ucrt/${WINSDK_ARCH}"
286 -libpath:"${WINSDK_LIB}/um/${WINSDK_ARCH}")
288 if(case_sensitive_filesystem)
289 # Ensure all sub-configures use the top-level symlinks dir instead of generating their own.
290 if(NOT winsdk_lib_symlinks_dir)
291 set(winsdk_lib_symlinks_dir "${CMAKE_BINARY_DIR}/winsdk_lib_symlinks")
292 generate_winsdk_lib_symlinks("${WINSDK_LIB}/um/${WINSDK_ARCH}" "${winsdk_lib_symlinks_dir}")
294 list(APPEND LINK_FLAGS
295 -libpath:"${winsdk_lib_symlinks_dir}")
298 string(REPLACE ";" " " LINK_FLAGS "${LINK_FLAGS}")
299 string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT " ${LINK_FLAGS}")
300 string(APPEND CMAKE_MODULE_LINKER_FLAGS_INIT " ${LINK_FLAGS}")
301 string(APPEND CMAKE_SHARED_LINKER_FLAGS_INIT " ${LINK_FLAGS}")
303 # CMake populates these with a bunch of unnecessary libraries, which requires
304 # extra case-correcting symlinks and what not. Instead, let projects explicitly
305 # control which libraries they require.
306 set(CMAKE_C_STANDARD_LIBRARIES "" CACHE STRING "" FORCE)
307 set(CMAKE_CXX_STANDARD_LIBRARIES "" CACHE STRING "" FORCE)
309 # Allow clang-cl to work with macOS paths.
310 set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_CURRENT_LIST_DIR}/ClangClCMakeCompileRules.cmake")