Add ctx.experimental_new_directory (undocumented) to create tree artifacts in Skylark.

RELNOTES: None.

PiperOrigin-RevId: 151744710
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index f0586b3..7c9e921 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -651,6 +651,14 @@
   }
 
   /**
+   * Creates a tree artifact in a directory that is unique to the package that contains the rule,
+   * thus guaranteeing that it never clashes with artifacts created by rules in other packages.
+   */
+  public Artifact getPackageRelativeTreeArtifact(PathFragment relative, Root root) {
+    return getTreeArtifact(getPackageDirectory().getRelative(relative), root);
+  }
+
+  /**
    * Creates an artifact in a directory that is unique to the rule, thus guaranteeing that it never
    * clashes with artifacts created by other rules.
    */
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java
index 3eee895..445716a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java
@@ -896,6 +896,32 @@
     return ruleContext.getDerivedArtifact(fragment, root);
   }
 
+  // TODO(b/36548861): Document this when it's ready to be made publicly available.
+  @SkylarkCallable(
+    name = "experimental_new_directory",
+    documented = false,
+    parameters = {
+      @Param(name = "name", type = String.class),
+      @Param(
+        name = "sibling",
+        type = Artifact.class,
+        defaultValue = "None",
+        noneable = true,
+        named = true
+      )
+    }
+  )
+  public Artifact newDirectory(String name, Object siblingArtifactUnchecked) throws EvalException {
+    checkMutable("experimental_new_directory");
+    if (siblingArtifactUnchecked == Runtime.NONE) {
+      return ruleContext.getPackageRelativeTreeArtifact(new PathFragment(name), newFileRoot());
+    }
+    Artifact siblingArtifact = (Artifact) siblingArtifactUnchecked;
+    PathFragment original = siblingArtifact.getRootRelativePath();
+    PathFragment fragment = original.replaceName(name);
+    return ruleContext.getTreeArtifact(fragment, newFileRoot());
+  }
+
   @SkylarkCallable(documented = false)
   public NestedSet<Artifact> middleMan(String attribute) throws EvalException {
     checkMutable("middle_man");