Skip to content

Commit bc83389

Browse files
fmeumcopybara-github
authored andcommitted
Clear runfiles environment variables for bazel run
When an environment variable such as `RUNFILES_DIR` is set in the client environment when a target using runfiles libraries is run via `bazel run`, the libraries can't look up the runfiles directory or manifest. This is fixed by clearing the runfiles-related environment variables from the env in which the target is executed. Fixes bazelbuild#17571 Closes bazelbuild#17690. PiperOrigin-RevId: 516474822 Change-Id: Ia5201d4334b286b36ba2e476e850b98992ca0ffa
1 parent 24470be commit bc83389

File tree

9 files changed

+107
-3
lines changed

9 files changed

+107
-3
lines changed

src/main/cpp/blaze.cc

+6
Original file line numberDiff line numberDiff line change
@@ -2151,6 +2151,12 @@ unsigned int BlazeServer::Communicate(
21512151
return blaze_exit_code::INTERNAL_ERROR;
21522152
}
21532153

2154+
// Clear environment variables before setting the requested ones so that
2155+
// users can still explicitly override the clearing.
2156+
for (const auto &variable_name : request.environment_variable_to_clear()) {
2157+
UnsetEnv(variable_name);
2158+
}
2159+
21542160
vector<string> argv(request.argv().begin(), request.argv().end());
21552161
for (const auto &variable : request.environment_variable()) {
21562162
SetEnv(variable.name(), variable.value());

src/main/java/com/google/devtools/build/lib/actions/ActionExecutionContext.java

+1
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ public void maybeReportSubcommand(Spawn spawn) {
342342
showSubcommands.prettyPrintArgs,
343343
spawn.getArguments(),
344344
spawn.getEnvironment(),
345+
/* environmentVariablesToClear= */ null,
345346
getExecRoot().getPathString(),
346347
spawn.getConfigurationChecksum(),
347348
spawn.getExecutionPlatformLabelString());

src/main/java/com/google/devtools/build/lib/query2/aquery/ActionGraphTextOutputFormatterCallback.java

+1
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ private void writeAction(ActionAnalysisMetadata action, PrintStream printStream)
281281
.map(a -> escapeBytestringUtf8(a))
282282
.collect(toImmutableList()),
283283
/* environment= */ null,
284+
/* environmentVariablesToClear= */ null,
284285
/* cwd= */ null,
285286
action.getOwner().getConfigurationChecksum(),
286287
action.getExecutionPlatform() == null

src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java

+19
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package com.google.devtools.build.lib.runtime.commands;
1616

17+
import static com.google.common.collect.ImmutableList.toImmutableList;
1718
import static java.nio.charset.StandardCharsets.ISO_8859_1;
1819

1920
import com.google.common.base.Joiner;
@@ -145,6 +146,17 @@ public static class RunOptions extends OptionsBase {
145146

146147
private static final FileType RUNFILES_MANIFEST = FileType.of(".runfiles_manifest");
147148

149+
private static final ImmutableList<String> ENV_VARIABLES_TO_CLEAR =
150+
ImmutableList.of(
151+
// These variables are all used by runfiles libraries to locate the runfiles directory or
152+
// manifest and can cause incorrect behavior when set for the top-level binary run with
153+
// bazel run.
154+
"JAVA_RUNFILES",
155+
"RUNFILES_DIR",
156+
"RUNFILES_MANIFEST_FILE",
157+
"RUNFILES_MANIFEST_ONLY",
158+
"TEST_SRCDIR");
159+
148160
/** The test policy to determine the environment variables from when running tests */
149161
private final TestPolicy testPolicy;
150162

@@ -211,6 +223,7 @@ public BlazeCommandResult exec(CommandEnvironment env, OptionsParsingResult opti
211223
/* prettyPrintArgs= */ false,
212224
runCommandLine.args,
213225
runCommandLine.runEnvironment,
226+
ENV_VARIABLES_TO_CLEAR,
214227
runCommandLine.workingDir.getPathString(),
215228
builtTargets.configuration.checksum(),
216229
/* executionPlatformAsLabelString= */ null);
@@ -261,6 +274,7 @@ public BlazeCommandResult exec(CommandEnvironment env, OptionsParsingResult opti
261274
runCommandLine.workingDir,
262275
runCommandLine.args,
263276
runEnv.buildOrThrow(),
277+
ENV_VARIABLES_TO_CLEAR,
264278
builtTargets.configuration));
265279
} catch (RunCommandException e) {
266280
return e.result;
@@ -678,6 +692,7 @@ private static ExecRequest buildExecRequest(
678692
Path workingDir,
679693
ImmutableList<String> args,
680694
ImmutableSortedMap<String, String> runEnv,
695+
ImmutableList<String> runEnvToClear,
681696
BuildConfigurationValue configuration)
682697
throws RunCommandException {
683698
ExecRequest.Builder execDescription =
@@ -729,6 +744,10 @@ private static ExecRequest buildExecRequest(
729744
.setValue(ByteString.copyFrom(variable.getValue(), ISO_8859_1))
730745
.build());
731746
}
747+
execDescription.addAllEnvironmentVariableToClear(
748+
runEnvToClear.stream()
749+
.map(s -> ByteString.copyFrom(s, ISO_8859_1))
750+
.collect(toImmutableList()));
732751
return execDescription.build();
733752
}
734753

