• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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