Add Starlark mechanism for default_copts and defaults_hdrs_check

We add a new native computed default class which can only be seen from builtins. This ensures that not only are we able to get the value of default_copts and default_hdrs_check as set in the package() rule, but that when the values in package() changes, the dependency server is able to compute the affected targets properly.

This is not a long term mechanism and should be replaced by something more generic as described in b/200065655#comment3

This CL also adds two additional methods to CcStarlarkInternal, init_make_variables and create_linkstamp.

RELNOTES:none
PiperOrigin-RevId: 415258162
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AspectAwareAttributeMapper.java b/src/main/java/com/google/devtools/build/lib/analysis/AspectAwareAttributeMapper.java
index c2c5041..6e5e797 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/AspectAwareAttributeMapper.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AspectAwareAttributeMapper.java
@@ -148,6 +148,11 @@
   }
 
   @Override
+  public boolean isPackageDefaultHdrsCheckSet() {
+    return ruleAttributes.isPackageDefaultHdrsCheckSet();
+  }
+
+  @Override
   public Boolean getPackageDefaultTestOnly() {
     return ruleAttributes.getPackageDefaultTestOnly();
   }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkAttrModule.java b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkAttrModule.java
index 6c2d9cd..b0270b5 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkAttrModule.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkAttrModule.java
@@ -41,6 +41,7 @@
 import com.google.devtools.build.lib.packages.Type;
 import com.google.devtools.build.lib.packages.Type.ConversionException;
 import com.google.devtools.build.lib.packages.Type.LabelClass;
+import com.google.devtools.build.lib.starlarkbuildapi.NativeComputedDefaultApi;
 import com.google.devtools.build.lib.starlarkbuildapi.StarlarkAttrModuleApi;
 import com.google.devtools.build.lib.util.FileType;
 import com.google.devtools.build.lib.util.FileTypeSet;
