Building multiple configurations with CMake in one go!
source link: https://www.tuicool.com/articles/E7FFVrr
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Coming from other build systems to CMake one will quickly learn that CMake can build only one configuration at a time. In practice you need to set up multiple build directories and configure/build with CMake for each and every one.
Autotools can do static and shared builds of libraries. For CMake most of the project would do a static build, then a shared build by setting the CMake variable BUILD_SHARED_LIBS
to ON
.
QMake can do debug and release builds at the same time, and as we can read at Qt for Android better than ever before , it can configure multiple Android architecture configurations at the same time.
What can we do to get the same level of convenience with CMake?
Shared and static
CMake needs to have unique target names, so if we would have to build a shared and static build we would need to have different target names.
Since we need to build the same library twice, but with only one cmake --build
invocation, it would mean that CMake needs to call itself.
That’s it what I’m going to do. Build the same source directory in two different build directories. The add_subdirectory CMake command allows a second parameter for a build directory.
Here is what’s needed to have a library build itself shared and static:
cmake_minimum_required(VERSION 3.9) project(lib LANGUAGES CXX) if (NOT ${PROJECT_NAME}-MultiBuild) set(${PROJECT_NAME}-MultiBuild ON) macro (setup_library library_name build_type) set(LIBNAME ${library_name}) set(LIBTYPE ${build_type}) add_subdirectory( ${CMAKE_CURRENT_SOURCE_DIR} build-${build_type} ) endmacro() setup_library(${PROJECT_NAME}_s STATIC) setup_library(${PROJECT_NAME} SHARED) return() endif() # The normal CMake library code goes here add_library(${LIBNAME} ${LIBTYPE} lib.cpp)
Debug and release
If we apply the same idea to a debug and release build, we have:
cmake_minimum_required(VERSION 3.9) project(lib LANGUAGES CXX) if (NOT ${PROJECT_NAME}-MultiBuild) set(${PROJECT_NAME}-MultiBuild ON) macro (setup_library library_name build_type) set(LIBNAME ${library_name}) set(CMAKE_BUILD_TYPE ${build_type}) add_subdirectory( ${CMAKE_CURRENT_SOURCE_DIR} build-${build_type} ) endmacro() setup_library(${PROJECT_NAME}_d Debug) setup_library(${PROJECT_NAME} Release) return() endif() # The normal CMake library code goes here add_library(${LIBNAME} lib.cpp)
This will work with command line generators like Ninja or Makefiles, but it won’t work with multi-config generators like Visual Studio.
Debug and release for Visual Studio
In order to get Visual Studio to produce a debug and release mode, we need to be able to invoke CMake with separate --config <CONFIG>
values for Debug and Release.
Even if we fiddle with CMAKE_CONFIGURATION_TYPES the above method is not enough. msbuild will fail to build.
We need to get independent CMake runs on the same source code. Luckily CMake provides us with ExternalProject module.
ExternalProject
is meant for software downloaded from the internet, but it also works fine with existing source code
The code looks like this:
cmake_minimum_required(VERSION 3.9) project(lib LANGUAGES CXX) if (NOT ${PROJECT_NAME}-MultiBuild) include(ExternalProject) macro (setup_library library_name build_type) ExternalProject_Add(${library_name}-builder SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CMAKE_ARGS -DLIBNAME=${library_name} -DCMAKE_BUILD_TYPE=${build_type} -DCMAKE_CONFIGURATION_TYPES=${build_type} -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -D${PROJECT_NAME}-MultiBuild=ON BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${build_type} INSTALL_COMMAND ${CMAKE_COMMAND} -P cmake_install.cmake ) endmacro() setup_library(${PROJECT_NAME}_d Debug) setup_library(${PROJECT_NAME} Release) return() endif() # The normal CMake library code goes here add_library(${LIBNAME} lib.cpp) install(TARGETS ${LIBNAME})
I needed to restrict the CMAKE_CONFIGURATION_TYPES
only for the needed configuration, and to have a custom BUILD_COMMAND
, INSTALL_COMMAND
, and to install the library. At the end in the build directory I’ve got a lib
directory containing the two libraries.
If you have multiple libraries depending on each other, you will have to have proper CMake packages for the libraries, and set the appropriate CMAKE_PREFIX_PATH values.
Android multi architecture
In order to test the same setup for Android, I am assuming you have the Android NDK somewhere in your system.
I configured and build the project from a Windows command prompt window like this:
$ cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=c:\Tools\android-ndk-r20\build\cmake\android.toolchain.cmake .. $ cmake --build .
The CMake code which builds for armeabi-v7a
, arm64-v8a
, x86
, x86_64
is below:
cmake_minimum_required(VERSION 3.9) project(lib) if (NOT ${PROJECT_NAME}-MultiBuild) include(ExternalProject) file(TO_CMAKE_PATH "${CMAKE_TOOLCHAIN_FILE}" toolchain_file) macro (setup_library library_name android_abi) ExternalProject_Add(${library_name}-builder SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CMAKE_ARGS -DLIBNAME=${library_name} -DANDROID_ABI=${android_abi} -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DCMAKE_TOOLCHAIN_FILE=${toolchain_file} -D${PROJECT_NAME}-MultiBuild=ON ) endmacro() setup_library(${PROJECT_NAME}-v7a armeabi-v7a) setup_library(${PROJECT_NAME}-v8a arm64-v8a) setup_library(${PROJECT_NAME}-x86 x86) setup_library(${PROJECT_NAME}-x86_64 x86_64) return() endif() # The normal CMake library code add_library(${LIBNAME} lib.cpp) install(TARGETS ${LIBNAME})
I only needed to pass the ANDROID_ABI
, and CMAKE_TOOLCHAIN_FILE
variables.
Conclusion
I hope I managed to convince you that CMake is not limited to only one build configuration at one time
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK