diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 197444968..b7de50fcc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,7 @@ concurrency: env: DISPLAY: ":99" # Display number to use for the X server GALLIUM_DRIVER: llvmpipe # Use Mesa 3D software OpenGL renderer + ANDROID_NDK_VERSION: "26.1.10909125" # Android NDK version to use defaults: run: @@ -64,20 +65,47 @@ jobs: config: { name: Static with PCH (GCC), flags: -GNinja -DSFML_USE_MESA3D=TRUE -DCMAKE_CXX_COMPILER=g++ -DBUILD_SHARED_LIBS=FALSE -DSFML_ENABLE_PCH=1 } - platform: { name: macOS, os: macos-12 } config: { name: Frameworks, flags: -GNinja -DSFML_BUILD_FRAMEWORKS=TRUE -DBUILD_SHARED_LIBS=TRUE } - - platform: { name: Android, os: ubuntu-22.04 } - config: { name: x86 (API 21), flags: -GNinja -DCMAKE_ANDROID_ARCH_ABI=x86 -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=21 -DCMAKE_ANDROID_NDK=$ANDROID_SDK_ROOT/ndk/26.1.10909125 -DBUILD_SHARED_LIBS=TRUE -DCMAKE_ANDROID_STL_TYPE=c++_shared, arch: x86, api: 21 } - type: { name: Release } - - platform: { name: Android, os: ubuntu-22.04 } - config: { name: x86_64 (API 24), flags: -GNinja -DCMAKE_ANDROID_ARCH_ABI=x86_64 -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=24 -DCMAKE_ANDROID_NDK=$ANDROID_SDK_ROOT/ndk/26.1.10909125 -DBUILD_SHARED_LIBS=TRUE -DCMAKE_ANDROID_STL_TYPE=c++_shared, arch: x86_64, api: 24 } - type: { name: Release } - - platform: { name: Android, os: ubuntu-22.04 } - config: { name: armeabi-v7a (API 29), flags: -GNinja -DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=29 -DCMAKE_ANDROID_NDK=$ANDROID_SDK_ROOT/ndk/26.1.10909125 -DBUILD_SHARED_LIBS=TRUE -DCMAKE_ANDROID_STL_TYPE=c++_shared, arch: armeabi-v7a, api: 29 } - type: { name: Debug, flags: -DCMAKE_BUILD_TYPE=Debug } - - platform: { name: Android, os: ubuntu-22.04 } - config: { name: arm64-v8a (API 33), flags: -GNinja -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=33 -DCMAKE_ANDROID_NDK=$ANDROID_SDK_ROOT/ndk/26.1.10909125 -DBUILD_SHARED_LIBS=TRUE -DCMAKE_ANDROID_STL_TYPE=c++_shared, arch: arm64-v8a, api: 33 } - type: { name: Debug, flags: -DCMAKE_BUILD_TYPE=Debug } - platform: { name: macOS , os: macos-12 } config: { name: System Deps, flags: -GNinja -DBUILD_SHARED_LIBS=TRUE -DSFML_USE_SYSTEM_DEPS=TRUE } + - platform: { name: Android, os: ubuntu-latest } + config: + name: x86 (API 21) + flags: -GNinja -DCMAKE_ANDROID_ARCH_ABI=x86 -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=21 -DCMAKE_ANDROID_NDK=$ANDROID_NDK_ROOT -DBUILD_SHARED_LIBS=TRUE -DCMAKE_ANDROID_STL_TYPE=c++_shared -DSFML_RUN_DISPLAY_TESTS=OFF + arch: x86 + api: 21 + libcxx: i686-linux-android/libc++_shared.so + emuarch: x86 + emuapi: 29 + type: { name: Release } + - platform: { name: Android, os: ubuntu-latest } + config: + name: x86_64 (API 24) + flags: -GNinja -DCMAKE_ANDROID_ARCH_ABI=x86_64 -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=24 -DCMAKE_ANDROID_NDK=$ANDROID_NDK_ROOT -DBUILD_SHARED_LIBS=TRUE -DCMAKE_ANDROID_STL_TYPE=c++_shared -DSFML_RUN_DISPLAY_TESTS=OFF + arch: x86_64 + api: 24 + libcxx: x86_64-linux-android/libc++_shared.so + emuarch: x86_64 + emuapi: 34 + type: { name: Release } + - platform: { name: Android, os: ubuntu-latest } + config: + name: armeabi-v7a (API 29) + flags: -GNinja -DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=29 -DCMAKE_ANDROID_NDK=$ANDROID_NDK_ROOT -DBUILD_SHARED_LIBS=TRUE -DCMAKE_ANDROID_STL_TYPE=c++_shared + arch: armeabi-v7a + api: 29 + # There are no emulators available for armeabi-v7a so we skip running the tests (we still build them) by not specifying emuapi + type: { name: Debug, flags: -DCMAKE_BUILD_TYPE=Debug } + - platform: { name: Android, os: ubuntu-latest } + config: + name: arm64-v8a (API 33) + flags: -GNinja -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=33 -DCMAKE_ANDROID_NDK=$ANDROID_NDK_ROOT -DBUILD_SHARED_LIBS=TRUE -DCMAKE_ANDROID_STL_TYPE=c++_shared -DSFML_RUN_DISPLAY_TESTS=OFF + arch: arm64-v8a + api: 33 + libcxx: aarch64-linux-android/libc++_shared.so + emuarch: arm64-v8a + emuapi: 27 + emuflags: -qemu -machine virt + type: { name: Debug, flags: -DCMAKE_BUILD_TYPE=Debug } steps: @@ -107,11 +135,17 @@ jobs: echo "CLANG_VERSION=$CLANG_VERSION" >> $GITHUB_ENV sudo apt-get update && sudo apt-get install xorg-dev libxrandr-dev libxcursor-dev libudev-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1-mesa-dev libdrm-dev libgbm-dev xvfb fluxbox ccache gcovr ${{ matrix.platform.name == 'Linux Clang' && 'llvm-$CLANG_VERSION' || '' }} + # LIBCXX_SHARED_SO is the path used by CMake to copy the necessary runtime library to the AVD + # We find it by searching ANDROID_NDK_ROOT for file paths ending with matrix.config.libcxx - name: Install Android Components if: matrix.platform.name == 'Android' run: | echo "y" | ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --install "build-tools;33.0.2" echo "y" | ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --install "ndk;26.1.10909125" + ANDROID_NDK_ROOT=$(echo $ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION) + echo "ANDROID_NDK_ROOT=$ANDROID_NDK_ROOT" >> $GITHUB_ENV + LIBCXX_SHARED_SO=$(find $ANDROID_NDK_ROOT -path \*/${{ matrix.config.libcxx }}) + echo "LIBCXX_SHARED_SO=$LIBCXX_SHARED_SO" >> $GITHUB_ENV - name: Install macOS Tools if: runner.os == 'macOS' @@ -197,11 +231,11 @@ jobs: # Make use of a test to print OpenGL vendor/renderer/version info to the console find build/bin -name test-sfml-window -or -name test-sfml-window.exe -exec sh -c "{} *sf::Context* --section=\"Version String\" --success | grep OpenGL" \; - - name: Test + - name: Test (Windows) if: runner.os == 'Windows' && !contains(matrix.platform.name, 'MinGW') run: cmake --build build --target runtests --config ${{ matrix.type.name == 'Debug' && 'Debug' || 'Release' }} - - name: Test + - name: Test (Linux/macOS/MinGW) if: (runner.os != 'Windows' || contains(matrix.platform.name, 'MinGW')) && !contains(matrix.platform.name, 'iOS') && !contains(matrix.platform.name, 'Android') run: | ctest --test-dir build --output-on-failure -C ${{ matrix.type.name == 'Debug' && 'Debug' || 'Release' }} --repeat until-pass:3 @@ -210,6 +244,47 @@ jobs: gcovr -r $GITHUB_WORKSPACE -x build/coverage.out -s -f 'src/SFML/.*' -f 'include/SFML/.*' ${{ matrix.platform.gcovr_options }} $GITHUB_WORKSPACE fi + - name: Enable KVM + if: contains(matrix.platform.name, 'Android') && matrix.config.emuapi + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: Cache AVD + if: contains(matrix.platform.name, 'Android') && matrix.config.emuapi + uses: actions/cache@v4 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-${{ matrix.config.emuarch }}-${{ matrix.config.emuapi }} + + - name: Create AVD and Generate Snapshot for Caching + if: contains(matrix.platform.name, 'Android') && matrix.config.emuapi && steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.config.emuapi }} + arch: ${{ matrix.config.emuarch }} + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none ${{ matrix.config.emuflags }} + disable-animations: false + script: echo "Generated AVD snapshot for caching." + + - name: Test (Android) + if: contains(matrix.platform.name, 'Android') && matrix.config.emuapi + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.config.emuapi }} + arch: ${{ matrix.config.emuarch }} + force-avd-creation: false + emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none ${{ matrix.config.emuflags }} + disable-animations: true + script: | + cmake --build build --config ${{ matrix.type.name == 'Debug' && 'Debug' || 'Release' }} --target prepare-android-files + ctest --test-dir build --output-on-failure -C ${{ matrix.type.name == 'Debug' && 'Debug' || 'Release' }} --repeat until-pass:3 + - name: Upload Coverage Report to Coveralls if: matrix.type.name == 'Debug' && github.repository == 'SFML/SFML' && !contains(matrix.platform.name, 'iOS') && !contains(matrix.platform.name, 'Android') # Disable upload in forks uses: coverallsapp/github-action@v2 diff --git a/cmake/Macros.cmake b/cmake/Macros.cmake index 38e97098a..919b6d4f4 100644 --- a/cmake/Macros.cmake +++ b/cmake/Macros.cmake @@ -368,6 +368,11 @@ function(sfml_add_test target SOURCES DEPENDS) # Delay test registration when cross compiling to avoid running crosscompiled app on host OS if(CMAKE_CROSSCOMPILING) set(CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE PRE_TEST) + + # When running tests on Android, use a custom shell script to invoke commands using adb shell + if(SFML_OS_ANDROID) + set_target_properties(${target} PROPERTIES CROSSCOMPILING_EMULATOR "${PROJECT_BINARY_DIR}/run-in-adb-shell.sh") + endif() endif() # Add the test diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6d60480e0..26fc8a2ff 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -146,6 +146,41 @@ set(AUDIO_SRC ) sfml_add_test(test-sfml-audio "${AUDIO_SRC}" SFML::Audio) +if(SFML_OS_ANDROID AND DEFINED ENV{LIBCXX_SHARED_SO}) + # Because we can only write to the tmp directory on the Android virtual device we will need to build our directory tree under it + set(TARGET_DIR "/data/local/tmp/$") + + # Generate script that copies necessary files over to the Android virtual device + file(GENERATE OUTPUT "${PROJECT_BINARY_DIR}/prepare-android-files.sh" CONTENT + "#!/bin/bash\n\ + adb shell \"mkdir -p ${TARGET_DIR}\"\n\ + adb push $ ${TARGET_DIR}\n\ + adb push $ ${TARGET_DIR}\n\ + adb push $ ${TARGET_DIR}\n\ + adb push $ ${TARGET_DIR}\n\ + adb push $ ${TARGET_DIR}\n\ + adb push $ ${TARGET_DIR}\n\ + adb push $ ${TARGET_DIR}\n\ + adb push $ ${TARGET_DIR}\n\ + adb push $ ${TARGET_DIR}\n\ + adb push $ ${TARGET_DIR}\n\ + adb push $ ${TARGET_DIR}\n\ + adb push $ ${TARGET_DIR}\n\ + adb push $ENV{LIBCXX_SHARED_SO} ${TARGET_DIR}\n\ + adb push ${CMAKE_CURRENT_LIST_DIR} ${TARGET_DIR}\n\ + adb shell \"chmod -R 775 ${TARGET_DIR} && ls -la ${TARGET_DIR}\"\n" + FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) + + # Add the target to invoke the file copy script + add_custom_target(prepare-android-files COMMAND "${PROJECT_BINARY_DIR}/prepare-android-files.sh") + + # Generate proxy script that translates CTest commands into adb shell commands + file(GENERATE OUTPUT "${PROJECT_BINARY_DIR}/run-in-adb-shell.sh" CONTENT + "#!/bin/bash\n\ + adb shell \"cd ${TARGET_DIR}/test; LD_LIBRARY_PATH=${TARGET_DIR} /data/local/tmp/$1 \\\"$2\\\" \\\"$3\\\" \\\"$4\\\" \\\"$5\\\" \\\"$6\\\" \\\"$7\\\" \\\"$8\\\" \\\"$9\\\"\"\n" + FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) +endif() + if(SFML_ENABLE_COVERAGE AND SFML_OS_WINDOWS AND NOT SFML_COMPILER_GCC) # Try to find and use OpenCppCoverage for coverage reporting when building with MSVC find_program(OpenCppCoverage_BINARY "OpenCppCoverage.exe")