src/main/java/com/google/devtools/build/lib/util/CommandFailureUtils.java

+22
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.io.File;
2323
import java.util.Collection;
2424
import java.util.Comparator;
25+
import java.util.List;
2526
import java.util.Map;
2627
import javax.annotation.Nullable;
2728

@@ -41,6 +42,8 @@ private interface DescribeCommandImpl {
4142
void describeCommandCwd(String cwd, StringBuilder message);
4243
void describeCommandEnvPrefix(StringBuilder message, boolean isolated);
4344
void describeCommandEnvVar(StringBuilder message, Map.Entry<String, String> entry);
45+
46+
void describeCommandUnsetEnvVar(StringBuilder message, String name);
4447
/**
4548
* Formats the command element and adds it to the message.
4649
*
@@ -83,6 +86,12 @@ public void describeCommandEnvVar(StringBuilder message, Map.Entry<String, Strin
8386
.append(ShellEscaper.escapeString(entry.getValue())).append(" \\\n ");
8487
}
8588

89+
@Override
90+
public void describeCommandUnsetEnvVar(StringBuilder message, String name) {
91+
// Only the short form of --unset is supported on macOS.
92+
message.append("-u ").append(ShellEscaper.escapeString(name)).append(" \\\n ");
93+
}
94+
8695
@Override
8796
public void describeCommandElement(
8897
StringBuilder message, String commandElement, boolean isBinary) {
@@ -123,6 +132,11 @@ public void describeCommandEnvVar(StringBuilder message, Map.Entry<String, Strin
123132
.append(entry.getValue()).append("\n ");
124133
}
125134

135+
@Override
136+
public void describeCommandUnsetEnvVar(StringBuilder message, String name) {
137+
message.append("SET ").append(name).append('=').append("\n ");
138+
}
139+
126140
@Override
127141
public void describeCommandElement(
128142
StringBuilder message, String commandElement, boolean isBinary) {
@@ -156,6 +170,7 @@ public static String describeCommand(
156170
boolean prettyPrintArgs,
157171
Collection<String> commandLineElements,
158172
@Nullable Map<String, String> environment,
173+
@Nullable List<String> environmentVariablesToClear,
159174
@Nullable String cwd,
160175
@Nullable String configurationChecksum,
161176
@Nullable String executionPlatformAsLabelString) {
@@ -205,6 +220,12 @@ public static String describeCommand(
205220
if (environment != null) {
206221
describeCommandImpl.describeCommandEnvPrefix(
207222
message, form != CommandDescriptionForm.COMPLETE_UNISOLATED);
223+
if (environmentVariablesToClear != null) {
224+
for (String name : Ordering.natural().sortedCopy(environmentVariablesToClear)) {
225+
message.append(" ");
226+
describeCommandImpl.describeCommandUnsetEnvVar(message, name);
227+
}
228+
}
208229
// A map can never have two keys with the same value, so we only need to compare the keys.
209230
Comparator<Map.Entry<String, String>> mapEntryComparator = comparingByKey();
210231
for (Map.Entry<String, String> entry :
@@ -291,6 +312,7 @@ static String describeCommandFailure(
291312
/* prettyPrintArgs= */ false,
292313
commandLineElements,
293314
env,
315+
null,
294316
cwd,
295317
configurationChecksum,
296318
executionPlatformAsLabelString));

src/main/java/com/google/devtools/build/lib/worker/WorkerKey.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,12 @@ public String toString() {
211211
// debugging.
212212
return CommandFailureUtils.describeCommand(
213213
CommandDescriptionForm.COMPLETE,
214-
/*prettyPrintArgs=*/ false,
214+
/* prettyPrintArgs= */ false,
215215
args,
216216
env,
217+
/* environmentVariablesToClear= */ null,
217218
execRoot.getPathString(),
218-
/*configurationChecksum=*/ null,
219-
/*executionPlatformAsLabelString=*/ null);
219+
/* configurationChecksum= */ null,
220+
/* executionPlatformAsLabelString= */ null);
220221
}
221222
}

