mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-11-29 02:55:13 +00:00
- Update to Loom 1.12. This requires Java 21, so we now build with Java 21, but targetting 17. The new "-release" flag does make this much easier, so hopefully shouldn't cause too many issues. - Bump versions of a lot of other things. - Fix various errorprone/checkstyle grumbles.
255 lines
11 KiB
Kotlin
255 lines
11 KiB
Kotlin
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
|
//
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package cc.tweaked.gradle
|
|
|
|
import net.ltgt.gradle.errorprone.CheckSeverity
|
|
import net.ltgt.gradle.errorprone.errorprone
|
|
import org.gradle.api.GradleException
|
|
import org.gradle.api.NamedDomainObjectProvider
|
|
import org.gradle.api.Project
|
|
import org.gradle.api.Task
|
|
import org.gradle.api.plugins.JavaPluginExtension
|
|
import org.gradle.api.provider.Provider
|
|
import org.gradle.api.provider.SetProperty
|
|
import org.gradle.api.tasks.SourceSet
|
|
import org.gradle.api.tasks.bundling.Jar
|
|
import org.gradle.api.tasks.compile.JavaCompile
|
|
import org.gradle.api.tasks.javadoc.Javadoc
|
|
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
|
import org.gradle.language.jvm.tasks.ProcessResources
|
|
import org.gradle.process.JavaForkOptions
|
|
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
|
|
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
|
|
import org.gradle.testing.jacoco.tasks.JacocoReport
|
|
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
|
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|
import java.io.File
|
|
import java.io.IOException
|
|
import java.net.URI
|
|
import java.util.regex.Pattern
|
|
|
|
abstract class CCTweakedExtension(private val project: Project) {
|
|
/** Get the hash of the latest git commit. */
|
|
val gitHash: Provider<String> =
|
|
gitProvider("<no git commit>", listOf("rev-parse", "HEAD")) { it.trim() }
|
|
|
|
/** Get the current git branch. */
|
|
val gitBranch: Provider<String> =
|
|
gitProvider("<no git branch>", listOf("rev-parse", "--abbrev-ref", "HEAD")) { it.trim() }
|
|
|
|
/** Get a list of all contributors to the project. */
|
|
val gitContributors: Provider<List<String>> =
|
|
gitProvider(listOf(), listOf("shortlog", "-ns", "--group=author", "--group=trailer:co-authored-by", "HEAD")) { input ->
|
|
input.lineSequence()
|
|
.filter { it.isNotEmpty() }
|
|
.map {
|
|
val matcher = COMMIT_COUNTS.matcher(it)
|
|
matcher.find()
|
|
matcher.group(1)
|
|
}
|
|
.filter { !IGNORED_USERS.contains(it) }
|
|
.toList()
|
|
.sortedWith(String.CASE_INSENSITIVE_ORDER)
|
|
}
|
|
|
|
/**
|
|
* References to other sources
|
|
*/
|
|
val sourceDirectories: SetProperty<SourceSetReference> = project.objects.setProperty(SourceSetReference::class.java)
|
|
|
|
/** All source sets referenced by this project. */
|
|
val sourceSets = sourceDirectories.map { x -> x.map { it.sourceSet } }
|
|
|
|
init {
|
|
sourceDirectories.finalizeValueOnRead()
|
|
project.afterEvaluate { sourceDirectories.disallowChanges() }
|
|
}
|
|
|
|
/**
|
|
* Mark this project as consuming another project. Its [sourceDirectories] are added, allowing easier configuration
|
|
* of run configurations and other tasks which consume sources/classes.
|
|
*/
|
|
fun externalSources(project: Project) {
|
|
val otherCct = project.extensions.getByType(CCTweakedExtension::class.java)
|
|
for (sourceSet in otherCct.sourceDirectories.get()) {
|
|
sourceDirectories.add(SourceSetReference(sourceSet.sourceSet, classes = sourceSet.classes, external = true))
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a dependency on another project such that its sources and compiles are processed with this one.
|
|
*
|
|
* This is used when importing a common library into a loader-specific one, as we want to compile sources using
|
|
* the loader-specific sources.
|
|
*/
|
|
fun inlineProject(path: String) {
|
|
val otherProject = project.evaluationDependsOn(path)
|
|
val otherJava = otherProject.extensions.getByType(JavaPluginExtension::class.java)
|
|
val main = otherJava.sourceSets.getByName("main")
|
|
val client = otherJava.sourceSets.getByName("client")
|
|
|
|
// Pull in sources from the other project.
|
|
extendSourceSet(otherProject, main)
|
|
extendSourceSet(otherProject, client)
|
|
for (sourceSet in listOf(MinecraftConfigurations.DATAGEN, MinecraftConfigurations.EXAMPLES, MinecraftConfigurations.TEST_MOD, "testFixtures")) {
|
|
otherJava.sourceSets.findByName(sourceSet)?.let { extendSourceSet(otherProject, it) }
|
|
}
|
|
|
|
// The extra source-processing tasks should include these files too.
|
|
project.tasks.named(main.javadocTaskName, Javadoc::class.java) { source(main.allJava, client.allJava) }
|
|
project.tasks.named(main.sourcesJarTaskName, Jar::class.java) { from(main.allSource, client.allSource) }
|
|
sourceDirectories.addAll(SourceSetReference.inline(main), SourceSetReference.inline(client))
|
|
}
|
|
|
|
/**
|
|
* Extend a source set with files from another project.
|
|
*
|
|
* This actually extends the original compile tasks, as extending the source sets does not play well with IDEs.
|
|
*/
|
|
private fun extendSourceSet(otherProject: Project, sourceSet: SourceSet) {
|
|
project.tasks.named(sourceSet.compileJavaTaskName, JavaCompile::class.java) {
|
|
dependsOn(otherProject.tasks.named(sourceSet.compileJavaTaskName)) // Avoid duplicate compile errors
|
|
source(sourceSet.allJava)
|
|
}
|
|
|
|
project.tasks.named(sourceSet.processResourcesTaskName, ProcessResources::class.java) {
|
|
from(sourceSet.resources)
|
|
}
|
|
|
|
// Also try to depend on Kotlin if it exists
|
|
val kotlin = otherProject.extensions.findByType(KotlinProjectExtension::class.java)
|
|
if (kotlin != null) {
|
|
val compileKotlin = sourceSet.getCompileTaskName("kotlin")
|
|
project.tasks.named(compileKotlin, KotlinCompile::class.java) {
|
|
dependsOn(otherProject.tasks.named(compileKotlin))
|
|
source(kotlin.sourceSets.getByName(sourceSet.name).kotlin)
|
|
}
|
|
}
|
|
|
|
// If we're doing an IDE sync, add a fake dependency to ensure it's on the classpath.
|
|
if (isIdeSync) project.dependencies.add(sourceSet.apiConfigurationName, sourceSet.output)
|
|
}
|
|
|
|
fun linters(@Suppress("UNUSED_PARAMETER") vararg unused: UseNamedArgs, minecraft: Boolean, loader: String?) {
|
|
val java = project.extensions.getByType(JavaPluginExtension::class.java)
|
|
val sourceSets = java.sourceSets
|
|
|
|
project.dependencies.run { add("errorprone", project(mapOf("path" to ":lints"))) }
|
|
sourceSets.all {
|
|
val name = name
|
|
project.tasks.named(compileJavaTaskName, JavaCompile::class.java) {
|
|
options.errorprone {
|
|
// Only the main source set should run the side checker
|
|
check("SideChecker", if (minecraft && name == "main") CheckSeverity.DEFAULT else CheckSeverity.OFF)
|
|
|
|
// The MissingLoaderOverride check superseeds the MissingOverride one, so disable that.
|
|
if (loader != null) {
|
|
check("MissingOverride", CheckSeverity.OFF)
|
|
option("ModLoader", loader)
|
|
} else {
|
|
check("LoaderOverride", CheckSeverity.OFF)
|
|
check("MissingLoaderOverride", CheckSeverity.OFF)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
|
|
val reportTaskName = "jacoco${task.name.capitalise()}Report"
|
|
|
|
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
|
|
task.configure {
|
|
finalizedBy(reportTaskName)
|
|
jacoco.applyTo(this)
|
|
|
|
extensions.configure(JacocoTaskExtension::class.java) {
|
|
includes = listOf("dan200.computercraft.*")
|
|
excludes = listOf(
|
|
"dan200.computercraft.mixin.*", // Exclude mixins, as they're not executed at runtime.
|
|
"dan200.computercraft.shared.Capabilities$*", // Exclude capability tokens, as Forge rewrites them.
|
|
)
|
|
}
|
|
}
|
|
|
|
project.tasks.register(reportTaskName, JacocoReport::class.java) {
|
|
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
|
description = "Generates code coverage report for the ${task.name} task."
|
|
|
|
executionData(task.get())
|
|
|
|
// Don't want to use sourceSets(...) here as we don't use all class directories.
|
|
for (ref in this@CCTweakedExtension.sourceDirectories.get()) {
|
|
sourceDirectories.from(ref.sourceSet.allSource.sourceDirectories)
|
|
if (ref.classes) classDirectories.from(ref.sourceSet.output)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Download a file by creating a dummy Ivy repository.
|
|
*
|
|
* This should only be used for one-off downloads. Using a more conventional Ivy or Maven repository is preferred
|
|
* where possible.
|
|
*/
|
|
fun downloadFile(label: String, url: String): File {
|
|
val uri = URI(url)
|
|
val path = File(uri.path)
|
|
|
|
project.repositories.ivy {
|
|
name = label
|
|
setUrl(URI(uri.scheme, uri.userInfo, uri.host, uri.port, path.parent, null, null))
|
|
patternLayout {
|
|
artifact("[artifact].[ext]")
|
|
}
|
|
metadataSources {
|
|
artifact()
|
|
}
|
|
content {
|
|
includeModule("cc.tweaked.internal", path.nameWithoutExtension)
|
|
}
|
|
}
|
|
|
|
return project.configurations.detachedConfiguration(
|
|
project.dependencies.create(
|
|
mapOf(
|
|
"group" to "cc.tweaked.internal",
|
|
"name" to path.nameWithoutExtension,
|
|
"ext" to path.extension,
|
|
),
|
|
),
|
|
).resolve().single()
|
|
}
|
|
|
|
private fun <T: Any> gitProvider(default: T, command: List<String>, process: (String) -> T): Provider<T> {
|
|
val baseResult = project.providers.exec {
|
|
commandLine = listOf("git", "-C", project.rootDir.absolutePath) + command
|
|
}
|
|
|
|
return project.provider {
|
|
val res = try {
|
|
baseResult.standardOutput.asText.get()
|
|
} catch (e: IOException) {
|
|
project.logger.error("Cannot read Git repository: ${e.message}", e)
|
|
return@provider default
|
|
} catch (e: GradleException) {
|
|
project.logger.error("Cannot read Git repository: ${e.message}", e)
|
|
return@provider default
|
|
}
|
|
process(res)
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
private val COMMIT_COUNTS = Pattern.compile("""^\s*[0-9]+\s+(.*)$""")
|
|
private val IGNORED_USERS = setOf(
|
|
"GitHub", "Daniel Ratcliffe", "NotSquidDev", "Weblate",
|
|
)
|
|
|
|
private val isIdeSync: Boolean
|
|
get() = java.lang.Boolean.parseBoolean(System.getProperty("idea.sync.active", "false"))
|
|
}
|
|
}
|