Crosscompiling toolchain is functional but cmake does not want to cooperate

Hello. I am long time CMake user but I can’t figure out how to crosscompile for windows using linux as a host using clang (version 12, built manually and placed in /opt and PATH if it matters). Without going into to much details i have copied required includes/libs from Visual Studio 2019 and Windows SDK, then on linux box ran:

clang++ main.cpp -target i386-pc-windows-msvc -fuse-ld=lld -I msvc/include/ -I sdk/ucrt/ -L msvc/lib/x86 -L sdklibs/um/x86/ -L sdklibs/ucrt/x86/

It works like a charm. The resulting executable is fully functional and tested on windows box. Switching from x86 to x64 also produces working binary, so the 32/64 toolchains are functional.

So next thing i wanted to do was to build the same main.cpp file but with help of CMake. Reading the documentation i rolled this toolchain file:

set(CMAKE_SYSTEM_NAME Windows)
set(triple i386-pc-windows-msvc)

set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_C_COMPILER clang)

set(CMAKE_EXE_LINKER_FLAGS -fuse-ld=lld)
set(CMAKE_C_COMPILER_TARGET ${triple})
set(CMAKE_CXX_COMPILER_TARGET ${triple})

include_directories(/home/tgq/msvc/include)
include_directories(/home/tgq/sdk/ucrt)
link_directories(/home/tgq/msvc/lib/x86)
link_directories(/home/tgq/sdklibs/um/x86)
link_directories(/home/tgq/sdklibs/ucrt/x86)

Invoking

cmake .. -DCMAKE_TOOLCHAIN_FILE=~/i386-pc-windows-msvc.cmake

fails with output

cmake .. -DCMAKE_TOOLCHAIN_FILE=~/i386-pc-windows-msvc.cmake
-- The C compiler identification is Clang 12.0.0
-- The CXX compiler identification is Clang 12.0.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - failed
-- Check for working C compiler: /opt/curiosity/bin/clang
-- Check for working C compiler: /opt/curiosity/bin/clang - broken
CMake Error at /opt/curiosity/share/cmake-3.18/Modules/CMakeTestCCompiler.cmake:66 (message):
  The C compiler

    "/opt/curiosity/bin/clang"

  is not able to compile a simple test program.

  It fails with the following output:

    Change Dir: /home/tgq/crosscompiling/project/build/CMakeFiles/CMakeTmp

    Run Build Command(s):/usr/bin/gmake cmTC_b7f4c/fast && /usr/bin/gmake  -f CMakeFiles/cmTC_b7f4c.dir/build.make CMakeFiles/cmTC_b7f4c.dir/build
    gmake[1]: Entering directory '/home/tgq/crosscompiling/project/build/CMakeFiles/CMakeTmp'
    Building C object CMakeFiles/cmTC_b7f4c.dir/testCCompiler.c.obj
    /opt/curiosity/bin/clang --target=i386-pc-windows-msvc  @CMakeFiles/cmTC_b7f4c.dir/includes_C.rsp -O2 -march=native -pedantic -DNDEBUG  -o CMakeFiles/cmTC_b7f4c.dir/testCCompiler.c.obj -c /home/tgq/crosscompiling/project/build/CMakeFiles/CMakeTmp/testCCompiler.c
    Linking C executable cmTC_b7f4c.exe
    /opt/curiosity/bin/cmake -E cmake_link_script CMakeFiles/cmTC_b7f4c.dir/link.txt --verbose=1
    /opt/curiosity/bin/cmake -E rm -f CMakeFiles/cmTC_b7f4c.dir/objects.a
    /opt/curiosity/bin/llvm-ar cr CMakeFiles/cmTC_b7f4c.dir/objects.a @CMakeFiles/cmTC_b7f4c.dir/objects1.rsp
    /opt/curiosity/bin/clang --target=i386-pc-windows-msvc -O2 -march=native -pedantic -DNDEBUG  -fuse-ld=lld  -Wl,--whole-archive CMakeFiles/cmTC_b7f4c.dir/objects.a -Wl,--no-whole-archive -o cmTC_b7f4c.exe -Wl,--out-implib,libcmTC_b7f4c.dll.a -Wl,--major-image-version,0,--minor-image-version,0 @CMakeFiles/cmTC_b7f4c.dir/linklibs.rsp
    lld-link: warning: ignoring unknown argument '--whole-archive'
    lld-link: warning: ignoring unknown argument '--no-whole-archive'
    lld-link: warning: ignoring unknown argument '--out-implib'
    lld-link: warning: ignoring unknown argument '--major-image-version'
    lld-link: warning: ignoring unknown argument '--minor-image-version'
    lld-link: error: could not open 'libcmTC_b7f4c.dll.a': No such file or directory
    lld-link: error: could not open '0': No such file or directory
    lld-link: error: could not open '0': No such file or directory
    clang-12: error: linker command failed with exit code 1 (use -v to see invocation)
    gmake[1]: *** [CMakeFiles/cmTC_b7f4c.dir/build.make:109: cmTC_b7f4c.exe] Error 1
    gmake[1]: Leaving directory '/home/tgq/crosscompiling/project/build/CMakeFiles/CMakeTmp'
    gmake: *** [Makefile:140: cmTC_b7f4c/fast] Error 2





  CMake will not be able to correctly generate this project.
Call Stack (most recent call first):
  CMakeLists.txt:3 (project)