src/main/protobuf/command_server.proto

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ message ExecRequest {
9292
bytes working_directory = 1;
9393
repeated bytes argv = 2;
9494
repeated EnvironmentVariable environment_variable = 3;
95+
repeated bytes environment_variable_to_clear = 4;
9596
}
9697

9798
// Contains metadata and result data for a command execution.

src/test/java/com/google/devtools/build/lib/util/CommandFailureUtilsTest.java

+6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import static com.google.common.truth.Truth.assertThat;
1717

18+
import com.google.common.collect.ImmutableList;
1819
import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
1920
import com.google.devtools.build.lib.cmdline.Label;
2021
import java.util.Arrays;
@@ -210,6 +211,8 @@ public void describeCommandPrettyPrintArgs() throws Exception {
210211
env.put("FOO", "foo");
211212
env.put("PATH", "/usr/bin:/bin:/sbin");
212213

214+
ImmutableList<String> envToClear = ImmutableList.of("CLEAR", "THIS");
215+
213216
String cwd = "/my/working/directory";
214217
PlatformInfo executionPlatform =
215218
PlatformInfo.builder().setLabel(Label.parseCanonicalUnchecked("//platform:exec")).build();
@@ -219,6 +222,7 @@ public void describeCommandPrettyPrintArgs() throws Exception {
219222
true,
220223
Arrays.asList(args),
221224
env,
225+
envToClear,
222226
cwd,
223227
"cfg12345",
224228
executionPlatform.label().toString());
@@ -227,6 +231,8 @@ public void describeCommandPrettyPrintArgs() throws Exception {
227231
.isEqualTo(
228232
"(cd /my/working/directory && \\\n"
229233
+ " exec env - \\\n"
234+
+ " -u CLEAR \\\n"
235+
+ " -u THIS \\\n"
230236
+ " FOO=foo \\\n"
231237
+ " PATH=/usr/bin:/bin:/sbin \\\n"
232238
+ " some_command \\\n"

src/test/shell/bazel/run_test.sh

+47
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,51 @@ eof
125125
echo "$output" | grep --fixed-strings 'ExecuteProgram(C:\first_part second_part)' || fail "Expected error message to contain unquoted path"
126126
}
127127

128+
function test_run_with_runfiles_env() {
129+
mkdir -p b
130+
cat > b/BUILD <<'EOF'
131+
sh_binary(
132+
name = "binary",
133+
srcs = ["binary.sh"],
134+
deps = ["@bazel_tools//tools/bash/runfiles"],
135+
)
136+
EOF
137+
cat > b/binary.sh <<'EOF'
138+
#!/usr/bin/env bash
139+
# --- begin runfiles.bash initialization v3 ---
140+
# Copy-pasted from the Bazel Bash runfiles library v3.
141+
set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash
142+
source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \
143+
source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \
144+
source "$0.runfiles/$f" 2>/dev/null || \
145+
source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
146+
source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
147+
{ echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
148+
# --- end runfiles.bash initialization v3 ---
149+
150+
own_path=$(rlocation main/b/binary.sh)
151+
echo "own path: $own_path"
152+
test -f "$own_path"
153+
EOF
154+
chmod +x b/binary.sh
155+
156+
bazel run //b:binary --script_path=script.bat &>"$TEST_log" \
157+
|| fail "Script generation should succeed"
158+
159+
cat ./script.bat &>"$TEST_log"
160+
161+
# Make it so that the runfiles variables point to an incorrect but valid
162+
# runfiles directory/manifest, simulating a left over one from a different
163+
# test to which RUNFILES_DIR and RUNFILES_MANIFEST_FILE point in the client
164+
# env.
165+
BOGUS_RUNFILES_DIR="$(pwd)/bogus_runfiles/bazel_tools/tools/bash/runfiles"
166+
mkdir -p "$BOGUS_RUNFILES_DIR"
167+
touch "$BOGUS_RUNFILES_DIR/runfiles.bash"
168+
BOGUS_RUNFILES_MANIFEST_FILE="$(pwd)/bogus_manifest"
169+
echo "bazel_tools/tools/bash/runfiles/runfiles.bash bogus/path" > "$BOGUS_RUNFILES_MANIFEST_FILE"
170+
171+
RUNFILES_DIR="$BOGUS_RUNFILES_DIR" RUNFILES_MANIFEST_FILE="$BOGUS_RUNFILES_MANIFEST_FILE" \
172+
./script.bat || fail "Run should succeed"
173+
}
174+
128175
run_suite "run_under_tests"

0 commit comments

Comments
 (0)