Exposing more C++ sources

This commit is contained in:
Eric Froemling 2020-10-13 13:41:50 -07:00
parent 85296ea6ab
commit fafd23b673
11 changed files with 3218 additions and 83 deletions

View File

@ -3932,24 +3932,24 @@
"assets/build/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/b5/85/f8b6d0558ddb87267f34254b1450",
"assets/build/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/1c/e1/4a1a2eddda2f4aebd5f8b64ab08e",
"assets/build/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/50/8d/bc2600ac9491f1b14d659709451f",
"build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/31/9f/f2aa794e452ae380c363dab7744e",
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/cf/0d/24472a7bce6c6de12493d506ce64",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/3b/64/e544f697481ca43b38b3568b8a1e",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/70/f0/5df34ce15aa534d8c42cd7235984",
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/62/2c/4ddcde6c2483ab591646c2f49257",
"build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/94/e8/f272b412ca176fde161346bf1363",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d3/b2/63d02079a0fb191abf8366f64814",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/44/67/c3d7dcb5e16ad5451ba7daf2f9bd",
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/e0/2f/1665f879dbc7d9241b84990cffaf",
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/fe/d6/77e6b6d65a3484e4c264be8dbfab",
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/2a/01/3d42c36b2afd5a24d37edc2f6883",
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/f5/89/558acefe3774686fc10f967e737b",
"build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ba/78/292390a40c1e4b9804d7572d7a04",
"build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/4c/a9/568023651355fdd0ce7a865c2872",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/69/cc/a5555af8babdef9948380ede8431",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/42/be/79eec8bc7b2cc914cc6cb8ed0769",
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/66/66/063065ddf0a99cb992e936373f53",
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/30/6f/27022976202b3886ad4a395f1f06",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/9e/55/2caa189c8674ef770c2ab3a7cd7d",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b2/14/f0e7a95e18c4484cf4a7894a3fbd"
"build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/c3/64/d09fa017e267f7239b53ea15e60b",
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/f2/0a/8923a5a2c428fe3c355eb3e9e024",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/8e/da/41e88ab335c7edfa162c4a0de4be",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a0/76/805f87ceafce55784d025a3e9977",
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/90/38/615955c8501a8ee201166bf44e36",
"build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/1d/2a/01fe857df7ef297a7f7ea3886d26",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/0a/f8/59aeb014837f2419d76ee66cb01d",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a2/31/0a4cc16d108f66c9c8f323799103",
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/1d/4b/d1cdb7ad12c6a9a5ce6fef4e54af",
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/69/7b/50ab311eea3401bdecedb322022e",
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/b5/95/dee668a0080fd9d529bc4d7a6ce8",
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/53/b2/0e959273e553034e2d7ff135c8b0",
"build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c4/c0/91eab0d803b753a0a2e1ba46c22f",
"build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/cb/72/fcba4049353a8cdbfec9d7065a3b",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e8/25/986296217583b22aaa1652e67086",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/38/1e/6650391748d1410acf7c26f0eb72",
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d0/bf/73561ee1722ddce9a98d9a8c9b4f",
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fe/1f/13289c985eaefb448c8c9c8e2cfc",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a5/4b/c79bf59874bf83cba38485114851",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e7/3d/d1e07fd3d91655d11787e7b60958"
}

View File

