Initial commit

This commit is contained in:
Laurent Gomila 2015-02-12 13:52:33 +01:00
commit 131f82a2ca
6 changed files with 2133 additions and 0 deletions

183
AddQtAndroidApk.cmake Normal file
View File

@ -0,0 +1,183 @@
cmake_minimum_required(VERSION 3.0)
cmake_policy(SET CMP0026 OLD) # allow use of the LOCATION target property
# 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()
# find the Qt root directory
if(NOT Qt5Core_DIR)
find_package(Qt5Core REQUIRED)
endif()
get_filename_component(QT_ANDROID_QT_ROOT "${Qt5Core_DIR}/../../.." ABSOLUTE)
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)
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()
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)
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")
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}")
# find ANT
if(NOT QT_ANDROID_ANT)
set(QT_ANDROID_ANT $ENV{ANT})
if(NOT QT_ANDROID_ANT)
find_program(QT_ANDROID_ANT NAME ant)
if(NOT QT_ANDROID_ANT)
message(FATAL_ERROR "Could not find ANT. Please add its directory to the PATH environment variable, or set the ANT environment variable or QT_ANDROID_ANT CMake variable to its path.")
endif()
endif()
endif()
message(STATUS "Found ANT: ${QT_ANDROID_ANT}")
include(CMakeParseArguments)
# define a macro to create an Android APK target
#
# example:
# add_qt_android_apk(my_app_apk my_app
# NAME "My App"
# 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
#)
#
macro(add_qt_android_apk TARGET SOURCE_TARGET)
# parse the macro arguments
cmake_parse_arguments(ARG "INSTALL" "NAME;PACKAGE_NAME;PACKAGE_SOURCES;KEYSTORE_PASSWORD" "DEPENDS;KEYSTORE" ${ARGN})
# check the configuration
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(ANT_CONFIG debug)
else()
set(ANT_CONFIG release)
endif()
# extract the full path of the source target binary
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
get_property(QT_ANDROID_APP_PATH TARGET ${SOURCE_TARGET} PROPERTY DEBUG_LOCATION)
else()
get_property(QT_ANDROID_APP_PATH TARGET ${SOURCE_TARGET} PROPERTY LOCATION)
endif()
# 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()
# define the application source package directory
if(ARG_PACKAGE_SOURCES)
set(QT_ANDROID_APP_PACKAGE_SOURCE_ROOT ${ARG_PACKAGE_SOURCES})
else()
# get app version
get_property(QT_ANDROID_APP_VERSION TARGET ${SOURCE_TARGET} PROPERTY VERSION)
# use the major version number for code version (must be a single number)
string(REGEX MATCH "[0-9]+" QT_ANDROID_APP_VERSION_CODE ${QT_ANDROID_APP_VERSION})
# create a subdirectory for the extra package sources
set(QT_ANDROID_APP_PACKAGE_SOURCE_ROOT "${CMAKE_CURRENT_BINARY_DIR}/package")
# generate a manifest from the template
configure_file(${QT_ANDROID_SOURCE_DIR}/AndroidManifest.xml.in ${QT_ANDROID_APP_PACKAGE_SOURCE_ROOT}/AndroidManifest.xml @ONLY)
endif()
# set the list of dependant libraries
if(ARG_DEPENDS)
set(EXTRA_LIBS)
foreach(LIB ${ARG_DEPENDS})
if(TARGET ${LIB})
# item is a CMake target
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
get_property(LIB_PATH TARGET ${LIB} PROPERTY DEBUG_LOCATION)
else()
get_property(LIB_PATH TARGET ${LIB} PROPERTY LOCATION)
endif()
list(APPEND EXTRA_LIBS ${LIB_PATH})
else()
# item is a path
list(APPEND EXTRA_LIBS ${LIB})
endif()
endforeach()
string (REPLACE ";" "," QT_ANDROID_APP_EXTRA_LIBS "${EXTRA_LIBS}")
else()
set(QT_ANDROID_APP_EXTRA_LIBS) # empty
endif()
# make sure that the output directory for the Android package exists
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/libs/${ANDROID_ABI})
# create the configuration file that will feed androiddeployqt
configure_file(${QT_ANDROID_SOURCE_DIR}/qtdeploy.json.in ${CMAKE_CURRENT_BINARY_DIR}/qtdeploy.json @ONLY)
# check if the apk must be signed
if(ARG_KEYSTORE)
set(SIGN_OPTIONS --release --sign ${ARG_KEYSTORE})
if(ARG_KEYSTORE_PASSWORD)
set(SIGN_OPTIONS ${SIGN_OPTIONS} --storepass ${ARG_KEYSTORE_PASSWORD})
endif()
endif()
# check if the apok must be installed to the device
if(ARG_INSTALL)
set(INSTALL_OPTIONS --install)
endif()
# create a custom command that will run the androiddeployqt utility to prepare the Android package
add_custom_command(
OUTPUT run_android_deploy_qt
DEPENDS ${SOURCE_TARGET}
COMMAND ${CMAKE_COMMAND} -E copy ${QT_ANDROID_APP_PATH} ${CMAKE_CURRENT_BINARY_DIR}/libs/${ANDROID_ABI}
COMMAND ${QT_ANDROID_QT_ROOT}/bin/androiddeployqt --verbose --output ${CMAKE_CURRENT_BINARY_DIR} --input ${CMAKE_CURRENT_BINARY_DIR}/qtdeploy.json --ant ${QT_ANDROID_ANT} ${INSTALL_OPTIONS} ${SIGN_OPTIONS}
)
# create the custom target that invokes ANT to create the apk
add_custom_target(
${TARGET}
ALL
COMMAND ${QT_ANDROID_ANT} ${ANT_CONFIG}
DEPENDS run_android_deploy_qt
)
endmacro()