@@ -136,6 +137,11 @@
             new StarlarkComputedDefaultTemplate(type, callback.getParameterNames(), callback));
       } else if (defaultValue instanceof StarlarkLateBoundDefault) {
         builder.value((StarlarkLateBoundDefault) defaultValue); // unchecked cast
+      } else if (defaultValue instanceof NativeComputedDefaultApi) {
+        // TODO(b/200065655#comment3): This hack exists until default_copts and default_hdrs_check
+        //  in package() is replaced by proper package defaults. We don't check the particular
+        //  instance to avoid adding a dependency to the C++ package.
+        builder.value((NativeComputedDefaultApi) defaultValue);
       } else {
         BazelModuleContext moduleContext =
             BazelModuleContext.of(Module.ofInnermostEnclosingStarlarkFunction(thread));
@@ -420,11 +426,7 @@
 
   @Override
   public Descriptor stringAttribute(
-      String defaultValue,
-      String doc,
-      Boolean mandatory,
-      Sequence<?> values,
-      StarlarkThread thread)
+      Object defaultValue, String doc, Boolean mandatory, Sequence<?> values, StarlarkThread thread)
       throws EvalException {
     BazelStarlarkContext.from(thread).checkLoadingOrWorkspacePhase("attr.string");
     return createAttrDescriptor(
@@ -497,11 +499,7 @@
 
   @Override
   public Descriptor stringListAttribute(
-      Boolean mandatory,
-      Boolean allowEmpty,
-      Sequence<?> defaultValue,
-      String doc,
-      StarlarkThread thread)
+      Boolean mandatory, Boolean allowEmpty, Object defaultValue, String doc, StarlarkThread thread)
       throws EvalException {
     BazelStarlarkContext.from(thread).checkLoadingOrWorkspacePhase("attr.string_list");
     return createAttrDescriptor(
diff --git a/src/main/java/com/google/devtools/build/lib/packages/AbstractAttributeMapper.java b/src/main/java/com/google/devtools/build/lib/packages/AbstractAttributeMapper.java
index 60a28ec..4c34b48 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/AbstractAttributeMapper.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/AbstractAttributeMapper.java
@@ -148,6 +148,11 @@
   }
 
   @Override
+  public boolean isPackageDefaultHdrsCheckSet() {
+    return rule.getPackage().isDefaultHdrsCheckSet();
+  }
+
+  @Override
   public Boolean getPackageDefaultTestOnly() {
     return rule.getPackage().getDefaultTestOnly();
   }
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
index 5b673e8..558ab43 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
@@ -41,6 +41,7 @@
 import com.google.devtools.build.lib.packages.Type.LabelClass;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationConstant;
+import com.google.devtools.build.lib.starlarkbuildapi.NativeComputedDefaultApi;
 import com.google.devtools.build.lib.util.FileType;
 import com.google.devtools.build.lib.util.FileTypeSet;
 import com.google.devtools.build.lib.util.StringUtil;
@@ -554,34 +555,6 @@
     }
 
     /**
-     * See value(TYPE) above. This method is only meant for Starlark usage.
-     *
-     * <p>The parameter {@code context} is relevant iff the default value is a Label string. In this
-     * case, {@code context} must point to the parent Label in order to be able to convert the
-     * default value string to a proper Label.
-     *
-     * @param parameterName The name of the attribute to use in error messages
-     */
-    public Builder<TYPE> defaultValue(
-        Object defaultValue, Object context, @Nullable String parameterName)
-        throws ConversionException {
-      Preconditions.checkState(!valueSet, "the default value is already set");
-      value =
-          type.convert(
-              defaultValue,
-              ((parameterName == null) ? "" : String.format("parameter '%s' of ", parameterName))
-                  + String.format("attribute '%s'", name),
-              context);
-      valueSet = true;
-      return this;
-    }
-
-    /** See value(TYPE) above. This method is only meant for Starlark usage. */
-    public Builder<TYPE> defaultValue(Object defaultValue) throws ConversionException {
-      return defaultValue(defaultValue, null, null);
-    }
-
-    /**
      * Sets the attribute default value to a computed default value - use this when the default
      * value is a function of other attributes of the Rule. The type of the computed default value
      * for a mandatory attribute must match the type parameter: (e.g. list=[], integer=0, string="",
@@ -599,6 +572,15 @@
       return this;
     }
 
+    /** Used for b/200065655#comment3. */
+    public Builder<TYPE> value(NativeComputedDefaultApi defaultValue) {
+      Preconditions.checkState(!valueSet, "the default value is already set");
+      value = defaultValue;
+      valueSource = AttributeValueSource.NATIVE_COMPUTED_DEFAULT;
+      valueSet = true;
+      return this;
+    }
+
     /**
      * Sets the attribute default value to a Starlark computed default template. Like a native
      * Computed Default, this allows a Starlark-defined Rule Class to specify that the default value
@@ -633,6 +615,34 @@
       return this;
     }
 
+    /**
+     * See value(TYPE) above. This method is only meant for Starlark usage.
+     *
+     * <p>The parameter {@code context} is relevant iff the default value is a Label string. In this
+     * case, {@code context} must point to the parent Label in order to be able to convert the
+     * default value string to a proper Label.
+     *
+     * @param parameterName The name of the attribute to use in error messages
+     */
+    public Builder<TYPE> defaultValue(
+        Object defaultValue, Object context, @Nullable String parameterName)
+        throws ConversionException {
+      Preconditions.checkState(!valueSet, "the default value is already set");
+      value =
+          type.convert(
+              defaultValue,
+              ((parameterName == null) ? "" : String.format("parameter '%s' of ", parameterName))
+                  + String.format("attribute '%s'", name),
+              context);
+      valueSet = true;
+      return this;
+    }
+
+    /** See value(TYPE) above. This method is only meant for Starlark usage. */
+    public Builder<TYPE> defaultValue(Object defaultValue) throws ConversionException {
+      return defaultValue(defaultValue, null, null);
+    }
+
     /** Returns where the value of this attribute comes from. Useful only for Starlark. */
     public AttributeValueSource getValueSource() {
       return valueSource;
diff --git a/src/main/java/com/google/devtools/build/lib/packages/AttributeMap.java b/src/main/java/com/google/devtools/build/lib/packages/AttributeMap.java
index ffac418..cc27242 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/AttributeMap.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/AttributeMap.java
@@ -142,6 +142,8 @@
   // a more generic interface.
   String getPackageDefaultHdrsCheck();
 
+  boolean isPackageDefaultHdrsCheckSet();
+
   Boolean getPackageDefaultTestOnly();
 
   String getPackageDefaultDeprecation();
diff --git a/src/main/java/com/google/devtools/build/lib/packages/AttributeValueSource.java b/src/main/java/com/google/devtools/build/lib/packages/AttributeValueSource.java
index 7e013b7..683e02c 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/AttributeValueSource.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/AttributeValueSource.java
@@ -21,6 +21,7 @@
  * from.
  */
 public enum AttributeValueSource {
+  NATIVE_COMPUTED_DEFAULT("$", false),
   COMPUTED_DEFAULT("$", true),
   LATE_BOUND(":", true),
   DIRECT("$", false);
diff --git a/src/main/java/com/google/devtools/build/lib/packages/DelegatingAttributeMapper.java b/src/main/java/com/google/devtools/build/lib/packages/DelegatingAttributeMapper.java
index 4b27dff..165d87f 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/DelegatingAttributeMapper.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/DelegatingAttributeMapper.java
@@ -99,6 +99,11 @@
   }
 
   @Override
+  public boolean isPackageDefaultHdrsCheckSet() {
+    return delegate.isPackageDefaultHdrsCheckSet();
+  }
+
+  @Override
   public Boolean getPackageDefaultTestOnly() {
     return delegate.getPackageDefaultTestOnly();
   }
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 2ca71d6..5e3a520 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
@@ -14,18 +14,28 @@
 
 package com.google.devtools.build.lib.rules.cpp;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.docgen.annot.DocCategory;
 import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.CommandLineExpansionException;
+import com.google.devtools.build.lib.analysis.MakeVariableSupplier.MapBackedMakeVariableSupplier;
+import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.StaticallyLinkedMarkerProvider;
 import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
 import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue;
 import com.google.devtools.build.lib.analysis.configuredtargets.PackageGroupConfiguredTarget;
+import com.google.devtools.build.lib.analysis.starlark.StarlarkActionFactory;
 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.Attribute.ComputedDefault;
+import com.google.devtools.build.lib.packages.AttributeMap;
 import com.google.devtools.build.lib.packages.Provider;
 import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
 import com.google.devtools.build.lib.rules.cpp.CcBinary.CcLauncherInfo;
+import com.google.devtools.build.lib.rules.cpp.CcCommon.CcFlagsSupplier;
+import com.google.devtools.build.lib.rules.cpp.CcLinkingContext.Linkstamp;
 import com.google.devtools.build.lib.starlarkbuildapi.FileApi;
+import com.google.devtools.build.lib.starlarkbuildapi.NativeComputedDefaultApi;
 import com.google.devtools.build.lib.starlarkbuildapi.core.ProviderApi;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import net.starlark.java.annot.Param;
@@ -183,6 +193,23 @@
   }
 
   @StarlarkMethod(
+      name = "init_make_variables",
+      documented = false,
+      parameters = {
+        @Param(name = "ctx", positional = false, named = true),
+        @Param(name = "cc_toolchain", positional = false, named = true),
+      })
+  public void initMakeVariables(
+      StarlarkRuleContext starlarkRuleContext, CcToolchainProvider ccToolchain) {
+    ImmutableMap.Builder<String, String> toolchainMakeVariables = ImmutableMap.builder();
+    ccToolchain.addGlobalMakeVariables(toolchainMakeVariables);
+    RuleContext ruleContext = starlarkRuleContext.getRuleContext();
+    ruleContext.initConfigurationMakeVariableContext(
+        new MapBackedMakeVariableSupplier(toolchainMakeVariables.buildOrThrow()),
+        new CcFlagsSupplier(starlarkRuleContext.getRuleContext()));
+  }
+
+  @StarlarkMethod(
       name = "get_build_info",
       documented = false,
       parameters = {@Param(name = "ctx")})
@@ -197,6 +224,65 @@
     return CcLauncherInfo.PROVIDER;
   }
 
