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.
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 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 get_property(_languages GLOBAL PROPERTY ENABLED_LANGUAGES)
0190 if (NOT TARGET Threads::Threads AND (NOT DEFINED __qt_chainload_toolchain_file OR NOT "C" IN_LIST _languages))
0191     set(Threads_FOUND TRUE)
0192     set(CMAKE_THREAD_LIBS_INIT "-pthread")
0193     add_library(Threads::Threads INTERFACE IMPORTED)
0194     set_property(TARGET Threads::Threads PROPERTY INTERFACE_COMPILE_OPTIONS "-pthread")
0195     set_property(TARGET Threads::Threads PROPERTY INTERFACE_LINK_LIBRARIES "-pthread")
0196 endif()
0197 
0198 # let the Android NDK toolchain file do the actual work
0199 set(ANDROID_PLATFORM "android-${CMAKE_ANDROID_API}")
0200 set(ANDROID_STL ${CMAKE_ANDROID_STL_TYPE})
0201 include(${CMAKE_ANDROID_NDK}/build/cmake/android.toolchain.cmake REQUIRED)
0202 
0203 # Export configurable variables for the try_compile() command.
0204 list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
0205   CMAKE_ANDROID_NDK
0206   CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION
0207   CMAKE_ANDROID_API
0208   CMAKE_ANDROID_ARCH
0209   CMAKE_ANDROID_ARCH_ABI
0210   ANDROID_SDK_ROOT
0211   ANDROID_SDK_COMPILE_API
0212 )
0213 
0214 ## HACK: Remove when we can depend on NDK r23
0215 # Workaround issue https://github.com/android/ndk/issues/929
0216 if(ANDROID_NDK_MAJOR VERSION_LESS 23)
0217     unset(CMAKE_SYSROOT)
0218     set(ANDROID_SYSROOT_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/sysroot/usr")
0219 
0220     list(APPEND CMAKE_SYSTEM_INCLUDE_PATH
0221       "${ANDROID_SYSROOT_PREFIX}/include/${CMAKE_LIBRARY_ARCHITECTURE}")
0222     list(APPEND CMAKE_SYSTEM_INCLUDE_PATH "${ANDROID_SYSROOT_PREFIX}/include")
0223 
0224     # Prepend in reverse order
0225     list(PREPEND CMAKE_SYSTEM_LIBRARY_PATH
0226       "${ANDROID_SYSROOT_PREFIX}/lib/${CMAKE_LIBRARY_ARCHITECTURE}")
0227     list(PREPEND CMAKE_SYSTEM_LIBRARY_PATH
0228       "${ANDROID_SYSROOT_PREFIX}/lib/${CMAKE_LIBRARY_ARCHITECTURE}/${ANDROID_PLATFORM_LEVEL}")
0229 endif()
0230 
0231 # these aren't set yet at this point by the Android toolchain, but without
0232 # those the find_package() call in ECMAndroidDeployQt will fail
0233 set(CMAKE_FIND_LIBRARY_PREFIXES "lib")
0234 set(CMAKE_FIND_LIBRARY_SUFFIXES "_${CMAKE_ANDROID_ARCH_ABI}.so" ".so" ".a")
0235 
0236 # Work around Qt messing with CMAKE_SHARED_LIBRARY_SUFFIX and thus breaking find_library()
0237 # Unfortunately, just setting CMAKE_FIND_LIBRARY_SUFFIXES here won't help, as this will
0238 # be subsequently overwritten.
0239 macro(addAbiSuffix _var _access)
0240      if (${_access} STREQUAL "MODIFIED_ACCESS")
0241          list(PREPEND CMAKE_FIND_LIBRARY_SUFFIXES "_${CMAKE_ANDROID_ARCH_ABI}.so")
0242      endif()
0243 endmacro()
0244 variable_watch(CMAKE_FIND_LIBRARY_SUFFIXES addAbiSuffix)
0245 
0246 # determine STL architecture, which is using a different format than ANDROID_ARCH_ABI
0247 string(REGEX REPLACE "-(clang)?([0-9].[0-9])?$" "" ECM_ANDROID_STL_ARCH ${ANDROID_TOOLCHAIN_NAME})
0248 
0249 if (NOT DEFINED ECM_ADDITIONAL_FIND_ROOT_PATH)
0250     SET(ECM_ADDITIONAL_FIND_ROOT_PATH ${CMAKE_PREFIX_PATH})
0251 endif()
0252 
0253 LIST(APPEND CMAKE_FIND_ROOT_PATH ${ECM_ADDITIONAL_FIND_ROOT_PATH})
0254 
0255 #we want executables to be shared libraries, hooks will invoke the exported cmake function
0256 set(CMAKE_CXX_LINK_EXECUTABLE
0257     "<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>"
0258 )
0259 
0260 # As our executables are shared libraries, we also need them build with position independent code (PIC).
0261 # Qt 5 forces that anyway, but in Qt 6 that is no longer the case for exectuables (which we pretend to build here),
0262 # and so we end up with just PIE (coming from CMake).
0263 # And as subsequent steps overwrite that setting again, we have to watch for that and redo our change.
0264 set(CMAKE_CXX_COMPILE_OPTIONS_PIE "-fPIC")
0265 macro(resetPieOption _var _access)
0266     if (${_access} STREQUAL "MODIFIED_ACCESS")
0267         set(CMAKE_CXX_COMPILE_OPTIONS_PIE "-fPIC")
0268     endif()
0269 endmacro()
0270 variable_watch(CMAKE_CXX_COMPILE_OPTIONS_PIE resetPieOption)
0271 
0272 set(ECM_DIR "${CMAKE_CURRENT_LIST_DIR}/../cmake" CACHE STRING "")
0273 
0274 ######### generation
0275 
0276 # Need to ensure we only get in here once, as this file is included twice:
0277 # from CMakeDetermineSystem.cmake and from CMakeSystem.cmake generated within the
0278 # build directory.
0279 if(DEFINED QTANDROID_EXPORTED_TARGET AND NOT TARGET "create-apk")
0280     get_filename_component(_CMAKE_ANDROID_DIR "${CMAKE_TOOLCHAIN_FILE}" PATH)
0281     list(LENGTH QTANDROID_EXPORTED_TARGET targetsCount)
0282     include(${_CMAKE_ANDROID_DIR}/ECMAndroidDeployQt.cmake)
0283 
0284     math(EXPR last "${targetsCount}-1")
0285     foreach(idx RANGE 0 ${last})
0286         list(GET QTANDROID_EXPORTED_TARGET ${idx} exportedTarget)
0287         list(GET ANDROID_APK_DIR ${idx} APK_DIR)
0288         if(APK_DIR AND NOT EXISTS "${ANDROID_APK_DIR}/AndroidManifest.xml" AND IS_ABSOLUTE ANDROID_APK_DIR)
0289             message(FATAL_ERROR "Cannot find ${APK_DIR}/AndroidManifest.xml according to ANDROID_APK_DIR. ${ANDROID_APK_DIR} ${exportedTarget}")
0290         elseif(NOT APK_DIR)
0291             get_filename_component(_qt5Core_install_prefix "${Qt5Core_DIR}/../../../" ABSOLUTE)
0292             set(APK_DIR "${_qt5Core_install_prefix}/src/android/templates/")
0293         endif()
0294 
0295         ecm_androiddeployqt("${exportedTarget}" "${ECM_ADDITIONAL_FIND_ROOT_PATH}")
0296         set_target_properties(create-apk-${exportedTarget} PROPERTIES ANDROID_APK_DIR "${APK_DIR}")
0297     endforeach()
0298 else()
0299     message(STATUS "You can export a target by specifying -DQTANDROID_EXPORTED_TARGET=<targetname> and -DANDROID_APK_DIR=<paths>")
0300 endif()