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.