Skip to content

Commit 1d33409

Browse files
authored
feat(workspace): self-referential mcpp [workspace] over the index (#57)
The repo becomes both the package index (pkgs/) and a mcpp workspace that consumes it: a virtual root [workspace] whose members are the per-library example projects under tests/examples/, each resolving this repo's own pkgs/ via a local [indices] path. Eat our own dog food. - root mcpp.toml: [workspace].members = cjson/eigen/nlohmann.json/openblas - tests/run_workspace.sh: thin driver, members are the single source of truth - tests/examples/openblas: L1-cfg consumer — Windows-only openblas dep+flags (cfg(windows)), cblas_dgemm assert on Windows, no-op on linux/macos - validate.yml: detect.workspace output + smoke-workspace job; MCPP_VERSION 0.0.73 -> 0.0.77 (carries L1 per-OS deps/flags + [xlings] env) - design doc: §8 implementation status
1 parent 078783d commit 1d33409

6 files changed

Lines changed: 168 additions & 11 deletions

File tree

.agents/docs/2026-06-29-mcpp-native-workspace-ci-design.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,3 +192,28 @@ windows-runtime-dll(0.0.73)那次"mcpp 补能力 → recipe/CI 变简单"完全
192192

193193
> 关联:本仓 `.github/workflows/validate.yml`(detect 拆分 + 下载缓存,2026-06-29 已落);mcpp 仓
194194
> `.agents/docs/2026-06-29-windows-runtime-dll-deployment-and-openblas.md`(per-OS 能力如何让 recipe 变简单的先例)。
195+
196+
---
197+
198+
## 8. 实现状态(2026-06-30 落地)
199+
200+
自参照 workspace 的**首个可签入切片**已落地(对应 §6 的 P0 + P1 起步,依赖 mcpp 0.0.77 的 L1
201+
`[target.'cfg(...)']` per-OS 依赖/flags + windows-runtime-dll 部署):
202+
203+
- **`mcpp.toml`(仓根)**:虚拟 `[workspace]`,`members` 列出 `tests/examples/{cjson,eigen,
204+
nlohmann.json,openblas}`——即真实索引的逐库示例本身就是 workspace 成员。仓 = 索引 ⊕ 消费索引的
205+
workspace(双视角)。
206+
- **`tests/run_workspace.sh`**:薄驱动。从仓根 `mcpp.toml``[workspace].members` 解析成员(单一
207+
事实源,无硬编码),逐个 `mcpp build && mcpp run`;每个成员经本地 `[indices]` path 解析**本仓
208+
pkgs/** 的真实 recipe。
209+
- **`tests/examples/openblas/`**:L1-cfg 消费者示范。`[target.'cfg(windows)'.dependencies.compat]
210+
openblas` + `[target.'cfg(windows)'.build] cxxflags=["-DHAVE_OPENBLAS=1"]`;Windows 走 `cblas_dgemm`
211+
断言 `[19 22; 43 50]`,linux/macos 为 no-op(openblas 不被拉取)。证明 per-OS 依赖按**解析目标
212+
三元组**门控。
213+
- **`validate.yml`**:`detect``workspace` 输出(push/schedule 全跑;PR 命中仓根 `mcpp.toml`
214+
`tests/run_workspace.sh`、或任一 `tests/examples/*` 成员时触发);新增 `smoke-workspace`(ubuntu)
215+
跑驱动。`MCPP_VERSION` 升至 0.0.77。
216+
217+
**尚未收敛**(留待 §6 P2/P3):per-OS 成员差异仍可由 cfg 表承担而非驱动;环境隔离/缓存/产物断言仍在
218+
各 smoke 脚本;`--workspace` 单命令(G1)仍是 mcpp 侧待加能力——当前驱动按成员循环。旧 smoke 脚本
219+
**保留**,与 workspace 并行覆盖,等价验证充分后再逐个迁移/删除。

.github/workflows/validate.yml

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ on:
1111
workflow_dispatch:
1212

1313
env:
14-
# Bumped to 0.0.73: carries Windows runtime-DLL deployment, required by the
14+
# Bumped to 0.0.77: carries L1 platform-conditional deps/flags + [xlings] env,
1515
# compat.openblas Windows recipe (bin/libopenblas.dll staged beside the .exe).
16-
MCPP_VERSION: "0.0.73"
16+
MCPP_VERSION: "0.0.77"
1717

1818
jobs:
1919
lint:
@@ -105,6 +105,7 @@ jobs:
105105
full: ${{ steps.f.outputs.full }}
106106
full_linux: ${{ steps.f.outputs.full_linux }}
107107
full_portable: ${{ steps.f.outputs.full_portable }}
108+
workspace: ${{ steps.f.outputs.workspace }}
108109
steps:
109110
- uses: actions/checkout@v4
110111
with:
@@ -123,11 +124,12 @@ jobs:
123124
# coverage is ever silently dropped).
124125
full_linux=false
125126
full_portable=false
127+
workspace=false
126128
examples='[]'
127129
event="${{ github.event_name }}"
128130
if [ "$event" != "pull_request" ]; then
129131
# push to main / nightly schedule / manual dispatch → full regression
130-
full_linux=true; full_portable=true
132+
full_linux=true; full_portable=true; workspace=true
131133
else
132134
git fetch -q origin "${{ github.base_ref }}"
133135
changed=$(git diff --name-only "origin/${{ github.base_ref }}...HEAD")
@@ -138,16 +140,22 @@ jobs:
138140
case "$file" in
139141
# Common harness / the workflow itself → both platforms.
140142
tests/run_example.sh|tests/check_mirror_urls.lua|tests/list_cn_urls.lua|.github/workflows/validate.yml)
141-
full_linux=true; full_portable=true ;;
143+
full_linux=true; full_portable=true; workspace=true ;;
144+
# The self-referential workspace: its declaration (root mcpp.toml)
145+
# and its driver. A member change under tests/examples/* also feeds
146+
# the workspace job (handled below, with the examples fast path).
147+
mcpp.toml|tests/run_workspace.sh)
148+
workspace=true ;;
142149
# Portable suite script runs only in smoke-portable (win+mac).
143150
tests/smoke_compat_portable.sh)
144151
full_portable=true ;;
145152
# Linux-only smoke scripts run only in smoke-full-linux.
146153
tests/smoke_compat_core.sh|tests/smoke_compat_imgui.sh|tests/smoke_compat_archive.sh|tests/smoke_compat_imgui_window.sh|tests/smoke_imgui_module.sh)
147154
full_linux=true ;;
148-
# Example projects are handled by the smoke-examples fast path.
155+
# Example projects are handled by the smoke-examples fast path;
156+
# they are also workspace members, so re-run the workspace driver.
149157
tests/examples/*)
150-
: ;;
158+
workspace=true ;;
151159
# Any other tests/ change (new helper / new smoke script) → both (safe).
152160
tests/*)
153161
full_linux=true; full_portable=true ;;
@@ -171,8 +179,9 @@ jobs:
171179
echo "full=$full" >> "$GITHUB_OUTPUT"
172180
echo "full_linux=$full_linux" >> "$GITHUB_OUTPUT"
173181
echo "full_portable=$full_portable" >> "$GITHUB_OUTPUT"
182+
echo "workspace=$workspace" >> "$GITHUB_OUTPUT"
174183
echo "examples=$examples" >> "$GITHUB_OUTPUT"
175-
echo "=> full_linux=$full_linux full_portable=$full_portable examples=$examples"
184+
echo "=> full_linux=$full_linux full_portable=$full_portable workspace=$workspace examples=$examples"
176185
177186
# ── Fast path: per-changed-package example projects (Linux) ───────────
178187
smoke-examples:
@@ -210,6 +219,44 @@ jobs:
210219
"$MCPP" --version
211220
timeout 1800 bash tests/run_example.sh "${{ matrix.pkg }}"
212221
222+
# ── Self-referential workspace: mcpp-index AS a mcpp [workspace] ──────
223+
# Dogfoods mcpp's own native [workspace]: the root mcpp.toml declares every
224+
# per-library example as a workspace member, and the driver builds+runs each
225+
# through the real pkgs/ index (local [indices] path). This is the dual
226+
# perspective — the repo is both the package index and a consumer of it. The
227+
# openblas member additionally exercises L1 platform-conditional deps/flags
228+
# (no-op here on linux; the Windows cblas path is covered by smoke-portable).
229+
smoke-workspace:
230+
needs: detect
231+
if: needs.detect.outputs.workspace == 'true'
232+
runs-on: ubuntu-latest
233+
steps:
234+
- uses: actions/checkout@v4
235+
- name: Restore mcpp registry cache
236+
uses: actions/cache@v4
237+
with:
238+
path: ~/.mcpp/registry
239+
key: mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}-${{ hashFiles('pkgs/**/*.lua', 'tests/**', '.github/workflows/validate.yml') }}
240+
restore-keys: |
241+
mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}-
242+
- name: Download mcpp
243+
run: |
244+
curl -L -fsS -o mcpp.tar.gz \
245+
"https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_VERSION}/mcpp-${MCPP_VERSION}-linux-x86_64.tar.gz"
246+
tar -xzf mcpp.tar.gz
247+
root="$PWD/mcpp-${MCPP_VERSION}-linux-x86_64"
248+
mkdir -p "$HOME/.mcpp/registry"
249+
cp -a "$root/registry/." "$HOME/.mcpp/registry/"
250+
echo "MCPP=$root/bin/mcpp" >> "$GITHUB_ENV"
251+
echo "MCPP_VENDORED_XLINGS=$root/registry/bin/xlings" >> "$GITHUB_ENV"
252+
echo "$root/bin" >> "$GITHUB_PATH"
253+
- name: Build + run every workspace member
254+
env:
255+
MCPP_INDEX_MIRROR: GLOBAL
256+
run: |
257+
"$MCPP" --version
258+
timeout 1800 bash tests/run_workspace.sh
259+
213260
# ── Full regression (legacy whole-suite smoke) ────────────────────────
214261
smoke-full-linux:
215262
needs: detect
@@ -279,14 +326,14 @@ jobs:
279326
include:
280327
- platform: macos
281328
os: macos-15
282-
archive: mcpp-0.0.73-macosx-arm64.tar.gz
283-
root: mcpp-0.0.73-macosx-arm64
329+
archive: mcpp-0.0.77-macosx-arm64.tar.gz
330+
root: mcpp-0.0.77-macosx-arm64
284331
mcpp: bin/mcpp
285332
xlings: registry/bin/xlings
286333
- platform: windows
287334
os: windows-latest
288-
archive: mcpp-0.0.73-windows-x86_64.zip
289-
root: mcpp-0.0.73-windows-x86_64
335+
archive: mcpp-0.0.77-windows-x86_64.zip
336+
root: mcpp-0.0.77-windows-x86_64
290337
mcpp: bin/mcpp.exe
291338
xlings: registry/bin/xlings.exe
292339
steps:

mcpp.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# mcpp-index is simultaneously the package index (pkgs/) AND a mcpp workspace that
2+
# tests it: a virtual workspace whose members are the per-library example/test
3+
# projects under tests/examples/, each consuming this repo's own pkgs/ via a local
4+
# [indices] path. "Eat our own dog food." See
5+
# .agents/docs/2026-06-29-mcpp-native-workspace-ci-design.md.
6+
[workspace]
7+
members = [
8+
"tests/examples/cjson",
9+
"tests/examples/eigen",
10+
"tests/examples/nlohmann.json",
11+
"tests/examples/openblas",
12+
]

tests/examples/openblas/mcpp.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# L1-cfg consumer: openblas is a Windows-only runtime dependency (import lib +
2+
# DLL); on linux/macos this member is a trivial no-op binary. Demonstrates mcpp's
3+
# platform-conditional [target.'cfg(...)'] dependencies + flags (mcpp >= 0.0.75),
4+
# with the windows-runtime-DLL deployment (mcpp >= 0.0.73). Self-referential:
5+
# the openblas recipe is resolved from THIS repo's pkgs/ via the local index.
6+
[package]
7+
name = "openblas-example"
8+
version = "0.1.0"
9+
10+
[indices]
11+
compat = { path = "../../.." }
12+
13+
# Pull OpenBLAS only when building for Windows; define HAVE_OPENBLAS so the source
14+
# compiles the cblas path there and a no-op everywhere else.
15+
[target.'cfg(windows)'.dependencies.compat]
16+
openblas = "0.3.33"
17+
[target.'cfg(windows)'.build]
18+
cxxflags = ["-DHAVE_OPENBLAS=1"]
19+
20+
[targets.openblas-example]
21+
kind = "bin"
22+
main = "src/main.cpp"
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Portable: on Windows the cfg-gated openblas dep + HAVE_OPENBLAS are active and
2+
// this calls cblas_dgemm (asserting [19 22; 43 50]); elsewhere it is a no-op.
3+
#ifdef HAVE_OPENBLAS
4+
#include <cblas.h>
5+
int main() {
6+
const double A[4] = {1, 2, 3, 4};
7+
const double B[4] = {5, 6, 7, 8};
8+
double C[4] = {0, 0, 0, 0};
9+
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 2, 2, 2, 1.0, A, 2, B, 2, 0.0, C, 2);
10+
const double expected[4] = {19, 22, 43, 50};
11+
for (int i = 0; i < 4; ++i) if (C[i] != expected[i]) return 10 + i;
12+
return 0;
13+
}
14+
#else
15+
int main() { return 0; } // openblas is Windows-only; no-op elsewhere
16+
#endif

tests/run_workspace.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env bash
2+
# run_workspace.sh — drive the self-referential mcpp workspace: build + run every
3+
# member declared in the root mcpp.toml [workspace].members. Each member is a
4+
# per-library example/test project that consumes THIS repo's pkgs/ via a local
5+
# [indices] path, so this exercises the real package descriptors through the real
6+
# mcpp pipeline — and dogfoods mcpp's own [workspace]. The member list is the
7+
# single source of truth (no hardcoded duplication).
8+
# Usage: MCPP=/path/to/mcpp tests/run_workspace.sh
9+
set -euo pipefail
10+
11+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
12+
MCPP_BIN="${MCPP:-$(command -v mcpp || true)}"
13+
[ -n "$MCPP_BIN" ] && [ -x "$MCPP_BIN" ] || { echo "FATAL: set MCPP=/path/to/mcpp" >&2; exit 1; }
14+
15+
# Read [workspace].members from the root mcpp.toml (one "path" per line).
16+
members=$(awk '
17+
/^\[workspace\]/ {inws=1; next}
18+
/^\[/ && !/^\[workspace\]/ {inws=0}
19+
inws && /"/ { gsub(/[",[:space:]]/,""); if ($0!="members=[" && $0!="") print $0 }
20+
' "$ROOT/mcpp.toml" | sed 's/members=\[//;s/\]//' | grep -v '^$')
21+
22+
[ -n "$members" ] || { echo "FATAL: no [workspace].members in $ROOT/mcpp.toml" >&2; exit 1; }
23+
echo "workspace members:"; echo "$members" | sed 's/^/ /'
24+
25+
for m in $members; do
26+
dir="$ROOT/$m"
27+
[ -d "$dir" ] || { echo "FATAL: member dir missing: $m" >&2; exit 1; }
28+
echo "==> [$m] mcpp build + run"
29+
( cd "$dir"
30+
rm -rf target .mcpp # hermetic: exercise the index descriptor, not a stale cache
31+
"$MCPP_BIN" build
32+
"$MCPP_BIN" run )
33+
echo "OK: $m"
34+
done
35+
echo "ALL WORKSPACE MEMBERS OK"

0 commit comments

Comments
 (0)