AnimTestbed/3rdparty/ozz-animation/build-helper.py

389 lines
13 KiB
Python

#!/usr/bin/python
#----------------------------------------------------------------------------#
# #
# ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation #
# and distributed under the MIT License (MIT). #
# #
# Copyright (c) 2019 Guillaume Blanc #
# #
# Permission is hereby granted, free of charge, to any person obtaining a #
# copy of this software and associated documentation files (the "Software"), #
# to deal in the Software without restriction, including without limitation #
# the rights to use, copy, modify, merge, publish, distribute, sublicense, #
# and/or sell copies of the Software, and to permit persons to whom the #
# Software is furnished to do so, subject to the following conditions: #
# #
# The above copyright notice and this permission notice shall be included in #
# all copies or substantial portions of the Software. #
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL #
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING #
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER #
# DEALINGS IN THE SOFTWARE. #
# #
#----------------------------------------------------------------------------#
# CMake python helper script.
import subprocess
import shutil
import sys
import os
import os.path
import re
import platform
from functools import partial
# Build global path variables.
root = os.path.abspath(os.path.join(os.getcwd(), '.'))
build_dir = os.path.join(root, 'build')
build_dir_cc = os.path.join(root, 'build-cc')
cmake_cache_file = os.path.join(build_dir, 'CMakeCache.txt')
ctest_cache_file = os.path.join(build_dir, 'CTestTestfile.cmake')
config = 'Release'
generators = {0: 'default'}
generator = generators[0]
enable_testing = False
emscripten_path = os.environ.get('EMSCRIPTEN')
def ValidateCMake():
try:
# Test that cmake can be executed, silently...
pipe = subprocess.Popen(['cmake'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = pipe.communicate()
except OSError as e:
print("CMake is not installed or properly setup. Please visit www.cmake.org.")
return False
print("CMake is installed and setup properly.")
return True
def CheckEmscripten():
if(emscripten_path == None):
return False
try:
# Test that cmake can be executed, silently...
pipe = subprocess.Popen(['emcc'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = pipe.communicate()
except OSError as e:
print("Emscripten is not installed or properly setup.")
return False
print("Emscripten is installed and setup properly.")
return True
def MakeBuildDir(_build_dir = build_dir):
print("Creating out-of-source build directory: \"" + _build_dir + "\".")
if not os.path.exists(_build_dir):
os.makedirs(_build_dir)
return True
def CleanBuildDir():
print("Deleting out-of-source build directory: \"" + build_dir + "\".")
if os.path.exists(build_dir):
shutil.rmtree(build_dir)
print("Deleting out-of-source cross compilation build directory: \"" + build_dir_cc + "\".")
if os.path.exists(build_dir_cc):
shutil.rmtree(build_dir_cc)
return True
def Configure():
# Configure build process.
print("Configuring build project.")
options = ['cmake']
options += ['-D', 'CMAKE_BUILD_TYPE=' + config]
if (enable_testing) :
options += ['-D', 'ozz_build_tests=1']
else:
options += ['-D', 'ozz_build_tests=0']
global generator
if(generator != 'default'):
options += ['-G', generator]
options += [root]
config_process = subprocess.Popen(options, cwd=build_dir)
config_process.wait()
if(config_process.returncode != 0):
print("Configuration failed.")
return False
print("Configuration succeeded.")
# Updates generator once configuration is complete
generator = DetectGenerator()
return True
def ConfigureCC():
# Configure build process.
print("Configuring emscripten cross compilation build with path: " + emscripten_path)
options = ['cmake']
options += ['-D', 'CMAKE_BUILD_TYPE=' + config]
options += ['-D', 'CMAKE_TOOLCHAIN_FILE=' + emscripten_path + '/cmake/Modules/Platform/Emscripten.cmake']
if(platform.system() == 'Windows'):
options += ['-G', 'MinGW Makefiles']
else:
options += ['-G', 'Unix Makefiles']
options += [root]
config_process = subprocess.Popen(options, cwd=build_dir_cc)
config_process.wait()
if(config_process.returncode != 0):
print("Configuration failed.")
return False
print("Configuration succeeded.")
# Updates generator once configuration is complete
generator = DetectGenerator()
return True
def Build(_build_dir = build_dir):
# Configure build process.
print("Building project.")
options = ['cmake', '--build', _build_dir, '--config', config, '--use-stderr'];
# Appends parallel build option if supported by the generator.
if "Unix Makefiles" in generator:
options += ['--', '-j4']
config_process = subprocess.Popen(options, cwd=_build_dir)
config_process.wait()
if(config_process.returncode != 0):
print("Build failed.")
return False
print("Build succeeded.")
return True
def Test():
# Configure Test process, parallelize a lot of tests in order to stress their dependencies
print("Running unit tests.")
options = ['ctest' ,'--output-on-failure', '-j8', '--build-config', config]
config_process = subprocess.Popen(options, cwd=build_dir)
config_process.wait()
if(config_process.returncode != 0):
print("Testing failed.")
return False
print("Testing succeeded.")
return True
def PackSources(_type):
print("Packing sources.")
options = ['cpack', '-G', _type, '--config', 'CPackSourceConfig.cmake']
config_process = subprocess.Popen(options, cwd=build_dir)
config_process.wait()
if(config_process.returncode != 0):
print("Packing sources of type " + _type + " failed.")
return False
print("Packing sources of type " + _type + " succeeded.")
return True
def PackBinaries(_type, _build_dir = build_dir):
print("Packing binaries.")
options = ['cpack', '-G', _type, '-C', config]
config_process = subprocess.Popen(options, cwd=_build_dir)
config_process.wait()
if(config_process.returncode != 0):
print("Packing binaries of type " + _type + " failed.")
return False
print("Packing binaries of type " + _type + " succeeded.")
return True
def SelecConfig():
configs = {
1: 'Debug',
2: 'Release',
3: 'RelWithDebInfo',
4: 'MinSizeRel'}
while True:
print("Select build configuration:")
for num, message in sorted(configs.iteritems()):
print("%d: %s") % (num, message)
# Get input and check validity
try:
answer = int(raw_input("Enter a value: "))
except:
continue
if not answer in configs:
continue
# Affect global configuration variable
global config
config = configs[answer]
return True
def FindGenerators():
# Finds all generators outputted from cmake usage
process = subprocess.Popen(['cmake', '--help'], stdout=subprocess.PIPE)
stdout = process.communicate()[0]
sub_stdout = stdout[stdout.rfind('Generators'):]
matches = re.findall(r"\s*\**\s*(.+)\s*=.+", sub_stdout, re.MULTILINE)
# Fills generators list
global generators
for match in matches:
generator_name = match.strip()
# Appends also Win64/ARM option if generator is VS
if " [arch]" in generator_name:
gen_name = generator_name[0:len(generator_name) - 7]
generators[len(generators)] = gen_name
generators[len(generators)] = gen_name + " Win64"
generators[len(generators)] = gen_name + " ARM"
else:
generators[len(generators)] = generator_name
def FindInCache(_regex):
try:
cache_file = open(cmake_cache_file)
except:
return None
return re.search(_regex, cache_file.read())
def DetectGenerator():
match = FindInCache(r"CMAKE_GENERATOR:INTERNAL=(.*)")
if match:
global generators
global generator
for num, message in sorted(generators.iteritems()):
if match.group(1) == message:
return message
return 'default'
def SelecGenerator():
global generators
while True:
print("Select generator:")
for num, message in sorted(generators.iteritems()):
print("%d: %s") % (num, message)
# Get input and check validity
try:
answer = int(raw_input("Enter a value: "))
except:
continue
if not answer in generators:
continue
# Check if this is the current generator
current_generator = DetectGenerator()
if current_generator == 'default':
global generator
generator = generators[answer]
return True
if current_generator != generators[answer]:
print("Selected generator '%s' is different from the current one '%s'.") % (generators[answer], current_generator)
clean = raw_input("Do you want to clean build directory to apply the change? (y/n): ") == "y"
if clean:
generator = generators[answer]
return CleanBuildDir()
return True
def DetectTesting():
global enable_testing
enable_testing = os.path.isfile(ctest_cache_file)
def EnableTesting():
global enable_testing
while True:
# Get input and check validity
answer = raw_input("enable testing (y/n): ")
if answer != 'y' and answer != 'n':
continue
wanted = (answer == 'y')
# Get current state
if (enable_testing != wanted):
enable_testing = wanted
print("Testing state has changed.")
return True
def ClearScreen():
os.system('cls' if os.name=='nt' else 'clear')
def Quit():
sys.exit(0)
return True
def main():
# Checks CMake installation is correct.
if not ValidateCMake():
return
# Emscripten is optional
CheckEmscripten()
# Detects available generators
FindGenerators()
# Detects testing state
DetectTesting()
# Update current generator
print("DetectGenerator")
global generator
generator = DetectGenerator()
options = {
'1': ["Build", [MakeBuildDir, Configure, Build]],
'2': ["Run unit tests", [MakeBuildDir, Configure, Build, Test]],
'3': ["Execute CMake generation step (don't build)", [MakeBuildDir, Configure]],
'4': ["Clean out-of-source build directory\n ------------------", [CleanBuildDir]],
'5': ["Pack binaries", [MakeBuildDir, Configure, Build, partial(PackBinaries, "ZIP"), partial(PackBinaries, "TBZ2")]],
'6': ["Pack sources\n ------------------", [MakeBuildDir, Configure, partial(PackSources, "ZIP"), partial(PackSources, "TBZ2")]],
'7': ["Enable testing", [EnableTesting]],
'8': ["Select build configuration", [SelecConfig]],
'9': ["Select cmake generator\n ------------------", [SelecGenerator]],
'q': ["Quit\n------------------", [Quit]]}
# Adds emscripten
global emscripten_path
if emscripten_path != None:
options['1a'] = ["Build emscripten", [partial(MakeBuildDir, build_dir_cc), ConfigureCC, partial(Build, build_dir_cc)]]
options['5a'] = ["Pack emscripten binaries", [MakeBuildDir, Configure, Build, partial(MakeBuildDir, build_dir_cc), ConfigureCC, partial(Build, build_dir_cc), partial(PackBinaries, "ZIP", build_dir_cc)]]
while True:
# Displays options
ClearScreen()
print("ozz CMake build helper tool")
print("")
print("Selected build configuration: %s") % config
print("Selected generator: %s") % generator
print("Testing enabled: %s") % enable_testing
print("")
print("Choose an option:")
print("------------------")
for key, message in sorted(options.iteritems()):
print(" %s: %s") % (key, message[0])
# Get input and check validity
answer = raw_input("Enter a value: ")
if not answer in options:
continue
# Execute command in a try catch to avoid crashes and allow retries.
ClearScreen()
try:
for command in options[answer][1]:
if command():
print("\nExecution success.\n")
else:
print("\nExecution failed.\n")
break
except Exception as e:
print("\nAn error occured during script execution: %s\n") % e
raw_input("Press enter to continue...")
return 0
if __name__ == '__main__':
main()