C++: Expose several methods from C++ to Starlark

The CcCommon object itself (unrelated to cc_common) can only be created from
builtins. Eventually all the code in CcCommon should be starlarkified and be
made part of cc_helper.

RELNOTES:none
PiperOrigin-RevId: 397070425
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java
index f48a3b6..5a09880 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java
@@ -13,6 +13,7 @@
 // limitations under the License.
 package com.google.devtools.build.lib.rules.cpp;
 
+import static com.google.common.collect.ImmutableList.toImmutableList;
 import static com.google.devtools.build.lib.packages.BuildType.LABEL;
 import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
 
@@ -72,13 +73,17 @@
 import java.util.regex.PatternSyntaxException;
 import java.util.stream.Stream;
 import javax.annotation.Nullable;
+import net.starlark.java.annot.Param;
+import net.starlark.java.annot.StarlarkMethod;
 import net.starlark.java.eval.EvalException;
+import net.starlark.java.eval.Sequence;
 import net.starlark.java.eval.Starlark;
+import net.starlark.java.eval.StarlarkList;
+import net.starlark.java.eval.StarlarkValue;
+import net.starlark.java.eval.Tuple;
 
-/**
- * Common parts of the implementation of cc rules.
- */
-public final class CcCommon {
+/** Common parts of the implementation of cc rules. */
+public final class CcCommon implements StarlarkValue {
 
   /** Name of the build variable for the sysroot path variable name. */
   public static final String SYSROOT_VARIABLE_NAME = "sysroot";
@@ -201,9 +206,14 @@
     return mergedOutputGroups;
   }
 
+  @StarlarkMethod(name = "linkopts", structField = true, documented = false)
+  public Sequence<String> getLinkoptsForStarlark() {
+    return StarlarkList.immutableCopyOf(getLinkopts());
+  }
+
   /**
-   * Returns our own linkopts from the rule attribute. This determines linker
-   * options to use when building this target and anything that depends on it.
+   * Returns our own linkopts from the rule attribute. This determines linker options to use when
+   * building this target and anything that depends on it.
    */
   public ImmutableList<String> getLinkopts() {
     Preconditions.checkState(hasAttribute("linkopts", Type.STRING_LIST));
@@ -217,6 +227,11 @@
     return ImmutableList.copyOf(result);
   }
 
+  @StarlarkMethod(name = "copts", structField = true, documented = false)
+  public Sequence<String> getCoptsForStarlark() {
+    return StarlarkList.immutableCopyOf(getCopts());
+  }
+
   public ImmutableList<String> getCopts() {
     if (!getCoptsFilter(ruleContext).passesFilter("-Wno-future-warnings")) {
       ruleContext.attributeWarning(
@@ -263,6 +278,30 @@
     return mapToListOfPairs(map);
   }
 
+  @StarlarkMethod(name = "srcs", documented = false, structField = true)
+  public Sequence<Tuple> getSourcesForStarlark() {
+    List<Pair<Artifact, Label>> sources = getSources();
+    ImmutableList<Tuple> tupleList =
+        sources.stream().map(p -> Tuple.pair(p.first, p.second)).collect(toImmutableList());
+    return StarlarkList.immutableCopyOf(tupleList);
+  }
+
+  @StarlarkMethod(name = "private_hdrs", documented = false, structField = true)
+  public Sequence<Tuple> getPrivateHeaderForStarlark() {
+    return convertListPairToTuple(getPrivateHeaders());
+  }
+
+  @StarlarkMethod(name = "public_hdrs", documented = false, structField = true)
+  public Sequence<Tuple> getPublicHeaderForStarlark() {
+    return convertListPairToTuple(getHeaders());
+  }
+
+  private Sequence<Tuple> convertListPairToTuple(List<Pair<Artifact, Label>> listPair) {
+    ImmutableList<Tuple> tupleList =
+        listPair.stream().map(p -> Tuple.pair(p.first, p.second)).collect(toImmutableList());
+    return StarlarkList.immutableCopyOf(tupleList);
+  }
+
   /**
    * Returns a list of ({@link Artifact}, {@link Label}) pairs. Each pair represents an input source
    * file and the label of the rule that generates it (or the label of the source file itself if it
@@ -335,9 +374,8 @@
     return result.build();
   }
 
-  /**
-   * Returns the C++ toolchain provider.
-   */
+  /** Returns the C++ toolchain provider. */
+  @StarlarkMethod(name = "toolchain", documented = false, structField = true)
   public CcToolchainProvider getToolchain() {
     return ccToolchain;
   }
@@ -355,6 +393,21 @@
     return getHeaders(ruleContext);
   }
 
+  @StarlarkMethod(
+      name = "report_invalid_options",
+      documented = false,
+      parameters = {
+        @Param(name = "ctx", positional = false, named = true),
+      })
+  public void reportInvalidOptionsForStarlark(StarlarkRuleContext starlarkRuleContext)
+      throws EvalException {
+    RuleContext ruleContext = starlarkRuleContext.getRuleContext();
+    reportInvalidOptions(ruleContext);
+    if (ruleContext.hasErrors()) {
+      throw new EvalException("Invalid options.");
+    }
+  }
+
   public void reportInvalidOptions(RuleContext ruleContext) {
     reportInvalidOptions(ruleContext, cppConfiguration, ccToolchain);
   }
@@ -388,10 +441,17 @@
         return null;
       }
 
+      TransitiveInfoCollection toolchain;
+      if (ruleContext.attributes().has(CcToolchain.CC_TOOLCHAIN_DEFAULT_ATTRIBUTE_NAME)) {
+        toolchain = ruleContext.getPrerequisite(CcToolchain.CC_TOOLCHAIN_DEFAULT_ATTRIBUTE_NAME);
+      } else {
+        toolchain =
+            ruleContext.getPrerequisite(
+                CcToolchain.CC_TOOLCHAIN_DEFAULT_ATTRIBUTE_NAME_FOR_STARLARK);
+      }
+
       try {
-        return CcCommon.computeCcFlags(
-            ruleContext,
-            ruleContext.getPrerequisite(CcToolchain.CC_TOOLCHAIN_DEFAULT_ATTRIBUTE_NAME));
+        return CcCommon.computeCcFlags(ruleContext, toolchain);
       } catch (RuleErrorException e) {
         throw new ExpansionException(e.getMessage());
       }
@@ -407,7 +467,7 @@
 
   /** A filter that removes copts from a c++ compile action according to a nocopts regex. */
   @AutoCodec
-  static class CoptsFilter {
+  public static class CoptsFilter implements StarlarkValue {
     private final Pattern noCoptsPattern;
     private final boolean allPasses;
 
@@ -441,7 +501,8 @@
   }
 
   /** Returns copts filter built from the make variable expanded nocopts attribute. */
-  CoptsFilter getCoptsFilter() {
+  @StarlarkMethod(name = "copts_filter", structField = true, documented = false)
+  public CoptsFilter getCoptsFilter() {
     return getCoptsFilter(ruleContext);
   }
 
@@ -482,18 +543,6 @@
     }
   }
 
-  // TODO(bazel-team): calculating nocopts every time is not very efficient,
-  // fix this after the rule migration. The problem is that in some cases we call this after
-  // the RCT is created (so RuleContext is not accessible), in some cases during the creation.
-  // It would probably make more sense to use TransitiveInfoProviders.
-  /**
-   * Returns true if the rule context has a nocopts regex that matches the given value, false
-   * otherwise.
-   */
-  static boolean noCoptsMatches(String option, RuleContext ruleContext) {
-    return !getCoptsFilter(ruleContext).passesFilter(option);
-  }
-
   private static final String DEFINES_ATTRIBUTE = "defines";
   private static final String LOCAL_DEFINES_ATTRIBUTE = "local_defines";
 
@@ -509,6 +558,11 @@
     return getDefinesFromAttribute(DEFINES_ATTRIBUTE);
   }
 