@ -817,14 +817,15 @@ CM_BT_LC = $(shell echo $(CMAKE_BUILD_TYPE) | tr A-Z a-z)
ballisticacore-cmake/.clang-format: .clang-format
@cd ballisticacore-cmake && ln -sf ../.clang-format .
# Simple target for CI to build a binary but no assets/etc.
_cmake-simple-ci-server-build:
rm -rf build/cmake_scsb
mkdir -p build/cmake_scsb
tools/pcommand update_cmake_prefab_lib server debug build/cmake_scsb
cd build/cmake_scsb && \
# Simple target for CI to build a binary but not download/assemble assets/etc.
_cmake-simple-ci-server-build: code
rm -rf build/cmake_simple_ci_server_build
mkdir -p build/cmake_simple_ci_server_build
tools/pcommand update_cmake_prefab_lib \
server debug build/cmake_simple_ci_server_build
cd build/cmake_simple_ci_server_build && \
cmake -DCMAKE_BUILD_TYPE=Debug -DHEADLESS=true ${PWD}/ballisticacore-cmake
cd build/cmake_scsb && ${MAKE} -j${CPUS}
cd build/cmake_simple_ci_server_build && ${MAKE} -j${CPUS}
# Tell make which of these targets don't represent files.
.PHONY: _cmake-simple-ci-server-build

View File

@ -154,8 +154,8 @@ auto IsBootstrapped() -> bool;
/// Internal bits.
auto CreateAppInternal() -> AppInternal*;
auto AppInternalPythonInit() -> PyObject*;
auto AppInternalPythonInit2() -> void;
auto AppInternalInitModule() -> void;
auto AppInternalHasBlessingHash() -> bool;
auto AppInternalPutLog(bool fatal) -> bool;
auto AppInternalAwardAdTickets() -> void;

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,18 @@
# Released under the MIT License. See LICENSE for details.
#
# This file was generated by tools/update_generated_code_makefile.
# Dummy generated-src makefile; nothing here for now.
all: generated_code
generated_code:
generated_code: ../generated/ballistica/binding.inc \
../generated/ballistica/bootstrap.inc
clean:
@rm -rf ../generated
../generated/ballistica/bootstrap.inc : ballistica/bootstrap.py
@../../tools/pcommand gen_flat_data_code $< $@ bootstrap_code
../generated/ballistica/binding.inc : ballistica/binding.py
@../../tools/pcommand gen_binding_code $< $@

View File

