@@ -734,6 +734,54 @@ unfolded_defines_error(const mcpp::manifest::Manifest& m) {
734734 d.size(), d.size() == 1 ? "y" : "ies", d.front());
735735}
736736
737+ // WHAT A MEMBER RECEIVES FROM ITS WORKSPACE WHEN IT IS REACHED AS A DEPENDENCY.
738+ //
739+ // Three parts of the inheritance matter to a dependency: `[workspace.package]`
740+ // (a member may omit `version`), `x.workspace = true` dependency entries
741+ // (without the merge the entry reaches resolution with neither version nor
742+ // path), and `[workspace.build]`. They are applied at the dependency's LOAD
743+ // site, before the conditional merge and the `defines` fold, which is the
744+ // order the root follows; `makePackageRoot` only captures the result (#690).
745+ // `[toolchain]`, `[target.<triple>]` and `[indices]` are decided by the root
746+ // for the whole graph and are not applied to a dependency.
747+ //
748+ // One function for every way a member is reached: a sibling `path`
749+ // dependency, a member of a git-hosted workspace, and a member inside an
750+ // index package's archive. The same commit then compiles the same way in its
751+ // own checkout and in every consumer's graph.
752+ std::optional<std::string>
753+ inherit_as_workspace_member(mcpp::manifest::Manifest& member,
754+ const mcpp::manifest::Manifest& workspace,
755+ const std::filesystem::path& workspaceRoot,
756+ const std::filesystem::path& memberDir) {
757+ mcpp::project::inherit_workspace_package(member, workspace);
758+ mcpp::project::merge_workspace_deps(member, workspace, workspaceRoot);
759+ mcpp::project::inherit_workspace_build(member, workspace, workspaceRoot);
760+ return mcpp::project::workspace_inheritance_error(member, memberDir);
761+ }
762+
763+ // The workspace whose `members` list `memberDir`, searched upward from its
764+ // parent and never above `bound` (an index package's install root: the
765+ // archive is the only tree the package's author wrote).
766+ std::optional<std::pair<mcpp::manifest::Manifest, std::filesystem::path>>
767+ workspace_listing(const std::filesystem::path& memberDir,
768+ const std::filesystem::path& bound) {
769+ auto inside = [&](const std::filesystem::path& p) {
770+ auto rel = p.lexically_normal().lexically_relative(bound.lexically_normal());
771+ return !rel.empty() && *rel.begin() != "..";
772+ };
773+ for (auto p = memberDir.parent_path(); inside(p); p = p.parent_path()) {
774+ if (std::filesystem::exists(p / "mcpp.toml")) {
775+ if (auto ws = mcpp::manifest::load(p / "mcpp.toml");
776+ ws && ws->workspace.present
777+ && mcpp::project::is_workspace_member(*ws, p, memberDir))
778+ return std::pair{std::move(*ws), p};
779+ }
780+ if (p == p.parent_path()) break;
781+ }
782+ return std::nullopt;
783+ }
784+
737785// ── The SECOND conditional pass: predicates that name a target-side layer ────
738786//
739787// #540/#494. `docs/14` documents a package adapting to the C library it was
@@ -2327,7 +2375,7 @@ prepare_build(bool print_fingerprint,
23272375 //
23282376 // A PRELOADED manifest (a host-tool sub-build) is already effective: the
23292377 // resolver loaded it at the dependency's load site, where a member
2330- // inherits (see `inheritAsMember `). It is not inherited a second time; the
2378+ // inherits (see `inherit_as_workspace_member `). It is not inherited a second time; the
23312379 // workspace it belongs to is still recorded below, so that its own sibling
23322380 // dependencies inherit as members.
23332381 std::optional<mcpp::project::EffectiveManifest> effective;
@@ -6199,10 +6247,22 @@ prepare_build(bool print_fingerprint,
61996247 auto loadFrom = [&](const std::filesystem::path& mcppToml)
62006248 -> std::expected<void, std::string>
62016249 {
6202- auto dm = mcpp::manifest::load(mcppToml);
6250+ // A manifest that is a member of a workspace inside the archive
6251+ // receives that workspace's inheritance, as it does from a git
6252+ // clone of the same commit (#690).
6253+ auto repoWorkspace = workspace_listing(mcppToml.parent_path(), verRoot);
6254+ auto dm = mcpp::manifest::load(
6255+ mcppToml, {.insideWorkspace = repoWorkspace.has_value()});
62036256 if (!dm) return std::unexpected(std::format(
62046257 "dependency '{}' (at '{}'): {}",
62056258 depName, mcppToml.string(), dm.error().format()));
6259+ if (repoWorkspace) {
6260+ if (auto bad = inherit_as_workspace_member(
6261+ *dm, repoWorkspace->first, repoWorkspace->second,
6262+ mcppToml.parent_path()))
6263+ return std::unexpected(std::format(
6264+ "dependency '{}': {}", depName, *bad));
6265+ }
62066266 manifest = std::move(*dm);
62076267 effRoot = mcppToml.parent_path();
62086268 return {};
@@ -8279,32 +8339,13 @@ prepare_build(bool print_fingerprint,
82798339 name, dep_root.string(), dm.error().format()));
82808340 }
82818341 dep_manifest = std::move(*dm);
8282- // A MEMBER REACHED AS A DEPENDENCY INHERITS HERE, AT ITS LOAD SITE,
8283- // EXACTLY AS THE ROOT INHERITS AT ITS OWN.
8284- //
8285- // Three parts of what a member receives from its workspace matter
8286- // to a dependency: `[workspace.package]` (a member may omit
8287- // `version`), `x.workspace = true` dependency entries (without the
8288- // merge the entry reaches resolution with no version and no path,
8289- // and is reported as an unreadable index entry), and
8290- // `[workspace.build]`. All three run before the conditional merge
8291- // and the `defines` fold below, which is the order the root
8292- // follows; the snapshot in `makePackageRoot` only captures the
8293- // result (#690). The remaining parts of `inherit_workspace_config`
8294- // (`[toolchain]`, `[target.<triple>]`, `[indices]`) are decided by
8295- // the root for the whole graph and are not applied to a dependency.
8296- //
8297- // A member of a git-hosted workspace inherits from ITS repository,
8298- // anchored at the clone, so that the same commit compiles the same
8299- // way in its own checkout and in a consumer's graph.
8342+ // A member reached as a dependency inherits here, at its load
8343+ // site; see `inherit_as_workspace_member`. A member of a
8344+ // git-hosted workspace inherits from ITS repository, anchored at
8345+ // the clone.
83008346 auto inheritAsMember = [&](const mcpp::manifest::Manifest& ws,
8301- const std::filesystem::path& wsRoot)
8302- -> std::optional<std::string> {
8303- mcpp::project::inherit_workspace_package(*dep_manifest, ws);
8304- mcpp::project::merge_workspace_deps(*dep_manifest, ws, wsRoot);
8305- mcpp::project::inherit_workspace_build(*dep_manifest, ws, wsRoot);
8306- return mcpp::project::workspace_inheritance_error(
8307- *dep_manifest, dep_root);
8347+ const std::filesystem::path& wsRoot) {
8348+ return inherit_as_workspace_member(*dep_manifest, ws, wsRoot, dep_root);
83088349 };
83098350 if (depIsMember) {
83108351 if (auto bad = inheritAsMember(*wsManifest, runtimeWorkspaceRoot))
0 commit comments