@@ -2479,32 +2479,47 @@ prepare_build(bool print_fingerprint,
24792479 }
24802480 }
24812481 // Feature-gated sources (e.g. gtest's gtest_main.cc behind "main"):
2482- // drop EVERY feature-listed glob from the default build, then re- add
2483- // only the ones whose feature is active. Runs even when no feature is
2484- // active, so a gated source is excluded by default.
2482+ // drop EVERY feature-listed glob from the default build, then add
2483+ // back only the ones whose feature is active. Runs even when no
2484+ // feature is active, so a gated source is excluded by default.
24852485 //
2486- // ONLY in build mode (!includeDevDeps). `mcpp test` (includeDevDeps)
2487- // keeps the full surface so the dev-dependency track's per-test main
2488- // detection (run_tests / make_plan) still sees gtest_main.cc and
2489- // prunes it per test — the two tracks stay decoupled. Combined with
2490- // the descriptor keeping gtest_main.cc in base `sources` too, this
2491- // means test mode is unaffected.
2486+ // The DROP is build-mode only (!includeDevDeps). `mcpp test`
2487+ // (includeDevDeps) keeps the full surface so the dev-dependency
2488+ // track's per-test main detection (run_tests / make_plan) still sees
2489+ // gtest_main.cc and prunes it per test — the two tracks stay
2490+ // decoupled; gtest's descriptor keeps gtest_main.cc in base `sources`
2491+ // too, so skipping the drop leaves it visible.
2492+ //
2493+ // The ADD runs in BOTH modes. A descriptor may list a glob ONLY under
2494+ // `features` and never in base `sources` (xpkg's `features.X.sources`
2495+ // lands in featureSources alone — compat.spdlog's `compiled`,
2496+ // compat.cjson's `utils`, compat.eigen's `eigen_blas`). Gating the add
2497+ // on !includeDevDeps meant those sources were never compiled under
2498+ // `mcpp test` → link-time `undefined reference` (the eigen_blas
2499+ // `dgemm_` failure, long misread as a linking follow-up: it was
2500+ // source-set resolution, not linking). Add is dedup'd so gtest's
2501+ // doubly-listed gtest_main.cc cannot land twice.
24922502 auto& bc = pkg.manifest.buildConfig;
2493- if (!includeDevDeps && !bc.featureSources.empty()) {
2494- std::set<std::string> gated;
2495- for (auto& [f, globs] : bc.featureSources)
2496- for (auto& g : globs) gated.insert(g);
2497- auto drop = [&](std::vector<std::string>& v) {
2498- std::erase_if(v, [&](const std::string& s) { return gated.contains(s); });
2499- };
2500- drop(bc.sources);
2501- drop(pkg.manifest.modules.sources);
2503+ if (!bc.featureSources.empty()) {
2504+ if (!includeDevDeps) {
2505+ std::set<std::string> gated;
2506+ for (auto& [f, globs] : bc.featureSources)
2507+ for (auto& g : globs) gated.insert(g);
2508+ auto drop = [&](std::vector<std::string>& v) {
2509+ std::erase_if(v, [&](const std::string& s) { return gated.contains(s); });
2510+ };
2511+ drop(bc.sources);
2512+ drop(pkg.manifest.modules.sources);
2513+ }
25022514 std::set<std::string> activeSet(active.begin(), active.end());
2515+ auto add = [](std::vector<std::string>& v, const std::string& g) {
2516+ if (std::ranges::find(v, g) == v.end()) v.push_back(g);
2517+ };
25032518 for (auto& [f, globs] : bc.featureSources) {
25042519 if (!activeSet.contains(f)) continue;
25052520 for (auto& g : globs) {
2506- bc.sources.push_back( g);
2507- pkg.manifest.modules.sources.push_back( g);
2521+ add( bc.sources, g);
2522+ add( pkg.manifest.modules.sources, g);
25082523 }
25092524 }
25102525 }
0 commit comments