From e9e9f57295b49ccaffd7cfa196d42df354e7bf78 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Sun, 11 Oct 2026 12:35:29 +0900 Subject: [PATCH 1/2] A plugin hands a dependency on with reexport = true on its [dependencies] edge (#797) The build programs' graph compiles a plugin's [dependencies] with it, and the import check offered `reexport = true` as the way to make such a dependency importable by the plugin's consumers' build programs -- but read reexport only on [build-dependencies] edges, so the advice did not work. A [dependencies] edge with reexport = true hands the library on; without it the import stays refused (e2e 910 G). docs/31 states it. --- docs/31-authoring-a-rule-package.md | 8 +++++--- docs/zh/31-authoring-a-rule-package.md | 5 +++-- src/build/prepare/host_graph.cpp | 11 ++++++++++- ...uild_plugin_is_a_package_of_the_build_program.sh | 13 ++++++++++++- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/docs/31-authoring-a-rule-package.md b/docs/31-authoring-a-rule-package.md index 8b28c388..646513fd 100644 --- a/docs/31-authoring-a-rule-package.md +++ b/docs/31-authoring-a-rule-package.md @@ -178,9 +178,11 @@ the build-program configuration only. Three consequences follow for its author. - **What the rule's library imports is in its `[dependencies]`.** A rule built on another rule (`mcpp:plugins`' `plugins-core`, say) or on an ordinary library (a TOML parser) names it there. When a consumer's build program uses - the rule, those dependencies are compiled for the build program with it. The - rule's `[build-dependencies]` serve the rule package's own `build.mcpp`, as - any package's do. + the rule, those dependencies are compiled for the build program with it. A + dependency the consumers' build programs import themselves is handed on with + `reexport = true` on its entry; without it the import is refused, naming the + key. The rule's `[build-dependencies]` serve the rule package's own + `build.mcpp`, as any package's do. - **The package builds and tests as the root of a build.** `mcpp build` in the package compiles its library for this machine, with the engine interface, and `mcpp test` runs its tests under `tests/` the same way: a test may import the diff --git a/docs/zh/31-authoring-a-rule-package.md b/docs/zh/31-authoring-a-rule-package.md index 0334c52e..f6a673e5 100644 --- a/docs/zh/31-authoring-a-rule-package.md +++ b/docs/zh/31-authoring-a-rule-package.md @@ -163,8 +163,9 @@ bool compile(options opt = {}); - **规则的库所导入的东西写在它的 `[dependencies]` 中。** 基于另一条规则(例如 `mcpp:plugins` 的 `plugins-core`)或普通库(例如 TOML 解析器)的规则在那里写出它。消费方的 - 构建程序使用这条规则时,这些依赖与它一起为构建程序编译。规则的 `[build-dependencies]` - 服务规则包自己的 `build.mcpp`,与任何包相同。 + 构建程序使用这条规则时,这些依赖与它一起为构建程序编译。消费方的构建程序自己也导入的依赖, + 在其条目上写 `reexport = true` 交出;不写时该导入被拒绝,诊断写出这个键。规则的 + `[build-dependencies]` 服务规则包自己的 `build.mcpp`,与任何包相同。 - **规则包可以作为构建的根构建与测试。** 在包中执行 `mcpp build` 会为本机编译它的库并带上 引擎接口,`mcpp test` 以同样的方式运行 `tests/` 下的测试:测试可以导入规则的模块与 `mcpp`, 在没有消费方工程的情况下检查规则的逻辑。 diff --git a/src/build/prepare/host_graph.cpp b/src/build/prepare/host_graph.cpp index 44e8e375..7c1599bd 100644 --- a/src/build/prepare/host_graph.cpp +++ b/src/build/prepare/host_graph.cpp @@ -307,7 +307,16 @@ std::expected check_program_imports(PrepareState& state, std: } const auto wants = mcpp::pm::imported_module_names(text); if (wants.empty()) return {}; - const auto libs = program_libraries(state, i); + auto libs = program_libraries(state, i); + // What a library hands on: a dependency on a `reexport = true` edge of its + // own `[dependencies]` is importable by the programs that use the library, + // as a `[build-dependencies]` entry it re-exports is (provisions). + for (std::size_t k = 0; k < libs.size(); ++k) + for (auto const& e : state.dependencyEdges) + if (e.consumerPackageIndex == libs[k] && !e.buildTable && e.reexport + && e.dependencyPackageIndex < state.packages.size() + && std::ranges::find(libs, e.dependencyPackageIndex) == libs.end()) + libs.push_back(e.dependencyPackageIndex); std::set available{"std", "std.compat", "mcpp", "mcpp.core"}; std::map providerOf; for (auto d : libs) diff --git a/tests/e2e/910_a_build_plugin_is_a_package_of_the_build_program.sh b/tests/e2e/910_a_build_plugin_is_a_package_of_the_build_program.sh index 452add2b..7fbd6170 100755 --- a/tests/e2e/910_a_build_plugin_is_a_package_of_the_build_program.sh +++ b/tests/e2e/910_a_build_plugin_is_a_package_of_the_build_program.sh @@ -11,7 +11,10 @@ # D P1: a plugin reached through [dependencies] of a target is refused, # naming the chain and [build-dependencies]; # E P4: a program target importing mcpp is refused; -# F P5: `mcpp pack` refuses a plugin. +# F P5: `mcpp pack` refuses a plugin; +# G a library a plugin depends on is importable by the plugin's consumers' +# build programs when the plugin hands it on (`reexport = true` on its +# [dependencies] edge), and refused, naming `reexport`, when it does not. set -e TMP=$(mktemp -d) trap 'rm -rf "$TMP"' EXIT @@ -95,4 +98,12 @@ grep -q "exists only in a build" e.log || fail "E: the refusal does not say why" cd "$TMP/rule" "$MCPP" pack > f.log 2>&1 && fail "F: mcpp pack accepted a plugin" f.log grep -q "is a build plugin" f.log || fail "F: the refusal does not say why" f.log +# G +cd "$TMP/app" +printf 'import acme.rule;\nimport acme.util;\nint main() { if (util_base() == 40) acme::rule::go(); }\n' > build.mcpp +"$MCPP" build > g1.log 2>&1 && fail "G: a library the plugin does not hand on was importable" g1.log +grep -q "reexport = true" g1.log || fail "G: the refusal does not name reexport" g1.log +sed -i 's|util = { path = "../util" }|util = { path = "../util", reexport = true }|' "$TMP/rule/mcpp.toml" +"$MCPP" build > g2.log 2>&1 || fail "G: a library the plugin hands on was refused" g2.log +[ "$("$MCPP" run 2>/dev/null | grep '^V=' | tail -1)" = "V=42" ] || fail "G: value" g2.log echo "PASS: 910_a_build_plugin_is_a_package_of_the_build_program" From f5d47cf7cc0bb6bb5e64cc7b3df772dcc0638f21 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Sun, 11 Oct 2026 12:36:02 +0900 Subject: [PATCH 2/2] CHANGELOG: a plugin hands on a dependency with reexport = true --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3802d5e7..dd156950 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,8 @@ directory as `engineModules`. - Read a unit that imports `mcpp.core` as a build-plugin unit (SPEC-007 §10). A plugin's `[dependencies]` are compiled with it for the build program, so a - plugin may build on plugins and libraries; `mcpp build` and `mcpp test` in a + plugin may build on plugins and libraries, and one it marks `reexport = true` + is importable by its consumers' build programs; `mcpp build` and `mcpp test` in a plugin plan it in the build-program configuration. A plugin reached through a target's `[dependencies]`, a program target that imports `mcpp`, `--target` for another machine on a plugin, and `mcpp pack` of a plugin are refused,