1buildscript {
2    repositories {
3        mavenLocal()
4        maven { url "https://plugins.gradle.org/m2/" }
5    }
6    dependencies {
7        classpath "com.diffplug.spotless:spotless-plugin-gradle:3.13.0"
8        classpath 'com.google.gradle:osdetector-gradle-plugin:1.4.0'
9        classpath 'ru.vyarus:gradle-animalsniffer-plugin:1.4.5'
10        classpath 'net.ltgt.gradle:gradle-errorprone-plugin:0.0.13'
11        classpath 'net.ltgt.gradle:gradle-apt-plugin:0.13'
12        classpath "me.champeau.gradle:jmh-gradle-plugin:0.4.5"
13        classpath 'me.champeau.gradle:japicmp-gradle-plugin:0.2.5'
14    }
15}
16
17subprojects {
18    apply plugin: "checkstyle"
19    apply plugin: "java"
20    apply plugin: "maven"
21    apply plugin: "idea"
22    apply plugin: "signing"
23    apply plugin: "jacoco"
24
25    apply plugin: "me.champeau.gradle.jmh"
26    apply plugin: "com.google.osdetector"
27    // The plugin only has an effect if a signature is specified
28    apply plugin: "ru.vyarus.animalsniffer"
29    // jdk10 not supported by errorprone: https://github.com/google/error-prone/issues/860
30    if (!JavaVersion.current().isJava10Compatible() &&
31        rootProject.properties.get('errorProne', true)) {
32        apply plugin: "net.ltgt.errorprone"
33        apply plugin: "net.ltgt.apt"
34
35        dependencies {
36            // The ErrorProne plugin defaults to the latest, which would break our
37            // build if error prone releases a new version with a new check
38            errorprone 'com.google.errorprone:error_prone_core:2.2.0'
39            apt 'com.google.guava:guava-beta-checker:1.0'
40        }
41    } else {
42        // Remove per-project error-prone checker config
43        allprojects {
44            afterEvaluate { project ->
45                project.tasks.withType(JavaCompile) {
46                    options.compilerArgs.removeAll { it.startsWith("-Xep") }
47                }
48            }
49        }
50    }
51    // TODO(zpencer): remove when intellij 2017.2 is released
52    // https://github.com/gradle/gradle/issues/2315
53    idea.module.inheritOutputDirs = true
54
55    group = "io.grpc"
56    version = "1.16.0-SNAPSHOT" // CURRENT_GRPC_VERSION
57
58    sourceCompatibility = 1.7
59    targetCompatibility = 1.7
60
61    repositories {
62        maven { // The google mirror is less flaky than mavenCentral()
63            url "https://maven-central.storage-download.googleapis.com/repos/central/data/" }
64        mavenLocal()
65    }
66
67    [
68        compileJava,
69        compileTestJava,
70        compileJmhJava
71    ].each() {
72        it.options.compilerArgs += [
73            "-Xlint:all",
74            "-Xlint:-options",
75            "-Xlint:-path",
76            "-Xlint:-try"
77        ]
78        it.options.encoding = "UTF-8"
79        if (rootProject.hasProperty('failOnWarnings') && rootProject.failOnWarnings.toBoolean()) {
80            it.options.compilerArgs += ["-Werror"]
81        }
82    }
83
84    compileTestJava {
85        // serialVersionUID is basically guaranteed to be useless in our tests
86        // LinkedList doesn't hurt much in tests and has lots of usages
87        options.compilerArgs += [
88            "-Xlint:-serial",
89            "-Xep:JdkObsolete:OFF"
90        ]
91    }
92
93    jar.manifest {
94        attributes('Implementation-Title': name,
95        'Implementation-Version': version,
96        'Built-By': System.getProperty('user.name'),
97        'Built-JDK': System.getProperty('java.version'),
98        'Source-Compatibility': sourceCompatibility,
99        'Target-Compatibility': targetCompatibility)
100    }
101
102    javadoc.options {
103        encoding = 'UTF-8'
104        use = true
105        links 'https://docs.oracle.com/javase/8/docs/api/'
106    }
107
108    ext {
109        def exeSuffix = osdetector.os == 'windows' ? ".exe" : ""
110        protocPluginBaseName = 'protoc-gen-grpc-java'
111        javaPluginPath = "$rootDir/compiler/build/exe/java_plugin/$protocPluginBaseName$exeSuffix"
112
113        nettyVersion = '4.1.27.Final'
114        guavaVersion = '20.0'
115        protobufVersion = '3.5.1'
116        protocVersion = '3.5.1-1'
117        protobufNanoVersion = '3.0.0-alpha-5'
118        opencensusVersion = '0.12.3'
119
120        configureProtoCompilation = {
121            String generatedSourcePath = "${projectDir}/src/generated"
122            if (rootProject.childProjects.containsKey('grpc-compiler')) {
123                // Only when the codegen is built along with the project, will we be able to recompile
124                // the proto files.
125                project.apply plugin: 'com.google.protobuf'
126                project.protobuf {
127                    protoc {
128                        if (project.hasProperty('protoc')) {
129                            path = project.protoc
130                        } else {
131                            artifact = "com.google.protobuf:protoc:${protocVersion}"
132                        }
133                    }
134                    plugins { grpc { path = javaPluginPath } }
135                    generateProtoTasks {
136                        all().each { task ->
137                            task.dependsOn ':grpc-compiler:java_pluginExecutable'
138                            // Delete the generated sources first, so that we can be alerted if they are not re-compiled.
139                            task.dependsOn 'deleteGeneratedSource' + task.sourceSet.name
140                            // Recompile protos when the codegen has been changed
141                            task.inputs.file javaPluginPath
142                            // Recompile protos when build.gradle has been changed, because
143                            // it's possible the version of protoc has been changed.
144                            task.inputs.file "${rootProject.projectDir}/build.gradle"
145                            task.plugins { grpc { option 'noversion' } }
146                        }
147                    }
148                    generatedFilesBaseDir = generatedSourcePath
149                }
150
151                sourceSets.each { sourceSet ->
152                    task "deleteGeneratedSource${sourceSet.name}" {
153                        doLast {
154                            project.delete project.fileTree(dir: generatedSourcePath + '/' + sourceSet.name)
155                        }
156                    }
157                }
158            } else {
159                // Otherwise, we just use the checked-in generated code.
160                project.sourceSets {
161                    main {
162                        java {
163                            srcDir "${generatedSourcePath}/main/java"
164                            srcDir "${generatedSourcePath}/main/javanano"
165                            srcDir "${generatedSourcePath}/main/grpc"
166                        }
167                    }
168                    test {
169                        java {
170                            srcDir "${generatedSourcePath}/test/java"
171                            srcDir "${generatedSourcePath}/test/javanano"
172                            srcDir "${generatedSourcePath}/test/grpc"
173                        }
174                    }
175                }
176            }
177
178            [
179                compileJava,
180                compileTestJava,
181                compileJmhJava
182            ].each() {
183                // Protobuf-generated code produces some warnings.
184                // https://github.com/google/protobuf/issues/2718
185                it.options.compilerArgs += [
186                    "-Xlint:-cast",
187                    "-XepExcludedPaths:.*/src/generated/[^/]+/java/.*",
188                ]
189            }
190        }
191
192        def epoll_suffix = "";
193        if (osdetector.classifier in ["linux-x86_64"]) {
194            // The native code is only pre-compiled on certain platforms.
195            epoll_suffix = ":" + osdetector.classifier
196        }
197        libraries = [
198            animalsniffer_annotations: "org.codehaus.mojo:animal-sniffer-annotations:1.17",
199            errorprone: "com.google.errorprone:error_prone_annotations:2.2.0",
200            gson: "com.google.code.gson:gson:2.7",
201            guava: "com.google.guava:guava:${guavaVersion}",
202            hpack: 'com.twitter:hpack:0.10.1',
203            javax_annotation: 'javax.annotation:javax.annotation-api:1.2',
204            jsr305: 'com.google.code.findbugs:jsr305:3.0.0',
205            oauth_client: 'com.google.auth:google-auth-library-oauth2-http:0.9.0',
206            google_api_protos: 'com.google.api.grpc:proto-google-common-protos:1.0.0',
207            google_auth_credentials: 'com.google.auth:google-auth-library-credentials:0.9.0',
208            google_auth_oauth2_http: 'com.google.auth:google-auth-library-oauth2-http:0.9.0',
209            okhttp: 'com.squareup.okhttp:okhttp:2.5.0',
210            okio: 'com.squareup.okio:okio:1.13.0',
211            opencensus_api: "io.opencensus:opencensus-api:${opencensusVersion}",
212            opencensus_contrib_grpc_metrics: "io.opencensus:opencensus-contrib-grpc-metrics:${opencensusVersion}",
213            opencensus_impl: "io.opencensus:opencensus-impl:${opencensusVersion}",
214            opencensus_impl_lite: "io.opencensus:opencensus-impl-lite:${opencensusVersion}",
215            instrumentation_api: 'com.google.instrumentation:instrumentation-api:0.4.3',
216            protobuf: "com.google.protobuf:protobuf-java:${protobufVersion}",
217            protobuf_lite: "com.google.protobuf:protobuf-lite:3.0.1",
218            protoc_lite: "com.google.protobuf:protoc-gen-javalite:3.0.0",
219            protobuf_nano: "com.google.protobuf.nano:protobuf-javanano:${protobufNanoVersion}",
220            protobuf_plugin: 'com.google.protobuf:protobuf-gradle-plugin:0.8.5',
221            protobuf_util: "com.google.protobuf:protobuf-java-util:${protobufVersion}",
222            lang: "org.apache.commons:commons-lang3:3.5",
223
224            netty: "io.netty:netty-codec-http2:[${nettyVersion}]",
225            netty_epoll: "io.netty:netty-transport-native-epoll:${nettyVersion}" + epoll_suffix,
226            netty_proxy_handler: "io.netty:netty-handler-proxy:${nettyVersion}",
227            netty_tcnative: 'io.netty:netty-tcnative-boringssl-static:2.0.12.Final',
228
229            conscrypt: 'org.conscrypt:conscrypt-openjdk-uber:1.0.1',
230            re2j: 'com.google.re2j:re2j:1.2',
231
232            // Test dependencies.
233            junit: 'junit:junit:4.12',
234            mockito: 'org.mockito:mockito-core:1.9.5',
235            truth: 'com.google.truth:truth:0.42',
236            guava_testlib: 'com.google.guava:guava-testlib:20.0',
237
238            // Benchmark dependencies
239            hdrhistogram: 'org.hdrhistogram:HdrHistogram:2.1.10',
240            math: 'org.apache.commons:commons-math3:3.6',
241
242            // Jetty ALPN dependencies
243            jetty_alpn_agent: 'org.mortbay.jetty.alpn:jetty-alpn-agent:2.0.7'
244        ]
245    }
246
247    // Define a separate configuration for managing the dependency on Jetty ALPN agent.
248    configurations {
249        alpnagent
250
251        compile {
252            // Detect Maven Enforcer's dependencyConvergence failures. We only
253            // care for artifacts used as libraries by others.
254            if (!(project.name in [
255                'grpc-benchmarks',
256                'grpc-interop-testing',
257                'grpc-gae-interop-testing-jdk7',
258                'grpc-gae-interop-testing-jdk8',
259            ])) {
260                resolutionStrategy.failOnVersionConflict()
261            }
262        }
263    }
264
265    dependencies {
266        testCompile libraries.junit,
267                libraries.mockito,
268                libraries.truth
269
270        // Configuration for modules that use Jetty ALPN agent
271        alpnagent libraries.jetty_alpn_agent
272
273        jmh 'org.openjdk.jmh:jmh-core:1.19',
274                'org.openjdk.jmh:jmh-generator-bytecode:1.19'
275    }
276
277    signing {
278        required false
279        sign configurations.archives
280    }
281
282    // Disable JavaDoc doclint on Java 8. It's annoying.
283    if (JavaVersion.current().isJava8Compatible()) {
284        allprojects {
285            tasks.withType(Javadoc) {
286                options.addStringOption('Xdoclint:none', '-quiet')
287            }
288        }
289    }
290
291    // For jdk10 we must explicitly choose between html4 and html5, otherwise we get a warning
292    if (JavaVersion.current().isJava10Compatible()) {
293        allprojects {
294            tasks.withType(Javadoc) {
295                options.addBooleanOption('html4', true)
296            }
297        }
298    }
299
300    checkstyle {
301        configDir = file("$rootDir/buildscripts")
302        toolVersion = "6.17"
303        ignoreFailures = false
304        if (rootProject.hasProperty("checkstyle.ignoreFailures")) {
305            ignoreFailures = rootProject.properties["checkstyle.ignoreFailures"].toBoolean()
306        }
307    }
308
309    checkstyleMain {
310        source = fileTree(dir: "src/main", include: "**/*.java")
311    }
312
313    checkstyleTest {
314        source = fileTree(dir: "src/test", include: "**/*.java")
315    }
316
317    // invoke jmh on a single benchmark class like so:
318    //   ./gradlew -PjmhIncludeSingleClass=StatsTraceContextBenchmark clean :grpc-core:jmh
319    jmh {
320        warmupIterations = 10
321        iterations = 10
322        fork = 1
323        // None of our benchmarks need the tests, and we have pseudo-circular
324        // dependencies that break when including them. (context's testCompile
325        // depends on core; core's testCompile depends on testing)
326        includeTests = false
327        if (project.hasProperty('jmhIncludeSingleClass')) {
328            include = [
329                project.property('jmhIncludeSingleClass')
330            ]
331        }
332    }
333
334    task javadocJar(type: Jar) {
335        classifier = 'javadoc'
336        from javadoc
337    }
338
339    task sourcesJar(type: Jar) {
340        classifier = 'sources'
341        from sourceSets.main.allSource
342    }
343
344    artifacts { archives javadocJar, sourcesJar }
345
346    uploadArchives.repositories.mavenDeployer {
347        beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
348        if (rootProject.hasProperty('repositoryDir')) {
349            repository(url: new File(rootProject.repositoryDir).toURI())
350        } else {
351            String stagingUrl
352            if (rootProject.hasProperty('repositoryId')) {
353                stagingUrl = 'https://oss.sonatype.org/service/local/staging/deployByRepositoryId/' +
354                        rootProject.repositoryId
355            } else {
356                stagingUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
357            }
358            def configureAuth = {
359                if (rootProject.hasProperty('ossrhUsername') && rootProject.hasProperty('ossrhPassword')) {
360                    authentication(userName: rootProject.ossrhUsername, password: rootProject.ossrhPassword)
361                }
362            }
363            repository(url: stagingUrl, configureAuth)
364            snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots/', configureAuth)
365        }
366    }
367    uploadArchives.onlyIf { !name.contains("grpc-gae-interop-testing") }
368
369    [
370        install.repositories.mavenInstaller,
371        uploadArchives.repositories.mavenDeployer,
372    ]*.pom*.whenConfigured { pom ->
373        pom.project {
374            name "$project.group:$project.name"
375            description project.description
376            url 'https://github.com/grpc/grpc-java'
377
378            scm {
379                connection 'scm:git:https://github.com/grpc/grpc-java.git'
380                developerConnection 'scm:git:git@github.com:grpc/grpc-java.git'
381                url 'https://github.com/grpc/grpc-java'
382            }
383
384            licenses {
385                license {
386                    name 'Apache 2.0'
387                    url 'https://opensource.org/licenses/Apache-2.0'
388                }
389            }
390
391            developers {
392                developer {
393                    id "grpc.io"
394                    name "gRPC Contributors"
395                    email "grpc-io@googlegroups.com"
396                    url "https://grpc.io/"
397                    // https://issues.gradle.org/browse/GRADLE-2719
398                    organization = "gRPC Authors"
399                    organizationUrl "https://www.google.com"
400                }
401            }
402        }
403        if (!(project.name in
404        [
405            "grpc-stub",
406            "grpc-protobuf",
407            "grpc-protobuf-lite",
408            "grpc-protobuf-nano"
409        ])) {
410            def core = pom.dependencies.find {dep -> dep.artifactId == 'grpc-core'}
411            if (core != null) {
412                // Depend on specific version of grpc-core because internal package is unstable
413                core.version = "[" + core.version + "]"
414            }
415        }
416    }
417    // At a test failure, log the stack trace to the console so that we don't
418    // have to open the HTML in a browser.
419    test {
420        testLogging {
421            exceptionFormat = 'full'
422            showExceptions true
423            showCauses true
424            showStackTraces true
425        }
426        maxHeapSize = '1500m'
427    }
428}
429
430// Run with: ./gradlew japicmp --continue
431def baselineGrpcVersion = '1.6.1'
432def publicApiSubprojects = [
433    // TODO: uncomment after grpc-alts artifact is published.
434    // ':grpc-alts',
435    ':grpc-auth',
436    ':grpc-context',
437    ':grpc-core',
438    ':grpc-grpclb',
439    ':grpc-netty',
440    ':grpc-okhttp',
441    ':grpc-protobuf',
442    ':grpc-protobuf-lite',
443    ':grpc-protobuf-nano',
444    ':grpc-stub',
445    ':grpc-testing',
446]
447
448publicApiSubprojects.each { name ->
449    project(":$name") {
450        apply plugin: 'me.champeau.gradle.japicmp'
451
452        // Get the baseline version's jar for this subproject
453        File baselineArtifact = null
454        // Use a detached configuration, otherwise the current version's jar will take precedence
455        // over the baseline jar.
456        // A necessary hack, the intuitive thing does NOT work:
457        // https://discuss.gradle.org/t/is-the-default-configuration-leaking-into-independent-configurations/2088/6
458        def oldGroup = project.group
459        try {
460            project.group = 'virtual_group_for_japicmp'
461            String depModule = "io.grpc:${project.name}:${baselineGrpcVersion}@jar"
462            String depJar = "${project.name}-${baselineGrpcVersion}.jar"
463            Configuration configuration = configurations.detachedConfiguration(
464                    dependencies.create(depModule)
465                    )
466            baselineArtifact = files(configuration.files).filter {
467                it.name.equals(depJar)
468            }.singleFile
469        } finally {
470            project.group = oldGroup
471        }
472
473        // Add a japicmp task that compares the current .jar with baseline .jar
474        task japicmp(type: me.champeau.gradle.japicmp.JapicmpTask, dependsOn: jar) {
475            oldClasspath = files(baselineArtifact)
476            newClasspath = files(jar.archivePath)
477            onlyBinaryIncompatibleModified = false
478            // Be quiet about things that did not change
479            onlyModified = true
480            // This task should fail if there are incompatible changes
481            failOnModification = true
482            ignoreMissingClasses = true
483            htmlOutputFile = file("$buildDir/reports/japi.html")
484
485            packageExcludes = ['io.grpc.internal']
486
487            // Also break on source incompatible changes, not just binary.
488            // Eg adding abstract method to public class.
489            // TODO(zpencer): enable after japicmp-gradle-plugin/pull/14
490            // breakOnSourceIncompatibility = true
491
492            // Ignore any classes or methods marked @ExperimentalApi
493            // TODO(zpencer): enable after japicmp-gradle-plugin/pull/15
494            // annotationExcludes = ['@io.grpc.ExperimentalApi']
495        }
496    }
497}
498
499// format checkers
500apply plugin: "com.diffplug.gradle.spotless"
501apply plugin: 'groovy'
502spotless {
503    groovyGradle {
504        target '**/*.gradle'
505        greclipse()
506        indentWithSpaces()
507        paddedCell()
508    }
509}
510