Problem
An executable requested through artifacts is a separate program, but mcpp scans its C++ modules together with the consumer's modules and requires module names to be unique across both programs. This makes otherwise independent executables fail to configure when each has a module with the same name.
Observed with mcpp 2026.9.28.2 while changing a GUI's updater dependency from tools = ["Updater"] to artifacts = ["Updater"]. The GUI uses a core package that compiles boost.ixx, and the updater package also compiled that same boost.ixx for its own executable. mcpp build -p GPPGUI --profile fast-release --configure-only failed with:
error: scanner errors:
.../Updater/../3rdParty/3rdModule/boost.ixx: module 'boost' is provided by package 'gpp.core' (.../GalTranslPP/../3rdParty/3rdModule/boost.ixx) and by package 'gpp.updater' (.../Updater/../3rdParty/3rdModule/boost.ixx)
Minimal example
app/
mcpp.toml
src/common.ixx
src/main.cpp
updater/
mcpp.toml
src/common.ixx
src/main.cpp
app/mcpp.toml:
[package]
name = "app"
version = "0.1.0"
[dependencies]
updater = { path = "../updater", artifacts = ["updater"] }
[build]
sources = ["src/common.ixx"]
module_extensions = [".ixx"]
[targets.app]
kind = "bin"
main = "src/main.cpp"
updater/mcpp.toml is the same except for the package and target names, and it has no dependency on app:
[package]
name = "updater"
version = "0.1.0"
[build]
sources = ["src/common.ixx"]
module_extensions = [".ixx"]
[targets.updater]
kind = "bin"
main = "src/main.cpp"
Both common.ixx files can contain export module common; export int value() { return 1; }, and both main.cpp files can contain import common; int main() { return value() - 1; }. The two module units belong to different executables and need not import each other. Run mcpp build --configure-only from app/.
The minimal example is extracted from the observed project case; I have not run this reduced directory separately.
Expected behavior
artifacts should build the updater for the consumer's target and profile, while allowing each independent executable to have its own module provider scope (or otherwise isolating the updater's build). The updater's object/BMI should not be linked into the consumer.
Actual behavior / likely cause
scan_packages() collects units from every package and then calls resolve_graph() once. resolve_graph() keys producerOf only by the logical module name and rejects the second provider, without considering which executable owns it. This rejection is understandable for two providers within one executable's import closure, but it also applies across independent executables brought together by an artifacts edge.
The project worked around the conflict by replacing the updater's single import boost; with a Boost header include and removing its duplicate module source. That avoids this particular error but does not address the scope issue.
Related: #711 introduced target-side executable artifacts.
Problem
An executable requested through
artifactsis a separate program, but mcpp scans its C++ modules together with the consumer's modules and requires module names to be unique across both programs. This makes otherwise independent executables fail to configure when each has a module with the same name.Observed with mcpp 2026.9.28.2 while changing a GUI's updater dependency from
tools = ["Updater"]toartifacts = ["Updater"]. The GUI uses a core package that compilesboost.ixx, and the updater package also compiled that sameboost.ixxfor its own executable.mcpp build -p GPPGUI --profile fast-release --configure-onlyfailed with:Minimal example
app/mcpp.toml:updater/mcpp.tomlis the same except for the package and target names, and it has no dependency onapp:Both
common.ixxfiles can containexport module common; export int value() { return 1; }, and bothmain.cppfiles can containimport common; int main() { return value() - 1; }. The two module units belong to different executables and need not import each other. Runmcpp build --configure-onlyfromapp/.The minimal example is extracted from the observed project case; I have not run this reduced directory separately.
Expected behavior
artifactsshould build the updater for the consumer's target and profile, while allowing each independent executable to have its own module provider scope (or otherwise isolating the updater's build). The updater's object/BMI should not be linked into the consumer.Actual behavior / likely cause
scan_packages()collects units from every package and then callsresolve_graph()once.resolve_graph()keysproducerOfonly by the logical module name and rejects the second provider, without considering which executable owns it. This rejection is understandable for two providers within one executable's import closure, but it also applies across independent executables brought together by anartifactsedge.The project worked around the conflict by replacing the updater's single
import boost;with a Boost header include and removing its duplicate module source. That avoids this particular error but does not address the scope issue.Related: #711 introduced target-side executable artifacts.