EngineFinder.cmake 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. # {BEGIN_LICENSE}
  2. #
  3. # Copyright (c) Contributors to the Open 3D Engine Project.
  4. # For complete copyright and license terms please see the LICENSE at the root of this distribution.
  5. #
  6. # SPDX-License-Identifier: Apache-2.0 OR MIT
  7. #
  8. #
  9. # {END_LICENSE}
  10. # Edits to this file may be lost in upgrades. Instead of changing this file, use
  11. # the 'engine_finder_cmake_path' key in your project.json or user/project.json to specify
  12. # an alternate .cmake file to use instead of this one.
  13. include_guard()
  14. set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/project.json)
  15. # Option 1: Use engine manually set in CMAKE_MODULE_PATH
  16. # CMAKE_MODULE_PATH must contain a path to an engine's cmake folder
  17. if(CMAKE_MODULE_PATH)
  18. foreach(module_path ${CMAKE_MODULE_PATH})
  19. cmake_path(SET module_engine_version_cmake_path "${module_path}/o3deConfigVersion.cmake")
  20. if(EXISTS "${module_engine_version_cmake_path}")
  21. include("${module_engine_version_cmake_path}")
  22. if(PACKAGE_VERSION_COMPATIBLE)
  23. message(STATUS "Selecting engine from CMAKE_MODULE_PATH '${module_path}'")
  24. return()
  25. else()
  26. message(WARNING "Not using engine from CMAKE_MODULE_PATH '${module_path}' because it is not compatible with this project.")
  27. endif()
  28. endif()
  29. endforeach()
  30. message(VERBOSE "No compatible engine found from CMAKE_MODULE_PATH '${CMAKE_MODULE_PATH}'.")
  31. endif()
  32. # before proceeding, check and set a global variable if its a "script only" project
  33. file(READ ${CMAKE_CURRENT_SOURCE_DIR}/project.json o3de_project_json)
  34. string(JSON project_is_script_only ERROR_VARIABLE json_error GET ${o3de_project_json} "script_only")
  35. if(project_is_script_only AND NOT json_error)
  36. # we only set it if there's no error and it is a script only project
  37. message(STATUS "This is a script-only project, no C++ compiler will be used")
  38. set_property(GLOBAL PROPERTY "O3DE_SCRIPT_ONLY" TRUE)
  39. endif()
  40. # Option 2: Use the engine from the 'engine_path' field in <project>/user/project.json
  41. cmake_path(SET O3DE_USER_PROJECT_JSON_PATH ${CMAKE_CURRENT_SOURCE_DIR}/user/project.json)
  42. if(EXISTS "${O3DE_USER_PROJECT_JSON_PATH}")
  43. file(READ "${O3DE_USER_PROJECT_JSON_PATH}" user_project_json)
  44. if(user_project_json)
  45. string(JSON user_project_engine_path ERROR_VARIABLE json_error GET ${user_project_json} engine_path)
  46. if(user_project_engine_path AND NOT json_error)
  47. cmake_path(SET user_engine_version_cmake_path "${user_project_engine_path}/cmake/o3deConfigVersion.cmake")
  48. if(EXISTS "${user_engine_version_cmake_path}")
  49. include("${user_engine_version_cmake_path}")
  50. if(PACKAGE_VERSION_COMPATIBLE)
  51. message(STATUS "Selecting engine '${user_project_engine_path}' from 'engine_path' in '<project>/user/project.json'.")
  52. list(APPEND CMAKE_MODULE_PATH "${user_project_engine_path}/cmake")
  53. return()
  54. else()
  55. message(FATAL_ERROR "The engine at '${user_project_engine_path}' from 'engine_path' in '${O3DE_USER_PROJECT_JSON_PATH}' is not compatible with this project. Please register this project with a compatible engine, or remove the local override by running:\nscripts\\o3de edit-project-properties -pp ${CMAKE_CURRENT_SOURCE_DIR} --user --engine-path \"\"")
  56. endif()
  57. else()
  58. message(FATAL_ERROR "This project cannot use the engine at '${user_project_engine_path}' because the version cmake file '${user_engine_version_cmake_path}' needed to check compatibility is missing. \nPlease register this project with a compatible engine, or remove the local override by running:\nscripts\\o3de edit-project-properties -pp ${CMAKE_CURRENT_SOURCE_DIR} --user --engine-path \"\"\nIf you want this project to use an older engine(not recommended), provide a custom EngineFinder.cmake using the o3de CLI's --engine-finder-cmake-path option. ")
  59. endif()
  60. elseif(json_error AND ${user_project_engine_path} STREQUAL "NOTFOUND")
  61. # When the value is just NOTFOUND that means there is a JSON
  62. # parsing error, and not simply a missing key
  63. message(FATAL_ERROR "Unable to read 'engine_path' from '${user_project_engine_path}'\nError: ${json-error}")
  64. endif()
  65. endif()
  66. endif()
  67. # Option 3: Find a compatible engine registered in ~/.o3de/o3de_manifest.json
  68. if(DEFINED ENV{USERPROFILE} AND EXISTS $ENV{USERPROFILE})
  69. set(manifest_path $ENV{USERPROFILE}/.o3de/o3de_manifest.json) # Windows
  70. else()
  71. set(manifest_path $ENV{HOME}/.o3de/o3de_manifest.json) # Unix
  72. endif()
  73. set(registration_error [=[
  74. To enable more verbose logging, run the cmake command again with '--log-level VERBOSE'
  75. Engine registration is required before configuring a project.
  76. Run 'scripts/o3de register --this-engine' from the engine root.
  77. ]=])
  78. # Create a list of all engines
  79. if(EXISTS ${manifest_path})
  80. file(READ ${manifest_path} manifest_json)
  81. set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${manifest_path})
  82. string(JSON engines_count ERROR_VARIABLE json_error LENGTH ${manifest_json} engines)
  83. if(json_error)
  84. message(FATAL_ERROR "Unable to read key 'engines' from '${manifest_path}'\nError: ${json_error}\n${registration_error}")
  85. endif()
  86. string(JSON engines_type ERROR_VARIABLE json_error TYPE ${manifest_json} engines)
  87. if(json_error OR NOT ${engines_type} STREQUAL "ARRAY")
  88. message(FATAL_ERROR "Type of 'engines' in '${manifest_path}' is not a JSON ARRAY\nError: ${json_error}\n${registration_error}")
  89. endif()
  90. math(EXPR engines_count "${engines_count}-1")
  91. foreach(array_index RANGE ${engines_count})
  92. string(JSON manifest_engine_path ERROR_VARIABLE json_error GET ${manifest_json} engines "${array_index}")
  93. if(json_error)
  94. message(FATAL_ERROR "Unable to read 'engines/${array_index}' from '${manifest_path}'\nError: ${json_error}\n${registration_error}")
  95. endif()
  96. list(APPEND O3DE_ENGINE_PATHS ${manifest_engine_path})
  97. endforeach()
  98. elseif(NOT CMAKE_MODULE_PATH)
  99. message(FATAL_ERROR "O3DE Manifest file not found at '${manifest_path}'.\n${registration_error}")
  100. endif()
  101. # We cannot just run find_package() on the list of engine paths because
  102. # CMAKE_FIND_PACKAGE_SORT_ORDER sorts based on file name and chooses
  103. # the first package that returns PACKAGE_VERSION_COMPATIBLE
  104. set(O3DE_MOST_COMPATIBLE_ENGINE_PATH "")
  105. set(O3DE_MOST_COMPATIBLE_ENGINE_VERSION "")
  106. foreach(manifest_engine_path IN LISTS O3DE_ENGINE_PATHS)
  107. # Does this engine have a config version cmake file?
  108. cmake_path(SET version_cmake_path "${manifest_engine_path}/cmake/o3deConfigVersion.cmake")
  109. if(NOT EXISTS "${version_cmake_path}")
  110. message(VERBOSE "Ignoring '${manifest_engine_path}' because no config version cmake file was found at '${version_cmake_path}'")
  111. continue()
  112. endif()
  113. unset(PACKAGE_VERSION)
  114. unset(PACKAGE_VERSION_COMPATIBLE)
  115. include("${version_cmake_path}")
  116. # Follow the version checking convention from find_package(CONFIG)
  117. if(PACKAGE_VERSION_COMPATIBLE)
  118. if(NOT O3DE_MOST_COMPATIBLE_ENGINE_PATH)
  119. set(O3DE_MOST_COMPATIBLE_ENGINE_PATH "${manifest_engine_path}")
  120. set(O3DE_MOST_COMPATIBLE_ENGINE_VERSION ${PACKAGE_VERSION})
  121. message(VERBOSE "Found compatible engine '${manifest_engine_path}' with version '${PACKAGE_VERSION}'")
  122. elseif(${PACKAGE_VERSION} VERSION_GREATER ${O3DE_MOST_COMPATIBLE_ENGINE_VERSION})
  123. set(O3DE_MOST_COMPATIBLE_ENGINE_PATH "${manifest_engine_path}")
  124. set(O3DE_MOST_COMPATIBLE_ENGINE_VERSION ${PACKAGE_VERSION})
  125. message(VERBOSE "Found more compatible engine '${manifest_engine_path}' with version '${PACKAGE_VERSION}' because it has a greater version number.")
  126. else()
  127. message(VERBOSE "Not using engine '${manifest_engine_path}' with version '${PACKAGE_VERSION}' because it doesn't have a greater version number or has a different engine name.")
  128. endif()
  129. else()
  130. message(VERBOSE "Ignoring '${manifest_engine_path}' because it is not a compatible engine.")
  131. endif()
  132. endforeach()
  133. if(O3DE_MOST_COMPATIBLE_ENGINE_PATH)
  134. message(STATUS "Selecting engine '${O3DE_MOST_COMPATIBLE_ENGINE_PATH}'")
  135. # Make sure PACKAGE_VERSION_COMPATIBLE is set so Findo3de.cmake knows
  136. # compatibility was checked
  137. set(PACKAGE_VERSION_COMPATIBLE True)
  138. set(PACKAGE_VERSION O3DE_MOST_COMPATIBLE_ENGINE_VERSION)
  139. list(APPEND CMAKE_MODULE_PATH "${O3DE_MOST_COMPATIBLE_ENGINE_PATH}/cmake")
  140. return()
  141. endif()
  142. # No compatible engine was found.
  143. # Read the 'engine' field in project.json or user/project.json for more helpful messages
  144. if(user_project_json)
  145. string(JSON user_project_engine ERROR_VARIABLE json_error GET ${user_project_json} engine)
  146. endif()
  147. if(NOT user_project_engine)
  148. string(JSON project_engine ERROR_VARIABLE json_error GET ${o3de_project_json} engine)
  149. if(json_error AND ${project_engine} STREQUAL "NOTFOUND")
  150. message(FATAL_ERROR "Unable to read key 'engine' from 'project.json'\nError: ${json_error}")
  151. endif()
  152. endif()
  153. if(user_project_engine)
  154. message(FATAL_ERROR "The local '${O3DE_USER_PROJECT_JSON_PATH}' engine is '${user_project_engine}' but no compatible engine with that name and version was found. Please register the compatible engine, or remove the local engine override.\n${registration_error}")
  155. elseif(project_engine)
  156. message(FATAL_ERROR "The project.json engine is '${project_engine}' but no engine with that name and version was found.\n${registration_error}")
  157. else()
  158. set(project_registration_error [=[
  159. Project registration is required before configuring a project.
  160. Run 'scripts/o3de register -pp PROJECT_PATH --engine-path ENGINE_PATH' from the engine root.
  161. ]=])
  162. message(FATAL_ERROR "${project_registration_error}")
  163. endif()