123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833 |
- #
- # Copyright (c) Contributors to the Open 3D Engine Project.
- # For complete copyright and license terms please see the LICENSE at the root of this distribution.
- #
- # SPDX-License-Identifier: Apache-2.0 OR MIT
- #
- #
- set(LY_UNITY_BUILD ON CACHE BOOL "UNITY builds")
- include(CMakeFindDependencyMacro)
- include(cmake/LyAutoGen.cmake)
- o3de_pal_dir(pal_dir ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Platform/${PAL_PLATFORM_NAME} "${O3DE_ENGINE_RESTRICTED_PATH}" "${LY_ROOT_FOLDER}")
- include(${pal_dir}/LYWrappers_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
- # Not all platforms support unity builds
- if(LY_UNITY_BUILD AND NOT PAL_TRAIT_BUILD_UNITY_SUPPORTED)
- message(ERROR "LY_UNITY_BUILD is specified, but not supported for the current target platform")
- endif()
- define_property(TARGET PROPERTY GEM_MODULE
- BRIEF_DOCS "Defines a MODULE library as a Gem"
- FULL_DOCS [[
- Property which is set on targets that should be seen as gems
- This is used to determine whether this target should load as
- when used as a runtime dependency should load at the same time
- as its dependee
- ]]
- )
- define_property(TARGET PROPERTY RUNTIME_DEPENDENCIES_DEPENDS
- BRIEF_DOCS "Defines the dependencies the runtime dependencies of a target has"
- FULL_DOCS [[
- Property which is queried through generator expressions at the moment
- the target is declared so a custom command that will do the copies can
- be generated later. Custom commands need to be declared in the same folder
- the target is declared, however, runtime dependencies need all targets
- to be declared, so it is done towards the end of CMake parsing. When
- runtime dependencies are processed, this target property is filled so the
- right dependencies are set for the custom command.
- This property contains all the files that are going to be copied to the output
- when the target gets built.
- ]]
- )
- #! ly_add_target: adds a target and provides parameters for the common configurations.
- #
- # Adds a target (static/dynamic library, executable) and convenient wrappers around most
- # common parameters that need to be set.
- # This function also creates an interface to use for dependencies. The interface will be
- # named as "NAMESPACE::NAME"
- # Some examples:
- # ly_add_target(NAME mystaticlib STATIC FILES_CMAKE somestatic_files.cmake)
- # ly_add_target(NAME mydynamiclib SHARED FILES_CMAKE somedyn_files.cmake)
- # ly_add_target(NAME myexecutable EXECUTABLE FILES_CMAKE someexe_files.cmake)
- #
- # \arg:NAME name of the target
- # \arg:STATIC (bool) defines this target to be a static library
- # \arg:GEM_STATIC (bool) defines this target to be a static library while also setting the GEM_MODULE property
- # \arg:SHARED (bool) defines this target to be a dynamic library
- # \arg:GEM_SHARED (bool) defines this target to be a dynamic library while also setting the GEM_MODULE property
- # \arg:MODULE (bool) defines this target to be a module library
- # \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
- # \arg:OBJECT (bool) defines this target to be an object library
- # \arg:INTERFACE (bool) defines this target to be an interface library. A ${NAME}_HEADERS project will be created for the IDE
- # The HEADERONLY option can be specified as an alternative
- # \arg:EXECUTABLE (bool) defines this target to be an executable
- # \arg:APPLICATION (bool) defines this target to be an application (executable that is not a console)
- # \arg:IMPORTED (bool) defines this target to be imported.
- # \arg:NAMESPACE namespace declaration for this target. It will be used for IDE and dependencies
- # \arg:OUTPUT_NAME (optional) overrides the name of the output target. If not specified, the name will be used.
- # \arg:OUTPUT_SUBDIRECTORY places the runtime binary in a subfolder within the output folder (this only affects to runtime binaries)
- # \arg:AUTOMOC enables Qt moc in the target
- # \arg:AUTOUIC enables Qt uic in the target
- # \arg:AUTORCC enables Qt rcc in the target
- # \arg:NO_UNITY Prevent the target from employing unity builds even when unity builds (LY_UNITY_BUILD) are enabled
- # \arg:FILES_CMAKE list of *_files.cmake files that contain files for this target
- # \arg:GENERATED_FILES list of files to add to this target that are generated out of some other task
- # \arg:INCLUDE_DIRECTORIES list of directories to use as include paths
- # \arg:BUILD_DEPENDENCIES list of interfaces this target depends on (could be a compilation dependency
- # if the dependency is only exposing an include path, or could be a linking
- # dependency is exposing a lib)
- # \arg:RUNTIME_DEPENDENCIES list of dependencies this target depends on at runtime
- # \arg:COMPILE_DEFINITIONS list of compilation definitions this target will use to compile
- # \arg:PLATFORM_INCLUDE_FILES *.cmake files which should contain platform specific configuration to be added to the target
- # Look at the documentation for the ly_configure_target_platform_properties() function below
- # for the list of variables that will be used by the target
- # \arg:TARGET_PROPERTIES additional properties to set to the target
- # \arg:AUTOGEN_RULES a set of AutoGeneration rules to be passed to the AzAutoGen expansion system
- function(ly_add_target)
- set(options STATIC SHARED MODULE GEM_STATIC GEM_MODULE OBJECT HEADERONLY EXECUTABLE APPLICATION IMPORTED AUTOMOC AUTOUIC AUTORCC NO_UNITY)
- set(oneValueArgs NAME NAMESPACE OUTPUT_SUBDIRECTORY OUTPUT_NAME)
- set(multiValueArgs FILES_CMAKE GENERATED_FILES INCLUDE_DIRECTORIES COMPILE_DEFINITIONS BUILD_DEPENDENCIES RUNTIME_DEPENDENCIES PLATFORM_INCLUDE_FILES TARGET_PROPERTIES AUTOGEN_RULES)
- cmake_parse_arguments(ly_add_target "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
- # Since the term "INTERFACE" used in other context such as INCLUDE_DIRECTORIES, COMPILE_DEFINITIONS to specifiy property visibility
- # It needs to be parsed after those arguments have been parsed to avoid the usage of INTERFACE as a visibility scope
- cmake_parse_arguments(ly_add_target "INTERFACE" "" "" ${ly_add_target_UNPARSED_ARGUMENTS})
- # Validate input arguments
- if(NOT ly_add_target_NAME)
- message(FATAL_ERROR "You must provide a name for the target")
- endif()
- # Map HEADERONLY option to INTERFACE
- if(ly_add_target_HEADERONLY)
- set(ly_add_target_INTERFACE ly_add_target_HEADERONLY)
- endif()
- if(NOT ly_add_target_IMPORTED AND NOT ly_add_target_INTERFACE)
- if(NOT ly_add_target_FILES_CMAKE)
- message(FATAL_ERROR "You must provide a list of _files.cmake files for the target")
- endif()
- endif()
- # If the GEM_MODULE tag is passed set the normal MODULE argument
- if(ly_add_target_GEM_MODULE)
- set(ly_add_target_MODULE ${ly_add_target_GEM_MODULE})
- endif()
- # If the GEM_STATIC tag is passed mark the target as STATIC
- if(ly_add_target_GEM_STATIC)
- set(ly_add_target_STATIC ${ly_add_target_GEM_STATIC})
- endif()
- # If the GEM_SHARED tag is passed mark the target as SHARED
- if(ly_add_target_GEM_SHARED)
- set(ly_add_target_SHARED ${ly_add_target_GEM_SHARED})
- endif()
- foreach(file_cmake ${ly_add_target_FILES_CMAKE})
- ly_include_cmake_file_list(${file_cmake})
- endforeach()
- unset(linking_options)
- unset(linking_count)
- unset(target_type_options)
- if(ly_add_target_STATIC)
- set(linking_options STATIC)
- set(target_type_options STATIC)
- set(linking_count "${linking_count}1")
- endif()
- if(ly_add_target_OBJECT)
- set(linking_options OBJECT)
- set(target_type_options OBJECT)
- set(linking_count "${linking_count}1")
- endif()
- if(ly_add_target_SHARED)
- set(linking_options SHARED)
- set(target_type_options SHARED)
- set(linking_count "${linking_count}1")
- endif()
- if(ly_add_target_MODULE)
- set(linking_options ${PAL_LINKOPTION_MODULE})
- set(target_type_options ${PAL_LINKOPTION_MODULE})
- set(linking_count "${linking_count}1")
- endif()
- if(ly_add_target_INTERFACE)
- set(linking_options INTERFACE)
- set(target_type_options INTERFACE)
- set(linking_count "${linking_count}1")
- endif()
- if(ly_add_target_EXECUTABLE)
- set(linking_options EXECUTABLE)
- set(linking_count "${linking_count}1")
- endif()
- if(ly_add_target_APPLICATION)
- set(linking_options APPLICATION)
- set(linking_count "${linking_count}1")
- endif()
- if(NOT ("${linking_count}" STREQUAL "1"))
- message(FATAL_ERROR "More than one of the following options [STATIC | SHARED | MODULE | OBJECT | INTERFACE | EXECUTABLE | APPLICATION ] was specified and they are mutually exclusive")
- endif()
- if(ly_add_target_IMPORTED)
- list(APPEND target_type_options IMPORTED GLOBAL)
- endif()
- if(ly_add_target_NAMESPACE)
- set(interface_name "${ly_add_target_NAMESPACE}::${ly_add_target_NAME}")
- else()
- set(interface_name "${ly_add_target_NAME}")
- endif()
- set(project_NAME ${ly_add_target_NAME})
- if(ly_add_target_EXECUTABLE)
- add_executable(${ly_add_target_NAME}
- ${target_type_options}
- ${ALLFILES} ${ly_add_target_GENERATED_FILES}
- )
- ly_apply_platform_properties(${ly_add_target_NAME})
- if(ly_add_target_IMPORTED)
- set_target_properties(${ly_add_target_NAME} PROPERTIES LINKER_LANGUAGE CXX)
- endif()
- elseif(ly_add_target_APPLICATION)
- add_executable(${ly_add_target_NAME}
- ${target_type_options}
- ${PAL_EXECUTABLE_APPLICATION_FLAG}
- ${ALLFILES} ${ly_add_target_GENERATED_FILES}
- )
- ly_apply_platform_properties(${ly_add_target_NAME})
- if(ly_add_target_IMPORTED)
- set_target_properties(${ly_add_target_NAME} PROPERTIES LINKER_LANGUAGE CXX)
- endif()
- elseif(ly_add_target_INTERFACE)
- add_library(${ly_add_target_NAME}
- ${target_type_options}
- ${ALLFILES} ${ly_add_target_GENERATED_FILES}
- )
- else()
- add_library(${ly_add_target_NAME}
- ${target_type_options}
- ${ALLFILES} ${ly_add_target_GENERATED_FILES}
- )
- ly_apply_platform_properties(${ly_add_target_NAME})
- endif()
- if(${ly_add_target_GENERATED_FILES})
- set_source_files_properties(${ly_add_target_GENERATED_FILES}
- PROPERTIES GENERATED TRUE
- )
- endif()
- if(${ly_add_target_EXECUTABLE} OR ${ly_add_target_APPLICATION})
- add_executable(${interface_name} ALIAS ${ly_add_target_NAME})
- else()
- add_library(${interface_name} ALIAS ${ly_add_target_NAME})
- endif()
- if(ly_add_target_OUTPUT_NAME)
- set_target_properties(${ly_add_target_NAME} PROPERTIES
- OUTPUT_NAME ${ly_add_target_OUTPUT_NAME}
- )
- endif()
- # Add the target dependencies so they can be walked later
- if(ly_add_target_BUILD_DEPENDENCIES)
- set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGETS_${ly_add_target_NAME}_BUILD_DEPENDENCIES ${ly_add_target_BUILD_DEPENDENCIES})
- endif()
- if(ly_add_target_RUNTIME_DEPENDENCIES)
- set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGETS_${ly_add_target_NAME}_RUNTIME_DEPENDENCIES ${ly_add_target_RUNTIME_DEPENDENCIES})
- endif()
- if (ly_add_target_SHARED OR ly_add_target_MODULE OR ly_add_target_EXECUTABLE OR ly_add_target_APPLICATION)
- if (ly_add_target_OUTPUT_SUBDIRECTORY)
- ly_handle_custom_output_directory(${ly_add_target_NAME} ${ly_add_target_OUTPUT_SUBDIRECTORY})
- else()
- ly_handle_custom_output_directory(${ly_add_target_NAME} "")
- endif()
- endif()
- if(ly_add_target_GEM_MODULE OR ly_add_target_GEM_STATIC OR ly_add_target_GEM_SHARED)
- set_target_properties(${ly_add_target_NAME} PROPERTIES GEM_MODULE TRUE)
- endif()
- if (ly_add_target_INCLUDE_DIRECTORIES)
- target_include_directories(${ly_add_target_NAME}
- ${ly_add_target_INCLUDE_DIRECTORIES}
- )
- endif()
- ly_apply_debug_strip_options(${ly_add_target_NAME})
- # Parse the 3rdParty library dependencies
- ly_parse_third_party_dependencies("${ly_add_target_BUILD_DEPENDENCIES}")
- ly_target_link_libraries(${ly_add_target_NAME}
- ${ly_add_target_BUILD_DEPENDENCIES}
- )
- if(ly_add_target_COMPILE_DEFINITIONS)
- target_compile_definitions(${ly_add_target_NAME}
- ${ly_add_target_COMPILE_DEFINITIONS}
- )
- endif()
- # For any target that depends on AzTest and is built as an executable, an additional 'AZ_TEST_EXECUTABLE' define will
- # enable the 'AZ_UNIT_TEST_HOOK' macro to also implement main() so that running the executable directly will run
- # the AZ_UNIT_TEST_HOOK function
- if (${linking_options} STREQUAL "EXECUTABLE" AND "AZ::AzTest" IN_LIST ly_add_target_BUILD_DEPENDENCIES)
- target_compile_definitions(${ly_add_target_NAME}
- PRIVATE
- AZ_TEST_EXECUTABLE
- )
- endif()
- if(ly_add_target_TARGET_PROPERTIES)
- set_target_properties(${ly_add_target_NAME} PROPERTIES
- ${ly_add_target_TARGET_PROPERTIES})
- endif()
- set(unity_target_types SHARED MODULE EXECUTABLE APPLICATION STATIC OBJECT)
- if(linking_options IN_LIST unity_target_types)
- # For eligible target types, if unity builds (LY_UNITY_BUILD) is enabled and the target is not marked with 'NO_UNITY',
- # enable UNITY builds individually for the target
- if(LY_UNITY_BUILD AND NOT ${ly_add_target_NO_UNITY})
- set_target_properties(${ly_add_target_NAME} PROPERTIES
- UNITY_BUILD ON
- UNITY_BUILD_MODE BATCH
- )
- endif()
- endif()
- # IDE organization
- ly_source_groups_from_folders("${ALLFILES}")
- source_group("Generated Files" REGULAR_EXPRESSION "(${CMAKE_BINARY_DIR})") # Any file coming from the output folder
- ly_get_vs_folder_directory(${CMAKE_CURRENT_SOURCE_DIR} ide_path)
- set_property(TARGET ${project_NAME} PROPERTY FOLDER ${ide_path})
- if(ly_add_target_RUNTIME_DEPENDENCIES)
- ly_parse_third_party_dependencies("${ly_add_target_RUNTIME_DEPENDENCIES}")
- ly_add_dependencies(${ly_add_target_NAME} ${ly_add_target_RUNTIME_DEPENDENCIES})
- endif()
- ly_configure_target_platform_properties()
- # Handle Qt MOC, RCC, UIC
- # https://gitlab.kitware.com/cmake/cmake/issues/18749
- # AUTOMOC is supposed to always rebuild because it checks files that are not listed in the sources (like extra
- # "_p.h" headers) and which may change outside the visibility of the generator.
- # We are not using AUTOUIC because of:
- # https://gitlab.kitware.com/cmake/cmake/-/issues/18741
- # To overcome this problem, we manually wrap all the ui files listed in the target with qt5_wrap_ui
- foreach(prop IN ITEMS AUTOMOC AUTORCC)
- if(${ly_add_target_${prop}})
- set_property(TARGET ${ly_add_target_NAME} PROPERTY ${prop} ON)
- endif()
- endforeach()
- if(${ly_add_target_AUTOUIC})
- get_target_property(all_ui_sources ${ly_add_target_NAME} SOURCES)
- list(FILTER all_ui_sources INCLUDE REGEX "^.*\\.ui$")
- if(NOT all_ui_sources)
- message(FATAL_ERROR "Target ${ly_add_target_NAME} contains AUTOUIC but doesnt have any .ui file")
- endif()
- ly_qt_uic_target(${ly_add_target_NAME})
- endif()
- # Add dependencies that were added before this target was available
- get_property(additional_dependencies GLOBAL PROPERTY LY_DELAYED_DEPENDENCIES_${ly_add_target_NAME})
- if(additional_dependencies)
- ly_add_dependencies(${ly_add_target_NAME} ${additional_dependencies})
- # Clear the variable so we can track issues in case some dependency is added after
- set_property(GLOBAL PROPERTY LY_DELAYED_DEPENDENCIES_${ly_add_target_NAME})
- endif()
- # Store the target so we can walk through all of them in LocationDependencies.cmake
- set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGETS ${interface_name})
- if(NOT ly_add_target_IMPORTED)
- # Store the aliased target into a DIRECTORY property
- set_property(DIRECTORY APPEND PROPERTY LY_DIRECTORY_TARGETS ${interface_name})
- # Store the directory path in a GLOBAL property so that it can be accessed
- # in the layout install logic. Skip if the directory has already been added
- get_property(ly_all_target_directories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES)
- if(NOT CMAKE_CURRENT_SOURCE_DIR IN_LIST ly_all_target_directories)
- set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGET_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR})
- endif()
- endif()
- # Custom commands need to be declared in the same folder as the target that they use.
- # Not all the targets will require runtime dependencies, but we will generate at least an
- # empty file for them.
- set(runtime_dependencies_list SHARED MODULE EXECUTABLE APPLICATION)
- if(NOT ly_add_target_IMPORTED AND linking_options IN_LIST runtime_dependencies_list)
- get_property(is_multi_config_generator GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
- # XCode generator doesnt support different source files per configuration, so we cannot have
- # the runtime dependencies using file-tracking, instead, we will have them as a post build step
- # Non-multi config generators like Ninja (not "Ninja Multi-Config"), Makefiles, etc have trouble to
- # produce file-level dependencies per configuration, so we also default to use a post build step
- if(NOT is_multi_config_generator OR CMAKE_GENERATOR MATCHES Xcode)
- add_custom_command(TARGET ${ly_add_target_NAME} POST_BUILD
- COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/runtime_dependencies/$<CONFIG>/${ly_add_target_NAME}.cmake
- COMMENT "Copying ${ly_add_target_NAME} runtime dependencies to output..."
- DEPENDS ${CMAKE_BINARY_DIR}/runtime_dependencies/${ly_add_target_NAME}.cmake
- COMMENT "Copying runtime dependencies..."
- VERBATIM
- )
- else()
- # the stamp file will be the one that triggers the execution of the custom rule. At the end
- # of running the copy of runtime dependencies, the stamp file is touched so the timestamp is updated.
- # Adding a config as part of the name since the stamp file is added to the VS project.
- # Note the STAMP_OUTPUT_FILE need to match with the one used in runtime dependencies (e.g. RuntimeDependencies_common.cmake)
- set(STAMP_OUTPUT_FILE ${CMAKE_BINARY_DIR}/runtime_dependencies/$<CONFIG>/${ly_add_target_NAME}.stamp)
- add_custom_command(
- OUTPUT ${STAMP_OUTPUT_FILE}
- DEPENDS "$<GENEX_EVAL:$<TARGET_PROPERTY:${ly_add_target_NAME},RUNTIME_DEPENDENCIES_DEPENDS>>"
- COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/runtime_dependencies/$<CONFIG>/${ly_add_target_NAME}.cmake
- COMMENT "Copying ${ly_add_target_NAME} runtime dependencies to output..."
- VERBATIM
- )
- # Unfortunately the VS generator cannot deal with generation expressions as part of the file name, wrapping the
- # stamp file on each configuration so it gets properly excluded by the generator
- unset(stamp_files_per_config)
- foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES)
- set(stamp_file_conf ${CMAKE_BINARY_DIR}/runtime_dependencies/${conf}/${ly_add_target_NAME}.stamp)
- set_source_files_properties(${stamp_file_conf} PROPERTIES GENERATED TRUE SKIP_AUTOGEN TRUE)
- list(APPEND stamp_files_per_config $<$<CONFIG:${conf}>:${stamp_file_conf}>)
- endforeach()
- target_sources(${ly_add_target_NAME} PRIVATE ${stamp_files_per_config})
- endif()
- endif()
- if(ly_add_target_AUTOGEN_RULES)
- ly_add_autogen(
- NAME ${ly_add_target_NAME}
- INCLUDE_DIRECTORIES ${ly_add_target_INCLUDE_DIRECTORIES}
- AUTOGEN_RULES ${ly_add_target_AUTOGEN_RULES}
- ALLFILES ${ALLFILES}
- )
- endif()
- endfunction()
- #! ly_target_link_libraries: wraps target_link_libraries handling also MODULE linkage.
- # MODULE libraries cannot be passed to target_link_libraries. MODULE libraries are shared libraries that we
- # dont want to link against because they will be loaded dynamically. However, we want to include their public headers
- # and transition their public dependencies.
- # To achieve this, we delay the target_link_libraries call to after all targets are declared (see ly_delayed_target_link_libraries)
- #
- # Signature is the same as target_link_libraries:
- # target_link_libraries(<target> ... <item>... ...)
- #
- function(ly_target_link_libraries TARGET)
- if(NOT TARGET)
- message(FATAL_ERROR "You must provide a target")
- endif()
- set_property(TARGET ${TARGET} APPEND PROPERTY LY_DELAYED_LINK ${ARGN})
- set_property(GLOBAL APPEND PROPERTY LY_DELAYED_LINK_TARGETS ${TARGET}) # to walk them at the end
- endfunction()
- #! o3de_copy_targets_usage_requires: Copies the usage requirements of the input targets
- # For input target that have a TYPE of "MODULE_LIBRARY", its INTERFACE_* properties are copied,
- # since a MODULE_LIBRARY cannot be linked
- # For input targets that are not of "MODULE_LIBRARY" TYPE, they are addded as dependency of the
- # destination target through the target_link_libraries command
- # See: https://cmake.org/cmake/help/latest/prop_tgt/TYPE.html for list of available library types
- #
- # The primary purpose of this function is to aggregate the usage requirements of multiple dependencies
- # into an INTERFACE target, where it would act as a psuedo ALIAS target which can depend on multiple targets
- #\arg:TARGET destination INTERFACE target where usage requirements of the input targets will be aggregated into
- #\arg:SOURCE_TARGETS list of targets whose usage requirements will be copied from
- function(o3de_copy_targets_usage_requirements)
- set(oneValueArgs TARGET)
- set(multiValueArgs SOURCE_TARGETS)
- cmake_parse_arguments(usage_dependencies "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
- if(NOT usage_dependencies_TARGET OR NOT TARGET ${usage_dependencies_TARGET})
- message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} must be specified a TARGET argument")
- endif()
- get_target_property(item_type ${usage_dependencies_TARGET} TYPE)
- if (NOT item_type STREQUAL INTERFACE_LIBRARY)
- message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} only supports copy of usage requirements to an INTERFACE TARGET")
- endif()
- set(dest_target ${usage_dependencies_TARGET})
- foreach(source_target IN LISTS usage_dependencies_SOURCE_TARGETS)
- unset(item_type)
- # Retrieve the resolved source target name
- set(source_target_de_alias ${source_target})
- if (TARGET ${source_target})
- get_property(item_type TARGET ${source_target} PROPERTY TYPE)
- ly_de_alias_target(${source_target} source_target_de_alias)
- endif()
- # If the source target does not exist the item_type is empty
- # In that case the TARGET is assumed to not be a MODULE_LIBRARY
- if(item_type STREQUAL MODULE_LIBRARY)
- # For module libraries the INTERFACE properties are copied over
- # Copy over include and system include directories
- target_include_directories(${dest_target} INTERFACE $<GENEX_EVAL:$<TARGET_PROPERTY:${source_target_de_alias},INTERFACE_INCLUDE_DIRECTORIES>>)
- target_include_directories(${dest_target} SYSTEM INTERFACE $<GENEX_EVAL:$<TARGET_PROPERTY:${source_target_de_alias},INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>>)
- # Copy over dependent link libraries and linker options
- target_link_libraries(${dest_target} INTERFACE $<GENEX_EVAL:$<TARGET_PROPERTY:${source_target_de_alias},INTERFACE_LINK_LIBRARIES>>)
- target_link_options(${dest_target} INTERFACE $<GENEX_EVAL:$<TARGET_PROPERTY:${source_target_de_alias},INTERFACE_LINK_OPTIONS>>)
- # Copy over compiler defintions and compiler options
- target_compile_definitions(${dest_target} INTERFACE $<GENEX_EVAL:$<TARGET_PROPERTY:${source_target_de_alias},INTERFACE_COMPILE_DEFINITIONS>>)
- target_compile_options(${dest_target} INTERFACE $<GENEX_EVAL:$<TARGET_PROPERTY:${source_target_de_alias},INTERFACE_COMPILE_OPTIONS>>)
- # Copy over source filenames; For the destionation INTERFACE target these will not compile,
- # but can be used as part of add_custom_command
- target_sources(${dest_target} INTERFACE $<GENEX_EVAL:$<TARGET_PROPERTY:${source_target_de_alias},INTERFACE_SOURCES>>)
- else()
- # The simpler case for non-MODULE targets is to copy the usage dependencies via target_link_libraries
- # This even works for source targets with a TYPE of "INTERFACE_LIBRARY" as its INTERFACE_* PROPERTIES
- # are transferred via target_link_libraries as well
- # https://cmake.org/cmake/help/latest/command/add_library.html#interface-libraries
- target_link_libraries(${dest_target} INTERFACE ${source_target_de_alias})
- endif()
- endforeach()
- endfunction()
- #! ly_delayed_target_link_libraries: internal function called by the root CMakeLists.txt after all targets
- # have been declared to determine if they are regularly
- # 1) If a MODULE is passed in the list of items, it will add the INTERFACE_INCLUDE_DIRECTORIES as include
- # directories of TARGET. It will also add the "INTERFACE_LINK_LIBRARIES" to TARGET. MODULEs cannot be
- # directly linked, but we can include the public headers and link against the things the MODULE expose
- # to link.
- # 2) If a target that has not yet been declared is passed, then it will defer it to after all targets are
- # declared. This way we can do a check again. We could delay the link to when
- # target is declared. This is needed for (1) since we dont know the type of target. This also addresses
- # another issue with target_link_libraries where it will only validate that a MODULE is not being passed
- # if the target is already declared, if not, it will fail later at linking time.
- function(ly_delayed_target_link_libraries)
- set(visibilities PRIVATE PUBLIC INTERFACE)
- get_property(additional_module_paths GLOBAL PROPERTY LY_ADDITIONAL_MODULE_PATH)
- list(APPEND CMAKE_MODULE_PATH ${additional_module_paths})
- get_property(delayed_targets GLOBAL PROPERTY LY_DELAYED_LINK_TARGETS)
- foreach(target ${delayed_targets})
- get_property(delayed_link TARGET ${target} PROPERTY LY_DELAYED_LINK)
- if(delayed_link)
- cmake_parse_arguments(ly_delayed_target_link_libraries "" "" "${visibilities}" ${delayed_link})
- foreach(visibility ${visibilities})
- foreach(alias_item ${ly_delayed_target_link_libraries_${visibility}})
- if(TARGET ${alias_item})
- get_target_property(item_type ${alias_item} TYPE)
- ly_de_alias_target(${alias_item} item)
- else()
- unset(item_type)
- set(item ${alias_item})
- endif()
- if(item_type STREQUAL MODULE_LIBRARY)
- target_include_directories(${target} ${visibility} $<GENEX_EVAL:$<TARGET_PROPERTY:${item},INTERFACE_INCLUDE_DIRECTORIES>>)
- target_link_libraries(${target} ${visibility} $<GENEX_EVAL:$<TARGET_PROPERTY:${item},INTERFACE_LINK_LIBRARIES>>)
- target_compile_definitions(${target} ${visibility} $<GENEX_EVAL:$<TARGET_PROPERTY:${item},INTERFACE_COMPILE_DEFINITIONS>>)
- target_compile_options(${target} ${visibility} $<GENEX_EVAL:$<TARGET_PROPERTY:${item},INTERFACE_COMPILE_OPTIONS>>)
- else()
- ly_parse_third_party_dependencies(${item})
- target_link_libraries(${target} ${visibility} ${item})
- endif()
- endforeach()
- endforeach()
- endif()
- endforeach()
- set_property(GLOBAL PROPERTY LY_DELAYED_LINK_TARGETS)
- endfunction()
- #! ly_parse_third_party_dependencies: Validates any 3rdParty library dependencies through the find_package command
- #
- # \arg:ly_THIRD_PARTY_LIBRARIES name of the target libraries to validate existance of through the find_package command.
- #
- function(ly_parse_third_party_dependencies ly_THIRD_PARTY_LIBRARIES)
- # Support for deprecated LY_VERSION_ENGINE_NAME used in 3p-package-source and misc 3p packages
- set(LY_VERSION_ENGINE_NAME ${O3DE_ENGINE_NAME})
- # Interface dependencies may require to find_packages. So far, we are just using packages for 3rdParty, so we will
- # search for those and automatically bring those packages. The naming convention used is 3rdParty::PackageName::OptionalInterface
- unset(all_thirdparty_dependencies_found)
- foreach(dependency ${ly_THIRD_PARTY_LIBRARIES})
- string(REPLACE "::" ";" dependency_list ${dependency})
- list(GET dependency_list 0 dependency_namespace)
- if(${dependency_namespace} STREQUAL "3rdParty")
- list(APPEND all_thirdparty_dependencies_found ${dependency})
- if (NOT TARGET ${dependency})
- if (O3DE_SCRIPT_ONLY)
- # we don't actually need 3p deps to try to download the package or call find_package.
- # instead we use pre-created part-of-the-installer 3p targets baked in from the above list
- # which will have been made at install time.
- # instead, we execute a pregenerated file that was created as part of install to
- # create this target:
- string(REPLACE "::" "__" CLEAN_TARGET_NAME "${dependency}")
- # not all 3ps actually exist as real targets, so this is an OPTIONAL include.
- include(${LY_ROOT_FOLDER}/cmake/3rdParty/Platform/${PAL_PLATFORM_NAME}/Default/${CLEAN_TARGET_NAME}.cmake OPTIONAL)
- else()
- list(GET dependency_list 1 dependency_package)
- list(LENGTH dependency_list dependency_list_length)
- ly_download_associated_package(${dependency_package})
- if (dependency_list_length GREATER 2)
- # There's an optional interface specified
- list(GET dependency_list 2 component)
- list(APPEND packages_with_components ${dependency_package})
- list(APPEND ${dependency_package}_components ${component})
- else()
- find_package(${dependency_package} REQUIRED MODULE)
- endif()
- endif()
- endif()
- endif()
- endforeach()
- foreach(dependency IN LISTS packages_with_components)
- find_package(${dependency} REQUIRED MODULE COMPONENTS ${${dependency}_components})
- endforeach()
-
- foreach(dependency ${all_thirdparty_dependencies_found})
- if (TARGET ${dependency})
- # keep track of all the 3p dependencies we actually depended on.
- get_property(o3de_all_3rdparty_targets GLOBAL PROPERTY O3DE_ALL_3RDPARTY_TARGETS)
- if(NOT ${dependency} IN_LIST o3de_all_3rdparty_targets)
- set_property(GLOBAL APPEND PROPERTY O3DE_ALL_3RDPARTY_TARGETS "${dependency}")
- endif()
- endif()
- endforeach()
- endfunction()
- #! ly_configure_target_platform_properties: Configures any platform specific properties on target
- #
- # Looks at the the following variables within the platform include file to set the equivalent target properties
- # LY_FILES_CMAKE -> extract list of files -> target_sources
- # LY_FILES -> target_source
- # LY_INCLUDE_DIRECTORIES -> target_include_directories
- # LY_COMPILE_DEFINITIONS -> target_compile_definitions
- # LY_COMPILE_OPTIONS -> target_compile_options
- # LY_LINK_OPTIONS -> target_link_options
- # LY_BUILD_DEPENDENCIES -> target_link_libraries
- # LY_RUNTIME_DEPENDENCIES -> ly_add_dependencies
- # LY_TARGET_PROPERTIES -> target_properties
- #
- macro(ly_configure_target_platform_properties)
- foreach(platform_include_file ${ly_add_target_PLATFORM_INCLUDE_FILES})
- set(LY_FILES_CMAKE)
- set(LY_FILES)
- set(LY_INCLUDE_DIRECTORIES)
- set(LY_COMPILE_DEFINITIONS)
- set(LY_COMPILE_OPTIONS)
- set(LY_LINK_OPTIONS)
- set(LY_BUILD_DEPENDENCIES)
- set(LY_RUNTIME_DEPENDENCIES)
- set(LY_TARGET_PROPERTIES)
- include(${platform_include_file} RESULT_VARIABLE ly_platform_cmake_file)
- if(NOT ly_platform_cmake_file)
- message(FATAL_ERROR "The supplied PLATFORM_INCLUDE_FILE(${platform_include_file}) cannot be included.\
- Parsing of target will halt")
- endif()
- if(ly_add_target_INTERFACE OR ly_add_target_IMPORTED)
- target_sources(${ly_add_target_NAME} INTERFACE ${platform_include_file})
- else()
- target_sources(${ly_add_target_NAME} PRIVATE ${platform_include_file})
- endif()
- ly_source_groups_from_folders("${platform_include_file}")
- if(LY_FILES_CMAKE)
- foreach(file_cmake ${LY_FILES_CMAKE})
- ly_include_cmake_file_list(${file_cmake})
- endforeach()
- target_sources(${ly_add_target_NAME} PRIVATE ${ALLFILES})
- ly_source_groups_from_folders("${ALLFILES}")
- endif()
- if(LY_FILES)
- target_sources(${ly_add_target_NAME} PRIVATE ${LY_FILES})
- endif()
- if (LY_INCLUDE_DIRECTORIES)
- target_include_directories(${ly_add_target_NAME} ${LY_INCLUDE_DIRECTORIES})
- endif()
- if(LY_COMPILE_DEFINITIONS)
- target_compile_definitions(${ly_add_target_NAME} ${LY_COMPILE_DEFINITIONS})
- endif()
- if(LY_COMPILE_OPTIONS)
- target_compile_options(${ly_add_target_NAME} ${LY_COMPILE_OPTIONS})
- endif()
- if(LY_LINK_OPTIONS)
- target_link_options(${ly_add_target_NAME} ${LY_LINK_OPTIONS})
- endif()
- if(LY_BUILD_DEPENDENCIES)
- ly_target_link_libraries(${ly_add_target_NAME} ${LY_BUILD_DEPENDENCIES})
- endif()
- if(LY_RUNTIME_DEPENDENCIES)
- ly_add_dependencies(${ly_add_target_NAME} ${LY_RUNTIME_DEPENDENCIES})
- endif()
- if(LY_TARGET_PROPERTIES)
- set_target_properties(${ly_add_target_NAME} PROPERTIES ${LY_TARGET_PROPERTIES})
- endif()
- endforeach()
- endmacro()
- #! ly_add_target_files: adds files to a target. This will copy the specified files to where the target is.
- #
- # \arg:TARGETS name of the targets that depends on this file
- # \arg:FILES files to copy
- # \arg:OUTPUT_SUBDIRECTORY (OPTIONAL) where to place the files relative to TARGET_NAME's output dir.
- # If not specified, they are located in the same folder as TARGET_NAME
- #
- function(ly_add_target_files)
- set(options)
- set(oneValueArgs OUTPUT_SUBDIRECTORY)
- set(multiValueArgs TARGETS FILES)
- cmake_parse_arguments(ly_add_target_files "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
- # Validate input arguments
- if(NOT ly_add_target_files_TARGETS)
- message(FATAL_ERROR "You must provide at least one target")
- endif()
- if(NOT ly_add_target_files_FILES)
- message(FATAL_ERROR "You must provide at least a file to copy")
- endif()
- foreach(target ${ly_add_target_files_TARGETS})
- foreach(file ${ly_add_target_files_FILES})
- set_property(TARGET ${target} APPEND PROPERTY INTERFACE_LY_TARGET_FILES "${file}\n${ly_add_target_files_OUTPUT_SUBDIRECTORY}")
- endforeach()
- endforeach()
- endfunction()
- #! ly_add_source_properties: adds/appends properties to a source file.
- #
- # This wraps set_source_files_properties to be able to pass a property with multiple values
- # and get it appended instead of set.
- #
- # \arg:SOURCES list of sources to apply this property on
- # \arg:PROPERTY property to set
- # \arg:VALUES values to append
- #
- function(ly_add_source_properties)
- set(options)
- set(oneValueArgs PROPERTY)
- set(multiValueArgs SOURCES VALUES)
- cmake_parse_arguments(ly_add_source_properties "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
- # Validate input arguments
- if(NOT ly_add_source_properties_SOURCES)
- message(FATAL_ERROR "You must provide at least one source")
- endif()
- if(NOT ly_add_source_properties_PROPERTY)
- message(FATAL_ERROR "You must provide a property")
- endif()
- foreach(file ${ly_add_source_properties_SOURCES})
- if(NOT IS_ABSOLUTE ${file})
- get_filename_component(file ${file} ABSOLUTE)
- endif()
- if(NOT EXISTS ${file})
- message(SEND_ERROR "File ${file} not found when setting property ${ly_add_source_properties_PROPERTY}")
- endif()
- endforeach()
- # We allow to pass empty values because in some cases we expand the values based on conditions
- # If the values are empty then this call does nothing
- if(ly_add_source_properties_VALUES)
- set_property(
- SOURCE ${ly_add_source_properties_SOURCES}
- APPEND PROPERTY ${ly_add_source_properties_PROPERTY} ${ly_add_source_properties_VALUES}
- )
- endif()
- endfunction()
- # given a target name, returns the "real" name of the target if its an alias.
- # this function recursively de-aliases
- function(ly_de_alias_target target_name output_variable_name)
- # its not okay to call get_target_property on a non-existent target
- if (NOT TARGET ${target_name})
- message(FATAL_ERROR "ly_de_alias_target called on non-existent target: ${target_name}")
- endif()
- while(target_name)
- set(de_aliased_target_name ${target_name})
- get_target_property(target_name ${target_name} ALIASED_TARGET)
- endwhile()
- if(NOT de_aliased_target_name)
- message(FATAL_ERROR "Empty de_aliased for ${target_name}")
- endif()
- set(${output_variable_name} ${de_aliased_target_name} PARENT_SCOPE)
- endfunction()
- #! ly_get_vs_folder_directory: Sets the Visual Studio folder name used for organizing vcxproj
- # in the IDE
- #
- # Visual Studio cannot load projects that with a ".." relative path or contain a colon ":" as part of its FOLDER
- # Therefore if the .vcxproj is absolute, the drive letter must be removed from the folder name
- #
- # What this method does is first check if the target being added to the Visual Studio solution is within
- # the LY_ROOT_FOLDER(i.e is the LY_ROOT_FOLDER a prefix of the target source directory)
- # If it is a relative path to the target is used as the folder name
- # Otherwise the target directory would either
- # 1. Be a path outside of the LY_ROOT_FOLDER on the same drive.
- # In that case forming a relative path would cause it to start with ".." which will not work
- # 2. Be an path outside of the LY_ROOT_FOLDER on a different drive
- # Here a relative path cannot be formed and therefore the path would start with "<drive>:/Path/To/Project"
- # Which shows up as unloaded due to containing a colon
- # In this scenario the relative part of the path from the drive letter is used as the FOLDER name
- # to make sure the projects show up in the loaded .sln
- function(ly_get_vs_folder_directory absolute_target_source_dir output_source_dir)
- # Get a relative directory to the LY_ROOT_FOLDER if possible for the Visual Studio solution hierarchy
- # If a relative path cannot be formed, then retrieve a path with the drive letter stripped from it
- cmake_path(IS_PREFIX LY_ROOT_FOLDER ${absolute_target_source_dir} is_target_prefix_of_engine_root)
- if(is_target_prefix_of_engine_root)
- cmake_path(RELATIVE_PATH absolute_target_source_dir BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE relative_target_source_dir)
- else()
- cmake_path(IS_PREFIX CMAKE_SOURCE_DIR ${absolute_target_source_dir} is_target_prefix_of_source_dir)
- if(is_target_prefix_of_source_dir)
- cmake_path(RELATIVE_PATH absolute_target_source_dir BASE_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE relative_target_source_dir)
- else()
- cmake_path(GET absolute_target_source_dir RELATIVE_PART relative_target_source_dir)
- endif()
- endif()
- set(${output_source_dir} ${relative_target_source_dir} PARENT_SCOPE)
- endfunction()
|