mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-31 05:33:00 +00:00 
			
		
		
		
	Move some more build logic to buildSrc
Look, I don't enjoy having 600 LOC long build.gradle files, it's just very easy to do! This at least moves some of the complexity elsewhere, so the build script is a little more declarative.
This commit is contained in:
		| @@ -4,11 +4,68 @@ import com.diffplug.spotless.LineEnding | ||||
| import java.nio.charset.StandardCharsets | ||||
|  | ||||
| plugins { | ||||
|     java | ||||
|     `java-library` | ||||
|     jacoco | ||||
|     checkstyle | ||||
|     id("com.diffplug.spotless") | ||||
| } | ||||
|  | ||||
| java { | ||||
|     toolchain { | ||||
|         languageVersion.set(JavaLanguageVersion.of(8)) | ||||
|     } | ||||
|  | ||||
|     withSourcesJar() | ||||
|     withJavadocJar() | ||||
| } | ||||
|  | ||||
| repositories { | ||||
|     mavenCentral() | ||||
|     maven("https://squiddev.cc/maven") { | ||||
|         name = "SquidDev" | ||||
|         content { | ||||
|             includeGroup("org.squiddev") | ||||
|             includeGroup("cc.tweaked") | ||||
|             // Things we mirror | ||||
|             includeGroup("com.blamejared.crafttweaker") | ||||
|             includeGroup("commoble.morered") | ||||
|             includeGroup("maven.modrinth") | ||||
|             includeGroup("mezz.jei") | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
|     val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs") | ||||
|     checkstyle(libs.findLibrary("checkstyle").get()) | ||||
| } | ||||
|  | ||||
| // Configure default JavaCompile tasks with our arguments. | ||||
| sourceSets.all { | ||||
|     tasks.named(compileJavaTaskName, JavaCompile::class.java) { | ||||
|         // Processing just gives us "No processor claimed any of these annotations", so skip that! | ||||
|         options.compilerArgs.addAll(listOf("-Xlint", "-Xlint:-processing")) | ||||
|     } | ||||
| } | ||||
|  | ||||
| tasks.withType(JavaCompile::class.java).configureEach { | ||||
|     options.encoding = "UTF-8" | ||||
| } | ||||
|  | ||||
| tasks.test { | ||||
|     finalizedBy("jacocoTestReport") | ||||
|  | ||||
|     useJUnitPlatform() | ||||
|     testLogging { | ||||
|         events("skipped", "failed") | ||||
|     } | ||||
| } | ||||
|  | ||||
| tasks.withType(JacocoReport::class.java).configureEach { | ||||
|     reports.xml.required.set(true) | ||||
|     reports.html.required.set(true) | ||||
| } | ||||
|  | ||||
| spotless { | ||||
|     encoding = StandardCharsets.UTF_8 | ||||
|     lineEndings = LineEnding.UNIX | ||||
|   | ||||
							
								
								
									
										124
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/CCTweakedExtension.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/CCTweakedExtension.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| package cc.tweaked.gradle | ||||
| 
 | ||||
| import org.gradle.api.NamedDomainObjectProvider | ||||
| import org.gradle.api.Project | ||||
| import org.gradle.api.attributes.TestSuiteType | ||||
| import org.gradle.api.file.FileSystemOperations | ||||
| import org.gradle.api.provider.Provider | ||||
| import org.gradle.api.reporting.ReportingExtension | ||||
| import org.gradle.api.tasks.JavaExec | ||||
| import org.gradle.api.tasks.SourceSetContainer | ||||
| import org.gradle.configurationcache.extensions.capitalized | ||||
| import org.gradle.kotlin.dsl.get | ||||
| import org.gradle.language.base.plugins.LifecycleBasePlugin | ||||
| import org.gradle.testing.jacoco.plugins.JacocoCoverageReport | ||||
| import org.gradle.testing.jacoco.plugins.JacocoPluginExtension | ||||
| import org.gradle.testing.jacoco.plugins.JacocoTaskExtension | ||||
| import org.gradle.testing.jacoco.tasks.JacocoReport | ||||
| import java.io.BufferedWriter | ||||
| import java.io.IOException | ||||
| import java.io.OutputStreamWriter | ||||
| import java.util.regex.Pattern | ||||
| 
 | ||||
| abstract class CCTweakedExtension( | ||||
|     private val project: Project, | ||||
|     private val fs: FileSystemOperations, | ||||
| ) { | ||||
|     /** Get the hash of the latest git commit. */ | ||||
|     val gitHash: Provider<String> = gitProvider(project) { | ||||
|         ProcessHelpers.captureOut("git", "-C", project.projectDir.absolutePath, "rev-parse", "HEAD") | ||||
|     } | ||||
| 
 | ||||
|     /** Get the current git branch. */ | ||||
|     val gitBranch: Provider<String> = gitProvider(project) { | ||||
|         ProcessHelpers.captureOut("git", "-C", project.projectDir.absolutePath, "rev-parse", "--abbrev-ref", "HEAD") | ||||
|     } | ||||
| 
 | ||||
|     /** Get a list of all contributors to the project. */ | ||||
|     val gitContributors: Provider<List<String>> = gitProvider(project) { | ||||
|         val authors: Set<String> = HashSet( | ||||
|             ProcessHelpers.captureLines( | ||||
|                 "git", "-C", project.projectDir.absolutePath, "log", | ||||
|                 "--format=tformat:%an <%ae>%n%cn <%ce>%n%(trailers:key=Co-authored-by,valueonly)", | ||||
|             ), | ||||
|         ) | ||||
|         val process = ProcessHelpers.startProcess("git", "check-mailmap", "--stdin") | ||||
|         BufferedWriter(OutputStreamWriter(process.outputStream)).use { writer -> | ||||
|             for (authorName in authors) { | ||||
|                 var author = authorName | ||||
| 
 | ||||
|                 if (author.isEmpty()) continue | ||||
|                 if (!author.endsWith(">")) author += ">" // Some commits have broken Co-Authored-By lines! | ||||
|                 writer.write(author) | ||||
|                 writer.newLine() | ||||
|             } | ||||
|         } | ||||
|         val contributors: MutableSet<String> = HashSet() | ||||
|         for (authorLine in ProcessHelpers.captureLines(process)) { | ||||
|             val matcher = EMAIL.matcher(authorLine) | ||||
|             matcher.find() | ||||
|             val name = matcher.group(1) | ||||
|             if (!IGNORED_USERS.contains(name)) contributors.add(name) | ||||
|         } | ||||
| 
 | ||||
|         contributors.sortedWith(String.CASE_INSENSITIVE_ORDER) | ||||
|     } | ||||
| 
 | ||||
|     fun jacoco(task: NamedDomainObjectProvider<JavaExec>) { | ||||
|         val classDump = project.buildDir.resolve("jacocoClassDump/${task.name}") | ||||
|         val reportTaskName = "jacoco${task.name.capitalized()}Report" | ||||
| 
 | ||||
|         val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java) | ||||
|         task.configure { | ||||
|             finalizedBy(reportTaskName) | ||||
| 
 | ||||
|             doFirst("Clean class dump directory") { fs.delete { delete(classDump) } } | ||||
| 
 | ||||
|             jacoco.applyTo(this) | ||||
|             extensions.configure(JacocoTaskExtension::class.java) { | ||||
|                 includes = listOf("dan200.computercraft.*") | ||||
|                 classDumpDir = classDump | ||||
| 
 | ||||
|                 // Older versions of modlauncher don't include a protection domain (and thus no code | ||||
|                 // source). Jacoco skips such classes by default, so we need to explicitly include them. | ||||
|                 isIncludeNoLocationClasses = true | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         project.tasks.register(reportTaskName, JacocoReport::class.java) { | ||||
|             group = LifecycleBasePlugin.VERIFICATION_GROUP | ||||
|             description = "Generates code coverage report for the ${task.name} task." | ||||
| 
 | ||||
|             executionData(task.get()) | ||||
|             classDirectories.from(classDump) | ||||
| 
 | ||||
|             // Don't want to use sourceSets(...) here as we have a custom class directory. | ||||
|             val sourceSets = project.extensions.getByType(SourceSetContainer::class.java) | ||||
|             sourceDirectories.from(sourceSets["main"].allSource.sourceDirectories) | ||||
|         } | ||||
| 
 | ||||
|         project.extensions.configure(ReportingExtension::class.java) { | ||||
|             reports.register("${task.name}CodeCoverageReport", JacocoCoverageReport::class.java) { | ||||
|                 testType.set(TestSuiteType.INTEGRATION_TEST) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         private val EMAIL = Pattern.compile("^([^<]+) <.+>$") | ||||
|         private val IGNORED_USERS = setOf( | ||||
|             "GitHub", "Daniel Ratcliffe", "Weblate", | ||||
|         ) | ||||
| 
 | ||||
|         private fun <T> gitProvider(project: Project, supplier: () -> T): Provider<T> { | ||||
|             return project.provider { | ||||
|                 try { | ||||
|                     supplier() | ||||
|                 } catch (e: IOException) { | ||||
|                     project.logger.error("Cannot read Git Repository", e) | ||||
|                     null | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,13 @@ | ||||
| package cc.tweaked.gradle | ||||
| 
 | ||||
| import org.gradle.api.Plugin | ||||
| import org.gradle.api.Project | ||||
| 
 | ||||
| /** | ||||
|  * Configures projects to match a shared configuration. | ||||
|  */ | ||||
| class CCTweakedPlugin : Plugin<Project> { | ||||
|     override fun apply(project: Project) { | ||||
|         project.extensions.create("cct", CCTweakedExtension::class.java) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										65
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/CheckChangelog.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/CheckChangelog.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| package cc.tweaked.gradle | ||||
| 
 | ||||
| import org.gradle.api.DefaultTask | ||||
| import org.gradle.api.GradleException | ||||
| import org.gradle.api.file.RegularFileProperty | ||||
| import org.gradle.api.provider.Property | ||||
| import org.gradle.api.tasks.* | ||||
| import org.gradle.language.base.plugins.LifecycleBasePlugin | ||||
| import java.nio.charset.StandardCharsets | ||||
| 
 | ||||
| /** | ||||
|  * Checks the `changelog.md` and `whatsnew.md` files are well-formed. | ||||
|  */ | ||||
| @CacheableTask | ||||
| abstract class CheckChangelog : DefaultTask() { | ||||
|     init { | ||||
|         group = LifecycleBasePlugin.VERIFICATION_GROUP | ||||
|         description = "Verifies the changelog and whatsnew file are consistent." | ||||
|     } | ||||
| 
 | ||||
|     @get:Input | ||||
|     abstract val version: Property<String> | ||||
| 
 | ||||
|     @get:InputFile | ||||
|     @get:PathSensitive(PathSensitivity.NONE) | ||||
|     abstract val changelog: RegularFileProperty | ||||
| 
 | ||||
|     @get:InputFile | ||||
|     @get:PathSensitive(PathSensitivity.NONE) | ||||
|     abstract val whatsNew: RegularFileProperty | ||||
| 
 | ||||
|     @TaskAction | ||||
|     fun check() { | ||||
|         val version = version.get() | ||||
| 
 | ||||
|         var ok = true | ||||
| 
 | ||||
|         // Check we're targetting the current version | ||||
|         var whatsNew = whatsNew.get().asFile.readLines() | ||||
|         if (whatsNew[0] != "New features in CC: Tweaked $version") { | ||||
|             ok = false | ||||
|             logger.error("Expected `whatsnew.md' to target $version.") | ||||
|         } | ||||
| 
 | ||||
|         // Check "read more" exists and trim it | ||||
|         val idx = whatsNew.indexOfFirst { it == "Type \"help changelog\" to see the full version history." } | ||||
|         if (idx == -1) { | ||||
|             ok = false | ||||
|             logger.error("Must mention the changelog in whatsnew.md") | ||||
|         } else { | ||||
|             whatsNew = whatsNew.slice(0 until idx) | ||||
|         } | ||||
| 
 | ||||
|         // Check whatsnew and changelog match. | ||||
|         val expectedChangelog = sequenceOf("# ${whatsNew[0]}") + whatsNew.slice(1 until whatsNew.size).asSequence() | ||||
|         val changelog = changelog.get().asFile.readLines() | ||||
|         val mismatch = expectedChangelog.zip(changelog.asSequence()).filter { (a, b) -> a != b }.firstOrNull() | ||||
|         if (mismatch != null) { | ||||
|             ok = false | ||||
|             logger.error("whatsnew and changelog are not in sync") | ||||
|         } | ||||
| 
 | ||||
|         if (!ok) throw GradleException("Could not check release") | ||||
|     } | ||||
| } | ||||
| @@ -25,7 +25,7 @@ object LicenseHeader { | ||||
|     ) | ||||
| 
 | ||||
|     private fun getTemplateText(file: File): String = | ||||
|         file.readText(StandardCharsets.UTF_8).trim().replace("\${year}", "$YEAR") | ||||
|         file.readText().trim().replace("\${year}", "$YEAR") | ||||
| 
 | ||||
|     private fun formatFile(licenses: Licenses, contents: String, file: File): String { | ||||
|         val license = getLicense(contents) | ||||
| @@ -38,8 +38,8 @@ object LicenseHeader { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun getExpectedLicense(licenses: Licenses, file: File): String { | ||||
|         var file: File? = file | ||||
|     private fun getExpectedLicense(licenses: Licenses, root: File): String { | ||||
|         var file: File? = root | ||||
|         while (file != null) { | ||||
|             if (file.name == "api" && file.parentFile?.name == "computercraft") return licenses.api | ||||
|             file = file.parentFile | ||||
|   | ||||
							
								
								
									
										34
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| package cc.tweaked.gradle | ||||
| 
 | ||||
| import org.codehaus.groovy.runtime.ProcessGroovyMethods | ||||
| import java.io.BufferedReader | ||||
| import java.io.IOException | ||||
| import java.io.InputStreamReader | ||||
| 
 | ||||
| internal object ProcessHelpers { | ||||
|     fun startProcess(vararg command: String): Process { | ||||
|         // Something randomly passes in "GIT_DIR=" as an environment variable which clobbers everything else. Don't | ||||
|         // inherit the environment array! | ||||
|         return Runtime.getRuntime().exec(command, arrayOfNulls(0)) | ||||
|     } | ||||
| 
 | ||||
|     fun captureOut(vararg command: String): String { | ||||
|         val process = startProcess(*command) | ||||
|         val result = ProcessGroovyMethods.getText(process) | ||||
|         if (process.waitFor() != 0) throw IOException("Command exited with a non-0 status") | ||||
|         return result | ||||
|     } | ||||
| 
 | ||||
|     fun captureLines(vararg command: String): List<String> { | ||||
|         return captureLines(startProcess(*command)) | ||||
|     } | ||||
| 
 | ||||
|     fun captureLines(process: Process): List<String> { | ||||
|         val out = BufferedReader(InputStreamReader(process.inputStream)).use { reader -> | ||||
|             reader.lines().filter { it.isNotEmpty() }.toList() | ||||
|         } | ||||
|         ProcessGroovyMethods.closeStreams(process) | ||||
|         if (process.waitFor() != 0) throw IOException("Command exited with a non-0 status") | ||||
|         return out | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Jonathan Coates
					Jonathan Coates