aboutsummaryrefslogtreecommitdiff
path: root/toolchain/Android.cmake
blob: 8272ef9d55ea3426a2e4ffa1f8db3d1692dd10a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#.rst:
# AndroidToolchain
# -------------------
#
# Enable easy compilation of cmake projects on Android
#
# By using this android toolchain, the projects will be set up to compile the
# specified project targeting an Android platform, depending on its input.
# Furthermore, if desired, an APK can be directly generated by using the
# androiddeployqt tool.
#
# Note: Requires CMake 3.1
#
# How to use it?
# --------------
# First of all, to make use of this toolchain file it's required to specify the
# CMAKE_TOOLCHAIN_FILE variable pointing to AndroidToolchain.cmake.
#
# Then, there's many settings that we may want to specify under what circumstances
# the project will be built. This will be done through environment variables:
# - ANDROID_NDK: points to the NDK root path
# - ANDROID_SDK_ROOT: points to the SDK root path
#
# Also there's some cache variables we can pass as well to narrow down the
# preferred settings:
# - ANDROID_NDK: Points to the NDK root, defaults to the environment variable
# with the same name.
# - ANDROID_SDK_ROOT: Points to the Android SDK root, defaults to the environment
# variable with the same name.
# - ANDROID_ARCHITECTURE: Specifies the used architecture, "arm" by default. See
# arch-* directory.
# - ANDROID_TOOLCHAIN: Specifies the toolchain to be used. Defaults to
# "arm-linux-androideabi". See <ndk>/toolchains/ directory.
# - ANDROID_ABI: Specifies the ABI to be used. Defaults to "armeabi-v7a". See
# <ndk>/sources/cxx-stl/gnu-libstdc++/*/libs/ directories.
# - ANDROID_GCC_VERSION: Specifies the GCC version. Defaults to "4.9".
# - ANDROID_API_LEVEL: Specifies the API level to require. Defaults to "14". See
# http://developer.android.com/guide/topics/manifest/uses-sdk-element.html
# - ANDROID_SDK_BUILD_TOOLS_REVISION: Specifies the build tools version to be used.
# Defaults to "21.1.1".
#
# Once we have the application built, we will want to generate an APK that we
# can run on an Android device. To this end we've integrated androiddeployqt so
# this can be done easily. This won't work with non-qt projects.
# To make use of the APK generation, we'll define QTANDROID_EXPORTED_TARGET with
# the target we want to have exported.
# Additionally, we'll need to specify a ANDROID_APK_DIR with the base information
# to set the project up. For more information see:
# https://qt-project.org/doc/qt-5-snapshot/deployment-android.html
#
# Once set up, after building, make create-apk-<target_name> will process this
# input and generate an apk that can be found inside the build directory:
# ${CMAKE_BINARY_DIR}/<target_name>_build_apk/bin/QtApp-*.apk.
#
# =============================================================================
# Copyright 2014 Aleix Pol i Gonzalez <aleixpol@kde.org>
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file COPYING-CMAKE-SCRIPTS for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of extra-cmake-modules, substitute the full
#  License text for the above reference.)

cmake_minimum_required(VERSION "3.1")

#input
set(ANDROID_NDK "$ENV{ANDROID_NDK}" CACHE path "Android NDK path")
set(ANDROID_SDK_ROOT "$ENV{ANDROID_SDK_ROOT}" CACHE path "Android SDK path")
set(ANDROID_ARCHITECTURE "arm" CACHE string "Used Architecture, related to the ABI and TOOLCHAIN")
set(ANDROID_TOOLCHAIN "arm-linux-androideabi" CACHE string "Used SDK")
set(ANDROID_ABI "armeabi-v7a" CACHE string "Used ABI")
set(ANDROID_GCC_VERSION "4.9" CACHE string "Used GCC version" )
set(ANDROID_API_LEVEL "14" CACHE string "Android API Level")
set(ANDROID_SDK_BUILD_TOOLS_REVISION "21.1.1" CACHE string "Android API Level")

set(_HOST "${CMAKE_HOST_SYSTEM_NAME}-${CMAKE_HOST_SYSTEM_PROCESSOR}")
string(TOLOWER "${_HOST}" _HOST)

get_filename_component(_CMAKE_ANDROID_DIR "${CMAKE_TOOLCHAIN_FILE}" PATH)

cmake_policy(SET CMP0011 OLD)
cmake_policy(SET CMP0017 OLD)

set(CMAKE_SYSROOT
    "${ANDROID_NDK}/platforms/android-${ANDROID_API_LEVEL}/arch-${ANDROID_ARCHITECTURE}")
if(NOT EXISTS ${CMAKE_SYSROOT})
    message(FATAL_ERROR "Couldn't find the Android NDK Root in ${CMAKE_SYSROOT}")
endif()

#actual code
SET(CMAKE_SYSTEM_NAME Android)
SET(CMAKE_SYSTEM_VERSION 1)

