functionality for stripping out code in spinoff when a feature-set is not present

This commit is contained in:
Eric 2023-08-30 15:26:20 -07:00
parent 0652af2bcf
commit eb213d6268
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
14 changed files with 157 additions and 71 deletions

56
.efrocachemap generated
View File

@ -4064,26 +4064,26 @@
"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": "a039bab674a99b559440323b965d2274",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "fb15d3a3e792163f18af727447564192",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "246782dc8e1f2114c62980f8addbc4f4",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "8e59c9779e54f22b66ddfe2cd7c21528",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "0c241652d1669e3bbaf8df19c3ae756c",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "40ba4e0316c063238ab8e8b94f98351c",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "9d80b87c57556a0877f260305f571c78",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "0e7d5147a5b1b9a7ecb8e6fc4cfc1174",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "221c74c7e4cb4d17d8922fdb9bc67246",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "af9ef217e000fb8e2d7fff8770b7bf44",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "cb99167ea13b4b452354f54fb8c9279d",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "3f265457324e3a07a676b4db52a5f111",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "9f1982bc3d674e27202521e2fdfe1e26",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "c6354818c9abd243e9b9af03f1f075f7",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "00b1e678fc49a78ad7dc8758c38d0cb5",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "c91f0c62c989a33caa7b4b4769754f1a",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "9f86ca681c50be1ff449dd28346896c7",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "1728872a08e4db33e5506839ad8fe227",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "99b9add3338ff96d3ffaac7bf490d517",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "6ff18bb40c63970dbf7d6771b240243c",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "d3d9fb45a4c9b2d03c3d8d92b21e391c",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "4046514b27b8de5eec099669270e760a",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "1eb01901da8e4e6a9aae10d93e75f669",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "b2bc222a442085ae9e77c4736c1a47c2",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "98cc6c34ae3baf9f03483ecf718f0860",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "f55ff138c57aeb6d3b5061b6e5289c89",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "47b4af4d42c6ab169bb33d9b1016a321",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "37b2fd64034132fef6a8f202760fc833",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "b4c9e97df51d5cc4f4086c20f75fadea",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "017b34a328d86dc83a975504c3228a23",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "f3f4e7aac95d70d36f1458f793df846a",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "5e0c95b6088822a1a8dbb11d889832e3",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "adb8dfd68d6aea6f450b2ad5ccad3d67",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "951745d14df5d233952a002468b37efa",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "bdd54abe7304f4458867692c28ad1d35",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "6585700cc49deb2c42c4119042648ebd",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "aae37dce0c102274289663de0ac083d5",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "3c30cbf4fb4bde6ce99f0f287052a4a2",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "63bf6d378e6af7f7c02837d201d06a17",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "85aaa5262f488e0a411d8ca8ec6e8987",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "56ebc8c31e020e515395d3a81d2bb766",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "b9aabf060ca52f9957e5c0c68975dd0d",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "56ebc8c31e020e515395d3a81d2bb766",
@ -4100,14 +4100,14 @@
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "5bfe717b5f30a67822130299b7342bcf",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "6c7d4caaad12d39c61b291fe33eef2af",
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "5bfe717b5f30a67822130299b7342bcf",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "4bfc1596cb2eea5a3925de63c28be1e1",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "e4b88f921516e864d9b7cb5d346a3c29",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "b91013c1acf051072c0d9b255ac85d34",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "bd0905746e2a31b608409ad289decb06",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "0ec18bf4b330a9aa06943fcd9fa7ad22",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "52c457717ae225929291bfb5c2d73656",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "34d233aa7492cdd7d3c08b5d23875fb0",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "c597bd1d50530d16a5e920d19f3be226",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "facd9669de199a464287b985d9373c0f",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "deff82055a269fa905dd0cd7622adb64",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "e5ebf7e622ff126bc6b91dd7148fac4f",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "ad8c2bae99b72b48a510cdf1ce849358",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "a5302ab082d0555788b32e7485e07620",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "0376b9a3e88c8c7305b33160f11a3de9",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "1260620b96e0615fd993bfa19d5368c8",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "2ab0357736b91e5be4cd03c153f92c36",
"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",

View File

@ -1,8 +1,10 @@
### 1.7.27 (build 21269, api 8, 2023-08-30)
### 1.7.27 (build 21272, api 8, 2023-08-30)
- Fixed a rare crash that could occur if the app shuts down while a background
thread is making a web request. The app will now try to wait for any such
attempts to complete.
- Fixed a bug where PlayerSpaz used `bs.apptime()` where `bs.time()` should have
been used (thanks EraOSBeta!).
- Added `babase.app.env` which is a type-friendly object containing various
environment/runtime values. Values directly under `app` such as
`babase.app.debug_build` will either be consolidated here or moved to classic
@ -20,11 +22,16 @@
deprecation warnings and will disappear sometime soon. This includes
`build_number`, `device_name`, `config_file_path`, `version`, `debug_build`,
`test_build`, `data_directory`, `python_directory_user`,
`python_directory_app`, `python_directory_app_site`, `api_version`.
`python_directory_app`, `python_directory_app_site`, `api_version`, `on_tv`,
`vr_mode`.
- Reverting the Android keyboard changes from 1.7.26, as I've received a few
reports of bluetooth game controllers now thinking they are keyboards. I'm
thinking I'll have to bite the bullet and implement something that asks the
user what the thing is to solve cases like that.
- Added tags allowing easily stripping code out of spinoff projects when a
specific feature-set is not present. For example, for feature-set 'foo', one
can strip lines out by adding `__SPINOFF_REQUIRE_FOO_BEGIN__` and
`__SPINOFF_REQUIRE_FOO_END__` tags.
### 1.7.26 (build 21259, api 8, 2023-08-29)

View File

@ -58,8 +58,6 @@ class App:
plugins: PluginSubsystem
lang: LanguageSubsystem
env: babase.Env
health_monitor: AppHealthMonitor
# How long we allow shutdown tasks to run before killing them.
@ -154,7 +152,6 @@ class App:
self.env: babase.Env = _babase.Env()
self._subsystems: list[AppSubsystem] = []
self._native_bootstrapping_completed = False
self._init_completed = False
self._meta_scan_completed = False
@ -180,8 +177,8 @@ class App:
self._env = _babase.env()
self.protocol_version: int = self._env['protocol_version']
assert isinstance(self.protocol_version, int)
self.toolbar_test: bool = self._env['toolbar_test']
assert isinstance(self.toolbar_test, bool)
# self.toolbar_test: bool = self._env['toolbar_test']
# assert isinstance(self.toolbar_test, bool)
self.demo_mode: bool = self._env['demo_mode']
assert isinstance(self.demo_mode, bool)
self.arcade_mode: bool = self._env['arcade_mode']
@ -1000,3 +997,17 @@ class App:
stacklevel=2,
)
return self.env.vr
# __SPINOFF_REQUIRE_UI_V1_BEGIN__
@property
def toolbar_test(self) -> bool:
"""(internal)."""
warnings.warn(
'app.toolbar_test is deprecated; use app.ui_v1.use_toolbars',
DeprecationWarning,
stacklevel=2,
)
return self.ui_v1.use_toolbars
# __SPINOFF_REQUIRE_UI_V1_END__

View File

@ -52,7 +52,7 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be
# using.
TARGET_BALLISTICA_BUILD = 21269
TARGET_BALLISTICA_BUILD = 21272
TARGET_BALLISTICA_VERSION = '1.7.27'

View File