+  @StarlarkMethod(
+      name = "create_linkstamp",
+      documented = false,
+      parameters = {
+        @Param(name = "actions", positional = false, named = true),
+        @Param(name = "linkstamp", positional = false, named = true),
+        @Param(name = "compilation_context", positional = false, named = true),
+      })
+  public Linkstamp createLinkstamp(
+      StarlarkActionFactory starlarkActionFactoryApi,
+      Artifact linkstamp,
+      CcCompilationContext ccCompilationContext)
+      throws EvalException {
+    try {
+      return new Linkstamp( // throws InterruptedException
+          linkstamp,
+          ccCompilationContext.getDeclaredIncludeSrcs(),
+          starlarkActionFactoryApi.getActionConstructionContext().getActionKeyContext());
+    } catch (CommandLineExpansionException | InterruptedException ex) {
+      throw new EvalException(ex);
+    }
+  }
+
+  static class DefaultCoptsBuiltinComputedDefault extends ComputedDefault
+      implements NativeComputedDefaultApi {
+    @Override
+    public Object getDefault(AttributeMap rule) {
+      return rule.getPackageDefaultCopts();
+    }
+
+    @Override
+    public boolean resolvableWithRawAttributes() {
+      return true;
+    }
+  }
+
+  @StarlarkMethod(name = "default_copts_computed_default", documented = false)
+  public ComputedDefault getDefaultCoptsComputedDefault() {
+    return new DefaultCoptsBuiltinComputedDefault();
+  }
+
+  static class DefaultHdrsCheckBuiltinComputedDefault extends ComputedDefault
+      implements NativeComputedDefaultApi {
+    @Override
+    public Object getDefault(AttributeMap rule) {
+      return rule.isPackageDefaultHdrsCheckSet() ? rule.getPackageDefaultHdrsCheck() : "";
+    }
+
+    @Override
+    public boolean resolvableWithRawAttributes() {
+      return true;
+    }
+  }
+
+  @StarlarkMethod(name = "default_hdrs_check_computed_default", documented = false)
+  public ComputedDefault getDefaultHdrsCheckComputedDefault() {
+    return new DefaultHdrsCheckBuiltinComputedDefault();
+  }
+
   // TODO(b/207761932): perhaps move this to another internal module
   @StarlarkMethod(
       name = "declare_shareable_artifact",
diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/NativeComputedDefaultApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/NativeComputedDefaultApi.java
new file mode 100644
index 0000000..867e856
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/NativeComputedDefaultApi.java
@@ -0,0 +1,26 @@
+// 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.starlarkbuildapi;
+
+import com.google.devtools.build.docgen.annot.DocCategory;
+import net.starlark.java.annot.StarlarkBuiltin;
+import net.starlark.java.eval.StarlarkValue;
+
+/**
+ * The interface for native computed default in Starlark. This will go away once proper package
+ * defaults exist.
+ */
+@StarlarkBuiltin(name = "NativeComputedDefault", category = DocCategory.BUILTIN, documented = false)
+public interface NativeComputedDefaultApi extends StarlarkValue {}
diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkAttrModuleApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkAttrModuleApi.java
index 0e7dc1e..5edd471 100644
--- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkAttrModuleApi.java
+++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkAttrModuleApi.java
@@ -188,6 +188,10 @@
             name = DEFAULT_ARG,
             defaultValue = "''",
             doc = DEFAULT_DOC,
+            allowedTypes = {
+              @ParamType(type = String.class),
+              @ParamType(type = NativeComputedDefaultApi.class)
+            },
             named = true,
             positional = false),
         @Param(
@@ -214,7 +218,7 @@
       },
       useStarlarkThread = true)
   Descriptor stringAttribute(
-      String defaultValue, String doc, Boolean mandatory, Sequence<?> values, StarlarkThread thread)
+      Object defaultValue, String doc, Boolean mandatory, Sequence<?> values, StarlarkThread thread)
       throws EvalException;
 
   @StarlarkMethod(
@@ -360,7 +364,10 @@
         @Param(name = ALLOW_EMPTY_ARG, defaultValue = "True", doc = ALLOW_EMPTY_DOC, named = true),
         @Param(
             name = DEFAULT_ARG,
-            allowedTypes = {@ParamType(type = Sequence.class, generic1 = String.class)},
+            allowedTypes = {
+              @ParamType(type = Sequence.class, generic1 = String.class),
+              @ParamType(type = NativeComputedDefaultApi.class)
+            },
             defaultValue = "[]",
             doc = DEFAULT_DOC,
             named = true,
@@ -369,11 +376,7 @@
       },
       useStarlarkThread = true)
   Descriptor stringListAttribute(
-      Boolean mandatory,
-      Boolean allowEmpty,
-      Sequence<?> defaultValue,
-      String doc,
-      StarlarkThread thread)
+      Boolean mandatory, Boolean allowEmpty, Object defaultValue, String doc, StarlarkThread thread)
       throws EvalException;
 
   @StarlarkMethod(
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeStarlarkAttrModuleApi.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeStarlarkAttrModuleApi.java
index ee4af2c..20a7785 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeStarlarkAttrModuleApi.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeStarlarkAttrModuleApi.java
@@ -47,7 +47,7 @@
 
   @Override
   public Descriptor stringAttribute(
-      String defaultString,
+      Object defaultString,
       String doc,
       Boolean mandatory,
       Sequence<?> values,
@@ -85,11 +85,7 @@
 
   @Override
   public Descriptor stringListAttribute(
-      Boolean mandatory,
-      Boolean allowEmpty,
-      Sequence<?> defaultList,
-      String doc,
-      StarlarkThread thread)
+      Boolean mandatory, Boolean allowEmpty, Object defaultList, String doc, StarlarkThread thread)
       throws EvalException {
     return new FakeDescriptor(
         AttributeType.STRING_LIST, doc, mandatory, ImmutableList.of(), defaultList);
diff --git a/src/main/java/net/starlark/java/eval/ParamDescriptor.java b/src/main/java/net/starlark/java/eval/ParamDescriptor.java
index 5f66f1f..23f7e1b 100644
--- a/src/main/java/net/starlark/java/eval/ParamDescriptor.java
+++ b/src/main/java/net/starlark/java/eval/ParamDescriptor.java
@@ -15,6 +15,7 @@
 package net.starlark.java.eval;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
@@ -105,11 +106,16 @@
     // "a or b"
     // "a, b, or c"
     StringBuilder buf = new StringBuilder();
-    for (int i = 0, n = allowedClasses.size(); i < n; i++) {
+    // TODO(b/200065655#comment3): Remove when we have an official way for package defaults.
+    ImmutableList<Class<?>> allowedClassesFiltered =
+        allowedClasses.stream()
+            .filter(x -> !Starlark.classType(x).equals("NativeComputedDefault"))
+            .collect(ImmutableList.toImmutableList());
+    for (int i = 0, n = allowedClassesFiltered.size(); i < n; i++) {
       if (i > 0) {
         buf.append(n == 2 ? " or " : i < n - 1 ? ", " : ", or ");
       }
-      buf.append(Starlark.classType(allowedClasses.get(i)));
+      buf.append(Starlark.classType(allowedClassesFiltered.get(i)));
     }
     return buf.toString();
   }
diff --git a/src/test/java/com/google/devtools/build/lib/testutil/FakeAttributeMapper.java b/src/test/java/com/google/devtools/build/lib/testutil/FakeAttributeMapper.java
index 0201e2a..39b75b2 100644
--- a/src/test/java/com/google/devtools/build/lib/testutil/FakeAttributeMapper.java
+++ b/src/test/java/com/google/devtools/build/lib/testutil/FakeAttributeMapper.java
@@ -122,6 +122,11 @@
   }
 
   @Override
+  public boolean isPackageDefaultHdrsCheckSet() {
+    return false;
+  }
+
+  @Override
   public Boolean getPackageDefaultTestOnly() {
     return false;
   }