| // Copyright 2022 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.analysis.config; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Iterables; |
| import com.google.devtools.build.lib.analysis.ConfiguredTarget; |
| import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.packages.Provider; |
| import com.google.devtools.build.lib.packages.StarlarkProvider; |
| import com.google.devtools.build.lib.packages.StructImpl; |
| import com.google.devtools.build.lib.rules.cpp.CppConfiguration; |
| import com.google.devtools.build.lib.rules.java.JavaConfiguration; |
| import java.util.List; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** Tests for {@link BuildConfigurationFunction}'s special behaviors. */ |
| @RunWith(JUnit4.class) |
| public final class BuildConfigurationFunctionTest extends BuildViewTestCase { |
| |
| @Before |
| public void setupMyInfo() throws Exception { |
| scratch.file("myinfo/myinfo.bzl", "MyInfo = provider()"); |
| |
| scratch.file("myinfo/BUILD"); |
| } |
| |
| private static StructImpl getMyInfoFromTarget(ConfiguredTarget configuredTarget) |
| throws Exception { |
| Provider.Key key = |
| new StarlarkProvider.Key(Label.parseCanonical("//myinfo:myinfo.bzl"), "MyInfo"); |
| return (StructImpl) configuredTarget.get(key); |
| } |
| |
| private void writeAllowlistFile() throws Exception { |
| scratch.overwriteFile( |
| "tools/allowlists/function_transition_allowlist/BUILD", |
| "package_group(", |
| " name = 'function_transition_allowlist',", |
| " packages = [", |
| " '//test/...',", |
| " ],", |
| ")"); |
| } |
| |
| private void writeBuildSettingsBzl() throws Exception { |
| scratch.file( |
| "test/build_settings.bzl", |
| "BuildSettingInfo = provider(fields = ['value'])", |
| "def _impl(ctx):", |
| " return [BuildSettingInfo(value = ctx.build_setting_value)]", |
| "string_flag = rule(implementation = _impl, build_setting = config.string(flag=True))"); |
| } |
| |
| private CoreOptions getCoreOptions(ConfiguredTarget target) { |
| return getConfiguration(target).getOptions().get(CoreOptions.class); |
| } |
| |
| private String getMnemonic(ConfiguredTarget target) { |
| return getConfiguration(target).getMnemonic(); |
| } |
| |
| @Test |
| public void testDiffAgainstBaselineOutputScheme_hasHash() throws Exception { |
| writeAllowlistFile(); |
| writeBuildSettingsBzl(); |
| scratch.file( |
| "test/transitions.bzl", |
| "def _foo_impl(settings, attr):", |
| " return {'//test:foo': 'transitioned'}", |
| "foo_transition = transition(implementation = _foo_impl, inputs = [],", |
| " outputs = ['//test:foo'])"); |
| scratch.file( |
| "test/rules.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "load('//test:transitions.bzl', 'foo_transition')", |
| "def _impl(ctx):", |
| " return MyInfo(dep = ctx.attr.dep)", |
| "my_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {", |
| " 'dep': attr.label(cfg = foo_transition), ", |
| " '_allowlist_function_transition': attr.label(", |
| " default = '//tools/allowlists/function_transition_allowlist',", |
| " ),", |
| " })", |
| "def _basic_impl(ctx):", |
| " return []", |
| "simple = rule(_basic_impl)"); |
| scratch.file( |
| "test/BUILD", |
| "load('//test:rules.bzl', 'my_rule', 'simple')", |
| "load('//test:build_settings.bzl', 'string_flag')", |
| "string_flag(name = 'foo', build_setting_default='default')", |
| "my_rule(name = 'test', dep = ':dep')", |
| "simple(name = 'dep')"); |
| |
| useConfiguration("--experimental_output_directory_naming_scheme=diff_against_baseline"); |
| ConfiguredTarget test = getConfiguredTarget("//test"); |
| |
| assertThat(getMnemonic(test)).doesNotContain("-ST-"); |
| assertThat(getCoreOptions(test).affectedByStarlarkTransition).isEmpty(); |
| |
| @SuppressWarnings("unchecked") |
| ConfiguredTarget dep = |
| Iterables.getOnlyElement( |
| (List<ConfiguredTarget>) getMyInfoFromTarget(test).getValue("dep")); |
| |
| assertThat(getMnemonic(dep)) |
| .endsWith( |
| OutputPathMnemonicComputer.transitionDirectoryNameFragment( |
| ImmutableList.of("//test:foo=transitioned"))); |
| assertThat(getCoreOptions(dep).affectedByStarlarkTransition).isEmpty(); |
| } |
| |
| @Test |
| public void testDiffAgainstBaselineOutputScheme_avoidHashForInExplicitOutputPath() |
| throws Exception { |
| writeAllowlistFile(); |
| scratch.file( |
| "test/transitions.bzl", |
| "def _opt_impl(settings, attr):", |
| " return {'//command_line_option:compilation_mode': 'opt'}", |
| "opt_transition = transition(implementation = _opt_impl, inputs = [],", |
| " outputs = ['//command_line_option:compilation_mode'])"); |
| scratch.file( |
| "test/rules.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "load('//test:transitions.bzl', 'opt_transition')", |
| "def _impl(ctx):", |
| " return MyInfo(dep = ctx.attr.dep)", |
| "my_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {", |
| " 'dep': attr.label(cfg = opt_transition), ", |
| " '_allowlist_function_transition': attr.label(", |
| " default = '//tools/allowlists/function_transition_allowlist',", |
| " ),", |
| " })", |
| "def _basic_impl(ctx):", |
| " return []", |
| "simple = rule(_basic_impl)"); |
| scratch.file( |
| "test/BUILD", |
| "load('//test:rules.bzl', 'my_rule', 'simple')", |
| "my_rule(name = 'test', dep = ':dep')", |
| "simple(name = 'dep')"); |
| |
| useConfiguration( |
| "--compilation_mode=fastbuild", |
| "--experimental_output_directory_naming_scheme=diff_against_baseline"); |
| ConfiguredTarget test = getConfiguredTarget("//test"); |
| |
| assertThat(getConfiguration(test).getMnemonic()).contains("fastbuild"); |
| assertThat(getMnemonic(test)).doesNotContain("-ST-"); |
| assertThat(getCoreOptions(test).affectedByStarlarkTransition).isEmpty(); |
| |
| @SuppressWarnings("unchecked") |
| ConfiguredTarget dep = |
| Iterables.getOnlyElement( |
| (List<ConfiguredTarget>) getMyInfoFromTarget(test).getValue("dep")); |
| |
| assertThat(getConfiguration(dep).getMnemonic()).contains("opt"); |
| assertThat(getMnemonic(dep)).doesNotContain("-ST-"); |
| assertThat(getCoreOptions(dep).affectedByStarlarkTransition).isEmpty(); |
| } |
| |
| @Test |
| public void testDiffAgainstBaselineOutputScheme_abaAvoidsHash() throws Exception { |
| writeAllowlistFile(); |
| writeBuildSettingsBzl(); |
| scratch.file( |
| "test/transitions.bzl", |
| "def _toggle_impl(settings, attr):", |
| " if (settings['//test:foo'] != 'default'):", |
| " return {'//test:foo': 'default'}", |
| " else:", |
| " return {'//test:foo': 'transitioned'}", |
| "toggle_foo_transition = transition(implementation = _toggle_impl,", |
| " inputs = ['//test:foo'], outputs = ['//test:foo'])"); |
| scratch.file( |
| "test/rules.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "load('//test:transitions.bzl', 'toggle_foo_transition')", |
| "def _impl(ctx):", |
| " return MyInfo(dep = ctx.attr.dep)", |
| "my_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {", |
| " 'dep': attr.label(cfg = toggle_foo_transition), ", |
| " '_allowlist_function_transition': attr.label(", |
| " default = '//tools/allowlists/function_transition_allowlist',", |
| " ),", |
| " })", |
| "def _basic_impl(ctx):", |
| " return []", |
| "simple = rule(_basic_impl)"); |
| scratch.file( |
| "test/BUILD", |
| "load('//test:rules.bzl', 'my_rule', 'simple')", |
| "load('//test:build_settings.bzl', 'string_flag')", |
| "string_flag(name = 'foo', build_setting_default='default')", |
| "my_rule(name = 'test', dep = ':middle')", |
| "my_rule(name = 'middle', dep = ':root')", |
| "simple(name = 'root')"); |
| |
| useConfiguration("--experimental_output_directory_naming_scheme=diff_against_baseline"); |
| ConfiguredTarget test = getConfiguredTarget("//test"); |
| |
| assertThat(getMnemonic(test)).doesNotContain("-ST-"); |
| assertThat(getCoreOptions(test).affectedByStarlarkTransition).isEmpty(); |
| |
| @SuppressWarnings("unchecked") |
| ConfiguredTarget middle = |
| Iterables.getOnlyElement( |
| (List<ConfiguredTarget>) getMyInfoFromTarget(test).getValue("dep")); |
| |
| assertThat(getMnemonic(middle)) |
| .endsWith( |
| OutputPathMnemonicComputer.transitionDirectoryNameFragment( |
| ImmutableList.of("//test:foo=transitioned"))); |
| assertThat(getCoreOptions(middle).affectedByStarlarkTransition).isEmpty(); |
| |
| @SuppressWarnings("unchecked") |
| ConfiguredTarget root = |
| Iterables.getOnlyElement( |
| (List<ConfiguredTarget>) getMyInfoFromTarget(middle).getValue("dep")); |
| |
| assertThat(getMnemonic(test)).doesNotContain("-ST-"); |
| assertThat(getCoreOptions(test).affectedByStarlarkTransition).isEmpty(); |
| |
| assertThat(getConfiguration(test)).isEqualTo(getConfiguration(root)); |
| assertThat(getConfiguration(test)).isNotEqualTo(getConfiguration(middle)); |
| |
| // This should be implied by everything else but as a final check.... |
| assertThat(getConfiguration(test).getMnemonic()) |
| .isEqualTo(getConfiguration(root).getMnemonic()); |
| } |
| |
| @Test |
| public void testPlatformExplicitInOutputDirAndDynamicBaseline_withPlatformMappings() |
| throws Exception { |
| writeAllowlistFile(); |
| scratch.file( |
| "test/transitions.bzl", |
| "def _platform_impl(settings, attr):", |
| " return {'//command_line_option:platforms': [attr.platform]}", |
| "platform_transition = transition(implementation = _platform_impl, inputs = [],", |
| " outputs = ['//command_line_option:platforms'])"); |
| scratch.file( |
| "test/rules.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "load('//test:transitions.bzl', 'platform_transition')", |
| "def _impl(ctx):", |
| " return MyInfo(dep = ctx.attr.dep)", |
| "my_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {", |
| " 'dep': attr.label(), ", |
| " })", |
| "def _basic_impl(ctx):", |
| " return []", |
| "as_platform = rule(", |
| " implementation = _basic_impl,", |
| " cfg = platform_transition,", |
| " attrs = {", |
| " 'platform': attr.label(default = '//platforms:alpha'),", |
| " '_allowlist_function_transition': attr.label(", |
| " default = '//tools/allowlists/function_transition_allowlist',", |
| " ),", |
| " })"); |
| scratch.file( |
| "test/BUILD", |
| "load('//test:rules.bzl', 'my_rule', 'as_platform')", |
| "my_rule(name = 'test', dep = ':dep')", |
| "as_platform(name = 'dep', platform='//platforms:beta')"); |
| scratch.file("platforms/BUILD", "platform(name = 'alpha')", "platform(name = 'beta')"); |
| scratch.file( |
| "tools/platform_mappings", |
| "platforms:", |
| " //platforms:alpha", |
| " --cpu=alpha", |
| " //platforms:beta", |
| " --cpu=beta", |
| "flags:", |
| " --cpu=alpha", |
| " //platforms:alpha", |
| " --cpu=beta", |
| " //platforms:beta"); |
| |
| useConfiguration( |
| "--compilation_mode=fastbuild", |
| "--platforms=//platforms:alpha", |
| "--platform_mappings=tools/platform_mappings", |
| "--experimental_platform_in_output_dir", |
| "--noexperimental_use_platforms_in_output_dir_legacy_heuristic", |
| "--experimental_override_name_platform_in_output_dir=//platforms:alpha=alpha", |
| "--experimental_override_name_platform_in_output_dir=//platforms:beta=beta", |
| "--experimental_output_directory_naming_scheme=diff_against_dynamic_baseline"); |
| ConfiguredTarget test = getConfiguredTarget("//test"); |
| |
| assertThat(getMnemonic(test)).contains("alpha-fastbuild"); |
| assertThat(getMnemonic(test)).doesNotContain("-ST-"); |
| |
| ConfiguredTarget dep = (ConfiguredTarget) getMyInfoFromTarget(test).getValue("dep"); |
| |
| assertThat(getMnemonic(dep)).contains("beta-fastbuild"); |
| assertThat(getMnemonic(dep)).doesNotContain("-ST-"); |
| |
| // Verify platform_mappings applied properly |
| assertThat(getConfiguration(test).getCpu()).isEqualTo("alpha"); |
| assertThat(getConfiguration(dep).getCpu()).isEqualTo("beta"); |
| } |
| |
| @Test |
| public void testPlatformExplicitInOutputDirAndDynamicBaseline_withMorePlatformMappings() |
| throws Exception { |
| writeAllowlistFile(); |
| scratch.file( |
| "test/transitions.bzl", |
| "def _platform_impl(settings, attr):", |
| " return {'//command_line_option:platforms': [attr.platform]}", |
| "platform_transition = transition(implementation = _platform_impl, inputs = [],", |
| " outputs = ['//command_line_option:platforms'])"); |
| scratch.file( |
| "test/rules.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "load('//test:transitions.bzl', 'platform_transition')", |
| "def _impl(ctx):", |
| " return MyInfo(dep = ctx.attr.dep)", |
| "my_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {", |
| " 'dep': attr.label(), ", |
| " })", |
| "def _basic_impl(ctx):", |
| " return []", |
| "as_platform = rule(", |
| " implementation = _basic_impl,", |
| " cfg = platform_transition,", |
| " attrs = {", |
| " 'platform': attr.label(default = '//platforms:alpha'),", |
| " '_allowlist_function_transition': attr.label(", |
| " default = '//tools/allowlists/function_transition_allowlist',", |
| " ),", |
| " })"); |
| scratch.file( |
| "test/BUILD", |
| "load('//test:rules.bzl', 'my_rule', 'as_platform')", |
| "my_rule(name = 'test', dep = ':dep')", |
| "as_platform(name = 'dep', platform='//platforms:beta')"); |
| scratch.file("platforms/BUILD", "platform(name = 'alpha')", "platform(name = 'beta')"); |
| |
| // Test just wants to transition some options not usually explicitly in the output path |
| // so if those options are changed/removed, just replace them here. |
| scratch.file( |
| "tools/platform_mappings", |
| "platforms:", |
| " //platforms:alpha", |
| " --cpu=alpha", |
| " --use_ijars=false", |
| " --dynamic_mode=default", |
| " //platforms:beta", |
| " --cpu=beta", |
| " --use_ijars=true", |
| " --dynamic_mode=off"); |
| |
| useConfiguration( |
| "--compilation_mode=fastbuild", |
| "--platforms=//platforms:alpha", |
| "--platform_mappings=tools/platform_mappings", |
| "--experimental_platform_in_output_dir", |
| "--noexperimental_use_platforms_in_output_dir_legacy_heuristic", |
| "--experimental_override_name_platform_in_output_dir=//platforms:alpha=alpha", |
| "--experimental_override_name_platform_in_output_dir=//platforms:beta=beta", |
| "--experimental_output_directory_naming_scheme=diff_against_dynamic_baseline"); |
| ConfiguredTarget test = getConfiguredTarget("//test"); |
| |
| assertThat(getMnemonic(test)).contains("alpha-fastbuild"); |
| assertThat(getMnemonic(test)).doesNotContain("-ST-"); |
| |
| ConfiguredTarget dep = (ConfiguredTarget) getMyInfoFromTarget(test).getValue("dep"); |
| |
| assertThat(getMnemonic(dep)).contains("beta-fastbuild"); |
| assertThat(getMnemonic(dep)).doesNotContain("-ST-"); |
| |
| // Verify platform_mappings applied properly |
| assertThat(getConfiguration(test).getCpu()).isEqualTo("alpha"); |
| assertThat(getConfiguration(test).getFragment(CppConfiguration.class).getDynamicModeFlag()) |
| .isEqualTo(CppConfiguration.DynamicMode.DEFAULT); |
| assertThat(getConfiguration(test).getFragment(JavaConfiguration.class).getUseIjars()).isFalse(); |
| assertThat(getConfiguration(dep).getCpu()).isEqualTo("beta"); |
| assertThat(getConfiguration(dep).getFragment(CppConfiguration.class).getDynamicModeFlag()) |
| .isEqualTo(CppConfiguration.DynamicMode.OFF); |
| assertThat(getConfiguration(dep).getFragment(JavaConfiguration.class).getUseIjars()).isTrue(); |
| } |
| } |