Skip to content

Commit e1e8734

Browse files
GooglermeisterT
Googler
authored andcommitted
Add an env attribute to all test and binary rule classes
Fixes bazelbuild#7364. PiperOrigin-RevId: 345355968
1 parent b2023c5 commit e1e8734

File tree

12 files changed

+181
-26
lines changed

12 files changed

+181
-26
lines changed

src/main/java/com/google/devtools/build/docgen/PredefinedAttributes.java

+15-11
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,19 @@
2727
public class PredefinedAttributes {
2828

2929
/**
30-
* List of documentation for common attributes of *_test rules, relative to
31-
* {@link com.google.devtools.build.docgen}.
30+
* List of documentation for common attributes of *_test rules, relative to {@link
31+
* com.google.devtools.build.docgen}.
3232
*/
33-
public static final ImmutableList<String> TEST_ATTRIBUTES_DOCFILES = ImmutableList.of(
34-
"templates/attributes/test/args.html",
35-
"templates/attributes/test/size.html",
36-
"templates/attributes/test/timeout.html",
37-
"templates/attributes/test/flaky.html",
38-
"templates/attributes/test/shard_count.html",
39-
"templates/attributes/test/local.html");
33+
public static final ImmutableList<String> TEST_ATTRIBUTES_DOCFILES =
34+
ImmutableList.of(
35+
"templates/attributes/test/args.html",
36+
"templates/attributes/test/env.html",
37+
"templates/attributes/test/env_inherit.html",
38+
"templates/attributes/test/size.html",
39+
"templates/attributes/test/timeout.html",
40+
"templates/attributes/test/flaky.html",
41+
"templates/attributes/test/shard_count.html",
42+
"templates/attributes/test/local.html");
4043

4144
/**
4245
* List of common attributes documentation, relative to {@link com.google.devtools.build.docgen}.
@@ -60,12 +63,13 @@ public class PredefinedAttributes {
6063
"templates/attributes/common/visibility.html");
6164

6265
/**
63-
* List of documentation for common attributes of *_binary rules, relative to
64-
* {@link com.google.devtools.build.docgen}.
66+
* List of documentation for common attributes of *_binary rules, relative to {@link
67+
* com.google.devtools.build.docgen}.
6568
*/
6669
public static final ImmutableList<String> BINARY_ATTRIBUTES_DOCFILES =
6770
ImmutableList.of(
6871
"templates/attributes/binary/args.html",
72+
"templates/attributes/binary/env.html",
6973
"templates/attributes/binary/output_licenses.html");
7074

7175
private static ImmutableMap<String, RuleDocumentationAttribute> generateAttributeMap(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<p><code>Dictionary of strings; optional; values are subject to
2+
<a href="${link make-variables#location}">$(location)</a> and
3+
<a href="${link make-variables}">"Make variable"</a> substitution</code></p>
4+
5+
<p>Specifies additional environment variables to set when the target is
6+
executed by <code>bazel run</code>.
7+
</p>
8+
9+
<p>
10+
<em class="harmful">NOTE: The environment variables are not set when you run the target
11+
outside of bazel (for example, by manually executing the binary in
12+
<code>bazel-bin/</code>).</em>
13+
</p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<p><code>Dictionary of strings; optional; values are subject to
2+
<a href="${link make-variables#location}">$(location)</a> and
3+
<a href="${link make-variables}">"Make variable"</a> substitution</code>
4+
</p>
5+
6+
<p>Specifies additional environment variables to set when the test is
7+
executed by <code>bazel test</code>.
8+
</p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<p><code>List of strings; optional</p>
2+
3+
<p>Specifies additional environment variables to inherit from the
4+
external environment when the test is executed by <code>bazel test</code>.
5+
</p>

src/main/java/com/google/devtools/build/lib/analysis/BaseRuleClasses.java

+3
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ public Object getDefault(AttributeMap rule) {
194194
.taggable()
195195
.nonconfigurable("policy decision: should be consistent across configurations"))
196196
.add(attr("args", STRING_LIST))
197+
.add(attr("env", STRING_DICT))
198+
.add(attr("env_inherit", STRING_LIST))
197199
// Input files for every test action
198200
.add(
199201
attr("$test_wrapper", LABEL)
@@ -475,6 +477,7 @@ public static final class BinaryBaseRule implements RuleDefinition {
475477
public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) {
476478
return builder
477479
.add(attr("args", STRING_LIST))
480+
.add(attr("env", STRING_DICT))
478481
.add(attr("output_licenses", LICENSE))
479482
.add(
480483
attr("$is_executable", BOOLEAN)

src/main/java/com/google/devtools/build/lib/analysis/RunfilesSupport.java

+59-6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
import com.google.common.annotations.VisibleForTesting;
1818
import com.google.common.base.Preconditions;
19+
import com.google.common.collect.ImmutableSet;
20+
import com.google.devtools.build.lib.actions.ActionEnvironment;
1921
import com.google.devtools.build.lib.actions.Artifact;
2022
import com.google.devtools.build.lib.actions.CommandLine;
2123
import com.google.devtools.build.lib.analysis.SourceManifestAction.ManifestType;
@@ -34,9 +36,11 @@
3436
import com.google.devtools.build.lib.vfs.Path;
3537
import com.google.devtools.build.lib.vfs.PathFragment;
3638
import java.util.Collection;
39+
import java.util.LinkedHashSet;
3740
import java.util.List;
3841
import java.util.Map;
3942
import java.util.Set;
43+
import java.util.TreeMap;
4044
import javax.annotation.Nullable;
4145

4246
/**
@@ -85,6 +89,7 @@ public final class RunfilesSupport {
8589
private final boolean buildRunfileLinks;
8690
private final boolean runfilesEnabled;
8791
private final CommandLine args;
92+
private final ActionEnvironment actionEnvironment;
8893

8994
/**
9095
* Creates the RunfilesSupport helper with the given executable and runfiles.
@@ -94,7 +99,11 @@ public final class RunfilesSupport {
9499
* @param runfiles the runfiles
95100
*/
96101
private static RunfilesSupport create(
97-
RuleContext ruleContext, Artifact executable, Runfiles runfiles, CommandLine args) {
102+
RuleContext ruleContext,
103+
Artifact executable,
104+
Runfiles runfiles,
105+
CommandLine args,
106+
ActionEnvironment actionEnvironment) {
98107
Artifact owningExecutable = Preconditions.checkNotNull(executable);
99108
boolean createManifest = ruleContext.getConfiguration().buildRunfilesManifests();
100109
boolean buildRunfileLinks = ruleContext.getConfiguration().buildRunfileLinks();
@@ -139,7 +148,8 @@ private static RunfilesSupport create(
139148
owningExecutable,
140149
buildRunfileLinks,
141150
runfilesEnabled,
142-
args);
151+
args,
152+
actionEnvironment);
143153
}
144154

145155
@AutoCodec.Instantiator
@@ -152,7 +162,8 @@ private static RunfilesSupport create(
152162
Artifact owningExecutable,
153163
boolean buildRunfileLinks,
154164
boolean runfilesEnabled,
155-
CommandLine args) {
165+
CommandLine args,
166+
ActionEnvironment actionEnvironment) {
156167
this.runfiles = runfiles;
157168
this.runfilesInputManifest = runfilesInputManifest;
158169
this.runfilesManifest = runfilesManifest;
@@ -161,6 +172,7 @@ private static RunfilesSupport create(
161172
this.buildRunfileLinks = buildRunfileLinks;
162173
this.runfilesEnabled = runfilesEnabled;
163174
this.args = args;
175+
this.actionEnvironment = actionEnvironment;
164176
}
165177

166178
/** Returns the executable owning this RunfilesSupport. Only use from Starlark. */
@@ -394,14 +406,23 @@ public CommandLine getArgs() {
394406
return args;
395407
}
396408

409+
/** Returns the immutable environment from the 'env' and 'env_inherit' attribute values. */
410+
public ActionEnvironment getActionEnvironment() {
411+
return actionEnvironment;
412+
}
413+
397414
/**
398415
* Creates and returns a {@link RunfilesSupport} object for the given rule and executable. Note
399416
* that this method calls back into the passed in rule to obtain the runfiles.
400417
*/
401418
public static RunfilesSupport withExecutable(
402419
RuleContext ruleContext, Runfiles runfiles, Artifact executable) {
403420
return RunfilesSupport.create(
404-
ruleContext, executable, runfiles, computeArgs(ruleContext, CommandLine.EMPTY));
421+
ruleContext,
422+
executable,
423+
runfiles,
424+
computeArgs(ruleContext, CommandLine.EMPTY),
425+
computeActionEnvironment(ruleContext));
405426
}
406427

407428
/**
@@ -411,7 +432,11 @@ public static RunfilesSupport withExecutable(
411432
public static RunfilesSupport withExecutable(
412433
RuleContext ruleContext, Runfiles runfiles, Artifact executable, List<String> appendingArgs) {
413434
return RunfilesSupport.create(
414-
ruleContext, executable, runfiles, computeArgs(ruleContext, CommandLine.of(appendingArgs)));
435+
ruleContext,
436+
executable,
437+
runfiles,
438+
computeArgs(ruleContext, CommandLine.of(appendingArgs)),
439+
computeActionEnvironment(ruleContext));
415440
}
416441

417442
/**
@@ -421,7 +446,11 @@ public static RunfilesSupport withExecutable(
421446
public static RunfilesSupport withExecutable(
422447
RuleContext ruleContext, Runfiles runfiles, Artifact executable, CommandLine appendingArgs) {
423448
return RunfilesSupport.create(
424-
ruleContext, executable, runfiles, computeArgs(ruleContext, appendingArgs));
449+
ruleContext,
450+
executable,
451+
runfiles,
452+
computeArgs(ruleContext, appendingArgs),
453+
computeActionEnvironment(ruleContext));
425454
}
426455

427456
private static CommandLine computeArgs(RuleContext ruleContext, CommandLine additionalArgs) {
@@ -434,6 +463,30 @@ private static CommandLine computeArgs(RuleContext ruleContext, CommandLine addi
434463
ruleContext.getExpander().withDataLocations().tokenized("args"), additionalArgs);
435464
}
436465

466+
private static ActionEnvironment computeActionEnvironment(RuleContext ruleContext) {
467+
if (!ruleContext.getRule().isAttrDefined("env", Type.STRING_DICT)
468+
&& !ruleContext.getRule().isAttrDefined("env_inherit", Type.STRING_LIST)) {
469+
return ActionEnvironment.EMPTY;
470+
}
471+
TreeMap<String, String> fixedEnv = new TreeMap<>();
472+
Set<String> inheritedEnv = new LinkedHashSet<>();
473+
if (ruleContext.isAttrDefined("env", Type.STRING_DICT)) {
474+
Expander expander = ruleContext.getExpander().withDataLocations();
475+
for (Map.Entry<String, String> entry :
476+
ruleContext.attributes().get("env", Type.STRING_DICT).entrySet()) {
477+
fixedEnv.put(entry.getKey(), expander.expand("env", entry.getValue()));
478+
}
479+
}
480+
if (ruleContext.isAttrDefined("env_inherit", Type.STRING_LIST)) {
481+
for (String key : ruleContext.attributes().get("env_inherit", Type.STRING_LIST)) {
482+
if (!fixedEnv.containsKey(key)) {
483+
inheritedEnv.add(key);
484+
}
485+
}
486+
}
487+
return ActionEnvironment.create(fixedEnv, ImmutableSet.copyOf(inheritedEnv));
488+
}
489+
437490
/** Returns the path of the input manifest of {@code runfilesDir}. */
438491
public static Path inputManifestPath(Path runfilesDir) {
439492
return FileSystemUtils.replaceExtension(runfilesDir, INPUT_MANIFEST_EXT);

src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
* Helper class to create test actions.
5353
*/
5454
public final class TestActionBuilder {
55-
5655
private static final String CC_CODE_COVERAGE_SCRIPT = "CC_CODE_COVERAGE_SCRIPT";
5756
private static final String LCOV_MERGER = "LCOV_MERGER";
5857
// The coverage tool Bazel uses to generate a code coverage report for C++.
@@ -396,7 +395,7 @@ private TestParams createTestAction(int shards) throws InterruptedException {
396395
coverageArtifact,
397396
coverageDirectory,
398397
testProperties,
399-
extraTestEnv,
398+
runfilesSupport.getActionEnvironment().addFixedVariables(extraTestEnv),
400399
executionSettings,
401400
shard,
402401
run,

src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.google.common.util.concurrent.MoreExecutors;
2929
import com.google.devtools.build.lib.actions.AbstractAction;
3030
import com.google.devtools.build.lib.actions.ActionContinuationOrResult;
31+
import com.google.devtools.build.lib.actions.ActionEnvironment;
3132
import com.google.devtools.build.lib.actions.ActionExecutionContext;
3233
import com.google.devtools.build.lib.actions.ActionExecutionException;
3334
import com.google.devtools.build.lib.actions.ActionInput;
@@ -132,7 +133,7 @@ public class TestRunnerAction extends AbstractAction
132133
private Boolean unconditionalExecution;
133134

134135
/** Any extra environment variables (and values) added by the rule that created this action. */
135-
private final ImmutableMap<String, String> extraTestEnv;
136+
private final ActionEnvironment extraTestEnv;
136137

137138
/**
138139
* The set of environment variables that are inherited from the client environment. These are
@@ -176,7 +177,7 @@ private static ImmutableSet<Artifact> nonNullAsSet(Artifact... artifacts) {
176177
Artifact coverageArtifact,
177178
@Nullable Artifact coverageDirectory,
178179
TestTargetProperties testProperties,
179-
Map<String, String> extraTestEnv,
180+
ActionEnvironment extraTestEnv,
180181
TestTargetExecutionSettings executionSettings,
181182
int shardNum,
182183
int runNumber,
@@ -236,12 +237,13 @@ private static ImmutableSet<Artifact> nonNullAsSet(Artifact... artifacts) {
236237
this.testInfrastructureFailure = baseDir.getChild("test.infrastructure_failure");
237238
this.workspaceName = workspaceName;
238239

239-
this.extraTestEnv = ImmutableMap.copyOf(extraTestEnv);
240+
this.extraTestEnv = extraTestEnv;
240241
this.requiredClientEnvVariables =
241242
ImmutableIterable.from(
242243
Iterables.concat(
243244
configuration.getActionEnvironment().getInheritedEnv(),
244-
configuration.getTestActionEnvironment().getInheritedEnv()));
245+
configuration.getTestActionEnvironment().getInheritedEnv(),
246+
this.extraTestEnv.getInheritedEnv()));
245247
this.cancelConcurrentTestsOnSuccess = cancelConcurrentTestsOnSuccess;
246248
this.splitCoveragePostProcessing = splitCoveragePostProcessing;
247249
this.lcovMergerFilesToRun = lcovMergerFilesToRun;
@@ -378,7 +380,7 @@ protected void computeKey(
378380
fp.addBoolean(executionSettings.getTestRunnerFailFast());
379381
RunUnder runUnder = executionSettings.getRunUnder();
380382
fp.addString(runUnder == null ? "" : runUnder.getValue());
381-
fp.addStringMap(extraTestEnv);
383+
extraTestEnv.addTo(fp);
382384
// TODO(ulfjack): It might be better for performance to hash the action and test envs in config,
383385
// and only add a hash here.
384386
configuration.getActionEnvironment().addTo(fp);
@@ -675,7 +677,7 @@ public Artifact getTestLog() {
675677
}
676678

677679
/** Returns all environment variables which must be set in order to run this test. */
678-
public Map<String, String> getExtraTestEnv() {
680+
public ActionEnvironment getExtraTestEnv() {
679681
return extraTestEnv;
680682
}
681683

src/main/java/com/google/devtools/build/lib/exec/TestPolicy.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public Map<String, String> computeTestEnvironment(
8888
}
8989

9090
// Rule-specified test env.
91-
env.putAll(testAction.getExtraTestEnv());
91+
testAction.getExtraTestEnv().resolve(env, clientEnv);
9292

9393
// Overwrite with the environment common to all actions, see --action_env.
9494
testAction.getConfiguration().getActionEnvironment().resolve(env, clientEnv);

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

+3
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,9 @@ public BlazeCommandResult exec(CommandEnvironment env, OptionsParsingResult opti
471471
}
472472
} else {
473473
workingDir = runfilesDir;
474+
if (runfilesSupport != null) {
475+
runfilesSupport.getActionEnvironment().resolve(runEnvironment, env.getClientEnv());
476+
}
474477
List<String> args = computeArgs(env, targetToRun, commandLineArgs);
475478
try {
476479
constructCommandLine(

src/test/shell/integration/run_test.sh

+31
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,37 @@ EOF
487487
assert_starts_with "${tmpdir}/test_bazel_run_with_custom_tmpdir" "$(cat "${TEST_TMPDIR}/tmpdir_value")"
488488
}
489489

490+
function test_run_binary_with_env_attribute() {
491+
local -r pkg="pkg${LINENO}"
492+
mkdir -p ${pkg}
493+
cat > $pkg/BUILD <<'EOF'
494+
sh_binary(
495+
name = 't',
496+
srcs = [':t.sh'],
497+
data = [':t.dat'],
498+
env = {
499+
"ENV_A": "not_inherited",
500+
"ENV_C": "no_surprise",
501+
"ENV_DATA": "$(location :t.dat)",
502+
},
503+
)
504+
EOF
505+
cat > $pkg/t.sh <<'EOF'
506+
#!/bin/sh
507+
env
508+
cat $ENV_DATA
509+
exit 0
510+
EOF
511+
touch $pkg/t.dat
512+
chmod +x $pkg/t.sh
513+
ENV_B=surprise ENV_C=surprise bazel run //$pkg:t > $TEST_log \
514+
|| fail "expected test to pass"
515+
expect_log "ENV_A=not_inherited"
516+
expect_log "ENV_B=surprise"
517+
expect_log "ENV_C=no_surprise"
518+
expect_log "ENV_DATA=$pkg/t.dat"
519+
}
520+
490521
# Usage: assert_starts_with PREFIX STRING_TO_CHECK.
491522
# Asserts that `$1` is a prefix of `$2`.
492523
function assert_starts_with() {

0 commit comments

Comments
 (0)