Skip to content

Commit 9b940b2

Browse files
committed
2026.9.29.2: a workspace plan reads what a member states about itself from the member
The plan's root is a virtual root that holds the values shared by the whole graph. Five statements a member makes about itself were read from that root in 2026.9.29.1 and were therefore empty; the mcpp-index sweep of 2026.9.29.1 found the first two, and an inventory of every read of the root's manifest found the rest (design document section 17.1). - A member's relative [indices].path is anchored at the member when the member is loaded; it was resolved against the workspace root (e2e 120). - The std module is built when a selected member's target entries import std, so a member whose only sources are its tests is tested (e2e 836 M1). - Each selected member's [resources] and windows_code_page are compiled against the member's directory and include directories, under res/<member>/, and embedded into the member's images only (e2e 837). A quoted include or resource file in a script is found through the include directories, as the resource compiler finds it. - [build] linkage is a root-position value. - A member's unknown mcpp: capability is refused, and its cfg() and schema warnings are reported, as a root's (e2e 836 M2, M3).
1 parent e7b3218 commit 9b940b2

20 files changed

Lines changed: 910 additions & 411 deletions

‎.agents/docs/2026-09-29-workspace-build-graph-design.md‎

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,8 +435,10 @@ it.
435435

436436
**Configurations.** The selected members are grouped by their effective
437437
root-position values: the toolchain request, `[package] standard`,
438-
`dialect_cxxflags`, `cxx_runtime`, `dependency_linkage`, the target and the
439-
profile, each after workspace inheritance and the command line. Each group is
438+
`dialect_cxxflags`, `cxx_runtime`, `linkage`, `dependency_linkage`, the target,
439+
the profile and the indices, each after workspace inheritance and the command
440+
line. A relative path a member writes (an `[indices].path`) is anchored at the
441+
member before the values are compared. Each group is
440442
one plan with one virtual root, whose root-position values are the group's.
441443
Two groups that resolve to one directory (two spellings of one toolchain) are
442444
built one after the other in it.
@@ -538,3 +540,30 @@ Readings with the implementation (Linux, llvm 22.1.8):
538540
mcpp's own repository, a rooted workspace, builds itself with the new engine
539541
(full build, gcc 16.1.0, 115 s), and its module tests run through member plans
540542
(`mcpp test -p graph`, `-p versioning`).
543+
544+
### 17.1 What a member states about itself (2026.9.29.2)
545+
546+
The inventory of §15 classified the sites that treat `packages[0]` as the
547+
root. It did not cover the sites that read the root's manifest (`state.m`,
548+
`plan.manifest`) for a value the package owns; under a virtual root those read
549+
an empty value. The mcpp-index sweep of 2026.9.29.1 found two of them, and an
550+
inventory of every such read (about 170 sites) found the rest. Each is now
551+
read from the member, or made a value of the plan:
552+
553+
| Statement | 2026.9.29.1 | Now | Criterion |
554+
|---|---|---|---|
555+
| a member's relative `[indices].path` | resolved against the workspace root | anchored at the member when the member is loaded; two spellings of one tree are one configuration | e2e 120, unit `IndexPathsAreAnchoredWhereTheyWereWritten` |
556+
| `import std` in a member's test entries | not seen (the root's targets only) | the entries of the root and of every selected member | e2e 836 M1 |
557+
| a member's `[resources]`, `windows_code_page` | the virtual root's (empty) | per member: its directory and include directories, `res/<member>/`, its images only | e2e 837 |
558+
| `[build] linkage` | not copied to the virtual root | a root-position value | unit `RootPositionValuesSeparateMembers` |
559+
| unknown `mcpp:` capability, cfg() and schema warnings | not reported for a member | refused and reported for each selected member | e2e 836 M2, M3 |
560+
561+
Each criterion fails on 2026.9.29.1 and passes on 2026.9.29.2. The resource
562+
case also showed a defect of every build: a quoted `#include` in a script was
563+
tracked beside the script although the resource compiler finds it through the
564+
include directories, so a script that relied on them could not be built. The
565+
scan now resolves as the compiler does (unit
566+
`InputsAreResolvedThroughTheIncludeDirectories`).
567+
568+
The rule the inventory missed is general: a virtual root answers for the plan,
569+
and a read of the root's manifest is correct only for a value of the plan.

‎CHANGELOG.md‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,38 @@
44
> Each `## [<version>]` section is that release's notes. Entries are written in English
55
> from 2026.9.28.3 on; earlier entries remain as written.
66

