draco_emscripten.cmake 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. # Copyright 2021 The Draco Authors
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may not
  4. # use this file except in compliance with the License. You may obtain a copy of
  5. # the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. # License for the specific language governing permissions and limitations under
  13. # the License.
  14. if(DRACO_CMAKE_DRACO_EMSCRIPTEN_CMAKE_)
  15. return()
  16. endif() # DRACO_CMAKE_DRACO_EMSCRIPTEN_CMAKE_
  17. # Checks environment for Emscripten prerequisites.
  18. macro(draco_check_emscripten_environment)
  19. if(NOT PYTHONINTERP_FOUND)
  20. message(
  21. FATAL_ERROR
  22. "Python required for Emscripten builds, but cmake cannot find it.")
  23. endif()
  24. if(NOT EXISTS "$ENV{EMSCRIPTEN}")
  25. message(
  26. FATAL_ERROR
  27. "The EMSCRIPTEN environment variable must be set. See README.md.")
  28. endif()
  29. endmacro()
  30. # Obtains the required Emscripten flags for Draco targets.
  31. macro(draco_get_required_emscripten_flags)
  32. set(em_FLAG_LIST_VAR_COMPILER)
  33. set(em_FLAG_LIST_VAR_LINKER)
  34. set(em_flags)
  35. set(em_single_arg_opts FLAG_LIST_VAR_COMPILER FLAG_LIST_VAR_LINKER)
  36. set(em_multi_arg_opts)
  37. cmake_parse_arguments(em "${em_flags}" "${em_single_arg_opts}"
  38. "${em_multi_arg_opts}" ${ARGN})
  39. if(NOT em_FLAG_LIST_VAR_COMPILER)
  40. message(
  41. FATAL
  42. "draco_get_required_emscripten_flags: FLAG_LIST_VAR_COMPILER required")
  43. endif()
  44. if(NOT em_FLAG_LIST_VAR_LINKER)
  45. message(
  46. FATAL
  47. "draco_get_required_emscripten_flags: FLAG_LIST_VAR_LINKER required")
  48. endif()
  49. if(DRACO_JS_GLUE)
  50. unset(required_flags)
  51. # TODO(tomfinegan): Revisit splitting of compile/link flags for Emscripten,
  52. # and drop -Wno-unused-command-line-argument. Emscripten complains about
  53. # what are supposedly link-only flags sent with compile commands, but then
  54. # proceeds to produce broken code if the warnings are heeded.
  55. list(APPEND ${em_FLAG_LIST_VAR_COMPILER}
  56. "-Wno-unused-command-line-argument")
  57. list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-Wno-almost-asm")
  58. list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "--memory-init-file" "0")
  59. list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-fno-omit-frame-pointer")
  60. # According to Emscripten the following flags are linker only, but sending
  61. # these flags (en masse) to only the linker results in a broken Emscripten
  62. # build with an empty DracoDecoderModule.
  63. list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sALLOW_MEMORY_GROWTH=1")
  64. list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sMODULARIZE=1")
  65. list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sFILESYSTEM=0")
  66. list(APPEND ${em_FLAG_LIST_VAR_COMPILER}
  67. "-sEXPORTED_FUNCTIONS=[\"_free\",\"_malloc\"]")
  68. list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sPRECISE_F32=1")
  69. list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sNODEJS_CATCH_EXIT=0")
  70. list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sNODEJS_CATCH_REJECTION=0")
  71. if(DRACO_FAST)
  72. list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "--llvm-lto" "1")
  73. endif()
  74. # The WASM flag is reported as linker only.
  75. if(DRACO_WASM)
  76. list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sWASM=1")
  77. else()
  78. list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sWASM=0")
  79. endif()
  80. # The LEGACY_VM_SUPPORT flag is reported as linker only.
  81. if(DRACO_IE_COMPATIBLE)
  82. list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sLEGACY_VM_SUPPORT=1")
  83. endif()
  84. endif()
  85. endmacro()
  86. # Macro for generating C++ glue code from IDL for Emscripten targets. Executes
  87. # python to generate the C++ binding, and establishes dendency: $OUTPUT_PATH.cpp
  88. # on $INPUT_IDL.
  89. macro(draco_generate_emscripten_glue)
  90. set(glue_flags)
  91. set(glue_single_arg_opts INPUT_IDL OUTPUT_PATH)
  92. set(glue_multi_arg_opts)
  93. cmake_parse_arguments(glue "${glue_flags}" "${glue_single_arg_opts}"
  94. "${glue_multi_arg_opts}" ${ARGN})
  95. if(DRACO_VERBOSE GREATER 1)
  96. message(
  97. "--------- draco_generate_emscripten_glue -----------\n"
  98. "glue_INPUT_IDL=${glue_INPUT_IDL}\n"
  99. "glue_OUTPUT_PATH=${glue_OUTPUT_PATH}\n"
  100. "----------------------------------------------------\n")
  101. endif()
  102. if(NOT glue_INPUT_IDL OR NOT glue_OUTPUT_PATH)
  103. message(
  104. FATAL_ERROR
  105. "draco_generate_emscripten_glue: INPUT_IDL and OUTPUT_PATH required.")
  106. endif()
  107. # Generate the glue source.
  108. execute_process(
  109. COMMAND ${PYTHON_EXECUTABLE} $ENV{EMSCRIPTEN}/tools/webidl_binder.py
  110. ${glue_INPUT_IDL} ${glue_OUTPUT_PATH})
  111. if(NOT EXISTS "${glue_OUTPUT_PATH}.cpp")
  112. message(FATAL_ERROR "JS glue generation failed for ${glue_INPUT_IDL}.")
  113. endif()
  114. # Create a dependency so that it regenerated on edits.
  115. add_custom_command(
  116. OUTPUT "${glue_OUTPUT_PATH}.cpp"
  117. COMMAND ${PYTHON_EXECUTABLE} $ENV{EMSCRIPTEN}/tools/webidl_binder.py
  118. ${glue_INPUT_IDL} ${glue_OUTPUT_PATH}
  119. DEPENDS ${draco_js_dec_idl}
  120. COMMENT "Generating ${glue_OUTPUT_PATH}.cpp."
  121. WORKING_DIRECTORY ${draco_build}
  122. VERBATIM)
  123. endmacro()
  124. # Wrapper for draco_add_executable() that handles the extra work necessary for
  125. # emscripten targets when generating JS glue:
  126. #
  127. # ~~~
  128. # - Set source level dependency on the C++ binding.
  129. # - Pre/Post link emscripten magic.
  130. #
  131. # Required args:
  132. # - GLUE_PATH: Base path for glue file. Used to generate .cpp and .js files.
  133. # - PRE_LINK_JS_SOURCES: em_link_pre_js() source files.
  134. # - POST_LINK_JS_SOURCES: em_link_post_js() source files.
  135. # Optional args:
  136. # - FEATURES:
  137. # ~~~
  138. macro(draco_add_emscripten_executable)
  139. unset(emexe_NAME)
  140. unset(emexe_FEATURES)
  141. unset(emexe_SOURCES)
  142. unset(emexe_DEFINES)
  143. unset(emexe_INCLUDES)
  144. unset(emexe_LINK_FLAGS)
  145. set(optional_args)
  146. set(single_value_args NAME GLUE_PATH)
  147. set(multi_value_args
  148. SOURCES
  149. DEFINES
  150. FEATURES
  151. INCLUDES
  152. LINK_FLAGS
  153. PRE_LINK_JS_SOURCES
  154. POST_LINK_JS_SOURCES)
  155. cmake_parse_arguments(emexe "${optional_args}" "${single_value_args}"
  156. "${multi_value_args}" ${ARGN})
  157. if(NOT
  158. (emexe_GLUE_PATH
  159. AND emexe_POST_LINK_JS_SOURCES
  160. AND emexe_PRE_LINK_JS_SOURCES))
  161. message(FATAL
  162. "draco_add_emscripten_executable: GLUE_PATH PRE_LINK_JS_SOURCES "
  163. "POST_LINK_JS_SOURCES args required.")
  164. endif()
  165. if(DRACO_VERBOSE GREATER 1)
  166. message(
  167. "--------- draco_add_emscripten_executable ---------\n"
  168. "emexe_NAME=${emexe_NAME}\n"
  169. "emexe_SOURCES=${emexe_SOURCES}\n"
  170. "emexe_DEFINES=${emexe_DEFINES}\n"
  171. "emexe_INCLUDES=${emexe_INCLUDES}\n"
  172. "emexe_LINK_FLAGS=${emexe_LINK_FLAGS}\n"
  173. "emexe_GLUE_PATH=${emexe_GLUE_PATH}\n"
  174. "emexe_FEATURES=${emexe_FEATURES}\n"
  175. "emexe_PRE_LINK_JS_SOURCES=${emexe_PRE_LINK_JS_SOURCES}\n"
  176. "emexe_POST_LINK_JS_SOURCES=${emexe_POST_LINK_JS_SOURCES}\n"
  177. "----------------------------------------------------\n")
  178. endif()
  179. # The Emscripten linker needs the C++ flags in addition to whatever has been
  180. # passed in with the target.
  181. list(APPEND emexe_LINK_FLAGS ${DRACO_CXX_FLAGS})
  182. if(DRACO_GLTF_BITSTREAM)
  183. # Add "_gltf" suffix to target output name.
  184. draco_add_executable(
  185. NAME ${emexe_NAME}
  186. OUTPUT_NAME ${emexe_NAME}_gltf
  187. SOURCES ${emexe_SOURCES}
  188. DEFINES ${emexe_DEFINES}
  189. INCLUDES ${emexe_INCLUDES}
  190. LINK_FLAGS ${emexe_LINK_FLAGS})
  191. else()
  192. draco_add_executable(
  193. NAME ${emexe_NAME}
  194. SOURCES ${emexe_SOURCES}
  195. DEFINES ${emexe_DEFINES}
  196. INCLUDES ${emexe_INCLUDES}
  197. LINK_FLAGS ${emexe_LINK_FLAGS})
  198. endif()
  199. foreach(feature ${emexe_FEATURES})
  200. draco_enable_feature(FEATURE ${feature} TARGETS ${emexe_NAME})
  201. endforeach()
  202. set_property(
  203. SOURCE ${emexe_SOURCES}
  204. APPEND
  205. PROPERTY OBJECT_DEPENDS "${emexe_GLUE_PATH}.cpp")
  206. em_link_pre_js(${emexe_NAME} ${emexe_PRE_LINK_JS_SOURCES})
  207. em_link_post_js(${emexe_NAME} "${emexe_GLUE_PATH}.js"
  208. ${emexe_POST_LINK_JS_SOURCES})
  209. endmacro()