From 0de692d24153f32daee00efbcab050585ae771ce Mon Sep 17 00:00:00 2001 From: John Cater Date: Thu, 23 Feb 2017 11:21:52 -0500 Subject: [PATCH] Add platform rule to define a platform as a collection of constraint values. Part of ongoing work on #2219. Change-Id: Ie4e842a5d8218e47f41a954c2b955ab24237aa65 --- .../bazel/rules/BazelRuleClassProvider.java | 2 + .../build/lib/rules/platform/Platform.java | 60 +++++++++++++++++++ .../lib/rules/platform/PlatformProvider.java | 43 +++++++++++++ .../lib/rules/platform/PlatformRule.java | 54 +++++++++++++++++ .../lib/rules/platform/ConstraintTest.java | 45 ++++++++++++++ 5 files changed, 204 insertions(+) create mode 100644 src/main/java/com/google/devtools/build/lib/rules/platform/Platform.java create mode 100644 src/main/java/com/google/devtools/build/lib/rules/platform/PlatformProvider.java create mode 100644 src/main/java/com/google/devtools/build/lib/rules/platform/PlatformRule.java diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java index 9f3bfde7ac8ef1..4784c249f5fd02 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java @@ -161,6 +161,7 @@ import com.google.devtools.build.lib.rules.objc.XcTestAppProvider; import com.google.devtools.build.lib.rules.platform.ConstraintSettingRule; import com.google.devtools.build.lib.rules.platform.ConstraintValueRule; +import com.google.devtools.build.lib.rules.platform.PlatformRule; import com.google.devtools.build.lib.rules.proto.BazelProtoLibraryRule; import com.google.devtools.build.lib.rules.proto.ProtoConfiguration; import com.google.devtools.build.lib.rules.proto.ProtoLangToolchainRule; @@ -343,6 +344,7 @@ public ImmutableList requires() { public void init(Builder builder) { builder.addRuleDefinition(new ConstraintSettingRule()); builder.addRuleDefinition(new ConstraintValueRule()); + builder.addRuleDefinition(new PlatformRule()); } @Override diff --git a/src/main/java/com/google/devtools/build/lib/rules/platform/Platform.java b/src/main/java/com/google/devtools/build/lib/rules/platform/Platform.java new file mode 100644 index 00000000000000..1d6d75d6fe9468 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/platform/Platform.java @@ -0,0 +1,60 @@ +package com.google.devtools.build.lib.rules.platform; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Multimap; +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.FileProvider; +import com.google.devtools.build.lib.analysis.FilesToRunProvider; +import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; +import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.RunfilesProvider; +import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; + +/** Defines a platform for execution contexts. */ +public class Platform implements RuleConfiguredTargetFactory { + @Override + public ConfiguredTarget create(RuleContext ruleContext) + throws InterruptedException, RuleErrorException { + + // TODO(katre): Create platform provider with correct data. + Iterable constraintValues = + ruleContext.getPrerequisites( + PlatformRule.CONSTRAINTS_ATTR, Mode.DONT_CHECK, ConstraintValueProvider.class); + + // TODO(katre): Verify the constraints - no two settings. + ImmutableMap constraints = validateConstraints(ruleContext, constraintValues); + if (constraints == null) { + // An error occurred, return null. + return null; + } + + PlatformProvider provider = PlatformProvider.create(constraints); + + return new RuleConfiguredTargetBuilder(ruleContext) + .addProvider(RunfilesProvider.class, RunfilesProvider.EMPTY) + .addProvider(FileProvider.class, FileProvider.EMPTY) + .addProvider(FilesToRunProvider.class, FilesToRunProvider.EMPTY) + .addProvider(PlatformProvider.class, provider) + .build(); + } + + private ImmutableMap validateConstraints(RuleContext ruleContext, + Iterable constraintValues) { + Multimap constraints = ArrayListMultimap.create(); + + for (ConstraintValueProvider constraintValue : constraintValues) { + constraints.put(constraintValue.constraint(), constraintValue); + } + + // Are there any settings with more than one value? + for (ConstraintSettingProvider constraintSetting : constraints.keySet()) { + if (constraints.get(constraintSetting).size() > 1) { + // error + } + } + + return null; + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/platform/PlatformProvider.java b/src/main/java/com/google/devtools/build/lib/rules/platform/PlatformProvider.java new file mode 100644 index 00000000000000..6180d09fb14cd2 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/platform/PlatformProvider.java @@ -0,0 +1,43 @@ +// Copyright 2017 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.platform; + +import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableMap; +import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; +import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; + +/** Provider for a platform, which is a group of constraints and values. */ +@AutoValue +@Immutable +public abstract class PlatformProvider implements TransitiveInfoProvider { + public abstract ImmutableMap constraints(); + + public static PlatformProvider create( + ImmutableMap constraints) { + return new AutoValue_PlatformProvider(constraints); + } + + public static PlatformProvider create(Iterable constraintValues) { + ImmutableMap.Builder builder = + new ImmutableMap.Builder<>(); + + for (ConstraintValueProvider constraintValue : constraintValues) { + builder.put(constraintValue.constraint(), constraintValue); + } + + return create(builder.build()); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/platform/PlatformRule.java b/src/main/java/com/google/devtools/build/lib/rules/platform/PlatformRule.java new file mode 100644 index 00000000000000..60150b9d5ee2b7 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/platform/PlatformRule.java @@ -0,0 +1,54 @@ +package com.google.devtools.build.lib.rules.platform; + +import static com.google.devtools.build.lib.packages.Attribute.attr; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.analysis.BaseRuleClasses; +import com.google.devtools.build.lib.analysis.RuleDefinition; +import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; +import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; +import com.google.devtools.build.lib.packages.BuildType; +import com.google.devtools.build.lib.packages.RuleClass; +import com.google.devtools.build.lib.syntax.Type; +import com.google.devtools.build.lib.util.FileTypeSet; + +/** Rule definition for {@link Platform}. */ +public class PlatformRule implements RuleDefinition { + public static final String RULE_NAME = "platform"; + public static final String CONSTRAINTS_ATTR = "constraints"; + + @Override + public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) { + /* + */ + return builder + .override( + attr("tags", Type.STRING_LIST) + // No need to show up in ":all", etc. target patterns. + .value(ImmutableList.of("manual")) + .nonconfigurable("low-level attribute, used in platform configuration")) + + // TODO(katre): add constraints attr. + .add( + attr(CONSTRAINTS_ATTR, BuildType.LABEL_LIST) + .allowedFileTypes(FileTypeSet.NO_FILE) + .mandatoryNativeProviders( + ImmutableList.>of( + ConstraintValueProvider.class))) + .exemptFromConstraintChecking("this rule *defines* a constraint") + .build(); + } + + @Override + public Metadata getMetadata() { + return Metadata.builder() + .name(RULE_NAME) + .ancestors(BaseRuleClasses.RuleBase.class) + .factoryClass(Platform.class) + .build(); + } +} +/* + + +*/ diff --git a/src/test/java/com/google/devtools/build/lib/rules/platform/ConstraintTest.java b/src/test/java/com/google/devtools/build/lib/rules/platform/ConstraintTest.java index 4e74ca226c20f4..3e1a85e0fced23 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/platform/ConstraintTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/platform/ConstraintTest.java @@ -57,4 +57,49 @@ public void testConstraint() throws Exception { assertThat(barValue.getProvider(ConstraintValueProvider.class).value()) .isEqualTo(Label.parseAbsolute("//constraint:bar")); } + + @Test + public void testPlatform() throws Exception { + scratch.file( + "constraint/BUILD", + "constraint_setting(name = 'basic')", + "constraint_value(name = 'foo',", + " constraint=':basic',", + " )", + "platform(name = 'plat1',", + " constraints = [", + " ':foo',", + " ])" + ); + + ConfiguredTarget platform = getConfiguredTarget("//constraint:plat1"); + assertThat(platform).isNotNull(); + + PlatformProvider provider = platform.getProvider(PlatformProvider.class); + assertThat(provider).isNotNull(); + assertThat(provider.constraints()).hasSize(1); + assertThat(provider.constraints()).containsKey(ConstraintSettingProvider.create(Label.parseAbsolute("//constraint:basic"))); + assertThat(provider.constraints().get(ConstraintSettingProvider.create(Label.parseAbsolute("//constraint:basic"))).value()) + .isEqualTo(Label.parseAbsolute("//constraint:foo")); + } + + @Test + public void testPlatform_overlappingConstraintValueError() throws Exception { + checkError("constraint", + "plat1", + "Conflicting constraint values for //constraint:basic", + "constraint_setting(name = 'basic')", + "constraint_value(name = 'foo',", + " constraint=':basic',", + " )", + "constraint_value(name = 'bar',", + " constraint=':basic',", + " )", + "platform(name = 'plat1',", + " constraints = [", + " ':foo',", + " ':bar',", + " ])" + ); + } }