diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaImportRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaImportRule.java
index 377db909e17205..281605fd483fbf 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaImportRule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaImportRule.java
@@ -44,26 +44,32 @@ public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
The list of other libraries to be linked in to the target.
See java_library.deps.
*/
- .add(attr("deps", LABEL_LIST)
- .allowedRuleClasses(ALLOWED_DEPS)
- .allowedFileTypes() // none allowed
- .validityPredicate(ANY_EDGE))
+ .add(
+ attr("deps", LABEL_LIST)
+ .allowedRuleClasses(ALLOWED_DEPS)
+ .allowedFileTypes() // none allowed
+ .validityPredicate(ANY_EDGE)
+ .mandatoryProvidersList(BazelJavaRuleClasses.MANDATORY_JAVA_PROVIDER_ONLY))
/*
Targets to make available to users of this rule.
See java_library.exports.
*/
- .add(attr("exports", LABEL_LIST)
- .allowedRuleClasses(ALLOWED_DEPS)
- .allowedFileTypes() // none allowed
- .validityPredicate(ANY_EDGE))
+ .add(
+ attr("exports", LABEL_LIST)
+ .allowedRuleClasses(ALLOWED_DEPS)
+ .allowedFileTypes() // none allowed
+ .validityPredicate(ANY_EDGE)
+ .mandatoryProvidersList(BazelJavaRuleClasses.MANDATORY_JAVA_PROVIDER_ONLY))
/*
Libraries to make available to the final binary or test at runtime only.
See java_library.runtime_deps.
*/
- .add(attr("runtime_deps", LABEL_LIST)
- .allowedFileTypes(JavaSemantics.JAR)
- .allowedRuleClasses(ALLOWED_DEPS)
- .skipAnalysisTimeFileTypeCheck())
+ .add(
+ attr("runtime_deps", LABEL_LIST)
+ .allowedFileTypes(JavaSemantics.JAR)
+ .allowedRuleClasses(ALLOWED_DEPS)
+ .mandatoryProvidersList(BazelJavaRuleClasses.MANDATORY_JAVA_PROVIDER_ONLY)
+ .skipAnalysisTimeFileTypeCheck())
.advertiseProvider(JavaSourceInfoProvider.class)
.build();
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaLibraryRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaLibraryRule.java
index 6f1e81016c26c3..4039821c19c783 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaLibraryRule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaLibraryRule.java
@@ -115,8 +115,8 @@ depended on these rules. This is not true for regular (non-exported) deps<
.add(
attr("exports", LABEL_LIST)
.allowedRuleClasses(BazelJavaRuleClasses.ALLOWED_RULES_IN_DEPS)
- .allowedFileTypes(/*May not have files in exports!*/ ))
-
+ .allowedFileTypes(/*May not have files in exports!*/ )
+ .mandatoryProvidersList(BazelJavaRuleClasses.MANDATORY_JAVA_PROVIDER_ONLY))
/*
Whether this library should only be used for compilation and not at runtime.
Useful if the library will be provided by the runtime environment during execution. Examples
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 363f6ae20a9529..e8f200474aafdb 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
@@ -74,6 +74,26 @@ public class BazelJavaRuleClasses {
JavaSemantics.JAVA_LIBRARY_CLASS_JAR,
JavaSemantics.JAVA_LIBRARY_SOURCE_JAR);
+ /**
+ * Meant to be an element of {@code mandatoryProvidersLists} in order to accept rules providing
+ * a {@link JavaProvider} through an attribute. Other providers can be included in
+ * {@code mandatoryProvidersLists} as well.
+ */
+ public static final ImmutableList CONTAINS_JAVA_PROVIDER =
+ ImmutableList.of(SkylarkProviderIdentifier.forKey(JavaProvider.JAVA_PROVIDER.getKey()));
+
+ public static final ImmutableList CONTAINS_CC_LINK_PARAMS =
+ ImmutableList.of(
+ SkylarkProviderIdentifier.forKey(CcLinkParamsProvider.CC_LINK_PARAMS.getKey()));
+
+ /**
+ * Meant to be the value of {@code mandatoryProvidersLists} in order for the rule to provide only
+ * a {@link JavaProvider} through an attribute.
+ */
+ public static final ImmutableList>
+ MANDATORY_JAVA_PROVIDER_ONLY = ImmutableList.of(CONTAINS_JAVA_PROVIDER);
+
+
/**
* Common attributes for rules that depend on ijar.
*/
@@ -158,13 +178,7 @@ public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
.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()))))
+ ImmutableList.of(CONTAINS_CC_LINK_PARAMS, CONTAINS_JAVA_PROVIDER))
.skipAnalysisTimeFileTypeCheck())
/*
Libraries to make available to the final binary or test at runtime only.
@@ -177,11 +191,7 @@ public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
attr("runtime_deps", LABEL_LIST)
.allowedFileTypes(JavaSemantics.JAR)
.allowedRuleClasses(ALLOWED_RULES_IN_DEPS)
- .mandatoryProvidersList(
- ImmutableList.of(
- ImmutableList.of(
- SkylarkProviderIdentifier.forKey(
- JavaProvider.JAVA_PROVIDER.getKey()))))
+ .mandatoryProvidersList(MANDATORY_JAVA_PROVIDER_ONLY)
.skipAnalysisTimeFileTypeCheck())
/*
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 25e4c370a0fb91..34d2af525ed034 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
@@ -324,6 +324,102 @@ public void javaProviderPropagation() throws Exception {
jlTopJavaProvider.getProvider(JavaCompilationArgsProvider.class).getJavaCompilationArgs());
}
+ @Test
+ public void skylarkJavaToJavaLibraryAttributes() throws Exception {
+ scratch.file(
+ "foo/extension.bzl",
+ "def _impl(ctx):",
+ " dep_params = ctx.attr.dep[java_common.provider]",
+ " return struct(providers = [dep_params])",
+ "my_rule = rule(_impl, attrs = { 'dep' : attr.label() })");
+ scratch.file(
+ "foo/BUILD",
+ "load(':extension.bzl', 'my_rule')",
+ "java_library(name = 'jl_bottom_for_deps', srcs = ['java/A.java'])",
+ "java_library(name = 'jl_bottom_for_exports', srcs = ['java/A2.java'])",
+ "java_library(name = 'jl_bottom_for_runtime_deps', srcs = ['java/A2.java'])",
+ "my_rule(name = 'mya', dep = ':jl_bottom_for_deps')",
+ "my_rule(name = 'myb', dep = ':jl_bottom_for_exports')",
+ "my_rule(name = 'myc', dep = ':jl_bottom_for_runtime_deps')",
+ "java_library(name = 'lib_exports', srcs = ['java/B.java'], deps = [':mya'],",
+ " exports = [':myb'], runtime_deps = [':myc'])",
+ "java_library(name = 'lib_interm', srcs = ['java/C.java'], deps = [':lib_exports'])",
+ "java_library(name = 'lib_top', srcs = ['java/D.java'], deps = [':lib_interm'])");
+ assertNoEvents();
+
+ // Test that all bottom jars are on the runtime classpath of lib_exports.
+ ConfiguredTarget jlExports = getConfiguredTarget("//foo:lib_exports");
+ JavaCompilationArgsProvider jlExportsProvider =
+ JavaProvider.getProvider(JavaCompilationArgsProvider.class, jlExports);
+ assertThat(prettyJarNames(jlExportsProvider.getRecursiveJavaCompilationArgs().getRuntimeJars()))
+ .containsAllOf(
+ "foo/libjl_bottom_for_deps.jar",
+ "foo/libjl_bottom_for_runtime_deps.jar",
+ "foo/libjl_bottom_for_exports.jar");
+
+ // Test that libjl_bottom_for_exports.jar is in the recursive java compilation args of lib_top.
+ ConfiguredTarget jlTop = getConfiguredTarget("//foo:lib_interm");
+ JavaCompilationArgsProvider jlTopProvider =
+ JavaProvider.getProvider(JavaCompilationArgsProvider.class, jlTop);
+ assertThat(prettyJarNames(jlTopProvider.getRecursiveJavaCompilationArgs().getRuntimeJars()))
+ .contains("foo/libjl_bottom_for_exports.jar");
+ }
+
+ @Test
+ public void skylarkJavaToJavaBinaryAttributes() throws Exception {
+ scratch.file(
+ "foo/extension.bzl",
+ "def _impl(ctx):",
+ " dep_params = ctx.attr.dep[java_common.provider]",
+ " return struct(providers = [dep_params])",
+ "my_rule = rule(_impl, attrs = { 'dep' : attr.label() })");
+ scratch.file(
+ "foo/BUILD",
+ "load(':extension.bzl', 'my_rule')",
+ "java_library(name = 'jl_bottom_for_deps', srcs = ['java/A.java'])",
+ "java_library(name = 'jl_bottom_for_runtime_deps', srcs = ['java/A2.java'])",
+ "my_rule(name = 'mya', dep = ':jl_bottom_for_deps')",
+ "my_rule(name = 'myb', dep = ':jl_bottom_for_runtime_deps')",
+ "java_binary(name = 'binary', srcs = ['java/B.java'], main_class = 'foo.A',",
+ " deps = [':mya'], runtime_deps = [':myb'])");
+ assertNoEvents();
+
+ // Test that all bottom jars are on the runtime classpath.
+ ConfiguredTarget binary = getConfiguredTarget("//foo:binary");
+ assertThat(prettyJarNames(
+ binary.getProvider(JavaRuntimeClasspathProvider.class).getRuntimeClasspath()))
+ .containsAllOf(
+ "foo/libjl_bottom_for_deps.jar", "foo/libjl_bottom_for_runtime_deps.jar");
+ }
+
+ @Test
+ public void skylarkJavaToJavaImportAttributes() throws Exception {
+ scratch.file(
+ "foo/extension.bzl",
+ "def _impl(ctx):",
+ " dep_params = ctx.attr.dep[java_common.provider]",
+ " return struct(providers = [dep_params])",
+ "my_rule = rule(_impl, attrs = { 'dep' : attr.label() })");
+ scratch.file(
+ "foo/BUILD",
+ "load(':extension.bzl', 'my_rule')",
+ "java_library(name = 'jl_bottom_for_deps', srcs = ['java/A.java'])",
+ "java_library(name = 'jl_bottom_for_runtime_deps', srcs = ['java/A2.java'])",
+ "my_rule(name = 'mya', dep = ':jl_bottom_for_deps')",
+ "my_rule(name = 'myb', dep = ':jl_bottom_for_runtime_deps')",
+ "java_import(name = 'import', jars = ['B.jar'], deps = [':mya'], runtime_deps = [':myb'])");
+ assertNoEvents();
+
+ // Test that all bottom jars are on the runtime classpath.
+ ConfiguredTarget importTarget = getConfiguredTarget("//foo:import");
+ JavaCompilationArgsProvider compilationProvider =
+ JavaProvider.getProvider(JavaCompilationArgsProvider.class, importTarget);
+ assertThat(prettyJarNames(
+ compilationProvider.getRecursiveJavaCompilationArgs().getRuntimeJars()))
+ .containsAllOf(
+ "foo/libjl_bottom_for_deps.jar", "foo/libjl_bottom_for_runtime_deps.jar");
+ }
+
@Test
public void strictDepsEnabled() throws Exception {
scratch.file(
diff --git a/src/test/shell/bazel/bazel_java_test.sh b/src/test/shell/bazel/bazel_java_test.sh
index 83d967640e7c12..509eb700e26cf1 100755
--- a/src/test/shell/bazel/bazel_java_test.sh
+++ b/src/test/shell/bazel/bazel_java_test.sh
@@ -59,6 +59,111 @@ public class HelloLibrary {
EOF
}
+function write_files_for_java_provider_in_attr() {
+ mkdir -p java/com/google/sandwich
+ cd java/com/google/sandwich
+
+ touch BUILD A.java B.java Main.java java_custom_library.bzl
+
+ rule_type="$1" # java_library / java_import
+ attribute_name="$2" # exports / runtime_deps
+ srcs_attribute_row="srcs = ['A.java']"
+ if [ "$rule_type" = "java_import" ]; then
+ srcs_attribute_row="jars = []"
+ fi
+
+ cat > BUILD <> BUILD <> BUILD <> BUILD
+
+ cat >> BUILD <> BUILD
+ echo " $attribute_name = [':middle']" >> BUILD
+
+ cat >> BUILD <> BUILD <> BUILD < B.java < A.java < Main.java <> Main.java <> Main.java < java_custom_library.bzl << EOF
def _impl(ctx):
@@ -494,6 +599,39 @@ EOF
expect_log "Message from C"
}
+function test_java_library_exports_java_sandwich() {
+ write_files_for_java_provider_in_attr "java_library" "exports"
+ write_java_custom_rule
+
+ bazel run :Main > $TEST_log || fail "Java sandwich build failed"
+ expect_log "Message from A"
+ expect_log "Message from B"
+}
+
+function test_java_library_runtime_deps_java_sandwich() {
+ write_files_for_java_provider_in_attr "java_library" "runtime_deps"
+ write_java_custom_rule
+
+ bazel run :Main > $TEST_log || fail "Java sandwich build failed"
+ expect_log "Message from B"
+}
+
+function test_java_import_exports_java_sandwich() {
+ write_files_for_java_provider_in_attr "java_import" "exports"
+ write_java_custom_rule
+
+ bazel run :Main > $TEST_log || fail "Java sandwich build failed"
+ expect_log "Message from B"
+}
+
+function test_java_import_runtime_deps_java_sandwich() {
+ write_files_for_java_provider_in_attr "java_import" "runtime_deps"
+ write_java_custom_rule
+
+ bazel run :Main > $TEST_log || fail "Java sandwich build failed"
+ expect_log "Message from B"
+}
+
function test_java_binary_deps_java_sandwich() {
mkdir -p java/com/google/sandwich
cd java/com/google/sandwich