LYWrappers.cmake 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833
  1. #
  2. # Copyright (c) Contributors to the Open 3D Engine Project.
  3. # For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. #
  5. # SPDX-License-Identifier: Apache-2.0 OR MIT
  6. #
  7. #
  8. set(LY_UNITY_BUILD ON CACHE BOOL "UNITY builds")
  9. include(CMakeFindDependencyMacro)
  10. include(cmake/LyAutoGen.cmake)
  11. o3de_pal_dir(pal_dir ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Platform/${PAL_PLATFORM_NAME} "${O3DE_ENGINE_RESTRICTED_PATH}" "${LY_ROOT_FOLDER}")
  12. include(${pal_dir}/LYWrappers_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
  13. # Not all platforms support unity builds
  14. if(LY_UNITY_BUILD AND NOT PAL_TRAIT_BUILD_UNITY_SUPPORTED)
  15. message(ERROR "LY_UNITY_BUILD is specified, but not supported for the current target platform")
  16. endif()
  17. define_property(TARGET PROPERTY GEM_MODULE
  18. BRIEF_DOCS "Defines a MODULE library as a Gem"
  19. FULL_DOCS [[
  20. Property which is set on targets that should be seen as gems
  21. This is used to determine whether this target should load as
  22. when used as a runtime dependency should load at the same time
  23. as its dependee
  24. ]]
  25. )
  26. define_property(TARGET PROPERTY RUNTIME_DEPENDENCIES_DEPENDS
  27. BRIEF_DOCS "Defines the dependencies the runtime dependencies of a target has"
  28. FULL_DOCS [[
  29. Property which is queried through generator expressions at the moment
  30. the target is declared so a custom command that will do the copies can
  31. be generated later. Custom commands need to be declared in the same folder
  32. the target is declared, however, runtime dependencies need all targets
  33. to be declared, so it is done towards the end of CMake parsing. When
  34. runtime dependencies are processed, this target property is filled so the
  35. right dependencies are set for the custom command.
  36. This property contains all the files that are going to be copied to the output
  37. when the target gets built.
  38. ]]
  39. )
  40. #! ly_add_target: adds a target and provides parameters for the common configurations.
  41. #
  42. # Adds a target (static/dynamic library, executable) and convenient wrappers around most
  43. # common parameters that need to be set.
  44. # This function also creates an interface to use for dependencies. The interface will be
  45. # named as "NAMESPACE::NAME"
  46. # Some examples:
  47. # ly_add_target(NAME mystaticlib STATIC FILES_CMAKE somestatic_files.cmake)
  48. # ly_add_target(NAME mydynamiclib SHARED FILES_CMAKE somedyn_files.cmake)
  49. # ly_add_target(NAME myexecutable EXECUTABLE FILES_CMAKE someexe_files.cmake)
  50. #
  51. # \arg:NAME name of the target
  52. # \arg:STATIC (bool) defines this target to be a static library
  53. # \arg:GEM_STATIC (bool) defines this target to be a static library while also setting the GEM_MODULE property
  54. # \arg:SHARED (bool) defines this target to be a dynamic library
  55. # \arg:GEM_SHARED (bool) defines this target to be a dynamic library while also setting the GEM_MODULE property
  56. # \arg:MODULE (bool) defines this target to be a module library
  57. # \arg:GEM_MODULE (bool) defines this target to be a module library while also marking the target as a "Gem" via the GEM_MODULE property
  58. # \arg:OBJECT (bool) defines this target to be an object library
  59. # \arg:INTERFACE (bool) defines this target to be an interface library. A ${NAME}_HEADERS project will be created for the IDE
  60. # The HEADERONLY option can be specified as an alternative
  61. # \arg:EXECUTABLE (bool) defines this target to be an executable
  62. # \arg:APPLICATION (bool) defines this target to be an application (executable that is not a console)
  63. # \arg:IMPORTED (bool) defines this target to be imported.
  64. # \arg:NAMESPACE namespace declaration for this target. It will be used for IDE and dependencies
  65. # \arg:OUTPUT_NAME (optional) overrides the name of the output target. If not specified, the name will be used.
  66. # \arg:OUTPUT_SUBDIRECTORY places the runtime binary in a subfolder within the output folder (this only affects to runtime binaries)
  67. # \arg:AUTOMOC enables Qt moc in the target
  68. # \arg:AUTOUIC enables Qt uic in the target
  69. # \arg:AUTORCC enables Qt rcc in the target
  70. # \arg:NO_UNITY Prevent the target from employing unity builds even when unity builds (LY_UNITY_BUILD) are enabled
  71. # \arg:FILES_CMAKE list of *_files.cmake files that contain files for this target
  72. # \arg:GENERATED_FILES list of files to add to this target that are generated out of some other task
  73. # \arg:INCLUDE_DIRECTORIES list of directories to use as include paths
  74. # \arg:BUILD_DEPENDENCIES list of interfaces this target depends on (could be a compilation dependency
  75. # if the dependency is only exposing an include path, or could be a linking
  76. # dependency is exposing a lib)
  77. # \arg:RUNTIME_DEPENDENCIES list of dependencies this target depends on at runtime
  78. # \arg:COMPILE_DEFINITIONS list of compilation definitions this target will use to compile
  79. # \arg:PLATFORM_INCLUDE_FILES *.cmake files which should contain platform specific configuration to be added to the target
  80. # Look at the documentation for the ly_configure_target_platform_properties() function below
  81. # for the list of variables that will be used by the target
  82. # \arg:TARGET_PROPERTIES additional properties to set to the target
  83. # \arg:AUTOGEN_RULES a set of AutoGeneration rules to be passed to the AzAutoGen expansion system
  84. function(ly_add_target)
  85. set(options STATIC SHARED MODULE GEM_STATIC GEM_MODULE OBJECT HEADERONLY EXECUTABLE APPLICATION IMPORTED AUTOMOC AUTOUIC AUTORCC NO_UNITY)
  86. set(oneValueArgs NAME NAMESPACE OUTPUT_SUBDIRECTORY OUTPUT_NAME)
  87. set(multiValueArgs FILES_CMAKE GENERATED_FILES INCLUDE_DIRECTORIES COMPILE_DEFINITIONS BUILD_DEPENDENCIES RUNTIME_DEPENDENCIES PLATFORM_INCLUDE_FILES TARGET_PROPERTIES AUTOGEN_RULES)
  88. cmake_parse_arguments(ly_add_target "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  89. # Since the term "INTERFACE" used in other context such as INCLUDE_DIRECTORIES, COMPILE_DEFINITIONS to specifiy property visibility
  90. # It needs to be parsed after those arguments have been parsed to avoid the usage of INTERFACE as a visibility scope
  91. cmake_parse_arguments(ly_add_target "INTERFACE" "" "" ${ly_add_target_UNPARSED_ARGUMENTS})
  92. # Validate input arguments
  93. if(NOT ly_add_target_NAME)
  94. message(FATAL_ERROR "You must provide a name for the target")
  95. endif()
  96. # Map HEADERONLY option to INTERFACE
  97. if(ly_add_target_HEADERONLY)
  98. set(ly_add_target_INTERFACE ly_add_target_HEADERONLY)
  99. endif()
  100. if(NOT ly_add_target_IMPORTED AND NOT ly_add_target_INTERFACE)
  101. if(NOT ly_add_target_FILES_CMAKE)
  102. message(FATAL_ERROR "You must provide a list of _files.cmake files for the target")
  103. endif()
  104. endif()
  105. # If the GEM_MODULE tag is passed set the normal MODULE argument
  106. if(ly_add_target_GEM_MODULE)
  107. set(ly_add_target_MODULE ${ly_add_target_GEM_MODULE})
  108. endif()
  109. # If the GEM_STATIC tag is passed mark the target as STATIC
  110. if(ly_add_target_GEM_STATIC)
  111. set(ly_add_target_STATIC ${ly_add_target_GEM_STATIC})
  112. endif()
  113. # If the GEM_SHARED tag is passed mark the target as SHARED
  114. if(ly_add_target_GEM_SHARED)
  115. set(ly_add_target_SHARED ${ly_add_target_GEM_SHARED})
  116. endif()
  117. foreach(file_cmake ${ly_add_target_FILES_CMAKE})
  118. ly_include_cmake_file_list(${file_cmake})
  119. endforeach()
  120. unset(linking_options)
  121. unset(linking_count)
  122. unset(target_type_options)
  123. if(ly_add_target_STATIC)
  124. set(linking_options STATIC)
  125. set(target_type_options STATIC)
  126. set(linking_count "${linking_count}1")
  127. endif()
  128. if(ly_add_target_OBJECT)
  129. set(linking_options OBJECT)
  130. set(target_type_options OBJECT)
  131. set(linking_count "${linking_count}1")
  132. endif()
  133. if(ly_add_target_SHARED)
  134. set(linking_options SHARED)
  135. set(target_type_options SHARED)
  136. set(linking_count "${linking_count}1")
  137. endif()
  138. if(ly_add_target_MODULE)
  139. set(linking_options ${PAL_LINKOPTION_MODULE})
  140. set(target_type_options ${PAL_LINKOPTION_MODULE})
  141. set(linking_count "${linking_count}1")
  142. endif()
  143. if(ly_add_target_INTERFACE)
  144. set(linking_options INTERFACE)
  145. set(target_type_options INTERFACE)
  146. set(linking_count "${linking_count}1")
  147. endif()
  148. if(ly_add_target_EXECUTABLE)
  149. set(linking_options EXECUTABLE)
  150. set(linking_count "${linking_count}1")
  151. endif()
  152. if(ly_add_target_APPLICATION)
  153. set(linking_options APPLICATION)
  154. set(linking_count "${linking_count}1")
  155. endif()
  156. if(NOT ("${linking_count}" STREQUAL "1"))
  157. message(FATAL_ERROR "More than one of the following options [STATIC | SHARED | MODULE | OBJECT | INTERFACE | EXECUTABLE | APPLICATION ] was specified and they are mutually exclusive")
  158. endif()
  159. if(ly_add_target_IMPORTED)
  160. list(APPEND target_type_options IMPORTED GLOBAL)
  161. endif()
  162. if(ly_add_target_NAMESPACE)
  163. set(interface_name "${ly_add_target_NAMESPACE}::${ly_add_target_NAME}")
  164. else()
  165. set(interface_name "${ly_add_target_NAME}")
  166. endif()
  167. set(project_NAME ${ly_add_target_NAME})
  168. if(ly_add_target_EXECUTABLE)
  169. add_executable(${ly_add_target_NAME}
  170. ${target_type_options}
  171. ${ALLFILES} ${ly_add_target_GENERATED_FILES}
  172. )
  173. ly_apply_platform_properties(${ly_add_target_NAME})
  174. if(ly_add_target_IMPORTED)
  175. set_target_properties(${ly_add_target_NAME} PROPERTIES LINKER_LANGUAGE CXX)
  176. endif()
  177. elseif(ly_add_target_APPLICATION)
  178. add_executable(${ly_add_target_NAME}
  179. ${target_type_options}
  180. ${PAL_EXECUTABLE_APPLICATION_FLAG}
  181. ${ALLFILES} ${ly_add_target_GENERATED_FILES}
  182. )
  183. ly_apply_platform_properties(${ly_add_target_NAME})
  184. if(ly_add_target_IMPORTED)
  185. set_target_properties(${ly_add_target_NAME} PROPERTIES LINKER_LANGUAGE CXX)
  186. endif()
  187. elseif(ly_add_target_INTERFACE)
  188. add_library(${ly_add_target_NAME}
  189. ${target_type_options}
  190. ${ALLFILES} ${ly_add_target_GENERATED_FILES}
  191. )
  192. else()
  193. add_library(${ly_add_target_NAME}
  194. ${target_type_options}
  195. ${ALLFILES} ${ly_add_target_GENERATED_FILES}
  196. )
  197. ly_apply_platform_properties(${ly_add_target_NAME})
  198. endif()
  199. if(${ly_add_target_GENERATED_FILES})
  200. set_source_files_properties(${ly_add_target_GENERATED_FILES}
  201. PROPERTIES GENERATED TRUE
  202. )
  203. endif()
  204. if(${ly_add_target_EXECUTABLE} OR ${ly_add_target_APPLICATION})
  205. add_executable(${interface_name} ALIAS ${ly_add_target_NAME})
  206. else()
  207. add_library(${interface_name} ALIAS ${ly_add_target_NAME})
  208. endif()
  209. if(ly_add_target_OUTPUT_NAME)
  210. set_target_properties(${ly_add_target_NAME} PROPERTIES
  211. OUTPUT_NAME ${ly_add_target_OUTPUT_NAME}
  212. )
  213. endif()
  214. # Add the target dependencies so they can be walked later
  215. if(ly_add_target_BUILD_DEPENDENCIES)
  216. set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGETS_${ly_add_target_NAME}_BUILD_DEPENDENCIES ${ly_add_target_BUILD_DEPENDENCIES})
  217. endif()
  218. if(ly_add_target_RUNTIME_DEPENDENCIES)
  219. set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGETS_${ly_add_target_NAME}_RUNTIME_DEPENDENCIES ${ly_add_target_RUNTIME_DEPENDENCIES})
  220. endif()
  221. if (ly_add_target_SHARED OR ly_add_target_MODULE OR ly_add_target_EXECUTABLE OR ly_add_target_APPLICATION)
  222. if (ly_add_target_OUTPUT_SUBDIRECTORY)
  223. ly_handle_custom_output_directory(${ly_add_target_NAME} ${ly_add_target_OUTPUT_SUBDIRECTORY})
  224. else()
  225. ly_handle_custom_output_directory(${ly_add_target_NAME} "")
  226. endif()
  227. endif()
  228. if(ly_add_target_GEM_MODULE OR ly_add_target_GEM_STATIC OR ly_add_target_GEM_SHARED)
  229. set_target_properties(${ly_add_target_NAME} PROPERTIES GEM_MODULE TRUE)
  230. endif()
  231. if (ly_add_target_INCLUDE_DIRECTORIES)
  232. target_include_directories(${ly_add_target_NAME}
  233. ${ly_add_target_INCLUDE_DIRECTORIES}
  234. )
  235. endif()
  236. ly_apply_debug_strip_options(${ly_add_target_NAME})
  237. # Parse the 3rdParty library dependencies
  238. ly_parse_third_party_dependencies("${ly_add_target_BUILD_DEPENDENCIES}")
  239. ly_target_link_libraries(${ly_add_target_NAME}
  240. ${ly_add_target_BUILD_DEPENDENCIES}
  241. )
  242. if(ly_add_target_COMPILE_DEFINITIONS)
  243. target_compile_definitions(${ly_add_target_NAME}
  244. ${ly_add_target_COMPILE_DEFINITIONS}
  245. )
  246. endif()
  247. # For any target that depends on AzTest and is built as an executable, an additional 'AZ_TEST_EXECUTABLE' define will
  248. # enable the 'AZ_UNIT_TEST_HOOK' macro to also implement main() so that running the executable directly will run
  249. # the AZ_UNIT_TEST_HOOK function
  250. if (${linking_options} STREQUAL "EXECUTABLE" AND "AZ::AzTest" IN_LIST ly_add_target_BUILD_DEPENDENCIES)
  251. target_compile_definitions(${ly_add_target_NAME}
  252. PRIVATE
  253. AZ_TEST_EXECUTABLE
  254. )
  255. endif()
  256. if(ly_add_target_TARGET_PROPERTIES)
  257. set_target_properties(${ly_add_target_NAME} PROPERTIES
  258. ${ly_add_target_TARGET_PROPERTIES})
  259. endif()
  260. set(unity_target_types SHARED MODULE EXECUTABLE APPLICATION STATIC OBJECT)
  261. if(linking_options IN_LIST unity_target_types)
  262. # For eligible target types, if unity builds (LY_UNITY_BUILD) is enabled and the target is not marked with 'NO_UNITY',
  263. # enable UNITY builds individually for the target
  264. if(LY_UNITY_BUILD AND NOT ${ly_add_target_NO_UNITY})
  265. set_target_properties(${ly_add_target_NAME} PROPERTIES
  266. UNITY_BUILD ON
  267. UNITY_BUILD_MODE BATCH
  268. )
  269. endif()
  270. endif()
  271. # IDE organization
  272. ly_source_groups_from_folders("${ALLFILES}")
  273. source_group("Generated Files" REGULAR_EXPRESSION "(${CMAKE_BINARY_DIR})") # Any file coming from the output folder
  274. ly_get_vs_folder_directory(${CMAKE_CURRENT_SOURCE_DIR} ide_path)
  275. set_property(TARGET ${project_NAME} PROPERTY FOLDER ${ide_path})
  276. if(ly_add_target_RUNTIME_DEPENDENCIES)
  277. ly_parse_third_party_dependencies("${ly_add_target_RUNTIME_DEPENDENCIES}")
  278. ly_add_dependencies(${ly_add_target_NAME} ${ly_add_target_RUNTIME_DEPENDENCIES})
  279. endif()
  280. ly_configure_target_platform_properties()
  281. # Handle Qt MOC, RCC, UIC
  282. # https://gitlab.kitware.com/cmake/cmake/issues/18749
  283. # AUTOMOC is supposed to always rebuild because it checks files that are not listed in the sources (like extra
  284. # "_p.h" headers) and which may change outside the visibility of the generator.
  285. # We are not using AUTOUIC because of:
  286. # https://gitlab.kitware.com/cmake/cmake/-/issues/18741
  287. # To overcome this problem, we manually wrap all the ui files listed in the target with qt5_wrap_ui
  288. foreach(prop IN ITEMS AUTOMOC AUTORCC)
  289. if(${ly_add_target_${prop}})
  290. set_property(TARGET ${ly_add_target_NAME} PROPERTY ${prop} ON)
  291. endif()
  292. endforeach()
  293. if(${ly_add_target_AUTOUIC})
  294. get_target_property(all_ui_sources ${ly_add_target_NAME} SOURCES)
  295. list(FILTER all_ui_sources INCLUDE REGEX "^.*\\.ui$")
  296. if(NOT all_ui_sources)
  297. message(FATAL_ERROR "Target ${ly_add_target_NAME} contains AUTOUIC but doesnt have any .ui file")
  298. endif()
  299. ly_qt_uic_target(${ly_add_target_NAME})
  300. endif()
  301. # Add dependencies that were added before this target was available
  302. get_property(additional_dependencies GLOBAL PROPERTY LY_DELAYED_DEPENDENCIES_${ly_add_target_NAME})
  303. if(additional_dependencies)
  304. ly_add_dependencies(${ly_add_target_NAME} ${additional_dependencies})
  305. # Clear the variable so we can track issues in case some dependency is added after
  306. set_property(GLOBAL PROPERTY LY_DELAYED_DEPENDENCIES_${ly_add_target_NAME})
  307. endif()
  308. # Store the target so we can walk through all of them in LocationDependencies.cmake
  309. set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGETS ${interface_name})
  310. if(NOT ly_add_target_IMPORTED)
  311. # Store the aliased target into a DIRECTORY property
  312. set_property(DIRECTORY APPEND PROPERTY LY_DIRECTORY_TARGETS ${interface_name})
  313. # Store the directory path in a GLOBAL property so that it can be accessed
  314. # in the layout install logic. Skip if the directory has already been added
  315. get_property(ly_all_target_directories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES)
  316. if(NOT CMAKE_CURRENT_SOURCE_DIR IN_LIST ly_all_target_directories)
  317. set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGET_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR})
  318. endif()
  319. endif()
  320. # Custom commands need to be declared in the same folder as the target that they use.
  321. # Not all the targets will require runtime dependencies, but we will generate at least an
  322. # empty file for them.
  323. set(runtime_dependencies_list SHARED MODULE EXECUTABLE APPLICATION)
  324. if(NOT ly_add_target_IMPORTED AND linking_options IN_LIST runtime_dependencies_list)
  325. get_property(is_multi_config_generator GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
  326. # XCode generator doesnt support different source files per configuration, so we cannot have
  327. # the runtime dependencies using file-tracking, instead, we will have them as a post build step
  328. # Non-multi config generators like Ninja (not "Ninja Multi-Config"), Makefiles, etc have trouble to
  329. # produce file-level dependencies per configuration, so we also default to use a post build step
  330. if(NOT is_multi_config_generator OR CMAKE_GENERATOR MATCHES Xcode)
  331. add_custom_command(TARGET ${ly_add_target_NAME} POST_BUILD
  332. COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/runtime_dependencies/$<CONFIG>/${ly_add_target_NAME}.cmake
  333. COMMENT "Copying ${ly_add_target_NAME} runtime dependencies to output..."
  334. DEPENDS ${CMAKE_BINARY_DIR}/runtime_dependencies/${ly_add_target_NAME}.cmake
  335. COMMENT "Copying runtime dependencies..."
  336. VERBATIM
  337. )
  338. else()
  339. # the stamp file will be the one that triggers the execution of the custom rule. At the end
  340. # of running the copy of runtime dependencies, the stamp file is touched so the timestamp is updated.
  341. # Adding a config as part of the name since the stamp file is added to the VS project.
  342. # Note the STAMP_OUTPUT_FILE need to match with the one used in runtime dependencies (e.g. RuntimeDependencies_common.cmake)
  343. set(STAMP_OUTPUT_FILE ${CMAKE_BINARY_DIR}/runtime_dependencies/$<CONFIG>/${ly_add_target_NAME}.stamp)
  344. add_custom_command(
  345. OUTPUT ${STAMP_OUTPUT_FILE}
  346. DEPENDS "$<GENEX_EVAL:$<TARGET_PROPERTY:${ly_add_target_NAME},RUNTIME_DEPENDENCIES_DEPENDS>>"
  347. COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/runtime_dependencies/$<CONFIG>/${ly_add_target_NAME}.cmake
  348. COMMENT "Copying ${ly_add_target_NAME} runtime dependencies to output..."
  349. VERBATIM
  350. )
  351. # Unfortunately the VS generator cannot deal with generation expressions as part of the file name, wrapping the
  352. # stamp file on each configuration so it gets properly excluded by the generator
  353. unset(stamp_files_per_config)
  354. foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES)
  355. set(stamp_file_conf ${CMAKE_BINARY_DIR}/runtime_dependencies/${conf}/${ly_add_target_NAME}.stamp)
  356. set_source_files_properties(${stamp_file_conf} PROPERTIES GENERATED TRUE SKIP_AUTOGEN TRUE)
  357. list(APPEND stamp_files_per_config $<$<CONFIG:${conf}>:${stamp_file_conf}>)
  358. endforeach()
  359. target_sources(${ly_add_target_NAME} PRIVATE ${stamp_files_per_config})
  360. endif()
  361. endif()
  362. if(ly_add_target_AUTOGEN_RULES)
  363. ly_add_autogen(
  364. NAME ${ly_add_target_NAME}
  365. INCLUDE_DIRECTORIES ${ly_add_target_INCLUDE_DIRECTORIES}
  366. AUTOGEN_RULES ${ly_add_target_AUTOGEN_RULES}
  367. ALLFILES ${ALLFILES}
  368. )
  369. endif()
  370. endfunction()
  371. #! ly_target_link_libraries: wraps target_link_libraries handling also MODULE linkage.
  372. # MODULE libraries cannot be passed to target_link_libraries. MODULE libraries are shared libraries that we
  373. # dont want to link against because they will be loaded dynamically. However, we want to include their public headers
  374. # and transition their public dependencies.
  375. # To achieve this, we delay the target_link_libraries call to after all targets are declared (see ly_delayed_target_link_libraries)
  376. #
  377. # Signature is the same as target_link_libraries:
  378. # target_link_libraries(<target> ... <item>... ...)
  379. #
  380. function(ly_target_link_libraries TARGET)
  381. if(NOT TARGET)
  382. message(FATAL_ERROR "You must provide a target")
  383. endif()
  384. set_property(TARGET ${TARGET} APPEND PROPERTY LY_DELAYED_LINK ${ARGN})
  385. set_property(GLOBAL APPEND PROPERTY LY_DELAYED_LINK_TARGETS ${TARGET}) # to walk them at the end
  386. endfunction()
  387. #! o3de_copy_targets_usage_requires: Copies the usage requirements of the input targets
  388. # For input target that have a TYPE of "MODULE_LIBRARY", its INTERFACE_* properties are copied,
  389. # since a MODULE_LIBRARY cannot be linked
  390. # For input targets that are not of "MODULE_LIBRARY" TYPE, they are addded as dependency of the
  391. # destination target through the target_link_libraries command
  392. # See: https://cmake.org/cmake/help/latest/prop_tgt/TYPE.html for list of available library types
  393. #
  394. # The primary purpose of this function is to aggregate the usage requirements of multiple dependencies
  395. # into an INTERFACE target, where it would act as a psuedo ALIAS target which can depend on multiple targets
  396. #\arg:TARGET destination INTERFACE target where usage requirements of the input targets will be aggregated into
  397. #\arg:SOURCE_TARGETS list of targets whose usage requirements will be copied from
  398. function(o3de_copy_targets_usage_requirements)
  399. set(oneValueArgs TARGET)
  400. set(multiValueArgs SOURCE_TARGETS)
  401. cmake_parse_arguments(usage_dependencies "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  402. if(NOT usage_dependencies_TARGET OR NOT TARGET ${usage_dependencies_TARGET})
  403. message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} must be specified a TARGET argument")
  404. endif()
  405. get_target_property(item_type ${usage_dependencies_TARGET} TYPE)
  406. if (NOT item_type STREQUAL INTERFACE_LIBRARY)
  407. message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} only supports copy of usage requirements to an INTERFACE TARGET")
  408. endif()
  409. set(dest_target ${usage_dependencies_TARGET})
  410. foreach(source_target IN LISTS usage_dependencies_SOURCE_TARGETS)
  411. unset(item_type)
  412. # Retrieve the resolved source target name
  413. set(source_target_de_alias ${source_target})
  414. if (TARGET ${source_target})
  415. get_property(item_type TARGET ${source_target} PROPERTY TYPE)
  416. ly_de_alias_target(${source_target} source_target_de_alias)
  417. endif()
  418. # If the source target does not exist the item_type is empty
  419. # In that case the TARGET is assumed to not be a MODULE_LIBRARY
  420. if(item_type STREQUAL MODULE_LIBRARY)
  421. # For module libraries the INTERFACE properties are copied over
  422. # Copy over include and system include directories
  423. target_include_directories(${dest_target} INTERFACE $<GENEX_EVAL:$<TARGET_PROPERTY:${source_target_de_alias},INTERFACE_INCLUDE_DIRECTORIES>>)
  424. target_include_directories(${dest_target} SYSTEM INTERFACE $<GENEX_EVAL:$<TARGET_PROPERTY:${source_target_de_alias},INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>>)
  425. # Copy over dependent link libraries and linker options
  426. target_link_libraries(${dest_target} INTERFACE $<GENEX_EVAL:$<TARGET_PROPERTY:${source_target_de_alias},INTERFACE_LINK_LIBRARIES>>)
  427. target_link_options(${dest_target} INTERFACE $<GENEX_EVAL:$<TARGET_PROPERTY:${source_target_de_alias},INTERFACE_LINK_OPTIONS>>)
  428. # Copy over compiler defintions and compiler options
  429. target_compile_definitions(${dest_target} INTERFACE $<GENEX_EVAL:$<TARGET_PROPERTY:${source_target_de_alias},INTERFACE_COMPILE_DEFINITIONS>>)
  430. target_compile_options(${dest_target} INTERFACE $<GENEX_EVAL:$<TARGET_PROPERTY:${source_target_de_alias},INTERFACE_COMPILE_OPTIONS>>)
  431. # Copy over source filenames; For the destionation INTERFACE target these will not compile,
  432. # but can be used as part of add_custom_command
  433. target_sources(${dest_target} INTERFACE $<GENEX_EVAL:$<TARGET_PROPERTY:${source_target_de_alias},INTERFACE_SOURCES>>)
  434. else()
  435. # The simpler case for non-MODULE targets is to copy the usage dependencies via target_link_libraries
  436. # This even works for source targets with a TYPE of "INTERFACE_LIBRARY" as its INTERFACE_* PROPERTIES
  437. # are transferred via target_link_libraries as well
  438. # https://cmake.org/cmake/help/latest/command/add_library.html#interface-libraries
  439. target_link_libraries(${dest_target} INTERFACE ${source_target_de_alias})
  440. endif()
  441. endforeach()
  442. endfunction()
  443. #! ly_delayed_target_link_libraries: internal function called by the root CMakeLists.txt after all targets
  444. # have been declared to determine if they are regularly
  445. # 1) If a MODULE is passed in the list of items, it will add the INTERFACE_INCLUDE_DIRECTORIES as include
  446. # directories of TARGET. It will also add the "INTERFACE_LINK_LIBRARIES" to TARGET. MODULEs cannot be
  447. # directly linked, but we can include the public headers and link against the things the MODULE expose
  448. # to link.
  449. # 2) If a target that has not yet been declared is passed, then it will defer it to after all targets are
  450. # declared. This way we can do a check again. We could delay the link to when
  451. # target is declared. This is needed for (1) since we dont know the type of target. This also addresses
  452. # another issue with target_link_libraries where it will only validate that a MODULE is not being passed
  453. # if the target is already declared, if not, it will fail later at linking time.
  454. function(ly_delayed_target_link_libraries)
  455. set(visibilities PRIVATE PUBLIC INTERFACE)
  456. get_property(additional_module_paths GLOBAL PROPERTY LY_ADDITIONAL_MODULE_PATH)
  457. list(APPEND CMAKE_MODULE_PATH ${additional_module_paths})
  458. get_property(delayed_targets GLOBAL PROPERTY LY_DELAYED_LINK_TARGETS)
  459. foreach(target ${delayed_targets})
  460. get_property(delayed_link TARGET ${target} PROPERTY LY_DELAYED_LINK)
  461. if(delayed_link)
  462. cmake_parse_arguments(ly_delayed_target_link_libraries "" "" "${visibilities}" ${delayed_link})
  463. foreach(visibility ${visibilities})
  464. foreach(alias_item ${ly_delayed_target_link_libraries_${visibility}})
  465. if(TARGET ${alias_item})
  466. get_target_property(item_type ${alias_item} TYPE)
  467. ly_de_alias_target(${alias_item} item)
  468. else()
  469. unset(item_type)
  470. set(item ${alias_item})
  471. endif()
  472. if(item_type STREQUAL MODULE_LIBRARY)
  473. target_include_directories(${target} ${visibility} $<GENEX_EVAL:$<TARGET_PROPERTY:${item},INTERFACE_INCLUDE_DIRECTORIES>>)
  474. target_link_libraries(${target} ${visibility} $<GENEX_EVAL:$<TARGET_PROPERTY:${item},INTERFACE_LINK_LIBRARIES>>)
  475. target_compile_definitions(${target} ${visibility} $<GENEX_EVAL:$<TARGET_PROPERTY:${item},INTERFACE_COMPILE_DEFINITIONS>>)
  476. target_compile_options(${target} ${visibility} $<GENEX_EVAL:$<TARGET_PROPERTY:${item},INTERFACE_COMPILE_OPTIONS>>)
  477. else()
  478. ly_parse_third_party_dependencies(${item})
  479. target_link_libraries(${target} ${visibility} ${item})
  480. endif()
  481. endforeach()
  482. endforeach()
  483. endif()
  484. endforeach()
  485. set_property(GLOBAL PROPERTY LY_DELAYED_LINK_TARGETS)
  486. endfunction()
  487. #! ly_parse_third_party_dependencies: Validates any 3rdParty library dependencies through the find_package command
  488. #
  489. # \arg:ly_THIRD_PARTY_LIBRARIES name of the target libraries to validate existance of through the find_package command.
  490. #
  491. function(ly_parse_third_party_dependencies ly_THIRD_PARTY_LIBRARIES)
  492. # Support for deprecated LY_VERSION_ENGINE_NAME used in 3p-package-source and misc 3p packages
  493. set(LY_VERSION_ENGINE_NAME ${O3DE_ENGINE_NAME})
  494. # Interface dependencies may require to find_packages. So far, we are just using packages for 3rdParty, so we will
  495. # search for those and automatically bring those packages. The naming convention used is 3rdParty::PackageName::OptionalInterface
  496. unset(all_thirdparty_dependencies_found)
  497. foreach(dependency ${ly_THIRD_PARTY_LIBRARIES})
  498. string(REPLACE "::" ";" dependency_list ${dependency})
  499. list(GET dependency_list 0 dependency_namespace)
  500. if(${dependency_namespace} STREQUAL "3rdParty")
  501. list(APPEND all_thirdparty_dependencies_found ${dependency})
  502. if (NOT TARGET ${dependency})
  503. if (O3DE_SCRIPT_ONLY)
  504. # we don't actually need 3p deps to try to download the package or call find_package.
  505. # instead we use pre-created part-of-the-installer 3p targets baked in from the above list
  506. # which will have been made at install time.
  507. # instead, we execute a pregenerated file that was created as part of install to
  508. # create this target:
  509. string(REPLACE "::" "__" CLEAN_TARGET_NAME "${dependency}")
  510. # not all 3ps actually exist as real targets, so this is an OPTIONAL include.
  511. include(${LY_ROOT_FOLDER}/cmake/3rdParty/Platform/${PAL_PLATFORM_NAME}/Default/${CLEAN_TARGET_NAME}.cmake OPTIONAL)
  512. else()
  513. list(GET dependency_list 1 dependency_package)
  514. list(LENGTH dependency_list dependency_list_length)
  515. ly_download_associated_package(${dependency_package})
  516. if (dependency_list_length GREATER 2)
  517. # There's an optional interface specified
  518. list(GET dependency_list 2 component)
  519. list(APPEND packages_with_components ${dependency_package})
  520. list(APPEND ${dependency_package}_components ${component})
  521. else()
  522. find_package(${dependency_package} REQUIRED MODULE)
  523. endif()
  524. endif()
  525. endif()
  526. endif()
  527. endforeach()
  528. foreach(dependency IN LISTS packages_with_components)
  529. find_package(${dependency} REQUIRED MODULE COMPONENTS ${${dependency}_components})
  530. endforeach()
  531. foreach(dependency ${all_thirdparty_dependencies_found})
  532. if (TARGET ${dependency})
  533. # keep track of all the 3p dependencies we actually depended on.
  534. get_property(o3de_all_3rdparty_targets GLOBAL PROPERTY O3DE_ALL_3RDPARTY_TARGETS)
  535. if(NOT ${dependency} IN_LIST o3de_all_3rdparty_targets)
  536. set_property(GLOBAL APPEND PROPERTY O3DE_ALL_3RDPARTY_TARGETS "${dependency}")
  537. endif()
  538. endif()
  539. endforeach()
  540. endfunction()
  541. #! ly_configure_target_platform_properties: Configures any platform specific properties on target
  542. #
  543. # Looks at the the following variables within the platform include file to set the equivalent target properties
  544. # LY_FILES_CMAKE -> extract list of files -> target_sources
  545. # LY_FILES -> target_source
  546. # LY_INCLUDE_DIRECTORIES -> target_include_directories
  547. # LY_COMPILE_DEFINITIONS -> target_compile_definitions
  548. # LY_COMPILE_OPTIONS -> target_compile_options
  549. # LY_LINK_OPTIONS -> target_link_options
  550. # LY_BUILD_DEPENDENCIES -> target_link_libraries
  551. # LY_RUNTIME_DEPENDENCIES -> ly_add_dependencies
  552. # LY_TARGET_PROPERTIES -> target_properties
  553. #
  554. macro(ly_configure_target_platform_properties)
  555. foreach(platform_include_file ${ly_add_target_PLATFORM_INCLUDE_FILES})
  556. set(LY_FILES_CMAKE)
  557. set(LY_FILES)
  558. set(LY_INCLUDE_DIRECTORIES)
  559. set(LY_COMPILE_DEFINITIONS)
  560. set(LY_COMPILE_OPTIONS)
  561. set(LY_LINK_OPTIONS)
  562. set(LY_BUILD_DEPENDENCIES)
  563. set(LY_RUNTIME_DEPENDENCIES)
  564. set(LY_TARGET_PROPERTIES)
  565. include(${platform_include_file} RESULT_VARIABLE ly_platform_cmake_file)
  566. if(NOT ly_platform_cmake_file)
  567. message(FATAL_ERROR "The supplied PLATFORM_INCLUDE_FILE(${platform_include_file}) cannot be included.\
  568. Parsing of target will halt")
  569. endif()
  570. if(ly_add_target_INTERFACE OR ly_add_target_IMPORTED)
  571. target_sources(${ly_add_target_NAME} INTERFACE ${platform_include_file})
  572. else()
  573. target_sources(${ly_add_target_NAME} PRIVATE ${platform_include_file})
  574. endif()
  575. ly_source_groups_from_folders("${platform_include_file}")
  576. if(LY_FILES_CMAKE)
  577. foreach(file_cmake ${LY_FILES_CMAKE})
  578. ly_include_cmake_file_list(${file_cmake})
  579. endforeach()
  580. target_sources(${ly_add_target_NAME} PRIVATE ${ALLFILES})
  581. ly_source_groups_from_folders("${ALLFILES}")
  582. endif()
  583. if(LY_FILES)
  584. target_sources(${ly_add_target_NAME} PRIVATE ${LY_FILES})
  585. endif()
  586. if (LY_INCLUDE_DIRECTORIES)
  587. target_include_directories(${ly_add_target_NAME} ${LY_INCLUDE_DIRECTORIES})
  588. endif()
  589. if(LY_COMPILE_DEFINITIONS)
  590. target_compile_definitions(${ly_add_target_NAME} ${LY_COMPILE_DEFINITIONS})
  591. endif()
  592. if(LY_COMPILE_OPTIONS)
  593. target_compile_options(${ly_add_target_NAME} ${LY_COMPILE_OPTIONS})
  594. endif()
  595. if(LY_LINK_OPTIONS)
  596. target_link_options(${ly_add_target_NAME} ${LY_LINK_OPTIONS})
  597. endif()
  598. if(LY_BUILD_DEPENDENCIES)
  599. ly_target_link_libraries(${ly_add_target_NAME} ${LY_BUILD_DEPENDENCIES})
  600. endif()
  601. if(LY_RUNTIME_DEPENDENCIES)
  602. ly_add_dependencies(${ly_add_target_NAME} ${LY_RUNTIME_DEPENDENCIES})
  603. endif()
  604. if(LY_TARGET_PROPERTIES)
  605. set_target_properties(${ly_add_target_NAME} PROPERTIES ${LY_TARGET_PROPERTIES})
  606. endif()
  607. endforeach()
  608. endmacro()
  609. #! ly_add_target_files: adds files to a target. This will copy the specified files to where the target is.
  610. #
  611. # \arg:TARGETS name of the targets that depends on this file
  612. # \arg:FILES files to copy
  613. # \arg:OUTPUT_SUBDIRECTORY (OPTIONAL) where to place the files relative to TARGET_NAME's output dir.
  614. # If not specified, they are located in the same folder as TARGET_NAME
  615. #
  616. function(ly_add_target_files)
  617. set(options)
  618. set(oneValueArgs OUTPUT_SUBDIRECTORY)
  619. set(multiValueArgs TARGETS FILES)
  620. cmake_parse_arguments(ly_add_target_files "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  621. # Validate input arguments
  622. if(NOT ly_add_target_files_TARGETS)
  623. message(FATAL_ERROR "You must provide at least one target")
  624. endif()
  625. if(NOT ly_add_target_files_FILES)
  626. message(FATAL_ERROR "You must provide at least a file to copy")
  627. endif()
  628. foreach(target ${ly_add_target_files_TARGETS})
  629. foreach(file ${ly_add_target_files_FILES})
  630. set_property(TARGET ${target} APPEND PROPERTY INTERFACE_LY_TARGET_FILES "${file}\n${ly_add_target_files_OUTPUT_SUBDIRECTORY}")
  631. endforeach()
  632. endforeach()
  633. endfunction()
  634. #! ly_add_source_properties: adds/appends properties to a source file.
  635. #
  636. # This wraps set_source_files_properties to be able to pass a property with multiple values
  637. # and get it appended instead of set.
  638. #
  639. # \arg:SOURCES list of sources to apply this property on
  640. # \arg:PROPERTY property to set
  641. # \arg:VALUES values to append
  642. #
  643. function(ly_add_source_properties)
  644. set(options)
  645. set(oneValueArgs PROPERTY)
  646. set(multiValueArgs SOURCES VALUES)
  647. cmake_parse_arguments(ly_add_source_properties "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  648. # Validate input arguments
  649. if(NOT ly_add_source_properties_SOURCES)
  650. message(FATAL_ERROR "You must provide at least one source")
  651. endif()
  652. if(NOT ly_add_source_properties_PROPERTY)
  653. message(FATAL_ERROR "You must provide a property")
  654. endif()
  655. foreach(file ${ly_add_source_properties_SOURCES})
  656. if(NOT IS_ABSOLUTE ${file})
  657. get_filename_component(file ${file} ABSOLUTE)
  658. endif()
  659. if(NOT EXISTS ${file})
  660. message(SEND_ERROR "File ${file} not found when setting property ${ly_add_source_properties_PROPERTY}")
  661. endif()
  662. endforeach()
  663. # We allow to pass empty values because in some cases we expand the values based on conditions
  664. # If the values are empty then this call does nothing
  665. if(ly_add_source_properties_VALUES)
  666. set_property(
  667. SOURCE ${ly_add_source_properties_SOURCES}
  668. APPEND PROPERTY ${ly_add_source_properties_PROPERTY} ${ly_add_source_properties_VALUES}
  669. )
  670. endif()
  671. endfunction()
  672. # given a target name, returns the "real" name of the target if its an alias.
  673. # this function recursively de-aliases
  674. function(ly_de_alias_target target_name output_variable_name)
  675. # its not okay to call get_target_property on a non-existent target
  676. if (NOT TARGET ${target_name})
  677. message(FATAL_ERROR "ly_de_alias_target called on non-existent target: ${target_name}")
  678. endif()
  679. while(target_name)
  680. set(de_aliased_target_name ${target_name})
  681. get_target_property(target_name ${target_name} ALIASED_TARGET)
  682. endwhile()
  683. if(NOT de_aliased_target_name)
  684. message(FATAL_ERROR "Empty de_aliased for ${target_name}")
  685. endif()
  686. set(${output_variable_name} ${de_aliased_target_name} PARENT_SCOPE)
  687. endfunction()
  688. #! ly_get_vs_folder_directory: Sets the Visual Studio folder name used for organizing vcxproj
  689. # in the IDE
  690. #
  691. # Visual Studio cannot load projects that with a ".." relative path or contain a colon ":" as part of its FOLDER
  692. # Therefore if the .vcxproj is absolute, the drive letter must be removed from the folder name
  693. #
  694. # What this method does is first check if the target being added to the Visual Studio solution is within
  695. # the LY_ROOT_FOLDER(i.e is the LY_ROOT_FOLDER a prefix of the target source directory)
  696. # If it is a relative path to the target is used as the folder name
  697. # Otherwise the target directory would either
  698. # 1. Be a path outside of the LY_ROOT_FOLDER on the same drive.
  699. # In that case forming a relative path would cause it to start with ".." which will not work
  700. # 2. Be an path outside of the LY_ROOT_FOLDER on a different drive
  701. # Here a relative path cannot be formed and therefore the path would start with "<drive>:/Path/To/Project"
  702. # Which shows up as unloaded due to containing a colon
  703. # In this scenario the relative part of the path from the drive letter is used as the FOLDER name
  704. # to make sure the projects show up in the loaded .sln
  705. function(ly_get_vs_folder_directory absolute_target_source_dir output_source_dir)
  706. # Get a relative directory to the LY_ROOT_FOLDER if possible for the Visual Studio solution hierarchy
  707. # If a relative path cannot be formed, then retrieve a path with the drive letter stripped from it
  708. cmake_path(IS_PREFIX LY_ROOT_FOLDER ${absolute_target_source_dir} is_target_prefix_of_engine_root)
  709. if(is_target_prefix_of_engine_root)
  710. cmake_path(RELATIVE_PATH absolute_target_source_dir BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE relative_target_source_dir)
  711. else()
  712. cmake_path(IS_PREFIX CMAKE_SOURCE_DIR ${absolute_target_source_dir} is_target_prefix_of_source_dir)
  713. if(is_target_prefix_of_source_dir)
  714. cmake_path(RELATIVE_PATH absolute_target_source_dir BASE_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE relative_target_source_dir)
  715. else()
  716. cmake_path(GET absolute_target_source_dir RELATIVE_PART relative_target_source_dir)
  717. endif()
  718. endif()
  719. set(${output_source_dir} ${relative_target_source_dir} PARENT_SCOPE)
  720. endfunction()