-- Configuring incomplete, errors occurred!
See also "/home/tgq/crosscompiling/project/build/CMakeFiles/CMakeOutput.log".
See also "/home/tgq/crosscompiling/project/build/CMakeFiles/CMakeError.log".

What am I missing?

I can separately compile and link any .cpp file into working binary. Why does cmake add some uncontrollable linker flags (the -Wl, things)?? I can’t override anything with “CMAKE_CXX_LINK_EXECUTABLE” in my toolchain file. CMake is still using bad command line to link.
Below i present my working, manual way of building the file that i want CMake to do for me project-wide. What is the way to achieve equivalent of these?

  1. To compile:
    clang++ --target=i386-pc-windows-msvc -c main.cpp -o main.o -I msvc/include/ -I sdk/ucrt/
  2. To link:
    clang++ --target=i386-pc-windows-msvc -fuse-ld=lld main.o -o a.exe -L ~/sdklibs/um/x86/ -L ~/sdklibs/ucrt/x86/ -L ~/msvc/lib/x86/

Result:

$ file a.exe
a.exe: PE32 executable (console) Intel 80386, for MS Windows

I don’t use clang so I can’t really comment but your project is using both C and CXX languages and it’s failing the part for C. If you aren’t using C or you haven’t verified that it works with your setup then the project() command probably should specify CXX as the only language you want to use. The --debug-trycompile option to CMake may also be useful so you can inspect the project files CMake generates at the Detecting C compiler ABI info step.

It’s the compiler detection that fails. It does not even get to the point of project() call.

I’ve only seen it perform compiler detection for the enabled languages. The defaults being both C and CXX.

Yes… And as you can see it FAILS. But the toolchain is fully functional. I am unable to customize the linking process and that’s the problem…

The point I was making is in your post you only showed that CXX (clang++) worked not C (clang). You haven’t showed the toolchain being fully functional.

Anyways, the variable you want to look at is CMAKE_EXE_LINKER_FLAGS_INIT not CMAKE_EXE_LINKER_FLAGS. You’ll have to delete the build tree when making this change to your toolchain. Refer to the documentation on the difference between the two variables.

Again I highly suggest that you use the --debug-trycompile and inspect the project that gets created and check the CMakeLists.txt looks correct to you.

Indeed, but i just checked and C toolchain is working. Setting CMAKE_EXE_LINKER_FLAGS_INIT does not change the linker command line at all. --debug-trycompile added to first cmake invocation does not print any additional info, just the same as in first post. And of course i cant trycompile the projects since the configuratio nstep failed.
All i need is to speficy the linker line (because compiling is successful) and force cmake not to add any garbage lines to it. Cross clang’s lld linker seems not to understand the flags that are added without my knowledge

   lld-link: warning: ignoring unknown argument '--whole-archive'
    lld-link: warning: ignoring unknown argument '--no-whole-archive'
    lld-link: warning: ignoring unknown argument '--out-implib'
    lld-link: warning: ignoring unknown argument '--major-image-version'
    lld-link: warning: ignoring unknown argument '--minor-image-version'

How do i get rid of these?? Without them linking is succesful!!

i even went to CMakeFiles/CMakeTmp/ where testCCompiler.c is located and again - i built it succesfully with the compile and link commands…
Compile:
tgq@qq:~/crosscompiling/project/build/CMakeFiles/CMakeTmp$ clang -c testCCompiler.c --target=i386-pc-windows-msvc -I ~/msvc/include/ -I ~/sdk/ucrt/
Link:
tgq@qq:~/crosscompiling/project/build/CMakeFiles/CMakeTmp$ clang testCCompiler.o --target=i386-pc-windows-msvc -fuse-ld=lld -L ~/msvc/lib/x86 -L ~/sdklibs/um/x86/ -L ~/sdklibs/ucrt/x86/ tgq@qq:~/crosscompiling/project/build/CMakeFiles/CMakeTmp$ file a.exe a.exe: PE32 executable (console) Intel 80386, for MS Windows

The steps you are failing at are essentially part of an automatic try_compile(). Using the --debug-trycompile prevents the files from automatically being deleted.

These flags appear to be coming from Windows-GNU.cmake which is included when building for the Windows platform. It seems to be tailored to make Windows tools run correctly on Windows hosts. You’ll also see examples of CMAKE_<LANG>_LINK_EXECUTABLE rule variable if you need to alter it for your cross compiler.

You may want to change set(CMAKE_SYSTEM_NAME Windows) to set(CMAKE_SYSTEM_NAME Generic). But then you will have to fill in the gaps of missing compiler / linker flags that are normally set by the platform module.

I have changed set(CMAKE_SYSTEM_NAME Windows) to set(CMAKE_SYSTEM_NAME Generic)
AND IT SUCCESFULLY BUILT!!!

I cant believe i spent last 2 days on it and it was just about that… At least i learned ar, ranlib and all other tiny steps of compilation… Unbelievable!!! Thank you so much.

There is only one small issue remaining. I ran the cross toolchain on one of my bigger projects and it works fine except for linking shared libraries (.dll files):
clang-12: error: unsupported option '-fPIC' for target 'i386-pc-windows-msvc'
How do i make that work?

You can disable Position Independent Code (PIC) by setting the corresponding property on your target(s). See https://cmake.org/cmake/help/latest/prop_tgt/POSITION_INDEPENDENT_CODE.html.