set(ANDROID_TOOLCHAIN_ROOT "${ANDROID_NDK}/toolchains/${ANDROID_TOOLCHAIN}-${ANDROID_GCC_VERSION}/prebuilt/${_HOST}/bin")
set(ANDROID_LIBS_ROOT "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_GCC_VERSION}")

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM "${ANDROID_TOOLCHAIN_ROOT}")
set(ANDROID_LIBRARIES_PATH
    "${CMAKE_SYSROOT}/usr/lib")
set(CMAKE_SYSTEM_LIBRARY_PATH
    ${ANDROID_LIBRARIES_PATH}
    "${ANDROID_LIBS_ROOT}/libs/${ANDROID_ABI}/"
)
set(CMAKE_FIND_LIBRARY_SUFFIXES ".so")
set(CMAKE_FIND_LIBRARY_PREFIXES "lib")
find_library(GNUSTL_SHARED gnustl_shared)
if(NOT GNUSTL_SHARED)
    message(FATAL_ERROR "you need gnustl_shared: ${CMAKE_SYSTEM_LIBRARY_PATH}")
endif()
include_directories(SYSTEM
    "${CMAKE_SYSROOT}/usr/include"
    "${ANDROID_LIBS_ROOT}/include/"
    "${ANDROID_LIBS_ROOT}/libs/${ANDROID_ABI}/include"
)

link_directories(${CMAKE_SYSTEM_LIBRARY_PATH})

set(CMAKE_C_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN}-gcc")
set(CMAKE_CXX_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN}-g++")

SET(CMAKE_FIND_ROOT_PATH ${ANDROID_NDK})
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

set(CMAKE_EXE_LINKER_FLAGS "${GNUSTL_SHARED} -Wl,-rpath-link,${ANDROID_LIBRARIES_PATH} -llog -lz -lm -ldl -lc -lgcc" CACHE STRING "")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}" CACHE STRING "")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}" CACHE STRING "")

#we want executables to be shared libraries, hooks will invoke the exported cmake function
set(CMAKE_CXX_LINK_EXECUTABLE
    "<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>"
)

######### generation

set(CREATEAPK_TARGET_NAME "create-apk-${QTANDROID_EXPORTED_TARGET}")
if(DEFINED QTANDROID_EXPORTED_TARGET AND NOT TARGET ${CREATEAPK_TARGET_NAME})
    if(NOT EXISTS "${ANDROID_APK_DIR}/AndroidManifest.xml")
        message(FATAL_ERROR "Define an apk dir to initialize from using -DANDROID_APK_DIR=<path>. The specified directory must contain the AndroidManifest.xml file.")
    endif()

    function(EOFHook)
        if(CMAKE_PARENT_LIST_FILE STREQUAL "")
            generate_deployment_file()
        endif()
    endfunction()

    function(generate_deployment_file)
        get_property(_DEPENDENCIES TARGET ${QTANDROID_EXPORTED_TARGET} PROPERTY INTERFACE_LINK_LIBRARIES)
        set(_DEPS_LIST)
        foreach(_DEP IN LISTS _DEPENDENCIES)
            if(NOT _DEP MATCHES "Qt5::.*")
                get_property(_DEP_LOCATION TARGET ${_DEP} PROPERTY "LOCATION_${CMAKE_BUILD_TYPE}")
                list(APPEND _DEPS_LIST ${_DEP_LOCATION})
            endif()
        endforeach()
        string(REPLACE ";" "," _DEPS "${_DEPS_LIST}")
        configure_file("${_CMAKE_ANDROID_DIR}/deployment-file.json.in" "${QTANDROID_EXPORTED_TARGET}-deployment.json")
    endfunction()

#   Create the target that will eventually generate the apk
    get_filename_component(QTDIR "${Qt5Core_DIR}/../../../" ABSOLUTE)
    find_program(ANDROID_DEPLOY_QT androiddeployqt HINTS "${QTDIR}/bin")
    set(EXPORT_DIR "${CMAKE_BINARY_DIR}/${QTANDROID_EXPORTED_TARGET}_build_apk/")
    set(EXECUTABLE_DESTINATION_PATH "${EXPORT_DIR}/libs/${ANDROID_ABI}/lib${QTANDROID_EXPORTED_TARGET}.so")

    add_custom_target(${CREATEAPK_TARGET_NAME}
        COMMAND cmake -E echo "Generating $<TARGET_NAME:${QTANDROID_EXPORTED_TARGET}> with ${ANDROID_DEPLOY_QT}"
        COMMAND cmake -E copy_directory "${ANDROID_APK_DIR}" "${EXPORT_DIR}"
        COMMAND cmake -E copy "$<TARGET_FILE:${QTANDROID_EXPORTED_TARGET}>" "${EXECUTABLE_DESTINATION_PATH}"
        COMMAND ${ANDROID_DEPLOY_QT} --input "${QTANDROID_EXPORTED_TARGET}-deployment.json" --output "${EXPORT_DIR}" --deployment bundled "\\$(ARGS)"
    )

    #we want to call the function after the project has been set up
    variable_watch(CMAKE_PARENT_LIST_FILE EOFHook)
else()
    message(STATUS "You can export a target by specifying -DQTANDROID_EXPORTED_TARGET=<targetname>")
endif()