diff --git a/.efrocachemap b/.efrocachemap
index 52321f95..196bf33e 100644
--- a/.efrocachemap
+++ b/.efrocachemap
@@ -4072,26 +4072,26 @@
"build/assets/workspace/ninjafightplug.py": "https://files.ballistica.net/cache/ba1/18/4b/787a9267e17be3c49966072581a5",
"build/assets/workspace/onslaughtplug.py": "https://files.ballistica.net/cache/ba1/20/f6/4ce9bc3c1f3732f6adf8237fbe9b",
"build/assets/workspace/runaroundplug.py": "https://files.ballistica.net/cache/ba1/a5/30/9058181df0b1255bf6950cbc7813",
- "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/f2/dc/bf56d88353b11986373e899b5bf6",
- "build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/3c/43/731228e0f7c8e5396b06ca916ffd",
- "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/71/24/b8bedec2fab3fd83381a23385a9a",
- "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/5f/96/a43273ad50d90d9342f45c22c0e8",
- "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/c9/75/393357c2a3f4d3eec2035bc3ecff",
- "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/b0/85/d8778105cf5a536cb5e6e618c723",
- "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/e8/41/5128ac78c21f238d0613ab8e2ece",
- "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/7f/4d/96dadaf30683f4a69426518ca334",
- "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/14/14/9f38fb471edd66d076b9d611ba55",
- "build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/12/37/d741089b132fbcb8b22d9795479a",
- "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/b0/4f/e9ab4d8fc7405d9c8f667eaacb73",
- "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/7e/2f/2e475906c60e960a63fdd3e5f24d",
- "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/34/48/662a2168751cd25eaae50f0707df",
- "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/1d/2f/ab1c77b292726178d0eb1f5699ad",
- "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/44/b8/3c6e810fa14f014b9c91db7ab88a",
- "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/14/59/2164e23d78a6b16a97b3bfe7bdb2",
- "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/4f/91/83fa21e46183f74300820e98f4a1",
- "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/61/f5/18b5f9055fb48a1c91ce25f45dee",
- "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/87/53/556e49d3273d4be8bfeba546ecb8",
- "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/98/9b/73c254c31fe68fc21cae40a1761e",
+ "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/dd/e6/324379d12d8773f739bbc1ee8742",
+ "build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/78/24/27722a657784f579f5824b089691",
+ "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/83/df/eabeb7b26fb4b5bf8b899ebcd067",
+ "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/0a/99/6e19e6ad69a6f0fb4b73cacdd8e5",
+ "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/ad/bd/2f23197da76c148e41b359f0022a",
+ "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/29/29/2a87524a909d447f938762919de4",
+ "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/71/de/0b68a02b52ee2c5d356343dccd00",
+ "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/ea/22/0f260a3a44e4cfdf7706705c81a1",
+ "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/58/1e/023f58fe727fc70ade8ae6d5a880",
+ "build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/1e/50/a607cd2582de36c989f0d470658e",
+ "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/81/e2/d816ac2f91de53683636bf3144bd",
+ "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/39/8e/756a7cc3ddcecd1146ea4589bf72",
+ "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/3c/ad/dcabf1359a1cae7c683036b23856",
+ "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/f9/06/3b9d1661d6e4cc21c42748db322b",
+ "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/cb/89/a4a0de5aaa1e1929871973587ddd",
+ "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/7a/3b/35ea440785a5ac39cb802b159c8a",
+ "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/53/9b/817e65aebfe3574d50385a08be52",
+ "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/84/b0/70de1c4c94a2c1c8d1fe54da695f",
+ "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/ba/ec/009c2ead7a78de11c0e55e447881",
+ "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/1f/8d/cdfe64de06a6861c27828b9120e8",
"build/prefab/lib/linux_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/29/dc/e5b08aae2dbe3222082aaa1e90e7",
"build/prefab/lib/linux_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/ce/bb/5796a83ce9d1c9cf58e08f3ff351",
"build/prefab/lib/linux_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/31/65/014221e46f7974f40aa84c8922dc",
@@ -4108,14 +4108,14 @@
"build/prefab/lib/mac_x86_64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/14/4b/68a0ece3c1f191183b695cf45a4d",
"build/prefab/lib/mac_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/1f/3f/d899a46cc0dc8bc4f1b38f9318a5",
"build/prefab/lib/mac_x86_64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/de/99/95fdcb3f614a7b83ada148bca38d",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/74/cb/8be4f4dec7fd2bf11920a48c5446",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/ee/30/7d0a59834145150a5a64d29f2add",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/ad/53/a20f19509c6f90472c46004618e7",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/d7/20/8311a232ef822de08b7eea1185e5",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/f9/bb/cfb390d5f9d37997cacc804ee7ae",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/a6/fa/5651f4f3329109515ad15fbf7fbc",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/d8/49/3f000f69e2b50364b7d444f90b3f",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/7b/bd/aff34983fc5a00bfb59fd47d14cb",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/52/08/03398c2a3fcc29a99c687d5efbb4",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/e8/85/12943e9d34b55fd9a0d0ec0288ea",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/da/b1/a90ccef09b8305f32520688545bf",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/aa/a0/ce7ffae656e7877a72d35677114f",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/26/ef/dcfa52d8f4bb9c518c51687b2b3d",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/09/e9/31c76975c552041232b3ffd5fbf0",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/b0/35/a132ee60e1a20af514abe1276236",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/a4/94/64fc55a516281cc28fcfccebbd5b",
"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/ballistica/base/mgen/pyembed/binding_base.inc": "https://files.ballistica.net/cache/ba1/ea/6a/6a4721b144e5e297b542d2a0eea2",
diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml
index e4100ccf..6ecadfcc 100644
--- a/.idea/dictionaries/ericf.xml
+++ b/.idea/dictionaries/ericf.xml
@@ -1093,8 +1093,11 @@
fsets
fsettings
fsmetapackagename
+ fsmodulename
+ fsmodulepath
fsname
fspackagename
+ fspackagesroot
fsplit
fsrc
fstab
@@ -1359,6 +1362,7 @@
imghdr
imgw
importlines
+ importparts
incentivized
includelines
includetest
@@ -1574,8 +1578,10 @@
linenumber
linenums
linestart
+ linestripped
linetype
linetypes
+ linewords
linflav
linkstoryboards
linkto
@@ -1765,6 +1771,7 @@
moduledir
modulefinder
modulename
+ modulenames
modulepath
modutils
moola
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee5eae32..2f0a8fc4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-### 1.7.20 (build 21090, api 8, 2023-06-10)
+### 1.7.20 (build 21091, api 8, 2023-06-12)
- 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
diff --git a/ballisticakit-cmake/.idea/dictionaries/ericf.xml b/ballisticakit-cmake/.idea/dictionaries/ericf.xml
index e1e330b6..bc7b5fd5 100644
--- a/ballisticakit-cmake/.idea/dictionaries/ericf.xml
+++ b/ballisticakit-cmake/.idea/dictionaries/ericf.xml
@@ -649,8 +649,11 @@
fsetname
fsets
fsmetapackagename
+ fsmodulename
+ fsmodulepath
fsname
fspackagename
+ fspackagesroot
fstestspackagename
fsum
ftos
@@ -799,6 +802,7 @@
imagewidget
imayushsaini
importlines
+ importparts
incentivized
includelines
indata
@@ -907,6 +911,8 @@
linebegin
linemax
linestart
+ linestripped
+ linewords
linkstoryboards
listobj
llock
@@ -1013,6 +1019,7 @@
modetype
modstr
modtime
+ modulenames
moduletype
momemtary
morecnt
diff --git a/src/assets/ba_data/python/baclassic/_tips.py b/src/assets/ba_data/python/baclassic/_tips.py
index 9a63acb8..d356b664 100644
--- a/src/assets/ba_data/python/baclassic/_tips.py
+++ b/src/assets/ba_data/python/baclassic/_tips.py
@@ -7,7 +7,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
-import _babase
+import babase
if TYPE_CHECKING:
pass
@@ -105,7 +105,7 @@ def get_all_tips() -> list[str]:
'color of sparks from its fuse: yellow..orange..red..BOOM.'
),
]
- app = _babase.app
+ app = babase.app
if not app.iircade_mode:
tips += [
'If your framerate is choppy, try turning down resolution\nor '
diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py
index 0075dfca..0beaed6a 100644
--- a/src/assets/ba_data/python/baenv.py
+++ b/src/assets/ba_data/python/baenv.py
@@ -28,7 +28,7 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be
# using.
-TARGET_BALLISTICA_BUILD = 21090
+TARGET_BALLISTICA_BUILD = 21091
TARGET_BALLISTICA_VERSION = '1.7.20'
_g_env_config: EnvConfig | None = None
diff --git a/src/assets/ba_data/python/bascenev1/_dependency.py b/src/assets/ba_data/python/bascenev1/_dependency.py
index ecbc1610..bbc956c0 100644
--- a/src/assets/ba_data/python/bascenev1/_dependency.py
+++ b/src/assets/ba_data/python/bascenev1/_dependency.py
@@ -7,7 +7,8 @@ from __future__ import annotations
import weakref
from typing import Generic, TypeVar, TYPE_CHECKING
-import _babase
+import babase
+
import _bascenev1
if TYPE_CHECKING:
@@ -304,7 +305,7 @@ class AssetPackage(DependencyComponent):
super().__init__()
# This is used internally by the get_package_xxx calls.
- self.context = _babase.ContextRef()
+ self.context = babase.ContextRef()
entry = self._dep_entry()
assert entry is not None
@@ -424,7 +425,7 @@ def test_depset() -> None:
# To test this, add prints on __del__ for stuff used above;
# everything should be dead at this point if we have no cycles.
print('everything should be cleaned up...')
- _babase.quit()
+ babase.quit()
class DependencyError(Exception):
diff --git a/src/assets/ba_data/python/bascenev1/_hooks.py b/src/assets/ba_data/python/bascenev1/_hooks.py
index 7ab12a7f..c531feb3 100644
--- a/src/assets/ba_data/python/bascenev1/_hooks.py
+++ b/src/assets/ba_data/python/bascenev1/_hooks.py
@@ -7,7 +7,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING
-import _babase
+import babase
+
import _bascenev1
if TYPE_CHECKING:
@@ -17,9 +18,9 @@ if TYPE_CHECKING:
def launch_main_menu_session() -> None:
- assert _babase.app.classic is not None
+ assert babase.app.classic is not None
- _bascenev1.new_host_session(_babase.app.classic.get_main_menu_session())
+ _bascenev1.new_host_session(babase.app.classic.get_main_menu_session())
def get_player_icon(sessionplayer: bascenev1.SessionPlayer) -> dict[str, Any]:
@@ -45,7 +46,7 @@ def filter_chat_message(msg: str, client_id: int) -> str | None:
def local_chat_message(msg: str) -> None:
- classic = _babase.app.classic
+ classic = babase.app.classic
assert classic is not None
party_window = (
None if classic.party_window is None else classic.party_window()
diff --git a/src/assets/ba_data/python/bauiv1/_uitypes.py b/src/assets/ba_data/python/bauiv1/_uitypes.py
index f8e5989a..b97ed2a3 100644
--- a/src/assets/ba_data/python/bauiv1/_uitypes.py
+++ b/src/assets/ba_data/python/bauiv1/_uitypes.py
@@ -9,7 +9,8 @@ import weakref
from dataclasses import dataclass
from typing import TYPE_CHECKING
-import _babase
+import babase
+
import _bauiv1
if TYPE_CHECKING:
@@ -199,8 +200,8 @@ def uicleanupcheck(obj: Any, widget: bauiv1.Widget) -> None:
widget.add_delete_callback(foobar)
- assert _babase.app.classic is not None
- _babase.app.ui_v1.cleanupchecks.append(
+ assert babase.app.classic is not None
+ babase.app.ui_v1.cleanupchecks.append(
UICleanupCheck(
obj=weakref.ref(obj), widget=widget, widget_death_time=None
)
@@ -209,10 +210,10 @@ def uicleanupcheck(obj: Any, widget: bauiv1.Widget) -> None:
def ui_upkeep() -> None:
"""Run UI cleanup checks, etc. should be called periodically."""
- assert _babase.app.classic is not None
- ui = _babase.app.ui_v1
+ assert babase.app.classic is not None
+ ui = babase.app.ui_v1
remainingchecks = []
- now = _babase.apptime()
+ now = babase.apptime()
for check in ui.cleanupchecks:
obj = check.obj()
diff --git a/src/assets/ba_data/python/bauiv1/onscreenkeyboard.py b/src/assets/ba_data/python/bauiv1/onscreenkeyboard.py
index 9c614c7a..a8e6098f 100644
--- a/src/assets/ba_data/python/bauiv1/onscreenkeyboard.py
+++ b/src/assets/ba_data/python/bauiv1/onscreenkeyboard.py
@@ -10,7 +10,7 @@ from typing import cast
from typing import TYPE_CHECKING
import babase
-from _babase import screenmessage
+
import _bauiv1
from bauiv1._uitypes import Window
@@ -379,12 +379,12 @@ class OnScreenKeyboardWindow(Window):
self._load_keyboard()
if len(kbexports) < 2:
_bauiv1.getsound('error').play()
- screenmessage(
+ babase.screenmessage(
babase.Lstr(resource='keyboardNoOthersAvailableText'),
color=(1, 0, 0),
)
else:
- screenmessage(
+ babase.screenmessage(
babase.Lstr(
resource='keyboardSwitchText',
subs=[('${NAME}', self._keyboard.name)],
diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc
index 077fad41..ac7ad7f4 100644
--- a/src/ballistica/shared/ballistica.cc
+++ b/src/ballistica/shared/ballistica.cc
@@ -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 = 21090;
+const int kEngineBuildNumber = 21091;
const char* kEngineVersion = "1.7.20";
auto MonolithicMain(const core::CoreConfig& core_config) -> int {
diff --git a/tools/batools/build.py b/tools/batools/build.py
index df5585ae..cd31fe69 100644
--- a/tools/batools/build.py
+++ b/tools/batools/build.py
@@ -35,11 +35,11 @@ class PyRequirement:
# entries; this accounts for manual installations or other nonstandard
# setups.
-# Note 2: That is probably overkill. We can probably just replace
-# this with a simple requirements.txt file, can't we? Feels like we're
-# mostly reinventing the wheel here. We just need a clean way to
-# check/list missing stuff without necessarily installing it. And as far
-# as manually-installed bits, pip itself must have some way to allow for
+# Note 2: That is probably overkill. We can probably just replace this
+# with a simple requirements.txt file, can't we? Feels like we're mostly
+# reinventing the wheel here. We just need a clean way to check/list
+# missing stuff without necessarily installing it. And as far as
+# manually-installed bits, pip itself must have some way to allow for
# that, right?...
PY_REQUIREMENTS = [
PyRequirement(modulename='pylint', minversion=[2, 17, 3]),
@@ -63,9 +63,9 @@ PY_REQUIREMENTS = [
PyRequirement(pipname='filelock', minversion=[3, 12, 0]),
]
-# Parts of full-tests suite we only run on particular days.
-# (This runs in listed order so should be randomized by hand to avoid
-# clustering similar tests too much)
+# Parts of full-tests suite we only run on particular days. This runs in
+# listed order so should be randomized by hand to avoid clustering
+# similar tests too much.
SPARSE_TEST_BUILDS: list[list[str]] = [
['ios.pylibs.debug', 'android.pylibs.arm'],
['linux.package', 'android.pylibs.arm64'],
@@ -82,8 +82,8 @@ SPARSE_TEST_BUILDS: list[list[str]] = [
['mac.pylibs.debug', 'android.package'],
]
-# Currently only doing sparse-tests in core; not spinoffs.
-# (whole word will get subbed out in spinoffs so this will be false)
+# Currently only doing sparse-tests in core; not spinoffs (whole word
+# will get subbed out in spinoffs so this will evaluate to False).
DO_SPARSE_TEST_BUILDS = 'ballistica' + 'kit' == 'ballisticakit'
@@ -115,8 +115,9 @@ def lazybuild(target: str, category: LazyBuildCategory, command: str) -> None:
LazyBuildContext(
target=target,
command=command,
- # Since this category can kick off cleans and blow things away,
- # its not safe to have multiple builds going with it at once.
+ # Since this category can kick off cleans and blow things
+ # away, its not safe to have multiple builds going with it
+ # at once.
buildlockname=category.value,
# Regular paths; changes to these will trigger meta build.
srcpaths=[
@@ -124,12 +125,14 @@ def lazybuild(target: str, category: LazyBuildCategory, command: str) -> None:
'src/meta',
'src/ballistica/shared/foundation/types.h',
],
- # Our meta Makefile targets generally don't list tools scripts
- # that can affect their creation as sources, so let's set up
- # a catch-all here: when any of our tools stuff changes we'll
- # blow away all existing meta builds.
- # Update: also including featureset-defs here; any time we're
- # mucking with those it's good to start fresh to be sure.
+ # Our meta Makefile targets generally don't list tools
+ # scripts that can affect their creation as sources, so
+ # let's set up a catch-all here: when any of our tools stuff
+ # changes we'll blow away all existing meta builds.
+ #
+ # Update: also including featureset-defs here; any time
+ # we're mucking with those it's good to start fresh to be
+ # sure.
srcpaths_fullclean=[
'tools/efrotools',
'tools/efrotoolsinternal',
@@ -198,9 +201,9 @@ def lazybuild(target: str, category: LazyBuildCategory, command: str) -> None:
elif category is LazyBuildCategory.RESOURCES:
LazyBuildContext(
target=target,
- # Even though this category currently doesn't run any
- # clean commands, going to restrict to one use at a time for
- # now in case we want to add that.
+ # Even though this category currently doesn't run any clean
+ # commands, going to restrict to one use at a time for now
+ # in case we want to add that.
buildlockname=category.value,
srcpaths=[
'Makefile',
@@ -214,18 +217,18 @@ def lazybuild(target: str, category: LazyBuildCategory, command: str) -> None:
elif category is LazyBuildCategory.ASSETS:
def _filefilter(root: str, filename: str) -> bool:
- # Exclude tools/spinoff; it doesn't affect asset builds
- # and we don't want to break if it is a symlink pointing
- # to a not-present parent repo.
+ # Exclude tools/spinoff; it doesn't affect asset builds and
+ # we don't want to break if it is a symlink pointing to a
+ # not-present parent repo.
if root == 'tools' and filename == 'spinoff':
return False
return True
LazyBuildContext(
target=target,
- # Even though this category currently doesn't run any
- # clean commands, going to restrict to one use at a time for
- # now in case we want to add that.
+ # Even though this category currently doesn't run any clean
+ # commands, going to restrict to one use at a time for now
+ # in case we want to add that.
buildlockname=category.value,
srcpaths=[
'Makefile',
@@ -240,8 +243,8 @@ def lazybuild(target: str, category: LazyBuildCategory, command: str) -> None:
elif category is LazyBuildCategory.DUMMYMODULES:
def _filefilter(root: str, filename: str) -> bool:
- # In our C++ sources, only look at stuff with 'python' in the
- # name.
+ # In our C++ sources, only look at stuff with 'python' in
+ # the name.
if root.startswith('ballistica'):
return 'python' in filename
@@ -250,8 +253,8 @@ def lazybuild(target: str, category: LazyBuildCategory, command: str) -> None:
LazyBuildContext(
target=target,
- # This category builds binaries and other crazy stuff
- # so we definitely want to restrict to one at a time.
+ # This category builds binaries and other crazy stuff so we
+ # definitely want to restrict to one at a time.
buildlockname=category.value,
srcpaths=[
'config/featuresets',
@@ -313,26 +316,28 @@ def gen_fulltest_buildfile_android() -> None:
"""
# pylint: disable=too-many-branches
- # Its a pretty big time-suck building all architectures for
- # all of our subplatforms, so lets usually just build a single one.
- # We'll rotate it though and occasionally do all 4 at once just to
- # be safe.
+ # Its a pretty big time-suck building all architectures for all of
+ # our subplatforms, so lets usually just build a single one. We'll
+ # rotate it though and occasionally do all 4 at once just to be
+ # safe.
dayoffset = datetime.datetime.now().timetuple().tm_yday
# Let's only do a full 'prod' once every two times through the loop.
- # (it really should never catch anything that individual platforms don't)
+ # (it really should never catch anything that individual platforms
+ # don't)
modes = ['arm', 'arm64', 'x86', 'x86_64']
modes += modes
modes.append('prod')
# By default we cycle through build architectures for each flavor.
- # However, for minor flavor with low risk of platform-dependent breakage
- # we stick to a single one to keep disk space costs lower. (build files
- # amount to several gigs per mode per flavor)
- # UPDATE: Now that we have CPU time to spare, we simply always do 'arm64'
- # or 'prod' depending on build type; this results in 1 or 4 architectures
- # worth of build files per flavor instead of 8 (prod + 4 singles) and
- # keeps our daily runs identical.
+ # However, for minor flavor with low risk of platform-dependent
+ # breakage we stick to a single one to keep disk space costs lower.
+ # (build files amount to several gigs per mode per flavor)
+ #
+ # UPDATE: Now that we have CPU time to spare, we simply always do
+ # 'arm64' or 'prod' depending on build type; this results in 1 or 4
+ # architectures worth of build files per flavor instead of 8 (prod +
+ # 4 singles) and keeps our daily runs identical.
lightweight_flavors = {'template', 'arcade', 'demo', 'iircade'}
lines = []
@@ -433,10 +438,10 @@ def gen_fulltest_buildfile_windows() -> None:
lines: list[str] = []
- # We want to do one regular, one headless, and one oculus build,
- # but let's switch up 32 or 64 bit based on the day.
- # Also occasionally throw a release build in but stick to
- # mostly debug builds to keep build times speedier.
+ # We want to do one regular, one headless, and one oculus build, but
+ # let's switch up 32 or 64 bit based on the day. Also occasionally
+ # throw a release build in but stick to mostly debug builds to keep
+ # build times speedier.
pval1 = 'Win32' if dayoffset % 2 == 0 else 'x64'
pval2 = 'Win32' if (dayoffset + 1) % 2 == 0 else 'x64'
pval3 = 'Win32' if (dayoffset + 2) % 2 == 0 else 'x64'
@@ -692,10 +697,11 @@ def checkenv() -> None:
)
# Make sure rsync is version 3.1.0 or newer.
- # Macs come with ancient rsync versions with significant downsides such
- # as single second file mod time resolution which has started to cause
- # problems with build setups. So now am trying to make sure my Macs
- # have an up-to-date one installed (via homebrew).
+ #
+ # Macs come with ancient rsync versions with significant downsides
+ # such as single second file mod time resolution which has started
+ # to cause problems with build setups. So now am trying to make sure
+ # my Macs have an up-to-date one installed (via homebrew).
rsyncver = tuple(
int(s)
for s in subprocess.run(
@@ -772,8 +778,10 @@ def checkenv() -> None:
) from exc
# Check for some required python modules.
+ #
# FIXME: since all of these come from pip now, we should just use
- # pip --list to check versions on everything instead of doing it ad-hoc.
+ # pip --list to check versions on everything instead of doing it
+ # ad-hoc.
for req in PY_REQUIREMENTS:
try:
modname = req.modulename
diff --git a/tools/batools/project/_checks.py b/tools/batools/project/_checks.py
index df396736..6de16e06 100755
--- a/tools/batools/project/_checks.py
+++ b/tools/batools/project/_checks.py
@@ -277,6 +277,7 @@ def check_python_files(self: ProjectUpdater) -> None:
from efrotools.code import get_script_filenames
scriptfiles = get_script_filenames(Path(self.projroot))
+
for fname in scriptfiles:
_check_python_file(self, fname)
@@ -289,6 +290,7 @@ def check_python_files(self: ProjectUpdater) -> None:
'src/assets/ba_data/python',
'src/meta',
]
+
# EXCEPT for the following specifics.
ignores: dict[str, set[str]] = {
'tools': {
@@ -337,58 +339,125 @@ def check_python_files(self: ProjectUpdater) -> None:
def _check_python_file(self: ProjectUpdater, fname: str) -> None:
- # pylint: disable=too-many-branches
-
- with open(fname, encoding='utf-8') as infile:
+ with open(os.path.join(self.projroot, fname), encoding='utf-8') as infile:
contents = infile.read()
lines = contents.splitlines()
- # Make sure all standalone scripts are pointing to the right version
- # of Python (with a few exceptions where it needs to differ)
- if contents.startswith('#!/'):
- copyrightline = 1
- if fname not in ['tools/vmshell']:
- if not contents.startswith(f'#!/usr/bin/env python{PYVER}'):
- raise CleanError(
- f'Incorrect shebang (first line) for ' f'{fname}.'
- )
- else:
- copyrightline = 0
+ _check_python_file_shebang(self, fname, contents)
+ _check_python_file_license(self, fname, lines)
+ _check_python_file_imports(self, fname, lines)
- # Special case: it there's spinoff autogenerate notice there,
- # look below it.
- if (
- lines[copyrightline] == ''
- and 'THIS FILE IS AUTOGENERATED' in lines[copyrightline + 1]
- ):
- copyrightline += 2
- if lines[copyrightline].startswith('# Synced from '):
- copyrightline += 3
+def _check_python_file_imports(
+ self: ProjectUpdater, fname: str, lines: list[str]
+) -> None:
+ # pylint: disable=too-many-locals
+ # pylint: disable=too-many-branches
+ fspackagesroot = 'src/assets/ba_data/python/'
+
+ # Only checking modules that are part of a featureset.
+ if not fname.startswith(fspackagesroot):
+ return
+ parts = fname.split('/')
+ if parts[4].endswith('.py'):
+ return
+ fsmodulename = parts[4]
for i, line in enumerate(lines):
- # FIXME: update this for the new feature-set world. Perhaps we
- # can screen for imports based on feature-set dependencies or
- # perhaps we should not even try and just let feature-set test
- # builds catch problems like that.
- if bool(False):
- # Stuff under the babase module.
- if '/babase/' in fname:
- # Don't allow importing ba at the top level from within babase.
- if line == 'import babase':
- raise CleanError(
- f'{fname}:{i+1}: no top level babase imports allowed'
- f' under babase module.'
- )
- if '/bascenev1lib/' in fname:
- # Don't allow importing _babase or _baplus anywhere here.
- # (any internal needs should be in babase.internal)
- if 'import _babase' in line:
- raise CleanError(
- f'{fname}:{i+1}: _babase or _baplus imports not'
- f' allowed under bascenev1lib.'
- )
+ linestripped = line.strip()
+ top_level = not line.startswith(' ')
+ linewords = linestripped.split(' ')
+ if len(linewords) < 2:
+ continue
+
+ if linewords[0] == 'import':
+ assert len(linewords) > 1
+ modulepath = linewords[1]
+ importparts = None
+ elif (
+ linewords[0] == 'from'
+ and len(linewords) > 2
+ and linewords[2] == 'import'
+ ):
+ modulepath = linewords[1]
+ importparts = linewords[3:]
+ else:
+ continue
+
+ # Look for simple cases of 'from foo import bar'.
+ if (
+ importparts
+ and '(' not in importparts[0]
+ and ',' not in importparts[0]
+ ):
+ single_import = importparts[0]
+ else:
+ single_import = None
+
+ modulenames = modulepath.split('.')
+
+ # These are fine.
+ if modulenames[0] in {'__future__', '__main__'}:
+ continue
+
+ # If they're importing a _foo module, make sure they are part of
+ # featureset foo.
+ if (
+ modulenames[0].startswith('_')
+ and modulenames[0] != f'_{fsmodulename}'
+ ):
+ raise CleanError(
+ f'{fname}:{i+1}: Import of private module {modulenames[0]}'
+ f' not allowed from this featureset package ({fsmodulename}).'
+ )
+
+ # If they're importing foo._bar, make sure they are part of
+ # featureset foo.
+ for modulename in modulenames[1:]:
+ if modulename.startswith('_') and modulenames[0] != fsmodulename:
+ raise CleanError(
+ f'{fname}:{i+1}: import of private module {modulepath}'
+ f' not allowed from this featureset package'
+ f' ({fsmodulename}).'
+ )
+
+ # Modules under feature-set package foo should never be using
+ # the top level feature-set package foo directly; only
+ # individual modules under it. (foo._bar, etc). Doing this can
+ # lead to lots of import order problems. We currently make an
+ # exception for anything *not* top-level which covers
+ # TYPE_CHECKING blocks and functions, both of which should be
+ # generally ok.
+ if top_level and modulepath == fsmodulename:
+ # Also we want to allow 'from foo import bar' *IF* bar is a
+ # submodule. Note: currently only looking for single
+ # submodule imports but I don't think we ever import
+ # multiple.
+ if importparts is not None and single_import is not None:
+ fsmodulepath = os.path.join(
+ self.projroot, fspackagesroot, fsmodulename
+ )
+ assert os.path.exists(fsmodulepath)
+ is_submodule_import = os.path.isdir(
+ f'{fsmodulepath}/{single_import}'
+ ) or os.path.isfile(f'{fsmodulepath}/{single_import}.py')
+ else:
+ is_submodule_import = False
+
+ if not is_submodule_import:
+ raise CleanError(
+ f'{fname}:{i+1}: Top level package module {modulepath}'
+ f' should not be imported by its own code'
+ ' (except for within TYPE_CHECKING blocks or functions).'
+ ' Import stuff from submodules directly.'
+ )
+
+
+def _check_python_file_license(
+ self: ProjectUpdater, fname: str, lines: list[str]
+) -> None:
+ copyrightline = _calc_python_file_copyright_line(self, lines)
# In all cases, look for our one-line legal notice.
# In the public case, look for the rest of our public license too.
if self.license_line_checks:
@@ -430,6 +499,43 @@ def _check_python_file(self: ProjectUpdater, fname: str) -> None:
)
+def _calc_python_file_copyright_line(
+ self: ProjectUpdater, lines: list[str]
+) -> int:
+ del self # Unused.
+ copyrightline = 1 if lines[0].startswith('#!/') else 0
+
+ # Special case: it there's spinoff autogenerate notice there,
+ # look below it.
+ if (
+ lines[copyrightline] == ''
+ and 'THIS FILE IS AUTOGENERATED' in lines[copyrightline + 1]
+ ):
+ copyrightline += 2
+
+ if lines[copyrightline].startswith('# Synced from '):
+ copyrightline += 3
+
+ return copyrightline
+
+
+def _check_python_file_shebang(
+ self: ProjectUpdater, fname: str, contents: str
+) -> None:
+ del self # Unused.
+ if contents.startswith('#!'):
+ # Make sure all standalone scripts are pointing to the right version
+ # of Python (with a few exceptions where it needs to differ)
+ if fname not in ['tools/vmshell']:
+ expected = f'#!/usr/bin/env python{PYVER}'
+ if not contents.startswith(expected):
+ raise CleanError(
+ f'Incorrect shebang (first line) for {fname}.\n'
+ f'Expected:\n{expected}\n\n'
+ f'Found:\n{contents.splitlines()[0]}'
+ )
+
+
def _contains_only_pycache_and_cruft(path: str) -> bool:
for _root, dirs, files in os.walk(path, topdown=True):
# Skip over hidden and pycache dirs.
diff --git a/tools/efrotools/code.py b/tools/efrotools/code.py
index c848a9bf..4b6cada4 100644
--- a/tools/efrotools/code.py
+++ b/tools/efrotools/code.py
@@ -6,8 +6,8 @@
from __future__ import annotations
import os
-import subprocess
import sys
+import subprocess
from pathlib import Path
from typing import TYPE_CHECKING
@@ -196,8 +196,17 @@ def get_code_filenames(projroot: Path, include_generated: bool) -> list[str]:
pass
else:
codefilenames.append(path)
- codefilenames.sort()
- return codefilenames
+ out = sorted(codefilenames)
+
+ # Watch for breakage.
+ if not out:
+ print(
+ 'WARNING: get_code_filename returning no results;'
+ ' is something broken?',
+ file=sys.stderr,
+ )
+
+ return out
def black_base_args() -> list[str]:
@@ -291,20 +300,33 @@ def get_script_filenames(projroot: Path) -> list[str]:
"""Return the Python filenames to lint-check or auto-format."""
from efrotools import getconfig
+ proot = f'{projroot}/'
+
filenames = set()
places = getconfig(projroot).get('python_source_dirs', None)
if places is None:
raise RuntimeError('python_source_dirs not declared in config')
for place in places:
- for root, _dirs, files in os.walk(place):
+ for root, _dirs, files in os.walk(os.path.join(projroot, place)):
for fname in files:
fnamefull = os.path.join(root, fname)
# Skip symlinks (we conceivably operate on the original too)
if os.path.islink(fnamefull):
continue
if _should_include_script(fnamefull):
- filenames.add(fnamefull)
- return sorted(list(f for f in filenames if 'flycheck_' not in f))
+ assert fnamefull.startswith(proot)
+ filenames.add(fnamefull.removeprefix(proot))
+ out = sorted(list(f for f in filenames if 'flycheck_' not in f))
+
+ # Watch for breakage.
+ if not out:
+ print(
+ 'WARNING: get_script_filename returning no results;'
+ ' is something broken?',
+ file=sys.stderr,
+ )
+
+ return out
def runpylint(projroot: Path, filenames: list[str]) -> None: