SdlAndroidFunctions.cmake 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. #[=======================================================================[
  2. This CMake script contains functions to build an Android APK.
  3. It is (currently) limited to packaging binaries for a single architecture.
  4. #]=======================================================================]
  5. cmake_minimum_required(VERSION 3.7)
  6. if(NOT PROJECT_NAME MATCHES "^SDL.*")
  7. message(WARNING "This module is internal to SDL and is currently not supported.")
  8. endif()
  9. function(_sdl_create_outdir_for_target OUTDIRECTORY TARGET)
  10. set(outdir "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TARGET}.dir")
  11. # Some CMake versions have a slow `cmake -E make_directory` implementation
  12. if(NOT IS_DIRECTORY "${outdir}")
  13. execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${outdir}")
  14. endif()
  15. set("${OUTDIRECTORY}" "${outdir}" PARENT_SCOPE)
  16. endfunction()
  17. function(sdl_create_android_debug_keystore TARGET)
  18. set(output "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_debug.keystore")
  19. add_custom_command(OUTPUT ${output}
  20. COMMAND ${CMAKE_COMMAND} -E rm -f "${output}"
  21. 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"
  22. )
  23. add_custom_target(${TARGET} DEPENDS "${output}")
  24. set_property(TARGET ${TARGET} PROPERTY OUTPUT "${output}")
  25. endfunction()
  26. function(sdl_android_compile_resources TARGET)
  27. cmake_parse_arguments(arg "" "RESFOLDER" "RESOURCES" ${ARGN})
  28. if(NOT arg_RESFOLDER AND NOT arg_RESOURCES)
  29. message(FATAL_ERROR "Missing RESFOLDER or RESOURCES argument (need one or both)")
  30. endif()
  31. _sdl_create_outdir_for_target(outdir "${TARGET}")
  32. set(out_files "")
  33. set(res_files "")
  34. if(arg_RESFOLDER)
  35. get_filename_component(arg_RESFOLDER "${arg_RESFOLDER}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
  36. file(GLOB_RECURSE res_folder_files "${arg_RESFOLDER}/*")
  37. list(APPEND res_files ${res_folder_files})
  38. foreach(res_file IN LISTS res_files)
  39. file(RELATIVE_PATH rel_res_file "${arg_RESFOLDER}" "${res_file}")
  40. string(REPLACE "/" "_" rel_comp_path "${rel_res_file}")
  41. if(res_file MATCHES ".*res/values.*\\.xml$")
  42. string(REGEX REPLACE "\\.xml" ".arsc" rel_comp_path "${rel_comp_path}")
  43. endif()
  44. set(comp_path "${outdir}/${rel_comp_path}.flat")
  45. add_custom_command(
  46. OUTPUT "${comp_path}"
  47. COMMAND SdlAndroid::aapt2 compile -o "${outdir}" "${res_file}"
  48. DEPENDS ${res_file}
  49. )
  50. list(APPEND out_files "${comp_path}")
  51. endforeach()
  52. endif()
  53. if(arg_RESOURCES)
  54. list(APPEND res_files ${arg_RESOURCES})
  55. foreach(res_file IN LISTS arg_RESOURCES)
  56. string(REGEX REPLACE ".*/res/" "" rel_res_file ${res_file})
  57. string(REPLACE "/" "_" rel_comp_path "${rel_res_file}")
  58. if(res_file MATCHES ".*res/values.*\\.xml$")
  59. string(REGEX REPLACE "\\.xml" ".arsc" rel_comp_path "${rel_comp_path}")
  60. endif()
  61. set(comp_path "${outdir}/${rel_comp_path}.flat")
  62. add_custom_command(
  63. OUTPUT "${comp_path}"
  64. COMMAND SdlAndroid::aapt2 compile -o "${outdir}" "${res_file}"
  65. DEPENDS ${res_file}
  66. )
  67. list(APPEND out_files "${comp_path}")
  68. endforeach()
  69. endif()
  70. add_custom_target(${TARGET} DEPENDS ${out_files})
  71. set_property(TARGET "${TARGET}" PROPERTY OUTPUTS "${out_files}")
  72. set_property(TARGET "${TARGET}" PROPERTY SOURCES "${res_files}")
  73. endfunction()
  74. function(sdl_android_link_resources TARGET)
  75. cmake_parse_arguments(arg "NO_DEBUG" "MIN_SDK_VERSION;TARGET_SDK_VERSION;ANDROID_JAR;OUTPUT_APK;MANIFEST;PACKAGE" "RES_TARGETS" ${ARGN})
  76. if(arg_MANIFEST)
  77. get_filename_component(arg_MANIFEST "${arg_MANIFEST}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
  78. else()
  79. message(FATAL_ERROR "sdl_add_android_link_resources_target requires a Android MANIFEST path (${arg_MANIFEST})")
  80. endif()
  81. if(NOT arg_PACKAGE)
  82. file(READ "${arg_MANIFEST}" manifest_contents)
  83. string(REGEX MATCH "package=\"([a-zA-Z0-9_.]+)\"" package_match "${manifest_contents}")
  84. if(NOT package_match)
  85. message(FATAL_ERROR "Could not extract package from Android manifest (${arg_MANIFEST})")
  86. endif()
  87. set(arg_PACKAGE "${CMAKE_MATCH_1}")
  88. endif()
  89. set(depends "")
  90. _sdl_create_outdir_for_target(outdir "${TARGET}")
  91. string(REPLACE "." "/" java_r_path "${arg_PACKAGE}")
  92. get_filename_component(java_r_path "${java_r_path}" ABSOLUTE BASE_DIR "${outdir}")
  93. set(java_r_path "${java_r_path}/R.java")
  94. set(command SdlAndroid::aapt2 link)
  95. if(NOT arg_NO_DEBUG)
  96. list(APPEND command --debug-mode)
  97. endif()
  98. if(arg_MIN_SDK_VERSION)
  99. list(APPEND command --min-sdk-version ${arg_MIN_SDK_VERSION})
  100. endif()
  101. if(arg_TARGET_SDK_VERSION)
  102. list(APPEND command --target-sdk-version ${arg_TARGET_SDK_VERSION})
  103. endif()
  104. if(arg_ANDROID_JAR)
  105. list(APPEND command -I "${arg_ANDROID_JAR}")
  106. else()
  107. list(APPEND command -I "${SDL_ANDROID_PLATFORM_ANDROID_JAR}")
  108. endif()
  109. if(NOT arg_OUTPUT_APK)
  110. set(arg_OUTPUT_APK "${TARGET}.apk")
  111. endif()
  112. get_filename_component(arg_OUTPUT_APK "${arg_OUTPUT_APK}" ABSOLUTE BASE_DIR "${outdir}")
  113. list(APPEND command -o "${arg_OUTPUT_APK}")
  114. list(APPEND command --java "${outdir}")
  115. list(APPEND command --manifest "${arg_MANIFEST}")
  116. foreach(res_target IN LISTS arg_RES_TARGETS)
  117. list(APPEND command $<TARGET_PROPERTY:${res_target},OUTPUTS>)
  118. list(APPEND depends $<TARGET_PROPERTY:${res_target},OUTPUTS>)
  119. endforeach()
  120. add_custom_command(
  121. OUTPUT "${arg_OUTPUT_APK}" "${java_r_path}"
  122. COMMAND ${command}
  123. DEPENDS ${depends} ${arg_MANIFEST}
  124. COMMAND_EXPAND_LISTS
  125. VERBATIM
  126. )
  127. add_custom_target(${TARGET} DEPENDS "${arg_OUTPUT_APK}" "${java_r_path}")
  128. set_property(TARGET ${TARGET} PROPERTY OUTPUT "${arg_OUTPUT_APK}")
  129. set_property(TARGET ${TARGET} PROPERTY JAVA_R "${java_r_path}")
  130. set_property(TARGET ${TARGET} PROPERTY OUTPUTS "${${arg_OUTPUT_APK}};${java_r_path}")
  131. endfunction()
  132. function(sdl_add_to_apk_unaligned TARGET)
  133. cmake_parse_arguments(arg "" "APK_IN;NAME;OUTDIR" "ASSETS;NATIVE_LIBS;DEX" ${ARGN})
  134. if(NOT arg_APK_IN)
  135. message(FATAL_ERROR "Missing APK_IN argument")
  136. endif()
  137. if(NOT TARGET ${arg_APK_IN})
  138. message(FATAL_ERROR "APK_IN (${arg_APK_IN}) must be a target providing an apk")
  139. endif()
  140. _sdl_create_outdir_for_target(workdir ${TARGET})
  141. if(NOT arg_OUTDIR)
  142. set(arg_OUTDIR "${CMAKE_CURRENT_BINARY_DIR}")
  143. endif()
  144. if(NOT arg_NAME)
  145. string(REGEX REPLACE "[:-]+" "." arg_NAME "${TARGET}")
  146. if(NOT arg_NAME MATCHES "\\.apk")
  147. set(arg_NAME "${arg_NAME}.apk")
  148. endif()
  149. endif()
  150. get_filename_component(apk_file "${arg_NAME}" ABSOLUTE BASE_DIR "${arg_OUTDIR}")
  151. set(apk_libdir "lib/${ANDROID_ABI}")
  152. set(depends "")
  153. set(commands
  154. COMMAND "${CMAKE_COMMAND}" -E remove_directory -rf "${apk_libdir}" "assets"
  155. COMMAND "${CMAKE_COMMAND}" -E make_directory "${apk_libdir}" "assets"
  156. COMMAND "${CMAKE_COMMAND}" -E copy "$<TARGET_PROPERTY:${arg_APK_IN},OUTPUT>" "${apk_file}"
  157. )
  158. set(dex_i "1")
  159. foreach(dex IN LISTS arg_DEX)
  160. set(suffix "${dex_i}")
  161. if(suffix STREQUAL "1")
  162. set(suffix "")
  163. endif()
  164. list(APPEND commands
  165. COMMAND "${CMAKE_COMMAND}" -E copy "$<TARGET_PROPERTY:${dex},OUTPUT>" "classes${suffix}.dex"
  166. COMMAND SdlAndroid::zip -u -q -j "${apk_file}" "classes${suffix}.dex"
  167. )
  168. math(EXPR dex_i "${dex_i}+1")
  169. list(APPEND depends "$<TARGET_PROPERTY:${dex},OUTPUT>")
  170. endforeach()
  171. foreach(native_lib IN LISTS arg_NATIVE_LIBS)
  172. list(APPEND commands
  173. COMMAND "${CMAKE_COMMAND}" -E copy $<TARGET_FILE:${native_lib}> "${apk_libdir}/$<TARGET_FILE_NAME:${native_lib}>"
  174. COMMAND SdlAndroid::zip -u -q "${apk_file}" "${apk_libdir}/$<TARGET_FILE_NAME:${native_lib}>"
  175. )
  176. endforeach()
  177. if(arg_ASSETS)
  178. list(APPEND commands
  179. COMMAND "${CMAKE_COMMAND}" -E copy ${arg_ASSETS} "assets"
  180. COMMAND SdlAndroid::zip -u -r -q "${apk_file}" "assets"
  181. )
  182. endif()
  183. add_custom_command(OUTPUT "${apk_file}"
  184. ${commands}
  185. DEPENDS ${arg_NATIVE_LIBS} ${depends} "$<TARGET_PROPERTY:${arg_APK_IN},OUTPUT>"
  186. WORKING_DIRECTORY "${workdir}"
  187. )
  188. add_custom_target(${TARGET} DEPENDS "${apk_file}")
  189. set_property(TARGET ${TARGET} PROPERTY OUTPUT "${apk_file}")
  190. endfunction()
  191. function(sdl_apk_align TARGET APK_IN)
  192. cmake_parse_arguments(arg "" "NAME;OUTDIR" "" ${ARGN})
  193. if(NOT TARGET ${arg_APK_IN})
  194. message(FATAL_ERROR "APK_IN (${arg_APK_IN}) must be a target providing an apk")
  195. endif()
  196. if(NOT arg_OUTDIR)
  197. set(arg_OUTDIR "${CMAKE_CURRENT_BINARY_DIR}")
  198. endif()
  199. if(NOT arg_NAME)
  200. string(REGEX REPLACE "[:-]+" "." arg_NAME "${TARGET}")
  201. if(NOT arg_NAME MATCHES "\\.apk")
  202. set(arg_NAME "${arg_NAME}.apk")
  203. endif()
  204. endif()
  205. get_filename_component(apk_file "${arg_NAME}" ABSOLUTE BASE_DIR "${arg_OUTDIR}")
  206. add_custom_command(OUTPUT "${apk_file}"
  207. COMMAND SdlAndroid::zipalign -f 4 "$<TARGET_PROPERTY:${APK_IN},OUTPUT>" "${apk_file}"
  208. DEPENDS "$<TARGET_PROPERTY:${APK_IN},OUTPUT>"
  209. )
  210. add_custom_target(${TARGET} DEPENDS "${apk_file}")
  211. set_property(TARGET ${TARGET} PROPERTY OUTPUT "${apk_file}")
  212. endfunction()
  213. function(sdl_apk_sign TARGET APK_IN)
  214. cmake_parse_arguments(arg "" "OUTPUT;KEYSTORE" "" ${ARGN})
  215. if(NOT TARGET ${arg_APK_IN})
  216. message(FATAL_ERROR "APK_IN (${arg_APK_IN}) must be a target providing an apk")
  217. endif()
  218. if(NOT TARGET ${arg_KEYSTORE})
  219. message(FATAL_ERROR "APK_KEYSTORE (${APK_KEYSTORE}) must be a target providing a keystore")
  220. endif()
  221. if(NOT arg_OUTPUT)
  222. string(REGEX REPLACE "[:-]+" "." arg_OUTPUT "${TARGET}")
  223. if(NOT arg_OUTPUT MATCHES "\\.apk")
  224. set(arg_OUTPUT "${arg_OUTPUT}.apk")
  225. endif()
  226. endif()
  227. get_filename_component(apk_file "${arg_OUTPUT}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
  228. add_custom_command(OUTPUT "${apk_file}"
  229. COMMAND SdlAndroid::apksigner sign
  230. --ks "$<TARGET_PROPERTY:${arg_KEYSTORE},OUTPUT>"
  231. --ks-pass pass:android --in "$<TARGET_PROPERTY:${APK_IN},OUTPUT>" --out "${apk_file}"
  232. DEPENDS "$<TARGET_PROPERTY:${APK_IN},OUTPUT>" "$<TARGET_PROPERTY:${arg_KEYSTORE},OUTPUT>"
  233. BYPRODUCTS "${apk_file}.idsig"
  234. )
  235. add_custom_target(${TARGET} DEPENDS "${apk_file}")
  236. set_property(TARGET ${TARGET} PROPERTY OUTPUT "${apk_file}")
  237. endfunction()