From 9247b1dbb1c9ebed7e6768078ba3f0e7f0b0e982 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 30 Jun 2026 03:37:09 +0800 Subject: [PATCH] feat(workspace): self-referential mcpp [workspace] over the index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- ...6-06-29-mcpp-native-workspace-ci-design.md | 25 +++++++ .github/workflows/validate.yml | 69 ++++++++++++++++--- mcpp.toml | 12 ++++ tests/examples/openblas/mcpp.toml | 22 ++++++ tests/examples/openblas/src/main.cpp | 16 +++++ tests/run_workspace.sh | 35 ++++++++++ 6 files changed, 168 insertions(+), 11 deletions(-) create mode 100644 mcpp.toml create mode 100644 tests/examples/openblas/mcpp.toml create mode 100644 tests/examples/openblas/src/main.cpp create mode 100755 tests/run_workspace.sh diff --git a/.agents/docs/2026-06-29-mcpp-native-workspace-ci-design.md b/.agents/docs/2026-06-29-mcpp-native-workspace-ci-design.md index 9b2a43b..19f1f8f 100644 --- a/.agents/docs/2026-06-29-mcpp-native-workspace-ci-design.md +++ b/.agents/docs/2026-06-29-mcpp-native-workspace-ci-design.md @@ -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 并行覆盖,等价验证充分后再逐个迁移/删除。 diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 00625eb..4689926 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -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: @@ -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: @@ -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") @@ -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 ;; @@ -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: @@ -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 @@ -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: diff --git a/mcpp.toml b/mcpp.toml new file mode 100644 index 0000000..3a3fcb3 --- /dev/null +++ b/mcpp.toml @@ -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", +] diff --git a/tests/examples/openblas/mcpp.toml b/tests/examples/openblas/mcpp.toml new file mode 100644 index 0000000..5d5c16f --- /dev/null +++ b/tests/examples/openblas/mcpp.toml @@ -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" diff --git a/tests/examples/openblas/src/main.cpp b/tests/examples/openblas/src/main.cpp new file mode 100644 index 0000000..bdfc5d8 --- /dev/null +++ b/tests/examples/openblas/src/main.cpp @@ -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 +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 diff --git a/tests/run_workspace.sh b/tests/run_workspace.sh new file mode 100755 index 0000000..2fdec94 --- /dev/null +++ b/tests/run_workspace.sh @@ -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"