feat(physics): wire physx sdk into build
This commit is contained in:
419
engine/third_party/physx/buildtools/cmake_generate_projects.py
vendored
Normal file
419
engine/third_party/physx/buildtools/cmake_generate_projects.py
vendored
Normal file
@@ -0,0 +1,419 @@
|
||||
import sys
|
||||
import os
|
||||
import glob
|
||||
import os.path
|
||||
import shutil
|
||||
import subprocess
|
||||
import xml.etree.ElementTree
|
||||
|
||||
|
||||
def cmakeExt():
|
||||
if sys.platform == 'win32':
|
||||
return '.exe'
|
||||
return ''
|
||||
|
||||
|
||||
def filterPreset(presetPath):
|
||||
# If this is a file path, extract the actual preset name from XML or filename
|
||||
if os.path.isfile(presetPath):
|
||||
try:
|
||||
presetXml = xml.etree.ElementTree.parse(presetPath).getroot()
|
||||
presetName = presetXml.get('name')
|
||||
except:
|
||||
# Fall back to just using the basename without extension if XML parsing fails
|
||||
basename = os.path.basename(presetPath)
|
||||
presetName = os.path.splitext(basename)[0]
|
||||
else:
|
||||
# If not a file path, assume it's already a preset name
|
||||
presetName = presetPath
|
||||
|
||||
# Platform-specific filtering
|
||||
winPresetFilter = ['win','switch','crosscompile']
|
||||
if sys.platform == 'win32':
|
||||
# On Windows, include presets that contain win, switch, or crosscompile
|
||||
# (but not windows-crosscompile)
|
||||
if any((presetName.find(elem) != -1 and 'windows-crosscompile' not in presetName) for elem in winPresetFilter):
|
||||
return True
|
||||
else:
|
||||
# On non-Windows, include Linux presets and windows-crosscompile
|
||||
# Check for Linux or other Unix/macOS presets (those not containing Windows-specific terms)
|
||||
# Special case: include windows-crosscompile, which is for cross-compiling Windows targets
|
||||
if 'linux' in presetName.lower() or 'mac' in presetName.lower() or 'windows-crosscompile' in presetName:
|
||||
return True
|
||||
if all(presetName.find(elem) == -1 for elem in ['win', 'switch']):
|
||||
return True
|
||||
return False
|
||||
|
||||
def noPresetProvided(physx_root_dir):
|
||||
global input
|
||||
print('Preset parameter required, available presets:')
|
||||
presets_dir = os.path.join(physx_root_dir, "buildtools", "presets")
|
||||
internal_presets = os.path.join(presets_dir, "*.xml")
|
||||
public_presets = os.path.join(presets_dir, "public", "*.xml")
|
||||
|
||||
# Get all XML files in the presets directory
|
||||
internal_preset_files = glob.glob(internal_presets)
|
||||
|
||||
# Check if we have any non-directory XML files directly in presets folder
|
||||
presetfiles = []
|
||||
for file in internal_preset_files:
|
||||
if not os.path.isdir(file): # Make sure it's a file, not a directory
|
||||
basename = os.path.basename(file)
|
||||
dirname = os.path.dirname(file)
|
||||
if os.path.basename(dirname) != "public": # Skip files in public subdirectory
|
||||
presetfiles.append(file)
|
||||
|
||||
# If no XML files in main presets directory, we're in public distribution
|
||||
# So use the files from public directory
|
||||
if len(presetfiles) == 0:
|
||||
print("No presets in main folder, using public presets")
|
||||
presetfiles = glob.glob(public_presets)
|
||||
|
||||
if len(presetfiles) == 0:
|
||||
print("Error: No preset files found. Make sure the directory structure is correct.")
|
||||
exit(1)
|
||||
|
||||
counter = 0
|
||||
presetList = []
|
||||
for preset in presetfiles:
|
||||
if filterPreset(preset):
|
||||
try:
|
||||
presetXml = xml.etree.ElementTree.parse(preset).getroot()
|
||||
if preset.find('user') == -1:
|
||||
print('(' + str(counter) + ') ' + presetXml.get('name') +
|
||||
' <--- ' + presetXml.get('comment'))
|
||||
presetList.append(presetXml.get('name'))
|
||||
else:
|
||||
print('(' + str(counter) + ') ' + presetXml.get('name') +
|
||||
'.user <--- ' + presetXml.get('comment'))
|
||||
presetList.append(presetXml.get('name') + '.user')
|
||||
counter = counter + 1
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not parse preset file {preset}: {e}")
|
||||
continue
|
||||
|
||||
if counter == 0:
|
||||
print("Error: No valid presets found for this platform.")
|
||||
exit(1)
|
||||
|
||||
# Fix Python 2.x.
|
||||
try:
|
||||
input = raw_input
|
||||
except NameError:
|
||||
pass
|
||||
mode = int(eval(input('Enter preset number: ')))
|
||||
return presetList[mode]
|
||||
|
||||
class CMakePreset:
|
||||
presetName = ''
|
||||
targetPlatform = ''
|
||||
compiler = ''
|
||||
generator = ''
|
||||
cmakeSwitches = []
|
||||
cmakeParams = []
|
||||
|
||||
def __init__(self, presetName, physx_root_dir):
|
||||
xmlPath = os.path.join(physx_root_dir, "buildtools", "presets", f"{presetName}.xml")
|
||||
if os.path.isfile(xmlPath):
|
||||
print('Using preset xml: '+xmlPath)
|
||||
else:
|
||||
xmlPath = os.path.join(physx_root_dir, "buildtools", "presets", "public", f"{presetName}.xml")
|
||||
if os.path.isfile(xmlPath):
|
||||
print('Using preset xml: '+xmlPath)
|
||||
else:
|
||||
print('Preset xml file: '+xmlPath+' not found')
|
||||
exit()
|
||||
|
||||
# get the xml
|
||||
presetNode = xml.etree.ElementTree.parse(xmlPath).getroot()
|
||||
self.presetName = presetNode.attrib['name']
|
||||
for platform in presetNode.findall('platform'):
|
||||
self.targetPlatform = platform.attrib['targetPlatform']
|
||||
self.compiler = platform.attrib['compiler']
|
||||
self.generator = platform.get('generator')
|
||||
print('Target platform: ' + self.targetPlatform +
|
||||
' using compiler: ' + self.compiler)
|
||||
if self.generator is not None:
|
||||
print(' using generator: ' + self.generator)
|
||||
|
||||
for cmakeSwitch in presetNode.find('CMakeSwitches'):
|
||||
cmSwitch = '-D' + \
|
||||
cmakeSwitch.attrib['name'] + '=' + \
|
||||
cmakeSwitch.attrib['value'].upper()
|
||||
self.cmakeSwitches.append(cmSwitch)
|
||||
|
||||
for cmakeParam in presetNode.find('CMakeParams'):
|
||||
if cmakeParam.attrib['name'] == 'CMAKE_INSTALL_PREFIX' or cmakeParam.attrib['name'] == 'PX_OUTPUT_LIB_DIR' or cmakeParam.attrib['name'] == 'PX_OUTPUT_EXE_DIR' or cmakeParam.attrib['name'] == 'PX_OUTPUT_DLL_DIR':
|
||||
cmParam = '-D' + cmakeParam.attrib['name'] + '=\"' + \
|
||||
os.environ['PHYSX_ROOT_DIR'] + '/' + \
|
||||
cmakeParam.attrib['value'] + '\"'
|
||||
else:
|
||||
cmParam = '-D' + \
|
||||
cmakeParam.attrib['name'] + '=' + \
|
||||
cmakeParam.attrib['value']
|
||||
self.cmakeParams.append(cmParam)
|
||||
pass
|
||||
|
||||
def isMultiConfigPlatform(self):
|
||||
if self.targetPlatform == 'linux':
|
||||
return False
|
||||
elif self.targetPlatform == 'linuxAarch64':
|
||||
return False
|
||||
elif self.compiler == 'x86_64-w64-mingw32-g++':
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def getCMakeSwitches(self):
|
||||
outString = ''
|
||||
# We need to check both GPU-related switches
|
||||
gpuProjectsEnabled = False
|
||||
gpuProjectsOnlyEnabled = False
|
||||
|
||||
# Define the switch names for clarity and consistency
|
||||
GPU_PROJECTS_SWITCH = 'PX_GENERATE_GPU_PROJECTS'
|
||||
GPU_PROJECTS_ONLY_SWITCH = 'PX_GENERATE_GPU_PROJECTS_ONLY'
|
||||
|
||||
# First pass: Check the state of GPU-related switches
|
||||
gpu_projects_found = False
|
||||
gpu_projects_only_found = False
|
||||
|
||||
for cmakeSwitch in self.cmakeSwitches:
|
||||
# Format of cmakeSwitch is "-DSWITCH_NAME=VALUE"
|
||||
# Use a more flexible approach to match switches
|
||||
if f'-D{GPU_PROJECTS_SWITCH}=' in cmakeSwitch:
|
||||
gpu_projects_found = True
|
||||
gpuProjectsEnabled = cmakeSwitch.endswith('=TRUE')
|
||||
elif f'-D{GPU_PROJECTS_ONLY_SWITCH}=' in cmakeSwitch:
|
||||
gpu_projects_only_found = True
|
||||
gpuProjectsOnlyEnabled = cmakeSwitch.endswith('=TRUE')
|
||||
|
||||
# Log the state of GPU switches for debugging
|
||||
if not gpu_projects_found:
|
||||
print(f"Warning: {GPU_PROJECTS_SWITCH} switch not found in preset. Defaulting to disabled.")
|
||||
if not gpu_projects_only_found:
|
||||
print(f"Warning: {GPU_PROJECTS_ONLY_SWITCH} switch not found in preset. Defaulting to disabled.")
|
||||
|
||||
# Determine if we need to add CUDA paths
|
||||
gpuEnabled = gpuProjectsEnabled or gpuProjectsOnlyEnabled
|
||||
|
||||
# Log GPU status
|
||||
print(f"GPU projects enabled: {gpuEnabled} ({GPU_PROJECTS_SWITCH}={gpuProjectsEnabled}, {GPU_PROJECTS_ONLY_SWITCH}={gpuProjectsOnlyEnabled})")
|
||||
|
||||
# Second pass: Add all switches to output
|
||||
for cmakeSwitch in self.cmakeSwitches:
|
||||
outString = outString + ' ' + cmakeSwitch
|
||||
|
||||
# Only add CUDA paths if GPU is enabled
|
||||
if gpuEnabled:
|
||||
if os.environ.get('PM_CUDA_PATH') is not None:
|
||||
if os.environ.get('PM_CUDA_PATH') is not None:
|
||||
outString = outString + ' -DCUDAToolkit_ROOT_DIR=' + \
|
||||
os.environ['PM_CUDA_PATH']
|
||||
if self.compiler in ['vc15', 'vc16', 'vc17'] and self.generator != 'ninja':
|
||||
outString = outString + ' -T cuda=' + os.environ['PM_CUDA_PATH']
|
||||
# TODO: Need to do the same for gcc (aarch64) when we package it with Packman
|
||||
elif self.compiler == 'clang':
|
||||
if os.environ.get('PM_clang_PATH') is not None:
|
||||
outString = outString + ' -DCMAKE_CUDA_HOST_COMPILER=' + \
|
||||
os.environ['PM_clang_PATH'] + '/bin/clang++'
|
||||
|
||||
return outString
|
||||
|
||||
def getCMakeParams(self):
|
||||
outString = ''
|
||||
for cmakeParam in self.cmakeParams:
|
||||
outString = outString + ' ' + cmakeParam # + ' --trace'
|
||||
return outString
|
||||
|
||||
def getPlatformCMakeParams(self):
|
||||
cmake_modules_root = os.environ['PHYSX_ROOT_DIR'] + '/source/compiler/cmake/modules'
|
||||
outString = ' '
|
||||
|
||||
vs_versions = {
|
||||
'vc15': '\"Visual Studio 15 2017\"',
|
||||
'vc16': '\"Visual Studio 16 2019\"',
|
||||
'vc17': '\"Visual Studio 17 2022\"'
|
||||
}
|
||||
|
||||
# Visual studio
|
||||
if self.compiler in vs_versions:
|
||||
generator = '-G \"Ninja Multi-Config\"' if self.generator == 'ninja' else '-G ' + vs_versions[self.compiler]
|
||||
outString += generator
|
||||
# Windows crosscompile
|
||||
elif self.compiler == 'x86_64-w64-mingw32-g++':
|
||||
outString = outString + '-G \"Ninja\"'
|
||||
# mac
|
||||
elif self.compiler == 'xcode':
|
||||
outString = outString + '-G Xcode'
|
||||
# Linux
|
||||
elif self.targetPlatform in ['linux', 'linuxAarch64']:
|
||||
if self.generator is not None and self.generator == 'ninja':
|
||||
outString = outString + '-G \"Ninja\"'
|
||||
outString = outString + ' -DCMAKE_MAKE_PROGRAM=' + os.environ['PM_ninja_PATH'] + '/ninja'
|
||||
else:
|
||||
outString = outString + '-G \"Unix Makefiles\"'
|
||||
|
||||
if self.targetPlatform == 'win64':
|
||||
if self.generator != 'ninja':
|
||||
outString = outString + ' -Ax64'
|
||||
outString = outString + ' -DTARGET_BUILD_PLATFORM=windows'
|
||||
outString = outString + ' -DPX_OUTPUT_ARCH=x86'
|
||||
if self.compiler == 'x86_64-w64-mingw32-g++':
|
||||
outString = outString + ' -DCMAKE_TOOLCHAIN_FILE=' + \
|
||||
cmake_modules_root + '/linux/WindowsCrossToolchain.linux-unknown-x86_64.cmake'
|
||||
return outString
|
||||
elif self.targetPlatform == 'switch64':
|
||||
outString = outString + ' -DTARGET_BUILD_PLATFORM=switch'
|
||||
outString = outString + ' -DCMAKE_TOOLCHAIN_FILE=' + \
|
||||
cmake_modules_root + '/switch/NX64Toolchain.txt'
|
||||
outString = outString + ' -DCMAKE_GENERATOR_PLATFORM=NX64'
|
||||
return outString
|
||||
elif self.targetPlatform == 'linux':
|
||||
outString = outString + ' -DTARGET_BUILD_PLATFORM=linux'
|
||||
outString = outString + ' -DPX_OUTPUT_ARCH=x86'
|
||||
if self.compiler == 'clang-crosscompile':
|
||||
outString = outString + ' -DCMAKE_TOOLCHAIN_FILE=' + \
|
||||
cmake_modules_root + '/linux/LinuxCrossToolchain.x86_64-unknown-linux-gnu.cmake'
|
||||
outString = outString + ' -DCMAKE_MAKE_PROGRAM=' + os.environ.get('PM_MinGW_PATH') + '/bin/mingw32-make.exe'
|
||||
elif self.compiler == 'clang':
|
||||
if os.environ.get('PM_clang_PATH') is not None:
|
||||
outString = outString + ' -DCMAKE_C_COMPILER=' + \
|
||||
os.environ['PM_clang_PATH'] + '/bin/clang'
|
||||
outString = outString + ' -DCMAKE_CXX_COMPILER=' + \
|
||||
os.environ['PM_clang_PATH'] + '/bin/clang++'
|
||||
else:
|
||||
outString = outString + ' -DCMAKE_C_COMPILER=clang'
|
||||
outString = outString + ' -DCMAKE_CXX_COMPILER=clang++'
|
||||
return outString
|
||||
elif self.targetPlatform == 'linuxAarch64':
|
||||
outString = outString + ' -DTARGET_BUILD_PLATFORM=linux'
|
||||
outString = outString + ' -DPX_OUTPUT_ARCH=arm'
|
||||
if self.compiler == 'clang-crosscompile':
|
||||
outString = outString + ' -DCMAKE_TOOLCHAIN_FILE=' + \
|
||||
cmake_modules_root + '/linux/LinuxCrossToolchain.aarch64-unknown-linux-gnueabihf.cmake'
|
||||
outString = outString + ' -DCMAKE_MAKE_PROGRAM=' + os.environ.get('PM_MinGW_PATH') + '/bin/mingw32-make.exe'
|
||||
elif self.compiler == 'gcc':
|
||||
# TODO: To change so it uses Packman's compiler. Then add it as
|
||||
# host compiler for CUDA above.
|
||||
outString = outString + ' -DCMAKE_TOOLCHAIN_FILE=\"' + \
|
||||
cmake_modules_root + '/linux/LinuxAarch64.cmake\"'
|
||||
elif self.compiler == 'clang':
|
||||
if os.environ.get('PM_clang_PATH') is not None:
|
||||
outString = outString + ' -DCMAKE_C_COMPILER=' + \
|
||||
os.environ['PM_clang_PATH'] + '/bin/clang'
|
||||
outString = outString + ' -DCMAKE_CXX_COMPILER=' + \
|
||||
os.environ['PM_clang_PATH'] + '/bin/clang++'
|
||||
else:
|
||||
outString = outString + ' -DCMAKE_C_COMPILER=clang'
|
||||
outString = outString + ' -DCMAKE_CXX_COMPILER=clang++'
|
||||
|
||||
return outString
|
||||
elif self.targetPlatform == 'mac64':
|
||||
outString = outString + ' -DTARGET_BUILD_PLATFORM=mac'
|
||||
outString = outString + ' -DPX_OUTPUT_ARCH=x86'
|
||||
return outString
|
||||
return ''
|
||||
|
||||
|
||||
def getCommonParams():
|
||||
outString = '--no-warn-unused-cli'
|
||||
outString = outString + ' -DCMAKE_PREFIX_PATH=\"' + os.environ['PM_PATHS'] + '\"'
|
||||
outString = outString + ' -DPHYSX_ROOT_DIR=\"' + \
|
||||
os.environ['PHYSX_ROOT_DIR'] + '\"'
|
||||
outString = outString + ' -DPX_OUTPUT_LIB_DIR=\"' + \
|
||||
os.environ['PHYSX_ROOT_DIR'] + '\"'
|
||||
outString = outString + ' -DPX_OUTPUT_BIN_DIR=\"' + \
|
||||
os.environ['PHYSX_ROOT_DIR'] + '\"'
|
||||
if os.environ.get('GENERATE_SOURCE_DISTRO') == '1':
|
||||
outString = outString + ' -DPX_GENERATE_SOURCE_DISTRO=1'
|
||||
return outString
|
||||
|
||||
def cleanupCompilerDir(compilerDirName):
|
||||
if os.path.exists(compilerDirName):
|
||||
if sys.platform == 'win32':
|
||||
os.system('rmdir /S /Q ' + compilerDirName)
|
||||
else:
|
||||
shutil.rmtree(compilerDirName, True)
|
||||
if os.path.exists(compilerDirName) == False:
|
||||
os.makedirs(compilerDirName)
|
||||
|
||||
def presetProvided(pName, physx_root_dir):
|
||||
parsedPreset = CMakePreset(pName, physx_root_dir)
|
||||
|
||||
print('PM_PATHS: ' + os.environ['PM_PATHS'])
|
||||
|
||||
if os.environ.get('PM_cmake_PATH') is not None:
|
||||
cmakeExec = os.environ['PM_cmake_PATH'] + '/bin/cmake' + cmakeExt()
|
||||
else:
|
||||
cmakeExec = 'cmake' + cmakeExt()
|
||||
print('Cmake: ' + cmakeExec)
|
||||
|
||||
# gather cmake parameters
|
||||
cmakeParams = parsedPreset.getPlatformCMakeParams()
|
||||
cmakeParams = cmakeParams + ' ' + getCommonParams()
|
||||
cmakeParams = cmakeParams + ' ' + parsedPreset.getCMakeSwitches()
|
||||
cmakeParams = cmakeParams + ' ' + parsedPreset.getCMakeParams()
|
||||
# print(cmakeParams)
|
||||
|
||||
if os.path.isfile(physx_root_dir + '/compiler/internal/CMakeLists.txt'):
|
||||
cmakeMasterDir = 'internal'
|
||||
else:
|
||||
cmakeMasterDir = 'public'
|
||||
if parsedPreset.isMultiConfigPlatform():
|
||||
# cleanup and create output directory
|
||||
outputDir = os.path.join(physx_root_dir, 'compiler', parsedPreset.presetName)
|
||||
cleanupCompilerDir(outputDir)
|
||||
|
||||
# run the cmake script
|
||||
#print('Cmake params:' + cmakeParams)
|
||||
os.chdir(outputDir)
|
||||
os.system(cmakeExec + ' \"' +
|
||||
physx_root_dir + '/compiler/' + cmakeMasterDir + '\"' + cmakeParams)
|
||||
os.chdir(physx_root_dir)
|
||||
else:
|
||||
configs = ['debug', 'checked', 'profile', 'release']
|
||||
for config in configs:
|
||||
# cleanup and create output directory
|
||||
outputDir = os.path.join(physx_root_dir, 'compiler', parsedPreset.presetName + '-' + config)
|
||||
cleanupCompilerDir(outputDir)
|
||||
|
||||
# run the cmake script
|
||||
#print('Cmake params:' + cmakeParams)
|
||||
os.chdir(outputDir)
|
||||
# print(cmakeExec + ' \"' + physx_root_dir + '/compiler/' + cmakeMasterDir + '\"' + cmakeParams + ' -DCMAKE_BUILD_TYPE=' + config)
|
||||
os.system(cmakeExec + ' \"' + physx_root_dir + '/compiler/' +
|
||||
cmakeMasterDir + '\"' + cmakeParams + ' -DCMAKE_BUILD_TYPE=' + config)
|
||||
os.chdir(physx_root_dir)
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
if (sys.version_info[0] < 3) or (sys.version_info[0] == 3 and sys.version_info[1] < 5):
|
||||
print("You are using Python {}. You must use Python 3.5 and up. Please read README.md for requirements.").format(sys.version)
|
||||
exit()
|
||||
|
||||
physx_root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
os.environ['PHYSX_ROOT_DIR'] = physx_root_dir.replace("\\", "/")
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
presetName = noPresetProvided(physx_root_dir) # Ensure this function returns the preset name
|
||||
if sys.platform == 'win32':
|
||||
print('Running generate_projects.bat ' + presetName)
|
||||
cmd_path = os.path.join(physx_root_dir, 'generate_projects.bat')
|
||||
cmd = f'"{cmd_path}" {presetName}'
|
||||
result = subprocess.run(cmd, cwd=physx_root_dir, check=True, shell=True, universal_newlines=True)
|
||||
# TODO: catch exception and add capture errors
|
||||
else:
|
||||
print('Running generate_projects.sh ' + presetName)
|
||||
cmd_path = os.path.join(physx_root_dir, 'generate_projects.sh')
|
||||
cmd = [cmd_path, presetName]
|
||||
result = subprocess.run(cmd, cwd=physx_root_dir, check=True, universal_newlines=True)
|
||||
# TODO: catch exception and add capture errors
|
||||
else:
|
||||
presetName = sys.argv[1]
|
||||
if filterPreset(presetName):
|
||||
presetProvided(presetName, physx_root_dir)
|
||||
else:
|
||||
print('Preset not supported on this build platform.')
|
||||
main()
|
||||
Reference in New Issue
Block a user