Add the ExecGroup object and `exec_group(toolchains = [], exec_compatible_with = [])` to Starlark.

ExecGroup constructor doesn't do a whole lot yet, just gets and stores the inputs.

PiperOrigin-RevId: 303969136
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
index c1b76d8..2e27ccd 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
@@ -54,6 +54,7 @@
 import com.google.devtools.build.lib.packages.BazelStarlarkContext;
 import com.google.devtools.build.lib.packages.BuildSetting;
 import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.packages.ExecGroup;
 import com.google.devtools.build.lib.packages.FunctionSplitTransitionWhitelist;
 import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunctionWithCallback;
 import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunctionWithMap;
@@ -862,4 +863,14 @@
       throw Starlark.errorf("Illegal absolute label syntax: %s", labelString);
     }
   }
+
+  @Override
+  public ExecGroup execGroup(
+      Sequence<?> toolchains, Sequence<?> execCompatibleWith, StarlarkThread thread)
+      throws EvalException {
+    ImmutableSet<Label> toolchainTypes = ImmutableSet.copyOf(parseToolchains(toolchains, thread));
+    ImmutableSet<Label> constraints =
+        ImmutableSet.copyOf(parseExecCompatibleWith(execCompatibleWith, thread));
+    return new ExecGroup(toolchainTypes, constraints);
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/packages/ExecGroup.java b/src/main/java/com/google/devtools/build/lib/packages/ExecGroup.java
new file mode 100644
index 0000000..7e43d93
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/packages/ExecGroup.java
@@ -0,0 +1,43 @@
+// Copyright 2020 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.packages;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.skylarkbuildapi.ExecGroupApi;
+import java.util.Set;
+
+/**
+ * Resolves the appropriate toolchains for the given parameters.
+ *
+ * <p>Currently just a stub.
+ */
+public class ExecGroup implements ExecGroupApi {
+  private final ImmutableSet<Label> requiredToolchains;
+  private final ImmutableSet<Label> execComptaibleWith;
+
+  public ExecGroup(Set<Label> requiredToolchains, Set<Label> execCompatibleWith) {
+    this.requiredToolchains = ImmutableSet.copyOf(requiredToolchains);
+    this.execComptaibleWith = ImmutableSet.copyOf(execCompatibleWith);
+  }
+
+  public ImmutableSet<Label> getRequiredToolchains() {
+    return requiredToolchains;
+  }
+
+  public ImmutableSet<Label> getExecutionPlatformConstraints() {
+    return execComptaibleWith;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/ExecGroupApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/ExecGroupApi.java
new file mode 100644
index 0000000..f54f383
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/ExecGroupApi.java
@@ -0,0 +1,23 @@
+// Copyright 2020 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.skylarkbuildapi;
+
+import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
+import com.google.devtools.build.lib.syntax.StarlarkValue;
+
+/** A unit of toolchain resolution. Every rule has one or more of these. */
+@SkylarkModule(name = "exec_group", category = SkylarkModuleCategory.BUILTIN, documented = false)
+public interface ExecGroupApi extends StarlarkValue {}
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java
index 651136f..40a4c24 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java
@@ -37,6 +37,9 @@
 @SkylarkGlobalLibrary
 public interface SkylarkRuleFunctionsApi<FileApiT extends FileApi> {
 
+  String EXEC_COMPATIBLE_WITH_PARAM = "exec_compatible_with";
+  String TOOLCHAINS_PARAM = "toolchains";
+
   String PROVIDES_DOC =
       "A list of providers that the implementation function must return."
           + ""
@@ -246,7 +249,7 @@
                     + "This should only be used for testing the analysis-time behavior of "
                     + "Starlark rules. This flag may be removed in the future."),
         @Param(
-            name = "toolchains",
+            name = TOOLCHAINS_PARAM,
             type = Sequence.class,
             named = true,
             generic1 = String.class,
@@ -272,7 +275,7 @@
             defaultValue = "[]",
             doc = PROVIDES_DOC),
         @Param(
-            name = "exec_compatible_with",
+            name = EXEC_COMPATIBLE_WITH_PARAM,
             type = Sequence.class,
             generic1 = String.class,
             named = true,
@@ -461,7 +464,7 @@
                 "List of names of configuration fragments that the aspect requires "
                     + "in host configuration."),
         @Param(
-            name = "toolchains",
+            name = TOOLCHAINS_PARAM,
             type = Sequence.class,
             named = true,
             generic1 = String.class,
@@ -544,4 +547,35 @@
   @SkylarkConstructor(objectType = Label.class)
   Label label(String labelString, Boolean relativeToCallerRepository, StarlarkThread thread)
       throws EvalException;
+
+  @SkylarkCallable(
+      name = "exec_group",
+      // TODO(juliexxia); uncomment or remove based on resolution of b/152637857
+      // enableOnlyWithFlag = FlagIdentifier.EXPERIMENTAL_EXEC_GROUPS,
+      doc =
+          "<i>experimental</i> Creates an execution group which can be used to create"
+              + "actions for a specific execution platform during rule implementation. This is "
+              + "ongoing work and not yet functional - DO NOT USE.",
+      parameters = {
+        @Param(
+            name = TOOLCHAINS_PARAM,
+            type = Sequence.class,
+            generic1 = String.class,
+            named = true,
+            positional = false,
+            defaultValue = "[]",
+            doc = "<i>Experimental</i> The set of toolchains this execution group requires."),
+        @Param(
+            name = EXEC_COMPATIBLE_WITH_PARAM,
+            type = Sequence.class,
+            generic1 = String.class,
+            named = true,
+            positional = false,
+            defaultValue = "[]",
+            doc = "<i>Experimental</i> A list of constraints on the execution platform."),
+      },
+      useStarlarkThread = true)
+  ExecGroupApi execGroup(
+      Sequence<?> execCompatibleWith, Sequence<?> toolchains, StarlarkThread thread)
+      throws EvalException;
 }
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeExecGroup.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeExecGroup.java
new file mode 100644
index 0000000..2c7a670
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeExecGroup.java
@@ -0,0 +1,24 @@
+// Copyright 2020 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.skydoc.fakebuildapi;
+
+import com.google.devtools.build.lib.skylarkbuildapi.ExecGroupApi;
+import com.google.devtools.build.lib.syntax.Printer;
+
+/** Fake implementation of {@link ExecGroupApi} */
+public class FakeExecGroup implements ExecGroupApi {
+  @Override
+  public void repr(Printer printer) {}
+}
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
index a535549..ef24250 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
@@ -19,6 +19,7 @@
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
 import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.skylarkbuildapi.ExecGroupApi;
 import com.google.devtools.build.lib.skylarkbuildapi.FileApi;
 import com.google.devtools.build.lib.skylarkbuildapi.SkylarkAspectApi;
 import com.google.devtools.build.lib.skylarkbuildapi.SkylarkRuleFunctionsApi;
@@ -237,6 +238,12 @@
     return fakeAspect;
   }
 
+  @Override
+  public ExecGroupApi execGroup(
+      Sequence<?> execCompatibleWith, Sequence<?> toolchains, StarlarkThread thread) {
+    return new FakeExecGroup();
+  }
+
   /**
    * A fake {@link BaseFunction} implementation which serves as an identifier for a rule definition.
    * A skylark invocation of 'rule()' should spawn a unique instance of this class and return it.
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
index d22076b..b15fc6d 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
@@ -33,6 +33,7 @@
 import com.google.devtools.build.lib.packages.AspectParameters;
 import com.google.devtools.build.lib.packages.Attribute;
 import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.packages.ExecGroup;
 import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
 import com.google.devtools.build.lib.packages.PredicateWithMessage;
 import com.google.devtools.build.lib.packages.RequiredProviders;
@@ -1791,4 +1792,22 @@
     assertThat(lookup("p")).isEqualTo("Provider");
     assertThat(lookup("s")).isEqualTo("struct");
   }
+
+  @Test
+  public void testCreateExecGroup() throws Exception {
+    setSkylarkSemanticsOptions("--experimental_exec_groups=true");
+    reset();
+
+    scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
+    evalAndExport(
+        "group = exec_group(",
+        "  toolchains=['//test:my_toolchain_type'],",
+        "  exec_compatible_with=['//constraint:cv1', '//constraint:cv2'],",
+        ")");
+    ExecGroup group = ((ExecGroup) lookup("group"));
+    assertThat(group.getRequiredToolchains())
+        .containsExactly(makeLabel("//test:my_toolchain_type"));
+    assertThat(group.getExecutionPlatformConstraints())
+        .containsExactly(makeLabel("//constraint:cv1"), makeLabel("//constraint:cv2"));
+  }
 }