--
PiperOrigin-RevId: 150019356
MOS_MIGRATED_REVID=150019356
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 23e1e7d..7843d31 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -819,6 +819,7 @@
         "rules/java/ProguardSpecProvider.java",
         "rules/java/ProtoJavaApiInfoAspectProvider.java",
         "rules/java/ProtoJavaApiInfoProvider.java",
+        "rules/java/proto/ActionReuser.java",
         "rules/java/proto/JavaCompilationArgsAspectProvider.java",
         "rules/java/proto/JavaLiteProtoAspect.java",
         "rules/java/proto/JavaLiteProtoLibrary.java",
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/ProtoJavaApiInfoProvider.java b/src/main/java/com/google/devtools/build/lib/rules/java/ProtoJavaApiInfoProvider.java
index b43c112..e76072a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/ProtoJavaApiInfoProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/ProtoJavaApiInfoProvider.java
@@ -14,11 +14,13 @@
 package com.google.devtools.build.lib.rules.java;
 
 import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import java.util.Map;
+import javax.annotation.Nullable;
 
 /**
  * An object that provides information about API versions used by a proto library.
@@ -37,6 +39,12 @@
       JavaCompilationArgs javaCompilationArgs1,
       JavaCompilationArgs javaCompilationArgsMutable,
       JavaCompilationArgs javaCompilationArgsImmutable,
+      Artifact sourceJar1,
+      Artifact sourceJarMutable,
+      Artifact sourceJarImmutable,
+      ImmutableList<JavaCompilationArgsProvider> protoRuntime1,
+      ImmutableList<JavaCompilationArgsProvider> protoRuntimeMutable,
+      ImmutableList<JavaCompilationArgsProvider> protoRuntimeImmutable,
       Map<Artifact, Artifact> compileTimeJarToRuntimeJar,
       boolean mixedApiVersions,
       int apiVersion,
@@ -53,6 +61,12 @@
         javaCompilationArgs1,
         javaCompilationArgsMutable,
         javaCompilationArgsImmutable,
+        sourceJar1,
+        sourceJarMutable,
+        sourceJarImmutable,
+        protoRuntime1,
+        protoRuntimeMutable,
+        protoRuntimeImmutable,
         mixedApiVersions,
         apiVersion,
         supportsProto1,
@@ -114,6 +128,32 @@
    */
   public abstract JavaCompilationArgs getJavaCompilationArgsImmutable();
 
+  // The following 3 fields are the -src.jar artifact created by proto_library. If a certain
+  // proto_library does not produce some artifact, it'll be null. This can happen for example when
+  // there are no srcs, or when a certain combination of attributes results in "mutable" not being
+  // produced.
+  @Nullable
+  public abstract Artifact sourceJar1();
+
+  @Nullable
+  public abstract Artifact sourceJarMutable();
+
+  @Nullable
+  public abstract Artifact sourceJarImmutable();
+
+  // The following 3 fields are the jars that proto_library got from the proto runtime, including
+  // Stubby. Different flavors can have different runtimes. If a certain proto_library does not
+  // produce some artifact, it'll be null. This can happen for example when a certain combination of
+  // attributes results in "mutable" not being produced.
+  @Nullable
+  public abstract ImmutableList<JavaCompilationArgsProvider> getProtoRuntime1();
+
+  @Nullable
+  public abstract ImmutableList<JavaCompilationArgsProvider> getProtoRuntimeMutable();
+
+  @Nullable
+  public abstract ImmutableList<JavaCompilationArgsProvider> getProtoRuntimeImmutable();
+
   /**
    * Returns true if the transitive closure contains libraries with API versions other than the one
    * specified in this target. Building in mixed mode will add implicit deps for all the api_version
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/proto/ActionReuser.java b/src/main/java/com/google/devtools/build/lib/rules/java/proto/ActionReuser.java
new file mode 100644
index 0000000..64913ad
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/proto/ActionReuser.java
@@ -0,0 +1,123 @@
+// Copyright 2017 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.rules.java.proto;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.collect.Iterables.isEmpty;
+import static com.google.common.collect.Iterables.transform;
+import static com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode.TARGET;
+import static com.google.devtools.build.lib.rules.java.JavaCompilationArgs.ClasspathType.BOTH;
+import static com.google.devtools.build.lib.rules.java.proto.JavaProtoLibraryTransitiveFilesToBuildProvider.GET_JARS;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.ConfiguredAspect;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.rules.java.JavaCompilationArgs;
+import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider;
+import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider;
+import com.google.devtools.build.lib.rules.java.JavaSkylarkApiProvider;
+import com.google.devtools.build.lib.rules.java.JavaSourceJarsProvider;
+import com.google.devtools.build.lib.rules.java.ProtoJavaApiInfoProvider;
+import com.google.devtools.build.lib.rules.proto.ProtoConfiguration;
+
+public class ActionReuser {
+
+  /**
+   * If the underlying proto_library rule already registers the compile actions we need, just reuse
+   * them. This will preserve memory.
+   *
+   * <p>TODO(b/36191931): Delete when it's time.
+   */
+  public static boolean reuseExistingActions(
+      ConfiguredTarget base, RuleContext ruleContext, ConfiguredAspect.Builder aspect) {
+    if (!ruleContext
+        .getConfiguration()
+        .getFragment(ProtoConfiguration.class)
+        .reuseJavaCompileActionsFromProtoLibrary()) {
+      return false;
+    }
+
+    ProtoJavaApiInfoProvider javaApi = base.getProvider(ProtoJavaApiInfoProvider.class);
+    if (javaApi == null) {
+      return false;
+    }
+
+    JavaCompilationArgs directJars = javaApi.getJavaCompilationArgsImmutable();
+    if (isEmpty(directJars.getCompileTimeJars()) || javaApi.sourceJarImmutable() == null) {
+      return false;
+    }
+
+    JavaCompilationArgs transitiveJars =
+        JavaCompilationArgs.builder()
+            .addTransitiveArgs(javaApi.getTransitiveJavaCompilationArgsImmutable(), BOTH)
+            .addTransitiveDependencies(javaApi.getProtoRuntimeImmutable(), true /* recursive */)
+            .addTransitiveArgs(directJars, BOTH)
+            .build();
+
+    Artifact outputJar = getOnlyElement(directJars.getRuntimeJars());
+    Artifact compileTimeJar = getOnlyElement(directJars.getCompileTimeJars());
+    Artifact sourceJar = checkNotNull(javaApi.sourceJarImmutable());
+
+    JavaCompilationArgsProvider compilationArgsProvider =
+        JavaCompilationArgsProvider.create(
+            directJars,
+            transitiveJars,
+            NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER),
+            NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER));
+
+    JavaSkylarkApiProvider.Builder skylarkApiProvider =
+        JavaSkylarkApiProvider.builder()
+            .setRuleOutputJarsProvider(
+                createOutputJarProvider(outputJar, compileTimeJar, sourceJar))
+            .setSourceJarsProvider(createSrcJarProvider(sourceJar))
+            .setCompilationArgsProvider(compilationArgsProvider);
+
+    NestedSet<Artifact> transitiveOutputJars =
+        NestedSetBuilder.fromNestedSets(
+                transform(
+                    ruleContext.getPrerequisites(
+                        "deps", TARGET, JavaProtoLibraryTransitiveFilesToBuildProvider.class),
+                    GET_JARS))
+            .add(outputJar)
+            .build();
+
+    aspect
+        .addSkylarkTransitiveInfo(
+            JavaSkylarkApiProvider.PROTO_NAME.getLegacyId(), skylarkApiProvider.build())
+        .addProviders(
+            new JavaProtoLibraryTransitiveFilesToBuildProvider(transitiveOutputJars),
+            new JavaCompilationArgsAspectProvider(compilationArgsProvider));
+    return true;
+  }
+
+  private static JavaRuleOutputJarsProvider createOutputJarProvider(
+      Artifact outputJar, Artifact compileTimeJar, Artifact sourceJar) {
+    return JavaRuleOutputJarsProvider.builder()
+        .addOutputJar(outputJar, compileTimeJar, ImmutableList.of(sourceJar))
+        .build();
+  }
+
+  private static JavaSourceJarsProvider createSrcJarProvider(Artifact sourceJar) {
+    return JavaSourceJarsProvider.create(
+        NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER),
+        NestedSetBuilder.<Artifact>stableOrder().add(sourceJar).build());
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/proto/JavaProtoAspect.java b/src/main/java/com/google/devtools/build/lib/rules/java/proto/JavaProtoAspect.java
index e2ba1bd..36be8ab 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/proto/JavaProtoAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/proto/JavaProtoAspect.java
@@ -114,6 +114,9 @@
         checkNotNull(base.getProvider(ProtoSupportDataProvider.class)).getSupportData();
 
     Impl impl = new Impl(ruleContext, supportData, javaSemantics, rpcSupport);
