2020-01-29 13:18:59 +01:00
cmake_minimum_required ( VERSION 3.0.0 FATAL_ERROR )
2015-02-12 13:52:33 +01:00
# store the current source directory for future use
set ( QT_ANDROID_SOURCE_DIR ${ CMAKE_CURRENT_LIST_DIR } )
# check the JAVA_HOME environment variable
# (I couldn't find a way to set it from this script, it has to be defined outside)
set ( JAVA_HOME $ENV{ JAVA_HOME } )
if ( NOT JAVA_HOME )
message ( FATAL_ERROR "The JAVA_HOME environment variable is not set. Please set it to the root directory of the JDK." )
endif ( )
# make sure that the Android toolchain is used
if ( NOT ANDROID )
message ( FATAL_ERROR "Trying to use the CMake Android package without the Android toolchain. Please use the provided toolchain (toolchain/android.toolchain.cmake)" )
endif ( )
2021-07-03 14:54:59 +03:00
if ( NOT QT_VERSION_MAJOR )
2021-07-20 11:54:01 +03:00
find_package ( QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED )
2021-07-03 14:54:59 +03:00
endif ( )
# find the Qt root directory
if ( NOT Qt ${ QT_VERSION_MAJOR } Core_DIR )
2021-07-03 15:04:49 +03:00
find_package ( Qt ${ QT_VERSION_MAJOR } Core REQUIRED )
2021-07-03 14:54:59 +03:00
endif ( )
get_filename_component ( QT_ANDROID_QT_ROOT "${Qt${QT_VERSION_MAJOR}Core_DIR}/../../.." ABSOLUTE )
2015-02-12 13:52:33 +01:00
message ( STATUS "Found Qt for Android: ${QT_ANDROID_QT_ROOT}" )
# find the Android SDK
if ( NOT QT_ANDROID_SDK_ROOT )
set ( QT_ANDROID_SDK_ROOT $ENV{ ANDROID_SDK } )
if ( NOT QT_ANDROID_SDK_ROOT )
2020-05-12 20:38:05 +02:00
set ( QT_ANDROID_SDK_ROOT $ENV{ ANDROID_SDK_ROOT } )
if ( NOT QT_ANDROID_SDK_ROOT )
message ( FATAL_ERROR "Could not find the Android SDK. Please set either the ANDROID_SDK environment variable, or the QT_ANDROID_SDK_ROOT CMake variable to the root directory of the Android SDK" )
endif ( )
2015-02-12 13:52:33 +01:00
endif ( )
endif ( )
string ( REPLACE "\\" "/" QT_ANDROID_SDK_ROOT ${ QT_ANDROID_SDK_ROOT } ) # androiddeployqt doesn't like backslashes in paths
message ( STATUS "Found Android SDK: ${QT_ANDROID_SDK_ROOT}" )
# find the Android NDK
if ( NOT QT_ANDROID_NDK_ROOT )
set ( QT_ANDROID_NDK_ROOT $ENV{ ANDROID_NDK } )
if ( NOT QT_ANDROID_NDK_ROOT )
set ( QT_ANDROID_NDK_ROOT ${ ANDROID_NDK } )
if ( NOT QT_ANDROID_NDK_ROOT )
2020-01-29 13:18:59 +01:00
message ( FATAL_ERROR "Could not find the Android NDK. Please set either the ANDROID_NDK environment or CMake variable, or the QT_ANDROID_NDK_ROOT CMake variable to the root directory of the Android NDK" )
2015-02-12 13:52:33 +01:00
endif ( )
endif ( )
endif ( )
string ( REPLACE "\\" "/" QT_ANDROID_NDK_ROOT ${ QT_ANDROID_NDK_ROOT } ) # androiddeployqt doesn't like backslashes in paths
message ( STATUS "Found Android NDK: ${QT_ANDROID_NDK_ROOT}" )
include ( CMakeParseArguments )
# define a macro to create an Android APK target
#
# example:
# add_qt_android_apk(my_app_apk my_app
# NAME "My App"
2016-08-16 15:09:56 +02:00
# VERSION_CODE 12
2015-02-12 13:52:33 +01:00
# PACKAGE_NAME "org.mycompany.myapp"
# PACKAGE_SOURCES ${CMAKE_CURRENT_LIST_DIR}/my-android-sources
# KEYSTORE ${CMAKE_CURRENT_LIST_DIR}/mykey.keystore myalias
# KEYSTORE_PASSWORD xxxx
# DEPENDS a_linked_target "path/to/a_linked_library.so" ...
# INSTALL
#)
2020-01-29 13:18:59 +01:00
#
2015-02-12 13:52:33 +01:00
macro ( add_qt_android_apk TARGET SOURCE_TARGET )
# parse the macro arguments
2018-11-26 10:26:00 +01:00
cmake_parse_arguments ( ARG "INSTALL" "NAME;VERSION_CODE;PACKAGE_NAME;PACKAGE_SOURCES;KEYSTORE_PASSWORD" "DEPENDS;KEYSTORE" ${ ARGN } )
2015-02-12 13:52:33 +01:00
# extract the full path of the source target binary
2020-01-29 13:18:59 +01:00
set ( QT_ANDROID_APP_PATH "$<TARGET_FILE:${SOURCE_TARGET}>" ) # full file path to the app's main shared library
2020-06-02 18:21:32 +02:00
if ( ${ Qt5Core_VERSION } VERSION_GREATER_EQUAL 5.14 )
set ( QT_ANDROID_SUPPORT_MULTI_ABI ON )
endif ( )
if ( QT_ANDROID_SUPPORT_MULTI_ABI )
# qtandroideploy will append by itself the ANDROID_ABI to the target name
set ( QT_ANDROID_APPLICATION_BINARY "${SOURCE_TARGET}" )
else ( )
set ( QT_ANDROID_APPLICATION_BINARY ${ QT_ANDROID_APP_PATH } )
endif ( )
2015-02-12 13:52:33 +01:00
# define the application name
if ( ARG_NAME )
set ( QT_ANDROID_APP_NAME ${ ARG_NAME } )
else ( )
set ( QT_ANDROID_APP_NAME ${ SOURCE_TARGET } )
endif ( )
# define the application package name
if ( ARG_PACKAGE_NAME )
set ( QT_ANDROID_APP_PACKAGE_NAME ${ ARG_PACKAGE_NAME } )
else ( )
set ( QT_ANDROID_APP_PACKAGE_NAME org.qtproject. ${ SOURCE_TARGET } )
endif ( )
2018-11-26 10:26:00 +01:00
# detect latest Android SDK build-tools revision
set ( QT_ANDROID_SDK_BUILDTOOLS_REVISION "0.0.0" )
file ( GLOB ALL_BUILD_TOOLS_VERSIONS RELATIVE ${ QT_ANDROID_SDK_ROOT } /build-tools ${ QT_ANDROID_SDK_ROOT } /build-tools/* )
foreach ( BUILD_TOOLS_VERSION ${ ALL_BUILD_TOOLS_VERSIONS } )
# find subfolder with greatest version
if ( ${ BUILD_TOOLS_VERSION } VERSION_GREATER ${ QT_ANDROID_SDK_BUILDTOOLS_REVISION } )
set ( QT_ANDROID_SDK_BUILDTOOLS_REVISION ${ BUILD_TOOLS_VERSION } )
endif ( )
endforeach ( )
2020-01-29 13:18:59 +01:00
message ( STATUS "Found Android SDK build tools version: ${QT_ANDROID_SDK_BUILDTOOLS_REVISION}" )
2017-02-06 12:11:59 +01:00
2020-01-29 13:18:59 +01:00
# get version code from arguments, or generate a fixed one if not provided
set ( QT_ANDROID_APP_VERSION_CODE ${ ARG_VERSION_CODE } )
if ( NOT QT_ANDROID_APP_VERSION_CODE )
set ( QT_ANDROID_APP_VERSION_CODE 1 )
endif ( )
2015-02-12 13:52:33 +01:00
2020-01-29 13:18:59 +01:00
# try to extract the app version from the target properties, or use the version code if not provided
get_property ( QT_ANDROID_APP_VERSION TARGET ${ SOURCE_TARGET } PROPERTY VERSION )
if ( NOT QT_ANDROID_APP_VERSION )
2020-02-11 11:34:36 +01:00
if ( PROJECT_VERSION )
2020-01-29 13:18:59 +01:00
set ( QT_ANDROID_APP_VERSION ${ PROJECT_VERSION } )
2020-02-11 11:34:36 +01:00
else ( )
set ( QT_ANDROID_APP_VERSION ${ QT_ANDROID_APP_VERSION_CODE } )
2016-08-16 15:09:56 +02:00
endif ( )
2020-01-29 13:18:59 +01:00
endif ( )
# check if the user provides a custom source package and its own manifest file
if ( ARG_PACKAGE_SOURCES )
if ( EXISTS "${ARG_PACKAGE_SOURCES}/AndroidManifest.xml" )
# custom manifest provided, use the provided source package directly
set ( QT_ANDROID_APP_PACKAGE_SOURCE_ROOT ${ ARG_PACKAGE_SOURCES } )
elseif ( EXISTS "${ARG_PACKAGE_SOURCES}/AndroidManifest.xml.in" )
# custom manifest template provided
set ( QT_ANDROID_MANIFEST_TEMPLATE "${ARG_PACKAGE_SOURCES}/AndroidManifest.xml.in" )
endif ( )
endif ( )
2015-02-12 13:52:33 +01:00
2020-01-29 13:18:59 +01:00
# generate a source package directory if none was provided, or if we need to configure a manifest file
if ( NOT QT_ANDROID_APP_PACKAGE_SOURCE_ROOT )
# create our own configured package directory in build dir
2015-02-12 13:52:33 +01:00
set ( QT_ANDROID_APP_PACKAGE_SOURCE_ROOT "${CMAKE_CURRENT_BINARY_DIR}/package" )
2020-01-29 13:18:59 +01:00
# create the manifest from the template file
if ( NOT QT_ANDROID_MANIFEST_TEMPLATE )
set ( QT_ANDROID_MANIFEST_TEMPLATE "${QT_ANDROID_SOURCE_DIR}/AndroidManifest.xml.in" )
endif ( )
configure_file ( ${ QT_ANDROID_MANIFEST_TEMPLATE } ${ CMAKE_CURRENT_BINARY_DIR } /AndroidManifest.xml @ONLY )
# define commands that will be added before the APK target build commands, to refresh the source package directory
set ( QT_ANDROID_PRE_COMMANDS ${ QT_ANDROID_PRE_COMMANDS } COMMAND ${ CMAKE_COMMAND } -E remove_directory ${ QT_ANDROID_APP_PACKAGE_SOURCE_ROOT } ) # clean the destination directory
set ( QT_ANDROID_PRE_COMMANDS ${ QT_ANDROID_PRE_COMMANDS } COMMAND ${ CMAKE_COMMAND } -E make_directory ${ QT_ANDROID_APP_PACKAGE_SOURCE_ROOT } ) # re-create it
if ( ARG_PACKAGE_SOURCES )
set ( QT_ANDROID_PRE_COMMANDS ${ QT_ANDROID_PRE_COMMANDS } COMMAND ${ CMAKE_COMMAND } -E copy_directory ${ ARG_PACKAGE_SOURCES } ${ QT_ANDROID_APP_PACKAGE_SOURCE_ROOT } ) # copy the user package
endif ( )
set ( QT_ANDROID_PRE_COMMANDS ${ QT_ANDROID_PRE_COMMANDS } COMMAND ${ CMAKE_COMMAND } -E copy ${ CMAKE_CURRENT_BINARY_DIR } /AndroidManifest.xml ${ QT_ANDROID_APP_PACKAGE_SOURCE_ROOT } /AndroidManifest.xml ) # copy the generated manifest
endif ( )
# newer NDK toolchains don't define ANDROID_STL_PREFIX anymore,
# so this is a fallback to the only supported value in recent versions
if ( NOT ANDROID_STL_PREFIX )
if ( ANDROID_STL MATCHES "^c\\+\\+_" )
set ( ANDROID_STL_PREFIX llvm-libc++ )
endif ( )
endif ( )
if ( NOT ANDROID_STL_PREFIX )
message ( WARNING "Failed to determine ANDROID_STL_PREFIX value for ANDROID_STL=${ANDROID_STL}" )
2015-02-12 13:52:33 +01:00
endif ( )
2020-06-02 18:21:32 +02:00
if ( QT_ANDROID_SUPPORT_MULTI_ABI )
# from Qt 5.14 qtandroideploy will find the correct stl.
set ( QT_ANDROID_STL_PATH "${QT_ANDROID_NDK_ROOT}/sources/cxx-stl/${ANDROID_STL_PREFIX}/libs" )
else ( )
# define the STL shared library path
# up until NDK r18, ANDROID_STL_SHARED_LIBRARIES is populated by the NDK's toolchain file
# since NDK r19, the only option for a shared STL library is libc++_shared
if ( ANDROID_STL_SHARED_LIBRARIES )
list ( GET ANDROID_STL_SHARED_LIBRARIES 0 STL_LIBRARY_NAME ) # we can only give one to androiddeployqt
if ( ANDROID_STL_PATH )
set ( QT_ANDROID_STL_PATH "${ANDROID_STL_PATH}/libs/${ANDROID_ABI}/lib${ANDROID_STL}.so" )
else ( )
set ( QT_ANDROID_STL_PATH "${QT_ANDROID_NDK_ROOT}/sources/cxx-stl/${ANDROID_STL_PREFIX}/libs/${ANDROID_ABI}/lib${ANDROID_STL}.so" )
endif ( )
elseif ( ANDROID_STL STREQUAL c++_shared )
set ( QT_ANDROID_STL_PATH "${QT_ANDROID_NDK_ROOT}/sources/cxx-stl/${ANDROID_STL_PREFIX}/libs/${ANDROID_ABI}/libc++_shared.so" )
2018-11-08 15:23:33 +01:00
else ( )
2020-06-02 18:21:32 +02:00
message ( WARNING "ANDROID_STL (${ANDROID_STL}) isn't a known shared stl library."
" Y o u s h o u l d c o n s i d e r s e t t i n g A N D R O I D _ S T L t o c + + _shared ( like Qt ) . " )
set ( QT_ANDROID_STL_PATH "${QT_ANDROID_NDK_ROOT}/sources/cxx-stl/${ANDROID_STL_PREFIX}/libs/${ANDROID_ABI}/libc++_shared.so" )
2018-11-08 15:23:33 +01:00
endif ( )
2020-06-02 18:21:32 +02:00
endif ( )
# From Qt 5.14 qtandroideploy "target-architecture" is no longer valid in input file
# It have been replaced by "architectures": { "${ANDROID_ABI}": "${ANDROID_ABI}" }
# This allow to package multiple ABI in a single apk
# For now we only support single ABI build with this script (to ensure it work with Qt5.14 & Qt5.15)
if ( QT_ANDROID_SUPPORT_MULTI_ABI )
set ( QT_ANDROID_ARCHITECTURES "\" ${ ANDROID_ABI } \":\"${ANDROID_ABI}\"")
2018-11-08 14:38:07 +01:00
endif ( )
2015-02-12 13:52:33 +01:00
# set the list of dependant libraries
if ( ARG_DEPENDS )
foreach ( LIB ${ ARG_DEPENDS } )
if ( TARGET ${ LIB } )
2015-02-12 15:34:57 +01:00
# item is a CMake target, extract the library path
2020-01-29 13:18:59 +01:00
set ( LIB "$<TARGET_FILE:${LIB}>" )
endif ( )
if ( EXTRA_LIBS )
set ( EXTRA_LIBS "${EXTRA_LIBS},${LIB}" )
else ( )
set ( EXTRA_LIBS "${LIB}" )
2015-02-12 13:52:33 +01:00
endif ( )
endforeach ( )
2015-02-12 15:34:57 +01:00
set ( QT_ANDROID_APP_EXTRA_LIBS "\" android-extra-libs\ ": \" ${ EXTRA_LIBS } \",")
2015-02-12 13:52:33 +01:00
endif ( )
2020-01-29 13:18:59 +01:00
# determine whether to use the gcc- or llvm/clang- toolchain;
# if ANDROID_USE_LLVM was explicitly set, use its value directly,
# otherwise ANDROID_TOOLCHAIN value (set by the NDK's toolchain file)
# says whether llvm/clang or gcc is used
if ( DEFINED ANDROID_USE_LLVM )
string ( TOLOWER "${ANDROID_USE_LLVM}" QT_ANDROID_USE_LLVM )
elseif ( ANDROID_TOOLCHAIN STREQUAL clang )
set ( QT_ANDROID_USE_LLVM "true" )
else ( )
set ( QT_ANDROID_USE_LLVM "false" )
endif ( )
2018-11-08 14:38:07 +01:00
# set some toolchain variables used by androiddeployqt;
# unfortunately, Qt tries to build paths from these variables although these full paths
2020-01-29 13:18:59 +01:00
# are already available in the toochain file, so we have to parse them if using gcc
if ( QT_ANDROID_USE_LLVM STREQUAL "true" )
set ( QT_ANDROID_TOOLCHAIN_PREFIX "llvm" )
set ( QT_ANDROID_TOOLCHAIN_VERSION )
set ( QT_ANDROID_TOOL_PREFIX "llvm" )
2018-11-08 14:38:07 +01:00
else ( )
2020-01-29 13:18:59 +01:00
string ( REGEX MATCH "${QT_ANDROID_NDK_ROOT}/toolchains/(.*)-(.*)/prebuilt/.*/bin/(.*)-" ANDROID_TOOLCHAIN_PARSED ${ ANDROID_TOOLCHAIN_PREFIX } )
if ( ANDROID_TOOLCHAIN_PARSED )
set ( QT_ANDROID_TOOLCHAIN_PREFIX ${ CMAKE_MATCH_1 } )
set ( QT_ANDROID_TOOLCHAIN_VERSION ${ CMAKE_MATCH_2 } )
set ( QT_ANDROID_TOOL_PREFIX ${ CMAKE_MATCH_3 } )
else ( )
message ( FATAL_ERROR "Failed to parse ANDROID_TOOLCHAIN_PREFIX to get toolchain prefix and version and tool prefix" )
endif ( )
2018-11-08 14:38:07 +01:00
endif ( )
2015-02-12 13:52:33 +01:00
# make sure that the output directory for the Android package exists
2020-01-29 13:18:59 +01:00
set ( QT_ANDROID_APP_BINARY_DIR ${ CMAKE_CURRENT_BINARY_DIR } / ${ SOURCE_TARGET } - ${ ANDROID_ABI } )
file ( MAKE_DIRECTORY ${ QT_ANDROID_APP_BINARY_DIR } /libs/ ${ ANDROID_ABI } )
2015-02-12 13:52:33 +01:00
# create the configuration file that will feed androiddeployqt
2020-01-29 13:18:59 +01:00
# 1. replace placeholder variables at generation time
configure_file ( ${ QT_ANDROID_SOURCE_DIR } /qtdeploy.json.in ${ CMAKE_CURRENT_BINARY_DIR } /qtdeploy.json.in @ONLY )
# 2. evaluate generator expressions at build time
file ( GENERATE
O U T P U T $ { C M A K E _ C U R R E N T _ B I N A R Y _ D I R } / q t d e p l o y . j s o n
I N P U T $ { C M A K E _ C U R R E N T _ B I N A R Y _ D I R } / q t d e p l o y . j s o n . i n
)
# 3. Configure build.gradle to properly work with Android Studio import
set ( QT_ANDROID_NATIVE_API_LEVEL ${ ANDROID_NATIVE_API_LEVEL } )
configure_file ( ${ QT_ANDROID_SOURCE_DIR } /build.gradle.in ${ QT_ANDROID_APP_BINARY_DIR } /build.gradle @ONLY )
2015-02-12 13:52:33 +01:00
# check if the apk must be signed
if ( ARG_KEYSTORE )
2020-01-29 13:18:59 +01:00
set ( SIGN_OPTIONS --sign ${ ARG_KEYSTORE } )
2015-02-12 13:52:33 +01:00
if ( ARG_KEYSTORE_PASSWORD )
set ( SIGN_OPTIONS ${ SIGN_OPTIONS } --storepass ${ ARG_KEYSTORE_PASSWORD } )
endif ( )
endif ( )
2015-11-06 11:22:46 +01:00
# check if the apk must be installed to the device
2015-02-12 13:52:33 +01:00
if ( ARG_INSTALL )
2015-11-06 11:22:46 +01:00
set ( INSTALL_OPTIONS --reinstall )
2015-02-12 13:52:33 +01:00
endif ( )
2017-05-19 12:08:45 +02:00
# specify the Android API level
2018-11-08 14:38:07 +01:00
if ( ANDROID_PLATFORM_LEVEL )
set ( TARGET_LEVEL_OPTIONS --android-platform android- ${ ANDROID_PLATFORM_LEVEL } )
2017-05-19 12:08:45 +02:00
endif ( )
2020-01-29 13:18:59 +01:00
# determine the build type to pass to androiddeployqt
2020-02-17 14:02:11 +01:00
if ( "${CMAKE_BUILD_TYPE}" STREQUAL "Debug" AND NOT ARG_KEYSTORE )
2020-01-29 13:18:59 +01:00
set ( QT_ANDROID_BUILD_TYPE --debug )
elseif ( )
set ( QT_ANDROID_BUILD_TYPE --release )
endif ( )
2015-02-12 13:52:33 +01:00
# create a custom command that will run the androiddeployqt utility to prepare the Android package
add_custom_target (
$ { T A R G E T }
2017-05-19 14:58:12 +02:00
D E P E N D S $ { S O U R C E _ T A R G E T }
2020-01-29 13:18:59 +01:00
$ { Q T _ A N D R O I D _ P R E _ C O M M A N D S }
# it seems that recompiled libraries are not copied if we don't remove them first
C O M M A N D $ { C M A K E _ C O M M A N D } - E r e m o v e _ d i r e c t o r y $ { Q T _ A N D R O I D _ A P P _ B I N A R Y _ D I R } / l i b s / $ { A N D R O I D _ A B I }
C O M M A N D $ { C M A K E _ C O M M A N D } - E m a k e _ d i r e c t o r y $ { Q T _ A N D R O I D _ A P P _ B I N A R Y _ D I R } / l i b s / $ { A N D R O I D _ A B I }
C O M M A N D $ { C M A K E _ C O M M A N D } - E c o p y $ { Q T _ A N D R O I D _ A P P _ P A T H } $ { Q T _ A N D R O I D _ A P P _ B I N A R Y _ D I R } / l i b s / $ { A N D R O I D _ A B I }
C O M M A N D $ { Q T _ A N D R O I D _ Q T _ R O O T } / b i n / a n d r o i d d e p l o y q t
- - v e r b o s e
- - o u t p u t $ { Q T _ A N D R O I D _ A P P _ B I N A R Y _ D I R }
- - i n p u t $ { C M A K E _ C U R R E N T _ B I N A R Y _ D I R } / q t d e p l o y . j s o n
- - g r a d l e
$ { Q T _ A N D R O I D _ B U I L D _ T Y P E }
$ { T A R G E T _ L E V E L _ O P T I O N S }
$ { I N S T A L L _ O P T I O N S }
$ { S I G N _ O P T I O N S }
2015-02-12 13:52:33 +01:00
)
endmacro ( )