| // Copyright 2018 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.rules.cpp; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.common.truth.Truth8.assertThat; |
| import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.baseArtifactNames; |
| import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.prettyArtifactNames; |
| import static com.google.devtools.build.lib.rules.cpp.SolibSymlinkAction.MAX_FILENAME_LENGTH; |
| import static org.junit.Assert.assertThrows; |
| |
| import com.google.common.base.Joiner; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Iterables; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.actions.CommandLineExpansionException; |
| import com.google.devtools.build.lib.actions.util.ActionsTestUtil; |
| import com.google.devtools.build.lib.analysis.ConfiguredTarget; |
| import com.google.devtools.build.lib.analysis.FileProvider; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.util.AnalysisMock; |
| import com.google.devtools.build.lib.analysis.util.AnalysisTestUtil; |
| import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.cmdline.LabelSyntaxException; |
| import com.google.devtools.build.lib.collect.nestedset.Depset; |
| import com.google.devtools.build.lib.packages.Provider; |
| import com.google.devtools.build.lib.packages.StarlarkInfo; |
| import com.google.devtools.build.lib.packages.StarlarkProvider; |
| import com.google.devtools.build.lib.packages.StarlarkProviderIdentifier; |
| import com.google.devtools.build.lib.packages.StructImpl; |
| import com.google.devtools.build.lib.packages.util.Crosstool.CcToolchainConfig; |
| import com.google.devtools.build.lib.packages.util.MockCcSupport; |
| import com.google.devtools.build.lib.packages.util.ResourceLoader; |
| import com.google.devtools.build.lib.rules.cpp.CcCommon.Language; |
| import com.google.devtools.build.lib.rules.cpp.CcLinkingContext.Linkstamp; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.ActionConfig; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.EnvEntry; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.EnvSet; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Feature; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Flag.SingleChunkFlag; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FlagGroup; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FlagSet; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Tool; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.VariableWithValue; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.WithFeatureSet; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainVariables.StringValueParser; |
| import com.google.devtools.build.lib.testutil.Scratch; |
| import com.google.devtools.build.lib.testutil.TestConstants; |
| import com.google.devtools.build.lib.util.Pair; |
| import com.google.devtools.build.lib.vfs.FileSystemUtils; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain; |
| import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.MakeVariable; |
| import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.ToolPath; |
| import com.google.protobuf.TextFormat; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import net.starlark.java.eval.EvalException; |
| import net.starlark.java.eval.Sequence; |
| import net.starlark.java.eval.Starlark; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** Unit tests for the {@code cc_common} Starlark module. */ |
| @RunWith(JUnit4.class) |
| public class StarlarkCcCommonTest extends BuildViewTestCase { |
| |
| @Before |
| public void setUp() throws Exception { |
| scratch.file("myinfo/myinfo.bzl", "MyInfo = provider()"); |
| |
| scratch.file("myinfo/BUILD"); |
| } |
| |
| private static StructImpl getMyInfoFromTarget(ConfiguredTarget configuredTarget) |
| throws LabelSyntaxException { |
| Provider.Key key = |
| new StarlarkProvider.Key(Label.parseCanonical("//myinfo:myinfo.bzl"), "MyInfo"); |
| return (StructImpl) configuredTarget.get(key); |
| } |
| |
| private static Iterable<Artifact> getArtifactsFromMyInfo(ConfiguredTarget target, String field) |
| throws Exception { |
| StructImpl myInfo = getMyInfoFromTarget(target); |
| @SuppressWarnings("unchecked") |
| Iterable<Artifact> artifacts = (Iterable<Artifact>) myInfo.getValue(field); |
| return artifacts; |
| } |
| |
| @Test |
| public void testAllFiles() throws Exception { |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| |
| scratch.file( |
| "a/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " return [MyInfo(all_files = toolchain.all_files)]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| ");"); |
| |
| ConfiguredTarget r = getConfiguredTarget("//a:r"); |
| Depset allFiles = (Depset) getMyInfoFromTarget(r).getValue("all_files"); |
| RuleContext ruleContext = getRuleContext(r); |
| CcToolchainProvider toolchain = |
| CppHelper.getToolchain(ruleContext, ruleContext.getPrerequisite("$cc_toolchain")); |
| assertThat(allFiles.getSet(Artifact.class)).isEqualTo(toolchain.getAllFiles()); |
| } |
| |
| @Test |
| public void testRuntimeLib() throws Exception { |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| |
| scratch.file( |
| "a/rule.bzl", |
| "CruleInfo = provider(fields=['static', 'dynamic'])", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " return [CruleInfo(", |
| " static = toolchain.static_runtime_lib(feature_configuration = feature_configuration),", |
| " dynamic = toolchain.dynamic_runtime_lib(", |
| " feature_configuration = feature_configuration),", |
| " )]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| // 1. Build without static_link_cpp_runtimes |
| ConfiguredTarget r = getConfiguredTarget("//a:r"); |
| Provider.Key key = |
| new StarlarkProvider.Key( |
| Label.create(r.getLabel().getPackageIdentifier(), "rule.bzl"), "CruleInfo"); |
| StarlarkInfo cruleInfo = (StarlarkInfo) r.get(key); |
| Depset staticRuntimeLib = (Depset) cruleInfo.getValue("static"); |
| Depset dynamicRuntimeLib = (Depset) cruleInfo.getValue("dynamic"); |
| |
| assertThat(staticRuntimeLib.getSet(Artifact.class).toList()).isEmpty(); |
| assertThat(dynamicRuntimeLib.getSet(Artifact.class).toList()).isEmpty(); |
| |
| // 2. Build with static_link_cpp_runtimes |
| getAnalysisMock() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder().withFeatures(CppRuleClasses.STATIC_LINK_CPP_RUNTIMES)); |
| invalidatePackages(); |
| r = getConfiguredTarget("//a:r"); |
| cruleInfo = (StarlarkInfo) r.get(key); |
| staticRuntimeLib = (Depset) cruleInfo.getValue("static"); |
| dynamicRuntimeLib = (Depset) cruleInfo.getValue("dynamic"); |
| |
| RuleContext ruleContext = getRuleContext(r); |
| CcToolchainProvider toolchain = |
| CppHelper.getToolchain(ruleContext, ruleContext.getPrerequisite("$cc_toolchain")); |
| assertThat(staticRuntimeLib.getSet(Artifact.class)) |
| .isEqualTo(toolchain.getStaticRuntimeLibForTesting()); |
| assertThat(dynamicRuntimeLib.getSet(Artifact.class)) |
| .isEqualTo(toolchain.getDynamicRuntimeLibForTesting()); |
| } |
| |
| @Test |
| public void testGetValuesFromCcToolchain() throws Exception { |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| |
| scratch.file( |
| "a/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " return [MyInfo(", |
| " dynamic_runtime_solib_dir = toolchain.dynamic_runtime_solib_dir,", |
| " toolchain_id = toolchain.toolchain_id,", |
| " )]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| ConfiguredTarget r = getConfiguredTarget("//a:r"); |
| StructImpl info = getMyInfoFromTarget(r); |
| String dynamicRuntimeSolibDir = (String) info.getValue("dynamic_runtime_solib_dir"); |
| String toolchainId = (String) info.getValue("toolchain_id"); |
| |
| RuleContext ruleContext = getRuleContext(r); |
| CcToolchainProvider toolchain = |
| CppHelper.getToolchain(ruleContext, ruleContext.getPrerequisite("$cc_toolchain")); |
| |
| assertThat(dynamicRuntimeSolibDir).isEqualTo(toolchain.getDynamicRuntimeSolibDirForStarlark()); |
| assertThat(toolchainId).isEqualTo(toolchain.getToolchainIdentifier()); |
| } |
| |
| @Test |
| public void testGetToolForAction() throws Exception { |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| |
| scratch.file( |
| "a/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " return [MyInfo(", |
| " action_tool_path = cc_common.get_tool_for_action(", |
| " feature_configuration = feature_configuration,", |
| " action_name = 'c++-compile'))]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| ConfiguredTarget r = getConfiguredTarget("//a:r"); |
| String actionToolPath = (String) getMyInfoFromTarget(r).getValue("action_tool_path"); |
| RuleContext ruleContext = getRuleContext(r); |
| CcToolchainProvider toolchain = |
| CppHelper.getToolchain(ruleContext, ruleContext.getPrerequisite("$cc_toolchain")); |
| FeatureConfiguration featureConfiguration = |
| CcCommon.configureFeaturesOrThrowEvalException( |
| ImmutableSet.of(), |
| ImmutableSet.of(), |
| Language.CPP, |
| toolchain, |
| ruleContext.getFragment(CppConfiguration.class)); |
| assertThat(actionToolPath) |
| .isEqualTo(featureConfiguration.getToolPathForAction(CppActionNames.CPP_COMPILE)); |
| } |
| |
| @Test |
| public void testExecutionRequirements() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder() |
| .withFeatures(MockCcSupport.CPP_COMPILE_ACTION_WITH_REQUIREMENTS)); |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| |
| scratch.file( |
| "a/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " return [MyInfo(", |
| " requirements = cc_common.get_execution_requirements(", |
| " feature_configuration = feature_configuration,", |
| " action_name = 'yolo_action_with_requirements'))]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| ConfiguredTarget r = getConfiguredTarget("//a:r"); |
| @SuppressWarnings("unchecked") |
| Sequence<String> requirements = |
| (Sequence<String>) getMyInfoFromTarget(r).getValue("requirements"); |
| assertThat(requirements).containsExactly("requires-yolo"); |
| } |
| |
| @Test |
| public void testFeatureConfigurationWithAdditionalEnabledFeature() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, CcToolchainConfig.builder().withFeatures("foo_feature")); |
| useConfiguration(); |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| |
| scratch.file( |
| "a/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " requested_features = ['foo_feature'])", |
| " return [MyInfo(", |
| " foo_feature_enabled = cc_common.is_enabled(", |
| " feature_configuration = feature_configuration,", |
| " feature_name = 'foo_feature'))]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| ConfiguredTarget r = getConfiguredTarget("//a:r"); |
| boolean fooFeatureEnabled = (boolean) getMyInfoFromTarget(r).getValue("foo_feature_enabled"); |
| assertThat(fooFeatureEnabled).isTrue(); |
| } |
| |
| @Test |
| public void testFeatureConfigurationWithAdditionalUnsupportedFeature() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, CcToolchainConfig.builder().withFeatures("foo_feature")); |
| useConfiguration("--features=foo_feature"); |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| |
| scratch.file( |
| "a/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " unsupported_features = ['foo_feature'])", |
| " return [MyInfo(", |
| " foo_feature_enabled = cc_common.is_enabled(", |
| " feature_configuration = feature_configuration,", |
| " feature_name = 'foo_feature'))]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| ConfiguredTarget r = getConfiguredTarget("//a:r"); |
| boolean fooFeatureEnabled = (boolean) getMyInfoFromTarget(r).getValue("foo_feature_enabled"); |
| assertThat(fooFeatureEnabled).isFalse(); |
| } |
| |
| @Test |
| public void testGetCommandLine() throws Exception { |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| |
| scratch.file( |
| "a/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " return [MyInfo(", |
| " command_line = cc_common.get_memory_inefficient_command_line(", |
| " feature_configuration = feature_configuration,", |
| " action_name = 'c++-link-executable',", |
| " variables = cc_common.empty_variables()))]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| ConfiguredTarget r = getConfiguredTarget("//a:r"); |
| @SuppressWarnings("unchecked") |
| Sequence<String> commandLine = |
| (Sequence<String>) getMyInfoFromTarget(r).getValue("command_line"); |
| RuleContext ruleContext = getRuleContext(r); |
| CcToolchainProvider toolchain = |
| CppHelper.getToolchain(ruleContext, ruleContext.getPrerequisite("$cc_toolchain")); |
| FeatureConfiguration featureConfiguration = |
| CcCommon.configureFeaturesOrThrowEvalException( |
| ImmutableSet.of(), |
| ImmutableSet.of(), |
| Language.CPP, |
| toolchain, |
| ruleContext.getFragment(CppConfiguration.class)); |
| assertThat(commandLine) |
| .containsExactlyElementsIn( |
| featureConfiguration.getCommandLine("c++-link-executable", CcToolchainVariables.EMPTY)); |
| } |
| |
| @Test |
| public void testGetEnvironment() throws Exception { |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| |
| scratch.file( |
| "a/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " return [MyInfo(", |
| " environment_variables = cc_common.get_environment_variables(", |
| " feature_configuration = feature_configuration,", |
| " action_name = 'c++-compile',", |
| " variables = cc_common.empty_variables()))]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| ConfiguredTarget r = getConfiguredTarget("//a:r"); |
| @SuppressWarnings("unchecked") |
| Map<String, String> environmentVariables = |
| (Map<String, String>) getMyInfoFromTarget(r).getValue("environment_variables"); |
| RuleContext ruleContext = getRuleContext(r); |
| CcToolchainProvider toolchain = |
| CppHelper.getToolchain(ruleContext, ruleContext.getPrerequisite("$cc_toolchain")); |
| FeatureConfiguration featureConfiguration = |
| CcCommon.configureFeaturesOrThrowEvalException( |
| ImmutableSet.of(), |
| ImmutableSet.of(), |
| Language.CPP, |
| toolchain, |
| ruleContext.getFragment(CppConfiguration.class)); |
| assertThat(environmentVariables) |
| .containsExactlyEntriesIn( |
| featureConfiguration.getEnvironmentVariables( |
| CppActionNames.CPP_COMPILE, CcToolchainVariables.EMPTY)); |
| } |
| |
| @Test |
| public void testActionIsEnabled() throws Exception { |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| |
| scratch.file( |
| "a/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " return [MyInfo(", |
| " enabled_action = cc_common.action_is_enabled(", |
| " feature_configuration = feature_configuration,", |
| " action_name = 'c-compile'),", |
| " disabled_action = cc_common.action_is_enabled(", |
| " feature_configuration = feature_configuration,", |
| " action_name = 'wololoo'))]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| StructImpl myInfo = getMyInfoFromTarget(getConfiguredTarget("//a:r")); |
| boolean enabledActionIsEnabled = (boolean) myInfo.getValue("enabled_action"); |
| boolean disabledActionIsDisabled = (boolean) myInfo.getValue("disabled_action"); |
| assertThat(enabledActionIsEnabled).isTrue(); |
| assertThat(disabledActionIsDisabled).isFalse(); |
| } |
| |
| @Test |
| public void testIsEnabled() throws Exception { |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| |
| scratch.file( |
| "a/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " return [MyInfo(", |
| " enabled_feature = cc_common.is_enabled(", |
| " feature_configuration = feature_configuration,", |
| " feature_name = 'libraries_to_link'),", |
| " disabled_feature = cc_common.is_enabled(", |
| " feature_configuration = feature_configuration,", |
| " feature_name = 'wololoo'))]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| StructImpl myInfo = getMyInfoFromTarget(getConfiguredTarget("//a:r")); |
| |
| boolean enabledFeatureIsEnabled = (boolean) myInfo.getValue("enabled_feature"); |
| boolean disabledFeatureIsDisabled = (boolean) myInfo.getValue("disabled_feature"); |
| assertThat(enabledFeatureIsEnabled).isTrue(); |
| assertThat(disabledFeatureIsDisabled).isFalse(); |
| } |
| |
| @Test |
| public void testFeatureConfigurationRequiresCtx() throws Exception { |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| |
| scratch.file( |
| "a/rule.bzl", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(cc_toolchain = toolchain)", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| useConfiguration("--incompatible_require_ctx_in_configure_features"); |
| reporter.removeHandler(failFastHandler); |
| |
| getConfiguredTarget("//a:r"); |
| assertContainsEvent("mandatory parameter 'ctx' of cc_common.configure_features is missing"); |
| } |
| |
| @Test |
| public void testActionNames() throws Exception { |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| scratch.overwriteFile("tools/build_defs/cc/BUILD"); |
| scratch.overwriteFile( |
| "tools/build_defs/cc/action_names.bzl", |
| ResourceLoader.readFromResources( |
| TestConstants.RULES_CC_REPOSITORY_EXECROOT + "cc/action_names.bzl")); |
| |
| scratch.file( |
| "a/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "load('//tools/build_defs/cc:action_names.bzl',", |
| " 'C_COMPILE_ACTION_NAME',", |
| " 'CPP_COMPILE_ACTION_NAME',", |
| " 'LINKSTAMP_COMPILE_ACTION_NAME',", |
| " 'CC_FLAGS_MAKE_VARIABLE_ACTION_NAME',", |
| " 'CPP_MODULE_CODEGEN_ACTION_NAME',", |
| " 'CPP_HEADER_PARSING_ACTION_NAME',", |
| " 'CPP_MODULE_COMPILE_ACTION_NAME',", |
| " 'ASSEMBLE_ACTION_NAME',", |
| " 'PREPROCESS_ASSEMBLE_ACTION_NAME',", |
| " 'LTO_INDEXING_ACTION_NAME',", |
| " 'LTO_BACKEND_ACTION_NAME',", |
| " 'CPP_LINK_EXECUTABLE_ACTION_NAME',", |
| " 'CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME',", |
| " 'CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME',", |
| " 'CPP_LINK_STATIC_LIBRARY_ACTION_NAME',", |
| " 'STRIP_ACTION_NAME')", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " return [MyInfo(", |
| " c_compile_action_name=C_COMPILE_ACTION_NAME,", |
| " cpp_compile_action_name=CPP_COMPILE_ACTION_NAME,", |
| " linkstamp_compile_action_name=LINKSTAMP_COMPILE_ACTION_NAME,", |
| " cc_flags_make_variable_action_name_action_name=CC_FLAGS_MAKE_VARIABLE_ACTION_NAME,", |
| " cpp_module_codegen_action_name=CPP_MODULE_CODEGEN_ACTION_NAME,", |
| " cpp_header_parsing_action_name=CPP_HEADER_PARSING_ACTION_NAME,", |
| " cpp_module_compile_action_name=CPP_MODULE_COMPILE_ACTION_NAME,", |
| " assemble_action_name=ASSEMBLE_ACTION_NAME,", |
| " preprocess_assemble_action_name=PREPROCESS_ASSEMBLE_ACTION_NAME,", |
| " lto_indexing_action_name=LTO_INDEXING_ACTION_NAME,", |
| " lto_backend_action_name=LTO_BACKEND_ACTION_NAME,", |
| " cpp_link_executable_action_name=CPP_LINK_EXECUTABLE_ACTION_NAME,", |
| " cpp_link_dynamic_library_action_name=CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME,", |
| " " |
| + " cpp_link_nodeps_dynamic_library_action_name=CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME,", |
| " cpp_link_static_library_action_name=CPP_LINK_STATIC_LIBRARY_ACTION_NAME,", |
| " strip_action_name=STRIP_ACTION_NAME)]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ")"); |
| |
| assertThat(getTarget("//a:r")).isNotNull(); |
| |
| StructImpl myInfo = getMyInfoFromTarget(getConfiguredTarget("//a:r")); |
| |
| assertThat(myInfo.getValue("c_compile_action_name")).isEqualTo(CppActionNames.C_COMPILE); |
| assertThat(myInfo.getValue("cpp_compile_action_name")).isEqualTo(CppActionNames.CPP_COMPILE); |
| assertThat(myInfo.getValue("linkstamp_compile_action_name")) |
| .isEqualTo(CppActionNames.LINKSTAMP_COMPILE); |
| assertThat(myInfo.getValue("cc_flags_make_variable_action_name_action_name")) |
| .isEqualTo(CppActionNames.CC_FLAGS_MAKE_VARIABLE); |
| assertThat(myInfo.getValue("cpp_module_codegen_action_name")) |
| .isEqualTo(CppActionNames.CPP_MODULE_CODEGEN); |
| assertThat(myInfo.getValue("cpp_header_parsing_action_name")) |
| .isEqualTo(CppActionNames.CPP_HEADER_PARSING); |
| assertThat(myInfo.getValue("cpp_module_compile_action_name")) |
| .isEqualTo(CppActionNames.CPP_MODULE_COMPILE); |
| assertThat(myInfo.getValue("assemble_action_name")).isEqualTo(CppActionNames.ASSEMBLE); |
| assertThat(myInfo.getValue("preprocess_assemble_action_name")) |
| .isEqualTo(CppActionNames.PREPROCESS_ASSEMBLE); |
| assertThat(myInfo.getValue("lto_indexing_action_name")).isEqualTo(CppActionNames.LTO_INDEXING); |
| assertThat(myInfo.getValue("lto_backend_action_name")).isEqualTo(CppActionNames.LTO_BACKEND); |
| assertThat(myInfo.getValue("cpp_link_executable_action_name")) |
| .isEqualTo(CppActionNames.CPP_LINK_EXECUTABLE); |
| assertThat(myInfo.getValue("cpp_link_dynamic_library_action_name")) |
| .isEqualTo(CppActionNames.CPP_LINK_DYNAMIC_LIBRARY); |
| assertThat(myInfo.getValue("cpp_link_nodeps_dynamic_library_action_name")) |
| .isEqualTo(CppActionNames.CPP_LINK_NODEPS_DYNAMIC_LIBRARY); |
| assertThat(myInfo.getValue("cpp_link_static_library_action_name")) |
| .isEqualTo(CppActionNames.CPP_LINK_STATIC_LIBRARY); |
| assertThat(myInfo.getValue("strip_action_name")).isEqualTo(CppActionNames.STRIP); |
| } |
| |
| @Test |
| public void testCompileBuildVariablesWithSourceFile() throws Exception { |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_COMPILE, |
| "cc_common.create_compile_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "source_file = 'foo/bar/hello'", |
| ")")) |
| .containsAtLeast("-c", "foo/bar/hello") |
| .inOrder(); |
| } |
| |
| @Test |
| public void testCompileBuildVariablesWithOutputFile() throws Exception { |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_COMPILE, |
| "cc_common.create_compile_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "output_file = 'foo/bar/hello.o'", |
| ")")) |
| .containsAtLeast("-o", "foo/bar/hello.o") |
| .inOrder(); |
| } |
| |
| @Test |
| public void testCompileBuildVariablesForIncludes() throws Exception { |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_COMPILE, |
| "cc_common.create_compile_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "include_directories = depset(['foo/bar/include'])", |
| ")")) |
| .contains("-Ifoo/bar/include"); |
| } |
| |
| @Test |
| public void testCompileBuildVariablesForFrameworkIncludes() throws Exception { |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_COMPILE, |
| "cc_common.create_compile_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "framework_include_directories = depset(['foo/bar'])", |
| ")")) |
| .contains("-Ffoo/bar"); |
| } |
| |
| @Test |
| public void testCompileBuildVariablesForDefines() throws Exception { |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_COMPILE, |
| "cc_common.create_compile_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "preprocessor_defines = depset(['DEBUG_FOO'])", |
| ")")) |
| .contains("-DDEBUG_FOO"); |
| } |
| |
| @Test |
| public void testCompileBuildVariablesForPic() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder() |
| .withFeatures(CppRuleClasses.SUPPORTS_PIC, CppRuleClasses.PIC)); |
| useConfiguration(); |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_COMPILE, |
| "cc_common.create_compile_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "use_pic = True", |
| ")")) |
| .contains("-fPIC"); |
| } |
| |
| @Test |
| public void testUserCompileFlags() throws Exception { |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_COMPILE, |
| "cc_common.create_compile_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "user_compile_flags = ['-foo']", |
| ")")) |
| .contains("-foo"); |
| } |
| |
| @Test |
| public void testCompileBuildVariablesForDummyLtoBackendAction() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, CcToolchainConfig.builder().withFeatures(CppRuleClasses.THIN_LTO)); |
| useConfiguration("--features=thin_lto"); |
| Sequence<String> commandLine = |
| commandLineForVariables( |
| CppActionNames.LTO_BACKEND, |
| "cc_common.create_compile_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "thinlto_input_bitcode_file = 'path/to/input',", |
| "thinlto_output_object_file = 'path/to/output',", |
| "thinlto_index = '/dev/null'", |
| ")"); |
| |
| assertThat(commandLine) |
| .containsAtLeast( |
| "thinlto_index=/dev/null", |
| "thinlto_output_object_file=path/to/output", |
| "thinlto_input_bitcode_file=path/to/input"); |
| } |
| |
| @Test |
| public void testCompileBuildVariablesWithVariablesExtension() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder().withFeatures("check_additional_variables_feature")); |
| useConfiguration("--features=check_additional_variables_feature"); |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_COMPILE, |
| "cc_common.create_compile_variables(", |
| " feature_configuration = feature_configuration,", |
| " cc_toolchain = toolchain,", |
| " variables_extension = {", |
| " 'string_variable': 'foo',", |
| " 'list_variable': ['bar', 'baz']", |
| " }", |
| ")")) |
| .containsAtLeast("--my_string=foo", "--my_list_element=bar", "--my_list_element=baz"); |
| } |
| |
| @Test |
| public void testEmptyLinkVariables() throws Exception { |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_LINK_EXECUTABLE, |
| "cc_common.create_link_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "user_link_flags = [ '-foo' ],", |
| ")")) |
| .contains("-foo"); |
| } |
| |
| @Test |
| public void testEmptyLinkVariablesContainSysroot() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, CcToolchainConfig.builder().withSysroot("/foo/bar/sysroot")); |
| useConfiguration(); |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_LINK_EXECUTABLE, |
| "cc_common.create_link_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| ")")) |
| .contains("--sysroot=/foo/bar/sysroot"); |
| } |
| |
| @Test |
| public void testLibrarySearchDirectoriesLinkVariables() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder().withFeatures("library_search_directories")); |
| useConfiguration(); |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_LINK_EXECUTABLE, |
| "cc_common.create_link_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "library_search_directories = depset([ 'a', 'b', 'c' ]),", |
| ")")) |
| .containsAtLeast("--library=a", "--library=b", "--library=c") |
| .inOrder(); |
| } |
| |
| @Test |
| public void testRuntimeLibrarySearchDirectoriesLinkVariables() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder().withFeatures("runtime_library_search_directories")); |
| useConfiguration(); |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_LINK_EXECUTABLE, |
| "cc_common.create_link_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "runtime_library_search_directories = depset([ 'a', 'b', 'c' ]),", |
| ")")) |
| .containsAtLeast("--runtime_library=a", "--runtime_library=b", "--runtime_library=c") |
| .inOrder(); |
| } |
| |
| @Test |
| public void testUserLinkFlagsLinkVariables() throws Exception { |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_LINK_EXECUTABLE, |
| "cc_common.create_link_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "user_link_flags = [ '-avocado' ],", |
| ")")) |
| .contains("-avocado"); |
| } |
| |
| @Test |
| public void testIfsoRelatedVariablesAreNotExposed() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, CcToolchainConfig.builder().withFeatures("uses_ifso_variables")); |
| useConfiguration(); |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_LINK_DYNAMIC_LIBRARY, |
| "cc_common.create_link_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| ")")) |
| .doesNotContain("--generate_interface_library_was_available"); |
| } |
| |
| @Test |
| public void testOutputFileLinkVariables() throws Exception { |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_LINK_EXECUTABLE, |
| "cc_common.create_link_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "output_file = 'foo/bar/executable',", |
| ")")) |
| .contains("foo/bar/executable"); |
| } |
| |
| @Test |
| public void testParamFileLinkVariables() throws Exception { |
| AnalysisMock.get().ccSupport().setupCcToolchainConfig(mockToolsConfig); |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_LINK_EXECUTABLE, |
| "cc_common.create_link_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "param_file = 'foo/bar/params',", |
| ")")) |
| .contains("@foo/bar/params"); |
| } |
| |
| @Test |
| public void testDefFileLinkVariables() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig(mockToolsConfig, CcToolchainConfig.builder().withFeatures("def")); |
| useConfiguration(); |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_LINK_EXECUTABLE, |
| "cc_common.create_link_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "def_file = 'foo/bar/def',", |
| ")")) |
| .contains("-qux_foo/bar/def"); |
| } |
| |
| @Test |
| public void testMustKeepDebugLinkVariables() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, CcToolchainConfig.builder().withFeatures("strip_debug_symbols")); |
| |
| useConfiguration(); |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_LINK_EXECUTABLE, |
| 0, |
| "cc_common.create_link_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "must_keep_debug = False,", |
| ")")) |
| .contains("-strip_stuff"); |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_LINK_EXECUTABLE, |
| 1, |
| "cc_common.create_link_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "must_keep_debug = True,", |
| ")")) |
| .doesNotContain("-strip_stuff"); |
| } |
| |
| @Test |
| public void testIsLinkingDynamicLibraryLinkVariables() throws Exception { |
| useConfiguration("--linkopt=-pie"); |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_LINK_EXECUTABLE, |
| 0, |
| "cc_common.create_link_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "is_linking_dynamic_library = True,", |
| "user_link_flags = [ '-pie' ],", |
| ")")) |
| .doesNotContain("-pie"); |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_LINK_EXECUTABLE, |
| 1, |
| "cc_common.create_link_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "is_linking_dynamic_library = False,", |
| "user_link_flags = [ '-pie' ],", |
| ")")) |
| .contains("-pie"); |
| } |
| |
| @Test |
| public void testIsUsingLinkerLinkVariables() throws Exception { |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_LINK_EXECUTABLE, |
| 0, |
| "cc_common.create_link_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "is_using_linker = True,", |
| "user_link_flags = [ '-i_dont_want_to_see_this_on_archiver_command_line' ],", |
| ")")) |
| .contains("-i_dont_want_to_see_this_on_archiver_command_line"); |
| assertThat( |
| commandLineForVariables( |
| CppActionNames.CPP_LINK_EXECUTABLE, |
| 1, |
| "cc_common.create_link_variables(", |
| "feature_configuration = feature_configuration,", |
| "cc_toolchain = toolchain,", |
| "is_using_linker = False,", |
| "user_link_flags = [ '-i_dont_want_to_see_this_on_archiver_command_line' ],", |
| ")")) |
| .doesNotContain("-i_dont_want_to_see_this_on_archiver_command_line"); |
| } |
| |
| private Sequence<String> commandLineForVariables(String actionName, String... variables) |
| throws Exception { |
| return commandLineForVariables(actionName, 0, variables); |
| } |
| |
| // This method is only there to change the package to fix multiple runs of this method in a single |
| // test. |
| // TODO(b/109917616): Remove pkgSuffix argument when bzl files are not cached within single test |
| private Sequence<String> commandLineForVariables( |
| String actionName, int pkgSuffix, String... variables) throws Exception { |
| scratch.file( |
| "a" + pkgSuffix + "/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| |
| scratch.file( |
| "a" + pkgSuffix + "/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " requested_features = ctx.features,", |
| " )", |
| " variables = " + Joiner.on("\n").join(variables), |
| " return [MyInfo(", |
| " command_line = cc_common.get_memory_inefficient_command_line(", |
| " feature_configuration = feature_configuration,", |
| " action_name = '" + actionName + "',", |
| " variables = variables))]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a" + pkgSuffix + ":alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| /** Calling {@link #getTarget} to get loading errors */ |
| getTarget("//a" + pkgSuffix + ":r"); |
| ConfiguredTarget r = getConfiguredTarget("//a" + pkgSuffix + ":r"); |
| if (r == null) { |
| return null; |
| } |
| @SuppressWarnings("unchecked") |
| Sequence<String> result = (Sequence<String>) getMyInfoFromTarget(r).getValue("command_line"); |
| return result; |
| } |
| |
| @Test |
| public void testCcCompilationProvider() throws Exception { |
| scratch.file( |
| "a/BUILD", |
| "load('//tools/build_defs/cc:rule.bzl', 'crule')", |
| "licenses(['notice'])", |
| "cc_library(", |
| " name='lib',", |
| " hdrs = ['lib.h'],", |
| " srcs = ['lib.cc'],", |
| " deps = ['r']", |
| ")", |
| "cc_library(", |
| " name = 'dep1',", |
| " srcs = ['dep1.cc'],", |
| " hdrs = ['dep1.h'],", |
| " includes = ['dep1/baz'],", |
| " defines = ['DEP1'],", |
| " local_defines = ['LOCALDEP1'],", |
| ")", |
| "cc_library(", |
| " name = 'dep2',", |
| " srcs = ['dep2.cc'],", |
| " hdrs = ['dep2.h'],", |
| " includes = ['dep2/qux'],", |
| " defines = ['DEP2'],", |
| ")", |
| "crule(name='r')"); |
| scratch.overwriteFile("tools/build_defs/cc/BUILD", ""); |
| scratch.file( |
| "tools/build_defs/cc/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " compilation_context = cc_common.create_compilation_context(", |
| " headers=depset([ctx.file._header]),", |
| " direct_textual_headers=[ctx.file._textual_header],", |
| " direct_public_headers=[ctx.file._public_header],", |
| " direct_private_headers=[ctx.file._private_header],", |
| " system_includes=depset([ctx.attr._system_include]),", |
| " includes=depset([ctx.attr._include]),", |
| " quote_includes=depset([ctx.attr._quote_include]),", |
| " framework_includes=depset([ctx.attr._framework_include]),", |
| " defines=depset([ctx.attr._define]))", |
| " cc_infos = [CcInfo(compilation_context=compilation_context)]", |
| " for dep in ctx.attr._deps:", |
| " cc_infos.append(dep[CcInfo])", |
| " merged_cc_info=cc_common.merge_cc_infos(cc_infos=cc_infos)", |
| " return [", |
| " merged_cc_info,", |
| " MyInfo(", |
| " merged_headers=merged_cc_info.compilation_context.headers,", |
| " textual_headers=compilation_context.direct_textual_headers,", |
| " public_headers=compilation_context.direct_public_headers,", |
| " private_headers=compilation_context.direct_private_headers,", |
| " merged_system_includes=merged_cc_info.compilation_context.system_includes,", |
| " merged_includes=merged_cc_info.compilation_context.includes,", |
| " merged_quote_includes=merged_cc_info.compilation_context.quote_includes,", |
| " " |
| + " merged_framework_includes=merged_cc_info.compilation_context.framework_includes,", |
| " merged_defines=merged_cc_info.compilation_context.defines", |
| " )]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_header': attr.label(allow_single_file=True,", |
| " default=Label('//a:header.h')),", |
| " '_textual_header': attr.label(allow_single_file=True,", |
| " default=Label('//a:textual_header.h')),", |
| " '_public_header': attr.label(allow_single_file=True,", |
| " default=Label('//a:public_header.h')),", |
| " '_private_header': attr.label(allow_single_file=True,", |
| " default=Label('//a:private_header.h')),", |
| " '_system_include': attr.string(default='foo/bar'),", |
| " '_include': attr.string(default='baz/qux'),", |
| " '_quote_include': attr.string(default='quux/abc'),", |
| " '_framework_include': attr.string(default='fuux/fgh'),", |
| " '_define': attr.string(default='MYDEFINE'),", |
| " '_local_define': attr.string(default='MYLOCALDEFINE'),", |
| " '_deps': attr.label_list(default=['//a:dep1', '//a:dep2'])", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| ConfiguredTarget lib = getConfiguredTarget("//a:lib"); |
| CcCompilationContext ccCompilationContext = lib.get(CcInfo.PROVIDER).getCcCompilationContext(); |
| assertThat( |
| ccCompilationContext.getDeclaredIncludeSrcs().toList().stream() |
| .map(Artifact::getFilename) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactly("lib.h", "header.h", "dep1.h", "dep2.h"); |
| |
| StructImpl myInfo = getMyInfoFromTarget(getConfiguredTarget("//a:r")); |
| |
| List<Artifact> mergedHeaders = |
| ((Depset) myInfo.getValue("merged_headers")).getSet(Artifact.class).toList(); |
| assertThat( |
| mergedHeaders.stream() |
| .map(Artifact::getFilename) |
| .collect(ImmutableList.toImmutableList())) |
| .containsAtLeast("header.h", "dep1.h", "dep2.h"); |
| |
| @SuppressWarnings("unchecked") |
| List<Artifact> textualHeaders = |
| ((Sequence<Artifact>) myInfo.getValue("textual_headers")).getImmutableList(); |
| assertThat( |
| textualHeaders.stream() |
| .map(Artifact::getFilename) |
| .collect(ImmutableList.toImmutableList())) |
| .contains("textual_header.h"); |
| @SuppressWarnings("unchecked") |
| List<Artifact> publicHeaders = |
| ((Sequence<Artifact>) myInfo.getValue("public_headers")).getImmutableList(); |
| assertThat( |
| publicHeaders.stream() |
| .map(Artifact::getFilename) |
| .collect(ImmutableList.toImmutableList())) |
| .contains("public_header.h"); |
| @SuppressWarnings("unchecked") |
| List<Artifact> privateHeaders = |
| ((Sequence<Artifact>) myInfo.getValue("private_headers")).getImmutableList(); |
| assertThat( |
| privateHeaders.stream() |
| .map(Artifact::getFilename) |
| .collect(ImmutableList.toImmutableList())) |
| .contains("private_header.h"); |
| |
| List<String> mergedDefines = |
| ((Depset) myInfo.getValue("merged_defines")).getSet(String.class).toList(); |
| assertThat(mergedDefines).containsAtLeast("MYDEFINE", "DEP1", "DEP2"); |
| assertThat(mergedDefines).doesNotContain("LOCALDEP1"); |
| |
| List<String> mergedSystemIncludes = |
| ((Depset) myInfo.getValue("merged_system_includes")).getSet(String.class).toList(); |
| assertThat(mergedSystemIncludes).containsAtLeast("foo/bar", "a/dep1/baz", "a/dep2/qux"); |
| |
| List<String> mergedIncludes = |
| ((Depset) myInfo.getValue("merged_includes")).getSet(String.class).toList(); |
| assertThat(mergedIncludes).contains("baz/qux"); |
| |
| List<String> mergedQuoteIncludes = |
| ((Depset) myInfo.getValue("merged_quote_includes")).getSet(String.class).toList(); |
| assertThat(mergedQuoteIncludes).contains("quux/abc"); |
| |
| List<String> mergedFrameworkIncludes = |
| ((Depset) myInfo.getValue("merged_framework_includes")).getSet(String.class).toList(); |
| assertThat(mergedFrameworkIncludes).contains("fuux/fgh"); |
| } |
| |
| @Test |
| public void testCcCompilationProviderDefaultValues() throws Exception { |
| scratch.file( |
| "a/BUILD", |
| "load('//tools/build_defs/cc:rule.bzl', 'crule')", |
| "licenses(['notice'])", |
| "crule(name='r')"); |
| scratch.overwriteFile("tools/build_defs/cc/BUILD", ""); |
| scratch.file( |
| "tools/build_defs/cc/rule.bzl", |
| "def _impl(ctx):", |
| " compilation_context = cc_common.create_compilation_context()", |
| "crule = rule(", |
| " _impl,", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| assertThat(getConfiguredTarget("//a:r")).isNotNull(); |
| } |
| |
| @Test |
| public void testCcCompilationProviderInvalidValues() throws Exception { |
| reporter.removeHandler(failFastHandler); |
| scratch.file( |
| "a/BUILD", |
| "load('//tools/build_defs/cc:rule.bzl', 'crule')", |
| "licenses(['notice'])", |
| "crule(name='r')"); |
| scratch.overwriteFile("tools/build_defs/cc/BUILD", ""); |
| scratch.file( |
| "tools/build_defs/cc/rule.bzl", |
| "def _impl(ctx):", |
| " compilation_context = cc_common.create_compilation_context(headers=[])", |
| "crule = rule(", |
| " _impl,", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| getConfiguredTarget("//a:r"); |
| assertContainsEvent("for headers, got list, want a depset of File"); |
| } |
| |
| @Test |
| public void testCreateCompilationOutputs_invalidDepset() throws Exception { |
| reporter.removeHandler(failFastHandler); |
| scratch.file("test/BUILD", "load(':my_rule.bzl', 'my_rule')", "my_rule(name='x')"); |
| scratch.file( |
| "test/my_rule.bzl", |
| "def _impl(ctx):", |
| " cc_common.create_compilation_outputs(", |
| " objects = depset([1, 2]), pic_objects = depset([1, 2]))", |
| "my_rule = rule(", |
| " _impl,", |
| ");"); |
| |
| assertThat(getConfiguredTarget("//test:x")).isNull(); |
| assertContainsEvent("for 'objects', got a depset of 'int', expected a depset of 'File'"); |
| } |
| |
| @Test |
| public void testCreateCompilationOutputs_empty() throws Exception { |
| scratch.file("test/BUILD", "load(':my_rule.bzl', 'my_rule')", "my_rule(name='x')"); |
| scratch.file( |
| "test/my_rule.bzl", |
| "def _impl(ctx):", |
| " cc_common.create_compilation_outputs()", |
| "my_rule = rule(", |
| " _impl,", |
| ");"); |
| |
| assertThat(getConfiguredTarget("//test:x")).isNotNull(); |
| } |
| |
| @Test |
| public void testCcLinkingContextOnWindows() throws Exception { |
| if (!AnalysisMock.get().isThisBazel()) { |
| return; |
| } |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder() |
| .withFeatures( |
| CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY, |
| CppRuleClasses.TARGETS_WINDOWS, |
| CppRuleClasses.SUPPORTS_DYNAMIC_LINKER)); |
| doTestCcLinkingContext( |
| ImmutableList.of("a.a", "libdep2.a", "b.rlib", "c.a", "d.a", "libdep1.a"), |
| ImmutableList.of("a.pic.a", "b.rlib", "c.pic.a", "e.pic.a"), |
| // The suffix of dynamic library is calculated based on repository name and package path |
| // to avoid conflicts with dynamic library from other packages. |
| ImmutableList.of("a.so", "libdep2_61.so", "b.so", "e.so", "libdep1_61.so")); |
| } |
| |
| @Test |
| public void testCcLinkingContext() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder() |
| .withFeatures( |
| CppRuleClasses.PIC, |
| CppRuleClasses.SUPPORTS_PIC, |
| CppRuleClasses.SUPPORTS_DYNAMIC_LINKER)); |
| doTestCcLinkingContext( |
| ImmutableList.of("a.a", "b.rlib", "c.a", "d.a"), |
| ImmutableList.of("a.pic.a", "libdep2.a", "b.rlib", "c.pic.a", "e.pic.a", "libdep1.a"), |
| ImmutableList.of("a.so", "liba_Slibdep2.so", "b.so", "e.so", "liba_Slibdep1.so")); |
| } |
| |
| @Test |
| public void testCcLinkingContextForExperimentalCcSharedLibrary() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder() |
| .withFeatures( |
| CppRuleClasses.PIC, |
| CppRuleClasses.SUPPORTS_PIC, |
| CppRuleClasses.SUPPORTS_DYNAMIC_LINKER)); |
| doTestCcLinkingContext( |
| ImmutableList.of("a.a", "b.rlib", "c.a", "d.a"), |
| ImmutableList.of("a.pic.a", "libdep2.a", "b.rlib", "c.pic.a", "e.pic.a", "libdep1.a"), |
| ImmutableList.of("a.so", "liba_Slibdep2.so", "b.so", "e.so", "liba_Slibdep1.so")); |
| } |
| |
| /** TODO(#8118): This test can go away once flag is flipped. */ |
| @Test |
| public void testIncompatibleDepsetForLibrariesToLinkGetter() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder() |
| .withFeatures( |
| CppRuleClasses.PIC, |
| CppRuleClasses.SUPPORTS_PIC, |
| CppRuleClasses.SUPPORTS_DYNAMIC_LINKER)); |
| this.setBuildLanguageOptions("--incompatible_depset_for_libraries_to_link_getter"); |
| setUpCcLinkingContextTest(false); |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| StructImpl info = ((StructImpl) getMyInfoFromTarget(a).getValue("info")); |
| |
| Depset librariesToLink = info.getValue("libraries_to_link", Depset.class); |
| assertThat( |
| librariesToLink.toList(LibraryToLink.class).stream() |
| .filter(x -> x.getStaticLibrary() != null) |
| .map(x -> x.getStaticLibrary().getFilename()) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactly("a.a", "b.rlib", "c.a", "d.a"); |
| assertThat( |
| librariesToLink.toList(LibraryToLink.class).stream() |
| .filter(x -> x.getPicStaticLibrary() != null) |
| .map(x -> x.getPicStaticLibrary().getFilename()) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactly("a.pic.a", "libdep2.a", "b.rlib", "c.pic.a", "e.pic.a", "libdep1.a"); |
| assertThat( |
| librariesToLink.toList(LibraryToLink.class).stream() |
| .filter(x -> x.getDynamicLibrary() != null) |
| .map(x -> x.getDynamicLibrary().getFilename()) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactly("a.so", "liba_Slibdep2.so", "b.so", "e.so", "liba_Slibdep1.so"); |
| } |
| |
| private static String getSolibRelativePath(Artifact library, CcToolchainProvider toolchain) { |
| return library.getRootRelativePath().relativeTo(toolchain.getSolibDirectory()).toString(); |
| } |
| |
| @Test |
| public void testSolibLinkDefault() throws Exception { |
| setUpCcLinkingContextTest(false); |
| scratch.file( |
| "foo/BUILD", |
| "load('//tools/build_defs/cc:rule.bzl', 'crule')", |
| "cc_binary(name='bin',", |
| " deps = [':a'],", |
| ")", |
| "crule(name='a',", |
| " dynamic_library = 'a.so',", |
| " interface_library = 'a.ifso',", |
| ")"); |
| ConfiguredTarget a = getConfiguredTarget("//foo:a"); |
| RuleContext ruleContext = getRuleContext(a); |
| CcToolchainProvider toolchain = |
| CppHelper.getToolchain(ruleContext, ruleContext.getPrerequisite("$cc_toolchain")); |
| StructImpl info = ((StructImpl) getMyInfoFromTarget(a).getValue("info")); |
| Depset librariesToLink = info.getValue("libraries_to_link", Depset.class); |
| assertThat( |
| librariesToLink.toList(LibraryToLink.class).stream() |
| .filter(x -> x.getDynamicLibrary() != null) |
| .map(x -> getSolibRelativePath(x.getDynamicLibrary(), toolchain)) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactly("_U_S_Sfoo_Ca___Ufoo/a.so"); |
| assertThat( |
| librariesToLink.toList(LibraryToLink.class).stream() |
| .filter(x -> x.getInterfaceLibrary() != null) |
| .map(x -> getSolibRelativePath(x.getInterfaceLibrary(), toolchain)) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactly("_U_S_Sfoo_Ca___Ufoo/a.ifso"); |
| } |
| |
| @Test |
| public void testSolibLinkCustom() throws Exception { |
| setUpCcLinkingContextTest(false); |
| scratch.file( |
| "foo/BUILD", |
| "load('//tools/build_defs/cc:rule.bzl', 'crule')", |
| "cc_binary(name='bin',", |
| " deps = [':a'],", |
| ")", |
| "crule(name='a',", |
| " dynamic_library = 'a.so',", |
| " interface_library = 'a.ifso',", |
| " dynamic_library_symlink_path = 'custom/libcustom.so',", |
| " interface_library_symlink_path = 'libcustom.ifso',", |
| ")"); |
| ConfiguredTarget a = getConfiguredTarget("//foo:a"); |
| RuleContext ruleContext = getRuleContext(a); |
| CcToolchainProvider toolchain = |
| CppHelper.getToolchain(ruleContext, ruleContext.getPrerequisite("$cc_toolchain")); |
| StructImpl info = ((StructImpl) getMyInfoFromTarget(a).getValue("info")); |
| Depset librariesToLink = info.getValue("libraries_to_link", Depset.class); |
| assertThat( |
| librariesToLink.toList(LibraryToLink.class).stream() |
| .filter(x -> x.getDynamicLibrary() != null) |
| .map( |
| x -> |
| x.getDynamicLibrary() |
| .getRootRelativePath() |
| .relativeTo(toolchain.getSolibDirectory()) |
| .toString()) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactly("custom/libcustom.so"); |
| assertThat( |
| librariesToLink.toList(LibraryToLink.class).stream() |
| .filter(x -> x.getInterfaceLibrary() != null) |
| .map( |
| x -> |
| x.getInterfaceLibrary() |
| .getRootRelativePath() |
| .relativeTo(toolchain.getSolibDirectory()) |
| .toString()) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactly("libcustom.ifso"); |
| } |
| |
| @Test |
| public void testReallyLongSolibLink() throws Exception { |
| setUpCcLinkingContextTest(false); |
| |
| String longpath = |
| "this/is/a/really/really/really/really/really/really/really/really/really/really/" |
| + "really/really/really/really/really/really/really/really/really/really/really/" |
| + "really/really/long/path/that/generates/really/long/solib/link/path"; |
| scratch.file( |
| longpath + "/BUILD", |
| "load('//tools/build_defs/cc:rule.bzl', 'crule')", |
| "crule(name='a',", |
| " dynamic_library = 'a.so',", |
| ")"); |
| |
| ConfiguredTarget a = getConfiguredTarget("//" + longpath + ":a"); |
| StructImpl info = ((StructImpl) getMyInfoFromTarget(a).getValue("info")); |
| Depset librariesToLink = info.getValue("libraries_to_link", Depset.class); |
| ImmutableList<String> dynamicLibraryParentDirectories = |
| librariesToLink.toList(LibraryToLink.class).stream() |
| .filter(x -> x.getDynamicLibrary() != null) |
| .map( |
| x -> x.getDynamicLibrary().getRootRelativePath().getParentDirectory().getBaseName()) |
| .collect(ImmutableList.toImmutableList()); |
| for (String dynamicLibraryParentDirectory : dynamicLibraryParentDirectories) { |
| assertThat(dynamicLibraryParentDirectory.length()).isLessThan(MAX_FILENAME_LENGTH + 1); |
| } |
| } |
| |
| private void doTestCcLinkingContext( |
| List<String> staticLibraryList, |
| List<String> picStaticLibraryList, |
| List<String> dynamicLibraryList) |
| throws Exception { |
| useConfiguration("--features=-supports_interface_shared_libraries"); |
| this.setBuildLanguageOptions("--incompatible_depset_for_libraries_to_link_getter"); |
| setUpCcLinkingContextTest(false); |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| |
| StructImpl info = ((StructImpl) getMyInfoFromTarget(a).getValue("info")); |
| @SuppressWarnings("unchecked") |
| Sequence<String> userLinkFlags = |
| (Sequence<String>) info.getValue("user_link_flags", Sequence.class); |
| assertThat(userLinkFlags.getImmutableList()) |
| .containsExactly("-la", "-lc2", "-DEP2_LINKOPT", "-lc1", "-lc2", "-DEP1_LINKOPT"); |
| Depset additionalInputs = info.getValue("additional_inputs", Depset.class); |
| assertThat(additionalInputs.toList(Artifact.class).stream().map(Artifact::getFilename)) |
| .containsExactly("b.lds", "d.lds"); |
| Depset linkstamps = info.getValue("linkstamps", Depset.class); |
| assertThat( |
| artifactsToStrings( |
| linkstamps.toList(Linkstamp.class).stream() |
| .map(Linkstamp::getArtifact) |
| .collect(ImmutableList.toImmutableList()))) |
| .containsExactly("src a/linkstamp.cc"); |
| Collection<LibraryToLink> librariesToLink = |
| info.getValue("libraries_to_link", Depset.class).toList(LibraryToLink.class); |
| assertThat( |
| librariesToLink.stream() |
| .filter(x -> x.getStaticLibrary() != null) |
| .map(x -> x.getStaticLibrary().getFilename()) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactlyElementsIn(staticLibraryList); |
| assertThat( |
| librariesToLink.stream() |
| .filter(x -> x.getPicStaticLibrary() != null) |
| .map(x -> x.getPicStaticLibrary().getFilename()) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactlyElementsIn(picStaticLibraryList); |
| assertThat( |
| librariesToLink.stream() |
| .filter(x -> x.getDynamicLibrary() != null) |
| .map(x -> x.getDynamicLibrary().getFilename()) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactlyElementsIn(dynamicLibraryList); |
| assertThat( |
| librariesToLink.stream() |
| .filter(x -> x.getInterfaceLibrary() != null) |
| .map(x -> x.getInterfaceLibrary().getFilename()) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactly("a.ifso"); |
| Artifact staticLibrary = info.getValue("static_library", Artifact.class); |
| assertThat(staticLibrary.getFilename()).isEqualTo("a.a"); |
| Artifact picStaticLibrary = info.getValue("pic_static_library", Artifact.class); |
| assertThat(picStaticLibrary.getFilename()).isEqualTo("a.pic.a"); |
| Artifact dynamicLibrary = info.getValue("dynamic_library", Artifact.class); |
| assertThat(dynamicLibrary.getFilename()).isEqualTo("a.so"); |
| Artifact interfaceLibrary = info.getValue("interface_library", Artifact.class); |
| assertThat(interfaceLibrary.getFilename()).isEqualTo("a.ifso"); |
| boolean alwayslink = info.getValue("alwayslink", Boolean.class); |
| assertThat(alwayslink).isTrue(); |
| |
| ConfiguredTarget bin = getConfiguredTarget("//a:bin"); |
| assertThat(bin).isNotNull(); |
| } |
| |
| private void setUpCcLinkingContextTest(boolean enableExperimentalCcImport) throws Exception { |
| if (enableExperimentalCcImport) { |
| useConfiguration("--experimental_starlark_cc_import"); |
| } |
| scratch.file( |
| "a/BUILD", |
| "load('//tools/build_defs/cc:rule.bzl', 'crule')", |
| "cc_binary(name='bin',", |
| " deps = [':a'],", |
| ")", |
| "crule(name='a',", |
| " static_library = 'a.a',", |
| " pic_static_library = 'a.pic.a',", |
| " dynamic_library = 'a.so',", |
| " interface_library = 'a.ifso',", |
| " user_link_flags = ['-la', '-lc2'],", |
| " alwayslink = True,", |
| " deps = [':c', ':dep2', ':b'],", |
| ")", |
| "crule(name='b',", |
| " static_library = 'b.rlib',", |
| " pic_static_library = 'b.rlib',", |
| " dynamic_library = 'b.so',", |
| " deps = [':c', ':d'],", |
| " additional_inputs = ['b.lds'],", |
| ")", |
| "crule(name='c',", |
| " static_library = 'c.a',", |
| " pic_static_library = 'c.pic.a',", |
| " user_link_flags = ['-lc1', '-lc2'],", |
| ")", |
| "crule(name='d',", |
| " static_library = 'd.a',", |
| " alwayslink = True,", |
| " deps = [':e'],", |
| " additional_inputs = ['d.lds'],", |
| ")", |
| "crule(name='e',", |
| " pic_static_library = 'e.pic.a',", |
| " dynamic_library = 'e.so',", |
| " deps = [':dep1'],", |
| ")", |
| "cc_toolchain_alias(name='alias')", |
| "cc_library(", |
| " name = 'dep1',", |
| " srcs = ['dep1.cc'],", |
| " linkstamp = 'linkstamp.cc',", |
| " hdrs = ['dep1.h'],", |
| " linkopts = ['-DEP1_LINKOPT'],", |
| ")", |
| "cc_library(", |
| " name = 'dep2',", |
| " srcs = ['dep2.cc'],", |
| " hdrs = ['dep2.h'],", |
| " linkopts = ['-DEP2_LINKOPT'],", |
| ")"); |
| scratch.file("a/lib.a", ""); |
| scratch.file("a/lib.so", ""); |
| scratch.overwriteFile("tools/build_defs/cc/BUILD", ""); |
| scratch.file( |
| "tools/build_defs/cc/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "linker_input = cc_common.create_linker_input(", |
| " owner=Label('//toplevel'),", |
| " user_link_flags=[['-first_flag'], ['-second_flag']])", |
| "top_linking_context_smoke = cc_common.create_linking_context(", |
| " linker_inputs=depset([linker_input]))", |
| "def _create(ctx, feature_configuration, static_library, pic_static_library,", |
| " dynamic_library,", |
| " interface_library, dynamic_library_symlink_path, interface_library_symlink_path,", |
| " alwayslink, objects, pic_objects):", |
| " return cc_common.create_library_to_link(", |
| " actions=ctx.actions, feature_configuration=feature_configuration, ", |
| " cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo], ", |
| " static_library=static_library, pic_static_library=pic_static_library,", |
| " dynamic_library=dynamic_library, interface_library=interface_library,", |
| " dynamic_library_symlink_path=dynamic_library_symlink_path,", |
| " interface_library_symlink_path=interface_library_symlink_path,", |
| " alwayslink=alwayslink, ", |
| enableExperimentalCcImport ? " objects=objects, " : "", |
| enableExperimentalCcImport ? " pic_objects=pic_objects" : "", |
| " )", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " library_to_link = _create(ctx, feature_configuration, ctx.file.static_library, ", |
| " ctx.file.pic_static_library, ctx.file.dynamic_library, ctx.file.interface_library,", |
| " ctx.attr.dynamic_library_symlink_path,", |
| " ctx.attr.interface_library_symlink_path,", |
| " ctx.attr.alwayslink, ctx.files.objects, ctx.files.pic_objects)", |
| " linker_input = cc_common.create_linker_input(", |
| " owner=ctx.label,", |
| " libraries=depset([library_to_link]),", |
| " user_link_flags=depset(ctx.attr.user_link_flags),", |
| " additional_inputs=depset(ctx.files.additional_inputs))", |
| " linking_context = cc_common.create_linking_context(", |
| " linker_inputs=depset([linker_input]))", |
| " cc_infos = [CcInfo(linking_context=linking_context)]", |
| " for dep in ctx.attr.deps:", |
| " cc_infos.append(dep[CcInfo])", |
| " merged_cc_info = cc_common.merge_cc_infos(cc_infos=cc_infos)", |
| " merged_libraries = []", |
| " merged_additional_inputs = []", |
| " merged_user_link_flags = []", |
| " merged_linkstamps = []", |
| " for l in merged_cc_info.linking_context.linker_inputs.to_list():", |
| " merged_libraries.extend(l.libraries)", |
| " merged_additional_inputs.extend(l.additional_inputs)", |
| " merged_user_link_flags.extend(l.user_link_flags)", |
| " merged_linkstamps.extend(l.linkstamps)", |
| " linkstamps_linker_input = cc_common.create_linker_input(", |
| " owner=ctx.label,", |
| " linkstamps=depset(merged_linkstamps))", |
| " return [", |
| " MyInfo(", |
| " info = struct(", |
| " cc_info = merged_cc_info,", |
| " user_link_flags = merged_user_link_flags,", |
| " additional_inputs = depset(merged_additional_inputs),", |
| " libraries_to_link = depset(merged_libraries),", |
| " linkstamps = depset(linkstamps_linker_input.linkstamps),", |
| " static_library = library_to_link.static_library,", |
| " pic_static_library = library_to_link.pic_static_library,", |
| " dynamic_library = library_to_link.dynamic_library,", |
| " interface_library = library_to_link.interface_library,", |
| " alwayslink = library_to_link.alwayslink,", |
| " objects = library_to_link.objects,", |
| " pic_objects = library_to_link.pic_objects),", |
| " ),", |
| " merged_cc_info]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " 'user_link_flags' : attr.string_list(),", |
| " 'additional_inputs': attr.label_list(allow_files=True),", |
| " 'static_library': attr.label(allow_single_file=True),", |
| " 'pic_static_library': attr.label(allow_single_file=True),", |
| " 'dynamic_library': attr.label(allow_single_file=True),", |
| " 'dynamic_library_symlink_path': attr.string(),", |
| " 'interface_library': attr.label(allow_single_file=True),", |
| " 'interface_library_symlink_path': attr.string(),", |
| " 'objects': attr.label_list(allow_files=True),", |
| " 'pic_objects': attr.label_list(allow_files=True),", |
| " 'alwayslink': attr.bool(),", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias')),", |
| " 'deps': attr.label_list(),", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| } |
| |
| private void loadCcToolchainConfigLib() throws IOException { |
| scratch.appendFile("tools/cpp/BUILD", ""); |
| scratch.overwriteFile( |
| "tools/cpp/cc_toolchain_config_lib.bzl", |
| ResourceLoader.readFromResources( |
| TestConstants.RULES_CC_REPOSITORY_EXECROOT + "cc/cc_toolchain_config_lib.bzl")); |
| } |
| |
| @Test |
| public void testVariableWithValue() throws Exception { |
| loadCcToolchainConfigLib(); |
| createVariableWithValueRule("one", /* name= */ "None", /* value= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//one:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("name parameter of variable_with_value should be a string, found NoneType"); |
| |
| createVariableWithValueRule("two", /* name= */ "'abc'", /* value= */ "None"); |
| |
| e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//two:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("value parameter of variable_with_value should be a string, found NoneType"); |
| |
| createVariableWithValueRule("three", /* name= */ "''", /* value= */ "None"); |
| |
| e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//three:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("name parameter of variable_with_value must be a nonempty string"); |
| |
| createVariableWithValueRule("four", /* name= */ "'abc'", /* value= */ "''"); |
| |
| e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//four:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("value parameter of variable_with_value must be a nonempty string"); |
| |
| createVariableWithValueRule("five", /* name= */ "'abc'", /* value= */ "'def'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//five:a"); |
| StarlarkInfo variable = (StarlarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| assertThat(variable).isNotNull(); |
| VariableWithValue v = CcModule.variableWithValueFromStarlark(variable); |
| assertThat(v).isNotNull(); |
| assertThat(v.variable).isEqualTo("abc"); |
| assertThat(v.value).isEqualTo("def"); |
| |
| createEnvEntryRule("six", /* key= */ "'abc'", /* value= */ "'def'"); |
| t = getConfiguredTarget("//six:a"); |
| StarlarkInfo envEntry = (StarlarkInfo) getMyInfoFromTarget(t).getValue("entry"); |
| EvalException ee = |
| assertThrows(EvalException.class, () -> CcModule.variableWithValueFromStarlark(envEntry)); |
| assertThat(ee) |
| .hasMessageThat() |
| .contains("Expected object of type 'variable_with_value', received 'env_entry"); |
| } |
| |
| private void createVariableWithValueRule(String pkg, String name, String value) |
| throws IOException { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl', 'variable_with_value')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(variable = variable_with_value(", |
| " name = " + name + ",", |
| " value = " + value + "))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testCustomVariableWithValue_none_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomVariableWithValueRule("one", /* name= */ "None", /* value= */ "None"); |
| ConfiguredTarget t = getConfiguredTarget("//one:a"); |
| StarlarkInfo variable = (StarlarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.variableWithValueFromStarlark(variable)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'name' parameter of variable_with_value must be a nonempty string."); |
| } |
| |
| @Test |
| public void testCustomVariableWithValue_string_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomVariableWithValueRule("two", /* name= */ "'abc'", /* value= */ "None"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//two:a"); |
| StarlarkInfo variable = (StarlarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.variableWithValueFromStarlark(variable)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'value' parameter of variable_with_value must be a nonempty string."); |
| } |
| |
| @Test |
| public void testCustomVariableWithValue_string_struct() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomVariableWithValueRule("three", /* name= */ "'abc'", /* value= */ "struct()"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| StarlarkInfo variable = (StarlarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.variableWithValueFromStarlark(variable)); |
| assertThat(e).hasMessageThat().contains("Field 'value' is not of 'java.lang.String' type."); |
| } |
| |
| @Test |
| public void testCustomVariableWithValue_boolean_string() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomVariableWithValueRule("four", /* name= */ "True", /* value= */ "'abc'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo variable = (StarlarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.variableWithValueFromStarlark(variable)); |
| assertThat(e).hasMessageThat().contains("Field 'name' is not of 'java.lang.String' type."); |
| } |
| |
| private void createCustomVariableWithValueRule(String pkg, String name, String value) |
| throws IOException { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(variable = struct(", |
| " name = " + name + ",", |
| " value = " + value + ",", |
| " type_name = 'variable_with_value'))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testEnvEntry_none_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createEnvEntryRule("one", "None", /* value= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//one:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("key parameter of env_entry should be a string, found NoneType"); |
| } |
| |
| @Test |
| public void testEnvEntry_string_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createEnvEntryRule("two", "'abc'", /* value= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//two:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("value parameter of env_entry should be a string, found NoneType"); |
| } |
| |
| @Test |
| public void testEnvEntry_emptyString_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createEnvEntryRule("three", "''", /* value= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//three:a")); |
| assertThat(e).hasMessageThat().contains("key parameter of env_entry must be a nonempty string"); |
| } |
| |
| @Test |
| public void testEnvEntry_string_emptyString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createEnvEntryRule("four", "'abc'", /* value= */ "''"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//four:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("value parameter of env_entry must be a nonempty string"); |
| } |
| |
| @Test |
| public void testEnvEntry_string_string() throws Exception { |
| loadCcToolchainConfigLib(); |
| createEnvEntryRule("five", "'abc'", /* value= */ "'def'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//five:a"); |
| StarlarkInfo entryProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("entry"); |
| assertThat(entryProvider).isNotNull(); |
| EnvEntry entry = CcModule.envEntryFromStarlark(entryProvider); |
| assertThat(entry).isNotNull(); |
| StringValueParser parser = new StringValueParser("def"); |
| assertThat(entry).isEqualTo(new EnvEntry("abc", parser.getChunks())); |
| } |
| |
| @Test |
| public void testEnvEntryVariable_string_string() throws Exception { |
| loadCcToolchainConfigLib(); |
| createVariableWithValueRule("six", /* name= */ "'abc'", /* value= */ "'def'"); |
| ConfiguredTarget t = getConfiguredTarget("//six:a"); |
| StarlarkInfo variable = (StarlarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.envEntryFromStarlark(variable)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Expected object of type 'env_entry', received 'variable_with_value"); |
| } |
| |
| private void createEnvEntryRule(String pkg, String key, String value) throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl', 'env_entry')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(entry = env_entry(", |
| " key = " + key + ",", |
| " value = " + value + "))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testCustomEnvEntry_none_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomEnvEntryRule("one", /* key= */ "None", /* value= */ "None"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//one:a"); |
| StarlarkInfo entry = (StarlarkInfo) getMyInfoFromTarget(t).getValue("entry"); |
| EvalException e = assertThrows(EvalException.class, () -> CcModule.envEntryFromStarlark(entry)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'key' parameter of env_entry must be a nonempty string."); |
| } |
| |
| @Test |
| public void testCustomEnvEntry_string_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomEnvEntryRule("two", /* key= */ "'abc'", /* value= */ "None"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//two:a"); |
| StarlarkInfo entry = (StarlarkInfo) getMyInfoFromTarget(t).getValue("entry"); |
| EvalException e = |
| assertThrows( |
| "Should have failed because of empty string.", |
| EvalException.class, |
| () -> CcModule.envEntryFromStarlark(entry)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'value' parameter of env_entry must be a nonempty string."); |
| } |
| |
| @Test |
| public void testCustomEnvEntry_string_struct() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomEnvEntryRule("three", /* key= */ "'abc'", /* value= */ "struct()"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| StarlarkInfo entry = (StarlarkInfo) getMyInfoFromTarget(t).getValue("entry"); |
| EvalException e = assertThrows(EvalException.class, () -> CcModule.envEntryFromStarlark(entry)); |
| assertThat(e).hasMessageThat().contains("Field 'value' is not of 'java.lang.String' type."); |
| } |
| |
| @Test |
| public void testCustomEnvEntry_boolean_string() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomEnvEntryRule("four", /* key= */ "True", /* value= */ "'abc'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo entry = (StarlarkInfo) getMyInfoFromTarget(t).getValue("entry"); |
| EvalException e = assertThrows(EvalException.class, () -> CcModule.envEntryFromStarlark(entry)); |
| assertThat(e).hasMessageThat().contains("Field 'key' is not of 'java.lang.String' type."); |
| } |
| |
| private void createCustomEnvEntryRule(String pkg, String key, String value) throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(entry = struct(", |
| " key = " + key + ",", |
| " value = " + value + ",", |
| " type_name = 'env_entry'))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testToolPath_none_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createToolPathRule("one", /* name= */ "None", "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//one:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("name parameter of tool_path should be a string, found NoneType"); |
| } |
| |
| @Test |
| public void testToolPath_string_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createToolPathRule("two", /* name= */ "'abc'", "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//two:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("path parameter of tool_path should be a string, found NoneType"); |
| } |
| |
| @Test |
| public void testToolPath_emptyString_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createToolPathRule("three", /* name= */ "''", "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//three:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("name parameter of tool_path must be a nonempty string"); |
| } |
| |
| @Test |
| public void testToolPath_string_emptyString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createToolPathRule("four", /* name= */ "'abc'", "''"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//four:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("path parameter of tool_path must be a nonempty string"); |
| } |
| |
| @Test |
| public void testToolPath_string_escapedString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createToolPathRule("five", /* name= */ "'abc'", "'/d/e/f'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//five:a"); |
| StarlarkInfo toolPathProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("toolpath"); |
| assertThat(toolPathProvider).isNotNull(); |
| Pair<String, String> toolPath = CcModule.toolPathFromStarlark(toolPathProvider); |
| assertThat(toolPath).isNotNull(); |
| assertThat(toolPath.first).isEqualTo("abc"); |
| assertThat(toolPath.second).isEqualTo("/d/e/f"); |
| } |
| |
| @Test |
| public void testToolPath_string_string() throws Exception { |
| loadCcToolchainConfigLib(); |
| createVariableWithValueRule("six", /* name= */ "'abc'", /* value= */ "'def'"); |
| ConfiguredTarget t = getConfiguredTarget("//six:a"); |
| StarlarkInfo variable = (StarlarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolPathFromStarlark(variable)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Expected object of type 'tool_path', received 'variable_with_value"); |
| } |
| |
| private void createToolPathRule(String pkg, String name, String path) throws IOException { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl', 'tool_path')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(toolpath = tool_path(", |
| " name = " + name + ",", |
| " path = " + path + "))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testCustomToolPath_name_mustBeNonEmpty() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomToolPathRule("one", /* name= */ "None", /* path= */ "None"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//one:a"); |
| StarlarkInfo toolPath = (StarlarkInfo) getMyInfoFromTarget(t).getValue("toolpath"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolPathFromStarlark(toolPath)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'name' parameter of tool_path must be a nonempty string."); |
| } |
| |
| @Test |
| public void testCustomToolPath_path_mustBeNonEmpty() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomToolPathRule("two", /* name= */ "'abc'", /* path= */ "None"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//two:a"); |
| StarlarkInfo toolPath = (StarlarkInfo) getMyInfoFromTarget(t).getValue("toolpath"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolPathFromStarlark(toolPath)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'path' parameter of tool_path must be a nonempty string."); |
| } |
| |
| @Test |
| public void testCustomToolPath_path_mustBeString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomToolPathRule("three", /* name= */ "'abc'", /* path= */ "struct()"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| StarlarkInfo toolPath = (StarlarkInfo) getMyInfoFromTarget(t).getValue("toolpath"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolPathFromStarlark(toolPath)); |
| assertThat(e).hasMessageThat().contains("Field 'path' is not of 'java.lang.String' type."); |
| } |
| |
| @Test |
| public void testCustomToolPath_name_mustBeString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomToolPathRule("four", /* name= */ "True", /* path= */ "'abc'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo toolPath = (StarlarkInfo) getMyInfoFromTarget(t).getValue("toolpath"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolPathFromStarlark(toolPath)); |
| assertThat(e).hasMessageThat().contains("Field 'name' is not of 'java.lang.String' type."); |
| } |
| |
| private void createCustomToolPathRule(String pkg, String name, String path) throws IOException { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl', 'tool_path')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(toolpath = struct(", |
| " name = " + name + ",", |
| " path = " + path + ",", |
| " type_name = 'tool_path'))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testMakeVariable() throws Exception { |
| loadCcToolchainConfigLib(); |
| createMakeVariablerule("one", /* name= */ "None", /* value= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//one:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("name parameter of make_variable should be a string, found NoneType"); |
| |
| createMakeVariablerule("two", /* name= */ "'abc'", /* value= */ "None"); |
| |
| e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//two:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("value parameter of make_variable should be a string, found NoneType"); |
| |
| createMakeVariablerule("three", /* name= */ "''", /* value= */ "None"); |
| |
| e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//three:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("name parameter of make_variable must be a nonempty string"); |
| |
| createMakeVariablerule("four", /* name= */ "'abc'", /* value= */ "''"); |
| |
| e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//four:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("value parameter of make_variable must be a nonempty string"); |
| |
| createMakeVariablerule("five", /* name= */ "'abc'", /* value= */ "'val'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//five:a"); |
| StarlarkInfo makeVariableProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| assertThat(makeVariableProvider).isNotNull(); |
| Pair<String, String> makeVariable = CcModule.makeVariableFromStarlark(makeVariableProvider); |
| assertThat(makeVariable).isNotNull(); |
| assertThat(makeVariable.first).isEqualTo("abc"); |
| assertThat(makeVariable.second).isEqualTo("val"); |
| |
| createVariableWithValueRule("six", /* name= */ "'abc'", /* value= */ "'def'"); |
| t = getConfiguredTarget("//six:a"); |
| StarlarkInfo variable = (StarlarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException ee = |
| assertThrows(EvalException.class, () -> CcModule.makeVariableFromStarlark(variable)); |
| assertThat(ee) |
| .hasMessageThat() |
| .contains("Expected object of type 'make_variable', received 'variable_with_value"); |
| } |
| |
| private void createMakeVariablerule(String pkg, String name, String value) throws IOException { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl', 'make_variable')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(variable = make_variable(", |
| " name = " + name + ",", |
| " value = " + value + "))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testCustomMakeVariable_none_none() throws Exception { |
| createCustomMakeVariableRule("one", /* name= */ "None", /* value= */ "None"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//one:a"); |
| StarlarkInfo makeVariableProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.makeVariableFromStarlark(makeVariableProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'name' parameter of make_variable must be a nonempty string."); |
| } |
| |
| @Test |
| public void testCustomMakeVariable_string_none() throws Exception { |
| createCustomMakeVariableRule("two", /* name= */ "'abc'", /* value= */ "None"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//two:a"); |
| StarlarkInfo makeVariableProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.makeVariableFromStarlark(makeVariableProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'value' parameter of make_variable must be a nonempty string."); |
| } |
| |
| @Test |
| public void testCustomMakeVariable_list_none() throws Exception { |
| createCustomMakeVariableRule("three", /* name= */ "[]", /* value= */ "None"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| StarlarkInfo makeVariableProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.makeVariableFromStarlark(makeVariableProvider)); |
| assertThat(e).hasMessageThat().contains("Field 'name' is not of 'java.lang.String' type."); |
| } |
| |
| @Test |
| public void testCustomMakeVariable_string_boolean() throws Exception { |
| createCustomMakeVariableRule("four", /* name= */ "'abc'", /* value= */ "True"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo makeVariableProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.makeVariableFromStarlark(makeVariableProvider)); |
| assertThat(e).hasMessageThat().contains("Field 'value' is not of 'java.lang.String' type."); |
| } |
| |
| private void createCustomMakeVariableRule(String pkg, String name, String value) |
| throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(variable = struct(", |
| " name = " + name + ",", |
| " value = " + value + ",", |
| " type_name = 'make_variable'))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testWithFeatureSet() throws Exception { |
| loadCcToolchainConfigLib(); |
| createWithFeatureSetRule("one", /* features= */ "None", /* notFeatures= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//one:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("features parameter of with_feature_set should be a list, found NoneType"); |
| |
| createWithFeatureSetRule("two", /* features= */ "['abc']", /* notFeatures= */ "None"); |
| |
| e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//two:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("not_features parameter of with_feature_set should be a list, found NoneType"); |
| |
| createWithFeatureSetRule("three", /* features= */ "'asdf'", /* notFeatures= */ "None"); |
| |
| e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//three:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("features parameter of with_feature_set should be a list, found string"); |
| |
| createWithFeatureSetRule("four", /* features= */ "['abc']", /* notFeatures= */ "'def'"); |
| |
| e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//four:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("not_features parameter of with_feature_set should be a list, found string"); |
| |
| createWithFeatureSetRule( |
| "five", /* features= */ "['f1', 'f2']", /* notFeatures= */ "['nf1', 'nf2']"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//five:a"); |
| StarlarkInfo withFeatureSetProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("wfs"); |
| assertThat(withFeatureSetProvider).isNotNull(); |
| WithFeatureSet withFeatureSet = CcModule.withFeatureSetFromStarlark(withFeatureSetProvider); |
| assertThat(withFeatureSet).isNotNull(); |
| assertThat(withFeatureSet.getFeatures()).containsExactly("f1", "f2"); |
| assertThat(withFeatureSet.getNotFeatures()).containsExactly("nf1", "nf2"); |
| |
| createVariableWithValueRule("six", /* name= */ "'abc'", /* value= */ "'def'"); |
| t = getConfiguredTarget("//six:a"); |
| StarlarkInfo variable = (StarlarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException ee = |
| assertThrows(EvalException.class, () -> CcModule.withFeatureSetFromStarlark(variable)); |
| assertThat(ee) |
| .hasMessageThat() |
| .contains("Expected object of type 'with_feature_set', received 'variable_with_value"); |
| } |
| |
| private void createWithFeatureSetRule(String pkg, String features, String notFeatures) |
| throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl', 'with_feature_set')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(wfs = with_feature_set(", |
| " features = " + features + ",", |
| " not_features = " + notFeatures + "))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testCustomWithFeatureSet_struct_none() throws Exception { |
| createCustomWithFeatureSetRule("one", /* features= */ "struct()", /* notFeatures= */ "None"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//one:a"); |
| StarlarkInfo withFeatureSetProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("wfs"); |
| assertThat(withFeatureSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.withFeatureSetFromStarlark(withFeatureSetProvider)); |
| assertThat(e).hasMessageThat().contains("for features, got struct, want sequence"); |
| } |
| |
| @Test |
| public void testCustomWithFeatureSet_listOfString_struct() throws Exception { |
| createCustomWithFeatureSetRule("two", /* features= */ "['abc']", /* notFeatures= */ "struct()"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//two:a"); |
| StarlarkInfo withFeatureSetProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("wfs"); |
| assertThat(withFeatureSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.withFeatureSetFromStarlark(withFeatureSetProvider)); |
| assertThat(e).hasMessageThat().contains("for not_features, got struct, want sequence"); |
| } |
| |
| @Test |
| public void testCustomWithFeatureSet_listOfStruct_emptyList() throws Exception { |
| createCustomWithFeatureSetRule("three", /* features= */ "[struct()]", /* notFeatures= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| StarlarkInfo withFeatureSetProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("wfs"); |
| assertThat(withFeatureSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.withFeatureSetFromStarlark(withFeatureSetProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("at index 0 of features, got element of type struct, want string"); |
| } |
| |
| @Test |
| public void testCustomWithFeatureSet_emptyList_listOfStruct() throws Exception { |
| createCustomWithFeatureSetRule("four", /* features= */ "[]", /* notFeatures= */ "[struct()]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo withFeatureSetProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("wfs"); |
| assertThat(withFeatureSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.withFeatureSetFromStarlark(withFeatureSetProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("at index 0 of not_features, got element of type struct, want string"); |
| } |
| |
| private void createCustomWithFeatureSetRule(String pkg, String features, String notFeatures) |
| throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(wfs = struct(", |
| " features = " + features + ",", |
| " not_features = " + notFeatures + ",", |
| " type_name = 'with_feature_set'))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testEnvSet_none_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createEnvSetRule( |
| "one", /* actions= */ "['a1']", /* envEntries= */ "None", /* withFeatures= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//one:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("env_entries parameter of env_set should be a list, found NoneType"); |
| } |
| |
| @Test |
| public void testEnvSet_list_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createEnvSetRule( |
| "two", /* actions= */ "['a1']", /* envEntries= */ "['abc']", /* withFeatures= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//two:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("with_features parameter of env_set should be a list, found NoneType"); |
| } |
| |
| @Test |
| public void testEnvSet_string_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createEnvSetRule( |
| "three", /* actions= */ "['a1']", /* envEntries= */ "'asdf'", /* withFeatures= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//three:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("env_entries parameter of env_set should be a list, found string"); |
| } |
| |
| @Test |
| public void testEnvSet_list_string() throws Exception { |
| loadCcToolchainConfigLib(); |
| createEnvSetRule( |
| "four", /* actions= */ "['a1']", /* envEntries= */ "['abc']", /* withFeatures= */ "'def'"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//four:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("with_features parameter of env_set should be a list, found string"); |
| } |
| |
| @Test |
| public void testEnvSet_envEntry_emptyList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createEnvSetRule( |
| "five", |
| /* actions= */ "['a1']", |
| /* envEntries= */ "[env_entry(key = 'a', value = 'b')," |
| + "variable_with_value(name = 'a', value = 'b')]", |
| /* withFeatures= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//five:a"); |
| StarlarkInfo envSetProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("envset"); |
| assertThat(envSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.envSetFromStarlark(envSetProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Expected object of type 'env_entry', received 'variable_with_value'"); |
| } |
| |
| @Test |
| public void testEnvSet_emptyList_emptyList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createEnvSetRule("six", /* actions= */ "[]", /* envEntries= */ "[]", /* withFeatures= */ "[]"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//six:a")); |
| assertThat(e).hasMessageThat().contains("actions parameter of env_set must be a nonempty list"); |
| } |
| |
| @Test |
| public void testEnvSet_envEntry_featureSet() throws Exception { |
| loadCcToolchainConfigLib(); |
| createEnvSetRule( |
| "seven", |
| /* actions= */ "['a1']", |
| /* envEntries= */ "[env_entry(key = 'a', value = 'b')]", |
| /* withFeatures= */ "[with_feature_set(features = ['a'])]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//seven:a"); |
| StarlarkInfo envSetProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("envset"); |
| assertThat(envSetProvider).isNotNull(); |
| EnvSet envSet = CcModule.envSetFromStarlark(envSetProvider); |
| assertThat(envSet).isNotNull(); |
| } |
| |
| @Test |
| public void testEnvSet_string_string() throws Exception { |
| loadCcToolchainConfigLib(); |
| createVariableWithValueRule("eight", /* name= */ "'abc'", /* value= */ "'def'"); |
| ConfiguredTarget t = getConfiguredTarget("//eight:a"); |
| StarlarkInfo variable = (StarlarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.envSetFromStarlark(variable)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Expected object of type 'env_set', received 'variable_with_value"); |
| } |
| |
| private void createEnvSetRule(String pkg, String actions, String envEntries, String withFeatures) |
| throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl',", |
| " 'env_set', 'env_entry', 'with_feature_set', 'variable_with_value')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(envset = env_set(", |
| " actions = " + actions + ",", |
| " env_entries = " + envEntries + ",", |
| " with_features = " + withFeatures + "))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testCustomEnvSet_none_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomEnvSetRule( |
| "one", /* actions= */ "[]", /* envEntries= */ "None", /* withFeatures= */ "None"); |
| ConfiguredTarget t = getConfiguredTarget("//one:a"); |
| StarlarkInfo envSetProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("envset"); |
| assertThat(envSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.envSetFromStarlark(envSetProvider)); |
| assertThat(e).hasMessageThat().contains("actions parameter of env_set must be a nonempty list"); |
| } |
| |
| @Test |
| public void testCustomEnvSet_struct_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomEnvSetRule( |
| "two", /* actions= */ "['a1']", /* envEntries= */ "struct()", /* withFeatures= */ "None"); |
| ConfiguredTarget t = getConfiguredTarget("//two:a"); |
| StarlarkInfo envSetProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("envset"); |
| assertThat(envSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.envSetFromStarlark(envSetProvider)); |
| assertThat(e).hasMessageThat().contains("for env_entries, got struct, want sequence"); |
| } |
| |
| @Test |
| public void testCustomEnvSet_structList_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomEnvSetRule( |
| "three", |
| /* actions= */ "['a1']", |
| /* envEntries= */ "[struct()]", |
| /* withFeatures= */ "None"); |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| StarlarkInfo envSetProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("envset"); |
| assertThat(envSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.envSetFromStarlark(envSetProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Expected object of type 'env_entry', received 'struct'"); |
| } |
| |
| @Test |
| public void testCustomEnvSet_envEntry_string() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomEnvSetRule( |
| "four", |
| /* actions= */ "['a1']", |
| /* envEntries= */ "[env_entry(key = 'a', value = 'b')]", |
| /* withFeatures= */ "'a'"); |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo envSetProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("envset"); |
| assertThat(envSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.envSetFromStarlark(envSetProvider)); |
| assertThat(e).hasMessageThat().contains("for with_features, got string, want sequence"); |
| } |
| |
| @Test |
| public void testCustomEnvSet_envEntry_envEntry() throws Exception { |
| loadCcToolchainConfigLib(); |
| |
| createCustomEnvSetRule( |
| "five", |
| /* actions= */ "['a1']", |
| /* envEntries= */ "[env_entry(key = 'a', value = 'b')]", |
| /* withFeatures= */ "[env_entry(key = 'a', value = 'b')]"); |
| ConfiguredTarget t = getConfiguredTarget("//five:a"); |
| StarlarkInfo envSetProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("envset"); |
| assertThat(envSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.envSetFromStarlark(envSetProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Expected object of type 'with_feature_set', received 'env_entry'."); |
| } |
| |
| private void createCustomEnvSetRule( |
| String pkg, String actions, String envEntries, String withFeatures) throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl',", |
| " 'env_entry', 'with_feature_set', 'variable_with_value')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(envset = struct(", |
| " actions = " + actions + ",", |
| " env_entries = " + envEntries + ",", |
| " with_features = " + withFeatures + ",", |
| " type_name = 'env_set'))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testFlagGroup_flagGroup_notListofFlags() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagGroupRule( |
| "one", |
| /* flags= */ "[]", |
| /* flagGroups= */ "[]", |
| /* iterateOver= */ "None", |
| /* expandIfTrue= */ "None", |
| /* expandIfFalse= */ "None", |
| /* expandIfAvailable= */ "None", |
| /* expandIfNotAvailable= */ "None", |
| /* expandIfEqual= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//one:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("flag_group must contain either a list of flags or a list of flag_groups"); |
| } |
| |
| @Test |
| public void testFlagGroup_iterateOver_notString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagGroupRule( |
| "two", |
| /* flags= */ "['a']", |
| /* flagGroups= */ "[]", |
| /* iterateOver= */ "struct(val = 'a')", |
| /* expandIfTrue= */ "None", |
| /* expandIfFalse= */ "None", |
| /* expandIfAvailable= */ "None", |
| /* expandIfNotAvailable= */ "None", |
| /* expandIfEqual= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//two:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("iterate_over parameter of flag_group should be a string, found struct"); |
| } |
| |
| @Test |
| public void testFlagGroup_expandIfTrue_notString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagGroupRule( |
| "three", |
| /* flags= */ "['a']", |
| /* flagGroups= */ "[]", |
| /* iterateOver= */ "None", |
| /* expandIfTrue= */ "struct(val = 'a')", |
| /* expandIfFalse= */ "None", |
| /* expandIfAvailable= */ "None", |
| /* expandIfNotAvailable= */ "None", |
| /* expandIfEqual= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//three:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("expand_if_true parameter of flag_group should be a string, found struct"); |
| } |
| |
| @Test |
| public void testFlagGroup_expandIfFalse_notString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagGroupRule( |
| "four", |
| /* flags= */ "['a']", |
| /* flagGroups= */ "[]", |
| /* iterateOver= */ "None", |
| /* expandIfTrue= */ "None", |
| /* expandIfFalse= */ "struct(val = 'a')", |
| /* expandIfAvailable= */ "None", |
| /* expandIfNotAvailable= */ "None", |
| /* expandIfEqual= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//four:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("expand_if_false parameter of flag_group should be a string, found struct"); |
| } |
| |
| @Test |
| public void testFlagGroup_expandIfAvailable_notString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagGroupRule( |
| "five", |
| /* flags= */ "['a']", |
| /* flagGroups= */ "[]", |
| /* iterateOver= */ "None", |
| /* expandIfTrue= */ "None", |
| /* expandIfFalse= */ "None", |
| /* expandIfAvailable= */ "struct(val = 'a')", |
| /* expandIfNotAvailable= */ "None", |
| /* expandIfEqual= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//five:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("expand_if_available parameter of flag_group should be a string, found struct"); |
| } |
| |
| @Test |
| public void testFlagGroup_expandIfNotAvailable_notString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagGroupRule( |
| "six", |
| /* flags= */ "['a']", |
| /* flagGroups= */ "[]", |
| /* iterateOver= */ "None", |
| /* expandIfTrue= */ "None", |
| /* expandIfFalse= */ "None", |
| /* expandIfAvailable= */ "None", |
| /* expandIfNotAvailable= */ "struct(val = 'a')", |
| /* expandIfEqual= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//six:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains( |
| "expand_if_not_available parameter of flag_group should be a string, found struct"); |
| } |
| |
| @Test |
| public void testFlagGroup_flagGroup_cannotContainFlagAndGroup() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagGroupRule( |
| "seven", |
| /* flags= */ "['a']", |
| /* flagGroups= */ "['b']", |
| /* iterateOver= */ "None", |
| /* expandIfTrue= */ "None", |
| /* expandIfFalse= */ "None", |
| /* expandIfAvailable= */ "None", |
| /* expandIfNotAvailable= */ "struct(val = 'a')", |
| /* expandIfEqual= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//seven:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("flag_group must not contain both a flag and another flag_group"); |
| } |
| |
| @Test |
| public void testFlagGroup_expandIfEqual_notStarlarkInfo() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagGroupRule( |
| "eight", |
| /* flags= */ "['a']", |
| /* flagGroups= */ "[]", |
| /* iterateOver= */ "'a'", |
| /* expandIfTrue= */ "'b'", |
| /* expandIfFalse= */ "''", |
| /* expandIfAvailable= */ "''", |
| /* expandIfNotAvailable= */ "''", |
| /* expandIfEqual= */ "'a'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//eight:a"); |
| StarlarkInfo flagGroupProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromStarlark(flagGroupProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains( |
| "Field 'expand_if_equal' is not of " |
| + "'com.google.devtools.build.lib.packages.StarlarkInfo' type."); |
| } |
| |
| @Test |
| public void testFlagGroup() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagGroupRule( |
| "nine", |
| /* flags= */ "[]", |
| /* flagGroups= */ "[flag_group(flags = ['a']), flag_group(flags = ['b'])]", |
| /* iterateOver= */ "''", |
| /* expandIfTrue= */ "''", |
| /* expandIfFalse= */ "''", |
| /* expandIfAvailable= */ "''", |
| /* expandIfNotAvailable= */ "''", |
| /* expandIfEqual= */ "variable_with_value(name = 'a', value = 'b')"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//nine:a"); |
| StarlarkInfo flagGroupProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| FlagGroup f = CcModule.flagGroupFromStarlark(flagGroupProvider); |
| assertThat(f).isNotNull(); |
| } |
| |
| @Test |
| public void testFlagGroup_flagGroup_notStruct() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagGroupRule( |
| "ten", |
| /* flags= */ "[]", |
| /* flagGroups= */ "[flag_group(flags = ['a']), struct(value = 'a')]", |
| /* iterateOver= */ "''", |
| /* expandIfTrue= */ "''", |
| /* expandIfFalse= */ "''", |
| /* expandIfAvailable= */ "''", |
| /* expandIfNotAvailable= */ "''", |
| /* expandIfEqual= */ "variable_with_value(name = 'a', value = 'b')"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//ten:a"); |
| StarlarkInfo flagGroupProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromStarlark(flagGroupProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Expected object of type 'flag_group', received 'struct'"); |
| } |
| |
| private void createFlagGroupRule( |
| String pkg, |
| String flags, |
| String flagGroups, |
| String iterateOver, |
| String expandIfTrue, |
| String expandIfFalse, |
| String expandIfAvailable, |
| String expandIfNotAvailable, |
| String expandIfEqual) |
| throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl',", |
| " 'env_set', 'env_entry', 'with_feature_set', 'variable_with_value', 'flag_group')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(flaggroup = flag_group(", |
| " flags = " + flags + ",", |
| " flag_groups = " + flagGroups + ",", |
| " expand_if_true = " + expandIfTrue + ",", |
| " expand_if_false = " + expandIfFalse + ",", |
| " expand_if_available = " + expandIfAvailable + ",", |
| " expand_if_not_available = " + expandIfNotAvailable + ",", |
| " expand_if_equal = " + expandIfEqual + ",", |
| " iterate_over = " + iterateOver + "))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testSingleChunkFlagIsUsed() throws Exception { |
| loadCcToolchainConfigLib(); |
| |
| createCustomFlagGroupRule( |
| "single_chunk_flag", |
| /* flags= */ "['a']", |
| /* flagGroups= */ "[]", |
| /* iterateOver= */ "''", |
| /* expandIfTrue= */ "''", |
| /* expandIfFalse= */ "''", |
| /* expandIfAvailable= */ "''", |
| /* expandIfNotAvailable= */ "''", |
| /* expandIfEqual= */ "None"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//single_chunk_flag:a"); |
| StarlarkInfo flagGroupProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| FlagGroup flagGroup = CcModule.flagGroupFromStarlark(flagGroupProvider); |
| assertThat(flagGroup.getExpandables()).isNotEmpty(); |
| assertThat(flagGroup.getExpandables().get(0)).isInstanceOf(SingleChunkFlag.class); |
| } |
| |
| @Test |
| public void testCustomFlagGroup_iterateOver_notString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFlagGroupRule( |
| "one", |
| /* flags= */ "['a']", |
| /* flagGroups= */ "[]", |
| /* iterateOver= */ "struct()", |
| /* expandIfTrue= */ "'b'", |
| /* expandIfFalse= */ "''", |
| /* expandIfAvailable= */ "''", |
| /* expandIfNotAvailable= */ "''", |
| /* expandIfEqual= */ "None"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//one:a"); |
| StarlarkInfo flagGroupProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromStarlark(flagGroupProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Field 'iterate_over' is not of 'java.lang.String' type."); |
| } |
| |
| @Test |
| public void testCustomFlagGroup_expandIfTrue_notString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFlagGroupRule( |
| "two", |
| /* flags= */ "[]", |
| /* flagGroups= */ "[flag_group(flags = ['a']), flag_group(flags = ['b'])]", |
| /* iterateOver= */ "''", |
| /* expandIfTrue= */ "struct()", |
| /* expandIfFalse= */ "''", |
| /* expandIfAvailable= */ "''", |
| /* expandIfNotAvailable= */ "''", |
| /* expandIfEqual= */ "variable_with_value(name = 'a', value = 'b')"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//two:a"); |
| StarlarkInfo flagGroupProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromStarlark(flagGroupProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Field 'expand_if_true' is not of 'java.lang.String' type."); |
| } |
| |
| @Test |
| public void testCustomFlagGroup_expandIfFalse_notString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFlagGroupRule( |
| "three", |
| /* flags= */ "[]", |
| /* flagGroups= */ "[flag_group(flags = ['a'])]", |
| /* iterateOver= */ "''", |
| /* expandIfTrue= */ "''", |
| /* expandIfFalse= */ "True", |
| /* expandIfAvailable= */ "''", |
| /* expandIfNotAvailable= */ "''", |
| /* expandIfEqual= */ "variable_with_value(name = 'a', value = 'b')"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| StarlarkInfo flagGroupProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromStarlark(flagGroupProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Field 'expand_if_false' is not of 'java.lang.String' type."); |
| } |
| |
| @Test |
| public void testCustomFlagGroup_expandIfAvailable_notString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFlagGroupRule( |
| "four", |
| /* flags= */ "[]", |
| /* flagGroups= */ "[flag_group(flags = ['a'])]", |
| /* iterateOver= */ "''", |
| /* expandIfTrue= */ "''", |
| /* expandIfFalse= */ "''", |
| /* expandIfAvailable= */ "struct()", |
| /* expandIfNotAvailable= */ "''", |
| /* expandIfEqual= */ "variable_with_value(name = 'a', value = 'b')"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo flagGroupProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromStarlark(flagGroupProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Field 'expand_if_available' is not of 'java.lang.String' type."); |
| } |
| |
| @Test |
| public void testCustomFlagGroup_expandIfNotAvailable_notString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFlagGroupRule( |
| "five", |
| /* flags= */ "[]", |
| /* flagGroups= */ "[flag_group(flags = ['a'])]", |
| /* iterateOver= */ "''", |
| /* expandIfTrue= */ "''", |
| /* expandIfFalse= */ "''", |
| /* expandIfAvailable= */ "''", |
| /* expandIfNotAvailable= */ "3", |
| /* expandIfEqual= */ "variable_with_value(name = 'a', value = 'b')"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//five:a"); |
| StarlarkInfo flagGroupProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromStarlark(flagGroupProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Field 'expand_if_not_available' is not of 'java.lang.String' type."); |
| } |
| |
| @Test |
| public void testCustomFlagGroup_expandIfEqual_notStruct() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFlagGroupRule( |
| "six", |
| /* flags= */ "[]", |
| /* flagGroups= */ "[flag_group(flags = ['a'])]", |
| /* iterateOver= */ "''", |
| /* expandIfTrue= */ "''", |
| /* expandIfFalse= */ "''", |
| /* expandIfAvailable= */ "''", |
| /* expandIfNotAvailable= */ "''", |
| /* expandIfEqual= */ "struct(name = 'a', value = 'b')"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//six:a"); |
| StarlarkInfo flagGroupProvider = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromStarlark(flagGroupProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Expected object of type 'variable_with_value', received 'struct'."); |
| } |
| |
| private void createCustomFlagGroupRule( |
| String pkg, |
| String flags, |
| String flagGroups, |
| String iterateOver, |
| String expandIfTrue, |
| String expandIfFalse, |
| String expandIfAvailable, |
| String expandIfNotAvailable, |
| String expandIfEqual) |
| throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl',", |
| " 'env_set', 'env_entry', 'with_feature_set', 'variable_with_value', 'flag_group')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(flaggroup = struct(", |
| " flags = " + flags + ",", |
| " flag_groups = " + flagGroups + ",", |
| " expand_if_true = " + expandIfTrue + ",", |
| " expand_if_false = " + expandIfFalse + ",", |
| " expand_if_available = " + expandIfAvailable + ",", |
| " expand_if_not_available = " + expandIfNotAvailable + ",", |
| " expand_if_equal = " + expandIfEqual + ",", |
| " iterate_over = " + iterateOver + ",", |
| " type_name = 'flag_group'))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testTool_path_mustBeNonEmpty() throws Exception { |
| loadCcToolchainConfigLib(); |
| createToolRule("one", /* path= */ "''", /* withFeatures= */ "[]", /* requirements= */ "[]"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//one:a")); |
| assertThat(e).hasMessageThat().contains("path parameter of tool must be a nonempty string"); |
| } |
| |
| @Test |
| public void testTool_withFeatures_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createToolRule("two", /* path= */ "'a'", /* withFeatures= */ "None", /* requirements= */ "[]"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//two:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("with_features parameter of tool should be a list, found NoneType"); |
| } |
| |
| @Test |
| public void testTool_executionRequirements_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createToolRule( |
| "three", /* path= */ "'a'", /* withFeatures= */ "[]", /* requirements= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//three:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("execution_requirements parameter of tool should be a list, found NoneType"); |
| } |
| |
| @Test |
| public void testTool_withFeatures_mustBeWithFeatureSet() throws Exception { |
| loadCcToolchainConfigLib(); |
| createToolRule( |
| "four", |
| /* path= */ "'a'", |
| /* withFeatures= */ "[struct(val = 'a')]", |
| /* requirements= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Expected object of type 'with_feature_set', received 'struct'"); |
| } |
| |
| @Test |
| public void testTool_requirements_mustBeString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createToolRule( |
| "five", |
| /* path= */ "'a'", |
| /* withFeatures= */ "[]", |
| /* requirements= */ "[struct(val = 'a')]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//five:a"); |
| StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("at index 0 of execution_requirements, got element of type struct, want string"); |
| } |
| |
| @Test |
| public void testTool() throws Exception { |
| loadCcToolchainConfigLib(); |
| createToolRule( |
| "six", |
| /* path= */ "'/a/b/c'", |
| /* withFeatures= */ "[with_feature_set(features = ['a'])]", |
| /* requirements= */ "['a', 'b']"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//six:a"); |
| StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| Tool tool = CcModule.toolFromStarlark(toolStruct); |
| assertThat(tool.getExecutionRequirements()).containsExactly("a", "b"); |
| assertThat(tool.getToolPathString(PathFragment.EMPTY_FRAGMENT)).isEqualTo("/a/b/c"); |
| assertThat(tool.getWithFeatureSetSets()) |
| .contains(new WithFeatureSet(ImmutableSet.of("a"), ImmutableSet.of())); |
| } |
| |
| private void createToolRule(String pkg, String path, String withFeatures, String requirements) |
| throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl', 'with_feature_set', 'tool')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(tool = tool(", |
| " path = " + path + ",", |
| " with_features = " + withFeatures + ",", |
| " execution_requirements = " + requirements + "))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testCustomTool_path_nonEmpty() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomToolRule( |
| "one", /* path= */ "''", /* withFeatures= */ "[]", /* requirements= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//one:a"); |
| StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); |
| assertThat(e).hasMessageThat().contains("The 'path' field of tool must be a nonempty string."); |
| } |
| |
| @Test |
| public void testCustomTool_path_mustBeString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomToolRule( |
| "two", /* path= */ "struct()", /* withFeatures= */ "[]", /* requirements= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//two:a"); |
| StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); |
| assertThat(e).hasMessageThat().contains("Field 'path' is not of 'java.lang.String' type."); |
| } |
| |
| @Test |
| public void testCustomTool_withFeatures_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomToolRule( |
| "three", /* path= */ "'a'", /* withFeatures= */ "struct()", /* requirements= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); |
| assertThat(e).hasMessageThat().contains("for with_features, got struct, want sequence"); |
| } |
| |
| @Test |
| public void testCustomTool_withFeatures_mustBeWithFeatureSet() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomToolRule( |
| "four", |
| /* path= */ "'a'", |
| /* withFeatures= */ "[struct(val = 'a')]", |
| /* requirements= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Expected object of type 'with_feature_set', received 'struct'"); |
| } |
| |
| @Test |
| public void testCustomTool_executionRequirements_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomToolRule( |
| "five", /* path= */ "'a'", /* withFeatures= */ "[]", /* requirements= */ "'a'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//five:a"); |
| StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("for execution_requirements, got string, want sequence"); |
| } |
| |
| @Test |
| public void testCustomTool_executionRequirements_mustBeString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomToolRule( |
| "six", /* path= */ "'a'", /* withFeatures= */ "[]", /* requirements= */ "[struct()]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//six:a"); |
| StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("at index 0 of execution_requirements, got element of type struct, want string"); |
| } |
| |
| private void createCustomToolRule( |
| String pkg, String path, String withFeatures, String requirements) throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl', 'with_feature_set')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(tool = struct(", |
| " path = " + path + ",", |
| " with_features = " + withFeatures + ",", |
| " execution_requirements = " + requirements + ",", |
| " type_name = 'tool'))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testFlagSet_withFeatures_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagSetRule( |
| "two", /* actions= */ "['a']", /* flagGroups= */ "[]", /* withFeatures= */ "None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//two:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("with_features parameter of flag_set should be a list, found NoneType"); |
| } |
| |
| @Test |
| public void testFlagSet_flagGroups_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagSetRule( |
| "three", /* actions= */ "['a']", /* flagGroups= */ "None", /* withFeatures= */ "[]"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//three:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("flag_groups parameter of flag_set should be a list, found NoneType"); |
| } |
| |
| @Test |
| public void testFlagSet_actions_mustBeString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagSetRule( |
| "four", |
| /* actions= */ "['a', struct(val = 'a')]", |
| /* flagGroups= */ "[]", |
| /* withFeatures= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo flagSetStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.flagSetFromStarlark(flagSetStruct, /* actionName= */ null)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("at index 1 of actions, got element of type struct, want string"); |
| } |
| |
| @Test |
| public void testFlagSet_flagGroups_mustBeFlagGroup() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagSetRule( |
| "five", |
| /* actions= */ "['a']", |
| /* flagGroups= */ "[flag_group(flags = ['a']), struct(value = 'a')]", |
| /* withFeatures= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//five:a"); |
| StarlarkInfo flagSetStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.flagSetFromStarlark(flagSetStruct, /* actionName= */ null)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Expected object of type 'flag_group', received 'struct'"); |
| } |
| |
| @Test |
| public void testFlagSet_withFeatures_mustBeWithFeatureSet() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagSetRule( |
| "six", |
| /* actions= */ "['a']", |
| /* flagGroups= */ "[flag_group(flags = ['a'])]", |
| /* withFeatures= */ "[struct(val = 'a')]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//six:a"); |
| StarlarkInfo flagSetStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.flagSetFromStarlark(flagSetStruct, /* actionName= */ null)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Expected object of type 'with_feature_set', received 'struct'"); |
| } |
| |
| @Test |
| public void testFlagSet() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagSetRule( |
| "seven", |
| /* actions= */ "['a']", |
| /* flagGroups= */ "[flag_group(flags = ['a'])]", |
| /* withFeatures= */ "[with_feature_set(features = ['a'])]"); |
| ConfiguredTarget t = getConfiguredTarget("//seven:a"); |
| StarlarkInfo flagSetStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| FlagSet f = CcModule.flagSetFromStarlark(flagSetStruct, /* actionName= */ null); |
| assertThat(f).isNotNull(); |
| } |
| |
| @Test |
| public void testFlagSet_actionConfig_notActionList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagSetRule( |
| "eight", |
| /* actions= */ "['a']", |
| /* flagGroups= */ "[flag_group(flags = ['a'])]", |
| /* withFeatures= */ "[struct(val = 'a')]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//eight:a"); |
| StarlarkInfo flagSetStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.flagSetFromStarlark(flagSetStruct, /* actionName= */ "action")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Thus, you must not specify action lists in an action_config's flag set."); |
| } |
| |
| @Test |
| public void testFlagSet_emptyAction() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagSetRule( |
| "nine", |
| /* actions= */ "[]", |
| /* flagGroups= */ "[flag_group(flags = ['a'])]", |
| /* withFeatures= */ "[with_feature_set(features = ['a'])]"); |
| ConfiguredTarget t = getConfiguredTarget("//nine:a"); |
| StarlarkInfo flagSetStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| FlagSet f = CcModule.flagSetFromStarlark(flagSetStruct, /* actionName= */ "action"); |
| assertThat(f).isNotNull(); |
| assertThat(f.getActions()).containsExactly("action"); |
| } |
| |
| private void createFlagSetRule(String pkg, String actions, String flagGroups, String withFeatures) |
| throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl',", |
| " 'env_set', 'env_entry', 'with_feature_set', 'variable_with_value', 'flag_group',", |
| " 'flag_set')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(flagset = flag_set(", |
| " flag_groups = " + flagGroups + ",", |
| " actions = " + actions + ",", |
| " with_features = " + withFeatures + "))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testCustomFlagSet() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFlagSetRule( |
| "one", /* actions= */ "[]", /* flagGroups= */ "[]", /* withFeatures= */ "[]"); |
| |
| ConfiguredTarget target = getConfiguredTarget("//one:a"); |
| StarlarkInfo flagSet = (StarlarkInfo) getMyInfoFromTarget(target).getValue("flagset"); |
| assertThat(flagSet).isNotNull(); |
| FlagSet flagSetObject = CcModule.flagSetFromStarlark(flagSet, /* actionName */ null); |
| assertThat(flagSetObject).isNotNull(); |
| } |
| |
| @Test |
| public void testCustomFlagSet_flagGroups_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFlagSetRule( |
| "two", /* actions= */ "['a']", /* flagGroups= */ "struct()", /* withFeatures= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//two:a"); |
| StarlarkInfo flagSetStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.flagSetFromStarlark(flagSetStruct, /* actionName */ null)); |
| assertThat(e).hasMessageThat().contains("for flag_groups, got struct, want sequence"); |
| } |
| |
| @Test |
| public void testCustomFlagSet_withFeatures_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFlagSetRule( |
| "three", /* actions= */ "['a']", /* flagGroups= */ "[]", /* withFeatures= */ "struct()"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| StarlarkInfo flagSetStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.flagSetFromStarlark(flagSetStruct, /* actionName */ null)); |
| assertThat(e).hasMessageThat().contains("for with_features, got struct, want sequence"); |
| } |
| |
| @Test |
| public void testCustomFlagSet_actions_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFlagSetRule( |
| "four", /* actions= */ "struct()", /* flagGroups= */ "[]", /* withFeatures= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo flagSetStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.flagSetFromStarlark(flagSetStruct, /* actionName */ null)); |
| assertThat(e).hasMessageThat().contains("for actions, got struct, want sequence"); |
| } |
| |
| private void createCustomFlagSetRule( |
| String pkg, String actions, String flagGroups, String withFeatures) throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl',", |
| " 'env_set', 'env_entry', 'with_feature_set', 'variable_with_value', 'flag_group',", |
| " 'flag_set')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(flagset = struct(", |
| " flag_groups = " + flagGroups + ",", |
| " actions = " + actions + ",", |
| " with_features = " + withFeatures + ",", |
| " type_name = 'flag_set'))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testActionConfig_actionName_mustBeNonEmpty() throws Exception { |
| loadCcToolchainConfigLib(); |
| createActionConfigRule( |
| "one", |
| /* actionName= */ "''", |
| /* enabled= */ "True", |
| /* tools= */ "[]", |
| /* flagSets= */ "[]", |
| /* implies= */ "[]"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//one:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("name parameter of action_config must be a nonempty string"); |
| } |
| |
| @Test |
| public void testActionConfig_enabled_mustBeBool() throws Exception { |
| loadCcToolchainConfigLib(); |
| createActionConfigRule( |
| "two", |
| /* actionName= */ "'actionname'", |
| /* enabled= */ "['asd']", |
| /* tools= */ "[]", |
| /* flagSets= */ "[]", |
| /* implies= */ "[]"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//two:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("enabled parameter of action_config should be a bool, found list"); |
| } |
| |
| @Test |
| public void testActionConfig_tools_mustBeTool() throws Exception { |
| loadCcToolchainConfigLib(); |
| createActionConfigRule( |
| "three", |
| /* actionName= */ "'actionname'", |
| /* enabled= */ "True", |
| /* tools= */ "[with_feature_set(features = ['a'])]", |
| /* flagSets= */ "[]", |
| /* implies= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| StarlarkInfo actionConfigStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Expected object of type 'tool', received 'with_feature_set'"); |
| } |
| |
| @Test |
| public void testActionConfig_flagSets_mustBeFlagSet() throws Exception { |
| loadCcToolchainConfigLib(); |
| createActionConfigRule( |
| "four", |
| /* actionName= */ "'actionname'", |
| /* enabled= */ "True", |
| /* tools= */ "[tool(path = 'a/b/c')]", |
| /* flagSets= */ "[tool(path = 'a/b/c')]", |
| /* implies= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo actionConfigStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); |
| assertThat(e).hasMessageThat().contains("Expected object of type 'flag_set', received 'tool'"); |
| } |
| |
| @Test |
| public void testActionConfig_implies_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createActionConfigRule( |
| "five", |
| /* actionName= */ "'actionname'", |
| /* enabled= */ "True", |
| /* tools= */ "[tool(path = 'a/b/c')]", |
| /* flagSets= */ "[]", |
| /* implies= */ "flag_set(actions = ['a', 'b'])"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//five:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("implies parameter of action_config should be a list, found struct"); |
| } |
| |
| @Test |
| public void testActionConfig_implies_mustContainString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createActionConfigRule( |
| "six", |
| /* actionName= */ "'actionname'", |
| /* enabled= */ "True", |
| /* tools= */ "[tool(path = 'a/b/c')]", |
| /* flagSets= */ "[]", |
| /* implies= */ "[flag_set(actions = ['a', 'b'])]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//six:a"); |
| StarlarkInfo actionConfigStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("at index 0 of implies, got element of type struct, want string"); |
| } |
| |
| @Test |
| public void testActionConfig_implies_mustContainString_notStruct() throws Exception { |
| loadCcToolchainConfigLib(); |
| createActionConfigRule( |
| "seven", |
| /* actionName= */ "'actionname'", |
| /* enabled= */ "True", |
| /* tools= */ "[tool(path = 'a/b/c')]", |
| /* flagSets= */ "[]", |
| /* implies= */ "[flag_set(actions = ['a', 'b'])]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//seven:a"); |
| StarlarkInfo actionConfigStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("at index 0 of implies, got element of type struct, want string"); |
| } |
| |
| @Test |
| public void testActionConfig() throws Exception { |
| loadCcToolchainConfigLib(); |
| createActionConfigRule( |
| "eight", |
| /* actionName= */ "'actionname32._++-'", |
| /* enabled= */ "True", |
| /* tools= */ "[tool(path = 'a/b/c')]", |
| /* flagSets= */ "[flag_set(flag_groups=[flag_group(flags=['a'])])]", |
| /* implies= */ "['a', 'b']"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//eight:a"); |
| StarlarkInfo actionConfigStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| ActionConfig a = CcModule.actionConfigFromStarlark(actionConfigStruct); |
| assertThat(a).isNotNull(); |
| assertThat(a.getActionName()).isEqualTo("actionname32._++-"); |
| assertThat(a.getImplies()).containsExactly("a", "b").inOrder(); |
| assertThat(Iterables.getOnlyElement(a.getFlagSets()).getActions()) |
| .containsExactly("actionname32._++-"); |
| } |
| |
| @Test |
| public void testActionConfig_actionName_validChars_notUpper() throws Exception { |
| loadCcToolchainConfigLib(); |
| createActionConfigRule( |
| "nine", |
| /* actionName= */ "'Upper'", |
| /* enabled= */ "True", |
| /* tools= */ "[tool(path = 'a/b/c')]", |
| /* flagSets= */ "[]", |
| /* implies= */ "[flag_set(actions = ['a', 'b'])]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//nine:a"); |
| StarlarkInfo actionConfigStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains( |
| "An action_config's name must consist solely " |
| + "of lowercase ASCII letters, digits, '.', '_', '+', and '-', got 'Upper'"); |
| } |
| |
| @Test |
| public void testActionConfig_actionName_validChars_notWhitespace() throws Exception { |
| loadCcToolchainConfigLib(); |
| createActionConfigRule( |
| "ten", |
| /* actionName= */ "'white\tspace'", |
| /* enabled= */ "True", |
| /* tools= */ "[tool(path = 'a/b/c')]", |
| /* flagSets= */ "[]", |
| /* implies= */ "[flag_set(actions = ['a', 'b'])]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//ten:a"); |
| StarlarkInfo actionConfigStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains( |
| "An action_config's name must consist solely " |
| + "of lowercase ASCII letters, digits, '.', '_', '+', and '-', " |
| + "got 'white\tspace'"); |
| } |
| |
| private void createActionConfigRule( |
| String pkg, String actionName, String enabled, String tools, String flagSets, String implies) |
| throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl', 'with_feature_set',", |
| " 'tool', 'flag_set', 'action_config', 'flag_group')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(config = action_config(", |
| " action_name = " + actionName + ",", |
| " enabled = " + enabled + ",", |
| " tools = " + tools + ",", |
| " flag_sets = " + flagSets + ",", |
| " implies = " + implies + "))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testCustomActionConfig_actionName_mustBeString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomActionConfigRule( |
| "one", |
| /* actionName= */ "struct()", |
| /* enabled= */ "True", |
| /* tools= */ "[]", |
| /* flagSets= */ "[]", |
| /* implies= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//one:a"); |
| StarlarkInfo actionConfigStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Field 'action_name' is not of 'java.lang.String' type."); |
| } |
| |
| @Test |
| public void testCustomActionConfig_enabled_mustBeBool() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomActionConfigRule( |
| "two", |
| /* actionName= */ "'actionname'", |
| /* enabled= */ "['asd']", |
| /* tools= */ "[]", |
| /* flagSets= */ "[]", |
| /* implies= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//two:a"); |
| StarlarkInfo actionConfigStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); |
| assertThat(e).hasMessageThat().contains("Field 'enabled' is not of 'java.lang.Boolean' type."); |
| } |
| |
| @Test |
| public void testCustomActionConfig_tools_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomActionConfigRule( |
| "three", |
| /* actionName= */ "'actionname'", |
| /* enabled= */ "True", |
| /* tools= */ "struct()", |
| /* flagSets= */ "[]", |
| /* implies= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| StarlarkInfo actionConfigStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); |
| assertThat(e).hasMessageThat().contains("for tools, got struct, want sequence"); |
| } |
| |
| @Test |
| public void testCustomActionConfig_flagSets_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomActionConfigRule( |
| "four", |
| /* actionName= */ "'actionname'", |
| /* enabled= */ "True", |
| /* tools= */ "[tool(path = 'a/b/c')]", |
| /* flagSets= */ "True", |
| /* implies= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo actionConfigStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); |
| assertThat(e).hasMessageThat().contains("for flag_sets, got bool, want sequence"); |
| } |
| |
| @Test |
| public void testCustomActionConfig_implies_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomActionConfigRule( |
| "five", |
| /* actionName= */ "'actionname'", |
| /* enabled= */ "True", |
| /* tools= */ "[tool(path = 'a/b/c')]", |
| /* flagSets= */ "[]", |
| /* implies= */ "flag_set(actions = ['a', 'b'])"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//five:a"); |
| StarlarkInfo actionConfigStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); |
| assertThat(e).hasMessageThat().contains("for implies, got struct, want sequence"); |
| } |
| |
| private void createCustomActionConfigRule( |
| String pkg, String actionName, String enabled, String tools, String flagSets, String implies) |
| throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl', 'with_feature_set',", |
| " 'tool', 'flag_set', 'action_config', )", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(config = struct(", |
| " action_name = " + actionName + ",", |
| " enabled = " + enabled + ",", |
| " tools = " + tools + ",", |
| " flag_sets = " + flagSets + ",", |
| " implies = " + implies + ",", |
| " type_name = 'action_config'))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testFeature_name_mustBeNonempty() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFeatureRule( |
| "one", |
| /* name= */ "''", |
| /* enabled= */ "False", |
| /* flagSets= */ "[]", |
| /* envSets= */ "[]", |
| /* requires= */ "[]", |
| /* implies= */ "[]", |
| /* provides= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//one:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| String msg = e.getMessage(); |
| assertThat(msg).contains("A feature must either have a nonempty 'name' field or be enabled."); |
| assertThat(msg) |
| .contains( |
| "in FeatureInfo instantiated at /workspace/tools/cpp/cc_toolchain_config_lib.bzl:"); |
| } |
| |
| @Test |
| public void testFeature_enabled_mustBeBool() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFeatureRule( |
| "two", |
| /* name= */ "'featurename'", |
| /* enabled= */ "None", |
| /* flagSets= */ "[]", |
| /* envSets= */ "[]", |
| /* requires= */ "[]", |
| /* implies= */ "[]", |
| /* provides= */ "[]"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//two:a")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("enabled parameter of feature should be a bool, found NoneType"); |
| } |
| |
| @Test |
| public void testFeature_flagSets_mustBeFlagSet() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFeatureRule( |
| "three", |
| /* name= */ "'featurename'", |
| /* enabled= */ "True", |
| /* flagSets= */ "[struct()]", |
| /* envSets= */ "[]", |
| /* requires= */ "[]", |
| /* implies= */ "[]", |
| /* provides= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Expected object of type 'flag_set', received 'struct'"); |
| } |
| |
| @Test |
| public void testFeature_envSets_mustBeEnvSet() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFeatureRule( |
| "four", |
| /* name= */ "'featurename'", |
| /* enabled= */ "True", |
| /* flagSets= */ "[flag_set(actions = ['a'], flag_groups = [flag_group(flags = ['a'])])]", |
| /* envSets= */ "[tool(path = 'a/b/c')]", |
| /* requires= */ "[]", |
| /* implies= */ "[]", |
| /* provides= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| assertThat(e).hasMessageThat().contains("Expected object of type 'env_set', received 'tool'"); |
| } |
| |
| @Test |
| public void testFeature_something_mustBeFeatureSet() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFeatureRule( |
| "five", |
| /* name= */ "'featurename'", |
| /* enabled= */ "True", |
| /* flagSets= */ "[flag_set(actions = ['a'], flag_groups = [flag_group(flags = ['a'])])]", |
| /* envSets= */ "[env_set(actions = ['a1'], " |
| + "env_entries = [env_entry(key = 'a', value = 'b')])]", |
| /* requires= */ "[tool(path = 'a/b/c')]", |
| /* implies= */ "[]", |
| /* provides= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//five:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| assertThat(e).hasMessageThat().contains("expected object of type 'feature_set'"); |
| } |
| |
| @Test |
| public void testFeature_implies_mustBeString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFeatureRule( |
| "six", |
| /* name= */ "'featurename'", |
| /* enabled= */ "True", |
| /* flagSets= */ "[flag_set(actions = ['a'], flag_groups = [flag_group(flags = ['a'])])]", |
| /* envSets= */ "[env_set(actions = ['a1'], " |
| + "env_entries = [env_entry(key = 'a', value = 'b')])]", |
| /* requires= */ "[feature_set(features = ['f1', 'f2'])]", |
| /* implies= */ "[tool(path = 'a/b/c')]", |
| /* provides= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//six:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("at index 0 of implies, got element of type struct, want string"); |
| } |
| |
| @Test |
| public void testFeature_provides_mustBeString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFeatureRule( |
| "seven", |
| /* name= */ "'featurename'", |
| /* enabled= */ "True", |
| /* flagSets= */ "[flag_set(actions = ['a'], flag_groups = [flag_group(flags = ['a'])])]", |
| /* envSets= */ "[env_set(actions = ['a1'], " |
| + "env_entries = [env_entry(key = 'a', value = 'b')])]", |
| /* requires= */ "[feature_set(features = ['f1', 'f2'])]", |
| /* implies= */ "['a', 'b', 'c']", |
| /* provides= */ "[struct()]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//seven:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("at index 0 of provides, got element of type struct, want string"); |
| } |
| |
| @Test |
| public void testFeature() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFeatureRule( |
| "eight", |
| /* name= */ "'featurename32+.-_'", |
| /* enabled= */ "True", |
| /* flagSets= */ "[flag_set(actions = ['a'], flag_groups = [flag_group(flags = ['a'])])]", |
| /* envSets= */ "[env_set(actions = ['a1'], " |
| + "env_entries = [env_entry(key = 'a', value = 'b')])]", |
| /* requires= */ "[feature_set(features = ['f1', 'f2'])]", |
| /* implies= */ "['a', 'b', 'c']", |
| /* provides= */ "['a', 'b', 'c']"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//eight:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| Feature a = CcModule.featureFromStarlark(featureStruct); |
| assertThat(a).isNotNull(); |
| } |
| |
| @Test |
| public void testFeature_name_validCharacters_notUpper() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFeatureRule( |
| "nine", |
| /* name= */ "'UpperCase'", |
| /* enabled= */ "False", |
| /* flagSets= */ "[]", |
| /* envSets= */ "[]", |
| /* requires= */ "[]", |
| /* implies= */ "[]", |
| /* provides= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//nine:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains( |
| "A feature's name must consist solely of lowercase ASCII letters, digits, " |
| + "'.', '_', '+', and '-', got 'UpperCase'"); |
| } |
| |
| @Test |
| public void testFeature_name_validCharacters_notWhitespace() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFeatureRule( |
| "ten", |
| /* name= */ "'white space'", |
| /* enabled= */ "False", |
| /* flagSets= */ "[]", |
| /* envSets= */ "[]", |
| /* requires= */ "[]", |
| /* implies= */ "[]", |
| /* provides= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//ten:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains( |
| "A feature's name must consist solely of " |
| + "lowercase ASCII letters, digits, '.', '_', '+', and '-', got 'white space"); |
| } |
| |
| private void createFeatureRule( |
| String pkg, |
| String name, |
| String enabled, |
| String flagSets, |
| String envSets, |
| String requires, |
| String implies, |
| String provides) |
| throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl', 'with_feature_set', 'feature_set',", |
| " 'flag_set', 'flag_group', 'tool', 'env_set', 'env_entry', 'feature')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(f = feature(", |
| " name = " + name + ",", |
| " enabled = " + enabled + ",", |
| " flag_sets = " + flagSets + ",", |
| " env_sets = " + envSets + ",", |
| " requires = " + requires + ",", |
| " implies = " + implies + ",", |
| " provides = " + provides + "))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testCustomFeature_name_mustBeString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFeatureRule( |
| "one", |
| /* name= */ "struct()", |
| /* enabled= */ "False", |
| /* flagSets= */ "[]", |
| /* envSets= */ "[]", |
| /* requires= */ "[]", |
| /* implies= */ "[]", |
| /* provides= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//one:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| assertThat(e).hasMessageThat().contains("Field 'name' is not of 'java.lang.String' type."); |
| } |
| |
| @Test |
| public void testCustomFeature_enabled_mustBeBool() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFeatureRule( |
| "two", |
| /* name= */ "'featurename'", |
| /* enabled= */ "struct()", |
| /* flagSets= */ "[]", |
| /* envSets= */ "[]", |
| /* requires= */ "[]", |
| /* implies= */ "[]", |
| /* provides= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//two:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| assertThat(e).hasMessageThat().contains("Field 'enabled' is not of 'java.lang.Boolean' type."); |
| } |
| |
| @Test |
| public void testCustomFeature_flagSets_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFeatureRule( |
| "three", |
| /* name= */ "'featurename'", |
| /* enabled= */ "True", |
| /* flagSets= */ "struct()", |
| /* envSets= */ "[]", |
| /* requires= */ "[]", |
| /* implies= */ "[]", |
| /* provides= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| assertThat(e).hasMessageThat().contains("for flag_sets, got struct, want sequence"); |
| } |
| |
| @Test |
| public void testCustomFeature_envSets_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFeatureRule( |
| "four", |
| /* name= */ "'featurename'", |
| /* enabled= */ "True", |
| /* flagSets= */ "[]", |
| /* envSets= */ "struct()", |
| /* requires= */ "[]", |
| /* implies= */ "[]", |
| /* provides= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| assertThat(e).hasMessageThat().contains("for env_sets, got struct, want sequence"); |
| } |
| |
| @Test |
| public void testCustomFeature_requires_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFeatureRule( |
| "five", |
| /* name= */ "'featurename'", |
| /* enabled= */ "True", |
| /* flagSets= */ "[]", |
| /* envSets= */ "[]", |
| /* requires= */ "struct()", |
| /* implies= */ "[]", |
| /* provides= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//five:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| assertThat(e).hasMessageThat().contains("for requires, got struct, want sequence"); |
| } |
| |
| @Test |
| public void testCustomFeature_implies_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFeatureRule( |
| "six", |
| /* name= */ "'featurename'", |
| /* enabled= */ "True", |
| /* flagSets= */ "[]", |
| /* envSets= */ "[]", |
| /* requires= */ "[]", |
| /* implies= */ "struct()", |
| /* provides= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//six:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| assertThat(e).hasMessageThat().contains("for implies, got struct, want sequence"); |
| } |
| |
| @Test |
| public void testCustomFeature_provides_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFeatureRule( |
| "seven", |
| /* name= */ "'featurename'", |
| /* enabled= */ "True", |
| /* flagSets= */ "[]", |
| /* envSets= */ "[]", |
| /* requires= */ "[]", |
| /* implies= */ "[]", |
| /* provides= */ "struct()"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//seven:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| assertThat(e).hasMessageThat().contains("for provides, got struct, want sequence"); |
| } |
| |
| @Test |
| public void testCustomFeature_flagSet_musthaveActions() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFeatureRule( |
| "eight", |
| /* name= */ "'featurename'", |
| /* enabled= */ "True", |
| /* flagSets= */ "[flag_set()]", |
| /* envSets= */ "[]", |
| /* requires= */ "[]", |
| /* implies= */ "[]", |
| /* provides= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//eight:a"); |
| StarlarkInfo featureStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromStarlark(featureStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("A flag_set that belongs to a feature must have nonempty 'actions' parameter."); |
| } |
| |
| private void createCustomFeatureRule( |
| String pkg, |
| String name, |
| String enabled, |
| String flagSets, |
| String envSets, |
| String requires, |
| String implies, |
| String provides) |
| throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl', 'with_feature_set', 'feature_set',", |
| " 'flag_set', 'flag_group', 'tool', 'env_set', 'env_entry', 'feature')", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(f = struct(", |
| " name = " + name + ",", |
| " enabled = " + enabled + ",", |
| " flag_sets = " + flagSets + ",", |
| " env_sets = " + envSets + ",", |
| " requires = " + requires + ",", |
| " implies = " + implies + ",", |
| " provides = " + provides + ",", |
| " type_name = 'feature'))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testCustomArtifactNamePattern_categoryName_mustBeString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomArtifactNamePatternRule( |
| "one", /* categoryName= */ "struct()", /* extension= */ "'a'", /* prefix= */ "'a'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//one:a"); |
| StarlarkInfo artifactNamePatternStruct = |
| (StarlarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> |
| CcModule.artifactNamePatternFromStarlark( |
| artifactNamePatternStruct, (c, p, ext) -> {})); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Field 'category_name' is not of 'java.lang.String' type."); |
| } |
| |
| @Test |
| public void testCustomArtifactNamePattern_extension_mustBeString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomArtifactNamePatternRule( |
| "two", |
| /* categoryName= */ "'static_library'", |
| /* extension= */ "struct()", |
| /* prefix= */ "'a'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//two:a"); |
| StarlarkInfo artifactNamePatternStruct = |
| (StarlarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> |
| CcModule.artifactNamePatternFromStarlark( |
| artifactNamePatternStruct, (c, p, ext) -> {})); |
| assertThat(e).hasMessageThat().contains("Field 'extension' is not of 'java.lang.String' type."); |
| } |
| |
| @Test |
| public void testCustomArtifactNamePattern_prefix_mustBeString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomArtifactNamePatternRule( |
| "three", |
| /* categoryName= */ "'static_library'", |
| /* extension= */ "'.a'", |
| /* prefix= */ "struct()"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| StarlarkInfo artifactNamePatternStruct = |
| (StarlarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> |
| CcModule.artifactNamePatternFromStarlark( |
| artifactNamePatternStruct, (c, p, ext) -> {})); |
| assertThat(e).hasMessageThat().contains("Field 'prefix' is not of 'java.lang.String' type."); |
| } |
| |
| @Test |
| public void testCustomArtifactNamePattern_categoryName_mustBeNonempty() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomArtifactNamePatternRule( |
| "four", /* categoryName= */ "''", /* extension= */ "'.a'", /* prefix= */ "'a'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| StarlarkInfo artifactNamePatternStruct = |
| (StarlarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> |
| CcModule.artifactNamePatternFromStarlark( |
| artifactNamePatternStruct, (c, p, ext) -> {})); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("The 'category_name' field of artifact_name_pattern must be a nonempty string."); |
| } |
| |
| @Test |
| public void testCustomArtifactNamePattern_emptyString_emptyString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomArtifactNamePatternRule( |
| "five", /* categoryName= */ "'executable'", /* extension= */ "''", /* prefix= */ "''"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//five:a"); |
| StarlarkInfo artifactNamePatternStruct = |
| (StarlarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| AtomicBoolean called = new AtomicBoolean(false); |
| CcModule.artifactNamePatternFromStarlark( |
| artifactNamePatternStruct, (c, p, ext) -> called.set(true)); |
| assertThat(called.get()).isTrue(); |
| } |
| |
| @Test |
| public void testCustomArtifactNamePattern_none_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomArtifactNamePatternRule( |
| "six", /* categoryName= */ "'executable'", /* extension= */ "None", /* prefix= */ "None"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//six:a"); |
| StarlarkInfo artifactNamePatternStruct = |
| (StarlarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| AtomicBoolean called = new AtomicBoolean(false); |
| CcModule.artifactNamePatternFromStarlark( |
| artifactNamePatternStruct, (c, p, ext) -> called.set(true)); |
| assertThat(called.get()).isTrue(); |
| } |
| |
| @Test |
| public void testCustomArtifactNamePattern_categoryName_unknown() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomArtifactNamePatternRule( |
| "seven", /* categoryName= */ "'unknown'", /* extension= */ "'.a'", /* prefix= */ "'a'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//seven:a"); |
| StarlarkInfo artifactNamePatternStruct = |
| (StarlarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> |
| CcModule.artifactNamePatternFromStarlark( |
| artifactNamePatternStruct, (c, p, ext) -> {})); |
| assertThat(e).hasMessageThat().contains("Artifact category unknown not recognized"); |
| } |
| |
| @Test |
| public void testCustomArtifactNamePattern_fileExtension_unknown() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomArtifactNamePatternRule( |
| "eight", |
| /* categoryName= */ "'static_library'", |
| /* extension= */ "'a'", |
| /* prefix= */ "'a'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//eight:a"); |
| StarlarkInfo artifactNamePatternStruct = |
| (StarlarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> |
| CcModule.artifactNamePatternFromStarlark( |
| artifactNamePatternStruct, (c, p, ext) -> {})); |
| assertThat(e).hasMessageThat().contains("Unrecognized file extension 'a'"); |
| } |
| |
| private void createCustomArtifactNamePatternRule( |
| String pkg, String categoryName, String extension, String prefix) throws Exception { |
| scratch.file( |
| pkg + "/foo.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " return [MyInfo(namepattern = struct(", |
| " category_name = " + categoryName + ",", |
| " extension = " + extension + ",", |
| " prefix = " + prefix + ",", |
| " type_name = 'artifact_name_pattern'))]", |
| "crule = rule(implementation = _impl)"); |
| scratch.file(pkg + "/BUILD", "load(':foo.bzl', 'crule')", "crule(name = 'a')"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromStarlark() throws Exception { |
| loadCcToolchainConfigLib(); |
| scratch.file( |
| "foo/crosstool.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl',", |
| " 'feature',", |
| " 'action_config',", |
| " 'artifact_name_pattern',", |
| " 'env_entry',", |
| " 'variable_with_value',", |
| " 'make_variable',", |
| " 'feature_set',", |
| " 'with_feature_set',", |
| " 'env_set',", |
| " 'flag_group',", |
| " 'flag_set',", |
| " 'tool_path',", |
| " 'tool')", |
| "", |
| "def _impl(ctx):", |
| " return cc_common.create_cc_toolchain_config_info(", |
| " ctx = ctx,", |
| " features = [feature(name = 'featureone')],", |
| " action_configs = [action_config(action_name = 'action', enabled=True)],", |
| " artifact_name_patterns = [artifact_name_pattern(", |
| " category_name = 'static_library',", |
| " prefix = 'prefix',", |
| " extension = '.a')],", |
| " cxx_builtin_include_directories = ['dir1', 'dir2', 'dir3'],", |
| " toolchain_identifier = 'toolchain',", |
| " host_system_name = 'host',", |
| " target_system_name = 'target',", |
| " target_cpu = 'cpu',", |
| " target_libc = 'libc',", |
| " compiler = 'compiler',", |
| " abi_libc_version = 'abi_libc',", |
| " abi_version = 'abi',", |
| " tool_paths = [tool_path(name = 'name1', path = 'path1')],", |
| " cc_target_os = 'os',", |
| " builtin_sysroot = 'sysroot',", |
| " make_variables = [make_variable(name = 'acs', value = 'asd')])", |
| "cc_toolchain_config_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {},", |
| " provides = [CcToolchainConfigInfo], ", |
| ")"); |
| |
| scratch.file( |
| "foo/BUILD", |
| "load(':crosstool.bzl', 'cc_toolchain_config_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "cc_toolchain_config_rule(name='r')"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:r"); |
| assertThat(target).isNotNull(); |
| CcToolchainConfigInfo ccToolchainConfigInfo = |
| (CcToolchainConfigInfo) target.get(CcToolchainConfigInfo.PROVIDER.getKey()); |
| assertThat(ccToolchainConfigInfo).isNotNull(); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromStarlarkRequiredToolchainIdentifier() throws Exception { |
| setupStarlarkRuleForStringFieldsTesting("toolchain_identifier"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:r")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("missing 1 required keyword-only argument: toolchain_identifier"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromStarlarkRequiredTargetSystemName() throws Exception { |
| setupStarlarkRuleForStringFieldsTesting("target_system_name"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:r")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("missing 1 required keyword-only argument: target_system_name"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromStarlarkRequiredTargetCpu() throws Exception { |
| setupStarlarkRuleForStringFieldsTesting("target_cpu"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:r")); |
| assertThat(e).hasMessageThat().contains("missing 1 required keyword-only argument: target_cpu"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromStarlarkRequiredTargetLibc() throws Exception { |
| setupStarlarkRuleForStringFieldsTesting("target_libc"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:r")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("missing 1 required keyword-only argument: target_libc"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromStarlarkRequiredCompiler() throws Exception { |
| setupStarlarkRuleForStringFieldsTesting("compiler"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:r")); |
| assertThat(e).hasMessageThat().contains("missing 1 required keyword-only argument: compiler"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromStarlarkAllRequiredStringsPresent() throws Exception { |
| setupStarlarkRuleForStringFieldsTesting(""); |
| ConfiguredTarget target = getConfiguredTarget("//foo:r"); |
| assertThat(target).isNotNull(); |
| CcToolchainConfigInfo ccToolchainConfigInfo = |
| (CcToolchainConfigInfo) target.get(CcToolchainConfigInfo.PROVIDER.getKey()); |
| assertThat(ccToolchainConfigInfo).isNotNull(); |
| } |
| |
| private void setupStarlarkRuleForStringFieldsTesting(String fieldToExclude) throws Exception { |
| ImmutableList<String> fields = |
| ImmutableList.of( |
| "toolchain_identifier = 'identifier'", |
| "host_system_name = 'host_system_name'", |
| "target_system_name = 'target_system_name'", |
| "target_cpu = 'target_cpu'", |
| "target_libc = 'target_libc'", |
| "compiler = 'compiler'", |
| "abi_version = 'abi'", |
| "abi_libc_version = 'abi_libc'"); |
| |
| scratch.file( |
| "foo/crosstool.bzl", |
| "def _impl(ctx):", |
| " return cc_common.create_cc_toolchain_config_info(", |
| " ctx = ctx,", |
| Joiner.on(",\n") |
| .join(fields.stream().filter(el -> !el.startsWith(fieldToExclude + " =")).toArray()), |
| ")", |
| "cc_toolchain_config_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {},", |
| " provides = [CcToolchainConfigInfo], ", |
| ")"); |
| scratch.file( |
| "foo/BUILD", |
| "load(':crosstool.bzl', 'cc_toolchain_config_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "cc_toolchain_config_rule(name='r')"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromStarlarkNoLegacyFeatures() throws Exception { |
| loadCcToolchainConfigLib(); |
| scratch.file( |
| "foo/crosstool.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl',", |
| " 'feature',", |
| " 'action_config',", |
| " 'artifact_name_pattern',", |
| " 'env_entry',", |
| " 'variable_with_value',", |
| " 'make_variable',", |
| " 'feature_set',", |
| " 'with_feature_set',", |
| " 'env_set',", |
| " 'flag_group',", |
| " 'flag_set',", |
| " 'tool_path',", |
| " 'tool')", |
| "", |
| "def _impl(ctx):", |
| " return cc_common.create_cc_toolchain_config_info(", |
| " ctx = ctx,", |
| " features = [", |
| " feature(name = 'no_legacy_features'),", |
| " feature(name = 'custom_feature'),", |
| " ],", |
| " action_configs = [action_config(action_name = 'custom_action')],", |
| " artifact_name_patterns = [artifact_name_pattern(", |
| " category_name = 'static_library',", |
| " prefix = 'prefix',", |
| " extension = '.a')],", |
| " toolchain_identifier = 'toolchain',", |
| " host_system_name = 'host',", |
| " target_system_name = 'target',", |
| " target_cpu = 'cpu',", |
| " target_libc = 'libc',", |
| " compiler = 'compiler',", |
| " abi_libc_version = 'abi_libc',", |
| " abi_version = 'abi')", |
| "cc_toolchain_config_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {},", |
| " provides = [CcToolchainConfigInfo], ", |
| ")"); |
| |
| scratch.file( |
| "foo/BUILD", |
| "load(':crosstool.bzl', 'cc_toolchain_config_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "cc_toolchain_config_rule(name='r')"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:r"); |
| assertThat(target).isNotNull(); |
| CcToolchainConfigInfo ccToolchainConfigInfo = |
| (CcToolchainConfigInfo) target.get(CcToolchainConfigInfo.PROVIDER.getKey()); |
| ImmutableSet<String> featureNames = |
| ccToolchainConfigInfo.getFeatures().stream() |
| .map(Feature::getName) |
| .collect(ImmutableSet.toImmutableSet()); |
| ImmutableSet<String> actionConfigNames = |
| ccToolchainConfigInfo.getActionConfigs().stream() |
| .map(ActionConfig::getActionName) |
| .collect(ImmutableSet.toImmutableSet()); |
| assertThat(featureNames).containsExactly("no_legacy_features", "custom_feature"); |
| assertThat(actionConfigNames).containsExactly("custom_action"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromStarlarkWithLegacyFeatures() throws Exception { |
| loadCcToolchainConfigLib(); |
| scratch.file( |
| "foo/crosstool.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl',", |
| " 'feature',", |
| " 'action_config',", |
| " 'artifact_name_pattern',", |
| " 'env_entry',", |
| " 'variable_with_value',", |
| " 'make_variable',", |
| " 'feature_set',", |
| " 'with_feature_set',", |
| " 'env_set',", |
| " 'flag_group',", |
| " 'flag_set',", |
| " 'tool_path',", |
| " 'tool')", |
| "", |
| "def _impl(ctx):", |
| " return cc_common.create_cc_toolchain_config_info(", |
| " ctx = ctx,", |
| " features = [", |
| " feature(name = 'custom_feature'),", |
| " feature(name = 'legacy_compile_flags'),", |
| " feature(name = 'fdo_optimize'),", |
| " feature(name = 'default_compile_flags'),", |
| " ],", |
| " action_configs = [action_config(action_name = 'custom-action')],", |
| " artifact_name_patterns = [artifact_name_pattern(", |
| " category_name = 'static_library',", |
| " prefix = 'prefix',", |
| " extension = '.a')],", |
| " toolchain_identifier = 'toolchain',", |
| " host_system_name = 'host',", |
| " target_system_name = 'target',", |
| " target_cpu = 'cpu',", |
| " target_libc = 'libc',", |
| " compiler = 'compiler',", |
| " abi_libc_version = 'abi_libc',", |
| " abi_version = 'abi')", |
| "cc_toolchain_config_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {},", |
| " provides = [CcToolchainConfigInfo], ", |
| ")"); |
| |
| scratch.file( |
| "foo/BUILD", |
| "load(':crosstool.bzl', 'cc_toolchain_config_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "cc_toolchain_config_rule(name='r')"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:r"); |
| assertThat(target).isNotNull(); |
| CcToolchainConfigInfo ccToolchainConfigInfo = |
| (CcToolchainConfigInfo) target.get(CcToolchainConfigInfo.PROVIDER.getKey()); |
| ImmutableList<String> featureNames = |
| ccToolchainConfigInfo.getFeatures().stream() |
| .map(Feature::getName) |
| .collect(ImmutableList.toImmutableList()); |
| ImmutableSet<String> actionConfigNames = |
| ccToolchainConfigInfo.getActionConfigs().stream() |
| .map(ActionConfig::getActionName) |
| .collect(ImmutableSet.toImmutableSet()); |
| // fdo_optimize should not be re-added to the list of features by legacy behavior |
| assertThat(featureNames).containsNoDuplicates(); |
| // legacy_compile_flags should appear first in the list of features, followed by |
| // default_compile_flags. |
| assertThat(featureNames) |
| .containsAtLeast( |
| "legacy_compile_flags", "default_compile_flags", "custom_feature", "fdo_optimize") |
| .inOrder(); |
| // assemble is one of the action_configs added as a legacy behavior, therefore it needs to be |
| // prepended to the action configs defined by the user. |
| assertThat(actionConfigNames).containsAtLeast("assemble", "custom-action").inOrder(); |
| } |
| |
| @Test |
| public void testCcToolchainInfoToProto() throws Exception { |
| loadCcToolchainConfigLib(); |
| scratch.file( |
| "foo/crosstool.bzl", |
| "load('//tools/cpp:cc_toolchain_config_lib.bzl',", |
| " 'feature',", |
| " 'action_config',", |
| " 'artifact_name_pattern',", |
| " 'env_entry',", |
| " 'variable_with_value',", |
| " 'make_variable',", |
| " 'feature_set',", |
| " 'with_feature_set',", |
| " 'env_set',", |
| " 'flag_group',", |
| " 'flag_set',", |
| " 'tool_path',", |
| " 'tool')", |
| "", |
| "def _impl(ctx):", |
| " return cc_common.create_cc_toolchain_config_info(", |
| " ctx = ctx,", |
| " features = [", |
| " feature(name = 'featureone', enabled = True),", |
| " feature(name = 'sysroot',", |
| " flag_sets = [", |
| " flag_set(", |
| " actions = ['assemble', 'preprocess-assemble'],", |
| " flag_groups = [", |
| " flag_group(", |
| " expand_if_available= 'available',", |
| " expand_if_not_available= 'notavailable',", |
| " expand_if_true = 'true',", |
| " expand_if_false = 'false',", |
| " expand_if_equal = variable_with_value(", |
| " name = 'variable', value = 'value'),", |
| " iterate_over = 'iterate_over',", |
| " flag_groups = [", |
| " flag_group(flags = ", |
| " ['foo%{libraries_to_link}bar',", |
| " '-D__DATE__=\"redacted\"'])],", |
| " ),", |
| " flag_group(flags = ['a', 'b'])],", |
| " )],", |
| " implies = ['imply2', 'imply1'],", |
| " provides = ['provides2', 'provides1'],", |
| " requires = [feature_set(features = ['r1', 'r2']),", |
| " feature_set(features = ['r3'])],", |
| " env_sets = [", |
| " env_set(actions = ['a1', 'a2'],", |
| " env_entries = [", |
| " env_entry(key = 'k1', value = 'v1'),", |
| " env_entry(key = 'k2', value = 'v2')],", |
| " with_features = [", |
| " with_feature_set(", |
| " features = ['f1', 'f2'],", |
| " not_features = ['nf1', 'nf2'])]),", |
| " env_set(actions = ['a1', 'a2'])]", |
| " ),", |
| " ],", |
| " action_configs = [", |
| " action_config(action_name = 'action_one', enabled=True),", |
| " action_config(", |
| " action_name = 'action_two',", |
| " tools = [tool(path = 'fake/path',", |
| " with_features = [", |
| " with_feature_set(features = ['a'],", |
| " not_features=['b', 'c']),", |
| " with_feature_set(features=['b'])],", |
| " execution_requirements = ['a', 'b', 'c'])],", |
| " implies = ['compiler_input_flags', 'compiler_output_flags'],", |
| " flag_sets = [flag_set(flag_groups = [flag_group(flags = ['a'])]", |
| " )])],", |
| " artifact_name_patterns = [artifact_name_pattern(", |
| " category_name = 'static_library',", |
| " prefix = 'prefix',", |
| " extension = '.a')],", |
| " cxx_builtin_include_directories = ['dir1', 'dir2', 'dir3'],", |
| " toolchain_identifier = 'toolchain',", |
| " host_system_name = 'host',", |
| " target_system_name = 'target',", |
| " target_cpu = 'cpu',", |
| " target_libc = 'libc',", |
| " compiler = 'compiler',", |
| " abi_libc_version = 'abi_libc',", |
| " abi_version = 'abi',", |
| " tool_paths = [tool_path(name = 'name1', path = 'path1')],", |
| " make_variables = [make_variable(name = 'variable', value = '--a -b -c')],", |
| " builtin_sysroot = 'sysroot',", |
| " cc_target_os = 'os',", |
| " )", |
| "cc_toolchain_config_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {},", |
| " provides = [CcToolchainConfigInfo], ", |
| ")"); |
| |
| scratch.file( |
| "foo/BUILD", |
| "load(':crosstool.bzl', 'cc_toolchain_config_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "cc_toolchain_config_rule(name='r')"); |
| |
| ConfiguredTarget target = getConfiguredTarget("//foo:r"); |
| assertThat(target).isNotNull(); |
| CcToolchainConfigInfo ccToolchainConfigInfo = |
| (CcToolchainConfigInfo) target.get(CcToolchainConfigInfo.PROVIDER.getKey()); |
| assertThat(ccToolchainConfigInfo).isNotNull(); |
| CToolchain.Builder toolchainBuilder = CToolchain.newBuilder(); |
| TextFormat.merge(ccToolchainConfigInfo.getProto(), toolchainBuilder); |
| CToolchain toolchain = toolchainBuilder.build(); |
| |
| assertThat(toolchain.getCxxBuiltinIncludeDirectoryList()) |
| .containsExactly("dir1", "dir2", "dir3"); |
| assertThat(toolchain.getToolchainIdentifier()).isEqualTo("toolchain"); |
| assertThat(toolchain.getHostSystemName()).isEqualTo("host"); |
| assertThat(toolchain.getTargetSystemName()).isEqualTo("target"); |
| assertThat(toolchain.getTargetCpu()).isEqualTo("cpu"); |
| assertThat(toolchain.getTargetLibc()).isEqualTo("libc"); |
| assertThat(toolchain.getCompiler()).isEqualTo("compiler"); |
| assertThat(toolchain.getAbiLibcVersion()).isEqualTo("abi_libc"); |
| assertThat(toolchain.getAbiVersion()).isEqualTo("abi"); |
| ToolPath toolPath = Iterables.getOnlyElement(toolchain.getToolPathList()); |
| assertThat(toolPath.getName()).isEqualTo("name1"); |
| assertThat(toolPath.getPath()).isEqualTo("path1"); |
| MakeVariable makeVariable = Iterables.getOnlyElement(toolchain.getMakeVariableList()); |
| assertThat(makeVariable.getName()).isEqualTo("variable"); |
| assertThat(makeVariable.getValue()).isEqualTo("--a -b -c"); |
| assertThat(toolchain.getBuiltinSysroot()).isEqualTo("sysroot"); |
| assertThat(toolchain.getCcTargetOs()).isEqualTo("os"); |
| assertThat( |
| toolchain.getFeatureList().stream() |
| .map(CToolchain.Feature::getName) |
| .collect(ImmutableList.toImmutableList())) |
| .containsAtLeast("featureone", "sysroot") |
| .inOrder(); |
| // check for legacy features |
| assertThat(toolchain.getFeatureCount()).isGreaterThan(2); |
| CToolchain.Feature feature = |
| toolchain.getFeatureList().stream() |
| .filter(f -> f.getName().equals("sysroot")) |
| .findFirst() |
| .get(); |
| assertThat(feature.getName()).isEqualTo("sysroot"); |
| assertThat(feature.getImpliesList()).containsExactly("imply2", "imply1").inOrder(); |
| assertThat(feature.getProvidesList()).containsExactly("provides2", "provides1").inOrder(); |
| assertThat(feature.getRequires(0).getFeatureList()).containsExactly("r1", "r2"); |
| assertThat(feature.getRequires(1).getFeatureList()).containsExactly("r3"); |
| assertThat(feature.getEnvSetCount()).isEqualTo(2); |
| CToolchain.EnvSet envSet = feature.getEnvSet(0); |
| assertThat(envSet.getActionList()).containsExactly("a1", "a2"); |
| assertThat(envSet.getEnvEntry(0).getKey()).isEqualTo("k1"); |
| assertThat(envSet.getEnvEntry(0).getValue()).isEqualTo("v1"); |
| assertThat(Iterables.getOnlyElement(envSet.getWithFeatureList()).getFeatureList()) |
| .containsExactly("f1", "f2"); |
| assertThat(Iterables.getOnlyElement(envSet.getWithFeatureList()).getNotFeatureList()) |
| .containsExactly("nf1", "nf2"); |
| CToolchain.FlagSet flagSet = Iterables.getOnlyElement(feature.getFlagSetList()); |
| assertThat(flagSet.getActionList()).containsExactly("assemble", "preprocess-assemble"); |
| assertThat(flagSet.getFlagGroupCount()).isEqualTo(2); |
| CToolchain.FlagGroup flagGroup = flagSet.getFlagGroup(0); |
| assertThat(flagGroup.getExpandIfAllAvailableList()).containsExactly("available"); |
| assertThat(flagGroup.getExpandIfNoneAvailableList()).containsExactly("notavailable"); |
| assertThat(flagGroup.getIterateOver()).isEqualTo("iterate_over"); |
| assertThat(flagGroup.getExpandIfTrue()).isEqualTo("true"); |
| assertThat(flagGroup.getExpandIfFalse()).isEqualTo("false"); |
| CToolchain.VariableWithValue variableWithValue = flagGroup.getExpandIfEqual(); |
| assertThat(variableWithValue.getVariable()).isEqualTo("variable"); |
| assertThat(variableWithValue.getValue()).isEqualTo("value"); |
| assertThat(flagGroup.getFlagGroup(0).getFlagList()) |
| .containsExactly("foo%{libraries_to_link}bar", "-D__DATE__=\"redacted\"") |
| .inOrder(); |
| |
| assertThat( |
| toolchain.getActionConfigList().stream() |
| .map(CToolchain.ActionConfig::getActionName) |
| .collect(ImmutableList.toImmutableList())) |
| .containsAtLeast("action_one", "action_two") |
| .inOrder(); |
| |
| // legacy crosstool patching adds the missing action_configs as well |
| assertThat(toolchain.getActionConfigCount()).isGreaterThan(2); |
| |
| CToolchain.ActionConfig actionConfig = |
| toolchain.getActionConfigList().stream() |
| .filter(config -> config.getConfigName().equals("action_two")) |
| .findFirst() |
| .get(); |
| assertThat(actionConfig.getImpliesList()) |
| .containsExactly("compiler_input_flags", "compiler_output_flags") |
| .inOrder(); |
| CToolchain.Tool tool = Iterables.getOnlyElement(actionConfig.getToolList()); |
| assertThat(tool.getToolPath()).isEqualTo("fake/path"); |
| assertThat(tool.getExecutionRequirementList()).containsExactly("a", "b", "c"); |
| assertThat(tool.getWithFeature(0).getFeatureList()).containsExactly("a"); |
| assertThat(tool.getWithFeature(0).getNotFeatureList()).containsExactly("b", "c"); |
| // When we create an ActionConfig from a proto we put the action name as the only element of its |
| // FlagSet.action lists. So when we convert back to proto, we need to remove it. |
| assertThat( |
| ccToolchainConfigInfo.getActionConfigs().stream() |
| .filter(config -> config.getName().equals("action_two")) |
| .findFirst() |
| .get() |
| .getFlagSets() |
| .get(0) |
| .getActions()) |
| .containsExactly("action_two"); |
| CToolchain.FlagSet actionConfigFlagSet = |
| Iterables.getOnlyElement(actionConfig.getFlagSetList()); |
| assertThat(actionConfigFlagSet.getActionCount()).isEqualTo(0); |
| |
| // Missing artifact name patterns are filled in too |
| assertThat(toolchain.getArtifactNamePatternCount()).isGreaterThan(1); |
| assertThat(toolchain.getArtifactNamePattern(0)) |
| .isEqualTo( |
| CToolchain.ArtifactNamePattern.newBuilder() |
| .setCategoryName("static_library") |
| .setPrefix("prefix") |
| .setExtension(".a") |
| .build()); |
| } |
| |
| // Tests that default None values in non-required fields do not cause trouble while building |
| // the CToolchain |
| @Test |
| public void testCcToolchainInfoToProtoBareMinimum() throws Exception { |
| loadCcToolchainConfigLib(); |
| scratch.file( |
| "foo/crosstool.bzl", |
| "def _impl(ctx):", |
| " return cc_common.create_cc_toolchain_config_info(", |
| " ctx = ctx,", |
| " toolchain_identifier = 'toolchain',", |
| " host_system_name = 'host',", |
| " target_system_name = 'target',", |
| " target_cpu = 'cpu',", |
| " target_libc = 'libc',", |
| " compiler = 'compiler',", |
| " )", |
| "cc_toolchain_config_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {},", |
| " provides = [CcToolchainConfigInfo], ", |
| ")"); |
| |
| scratch.file( |
| "foo/BUILD", |
| "load(':crosstool.bzl', 'cc_toolchain_config_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "cc_toolchain_config_rule(name='r')"); |
| |
| ConfiguredTarget target = getConfiguredTarget("//foo:r"); |
| assertThat(target).isNotNull(); |
| CcToolchainConfigInfo ccToolchainConfigInfo = |
| (CcToolchainConfigInfo) target.get(CcToolchainConfigInfo.PROVIDER.getKey()); |
| assertThat(ccToolchainConfigInfo).isNotNull(); |
| CToolchain.Builder toolchainBuilder = CToolchain.newBuilder(); |
| TextFormat.merge(ccToolchainConfigInfo.getProto(), toolchainBuilder); |
| assertNoEvents(); |
| } |
| |
| @Test |
| public void testWrongElementTypeInListParameter_features() throws Exception { |
| getBasicCcToolchainConfigInfoWithAdditionalParameter( |
| "features = ['string_instead_of_feature']"); |
| reporter.removeHandler(failFastHandler); |
| getConfiguredTarget("//foo:r"); |
| assertContainsEvent( |
| "'features' parameter of cc_common.create_cc_toolchain_config_info() contains an element" |
| + " of type 'string' instead of a 'FeatureInfo' provider."); |
| } |
| |
| @Test |
| public void testWrongElementTypeInListParameter_actionConfigs() throws Exception { |
| getBasicCcToolchainConfigInfoWithAdditionalParameter("action_configs = [None]"); |
| reporter.removeHandler(failFastHandler); |
| getConfiguredTarget("//foo:r"); |
| assertContainsEvent( |
| "'action_configs' parameter of cc_common.create_cc_toolchain_config_info() contains an" |
| + " element of type 'NoneType' instead of a 'ActionConfigInfo' provider."); |
| } |
| |
| @Test |
| public void testWrongElementTypeInListParameter_artifactNamePatterns() throws Exception { |
| getBasicCcToolchainConfigInfoWithAdditionalParameter("artifact_name_patterns = [1]"); |
| reporter.removeHandler(failFastHandler); |
| getConfiguredTarget("//foo:r"); |
| assertContainsEvent( |
| "'artifact_name_patterns' parameter of cc_common.create_cc_toolchain_config_info()" |
| + " contains an element of type 'int' instead of a 'ArtifactNamePatternInfo'" |
| + " provider."); |
| } |
| |
| @Test |
| public void testWrongElementTypeInListParameter_makeVariables() throws Exception { |
| getBasicCcToolchainConfigInfoWithAdditionalParameter("make_variables = [True]"); |
| reporter.removeHandler(failFastHandler); |
| getConfiguredTarget("//foo:r"); |
| assertContainsEvent( |
| "'make_variables' parameter of cc_common.create_cc_toolchain_config_info() contains an" |
| + " element of type 'bool' instead of a 'MakeVariableInfo' provider."); |
| } |
| |
| @Test |
| public void testWrongElementTypeInListParameter_toolPaths() throws Exception { |
| getBasicCcToolchainConfigInfoWithAdditionalParameter("tool_paths = [{}]"); |
| reporter.removeHandler(failFastHandler); |
| getConfiguredTarget("//foo:r"); |
| assertContainsEvent( |
| "'tool_paths' parameter of cc_common.create_cc_toolchain_config_info() contains an element" |
| + " of type 'dict' instead of a 'ToolPathInfo' provider."); |
| } |
| |
| private void getBasicCcToolchainConfigInfoWithAdditionalParameter(String s) throws Exception { |
| scratch.file( |
| "foo/crosstool.bzl", |
| "def _impl(ctx):", |
| " return cc_common.create_cc_toolchain_config_info(", |
| " ctx = ctx,", |
| " toolchain_identifier = 'toolchain',", |
| " host_system_name = 'host',", |
| " target_system_name = 'target',", |
| " target_cpu = 'cpu',", |
| " target_libc = 'libc',", |
| " compiler = 'compiler',", |
| " abi_libc_version = 'abi_libc',", |
| " abi_version = 'abi',", |
| " " + s + ",", |
| " )", |
| "cc_toolchain_config_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {},", |
| " provides = [CcToolchainConfigInfo], ", |
| ")"); |
| |
| scratch.file( |
| "foo/BUILD", |
| "load(':crosstool.bzl', 'cc_toolchain_config_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "cc_toolchain_config_rule(name='r')"); |
| } |
| |
| @Test |
| public void testGetLegacyCcFlagsMakeVariable() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder() |
| .withMakeVariables(Pair.of("CC_FLAGS", "-test-cflag1 -testcflag2"))); |
| |
| loadCcToolchainConfigLib(); |
| scratch.file( |
| "a/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " cc_flags = cc_common.legacy_cc_flags_make_variable_do_not_use(", |
| " cc_toolchain = toolchain)", |
| " return [MyInfo(", |
| " cc_flags = cc_flags)]", |
| "cc_flags = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'cc_flags')", |
| "cc_toolchain_alias(name='alias')", |
| "cc_flags(name='r')"); |
| |
| String ccFlags = |
| (String) getMyInfoFromTarget(getConfiguredTarget("//a:r")).getValue("cc_flags"); |
| |
| assertThat(ccFlags).isEqualTo("-test-cflag1 -testcflag2"); |
| } |
| |
| private boolean toolchainResolutionEnabled() throws Exception { |
| scratch.file( |
| "a/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " toolchain_resolution_enabled = cc_common.is_cc_toolchain_resolution_enabled_do_not_use(", |
| " ctx = ctx)", |
| " return [MyInfo(", |
| " toolchain_resolution_enabled = toolchain_resolution_enabled)]", |
| "toolchain_resolution_enabled = rule(", |
| " _impl,", |
| ");"); |
| |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'toolchain_resolution_enabled')", |
| "toolchain_resolution_enabled(name='r')"); |
| |
| ConfiguredTarget r = getConfiguredTarget("//a:r"); |
| // Use an extra variable in order to suppress the warning. |
| boolean toolchainResolutionEnabled = |
| (boolean) getMyInfoFromTarget(r).getValue("toolchain_resolution_enabled"); |
| return toolchainResolutionEnabled; |
| } |
| |
| @Test |
| public void testIsToolchainResolutionEnabled_disabled() throws Exception { |
| useConfiguration("--incompatible_enable_cc_toolchain_resolution=false"); |
| |
| assertThat(toolchainResolutionEnabled()).isFalse(); |
| } |
| |
| @Test |
| public void testIsToolchainResolutionEnabled_enabled() throws Exception { |
| useConfiguration("--incompatible_enable_cc_toolchain_resolution"); |
| |
| assertThat(toolchainResolutionEnabled()).isTrue(); |
| } |
| |
| @Test |
| public void testWrongExtensionThrowsError() throws Exception { |
| setUpCcLinkingContextTest(false); |
| scratch.file( |
| "foo/BUILD", |
| "load('//tools/build_defs/cc:rule.bzl', 'crule')", |
| "cc_binary(name='bin',", |
| " deps = [':a'],", |
| ")", |
| "crule(name='a',", |
| " static_library = 'a.o',", |
| " pic_static_library = 'a.pic.o',", |
| " dynamic_library = 'a.ifso',", |
| " interface_library = 'a.so',", |
| " dynamic_library_symlink_path = 'a.lib',", |
| " interface_library_symlink_path = 'a.dll',", |
| ")"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:bin")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'a.o' does not have any of the allowed extensions .a, .lib, .pic.a or .rlib"); |
| assertThat(e) |
| .hasMessageThat() |
| .contains( |
| "'a.pic.o' does not have any of the allowed extensions .a, .lib, .pic.a or .rlib"); |
| assertThat(e) |
| .hasMessageThat() |
| .contains( |
| "'a.ifso' does not have any of the allowed extensions .so, .dylib, .dll, .pyd or" |
| + " .wasm"); |
| assertThat(e) |
| .hasMessageThat() |
| .contains( |
| "'a.lib' does not have any of the allowed extensions .so, .dylib, .dll, .pyd or .wasm"); |
| assertThat(e) |
| .hasMessageThat() |
| .contains( |
| "'a.dll' does not have any of the allowed extensions .ifso, .tbd, .lib or .dll.a"); |
| } |
| |
| @Test |
| public void testCcOutputsMerging() throws Exception { |
| setupCcOutputsTest(); |
| scratch.file( |
| "foo/BUILD", |
| "load('//tools/build_defs/foo:extension.bzl', 'cc_starlark_library')", |
| "cc_starlark_library(", |
| " name = 'starlark_lib',", |
| " object1 = 'object1.o',", |
| " pic_object1 = 'pic_object1.o',", |
| " object2 = 'object2.o',", |
| " pic_object2 = 'pic_object2.o',", |
| ")"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| CcCompilationOutputs compilationOutputs = |
| (CcCompilationOutputs) getMyInfoFromTarget(target).getValue("compilation_outputs"); |
| assertThat( |
| AnalysisTestUtil.artifactsToStrings( |
| targetConfig, compilationOutputs.getObjectFiles(/* usePic= */ true))) |
| .containsExactly("src foo/pic_object1.o", "src foo/pic_object2.o"); |
| assertThat( |
| AnalysisTestUtil.artifactsToStrings( |
| targetConfig, compilationOutputs.getObjectFiles(/* usePic= */ false))) |
| .containsExactly("src foo/object1.o", "src foo/object2.o"); |
| } |
| |
| @Test |
| public void testObjectsWrongExtension() throws Exception { |
| doTestCcOutputsWrongExtension("object1", "objects"); |
| } |
| |
| @Test |
| public void testPicObjectsWrongExtension() throws Exception { |
| doTestCcOutputsWrongExtension("pic_object1", "pic_objects"); |
| } |
| |
| @Test |
| public void testObjectsRightExtension() throws Exception { |
| doTestCcOutputsRightExtension("object1"); |
| } |
| |
| @Test |
| public void testPicObjectsRightExtension() throws Exception { |
| doTestCcOutputsRightExtension("pic_object1"); |
| } |
| |
| @Test |
| public void testCreateOnlyPic() throws Exception { |
| getAnalysisMock() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, CcToolchainConfig.builder().withFeatures(CppRuleClasses.SUPPORTS_PIC)); |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", String.join("", "disallow_nopic_outputs=True")); |
| assertThat(getConfiguredTarget("//foo:bin")).isNotNull(); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(getFilenamesToBuild(target)).doesNotContain("starlark_lib.o"); |
| assertThat(getFilenamesToBuild(target)).contains("starlark_lib.pic.o"); |
| } |
| |
| @Test |
| public void testCreateOnlyNoPic() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", String.join("", "disallow_pic_outputs=True")); |
| assertThat(getConfiguredTarget("//foo:bin")).isNotNull(); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(getFilenamesToBuild(target)).contains("starlark_lib.o"); |
| assertThat(getFilenamesToBuild(target)).doesNotContain("starlark_lib.pic.o"); |
| } |
| |
| @Test |
| public void testCreatePicAndNoPic() throws Exception { |
| getAnalysisMock() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, CcToolchainConfig.builder().withFeatures(CppRuleClasses.SUPPORTS_PIC)); |
| createFilesForTestingCompilation(scratch, "tools/build_defs/foo", ""); |
| useConfiguration("--compilation_mode=opt"); |
| assertThat(getConfiguredTarget("//foo:bin")).isNotNull(); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(getFilenamesToBuild(target)).contains("starlark_lib.pic.o"); |
| assertThat(getFilenamesToBuild(target)).contains("starlark_lib.o"); |
| } |
| |
| @Test |
| public void testDoNotCreateEitherPicOrNoPic() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, |
| "tools/build_defs/foo", |
| String.join("", "disallow_nopic_outputs=True, disallow_pic_outputs=True")); |
| reporter.removeHandler(failFastHandler); |
| getConfiguredTarget("//foo:bin"); |
| assertContainsEvent("Either PIC or no PIC actions have to be created."); |
| } |
| |
| @Test |
| public void testCreateStaticLibraries() throws Exception { |
| getAnalysisMock() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder() |
| .withFeatures(CppRuleClasses.SUPPORTS_DYNAMIC_LINKER, CppRuleClasses.SUPPORTS_PIC)); |
| createFilesForTestingLinking(scratch, "tools/build_defs/foo", /* linkProviderLines= */ ""); |
| assertThat(getConfiguredTarget("//foo:starlark_lib")).isNotNull(); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat( |
| getFilesToBuild(target).toList().stream() |
| .map(Artifact::getFilename) |
| .collect(ImmutableList.toImmutableList())) |
| .contains("libstarlark_lib.a"); |
| } |
| |
| @Test |
| public void testDoNotCreateStaticLibraries() throws Exception { |
| createFilesForTestingLinking(scratch, "tools/build_defs/foo", "disallow_static_libraries=True"); |
| assertThat(getConfiguredTarget("//foo:starlark_lib")).isNotNull(); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat( |
| getFilesToBuild(target).toList().stream() |
| .map(Artifact::getFilename) |
| .collect(ImmutableList.toImmutableList())) |
| .doesNotContain("libstarlark_lib.a"); |
| } |
| |
| private static ImmutableList<String> getFilenamesToBuild(ConfiguredTarget target) { |
| return getFilesToBuild(target).toList().stream() |
| .map(Artifact::getFilename) |
| .collect(ImmutableList.toImmutableList()); |
| } |
| |
| @Test |
| public void testCcNativeRuleDependingOnStarlarkDefinedRule() throws Exception { |
| createFiles(scratch, "tools/build_defs/cc"); |
| assertThat(getConfiguredTarget("//foo:bin")).isNotNull(); |
| } |
| |
| @Test |
| public void testUserCompileFlagsInRulesApi() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", "user_compile_flags=['-COMPILATION_OPTION']"); |
| assertThat(getConfiguredTarget("//foo:bin")).isNotNull(); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| CppCompileAction action = |
| (CppCompileAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".o")); |
| assertThat(action.getArguments()).contains("-COMPILATION_OPTION"); |
| } |
| |
| @Test |
| public void testIncludeDirs() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", "includes=['foo/bar', 'baz/qux']"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(target).isNotNull(); |
| CppCompileAction action = |
| (CppCompileAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".o")); |
| assertThat(action.getArguments()).containsAtLeast("-Ifoo/bar", "-Ibaz/qux"); |
| } |
| |
| @Test |
| public void testSystemIncludeDirs() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", "system_includes=['foo/bar', 'baz/qux']"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(target).isNotNull(); |
| CppCompileAction action = |
| (CppCompileAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".o")); |
| assertThat(action.getArguments()) |
| .containsAtLeast("-isystem", "foo/bar", "-isystem", "baz/qux") |
| .inOrder(); |
| } |
| |
| @Test |
| public void testQuoteIncludeDirs() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", "quote_includes=['foo/bar', 'baz/qux']"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(target).isNotNull(); |
| CppCompileAction action = |
| (CppCompileAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".o")); |
| assertThat(action.getArguments()) |
| .containsAtLeast("-iquote", "foo/bar", "-iquote", "baz/qux") |
| .inOrder(); |
| } |
| |
| @Test |
| public void testFrameworkIncludeDirs() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", "framework_includes=['foo/bar', 'baz/qux']"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(target).isNotNull(); |
| CppCompileAction action = |
| (CppCompileAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".o")); |
| assertThat(action.getArguments()).containsAtLeast("-Ffoo/bar", "-Fbaz/qux").inOrder(); |
| } |
| |
| @Test |
| public void testDefines() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", "defines=['DEFINE1', 'DEFINE2']"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(target).isNotNull(); |
| CppCompileAction action = |
| (CppCompileAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".o")); |
| assertThat(action.getArguments()).containsAtLeast("-DDEFINE1", "-DDEFINE2"); |
| } |
| |
| @Test |
| public void testLocalDefines() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", "local_defines=['DEFINE1', 'DEFINE2']"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(target).isNotNull(); |
| CppCompileAction action = |
| (CppCompileAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".o")); |
| assertThat(action.getArguments()).containsAtLeast("-DDEFINE1", "-DDEFINE2"); |
| } |
| |
| @Test |
| public void testIncludePrefix() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "third_party/tools/build_defs/foo", "include_prefix='prefix'"); |
| scratch.file( |
| "third_party/bar/BUILD", |
| "load('//third_party/tools/build_defs/foo:extension.bzl', 'cc_starlark_library')", |
| "cc_starlark_library(", |
| " name = 'starlark_lib',", |
| " srcs = ['starlark_lib.cc'],", |
| " public_hdrs = ['starlark_lib.h'],", |
| " private_hdrs = ['private_starlark_lib.h'],", |
| ")"); |
| ConfiguredTarget target = getConfiguredTarget("//third_party/bar:starlark_lib"); |
| assertThat(target).isNotNull(); |
| CcInfo ccInfo = target.get(CcInfo.PROVIDER); |
| assertThat(artifactsToStrings(ccInfo.getCcCompilationContext().getDirectPublicHdrs())) |
| .contains("bin third_party/bar/_virtual_includes/starlark_lib/prefix/starlark_lib.h"); |
| } |
| |
| @Test |
| public void testStripIncludePrefix() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "third_party/tools/build_defs/foo", "strip_include_prefix='v1'"); |
| scratch.file( |
| "third_party/bar/BUILD", |
| "load('//third_party/tools/build_defs/foo:extension.bzl', 'cc_starlark_library')", |
| "cc_starlark_library(", |
| " name = 'starlark_lib',", |
| " srcs = ['starlark_lib.cc'],", |
| " public_hdrs = ['v1/starlark_lib.h'],", |
| " private_hdrs = ['v1/private_starlark_lib.h'],", |
| ")"); |
| ConfiguredTarget target = getConfiguredTarget("//third_party/bar:starlark_lib"); |
| assertThat(target).isNotNull(); |
| CcInfo ccInfo = target.get(CcInfo.PROVIDER); |
| assertThat(artifactsToStrings(ccInfo.getCcCompilationContext().getDirectPublicHdrs())) |
| .contains("bin third_party/bar/_virtual_includes/starlark_lib/starlark_lib.h"); |
| } |
| |
| @Test |
| public void testStripIncludePrefixAndIncludePrefix() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, |
| "third_party/tools/build_defs/foo", |
| "strip_include_prefix='v1', include_prefix='prefix'"); |
| scratch.file( |
| "third_party/bar/BUILD", |
| "load('//third_party/tools/build_defs/foo:extension.bzl', 'cc_starlark_library')", |
| "cc_starlark_library(", |
| " name = 'starlark_lib',", |
| " srcs = ['starlark_lib.cc'],", |
| " public_hdrs = ['v1/starlark_lib.h'],", |
| " private_hdrs = ['v1/private_starlark_lib.h'],", |
| ")"); |
| ConfiguredTarget target = getConfiguredTarget("//third_party/bar:starlark_lib"); |
| assertThat(target).isNotNull(); |
| CcInfo ccInfo = target.get(CcInfo.PROVIDER); |
| assertThat(artifactsToStrings(ccInfo.getCcCompilationContext().getDirectPublicHdrs())) |
| .contains("bin third_party/bar/_virtual_includes/starlark_lib/prefix/starlark_lib.h"); |
| } |
| |
| @Test |
| public void testHeaders() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", /* compileProviderLines= */ ""); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(target).isNotNull(); |
| CcInfo ccInfo = target.get(CcInfo.PROVIDER); |
| assertThat(artifactsToStrings(ccInfo.getCcCompilationContext().getDeclaredIncludeSrcs())) |
| .containsAtLeast( |
| "src foo/dep2.h", "src foo/starlark_lib.h", "src foo/private_starlark_lib.h"); |
| assertThat(artifactsToStrings(ccInfo.getCcCompilationContext().getTextualHdrs())) |
| .containsExactly("src foo/textual_hdr.h"); |
| } |
| |
| @Test |
| public void testCompileOutputHasSuffix() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", /* compileProviderLines= */ ""); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(target).isNotNull(); |
| assertThat(artifactsToStrings(getFilesToBuild(target))) |
| .contains("bin foo/_objs/starlark_lib_suffix/starlark_lib.o"); |
| } |
| |
| @Test |
| public void testCompilationContexts() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", /* compileProviderLines= */ ""); |
| assertThat(getConfiguredTarget("//foo:bin")).isNotNull(); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| CppCompileAction action = |
| (CppCompileAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".o")); |
| assertThat(action.getArguments()).containsAtLeast("-DDEFINE_DEP1", "-DDEFINE_DEP2"); |
| } |
| |
| @Test |
| public void testLinkingOutputs() throws Exception { |
| createFiles(scratch, "tools/build_defs/foo"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(target).isNotNull(); |
| @SuppressWarnings("unchecked") |
| Sequence<LibraryToLink> libraries = |
| (Sequence<LibraryToLink>) getMyInfoFromTarget(target).getValue("libraries"); |
| assertThat( |
| libraries.stream() |
| .map(x -> x.getResolvedSymlinkDynamicLibrary().getFilename()) |
| .collect(ImmutableList.toImmutableList())) |
| .contains("libstarlark_lib.so"); |
| } |
| |
| @Test |
| public void testUserLinkFlags() throws Exception { |
| createFilesForTestingLinking( |
| scratch, "tools/build_defs/foo", "user_link_flags=['-LINKING_OPTION']"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(target).isNotNull(); |
| assertThat(target.get(CcInfo.PROVIDER).getCcLinkingContext().getFlattenedUserLinkFlags()) |
| .contains("-LINKING_OPTION"); |
| } |
| |
| @Test |
| public void testLinkingContexts() throws Exception { |
| createFilesForTestingLinking(scratch, "tools/build_defs/foo", /* linkProviderLines= */ ""); |
| assertThat(getConfiguredTarget("//foo:bin")).isNotNull(); |
| ConfiguredTarget target = getConfiguredTarget("//foo:bin"); |
| CppLinkAction action = |
| (CppLinkAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), "bin")); |
| assertThat(action.getArguments()).containsAtLeast("-DEP1_LINKOPT", "-DEP2_LINKOPT"); |
| } |
| |
| @Test |
| public void testAlwayslinkTrue() throws Exception { |
| createFilesForTestingLinking(scratch, "tools/build_defs/foo", "alwayslink=True"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(target).isNotNull(); |
| assertThat( |
| target.get(CcInfo.PROVIDER).getCcLinkingContext().getLibraries().toList().stream() |
| .filter(LibraryToLink::getAlwayslink) |
| .collect(ImmutableList.toImmutableList())) |
| .hasSize(1); |
| } |
| |
| @Test |
| public void testAlwayslinkFalse() throws Exception { |
| createFilesForTestingLinking(scratch, "tools/build_defs/foo", "alwayslink=False"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(target).isNotNull(); |
| assertThat( |
| target.get(CcInfo.PROVIDER).getCcLinkingContext().getLibraries().toList().stream() |
| .filter(LibraryToLink::getAlwayslink) |
| .collect(ImmutableList.toImmutableList())) |
| .isEmpty(); |
| } |
| |
| @Test |
| public void testAdditionalLinkingInputs() throws Exception { |
| createFilesForTestingLinking( |
| scratch, "tools/build_defs/foo", "additional_inputs=ctx.files._additional_inputs"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(target).isNotNull(); |
| assertThat(target.get(CcInfo.PROVIDER).getCcLinkingContext().getNonCodeInputs().toList()) |
| .hasSize(1); |
| } |
| |
| @Test |
| public void testAdditionalCompilationInputs() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", "additional_inputs=ctx.files._additional_compiler_inputs"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| assertThat(target).isNotNull(); |
| CppCompileAction action = |
| (CppCompileAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".o")); |
| assertThat(artifactsToStrings(action.getMandatoryInputs())) |
| .contains("src foo/extra_compiler_input"); |
| } |
| |
| @Test |
| public void testPossibleSrcsExtensions() throws Exception { |
| doTestPossibleExtensionsOfSrcsAndHdrs( |
| "srcs", CppFileTypes.ALL_C_CLASS_SOURCE.including(CppFileTypes.ASSEMBLER).getExtensions()); |
| } |
| |
| @Test |
| public void testPossiblePrivateHdrExtensions() throws Exception { |
| doTestPossibleExtensionsOfSrcsAndHdrs("private_hdrs", CppFileTypes.CPP_HEADER.getExtensions()); |
| } |
| |
| @Test |
| public void testPossiblePublicHdrExtensions() throws Exception { |
| doTestPossibleExtensionsOfSrcsAndHdrs("public_hdrs", CppFileTypes.CPP_HEADER.getExtensions()); |
| } |
| |
| @Test |
| public void testTreeArtifactSrcs() throws Exception { |
| doTestTreeAtrifactInSrcsAndHdrs("srcs"); |
| } |
| |
| @Test |
| public void testTreeArtifactPrivateHdrs() throws Exception { |
| doTestTreeAtrifactInSrcsAndHdrs("private_hdrs"); |
| } |
| |
| @Test |
| public void testTreeArtifactPublicHdrs() throws Exception { |
| doTestTreeAtrifactInSrcsAndHdrs("public_hdrs"); |
| } |
| |
| private static void createFilesForTestingCompilation( |
| Scratch scratch, String bzlFilePath, String compileProviderLines) throws Exception { |
| createFiles(scratch, bzlFilePath, compileProviderLines, ""); |
| } |
| |
| private static void createFilesForTestingLinking( |
| Scratch scratch, String bzlFilePath, String linkProviderLines) throws Exception { |
| createFiles(scratch, bzlFilePath, "", linkProviderLines); |
| } |
| |
| private static void createFiles(Scratch scratch, String bzlFilePath) throws Exception { |
| createFiles(scratch, bzlFilePath, "", ""); |
| } |
| |
| private static void createFiles( |
| Scratch scratch, String bzlFilePath, String compileProviderLines, String linkProviderLines) |
| throws Exception { |
| String fragments = " fragments = ['google_cpp', 'cpp'],"; |
| if (AnalysisMock.get().isThisBazel()) { |
| fragments = " fragments = ['cpp'],"; |
| } |
| scratch.overwriteFile(bzlFilePath + "/BUILD"); |
| scratch.file( |
| bzlFilePath + "/extension.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _cc_aspect_impl(target, ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " requested_features = ctx.features,", |
| " unsupported_features = ctx.disabled_features,", |
| " )", |
| " (compilation_context, compilation_outputs) = cc_common.compile(", |
| " actions = ctx.actions,", |
| " feature_configuration = feature_configuration,", |
| " cc_toolchain = toolchain,", |
| " name = ctx.label.name + '_aspect',", |
| " srcs = ctx.rule.files.srcs,", |
| " public_hdrs = ctx.rule.files.hdrs,", |
| " )", |
| " (linking_context, linking_outputs) = (", |
| " cc_common.create_linking_context_from_compilation_outputs(", |
| " actions = ctx.actions,", |
| " feature_configuration = feature_configuration,", |
| " name = ctx.label.name + '_aspect',", |
| " cc_toolchain = toolchain,", |
| " compilation_outputs = compilation_outputs,", |
| " )", |
| " )", |
| " return []", |
| "_cc_aspect = aspect(", |
| " implementation = _cc_aspect_impl,", |
| " attrs = {", |
| " '_cc_toolchain': attr.label(default =" |
| + " '@bazel_tools//tools/cpp:current_cc_toolchain'),", |
| " },", |
| fragments, |
| ")", |
| "def _cc_starlark_library_impl(ctx):", |
| " dep_compilation_contexts = []", |
| " dep_linking_contexts = []", |
| " for dep in ctx.attr._deps:", |
| " dep_compilation_contexts.append(dep[CcInfo].compilation_context)", |
| " dep_linking_contexts.append(dep[CcInfo].linking_context)", |
| " toolchain = ctx.attr._my_cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain=toolchain,", |
| " requested_features = ctx.features,", |
| " unsupported_features = ctx.disabled_features)", |
| " foo_dict = {'string_variable': 'foo',", |
| " 'string_sequence_variable' : ['foo'],", |
| " 'string_depset_variable': depset(['foo'])}", |
| " (compilation_context, compilation_outputs) = cc_common.compile(", |
| " actions=ctx.actions,", |
| " feature_configuration=feature_configuration,", |
| " cc_toolchain=toolchain,", |
| " srcs=ctx.files.srcs,", |
| " name=ctx.label.name + '_suffix',", |
| " compilation_contexts = dep_compilation_contexts,", |
| " public_hdrs=ctx.files.public_hdrs,", |
| " textual_hdrs=ctx.files.textual_hdrs,", |
| " private_hdrs=ctx.files.private_hdrs" + (compileProviderLines.isEmpty() ? "" : ","), |
| " " + compileProviderLines, |
| " )", |
| " (linking_context,", |
| " linking_outputs) = cc_common.create_linking_context_from_compilation_outputs(", |
| " actions=ctx.actions,", |
| " feature_configuration=feature_configuration,", |
| " compilation_outputs=compilation_outputs,", |
| " name = ctx.label.name,", |
| " linking_contexts = dep_linking_contexts,", |
| " cc_toolchain=toolchain" + (linkProviderLines.isEmpty() ? "" : ","), |
| " " + linkProviderLines, |
| " )", |
| " files_to_build = []", |
| " files_to_build.extend(compilation_outputs.pic_objects)", |
| " files_to_build.extend(compilation_outputs.objects)", |
| " library_to_link = None", |
| " if len(ctx.files.srcs) > 0:", |
| " library_to_link = linking_outputs.library_to_link", |
| " if library_to_link.pic_static_library != None:", |
| " files_to_build.append(library_to_link.pic_static_library)", |
| " if library_to_link.static_library != None:", |
| " files_to_build.append(library_to_link.static_library)", |
| " if library_to_link.dynamic_library != None:", |
| " files_to_build.append(library_to_link.dynamic_library)", |
| " return [MyInfo(libraries=[library_to_link]),", |
| " DefaultInfo(files=depset(files_to_build)),", |
| " CcInfo(compilation_context=compilation_context,", |
| " linking_context=linking_context)]", |
| "cc_starlark_library = rule(", |
| " implementation = _cc_starlark_library_impl,", |
| " attrs = {", |
| " 'srcs': attr.label_list(allow_files=True),", |
| " 'public_hdrs': attr.label_list(allow_files=True),", |
| " 'textual_hdrs': attr.label_list(allow_files=True),", |
| " 'private_hdrs': attr.label_list(allow_files=True),", |
| " '_additional_inputs': attr.label_list(allow_files=True," |
| + " default=['//foo:script.lds']),", |
| " '_additional_compiler_inputs': attr.label_list(allow_files=True," |
| + " default=['//foo:extra_compiler_input']),", |
| " '_deps': attr.label_list(default=['//foo:dep1', '//foo:dep2']),", |
| " 'aspect_deps': attr.label_list(aspects=[_cc_aspect]),", |
| " '_my_cc_toolchain': attr.label(default =", |
| " configuration_field(fragment = 'cpp', name = 'cc_toolchain'))", |
| " },", |
| fragments, |
| ")"); |
| scratch.file( |
| "foo/BUILD", |
| "load('//" + bzlFilePath + ":extension.bzl', 'cc_starlark_library')", |
| "cc_library(", |
| " name = 'dep1',", |
| " srcs = ['dep1.cc'],", |
| " hdrs = ['dep1.h'],", |
| " defines = ['DEFINE_DEP1'],", |
| " linkopts = ['-DEP1_LINKOPT'],", |
| ")", |
| "cc_library(", |
| " name = 'dep2',", |
| " srcs = ['dep2.cc'],", |
| " hdrs = ['dep2.h'],", |
| " defines = ['DEFINE_DEP2'],", |
| " linkopts = ['-DEP2_LINKOPT'],", |
| ")", |
| "cc_starlark_library(", |
| " name = 'starlark_lib',", |
| " srcs = ['starlark_lib.cc'],", |
| " public_hdrs = ['starlark_lib.h'],", |
| " textual_hdrs = ['textual_hdr.h'],", |
| " private_hdrs = ['private_starlark_lib.h'],", |
| ")", |
| "cc_binary(", |
| " name = 'bin',", |
| " deps = ['starlark_lib'],", |
| ")"); |
| } |
| |
| @Test |
| public void testTransitiveLinkWithDeps() throws Exception { |
| setupTestTransitiveLink(scratch, "linking_contexts = dep_linking_contexts"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:bin"); |
| assertThat(target).isNotNull(); |
| Artifact executable = (Artifact) getMyInfoFromTarget(target).getValue("executable"); |
| assertThat(artifactsToStrings(getGeneratingAction(executable).getInputs())) |
| .containsAtLeast("bin foo/libdep1.a", "bin foo/libdep2.a"); |
| } |
| |
| @Test |
| public void testTransitiveLinkForDynamicLibrary() throws Exception { |
| setupTestTransitiveLink(scratch, "output_type = 'dynamic_library'"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:bin"); |
| assertThat(target).isNotNull(); |
| LibraryToLink library = (LibraryToLink) getMyInfoFromTarget(target).getValue("library"); |
| assertThat(library).isNotNull(); |
| Object executable = getMyInfoFromTarget(target).getValue("executable"); |
| assertThat(Starlark.isNullOrNone(executable)).isTrue(); |
| } |
| |
| @Test |
| public void testCustomNameOutputArtifact() throws Exception { |
| createCcBinRule( |
| scratch, |
| /* internalApi= */ true, |
| "output_type = 'dynamic_library'", |
| " main_output=ctx.actions.declare_file('custom_name.so')"); |
| scratch.file( |
| "foo/BUILD", |
| "load('//bazel_internal/test_rules/cc:extension.bzl', 'cc_bin')", |
| "cc_bin(", |
| " name = 'bin',", |
| ")"); |
| |
| ConfiguredTarget target = getConfiguredTarget("//foo:bin"); |
| assertThat(target).isNotNull(); |
| |
| LibraryToLink library = (LibraryToLink) getMyInfoFromTarget(target).getValue("library"); |
| Artifact dynamicLibrary = library.getResolvedSymlinkDynamicLibrary(); |
| if (dynamicLibrary == null) { |
| dynamicLibrary = library.getDynamicLibrary(); |
| } |
| |
| assertThat(dynamicLibrary).isNotNull(); |
| assertThat(dynamicLibrary.getFilename()).isEqualTo("custom_name.so"); |
| } |
| |
| @Test |
| public void testCustomNameOutputArtifactRaisesError() throws Exception { |
| setupTestTransitiveLink(scratch, "output_type = 'dynamic_library'", " main_output=None"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:bin")); |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testInterfaceLibraryProducedForTransitiveLinkOnWindows() throws Exception { |
| getAnalysisMock() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder() |
| .withFeatures( |
| CppRuleClasses.SUPPORTS_DYNAMIC_LINKER, |
| CppRuleClasses.TARGETS_WINDOWS, |
| CppRuleClasses.SUPPORTS_INTERFACE_SHARED_LIBRARIES, |
| CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY)); |
| setupTestTransitiveLink(scratch, "output_type = 'dynamic_library'"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:bin"); |
| assertThat(target).isNotNull(); |
| LibraryToLink library = (LibraryToLink) getMyInfoFromTarget(target).getValue("library"); |
| assertThat(library).isNotNull(); |
| assertThat(library.getDynamicLibrary()).isNotNull(); |
| assertThat(library.getInterfaceLibrary()).isNotNull(); |
| } |
| |
| @Test |
| public void testTransitiveLinkForExecutable() throws Exception { |
| setupTestTransitiveLink(scratch, "output_type = 'executable'"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:bin"); |
| assertThat(target).isNotNull(); |
| Artifact executable = (Artifact) getMyInfoFromTarget(target).getValue("executable"); |
| assertThat(executable).isNotNull(); |
| Object library = getMyInfoFromTarget(target).getValue("library"); |
| assertThat(Starlark.isNullOrNone(library)).isTrue(); |
| } |
| |
| @Test |
| public void testPassGrepIncludesToApiEvenThoughItDoesntDoAnything() throws Exception { |
| setupTestTransitiveLink(scratch, "grep_includes = ctx.executable._grep_includes"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:bin"); |
| assertThat(target).isNotNull(); |
| } |
| |
| @Test |
| public void testTransitiveLinkWithCompilationOutputs() throws Exception { |
| setupTestTransitiveLink(scratch, "compilation_outputs=objects"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:bin"); |
| assertThat(target).isNotNull(); |
| Artifact executable = (Artifact) getMyInfoFromTarget(target).getValue("executable"); |
| assertThat(artifactsToStrings(getGeneratingAction(executable).getInputs())) |
| .contains("src foo/file.o"); |
| } |
| |
| @Test |
| public void testLinkStampExpliciltyEnabledOverridesNoStampFlag() throws Exception { |
| useConfiguration("--nostamp"); |
| setupTestTransitiveLink(scratch, "stamp=1", "linking_contexts=dep_linking_contexts"); |
| assertStampEnabled(getLinkstampCompileAction("//foo:bin")); |
| } |
| |
| @Test |
| public void testLinkExplicitlyDisabledOverridesStampFlag() throws Exception { |
| useConfiguration("--nostamp"); |
| setupTestTransitiveLink(scratch, "stamp=0", "linking_contexts=dep_linking_contexts"); |
| assertStampDisabled(getLinkstampCompileAction("//foo:bin")); |
| } |
| |
| @Test |
| public void testLinkStampUseFlagStamp() throws Exception { |
| useConfiguration("--stamp"); |
| setupTestTransitiveLink(scratch, "stamp=-1", "linking_contexts=dep_linking_contexts"); |
| assertStampEnabled(getLinkstampCompileAction("//foo:bin")); |
| } |
| |
| @Test |
| public void testLinkStampUseFlagNoStamp() throws Exception { |
| useConfiguration("--nostamp"); |
| setupTestTransitiveLink(scratch, "stamp=-1", "linking_contexts=dep_linking_contexts"); |
| assertStampDisabled(getLinkstampCompileAction("//foo:bin")); |
| } |
| |
| @Test |
| public void testLinkStampDisabledByDefaultDespiteStampFlag() throws Exception { |
| useConfiguration("--stamp"); |
| setupTestTransitiveLink(scratch, "linking_contexts=dep_linking_contexts"); |
| assertStampDisabled(getLinkstampCompileAction("//foo:bin")); |
| } |
| |
| @Test |
| public void testLinkStampInvalid() throws Exception { |
| setupTestTransitiveLink(scratch, "stamp=2"); |
| checkError( |
| "//foo:bin", |
| "stamp value 2 is not supported, must be 0 (disabled), 1 (enabled), or -1 (default)"); |
| } |
| |
| private CppCompileAction getLinkstampCompileAction(String label) |
| throws LabelSyntaxException, EvalException { |
| ConfiguredTarget target = getConfiguredTarget(label); |
| Artifact executable = (Artifact) getMyInfoFromTarget(target).getValue("executable"); |
| CppLinkAction generatingAction = (CppLinkAction) getGeneratingAction(executable); |
| Artifact compiledLinkstamp = |
| ActionsTestUtil.getFirstArtifactEndingWith(generatingAction.getInputs(), "version.o"); |
| CppCompileAction linkstampCompileAction = |
| (CppCompileAction) getGeneratingAction(compiledLinkstamp); |
| assertThat(linkstampCompileAction.getMnemonic()).isEqualTo("CppLinkstampCompile"); |
| return linkstampCompileAction; |
| } |
| |
| private void assertStampEnabled(CppCompileAction linkstampAction) |
| throws CommandLineExpansionException { |
| assertThat(linkstampAction.getArguments()) |
| .contains(getRelativeOutputPath() + "/k8-fastbuild/include/build-info-volatile.h"); |
| } |
| |
| private void assertStampDisabled(CppCompileAction linkstampAction) |
| throws CommandLineExpansionException { |
| assertThat(linkstampAction.getArguments()) |
| .contains(getRelativeOutputPath() + "/k8-fastbuild/include/build-info-redacted.h"); |
| } |
| |
| @Test |
| public void testApiWithAspectsOnTargetsInExternalRepos() throws Exception { |
| if (!AnalysisMock.get().isThisBazel()) { |
| return; |
| } |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", /* compileProviderLines= */ ""); |
| FileSystemUtils.appendIsoLatin1( |
| scratch.resolve("WORKSPACE"), "local_repository(name='r', path='/r')"); |
| scratch.file("/r/WORKSPACE"); |
| scratch.file("/r/p/BUILD", "cc_library(", " name = 'a',", " srcs = ['a.cc'],", ")"); |
| invalidatePackages(); |
| scratch.file( |
| "b/BUILD", |
| "load('//tools/build_defs/foo:extension.bzl', 'cc_starlark_library')", |
| "cc_starlark_library(", |
| " name = 'b',", |
| " srcs = ['b.cc'],", |
| " aspect_deps = ['@r//p:a']", |
| ")"); |
| assertThat(getConfiguredTarget("//b:b")).isNotNull(); |
| } |
| |
| @Test |
| public void testObjectsApi() throws Exception { |
| useConfiguration("--compilation_mode=opt"); |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder() |
| .withFeatures(CppRuleClasses.SUPPORTS_PIC, CppRuleClasses.PIC)); |
| |
| scratchObjectsProvidingRule(); |
| |
| Provider.Key key = new StarlarkProvider.Key(Label.parseCanonical("//foo:foo.bzl"), "FooInfo"); |
| LibraryToLink fooLibrary = |
| Iterables.getOnlyElement( |
| getConfiguredTarget("//foo:dep") |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getLibraries() |
| .toList()); |
| StarlarkInfo fooInfo = |
| (StarlarkInfo) getConfiguredTarget("//foo:foo").get(StarlarkProviderIdentifier.forKey(key)); |
| |
| assertThat(fooLibrary.getObjectFiles()).isEqualTo(fooInfo.getValue("objects")); |
| assertThat(fooLibrary.getPicObjectFiles()).isEqualTo(fooInfo.getValue("pic_objects")); |
| } |
| |
| @Test |
| public void testObjectsApiNeverReturningNones() throws Exception { |
| scratchObjectsProvidingRule(); |
| |
| Provider.Key key = new StarlarkProvider.Key(Label.parseCanonical("//foo:foo.bzl"), "FooInfo"); |
| |
| // Default toolchain is without PIC support, so pic_objects should be None |
| StarlarkInfo fooInfoForPic = |
| (StarlarkInfo) getConfiguredTarget("//foo:foo").get(StarlarkProviderIdentifier.forKey(key)); |
| |
| Object picObjects = fooInfoForPic.getValue("pic_objects"); |
| assertThat(picObjects).isNotEqualTo(Starlark.NONE); |
| assertThat((List) picObjects).isEmpty(); |
| |
| // With PIC and the default compilation_mode which is fastbuild C++ rules only produce PIC |
| // objects. |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder() |
| .withFeatures(CppRuleClasses.SUPPORTS_PIC, CppRuleClasses.PIC)); |
| invalidatePackages(); |
| StarlarkInfo fooInfoForNoPic = |
| (StarlarkInfo) getConfiguredTarget("//foo:foo").get(StarlarkProviderIdentifier.forKey(key)); |
| |
| Object objects = fooInfoForNoPic.getValue("objects"); |
| assertThat(objects).isNotEqualTo(Starlark.NONE); |
| assertThat((List) objects).isEmpty(); |
| } |
| |
| @Test |
| public void testLtoBitcodeFilesApi() throws Exception { |
| useConfiguration("--compilation_mode=opt", "--features=thin_lto"); |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder() |
| .withFeatures( |
| CppRuleClasses.THIN_LTO, CppRuleClasses.SUPPORTS_PIC, CppRuleClasses.PIC)); |
| |
| scratchObjectsProvidingRule(); |
| |
| Provider.Key key = new StarlarkProvider.Key(Label.parseCanonical("//foo:foo.bzl"), "FooInfo"); |
| LibraryToLink fooLibrary = |
| Iterables.getOnlyElement( |
| getConfiguredTarget("//foo:dep") |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getLibraries() |
| .toList()); |
| StarlarkInfo fooInfo = |
| (StarlarkInfo) getConfiguredTarget("//foo:foo").get(StarlarkProviderIdentifier.forKey(key)); |
| |
| assertThat(ImmutableList.copyOf(fooLibrary.getLtoCompilationContext().getBitcodeFiles())) |
| .isEqualTo(fooInfo.getValue("lto_bitcode_files")); |
| assertThat(fooLibrary.getLtoCompilationContext().getBitcodeFiles()).isNotEmpty(); |
| |
| assertThat(ImmutableList.copyOf(fooLibrary.getPicLtoCompilationContext().getBitcodeFiles())) |
| .isEqualTo(fooInfo.getValue("pic_lto_bitcode_files")); |
| assertThat(fooLibrary.getPicLtoCompilationContext().getBitcodeFiles()).isNotEmpty(); |
| } |
| |
| @Test |
| public void testLtoBitcodeFilesApiNeverReturningNones() throws Exception { |
| // We do not add --features=thin_lto for this test. |
| useConfiguration("--compilation_mode=opt"); |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder() |
| // We do not enable the THIN_LTO feature for this test. |
| .withFeatures(CppRuleClasses.SUPPORTS_PIC, CppRuleClasses.PIC)); |
| |
| scratchObjectsProvidingRule(); |
| |
| Provider.Key key = new StarlarkProvider.Key(Label.parseCanonical("//foo:foo.bzl"), "FooInfo"); |
| StarlarkInfo fooInfo = |
| (StarlarkInfo) getConfiguredTarget("//foo:foo").get(StarlarkProviderIdentifier.forKey(key)); |
| |
| Object picLtoBitcodeFiles = fooInfo.getValue("pic_lto_bitcode_files"); |
| assertThat(picLtoBitcodeFiles).isNotEqualTo(Starlark.NONE); |
| assertThat((List) picLtoBitcodeFiles).isEmpty(); |
| |
| Object ltoBitcodeFiles = fooInfo.getValue("lto_bitcode_files"); |
| assertThat(ltoBitcodeFiles).isNotEqualTo(Starlark.NONE); |
| assertThat((List) ltoBitcodeFiles).isEmpty(); |
| } |
| |
| private void scratchObjectsProvidingRule() throws IOException { |
| scratch.file( |
| "foo/BUILD", |
| "load(':foo.bzl', 'foo')", |
| "foo(", |
| " name = 'foo',", |
| " dep = ':dep',", |
| ")", |
| "cc_library(", |
| " name = 'dep',", |
| " srcs = ['dep.cc'],", |
| ")"); |
| scratch.file( |
| "foo/foo.bzl", |
| "FooInfo = provider(fields=['objects'," |
| + " 'pic_objects','lto_bitcode_files','pic_lto_bitcode_files'])", |
| "", |
| "def _foo_impl(ctx):", |
| " lib = ctx.attr.dep[CcInfo].linking_context.linker_inputs.to_list()[0].libraries[0]", |
| " return [FooInfo(objects=lib.objects, pic_objects=lib.pic_objects," |
| + " lto_bitcode_files=lib.lto_bitcode_files," |
| + " pic_lto_bitcode_files=lib.pic_lto_bitcode_files)]", |
| "", |
| "foo = rule(", |
| " implementation = _foo_impl,", |
| " attrs = {", |
| " 'dep': attr.label(),", |
| " }", |
| ")"); |
| } |
| |
| private void doTestPossibleExtensionsOfSrcsAndHdrs(String attrName, List<String> extensions) |
| throws Exception { |
| createFiles(scratch, "tools/build_defs/foo"); |
| reporter.removeHandler(failFastHandler); |
| |
| for (String extension : extensions) { |
| scratch.deleteFile("bar/BUILD"); |
| scratch.file( |
| "bar/BUILD", |
| "load('//tools/build_defs/foo:extension.bzl', 'cc_starlark_library')", |
| "cc_starlark_library(", |
| " name = 'starlark_lib',", |
| " " + attrName + " = ['file" + extension + "'],", |
| ")"); |
| getConfiguredTarget("//bar:starlark_lib"); |
| assertNoEvents(); |
| } |
| } |
| |
| private void doTestTreeAtrifactInSrcsAndHdrs(String attrName) throws Exception { |
| createFiles(scratch, "tools/build_defs/foo"); |
| reporter.removeHandler(failFastHandler); |
| |
| scratch.file( |
| "bar/create_tree_artifact.bzl", |
| "def _impl(ctx):", |
| " tree = ctx.actions.declare_directory('dir')", |
| " ctx.actions.run_shell(", |
| " outputs = [tree],", |
| " inputs = [],", |
| " arguments = [tree.path],", |
| " command = 'mkdir $1',", |
| " )", |
| " return [DefaultInfo(files = depset([tree]))]", |
| "create_tree_artifact = rule(implementation = _impl)"); |
| scratch.file( |
| "bar/BUILD", |
| "load('//tools/build_defs/foo:extension.bzl', 'cc_starlark_library')", |
| "load(':create_tree_artifact.bzl', 'create_tree_artifact')", |
| "create_tree_artifact(name = 'tree_artifact')", |
| "cc_starlark_library(", |
| " name = 'starlark_lib',", |
| " " + attrName + " = [':tree_artifact'],", |
| ")"); |
| getConfiguredTarget("//bar:starlark_lib"); |
| assertNoEvents(); |
| } |
| |
| private void doTestCcOutputsWrongExtension(String attrName, String paramName) throws Exception { |
| setupCcOutputsTest(); |
| scratch.file( |
| "foo/BUILD", |
| "load('//tools/build_defs/foo:extension.bzl', 'cc_starlark_library')", |
| "cc_starlark_library(", |
| " name = 'starlark_lib',", |
| " " + attrName + " = 'object.cannotpossiblybevalid',", |
| ")"); |
| reporter.removeHandler(failFastHandler); |
| getConfiguredTarget("//foo:starlark_lib"); |
| assertContainsEvent( |
| "has wrong extension. The list of possible extensions for '" + paramName + "'"); |
| } |
| |
| private void doTestCcOutputsRightExtension(String paramName) throws Exception { |
| setupCcOutputsTest(); |
| reporter.removeHandler(failFastHandler); |
| |
| for (String extension : Link.OBJECT_FILETYPES.getExtensions()) { |
| scratch.deleteFile("foo/BUILD"); |
| scratch.file( |
| "foo/BUILD", |
| "load('//tools/build_defs/foo:extension.bzl', 'cc_starlark_library')", |
| "cc_starlark_library(", |
| " name = 'starlark_lib',", |
| " " + paramName + " = 'object1" + extension + "',", |
| ")"); |
| getConfiguredTarget("//foo:starlark_lib"); |
| assertNoEvents(); |
| } |
| } |
| |
| private void setupCcOutputsTest() throws Exception { |
| scratch.overwriteFile("tools/build_defs/foo/BUILD"); |
| scratch.file( |
| "tools/build_defs/foo/extension.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _cc_starlark_library_impl(ctx):", |
| " objects = []", |
| " pic_objects = []", |
| " if ctx.file.object1 != None:", |
| " objects.append(ctx.file.object1)", |
| " if ctx.file.pic_object1 != None:", |
| " pic_objects.append(ctx.file.pic_object1)", |
| " c1 = cc_common.create_compilation_outputs(objects=depset(objects),", |
| " pic_objects=depset(pic_objects))", |
| " objects = []", |
| " pic_objects = []", |
| " if ctx.file.object2 != None:", |
| " objects.append(ctx.file.object2)", |
| " if ctx.file.pic_object2 != None:", |
| " pic_objects.append(ctx.file.pic_object2)", |
| " c2 = cc_common.create_compilation_outputs(objects=depset(objects),", |
| " pic_objects=depset(pic_objects))", |
| " compilation_outputs = cc_common.merge_compilation_outputs(", |
| " compilation_outputs=[c1, c2])", |
| " return [MyInfo(compilation_outputs=compilation_outputs)]", |
| "cc_starlark_library = rule(", |
| " implementation = _cc_starlark_library_impl,", |
| " attrs = {", |
| " 'object1': attr.label(allow_single_file=True),", |
| " 'pic_object1': attr.label(allow_single_file=True),", |
| " 'object2': attr.label(allow_single_file=True),", |
| " 'pic_object2': attr.label(allow_single_file=True),", |
| " },", |
| ")"); |
| } |
| |
| private static void createCcBinRule( |
| Scratch scratch, boolean internalApi, String... additionalLines) throws Exception { |
| String fragments = " fragments = ['google_cpp', 'cpp'],"; |
| if (AnalysisMock.get().isThisBazel()) { |
| fragments = " fragments = ['cpp'],"; |
| } |
| scratch.overwriteFile("tools/build_defs/BUILD"); |
| /*scratch.overwriteFile("tools/cpp/grep-includes");*/ |
| scratch.overwriteFile("tools/cpp/grep_includes/grep-includes.sh"); |
| scratch.appendFile( |
| "tools/cpp/grep_includes/BUILD", |
| "sh_binary(", |
| " name = 'grep-includes',", |
| " srcs = ['grep-includes.sh'],", |
| ")"); |
| |
| String extensionDirectory = "tools/build_defs"; |
| if (internalApi) { |
| extensionDirectory = "bazel_internal/test_rules/cc"; |
| scratch.overwriteFile(extensionDirectory + "/BUILD", ""); |
| } |
| scratch.file( |
| extensionDirectory + "/extension.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _cc_bin_impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " dep_linking_contexts = []", |
| " for dep in ctx.attr.deps:", |
| " dep_linking_contexts.append(dep[CcInfo].linking_context)", |
| " objects = cc_common.create_compilation_outputs(objects=depset(ctx.files.objects),", |
| " pic_objects=depset(ctx.files.pic_objects))", |
| " foo_dict = {'string_variable': 'foo',", |
| " 'string_sequence_variable' : ['foo'],", |
| " 'string_depset_variable': depset(['foo'])}", |
| " linking_outputs = cc_common.link(", |
| " actions=ctx.actions,", |
| " feature_configuration=feature_configuration,", |
| " name = ctx.label.name,", |
| " cc_toolchain=toolchain,", |
| " " + Joiner.on(",\n ").join(additionalLines), |
| " )", |
| " return [", |
| " MyInfo(", |
| " library=linking_outputs.library_to_link,", |
| " executable=linking_outputs.executable", |
| " ),", |
| " ]", |
| "cc_bin = rule(", |
| " implementation = _cc_bin_impl,", |
| " attrs = {", |
| " 'objects': attr.label_list(allow_files=True),", |
| " 'pic_objects': attr.label_list(allow_files=True),", |
| " 'deps': attr.label_list(),", |
| " '_cc_toolchain': attr.label(default =", |
| " configuration_field(fragment = 'cpp', name = 'cc_toolchain')),", |
| " '_grep_includes': attr.label(", |
| " executable = True,", |
| " default = Label('//tools/cpp/grep_includes:grep-includes'),", |
| " cfg = 'exec'", |
| " ),", |
| " 'additional_outputs': attr.output_list(),", |
| " },", |
| fragments, |
| ")"); |
| } |
| |
| private static void setupTestTransitiveLink(Scratch scratch, String... additionalLines) |
| throws Exception { |
| createCcBinRule(scratch, /* internalApi= */ false, additionalLines); |
| scratch.file( |
| "foo/BUILD", |
| "load('//tools/build_defs:extension.bzl', 'cc_bin')", |
| "cc_library(", |
| " name = 'dep1',", |
| " srcs = ['dep1.cc'],", |
| " linkstamp = 'version.cc',", |
| " hdrs = ['dep1.h'],", |
| " includes = ['dep1/baz'],", |
| " defines = ['DEP1'],", |
| ")", |
| "cc_library(", |
| " name = 'dep2',", |
| " srcs = ['dep2.cc'],", |
| " hdrs = ['dep2.h'],", |
| " includes = ['dep2/qux'],", |
| " defines = ['DEP2'],", |
| ")", |
| "cc_bin(", |
| " name = 'bin',", |
| " objects = ['file.o'],", |
| " pic_objects = ['file.pic.o'],", |
| " deps = [':dep1', ':dep2'],", |
| ")"); |
| } |
| |
| private static void setupDirectHeaderExtractionSupport(Scratch scratch) throws Exception { |
| scratch.file( |
| "direct/cc_info_extractor.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _cc_info_extractor_impl(ctx):", |
| " compilation_context = ctx.attr.dep[CcInfo].compilation_context", |
| " return [MyInfo(", |
| " direct_headers = compilation_context.direct_headers,", |
| " direct_public_headers = compilation_context.direct_public_headers,", |
| " direct_private_headers = compilation_context.direct_private_headers,", |
| " direct_textual_headers = compilation_context.direct_textual_headers,", |
| " )]", |
| "cc_info_extractor = rule(", |
| " _cc_info_extractor_impl,", |
| " attrs = {", |
| " 'dep': attr.label(providers = [[CcInfo]]),", |
| " }", |
| ")"); |
| scratch.file( |
| "direct/BUILD", |
| "load('//direct:cc_info_extractor.bzl', 'cc_info_extractor')", |
| "cc_info_extractor(", |
| " name = 'foo',", |
| " dep = '//direct/libs:foo_lib',", |
| ")", |
| "cc_info_extractor(", |
| " name = 'bar',", |
| " dep = '//direct/libs:bar_lib',", |
| ")"); |
| } |
| |
| private static void setupCcLibraryDirectPropagationTestTargets(Scratch scratch) throws Exception { |
| scratch.file( |
| "direct/libs/BUILD", |
| "cc_library(", |
| " name = 'foo_lib',", |
| " srcs = ['foo.cc', 'foo_impl.h'],", |
| " hdrs = ['foo.h'],", |
| " textual_hdrs = ['foo.def'],", |
| ")", |
| "cc_library(", |
| " name = 'bar_lib',", |
| " hdrs = ['bar.h'],", |
| " textual_hdrs = ['bar.def'],", |
| " deps = [':foo_lib'],", |
| ")"); |
| } |
| |
| @Test |
| public void testCcLibraryPropagatesCcInfoWithDirectHeaders() throws Exception { |
| setupDirectHeaderExtractionSupport(scratch); |
| setupCcLibraryDirectPropagationTestTargets(scratch); |
| |
| ConfiguredTarget fooTarget = getConfiguredTarget("//direct:foo"); |
| Iterable<Artifact> fooDirectHeaders = getArtifactsFromMyInfo(fooTarget, "direct_headers"); |
| assertThat(baseArtifactNames(fooDirectHeaders)).containsExactly("foo.h", "foo_impl.h"); |
| |
| Iterable<Artifact> fooDirectPublicHeaders = |
| getArtifactsFromMyInfo(fooTarget, "direct_public_headers"); |
| assertThat(baseArtifactNames(fooDirectPublicHeaders)).containsExactly("foo.h"); |
| |
| Iterable<Artifact> fooDirectPrivateHeaders = |
| getArtifactsFromMyInfo(fooTarget, "direct_private_headers"); |
| assertThat(baseArtifactNames(fooDirectPrivateHeaders)).containsExactly("foo_impl.h"); |
| |
| ConfiguredTarget barTarget = getConfiguredTarget("//direct:bar"); |
| Iterable<Artifact> barDirectHeaders = getArtifactsFromMyInfo(barTarget, "direct_headers"); |
| assertThat(baseArtifactNames(barDirectHeaders)).containsExactly("bar.h"); |
| |
| Iterable<Artifact> barDirectPublicHeaders = |
| getArtifactsFromMyInfo(barTarget, "direct_public_headers"); |
| assertThat(baseArtifactNames(barDirectPublicHeaders)).containsExactly("bar.h"); |
| |
| Iterable<Artifact> barDirectPrivateHeaders = |
| getArtifactsFromMyInfo(barTarget, "direct_private_headers"); |
| assertThat(barDirectPrivateHeaders).isEmpty(); |
| } |
| |
| @Test |
| public void testCcLibraryPropagatesCcInfoWithDirectTextualHeaders() throws Exception { |
| setupDirectHeaderExtractionSupport(scratch); |
| setupCcLibraryDirectPropagationTestTargets(scratch); |
| |
| ConfiguredTarget fooTarget = getConfiguredTarget("//direct:foo"); |
| Iterable<Artifact> fooDirectTextualHeaders = |
| getArtifactsFromMyInfo(fooTarget, "direct_textual_headers"); |
| assertThat(baseArtifactNames(fooDirectTextualHeaders)).containsExactly("foo.def"); |
| |
| ConfiguredTarget barTarget = getConfiguredTarget("//direct:bar"); |
| Iterable<Artifact> barDirectTextualHeaders = |
| getArtifactsFromMyInfo(barTarget, "direct_textual_headers"); |
| assertThat(baseArtifactNames(barDirectTextualHeaders)).containsExactly("bar.def"); |
| } |
| |
| /** Fixes #10580 */ |
| @Test |
| public void testMixedLinkerInputsWithOwnerAndWithout() throws Exception { |
| setBuildLanguageOptions("--noincompatible_require_linker_input_cc_api"); |
| setUpCcLinkingContextTest(false); |
| scratch.file("foo/BUILD", "load(':rule.bzl', 'crule')", "crule(name='a')"); |
| scratch.file( |
| "foo/rule.bzl", |
| "load('//myinfo:myinfo.bzl', 'MyInfo')", |
| "def _impl(ctx):", |
| " linker_input = cc_common.create_linker_input(owner=ctx.label, user_link_flags=['-l'])", |
| " linking_context = cc_common.create_linking_context(", |
| " linker_inputs=depset([linker_input]))", |
| " linking_context = cc_common.create_linking_context(", |
| " libraries_to_link=[],)", |
| " cc_info = CcInfo(linking_context=linking_context)", |
| " if cc_info.linking_context.linker_inputs.to_list()[0] == linker_input:", |
| " pass", |
| " return [cc_info]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| assertThat(getConfiguredTarget("//foo:a")).isNotNull(); |
| assertNoEvents(); |
| } |
| |
| @Test |
| public void testMergeCcInfosWithDirects() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder().withFeatures(MockCcSupport.HEADER_MODULES_FEATURES)); |
| |
| scratch.file( |
| "direct/cc_merger.bzl", |
| "def _cc_merger_impl(ctx):", |
| " direct_cc_infos = [dep[CcInfo] for dep in ctx.attr.exports]", |
| " cc_infos = [dep[CcInfo] for dep in ctx.attr.deps]", |
| " return [cc_common.merge_cc_infos(", |
| " direct_cc_infos = direct_cc_infos,", |
| " cc_infos = cc_infos,", |
| " )]", |
| "cc_merger = rule(", |
| " _cc_merger_impl,", |
| " attrs = {", |
| " 'deps': attr.label_list(providers = [[CcInfo]]),", |
| " 'exports': attr.label_list(providers = [[CcInfo]]),", |
| " }", |
| ")"); |
| scratch.file( |
| "direct/BUILD", |
| "load('//direct:cc_merger.bzl', 'cc_merger')", |
| "cc_library(", |
| " name = 'public1',", |
| " srcs = ['public1.cc', 'public1_impl.h'],", |
| " hdrs = ['public1.h'],", |
| " textual_hdrs = ['public1.inc'],", |
| ")", |
| "cc_library(", |
| " name = 'public2',", |
| " srcs = ['public2.cc', 'public2_impl.h'],", |
| " hdrs = ['public2.h'],", |
| " textual_hdrs = ['public2.inc'],", |
| ")", |
| "cc_library(", |
| " name = 'private',", |
| " srcs = ['private.cc', 'private_impl.h'],", |
| " hdrs = ['private.h'],", |
| " textual_hdrs = ['private.inc'],", |
| ")", |
| "cc_merger(", |
| " name = 'merge',", |
| " exports = [':public1', ':public2'],", |
| " deps = [':private'],", |
| ")"); |
| |
| ConfiguredTarget lib = getConfiguredTarget("//direct:merge"); |
| CcCompilationContext ccCompilationContext = lib.get(CcInfo.PROVIDER).getCcCompilationContext(); |
| assertThat( |
| baseArtifactNames( |
| ccCompilationContext.getExportingModuleMaps().stream() |
| .map(CppModuleMap::getArtifact) |
| .collect(ImmutableList.toImmutableList()))) |
| .containsExactly("public1.cppmap", "public2.cppmap"); |
| assertThat(baseArtifactNames(ccCompilationContext.getDirectPublicHdrs())) |
| .containsExactly("public1.h", "public2.h"); |
| assertThat(baseArtifactNames(ccCompilationContext.getDirectPrivateHdrs())) |
| .containsExactly("public1_impl.h", "public2_impl.h"); |
| assertThat(baseArtifactNames(ccCompilationContext.getTextualHdrs())) |
| .containsExactly("public1.inc", "public2.inc"); |
| } |
| |
| @Test |
| public void testMergeCompilationContexts() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder().withFeatures(MockCcSupport.HEADER_MODULES_FEATURES)); |
| |
| scratch.file( |
| "direct/cc_merger.bzl", |
| "def _cc_merger_impl(ctx):", |
| " compilation_contexts = [dep[CcInfo].compilation_context for dep in ctx.attr.deps]", |
| " merged_context = cc_common.merge_compilation_contexts(", |
| " compilation_contexts = compilation_contexts,", |
| " )", |
| " return [CcInfo(compilation_context = merged_context)]", |
| "cc_merger = rule(", |
| " _cc_merger_impl,", |
| " attrs = {", |
| " 'deps': attr.label_list(providers = [[CcInfo]]),", |
| " }", |
| ")"); |
| scratch.file( |
| "direct/BUILD", |
| "load('//direct:cc_merger.bzl', 'cc_merger')", |
| "cc_library(", |
| " name = 'public1',", |
| " srcs = ['public1.cc', 'public1_impl.h'],", |
| " hdrs = ['public1.h'],", |
| " textual_hdrs = ['public1.inc'],", |
| ")", |
| "cc_library(", |
| " name = 'public2',", |
| " srcs = ['public2.cc', 'public2_impl.h'],", |
| " hdrs = ['public2.h'],", |
| " textual_hdrs = ['public2.inc'],", |
| ")", |
| "cc_merger(", |
| " name = 'merge',", |
| " deps = [':public1', ':public2'],", |
| ")"); |
| |
| ConfiguredTarget lib = getConfiguredTarget("//direct:merge"); |
| CcCompilationContext ccCompilationContext = lib.get(CcInfo.PROVIDER).getCcCompilationContext(); |
| assertThat( |
| baseArtifactNames( |
| ccCompilationContext.getExportingModuleMaps().stream() |
| .map(CppModuleMap::getArtifact) |
| .collect(ImmutableList.toImmutableList()))) |
| .containsExactly("public1.cppmap", "public2.cppmap"); |
| assertThat(baseArtifactNames(ccCompilationContext.getDirectPublicHdrs())) |
| .containsExactly("public1.h", "public2.h"); |
| assertThat(baseArtifactNames(ccCompilationContext.getDirectPrivateHdrs())) |
| .containsExactly("public1_impl.h", "public2_impl.h"); |
| assertThat(baseArtifactNames(ccCompilationContext.getTextualHdrs())) |
| .containsExactly("public1.inc", "public2.inc"); |
| } |
| |
| @Test |
| public void testObjectFilesInCreateLibraryToLinkApiGuardedByFlag() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, CcToolchainConfig.builder().withFeatures("foo_feature")); |
| useConfiguration("--noexperimental_starlark_cc_import"); |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| |
| scratch.file( |
| "a/rule.bzl", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " requested_features = ['foo_feature'])", |
| " cc_common.create_library_to_link(", |
| " actions=ctx.actions, feature_configuration=feature_configuration, ", |
| " cc_toolchain = toolchain, objects = [])", |
| " return CcInfo()", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp']", |
| ")"); |
| checkError("//a:r", "Cannot use objects/pic_objects without --experimental_starlark_cc_import"); |
| } |
| |
| @Test |
| public void testObjectFilesInCreateLibrary() throws Exception { |
| setUpCcLinkingContextTest(true); |
| scratch.file( |
| "b/BUILD", |
| "load('//tools/build_defs/cc:rule.bzl', 'crule')", |
| "crule(name='import_objects',", |
| " static_library = 'lib.a',", |
| " pic_static_library = 'lib.pic.a',", |
| " objects = ['object.o'],", |
| " pic_objects = ['object.pic.o'],", |
| ")"); |
| |
| assertNoEvents(); |
| ConfiguredTarget lib = getConfiguredTarget("//b:import_objects"); |
| CcLinkingContext ccLinkingContext = lib.get(CcInfo.PROVIDER).getCcLinkingContext(); |
| ImmutableList<LibraryToLink> libraries = |
| ccLinkingContext.getLinkerInputs().toList().stream() |
| .flatMap(i -> i.getLibraries().stream()) |
| .collect(ImmutableList.toImmutableList()); |
| |
| assertThat( |
| baseArtifactNames( |
| libraries.stream() |
| .flatMap(l -> l.getObjectFiles().stream()) |
| .collect(ImmutableList.toImmutableList()))) |
| .containsExactly("object.o"); |
| assertThat( |
| baseArtifactNames( |
| libraries.stream() |
| .flatMap(l -> l.getPicObjectFiles().stream()) |
| .collect(ImmutableList.toImmutableList()))) |
| .containsExactly("object.pic.o"); |
| } |
| |
| private void setupDebugPackageProviderTest(String fission) throws Exception { |
| getAnalysisMock() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder().withFeatures(CppRuleClasses.PER_OBJECT_DEBUG_INFO)); |
| useConfiguration(fission); |
| scratch.file( |
| "a/rule.bzl", |
| "def _impl(ctx):", |
| " out = ctx.actions.declare_file(ctx.label.name)", |
| " ctx.actions.run_shell(", |
| " inputs = [ctx.executable.cc_binary],", |
| " tools = [],", |
| " outputs = [out],", |
| " command = 'cp %s %s' % (ctx.executable.cc_binary.path, out.path),", |
| " )", |
| " wrapped_defaultinfo = ctx.attr.cc_binary[DefaultInfo]", |
| " runfiles = ctx.runfiles(files = [out])", |
| " wrapped_default_runfiles = wrapped_defaultinfo.default_runfiles.files.to_list()", |
| " if ctx.executable.cc_binary in wrapped_default_runfiles:", |
| " wrapped_default_runfiles.remove(ctx.executable.cc_binary)", |
| " result = [", |
| " DefaultInfo(", |
| " executable = out,", |
| " files = depset([out]),", |
| " runfiles = runfiles.merge(ctx.runfiles(files = wrapped_default_runfiles)),", |
| " ),", |
| " ]", |
| " if ctx.file.stripped_file:", |
| " wrapped_dbginfo = ctx.attr.cc_binary[DebugPackageInfo]", |
| " result.append(", |
| " DebugPackageInfo(", |
| " target_label = ctx.label,", |
| " stripped_file = ctx.file.stripped_file \\", |
| " if wrapped_dbginfo.stripped_file else None,", |
| " unstripped_file = out,", |
| " dwp_file = ctx.file.dwp_file if wrapped_dbginfo.dwp_file else None,", |
| " ),", |
| " )", |
| " return result", |
| "wrapped_binary = rule(", |
| " _impl,", |
| " attrs = {", |
| " 'cc_binary': attr.label(", |
| " allow_single_file = True,", |
| " mandatory = True,", |
| " executable = True,", |
| " cfg = 'target',", |
| " ),", |
| " 'stripped_file': attr.label(", |
| " allow_single_file = True,", |
| " default = None,", |
| " ),", |
| " 'dwp_file': attr.label(", |
| " allow_single_file = True,", |
| " default = None,", |
| " )", |
| " },", |
| " executable = True,", |
| ")"); |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'wrapped_binary')", |
| "wrapped_binary(name = 'w',", |
| " cc_binary = ':native_binary',", |
| " stripped_file = ':w.stripped',", |
| " dwp_file = ':w.dwp',", |
| ")", |
| "wrapped_binary(name = 'w.stripped',", |
| " cc_binary = ':native_binary.stripped'", |
| ")", |
| "wrapped_binary(name = 'w.dwp',", |
| " cc_binary = ':native_binary.dwp'", |
| ")", |
| "cc_binary(name = 'native_binary',", |
| " srcs = ['main.cc']", |
| ")"); |
| scratch.file("a/main.cc", "int main() {}"); |
| } |
| |
| @Test |
| public void testDebugPackageProviderFissionDisabled() throws Exception { |
| setupDebugPackageProviderTest("--fission=no"); |
| ConfiguredTarget target = getConfiguredTarget("//a:w"); |
| assertNoEvents(); |
| assertThat(target).isNotNull(); |
| DebugPackageProvider debugPackageProvider = target.get(DebugPackageProvider.PROVIDER); |
| assertThat(debugPackageProvider.getStrippedArtifact().getFilename()).isEqualTo("w.stripped"); |
| assertThat(debugPackageProvider.getUnstrippedArtifact().getFilename()).isEqualTo("w"); |
| assertThat(debugPackageProvider.getDwpArtifact()).isNull(); |
| } |
| |
| @Test |
| public void testDebugPackageProviderFissionEnabled() throws Exception { |
| setupDebugPackageProviderTest("--fission=yes"); |
| ConfiguredTarget target = getConfiguredTarget("//a:w"); |
| assertNoEvents(); |
| assertThat(target).isNotNull(); |
| DebugPackageProvider debugPackageProvider = target.get(DebugPackageProvider.PROVIDER); |
| assertThat(debugPackageProvider.getStrippedArtifact().getFilename()).isEqualTo("w.stripped"); |
| assertThat(debugPackageProvider.getUnstrippedArtifact().getFilename()).isEqualTo("w"); |
| assertThat(debugPackageProvider.getDwpArtifact().getFilename()).isEqualTo("w.dwp"); |
| } |
| |
| @Test |
| public void testCcDebugContextDisabled() throws Exception { |
| scratch.file( |
| "b/BUILD", |
| "load('//my_rules:rule.bzl', 'cc_compile_rule')", |
| "cc_compile_rule(", |
| " name='b_lib',", |
| " srcs = ['b_lib.cc'],", |
| ")"); |
| scratch.file("my_rules/BUILD"); |
| scratch.file( |
| "my_rules/rule.bzl", |
| "def _impl(ctx):", |
| " comp_context = cc_common.create_compilation_context()", |
| " comp_outputs = cc_common.create_compilation_outputs()", |
| " debug_info = cc_common.create_debug_context(comp_outputs)", |
| " return [CcInfo(compilation_context = comp_context, debug_info = debug_info)]", |
| "cc_compile_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {", |
| " 'srcs': attr.label_list(allow_files = ['.cc']),", |
| " },", |
| ")"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//b:b_lib")); |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testCcDebugContext() throws Exception { |
| useConfiguration("--fission=yes"); |
| scratch.file( |
| "b/BUILD", |
| "load('//bazel_internal/test_rules/cc:rule.bzl', 'cc_compile_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "cc_compile_rule(", |
| " name='b_lib',", |
| " srcs = ['b_lib.cc'],", |
| ")"); |
| scratch.file("bazel_internal/test_rules/cc/BUILD"); |
| scratch.file( |
| "bazel_internal/test_rules/cc/rule.bzl", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx=ctx,", |
| " cc_toolchain=toolchain,", |
| " requested_features=ctx.features + ['per_object_debug_info'],", |
| " unsupported_features=ctx.disabled_features)", |
| " (comp_context, comp_outputs) = cc_common.compile(", |
| " name = ctx.label.name,", |
| " actions = ctx.actions,", |
| " feature_configuration = feature_configuration,", |
| " cc_toolchain = toolchain,", |
| " srcs = ctx.files.srcs,", |
| " )", |
| " debug_info = cc_common.create_debug_context(comp_outputs)", |
| " return [CcInfo(compilation_context = comp_context, debug_context = debug_info)]", |
| "cc_compile_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {", |
| " '_cc_toolchain': attr.label(default=Label('//b:alias')),", |
| " 'srcs': attr.label_list(allow_files = ['.cc']),", |
| " },", |
| " fragments = ['cpp'],", |
| ")"); |
| ConfiguredTarget target = getConfiguredTarget("//b:b_lib"); |
| assertThat( |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcDebugInfoContext() |
| .getTransitiveDwoFiles() |
| .toList() |
| .stream() |
| .map(Artifact::getFilename)) |
| .containsExactly("b_lib.dwo"); |
| assertThat( |
| target.get(CcInfo.PROVIDER).getCcDebugInfoContext().getTransitivePicDwoFiles().toList()) |
| .isEmpty(); |
| } |
| |
| @Test |
| public void testExpandedToolchainApiBlocked() throws Exception { |
| List<String> toolchainCalls = |
| ImmutableList.of( |
| "toolchain.as_files()", |
| "toolchain.ar_files()", |
| "toolchain.objcopy_files()", |
| "toolchain.tool_path(tool='ld')", |
| "toolchain.solib_dir()", |
| "toolchain.linker_files()", |
| "toolchain.coverage_files()", |
| "toolchain.strip_files()"); |
| scratch.file( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r')"); |
| |
| for (String call : toolchainCalls) { |
| scratch.overwriteFile( |
| "a/rule.bzl", |
| "CruleInfo = provider()", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " " + call, |
| " return [CruleInfo()]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| invalidatePackages(); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//a:r")); |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| } |
| |
| @Test |
| public void testExpandedCppConfigurationApiBlocked() throws Exception { |
| List<String> cppConfigurationOptions = |
| ImmutableList.of( |
| "strip_opts()", |
| "incompatible_enable_cc_test_feature()", |
| "build_test_dwp()", |
| "grte_top()", |
| "experimental_cc_implementation_deps()", |
| "share_native_deps()", |
| "experimental_platform_cc_test()"); |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'cpp_config_rule')", |
| "cpp_config_rule(name = 'custom')"); |
| for (String option : cppConfigurationOptions) { |
| scratch.overwriteFile( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " ctx.fragments.cpp." + option, |
| " return []", |
| "cpp_config_rule = rule(", |
| " implementation = _impl,", |
| " fragments = [\"cpp\"],", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| } |
| |
| @Test |
| public void testExpandedCompileApiBlocked() throws Exception { |
| scratch.file( |
| "bazel_internal/test_rules/cc/BUILD", |
| "load(':module_map.bzl', 'module_map')", |
| "module_map(name='module_map', file = 'a_file.txt')"); |
| |
| scratch.file( |
| "bazel_internal/test_rules/cc/module_map.bzl", |
| "ModuleMapInfo = provider(fields=['module_map', 'file'])", |
| "def _impl(ctx):", |
| " module_map = cc_common.create_module_map(file=ctx.file.file,", |
| " umbrella_header=ctx.file.file, name='module')", |
| " return [ModuleMapInfo(module_map=module_map, file=ctx.file.file)]", |
| "module_map = rule(", |
| " _impl,", |
| " attrs = { ", |
| " 'file': attr.label(allow_single_file=True)", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| String compileCall = |
| "cc_common.compile(actions = ctx.actions, feature_configuration = feature_configuration," |
| + " name = 'name', cc_toolchain = toolchain, "; |
| List<String> calls = |
| ImmutableList.of( |
| "cc_common.create_module_map(file=file, umbrella_header=file, name='name')", |
| "module_map.file()", |
| "module_map.umbrella_header()", |
| compileCall + " module_map = module_map)", |
| compileCall + " additional_module_maps = [module_map])", |
| compileCall + "additional_exported_hdrs = [])", |
| compileCall + "propagate_module_map_to_compile_action = True)", |
| compileCall + "do_not_generate_module_map = True)", |
| compileCall + "code_coverage_enabled = True)", |
| compileCall + "separate_module_headers = [])", |
| compileCall + "hdrs_checking_mode = 'strict')"); |
| scratch.overwriteFile( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "crule(name='r', deps=['//bazel_internal/test_rules/cc:module_map'])"); |
| |
| for (String call : calls) { |
| scratch.overwriteFile( |
| "a/rule.bzl", |
| "load('//bazel_internal/test_rules/cc:module_map.bzl', 'ModuleMapInfo')", |
| "CruleInfo = provider()", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " module_map = ctx.attr.deps[0][ModuleMapInfo].module_map", |
| " file = ctx.attr.deps[0][ModuleMapInfo].file", |
| " " + call, |
| " return [CruleInfo()]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " 'deps': attr.label_list(),", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| initializeSkyframeExecutor(); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//a:r")); |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| } |
| |
| @Test |
| public void testExpandedCcCompilationContextApiBlocked() throws Exception { |
| scratch.file( |
| "b/BUILD", |
| "load('//my_rules:rule.bzl', 'method_rule', 'param_2_rule', 'additional_inputs_rule'," |
| + " 'transitive_modules_rule')", |
| "param_2_rule(", |
| " name = 'p2',", |
| ")", |
| "method_rule(", |
| " name = 'm',", |
| ")", |
| "additional_inputs_rule(", |
| " name = 'ai',", |
| ")", |
| "transitive_modules_rule(", |
| " name = 'tm',", |
| ")"); |
| scratch.file("my_rules/BUILD"); |
| scratch.file( |
| "my_rules/rule.bzl", |
| "def _m_impl(ctx):", |
| " comp_context = cc_common.create_compilation_context()", |
| " comp_context.transitive_compilation_prerequisites()", |
| " return [CcInfo(compilation_context = comp_context)]", |
| "def _p2_impl(ctx):", |
| " comp_context = cc_common.create_compilation_context(purpose = 'testing')", |
| " return [CcInfo(compilation_context = comp_context)]", |
| "def _additional_inputs_impl(ctx):", |
| " comp_context = cc_common.create_compilation_context()", |
| " comp_context.additional_inputs()", |
| "def _transitive_modules_impl(ctx):", |
| " comp_context = cc_common.create_compilation_context()", |
| " comp_context.transitive_modules(use_pic = True)", |
| "method_rule = rule(", |
| " implementation = _m_impl,", |
| ")", |
| "param_2_rule = rule(", |
| " implementation = _p2_impl)", |
| "additional_inputs_rule = rule(", |
| " implementation = _additional_inputs_impl)", |
| "transitive_modules_rule = rule(", |
| " implementation = _transitive_modules_impl", |
| ")"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//b:m")); |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//b:p2")); |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//b:ai")); |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//b:tm")); |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testExpandedLinkApiRaisesError() throws Exception { |
| scratch.file( |
| "b/BUILD", |
| "load('//b:rule.bzl', 'link_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "link_rule(name = 'foo')"); |
| String callFormatString = |
| "cc_common.link(name='test', actions=ctx.actions," |
| + "feature_configuration=feature_configuration, cc_toolchain=toolchain, %s)"; |
| ImmutableList<String> calls = |
| ImmutableList.of( |
| String.format(callFormatString, "link_artifact_name_suffix='test'"), |
| String.format(callFormatString, "never_link=False"), |
| String.format(callFormatString, "test_only_target=False"), |
| String.format(callFormatString, "always_link=False"), |
| String.format(callFormatString, "additional_linkstamp_defines=[]"), |
| String.format(callFormatString, "whole_archive=False"), |
| String.format(callFormatString, "native_deps=False"), |
| String.format(callFormatString, "only_for_dynamic_libs=False")); |
| for (String call : calls) { |
| scratch.overwriteFile( |
| "b/rule.bzl", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " " + call, |
| " return [DefaultInfo()]", |
| "link_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {", |
| " '_cc_toolchain': attr.label(default=Label('//b:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ")"); |
| initializeSkyframeExecutor(); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//b:foo")); |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| } |
| |
| @Test |
| public void testExpandedCcCompilationOutputsApiRaisesError() throws Exception { |
| scratch.file("b/BUILD", "load('//b:rule.bzl', 'cc_rule')", "cc_rule(", " name='foo',", ")"); |
| ImmutableList<String> calls = |
| ImmutableList.of( |
| "comp_outputs.temps()", |
| "comp_outputs.files_to_compile(parse_headers=False, use_pic=True)", |
| "comp_outputs.header_tokens()"); |
| for (String call : calls) { |
| scratch.overwriteFile( |
| "b/rule.bzl", |
| "def _impl(ctx):", |
| " comp_outputs = cc_common.create_compilation_outputs()", |
| " " + call, |
| " return [DefaultInfo()]", |
| "cc_rule = rule(", |
| " implementation = _impl,", |
| ")"); |
| invalidatePackages(); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//b:foo")); |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| } |
| |
| @Test |
| public void testExpandedLtoAndFdoApiRaisesError() throws Exception { |
| useConfiguration("--fdo_optimize=/pkg/profile.afdo", "--compilation_mode=opt"); |
| scratch.file( |
| "bazel_internal/test_rules/cc/BUILD", |
| "load(':lto_backend_artifacts.bzl', 'lto_backend_artifacts')", |
| "lto_backend_artifacts(name='lto_backend_artifacts', file = 'a_file.txt')"); |
| |
| scratch.file( |
| "bazel_internal/test_rules/cc/lto_backend_artifacts.bzl", |
| "LtoBackendArtifactsInfo = provider(fields=['lto_backend_artifacts',", |
| " 'fdo_context', 'branch_fdo_profile'])", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " fdo_context = toolchain.fdo_context()", |
| " branch_fdo_profile = fdo_context.branch_fdo_profile()", |
| " lto_backend_artifacts = cc_common.create_lto_backend_artifacts(ctx=ctx,", |
| " lto_output_root_prefix=ctx.label.package, bitcode_file=ctx.file.file,", |
| " feature_configuration=feature_configuration, cc_toolchain=toolchain,", |
| " fdo_context=fdo_context, use_pic=True,", |
| " should_create_per_object_debug_info=False, argv=[])", |
| " return [LtoBackendArtifactsInfo(lto_backend_artifacts=lto_backend_artifacts,", |
| " fdo_context=fdo_context, branch_fdo_profile=branch_fdo_profile)]", |
| "lto_backend_artifacts = rule(", |
| " _impl,", |
| " attrs = { ", |
| " 'file': attr.label(allow_single_file=True),", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| |
| ImmutableList<String> calls = |
| ImmutableList.of( |
| "toolchain.fdo_context()", |
| "library_to_link.shared_non_lto_backends()", |
| "library_to_link.pic_shared_non_lto_backends()", |
| "lto_backend_artifacts_info.lto_backend_artifacts.object_file()", |
| "lto_backend_artifacts_info.fdo_context.branch_fdo_profile()", |
| "lto_backend_artifacts_info.branch_fdo_profile.auto_fdo()", |
| "lto_backend_artifacts_info.branch_fdo_profile.auto_xbinary_fdo()", |
| "lto_backend_artifacts_info.branch_fdo_profile.llvm_fdo()", |
| "lto_backend_artifacts_info.branch_fdo_profile.llvm_cs_fdo()"); |
| scratch.overwriteFile( |
| "a/BUILD", |
| "load(':rule.bzl', 'crule')", |
| "cc_toolchain_alias(name='alias')", |
| "cc_library(name='clib', srcs=['clib.cc'], hdrs=['clib.h'])", |
| "crule(name='r', lto_dep='//bazel_internal/test_rules/cc:lto_backend_artifacts',", |
| " cc_dep=':clib')"); |
| |
| for (String call : calls) { |
| scratch.overwriteFile( |
| "a/rule.bzl", |
| "load('//bazel_internal/test_rules/cc:lto_backend_artifacts.bzl',", |
| " 'LtoBackendArtifactsInfo')", |
| "CruleInfo = provider()", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " library_to_link = (ctx.attr.cc_dep[CcInfo].linking_context", |
| " .linker_inputs.to_list()[0].libraries[0])", |
| " lto_backend_artifacts_info = ctx.attr.lto_dep[LtoBackendArtifactsInfo]", |
| " " + call, |
| " return [CruleInfo()]", |
| "crule = rule(", |
| " _impl,", |
| " attrs = { ", |
| " 'lto_dep': attr.label(),", |
| " 'cc_dep': attr.label(),", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| invalidatePackages(); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//a:r")); |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| } |
| |
| @Test |
| public void testExpandedLibraryToLinkApiRaisesError() throws Exception { |
| scratch.file( |
| "b/BUILD", |
| "load('//b:rule.bzl', 'cc_rule')", |
| "cc_library(name='cc_dep', srcs=['cc_dep.cc'])", |
| "cc_toolchain_alias(name='alias')", |
| "cc_rule(name='foo', cc_dep=':cc_dep')"); |
| ImmutableList<String> calls = |
| ImmutableList.of( |
| "cc_common.create_library_to_link(actions=ctx.actions," |
| + "feature_configuration=feature_configuration, cc_toolchain=toolchain," |
| + " must_keep_debug=False)", |
| "library_to_link.must_keep_debug()"); |
| for (String call : calls) { |
| scratch.overwriteFile( |
| "b/rule.bzl", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " library_to_link = (ctx.attr.cc_dep[CcInfo].linking_context", |
| " .linker_inputs.to_list()[0].libraries[0])", |
| " " + call, |
| " return [DefaultInfo()]", |
| "cc_rule = rule(", |
| " implementation = _impl,", |
| " attrs = { ", |
| " 'cc_dep': attr.label(),", |
| " '_cc_toolchain': attr.label(default=Label('//b:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ")"); |
| invalidatePackages(); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//b:foo")); |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| } |
| |
| @Test |
| public void testExpandedLinkstampApiRaisesError() throws Exception { |
| scratch.file( |
| "bazel_internal/test_rules/cc/BUILD", |
| "load(':linkstamps.bzl', 'linkstamps')", |
| "cc_library(name='cc_dep', srcs=['cc_dep.cc'], linkstamp='stamp.cc')", |
| "linkstamps(name='linkstamps', deps = [':cc_dep'])"); |
| |
| scratch.file( |
| "bazel_internal/test_rules/cc/linkstamps.bzl", |
| "LinkstampsInfo = provider(fields=['linkstamps'])", |
| "def _impl(ctx):", |
| " linkstamps = ctx.attr.deps[0][CcInfo].linking_context.linkstamps().to_list()", |
| " return [LinkstampsInfo(linkstamps=linkstamps)]", |
| "linkstamps = rule(", |
| " _impl,", |
| " attrs = { ", |
| " 'deps': attr.label_list(),", |
| " },", |
| ");"); |
| |
| scratch.file( |
| "b/BUILD", |
| "load('//b:rule.bzl', 'cc_rule')", |
| "cc_library(name='cc_dep', srcs=['cc_dep.cc'])", |
| "cc_toolchain_alias(name='alias')", |
| "cc_rule(name='foo', cc_dep=':cc_dep',", |
| " file='file.cc',", |
| " linkstamps_dep='//bazel_internal/test_rules/cc:linkstamps')"); |
| scratch.appendFile( |
| "tools/cpp/grep_includes/BUILD", |
| "sh_binary(", |
| " name = 'grep-includes',", |
| " srcs = ['grep-includes.sh'],", |
| ")"); |
| List<String> calls = |
| new ArrayList<>( |
| Arrays.asList("linkstamp.file()", "linkstamp.hdrs()", "linking_context.linkstamps()")); |
| if (!analysisMock.isThisBazel()) { |
| calls.add( |
| "cc_common.register_linkstamp_compile_action(actions=ctx.actions,cc_toolchain=toolchain," |
| + " feature_configuration=feature_configuration, " |
| + " grep_includes=ctx.executable._grep_includes, source_file=file, output_file=file," |
| + " compilation_inputs=depset([]), inputs_for_validation=depset([])," |
| + " label_replacement='', output_replacement='')"); |
| } |
| for (String call : calls) { |
| scratch.overwriteFile( |
| "b/rule.bzl", |
| "load('//bazel_internal/test_rules/cc:linkstamps.bzl',", |
| " 'LinkstampsInfo')", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " linkstamp = ctx.attr.linkstamps_dep[LinkstampsInfo].linkstamps[0]", |
| " linking_context = ctx.attr.cc_dep[CcInfo].linking_context", |
| " file = ctx.file.file", |
| " " + call, |
| " return [DefaultInfo()]", |
| "cc_rule = rule(", |
| " implementation = _impl,", |
| " attrs = { ", |
| " 'cc_dep': attr.label(),", |
| " 'linkstamps_dep': attr.label(),", |
| " '_cc_toolchain': attr.label(default=Label('//b:alias')),", |
| " 'file': attr.label(allow_single_file=True),", |
| " '_grep_includes': attr.label(", |
| " executable = True,", |
| " default = Label('//tools/cpp/grep_includes:grep-includes'),", |
| " cfg = 'exec'", |
| " ),", |
| " },", |
| " fragments = ['cpp'],", |
| ")"); |
| invalidatePackages(); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//b:foo")); |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| } |
| |
| @Test |
| public void testVariableExtensionCompileApi() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", "variables_extension = foo_dict"); |
| assertThat(getConfiguredTarget("//foo:bin")).isNotNull(); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| CppCompileAction action = |
| (CppCompileAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".o")); |
| |
| action.getCompileCommandLine().getVariables().getSequenceVariable("string_sequence_variable"); |
| action.getCompileCommandLine().getVariables().getStringVariable("string_variable"); |
| action.getCompileCommandLine().getVariables().getSequenceVariable("string_depset_variable"); |
| } |
| |
| @Test |
| public void testVariableExtensionLinkingContextApi() throws Exception { |
| createFilesForTestingLinking(scratch, "tools/build_defs/foo", "variables_extension = foo_dict"); |
| assertThat(getConfiguredTarget("//foo:bin")).isNotNull(); |
| ConfiguredTarget target = getConfiguredTarget("//foo:starlark_lib"); |
| CppLinkAction action = |
| (CppLinkAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".a")); |
| |
| action |
| .getLinkCommandLineForTesting() |
| .getBuildVariables() |
| .getSequenceVariable("string_sequence_variable"); |
| action.getLinkCommandLineForTesting().getBuildVariables().getStringVariable("string_variable"); |
| action |
| .getLinkCommandLineForTesting() |
| .getBuildVariables() |
| .getSequenceVariable("string_depset_variable"); |
| } |
| |
| @Test |
| public void testVariableExtensionLinkApi() throws Exception { |
| setupTestTransitiveLink( |
| scratch, "output_type = 'executable'", "variables_extension = foo_dict"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:bin"); |
| assertThat(target).isNotNull(); |
| CppLinkAction action = |
| (CppLinkAction) |
| getGeneratingAction((Artifact) getMyInfoFromTarget(target).getValue("executable")); |
| |
| action |
| .getLinkCommandLineForTesting() |
| .getBuildVariables() |
| .getSequenceVariable("string_sequence_variable"); |
| action.getLinkCommandLineForTesting().getBuildVariables().getStringVariable("string_variable"); |
| action |
| .getLinkCommandLineForTesting() |
| .getBuildVariables() |
| .getSequenceVariable("string_depset_variable"); |
| } |
| |
| @Test |
| public void testVariablesExtensionInvalidValueTypesThrowsError() throws Exception { |
| String rulePkg = "b"; |
| scratch.overwriteFile(rulePkg + "/BUILD", ""); |
| scratch.overwriteFile( |
| "b/BUILD", |
| "load('//" + rulePkg + ":rule.bzl', 'cc_rule')", |
| "cc_library(name='cc_dep', srcs=['cc_dep.cc'])", |
| "cc_toolchain_alias(name='alias')", |
| "cc_rule(name='foo', cc_dep=':cc_dep')"); |
| for (String value : ImmutableList.of("1", "[1]", "depset([1])")) { |
| scratch.overwriteFile( |
| rulePkg + "/rule.bzl", |
| getVariablesExtensionStarlarkRule("compile", "dict = {'variable': " + value + "}")); |
| invalidatePackages(); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//b:foo")); |
| assertThat(e).hasMessageThat().contains("Trying to build UserVariableExtension"); |
| } |
| } |
| |
| @Test |
| public void testAdditionalLinkingOutputsAppearAsOutputsOfLinkAction() throws Exception { |
| createCcBinRule( |
| scratch, /* internalApi= */ false, "additional_outputs=ctx.outputs.additional_outputs"); |
| scratch.file( |
| "foo/BUILD", |
| "load('//tools/build_defs:extension.bzl', 'cc_bin')", |
| "cc_bin(", |
| " name = 'bin',", |
| " objects = ['file.o'],", |
| " pic_objects = ['file.pic.o'],", |
| " additional_outputs = [':bin.map'],", |
| ")"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:bin"); |
| assertThat(target).isNotNull(); |
| CppLinkAction action = |
| (CppLinkAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".map")); |
| assertThat(artifactsToStrings(action.getOutputs())).contains("bin foo/bin.map"); |
| } |
| |
| private String getVariablesExtensionStarlarkRule(String call, String dictionaryEntries) { |
| return Joiner.on("\n") |
| .join( |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " requested_features = ctx.features,", |
| " unsupported_features = ctx.disabled_features,", |
| " )", |
| " " + dictionaryEntries, |
| " cc_common." + call + "(", |
| " actions = ctx.actions,", |
| " feature_configuration = feature_configuration,", |
| " cc_toolchain = toolchain,", |
| " name = ctx.label.name + '_aspect',", |
| " variables_extension = dict,", |
| " )", |
| " return [DefaultInfo()]", |
| "cc_rule = rule(", |
| " implementation = _impl,", |
| " attrs = { ", |
| " 'cc_dep': attr.label(),", |
| " '_cc_toolchain': attr.label(default=Label('//b:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ")"); |
| } |
| |
| |
| @Test |
| public void testMissingToolchainAndFeatureConfigurationRaisesErrorInCreateLibraryToLink() |
| throws Exception { |
| scratch.file( |
| "b/BUILD", |
| "load('//b:rule.bzl', 'cc_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "cc_rule(name='foo')"); |
| |
| runTestMissingToolchainAndFeatureConfigurationRaisesErrorInCreateLibraryToLink( |
| "None,", "feature_configuration,", "ctx.file._artifact,", "None,"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//b:foo")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("If you pass 'dynamic_library', you must also pass a 'cc_toolchain'"); |
| |
| runTestMissingToolchainAndFeatureConfigurationRaisesErrorInCreateLibraryToLink( |
| "toolchain,", "None,", "ctx.file._artifact,", "None,"); |
| e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//b:foo")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("If you pass 'dynamic_library', you must also pass a 'feature_configuration'"); |
| |
| runTestMissingToolchainAndFeatureConfigurationRaisesErrorInCreateLibraryToLink( |
| "None,", "feature_configuration,", "None,", "ctx.file._artifact,"); |
| e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//b:foo")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("If you pass 'interface_library', you must also pass a 'cc_toolchain'"); |
| |
| runTestMissingToolchainAndFeatureConfigurationRaisesErrorInCreateLibraryToLink( |
| "toolchain,", "None,", "None,", "ctx.file._artifact,"); |
| e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//b:foo")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("If you pass 'interface_library', you must also pass a 'feature_configuration'"); |
| } |
| |
| private void runTestMissingToolchainAndFeatureConfigurationRaisesErrorInCreateLibraryToLink( |
| String toolchain, String featureConfiguration, String dynamicLibrary, String interfaceLibrary) |
| throws Exception { |
| scratch.overwriteFile( |
| "b/rule.bzl", |
| "def _impl(ctx):", |
| " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(", |
| " ctx = ctx,", |
| " cc_toolchain = toolchain,", |
| " )", |
| " cc_common.create_library_to_link(", |
| " actions=ctx.actions, ", |
| " cc_toolchain = " + toolchain, |
| " feature_configuration = " + featureConfiguration, |
| " dynamic_library =" + dynamicLibrary, |
| " interface_library =" + interfaceLibrary, |
| " )", |
| " return [DefaultInfo()]", |
| "cc_rule = rule(", |
| " implementation = _impl,", |
| " attrs = { ", |
| " '_artifact': attr.label(allow_single_file=True, default=Label('//b:foo.soifso')),", |
| " '_cc_toolchain': attr.label(default=Label('//b:alias'))", |
| " },", |
| " fragments = ['cpp'],", |
| ")"); |
| invalidatePackages(); |
| } |
| |
| @Test |
| public void testCcInternalIsNotAccessibleFromOutsideBuiltins() throws Exception { |
| scratch.file("a/BUILD", "load(':rule.bzl', 'crule')", "crule(name='r')"); |
| |
| scratch.file( |
| "a/rule.bzl", |
| "def _impl(ctx):", |
| " cc_internal", |
| " return DefaultInfo()", |
| "crule = rule(", |
| " _impl,", |
| ")"); |
| |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//a:r")); |
| assertThat(e).hasMessageThat().contains("name 'cc_internal' is not defined"); |
| } |
| |
| @Test |
| public void testExtendedBuildConfigurationApiBlocked() throws Exception { |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'build_config_rule')", |
| "build_config_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " ctx.configuration.stamp_binaries()", |
| " return []", |
| "build_config_rule = rule(", |
| " implementation = _impl,", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("private API only for use in builtins"); |
| } |
| |
| @Test |
| public void testExtendedCcCompilationOutputsLtoCompilationContextBlocked() throws Exception { |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'compilation_outputs_rule')", |
| "compilation_outputs_rule(name = 'custom')"); |
| |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_common.create_compilation_outputs().lto_compilation_context()", |
| " return []", |
| "compilation_outputs_rule = rule(", |
| " implementation = _impl,", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testExtendedCcCompilationOutputsDwoFilesBlocked() throws Exception { |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'compilation_outputs_rule')", |
| "compilation_outputs_rule(name = 'custom')"); |
| |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_common.create_compilation_outputs().dwo_files()", |
| " return []", |
| "compilation_outputs_rule = rule(", |
| " implementation = _impl,", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testExtendedCcCompilationOutputsPicDwoFilesBlocked() throws Exception { |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'compilation_outputs_rule')", |
| "compilation_outputs_rule(name = 'custom')"); |
| |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_common.create_compilation_outputs().pic_dwo_files()", |
| " return []", |
| "compilation_outputs_rule = rule(", |
| " implementation = _impl,", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testExtendedCcLinkingContextApiBlocked() throws Exception { |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'cc_linking_context_rule')", |
| "cc_linking_context_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_common.create_linking_context(linker_inputs = depset()).extra_link_time_libraries()", |
| " return []", |
| "cc_linking_context_rule = rule(", |
| " implementation = _impl,", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testExtendedCcLinkingOutputsApiBlocked() throws Exception { |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'cc_linking_outputs_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "cc_linking_outputs_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(ctx = ctx, cc_toolchain =" |
| + " cc_toolchain)", |
| " cc_linking_outputs = cc_common.link(actions = ctx.actions, feature_configuration =" |
| + " feature_configuration, cc_toolchain = cc_toolchain, name = ctx.label.name)", |
| " cc_linking_outputs.all_lto_artifacts()", |
| " return []", |
| "cc_linking_outputs_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {'_cc_toolchain':" + " attr.label(default=Label('//foo:alias'))},", |
| " fragments = ['cpp'],", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testToolRequirementForActionIsNotAccessibleFromOutsideBuiltins() throws Exception { |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'custom_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "custom_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(ctx = ctx, cc_toolchain =" |
| + " cc_toolchain)", |
| " cc_common.get_tool_requirement_for_action(feature_configuration = feature_configuration," |
| + " action_name = 'test')", |
| " return []", |
| "custom_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {'_cc_toolchain':" + " attr.label(default=Label('//foo:alias'))},", |
| " fragments = ['cpp'],", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testCreateCompilationOutputsPrivateParameterIsNotAccessibleFromOutsideBuiltins() |
| throws Exception { |
| scratch.file( |
| "foo/BUILD", "load(':custom_rule.bzl', 'custom_rule')", "custom_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_common.create_compilation_outputs(objects = None, pic_objects = None," |
| + " lto_compilation_context = None)", |
| " return []", |
| "custom_rule = rule(", |
| " implementation = _impl,", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testGetCompileBuildVariablesStripOptsNotAcessibleFromOutsideBuiltins() |
| throws Exception { |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'custom_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "custom_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(ctx = ctx, cc_toolchain =" |
| + " cc_toolchain)", |
| " cc_common.create_compile_variables(cc_toolchain = cc_toolchain, feature_configuration =" |
| + " feature_configuration, strip_opts = [])", |
| " return []", |
| "custom_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {'_cc_toolchain':" + " attr.label(default=Label('//foo:alias'))},", |
| " fragments = ['cpp'],", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testGetCompileBuildVariablesInputFileNotAcessibleFromOutsideBuiltins() |
| throws Exception { |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'custom_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "custom_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(ctx = ctx, cc_toolchain =" |
| + " cc_toolchain)", |
| " cc_common.create_compile_variables(cc_toolchain = cc_toolchain, feature_configuration =" |
| + " feature_configuration, input_file = '')", |
| " return []", |
| "custom_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {'_cc_toolchain':" + " attr.label(default=Label('//foo:alias'))},", |
| " fragments = ['cpp'],", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testCreateLinkingContextFromCompilationOutputsStampNotAcessibleFromOutsideBuiltins() |
| throws Exception { |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'custom_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "custom_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(ctx = ctx, cc_toolchain =" |
| + " cc_toolchain)", |
| " cc_common.create_linking_context_from_compilation_outputs(actions = ctx.actions," |
| + " cc_toolchain = cc_toolchain, feature_configuration =" |
| + " feature_configuration,compilation_outputs = cc_common.create_compilation_outputs()," |
| + " name = 'test', stamp = 0)", |
| " return []", |
| "custom_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {'_cc_toolchain':" + " attr.label(default=Label('//foo:alias'))},", |
| " fragments = ['cpp'],", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testLinkUseTestOnlyFlagNotAcessibleFromOutsideBuiltins() throws Exception { |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'custom_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "custom_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(ctx = ctx, cc_toolchain =" |
| + " cc_toolchain)", |
| " cc_common.link(actions = ctx.actions, cc_toolchain = cc_toolchain, feature_configuration" |
| + " = feature_configuration,use_test_only_flags = True, name = 'test')", |
| " return []", |
| "custom_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {'_cc_toolchain':" + " attr.label(default=Label('//foo:alias'))},", |
| " fragments = ['cpp'],", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testLinkUsePdbFileNotAcessibleFromOutsideBuiltins() throws Exception { |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'custom_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "custom_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(ctx = ctx, cc_toolchain =" |
| + " cc_toolchain)", |
| " cc_common.link(actions = ctx.actions, cc_toolchain = cc_toolchain, feature_configuration" |
| + " = feature_configuration,pdb_file = None, name = 'test')", |
| " return []", |
| "custom_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {'_cc_toolchain':" + " attr.label(default=Label('//foo:alias'))},", |
| " fragments = ['cpp'],", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testLinkUseWinDefFileNotAcessibleFromOutsideBuiltins() throws Exception { |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'custom_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "custom_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " feature_configuration = cc_common.configure_features(ctx = ctx, cc_toolchain =" |
| + " cc_toolchain)", |
| " cc_common.link(actions = ctx.actions, cc_toolchain = cc_toolchain, feature_configuration" |
| + " = feature_configuration,win_def_file = None, name = 'test')", |
| " return []", |
| "custom_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {'_cc_toolchain':" + " attr.label(default=Label('//foo:alias'))},", |
| " fragments = ['cpp'],", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testCcToolchainRuntimeSysrootNotAccessibleFromOutsideBuiltins() throws Exception { |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'custom_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "custom_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " cc_toolchain.runtime_sysroot()", |
| " return [DefaultInfo()]", |
| "custom_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {'_cc_toolchain': attr.label(default=Label('//foo:alias'))},", |
| " fragments = ['cpp'],", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testCcToolchainCompileFilesNotAccessibleFromOutsideBuiltins() throws Exception { |
| scratch.file( |
| "foo/BUILD", |
| "load(':custom_rule.bzl', 'custom_rule')", |
| "cc_toolchain_alias(name='alias')", |
| "custom_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", |
| " cc_toolchain.compiler_files()", |
| " return [DefaultInfo()]", |
| "custom_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {'_cc_toolchain': attr.label(default=Label('//foo:alias'))},", |
| " fragments = ['cpp'],", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testGetBuildInfoArtifactsIsPrivateApi() throws Exception { |
| scratch.file( |
| "foo/BUILD", "load(':custom_rule.bzl', 'custom_rule')", "custom_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_common.get_build_info(ctx)", |
| " return []", |
| "custom_rule = rule(", |
| " implementation = _impl,", |
| ")"); |
| invalidatePackages(); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testBuildInfoArtifacts() throws Exception { |
| scratch.file( |
| "bazel_internal/test_rules/cc/rule.bzl", |
| "def _impl(ctx):", |
| " artifacts = cc_common.get_build_info(ctx)", |
| " return [DefaultInfo(files = depset(artifacts))]", |
| "build_info_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {'stamp': attr.int()},", |
| ")"); |
| scratch.file( |
| "bazel_internal/test_rules/cc/BUILD", |
| "load(':rule.bzl', 'build_info_rule')", |
| "build_info_rule(name = 'stamped', stamp = 1,)", |
| "build_info_rule(name = 'unstamped', stamp = 0,)"); |
| assertThat( |
| prettyArtifactNames( |
| getConfiguredTarget("//bazel_internal/test_rules/cc:stamped") |
| .getProvider(FileProvider.class) |
| .getFilesToBuild())) |
| .containsExactly("build-info-nonvolatile.h", "build-info-volatile.h"); |
| assertThat( |
| prettyArtifactNames( |
| getConfiguredTarget("//bazel_internal/test_rules/cc:unstamped") |
| .getProvider(FileProvider.class) |
| .getFilesToBuild())) |
| .containsExactly("build-info-redacted.h"); |
| } |
| |
| @Test |
| public void testCheckPrivateApiCanOnlyBeCalledFromCcCommonBzl() throws Exception { |
| scratch.file( |
| "foo/BUILD", // |
| "load(':custom_rule.bzl', 'custom_rule')", |
| "custom_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_common_internal_do_not_use.check_private_api(allowlist = [])", |
| " return []", |
| "custom_rule = rule(", |
| " implementation = _impl,", |
| ")"); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("name 'cc_common_internal_do_not_use' is not defined"); |
| } |
| |
| @Test |
| public void testCheckPrivateApiAllowlistBlocksPrivateParameter() throws Exception { |
| scratch.file( |
| "foo/BUILD", // |
| "load(':custom_rule.bzl', 'custom_rule')", |
| "custom_rule(name = 'custom')"); |
| scratch.file( |
| "foo/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_common.create_compilation_context(purpose = 'whatever')", |
| " return []", |
| "custom_rule = rule(", |
| " implementation = _impl,", |
| ")"); |
| |
| AssertionError e = |
| assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:custom")); |
| |
| assertThat(e).hasMessageThat().contains("cannot use private API"); |
| } |
| |
| @Test |
| public void testCheckPrivateApiAllowlistAllowsPrivateParameter() throws Exception { |
| scratch.overwriteFile( |
| "tools/build_defs/android/BUILD", |
| "load(':custom_rule.bzl', 'custom_rule')", |
| "custom_rule(name = 'custom')"); |
| scratch.overwriteFile( |
| "tools/build_defs/android/custom_rule.bzl", |
| "def _impl(ctx):", |
| " cc_common.create_compilation_context(purpose = 'whatever')", |
| " return []", |
| "custom_rule = rule(", |
| " implementation = _impl,", |
| ")"); |
| |
| getConfiguredTarget("//tools/build_defs/android:custom"); |
| } |
| |
| // grep_includes is not supported by Bazel. |
| @Test |
| public void testGrepIncludesIsSetToNullInsideCcToolchain() throws Exception { |
| if (!AnalysisMock.get().isThisBazel()) { |
| return; |
| } |
| scratch.file( |
| "foo/BUILD", |
| "load(':extension.bzl', 'cc_skylark_library')", |
| "cc_skylark_library(", |
| " name = 'skylark_lib',", |
| ")"); |
| scratch.file( |
| "foo/extension.bzl", |
| "def _cc_skylark_library_impl(ctx):", |
| " return [ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]]", |
| "cc_skylark_library = rule(", |
| " implementation = _cc_skylark_library_impl,", |
| " attrs = {", |
| " '_cc_toolchain': attr.label(default =", |
| " configuration_field(fragment = 'cpp', name = 'cc_toolchain')),", |
| " },", |
| " fragments = ['cpp'],", |
| ")"); |
| |
| ConfiguredTarget target = getConfiguredTarget("//foo:skylark_lib"); |
| CcToolchainProvider toolchainProvider = target.get(CcToolchainProvider.PROVIDER); |
| Artifact grepIncludes = toolchainProvider.getGrepIncludes(); |
| |
| assertThat(grepIncludes).isNull(); |
| } |
| } |