mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-19 21:37:57 +08:00
347 lines
11 KiB
Python
347 lines
11 KiB
Python
# Released under the MIT License. See LICENSE for details.
|
|
#
|
|
# pylint: disable=too-many-lines
|
|
"""A nice collection of ready-to-use pcommands for this package."""
|
|
from __future__ import annotations
|
|
|
|
# Note: import as little as possible here at the module level to
|
|
# keep launch times fast for small snippets.
|
|
import sys
|
|
|
|
from efrotools.pcommand import PROJROOT
|
|
|
|
|
|
def gen_monolithic_register_modules() -> None:
|
|
"""Generate .h file for registering py modules in monolithic builds."""
|
|
import os
|
|
import textwrap
|
|
|
|
from efro.error import CleanError
|
|
from batools.featureset import FeatureSet
|
|
|
|
if len(sys.argv) != 3:
|
|
raise CleanError('Expected 1 arg.')
|
|
outpath = sys.argv[2]
|
|
|
|
featuresets = FeatureSet.get_all_for_project(str(PROJROOT))
|
|
|
|
# Filter out ones without native modules.
|
|
featuresets = [f for f in featuresets if f.has_native_python_module]
|
|
|
|
pymodulenames = sorted(f.name_python_binary_module for f in featuresets)
|
|
|
|
extern_def_code = '\n'.join(
|
|
f'auto PyInit_{n}() -> PyObject*;' for n in pymodulenames
|
|
)
|
|
py_register_code = '\n'.join(
|
|
f'PyImport_AppendInittab("{n}", &PyInit_{n});' for n in pymodulenames
|
|
)
|
|
base_code = """
|
|
// Released under the MIT License. See LICENSE for details.
|
|
|
|
#ifndef BALLISTICA_CORE_MGEN_PYTHON_MODULES_MONOLITHIC_H_
|
|
#define BALLISTICA_CORE_MGEN_PYTHON_MODULES_MONOLITHIC_H_
|
|
|
|
// THIS CODE IS AUTOGENERATED BY META BUILD; DO NOT EDIT BY HAND.
|
|
|
|
#include "ballistica/shared/python/python_sys.h"
|
|
|
|
#if BA_MONOLITHIC_BUILD
|
|
extern "C" {
|
|
${EXTERN_DEF_CODE}
|
|
}
|
|
#endif // BA_MONOLITHIC_BUILD
|
|
|
|
namespace ballistica {
|
|
|
|
/// Register init calls for all of our built-in Python modules.
|
|
/// Should only be used in monolithic builds. In modular builds
|
|
/// binary modules get located as .so files on disk as per regular
|
|
/// Python behavior.
|
|
void MonolithicRegisterPythonModules() {
|
|
#if BA_MONOLITHIC_BUILD
|
|
${PY_REGISTER_CODE}
|
|
#else
|
|
FatalError(
|
|
"MonolithicRegisterPythonModules should not be called"
|
|
" in modular builds.");
|
|
#endif // BA_MONOLITHIC_BUILD
|
|
}
|
|
|
|
} // namespace ballistica
|
|
|
|
#endif // BALLISTICA_CORE_MGEN_PYTHON_MODULES_MONOLITHIC_H_
|
|
"""
|
|
out = (
|
|
textwrap.dedent(base_code)
|
|
.replace('${EXTERN_DEF_CODE}', extern_def_code)
|
|
.replace('${PY_REGISTER_CODE}', textwrap.indent(py_register_code, ' '))
|
|
.strip()
|
|
+ '\n'
|
|
)
|
|
|
|
os.makedirs(os.path.dirname(outpath), exist_ok=True)
|
|
with open(outpath, 'w', encoding='utf-8') as outfile:
|
|
outfile.write(out)
|
|
|
|
|
|
def stage_server_file() -> None:
|
|
"""Stage files for the server environment with some filtering."""
|
|
from efro.error import CleanError
|
|
import batools.assetstaging
|
|
|
|
if len(sys.argv) != 5:
|
|
raise CleanError('Expected 3 args (mode, infile, outfile).')
|
|
mode, infilename, outfilename = sys.argv[2], sys.argv[3], sys.argv[4]
|
|
batools.assetstaging.stage_server_file(
|
|
str(PROJROOT), mode, infilename, outfilename
|
|
)
|
|
|
|
|
|
def py_examine() -> None:
|
|
"""Run a python examination at a given point in a given file."""
|
|
import os
|
|
from pathlib import Path
|
|
import efrotools
|
|
|
|
if len(sys.argv) != 7:
|
|
print('ERROR: expected 7 args')
|
|
sys.exit(255)
|
|
filename = Path(sys.argv[2])
|
|
line = int(sys.argv[3])
|
|
column = int(sys.argv[4])
|
|
selection: str | None = None if sys.argv[5] == '' else sys.argv[5]
|
|
operation = sys.argv[6]
|
|
|
|
# This stuff assumes it is being run from project root.
|
|
os.chdir(PROJROOT)
|
|
|
|
# Set up pypaths so our main distro stuff works.
|
|
scriptsdir = os.path.abspath(
|
|
os.path.join(
|
|
os.path.dirname(sys.argv[0]), '../src/assets/ba_data/python'
|
|
)
|
|
)
|
|
toolsdir = os.path.abspath(
|
|
os.path.join(os.path.dirname(sys.argv[0]), '../tools')
|
|
)
|
|
if scriptsdir not in sys.path:
|
|
sys.path.append(scriptsdir)
|
|
if toolsdir not in sys.path:
|
|
sys.path.append(toolsdir)
|
|
efrotools.py_examine(PROJROOT, filename, line, column, selection, operation)
|
|
|
|
|
|
def clean_orphaned_assets() -> None:
|
|
"""Remove asset files that are no longer part of the build."""
|
|
import os
|
|
import json
|
|
import subprocess
|
|
|
|
# Operate from dist root..
|
|
os.chdir(PROJROOT)
|
|
|
|
# Our manifest is split into 2 files (public and private)
|
|
with open(
|
|
'src/assets/.asset_manifest_public.json', encoding='utf-8'
|
|
) as infile:
|
|
manifest = set(json.loads(infile.read()))
|
|
with open(
|
|
'src/assets/.asset_manifest_private.json', encoding='utf-8'
|
|
) as infile:
|
|
manifest.update(set(json.loads(infile.read())))
|
|
for root, _dirs, fnames in os.walk('build/assets'):
|
|
for fname in fnames:
|
|
fpath = os.path.join(root, fname)
|
|
fpathrel = fpath[13:] # paths are relative to build/assets
|
|
if fpathrel not in manifest:
|
|
print(f'Removing orphaned asset file: {fpath}')
|
|
os.unlink(fpath)
|
|
|
|
# Lastly, clear empty dirs.
|
|
subprocess.run(
|
|
'find build/assets -depth -empty -type d -delete',
|
|
shell=True,
|
|
check=True,
|
|
)
|
|
|
|
|
|
def win_ci_install_prereqs() -> None:
|
|
"""Install bits needed for basic win ci."""
|
|
import json
|
|
from efrotools.efrocache import get_target
|
|
|
|
# We'll need to pull a handful of things out of efrocache for the
|
|
# build to succeed. Normally this would happen through our Makefile
|
|
# targets but we can't use them under raw window so we need to just
|
|
# hard-code whatever we need here.
|
|
lib_dbg_win32 = 'build/prefab/lib/windows/Debug_Win32'
|
|
needed_targets: set[str] = {
|
|
f'{lib_dbg_win32}/BallisticaKitGenericPlus.lib',
|
|
f'{lib_dbg_win32}/BallisticaKitGenericPlus.pdb',
|
|
'ballisticakit-windows/Generic/BallisticaKit.ico',
|
|
}
|
|
|
|
# Look through everything that gets generated by our meta builds
|
|
# and pick out anything we need for our basic builds/tests.
|
|
with open(
|
|
'src/meta/.meta_manifest_public.json', encoding='utf-8'
|
|
) as infile:
|
|
meta_public: list[str] = json.loads(infile.read())
|
|
with open(
|
|
'src/meta/.meta_manifest_private.json', encoding='utf-8'
|
|
) as infile:
|
|
meta_private: list[str] = json.loads(infile.read())
|
|
for target in meta_public + meta_private:
|
|
if (target.startswith('src/ballistica/') and '/mgen/' in target) or (
|
|
target.startswith('src/assets/ba_data/python/')
|
|
and '/_mgen/' in target
|
|
):
|
|
needed_targets.add(target)
|
|
|
|
for target in needed_targets:
|
|
get_target(target)
|
|
|
|
|
|
def win_ci_binary_build() -> None:
|
|
"""Simple windows binary build for ci."""
|
|
import subprocess
|
|
|
|
# Do the thing.
|
|
subprocess.run(
|
|
[
|
|
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\'
|
|
'Enterprise\\MSBuild\\Current\\Bin\\MSBuild.exe',
|
|
'ballisticakit-windows\\Generic\\BallisticaKitGeneric.vcxproj',
|
|
'-target:Build',
|
|
'-property:Configuration=Debug',
|
|
'-property:Platform=Win32',
|
|
'-property:VisualStudioVersion=16',
|
|
],
|
|
check=True,
|
|
)
|
|
|
|
|
|
def update_cmake_prefab_lib() -> None:
|
|
"""Update prefab internal libs for builds."""
|
|
import subprocess
|
|
import os
|
|
from efro.error import CleanError
|
|
import batools.build
|
|
|
|
if len(sys.argv) != 5:
|
|
raise CleanError(
|
|
'Expected 3 args (standard/server, debug/release, build-dir)'
|
|
)
|
|
buildtype = sys.argv[2]
|
|
mode = sys.argv[3]
|
|
builddir = sys.argv[4]
|
|
if buildtype not in {'standard', 'server'}:
|
|
raise CleanError(f'Invalid buildtype: {buildtype}')
|
|
if mode not in {'debug', 'release'}:
|
|
raise CleanError(f'Invalid mode: {mode}')
|
|
platform = batools.build.get_current_prefab_platform(
|
|
wsl_gives_windows=False
|
|
)
|
|
suffix = '_server' if buildtype == 'server' else '_gui'
|
|
target = (
|
|
f'build/prefab/lib/{platform}{suffix}/{mode}/' f'libballistica_plus.a'
|
|
)
|
|
|
|
# Build the target and then copy it to dst if it doesn't exist there yet
|
|
# or the existing one is older than our target.
|
|
subprocess.run(['make', target], check=True)
|
|
|
|
libdir = os.path.join(builddir, 'prefablib')
|
|
libpath = os.path.join(libdir, 'libballistica_plus.a')
|
|
|
|
update = True
|
|
time1 = os.path.getmtime(target)
|
|
if os.path.exists(libpath):
|
|
time2 = os.path.getmtime(libpath)
|
|
if time1 <= time2:
|
|
update = False
|
|
|
|
if update:
|
|
if not os.path.exists(libdir):
|
|
os.makedirs(libdir, exist_ok=True)
|
|
subprocess.run(['cp', target, libdir], check=True)
|
|
|
|
|
|
def android_archive_unstripped_libs() -> None:
|
|
"""Copy libs to a build archive."""
|
|
import subprocess
|
|
from pathlib import Path
|
|
from efro.error import CleanError
|
|
from efro.terminal import Clr
|
|
|
|
if len(sys.argv) != 4:
|
|
raise CleanError('Expected 2 args; src-dir and dst-dir')
|
|
src = Path(sys.argv[2])
|
|
dst = Path(sys.argv[3])
|
|
if dst.exists():
|
|
subprocess.run(['rm', '-rf', dst], check=True)
|
|
dst.mkdir(parents=True, exist_ok=True)
|
|
if not src.is_dir():
|
|
raise CleanError(f"Source dir not found: '{src}'")
|
|
libname = 'libmain'
|
|
libext = '.so'
|
|
for abi, abishort in [
|
|
('armeabi-v7a', 'arm'),
|
|
('arm64-v8a', 'arm64'),
|
|
('x86', 'x86'),
|
|
('x86_64', 'x86-64'),
|
|
]:
|
|
srcpath = Path(src, abi, libname + libext)
|
|
dstname = f'{libname}_{abishort}{libext}'
|
|
dstpath = Path(dst, dstname)
|
|
if srcpath.exists():
|
|
print(f'Archiving unstripped library: {Clr.BLD}{dstname}{Clr.RST}')
|
|
subprocess.run(['cp', srcpath, dstpath], check=True)
|
|
subprocess.run(
|
|
['tar', '-zcf', dstname + '.tgz', dstname], cwd=dst, check=True
|
|
)
|
|
subprocess.run(['rm', dstpath], check=True)
|
|
|
|
|
|
def spinoff_test() -> None:
|
|
"""Test spinoff functionality."""
|
|
import os
|
|
import subprocess
|
|
|
|
from efro.terminal import Clr
|
|
from efro.error import CleanError
|
|
|
|
args = sys.argv[2:]
|
|
if len(args) != 1:
|
|
raise CleanError('Expected 1 arg.')
|
|
testtype = args[0]
|
|
if testtype == 'empty':
|
|
path = 'build/spinofftest/empty'
|
|
print(
|
|
f'{Clr.SBLU}{Clr.BLD}Running spinoff test'
|
|
f" {Clr.RST}{Clr.BLD}'{testtype}'{Clr.RST}{Clr.SBLU}{Clr.BLD}"
|
|
f'...{Clr.RST}',
|
|
flush=True,
|
|
)
|
|
subprocess.run(['rm', '-rf', path], check=True)
|
|
cmd = [
|
|
'./tools/spinoff',
|
|
'create',
|
|
'SpinoffTest',
|
|
path,
|
|
'--featuresets',
|
|
'none',
|
|
]
|
|
print(Clr.MAG + ' '.join(cmd) + Clr.RST)
|
|
subprocess.run(
|
|
cmd + ['--noninteractive'],
|
|
check=True,
|
|
)
|
|
os.makedirs(path, exist_ok=True)
|
|
print(f'{Clr.MAG}tools/spinoff update{Clr.RST}', flush=True)
|
|
subprocess.run(['tools/spinoff', 'update'], cwd=path, check=True)
|
|
subprocess.run(['make', 'cmake-server-binary'], cwd=path, check=True)
|
|
else:
|
|
raise CleanError(f"Invalid test type '{testtype}'.")
|