Android: Final changes for APK generation

Generate the ${MODULE}-android-dependencies.xml for the androiddeployqt
tool. This will ensure all the right plugins and dependencies are
packaged when generating the apk.

This change also changes the visibility for executable to default/public.
Not having this will cause the application to crash as we can't locate
main() in the executable (shared library).

Additionally pro2cmake conversion script has been updated to perform
the required conversions for the Android settings.

Finally, the 6 projects in QtBase that have Android dependencies have
been updated with the new script and the step that produces the xml
files in run at the end in QtPostProcess.cmake.

Change-Id: I9774ba1b123bc11cae972fa37054ef2c51988498
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Leander Beernaert 2019-06-24 14:54:40 +02:00
parent ffec6c7180
commit a9d2c5b6d7
11 changed files with 313 additions and 14 deletions

View File

@ -1293,6 +1293,14 @@ set(QT_CMAKE_EXPORT_NAMESPACE ${QT_CMAKE_EXPORT_NAMESPACE})")
PUBLIC_HEADER DESTINATION ${INSTALL_INCLUDEDIR}/${module}
PRIVATE_HEADER DESTINATION ${INSTALL_INCLUDEDIR}/${module}/${PROJECT_VERSION}/${module}/private
)
if (ANDROID)
# Record install library location so it can be accessed by
# qt_android_dependencies without having to specify it again.
set_target_properties(${target} PROPERTIES
QT_ANDROID_MODULE_INSTALL_DIR ${INSTALL_LIBDIR})
endif()
qt_install(EXPORT ${export_name}
NAMESPACE ${QT_CMAKE_EXPORT_NAMESPACE}::
DESTINATION ${config_install_dir})

View File

