I am developing a cross-platform C++ library for Android and iOS and I am using cmake to prepare build scripts.
A project structure looks something like:
somelib | ├─ .gitignore | ├── src │ └── CMakeLists.txt │ └── Doxyfile.in | └── include (Public headers) │ └── somelib │ └── somelib.hpp │ └── somelib │ └── CMakeLists.txt │ └── somelib.cpp │ └── ....hpp, ....cpp │ └── test │ └── test_classname.cpp │ └── libs (source of 3rd party libs) │ └── 3rd_party_lib_1 │ └── CMakeLists.txt │ └── ... │ └── ... │ └── CMakeLists.txt │ └── ... │_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | ├── toolchains │ └── andoid │ └── android.toolchain.cmake │ └── ... │ └── ios │ └── ios.toolchain.cmake │ └── ... │_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ │ ├── build │ └── build.sh │ └── Doxyfile │ └── ... │_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | ├── dist (distribute) │ └── docs │ └── index.html │ └── ... | └── include (Public headers) │ └── somelib.hpp │ └── local | └── somelibtest (executable tests) │ └── android │ └── Debug │ └── armeabi │ └── armeabi-v7a │ └── ... │ └── Release │ └── armeabi │ └── armeabi-v7a │ └── ... │ └── ios │ └── Debug │ └── armv │ └── armv7 │ └── ... │ └── Release │ └── armv │ └── armv7 │ └── ... |
I am running cmake from ./build/ path by executing build.sh which in case of building for android, looks something like this:
build.sh:
... TARGETS="armeabi-v7a armeabi x86 mips arm64-v8a mips64" mkdir -p "$BUILD_PATH" for TARGET in $TARGETS do cmake -DANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_TOOLCHAIN} -DANDROID_NDK=${ANDROID_NDK} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DANDROID_ABI=${TARGET} -DPROJ_HOME=${PROJ_HOME} ../src make -j32 done fi ...
If I am building for one particular architecture, for example setting TARGETS to “arm64-v8a” only, the library builts well. In case if I want to build a library for multiple architectures at once then it does not work well since cmake prepares build script specifically for each platform/architecture. I get linker errors such as “incompatible target” durring the build.
What is the best practice to build libraries targeting multiple platforms and multi-architecture?
How can I avoid cleaning build script files before preparing to build for another architecture?
Thank you for any suggestions!
PS: Here are some lines from main src/CmakeLists.txt file:
cmake_minimum_required(VERSION 3.6.0 FATAL_ERROR) include(GNUInstallDirs) # Set default build type if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") endif() ## Output directories if (IOS) set(REL_OUTPUT_DIR "ios/${CMAKE_BUILD_TYPE}/${IOS_ARCH}") elseif (ANDROID) set(REL_OUTPUT_DIR "android/${CMAKE_BUILD_TYPE}/${ANDROID_ABI}") else() set(REL_OUTPUT_DIR "local") endif() set(OUTPUT_DIR ${CMAKE_BINARY_DIR}/../dist/${REL_OUTPUT_DIR}) set(DESTDIR ${CMAKE_BINARY_DIR}/../dist/${REL_OUTPUT_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${OUTPUT_DIR}") # Static libraries set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${OUTPUT_DIR}") # Dynamic libraries set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OUTPUT_DIR}") # Executables # Include libraries add_subdirectory(libs/3rd_party_lib_1) add_subdirectory(libs/3rd_party_lib_n)
Answer
An option, as I eluded to in the comments, is to use one build directory per target architecture
... TARGETS="armeabi-v7a armeabi x86 mips arm64-v8a mips64" for TARGET in ${TARGETS} do # create one build dir per target architecture mkdir -p ${BUILD_PATH}/${TARGET} cd ${BUILD_PATH}/${TARGET} cmake -DANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_TOOLCHAIN} -DANDROID_NDK=${ANDROID_NDK} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DANDROID_ABI=${TARGET} -DPROJ_HOME=${PROJ_HOME} ../../src make -j32 cd - done ...
This will result in a tree looking somewhat like the following
project/ +--- src/ +--- build/ +--- armeabi-v7a/ +--- armeabi/ +--- x86/ +--- mips/ +--- arm64-v8a/ +--- mips64/