@ -0,0 +1,126 @@
# Released under the MIT License. See LICENSE for details.
# Where most of our python-c++ binding happens.
# Python objects should be added here along with their associated c++ enum.
# Run make update to update the project after editing this..
# pylint: disable=missing-module-docstring, missing-function-docstring
# pylint: disable=line-too-long
def get_binding_values() -> object:
from ba import _hooks
import _ba
import json
import copy
import ba
from ba import _lang
from ba import _music
from ba import _input
from ba import _apputils
from ba import _account
from ba import _dependency
from ba import _enums
from ba import _player
# FIXME: There should be no bastd in here;
# should pull in bases from ba which get overridden by bastd (or other).
from bastd.ui.onscreenkeyboard import OnScreenKeyboardWindow
from bastd.ui import party
return (
_ba.client_info_query_response, # kClientInfoQueryResponseCall
_hooks.reset_to_main_menu, # kResetToMainMenuCall
_hooks.set_config_fullscreen_on, # kSetConfigFullscreenOnCall
_hooks.set_config_fullscreen_off, # kSetConfigFullscreenOffCall
_hooks.not_signed_in_screen_message, # kNotSignedInScreenMessageCall
_hooks.connecting_to_party_message, # kConnectingToPartyMessageCall
_hooks.rejecting_invite_already_in_party_message, # kRejectingInviteAlreadyInPartyMessageCall
_hooks.connection_failed_message, # kConnectionFailedMessageCall
_hooks.temporarily_unavailable_message, # kTemporarilyUnavailableMessageCall
_hooks.in_progress_message, # kInProgressMessageCall
_hooks.error_message, # kErrorMessageCall
_hooks.purchase_not_valid_error, # kPurchaseNotValidErrorCall
_hooks.purchase_already_in_progress_error, # kPurchaseAlreadyInProgressErrorCall
_hooks.gear_vr_controller_warning, # kGearVRControllerWarningCall
_hooks.orientation_reset_cb_message, # kVROrientationResetCBMessageCall
_hooks.orientation_reset_message, # kVROrientationResetMessageCall
_hooks.on_app_resume, # kHandleAppResumeCall
_apputils.handle_log, # kHandleLogCall
_hooks.launch_main_menu_session, # kLaunchMainMenuSessionCall
_hooks.language_test_toggle, # kLanguageTestToggleCall
_hooks.award_in_control_achievement, # kAwardInControlAchievementCall
_hooks.award_dual_wielding_achievement, # kAwardDualWieldingAchievementCall
_apputils.print_corrupt_file_error, # kPrintCorruptFileErrorCall
_hooks.play_gong_sound, # kPlayGongSoundCall
_hooks.launch_coop_game, # kLaunchCoopGameCall
_hooks.purchases_restored_message, # kPurchasesRestoredMessageCall
_hooks.dismiss_wii_remotes_window, # kDismissWiiRemotesWindowCall
_hooks.unavailable_message, # kUnavailableMessageCall
_hooks.submit_analytics_counts, # kSubmitAnalyticsCountsCall
_hooks.set_last_ad_network, # kSetLastAdNetworkCall
_hooks.no_game_circle_message, # kNoGameCircleMessageCall
_hooks.empty_call, # kEmptyCall
_hooks.level_icon_press, # kLevelIconPressCall
_hooks.trophy_icon_press, # kTrophyIconPressCall
_hooks.coin_icon_press, # kCoinIconPressCall
_hooks.ticket_icon_press, # kTicketIconPressCall
_hooks.back_button_press, # kBackButtonPressCall
_hooks.friends_button_press, # kFriendsButtonPressCall
_hooks.print_trace, # kPrintTraceCall
_hooks.toggle_fullscreen, # kToggleFullscreenCall
_hooks.party_icon_activate, # kPartyIconActivateCall
_hooks.read_config, # kReadConfigCall
_hooks.ui_remote_press, # kUIRemotePressCall
_hooks.quit_window, # kQuitWindowCall
_hooks.remove_in_game_ads_message, # kRemoveInGameAdsMessageCall
_hooks.telnet_access_request, # kTelnetAccessRequestCall
_hooks.on_app_pause, # kOnAppPauseCall
_hooks.do_quit, # kQuitCall
_hooks.shutdown, # kShutdownCall
_hooks.gc_disable, # kGCDisableCall
_account.show_post_purchase_message, # kShowPostPurchaseMessageCall
_hooks.device_menu_press, # kDeviceMenuPressCall
_hooks.show_url_window, # kShowURLWindowCall
_hooks.party_invite_revoke, # kHandlePartyInviteRevokeCall
_hooks.filter_chat_message, # kFilterChatMessageCall
_hooks.local_chat_message, # kHandleLocalChatMessageCall
ba.ShouldShatterMessage, # kShouldShatterMessageClass
ba.ImpactDamageMessage, # kImpactDamageMessageClass
ba.PickedUpMessage, # kPickedUpMessageClass
ba.DroppedMessage, # kDroppedMessageClass
ba.OutOfBoundsMessage, # kOutOfBoundsMessageClass
ba.PickUpMessage, # kPickUpMessageClass
ba.DropMessage, # kDropMessageClass
ba.app.on_app_launch, # kOnAppLaunchCall
_input.get_device_value, # kGetDeviceValueCall
_input.get_last_player_name_from_input_device, # kGetLastPlayerNameFromInputDeviceCall
copy.deepcopy, # kDeepCopyCall
copy.copy, # kShallowCopyCall
ba.Activity, # kActivityClass
ba.Session, # kSessionClass
json.dumps, # kJsonDumpsCall
json.loads, # kJsonLoadsCall
OnScreenKeyboardWindow, # kOnScreenKeyboardClass
party.handle_party_invite, # kHandlePartyInviteCall
_music.do_play_music, # kDoPlayMusicCall
ba.app.handle_deep_link, # kDeepLinkCall
_lang.get_resource, # kGetResourceCall
_lang.translate, # kTranslateCall
ba.Lstr, # kLStrClass
ba.Call, # kCallClass
_apputils.garbage_collect, # kGarbageCollectCall
ba.ContextError, # kContextError
ba.NotFoundError, # kNotFoundError
ba.NodeNotFoundError, # kNodeNotFoundError
ba.SessionTeamNotFoundError, # kSessionTeamNotFoundError
ba.InputDeviceNotFoundError, # kInputDeviceNotFoundError
ba.DelegateNotFoundError, # kDelegateNotFoundError
ba.SessionPlayerNotFoundError, # kSessionPlayerNotFoundError
ba.WidgetNotFoundError, # kWidgetNotFoundError
ba.ActivityNotFoundError, # kActivityNotFoundError
ba.SessionNotFoundError, # kSessionNotFoundError
_dependency.AssetPackage, # kAssetPackageClass
_enums.TimeFormat, # kTimeFormatClass
_enums.TimeType, # kTimeTypeClass
_enums.InputType, # kInputTypeClass
_enums.Permission, # kPermissionClass
_enums.SpecialChar, # kSpecialCharClass
_player.Player, # kPlayerClass
_hooks.get_player_icon, # kGetPlayerIconCall
_lang.Lstr.from_json, # kLstrFromJsonCall
) # yapf: disable

