Cross-compiling fails; recursive cmake discards options

I’m trying to cross-compile my JUCE audio plugin.
I’m using an x86_64 Linux Docker container to target Win32 x86_64, i686, arm64 architectures.

(and if that’s not bad enough: I’m doing this from an M1 Mac! :slight_smile:)

I’m failing at an early hurdle, building juceaide.

You can reproduce this with the x86_64 Linux Docker image mstorsjo/llvm-mingw:

docker run -it --rm --name win32-xcompile mstorsjo/llvm-mingw
# now you're in the container
git clone --branch 6.1.4 --depth 1 https://github.com/juce-framework/JUCE.git
cd JUCE

TOOLCHAIN=x86_64
TOOLCHAIN_FILE="${TOOLCHAIN}_toolchain.cmake"
REPO=clang64

cat <<EOF >"$TOOLCHAIN_FILE"
# the name of the target operating system
set(CMAKE_SYSTEM_NAME Windows)

# which compilers to use for C and C++
set(CMAKE_C_COMPILER   $TOOLCHAIN-w64-mingw32-clang)
set(CMAKE_CXX_COMPILER $TOOLCHAIN-w64-mingw32-clang++)

# where is the target environment located
set(CMAKE_FIND_ROOT_PATH  /$REPO)

# adjust the default behavior of the FIND_XXX() commands:
# search programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)

# search headers and libraries in the target environment
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
EOF

BUILD="build_$TOOLCHAIN"

# add (high on our PATH) a few more aliases to our cross-compile tools,
# to avoid accidentally getting a native tool from elsewhere on our path
ln -sf "/opt/llvm-mingw/bin/${TOOLCHAIN}-w64-mingw32-clang++" /opt/llvm-mingw/bin/c++
ln -sf "/opt/llvm-mingw/bin/${TOOLCHAIN}-w64-mingw32-clang" /opt/llvm-mingw/bin/cc
declare -a TOOLS=(addr2line ar ld nm objcopy objdump ranlib readelf strip)
for TOOL in ${TOOLS[@]}; do
  ln -sf "/opt/llvm-mingw/bin/${TOOLCHAIN}-w64-mingw32-${TOOL}" "/opt/llvm-mingw/bin/$TOOL"
done

VERBOSE=1 PKG_CONFIG_PATH="/$REPO/lib/pkgconfig" cmake -B"$BUILD" \
-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \
-DCMAKE_INSTALL_PREFIX="/$REPO" \
-DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" \
-DCMAKE_BUILD_TYPE=Debug
# we don't get this far:
# cmake --build "$BUILD" --target install

The verbose output I get is:

