Skip to content

Commit eb5b125

Browse files
authored
feat(profile): default build profile = dev (mainstream), --release opt-in (v0.0.76) (#188)
* feat(profile): default build profile = dev (mainstream); --release opt-in; v0.0.76 Resolves #179. mcpp's old default profile was "release" (inherited from its xmake/xlings lineage), but the dominant convention is the opposite: a bare build is debug/dev and release is opt-in (Cargo/Meson/CMake/Zig/Bazel/MSBuild). Since mcpp's surface is Cargo-flavored, flip the global default to "dev" (-O0 -g); release is opt-in via --release / --profile release. - prepare.cppm: global default profile release -> dev. Precedence: --profile/--release/--dev flag > [build].default-profile > "dev". - cli.cppm/cmd_build.cppm: add --release / --dev shorthands (--release is now the mainstream opt-in flag). - manifest.cppm: [build].default-profile (alias: profile) — a project's own default; its role inverts under the flip to "opt into release" for projects that ship/run optimized. This is the migration mechanism. - mcpp.toml: default-profile = "release" — keeps mcpp's self-host CI build and release.yml at -O2 with ZERO workflow changes (no --release threaded through release pipelines; mcpp's own CI doesn't slow to -O0). - cross-build-test.yml: bump xlings cache key v2->v3 (the failing job was a poisoned ~/.xlings cache from the bootstrap-pin-timing, not code). Tests: 87_build_default_profile.sh (global dev; --release/--dev; project default-profile=release; --dev override), 68_profile_passthrough.sh (updated for the new default). Regression: unit 27/0. Doc: design doc L0.5 (convention table + decision + migration). mcpp self-build verified 'Finished release [optimized]'. * ci+docs: revert cross-build cache key to v2 (warm); document dev default in user docs - cross-build-test.yml: revert xlings cache key v3->v2. The v3 bump forced a COLD ~/.xlings, whose first-time toolchain install 127'd; v2 reuses the warm cache the passing e2e/ci-linux jobs already use (has 0.0.75 + the toolchain). The original 'version 0.0.75 not found' poison is moot now 0.0.75 is fully propagated. The self-host bootstrap is otherwise unrelated to this PR's code (e2e passed; step 6 bootstrap succeeded). - docs/05-mcpp-toml.md (+ zh): document the new convention — bare `mcpp build` uses dev (-O0 -g); release is opt-in via --release/--profile release; --dev shorthand; [build].default-profile per-project default (and the distribution note). * ci(cross-build): xlings 0.4.30 -> 0.4.61 + force index re-sync (fix bootstrap pin resolution) The cross-build aarch64 job failed at the self-host bootstrap with 'xlings: version <pin> not found for mcpp' — the classic first-PR-after-a- bootstrap-pin-bump failure: a warm cached ~/.xlings carried a 'fresh-looking' TTL refresh marker, so 0.4.30's `xlings update` no-op'd and the stale index never saw the just-bumped pin (0.0.75). (Not this PR's code: e2e passed, the build+tests pass locally.) Fix: (1) bump XLINGS_VERSION to 0.4.61 (current; has the index-refresh fixes); (2) delete the .xlings-index-cache.json TTL markers before `xlings update` so it actually re-pulls the latest index while the toolchain payloads stay cached (avoids both the stale-index 'not found' and the cold-cache toolchain 127). * ci(cross-build): keep xlings 0.4.30, fix via index marker-clear only 0.4.61 resolved the pin (index re-sync worked) but 404'd downloading xim:mcpp@0.0.75 — it changed the XLINGS_RES download resolution and no longer matches the xlings-res/mcpp asset layout (0.4.30's resolution works; e2e@0.4.30 downloads 0.0.75 fine). The actual bug was the STALE INDEX (a fresh-looking TTL marker made 0.4.30's `xlings update` no-op). Revert to 0.4.30 and keep the .xlings-index-cache.json marker-clear, so update re-pulls the index (gets the bootstrap pin) while download uses 0.4.30's working path. * ci(cross-build): xlings 0.4.61 (root cause was broken xlings-res assets, now fixed) The real root cause of the cross-build bootstrap failures was NOT the xlings version or the profile code: the xlings-res/mcpp 0.0.75 GitHub release assets were uploaded in a BROKEN state during a flaky network (asset records present but blobs missing -> HTTP 404 on download). cross-build uses --mirror GLOBAL (GitHub), so its 'xlings install mcpp@0.0.75' 404'd; the downstream symptom on 0.4.30 was 'version 0.0.75 not found'. GitCode assets were fine all along. Re-uploaded all 4 GitHub xlings-res/mcpp 0.0.75 assets clean (verified GET 200 + correct sizes). With downloads working, use 0.4.61 (current) + keep the .xlings-index-cache.json marker-clear (forces index re-sync past a stale TTL marker on warm caches).
1 parent 7186ec2 commit eb5b125

12 files changed

Lines changed: 156 additions & 18 deletions

.agents/docs/2026-06-29-manifest-environment-and-platform-design.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,43 @@ expressible, all backed by existing xlings behavior.
9292

9393
---
9494

95+
## L0.5 — Default build profile: follow the mainstream (dev), `--release` opt-in
96+
97+
(Issue #179.) mcpp's old default was `release`; the prevailing convention across
98+
modern build tools is the **opposite** — a bare build is **debug/dev**, release is
99+
opt-in:
100+
101+
| Tool | bare-command default | release via |
102+
|---|---|---|
103+
| Cargo | **dev/debug** (-O0 + debuginfo) | `cargo build --release` |
104+
| Meson | **debug** | `--buildtype=release` |
105+
| CMake | empty `CMAKE_BUILD_TYPE` (≈debug) | `-DCMAKE_BUILD_TYPE=Release` |
106+
| Zig | **Debug** | `-Doptimize=ReleaseFast/Safe/Small` |
107+
| Bazel | **fastbuild** (no opt) | `-c opt` |
108+
| MSBuild/VS | **Debug** | Release configuration |
109+
| **xmake** | **release** | `xmake f -m debug` |
110+
111+
Debug-default dominates (Cargo/Meson/CMake/Zig/Bazel/MSBuild); only the **xmake**
112+
lineage defaults to release — exactly where mcpp's old default came from (mcpp builds
113+
on xlings/xmake). But mcpp's *surface* is Cargo-flavored, so users expect Cargo
114+
semantics. **Decision: flip the global default to `dev` (-O0 -g); release is opt-in
115+
via `--release` / `--profile release`.**
116+
117+
- **Precedence**: `--profile NAME` / `--release` / `--dev` flag > `[build].default-profile`
118+
(project default) > global `dev`. (`prepare.cppm`; `--dev`/`--release` are shorthands
119+
in `cli.cppm`/`cmd_build.cppm`.)
120+
- **`[build].default-profile`** (alias: `profile`) — a project's own default; its role
121+
*inverts* under the flip: it now means **"opt into release"** for projects that ship/
122+
run optimized by default. **mcpp's own `mcpp.toml` sets `default-profile = "release"`**
123+
so the self-host CI build and `release.yml` stay `-O2` with **zero workflow changes**
124+
the knob *is* the migration mechanism (no `--release` threaded through release pipelines,
125+
and mcpp's own CI doesn't slow to `-O0`).
126+
- **Distribution footgun**: a project that defaults to dev passes `--release` to produce
127+
a distributable (a pack-time release guard is a possible follow-up).
128+
- Tests: `tests/e2e/87_build_default_profile.sh`, `68_profile_passthrough.sh` (updated).
129+
130+
---
131+
95132
## L0 — Dependency categories: keep three axes, wire `build`
96133

97134
Keep Cargo/Conan's **normal / dev / build** trichotomy (mcpp already declares all

.github/workflows/cross-build-test.yml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,12 @@ jobs:
8787
- name: Bootstrap mcpp via xlings
8888
env:
8989
XLINGS_NON_INTERACTIVE: '1'
90-
XLINGS_VERSION: '0.4.30'
90+
# 0.4.61 (current). The earlier 0.4.61 "download 404 for mcpp@<pin>" was
91+
# NOT a version bug — the xlings-res/mcpp GitHub release assets were
92+
# uploaded in a broken state (records present, blobs missing → 404 on
93+
# GET); re-uploaded clean. The stale-INDEX half is handled by the
94+
# marker-clear below.
95+
XLINGS_VERSION: '0.4.61'
9196
run: |
9297
tarball="xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz"
9398
curl -fsSL -o "/tmp/${tarball}" \
@@ -96,8 +101,10 @@ jobs:
96101
"/tmp/xlings-${XLINGS_VERSION}-linux-x86_64/subos/default/bin/xlings" self install
97102
export PATH="$HOME/.xlings/subos/default/bin:$PATH"
98103
xlings --version
99-
# Refresh the index so a cached ~/.xlings still sees newly published
100-
# cross toolchains (xim:aarch64-linux-musl-gcc, static ninja, ...).
104+
# Force a real index re-sync even on a warm cache: drop the TTL refresh
105+
# markers so `xlings update` actually pulls the latest index (sees the
106+
# current bootstrap pin) while the toolchain payloads stay cached.
107+
find "$HOME/.xlings" -name '.xlings-index-cache.json' -delete 2>/dev/null || true
101108
xlings config --mirror GLOBAL 2>/dev/null || true
102109
xlings update -y 2>/dev/null || xlings update 2>/dev/null || true
103110
xlings install mcpp -y

docs/05-mcpp-toml.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,16 @@ cxxflags = ["-fno-plt"]
411411
ldflags = []
412412
```
413413

414-
- Selection: `mcpp build --profile <name>` (and `mcpp test --profile <name>`, which builds the
415-
code-under-test plus the test binaries under that profile), defaulting to `release`.
414+
- Selection & default: a bare `mcpp build` uses the **`dev`** profile (`-O0 -g`) — the
415+
mainstream convention (cf. Cargo/Meson/CMake/Zig/Bazel). **Release is opt-in:**
416+
`mcpp build --release` (shorthand) or `--profile release`. `--dev` is the explicit
417+
shorthand for dev. Same applies to `mcpp test --profile <name>` (builds the
418+
code-under-test plus the test binaries under that profile).
419+
- **Per-project default**`[build].default-profile = "<name>"` (alias: `profile`) sets
420+
the project's own default when no flag is passed. The typical use is a tool/library
421+
that should build optimized by default: `[build] default-profile = "release"`. Precedence:
422+
`--profile`/`--release`/`--dev` flag **>** `[build].default-profile` **>** global `dev`.
423+
(A project that defaults to dev should pass `--release` when producing a distributable.)
416424
- Built-in profiles: `release` (-O2) / `dev`, `debug` (-O0 -g) / `dist` (-O3 + strip;
417425
**LTO is not enabled by default**). `[profile.<built-in name>]` can override a
418426
built-in definition wholesale.

docs/zh/05-mcpp-toml.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,14 @@ cxxflags = ["-fno-plt"]
389389
ldflags = []
390390
```
391391

392-
- 选择:`mcpp build --profile <name>`(以及 `mcpp test --profile <name>`,会让被测代码与测试二进制
393-
都在该 profile 下编译),默认 `release`
392+
- 选择与默认:裸 `mcpp build`**`dev`** 档(`-O0 -g`)——主流惯例(参照
393+
Cargo/Meson/CMake/Zig/Bazel)。**release 为 opt-in:** `mcpp build --release`(短写)或
394+
`--profile release`;`--dev` 是 dev 的显式短写。`mcpp test --profile <name>` 同理
395+
(被测代码与测试二进制都在该 profile 下编译)。
396+
- **项目级默认** —— `[build].default-profile = "<name>"`(别名 `profile`)设置该项目在不带
397+
flag 时的默认。典型用途是"以发布优化为常态"的工具/库:`[build] default-profile = "release"`
398+
优先级:`--profile`/`--release`/`--dev` flag **>** `[build].default-profile` **>** 全局 `dev`
399+
(默认 dev 的项目在产出可分发物时应显式 `--release`。)
394400
- 内置档案:`release`(-O2)/ `dev``debug`(-O0 -g)/ `dist`(-O3 + strip;
395401
**不默认开 lto**)。`[profile.<内置名>]` 可整体覆盖内置定义。
396402

mcpp.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
[package]
22
name = "mcpp"
3-
version = "0.0.75"
3+
version = "0.0.76"
44
description = "Modern C++ build & package management tool"
55
license = "Apache-2.0"
66
authors = ["mcpp-community"]
77
repo = "https://github.com/mcpp-community/mcpp"
88

99
[build]
10+
# The global default profile is now "dev" (-O0 -g, mainstream convention);
11+
# mcpp itself is a shipped tool, so opt its plain `mcpp build` (and release.yml's
12+
# self-host build) back into the optimized profile. Without this the released
13+
# binary would be -O0. A `--profile`/`--dev`/`--release` flag still overrides.
14+
default-profile = "release"
1015
# nlohmann/json.hpp lives in src/libs/json/; expose it to the global
1116
# module fragment `#include <json.hpp>` in src/libs/json.cppm.
1217
include_dirs = ["src/libs/json"]

src/build/prepare.cppm

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -554,10 +554,19 @@ prepare_build(bool print_fingerprint,
554554
// 1. project mcpp.toml [toolchain].<platform> or .default
555555
// 2. global ~/.mcpp/config.toml [toolchain].default
556556
// 3. hard error (no system fallback)
557-
// Resolve the build profile: --profile (default "release") → built-in
558-
// defaults, overlaid by any [profile.<name>] from the manifest → buildConfig.
557+
// Resolve the build profile, overlaid by any [profile.<name>] from the
558+
// manifest → buildConfig.
559559
{
560-
std::string pname = overrides.profile.empty() ? "release" : overrides.profile;
560+
// Precedence: --profile / --release / --dev flag (overrides.profile) >
561+
// [build].default-profile (project default) > "dev" (global default).
562+
// The global default is "dev" (-O0 -g) to follow the dominant convention
563+
// (Cargo/Meson/CMake/Zig/Bazel/MSBuild all default to debug); release is
564+
// opt-in via --release / --profile release. A project that wants its
565+
// plain `mcpp build` optimized sets [build].default-profile = "release"
566+
// (mcpp's own mcpp.toml does this, so the released binary stays -O2).
567+
std::string pname = !overrides.profile.empty() ? overrides.profile
568+
: !m->buildConfig.defaultProfile.empty() ? m->buildConfig.defaultProfile
569+
: "dev";
561570
mcpp::manifest::Profile pr;
562571
if (pname == "dev" || pname == "debug") { pr.optLevel = "0"; pr.debug = true; }
563572
else if (pname == "dist") { pr.optLevel = "3"; pr.strip = true; }

src/cli.cppm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ int run(int argc, char** argv) {
226226
.help("Build only the named workspace member"))
227227
.option(cl::Option("profile").takes_value().value_name("NAME")
228228
.help("Build profile: release (default) | dev | dist | <[profile.*] name>"))
229+
.option(cl::Option("release").help("Shorthand for --profile release"))
230+
.option(cl::Option("dev").help("Shorthand for --profile dev (-O0 -g)"))
229231
.option(cl::Option("features").takes_value().value_name("LIST")
230232
.help("Activate root-package features (comma-separated)"))
231233
.option(cl::Option("cap").takes_value().value_name("LIST")

src/cli/cmd_build.cppm

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ export int cmd_build(const mcpplibs::cmdline::ParsedArgs& parsed) {
2626
mcpp::build::BuildOverrides ov;
2727
if (auto t = parsed.value("target")) ov.target_triple = *t;
2828
if (auto p = parsed.value("package")) ov.package_filter = *p;
29+
// Profile selection precedence: --profile NAME > --release / --dev > the
30+
// project default ([build].default-profile) > "release", resolved in
31+
// prepare_build. --release/--dev are shorthands only.
2932
if (auto pr = parsed.value("profile")) ov.profile = *pr;
33+
else if (parsed.is_flag_set("release")) ov.profile = "release";
34+
else if (parsed.is_flag_set("dev")) ov.profile = "dev";
3035
if (auto fs = parsed.value("features")) ov.features = *fs;
3136
if (auto cp = parsed.value("cap")) ov.capabilities = *cp;
3237
ov.strict = parsed.is_flag_set("strict");

src/manifest.cppm

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,14 @@ struct BuildConfig {
148148
bool debug = false; // -g
149149
bool lto = false; // -flto
150150
bool strip = false; // link -s
151+
// `[build].default-profile` (alias: `profile`) — the project's DEFAULT
152+
// profile when no --profile/--dev/--release is passed. The global convention
153+
// default stays "release"; this lets a project opt its plain `mcpp build`
154+
// into e.g. "dev" without typing --profile. Precedence: --profile/--dev/
155+
// --release flag > [build].default-profile > "release". NOTE (distribution
156+
// footgun): a project that defaults to dev should pass `--profile release`
157+
// when producing a distributable (a pack-time release guard is a follow-up).
158+
std::string defaultProfile;
151159
};
152160

153161
// `[runtime]` — requirements needed when launching built binaries.
@@ -1118,6 +1126,8 @@ std::expected<Manifest, ManifestError> parse_string(std::string_view content,
11181126
if (auto v = doc->get_string_array("build.cxxflags")) m.buildConfig.cxxflags = *v;
11191127
if (auto v = doc->get_string_array("build.ldflags")) m.buildConfig.ldflags = *v;
11201128
if (auto v = doc->get_string("build.c_standard")) m.buildConfig.cStandard = *v;
1129+
if (auto v = doc->get_string("build.default-profile")) m.buildConfig.defaultProfile = *v;
1130+
else if (auto v = doc->get_string("build.profile")) m.buildConfig.defaultProfile = *v; // accepted alias
11211131
if (auto v = doc->get_string("build.macos_deployment_target"))
11221132
m.buildConfig.macosDeploymentTarget = *v;
11231133
for (auto const& flag : m.buildConfig.cxxflags) {

src/toolchain/fingerprint.cppm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import mcpp.toolchain.detect;
1818

1919
export namespace mcpp::toolchain {
2020

21-
inline constexpr std::string_view MCPP_VERSION = "0.0.75";
21+
inline constexpr std::string_view MCPP_VERSION = "0.0.76";
2222

2323
struct FingerprintInputs {
2424
Toolchain toolchain;

0 commit comments

Comments
 (0)