Introducing the Java compilation black box in Skylark.

Adding a new declared provider that will be the returning value of the Java compilation method in Skylark. This provider is considered a black-box from Skylark and it should only be passed around (returned from a rule/aspect implementation) to allow Java rules to depend on it.

Also adding a new Skylark module (java_common) to allow retrieving the Java dependencies' black-box from Skylark implementations.

--
MOS_MIGRATED_REVID=140138446
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 0fe30e1..3da2115 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -761,8 +761,10 @@
         "rules/java/JavaLibrary.java",
         "rules/java/JavaPlugin.java",
         "rules/java/JavaPrimaryClassProvider.java",
+        "rules/java/JavaProvider.java",
         "rules/java/JavaRuntimeClasspathProvider.java",
         "rules/java/JavaRuntimeJarProvider.java",
+        "rules/java/JavaSkylarkCommon.java",
         "rules/java/JavaSourceInfoProvider.java",
         "rules/java/JavaToolchain.java",
         "rules/java/JavaToolchainRule.java",
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
index ef69aef..5881098 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
@@ -109,6 +109,7 @@
 import com.google.devtools.build.lib.rules.java.JavaConfigurationLoader;
 import com.google.devtools.build.lib.rules.java.JavaImportBaseRule;
 import com.google.devtools.build.lib.rules.java.JavaOptions;
+import com.google.devtools.build.lib.rules.java.JavaSkylarkCommon;
 import com.google.devtools.build.lib.rules.java.JavaToolchainRule;
 import com.google.devtools.build.lib.rules.java.JvmConfigurationLoader;
 import com.google.devtools.build.lib.rules.java.ProguardLibraryRule;
@@ -522,6 +523,7 @@
 
           builder.addSkylarkAccessibleTopLevels("android_common", new AndroidSkylarkCommon());
           builder.addSkylarkAccessibleTopLevels("java_proto_common", JavaProtoSkylarkCommon.class);
