2014-02-09 22:10:30 -03:00
import os
import platform
2022-06-01 08:54:08 +01:00
import subprocess
2024-05-21 15:14:59 +02:00
import sys
2022-08-23 22:21:46 +09:00
from typing import TYPE_CHECKING
2024-05-21 15:14:59 +02:00
from methods import print_error , print_warning
2024-09-27 21:36:52 +03:00
from platform_methods import validate_arch
2024-05-21 15:14:59 +02:00
2022-08-23 22:21:46 +09:00
if TYPE_CHECKING :
2023-11-24 13:31:05 -06:00
from SCons . Script . SConscript import SConsEnvironment
2022-08-23 22:21:46 +09:00
2016-10-30 19:05:14 +01:00
2014-02-09 22:10:30 -03:00
def get_name ( ) :
2016-10-30 18:44:57 +01:00
return " Android "
2014-02-09 22:10:30 -03:00
2016-10-30 19:05:14 +01:00
2014-02-09 22:10:30 -03:00
def can_build ( ) :
2022-06-01 08:54:08 +01:00
return os . path . exists ( get_env_android_sdk_root ( ) )
2017-12-18 15:39:09 +00:00
2025-01-25 13:59:30 +01:00
def get_tools ( env : " SConsEnvironment " ) :
return [ " clang " , " clang++ " , " as " , " ar " , " link " ]
2014-02-09 22:10:30 -03:00
def get_opts ( ) :
2023-03-01 14:09:30 -08:00
from SCons . Variables import BoolVariable
2016-10-30 18:44:57 +01:00
return [
2023-11-01 09:17:19 -07:00
( " ANDROID_HOME " , " Path to the Android SDK " , get_env_android_sdk_root ( ) ) ,
2022-10-18 21:39:43 -07:00
(
" ndk_platform " ,
' Target platform (android-<api>, e.g. " android- ' + str ( get_min_target_api ( ) ) + ' " ) ' ,
" android- " + str ( get_min_target_api ( ) ) ,
) ,
2023-03-01 14:09:30 -08:00
BoolVariable ( " store_release " , " Editor build for Google Play Store (for official builds only) " , False ) ,
2025-04-20 15:02:59 -07:00
BoolVariable (
2025-05-15 12:42:20 +02:00
( " generate_android_binaries " , " generate_apk " ) ,
2025-04-20 15:02:59 -07:00
" Generate APK, AAB & AAR binaries after building Android library by calling Gradle " ,
False ,
) ,
2024-11-16 21:58:23 +02:00
BoolVariable ( " swappy " , " Use Swappy Frame Pacing library " , False ) ,
2016-10-30 18:44:57 +01:00
]
2014-02-09 22:10:30 -03:00
2016-03-20 17:22:48 +08:00
2023-04-19 12:23:22 +03:00
def get_doc_classes ( ) :
return [
" EditorExportPlatformAndroid " ,
]
def get_doc_path ( ) :
return " doc_classes "
2023-11-01 09:17:19 -07:00
# Return the ANDROID_HOME environment variable.
2022-06-01 08:54:08 +01:00
def get_env_android_sdk_root ( ) :
2023-11-01 09:17:19 -07:00
return os . environ . get ( " ANDROID_HOME " , os . environ . get ( " ANDROID_SDK_ROOT " , " " ) )
2021-01-05 13:40:42 -08:00
2022-06-01 08:54:08 +01:00
def get_min_sdk_version ( platform ) :
return int ( platform . split ( " - " ) [ 1 ] )
2023-11-24 13:31:05 -06:00
def get_android_ndk_root ( env : " SConsEnvironment " ) :
2025-06-09 10:37:54 -04:00
return os . path . join ( env [ " ANDROID_HOME " ] , " ndk " , get_ndk_version ( ) )
2022-06-01 08:54:08 +01:00
# This is kept in sync with the value in 'platform/android/java/app/config.gradle'.
def get_ndk_version ( ) :
2025-05-13 08:27:36 -07:00
return " 28.1.13356709 "
2021-01-05 13:40:42 -08:00
2022-10-18 21:39:43 -07:00
# This is kept in sync with the value in 'platform/android/java/app/config.gradle'.
def get_min_target_api ( ) :
2025-05-07 12:00:20 -04:00
return 24
2022-10-18 21:39:43 -07:00
2014-02-09 22:10:30 -03:00
def get_flags ( ) :
2024-05-19 09:41:03 -05:00
return {
2024-05-10 12:07:59 +02:00
" arch " : " arm64 " ,
2024-05-19 09:41:03 -05:00
" target " : " template_debug " ,
" supported " : [ " mono " ] ,
}
2014-02-09 22:10:30 -03:00
2022-06-01 08:54:08 +01:00
# Check if Android NDK version is installed
# If not, install it.
2023-11-24 13:31:05 -06:00
def install_ndk_if_needed ( env : " SConsEnvironment " ) :
2023-11-01 09:17:19 -07:00
sdk_root = env [ " ANDROID_HOME " ]
2022-06-01 08:54:08 +01:00
if not os . path . exists ( get_android_ndk_root ( env ) ) :
2021-01-05 13:40:42 -08:00
extension = " .bat " if os . name == " nt " else " "
2025-06-09 10:37:54 -04:00
sdkmanager = os . path . join ( sdk_root , " cmdline-tools " , " latest " , " bin " , " sdkmanager " + extension )
2022-06-01 08:54:08 +01:00
if os . path . exists ( sdkmanager ) :
# Install the Android NDK
print ( " Installing Android NDK... " )
ndk_download_args = " ndk; " + get_ndk_version ( )
subprocess . check_call ( [ sdkmanager , ndk_download_args ] )
else :
2024-04-26 12:35:07 -05:00
print_error (
f ' Cannot find " { sdkmanager } " . Please ensure ANDROID_HOME is correct and cmdline-tools '
2024-05-04 20:51:21 -07:00
f ' are installed, or install NDK version " { get_ndk_version ( ) } " manually. '
2022-06-01 08:54:08 +01:00
)
2024-04-26 12:35:07 -05:00
sys . exit ( 255 )
2022-06-01 08:54:08 +01:00
env [ " ANDROID_NDK_ROOT " ] = get_android_ndk_root ( env )
2021-01-05 13:40:42 -08:00
2024-05-05 19:15:56 -03:00
def detect_swappy ( ) :
archs = [ " arm64-v8a " , " armeabi-v7a " , " x86 " , " x86_64 " ]
has_swappy = True
for arch in archs :
2024-11-16 21:58:23 +02:00
if not os . path . isfile ( f " thirdparty/swappy-frame-pacing/ { arch } /libswappy_static.a " ) :
2024-05-05 19:15:56 -03:00
has_swappy = False
return has_swappy
2023-11-24 13:31:05 -06:00
def configure ( env : " SConsEnvironment " ) :
2021-12-15 17:38:10 -08:00
# Validate arch.
supported_arches = [ " x86_32 " , " x86_64 " , " arm32 " , " arm64 " ]
2024-09-27 21:36:52 +03:00
validate_arch ( env [ " arch " ] , get_name ( ) , supported_arches )
2021-12-15 17:38:10 -08:00
2022-10-18 21:39:43 -07:00
if get_min_sdk_version ( env [ " ndk_platform " ] ) < get_min_target_api ( ) :
2024-04-26 12:35:07 -05:00
print_warning (
" Minimum supported Android target api is %d . Forcing target api %d . "
2022-10-18 21:39:43 -07:00
% ( get_min_target_api ( ) , get_min_target_api ( ) )
)
env [ " ndk_platform " ] = " android- " + str ( get_min_target_api ( ) )
2021-01-05 13:40:42 -08:00
install_ndk_if_needed ( env )
2022-06-01 08:54:08 +01:00
ndk_root = env [ " ANDROID_NDK_ROOT " ]
2016-10-30 18:44:57 +01:00
2019-08-27 11:16:33 +02:00
# Architecture
2016-10-30 18:44:57 +01:00
2021-12-15 17:38:10 -08:00
if env [ " arch " ] == " arm32 " :
2022-06-01 08:54:08 +01:00
target_triple = " armv7a-linux-androideabi "
2021-12-15 17:38:10 -08:00
elif env [ " arch " ] == " arm64 " :
2022-06-01 08:54:08 +01:00
target_triple = " aarch64-linux-android "
2021-12-15 17:38:10 -08:00
elif env [ " arch " ] == " x86_32 " :
2022-06-01 08:54:08 +01:00
target_triple = " i686-linux-android "
2021-12-15 17:38:10 -08:00
elif env [ " arch " ] == " x86_64 " :
2022-06-01 08:54:08 +01:00
target_triple = " x86_64-linux-android "
target_option = [ " -target " , target_triple + str ( get_min_sdk_version ( env [ " ndk_platform " ] ) ) ]
2022-07-07 13:26:31 +03:00
env . Append ( ASFLAGS = [ target_option , " -c " ] )
2022-06-01 08:54:08 +01:00
env . Append ( CCFLAGS = target_option )
env . Append ( LINKFLAGS = target_option )
2016-10-30 18:44:57 +01:00
2022-07-21 15:15:54 +02:00
# LTO
2022-09-13 17:01:47 +02:00
if env [ " lto " ] == " auto " : # LTO benefits for Android (size, performance) haven't been clearly established yet.
env [ " lto " ] = " none "
2022-07-21 15:15:54 +02:00
if env [ " lto " ] != " none " :
if env [ " lto " ] == " thin " :
env . Append ( CCFLAGS = [ " -flto=thin " ] )
env . Append ( LINKFLAGS = [ " -flto=thin " ] )
else :
env . Append ( CCFLAGS = [ " -flto " ] )
env . Append ( LINKFLAGS = [ " -flto " ] )
2019-08-27 11:16:33 +02:00
# Compiler configuration
2017-06-30 19:21:38 +02:00
2020-03-30 08:28:32 +02:00
env [ " SHLIBSUFFIX " ] = " .so "
2017-06-30 19:21:38 +02:00
2020-03-30 08:28:32 +02:00
if env [ " PLATFORM " ] == " win32 " :
2017-06-30 19:21:38 +02:00
env . use_windows_spawn_fix ( )
2020-03-30 08:28:32 +02:00
if sys . platform . startswith ( " linux " ) :
2016-11-13 23:54:06 +01:00
host_subpath = " linux-x86_64 "
2020-03-30 08:28:32 +02:00
elif sys . platform . startswith ( " darwin " ) :
2016-11-02 10:54:51 +01:00
host_subpath = " darwin-x86_64 "
2020-03-30 08:28:32 +02:00
elif sys . platform . startswith ( " win " ) :
if platform . machine ( ) . endswith ( " 64 " ) :
2016-11-02 10:54:51 +01:00
host_subpath = " windows-x86_64 "
2016-10-30 18:44:57 +01:00
else :
2016-11-13 23:54:06 +01:00
host_subpath = " windows "
2016-10-30 18:57:40 +01:00
2025-06-09 10:37:54 -04:00
toolchain_path = os . path . join ( ndk_root , " toolchains " , " llvm " , " prebuilt " , host_subpath )
compiler_path = os . path . join ( toolchain_path , " bin " )
2016-10-30 18:44:57 +01:00
2025-06-09 10:37:54 -04:00
env [ " CC " ] = os . path . join ( compiler_path , " clang " )
env [ " CXX " ] = os . path . join ( compiler_path , " clang++ " )
env [ " AR " ] = os . path . join ( compiler_path , " llvm-ar " )
env [ " RANLIB " ] = os . path . join ( compiler_path , " llvm-ranlib " )
env [ " AS " ] = os . path . join ( compiler_path , " clang " )
2019-07-30 15:33:24 +02:00
2020-03-30 08:28:32 +02:00
env . Append (
2024-11-16 21:58:23 +02:00
CCFLAGS = ( [ " -fpic " , " -ffunction-sections " , " -funwind-tables " , " -fstack-protector-strong " , " -fvisibility=hidden " ] )
2020-03-30 08:28:32 +02:00
)
2014-10-07 01:31:49 -03:00
2024-05-05 19:15:56 -03:00
has_swappy = detect_swappy ( )
if not has_swappy :
print_warning (
2025-02-28 15:01:07 -03:00
" Swappy Frame Pacing not detected! It is strongly recommended you download it from https://github.com/godotengine/godot-swappy/releases and extract it so that the following files can be found: \n "
2024-05-05 19:15:56 -03:00
+ " thirdparty/swappy-frame-pacing/arm64-v8a/libswappy_static.a \n "
+ " thirdparty/swappy-frame-pacing/armeabi-v7a/libswappy_static.a \n "
+ " thirdparty/swappy-frame-pacing/x86/libswappy_static.a \n "
+ " thirdparty/swappy-frame-pacing/x86_64/libswappy_static.a \n "
+ " Without Swappy, Godot apps on Android will inevitable suffer stutter and struggle to keep consistent 30/60/90/120 fps. Though Swappy cannot guarantee your app will be stutter-free, not having Swappy will guarantee there will be stutter even on the best phones and the most simple of scenes. "
)
if env [ " swappy " ] :
print_error ( " Use build option `swappy=no` to ignore missing Swappy dependency and build without it. " )
sys . exit ( 255 )
2022-06-01 08:54:08 +01:00
if get_min_sdk_version ( env [ " ndk_platform " ] ) > = 24 :
2019-03-26 18:51:13 +01:00
env . Append ( CPPDEFINES = [ ( " _FILE_OFFSET_BITS " , 64 ) ] )
2021-12-15 17:38:10 -08:00
if env [ " arch " ] == " x86_32 " :
2024-05-05 19:15:56 -03:00
if has_swappy :
2024-11-16 21:58:23 +02:00
env . Append ( LIBPATH = [ " #thirdparty/swappy-frame-pacing/x86 " ] )
2024-05-05 19:15:56 -03:00
elif env [ " arch " ] == " x86_64 " :
if has_swappy :
2024-11-16 21:58:23 +02:00
env . Append ( LIBPATH = [ " #thirdparty/swappy-frame-pacing/x86_64 " ] )
2021-12-15 17:38:10 -08:00
elif env [ " arch " ] == " arm32 " :
2024-11-16 21:58:23 +02:00
env . Append ( CCFLAGS = [ " -march=armv7-a " , " -mfloat-abi=softfp " ] )
2020-03-30 08:28:32 +02:00
env . Append ( CPPDEFINES = [ " __ARM_ARCH_7__ " , " __ARM_ARCH_7A__ " ] )
2021-10-12 15:27:30 +02:00
env . Append ( CPPDEFINES = [ " __ARM_NEON__ " ] )
2024-05-05 19:15:56 -03:00
if has_swappy :
2024-11-16 21:58:23 +02:00
env . Append ( LIBPATH = [ " #thirdparty/swappy-frame-pacing/armeabi-v7a " ] )
2021-12-15 17:38:10 -08:00
elif env [ " arch " ] == " arm64 " :
2020-03-30 08:28:32 +02:00
env . Append ( CCFLAGS = [ " -mfix-cortex-a53-835769 " ] )
env . Append ( CPPDEFINES = [ " __ARM_ARCH_8A__ " ] )
2024-05-05 19:15:56 -03:00
if has_swappy :
2024-11-16 21:58:23 +02:00
env . Append ( LIBPATH = [ " #thirdparty/swappy-frame-pacing/arm64-v8a " ] )
2017-07-25 12:28:31 +02:00
2024-07-23 08:52:40 +03:00
env . Append ( CCFLAGS = [ " -ffp-contract=off " ] )
2019-08-27 11:16:33 +02:00
# Link flags
2019-07-30 15:49:31 +02:00
2024-11-16 21:58:23 +02:00
env . Append ( LINKFLAGS = [ " -Wl,--gc-sections " , " -Wl,--no-undefined " , " -Wl,-z,now " ] )
env . Append ( LINKFLAGS = [ " -Wl,-soname,libgodot_android.so " ] )
2020-03-30 08:28:32 +02:00
env . Prepend ( CPPPATH = [ " #platform/android " ] )
2022-10-03 11:43:20 +02:00
env . Append ( CPPDEFINES = [ " ANDROID_ENABLED " , " UNIX_ENABLED " ] )
2022-11-11 15:30:06 -08:00
env . Append ( LIBS = [ " OpenSLES " , " EGL " , " android " , " log " , " z " , " dl " ] )
2021-08-12 14:24:54 +03:00
if env [ " vulkan " ] :
2023-12-22 20:49:29 +08:00
env . Append ( CPPDEFINES = [ " VULKAN_ENABLED " , " RD_ENABLED " ] )
2024-05-05 19:15:56 -03:00
if has_swappy :
env . Append ( CPPDEFINES = [ " SWAPPY_FRAME_PACING_ENABLED " ] )
env . Append ( LIBS = [ " swappy_static " ] )
2021-08-12 14:24:54 +03:00
if not env [ " use_volk " ] :
env . Append ( LIBS = [ " vulkan " ] )
2022-11-11 15:30:06 -08:00
if env [ " opengl3 " ] :
env . Append ( CPPDEFINES = [ " GLES3_ENABLED " ] )
env . Append ( LIBS = [ " GLESv3 " ] )