blob: a09ff9804356ad8741b9751380384446a8425580 [file] [log] [blame]
// 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.devtools.build.lib.analysis.config.BuildOptions;
import com.google.devtools.build.lib.analysis.config.Fragment;
import com.google.devtools.build.lib.analysis.config.FragmentOptions;
import com.google.devtools.build.lib.analysis.config.RequiresOptions;
import com.google.devtools.build.lib.analysis.platform.ConstraintSettingInfo;
import com.google.devtools.build.lib.analysis.platform.ConstraintValueInfo;
import com.google.devtools.build.lib.cmdline.Label;
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.devtools.common.options.OptionMetadataTag;
import java.util.Map;
import java.util.function.Function;
/** A configuration fragment that tells where the shell is. */
@RequiresOptions(options = {ShellConfiguration.Options.class})
public class ShellConfiguration extends Fragment {
private static Map<OS, PathFragment> shellExecutables;
private static final ConstraintSettingInfo OS_CONSTRAINT_SETTING =
ConstraintSettingInfo.create(
Label.parseCanonicalUnchecked("@platforms//os:os"));
private static Function<Options, PathFragment> optionsBasedDefault;
/**
* Injects a function for retrieving the default sh path from build options, and a map for
* locating the correct sh executable given a set of target constraints.
*/
public static void injectShellExecutableFinder(
Function<Options, PathFragment> shellFromOptionsFinder, Map<OS, PathFragment> osToShellMap) {
// It'd be nice not to have to set a global static field. But there are so many disparate calls
// to getShellExecutables() (in both the build's analysis phase and in the run command) that
// feeding this through instance variables is unwieldy. Fortunately this info is a function of
// the Blaze implementation and not something that might change between builds.
optionsBasedDefault = shellFromOptionsFinder;
shellExecutables = osToShellMap;
}
/**
* Injects a map for locating the correct sh executable given a set of target constraints. Assumes
* no options-based default shell.
*/
public static void injectShellExecutableFinder(Map<OS, PathFragment> osToShellMap) {
optionsBasedDefault = (options) -> null;
shellExecutables = osToShellMap;
}
// Standard mapping between OS and the corresponding platform constraints.
static final ImmutableMap<OS, ConstraintValueInfo> OS_TO_CONSTRAINTS =
ImmutableMap.<OS, ConstraintValueInfo>builder()
.put(
OS.DARWIN,
ConstraintValueInfo.create(
OS_CONSTRAINT_SETTING,
Label.parseCanonicalUnchecked("@platforms//os:osx")))
.put(
OS.WINDOWS,
ConstraintValueInfo.create(
OS_CONSTRAINT_SETTING,
Label.parseCanonicalUnchecked("@platforms//os:windows")))
.put(
OS.FREEBSD,
ConstraintValueInfo.create(
OS_CONSTRAINT_SETTING,
Label.parseCanonicalUnchecked("@platforms//os:freebsd")))
.put(
OS.OPENBSD,
ConstraintValueInfo.create(
OS_CONSTRAINT_SETTING,
Label.parseCanonicalUnchecked("@platforms//os:openbsd")))
.put(
OS.UNKNOWN,
ConstraintValueInfo.create(
OS_CONSTRAINT_SETTING,
Label.parseCanonicalUnchecked("@platforms//os:none")))
.buildOrThrow();
private final boolean useShBinaryStubScript;
private final PathFragment defaultShellExecutableFromOptions;
public ShellConfiguration(BuildOptions buildOptions) {
this.defaultShellExecutableFromOptions =
optionsBasedDefault.apply(buildOptions.get(Options.class));
this.useShBinaryStubScript = buildOptions.get(Options.class).useShBinaryStubScript;
}
public static Map<OS, PathFragment> getShellExecutables() {
return shellExecutables;
}
/* Returns a function for retrieving the default shell from build options. */
public PathFragment getOptionsBasedDefault() {
return defaultShellExecutableFromOptions;
}
public boolean useShBinaryStubScript() {
return useShBinaryStubScript;
}
/** An option that tells Bazel where the shell is. */
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;
@Option(
name = "experimental_use_sh_binary_stub_script",
documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
effectTags = {OptionEffectTag.AFFECTS_OUTPUTS},
metadataTags = {OptionMetadataTag.EXPERIMENTAL},
defaultValue = "false",
help = "If enabled, use a stub script for sh_binary targets.")
public boolean useShBinaryStubScript;
@Override
public Options getExec() {
Options exec = (Options) getDefault();
exec.shellExecutable = shellExecutable;
exec.useShBinaryStubScript = useShBinaryStubScript;
return exec;
}
}
}