projectcmake.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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. import json
  9. import sys
  10. import os
  11. import subprocess
  12. import argparse
  13. import glob
  14. import waffiles2cmake
  15. import gemcmake
  16. def getProjectGemCMakeListsTemplate():
  17. return """ly_add_target(
  18. NAME {GEM_NAME}.Static STATIC
  19. NAMESPACE Gem
  20. FILES_CMAKE
  21. {GEM_NAME_LOWERCASE}_files.cmake
  22. INCLUDE_DIRECTORIES
  23. PRIVATE
  24. Source
  25. PUBLIC
  26. Include
  27. BUILD_DEPENDENCIES
  28. PRIVATE
  29. #AZ::AzCore
  30. )
  31. ly_add_target(
  32. NAME {GEM_NAME} ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
  33. NAMESPACE Gem
  34. FILES_CMAKE
  35. {GEM_NAME_LOWERCASE}_shared_files.cmake
  36. INCLUDE_DIRECTORIES
  37. PRIVATE
  38. Source
  39. PUBLIC
  40. Include
  41. BUILD_DEPENDENCIES
  42. PRIVATE
  43. Gem::{GEM_NAME}.Static
  44. )
  45. if(PAL_TRAIT_BUILD_HOST_TOOLS)
  46. ly_add_target(
  47. NAME {GEM_NAME}.Editor GEM_MODULE
  48. NAMESPACE Gem
  49. FILES_CMAKE
  50. {GEM_NAME_LOWERCASE}_editor_files.cmake
  51. INCLUDE_DIRECTORIES
  52. PRIVATE
  53. Source
  54. PUBLIC
  55. Include
  56. BUILD_DEPENDENCIES
  57. PRIVATE
  58. #AZ::AzCore
  59. )
  60. endif()
  61. ################################################################################
  62. # Gem dependencies
  63. ################################################################################
  64. ly_add_project_dependencies(
  65. PROJECT_NAME
  66. {GEM_NAME}
  67. TARGETS
  68. {GEM_NAME}.GameLauncher
  69. DEPENDENCIES_FILES
  70. runtime_dependencies.cmake
  71. )
  72. if(PAL_TRAIT_BUILD_HOST_TOOLS)
  73. ly_add_project_dependencies(
  74. PROJECT_NAME
  75. {GEM_NAME}
  76. TARGETS
  77. AssetBuilder
  78. AssetProcessor
  79. AssetProcessorBatch
  80. Editor
  81. DEPENDENCIES_FILES
  82. tool_dependencies.cmake
  83. )
  84. endif()
  85. ################################################################################
  86. # Tests
  87. ################################################################################
  88. if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
  89. ly_add_target(
  90. NAME {GEM_NAME}.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
  91. NAMESPACE Gem
  92. FILES_CMAKE
  93. {GEM_NAME_LOWERCASE}_tests_files.cmake
  94. INCLUDE_DIRECTORIES
  95. PRIVATE
  96. Tests
  97. BUILD_DEPENDENCIES
  98. PRIVATE
  99. AZ::AzTest
  100. Gem::{GEM_NAME}.Static
  101. )
  102. ly_add_googletest(
  103. NAME {GEM_NAME}.Tests
  104. )
  105. endif()
  106. """
  107. def getEmptyGemDependencyCMakeFormat():
  108. return """set(GEM_DEPENDENCIES
  109. """
  110. def getGemPaths(gems_list, project_path):
  111. gem_paths = []
  112. # Get the parent directory of the project
  113. # If this is not an external project this should be the dev folder for the LY install
  114. project_parent_path = os.path.abspath(os.path.join(project_path, os.pardir))
  115. for gem_json in gems_list:
  116. gem_path = gem_json['Path']
  117. # First check if this Gem is found within the project itself
  118. project_gem_path = os.path.normcase(os.path.join(project_path, gem_path))
  119. # If so then store the path
  120. if os.path.exists(project_gem_path):
  121. gem_paths.append(project_gem_path)
  122. continue
  123. # Otherwise check if the Gem can be found in the parent directory's Gems folder
  124. parent_gems_path = os.path.normcase(os.path.join(project_parent_path, gem_path))
  125. if os.path.exists(parent_gems_path):
  126. gem_paths.append(parent_gems_path)
  127. else:
  128. # Could not find the gem in the project or in the LY install
  129. print(f'Could not find full path for {gem_path}')
  130. sys.exit(1)
  131. return gem_paths
  132. def getGemJson(gem_path):
  133. # Since the gem.json file can live in subdirectories of the gem root
  134. # walk the directory tree until we come across it
  135. gem_json_path = None
  136. for root, dirs, files in os.walk(gem_path):
  137. for filename in files:
  138. if filename == 'gem.json':
  139. gem_json_path = os.path.join(root, filename)
  140. # load and return the gem.json for this particular gem
  141. if gem_json_path:
  142. with open(gem_json_path) as f:
  143. gem_json = json.load(f)
  144. return gem_json
  145. def processGemDependencies(gem_paths):
  146. toolTime_gems = []
  147. runTime_gems = []
  148. for gem_path in gem_paths:
  149. #Acquire the gem.json info for the gem located at this path
  150. gem_json = getGemJson(gem_path)
  151. if gem_json is None:
  152. print(f'Could not find gem.json file for gem located at {gem_path}')
  153. sys.exit(1)
  154. # Filter out asset only gems
  155. if 'LinkType' in gem_json:
  156. link_type = gem_json['LinkType']
  157. if link_type == 'NoCode':
  158. continue
  159. # Assume there's always a game gem
  160. has_editor_gem = False
  161. has_game_gem = True
  162. has_modules = 'Modules' in gem_json
  163. gem_name = gem_json['Name']
  164. if gem_name is None:
  165. print(f'gem.json for gem located at {gem_path} does not have a Name field')
  166. sys.exit(1)
  167. if has_modules:
  168. # If only an EditorModule is declared there is no GameModule
  169. has_game_gem = False
  170. gem_modules = gem_json['Modules']
  171. for module in gem_modules:
  172. if 'Type' not in module:
  173. continue
  174. module_type = module['Type']
  175. if module_type == 'EditorModule':
  176. has_editor_gem = True
  177. elif module_type == 'GameModule':
  178. has_game_gem = True
  179. else:
  180. if 'EditorModule' in gem_json:
  181. editor_module = gem_json['EditorModule']
  182. if editor_module is True:
  183. has_editor_gem = True
  184. if not has_editor_gem and not has_game_gem:
  185. print(f'gem.json for gem located at {gem_path} does not define a Game or Editor Module and is not an asset only gem')
  186. sys.exit(1)
  187. # If an EditorModule was explicitly declared then we will load the .Editor form
  188. # Otherwise we will still add the game module to our toolTime list
  189. if has_editor_gem:
  190. toolTime_gems.append(f'{gem_name}.Editor')
  191. else:
  192. toolTime_gems.append(gem_name);
  193. if has_game_gem:
  194. runTime_gems.append(gem_name)
  195. return toolTime_gems, runTime_gems
  196. def generateCMakeFilesForProjectGemDependencies(toolTime_dependencies, runTime_dependencies, project_code_path):
  197. toolTime_filePath = os.path.join(project_code_path, 'tool_dependencies.cmake')
  198. runTime_filePath = os.path.join(project_code_path, 'runtime_dependencies.cmake')
  199. # Generate tool_dependencies.cmake
  200. with open(toolTime_filePath, 'w') as toolTime_file:
  201. toolTime_file.write(gemcmake.getCopyright())
  202. toolTime_file.write(getEmptyGemDependencyCMakeFormat())
  203. # Add each dependent tooltime gem as a line item into the file
  204. for toolTime_dependency in toolTime_dependencies:
  205. toolTime_file.write(f' Gem::{toolTime_dependency}\n')
  206. toolTime_file.write(')')
  207. subprocess.run(['p4', 'add', toolTime_filePath])
  208. # Generate runtime dependencies
  209. with open(runTime_filePath, 'w') as runTime_file:
  210. runTime_file.write(gemcmake.getCopyright())
  211. runTime_file.write(getEmptyGemDependencyCMakeFormat())
  212. # Add each dependent runtime gem as a line item into the file
  213. for runTime_dependency in runTime_dependencies:
  214. runTime_file.write(f' Gem::{runTime_dependency}\n')
  215. runTime_file.write(')')
  216. subprocess.run(['p4', 'add', runTime_filePath])
  217. def main():
  218. parser = argparse.ArgumentParser(description='This script creates basic CMakeLists.txt and .cmake files for a waf based LY project',
  219. formatter_class=argparse.RawTextHelpFormatter)
  220. parser.add_argument('path_to_projects', type=str, nargs='+',
  221. help='list of project directories to create CMakeLists.txt and .cmake files within and add to p4')
  222. args = parser.parse_args()
  223. for input_path in args.path_to_projects:
  224. if not os.path.isdir(input_path):
  225. print('Expected a valid path, got {}'.format(input_path))
  226. sys.exit(1)
  227. project_path = os.path.abspath(input_path)
  228. project_name = os.path.basename(project_path)
  229. project_gem_path = os.path.abspath(os.path.join(project_path, 'Gem'))
  230. project_gem_code_path = os.path.abspath(os.path.join(project_gem_path, 'Code'))
  231. gems_json_file = os.path.join(project_path, 'gems.json')
  232. project_gem_json_file = os.path.join(project_gem_path, 'gem.json')
  233. with open(gems_json_file) as f:
  234. gems_json_dict = json.load(f)
  235. if not gems_json_dict:
  236. print(f'Could not load gems.json for project {project_name}')
  237. sys.exit(1)
  238. gems_list = gems_json_dict['Gems']
  239. if not gems_list:
  240. print('Invalid Gems list found in gems.json for project {}').format(project_name)
  241. sys.exit(1)
  242. with open(project_gem_json_file) as f:
  243. project_gem_json_dict = json.load(f)
  244. if not project_gem_json_dict:
  245. print(f'Could not load gem.json within the Gem folder for project {project_name}')
  246. sys.exit(1)
  247. project_gem_uuid = project_gem_json_dict['Uuid']
  248. project_gem_version = project_gem_json_dict['Version']
  249. gem_paths = getGemPaths(gems_list, project_path)
  250. toolTime_dependencies, runTime_dependencies = processGemDependencies(gem_paths)
  251. # Create the chain of subdirectory CMakeLists files needed to get to the projects gem code
  252. gemcmake.addSubdirectoryToCMakeLists(os.path.join(project_path, 'CMakeLists.txt'), 'Gem')
  253. gemcmake.addSubdirectoryToCMakeLists(os.path.join(project_gem_path, 'CMakeLists.txt'), 'Code')
  254. # Invoke the gem conversion script and pass in our project CMakeLists template
  255. gemcmake.generateCMakeFilesForGem(project_gem_path, project_name, project_gem_uuid, project_gem_version, getProjectGemCMakeListsTemplate)
  256. # Generate our tooltime and runtime .cmake files
  257. generateCMakeFilesForProjectGemDependencies(toolTime_dependencies, runTime_dependencies, project_gem_code_path)
  258. #entrypoint
  259. if __name__ == '__main__':
  260. main()