MingW64 Cross-Compiling with Extra Libraries

This is a bit of weird thing that might be a bug, or just me doing something
wrong.

Given the MingW64 CMake toolchain file:

set(CMAKE_SYSTEM_NAME Windows)
set(TOOLCHAIN_PREFIX x86_64-w64-mingw32)

# Cross compilers to use for C, C++ and Fortran.

# NOTE: Use -posix suffix to explicitly use the pthreads threading model. By
# default, the -win32 based threading model is used.
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc-posix)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++-posix)
set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres)

# Extra compiler and library flags.
set(CMAKE_C_FLAGS_INIT "-D_WIN32_WINNT=0x600")
set(CMAKE_CXX_FLAGS_INIT "-D_WIN32_WINNT=0x600")
set(CMAKE_C_STANDARD_LIBRARIES "-lws2_32")
set(CMAKE_CXX_STANDARD_LIBRARIES "-lws2_32")
set(CMAKE_EXE_LINKER_FLAGS_INIT "-lws2_32")
set(CMAKE_STATIC_LINKER_FLAGS_INIT "-lws2_32")
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-lws2_32")
set(CMAKE_MODULE_LINKER_FLAGS_INIT "-lws2_32")

# target environment on the build host system.
set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX})
set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_FIND_ROOT_PATH})
set(ENV{PKG_CONFIG_LIBDIR} "${CMAKE_FIND_ROOT_PATH}/usr/lib/pkgconfig:${CMAKE_FIND_ROOT_PATH}/usr/share/pkgconfig")

# Modify default behavior of FIND_XXX() commands.
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

And an example project as follows (CMakeLists.txt, main.cpp):

CMakeLists.txt:

project(CCL LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
add_executable(main main.cpp)

Main.cpp:

#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>

int main(void)
{
    WSADATA wsaData;
    WSAStartup(0x0101, &wsaData);
    WSACleanup();
    return 0;
}

This project consistently fails to build (i.e., link) with the errors:

$ mkdir -p build && cd build && cmake -DCMAKE_TOOLCHAIN_FILE=../mingw64-toolchain.cmake .. && make VERBOSE=1
# ...
[ 50%] Building CXX object CMakeFiles/main.dir/main.cpp.obj
/usr/bin/x86_64-w64-mingw32-g++-posix   -D_WIN32_WINNT=0x600 -std=gnu++17 -MD -MT CMakeFiles/main.dir/main.cpp.obj -MF CMakeFiles/main.dir/main.cpp.obj.d -o CMakeFiles/main.dir/main.cpp.obj -c /home/xaldew/Dokument/cmake-mingw64/main.cpp
[100%] Linking CXX executable main.exe
/usr/bin/cmake -E cmake_link_script CMakeFiles/main.dir/link.txt --verbose=1
/usr/bin/cmake -E rm -f CMakeFiles/main.dir/objects.a
/usr/bin/x86_64-w64-mingw32-ar qc CMakeFiles/main.dir/objects.a @CMakeFiles/main.dir/objects1.rsp
/usr/bin/x86_64-w64-mingw32-g++-posix -D_WIN32_WINNT=0x600 -lws2_32 -Wl,--whole-archive CMakeFiles/main.dir/objects.a -Wl,--no-whole-archive -o main.exe -Wl,--out-implib,libmain.dll.a -Wl,--major-image-version,0,--minor-image-version,0 @CMakeFiles/main.dir/linklibs.rsp
/usr/bin/x86_64-w64-mingw32-ld: CMakeFiles/main.dir/objects.a(main.cpp.obj):main.cpp:(.text+0x24): undefined reference to `__imp_WSAStartup'
/usr/bin/x86_64-w64-mingw32-ld: CMakeFiles/main.dir/objects.a(main.cpp.obj):main.cpp:(.text+0x2d): undefined reference to `__imp_WSACleanup'
collect2: error: ld returned 1 exit status

However, if I touch any of the CMake files (toolchain or CMakeLists.txt) after
the project was attempted to be built, the thing will build without any issues:

$ touch ../CMakeLists.txt && make VERBOSE=1
/usr/bin/cmake -S/home/xaldew/Dokument/cmake-mingw64 -B/home/xaldew/Dokument/cmake-mingw64/build --check-build-system CMakeFiles/Makefile.cmake 0
# ...
[ 50%] Linking CXX executable main.exe
/usr/bin/cmake -E cmake_link_script CMakeFiles/main.dir/link.txt --verbose=1
/usr/bin/cmake -E rm -f CMakeFiles/main.dir/objects.a
/usr/bin/x86_64-w64-mingw32-ar qc CMakeFiles/main.dir/objects.a @CMakeFiles/main.dir/objects1.rsp
/usr/bin/x86_64-w64-mingw32-g++-posix -D_WIN32_WINNT=0x600 -lws2_32 -Wl,--whole-archive CMakeFiles/main.dir/objects.a -Wl,--no-whole-archive -o main.exe -Wl,--out-implib,libmain.dll.a -Wl,--major-image-version,0,--minor-image-version,0 @CMakeFiles/main.dir/linklibs.rsp
make[2]: Lämnar katalogen ”/home/xaldew/Dokument/cmake-mingw64/build”
[100%] Built target main
make[1]: Lämnar katalogen ”/home/xaldew/Dokument/cmake-mingw64/build”
/usr/bin/cmake -E cmake_progress_start /home/xaldew/Dokument/cmake-mingw64/build/CMakeFiles 0

Looking at the command lines, it looks like the socket library (-lws2_32) is
added way too early and the symbols for it discarded before the actual object is
processed. Am I missing some CMake library variable in my toolchain that causes
this problem, or are one of the LINKER variables incorrectly being set too
early on the command line for the first run?

One way to solve this is simply to add the library dependency to the target in
the main CMakeLists.txt:

if (WIN32)
  find_library(WINSOCK2 ws2_32)
  target_link_libraries(main PUBLIC ${WINSOCK2})
endif()

I would have preferred not to do that and keep the platform specific
dependencies in the toolchain but maybe that’s not possible?