Move shell executable to its own configuration fragment.
RELNOTES: None.
PiperOrigin-RevId: 191861074
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
index 0e43077..6d6d8a0 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
@@ -1270,7 +1270,7 @@
getPrerequisiteMapForTesting(
eventHandler, configuredTarget, configurations, toolchainContext))
.setConfigConditions(ImmutableMap.<Label, ConfigMatchingProvider>of())
- .setUniversalFragment(ruleClassProvider.getUniversalFragment())
+ .setUniversalFragments(ruleClassProvider.getUniversalFragments())
.setToolchainContext(toolchainContext)
.build();
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java b/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java
index 85dfc7a..f364257 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java
@@ -306,6 +306,7 @@
private PathFragment shellPath(Map<String, String> executionInfo) {
// Use vanilla /bin/bash for actions running on mac machines.
return executionInfo.containsKey(ExecutionRequirements.REQUIRES_DARWIN)
- ? PathFragment.create("/bin/bash") : ruleContext.getConfiguration().getShellExecutable();
+ ? PathFragment.create("/bin/bash")
+ : ruleContext.getConfiguration().getFragment(ShellConfiguration.class).getShellExecutable();
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
index 34836ab..e55b44a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
@@ -225,7 +225,8 @@
private final Digraph<Class<? extends RuleDefinition>> dependencyGraph =
new Digraph<>();
private PatchTransition lipoDataTransition;
- private Class<? extends BuildConfiguration.Fragment> universalFragment;
+ private List<Class<? extends BuildConfiguration.Fragment>> universalFragments =
+ new ArrayList<>();
private PrerequisiteValidator prerequisiteValidator;
private ImmutableMap.Builder<String, Object> skylarkAccessibleTopLevels =
ImmutableMap.builder();
@@ -340,9 +341,9 @@
return this;
}
- public Builder setUniversalConfigurationFragment(
+ public Builder addUniversalConfigurationFragment(
Class<? extends BuildConfiguration.Fragment> fragment) {
- this.universalFragment = fragment;
+ this.universalFragments.add(fragment);
return this;
}
@@ -456,7 +457,7 @@
ImmutableList.copyOf(configurationOptions),
ImmutableList.copyOf(configurationFragmentFactories),
lipoDataTransition,
- universalFragment,
+ ImmutableList.copyOf(universalFragments),
prerequisiteValidator,
skylarkAccessibleTopLevels.build(),
skylarkModules.build(),
@@ -555,10 +556,10 @@
private final PatchTransition lipoDataTransition;
/**
- * A configuration fragment that should be available to all rules even when they don't
+ * Configuration fragments that should be available to all rules even when they don't
* explicitly require it.
*/
- private final Class<? extends BuildConfiguration.Fragment> universalFragment;
+ private final ImmutableList<Class<? extends BuildConfiguration.Fragment>> universalFragments;
private final ImmutableList<BuildInfoFactory> buildInfoFactories;
@@ -583,7 +584,7 @@
ImmutableList<Class<? extends FragmentOptions>> configurationOptions,
ImmutableList<ConfigurationFragmentFactory> configurationFragments,
PatchTransition lipoDataTransition,
- Class<? extends BuildConfiguration.Fragment> universalFragment,
+ ImmutableList<Class<? extends BuildConfiguration.Fragment>> universalFragments,
PrerequisiteValidator prerequisiteValidator,
ImmutableMap<String, Object> skylarkAccessibleJavaClasses,
ImmutableList<Class<?>> skylarkModules,
@@ -600,7 +601,7 @@
this.configurationOptions = configurationOptions;
this.configurationFragmentFactories = configurationFragments;
this.lipoDataTransition = lipoDataTransition;
- this.universalFragment = universalFragment;
+ this.universalFragments = universalFragments;
this.prerequisiteValidator = prerequisiteValidator;
this.globals = createGlobals(skylarkAccessibleJavaClasses, skylarkModules);
this.nativeProviders = nativeProviders;
@@ -685,8 +686,8 @@
* Returns the configuration fragment that should be available to all rules even when they
* don't explicitly require it.
*/
- public Class<? extends BuildConfiguration.Fragment> getUniversalFragment() {
- return universalFragment;
+ public ImmutableList<Class<? extends BuildConfiguration.Fragment>> getUniversalFragments() {
+ return universalFragments;
}
/**
@@ -808,7 +809,7 @@
for (ConfigurationFragmentFactory factory : getConfigurationFragments()) {
fragmentsBuilder.add(factory.creates());
}
- fragmentsBuilder.add(getUniversalFragment());
+ fragmentsBuilder.addAll(getUniversalFragments());
return fragmentsBuilder.build();
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
index 1edf561..7a1d4ec 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
@@ -339,7 +339,7 @@
.setVisibility(convertVisibility(prerequisiteMap, env.getEventHandler(), rule, null))
.setPrerequisites(prerequisiteMap)
.setConfigConditions(configConditions)
- .setUniversalFragment(ruleClassProvider.getUniversalFragment())
+ .setUniversalFragments(ruleClassProvider.getUniversalFragments())
.setToolchainContext(toolchainContext)
.build();
if (ruleContext.hasErrors()) {
@@ -461,7 +461,7 @@
.setPrerequisites(prerequisiteMap)
.setAspectAttributes(aspectAttributes)
.setConfigConditions(configConditions)
- .setUniversalFragment(ruleClassProvider.getUniversalFragment())
+ .setUniversalFragments(ruleClassProvider.getUniversalFragments())
.setToolchainContext(toolchainContext)
.build();
if (ruleContext.hasErrors()) {
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index c8c4775..043bee7 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -184,7 +184,7 @@
private final BuildConfiguration hostConfiguration;
private final PatchTransition disableLipoTransition;
private final ConfigurationFragmentPolicy configurationFragmentPolicy;
- private final Class<? extends BuildConfiguration.Fragment> universalFragment;
+ private final ImmutableList<Class<? extends BuildConfiguration.Fragment>> universalFragments;
private final ErrorReporter reporter;
@Nullable private final ToolchainContext toolchainContext;
@@ -199,7 +199,7 @@
ListMultimap<String, ConfiguredTargetAndData> targetMap,
ListMultimap<String, ConfiguredFilesetEntry> filesetEntryMap,
ImmutableMap<Label, ConfigMatchingProvider> configConditions,
- Class<? extends BuildConfiguration.Fragment> universalFragment,
+ ImmutableList<Class<? extends BuildConfiguration.Fragment>> universalFragments,
String ruleClassNameForLogging,
ImmutableMap<String, Attribute> aspectAttributes,
@Nullable ToolchainContext toolchainContext) {
@@ -214,7 +214,7 @@
.map(a -> a.getDescriptor())
.collect(ImmutableList.toImmutableList());
this.configurationFragmentPolicy = builder.configurationFragmentPolicy;
- this.universalFragment = universalFragment;
+ this.universalFragments = universalFragments;
this.targetMap = targetMap;
this.filesetEntryMap = filesetEntryMap;
this.configConditions = configConditions;
@@ -452,7 +452,7 @@
public <T extends Fragment> boolean isLegalFragment(
Class<T> fragment, ConfigurationTransition transition) {
- return fragment == universalFragment
+ return universalFragments.contains(fragment)
|| fragment == PlatformConfiguration.class
|| configurationFragmentPolicy.isLegalConfigurationFragment(fragment, transition);
}
@@ -1405,7 +1405,7 @@
private final AnalysisEnvironment env;
private final Rule rule;
private final ConfigurationFragmentPolicy configurationFragmentPolicy;
- private Class<? extends BuildConfiguration.Fragment> universalFragment;
+ private ImmutableList<Class<? extends BuildConfiguration.Fragment>> universalFragments;
private final BuildConfiguration configuration;
private final BuildConfiguration hostConfiguration;
private PatchTransition disableLipoTransition;
@@ -1453,7 +1453,7 @@
targetMap,
filesetEntryMap,
configConditions,
- universalFragment,
+ universalFragments,
getRuleClassNameForLogging(),
aspectAttributes != null ? aspectAttributes : ImmutableMap.<String, Attribute>of(),
toolchainContext);
@@ -1498,12 +1498,13 @@
/**
* Sets the fragment that can be legally accessed even when not explicitly declared.
*/
- Builder setUniversalFragment(Class<? extends BuildConfiguration.Fragment> fragment) {
+ Builder setUniversalFragments(
+ ImmutableList<Class<? extends BuildConfiguration.Fragment>> fragments) {
// TODO(bazel-team): Add this directly to ConfigurationFragmentPolicy, so we
// don't need separate logic specifically for checking this fragment. The challenge is
// that we need RuleClassProvider to figure out what this fragment is, and not every
// call state that creates ConfigurationFragmentPolicy has access to that.
- this.universalFragment = fragment;
+ this.universalFragments = fragments;
return this;
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ShellConfiguration.java b/src/main/java/com/google/devtools/build/lib/analysis/ShellConfiguration.java
new file mode 100644
index 0000000..2e4769e
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ShellConfiguration.java
@@ -0,0 +1,213 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.analysis;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration.Fragment;
+import com.google.devtools.build.lib.analysis.config.BuildOptions;
+import com.google.devtools.build.lib.analysis.config.ConfigurationEnvironment;
+import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory;
+import com.google.devtools.build.lib.analysis.config.FragmentOptions;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
+import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
+import com.google.devtools.build.lib.util.OS;
+import com.google.devtools.build.lib.util.OptionsUtils.PathFragmentConverter;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.common.options.Option;
+import com.google.devtools.common.options.OptionDocumentationCategory;
+import com.google.devtools.common.options.OptionEffectTag;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
+import java.util.Map;
+import javax.annotation.Nullable;
+
+/** A configuration fragment that tells where the shell is. */
+public class ShellConfiguration extends BuildConfiguration.Fragment {
+
+ /**
+ * A codec for {@link ShellConfiguration}.
+ *
+ * <p>It does not handle the Bazel version, but that's fine, because we don't want to serialize
+ * anything in Bazel.
+ *
+ * <p>We cannot use {@code AutoCodec} because the field {@link #actionEnvironment} is a lambda.
+ * That does not necessarily need to be the case, but it's there in support for
+ * {@link BuildConfiguration.Fragment#setupActionEnvironment()}, which is slated to be removed.
+ */
+ public static final class Codec implements ObjectCodec<ShellConfiguration> {
+ @Override
+ public Class<? extends ShellConfiguration> getEncodedClass() {
+ return ShellConfiguration.class;
+ }
+
+ @Override
+ public void serialize(SerializationContext context, ShellConfiguration obj,
+ CodedOutputStream codedOut) throws SerializationException, IOException {
+ context.serialize(obj.shellExecutable, codedOut);
+ }
+
+ @Override
+ public ShellConfiguration deserialize(DeserializationContext context, CodedInputStream codedIn)
+ throws SerializationException, IOException {
+ PathFragment shellExecutable = context.deserialize(codedIn);
+ return new ShellConfiguration(shellExecutable, NO_ACTION_ENV.fromOptions(null));
+ }
+ }
+
+ private static final ImmutableMap<OS, PathFragment> OS_SPECIFIC_SHELL =
+ ImmutableMap.<OS, PathFragment>builder()
+ .put(OS.WINDOWS, PathFragment.create("c:/tools/msys64/usr/bin/bash.exe"))
+ .put(OS.FREEBSD, PathFragment.create("/usr/local/bin/bash"))
+ .build();
+
+ private final PathFragment shellExecutable;
+ private final ShellActionEnvironment actionEnvironment;
+
+ public ShellConfiguration(PathFragment shellExecutable,
+ ShellActionEnvironment actionEnvironment) {
+ this.shellExecutable = shellExecutable;
+ this.actionEnvironment = actionEnvironment;
+ }
+
+ public PathFragment getShellExecutable() {
+ return shellExecutable;
+ }
+
+ @Override
+ public void setupActionEnvironment(Map<String, String> builder) {
+ actionEnvironment.setupActionEnvironment(this, builder);
+ }
+
+ /** An option that tells Bazel where the shell is. */
+ @AutoCodec(strategy = AutoCodec.Strategy.PUBLIC_FIELDS)
+ public static class Options extends FragmentOptions {
+ @Option(
+ name = "shell_executable",
+ converter = PathFragmentConverter.class,
+ defaultValue = "null",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
+ help =
+ "Absolute path to the shell executable for Bazel to use. If this is unset, but the "
+ + "BAZEL_SH environment variable is set on the first Bazel invocation (that starts "
+ + "up a Bazel server), Bazel uses that. If neither is set, Bazel uses a hard-coded "
+ + "default path depending on the operating system it runs on (Windows: "
+ + "c:/tools/msys64/usr/bin/bash.exe, FreeBSD: /usr/local/bin/bash, all others: "
+ + "/bin/bash). Note that using a shell that is not compatible with bash may lead "
+ + "to build failures or runtime failures of the generated binaries."
+ )
+ public PathFragment shellExecutable;
+
+ @Override
+ public Options getHost() {
+ Options host = (Options) getDefault();
+ host.shellExecutable = shellExecutable;
+ return host;
+ }
+ }
+
+ /**
+ * Encapsulates the contributions of {@link ShellConfiguration} to the action environment.
+ *
+ * <p>This is done this way because we need the shell environment to be different between Bazel
+ * and Blaze, but configuration fragments are handed out based on their classes, thus,
+ * doing this with inheritance would be difficult. The good old "has-a instead of this-a" pattern.
+ */
+ public interface ShellActionEnvironment {
+ void setupActionEnvironment(ShellConfiguration configuration, Map<String, String> builder);
+ }
+
+ /**
+ * A factory for shell action environments.
+ *
+ * <p>This is necessary because the Bazel shell action environment depends on whether we use
+ * strict action environments or not, but we cannot simply hardcode the dependency on that bit
+ * here because it doesn't exist in Blaze. Thus, during configuration creation time, we call this
+ * factory which returns an object, which, when called, updates the actual action environment.
+ */
+ public interface ShellActionEnvironmentFactory {
+ ShellActionEnvironment fromOptions(BuildOptions options);
+ }
+
+ /** A {@link ShellConfiguration} that contributes nothing to the action environment. */
+ public static final ShellActionEnvironmentFactory NO_ACTION_ENV =
+ (BuildOptions options) -> (ShellConfiguration config, Map<String, String> builder) -> {};
+
+ /** the part of {@link ShellConfiguration} that determines where the shell is. */
+ public interface ShellExecutableProvider {
+ PathFragment getShellExecutable(BuildOptions options);
+ }
+
+ /** A shell executable whose path is hard-coded. */
+ public static ShellExecutableProvider hardcodedShellExecutable(String shell) {
+ return (BuildOptions options) -> PathFragment.create(shell);
+ }
+
+ /** The loader for {@link ShellConfiguration}. */
+ public static class Loader implements ConfigurationFragmentFactory {
+ private final ShellExecutableProvider shellExecutableProvider;
+ private final ShellActionEnvironmentFactory actionEnvironmentFactory;
+ private final ImmutableSet<Class<? extends FragmentOptions>> requiredOptions;
+
+ public Loader(ShellExecutableProvider shellExecutableProvider,
+ ShellActionEnvironmentFactory actionEnvironmentFactory,
+ Class<? extends FragmentOptions>... requiredOptions) {
+ this.shellExecutableProvider = shellExecutableProvider;
+ this.actionEnvironmentFactory = actionEnvironmentFactory;
+ this.requiredOptions = ImmutableSet.copyOf(requiredOptions);
+ }
+
+ @Nullable
+ @Override
+ public Fragment create(ConfigurationEnvironment env, BuildOptions buildOptions) {
+ return new ShellConfiguration(
+ shellExecutableProvider.getShellExecutable(buildOptions),
+ actionEnvironmentFactory.fromOptions(buildOptions));
+ }
+
+ public static PathFragment determineShellExecutable(
+ OS os, Options options, PathFragment defaultShell) {
+ if (options.shellExecutable != null) {
+ return options.shellExecutable;
+ }
+
+ // Honor BAZEL_SH env variable for backwards compatibility.
+ String path = System.getenv("BAZEL_SH");
+ if (path != null) {
+ return PathFragment.create(path);
+ }
+ // TODO(ulfjack): instead of using the OS Bazel runs on, we need to use the exec platform,
+ // which may be different for remote execution. For now, this can be overridden with
+ // --shell_executable, so at least there's a workaround.
+ PathFragment result = OS_SPECIFIC_SHELL.get(os);
+ return result != null ? result : defaultShell;
+ }
+
+ @Override
+ public Class<? extends Fragment> creates() {
+ return ShellConfiguration.class;
+ }
+
+ @Override
+ public ImmutableSet<Class<? extends FragmentOptions>> requiredOptions() {
+ return requiredOptions;
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java
index 3d352c4..a89651c 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java
@@ -55,6 +55,7 @@
import com.google.devtools.build.lib.actions.extra.SpawnInfo;
import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
+import com.google.devtools.build.lib.analysis.ShellConfiguration;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
@@ -678,8 +679,8 @@
AnalysisEnvironment analysisEnvironment,
BuildConfiguration configuration,
List<Action> paramFileActions) {
- ImmutableList<String> executableArgs =
- buildExecutableArgs(configuration.getShellExecutable());
+ ImmutableList<String> executableArgs = buildExecutableArgs(
+ configuration.getFragment(ShellConfiguration.class).getShellExecutable());
boolean hasConditionalParamFile =
commandLines.stream().anyMatch(c -> c.paramFileInfo != null && !c.paramFileInfo.always());
boolean spillToParamFiles = false;
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
index 51cbfbc..a068e51 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
@@ -165,16 +165,6 @@
}
/**
- * Returns the shell to be used.
- *
- * <p>Each configuration instance must have at most one fragment that returns non-null.
- */
- @SuppressWarnings("unused")
- public PathFragment getShellExecutable() {
- return null;
- }
-
- /**
* Returns { 'option name': 'alternative default' } entries for options where the
* "real default" should be something besides the default specified in the {@link Option}
* declaration.
@@ -1115,9 +1105,6 @@
// is mutable, so a cached value there could fall out of date when it's updated.
private final boolean actionsEnabled;
- // TODO(bazel-team): Move this to a configuration fragment.
- private final PathFragment shellExecutable;
-
/**
* The global "make variables" such as "$(TARGET_CPU)"; these get applied to all rules analyzed in
* this configuration.
@@ -1335,8 +1322,6 @@
OutputDirectory.MIDDLEMAN.getRoot(
RepositoryName.MAIN, outputDirName, directories, mainRepositoryName);
- this.shellExecutable = computeShellExecutable();
-
this.actionEnv = setupActionEnvironment();
this.testEnv = setupTestEnvironment();
@@ -1642,13 +1627,6 @@
}
/**
- * Returns the path to sh.
- */
- public PathFragment getShellExecutable() {
- return shellExecutable;
- }
-
- /**
* Returns a regex-based instrumentation filter instance that used to match label
* names to identify targets to be instrumented in the coverage mode.
*/
@@ -1930,22 +1908,6 @@
}
/**
- * Collects executables defined by fragments.
- */
- private PathFragment computeShellExecutable() {
- PathFragment result = null;
-
- for (Fragment fragment : fragments.values()) {
- if (fragment.getShellExecutable() != null) {
- Verify.verify(result == null);
- result = fragment.getShellExecutable();
- }
- }
-
- return result;
- }
-
- /**
* Returns the transition that produces the "artifact owner" for this configuration, or null
* if this configuration is its own owner.
*/
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
index 336ca69..500a4f5 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
@@ -33,6 +33,7 @@
import com.google.devtools.build.lib.actions.ExecException;
import com.google.devtools.build.lib.actions.NotifyOnActionCacheHit;
import com.google.devtools.build.lib.analysis.RunfilesSupplierImpl;
+import com.google.devtools.build.lib.analysis.ShellConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.RunUnder;
import com.google.devtools.build.lib.buildeventstream.TestFileNameConstants;
@@ -754,7 +755,7 @@
}
public PathFragment getShExecutable() {
- return configuration.getShellExecutable();
+ return configuration.getFragment(ShellConfiguration.class).getShellExecutable();
}
public ImmutableMap<String, String> getLocalShellEnvironment() {