Reproduction
A workspace member declares a shared-library target and an executable target over the same implementation:
repro/
mcpp.toml
player/
mcpp.toml
player.cppm
impl.cpp
main.cpp
Root mcpp.toml:
[workspace]
members = ["player"]
[workspace.package]
version = "0.1.0"
standard = "c++23"
[toolchain]
windows = "llvm@22.1.8"
player/mcpp.toml:
[package]
namespace = "probe"
name = "player"
[build]
sources = ["player.cppm", "impl.cpp"]
[targets.player_dll]
kind = "shared"
[targets.player]
kind = "bin"
main = "main.cpp"
player/player.cppm:
export module probe.player;
export int answer();
player/impl.cpp:
module probe.player;
int answer() { return 42; }
player/main.cpp:
#include <cstdio>
import probe.player;
int main() { std::printf("%d\n", answer()); }
Run mcpp build --workspace or mcpp build -p probe.player from the root.
Expected behavior
The executable links the member's implementation objects, as the documented compile-once model describes. It remains usable without its sibling player_dll.dll; a bin target should not acquire a runtime dependency on a sibling shared target simply because they belong to the same package.
Other packages consuming probe.player should continue to link its shared-library target. Static dependencies needed by the member's executable must also be included in that executable's own static closure.
Actual behavior
The shared library links, but the executable link omits the member's implementation objects and fails:
lld-link: error: undefined symbol: int __cdecl answer(void)
>>> referenced by player/main.cpp:3
>>> obj/probe_player/main.o:(main)
clang++: error: linker command failed with exit code 1
The same failure was reproduced with a static module dependency under the producer and a second workspace member consuming the DLL. The executable response file contains its entry object, but not the producer's implementation objects.
Code path
src/build/plan.cppm records every package with a shared target in sharedDepPackages. The workspace-member executable loop then skips all compile units whose package is in that set, including its own package. The exclusion is appropriate for external consumers, but removes the executable's own implementation.
Adding the sibling DLL import library would hide the missing objects while changing the executable's semantics. The fix should preserve a self-contained executable and retain shared linkage for external consumers.
Environment
- Windows x64.
- mcpp 2026.10.3.1; current
main also contains the same exclusion.
- LLVM 22.1.8, MSVC sysroot 14.51.36231, Windows SDK 10.0.26100.0.
- Default development profile; no LTO or export annotations are needed to reproduce this failure.
Reproduction
A workspace member declares a shared-library target and an executable target over the same implementation:
Root
mcpp.toml:player/mcpp.toml:player/player.cppm:player/impl.cpp:player/main.cpp:Run
mcpp build --workspaceormcpp build -p probe.playerfrom the root.Expected behavior
The executable links the member's implementation objects, as the documented compile-once model describes. It remains usable without its sibling
player_dll.dll; abintarget should not acquire a runtime dependency on a siblingsharedtarget simply because they belong to the same package.Other packages consuming
probe.playershould continue to link its shared-library target. Static dependencies needed by the member's executable must also be included in that executable's own static closure.Actual behavior
The shared library links, but the executable link omits the member's implementation objects and fails:
The same failure was reproduced with a static module dependency under the producer and a second workspace member consuming the DLL. The executable response file contains its entry object, but not the producer's implementation objects.
Code path
src/build/plan.cppmrecords every package with a shared target insharedDepPackages. The workspace-member executable loop then skips all compile units whose package is in that set, including its own package. The exclusion is appropriate for external consumers, but removes the executable's own implementation.Adding the sibling DLL import library would hide the missing objects while changing the executable's semantics. The fix should preserve a self-contained executable and retain shared linkage for external consumers.
Environment
mainalso contains the same exclusion.