getting make spinoff-test-base working

This commit is contained in:
Eric 2023-06-02 22:00:25 -07:00
parent 91cfa632c8
commit 4ae76a45f6
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
20 changed files with 417 additions and 151 deletions

View File

@ -4072,26 +4072,26 @@
"build/assets/workspace/ninjafightplug.py": "https://files.ballistica.net/cache/ba1/c5/09/4f10b8a21ba87aa5509cff7a164b", "build/assets/workspace/ninjafightplug.py": "https://files.ballistica.net/cache/ba1/c5/09/4f10b8a21ba87aa5509cff7a164b",
"build/assets/workspace/onslaughtplug.py": "https://files.ballistica.net/cache/ba1/ff/0a/a354984f9c074dab0676ac7e4877", "build/assets/workspace/onslaughtplug.py": "https://files.ballistica.net/cache/ba1/ff/0a/a354984f9c074dab0676ac7e4877",
"build/assets/workspace/runaroundplug.py": "https://files.ballistica.net/cache/ba1/2a/1c/9ee5db6d1bceca7fa6638fb8abde", "build/assets/workspace/runaroundplug.py": "https://files.ballistica.net/cache/ba1/2a/1c/9ee5db6d1bceca7fa6638fb8abde",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/4f/46/80f41faa8a51075c2472d7429b77", "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/b0/77/0fb608a5a11844433c94237b80fd",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/30/cb/93ac10daaf9475d01f5d1064d358", "build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/6b/37/9122b03565ee2d08d9fc6d30a221",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/34/4e/e2bf216edcd7d6a9a1bee3b55b69", "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/a4/11/0f7c0fe6af5d5e46795f33b53f8e",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/5e/2b/c22b17ffa90f65bc906ae9e477d6", "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/59/f4/7e5d155abb264a76b633d6f4225f",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/cf/ac/1485911ed48dac0c430b18687917", "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/dc/0a/131caf082f3877e5878ae349746d",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/0d/99/2b9a15fa962b5ea354f8de27fe6c", "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/fb/46/34eada1e570cce2107cb55a033e8",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/03/e1/e795c72de227173711da1a162632", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/1d/3e/be779b3740cda2a7d98418d4007a",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/80/26/b82964ce5a5bb8509ca6d7516b71", "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/18/b7/4389f6000decbcde1044180b134a",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/2e/c8/67482dedb186c9dbca2dc66f7699", "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/4a/91/2c3bf6cca0baecc16138db7fe7ff",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/18/fa/c42a603705d7453279689706d2ee", "build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/01/b4/31635bc46cbda94cc73fc019d28c",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/bc/d2/89526b59dee86888c628028a406c", "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/13/3d/50286e4d2fe1fc7507ba20b45ca2",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/1d/e5/7425978a2c0112a46192a76563ce", "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/d7/17/7f186bd856ea7bd18b2fc8d64639",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/ff/cd/21d36a06bbc2322d8e944f392ef0", "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/b9/63/640aa2e767450800ff481c51b665",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/97/ee/e2c3b4a6ee9abb170bd98f38c7de", "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/19/ca/ae8153fb5fd0ea1045d386c31134",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/be/13/229e4c027d0ca4b037dbf67134bd", "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/70/e8/796d18c51690f54efcb3df22c048",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/53/bb/acf5f4b58a156c53e6ff8f0b58ad", "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/e2/8a/0ae529b92bcf4afe5d1c9c797e96",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/f3/d7/426c5cd3ec71438a98474f39ba2c", "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/a3/d7/f415b115cd0e348be8aa75e28101",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/90/79/74d6111711d655963a1e9a2728c5", "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/58/77/7244f67e35fcb0ab4047d5cc6f5e",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/c1/f7/95ff199637a86fe138309e3a5275", "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/91/de/bef5f0a5b9a0c6d50f4e2922a959",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/61/f0/9447fe7637ed96ce1840aa277271", "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/75/a5/42ad0f7c2944b1033e5b12395743",
"build/prefab/lib/linux_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/d4/6a/dd303a200b98a56ba3b100277057", "build/prefab/lib/linux_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/d4/6a/dd303a200b98a56ba3b100277057",
"build/prefab/lib/linux_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/fc/2c/2996c558fb408a548fdd37398c9a", "build/prefab/lib/linux_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/fc/2c/2996c558fb408a548fdd37398c9a",
"build/prefab/lib/linux_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/ed/28/b7a72be7ae1bd2b58dda4b6902a0", "build/prefab/lib/linux_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/ed/28/b7a72be7ae1bd2b58dda4b6902a0",
@ -4108,14 +4108,14 @@
"build/prefab/lib/mac_x86_64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/71/f6/691482915ad58ea1e953cc23d74c", "build/prefab/lib/mac_x86_64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/71/f6/691482915ad58ea1e953cc23d74c",
"build/prefab/lib/mac_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/b8/2b/6ec8c78980a62e3e0ee4b36ece04", "build/prefab/lib/mac_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/b8/2b/6ec8c78980a62e3e0ee4b36ece04",
"build/prefab/lib/mac_x86_64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/4e/56/a95c987b2a371759896b037fea86", "build/prefab/lib/mac_x86_64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/4e/56/a95c987b2a371759896b037fea86",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/2d/55/0b7f8677e535082c9224259aa9d7", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/60/31/7c82d61f768515a6f5f8f20a967b",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/63/2f/b4b402d2f3a47dad74a077db036c", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/ba/38/d98a316972f47a5509be6ab28a96",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/b6/e4/fffa1b46696ecc804b5a66896359", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/a5/8d/8120340f79d9f9244013ef24c9da",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/ca/70/e5eec4f437e387d61ceba7f9fffe", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/84/47/90ccac6eb182c16a973855a160ee",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/47/8c/0142dfbee4871619a8d404898eef", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/5f/7b/ea3d74c27c2272084b459814986f",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/f8/d9/9facb83ac8c0ef39c97c3211ebd2", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/f6/a9/a780cd9b23e680a9f5cc87e5ac21",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/4d/90/79a005d72aa7a4277d06571dcdb5", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/8c/9e/3902170e5e8662ec1fccc720ed48",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/ff/3a/e319ce586b9cb93ac29282569184", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/c9/d4/5fa5dc68d04b906ef22a10eae5ae",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "https://files.ballistica.net/cache/ba1/52/c6/c11130af7b10d6c0321add5518fa", "src/assets/ba_data/python/babase/_mgen/__init__.py": "https://files.ballistica.net/cache/ba1/52/c6/c11130af7b10d6c0321add5518fa",
"src/assets/ba_data/python/babase/_mgen/enums.py": "https://files.ballistica.net/cache/ba1/38/c3/1dedd5e74f2508efc5974c8815a1", "src/assets/ba_data/python/babase/_mgen/enums.py": "https://files.ballistica.net/cache/ba1/38/c3/1dedd5e74f2508efc5974c8815a1",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "https://files.ballistica.net/cache/ba1/d5/4a/0e480a855ce83709bd7f6761107d", "src/ballistica/base/mgen/pyembed/binding_base.inc": "https://files.ballistica.net/cache/ba1/d5/4a/0e480a855ce83709bd7f6761107d",

View File

@ -1,4 +1,4 @@
### 1.7.20 (build 21041, api 8, 2023-06-02) ### 1.7.20 (build 21042, api 8, 2023-06-02)
- This seems like a good time for a `refactoring` release in anticipation of - This seems like a good time for a `refactoring` release in anticipation of
changes coming in 1.8. Basically this means that a lot of things will be changes coming in 1.8. Basically this means that a lot of things will be

View File

@ -13,3 +13,5 @@ from batools.featureset import FeatureSet
fset = FeatureSet.get_active() fset = FeatureSet.get_active()
fset.requirements = {'core'} fset.requirements = {'core'}
fset.soft_requirements = {'classic', 'plus'}

View File

@ -13,3 +13,9 @@ from batools.featureset import FeatureSet
fset = FeatureSet.get_active() fset = FeatureSet.get_active()
fset.requirements = {'base', 'scene_v1', 'ui_v1'} fset.requirements = {'base', 'scene_v1', 'ui_v1'}
# We provide 'babase.app.classic'.
fset.has_python_app_subsystem = True
# We want things to work without us.
fset.allow_as_soft_requirement = True

View File

@ -14,3 +14,9 @@ fset = FeatureSet.get_active()
fset.requirements = {'base'} fset.requirements = {'base'}
fset.internal = True fset.internal = True
# We provide 'babase.app.plus'.
fset.has_python_app_subsystem = True
# We want things to work without us.
fset.allow_as_soft_requirement = True

View File

@ -98,13 +98,27 @@ ctx.src_unchecked_paths = {
'ballisticakit-android/BallisticaKit/src/*/assets', 'ballisticakit-android/BallisticaKit/src/*/assets',
} }
# Files at or under these paths are considered 'project' files. # Paths/names/suffixes we consider 'project' files.
# These files are synced after all other files and go through # These files are synced after all other files and go through
# batools.project.Updater class as part of their filtering. # batools.project.Updater class as part of their filtering.
# This allows them to update themselves in the same way as they # This allows them to update themselves in the same way as they
# do when running 'make update' for the project; adding the final # do when running 'make update' for the project; adding the final
# filtered set of project source files to themself, etc. # filtered set of project source files to themself, etc.
ctx.project_file_paths = set() ctx.project_file_paths = {'src/assets/ba_data/python/babase/_app.py'}
ctx.project_file_names = {
'Makefile',
'CMakeLists.txt',
'.meta_manifest_public.json',
'.meta_manifest_private.json',
'.asset_manifest_public.json',
'.asset_manifest_private.json',
}
ctx.project_file_suffixes = {
'.vcxproj',
'.vcxproj.filters',
'.pbxproj',
}
# Everything actually synced into dst will use the following filter rules: # Everything actually synced into dst will use the following filter rules:

View File

@ -33,12 +33,13 @@ if TYPE_CHECKING:
from babase._appintent import AppIntent from babase._appintent import AppIntent
from babase._appmode import AppMode from babase._appmode import AppMode
# WOULD-AUTOGEN-BEGIN # __FEATURESET_APP_SUBSYSTEM_IMPORTS_BEGIN__
# This section autogenerated by project-update.
from baclassic import ClassicSubsystem from baclassic import ClassicSubsystem
from baplus import PlusSubsystem from baplus import PlusSubsystem
# WOULD-AUTOGEN-END # __FEATURESET_APP_SUBSYSTEM_IMPORTS_END__
class App: class App:
@ -301,18 +302,6 @@ class App:
# some of this stuff might try importing babase.app and that doesn't # some of this stuff might try importing babase.app and that doesn't
# exist yet as of our __init__() call. # exist yet as of our __init__() call.
# Init classic if present.
# classic_subsystem_type: type[ClassicSubsystem] | None
# try:
# from baclassic import ClassicSubsystem
# classic_subsystem_type = ClassicSubsystem
# except ImportError:
# classic_subsystem_type = None
# if classic_subsystem_type is not None:
# self._classic = classic_subsystem_type()
def _threadpool_no_wait_done(self, fut: Future) -> None: def _threadpool_no_wait_done(self, fut: Future) -> None:
try: try:
fut.result() fut.result()
@ -330,7 +319,8 @@ class App:
fut = self.threadpool.submit(call) fut = self.threadpool.submit(call)
fut.add_done_callback(self._threadpool_no_wait_done) fut.add_done_callback(self._threadpool_no_wait_done)
# WOULD-AUTOGEN-BEGIN # __FEATURESET_APP_SUBSYSTEM_PROPERTIES_BEGIN__
# This section autogenerated by project-update.
@cached_property @cached_property
def classic(self) -> ClassicSubsystem | None: def classic(self) -> ClassicSubsystem | None:
@ -343,7 +333,7 @@ class App:
except ImportError: except ImportError:
return None return None
except Exception: except Exception:
logging.exception('Error importing baclassic') logging.exception('Error importing baclassic.')
return None return None
@cached_property @cached_property
@ -357,10 +347,10 @@ class App:
except ImportError: except ImportError:
return None return None
except Exception: except Exception:
logging.exception('Error importing baplus') logging.exception('Error importing baplus.')
return None return None
# WOULD-AUTOGEN-END # __FEATURESET_APP_SUBSYSTEM_PROPERTIES_END__
def set_intent(self, intent: AppIntent) -> None: def set_intent(self, intent: AppIntent) -> None:
"""Set the intent for the app. """Set the intent for the app.
@ -514,13 +504,14 @@ class App:
"""Decides which app modes to use to handle intents.""" """Decides which app modes to use to handle intents."""
def app_mode_for_intent(self, intent: AppIntent) -> type[AppMode]: def app_mode_for_intent(self, intent: AppIntent) -> type[AppMode]:
# WOULD-AUTOGEN-BEGIN # __DEFAULT_APP_MODE_SELECTION_BEGIN__
# This section autogenerated by project-update.
import bascenev1 import bascenev1
return bascenev1.SceneV1AppMode return bascenev1.SceneV1AppMode
# WOULD-AUTOGEN-END # __DEFAULT_APP_MODE_SELECTION_END__
def on_app_running(self) -> None: def on_app_running(self) -> None:
"""Called when initially entering the running state.""" """Called when initially entering the running state."""

