Expose DebugPackageProvider to Starlark as DebugPackageInfo

PiperOrigin-RevId: 334455669
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/CcRules.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/CcRules.java
index eef35d4..316c5ad 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/CcRules.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/CcRules.java
@@ -37,6 +37,7 @@
 import com.google.devtools.build.lib.rules.cpp.CppRuleClasses.CcIncludeScanningRule;
 import com.google.devtools.build.lib.rules.cpp.CppRuleClasses.CcLinkingRule;
 import com.google.devtools.build.lib.rules.cpp.CpuTransformer;
+import com.google.devtools.build.lib.rules.cpp.DebugPackageProvider;
 import com.google.devtools.build.lib.rules.cpp.FdoPrefetchHintsRule;
 import com.google.devtools.build.lib.rules.cpp.FdoProfileRule;
 import com.google.devtools.build.lib.rules.cpp.GoogleLegacyStubs;
@@ -89,6 +90,7 @@
         new CcBootstrap(
             new BazelCcModule(),
             CcInfo.PROVIDER,
+            DebugPackageProvider.PROVIDER,
             CcToolchainConfigInfo.PROVIDER,
             new GoogleLegacyStubs.PyWrapCcHelper(),
             new GoogleLegacyStubs.GoWrapCcHelper(),
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
index b588fa2..30b3b2e 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
@@ -661,8 +661,7 @@
     CcStarlarkApiProvider.maybeAdd(ruleContext, ruleBuilder);
     ruleBuilder
         .addProvider(RunfilesProvider.class, RunfilesProvider.simple(runfiles))
-        .addProvider(
-            DebugPackageProvider.class,
+        .addNativeDeclaredProvider(
             new DebugPackageProvider(ruleContext.getLabel(), strippedFile, binary, explicitDwpFile))
         .setRunfilesSupport(runfilesSupport, binary)
         .addNativeDeclaredProvider(ccLauncherInfo);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/DebugPackageProvider.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/DebugPackageProvider.java
index f287c5e..d610338 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/DebugPackageProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/DebugPackageProvider.java
@@ -16,11 +16,15 @@
 
 import com.google.common.base.Preconditions;
 import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.BuiltinProvider;
+import com.google.devtools.build.lib.packages.NativeInfo;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
+import com.google.devtools.build.lib.starlarkbuildapi.cpp.DebugPackageInfoApi;
 import javax.annotation.Nullable;
+import net.starlark.java.eval.EvalException;
+import net.starlark.java.eval.Starlark;
 
 /**
  * Provides the binary artifact and its associated .dwp files, if fission is enabled. If Fission
@@ -28,7 +32,10 @@
  */
 @Immutable
 @AutoCodec
-public final class DebugPackageProvider implements TransitiveInfoProvider {
+public final class DebugPackageProvider extends NativeInfo
+    implements DebugPackageInfoApi<Artifact> {
+  public static final Provider PROVIDER = new Provider();
+
   private final Label targetLabel;
   private final Artifact strippedArtifact;
   private final Artifact unstrippedArtifact;
@@ -40,6 +47,7 @@
       @Nullable Artifact strippedArtifact,
       Artifact unstrippedArtifact,
       @Nullable Artifact dwpArtifact) {
+    super(PROVIDER);
     Preconditions.checkNotNull(unstrippedArtifact);
     this.targetLabel = targetLabel;
     this.strippedArtifact = strippedArtifact;
@@ -48,30 +56,54 @@
   }
 
   /** Returns the label for the *_binary target. */
+  @Override
   public final Label getTargetLabel() {
     return targetLabel;
   }
 
-  /**
-   * Returns the stripped file (the explicit ".stripped" target).
-   */
+  /** Returns the stripped file (the explicit ".stripped" target). */
+  @Override
   public final Artifact getStrippedArtifact() {
     return strippedArtifact;
   }
 
-  /**
-   * Returns the unstripped file (the default executable target).
-   */
+  /** Returns the unstripped file (the default executable target). */
+  @Override
   public final Artifact getUnstrippedArtifact() {
     return unstrippedArtifact;
   }
 
-  /**
-   * Returns the .dwp file (for fission builds) or null if --fission=no.
-   */
+  /** Returns the .dwp file (for fission builds) or null if --fission=no. */
   @Nullable
+  @Override
   public final Artifact getDwpArtifact() {
     return dwpArtifact;
   }
 
+  /** Provider class for {@link DebugPackageProvider} objects. */
+  public static class Provider extends BuiltinProvider<DebugPackageProvider>
+      implements DebugPackageInfoApi.Provider<Artifact> {
+    private Provider() {
+      super(DebugPackageInfoApi.NAME, DebugPackageProvider.class);
+    }
+
+    @Override
+    public DebugPackageProvider createDebugPackageInfo(
+        Label starlarkTargetLabel,
+        Artifact starlarkStrippedArtifact,
+        Artifact starlarkUnstrippedArtifact,
+        Object starlarkDwpArtifact)
+        throws EvalException {
+      return new DebugPackageProvider(
+          starlarkTargetLabel,
+          starlarkStrippedArtifact,
+          starlarkUnstrippedArtifact,
+          nullIfNone(starlarkDwpArtifact, Artifact.class));
+    }
+
+    @Nullable
+    private static <T> T nullIfNone(Object object, Class<T> type) {
+      return object != Starlark.NONE ? type.cast(object) : null;
+    }
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcBootstrap.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcBootstrap.java
index 81a332b..2db806a 100644
--- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcBootstrap.java
+++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcBootstrap.java
@@ -43,6 +43,7 @@
       ccModule;
 
   private final CcInfoApi.Provider<? extends FileApi> ccInfoProvider;
+  private final DebugPackageInfoApi.Provider<? extends FileApi> debugPackageInfoProvider;
   private final CcToolchainConfigInfoApi.Provider ccToolchainConfigInfoProvider;
   private final PyWrapCcHelperApi<?, ?, ?, ?, ?, ?, ?, ?, ?> pyWrapCcHelper;
   private final GoWrapCcHelperApi<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?> goWrapCcHelper;
@@ -67,6 +68,7 @@
               ? extends CcCompilationOutputsApi<? extends FileApi>>
           ccModule,
       CcInfoApi.Provider<? extends FileApi> ccInfoProvider,
+      DebugPackageInfoApi.Provider<? extends FileApi> debugPackageInfoProvider,
       CcToolchainConfigInfoApi.Provider ccToolchainConfigInfoProvider,
       PyWrapCcHelperApi<?, ?, ?, ?, ?, ?, ?, ?, ?> pyWrapCcHelper,
       GoWrapCcHelperApi<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?> goWrapCcHelper,
@@ -74,6 +76,7 @@
       PyCcLinkParamsProviderApi.Provider pyCcLinkInfoParamsInfoProvider) {
     this.ccModule = ccModule;
     this.ccInfoProvider = ccInfoProvider;
+    this.debugPackageInfoProvider = debugPackageInfoProvider;
     this.ccToolchainConfigInfoProvider = ccToolchainConfigInfoProvider;
     this.pyWrapCcHelper = pyWrapCcHelper;
     this.goWrapCcHelper = goWrapCcHelper;
@@ -85,6 +88,7 @@
   public void addBindingsToBuilder(ImmutableMap.Builder<String, Object> builder) {
     builder.put("cc_common", ccModule);
     builder.put("CcInfo", ccInfoProvider);
+    builder.put("DebugPackageInfo", debugPackageInfoProvider);
     builder.put("CcToolchainConfigInfo", ccToolchainConfigInfoProvider);
     builder.put(
         "py_wrap_cc_helper_do_not_use",
diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/DebugPackageInfoApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/DebugPackageInfoApi.java
new file mode 100644
index 0000000..8612838
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/DebugPackageInfoApi.java
@@ -0,0 +1,110 @@
+// 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.starlarkbuildapi.cpp;
+
+import com.google.devtools.build.docgen.annot.DocCategory;
+import com.google.devtools.build.docgen.annot.StarlarkConstructor;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.starlarkbuildapi.FileApi;
+import com.google.devtools.build.lib.starlarkbuildapi.core.ProviderApi;
+import com.google.devtools.build.lib.starlarkbuildapi.core.StructApi;
+import net.starlark.java.annot.Param;
+import net.starlark.java.annot.ParamType;
+import net.starlark.java.annot.StarlarkBuiltin;
+import net.starlark.java.annot.StarlarkMethod;
+import net.starlark.java.eval.EvalException;
+import net.starlark.java.eval.NoneType;
+
+/** Wrapper for the native DebugPackageProvider. */
+@StarlarkBuiltin(
+    name = "DebugPackageInfo",
+    category = DocCategory.PROVIDER,
+    doc =
+        "A provider for the binary file and its associated .dwp files, if fission is enabled."
+            + "If Fission ({@url https://gcc.gnu.org/wiki/DebugFission}) is not enabled, the dwp "
+            + "file will be null.")
+public interface DebugPackageInfoApi<FileT extends FileApi> extends StructApi {
+  String NAME = "DebugPackageInfo";
+
+  @StarlarkMethod(
+      name = "target_label",
+      doc = "Returns the label for the *_binary target",
+      structField = true)
+  Label getTargetLabel();
+
+  @StarlarkMethod(
+      name = "stripped_file",
+      doc = "Returns the stripped file (the explicit \".stripped\" target).",
+      structField = true)
+  FileT getStrippedArtifact();
+
+  @StarlarkMethod(
+      name = "unstripped_file",
+      doc = "Returns the unstripped file (the default executable target)",
+      structField = true)
+  FileT getUnstrippedArtifact();
+
+  @StarlarkMethod(
+      name = "dwp_file",
+      doc = "Returns the .dwp file (for fission builds) or null if --fission=no.",
+      structField = true,
+      allowReturnNones = true)
+  FileT getDwpArtifact();
+
+  /** The provider implementing this can construct DebugPackageInfo objects. */
+  @StarlarkBuiltin(name = "Provider", doc = "", documented = false)
+  interface Provider<FileT extends FileApi> extends ProviderApi {
+
+    @StarlarkMethod(
+        name = NAME,
+        doc = "The <code>DebugPackageInfo</code> constructor.",
+        parameters = {
+          @Param(
+              name = "target_label",
+              doc = "The label for the *_binary target",
+              positional = false,
+              named = true,
+              noneable = false,
+              allowedTypes = {@ParamType(type = Label.class)}),
+          @Param(
+              name = "stripped_file",
+              doc = "The stripped file (the explicit \".stripped\" target)",
+              positional = false,
+              named = true,
+              noneable = false,
+              allowedTypes = {@ParamType(type = FileApi.class)}),
+          @Param(
+              name = "unstripped_file",
+              doc = "The unstripped file (the default executable target).",
+              positional = false,
+              named = true,
+              noneable = false,
+              allowedTypes = {@ParamType(type = FileApi.class)}),
+          @Param(
+              name = "dwp_file",
+              doc = "The .dwp file (for fission builds) or null if --fission=no.",
+              positional = false,
+              named = true,
+              noneable = true,
+              defaultValue = "None",
+              allowedTypes = {@ParamType(type = FileApi.class), @ParamType(type = NoneType.class)})
+        },
+        selfCall = true)
+    @StarlarkConstructor
+    DebugPackageInfoApi<FileT> createDebugPackageInfo(
+        Label targetLabel, FileT strippedFile, FileT unstrippedFile, Object dwpFile)
+        throws EvalException;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeApi.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeApi.java
index 11ea82d..2d020f5 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeApi.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeApi.java
@@ -56,6 +56,7 @@
 import com.google.devtools.build.skydoc.fakebuildapi.cpp.FakeCcInfo;
 import com.google.devtools.build.skydoc.fakebuildapi.cpp.FakeCcModule;
 import com.google.devtools.build.skydoc.fakebuildapi.cpp.FakeCcToolchainConfigInfo;
+import com.google.devtools.build.skydoc.fakebuildapi.cpp.FakeDebugPackageInfo;
 import com.google.devtools.build.skydoc.fakebuildapi.cpp.FakeGoWrapCcHelper;
 import com.google.devtools.build.skydoc.fakebuildapi.cpp.FakePyCcLinkParamsProvider;
 import com.google.devtools.build.skydoc.fakebuildapi.cpp.FakePyWrapCcHelper;
@@ -151,6 +152,7 @@
     new CcBootstrap(
             new FakeCcModule(),
             new FakeCcInfo.Provider(),
+            new FakeDebugPackageInfo.Provider(),
             new FakeCcToolchainConfigInfo.Provider(),
             new FakePyWrapCcHelper(),
             new FakeGoWrapCcHelper(),
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/cpp/FakeDebugPackageInfo.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/cpp/FakeDebugPackageInfo.java
new file mode 100644
index 0000000..084e116
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/cpp/FakeDebugPackageInfo.java
@@ -0,0 +1,73 @@
+// 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.cpp;
+
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.starlarkbuildapi.FileApi;
+import com.google.devtools.build.lib.starlarkbuildapi.cpp.DebugPackageInfoApi;
+import net.starlark.java.eval.EvalException;
+import net.starlark.java.eval.Printer;
+
+/** Fake implementation of {@link DebugPackageInfoApi}. */
+public class FakeDebugPackageInfo implements DebugPackageInfoApi<FileApi> {
+
+  @Override
+  public Label getTargetLabel() {
+    return null;
+  }
+
+  @Override
+  public FileApi getStrippedArtifact() {
+    return null;
+  }
+
+  @Override
+  public FileApi getUnstrippedArtifact() {
+    return null;
+  }
+
+  /** Returns the .dwp file (for fission builds) or null if --fission=no. */
+  @Override
+  public final FileApi getDwpArtifact() {
+    return null;
+  }
+
+  @Override
+  public String toProto() throws EvalException {
+    return null;
+  }
+
+  @Override
+  public String toJson() throws EvalException {
+    return null;
+  }
+
+  @Override
+  public void repr(Printer printer) {}
+
+  /** Fake implementation of {@link DebugPackageInfoApi.Provider}. */
+  public static class Provider implements DebugPackageInfoApi.Provider<FileApi> {
+
+    @Override
+    public DebugPackageInfoApi<FileApi> createDebugPackageInfo(
+        Label targetLabel, FileApi strippedArtifact, FileApi unstrippedArtifact, Object dwpArtifact)
+        throws EvalException {
+      return new FakeDebugPackageInfo();
+    }
+
+    @Override
+    public void repr(Printer printer) {}
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/StarlarkCcCommonTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/StarlarkCcCommonTest.java
index 089d523..ceef382 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/StarlarkCcCommonTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/StarlarkCcCommonTest.java
@@ -6556,4 +6556,109 @@
         "//b:import_objects_no_pic_lib",
         "If you pass 'pic_objects' you must also pass a 'pic_static_library'");
   }
+
+  private void setupDebugPackageProviderTest(String fission) throws Exception {
+    getAnalysisMock()
+        .ccSupport()
+        .setupCcToolchainConfig(
+            mockToolsConfig,
+            CcToolchainConfig.builder().withFeatures(CppRuleClasses.PER_OBJECT_DEBUG_INFO));
+    useConfiguration(fission);
+    scratch.file(
+        "a/rule.bzl",
+        "def _impl(ctx):",
+        "    out = ctx.actions.declare_file(ctx.label.name)",
+        "    ctx.actions.run_shell(",
+        "        inputs = [ctx.executable.cc_binary],",
+        "        tools = [],",
+        "        outputs = [out],",
+        "        command = 'cp %s %s' % (ctx.executable.cc_binary.path, out.path),",
+        "    )",
+        "    wrapped_defaultinfo = ctx.attr.cc_binary[DefaultInfo]",
+        "    runfiles = ctx.runfiles(files = [out])",
+        "    wrapped_default_runfiles = wrapped_defaultinfo.default_runfiles.files.to_list()",
+        "    if ctx.executable.cc_binary in wrapped_default_runfiles:",
+        "        wrapped_default_runfiles.remove(ctx.executable.cc_binary)",
+        "    result = [",
+        "        DefaultInfo(",
+        "            executable = out,",
+        "            files = depset([out]),",
+        "            runfiles = runfiles.merge(ctx.runfiles(files = wrapped_default_runfiles)),",
+        "        ),",
+        "    ]",
+        "    if ctx.file.stripped_file:",
+        "        wrapped_dbginfo = ctx.attr.cc_binary[DebugPackageInfo]",
+        "        result.append(",
+        "            DebugPackageInfo(",
+        "                target_label = ctx.label,",
+        "                stripped_file = ctx.file.stripped_file \\",
+        "                                if wrapped_dbginfo.stripped_file else None,",
+        "                unstripped_file = out,",
+        "                dwp_file = ctx.file.dwp_file if wrapped_dbginfo.dwp_file else None,",
+        "            ),",
+        "        )",
+        "    return result",
+        "wrapped_binary = rule(",
+        "    _impl,",
+        "    attrs = {",
+        "        'cc_binary': attr.label(",
+        "            allow_single_file = True,",
+        "            mandatory = True,",
+        "            executable = True,",
+        "            cfg = 'target',",
+        "        ),",
+        "        'stripped_file': attr.label(",
+        "            allow_single_file = True,",
+        "            default = None,",
+        "        ),",
+        "        'dwp_file': attr.label(",
+        "            allow_single_file = True,",
+        "            default = None,",
+        "        )",
+        "    },",
+        "    executable = True,",
+        ")");
+    scratch.file(
+        "a/BUILD",
+        "load(':rule.bzl', 'wrapped_binary')",
+        "wrapped_binary(name = 'w',",
+        "    cc_binary = ':native_binary',",
+        "    stripped_file = ':w.stripped',",
+        "    dwp_file = ':w.dwp',",
+        ")",
+        "wrapped_binary(name = 'w.stripped',",
+        "    cc_binary = ':native_binary.stripped'",
+        ")",
+        "wrapped_binary(name = 'w.dwp',",
+        "    cc_binary = ':native_binary.dwp'",
+        ")",
+        "cc_binary(name = 'native_binary',",
+        "    srcs = ['main.cc']",
+        ")");
+    scratch.file("a/main.cc", "int main() {}");
+  }
+
+  @Test
+  public void testDebugPackageProviderFissionDisabled() throws Exception {
+    setupDebugPackageProviderTest("--fission=no");
+    ConfiguredTarget target = getConfiguredTarget("//a:w");
+    assertNoEvents();
+    assertThat(target).isNotNull();
+    DebugPackageProvider debugPackageProvider = target.get(DebugPackageProvider.PROVIDER);
+    assertThat(debugPackageProvider.getStrippedArtifact().getFilename()).isEqualTo("w.stripped");
+    assertThat(debugPackageProvider.getUnstrippedArtifact().getFilename()).isEqualTo("w");
+    assertThat(debugPackageProvider.getDwpArtifact()).isNull();
+  }
+
+  @Test
+  public void testDebugPackageProviderFissionEnabled() throws Exception {
+    setupDebugPackageProviderTest("--fission=yes");
+    ConfiguredTarget target = getConfiguredTarget("//a:w");
+    assertNoEvents();
+    assertThat(target).isNotNull();
+    DebugPackageProvider debugPackageProvider = target.get(DebugPackageProvider.PROVIDER);
+    assertThat(debugPackageProvider.getStrippedArtifact().getFilename()).isEqualTo("w.stripped");
+    assertThat(debugPackageProvider.getUnstrippedArtifact().getFilename()).isEqualTo("w");
+    assertThat(debugPackageProvider.getDwpArtifact().getFilename()).isEqualTo("w.dwp");
+  }
 }