Android: Add multi-abi support for CMake

The patch adds ANDROID_BUILD_ABI_<abi> CMake options, which when enabled
will determine CMake to build the current project with the enabled ABI
settings.

When building with CMake and the official Android CMake toolchain
one needs to specify the Qt base directory as an argument to
CMAKE_FIND_ROOT_PATH, which contains the Android NDK sysroot
set by the toolchain.

CMake will consider directories that contain this base path as
valid directories to search packages. In the developer build case we
have to append "lib/cmake" because the Qt base directory passed as
CMAKE_FIND_ROOT_PATH will be the same directory as the developer
build base, and will not be considered.

Change-Id: I180502032c8ea1105bde2456252b367497f511d6
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Reviewed-by: BogDan Vatra <bogdan@kdab.com>
bb10
Cristian Adam 2019-10-18 16:07:45 +02:00
parent f3dbe98dca
commit 52c799ed44
4 changed files with 207 additions and 1 deletions

View File

@ -0,0 +1,191 @@
if (NOT ${PROJECT_NAME}-MultiAbiBuild)
set(ANDROID_ABIS armeabi-v7a arm64-v8a x86 x86_64)
# Match Android's sysroots
set(ANDROID_SYSROOT_armeabi-v7a arm-linux-androideabi)
set(ANDROID_SYSROOT_arm64-v8a aarch64-linux-android)
set(ANDROID_SYSROOT_x86 i686-linux-android)
set(ANDROID_SYSROOT_x86_64 x86_64-linux-android)
foreach(abi IN LISTS ANDROID_ABIS)
set(abi_initial_value OFF)
if (abi STREQUAL ${ANDROID_ABI})
set(abi_initial_value ON)
endif()
find_library(Qt5Core_${abi}_Probe Qt5Core_${abi})
if (Qt5Core_${abi}_Probe)
option(ANDROID_BUILD_ABI_${abi} "Enable the build for Android ${abi}" ${abi_initial_value})
endif()
endforeach()
# Make sure to delete the "android-build" directory, which contains all the
# build artefacts, and also the androiddeployqt/gradle artefacts
set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
APPEND PROPERTY ADDITIONAL_CLEAN_FILES ${CMAKE_BINARY_DIR}/android-build)
if (CMAKE_VERSION VERSION_LESS 3.15)
message(STATUS "-----------------------------------------------------------------------------------------------------------")
message(STATUS "CMake version 3.15 is required to clean the <build_dir>/android-build when issuing the \"clean\" target.\n\n"
"For this CMake version please use the \"clean-android-build\" support target additionally to the \"clean\" target.")
message(STATUS "-----------------------------------------------------------------------------------------------------------")
add_custom_target(clean-android-build
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/android-build
VERBATIM)
endif()
# Write the android_<project_name>_deployment_settings.json file
file(WRITE ${CMAKE_BINARY_DIR}/android_deployment_settings.json.in
[=[{
"_description": "This file is created by CMake to be read by androiddeployqt and should not be modified by hand.",
"application-binary": "@QT_ANDROID_APPLICATION_BINARY@",
"architectures": {
@QT_ANDROID_ARCHITECTURES@
},
@QT_ANDROID_DEPLOYMENT_DEPENDENCIES@
@QT_ANDROID_EXTRA_PLUGINS@
@QT_ANDROID_PACKAGE_SOURCE_DIR@
@QT_ANDROID_VERSION_CODE@
@QT_ANDROID_VERSION_NAME@
@QT_ANDROID_EXTRA_LIBS@
@QT_QML_IMPORT_PATH@
"ndk": "@ANDROID_NDK@",
"ndk-host": "@ANDROID_HOST_TAG@",
"qml-root-path": "@CMAKE_CURRENT_SOURCE_DIR@",
"qt": "@QT_DIR@",
"sdk": "@ANDROID_SDK@",
"stdcpp-path": "@ANDROID_TOOLCHAIN_ROOT@/sysroot/usr/lib/",
"tool-prefix": "llvm",
"toolchain-prefix": "llvm",
"useLLVM": true
}]=])
if (NOT QT_ANDROID_APPLICATION_BINARY)
set(QT_ANDROID_APPLICATION_BINARY ${PROJECT_NAME})
endif()
if(NOT ANDROID_SDK)
get_filename_component(ANDROID_SDK ${ANDROID_NDK}/../ ABSOLUTE)
endif()
find_program(ANDROID_DEPLOY_QT androiddeployqt)
get_filename_component(QT_DIR ${ANDROID_DEPLOY_QT}/../../ ABSOLUTE)
unset(QT_ANDROID_ARCHITECTURES)
foreach(abi IN LISTS ANDROID_ABIS)
if (ANDROID_BUILD_ABI_${abi})
list(APPEND QT_ANDROID_ARCHITECTURES " \"${abi}\" : \"${ANDROID_SYSROOT_${abi}}\"")
endif()
endforeach()
string(REPLACE ";" ",\n" QT_ANDROID_ARCHITECTURES "${QT_ANDROID_ARCHITECTURES}")
macro(generate_json_variable_list var_list json_key)
if (${var_list})
set(QT_${var_list} "\"${json_key}\": \"")
string(REPLACE ";" "," joined_var_list "${${var_list}}")
string(APPEND QT_${var_list} "${joined_var_list}\",")
endif()
endmacro()
macro(generate_json_variable var json_key)
if (${var})
set(QT_${var} "\"${json_key}\": \"${${var}}\",")
endif()
endmacro()
generate_json_variable_list(ANDROID_DEPLOYMENT_DEPENDENCIES "deployment-dependencies")
generate_json_variable_list(ANDROID_EXTRA_PLUGINS "android-extra-plugins")
generate_json_variable(ANDROID_PACKAGE_SOURCE_DIR "android-package-source-directory")
generate_json_variable(ANDROID_VERSION_CODE "android-version-code")
generate_json_variable(ANDROID_VERSION_NAME "android-version-name")
generate_json_variable_list(ANDROID_EXTRA_LIBS "android-extra-libs")
generate_json_variable_list(QML_IMPORT_PATH "qml-import-paths")
configure_file(
"${CMAKE_BINARY_DIR}/android_deployment_settings.json.in"
"${CMAKE_BINARY_DIR}/android_deployment_settings.json" @ONLY)
# Create "apk" and "aab" targets
if (DEFINED ENV{JAVA_HOME})
set(JAVA_HOME $ENV{JAVA_HOME} CACHE INTERNAL "Saved JAVA_HOME variable")
endif()
if (JAVA_HOME)
set(android_deploy_qt_jdk "--jdk ${JAVA_HOME}")
endif()
if (ANDROID_SDK_PLATFORM)
set(android_deploy_qt_platform "--android-platform ${ANDROID_SDK_PLATFORM}")
endif()
add_custom_target(apk
COMMAND ${CMAKE_COMMAND} -E env JAVA_HOME=${JAVA_HOME} ${ANDROID_DEPLOY_QT}
--input "${CMAKE_BINARY_DIR}/android_deployment_settings.json"
--output "${CMAKE_BINARY_DIR}/android-build"
--apk "${CMAKE_BINARY_DIR}/android-build/${PROJECT_NAME}.apk"
${android_deploy_qt_platform}
${android_deploy_qt_jdk}
VERBATIM)
add_custom_target(aab
COMMAND ${CMAKE_COMMAND} -E env JAVA_HOME=${JAVA_HOME} ${ANDROID_DEPLOY_QT}
--input "${CMAKE_BINARY_DIR}/android_deployment_settings.json"
--output "${CMAKE_BINARY_DIR}/android-build"
--apk "${CMAKE_BINARY_DIR}/android-build/${PROJECT_NAME}.apk"
--aab
${android_deploy_qt_platform}
${android_deploy_qt_jdk}
VERBATIM)
include(ExternalProject)
macro (setup_library library_name android_abi)
# Use Qt Creator's 4.12 settings file if present
unset(QTC_SETTINGS_PARAMETER)
if (EXISTS ${CMAKE_BINARY_DIR}/qtcsettings.cmake)
set(QTC_SETTINGS_PARAMETER -C ${CMAKE_BINARY_DIR}/qtcsettings.cmake)
endif()
# Build all the given ABI as an external project
ExternalProject_Add(${library_name}-builder
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
PREFIX MultiAbi
BUILD_ALWAYS YES
DOWNLOAD_COMMAND ""
INSTALL_COMMAND ""
UPDATE_COMMAND ""
PATCH_COMMAND ""
CMAKE_ARGS
${QTC_SETTINGS_PARAMETER}
-D ANDROID_ABI=${android_abi}
-D CMAKE_FIND_ROOT_PATH=${CMAKE_FIND_ROOT_PATH}
-D CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}
-D CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-D ANDROID_PLATFORM=${ANDROID_PLATFORM}
-D ANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL}
-D CMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}
-D CMAKE_FIND_ROOT_PATH_MODE_PROGRAM=${CMAKE_FIND_ROOT_PATH_MODE_PROGRAM}
-D CMAKE_FIND_ROOT_PATH_MODE_LIBRARY=${CMAKE_FIND_ROOT_PATH_MODE_LIBRARY}
-D CMAKE_FIND_ROOT_PATH_MODE_INCLUDE=${CMAKE_FIND_ROOT_PATH_MODE_INCLUDE}
-D CMAKE_FIND_ROOT_PATH_MODE_PACKAGE=${CMAKE_FIND_ROOT_PATH_MODE_PACKAGE}
-D CMAKE_SHARED_LIBRARY_SUFFIX_CXX=_${android_abi}.so
-D CMAKE_LIBRARY_OUTPUT_DIRECTORY=${CMAKE_BINARY_DIR}/android-build/libs/${android_abi}
-D ${PROJECT_NAME}-MultiAbiBuild=ON
)
endmacro()
foreach(abi IN LISTS ANDROID_ABIS)
if (NOT abi STREQUAL ${ANDROID_ABI})
if (ANDROID_BUILD_ABI_${abi})
setup_library(${PROJECT_NAME}-${abi} ${abi} ${CMAKE_PREFIX_PATH})
endif()
else()
# For the default abi just use the regular cmake run, to have
# nice IDE integration and so on
set(CMAKE_SHARED_LIBRARY_SUFFIX_CXX "_${ANDROID_ABI}.so")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/android-build/libs/${ANDROID_ABI})
endif()
endforeach()
else()
# unset the variable, just not to issue an unused variable warning
unset(${PROJECT_NAME}-MultiAbiBuild)
endif()

