Scope the paths of objc compilation artifacts from J2Objc-generated code using both the associated Java target label and the binary/test target that generates the compilation action. This fixes a bug in which two binary/test targets containing the same Java target to translate leads to shared actions when users execute more than one such binary/test targets in a single Blaze invocation.

--
MOS_MIGRATED_REVID=87364125
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java
index f2e7bdc..20eda21 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java
@@ -14,6 +14,7 @@
 
 package com.google.devtools.build.lib.rules.objc;
 
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.actions.Root;
@@ -41,12 +42,28 @@
   private final Label ownerLabel;
   private final String archiveFileNameSuffix;
 
+  /**
+   * Label to scope the output paths of generated artifacts, in addition to {@link ownerLabel}.
+   */
+  private final Optional<Label> scopingLabel;
+
   IntermediateArtifacts(
       AnalysisEnvironment analysisEnvironment, Root binDirectory, Label ownerLabel,
       String archiveFileNameSuffix) {
     this.analysisEnvironment = Preconditions.checkNotNull(analysisEnvironment);
     this.binDirectory = Preconditions.checkNotNull(binDirectory);
     this.ownerLabel = Preconditions.checkNotNull(ownerLabel);
+    this.scopingLabel = Optional.<Label>absent();
+    this.archiveFileNameSuffix = Preconditions.checkNotNull(archiveFileNameSuffix);
+  }
+
+  IntermediateArtifacts(
+      AnalysisEnvironment analysisEnvironment, Root binDirectory, Label ownerLabel,
+      Label scopingLabel, String archiveFileNameSuffix) {
+    this.analysisEnvironment = Preconditions.checkNotNull(analysisEnvironment);
+    this.binDirectory = Preconditions.checkNotNull(binDirectory);
+    this.ownerLabel = Preconditions.checkNotNull(ownerLabel);
+    this.scopingLabel = Optional.of(Preconditions.checkNotNull(scopingLabel));
     this.archiveFileNameSuffix = Preconditions.checkNotNull(archiveFileNameSuffix);
   }
 
@@ -64,7 +81,7 @@
    * of the {@link PathFragment} corresponding to the owner {@link Label}.
    */
   private Artifact appendExtension(String extension) {
-    return appendExtension(ownerLabel.toPathFragment(), extension);
+    return appendExtension(labelScopedDir(), extension);
   }
 
   /**
@@ -120,7 +137,7 @@
    * The {@code .a} file which contains all the compiled sources for a rule.
    */
   public Artifact archive() {
-    PathFragment labelPath = ownerLabel.toPathFragment();
+    PathFragment labelPath = labelScopedDir();
     PathFragment rootRelative = labelPath
         .getParentDirectory()
         .getRelative(String.format("lib%s%s.a", labelPath.getBaseName(), archiveFileNameSuffix));
@@ -134,9 +151,26 @@
     return appendExtension(TMP_DSYM_BUNDLE_SUFFIX);
   }
 
+  /**
+   * Returns a unique directory scoped by {@code ownerLabel} and {@code scopingLabel}. Normally
+   * when {@code scopingLabel} is absent, the returned directory is just a path fragment
+   * containing the package and the name of the ownerLabel. If scopingLabel is present, the
+   * returned directory is scoped by scopingLabel first. For example, if ownerLabel is
+   * //a/b:c and scopingLabel is //d/e:f, the returned directory will be: d/e/a/b/c/f/.
+   */
+  private PathFragment labelScopedDir() {
+    if (scopingLabel.isPresent()) {
+      return AnalysisUtils.getUniqueDirectory(scopingLabel.get(), ownerLabel.toPathFragment());
+    } else {
+      return ownerLabel.toPathFragment();
+    }
+  }
+
   private PathFragment inUniqueObjsDir(Artifact source, String extension) {
-    PathFragment dir = AnalysisUtils.getUniqueDirectory(ownerLabel, new PathFragment("_objs"));
-    PathFragment sourceFile = dir.getRelative(source.getRootRelativePath());
+    PathFragment labelPath = labelScopedDir();
+    PathFragment uniqueDir = labelPath.getParentDirectory().getRelative(new PathFragment("_objs"))
+        .getRelative(labelPath.getBaseName());
+    PathFragment sourceFile = uniqueDir.getRelative(source.getRootRelativePath());
     return FileSystemUtils.replaceExtension(sourceFile, extension);
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java
index cce0b8b..562580d 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java
@@ -88,6 +88,7 @@
     return new IntermediateArtifacts(
         ruleContext.getAnalysisEnvironment(),
         ruleContext.getConfiguration().getBinDirectory(),
+        ruleContext.getLabel(),
         j2ObjcSource.getTargetLabel(),
         /*archiveFileNameSuffix=*/"_j2objc");
   }