Support absolute paths for Propeller profiles with propeller_optimize option.

propeller_optimize rule was added to blaze in https://github.com/bazelbuild/bazel/commit/2c7794b5053d849a45e4c73bc93e3004ce8e9e86.  This change allows
absolute paths to be specified for cc_profile and ld_profile.  This is needed
when the profiles are generated in one  snapshot and are used in a
different snapshot.

This is temporary and will be deleted once this feature is ported to Starlark.

PiperOrigin-RevId: 336201673
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/PropellerOptimizeInputFile.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/PropellerOptimizeInputFile.java
index fcc2e0f..131666f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/PropellerOptimizeInputFile.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/PropellerOptimizeInputFile.java
@@ -16,8 +16,10 @@
 import com.google.common.base.Preconditions;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.actions.SymlinkAction;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.util.FileType.HasFileType;
+import com.google.devtools.build.lib.vfs.PathFragment;
 import java.util.Objects;
 
 /** Value object reused by propeller configurations that has two artifacts. */
@@ -65,23 +67,63 @@
     return Objects.hash(ccArtifact, ldArtifact);
   }
 
+  public static Artifact getAbsolutePathArtifact(RuleContext ruleContext, String attributeName) {
+    if (!ruleContext.getFragment(CppConfiguration.class).isFdoAbsolutePathEnabled()) {
+      ruleContext.attributeError(
+          attributeName,
+          "this attribute cannot be used when --enable_fdo_profile_absolute_path is false");
+      return null;
+    }
+    String pathString = ruleContext.getExpander().expand(attributeName);
+    PathFragment absolutePath = PathFragment.create(pathString);
+    if (!absolutePath.isAbsolute()) {
+      ruleContext.attributeError(
+          attributeName, String.format("%s is not an absolute path", absolutePath.getPathString()));
+      return null;
+    }
+    Artifact artifact =
+        ruleContext.getUniqueDirectoryArtifact(
+            "fdo", absolutePath.getBaseName(), ruleContext.getBinOrGenfilesDirectory());
+    ruleContext.registerAction(
+        SymlinkAction.toAbsolutePath(
+            ruleContext.getActionOwner(),
+            PathFragment.create(absolutePath.getPathString()),
+            artifact,
+            "Symlinking LLVM Propeller Profile " + absolutePath.getPathString()));
+    return artifact;
+  }
+
   public static PropellerOptimizeInputFile fromProfileRule(RuleContext ruleContext) {
 
     boolean isCcProfile =
         ruleContext.attributes().isAttributeValueExplicitlySpecified("cc_profile");
+    boolean isAbsCcProfile =
+        ruleContext.attributes().isAttributeValueExplicitlySpecified("absolute_cc_profile");
     boolean isLdProfile =
         ruleContext.attributes().isAttributeValueExplicitlySpecified("ld_profile");
+    boolean isAbsLdProfile =
+        ruleContext.attributes().isAttributeValueExplicitlySpecified("absolute_ld_profile");
 
-    if (!isCcProfile && !isLdProfile) {
+    if (!isCcProfile && !isLdProfile && !isAbsCcProfile && !isAbsLdProfile) {
       return null;
     }
 
+    if (isCcProfile && isAbsCcProfile) {
+      ruleContext.attributeError("cc_profile", "Both relative and absolute profiles are provided.");
+    }
+
+    if (isLdProfile && isAbsLdProfile) {
+      ruleContext.attributeError("ld_profile", "Both relative and absolute profiles are provided.");
+    }
+
     Artifact ccArtifact = null;
     if (isCcProfile) {
       ccArtifact = ruleContext.getPrerequisiteArtifact("cc_profile");
       if (!ccArtifact.isSourceArtifact()) {
         ruleContext.attributeError("cc_profile", "the target is not an input file");
       }
+    } else if (isAbsCcProfile) {
+      ccArtifact = getAbsolutePathArtifact(ruleContext, "absolute_cc_profile");
     }
 
     Artifact ldArtifact = null;
@@ -90,6 +132,8 @@
       if (!ldArtifact.isSourceArtifact()) {
         ruleContext.attributeError("ld_profile", "the target is not an input file");
       }
+    } else if (isAbsLdProfile) {
+      ldArtifact = getAbsolutePathArtifact(ruleContext, "absolute_ld_profile");
     }
     return new PropellerOptimizeInputFile(ccArtifact, ldArtifact);
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/PropellerOptimizeRule.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/PropellerOptimizeRule.java
index 7e360ef..60fbab0 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/PropellerOptimizeRule.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/PropellerOptimizeRule.java
@@ -20,6 +20,7 @@
 import com.google.devtools.build.lib.analysis.RuleDefinition;
 import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
 import com.google.devtools.build.lib.packages.RuleClass;
+import com.google.devtools.build.lib.packages.Type;
 import com.google.devtools.build.lib.util.FileType;
 import com.google.devtools.build.lib.util.FileTypeSet;
 
@@ -45,6 +46,16 @@
             attr("ld_profile", LABEL)
                 .allowedFileTypes(FileTypeSet.of(FileType.of(".txt")))
                 .singleArtifact())
