Files

269 lines
9.1 KiB
Python

#!/usr/bin/env python
import subprocess
import os
import sys
import time
import shutil
import argparse
from typing import Optional, Iterable
PYTHON_PATH = sys.executable
def print_error(*s: str):
print("\033[91m {}\033[00m".format(' '.join(s)))
def print_succes(*s: str):
print("\033[92m {}\033[00m".format(' '.join(s)))
def print_info(*s: str):
print("\033[93m {}\033[00m".format(' '.join(s)))
def build_type_mismatch(debug: bool) -> bool:
has_mismatch = False
if os.path.exists("build/build.cache"):
with open("build/build.cache", 'rb') as f:
f.seek(0, 2)
pos = f.tell()
if pos:
f.seek(0)
cur_mode = b'\x00' if debug else b'\x01'
old_mode = f.read(1)
if old_mode != cur_mode:
has_mismatch = True
else:
has_mismatch = True
else:
has_mismatch = True
with open("build/build.cache", 'wb') as f:
f.write(b'\x00' if debug else b'\x01')
return has_mismatch
def clean_build_data(ext_dirs: Iterable[str]):
for ext_dir in ext_dirs:
shutil.rmtree(os.path.join(ext_dir, "build"), ignore_errors=True)
def create_distribution(addon_root_path: str, dist_path: str):
print_info(f'\nCreating WBS distribution in \"{dist_path}\" ...')
os.makedirs(dist_path, exist_ok=True)
final_dist_path = os.path.join(dist_path, 'io_scene_wmo')
os.makedirs(final_dist_path, exist_ok=True)
shutil.copytree(addon_root_path, final_dist_path, dirs_exist_ok=True)
paths_to_remove = (
"__pycache__",
"build",
"venv",
"build.py",
".idea",
".vs",
".vscode",
".gitignore",
"requirements.txt",
"wbs_kernel/build",
"wbs_kernel/cmake-build-debug",
"wbs_kernel/cmake-build-release",
"wbs_kernel/.idea",
"wbs_kernel/.vs",
"wbs_kernel/.vscode",
"wbs_kernel/CmakeLists.txt",
"wbs_kernel/setup.py",
"wbs_kernel/src",
"pywowlib/.git",
"pywowlib/.gitignore",
"pywowlib/.gitmodules",
"pywowlib/.travis.yml",
"pywowlib/build.py",
"pywowlib/cmake.py",
"pywowlib/requirements.txt",
"pywowlib/__pycache__",
"pywowlib/archives/casc/build",
"pywowlib/archives/casc/__pycache__",
"pywowlib/archives/casc/include",
"pywowlib/archives/casc/CASCLib",
"pywowlib/archives/casc/lib",
"pywowlib/archives/casc/casc.cpp",
"pywowlib/archives/casc/setup.py",
"pywowlib/archives/casc/cmake.py",
"pywowlib/archives/casc/requirements.txt",
"pywowlib/archives/casc/.git",
"pywowlib/archives/casc/.gitignore",
"pywowlib/archives/casc/.gitmodules",
"pywowlib/archives/mpq/native/build",
"pywowlib/archives/mpq/native/include",
"pywowlib/archives/mpq/native/lib",
"pywowlib/archives/mpq/native/StormLib",
"pywowlib/archives/mpq/native/python_wrapper.hpp",
"pywowlib/archives/mpq/native/stormmodule.cc",
"pywowlib/archives/mpq/native/stormmodule.h",
"pywowlib/archives/mpq/native/setup.py",
"pywowlib/blp/include",
"pywowlib/blp/BLP2PNG/build",
"pywowlib/blp/BLP2PNG/native",
"pywowlib/blp/BLP2PNG/blp.cpp",
"pywowlib/blp/BLP2PNG/test.py",
"pywowlib/blp/BLP2PNG/setup.py",
"pywowlib/blp/PNG2BLP/build",
"pywowlib/blp/PNG2BLP/native",
"pywowlib/blp/PNG2BLP/png2blp.cpp",
"pywowlib/blp/PNG2BLP/setup.py",
)
for path in paths_to_remove:
new_path = os.path.join(final_dist_path, path)
if not os.path.exists(new_path):
continue
if os.path.isdir(new_path):
shutil.rmtree(new_path, ignore_errors=True)
else:
os.remove(new_path)
print_succes("\nSuccessfully created WBS distribution.")
def build_project(debug: bool, clean: bool, no_req: bool, dist_path: Optional[str]):
start_time = time.time()
print_info('\nBuilding WoW Blender Studio...')
print('Detected build parameters:')
print(f'Build type: {"Debug" if debug else "Release"}')
print(f'Build mode: {"Clean" if clean else "Incremental"}')
print(f'Python third-party modules: {"OFF" if no_req else "ON"}')
do_distribute = bool(dist_path)
try:
import Cython
except ImportError:
print_error("\nCython is required to build this project")
sys.exit(1)
try:
from pip import main as pipmain
except ImportError:
try:
from pip._internal import main as pipmain
except ImportError:
print_error("\npip is required to build this project.")
sys.exit(1)
addon_root_path = os.path.realpath(os.path.dirname(os.path.abspath(__file__)).replace('\\', '/'))
extension_dirs = (
"wbs_kernel/",
)
os.chdir(addon_root_path)
os.makedirs("build", exist_ok=True)
# clean up build data if previous build type does not match with current, or if set manually with an option
if build_type_mismatch(debug) or clean:
clean = True
clean_build_data(extension_dirs)
print_info('\nBuilding C++ extensions...')
for module_relpath in extension_dirs:
try:
os.chdir(os.path.join(addon_root_path, module_relpath))
launch_args = [PYTHON_PATH, "setup.py", 'build_clib', 'build_ext', '--inplace']
if debug:
launch_args.append('--wbs_debug')
status = subprocess.call(launch_args)
if status:
print_error(f"\nProcess returned error code {status} while building module \"{module_relpath}\"")
sys.exit(1)
except PermissionError:
print_error("\nThis build script may need to be called with admin (root) rights.")
sys.exit(1)
except RuntimeError:
print_error("\nUnknown error occurred.")
sys.exit(1)
# build pywowlib
os.chdir(addon_root_path)
launch_args = [PYTHON_PATH, "pywowlib/build.py"]
if debug:
launch_args.append('--debug')
if clean:
launch_args.append('--clean')
status = subprocess.call(launch_args)
if status:
print_error("\nError building pywowlib.")
sys.exit(1)
# install required Python modules
if not no_req:
print_info('\nInstalling third-party Python modules...')
def install_requirements(f):
for line in f.readlines():
status = subprocess.call([PYTHON_PATH, '-m', 'pip', 'install', line, '-t', 'third_party', '--upgrade'])
if status:
print('\nError: failed installing module \"{}\". See pip error above.'.format(line))
sys.exit(1)
with open('requirements.txt') as f:
install_requirements(f)
with open('pywowlib/requirements.txt') as f:
install_requirements(f)
else:
print_info("\nWarning: Third-party Python modules will not be installed. (--noreq option)")
print_succes('\nWBS building finished successfully.',
"\nTotal build time: ", time.strftime("%M minutes %S seconds\a", time.gmtime(time.time() - start_time)))
if do_distribute:
create_distribution(addon_root_path, dist_path)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Build WoW Blender Studio."
"\nMake sure your Python version matches the version of Python"
" inside the target Blender distribution."
"\n"
"\nRequired dependencies are:"
"\n All:"
"\n * pip (https://pip.pypa.io/en/stable/installation/)"
"\n * Cython (pip install Cython)"
"\n * CMake (commandline CMake is required, see: https://cmake.org"
"\n * C++ compiler (MSVC for Windows, GCC/Clang for Linux/Mac)"
"\n"
"\n Linux:"
"\n * OpenGL (sudo apt install libglu1-mesa-dev freeglut3-dev)"
"\n * Python headers (sudo apt install python3-dev)"
, formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('--dist', type=str, help='create a distribution of WBS in specified directory')
parser.add_argument('--debug', action='store_true', help='compile WBS in debug mode')
parser.add_argument('--clean', action='store_true', help='erase previous build files and recompile from scratch')
parser.add_argument('--noreq', action='store_true', help='do not pull python modules from PyPi')
args = parser.parse_args()
build_project(args.debug, args.clean, args.noreq, args.dist)