You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/cmake/projectcmake.py

315 lines
11 KiB
Python

#
# 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
#
#
import json
import sys
import os
import subprocess
import argparse
import glob
import waffiles2cmake
import gemcmake
def getProjectGemCMakeListsTemplate():
return """ly_add_target(
NAME {GEM_NAME}.Static STATIC
NAMESPACE Gem
FILES_CMAKE
{GEM_NAME_LOWERCASE}_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
#AZ::AzCore
)
ly_add_target(
NAME {GEM_NAME} ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
NAMESPACE Gem
FILES_CMAKE
{GEM_NAME_LOWERCASE}_shared_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
Gem::{GEM_NAME}.Static
)
if(PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME {GEM_NAME}.Editor GEM_MODULE
NAMESPACE Gem
FILES_CMAKE
{GEM_NAME_LOWERCASE}_editor_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
#AZ::AzCore
)
endif()
################################################################################
# Gem dependencies
################################################################################
ly_add_project_dependencies(
PROJECT_NAME
{GEM_NAME}
TARGETS
{GEM_NAME}.GameLauncher
DEPENDENCIES_FILES
runtime_dependencies.cmake
)
if(PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_project_dependencies(
PROJECT_NAME
{GEM_NAME}
TARGETS
AssetBuilder
AssetProcessor
AssetProcessorBatch
Editor
DEPENDENCIES_FILES
tool_dependencies.cmake
)
endif()
################################################################################
# Tests
################################################################################
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_target(
NAME {GEM_NAME}.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE Gem
FILES_CMAKE
{GEM_NAME_LOWERCASE}_tests_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Tests
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
Gem::{GEM_NAME}.Static
)
ly_add_googletest(
NAME {GEM_NAME}.Tests
)
endif()
"""
def getEmptyGemDependencyCMakeFormat():
return """set(GEM_DEPENDENCIES
"""
def getGemPaths(gems_list, project_path):
gem_paths = []
# Get the parent directory of the project
# If this is not an external project this should be the dev folder for the LY install
project_parent_path = os.path.abspath(os.path.join(project_path, os.pardir))
for gem_json in gems_list:
gem_path = gem_json['Path']
# First check if this Gem is found within the project itself
project_gem_path = os.path.normcase(os.path.join(project_path, gem_path))
# If so then store the path
if os.path.exists(project_gem_path):
gem_paths.append(project_gem_path)
continue
# Otherwise check if the Gem can be found in the parent directory's Gems folder
parent_gems_path = os.path.normcase(os.path.join(project_parent_path, gem_path))
if os.path.exists(parent_gems_path):
gem_paths.append(parent_gems_path)
else:
# Could not find the gem in the project or in the LY install
print(f'Could not find full path for {gem_path}')
sys.exit(1)
return gem_paths
def getGemJson(gem_path):
# Since the gem.json file can live in subdirectories of the gem root
# walk the directory tree until we come across it
gem_json_path = None
for root, dirs, files in os.walk(gem_path):
for filename in files:
if filename == 'gem.json':
gem_json_path = os.path.join(root, filename)
# load and return the gem.json for this particular gem
if gem_json_path:
with open(gem_json_path) as f:
gem_json = json.load(f)
return gem_json
def processGemDependencies(gem_paths):
toolTime_gems = []
runTime_gems = []
for gem_path in gem_paths:
#Acquire the gem.json info for the gem located at this path
gem_json = getGemJson(gem_path)
if gem_json is None:
print(f'Could not find gem.json file for gem located at {gem_path}')
sys.exit(1)
# Filter out asset only gems
if 'LinkType' in gem_json:
link_type = gem_json['LinkType']
if link_type == 'NoCode':
continue
# Assume there's always a game gem
has_editor_gem = False
has_game_gem = True
has_modules = 'Modules' in gem_json
gem_name = gem_json['Name']
if gem_name is None:
print(f'gem.json for gem located at {gem_path} does not have a Name field')
sys.exit(1)
if has_modules:
# If only an EditorModule is declared there is no GameModule
has_game_gem = False
gem_modules = gem_json['Modules']
for module in gem_modules:
if 'Type' not in module:
continue
module_type = module['Type']
if module_type == 'EditorModule':
has_editor_gem = True
elif module_type == 'GameModule':
has_game_gem = True
else:
if 'EditorModule' in gem_json:
editor_module = gem_json['EditorModule']
if editor_module is True:
has_editor_gem = True
if not has_editor_gem and not has_game_gem:
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')
sys.exit(1)
# If an EditorModule was explicitly declared then we will load the .Editor form
# Otherwise we will still add the game module to our toolTime list
if has_editor_gem:
toolTime_gems.append(f'{gem_name}.Editor')
else:
toolTime_gems.append(gem_name);
if has_game_gem:
runTime_gems.append(gem_name)
return toolTime_gems, runTime_gems
def generateCMakeFilesForProjectGemDependencies(toolTime_dependencies, runTime_dependencies, project_code_path):
toolTime_filePath = os.path.join(project_code_path, 'tool_dependencies.cmake')
runTime_filePath = os.path.join(project_code_path, 'runtime_dependencies.cmake')
# Generate tool_dependencies.cmake
with open(toolTime_filePath, 'w') as toolTime_file:
toolTime_file.write(gemcmake.getCopyright())
toolTime_file.write(getEmptyGemDependencyCMakeFormat())
# Add each dependent tooltime gem as a line item into the file
for toolTime_dependency in toolTime_dependencies:
toolTime_file.write(f' Gem::{toolTime_dependency}\n')
toolTime_file.write(')')
subprocess.run(['p4', 'add', toolTime_filePath])
# Generate runtime dependencies
with open(runTime_filePath, 'w') as runTime_file:
runTime_file.write(gemcmake.getCopyright())
runTime_file.write(getEmptyGemDependencyCMakeFormat())
# Add each dependent runtime gem as a line item into the file
for runTime_dependency in runTime_dependencies:
runTime_file.write(f' Gem::{runTime_dependency}\n')
runTime_file.write(')')
subprocess.run(['p4', 'add', runTime_filePath])
def main():
parser = argparse.ArgumentParser(description='This script creates basic CMakeLists.txt and .cmake files for a waf based LY project',
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('path_to_projects', type=str, nargs='+',
help='list of project directories to create CMakeLists.txt and .cmake files within and add to p4')
args = parser.parse_args()
for input_path in args.path_to_projects:
if not os.path.isdir(input_path):
print('Expected a valid path, got {}'.format(input_path))
sys.exit(1)
project_path = os.path.abspath(input_path)
project_name = os.path.basename(project_path)
project_gem_path = os.path.abspath(os.path.join(project_path, 'Gem'))
project_gem_code_path = os.path.abspath(os.path.join(project_gem_path, 'Code'))
gems_json_file = os.path.join(project_path, 'gems.json')
project_gem_json_file = os.path.join(project_gem_path, 'gem.json')
with open(gems_json_file) as f:
gems_json_dict = json.load(f)
if not gems_json_dict:
print(f'Could not load gems.json for project {project_name}')
sys.exit(1)
gems_list = gems_json_dict['Gems']
if not gems_list:
print('Invalid Gems list found in gems.json for project {}').format(project_name)
sys.exit(1)
with open(project_gem_json_file) as f:
project_gem_json_dict = json.load(f)
if not project_gem_json_dict:
print(f'Could not load gem.json within the Gem folder for project {project_name}')
sys.exit(1)
project_gem_uuid = project_gem_json_dict['Uuid']
project_gem_version = project_gem_json_dict['Version']
gem_paths = getGemPaths(gems_list, project_path)
toolTime_dependencies, runTime_dependencies = processGemDependencies(gem_paths)
# Create the chain of subdirectory CMakeLists files needed to get to the projects gem code
gemcmake.addSubdirectoryToCMakeLists(os.path.join(project_path, 'CMakeLists.txt'), 'Gem')
gemcmake.addSubdirectoryToCMakeLists(os.path.join(project_gem_path, 'CMakeLists.txt'), 'Code')
# Invoke the gem conversion script and pass in our project CMakeLists template
gemcmake.generateCMakeFilesForGem(project_gem_path, project_name, project_gem_uuid, project_gem_version, getProjectGemCMakeListsTemplate)
# Generate our tooltime and runtime .cmake files
generateCMakeFilesForProjectGemDependencies(toolTime_dependencies, runTime_dependencies, project_gem_code_path)
#entrypoint
if __name__ == '__main__':
main()