Files
o3de/cmake/3rdParty.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

332 lines
16 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
#
#
# Do not overcomplicate searching for the 3rdParty path, if it is not easy to find,
# the user should define it.
#! get_default_third_party_folder: Stores the default 3rdParty directory into the supplied output variable
#
# \arg:output_third_party_path name of variable to set the default project directory into
# It defaults to the ~/.o3de/3rdParty directory
function(get_default_third_party_folder output_third_party_path)
cmake_path(SET home_directory "$ENV{USERPROFILE}") # Windows
if(NOT EXISTS ${home_directory})
cmake_path(SET home_directory "$ENV{HOME}") # Unix
if (NOT EXISTS ${home_directory})
return()
endif()
endif()
set(${output_third_party_path} ${home_directory}/.o3de/3rdParty PARENT_SCOPE)
endfunction()
get_default_third_party_folder(o3de_default_third_party_path)
set(LY_3RDPARTY_PATH "${o3de_default_third_party_path}" CACHE PATH "Path to the 3rdParty folder")
if(LY_3RDPARTY_PATH)
file(TO_CMAKE_PATH ${LY_3RDPARTY_PATH} LY_3RDPARTY_PATH)
endif()
if(NOT EXISTS ${LY_3RDPARTY_PATH})
message(FATAL_ERROR "3rdParty folder: ${LY_3RDPARTY_PATH} does not exist, call cmake defining a valid LY_3RDPARTY_PATH or use cmake-gui to configure it")
endif()
#! ly_add_external_target_path: adds a path to module path so 3rdparty Find files can be added from paths different than cmake/3rdParty
#
# \arg:PATH path to add
#
function(ly_add_external_target_path PATH)
list(APPEND CMAKE_MODULE_PATH ${PATH})
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} PARENT_SCOPE)
set_property(GLOBAL APPEND PROPERTY LY_ADDITIONAL_MODULE_PATH ${PATH})
endfunction()
#! ly_add_external_target: adds a library interface that exposes external libraries to be used as cmake dependencies.
#
# \arg:NAME name of the external library
# \arg:VERSION version of the external library. Location will be defined by 3rdPartyPath/NAME/VERSION
# \arg:3RDPARTY_DIRECTORY overrides the path to use when searching in the 3rdPartyPath (instead of NAME).
# If not indicated, PACKAGE will be used, if not indicated, NAME will be used.
# \arg:3RDPARTY_ROOT_DIRECTORY overrides the root path to the external library directory. This will be used instead of ${LY_3RDPARTY_PATH}.
# \arg:INCLUDE_DIRECTORIES include folders (relative to the root path where the external library is: ${LY_3RDPARTY_PATH}/${NAME}/${VERSION})
# \arg:PACKAGE if defined, defines the name of the external library "package". This is used when a package exposes multiple interfaces
# if not defined, NAME is used
# \arg:COMPILE_DEFINITIONS compile definitions to be added to the interface
# \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 files this target depends on (could be a dynamic libraries, text files, executables,
# applications, other 3rdParty targets, etc)
# \arg:OUTPUT_SUBDIRECTORY Subdirectory within bin/<Profile/Debug>/ where the ${PACKAGE_AND_NAME}_RUNTIME_DEPENDENCIES exported by the target will be copied to.
# If not specified, then the ${PACKAGE_AND_NAME}_RUNTIME_DEPENDENCIES will be copied directly under bin/<Profile/Debug>/.
# Each file listed in runtime dependencies can also customize its own output subfolder
# by adding "\n<subfolder path>" at the end of each listed file. OUTPUT_SUBDIRECTORY only works
# if such customized subfolder per file is NOT specified.
# Examples:
# 1- If there are 5 files listed in ${PACKAGE_AND_NAME}_RUNTIME_DEPENDENCIES, and all of them
# should be copied to the same output subfolder named "My/Output/Subfolder" then it is advisable
# to set OUTPUT_SUBDIRECTORY to "My/Output/Subfolder" instead of appending: "\nMy/Output/Subfolder"
# at the end of each listed file.
# 2- Assume there are 2 files listed in ${PACKAGE_AND_NAME}_RUNTIME_DEPENDENCIES, "fileA" must go to
# subdirectory "My/Output/Subfolder/lib" and "fileB" must go to subdirectory "My/Output/Subfolder/bin".
# In this case OUTPUT_SUBDIRECTORY should NOT be used, instead the files can be listed as:
# "fileA\nMy/Output/Subfolder/lib"
# "fileB\nMy/Output/Subfolder/bin"
#
function(ly_add_external_target)
set(options)
set(oneValueArgs NAME VERSION 3RDPARTY_DIRECTORY PACKAGE 3RDPARTY_ROOT_DIRECTORY OUTPUT_SUBDIRECTORY)
set(multiValueArgs HEADER_CHECK COMPILE_DEFINITIONS INCLUDE_DIRECTORIES BUILD_DEPENDENCIES RUNTIME_DEPENDENCIES)
cmake_parse_arguments(ly_add_external_target "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# Validate input arguments
if(NOT ly_add_external_target_NAME)
message(FATAL_ERROR "You must provide a name for the 3rd party library")
endif()
if(NOT ly_add_external_target_PACKAGE)
set(ly_add_external_target_PACKAGE ${ly_add_external_target_NAME})
set(PACKAGE_AND_NAME "${ly_add_external_target_NAME}")
set(NAME_WITH_NAMESPACE "${ly_add_external_target_NAME}")
set(has_package FALSE)
else()
set(PACKAGE_AND_NAME "${ly_add_external_target_PACKAGE}_${ly_add_external_target_NAME}")
set(NAME_WITH_NAMESPACE "${ly_add_external_target_PACKAGE}::${ly_add_external_target_NAME}")
set(has_package TRUE)
endif()
string(TOUPPER ${PACKAGE_AND_NAME} PACKAGE_AND_NAME)
string(TOUPPER ${ly_add_external_target_PACKAGE} PACKAGE)
if(NOT TARGET 3rdParty::${NAME_WITH_NAMESPACE})
if(NOT DEFINED ly_add_external_target_VERSION AND NOT VERSION IN_LIST ly_add_external_target_KEYWORDS_MISSING_VALUES)
message(FATAL_ERROR "You must provide a version of the \"${ly_add_external_target_PACKAGE}\" 3rd party library")
endif()
if(NOT ly_add_external_target_3RDPARTY_ROOT_DIRECTORY)
if(NOT ly_add_external_target_3RDPARTY_DIRECTORY)
if(ly_add_external_target_PACKAGE)
set(ly_add_external_target_3RDPARTY_DIRECTORY ${ly_add_external_target_PACKAGE})
else()
set(ly_add_external_target_3RDPARTY_DIRECTORY ${ly_add_external_target_NAME})
endif()
endif()
set(BASE_PATH "${LY_3RDPARTY_PATH}/${ly_add_external_target_3RDPARTY_DIRECTORY}")
else()
ly_install_external_target(${ly_add_external_target_3RDPARTY_ROOT_DIRECTORY})
set(BASE_PATH "${ly_add_external_target_3RDPARTY_ROOT_DIRECTORY}")
endif()
if(ly_add_external_target_VERSION)
set(BASE_PATH "${BASE_PATH}/${ly_add_external_target_VERSION}")
endif()
# Setting BASE_PATH variable in the parent scope to allow for the Find<3rdParty>.cmake scripts to use them
set(BASE_PATH ${BASE_PATH} PARENT_SCOPE)
add_library(3rdParty::${NAME_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL)
if(ly_add_external_target_INCLUDE_DIRECTORIES)
list(TRANSFORM ly_add_external_target_INCLUDE_DIRECTORIES PREPEND ${BASE_PATH}/)
foreach(include_path ${ly_add_external_target_INCLUDE_DIRECTORIES})
if(NOT EXISTS ${include_path})
message(FATAL_ERROR "Cannot find include path ${include_path} for 3rdParty::${NAME_WITH_NAMESPACE}")
endif()
endforeach()
ly_target_include_system_directories(TARGET 3rdParty::${NAME_WITH_NAMESPACE}
INTERFACE ${ly_add_external_target_INCLUDE_DIRECTORIES}
)
endif()
# Check if there is a pal file
ly_get_absolute_pal_filename(pal_file ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME}/${ly_add_external_target_PACKAGE}_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
if(NOT EXISTS ${pal_file})
set(pal_file ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME}/${ly_add_external_target_PACKAGE}_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
endif()
if(EXISTS ${pal_file})
include(${pal_file})
endif()
if(${PACKAGE_AND_NAME}_INCLUDE_DIRECTORIES)
list(TRANSFORM ${PACKAGE_AND_NAME}_INCLUDE_DIRECTORIES PREPEND ${BASE_PATH}/)
foreach(include_path ${${PACKAGE_AND_NAME}_INCLUDE_DIRECTORIES})
string(GENEX_STRIP ${include_path} include_genex_expr)
if(include_genex_expr STREQUAL include_path AND NOT EXISTS ${include_path}) # Exclude include paths that have generation expressions from validation
message(FATAL_ERROR "Cannot find include path ${include_path} for 3rdParty::${NAME_WITH_NAMESPACE}")
endif()
endforeach()
ly_target_include_system_directories(TARGET 3rdParty::${NAME_WITH_NAMESPACE}
INTERFACE ${${PACKAGE_AND_NAME}_INCLUDE_DIRECTORIES}
)
endif()
if(has_package AND ${PACKAGE}_LIBS)
set_property(TARGET 3rdParty::${NAME_WITH_NAMESPACE}
APPEND PROPERTY
INTERFACE_LINK_LIBRARIES "${${PACKAGE}_LIBS}"
)
endif()
if(${PACKAGE_AND_NAME}_LIBS)
set_property(TARGET 3rdParty::${NAME_WITH_NAMESPACE}
APPEND PROPERTY
INTERFACE_LINK_LIBRARIES "${${PACKAGE_AND_NAME}_LIBS}"
)
endif()
if(has_package AND ${PACKAGE}_LINK_OPTIONS)
set_property(TARGET 3rdParty::${NAME_WITH_NAMESPACE}
APPEND PROPERTY
INTERFACE_LINK_OPTIONS "${${PACKAGE}_LINK_OPTIONS}"
)
endif()
if(${PACKAGE_AND_NAME}_LINK_OPTIONS)
set_property(TARGET 3rdParty::${NAME_WITH_NAMESPACE}
APPEND PROPERTY
INTERFACE_LINK_OPTIONS "${${PACKAGE_AND_NAME}_LINK_OPTIONS}"
)
endif()
if(has_package AND ${PACKAGE}_COMPILE_OPTIONS)
set_property(TARGET 3rdParty::${NAME_WITH_NAMESPACE}
APPEND PROPERTY
INTERFACE_COMPILE_OPTIONS "${${PACKAGE}_COMPILE_OPTIONS}"
)
endif()
if(${PACKAGE_AND_NAME}_COMPILE_OPTIONS)
set_property(TARGET 3rdParty::${NAME_WITH_NAMESPACE}
APPEND PROPERTY
INTERFACE_COMPILE_OPTIONS "${${PACKAGE_AND_NAME}_COMPILE_OPTIONS}"
)
endif()
unset(all_dependencies)
if(ly_add_external_target_RUNTIME_DEPENDENCIES)
list(APPEND all_dependencies ${ly_add_external_target_RUNTIME_DEPENDENCIES})
endif()
if(has_package AND ${PACKAGE}_RUNTIME_DEPENDENCIES)
list(APPEND all_dependencies ${${PACKAGE}_RUNTIME_DEPENDENCIES})
endif()
if(${PACKAGE_AND_NAME}_RUNTIME_DEPENDENCIES)
list(APPEND all_dependencies ${${PACKAGE_AND_NAME}_RUNTIME_DEPENDENCIES})
endif()
unset(locations)
unset(manual_dependencies)
if(all_dependencies)
foreach(dependency ${all_dependencies})
if(dependency MATCHES "3rdParty::")
list(APPEND manual_dependencies ${dependency})
else()
if(ly_add_external_target_OUTPUT_SUBDIRECTORY)
string(APPEND dependency "\n${ly_add_external_target_OUTPUT_SUBDIRECTORY}")
endif()
list(APPEND locations ${dependency})
endif()
endforeach()
endif()
if(locations)
set_property(TARGET 3rdParty::${NAME_WITH_NAMESPACE}
APPEND PROPERTY
INTERFACE_IMPORTED_LOCATION "${locations}"
)
endif()
if(manual_dependencies)
ly_add_dependencies(3rdParty::${NAME_WITH_NAMESPACE} ${manual_dependencies})
endif()
get_property(additional_dependencies GLOBAL PROPERTY LY_DELAYED_DEPENDENCIES_3rdParty::${NAME_WITH_NAMESPACE})
if(additional_dependencies)
ly_add_dependencies(3rdParty::${NAME_WITH_NAMESPACE} ${additional_dependencies})
# Clear the variable so we can track issues in case some dependency is added after
set_property(GLOBAL PROPERTY LY_DELAYED_DEPENDENCIES_3rdParty::${NAME_WITH_NAMESPACE})
endif()
if(ly_add_external_target_COMPILE_DEFINITIONS)
target_compile_definitions(3rdParty::${NAME_WITH_NAMESPACE}
INTERFACE ${ly_add_external_target_COMPILE_DEFINITIONS}
)
endif()
if(has_package AND ${PACKAGE}_COMPILE_DEFINITIONS)
set_property(TARGET 3rdParty::${NAME_WITH_NAMESPACE}
APPEND PROPERTY
INTERFACE_COMPILE_DEFINITIONS "${${PACKAGE}_COMPILE_DEFINITIONS}"
)
endif()
if(${PACKAGE_AND_NAME}_COMPILE_DEFINITIONS)
set_property(TARGET 3rdParty::${NAME_WITH_NAMESPACE}
APPEND PROPERTY
INTERFACE_COMPILE_DEFINITIONS "${${PACKAGE_AND_NAME}_COMPILE_DEFINITIONS}"
)
endif()
if(has_package AND ${PACKAGE}_BUILD_DEPENDENCIES)
list(APPEND ly_add_external_target_BUILD_DEPENDENCIES "${${PACKAGE}_BUILD_DEPENDENCIES}")
list(REMOVE_DUPLICATES ly_add_external_target_BUILD_DEPENDENCIES)
endif()
if(${PACKAGE_AND_NAME}_BUILD_DEPENDENCIES)
list(APPEND ly_add_external_target_BUILD_DEPENDENCIES "${${PACKAGE_AND_NAME}_BUILD_DEPENDENCIES}")
list(REMOVE_DUPLICATES ly_add_external_target_BUILD_DEPENDENCIES)
endif()
# 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
foreach(dependency ${ly_add_external_target_BUILD_DEPENDENCIES})
string(REPLACE "::" ";" dependency_list ${dependency})
list(GET dependency_list 0 dependency_namespace)
if(${dependency_namespace} STREQUAL "3rdParty")
list(GET dependency_list 1 dependency_package)
ly_download_associated_package(${dependency_package})
find_package(${dependency_package} REQUIRED MODULE)
endif()
endforeach()
if(ly_add_external_target_BUILD_DEPENDENCIES)
target_link_libraries(3rdParty::${NAME_WITH_NAMESPACE}
INTERFACE
${ly_add_external_target_BUILD_DEPENDENCIES}
)
endif()
endif()
endfunction()
#! ly_install_external_target: external libraries which are not part of 3rdParty need to be installed
#
# \arg:3RDPARTY_ROOT_DIRECTORY custom 3rd party directory which needs to be installed
function(ly_install_external_target 3RDPARTY_ROOT_DIRECTORY)
# Install the Find file to our <install_location>/cmake directory
install(FILES ${CMAKE_CURRENT_LIST_FILE}
DESTINATION cmake
)
# We only want to install external targets that are part of our source tree
# Checking for relative path beginning with "../" also works when the path
# given is on another drive letter on windows(i.e., RELATIVE_PATH returns an absolute path)
file(RELATIVE_PATH rel_path ${CMAKE_SOURCE_DIR} ${3RDPARTY_ROOT_DIRECTORY})
if (NOT ${rel_path} MATCHES "^../")
get_filename_component(rel_path ${rel_path} DIRECTORY)
install(DIRECTORY ${3RDPARTY_ROOT_DIRECTORY}
DESTINATION ${rel_path}
)
endif()
endfunction()
# Add the 3rdParty folder to find the modules
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/3rdParty)
ly_get_absolute_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/3rdParty/Platform/${PAL_PLATFORM_NAME})
list(APPEND CMAKE_MODULE_PATH ${pal_dir})
if(NOT INSTALLED_ENGINE)
# Add the 3rdParty cmake files to the IDE
ly_include_cmake_file_list(cmake/3rdParty/cmake_files.cmake)
ly_get_absolute_pal_filename(pal_3rdparty_dir ${CMAKE_CURRENT_SOURCE_DIR}/cmake/3rdParty/Platform/${PAL_PLATFORM_NAME})
ly_include_cmake_file_list(${pal_3rdparty_dir}/cmake_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake)
endif()