@ -62,7 +62,7 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
# sees things in their own optimal way.
vr_mode = bs.app.env.vr
if not bs.app.toolbar_test:
if not bs.app.ui_v1.use_toolbars:
color = (1.0, 1.0, 1.0, 1.0) if vr_mode else (0.5, 0.6, 0.5, 0.6)
# FIXME: Need a node attr for vr-specific-scale.
@ -125,7 +125,7 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
# build number.
force_show_build_number = False
if not bs.app.toolbar_test:
if not bs.app.ui_v1.use_toolbars:
if env.debug or env.test or force_show_build_number:
if env.debug:
text = bs.Lstr(
@ -292,7 +292,10 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
random.seed()
if not (app.demo_mode or app.arcade_mode) and not app.toolbar_test:
if (
not (app.demo_mode or app.arcade_mode)
and not app.ui_v1.use_toolbars
):
self._news = NewsDisplay(self)
# Bring up the last place we were, or start at the main menu otherwise.

View File

@ -55,7 +55,9 @@ class UIV1Subsystem(babase.AppSubsystem):
self.have_party_queue_window = False
self.cleanupchecks: list[UICleanupCheck] = []
self.upkeeptimer: babase.AppTimer | None = None
self.use_toolbars = env.get('toolbar_test', True)
self.use_toolbars = _bauiv1.toolbar_test()
# self.use_toolbars = env.get('toolbar_test', True)
self.title_color = (0.72, 0.7, 0.75)
self.heading_color = (0.72, 0.7, 0.75)
self.infotextcolor = (0.7, 0.9, 0.7)

View File

@ -219,7 +219,7 @@ class MainMenuWindow(bui.Window):
self._have_store_button = not self._in_game
self._have_settings_button = (
not self._in_game or not app.toolbar_test
not self._in_game or not app.ui_v1.use_toolbars
) and not (self._is_demo or self._is_arcade)
self._input_device = input_device = bs.get_ui_input_device()

View File

@ -150,6 +150,7 @@ void PythonClassEnv::SetupType(PyTypeObject* cls) {
envs["vr"] = BoolEntry_(g_core->IsVRMode(),
"Whether the app is currently running in VR.");
bool first = true;
for (auto&& entry : envs) {
if (!first) {

View File

@ -753,7 +753,7 @@ static auto PyEnv(PyObject* self) -> PyObject* {
"ss" // ui_scale
"sO" // on_tv
"sO" // vr_mode
"sO" // toolbar_test
// "sO" // toolbar_test
"sO" // demo_mode
"sO" // arcade_mode
"sO" // iircade_mode
@ -779,7 +779,7 @@ static auto PyEnv(PyObject* self) -> PyObject* {
"ui_scale", ui_scale,
"on_tv", g_core->platform->IsRunningOnTV() ? Py_True : Py_False,
"vr_mode", g_core->IsVRMode() ? Py_True : Py_False,
"toolbar_test", BA_TOOLBAR_TEST ? Py_True : Py_False,
// "toolbar_test", BA_TOOLBAR_TEST ? Py_True : Py_False,
"demo_mode", g_buildconfig.demo_build() ? Py_True : Py_False,
"arcade_mode", g_buildconfig.arcade_build() ? Py_True : Py_False,
"iircade_mode", g_buildconfig.iircade_build() ? Py_True: Py_False,

View File

@ -554,13 +554,12 @@ void SceneV1AppMode::UpdateGameRoster() {
// ..but only if we have a connected client (otherwise our party is
// considered 'empty').
// UPDATE: starting with our big ui revision we'll always include ourself
// here.
bool include_self = (connections()->GetConnectedClientCount() > 0);
#if BA_TOOLBAR_TEST
include_self = true;
#endif // BA_TOOLBAR_TEST
// Previously were gonna enable this with the UI revision.
// #if BA_TOOLBAR_TEST
// include_self = true;
// #endif // BA_TOOLBAR_TEST
if (auto* hs = dynamic_cast<HostSession*>(GetForegroundSession())) {
// Add our host-y self.

View File

@ -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 = 21269;
const int kEngineBuildNumber = 21272;
const char* kEngineVersion = "1.7.27";
const int kEngineApiVersion = 8;

View File

@ -2821,8 +2821,7 @@ static PyMethodDef PyConsolePrintDef = {
// ------------------------ is_party_icon_visible ------------------------------
static auto PyIsPartyIconVisible(PyObject* self, PyObject* args,
PyObject* keywds) -> PyObject* {
static auto PyIsPartyIconVisible(PyObject* self) -> PyObject* {
BA_PYTHON_TRY;
bool party_button_active = (g_base->app_mode()->HasConnectionToClients()
|| g_base->app_mode()->HasConnectionToHost()
@ -2838,13 +2837,35 @@ static auto PyIsPartyIconVisible(PyObject* self, PyObject* args,
static PyMethodDef PyIsPartyIconVisibleDef = {
"is_party_icon_visible", // name
(PyCFunction)PyIsPartyIconVisible, // method
METH_VARARGS | METH_KEYWORDS, // flags
METH_NOARGS, // flags
"is_party_icon_visible() -> bool\n"
"\n"
"(internal)",
};
// ------------------------ is_party_icon_visible ------------------------------
static auto PyToolbarTest(PyObject* self) -> PyObject* {
BA_PYTHON_TRY;
if (BA_TOOLBAR_TEST) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
BA_PYTHON_CATCH;
}
static PyMethodDef PyToolbarTestDef = {
"toolbar_test", // name
(PyCFunction)PyToolbarTest, // method
METH_NOARGS, // flags
"toolbar_test() -> bool\n"
"\n"
"(internal)",
};
// -----------------------------------------------------------------------------
auto PythonMethodsUIV1::GetMethods() -> std::vector<PyMethodDef> {
@ -2880,6 +2901,7 @@ auto PythonMethodsUIV1::GetMethods() -> std::vector<PyMethodDef> {
PyGetSoundDef,
PyGetTextureDef,
PyGetMeshDef,
PyToolbarTestDef,
};
}

View File

@ -231,7 +231,7 @@ void ButtonWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
float l_border, r_border, b_border, t_border;
bool doDraw = true;
bool do_draw = true;
base::MeshAsset* mesh;
@ -255,7 +255,7 @@ void ButtonWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
}
c.SetMaskTexture(mask_texture_.Get());
} else {
doDraw = false;
do_draw = false;
}
l_border = r_border = 0.04f * width_;
b_border = t_border = 0.04f * height_;
@ -351,7 +351,7 @@ void ButtonWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
c.SetTexture(g_base->assets->SysTexture(tex_id));
mesh = g_base->assets->SysMesh(mesh_id);
}
if (doDraw) {
if (do_draw) {
c.PushTransform();
c.Translate((l - l_border + r + r_border) * 0.5f + extra_offs_x,
(b - b_border + t + t_border) * 0.5f + extra_offs_y, 0);

View File

@ -33,13 +33,6 @@ if TYPE_CHECKING:
from batools.project import ProjectUpdater
# Tags that default_filter_file() looks for; these can be used to strip
# out sections that should never be included in spinoff projects.
STRIP_TAG_PAIRS: list[tuple[str, str]] = [
('# __SPINOFF_STRIP_BEGIN__', '# __SPINOFF_STRIP_END__'),
('// __SPINOFF_STRIP_BEGIN__', '// __SPINOFF_STRIP_END__'),
]
class SpinoffContext:
"""Guts of the spinoff system."""
@ -120,6 +113,27 @@ class SpinoffContext:
f.name: f for f in FeatureSet.get_all_for_project(self._src_root)
}
# Generate our list of tags for selectively stripping out code.
# __SPINOFF_STRIP_BEGIN__ / __SPINOFF_STRIP_END__ will *always*
# strip code in spinoff projects and
# __SPINOFF_REQUIRE_FOO_BEGIN__ / __SPINOFF_REQUIRE_FOO_END__ will
# strip code only when feature-set foo is not present in the
# spinoff project.
# begin-tag / end-tag / associated-feature-set-name
self._strip_tags: list[tuple[str, str, str | None]] = [
('__SPINOFF_STRIP_BEGIN__', '__SPINOFF_STRIP_END__', None)
]
for fsetname in sorted(self._src_all_feature_sets.keys()):
fnsu = fsetname.upper()
self._strip_tags.append(
(
f'__SPINOFF_REQUIRE_{fnsu}_BEGIN__',
f'__SPINOFF_REQUIRE_{fnsu}_END__',
fsetname,
)
)
self._src_git_files: set[str] | None = None
self._dst_git_files: set[str] | None = None
self._dst_git_file_dirs: set[str] | None = None
@ -259,7 +273,10 @@ class SpinoffContext:
# Based on feature-sets they requested, calc which feature-sets
# from src we *exclude*.
self._src_omit_feature_sets = self._calc_src_omit_feature_sets()
(
self._src_retain_feature_sets,
self._src_omit_feature_sets,
) = self._calc_src_retain_omit_feature_sets()
# Generate a version of src_omit_paths that includes our feature-set
# omissions. Basically, omitting a feature set simply omits
@ -289,10 +306,10 @@ class SpinoffContext:
self._sanity_test_setup()
self._generate_env_hash()
def _calc_src_omit_feature_sets(self) -> set[str]:
def _calc_src_retain_omit_feature_sets(self) -> tuple[set[str], set[str]]:
# If they want everything, omit nothing.
if self.src_feature_sets is None:
return set()
return set(self._src_all_feature_sets.keys()), set()
# Based on the requested set, calc the total sets we'll need.
# Also always include 'core' since we'd be totally broken
@ -303,7 +320,8 @@ class SpinoffContext:
)
# Now simply return any sets *not* included in our resolved set.
return {s for s in self._src_all_feature_sets.keys() if s not in reqs}
omits = {s for s in self._src_all_feature_sets.keys() if s not in reqs}
return (reqs, omits)
def _add_feature_set_omit_paths(self, paths: set[str]) -> None:
for fsname in sorted(self._src_omit_feature_sets):
@ -725,14 +743,37 @@ class SpinoffContext:
# pylint: disable=too-many-branches
# Strip out any sections frames by our strip-begin/end tags.
if any(t[0] in text for t in STRIP_TAG_PAIRS):
# strip_tag_pairs: list[tuple[str, str]] = []
# print('HELLO WORLD')
def _first_index_containing_string(
items: list[str], substring: str
) -> int | None:
for f_index, f_item in enumerate(items):
if substring in f_item:
return f_index
return None
# Quick-out if no begin-tags are found in the entire text.
if any(t[0] in text for t in self._strip_tags):
lines = text.splitlines()
for begin_tag, end_tag in STRIP_TAG_PAIRS:
while begin_tag in lines:
index = lines.index(begin_tag)
for begin_tag, end_tag, fsetname in self._strip_tags:
# For sections requiring a specific fset, don't touch
# it if we're keeping that set.
if (
fsetname is not None
and fsetname in self._src_retain_feature_sets
):
continue
while (
index := _first_index_containing_string(lines, begin_tag)
) is not None:
# while begin_tag in lines:
# index = lines.index(begin_tag)
endindex = index
while lines[endindex] != end_tag:
while end_tag not in lines[endindex]:
endindex += 1
# If the line after us is blank,