plugins { id "checkstyle" id "jacoco" id "maven-publish" id "org.cadixdev.licenser" version "0.6.1" id "com.matthewprenger.cursegradle" version "1.4.0" id "com.github.breadmoirai.github-release" version "2.2.12" id "org.jetbrains.kotlin.jvm" version "1.7.0" id "com.modrinth.minotaur" version "2.+" id "net.minecraftforge.gradle" version "5.1.+" id "org.spongepowered.mixin" version "0.7.+" id "org.parchmentmc.librarian.forgegradle" version "1.+" id "com.github.johnrengelman.shadow" version "7.1.2" } import org.apache.tools.ant.taskdefs.condition.Os version = mod_version group = "org.squiddev" archivesBaseName = "cc-tweaked-${mc_version}" def javaVersion = JavaLanguageVersion.of(8) java { toolchain { languageVersion = javaVersion } withSourcesJar() withJavadocJar() registerFeature("extraMods") { usingSourceSet(sourceSets.main) } } sourceSets { main.resources { srcDir 'src/generated/resources' } testMod {} } minecraft { runs { all { property 'forge.logging.markers', 'REGISTRIES' property 'forge.logging.console.level', 'debug' forceExit = false mods { computercraft { source sourceSets.main } } arg "-mixin.config=computercraft.mixins.json" } client { workingDirectory project.file('run') } server { workingDirectory project.file("run/server") arg "--nogui" } data { workingDirectory project.file('run') args '--mod', 'computercraft', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') } testClient { workingDirectory project.file('test-files/client') parent runs.client mods { cctest { source sourceSets.testMod } } } testServer { workingDirectory project.file('test-files/server') parent runs.server property("cctest.run", "true") property("forge.logging.console.level", "info") mods { cctest { source sourceSets.testMod } } } } mappings channel: 'parchment', version: "${mapping_version}-${mc_version}" accessTransformer file('src/main/resources/META-INF/accesstransformer.cfg') accessTransformer file('src/testMod/resources/META-INF/accesstransformer.cfg') } mixin { add sourceSets.main, 'computercraft.mixins.refmap.json' } reobf { shadowJar {} } repositories { mavenCentral() maven { name "SquidDev" url "https://squiddev.cc/maven" } } configurations { shade { transitive = false } implementation.extendsFrom shade cctJavadoc testModImplementation.extendsFrom(implementation) testModImplementation.extendsFrom(testImplementation) } dependencies { checkstyle "com.puppycrawl.tools:checkstyle:8.25" minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}" annotationProcessor 'org.spongepowered:mixin:0.8.4:processor' extraModsCompileOnly fg.deobf("mezz.jei:jei-1.16.5:7.7.0.104:api") extraModsRuntimeOnly fg.deobf("mezz.jei:jei-1.16.5:7.7.0.104") extraModsCompileOnly fg.deobf("com.blamejared.crafttweaker:CraftTweaker-1.16.5:7.1.0.313") extraModsCompileOnly fg.deobf("commoble.morered:morered-1.16.5:2.1.1.0") shade 'org.squiddev:Cobalt:0.5.5' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.0' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0' testImplementation 'org.hamcrest:hamcrest:2.2' testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0' testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.2' testModImplementation sourceSets.main.output cctJavadoc 'cc.tweaked:cct-javadoc:1.4.7' } // Compile tasks javadoc { include "dan200/computercraft/api/**/*.java" } def apiJar = tasks.register("apiJar", Jar.class) { archiveClassifier.set("api") from(sourceSets.main.output) { include "dan200/computercraft/api/**/*" } } assemble.dependsOn(apiJar) def luaJavadoc = tasks.register("luaJavadoc", Javadoc.class) { description "Generates documentation for Java-side Lua functions." group "documentation" source = sourceSets.main.allJava destinationDir = file("${project.docsDir}/luaJavadoc") classpath = sourceSets.main.compileClasspath options.docletpath = configurations.cctJavadoc.files as List options.doclet = "cc.tweaked.javadoc.LuaDoclet" options.noTimestamp = false javadocTool = javaToolchains.javadocToolFor { languageVersion = JavaLanguageVersion.of(11) } } jar { finalizedBy("reobfJar") archiveClassifier.set("slim") manifest { attributes([ "Specification-Title" : "computercraft", "Specification-Vendor" : "SquidDev", "Specification-Version" : "1", "Implementation-Title" : "CC: Tweaked", "Implementation-Version" : "${mod_version}", "Implementation-Vendor" : "SquidDev", "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), "MixinConfigs" : "computercraft.mixins.json", ]) } } shadowJar { finalizedBy("reobfShadowJar") archiveClassifier.set("") configurations = [project.configurations.shade] relocate("org.squiddev.cobalt", "cc.tweaked.internal.cobalt") minimize() } assemble.dependsOn("shadowJar") [ tasks.named("compileJava", JavaCompile.class), tasks.named("compileTestJava", JavaCompile.class), tasks.named("compileTestModJava", JavaCompile.class) ].forEach { it.configure { options.compilerArgs << "-Xlint" << "-Xlint:-processing" } } processResources { inputs.property "version", mod_version inputs.property "mcversion", mc_version def hash = 'none' Set contributors = [] try { hash = ["git", "-C", projectDir, "rev-parse", "HEAD"].execute().text.trim() def blacklist = ['GitHub', 'Daniel Ratcliffe', 'Weblate'] // Extract all authors, commiters and co-authors from the git log. def authors = ["git", "-C", projectDir, "log", "--format=tformat:%an <%ae>%n%cn <%ce>%n%(trailers:key=Co-authored-by,valueonly)"] .execute().text.readLines().unique() // We now pass this through git's mailmap to de-duplicate some authors. def remapAuthors = ["git", "check-mailmap", "--stdin"].execute() remapAuthors.withWriter { stdin -> if (stdin !instanceof BufferedWriter) stdin = new BufferedWriter(stdin) authors.forEach { if (it == "") return if (!it.endsWith(">")) it += ">" // Some commits have broken Co-Authored-By lines! stdin.writeLine(it) } stdin.close() } // And finally extract out the actual name. def emailRegex = ~/^([^<]+) <.+>$/ remapAuthors.text.readLines().forEach { def matcher = it =~ emailRegex matcher.find() def name = matcher.group(1) if (!blacklist.contains(name)) contributors.add(name) } } catch (Exception e) { e.printStackTrace() } inputs.property "commithash", hash duplicatesStrategy = DuplicatesStrategy.INCLUDE from(sourceSets.main.resources.srcDirs) { include 'META-INF/mods.toml' include 'data/computercraft/lua/rom/help/credits.txt' expand 'version': mod_version, 'mcversion': mc_version, 'gitcontributors': contributors.sort(false, String.CASE_INSENSITIVE_ORDER).join('\n') } from(sourceSets.main.resources.srcDirs) { exclude 'META-INF/mods.toml' exclude 'data/computercraft/lua/rom/help/credits.txt' } } sourcesJar { duplicatesStrategy = DuplicatesStrategy.INCLUDE } // Web tasks List mkCommand(String command) { return Os.isFamily(Os.FAMILY_WINDOWS) ? ["cmd", "/c", command] : ["sh", "-c", command] } def rollup = tasks.register("rollup", Exec.class) { group = "build" description = "Bundles JS into rollup" inputs.files(fileTree("src/web")).withPropertyName("sources") inputs.file("package-lock.json").withPropertyName("package-lock.json") inputs.file("tsconfig.json").withPropertyName("Typescript config") inputs.file("rollup.config.js").withPropertyName("Rollup config") outputs.file("$buildDir/rollup/index.js").withPropertyName("output") commandLine mkCommand('"node_modules/.bin/rollup" --config rollup.config.js') } def illuaminateDocs = tasks.register("illuaminateDocs", Exec.class) { group = "documentation" description = "Generates docs using Illuaminate" dependsOn(rollup, luaJavadoc) inputs.files(fileTree("doc")).withPropertyName("docs") inputs.files(fileTree("src/main/resources/data/computercraft/lua/rom")).withPropertyName("lua rom") inputs.file("illuaminate.sexp").withPropertyName("illuaminate.sexp") inputs.dir("$buildDir/docs/luaJavadoc") inputs.file("$buildDir/rollup/index.js").withPropertyName("scripts") inputs.file("src/web/styles.css").withPropertyName("styles") outputs.dir("$buildDir/docs/lua") commandLine mkCommand('"bin/illuaminate" doc-gen') } def jsxDocs = tasks.register("jsxDocs", Exec) { group = "documentation" description = "Post-processes documentation to statically render some dynamic content." dependsOn(illuaminateDocs) inputs.files(fileTree("src/web")).withPropertyName("sources") inputs.file("src/generated/export/index.json").withPropertyName("export") inputs.file("package-lock.json").withPropertyName("package-lock.json") inputs.file("tsconfig.json").withPropertyName("Typescript config") inputs.files(fileTree("$buildDir/docs/lua")) outputs.dir("$buildDir/docs/site") commandLine mkCommand('"node_modules/.bin/ts-node" -T --esm src/web/transform.tsx') } def docWebsite = tasks.register("docWebsite", Copy.class) { group = "documentation" description = "Copy additional assets to the website directory." dependsOn(jsxDocs) from('doc') { include 'logo.png' include 'images/**' } from("$buildDir/rollup") { exclude 'index.js' } from("$buildDir/docs/lua") { exclude '**/*.html' } from("src/generated/export/items") { into("images/items") } into "${project.docsDir}/site" } // Check tasks test { useJUnitPlatform() testLogging { events "skipped", "failed" } } jacocoTestReport { dependsOn('test') reports { xml.required = true html.required = true } } test.finalizedBy("jacocoTestReport") license { header = file('config/license/main.txt') lineEnding = '\n' newLine = false properties { year = Calendar.getInstance().get(Calendar.YEAR) } include("**/*.java") // We could apply to Kotlin, but for now let's not matching("dan200/computercraft/api/**") { header = file('config/license/api.txt') } } check.dependsOn("licenseCheck") def setupServer = tasks.register("setupServer", Copy.class) { group "test server" description "Sets up the environment for the test server." from("src/testMod/server-files") { include "eula.txt" include "server.properties" } into "test-files/server" } def testServerClassDumpDir = new File(buildDir, "jacocoClassDump/runTestServer") def testServer = tasks.register("testServer", JavaExec.class) { group("In-game tests") description("Runs tests on a temporary Minecraft instance.") dependsOn(setupServer, "cleanTestServer") finalizedBy("jacocoTestServerReport") // Copy from runTestServer. We do it in this slightly odd way as runTestServer // isn't created until the task is configured (which is no good for us). JavaExec exec = tasks.getByName("runTestServer") dependsOn(exec.getDependsOn()) exec.copyTo(it) setClasspath(exec.getClasspath()) mainClass = exec.mainClass javaLauncher = exec.javaLauncher setArgs(exec.getArgs()) // Jacoco and modlauncher don't play well together as the classes loaded in-game don't // match up with those written to disk. We get Jacoco to dump all classes to disk, and // use that when generating the report. jacoco.applyTo(it) it.jacoco.setIncludes(["dan200.computercraft.*"]) it.jacoco.setClassDumpDir(testServerClassDumpDir) outputs.dir(testServerClassDumpDir) // 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. it.jacoco.setIncludeNoLocationClasses(true) } tasks.register("jacocoTestServerReport", JacocoReport.class) { group("In-game tests") description("Generate coverage reports for testServer") dependsOn(testServer) executionData(new File(buildDir, "jacoco/testServer.exec")) sourceDirectories.from(sourceSets.main.allJava.srcDirs) classDirectories.from(testServerClassDumpDir) reports { xml.enabled true html.enabled true } } check.dependsOn(testServer) // Upload tasks def checkRelease = tasks.register("checkRelease") { group "upload" description "Verifies that everything is ready for a release" inputs.property "version", mod_version inputs.file("src/main/resources/data/computercraft/lua/rom/help/changelog.md") inputs.file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.md") doLast { def ok = true // Check we're targetting the current version def whatsnew = new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/whatsnew.md").readLines() if (whatsnew[0] != "New features in CC: Tweaked $mod_version") { ok = false project.logger.error("Expected `whatsnew.md' to target $mod_version.") } // Check "read more" exists and trim it def idx = whatsnew.findIndexOf { it == 'Type "help changelog" to see the full version history.' } if (idx == -1) { ok = false project.logger.error("Must mention the changelog in whatsnew.md") } else { whatsnew = whatsnew.getAt(0..