From a4bb4eef730718a8c464d8c72d763fe75ad54725 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Mon, 27 Mar 2023 12:03:42 +0200 Subject: [PATCH] cmake: create Android jars + apks for tests --- .github/workflows/android.yml | 17 +- CMakeLists.txt | 69 ++++- cmake/android/FindSdlAndroid.cmake | 103 +++++++ cmake/android/FindSdlAndroidBuildTools.cmake | 115 ++++++++ cmake/android/FindSdlAndroidPlatform.cmake | 123 ++++++++ cmake/android/SdlAndroidFunctions.cmake | 276 ++++++++++++++++++ cmake/android/SdlAndroidScript.cmake | 74 +++++ docs/README-android.md | 76 +++++ test/CMakeLists.txt | 135 +++++++++ test/android/cmake/AndroidManifest.xml.cmake | 70 +++++ .../cmake/SDLEntryTestActivity.java.cmake | 121 ++++++++ test/android/cmake/SDLTestActivity.java.cmake | 33 +++ .../cmake/res/values/strings.xml.cmake | 5 + .../android/cmake/res/xml/shortcuts.xml.cmake | 24 ++ .../res/drawable/sdl-test_foreground.xml | 102 +++++++ test/android/res/layout/arguments_layout.xml | 26 ++ .../res/mipmap-anydpi-v26/sdl-test.xml | 5 + .../res/mipmap-anydpi-v26/sdl-test_round.xml | 5 + test/android/res/mipmap-hdpi/sdl-test.png | Bin 0 -> 3041 bytes .../res/mipmap-hdpi/sdl-test_round.png | Bin 0 -> 5292 bytes test/android/res/mipmap-mdpi/sdl-test.png | Bin 0 -> 1881 bytes .../res/mipmap-mdpi/sdl-test_round.png | Bin 0 -> 3150 bytes test/android/res/mipmap-xhdpi/sdl-test.png | Bin 0 -> 4426 bytes .../res/mipmap-xhdpi/sdl-test_round.png | Bin 0 -> 7789 bytes test/android/res/mipmap-xxhdpi/sdl-test.png | Bin 0 -> 7389 bytes .../res/mipmap-xxhdpi/sdl-test_round.png | Bin 0 -> 13232 bytes test/android/res/mipmap-xxxhdpi/sdl-test.png | Bin 0 -> 11218 bytes .../res/mipmap-xxxhdpi/sdl-test_round.png | Bin 0 -> 19626 bytes test/android/res/values/arg_strings.xml | 7 + .../res/values/sdl-test_background.xml | 4 + 30 files changed, 1387 insertions(+), 3 deletions(-) create mode 100644 cmake/android/FindSdlAndroid.cmake create mode 100644 cmake/android/FindSdlAndroidBuildTools.cmake create mode 100644 cmake/android/FindSdlAndroidPlatform.cmake create mode 100644 cmake/android/SdlAndroidFunctions.cmake create mode 100644 cmake/android/SdlAndroidScript.cmake create mode 100644 test/android/cmake/AndroidManifest.xml.cmake create mode 100644 test/android/cmake/SDLEntryTestActivity.java.cmake create mode 100644 test/android/cmake/SDLTestActivity.java.cmake create mode 100644 test/android/cmake/res/values/strings.xml.cmake create mode 100644 test/android/cmake/res/xml/shortcuts.xml.cmake create mode 100644 test/android/res/drawable/sdl-test_foreground.xml create mode 100644 test/android/res/layout/arguments_layout.xml create mode 100644 test/android/res/mipmap-anydpi-v26/sdl-test.xml create mode 100644 test/android/res/mipmap-anydpi-v26/sdl-test_round.xml create mode 100644 test/android/res/mipmap-hdpi/sdl-test.png create mode 100644 test/android/res/mipmap-hdpi/sdl-test_round.png create mode 100644 test/android/res/mipmap-mdpi/sdl-test.png create mode 100644 test/android/res/mipmap-mdpi/sdl-test_round.png create mode 100644 test/android/res/mipmap-xhdpi/sdl-test.png create mode 100644 test/android/res/mipmap-xhdpi/sdl-test_round.png create mode 100644 test/android/res/mipmap-xxhdpi/sdl-test.png create mode 100644 test/android/res/mipmap-xxhdpi/sdl-test_round.png create mode 100644 test/android/res/mipmap-xxxhdpi/sdl-test.png create mode 100644 test/android/res/mipmap-xxxhdpi/sdl-test_round.png create mode 100644 test/android/res/values/arg_strings.xml create mode 100644 test/android/res/values/sdl-test_background.xml diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 2805ebc2c..20a09fe48 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -16,7 +16,7 @@ jobs: matrix: platform: - { name: Android.mk } - - { name: CMake, cmake: 1, android_abi: "arm64-v8a", android_platform: 23, arch: "aarch64", artifact: "SDL-android-arm64" } + - { name: CMake, cmake: 1, android_abi: "arm64-v8a", android_platform: 23, arch: "aarch64", artifact: "SDL-android-arm64", apk-artifact: "SDL-android-apks-arm64" } steps: - uses: actions/checkout@v3 @@ -28,6 +28,11 @@ jobs: if: ${{ matrix.platform.name == 'Android.mk' }} run: | ./build-scripts/androidbuildlibs.sh + - uses: actions/setup-java@v3 + if: ${{ matrix.platform.name == 'CMake' }} + with: + distribution: 'temurin' + java-version: '11' - name: Setup (CMake) if: ${{ matrix.platform.name == 'CMake' }} run: | @@ -56,6 +61,10 @@ jobs: if: ${{ matrix.platform.name == 'CMake' }} run: | cmake --build build --config Release --parallel --verbose + - name: Build test apk's (CMake) + if: ${{ matrix.platform.name == 'CMake' }} + run: | + cmake --build build --config Release --parallel --verbose --target testaudiocapture-apk testcontroller-apk testmultiaudio-apk testsprite-apk - name: Install (CMake) if: ${{ matrix.platform.name == 'CMake' }} run: | @@ -88,3 +97,9 @@ jobs: if-no-files-found: error name: ${{ matrix.platform.artifact }} path: build/dist/SDL3* + - uses: actions/upload-artifact@v3 + if: ${{ matrix.platform.name == 'CMake' }} + with: + if-no-files-found: error + name: ${{ matrix.platform.apk-artifact }} + path: build/test/*.apk diff --git a/CMakeLists.txt b/CMakeLists.txt index e420b65c6..c289ad043 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -368,9 +368,11 @@ endforeach() # Allow some projects to be built conditionally. set_option(SDL_DISABLE_INSTALL "Disable installation of SDL3" ${SDL3_SUBPROJECT}) cmake_dependent_option(SDL_DISABLE_INSTALL_CPACK "Create binary SDL3 archive using CPack" ${SDL3_SUBPROJECT} "NOT SDL_DISABLE_INSTALL" ON) -cmake_dependent_option(SDL_DISABLE_INSTALL_MAN "Install man pages for SDL3" ${SDL3_SUBPROJECT} "NOT SDL_DISABLE_INSTALL;NOT SDL_FRAMEWORK" ON) +cmake_dependent_option(SDL_DISABLE_INSTALL_DOCS "Install docs for SDL3" ${SDL3_SUBPROJECT} "NOT SDL_DISABLE_INSTALL;NOT SDL_FRAMEWORK" ON) set_option(SDL_DISABLE_UNINSTALL "Disable uninstallation of SDL3" OFF) +cmake_dependent_option(SDL_DISABLE_ANDROID_JAR "Disable creation of SDL3.jar" ${SDL3_SUBPROJECT} "ANDROID" ON) + option_string(SDL_ASSERTIONS "Enable internal sanity checks (auto/disabled/release/enabled/paranoid)" "auto") #set_option(SDL_DEPENDENCY_TRACKING "Use gcc -MMD -MT dependency tracking" ON) set_option(SDL_ASSEMBLY "Enable assembly routines" ${SDL_ASSEMBLY_DEFAULT}) @@ -1310,6 +1312,8 @@ endif() # Platform-specific options and settings if(ANDROID) + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/android") + file(GLOB ANDROID_CORE_SOURCES ${SDL3_SOURCE_DIR}/src/core/android/*.c) list(APPEND SOURCE_FILES ${ANDROID_CORE_SOURCES} ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c) set_property(SOURCE "${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c" APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-declaration-after-statement") @@ -1442,6 +1446,50 @@ if(ANDROID) set(HAVE_CLOCK_GETTIME 1) endif() + if(NOT SDL_DISABLE_ANDROID_JAR) + find_package(Java) + find_package(SdlAndroidPlatform MODULE) + + if(Java_FOUND AND SdlAndroidPlatform_FOUND) + include(UseJava) + set(path_android_jar "${SDL_ANDROID_PLATFORM_ROOT}/android.jar") + set(android_java_sources_root "${SDL3_SOURCE_DIR}/android-project/app/src/main/java") + file(GLOB SDL_JAVA_SOURCES "${android_java_sources_root}/org/libsdl/app/*.java") + set(CMAKE_JAVA_COMPILE_FLAGS "-encoding;utf-8") + add_jar(SDL3-jar + SOURCES ${SDL_JAVA_SOURCES} + INCLUDE_JARS "${path_android_jar}" + OUTPUT_NAME "SDL3" + VERSION "${SDL3_VERSION}" + ) + set_property(TARGET SDL3-jar PROPERTY OUTPUT "${SDL3_BINARY_DIR}/SDL3-${SDL3_VERSION}.jar") + set(javasourcesjar "${SDL3_BINARY_DIR}/SDL3-${SDL3_VERSION}-sources.jar") + string(REGEX REPLACE "${android_java_sources_root}/" "" sdl_relative_java_sources "${SDL_JAVA_SOURCES}") + add_custom_command( + OUTPUT "${javasourcesjar}" + COMMAND ${Java_JAR_EXECUTABLE} cf "${javasourcesjar}" ${sdl_relative_java_sources} + WORKING_DIRECTORY "${android_java_sources_root}" + DEPENDS ${SDL_JAVA_SOURCES} + ) + add_custom_target(SDL3-javasources ALL DEPENDS "${javasourcesjar}") + if(NOT SDL_DISABLE_INSTALL_DOCS) + set(javadocdir "${SDL3_BINARY_DIR}/docs/javadoc") + set(javadocjar "${SDL3_BINARY_DIR}/SDL3-${SDL3_VERSION}-javadoc.jar") + set(javadoc_index_html "${javadocdir}/index.html") + add_custom_command( + OUTPUT "${javadoc_index_html}" + COMMAND ${CMAKE_COMMAND} -E rm -rf "${javadocdir}" + COMMAND ${Java_JAVADOC_EXECUTABLE} -encoding utf8 -d "${javadocdir}" + -classpath "${path_android_jar}" + -author -use -version ${SDL_JAVA_SOURCES} + DEPENDS ${SDL_JAVA_SOURCES} "${path_android_jar}" + ) + add_custom_target(SDL3-javadoc ALL DEPENDS "${javadoc_index_html}") + set_property(TARGET SDL3-javadoc PROPERTY OUTPUT_DIR "${javadocdir}") + endif() + endif() + endif() + elseif(EMSCRIPTEN) # Hide noisy warnings that intend to aid mostly during initial stages of porting a new # project. Uncomment at will for verbose cross-compiling -I/../ path info. @@ -3695,11 +3743,28 @@ if(NOT SDL_DISABLE_INSTALL) include(CPack) endif() - if(NOT SDL_DISABLE_INSTALL_MAN) + if(ANDROID) + set(SDL_INSTALL_JAVADIR "${CMAKE_INSTALL_DATAROOTDIR}/java" CACHE PATH "Path where to install java clases + java sources") + if(TARGET SDL3-jar) + install(FILES "${SDL3_BINARY_DIR}/SDL3.jar" "${SDL3_BINARY_DIR}/SDL3-${SDL3_VERSION}.jar" + DESTINATION "${SDL_INSTALL_JAVADIR}/SDL3") + endif() + if(TARGET SDL3-javasources) + install(FILES "${SDL3_BINARY_DIR}/SDL3-${SDL3_VERSION}-sources.jar" + DESTINATION "${SDL_INSTALL_JAVADIR}/SDL3") + endif() + endif() + + if(NOT SDL_DISABLE_INSTALL_DOCS) SDL_generate_manpages( SYMBOL "SDL_Init" WIKIHEADERS_PL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/build-scripts/wikiheaders.pl" ) + if(TARGET SDL3-javadoc) + set(SDL_INSTALL_JAVADOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/javadoc" CACHE PATH "Path where to install SDL3 javadoc") + install(DIRECTORY "${SDL3_BINARY_DIR}/docs/javadoc/" + DESTINATION "${SDL_INSTALL_JAVADOCDIR}/SDL3") + endif() endif() endif() diff --git a/cmake/android/FindSdlAndroid.cmake b/cmake/android/FindSdlAndroid.cmake new file mode 100644 index 000000000..38a2d7a13 --- /dev/null +++ b/cmake/android/FindSdlAndroid.cmake @@ -0,0 +1,103 @@ +#[=======================================================================[ + +FindSdlAndroid +---------------------- + +Locate various executables that are essential to creating an Android APK archive. +This find module uses the FindSdlAndroidBuildTools module to locate some Android utils. + + +Imported targets +^^^^^^^^^^^^^^^^ + +This module defines the following :prop_tgt:`IMPORTED` target(s): + +`` SdlAndroid::aapt2 `` + Imported executable for the "android package tool" v2 + +`` SdlAndroid::apksigner`` + Imported executable for the APK signer tool + +`` SdlAndroid::d8 `` + Imported executable for the dex compiler + +`` SdlAndroid::zipalign `` + Imported executable for the zipalign util + +`` SdlAndroid::adb `` + Imported executable for the "android debug bridge" tool + +`` SdlAndroid::keytool `` + Imported executable for the keytool, a key and certificate management utility + +`` SdlAndroid::zip `` + Imported executable for the zip, for packaging and compressing files + +Result variables +^^^^^^^^^^^^^^^^ + +This module will set the following variables in your project: + +`` AAPT2_BIN `` + Path of aapt2 + +`` APKSIGNER_BIN `` + Path of apksigner + +`` D8_BIN `` + Path of d8 + +`` ZIPALIGN_BIN `` + Path of zipalign + +`` ADB_BIN `` + Path of adb + +`` KEYTOOL_BIN `` + Path of keytool + +`` ZIP_BIN `` + Path of zip + +#]=======================================================================] + +cmake_minimum_required(VERSION 3.7) + +if(NOT PROJECT_NAME MATCHES "^SDL.*") + message(WARNING "This module is internal to SDL and is currently not supported.") +endif() + +find_package(SdlAndroidBuildTools MODULE) + +function(_sdl_android_find_create_imported_executable NAME) + string(TOUPPER "${NAME}" NAME_UPPER) + set(varname "${NAME_UPPER}_BIN") + find_program("${varname}" NAMES "${NAME}" PATHS ${SDL_ANDROID_BUILD_TOOLS_ROOT}) + if(EXISTS "${${varname}}" AND NOT TARGET SdlAndroid::${NAME}) + add_executable(SdlAndroid::${NAME} IMPORTED) + set_property(TARGET SdlAndroid::${NAME} PROPERTY IMPORTED_LOCATION "${${varname}}") + endif() +endfunction() + +if(SdlAndroidBuildTools_FOUND) + _sdl_android_find_create_imported_executable(aapt2) + _sdl_android_find_create_imported_executable(apksigner) + _sdl_android_find_create_imported_executable(d8) + _sdl_android_find_create_imported_executable(zipalign) +endif() + +_sdl_android_find_create_imported_executable(adb) +_sdl_android_find_create_imported_executable(keytool) +_sdl_android_find_create_imported_executable(zip) +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(SdlAndroid + VERSION_VAR + REQUIRED_VARS + AAPT2_BIN + APKSIGNER_BIN + D8_BIN + ZIPALIGN_BIN + KEYTOOL_BIN + ZIP_BIN +) diff --git a/cmake/android/FindSdlAndroidBuildTools.cmake b/cmake/android/FindSdlAndroidBuildTools.cmake new file mode 100644 index 000000000..b8825d92b --- /dev/null +++ b/cmake/android/FindSdlAndroidBuildTools.cmake @@ -0,0 +1,115 @@ +#[=======================================================================[ + +FindSdlAndroidBuildTools +---------------------- + +Locate the Android build tools directory. + + +Imported targets +^^^^^^^^^^^^^^^^ + +This find module defines the following :prop_tgt:`IMPORTED` target(s): + + + +Result variables +^^^^^^^^^^^^^^^^ + +This module will set the following variables in your project: + +`` SdlAndroidBuildTools_FOUND + if false, no Android build tools have been found + +`` SDL_ANDROID_BUILD_TOOLS_ROOT + path of the Android build tools root directory if found + +`` SDL_ANDROID_BUILD_TOOLS_VERSION + the human-readable string containing the android build tools version if found + +Cache variables +^^^^^^^^^^^^^^^ + +These variables may optionally be set to help this module find the correct files: + +``SDL_ANDROID_BUILD_TOOLS_ROOT`` + path of the Android build tools root directory + + +Variables for locating Android platform +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This module responds to the flags: + +``SDL_ANDROID_HOME + First, this module will look for platforms in this CMake variable. + +``ANDROID_HOME + If no platform was found in `SDL_ANDROID_HOME`, then try `ANDROID_HOME`. + +``$ENV{ANDROID_HOME} + If no platform was found in neither `SDL_ANDROID_HOME` or `ANDROID_HOME`, then try `ANDROID_HOME}` + +#]=======================================================================] + +cmake_minimum_required(VERSION 3.7) + +if(NOT PROJECT_NAME MATCHES "^SDL.*") + message(WARNING "This module is internal to SDL and is currently not supported.") +endif() + +function(_sdl_is_valid_android_build_tools_root RESULT VERSION BUILD_TOOLS_ROOT) + set(result TRUE) + set(version -1) + + string(REGEX MATCH "/([0-9.]+)$" root_match "${BUILD_TOOLS_ROOT}") + if(root_match + AND EXISTS "${BUILD_TOOLS_ROOT}/aapt2" + AND EXISTS "${BUILD_TOOLS_ROOT}/apksigner" + AND EXISTS "${BUILD_TOOLS_ROOT}/d8" + AND EXISTS "${BUILD_TOOLS_ROOT}/zipalign") + set(result "${BUILD_TOOLS_ROOT}") + set(version "${CMAKE_MATCH_1}") + endif() + + set(${RESULT} ${result} PARENT_SCOPE) + set(${VERSION} ${version} PARENT_SCOPE) +endfunction() + +function(_find_sdl_android_build_tools_root ROOT) + cmake_parse_arguments(fsabtr "" "" "" ${ARGN}) + set(homes ${SDL_ANDROID_HOME} ${ANDROID_HOME} $ENV{ANDROID_HOME}) + set(root ${ROOT}-NOTFOUND) + foreach(home IN LISTS homes) + if(NOT IS_DIRECTORY "${home}") + continue() + endif() + file(GLOB build_tools_roots LIST_DIRECTORIES true "${home}/build-tools/*") + set(max_build_tools_version -1) + set(max_build_tools_root "") + foreach(build_tools_root IN LISTS build_tools_roots) + _sdl_is_valid_android_build_tools_root(is_valid build_tools_version "${build_tools_root}") + if(is_valid AND build_tools_version GREATER max_build_tools_version) + set(max_build_tools_version "${build_tools_version}") + set(max_build_tools_root "${build_tools_root}") + endif() + endforeach() + if(max_build_tools_version GREATER -1) + set(root ${max_build_tools_root}) + break() + endif() + endforeach() + set(${ROOT} ${root} PARENT_SCOPE) +endfunction() + +if(NOT DEFINED SDL_ANDROID_BUILD_TOOLS_ROOT) + _find_sdl_android_build_tools_root(SDL_ANDROID_BUILD_TOOLS_ROOT) + set(SDL_ANDROID_BUILD_TOOLS_ROOT "${SDL_ANDROID_BUILD_TOOLS_ROOT}" CACHE PATH "Path of Android build tools") +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(SdlAndroidBuildTools + VERSION_VAR SDL_ANDROID_BUILD_TOOLS_VERSION + REQUIRED_VARS SDL_ANDROID_BUILD_TOOLS_ROOT +) diff --git a/cmake/android/FindSdlAndroidPlatform.cmake b/cmake/android/FindSdlAndroidPlatform.cmake new file mode 100644 index 000000000..0f9584906 --- /dev/null +++ b/cmake/android/FindSdlAndroidPlatform.cmake @@ -0,0 +1,123 @@ +#[=======================================================================[ + +FindSdlAndroidPlatform +---------------------- + +Locate the Android SDK platform. + + +Imported targets +^^^^^^^^^^^^^^^^ + +This module defines the following :prop_tgt:`IMPORTED` target(s): + + + +Result variables +^^^^^^^^^^^^^^^^ + +This find module will set the following variables in your project: + +`` SdlAndroidPlatform_FOUND + if false, no Android platform has been found + +`` SDL_ANDROID_PLATFORM_ROOT + path of the Android SDK platform root directory if found + +`` SDL_ANDROID_PLATFORM_ANDROID_JAR + path of the Android SDK platform jar file if found + +`` SDL_ANDROID_PLATFORM_VERSION + the human-readable string containing the android platform version if found + +Cache variables +^^^^^^^^^^^^^^^ + +These variables may optionally be set to help this module find the correct files: + +``SDL_ANDROID_PLATFORM_ROOT`` + path of the Android SDK platform root directory + + +Variables for locating Android platform +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This module responds to the flags: + +``SDL_ANDROID_HOME + First, this module will look for platforms in this CMake variable. + +``ANDROID_HOME + If no platform was found in `SDL_ANDROID_HOME`, then try `ANDROID_HOME`. + +``$ENV{ANDROID_HOME} + If no platform was found in neither `SDL_ANDROID_HOME` or `ANDROID_HOME`, then try `ANDROID_HOME}` + +#]=======================================================================] + +cmake_minimum_required(VERSION 3.7) + +if(NOT PROJECT_NAME MATCHES "^SDL.*") + message(WARNING "This module is internal to SDL and is currently not supported.") +endif() + +function(_sdl_is_valid_android_platform_root RESULT VERSION PLATFORM_ROOT) + set(result FALSE) + set(version -1) + + string(REGEX MATCH "/android-([0-9]+)$" root_match "${PLATFORM_ROOT}") + if(root_match AND EXISTS "${PLATFORM_ROOT}/android.jar") + set(result TRUE) + set(version "${CMAKE_MATCH_1}") + endif() + + set(${RESULT} ${result} PARENT_SCOPE) + set(${VERSION} ${version} PARENT_SCOPE) +endfunction() + +function(_sdl_find_android_platform_root ROOT) + cmake_parse_arguments(sfapr "" "" "" ${ARGN}) + set(homes ${SDL_ANDROID_HOME} ${ANDROID_HOME} $ENV{ANDROID_HOME}) + set(root ${ROOT}-NOTFOUND) + foreach(home IN LISTS homes) + if(NOT IS_DIRECTORY "${home}") + continue() + endif() + file(GLOB platform_roots LIST_DIRECTORIES true "${home}/platforms/*") + set(max_platform_version -1) + set(max_platform_root "") + foreach(platform_root IN LISTS platform_roots) + _sdl_is_valid_android_platform_root(is_valid platform_version "${platform_root}") + if(is_valid AND platform_version GREATER max_platform_version) + set(max_platform_version "${platform_version}") + set(max_platform_root "${platform_root}") + endif() + endforeach() + if(max_platform_version GREATER -1) + set(root ${max_platform_root}) + break() + endif() + endforeach() + set(${ROOT} ${root} PARENT_SCOPE) +endfunction() + +set(SDL_ANDROID_PLATFORM_ANDROID_JAR "SDL_ANDROID_PLATFORM_ANDROID_JAR-NOTFOUND") + +if(NOT DEFINED SDL_ANDROID_PLATFORM_ROOT) + _sdl_find_android_platform_root(SDL_ANDROID_PLATFORM_ROOT) + set(SDL_ANDROID_PLATFORM_ROOT "${SDL_ANDROID_PLATFORM_ROOT}" CACHE PATH "Path of Android platform") +endif() +if(SDL_ANDROID_PLATFORM_ROOT) + _sdl_is_valid_android_platform_root(_valid SDL_ANDROID_PLATFORM_VERSION "${SDL_ANDROID_PLATFORM_ROOT}") + if(_valid) + set(SDL_ANDROID_PLATFORM_ANDROID_JAR "${SDL_ANDROID_PLATFORM_ROOT}/android.jar") + endif() + unset(_valid) +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(SdlAndroidPlatform + VERSION_VAR SDL_ANDROID_PLATFORM_VERSION + REQUIRED_VARS SDL_ANDROID_PLATFORM_ROOT SDL_ANDROID_PLATFORM_ANDROID_JAR +) diff --git a/cmake/android/SdlAndroidFunctions.cmake b/cmake/android/SdlAndroidFunctions.cmake new file mode 100644 index 000000000..b7b3aa849 --- /dev/null +++ b/cmake/android/SdlAndroidFunctions.cmake @@ -0,0 +1,276 @@ +#[=======================================================================[ + +This CMake script contains functions to build an Android APK. +It is (currently) limited to packaging binaries for a single architecture. + +#]=======================================================================] + +cmake_minimum_required(VERSION 3.7) + +if(NOT PROJECT_NAME MATCHES "^SDL.*") + message(WARNING "This module is internal to SDL and is currently not supported.") +endif() + +function(_sdl_create_outdir_for_target OUTDIRECTORY TARGET) + set(outdir "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TARGET}.dir") + # Some CMake versions have a slow `cmake -E make_directory` implementation + if(NOT IS_DIRECTORY "${outdir}") + execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${outdir}") + endif() + set("${OUTDIRECTORY}" "${outdir}" PARENT_SCOPE) +endfunction() + +function(sdl_create_android_debug_keystore TARGET) + set(output "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_debug.keystore") + add_custom_command(OUTPUT ${output} + COMMAND ${CMAKE_COMMAND} -E rm -f "${output}" + COMMAND SdlAndroid::keytool -genkey -keystore "${output}" -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname "C=US, O=Android, CN=Android Debug" + ) + add_custom_target(${TARGET} DEPENDS "${output}") + set_property(TARGET ${TARGET} PROPERTY OUTPUT "${output}") +endfunction() + +function(sdl_android_compile_resources TARGET) + cmake_parse_arguments(arg "" "RESFOLDER" "RESOURCES" ${ARGN}) + + if(NOT arg_RESFOLDER AND NOT arg_RESOURCES) + message(FATAL_ERROR "Missing RESFOLDER or RESOURCES argument (need one or both)") + endif() + _sdl_create_outdir_for_target(outdir "${TARGET}") + set(out_files "") + + set(res_files "") + if(arg_RESFOLDER) + get_filename_component(arg_RESFOLDER "${arg_RESFOLDER}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + file(GLOB_RECURSE res_folder_files "${arg_RESFOLDER}/*") + list(APPEND res_files ${res_folder_files}) + + foreach(res_file IN LISTS res_files) + file(RELATIVE_PATH rel_res_file "${arg_RESFOLDER}" "${res_file}") + string(REPLACE "/" "_" rel_comp_path "${rel_res_file}") + if(res_file MATCHES ".*res/values.*\\.xml$") + string(REGEX REPLACE "\\.xml" ".arsc" rel_comp_path "${rel_comp_path}") + endif() + set(comp_path "${outdir}/${rel_comp_path}.flat") + add_custom_command( + OUTPUT "${comp_path}" + COMMAND SdlAndroid::aapt2 compile -o "${outdir}" "${res_file}" + DEPENDS ${res_file} + ) + list(APPEND out_files "${comp_path}") + endforeach() + endif() + + if(arg_RESOURCES) + list(APPEND res_files ${arg_RESOURCES}) + foreach(res_file IN LISTS arg_RESOURCES) + string(REGEX REPLACE ".*/res/" "" rel_res_file ${res_file}) + string(REPLACE "/" "_" rel_comp_path "${rel_res_file}") + if(res_file MATCHES ".*res/values.*\\.xml$") + string(REGEX REPLACE "\\.xml" ".arsc" rel_comp_path "${rel_comp_path}") + endif() + set(comp_path "${outdir}/${rel_comp_path}.flat") + add_custom_command( + OUTPUT "${comp_path}" + COMMAND SdlAndroid::aapt2 compile -o "${outdir}" "${res_file}" + DEPENDS ${res_file} + ) + list(APPEND out_files "${comp_path}") + endforeach() + endif() + + add_custom_target(${TARGET} DEPENDS ${out_files}) + set_property(TARGET "${TARGET}" PROPERTY OUTPUTS "${out_files}") + set_property(TARGET "${TARGET}" PROPERTY SOURCES "${res_files}") +endfunction() + +function(sdl_android_link_resources TARGET) + cmake_parse_arguments(arg "NO_DEBUG" "MIN_SDK_VERSION;TARGET_SDK_VERSION;ANDROID_JAR;OUTPUT_APK;MANIFEST;PACKAGE" "RES_TARGETS" ${ARGN}) + + if(arg_MANIFEST) + get_filename_component(arg_MANIFEST "${arg_MANIFEST}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + else() + message(FATAL_ERROR "sdl_add_android_link_resources_target requires a Android MANIFEST path (${arg_MANIFEST})") + endif() + if(NOT arg_PACKAGE) + file(READ "${arg_MANIFEST}" manifest_contents) + string(REGEX MATCH "package=\"([a-zA-Z0-9_.]+)\"" package_match "${manifest_contents}") + if(NOT package_match) + message(FATAL_ERROR "Could not extract package from Android manifest (${arg_MANIFEST})") + endif() + set(arg_PACKAGE "${CMAKE_MATCH_1}") + endif() + + set(depends "") + + _sdl_create_outdir_for_target(outdir "${TARGET}") + string(REPLACE "." "/" java_r_path "${arg_PACKAGE}") + get_filename_component(java_r_path "${java_r_path}" ABSOLUTE BASE_DIR "${outdir}") + set(java_r_path "${java_r_path}/R.java") + + set(command SdlAndroid::aapt2 link) + if(NOT arg_NO_DEBUG) + list(APPEND command --debug-mode) + endif() + if(arg_MIN_SDK_VERSION) + list(APPEND command --min-sdk-version ${arg_MIN_SDK_VERSION}) + endif() + if(arg_TARGET_SDK_VERSION) + list(APPEND command --target-sdk-version ${arg_TARGET_SDK_VERSION}) + endif() + if(arg_ANDROID_JAR) + list(APPEND command -I "${arg_ANDROID_JAR}") + else() + list(APPEND command -I "${SDL_ANDROID_PLATFORM_ANDROID_JAR}") + endif() + if(NOT arg_OUTPUT_APK) + set(arg_OUTPUT_APK "${TARGET}.apk") + endif() + get_filename_component(arg_OUTPUT_APK "${arg_OUTPUT_APK}" ABSOLUTE BASE_DIR "${outdir}") + list(APPEND command -o "${arg_OUTPUT_APK}") + list(APPEND command --java "${outdir}") + list(APPEND command --manifest "${arg_MANIFEST}") + foreach(res_target IN LISTS arg_RES_TARGETS) + list(APPEND command $) + list(APPEND depends $) + endforeach() + add_custom_command( + OUTPUT "${arg_OUTPUT_APK}" "${java_r_path}" + COMMAND ${command} + DEPENDS ${depends} ${arg_MANIFEST} + COMMAND_EXPAND_LISTS + VERBATIM + ) + add_custom_target(${TARGET} DEPENDS "${arg_OUTPUT_APK}" "${java_r_path}") + set_property(TARGET ${TARGET} PROPERTY OUTPUT "${arg_OUTPUT_APK}") + set_property(TARGET ${TARGET} PROPERTY JAVA_R "${java_r_path}") + set_property(TARGET ${TARGET} PROPERTY OUTPUTS "${${arg_OUTPUT_APK}};${java_r_path}") +endfunction() + +function(sdl_add_to_apk_unaligned TARGET) + cmake_parse_arguments(arg "" "APK_IN;NAME;OUTDIR" "ASSETS;NATIVE_LIBS;DEX" ${ARGN}) + + if(NOT arg_APK_IN) + message(FATAL_ERROR "Missing APK_IN argument") + endif() + + if(NOT TARGET ${arg_APK_IN}) + message(FATAL_ERROR "APK_IN (${arg_APK_IN}) must be a target providing an apk") + endif() + + _sdl_create_outdir_for_target(workdir ${TARGET}) + + if(NOT arg_OUTDIR) + set(arg_OUTDIR "${CMAKE_CURRENT_BINARY_DIR}") + endif() + + if(NOT arg_NAME) + string(REGEX REPLACE "[:-]+" "." arg_NAME "${TARGET}") + if(NOT arg_NAME MATCHES "\\.apk") + set(arg_NAME "${arg_NAME}.apk") + endif() + endif() + get_filename_component(apk_file "${arg_NAME}" ABSOLUTE BASE_DIR "${arg_OUTDIR}") + + set(apk_libdir "lib/${ANDROID_ABI}") + + set(depends "") + + set(commands + COMMAND "${CMAKE_COMMAND}" -E remove_directory -rf "${apk_libdir}" "assets" + COMMAND "${CMAKE_COMMAND}" -E make_directory "${apk_libdir}" "assets" + COMMAND "${CMAKE_COMMAND}" -E copy "$" "${apk_file}" + ) + + set(dex_i "1") + foreach(dex IN LISTS arg_DEX) + set(suffix "${dex_i}") + if(suffix STREQUAL "1") + set(suffix "") + endif() + list(APPEND commands + COMMAND "${CMAKE_COMMAND}" -E copy "$" "classes${suffix}.dex" + COMMAND SdlAndroid::zip -u -q -j "${apk_file}" "classes${suffix}.dex" + ) + math(EXPR dex_i "${dex_i}+1") + list(APPEND depends "$") + endforeach() + + foreach(native_lib IN LISTS arg_NATIVE_LIBS) + list(APPEND commands + COMMAND "${CMAKE_COMMAND}" -E copy $ "${apk_libdir}/$" + COMMAND SdlAndroid::zip -u -q "${apk_file}" "${apk_libdir}/$" + ) + endforeach() + if(arg_ASSETS) + list(APPEND commands + COMMAND "${CMAKE_COMMAND}" -E copy ${arg_ASSETS} "assets" + COMMAND SdlAndroid::zip -u -r -q "${apk_file}" "assets" + ) + endif() + + add_custom_command(OUTPUT "${apk_file}" + ${commands} + DEPENDS ${arg_NATIVE_LIBS} ${depends} "$" + WORKING_DIRECTORY "${workdir}" + ) + add_custom_target(${TARGET} DEPENDS "${apk_file}") + set_property(TARGET ${TARGET} PROPERTY OUTPUT "${apk_file}") +endfunction() + +function(sdl_apk_align TARGET APK_IN) + cmake_parse_arguments(arg "" "NAME;OUTDIR" "" ${ARGN}) + + if(NOT TARGET ${arg_APK_IN}) + message(FATAL_ERROR "APK_IN (${arg_APK_IN}) must be a target providing an apk") + endif() + + if(NOT arg_OUTDIR) + set(arg_OUTDIR "${CMAKE_CURRENT_BINARY_DIR}") + endif() + + if(NOT arg_NAME) + string(REGEX REPLACE "[:-]+" "." arg_NAME "${TARGET}") + if(NOT arg_NAME MATCHES "\\.apk") + set(arg_NAME "${arg_NAME}.apk") + endif() + endif() + get_filename_component(apk_file "${arg_NAME}" ABSOLUTE BASE_DIR "${arg_OUTDIR}") + + add_custom_command(OUTPUT "${apk_file}" + COMMAND SdlAndroid::zipalign -f 4 "$" "${apk_file}" + DEPENDS "$" + ) + add_custom_target(${TARGET} DEPENDS "${apk_file}") + set_property(TARGET ${TARGET} PROPERTY OUTPUT "${apk_file}") +endfunction() + +function(sdl_apk_sign TARGET APK_IN) + cmake_parse_arguments(arg "" "OUTPUT;KEYSTORE" "" ${ARGN}) + + if(NOT TARGET ${arg_APK_IN}) + message(FATAL_ERROR "APK_IN (${arg_APK_IN}) must be a target providing an apk") + endif() + + if(NOT TARGET ${arg_KEYSTORE}) + message(FATAL_ERROR "APK_KEYSTORE (${APK_KEYSTORE}) must be a target providing a keystore") + endif() + + if(NOT arg_OUTPUT) + string(REGEX REPLACE "[:-]+" "." arg_OUTPUT "${TARGET}") + if(NOT arg_OUTPUT MATCHES "\\.apk") + set(arg_OUTPUT "${arg_OUTPUT}.apk") + endif() + endif() + get_filename_component(apk_file "${arg_OUTPUT}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}") + + add_custom_command(OUTPUT "${apk_file}" + COMMAND SdlAndroid::apksigner sign + --ks "$" + --ks-pass pass:android --in "$" --out "${apk_file}" + DEPENDS "$" "$" + BYPRODUCTS "${apk_file}.idsig" + ) + add_custom_target(${TARGET} DEPENDS "${apk_file}") + set_property(TARGET ${TARGET} PROPERTY OUTPUT "${apk_file}") +endfunction() diff --git a/cmake/android/SdlAndroidScript.cmake b/cmake/android/SdlAndroidScript.cmake new file mode 100644 index 000000000..dd98f9b4b --- /dev/null +++ b/cmake/android/SdlAndroidScript.cmake @@ -0,0 +1,74 @@ +#[=======================================================================[ + +This CMake script is meant to be used in CMake script mode (cmake -P). +It wraps commands that communicate with an actual Android device. +Because + +#]=======================================================================] + +cmake_minimum_required(VERSION 3.16) + +if(NOT CMAKE_SCRIPT_MODE_FILE) + message(FATAL_ERROR "This file can only be used in CMake script mode") +endif() +if(NOT ADB) + set(ADB "adb") +endif() + +if(NOT ACTION) + message(FATAL_ERROR "Missing ACTION argument") +endif() + +if(ACTION STREQUAL "uninstall") + # The uninstall action attempts to uninstall all packages. All failures are ignored. + foreach(package IN LISTS PACKAGES) + message("Uninstalling ${package} ...") + execute_process( + COMMAND ${ADB} uninstall ${package} + RESULT_VARIABLE res + ) + message("... result=${res}") + endforeach() +elseif(ACTION STREQUAL "install") + # The install actions attempts to install APK's to an Android device using adb. Failures are ignored. + set(failed_apks "") + foreach(apk IN LISTS APKS) + message("Installing ${apk} ...") + execute_process( + COMMAND ${ADB} install -d -r --streaming ${apk} + RESULT_VARIABLE res + ) + message("... result=${res}") + if(NOT res EQUAL 0) + list(APPEND failed_apks ${apk}) + endif() + endforeach() + if(failed_apks) + message(FATAL_ERROR "Failed to install ${failed_apks}") + endif() +elseif(ACTION STREQUAL "build-install-run") + if(NOT EXECUTABLES) + message(FATAL_ERROR "Missing EXECUTABLES (don't know what executables to build/install and start") + endif() + if(NOT BUILD_FOLDER) + message(FATAL_ERROR "Missing BUILD_FOLDER (don't know where to build the APK's") + endif() + set(install_targets "") + foreach(executable IN LISTS EXECUTABLES) + list(APPEND install_targets "install-${executable}") + endforeach() + execute_process( + COMMAND ${CMAKE_COMMAND} --build "${BUILD_FOLDER}" --target ${install_targets} + RESULT_VARIABLE res + ) + if(NOT res EQUAL 0) + message(FATAL_ERROR "Failed to install APK(s) for ${EXECUTABLES}") + endif() + list(GET EXECUTABLES 0 start_executable) + execute_process( + COMMAND ${CMAKE_COMMAND} --build "${BUILD_FOLDER}" --target start-${start_executable} + RESULT_VARIABLE res + ) +else() + message(FATAL_ERROR "Unknown ACTION=${ACTION}") +endif() diff --git a/docs/README-android.md b/docs/README-android.md index 6839e68e6..1c4d2b817 100644 --- a/docs/README-android.md +++ b/docs/README-android.md @@ -485,3 +485,79 @@ Known issues - The number of buttons reported for each joystick is hardcoded to be 36, which is the current maximum number of buttons Android can report. +Building the SDL tests +================================================================================ + +SDL's CMake build system can create APK's for the tests. +It can build all tests with a single command without a dependency on gradle or Android Studio. +The APK's are signed with a debug certificate. +The only caveat is that the APK's support a single architecture. + +### Requirements +- SDL source tree +- CMake +- ninja or make +- Android Platform SDK +- Android NDK +- Android Build tools +- Java JDK (version should be compatible with Android) +- keytool (usually provided with the Java JDK), used for generating a debug certificate +- zip + +### CMake configuration + +When configuring the CMake project, you need to use the Android NDK CMake toolchain, and pass the Android home path through `SDL_ANDROID_HOME`. +``` +cmake .. -DCMAKE_TOOLCHAIN_FILE= -DANDROID_ABI= -DSDL_ANDROID_HOME= -DANDROID_PLATFORM=23 -DSDL_TESTS=ON +``` + +Remarks: +- `android.toolchain.cmake` can usually be found at `$ANDROID_HOME/ndk/x.y.z/build/cmake/android.toolchain.cmake` +- `ANDROID_ABI` should be one of `arm64-v8a`, `armeabi-v7a`, `x86` or `x86_64`. +- When CMake is unable to find required paths, use `cmake-gui` to override required `SDL_ANDROID_` CMake cache variables. + +### Building the APK's + +For the `testsprite` executable, the `testsprite-apk` target will build the associated APK: +``` +cmake --build . --target testsprite-apk +``` + +APK's of all tests can be built with the `sdl-test-apks` target: +``` +cmake --build . --target sdl-test-apks +``` + +### Installation/removal of the tests + +`testsprite.apk` APK can be installed on your Android machine using the `install-testsprite` target: +``` +cmake --build . --target install-testsprite +``` + +APK's of all tests can be installed with the `install-sdl-test-apks` target: +``` +cmake --build . --target install-sdl-test-apks +``` + +All SDL tests can be uninstalled with the `uninstall-sdl-test-apks` target: +``` +cmake --build . --target uninstall-sdl-test-apks +``` + +### Starting the tests + +After installation, the tests can be started using the Android Launcher GUI. +Alternatively, they can also be started using CMake targets. + +This command will start the testsprite executable: +``` +cmake --build . --target start-testsprite +``` + +There is also a convenience target which will build, install and start a test: +``` +cmake --build . --target build-install-start-testsprite +``` + +Not all tests provide a GUI. For those, you can use `adb logcat` to read the output of stdout. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b633e5b83..40b18c073 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -460,3 +460,138 @@ if(SDL_INSTALL_TESTS) DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/installed-tests/SDL3 ) endif() + +if(ANDROID AND TARGET SDL3-jar) + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../cmake/android") + find_package(SdlAndroid MODULE) + if(SdlAndroid_FOUND) + set(apks "") + set(packages "") + + include(SdlAndroidFunctions) + sdl_create_android_debug_keystore(SDL_test-debug-keystore) + sdl_android_compile_resources(SDL_test-resources RESFOLDER android/res) + add_custom_target(sdl-test-apks) + foreach(TEST ${SDL_TEST_EXECUTABLES}) + set(ANDROID_MANIFEST_APP_NAME "${TEST}") + set(ANDROID_MANIFEST_LABEL "${TEST}") + set(ANDROID_MANIFEST_LIB_NAME "$") + set(ANDROID_MANIFEST_PACKAGE "org.libsdl.sdl.test.${TEST}") + set(generated_manifest_path "${CMAKE_CURRENT_BINARY_DIR}/android/${TEST}-src/AndroidManifest.xml") + string(REPLACE "." "/" JAVA_PACKAGE_DIR "${ANDROID_MANIFEST_PACKAGE}") + set(GENERATED_SRC_FOLDER "${CMAKE_CURRENT_BINARY_DIR}/android/${TEST}-src") + set(GENERATED_RES_FOLDER "${GENERATED_SRC_FOLDER}/res") + set(JAVA_PACKAGE_DIR "${GENERATED_SRC_FOLDER}/${JAVA_PACKAGE_DIR}") + configure_file(android/cmake/SDLEntryTestActivity.java.cmake "${JAVA_PACKAGE_DIR}/SDLEntryTestActivity.java" @ONLY) + configure_file(android/cmake/SDLTestActivity.java.cmake "${JAVA_PACKAGE_DIR}/SDLTestActivity.java" @ONLY) + configure_file(android/cmake/res/values/strings.xml.cmake android/res/values/strings-${TEST}.xml @ONLY) + configure_file(android/cmake/res/xml/shortcuts.xml.cmake "${GENERATED_RES_FOLDER}/xml/shortcuts.xml" @ONLY) + configure_file(android/cmake/AndroidManifest.xml.cmake "${generated_manifest_path}" @ONLY) + file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/android/${TEST}-$/res/values/strings.xml" + INPUT "${CMAKE_CURRENT_BINARY_DIR}/android/res/values/strings-${TEST}.xml" + ) + + sdl_android_compile_resources(${TEST}-resources + RESOURCES + "${CMAKE_CURRENT_BINARY_DIR}/android/${TEST}-$/res/values/strings.xml" + "${GENERATED_RES_FOLDER}/xml/shortcuts.xml" + ) + + sdl_android_link_resources(${TEST}-apk-linked + MANIFEST "${generated_manifest_path}" + PACKAGE ${ANDROID_MANIFEST_PACKAGE} + RES_TARGETS SDL_test-resources ${TEST}-resources + TARGET_SDK_VERSION 31 + ) + + set(CMAKE_JAVA_COMPILE_FLAGS "-encoding;utf-8") + set(classes_path "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TEST}-java.dir/classes") + # Some CMake versions have a slow `cmake -E make_directory` implementation + if(NOT IS_DIRECTORY "${classes_path}") + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory "${classes_path}") + endif() + set(OUT_JAR "${CMAKE_CURRENT_BINARY_DIR}/${TEST}.jar") + add_custom_command( + OUTPUT "${OUT_JAR}" + COMMAND ${CMAKE_COMMAND} -E rm -rf "${classes_path}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${classes_path}" + COMMAND ${Java_JAVAC_EXECUTABLE} + -source 1.8 -target 1.8 + -bootclasspath "$" + "${JAVA_PACKAGE_DIR}/SDLEntryTestActivity.java" + "${JAVA_PACKAGE_DIR}/SDLTestActivity.java" + $ + -cp "$:${path_android_jar}" + -d "${classes_path}" + COMMAND ${Java_JAR_EXECUTABLE} cf "${OUT_JAR}" -C "${classes_path}" . + DEPENDS $ "$" "${JAVA_PACKAGE_DIR}/SDLTestActivity.java" "${JAVA_PACKAGE_DIR}/SDLEntryTestActivity.java" + ) + add_custom_target(${TEST}-jar DEPENDS "${OUT_JAR}") + set_property(TARGET ${TEST}-jar PROPERTY OUTPUT "${OUT_JAR}") + + set(dexworkdir "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TEST}-dex.dir") + # Some CMake versions have a slow `cmake -E make_directory` implementation + if(NOT IS_DIRECTORY "${dexworkdir}") + execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${dexworkdir}") + endif() + set(classes_dex_base_name "classes.dex") + set(classes_dex "${dexworkdir}/${classes_dex_base_name}") + add_custom_command( + OUTPUT "${classes_dex}" + COMMAND SdlAndroid::d8 + $ + $ + --lib "${path_android_jar}" + --output "${dexworkdir}" + DEPENDS $ $ + ) + add_custom_target(${TEST}-dex DEPENDS "${classes_dex}") + set_property(TARGET ${TEST}-dex PROPERTY OUTPUT "${classes_dex}") + set_property(TARGET ${TEST}-dex PROPERTY OUTPUT_BASE_NAME "${classes_dex_base_name}") + + sdl_add_to_apk_unaligned(${TEST}-unaligned-apk + APK_IN ${TEST}-apk-linked + OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/intermediates" + ASSETS ${RESOURCE_FILES} + NATIVE_LIBS SDL3::SDL3-shared ${TEST} + DEX ${TEST}-dex + ) + + sdl_apk_align(${TEST}-aligned-apk ${TEST}-unaligned-apk + OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/intermediates" + ) + sdl_apk_sign(${TEST}-apk ${TEST}-aligned-apk + KEYSTORE SDL_test-debug-keystore + ) + add_dependencies(sdl-test-apks ${TEST}-apk) + + if(TARGET SdlAndroid::adb) + add_custom_target(install-${TEST} + COMMAND "${CMAKE_COMMAND}" -DACTION=install "-DAPKS=$" -P "${SDL3_SOURCE_DIR}/cmake/android/SdlAndroidScript.cmake" + DEPENDS "${TEST}-apk" + ) + add_custom_target(start-${TEST} + COMMAND "${ADB_BIN}" shell am start-activity -S "${ANDROID_MANIFEST_PACKAGE}/.SDLTestActivity" + ) + add_custom_target(build-install-start-${TEST} + COMMAND "${CMAKE_COMMAND}" -DACTION=build-install-run "-DEXECUTABLES=${TEST}" "-DBUILD_FOLDER=${CMAKE_BINARY_DIR}" -P "${SDL3_SOURCE_DIR}/cmake/android/SdlAndroidScript.cmake" + ) + endif() + + list(APPEND packages "${ANDROID_MANIFEST_PACKAGE}") + list(APPEND install_targets install-${TEST}) + endforeach() + + if(TARGET SdlAndroid::adb) + add_custom_target(install-sdl-test-apks + DEPENDS ${install_targets} + VERBATIM + ) + add_custom_target(uninstall-sdl-test-apks + COMMAND "${CMAKE_COMMAND}" "-DADB=$" -DACTION=uninstall "-DPACKAGES=${packages}" -P "${SDL3_SOURCE_DIR}/cmake/android/SdlAndroidScript.cmake" + VERBATIM + ) + endif() + endif() +endif() diff --git a/test/android/cmake/AndroidManifest.xml.cmake b/test/android/cmake/AndroidManifest.xml.cmake new file mode 100644 index 000000000..ae2ead0c2 --- /dev/null +++ b/test/android/cmake/AndroidManifest.xml.cmake @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/android/cmake/SDLEntryTestActivity.java.cmake b/test/android/cmake/SDLEntryTestActivity.java.cmake new file mode 100644 index 000000000..f8d08f19f --- /dev/null +++ b/test/android/cmake/SDLEntryTestActivity.java.cmake @@ -0,0 +1,121 @@ +package @ANDROID_MANIFEST_PACKAGE@; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +import org.libsdl.app.SDL; +import org.libsdl.app.SDLActivity; + +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; + +import android.view.View; +import android.view.ViewGroup; +import android.view.LayoutInflater; + +public class SDLEntryTestActivity extends Activity { + + public String MODIFY_ARGUMENTS = "@ANDROID_MANIFEST_PACKAGE@.MODIFY_ARGUMENTS"; + boolean isModifyingArguments; + + @Override + protected void onCreate(Bundle savedInstanceState) { + Log.v("SDL", "SDLEntryTestActivity onCreate"); + super.onCreate(savedInstanceState); + + String intent_action = getIntent().getAction(); + Log.v("SDL", "SDLEntryTestActivity intent.action = " + intent_action); + + if (intent_action == MODIFY_ARGUMENTS) { + isModifyingArguments = true; + createArgumentLayout(); + } else { + startChildActivityAndFinish(); + } + } + + protected void createArgumentLayout() { + LayoutInflater inflater = getLayoutInflater(); + View view = inflater.inflate(R.layout.arguments_layout, null); + setContentView(view); + + Button button = (Button)requireViewById(R.id.arguments_start_button); + button.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + startChildActivityAndFinish(); + } + }); + } + + protected String[] getArguments() { + if (!isModifyingArguments) { + return new String[0]; + } + EditText editText = (EditText)findViewById(R.id.arguments_edit); + String text = editText.getText().toString(); + String new_text = text.replace("[ \t]*[ \t\n]+[ \t]+", "\n").strip(); + Log.v("SDL", "text = " + text + "\n becomes \n" + new_text); + return new_text.split("\n", 0); + } + + @Override + protected void onStart() { + Log.v("SDL", "SDLEntryTestActivity onStart"); + super.onStart(); + } + + @Override + protected void onResume() { + Log.v("SDL", "SDLEntryTestActivity onResume"); + super.onResume(); + } + + @Override + protected void onPause() { + Log.v("SDL", "SDLEntryTestActivity onPause"); + super.onPause(); + } + + @Override + protected void onStop() { + Log.v("SDL", "SDLEntryTestActivity onStop"); + super.onStop(); + } + + @Override + protected void onDestroy() { + Log.v("SDL", "SDLEntryTestActivity onDestroy"); + super.onDestroy(); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + Log.v("SDL", "SDLEntryTestActivity onRestoreInstanceState"); + super.onRestoreInstanceState(savedInstanceState); + EditText editText = (EditText)findViewById(R.id.arguments_edit); + editText.setText(savedInstanceState.getCharSequence("args", ""), TextView.BufferType.EDITABLE); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + Log.v("SDL", "SDLEntryTestActivity onSaveInstanceState"); + EditText editText = (EditText)findViewById(R.id.arguments_edit); + outState.putCharSequence("args", editText.getText()); + super.onSaveInstanceState(outState); + } + + private void startChildActivityAndFinish() { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + intent.setClassName("@ANDROID_MANIFEST_PACKAGE@", "@ANDROID_MANIFEST_PACKAGE@.SDLTestActivity"); + intent.putExtra("arguments", getArguments()); + startActivity(intent); + finish(); + } +} diff --git a/test/android/cmake/SDLTestActivity.java.cmake b/test/android/cmake/SDLTestActivity.java.cmake new file mode 100644 index 000000000..c43955dba --- /dev/null +++ b/test/android/cmake/SDLTestActivity.java.cmake @@ -0,0 +1,33 @@ +package @ANDROID_MANIFEST_PACKAGE@; + +import org.libsdl.app.SDLActivity; + +import android.os.Bundle; +import android.util.Log; + +public class SDLTestActivity extends SDLActivity { + private String[] m_arguments; + + @Override + protected void onCreate(Bundle savedInstanceState) { + m_arguments = getIntent().getStringArrayExtra("arguments"); + if (m_arguments == null) { + m_arguments = new String[0]; + } + super.onCreate(savedInstanceState); + } + + @Override + protected String[] getLibraries() { + return new String[] { getString(R.string.lib_name) }; + } + + @Override + protected String[] getArguments() { + Log.v("SDLTest", "#arguments = " + m_arguments.length); + for(int i = 0; i < m_arguments.length; i++) { + Log.v("SDLTest", "argument[" + i + "] = " + m_arguments[i]); + } + return m_arguments; + } +} diff --git a/test/android/cmake/res/values/strings.xml.cmake b/test/android/cmake/res/values/strings.xml.cmake new file mode 100644 index 000000000..871c17fd7 --- /dev/null +++ b/test/android/cmake/res/values/strings.xml.cmake @@ -0,0 +1,5 @@ + + @ANDROID_MANIFEST_APP_NAME@ + @ANDROID_MANIFEST_LIB_NAME@ + @ANDROID_MANIFEST_LABEL@ + diff --git a/test/android/cmake/res/xml/shortcuts.xml.cmake b/test/android/cmake/res/xml/shortcuts.xml.cmake new file mode 100644 index 000000000..a29a2deab --- /dev/null +++ b/test/android/cmake/res/xml/shortcuts.xml.cmake @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/test/android/res/drawable/sdl-test_foreground.xml b/test/android/res/drawable/sdl-test_foreground.xml new file mode 100644 index 000000000..f9f0ed29d --- /dev/null +++ b/test/android/res/drawable/sdl-test_foreground.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/android/res/layout/arguments_layout.xml b/test/android/res/layout/arguments_layout.xml new file mode 100644 index 000000000..30493af41 --- /dev/null +++ b/test/android/res/layout/arguments_layout.xml @@ -0,0 +1,26 @@ + + + + +