detect.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import os
  2. import sys
  3. import platform
  4. import subprocess
  5. def is_active():
  6. return True
  7. def get_name():
  8. return "Android"
  9. def can_build():
  10. return os.path.exists(get_env_android_sdk_root())
  11. def get_opts():
  12. from SCons.Variables import BoolVariable, EnumVariable
  13. return [
  14. ("ANDROID_SDK_ROOT", "Path to the Android SDK", get_env_android_sdk_root()),
  15. ("ndk_platform", 'Target platform (android-<api>, e.g. "android-19")', "android-19"),
  16. EnumVariable("android_arch", "Target architecture", "armv7", ("armv7", "arm64v8", "x86", "x86_64")),
  17. BoolVariable("android_neon", "Enable NEON support (armv7 only)", True),
  18. ]
  19. # Return the ANDROID_SDK_ROOT environment variable.
  20. def get_env_android_sdk_root():
  21. return os.environ.get("ANDROID_SDK_ROOT", -1)
  22. def get_min_sdk_version(platform):
  23. return int(platform.split("-")[1])
  24. def get_android_ndk_root(env):
  25. return env["ANDROID_SDK_ROOT"] + "/ndk/" + get_ndk_version()
  26. # This is kept in sync with the value in 'platform/android/java/app/config.gradle'.
  27. def get_ndk_version():
  28. return "23.2.8568313"
  29. def get_flags():
  30. return [
  31. ("tools", False),
  32. ]
  33. # Check if Android NDK version is installed
  34. # If not, install it.
  35. def install_ndk_if_needed(env):
  36. print("Checking for Android NDK...")
  37. sdk_root = env["ANDROID_SDK_ROOT"]
  38. if not os.path.exists(get_android_ndk_root(env)):
  39. extension = ".bat" if os.name == "nt" else ""
  40. sdkmanager = sdk_root + "/cmdline-tools/latest/bin/sdkmanager" + extension
  41. if os.path.exists(sdkmanager):
  42. # Install the Android NDK
  43. print("Installing Android NDK...")
  44. ndk_download_args = "ndk;" + get_ndk_version()
  45. subprocess.check_call([sdkmanager, ndk_download_args])
  46. else:
  47. print("Cannot find " + sdkmanager)
  48. print(
  49. "Please ensure ANDROID_SDK_ROOT is correct and cmdline-tools are installed, or install NDK version "
  50. + get_ndk_version()
  51. + " manually."
  52. )
  53. sys.exit()
  54. env["ANDROID_NDK_ROOT"] = get_android_ndk_root(env)
  55. def configure(env):
  56. install_ndk_if_needed(env)
  57. ndk_root = env["ANDROID_NDK_ROOT"]
  58. # Architecture
  59. if env["android_arch"] not in ["armv7", "arm64v8", "x86", "x86_64"]:
  60. env["android_arch"] = "arm64v8"
  61. neon_text = ""
  62. if env["android_arch"] == "armv7" and env["android_neon"]:
  63. neon_text = " (with NEON)"
  64. print("Building for Android (" + env["android_arch"] + ")" + neon_text)
  65. if get_min_sdk_version(env["ndk_platform"]) < 21:
  66. if env["android_arch"] == "x86_64" or env["android_arch"] == "arm64v8":
  67. print(
  68. "WARNING: android_arch="
  69. + env["android_arch"]
  70. + " is not supported by ndk_platform lower than android-21; setting ndk_platform=android-21"
  71. )
  72. env["ndk_platform"] = "android-21"
  73. if env["android_arch"] == "armv7":
  74. target_triple = "armv7a-linux-androideabi"
  75. bin_utils = "arm-linux-androideabi"
  76. if env["android_neon"]:
  77. env.extra_suffix = ".armv7.neon" + env.extra_suffix
  78. else:
  79. env.extra_suffix = ".armv7" + env.extra_suffix
  80. elif env["android_arch"] == "arm64v8":
  81. target_triple = "aarch64-linux-android"
  82. bin_utils = target_triple
  83. env.extra_suffix = ".armv8" + env.extra_suffix
  84. elif env["android_arch"] == "x86":
  85. target_triple = "i686-linux-android"
  86. bin_utils = target_triple
  87. env.extra_suffix = ".x86" + env.extra_suffix
  88. elif env["android_arch"] == "x86_64":
  89. target_triple = "x86_64-linux-android"
  90. bin_utils = target_triple
  91. env.extra_suffix = ".x86_64" + env.extra_suffix
  92. target_option = ["-target", target_triple + str(get_min_sdk_version(env["ndk_platform"]))]
  93. env.Append(CCFLAGS=target_option)
  94. env.Append(LINKFLAGS=target_option)
  95. # Build type
  96. if env["target"].startswith("release"):
  97. if env["optimize"] == "speed": # optimize for speed (default)
  98. # `-O2` is more friendly to debuggers than `-O3`, leading to better crash backtraces
  99. # when using `target=release_debug`.
  100. opt = "-O3" if env["target"] == "release" else "-O2"
  101. env.Append(CCFLAGS=[opt, "-fomit-frame-pointer"])
  102. elif env["optimize"] == "size": # optimize for size
  103. env.Append(CCFLAGS=["-Oz"])
  104. env.Append(CPPDEFINES=["NDEBUG"])
  105. env.Append(CCFLAGS=["-ftree-vectorize"])
  106. elif env["target"] == "debug":
  107. env.Append(LINKFLAGS=["-O0"])
  108. env.Append(CCFLAGS=["-O0", "-g", "-fno-limit-debug-info"])
  109. env.Append(CPPDEFINES=["_DEBUG"])
  110. env.Append(CPPFLAGS=["-UNDEBUG"])
  111. # Compiler configuration
  112. env["SHLIBSUFFIX"] = ".so"
  113. if env["PLATFORM"] == "win32":
  114. env.use_windows_spawn_fix()
  115. if sys.platform.startswith("linux"):
  116. host_subpath = "linux-x86_64"
  117. elif sys.platform.startswith("darwin"):
  118. host_subpath = "darwin-x86_64"
  119. elif sys.platform.startswith("win"):
  120. if platform.machine().endswith("64"):
  121. host_subpath = "windows-x86_64"
  122. else:
  123. host_subpath = "windows"
  124. toolchain_path = ndk_root + "/toolchains/llvm/prebuilt/" + host_subpath
  125. compiler_path = toolchain_path + "/bin"
  126. bin_utils_path = toolchain_path + "/" + bin_utils + "/bin"
  127. env["CC"] = compiler_path + "/clang"
  128. env["CXX"] = compiler_path + "/clang++"
  129. env["AR"] = compiler_path + "/llvm-ar"
  130. env["RANLIB"] = compiler_path + "/llvm-ranlib"
  131. env["AS"] = bin_utils_path + "/as"
  132. # Disable exceptions and rtti on non-tools (template) builds
  133. if env["tools"]:
  134. env.Append(CXXFLAGS=["-frtti"])
  135. else:
  136. env.Append(CXXFLAGS=["-fno-rtti", "-fno-exceptions"])
  137. # Don't use dynamic_cast, necessary with no-rtti.
  138. env.Append(CPPDEFINES=["NO_SAFE_CAST"])
  139. env.Append(
  140. CCFLAGS="-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing".split()
  141. )
  142. env.Append(CPPDEFINES=["NO_STATVFS", "GLES_ENABLED"])
  143. if get_min_sdk_version(env["ndk_platform"]) >= 24:
  144. env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)])
  145. env["neon_enabled"] = False
  146. if env["android_arch"] == "x86":
  147. # The NDK adds this if targeting API < 24, so we can drop it when Godot targets it at least
  148. env.Append(CCFLAGS=["-mstackrealign"])
  149. elif env["android_arch"] == "armv7":
  150. env.Append(CCFLAGS="-march=armv7-a -mfloat-abi=softfp".split())
  151. env.Append(CPPDEFINES=["__ARM_ARCH_7__", "__ARM_ARCH_7A__"])
  152. if env["android_neon"]:
  153. env["neon_enabled"] = True
  154. env.Append(CPPDEFINES=["__ARM_NEON__"])
  155. else:
  156. env.Append(CCFLAGS=["-mfpu=vfpv3-d16"])
  157. elif env["android_arch"] == "arm64v8":
  158. env.Append(CCFLAGS=["-mfix-cortex-a53-835769"])
  159. env.Append(CPPDEFINES=["__ARM_ARCH_8A__"])
  160. # Link flags
  161. env.Append(LINKFLAGS="-Wl,--gc-sections -Wl,--no-undefined -Wl,-z,now".split())
  162. env.Append(LINKFLAGS="-Wl,-soname,libgodot_android.so")
  163. env.Prepend(CPPPATH=["#platform/android"])
  164. env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED", "NO_FCNTL"])
  165. env.Append(LIBS=["OpenSLES", "EGL", "GLESv3", "GLESv2", "android", "log", "z", "dl"])