FileUtil.cmake 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. #
  2. # Copyright (c) Contributors to the Open 3D Engine Project.
  3. # For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. #
  5. # SPDX-License-Identifier: Apache-2.0 OR MIT
  6. #
  7. #
  8. #! ly_include_cmake_file_list: includes a cmake file list
  9. #
  10. # Includes a .cmake file that contains a variable "FILE" with the list of files
  11. # The function appends the list of files to the variable ALLFILES. Callers of this
  12. # function can include multiple cmake file list files and then use ALLFILES as the
  13. # source for libraries/binaries/etc. Files that need to be excluded from unity builds
  14. # due to possible ODR violations will need to be set in the variable 'SKIP_UNITY_BUILD_INCLUSION_FILES'
  15. # in the same .cmake file (regardless of whether unity builds are enabled or not)
  16. #
  17. # The function takes care of appending the path relative to the cmake list file
  18. #
  19. # \arg:file cmake file list to include
  20. #
  21. function(ly_include_cmake_file_list file)
  22. set(UNITY_AUTO_EXCLUSIONS)
  23. include(${file})
  24. get_filename_component(file_path "${file}" PATH)
  25. if(file_path)
  26. foreach(f ${FILES})
  27. cmake_path(IS_RELATIVE f is_relative)
  28. if(is_relative)
  29. string(PREPEND f ${file_path}/)
  30. endif()
  31. list(APPEND TRANSFORMED_FILES ${f})
  32. endforeach()
  33. set(FILES ${TRANSFORMED_FILES})
  34. endif()
  35. foreach(f ${FILES})
  36. get_filename_component(absolute_path ${f} ABSOLUTE)
  37. if(NOT EXISTS ${absolute_path})
  38. message(SEND_ERROR "File ${absolute_path} referenced in ${file} not found")
  39. endif()
  40. # Automatically exclude any extensions marked for the current platform
  41. if (PAL_TRAIT_BUILD_UNITY_EXCLUDE_EXTENSIONS)
  42. get_filename_component(file_extension ${f} LAST_EXT)
  43. if (${file_extension} IN_LIST PAL_TRAIT_BUILD_UNITY_EXCLUDE_EXTENSIONS)
  44. list(APPEND UNITY_AUTO_EXCLUSIONS ${f})
  45. endif()
  46. endif()
  47. endforeach()
  48. list(APPEND FILES ${file}) # Add the _files.cmake to the list so it shows in the IDE
  49. if(file_path)
  50. foreach(f ${SKIP_UNITY_BUILD_INCLUSION_FILES})
  51. cmake_path(IS_RELATIVE f is_relative)
  52. if(is_relative)
  53. string(PREPEND f ${file_path}/)
  54. endif()
  55. list(APPEND SKIP_UNITY_BUILD_INCLUSION_TRANSFORMED_FILES ${f})
  56. endforeach()
  57. set(SKIP_UNITY_BUILD_INCLUSION_FILES ${SKIP_UNITY_BUILD_INCLUSION_TRANSFORMED_FILES})
  58. endif()
  59. # Check if there are any files to exclude from unity groupings
  60. foreach(f ${SKIP_UNITY_BUILD_INCLUSION_FILES})
  61. get_filename_component(absolute_path ${f} ABSOLUTE)
  62. if(NOT EXISTS ${absolute_path})
  63. message(FATAL_ERROR "File ${absolute_path} for SKIP_UNITY_BUILD_INCLUSION_FILES referenced in ${file} not found")
  64. endif()
  65. if(NOT f IN_LIST FILES)
  66. list(APPEND FILES ${f})
  67. endif()
  68. endforeach()
  69. # Mark files tagged for unity build group exclusions for unity builds
  70. if (LY_UNITY_BUILD)
  71. set_source_files_properties(
  72. ${UNITY_AUTO_EXCLUSIONS}
  73. ${SKIP_UNITY_BUILD_INCLUSION_FILES}
  74. PROPERTIES
  75. SKIP_UNITY_BUILD_INCLUSION ON
  76. )
  77. endif()
  78. set(ALLFILES ${ALLFILES} ${FILES} PARENT_SCOPE)
  79. endfunction()
  80. #! ly_source_groups_from_folders: creates source_group for files
  81. #
  82. # Uses the path to the files being passed to create source_gropups for them.
  83. #
  84. # \arg:files list of files to create source_group for
  85. #
  86. function(ly_source_groups_from_folders files)
  87. foreach(file IN LISTS files)
  88. if(IS_ABSOLUTE ${file})
  89. file(RELATIVE_PATH file ${CMAKE_CURRENT_SOURCE_DIR} ${file})
  90. endif()
  91. get_filename_component(file_path "${file}" PATH)
  92. string(REPLACE "/" "\\" file_filters "${file_path}")
  93. source_group("${file_filters}" FILES "${file}")
  94. endforeach()
  95. endfunction()
  96. #! ly_update_platform_settings: Function to generate a config-parseable file for cmake-project generation settings
  97. #
  98. # This generate a platform-specific, parseable config file that will live in the build directory specified during
  99. # the cmake project generation. This will provide a bridge to non-cmake tools to read the platform-specific cmake
  100. # project generation settings.
  101. #
  102. get_property(O3DE_PROJECTS_NAME GLOBAL PROPERTY O3DE_PROJECTS_NAME)
  103. function(ly_update_platform_settings)
  104. # Update the <platform>.last file to keep track of the recent build_dir
  105. set(ly_platform_last_path "${CMAKE_BINARY_DIR}/platform.settings")
  106. string(TIMESTAMP ly_last_generation_timestamp "%Y-%m-%dT%H:%M:%S")
  107. file(WRITE ${ly_platform_last_path} "# Auto Generated from last cmake project generation (${ly_last_generation_timestamp})
  108. [settings]
  109. platform=${PAL_PLATFORM_NAME}
  110. game_projects=${O3DE_PROJECTS_NAME}
  111. asset_deploy_mode=${LY_ASSET_DEPLOY_MODE}
  112. asset_deploy_type=${LY_ASSET_DEPLOY_ASSET_TYPE}
  113. override_pak_root=${LY_ASSET_OVERRIDE_PAK_FOLDER_ROOT}
  114. ")
  115. endfunction()
  116. #! ly_file_read: wrap to file(READ) that adds the file to configuration tracking
  117. #
  118. # file(READ) does not add file tracking. So changes to the file being read will not cause a cmake regeneration
  119. #
  120. function(ly_file_read path content)
  121. unset(file_content)
  122. file(READ ${path} file_content)
  123. set(${content} ${file_content} PARENT_SCOPE)
  124. set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${path})
  125. endfunction()
  126. #! o3de_file_read_cache: wrap ly_file_read but store the file in a cache to avoid
  127. # extra reads.
  128. function(o3de_file_read_cache path content)
  129. unset(file_content)
  130. cmake_path(SET path "${path}")
  131. cmake_path(NORMAL_PATH path)
  132. set(file_cache_var_name "O3DE_FILE_CACHE_${path}")
  133. get_property(file_content GLOBAL PROPERTY ${file_cache_var_name})
  134. if(NOT file_content)
  135. ly_file_read(${path} file_content)
  136. set_property(GLOBAL PROPERTY ${file_cache_var_name} ${file_content})
  137. endif()
  138. set(${content} ${file_content} PARENT_SCOPE)
  139. endfunction()
  140. #! ly_get_last_path_segment_concat_sha256 : Concatenates the last path segment of the absolute path
  141. # with the first 8 characters of the absolute path SHA256 hash to make a unique relative path segment
  142. function(ly_get_last_path_segment_concat_sha256 absolute_path output_path)
  143. string(SHA256 target_source_hash ${absolute_path})
  144. string(SUBSTRING ${target_source_hash} 0 8 target_source_hash)
  145. cmake_path(GET absolute_path FILENAME last_path_segment)
  146. cmake_path(SET last_path_segment_sha256_path "${last_path_segment}-${target_source_hash}")
  147. set(${output_path} ${last_path_segment_sha256_path} PARENT_SCOPE)
  148. endfunction()
  149. #! ly_get_root_subdirectory_which_is_parent: Locates the root source directory added the input directory
  150. # as a subdirectory of the build, which an actual prefix of the input directory
  151. # This is done by recursing through the PARENT_DIRECTORY "DIRECTORY" property
  152. # The use for this is to locate the top most directory which called add_subdirectory from any input path
  153. # i.e Given an
  154. # LY_ROOT_FOLDER = D:\o3de
  155. # EXTERNAL_SUBDIRS = [D:\TestGem, D:\o3de\Gems\MyGem]
  156. # The LY_ROOT_FOLDER is responsible for invoking add_subdirectory on the external subdirectories
  157. # so it in the PARENT_DIRECTORY property, of the subdirectory, though it might not be an actual "parent"
  158. # If the input path to this function is D:\TestGem\Code, then the return value is D:\TestGem
  159. # If the input path to this function is D:\o3de\Gems\MyGem, then the return value is D:\o3de
  160. # \arg:absolute_path - directory to locate top most parent "subdirectory", which is an "parent" of the input
  161. # \return:output_path- top most parent subdirectory, which is actual parent(i.e a prefix)
  162. function(ly_get_root_subdirectory_which_is_parent absolute_path output_path)
  163. # Walk up the parent add_subdirectory calls until a parent directory which is not a prefix of the target directory
  164. # is found
  165. cmake_path(SET candidate_path ${absolute_path})
  166. get_property(parent_subdir DIRECTORY ${candidate_path} PROPERTY PARENT_DIRECTORY)
  167. cmake_path(IS_PREFIX parent_subdir ${candidate_path} is_parent_subdir)
  168. while(parent_subdir AND is_parent_subdir)
  169. cmake_path(SET candidate_path "${parent_subdir}")
  170. get_property(parent_subdir DIRECTORY ${candidate_path} PROPERTY PARENT_DIRECTORY)
  171. cmake_path(IS_PREFIX parent_subdir ${candidate_path} is_parent_subdir)
  172. endwhile()
  173. message(DEBUG "Root subdirectory of path \"${absolute_path}\" is \"${candidate_path}\"")
  174. set(${output_path} ${candidate_path} PARENT_SCOPE)
  175. endfunction()
  176. #! ly_get_engine_relative_source_dir: Attempts to form a path relative to the BASE_DIRECTORY.
  177. # If that fails the last path segment of the absolute_target_source_dir concatenated with a SHA256 hash to form a target directory
  178. # \arg:BASE_DIRECTORY - Directory to base relative path against. Defaults to LY_ROOT_FOLDER
  179. function(ly_get_engine_relative_source_dir absolute_target_source_dir output_source_dir)
  180. set(options)
  181. set(oneValueArgs BASE_DIRECTORY)
  182. set(multiValueArgs)
  183. cmake_parse_arguments(ly_get_engine_relative_source_dir "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  184. if(NOT ly_get_engine_relative_source_dir_BASE_DIRECTORY)
  185. set(ly_get_engine_relative_source_dir_BASE_DIRECTORY ${LY_ROOT_FOLDER})
  186. endif()
  187. # Get a relative target source directory to the LY root folder if possible
  188. # Otherwise use the top most source directory which led to calling add_subdirectory on the input directory
  189. ly_get_root_subdirectory_which_is_parent(${absolute_target_source_dir} root_subdir_of_target)
  190. cmake_path(RELATIVE_PATH absolute_target_source_dir BASE_DIRECTORY ${root_subdir_of_target} OUTPUT_VARIABLE relative_target_source_dir)
  191. cmake_path(IS_PREFIX LY_ROOT_FOLDER ${absolute_target_source_dir} is_target_source_dir_subdirectory_of_engine)
  192. if(NOT is_target_source_dir_subdirectory_of_engine)
  193. cmake_path(GET root_subdir_of_target FILENAME root_subdir_dirname)
  194. set(relative_subdir ${relative_target_source_dir})
  195. unset(relative_target_source_dir)
  196. cmake_path(APPEND relative_target_source_dir "External" ${root_subdir_dirname} ${relative_subdir})
  197. endif()
  198. set(${output_source_dir} ${relative_target_source_dir} PARENT_SCOPE)
  199. endfunction()