@ -0,0 +1,206 @@
#
# Android specific functions/macros/properties required for building Qt Modules
#
define_property(TARGET
PROPERTY
QT_ANDROID_MODULE_INSTALL_DIR
BRIEF_DOCS
"Recorded install location for a Qt Module."
FULL_DOCS
"Recorded install location for a Qt Module. Used by qt_android_dependencies()."
)
define_property(TARGET
PROPERTY
QT_ANDROID_JAR_DEPENDENCIES
BRIEF_DOCS
"Qt Module Jar dependencies list."
FULL_DOCS
"Qt Module Jar dependencies list."
)
define_property(TARGET
PROPERTY
QT_ANDROID_BUNDLED_JAR_DEPENDENCIES
BRIEF_DOCS
"Qt Module Jars that should be bundled with it during packing."
FULL_DOCS
"Qt Module Jars that should be bundled with it during packing."
)
define_property(TARGET
PROPERTY
QT_ANDROID_LIB_DEPENDENCIES
BRIEF_DOCS
"Qt Module C++ libraries that should be bundled with it during packing."
FULL_DOCS
"Qt Module C++ libraries that should be bundled with it during packing."
)
define_property(TARGET
PROPERTY
QT_ANDROID_LIB_DEPENDENCY_REPLACEMENTS
BRIEF_DOCS
"Qt Module C++ libraries that can replace libraries declared with the QT_ANDROID_LIB_DEPENDENCIES property."
FULL_DOCS
"Qt Module C++ libraries that can replace libraries declared with the QT_ANDROID_LIB_DEPENDENCIES property."
)
define_property(TARGET
PROPERTY
QT_ANDROID_BUNDLED_FILES
BRIEF_DOCS
"Qt Module files that need to be bundled during packing."
FULL_DOCS
"Qt Module files that need to be bundled during packing."
)
define_property(TARGET
PROPERTY
QT_ANDROID_PERMISSIONS
BRIEF_DOCS
"Qt Module android permission list."
FULL_DOCS
"Qt Module android permission list."
)
# Generate Qt Module -android-dependencies.xml required by the
# androiddeploytoolqt to successfully copy all the plugins and other dependent
# items into tha APK
function(qt_android_dependencies target)
get_target_property(arg_JAR_DEPENDENCIES ${target} QT_ANDROID_JAR_DEPENDENCIES)
get_target_property(arg_BUNDLED_JAR_DEPENDENCIES ${target} QT_ANDROID_BUNDLED_JAR_DEPENDENCIES)
get_target_property(arg_LIB_DEPENDENCIES ${target} QT_ANDROID_LIB_DEPENDENCIES)
get_target_property(arg_LIB_DEPENDENCY_REPLACEMENTS ${target} QT_ANDROID_LIB_DEPENDENCY_REPLACEMENTS)
get_target_property(arg_PERMISSIONS ${target} QT_ANDROID_PERMISSIONS)
get_target_property(module_plugins ${target} MODULE_PLUGIN_TYPES)
if ((NOT module_plugins)
AND (NOT arg_JAR_DEPENDENCIES)
AND (NOT arg_LIB_DEPENDENCY_REPLACEMENTS)
AND (NOT arg_LIB_DEPENDENCIES)
AND (NOT arg_BUNDLED_JAR_DEPENDENCIES)
AND (NOT arg_PERMISSIONS))
# None of the values were set, so there's nothing to do
return()
endif()
get_target_property(target_output_name ${target} OUTPUT_NAME)
if (NOT target_output_name)
set(target_name ${target})
else()
set(target_name ${target_output_name})
endif()
# mimic qmake's section and string splitting from
# mkspecs/feature/qt_android_deps.prf
macro(section string delimiter first second)
string(FIND ${string} ${delimiter} delimiter_location)
if (NOT ${delimiter_location} EQUAL -1)
string(SUBSTRING ${string} 0 ${delimiter_location} ${first})
math(EXPR delimiter_location "${delimiter_location} + 1")
string(SUBSTRING ${string} ${delimiter_location} -1 ${second})
else()
set(${first} ${string})
set(${second} "")
endif()
endmacro()
get_target_property(target_bin_dir ${target} BINARY_DIR)
set(dependency_file "${target_bin_dir}/${target_name}-android-dependencies.xml")
file(WRITE ${dependency_file} "<rules><dependencies>\n")
file(APPEND ${dependency_file} "<lib name=\"${target_name}\"><depends>\n")
# Jar Dependencies
if(arg_JAR_DEPENDENCIES)
foreach(jar_dependency IN LISTS arg_JAR_DEPENDENCIES)
section(${jar_dependency} ":" jar_file init_class)
if (init_class)
set(init_class "initClass=\"${init_class}\"")
endif()
file(TO_NATIVE_PATH ${jar_file} jar_file_native)
file(APPEND ${dependency_file} "<jar file=\"${jar_file_native}\" ${init_class} />\n")
endforeach()
endif()
# Bundled Jar Dependencies
if(arg_BUNDLED_JAR_DEPENDENCIES)
foreach(jar_bundle IN LISTS arg_BUNDLED_JAR_DEPENDENCIES)
section(${jar_bundle} ":" bundle_file init_calss)
if (init_class)
set(init_class "initClass=\"${init_class}\"")
endif()
file(TO_NATIVE_PATH ${jar_bundle} jar_bundle_native)
file(APPEND ${dependency_file} "<jar bundling=\"1\" file=\"${jar_bundle_native}\" ${init_class} />\n")
endforeach()
endif()
# Lib Dependencies
if(arg_LIB_DEPENDENCIES)
foreach(lib IN LISTS arg_LIB_DEPENDENCIES)
section(${lib} ":" lib_file lib_extends)
if (lib_extends)
set(lib_extends "extends=\"${lib_extends}\"")
endif()
file(TO_NATIVE_PATH ${lib_file} lib_file_native)
file(APPEND ${dependency_file} "<lib file=\"${lib_file_native}\" ${lib_extends} />\n")
endforeach()
endif()
# Lib Dependencies Replacements
if(arg_LIB_DEPENDENCY_REPLACEMENTS)
foreach(lib IN LISTS arg_LIB_DEPENDENCY_REPLACEMENTS)
section(${lib} ":" lib_file lib_replacement)
if (lib_replacement)
file(TO_NATIVE_PATH ${lib_replacement} lib_replacement_native)
set(lib_replacement "replaces=\"${lib_replacement_native}\"")
endif()
file(TO_NATIVE_PATH ${lib_file} lib_file_native)
file(APPEND ${dependency_file} "<lib file=\"${lib_file_native}\" ${lib_replacement} />\n")
endforeach()
endif()
# Bundled files
if(arg_BUNDLED_FILES)
foreach(file IN LISTS arg_BUNDLED_FILES)
file(TO_NATIVE_PATH ${lib_file} file_native)
file(APPEND ${dependency_file} "<bundled file=\"${file_native}\" />\n")
endforeach()
endif()
# Module plugins
if(module_plugins)
foreach(plugin IN LISTS module_plugins)
file(APPEND ${dependency_file} "<bundled file=\"plugins/${plugin}\" />\n")
endforeach()
endif()
# Android Permissions
if(arg_PERMISSIONS)
foreach(permission IN LISTS arg_PERMISSIONS)
file(APPEND ${dependency_file} "<permission name=\"${permission}\" />\n")
endforeach()
endif()
file(APPEND ${dependency_file} "</depends></lib>")
file(APPEND ${dependency_file} "</dependencies></rules>\n")
get_target_property(target_install_dir ${target} QT_ANDROID_MODULE_INSTALL_DIR)
if (NOT target_install_dir)
message(SEND_ERROR "qt_android_dependencies: Target ${target} is either not a Qt Module or has no recorded install location")
return()
endif()
# Copy file into install directory, required by the androiddeployqt tool.
qt_install(FILES
${dependency_file}
DESTINATION
${target_install_dir}
COMPONENT
Devel)
endfunction()

