diff --git a/src/main/java/com/google/devtools/build/lib/actions/ExecutionRequirements.java b/src/main/java/com/google/devtools/build/lib/actions/ExecutionRequirements.java index e63283913b8cbd..330dffa665e930 100644 --- a/src/main/java/com/google/devtools/build/lib/actions/ExecutionRequirements.java +++ b/src/main/java/com/google/devtools/build/lib/actions/ExecutionRequirements.java @@ -18,11 +18,23 @@ import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; +import com.google.devtools.build.lib.packages.Rule; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Strings used to express requirements on action execution environments. + * + *
    + * If you are adding a new execution requirement, pay attention to the following: + *
  1. If its name starts with one of the supported prefixes, then it can be also used as a tag on + * a target and will be propagated to the execution requirements, see for prefixes {@link + * com.google.devtools.build.lib.packages.TargetUtils#getExecutionInfo(Rule)} + *
  2. If this is a potentially conflicting execution requirements, e.g. you are adding a pair + * 'requires-x' and 'block-x', you MUST take care of a potential conflict in the Executor that + * is using new execution requirements. As an example, see {@link + * Spawns#requiresNetwork(com.google.devtools.build.lib.actions.Spawn, boolean)}. + *
*/ public class ExecutionRequirements { diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkActionFactory.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkActionFactory.java index 29b7e5bc711df8..16dea75ab02648 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkActionFactory.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkActionFactory.java @@ -558,15 +558,14 @@ private void registerStarlarkAction( if (EvalUtils.toBoolean(useDefaultShellEnv)) { builder.useDefaultShellEnvironment(); } - if (executionRequirementsUnchecked != Runtime.NONE) { - builder.setExecutionInfo( - TargetUtils.filter( - SkylarkDict.castSkylarkDictOrNoneToDict( - executionRequirementsUnchecked, - String.class, - String.class, - "execution_requirements"))); - } + + ImmutableMap executionInfo = + TargetUtils.getFilteredExecutionInfo( + executionRequirementsUnchecked, + ruleContext.getRule(), + starlarkSemantics.incompatibleAllowTagsPropagation()); + builder.setExecutionInfo(executionInfo); + if (inputManifestsUnchecked != Runtime.NONE) { for (RunfilesSupplier supplier : SkylarkList.castSkylarkListOrNoneToList( inputManifestsUnchecked, RunfilesSupplier.class, "runfiles suppliers")) { diff --git a/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java b/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java index 868697d347871b..73879e358d9d5e 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java +++ b/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java @@ -176,6 +176,21 @@ public class StarlarkSemanticsOptions extends OptionsBase implements Serializabl + "statement.") public boolean incompatibleBzlDisallowLoadAfterStatement; + @Option( + name = "incompatible_allow_tags_propagation", + defaultValue = "false", + documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS, + effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS}, + metadataTags = { + OptionMetadataTag.INCOMPATIBLE_CHANGE, + OptionMetadataTag.TRIGGERED_BY_ALL_INCOMPATIBLE_CHANGES + }, + help = + "If set to true, tags will be propagated from a target to the actions' execution" + + " requirements; otherwise tags are not propagated. See" + + " https://github.com/bazelbuild/bazel/issues/8830 for details.") + public boolean incompatibleAllowTagsPropagation; + @Option( name = "incompatible_depset_union", defaultValue = "true", @@ -697,6 +712,7 @@ public StarlarkSemantics toSkylarkSemantics() { .incompatibleDisallowSplitEmptySeparator(incompatibleDisallowSplitEmptySeparator) .incompatibleDisallowDictLookupUnhashableKeys( incompatibleDisallowDictLookupUnhashableKeys) + .incompatibleAllowTagsPropagation(incompatibleAllowTagsPropagation) .build(); return INTERNER.intern(semantics); } diff --git a/src/main/java/com/google/devtools/build/lib/packages/TargetUtils.java b/src/main/java/com/google/devtools/build/lib/packages/TargetUtils.java index acb035268a6054..3ab123a4eaa8eb 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/TargetUtils.java +++ b/src/main/java/com/google/devtools/build/lib/packages/TargetUtils.java @@ -24,6 +24,8 @@ import com.google.common.collect.Maps; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.events.Location; +import com.google.devtools.build.lib.syntax.EvalException; +import com.google.devtools.build.lib.syntax.SkylarkDict; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.Pair; import java.util.ArrayList; @@ -46,18 +48,15 @@ public final class TargetUtils { // some internal tags that we don't allow to be set on targets. We also don't want to // exhaustively enumerate all the legal values here. Right now, only a ~small set of tags is // recognized by Bazel. - private static final Predicate LEGAL_EXEC_INFO_KEYS = new Predicate() { - @Override - public boolean apply(String tag) { - return tag.startsWith("block-") - || tag.startsWith("requires-") - || tag.startsWith("no-") - || tag.startsWith("supports-") - || tag.startsWith("disable-") - || tag.equals("local") - || tag.startsWith("cpu:"); - } - }; + private static boolean legalExecInfoKeys(String tag) { + return tag.startsWith("block-") + || tag.startsWith("requires-") + || tag.startsWith("no-") + || tag.startsWith("supports-") + || tag.startsWith("disable-") + || tag.equals("local") + || tag.startsWith("cpu:"); + } private TargetUtils() {} // Uninstantiable. @@ -220,31 +219,64 @@ private static boolean hasConstraint(Rule rule, String keyword) { } /** - * Returns the execution info. These include execution requirement tags ('block-*', 'requires-*', - * 'no-*', 'supports-*', 'disable-*', 'local', and 'cpu:*') as keys with empty values. + * Returns the execution info from the tags declared on the target. These include only some tags + * {@link #legalExecInfoKeys} as keys with empty values. */ public static Map getExecutionInfo(Rule rule) { // tags may contain duplicate values. Map map = new HashMap<>(); for (String tag : NonconfigurableAttributeMapper.of(rule).get(CONSTRAINTS_ATTR, Type.STRING_LIST)) { - // We don't want to pollute the execution info with random things, and we also need to reserve - // some internal tags that we don't allow to be set on targets. We also don't want to - // exhaustively enumerate all the legal values here. Right now, only a ~small set of tags is - // recognized by Bazel. - if (LEGAL_EXEC_INFO_KEYS.apply(tag)) { + if (legalExecInfoKeys(tag)) { map.put(tag, ""); } } return ImmutableMap.copyOf(map); } + /** + * Returns the execution info, obtained from the rule's tags and the execution requirements + * provided. Only supported tags are included into the execution info, see {@link + * #legalExecInfoKeys}. + * + * @param executionRequirementsUnchecked execution_requirements of a rule, expected to be of a + * {@code SkylarkDict} type, null or {@link + * com.google.devtools.build.lib.syntax.Runtime#NONE} + * @param rule a rule instance to get tags from + * @param incompatibleAllowTagsPropagation if set to true, tags will be propagated from a target + * to the actions' execution requirements, for more details {@see + * SkylarkSematicOptions#incompatibleAllowTagsPropagation} + */ + public static ImmutableMap getFilteredExecutionInfo( + Object executionRequirementsUnchecked, Rule rule, boolean incompatibleAllowTagsPropagation) + throws EvalException { + Map checkedExecutionRequirements = + TargetUtils.filter( + SkylarkDict.castSkylarkDictOrNoneToDict( + executionRequirementsUnchecked, + String.class, + String.class, + "execution_requirements")); + + Map executionInfoBuilder = new HashMap<>(); + // adding filtered execution requirements to the execution info map + executionInfoBuilder.putAll(checkedExecutionRequirements); + + if (incompatibleAllowTagsPropagation) { + Map checkedTags = getExecutionInfo(rule); + // merging filtered tags to the execution info map avoiding duplicates + checkedTags.forEach(executionInfoBuilder::putIfAbsent); + } + + return ImmutableMap.copyOf(executionInfoBuilder); + } + /** * Returns the execution info. These include execution requirement tags ('block-*', 'requires-*', * 'no-*', 'supports-*', 'disable-*', 'local', and 'cpu:*') as keys with empty values. */ public static Map filter(Map executionInfo) { - return Maps.filterKeys(executionInfo, LEGAL_EXEC_INFO_KEYS); + return Maps.filterKeys(executionInfo, TargetUtils::legalExecInfoKeys); } /** diff --git a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java index 87b8c5f4b7bf4a..f2181f4d400ec0 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java @@ -60,6 +60,7 @@ public enum FlagIdentifier { INCOMPATIBLE_OBJC_FRAMEWORK_CLEANUP(StarlarkSemantics::incompatibleObjcFrameworkCleanup), INCOMPATIBLE_DISALLOW_RULE_EXECUTION_PLATFORM_CONSTRAINTS_ALLOWED( StarlarkSemantics::incompatibleDisallowRuleExecutionPlatformConstraintsAllowed), + INCOMPATIBLE_ALLOW_TAGS_PROPAGATION(StarlarkSemantics::incompatibleAllowTagsPropagation), NONE(null); // Using a Function here makes the enum definitions far cleaner, and, since this is @@ -210,6 +211,8 @@ public boolean flagValue(FlagIdentifier flagIdentifier) { public abstract boolean incompatibleDisallowDictLookupUnhashableKeys(); + public abstract boolean incompatibleAllowTagsPropagation(); + @Memoized @Override public abstract int hashCode(); @@ -287,6 +290,7 @@ public static Builder builderWithDefaults() { .incompatibleRestrictStringEscapes(false) .incompatibleDisallowSplitEmptySeparator(false) .incompatibleDisallowDictLookupUnhashableKeys(false) + .incompatibleAllowTagsPropagation(false) .build(); /** Builder for {@link StarlarkSemantics}. All fields are mandatory. */ @@ -312,6 +316,8 @@ public abstract static class Builder { public abstract Builder experimentalStarlarkUnusedInputsList(boolean value); + public abstract Builder incompatibleAllowTagsPropagation(boolean value); + public abstract Builder incompatibleBzlDisallowLoadAfterStatement(boolean value); public abstract Builder incompatibleDepsetIsNotIterable(boolean value); diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTest.java index 5826f5a75ea77f..0788a8970a213a 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTest.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTest.java @@ -114,6 +114,14 @@ public void testDestinationArtifactIsOutput() { assertThat(outputs).containsExactly(destinationArtifact); } + @Test + public void testExecutionInfoCopied() { + SpawnAction copyFromWelcomeToDestination = + createCopyFromWelcomeToDestination(ImmutableMap.of()); + Map executionInfo = copyFromWelcomeToDestination.getExecutionInfo(); + assertThat(executionInfo).containsExactly("local", ""); + } + @Test public void testBuilder() throws Exception { Artifact input = getSourceArtifact("input"); @@ -288,7 +296,7 @@ public void testExtraActionInfo() throws Exception { assertThat(info.getMnemonic()).isEqualTo("Dummy"); SpawnInfo spawnInfo = info.getExtension(SpawnInfo.spawnInfo); - assertThat(spawnInfo).isNotNull(); + assertThat(info.hasExtension(SpawnInfo.spawnInfo)).isTrue(); assertThat(spawnInfo.getArgumentList()) .containsExactlyElementsIn(action.getArguments()); diff --git a/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java b/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java index fbd3c0a5b06ae5..995e0f7a55cdb8 100644 --- a/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java +++ b/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java @@ -133,6 +133,7 @@ private static StarlarkSemanticsOptions buildRandomOptions(Random rand) throws E "--experimental_platforms_api=" + rand.nextBoolean(), "--experimental_starlark_config_transitions=" + rand.nextBoolean(), "--experimental_starlark_unused_inputs_list=" + rand.nextBoolean(), + "--incompatible_allow_tags_propagation=" + rand.nextBoolean(), "--incompatible_bzl_disallow_load_after_statement=" + rand.nextBoolean(), "--incompatible_depset_for_libraries_to_link_getter=" + rand.nextBoolean(), "--incompatible_depset_is_not_iterable=" + rand.nextBoolean(), @@ -188,6 +189,7 @@ private static StarlarkSemantics buildRandomSemantics(Random rand) { .experimentalPlatformsApi(rand.nextBoolean()) .experimentalStarlarkConfigTransitions(rand.nextBoolean()) .experimentalStarlarkUnusedInputsList(rand.nextBoolean()) + .incompatibleAllowTagsPropagation(rand.nextBoolean()) .incompatibleBzlDisallowLoadAfterStatement(rand.nextBoolean()) .incompatibleDepsetForLibrariesToLinkGetter(rand.nextBoolean()) .incompatibleDepsetIsNotIterable(rand.nextBoolean()) diff --git a/src/test/java/com/google/devtools/build/lib/packages/TargetUtilsTest.java b/src/test/java/com/google/devtools/build/lib/packages/TargetUtilsTest.java index eb49366cf26fb0..058524e87023bd 100644 --- a/src/test/java/com/google/devtools/build/lib/packages/TargetUtilsTest.java +++ b/src/test/java/com/google/devtools/build/lib/packages/TargetUtilsTest.java @@ -18,6 +18,8 @@ import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.devtools.build.lib.packages.util.PackageLoadingTestCase; +import com.google.devtools.build.lib.syntax.Runtime; +import com.google.devtools.build.lib.syntax.SkylarkDict; import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; @@ -96,4 +98,178 @@ public void testExecutionInfo() throws Exception { execInfo = TargetUtils.getExecutionInfo(tag1b); assertThat(execInfo).containsExactly("local", "", "cpu:4", ""); } + + @Test + public void testExecutionInfo_withPrefixSupports() throws Exception { + scratch.file( + "tests/BUILD", + "sh_binary(name = 'with-prefix-supports', srcs=['sh.sh'], tags=['supports-workers'," + + " 'supports-whatever', 'my-tag'])"); + + Rule withSupportsPrefix = (Rule) getTarget("//tests:with-prefix-supports"); + + Map execInfo = TargetUtils.getExecutionInfo(withSupportsPrefix); + assertThat(execInfo).containsExactly("supports-whatever", "", "supports-workers", ""); + } + + @Test + public void testExecutionInfo_withPrefixDisable() throws Exception { + scratch.file( + "tests/BUILD", + "sh_binary(name = 'with-prefix-disable', srcs=['sh.sh'], tags=['disable-local-prefetch'," + + " 'disable-something-else', 'another-tag'])"); + + Rule withDisablePrefix = (Rule) getTarget("//tests:with-prefix-disable"); + + Map execInfo = TargetUtils.getExecutionInfo(withDisablePrefix); + assertThat(execInfo) + .containsExactly("disable-local-prefetch", "", "disable-something-else", ""); + } + + @Test + public void testExecutionInfo_withPrefixNo() throws Exception { + scratch.file( + "tests/BUILD", + "sh_binary(name = 'with-prefix-no', srcs=['sh.sh'], tags=['no-remote-imaginary-flag'," + + " 'no-sandbox', 'unknown'])"); + + Rule withNoPrefix = (Rule) getTarget("//tests:with-prefix-no"); + + Map execInfo = TargetUtils.getExecutionInfo(withNoPrefix); + assertThat(execInfo).containsExactly("no-remote-imaginary-flag", "", "no-sandbox", ""); + } + + @Test + public void testExecutionInfo_withPrefixRequires() throws Exception { + scratch.file( + "tests/BUILD", + "sh_binary(name = 'with-prefix-requires', srcs=['sh.sh'], tags=['requires-network'," + + " 'requires-sunlight', 'test-only'])"); + + Rule withRequiresPrefix = (Rule) getTarget("//tests:with-prefix-requires"); + + Map execInfo = TargetUtils.getExecutionInfo(withRequiresPrefix); + assertThat(execInfo).containsExactly("requires-network", "", "requires-sunlight", ""); + } + + @Test + public void testExecutionInfo_withPrefixBlock() throws Exception { + scratch.file( + "tests/BUILD", + "sh_binary(name = 'with-prefix-block', srcs=['sh.sh'], tags=['block-some-feature'," + + " 'block-network', 'wrong-tag'])"); + + Rule withBlockPrefix = (Rule) getTarget("//tests:with-prefix-block"); + + Map execInfo = TargetUtils.getExecutionInfo(withBlockPrefix); + assertThat(execInfo).containsExactly("block-network", "", "block-some-feature", ""); + } + + @Test + public void testExecutionInfo_withPrefixCpu() throws Exception { + scratch.file( + "tests/BUILD", + "sh_binary(name = 'with-prefix-cpu', srcs=['sh.sh'], tags=['cpu:123', 'wrong-tag'])"); + + Rule withCpuPrefix = (Rule) getTarget("//tests:with-prefix-cpu"); + + Map execInfo = TargetUtils.getExecutionInfo(withCpuPrefix); + assertThat(execInfo).containsExactly("cpu:123", ""); + } + + @Test + public void testExecutionInfo_withLocalTag() throws Exception { + scratch.file( + "tests/BUILD", + "sh_binary(name = 'with-local-tag', srcs=['sh.sh'], tags=['local', 'some-tag'])"); + + Rule withLocal = (Rule) getTarget("//tests:with-local-tag"); + + Map execInfo = TargetUtils.getExecutionInfo(withLocal); + assertThat(execInfo).containsExactly("local", ""); + } + + @Test + public void testFilteredExecutionInfo_FromUncheckedExecRequirements() throws Exception { + scratch.file("tests/BUILD", "sh_binary(name = 'no-tag', srcs=['sh.sh'])"); + + Rule noTag = (Rule) getTarget("//tests:no-tag"); + + Map execInfo = + TargetUtils.getFilteredExecutionInfo( + SkylarkDict.of(null, "supports-worker", "1"), noTag, /* allowTagsPropagation */ true); + assertThat(execInfo).containsExactly("supports-worker", "1"); + + execInfo = + TargetUtils.getFilteredExecutionInfo( + SkylarkDict.of(null, "some-custom-tag", "1", "no-cache", "1"), + noTag, + /* allowTagsPropagation */ true); + assertThat(execInfo).containsExactly("no-cache", "1"); + } + + @Test + public void testFilteredExecutionInfo() throws Exception { + scratch.file( + "tests/BUILD", + "sh_binary(name = 'tag1', srcs=['sh.sh'], tags=['supports-workers', 'no-cache'])"); + Rule tag1 = (Rule) getTarget("//tests:tag1"); + SkylarkDict executionRequirementsUnchecked = + SkylarkDict.of(null, "no-remote", "1"); + + Map execInfo = + TargetUtils.getFilteredExecutionInfo( + executionRequirementsUnchecked, tag1, /* allowTagsPropagation */ true); + + assertThat(execInfo).containsExactly("no-cache", "", "supports-workers", "", "no-remote", "1"); + } + + @Test + public void testFilteredExecutionInfo_withDuplicateTags() throws Exception { + scratch.file( + "tests/BUILD", + "sh_binary(name = 'tag1', srcs=['sh.sh'], tags=['supports-workers', 'no-cache'])"); + Rule tag1 = (Rule) getTarget("//tests:tag1"); + SkylarkDict executionRequirementsUnchecked = + SkylarkDict.of(null, "no-cache", "1"); + + Map execInfo = + TargetUtils.getFilteredExecutionInfo( + executionRequirementsUnchecked, tag1, /* allowTagsPropagation */ true); + + assertThat(execInfo).containsExactly("no-cache", "1", "supports-workers", ""); + } + + @Test + public void testFilteredExecutionInfo_WithNullUncheckedExecRequirements() throws Exception { + scratch.file( + "tests/BUILD", + "sh_binary(name = 'tag1', srcs=['sh.sh'], tags=['supports-workers', 'no-cache'])"); + Rule tag1 = (Rule) getTarget("//tests:tag1"); + + Map execInfo = + TargetUtils.getFilteredExecutionInfo(null, tag1, /* allowTagsPropagation */ true); + assertThat(execInfo).containsExactly("no-cache", "", "supports-workers", ""); + + execInfo = + TargetUtils.getFilteredExecutionInfo(Runtime.NONE, tag1, /* allowTagsPropagation */ true); + assertThat(execInfo).containsExactly("no-cache", "", "supports-workers", ""); + } + + @Test + public void testFilteredExecutionInfoWhenIncompatibleFlagDisabled() throws Exception { + // when --incompatible_allow_tags_propagation=false + scratch.file( + "tests/BUILD", + "sh_binary(name = 'tag1', srcs=['sh.sh'], tags=['supports-workers', 'no-cache'])"); + Rule tag1 = (Rule) getTarget("//tests:tag1"); + SkylarkDict executionRequirementsUnchecked = + SkylarkDict.of(null, "no-remote", "1"); + + Map execInfo = + TargetUtils.getFilteredExecutionInfo( + executionRequirementsUnchecked, tag1, /* allowTagsPropagation */ false); + + assertThat(execInfo).containsExactly("no-remote", "1"); + } } diff --git a/src/test/shell/bazel/BUILD b/src/test/shell/bazel/BUILD index 0d19873cb00659..b918b54b65a1cf 100644 --- a/src/test/shell/bazel/BUILD +++ b/src/test/shell/bazel/BUILD @@ -710,6 +710,14 @@ sh_test( tags = ["no_windows"], ) +sh_test( + name = "tags_propagation_skylark_test", + size = "large", + srcs = ["tags_propagation_skylark_test.sh"], + data = [":test-deps"], + tags = ["no_windows"], +) + sh_test( name = "disk_cache_test", size = "small", diff --git a/src/test/shell/bazel/tags_propagation_skylark_test.sh b/src/test/shell/bazel/tags_propagation_skylark_test.sh new file mode 100755 index 00000000000000..8ae1568eed6aa0 --- /dev/null +++ b/src/test/shell/bazel/tags_propagation_skylark_test.sh @@ -0,0 +1,175 @@ +#!/bin/bash +# +# Copyright 2019 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. + +# Tests target's tags propagation with rules defined in Skylark. +# Tests for https://github.com/bazelbuild/bazel/issues/7766 + +# Load the test setup defined in the parent directory +CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${CURRENT_DIR}/../integration_test_setup.sh" \ + || { echo "integration_test_setup.sh not found!" >&2; exit 1; } + +# Test a basic skylark ctx.actions.run_shell rule which has tags, that should be propagated +function test_tags_propagated_to_run_shell() { + mkdir -p test + cat << EOF >> test/BUILD +load(":skylark.bzl", "test_rule") + +test_rule( + name = "test", + out = "output.txt", + tags = ["no-cache", "no-remote", "local"] +) +EOF + + cat << 'EOF' >> test/skylark.bzl +def _test_impl(ctx): + ctx.actions.run_shell(outputs = [ctx.outputs.out], + command = ["touch", ctx.outputs.out.path]) + files_to_build = depset([ctx.outputs.out]) + return DefaultInfo( + files = files_to_build, + ) + +test_rule = rule( + implementation=_test_impl, + attrs = { + "out": attr.output(mandatory = True), + }, +) +EOF + + bazel aquery --incompatible_allow_tags_propagation '//test:test' > output1 2> $TEST_log \ + || fail "should have generated output successfully" + + assert_contains "ExecutionInfo: {local: '', no-cache: '', no-remote: ''}" output1 +} + +# Test a basic skylark ctx.actions.run rule which has tags, that should be propagated +function test_tags_propagated_to_run() { + mkdir -p test + cat << EOF >> test/BUILD +load(":skylark.bzl", "test_rule") + +test_rule( + name = "test", + out = "output.txt", + tags = ["no-cache", "no-remote", "no-sandbox", "requires-network", "local"] +) +EOF + + cat << 'EOF' >> test/skylark.bzl +def _test_impl(ctx): + ctx.actions.run( + outputs = [ctx.outputs.out], + executable = 'dummy') + files_to_build = depset([ctx.outputs.out]) + return DefaultInfo( + files = files_to_build, + ) + +test_rule = rule( + implementation=_test_impl, + attrs = { + "out": attr.output(mandatory = True), + }, +) +EOF + + bazel aquery --incompatible_allow_tags_propagation '//test:test' > output1 2> $TEST_log \ + || fail "should have generated output successfully" + + assert_contains "ExecutionInfo: {local: '', no-cache: '', no-remote: '', no-sandbox: '', requires-network: ''}" output1 +} + +# Test a basic skylark ctx.actions.run rule which has tags, that should be propagated, +# when the rule also has execution_info +function test_tags_propagated_to_run_with_exec_info_in_rule() { + mkdir -p test + cat << EOF >> test/BUILD +load(":skylark.bzl", "test_rule") + +test_rule( + name = "test", + out = "output.txt", + tags = ["no-cache", "no-remote", "custom-tag-1", "requires-network", "local"] +) +EOF + + cat << 'EOF' >> test/skylark.bzl +def _test_impl(ctx): + ctx.actions.run( + outputs = [ctx.outputs.out], + executable = 'dummy', + execution_requirements = {"requires-x": "", "custom-tag-whatever": "", "no-cache": "1"}) + files_to_build = depset([ctx.outputs.out]) + return DefaultInfo( + files = files_to_build, + ) + +test_rule = rule( + implementation=_test_impl, + attrs = { + "out": attr.output(mandatory = True), + }, +) +EOF + + bazel aquery --incompatible_allow_tags_propagation '//test:test' > output1 2> $TEST_log \ + || fail "should have generated output successfully" + + assert_contains "ExecutionInfo: {local: '', no-cache: 1, no-remote: '', requires-network: '', requires-x: ''}" output1 +} + +# Test a basic skylark ctx.actions.run rule which has tags, that should not be propagated +# as --incompatible_allow_tags_propagation flag set to false +function test_tags_not_propagated_to_run_when_incompatible_flag_off() { + mkdir -p test + cat << EOF >> test/BUILD +load(":skylark.bzl", "test_rule") + +test_rule( + name = "test", + out = "output.txt", + tags = ["no-cache", "no-remote", "no-sandbox", "requires-network", "local"] +) +EOF + + cat << 'EOF' >> test/skylark.bzl +def _test_impl(ctx): + ctx.actions.run( + outputs = [ctx.outputs.out], + executable = 'dummy') + files_to_build = depset([ctx.outputs.out]) + return DefaultInfo( + files = files_to_build, + ) + +test_rule = rule( + implementation=_test_impl, + attrs = { + "out": attr.output(mandatory = True), + }, +) +EOF + + bazel aquery --incompatible_allow_tags_propagation=false '//test:test' > output1 2> $TEST_log \ + || fail "should have generated output successfully" + + assert_not_contains "ExecutionInfo: {}" output1 +} + +run_suite "tags propagation: skylark rule tests"