+        /* <!-- #BLAZE_RULE(propeller_optimize).ATTRIBUTE(profile) -->
+        Label of the absolute profile passed to the various compile actions.  This file has
+        the .txt extension.
+        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+        .add(attr("absolute_cc_profile", Type.STRING))
+        /* <!-- #BLAZE_RULE(propeller_optimize).ATTRIBUTE(profile) -->
+        Label of the absolute profile passed to the various link actions.  This file has
+        the .txt extension.
+        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+        .add(attr("absolute_ld_profile", Type.STRING))
         .advertiseProvider(PropellerOptimizeProvider.class)
         .build();
   }
@@ -67,9 +78,14 @@
 <pre class="code">
 propeller_optimize(
     name = "layout",
-    profile = "//path:profile.txt",
+    cc_profile = "//path:cc_profile.txt",
     ld_profile = "//path:ld_profile.txt"
 )
 
+propeller_optimize(
+    name = "layout_absolute",
+    absolute_cc_profile = "/absolute/cc_profile.txt",
+    absolute_ld_profile = "/absolute/ld_profile.txt"
+)
 </pre>
 <!-- #END_BLAZE_RULE -->*/
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcBinaryThinLtoTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcBinaryThinLtoTest.java
index c8b9dca..6c89eba 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcBinaryThinLtoTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcBinaryThinLtoTest.java
@@ -1859,8 +1859,7 @@
     assertThat(backendAction.getArguments()).containsAtLeast("-fno-PIE", "-fPIC").inOrder();
   }
 
-  @Test
-  public void testPropellerOptimizeOption() throws Exception {
+  private void testPropellerOptimizeOption(boolean label) throws Exception {
     scratch.file(
         "pkg/BUILD",
         "package(features = ['thin_lto'])",
@@ -1868,10 +1867,18 @@
         "cc_binary(name = 'bin',",
         "          srcs = ['binfile.cc', ])");
 
-    scratch.file(
-        "fdo/BUILD",
-        "propeller_optimize(name='test_propeller_optimize', cc_profile=':cc_profile.txt',"
-            + " ld_profile=':ld_profile.txt')");
+    if (label) {
+      scratch.file(
+          "fdo/BUILD",
+          "propeller_optimize(name='test_propeller_optimize', cc_profile=':cc_profile.txt',"
+              + " ld_profile=':ld_profile.txt')");
+    } else {
+      scratch.file(
+          "fdo/BUILD",
+          "propeller_optimize(name='test_propeller_optimize',"
+              + "absolute_cc_profile='/tmp/cc_profile.txt',"
+              + "absolute_ld_profile='/tmp/ld_profile.txt')");
+    }
 
     scratch.file("pkg/binfile.cc", "int main() {}");
 
@@ -1896,19 +1903,30 @@
     assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
 
     List<String> commandLine = linkAction.getLinkCommandLine().getRawLinkArgv();
-    assertThat(commandLine).contains("-Wl,--symbol-ordering-file=fdo/ld_profile.txt");
+    assertThat(commandLine.toString())
+        .containsMatch("-Wl,--symbol-ordering-file=.*/ld_profile.txt");
 
     LtoBackendAction backendAction =
         (LtoBackendAction)
             getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
 
-    String expectedCompilerFlag = "-fbasic-block-sections=list=fdo/cc_profile.txt";
+    String expectedCompilerFlag = "-fbasic-block-sections=list=.*/cc_profile.txt";
     assertThat(Joiner.on(" ").join(backendAction.getArguments()))
         .containsMatch(expectedCompilerFlag);
     assertThat(ActionsTestUtil.baseArtifactNames(backendAction.getInputs()))
         .contains("cc_profile.txt");
   }
 
+  @Test
+  public void testPropellerOptimizeOptionFromAbsolutePath() throws Exception {
+    testPropellerOptimizeOption(false);
+  }
+
+  @Test
+  public void testPropellerOptimizeOptionFromLabel() throws Exception {
+    testPropellerOptimizeOption(true);
+  }
+
   private void testLLVMCachePrefetchBackendOption(String extraOption, boolean asLabel)
       throws Exception {
     scratch.file(