diff options
| -rw-r--r-- | .clangd | 3 | ||||
| -rw-r--r-- | .zed/debug.json | 56 | ||||
| -rw-r--r-- | .zed/tasks.json | 68 | ||||
| -rw-r--r-- | IDE_INTEGRATION.md | 198 | ||||
| -rw-r--r-- | README.md | 1 | ||||
| -rw-r--r-- | meson.build | 2 |
6 files changed, 327 insertions, 1 deletions
@@ -0,0 +1,3 @@ +# Meson's Ninja backend generates this database during setup. +CompileFlags: + CompilationDatabase: build diff --git a/.zed/debug.json b/.zed/debug.json new file mode 100644 index 0000000..8e54bf5 --- /dev/null +++ b/.zed/debug.json @@ -0,0 +1,56 @@ +[ + { + "label": "YASL: debug hello", + "adapter": "CodeLLDB", + "request": "launch", + "build": "YASL: build for debugger", + "stopOnEntry": true, + "program": "$ZED_WORKTREE_ROOT/build/yasl-hello", + "cwd": "$ZED_WORKTREE_ROOT" + }, + { + "label": "YASL: debug simple window", + "adapter": "CodeLLDB", + "request": "launch", + "build": "YASL: build for debugger", + "stopOnEntry": true, + "program": "$ZED_WORKTREE_ROOT/build/yasl-example", + "cwd": "$ZED_WORKTREE_ROOT" + }, + { + "label": "YASL: debug tablet", + "adapter": "CodeLLDB", + "request": "launch", + "build": "YASL: build for debugger", + "stopOnEntry": true, + "program": "$ZED_WORKTREE_ROOT/build/yasl-tablet", + "cwd": "$ZED_WORKTREE_ROOT" + }, + { + "label": "YASL: debug notepad", + "adapter": "CodeLLDB", + "request": "launch", + "build": "YASL: build for debugger", + "stopOnEntry": true, + "program": "$ZED_WORKTREE_ROOT/build/yasl-notepad", + "cwd": "$ZED_WORKTREE_ROOT" + }, + { + "label": "YASL: debug game", + "adapter": "CodeLLDB", + "request": "launch", + "build": "YASL: build for debugger", + "stopOnEntry": true, + "program": "$ZED_WORKTREE_ROOT/build/yasl-game", + "cwd": "$ZED_WORKTREE_ROOT" + }, + { + "label": "YASL: debug lifecycle test (no display required)", + "adapter": "CodeLLDB", + "request": "launch", + "build": "YASL: build for debugger", + "stopOnEntry": true, + "program": "$ZED_WORKTREE_ROOT/build/tests/yasl-test", + "cwd": "$ZED_WORKTREE_ROOT" + } +] diff --git a/.zed/tasks.json b/.zed/tasks.json new file mode 100644 index 0000000..6df4810 --- /dev/null +++ b/.zed/tasks.json @@ -0,0 +1,68 @@ +[ + { + "label": "YASL: configure debug build", + "command": "meson", + "args": ["setup", "build", "--backend=ninja", "--buildtype=debug"], + "cwd": "$ZED_WORKTREE_ROOT" + }, + { + "label": "YASL: reconfigure", + "command": "meson", + "args": ["setup", "--reconfigure", "build"], + "cwd": "$ZED_WORKTREE_ROOT" + }, + { + "label": "YASL: build", + "command": "meson", + "args": ["compile", "-C", "build"], + "cwd": "$ZED_WORKTREE_ROOT" + }, + { + "label": "YASL: build for debugger", + "command": "meson setup --reconfigure build --backend=ninja -Dbuildtype=debug -Ddebug=true -Doptimization=0 -Db_lto=false && meson compile -C build", + "cwd": "$ZED_WORKTREE_ROOT", + "shell": { "program": "sh" } + }, + { + "label": "YASL: test", + "command": "meson", + "args": ["test", "-C", "build", "--print-errorlogs"], + "cwd": "$ZED_WORKTREE_ROOT" + }, + { + "label": "YASL: benchmark", + "command": "meson", + "args": ["test", "-C", "build", "--benchmark", "--verbose"], + "cwd": "$ZED_WORKTREE_ROOT" + }, + { + "label": "YASL: run hello", + "command": "meson compile -C build yasl-hello && exec ./build/yasl-hello", + "cwd": "$ZED_WORKTREE_ROOT", + "shell": { "program": "sh" } + }, + { + "label": "YASL: run simple window", + "command": "meson compile -C build yasl-example && exec ./build/yasl-example", + "cwd": "$ZED_WORKTREE_ROOT", + "shell": { "program": "sh" } + }, + { + "label": "YASL: run tablet", + "command": "meson compile -C build yasl-tablet && exec ./build/yasl-tablet", + "cwd": "$ZED_WORKTREE_ROOT", + "shell": { "program": "sh" } + }, + { + "label": "YASL: run notepad", + "command": "meson compile -C build yasl-notepad && exec ./build/yasl-notepad", + "cwd": "$ZED_WORKTREE_ROOT", + "shell": { "program": "sh" } + }, + { + "label": "YASL: run game", + "command": "meson compile -C build yasl-game && exec ./build/yasl-game", + "cwd": "$ZED_WORKTREE_ROOT", + "shell": { "program": "sh" } + } +] diff --git a/IDE_INTEGRATION.md b/IDE_INTEGRATION.md new file mode 100644 index 0000000..67ba96e --- /dev/null +++ b/IDE_INTEGRATION.md @@ -0,0 +1,198 @@ +# IDE integration + +YASL includes Zed debugger profiles for stepping from example applications into +the library's display and window creation code. Open the repository root (the +directory containing `meson.build`) as your Zed project. The shared configuration +uses `build/` for the build directory on Linux and macOS. + +## Prerequisites + +Install Zed, a C23 compiler, Meson 1.6 or newer, Ninja, pkg-config, and Python 3. +A C++ compiler enables the optional public-header compatibility test. Install +the backend development dependencies described in +[Getting started](docs/getting-started.md#build-and-run): at least one of Wayland +or X11 on Linux, or the macOS 14+ SDK on macOS. Running windows on macOS also +requires a Metal-capable device. + +[Zed's C support](https://zed.dev/docs/languages/c) includes clangd for code +completion, diagnostics, and navigation, and CodeLLDB for debugging. Allow Zed +to obtain these tools when prompted, or configure your existing installations +in your personal Zed settings. Use a recent clangd with C23 support. + +Make sure `meson`, `ninja`, your compiler, `pkg-config`, and `python3` are on the +PATH visible to Zed's terminal. You can open the project from an initialized +terminal with `zed .` when Zed's CLI is installed. On macOS, if your toolchain +defaults to an older deployment target, launch from a terminal with +`MACOSX_DEPLOYMENT_TARGET=14.0` (or newer) exported before configuring. + +## Start debugging window creation + +1. Save your changes and open `examples/hello.c`. +2. Set gutter breakpoints on the calls to `yasl_display_open()` and + `yasl_surface_create_toplevel()` in `main()`. +3. Open the command palette (`Ctrl+Shift+P` on Linux, `Cmd+Shift+P` on macOS), + run `debugger: start`, and choose **YASL: debug hello**. +4. The debugger prepares the build and pauses on entry. Continue to the display + creation breakpoint, then use **Step Into** to enter YASL's implementation in + `src/common/backend.c`. Continue to the surface creation breakpoint and step + into `yasl_surface_create_toplevel()` and `yasl_create_toplevel()`. +5. Step into `d->ops->surface_create_toplevel(...)` to reach native window + creation. Inspect `spec`, `d`, local variables, and the call stack in Zed's + debug panel. Use **Step Over** for calls you want to execute without entering. + +Every debug profile runs **YASL: build for debugger** before launching. This +task creates or reconfigures `build/` with debug symbols, optimization disabled, +and LTO disabled, then builds the library and examples. It explicitly sets +`buildtype=debug`, `debug=true`, `optimization=0`, and `b_lto=false`, including +when `build/` was previously a release or customized build. Other Meson options, +including backend selection, are preserved. Both the example and YASL have +source-level debug information; the shared-library examples load the library +from the Meson build tree. + +Profiles pause on entry through CodeLLDB's `stopOnEntry` setting, so initialization +does not run before you can inspect the session. To launch directly to your +breakpoints instead, set `"stopOnEntry": false` in the desired profile. See +[CodeLLDB's launch settings](https://github.com/vadimcn/codelldb/blob/master/MANUAL.md#launching-a-new-process). + +Native creation follows the backend selected at runtime: + +| Backend | Window creation implementation | +| --- | --- | +| Wayland | `wayland_surface_create_toplevel()` in `src/platform/wayland/surface.c` | +| X11 | `create_toplevel()` in `src/platform/x11/window.c` | +| Cocoa | `cocoa_surface_create_toplevel()` in `src/platform/cocoa/window.m` | + +You can also put a source breakpoint directly in these functions before +launching. A breakpoint in a backend that is disabled or not selected will not +be hit. In the notepad and game examples, `yasl_run()` opens the display before +calling the example's initialization callback; set a breakpoint in `yasl_run()` +in `src/common/app.c` to follow that path. + +## Configure and build without launching + +1. Open the command palette (`Ctrl+Shift+P` on Linux, `Cmd+Shift+P` on macOS). +2. Run `task: spawn`, then select **YASL: configure debug build**. +3. Run **YASL: build** from the same task picker. +4. Open `examples/hello.c` or a file in `src/` to use completion and navigation. + +The configure task runs: + +```sh +meson setup build --backend=ninja --buildtype=debug +``` + +Meson's Ninja backend writes `build/compile_commands.json`. The root +[.clangd](.clangd) points clangd at that directory, so it uses Meson's actual +include paths, C standard, and enabled-backend definitions. Keep the generated +database in the ignored build directory; no root-level copy or symlink is +needed. The same configuration works with other clangd-based editors. See +[clangd's compilation database setting](https://clangd.llvm.org/config#compilationdatabase). + +If `build/` is already configured, the configure task leaves its existing +options in place. To change an existing build to debug, run this in Zed's +terminal: + +```sh +meson configure build -Dbuildtype=debug +``` + +Save edited files before building, running, or debugging. Use **YASL: +reconfigure** to rerun Meson configuration and refresh its metadata. Meson also +regenerates the build automatically when build definitions change. + +## Tasks and examples + +The project tasks live in [.zed/tasks.json](.zed/tasks.json). Select them with +`task: spawn`; `task: rerun` repeats the last task. See +[Zed's task documentation](https://zed.dev/docs/tasks) for personal keybindings. + +| Task | Behavior | +| --- | --- | +| YASL: configure debug build | Set up `build/` with Ninja and debug symbols. | +| YASL: reconfigure | Reconfigure an existing `build/`, preserving its options. | +| YASL: build | Compile the library, examples, and enabled tests. | +| YASL: build for debugger | Configure symbols and disable optimization/LTO, then build. | +| YASL: test | Build as needed and run enabled tests, printing failure logs. | +| YASL: benchmark | Build as needed and run Meson's registered benchmarks. | +| YASL: run hello | Build and launch `yasl-hello`. | +| YASL: run simple window | Build and launch `yasl-example`. | +| YASL: run tablet | Build and launch `yasl-tablet`. | +| YASL: run notepad | Build and launch `yasl-notepad`. | +| YASL: run game | Build and launch `yasl-game`. | + +Each run task builds its executable before launching and stops if compilation +fails. Run tasks explicitly use `sh` for their compound command. All tasks use +the repository root as their working directory, including when a nested source +file is active. + +Examples need a desktop connection. On Linux, run Zed in your desktop session +so the launched program inherits `WAYLAND_DISPLAY`/`DISPLAY` and +`XDG_RUNTIME_DIR` as appropriate. Most tests can run without a display, but the +currently registered `app-runner` test opens a window even in a default build. +`app-input-destroy` also needs a Wayland compositor when `WAYLAND_DISPLAY` is +set. Run the full suite in a desktop session with the corresponding backend +enabled; additional tests retain their configured requirements. + +## Debugging + +The profiles in [.zed/debug.json](.zed/debug.json) use CodeLLDB. Run +`debugger: start` from the command palette and select an example. Profiles cover +hello, simple window, tablet, notepad, game, and the lifecycle test +(`build/tests/yasl-test`), which needs no display. Each profile builds through +**YASL: build for debugger** and pauses on entry. +See [Zed's debugger documentation](https://zed.dev/docs/debugger). + +To debug another test, copy the lifecycle profile and change its `label` and +`program` to the executable under `build/tests/`. To pass example arguments, +add an `args` array to a debug profile; for example, the tablet profile accepts +`"args": ["--backend", "x11"]` when that backend is enabled. + +## Backend options and additional builds + +Linux backend selection defaults to automatic dependency detection. To require +X11 and disable Wayland in the IDE build, run: + +```sh +meson configure build -Dx11=enabled -Dwayland=disabled +meson compile -C build +``` + +Restore automatic selection with `-Dx11=auto -Dwayland=auto`. Integration tests +are opt-in through `-Dintegration_tests=true`; their server dependencies and +desktop requirements are documented in [Testing](docs/testing.md). + +For sanitizers or a different compiler, prefer a separate build directory: + +```sh +meson setup build-sanitize -Db_sanitize=address,undefined +meson test -C build-sanitize --print-errorlogs +``` + +The checked-in Zed tasks and profiles continue to target `build/`. To use a +different directory throughout the IDE, update the tasks' build arguments and +run commands, debugger program paths, and `.clangd`'s `CompilationDatabase` +together. Ignore additional build directories locally (for example through +`.git/info/exclude`) and keep machine-specific tool paths out of shared files. + +## Troubleshooting + +- **Missing headers or incorrect backend diagnostics:** confirm that + `build/compile_commands.json` exists, build successfully, then run + `editor: restart language server`. clangd analyzes the backend configuration + in that database; disabled native backends may lack their SDK headers. +- **C23 syntax errors in the editor:** update clangd; the compiler used by Meson + and the clangd used by Zed are separate tools. +- **A task cannot find Meson or a compiler:** check `command -v meson ninja cc` + in Zed's terminal and fix the PATH used by Zed and its task shell. +- **No window opens:** check the desktop environment and enabled backends. + Use the lifecycle debug profile to check debugging without a display. +- **`app-runner` or `app-input-destroy` fails at startup:** check that the test + can connect to the inherited desktop session. For a headless check of the + lifecycle test alone, run `meson test -C build basic --print-errorlogs`. +- **Breakpoints do not bind:** use a **YASL: debug ...** profile so the debugger + build task runs, and confirm the executable and library come from `build/`. + Shared-library breakpoints may remain pending until the library loads. Check + that a native backend breakpoint belongs to the enabled, selected backend. +- **Configuration fails on macOS:** check the SDK and deployment target against + [Getting started](docs/getting-started.md#build-and-run). Compiler or SDK + changes may require a fresh build directory. @@ -26,6 +26,7 @@ for platform capabilities and limits. - [API policy](docs/api-policy.md), [migration notes](docs/migrating.md), and [contributing](CONTRIBUTING.md). - [Tests, tablet example, and benchmarks](docs/testing.md). +- [IDE integration](IDE_INTEGRATION.md): step through example and window creation code with Zed's debugger. ## Build a window diff --git a/meson.build b/meson.build index 201de74..b0ea8e8 100644 --- a/meson.build +++ b/meson.build @@ -160,7 +160,7 @@ game_example = executable( subdir('tests') doc_dir = get_option('datadir') / 'doc' / 'yasl' -install_data('README.md', 'CONTRIBUTING.md', 'Doxyfile', 'LICENSE', install_dir: doc_dir) +install_data('README.md', 'CONTRIBUTING.md', 'IDE_INTEGRATION.md', 'Doxyfile', 'LICENSE', install_dir: doc_dir) # Preserve source-relative links and keep the reference buildable offline. install_subdir('docs', install_dir: doc_dir) install_subdir('examples', install_dir: doc_dir) |
