Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
331 changes: 331 additions & 0 deletions .agents/docs/2026-06-27-add-cjson-and-nlohmann-json-plan.md

Large diffs are not rendered by default.

162 changes: 151 additions & 11 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ on:
paths: ["pkgs/**/*.lua", "tests/**", "README.md", ".github/workflows/validate.yml"]
push:
branches: [main]
schedule:
# nightly full regression — exercises every smoke suite regardless of diff
- cron: "0 6 * * *"
workflow_dispatch:

env:
MCPP_VERSION: "0.0.67"

jobs:
lint:
Expand Down Expand Up @@ -84,20 +91,111 @@ jobs:
[ $fail -eq 0 ] && echo "All CN mirror urls reachable."
exit $fail

smoke-linux:
# ── Decide what to smoke-test ─────────────────────────────────────────
# PR builds run ONLY the example projects whose package descriptor changed
# (tests/examples/<pkg>/). Anything that can't be narrowed that way — a
# changed pkg without an example yet, a scaffolding/CI change, or a
# push/nightly/manual run — falls back to the full legacy smoke regression.
detect:
runs-on: ubuntu-latest
outputs:
examples: ${{ steps.f.outputs.examples }}
full: ${{ steps.f.outputs.full }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install jq
run: sudo apt-get install -y --no-install-recommends jq
- id: f
run: |
full=false
examples='[]'
event="${{ github.event_name }}"
if [ "$event" != "pull_request" ]; then
# push to main / nightly schedule / manual dispatch → full regression
full=true
else
git fetch -q origin "${{ github.base_ref }}"
changed=$(git diff --name-only "origin/${{ github.base_ref }}...HEAD")
echo "changed files:"; echo "$changed" | sed 's/^/ /'
# scaffolding / CI change → full regression
if echo "$changed" | grep -qE '^(tests/run_example\.sh|tests/smoke_.*\.sh|tests/check_mirror_urls\.lua|tests/list_cn_urls\.lua|\.github/workflows/validate\.yml)$'; then
full=true
fi
sel=""
while IFS= read -r file; do
case "$file" in
pkgs/*/*.lua)
b=$(basename "$file" .lua); b=${b#compat.}
if [ -d "tests/examples/$b" ]; then
sel="$sel$b"$'\n'
else
# changed pkg with no example yet → cannot narrow, run full
full=true
fi
;;
esac
done <<< "$changed"
if [ -n "$sel" ]; then
examples=$(printf '%s' "$sel" | grep -v '^$' | sort -u | jq -R . | jq -sc .)
fi
fi
echo "full=$full" >> "$GITHUB_OUTPUT"
echo "examples=$examples" >> "$GITHUB_OUTPUT"
echo "=> full=$full examples=$examples"

# ── Fast path: per-changed-package example projects (Linux) ───────────
smoke-examples:
needs: detect
if: needs.detect.outputs.examples != '[]'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
pkg: ${{ fromJson(needs.detect.outputs.examples) }}
steps:
- uses: actions/checkout@v4
- name: Restore mcpp registry cache
uses: actions/cache@v4
with:
path: ~/.mcpp/registry
key: mcpp-registry-${{ runner.os }}-0.0.46-${{ hashFiles('pkgs/**/*.lua', 'tests/*.sh', '.github/workflows/validate.yml') }}
key: mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}-${{ hashFiles('pkgs/**/*.lua', 'tests/**', '.github/workflows/validate.yml') }}
restore-keys: |
mcpp-registry-${{ runner.os }}-0.0.46-
mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}-
- name: Download mcpp
run: |
curl -L -fsS -o mcpp.tar.gz \
"https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_VERSION}/mcpp-${MCPP_VERSION}-linux-x86_64.tar.gz"
tar -xzf mcpp.tar.gz
root="$PWD/mcpp-${MCPP_VERSION}-linux-x86_64"
mkdir -p "$HOME/.mcpp/registry"
cp -a "$root/registry/." "$HOME/.mcpp/registry/"
echo "MCPP=$root/bin/mcpp" >> "$GITHUB_ENV"
echo "MCPP_VENDORED_XLINGS=$root/registry/bin/xlings" >> "$GITHUB_ENV"
echo "$root/bin" >> "$GITHUB_PATH"
- name: Run example "${{ matrix.pkg }}"
env:
MCPP_VERSION: "0.0.46"
MCPP_INDEX_MIRROR: GLOBAL
run: |
"$MCPP" --version
timeout 1800 bash tests/run_example.sh "${{ matrix.pkg }}"

# ── Full regression (legacy whole-suite smoke) ────────────────────────
smoke-full-linux:
needs: detect
if: needs.detect.outputs.full == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Restore mcpp registry cache
uses: actions/cache@v4
with:
path: ~/.mcpp/registry
key: mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}-${{ hashFiles('pkgs/**/*.lua', 'tests/**', '.github/workflows/validate.yml') }}
restore-keys: |
mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}-
- name: Download mcpp
run: |
curl -L -fsS -o mcpp.tar.gz \
"https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_VERSION}/mcpp-${MCPP_VERSION}-linux-x86_64.tar.gz"
Expand All @@ -118,10 +216,53 @@ jobs:
timeout 1800 bash tests/smoke_compat_core.sh
timeout 1800 bash tests/smoke_compat_imgui.sh
timeout 1800 bash tests/smoke_compat_archive.sh

# ── GLFW/OpenGL window + imgui-module demos (nightly / manual only) ────
# These two depend on `compat.glfw` (abi=glibc) and the GLX/OpenGL runtime.
# On mcpp 0.0.67 the window demo intermittently resolves a clang/libc++
# toolchain for compat.glfw despite a `default = "gcc@16.1.0"` pin, failing
# with `ABI mismatch: compat.glfw requires abi=glibc`. The same scripts pass
# on 0.0.66 locally, and glfw builds fine on 0.0.67 elsewhere (linux imgui
# smoke + mac/windows portable), so this is an mcpp toolchain-resolution
# regression unrelated to package descriptors — tracked separately. Kept out
# of the PR-blocking path (display/driver/ABI-sensitive, already optional)
# but still exercised on the nightly schedule and on manual dispatch.
smoke-gl-linux:
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Restore mcpp registry cache
uses: actions/cache@v4
with:
path: ~/.mcpp/registry
key: mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}-${{ hashFiles('pkgs/**/*.lua', 'tests/**', '.github/workflows/validate.yml') }}
restore-keys: |
mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}-
- name: Download mcpp
run: |
curl -L -fsS -o mcpp.tar.gz \
"https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_VERSION}/mcpp-${MCPP_VERSION}-linux-x86_64.tar.gz"
tar -xzf mcpp.tar.gz
root="$PWD/mcpp-${MCPP_VERSION}-linux-x86_64"
mkdir -p "$HOME/.mcpp/registry"
cp -a "$root/registry/." "$HOME/.mcpp/registry/"
echo "MCPP=$root/bin/mcpp" >> "$GITHUB_ENV"
echo "MCPP_VENDORED_XLINGS=$root/registry/bin/xlings" >> "$GITHUB_ENV"
echo "$root/bin" >> "$GITHUB_PATH"
- name: Run GL window + imgui-module smoke tests
env:
MCPP_INDEX_SMOKE_MCPP_HOME: ${{ runner.temp }}/mcpp-smoke-home
MCPP_INDEX_SMOKE_CACHE_DIR: ${{ runner.temp }}/mcpp-smoke-cache
run: |
mkdir -p "$MCPP_INDEX_SMOKE_MCPP_HOME"
"$MCPP" --version
timeout 1800 bash tests/smoke_compat_imgui_window.sh
timeout 1800 bash tests/smoke_imgui_module.sh

smoke-portable:
needs: detect
if: needs.detect.outputs.full == 'true'
name: smoke-${{ matrix.platform }}
runs-on: ${{ matrix.os }}
timeout-minutes: 60
Expand All @@ -131,14 +272,14 @@ jobs:
include:
- platform: macos
os: macos-15
archive: mcpp-0.0.46-macosx-arm64.tar.gz
root: mcpp-0.0.46-macosx-arm64
archive: mcpp-0.0.67-macosx-arm64.tar.gz
root: mcpp-0.0.67-macosx-arm64
mcpp: bin/mcpp
xlings: registry/bin/xlings
- platform: windows
os: windows-latest
archive: mcpp-0.0.46-windows-x86_64.zip
root: mcpp-0.0.46-windows-x86_64
archive: mcpp-0.0.67-windows-x86_64.zip
root: mcpp-0.0.67-windows-x86_64
mcpp: bin/mcpp.exe
xlings: registry/bin/xlings.exe
steps:
Expand All @@ -147,13 +288,12 @@ jobs:
uses: actions/cache@v4
with:
path: ~/.mcpp/registry
key: mcpp-registry-${{ runner.os }}-0.0.46-${{ hashFiles('pkgs/**/*.lua', 'tests/*.sh', '.github/workflows/validate.yml') }}
key: mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}-${{ hashFiles('pkgs/**/*.lua', 'tests/**', '.github/workflows/validate.yml') }}
restore-keys: |
mcpp-registry-${{ runner.os }}-0.0.46-
mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}-
- name: Download mcpp
shell: bash
env:
MCPP_VERSION: "0.0.46"
MCPP_ARCHIVE: ${{ matrix.archive }}
MCPP_ROOT: ${{ matrix.root }}
run: |
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
.xlings-index-cache.json
# example project build/fetch artifacts
tests/examples/*/target/
tests/examples/*/.mcpp/
tests/examples/*/compile_commands.json
tests/examples/*/mcpp.lock
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ mcpp build # 自动拉取源码 + 构建
| 包名 | 版本 | 简介 | 仓库 |
|------|------|------|------|
| `imgui` | 0.0.1 | Dear ImGui C++23 模块封装 — `import imgui.core;` / `import imgui.backend.glfw_opengl3;` | [mcpplibs/imgui-m](https://github.com/mcpplibs/imgui-m) |
| `nlohmann.json` | 3.12.0 | JSON for Modern C++,开箱即用的 C++23 模块 — `import nlohmann.json;` | [nlohmann/json](https://github.com/nlohmann/json) |

### 第三方 C/C++ 库

Expand All @@ -37,6 +38,7 @@ mcpp build # 自动拉取源码 + 构建
| `ftxui` | 6.1.9 | C++ 函数式终端 UI 库(screen + dom + component) |
| `glfw` | 3.4 | GLFW 窗口与输入库(X11/null 后端源码构建) |
| `gtest` | 1.15.2 | Google Test 测试框架 |
| `cjson` | 1.7.19 | 超轻量 ANSI C JSON 解析库(`#include <cJSON.h>`,`compat` 源码构建) |
| `imgui` | 1.92.8 | Dear ImGui immediate-mode GUI 核心源码 |
| `opengl` | 2026.05.31 | Khronos OpenGL API 头文件 |
| `glx-runtime` | 2026.06.03 | Linux host GLVND/GLX/OpenGL runtime adapter |
Expand Down Expand Up @@ -124,7 +126,19 @@ mcpp 0.0.3+ 的 transitive walker 自动沿链路传播头文件和依赖,消费
> 带入 host GLX 库常见的 X11/Xext ABI 依赖闭包。
> 窗口运行时仍需要宿主环境提供可用的 X server/GLX/OpenGL 驱动。

### 本地 smoke 验证
### 单库示例工程(`tests/examples/<pkg>/`)

每个库可以有一个最小可构建工程(自带 `mcpp.toml` + 源码,`[indices]` 相对指回本仓),
既是文档也是测试。CI 在 PR 上**只跑改动到的库**对应的示例(`tests/run_example.sh`),
其余情况(push / nightly / 改动脚手架)才跑下面的全量 smoke。

```bash
MCPP=/path/to/mcpp tests/run_example.sh cjson # #include <cJSON.h>
MCPP=/path/to/mcpp tests/run_example.sh nlohmann.json # import nlohmann.json;
# 等价于:cd tests/examples/cjson && mcpp run
```

### 本地 smoke 验证(全量回归)

```bash
MCPP=/path/to/mcpp tests/smoke_compat_core.sh
Expand Down
64 changes: 64 additions & 0 deletions pkgs/c/compat.cjson.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
-- Form B inline descriptor for cJSON — an ultralightweight JSON parser in
-- ANSI C. Pure-C source build (same shape as compat.zlib): compile cJSON.c
-- into a lib, expose cJSON.h via include_dirs. The optional cJSON_Utils
-- extension (JSON Pointer / Patch / merge) is gated behind the `utils`
-- feature, mirroring how compat.gtest gates gtest_main behind `main`:
-- listed under features only, so it is excluded by default and pulled in
-- when `features = ["utils"]` is requested on the dependency.
--
-- All `mcpp` paths are GLOBS relative to the verdir; the leading `*/`
-- absorbs the GitHub tarball's `cJSON-<tag>/` wrap layer.
package = {
spec = "1",
namespace = "compat",
name = "compat.cjson",
description = "Ultralightweight JSON parser in ANSI C",
licenses = {"MIT"},
repo = "https://github.com/DaveGamble/cJSON",
type = "package",

xpm = {
linux = {
["1.7.19"] = {
url = {
GLOBAL = "https://github.com/DaveGamble/cJSON/archive/refs/tags/v1.7.19.tar.gz",
CN = "https://gitcode.com/mcpp-res/cjson/releases/download/1.7.19/cjson-1.7.19.tar.gz",
},
sha256 = "7fa616e3046edfa7a28a32d5f9eacfd23f92900fe1f8ccd988c1662f30454562",
},
},
macosx = {
["1.7.19"] = {
url = {
GLOBAL = "https://github.com/DaveGamble/cJSON/archive/refs/tags/v1.7.19.tar.gz",
CN = "https://gitcode.com/mcpp-res/cjson/releases/download/1.7.19/cjson-1.7.19.tar.gz",
},
sha256 = "7fa616e3046edfa7a28a32d5f9eacfd23f92900fe1f8ccd988c1662f30454562",
},
},
windows = {
["1.7.19"] = {
url = {
GLOBAL = "https://github.com/DaveGamble/cJSON/archive/refs/tags/v1.7.19.tar.gz",
CN = "https://gitcode.com/mcpp-res/cjson/releases/download/1.7.19/cjson-1.7.19.tar.gz",
},
sha256 = "7fa616e3046edfa7a28a32d5f9eacfd23f92900fe1f8ccd988c1662f30454562",
},
},
},

mcpp = {
language = "c++23",
import_std = false,
c_standard = "c99",
include_dirs = { "*" },
sources = { "*/cJSON.c" },
targets = { ["cjson"] = { kind = "lib" } },
-- cJSON_Utils is an optional extension; excluded by default, pulled in
-- only when `features = ["utils"]` is requested on the dependency.
features = {
["utils"] = { sources = { "*/cJSON_Utils.c" } },
},
deps = { },
},
}
Loading
Loading