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