Add new configuration fragment for platforms, with host and target
options.

Change-Id: If623f2416f8bff7c74ddf99d5c957a075de6494f
PiperOrigin-RevId: 158275892
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 610486e..2864670 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -602,6 +602,7 @@
         ":vfs",
         "//src/main/java/com/google/devtools/build/lib/actions",
         "//src/main/java/com/google/devtools/build/lib/analysis/platform",
+        "//src/main/java/com/google/devtools/build/lib/analysis/platform:utils",
         "//src/main/java/com/google/devtools/build/lib/buildeventstream/proto:build_event_stream_java_proto",
         "//src/main/java/com/google/devtools/build/lib/causes",
         "//src/main/java/com/google/devtools/build/lib/cmdline",
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/PlatformConfiguration.java b/src/main/java/com/google/devtools/build/lib/analysis/PlatformConfiguration.java
new file mode 100644
index 0000000..1b86f7b
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/PlatformConfiguration.java
@@ -0,0 +1,57 @@
+// 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.analysis;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.concurrent.ThreadSafety;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
+import java.util.List;
+
+/** A configuration fragment describing the current platform configuration. */
+@ThreadSafety.Immutable
+@SkylarkModule(
+  name = "platform",
+  doc = "The platform configuration.",
+  category = SkylarkModuleCategory.CONFIGURATION_FRAGMENT
+)
+public class PlatformConfiguration extends BuildConfiguration.Fragment {
+
+  private final Label executionPlatform;
+  private final ImmutableList<Label> targetPlatforms;
+
+  public PlatformConfiguration(Label executionPlatform, List<Label> targetPlatforms) {
+
+    this.executionPlatform = executionPlatform;
+    this.targetPlatforms = ImmutableList.copyOf(targetPlatforms);
+  }
+
+  @SkylarkCallable(
+    name = "execution_platform",
+    structField = true,
+    doc = "The current execution platform"
+  )
+  public Label getExecutionPlatform() {
+    return executionPlatform;
+  }
+
+  @SkylarkCallable(name = "platforms", structField = true, doc = "The current target platforms")
+  public ImmutableList<Label> getTargetPlatforms() {
+    return targetPlatforms;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/PlatformConfigurationLoader.java b/src/main/java/com/google/devtools/build/lib/analysis/PlatformConfigurationLoader.java
new file mode 100644
index 0000000..85cc174
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/PlatformConfigurationLoader.java
@@ -0,0 +1,52 @@
+// 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.analysis;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.config.BuildOptions;
+import com.google.devtools.build.lib.analysis.config.ConfigurationEnvironment;
+import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory;
+import com.google.devtools.build.lib.analysis.config.FragmentOptions;
+import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
+import com.google.devtools.build.lib.cmdline.Label;
+
+/** A loader that creates {@link PlatformConfiguration} instances based on command-line options. */
+public class PlatformConfigurationLoader implements ConfigurationFragmentFactory {
+  @Override
+  public ImmutableSet<Class<? extends FragmentOptions>> requiredOptions() {
+    return ImmutableSet.<Class<? extends FragmentOptions>>of(PlatformOptions.class);
+  }
+
+  @Override
+  public PlatformConfiguration create(ConfigurationEnvironment env, BuildOptions buildOptions)
+      throws InvalidConfigurationException, InterruptedException {
+    PlatformOptions platformOptions = buildOptions.get(PlatformOptions.class);
+    return create(platformOptions);
+  }
+
+  @Override
+  public Class<? extends BuildConfiguration.Fragment> creates() {
+    return PlatformConfiguration.class;
+  }
+
+  private PlatformConfiguration create(PlatformOptions options)
+      throws InvalidConfigurationException {
+    // TODO(katre): This will change with remote execution.
+    Label executionPlatform = options.hostPlatform;
+
+    return new PlatformConfiguration(executionPlatform, options.platforms);
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/PlatformOptions.java b/src/main/java/com/google/devtools/build/lib/analysis/PlatformOptions.java
new file mode 100644
index 0000000..b9c8175
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/PlatformOptions.java
@@ -0,0 +1,56 @@
+// 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.analysis;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.config.FragmentOptions;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.common.options.Option;
+import com.google.devtools.common.options.OptionsParser;
+import java.util.List;
+
+/** Command-line options for platform-related configuration. */
+public class PlatformOptions extends FragmentOptions {
+
+  @Option(
+    name = "experimental_host_platform",
+    converter = BuildConfiguration.LabelConverter.class,
+    // TODO(katre): Use @bazel_tools//platforms:host_platform when available.
+    defaultValue = "@bazel_tools//platforms:default_platform",
+    optionUsageRestrictions = OptionsParser.OptionUsageRestrictions.HIDDEN,
+    help = "Declare the platform the build is started from"
+  )
+  public Label hostPlatform;
+
+  // TODO(katre): Add execution platforms.
+
+  @Option(
+    name = "experimental_platforms",
+    converter = BuildConfiguration.LabelListConverter.class,
+    // TODO(katre): Use @bazel_tools//platforms:target_platform when available.
+    defaultValue = "@bazel_tools//platforms:default_platform",
+    optionUsageRestrictions = OptionsParser.OptionUsageRestrictions.HIDDEN,
+    help = "Declare the platforms targeted by the current build"
+  )
+  public List<Label> platforms;
+
+  @Override
+  public PlatformOptions getHost(boolean fallback) {
+    PlatformOptions host = (PlatformOptions) getDefault();
+    host.platforms = ImmutableList.of(this.hostPlatform);
+    return host;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/PlatformSemantics.java b/src/main/java/com/google/devtools/build/lib/analysis/PlatformSemantics.java
new file mode 100644
index 0000000..7da9d4c
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/PlatformSemantics.java
@@ -0,0 +1,88 @@
+// 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.analysis;
+
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
+
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
+import com.google.devtools.build.lib.analysis.platform.PlatformProviderUtils;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.packages.Attribute;
+import com.google.devtools.build.lib.packages.AttributeMap;
+import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.RuleClass;
+import java.util.List;
+
+/** Helper class to manage rules' use of platforms. */
+public class PlatformSemantics {
+
+  public static final String TARGET_PLATFORMS_ATTR = ":target_platforms";
+  public static final String EXECUTION_PLATFORM_ATTR = ":execution_platform";
+
+  public Iterable<PlatformInfo> getTargetPlatforms(RuleContext ruleContext) {
+    Iterable<PlatformInfo> platform =
+        PlatformProviderUtils.platforms(
+            ruleContext.getPrerequisites(
+                TARGET_PLATFORMS_ATTR, RuleConfiguredTarget.Mode.DONT_CHECK));
+    return platform;
+  }
+
+  public PlatformInfo getExecutionPlatform(RuleContext ruleContext) {
+    PlatformInfo platform =
+        PlatformProviderUtils.platform(
+            ruleContext.getPrerequisite(
+                EXECUTION_PLATFORM_ATTR, RuleConfiguredTarget.Mode.DONT_CHECK));
+    return platform;
+  }
+
+  /** Implementation for the :target_platform attribute. */
+  public static final Attribute.LateBoundLabelList<BuildConfiguration> TARGET_PLATFORM =
+      new Attribute.LateBoundLabelList<BuildConfiguration>(PlatformConfiguration.class) {
+        @Override
+        public List<Label> resolve(
+            Rule rule, AttributeMap attributes, BuildConfiguration configuration) {
+          if (rule.getRuleClassObject().getRequiredToolchains().isEmpty()) {
+            return null;
+          }
+          return configuration.getFragment(PlatformConfiguration.class).getTargetPlatforms();
+        }
+      };
+  /** Implementation for the :execution_platform attribute. */
+  public static final Attribute.LateBoundLabel<BuildConfiguration> EXECUTION_PLATFORM =
+      new Attribute.LateBoundLabel<BuildConfiguration>(PlatformConfiguration.class) {
+        @Override
+        public Label resolve(Rule rule, AttributeMap attributes, BuildConfiguration configuration) {
+          if (rule.getRuleClassObject().getRequiredToolchains().isEmpty()) {
+            return null;
+          }
+          return configuration.getFragment(PlatformConfiguration.class).getExecutionPlatform();
+        }
+      };
+
+  public static RuleClass.Builder platformAttributes(RuleClass.Builder builder) {
+    return builder
+        .add(
+            attr(TARGET_PLATFORMS_ATTR, LABEL_LIST)
+                .value(TARGET_PLATFORM)
+                .nonconfigurable("Used in toolchain resolution"))
+        .add(
+            attr(EXECUTION_PLATFORM_ATTR, LABEL)
+                .value(EXECUTION_PLATFORM)
+                .nonconfigurable("Used in toolchain resolution"));
+  }
+}
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 20a072e..530a677 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
@@ -23,6 +23,8 @@
 import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
 import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider.Builder;
 import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider.RuleSet;
+import com.google.devtools.build.lib.analysis.PlatformConfigurationLoader;
+import com.google.devtools.build.lib.analysis.PlatformOptions;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
 import com.google.devtools.build.lib.analysis.constraints.EnvironmentRule;
 import com.google.devtools.build.lib.bazel.rules.BazelToolchainType.BazelToolchainTypeRule;
@@ -162,7 +164,6 @@
 import com.google.devtools.build.lib.rules.test.SkylarkTestingModule;
 import com.google.devtools.build.lib.rules.test.TestSuiteRule;
 import com.google.devtools.build.lib.util.ResourceFileLoader;
-
 import java.io.IOException;
 
 /** A rule class provider implementing the rules Bazel knows. */
@@ -242,6 +243,9 @@
       new RuleSet() {
         @Override
         public void init(Builder builder) {
+          builder.addConfigurationOptions(PlatformOptions.class);
+          builder.addConfigurationFragment(new PlatformConfigurationLoader());
+
           builder.addRuleDefinition(new ConstraintSettingRule());
           builder.addRuleDefinition(new ConstraintValueRule());
           builder.addRuleDefinition(new PlatformRule());
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
index f11b74a..004b215 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
@@ -39,6 +39,7 @@
 import com.google.devtools.build.lib.analysis.BaseRuleClasses;
 import com.google.devtools.build.lib.analysis.DefaultProvider;
 import com.google.devtools.build.lib.analysis.OutputGroupProvider;
+import com.google.devtools.build.lib.analysis.PlatformSemantics;
 import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
@@ -125,10 +126,11 @@
   /** Parent rule class for non-executable non-test Skylark rules. */
   public static final RuleClass baseRule =
       BaseRuleClasses.commonCoreAndSkylarkAttributes(
-          BaseRuleClasses.nameAttribute(
-            new RuleClass.Builder("$base_rule", RuleClassType.ABSTRACT, true))
-            .add(attr("expect_failure", STRING)))
-            .build();
+              PlatformSemantics.platformAttributes(
+                  BaseRuleClasses.nameAttribute(
+                          new RuleClass.Builder("$base_rule", RuleClassType.ABSTRACT, true))
+                      .add(attr("expect_failure", STRING))))
+          .build();
 
   /** Parent rule class for executable non-test Skylark rules. */
   public static final RuleClass binaryBaseRule =
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index d25a712..24e0835 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -367,6 +367,7 @@
         "//src/main/java/com/google/devtools/build/lib/rules/cpp",
         "//src/main/java/com/google/devtools/build/lib/rules/genquery",
         "//src/main/java/com/google/devtools/build/lib/rules/objc",
+        "//src/main/java/com/google/devtools/build/lib/rules/platform",
         "//src/main/java/com/google/devtools/build/skyframe",
         "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
         "//src/main/java/com/google/devtools/common/options",
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
index adb4c35..60794b2 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.analysis.ConfigurationCollectionFactory;
 import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
+import com.google.devtools.build.lib.analysis.PlatformConfigurationLoader;
 import com.google.devtools.build.lib.analysis.config.ConfigurationFactory;
 import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory;
 import com.google.devtools.build.lib.analysis.util.AnalysisMock;
@@ -270,7 +271,8 @@
         new J2ObjcConfiguration.Loader(),
         new ProtoConfiguration.Loader(),
         new ConfigFeatureFlagConfiguration.Loader(),
-        new AndroidConfiguration.Loader());
+        new AndroidConfiguration.Loader(),
+        new PlatformConfigurationLoader());
   }
 
   @Override
diff --git a/tools/platforms/platforms.BUILD b/tools/platforms/platforms.BUILD
index 416de4c..6fb275d 100644
--- a/tools/platforms/platforms.BUILD
+++ b/tools/platforms/platforms.BUILD
@@ -54,3 +54,6 @@
     name = "windows",
     constraint_setting = ":os",
 )
+
+# A default platform with nothing defined.
+platform(name = "default_platform")