123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- #
- # 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
- #
- #
- cmake_minimum_required(VERSION 3.22)
- function(gp_resolve_item_override context item exepath dirs resolved_item_var resolved_var)
- # Qt frameworks could resolve the binary to eg qt/lib/QtCore.framework/Headers/QtCore instead of qt/lib/QtCore.framework/Versions/5/QtCore
- # This is because GetPrerequisites.cmake gp_resolve_item function searches for the first file that matches the "frameworks name"
- if(${${resolved_var}} AND ${item} MATCHES "/(Qt[^\\.]+\\.framework)/(.*)")
- set(qt_framework ${CMAKE_MATCH_1})
- set(qt_framework_subpath ${CMAKE_MATCH_2})
- string(REGEX REPLACE "(.*)/(Qt[^\\.]+\\.framework)/(.*)" "\\1/\\2/${qt_framework_subpath}" new_resolved_item "${${resolved_item_var}}")
- set(${resolved_item_var} ${new_resolved_item} PARENT_SCOPE)
- elseif(NOT ${resolved_var})
- cmake_path(GET item FILENAME source_filename)
- cmake_path(COMPARE "${source_filename}" EQUAL "libdxcompiler.3.7.dylib" is_libdirectx_shader_compiler)
- cmake_path(COMPARE "${source_filename}" EQUAL "Python" is_python)
- if(is_libdirectx_shader_compiler)
- unset(resolved_filepath)
- cmake_path(APPEND resolved_filepath "${target_file_dir}" "Builders/DirectXShaderCompiler/bin/dxc-3.7")
- set(${resolved_item_var} "${resolved_filepath}" PARENT_SCOPE)
- set(${resolved_var} TRUE PARENT_SCOPE)
- message(STATUS "Custom resolving file ${item} -> ${resolved_filepath}")
- elseif(is_python)
- unset(resolved_filepath)
- cmake_path(APPEND resolved_filepath "${target_bundle_framework_dir}" "Python.framework/Versions/3.10/Python")
- set(${resolved_item_var} "${resolved_filepath}" PARENT_SCOPE)
- set(${resolved_var} TRUE PARENT_SCOPE)
- message(STATUS "Custom resolving file ${item} -> ${resolved_filepath}")
- endif()
- endif()
- endfunction()
- include(BundleUtilities)
- cmake_policy(SET CMP0012 NEW) # new policy for the if that evaluates a boolean out of the LY_BUILD_FIXUP_BUNDLE expansion
- cmake_policy(SET CMP0009 NEW) # do not traverse symlinks on GLOB_RECURSE
- # Stores a set of executable, dylib and frameworks to ignore for fixup bundle
- set_property(SOURCE "${CMAKE_CURRENT_LIST_FILE}" PROPERTY BUNDLE_IGNORE_FILES
- "Python;python3.10m;python3.10;dxc-3.7;dxc;dxsc-3.7;dxsc;libdxcompiler.3.7.dylib;libdxcompiler.dylib")
- # Values configured for the output locations of the target
- # Gathered from the generator expression for TARGET_FILE_DIR, TARGET_BUNDLE_DIR
- # and TARGET_BUNDLE_CONTENT_DIR
- cmake_path(SET target_file_dir "@target_file_dir@")
- cmake_path(SET target_bundle_dir "@target_bundle_dir@")
- # The parent of the bundle directory contains the bundle.app file
- # It should be the same as the TARGET_FILE_DIR for a non-bundle
- cmake_path(GET target_bundle_dir PARENT_PATH target_bundle_parent_dir)
- cmake_path(SET target_bundle_content_dir "@target_bundle_content_dir@")
- set(target_copy_files $<FILTER:$<REMOVE_DUPLICATES:"@target_copy_files@">,EXCLUDE,"^$">)
- set(target_target_files $<FILTER:$<REMOVE_DUPLICATES:"@target_target_files@">,EXCLUDE,"^$">)
- set(target_link_files $<FILTER:$<REMOVE_DUPLICATES:"@target_link_files@">,EXCLUDE,"^$">)
- set(target_imported_files $<FILTER:$<REMOVE_DUPLICATES:"@target_imported_files@">,EXCLUDE,"^$">)
- # Set the Frameworks directory using the <bundle-content-dir> as the root
- unset(target_bundle_framework_dir)
- cmake_path(APPEND target_bundle_framework_dir "${target_bundle_content_dir}" "Frameworks")
- unset(target_bundle_plugins_dir)
- cmake_path(APPEND target_bundle_plugins_dir "${target_bundle_content_dir}" "PlugIns")
- find_program(LY_INSTALL_NAME_TOOL install_name_tool)
- if (NOT LY_INSTALL_NAME_TOOL)
- message(FATAL_ERROR "Unable to locate 'install_name_tool'")
- endif()
- # IS_NEWER_THAN returns true if:
- # 1. file1 is newer than file2
- # 2. Either file1 or file2 do not exist
- # 3. If both files have the same timestamp
- # We would like it to return false if the mod times are the same.
- function(ly_is_newer_than file1 file2 is_newer)
- set(${is_newer} FALSE PARENT_SCOPE)
- if("${file1}" IS_NEWER_THAN "${file2}")
- file(TIMESTAMP "${file1}" file1_mod_time)
- file(TIMESTAMP "${file2}" file2_mod_time)
- # note that the above modtimes will come thru in string format
- # like "2022-06-09T14:21:30" - not as numbers, so use STREQUAL
- if (NOT "${file1_mod_time}" STREQUAL "${file2_mod_time}")
- set(${is_newer} TRUE PARENT_SCOPE)
- endif()
- endif()
- endfunction()
- function(o3de_copy_file_to_bundle)
- set(options)
- set(oneValueArgs SOURCE_FILE SOURCE_TYPE SOURCE_GEM_MODULE RELATIVE_TARGET_DIRECTORY)
- set(multiValueArgs)
- cmake_parse_arguments("${CMAKE_CURRENT_FUNCTION}" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
- cmake_path(SET source_file NORMALIZE "${${CMAKE_CURRENT_FUNCTION}_SOURCE_FILE}")
- set(source_type "${${CMAKE_CURRENT_FUNCTION}_SOURCE_TYPE}")
- set(source_is_gem "${${CMAKE_CURRENT_FUNCTION}_SOURCE_GEM_MODULE}")
- cmake_path(SET relative_target_directory NORMALIZE "${${CMAKE_CURRENT_FUNCTION}_RELATIVE_TARGET_DIRECTORY}")
- # Use the final component of the source file has the target_filename
- cmake_path(GET source_file FILENAME target_filename)
- if("${source_file}" MATCHES "qt/translations")
- # Do not copy QT translation files to bundle
- return()
- endif()
- string(REGEX MATCH "\\.dylib$" is_dylib "${source_file}")
- string(REGEX MATCH "^(.*\\.[Ff]ramework)(/.*|$)" is_framework "${source_file}")
- # If the source file is in a framework, get it's framework directory
- if(is_framework)
- set(source_framework_directory ${CMAKE_MATCH_1})
- endif()
- string(REGEX MATCH "qt/plugins" is_qt_plugin "${source_file}")
- # Option to indicate if a manual copy to the bundle should take place
- set(explicit_copy_to_bundle TRUE)
- ## Get the full target path based on the dependency type
- # Link time dylib dependencies should go into the Contents/Frameworks folder according to the
- # Apple Placing Content in a Bundle page
- # https://developer.apple.com/documentation/bundleresources/placing_content_in_a_bundle#3875930
- if(is_qt_plugin)
- # Plugins need to be copied into the bundle "PlugIns" before running fixup_bundle on them
- # https://cmake.org/cmake/help/latest/module/BundleUtilities.html?highlight=bundleutilities#bundleutilities
- unset(target_file)
- # set the target file QT plugins to be inside <bundle-content-dir>/PlugIns directory
- cmake_path(APPEND target_file "${target_bundle_plugins_dir}" "${relative_target_directory}" "${target_filename}")
- elseif(is_framework)
- cmake_path(GET source_framework_directory FILENAME source_framework_filename)
- unset(target_file)
- cmake_path(APPEND target_file "${target_bundle_framework_dir}" "${source_framework_filename}")
- # Python.framework produces a bug in BundleUtilities so it needs manual handling
- # https://gitlab.kitware.com/cmake/cmake/-/issues/20165
- # if the source file being copied is from the Python framework
- # copy the entire Python.framework folder to the <bundle-app/Conents/Frameworks directory
- cmake_path(COMPARE "${source_framework_filename}" EQUAL "Python.framework" is_python_framework)
- if(is_python_framework)
- set_property(SOURCE "${CMAKE_CURRENT_LIST_FILE}" PROPERTY DEPENDS_ON_PYTHON TRUE)
- ly_is_newer_than("${source_framework_directory}" "${target_file}" is_python_newer)
- # If 3rdParty python is newer it needs to be removed before copying it to the bundle.
- # Otherwise the copy would file to do a previous invovation of this script
- # creating symlinks to the "Python.framework/Versions/3.10/Headers" directory
- # Because the source framework has real "Headers" directory the copy would fail
- # with the following error
- # file COPY cannot make directory
- # "<build-dir>/bin/profile/AssetProcessor.app/Contents/Frameworks/Python.framework/Versions/3.10/Headers":
- # No such file or directory.
- if (is_python_newer)
- file(REMOVE_RECURSE "${target_file}")
- endif()
- else()
- # Do not copy other Frameworks to the bundle manually
- set(explicit_copy_to_bundle FALSE)
- endif()
- elseif("${source_file}" IN_LIST target_copy_files OR "${source_file}" IN_LIST target_imported_files)
- unset(target_file)
- # copy the file dependencies to the directory relative to the <bundle-app>/Content/MacOS directory
- # imported dependencies refer to the IMPORTED_LOCATION property of the TARGET, so those
- # should also be copied to the <bundle-dir>/Content/MacOS if they are not a dylib
- if(is_dylib)
- # fixup bundle expects raw .dylib files to be directly under the <bundle-app>/Content/Framework folder
- # So don't add relative_target_directory as part of the target file path
- cmake_path(APPEND target_file "${target_bundle_framework_dir}" "${target_filename}")
- else()
- cmake_path(APPEND target_file "${target_file_dir}" "${relative_target_directory}" "${target_filename}")
- endif()
- elseif("${source_file}" IN_LIST target_link_files)
- unset(target_file)
- # copy the link dependencies to the <bundle-app>/Content/Frameworks directory
- cmake_path(APPEND target_file "${target_bundle_framework_dir}" "${relative_target_directory}" "${target_filename}")
- elseif("${source_file}" IN_LIST target_target_files)
- unset(target_file)
- # copy the target dependencies to the directory containing the bundle
- cmake_path(APPEND target_file "${target_bundle_parent_dir}" "${relative_target_directory}" "${target_filename}")
- if(is_dylib)
- # These are dynamic load dependencies that are triggered by the application,
- # they should not be copied to the bundle
- set(explicit_copy_to_bundle FALSE)
- endif()
- endif()
- # Set the target directory to be the parent of the target_file
- cmake_path(GET target_file PARENT_PATH target_directory)
- # Any ignored files are skipped from the bundle
- get_property(bundle_files_to_ignore SOURCE "${CMAKE_CURRENT_LIST_FILE}" PROPERTY BUNDLE_IGNORE_FILES)
- # dylibs and frameworks are passed to fixup bundle
- # Ignored files are based only on the filename path segment and not the full path
- # So the ${target_filename} variable is the one being checked in this case
- if((is_dylib OR is_framework) AND NOT "${target_filename}" IN_LIST bundle_files_to_ignore)
- # Invoke fixup bundle on copy dependencies, link dependencies and imported dependencies
- # Fixup bundle should not be invoked on target dependencies.
- # target dependencies can be dynamically loaded libraries via `dlopen`
- # or dependencies that need to build before the current target and are used
- # in some way that is only in the runtime
- # Ex. Gem Modules such as libPrefabBuilder.Builders.dylib should not be added to the bundle
- # Append to the SOURCE file property target file path if the target_file is a dylib
- # otherwise the target_file is within a Mac framework so append to the bundle directory property
- if(is_dylib)
- # Also skip adding symlinks to the list of libraries to fix up
- if(NOT "${source_file}" IN_LIST target_target_files AND NOT IS_SYMLINK "${source_file}")
- set_property(SOURCE "${CMAKE_CURRENT_LIST_FILE}" APPEND PROPERTY BUNDLE_LIBS "${target_file}")
- endif()
- else()
- # For frameworks fixup_bundle is used copy them to the output bundle
- # Therefore the source framework directory into the BUNDLE_DIRS
- set_property(SOURCE "${CMAKE_CURRENT_LIST_FILE}" APPEND PROPERTY BUNDLE_DIRS "${source_framework_directory}")
- endif()
- endif()
- # For frameworks copy the entire framework to the bundle
- if(is_framework)
- set(source_file "${source_framework_directory}")
- endif()
- # invoke o3de_copy_file_to_filesystem to copy the files to the bundle
- if(explicit_copy_to_bundle)
- o3de_copy_file_to_filesystem(
- SOURCE_FILE "${source_file}"
- SOURCE_TYPE "${source_type}"
- SOURCE_GEM_MODULE "${source_is_gem}"
- TARGET_FILE "${target_file}"
- )
- endif()
- endfunction()
- function(o3de_copy_file_to_filesystem)
- set(options)
- set(oneValueArgs SOURCE_FILE SOURCE_TYPE SOURCE_GEM_MODULE TARGET_FILE)
- set(multiValueArgs)
- cmake_parse_arguments("${CMAKE_CURRENT_FUNCTION}" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
- cmake_path(SET source_file NORMALIZE "${${CMAKE_CURRENT_FUNCTION}_SOURCE_FILE}")
- set(source_type "${${CMAKE_CURRENT_FUNCTION}_SOURCE_TYPE}")
- set(source_is_gem "${${CMAKE_CURRENT_FUNCTION}_SOURCE_GEM_MODULE}")
- cmake_path(SET target_file NORMALIZE "${${CMAKE_CURRENT_FUNCTION}_TARGET_FILE}")
- cmake_path(GET target_file PARENT_PATH target_directory)
- # Only copy the the source_file to destination if they are different
- cmake_path(COMPARE "${source_file}" EQUAL "${target_file}" same_location)
- if(NOT ${same_location})
- if(NOT EXISTS "${target_directory}")
- file(MAKE_DIRECTORY "${target_directory}")
- endif()
- unset(is_source_newer)
- # ly_is_newer_than will be true if:
- # 1. The source library was rebuilt.
- # 2. The library is a 3rdParty lib and it was downloaded after the target was copied.
- # 3. The library is being copied over for the first time(target does not exist).
- # While downloaded 3rdParty libs will have the creation time set to when it was built,
- # their modification time will reflect the time it was downloaded.
- ly_is_newer_than(${source_file} ${target_file} is_source_newer)
- if(${is_source_newer})
- message(STATUS "Copying \"${source_file}\" to \"${target_directory}\"...")
- file(MAKE_DIRECTORY "${target_directory}")
- file(COPY "${source_file}" DESTINATION "${target_directory}" FILE_PERMISSIONS @LY_COPY_PERMISSIONS@ FOLLOW_SYMLINK_CHAIN)
- file(TOUCH_NOCREATE "${target_file}")
- set_property(SOURCE "${CMAKE_CURRENT_LIST_FILE}" PROPERTY ANYTHING_NEW TRUE)
- endif()
- endif()
- endfunction()
- function(ly_copy source_files relative_target_directory)
- set(options)
- set(oneValueArgs TARGET_FILE_DIR SOURCE_TYPE SOURCE_GEM_MODULE)
- set(multiValueArgs)
- cmake_parse_arguments("${CMAKE_CURRENT_FUNCTION}" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
- set(source_type "${${CMAKE_CURRENT_FUNCTION}_SOURCE_TYPE}")
- set(source_is_gem "${${CMAKE_CURRENT_FUNCTION}_SOURCE_GEM_MODULE}")
- foreach(source_file IN LISTS source_files)
- # target is a bundle
- if(target_bundle_dir)
- o3de_copy_file_to_bundle(
- SOURCE_FILE "${source_file}"
- SOURCE_TYPE "${source_type}"
- SOURCE_GEM_MODULE "${source_is_gem}"
- RELATIVE_TARGET_DIRECTORY "${relative_target_directory}"
- )
- else()
- # target is not a bundle
- if("${source_file}" MATCHES "\\.[Ff]ramework[^\\.]")
- # fixup origin to copy the whole Framework folder
- string(REGEX REPLACE "(.*\\.[Ff]ramework).*" "\\1" source_file "${source_file}")
- endif()
- # Use final path segment as target file name
- cmake_path(GET source_file FILENAME target_filename)
- cmake_path(APPEND target_file "${target_file_dir}" "${relative_target_directory}" "${target_filename}")
- # Invoke o3de_copy_file_to_filestream to copy the source_file to the target_file
- o3de_copy_file_to_filesystem(
- SOURCE_FILE "${source_file}"
- SOURCE_TYPE "${source_type}"
- SOURCE_GEM_MODULE "${source_is_gem}"
- TARGET_FILE "${target_file}"
- )
- endif()
- endforeach()
- endfunction()
- @LY_COPY_COMMANDS@
- if(NOT @LY_BUILD_FIXUP_BUNDLE@)
- return()
- endif()
- if(target_bundle_content_dir)
- # Use get_property(SOURCE) to query the variables set within the ly_copy function calls
- # The PARENT_SCOPE option to set only applys to the immediate function scope
- # So a nested function call would prevent the variables from being set in this file scope
- get_property(anything_new SOURCE "${CMAKE_CURRENT_LIST_FILE}" PROPERTY ANYTHING_NEW)
- get_property(plugin_libs SOURCE "${CMAKE_CURRENT_LIST_FILE}" PROPERTY BUNDLE_LIBS)
- get_property(plugin_dirs SOURCE "${CMAKE_CURRENT_LIST_FILE}" PROPERTY BUNDLE_DIRS)
- get_property(depends_on_python SOURCE "${CMAKE_CURRENT_LIST_FILE}" PROPERTY DEPENDS_ON_PYTHON)
- set(fixup_timestamp_file "${target_bundle_dir}.fixup.stamp")
- if(NOT anything_new)
- ly_is_newer_than(${target_bundle_dir} ${fixup_timestamp_file} anything_new)
- endif()
- if(anything_new)
- # LYN-4502: Patch python bundle, it contains some windows executables, some files that fixup_bundle doesnt like and has
- # other issues that produce signature problems
- if(depends_on_python)
- message(STATUS "Fixing ${target_bundle_framework_dir}/Python.framework...")
- file(REMOVE_RECURSE
- "${target_bundle_framework_dir}/Python.framework/Versions/Current"
- "${target_bundle_framework_dir}/Python.framework/Versions/3.10/Headers"
- "${target_bundle_framework_dir}/Python.framework/Versions/3.10/lib/Python"
- "${target_bundle_framework_dir}/Python.framework/Versions/3.10/lib/python3.10/test"
- "${target_bundle_framework_dir}/Python.framework/Versions/3.10/lib/python3.10/site-packages/scipy/io/tests"
- "${target_bundle_framework_dir}/Python.framework/Python"
- "${target_bundle_framework_dir}/Python.framework/Resources"
- "${target_bundle_framework_dir}/Python.framework/Headers"
- )
- file(GLOB_RECURSE exe_file_list "${target_bundle_framework_dir}/Python.framework/**/*.exe")
- if(exe_file_list)
- file(REMOVE_RECURSE ${exe_file_list})
- endif()
- execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink 3.10 Current
- WORKING_DIRECTORY "${target_bundle_framework_dir}/Python.framework/Versions/"
- )
- execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink Versions/Current/Python Python
- WORKING_DIRECTORY "${target_bundle_framework_dir}/Python.framework"
- )
- execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink Versions/Current/Resources Resources
- WORKING_DIRECTORY "${target_bundle_framework_dir}/Python.framework"
- )
- file(CHMOD "${target_bundle_framework_dir}/Python.framework/Versions/Current/Python"
- PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
- )
- endif()
- # Read the source property with the list of files to ignore for bundling
- get_property(fixup_bundle_ignore SOURCE "${CMAKE_CURRENT_LIST_FILE}" PROPERTY BUNDLE_IGNORE_FILES)
- list(REMOVE_DUPLICATES plugin_libs)
- list(REMOVE_DUPLICATES plugin_dirs)
- fixup_bundle("${target_bundle_dir}" "${plugin_libs}" "${plugin_dirs}" IGNORE_ITEM ${fixup_bundle_ignore})
- # fix the rpath for the DirectXShaderCompiler `dxc-3.7` to point at
- # <bundle-app>/Contents/Frameworks/libDirectXShaderCompiler.dylib
- #
- # The proper fix is to update the
- # https://github.com/o3de/3p-package-source/blob/main/package-system/DirectXShaderCompiler/FindDirectXShaderCompilerDxc.cmake.Mac
- # file to make an IMPORTED SHARED target for the library file and to add an IMPORTED executable for the application
- # That way the dxc executable and library will be copied through the link dependencies correctly
- # The [ly_add_target_files](https://github.com/o3de/3p-package-source/blob/main/package-system/DirectXShaderCompiler/FindDirectXShaderCompilerDxc.cmake.Mac#L24)
- # calls in that file should be removed as part resolving issue https://github.com/o3de/3p-package-source/issues/201
- # Furthermore the ly_add_target_files method would benefit from an rpath change argument
- # to allow authors to correct the rpath after a copy
- cmake_path(SET dxc_path "${target_file_dir}/Builders/DirectXShaderCompiler/bin/dxc-3.7")
- if(EXISTS "${dxc_path}")
- execute_process(COMMAND "${LY_INSTALL_NAME_TOOL}"
- -add_rpath "@executable_path/../../../../Frameworks"
- "${dxc_path}")
- endif()
- cmake_path(SET dxsc_path "${target_file_dir}/Builders/DirectXShaderCompiler/bin/dxsc-3.7")
- if(EXISTS "${dxsc_path}")
- execute_process(COMMAND "${LY_INSTALL_NAME_TOOL}"
- -add_rpath "@executable_path/../../../../Frameworks"
- "${dxsc_path}")
- endif()
- # misplaced .DS_Store files can cause signing to fail
- # Interrupted signatures can leave cstemp files behind that fail next signature
- file(GLOB_RECURSE remove_file_list
- "${target_bundle_dir}/**/.DS_Store"
- "${target_bundle_dir}/**/*.cstemp"
- )
- if(remove_file_list)
- message(STATUS "Cleaning up .DS_store *.cstemp files: ${remove_file_list}")
- file(REMOVE "${remove_file_list}")
- unset(remove_file_list)
- file(GLOB_RECURSE remove_file_list
- "${target_bundle_dir}/**/.DS_Store"
- "${target_bundle_dir}/**/*.cstemp"
- )
- if(remove_file_list)
- message(STATUS "Removal of files has failed: ${remove_file_list}")
- endif()
- endif()
- file(TOUCH "${target_bundle_dir}")
- file(TOUCH "${fixup_timestamp_file}")
- endif()
- endif()
|