View File

@ -0,0 +1,147 @@
# Released under the MIT License. See LICENSE for details.
"""Initial ba bootstrapping."""
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Any, TextIO, Callable, List
def _ballistica_bootstrap() -> object:
import sys
import signal
import _ba
import threading
class _BAConsoleRedirect:
def __init__(self, original: TextIO, call: Callable[[str],
None]) -> None:
self._lock = threading.Lock()
self._linebits: List[str] = []
self._original = original
self._call = call
self._pending_ship = False
def write(self, sval: Any) -> None:
"""Override standard stdout write."""
self._call(sval)
# Now do logging:
# Add it to our accumulated line.
# If the message ends in a newline, we can ship it
# immediately as a log entry. Otherwise, schedule a ship
# next cycle (if it hasn't yet at that point) so that we
# can accumulate subsequent prints.
# (so stuff like print('foo', 123, 'bar') will ship as one entry)
with self._lock:
self._linebits.append(sval)
if sval.endswith('\n'):
self._shiplog()
else:
_ba.pushcall(self._shiplog,
from_other_thread=True,
suppress_other_thread_warning=True)
def _shiplog(self) -> None:
with self._lock:
line = ''.join(self._linebits)
if not line:
return
self._linebits = []
# Log messages aren't expected to have trailing newlines.
if line.endswith('\n'):
line = line[:-1]
_ba.log(line, to_stdout=False)
def flush(self) -> None:
"""Flush the file."""
self._original.flush()
def isatty(self) -> bool:
"""Are we a terminal?"""
return self._original.isatty()
sys.stdout = _BAConsoleRedirect( # type: ignore
sys.stdout, _ba.print_stdout)
sys.stderr = _BAConsoleRedirect( # type: ignore
sys.stderr, _ba.print_stderr)
# Let's lookup mods first (so users can do whatever they want).
# and then our bundled scripts last (don't want to encourage dists
# overriding proper python functionality)
sys.path.insert(0, _ba.env()['python_directory_user'])
sys.path.append(_ba.env()['python_directory_app'])
sys.path.append(_ba.env()['python_directory_app_site'])
# Tell Python to not handle SIGINT itself (it normally generates
# KeyboardInterrupts which make a mess; we want to do a simple
# clean exit). We capture interrupts per-platform in the C++ layer.
# I tried creating a handler in Python but it seemed to often have
# a delay of up to a second before getting called. (not a huge deal
# but I'm picky).
signal.signal(signal.SIGINT, signal.SIG_DFL) # Do default handling.
# ..though it turns out we need to set up our C signal handling AFTER
# we've told Python to disable its own; otherwise (on Mac at least) it
# wipes out our existing C handler.
_ba.setup_sigint()
# Sanity check: we should always be run in UTF-8 mode.
if sys.flags.utf8_mode != 1:
print('ERROR: Python\'s UTF-8 mode is not set.'
' This will likely result in errors.')
# FIXME: I think we should init Python in the main thread, which should
# also avoid these issues. (and also might help us play better with
# Python debuggers?)
# Gloriously hacky workaround here:
# Our 'main' Python thread is the game thread (not the app's main
# thread) which means it has a small stack compared to the main
# thread (at least on apple). Sadly it turns out this causes the
# debug build of Python to blow its stack immediately when doing
# some big imports.
# Normally we'd just give the game thread the same stack size as
# the main thread and that'd be the end of it. However
# we're using std::threads which it turns out have no way to set
# the stack size (as of fall '19). Grumble.
#
# However python threads *can* take custom stack sizes.
# (and it appears they might use the main thread's by default?..)
# ...so as a workaround in the debug version, we can run problematic
# heavy imports here in another thread and all is well.
# If we ever see stack overflows in our release build we'll have
# to take more drastic measures like switching from std::threads
# to pthreads.
if _ba.env()['debug_build']:
def _thread_func() -> None:
# pylint: disable=unused-import
import json
import urllib.request
testthread = threading.Thread(target=_thread_func)
testthread.start()
testthread.join()
# Now spin up our App instance, store it on both _ba and ba,
# and return it to the C++ layer.
# noinspection PyProtectedMember
from ba._app import App
import ba
app = App()
_ba.app = app
ba.app = app
return app
_app_state = _ballistica_bootstrap()
# This code runs in the main namespace, so clean up after ourself.
del _ballistica_bootstrap, TYPE_CHECKING