7+
## [2026.9.29.2] - 2026-09-29
8+
9+
This release corrects what a workspace plan reads from its members. The plan's
10+
root is a virtual root that holds the values shared by the whole graph; five
11+
statements that a member makes about itself were read from that root in
12+
2026.9.29.1 and were therefore empty. The mcpp-index sweep of 2026.9.29.1 found
13+
the first two.
14+
15+
### Fixed
16+
17+
- **A member's own relative `[indices].path` is anchored at the member.** It
18+
was resolved against the workspace root, so a member that declares its own
19+
index (47 members of mcpp-index do) found no package. Two members that name
20+
one tree with different relative paths are now one configuration (e2e 120).
21+
- **A member whose tests import `std` is tested with the std module built.**
22+
The decision read the entry files of the root's targets only, and the virtual
23+
root has none; a member whose only sources are its tests failed to compile
24+
them (e2e 836).
25+
- **A member's `[resources]` and `windows_code_page` reach its own images.**
26+
Each selected member's resources are compiled against the member's directory
27+
and include directories, under `res/<member>/`, and embedded into the
28+
member's programs and shared libraries only (e2e 837). A quoted `#include`
29+
or resource file in a script is now found through the include directories as
30+
the resource compiler finds it, in every build.
31+
- **`[build] linkage` is a value of the plan.** It chooses the C runtime that
32+
every object is compiled against, so members that differ in it are separate
33+
configurations, and the plan takes it from its members.
34+
- **A member's manifest is checked as a root's.** An unknown capability under
35+
the reserved `mcpp:` prefix is refused, and a cfg() predicate mcpp cannot
36+
evaluate and the other schema warnings are reported, for each selected
37+
member (e2e 836).
38+
739
## [2026.9.29.1] - 2026-09-29
840

941
This release builds a workspace as one graph per configuration. The selected

‎docs/07-workspace.md‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -421,9 +421,11 @@ member that several members use is compiled once.
421421

422422
- **Configurations.** Members are built in one graph when they share their
423423
toolchain request, target, C++ standard, `dialect_cxxflags`, C++ runtime,
424-
profile and the other `[build]` values that apply to a whole graph. Members
425-
that differ in one of them are built in separate graphs, at the same time,
426-
sharing the command's jobs.
424+
`linkage`, profile, indices and the other `[build]` values that apply to a
425+
whole graph. Members that differ in one of them are built in separate
426+
graphs, at the same time, sharing the command's jobs. A relative path a
427+
member writes, such as its own `[indices]` path, is read from the member's
428+
directory.
427429
- **Selection.** `--workspace`, and a virtual root without `-p`, select every
428430
member. `-p X`, and a command run in X's directory, plan X and what X
429431
reaches. The two share the build directory, so `mcpp build --workspace`
@@ -436,6 +438,9 @@ member that several members use is compiled once.
436438
declares it, and is refused when no selected member declares it.
437439
- **Hooks.** The `[hooks]` of every selected member run around the build, in
438440
member order.
441+
- **Resources.** A member's `[resources]` and `windows_code_page` are
442+
compiled against the member's directory and include directories and embedded
443+
into that member's programs and shared libraries only (2026.9.29.2+).
439444
- **No-op builds.** A command repeated with nothing changed is answered by one
440445
check per configuration, without planning.
441446
- **Module names.** Members built in one graph share one module namespace:

‎docs/zh/07-workspace.md‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -392,9 +392,10 @@ mcpp test --workspace --workspace-timeout 1800 # whole fan-out (default 0 = no
392392
对工作空间的命令把成员放在一起规划:被选中的成员及其全部依赖构成一张构建图,只有一个
393393
`build.ninja`,被多个成员使用的成员只编译一次。
394394

395-
- **配置。** 工具链请求、目标、C++ 标准、`dialect_cxxflags`、C++ 运行时、配置档,以及其他
396-
作用于整张图的 `[build]` 值都相同的成员在同一张图中构建。其中任一项不同的成员在各自的图中
397-
构建,这些图同时进行,共享命令的并行任务数。
395+
- **配置。** 工具链请求、目标、C++ 标准、`dialect_cxxflags`、C++ 运行时、`linkage`、配置档、
396+
索引,以及其他作用于整张图的 `[build]` 值都相同的成员在同一张图中构建。其中任一项不同的成员
397+
在各自的图中构建,这些图同时进行,共享命令的并行任务数。成员写下的相对路径(例如它自己的
398+
`[indices]` 路径)按该成员的目录解析。
398399
- **选择。** `--workspace`,以及虚拟工作空间根下不带 `-p` 的命令,选中全体成员;`-p X` 与在
399400
X 的目录中执行的命令规划 X 及其所依赖的一切。两者共用构建目录,因此先执行
400401
`mcpp build --workspace` 再执行 `mcpp build -p X` 不编译任何内容;只有当某个包在两条命令中
@@ -404,6 +405,8 @@ mcpp test --workspace --workspace-timeout 1800 # whole fan-out (default 0 = no
404405
- **Feature。** `--features f` 在每个声明了 `f` 的被选成员中启用它;没有被选成员声明它时,
405406
命令被拒绝。
406407
- **Hooks。** 每个被选成员的 `[hooks]` 按成员顺序在构建前后运行。
408+
- **资源。** 成员的 `[resources]` 与 `windows_code_page` 按该成员的目录与 include 目录编译,
409+
只嵌入该成员自己的程序与共享库(2026.9.29.2+)。
407410
- **无事可做的构建。** 在没有任何改动时重复执行的命令,每个配置只做一次检查,不重新规划。
408411
- **模块名。** 在同一张图中构建的成员共享一个模块命名空间:两个成员各自提供同名模块时,不能在
409412
同一条 `--workspace` 命令中构建;分别用 `-p` 构建。

