1*e07d83d3SAndroid Build Coastguard Workerplugins { 2*e07d83d3SAndroid Build Coastguard Worker id 'java' // changes the behavior of TARGET_JVM_VERSION_ATTRIBUTE 3*e07d83d3SAndroid Build Coastguard Worker 4*e07d83d3SAndroid Build Coastguard Worker id "com.google.osdetector" apply false 5*e07d83d3SAndroid Build Coastguard Worker id "me.champeau.gradle.japicmp" apply false 6*e07d83d3SAndroid Build Coastguard Worker id "net.ltgt.errorprone" apply false 7*e07d83d3SAndroid Build Coastguard Worker id 'com.google.cloud.tools.jib' apply false 8*e07d83d3SAndroid Build Coastguard Worker} 9*e07d83d3SAndroid Build Coastguard Worker 10*e07d83d3SAndroid Build Coastguard Workerimport net.ltgt.gradle.errorprone.CheckSeverity 11*e07d83d3SAndroid Build Coastguard Workerimport org.gradle.util.GUtil 12*e07d83d3SAndroid Build Coastguard Worker 13*e07d83d3SAndroid Build Coastguard Workersubprojects { 14*e07d83d3SAndroid Build Coastguard Worker apply plugin: "checkstyle" 15*e07d83d3SAndroid Build Coastguard Worker apply plugin: "idea" 16*e07d83d3SAndroid Build Coastguard Worker apply plugin: "signing" 17*e07d83d3SAndroid Build Coastguard Worker apply plugin: "jacoco" 18*e07d83d3SAndroid Build Coastguard Worker 19*e07d83d3SAndroid Build Coastguard Worker apply plugin: "com.google.osdetector" 20*e07d83d3SAndroid Build Coastguard Worker apply plugin: "net.ltgt.errorprone" 21*e07d83d3SAndroid Build Coastguard Worker 22*e07d83d3SAndroid Build Coastguard Worker group = "io.grpc" 23*e07d83d3SAndroid Build Coastguard Worker version = "1.56.1-SNAPSHOT" // CURRENT_GRPC_VERSION 24*e07d83d3SAndroid Build Coastguard Worker 25*e07d83d3SAndroid Build Coastguard Worker repositories { 26*e07d83d3SAndroid Build Coastguard Worker maven { // The google mirror is less flaky than mavenCentral() 27*e07d83d3SAndroid Build Coastguard Worker url "https://maven-central.storage-download.googleapis.com/maven2/" } 28*e07d83d3SAndroid Build Coastguard Worker mavenCentral() 29*e07d83d3SAndroid Build Coastguard Worker mavenLocal() 30*e07d83d3SAndroid Build Coastguard Worker } 31*e07d83d3SAndroid Build Coastguard Worker 32*e07d83d3SAndroid Build Coastguard Worker tasks.withType(JavaCompile).configureEach { 33*e07d83d3SAndroid Build Coastguard Worker it.options.compilerArgs += [ 34*e07d83d3SAndroid Build Coastguard Worker "-Xlint:all", 35*e07d83d3SAndroid Build Coastguard Worker "-Xlint:-options", 36*e07d83d3SAndroid Build Coastguard Worker "-Xlint:-path", 37*e07d83d3SAndroid Build Coastguard Worker "-Xlint:-try" 38*e07d83d3SAndroid Build Coastguard Worker ] 39*e07d83d3SAndroid Build Coastguard Worker it.options.encoding = "UTF-8" 40*e07d83d3SAndroid Build Coastguard Worker if (rootProject.hasProperty('failOnWarnings') && rootProject.failOnWarnings.toBoolean()) { 41*e07d83d3SAndroid Build Coastguard Worker it.options.compilerArgs += ["-Werror"] 42*e07d83d3SAndroid Build Coastguard Worker } 43*e07d83d3SAndroid Build Coastguard Worker } 44*e07d83d3SAndroid Build Coastguard Worker 45*e07d83d3SAndroid Build Coastguard Worker tasks.withType(GenerateModuleMetadata).configureEach { 46*e07d83d3SAndroid Build Coastguard Worker // Module metadata, introduced in Gradle 6.0, conflicts with our publishing task for 47*e07d83d3SAndroid Build Coastguard Worker // grpc-alts and grpc-compiler. 48*e07d83d3SAndroid Build Coastguard Worker enabled = false 49*e07d83d3SAndroid Build Coastguard Worker } 50*e07d83d3SAndroid Build Coastguard Worker 51*e07d83d3SAndroid Build Coastguard Worker def isAndroid = project.name in [ 52*e07d83d3SAndroid Build Coastguard Worker 'grpc-android', 'grpc-android-interop-testing', 'grpc-cronet'] 53*e07d83d3SAndroid Build Coastguard Worker 54*e07d83d3SAndroid Build Coastguard Worker ext { 55*e07d83d3SAndroid Build Coastguard Worker def exeSuffix = osdetector.os == 'windows' ? ".exe" : "" 56*e07d83d3SAndroid Build Coastguard Worker protocPluginBaseName = 'protoc-gen-grpc-java' 57*e07d83d3SAndroid Build Coastguard Worker javaPluginPath = "$rootDir/compiler/build/exe/java_plugin/$protocPluginBaseName$exeSuffix" 58*e07d83d3SAndroid Build Coastguard Worker 59*e07d83d3SAndroid Build Coastguard Worker configureProtoCompilation = { 60*e07d83d3SAndroid Build Coastguard Worker String generatedSourcePath = "${projectDir}/src/generated" 61*e07d83d3SAndroid Build Coastguard Worker project.protobuf { 62*e07d83d3SAndroid Build Coastguard Worker protoc { 63*e07d83d3SAndroid Build Coastguard Worker if (project.hasProperty('protoc')) { 64*e07d83d3SAndroid Build Coastguard Worker path = project.protoc 65*e07d83d3SAndroid Build Coastguard Worker } else { 66*e07d83d3SAndroid Build Coastguard Worker artifact = libs.protobuf.protoc.get() 67*e07d83d3SAndroid Build Coastguard Worker } 68*e07d83d3SAndroid Build Coastguard Worker } 69*e07d83d3SAndroid Build Coastguard Worker generateProtoTasks { 70*e07d83d3SAndroid Build Coastguard Worker all().each { task -> 71*e07d83d3SAndroid Build Coastguard Worker // Recompile protos when build.gradle has been changed, because 72*e07d83d3SAndroid Build Coastguard Worker // it's possible the version of protoc has been changed. 73*e07d83d3SAndroid Build Coastguard Worker task.inputs.file("${rootProject.projectDir}/build.gradle") 74*e07d83d3SAndroid Build Coastguard Worker .withPathSensitivity(PathSensitivity.RELATIVE) 75*e07d83d3SAndroid Build Coastguard Worker .withPropertyName('root build.gradle') 76*e07d83d3SAndroid Build Coastguard Worker if (isAndroid) { 77*e07d83d3SAndroid Build Coastguard Worker task.builtins { 78*e07d83d3SAndroid Build Coastguard Worker java { option 'lite' } 79*e07d83d3SAndroid Build Coastguard Worker } 80*e07d83d3SAndroid Build Coastguard Worker } 81*e07d83d3SAndroid Build Coastguard Worker } 82*e07d83d3SAndroid Build Coastguard Worker } 83*e07d83d3SAndroid Build Coastguard Worker } 84*e07d83d3SAndroid Build Coastguard Worker if (rootProject.childProjects.containsKey('grpc-compiler')) { 85*e07d83d3SAndroid Build Coastguard Worker // Only when the codegen is built along with the project, will we be able to run 86*e07d83d3SAndroid Build Coastguard Worker // the grpc code generator. 87*e07d83d3SAndroid Build Coastguard Worker def syncGeneratedSources = tasks.register("syncGeneratedSources") { } 88*e07d83d3SAndroid Build Coastguard Worker project.protobuf { 89*e07d83d3SAndroid Build Coastguard Worker plugins { grpc { path = javaPluginPath } } 90*e07d83d3SAndroid Build Coastguard Worker generateProtoTasks { 91*e07d83d3SAndroid Build Coastguard Worker all().each { task -> 92*e07d83d3SAndroid Build Coastguard Worker String variantOrSourceSet = isAndroid ? task.variant.name : task.sourceSet.name 93*e07d83d3SAndroid Build Coastguard Worker def syncTask = project.tasks.register("syncGeneratedSources${variantOrSourceSet}", Sync) { 94*e07d83d3SAndroid Build Coastguard Worker from task 95*e07d83d3SAndroid Build Coastguard Worker into "$generatedSourcePath/$variantOrSourceSet" 96*e07d83d3SAndroid Build Coastguard Worker include "grpc/" 97*e07d83d3SAndroid Build Coastguard Worker } 98*e07d83d3SAndroid Build Coastguard Worker syncGeneratedSources.configure { 99*e07d83d3SAndroid Build Coastguard Worker dependsOn syncTask 100*e07d83d3SAndroid Build Coastguard Worker } 101*e07d83d3SAndroid Build Coastguard Worker 102*e07d83d3SAndroid Build Coastguard Worker task.configure { 103*e07d83d3SAndroid Build Coastguard Worker dependsOn ':grpc-compiler:java_pluginExecutable' 104*e07d83d3SAndroid Build Coastguard Worker // Recompile protos when the codegen has been changed 105*e07d83d3SAndroid Build Coastguard Worker inputs.file javaPluginPath 106*e07d83d3SAndroid Build Coastguard Worker plugins { grpc { option 'noversion' } } 107*e07d83d3SAndroid Build Coastguard Worker if (isAndroid) { 108*e07d83d3SAndroid Build Coastguard Worker plugins { 109*e07d83d3SAndroid Build Coastguard Worker grpc { 110*e07d83d3SAndroid Build Coastguard Worker option 'lite' 111*e07d83d3SAndroid Build Coastguard Worker } 112*e07d83d3SAndroid Build Coastguard Worker } 113*e07d83d3SAndroid Build Coastguard Worker } 114*e07d83d3SAndroid Build Coastguard Worker } 115*e07d83d3SAndroid Build Coastguard Worker } 116*e07d83d3SAndroid Build Coastguard Worker } 117*e07d83d3SAndroid Build Coastguard Worker } 118*e07d83d3SAndroid Build Coastguard Worker // Re-sync as part of a normal build, to avoid forgetting to run the sync 119*e07d83d3SAndroid Build Coastguard Worker tasks.named("assemble").configure { 120*e07d83d3SAndroid Build Coastguard Worker dependsOn syncGeneratedSources 121*e07d83d3SAndroid Build Coastguard Worker } 122*e07d83d3SAndroid Build Coastguard Worker } else { 123*e07d83d3SAndroid Build Coastguard Worker // Otherwise, we just use the checked-in generated code. 124*e07d83d3SAndroid Build Coastguard Worker if (isAndroid) { 125*e07d83d3SAndroid Build Coastguard Worker project.android.sourceSets { 126*e07d83d3SAndroid Build Coastguard Worker debug { java { srcDir "${generatedSourcePath}/debug/grpc" } } 127*e07d83d3SAndroid Build Coastguard Worker release { java { srcDir "${generatedSourcePath}/release/grpc" } } 128*e07d83d3SAndroid Build Coastguard Worker } 129*e07d83d3SAndroid Build Coastguard Worker } else { 130*e07d83d3SAndroid Build Coastguard Worker project.sourceSets.each() { sourceSet -> 131*e07d83d3SAndroid Build Coastguard Worker sourceSet.java { srcDir "${generatedSourcePath}/${sourceSet.name}/grpc" } 132*e07d83d3SAndroid Build Coastguard Worker } 133*e07d83d3SAndroid Build Coastguard Worker } 134*e07d83d3SAndroid Build Coastguard Worker } 135*e07d83d3SAndroid Build Coastguard Worker 136*e07d83d3SAndroid Build Coastguard Worker tasks.withType(JavaCompile).configureEach { 137*e07d83d3SAndroid Build Coastguard Worker appendToProperty( 138*e07d83d3SAndroid Build Coastguard Worker it.options.errorprone.excludedPaths, 139*e07d83d3SAndroid Build Coastguard Worker ".*/src/generated/[^/]+/java/.*" + 140*e07d83d3SAndroid Build Coastguard Worker "|.*/build/generated/source/proto/[^/]+/java/.*", 141*e07d83d3SAndroid Build Coastguard Worker "|") 142*e07d83d3SAndroid Build Coastguard Worker } 143*e07d83d3SAndroid Build Coastguard Worker } 144*e07d83d3SAndroid Build Coastguard Worker 145*e07d83d3SAndroid Build Coastguard Worker libraries = libs 146*e07d83d3SAndroid Build Coastguard Worker 147*e07d83d3SAndroid Build Coastguard Worker appendToProperty = { Property<String> property, String value, String separator -> 148*e07d83d3SAndroid Build Coastguard Worker if (property.present) { 149*e07d83d3SAndroid Build Coastguard Worker property.set(property.get() + separator + value) 150*e07d83d3SAndroid Build Coastguard Worker } else { 151*e07d83d3SAndroid Build Coastguard Worker property.set(value) 152*e07d83d3SAndroid Build Coastguard Worker } 153*e07d83d3SAndroid Build Coastguard Worker } 154*e07d83d3SAndroid Build Coastguard Worker } 155*e07d83d3SAndroid Build Coastguard Worker 156*e07d83d3SAndroid Build Coastguard Worker // Disable JavaDoc doclint on Java 8. It's annoying. 157*e07d83d3SAndroid Build Coastguard Worker if (JavaVersion.current().isJava8Compatible()) { 158*e07d83d3SAndroid Build Coastguard Worker allprojects { 159*e07d83d3SAndroid Build Coastguard Worker tasks.withType(Javadoc).configureEach { 160*e07d83d3SAndroid Build Coastguard Worker options.addStringOption('Xdoclint:none', '-quiet') 161*e07d83d3SAndroid Build Coastguard Worker } 162*e07d83d3SAndroid Build Coastguard Worker } 163*e07d83d3SAndroid Build Coastguard Worker } 164*e07d83d3SAndroid Build Coastguard Worker 165*e07d83d3SAndroid Build Coastguard Worker checkstyle { 166*e07d83d3SAndroid Build Coastguard Worker configDirectory = file("$rootDir/buildscripts") 167*e07d83d3SAndroid Build Coastguard Worker toolVersion = libs.checkstyle.get().version 168*e07d83d3SAndroid Build Coastguard Worker ignoreFailures = false 169*e07d83d3SAndroid Build Coastguard Worker if (rootProject.hasProperty("checkstyle.ignoreFailures")) { 170*e07d83d3SAndroid Build Coastguard Worker ignoreFailures = rootProject.properties["checkstyle.ignoreFailures"].toBoolean() 171*e07d83d3SAndroid Build Coastguard Worker } 172*e07d83d3SAndroid Build Coastguard Worker } 173*e07d83d3SAndroid Build Coastguard Worker 174*e07d83d3SAndroid Build Coastguard Worker if (!project.hasProperty('errorProne') || errorProne.toBoolean()) { 175*e07d83d3SAndroid Build Coastguard Worker dependencies { 176*e07d83d3SAndroid Build Coastguard Worker errorprone JavaVersion.current().isJava11Compatible() ? libs.errorprone.core : libs.errorprone.corejava8 177*e07d83d3SAndroid Build Coastguard Worker } 178*e07d83d3SAndroid Build Coastguard Worker } else { 179*e07d83d3SAndroid Build Coastguard Worker // Disable Error Prone 180*e07d83d3SAndroid Build Coastguard Worker tasks.withType(JavaCompile).configureEach { 181*e07d83d3SAndroid Build Coastguard Worker options.errorprone.enabled = false 182*e07d83d3SAndroid Build Coastguard Worker } 183*e07d83d3SAndroid Build Coastguard Worker } 184*e07d83d3SAndroid Build Coastguard Worker 185*e07d83d3SAndroid Build Coastguard Worker plugins.withId("java") { 186*e07d83d3SAndroid Build Coastguard Worker sourceCompatibility = 1.8 187*e07d83d3SAndroid Build Coastguard Worker targetCompatibility = 1.8 188*e07d83d3SAndroid Build Coastguard Worker 189*e07d83d3SAndroid Build Coastguard Worker dependencies { 190*e07d83d3SAndroid Build Coastguard Worker testImplementation libraries.junit, 191*e07d83d3SAndroid Build Coastguard Worker libraries.mockito.core, 192*e07d83d3SAndroid Build Coastguard Worker libraries.truth 193*e07d83d3SAndroid Build Coastguard Worker } 194*e07d83d3SAndroid Build Coastguard Worker 195*e07d83d3SAndroid Build Coastguard Worker tasks.named("compileTestJava").configure { 196*e07d83d3SAndroid Build Coastguard Worker // serialVersionUID is basically guaranteed to be useless in our tests 197*e07d83d3SAndroid Build Coastguard Worker options.compilerArgs += [ 198*e07d83d3SAndroid Build Coastguard Worker "-Xlint:-serial" 199*e07d83d3SAndroid Build Coastguard Worker ] 200*e07d83d3SAndroid Build Coastguard Worker } 201*e07d83d3SAndroid Build Coastguard Worker 202*e07d83d3SAndroid Build Coastguard Worker tasks.named("jar").configure { 203*e07d83d3SAndroid Build Coastguard Worker manifest { 204*e07d83d3SAndroid Build Coastguard Worker attributes('Implementation-Title': name, 205*e07d83d3SAndroid Build Coastguard Worker 'Implementation-Version': project.version) 206*e07d83d3SAndroid Build Coastguard Worker } 207*e07d83d3SAndroid Build Coastguard Worker } 208*e07d83d3SAndroid Build Coastguard Worker 209*e07d83d3SAndroid Build Coastguard Worker tasks.named("javadoc").configure { 210*e07d83d3SAndroid Build Coastguard Worker options { 211*e07d83d3SAndroid Build Coastguard Worker encoding = 'UTF-8' 212*e07d83d3SAndroid Build Coastguard Worker use = true 213*e07d83d3SAndroid Build Coastguard Worker links 'https://docs.oracle.com/javase/8/docs/api/' 214*e07d83d3SAndroid Build Coastguard Worker source = "8" 215*e07d83d3SAndroid Build Coastguard Worker } 216*e07d83d3SAndroid Build Coastguard Worker } 217*e07d83d3SAndroid Build Coastguard Worker 218*e07d83d3SAndroid Build Coastguard Worker tasks.named("checkstyleMain").configure { 219*e07d83d3SAndroid Build Coastguard Worker source = fileTree(dir: "$projectDir/src/main", include: "**/*.java") 220*e07d83d3SAndroid Build Coastguard Worker } 221*e07d83d3SAndroid Build Coastguard Worker 222*e07d83d3SAndroid Build Coastguard Worker tasks.named("checkstyleTest").configure { 223*e07d83d3SAndroid Build Coastguard Worker source = fileTree(dir: "$projectDir/src/test", include: "**/*.java") 224*e07d83d3SAndroid Build Coastguard Worker } 225*e07d83d3SAndroid Build Coastguard Worker 226*e07d83d3SAndroid Build Coastguard Worker // At a test failure, log the stack trace to the console so that we don't 227*e07d83d3SAndroid Build Coastguard Worker // have to open the HTML in a browser. 228*e07d83d3SAndroid Build Coastguard Worker tasks.named("test").configure { 229*e07d83d3SAndroid Build Coastguard Worker testLogging { 230*e07d83d3SAndroid Build Coastguard Worker exceptionFormat = 'full' 231*e07d83d3SAndroid Build Coastguard Worker showExceptions true 232*e07d83d3SAndroid Build Coastguard Worker showCauses true 233*e07d83d3SAndroid Build Coastguard Worker showStackTraces true 234*e07d83d3SAndroid Build Coastguard Worker } 235*e07d83d3SAndroid Build Coastguard Worker maxHeapSize = '1500m' 236*e07d83d3SAndroid Build Coastguard Worker } 237*e07d83d3SAndroid Build Coastguard Worker 238*e07d83d3SAndroid Build Coastguard Worker if (!project.hasProperty('errorProne') || errorProne.toBoolean()) { 239*e07d83d3SAndroid Build Coastguard Worker dependencies { 240*e07d83d3SAndroid Build Coastguard Worker annotationProcessor libs.guava.betaChecker 241*e07d83d3SAndroid Build Coastguard Worker } 242*e07d83d3SAndroid Build Coastguard Worker } 243*e07d83d3SAndroid Build Coastguard Worker 244*e07d83d3SAndroid Build Coastguard Worker tasks.named("compileJava").configure { 245*e07d83d3SAndroid Build Coastguard Worker // This project targets Java 7 (no time.Duration class) 246*e07d83d3SAndroid Build Coastguard Worker options.errorprone.check("PreferJavaTimeOverload", CheckSeverity.OFF) 247*e07d83d3SAndroid Build Coastguard Worker options.errorprone.check("JavaUtilDate", CheckSeverity.OFF) 248*e07d83d3SAndroid Build Coastguard Worker // The warning fails to provide a source location 249*e07d83d3SAndroid Build Coastguard Worker options.errorprone.check("MissingSummary", CheckSeverity.OFF) 250*e07d83d3SAndroid Build Coastguard Worker } 251*e07d83d3SAndroid Build Coastguard Worker tasks.named("compileTestJava").configure { 252*e07d83d3SAndroid Build Coastguard Worker // LinkedList doesn't hurt much in tests and has lots of usages 253*e07d83d3SAndroid Build Coastguard Worker options.errorprone.check("JdkObsolete", CheckSeverity.OFF) 254*e07d83d3SAndroid Build Coastguard Worker options.errorprone.check("PreferJavaTimeOverload", CheckSeverity.OFF) 255*e07d83d3SAndroid Build Coastguard Worker options.errorprone.check("JavaUtilDate", CheckSeverity.OFF) 256*e07d83d3SAndroid Build Coastguard Worker } 257*e07d83d3SAndroid Build Coastguard Worker 258*e07d83d3SAndroid Build Coastguard Worker plugins.withId("ru.vyarus.animalsniffer") { 259*e07d83d3SAndroid Build Coastguard Worker // Only available after java plugin has loaded 260*e07d83d3SAndroid Build Coastguard Worker animalsniffer { 261*e07d83d3SAndroid Build Coastguard Worker toolVersion = libs.animalsniffer.asProvider().get().version 262*e07d83d3SAndroid Build Coastguard Worker } 263*e07d83d3SAndroid Build Coastguard Worker } 264*e07d83d3SAndroid Build Coastguard Worker } 265*e07d83d3SAndroid Build Coastguard Worker 266*e07d83d3SAndroid Build Coastguard Worker plugins.withId("java-library") { 267*e07d83d3SAndroid Build Coastguard Worker // Detect Maven Enforcer's dependencyConvergence failures. We only care 268*e07d83d3SAndroid Build Coastguard Worker // for artifacts used as libraries by others with Maven. 269*e07d83d3SAndroid Build Coastguard Worker tasks.register('checkUpperBoundDeps') { 270*e07d83d3SAndroid Build Coastguard Worker inputs.files(configurations.runtimeClasspath).withNormalizer(ClasspathNormalizer) 271*e07d83d3SAndroid Build Coastguard Worker outputs.file("${buildDir}/tmp/${name}") // Fake output for UP-TO-DATE checking 272*e07d83d3SAndroid Build Coastguard Worker doLast { 273*e07d83d3SAndroid Build Coastguard Worker requireUpperBoundDepsMatch(configurations.runtimeClasspath, project) 274*e07d83d3SAndroid Build Coastguard Worker } 275*e07d83d3SAndroid Build Coastguard Worker } 276*e07d83d3SAndroid Build Coastguard Worker tasks.named('compileJava').configure { 277*e07d83d3SAndroid Build Coastguard Worker dependsOn checkUpperBoundDeps 278*e07d83d3SAndroid Build Coastguard Worker } 279*e07d83d3SAndroid Build Coastguard Worker } 280*e07d83d3SAndroid Build Coastguard Worker 281*e07d83d3SAndroid Build Coastguard Worker plugins.withId("me.champeau.jmh") { 282*e07d83d3SAndroid Build Coastguard Worker // invoke jmh on a single benchmark class like so: 283*e07d83d3SAndroid Build Coastguard Worker // ./gradlew -PjmhIncludeSingleClass=StatsTraceContextBenchmark clean :grpc-core:jmh 284*e07d83d3SAndroid Build Coastguard Worker tasks.named("compileJmhJava").configure { 285*e07d83d3SAndroid Build Coastguard Worker sourceCompatibility = 1.8 286*e07d83d3SAndroid Build Coastguard Worker targetCompatibility = 1.8 287*e07d83d3SAndroid Build Coastguard Worker } 288*e07d83d3SAndroid Build Coastguard Worker tasks.named("jmh").configure { 289*e07d83d3SAndroid Build Coastguard Worker warmupIterations = 10 290*e07d83d3SAndroid Build Coastguard Worker iterations = 10 291*e07d83d3SAndroid Build Coastguard Worker fork = 1 292*e07d83d3SAndroid Build Coastguard Worker // None of our benchmarks need the tests, and we have pseudo-circular 293*e07d83d3SAndroid Build Coastguard Worker // dependencies that break when including them. (context's testCompile 294*e07d83d3SAndroid Build Coastguard Worker // depends on core; core's testCompile depends on testing) 295*e07d83d3SAndroid Build Coastguard Worker includeTests = false 296*e07d83d3SAndroid Build Coastguard Worker if (project.hasProperty('jmhIncludeSingleClass')) { 297*e07d83d3SAndroid Build Coastguard Worker includes = [ 298*e07d83d3SAndroid Build Coastguard Worker project.property('jmhIncludeSingleClass') 299*e07d83d3SAndroid Build Coastguard Worker ] 300*e07d83d3SAndroid Build Coastguard Worker } 301*e07d83d3SAndroid Build Coastguard Worker } 302*e07d83d3SAndroid Build Coastguard Worker } 303*e07d83d3SAndroid Build Coastguard Worker 304*e07d83d3SAndroid Build Coastguard Worker plugins.withId("com.github.johnrengelman.shadow") { 305*e07d83d3SAndroid Build Coastguard Worker tasks.named("shadowJar").configure { 306*e07d83d3SAndroid Build Coastguard Worker // Do a dance to remove Class-Path. This needs to run after the doFirst() from the 307*e07d83d3SAndroid Build Coastguard Worker // shadow plugin that adds Class-Path and before the core jar action. Using doFirst will 308*e07d83d3SAndroid Build Coastguard Worker // have this run before the shadow plugin, and doLast will run after the core jar 309*e07d83d3SAndroid Build Coastguard Worker // action. See #8606. 310*e07d83d3SAndroid Build Coastguard Worker // The shadow plugin adds another doFirst when application is used for setting 311*e07d83d3SAndroid Build Coastguard Worker // Main-Class. Ordering with it doesn't matter. 312*e07d83d3SAndroid Build Coastguard Worker actions.add(plugins.hasPlugin("application") ? 2 : 1, new Action<Task>() { 313*e07d83d3SAndroid Build Coastguard Worker @Override public void execute(Task task) { 314*e07d83d3SAndroid Build Coastguard Worker if (!task.manifest.attributes.remove("Class-Path")) { 315*e07d83d3SAndroid Build Coastguard Worker throw new AssertionError("Did not find Class-Path to remove from manifest") 316*e07d83d3SAndroid Build Coastguard Worker } 317*e07d83d3SAndroid Build Coastguard Worker } 318*e07d83d3SAndroid Build Coastguard Worker }) 319*e07d83d3SAndroid Build Coastguard Worker } 320*e07d83d3SAndroid Build Coastguard Worker } 321*e07d83d3SAndroid Build Coastguard Worker 322*e07d83d3SAndroid Build Coastguard Worker plugins.withId("maven-publish") { 323*e07d83d3SAndroid Build Coastguard Worker publishing { 324*e07d83d3SAndroid Build Coastguard Worker publications { 325*e07d83d3SAndroid Build Coastguard Worker // do not use mavenJava, as java plugin will modify it via "magic" 326*e07d83d3SAndroid Build Coastguard Worker maven(MavenPublication) { 327*e07d83d3SAndroid Build Coastguard Worker pom { 328*e07d83d3SAndroid Build Coastguard Worker name = project.group + ":" + project.name 329*e07d83d3SAndroid Build Coastguard Worker url = 'https://github.com/grpc/grpc-java' 330*e07d83d3SAndroid Build Coastguard Worker afterEvaluate { 331*e07d83d3SAndroid Build Coastguard Worker // description is not available until evaluated. 332*e07d83d3SAndroid Build Coastguard Worker description = project.description 333*e07d83d3SAndroid Build Coastguard Worker } 334*e07d83d3SAndroid Build Coastguard Worker 335*e07d83d3SAndroid Build Coastguard Worker scm { 336*e07d83d3SAndroid Build Coastguard Worker connection = 'scm:git:https://github.com/grpc/grpc-java.git' 337*e07d83d3SAndroid Build Coastguard Worker developerConnection = 'scm:git:[email protected]:grpc/grpc-java.git' 338*e07d83d3SAndroid Build Coastguard Worker url = 'https://github.com/grpc/grpc-java' 339*e07d83d3SAndroid Build Coastguard Worker } 340*e07d83d3SAndroid Build Coastguard Worker 341*e07d83d3SAndroid Build Coastguard Worker licenses { 342*e07d83d3SAndroid Build Coastguard Worker license { 343*e07d83d3SAndroid Build Coastguard Worker name = 'Apache 2.0' 344*e07d83d3SAndroid Build Coastguard Worker url = 'https://opensource.org/licenses/Apache-2.0' 345*e07d83d3SAndroid Build Coastguard Worker } 346*e07d83d3SAndroid Build Coastguard Worker } 347*e07d83d3SAndroid Build Coastguard Worker 348*e07d83d3SAndroid Build Coastguard Worker developers { 349*e07d83d3SAndroid Build Coastguard Worker developer { 350*e07d83d3SAndroid Build Coastguard Worker id = "grpc.io" 351*e07d83d3SAndroid Build Coastguard Worker name = "gRPC Contributors" 352*e07d83d3SAndroid Build Coastguard Worker email = "[email protected]" 353*e07d83d3SAndroid Build Coastguard Worker url = "https://grpc.io/" 354*e07d83d3SAndroid Build Coastguard Worker organization = "gRPC Authors" 355*e07d83d3SAndroid Build Coastguard Worker organizationUrl = "https://www.google.com" 356*e07d83d3SAndroid Build Coastguard Worker } 357*e07d83d3SAndroid Build Coastguard Worker } 358*e07d83d3SAndroid Build Coastguard Worker } 359*e07d83d3SAndroid Build Coastguard Worker } 360*e07d83d3SAndroid Build Coastguard Worker } 361*e07d83d3SAndroid Build Coastguard Worker repositories { 362*e07d83d3SAndroid Build Coastguard Worker maven { 363*e07d83d3SAndroid Build Coastguard Worker if (rootProject.hasProperty('repositoryDir')) { 364*e07d83d3SAndroid Build Coastguard Worker url = new File(rootProject.repositoryDir).toURI() 365*e07d83d3SAndroid Build Coastguard Worker } else { 366*e07d83d3SAndroid Build Coastguard Worker String stagingUrl 367*e07d83d3SAndroid Build Coastguard Worker if (rootProject.hasProperty('repositoryId')) { 368*e07d83d3SAndroid Build Coastguard Worker stagingUrl = 'https://oss.sonatype.org/service/local/staging/deployByRepositoryId/' + 369*e07d83d3SAndroid Build Coastguard Worker rootProject.repositoryId 370*e07d83d3SAndroid Build Coastguard Worker } else { 371*e07d83d3SAndroid Build Coastguard Worker stagingUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/' 372*e07d83d3SAndroid Build Coastguard Worker } 373*e07d83d3SAndroid Build Coastguard Worker credentials { 374*e07d83d3SAndroid Build Coastguard Worker if (rootProject.hasProperty('ossrhUsername') && rootProject.hasProperty('ossrhPassword')) { 375*e07d83d3SAndroid Build Coastguard Worker username = rootProject.ossrhUsername 376*e07d83d3SAndroid Build Coastguard Worker password = rootProject.ossrhPassword 377*e07d83d3SAndroid Build Coastguard Worker } 378*e07d83d3SAndroid Build Coastguard Worker } 379*e07d83d3SAndroid Build Coastguard Worker def releaseUrl = stagingUrl 380*e07d83d3SAndroid Build Coastguard Worker def snapshotUrl = 'https://oss.sonatype.org/content/repositories/snapshots/' 381*e07d83d3SAndroid Build Coastguard Worker url = version.endsWith('SNAPSHOT') ? snapshotUrl : releaseUrl 382*e07d83d3SAndroid Build Coastguard Worker } 383*e07d83d3SAndroid Build Coastguard Worker } 384*e07d83d3SAndroid Build Coastguard Worker } 385*e07d83d3SAndroid Build Coastguard Worker } 386*e07d83d3SAndroid Build Coastguard Worker 387*e07d83d3SAndroid Build Coastguard Worker signing { 388*e07d83d3SAndroid Build Coastguard Worker required false 389*e07d83d3SAndroid Build Coastguard Worker sign publishing.publications.maven 390*e07d83d3SAndroid Build Coastguard Worker } 391*e07d83d3SAndroid Build Coastguard Worker 392*e07d83d3SAndroid Build Coastguard Worker plugins.withId("java") { 393*e07d83d3SAndroid Build Coastguard Worker java { 394*e07d83d3SAndroid Build Coastguard Worker withJavadocJar() 395*e07d83d3SAndroid Build Coastguard Worker withSourcesJar() 396*e07d83d3SAndroid Build Coastguard Worker } 397*e07d83d3SAndroid Build Coastguard Worker 398*e07d83d3SAndroid Build Coastguard Worker publishing { 399*e07d83d3SAndroid Build Coastguard Worker publications { 400*e07d83d3SAndroid Build Coastguard Worker maven { 401*e07d83d3SAndroid Build Coastguard Worker if (project.name != 'grpc-netty-shaded') { 402*e07d83d3SAndroid Build Coastguard Worker from components.java 403*e07d83d3SAndroid Build Coastguard Worker } 404*e07d83d3SAndroid Build Coastguard Worker } 405*e07d83d3SAndroid Build Coastguard Worker } 406*e07d83d3SAndroid Build Coastguard Worker } 407*e07d83d3SAndroid Build Coastguard Worker } 408*e07d83d3SAndroid Build Coastguard Worker } 409*e07d83d3SAndroid Build Coastguard Worker 410*e07d83d3SAndroid Build Coastguard Worker // Run with: ./gradlew japicmp --continue 411*e07d83d3SAndroid Build Coastguard Worker plugins.withId("me.champeau.gradle.japicmp") { 412*e07d83d3SAndroid Build Coastguard Worker def baselineGrpcVersion = '1.6.1' 413*e07d83d3SAndroid Build Coastguard Worker 414*e07d83d3SAndroid Build Coastguard Worker // Get the baseline version's jar for this subproject 415*e07d83d3SAndroid Build Coastguard Worker File baselineArtifact = null 416*e07d83d3SAndroid Build Coastguard Worker // Use a detached configuration, otherwise the current version's jar will take precedence 417*e07d83d3SAndroid Build Coastguard Worker // over the baseline jar. 418*e07d83d3SAndroid Build Coastguard Worker // A necessary hack, the intuitive thing does NOT work: 419*e07d83d3SAndroid Build Coastguard Worker // https://discuss.gradle.org/t/is-the-default-configuration-leaking-into-independent-configurations/2088/6 420*e07d83d3SAndroid Build Coastguard Worker def oldGroup = project.group 421*e07d83d3SAndroid Build Coastguard Worker try { 422*e07d83d3SAndroid Build Coastguard Worker project.group = 'virtual_group_for_japicmp' 423*e07d83d3SAndroid Build Coastguard Worker String depModule = "io.grpc:${project.name}:${baselineGrpcVersion}@jar" 424*e07d83d3SAndroid Build Coastguard Worker String depJar = "${project.name}-${baselineGrpcVersion}.jar" 425*e07d83d3SAndroid Build Coastguard Worker Configuration configuration = configurations.detachedConfiguration( 426*e07d83d3SAndroid Build Coastguard Worker dependencies.create(depModule) 427*e07d83d3SAndroid Build Coastguard Worker ) 428*e07d83d3SAndroid Build Coastguard Worker baselineArtifact = files(configuration.files).filter { 429*e07d83d3SAndroid Build Coastguard Worker it.name.equals(depJar) 430*e07d83d3SAndroid Build Coastguard Worker }.singleFile 431*e07d83d3SAndroid Build Coastguard Worker } finally { 432*e07d83d3SAndroid Build Coastguard Worker project.group = oldGroup 433*e07d83d3SAndroid Build Coastguard Worker } 434*e07d83d3SAndroid Build Coastguard Worker 435*e07d83d3SAndroid Build Coastguard Worker // Add a japicmp task that compares the current .jar with baseline .jar 436*e07d83d3SAndroid Build Coastguard Worker tasks.register("japicmp", me.champeau.gradle.japicmp.JapicmpTask) { 437*e07d83d3SAndroid Build Coastguard Worker dependsOn jar 438*e07d83d3SAndroid Build Coastguard Worker oldClasspath = files(baselineArtifact) 439*e07d83d3SAndroid Build Coastguard Worker newClasspath = files(jar.archiveFile) 440*e07d83d3SAndroid Build Coastguard Worker onlyBinaryIncompatibleModified = false 441*e07d83d3SAndroid Build Coastguard Worker // Be quiet about things that did not change 442*e07d83d3SAndroid Build Coastguard Worker onlyModified = true 443*e07d83d3SAndroid Build Coastguard Worker // This task should fail if there are incompatible changes 444*e07d83d3SAndroid Build Coastguard Worker failOnModification = true 445*e07d83d3SAndroid Build Coastguard Worker ignoreMissingClasses = true 446*e07d83d3SAndroid Build Coastguard Worker htmlOutputFile = file("$buildDir/reports/japi.html") 447*e07d83d3SAndroid Build Coastguard Worker 448*e07d83d3SAndroid Build Coastguard Worker packageExcludes = ['io.grpc.internal'] 449*e07d83d3SAndroid Build Coastguard Worker 450*e07d83d3SAndroid Build Coastguard Worker // Also break on source incompatible changes, not just binary. 451*e07d83d3SAndroid Build Coastguard Worker // Eg adding abstract method to public class. 452*e07d83d3SAndroid Build Coastguard Worker // TODO(zpencer): enable after japicmp-gradle-plugin/pull/14 453*e07d83d3SAndroid Build Coastguard Worker // breakOnSourceIncompatibility = true 454*e07d83d3SAndroid Build Coastguard Worker 455*e07d83d3SAndroid Build Coastguard Worker // Ignore any classes or methods marked @ExperimentalApi 456*e07d83d3SAndroid Build Coastguard Worker // TODO(zpencer): enable after japicmp-gradle-plugin/pull/15 457*e07d83d3SAndroid Build Coastguard Worker // annotationExcludes = ['@io.grpc.ExperimentalApi'] 458*e07d83d3SAndroid Build Coastguard Worker } 459*e07d83d3SAndroid Build Coastguard Worker } 460*e07d83d3SAndroid Build Coastguard Worker} 461*e07d83d3SAndroid Build Coastguard Worker 462*e07d83d3SAndroid Build Coastguard Workerclass DepAndParents { 463*e07d83d3SAndroid Build Coastguard Worker DependencyResult dep 464*e07d83d3SAndroid Build Coastguard Worker List<String> parents 465*e07d83d3SAndroid Build Coastguard Worker} 466*e07d83d3SAndroid Build Coastguard Worker 467*e07d83d3SAndroid Build Coastguard Worker/** 468*e07d83d3SAndroid Build Coastguard Worker * Make sure that Maven would select the same versions as Gradle selected. 469*e07d83d3SAndroid Build Coastguard Worker * This is essentially the same as if we used Maven Enforcer's 470*e07d83d3SAndroid Build Coastguard Worker * requireUpperBoundDeps for our artifacts. 471*e07d83d3SAndroid Build Coastguard Worker */ 472*e07d83d3SAndroid Build Coastguard Workerdef requireUpperBoundDepsMatch(Configuration conf, Project project) { 473*e07d83d3SAndroid Build Coastguard Worker // artifact name => version 474*e07d83d3SAndroid Build Coastguard Worker Map<String,String> golden = conf.resolvedConfiguration.resolvedArtifacts.collectEntries { 475*e07d83d3SAndroid Build Coastguard Worker ResolvedArtifact it -> 476*e07d83d3SAndroid Build Coastguard Worker ModuleVersionIdentifier id = it.moduleVersion.id 477*e07d83d3SAndroid Build Coastguard Worker [id.group + ":" + id.name, id.version] 478*e07d83d3SAndroid Build Coastguard Worker } 479*e07d83d3SAndroid Build Coastguard Worker // Breadth-first search like Maven for dependency resolution 480*e07d83d3SAndroid Build Coastguard Worker Queue<DepAndParents> queue = new ArrayDeque<>() 481*e07d83d3SAndroid Build Coastguard Worker conf.incoming.resolutionResult.root.dependencies.each { 482*e07d83d3SAndroid Build Coastguard Worker queue.add(new DepAndParents(dep: it, parents: [project.displayName])) 483*e07d83d3SAndroid Build Coastguard Worker } 484*e07d83d3SAndroid Build Coastguard Worker Set<String> found = new HashSet<>() 485*e07d83d3SAndroid Build Coastguard Worker while (!queue.isEmpty()) { 486*e07d83d3SAndroid Build Coastguard Worker DepAndParents depAndParents = queue.remove() 487*e07d83d3SAndroid Build Coastguard Worker ResolvedDependencyResult result = (ResolvedDependencyResult) depAndParents.dep 488*e07d83d3SAndroid Build Coastguard Worker ModuleVersionIdentifier id = result.selected.moduleVersion 489*e07d83d3SAndroid Build Coastguard Worker String artifact = id.group + ":" + id.name 490*e07d83d3SAndroid Build Coastguard Worker if (found.contains(artifact)) 491*e07d83d3SAndroid Build Coastguard Worker continue 492*e07d83d3SAndroid Build Coastguard Worker found.add(artifact) 493*e07d83d3SAndroid Build Coastguard Worker String version 494*e07d83d3SAndroid Build Coastguard Worker if (result.requested instanceof ProjectComponentSelector) { 495*e07d83d3SAndroid Build Coastguard Worker ProjectComponentSelector selector = (ProjectComponentSelector) result.requested 496*e07d83d3SAndroid Build Coastguard Worker version = project.findProject(selector.projectPath).version 497*e07d83d3SAndroid Build Coastguard Worker } else { 498*e07d83d3SAndroid Build Coastguard Worker version = ((ModuleComponentSelector) result.requested).version 499*e07d83d3SAndroid Build Coastguard Worker } 500*e07d83d3SAndroid Build Coastguard Worker String goldenVersion = golden[artifact] 501*e07d83d3SAndroid Build Coastguard Worker if (goldenVersion != version && "[$goldenVersion]" != version) { 502*e07d83d3SAndroid Build Coastguard Worker throw new RuntimeException( 503*e07d83d3SAndroid Build Coastguard Worker "Maven version skew: $artifact ($version != $goldenVersion) " 504*e07d83d3SAndroid Build Coastguard Worker + "Bad version dependency path: " + depAndParents.parents 505*e07d83d3SAndroid Build Coastguard Worker + " Run './gradlew $project.path:dependencies --configuration $conf.name' " 506*e07d83d3SAndroid Build Coastguard Worker + "to diagnose") 507*e07d83d3SAndroid Build Coastguard Worker } 508*e07d83d3SAndroid Build Coastguard Worker result.selected.dependencies.each { 509*e07d83d3SAndroid Build Coastguard Worker queue.add(new DepAndParents( 510*e07d83d3SAndroid Build Coastguard Worker dep: it, parents: depAndParents.parents + [artifact + ":" + version])) 511*e07d83d3SAndroid Build Coastguard Worker } 512*e07d83d3SAndroid Build Coastguard Worker } 513*e07d83d3SAndroid Build Coastguard Worker} 514*e07d83d3SAndroid Build Coastguard Worker 515*e07d83d3SAndroid Build Coastguard Workerrepositories { 516*e07d83d3SAndroid Build Coastguard Worker mavenCentral() 517*e07d83d3SAndroid Build Coastguard Worker google() 518*e07d83d3SAndroid Build Coastguard Worker} 519*e07d83d3SAndroid Build Coastguard Worker 520*e07d83d3SAndroid Build Coastguard Workerdef isAcceptableVersion(ModuleComponentIdentifier candidate) { 521*e07d83d3SAndroid Build Coastguard Worker String group = candidate.group 522*e07d83d3SAndroid Build Coastguard Worker String module = candidate.module 523*e07d83d3SAndroid Build Coastguard Worker String version = candidate.version 524*e07d83d3SAndroid Build Coastguard Worker if (group == 'com.google.guava') 525*e07d83d3SAndroid Build Coastguard Worker return true 526*e07d83d3SAndroid Build Coastguard Worker if (group == 'io.netty' && version.contains('Final')) 527*e07d83d3SAndroid Build Coastguard Worker return true 528*e07d83d3SAndroid Build Coastguard Worker if (module == 'android-api-level-19') 529*e07d83d3SAndroid Build Coastguard Worker return true 530*e07d83d3SAndroid Build Coastguard Worker return version ==~ /^[0-9]+(\.[0-9]+)+$/ 531*e07d83d3SAndroid Build Coastguard Worker} 532*e07d83d3SAndroid Build Coastguard Worker 533*e07d83d3SAndroid Build Coastguard Workerconfigurations { 534*e07d83d3SAndroid Build Coastguard Worker checkForUpdates { 535*e07d83d3SAndroid Build Coastguard Worker attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 8) 536*e07d83d3SAndroid Build Coastguard Worker resolutionStrategy { 537*e07d83d3SAndroid Build Coastguard Worker componentSelection { 538*e07d83d3SAndroid Build Coastguard Worker all { 539*e07d83d3SAndroid Build Coastguard Worker if (!isAcceptableVersion(it.candidate)) 540*e07d83d3SAndroid Build Coastguard Worker it.reject("Not stable version") 541*e07d83d3SAndroid Build Coastguard Worker } 542*e07d83d3SAndroid Build Coastguard Worker } 543*e07d83d3SAndroid Build Coastguard Worker } 544*e07d83d3SAndroid Build Coastguard Worker } 545*e07d83d3SAndroid Build Coastguard Worker} 546*e07d83d3SAndroid Build Coastguard Worker 547*e07d83d3SAndroid Build Coastguard Worker// Checks every dependency in the version catalog to see if there is a newer 548*e07d83d3SAndroid Build Coastguard Worker// version available. The 'checkForUpdates' configuration restricts the 549*e07d83d3SAndroid Build Coastguard Worker// versions considered. 550*e07d83d3SAndroid Build Coastguard Workertasks.register('checkForUpdates') { 551*e07d83d3SAndroid Build Coastguard Worker doLast { 552*e07d83d3SAndroid Build Coastguard Worker def updateConf = project.configurations.checkForUpdates 553*e07d83d3SAndroid Build Coastguard Worker updateConf.setVisible(false) 554*e07d83d3SAndroid Build Coastguard Worker updateConf.setTransitive(false) 555*e07d83d3SAndroid Build Coastguard Worker def versionCatalog = project.extensions.getByType(VersionCatalogsExtension).named("libs") 556*e07d83d3SAndroid Build Coastguard Worker versionCatalog.libraryAliases.each { name -> 557*e07d83d3SAndroid Build Coastguard Worker def dep = versionCatalog.findLibrary(name).get().get() 558*e07d83d3SAndroid Build Coastguard Worker 559*e07d83d3SAndroid Build Coastguard Worker def oldConf = updateConf.copy() 560*e07d83d3SAndroid Build Coastguard Worker def oldDep = project.dependencies.create( 561*e07d83d3SAndroid Build Coastguard Worker group: dep.group, name: dep.name, version: dep.versionConstraint, classifier: 'pom') 562*e07d83d3SAndroid Build Coastguard Worker oldConf.dependencies.add(oldDep) 563*e07d83d3SAndroid Build Coastguard Worker def oldResolved = oldConf.resolvedConfiguration.resolvedArtifacts.iterator().next() 564*e07d83d3SAndroid Build Coastguard Worker 565*e07d83d3SAndroid Build Coastguard Worker def newConf = updateConf.copy() 566*e07d83d3SAndroid Build Coastguard Worker def newDep = project.dependencies.create( 567*e07d83d3SAndroid Build Coastguard Worker group: dep.group, name: dep.name, version: '+', classifier: 'pom') 568*e07d83d3SAndroid Build Coastguard Worker newConf.dependencies.add(newDep) 569*e07d83d3SAndroid Build Coastguard Worker def newResolved = newConf.resolvedConfiguration.resolvedArtifacts.iterator().next() 570*e07d83d3SAndroid Build Coastguard Worker if (oldResolved != newResolved) { 571*e07d83d3SAndroid Build Coastguard Worker def oldId = oldResolved.id.componentIdentifier 572*e07d83d3SAndroid Build Coastguard Worker def newId = newResolved.id.componentIdentifier 573*e07d83d3SAndroid Build Coastguard Worker println("${newId.group}:${newId.module} ${oldId.version} -> ${newId.version}") 574*e07d83d3SAndroid Build Coastguard Worker } 575*e07d83d3SAndroid Build Coastguard Worker } 576*e07d83d3SAndroid Build Coastguard Worker } 577*e07d83d3SAndroid Build Coastguard Worker} 578