1/* 2 * Copyright (C) 2020 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16import java.util.zip.ZipEntry 17import java.util.zip.ZipFile 18 19plugins { 20 id 'org.jetbrains.kotlin.jvm' 21 id 'java-gradle-plugin' 22 id 'maven-publish' 23 id 'com.github.johnrengelman.shadow' 24} 25 26configurations { 27 // Config for dependencies that will be shadowed / jarjared 28 shadowed 29 // Make all shadowed dependencies be compileOnly dependencies to not affect 30 // main compilation / configuration 31 compileOnly.extendsFrom(shadowed) 32 // Make all shadowed dependencies be included in the plugin test classpath 33 // since they are compileOnly in the main configuration 34 testPluginCompile.extendsFrom(shadowed) 35 // Config for plugin classpath to be used during tests 36 testPluginCompile { 37 canBeConsumed = false 38 canBeResolved = true 39 } 40} 41 42// Renames default jar to avoid using it in publications. 43jar { 44 archiveClassifier = "before-jarjar" 45} 46shadowJar { 47 archiveClassifier = "" 48 configurations = [project.configurations.shadowed] 49 dependencies { 50 // Don't jarjar stdlib deps that are automatically added by Kotlin plugin 51 exclude(dependency("org.jetbrains.kotlin::")) 52 exclude(dependency("org.jetbrains:annotations:")) 53 } 54 doLast { 55 outputs.files.each { jarFile -> 56 checkJarFile(jarFile, 'dagger', 'META-INF') 57 } 58 } 59} 60 61dependencies { 62 // Include the shared library containing the APIs in 63 // dagger.hilt.processor.internal.root.ir. 64 shadowed fileTree(dir: 'libs', include: '*.jar') 65 // Use compile-only for other plugin dependencies to avoid brining those 66 // to projects that don't use them. 67 compileOnly "com.android.tools.build:gradle:$agp_version" 68 compileOnly "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 69 compileOnly "com.google.devtools.ksp:symbol-processing-gradle-plugin:$ksp_version" 70 71 implementation gradleApi() 72 implementation 'org.ow2.asm:asm:9.6' 73 implementation "com.squareup:javapoet:1.13.0" 74 75 testImplementation gradleTestKit() 76 testImplementation 'junit:junit:4.12' 77 testImplementation 'com.google.truth:truth:1.0.1' 78 testImplementation 'org.javassist:javassist:3.26.0-GA' 79 testPluginCompile "com.android.tools.build:gradle:$agp_version" 80 testPluginCompile "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 81 testPluginCompile "com.google.devtools.ksp:symbol-processing-gradle-plugin:$ksp_version" 82} 83 84// Configure the generating task of plugin-under-test-metadata.properties to 85// include additional dependencies for the injected plugin classpath that 86// are not present in the main runtime dependencies. This allows us to test 87// the desired AGP version while keeping a compileOnly dep on the main source. 88tasks.withType(PluginUnderTestMetadata.class).named("pluginUnderTestMetadata").configure { 89 it.pluginClasspath.from(configurations.testPluginCompile) 90} 91 92kotlin { 93 compilerOptions { 94 allWarningsAsErrors = true 95 freeCompilerArgs.add("-opt-in=kotlin.ExperimentalStdlibApi") 96 } 97 jvmToolchain(17) 98} 99 100// Imports a shared library from the main project. The library and its classes 101// will be shadowed in the plugin's artifact. 102tasks.register("importSharedLib").configure { 103 def outputDir = file("${project.projectDir}/libs") 104 outputs.dir(outputDir) 105 doLast { 106 def buildCmd = 'bazel' 107 def buildDir = 'bazel-bin' 108 def findGenFilesParent 109 findGenFilesParent = { File dir -> 110 if (dir == null || !dir.isDirectory()) { 111 return null 112 } 113 if (new File(dir, buildDir).exists()) { 114 return dir 115 } else { 116 return findGenFilesParent(dir.parentFile) 117 } 118 } 119 // Build shared lib 120 def bazelOutput = new ByteArrayOutputStream() 121 def buildResult = exec { 122 commandLine buildCmd, 'build', 'import-shared-lib' 123 standardOutput = bazelOutput 124 errorOutput = bazelOutput 125 } 126 buildResult.assertNormalExitValue() 127 // Find shared lib Jar in build directory. 128 def genFilesDir = findGenFilesParent(project.buildFile.parentFile) 129 if (genFilesDir == null) { 130 throw new GradleException("Couldn't find build folder '$buildDir'") 131 } 132 def libPath = bazelOutput.toString().split('\n') 133 .find { line -> line.contains("$buildDir/") }.trim() 134 def inputFile = file("$genFilesDir/$libPath") 135 def outputFile = file("$outputDir/${inputFile.name}") 136 outputFile << inputFile.newInputStream() 137 } 138} 139tasks.getByName('compileKotlin').dependsOn('importSharedLib') 140 141// Task that generates a top-level property containing the version of the 142// project so that it can be used in code and at runtime. 143def pluginVersionOutDir = file("$buildDir/generated/source/plugin-version/") 144tasks.register("generatePluginVersionSource").configure { 145 def version = getPublishVersion() 146 inputs.property('version', version) 147 outputs.dir(pluginVersionOutDir) 148 doLast { 149 def versionFile = 150 file("$pluginVersionOutDir/dagger/hilt/android/plugin/Version.kt") 151 versionFile.parentFile.mkdirs() 152 versionFile.text = """ 153 // Generated file. Do not edit! 154 package dagger.hilt.android.plugin 155 156 val HILT_VERSION = "${version}" 157 """.stripIndent() 158 } 159} 160sourceSets.main.java.srcDir pluginVersionOutDir 161tasks.getByName('compileKotlin').dependsOn('generatePluginVersionSource') 162 163// Create sources Jar from main kotlin sources 164tasks.register("sourcesJar", Jar).configure { 165 group = JavaBasePlugin.DOCUMENTATION_GROUP 166 description = "Assembles sources JAR" 167 archiveClassifier.set("sources") 168 from(sourceSets["main"].allSource) 169 dependsOn('generatePluginVersionSource') 170} 171 172// Create javadoc Jar. The jar is empty since we don't really have docs 173// for this plugin but this is required to upload to Sonatype. 174// https://central.sonatype.org/pages/requirements.html#supply-javadoc-and-sources 175tasks.register("javadocJar", Jar).configure { 176 group = JavaBasePlugin.DOCUMENTATION_GROUP 177 description = "Assembles javadoc JAR" 178 archiveClassifier.set("javadoc") 179} 180 181// Disable Gradle metadata publication. 182tasks.withType(GenerateModuleMetadata) { 183 enabled = false 184} 185 186// TODO(danysantiago): Use POM template in tools/ to avoid duplicating lines. 187publishing { 188 publications { 189 plugin(MavenPublication) { 190 artifactId = pluginArtifactId 191 version = getPublishVersion() 192 from components.kotlin 193 artifact(shadowJar) 194 artifact(sourcesJar) 195 artifact(javadocJar) 196 pom { 197 addPomTemplate(owner) 198 } 199 } 200 // https://docs.gradle.org/current/userguide/plugins.html#sec:plugin_markers 201 pluginMarker(MavenPublication) { 202 groupId = pluginId 203 artifactId = "${pluginId}.gradle.plugin" 204 version = getPublishVersion() 205 pom { 206 addPomTemplate(owner) 207 withXml { 208 def dependencyNode = 209 asNode().appendNode("dependencies").appendNode("dependency") 210 dependencyNode.appendNode("groupId", group) 211 dependencyNode.appendNode("artifactId", pluginArtifactId) 212 dependencyNode.appendNode("version", getPublishVersion()) 213 } 214 } 215 } 216 } 217 // Publish to build output repository. 218 repositories { 219 maven { 220 url = uri("$buildDir/repo") 221 } 222 } 223} 224 225group = 'com.google.dagger' 226 227// TODO(danysantiago): Use POM template in tools/ to avoid duplicating lines. 228def addPomTemplate(pom) { 229 pom.name = 'Hilt Android Gradle Plugin' 230 pom.description = 'A fast dependency injector for Android and Java.' 231 pom.url = 'https://github.com/google/dagger' 232 pom.scm { 233 url = 'https://github.com/google/dagger/' 234 connection = 'scm:git:git://github.com/google/dagger.git' 235 developerConnection = 'scm:git:ssh://git@github.com/google/dagger.git' 236 tag = 'HEAD' 237 } 238 pom.issueManagement { 239 system = 'GitHub Issues' 240 url = 'https://github.com/google/dagger/issues' 241 } 242 pom.licenses { 243 license { 244 name = 'Apache 2.0' 245 url = 'https://www.apache.org/licenses/LICENSE-2.0.txt' 246 } 247 } 248 pom.organization { 249 name = 'Google, Inc.' 250 url = 'https://www.google.com' 251 } 252 pom.withXml { 253 def projectNode = asNode() 254 // Adds: 255 // <parent> 256 // <groupId>org.sonatype.oss</groupId> 257 // <artifactId>oss-parent</artifactId> 258 // <version>7</version> 259 // </parent> 260 def parentNode = projectNode.appendNode('parent') 261 parentNode.appendNode('groupId', 'org.sonatype.oss') 262 parentNode.appendNode('artifactId', 'oss-parent') 263 parentNode.appendNode('version', '7') 264 // Adds scm->tag because for some reason the DSL API does not. 265 // <scm> 266 // <tag>HEAD</tag> 267 // </scm> 268 projectNode.get('scm').first().appendNode('tag', 'HEAD') 269 } 270} 271 272def getPublishVersion() { 273 def publishVersion = findProperty("PublishVersion") 274 return (publishVersion != null) ? publishVersion : "LOCAL-SNAPSHOT" 275} 276 277def checkJarFile(File jarFile, String... allowedPrefixes) { 278 def zip = new ZipFile(jarFile) 279 try { 280 Enumeration<ZipEntry> list = zip.entries() 281 while (list.hasMoreElements()) { 282 String entry = list.nextElement().name 283 if (!allowedPrefixes.any { entry.startsWith(it) }) { 284 throw new GradleException( 285 "Found a file that is not in " + 286 "${ allowedPrefixes.collect { "'$it'" }.join('/') }: $entry") 287 } 288 } 289 } finally { 290 zip.close() 291 } 292} 293