# Cross-platform build guide Nebula Browser uses a **single codebase** and **one git branch** for Windows, macOS, and Linux. You do not maintain separate platform branches. Instead, each machine unpacks the correct [CEF](https://bitbucket.org/chromiumembedded/cef) binary distribution into `thirdparty/cef/` and builds with CMake. ## Overview | Layer | Location | Platform-specific? | |-------|----------|-------------------| | CEF binaries | `thirdparty/cef/` (gitignored) | Yes — one distribution per OS | | CMake / deploy | `CMakeLists.txt` | Yes — uses CEF’s `COPY_FILES`, `COPY_MAC_FRAMEWORK`, etc. | | App entry & CEF init | `src/app/run.cpp`, `src/platform/*/startup_*.cpp` | Partly | | Native window shell | `src/platform/win\|mac\|linux/` | Yes | | Browser embedding | `src/platform/*/browser_host_*.cpp` | Partly | | Tabs, URLs, UI routing | `src/browser/`, `src/ui/`, `src/cef/` | No (shared) | **Windows** has a full native shell (custom frame, DWM, embedded CEF views). **macOS and Linux** compile and link today, but `NebulaWindow::Create()` is still a stub that returns `false` until a real Cocoa / X11 (or GTK) host is implemented. ## Directory layout ``` thirdparty/cef/ # CEF standard distribution for your OS (not in git) src/platform/ types.h # Rect, BrowserLayout, NativeWindow, AppStartup startup.h # PrepareApp, single-instance, CefMainArgs, CefSettings paths browser_host.h # CefWindowInfo helpers, resize/show/destroy browser HWNDs paths_platform.h # ExecutableDirectory, DefaultUserDataRoot, PathToUtf8 win/ # Windows implementation mac/ # macOS stubs + partial CEF glue linux/ # Linux stubs + partial CEF glue src/window/ nebula_window.h # Platform-agnostic window API (PIMPL) src/app/ # Shared controller and run loop src/browser/ # Shared tab/session logic src/cef/ # Shared CEF app and browser client src/ui/ # Shared nebula:// URLs and path helpers ui/ # HTML/JS UI (copied next to the binary at build time) ``` ## Prerequisites - **CMake** 3.21+ - **C++20** compiler - Windows: Visual Studio 2022 - macOS: Xcode 16+ (Clang) - Linux: GCC 10+ or Clang, plus development packages for X11 (CEF’s CMake runs `pkg-config` for X11 on Linux) - **CEF standard binary distribution** for your OS and CPU architecture from the [CEF builds](https://cef-builds.spotifycdn.com/index.html) page (or your usual CEF source) ## CEF setup 1. Download the **standard distribution** for your platform (e.g. `cef_binary_*_windows64`, `*_macos*`, `*_linux64`). 2. Extract so that this path exists: ``` thirdparty/cef/cmake/FindCEF.cmake ``` 3. The rest of the distribution (`Release/`, `Resources/`, `libcef_dll/`, etc.) should sit directly under `thirdparty/cef/` as shipped by CEF. `thirdparty/cef/` is listed in `.gitignore` so binaries are never committed. Each developer and CI machine supplies its own copy. Optional: override the location at configure time: ```bash cmake -B build -DCEF_ROOT=/path/to/cef_binary ``` ## Building ### Windows ```powershell cmake -B build cmake --build build --config Release ``` Output: `build/Release/NebulaBrowser.exe` (plus CEF DLLs and `ui/` copied beside it). ### macOS ```bash cmake -B build -DPROJECT_ARCH=x86_64 # or arm64 on Apple Silicon cmake --build build --config Release ``` CMake builds a `NebulaBrowser.app` bundle and copies the Chromium Embedded Framework into `Contents/Frameworks`. A full macOS port still requires: - A real `NebulaWindow` implementation (Cocoa `NSWindow` / `NSView`) - CEF **helper app** subprocess targets (see CEF’s `cefsimple` sample for the complete Mac bundle layout) Until that work lands, the macOS target may build but will exit early because `Create()` fails. ### Linux ```bash cmake -B build cmake --build build ``` Output: `build/NebulaBrowser` (or `build//NebulaBrowser` depending on generator). CEF `.so` files and resources are copied next to the executable. You may need to set **SUID** on `chrome-sandbox` as printed by CMake post-build (CEF documents this in its Linux README). Until a Linux host window exists, the binary will not show a UI for the same reason as macOS. ## How CMake picks a platform `find_package(CEF)` loads CEF’s `cef_variables.cmake`, which sets: - `OS_WINDOWS` - `OS_MACOSX` - `OS_LINUX` `CMakeLists.txt` then: 1. Adds the matching `src/platform/{win,mac,linux}/*.cpp` sources 2. Sets the executable type (`WIN32`, `MACOSX_BUNDLE`, or plain executable) 3. Links `libcef_dll_wrapper`, `libcef_lib`, and `${CEF_STANDARD_LIBS}` 4. Runs CEF’s copy macros so runtime files land beside the app Shared sources are always compiled; only the `src/platform/...` tree changes per OS. ## Application flow ``` main (app/main.cpp) └─ RunNebula(AppStartup) src/app/run.cpp ├─ platform::PrepareApp() ├─ CefExecuteProcess() (subprocesses) ├─ platform::TryAcquireSingleInstance() ├─ CefInitialize() ├─ NebulaController::Create() │ └─ NebulaWindow::Create() platform-specific └─ CefRunMessageLoop() ``` `AppStartup` carries platform entry data: - **Windows:** `HINSTANCE` + show command (`wWinMain`) - **macOS / Linux:** `argc` / `argv` (`main`) ## User data and profile paths Shared logic in `src/ui/paths.cpp` writes under: | OS | Default root | |----|----------------| | Windows | `%LOCALAPPDATA%\Nebula\User Data` (falls back to exe directory) | | macOS | `~/Library/Application Support/Nebula/User Data` | | Linux | `$XDG_DATA_HOME/Nebula/User Data` or `~/.local/share/Nebula/User Data` | Platform-specific discovery lives in `src/platform/*/paths_*.cpp`. ## GPU / Chromium flags `src/cef/nebula_app.cpp` applies shared switches (`no-sandbox`, `ignore-gpu-blocklist`, etc.) and platform graphics backends: | OS | Backend | |----|---------| | Windows | ANGLE + D3D11 | | macOS | ANGLE + Metal | | Linux | EGL | ## Adding or changing platform code 1. **Shared behavior** (tabs, `nebula://` URLs, CEF clients) → edit under `src/browser/`, `src/ui/`, `src/cef/`, `src/app/` only if it is truly cross-platform. 2. **Native window or OS API** → edit `src/platform//` and keep `src/window/nebula_window.h` free of `#include ` (or Cocoa/X11 headers). 3. **CEF child window embedding** → `src/platform//browser_host_*.cpp` (`MakeChildWindowInfo`, resize, visibility). 4. **Startup / paths** → `startup_*.cpp` and `paths_*.cpp` in the same folder. 5. **New OS** → add `src/platform//`, extend the `if(OS_...)` blocks in `CMakeLists.txt`, and document CEF distribution layout here. ## Porting checklist (macOS / Linux) - [ ] Implement `NebulaWindow` in `nebula_window_mac.cpp` / `nebula_window_linux.cpp` - [ ] Wire `browser_host_*.cpp` resize/show/raise to the real toolkit - [ ] macOS: add CEF helper app targets and bundle layout (copy from `cefsimple`) - [ ] Linux: confirm X11 (or chosen toolkit) parent handle for `CefWindowInfo::SetAsChild` - [ ] Test GPU diagnostics page and hardware acceleration on target hardware - [ ] Package or script runtime layout (`ui/`, CEF frameworks/libs, ICU locales, etc.) ## FAQ **Do I need a separate git branch per OS?** No. Use one branch; swap `thirdparty/cef` and rebuild on each machine. **Can I commit CEF into the repo?** Not recommended (size, licensing, per-arch binaries). Keeping `thirdparty/cef/` gitignored is intentional. **Why does macOS/Linux build but not run?** The window stub intentionally returns `false` from `Create()` until a native shell exists. Shared CEF and browser code are ready; the missing piece is the host window. **Where is the Windows UI code?** `src/platform/win/nebula_window_win.cpp` — custom chrome, hit-testing, fullscreen, and child browser placement.