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
25 changes: 25 additions & 0 deletions .agents/docs/2026-06-29-mcpp-native-workspace-ci-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,28 @@ windows-runtime-dll(0.0.73)那次"mcpp 补能力 → recipe/CI 变简单"完全

> 关联:本仓 `.github/workflows/validate.yml`(detect 拆分 + 下载缓存,2026-06-29 已落);mcpp 仓
> `.agents/docs/2026-06-29-windows-runtime-dll-deployment-and-openblas.md`(per-OS 能力如何让 recipe 变简单的先例)。

---

## 8. 实现状态(2026-06-30 落地)

自参照 workspace 的**首个可签入切片**已落地(对应 §6 的 P0 + P1 起步,依赖 mcpp 0.0.77 的 L1
`[target.'cfg(...)']` per-OS 依赖/flags + windows-runtime-dll 部署):

- **`mcpp.toml`(仓根)**:虚拟 `[workspace]`,`members` 列出 `tests/examples/{cjson,eigen,
nlohmann.json,openblas}`——即真实索引的逐库示例本身就是 workspace 成员。仓 = 索引 ⊕ 消费索引的
workspace(双视角)。
- **`tests/run_workspace.sh`**:薄驱动。从仓根 `mcpp.toml` 的 `[workspace].members` 解析成员(单一
事实源,无硬编码),逐个 `mcpp build && mcpp run`;每个成员经本地 `[indices]` path 解析**本仓
pkgs/** 的真实 recipe。
- **`tests/examples/openblas/`**:L1-cfg 消费者示范。`[target.'cfg(windows)'.dependencies.compat]
openblas` + `[target.'cfg(windows)'.build] cxxflags=["-DHAVE_OPENBLAS=1"]`;Windows 走 `cblas_dgemm`
断言 `[19 22; 43 50]`,linux/macos 为 no-op(openblas 不被拉取)。证明 per-OS 依赖按**解析目标
三元组**门控。
- **`validate.yml`**:`detect` 增 `workspace` 输出(push/schedule 全跑;PR 命中仓根 `mcpp.toml`、
`tests/run_workspace.sh`、或任一 `tests/examples/*` 成员时触发);新增 `smoke-workspace`(ubuntu)
跑驱动。`MCPP_VERSION` 升至 0.0.77。

**尚未收敛**(留待 §6 P2/P3):per-OS 成员差异仍可由 cfg 表承担而非驱动;环境隔离/缓存/产物断言仍在
各 smoke 脚本;`--workspace` 单命令(G1)仍是 mcpp 侧待加能力——当前驱动按成员循环。旧 smoke 脚本
**保留**,与 workspace 并行覆盖,等价验证充分后再逐个迁移/删除。
69 changes: 58 additions & 11 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ on:
workflow_dispatch:

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

jobs:
lint:
Expand Down Expand Up @@ -105,6 +105,7 @@ jobs:
full: ${{ steps.f.outputs.full }}
full_linux: ${{ steps.f.outputs.full_linux }}
full_portable: ${{ steps.f.outputs.full_portable }}
workspace: ${{ steps.f.outputs.workspace }}
steps:
- uses: actions/checkout@v4
with:
Expand All @@ -123,11 +124,12 @@ jobs:
# coverage is ever silently dropped).
full_linux=false
full_portable=false
workspace=false
examples='[]'
event="${{ github.event_name }}"
if [ "$event" != "pull_request" ]; then
# push to main / nightly schedule / manual dispatch → full regression
full_linux=true; full_portable=true
full_linux=true; full_portable=true; workspace=true
else
git fetch -q origin "${{ github.base_ref }}"
changed=$(git diff --name-only "origin/${{ github.base_ref }}...HEAD")
Expand All @@ -138,16 +140,22 @@ jobs:
case "$file" in
# Common harness / the workflow itself → both platforms.
tests/run_example.sh|tests/check_mirror_urls.lua|tests/list_cn_urls.lua|.github/workflows/validate.yml)
full_linux=true; full_portable=true ;;
full_linux=true; full_portable=true; workspace=true ;;
# The self-referential workspace: its declaration (root mcpp.toml)
# and its driver. A member change under tests/examples/* also feeds
# the workspace job (handled below, with the examples fast path).
mcpp.toml|tests/run_workspace.sh)
workspace=true ;;
# Portable suite script runs only in smoke-portable (win+mac).
tests/smoke_compat_portable.sh)
full_portable=true ;;
# Linux-only smoke scripts run only in smoke-full-linux.
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)
full_linux=true ;;
# Example projects are handled by the smoke-examples fast path.
# Example projects are handled by the smoke-examples fast path;
# they are also workspace members, so re-run the workspace driver.
tests/examples/*)
: ;;
workspace=true ;;
# Any other tests/ change (new helper / new smoke script) → both (safe).
tests/*)
full_linux=true; full_portable=true ;;
Expand All @@ -171,8 +179,9 @@ jobs:
echo "full=$full" >> "$GITHUB_OUTPUT"
echo "full_linux=$full_linux" >> "$GITHUB_OUTPUT"
echo "full_portable=$full_portable" >> "$GITHUB_OUTPUT"
echo "workspace=$workspace" >> "$GITHUB_OUTPUT"
echo "examples=$examples" >> "$GITHUB_OUTPUT"
echo "=> full_linux=$full_linux full_portable=$full_portable examples=$examples"
echo "=> full_linux=$full_linux full_portable=$full_portable workspace=$workspace examples=$examples"

# ── Fast path: per-changed-package example projects (Linux) ───────────
smoke-examples:
Expand Down Expand Up @@ -210,6 +219,44 @@ jobs:
"$MCPP" --version
timeout 1800 bash tests/run_example.sh "${{ matrix.pkg }}"

# ── Self-referential workspace: mcpp-index AS a mcpp [workspace] ──────
# Dogfoods mcpp's own native [workspace]: the root mcpp.toml declares every
# per-library example as a workspace member, and the driver builds+runs each
# through the real pkgs/ index (local [indices] path). This is the dual
# perspective — the repo is both the package index and a consumer of it. The
# openblas member additionally exercises L1 platform-conditional deps/flags
# (no-op here on linux; the Windows cblas path is covered by smoke-portable).
smoke-workspace:
needs: detect
if: needs.detect.outputs.workspace == '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"
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: Build + run every workspace member
env:
MCPP_INDEX_MIRROR: GLOBAL
run: |
"$MCPP" --version
timeout 1800 bash tests/run_workspace.sh

# ── Full regression (legacy whole-suite smoke) ────────────────────────
smoke-full-linux:
needs: detect
Expand Down Expand Up @@ -279,14 +326,14 @@ jobs:
include:
- platform: macos
os: macos-15
archive: mcpp-0.0.73-macosx-arm64.tar.gz
root: mcpp-0.0.73-macosx-arm64
archive: mcpp-0.0.77-macosx-arm64.tar.gz
root: mcpp-0.0.77-macosx-arm64
mcpp: bin/mcpp
xlings: registry/bin/xlings
- platform: windows
os: windows-latest
archive: mcpp-0.0.73-windows-x86_64.zip
root: mcpp-0.0.73-windows-x86_64
archive: mcpp-0.0.77-windows-x86_64.zip
root: mcpp-0.0.77-windows-x86_64
mcpp: bin/mcpp.exe
xlings: registry/bin/xlings.exe
steps:
Expand Down
12 changes: 12 additions & 0 deletions mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# mcpp-index is simultaneously the package index (pkgs/) AND a mcpp workspace that
# tests it: a virtual workspace whose members are the per-library example/test
# projects under tests/examples/, each consuming this repo's own pkgs/ via a local
# [indices] path. "Eat our own dog food." See
# .agents/docs/2026-06-29-mcpp-native-workspace-ci-design.md.
[workspace]
members = [
"tests/examples/cjson",
"tests/examples/eigen",
"tests/examples/nlohmann.json",
"tests/examples/openblas",
]
22 changes: 22 additions & 0 deletions tests/examples/openblas/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# L1-cfg consumer: openblas is a Windows-only runtime dependency (import lib +
# DLL); on linux/macos this member is a trivial no-op binary. Demonstrates mcpp's
# platform-conditional [target.'cfg(...)'] dependencies + flags (mcpp >= 0.0.75),
# with the windows-runtime-DLL deployment (mcpp >= 0.0.73). Self-referential:
# the openblas recipe is resolved from THIS repo's pkgs/ via the local index.
[package]
name = "openblas-example"
version = "0.1.0"

[indices]
compat = { path = "../../.." }

# Pull OpenBLAS only when building for Windows; define HAVE_OPENBLAS so the source
# compiles the cblas path there and a no-op everywhere else.
[target.'cfg(windows)'.dependencies.compat]
openblas = "0.3.33"
[target.'cfg(windows)'.build]
cxxflags = ["-DHAVE_OPENBLAS=1"]

[targets.openblas-example]
kind = "bin"
main = "src/main.cpp"
16 changes: 16 additions & 0 deletions tests/examples/openblas/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Portable: on Windows the cfg-gated openblas dep + HAVE_OPENBLAS are active and
// this calls cblas_dgemm (asserting [19 22; 43 50]); elsewhere it is a no-op.
#ifdef HAVE_OPENBLAS
#include <cblas.h>
int main() {
const double A[4] = {1, 2, 3, 4};
const double B[4] = {5, 6, 7, 8};
double C[4] = {0, 0, 0, 0};
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 2, 2, 2, 1.0, A, 2, B, 2, 0.0, C, 2);
const double expected[4] = {19, 22, 43, 50};
for (int i = 0; i < 4; ++i) if (C[i] != expected[i]) return 10 + i;
return 0;
}
#else
int main() { return 0; } // openblas is Windows-only; no-op elsewhere
#endif
35 changes: 35 additions & 0 deletions tests/run_workspace.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# run_workspace.sh — drive the self-referential mcpp workspace: build + run every
# member declared in the root mcpp.toml [workspace].members. Each member is a
# per-library example/test project that consumes THIS repo's pkgs/ via a local
# [indices] path, so this exercises the real package descriptors through the real
# mcpp pipeline — and dogfoods mcpp's own [workspace]. The member list is the
# single source of truth (no hardcoded duplication).
# Usage: MCPP=/path/to/mcpp tests/run_workspace.sh
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
MCPP_BIN="${MCPP:-$(command -v mcpp || true)}"
[ -n "$MCPP_BIN" ] && [ -x "$MCPP_BIN" ] || { echo "FATAL: set MCPP=/path/to/mcpp" >&2; exit 1; }

# Read [workspace].members from the root mcpp.toml (one "path" per line).
members=$(awk '
/^\[workspace\]/ {inws=1; next}
/^\[/ && !/^\[workspace\]/ {inws=0}
inws && /"/ { gsub(/[",[:space:]]/,""); if ($0!="members=[" && $0!="") print $0 }
' "$ROOT/mcpp.toml" | sed 's/members=\[//;s/\]//' | grep -v '^$')

[ -n "$members" ] || { echo "FATAL: no [workspace].members in $ROOT/mcpp.toml" >&2; exit 1; }
echo "workspace members:"; echo "$members" | sed 's/^/ /'

for m in $members; do
dir="$ROOT/$m"
[ -d "$dir" ] || { echo "FATAL: member dir missing: $m" >&2; exit 1; }
echo "==> [$m] mcpp build + run"
( cd "$dir"
rm -rf target .mcpp # hermetic: exercise the index descriptor, not a stale cache
"$MCPP_BIN" build
"$MCPP_BIN" run )
echo "OK: $m"
done
echo "ALL WORKSPACE MEMBERS OK"
Loading