33
AndroidManifest.xml.in Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0"?>
<manifest android:versionName="@QT_ANDROID_APP_VERSION@" package="@QT_ANDROID_APP_PACKAGE_NAME@" android:installLocation="auto" xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="@QT_ANDROID_APP_VERSION_CODE@">
<application android:label="@QT_ANDROID_APP_NAME@" android:name="org.qtproject.qt5.android.bindings.QtApplication">
<activity android:label="@QT_ANDROID_APP_NAME@" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:screenOrientation="unspecified" android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<!-- Messages maps -->
<meta-data android:name="android.app.ministro_not_found_msg" android:value="@string/ministro_not_found_msg"/>
<meta-data android:name="android.app.ministro_needed_msg" android:value="@string/ministro_needed_msg"/>
<meta-data android:name="android.app.fatal_error_msg" android:value="@string/fatal_error_msg"/>
</activity>
</application>
<supports-screens android:anyDensity="true" android:normalScreens="true" android:smallScreens="true" android:largeScreens="true"/>
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19"/>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

31
license.txt Normal file
View File

@ -0,0 +1,31 @@
Qt Android CMake utility script
-------------------------------
Copyright (c) 2015, Laurent Gomila (laurent.gom@gmail.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

16
qtdeploy.json.in Normal file
View File

@ -0,0 +1,16 @@
{
"description": "This file is to be read by androiddeployqt",
"qt": "@QT_ANDROID_QT_ROOT@",
"sdk": "@QT_ANDROID_SDK_ROOT@",
"ndk": "@QT_ANDROID_NDK_ROOT@",
"toolchain-prefix": "@ANDROID_TOOLCHAIN_MACHINE_NAME@",
"tool-prefix": "@ANDROID_TOOLCHAIN_MACHINE_NAME@",
"toolchain-version": "@ANDROID_COMPILER_VERSION@",
"ndk-host": "@ANDROID_NDK_HOST_SYSTEM_NAME@",
"target-architecture": "@ANDROID_ABI@",
"application-binary": "@QT_ANDROID_APP_PATH@",
"android-package": "@QT_ANDROID_APP_PACKAGE_NAME@",
"android-app-name": "@QT_ANDROID_APP_NAME@",
"android-extra-libs": "@QT_ANDROID_APP_EXTRA_LIBS@",
"android-package-source-directory": "@QT_ANDROID_APP_PACKAGE_SOURCE_ROOT@"
}

182
readme.md Normal file
View File

@ -0,0 +1,182 @@
# Qt Android CMake utility
## What it is
When using Qt for Android development, QMake & QtCreator is the only sane option for compiling and deploying. But if you prefer CMake, you're stuck and have no choice but writing .pro files that duplicate the functionality of your CMake files.
This utility tries to avoids this by providing a CMake way of doing Android compilation and deployment, without QtCreator. It is based on:
* the Android CMake toolchain at https://github.com/taka-no-me/android-cmake
* the ```androiddeployqt``` utility from the Qt Android SDK
* the QML / Android example at https://github.com/calincru/QML-Android-Demo
This utility has been developed for my own needs. Don't hesitate to use / share / fork / modify / improve it freely :)
## How to use it
### How to integrate it to your CMake configuration
The toolchain file defines the ```ANDROID``` variable, so that everything which is added specifically for Android in your CMake files can be surrounded with
```cmake
if(ANDROID)
...
endif()
```
The first thing to do is to change your executable target into a library, because on Android, the entry point has to be a Java activity, and your C++ code is then loaded (as a library) and called by this activity.
```cmake
if(ANDROID)
add_library(my_app SHARED ...)
else()
add_executable(my_app ...)
endif()
```
Then all you have to do is to call the ```add_qt_android_apk``` macro to create a new target that will create the Android APK.
```cmake
if(ANDROID)
include(qt-android-cmake/AddQtAndroidApk.cmake)
add_qt_android_apk(my_app_apk my_app)
endif()
```
And that's it. Your APK can now be created by running "make" (or "cmake --build ." if you don't want to bother typing the full path to the make.exe program included in the NDK).
Of course, ```add_qt_android_apk``` accepts more options, see below for the detail.
### How to run CMake
First, you must make sure that the following environment variables are defined:
* ```ANDROID_NDK```: root directory of the Android NDK
* ```JAVA_HOME```: root directory of the Java JDK
**IMPORTANT** ```JAVA_HOME``` must be defined when you compile the APK too.
Additionally you can define the following ones, but you can also define them as CMake variables if you prefer:
* ```ANDROID_SDK```: root directory of the Android SDK
* ```ANT```: Path of the ANT executable
You can then run CMake:.
**On Windows**
```
cmake -G"MinGW Makefiles"
-DCMAKE_TOOLCHAIN_FILE=<qt-android-cmake>/toolchain/android.toolchain.cmake
-DCMAKE_MAKE_PROGRAM="%ANDROID_NDK%/prebuilt/windows/bin/make.exe" .
```
**On Linux**
```
cmake -DCMAKE_TOOLCHAIN_FILE=path/to/the/android.toolchain.cmake .
```
**On Mac OS X**
```
This utility has not been tested on this OS yet :)
```
The Android toolchain can be customized with environment variables and/or CMake variables. Refer to its documentation (at the beginning of the toolchain file) for more details.
## Options of the ```add_qt_android_apk``` macro
The first two arguments of the macro are the name of the APK target to be created, and the target it must be based on (your executable). These are of course mandatory.
The macro also accepts optional named arguments. Any combination of these arguments is valid, so that you can customize the generated APK according to your own needs.
Here is the full list of possible arguments:
### NAME
The name of the application. If not given, the name of the source target is taken.
Example:
```cmake
add_qt_android_apk(my_app_apk my_app
NAME "My App"
)
```
### PACKAGE_NAME
The name of the application package. If not given, "org.qtproject.${source_target}" , where source_target is the name of the source target, is taken.
Example:
```cmake
add_qt_android_apk(my_app_apk my_app
PACKAGE_NAME "org.mycompany.myapp"
)
```
### PACKAGE_SOURCES
The path to a directory containing additional files for the package (custom manifest, resources, translations, Java classes, ...). If you were using a regular QMake project file (.pro), this directory would be the one that you assign to the ```ANDROID_PACKAGE_SOURCE_DIR``` variable.
If you don't provide this argument, a default manifest is generated from the ```AndroidManifest.xml.in``` template and automatically used for building the APK.
Example:
```cmake
add_qt_android_apk(my_app_apk my_app
PACKAGE_SOURCES ${CMAKE_CURRENT_LIST_DIR}/my-android-sources
)
```
### KEYSTORE
The path to a keystore file and an alias, for signing the APK. If not provided, the APK won't be signed.
Example:
```cmake
add_qt_android_apk(my_app_apk my_app
KEYSTORE ${CMAKE_CURRENT_LIST_DIR}/mykey.keystore myalias
)
```
### KEYSTORE_PASSWORD
The password associated to the gicen keystore. Note that this option is only considered if the ```KEYSTORE``` argument is used. If it is not given, the password will be asked directly in the console at build time.
Example:
```cmake
add_qt_android_apk(my_app_apk my_app
KEYSTORE ${CMAKE_CURRENT_LIST_DIR}/mykey.keystore myalias
KEYSTORE_PASSWORD xxxxx
)
```
### DEPENDS
A list of dependencies (libraries) to be included into the APK. All the dependencies of the application must be listed here; if one is missing, the deployed application will fail to run on the device. The listed items can be either target names, or library paths.
Example:
```cmake
add_qt_android_apk(my_app_apk my_app
DEPENDS a_linked_target "path/to/a_linked_library.so" etc.
)
```
### INSTALL
If this option is given, the created APK will be deployed to a connected Android device. By default, the chosen device is the default one, i.e. the first one of the ADB device list.
Example:
```cmake
add_qt_android_apk(my_app_apk my_app
INSTALL
)
```
## Contact
Laurent Gomila: laurent.gom@gmail.com

File diff suppressed because it is too large Load Diff