Files
o3de/cmake/Platform/Common/Install_common.cmake
T
lumberyard-employee-dm 2478c60793 Fixed several o3de python package and install layout issues (#1714)
* Updated the CrashLog directory path to save to the project user
directory instead of the engine-root directory

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Removing the custom OUTPUT_NAME for the Multiplayer Gem Builder target

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Changed the default value for the LY_3RDPARTY_PATH cache variable to be
~/.o3de/3rdParty

This simplifies the first time user experience when running cmake

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Fixed Windows only issue where using creating a VS Solution for an O3DE project on a different drive resulted in an unloaded "<drive letter>:" entry appearing in the solution explorer

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Refactored install layout logic for External Subdirectories

Instead of performing a regular expression over the Gems/* directory, now the each external subdirectory including the project (external) directory is recursied over and scanned for any folders that aren't excluded.
By default those folders are the [Cc]ache, [Bb]uild and [Uu]ser directories

Afterwards the list files to copy over are then split into a directory list and a file list that is filtered by an include regex
Next the directory list is iterated over and the directories are copied to the install layout
Finally the file list is iterated and the list of files are also copied to the install layout

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Fixed the o3de.bat script changing the working directory before running the o3de.py script

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Fixed exception in engine-template.py script when the create-gem command
is invoked with the --template-name parameter that does not correspond
to a registered Template

Updated the create-project command to register the project with the
o3de_manifest.json and the engine with the project as the final step

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Fixed register.py over-registration of non-engine directories when then
--this-engine parameter is supplied.
All the projects, gems and templates inside of the default o3de_manifest
folder locations were being registered with the engine that was being
registered

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Fixed register.py register_project_path() method to return 0 when the
project path is successfully registered.

This issue was that the save_o3de_manifest method "return" value was
being checked and that method doesn't actually return a value

Added question mark to the engine_template.py to correct text around
notifying the user if the project was registered

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Added doc comment on the new cmke ly_get_vs_folder_directory function()

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Added clarifying comment to the ly_setup_others() command logic to copy over directories and files from any external subdirectories(Gems) that are registered with the engine at the time of install

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Fixed infinite recursion when trying to create a template with the
source directory used to seed the template

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
2021-07-02 00:22:12 -05:00

629 lines
30 KiB
CMake

#
# 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(CMAKE_INSTALL_MESSAGE NEVER) # Simplify messages to reduce output noise
ly_set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Core)
cmake_path(RELATIVE_PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE runtime_output_directory)
cmake_path(RELATIVE_PATH CMAKE_LIBRARY_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE library_output_directory)
# Anywhere CMAKE_INSTALL_PREFIX is used, it has to be escaped so it is baked into the cmake_install.cmake script instead
# of baking the path. This is needed so `cmake --install --prefix <someprefix>` works regardless of the CMAKE_INSTALL_PREFIX
# used to generate the solution.
# CMAKE_INSTALL_PREFIX is still used when building the INSTALL target
set(install_output_folder "\${CMAKE_INSTALL_PREFIX}/${runtime_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>")
function(ly_get_engine_relative_source_dir absolute_target_source_dir output_source_dir)
# Get a relative target source directory to the LY root folder if possible
# Otherwise use the final component name
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()
# In this case the target source directory is outside of the engine root of the target source directory and concatenate the first
# is used first 8 characters of the absolute path SHA256 hash to make a unique relative directory
# that can be used to install the generated CMakeLists.txt
# of a SHA256 hash
string(SHA256 target_source_hash ${absolute_target_source_dir})
string(SUBSTRING ${target_source_hash} 0 8 target_source_hash)
cmake_path(GET absolute_target_source_dir FILENAME target_source_dirname)
cmake_path(SET relative_target_source_dir "${target_source_dirname}-${target_source_hash}")
endif()
set(${output_source_dir} ${relative_target_source_dir} PARENT_SCOPE)
endfunction()
#! ly_setup_target: Setup the data needed to re-create the cmake target commands for a single target
function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_target_source_dir)
# De-alias target name
ly_de_alias_target(${ALIAS_TARGET_NAME} TARGET_NAME)
# Get the target source directory relative to the LY root folder
ly_get_engine_relative_source_dir(${absolute_target_source_dir} relative_target_source_dir)
# get the component ID. if the property isn't set for the target, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME
get_property(install_component TARGET ${TARGET_NAME} PROPERTY INSTALL_COMPONENT)
# All include directories marked PUBLIC or INTERFACE will be installed. We dont use PUBLIC_HEADER because in order to do that
# we need to set the PUBLIC_HEADER property of the target for all the headers we are exporting. After doing that, installing the
# headers end up in one folder instead of duplicating the folder structure of the public/interface include directory.
# Instead, we install them with install(DIRECTORY)
set(include_location "include")
get_target_property(include_directories ${TARGET_NAME} INTERFACE_INCLUDE_DIRECTORIES)
if (include_directories)
unset(public_headers)
foreach(include_directory ${include_directories})
string(GENEX_STRIP ${include_directory} include_genex_expr)
if(include_genex_expr STREQUAL include_directory) # only for cases where there are no generation expressions
unset(current_public_headers)
cmake_path(NORMAL_PATH include_directory)
string(REGEX REPLACE "/$" "" include_directory "${include_directory}")
cmake_path(IS_PREFIX LY_ROOT_FOLDER ${absolute_target_source_dir} NORMALIZE include_directory_child_of_o3de_root)
if(NOT include_directory_child_of_o3de_root)
# Include directory is outside of the O3DE root folder ${LY_ROOT_FOLDER}.
# For the INSTALL step, the O3DE root folder must be a prefix of all include directories.
continue()
endif()
cmake_path(RELATIVE_PATH include_directory BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE rel_include_dir)
cmake_path(APPEND include_location "${rel_include_dir}" ".." OUTPUT_VARIABLE destination_dir)
cmake_path(NORMAL_PATH destination_dir)
install(DIRECTORY ${include_directory}
DESTINATION ${destination_dir}
COMPONENT ${install_component}
FILES_MATCHING
PATTERN *.h
PATTERN *.hpp
PATTERN *.inl
)
endif()
endforeach()
endif()
# Get the output folders, archive is always the same, but runtime/library can be in subfolders defined per target
cmake_path(RELATIVE_PATH CMAKE_ARCHIVE_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE archive_output_directory)
get_target_property(target_runtime_output_directory ${TARGET_NAME} RUNTIME_OUTPUT_DIRECTORY)
if(target_runtime_output_directory)
cmake_path(RELATIVE_PATH target_runtime_output_directory BASE_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} OUTPUT_VARIABLE target_runtime_output_subdirectory)
endif()
get_target_property(target_library_output_directory ${TARGET_NAME} LIBRARY_OUTPUT_DIRECTORY)
if(target_library_output_directory)
cmake_path(RELATIVE_PATH target_library_output_directory BASE_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} OUTPUT_VARIABLE target_library_output_subdirectory)
endif()
install(
TARGETS ${TARGET_NAME}
ARCHIVE
DESTINATION ${archive_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>
COMPONENT ${install_component}
LIBRARY
DESTINATION ${library_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/${target_library_output_subdirectory}
COMPONENT ${install_component}
RUNTIME
DESTINATION ${runtime_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/${target_runtime_output_subdirectory}
COMPONENT ${install_component}
)
# CMakeLists.txt file
string(REGEX MATCH "(.*)::(.*)$" match ${ALIAS_TARGET_NAME})
if(match)
set(NAMESPACE_PLACEHOLDER "NAMESPACE ${CMAKE_MATCH_1}")
set(NAME_PLACEHOLDER ${CMAKE_MATCH_2})
else()
set(NAMESPACE_PLACEHOLDER "")
set(NAME_PLACEHOLDER ${TARGET_NAME})
endif()
set(TARGET_TYPE_PLACEHOLDER "")
get_target_property(target_type ${NAME_PLACEHOLDER} TYPE)
# Remove the _LIBRARY since we dont need to pass that to ly_add_targets
string(REPLACE "_LIBRARY" "" TARGET_TYPE_PLACEHOLDER ${target_type})
# For HEADER_ONLY libs we end up generating "INTERFACE" libraries, need to specify HEADERONLY instead
string(REPLACE "INTERFACE" "HEADERONLY" TARGET_TYPE_PLACEHOLDER ${TARGET_TYPE_PLACEHOLDER})
if(TARGET_TYPE_PLACEHOLDER STREQUAL "MODULE")
get_target_property(gem_module ${NAME_PLACEHOLDER} GEM_MODULE)
if(gem_module)
set(TARGET_TYPE_PLACEHOLDER "GEM_MODULE")
endif()
endif()
get_target_property(COMPILE_DEFINITIONS_PLACEHOLDER ${TARGET_NAME} INTERFACE_COMPILE_DEFINITIONS)
if(COMPILE_DEFINITIONS_PLACEHOLDER)
string(REPLACE ";" "\n" COMPILE_DEFINITIONS_PLACEHOLDER "${COMPILE_DEFINITIONS_PLACEHOLDER}")
else()
unset(COMPILE_DEFINITIONS_PLACEHOLDER)
endif()
# Includes need additional processing to add the install root
if(include_directories)
foreach(include ${include_directories})
string(GENEX_STRIP ${include} include_genex_expr)
if(include_genex_expr STREQUAL include) # only for cases where there are no generation expressions
cmake_path(RELATIVE_PATH include BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE target_include)
cmake_path(NORMAL_PATH target_include)
# Escape the LY_ROOT_FOLDER variable so that it isn't resolved during the install step
string(APPEND INCLUDE_DIRECTORIES_PLACEHOLDER "\${LY_ROOT_FOLDER}/${include_location}/${target_include}\n")
endif()
endforeach()
endif()
get_target_property(RUNTIME_DEPENDENCIES_PLACEHOLDER ${TARGET_NAME} MANUALLY_ADDED_DEPENDENCIES)
if(RUNTIME_DEPENDENCIES_PLACEHOLDER) # not found properties return the name of the variable with a "-NOTFOUND" at the end, here we set it to empty if not found
string(REPLACE ";" "\n" RUNTIME_DEPENDENCIES_PLACEHOLDER "${RUNTIME_DEPENDENCIES_PLACEHOLDER}")
else()
unset(RUNTIME_DEPENDENCIES_PLACEHOLDER)
endif()
get_target_property(inteface_build_dependencies_props ${TARGET_NAME} INTERFACE_LINK_LIBRARIES)
unset(INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER)
if(inteface_build_dependencies_props)
foreach(build_dependency ${inteface_build_dependencies_props})
# Skip wrapping produced when targets are not created in the same directory
if(NOT ${build_dependency} MATCHES "^::@")
list(APPEND INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "${build_dependency}")
endif()
endforeach()
endif()
# We also need to pass the private link libraries since we will use that to generate the runtime dependencies
get_target_property(private_build_dependencies_props ${TARGET_NAME} LINK_LIBRARIES)
if(private_build_dependencies_props)
foreach(build_dependency ${private_build_dependencies_props})
# Skip wrapping produced when targets are not created in the same directory
if(NOT ${build_dependency} MATCHES "^::@")
list(APPEND INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "${build_dependency}")
endif()
endforeach()
endif()
list(REMOVE_DUPLICATES INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER)
string(REPLACE ";" "\n" INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "${INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER}")
# Config file
set(target_file_contents "# Generated by O3DE install\n\n")
if(NOT target_type STREQUAL INTERFACE_LIBRARY)
unset(target_location)
set(runtime_types EXECUTABLE APPLICATION)
if(target_type IN_LIST runtime_types)
set(target_location "\${LY_ROOT_FOLDER}/${runtime_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/${target_runtime_output_subdirectory}/$<TARGET_FILE_NAME:${TARGET_NAME}>")
elseif(target_type STREQUAL MODULE_LIBRARY)
set(target_location "\${LY_ROOT_FOLDER}/${library_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/${target_library_output_subdirectory}/$<TARGET_FILE_NAME:${TARGET_NAME}>")
elseif(target_type STREQUAL SHARED_LIBRARY)
string(APPEND target_file_contents
"set_property(TARGET ${TARGET_NAME}
APPEND_STRING PROPERTY IMPORTED_IMPLIB
$<$<CONFIG:$<CONFIG>$<ANGLE-R>:\"\${LY_ROOT_FOLDER}/${archive_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/$<TARGET_LINKER_FILE_NAME:${TARGET_NAME}>\"$<ANGLE-R>
)
")
string(APPEND target_file_contents
"set_property(TARGET ${TARGET_NAME}
PROPERTY IMPORTED_IMPLIB_$<UPPER_CASE:$<CONFIG>>
\"\${LY_ROOT_FOLDER}/${archive_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/$<TARGET_LINKER_FILE_NAME:${TARGET_NAME}>\"
)
")
set(target_location "\${LY_ROOT_FOLDER}/${library_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/${target_library_output_subdirectory}/$<TARGET_FILE_NAME:${TARGET_NAME}>")
else() # STATIC_LIBRARY, OBJECT_LIBRARY, INTERFACE_LIBRARY
set(target_location "\${LY_ROOT_FOLDER}/${archive_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/$<TARGET_LINKER_FILE_NAME:${TARGET_NAME}>")
endif()
if(target_location)
string(APPEND target_file_contents
"set_property(TARGET ${TARGET_NAME}
APPEND_STRING PROPERTY IMPORTED_LOCATION
$<$<CONFIG:$<CONFIG>$<ANGLE-R>:${target_location}$<ANGLE-R>
)
set_property(TARGET ${TARGET_NAME}
PROPERTY IMPORTED_LOCATION_$<UPPER_CASE:$<CONFIG>>
${target_location}
)
")
endif()
endif()
set(target_install_source_dir ${CMAKE_CURRENT_BINARY_DIR}/install/${relative_target_source_dir})
file(GENERATE OUTPUT "${target_install_source_dir}/${NAME_PLACEHOLDER}_$<CONFIG>.cmake" CONTENT "${target_file_contents}")
install(FILES "${target_install_source_dir}/${NAME_PLACEHOLDER}_$<CONFIG>.cmake"
DESTINATION ${relative_target_source_dir}
COMPONENT ${install_component}
)
# Since a CMakeLists.txt could contain multiple targets, we generate it in a folder per target
ly_file_read(${LY_ROOT_FOLDER}/cmake/install/InstalledTarget.in target_cmakelists_template)
string(CONFIGURE ${target_cmakelists_template} output_cmakelists_data @ONLY)
set(${OUTPUT_CONFIGURED_TARGET} ${output_cmakelists_data} PARENT_SCOPE)
endfunction()
#! ly_setup_subdirectories: setups all targets on a per directory basis
function(ly_setup_subdirectories)
get_property(all_subdirectories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES)
foreach(target_subdirectory IN LISTS all_subdirectories)
ly_setup_subdirectory(${target_subdirectory})
endforeach()
endfunction()
#! ly_setup_subdirectory: setup all targets in the subdirectory
function(ly_setup_subdirectory absolute_target_source_dir)
# Get the target source directory relative to the LY roo folder
ly_get_engine_relative_source_dir(${absolute_target_source_dir} relative_target_source_dir)
# The builtin BUILDSYSTEM_TARGETS property isn't being used here as that returns the de-alised
# TARGET and we need the alias namespace for recreating the CMakeLists.txt in the install layout
get_property(ALIAS_TARGETS_NAME DIRECTORY ${absolute_target_source_dir} PROPERTY LY_DIRECTORY_TARGETS)
foreach(ALIAS_TARGET_NAME IN LISTS ALIAS_TARGETS_NAME)
ly_setup_target(configured_target ${ALIAS_TARGET_NAME} ${absolute_target_source_dir})
string(APPEND all_configured_targets "${configured_target}")
endforeach()
# Replicate the ly_create_alias() calls based on the SOURCE_DIR for each target that generates an installed CMakeLists.txt
string(JOIN "\n" create_alias_template
"if(NOT TARGET @ALIAS_NAME@)"
" ly_create_alias(NAME @ALIAS_NAME@ NAMESPACE @ALIAS_NAMESPACE@ TARGETS @ALIAS_TARGETS@)"
"endif()"
""
)
get_property(create_alias_commands_arg_list DIRECTORY ${absolute_target_source_dir} PROPERTY LY_CREATE_ALIAS_ARGUMENTS)
foreach(create_alias_single_command_arg_list ${create_alias_commands_arg_list})
# Split the ly_create_alias arguments back out based on commas
string(REPLACE "," ";" create_alias_single_command_arg_list "${create_alias_single_command_arg_list}")
list(POP_FRONT create_alias_single_command_arg_list ALIAS_NAME)
list(POP_FRONT create_alias_single_command_arg_list ALIAS_NAMESPACE)
# The rest of the list are the target dependencies
set(ALIAS_TARGETS ${create_alias_single_command_arg_list})
string(CONFIGURE "${create_alias_template}" create_alias_command @ONLY)
string(APPEND CREATE_ALIASES_PLACEHOLDER ${create_alias_command})
endforeach()
# Reproduce the ly_enable_gems() calls made in the the SOURCE_DIR for this target into the CMakeLists.txt that
# is about to be generated
set(enable_gems_template "ly_enable_gems(@enable_gem_PROJECT_NAME@ @enable_gem_GEMS@ @enable_gem_GEM_FILE@ @enable_gem_VARIANTS@ @enable_gem_TARGETS@)\n")
get_property(enable_gems_commands_arg_list DIRECTORY ${absolute_target_source_dir} PROPERTY LY_ENABLE_GEMS_ARGUMENTS)
foreach(enable_gems_single_command_arg_list ${enable_gems_commands_arg_list})
# Split the ly_enable_gems arguments back out based on commas
string(REPLACE "," ";" enable_gems_single_command_arg_list "${enable_gems_single_command_arg_list}")
foreach(enable_gem_arg_kw IN ITEMS PROJECT_NAME GEMS GEM_FILE VARIANTS TARGETS)
list(POP_FRONT enable_gems_single_command_arg_list enable_gem_${enable_gem_arg_kw})
if(enable_gem_${enable_gem_arg_kw})
# if the argument exist append to argument keyword to the front
string(PREPEND enable_gem_${enable_gem_arg_kw} "${enable_gem_arg_kw} ")
endif()
endforeach()
string(CONFIGURE "${enable_gems_template}" enable_gems_command @ONLY)
string(APPEND ENABLE_GEMS_PLACEHOLDER ${enable_gems_command})
endforeach()
ly_file_read(${LY_ROOT_FOLDER}/cmake/install/Copyright.in cmake_copyright_comment)
# Initialize the target install source directory to path underneath the current binary directory
set(target_install_source_dir ${CMAKE_CURRENT_BINARY_DIR}/install/${relative_target_source_dir})
# Write out all the aggregated ly_add_target function calls and the final ly_create_alias() calls to the target CMakeLists.txt
file(WRITE ${target_install_source_dir}/CMakeLists.txt
"${cmake_copyright_comment}"
"${all_configured_targets}"
"\n"
"${CREATE_ALIASES_PLACEHOLDER}"
"${ENABLE_GEMS_PLACEHOLDER}"
)
# get the component ID. if the property isn't set for the directory, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME
get_property(install_component DIRECTORY ${absolute_target_source_dir} PROPERTY INSTALL_COMPONENT)
install(FILES "${target_install_source_dir}/CMakeLists.txt"
DESTINATION ${relative_target_source_dir}
COMPONENT ${install_component}
)
endfunction()
#! ly_setup_o3de_install: orchestrates the installation of the different parts. This is the entry point from the root CMakeLists.txt
function(ly_setup_o3de_install)
ly_setup_subdirectories()
ly_setup_cmake_install()
ly_setup_target_generator()
ly_setup_runtime_dependencies()
ly_setup_others()
endfunction()
#! ly_setup_cmake_install: install the "cmake" folder
function(ly_setup_cmake_install)
install(DIRECTORY "${LY_ROOT_FOLDER}/cmake"
DESTINATION .
PATTERN "__pycache__" EXCLUDE
REGEX "Findo3de.cmake" EXCLUDE
REGEX "Platform\/.*\/BuiltInPackages_.*\.cmake" EXCLUDE
)
# Transform the LY_EXTERNAL_SUBDIRS list into a json array
set(indent " ")
foreach(external_subdir ${LY_EXTERNAL_SUBDIRS})
cmake_path(RELATIVE_PATH external_subdir BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE engine_rel_external_subdir)
list(APPEND relative_external_subdirs "\"${engine_rel_external_subdir}\"")
endforeach()
list(JOIN relative_external_subdirs ",\n${indent}" LY_INSTALL_EXTERNAL_SUBDIRS)
# Read the "templates" key from the source engine.json
o3de_read_json_array(engine_templates ${LY_ROOT_FOLDER}/engine.json "templates")
foreach(template_path ${engine_templates})
list(APPEND relative_templates "\"${template_path}\"")
endforeach()
list(JOIN relative_templates ",\n${indent}" LY_INSTALL_TEMPLATES)
configure_file(${LY_ROOT_FOLDER}/cmake/install/engine.json.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/engine.json @ONLY)
install(
FILES
"${LY_ROOT_FOLDER}/CMakeLists.txt"
"${CMAKE_CURRENT_BINARY_DIR}/cmake/engine.json"
DESTINATION .
)
# Collect all Find files that were added with ly_add_external_target_path
unset(additional_find_files)
get_property(additional_module_paths GLOBAL PROPERTY LY_ADDITIONAL_MODULE_PATH)
foreach(additional_module_path ${additional_module_paths})
unset(find_files)
file(GLOB find_files "${additional_module_path}/Find*.cmake")
list(APPEND additional_find_files "${find_files}")
endforeach()
install(FILES ${additional_find_files}
DESTINATION cmake/3rdParty
)
# Findo3de.cmake file: we generate a different Findo3de.camke file than the one we have in cmake. This one is going to expose all
# targets that are pre-built
unset(FIND_PACKAGES_PLACEHOLDER)
# Add to the FIND_PACKAGES_PLACEHOLDER all directories in which ly_add_target were called in
get_property(all_subdirectories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES)
foreach(target_subdirectory IN LISTS all_subdirectories)
cmake_path(RELATIVE_PATH target_subdirectory BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE relative_target_subdirectory)
string(APPEND FIND_PACKAGES_PLACEHOLDER " add_subdirectory(${relative_target_subdirectory})\n")
endforeach()
configure_file(${LY_ROOT_FOLDER}/cmake/install/Findo3de.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake"
DESTINATION cmake
)
# BuiltInPackage_<platform>.cmake: since associations could happen in any cmake file across the engine. We collect
# all the associations in ly_associate_package and then generate them into BuiltInPackages_<platform>.cmake. This
# will consolidate all associations in one file
get_property(all_package_names GLOBAL PROPERTY LY_PACKAGE_NAMES)
set(builtinpackages "# Generated by O3DE install\n\n")
foreach(package_name IN LISTS all_package_names)
get_property(package_hash GLOBAL PROPERTY LY_PACKAGE_HASH_${package_name})
get_property(targets GLOBAL PROPERTY LY_PACKAGE_TARGETS_${package_name})
string(APPEND builtinpackages "ly_associate_package(PACKAGE_NAME ${package_name} TARGETS ${targets} PACKAGE_HASH ${package_hash})\n")
endforeach()
set(pal_builtin_file ${CMAKE_CURRENT_BINARY_DIR}/cmake/3rdParty/Platform/${PAL_PLATFORM_NAME}/BuiltInPackages_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
file(GENERATE OUTPUT ${pal_builtin_file}
CONTENT ${builtinpackages}
)
install(FILES "${pal_builtin_file}"
DESTINATION cmake/3rdParty/Platform/${PAL_PLATFORM_NAME}
)
endfunction()
#! ly_setup_runtime_dependencies: install runtime dependencies
function(ly_setup_runtime_dependencies)
# Common functions used by the bellow code
install(CODE
"function(ly_copy source_file target_directory)
file(COPY \"\${source_file}\" DESTINATION \"\${target_directory}\" FILE_PERMISSIONS ${LY_COPY_PERMISSIONS})
endfunction()"
)
unset(runtime_commands)
get_property(all_targets GLOBAL PROPERTY LY_ALL_TARGETS)
foreach(alias_target IN LISTS all_targets)
ly_de_alias_target(${alias_target} target)
# Exclude targets that dont produce runtime outputs
get_target_property(target_type ${target} TYPE)
if(NOT target_type IN_LIST LY_TARGET_TYPES_WITH_RUNTIME_OUTPUTS)
continue()
endif()
get_target_property(target_runtime_output_directory ${target} RUNTIME_OUTPUT_DIRECTORY)
if(target_runtime_output_directory)
cmake_path(RELATIVE_PATH target_runtime_output_directory BASE_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} OUTPUT_VARIABLE target_runtime_output_subdirectory)
endif()
# runtime dependencies that need to be copied to the output
set(target_file_dir "${install_output_folder}/${target_runtime_output_subdirectory}")
ly_get_runtime_dependencies(runtime_dependencies ${target})
foreach(runtime_dependency ${runtime_dependencies})
unset(runtime_command)
ly_get_runtime_dependency_command(runtime_command ${runtime_dependency})
string(CONFIGURE "${runtime_command}" runtime_command @ONLY)
list(APPEND runtime_commands ${runtime_command})
endforeach()
endforeach()
list(REMOVE_DUPLICATES runtime_commands)
list(JOIN runtime_commands " " runtime_commands_str) # the spaces are just to see the right identation in the cmake_install.cmake file
install(CODE "${runtime_commands_str}"
)
endfunction()
#! ly_setup_others: install directories required by the engine
function(ly_setup_others)
# List of directories we want to install relative to engine root
set(DIRECTORIES_TO_INSTALL Tools/LyTestTools Tools/RemoteConsole)
foreach(dir ${DIRECTORIES_TO_INSTALL})
get_filename_component(install_path ${dir} DIRECTORY)
if (NOT install_path)
set(install_path .)
endif()
install(DIRECTORY "${LY_ROOT_FOLDER}/${dir}"
DESTINATION ${install_path}
PATTERN "__pycache__" EXCLUDE
)
endforeach()
# Scripts
file(GLOB o3de_scripts "${LY_ROOT_FOLDER}/scripts/o3de.*")
install(FILES
${o3de_scripts}
DESTINATION ./scripts
)
install(DIRECTORY
${LY_ROOT_FOLDER}/scripts/bundler
${LY_ROOT_FOLDER}/scripts/o3de
DESTINATION ./scripts
PATTERN "__pycache__" EXCLUDE
PATTERN "CMakeLists.txt" EXCLUDE
PATTERN "tests" EXCLUDE
)
install(DIRECTORY "${LY_ROOT_FOLDER}/python"
DESTINATION .
REGEX "downloaded_packages" EXCLUDE
REGEX "runtime" EXCLUDE
)
# Registry
install(DIRECTORY
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/Registry
DESTINATION ./${runtime_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>
)
install(DIRECTORY
${LY_ROOT_FOLDER}/Registry
DESTINATION .
)
# Engine Source Assets
install(DIRECTORY
${LY_ROOT_FOLDER}/Assets
DESTINATION .
)
# Gem Source Assets and configuration files
# Find all gem directories relative to the CMake Source Dir
# This first loop is to filter out transient and .gitignore'd folders that should be added to
# the install layout from the root directory. Such as <external-subdirectory-root>/Cache.
# This is also done to avoid globbing thousands of files in subdirectories that shouldn't
# be processed.
foreach(gem_candidate_dir IN LISTS LY_EXTERNAL_SUBDIRS LY_PROJECTS)
file(REAL_PATH ${gem_candidate_dir} gem_candidate_dir BASE_DIRECTORY ${LY_ROOT_FOLDER})
# Don't recurse immediately in order to exclude transient source artifacts
file(GLOB
external_subdir_files
LIST_DIRECTORIES TRUE
"${gem_candidate_dir}/*"
)
# Exclude transient artifacts that shouldn't be copied to the install layout
list(FILTER external_subdir_files EXCLUDE REGEX "/([Bb]uild|[Cc]ache|[Uu]ser)$")
list(APPEND filtered_asset_paths ${external_subdir_files})
endforeach()
# At this point the filtered_assets_paths contains the list of all directories and files
# that are non-excluded candidates that can be scanned for target directories and files
# to copy over to the install layout
foreach(filtered_asset_path IN LISTS filtered_asset_paths)
if(IS_DIRECTORY ${filtered_asset_path})
file(GLOB_RECURSE
recurse_assets_paths
LIST_DIRECTORIES TRUE
"${filtered_asset_path}/*"
)
set(gem_file_paths ${recurse_assets_paths})
# Make sure to prepend the current path iteration to the gem_dirs_path to filter
set(gem_dir_paths ${filtered_asset_path} ${recurse_assets_paths})
# Gather directories to copy over
# Currently only the Assets, Registry and Config directories are copied over
list(FILTER gem_dir_paths INCLUDE REGEX "/(Assets|Registry|Config)$")
list(APPEND gems_assets_dir_path ${gem_dir_paths})
else()
set(gem_file_paths ${filtered_asset_path})
endif()
# Gather files to copy over
# Currently only the gem.json file is copied over
list(FILTER gem_file_paths INCLUDE REGEX "/(gem.json)$")
list(APPEND gems_assets_file_path "${gem_file_paths}")
endforeach()
# gem directories to install
foreach(gem_absolute_dir_path ${gems_assets_dir_path})
cmake_path(RELATIVE_PATH gem_absolute_dir_path BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE gem_relative_dir_path)
if (EXISTS ${gem_absolute_dir_path})
# The trailing slash is IMPORTANT here as that is needed to prevent
# the "Assets" folder from being copied underneath the <gem-root>/Assets folder
install(DIRECTORY "${gem_absolute_dir_path}/"
DESTINATION ${gem_relative_dir_path}
)
endif()
endforeach()
# gem files to install
foreach(gem_absolute_file_path ${gems_assets_file_path})
cmake_path(RELATIVE_PATH gem_absolute_file_path BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE gem_relative_file_path)
cmake_path(GET gem_relative_file_path PARENT_PATH gem_relative_parent_dir)
install(FILES ${gem_absolute_file_path}
DESTINATION ${gem_relative_parent_dir}
)
endforeach()
# Templates
install(DIRECTORY
${LY_ROOT_FOLDER}/Templates
DESTINATION .
)
# Misc
install(FILES
${LY_ROOT_FOLDER}/ctest_pytest.ini
${LY_ROOT_FOLDER}/LICENSE.txt
${LY_ROOT_FOLDER}/README.md
DESTINATION .
)
endfunction()
#! ly_setup_target_generator: install source files needed for project launcher generation
function(ly_setup_target_generator)
install(FILES
${LY_ROOT_FOLDER}/Code/LauncherUnified/launcher_generator.cmake
${LY_ROOT_FOLDER}/Code/LauncherUnified/launcher_project_files.cmake
${LY_ROOT_FOLDER}/Code/LauncherUnified/LauncherProject.cpp
${LY_ROOT_FOLDER}/Code/LauncherUnified/StaticModules.in
DESTINATION LauncherGenerator
)
install(DIRECTORY ${LY_ROOT_FOLDER}/Code/LauncherUnified/Platform
DESTINATION LauncherGenerator
)
install(FILES ${LY_ROOT_FOLDER}/Code/LauncherUnified/FindLauncherGenerator.cmake
DESTINATION cmake
)
endfunction()