2222import com .google .common .collect .ImmutableCollection ;
2323import com .google .common .collect .ImmutableList ;
2424import com .google .common .collect .ImmutableListMultimap ;
25+ import com .google .common .collect .ImmutableMap ;
2526import com .google .common .collect .ImmutableMultimap ;
2627import com .google .common .collect .LinkedListMultimap ;
2728import com .google .common .collect .ListMultimap ;
3435import com .google .devtools .build .lib .actions .SandboxedSpawnStrategy ;
3536import com .google .devtools .build .lib .actions .Spawn ;
3637import com .google .devtools .build .lib .actions .SpawnStrategy ;
38+ import com .google .devtools .build .lib .cmdline .Label ;
3739import com .google .devtools .build .lib .events .Event ;
3840import com .google .devtools .build .lib .events .EventHandler ;
3941import com .google .devtools .build .lib .events .Reporter ;
@@ -69,6 +71,7 @@ public final class SpawnStrategyRegistry
6971
7072 private final ImmutableListMultimap <String , SpawnStrategy > mnemonicToStrategies ;
7173 private final StrategyRegexFilter strategyRegexFilter ;
74+ private final StrategyPlatformFilter strategyPlatformFilter ;
7275 private final ImmutableList <? extends SpawnStrategy > defaultStrategies ;
7376 private final ImmutableMultimap <String , SandboxedSpawnStrategy > mnemonicToRemoteDynamicStrategies ;
7477 private final ImmutableMultimap <String , SandboxedSpawnStrategy > mnemonicToLocalDynamicStrategies ;
@@ -77,18 +80,21 @@ public final class SpawnStrategyRegistry
7780 private SpawnStrategyRegistry (
7881 ImmutableListMultimap <String , SpawnStrategy > mnemonicToStrategies ,
7982 StrategyRegexFilter strategyRegexFilter ,
83+ StrategyPlatformFilter strategyPlatformFilter ,
8084 ImmutableList <? extends SpawnStrategy > defaultStrategies ,
8185 ImmutableMultimap <String , SandboxedSpawnStrategy > mnemonicToRemoteDynamicStrategies ,
8286 ImmutableMultimap <String , SandboxedSpawnStrategy > mnemonicToLocalDynamicStrategies ,
8387 @ Nullable AbstractSpawnStrategy remoteLocalFallbackStrategy ) {
8488 this .mnemonicToStrategies = mnemonicToStrategies ;
8589 this .strategyRegexFilter = strategyRegexFilter ;
90+ this .strategyPlatformFilter = strategyPlatformFilter ;
8691 this .defaultStrategies = defaultStrategies ;
8792 this .mnemonicToRemoteDynamicStrategies = mnemonicToRemoteDynamicStrategies ;
8893 this .mnemonicToLocalDynamicStrategies = mnemonicToLocalDynamicStrategies ;
8994 this .remoteLocalFallbackStrategy = remoteLocalFallbackStrategy ;
9095 logger .atInfo ().log ("Default strategies: %s" , defaultStrategies );
91- logger .atInfo ().log ("Filter strategies: %s" , strategyRegexFilter );
96+ logger .atInfo ().log ("Regex filter strategies: %s" , strategyRegexFilter );
97+ logger .atInfo ().log ("Platform filter strategies: %s" , strategyPlatformFilter );
9298 logger .atInfo ().log ("Mnemonic strategies: %s" , mnemonicToStrategies );
9399 logger .atInfo ().log ("Remote strategies: %s" , mnemonicToRemoteDynamicStrategies );
94100 logger .atInfo ().log ("Local strategies: %s" , mnemonicToLocalDynamicStrategies );
@@ -105,7 +111,8 @@ private SpawnStrategyRegistry(
105111 * using the given {@link Reporter}.
106112 */
107113 public List <? extends SpawnStrategy > getStrategies (Spawn spawn , @ Nullable EventHandler reporter ) {
108- return getStrategies (spawn .getResourceOwner (), spawn .getMnemonic (), reporter );
114+ return strategyPlatformFilter .getStrategies (
115+ spawn , getStrategies (spawn .getResourceOwner (), spawn .getMnemonic (), reporter ));
109116 }
110117
111118 /**
@@ -116,6 +123,8 @@ public List<? extends SpawnStrategy> getStrategies(Spawn spawn, @Nullable EventH
116123 *
117124 * <p>If the reason for selecting the context is worth mentioning to the user, logs a message
118125 * using the given {@link Reporter}.
126+ *
127+ * NOTE: This method is public for Blaze, `getStrategies(Spawn, EventHandler)` must not be used in Bazel.
119128 */
120129 public List <? extends SpawnStrategy > getStrategies (
121130 ActionExecutionMetadata resourceOwner , String mnemonic , @ Nullable EventHandler reporter ) {
@@ -154,18 +163,22 @@ public ImmutableCollection<SandboxedSpawnStrategy> getDynamicSpawnActionContexts
154163 ? mnemonicToRemoteDynamicStrategies
155164 : mnemonicToLocalDynamicStrategies ;
156165 if (mnemonicToDynamicStrategies .containsKey (spawn .getMnemonic ())) {
157- return mnemonicToDynamicStrategies .get (spawn .getMnemonic ());
166+ return strategyPlatformFilter . getStrategies ( spawn , mnemonicToDynamicStrategies .get (spawn .getMnemonic () ));
158167 }
159168 if (mnemonicToDynamicStrategies .containsKey ("" )) {
160- return mnemonicToDynamicStrategies .get ("" );
169+ return strategyPlatformFilter . getStrategies ( spawn , mnemonicToDynamicStrategies .get ("" ) );
161170 }
162171 return ImmutableList .of ();
163172 }
164173
165174 @ Nullable
166175 @ Override
167- public AbstractSpawnStrategy getRemoteLocalFallbackStrategy () {
168- return remoteLocalFallbackStrategy ;
176+ public AbstractSpawnStrategy getRemoteLocalFallbackStrategy (Spawn spawn ) {
177+ var strategies = strategyPlatformFilter .getStrategies (spawn , Lists .newArrayList (remoteLocalFallbackStrategy ));
178+ if (strategies .isEmpty ()) {
179+ return null ;
180+ }
181+ return strategies .getFirst ();
169182 }
170183
171184 /**
@@ -203,9 +216,16 @@ void logSpawnStrategies() {
203216 strategyRegexFilter .getFilterToStrategies ().asMap ().entrySet ()) {
204217 Collection <SpawnStrategy > value = entry .getValue ();
205218 logger .atInfo ().log (
206- "FilterToStrategyImplementations : \" %s\" = [%s]" ,
219+ "FilterDescriptionToStrategyImplementations : \" %s\" = [%s]" ,
207220 entry .getKey (), toImplementationNames (value ));
208221 }
222+ for (Map .Entry <Label , Collection <SpawnStrategy >> entry :
223+ strategyPlatformFilter .getFilterToStrategies ().asMap ().entrySet ()) {
224+ Collection <SpawnStrategy > value = entry .getValue ();
225+ logger .atInfo ().log (
226+ "FilterPlatformToStrategyImplementations: \" %s\" = [%s]" ,
227+ entry .getKey ().getCanonicalForm (), toImplementationNames (value ));
228+ }
209229
210230 logger .atInfo ().log (
211231 "DefaultStrategyImplementations: [%s]" , toImplementationNames (defaultStrategies ));
@@ -287,6 +307,7 @@ public static final class Builder {
287307 private final HashMap <String , List <String >> mnemonicToRemoteDynamicIdentifiers =
288308 new HashMap <>();
289309 private final HashMap <String , List <String >> mnemonicToLocalDynamicIdentifiers = new HashMap <>();
310+ private final HashMap <Label , List <String >> execPlatformFilters = new HashMap <>();
290311
291312 @ Nullable private String remoteLocalFallbackStrategyIdentifier ;
292313
@@ -314,6 +335,12 @@ public Builder addDescriptionFilter(RegexFilter filter, List<String> identifiers
314335 return this ;
315336 }
316337
338+ @ CanIgnoreReturnValue
339+ public Builder addExecPlatformFilter (Label execPlatform , List <String > identifiers ) {
340+ this .execPlatformFilters .put (execPlatform , identifiers );
341+ return this ;
342+ }
343+
317344 /**
318345 * Adds a filter limiting any spawn whose {@linkplain Spawn#getMnemonic() mnemonic}
319346 * (case-sensitively) matches the given mnemonic to only use strategies with the given
@@ -444,6 +471,14 @@ public SpawnStrategyRegistry build() throws AbruptExitException {
444471 }
445472 }
446473
474+ ImmutableListMultimap .Builder <Label , SpawnStrategy > platformToStrategies = ImmutableListMultimap .builder ();
475+ for (Map .Entry <Label , List <String >> entry : execPlatformFilters .entrySet ()) {
476+ Label platform = entry .getKey ();
477+ platformToStrategies .putAll (
478+ platform ,
479+ strategyMapper .toStrategies (entry .getValue (), "platform " + platform .getCanonicalForm ()));
480+ }
481+
447482 ImmutableListMultimap .Builder <String , SpawnStrategy > mnemonicToStrategies =
448483 new ImmutableListMultimap .Builder <>();
449484 for (Map .Entry <String , List <String >> entry : mnemonicToIdentifiers .entrySet ()) {
@@ -514,6 +549,7 @@ public SpawnStrategyRegistry build() throws AbruptExitException {
514549 mnemonicToStrategies .build (),
515550 new StrategyRegexFilter (
516551 strategyMapper , strategyPolicy , filterToIdentifiers , filterToStrategies ),
552+ new StrategyPlatformFilter (strategyMapper , platformToStrategies .build ()),
517553 defaultStrategies ,
518554 mnemonicToRemoteStrategies .build (),
519555 mnemonicToLocalStrategies .build (),
@@ -595,6 +631,64 @@ public String toString() {
595631 }
596632 }
597633
634+ private static class StrategyPlatformFilter {
635+ private final StrategyMapper strategyMapper ;
636+ private final ImmutableListMultimap <Label , SpawnStrategy > platformToStrategies ;
637+
638+ private StrategyPlatformFilter (
639+ StrategyMapper strategyMapper ,
640+ ImmutableListMultimap <Label , SpawnStrategy > platformToStrategies ) {
641+ this .strategyMapper = strategyMapper ;
642+ this .platformToStrategies = platformToStrategies ;
643+ }
644+
645+ /**
646+ * Gets strategies for the given spawn that are allowed by the execution platform.
647+ * @param spawn Spawn to pick strategies for. Must have an execution platform.
648+ * @param candidateStrategies Strategies ordered by priority to pick from. The contents of this
649+ * list vary depending on the context but are always the result of
650+ * an initial strategy selection pass.
651+ * e.g. {@link SpawnStrategyRegistry#getStrategies(Spawn, EventHandler)},
652+ * {@link SpawnStrategyRegistry#getDynamicSpawnActionContexts(Spawn, DynamicMode)}
653+ * and {@link SpawnStrategyRegistry#getRemoteLocalFallbackStrategy(Spawn)}.
654+ * @return A subset of {@code candidateStrategies} that are allowed by the spawn's execution
655+ * platform or all if no restrictions are in place.
656+ * Order from {@code candidateStrategies} is preserved.
657+ */
658+ public <T extends SpawnStrategy > List <T > getStrategies (
659+ Spawn spawn , List <T > candidateStrategies ) {
660+ var platformLabel = spawn .getExecutionPlatformLabel ();
661+ Preconditions .checkNotNull (platformLabel , "Attempting to spawn action without an execution platform." );
662+
663+ if (platformToStrategies .containsKey (platformLabel )) {
664+ var allowedStrategies = platformToStrategies .get (platformLabel );
665+ List <T > filteredStrategies = new ArrayList <>();
666+ for (var strategy : candidateStrategies ) {
667+ if (allowedStrategies .contains (strategy )) {
668+ filteredStrategies .add (strategy );
669+ }
670+ }
671+ return filteredStrategies ;
672+ }
673+
674+ return candidateStrategies ;
675+ }
676+
677+ public <T extends SpawnStrategy > ImmutableCollection <T > getStrategies (
678+ Spawn spawn , ImmutableCollection <T > candidateStrategies ) {
679+ return ImmutableList .copyOf (getStrategies (spawn , Lists .newCopyOnWriteArrayList (candidateStrategies )));
680+ }
681+
682+ ImmutableListMultimap <Label , SpawnStrategy > getFilterToStrategies () {
683+ return platformToStrategies ;
684+ }
685+
686+ @ Override
687+ public String toString () {
688+ return platformToStrategies .toString ();
689+ }
690+ }
691+
598692 /* Maps the strategy identifier (e.g. "local", "worker"..) to the real strategy. */
599693 private static class StrategyMapper {
600694
0 commit comments