CMake Linux cross-compilation with (custom?) Clang toolchain on Windows host system

Hi all,

I’m looking to get my CMake project set up to cross-compile a library for Linux from a Windows host. Because what I’m compiling has to be compatible with a specific version of the Unreal Engine, I want to use the same version of the toolchain that Epic requires for cross-compiling Unreal Engine itself. The toolchains are listed on this page, and I’m currently using v17 for UE 4.26.

So far, I have set up a CMake toolchain file (below) to make use of the Clang toolchain. LINUX_MULTIARCH_ROOT is an environment variable that is set up when the toolchain is installed.

if("$ENV{LINUX_MULTIARCH_ROOT}" STREQUAL "")

    message(FATAL_ERROR "Provide the path to the Clang toolchain in the LINUX_MULTIARCH_ROOT environment variable.")

endif()

# Clang target triple
set(TARGET_TRIPLE x86_64-unknown-linux-gnu)

# Clean path separators
file(TO_CMAKE_PATH $ENV{LINUX_MULTIARCH_ROOT}/${TARGET_TRIPLE} TOOLCHAIN_ROOT)

list(APPEND CMAKE_PROGRAM_PATH ${TOOLCHAIN_ROOT}/bin)

set(CMAKE_CROSSCOMPILING TRUE)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR x86_64)

# specify the cross compiler
set(CMAKE_C_COMPILER_TARGET ${TARGET_TRIPLE})
set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER_TARGET ${TARGET_TRIPLE})
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_ASM_COMPILER_TARGET ${TARGET_TRIPLE})
set(CMAKE_ASM_COMPILER clang)

# C/C++ toolchain
set(CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN ${TOOLCHAIN_ROOT})
set(CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN ${TOOLCHAIN_ROOT})

# This must be set or compiler checks fail when linking
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)

set(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN_ROOT})
set(CMAKE_SYSROOT ${TOOLCHAIN_ROOT})

This seems to detect the compilers OK, but fails when CMake attempts to find other packages that I require. The Threads package is unable to be found using this toolchain, even though I can verify that libpthread.so/.a exist on disk.

My suspicion is that the Threads package is not found because CMake is expecting to find a package description file, rather than just the libraries and headers. As far as I know this is not provided with the Clang toolchain distribution. I don’t know where the toolchain specifically comes from - it could even be one that Epic maintains and builds in-house specifically for UE - but I would have thought that libraries could be found within this toolchain in a similar way to how I’d assume CMake would find Clang-related packages in existing toolchains.

Is there anything I can do here to properly locate the required packages, outside of writing package scripts myself?

You can use the --debug-find flag to CMake to get some information about where it is looking for things. --trace-expand may help with untangling the logic inside of FindThreads to see if it’s going down some wrong codepath as well.

I’ve tried --trace-expand and am slightly confused about how CMake is going about finding the threads library.

It seems to be to do with the CMAKE_HAVE_PTHREAD_H variable, and CheckIncludeFile.cmake first checks whether this variable already exists, and only looks for the header if the variable does not already exist. However, the trace is reporting that the variable does exist, and it is in my CMakeCache.txt as CMAKE_HAVE_PTHREAD_H:INTERNAL=, but I cannot see where it’s actually come from, since it’s not even mentioned in the trace output until CheckIncludeFile.cmake looks for it.

OK, I think I’ve found the cause of the problem: I was setting CMAKE_CXX_COMPILER_TARGET and CMAKE_SYSROOT, but CMake doesn’t actually seem to translate these into compiler flags automatically, which I thought it did. I added the following to the end of my toolchain file:

add_compile_options(--target=${CMAKE_CXX_COMPILER_TARGET} --sysroot=${CMAKE_SYSROOT})
add_link_options(--target=${CMAKE_CXX_COMPILER_TARGET} --sysroot=${CMAKE_SYSROOT})

This now seems to detect the threading library properly.

EDIT: However, when using the threading library, linking doesn’t work correctly if I link against ${CMAKE_THREAD_LIBS_INIT} (which is empty) - I have to explicitly add “pthreads” as a link target. Is this supposed to be the case?