Search code examples
ccmakeclangllvmmingw-w64

CMake: How to compile C on Windows with clang and lld with MinGW-w64 runtime


I'm building a LLVM environment on Windows that can work without Microsoft compiler and SDK and without GCC compiler or GNU linker, only LLVM (the official build) and MinGW-w64 runtime. When compiling this C program with clang compiler and lld linker, and with x86_64-windows-gnu target (MinGW-w64 runtime), it works:

> type c1.c
#include <stdio.h>

int main() {
    printf("hello 1\n");
    return 0;
}

> clang c1.c -target x86_64-windows-gnu -fuse-ld=lld -o c1.exe

> c1
hello 1

How to achieve the same with CMake? Is there a high level option for CMake to set these flags automatically? Or, when trying to set these flags manually (with CMAKE_EXE_LINKER_FLAGS or CMAKE_C_FLAGS) looks like that it confuses CMake and it still passes some Visual C++ style flags (like /subsystem:console) during linking:

> type CMakeLists.txt
project (P1 C)
cmake_minimum_required(VERSION 3.0)
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -target x86_64-windows-gnu -fuse-ld=lld")
add_executable (c1 c1.c)

> cmake -G Ninja -B build -DCMAKE_C_COMPILER=clang
-- The C compiler identification is Clang 12.0.0 with GNU-like command-line
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files/LLVM/bin/clang.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: C:/build

> cmake --build build
[2/2] Linking C executable c1.exe
FAILED: c1.exe
cmd.exe /C "cd . && C:\PROGRA~1\LLVM\bin\clang.exe -fuse-ld=lld-link -nostartfiles -nostdlib -g -Xclang -gcodeview -O0 -D_DEBUG -D_DLL -D_MT -Xclang --dependent-lib=msvcrtd -target x86_64-windows-gnu -fuse-ld=lld  -Xlinker /subsystem:console CMakeFiles/c1.dir/c1.c.obj -o c1.exe -Xlinker /implib:c1.lib -Xlinker /pdb:c1.pdb -Xlinker /version:0.0   -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 -loldnames && cd ."
lld: error: unable to find library -loldnames
clang: error: linker command failed with 

Solution

  • Please see the documentation on toolchains here: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-using-clang

    Since you're overriding the default Clang target, CMake needs to know about this during compiler detection. By the time you're in the CMakeLists.txt, it is too late. The following clang-x86_64_windows_gnu.cmake toolchain file might work (I can't test it without replicating your environment):

    set(CMAKE_C_COMPILER clang)
    set(CMAKE_C_COMPILER_TARGET x86_64-windows-gnu)
    
    set(CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld")
    set(CMAKE_MODULE_LINKER_FLAGS_INIT "-fuse-ld=lld")
    set(CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld")
    

    Then your CMakeLists.txt was wrong, it should be:

    cmake_minimum_required(VERSION 3.20)
    project(P1 LANGUAGES C)
    
    add_executable(c1 c1.c)
    

    Notice the order of the cmake_minimum_required and project commands. They should always be the first two lines in your CMakeLists.txt, in that order.