@@ -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