diff --git a/.clang-format-ignore b/.clang-format-ignore index 97e66e0aa..52ac9b665 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -7,6 +7,9 @@ cmake-build-*/* libtransmission/version.h web/node_modules/* +# android +android/* + # third-party maintained projects third-party/* diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index a2590a2d8..e0945a780 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -12,6 +12,7 @@ jobs: what-to-make: runs-on: ubuntu-22.04 outputs: + make-android: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.android-changed == '1' }} make-cli: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.cli-changed == '1' }} make-daemon: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.daemon-changed == '1' }} make-dist: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.dist-changed == '1' }} @@ -53,6 +54,7 @@ jobs: git diff --exit-code "$MERGE_BASE" -- "$@" echo "$name-changed=$?" >> "$GITHUB_OUTPUT" } + get_changes android CMakeLists.txt cmake third-party libtransmission android get_changes cli CMakeLists.txt cmake Transmission.xcodeproj third-party libtransmission cli get_changes any-code CMakeLists.txt cmake Transmission.xcodeproj libtransmission cli daemon gtk macosx qt utils tests web third-party get_changes our-code CMakeLists.txt cmake Transmission.xcodeproj libtransmission cli daemon gtk macosx qt utils tests web @@ -793,3 +795,53 @@ jobs: with: name: binaries-${{ github.job }} path: pfx/**/* + + android: + needs: [ what-to-make ] + if: ${{ needs.what-to-make.outputs.make-android == 'true' }} + runs-on: ubuntu-22.04 + env: + VCPKG_DEFAULT_TRIPLET: arm64-android + steps: + - name: Get Dependencies + run: | + set -ex + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + ninja-build + + - name: Get Source + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + with: + gradle-version: 7.6 + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + + - name: Install NDK + run: sdkmanager "ndk;26.1.10909125" + + - name: Setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: 53bef8994c541b6561884a8395ea35715ece75db # 2024.01.12 + + - name: Install vcpkg packages + run: | + vcpkg install openssl curl + + - name: Build Transmission + working-directory: ./android + run: | + gradle build diff --git a/.gitignore b/.gitignore index fa2bc39f9..ef369248b 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,9 @@ node_modules/ /third-party/miniupnp/miniupnpcstrings.h /third-party/suffixes_dafsa.h /web/public_html/transmission-app.js.map +/android/.cxx +/android/.gradle +/android/build # clangd compile commands compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 339260cc7..fda16eeac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,10 @@ if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) FORCE) endif() +if(VCPKG_TARGET_ANDROID) + include(cmake/VcpkgAndroid.cmake) +endif() + project(transmission) set(TR_THIRD_PARTY_DIR_NAME third-party) diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 000000000..04fd2e2ca --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,69 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath "com.android.tools.build:gradle:7.2.1" + } +} + +apply plugin: "com.android.library" + + +def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') } + +def getExtOrDefault(name) { + return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["Transmission_" + name] +} + +def getExtOrIntegerDefault(name) { + return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["Transmission_" + name]).toInteger() +} + +android { + ndkVersion getExtOrDefault("ndkVersion") + compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") + + defaultConfig { + minSdkVersion getExtOrIntegerDefault("minSdkVersion") + targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") + externalNativeBuild { + cmake { + abiFilters "arm64-v8a" + arguments "-DVCPKG_TARGET_ANDROID=ON", "-DWITH_CRYPTO=openssl" + } + } + } + externalNativeBuild { + cmake { + version "3.22.1" + path "../CMakeLists.txt" + } + } + buildTypes { + release { + minifyEnabled false + } + } + + lintOptions { + disable "GradleCompatible" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + buildFeatures { + prefab true + } +} + +repositories { + mavenCentral() + google() +} + diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 000000000..a450253b4 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,6 @@ +Transmission_kotlinVersion=1.7.0 +# quotactl is defined from >= 26 +Transmission_minSdkVersion=26 +Transmission_targetSdkVersion=31 +Transmission_compileSdkVersion=31 +Transmission_ndkVersion=26.1.10909125 diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000..42defb2ac --- /dev/null +++ b/android/src/main/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/cmake/TrMacros.cmake b/cmake/TrMacros.cmake index fc914c708..797b655b5 100644 --- a/cmake/TrMacros.cmake +++ b/cmake/TrMacros.cmake @@ -159,6 +159,7 @@ macro(tr_add_external_auto_library ID DIRNAME LIBNAME) set(${ID}_LIBRARIES ${${ID}_LIBRARY}) set(${ID}_EXT_PROJ_CMAKE_ARGS) + if(APPLE) string(REPLACE ";" "$" ${ID}_CMAKE_OSX_ARCHITECTURES "${CMAKE_OSX_ARCHITECTURES}") list(APPEND ${ID}_EXT_PROJ_CMAKE_ARGS @@ -167,6 +168,21 @@ macro(tr_add_external_auto_library ID DIRNAME LIBNAME) "-DCMAKE_OSX_SYSROOT:PATH=${CMAKE_OSX_SYSROOT}") endif() + if(ANDROID) + list(APPEND ${ID}_EXT_PROJ_CMAKE_ARGS + "-DANDROID_PLATFORM=${ANDROID_PLATFORM}" + "-DANDROID_NDK=${ANDROID_NDK}" + "-DANDROID_ABI=${ANDROID_ABI}" + "-DANDROID_STL=${ANDROID_STL}" + "-DCMAKE_ANDROID_NDK=${CMAKE_ANDROID_NDK}" + "-DCMAKE_ANDROID_ARCH_ABI=${CMAKE_ANDROID_ARCH_ABI}") + endif() + + if(VCPKG_CHAINLOAD_TOOLCHAIN_FILE) + list(APPEND ${ID}_EXT_PROJ_CMAKE_ARGS + "-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=${VCPKG_CHAINLOAD_TOOLCHAIN_FILE}") + endif() + ExternalProject_Add( ${${ID}_UPSTREAM_TARGET} PREFIX "${TR_THIRD_PARTY_BINARY_DIR}/${DIRNAME}.bld" diff --git a/cmake/VcpkgAndroid.cmake b/cmake/VcpkgAndroid.cmake new file mode 100644 index 000000000..02e47599b --- /dev/null +++ b/cmake/VcpkgAndroid.cmake @@ -0,0 +1,100 @@ +# Copied from https://github.com/microsoft/vcpkg-docs/blob/main/vcpkg/examples/vcpkg_android_example_cmake_script/cmake/vcpkg_android.cmake +# +# vcpkg_android.cmake +# +# Helper script when using vcpkg with cmake. It should be triggered via the variable VCPKG_TARGET_ANDROID +# +# For example: +# if (VCPKG_TARGET_ANDROID) +# include("cmake/vcpkg_android.cmake") +# endif() +# +# This script will: +# 1 & 2. check the presence of needed env variables: ANDROID_NDK_HOME and VCPKG_ROOT +# 3. set VCPKG_TARGET_TRIPLET according to ANDROID_ABI +# 4. Combine vcpkg and Android toolchains by setting CMAKE_TOOLCHAIN_FILE +# and VCPKG_CHAINLOAD_TOOLCHAIN_FILE + +# Note: VCPKG_TARGET_ANDROID is not an official vcpkg variable. +# it is introduced for the need of this script + +if (VCPKG_TARGET_ANDROID) + + # + # 1. Check the presence of environment variable ANDROID_NDK_HOME + # + if (NOT DEFINED ENV{ANDROID_NDK_HOME}) + message(FATAL_ERROR " + Please set an environment variable ANDROID_NDK_HOME + For example: + export ANDROID_NDK_HOME=/home/your-account/Android/Sdk/ndk-bundle + Or: + export ANDROID_NDK_HOME=/home/your-account/Android/android-ndk-r21b + ") + endif() + + # + # 2. Check the presence of environment variable VCPKG_ROOT + # + if (NOT DEFINED ENV{VCPKG_ROOT}) + message(FATAL_ERROR " + Please set an environment variable VCPKG_ROOT + For example: + export VCPKG_ROOT=/path/to/vcpkg + ") + endif() + + + # + # 3. Set VCPKG_TARGET_TRIPLET according to ANDROID_ABI + # + # There are four different Android ABI, each of which maps to + # a vcpkg triplet. The following table outlines the mapping from vcpkg architectures to android architectures + # + # |VCPKG_TARGET_TRIPLET | ANDROID_ABI | + # |---------------------------|----------------------| + # |arm64-android | arm64-v8a | + # |arm-android | armeabi-v7a | + # |x64-android | x86_64 | + # |x86-android | x86 | + # + # The variable must be stored in the cache in order to successfully the two toolchains. + # + if (ANDROID_ABI MATCHES "arm64-v8a") + set(VCPKG_TARGET_TRIPLET "arm64-android" CACHE STRING "" FORCE) + elseif(ANDROID_ABI MATCHES "armeabi-v7a") + set(VCPKG_TARGET_TRIPLET "arm-android" CACHE STRING "" FORCE) + elseif(ANDROID_ABI MATCHES "x86_64") + set(VCPKG_TARGET_TRIPLET "x64-android" CACHE STRING "" FORCE) + elseif(ANDROID_ABI MATCHES "x86") + set(VCPKG_TARGET_TRIPLET "x86-android" CACHE STRING "" FORCE) + else() + message(FATAL_ERROR " + Please specify ANDROID_ABI + For example + cmake ... -DANDROID_ABI=armeabi-v7a + + Possible ABIs are: arm64-v8a, armeabi-v7a, x64-android, x86-android + ") + endif() + message("vcpkg_android.cmake: VCPKG_TARGET_TRIPLET was set to ${VCPKG_TARGET_TRIPLET}") + + + # + # 4. Combine vcpkg and Android toolchains + # + + # vcpkg and android both provide dedicated toolchains: + # + # vcpkg_toolchain_file=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake + # android_toolchain_file=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake + # + # When using vcpkg, the vcpkg toolchain shall be specified first. + # However, vcpkg provides a way to preload and additional toolchain, + # with the VCPKG_CHAINLOAD_TOOLCHAIN_FILE option. + set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE $ENV{ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake) + set(CMAKE_TOOLCHAIN_FILE $ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake) + message("vcpkg_android.cmake: CMAKE_TOOLCHAIN_FILE was set to ${CMAKE_TOOLCHAIN_FILE}") + message("vcpkg_android.cmake: VCPKG_CHAINLOAD_TOOLCHAIN_FILE was set to ${VCPKG_CHAINLOAD_TOOLCHAIN_FILE}") + +endif(VCPKG_TARGET_ANDROID) diff --git a/libtransmission/CMakeLists.txt b/libtransmission/CMakeLists.txt index 639974f02..c7192d62d 100644 --- a/libtransmission/CMakeLists.txt +++ b/libtransmission/CMakeLists.txt @@ -271,6 +271,10 @@ target_include_directories(${TR_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/..) +if(ANDROID) + find_library(log-lib log) +endif() + target_link_libraries(${TR_NAME} PRIVATE Threads::Threads @@ -295,6 +299,7 @@ target_link_libraries(${TR_NAME} $<$:crypt32> $<$:shlwapi> "$<$:-framework Foundation>" + "$<$:${log-lib}>" PUBLIC fmt::fmt-header-only small::small diff --git a/libtransmission/file-capacity.cc b/libtransmission/file-capacity.cc index 326b318c5..2afdbfcf2 100644 --- a/libtransmission/file-capacity.cc +++ b/libtransmission/file-capacity.cc @@ -30,6 +30,9 @@ #else #include /* quotactl() */ #endif +#if !defined(btodb) && defined(QIF_DQBLKSIZE_BITS) +#define btodb(num) ((num) >> QIF_DQBLKSIZE_BITS) +#endif #ifdef HAVE_GETMNTENT #ifdef __sun #include