@@ -9,6 +9,7 @@ import mcpp.modgraph.scanner;
99import mcpp.platform;
1010import mcpp.platform.axis;
1111import mcpp.platform.runtime_binding;
12+ import mcpp.platform.runtime_search;
1213import mcpp.platform.xlings.subos_info;
1314
1415namespace build = mcpp::build;
@@ -277,6 +278,8 @@ TEST(RuntimeContract, XlingsSelectedFactsPrecedeDescriptorFallbacks) {
277278 });
278279
279280 build::merge_runtime_binding_contract (plan, binding);
281+ ASSERT_EQ (plan.runtimeSearch .size (), 0u )
282+ << " a binding with no SubOS library view contributes no farm entry" ;
280283 ASSERT_EQ (plan.runtimeProviders .size (), 2u );
281284 EXPECT_EQ (plan.runtimeProviders .front ().provider .canonical (),
282285 " xim.selected@4.0.0" );
@@ -287,3 +290,74 @@ TEST(RuntimeContract, XlingsSelectedFactsPrecedeDescriptorFallbacks) {
287290}
288291
289292} // namespace
293+
294+ // ── the farm's two guards, as a matrix ─────────────────────────────────────
295+ //
296+ // The farm belongs to THIS host's SubOS, so it may only reach an artifact that
297+ // will run here under this runtime. The guards are asserted directly because
298+ // each one costs a full cross toolchain to exercise end to end, and the failure
299+ // they prevent is silent: a path that is inert at best, and points at another
300+ // architecture's or another C library's objects at worst.
301+ namespace {
302+
303+ build::BuildPlan plan_for (std::string targetTriple) {
304+ build::BuildPlan plan;
305+ plan.toolchain .targetTriple = std::move (targetTriple);
306+ return plan;
307+ }
308+
309+ mcpp::platform::runtime::RuntimeBinding host_binding_with_farm () {
310+ mcpp::platform::runtime::RuntimeBinding b;
311+ b.platform = " linux" ;
312+ b.arch = " x86_64" ;
313+ b.declared = true ;
314+ b.runtimeId = " glibc@2.39" ;
315+ b.searchDirs = {" /home/u/.mcpp/registry/subos/default/lib" };
316+ return b;
317+ }
318+
319+ std::size_t farm_entries (const std::vector<mcpp::platform::search::Dir>& dirs) {
320+ return static_cast <std::size_t >(std::ranges::count_if (dirs, [](auto const & d) {
321+ return d.origin == mcpp::platform::search::Origin::SubosFarm;
322+ }));
323+ }
324+
325+ } // namespace
326+
327+ TEST (RuntimeSearchClosure, HostTargetGetsTheFarm) {
328+ EXPECT_EQ (farm_entries (build::runtime_search_closure (
329+ plan_for (" " ), host_binding_with_farm ())), 1u );
330+ EXPECT_EQ (farm_entries (build::runtime_search_closure (
331+ plan_for (" x86_64-linux-gnu" ), host_binding_with_farm ())), 1u );
332+ }
333+
334+ TEST (RuntimeSearchClosure, CrossTargetGetsNoFarm) {
335+ // Another architecture: the farm holds x86_64 objects.
336+ EXPECT_EQ (farm_entries (build::runtime_search_closure (
337+ plan_for (" aarch64-linux-gnu" ), host_binding_with_farm ())), 0u );
338+ // Another C library: a glibc farm on a musl program's search path is the
339+ // payload mixing rule B exists to prevent.
340+ EXPECT_EQ (farm_entries (build::runtime_search_closure (
341+ plan_for (" x86_64-linux-musl" ), host_binding_with_farm ())), 0u );
342+ // Another format: DT_RPATH does not exist there at all.
343+ EXPECT_EQ (farm_entries (build::runtime_search_closure (
344+ plan_for (" x86_64-windows-gnu" ), host_binding_with_farm ())), 0u );
345+ EXPECT_EQ (farm_entries (build::runtime_search_closure (
346+ plan_for (" aarch64-macos" ), host_binding_with_farm ())), 0u );
347+ }
348+
349+ // A MISMATCH must be proven, not assumed. An undeclared SubOS has no runtime
350+ // identity, and refusing its own library view on that basis would be exactly
351+ // the absence-read-as-contradiction this release removes elsewhere. Caught by
352+ // e2e 220, which builds against a SubOS it creates and which therefore has no
353+ // `subos_info` to declare anything.
354+ TEST (RuntimeSearchClosure, UndeclaredBindingStillGetsItsOwnFarm) {
355+ auto b = host_binding_with_farm ();
356+ b.declared = false ;
357+ b.runtimeId .clear ();
358+ EXPECT_EQ (farm_entries (build::runtime_search_closure (plan_for (" " ), b)), 1u );
359+ // …but the architecture guard still applies: unknown libc is not a licence
360+ // to ignore everything else.
361+ EXPECT_EQ (farm_entries (build::runtime_search_closure (
362+ plan_for (" aarch64-linux-gnu" ), b)), 0u );
363+ }
0 commit comments