+  @StarlarkMethod(name = "defines", structField = true, documented = false)
+  public Sequence<String> getDefinesForStarlark() {
+    return StarlarkList.immutableCopyOf(getDefines());
+  }
+
   /**
    * Returns a list of define tokens from "local_defines" attribute.
    *
@@ -521,6 +575,11 @@
     return getDefinesFromAttribute(LOCAL_DEFINES_ATTRIBUTE);
   }
 
+  @StarlarkMethod(name = "local_defines", structField = true, documented = false)
+  public Sequence<String> getLocalDefinesForStarlark() {
+    return StarlarkList.immutableCopyOf(getNonTransitiveDefines());
+  }
+
   private List<String> getDefinesFromAttribute(String attr) {
     List<String> defines = new ArrayList<>();
 
@@ -558,6 +617,12 @@
     return defines;
   }
 
+  @StarlarkMethod(name = "loose_include_dirs", structField = true, documented = false)
+  public Sequence<String> getLooseIncludeDirsForStarlark() {
+    return StarlarkList.immutableCopyOf(
+        getLooseIncludeDirs().stream().map(PathFragment::toString).collect(toImmutableList()));
+  }
+
   /**
    * Determines a list of loose include directories that are only allowed to be referenced when
    * headers checking is {@link HeadersCheckingMode#LOOSE}.
@@ -590,6 +655,12 @@
     return result.build();
   }
 
+  @StarlarkMethod(name = "system_include_dirs", structField = true, documented = false)
+  public Sequence<String> getSystemIncludeDirsForStarlark() {
+    return StarlarkList.immutableCopyOf(
+        getSystemIncludeDirs().stream().map(PathFragment::toString).collect(toImmutableList()));
+  }
+
   List<PathFragment> getSystemIncludeDirs() {
     boolean siblingRepositoryLayout = ruleContext.getConfiguration().isSiblingRepositoryLayout();
     List<PathFragment> result = new ArrayList<>();
@@ -642,10 +713,11 @@
   static NestedSet<Artifact> collectCompilationPrerequisites(
       RuleContext ruleContext, CcCompilationContext ccCompilationContext) {
     // TODO(bazel-team): Use ccCompilationContext.getCompilationPrerequisites() instead; note
-    // that this will need cleaning up the prerequisites, as the {@code CcCompilationContext}
-    // currently
-    // collects them transitively (to get transitive headers), but source files are not transitive
-    // compilation
+    // that this
+    // will
+    // need cleaning up the prerequisites, as the {@code CcCompilationContext} currently
+    // collects them
+    // transitively (to get transitive headers), but source files are not transitive compilation
     // prerequisites.
     NestedSetBuilder<Artifact> prerequisites = NestedSetBuilder.stableOrder();
     if (ruleContext.attributes().has("srcs", BuildType.LABEL_LIST)) {
@@ -721,6 +793,23 @@
     return ruleContext.getPrerequisiteArtifact("$def_parser");
   }
 
+  @StarlarkMethod(
+      name = "instrumented_files_info",
+      documented = false,
+      parameters = {
+        @Param(name = "files", positional = false, named = true),
+        @Param(name = "with_base_line_coverage", positional = false, named = true),
+      })
+  public InstrumentedFilesInfo getInstrumentedFilesProviderForStarlark(
+      Sequence<?> files, boolean withBaselineCoverage) throws EvalException {
+    try {
+      return getInstrumentedFilesProvider(
+          Sequence.cast(files, Artifact.class, "files"), withBaselineCoverage);
+    } catch (RuleErrorException e) {
+      throw new EvalException(e);
+    }
+  }
+
   /** Provides support for instrumentation. */
   public InstrumentedFilesInfo getInstrumentedFilesProvider(
       Iterable<Artifact> files, boolean withBaselineCoverage) throws RuleErrorException {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcStarlarkInternal.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcStarlarkInternal.java
index eeddf34..8741d79 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcStarlarkInternal.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcStarlarkInternal.java
@@ -18,9 +18,11 @@
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.starlark.StarlarkRuleContext;
 import com.google.devtools.build.lib.collect.nestedset.Depset;
+import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
 import net.starlark.java.annot.Param;
 import net.starlark.java.annot.StarlarkBuiltin;
 import net.starlark.java.annot.StarlarkMethod;
+import net.starlark.java.eval.EvalException;
 import net.starlark.java.eval.StarlarkValue;
 
 /** Utility methods for Objc rules in Starlark Builtins */
@@ -43,4 +45,18 @@
         CcCommon.collectCompilationPrerequisites(
             starlarkRuleContext.getRuleContext(), compilationContext));
   }
+
+  @StarlarkMethod(
+      name = "create_common",
+      documented = false,
+      parameters = {
+        @Param(name = "ctx", positional = false, named = true),
+      })
+  public CcCommon createCommon(StarlarkRuleContext starlarkRuleContext) throws EvalException {
+    try {
+      return new CcCommon(starlarkRuleContext.getRuleContext());
+    } catch (RuleErrorException e) {
+      throw new EvalException(e);
+    }
+  }
 }