How to pass compiler and options to external project configure script?

I am using CMake to compile some native code for Android which relies on a third-party library that uses the autoconf/automake build system. I having trouble trying to figure out how to pass in all the appropriate variables into the external project’s configure script in order to have it cross-compile for the appropriate architecture for Android. My question is how to pass the knowledge that is given to Ninja to invoke the cross-compiler through to the external project as well.

First, some background:

I am using the normal Gradle build system for Android at the outermost later and it’s externalNativeBuild plugin which calls CMake and Ninja once for each of the four supported Android Architectures: 32-bit and 64-bit variations of ARM and x86. My own native code as a CMake project is compiling fine for it without the library. As a test, I tried compiling the external project for one architecture, 64-bit ARM, successfully using the following:

export NDK=~/Android/Sdk/ndk/21.3.6528147
export CROSS_SYSROOT=${NDK}/toolchains/llvm/prebuilt/linux-x86_64/sysroot
export CROSS_COMPILE=aarch64-linux-android30-
export SYSROOT="${CROSS_SYSROOT}"
export CC="${CROSS_COMPILE}clang --sysroot=${SYSROOT}"
export CXX="${CROSS_COMPILE}clang++ --sysroot=${SYSROOT}" 
export PATH=${NDK}/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin:${NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
./configure --host=aarch64-linux-android --build=`./config.guess` --with-sysroot="${SYSROOT}"
make -j

The library appears to be appropriately compiled for Arm 64-bit, but I will need it built for each of the architectures and I would prefer to just have it built from the existing CMake build so it is built with the correct NDK for each architecture. I was able to hook it in as an External Project with this:

ExternalProject_Add(DSP_PROJECT
    SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libdsp"
    CONFIGURE_COMMAND <SOURCE_DIR>/configure "--host=${ANDROID_LLVM_TRIPLE}" --build=x86_64-pc-linux-gnu --prefix=<INSTALL_DIR> --disable-static "--with-sysroot=${CMAKE_SYSROOT}" "CC=${CMAKE_C_COMPILER}" "CXX=${CMAKE_CXX_COMPILER}" "AR=${CMAKE_AR}" "RANLIB=${CMAKE_RANLIB}"
    BUILD_COMMAND ${MAKE}
    BUILD_BYPRODUCTS <INSTALL_DIR>/lib/libdsp.so
)
ExternalProject_Get_Property(DSP_PROJECT INSTALL_DIR)
set(DSP_INSTALL_DIR ${INSTALL_DIR})
add_library(dsp SHARED IMPORTED GLOBAL)
set_property(TARGET dsp PROPERTY IMPORTED_LOCATION ${DSP_INSTALL_DIR}/lib/libdsp.so)
add_dependencies(dsp DSP_PROJECT)

This seemed to properly trigger the build of libdsp as a dependency, but later on, when it tried to link in libdsp with the other native code, it reported invalid format. It seems that libdsp was compiled for x86_64 instead of aarch64 and the problem seems to come down to the value of the C compile $CC variable. The value ${CMAKE_C_COMPILER} expands to the path to clang in the Android NDK which defaults to the host architecture if no --target=... is included on the command-line. Looking at the Ninja rules file that was generated for other native code internal to the CMake rules, it produced lines like this:

command = .../ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/bin/clang --target=armv7-none-linux-androideabi16 --gcc-toolchain=.../ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64 --sysroot=.../ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/sysroot ...

So it appears to be using clang directly as a front-end and selecting the correct architecture with --target=... and other command-line parameters instead of just pointing at the correct compiler as I did in my earlier test. This should be fine as long as I can pass the correct values through CMake to the external configure script, but I can’t seem to find the correct variables to pass. I have tried several variations on the configure line above to get it to work. Dumping a number of variables from the 32-bit ARM build, I see this:

CMAKE_C_COMPILER=.../ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/bin/clang
CMAKE_CXX_COMPILER=.../ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++
CMAKE_AR=.../ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/bin/arm-linux-androideabi-ar
CMAKE_RANLIB=.../ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/bin/arm-linux-androideabi-ranlib
CMAKE_NM=.../ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/bin/arm-linux-androideabi-nm
CMAKE_CXX_ANDROID_TOOLCHAIN_MACHINE=arm-linux-androideabi-clang
CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX=.../ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/bin/arm-linux-androideabi-

Other tools like ar and ranlib are using architecture-specific versions, but the C/C++ compilers are just clang/clang++. Nothing seems to lead me to aarch64-linux-android29-clang or --target=armv7-none-linux-androideabi16 from what I can tell. CMake must be aware of it as it generates it for the native internal code, but how do I pass it to the external configure?

I am using CMake 3.10.2.

1 Like

How to pass the information along to a build system is more a function of the target build system than anything else. I don’t know what the libdsp build is looking for, but you’ll have to stitch that information across the boundary between CMake and autotools. I suspect you’ll need to pass some other information down. Can you get libdsp to compile for aarch64 successfully outside of CMake? Figuring that out should help determine what you need to pass along from the CMake side.