blob: 2fab7708d152195c70fdfe8172037617e9c70821 [file] [log] [blame]
lberki78651d42018-04-06 01:52:58 -07001// Copyright 2018 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.analysis;
15
16import com.google.common.collect.ImmutableMap;
17import com.google.common.collect.ImmutableSet;
18import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
19import com.google.devtools.build.lib.analysis.config.BuildConfiguration.Fragment;
20import com.google.devtools.build.lib.analysis.config.BuildOptions;
lberki78651d42018-04-06 01:52:58 -070021import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory;
22import com.google.devtools.build.lib.analysis.config.FragmentOptions;
lberki78651d42018-04-06 01:52:58 -070023import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
24import com.google.devtools.build.lib.util.OS;
25import com.google.devtools.build.lib.util.OptionsUtils.PathFragmentConverter;
26import com.google.devtools.build.lib.vfs.PathFragment;
27import com.google.devtools.common.options.Option;
28import com.google.devtools.common.options.OptionDocumentationCategory;
29import com.google.devtools.common.options.OptionEffectTag;
cushon02642c22018-06-28 01:58:56 -070030import com.google.devtools.common.options.OptionMetadataTag;
janakrd7bec2e2018-06-19 09:47:01 -070031import java.io.Serializable;
lberki78651d42018-04-06 01:52:58 -070032import javax.annotation.Nullable;
33
34/** A configuration fragment that tells where the shell is. */
lberki86266232018-04-11 00:33:42 -070035@AutoCodec
lberki78651d42018-04-06 01:52:58 -070036public class ShellConfiguration extends BuildConfiguration.Fragment {
lberki78651d42018-04-06 01:52:58 -070037 private static final ImmutableMap<OS, PathFragment> OS_SPECIFIC_SHELL =
38 ImmutableMap.<OS, PathFragment>builder()
39 .put(OS.WINDOWS, PathFragment.create("c:/tools/msys64/usr/bin/bash.exe"))
40 .put(OS.FREEBSD, PathFragment.create("/usr/local/bin/bash"))
41 .build();
42
43 private final PathFragment shellExecutable;
cushon02642c22018-06-28 01:58:56 -070044 private final boolean useShBinaryStubScript;
lberki78651d42018-04-06 01:52:58 -070045
cushon02642c22018-06-28 01:58:56 -070046 public ShellConfiguration(PathFragment shellExecutable, boolean useShBinaryStubScript) {
lberki78651d42018-04-06 01:52:58 -070047 this.shellExecutable = shellExecutable;
cushon02642c22018-06-28 01:58:56 -070048 this.useShBinaryStubScript = useShBinaryStubScript;
lberki78651d42018-04-06 01:52:58 -070049 }
50
51 public PathFragment getShellExecutable() {
52 return shellExecutable;
53 }
54
cushon02642c22018-06-28 01:58:56 -070055 public boolean useShBinaryStubScript() {
56 return useShBinaryStubScript;
57 }
58
lberki78651d42018-04-06 01:52:58 -070059 /** An option that tells Bazel where the shell is. */
60 @AutoCodec(strategy = AutoCodec.Strategy.PUBLIC_FIELDS)
61 public static class Options extends FragmentOptions {
62 @Option(
63 name = "shell_executable",
64 converter = PathFragmentConverter.class,
65 defaultValue = "null",
66 documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
67 effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
68 help =
69 "Absolute path to the shell executable for Bazel to use. If this is unset, but the "
70 + "BAZEL_SH environment variable is set on the first Bazel invocation (that starts "
71 + "up a Bazel server), Bazel uses that. If neither is set, Bazel uses a hard-coded "
72 + "default path depending on the operating system it runs on (Windows: "
73 + "c:/tools/msys64/usr/bin/bash.exe, FreeBSD: /usr/local/bin/bash, all others: "
74 + "/bin/bash). Note that using a shell that is not compatible with bash may lead "
75 + "to build failures or runtime failures of the generated binaries."
76 )
77 public PathFragment shellExecutable;
78
cushon02642c22018-06-28 01:58:56 -070079 @Option(
80 name = "experimental_use_sh_binary_stub_script",
81 documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
82 effectTags = {OptionEffectTag.AFFECTS_OUTPUTS},
83 metadataTags = {OptionMetadataTag.EXPERIMENTAL},
84 defaultValue = "false",
85 help = "If enabled, use a stub script for sh_binary targets.")
86 public boolean useShBinaryStubScript;
87
lberki78651d42018-04-06 01:52:58 -070088 @Override
89 public Options getHost() {
90 Options host = (Options) getDefault();
91 host.shellExecutable = shellExecutable;
cushon02642c22018-06-28 01:58:56 -070092 host.useShBinaryStubScript = useShBinaryStubScript;
lberki78651d42018-04-06 01:52:58 -070093 return host;
94 }
95 }
96
lberki78651d42018-04-06 01:52:58 -070097 /** the part of {@link ShellConfiguration} that determines where the shell is. */
98 public interface ShellExecutableProvider {
99 PathFragment getShellExecutable(BuildOptions options);
100 }
101
102 /** A shell executable whose path is hard-coded. */
103 public static ShellExecutableProvider hardcodedShellExecutable(String shell) {
janakrd7bec2e2018-06-19 09:47:01 -0700104 return (ShellExecutableProvider & Serializable) (options) -> PathFragment.create(shell);
lberki78651d42018-04-06 01:52:58 -0700105 }
106
107 /** The loader for {@link ShellConfiguration}. */
108 public static class Loader implements ConfigurationFragmentFactory {
109 private final ShellExecutableProvider shellExecutableProvider;
lberki78651d42018-04-06 01:52:58 -0700110 private final ImmutableSet<Class<? extends FragmentOptions>> requiredOptions;
111
112 public Loader(ShellExecutableProvider shellExecutableProvider,
lberki78651d42018-04-06 01:52:58 -0700113 Class<? extends FragmentOptions>... requiredOptions) {
114 this.shellExecutableProvider = shellExecutableProvider;
lberki78651d42018-04-06 01:52:58 -0700115 this.requiredOptions = ImmutableSet.copyOf(requiredOptions);
116 }
117
118 @Nullable
119 @Override
gregce56eb80b2018-05-02 09:04:10 -0700120 public Fragment create(BuildOptions buildOptions) {
cushon02642c22018-06-28 01:58:56 -0700121 Options options = buildOptions.get(Options.class);
122 return new ShellConfiguration(
123 shellExecutableProvider.getShellExecutable(buildOptions),
124 options != null && options.useShBinaryStubScript);
lberki78651d42018-04-06 01:52:58 -0700125 }
126
127 public static PathFragment determineShellExecutable(
128 OS os, Options options, PathFragment defaultShell) {
129 if (options.shellExecutable != null) {
130 return options.shellExecutable;
131 }
132
133 // Honor BAZEL_SH env variable for backwards compatibility.
134 String path = System.getenv("BAZEL_SH");
135 if (path != null) {
136 return PathFragment.create(path);
137 }
138 // TODO(ulfjack): instead of using the OS Bazel runs on, we need to use the exec platform,
139 // which may be different for remote execution. For now, this can be overridden with
140 // --shell_executable, so at least there's a workaround.
141 PathFragment result = OS_SPECIFIC_SHELL.get(os);
142 return result != null ? result : defaultShell;
143 }
144
145 @Override
146 public Class<? extends Fragment> creates() {
147 return ShellConfiguration.class;
148 }
149
150 @Override
151 public ImmutableSet<Class<? extends FragmentOptions>> requiredOptions() {
152 return requiredOptions;
153 }
154 }
155}