root@8c832a516d54:/build/JUCE# VERBOSE=1 PKG_CONFIG_PATH="/$REPO/lib/pkgconfig" cmake -B"$BUILD" \
> -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \
> -DCMAKE_INSTALL_PREFIX="/$REPO" \
> -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" \
> -DCMAKE_BUILD_TYPE=Debug
Re-run cmake no build system arguments
-- The C compiler identification is Clang 13.0.0
-- The CXX compiler identification is Clang 13.0.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /opt/llvm-mingw/bin/x86_64-w64-mingw32-clang - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /opt/llvm-mingw/bin/x86_64-w64-mingw32-clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring juceaide
-- Building juceaide
CMake Error at extras/Build/juceaide/CMakeLists.txt:89 (message):
  Failed to build juceaide

  /opt/cmake/bin/cmake -P
  /build/JUCE/build_x86_64/tools/CMakeFiles/VerifyGlobs.cmake

  /opt/cmake/bin/cmake -S/build/JUCE -B/build/JUCE/build_x86_64/tools
  --check-build-system CMakeFiles/Makefile.cmake 0

  /opt/cmake/bin/cmake -E cmake_progress_start
  /build/JUCE/build_x86_64/tools/CMakeFiles
  /build/JUCE/build_x86_64/tools//CMakeFiles/progress.marks

  /usr/bin/make -f CMakeFiles/Makefile2 all

  make[1]: Entering directory '/build/JUCE/build_x86_64/tools'

  /usr/bin/make -f extras/Build/juceaide/CMakeFiles/juceaide.dir/build.make
  extras/Build/juceaide/CMakeFiles/juceaide.dir/depend

  make[2]: Entering directory '/build/JUCE/build_x86_64/tools'

  cd /build/JUCE/build_x86_64/tools && /opt/cmake/bin/cmake -E cmake_depends
  "Unix Makefiles" /build/JUCE /build/JUCE/extras/Build/juceaide
  /build/JUCE/build_x86_64/tools
  /build/JUCE/build_x86_64/tools/extras/Build/juceaide
  /build/JUCE/build_x86_64/tools/extras/Build/juceaide/CMakeFiles/juceaide.dir/DependInfo.cmake
  --color=

  make[2]: Leaving directory '/build/JUCE/build_x86_64/tools'

  /usr/bin/make -f extras/Build/juceaide/CMakeFiles/juceaide.dir/build.make
  extras/Build/juceaide/CMakeFiles/juceaide.dir/build

  make[2]: Entering directory '/build/JUCE/build_x86_64/tools'

  [ 12%] Building CXX object
  extras/Build/juceaide/CMakeFiles/juceaide.dir/Main.cpp.o

  cd /build/JUCE/build_x86_64/tools/extras/Build/juceaide &&
  /opt/llvm-mingw/bin/c++ -DDEBUG=1 -DJUCE_DISABLE_JUCE_VERSION_PRINTING=1
  -DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1
  -DJUCE_MODULE_AVAILABLE_juce_build_tools=1
  -DJUCE_MODULE_AVAILABLE_juce_core=1
  -DJUCE_MODULE_AVAILABLE_juce_data_structures=1
  -DJUCE_MODULE_AVAILABLE_juce_events=1
  -DJUCE_MODULE_AVAILABLE_juce_graphics=1
  -DJUCE_MODULE_AVAILABLE_juce_gui_basics=1 -DJUCE_STANDALONE_APPLICATION=1
  -DJUCE_USE_CURL=0 -DLINUX=1 -D_DEBUG=1
  -I/build/JUCE/build_x86_64/tools/extras/Build/juceaide/juceaide_artefacts/JuceLibraryCode
  -I/build/JUCE/extras/Build -I/build/JUCE/modules -g -O0 -Wall -Wshadow-all
  -Wshorten-64-to-32 -Wstrict-aliasing -Wuninitialized -Wunused-parameter
  -Wconversion -Wsign-compare -Wint-conversion -Wconditional-uninitialized
  -Wconstant-conversion -Wsign-conversion -Wbool-conversion -Wextra-semi
  -Wunreachable-code -Wcast-align -Wshift-sign-overflow
  -Wno-missing-field-initializers -Wnullable-to-nonnull-conversion
  -Wno-ignored-qualifiers -Wswitch-enum -Wpedantic
  -Wzero-as-null-pointer-constant -Wunused-private-field -Woverloaded-virtual
  -Wreorder -Winconsistent-missing-destructor-override -MD -MT
  extras/Build/juceaide/CMakeFiles/juceaide.dir/Main.cpp.o -MF
  CMakeFiles/juceaide.dir/Main.cpp.o.d -o CMakeFiles/juceaide.dir/Main.cpp.o
  -c /build/JUCE/extras/Build/juceaide/Main.cpp

The main point of note is that although I configured cmake correctly, it spawns child cmake processes and does not pass my options to them.

The end result is that the compiler is invoked with -DLINUX=1, which is not appropriate for my target platform.

Would it be possible for you to please pass the -DCMAKE_TOOLCHAIN_FILE option to your child cmake processes?

As for the environment variable PKG_CONFIG_PATH… I suspect the child process inherits this already. But otherwise, it’d be good to have a way to pass environment too (I don’t want it to lookup packages installed on the host platform).

The broader work I’m doing is here (fetching my dependencies for x86_64, i686, arm64 Windows via MinGW-w64, to statically-link them):

actually, maybe I’m misunderstanding this…

what is juceaide? it’s some kind of helper executable that your build system can invoke? it’s not a library that the audio plugin links against?

then, maybe I shouldn’t cross-compile it? it needs to be built for the same architecture as the host that’s doing the building?

I’d tried building it natively, but it was trying to compile juce_gui_basics, and was asking me to install x11 headers, which I didn’t understand (juceaide is a headless executable?) — so I thought maybe it’s trying to produce object code for my audio plugin to link against. but maybe that’s a red herring?

juceaide is a helper executable. It runs during the build to generate resources (icons, plists etc.) that would be difficult to generate in plain CMake. As it runs during the build, it should be built for the host platform, rather than the target platform.

The x11 headers are required during the build. This is because JUCE’s Drawable class (used for icon file manipulation) is a Component, which depends on the platform window system. Even so, juceaide can run headlessly, and the x11 shared library doesn’t need to be present on the system at runtime.

thanks, that’s clear. okay, successfully compiled juceaide, and onto the next step :slight_smile: