mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-19 21:37:57 +08:00
new high-level feature-set copy/delete functionality
This commit is contained in:
parent
b51bc29773
commit
0709e553d7
88
.efrocachemap
generated
88
.efrocachemap
generated
@ -4064,50 +4064,50 @@
|
||||
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
|
||||
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
|
||||
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "4542f7820b33b8f1f6719b17efa26453",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "c94898207acb63e3e09cb08b50ebd287",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "ad606ee2ee1367b906c9cbb18c53baf6",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "776e2461a852753010278d5b90ff32df",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "aefcfd8774114e7cebc418d0adad56f2",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "d21df3fc06db87786d68ccc1417d043e",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "7b218f79079e8e0d92b5f0a4afb0599d",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "94738f2f6fcbea9dad60af4bd6a7cd5d",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "033b39b34a10d5bce6e53572a816d0ef",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "f4eb1cc72a16eafcaba2d55c65f19a7b",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "fec5b1818aa97bc0dff1ddf127742574",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "76930acbbd2e8dd35f35639d85ce21ac",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "d5ab5ad85dd8d113125d15f03c221db3",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "29fbcf76221d71ea3ecb73d8ab353f7b",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "3d8b4b5d6ba376f2d280209e0aa3bcf2",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "fb9da4455aa7c0d8e5120a75ccaeabcb",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "056f8137ba8b1179b66eea59944aefb2",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "78f53224dd6052c93da31bda7ee1c84c",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "82128374a7eabc651061d778e099b923",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "2cd60cbebfbe25447791284b42c3caf9",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "a589af5b31246539eac3264c829c41a0",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "3dfaf945474294cb9f3808a835fb667c",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "a589af5b31246539eac3264c829c41a0",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "3dfaf945474294cb9f3808a835fb667c",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "47e08d2f265f4dda15b309fa67ba163b",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "c9a5225be07b4456e073014e1db2cafe",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "47e08d2f265f4dda15b309fa67ba163b",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "c9a5225be07b4456e073014e1db2cafe",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "f79382e5342db6f38f4c07170589d62d",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "b2d5386301891813a790f1a19d442022",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "f79382e5342db6f38f4c07170589d62d",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "b2d5386301891813a790f1a19d442022",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "899eda04958efce6903b7dd2abe6c76f",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "e98d76fe2d0af7e775801998d8591340",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "e9d551c0bfbb330470b1e0784028e4d3",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "e98d76fe2d0af7e775801998d8591340",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "77f25ad3348eb212969725c0ec7ebec8",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "df8038790ce73124950dc443c1e972ad",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "f184798b7973343cdc42d16e05aa335f",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "2e3ed4ea52261efd17d0927b21cf4075",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "1a5c374cfdd07ccbb2f1e9a44b886131",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "66da72816faa1d87d0b3fe677c6eb9c8",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "fbef69c5fba9b7c94d37e233eb6c9642",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "c37e1dc4f95a8db0b5059dae0b5f5241",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "3403caf882f5efc1c5a62bf452994bd1",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "c046dcf69b1d3cb122869ee1f8df06d6",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "10f90e814342ba8553c193d8ce3221d1",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "2827cb64562372b70fe78c30cc4dbf1e",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "37e25714c6b19f5edc1135820edb0ef4",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "bacec90936f644c739eb9230b61c1528",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "51e044f01c1f783f2c88d8ba490370a6",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "01aff77ee4c84b06b54664e26aaed0cf",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "7430ba5eadfdd15310d0b140d5ef3f2b",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "de88562b3c51a56a5a89bc35a7888e58",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "74b6f29023a4a3e90b2233d77c0c38bb",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "78150d99f0e8dfd2f6785af4335b3f49",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "ccc2f72a1a12a3314f6ffea3ea20875c",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "bab389beb2e52641e5a7e5cf9a62bf69",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "a3de45a3355c610719a477077babf451",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "cf35da279c2ff3d4a8241e51199fea5d",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "8fcb6c2f3be27b2aadee60a436ee5c82",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "695c693d94935c99e3990c885b882091",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "38a69802cca5b4bc3a344777f12679bd",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "4e24b9c6b980844de8c482dd1034fb97",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "a3607fd941915ab11503f82acfc392b5",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "b5a129d83796c9e7015ab5e319d2c22f",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "a3607fd941915ab11503f82acfc392b5",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "b5a129d83796c9e7015ab5e319d2c22f",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "13405a4a16a71d073b6b3cabbbcd9666",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "86b26dc84cc7fa7095e51cfcae759c0b",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "13405a4a16a71d073b6b3cabbbcd9666",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "86b26dc84cc7fa7095e51cfcae759c0b",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "678e09ecd5da367ce290ca7318617b61",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "a9cdc9dd029dabc6dfa5b61d33de7927",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "678e09ecd5da367ce290ca7318617b61",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "a9cdc9dd029dabc6dfa5b61d33de7927",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "4811585805942428ddb217917e4ad843",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "c5c40967e63471c9c4abd6dfbef892df",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "d34c0a142e7d391a109a33ea3cc77c08",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "c5c40967e63471c9c4abd6dfbef892df",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "4eb48f28a678a7f2eae8c10d4d86b879",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "675e12e534ebec6cb9760b066c158747",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "5d9bb2f2835069e186418b1b02598168",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "7fa0439993cdd80f098a2cf37d4e6b31",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "1bac9e9d670f8dfac75398085820d039",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "9ecb0b936f758250417f7c014f89d683",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "912935f68da6dfca6312b3859c14ee27",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "aa9ca81cff1cd3135af6fa81cb16851a",
|
||||
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
|
||||
"src/assets/ba_data/python/babase/_mgen/enums.py": "f8cd3af311ac63147882590123b78318",
|
||||
"src/ballistica/base/mgen/pyembed/binding_base.inc": "ad347097a38e0d7ede9eb6dec6a80ee9",
|
||||
|
||||
7
.idea/dictionaries/ericf.xml
generated
7
.idea/dictionaries/ericf.xml
generated
@ -823,6 +823,7 @@
|
||||
<w>dstent</w>
|
||||
<w>dstfile</w>
|
||||
<w>dstfin</w>
|
||||
<w>dstfs</w>
|
||||
<w>dstjson</w>
|
||||
<w>dstlines</w>
|
||||
<w>dstname</w>
|
||||
@ -1085,6 +1086,7 @@
|
||||
<w>flycheck</w>
|
||||
<w>fmod</w>
|
||||
<w>fname</w>
|
||||
<w>fnamefilt</w>
|
||||
<w>fnamefull</w>
|
||||
<w>fnames</w>
|
||||
<w>fnmatch</w>
|
||||
@ -2669,6 +2671,7 @@
|
||||
<w>shobs</w>
|
||||
<w>shortname</w>
|
||||
<w>shouldn</w>
|
||||
<w>shouldnt</w>
|
||||
<w>showbuffer</w>
|
||||
<w>showpoints</w>
|
||||
<w>showstats</w>
|
||||
@ -2753,11 +2756,13 @@
|
||||
<w>splitnumstr</w>
|
||||
<w>spwd</w>
|
||||
<w>squadcore</w>
|
||||
<w>src's</w>
|
||||
<w>srcabs</w>
|
||||
<w>srcattr</w>
|
||||
<w>srcdata</w>
|
||||
<w>srcdir</w>
|
||||
<w>srcfolder</w>
|
||||
<w>srcfs</w>
|
||||
<w>srcgrp</w>
|
||||
<w>srcid</w>
|
||||
<w>srcjson</w>
|
||||
@ -2840,6 +2845,7 @@
|
||||
<w>subdep</w>
|
||||
<w>subdeps</w>
|
||||
<w>subdirs</w>
|
||||
<w>subdst</w>
|
||||
<w>subfieldpath</w>
|
||||
<w>subfolders</w>
|
||||
<w>submpath</w>
|
||||
@ -2852,6 +2858,7 @@
|
||||
<w>subprocesses</w>
|
||||
<w>subrepos</w>
|
||||
<w>subsel</w>
|
||||
<w>subsrc</w>
|
||||
<w>subsys</w>
|
||||
<w>subtypestr</w>
|
||||
<w>subval</w>
|
||||
|
||||
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,5 +1,13 @@
|
||||
### 1.7.28 (build 21289, api 8, 2023-08-31)
|
||||
### 1.7.28 (build 21293, api 8, 2023-08-31)
|
||||
|
||||
- Added some high level functionality for copying and deleting feature-sets to
|
||||
the `tools/spinoff` tool. For example, to create your own `poo` feature-set,
|
||||
do `tools/spinoff fset-copy template_fs poo`. Then do `make update` and `make
|
||||
cmake` to build and run the app, and from within it you should be able to do
|
||||
`import bapoo` to get at your nice shiny poo feature-set. When you are done
|
||||
playing, you can do `tools/spinoff fset-delete poo` to blow away any traces of
|
||||
it.
|
||||
|
||||
### 1.7.27 (build 21282, api 8, 2023-08-30)
|
||||
|
||||
- Fixed a rare crash that could occur if the app shuts down while a background
|
||||
|
||||
6
ballisticakit-cmake/.idea/dictionaries/ericf.xml
generated
6
ballisticakit-cmake/.idea/dictionaries/ericf.xml
generated
@ -512,6 +512,7 @@
|
||||
<w>dstdir</w>
|
||||
<w>dstdirfull</w>
|
||||
<w>dstent</w>
|
||||
<w>dstfs</w>
|
||||
<w>dstnode</w>
|
||||
<w>dstpath</w>
|
||||
<w>dstr</w>
|
||||
@ -662,6 +663,7 @@
|
||||
<w>flopsy</w>
|
||||
<w>flushhhhh</w>
|
||||
<w>fname</w>
|
||||
<w>fnamefilt</w>
|
||||
<w>fnode</w>
|
||||
<w>fnsu</w>
|
||||
<w>fnumc</w>
|
||||
@ -1619,9 +1621,11 @@
|
||||
<w>spinups</w>
|
||||
<w>spivak</w>
|
||||
<w>spwd</w>
|
||||
<w>src's</w>
|
||||
<w>srcabs</w>
|
||||
<w>srcattr</w>
|
||||
<w>srcfolder</w>
|
||||
<w>srcfs</w>
|
||||
<w>srcgrp</w>
|
||||
<w>srcid</w>
|
||||
<w>srcname</w>
|
||||
@ -1683,6 +1687,7 @@
|
||||
<w>subargs</w>
|
||||
<w>subc</w>
|
||||
<w>subclsssing</w>
|
||||
<w>subdst</w>
|
||||
<w>subentities</w>
|
||||
<w>subfieldpath</w>
|
||||
<w>subitems</w>
|
||||
@ -1691,6 +1696,7 @@
|
||||
<w>subplatform</w>
|
||||
<w>subscale</w>
|
||||
<w>subscr</w>
|
||||
<w>subsrc</w>
|
||||
<w>subsys</w>
|
||||
<w>subtypestr</w>
|
||||
<w>successmsg</w>
|
||||
|
||||
@ -109,6 +109,7 @@ from babase._apputils import (
|
||||
is_browser_likely_available,
|
||||
garbage_collect,
|
||||
get_remote_app_name,
|
||||
AppHealthMonitor,
|
||||
)
|
||||
from babase._cloud import CloudSubsystem
|
||||
from babase._emptyappmode import EmptyAppMode
|
||||
@ -176,6 +177,7 @@ __all__ = [
|
||||
'app',
|
||||
'App',
|
||||
'AppConfig',
|
||||
'AppHealthMonitor',
|
||||
'AppIntent',
|
||||
'AppIntentDefault',
|
||||
'AppIntentExec',
|
||||
|
||||
@ -13,6 +13,7 @@ from concurrent.futures import ThreadPoolExecutor
|
||||
from functools import cached_property
|
||||
|
||||
from efro.call import tpartial
|
||||
|
||||
import _babase
|
||||
from babase._language import LanguageSubsystem
|
||||
from babase._plugin import PluginSubsystem
|
||||
|
||||
@ -85,7 +85,9 @@ class MasterServerV1CallThread(threading.Thread):
|
||||
|
||||
# Tearing the app down while this is running can lead to
|
||||
# rare crashes in LibSSL, so avoid that if at all possible.
|
||||
babase.shutdown_suppress_begin()
|
||||
if not babase.shutdown_suppress_begin():
|
||||
# App is already shutting down, so we're a no-op.
|
||||
return
|
||||
|
||||
try:
|
||||
classic = babase.app.classic
|
||||
|
||||
@ -52,7 +52,7 @@ if TYPE_CHECKING:
|
||||
|
||||
# Build number and version of the ballistica binary we expect to be
|
||||
# using.
|
||||
TARGET_BALLISTICA_BUILD = 21289
|
||||
TARGET_BALLISTICA_BUILD = 21293
|
||||
TARGET_BALLISTICA_VERSION = '1.7.28'
|
||||
|
||||
|
||||
@ -461,11 +461,12 @@ def _modular_main() -> None:
|
||||
# First baenv sets up things like Python paths the way the engine
|
||||
# needs them, and then we import and run the engine.
|
||||
#
|
||||
# Below we're doing a slightly fancier version of that. Namely we do
|
||||
# some processing of command line args to allow overriding of paths
|
||||
# or running explicit commands or whatever else. Our goal is that
|
||||
# this modular form of the app should be basically indistinguishable
|
||||
# from the monolithic form when used from the command line.
|
||||
# Below we're doing a slightly fancier version of that. Namely, we
|
||||
# do some processing of command line args to allow overriding of
|
||||
# paths or running explicit commands or whatever else. Our goal is
|
||||
# that this modular form of the app should be basically
|
||||
# indistinguishable from the monolithic form when used from the
|
||||
# command line.
|
||||
|
||||
try:
|
||||
# Take note that we're running via modular-main. The native
|
||||
|
||||
@ -4,8 +4,12 @@
|
||||
|
||||
# ba_meta require api 8
|
||||
|
||||
# Package up various private bits (including stuff from our native
|
||||
# module) into a nice clean public API.
|
||||
from _batemplatefs import hello_again_world
|
||||
from batemplatefs._subsystem import TemplateFsSubsystem
|
||||
|
||||
__all__ = [
|
||||
'TemplateFsSubsystem',
|
||||
'hello_again_world',
|
||||
]
|
||||
|
||||
@ -715,11 +715,33 @@ void BaseFeatureSet::DoPushObjCall(const PythonObjectSetBase* objset, int id,
|
||||
}
|
||||
|
||||
auto BaseFeatureSet::IsAppStarted() const -> bool { return app_started_; }
|
||||
void BaseFeatureSet::ShutdownSuppressBegin() { shutdown_suppress_count_++; }
|
||||
|
||||
auto BaseFeatureSet::ShutdownSuppressBegin() -> bool {
|
||||
std::scoped_lock lock(shutdown_suppress_lock_);
|
||||
if (!shutdown_suppress_disallowed_) {
|
||||
return false;
|
||||
}
|
||||
shutdown_suppress_count_++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void BaseFeatureSet::ShutdownSuppressEnd() {
|
||||
std::scoped_lock lock(shutdown_suppress_lock_);
|
||||
shutdown_suppress_count_--;
|
||||
assert(shutdown_suppress_count_ >= 0);
|
||||
}
|
||||
|
||||
auto BaseFeatureSet::ShutdownSuppressGetCount() -> int {
|
||||
std::scoped_lock lock(shutdown_suppress_lock_);
|
||||
return shutdown_suppress_count_;
|
||||
}
|
||||
|
||||
void BaseFeatureSet::ShutdownSuppressDisallow() {
|
||||
std::scoped_lock lock(shutdown_suppress_lock_);
|
||||
assert(!shutdown_suppress_disallowed_);
|
||||
shutdown_suppress_disallowed_ = true;
|
||||
}
|
||||
|
||||
auto BaseFeatureSet::GetReturnValue() const -> int { return return_value(); }
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#ifndef BALLISTICA_BASE_BASE_H_
|
||||
#define BALLISTICA_BASE_BASE_H_
|
||||
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
@ -690,9 +691,18 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
void DoPushObjCall(const PythonObjectSetBase* objset, int id,
|
||||
const std::string& arg) override;
|
||||
void OnReachedEndOfBaBaseImport();
|
||||
void ShutdownSuppressBegin();
|
||||
|
||||
/// Begin a shutdown-suppressing operation. Returns true if the operation
|
||||
/// can proceed; otherwise shutdown has already begun and the operation
|
||||
/// should be aborted.
|
||||
auto ShutdownSuppressBegin() -> bool;
|
||||
|
||||
/// End a shutddown-suppressing operation. Should only be called after a
|
||||
/// successful begin.
|
||||
void ShutdownSuppressEnd();
|
||||
auto shutdown_suppress_count() const { return shutdown_suppress_count_; }
|
||||
|
||||
auto ShutdownSuppressGetCount() -> int;
|
||||
void ShutdownSuppressDisallow();
|
||||
|
||||
/// Called in the logic thread once our screen is up and assets are
|
||||
/// loading.
|
||||
@ -756,6 +766,8 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
StressTest* stress_test_;
|
||||
|
||||
std::string console_startup_messages_;
|
||||
std::mutex shutdown_suppress_lock_;
|
||||
bool shutdown_suppress_disallowed_;
|
||||
int shutdown_suppress_count_{};
|
||||
bool tried_importing_plus_{};
|
||||
bool tried_importing_classic_{};
|
||||
|
||||
@ -226,7 +226,10 @@ void Logic::OnAppShutdown() {
|
||||
// Nuke the app from orbit if we get stuck while shutting down.
|
||||
g_core->StartSuicideTimer("shutdown", 10000);
|
||||
|
||||
// Let our subsystems know we're shutting down.
|
||||
// Tell base to disallow shutdown-suppressors from here on out.
|
||||
g_base->ShutdownSuppressDisallow();
|
||||
|
||||
// Let our logic thread subsystems know we're shutting down.
|
||||
// Note: Keep these in opposite order of OnAppStart.
|
||||
// Note2: Any shutdown processes that take a non-zero amount of time
|
||||
// should be registered as shutdown-tasks
|
||||
|
||||
@ -1495,8 +1495,12 @@ static PyMethodDef PyGetImmediateReturnCodeDef = {
|
||||
static auto PyShutdownSuppressBegin(PyObject* self) -> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
assert(g_base);
|
||||
g_base->ShutdownSuppressBegin();
|
||||
Py_RETURN_NONE;
|
||||
auto val = g_base->ShutdownSuppressBegin();
|
||||
if (val) {
|
||||
Py_RETURN_TRUE;
|
||||
} else {
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
|
||||
@ -1505,7 +1509,7 @@ static PyMethodDef PyShutdownSuppressBeginDef = {
|
||||
(PyCFunction)PyShutdownSuppressBegin, // method
|
||||
METH_NOARGS, // flags
|
||||
|
||||
"shutdown_suppress_begin() -> None\n"
|
||||
"shutdown_suppress_begin() -> bool\n"
|
||||
"\n"
|
||||
"(internal)\n",
|
||||
};
|
||||
@ -1536,7 +1540,7 @@ static PyMethodDef PyShutdownSuppressEndDef = {
|
||||
static auto PyShutdownSuppressCount(PyObject* self) -> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
assert(g_base);
|
||||
return PyLong_FromLong(g_base->shutdown_suppress_count());
|
||||
return PyLong_FromLong(g_base->ShutdownSuppressGetCount());
|
||||
Py_RETURN_NONE;
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
|
||||
@ -15,9 +15,8 @@ CoreFeatureSet* g_core{};
|
||||
BaseSoftInterface* g_base_soft{};
|
||||
|
||||
auto CoreFeatureSet::Import(const CoreConfig* config) -> CoreFeatureSet* {
|
||||
// In monolithic builds we can accept an explicit core-config the first
|
||||
// time we're imported. In this case, Python is not even spun up yet so
|
||||
// it can influence even that.
|
||||
// In monolithic builds, we accept an explicit core-config the first time
|
||||
// we're imported. It is fully up to the caller to build the config.
|
||||
if (g_buildconfig.monolithic_build()) {
|
||||
if (config != nullptr) {
|
||||
if (g_core != nullptr) {
|
||||
@ -29,17 +28,17 @@ auto CoreFeatureSet::Import(const CoreConfig* config) -> CoreFeatureSet* {
|
||||
DoImport(*config);
|
||||
}
|
||||
} else {
|
||||
// No config passed; use a default.
|
||||
// If no config is passed, use a default. If the user wants env vars
|
||||
// or anything else factored in, they should do so themselves in the
|
||||
// config they pass (CoreConfig::ForEnvVars(), etc.).
|
||||
if (g_core == nullptr) {
|
||||
DoImport({});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// In modular builds we autogenerate a CoreConfig that takes into
|
||||
// account only env-vars (or env-vars plus Python args if we're being
|
||||
// run via the baenv script). In this case, Python is already spun up
|
||||
// and baenv already handled any Python environment stuff so we have
|
||||
// less to do.
|
||||
// In modular builds, we generate a CoreConfig *after* Python is spun
|
||||
// up, implicitly using Python's sys args and/or env vars when
|
||||
// applicable.
|
||||
if (config != nullptr) {
|
||||
FatalError("CoreConfig can't be explicitly passed in modular builds.");
|
||||
}
|
||||
@ -57,6 +56,7 @@ auto CoreFeatureSet::Import(const CoreConfig* config) -> CoreFeatureSet* {
|
||||
DoImport(CoreConfig::ForArgsAndEnvVars(static_cast<int>(argv.size()),
|
||||
argv.data()));
|
||||
} else {
|
||||
// Not using Python sys args but we still want to process env vars.
|
||||
DoImport(CoreConfig::ForEnvVars());
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,27 +24,26 @@ class CoreFeatureSet;
|
||||
class BaseSoftInterface;
|
||||
|
||||
// Our feature-set's globals.
|
||||
// Feature-sets should NEVER directly access globals in another feature-set's
|
||||
// namespace. All functionality we need from other feature-sets should be
|
||||
// imported into globals in our own namespace. Generally we do this when we
|
||||
// are initially imported (just as regular Python modules do).
|
||||
//
|
||||
// Feature-sets should NEVER directly access globals in another
|
||||
// feature-set's namespace. All functionality we need from other
|
||||
// feature-sets should be imported into globals in our own namespace.
|
||||
// Generally we do this when we are initially imported (just as regular
|
||||
// Python modules do).
|
||||
|
||||
// Our pointer to our own feature-set.
|
||||
extern CoreFeatureSet* g_core;
|
||||
|
||||
// We don't require the base feature-set but can use it if present.
|
||||
// Base will supply us with this pointer if/when it spins up.
|
||||
// So we must never assume this pointer is valid and must check for it
|
||||
// with each use.
|
||||
// We don't require the base feature-set but can use it if present. Base
|
||||
// will supply us with this pointer if/when it spins up. So we must never
|
||||
// assume this pointer is valid and must check for it with each use.
|
||||
extern BaseSoftInterface* g_base_soft;
|
||||
|
||||
/// Platform-agnostic global state for our overall system.
|
||||
/// This gets created whenever we are used in any capacity, even if
|
||||
/// we don't create/run an app.
|
||||
/// Ideally most things here should be migrated to more specific
|
||||
/// subsystems.
|
||||
/// Core engine functionality.
|
||||
class CoreFeatureSet {
|
||||
public:
|
||||
/// Import the core feature set. A core-config can be passed ONLY
|
||||
/// in monolithic builds when it is guaranteed that the Import will be
|
||||
/// Import the core feature set. A core-config can be passed ONLY in
|
||||
/// monolithic builds when it is guaranteed that the Import will be
|
||||
/// allocating the CoreFeatureSet singleton.
|
||||
static auto Import(const CoreConfig* config = nullptr) -> CoreFeatureSet*;
|
||||
|
||||
@ -76,32 +75,35 @@ class CoreFeatureSet {
|
||||
auto HeadlessMode() -> bool;
|
||||
|
||||
/// Return current app-time in milliseconds.
|
||||
///
|
||||
/// App-time is basically the total time that the engine has been actively
|
||||
/// running. (The 'App' here is a slight misnomer). It will stop progressing
|
||||
/// while the app is suspended and will never go backwards.
|
||||
/// running. (The 'App' here is a slight misnomer). It will stop
|
||||
/// progressing while the app is suspended and will never go backwards.
|
||||
auto GetAppTimeMillisecs() -> millisecs_t;
|
||||
|
||||
/// Return current app-time in microseconds.
|
||||
///
|
||||
/// App-time is basically the total time that the engine has been actively
|
||||
/// running. (The 'App' here is a slight misnomer). It will stop progressing
|
||||
/// while the app is suspended and will never go backwards.
|
||||
/// running. (The 'App' here is a slight misnomer). It will stop
|
||||
/// progressing while the app is suspended and will never go backwards.
|
||||
auto GetAppTimeMicrosecs() -> microsecs_t;
|
||||
|
||||
/// Return current app-time in seconds.
|
||||
///
|
||||
/// App-time is basically the total time that the engine has been actively
|
||||
/// running. (The 'App' here is a slight misnomer). It will stop progressing
|
||||
/// while the app is suspended and will never go backwards.
|
||||
/// running. (The 'App' here is a slight misnomer). It will stop
|
||||
/// progressing while the app is suspended and will never go backwards.
|
||||
auto GetAppTimeSeconds() -> double;
|
||||
|
||||
/// Are we in the thread the main event loop is running on?
|
||||
/// Generally this is the thread that runs graphics and os event processing.
|
||||
/// Are we in the thread the main event loop is running on? Generally this
|
||||
/// is the thread that runs graphics and os event processing.
|
||||
auto InMainThread() -> bool;
|
||||
|
||||
/// Log a boot-related message (only if core_config.lifecycle_log is true).
|
||||
void LifecycleLog(const char* msg, double offset_seconds = 0.0);
|
||||
|
||||
/// Base path of build src dir so we can attempt to remove it from
|
||||
/// any source file paths we print.
|
||||
/// Base path of build src dir so we can attempt to remove it from any
|
||||
/// source file paths we print.
|
||||
auto build_src_dir() const { return build_src_dir_; }
|
||||
|
||||
const auto& legacy_user_agent_string() const {
|
||||
@ -113,8 +115,8 @@ class CoreFeatureSet {
|
||||
}
|
||||
|
||||
/// Return true if baenv values have been locked in: python paths, log
|
||||
/// handling, etc. Early-running code may wish to explicitly avoid making log
|
||||
/// calls until this condition is met to ensure predictable behavior.
|
||||
/// handling, etc. Early-running code may wish to explicitly avoid making
|
||||
/// log calls until this condition is met to ensure predictable behavior.
|
||||
auto have_ba_env_vals() const { return have_ba_env_vals_; }
|
||||
|
||||
/// Return the directory where the app expects to find its bundled Python
|
||||
@ -138,8 +140,8 @@ class CoreFeatureSet {
|
||||
/// Return the directory where bundled 3rd party Python files live.
|
||||
auto GetSitePythonDirectory() -> std::optional<std::string>;
|
||||
|
||||
// Are we using a non-standard app python dir (such as a 'sys' dir within a
|
||||
// user-python-dir).
|
||||
// Are we using a non-standard app python dir (such as a 'sys' dir within
|
||||
// a user-python-dir).
|
||||
auto using_custom_app_python_dir() const {
|
||||
return using_custom_app_python_dir_;
|
||||
}
|
||||
@ -165,7 +167,6 @@ class CoreFeatureSet {
|
||||
bool reset_vr_orientation{};
|
||||
bool user_ran_commands{};
|
||||
std::thread::id main_thread_id{};
|
||||
|
||||
bool vr_mode;
|
||||
std::mutex thread_name_map_mutex;
|
||||
std::unordered_map<std::thread::id, std::string> thread_name_map;
|
||||
@ -177,11 +178,11 @@ class CoreFeatureSet {
|
||||
#endif
|
||||
|
||||
private:
|
||||
explicit CoreFeatureSet(CoreConfig config);
|
||||
static void DoImport(const CoreConfig& config);
|
||||
static auto CalcBuildSrcDir() -> std::string;
|
||||
void RunSanityChecks();
|
||||
static void DoImport(const CoreConfig& config);
|
||||
void UpdateAppTime();
|
||||
explicit CoreFeatureSet(CoreConfig config);
|
||||
void PostInit();
|
||||
bool tried_importing_base_{};
|
||||
EventLoop* main_event_loop_{};
|
||||
|
||||
@ -27,8 +27,9 @@
|
||||
|
||||
// ------------------------- PLATFORM SELECTION --------------------------------
|
||||
|
||||
// This ugly chunk of macros simply pulls in the correct platform class header
|
||||
// for each platform and defines the actual class g_core->platform will be.
|
||||
// This ugly chunk of macros simply pulls in the correct platform class
|
||||
// header for each platform and defines the actual class g_core->platform
|
||||
// will be.
|
||||
|
||||
// Android ---------------------------------------------------------------------
|
||||
|
||||
@ -85,6 +86,7 @@
|
||||
|
||||
// A call that can be used by custom built native libraries (Python, etc.)
|
||||
// to forward along debug messages to us.
|
||||
//
|
||||
// FIXME: Reconcile this with our existing C++ version. This one does not
|
||||
// require the engine to be spun up so it better suited for things like
|
||||
// debugging native libs.
|
||||
|
||||
@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
|
||||
namespace ballistica {
|
||||
|
||||
// These are set automatically via script; don't modify them here.
|
||||
const int kEngineBuildNumber = 21289;
|
||||
const int kEngineBuildNumber = 21293;
|
||||
const char* kEngineVersion = "1.7.28";
|
||||
const int kEngineApiVersion = 8;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Template Feature Set
|
||||
# Template Fs Feature Set
|
||||
|
||||
This is an empty feature-set for use as reference or as a starting point when
|
||||
implementing new ones.
|
||||
|
||||
@ -9,16 +9,18 @@
|
||||
|
||||
namespace ballistica::template_fs {
|
||||
|
||||
// Declare a plain c PyInit_XXX function for our Python module;
|
||||
// this is how Python inits our binary module (and by extension, our
|
||||
// entire feature-set).
|
||||
// Declare a plain C PyInit_XXX function for our Python module. This is how
|
||||
// Python inits our binary module (and by extension, our entire
|
||||
// feature-set).
|
||||
extern "C" auto PyInit__batemplatefs() -> PyObject* {
|
||||
auto* builder = new PythonModuleBuilder(
|
||||
"_batemplatefs",
|
||||
// Native methods to add.
|
||||
|
||||
// Our native methods.
|
||||
{PythonMethodsTemplateFs::GetMethods()},
|
||||
// Our module exec. Here we can add classes, import other modules,
|
||||
// or whatever else (same as a regular Python script module).
|
||||
|
||||
// Our module exec. Here we can add classes, import other modules, or
|
||||
// whatever else (same as a regular Python script module).
|
||||
[](PyObject* module) -> int {
|
||||
BA_PYTHON_TRY;
|
||||
TemplateFsFeatureSet::OnModuleExec(module);
|
||||
@ -38,14 +40,16 @@ void TemplateFsPython::ImportPythonObjs() {
|
||||
|
||||
void TemplateFsPython::HelloWorld() {
|
||||
// Hold the GIL throughout this call so we can run in any thread.
|
||||
// Alternately we could limit this function to the logic thread
|
||||
// which always holds the GIL. In that case we'd want to
|
||||
// stick a BA_PRECONDITION(InLogicThread()) here to be sure.
|
||||
// Alternately, we could limit this function to the logic thread which
|
||||
// always holds the GIL. In that case we'd want to stick a
|
||||
// BA_PRECONDITION(InLogicThread()) here to so we'd raise an Exception if
|
||||
// someone called us from another thread.
|
||||
auto gil{Python::ScopedInterpreterLock()};
|
||||
|
||||
// Run the Python callable we grabbed. This will simply print any
|
||||
// errors, but we could disable that print and look at the call
|
||||
// results if any logic depended on this code running successfully.
|
||||
// Run the Python callable we grabbed in our binding code. By default,
|
||||
// this Call() will simply print any errors, but we could disable that
|
||||
// print and look at the call results if any logic depended on this code
|
||||
// running successfully.
|
||||
objs_.Get(ObjID::kHelloWorldCall).Call();
|
||||
}
|
||||
|
||||
|
||||
@ -9,13 +9,17 @@ import sys
|
||||
import subprocess
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import assert_never
|
||||
from typing import assert_never, TYPE_CHECKING
|
||||
|
||||
from efro.error import CleanError
|
||||
from efro.terminal import Clr
|
||||
from efrotools import replace_exact
|
||||
|
||||
from batools.spinoff._context import SpinoffContext
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from batools.featureset import FeatureSet
|
||||
|
||||
|
||||
class Command(Enum):
|
||||
"""Our top level commands."""
|
||||
@ -32,6 +36,8 @@ class Command(Enum):
|
||||
FEATURESETS = 'featuresets'
|
||||
CREATE = 'create'
|
||||
ADD_SUBMODULE_PARENT = 'add-submodule-parent'
|
||||
FEATURE_SET_COPY = 'fset-copy'
|
||||
FEATURE_SET_DELETE = 'fset-delete'
|
||||
|
||||
|
||||
def spinoff_main() -> None:
|
||||
@ -101,6 +107,10 @@ def _main() -> None:
|
||||
|
||||
public = getprojectconfig(Path(dst_root))['public']
|
||||
_do_add_submodule_parent(dst_root, is_new=False, public=public)
|
||||
elif cmd is Command.FEATURE_SET_COPY:
|
||||
_do_featureset_copy()
|
||||
elif cmd is Command.FEATURE_SET_DELETE:
|
||||
_do_featureset_delete()
|
||||
else:
|
||||
assert_never(cmd)
|
||||
|
||||
@ -250,6 +260,251 @@ def _do_featuresets(dst_root: str) -> None:
|
||||
print(f' {Clr.BLU}{fset.name}{Clr.RST}')
|
||||
|
||||
|
||||
def _fset_paths() -> list[str]:
|
||||
"""Given a feature-set, return all paths associated with it."""
|
||||
return [
|
||||
'config/featuresets/featureset_$(NAME).py',
|
||||
'src/assets/ba_data/python/$(NAME_PY_PKG)',
|
||||
'src/ballistica/$(NAME)',
|
||||
'src/meta/$(NAME_PY_PKG_META)',
|
||||
]
|
||||
|
||||
|
||||
def _filter_fset_path(path: str, fset: FeatureSet) -> str:
|
||||
return (
|
||||
path.replace('$(NAME)', fset.name)
|
||||
.replace('$(NAME_PY_PKG)', fset.name_python_package)
|
||||
.replace('$(NAME_PY_PKG_META)', fset.name_python_package_meta)
|
||||
)
|
||||
|
||||
|
||||
def _do_featureset_delete() -> None:
|
||||
from batools.featureset import FeatureSet
|
||||
|
||||
args = sys.argv[2:]
|
||||
if len(args) != 1:
|
||||
raise CleanError('Expected a featureset name.')
|
||||
|
||||
name = args[0]
|
||||
|
||||
# Just make a theoretical new featureset in case only parts of it
|
||||
# exist. (custom name formatting shouldnt matter here anyway)
|
||||
fset = FeatureSet(name)
|
||||
|
||||
if not os.path.exists('config/featuresets'):
|
||||
raise CleanError('Cannot run from this directory.')
|
||||
|
||||
paths_to_delete: list[str] = []
|
||||
for path in _fset_paths():
|
||||
paths_to_delete.append(_filter_fset_path(path, fset))
|
||||
|
||||
print(
|
||||
'\n' + '⎯' * 80 + f'\n{Clr.BLD}Deleting feature-set{Clr.RST}'
|
||||
f' {Clr.SMAG}{Clr.BLD}{name}{Clr.RST}{Clr.BLD}...{Clr.RST}\n'
|
||||
+ '⎯' * 80
|
||||
+ '\n'
|
||||
)
|
||||
|
||||
found_something = False
|
||||
for path in paths_to_delete:
|
||||
if os.path.exists(path):
|
||||
found_something = True
|
||||
print(f' Deleting {Clr.MAG}{path}{Clr.RST}')
|
||||
subprocess.run(['rm', '-rf', path], check=True)
|
||||
if not found_something:
|
||||
print(
|
||||
f' {Clr.WHT}No feature-set components found;'
|
||||
f' nothing to be done.{Clr.RST}'
|
||||
)
|
||||
|
||||
print(
|
||||
f"\n{Clr.GRN}{Clr.BLD}Job's done!{Clr.RST}\n"
|
||||
f'{Clr.BLD}Next, run'
|
||||
f' {Clr.BLU}`make update`{Clr.RST}{Clr.BLD} to update project'
|
||||
f' files to reflect these changes.{Clr.RST}'
|
||||
)
|
||||
|
||||
|
||||
def _do_featureset_copy() -> None:
|
||||
# pylint: disable=too-many-locals
|
||||
from efrotools import extract_flag
|
||||
|
||||
from batools.featureset import FeatureSet
|
||||
|
||||
args = sys.argv[2:]
|
||||
|
||||
force = extract_flag(args, '--force')
|
||||
|
||||
if len(args) != 2:
|
||||
raise CleanError('Expected a src and dst featureset name.')
|
||||
|
||||
src = args[0]
|
||||
dst = args[1]
|
||||
|
||||
if not os.path.exists('config/featuresets'):
|
||||
raise CleanError('Cannot run from this directory.')
|
||||
|
||||
# This will make sure both feature-set names are valid and give us
|
||||
# name variations. Load src from the project to pick up custom title
|
||||
# variations/etc.
|
||||
fsets = {f.name: f for f in FeatureSet.get_all_for_project('.')}
|
||||
if src not in fsets:
|
||||
raise CleanError('src feature-set {src} not found.')
|
||||
srcfs = fsets[src]
|
||||
# Just go with defaults for dst. Note that this means any custom
|
||||
# title forms in src's config script will get filtered to be setting
|
||||
# the default form of dst, which is redundant. Maybe we could filter that
|
||||
# out.
|
||||
dstfs = FeatureSet(dst)
|
||||
|
||||
# Make sure src *does* exist.
|
||||
if not os.path.exists(f'config/featuresets/featureset_{src}.py'):
|
||||
raise CleanError(f"Src feature-set '{src}' not found.")
|
||||
|
||||
# Make sure dst does *not* exist (unless we're forcing).
|
||||
if os.path.exists(f'config/featuresets/featureset_{dst}.py') and not force:
|
||||
raise CleanError(
|
||||
f"Dst feature-set '{dst}' already exists."
|
||||
' Use --force to blow it away.'
|
||||
)
|
||||
|
||||
paths_to_copy: list[tuple[str, str]] = []
|
||||
for path in _fset_paths():
|
||||
paths_to_copy.append(
|
||||
(_filter_fset_path(path, srcfs), _filter_fset_path(path, dstfs))
|
||||
)
|
||||
|
||||
# Replace variations of our name. Note that we don't have to include
|
||||
# stuff like name_python_package_meta here because that is covered
|
||||
# by our base name replacement. Also note that we include upper()
|
||||
# for C/C++ header #ifndefs.
|
||||
subs = [
|
||||
(srcfs.name, dstfs.name),
|
||||
(srcfs.name_compact, dstfs.name_compact),
|
||||
(srcfs.name_title, dstfs.name_title),
|
||||
(srcfs.name_camel, dstfs.name_camel),
|
||||
(srcfs.name.upper(), dstfs.name.upper()),
|
||||
]
|
||||
|
||||
# Sanity check: we don't currently support renaming subdirs, so error
|
||||
# if that would happen.
|
||||
for srcpath, _dstpath in paths_to_copy:
|
||||
for root, dirs, _fnames in os.walk(srcpath):
|
||||
for dname in dirs:
|
||||
if any(sub[0] in dname for sub in subs):
|
||||
raise CleanError(
|
||||
'Directory name filtering is not supported'
|
||||
f" (would filter '{root}/{dname}')."
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Ok, at this point we get started working and assume things will succeed.
|
||||
# If anything fails at this point we should add a pre-check for it above.
|
||||
print(
|
||||
'\n' + '⎯' * 80 + f'\n{Clr.BLD}Copying feature-set{Clr.RST}'
|
||||
f' {Clr.SMAG}{Clr.BLD}{src}{Clr.RST}'
|
||||
f' {Clr.BLD}to{Clr.RST}'
|
||||
f' {Clr.SMAG}{Clr.BLD}{dst}{Clr.RST}'
|
||||
f'{Clr.BLD}...{Clr.RST}\n' + '⎯' * 80
|
||||
)
|
||||
|
||||
print(f'\n{Clr.BLD}Will filter the following text:{Clr.RST}')
|
||||
for subsrc, subdst in subs:
|
||||
print(
|
||||
f' {Clr.MAG}{subsrc}{Clr.RST}'
|
||||
f' {Clr.BLD}->{Clr.RST} {Clr.MAG}{subdst}{Clr.RST}'
|
||||
)
|
||||
|
||||
print(f'\n{Clr.BLD}Copying/filtering files...{Clr.RST}')
|
||||
|
||||
for srcpath, dstpath in paths_to_copy:
|
||||
_do_featureset_copy_dir(srcpath, dstpath, subs, force)
|
||||
|
||||
print(
|
||||
f"\n{Clr.GRN}{Clr.BLD}Job's done!{Clr.RST}\n"
|
||||
f'{Clr.BLD}Next, run'
|
||||
f' {Clr.BLU}`make update`{Clr.RST}{Clr.BLD} to update project'
|
||||
f' files to reflect these changes.{Clr.RST}'
|
||||
)
|
||||
|
||||
|
||||
def _do_featureset_copy_dir(
|
||||
srcpath: str, dstpath: str, subs: list[tuple[str, str]], force: bool
|
||||
) -> None:
|
||||
# pylint: disable=too-many-locals
|
||||
# pylint: disable=too-many-branches
|
||||
|
||||
# This feature-set might not have this component. No biggie.
|
||||
if not os.path.exists(srcpath):
|
||||
return
|
||||
|
||||
if force:
|
||||
subprocess.run(['rm', '-rf', dstpath], check=True)
|
||||
|
||||
if not os.path.exists(srcpath):
|
||||
raise CleanError(f'src path {srcpath} is not a dir.')
|
||||
if os.path.exists(dstpath):
|
||||
raise CleanError(f'dst path {srcpath} already exists.')
|
||||
|
||||
filtered_exts = ['.cc', '.h', '.py', '.md', '.inc']
|
||||
|
||||
# Eww; reinventing the wheel here; should tap into existing
|
||||
# spinoff logic or something.
|
||||
cruft_names = ['.DS_Store', 'mgen', '_mgen']
|
||||
|
||||
# We currently just copy the full dir and then rename/filter
|
||||
# individual files. If we need to filter subdir names at some point
|
||||
# we'll need fancier code.
|
||||
subprocess.run(['cp', '-r', srcpath, dstpath], check=True)
|
||||
for root, dnames, fnames in os.walk(dstpath, topdown=True):
|
||||
for dname in dnames:
|
||||
if dname in cruft_names:
|
||||
# Prevent us from recursing into it and blow it away.
|
||||
dnames.remove(dname)
|
||||
subprocess.run(
|
||||
['rm', '-rf', os.path.join(root, dname)], check=True
|
||||
)
|
||||
for fname in fnames:
|
||||
if fname in cruft_names:
|
||||
os.unlink(os.path.join(root, fname))
|
||||
continue
|
||||
fnamefilt = fname
|
||||
for subsrc, subdst in subs:
|
||||
fnamefilt = fnamefilt.replace(subsrc, subdst)
|
||||
if fnamefilt != fname:
|
||||
subprocess.run(
|
||||
[
|
||||
'mv',
|
||||
os.path.join(root, fname),
|
||||
os.path.join(root, fnamefilt),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
# Now filter contents.
|
||||
if not any(fname.endswith(ext) for ext in filtered_exts):
|
||||
print(
|
||||
f'{Clr.YLW}WARNING:'
|
||||
f' not filtering file with unrecognized extension:'
|
||||
f" '{fname}'{Clr.RST}"
|
||||
)
|
||||
continue
|
||||
with open(
|
||||
os.path.join(root, fnamefilt), encoding='utf-8'
|
||||
) as infile:
|
||||
contents = infile.read()
|
||||
for subsrc, subdst in subs:
|
||||
contents = contents.replace(subsrc, subdst)
|
||||
with open(
|
||||
os.path.join(root, fnamefilt), 'w', encoding='utf-8'
|
||||
) as outfile:
|
||||
outfile.write(contents)
|
||||
|
||||
print(
|
||||
f' {Clr.MAG}{srcpath}{Clr.RST} {Clr.BLD}->{Clr.RST}'
|
||||
f' {Clr.MAG}{dstpath}{Clr.RST}'
|
||||
)
|
||||
|
||||
|
||||
def _do_override(src_root: str | None, dst_root: str) -> None:
|
||||
if src_root is None:
|
||||
raise CleanError('This only works on dst projects.')
|
||||
@ -364,7 +619,14 @@ def _print_available_commands() -> None:
|
||||
' The same can be\n'
|
||||
' achieved by passing --submodule-parent to'
|
||||
' the \'create\'\n'
|
||||
' command.'
|
||||
' command.\n'
|
||||
f' {bgn}fset-copy [src, dst]{end} Copy feature-set src to dst.'
|
||||
' Replaces variations of src\n'
|
||||
' feature-set name with dst equivalent,'
|
||||
' though may need\n'
|
||||
' some manual correction afterwards to be'
|
||||
' functional.\n'
|
||||
f' {bgn}fset-delete [name]{end} Delete a feature-set.'
|
||||
),
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user