Skip to content

Commit a171981

Browse files
authored
Merge pull request #75 from rubensousa/build-option
Add option to attach plugin to lifecycle tasks
2 parents 3a51b43 + 3ff318d commit a171981

File tree

18 files changed

+292
-34
lines changed

18 files changed

+292
-34
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
plugins {
2-
alias(libs.plugins.jetbrains.kotlin.jvm) apply false
2+
alias(libs.plugins.kotlin.jvm) apply false
33
alias(libs.plugins.kover) apply false
44
alias(libs.plugins.maven.publish) apply false
55
}

gradle.properties

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,6 @@
1-
# Project-wide Gradle settings.
2-
# IDE (e.g. Android Studio) users:
3-
# Gradle settings configured through the IDE *will override*
4-
# any settings specified in this file.
5-
# For more details on how to configure your build environment visit
6-
# http://www.gradle.org/docs/current/userguide/build_environment.html
7-
# Specifies the JVM arguments used for the daemon process.
8-
# The setting is particularly useful for tweaking memory settings.
9-
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10-
# When configured, Gradle will run in incubating parallel mode.
11-
# This option should only be used with decoupled projects. For more details, visit
12-
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
13-
# org.gradle.parallel=true
14-
# AndroidX package structure to make it clearer which packages are bundled with the
15-
# Android operating system, and which are packaged with your app's APK
16-
# https://developer.android.com/topic/libraries/support-library/androidx-rn
1+
org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 -XX:MetaspaceSize=1g
172
android.useAndroidX=true
18-
# Kotlin code style for this project: "official" or "obsolete":
193
kotlin.code.style=official
20-
# Enables namespacing of each library's R class so that its R class includes only the
21-
# resources declared in the library itself and none from the library's dependencies,
22-
# thereby reducing the size of the R class for that library
234
android.nonTransitiveRClass=true
245

256
POM_NAME=ProjectGuard Plugin

gradle/libs.versions.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", versio
2121
runner = { group = "androidx.test", name = "runner", version.ref = "runner" }
2222
core = { group = "androidx.test", name = "core", version.ref = "core" }
2323
ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
24+
gradle-android = { group = "com.android.tools.build", name = "gradle", version.ref = "agp" }
2425

2526
[plugins]
2627
android-application = { id = "com.android.application", version.ref = "agp" }
2728
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
28-
jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
29+
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
2930
maven-publish = { id = "com.vanniktech.maven.publish", version = "0.36.0" }
3031
projectguard = { id = "com.rubensousa.projectguard", version = "unspecified" }
3132
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }

projectguard/api/projectguard.api

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ public abstract interface class com/rubensousa/projectguard/plugin/GuardScope {
2525
public abstract fun deny (Lorg/gradle/api/provider/Provider;Lorg/gradle/api/Action;)V
2626
}
2727