View File

@ -30,7 +30,7 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be # Build number and version of the ballistica binary we expect to be
# using. # using.
TARGET_BALLISTICA_BUILD = 21041 TARGET_BALLISTICA_BUILD = 21042
TARGET_BALLISTICA_VERSION = '1.7.20' TARGET_BALLISTICA_VERSION = '1.7.20'
_g_env_config: EnvConfig | None = None _g_env_config: EnvConfig | None = None

View File

@ -37,6 +37,7 @@ class UIV1SoftInterface {
virtual void OnLanguageChange() = 0; virtual void OnLanguageChange() = 0;
virtual auto GetRootWidget() -> ui_v1::Widget* = 0; virtual auto GetRootWidget() -> ui_v1::Widget* = 0;
virtual auto SendWidgetMessage(const WidgetMessage& m) -> int = 0; virtual auto SendWidgetMessage(const WidgetMessage& m) -> int = 0;
virtual void ApplyAppConfig() = 0;
}; };
} // namespace ballistica::base } // namespace ballistica::base

View File

@ -8,12 +8,10 @@
#include "ballistica/base/input/input.h" #include "ballistica/base/input/input.h"
#include "ballistica/base/logic/logic.h" #include "ballistica/base/logic/logic.h"
#include "ballistica/base/python/base_python.h" #include "ballistica/base/python/base_python.h"
#include "ballistica/base/support/ui_v1_soft.h"
#include "ballistica/base/ui/console.h" #include "ballistica/base/ui/console.h"
#include "ballistica/shared/foundation/event_loop.h" #include "ballistica/shared/foundation/event_loop.h"
#include "ballistica/shared/generic/utils.h" #include "ballistica/shared/generic/utils.h"
#include "ballistica/ui_v1/widget/root_widget.h"
#include "ballistica/ui_v1/widget/stack_widget.h"
#include "ballistica/ui_v1/widget/text_widget.h"
namespace ballistica::base { namespace ballistica::base {
@ -87,9 +85,9 @@ void UI::OnAppShutdown() { assert(g_base->InLogicThread()); }
void UI::ApplyAppConfig() { void UI::ApplyAppConfig() {
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
ui_v1::TextWidget::set_always_use_internal_keyboard( if (g_base->HaveUIV1()) {
g_base->app_config->Resolve( g_base->ui_v1()->ApplyAppConfig();
AppConfig::BoolID::kAlwaysUseInternalKeyboard)); }
} }
auto UI::MainMenuVisible() const -> bool { auto UI::MainMenuVisible() const -> bool {
@ -227,16 +225,6 @@ auto UI::SendWidgetMessage(const WidgetMessage& m) -> int {
return false; return false;
} }
void UI::DeleteWidget(ui_v1::Widget* widget) {
assert(widget);
if (widget) {
ui_v1::ContainerWidget* parent = widget->parent_widget();
if (parent) {
parent->DeleteWidget(widget);
}
}
}
void UI::OnScreenSizeChange() { void UI::OnScreenSizeChange() {
if (g_base->HaveUIV1()) { if (g_base->HaveUIV1()) {
g_base->ui_v1()->OnScreenSizeChange(); g_base->ui_v1()->OnScreenSizeChange();

View File

@ -84,9 +84,6 @@ class UI {
// Send message to the active widget. // Send message to the active widget.
auto SendWidgetMessage(const WidgetMessage& msg) -> int; auto SendWidgetMessage(const WidgetMessage& msg) -> int;
// Use this to destroy any named widget (even those in containers).
void DeleteWidget(ui_v1::Widget* widget);
void SetUIInputDevice(InputDevice* input_device); void SetUIInputDevice(InputDevice* input_device);
// Returns the input-device that currently owns the menu; otherwise nullptr. // Returns the input-device that currently owns the menu; otherwise nullptr.

View File

@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
namespace ballistica { namespace ballistica {
// These are set automatically via script; don't modify them here. // These are set automatically via script; don't modify them here.
const int kEngineBuildNumber = 21041; const int kEngineBuildNumber = 21042;
const char* kEngineVersion = "1.7.20"; const char* kEngineVersion = "1.7.20";
auto MonolithicMain(const core::CoreConfig& core_config) -> int { auto MonolithicMain(const core::CoreConfig& core_config) -> int {

View File

@ -2,6 +2,7 @@
#include "ballistica/ui_v1/ui_v1.h" #include "ballistica/ui_v1/ui_v1.h"
#include "ballistica/base/app/app_config.h"
#include "ballistica/base/app/app_mode.h" #include "ballistica/base/app/app_mode.h"
#include "ballistica/base/graphics/component/empty_component.h" #include "ballistica/base/graphics/component/empty_component.h"
#include "ballistica/base/graphics/graphics.h" #include "ballistica/base/graphics/graphics.h"
@ -12,6 +13,7 @@
#include "ballistica/ui_v1/widget/container_widget.h" #include "ballistica/ui_v1/widget/container_widget.h"
#include "ballistica/ui_v1/widget/root_widget.h" #include "ballistica/ui_v1/widget/root_widget.h"
#include "ballistica/ui_v1/widget/stack_widget.h" #include "ballistica/ui_v1/widget/stack_widget.h"
#include "ballistica/ui_v1/widget/text_widget.h"
namespace ballistica::ui_v1 { namespace ballistica::ui_v1 {
@ -74,7 +76,7 @@ void UIV1FeatureSet::DoHandleDeviceMenuPress(base::InputDevice* device) {
void UIV1FeatureSet::DoShowURL(const std::string& url) { python->ShowURL(url); } void UIV1FeatureSet::DoShowURL(const std::string& url) { python->ShowURL(url); }
void UIV1FeatureSet::DoQuitWindow() { void UIV1FeatureSet::DoQuitWindow() {
g_ui_v1->python->objs().Get(ui_v1::UIV1Python::ObjID::kQuitWindowCall).Call(); g_ui_v1->python->objs().Get(UIV1Python::ObjID::kQuitWindowCall).Call();
} }
RootUI* UIV1FeatureSet::NewRootUI() { return new RootUI(); } RootUI* UIV1FeatureSet::NewRootUI() { return new RootUI(); }
@ -195,7 +197,7 @@ void UIV1FeatureSet::Reset() {
screen_root_widget_.Clear(); screen_root_widget_.Clear();
// (Re)create our screen-root widget. // (Re)create our screen-root widget.
auto sw(Object::New<ui_v1::StackWidget>()); auto sw(Object::New<StackWidget>());
sw->set_is_main_window_stack(true); sw->set_is_main_window_stack(true);
sw->SetWidth(g_base->graphics->screen_virtual_width()); sw->SetWidth(g_base->graphics->screen_virtual_width());
sw->SetHeight(g_base->graphics->screen_virtual_height()); sw->SetHeight(g_base->graphics->screen_virtual_height());
@ -203,7 +205,7 @@ void UIV1FeatureSet::Reset() {
screen_root_widget_ = sw; screen_root_widget_ = sw;
// (Re)create our screen-overlay widget. // (Re)create our screen-overlay widget.
auto ow(Object::New<ui_v1::StackWidget>()); auto ow(Object::New<StackWidget>());
ow->set_is_overlay_window_stack(true); ow->set_is_overlay_window_stack(true);
ow->SetWidth(g_base->graphics->screen_virtual_width()); ow->SetWidth(g_base->graphics->screen_virtual_width());
ow->SetHeight(g_base->graphics->screen_virtual_height()); ow->SetHeight(g_base->graphics->screen_virtual_height());
@ -211,7 +213,7 @@ void UIV1FeatureSet::Reset() {
overlay_root_widget_ = ow; overlay_root_widget_ = ow;
// (Re)create our abs-root widget. // (Re)create our abs-root widget.
auto rw(Object::New<ui_v1::RootWidget>()); auto rw(Object::New<RootWidget>());
root_widget_ = rw; root_widget_ = rw;
rw->SetWidth(g_base->graphics->screen_virtual_width()); rw->SetWidth(g_base->graphics->screen_virtual_width());
rw->SetHeight(g_base->graphics->screen_virtual_height()); rw->SetHeight(g_base->graphics->screen_virtual_height());
@ -267,4 +269,19 @@ auto UIV1FeatureSet::SendWidgetMessage(const base::WidgetMessage& m) -> int {
return root_widget_->HandleMessage(m); return root_widget_->HandleMessage(m);
} }
void UIV1FeatureSet::DeleteWidget(Widget* widget) {
assert(widget);
if (widget) {
ContainerWidget* parent = widget->parent_widget();
if (parent) {
parent->DeleteWidget(widget);
}
}
}
void UIV1FeatureSet::ApplyAppConfig() {
TextWidget::set_always_use_internal_keyboard(g_base->app_config->Resolve(
base::AppConfig::BoolID::kAlwaysUseInternalKeyboard));
}
} // namespace ballistica::ui_v1 } // namespace ballistica::ui_v1

View File

@ -95,10 +95,13 @@ class UIV1FeatureSet : public FeatureSetNativeComponent,
// If a parent is provided, the widget is added to it; otherwise it is added // If a parent is provided, the widget is added to it; otherwise it is added
// to the root widget. // to the root widget.
void AddWidget(Widget* w, ContainerWidget* to); void AddWidget(Widget* w, ContainerWidget* to);
void DeleteWidget(Widget* widget);
void OnScreenSizeChange() override; void OnScreenSizeChange() override;
void OnLanguageChange() override; void OnLanguageChange() override;
auto GetRootWidget() -> ui_v1::Widget* override; auto GetRootWidget() -> ui_v1::Widget* override;
auto SendWidgetMessage(const base::WidgetMessage& m) -> int override; auto SendWidgetMessage(const base::WidgetMessage& m) -> int override;
void ApplyAppConfig() override;
private: private:
UIV1FeatureSet(); UIV1FeatureSet();

View File

@ -820,7 +820,9 @@ void ContainerWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
Object::WeakRef<Widget> weakref(this); Object::WeakRef<Widget> weakref(this);
g_base->logic->event_loop()->PushCall([weakref] { g_base->logic->event_loop()->PushCall([weakref] {
Widget* w = weakref.Get(); Widget* w = weakref.Get();
if (w) g_base->ui->DeleteWidget(w); if (w) {
g_ui_v1->DeleteWidget(w);
}
}); });
return; return;
} }
@ -875,7 +877,9 @@ void ContainerWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
Object::WeakRef<Widget> weakref(this); Object::WeakRef<Widget> weakref(this);
g_base->logic->event_loop()->PushCall([weakref] { g_base->logic->event_loop()->PushCall([weakref] {
Widget* w = weakref.Get(); Widget* w = weakref.Get();
if (w) g_base->ui->DeleteWidget(w); if (w) {
g_ui_v1->DeleteWidget(w);
}
}); });
return; return;
} }

View File

@ -57,6 +57,7 @@ def _get_targets(
def _get_py_targets( def _get_py_targets(
projroot: str, projroot: str,
meta_manifests: dict[str, str], meta_manifests: dict[str, str],
explicit_sources: set[str],
src: str, src: str,
dst: str, dst: str,
py_targets: list[str], py_targets: list[str],
@ -66,14 +67,17 @@ def _get_py_targets(
) -> None: ) -> None:
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
# pylint: disable=too-many-locals # pylint: disable=too-many-locals
# pylint: disable=too-many-statements
py_generated_root = f'{ASSETS_SRC}/ba_data/python/babase/_mgen' py_generated_root = f'{ASSETS_SRC}/ba_data/python/babase/_mgen'
def _do_get_targets(proot: str, fnames: list[str]) -> None: def _do_get_targets(
proot: str, fnames: list[str], is_explicit: bool = False
) -> None:
# Special case: ignore temp py files in data src. # Special case: ignore temp py files in data src.
if proot == f'{ASSETS_SRC}/ba_data/data/maps': if proot == f'{ASSETS_SRC}/ba_data/data/maps':
return return
assert proot.startswith(src) assert proot.startswith(src), f'{proot} does not start with {src}'
assert dst.startswith(BUILD_DIR) assert dst.startswith(BUILD_DIR)
dstrootvar = ( dstrootvar = (
'$(BUILD_DIR)' '$(BUILD_DIR)'
@ -90,6 +94,16 @@ def _get_py_targets(
): ):
continue continue
# Ignore any files in the list of explicit sources we got;
# we explicitly add those at the end and don't want to do it
# twice (since we don't know if this one will always exist
# anyway).
if (
os.path.join(proot, fname) in explicit_sources
and not is_explicit
):
continue
if proot.startswith(f'{ASSETS_SRC}/ba_data/python-site-packages'): if proot.startswith(f'{ASSETS_SRC}/ba_data/python-site-packages'):
in_subset = 'private-common' in_subset = 'private-common'
elif proot.startswith(f'{ASSETS_SRC}/ba_data') or proot.startswith( elif proot.startswith(f'{ASSETS_SRC}/ba_data') or proot.startswith(
@ -122,7 +136,9 @@ def _get_py_targets(
# gamedata pass includes only data; otherwise do all else # gamedata pass includes only data; otherwise do all else
# .py: # .py:
all_targets.add(os.path.join(dstfin, fname)) targetpath = os.path.join(dstfin, fname)
assert targetpath not in all_targets
all_targets.add(targetpath)
py_targets.append(os.path.join(dstrootvar, fname)) py_targets.append(os.path.join(dstrootvar, fname))
# and .pyc: # and .pyc:
@ -173,14 +189,25 @@ def _get_py_targets(
proot=os.path.dirname(target), fnames=[os.path.basename(target)] proot=os.path.dirname(target), fnames=[os.path.basename(target)]
) )
# Now create targets for any explicitly passed paths.
for expsrc in explicit_sources:
if expsrc.startswith(f'{src}/'):
_do_get_targets(
proot=os.path.dirname(expsrc),
fnames=[os.path.basename(expsrc)],
is_explicit=True,
)
def _get_py_targets_subset( def _get_py_targets_subset(
projroot: str, projroot: str,
meta_manifests: dict[str, str], meta_manifests: dict[str, str],
explicit_sources: set[str],
all_targets: set[str], all_targets: set[str],
subset: str, subset: str,
suffix: str, suffix: str,
) -> str: ) -> str:
# pylint: disable=too-many-locals
if subset == 'public_tools': if subset == 'public_tools':
src = 'tools' src = 'tools'
dst = f'{BUILD_DIR}/ba_data/python' dst = f'{BUILD_DIR}/ba_data/python'
@ -197,6 +224,7 @@ def _get_py_targets_subset(
_get_py_targets( _get_py_targets(
projroot, projroot,
meta_manifests, meta_manifests,
explicit_sources,
src, src,
dst, dst,
py_targets, py_targets,
@ -368,6 +396,7 @@ def generate_assets_makefile(
fname: str, fname: str,
existing_data: str, existing_data: str,
meta_manifests: dict[str, str], meta_manifests: dict[str, str],
explicit_sources: set[str],
) -> dict[str, str]: ) -> dict[str, str]:
"""Main script entry point.""" """Main script entry point."""
# pylint: disable=too-many-locals # pylint: disable=too-many-locals
@ -377,8 +406,6 @@ def generate_assets_makefile(
public = getconfig(Path(projroot))['public'] public = getconfig(Path(projroot))['public']
assert isinstance(public, bool) assert isinstance(public, bool)
# with open(fname, encoding='utf-8') as infile:
# original = infile.read()
original = existing_data original = existing_data
lines = original.splitlines() lines = original.splitlines()
@ -395,6 +422,7 @@ def generate_assets_makefile(
_get_py_targets_subset( _get_py_targets_subset(
projroot, projroot,
meta_manifests, meta_manifests,
explicit_sources,
all_targets_public, all_targets_public,
subset='public', subset='public',
suffix='_PUBLIC', suffix='_PUBLIC',
@ -402,6 +430,7 @@ def generate_assets_makefile(
_get_py_targets_subset( _get_py_targets_subset(
projroot, projroot,
meta_manifests, meta_manifests,
explicit_sources,
all_targets_public, all_targets_public,
subset='public_tools', subset='public_tools',
suffix='_PUBLIC_TOOLS', suffix='_PUBLIC_TOOLS',
@ -416,6 +445,7 @@ def generate_assets_makefile(
_get_py_targets_subset( _get_py_targets_subset(
projroot, projroot,
meta_manifests, meta_manifests,
explicit_sources,
all_targets_private, all_targets_private,
subset='private-apple', subset='private-apple',
suffix='_PRIVATE_APPLE', suffix='_PRIVATE_APPLE',
@ -423,6 +453,7 @@ def generate_assets_makefile(
_get_py_targets_subset( _get_py_targets_subset(
projroot, projroot,
meta_manifests, meta_manifests,
explicit_sources,
all_targets_private, all_targets_private,
subset='private-android', subset='private-android',
suffix='_PRIVATE_ANDROID', suffix='_PRIVATE_ANDROID',
@ -430,6 +461,7 @@ def generate_assets_makefile(
_get_py_targets_subset( _get_py_targets_subset(
projroot, projroot,
meta_manifests, meta_manifests,
explicit_sources,
all_targets_private, all_targets_private,
subset='private-common', subset='private-common',
suffix='_PRIVATE_COMMON', suffix='_PRIVATE_COMMON',
@ -437,6 +469,7 @@ def generate_assets_makefile(
_get_py_targets_subset( _get_py_targets_subset(
projroot, projroot,
meta_manifests, meta_manifests,
explicit_sources,
all_targets_private, all_targets_private,
subset='private-windows-Win32', subset='private-windows-Win32',
suffix='_PRIVATE_WIN_WIN32', suffix='_PRIVATE_WIN_WIN32',
@ -444,6 +477,7 @@ def generate_assets_makefile(
_get_py_targets_subset( _get_py_targets_subset(
projroot, projroot,
meta_manifests, meta_manifests,
explicit_sources,
all_targets_private, all_targets_private,
subset='private-windows-x64', subset='private-windows-x64',
suffix='_PRIVATE_WIN_X64', suffix='_PRIVATE_WIN_X64',
@ -470,7 +504,11 @@ def generate_assets_makefile(
all_targets_private, all_targets_private,
), ),
_get_targets( _get_targets(
projroot, 'PEM_TARGETS', '.pem', '.pem', all_targets_private projroot,
'PEM_TARGETS',
'.pem',
'.pem',
all_targets_private,
), ),
_get_targets( _get_targets(
projroot, projroot,
@ -481,7 +519,11 @@ def generate_assets_makefile(
limit_to_prefix='ba_data/data', limit_to_prefix='ba_data/data',
), ),
_get_targets( _get_targets(
projroot, 'AUDIO_TARGETS', '.wav', '.ogg', all_targets_private projroot,
'AUDIO_TARGETS',
'.wav',
'.ogg',
all_targets_private,
), ),
_get_targets( _get_targets(
projroot, projroot,

View File

@ -28,49 +28,50 @@ class FeatureSet:
_active_feature_set: FeatureSet | None = None _active_feature_set: FeatureSet | None = None
@classmethod
def get_all_for_project(cls, project_root: str) -> list[FeatureSet]:
"""Return all feature-sets for the current project."""
project_root_abs = os.path.abspath(project_root)
# Only do this once per project.
if project_root_abs not in _g_feature_sets:
_g_feature_sets[project_root_abs] = _build_feature_set_list(
project_root_abs
)
return _g_feature_sets[project_root_abs]
@classmethod
def resolve_requirements(
cls, featuresets: list[FeatureSet], reqs: set[str]
) -> set[str]:
"""Resolve all required feature-sets based on a given set of them.
Throws descriptive CleanErrors if any are missing.
"""
fsets = {f.name: f for f in featuresets}
reqs_out = set[str]()
for req in reqs:
cls._resolve_requirements(fsets, reqs_out, req)
return reqs_out
@classmethod
def _resolve_requirements(
cls, featuresets: dict[str, FeatureSet], reqs_out: set[str], req: str
) -> None:
if req in reqs_out:
return
featureset = featuresets.get(req)
if featureset is None:
raise CleanError(f"Required featureset '{req}' not found.")
reqs_out.add(req)
for sub_req in featureset.requirements:
cls._resolve_requirements(featuresets, reqs_out, sub_req)
def __init__(self, name: str): def __init__(self, name: str):
self.requirements = set[str]() # (internal; don't set this)
self.internal = False self.internal = False
# Other feature-sets this one requires. Any spinoff project this
# feature-set is included in will implicitly include these as
# well.
self.requirements = set[str]()
# Feature-sets this one can use but can survive without. Note
# that each of these requirements must have
# 'allow_as_soft_requirement' enabled. While it is possible to
# programmatically check for the presence of *any* feature-set,
# officially listing soft-requirements ensures that any expected
# python-app-subsystems are in place even for feature-sets not
# included in the spinoff project (though be aware their type
# annotations will be 'Any | None' in that case instead of the
# usual 'FooBarSubsystem | None' due to 'FooBarSubsystem' not
# actually existing).
self.soft_requirements = set[str]()
# Whether this featureset defines a native Python module within
# its C++ code. The build process will try to create dummy
# modules for all native modules you must tell it if you don't
# have one.
self.has_native_python_module = True self.has_native_python_module = True
# If True, for feature-set 'foo_bar', the build system will
# define a 'babase.app.foo_bar' attr which points to a lazy
# loaded instance of type 'bafoobar.FooBarSubsystem'.
self.has_python_app_subsystem = False
# If True, feature-set 'foo_bar', will be allowed to be listed
# as a soft-requirement of other feature sets and its
# python-app-subsystem will be annotated as type
# 'bafoobar.FooBarSubsystem | None' instead of simply
# 'bafoobar.FooBarSubsystem'. This forces type-checked code to
# account for the possibility that it will not be present. Note
# that this currently requires has_python_app_subsystem to be
# True (because if a soft-required feature-set is missing we
# must assume that is the case anyway because there's no way to
# know).
self.allow_as_soft_requirement = False
self.validate_name(name) self.validate_name(name)
# Paths of files we should disable c++ namespace checks for. # Paths of files we should disable c++ namespace checks for.
@ -228,6 +229,45 @@ class FeatureSet:
assert type(self)._active_feature_set is self assert type(self)._active_feature_set is self
type(self)._active_feature_set = None type(self)._active_feature_set = None
@classmethod
def get_all_for_project(cls, project_root: str) -> list[FeatureSet]:
"""Return all feature-sets for the current project."""
project_root_abs = os.path.abspath(project_root)
# Only do this once per project.
if project_root_abs not in _g_feature_sets:
_g_feature_sets[project_root_abs] = _build_feature_set_list(
project_root_abs
)
return _g_feature_sets[project_root_abs]
@classmethod
def resolve_requirements(
cls, featuresets: list[FeatureSet], reqs: set[str]
) -> set[str]:
"""Resolve all required feature-sets based on a given set of them.
Throws descriptive CleanErrors if any are missing.
"""
fsets = {f.name: f for f in featuresets}
reqs_out = set[str]()
for req in reqs:
cls._resolve_requirements(fsets, reqs_out, req)
return reqs_out
@classmethod
def _resolve_requirements(
cls, featuresets: dict[str, FeatureSet], reqs_out: set[str], req: str
) -> None:
if req in reqs_out:
return
featureset = featuresets.get(req)
if featureset is None:
raise CleanError(f"Required featureset '{req}' not found.")
reqs_out.add(req)
for sub_req in featureset.requirements:
cls._resolve_requirements(featuresets, reqs_out, sub_req)
def _build_feature_set_list(project_root: str) -> list[FeatureSet]: def _build_feature_set_list(project_root: str) -> list[FeatureSet]:
featuresets: list[FeatureSet] = [] featuresets: list[FeatureSet] = []
@ -246,28 +286,56 @@ def _build_feature_set_list(project_root: str) -> list[FeatureSet]:
featureset.apply_config(os.path.join(fsdir, filename)) featureset.apply_config(os.path.join(fsdir, filename))
featuresets.append(featureset) featuresets.append(featureset)
# Run some sanity checks to make sure our featuresets don't have # Run some sanity checks to make sure our full set of featuresets
# clashing names/etc. (for instance, foo_v1 and foov_1 would resolve # don't have clashing names/etc. (for instance, foo_v1 and foov_1
# to the same foov1 py module name). # would resolve to the same foov1 py module name).
fsnames = {f.name for f in featuresets} featuresets_by_name = {f.name: f for f in featuresets}
assert len(fsnames) == len(featuresets) assert len(featuresets_by_name) == len(featuresets)
assert len({f.name_compact for f in featuresets}) == len(featuresets) assert len({f.name_compact for f in featuresets}) == len(featuresets)
assert len({f.name_compact for f in featuresets}) == len(featuresets) assert len({f.name_compact for f in featuresets}) == len(featuresets)
for featureset in featuresets: for featureset in featuresets:
# Require soft-req-enabled feature-sets to have app subsystems
# enabled (see above for explanation).
if featureset.allow_as_soft_requirement:
if not featureset.has_python_app_subsystem:
raise CleanError(
f"Feature-set '{featureset.name}'"
" has 'allow_as_soft_requirement' set to True but"
" 'has_python_app_subsystem' set to False;"
' soft-requireable feature-sets currently MUST'
' provide a subsystem.'
)
for req in featureset.requirements: for req in featureset.requirements:
if req == featureset.name: if req == featureset.name:
raise CleanError( raise CleanError(
f"Feature-set '{featureset.name}'" f"Feature-set '{featureset.name}'"
f' lists itself as a requirement; this is not allowed.' f' lists itself as a requirement; this is not allowed.'
) )
if req not in fsnames: if req not in featuresets_by_name:
raise CleanError( raise CleanError(
f"Undefined feature-set '{req}'" f"Undefined feature-set '{req}'"
f' listed as a requirement of feature-set' f' listed as a requirement of feature-set'
f" '{featureset.name}'." f" '{featureset.name}'."
) )
for req in featureset.soft_requirements:
if req == featureset.name:
raise CleanError(
f"Feature-set '{featureset.name}'"
f' lists itself as a soft-requirement; this is not allowed.'
)
if (
req in featuresets_by_name
and not featuresets_by_name[req].allow_as_soft_requirement
):
raise CleanError(
f"Feature-set '{req}'"
f' is listed as a soft-requirement of feature-set'
f" '{featureset.name}' but is not allowed to be soft"
' required.'
)
return featuresets return featuresets

View File

@ -111,6 +111,7 @@ class ProjectUpdater:
self._update_cmake_files() self._update_cmake_files()
self._update_visual_studio_projects() self._update_visual_studio_projects()
self._update_xcode_projects() self._update_xcode_projects()
self._update_app_module()
@property @property
def source_files(self) -> list[str]: def source_files(self) -> list[str]:
@ -401,6 +402,8 @@ class ProjectUpdater:
self._generate_resources_makefile(path, existing_data) self._generate_resources_makefile(path, existing_data)
elif path == 'src/meta/Makefile': elif path == 'src/meta/Makefile':
self._generate_meta_makefile(existing_data) self._generate_meta_makefile(existing_data)
elif path == 'src/assets/ba_data/python/babase/_app.py':
self._generate_app_module(path, existing_data)
elif path.startswith('src/meta/.meta_manifest_'): elif path.startswith('src/meta/.meta_manifest_'):
# These are always generated as a side-effect of the # These are always generated as a side-effect of the
# meta Makefile. # meta Makefile.
@ -414,6 +417,9 @@ class ProjectUpdater:
assert path in self._generated_files assert path in self._generated_files
return self._generated_files[path] return self._generated_files[path]
def _update_app_module(self) -> None:
self.enqueue_update('src/assets/ba_data/python/babase/_app.py')
def _update_xcode_projects(self) -> None: def _update_xcode_projects(self) -> None:
# from batools.xcode import update_xcode_project # from batools.xcode import update_xcode_project
@ -652,7 +658,7 @@ class ProjectUpdater:
def _generate_assets_makefile(self, path: str, existing_data: str) -> None: def _generate_assets_makefile(self, path: str, existing_data: str) -> None:
from batools.assetsmakefile import generate_assets_makefile from batools.assetsmakefile import generate_assets_makefile
# We need to now what files meta will be creating (since they # We need to know what files meta will be creating (since they
# can be asset sources). # can be asset sources).
meta_manifests: dict[str, str] = {} meta_manifests: dict[str, str] = {}
for mantype in ['public', 'private']: for mantype in ['public', 'private']:
@ -661,8 +667,17 @@ class ProjectUpdater:
manifest_file_name manifest_file_name
) )
# Special case; the app module file in the base feature set
# is created/updated here as a project file. It may or may not
# exist on disk, but we want to ignore it if it does and add it
# explicitly similarly to meta-manifests.
if 'base' in self.feature_sets:
explicit_sources = {'src/assets/ba_data/python/babase/_app.py'}
else:
explicit_sources = set()
outfiles = generate_assets_makefile( outfiles = generate_assets_makefile(
self.projroot, path, existing_data, meta_manifests self.projroot, path, existing_data, meta_manifests, explicit_sources
) )
for out_path, out_contents in outfiles.items(): for out_path, out_contents in outfiles.items():
@ -680,6 +695,120 @@ class ProjectUpdater:
self.projroot, existing_data self.projroot, existing_data
) )
def _generate_app_module(self, path: str, existing_data: str) -> None:
# pylint: disable=too-many-locals
import textwrap
from efrotools import replace_section
fsets = self.feature_sets
out = existing_data
info = '# This section autogenerated by project-update.'
indent = ' '
# Import modules we need for feature-set subsystems.
contents = ''
for _fsname, fset in sorted(fsets.items()):
if fset.has_python_app_subsystem:
modname = fset.name_python_package
classname = f'{fset.name_camel}Subsystem'
contents += f'from {modname} import {classname}\n'
out = replace_section(
out,
f'{indent}# __FEATURESET_APP_SUBSYSTEM_IMPORTS_BEGIN__\n',
f'{indent}# __FEATURESET_APP_SUBSYSTEM_IMPORTS_END__\n',
textwrap.indent(f'{info}\n\n{contents}\n', indent),
keep_markers=True,
)
# Calc which feature-sets are soft-required by any of the ones here
# but not included here. For those we'll expose app-subsystems that
# always return None.
missing_soft_fset_names = set[str]()
for fset in fsets.values():
for softreq in fset.soft_requirements:
if softreq not in fsets:
missing_soft_fset_names.add(softreq)
all_fset_names = missing_soft_fset_names | fsets.keys()
# Add properties to instantiate feature-set subsystems.
contents = ''
for fsetname in sorted(all_fset_names):
# for _fsname, fset in sorted(fsets.items()):
if fsetname in missing_soft_fset_names:
contents += (
f'\n'
f'@cached_property\n'
f'def {fsetname}(self) -> Any | None:\n'
f' """Our {fsetname} subsystem (not available)."""\n'
f'\n'
f' return None\n'
)
else:
fset = fsets[fsetname]
if fset.has_python_app_subsystem:
if not fset.allow_as_soft_requirement:
raise CleanError(
f'allow_as_soft_requirement is False for'
f' feature-set {fset.name};'
f' this is not yet supported.'
)
modname = fset.name_python_package
classname = f'{fset.name_camel}Subsystem'
contents += (
f'\n'
f'@cached_property\n'
f'def {fset.name}(self) -> {classname} | None:\n'
f' """Our {fset.name} subsystem (if available)."""\n'
f'\n'
f' try:\n'
f' from {modname} import {classname}\n'
f'\n'
f' return {classname}()\n'
f' except ImportError:\n'
f' return None\n'
f' except Exception:\n'
f" logging.exception('Error importing"
f" {modname}.')\n"
f' return None\n'
)
out = replace_section(
out,
f'{indent}# __FEATURESET_APP_SUBSYSTEM_PROPERTIES_BEGIN__\n',
f'{indent}# __FEATURESET_APP_SUBSYSTEM_PROPERTIES_END__\n',
textwrap.indent(f'{info}\n{contents}\n', indent),
keep_markers=True,
)
# Set default app-mode-selection logic.
# TEMP - fill this out with proper logic/options.
if 'scene_v1' in fsets:
contents = 'import bascenev1\n\nreturn bascenev1.SceneV1AppMode\n'
else:
contents = "raise RuntimeError('FIXME: unimplemented.')\n"
indent = ' '
out = replace_section(
out,
f'{indent}# __DEFAULT_APP_MODE_SELECTION_BEGIN__\n',
f'{indent}# __DEFAULT_APP_MODE_SELECTION_END__\n',
textwrap.indent(f'{info}\n\n{contents}\n', indent),
keep_markers=True,
)
# Note: we *should* format this string, but because this code
# runs with every project update I'm just gonna try to keep the
# formatting correct manually for now to save a bit of time.
# (project update time jumps from 0.3 to 0.5 seconds if I format
# thie one file).
self._generated_files[path] = out
def _update_meta_makefile(self) -> None: def _update_meta_makefile(self) -> None:
self.enqueue_update('src/meta/Makefile') self.enqueue_update('src/meta/Makefile')

View File

@ -129,20 +129,10 @@ class SpinoffContext:
self._execution_error = False self._execution_error = False
self._project_file_names = { self.project_file_paths = set[str]()
'Makefile', self.project_file_names = set[str]()
'CMakeLists.txt', self.project_file_suffixes = set[str]()
'.meta_manifest_public.json',
'.meta_manifest_private.json',
'.asset_manifest_public.json',
'.asset_manifest_private.json',
}
self._project_file_suffixes = {
'.vcxproj',
'.vcxproj.filters',
'.pbxproj',
}
# Set of files/symlinks in src. # Set of files/symlinks in src.
self._src_entities: dict[str, SrcEntity] = {} self._src_entities: dict[str, SrcEntity] = {}
@ -1300,8 +1290,10 @@ class SpinoffContext:
if path.startswith('tools/') or path.startswith('src/external'): if path.startswith('tools/') or path.startswith('src/external'):
return False return False
bname = os.path.basename(path) bname = os.path.basename(path)
return bname in self._project_file_names or any( return (
bname.endswith(s) for s in self._project_file_suffixes path in self.project_file_paths
or bname in self.project_file_names
or any(bname.endswith(s) for s in self.project_file_suffixes)
) )
def _update(self) -> None: def _update(self) -> None:
@ -1401,6 +1393,7 @@ class SpinoffContext:
print_individual_updates: bool, print_individual_updates: bool,
is_project_file: bool = False, is_project_file: bool = False,
) -> None: ) -> None:
# pylint: disable=too-many-locals
src_entity = self._src_entities[src_path] src_entity = self._src_entities[src_path]
dst_path = src_entity.dst dst_path = src_entity.dst
src_path_full = os.path.join(self._src_root, src_path) src_path_full = os.path.join(self._src_root, src_path)
@ -1465,10 +1458,12 @@ class SpinoffContext:
f'{Clr.RED}Error removing failed dst file: {exc2}{Clr.RST}' f'{Clr.RED}Error removing failed dst file: {exc2}{Clr.RST}'
) )
self._execution_error = True self._execution_error = True
verbose_note = (
'' if self._verbose else ' (use --verbose for full traceback)'
)
print( print(
f'{Clr.RED}Error copying/filtering file:' f'{Clr.RED}Error copying/filtering file:'
f" '{src_path_full}'{Clr.RST}: {exc}" f" '{src_path_full}'{Clr.RST}: {exc}{verbose_note}",
' (use --verbose for full traceback)',
file=sys.stderr, file=sys.stderr,
) )
if self._verbose: if self._verbose:

View File

@ -136,6 +136,7 @@ def replace_section(
begin_marker: str, begin_marker: str,
end_marker: str, end_marker: str,
replace_text: str = '', replace_text: str = '',
keep_markers: bool = False,
error_if_missing: bool = True, error_if_missing: bool = True,
) -> str: ) -> str:
"""Replace all text between two marker strings (including the markers).""" """Replace all text between two marker strings (including the markers)."""
@ -157,7 +158,9 @@ def replace_section(
f'; found {text.count(end_marker)}.' f'; found {text.count(end_marker)}.'
) )
_before_end, after_end = splits _before_end, after_end = splits
return before_begin + replace_text + after_end if keep_markers:
replace_text = f'{begin_marker}{replace_text}{end_marker}'
return f'{before_begin}{replace_text}{after_end}'
def get_public_license(style: str) -> str: def get_public_license(style: str) -> str: