TIL: Dynamically Extending the CMAKE_ARGS for External Project
I’ve recently introduced a bug that prevented the cross-compilation of the project for x86_64 from ARM-based macOS. In this short piece, I’d like to summarize what happened and how to fix it.
ITGmania project is an effort to modernize the StepMania game, a Dance Dance Revolution arcade game simulator. It’s a fork that receives regular updates, both security and new features.
In January I proposed to replace an old vendored libjpeg
library with a git submodule1 of a popular drop-in replacement, libjpeg-turbo
.
It uses SIMD instructions to accelerate compression and decompression operations, so we could also get some performance gains for free.
The crucial part of the whole story is that libjpeg-turbo
uses CMake as its build system.
That’s great news because ITGmania also uses it, so the old glue CMake module for building libjpeg
could be replaced with an ExternalProject
.
I wrote a short module to specify the library, how to build it, and the target for linking in the main project.
After some tweaking related to the Ninja generator that’s by default used on Windows in Visual Studio, all the obvious problems have been resolved and the automatic builds in the CI pipeline in GitHub Actions were green. The game worked with the new library.
The PR was merged a week ago, so the “real-world integration” could begin ;)
First, there was an issue reported about linking the project on Arch Linux.
It turned out that the output lib directory was using a different scheme – lib64
instead of lib
that was expected by the linker.
I quickly fixed that and submitted a PR.
Yesterday, teejusb, the project maintainer, contacted me about linking issues on macOS.
He got an Undefined symbols for architecture x86_64
message related to some symbols from the libjpeg
library, so I assumed he was building on an x86_64 macOS.
That’s weird because we perform that build in the pipeline on every commit and it passed.
I don’t have a Mac, so I created a fresh KVM VM with macOS in order to reproduce the issue. I had some problems with getting the GPU passthrough to work, but I managed to build and start the game on a virtual GPU. It compiled and linked – as expected.
What he didn’t tell me was that he was trying to cross-compile the x86_64 target from his Apple Silicon macOS (some aarch64). I traced the issue to the two bugs in these 3 lines of the original PR:
if(MACOSX)
set(ARCH_FLAGS "-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES}")
endif()
The first one being the wrong variable to check for macOS – it should be APPLE
, not MACOSX
.
The other one was more intriguing, it was related to how ARCH_FLAGS
were used later in the ExternalProject_Add
call within CMAKE_ARGS
.
include(ExternalProject)
ExternalProject_Add(
libjpeg_turbo_project
SOURCE_DIR ${SM_EXTERN_DIR}/libjpeg-turbo
INSTALL_DIR ${INSTALL_DIR}
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}
-DCMAKE_INSTALL_LIBDIR=${LIB_DIR}
-DENABLE_SHARED=OFF
-DENABLE_STATIC=ON
${ARCH_FLAGS}
(...)
After changing ARCH_FLAGS
to a list, I ended up with:
if(APPLE)
LIST(APPEND ARCH_FLAGS
-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}
-DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES}
)
endif()
This now properly passes the target and architecture to the libjpeg-turbo
build.
I’ve submitted it as a Pull Request this morning.
The old code never executed the if
branch in the first place, and even when I changed the condition to APPLE
, the variable didn’t work as intended.
So, why did it seem to work? Why was it merged?
It’s simply because the builds in the pipeline never exercised the cross-compilation path – they used separate hardware for each macOS build.
I also started an issue to propose new automatic builds to cover more actual use cases, not just the basic ones. They will hopefully prevent similar issues from happening in the future.
-
Poor-man’s dependency management :< I don’t like this, but that’s how the maintainers of the project want the deps to be specified and fetched. ↩︎