LÖVE backend + build target (compat/luajit.lua shims, Roblox build unchanged), examples/example_love, examples/example_shared (same app on Roblox and LÖVE), LuaJIT checksum tests

This commit is contained in:
4DBug
2026-10-08 19:31:22 -05:00
parent 81c5ad9e43
commit 66fa061089
23 changed files with 56780 additions and 17 deletions
+20 -7
View File
@@ -1,6 +1,8 @@
#!/usr/bin/env python3
# Bundles the root *.lua (library + imgui_demo.lua), backends/ and fonts/ into ONE ModuleScript: build/ImGui.luau
# --no-demo: library only (imgui_demo.lua left out, like not compiling imgui_demo.cpp)
# --target love: build/imgui_love.lua for LÖVE/LuaJIT instead (LÖVE backend, compat/luajit.lua shims, @native stripped).
# The default (roblox) build is unaffected by it.
# Why: ModuleScripts don't share globals, and the setfenv() alternative turns off Luau's fast paths / native codegen.
# Each source file becomes a function in __FILES; IM_INCLUDE(name) runs it once and caches the result.
# The module returns a table with every global the library defines (ImGui, ImVec2, ImGuiConfigFlags, ImGui_ImplRoblox, ...).
@@ -9,21 +11,31 @@ import os, re
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
import sys
NO_DEMO = "--no-demo" in sys.argv
LOVE = False
if "--target" in sys.argv:
i = sys.argv.index("--target"); LOVE = sys.argv[i + 1] == "love"; del sys.argv[i:i + 2]
sys.argv = [a for a in sys.argv if a != "--no-demo"]
BACKEND = "imgui_impl_love.lua" if LOVE else "imgui_impl_roblox.lua"
SKIP = {"imgui_impl_roblox.lua", "imgui_impl_roblox_fonts.lua"} if LOVE else {"imgui_impl_love.lua"}
FILES = {}
for d in (ROOT, os.path.join(ROOT, "backends"), os.path.join(ROOT, "fonts")):
for f in sorted(os.listdir(d)):
if f.endswith(".lua") and not (NO_DEMO and f == "imgui_demo.lua"):
FILES[f] = open(os.path.join(d, f)).read()
if f.endswith(".lua") and not (NO_DEMO and f == "imgui_demo.lua") and f not in SKIP:
src = open(os.path.join(d, f)).read()
if LOVE: src = re.sub(r"^@native$", "", src, flags=re.M) # keep line numbers
FILES[f] = src
ENTRY = ["imgui.lua", "imgui_impl_roblox.lua"] + ([] if NO_DEMO else ["imgui_demo.lua"]) # run order
ENTRY = ["imgui.lua", BACKEND] + ([] if NO_DEMO else ["imgui_demo.lua"]) # run order
names = set()
for src in FILES.values():
names.update(re.findall(r"^function ([A-Za-z_]\w*)\s*\(", src, re.M))
names.update(re.findall(r"^([A-Za-z_]\w*)\s*=[^=]", src, re.M))
out = ["--!optimize 2", "-- GENERATED by tools/bundle.py from *.lua, backends/, fonts/ - do not edit", "",
out = ["--!optimize 2", "-- GENERATED by tools/bundle.py from *.lua, backends/, fonts/ - do not edit", ""]
if LOVE:
out = ["-- GENERATED by tools/bundle.py --target love - do not edit", open(os.path.join(ROOT, "compat", "luajit.lua")).read(), ""]
out += [
"local __FILES, __LOADED = {}, {}",
"function IM_INCLUDE(name)",
" local r = __LOADED[name]",
@@ -48,7 +60,8 @@ out.append("}")
import sys
OUT = sys.argv[1] if len(sys.argv) > 1 else os.path.join(ROOT, "build")
os.makedirs(OUT, exist_ok=True)
open(os.path.join(OUT, "ImGui.luau"), "w").write("\n".join(out) + "\n")
NAME = "imgui_love.lua" if LOVE else "ImGui.luau"
open(os.path.join(OUT, NAME), "w").write("\n".join(out) + "\n")
import json
json.dump(linemap, open(os.path.join(OUT, "ImGui.linemap.json"), "w"))
print("wrote build/ImGui.luau (%d files, %d exports)" % (len(FILES), len(names)))
json.dump(linemap, open(os.path.join(OUT, NAME.rsplit(".", 1)[0] + ".linemap.json"), "w"))
print("wrote build/%s (%d files, %d exports)" % (NAME, len(FILES), len(names)))