Warning, /frameworks/extra-cmake-modules/toolchain/Android.cmake is written in an unsupported language. File is not indexed.

0001 # SPDX-FileCopyrightText: 2014 Aleix Pol i Gonzalez <aleixpol@kde.org>
0002 #
0003 # SPDX-License-Identifier: BSD-3-Clause
0004 
0005 #[=======================================================================[.rst:
0006 AndroidToolchain
0007 ----------------
0008 
0009 Enable easy compilation of cmake projects on Android.
0010 
0011 By using this android toolchain, the projects will be set up to compile the
0012 specified project targeting an Android platform, depending on its input.
0013 Furthermore, if desired, an APK can be directly generated by using the
0014 `androiddeployqt <https://doc.qt.io/qt-5/deployment-android.html>`_ tool.
0015 
0016 CMake upstream has Android support now. This module will still give us some
0017 useful features offering androiddeployqt integration and adequate executables
0018 format for our Android applications.
0019 
0020 Since we are using CMake Android support, any information from CMake documentation
0021 still applies:
0022 https://cmake.org/cmake/help/v3.7/manual/cmake-toolchains.7.html#cross-compiling-for-android
0023 
0024 .. note::
0025 
0026   This module requires CMake 3.18.
0027 
0028 Since 1.7.0.
0029 
0030 Usage
0031 =====
0032 
0033 To use this file, you need to set the ``CMAKE_TOOLCHAIN_FILE`` to point to
0034 ``Android.cmake`` on the command line::
0035 
0036   cmake -DCMAKE_TOOLCHAIN_FILE=/usr/share/ECM/toolchain/Android.cmake
0037 
0038 You will also need to provide the locations of the Android NDK and SDK. This
0039 can be done on the commandline or with environment variables; in either case
0040 the variable names are:
0041 
0042 ``CMAKE_ANDROID_NDK``
0043     The NDK root path.
0044 ``ANDROID_SDK_ROOT``
0045     The SDK root path.
0046 
0047 Additional options are specified as cache variables (eg: on the command line):
0048 
0049 ``ANDROID_ABI``
0050     The ABI to use. See the ``sources/cxx-stl/gnu-libstdc++/*/libs``
0051     directories in the NDK. Default: ``armeabi-v7a``.
0052 ``ANDROID_SDK_COMPILE_API``
0053     The platform API level to compile against. May be different from the NDK
0054     target. Default: newest installed version (e.g. android-30).
0055 ``ANDROID_SDK_BUILD_TOOLS_REVISION``
0056     The build tools version to use.
0057     Default: newest installed version (e.g. ``30.0.2``).
0058 ``ANDROID_EXTRA_LIBS``
0059     The ";"-separated list of full paths to libs to include in resulting APK (Qt 5 only).
0060 
0061 For integrating other libraries which are not part of the Android toolchain,
0062 like Qt5, and installed to a separate prefix on the host system, the install
0063 prefixes of those libraries would be passed as alternative roots as list via
0064 ``ECM_ADDITIONAL_FIND_ROOT_PATH``. Since 5.30.0.
0065 
0066 For example, for integrating a Qt5 for Android present at
0067 ``~/Qt/5.14.2/android/`` and some other libraries installed to
0068 the prefix ``/opt/android/foo``, you would use::
0069 
0070   cmake \
0071     -DCMAKE_TOOLCHAIN_FILE=/usr/share/ECM/toolchain/Android.cmake \
0072     -DECM_ADDITIONAL_FIND_ROOT_PATH="~/Qt/5.14.2/android/;/opt/android/foo"
0073 
0074 If your project uses ``find_package()`` to locate build tools on the host
0075 system, make sure to pass ``CMAKE_FIND_ROOT_PATH_BOTH`` or
0076 ``NO_CMAKE_FIND_ROOT_PATH`` as argument in the call. See the
0077 ``find_package()`` documentation for more details.
0078 
0079 Deploying Qt 5 Applications
0080 ===========================
0081 
0082 After building the application, you will need to generate an APK that can be
0083 deployed to an Android device. This module integrates androiddeployqt support
0084 to help with this for Qt-based projects. To enable this, set the
0085 ``QTANDROID_EXPORTED_TARGET`` variable to the targets you wish to export as an
0086 APK (in a ;-separed list), as well as ``ANDROID_APK_DIR`` to a directory
0087 containing some basic information. This will create a ``create-apk-<target>``
0088 target that will generate the APK file.  See the `Qt on Android deployment
0089 documentation <https://doc.qt.io/qt-5/deployment-android.html>`_ for more
0090 information.
0091 
0092 For example, you could do::
0093 
0094   cmake \
0095     -DCMAKE_TOOLCHAIN_FILE=/usr/share/ECM/toolchain/Android.cmake \
0096     -DQTANDROID_EXPORTED_TARGET=myapp \
0097     -DANDROID_APK_DIR=myapp-apk
0098   make
0099   make create-apk-myapp
0100 
0101 You can specify the APK output directory by setting ``ANDROID_APK_OUTPUT_DIR``.
0102 Otherwise the APK can be found in ``myapp_build_apk/`` in the build directory.
0103 
0104 The create-apk-myapp target will be able to take an ARGS parameter with further
0105 arguments for androiddeployqt. For example, one can use::
0106 
0107   make create-apk-myapp ARGS="--install"
0108 
0109 To install the apk to test. To generate a signed apk, one can do it with the
0110 following syntax::
0111 
0112   make create-apk-myapp ARGS="--sign ~/my.keystore alias_name"
0113 
0114 In case it's needed for your application to set the APK directory from cmake
0115 scripting you can also set the directory as the ANDROID_APK_DIR property of
0116 the create-apk-myapp target.
0117 
0118 See Android documentation on how to create a keystore to use
0119 
0120 Advanced Options
0121 ================
0122 
0123 The following packaging options are mainly interesting for automation or integration
0124 with CI/CD pipelines:
0125 
0126 ``ANDROID_APK_OUTPUT_DIR``
0127     Specifies a folder where the generated APK files should be placed.
0128 ``ANDROID_FASTLANE_METADATA_OUTPUT_DIR``
0129     Specifies a folder where the generated metadata for the F-Droid store
0130     should be placed.
0131 ``ANDROIDDEPLOYQT_EXTRA_ARGS``
0132     Allows to pass additional arguments to `androiddeployqt`. This is an alternative to
0133     the `ARGS=` argument for `make` and unlike that works with all CMake generators.
0134 #]=======================================================================]
0135 
0136 cmake_minimum_required(VERSION "3.18")
0137 
0138 macro(set_deprecated_variable actual_variable deprecated_variable default_value)
0139     set(${deprecated_variable} "${default_value}" CACHE STRING "Deprecated. Use ${actual_variable}")
0140     if (NOT DEFINED ${actual_variable})
0141         set(${actual_variable} ${${deprecated_variable}})
0142     endif()
0143 endmacro()
0144 
0145 set_deprecated_variable(CMAKE_ANDROID_NDK ANDROID_NDK "$ENV{ANDROID_NDK}")
0146 set_deprecated_variable(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION ANDROID_GCC_VERSION "clang")
0147 set_deprecated_variable(CMAKE_ANDROID_API ANDROID_API_LEVEL "21")
0148 if(NOT DEFINED ENV{ANDROID_ARCH})
0149     set(ENV{ANDROID_ARCH} "arm")
0150 endif()
0151 set_deprecated_variable(CMAKE_ANDROID_ARCH ANDROID_ARCHITECTURE $ENV{ANDROID_ARCH})
0152 if(NOT DEFINED ENV{ANDROID_ARCH_ABI})
0153     set(ENV{ANDROID_ARCH_ABI} "armeabi-v7a")
0154 endif()
0155 set_deprecated_variable(CMAKE_ANDROID_ARCH_ABI ANDROID_ABI "$ENV{ANDROID_ARCH_ABI}")
0156 
0157 set(ANDROID_SDK_ROOT "$ENV{ANDROID_SDK_ROOT}" CACHE PATH "Android SDK path")
0158 
0159 file(GLOB platforms LIST_DIRECTORIES TRUE RELATIVE ${ANDROID_SDK_ROOT}/platforms ${ANDROID_SDK_ROOT}/platforms/*)
0160 list(SORT platforms COMPARE NATURAL)
0161 list(GET platforms -1 _default_platform)
0162 set(ANDROID_SDK_COMPILE_API "${_default_platform}" CACHE STRING "Android API Level")
0163 if(ANDROID_SDK_COMPILE_API MATCHES "^android-([0-9]+)$")
0164     set(ANDROID_SDK_COMPILE_API ${CMAKE_MATCH_1})
0165 endif()
0166 
0167 file(GLOB build-tools LIST_DIRECTORIES TRUE RELATIVE ${ANDROID_SDK_ROOT}/build-tools ${ANDROID_SDK_ROOT}/build-tools/*)
0168 list(SORT build-tools COMPARE NATURAL)
0169 list(GET build-tools -1 _default_sdk)
0170 set(ANDROID_SDK_BUILD_TOOLS_REVISION "${_default_sdk}" CACHE STRING "Android Build Tools version")
0171 
0172 set(CMAKE_SYSTEM_VERSION ${CMAKE_ANDROID_API})
0173 set(CMAKE_SYSTEM_NAME Android)
0174 if (NOT CMAKE_ANDROID_STL_TYPE)
0175     set(CMAKE_ANDROID_STL_TYPE c++_shared)
0176 endif()
0177 
0178 # Workaround link failure at FindThreads in CXX-only mode,
0179 # armv7 really doesn't like mixing PIC/PIE code.
0180 # Since we only have to care about a single compiler,
0181 # hard-code the values here.
0182 # Qt6 fixes this and breaks in nested CMake calls (e.g. try_compile) if we
0183 # define Threads::Threads here, at least if the "C" language is enabled. In
0184 # CXX-only projects we still need to do this unconditionally...
0185 #
0186 # We cannot use our usual Qt version check at this point though yet,
0187 # se check whether we are chainloaded by the Qt toolchain as an indicator
0188 # for Qt6.
0189 # When building Qt6Base itself the check does not work, hence we have
0190 # ECM_THREADS_WORKAROUND for that case which set to OFF in the Craft blueprints.
0191 if (NOT DEFINED ECM_THREADS_WORKAROUND)
0192     set(ECM_THREADS_WORKAROUND TRUE)
0193 endif()
0194 get_property(_languages GLOBAL PROPERTY ENABLED_LANGUAGES)
0195 if (ECM_THREADS_WORKAROUND AND NOT TARGET Threads::Threads AND (NOT DEFINED __qt_chainload_toolchain_file OR NOT "C" IN_LIST _languages))
0196     set(Threads_FOUND TRUE)
0197     set(CMAKE_THREAD_LIBS_INIT "-pthread")
0198     add_library(Threads::Threads INTERFACE IMPORTED)
0199     set_property(TARGET Threads::Threads PROPERTY INTERFACE_COMPILE_OPTIONS "-pthread")
0200     set_property(TARGET Threads::Threads PROPERTY INTERFACE_LINK_LIBRARIES "-pthread")
0201 endif()
0202 
0203 # let the Android NDK toolchain file do the actual work
0204 set(ANDROID_PLATFORM "android-${CMAKE_ANDROID_API}")
0205 set(ANDROID_STL ${CMAKE_ANDROID_STL_TYPE})
0206 include(${CMAKE_ANDROID_NDK}/build/cmake/android.toolchain.cmake REQUIRED)
0207 
0208 # Export configurable variables for the try_compile() command.
0209 list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
0210   CMAKE_ANDROID_NDK
0211   CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION
0212   CMAKE_ANDROID_API
0213   CMAKE_ANDROID_ARCH
0214   CMAKE_ANDROID_ARCH_ABI
0215   ANDROID_SDK_ROOT
0216   ANDROID_SDK_COMPILE_API
0217 )
0218 
0219 # needed for androiddeployqt's stdcpp-path setting
0220 if (ANDROID_TOOLCHAIN_ROOT)
0221     set(ANDROID_SYSROOT_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/sysroot/usr")
0222 else()
0223     set(ANDROID_SYSROOT_PREFIX "${CMAKE_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr")
0224 endif()
0225 
0226 ## HACK: Remove when we can depend on NDK r23
0227 # Workaround issue https://github.com/android/ndk/issues/929
0228 if(ANDROID_NDK_MAJOR VERSION_LESS 23)
0229     unset(CMAKE_SYSROOT)
0230 
0231     list(APPEND CMAKE_SYSTEM_INCLUDE_PATH
0232       "${ANDROID_SYSROOT_PREFIX}/include/${CMAKE_LIBRARY_ARCHITECTURE}")
0233     list(APPEND CMAKE_SYSTEM_INCLUDE_PATH "${ANDROID_SYSROOT_PREFIX}/include")
0234 
0235     # Prepend in reverse order
0236     list(PREPEND CMAKE_SYSTEM_LIBRARY_PATH
0237       "${ANDROID_SYSROOT_PREFIX}/lib/${CMAKE_LIBRARY_ARCHITECTURE}")
0238     list(PREPEND CMAKE_SYSTEM_LIBRARY_PATH
0239       "${ANDROID_SYSROOT_PREFIX}/lib/${CMAKE_LIBRARY_ARCHITECTURE}/${ANDROID_PLATFORM_LEVEL}")
0240 endif()
0241 
0242 # Qt6 still expects ABI suffixes but only applies them in its own qt_add_[executable|library] macros
0243 # as we typically don't use those we need another way to get those
0244 if (DEFINED __qt_chainload_toolchain_file) # indicator for Qt6, see above
0245     set(CMAKE_MODULE_LIBRARY_SUFFIX_C "_${CMAKE_ANDROID_ARCH_ABI}.so")
0246     set(CMAKE_MODULE_LIBRARY_SUFFIX_CXX "_${CMAKE_ANDROID_ARCH_ABI}.so")
0247     set(CMAKE_SHARED_LIBRARY_SUFFIX_C "_${CMAKE_ANDROID_ARCH_ABI}.so")
0248     set(CMAKE_SHARED_LIBRARY_SUFFIX_CXX "_${CMAKE_ANDROID_ARCH_ABI}.so")
0249 endif()
0250 
0251 # these aren't set yet at this point by the Android toolchain, but without
0252 # those the find_package() call in ECMAndroidDeployQt5 will fail
0253 set(CMAKE_FIND_LIBRARY_PREFIXES "lib")
0254 set(CMAKE_FIND_LIBRARY_SUFFIXES "_${CMAKE_ANDROID_ARCH_ABI}.so" ".so" ".a")
0255 
0256 # Work around Qt messing with CMAKE_SHARED_LIBRARY_SUFFIX and thus breaking find_library()
0257 # Unfortunately, just setting CMAKE_FIND_LIBRARY_SUFFIXES here won't help, as this will
0258 # be subsequently overwritten.
0259 macro(addAbiSuffix _var _access)
0260      if (${_access} STREQUAL "MODIFIED_ACCESS")
0261          list(PREPEND CMAKE_FIND_LIBRARY_SUFFIXES "_${CMAKE_ANDROID_ARCH_ABI}.so")
0262      endif()
0263 endmacro()
0264 variable_watch(CMAKE_FIND_LIBRARY_SUFFIXES addAbiSuffix)
0265 
0266 # determine STL architecture, which is using a different format than ANDROID_ARCH_ABI
0267 string(REGEX REPLACE "-(clang)?([0-9].[0-9])?$" "" ECM_ANDROID_STL_ARCH "${ANDROID_TOOLCHAIN_NAME}")
0268 
0269 if (NOT DEFINED ECM_ADDITIONAL_FIND_ROOT_PATH)
0270     SET(ECM_ADDITIONAL_FIND_ROOT_PATH ${CMAKE_PREFIX_PATH})
0271 endif()
0272 
0273 LIST(APPEND CMAKE_FIND_ROOT_PATH ${ECM_ADDITIONAL_FIND_ROOT_PATH})
0274 
0275 #we want executables to be shared libraries, hooks will invoke the exported cmake function
0276 set(CMAKE_CXX_LINK_EXECUTABLE
0277     "<CMAKE_CXX_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>"
0278 )
0279 
0280 # As our executables are shared libraries, we also need them build with position independent code (PIC).
0281 # Qt 5 forces that anyway, but in Qt 6 that is no longer the case for exectuables (which we pretend to build here),
0282 # and so we end up with just PIE (coming from CMake).
0283 # And as subsequent steps overwrite that setting again, we have to watch for that and redo our change.
0284 set(CMAKE_CXX_COMPILE_OPTIONS_PIE "-fPIC")
0285 macro(resetPieOption _var _access)
0286     if (${_access} STREQUAL "MODIFIED_ACCESS")
0287         set(CMAKE_CXX_COMPILE_OPTIONS_PIE "-fPIC")
0288     endif()
0289 endmacro()
0290 variable_watch(CMAKE_CXX_COMPILE_OPTIONS_PIE resetPieOption)
0291 
0292 set(ECM_DIR "${CMAKE_CURRENT_LIST_DIR}/../cmake" CACHE STRING "")
0293 
0294 ######### generation (Qt 5 only)
0295 
0296 # Need to ensure we only get in here once, as this file is included twice:
0297 # from CMakeDetermineSystem.cmake and from CMakeSystem.cmake generated within the
0298 # build directory.
0299 if(DEFINED QTANDROID_EXPORTED_TARGET AND NOT TARGET "create-apk" AND NOT __qt_chainload_toolchain_file)
0300     get_filename_component(_CMAKE_ANDROID_DIR "${CMAKE_TOOLCHAIN_FILE}" PATH)
0301     list(LENGTH QTANDROID_EXPORTED_TARGET targetsCount)
0302     include(${_CMAKE_ANDROID_DIR}/ECMAndroidDeployQt5.cmake)
0303 
0304     math(EXPR last "${targetsCount}-1")
0305     foreach(idx RANGE 0 ${last})
0306         list(GET QTANDROID_EXPORTED_TARGET ${idx} exportedTarget)
0307         list(GET ANDROID_APK_DIR ${idx} APK_DIR)
0308         if(APK_DIR AND NOT EXISTS "${ANDROID_APK_DIR}/AndroidManifest.xml" AND IS_ABSOLUTE ANDROID_APK_DIR)
0309             message(FATAL_ERROR "Cannot find ${APK_DIR}/AndroidManifest.xml according to ANDROID_APK_DIR. ${ANDROID_APK_DIR} ${exportedTarget}")
0310         elseif(NOT APK_DIR)
0311             get_filename_component(_qt5Core_install_prefix "${Qt5Core_DIR}/../../../" ABSOLUTE)
0312             set(APK_DIR "${_qt5Core_install_prefix}/src/android/templates/")
0313         endif()
0314 
0315         ecm_androiddeployqt5("${exportedTarget}" "${ECM_ADDITIONAL_FIND_ROOT_PATH}")
0316         set_target_properties(create-apk-${exportedTarget} PROPERTIES ANDROID_APK_DIR "${APK_DIR}")
0317     endforeach()
0318 elseif (NOT __qt_chainload_toolchain_file)
0319     message(STATUS "You can export a target by specifying -DQTANDROID_EXPORTED_TARGET=<targetname> and -DANDROID_APK_DIR=<paths>")
0320 endif()