| 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 net.ltgt.gradle.errorprone.CheckSeverity |
| import org.gradle.util.GUtil |
| |
| 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.56.1-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" |
| 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 -> |
| // Recompile protos when build.gradle has been changed, because |
| // it's possible the version of protoc has been changed. |
| task.inputs.file("${rootProject.projectDir}/build.gradle") |
| .withPathSensitivity(PathSensitivity.RELATIVE) |
| .withPropertyName('root build.gradle') |
| 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 = libs.checkstyle.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") { |
| sourceCompatibility = 1.8 |
| targetCompatibility = 1.8 |
| |
| 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 |
| links 'https://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.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) |
| } |
| 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) |
| } |
| |
| 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') { |
| inputs.files(configurations.runtimeClasspath).withNormalizer(ClasspathNormalizer) |
| outputs.file("${buildDir}/tmp/${name}") // Fake output for UP-TO-DATE checking |
| doLast { |
| requireUpperBoundDepsMatch(configurations.runtimeClasspath, project) |
| } |
| } |
| tasks.named('compileJava').configure { |
| dependsOn checkUpperBoundDeps |
| } |
| } |
| |
| plugins.withId("me.champeau.jmh") { |
| // invoke jmh on a single benchmark class like so: |
| // ./gradlew -PjmhIncludeSingleClass=StatsTraceContextBenchmark clean :grpc-core:jmh |
| tasks.named("compileJmhJava").configure { |
| sourceCompatibility = 1.8 |
| targetCompatibility = 1.8 |
| } |
| 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:[email protected]: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 = "[email protected]" |
| 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 |
| File baselineArtifact = null |
| // Use a detached configuration, otherwise the current version's jar will take precedence |
| // over the baseline jar. |
| // 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' |
| String depModule = "io.grpc:${project.name}:${baselineGrpcVersion}@jar" |
| String depJar = "${project.name}-${baselineGrpcVersion}.jar" |
| Configuration configuration = configurations.detachedConfiguration( |
| dependencies.create(depModule) |
| ) |
| baselineArtifact = files(configuration.files).filter { |
| it.name.equals(depJar) |
| }.singleFile |
| } finally { |
| project.group = oldGroup |
| } |
| |
| // Add a japicmp task that compares the current .jar with baseline .jar |
| tasks.register("japicmp", me.champeau.gradle.japicmp.JapicmpTask) { |
| dependsOn jar |
| oldClasspath = files(baselineArtifact) |
| newClasspath = files(jar.archiveFile) |
| 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. |
| // TODO(zpencer): enable after japicmp-gradle-plugin/pull/14 |
| // breakOnSourceIncompatibility = true |
| |
| // Ignore any classes or methods marked @ExperimentalApi |
| // TODO(zpencer): enable after japicmp-gradle-plugin/pull/15 |
| // annotationExcludes = ['@io.grpc.ExperimentalApi'] |
| } |
| } |
| } |
| |
| class DepAndParents { |
| DependencyResult dep |
| List<String> parents |
| } |
| |
| /** |
| * Make sure that Maven would select the same versions as Gradle selected. |
| * This is essentially the same as if we used Maven Enforcer's |
| * requireUpperBoundDeps for our artifacts. |
| */ |
| def requireUpperBoundDepsMatch(Configuration conf, Project project) { |
| // artifact name => version |
| Map<String,String> golden = conf.resolvedConfiguration.resolvedArtifacts.collectEntries { |
| ResolvedArtifact it -> |
| ModuleVersionIdentifier id = it.moduleVersion.id |
| [id.group + ":" + id.name, id.version] |
| } |
| // Breadth-first search like Maven for dependency resolution |
| Queue<DepAndParents> queue = new ArrayDeque<>() |
| conf.incoming.resolutionResult.root.dependencies.each { |
| queue.add(new DepAndParents(dep: it, parents: [project.displayName])) |
| } |
| Set<String> found = new HashSet<>() |
| while (!queue.isEmpty()) { |
| DepAndParents depAndParents = queue.remove() |
| ResolvedDependencyResult result = (ResolvedDependencyResult) depAndParents.dep |
| ModuleVersionIdentifier id = result.selected.moduleVersion |
| String artifact = id.group + ":" + id.name |
| if (found.contains(artifact)) |
| continue |
| found.add(artifact) |
| String version |
| if (result.requested instanceof ProjectComponentSelector) { |
| ProjectComponentSelector selector = (ProjectComponentSelector) result.requested |
| version = project.findProject(selector.projectPath).version |
| } else { |
| version = ((ModuleComponentSelector) result.requested).version |
| } |
| String goldenVersion = golden[artifact] |
| if (goldenVersion != version && "[$goldenVersion]" != version) { |
| throw new RuntimeException( |
| "Maven version skew: $artifact ($version != $goldenVersion) " |
| + "Bad version dependency path: " + depAndParents.parents |
| + " Run './gradlew $project.path:dependencies --configuration $conf.name' " |
| + "to diagnose") |
| } |
| result.selected.dependencies.each { |
| queue.add(new DepAndParents( |
| dep: it, parents: depAndParents.parents + [artifact + ":" + version])) |
| } |
| } |
| } |
| |
| 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 (module == 'android-api-level-19') |
| 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") |
| } |
| } |
| } |
| } |
| } |
| |
| // Checks every dependency in the version catalog to see if there is a newer |
| // version available. The 'checkForUpdates' configuration restricts the |
| // versions considered. |
| tasks.register('checkForUpdates') { |
| doLast { |
| def updateConf = project.configurations.checkForUpdates |
| updateConf.setVisible(false) |
| updateConf.setTransitive(false) |
| def versionCatalog = project.extensions.getByType(VersionCatalogsExtension).named("libs") |
| versionCatalog.libraryAliases.each { name -> |
| def dep = versionCatalog.findLibrary(name).get().get() |
| |
| def oldConf = updateConf.copy() |
| def oldDep = project.dependencies.create( |
| group: dep.group, name: dep.name, version: dep.versionConstraint, classifier: 'pom') |
| oldConf.dependencies.add(oldDep) |
| def oldResolved = oldConf.resolvedConfiguration.resolvedArtifacts.iterator().next() |
| |
| def newConf = updateConf.copy() |
| def newDep = project.dependencies.create( |
| group: dep.group, name: dep.name, version: '+', classifier: 'pom') |
| newConf.dependencies.add(newDep) |
| def newResolved = newConf.resolvedConfiguration.resolvedArtifacts.iterator().next() |
| if (oldResolved != newResolved) { |
| def oldId = oldResolved.id.componentIdentifier |
| def newId = newResolved.id.componentIdentifier |
| println("${newId.group}:${newId.module} ${oldId.version} -> ${newId.version}") |
| } |
| } |
| } |
| } |