From 5271c90286fa0fc80fda695bb399516bb8c6b4b4 Mon Sep 17 00:00:00 2001 From: Akaps316 <48785708+Akaps316@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:47:28 +0530 Subject: fix(c++/modules): avoid leaking private dep flags to rebuilt BMIs Only inherit interface-visible defines/undefines/includedirs when collecting module fileconfigs from dependency targets. This fixes consumer-side BMI rebuilds under build.c++.modules.reuse.strict picking up private producer flags. Add a regression test for strict module reuse with a shared dependency. --- .../src/include/public_config.h | 3 ++ .../reuse_strict_private_defines/src/main.cpp | 5 ++++ .../reuse_strict_private_defines/src/mod.cpp | 7 +++++ .../reuse_strict_private_defines/src/mod.mpp | 3 ++ .../modules/reuse_strict_private_defines/test.lua | 34 ++++++++++++++++++++++ .../modules/reuse_strict_private_defines/xmake.lua | 16 ++++++++++ xmake/rules/c++/modules/scanner.lua | 6 ++-- 7 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 tests/projects/c++/modules/reuse_strict_private_defines/src/include/public_config.h create mode 100644 tests/projects/c++/modules/reuse_strict_private_defines/src/main.cpp create mode 100644 tests/projects/c++/modules/reuse_strict_private_defines/src/mod.cpp create mode 100644 tests/projects/c++/modules/reuse_strict_private_defines/src/mod.mpp create mode 100644 tests/projects/c++/modules/reuse_strict_private_defines/test.lua create mode 100644 tests/projects/c++/modules/reuse_strict_private_defines/xmake.lua diff --git a/tests/projects/c++/modules/reuse_strict_private_defines/src/include/public_config.h b/tests/projects/c++/modules/reuse_strict_private_defines/src/include/public_config.h new file mode 100644 index 000000000..3ceb5dcd5 --- /dev/null +++ b/tests/projects/c++/modules/reuse_strict_private_defines/src/include/public_config.h @@ -0,0 +1,3 @@ +#pragma once + +#define PUBLIC_CONFIG_VALUE 0 diff --git a/tests/projects/c++/modules/reuse_strict_private_defines/src/main.cpp b/tests/projects/c++/modules/reuse_strict_private_defines/src/main.cpp new file mode 100644 index 000000000..5b477f2d6 --- /dev/null +++ b/tests/projects/c++/modules/reuse_strict_private_defines/src/main.cpp @@ -0,0 +1,5 @@ +import mod; + +int main() { + return foo(); +} diff --git a/tests/projects/c++/modules/reuse_strict_private_defines/src/mod.cpp b/tests/projects/c++/modules/reuse_strict_private_defines/src/mod.cpp new file mode 100644 index 000000000..478993dfb --- /dev/null +++ b/tests/projects/c++/modules/reuse_strict_private_defines/src/mod.cpp @@ -0,0 +1,7 @@ +#include + +module mod; + +int foo() { + return PUBLIC_CONFIG_VALUE; +} diff --git a/tests/projects/c++/modules/reuse_strict_private_defines/src/mod.mpp b/tests/projects/c++/modules/reuse_strict_private_defines/src/mod.mpp new file mode 100644 index 000000000..b55b834a7 --- /dev/null +++ b/tests/projects/c++/modules/reuse_strict_private_defines/src/mod.mpp @@ -0,0 +1,3 @@ +export module mod; + +export int foo(); diff --git a/tests/projects/c++/modules/reuse_strict_private_defines/test.lua b/tests/projects/c++/modules/reuse_strict_private_defines/test.lua new file mode 100644 index 000000000..bd9b241cd --- /dev/null +++ b/tests/projects/c++/modules/reuse_strict_private_defines/test.lua @@ -0,0 +1,34 @@ +inherit(".test_base") + +local PRIVATE_DEFINE = "PRIVATE_DEP_DEFINE_DO_NOT_PROPAGATE" +local PUBLIC_SYSINCLUDEDIR = path.translate(path.absolute("src/include")) + +function _build() + local outdata = os.iorun("xmake -r -vD") + local leaked = false + local missing_sysincludedir = true + for line in outdata:gmatch("[^\r\n]+") do + if line:find("Consumer", 1, true) then + if line:find(PRIVATE_DEFINE, 1, true) then + leaked = true + end + if line:find(PUBLIC_SYSINCLUDEDIR, 1, true) then + missing_sysincludedir = false + end + end + end + if leaked then + raise("Private dependency defines leaked into Consumer module rebuilds under reuse.strict\n%s", outdata) + end + if missing_sysincludedir then + raise("Missing public sysincludedir in Consumer module rebuilds under reuse.strict\n%s", outdata) + end + os.run("xmake -vD") +end + +function main(_) + local clang_options = {compiler = "clang", version = CLANG_MIN_VER, build = _build} + local gcc_options = {compiler = "gcc", version = GCC_MIN_VER, build = _build} + local msvc_options = {version = MSVC_MIN_VER, build = _build} + run_tests(clang_options, gcc_options, msvc_options) +end diff --git a/tests/projects/c++/modules/reuse_strict_private_defines/xmake.lua b/tests/projects/c++/modules/reuse_strict_private_defines/xmake.lua new file mode 100644 index 000000000..aba5cc895 --- /dev/null +++ b/tests/projects/c++/modules/reuse_strict_private_defines/xmake.lua @@ -0,0 +1,16 @@ +add_rules("mode.debug", "mode.release") + +set_languages("cxx20") + +target("Producer") + set_kind("shared") + add_defines("PRIVATE_DEP_DEFINE_DO_NOT_PROPAGATE") + add_sysincludedirs("src/include", {public = true}) + add_files("src/mod.mpp", {public = true}) + add_files("src/mod.cpp") + +target("Consumer") + set_kind("binary") + set_policy("build.c++.modules.reuse.strict", true) + add_deps("Producer") + add_files("src/main.cpp") diff --git a/xmake/rules/c++/modules/scanner.lua b/xmake/rules/c++/modules/scanner.lua index 2bcee1f12..400e8a0dd 100644 --- a/xmake/rules/c++/modules/scanner.lua +++ b/xmake/rules/c++/modules/scanner.lua @@ -283,9 +283,9 @@ function _get_targetdeps_modules(target) fileconfig.undefines = _fileconfig.undefines fileconfig.includedirs = _fileconfig.includedirs end - fileconfig.defines = table.join(fileconfig.defines or {}, dep:get("defines") or {}) - fileconfig.undefines = table.join(fileconfig.undefines or {}, dep:get("undefines") or {}) - fileconfig.includedirs = table.join(fileconfig.includedirs or {}, dep:get("includedirs") or {}) + fileconfig.defines = table.join(fileconfig.defines or {}, dep:get("defines", {interface = true}) or {}) + fileconfig.undefines = table.join(fileconfig.undefines or {}, dep:get("undefines", {interface = true}) or {}) + fileconfig.includedirs = table.join(fileconfig.includedirs or {}, dep:get("includedirs", {interface = true}) or {}, dep:get("sysincludedirs", {interface = true}) or {}) if not dep:is_phony() then if target:namespace() == dep:namespace() then fileconfig.from_dep = dep:name() -- cgit v1.3.1