View File

@ -142,3 +142,7 @@ macro(qt_examples_build_end)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ${BACKUP_CMAKE_FIND_ROOT_PATH_MODE_PACKAGE})
endmacro()
if (ANDROID)
include(QtBuildInternals/QtBuildInternalsAndroid)
endif()

View File

@ -74,7 +74,7 @@ if (NOT QT_ANDROID_SDK_BUILD_TOOLS_VERSION)
list(SORT android_build_tools)
list(REVERSE android_build_tools)
list(GET android_build_tools 0 android_build_tools_latest)
set(qt_QT_ANDROID_SDK_BUILD_TOOLS_VERSION ${android_build_tools_latest})
set(QT_ANDROID_SDK_BUILD_TOOLS_VERSION ${android_build_tools_latest})
endif()
# Ensure we are using the shared version of libc++

View File

@ -276,6 +276,18 @@ function(qt_generate_build_internals_extra_cmake_code)
endif()
endfunction()
# For every Qt module check if there any android dependencies that require
# processing.
function(qt_modules_process_android_dependencies)
foreach (target ${QT_KNOWN_MODULES})
qt_android_dependencies(${target})
endforeach()
endfunction()
qt_internal_create_depends_files()
qt_generate_build_internals_extra_cmake_code()
qt_internal_create_plugins_files()
if (ANDROID)
qt_modules_process_android_dependencies()
endif()

View File

