Skip to content

Commit c2aab63

Browse files
authored
Merge pull request #54 from rubensousa/allow-external-libraries
Add allowExternalLibraries to restrictModule API
2 parents 254e4c0 + c20f0a9 commit c2aab63

16 files changed

+154
-98
lines changed

projectguard/api/projectguard.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public abstract interface class com/rubensousa/projectguard/plugin/ModuleRestric
2828
public abstract fun allow ([Ljava/lang/String;)V
2929
public abstract fun allow ([Lorg/gradle/api/internal/catalog/DelegatingProjectDependency;)V
3030
public abstract fun allow ([Lorg/gradle/api/provider/Provider;)V
31+
public abstract fun allowExternalLibraries ()V
3132
public abstract fun applyRule (Lcom/rubensousa/projectguard/plugin/RestrictModuleRule;)V
3233
public abstract fun reason (Ljava/lang/String;)V
3334
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ interface ModuleRestrictionScope {
3030

3131
fun allow(vararg library: Provider<MinimalExternalModuleDependency>)
3232

33+
fun allowExternalLibraries()
34+
3335
fun applyRule(rule: RestrictModuleRule)
3436

3537
// Not sure this is a good idea, bundles can be updated without seeing what dependencies we are allowing

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ abstract class ProjectGuardExtension @Inject constructor(
4646
ModuleRestrictionSpec(
4747
modulePath = modulePath,
4848
reason = scope.getReason(),
49-
allowed = scope.getAllowedDependencies()
49+
allowed = scope.getAllowedDependencies(),
50+
allowExternalLibraries = scope.areExternalLibrariesAllowed()
5051
)
5152
)
5253
}

projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/Dependency.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,20 @@
1616

1717
package com.rubensousa.projectguard.plugin.internal
1818

19-
internal sealed interface Dependency {
19+
import java.io.Serializable
20+
21+
internal sealed interface Dependency : Serializable {
2022
val id: String
23+
val isLibrary: Boolean
2124
}
2225

2326
internal data class DirectDependency(
2427
override val id: String,
28+
override val isLibrary: Boolean,
2529
) : Dependency
2630

2731
internal data class TransitiveDependency(
2832
override val id: String,
33+
override val isLibrary: Boolean,
2934
val path: List<String>,
3035
) : Dependency

projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraph.kt

Lines changed: 33 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -21,37 +21,42 @@ import java.io.Serializable
2121
internal class DependencyGraph : Serializable {
2222

2323
private val configurations = mutableMapOf<String, Configuration>()
24-
private val libraries = mutableSetOf<String>()
2524

2625
fun getConfigurations() = configurations.values.toList()
2726

27+
fun addDependency(
28+
module: String,
29+
dependency: DirectDependency,
30+
configurationId: String = DependencyConfiguration.COMPILE,
31+
) {
32+
val configuration = configurations.getOrPut(configurationId) {
33+
Configuration(configurationId)
34+
}
35+
configuration.add(module = module, dependency = dependency)
36+
}
37+
2838
fun addInternalDependency(
2939
module: String,
3040
dependency: String,
3141
configurationId: String = DependencyConfiguration.COMPILE,
3242
) {
3343
addDependency(
3444
module = module,
35-
dependency = dependency,
36-
configurationId = configurationId
45+
dependency = DirectDependency(dependency, isLibrary = false),
46+
configurationId = configurationId,
3747
)
3848
}
3949

40-
fun addExternalDependency(
50+
fun addLibraryDependency(
4151
module: String,
4252
dependency: String,
4353
configurationId: String = DependencyConfiguration.COMPILE,
4454
) {
4555
addDependency(
4656
module = module,
47-
dependency = dependency,
57+
dependency = DirectDependency(dependency, isLibrary = true),
4858
configurationId = configurationId
4959
)
50-
libraries.add(dependency)
51-
}
52-
53-
fun isExternalLibrary(dependency: String): Boolean {
54-
return libraries.contains(dependency)
5560
}
5661

5762
fun getDependencies(module: String): List<Dependency> {
@@ -64,71 +69,57 @@ internal class DependencyGraph : Serializable {
6469
TraversalState(
6570
configurationId = configuration.id,
6671
dependency = dependency,
67-
path = emptyList()
6872
)
6973
)
7074
}
7175
}
7276
while (queue.isNotEmpty()) {
7377
val currentTraversal = queue.removeFirst()
7478
val currentDependency = currentTraversal.dependency
75-
if (visitedDependencies.contains(currentDependency)) {
79+
if (visitedDependencies.contains(currentDependency.id)) {
7680
continue
7781
}
78-
paths[currentDependency] = if (currentTraversal.path.isEmpty()) {
79-
DirectDependency(currentDependency)
80-
} else {
81-
TransitiveDependency(
82-
currentDependency,
83-
currentTraversal.path + currentDependency
84-
)
85-
}
86-
visitedDependencies.add(currentDependency)
82+
paths[currentDependency.id] = currentDependency
83+
visitedDependencies.add(currentDependency.id)
8784
configurations.values.forEach { configuration ->
8885
// Search only for non-test configurations as they're not considered transitive at this point
8986
if (!DependencyConfiguration.isTestConfiguration(configuration.id)) {
90-
configuration.getDependencies(currentDependency).forEach { nextDependency ->
87+
configuration.getDependencies(currentDependency.id).forEach { nextDependency ->
9188
queue.addFirst(
9289
TraversalState(
9390
configurationId = configuration.id,
94-
dependency = nextDependency,
95-
path = currentTraversal.path + currentDependency
91+
dependency = TransitiveDependency(
92+
id = nextDependency.id,
93+
isLibrary = nextDependency.isLibrary,
94+
path = when (currentDependency) {
95+
is DirectDependency -> listOf(currentDependency.id, nextDependency.id)
96+
is TransitiveDependency -> currentDependency.path + nextDependency.id
97+
},
98+
)
9699
)
97100
)
98101
}
99102
}
100103
}
101104
}
102-
return paths.values.sortedBy { it.id }
103-
}
104-
105-
private fun addDependency(
106-
module: String,
107-
dependency: String,
108-
configurationId: String,
109-
) {
110-
val configuration = configurations.getOrPut(configurationId) {
111-
Configuration(configurationId)
112-
}
113-
configuration.add(module = module, dependency = dependency)
105+
return paths.values.sortedBy { dependency -> dependency.id }
114106
}
115107

116108
private data class TraversalState(
117109
val configurationId: String,
118-
val dependency: String,
119-
val path: List<String>,
110+
val dependency: Dependency,
120111
)
121112

122113
class Configuration(val id: String) : Serializable {
123114

124-
private val nodes = mutableMapOf<String, MutableSet<String>>()
115+
private val nodes = mutableMapOf<String, MutableSet<DirectDependency>>()
125116

126-
fun add(module: String, dependency: String) {
117+
fun add(module: String, dependency: DirectDependency) {
127118
val existingDependencies = nodes.getOrPut(module) { mutableSetOf() }
128119
existingDependencies.add(dependency)
129120
}
130121

131-
fun getDependencies(module: String): Set<String> {
122+
fun getDependencies(module: String): Set<DirectDependency> {
132123
return nodes[module] ?: emptySet()
133124
}
134125

@@ -148,14 +139,11 @@ internal class DependencyGraph : Serializable {
148139

149140
override fun equals(other: Any?): Boolean {
150141
return other is DependencyGraph
151-
&& other.libraries == this.libraries
152142
&& other.configurations == this.configurations
153143
}
154144

155145
override fun hashCode(): Int {
156-
var result = configurations.hashCode()
157-
result = 31 * result + libraries.hashCode()
158-
return result
146+
return configurations.hashCode()
159147
}
160148

161149

projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraphBuilder.kt

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,14 @@ internal class DependencyGraphBuilder {
2828
projectDump.modules.forEach { report ->
2929
report.configurations.forEach { configuration ->
3030
configuration.dependencies.forEach { dependency ->
31-
if (dependency.isLibrary) {
32-
graph.addExternalDependency(
33-
configurationId = configuration.id,
34-
module = report.module,
35-
dependency = dependency.id,
36-
)
37-
} else {
38-
graph.addInternalDependency(
39-
configurationId = configuration.id,
40-
module = report.module,
41-
dependency = dependency.id,
42-
)
43-
}
31+
graph.addDependency(
32+
module = report.module,
33+
dependency = DirectDependency(
34+
id = dependency.id,
35+
isLibrary = dependency.isLibrary
36+
),
37+
configurationId = configuration.id
38+
)
4439
}
4540
}
4641
}
@@ -67,7 +62,7 @@ internal class DependencyGraphBuilder {
6762
}
6863

6964
is ExternalModuleDependency -> {
70-
graph.addExternalDependency(
65+
graph.addLibraryDependency(
7166
module = moduleId,
7267
dependency = "${dependency.group}:${dependency.name}",
7368
configurationId = config.name

projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyRestriction.kt

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ internal sealed interface DependencyRestriction {
2626
return "module:$moduleId|dependency:${dependencyId}"
2727
}
2828

29-
fun getText(moduleId: String): String
30-
3129
companion object {
3230

3331
fun from(dependency: Dependency, reason: String): DependencyRestriction {
@@ -50,36 +48,14 @@ internal sealed interface DependencyRestriction {
5048
internal data class DirectDependencyRestriction(
5149
override val dependencyId: String,
5250
override val reason: String = UNSPECIFIED_REASON,
53-
) : DependencyRestriction {
54-
55-
override fun getText(moduleId: String): String {
56-
return """
57-
| Dependency restriction found!
58-
| Module -> $moduleId
59-
| Match -> $dependencyId
60-
| Module '$moduleId' cannot depend on '$dependencyId'
61-
| Reason: $reason
62-
""".trimMargin()
63-
}
64-
65-
}
51+
) : DependencyRestriction
6652

6753
internal data class TransitiveDependencyRestriction(
6854
override val dependencyId: String,
6955
val pathToDependency: List<String>,
7056
override val reason: String = UNSPECIFIED_REASON,
7157
) : DependencyRestriction {
7258

73-
override fun getText(moduleId: String): String {
74-
return """
75-
| Transitive dependency restriction found!
76-
| Module -> $moduleId
77-
| Match -> $dependencyId from ${getPathToDependencyText()}
78-
| Module '$moduleId' cannot depend on '$dependencyId'
79-
| Reason: $reason
80-
""".trimMargin()
81-
}
82-
8359
fun getPathToDependencyText(): String {
8460
return pathToDependency.joinToString(separator = " -> ") { it }
8561
}

projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyRestrictionFinder.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,10 @@ internal class DependencyRestrictionFinder(
8989
dependency: Dependency,
9090
spec: ProjectGuardSpec,
9191
) {
92-
spec.moduleRestrictionSpecs.forEach { restriction ->
92+
for (restriction in spec.moduleRestrictionSpecs) {
93+
if (dependency.isLibrary && restriction.allowExternalLibraries) {
94+
continue
95+
}
9396
val matchesModule = hasModuleMatch(
9497
modulePath = moduleId,
9598
referencePath = restriction.modulePath

projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/ModuleRestrictionScopeImpl.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ internal class ModuleRestrictionScopeImpl : ModuleRestrictionScope {
2626

2727
private val allowed = mutableListOf<ModuleAllowSpec>()
2828
private var restrictionReason = UNSPECIFIED_REASON
29+
private var allowExternalLibraries = false
2930

3031
override fun reason(reason: String) {
3132
restrictionReason = reason
@@ -41,6 +42,10 @@ internal class ModuleRestrictionScopeImpl : ModuleRestrictionScope {
4142
allow(modulePath = moduleDelegation.map { module -> module.path }.toTypedArray())
4243
}
4344

45+
override fun allowExternalLibraries() {
46+
allowExternalLibraries = true
47+
}
48+
4449
override fun allow(vararg library: Provider<MinimalExternalModuleDependency>) {
4550
allow(modulePath = library.map { lib ->
4651
lib.getDependencyPath()
@@ -55,5 +60,7 @@ internal class ModuleRestrictionScopeImpl : ModuleRestrictionScope {
5560

5661
fun getReason() = restrictionReason
5762

63+
fun areExternalLibrariesAllowed() = allowExternalLibraries
64+
5865

5966
}

projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/ModuleRestrictionSpec.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ internal data class ModuleRestrictionSpec(
2222
val modulePath: String,
2323
val reason: String,
2424
val allowed: List<ModuleAllowSpec>,
25+
val allowExternalLibraries: Boolean,
2526
): Serializable

0 commit comments

Comments
 (0)