diff --git a/Documentation/BuildInstructionsLadybird.md b/Documentation/BuildInstructionsLadybird.md index 23025ae1ba..4fdc782688 100644 --- a/Documentation/BuildInstructionsLadybird.md +++ b/Documentation/BuildInstructionsLadybird.md @@ -255,12 +255,14 @@ Ladybird will be built with one of the following browser frontends, depending on * [GTK 4](https://docs.gtk.org/gtk4/) - An alternative UI on Linux (experimental). * [Android UI](https://developer.android.com/develop/ui) - The native UI on Android. -The Qt and GTK UIs are available on Linux. You can pick the UI using the `LADYBIRD_GUI_FRAMEWORK` option: +You can pick the UI using the `LADYBIRD_GUI_FRAMEWORK` option, or the `--gui` argument to ladybird.py. +For example, to force building with the Qt UI: ```bash # From /path/to/ladybird cmake --preset Release -DLADYBIRD_GUI_FRAMEWORK=Qt -cmake --preset Release -DLADYBIRD_GUI_FRAMEWORK=Gtk +# Or +./Meta/ladybird.py run --gui=Qt ``` #### Additional prerequisites for the GTK UI diff --git a/Meta/CMake/gui_framework.cmake b/Meta/CMake/gui_framework.cmake index 6c261b92f9..5890e48407 100644 --- a/Meta/CMake/gui_framework.cmake +++ b/Meta/CMake/gui_framework.cmake @@ -1,3 +1,4 @@ +# Keep the lists here in sync with Meta/host_platform.py if (ANDROID OR VCPKG_TARGET_ANDROID) set(_possible_guis "Android") set(_default_gui "Android") diff --git a/Meta/host_platform.py b/Meta/host_platform.py index 129f9248b6..57703a22d7 100644 --- a/Meta/host_platform.py +++ b/Meta/host_platform.py @@ -22,6 +22,14 @@ class HostSystem(enum.IntEnum): BSD = enum.auto() +class GUIFramework(enum.StrEnum): + # enum.auto() will tolower the values + Qt = "Qt" + AppKit = "AppKit" + Gtk = "Gtk" + Android = "Android" + + class Platform: def __init__(self): self.system = platform.system() @@ -76,3 +84,19 @@ class Platform: libc, _ = platform.libc_ver() return libc + + def valid_gui_frameworks(self) -> list[GUIFramework]: + """ + List of valid GUI frameworks based on target platform. + Keep in sync with Meta/CMake/gui_framework.cmake + """ + if self.host_system == HostSystem.macOS: + return [GUIFramework.Qt, GUIFramework.AppKit] + if self.host_system in (HostSystem.Linux, HostSystem.BSD): + return [GUIFramework.Qt, GUIFramework.Gtk] + return [GUIFramework.Qt] + + def default_gui_framework(self) -> GUIFramework: + if self.host_system == HostSystem.macOS: + return GUIFramework.AppKit + return GUIFramework.Qt diff --git a/Meta/ladybird.py b/Meta/ladybird.py index e95c1683ae..2c689bc73d 100755 --- a/Meta/ladybird.py +++ b/Meta/ladybird.py @@ -19,6 +19,7 @@ sys.path.append(str(Path(__file__).resolve().parent.parent)) from Meta.build_vcpkg import build_vcpkg from Meta.find_compiler import pick_host_compiler +from Meta.host_platform import GUIFramework from Meta.host_platform import HostArchitecture from Meta.host_platform import HostSystem from Meta.host_platform import Platform @@ -43,6 +44,7 @@ def main(): compiler_parser.add_argument("--cc", required=False, default=default_cc) compiler_parser.add_argument("--cxx", required=False, default=default_cxx) compiler_parser.add_argument("--jobs", "-j", required=False) + compiler_parser.add_argument("--gui", required=False, choices=platform.valid_gui_frameworks()) target_parser = argparse.ArgumentParser(add_help=False) target_parser.add_argument("target", nargs=argparse.OPTIONAL) @@ -139,10 +141,10 @@ def main(): args.target = "ladybird" if platform.host_system == HostSystem.Windows else "Ladybird" if args.command == "build": - build_dir = configure_main(platform, args.preset, args.cc, args.cxx) + build_dir = configure_main(platform, args.preset, args.cc, args.cxx, args.gui) build_main(build_dir, args.jobs, args.target, args.args) elif args.command == "test": - build_dir = configure_main(platform, args.preset, args.cc, args.cxx) + build_dir = configure_main(platform, args.preset, args.cc, args.cxx, args.gui) build_main(build_dir, args.jobs) test_main(build_dir, args.preset, args.pattern) elif args.command == "run": @@ -156,19 +158,19 @@ def main(): os.environ["UBSAN_OPTIONS"] = os.environ.get( "UBSAN_OPTIONS", "print_stacktrace=1:print_summary=1:halt_on_error=1" ) - build_dir = configure_main(platform, args.preset, args.cc, args.cxx) + build_dir = configure_main(platform, args.preset, args.cc, args.cxx, args.gui) build_main(build_dir, args.jobs, args.target) run_main(platform.host_system, build_dir, args.target, args.args) elif args.command == "debug": - build_dir = configure_main(platform, args.preset, args.cc, args.cxx) + build_dir = configure_main(platform, args.preset, args.cc, args.cxx, args.gui) build_main(build_dir, args.jobs, args.target, args.args) debug_main(platform.host_system, build_dir, args.target, args.debugger, args.cmd) elif args.command == "profile": - build_dir = configure_main(platform, args.preset, args.cc, args.cxx) + build_dir = configure_main(platform, args.preset, args.cc, args.cxx, args.gui) build_main(build_dir, args.jobs, args.target) profile_main(platform.host_system, build_dir, args.target, args.args) elif args.command == "install": - build_dir = configure_main(platform, args.preset, args.cc, args.cxx) + build_dir = configure_main(platform, args.preset, args.cc, args.cxx, args.gui) build_main(build_dir, args.jobs, args.target, args.args) build_main(build_dir, args.jobs, "install", args.args) elif args.command == "vcpkg": @@ -178,20 +180,24 @@ def main(): clean_main(platform, args.preset) elif args.command == "rebuild": clean_main(platform, args.preset) - build_dir = configure_main(platform, args.preset, args.cc, args.cxx) + build_dir = configure_main(platform, args.preset, args.cc, args.cxx, args.gui) build_main(build_dir, args.jobs, args.target, args.args) elif args.command == "addr2line": - build_dir = configure_main(platform, args.preset, args.cc, args.cxx) + build_dir = configure_main(platform, args.preset, args.cc, args.cxx, args.gui) build_main(build_dir, args.jobs, args.target) addr2line_main(build_dir, args.target, args.program, args.addresses) -def configure_main(platform: Platform, preset: str, cc: str, cxx: str) -> Path: +def configure_main(platform: Platform, preset: str, cc: str, cxx: str, gui: Optional[GUIFramework]) -> Path: ladybird_source_dir, build_preset_dir = configure_build_env(platform, preset) build_vcpkg() if build_preset_dir.joinpath("build.ninja").exists() or build_preset_dir.joinpath("ladybird.sln").exists(): - return build_preset_dir + if not gui or gui == gui_for_build_dir(build_preset_dir): + return build_preset_dir + + if not gui: + gui = platform.default_gui_framework() validate_cmake_version() @@ -207,6 +213,7 @@ def configure_main(platform: Platform, preset: str, cc: str, cxx: str) -> Path: build_preset_dir, f"-DCMAKE_C_COMPILER={cc}", f"-DCMAKE_CXX_COMPILER={cxx}", + f"-DLADYBIRD_GUI_FRAMEWORK={gui}", ] if platform.host_system == HostSystem.Linux and platform.host_architecture == HostArchitecture.AArch64: @@ -219,6 +226,21 @@ def configure_main(platform: Platform, preset: str, cc: str, cxx: str) -> Path: return build_preset_dir +def gui_for_build_dir(build_preset_dir: Path) -> Optional[GUIFramework]: + cmake_cachefile = build_preset_dir.joinpath("CMakeCache.txt") + if not cmake_cachefile.exists(): + return None + + with cmake_cachefile.open("r") as f: + for line in f: + if line.startswith("LADYBIRD_GUI_FRAMEWORK:STRING="): + try: + return GUIFramework(line.strip().split("=", 1)[1]) + except ValueError: + return None + return None + + def configure_skia_jemalloc() -> list[str]: # NOTE: The resource module is only available on Unix, see the "Availability" section at # https://docs.python.org/3/library/resource.html. Given Windows never calls this function, we import locally