| 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.85.2-SNAPSHOT" // CURRENT_GRPC_VERSION | |
| repositories { | |
| maven { // The google mirror is less flaky than mavenCentral() | |
| url = "https://maven-central.storage-download.googleapis.com/maven2/" | |
| metadataSources { | |
| mavenPom() | |
| ignoreGradleMetadataRedirection() | |
| } | |
| } | |
| mavenCentral() { | |
| metadataSources { | |
| mavenPom() | |
| ignoreGradleMetadataRedirection() | |
| } | |
| } | |
| mavenLocal() { | |
| metadataSources { | |
| mavenPom() | |
| ignoreGradleMetadataRedirection() | |
| } | |
| } | |
| maven { url 'https://central.sonatype.com/repository/maven-snapshots/' } | |
| } | |
| 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/sources/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("com.android.base") { | |
| android { | |
| lint { | |
| abortOnError true | |
| if (rootProject.hasProperty('failOnWarnings') && rootProject.failOnWarnings.toBoolean()) { | |
| warningsAsErrors true | |
| } | |
| } | |
| } | |
| tasks.withType(JavaCompile).configureEach { | |
| it.options.compilerArgs += [ | |
| "-Xlint:all" | |
| ] | |
| if (rootProject.hasProperty('failOnWarnings') && rootProject.failOnWarnings.toBoolean()) { | |
| it.options.compilerArgs += ["-Werror"] | |
| } | |
| } | |
| } | |
| 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.withType(Test).configureEach { | |
| 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.gradleup.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 | |
| String baseUrl = "https://ossrh-staging-api.central.sonatype.com/service/local" | |
| if (rootProject.hasProperty('repositoryId')) { | |
| stagingUrl = "${baseUrl}/staging/deployByRepositoryId/" + rootProject.repositoryId | |
| } else { | |
| stagingUrl = "${baseUrl}/staging/deploy/maven2/" | |
| } | |
| credentials { | |
| if (rootProject.hasProperty('ossrhUsername') && rootProject.hasProperty('ossrhPassword')) { | |
| username = rootProject.ossrhUsername | |
| password = rootProject.ossrhPassword | |
| } | |
| } | |
| def releaseUrl = stagingUrl | |
| def snapshotUrl = 'https://central.sonatype.com/repository/maven-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", layout.projectDirectory.file("gradle/libs.versions.toml")) | |