@ -241,10 +241,19 @@ add_qt_simd_part(Core SIMD mips_dsp
if(ANDROID)
set_property(TARGET Core APPEND PROPERTY QT_ANDROID_BUNDLED_JAR_DEPENDENCIES
jar/QtAndroid.jar
)
set_property(TARGET Core APPEND PROPERTY QT_ANDROID_LIB_DEPENDENCIES
plugins/platforms/android/libqtforandroid.so
)
set_property(TARGET Core APPEND PROPERTY QT_ANDROID_PERMISSIONS
android.permission.INTERNET android.permission.WRITE_EXTERNAL_STORAGE
)
endif()
#### Keys ignored in scope 1:.:.:corelib.pro:<TRUE>:
# ANDROID_BUNDLED_JAR_DEPENDENCIES = "jar/QtAndroid.jar"
# ANDROID_LIB_DEPENDENCIES = "plugins/platforms/android/libqtforandroid.so"
# ANDROID_PERMISSIONS = "android.permission.INTERNET" "android.permission.WRITE_EXTERNAL_STORAGE"
# CMAKE_DISABLED_FEATURES = "$$joinQT_DISABLED_FEATURES,"$$escape_expand(\\n) ""
# CMAKE_HOST_DATA_DIR = "$$cmakeRelativePath$$[QT_HOST_DATA/src],$$[QT_INSTALL_PREFIX]"
# CMAKE_INSTALL_DATA_DIR = "$$cmakeRelativePath$$[QT_HOST_DATA],$$[QT_INSTALL_PREFIX]"

View File

@ -323,10 +323,19 @@ add_qt_simd_part(Core SIMD mips_dsp
if(ANDROID)
set_property(TARGET Core APPEND PROPERTY QT_ANDROID_BUNDLED_JAR_DEPENDENCIES
jar/QtAndroid.jar
)
set_property(TARGET Core APPEND PROPERTY QT_ANDROID_LIB_DEPENDENCIES
plugins/platforms/android/libqtforandroid.so
)
set_property(TARGET Core APPEND PROPERTY QT_ANDROID_PERMISSIONS
android.permission.INTERNET android.permission.WRITE_EXTERNAL_STORAGE
)
endif()
#### Keys ignored in scope 1:.:.:corelib.pro:<TRUE>:
# ANDROID_BUNDLED_JAR_DEPENDENCIES = "jar/QtAndroid.jar"
# ANDROID_LIB_DEPENDENCIES = "plugins/platforms/android/libqtforandroid.so"
# ANDROID_PERMISSIONS = "android.permission.INTERNET" "android.permission.WRITE_EXTERNAL_STORAGE"
# CMAKE_DISABLED_FEATURES = "$$joinQT_DISABLED_FEATURES,"$$escape_expand(\\n) ""
# CMAKE_HOST_DATA_DIR = "$$cmakeRelativePath$$[QT_HOST_DATA/src],$$[QT_INSTALL_PREFIX]"
# CMAKE_INSTALL_DATA_DIR = "$$cmakeRelativePath$$[QT_HOST_DATA],$$[QT_INSTALL_PREFIX]"

View File

@ -399,6 +399,11 @@ function(add_qt_gui_executable target)
if(ANDROID)
qt_android_generate_deployment_settings("${target}")
# On our qmake builds we do don't compile the executables with
# visibility=hidden. Not having this flag set will cause the
# executable to have main() hidden and can then no longer be loaded
# through dlopen()
target_compile_options(${target} PRIVATE -fvisibility=default)
endif()
endfunction()

View File

@ -76,10 +76,19 @@ extend_target(Network CONDITION MSVC AND (TEST_architecture_arch STREQUAL "i386"
"/BASE:0x64000000"
)
if(ANDROID AND (QT_FEATURE_bearermanagement))
set_property(TARGET Network APPEND PROPERTY QT_ANDROID_BUNDLED_JAR_DEPENDENCIES
jar/QtAndroidBearer.jar
)
set_property(TARGET Network APPEND PROPERTY QT_ANDROID_LIB_DEPENDENCIES
plugins/bearer/libqandroidbearer.so
)
set_property(TARGET Network APPEND PROPERTY QT_ANDROID_PERMISSIONS
android.permission.ACCESS_NETWORK_STATE
)
endif()
#### Keys ignored in scope 3:.:.:network.pro:QT_FEATURE_bearermanagement:
# ANDROID_BUNDLED_JAR_DEPENDENCIES = "jar/QtAndroidBearer.jar"
# ANDROID_LIB_DEPENDENCIES = "plugins/bearer/libqandroidbearer.so"
# ANDROID_PERMISSIONS = "android.permission.ACCESS_NETWORK_STATE"
# MODULE_PLUGIN_TYPES = "bearer"
extend_target(Network CONDITION QT_FEATURE_ftp

View File

@ -76,10 +76,19 @@ extend_target(Network CONDITION MSVC AND (TEST_architecture_arch STREQUAL "i386"
"/BASE:0x64000000"
)
if(ANDROID AND (QT_FEATURE_bearermanagement))
set_property(TARGET Network APPEND PROPERTY QT_ANDROID_BUNDLED_JAR_DEPENDENCIES
jar/QtAndroidBearer.jar
)
set_property(TARGET Network APPEND PROPERTY QT_ANDROID_LIB_DEPENDENCIES
plugins/bearer/libqandroidbearer.so
)
set_property(TARGET Network APPEND PROPERTY QT_ANDROID_PERMISSIONS
android.permission.ACCESS_NETWORK_STATE
)
endif()
#### Keys ignored in scope 3:.:.:network.pro:QT_FEATURE_bearermanagement:
# ANDROID_BUNDLED_JAR_DEPENDENCIES = "jar/QtAndroidBearer.jar"
# ANDROID_LIB_DEPENDENCIES = "plugins/bearer/libqandroidbearer.so"
# ANDROID_PERMISSIONS = "android.permission.ACCESS_NETWORK_STATE"
# MODULE_PLUGIN_TYPES = "bearer"
extend_target(Network CONDITION QT_FEATURE_ftp

View File

@ -1536,6 +1536,32 @@ def write_simd_part(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in
header = 'add_qt_simd_part({} SIMD {}\n'.format(target, simd),
footer = ')\n\n')
def write_android_part(cm_fh: typing.IO[str], target: str, scope:Scope, indent: int = 0):
keys = [ 'ANDROID_BUNDLED_JAR_DEPENDENCIES'
, 'ANDROID_LIB_DEPENDENCIES'
, 'ANDROID_JAR_DEPENDENCIES'
, 'ANDROID_LIB_DEPENDENCY_REPLACEMENTS'
, 'ANDROID_BUNDLED_FILES'
, 'ANDROID_PERMISSIONS' ]
has_no_values = True
for key in keys:
value = scope.get(key)
if len(value) != 0:
if has_no_values:
if scope.condition:
cm_fh.write('\n{}if(ANDROID AND ({}))\n'.format(spaces(indent), scope.condition))
else:
cm_fh.write('\n{}if(ANDROID)\n'.format(spaces(indent)))
indent += 1
has_no_values = False
cm_fh.write('{}set_property(TARGET {} APPEND PROPERTY QT_{}\n'.format(spaces(indent), target, key))
write_list(cm_fh, value, '', indent + 1)
cm_fh.write('{})\n'.format(spaces(indent)))
indent -= 1
if not has_no_values:
cm_fh.write('{}endif()\n'.format(spaces(indent)))
def write_main_part(cm_fh: typing.IO[str], name: str, typename: str,
cmake_function: str, scope: Scope, *,
@ -1577,6 +1603,8 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str,
write_simd_part(cm_fh, name, scope, indent)
write_android_part(cm_fh, name, scopes[0], indent)
ignored_keys_report = write_ignored_keys(scopes[0], spaces(indent))
if ignored_keys_report:
cm_fh.write(ignored_keys_report)
@ -1590,12 +1618,12 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str,
for c in scopes[1:]:
c.reset_visited_keys()
write_android_part(cm_fh, name, c, indent=indent)
write_extend_target(cm_fh, name, c, indent=indent)
ignored_keys_report = write_ignored_keys(c, spaces(indent))
if ignored_keys_report:
cm_fh.write(ignored_keys_report)
def write_module(cm_fh: typing.IO[str], scope: Scope, *,
indent: int = 0) -> None:
module_name = scope.TARGET