blob: a3797e3005211a235afc1101e8084838bfd97bc4 [file] [log] [blame]
// Copyright 2020 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.
package com.google.devtools.build.lib.buildtool;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.ViewCreationFailedException;
import com.google.devtools.build.lib.buildtool.util.BuildIntegrationTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Tests use of the --target_environment flag.
*/
@RunWith(JUnit4.class)
public class EnvironmentRestrictedBuildTest extends BuildIntegrationTestCase {
@Before
public final void addNoBuildOption() throws Exception {
addOptions("--nobuild"); // Target enforcement happens before the execution phase.
}
private void writeEnvironmentRules(String... defaults) throws Exception {
StringBuilder defaultsBuilder = new StringBuilder();
for (String defaultEnv : defaults) {
defaultsBuilder.append("'" + defaultEnv + "', ");
}
write("buildenv/BUILD",
"environment_group(",
" name = 'group',",
" environments = [':one', ':two'],",
" defaults = [" + defaultsBuilder + "])",
"environment(name = 'one')",
"environment(name = 'two')");
}
@Test
public void testTargetEnvironmentError() throws Exception {
writeEnvironmentRules();
write("foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'])");
addOptions("--target_environment=//buildenv:one");
assertThat(assertThrows(ViewCreationFailedException.class, () -> buildTarget("//foo:bar")))
.hasMessageThat()
.contains(""
+ "//foo:bar does not support:\n"
+ " //buildenv:one");
}
@Test
public void testTargetEnvironmentSuccess() throws Exception {
writeEnvironmentRules();
write("foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'], compatible_with = ['//buildenv:one'])");
addOptions("--target_environment=//buildenv:one");
buildTarget("//foo:bar");
assertThat(getResult().getSuccess()).isTrue();
}
@Test
public void testMultipleTargetEnvironments() throws Exception {
writeEnvironmentRules();
write("foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'], compatible_with = ['//buildenv:one'])");
addOptions("--target_environment=//buildenv:one", "--target_environment=//buildenv:two");
assertThat(assertThrows(ViewCreationFailedException.class, () -> buildTarget("//foo:bar")))
.hasMessageThat()
.contains(""
+ "//foo:bar does not support:\n"
+ " //buildenv:two");
}
@Test
public void testTargetEnvironmentIsDefault() throws Exception {
writeEnvironmentRules(":one");
write("foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'])");
addOptions("--target_environment=//buildenv:one");
buildTarget("//foo:bar");
assertThat(getResult().getSuccess()).isTrue();
}
@Test
public void testEmptyTargetEnvironment() throws Exception {
writeEnvironmentRules();
write("foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'])");
buildTarget("//foo:bar");
assertThat(getResult().getSuccess()).isTrue();
}
@Test
public void testOnlySomeTargetsQualify() throws Exception {
writeEnvironmentRules();
write("foo/BUILD",
"sh_library(name = 'good_bar', srcs = ['bar.sh'], compatible_with = ['//buildenv:one'])",
"sh_library(name = 'bad_bar', srcs = ['bar.sh'], compatible_with = ['//buildenv:two'])");
addOptions("--target_environment=//buildenv:one");
assertThat(assertThrows(ViewCreationFailedException.class, () -> buildTarget("//foo:all")))
.hasMessageThat()
.contains(""
+ "//foo:bad_bar does not support:\n"
+ " //buildenv:one");
}
@Test
public void testNoConstraintEnforcement() throws Exception {
writeEnvironmentRules();
write("foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'])");
addOptions("--target_environment=//buildenv:one", "--noenforce_constraints");
buildTarget("//foo:bar");
assertThat(getResult().getSuccess()).isTrue();
}
@Test
public void testFlagUsesNonexistentTarget() throws Exception {
writeEnvironmentRules();
write("foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'])");
addOptions("--target_environment=//buildenv:nada");
assertThat(assertThrows(ViewCreationFailedException.class, () -> buildTarget("//foo:bar")))
.hasMessageThat()
.contains("invalid target environment: no such target '//buildenv:nada'");
}
@Test
public void testFlagUsesWrongTargetType() throws Exception {
write("foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'])");
addOptions("--target_environment=//foo:bar");
assertThat(assertThrows(ViewCreationFailedException.class, () -> buildTarget("//foo:bar")))
.hasMessageThat()
.contains("//foo:bar is not a valid environment definition");
}
@Test
public void testRefinedEnvironmentCheckValidTarget() throws Exception {
writeEnvironmentRules();
write("foo/BUILD",
"config_setting(name = 'config_one', values = {'define': 'mode=one'})",
"config_setting(name = 'config_two', values = {'define': 'mode=two'})",
"sh_library(name = 'lib_one', srcs = [], compatible_with = ['//buildenv:one'])",
"sh_library(name = 'lib_two', srcs = [], compatible_with = ['//buildenv:two'])",
"sh_library(name = 'toplevel',",
" srcs = ['toplevel.sh'],",
" deps = select({",
" ':config_one': [':lib_one'],",
" ':config_two': [':lib_two'],",
" }),",
" compatible_with = ['//buildenv:one', '//buildenv:two'])");
// "--define mode=one" refines :toplevel to (matching) ["//buildenv:one"]:
addOptions("--target_environment=//buildenv:one", "--define", "mode=one");
buildTarget("//foo:toplevel");
assertThat(getResult().getSuccess()).isTrue();
}
@Test
public void testRefinedEnvironmentCheckBadTarget() throws Exception {
writeEnvironmentRules();
write("foo/BUILD",
"config_setting(name = 'config_one', values = {'define': 'mode=one'})",
"config_setting(name = 'config_two', values = {'define': 'mode=two'})",
"sh_library(name = 'lib_one', srcs = [], compatible_with = ['//buildenv:one'])",
"sh_library(name = 'lib_two', srcs = [], compatible_with = ['//buildenv:two'])",
"sh_library(name = 'toplevel',",
" srcs = ['toplevel.sh'],",
" deps = select({",
" ':config_one': [':lib_one'],",
" ':config_two': [':lib_two'],",
" }),",
" compatible_with = ['//buildenv:one', '//buildenv:two'])");
// "--define mode=two" refines :toplevel to (non-matching) ["//buildenv:two"]:
addOptions("--target_environment=//buildenv:one", "--define", "mode=two");
assertThat(assertThrows(ViewCreationFailedException.class, () -> buildTarget("//foo:toplevel")))
.hasMessageThat()
.contains(""
+ "//foo:toplevel does not support:\n"
+ " environment: //buildenv:one\n"
+ " removed by: //foo:toplevel");
}
@Test
public void topLevelOutputFile() throws Exception {
writeEnvironmentRules();
write("foo/BUILD",
"cc_library(name = 'bar', srcs = ['bar.cc'], compatible_with = ['//buildenv:one'])");
addOptions("--target_environment=//buildenv:one");
buildTarget("//foo:libbar.a");
assertThat(getResult().getSuccess()).isTrue();
}
@Test
public void topLevelAliasToCompatibleOutputFile() throws Exception {
writeEnvironmentRules();
write(
"foo/BUILD",
"genrule(",
" name = 'goodgen',",
" srcs = [],",
" cmd = '',",
" outs = ['goodgen.out'],",
" compatible_with = ['//buildenv:one'])",
"alias(",
" name = 'goodalias',",
" actual = 'goodgen.out')");
addOptions("--target_environment=//buildenv:one");
buildTarget("//foo:goodalias");
assertThat(getResult().getSuccess()).isTrue();
}
@Test
public void topLevelAliasToBadOutputFile() throws Exception {
writeEnvironmentRules();
write(
"foo/BUILD",
"genrule(",
" name = 'badgen',",
" srcs = [],",
" cmd = '',",
" outs = ['badgen.out'])",
"alias(",
" name = 'badalias',",
" actual = 'badgen.out')");
addOptions("--target_environment=//buildenv:one");
assertThat(assertThrows(ViewCreationFailedException.class, () -> buildTarget("//foo:badalias")))
.hasMessageThat()
.contains("" + "//foo:badgen.out does not support:\n" + " //buildenv:one");
}
@Test
public void doesNotCheckDefaultEnvironments() throws Exception {
write(
"buildenv/a/BUILD",
"environment_group(",
" name = 'a',",
" defaults = [':a1'],",
" environments = [':a1', ':a2'])",
"environment(name = 'a1')",
"environment(name = 'a2')");
write(
"buildenv/b/BUILD",
"environment_group(",
" name = 'b',",
" defaults = [':b1'],",
" environments = [':b1', ':b2'])",
"environment(name = 'b1')",
"environment(name = 'b2')");
write(
"foo/bar.sh",
"echo Bar!");
write(
"foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'], restricted_to = ['//buildenv/b:b2'])");
addOptions("--target_environment=//buildenv/a:a1");
buildTarget("//foo:bar");
assertThat(getResult().getSuccess()).isTrue();
}
@Test
public void autoTargetEnvironment_success() throws Exception {
write(
"buildenv/auto_cpu/BUILD",
"environment_group(",
" name = 'cpus',",
" defaults = [':k8'],",
" environments = [':k8', ':ppc'])",
"environment(name = 'k8')",
"environment(name = 'ppc')");
write(
"foo/bar.sh",
"echo Bar!");
write(
"foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'], restricted_to = ['//buildenv/auto_cpu:k8'])");
addOptions("--build", "--auto_cpu_environment_group=//buildenv/auto_cpu:cpus", "--cpu=k8");
buildTarget("//foo:bar");
ConfiguredTarget successful = Iterables.getOnlyElement(getResult().getSuccessfulTargets());
assertThat(successful.getLabel().toString()).isEqualTo("//foo:bar");
}
@Test
public void autoTargetEnvironment_cpuNotSet() throws Exception {
write(
"buildenv/auto_cpu/BUILD",
"environment_group(",
" name = 'cpus',",
" defaults = [':k8'],",
" environments = [':k8', ':ppc'])",
"environment(name = 'k8')",
"environment(name = 'ppc')");
write(
"foo/bar.sh",
"echo Bar!");
write(
"foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'], restricted_to = ['//buildenv/auto_cpu:k8'])");
addOptions("--build", "--auto_cpu_environment_group=//buildenv/auto_cpu:cpus");
buildTarget("//foo:bar");
ConfiguredTarget successful = Iterables.getOnlyElement(getResult().getSuccessfulTargets());
assertThat(successful.getLabel().toString()).isEqualTo("//foo:bar");
}
@Test
public void autoTargetEnvironment_disabled() throws Exception {
write(
"buildenv/auto_cpu/BUILD",
"environment_group(",
" name = 'cpus',",
" defaults = [':k8'],",
" environments = [':k8', ':ppc'])",
"environment(name = 'k8')",
"environment(name = 'ppc')");
write(
"foo/bar.sh",
"echo Bar!");
write(
"foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'], restricted_to = ['//buildenv/auto_cpu:k8'])");
addOptions("--build", "--auto_cpu_environment_group=", "--cpu=k8");
buildTarget("//foo:bar");
ConfiguredTarget successful = Iterables.getOnlyElement(getResult().getSuccessfulTargets());
assertThat(successful.getLabel().toString()).isEqualTo("//foo:bar");
}
@Test
public void autoTargetEnvironment_error() throws Exception {
write(
"buildenv/auto_cpu/BUILD",
"environment_group(",
" name = 'cpus',",
" defaults = [':k8'],",
" environments = [':k8', ':ppc'])",
"environment(name = 'k8')",
"environment(name = 'ppc')");
write(
"foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'], restricted_to = ['//buildenv/auto_cpu:k8'])");
addOptions("--build", "--auto_cpu_environment_group=//buildenv/auto_cpu:cpus", "--cpu=ppc");
buildTarget("//foo:bar");
assertThat(getResult().getSuccessfulTargets()).isEmpty();
}
@Test
public void autoTargetEnvironment_multi_cpu() throws Exception {
write(
"buildenv/auto_cpu/BUILD",
"environment_group(",
" name = 'cpus',",
" defaults = [':k8'],",
" environments = [':k8', ':ppc'])",
"environment(name = 'k8')",
"environment(name = 'ppc')");
write(
"foo/bar.sh",
"echo Bar!");
write(
"foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'], restricted_to = ['//buildenv/auto_cpu:k8'])");
addOptions(
"--build",
"--auto_cpu_environment_group=//buildenv/auto_cpu:cpus",
"--experimental_multi_cpu=k8,ppc");
buildTarget("//foo:bar");
ConfiguredTarget successful = Iterables.getOnlyElement(getResult().getSuccessfulTargets());
assertThat(getConfiguration(successful).getCpu()).isEqualTo("k8");
assertThat(successful.getLabel().toString()).isEqualTo("//foo:bar");
ConfiguredTarget skipped = Iterables.getOnlyElement(getResult().getSkippedTargets());
assertThat(getConfiguration(skipped).getCpu()).isEqualTo("ppc");
assertThat(skipped.getLabel().toString()).isEqualTo("//foo:bar");
}
@Test
public void autoTargetEnvironment_does_not_check_default_environments() throws Exception {
write(
"buildenv/auto_cpu/BUILD",
"environment_group(",
" name = 'cpus',",
" defaults = [':k8'],",
" environments = [':k8', ':ppc'])",
"environment(name = 'k8')",
"environment(name = 'ppc')");
write(
"buildenv/b/BUILD",
"environment_group(",
" name = 'b',",
" defaults = [':b1'],",
" environments = [':b1', ':b2'])",
"environment(name = 'b1')",
"environment(name = 'b2')");
write(
"foo/bar.sh",
"echo Bar!");
write(
"foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'], restricted_to = ['//buildenv/b:b2'])");
addOptions("--build", "--auto_cpu_environment_group=//buildenv/auto_cpu:cpus", "--cpu=k8");
// Even though //foo:bar doesn't support the expected //buildenv/b:b1 (which is the default
// for //buildenv/b), --auto_cpu_environment_group is only concerned about //buildenv/auto_cpu.
buildTarget("//foo:bar");
ConfiguredTarget successful = Iterables.getOnlyElement(getResult().getSuccessfulTargets());
assertThat(successful.getLabel().toString()).isEqualTo("//foo:bar");
}
@Test
public void autoTargetEnvironment_good_explicit_checking_bad() throws Exception {
write(
"buildenv/auto_cpu/BUILD",
"environment_group(",
" name = 'cpus',",
" defaults = [':k8'],",
" environments = [':k8', ':ppc'])",
"environment(name = 'k8')",
"environment(name = 'ppc')");
write(
"buildenv/b/BUILD",
"environment_group(",
" name = 'b',",
" defaults = [':b1'],",
" environments = [':b1', ':b2'])",
"environment(name = 'b1')",
"environment(name = 'b2')");
write(
"foo/bar.sh",
"echo Bar!");
write(
"foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'], restricted_to = ['//buildenv/b:b2'])");
addOptions("--build", "--auto_cpu_environment_group=//buildenv/auto_cpu:cpus", "--cpu=k8",
"--target_environment=//buildenv/b:b1");
assertThat(assertThrows(ViewCreationFailedException.class, () -> buildTarget("//foo:bar")))
.hasMessageThat()
.contains(""
+ "//foo:bar does not support:\n"
+ " //buildenv/b:b1");
}
@Test
public void autoTargetEnvironment_bad_explicit_checking_good() throws Exception {
write(
"buildenv/auto_cpu/BUILD",
"environment_group(",
" name = 'cpus',",
" defaults = [':k8'],",
" environments = [':k8', ':ppc'])",
"environment(name = 'k8')",
"environment(name = 'ppc')");
write(
"buildenv/b/BUILD",
"environment_group(",
" name = 'b',",
" defaults = [':b1'],",
" environments = [':b1', ':b2'])",
"environment(name = 'b1')",
"environment(name = 'b2')");
write(
"foo/bar.sh",
"echo Bar!");
write(
"foo/BUILD",
"sh_library(name = 'bar', srcs = ['bar.sh'], restricted_to = ['//buildenv/b:b2'])");
addOptions("--build", "--auto_cpu_environment_group=//buildenv/auto_cpu:cpus", "--cpu=ppc",
"--target_environment=//buildenv/b:b2");
buildTarget("//foo:bar");
assertThat(getResult().getSuccessfulTargets()).isEmpty();
assertThat(Iterables.getOnlyElement(getResult().getSkippedTargets()).getLabel().toString())
.isEqualTo("//foo:bar");
}
}