Skip to content

Commit

Permalink
Preserve analysis cache through --test_env changes
Browse files Browse the repository at this point in the history
Fixes #7450

RELNOTES[INC]: Changing --test_env no longer invalidates the analysis cache. `ctx.configuration.test_env` may be empty for non-test rules and should not be used by such rules.

Closes #24316.

PiperOrigin-RevId: 696998208
Change-Id: Iac6b2ac3d6c84b1c9f2e0d5284c6ce4f38d961cf
  • Loading branch information
fmeum authored and copybara-github committed Nov 15, 2024
1 parent 8dfaa4f commit 17057fd
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 43 deletions.
1 change: 1 addition & 0 deletions src/main/java/com/google/devtools/build/lib/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/analysis:template_expansion_exception",
"//src/main/java/com/google/devtools/build/lib/analysis:test/coverage_artifacts_known_event",
"//src/main/java/com/google/devtools/build/lib/analysis:test/coverage_report_action_factory",
"//src/main/java/com/google/devtools/build/lib/analysis:test/test_configuration",
"//src/main/java/com/google/devtools/build/lib/analysis:top_level_artifact_context",
"//src/main/java/com/google/devtools/build/lib/analysis:transitive_info_collection",
"//src/main/java/com/google/devtools/build/lib/analysis:view_creation_failed_exception",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.google.devtools.build.lib.actions.CommandLineLimits;
import com.google.devtools.build.lib.analysis.BlazeDirectories;
import com.google.devtools.build.lib.analysis.PlatformOptions;
import com.google.devtools.build.lib.analysis.test.TestConfiguration.TestOptions;
import com.google.devtools.build.lib.buildeventstream.BuildEvent;
import com.google.devtools.build.lib.buildeventstream.BuildEventIdUtil;
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
Expand Down Expand Up @@ -165,9 +166,13 @@ public void reportInvalidOptions(EventHandler reporter) {
* be inherited from the client environment.
*/
private ActionEnvironment setupTestEnvironment() {
// We make a copy first to remove duplicate entries; last one wins.
if (!buildOptions.contains(TestOptions.class)) {
// TestOptions have been trimmed.
return ActionEnvironment.EMPTY;
}
// Order doesn't matter here as ActionEnvironment sorts by key.
Map<String, String> testEnv = new HashMap<>();
for (Map.Entry<String, String> entry : options.testEnvironment) {
for (Map.Entry<String, String> entry : buildOptions.get(TestOptions.class).testEnvironment) {
testEnv.put(entry.getKey(), entry.getValue());
}
return ActionEnvironment.split(testEnv);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,9 @@
import com.google.devtools.common.options.OptionMetadataTag;
import com.google.devtools.common.options.OptionsParsingException;
import com.google.devtools.common.options.TriState;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
Expand Down Expand Up @@ -420,24 +418,6 @@ public ExecConfigurationDistinguisherSchemeConverter() {
help = "Specifies a suffix to be added to the configuration directory.")
public String platformSuffix;

// TODO(bazel-team): The test environment is actually computed in BlazeRuntime and this option
// is not read anywhere else. Thus, it should be in a different options class, preferably one
// specific to the "test" command or maybe in its own configuration fragment.
@Option(
name = "test_env",
converter = Converters.OptionalAssignmentConverter.class,
allowMultiple = true,
defaultValue = "null",
documentationCategory = OptionDocumentationCategory.TESTING,
effectTags = {OptionEffectTag.TEST_RUNNER},
help =
"Specifies additional environment variables to be injected into the test runner "
+ "environment. Variables can be either specified by name, in which case its value "
+ "will be read from the Bazel client environment, or by the name=value pair. "
+ "This option can be used multiple times to specify several variables. "
+ "Used only by the 'bazel test' command.")
public List<Map.Entry<String, String>> testEnvironment;

// TODO(bazel-team): The set of available variables from the client environment for actions
// is computed independently in CommandEnvironment to inject a more restricted client
// environment to skyframe.
Expand Down Expand Up @@ -985,20 +965,6 @@ public ImmutableMap<String, String> getNormalizedCommandLineBuildVariables() {
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
}

// Normalizes list of map entries by keeping only the last entry for each key.
private static List<Map.Entry<String, String>> normalizeEntries(
List<Map.Entry<String, String>> entries) {
LinkedHashMap<String, String> normalizedEntries = new LinkedHashMap<>();
for (Map.Entry<String, String> entry : entries) {
normalizedEntries.put(entry.getKey(), entry.getValue());
}
// If we made no changes, return the same instance we got to reduce churn.
if (normalizedEntries.size() == entries.size()) {
return entries;
}
return normalizedEntries.entrySet().stream().map(SimpleEntry::new).collect(toImmutableList());
}

// Sort the map entries by key.
private static List<Map.Entry<String, String>> sortEntries(
List<Map.Entry<String, String>> entries) {
Expand Down Expand Up @@ -1056,7 +1022,6 @@ public CoreOptions getNormalized() {

result.actionEnvironment = normalizeEntries(actionEnvironment);
result.hostActionEnvironment = normalizeEntries(hostActionEnvironment);
result.testEnvironment = normalizeEntries(testEnvironment);
result.commandLineFlagAliases = sortEntries(normalizeEntries(commandLineFlagAliases));

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.Options;
import com.google.devtools.common.options.OptionsBase;
import java.util.AbstractMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

/** Command-line build options for a Blaze module. */
Expand Down Expand Up @@ -89,6 +92,25 @@ protected static ImmutableList<String> dedupAndSort(@Nullable List<String> value
return result.equals(values) ? ImmutableList.copyOf(values) : result;
}

/**
* Helper method for subclasses to normalize list of map entries by keeping only the last entry
* for each key. The order of the entries is preserved.
*/
protected static List<Map.Entry<String, String>> normalizeEntries(
List<Map.Entry<String, String>> entries) {
LinkedHashMap<String, String> normalizedEntries = new LinkedHashMap<>();
for (Map.Entry<String, String> entry : entries) {
normalizedEntries.put(entry.getKey(), entry.getValue());
}
// If we made no changes, return the same instance we got to reduce churn.
if (normalizedEntries.size() == entries.size()) {
return entries;
}
return normalizedEntries.entrySet().stream()
.map(AbstractMap.SimpleEntry::new)
.collect(toImmutableList());
}

/** Tracks limitations on referring to an option in a {@code config_setting}. */
// TODO(bazel-team): There will likely also be a need to customize whether or not an option is
// visible to users for setting on the command line (or perhaps even in a test of a Starlark
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import com.google.devtools.build.lib.packages.TestTimeout;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.RegexFilter;
import com.google.devtools.common.options.Converters;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionDefinition;
import com.google.devtools.common.options.OptionDocumentationCategory;
Expand Down Expand Up @@ -84,6 +85,21 @@ public static class TestOptions extends FragmentOptions {
OptionsParser.getOptionDefinitionByName(
TestOptions.class, "experimental_retain_test_configuration_across_testonly"));

@Option(
name = "test_env",
converter = Converters.OptionalAssignmentConverter.class,
allowMultiple = true,
defaultValue = "null",
documentationCategory = OptionDocumentationCategory.TESTING,
effectTags = {OptionEffectTag.TEST_RUNNER},
help =
"Specifies additional environment variables to be injected into the test runner "
+ "environment. Variables can be either specified by name, in which case its value "
+ "will be read from the Bazel client environment, or by the name=value pair. "
+ "This option can be used multiple times to specify several variables. "
+ "Used only by the 'bazel test' command.")
public List<Map.Entry<String, String>> testEnvironment;

@Option(
name = "test_timeout",
defaultValue = "-1",
Expand Down Expand Up @@ -346,6 +362,13 @@ public static class TestOptions extends FragmentOptions {
effectTags = {OptionEffectTag.EXECUTION},
help = "If true, Bazel will allow local tests to run.")
public boolean allowLocalTests;

@Override
public TestOptions getNormalized() {
TestOptions result = (TestOptions) clone();
result.testEnvironment = normalizeEntries(testEnvironment);
return result;
}
}

private final TestOptions options;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.google.devtools.build.lib.analysis.BuildInfoEvent;
import com.google.devtools.build.lib.analysis.config.AdditionalConfigurationChangeEvent;
import com.google.devtools.build.lib.analysis.config.CoreOptions;
import com.google.devtools.build.lib.analysis.test.TestConfiguration.TestOptions;
import com.google.devtools.build.lib.bazel.repository.downloader.DelegatingDownloader;
import com.google.devtools.build.lib.bazel.repository.downloader.HttpDownloader;
import com.google.devtools.build.lib.buildtool.BuildRequestOptions;
Expand Down Expand Up @@ -329,7 +330,7 @@ public void exit(AbruptExitException exception) {
}
}
for (Map.Entry<String, String> entry :
options.getOptions(CoreOptions.class).testEnvironment) {
options.getOptions(TestOptions.class).testEnvironment) {
if (entry.getValue() == null) {
visibleTestEnv.add(entry.getKey());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,28 @@ public void testStarlarkWithTestEnvOptions() throws Exception {
MyInfo = provider()
def _test_rule_impl(ctx):
return MyInfo(test_env = ctx.configuration.test_env)
test_rule = rule(
out = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(out, "exit 0", is_executable = True)
return [
DefaultInfo(executable = out),
MyInfo(test_env = ctx.configuration.test_env),
]
my_test = rule(
implementation = _test_rule_impl,
attrs = {},
test = True,
)
""");

scratch.file(
"examples/config_starlark/BUILD",
"""
load("//examples/rule:config_test.bzl", "test_rule")
load("//examples/rule:config_test.bzl", "my_test")
package(default_visibility = ["//visibility:public"])
test_rule(
my_test(
name = "my_target",
)
""");
Expand Down
1 change: 1 addition & 0 deletions src/test/java/com/google/devtools/build/lib/remote/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/analysis:constraints/constraint_constants",
"//src/main/java/com/google/devtools/build/lib/analysis:server_directories",
"//src/main/java/com/google/devtools/build/lib/analysis:symlink_entry",
"//src/main/java/com/google/devtools/build/lib/analysis:test/test_configuration",
"//src/main/java/com/google/devtools/build/lib/analysis/platform",
"//src/main/java/com/google/devtools/build/lib/authandtls",
"//src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import com.google.devtools.build.lib.analysis.BlazeDirectories;
import com.google.devtools.build.lib.analysis.ServerDirectories;
import com.google.devtools.build.lib.analysis.config.CoreOptions;
import com.google.devtools.build.lib.analysis.test.TestConfiguration.TestOptions;
import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions;
import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperEnvironment;
import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialModule;
Expand Down Expand Up @@ -139,6 +140,7 @@ private static CommandEnvironment createTestCommandEnvironment(
PackageOptions packageOptions = Options.getDefaults(PackageOptions.class);
ClientOptions clientOptions = Options.getDefaults(ClientOptions.class);
ExecutionOptions executionOptions = Options.getDefaults(ExecutionOptions.class);
TestOptions testOptions = Options.getDefaults(TestOptions.class);

AuthAndTLSOptions authAndTLSOptions = Options.getDefaults(AuthAndTLSOptions.class);

Expand All @@ -150,6 +152,7 @@ private static CommandEnvironment createTestCommandEnvironment(
when(options.getOptions(RemoteOptions.class)).thenReturn(remoteOptions);
when(options.getOptions(AuthAndTLSOptions.class)).thenReturn(authAndTLSOptions);
when(options.getOptions(ExecutionOptions.class)).thenReturn(executionOptions);
when(options.getOptions(TestOptions.class)).thenReturn(testOptions);

String productName = "bazel";
Scratch scratch = new Scratch(new InMemoryFileSystem(DigestHashFunction.SHA256));
Expand Down
27 changes: 27 additions & 0 deletions src/test/shell/bazel/bazel_test_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1116,4 +1116,31 @@ EOF
expect_log "-- Test exited prematurely (TEST_PREMATURE_EXIT_FILE exists) --"
}

function test_test_env_change_preserves_cache() {
local -r pkg="pkg${LINENO}"
mkdir -p "${pkg}"
cat > "$pkg/BUILD" <<'EOF'
load(":defs.bzl", "my_rule")
my_rule(
name = "my_rule",
)
EOF
cat > "$pkg/defs.bzl" <<'EOF'
def _my_rule_impl(ctx):
print("my_rule is being analyzed")
out = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(out, "hi")
return [DefaultInfo(files = depset([out]))]
my_rule = rule(_my_rule_impl)
EOF

bazel build "${pkg}:my_rule" >$TEST_log 2>&1 \
|| fail "expected build to pass"
expect_log "my_rule is being analyzed"

bazel build --test_env=FOO=bar "${pkg}:my_rule" >$TEST_log 2>&1 \
|| fail "expected build to pass"
expect_not_log "my_rule is being analyzed"
}

run_suite "bazel test tests"

0 comments on commit 17057fd

Please sign in to comment.