From a9d2c5b6d7fa6b7365db8690f57aa78002c8bc4b Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 24 Jun 2019 14:54:40 +0200 Subject: [PATCH] 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 --- cmake/QtBuild.cmake | 8 + .../QtBuildInternalsAndroid.cmake | 206 ++++++++++++++++++ .../QtBuildInternalsConfig.cmake | 4 + cmake/QtPlatformAndroid.cmake | 2 +- cmake/QtPostProcess.cmake | 12 + src/corelib/.prev_CMakeLists.txt | 15 +- src/corelib/CMakeLists.txt | 15 +- src/corelib/Qt6CoreMacros.cmake | 5 + src/network/.prev_CMakeLists.txt | 15 +- src/network/CMakeLists.txt | 15 +- util/cmake/pro2cmake.py | 30 ++- 11 files changed, 313 insertions(+), 14 deletions(-) create mode 100644 cmake/QtBuildInternals/QtBuildInternalsAndroid.cmake diff --git a/cmake/QtBuild.cmake b/cmake/QtBuild.cmake index 3ee54f5d5a4..0852d8ed2fe 100644 --- a/cmake/QtBuild.cmake +++ b/cmake/QtBuild.cmake @@ -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}) diff --git a/cmake/QtBuildInternals/QtBuildInternalsAndroid.cmake b/cmake/QtBuildInternals/QtBuildInternalsAndroid.cmake new file mode 100644 index 00000000000..4677c9dcae4 --- /dev/null +++ b/cmake/QtBuildInternals/QtBuildInternalsAndroid.cmake @@ -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} "\n") + file(APPEND ${dependency_file} "\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} "\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} "\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} "\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} "\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} "\n") + endforeach() + endif() + + # Module plugins + if(module_plugins) + foreach(plugin IN LISTS module_plugins) + file(APPEND ${dependency_file} "\n") + endforeach() + endif() + + # Android Permissions + if(arg_PERMISSIONS) + foreach(permission IN LISTS arg_PERMISSIONS) + file(APPEND ${dependency_file} "\n") + endforeach() + endif() + + file(APPEND ${dependency_file} "") + file(APPEND ${dependency_file} "\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() diff --git a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake index 8a6c2cb7338..590885c44d1 100644 --- a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake +++ b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake @@ -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() diff --git a/cmake/QtPlatformAndroid.cmake b/cmake/QtPlatformAndroid.cmake index 70768947a06..bc7e0aa862f 100644 --- a/cmake/QtPlatformAndroid.cmake +++ b/cmake/QtPlatformAndroid.cmake @@ -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++ diff --git a/cmake/QtPostProcess.cmake b/cmake/QtPostProcess.cmake index 568719d4f0b..53b89f22f10 100644 --- a/cmake/QtPostProcess.cmake +++ b/cmake/QtPostProcess.cmake @@ -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() diff --git a/src/corelib/.prev_CMakeLists.txt b/src/corelib/.prev_CMakeLists.txt index d6726e9336a..f1000e6301a 100644 --- a/src/corelib/.prev_CMakeLists.txt +++ b/src/corelib/.prev_CMakeLists.txt @@ -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:: -# 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]" diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index da8eb4eea55..4d23f0f8929 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -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:: -# 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]" diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake index 738816ee4e9..d82a65e4a6a 100644 --- a/src/corelib/Qt6CoreMacros.cmake +++ b/src/corelib/Qt6CoreMacros.cmake @@ -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() diff --git a/src/network/.prev_CMakeLists.txt b/src/network/.prev_CMakeLists.txt index b1ba59e7be9..e5756e291d2 100644 --- a/src/network/.prev_CMakeLists.txt +++ b/src/network/.prev_CMakeLists.txt @@ -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 diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index 7a6bf8f9e9d..3148c007043 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -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 diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 28724b1fc05..96e2bacae9a 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -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