View File

@ -172,4 +172,8 @@ get_filename_component(_Qt5CoreConfigDir ${CMAKE_CURRENT_LIST_FILE} PATH)
set(_Qt5CTestMacros \"${_Qt5CoreConfigDir}/Qt5CTestMacros.cmake\")
if (ANDROID_PLATFORM)
include(\"${CMAKE_CURRENT_LIST_DIR}/Qt5AndroidSupport.cmake\")
endif()
_qt5_Core_check_file_exists(${_Qt5CTestMacros})

View File

@ -4,7 +4,7 @@ get_filename_component(_qt5_root_dir \"${CMAKE_CURRENT_LIST_DIR}/../../../..\" A
file(GLOB qtmodules ${_qt5_root_dir} "${_qt5_root_dir}/*")
foreach(qtmodule ${qtmodules})
if(IS_DIRECTORY ${qtmodule})
list(APPEND _qt5_module_paths ${qtmodule})
list(APPEND _qt5_module_paths "${qtmodule}" "${qtmodule}/lib/cmake")
endif()
endforeach()
!!ELSE

View File

@ -99,6 +99,12 @@ cmake_umbrella_config_module_location_for_install.output = $$DESTDIR/cmake/insta
cmake_umbrella_config_version_file.input = $$PWD/../../mkspecs/features/data/cmake/Qt5ConfigVersion.cmake.in
cmake_umbrella_config_version_file.output = $$DESTDIR/cmake/Qt5/Qt5ConfigVersion.cmake
android {
cmake_android_support.input = $$PWD/Qt5AndroidSupport.cmake
cmake_android_support.output = $$DESTDIR/cmake/Qt5Core/Qt5AndroidSupport.cmake
cmake_android_support.CONFIG = verbatim
}
load(cmake_functions)
defineTest(pathIsAbsolute) {
@ -144,6 +150,11 @@ QMAKE_SUBSTITUTES += \
cmake_extras_mkspec_dir \
cmake_extras_mkspec_dir_for_install
android {
QMAKE_SUBSTITUTES += cmake_android_support
ctest_qt5_module_files.files += $$cmake_android_support.output
}
ctest_qt5_module_files.files += $$ctest_macros_file.output $$cmake_extras_mkspec_dir_for_install.output
ctest_qt5_module_files.path = $$[QT_INSTALL_LIBS]/cmake/Qt5Core