View File

@ -16,7 +16,7 @@ from efro.error import CleanError
from efro.terminal import Clr
if TYPE_CHECKING:
from typing import List, Sequence, Optional, Any
from typing import List, Sequence, Optional, Any, Dict
# Python pip packages we require for this project.
@ -710,3 +710,61 @@ def update_docs_md(check: bool) -> None:
with open(docs_hash_path, 'w') as outfile:
outfile.write(curhash)
print(f'{docs_path} is up to date.')
def cmake_prep_dir(dirname: str) -> None:
"""Create a dir, recreating it when cmake/python/etc. version changes.
Useful to prevent builds from breaking when cmake or other components
are updated.
"""
import json
from efrotools import PYVER
verfilename = os.path.join(dirname, '.ba_cmake_env')
versions: Dict[str, str]
if os.path.isfile(verfilename):
with open(verfilename) as infile:
versions = json.loads(infile.read())
assert isinstance(versions, dict)
else:
versions = {}
# Get version of installed cmake.
cmake_ver_output = subprocess.run(['cmake', '--version'],
check=True,
capture_output=True).stdout.decode()
cmake_ver = cmake_ver_output.splitlines()[0].split('cmake version ')[1]
cmake_ver_existing = versions.get('cmake')
assert isinstance(cmake_ver_existing, (str, type(None)))
# Get specific version of our target python.
python_ver_output = subprocess.run([f'python{PYVER}', '--version'],
check=True,
capture_output=True).stdout.decode()
python_ver = python_ver_output.splitlines()[0].split('Python ')[1]
python_ver_existing = versions.get('python')
assert isinstance(python_ver_existing, (str, type(None)))
# If they don't match, blow away the dir and write the current version.
if cmake_ver_existing != cmake_ver or python_ver_existing != python_ver:
if (cmake_ver_existing != cmake_ver
and cmake_ver_existing is not None):
print(f'{Clr.BLU}CMake version changed from {cmake_ver_existing}'
f' to {cmake_ver}; clearing existing build at'
f' "{dirname}".{Clr.RST}')
if (python_ver_existing != python_ver
and python_ver_existing is not None):
print(f'{Clr.BLU}Python version changed from {python_ver_existing}'
f' to {python_ver}; clearing existing build at'
f' "{dirname}".{Clr.RST}')
subprocess.run(['rm', '-rf', dirname], check=True)
os.makedirs(dirname, exist_ok=True)
with open(verfilename, 'w') as outfile:
outfile.write(
json.dumps({
'cmake': cmake_ver,
'python': python_ver
}))

