grpc-java/build.gradle

503 lines
19 KiB
Groovy

plugins {
id 'java' // changes the behavior of TARGET_JVM_VERSION_ATTRIBUTE
id "com.google.osdetector" apply false
id "me.champeau.gradle.japicmp" apply false
id "net.ltgt.errorprone" apply false
id 'com.google.cloud.tools.jib' apply false
}
import io.grpc.gradle.CheckForUpdatesTask
import io.grpc.gradle.RequireUpperBoundDepsMatchTask
import net.ltgt.gradle.errorprone.CheckSeverity
subprojects {
apply plugin: "checkstyle"
apply plugin: "idea"
apply plugin: "signing"
apply plugin: "jacoco"
apply plugin: "com.google.osdetector"
apply plugin: "net.ltgt.errorprone"
group = "io.grpc"
version = "1.67.2-SNAPSHOT" // CURRENT_GRPC_VERSION
repositories {
maven { // The google mirror is less flaky than mavenCentral()
url "https://maven-central.storage-download.googleapis.com/maven2/" }
mavenCentral()
mavenLocal()
}
tasks.withType(JavaCompile).configureEach {
it.options.compilerArgs += [
"-Xlint:all",
"-Xlint:-options",
"-Xlint:-path",
"-Xlint:-try"
]
it.options.encoding = "UTF-8"
// Avoid Gradle OOM.
// https://docs.gradle.org/current/userguide/performance.html#run_the_compiler_as_a_separate_process
it.options.fork = true
if (rootProject.hasProperty('failOnWarnings') && rootProject.failOnWarnings.toBoolean()) {
it.options.compilerArgs += ["-Werror"]
}
}
tasks.withType(GenerateModuleMetadata).configureEach {
// Module metadata, introduced in Gradle 6.0, conflicts with our publishing task for
// grpc-alts and grpc-compiler.
enabled = false
}
def isAndroid = project.name in [
'grpc-android', 'grpc-android-interop-testing', 'grpc-cronet']
ext {
def exeSuffix = osdetector.os == 'windows' ? ".exe" : ""
protocPluginBaseName = 'protoc-gen-grpc-java'
javaPluginPath = "$rootDir/compiler/build/exe/java_plugin/$protocPluginBaseName$exeSuffix"
configureProtoCompilation = {
String generatedSourcePath = "${projectDir}/src/generated"
project.protobuf {
protoc {
if (project.hasProperty('protoc')) {
path = project.protoc
} else {
artifact = libs.protobuf.protoc.get()
}
}
generateProtoTasks {
all().each { task ->
if (isAndroid) {
task.builtins {
java { option 'lite' }
}
}
}
}
}
if (rootProject.childProjects.containsKey('grpc-compiler')) {
// Only when the codegen is built along with the project, will we be able to run
// the grpc code generator.
def syncGeneratedSources = tasks.register("syncGeneratedSources") { }
project.protobuf {
plugins { grpc { path = javaPluginPath } }
generateProtoTasks {
all().each { task ->
String variantOrSourceSet = isAndroid ? task.variant.name : task.sourceSet.name
def syncTask = project.tasks.register("syncGeneratedSources${variantOrSourceSet}", Sync) {
from task
into "$generatedSourcePath/$variantOrSourceSet"
include "grpc/"
}
syncGeneratedSources.configure {
dependsOn syncTask
}
task.configure {
dependsOn ':grpc-compiler:java_pluginExecutable'
// Recompile protos when the codegen has been changed
inputs.file javaPluginPath
plugins { grpc { option 'noversion' } }
if (isAndroid) {
plugins {
grpc {
option 'lite'
}
}
}
}
}
}
}
// Re-sync as part of a normal build, to avoid forgetting to run the sync
tasks.named("assemble").configure {
dependsOn syncGeneratedSources
}
} else {
// Otherwise, we just use the checked-in generated code.
if (isAndroid) {
project.android.sourceSets {
debug { java { srcDir "${generatedSourcePath}/debug/grpc" } }
release { java { srcDir "${generatedSourcePath}/release/grpc" } }
}
} else {
project.sourceSets.each() { sourceSet ->
sourceSet.java { srcDir "${generatedSourcePath}/${sourceSet.name}/grpc" }
}
}
}
tasks.withType(JavaCompile).configureEach {
appendToProperty(
it.options.errorprone.excludedPaths,
".*/src/generated/[^/]+/java/.*" +
"|.*/build/generated/source/proto/[^/]+/java/.*",
"|")
}
}
libraries = libs
appendToProperty = { Property<String> property, String value, String separator ->
if (property.present) {
property.set(property.get() + separator + value)
} else {
property.set(value)
}
}
}
// Disable JavaDoc doclint on Java 8. It's annoying.
if (JavaVersion.current().isJava8Compatible()) {
allprojects {
tasks.withType(Javadoc).configureEach {
options.addStringOption('Xdoclint:none', '-quiet')
}
}
}
checkstyle {
configDirectory = file("$rootDir/buildscripts")
toolVersion = JavaVersion.current().isJava11Compatible() ? libs.checkstyle.get().version : libs.checkstylejava8.get().version
ignoreFailures = false
if (rootProject.hasProperty("checkstyle.ignoreFailures")) {
ignoreFailures = rootProject.properties["checkstyle.ignoreFailures"].toBoolean()
}
}
if (!project.hasProperty('errorProne') || errorProne.toBoolean()) {
dependencies {
errorprone JavaVersion.current().isJava11Compatible() ? libs.errorprone.core : libs.errorprone.corejava8
}
} else {
// Disable Error Prone
tasks.withType(JavaCompile).configureEach {
options.errorprone.enabled = false
}
}
plugins.withId("java") {
dependencies {
testImplementation libraries.junit,
libraries.mockito.core,
libraries.truth
}
tasks.named("compileTestJava").configure {
// serialVersionUID is basically guaranteed to be useless in our tests
options.compilerArgs += [
"-Xlint:-serial"
]
}
tasks.named("jar").configure {
manifest {
attributes('Implementation-Title': name,
'Implementation-Version': project.version)
}
}
tasks.named("javadoc").configure {
options {
encoding = 'UTF-8'
use = true
linksOffline 'https://docs.oracle.com/javase/8/docs/api/',
"${rootProject.projectDir}/gradle/javadoc/docs.oracle.com-javase-8-docs-api/"
source = "8"
}
}
tasks.named("checkstyleMain").configure {
source = fileTree(dir: "$projectDir/src/main", include: "**/*.java")
}
tasks.named("checkstyleTest").configure {
source = fileTree(dir: "$projectDir/src/test", include: "**/*.java")
}
// At a test failure, log the stack trace to the console so that we don't
// have to open the HTML in a browser.
tasks.named("test").configure {
testLogging {
exceptionFormat = 'full'
showExceptions true
showCauses true
showStackTraces true
}
maxHeapSize = '1500m'
}
if (!project.hasProperty('errorProne') || errorProne.toBoolean()) {
dependencies {
annotationProcessor libs.guava.betaChecker
}
}
tasks.withType(JavaCompile).configureEach {
if (JavaVersion.current().isJava9Compatible()) {
options.release = 8
} else {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
tasks.named("compileJava").configure {
// This project targets Java 7 (no time.Duration class)
options.errorprone.check("PreferJavaTimeOverload", CheckSeverity.OFF)
options.errorprone.check("JavaUtilDate", CheckSeverity.OFF)
// The warning fails to provide a source location
options.errorprone.check("MissingSummary", CheckSeverity.OFF)
// This check is in libs.errorprone.corejava8 but has been removed
// in later versions. It isn't smart enough to realize the field is
// actually immutable. And it also doesn't complain about arrays
// that are actually mutable.
options.errorprone.check("MutableConstantField", CheckSeverity.OFF)
}
tasks.named("compileTestJava").configure {
// LinkedList doesn't hurt much in tests and has lots of usages
options.errorprone.check("JdkObsolete", CheckSeverity.OFF)
options.errorprone.check("PreferJavaTimeOverload", CheckSeverity.OFF)
options.errorprone.check("JavaUtilDate", CheckSeverity.OFF)
// This check is in libs.errorprone.corejava8 but has been removed
// in later versions. It isn't smart enough to realize the field is
// actually immutable. And it also doesn't complain about arrays
// that are actually mutable.
options.errorprone.check("MutableConstantField", CheckSeverity.OFF)
}
plugins.withId("ru.vyarus.animalsniffer") {
// Only available after java plugin has loaded
animalsniffer {
toolVersion = libs.animalsniffer.asProvider().get().version
}
}
}
plugins.withId("java-library") {
// Detect Maven Enforcer's dependencyConvergence failures. We only care
// for artifacts used as libraries by others with Maven.
tasks.register('checkUpperBoundDeps', RequireUpperBoundDepsMatchTask) {
configuration = configurations.getByName('runtimeClasspath')
}
tasks.named('assemble').configure {
dependsOn tasks.named('checkUpperBoundDeps')
}
}
plugins.withId("me.champeau.jmh") {
// invoke jmh on a single benchmark class like so:
// ./gradlew -PjmhIncludeSingleClass=StatsTraceContextBenchmark clean :grpc-core:jmh
tasks.named("jmh").configure {
warmupIterations = 10
iterations = 10
fork = 1
// None of our benchmarks need the tests, and we have pseudo-circular
// dependencies that break when including them. (context's testCompile
// depends on core; core's testCompile depends on testing)
includeTests = false
if (project.hasProperty('jmhIncludeSingleClass')) {
includes = [
project.property('jmhIncludeSingleClass')
]
}
}
}
plugins.withId("com.github.johnrengelman.shadow") {
tasks.named("shadowJar").configure {
// Do a dance to remove Class-Path. This needs to run after the doFirst() from the
// shadow plugin that adds Class-Path and before the core jar action. Using doFirst will
// have this run before the shadow plugin, and doLast will run after the core jar
// action. See #8606.
// The shadow plugin adds another doFirst when application is used for setting
// Main-Class. Ordering with it doesn't matter.
actions.add(plugins.hasPlugin("application") ? 2 : 1, new Action<Task>() {
@Override public void execute(Task task) {
if (!task.manifest.attributes.remove("Class-Path")) {
throw new AssertionError("Did not find Class-Path to remove from manifest")
}
}
})
}
}
plugins.withId("maven-publish") {
publishing {
publications {
// do not use mavenJava, as java plugin will modify it via "magic"
maven(MavenPublication) {
pom {
name = project.group + ":" + project.name
url = 'https://github.com/grpc/grpc-java'
afterEvaluate {
// description is not available until evaluated.
description = project.description
}
scm {
connection = 'scm:git:https://github.com/grpc/grpc-java.git'
developerConnection = 'scm:git:git@github.com:grpc/grpc-java.git'
url = 'https://github.com/grpc/grpc-java'
}
licenses {
license {
name = 'Apache 2.0'
url = 'https://opensource.org/licenses/Apache-2.0'
}
}
developers {
developer {
id = "grpc.io"
name = "gRPC Contributors"
email = "grpc-io@googlegroups.com"
url = "https://grpc.io/"
organization = "gRPC Authors"
organizationUrl = "https://www.google.com"
}
}
}
}
}
repositories {
maven {
if (rootProject.hasProperty('repositoryDir')) {
url = new File(rootProject.repositoryDir).toURI()
} else {
String stagingUrl
if (rootProject.hasProperty('repositoryId')) {
stagingUrl = 'https://oss.sonatype.org/service/local/staging/deployByRepositoryId/' +
rootProject.repositoryId
} else {
stagingUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
}
credentials {
if (rootProject.hasProperty('ossrhUsername') && rootProject.hasProperty('ossrhPassword')) {
username = rootProject.ossrhUsername
password = rootProject.ossrhPassword
}
}
def releaseUrl = stagingUrl
def snapshotUrl = 'https://oss.sonatype.org/content/repositories/snapshots/'
url = version.endsWith('SNAPSHOT') ? snapshotUrl : releaseUrl
}
}
}
}
signing {
required false
sign publishing.publications.maven
}
plugins.withId("java") {
java {
withJavadocJar()
withSourcesJar()
}
publishing {
publications {
maven {
if (project.name != 'grpc-netty-shaded') {
from components.java
}
}
}
}
}
}
// Run with: ./gradlew japicmp --continue
plugins.withId("me.champeau.gradle.japicmp") {
def baselineGrpcVersion = '1.6.1'
// Get the baseline version's jar for this subproject
configurations {
baselineArtifact
}
// A necessary hack, the intuitive thing does NOT work:
// https://discuss.gradle.org/t/is-the-default-configuration-leaking-into-independent-configurations/2088/6
def oldGroup = project.group
try {
project.group = 'virtual_group_for_japicmp'
dependencies {
baselineArtifact "io.grpc:${project.name}:${baselineGrpcVersion}@jar"
}
} finally {
project.group = oldGroup
}
// Add a japicmp task that compares the current .jar with baseline .jar
tasks.register("japicmp", me.champeau.gradle.japicmp.JapicmpTask) {
oldClasspath.from configurations.baselineArtifact
newClasspath.from tasks.named("jar")
onlyBinaryIncompatibleModified = false
// Be quiet about things that did not change
onlyModified = true
// This task should fail if there are incompatible changes
failOnModification = true
ignoreMissingClasses = true
htmlOutputFile = file("$buildDir/reports/japi.html")
packageExcludes = ['io.grpc.internal']
// Also break on source incompatible changes, not just binary.
// Eg adding abstract method to public class.
failOnSourceIncompatibility = true
// Ignore any classes or methods marked @ExperimentalApi
annotationExcludes = ['@io.grpc.ExperimentalApi']
}
}
}
repositories {
mavenCentral()
google()
}
def isAcceptableVersion(ModuleComponentIdentifier candidate) {
String group = candidate.group
String module = candidate.module
String version = candidate.version
if (group == 'com.google.guava')
return true
if (group == 'io.netty' && version.contains('Final'))
return true
if (group == 'io.undertow' && version.contains('Final'))
return true
if (module == 'android-api-level-19')
return true
if (module == 'opentelemetry-exporter-prometheus')
return true
if (module == 'opentelemetry-gcp-resources')
return true
return version ==~ /^[0-9]+(\.[0-9]+)+$/
}
configurations {
checkForUpdates {
attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 8)
resolutionStrategy {
componentSelection {
all {
if (!isAcceptableVersion(it.candidate))
it.reject("Not stable version")
}
}
}
}
}
tasks.register('checkForUpdates', CheckForUpdatesTask, project.configurations.checkForUpdates, "libs")