| // 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.devtools.build.lib.actions.util.ActionsTestUtil.baseArtifactNames; |
| import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows; |
| |
| import com.google.common.base.Joiner; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| 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.analysis.ConfiguredTarget; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; |
| 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.packages.Provider; |
| import com.google.devtools.build.lib.packages.SkylarkInfo; |
| import com.google.devtools.build.lib.packages.SkylarkProvider; |
| import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier; |
| 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.CcToolchainFeatures.ActionConfig; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.ArtifactNamePattern; |
| 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.syntax.Depset; |
| import com.google.devtools.build.lib.syntax.EvalException; |
| import com.google.devtools.build.lib.syntax.EvalUtils; |
| import com.google.devtools.build.lib.syntax.Sequence; |
| import com.google.devtools.build.lib.syntax.Starlark; |
| import com.google.devtools.build.lib.syntax.StarlarkList; |
| 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.Collection; |
| import java.util.List; |
| import java.util.Map; |
| 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} Skylark module. */ |
| @RunWith(JUnit4.class) |
| public class SkylarkCcCommonTest extends BuildViewTestCase { |
| |
| @Before |
| public void setSkylarkSemanticsOptions() throws Exception { |
| setSkylarkSemanticsOptions(SkylarkCcCommonTestHelper.CC_SKYLARK_WHITELIST_FLAG); |
| invalidatePackages(); |
| |
| scratch.file("myinfo/myinfo.bzl", "MyInfo = provider()"); |
| |
| scratch.file("myinfo/BUILD"); |
| } |
| |
| private static StructImpl getMyInfoFromTarget(ConfiguredTarget configuredTarget) |
| throws Exception { |
| Provider.Key key = |
| new SkylarkProvider.SkylarkKey( |
| Label.parseAbsolute("//myinfo:myinfo.bzl", ImmutableMap.of()), "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", Mode.TARGET)); |
| 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 SkylarkProvider.SkylarkKey( |
| Label.create(r.getLabel().getPackageIdentifier(), "rule.bzl"), "CruleInfo"); |
| SkylarkInfo cruleInfo = (SkylarkInfo) 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 = (SkylarkInfo) 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", Mode.TARGET)); |
| assertThat(staticRuntimeLib.getSet(Artifact.class)) |
| .isEqualTo(toolchain.getStaticRuntimeLibForTesting()); |
| assertThat(dynamicRuntimeLib.getSet(Artifact.class)) |
| .isEqualTo(toolchain.getDynamicRuntimeLibForTesting()); |
| } |
| |
| @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", Mode.TARGET)); |
| FeatureConfiguration featureConfiguration = |
| CcCommon.configureFeaturesOrThrowEvalException( |
| ImmutableSet.of(), |
| ImmutableSet.of(), |
| 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", Mode.TARGET)); |
| FeatureConfiguration featureConfiguration = |
| CcCommon.configureFeaturesOrThrowEvalException( |
| ImmutableSet.of(), |
| ImmutableSet.of(), |
| 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", Mode.TARGET)); |
| FeatureConfiguration featureConfiguration = |
| CcCommon.configureFeaturesOrThrowEvalException( |
| ImmutableSet.of(), |
| ImmutableSet.of(), |
| 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 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, |
| CcToolchainConfig.builder().withFeatures(CppRuleClasses.DO_NOT_SPLIT_LINKING_CMDLINE)); |
| 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,", |
| " )", |
| " 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]),", |
| " 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,", |
| " 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')),", |
| " '_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"); |
| |
| 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("expected a depset of 'File' but got '[]' for parameter 'headers'"); |
| } |
| |
| @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 parameter '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 { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder() |
| .withFeatures( |
| CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY, |
| CppRuleClasses.TARGETS_WINDOWS, |
| CppRuleClasses.SUPPORTS_DYNAMIC_LINKER)); |
| doTestCcLinkingContext( |
| /* experimentalCcSharedLibrary= */ false, |
| ImmutableList.of("a.a", "libdep2.a", "b.a", "c.a", "d.a", "libdep1.a"), |
| ImmutableList.of("a.pic.a", "b.pic.a", "c.pic.a", "e.pic.a"), |
| ImmutableList.of("a.so", "libdep2.so", "b.so", "e.so", "libdep1.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( |
| /* experimentalCcSharedLibrary= */ false, |
| ImmutableList.of("a.a", "b.a", "c.a", "d.a"), |
| ImmutableList.of("a.pic.a", "libdep2.a", "b.pic.a", "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( |
| /* experimentalCcSharedLibrary= */ true, |
| ImmutableList.of("a.a", "b.a", "c.a", "d.a"), |
| ImmutableList.of("a.pic.a", "libdep2.a", "b.pic.a", "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)); |
| setSkylarkSemanticsOptions("--incompatible_depset_for_libraries_to_link_getter"); |
| setUpCcLinkingContextTest(); |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| StructImpl info = ((StructImpl) getMyInfoFromTarget(a).getValue("info")); |
| |
| Depset librariesToLink = info.getValue("libraries_to_link", Depset.class); |
| assertThat( |
| librariesToLink.toCollection(LibraryToLink.class).stream() |
| .filter(x -> x.getStaticLibrary() != null) |
| .map(x -> x.getStaticLibrary().getFilename()) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactly("a.a", "b.a", "c.a", "d.a"); |
| assertThat( |
| librariesToLink.toCollection(LibraryToLink.class).stream() |
| .filter(x -> x.getPicStaticLibrary() != null) |
| .map(x -> x.getPicStaticLibrary().getFilename()) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactly("a.pic.a", "libdep2.a", "b.pic.a", "c.pic.a", "e.pic.a", "libdep1.a"); |
| assertThat( |
| librariesToLink.toCollection(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(); |
| 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", Mode.TARGET)); |
| StructImpl info = ((StructImpl) getMyInfoFromTarget(a).getValue("info")); |
| Depset librariesToLink = info.getValue("libraries_to_link", Depset.class); |
| assertThat( |
| librariesToLink.toCollection(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.toCollection(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(); |
| 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", Mode.TARGET)); |
| StructImpl info = ((StructImpl) getMyInfoFromTarget(a).getValue("info")); |
| Depset librariesToLink = info.getValue("libraries_to_link", Depset.class); |
| assertThat( |
| librariesToLink.toCollection(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.toCollection(LibraryToLink.class).stream() |
| .filter(x -> x.getInterfaceLibrary() != null) |
| .map( |
| x -> |
| x.getInterfaceLibrary() |
| .getRootRelativePath() |
| .relativeTo(toolchain.getSolibDirectory()) |
| .toString()) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactly("libcustom.ifso"); |
| } |
| |
| private void doTestCcLinkingContext( |
| boolean experimentalCcSharedLibrary, |
| List<String> staticLibraryList, |
| List<String> picStaticLibraryList, |
| List<String> dynamicLibraryList) |
| throws Exception { |
| useConfiguration("--features=-supports_interface_shared_libraries"); |
| setSkylarkSemanticsOptions("--incompatible_depset_for_libraries_to_link_getter"); |
| if (experimentalCcSharedLibrary) { |
| setUpCcLinkingContextTestForExperimentalCcSharedLibrary(); |
| } else { |
| setUpCcLinkingContextTest(); |
| } |
| 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.toCollection(Artifact.class).stream() |
| .map(x -> x.getFilename()) |
| .collect(ImmutableList.toImmutableList())) |
| .containsExactly("b.lds", "d.lds"); |
| Collection<LibraryToLink> librariesToLink = |
| info.getValue("libraries_to_link", Depset.class).toCollection(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() throws Exception { |
| 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.a',", |
| " pic_static_library = 'b.pic.a',", |
| " 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'],", |
| " 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')", |
| "top_linking_context_smoke = cc_common.create_linking_context(libraries_to_link=[],", |
| " user_link_flags=['-first_flag', '-second_flag'])", |
| "def _create(ctx, feature_configuration, static_library, pic_static_library," |
| + " dynamic_library,", |
| " interface_library, dynamic_library_symlink_path, interface_library_symlink_path," |
| + " alwayslink):", |
| " 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)", |
| "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)", |
| " linking_context = cc_common.create_linking_context(", |
| " libraries_to_link=[library_to_link],", |
| " user_link_flags=ctx.attr.user_link_flags,", |
| " additional_inputs=ctx.files.additional_inputs)", |
| " 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)", |
| " return [", |
| " MyInfo(", |
| " info = struct(", |
| " cc_info = merged_cc_info,", |
| " user_link_flags = merged_cc_info.linking_context.user_link_flags,", |
| " additional_inputs = merged_cc_info.linking_context.additional_inputs,", |
| " libraries_to_link = merged_cc_info.linking_context.libraries_to_link,", |
| " 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),", |
| " ),", |
| " 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(),", |
| " 'alwayslink': attr.bool(),", |
| " '_cc_toolchain': attr.label(default=Label('//a:alias')),", |
| " 'deps': attr.label_list(),", |
| " },", |
| " fragments = ['cpp'],", |
| ");"); |
| } |
| |
| // This test should replace the old linking context test when the old API is deprecated. |
| private void setUpCcLinkingContextTestForExperimentalCcSharedLibrary() throws Exception { |
| setSkylarkSemanticsOptions("--experimental_cc_shared_library"); |
| 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.a',", |
| " pic_static_library = 'b.pic.a',", |
| " 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'],", |
| " 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')", |
| "top_linking_context_smoke = cc_common.create_linking_context(linker_inputs=depset([]))", |
| "def _create(ctx, feature_configuration, static_library, pic_static_library,", |
| " dynamic_library, interface_library, alwayslink):", |
| " 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,", |
| " alwayslink=alwayslink)", |
| "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.alwayslink)", |
| " 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)", |
| " result_linker_input = merged_cc_info.linking_context.linker_inputs.to_list()[0]", |
| " user_link_flags= []", |
| " additional_inputs = []", |
| " libraries = []", |
| " for linker_input in merged_cc_info.linking_context.linker_inputs.to_list():", |
| " user_link_flags.extend(linker_input.user_link_flags)", |
| " additional_inputs.extend(linker_input.additional_inputs)", |
| " libraries.extend(linker_input.libraries)", |
| " return [", |
| " MyInfo(", |
| " info = struct(", |
| " cc_info = merged_cc_info,", |
| " user_link_flags = user_link_flags,", |
| " additional_inputs = depset(direct=additional_inputs),", |
| " libraries_to_link = depset(direct=libraries),", |
| " 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),", |
| " ),", |
| " 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),", |
| " 'interface_library': attr.label(allow_single_file=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"); |
| SkylarkInfo variable = (SkylarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| assertThat(variable).isNotNull(); |
| VariableWithValue v = CcModule.variableWithValueFromSkylark(variable); |
| assertThat(v).isNotNull(); |
| assertThat(v.variable).isEqualTo("abc"); |
| assertThat(v.value).isEqualTo("def"); |
| |
| createEnvEntryRule("six", /* key= */ "'abc'", /* value= */ "'def'"); |
| t = getConfiguredTarget("//six:a"); |
| SkylarkInfo envEntry = (SkylarkInfo) getMyInfoFromTarget(t).getValue("entry"); |
| EvalException ee = |
| assertThrows(EvalException.class, () -> CcModule.variableWithValueFromSkylark(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"); |
| SkylarkInfo variable = (SkylarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.variableWithValueFromSkylark(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"); |
| SkylarkInfo variable = (SkylarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.variableWithValueFromSkylark(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"); |
| SkylarkInfo variable = (SkylarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.variableWithValueFromSkylark(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"); |
| SkylarkInfo variable = (SkylarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.variableWithValueFromSkylark(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"); |
| SkylarkInfo entryProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("entry"); |
| assertThat(entryProvider).isNotNull(); |
| EnvEntry entry = CcModule.envEntryFromSkylark(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"); |
| SkylarkInfo variable = (SkylarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.envEntryFromSkylark(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"); |
| SkylarkInfo entry = (SkylarkInfo) getMyInfoFromTarget(t).getValue("entry"); |
| EvalException e = assertThrows(EvalException.class, () -> CcModule.envEntryFromSkylark(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"); |
| SkylarkInfo entry = (SkylarkInfo) getMyInfoFromTarget(t).getValue("entry"); |
| EvalException e = |
| assertThrows( |
| "Should have failed because of empty string.", |
| EvalException.class, |
| () -> CcModule.envEntryFromSkylark(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"); |
| SkylarkInfo entry = (SkylarkInfo) getMyInfoFromTarget(t).getValue("entry"); |
| EvalException e = assertThrows(EvalException.class, () -> CcModule.envEntryFromSkylark(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"); |
| SkylarkInfo entry = (SkylarkInfo) getMyInfoFromTarget(t).getValue("entry"); |
| EvalException e = assertThrows(EvalException.class, () -> CcModule.envEntryFromSkylark(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"); |
| SkylarkInfo toolPathProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("toolpath"); |
| assertThat(toolPathProvider).isNotNull(); |
| Pair<String, String> toolPath = CcModule.toolPathFromSkylark(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"); |
| SkylarkInfo variable = (SkylarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolPathFromSkylark(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"); |
| SkylarkInfo toolPath = (SkylarkInfo) getMyInfoFromTarget(t).getValue("toolpath"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolPathFromSkylark(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"); |
| SkylarkInfo toolPath = (SkylarkInfo) getMyInfoFromTarget(t).getValue("toolpath"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolPathFromSkylark(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"); |
| SkylarkInfo toolPath = (SkylarkInfo) getMyInfoFromTarget(t).getValue("toolpath"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolPathFromSkylark(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"); |
| SkylarkInfo toolPath = (SkylarkInfo) getMyInfoFromTarget(t).getValue("toolpath"); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.toolPathFromSkylark(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"); |
| SkylarkInfo makeVariableProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| assertThat(makeVariableProvider).isNotNull(); |
| Pair<String, String> makeVariable = CcModule.makeVariableFromSkylark(makeVariableProvider); |
| assertThat(makeVariable).isNotNull(); |
| assertThat(makeVariable.first).isEqualTo("abc"); |
| assertThat(makeVariable.second).isEqualTo("val"); |
| |
| createVariableWithValueRule("six", /* name= */ "'abc'", /* value= */ "'def'"); |
| t = getConfiguredTarget("//six:a"); |
| SkylarkInfo variable = (SkylarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException ee = |
| assertThrows(EvalException.class, () -> CcModule.makeVariableFromSkylark(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"); |
| SkylarkInfo makeVariableProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.makeVariableFromSkylark(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"); |
| SkylarkInfo makeVariableProvider = |
| (SkylarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.makeVariableFromSkylark(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"); |
| SkylarkInfo makeVariableProvider = |
| (SkylarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.makeVariableFromSkylark(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"); |
| SkylarkInfo makeVariableProvider = |
| (SkylarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.makeVariableFromSkylark(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"); |
| SkylarkInfo withFeatureSetProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("wfs"); |
| assertThat(withFeatureSetProvider).isNotNull(); |
| WithFeatureSet withFeatureSet = CcModule.withFeatureSetFromSkylark(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"); |
| SkylarkInfo variable = (SkylarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException ee = |
| assertThrows(EvalException.class, () -> CcModule.withFeatureSetFromSkylark(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"); |
| SkylarkInfo withFeatureSetProvider = |
| (SkylarkInfo) getMyInfoFromTarget(t).getValue("wfs"); |
| assertThat(withFeatureSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.withFeatureSetFromSkylark(withFeatureSetProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Illegal argument: 'features' is not of expected type list or NoneType"); |
| } |
| |
| @Test |
| public void testCustomWithFeatureSet_listOfString_struct() throws Exception { |
| createCustomWithFeatureSetRule("two", /* features= */ "['abc']", /* notFeatures= */ "struct()"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//two:a"); |
| SkylarkInfo withFeatureSetProvider = |
| (SkylarkInfo) getMyInfoFromTarget(t).getValue("wfs"); |
| assertThat(withFeatureSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.withFeatureSetFromSkylark(withFeatureSetProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Illegal argument: 'not_features' is not of expected type list or NoneType"); |
| } |
| |
| @Test |
| public void testCustomWithFeatureSet_listOfStruct_emptyList() throws Exception { |
| createCustomWithFeatureSetRule("three", /* features= */ "[struct()]", /* notFeatures= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| SkylarkInfo withFeatureSetProvider = |
| (SkylarkInfo) getMyInfoFromTarget(t).getValue("wfs"); |
| assertThat(withFeatureSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.withFeatureSetFromSkylark(withFeatureSetProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("expected type 'string' for 'features' element but got type 'struct' instead"); |
| } |
| |
| @Test |
| public void testCustomWithFeatureSet_emptyList_listOfStruct() throws Exception { |
| createCustomWithFeatureSetRule("four", /* features= */ "[]", /* notFeatures= */ "[struct()]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| SkylarkInfo withFeatureSetProvider = |
| (SkylarkInfo) getMyInfoFromTarget(t).getValue("wfs"); |
| assertThat(withFeatureSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.withFeatureSetFromSkylark(withFeatureSetProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains( |
| "expected type 'string' for 'not_features' element but got type 'struct' instead"); |
| } |
| |
| 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"); |
| SkylarkInfo envSetProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("envset"); |
| assertThat(envSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.envSetFromSkylark(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"); |
| SkylarkInfo envSetProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("envset"); |
| assertThat(envSetProvider).isNotNull(); |
| EnvSet envSet = CcModule.envSetFromSkylark(envSetProvider); |
| assertThat(envSet).isNotNull(); |
| } |
| |
| @Test |
| public void testEnvSet_string_string() throws Exception { |
| loadCcToolchainConfigLib(); |
| createVariableWithValueRule("eight", /* name= */ "'abc'", /* value= */ "'def'"); |
| ConfiguredTarget t = getConfiguredTarget("//eight:a"); |
| SkylarkInfo variable = (SkylarkInfo) getMyInfoFromTarget(t).getValue("variable"); |
| EvalException e = assertThrows(EvalException.class, () -> CcModule.envSetFromSkylark(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"); |
| SkylarkInfo envSetProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("envset"); |
| assertThat(envSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.envSetFromSkylark(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"); |
| SkylarkInfo envSetProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("envset"); |
| assertThat(envSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.envSetFromSkylark(envSetProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'env_entries' is not of expected type list or NoneType"); |
| } |
| |
| @Test |
| public void testCustomEnvSet_structList_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomEnvSetRule( |
| "three", |
| /* actions= */ "['a1']", |
| /* envEntries= */ "[struct()]", |
| /* withFeatures= */ "None"); |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| SkylarkInfo envSetProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("envset"); |
| assertThat(envSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.envSetFromSkylark(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"); |
| SkylarkInfo envSetProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("envset"); |
| assertThat(envSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.envSetFromSkylark(envSetProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'with_features' is not of expected type list or NoneType"); |
| } |
| |
| @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"); |
| SkylarkInfo envSetProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("envset"); |
| assertThat(envSetProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.envSetFromSkylark(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_notSkylarkInfo() throws Exception { |
| loadCcToolchainConfigLib(); |
| createFlagGroupRule( |
| "eight", |
| /* flags= */ "['a']", |
| /* flagGroups= */ "[]", |
| /* iterateOver= */ "'a'", |
| /* expandIfTrue= */ "'b'", |
| /* expandIfFalse= */ "''", |
| /* expandIfAvailable= */ "''", |
| /* expandIfNotAvailable= */ "''", |
| /* expandIfEqual= */ "'a'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//eight:a"); |
| SkylarkInfo flagGroupProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromSkylark(flagGroupProvider)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains( |
| "Field 'expand_if_equal' is not of " |
| + "'com.google.devtools.build.lib.packages.SkylarkInfo' 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"); |
| SkylarkInfo flagGroupProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| FlagGroup f = CcModule.flagGroupFromSkylark(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"); |
| SkylarkInfo flagGroupProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromSkylark(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"); |
| SkylarkInfo flagGroupProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| FlagGroup flagGroup = CcModule.flagGroupFromSkylark(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"); |
| SkylarkInfo flagGroupProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromSkylark(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"); |
| SkylarkInfo flagGroupProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromSkylark(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"); |
| SkylarkInfo flagGroupProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromSkylark(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"); |
| SkylarkInfo flagGroupProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromSkylark(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"); |
| SkylarkInfo flagGroupProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromSkylark(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"); |
| SkylarkInfo flagGroupProvider = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flaggroup"); |
| assertThat(flagGroupProvider).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.flagGroupFromSkylark(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"); |
| SkylarkInfo toolStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = assertThrows(EvalException.class, () -> CcModule.toolFromSkylark(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"); |
| SkylarkInfo toolStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = assertThrows(EvalException.class, () -> CcModule.toolFromSkylark(toolStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains( |
| "expected type 'string' for 'execution_requirements' " |
| + "element but got type 'struct' instead"); |
| } |
| |
| @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"); |
| SkylarkInfo toolStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| Tool tool = CcModule.toolFromSkylark(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"); |
| SkylarkInfo toolStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = assertThrows(EvalException.class, () -> CcModule.toolFromSkylark(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"); |
| SkylarkInfo toolStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = assertThrows(EvalException.class, () -> CcModule.toolFromSkylark(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"); |
| SkylarkInfo toolStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = assertThrows(EvalException.class, () -> CcModule.toolFromSkylark(toolStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Illegal argument: 'with_features' is not of expected type list or NoneType"); |
| } |
| |
| @Test |
| public void testCustomTool_withFeatures_mustBeWithFeatureSet() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomToolRule( |
| "four", |
| /* path= */ "'a'", |
| /* withFeatures= */ "[struct(val = 'a')]", |
| /* requirements= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| SkylarkInfo toolStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = assertThrows(EvalException.class, () -> CcModule.toolFromSkylark(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"); |
| SkylarkInfo toolStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = assertThrows(EvalException.class, () -> CcModule.toolFromSkylark(toolStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains( |
| "llegal argument: 'execution_requirements' is not of expected type list or NoneType"); |
| } |
| |
| @Test |
| public void testCustomTool_executionRequirements_mustBeString() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomToolRule( |
| "six", /* path= */ "'a'", /* withFeatures= */ "[]", /* requirements= */ "[struct()]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//six:a"); |
| SkylarkInfo toolStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("tool"); |
| assertThat(toolStruct).isNotNull(); |
| EvalException e = assertThrows(EvalException.class, () -> CcModule.toolFromSkylark(toolStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains( |
| "expected type 'string' for 'execution_requirements' " |
| + "element but got type 'struct' instead"); |
| } |
| |
| 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"); |
| SkylarkInfo flagSetStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.flagSetFromSkylark(flagSetStruct, /* actionName= */ null)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("expected type 'string' for 'actions' element but got type 'struct' instead"); |
| } |
| |
| @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"); |
| SkylarkInfo flagSetStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.flagSetFromSkylark(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"); |
| SkylarkInfo flagSetStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.flagSetFromSkylark(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"); |
| SkylarkInfo flagSetStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| FlagSet f = CcModule.flagSetFromSkylark(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"); |
| SkylarkInfo flagSetStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.flagSetFromSkylark(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"); |
| SkylarkInfo flagSetStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| FlagSet f = CcModule.flagSetFromSkylark(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"); |
| SkylarkInfo flagSet = (SkylarkInfo) getMyInfoFromTarget(target).getValue("flagset"); |
| assertThat(flagSet).isNotNull(); |
| FlagSet flagSetObject = CcModule.flagSetFromSkylark(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"); |
| SkylarkInfo flagSetStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.flagSetFromSkylark(flagSetStruct, /* actionName */ null)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Illegal argument: 'flag_groups' is not of expected type list or NoneType"); |
| } |
| |
| @Test |
| public void testCustomFlagSet_withFeatures_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFlagSetRule( |
| "three", /* actions= */ "['a']", /* flagGroups= */ "[]", /* withFeatures= */ "struct()"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//three:a"); |
| SkylarkInfo flagSetStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.flagSetFromSkylark(flagSetStruct, /* actionName */ null)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Illegal argument: 'with_features' is not of expected type list or NoneType"); |
| } |
| |
| @Test |
| public void testCustomFlagSet_actions_mustBeList() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomFlagSetRule( |
| "four", /* actions= */ "struct()", /* flagGroups= */ "[]", /* withFeatures= */ "[]"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//four:a"); |
| SkylarkInfo flagSetStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("flagset"); |
| assertThat(flagSetStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.flagSetFromSkylark(flagSetStruct, /* actionName */ null)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Illegal argument: 'actions' is not of expected type list or NoneType"); |
| } |
| |
| 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"); |
| SkylarkInfo actionConfigStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromSkylark(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"); |
| SkylarkInfo actionConfigStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromSkylark(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"); |
| SkylarkInfo actionConfigStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromSkylark(actionConfigStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("expected type 'string' for 'implies' element but got type 'struct' instead"); |
| } |
| |
| @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"); |
| SkylarkInfo actionConfigStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromSkylark(actionConfigStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("expected type 'string' for 'implies' element but got type 'struct' instead"); |
| } |
| |
| @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"); |
| SkylarkInfo actionConfigStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| ActionConfig a = CcModule.actionConfigFromSkylark(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"); |
| SkylarkInfo actionConfigStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromSkylark(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"); |
| SkylarkInfo actionConfigStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromSkylark(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"); |
| SkylarkInfo actionConfigStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromSkylark(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"); |
| SkylarkInfo actionConfigStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromSkylark(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"); |
| SkylarkInfo actionConfigStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromSkylark(actionConfigStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Illegal argument: 'tools' is not of expected type list or NoneType"); |
| } |
| |
| @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"); |
| SkylarkInfo actionConfigStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromSkylark(actionConfigStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Illegal argument: 'flag_sets' is not of expected type list or NoneType"); |
| } |
| |
| @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"); |
| SkylarkInfo actionConfigStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("config"); |
| assertThat(actionConfigStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, () -> CcModule.actionConfigFromSkylark(actionConfigStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Illegal argument: 'implies' is not of expected type list or NoneType"); |
| } |
| |
| 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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(featureStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("A feature must either have a nonempty 'name' field or be enabled."); |
| } |
| |
| @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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(featureStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("expected type 'string' for 'implies' element but got type 'struct' instead"); |
| } |
| |
| @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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(featureStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("expected type 'string' for 'provides' element but got type 'struct' instead"); |
| } |
| |
| @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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| Feature a = CcModule.featureFromSkylark(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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(featureStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Illegal argument: 'flag_sets' is not of expected type list or NoneType"); |
| } |
| |
| @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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(featureStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Illegal argument: 'env_sets' is not of expected type list or NoneType"); |
| } |
| |
| @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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(featureStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Illegal argument: 'requires' is not of expected type list or NoneType"); |
| } |
| |
| @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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(featureStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Illegal argument: 'implies' is not of expected type list or NoneType"); |
| } |
| |
| @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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(featureStruct)); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("Illegal argument: 'provides' is not of expected type list or NoneType"); |
| } |
| |
| @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"); |
| SkylarkInfo featureStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("f"); |
| assertThat(featureStruct).isNotNull(); |
| EvalException e = |
| assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(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"); |
| SkylarkInfo artifactNamePatternStruct = |
| (SkylarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.artifactNamePatternFromSkylark(artifactNamePatternStruct)); |
| 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"); |
| SkylarkInfo artifactNamePatternStruct = |
| (SkylarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.artifactNamePatternFromSkylark(artifactNamePatternStruct)); |
| 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"); |
| SkylarkInfo artifactNamePatternStruct = |
| (SkylarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.artifactNamePatternFromSkylark(artifactNamePatternStruct)); |
| 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"); |
| SkylarkInfo artifactNamePatternStruct = |
| (SkylarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.artifactNamePatternFromSkylark(artifactNamePatternStruct)); |
| 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"); |
| SkylarkInfo artifactNamePatternStruct = |
| (SkylarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| ArtifactNamePattern artifactNamePattern = |
| CcModule.artifactNamePatternFromSkylark(artifactNamePatternStruct); |
| assertThat(artifactNamePattern).isNotNull(); |
| } |
| |
| @Test |
| public void testCustomArtifactNamePattern_none_none() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomArtifactNamePatternRule( |
| "six", /* categoryName= */ "'executable'", /* extension= */ "None", /* prefix= */ "None"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//six:a"); |
| SkylarkInfo artifactNamePatternStruct = |
| (SkylarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| ArtifactNamePattern artifactNamePattern = |
| CcModule.artifactNamePatternFromSkylark(artifactNamePatternStruct); |
| assertThat(artifactNamePattern).isNotNull(); |
| } |
| |
| @Test |
| public void testCustomArtifactNamePattern_categoryName_unknown() throws Exception { |
| loadCcToolchainConfigLib(); |
| createCustomArtifactNamePatternRule( |
| "seven", /* categoryName= */ "'unknown'", /* extension= */ "'.a'", /* prefix= */ "'a'"); |
| |
| ConfiguredTarget t = getConfiguredTarget("//seven:a"); |
| SkylarkInfo artifactNamePatternStruct = |
| (SkylarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.artifactNamePatternFromSkylark(artifactNamePatternStruct)); |
| 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"); |
| SkylarkInfo artifactNamePatternStruct = |
| (SkylarkInfo) getMyInfoFromTarget(t).getValue("namepattern"); |
| assertThat(artifactNamePatternStruct).isNotNull(); |
| EvalException e = |
| assertThrows( |
| EvalException.class, |
| () -> CcModule.artifactNamePatternFromSkylark(artifactNamePatternStruct)); |
| 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 testCcToolchainInfoFromSkylark() 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 testCcToolchainInfoFromSkylarkRequiredToolchainIdentifier() throws Exception { |
| setupSkylarkRuleForStringFieldsTesting("toolchain_identifier"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:r")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("missing 1 required named argument: toolchain_identifier"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromSkylarkRequiredHostSystemName() throws Exception { |
| setupSkylarkRuleForStringFieldsTesting("host_system_name"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:r")); |
| assertThat(e).hasMessageThat().contains("missing 1 required named argument: host_system_name"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromSkylarkRequiredTargetSystemName() throws Exception { |
| setupSkylarkRuleForStringFieldsTesting("target_system_name"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:r")); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("missing 1 required named argument: target_system_name"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromSkylarkRequiredTargetCpu() throws Exception { |
| setupSkylarkRuleForStringFieldsTesting("target_cpu"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:r")); |
| assertThat(e).hasMessageThat().contains("missing 1 required named argument: target_cpu"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromSkylarkRequiredTargetLibc() throws Exception { |
| setupSkylarkRuleForStringFieldsTesting("target_libc"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:r")); |
| assertThat(e).hasMessageThat().contains("missing 1 required named argument: target_libc"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromSkylarkRequiredCompiler() throws Exception { |
| setupSkylarkRuleForStringFieldsTesting("compiler"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:r")); |
| assertThat(e).hasMessageThat().contains("missing 1 required named argument: compiler"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromSkylarkRequiredAbiVersion() throws Exception { |
| setupSkylarkRuleForStringFieldsTesting("abi_version"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:r")); |
| assertThat(e).hasMessageThat().contains("missing 1 required named argument: abi_version"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromSkylarkRequiredAbiLibcVersion() throws Exception { |
| setupSkylarkRuleForStringFieldsTesting("abi_libc_version"); |
| AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:r")); |
| assertThat(e).hasMessageThat().contains("missing 1 required named argument: abi_libc_version"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromSkylarkAllRequiredStringsPresent() throws Exception { |
| setupSkylarkRuleForStringFieldsTesting(""); |
| ConfiguredTarget target = getConfiguredTarget("//foo:r"); |
| assertThat(target).isNotNull(); |
| CcToolchainConfigInfo ccToolchainConfigInfo = |
| (CcToolchainConfigInfo) target.get(CcToolchainConfigInfo.PROVIDER.getKey()); |
| assertThat(ccToolchainConfigInfo).isNotNull(); |
| } |
| |
| private void setupSkylarkRuleForStringFieldsTesting(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 testCcToolchainInfoFromSkylarkNoLegacyFeatures() 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 -> feature.getName()) |
| .collect(ImmutableSet.toImmutableSet()); |
| ImmutableSet<String> actionConfigNames = |
| ccToolchainConfigInfo.getActionConfigs().stream() |
| .map(actionConfig -> actionConfig.getActionName()) |
| .collect(ImmutableSet.toImmutableSet()); |
| assertThat(featureNames).containsExactly("no_legacy_features", "custom_feature"); |
| assertThat(actionConfigNames).containsExactly("custom_action"); |
| } |
| |
| @Test |
| public void testCcToolchainInfoFromSkylarkWithLegacyFeatures() 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 -> feature.getName()) |
| .collect(ImmutableList.toImmutableList()); |
| ImmutableSet<String> actionConfigNames = |
| ccToolchainConfigInfo.getActionConfigs().stream() |
| .map(actionConfig -> 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(feature -> 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(actionConfig -> 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',", |
| " 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()); |
| 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(); |
| 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 or .pic.a"); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'a.pic.o' does not have any of the allowed extensions .a, .lib or .pic.a"); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'a.ifso' does not have any of the allowed extensions .so, .dylib or .dll"); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'a.so' does not have any of the allowed extensions .ifso, .tbd or .lib"); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'a.lib' does not have any of the allowed extensions .so, .dylib or .dll"); |
| assertThat(e) |
| .hasMessageThat() |
| .contains("'a.dll' does not have any of the allowed extensions .ifso, .tbd or .lib"); |
| } |
| |
| @Test |
| public void testCcOutputsMerging() throws Exception { |
| setupCcOutputsTest(); |
| scratch.file( |
| "foo/BUILD", |
| "load('//tools/build_defs/foo:extension.bzl', 'cc_skylark_library')", |
| "cc_skylark_library(", |
| " name = 'skylark_lib',", |
| " object1 = 'object1.o',", |
| " pic_object1 = 'pic_object1.o',", |
| " object2 = 'object2.o',", |
| " pic_object2 = 'pic_object2.o',", |
| ")"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:skylark_lib"); |
| CcCompilationOutputs compilationOutputs = |
| (CcCompilationOutputs) getMyInfoFromTarget(target).getValue("compilation_outputs"); |
| assertThat( |
| AnalysisTestUtil.artifactsToStrings( |
| masterConfig, compilationOutputs.getObjectFiles(/* usePic= */ true))) |
| .containsExactly("src foo/pic_object1.o", "src foo/pic_object2.o"); |
| assertThat( |
| AnalysisTestUtil.artifactsToStrings( |
| masterConfig, 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:skylark_lib"); |
| assertThat(getFilenamesToBuild(target)).doesNotContain("skylark_lib.o"); |
| assertThat(getFilenamesToBuild(target)).contains("skylark_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:skylark_lib"); |
| assertThat(getFilenamesToBuild(target)).contains("skylark_lib.o"); |
| assertThat(getFilenamesToBuild(target)).doesNotContain("skylark_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:skylark_lib"); |
| assertThat(getFilenamesToBuild(target)).contains("skylark_lib.pic.o"); |
| assertThat(getFilenamesToBuild(target)).contains("skylark_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:skylark_lib")).isNotNull(); |
| ConfiguredTarget target = getConfiguredTarget("//foo:skylark_lib"); |
| assertThat( |
| getFilesToBuild(target).toList().stream() |
| .map(x -> x.getFilename()) |
| .collect(ImmutableList.toImmutableList())) |
| .contains("libskylark_lib.a"); |
| } |
| |
| @Test |
| public void testDoNotCreateStaticLibraries() throws Exception { |
| createFilesForTestingLinking(scratch, "tools/build_defs/foo", "disallow_static_libraries=True"); |
| assertThat(getConfiguredTarget("//foo:skylark_lib")).isNotNull(); |
| ConfiguredTarget target = getConfiguredTarget("//foo:skylark_lib"); |
| assertThat( |
| getFilesToBuild(target).toList().stream() |
| .map(x -> x.getFilename()) |
| .collect(ImmutableList.toImmutableList())) |
| .doesNotContain("libskylark_lib.a"); |
| } |
| |
| private List<String> getFilenamesToBuild(ConfiguredTarget target) { |
| return getFilesToBuild(target).toList().stream() |
| .map(Artifact::getFilename) |
| .collect(ImmutableList.toImmutableList()); |
| } |
| |
| @Test |
| public void testCcNativeRuleDependingOnSkylarkDefinedRule() 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:skylark_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:skylark_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:skylark_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:skylark_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:skylark_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:skylark_lib"); |
| assertThat(target).isNotNull(); |
| CppCompileAction action = |
| (CppCompileAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".o")); |
| assertThat(action.getArguments()).containsAtLeast("-DDEFINE1", "-DDEFINE2"); |
| } |
| |
| @Test |
| public void testHeaders() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", /* compileProviderLines= */ ""); |
| ConfiguredTarget target = getConfiguredTarget("//foo:skylark_lib"); |
| assertThat(target).isNotNull(); |
| CcInfo ccInfo = target.get(CcInfo.PROVIDER); |
| assertThat(artifactsToStrings(ccInfo.getCcCompilationContext().getDeclaredIncludeSrcs())) |
| .containsAtLeast( |
| "src foo/dep2.h", "src foo/skylark_lib.h", "src foo/private_skylark_lib.h"); |
| } |
| |
| @Test |
| public void testCompileOutputHasSuffix() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", /* compileProviderLines= */ ""); |
| ConfiguredTarget target = getConfiguredTarget("//foo:skylark_lib"); |
| assertThat(target).isNotNull(); |
| assertThat(artifactsToStrings(getFilesToBuild(target))) |
| .contains("bin foo/_objs/skylark_lib_suffix/skylark_lib.o"); |
| } |
| |
| @Test |
| public void testCompilationContexts() throws Exception { |
| createFilesForTestingCompilation( |
| scratch, "tools/build_defs/foo", /* compileProviderLines= */ ""); |
| assertThat(getConfiguredTarget("//foo:bin")).isNotNull(); |
| ConfiguredTarget target = getConfiguredTarget("//foo:skylark_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:skylark_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("libskylark_lib.so"); |
| } |
| |
| @Test |
| public void testUserLinkFlags() throws Exception { |
| createFilesForTestingLinking( |
| scratch, "tools/build_defs/foo", "user_link_flags=['-LINKING_OPTION']"); |
| ConfiguredTarget target = getConfiguredTarget("//foo:skylark_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:skylark_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:skylark_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:skylark_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:skylark_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 testWrongSrcsExtensionGivesError() throws Exception { |
| doTestWrongExtensionOfSrcsAndHdrs("srcs"); |
| } |
| |
| @Test |
| public void testWrongPrivateHdrExtensionGivesError() throws Exception { |
| doTestWrongExtensionOfSrcsAndHdrs("private_hdrs"); |
| } |
| |
| @Test |
| public void testWrongPublicHdrExtensionGivesError() throws Exception { |
| doTestWrongExtensionOfSrcsAndHdrs("public_hdrs"); |
| } |
| |
| |
| @Test |
| public void testWrongSrcExtensionGivesError() throws Exception { |
| createFiles(scratch, "tools/build_defs/foo"); |
| |
| scratch.file( |
| "bar/BUILD", |
| "load('//tools/build_defs/foo:extension.bzl', 'cc_skylark_library')", |
| "cc_skylark_library(", |
| " name = 'skylark_lib',", |
| " srcs = ['skylark_lib.qweqwe'],", |
| ")"); |
| reporter.removeHandler(failFastHandler); |
| getConfiguredTarget("//bar:skylark_lib"); |
| assertContainsEvent("The list of possible extensions for 'srcs'"); |
| } |
| |
| 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, "", ""); |
| } |
| |
| @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(EvalUtils.isNullOrNone(executable)).isTrue(); |
| } |
| |
| @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(EvalUtils.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 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_skylark_library')", |
| "cc_skylark_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 SkylarkProvider.SkylarkKey( |
| Label.parseAbsolute("//foo:foo.bzl", ImmutableMap.of()), "FooInfo"); |
| LibraryToLink fooLibrary = |
| Iterables.getOnlyElement( |
| getConfiguredTarget("//foo:dep") |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getLibraries() |
| .toList()); |
| SkylarkInfo fooInfo = |
| (SkylarkInfo) getConfiguredTarget("//foo:foo").get(SkylarkProviderIdentifier.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 SkylarkProvider.SkylarkKey( |
| Label.parseAbsolute("//foo:foo.bzl", ImmutableMap.of()), "FooInfo"); |
| |
| // Default toolchain is without PIC support, so pic_objects should be None |
| SkylarkInfo fooInfoForPic = |
| (SkylarkInfo) getConfiguredTarget("//foo:foo").get(SkylarkProviderIdentifier.forKey(key)); |
| |
| Object picObjects = fooInfoForPic.getValue("pic_objects"); |
| assertThat(picObjects).isNotEqualTo(Starlark.NONE); |
| assertThat((StarlarkList) 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(); |
| SkylarkInfo fooInfoForNoPic = |
| (SkylarkInfo) getConfiguredTarget("//foo:foo").get(SkylarkProviderIdentifier.forKey(key)); |
| |
| Object objects = fooInfoForNoPic.getValue("objects"); |
| assertThat(objects).isNotEqualTo(Starlark.NONE); |
| assertThat((StarlarkList) objects).isEmpty(); |
| } |
| |
| @Test |
| public void testIncompatibleRequireLinkerInputCcApi() throws Exception { |
| setSkylarkSemanticsOptions("--incompatible_require_linker_input_cc_api"); |
| setUpCcLinkingContextTest(); |
| checkError("//a:a", "It may be temporarily re-enabled by setting"); |
| } |
| |
| 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'])", |
| "", |
| "def _foo_impl(ctx):", |
| " lib = ctx.attr.dep[CcInfo].linking_context.libraries_to_link.to_list()[0]", |
| " return [FooInfo(objects=lib.objects, pic_objects=lib.pic_objects)]", |
| "", |
| "foo = rule(", |
| " implementation = _foo_impl,", |
| " attrs = {", |
| " 'dep': attr.label(),", |
| " }", |
| ")"); |
| } |
| |
| 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_skylark_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._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,", |
| " srcs=ctx.files.srcs,", |
| " name=ctx.label.name + '_suffix',", |
| " compilation_contexts = dep_compilation_contexts,", |
| " public_hdrs=ctx.files.public_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)", |
| " 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_skylark_library = rule(", |
| " implementation = _cc_skylark_library_impl,", |
| " attrs = {", |
| " 'srcs': attr.label_list(allow_files=True),", |
| " 'public_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]),", |
| " '_cc_toolchain': attr.label(default =", |
| " configuration_field(fragment = 'cpp', name = 'cc_toolchain'))", |
| " },", |
| fragments, |
| ")"); |
| scratch.file( |
| "foo/BUILD", |
| "load('//" + bzlFilePath + ":extension.bzl', 'cc_skylark_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_skylark_library(", |
| " name = 'skylark_lib',", |
| " srcs = ['skylark_lib.cc'],", |
| " public_hdrs = ['skylark_lib.h'],", |
| " private_hdrs = ['private_skylark_lib.h'],", |
| ")", |
| "cc_binary(", |
| " name = 'bin',", |
| " deps = ['skylark_lib'],", |
| ")"); |
| } |
| |
| private void doTestWrongExtensionOfSrcsAndHdrs(String attrName) throws Exception { |
| createFiles(scratch, "tools/build_defs/foo"); |
| scratch.file( |
| "bar/BUILD", |
| "load('//tools/build_defs/foo:extension.bzl', 'cc_skylark_library')", |
| "cc_skylark_library(", |
| " name = 'skylark_lib',", |
| " " + attrName + " = ['skylark_lib.cannotpossiblybevalid'],", |
| ")"); |
| reporter.removeHandler(failFastHandler); |
| getConfiguredTarget("//bar:skylark_lib"); |
| assertContainsEvent( |
| "has wrong extension. The list of possible extensions for '" + attrName + "'"); |
| } |
| |
| 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_skylark_library')", |
| "cc_skylark_library(", |
| " name = 'skylark_lib',", |
| " " + attrName + " = ['file" + extension + "'],", |
| ")"); |
| getConfiguredTarget("//bar:skylark_lib"); |
| assertNoEvents(); |
| } |
| } |
| |
| private void doTestCcOutputsWrongExtension(String attrName, String paramName) throws Exception { |
| setupCcOutputsTest(); |
| scratch.file( |
| "foo/BUILD", |
| "load('//tools/build_defs/foo:extension.bzl', 'cc_skylark_library')", |
| "cc_skylark_library(", |
| " name = 'skylark_lib',", |
| " " + attrName + " = 'object.cannotpossiblybevalid',", |
| ")"); |
| reporter.removeHandler(failFastHandler); |
| getConfiguredTarget("//foo:skylark_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_skylark_library')", |
| "cc_skylark_library(", |
| " name = 'skylark_lib',", |
| " " + paramName + " = 'object1" + extension + "',", |
| ")"); |
| getConfiguredTarget("//foo:skylark_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_skylark_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_skylark_library = rule(", |
| " implementation = _cc_skylark_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 setupTestTransitiveLink(Scratch scratch, 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'],", |
| ")"); |
| scratch.file( |
| "tools/build_defs/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))", |
| " linking_outputs = cc_common.link(", |
| " actions=ctx.actions,", |
| " feature_configuration=feature_configuration,", |
| " name = ctx.label.name,", |
| " cc_toolchain=toolchain,", |
| " " + Joiner.on("").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 = 'host'", |
| " ),", |
| " },", |
| fragments, |
| ")"); |
| scratch.file( |
| "foo/BUILD", |
| "load('//tools/build_defs:extension.bzl', 'cc_bin')", |
| "cc_library(", |
| " name = 'dep1',", |
| " srcs = ['dep1.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_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',", |
| " 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"); |
| |
| ConfiguredTarget barTarget = getConfiguredTarget("//direct:bar"); |
| Iterable<Artifact> barDirectHeaders = getArtifactsFromMyInfo(barTarget, "direct_headers"); |
| assertThat(baseArtifactNames(barDirectHeaders)).containsExactly("bar.h"); |
| } |
| |
| @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"); |
| } |
| } |