Using lld-link with MSVC compiler on Windows

I’ve been struggling for a while to get a clean way to be able to link with a separate linker on windows…
I have huge executables linking and on similar projects using meson (where it is very easy to specify the linker), using msvc’s linking can take over 10 minutes, and lld-link takes seconds.

From my searching around, I found a post on stackoverflow that sets CMAKE_LINKER as well as the CMAKE_CXX_LINKER_EXECUTABLE lines, like so:

set(CMAKE_LINKER lld-link)
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_LINKER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")

message("Setting linker to ${CMAKE_LINKER}")
message(${CMAKE_CXX_LINK_EXECUTABLE})

While this approach does seem to work, it needs a lot of extra work on the user side, and honestly it really seems like a really messy way to set the link command.

From digging around, I’ve found that CMake uses the compiler as the linker, and while that works for compilers that allow you to change linkers like gcc via -fuse-ld, I cannot find a similar option for MSVC.

Is there a reason CMake uses the compiler, instead of using CMAKE_LINKER and initializing it to the linker the compiler defines if it is not specified?

What is the correct way to do this? Surely I can’t be the only person who wants to swap to a faster linker on windows?

After some work, I’ve managed to get this kind of working, but I’m running into strange issues.

First of all, I’ve managed to fix the syntax with the following:

set(CMAKE_LINKER lld-link CACHE STRING "")
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES> /out:<TARGET>" CACHE STRING "")

I’m running into issues where the link executable line is getting constantly overridden however…

If I place the lines in the top level CMakeLists.txt for the parent project, it links properly with lld-link, however any subprojects still build with vs_link_exe!

If I place the lines in a toolchain file, and pass it via -DCMAKE_TOOLCHAIN_FILE, the linker exe gets set for all of the compiler checks, and then is overridden AGAIN for the projects.

Here is an example:
Toolchain file:

# set lld link as our linker

set(CMAKE_LINKER lld-link CACHE STRING "")
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES> /out:<TARGET>" CACHE STRING "")

message("ToolChain CMAKE_CXX_LINK_EXECUTABLE: ${CMAKE_CXX_LINK_EXECUTABLE}")

Top level project file:

cmake_minimum_required(VERSION 3.23.0)

project(
  King
  VERSION 1.0.0
  LANGUAGES C CXX)

message(STATUS "Setting linker to ${CMAKE_LINKER}")
#if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND CMAKE_LINKER MATCHES ".*lld-link.*")
#  set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES> /out:<TARGET>")
#endif()

message("Project CMAKE_CXX_LINK_EXECUTABLE: ${CMAKE_CXX_LINK_EXECUTABLE}")

Running initial CMake configuration results in the following ( I am using an MSVC developer prompt):

 cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_TOOLCHAIN_FILE="D:/Projects/king/cmake/toolchains/msvc-lld-link.cmake" -G Ninja "D:\Projects\king"
ToolChain CMAKE_CXX_LINK_EXECUTABLE: <CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES> /out:<TARGET>
ToolChain CMAKE_CXX_LINK_EXECUTABLE: <CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES> /out:<TARGET>
-- The C compiler identification is MSVC 19.31.31104.0
-- The CXX compiler identification is MSVC 19.31.31104.0
-- Detecting C compiler ABI info
ToolChain CMAKE_CXX_LINK_EXECUTABLE: <CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES> /out:<TARGET>
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.31.31103/bin/Hostx64/x64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
ToolChain CMAKE_CXX_LINK_EXECUTABLE: <CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES> /out:<TARGET>
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.31.31103/bin/Hostx64/x64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Setting linker to C:/Program Files/LLVM/bin/lld-link.exe
Project CMAKE_CXX_LINK_EXECUTABLE: <CMAKE_COMMAND> -E vs_link_exe --intdir=<OBJECT_DIR> --rc=<CMAKE_RC_COMPILER> --mt=<CMAKE_MT> --manifests <MANIFESTS> -- <CMAKE_LINKER> /nologo <OBJECTS>  /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES>
-- Looking for pthread.h
ToolChain CMAKE_CXX_LINK_EXECUTABLE: <CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES> /out:<TARGET>
-- Looking for pthread.h - not found
-- Found Threads: TRUE
-- Looking for C++ include mntent.h
ToolChain CMAKE_CXX_LINK_EXECUTABLE: <CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES> /out:<TARGET>
-- Configuring done
-- Generating done
-- Build files have been written to: D:/Projects/king/build/test

Note that the project CMAKE_CXX_LINK_EXECUTABLE gets reset back to the compiler detected one…

I guess it’s not respecting the fact that it has already been set and it is getting overridden? I am not sure what else I can do here.