109
tools/batools/codegen.py Normal file
View File

@ -0,0 +1,109 @@
# Released under the MIT License. See LICENSE for details.
#
"""Functionality related to code generation."""
from __future__ import annotations
import os
import json
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import List, Sequence, Optional, Any, Dict
def gen_flat_data_code(projroot: str, in_path: str, out_path: str,
var_name: str) -> None:
"""Generate a C++ include file from a Python file."""
out_dir = os.path.dirname(out_path)
if not os.path.exists(out_dir):
os.makedirs(out_dir, exist_ok=True)
with open(in_path, 'rb') as infileb:
svalin = infileb.read()
# JSON should do the trick for us here as far as char escaping/etc.
# There's corner cases where it can differ from C strings but in this
# simple case we shouldn't run into them.
sval_out = f'const char* {var_name} ='
# Store in ballistica's simple xor encryption to at least
# slightly slow down hackers.
sval = svalin
sval1: Optional[bytes]
sval1 = sval
while sval1:
sval_out += ' ' + json.dumps(sval1[:1000].decode())
sval1 = sval1[1000:]
sval_out += ';\n'
pretty_path = os.path.abspath(out_path)
if pretty_path.startswith(projroot + '/'):
pretty_path = pretty_path[len(projroot) + 1:]
print(f'Generating code: {pretty_path}')
with open(out_path, 'w') as outfile:
outfile.write(sval_out)
def gen_binding_code(projroot: str, in_path: str, out_path: str) -> None:
"""Generate binding.inc file."""
out_dir = os.path.dirname(out_path)
if not os.path.exists(out_dir):
os.makedirs(out_dir, exist_ok=True)
# Pull all lines in the embedded list and split into py and c++ names.
with open(in_path) as infile:
pycode = infile.read()
# Double quotes causes errors.
if '"' in pycode:
raise Exception('bindings file can\'t contain double quotes.')
lines = [
l.strip().split(', # ') for l in pycode.splitlines()
if l.startswith(' ')
]
if not all(len(l) == 2 for l in lines):
raise Exception('malformatted data')
# Our C++ code first execs our input as a string.
ccode = ('{const char* bindcode = ' + repr(pycode).replace("'", '"') + ';')
ccode += ('\nint result = PyRun_SimpleString(bindcode);\n'
'if (result != 0) {\n'
' PyErr_PrintEx(0);\n'
' // Use a standard error to avoid a useless stack trace.\n'
' throw std::logic_error("Error fetching required Python'
' objects.");\n'
'}\n')
# Then it grabs the function that was defined and runs it.
ccode += ('PyObject* bindvals = PythonCommand("get_binding_values()",'
' "<get_binding_values>")'
'.RunReturnObj(true);\n'
'if (bindvals == nullptr) {\n'
' // Use a standard error to avoid a useless stack trace.\n'
' throw std::logic_error("Error binding required Python'
' objects.");\n'
'}\n')
# Then it pulls the individual values out of the returned tuple.
for i, line in enumerate(lines):
ccode += ('SetObjCallable(ObjID::' + line[1] +
', PyTuple_GET_ITEM(bindvals, ' + str(i) + '), true);\n')
# Lastly it cleans up after itself.
ccode += ('result = PyRun_SimpleString("del get_binding_values");\n'
'if (result != 0) {\n'
' PyErr_PrintEx(0);\n'
' // Use a standard error to avoid a useless stack trace.\n'
' throw std::logic_error("Error cleaning up after Python'
' binding.");\n'
'}\n'
'}\n')
pretty_path = os.path.abspath(out_path)
if pretty_path.startswith(projroot + '/'):
pretty_path = pretty_path[len(projroot) + 1:]
print('Generating code: ' + pretty_path)
with open(out_path, 'w') as outfile:
outfile.write(ccode)