+          builder.addSkylarkAccessibleTopLevels("java_common", JavaSkylarkCommon.INSTANCE);
 
           try {
             builder.addWorkspaceFilePrefix(
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaRuleClasses.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaRuleClasses.java
index 857e83a..213cf76 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaRuleClasses.java
@@ -44,6 +44,7 @@
 import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
 import com.google.devtools.build.lib.packages.TriState;
 import com.google.devtools.build.lib.rules.cpp.CcLinkParamsProvider;
+import com.google.devtools.build.lib.rules.java.JavaProvider;
 import com.google.devtools.build.lib.rules.java.JavaSemantics;
 import com.google.devtools.build.lib.rules.java.JavaToolchainProvider;
 import com.google.devtools.build.lib.syntax.Type;
@@ -149,12 +150,20 @@
           <a href="common-definitions.html#common-attributes">Attributes common to all build rules
           </a>.
           <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
-          .override(builder.copy("deps")
-              .allowedFileTypes(JavaSemantics.JAR)
-              .allowedRuleClasses(ALLOWED_RULES_IN_DEPS)
-              .mandatoryProviders(ImmutableList.of(
-                  SkylarkProviderIdentifier.forKey(CcLinkParamsProvider.CC_LINK_PARAMS.getKey())))
-              .skipAnalysisTimeFileTypeCheck())
+          .override(
+              builder
+                  .copy("deps")
+                  .allowedFileTypes(JavaSemantics.JAR)
+                  .allowedRuleClasses(ALLOWED_RULES_IN_DEPS)
+                  .mandatoryProvidersList(
+                      ImmutableList.of(
+                          ImmutableList.of(
+                              SkylarkProviderIdentifier.forKey(
+                                  CcLinkParamsProvider.CC_LINK_PARAMS.getKey())),
+                          ImmutableList.of(
+                              SkylarkProviderIdentifier.forKey(
+                                  JavaProvider.JAVA_PROVIDER.getKey()))))
+                  .skipAnalysisTimeFileTypeCheck())
           /* <!-- #BLAZE_RULE($java_rule).ATTRIBUTE(runtime_deps) -->
           Libraries to make available to the final binary or test at runtime only.
           Like ordinary <code>deps</code>, these will appear on the runtime classpath, but unlike
@@ -162,10 +171,11 @@
           listed here. Dependency-analysis tools should ignore targets that appear in both
           <code>runtime_deps</code> and <code>deps</code>.
           <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
-          .add(attr("runtime_deps", LABEL_LIST)
-              .allowedFileTypes(JavaSemantics.JAR)
-              .allowedRuleClasses(ALLOWED_RULES_IN_DEPS)
-              .skipAnalysisTimeFileTypeCheck())
+          .add(
+              attr("runtime_deps", LABEL_LIST)
+                  .allowedFileTypes(JavaSemantics.JAR)
+                  .allowedRuleClasses(ALLOWED_RULES_IN_DEPS)
+                  .skipAnalysisTimeFileTypeCheck())
 
           /* <!-- #BLAZE_RULE($java_rule).ATTRIBUTE(srcs) -->
           The list of source files that are processed to create the target.
@@ -195,11 +205,14 @@
             class on the runtime classpath or you specify the <code>runtime_deps</code> argument.
           </p>
           <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
-          .add(attr("srcs", LABEL_LIST)
-              .orderIndependent()
-              .direct_compile_time_input()
-              .allowedFileTypes(JavaSemantics.JAVA_SOURCE,
-                  JavaSemantics.SOURCE_JAR, JavaSemantics.PROPERTIES))
+          .add(
+              attr("srcs", LABEL_LIST)
+                  .orderIndependent()
+                  .direct_compile_time_input()
+                  .allowedFileTypes(
+                      JavaSemantics.JAVA_SOURCE,
+                      JavaSemantics.SOURCE_JAR,
+                      JavaSemantics.PROPERTIES))
           /* <!-- #BLAZE_RULE($java_rule).ATTRIBUTE(resources) -->
           A list of data files to include in a Java jar.
           <p>
@@ -217,8 +230,10 @@
             Resources may be source files or generated files.
           </p>
           <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
-          .add(attr("resources", LABEL_LIST).orderIndependent()
-              .allowedFileTypes(FileTypeSet.ANY_FILE))
+          .add(
+              attr("resources", LABEL_LIST)
+                  .orderIndependent()
+                  .allowedFileTypes(FileTypeSet.ANY_FILE))
           /* <!-- #BLAZE_RULE($java_rule).ATTRIBUTE(resource_strip_prefix) -->
           The path prefix to strip from Java resources.
           <p>
@@ -237,13 +252,17 @@
           <code><a href="#java_library.exported_plugins">exported_plugins</a></code>. Resources
           generated by the plugin will be included in the resulting jar of this rule.
           <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
-          .add(attr("plugins", LABEL_LIST).cfg(HOST).allowedRuleClasses("java_plugin")
-              .legacyAllowAnyFileType())
-          .add(attr(":java_plugins", LABEL_LIST)
-              .cfg(HOST)
-              .allowedRuleClasses("java_plugin")
-              .silentRuleClassFilter()
-              .value(JavaSemantics.JAVA_PLUGINS))
+          .add(
+              attr("plugins", LABEL_LIST)
+                  .cfg(HOST)
+                  .allowedRuleClasses("java_plugin")
+                  .legacyAllowAnyFileType())
+          .add(
+              attr(":java_plugins", LABEL_LIST)
+                  .cfg(HOST)
+                  .allowedRuleClasses("java_plugin")
+                  .silentRuleClassFilter()
+                  .value(JavaSemantics.JAVA_PLUGINS))
           /* <!-- #BLAZE_RULE($java_rule).ATTRIBUTE(javacopts) -->
           Extra compiler options for this library.
           Subject to <a href="make-variables.html">"Make variable"</a> substitution and
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaLibrary.java
index 6104833..a8de642 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaLibrary.java
@@ -24,7 +24,6 @@
 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.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
 import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
 import com.google.devtools.build.lib.rules.cpp.CcLinkParams;
 import com.google.devtools.build.lib.rules.cpp.CcLinkParamsProvider;
@@ -169,6 +168,11 @@
     NestedSet<Artifact> proguardSpecs = new ProguardLibrary(ruleContext).collectProguardSpecs();
 
     CcLinkParamsProvider ccLinkParamsProvider = new CcLinkParamsProvider(ccLinkParamsStore);
+    JavaCompilationArgsProvider compilationArgsProvider =
+        JavaCompilationArgsProvider.create(
+            javaCompilationArgs, recursiveJavaCompilationArgs,
+            compileTimeJavaDepArtifacts, runTimeJavaDepArtifacts);
+    JavaProvider javaProvider = new JavaProvider(compilationArgsProvider);
     builder
         .add(
             JavaRuleOutputJarsProvider.class,
@@ -186,13 +190,11 @@
         .setFilesToBuild(filesToBuild)
         .add(JavaNeverlinkInfoProvider.class, new JavaNeverlinkInfoProvider(neverLink))
         .add(CppCompilationContext.class, transitiveCppDeps)
-        .add(
-            JavaCompilationArgsProvider.class,
-            JavaCompilationArgsProvider.create(
-                javaCompilationArgs, recursiveJavaCompilationArgs,
-                compileTimeJavaDepArtifacts, runTimeJavaDepArtifacts))
+        .add(JavaCompilationArgsProvider.class, compilationArgsProvider)
+        .add(JavaProvider.class, javaProvider)
         .add(CcLinkParamsProvider.class, ccLinkParamsProvider)
         .addNativeDeclaredProvider(ccLinkParamsProvider)
+        .addNativeDeclaredProvider(javaProvider)
         .add(
             JavaNativeLibraryProvider.class,
             new JavaNativeLibraryProvider(transitiveJavaNativeLibraries))
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaProvider.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaProvider.java
new file mode 100644
index 0000000..b6ee004
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaProvider.java
@@ -0,0 +1,39 @@
+// Copyright 2016 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;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.SkylarkClassObject;
+import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
+
+/** A Skylark declared provider that encapsulates all providers that are needed by Java rules. */
+@Immutable
+public final class JavaProvider extends SkylarkClassObject implements TransitiveInfoProvider {
+
+  public static final SkylarkClassObjectConstructor JAVA_PROVIDER =
+      SkylarkClassObjectConstructor.createNative("java_common.provider");
+
+  private final JavaCompilationArgsProvider javaCompilationArgsProvider;
+
+  public JavaProvider(JavaCompilationArgsProvider javaCompilationArgsProvider) {
+    super(JAVA_PROVIDER, ImmutableMap.<String, Object>of());
+    this.javaCompilationArgsProvider = javaCompilationArgsProvider;
+  }
+
+  public JavaCompilationArgsProvider getJavaCompilationArgsProvider() {
+    return javaCompilationArgsProvider;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaSkylarkCommon.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaSkylarkCommon.java
new file mode 100644
index 0000000..7797ef5
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaSkylarkCommon.java
@@ -0,0 +1,33 @@
+// Copyright 2016 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;
+
+import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
+
+/** A module that contains Skylark utilities for Java support. */
+@SkylarkModule(name = "java_common", doc = "Utilities for Java compilation support in Skylark.")
+public class JavaSkylarkCommon {
+  public static final JavaSkylarkCommon INSTANCE = new JavaSkylarkCommon();
+
+  @SkylarkCallable(
+    name = "provider",
+    structField = true,
+    doc = "Returns the Java declared provider."
+  )
+  public SkylarkClassObjectConstructor getJavaProvider() {
+    return JavaProvider.JAVA_PROVIDER;
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 47e35ea..9fadf7f 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -895,6 +895,7 @@
         "//src/main/java/com/google/devtools/build/lib:bazel-main",
         "//src/main/java/com/google/devtools/build/lib:bazel-rules",
         "//src/main/java/com/google/devtools/build/lib:build-base",
+        "//src/main/java/com/google/devtools/build/lib:collect",
         "//src/main/java/com/google/devtools/build/lib:java-compilation",
         "//src/main/java/com/google/devtools/build/lib:java-rules",
         "//src/main/java/com/google/devtools/build/lib:packages-internal",
diff --git a/src/test/java/com/google/devtools/build/lib/rules/java/JavaSkylarkApiTest.java b/src/test/java/com/google/devtools/build/lib/rules/java/JavaSkylarkApiTest.java
index 8a18f54..616fdb7 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/java/JavaSkylarkApiTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/java/JavaSkylarkApiTest.java
@@ -22,8 +22,10 @@
 import com.google.devtools.build.lib.analysis.SkylarkProviders;
 import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
 import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
 import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor.SkylarkKey;
+import java.util.Iterator;
 import java.util.List;
 import javax.annotation.Nullable;
 import org.junit.Test;
@@ -80,4 +82,115 @@
     assertThat((List<?>) skylarkClassObject.getValue("processor_classnames"))
         .containsExactly("com.google.process.stuff");
   }
+
+  @Test
+  public void cannotConstructJavaProvider() throws Exception {
+    scratch.file(
+        "foo/extension.bzl",
+        "my_provider = provider()",
+        "def _impl(ctx):",
+        "  java_p = java_common.provider",
+        "  dep_params = java_p()",
+        "  return [my_provider(p = dep_params)]",
+        "my_rule = rule(_impl, attrs = { 'dep' : attr.label() })");
+    scratch.file("foo/BUILD", "load(':extension.bzl', 'my_rule')", "my_rule(name = 'r')");
+    reporter.removeHandler(failFastHandler);
+    assertThat(getConfiguredTarget("//foo:r")).isNull();
+    assertContainsEvent("'java_common.provider' cannot be constructed from Skylark");
+  }
+
+  @Test
+  public void javaProviderExposedOnJavaLibrary() throws Exception {
+    scratch.file(
+        "foo/extension.bzl",
+        "my_provider = provider()",
+        "def _impl(ctx):",
+        "  dep_params = ctx.attr.dep[java_common.provider]",
+        "  return [my_provider(p = dep_params)]",
+        "my_rule = rule(_impl, attrs = { 'dep' : attr.label() })");
+    scratch.file(
+        "foo/BUILD",
+        "load(':extension.bzl', 'my_rule')",
+        "java_library(name = 'jl', srcs = ['java/A.java'])",
+        "my_rule(name = 'r', dep = ':jl')");
+
+    ConfiguredTarget myRuleTarget = getConfiguredTarget("//foo:r");
+    ConfiguredTarget javaLibraryTarget = getConfiguredTarget("//foo:jl");
+    SkylarkKey myProviderKey =
+        new SkylarkKey(Label.parseAbsolute("//foo:extension.bzl"), "my_provider");
+    SkylarkClassObject declaredProvider =
+        myRuleTarget.getProvider(SkylarkProviders.class).getDeclaredProvider(myProviderKey);
+    Object javaProvider = declaredProvider.getValue("p");
+    assertThat(javaProvider).isInstanceOf(JavaProvider.class);
+    assertThat(javaLibraryTarget.getProvider(JavaProvider.class)).isEqualTo(javaProvider);
+  }
+
+  @Test
+  public void javaProviderPropagation() throws Exception {
+    scratch.file(
+        "foo/extension.bzl",
+        "def _impl(ctx):",
+        "  dep_params = ctx.attr.dep[java_common.provider]",
+        "  return [dep_params]",
+        "my_rule = rule(_impl, attrs = { 'dep' : attr.label() })");
+    scratch.file(
+        "foo/BUILD",
+        "load(':extension.bzl', 'my_rule')",
+        "java_library(name = 'jl', srcs = ['java/A.java'])",
+        "my_rule(name = 'r', dep = ':jl')",
+        "java_library(name = 'jl_top', srcs = ['java/C.java'], deps = [':r'])");
+
+    ConfiguredTarget myRuleTarget = getConfiguredTarget("//foo:r");
+    ConfiguredTarget javaLibraryTarget = getConfiguredTarget("//foo:jl");
+    ConfiguredTarget topJavaLibraryTarget = getConfiguredTarget("//foo:jl_top");
+
+    Object javaProvider = myRuleTarget.get(JavaProvider.JAVA_PROVIDER.getKey());
+    assertThat(javaProvider).isInstanceOf(JavaProvider.class);
+
+    JavaProvider jlJavaProvider = javaLibraryTarget.getProvider(JavaProvider.class);
+
+    assertThat(jlJavaProvider == javaProvider).isTrue();
+
+    JavaProvider jlTopJavaProvider = topJavaLibraryTarget.getProvider(JavaProvider.class);
+
+    javaCompilationArgsHaveTheSameParent(
+        jlJavaProvider.getJavaCompilationArgsProvider().getJavaCompilationArgs(),
+        jlTopJavaProvider.getJavaCompilationArgsProvider().getJavaCompilationArgs());
+  }
+
+  private static boolean javaCompilationArgsHaveTheSameParent(
+      JavaCompilationArgs args, JavaCompilationArgs otherArgs) {
+    if (!nestedSetsOfArtifactHaveTheSameParent(
+        args.getCompileTimeJars(), otherArgs.getCompileTimeJars())) {
+      return false;
+    }
+    if (!nestedSetsOfArtifactHaveTheSameParent(
+        args.getInstrumentationMetadata(), otherArgs.getInstrumentationMetadata())) {
+      return false;
+    }
+    if (!nestedSetsOfArtifactHaveTheSameParent(args.getRuntimeJars(), otherArgs.getRuntimeJars())) {
+      return false;
+    }
+    return true;
+  }
+
+  private static boolean nestedSetsOfArtifactHaveTheSameParent(
+      NestedSet<Artifact> artifacts, NestedSet<Artifact> otherArtifacts) {
+    Iterator<Artifact> iterator = artifacts.iterator();
+    Iterator<Artifact> otherIterator = otherArtifacts.iterator();
+    while (iterator.hasNext() && otherIterator.hasNext()) {
+      Artifact artifact = (Artifact) iterator.next();
+      Artifact otherArtifact = (Artifact) otherIterator.next();
+      if (!artifact
+          .getPath()
+          .getParentDirectory()
+          .equals(otherArtifact.getPath().getParentDirectory())) {
+        return false;
+      }
+    }
+    if (iterator.hasNext() || otherIterator.hasNext()) {
+      return false;
+    }
+    return true;
+  }
 }
\ No newline at end of file