28+
public final class com/rubensousa/projectguard/plugin/LifecycleTask : java/lang/Enum {
29+
public static final field ASSEMBLE Lcom/rubensousa/projectguard/plugin/LifecycleTask;
30+
public static final field CHECK Lcom/rubensousa/projectguard/plugin/LifecycleTask;
31+
public static fun valueOf (Ljava/lang/String;)Lcom/rubensousa/projectguard/plugin/LifecycleTask;
32+
public static fun values ()[Lcom/rubensousa/projectguard/plugin/LifecycleTask;
33+
}
34+
2835
public abstract interface class com/rubensousa/projectguard/plugin/ModuleRestrictionScope {
2936
public abstract fun allow ([Ljava/lang/String;)V
3037
public abstract fun allow ([Lorg/gradle/api/internal/catalog/DelegatingProjectDependency;)V
@@ -34,10 +41,16 @@ public abstract interface class com/rubensousa/projectguard/plugin/ModuleRestric
3441
public abstract fun reason (Ljava/lang/String;)V
3542
}
3643

44+
public abstract interface class com/rubensousa/projectguard/plugin/OptionScope {
45+
public abstract fun getLifecycleTask ()Lcom/rubensousa/projectguard/plugin/LifecycleTask;
46+
public abstract fun setLifecycleTask (Lcom/rubensousa/projectguard/plugin/LifecycleTask;)V
47+
}
48+
3749
public abstract class com/rubensousa/projectguard/plugin/ProjectGuardExtension : com/rubensousa/projectguard/plugin/ProjectGuardScope {
3850
public fun <init> (Lorg/gradle/api/model/ObjectFactory;)V
3951
public fun guard (Ljava/lang/String;Lorg/gradle/api/Action;)V
4052
public fun guardRule (Lorg/gradle/api/Action;)Lcom/rubensousa/projectguard/plugin/GuardRule;
53+
public fun options (Lorg/gradle/api/Action;)V
4154
public fun report (Lorg/gradle/api/Action;)V
4255
public fun restrictDependency (Ljava/lang/String;Lorg/gradle/api/Action;)V
4356
public fun restrictDependency (Lorg/gradle/api/provider/Provider;Lorg/gradle/api/Action;)V
@@ -56,6 +69,8 @@ public abstract interface class com/rubensousa/projectguard/plugin/ProjectGuardS
5669
public abstract fun guard (Ljava/lang/String;Lorg/gradle/api/Action;)V
5770
public fun guard (Lorg/gradle/api/internal/catalog/DelegatingProjectDependency;Lorg/gradle/api/Action;)V
5871
public abstract fun guardRule (Lorg/gradle/api/Action;)Lcom/rubensousa/projectguard/plugin/GuardRule;
72+
public fun options (Lgroovy/lang/Closure;)V
73+
public abstract fun options (Lorg/gradle/api/Action;)V
5974
public fun report (Lgroovy/lang/Closure;)V
6075
public abstract fun report (Lorg/gradle/api/Action;)V
6176
public fun restrictDependency (Ljava/lang/String;Lgroovy/lang/Closure;)V

projectguard/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ dependencies {
4242
implementation(libs.kotlin.serialization.json)
4343
implementation(libs.jackson.yaml)
4444
implementation(libs.jackson.kotlin)
45+
implementation(libs.gradle.android)
4546
testImplementation(gradleTestKit())
4647
testImplementation(libs.kotlin.test)
4748
testImplementation(libs.truth)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright 2026 Rúben Sousa
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 com.rubensousa.projectguard.plugin
18+
19+
enum class LifecycleTask {
20+
ASSEMBLE,
21+
CHECK
22+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2026 Rúben Sousa
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 com.rubensousa.projectguard.plugin
18+
19+
interface OptionScope {
20+
/**
21+
* `projectGuardCheck` will be included in either [LifecycleTask.CHECK], [LifecycleTask.ASSEMBLE]
22+
* or none if null
23+
*/
24+
var lifecycleTask: LifecycleTask?
25+
}

projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/ProjectGuardExtension.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import com.rubensousa.projectguard.plugin.internal.GuardScopeImpl
2222
import com.rubensousa.projectguard.plugin.internal.GuardSpec
2323
import com.rubensousa.projectguard.plugin.internal.ModuleRestrictionScopeImpl
2424
import com.rubensousa.projectguard.plugin.internal.ModuleRestrictionSpec
25+
import com.rubensousa.projectguard.plugin.internal.OptionScopeImpl
26+
import com.rubensousa.projectguard.plugin.internal.PluginOptions
2527
import com.rubensousa.projectguard.plugin.internal.ProjectGuardSpec
2628
import com.rubensousa.projectguard.plugin.internal.ReportScopeImpl
2729
import com.rubensousa.projectguard.plugin.internal.ReportSpec
@@ -41,6 +43,9 @@ abstract class ProjectGuardExtension @Inject constructor(
4143
private val moduleRestrictionSpecs = objects.listProperty<ModuleRestrictionSpec>()
4244
private val dependencyRestrictionSpecs = objects.listProperty<DependencyRestrictionSpec>()
4345
private var reportSpec = ReportSpec(showLibrariesInGraph = false)
46+
private var options = PluginOptions(
47+
lifecycleTask = null
48+
)
4449

4550
override fun restrictModule(modulePath: String, action: Action<ModuleRestrictionScope>) {
4651
val scope = ModuleRestrictionScopeImpl()
@@ -127,12 +132,21 @@ abstract class ProjectGuardExtension @Inject constructor(
127132
)
128133
}
129134

135+
override fun options(action: Action<OptionScope>) {
136+
val scope = OptionScopeImpl()
137+
action.execute(scope)
138+
options = options.copy(
139+
lifecycleTask = scope.lifecycleTask,
140+
)
141+
}
142+
130143
internal fun getSpec(): ProjectGuardSpec {
131144
return ProjectGuardSpec(
132145
guardSpecs = guardSpecs.get(),
133146
moduleRestrictionSpecs = moduleRestrictionSpecs.get(),
134147
dependencyRestrictionSpecs = dependencyRestrictionSpecs.get(),
135-
reportSpec = reportSpec
148+
reportSpec = reportSpec,
149+
options = options
136150
)
137151
}
138152

projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/ProjectGuardPlugin.kt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.rubensousa.projectguard.plugin
1818

19+
import com.android.build.api.variant.AndroidComponentsExtension
1920
import com.rubensousa.projectguard.plugin.internal.DependencyGraphBuilder
2021
import com.rubensousa.projectguard.plugin.internal.task.TaskAggregateDependencyDump
2122
import com.rubensousa.projectguard.plugin.internal.task.TaskAggregateRestrictionDump
@@ -53,6 +54,13 @@ class ProjectGuardPlugin : Plugin<Project> {
5354
private val dependenciesFilePath = "reports/$pluginId/dependencies.json"
5455
private val jsonReportFilePath = "reports/$pluginId/report.json"
5556
private val graphBuilder = DependencyGraphBuilder()
57+
private val androidPluginIds = listOf(
58+
"com.android.test",
59+
"com.android.application",
60+
"com.android.library",
61+
"com.android.dynamic-feature",
62+
"com.android.kotlin.multiplatform.library"
63+
)
5664

5765
override fun apply(target: Project) {
5866
val rootProject = target.rootProject
@@ -71,6 +79,8 @@ class ProjectGuardPlugin : Plugin<Project> {
7179
individualModuleTasks.add(moduleTasks)
7280
setupModuleTasks(
7381
aggregationTasks = aggregationTasks,
82+
project = targetProject,
83+
extension = extension,
7484
moduleTasks = moduleTasks
7585
)
7686
}
@@ -93,6 +103,8 @@ class ProjectGuardPlugin : Plugin<Project> {
93103
}
94104

95105
private fun setupModuleTasks(
106+
project: Project,
107+
extension: ProjectGuardExtension,
96108
aggregationTasks: AggregationTasks,
97109
moduleTasks: ModuleTasks,
98110
) {
@@ -109,6 +121,43 @@ class ProjectGuardPlugin : Plugin<Project> {
109121
outputDir.set(project.layout.buildDirectory.dir(htmlAggregateReportFilePath))
110122
reportFilePath.set(getProjectReportFilePath(project))
111123
}
124+
125+
project.afterEvaluate {
126+
val options = extension.getSpec().options
127+
options.lifecycleTask?.let { lifecycleTask ->
128+
if (lifecycleTask == LifecycleTask.ASSEMBLE) {
129+
attachToAndroidAssembleTasks(project, moduleTasks.check)
130+
project.tasks.findByName("assemble")?.dependsOn(moduleTasks.check)
131+
} else {
132+
project.tasks.findByName("check")?.dependsOn(moduleTasks.check)
133+
}
134+
}
135+
}
136+
}
137+
138+
private fun attachToAndroidAssembleTasks(
139+
project: Project,
140+
checkTask: TaskProvider<TaskCheck>,
141+
) {
142+
androidPluginIds.forEach { pluginId ->
143+
if (project.plugins.hasPlugin(pluginId)) {
144+
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
145+
val variantTasks = mutableListOf<String>()
146+
androidComponents.onVariants { variant ->
147+
val variantName = capitalizeVariantName(variant.name)
148+
variantTasks.add("assemble$variantName")
149+
}
150+
project.afterEvaluate {
151+
variantTasks.forEach { variantTask ->
152+
project.tasks.findByName(variantTask)?.dependsOn(checkTask)
153+
}
154+
}
155+
}
156+
}
157+
}
158+
159+
private fun capitalizeVariantName(name: String): String {
160+
return name.substring(0, 1).uppercase() + name.substring(1, name.length)
112161
}
113162

114163
private fun setupAggregationTasks(

projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/ProjectGuardScope.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,10 @@ interface ProjectGuardScope {
207207
report(ConfigureUtil.configureUsing(closure))
208208
}
209209

210+
fun options(action: Action<OptionScope>)
211+
212+
fun options(closure: Closure<OptionScope>) {
213+
options(ConfigureUtil.configureUsing(closure))
214+
}
215+
210216
}

0 commit comments

Comments
 (0)