1+ /*
2+ * Copyright (C) 2025 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+ */
16+
17+ package dagger.gradle.build
18+
19+ import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin
20+ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
21+ import com.vanniktech.maven.publish.MavenPublishBaseExtension
22+ import com.vanniktech.maven.publish.SonatypeHost
23+ import org.gradle.api.JavaVersion
24+ import org.gradle.api.Plugin
25+ import org.gradle.api.Project
26+ import org.gradle.api.plugins.JavaPluginExtension
27+ import org.gradle.api.tasks.TaskProvider
28+ import org.gradle.jvm.tasks.Jar
29+ import org.gradle.jvm.toolchain.JavaLanguageVersion
30+ import org.gradle.kotlin.dsl.create
31+ import org.gradle.kotlin.dsl.withType
32+ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
33+ import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
34+ import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
35+ import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
36+ import org.jetbrains.kotlin.gradle.plugin.KotlinBasePluginWrapper
37+
38+ /* *
39+ * A plugin to configure the Gradle projects. All projects should apply this plugin which will configure the project
40+ * based on other projects plugins applied, such as the Kotlin JVM plugin (`libs.plugins.kotlinJvm`) or the shadow
41+ * plugin (`libs.plugins.shadow`).
42+ *
43+ * This plugin can be applied using:
44+ * ```
45+ * plugins {
46+ * alias(libs.plugins.daggerBuild)
47+ * }
48+ * ```
49+ *
50+ * Source sets for the project are configured by this plugin and should have the following
51+ * convention:
52+ * ```
53+ * <project>
54+ * main
55+ * java - Library sources
56+ * resources - Library resources
57+ * test
58+ * javatests - Unit test sources
59+ * resources - Unit test resources
60+ * ```
61+ */
62+ class DaggerConventionPlugin : Plugin <Project > {
63+ override fun apply (project : Project ) {
64+ val daggerExtension = project.extensions.create<DaggerBuildExtension >(" daggerBuild" )
65+ val shadeExtension = project.extensions.create<ShadeExtension >(" shading" )
66+
67+ // Perform different configuration action based on the plugins applied to the project
68+ project.plugins.configureEach {
69+ when (this ) {
70+ is KotlinBasePluginWrapper ->
71+ configureWithKotlinPlugin(project)
72+ is ShadowPlugin ->
73+ configureWithShadowPlugin(project, shadeExtension)
74+ }
75+ }
76+ configurePublish(project, daggerExtension)
77+ }
78+
79+ private fun configureWithKotlinPlugin (project : Project ) {
80+ configureSourceSets(project)
81+ configureKotlinJvmTarget(project)
82+ }
83+
84+ private fun configureSourceSets (project : Project ) {
85+ val kotlinExtension =
86+ project.extensions.findByType(KotlinProjectExtension ::class .java)
87+ ? : error(" Unable to find Kotlin Project Extension" )
88+ val javaExtension =
89+ project.extensions.findByType(JavaPluginExtension ::class .java)
90+ ? : error(" Unable to find Java Project Extension" )
91+ listOf (" main" , " test" ).forEach { name ->
92+ val rootSrcDir =
93+ when (name) {
94+ " main" -> " java"
95+ " test" -> " javatests"
96+ else -> error(" Unknown source set named '$name '." )
97+ }
98+ kotlinExtension.sourceSets.named(name).configure {
99+ kotlin.srcDirs(" $name /$rootSrcDir " )
100+ resources.srcDirs(" $name /resources" )
101+ }
102+ javaExtension.sourceSets.named(name).configure { java.srcDirs(" $name /$rootSrcDir " ) }
103+ }
104+ }
105+
106+ private fun configureKotlinJvmTarget (project : Project ) {
107+ val kotlinProject = project.extensions.getByName(" kotlin" ) as KotlinJvmProjectExtension
108+ kotlinProject.jvmToolchain {
109+ languageVersion.set(JavaLanguageVersion .of(project.getVersionByName(" jdk" )))
110+ }
111+ kotlinProject.compilerOptions.apply {
112+ languageVersion.set(KotlinVersion .fromVersion(project.getVersionByName(" kotlinTarget" )))
113+ apiVersion.set(KotlinVersion .fromVersion(project.getVersionByName(" kotlinTarget" )))
114+ jvmTarget.set(JvmTarget .fromTarget(project.getVersionByName(" jvmTarget" )))
115+ }
116+ val javaProject = project.extensions.getByName(" java" ) as JavaPluginExtension
117+ javaProject.sourceCompatibility = JavaVersion .toVersion(project.getVersionByName(" jvmTarget" ))
118+ javaProject.targetCompatibility = JavaVersion .toVersion(project.getVersionByName(" jvmTarget" ))
119+ }
120+
121+ private fun configureWithShadowPlugin (project : Project , shadeExtension : ShadeExtension ) {
122+ // Configuration for shaded dependencies
123+ val shadedConfiguration =
124+ project.configurations.create(" shaded" ) {
125+ isCanBeConsumed = false
126+ isCanBeResolved = true
127+ isTransitive = false // Do not include transitive dependencies of shaded deps
128+ }
129+
130+ // Shaded dependencies are compile only dependencies
131+ project.configurations.named(" compileOnly" ).configure { extendsFrom(shadedConfiguration) }
132+
133+ val shadowTask =
134+ project.tasks.withType<ShadowJar >().named(" shadowJar" ) {
135+ // Use no classifier, the shaded jar is the one to be published.
136+ archiveClassifier.set(" " )
137+ // Set the 'shaded' configuration as the dependencies configuration to shade
138+ configurations = listOf (shadedConfiguration)
139+ // Enable service files merging
140+ mergeServiceFiles()
141+ // Enable package relocation (necessary for project that only relocate but have no
142+ // shaded deps)
143+ isEnableRelocation = true
144+
145+ shadeExtension.rules.forEach { (from, to) -> relocate(from, to) }
146+ }
147+
148+ // Change the default jar task classifier to avoid conflicting with the shaded one.
149+ project.tasks.withType<Jar >().named(" jar" ).configure { archiveClassifier.set(" before-shade" ) }
150+
151+ configureOutgoingArtifacts(project, shadowTask)
152+ }
153+
154+ /* *
155+ * Configure Gradle consumers (that use Gradle publishing metadata) of the project to use the
156+ * shaded jar.
157+ *
158+ * This is necessary so that the publishing Gradle module metadata references the shaded jar. See
159+ * https://github.com/GradleUp/shadow/issues/847
160+ */
161+ private fun configureOutgoingArtifacts (project : Project , task : TaskProvider <ShadowJar >) {
162+ project.configurations.configureEach {
163+ if (name == " apiElements" || name == " runtimeElements" ) {
164+ outgoing.artifacts.clear()
165+ outgoing.artifact(task)
166+ }
167+ }
168+ }
169+
170+ private fun configurePublish (project : Project , daggerExtension : DaggerBuildExtension ) {
171+ if (! daggerExtension.isPublished) {
172+ return
173+ }
174+ project.pluginManager.apply (project.getPluginIdByName(" publish" ))
175+ project.plugins.withId(project.getPluginIdByName(" publish" )) {
176+ val publishExtension =
177+ project.extensions.getByName(" mavenPublishing" ) as MavenPublishBaseExtension
178+ publishExtension.apply {
179+ coordinates(
180+ groupId = " com.google.dagger" ,
181+ artifactId = project.name,
182+ version = project.findProperty(" PUBLISH_VERSION" ).toString(),
183+ )
184+ publishToMavenCentral(SonatypeHost .CENTRAL_PORTAL )
185+ pom {
186+ name.set(project.name.asPomName())
187+ description.set(" A fast dependency injector for Android and Java." )
188+ url.set(" https://github.com/google/dagger" )
189+ scm {
190+ url.set(" https://github.com/google/dagger/" )
191+ connection.set(" scm:git:git://github.com/google/dagger.git" )
192+ }
193+ issueManagement {
194+ system.set(" GitHub Issues" )
195+ url.set(" https://github.com/google/dagger/issues" )
196+ }
197+ licenses {
198+ license {
199+ name.set(" The Apache Software License, Version 2.0" )
200+ url.set(" https://www.apache.org/licenses/LICENSE-2.0.txt" )
201+ }
202+ }
203+ organization {
204+ name.set(" Google, Inc." )
205+ url.set(" https://www.google.com" )
206+ }
207+ }
208+ }
209+ }
210+ }
211+
212+ /* *
213+ * Converts the Gradle project name to a more appropriate name for the POM file.
214+ *
215+ * For example: 'dagger-compiler' to 'Dagger Compiler'
216+ */
217+ private fun String.asPomName (): String {
218+ val parts = split(" -" ).map { first().uppercaseChar() + drop(1 ) }
219+ return parts.joinToString(separator = " " )
220+ }
221+ }
0 commit comments