name.remal.gradleplugins.toolkit.buildlogic.cross-compile.gradle Maven / Gradle / Ivy
import static org.gradle.api.attributes.Category.CATEGORY_ATTRIBUTE
import static org.gradle.api.attributes.Category.DOCUMENTATION
import static org.gradle.api.attributes.Category.ENFORCED_PLATFORM
import static org.gradle.api.attributes.Category.REGULAR_PLATFORM
import static org.gradle.api.attributes.Category.VERIFICATION
import java.util.regex.Matcher
import java.util.regex.Pattern
import org.gradle.util.GradleVersion
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.tree.AnnotationNode
import org.objectweb.asm.tree.ClassNode
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
buildscript {
dependencies {
classpath platform("org.ow2.asm:asm-bom:9.4")
classpath 'org.ow2.asm:asm-tree'
}
repositories {
mavenCentral()
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
allprojects {
Collection crossCompileProjects = project.subprojects.findAll { it.name.startsWith('cross-compile--') }
crossCompileProjects.forEach { crossCompileProject ->
if (!crossCompileProject.subprojects.isEmpty()) {
throw new GradleException("Cross-compile project ${crossCompileProject} can't have subprojects")
}
}
crossCompileProjects.forEach { crossCompileProject ->
; [
'maven-publish',
].forEach { forbiddenPluginId ->
crossCompileProject.pluginManager.withPlugin(forbiddenPluginId) { appliedPlugin ->
throw new GradleException("${appliedPlugin.id} plugin can't applied for cross-compile project ${crossCompileProject}")
}
}
}
pluginManager.withPlugin('java') {
crossCompileProjects.forEach { crossCompileProject ->
Pattern pattern = Pattern.compile(/^cross-compile--(.+)-(common|(\d(?:\.\d)*)-(lt|lte|eq|ne|gte|gt))$/)
Matcher matcher = pattern.matcher(crossCompileProject.name)
if (!matcher.matches()) {
throw new GradleException("Project name of cross-compile project ${crossCompileProject} doesn't match to /${pattern}/")
}
crossCompileProject.apply plugin: 'java-library'
crossCompileProject.apply plugin: 'name.remal.test-source-sets'
tasks.named('classes') { dependsOn(project.provider { crossCompileProject.tasks.classes }) }
tasks.named('jar') { filesMatching('**/package-info.class') { duplicatesStrategy(DuplicatesStrategy.EXCLUDE) } }
tasks.matching { it.name == 'sourcesJar' }.configureEach { filesMatching('**/package-info.java') { duplicatesStrategy(DuplicatesStrategy.EXCLUDE) } }
afterEvaluate {
sourceSets.main.allSource.srcDirs(project.provider { crossCompileProject.sourceSets.main.allSource.srcDirs })
sourceSets.main.output.dir(project.provider { crossCompileProject.sourceSets.main.output })
}
; [
'classes',
'javadocJar',
'sourcesJar',
].forEach { String taskName ->
tasks.matching { it.name == taskName }.all {
dependsOn(project.provider { crossCompileProject.tasks.matching { it.name == taskName } })
}
}
crossCompileProject.tasks.withType(Javadoc).configureEach { enabled = false }
tasks.withType(Javadoc).configureEach { Javadoc task ->
task.source(
project.provider {
crossCompileProject.tasks.withType(Javadoc)
.collect { it.source }
}
)
}
; [
'api',
'implementation',
].forEach { confName ->
crossCompileProject.configurations.getByName(confName).allDependencies.all { Dependency dependency ->
String category = dependency.attributes.getAttribute(CATEGORY_ATTRIBUTE)?.name
if (![REGULAR_PLATFORM, ENFORCED_PLATFORM, DOCUMENTATION, VERIFICATION].contains(category)) {
throw new GradleException("Can't add ${dependency} dependcency to cross-compile project $crossCompileProject '$confName' configuration, as this configuration can't have dependencies")
}
}
}
String dependency = matcher.group(1)
String versionSpec = matcher.group(2)
if (versionSpec == 'common') {
dependencies {
compileOnly project(crossCompileProject.path)
}
} else {
crossCompileProjects.find { it.name == "cross-compile--$dependency-common" }?.with { commonProject ->
crossCompileProject.dependencies {
compileOnly project(commonProject.path)
}
}
String version = matcher.group(3)
String versionExtender = matcher.group(4)
if (dependency == 'java') {
if (!version.matches(/^\d+$/)) throw new GradleException("Invalid Java version for cross-compile project $crossCompileProject (only major versions are supported): $version")
int majorVersion = Integer.parseInt(version)
JavaVersion javaVersion = JavaVersion.toVersion(majorVersion)
crossCompileProject.sourceCompatibility = crossCompileProject.targetCompatibility = javaVersion.toString()
} else if (dependency == 'gradle') {
GradleVersion gradleVersion = GradleVersion.version(version)
for (GradleVersion currentGradleVersion : project.getAllGradleVersions()) {
if (currentGradleVersion.version == version
|| currentGradleVersion.version.startsWith("${version}.")
|| currentGradleVersion.version.startsWith("${version}-")
) {
gradleVersion = currentGradleVersion
break
}
}
crossCompileProject.configurations.matching { it.name != 'optionalHidden' }.all { Configuration conf ->
conf.dependencies.all { Dependency dep ->
if (dep.group == 'name.remal.gradle-api') {
conf.dependencies.remove(dep)
}
}
}
crossCompileProject.dependencies {
optionalHidden 'name.remal.gradle-api:gradle-api'
optionalHidden 'name.remal.gradle-api:gradle-test-kit'
constraints {
projectDependencyConstraints "name.remal.gradle-api:local-groovy:${gradleVersion.version}"
projectDependencyConstraints "name.remal.gradle-api:gradle-api:${gradleVersion.version}"
projectDependencyConstraints "name.remal.gradle-api:gradle-test-kit:${gradleVersion.version}"
}
}
}
crossCompileProject.tasks.withType(AbstractCompile).configureEach { AbstractCompile task ->
doLast {
task.destinationDirectory.asFileTree
.matching { include('**/*.class') }
.matching { exclude('module-info.class') }
.visit { FileVisitDetails details ->
if (details.directory) return
File file = details.file
ClassReader classReader = new ClassReader(file.bytes)
ClassNode classNode = new ClassNode()
classReader.accept(classNode, 0)
List annotations = classNode.invisibleAnnotations ?: []
if (annotations.any { it.desc.endsWith('/RemalGradlePluginsCrossCompilation;') }) return
AnnotationNode annotation = new AnnotationNode('Lname/remal/gradleplugins/toolkit/internal/RemalGradlePluginsCrossCompilation;')
annotation.values = [
'dependency', dependency,
'version', version,
'versionExtender', versionExtender ?: '',
]
annotations.add(annotation)
classNode.invisibleAnnotations = annotations
ClassWriter classWriter = new ClassWriter(classReader, 0)
classNode.accept(classWriter)
byte[] bytecode = classWriter.toByteArray()
file.bytes = bytecode
}
}
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy