name.remal.gradle_plugins.dsl.utils.readJavaModuleName.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-plugins-kotlin-dsl Show documentation
Show all versions of gradle-plugins-kotlin-dsl Show documentation
Remal Gradle plugins: gradle-plugins-kotlin-dsl
package name.remal.gradle_plugins.dsl.utils
import name.remal.ASM_API
import name.remal.accept
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ModuleVisitor
import org.objectweb.asm.Opcodes
import java.io.File
import java.lang.reflect.Field
import java.util.jar.Manifest
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipFile.OPEN_READ
@Suppress("ComplexMethod", "NestedBlockDepth")
fun readJavaModuleName(artifactFile: File): String? {
val file = artifactFile.absoluteFile
try {
if (file.isFile) {
ZipFile(file, OPEN_READ).use { zipFile ->
MODULE_INFO_PATHS.forEach { moduleInfoPath ->
zipFile.getEntry(moduleInfoPath)?.takeUnless(ZipEntry::isDirectory)?.let(zipFile::getInputStream)?.use { inputStream ->
try {
val bytecode = inputStream.readBytes()
val classReader = ClassReader(bytecode)
val visitor = ModuleNameVisitor()
classReader.accept(visitor)
visitor.moduleName?.let { return it }
} catch (e: Throwable) {
getGradleLogger(::readJavaModuleName.name).warn("Error processing $file!/$moduleInfoPath", e)
}
}
}
zipFile.getEntry(MANIFEST_PATH)?.takeUnless(ZipEntry::isDirectory)?.let(zipFile::getInputStream)?.use { inputStream ->
try {
Manifest(inputStream).mainAttributes?.getValue(MODULE_NAME_MANIFEST_ATTRIBUTE)?.let { return it }
} catch (e: Throwable) {
getGradleLogger(::readJavaModuleName.name).warn("Error processing $file!/$MANIFEST_PATH", e)
}
}
}
return file.autoJavaModuleName
} else if (file.isDirectory) {
MODULE_INFO_PATHS.forEach { moduleInfoPath ->
val moduleInfoFile = file.resolve(moduleInfoPath)
if (moduleInfoFile.isFile) {
try {
val bytecode = moduleInfoFile.readBytes()
val classReader = ClassReader(bytecode)
val visitor = ModuleNameVisitor()
classReader.accept(visitor)
visitor.moduleName?.let { return it }
} catch (e: Throwable) {
getGradleLogger(::readJavaModuleName.name).warn("Error processing $moduleInfoFile", e)
}
}
}
file.resolve(MANIFEST_PATH).takeIf(File::isFile)?.let { manifestFile ->
try {
manifestFile.inputStream().use { inputStream ->
Manifest(inputStream).mainAttributes?.getValue(MODULE_NAME_MANIFEST_ATTRIBUTE)?.let { return it }
}
} catch (e: Throwable) {
getGradleLogger(::readJavaModuleName.name).warn("Error processing $manifestFile", e)
}
}
}
} catch (e: Throwable) {
getGradleLogger(::readJavaModuleName.name).warn("Error processing $file", e)
}
return null
}
private val DASH_VERSION_REGEX = Regex("-(\\d+(\\..*|$))")
private val NON_ALPHANUM_REGEX = Regex("[^A-Za-z0-9]")
private val REPEATING_DOTS = Regex("\\.{2,}")
private val File.autoJavaModuleName: String
get() {
var name = this.nameWithoutExtension
name = name.replace(DASH_VERSION_REGEX, "")
name = name.replace(NON_ALPHANUM_REGEX, ".")
name = name.replace(REPEATING_DOTS, ".").trim('.')
return name
}
private class ModuleNameVisitor : ClassVisitor(ASM_API) {
var moduleName: String? = null
override fun visitModule(name: String?, access: Int, version: String?): ModuleVisitor? {
moduleName = name
return null
}
}
private val MULTI_RELEASE_JAR_VERSIONS: List = Opcodes::class.java.fields.asSequence()
.filter { it.type == Int::class.java }
.filter { it.name.startsWith("V") && !it.name.contains('_') }
.map(Field::getName)
.mapNotNull { it.substring(1).toIntOrNull() }
.toList()
.sorted()
val MODULE_INFO_PATHS: List = listOf("module-info.class") + MULTI_RELEASE_JAR_VERSIONS.map { "META-INF/versions/$it/module-info.class" }
private const val MANIFEST_PATH = "META-INF/MANIFEST.MF"
const val MODULE_NAME_MANIFEST_ATTRIBUTE = "Automatic-Module-Name"