+    if (impl.shouldGenerateCode() && ActionReuser.reuseExistingActions(base, ruleContext, aspect)) {
+      return aspect.build();
+    }
     impl.addProviders(aspect);
     return aspect.build();
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoConfiguration.java
index 64a202d..3203f53 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoConfiguration.java
@@ -110,6 +110,17 @@
     )
     public StrictDepsMode strictProtoDeps;
 
+    @Option(
+      name = "reuseJavaCompileActionsFromProtoLibrary",
+      defaultValue = "false",
+      category = "experimental",
+      help =
+          "When true, a java_proto_library that wraps a proto_library with java_api_version!=0"
+              + "will reuse its actions. This saves memory and prevents duplicate jars from "
+              + "appearing on a Java compilation's classpath"
+    )
+    public boolean reuseJavaCompileActionsFromProtoLibrary;
+
     @Override
     public FragmentOptions getHost(boolean fallback) {
       Options host = (Options) super.getHost(fallback);
@@ -121,6 +132,7 @@
       host.protoToolchainForJavaLite = protoToolchainForJavaLite;
       host.protoToolchainForCc = protoToolchainForCc;
       host.strictProtoDeps = strictProtoDeps;
+      host.reuseJavaCompileActionsFromProtoLibrary = reuseJavaCompileActionsFromProtoLibrary;
       return host;
     }
   }
@@ -153,6 +165,7 @@
   private final Label protoToolchainForJavaLite;
   private final Label protoToolchainForCc;
   private final StrictDepsMode strictProtoDeps;
+  private final boolean reuseJavaCompileActionsFromProtoLibrary;
 
   public ProtoConfiguration(Options options) {
     this.experimentalProtoExtraActions = options.experimentalProtoExtraActions;
@@ -162,6 +175,7 @@
     this.protoToolchainForJavaLite = options.protoToolchainForJavaLite;
     this.protoToolchainForCc = options.protoToolchainForCc;
     this.strictProtoDeps = options.strictProtoDeps;
+    this.reuseJavaCompileActionsFromProtoLibrary = options.reuseJavaCompileActionsFromProtoLibrary;
   }
 
   public ImmutableList<String> protocOpts() {
@@ -196,4 +210,8 @@
   public StrictDepsMode strictProtoDeps() {
     return strictProtoDeps;
   }
+
+  public boolean reuseJavaCompileActionsFromProtoLibrary() {
+    return reuseJavaCompileActionsFromProtoLibrary;
+  }
 }