View File

@ -731,63 +731,33 @@ def cmake_prep_dir() -> None:
Useful to prevent builds from breaking when cmake or other components
are updated.
"""
# pylint: disable=too-many-locals
import os
import subprocess
import json
from efro.error import CleanError
from efro.terminal import Clr
from efrotools import PYVER
import batools.build
if len(sys.argv) != 3:
raise CleanError('Expected 1 arg (dir name)')
dirname = sys.argv[2]
batools.build.cmake_prep_dir(dirname)
verfilename = os.path.join(dirname, '.ba_cmake_env')
versions: Dict[str, str]
if os.path.isfile(verfilename):
with open(verfilename) as infile:
versions = json.loads(infile.read())
assert isinstance(versions, dict)
else:
versions = {}
def gen_binding_code() -> None:
"""Generate binding.inc file."""
from efro.error import CleanError
import batools.codegen
if len(sys.argv) != 4:
raise CleanError('Expected 2 args (srcfile, dstfile)')
inpath = sys.argv[2]
outpath = sys.argv[3]
batools.codegen.gen_binding_code(str(PROJROOT), inpath, outpath)
# Get version of installed cmake.
cmake_ver_output = subprocess.run(['cmake', '--version'],
check=True,
capture_output=True).stdout.decode()
cmake_ver = cmake_ver_output.splitlines()[0].split('cmake version ')[1]
cmake_ver_existing = versions.get('cmake')
assert isinstance(cmake_ver_existing, (str, type(None)))
# Get specific version of our target python.
python_ver_output = subprocess.run([f'python{PYVER}', '--version'],
check=True,
capture_output=True).stdout.decode()
python_ver = python_ver_output.splitlines()[0].split('Python ')[1]
python_ver_existing = versions.get('python')
assert isinstance(python_ver_existing, (str, type(None)))
# If they don't match, blow away the dir and write the current version.
if cmake_ver_existing != cmake_ver or python_ver_existing != python_ver:
if (cmake_ver_existing != cmake_ver
and cmake_ver_existing is not None):
print(f'{Clr.BLU}CMake version changed from {cmake_ver_existing}'
f' to {cmake_ver}; clearing existing build at'
f' "{dirname}".{Clr.RST}')
if (python_ver_existing != python_ver
and python_ver_existing is not None):
print(f'{Clr.BLU}Python version changed from {python_ver_existing}'
f' to {python_ver}; clearing existing build at'
f' "{dirname}".{Clr.RST}')
subprocess.run(['rm', '-rf', dirname], check=True)
os.makedirs(dirname, exist_ok=True)
with open(verfilename, 'w') as outfile:
outfile.write(
json.dumps({
'cmake': cmake_ver,
'python': python_ver
}))
def gen_flat_data_code() -> None:
"""Generate a C++ include file from a Python file."""
from efro.error import CleanError
import batools.codegen
if len(sys.argv) != 5:
raise CleanError('Expected 3 args (srcfile, dstfile, varname)')
inpath = sys.argv[2]
outpath = sys.argv[3]
varname = sys.argv[4]
batools.codegen.gen_flat_data_code(str(PROJROOT), inpath, outpath, varname)

View File

@ -38,7 +38,7 @@ from batools.pcommand import (
prefab_run_var, make_prefab, update_makebob, lazybuild,
android_archive_unstripped_libs, efro_gradle, stage_assets,
update_assets_makefile, update_project, update_cmake_prefab_lib,
cmake_prep_dir)
cmake_prep_dir, gen_binding_code, gen_flat_data_code)
# pylint: enable=unused-import
if TYPE_CHECKING: