name.remal.gradle_plugins.plugins.dependencies.filtered_dependencies.FilteredDependency.kt Maven / Gradle / Ivy
package name.remal.gradle_plugins.plugins.dependencies.filtered_dependencies
import name.remal.*
import name.remal.gradle_plugins.api.BuildTimeConstants.getStringProperty
import name.remal.gradle_plugins.dsl.extensions.autoFileTree
import org.gradle.api.Buildable
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.FileCollectionDependency
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.artifacts.component.ComponentIdentifier
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.artifacts.dependencies.SelfResolvingDependencyInternal
import org.gradle.api.tasks.TaskDependency
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.ObjectOutputStream
import java.nio.file.Files
import java.nio.file.Path
import java.util.jar.JarEntry
import java.util.jar.JarOutputStream
class FilteredDependency(
private val delegate: Dependency,
private val filter: DependencyFilter,
private val cacheDir: File,
private val project: Project
) : FileCollectionDependency, SelfResolvingDependencyInternal {
private val includes: Set by lazy { filter.includes.toHashSet() }
private val excludes: Set by lazy { filter.excludes.toHashSet() }
private val configurerHash: String by lazy {
sha256(ByteArrayOutputStream().also {
ObjectOutputStream(it).use {
it.writeObject(getStringProperty("version"))
it.writeObject(includes)
it.writeObject(excludes)
}
}.toByteArray())
}
private fun processResolvedFiles(files: Set): Set {
return files.asSequence()
.filter(File::exists)
.map { file ->
val cachedFile = File(cacheDir, "${file.nameWithoutExtension}/${file.hash}/$configurerHash/${file.nameWithoutExtension}.filtered.jar")
if (cachedFile.exists()) return@map cachedFile
JarOutputStream(cachedFile.createParentDirectories().outputStream()).use { jarOutputStream ->
project.autoFileTree(file)
.matching {
it.include(includes)
it.include("META-INF/MANIFEST.MF")
it.include("module-info.class")
it.exclude(excludes)
it.exclude("META-INF/*.SF")
it.exclude("META-INF/*.DSA")
it.exclude("META-INF/*.RSA")
}
.visit { details ->
if (details.isDirectory) {
val entry = JarEntry(details.path + "/")
entry.time = details.lastModified
jarOutputStream.putNextEntry(entry)
} else {
val entry = JarEntry(details.path)
entry.time = details.lastModified
jarOutputStream.putNextEntry(entry)
details.open().use { it.copyTo(jarOutputStream) }
}
}
}
return@map cachedFile
}
.toSet()
}
private val _transitiveFiles: Set by lazy {
processResolvedFiles(
project.configurations.detachedConfiguration(delegate).files
)
}
private val _notTransitiveFiles: Set by lazy {
processResolvedFiles(
project.configurations.detachedConfiguration(delegate.copy()
.also {
if (it is ModuleDependency) {
it.isTransitive = false
}
}
).files
)
}
override fun resolve(transitive: Boolean): Set {
if (transitive) {
return _transitiveFiles
} else {
return _notTransitiveFiles
}
}
override fun resolve(): Set {
return resolve(true)
}
override fun getFiles(): FileCollection {
return project.files(resolve())
}
override fun copy(): Dependency {
return FilteredDependency(
delegate.copy(),
DependencyFilter().also {
it.includes.addAll(filter.includes)
it.excludes.addAll(filter.excludes)
},
cacheDir,
project
).also {
it._reason = _reason
}
}
override fun contentEquals(dependency: Dependency): Boolean {
if (dependency !is FilteredDependency) return false
if (delegate != dependency.delegate) return false
if (filter != dependency.filter) return false
if (cacheDir != dependency.cacheDir) return false
if (project !== dependency.project) return false
return true
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || other !is FilteredDependency) return false
return contentEquals(other)
}
override fun hashCode(): Int {
var result = delegate.hashCode()
result = 31 * result + filter.hashCode()
result = 31 * result + project.hashCode()
return result
}
override fun getGroup(): String? = delegate.group
override fun getName(): String = delegate.name
override fun getVersion(): String? = delegate.version
override fun getBuildDependencies(): TaskDependency {
if (delegate is Buildable) {
return delegate.buildDependencies
}
return TaskDependency { emptySet() }
}
override fun getTargetComponentId(): ComponentIdentifier? {
return null
}
private var _reason: String? = null
override fun because(reason: String?) {
_reason = reason
}
override fun getReason(): String? {
return _reason
}
private val File.hash: String
get() {
val digest = newSha256Digest()
Files.walk(absoluteFile.toPath())
.map { it.toAbsolutePath() }
.filter(Path::isRegularFile)
.distinct()
.sorted()
.forEach { it.newInputStream().use(digest::update) }
return digest.digestHex()
}
}