‎mcpp.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mcpp"
3-
version = "2026.9.29.1"
3+
version = "2026.9.29.2"
44
description = "Modern C++ build & package management tool"
55
license = "Apache-2.0"
66
authors = ["mcpp-community"]

‎modules/versioning/src/version.cppm‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,6 @@ import std;
3131

3232
export namespace mcpp {
3333

34-
inline constexpr std::string_view MCPP_VERSION = "2026.9.29.1";
34+
inline constexpr std::string_view MCPP_VERSION = "2026.9.29.2";
3535

3636
} // namespace mcpp

‎src/build/ninja_backend.cppm‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2751,6 +2751,11 @@ std::string emit_ninja_string(const BuildPlan& plan, std::string* placements) {
27512751
escape_ninja_path(ru.output),
27522752
escape_ninja_path(ru.source),
27532753
implicit.empty() ? std::string{} : " |" + implicit));
2754+
if (!ru.flags.empty() && ru.flags != plan.rcFlags) {
2755+
std::string rcf;
2756+
for (auto const& f : ru.flags) { rcf += ' '; rcf += shell_quote_arg(f); }
2757+
append(std::format(" rcflags ={}\n", rcf));
2758+
}
27542759
}
27552760
if (!plan.resourceUnits.empty()) append("\n");
27562761

‎src/build/plan.cppm‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,13 @@ struct ResourceUnit {
176176
// (verified against llvm-rc 22.1.8: /I, /D, no dependency output), so these
177177
// come from a text scan plus `[resources].extra-inputs`.
178178
std::vector<std::filesystem::path> implicitInputs;
179+
// The directories the script's `#include`s are searched in: its package's
180+
// directory and include_dirs. A workspace plan compiles the resources of
181+
// several members, each against its own package.
182+
std::vector<std::filesystem::path> includeDirs;
183+
// The resource compiler's flags for this script. `BuildPlan::rcFlags` are
184+
// the first unit's; a unit whose flags differ states its own.
185+
std::vector<std::string> flags;
179186
};
180187

181188
struct RuntimeCapabilityProvider {

‎src/build/prepare/config.cpp‎

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -794,7 +794,8 @@ bool is_std_module(std::string_view name) {
794794

795795
bool graph_or_targets_import_std(const mcpp::modgraph::Graph& graph,
796796
const mcpp::manifest::Manifest& manifest,
797-
const std::filesystem::path& projectRoot) {
797+
const std::filesystem::path& projectRoot,
798+
const std::vector<mcpp::modgraph::PackageRoot>& packages) {
798799
for (auto& u : graph.units) {
799800
for (auto& req : u.requires_) {
800801
if (is_std_module(req.logicalName))
@@ -804,16 +805,26 @@ bool graph_or_targets_import_std(const mcpp::modgraph::Graph& graph,
804805

805806
// Some target entry files can be added to the plan after the package scan.
806807
// Check them here so std BMI setup matches what make_plan will compile: they
807-
// are read by the same scan_entry_file make_plan reads them with.
808-
const auto extTable = mcpp::extension_table_for(manifest.buildConfig.moduleExtensions,
809-
manifest.buildConfig.deviceExtensions);
810-
for (auto& t : manifest.targets) {
811-
if (t.main.empty()) continue;
812-
const auto entry = mcpp::modgraph::scan_entry_file(projectRoot / t.main,
813-
manifest.package.name, extTable);
814-
for (auto const& req : entry.requires_)
815-
if (is_std_module(req.logicalName)) return true;
816-
}
808+
// are read by the same scan_entry_file make_plan reads them with. The
809+
// packages whose targets make_plan compiles are the root and, in a
810+
// workspace plan, every selected member (a member whose only sources are
811+
// its tests is the case the root alone misses).
812+
auto targets_import_std = [](const mcpp::manifest::Manifest& m,
813+
const std::filesystem::path& root) {
814+
const auto extTable = mcpp::extension_table_for(m.buildConfig.moduleExtensions,
815+
m.buildConfig.deviceExtensions);
816+
for (auto& t : m.targets) {
817+
if (t.main.empty()) continue;
818+
const auto entry = mcpp::modgraph::scan_entry_file(root / t.main,
819+
m.package.name, extTable);
820+
for (auto const& req : entry.requires_)
821+
if (is_std_module(req.logicalName)) return true;
822+
}
823+
return false;
824+
};
825+
if (targets_import_std(manifest, projectRoot)) return true;
826+
for (auto const& pkg : packages)
827+
if (pkg.selectedMember && targets_import_std(pkg.manifest, pkg.root)) return true;
817828
return false;
818829
}
819830

0 commit comments

Comments
 (0)