diff --git a/.agents/docs/2026-10-25-unified-mwrap-checkpoint.md b/.agents/docs/2026-10-25-unified-mwrap-checkpoint.md new file mode 100644 index 0000000..3b7c104 --- /dev/null +++ b/.agents/docs/2026-10-25-unified-mwrap-checkpoint.md @@ -0,0 +1,8 @@ +# Unified mwrap first release + +Package/module: `mcpplibs.mwrap.imgui`; upstream version: `1.92.8`. + +This checkpoint migrates wrapper identity while retaining upstream API names and SDK sources. +Default README is English with a mandatory Chinese counterpart; original section order is preserved. +Validation and release evidence are recorded in the PR and the index ecosystem ledger. +No old release tag or source resource is rewritten. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f47b13..b368bfe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Mwrap identity and bilingual README contract + run: python3 tools/check_mwrap_release.py + - name: Install Xlings (Linux/macOS) if: runner.os != 'Windows' env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..f0c6447 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,49 @@ +name: Release +on: + push: + tags: ["mwrap-*"] +permissions: + contents: read +jobs: + publish: + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: write + actions: read + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Check tag, upstream version and bilingual entry pages + run: python3 tools/check_mwrap_release.py --tag "$GITHUB_REF_NAME" + - name: Require successful source CI at this exact commit + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + result=$(gh run list --repo "$GITHUB_REPOSITORY" --workflow ci.yml --commit "$GITHUB_SHA" --limit 1 --json status,conclusion --jq '.[0] | .status == "completed" and .conclusion == "success"') + test "$result" = true || { echo 'No successful source CI at the tag commit'; exit 1; } + - name: Build immutable source asset and publish + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + version="${GITHUB_REF_NAME#mwrap-}" + repo="${GITHUB_REPOSITORY#*/}" + asset="${repo}-${version}.tar.gz" + git archive --format=tar.gz --prefix="${repo}-${version}/" "$GITHUB_SHA" > "$RUNNER_TEMP/$asset" + (cd "$RUNNER_TEMP" && sha256sum "$asset" > "$asset.sha256") + if gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then + echo 'Release already exists; refusing to change immutable assets'; exit 1 + fi + extra=() + if [[ "$version" == *-rc* ]]; then extra+=(--prerelease); fi + gh release create "$GITHUB_REF_NAME" "$RUNNER_TEMP/$asset" "$RUNNER_TEMP/$asset.sha256" \ + --repo "$GITHUB_REPOSITORY" --verify-tag --title "$repo $version" \ + --notes "Module wrapper for upstream $version. New mwrap package identities become installable after mcpp-index publication; see the bilingual README for the tested support scope." "${extra[@]}" diff --git a/.github/workflows/templates.yml b/.github/workflows/templates.yml new file mode 100644 index 0000000..1f55d68 --- /dev/null +++ b/.github/workflows/templates.yml @@ -0,0 +1,54 @@ +name: Actual project templates +on: + push: + branches: [main] + pull_request: + workflow_dispatch: +permissions: + contents: read +concurrency: + group: templates-${{ github.ref }} + cancel-in-progress: true +env: + MCPP_VERSION: "2026.10.24.1" +jobs: + templates: + runs-on: ubuntu-latest + timeout-minutes: 120 + steps: + - uses: actions/checkout@v4 + - uses: actions/cache@v4 + with: + path: ~/.mcpp/registry + key: template-registry-${{ github.repository }}-${{ env.MCPP_VERSION }}-${{ hashFiles('mcpp.toml') }} + restore-keys: | + template-registry-${{ github.repository }}-${{ env.MCPP_VERSION }}- + - name: Install current mcpp + shell: bash + run: | + archive="mcpp-${MCPP_VERSION}-linux-x86_64.tar.gz" + curl -L -fsS -o "$archive" "https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_VERSION}/$archive" + tar -xzf "$archive" + root="$PWD/mcpp-${MCPP_VERSION}-linux-x86_64" + mkdir -p "$HOME/.mcpp/registry" + cp -a "$root/registry/." "$HOME/.mcpp/registry/" + echo "$root/bin" >> "$GITHUB_PATH" + - name: Install toolchains + run: | + mcpp toolchain install gcc 16.1.0 + mcpp toolchain install llvm 23.1.3 + - uses: actions/checkout@v4 + with: + repository: mcpplibs/mcpp-index + ref: f0f8e48c396dad2ac67315f2ac54a197772e6555 + path: .candidate-index + - name: Actual default scaffold without a display + run: env -u DISPLAY -u WAYLAND_DISPLAY python3 tools/test_template.py --toolchain llvm@23.1.3 --index .candidate-index + - name: Virtual display + run: sudo apt-get update && sudo apt-get install -y xvfb + - name: Actual window and docking scaffolds render three frames + env: + LIBGL_ALWAYS_SOFTWARE: "1" + run: | + xvfb-run -a python3 tools/test_template.py --template window --toolchain llvm@23.1.3 --run-arg=--smoke-test --index .candidate-index + xvfb-run -a python3 tools/test_template.py --template docking --toolchain llvm@23.1.3 --run-arg=--smoke-test --index .candidate-index diff --git a/.xlings.json b/.xlings.json index 1388672..4dcdd6b 100644 --- a/.xlings.json +++ b/.xlings.json @@ -1,5 +1,5 @@ { "workspace": { - "xim:mcpp": "2026.8.5.4" + "xim:mcpp": "2026.10.24.1" } } diff --git a/README.md b/README.md index 9f2f57d..845aaad 100644 --- a/README.md +++ b/README.md @@ -1,101 +1,64 @@ # imgui -> Dear ImGui 的 C++23 模块化封装 — `import` 即用 · 跨平台 · 特性可插拔 +> Dear ImGui through C++23 modules, with optional app and docking interfaces. + +**English** · [简体中文](README.zh.md) · [Upstream](https://github.com/ocornut/imgui) · [Releases](https://github.com/mcpplibs/imgui-m/releases) · [mcpp](https://github.com/mcpp-community/mcpp) · [Index](https://github.com/mcpplibs/mcpp-index) -[![Release](https://img.shields.io/github/v/release/mcpplibs/imgui-m)](https://github.com/mcpplibs/imgui-m/releases) [![C++23](https://img.shields.io/badge/C%2B%2B-23-blue.svg)](https://en.cppreference.com/w/cpp/23) -[![Module](https://img.shields.io/badge/module-ok-green.svg)](https://en.cppreference.com/w/cpp/language/modules) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) +[![CI](https://github.com/mcpplibs/imgui-m/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/mcpplibs/imgui-m/actions/workflows/ci.yml) -| [mcpp 构建工具](https://github.com/mcpp-community/mcpp) · [包索引 mcpp-index](https://github.com/mcpp-community/mcpp-index) · [Dear ImGui 上游](https://github.com/ocornut/imgui) · [Issues](https://github.com/mcpplibs/imgui-m/issues) · [Releases](https://github.com/mcpplibs/imgui-m/releases) | -|:---:| -| [![CI](https://github.com/mcpplibs/imgui-m/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/mcpplibs/imgui-m/actions/workflows/ci.yml) | +Keep `ImGui::` APIs. Core, backend contracts and the optional app facade remain separate. -## 核心特性 +## Modules -- **纯模块导入** — `import imgui.core;`,消费者代码零 `#include` -- **三档使用体验** — `imgui.app` 一行出窗口(默认档)/ `imgui.backend` 编译期契约(自动档)/ `imgui.backend.` 显式选后端(专家档),换后端 = 改一行 import + alias -- **特性可插拔** — `features = ["docking-full"]` 解锁面板停靠 + 拖出主窗口变独立 OS 窗口;`app` 档 - 开启 `imgui.app` facade(它是本仓库的发明,不属于上游 Dear ImGui,所以默认不给) -- **不绑定工具链、不打包 GL 驱动** — Linux / macOS / Windows 三平台 CI;GL 运行时由 mcpp 生态自动闭合 +- `import mcpplibs.mwrap.imgui;`: Core API and macro mappings +- `import mcpplibs.mwrap.imgui.core;`: Dear ImGui contexts, frames and widgets +- `import mcpplibs.mwrap.imgui.app;`: Optional app frame loop; requires app +- `import mcpplibs.mwrap.imgui.backend;`: Backend contracts and shared types +- `import mcpplibs.mwrap.imgui.backend.headless;`: Display-independent backend +- `import mcpplibs.mwrap.imgui.backend.glfw_opengl3;`: GLFW/OpenGL3 window backend -## 快速开始 +> More Modules API -> [Usage and API](docs/usage.md) -```bash -mcpp new myapp --template imgui && cd myapp && mcpp run # 窗口直接出现 -# 模板随库分发、版本自动对齐: -# mcpp new --list-templates imgui # 列出库提供的模板 -# mcpp new ide --template imgui:docking # IDE 式停靠布局(docking-full) -``` +## Getting started -或在已有项目中手动接入: +```sh +mcpp new ui --template mcpplibs.mwrap.imgui@1.92.8 +cd ui +mcpp run +``` ```toml -[dependencies.ocornut] -imgui = { version = "1.92.8", features = ["app"] } +[dependencies] +mcpplibs.mwrap.imgui = "1.92.8" ``` ```cpp -import imgui.core; -import imgui.app; - -int main() { - return ImGui::App::run([] { - ImGui::Begin("hello"); - ImGui::TextUnformatted("mcpp + imgui"); - ImGui::End(); - }); -} +import mcpplibs.mwrap.imgui; +int main() { ImGui::CreateContext(); ImGui::DestroyContext(); } ``` -## 模块一览 - -| 模块 | 说明 | -|---|---| -| `imgui.core` | Dear ImGui 核心:context / frame / widgets / draw-data | -| `imgui.app` | Tier-0 facade:平台默认后端 + 一行帧循环 | -| `imgui.backend` | 后端契约:`BackendApi` concept + 共享类型(`GlConfig`/`Error`/`FbSize`) | -| `imgui.backend.glfw_opengl3` | GLFW + OpenGL3 窗口后端(单一 `Backend` 类型) | -| `imgui.backend.headless` | 无显示后端:同一业务循环跑 CI / 逻辑测试 | - -后端实现模块**不 re-export** `imgui.core`,消费者需各自显式 `import`;换后端只改 import 行与一行 `using Backend = ...;`。 - ## Features -| Feature | 说明 | +| Feature | Purpose | |---|---| -| `docking` | 主窗口内面板停靠(Wayland 安全) | -| `viewports` | 面板拖出主窗口分离为独立 OS 窗口(X11 / Windows / macOS) | -| `docking-full` | 组合糖 = `["docking", "viewports"]`,完整 docking 体验 | +| `app` | App frame loop | +| `docking` | Dockable panels | +| `viewports` | Separate OS windows | +| `docking-full` | docking + viewports | -```toml -[dependencies.ocornut] -imgui = { version = "1.92.8", features = ["docking-full", "app"] } -``` - -docking/viewports 源码来自上游 docking tag(`compat.imgui 1.92.8-docking`,主线超集——特性关闭时行为与主线一致)。需 mcpp ≥ 0.0.47。 +The provider is upstream `1.92.8-docking`; desktop templates need a display. -## 示例 - -| 示例 | 内容 | -|---|---| -| [`examples/app_minimal`](examples/app_minimal) | facade 三行出窗口 | -| [`examples/docking`](examples/docking) | IDE 式四分屏 + 面板分离为独立 OS 窗口 | -| [`examples/minimal_window`](examples/minimal_window) | 显式后端 API 的最小窗口(带诊断) | -| [`examples/glfw_opengl3`](examples/glfw_opengl3) | 显式后端的完整事件循环 | -| [`examples/basic`](examples/basic) | headless 核心 API(CI 可跑) | - -```bash -cd examples/docking && mcpp run -``` +## Examples -## 工具链与运行时 +Default: display-independent context/frame checks. `:window` and `:docking` require a desktop. +[Examples](examples) · [Usage](docs/usage.md) · [Architecture](docs/architecture.md) -包不固定工具链(mcpp 解析环境默认,如 Linux glibc gcc);GL/GLX 运行时由生态包 `compat.glx-runtime` 透传闭合,不打包驱动。解析结果可用 `mcpp why` / `mcpp self doctor` 查看。 +## Build and runtime -> [!NOTE] -> 早期版本,接口可能调整。问题与想法欢迎提 [issue](https://github.com/mcpplibs/imgui-m/issues)。 +Linux, macOS and Windows CI. OpenGL/GLX runtime adapters do not redistribute the host GPU driver. ## License -封装代码 MIT;Dear ImGui 本体 MIT(经 `compat.imgui` 提供)。 +[MIT](LICENSE); the upstream provider has its own MIT notice. diff --git a/README.zh.md b/README.zh.md new file mode 100644 index 0000000..f941b21 --- /dev/null +++ b/README.zh.md @@ -0,0 +1,64 @@ +# imgui + +> Dear ImGui 的 C++23 模块封装,提供可选 app 与 docking 接口。 + +[English](README.md) · **简体中文** · [Upstream](https://github.com/ocornut/imgui) · [Releases](https://github.com/mcpplibs/imgui-m/releases) · [mcpp](https://github.com/mcpp-community/mcpp) · [Index](https://github.com/mcpplibs/mcpp-index) + +[![C++23](https://img.shields.io/badge/C%2B%2B-23-blue.svg)](https://en.cppreference.com/w/cpp/23) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) +[![CI](https://github.com/mcpplibs/imgui-m/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/mcpplibs/imgui-m/actions/workflows/ci.yml) + +保留 `ImGui::` API,core、后端契约与可选 app facade 分层。 + +## 模块 + +- `import mcpplibs.mwrap.imgui;`: 核心 API 与宏映射 +- `import mcpplibs.mwrap.imgui.core;`: Dear ImGui 上下文、帧与控件 +- `import mcpplibs.mwrap.imgui.app;`: 可选应用帧循环,需 app feature +- `import mcpplibs.mwrap.imgui.backend;`: 后端契约与共享类型 +- `import mcpplibs.mwrap.imgui.backend.headless;`: 无显示依赖后端 +- `import mcpplibs.mwrap.imgui.backend.glfw_opengl3;`: GLFW/OpenGL3 窗口后端 + +> More Modules API -> [模块与 API](docs/usage.md) + +## 快速开始 + +```sh +mcpp new ui --template mcpplibs.mwrap.imgui@1.92.8 +cd ui +mcpp run +``` + +```toml +[dependencies] +mcpplibs.mwrap.imgui = "1.92.8" +``` + +```cpp +import mcpplibs.mwrap.imgui; +int main() { ImGui::CreateContext(); ImGui::DestroyContext(); } +``` + +## 特性 + +| Feature | 用途 | +|---|---| +| `app` | 应用帧循环 | +| `docking` | 面板停靠 | +| `viewports` | 独立系统窗口 | +| `docking-full` | 组合 docking 与 viewports | + +供应者为上游 `1.92.8-docking`;桌面模板需要显示环境。 + +## 示例 + +默认模板验证无显示依赖的 context/frame;`:window` 与 `:docking` 需要桌面。 +[示例](examples) · [使用](docs/usage.md) · [架构](docs/architecture.md) + +## 工具链与运行时 + +Linux、macOS、Windows CI 覆盖;OpenGL/GLX 运行时适配不分发宿主 GPU 驱动。 + +## 许可证 + +[MIT](LICENSE);上游供应者保留其 MIT 通知。 diff --git a/docs/architecture.md b/docs/architecture.md index 202537a..8b1c221 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -6,16 +6,16 @@ upstream source ownership in compat packages. ## Goals - Offer a three-tier consumer UX: - - Tier-0 `imgui.app` — convenience facade owning the full lifecycle. - - Tier-1 automatic via the `imgui.backend` contract (`BackendApi` concept). + - Tier-0 `mcpplibs.mwrap.imgui.app` — convenience facade owning the full lifecycle. + - Tier-1 automatic via the `mcpplibs.mwrap.imgui.backend` contract (`BackendApi` concept). - Tier-2 explicit `imgui.backend.` module + alias. - Let consumers use explicit module imports: - - `import imgui.app;` (Tier-0 facade; `export import`s `imgui.core`) - - `import imgui.core;` - - `import imgui.backend;` (generic abstraction layer: shared types + contract) - - `import imgui.backend.platform.glfw;` - - `import imgui.backend.renderer.opengl3;` - - `import imgui.backend.glfw_opengl3;` + - `import mcpplibs.mwrap.imgui.app;` (Tier-0 facade; `export import`s `mcpplibs.mwrap.imgui.core`) + - `import mcpplibs.mwrap.imgui.core;` + - `import mcpplibs.mwrap.imgui.backend;` (generic abstraction layer: shared types + contract) + - `import mcpplibs.mwrap.imgui.backend.platform.glfw;` + - `import mcpplibs.mwrap.imgui.backend.renderer.opengl3;` + - `import mcpplibs.mwrap.imgui.backend.glfw_opengl3;` - Provide a uniform, compile-time-constrained backend surface so swapping a backend is just a different import plus a one-line alias, with cross-platform GL/GLSL defaults handled by the abstraction layer. @@ -52,7 +52,7 @@ upstream source ownership in compat packages. ## Package Configuration -The root `mcpp.toml` declares package `imgui`, lists the module wrappers and +The root `mcpp.toml` declares package `mcpplibs.mwrap.imgui`, lists the module wrappers and backend implementation translation units, and depends on compat packages: ```toml @@ -83,17 +83,17 @@ a transitive dependency of `compat.glfw` on Linux), not bundled by this package. ## Module Wrappers -`src/core.cppm` adapts upstream `imgui.h` internally, then exports selected +`src/core.cppm` adapts upstream `mcpplibs.mwrap.imgui.h` internally, then exports selected types and functions with explicit `using` declarations. This keeps consumer code -on `import imgui.core;` instead of requiring direct header usage. +on `import mcpplibs.mwrap.imgui.core;` instead of requiring direct header usage. Backend module interfaces similarly adapt the official backend headers internally and export explicit wrapper functions. The implementation `.cpp` files compile upstream backend sources from `compat.imgui/backends` so consumers do not need to copy backend sources into their own package. -`src/app.cppm` (`imgui.app`) is the Tier-0 facade. It `export import`s -`imgui.core` and privately imports `imgui.backend.glfw_opengl3`, exposing +`src/app.cppm` (`mcpplibs.mwrap.imgui.app`) is the Tier-0 facade. It `export import`s +`mcpplibs.mwrap.imgui.core` and privately imports `mcpplibs.mwrap.imgui.backend.glfw_opengl3`, exposing `ImGui::App::run(...)` as an inline header-style template (the UI callback is a template parameter, so there is no `` dependency). It drives the full lifecycle so a consumer supplies only a per-frame UI callback. diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..120949e --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,130 @@ +Detailed usage and background. See the [README](../README.md) for the current candidate quickstart and support boundary. + +# imgui + +> Dear ImGui 的 C++23 模块化封装 — `import` 即用 · 跨平台 · 特性可插拔 + +[![Release](https://img.shields.io/github/v/release/mcpplibs/imgui-m)](https://github.com/mcpplibs/imgui-m/releases) +[![C++23](https://img.shields.io/badge/C%2B%2B-23-blue.svg)](https://en.cppreference.com/w/cpp/23) +[![Module](https://img.shields.io/badge/module-ok-green.svg)](https://en.cppreference.com/w/cpp/language/modules) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](../LICENSE) + +| [mcpp 构建工具](https://github.com/mcpp-community/mcpp) · [包索引 mcpp-index](https://github.com/mcpp-community/mcpp-index) · [Dear ImGui 上游](https://github.com/ocornut/imgui) · [Issues](https://github.com/mcpplibs/imgui-m/issues) · [Releases](https://github.com/mcpplibs/imgui-m/releases) | +|:---:| +| [![CI](https://github.com/mcpplibs/imgui-m/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/mcpplibs/imgui-m/actions/workflows/ci.yml) | + +## 核心特性 + +- **纯模块导入** — `import mcpplibs.mwrap.imgui.core;`,消费者代码零 `#include` +- **三档使用体验** — `mcpplibs.mwrap.imgui.app` 一行出窗口(默认档)/ `mcpplibs.mwrap.imgui.backend` 编译期契约(自动档)/ `imgui.backend.` 显式选后端(专家档),换后端 = 改一行 import + alias +- **特性可插拔** — `features = ["docking-full"]` 解锁面板停靠 + 拖出主窗口变独立 OS 窗口;`app` 档 + 开启 `mcpplibs.mwrap.imgui.app` facade(它是本仓库的发明,不属于上游 Dear ImGui,所以默认不给) +- **不绑定工具链、不打包 GL 驱动** — Linux / macOS / Windows 三平台 CI;GL 运行时由 mcpp 生态自动闭合 + +## 快速开始 + +```bash +mcpp new myapp --template imgui && cd myapp && mcpp run # 窗口直接出现 +# 模板随库分发、版本自动对齐: +# mcpp new --list-templates imgui # 列出库提供的模板 +# mcpp new ide --template imgui:docking # IDE 式停靠布局(docking-full) +``` + +或在已有项目中手动接入: + +```toml +[dependencies.mcpplibs.mwrap] +imgui = { version = "1.92.8", features = ["app"] } +``` + +```cpp +import mcpplibs.mwrap.imgui.core; +import mcpplibs.mwrap.imgui.app; + +int main() { + return ImGui::App::run([] { + ImGui::Begin("hello"); + ImGui::TextUnformatted("mcpp + imgui"); + ImGui::End(); + }); +} +``` + +## 模块一览 + +| 模块 | 说明 | +|---|---| +| `mcpplibs.mwrap.imgui.core` | Dear ImGui 核心:context / frame / widgets / draw-data | +| `mcpplibs.mwrap.imgui.app` | Tier-0 facade:平台默认后端 + 一行帧循环 | +| `mcpplibs.mwrap.imgui.backend` | 后端契约:`BackendApi` concept + 共享类型(`GlConfig`/`Error`/`FbSize`) | +| `mcpplibs.mwrap.imgui.backend.glfw_opengl3` | GLFW + OpenGL3 窗口后端(单一 `Backend` 类型) | +| `mcpplibs.mwrap.imgui.backend.headless` | 无显示后端:同一业务循环跑 CI / 逻辑测试 | + +后端实现模块**不 re-export** `mcpplibs.mwrap.imgui.core`,消费者需各自显式 `import`;换后端只改 import 行与一行 `using Backend = ...;`。 + +## Features + +| Feature | 说明 | +|---|---| +| `docking` | 主窗口内面板停靠(Wayland 安全) | +| `viewports` | 面板拖出主窗口分离为独立 OS 窗口(X11 / Windows / macOS) | +| `docking-full` | 组合糖 = `["docking", "viewports"]`,完整 docking 体验 | + +```toml +[dependencies.mcpplibs.mwrap] +imgui = { version = "1.92.8", features = ["docking-full", "app"] } +``` + +docking/viewports 源码来自上游 docking tag(`compat.imgui 1.92.8-docking`,主线超集——特性关闭时行为与主线一致)。需 mcpp ≥ 0.0.47。 + +## 示例 + +| 示例 | 内容 | +|---|---| +| [`examples/app_minimal`](../examples/app_minimal) | facade 三行出窗口 | +| [`examples/docking`](../examples/docking) | IDE 式四分屏 + 面板分离为独立 OS 窗口 | +| [`examples/minimal_window`](../examples/minimal_window) | 显式后端 API 的最小窗口(带诊断) | +| [`examples/glfw_opengl3`](../examples/glfw_opengl3) | 显式后端的完整事件循环 | +| [`examples/basic`](../examples/basic) | headless 核心 API(CI 可跑) | + +```bash +cd examples/docking && mcpp run +``` + +## 工具链与运行时 + +包不固定工具链(mcpp 解析环境默认,如 Linux glibc gcc);GL/GLX 运行时由生态包 `compat.glx-runtime` 透传闭合,不打包驱动。解析结果可用 `mcpp why` / `mcpp self doctor` 查看。 + +> [!NOTE] +> 早期版本,接口可能调整。问题与想法欢迎提 [issue](https://github.com/mcpplibs/imgui-m/issues)。 + +## License + +封装代码 MIT;Dear ImGui 本体 MIT(经 `compat.imgui` 提供)。 + + +## Current-client quickstart candidate + +The wrapper revision is `1.92.8`; the upstream C/C++ API and published module +identity stay unchanged. Development tooling uses mcpp `2026.10.24.1`. Templates +now write one qualified dependency from `self.name`, without repeating a namespace. + +```sh +mcpp new quickstart --template mcpplibs.mwrap.imgui@1.92.8:window +cd quickstart +mcpp run +``` + +This revision is a PR candidate until its source archive and index entry are +validated. `tools/test_template.py` exercises actual `mcpp new`, checks the +rendered dependency, strictly builds the candidate and runs it. + +Both `window` and `docking` accept `--smoke-test`, render three frames and exit. +`ImGui::App::Options::max_frames` defaults to 0 (interactive); a nonzero value +bounds the render loop and still performs normal renderer/window cleanup. +The candidate index supplies the shared X11 adapter corrections; Linux rendering +validation uses a virtual display. A successful build alone does not verify GLX. + +The package default is `headless`: `mcpp new quickstart --template mcpplibs.mwrap.imgui@1.92.8` +creates a CPU-only frame example that checks real draw vertices and needs no +desktop session. `:window` and `:docking` remain explicit desktop templates. diff --git a/examples/app_minimal/mcpp.toml b/examples/app_minimal/mcpp.toml index 9032474..481f9ce 100644 --- a/examples/app_minimal/mcpp.toml +++ b/examples/app_minimal/mcpp.toml @@ -5,4 +5,4 @@ description = "Tier-0 imgui.app facade consumer" license = "MIT" [dependencies] -imgui = { path = "../..", features = ["app"] } +"mcpplibs.mwrap.imgui" = { path = "../..", features = ["app"] } diff --git a/examples/app_minimal/src/main.cpp b/examples/app_minimal/src/main.cpp index 1f7c1a5..5c7b344 100644 --- a/examples/app_minimal/src/main.cpp +++ b/examples/app_minimal/src/main.cpp @@ -1,5 +1,5 @@ -import imgui.core; -import imgui.app; +import mcpplibs.mwrap.imgui.core; +import mcpplibs.mwrap.imgui.app; // Tier-0: imgui.app owns the whole lifecycle; we only supply the per-frame UI. int main() { diff --git a/examples/basic/mcpp.toml b/examples/basic/mcpp.toml index acf4edf..5ae98ce 100644 --- a/examples/basic/mcpp.toml +++ b/examples/basic/mcpp.toml @@ -5,4 +5,4 @@ description = "Headless consumer for the imgui.core module" license = "MIT" [dependencies] -imgui = { path = "../.." } +"mcpplibs.mwrap.imgui" = { path = "../.." } diff --git a/examples/basic/src/main.cpp b/examples/basic/src/main.cpp index de83dd9..ed388c1 100644 --- a/examples/basic/src/main.cpp +++ b/examples/basic/src/main.cpp @@ -1,5 +1,5 @@ import std; -import imgui.core; +import mcpplibs.mwrap.imgui.core; int main() { ImGuiContext* context = ImGui::CreateContext(); @@ -15,7 +15,7 @@ int main() { ImGui::NewFrame(); bool open = true; ImGui::Begin("mcpp imgui", &open); - ImGui::TextUnformatted("hello from import imgui.core"); + ImGui::TextUnformatted("hello from import mcpplibs.mwrap.imgui.core"); ImGui::End(); ImGui::Render(); diff --git a/examples/docking/mcpp.toml b/examples/docking/mcpp.toml index 4f94183..b2e9cb1 100644 --- a/examples/docking/mcpp.toml +++ b/examples/docking/mcpp.toml @@ -5,4 +5,4 @@ description = "Dockable-windows demo using the imgui `docking` feature" license = "MIT" [dependencies] -imgui = { path = "../..", features = ["docking-full", "app"] } +"mcpplibs.mwrap.imgui" = { path = "../..", features = ["docking-full", "app"] } diff --git a/examples/docking/src/main.cpp b/examples/docking/src/main.cpp index 7fcbe4d..407fc3d 100644 --- a/examples/docking/src/main.cpp +++ b/examples/docking/src/main.cpp @@ -6,8 +6,8 @@ // a real dock node: drag any tab to re-split, stack, or float it. // With `viewports`, drag a tab OUTSIDE the main window and it detaches into // its own OS window; the "Detached" panel below starts outside on purpose. -import imgui.core; -import imgui.app; +import mcpplibs.mwrap.imgui.core; +import mcpplibs.mwrap.imgui.app; int main() { return ImGui::App::run({.title = "mcpp imgui docking demo"}, [] { diff --git a/examples/glfw_opengl3/mcpp.toml b/examples/glfw_opengl3/mcpp.toml index 2350a3b..31eac1c 100644 --- a/examples/glfw_opengl3/mcpp.toml +++ b/examples/glfw_opengl3/mcpp.toml @@ -5,4 +5,4 @@ description = "GLFW and OpenGL3 consumer for imgui backend modules" license = "MIT" [dependencies] -imgui = { path = "../.." } +"mcpplibs.mwrap.imgui" = { path = "../.." } diff --git a/examples/glfw_opengl3/src/main.cpp b/examples/glfw_opengl3/src/main.cpp index 9a662dd..52c25b7 100644 --- a/examples/glfw_opengl3/src/main.cpp +++ b/examples/glfw_opengl3/src/main.cpp @@ -1,6 +1,6 @@ import std; -import imgui.core; -import imgui.backend.glfw_opengl3; +import mcpplibs.mwrap.imgui.core; +import mcpplibs.mwrap.imgui.backend.glfw_opengl3; // Swap the import above and this alias to switch backends; the rest is identical. using Backend = ImGui::Backend::GlfwOpenGL3; @@ -39,7 +39,7 @@ int main() { ImGui::NewFrame(); bool open = true; ImGui::Begin("mcpp imgui", &open); - ImGui::TextUnformatted("Hello from import imgui.backend.glfw_opengl3"); + ImGui::TextUnformatted("Hello from import mcpplibs.mwrap.imgui.backend.glfw_opengl3"); if (ImGui::Button("Close")) { Backend::SetWindowShouldClose(window, true); } diff --git a/examples/minimal_window/mcpp.toml b/examples/minimal_window/mcpp.toml index 3924708..2b9e703 100644 --- a/examples/minimal_window/mcpp.toml +++ b/examples/minimal_window/mcpp.toml @@ -5,4 +5,4 @@ description = "Minimal GLFW/OpenGL3 window using imgui modules" license = "MIT" [dependencies] -imgui = { path = "../.." } +"mcpplibs.mwrap.imgui" = { path = "../.." } diff --git a/examples/minimal_window/src/main.cpp b/examples/minimal_window/src/main.cpp index 32fe91c..e7e3632 100644 --- a/examples/minimal_window/src/main.cpp +++ b/examples/minimal_window/src/main.cpp @@ -1,6 +1,6 @@ import std; -import imgui.core; -import imgui.backend.glfw_opengl3; +import mcpplibs.mwrap.imgui.core; +import mcpplibs.mwrap.imgui.backend.glfw_opengl3; // Swap the import above and this alias to switch backends; the rest is identical. using Backend = ImGui::Backend::GlfwOpenGL3; diff --git a/mcpp.toml b/mcpp.toml index b825fd1..a696b72 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,26 +1,23 @@ -# The namespace names whose library this is, not who packaged it. Dear ImGui is -# ocornut's; this repository only adds the C++23 module layer over it. The -# version is upstream's, for the same reason: `0.0.6` told a consumer nothing -# about which ImGui they were getting. -# -# See mcpp-index#163 for the ecosystem-wide rule and the migration table. +# Module wrapper; upstream API preserved. Policy: mcpp-index/docs/mwrap.md. [package] -namespace = "ocornut" -name = "imgui" -version = "1.92.8" +namespace = "mcpplibs.mwrap" +name = "imgui" +version = "1.92.8" description = "Dear ImGui 1.92.8 core and GLFW/OpenGL3 backend modules for mcpp" license = "MIT" authors = ["imgui module contributors"] repo = "https://github.com/mcpplibs/imgui-m" [lib] -path = "src/core.cppm" +path = "src/imgui.cppm" # `src/app.cppm` is NOT here — it is behind the `app` feature below. Everything # that remains corresponds to something upstream Dear ImGui actually ships: # the core API, and the GLFW/OpenGL3 backends from upstream's own backends/. [build] sources = [ + "src/imgui.cppm", + "src/macros.cppm", "src/core.cppm", "src/backends/backend.cppm", "src/backends/platform_glfw.cppm", @@ -56,3 +53,6 @@ compat.opengl = "2026.05.31" [dev-dependencies] compat.gtest = "1.15.2" + +[package.metadata.mwrap] +upstream-version = "1.92.8" diff --git a/src/app.cppm b/src/app.cppm index d74ea00..c80a6ae 100644 --- a/src/app.cppm +++ b/src/app.cppm @@ -1,8 +1,8 @@ -export module imgui.app; +export module mcpplibs.mwrap.imgui.app; import std; // private: std::println for init diagnostics -export import imgui.core; // consumers get the ImGui::* surface for free -import imgui.backend.glfw_opengl3; // private: the default concrete backend +export import mcpplibs.mwrap.imgui.core; // consumers get the ImGui::* surface for free +import mcpplibs.mwrap.imgui.backend.glfw_opengl3; // private: the default concrete backend // Tier-0 convenience facade. // @@ -17,6 +17,7 @@ export namespace ImGui::App { int width = 1280; int height = 720; const char* title = "mcpp imgui app"; + unsigned max_frames = 0; // 0 keeps the interactive window open until closed. }; // Runs the full GLFW/OpenGL3 lifecycle, calling `ui()` once per frame. @@ -76,7 +77,9 @@ export namespace ImGui::App { return 3; } - while (!Backend::WindowShouldClose(window)) { + unsigned frames = 0; + while (!Backend::WindowShouldClose(window) && + (opts.max_frames == 0 || frames < opts.max_frames)) { Backend::PollEvents(); Backend::NewFrame(); @@ -99,6 +102,7 @@ export namespace ImGui::App { } #endif Backend::SwapBuffers(window); + if (opts.max_frames != 0) ++frames; } Backend::Shutdown(); diff --git a/src/backends/backend.cppm b/src/backends/backend.cppm index 868fae2..cd9bab4 100644 --- a/src/backends/backend.cppm +++ b/src/backends/backend.cppm @@ -1,13 +1,13 @@ -export module imgui.backend; +export module mcpplibs.mwrap.imgui.backend; -import imgui.core; +import mcpplibs.mwrap.imgui.core; // Generic backend abstraction layer. // // This module owns the shared value types, the cross-platform default GL // configuration, and the compile-time API contract every concrete backend // must satisfy. It contains no platform code and does not re-export -// imgui.core: consumers import imgui.core themselves. +// imgui.core: consumers import mcpplibs.mwrap.imgui.core themselves. export namespace ImGui::Backend { // Requested OpenGL context configuration. Defaults are filled by // RecommendedGlConfig() so examples never hard-code platform values. diff --git a/src/backends/glfw_opengl3.cppm b/src/backends/glfw_opengl3.cppm index 1ce77b3..14cffe4 100644 --- a/src/backends/glfw_opengl3.cppm +++ b/src/backends/glfw_opengl3.cppm @@ -1,9 +1,9 @@ -export module imgui.backend.glfw_opengl3; +export module mcpplibs.mwrap.imgui.backend.glfw_opengl3; -import imgui.core; -export import imgui.backend; // shared types (GlConfig/Error/FbSize) are part of the backend surface -import imgui.backend.platform.glfw; -import imgui.backend.renderer.opengl3; +import mcpplibs.mwrap.imgui.core; +export import mcpplibs.mwrap.imgui.backend; // shared types (GlConfig/Error/FbSize) are part of the backend surface +import mcpplibs.mwrap.imgui.backend.platform.glfw; +import mcpplibs.mwrap.imgui.backend.renderer.opengl3; // Concrete backend: assembles the GLFW platform piece and the OpenGL3 renderer // piece into a single Backend type that satisfies BackendApi. Consumer code is diff --git a/src/backends/headless.cppm b/src/backends/headless.cppm index ec8b2dc..fe507ec 100644 --- a/src/backends/headless.cppm +++ b/src/backends/headless.cppm @@ -1,7 +1,7 @@ -export module imgui.backend.headless; +export module mcpplibs.mwrap.imgui.backend.headless; -import imgui.core; -export import imgui.backend; // shared types (GlConfig/Error/FbSize) + BackendApi +import mcpplibs.mwrap.imgui.core; +export import mcpplibs.mwrap.imgui.backend; // shared types (GlConfig/Error/FbSize) + BackendApi // Headless backend: a second, display-free BackendApi implementation. // diff --git a/src/backends/platform_glfw.cppm b/src/backends/platform_glfw.cppm index 66d590e..2db5cfc 100644 --- a/src/backends/platform_glfw.cppm +++ b/src/backends/platform_glfw.cppm @@ -4,10 +4,10 @@ module; #include #include -export module imgui.backend.platform.glfw; +export module mcpplibs.mwrap.imgui.backend.platform.glfw; -import imgui.core; -import imgui.backend; +import mcpplibs.mwrap.imgui.core; +import mcpplibs.mwrap.imgui.backend; // GLFW platform piece: window/event management plus the ImGui_ImplGlfw_* // platform binding. Reusable assembly unit; composed into a concrete backend diff --git a/src/backends/renderer_opengl3.cppm b/src/backends/renderer_opengl3.cppm index ac9517b..02c8616 100644 --- a/src/backends/renderer_opengl3.cppm +++ b/src/backends/renderer_opengl3.cppm @@ -3,10 +3,10 @@ module; #include #include -export module imgui.backend.renderer.opengl3; +export module mcpplibs.mwrap.imgui.backend.renderer.opengl3; -import imgui.core; -import imgui.backend; +import mcpplibs.mwrap.imgui.core; +import mcpplibs.mwrap.imgui.backend; // OpenGL3 renderer piece: GL framebuffer ops plus the ImGui_ImplOpenGL3_* // renderer binding. Reusable assembly unit; composed into a concrete backend diff --git a/src/core.cppm b/src/core.cppm index 81aa700..93b3f34 100644 --- a/src/core.cppm +++ b/src/core.cppm @@ -6,7 +6,7 @@ module; #include #endif -export module imgui.core; +export module mcpplibs.mwrap.imgui.core; export using ImDrawData = ::ImDrawData; export using ImGuiContext = ::ImGuiContext; diff --git a/src/imgui.cppm b/src/imgui.cppm new file mode 100644 index 0000000..a37cdb6 --- /dev/null +++ b/src/imgui.cppm @@ -0,0 +1,3 @@ +export module mcpplibs.mwrap.imgui; +export import mcpplibs.mwrap.imgui.core; +export import mcpplibs.mwrap.imgui.macros; diff --git a/src/macros.cppm b/src/macros.cppm new file mode 100644 index 0000000..73de294 --- /dev/null +++ b/src/macros.cppm @@ -0,0 +1,16 @@ +module; +#include +namespace mwrap_imgui_capture { +inline constexpr auto value_IMGUI_VERSION = IMGUI_VERSION; +inline constexpr auto value_IMGUI_VERSION_NUM = IMGUI_VERSION_NUM; +} +#undef IMGUI_VERSION +#undef IMGUI_VERSION_NUM + +export module mcpplibs.mwrap.imgui.macros; + +export namespace mcpplibs::mwrap::imgui::macros { +inline constexpr auto IMGUI_VERSION = mwrap_imgui_capture::value_IMGUI_VERSION; +inline constexpr auto IMGUI_VERSION_NUM = mwrap_imgui_capture::value_IMGUI_VERSION_NUM; + +} diff --git a/templates/docking/mcpp.toml.in b/templates/docking/mcpp.toml.in index 906ef6e..9ed092b 100644 --- a/templates/docking/mcpp.toml.in +++ b/templates/docking/mcpp.toml.in @@ -2,8 +2,5 @@ name = "{{project.name}}" version = "0.1.0" -# `ocornut` is spelled out: templates substitute only project.name, -# self.name and self.version — there is no namespace placeholder yet. -# See mcpp-index#163. -[dependencies.ocornut] -{{self.name}} = { version = "{{self.version}}", features = ["docking-full", "app"] } +[dependencies] +"{{self.name}}" = { version = "{{self.version}}", features = ["docking-full", "app"] } diff --git a/templates/docking/src/main.cpp.in b/templates/docking/src/main.cpp.in index 1dbb083..2fc6e46 100644 --- a/templates/docking/src/main.cpp.in +++ b/templates/docking/src/main.cpp.in @@ -1,9 +1,13 @@ // {{project.name}} — generated from {{self.name}}@{{self.version}}:docking -import imgui.core; -import imgui.app; +import std; +import mcpplibs.mwrap.imgui.core; +import mcpplibs.mwrap.imgui.app; -int main() { - return ImGui::App::run({.title = "{{project.name}}"}, [] { +int main(int argc, char** argv) { + const bool smoke = argc > 1 && std::string_view(argv[1]) == "--smoke-test"; + unsigned frames = 0; + const int result = ImGui::App::run({.title = "{{project.name}}", .max_frames = smoke ? 3u : 0u}, [&] { + ++frames; auto dockspace = ImGui::DockSpaceOverViewport(); static bool layout_built = false; @@ -28,4 +32,6 @@ int main() { ImGui::Begin("Inspector"); ImGui::TextUnformatted("properties"); ImGui::End(); ImGui::Begin("Console"); ImGui::TextUnformatted("logs"); ImGui::End(); }); + if (smoke) std::println("Rendered {} ImGui frames", frames); + return result != 0 ? result : (smoke && frames != 3 ? 4 : 0); } diff --git a/templates/headless/mcpp.toml.in b/templates/headless/mcpp.toml.in new file mode 100644 index 0000000..00381f1 --- /dev/null +++ b/templates/headless/mcpp.toml.in @@ -0,0 +1,6 @@ +[package] +name = "{{project.name}}" +version = "0.1.0" + +[dependencies] +"{{self.name}}" = "{{self.version}}" diff --git a/templates/headless/src/main.cpp.in b/templates/headless/src/main.cpp.in new file mode 100644 index 0000000..8778b6b --- /dev/null +++ b/templates/headless/src/main.cpp.in @@ -0,0 +1,28 @@ +import std; +import mcpplibs.mwrap.imgui.core; + +int main() { + struct Context { + ImGuiContext* value = ImGui::CreateContext(); + ~Context() { if (value) ImGui::DestroyContext(value); } + } context; + if (!context.value) return 1; + ImGui::SetCurrentContext(context.value); + auto& io = ImGui::GetIO(); + io.DisplaySize = ImVec2(320, 240); + unsigned char* pixels = nullptr; + int width = 0, height = 0; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + if (!pixels || width <= 0 || height <= 0) return 2; + for (int frame = 0; frame < 3; ++frame) { + ImGui::NewFrame(); + ImGui::Begin("{{project.name}}"); + ImGui::TextUnformatted("Hello from import mcpplibs.mwrap.imgui.core"); + ImGui::End(); + ImGui::Render(); + } + const auto* draw = ImGui::GetDrawData(); + if (!draw || draw->CmdListsCount <= 0 || draw->TotalVtxCount <= 0) return 3; + std::println("Dear ImGui {}: {} vertices generated without a window", ImGui::GetVersion(), draw->TotalVtxCount); + return 0; +} diff --git a/templates/headless/template.toml b/templates/headless/template.toml new file mode 100644 index 0000000..0040dc4 --- /dev/null +++ b/templates/headless/template.toml @@ -0,0 +1,4 @@ +[template] +description = "Build an ImGui frame without a window, GPU or desktop session" +default = true +post_message = "Run `mcpp run` to build and inspect a headless ImGui frame. Use :window or :docking for a desktop app." diff --git a/templates/window/mcpp.toml.in b/templates/window/mcpp.toml.in index b33e661..e010548 100644 --- a/templates/window/mcpp.toml.in +++ b/templates/window/mcpp.toml.in @@ -2,8 +2,5 @@ name = "{{project.name}}" version = "0.1.0" -# `ocornut` is spelled out: templates substitute only project.name, -# self.name and self.version — there is no namespace placeholder yet. -# See mcpp-index#163. -[dependencies.ocornut] -{{self.name}} = { version = "{{self.version}}", features = ["app"] } +[dependencies] +"{{self.name}}" = { version = "{{self.version}}", features = ["app"] } diff --git a/templates/window/src/main.cpp.in b/templates/window/src/main.cpp.in index 7c6c2cb..ad09bb2 100644 --- a/templates/window/src/main.cpp.in +++ b/templates/window/src/main.cpp.in @@ -1,11 +1,17 @@ // {{project.name}} — generated from {{self.name}}@{{self.version}}:window -import imgui.core; -import imgui.app; +import std; +import mcpplibs.mwrap.imgui.core; +import mcpplibs.mwrap.imgui.app; -int main() { - return ImGui::App::run({.title = "{{project.name}}"}, [] { +int main(int argc, char** argv) { + const bool smoke = argc > 1 && std::string_view(argv[1]) == "--smoke-test"; + unsigned frames = 0; + const int result = ImGui::App::run({.title = "{{project.name}}", .max_frames = smoke ? 3u : 0u}, [&] { + ++frames; ImGui::Begin("{{project.name}}"); ImGui::TextUnformatted("Hello from mcpp + imgui"); ImGui::End(); }); + if (smoke) std::println("Rendered {} ImGui frames", frames); + return result != 0 ? result : (smoke && frames != 3 ? 4 : 0); } diff --git a/templates/window/template.toml b/templates/window/template.toml index 891b381..b32e9f0 100644 --- a/templates/window/template.toml +++ b/templates/window/template.toml @@ -1,4 +1,4 @@ [template] description = "Minimal imgui.app window" -default = true +default = false post_message = "cd into the project and run `mcpp run` — the window appears." diff --git a/tests/backend_swap_test.cpp b/tests/backend_swap_test.cpp index d9c6755..d9c43eb 100644 --- a/tests/backend_swap_test.cpp +++ b/tests/backend_swap_test.cpp @@ -1,9 +1,9 @@ #include -import imgui.core; -import imgui.backend; -import imgui.backend.headless; -import imgui.backend.glfw_opengl3; +import mcpplibs.mwrap.imgui.core; +import mcpplibs.mwrap.imgui.backend; +import mcpplibs.mwrap.imgui.backend.headless; +import mcpplibs.mwrap.imgui.backend.glfw_opengl3; namespace B = ImGui::Backend; diff --git a/tests/backend_test.cpp b/tests/backend_test.cpp index a452660..36d62d8 100644 --- a/tests/backend_test.cpp +++ b/tests/backend_test.cpp @@ -1,10 +1,10 @@ #include -import imgui.core; -import imgui.backend; -import imgui.backend.platform.glfw; -import imgui.backend.renderer.opengl3; -import imgui.backend.glfw_opengl3; +import mcpplibs.mwrap.imgui.core; +import mcpplibs.mwrap.imgui.backend; +import mcpplibs.mwrap.imgui.backend.platform.glfw; +import mcpplibs.mwrap.imgui.backend.renderer.opengl3; +import mcpplibs.mwrap.imgui.backend.glfw_opengl3; namespace Backend = ImGui::Backend; diff --git a/tests/imgui_test.cpp b/tests/imgui_test.cpp index f6a9c89..f1555c1 100644 --- a/tests/imgui_test.cpp +++ b/tests/imgui_test.cpp @@ -1,7 +1,7 @@ #include import std; -import imgui.core; +import mcpplibs.mwrap.imgui.core; TEST(ImGuiCoreModuleTest, ExposesVersion) { const char* version = ImGui::GetVersion(); diff --git a/tests/macros.cpp b/tests/macros.cpp new file mode 100644 index 0000000..ac6d61f --- /dev/null +++ b/tests/macros.cpp @@ -0,0 +1,3 @@ +import mcpplibs.mwrap.imgui; +namespace macros = mcpplibs::mwrap::imgui::macros; +int main() { static_assert(macros::IMGUI_VERSION_NUM == 19280); return macros::IMGUI_VERSION[0] != '1'; } diff --git a/tools/check_mwrap_release.py b/tools/check_mwrap_release.py new file mode 100644 index 0000000..227fd8d --- /dev/null +++ b/tools/check_mwrap_release.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +"""Check the immutable mwrap release coordinate and bilingual entry pages.""" +import argparse +from pathlib import Path +import re +import tomllib + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--tag', help='release tag; omitted during PR checks') + args = parser.parse_args() + root = Path(__file__).resolve().parents[1] + package = tomllib.loads((root / 'mcpp.toml').read_text(encoding="utf-8"))['package'] + assert package['namespace'] == 'mcpplibs.mwrap', package + assert re.fullmatch(r'[A-Za-z_][A-Za-z_0-9]*', package['name']), package['name'] + upstream = package['metadata']['mwrap']['upstream-version'] + version = package['version'] + assert version == upstream or re.fullmatch(re.escape(upstream) + r'\.[1-9][0-9]*', version), (version, upstream) + if args.tag: + assert args.tag == 'mwrap-' + version, (args.tag, version) + identity = package['namespace'] + '.' + package['name'] + for name, other in [('README.md', 'README.zh.md'), ('README.zh.md', 'README.md')]: + text = (root / name).read_text(encoding="utf-8") + assert len(text.splitlines()) <= 150, name + assert '](' + other + ')' in '\n'.join(text.splitlines()[:12]), name + assert identity in text and version in text, name + assert text.count('```') % 2 == 0, name + print(identity + '@' + version) + + +if __name__ == '__main__': + main() diff --git a/tools/test_template.py b/tools/test_template.py new file mode 100644 index 0000000..81ea805 --- /dev/null +++ b/tools/test_template.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +"""Render an installed candidate's default mcpp template, then build and run it.""" +import argparse +import json +import os +from pathlib import Path +import shutil +import subprocess +import tempfile +import tomllib + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--mcpp", default="mcpp") + parser.add_argument("--namespace", help="index namespace for legacy manifests without package.namespace") + parser.add_argument("--toolchain", default="llvm@23.1.3") + parser.add_argument("--template", help="named template; omit for the package default") + parser.add_argument("--run-arg", action="append", default=[]) + parser.add_argument("--index", type=Path, help="candidate SDK/wrapper index for unpublished feature dependencies") + args = parser.parse_args() + root = Path(__file__).resolve().parents[1] + metadata = tomllib.loads((root / "mcpp.toml").read_text())["package"] + namespace = metadata.get("namespace", args.namespace) + assert namespace, "Supply the published index namespace for this legacy manifest" + name, version = metadata["name"], metadata["version"] + identity = namespace + "." + name + with tempfile.TemporaryDirectory(prefix=name + "-template-") as directory: + temporary = Path(directory) + home = temporary / "home" + package = home / "registry/data/xpkgs" / (namespace + "-x-" + name) / version / (name + "-" + version) + shutil.copytree(root, package, ignore=shutil.ignore_patterns( + ".git", "target", ".mcpp", ".xlings", ".candidate-index", "__pycache__", "mcpp.lock", "compile_commands.json", "mcpp-2026.*")) + index = home / "indices/mcpplibs/pkgs/mwrap" + index.mkdir(parents=True) + entry = '{url="https://example.invalid/candidate.tar.gz",sha256="fixture"}' + descriptor = ('package={spec="1",namespace=' + json.dumps(namespace) + ',name=' + json.dumps(name) + + ',description="Installed template fixture",type="package",mcpp="mcpp.toml",xpm={') + descriptor += ",".join(platform + "={[" + json.dumps(version) + "]=" + entry + "}" + for platform in ("linux", "macosx", "windows")) + "}}\n" + (index / (identity + ".lua")).write_text(descriptor) + env = dict(os.environ, MCPP_HOME=str(home)) + selector = identity + "@" + version + for command in ([args.mcpp, "new", "--list-templates", selector], + [args.mcpp, "new", "quickstart", "--template", selector + (":" + args.template if args.template else "")]): + print("+", " ".join(command), flush=True) + subprocess.run(command, cwd=temporary, env=env, check=True) + project = temporary / "quickstart" + manifest = project / "mcpp.toml" + text = manifest.read_text() + dependency = tomllib.loads(text)["dependencies"][identity] + options = {"version": dependency} if isinstance(dependency, str) else dependency + assert options["version"] == version, options + replacement = '{ path = ' + json.dumps(package.as_posix()) + if options.get("features"): + replacement += ', features = ' + json.dumps(options["features"]) + replacement += ' }' + lines = text.splitlines() + matches = [i for i, line in enumerate(lines) if line.startswith(json.dumps(identity) + " =")] + assert len(matches) == 1, "Expected exactly one qualified dependency from self.name" + lines[matches[0]] = json.dumps(identity) + " = " + replacement + manifest.write_text("\n".join(lines) + "\n") + if args.index: + candidate_index = args.index.resolve() + subprocess.run([os.sys.executable, str(candidate_index / "tests/prepare_mwrap_indices.py")], + cwd=candidate_index, check=True) + with manifest.open("a") as out: + out.write("\n[indices]\n") + for namespace in ("compat", "mcpplibs.mwrap"): + view = candidate_index / "tests/mwrap/.indices" / namespace + out.write(json.dumps(namespace) + ' = { path = ' + json.dumps(view.as_posix()) + ' }\n') + command = [args.mcpp, "build", "--toolchain", args.toolchain, "--strict"] + print("+", " ".join(command), flush=True) + subprocess.run(command, cwd=project, check=True) + command = [args.mcpp, "run", "--toolchain", args.toolchain] + if args.run_arg: + command += ["--", *args.run_arg] + print("+", " ".join(command), flush=True) + subprocess.run(command, cwd=project, check=True) + + +if __name__ == "__main__": + main()