Skip to content

Commit 74e7deb

Browse files
authored
Kotlin migration part 4 (#1021)
* Convert AbstractDependencyFilter * Convert DefaultDependencyFilter * Convert MinimizeDependencyFilter * Convert UnusedTracker * Simplify RelocatorRemapper * Convert RelocatorRemapper
1 parent 48ace22 commit 74e7deb

File tree

10 files changed

+310
-387
lines changed

10 files changed

+310
-387
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.github.jengelman.gradle.plugins.shadow.impl
2+
3+
import com.github.jengelman.gradle.plugins.shadow.ShadowStats
4+
import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext
5+
import com.github.jengelman.gradle.plugins.shadow.relocation.RelocatePathContext
6+
import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator
7+
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction
8+
import java.util.regex.Pattern
9+
import org.objectweb.asm.commons.Remapper
10+
11+
/**
12+
* Modified from `org.apache.maven.plugins.shade.DefaultShader.java#RelocatorRemapper`
13+
*
14+
* @author John Engelman
15+
*/
16+
open class RelocatorRemapper(
17+
private val relocators: List<Relocator>,
18+
private val stats: ShadowStats,
19+
) : Remapper() {
20+
private val classPattern: Pattern = Pattern.compile("(\\[*)?L(.+)")
21+
22+
open fun hasRelocators(): Boolean = relocators.isNotEmpty()
23+
24+
override fun mapValue(value: Any): Any {
25+
return if (value is String) {
26+
map(value)
27+
} else {
28+
super.mapValue(value)
29+
}
30+
}
31+
32+
override fun map(name: String): String {
33+
var newName = name
34+
var prefix = ""
35+
var suffix = ""
36+
37+
val matcher = classPattern.matcher(newName)
38+
if (matcher.matches()) {
39+
prefix = matcher.group(1) + "L"
40+
suffix = ""
41+
newName = matcher.group(2)
42+
}
43+
44+
for (relocator in relocators) {
45+
if (relocator.canRelocateClass(newName)) {
46+
val classContext = RelocateClassContext.builder().className(newName).stats(stats).build()
47+
return prefix + relocator.relocateClass(classContext) + suffix
48+
} else if (relocator.canRelocatePath(newName)) {
49+
val pathContext = RelocatePathContext.builder().path(newName).stats(stats).build()
50+
return prefix + relocator.relocatePath(pathContext) + suffix
51+
}
52+
}
53+
54+
return name
55+
}
56+
57+
open fun mapPath(path: String): String {
58+
return map(path.substring(0, path.indexOf('.')))
59+
}
60+
61+
open fun mapPath(path: ShadowCopyAction.RelativeArchivePath): String {
62+
return mapPath(path.pathString)
63+
}
64+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package com.github.jengelman.gradle.plugins.shadow.internal
2+
3+
import groovy.lang.Closure
4+
import org.gradle.api.Project
5+
import org.gradle.api.artifacts.Configuration
6+
import org.gradle.api.artifacts.Dependency
7+
import org.gradle.api.artifacts.ResolvedArtifact
8+
import org.gradle.api.artifacts.ResolvedDependency
9+
import org.gradle.api.file.FileCollection
10+
import org.gradle.api.specs.Spec
11+
import org.gradle.api.specs.Specs
12+
13+
internal sealed class AbstractDependencyFilter(
14+
private val project: Project,
15+
) : DependencyFilter {
16+
protected val includeSpecs: MutableList<Spec<ResolvedDependency>> = mutableListOf()
17+
protected val excludeSpecs: MutableList<Spec<ResolvedDependency>> = mutableListOf()
18+
19+
protected abstract fun resolve(
20+
dependencies: Set<ResolvedDependency>,
21+
includedDependencies: MutableSet<ResolvedDependency>,
22+
excludedDependencies: MutableSet<ResolvedDependency>,
23+
)
24+
25+
override fun resolve(configuration: FileCollection): FileCollection {
26+
val includedDeps = mutableSetOf<ResolvedDependency>()
27+
val excludedDeps = mutableSetOf<ResolvedDependency>()
28+
configuration as Configuration
29+
resolve(configuration.resolvedConfiguration.firstLevelModuleDependencies, includedDeps, excludedDeps)
30+
return project.files(configuration.files) -
31+
project.files(excludedDeps.flatMap { it.moduleArtifacts.map(ResolvedArtifact::getFile) })
32+
}
33+
34+
override fun resolve(configurations: Collection<FileCollection>): FileCollection {
35+
return configurations.map { resolve(it) }
36+
.reduceOrNull { acc, fileCollection -> acc + fileCollection }
37+
?: project.files()
38+
}
39+
40+
/**
41+
* Exclude dependencies that match the provided spec.
42+
*/
43+
override fun exclude(spec: Spec<ResolvedDependency>): DependencyFilter = apply {
44+
excludeSpecs.add(spec)
45+
}
46+
47+
/**
48+
* Include dependencies that match the provided spec.
49+
*/
50+
override fun include(spec: Spec<ResolvedDependency>): DependencyFilter = apply {
51+
includeSpecs.add(spec)
52+
}
53+
54+
/**
55+
* Create a spec that matches the provided project notation on group, name, and version.
56+
*/
57+
override fun project(notation: Map<String, *>): Spec<ResolvedDependency> {
58+
return dependency(dependency = project.dependencies.project(notation))
59+
}
60+
61+
/**
62+
* Create a spec that matches the default configuration for the provided project path on group, name, and version.
63+
*/
64+
override fun project(notation: String): Spec<ResolvedDependency> {
65+
return dependency(
66+
dependency = project.dependencies.project(
67+
mapOf(
68+
"path" to notation,
69+
"configuration" to "default",
70+
),
71+
),
72+
)
73+
}
74+
75+
/**
76+
* Create a spec that matches dependencies using the provided notation on group, name, and version.
77+
*/
78+
override fun dependency(notation: Any): Spec<ResolvedDependency> {
79+
return dependency(dependency = project.dependencies.create(notation))
80+
}
81+
82+
/**
83+
* Create a spec that matches the provided dependency on group, name, and version.
84+
*/
85+
override fun dependency(dependency: Dependency): Spec<ResolvedDependency> {
86+
return Spec<ResolvedDependency> { resolvedDependency ->
87+
(dependency.group == null || resolvedDependency.moduleGroup.matches(dependency.group!!.toRegex())) &&
88+
resolvedDependency.moduleName.matches(dependency.name.toRegex()) &&
89+
(dependency.version == null || resolvedDependency.moduleVersion.matches(dependency.version!!.toRegex()))
90+
}
91+
}
92+
93+
/**
94+
* Create a spec that matches the provided closure.
95+
*/
96+
override fun dependency(closure: Closure<*>): Spec<ResolvedDependency> {
97+
return Specs.convertClosureToSpec(closure)
98+
}
99+
100+
protected fun ResolvedDependency.isIncluded(): Boolean {
101+
val include = includeSpecs.isEmpty() || includeSpecs.any { it.isSatisfiedBy(this) }
102+
val exclude = excludeSpecs.isNotEmpty() && excludeSpecs.any { it.isSatisfiedBy(this) }
103+
return include && !exclude
104+
}
105+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.github.jengelman.gradle.plugins.shadow.internal
2+
3+
import org.gradle.api.Project
4+
import org.gradle.api.artifacts.ResolvedDependency
5+
6+
internal class DefaultDependencyFilter(
7+
project: Project,
8+
) : AbstractDependencyFilter(project) {
9+
override fun resolve(
10+
dependencies: Set<ResolvedDependency>,
11+
includedDependencies: MutableSet<ResolvedDependency>,
12+
excludedDependencies: MutableSet<ResolvedDependency>,
13+
) {
14+
dependencies.forEach {
15+
if (if (it.isIncluded()) includedDependencies.add(it) else excludedDependencies.add(it)) {
16+
resolve(it.children, includedDependencies, excludedDependencies)
17+
}
18+
}
19+
}
20+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.github.jengelman.gradle.plugins.shadow.internal
2+
3+
import org.gradle.api.Project
4+
import org.gradle.api.artifacts.ResolvedDependency
5+
6+
internal class MinimizeDependencyFilter(
7+
project: Project,
8+
) : AbstractDependencyFilter(project) {
9+
override fun resolve(
10+
dependencies: Set<ResolvedDependency>,
11+
includedDependencies: MutableSet<ResolvedDependency>,
12+
excludedDependencies: MutableSet<ResolvedDependency>,
13+
) {
14+
dependencies.forEach {
15+
if (it.isIncluded() && !isParentExcluded(excludedDependencies, it)) {
16+
includedDependencies.add(it)
17+
} else {
18+
excludedDependencies.add(it)
19+
}
20+
resolve(it.children, includedDependencies, excludedDependencies)
21+
}
22+
}
23+
24+
private fun isParentExcluded(
25+
excludedDependencies: Set<ResolvedDependency>,
26+
dependency: ResolvedDependency,
27+
): Boolean {
28+
return excludedDependencies.any { it in dependency.parents }
29+
}
30+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package com.github.jengelman.gradle.plugins.shadow.internal
2+
3+
import java.io.File
4+
import org.gradle.api.Project
5+
import org.gradle.api.artifacts.Configuration
6+
import org.gradle.api.artifacts.Dependency
7+
import org.gradle.api.artifacts.ProjectDependency
8+
import org.gradle.api.artifacts.SelfResolvingDependency
9+
import org.gradle.api.file.FileCollection
10+
import org.gradle.api.tasks.InputFiles
11+
import org.vafer.jdependency.Clazzpath
12+
import org.vafer.jdependency.ClazzpathUnit
13+
14+
/** Tracks unused classes in the project classpath. */
15+
internal class UnusedTracker private constructor(
16+
classDirs: Iterable<File>,
17+
classJars: FileCollection,
18+
private val _toMinimize: FileCollection,
19+
) {
20+
private val projectUnits: List<ClazzpathUnit>
21+
private val cp = Clazzpath()
22+
23+
init {
24+
projectUnits = classDirs.map { cp.addClazzpathUnit(it) } + classJars.map { cp.addClazzpathUnit(it) }
25+
}
26+
27+
@get:InputFiles
28+
val toMinimize: FileCollection get() = _toMinimize
29+
30+
fun findUnused(): Set<String> {
31+
val unused = cp.clazzes.toMutableSet()
32+
for (cpu in projectUnits) {
33+
unused.removeAll(cpu.clazzes)
34+
unused.removeAll(cpu.transitiveDependencies)
35+
}
36+
return unused.map { it.name }.toSet()
37+
}
38+
39+
fun addDependency(jarOrDir: File) {
40+
if (_toMinimize.contains(jarOrDir)) {
41+
cp.addClazzpathUnit(jarOrDir)
42+
}
43+
}
44+
45+
companion object {
46+
@JvmStatic
47+
fun forProject(
48+
apiJars: FileCollection,
49+
sourceSetsClassesDirs: Iterable<File>,
50+
toMinimize: FileCollection,
51+
): UnusedTracker {
52+
return UnusedTracker(sourceSetsClassesDirs, apiJars, toMinimize)
53+
}
54+
55+
@JvmStatic
56+
fun getApiJarsFromProject(project: Project): FileCollection {
57+
val apiDependencies = project.configurations.findByName("api")?.dependencies
58+
?: return project.files()
59+
val runtimeConfiguration = project.configurations.findByName("runtimeClasspath")
60+
?: project.configurations.getByName("runtime")
61+
val apiJars = mutableListOf<File>()
62+
apiDependencies.forEach { dep ->
63+
when (dep) {
64+
is ProjectDependency -> {
65+
apiJars.addAll(getApiJarsFromProject(dep.dependencyProject))
66+
addJar(runtimeConfiguration, dep, apiJars)
67+
}
68+
is SelfResolvingDependency -> {
69+
apiJars.addAll(dep.resolve())
70+
}
71+
else -> {
72+
addJar(runtimeConfiguration, dep, apiJars)
73+
apiJars.add(runtimeConfiguration.find { it.name.startsWith("${dep.name}-") } as File)
74+
}
75+
}
76+
}
77+
return project.files(apiJars)
78+
}
79+
80+
private fun addJar(config: Configuration, dep: Dependency, result: MutableList<File>) {
81+
config.find { isProjectDependencyFile(it, dep) }?.let { result.add(it) }
82+
}
83+
84+
private fun isProjectDependencyFile(file: File, dep: Dependency): Boolean {
85+
val fileName = file.name
86+
val dependencyName = dep.name
87+
return fileName == "$dependencyName.jar" ||
88+
(fileName.startsWith("$dependencyName-") && fileName.endsWith(".jar"))
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)