diff --git a/build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ProtobufPluginAction.java b/build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ProtobufPluginAction.java index 897c4eadacb1..5d11a9e5aab8 100644 --- a/build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ProtobufPluginAction.java +++ b/build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ProtobufPluginAction.java @@ -30,6 +30,7 @@ import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.DependencyResolveDetails; import org.gradle.api.artifacts.ModuleVersionSelector; import org.gradle.api.artifacts.component.ModuleComponentIdentifier; @@ -62,11 +63,27 @@ public Class> getPluginClass() { public void execute(Project project) { ProtobufExtension protobuf = project.getExtensions().getByType(ProtobufExtension.class); protobuf.protoc(this::configureProtoc); - protobuf.plugins(this::configurePlugins); - protobuf.generateProtoTasks(this::configureGenerateProtoTasks); project.getConfigurations() .named(this::isProtobufToolsLocator) .configureEach((configuration) -> configureProtobufToolsLocator(project, configuration)); + // gRPC plugin configuration is deferred so we can check if gRPC is actually + // declared as a dependency before registering the protoc-gen-grpc-java artifact. + // Without this guard, Gradle would try to resolve io.grpc:protoc-gen-grpc-java:null + // for projects that use protobuf but not gRPC. + project.afterEvaluate((p) -> { + if (hasGrpcDependency(p)) { + protobuf.plugins(this::configurePlugins); + protobuf.generateProtoTasks(this::configureGenerateProtoTasks); + } + }); + } + + private boolean hasGrpcDependency(Project project) { + return project.getConfigurations() + .stream() + .flatMap((configuration) -> configuration.getDependencies().stream()) + .map(Dependency::getGroup) + .anyMatch("io.grpc"::equals); } private void configureProtoc(ExecutableLocator protoc) { diff --git a/build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ProtobufPluginActionIntegrationTests.java b/build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ProtobufPluginActionIntegrationTests.java index aef986a3f756..332857901e5a 100644 --- a/build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ProtobufPluginActionIntegrationTests.java +++ b/build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ProtobufPluginActionIntegrationTests.java @@ -94,7 +94,12 @@ void usesVersionOfProtocDependencyWhenSpecified() { void usesVersionOfGrpcPluginDependencyWhenSpecified() { assertThat(this.gradleBuild.build("dependencies", "--configuration", "protobufToolsLocator_grpc").getOutput()) .contains("io.grpc:protoc-gen-grpc-java:1.78.0"); + } + @TestTemplate + void doesNotConfigureGrpcWhenGrpcIsNotPresent() { + assertThat(this.gradleBuild.build("grpcNotConfigured").getOutput()) + .contains("grpc tools locator present: false"); } } diff --git a/build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ProtobufPluginActionIntegrationTests-doesNotConfigureGrpcWhenGrpcIsNotPresent.gradle b/build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ProtobufPluginActionIntegrationTests-doesNotConfigureGrpcWhenGrpcIsNotPresent.gradle new file mode 100644 index 000000000000..079b684c5eb7 --- /dev/null +++ b/build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ProtobufPluginActionIntegrationTests-doesNotConfigureGrpcWhenGrpcIsNotPresent.gradle @@ -0,0 +1,40 @@ +/* + * Copyright 2012-present the original author or authors. + * + * 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 + * + * https://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. + */ + +plugins { + id 'org.springframework.boot' version '{version}' + id 'java' +} + +apply plugin: 'com.google.protobuf' + +group = 'com.example' +version = '0.0.1' + +repositories { + mavenCentral() +} + +dependencies { + implementation("com.google.protobuf:protobuf-java:4.34.0") +} + +tasks.register("grpcNotConfigured") { + doFirst { + def hasGrpcToolsLocator = project.configurations.names.any { it == "protobufToolsLocator_grpc" } + println "grpc tools locator present: $hasGrpcToolsLocator" + } +} diff --git a/module/spring-boot-test-classic-modules/build.gradle b/module/spring-boot-test-classic-modules/build.gradle index 0b7158ab5609..4c0ce74758b3 100644 --- a/module/spring-boot-test-classic-modules/build.gradle +++ b/module/spring-boot-test-classic-modules/build.gradle @@ -67,9 +67,6 @@ dependencies { api(project(":module:spring-boot-graphql-test")) { transitive = false } - api(project(":module:spring-boot-grpc-test")) { - transitive = false - } api(project(":module:spring-boot-jdbc-test")) { transitive = false }