Files
Applied-Energistics-2/build.gradle
2024-01-09 17:47:53 +11:00

1228 lines
42 KiB
Groovy

//version: 1704659416
/*
* DO NOT CHANGE THIS FILE!
* Also, you may replace this file at any time if there is an update available.
* Please check https://github.com/GregTechCEu/Buildscripts/blob/master/build.gradle for updates.
* You can also run ./gradlew updateBuildScript to update your buildscript.
*/
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import com.gtnewhorizons.retrofuturagradle.mcp.ReobfuscatedJar
import com.modrinth.minotaur.dependencies.ModDependency
import com.modrinth.minotaur.dependencies.VersionDependency
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
import org.gradle.internal.logging.text.StyledTextOutputFactory
import org.jetbrains.gradle.ext.Gradle
import static org.gradle.internal.logging.text.StyledTextOutput.Style
plugins {
id 'java'
id 'java-library'
id 'base'
id 'eclipse'
id 'maven-publish'
id 'org.jetbrains.gradle.plugin.idea-ext' version '1.1.7'
id 'com.gtnewhorizons.retrofuturagradle' version '1.3.25'
id 'net.darkhax.curseforgegradle' version '1.1.17' apply false
id 'com.modrinth.minotaur' version '2.8.6' apply false
id 'com.diffplug.spotless' version '6.13.0' apply false
id 'com.palantir.git-version' version '3.0.0' apply false
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
id 'org.jetbrains.kotlin.jvm' version '1.8.0' apply false
id 'org.jetbrains.kotlin.kapt' version '1.8.0' apply false
id 'com.google.devtools.ksp' version '1.8.0-1.0.9' apply false
}
def out = services.get(StyledTextOutputFactory).create('an-output')
// Project properties
// Required properties: we don't know how to handle these being missing gracefully
checkPropertyExists("modName")
checkPropertyExists("modId")
checkPropertyExists("modGroup")
checkPropertyExists("minecraftVersion") // hard-coding this makes it harder to immediately tell what version a mod is in (even though this only really supports 1.12.2)
checkPropertyExists("apiPackage")
checkPropertyExists("accessTransformersFile")
checkPropertyExists("usesMixins")
checkPropertyExists("mixinsPackage")
checkPropertyExists("coreModClass")
checkPropertyExists("containsMixinsAndOrCoreModOnly")
// Optional properties: we can assume some default behavior if these are missing
propertyDefaultIfUnset("modVersion", "")
propertyDefaultIfUnset("includeMCVersionJar", false)
propertyDefaultIfUnset("autoUpdateBuildScript", false)
propertyDefaultIfUnset("modArchivesBaseName", project.modId)
propertyDefaultIfUnsetWithEnvVar("developmentEnvironmentUserName", "Developer", "DEV_USERNAME")
propertyDefaultIfUnset("generateGradleTokenClass", "")
propertyDefaultIfUnset("gradleTokenModId", "")
propertyDefaultIfUnset("gradleTokenModName", "")
propertyDefaultIfUnset("gradleTokenVersion", "")
propertyDefaultIfUnset("useSrcApiPath", false)
propertyDefaultIfUnset("includeWellKnownRepositories", true)
propertyDefaultIfUnset("includeCommonDevEnvMods", true)
propertyDefaultIfUnset("noPublishedSources", false)
propertyDefaultIfUnset("forceEnableMixins", false)
propertyDefaultIfUnsetWithEnvVar("enableCoreModDebug", false, "CORE_MOD_DEBUG")
propertyDefaultIfUnset("generateMixinConfig", true)
propertyDefaultIfUnset("usesShadowedDependencies", false)
propertyDefaultIfUnset("minimizeShadowedDependencies", true)
propertyDefaultIfUnset("relocateShadowedDependencies", true)
propertyDefaultIfUnset("separateRunDirectories", false)
propertyDefaultIfUnset("versionDisplayFormat", '$MOD_NAME \u2212 $VERSION')
propertyDefaultIfUnsetWithEnvVar("modrinthProjectId", "", "MODRINTH_PROJECT_ID")
propertyDefaultIfUnset("modrinthRelations", "")
propertyDefaultIfUnsetWithEnvVar("curseForgeProjectId", "", "CURSEFORGE_PROJECT_ID")
propertyDefaultIfUnset("curseForgeRelations", "")
propertyDefaultIfUnsetWithEnvVar("releaseType", "release", "RELEASE_TYPE")
propertyDefaultIfUnset("generateDefaultChangelog", false)
propertyDefaultIfUnset("customMavenPublishUrl", "")
propertyDefaultIfUnset("mavenArtifactGroup", getDefaultArtifactGroup())
propertyDefaultIfUnset("enableModernJavaSyntax", false)
propertyDefaultIfUnset("enableSpotless", false)
propertyDefaultIfUnset("enableJUnit", false)
propertyDefaultIfUnsetWithEnvVar("deploymentDebug", false, "DEPLOYMENT_DEBUG")
// Project property assertions
final String javaSourceDir = 'src/main/java/'
final String scalaSourceDir = 'src/main/scala/'
final String kotlinSourceDir = 'src/main/kotlin/'
final String modGroupPath = modGroup.toString().replace('.' as char, '/' as char)
final String apiPackagePath = apiPackage.toString().replace('.' as char, '/' as char)
String targetPackageJava = javaSourceDir + modGroupPath
String targetPackageScala = scalaSourceDir + modGroupPath
String targetPackageKotlin = kotlinSourceDir + modGroupPath
if (!getFile(targetPackageJava).exists() && !getFile(targetPackageScala).exists() && !getFile(targetPackageKotlin).exists()) {
throw new GradleException("Could not resolve \"modGroup\"! Could not find ${targetPackageJava} or ${targetPackageScala} or ${targetPackageKotlin}")
}
if (apiPackage) {
final String endApiPath = modGroupPath + '/' + apiPackagePath
if (useSrcApiPath) {
targetPackageJava = 'src/api/java/' + endApiPath
targetPackageScala = 'src/api/scala/' + endApiPath
targetPackageKotlin = 'src/api/kotlin/' + endApiPath
} else {
targetPackageJava = javaSourceDir + endApiPath
targetPackageScala = scalaSourceDir + endApiPath
targetPackageKotlin = kotlinSourceDir + endApiPath
}
if (!getFile(targetPackageJava).exists() && !getFile(targetPackageScala).exists() && !getFile(targetPackageKotlin).exists()) {
throw new GradleException("Could not resolve \"apiPackage\"! Could not find ${targetPackageJava} or ${targetPackageScala} or ${targetPackageKotlin}")
}
}
if (accessTransformersFile) {
for (atFile in accessTransformersFile.split(",")) {
String targetFile = 'src/main/resources/' + atFile.trim()
if (!getFile(targetFile).exists()) {
throw new GradleException("Could not resolve \"accessTransformersFile\"! Could not find " + targetFile)
}
tasks.deobfuscateMergedJarToSrg.accessTransformerFiles.from(targetFile)
tasks.srgifyBinpatchedJar.accessTransformerFiles.from(targetFile)
}
}
if (usesMixins.toBoolean()) {
if (mixinsPackage.isEmpty()) {
throw new GradleException("\"usesMixins\" requires \"mixinsPackage\" to be set!")
}
final String mixinPackagePath = mixinsPackage.toString().replaceAll('\\.', '/')
targetPackageJava = javaSourceDir + modGroupPath + '/' + mixinPackagePath
targetPackageScala = scalaSourceDir + modGroupPath + '/' + mixinPackagePath
targetPackageKotlin = kotlinSourceDir + modGroupPath + '/' + mixinPackagePath
if (!getFile(targetPackageJava).exists() && !getFile(targetPackageScala).exists() && !getFile(targetPackageKotlin).exists()) {
throw new GradleException("Could not resolve \"mixinsPackage\"! Could not find ${targetPackageJava} or ${targetPackageScala} or ${targetPackageKotlin}")
}
}
if (coreModClass) {
final String coreModPath = coreModClass.toString().replaceAll('\\.', '/')
String targetFileJava = javaSourceDir + modGroupPath + '/' + coreModPath + '.java'
String targetFileScala = scalaSourceDir + modGroupPath + '/' + coreModPath + '.scala'
String targetFileScalaJava = scalaSourceDir + modGroupPath + '/' + coreModPath + '.java'
String targetFileKotlin = kotlinSourceDir + modGroupPath + '/' + coreModPath + '.kt'
if (!getFile(targetFileJava).exists() && !getFile(targetFileScala).exists() && !getFile(targetFileScalaJava).exists() && !getFile(targetFileKotlin).exists()) {
throw new GradleException("Could not resolve \"coreModClass\"! Could not find ${targetFileJava} or ${targetFileScala} or ${targetFileScalaJava} or ${targetFileKotlin}")
}
}
// Plugin application
// Scala
if (getFile('src/main/scala').exists()) {
apply plugin: 'scala'
}
if (getFile('src/main/kotlin').exists()) {
apply plugin: 'org.jetbrains.kotlin.jvm'
}
// Kotlin
pluginManager.withPlugin('org.jetbrains.kotlin.jvm') {
kotlin {
jvmToolchain(8)
}
def disabledKotlinTaskList = [
"kaptGenerateStubsMcLauncherKotlin",
"kaptGenerateStubsPatchedMcKotlin",
"kaptGenerateStubsInjectedTagsKotlin",
"compileMcLauncherKotlin",
"compilePatchedMcKotlin",
"compileInjectedTagsKotlin",
"kaptMcLauncherKotlin",
"kaptPatchedMcKotlin",
"kaptInjectedTagsKotlin",
"kspMcLauncherKotlin",
"kspPatchedMcKotlin",
"kspInjectedTagsKotlin",
]
tasks.configureEach { task ->
if (task.name in disabledKotlinTaskList) {
task.enabled = false
}
}
}
// Spotless
//noinspection GroovyAssignabilityCheck
project.extensions.add(com.diffplug.blowdryer.Blowdryer, 'Blowdryer', com.diffplug.blowdryer.Blowdryer) // make Blowdryer available in plugin application
if (enableSpotless.toBoolean()) {
apply plugin: 'com.diffplug.spotless'
// Spotless auto-formatter
// See https://github.com/diffplug/spotless/tree/main/plugin-gradle
// Can be locally toggled via spotless:off/spotless:on comments
spotless {
encoding 'UTF-8'
format 'misc', {
target '.gitignore'
trimTrailingWhitespace()
indentWithSpaces(4)
endWithNewline()
}
java {
target 'src/main/java/**/*.java', 'src/test/java/**/*.java' // exclude api as they are not our files
def orderFile = project.file('spotless.importorder')
if (!orderFile.exists()) {
orderFile = Blowdryer.file('spotless.importorder')
}
def formatFile = project.file('spotless.eclipseformat.xml')
if (!formatFile.exists()) {
formatFile = Blowdryer.file('spotless.eclipseformat.xml')
}
toggleOffOn()
importOrderFile(orderFile)
removeUnusedImports()
endWithNewline()
//noinspection GroovyAssignabilityCheck
eclipse('4.19.0').configFile(formatFile)
}
kotlin {
target 'src/*/kotlin/**/*.kt'
toggleOffOn()
ktfmt('0.39')
trimTrailingWhitespace()
indentWithSpaces(4)
endWithNewline()
}
scala {
target 'src/*/scala/**/*.scala'
scalafmt('3.7.1')
}
}
}
// Git version checking, also checking for if this is a submodule
if (project.file('.git/HEAD').isFile() || project.file('.git').isFile()) {
apply plugin: 'com.palantir.git-version'
}
// Shadowing
if (usesShadowedDependencies.toBoolean()) {
apply plugin: 'com.github.johnrengelman.shadow'
}
// Configure Java
java {
toolchain {
if (enableModernJavaSyntax.toBoolean()) {
languageVersion.set(JavaLanguageVersion.of(17))
} else {
languageVersion.set(JavaLanguageVersion.of(8))
}
// Azul covers the most platforms for Java 8+ toolchains, crucially including MacOS arm64
vendor.set(JvmVendorSpec.AZUL)
}
if (!noPublishedSources.toBoolean()) {
withSourcesJar()
}
}
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8'
if (enableModernJavaSyntax.toBoolean()) {
if (it.name in ['compileMcLauncherJava', 'compilePatchedMcJava']) {
return
}
sourceCompatibility = 17
options.release.set(8)
javaCompiler.set(javaToolchains.compilerFor {
languageVersion.set(JavaLanguageVersion.of(17))
vendor.set(JvmVendorSpec.AZUL)
})
}
}
tasks.withType(ScalaCompile).configureEach {
options.encoding = 'UTF-8'
}
// Allow others using this buildscript to have custom gradle code run
if (getFile('addon.gradle').exists()) {
apply from: 'addon.gradle'
} else if (getFile('addon.gradle.kts').exists()) {
apply from: 'addon.gradle.kts'
}
// Configure Minecraft
// Try to gather mod version from git tags if version is not manually specified
if (!modVersion) {
try {
modVersion = gitVersion()
} catch (Exception ignored) {
out.style(Style.Failure).text(
"Mod version could not be determined! Property 'modVersion' is not set, and either git is not installed or no git tags exist.\n" +
"Either specify a mod version in 'gradle.properties', or create at least one tag in git for this project."
)
modVersion = 'NO-GIT-TAG-SET'
}
}
if (includeMCVersionJar.toBoolean()){
version = "${minecraftVersion}-${modVersion}"
}
else {
version = modVersion
}
group = modGroup
base {
archivesName = modArchivesBaseName
}
minecraft {
mcVersion = minecraftVersion
username = developmentEnvironmentUserName.toString()
useDependencyAccessTransformers = true
// Automatic token injection with RetroFuturaGradle
if (gradleTokenModId) {
injectedTags.put gradleTokenModId, modId
}
if (gradleTokenModName) {
injectedTags.put gradleTokenModName, modName
}
if (gradleTokenVersion) {
injectedTags.put gradleTokenVersion, modVersion
}
// JVM arguments
extraRunJvmArguments.add("-ea:${modGroup}")
if (usesMixins.toBoolean()) {
extraRunJvmArguments.addAll([
'-Dmixin.hotSwap=true',
'-Dmixin.checks.interfaces=true',
'-Dmixin.debug.export=true'
])
}
if (enableCoreModDebug.toBoolean()) {
extraRunJvmArguments.addAll([
'-Dlegacy.debugClassLoading=true',
'-Dlegacy.debugClassLoadingFiner=true',
'-Dlegacy.debugClassLoadingSave=true'
])
}
}
if (coreModClass) {
for (runTask in ['runClient', 'runServer']) {
tasks.named(runTask).configure {
extraJvmArgs.add("-Dfml.coreMods.load=${modGroup}.${coreModClass}")
}
}
}
if (generateGradleTokenClass) {
tasks.injectTags.outputClassName.set(generateGradleTokenClass)
}
tasks.named('processIdeaSettings').configure {
dependsOn('injectTags')
}
// Repositories
// Allow unsafe repos but warn
repositories.configureEach { repo ->
if (repo instanceof UrlArtifactRepository) {
if (repo.getUrl() != null && repo.getUrl().getScheme() == "http" && !repo.allowInsecureProtocol) {
logger.warn("Deprecated: Allowing insecure connections for repo '${repo.name}' - add 'allowInsecureProtocol = true'")
repo.allowInsecureProtocol = true
}
}
}
// Allow adding custom repositories to the buildscript
if (getFile('repositories.gradle').exists()) {
apply from: 'repositories.gradle'
} else if (getFile('repositories.gradle.kts').exists()) {
apply from: 'repositories.gradle.kts'
}
repositories {
if (includeWellKnownRepositories.toBoolean() || includeCommonDevEnvMods.toBoolean()) {
exclusiveContent {
forRepository {
//noinspection ForeignDelegate
maven {
name = 'Curse Maven'
url = 'https://www.cursemaven.com'
// url = 'https://beta.cursemaven.com'
}
}
filter {
includeGroup 'curse.maven'
}
}
exclusiveContent {
forRepository {
//noinspection ForeignDelegate
maven {
name = 'Modrinth'
url = 'https://api.modrinth.com/maven'
}
}
filter {
includeGroup 'maven.modrinth'
}
}
maven {
name 'Cleanroom Maven'
url 'https://maven.cleanroommc.com'
}
maven {
name 'BlameJared Maven'
url 'https://maven.blamejared.com'
}
maven {
name 'GTNH Maven'
url 'https://nexus.gtnewhorizons.com/repository/public/'
}
}
if (usesMixins.toBoolean() || forceEnableMixins.toBoolean()) {
// need to add this here even if we did not above
if (!includeWellKnownRepositories.toBoolean()) {
maven {
name 'Cleanroom Maven'
url 'https://maven.cleanroommc.com'
}
}
}
mavenLocal() // Must be last for caching to work
}
// Dependencies
// Configure dependency configurations
configurations {
embed
implementation.extendsFrom(embed)
if (usesShadowedDependencies.toBoolean()) {
for (config in [compileClasspath, runtimeClasspath, testCompileClasspath, testRuntimeClasspath]) {
config.extendsFrom(shadowImplementation)
config.extendsFrom(shadowCompile)
}
}
}
String mixinProviderSpec = 'zone.rong:mixinbooter:8.9'
dependencies {
if (usesMixins.toBoolean()) {
annotationProcessor 'org.ow2.asm:asm-debug-all:5.2'
// should use 24.1.1 but 30.0+ has a vulnerability fix
annotationProcessor 'com.google.guava:guava:30.0-jre'
// should use 2.8.6 but 2.8.9+ has a vulnerability fix
annotationProcessor 'com.google.code.gson:gson:2.8.9'
mixinProviderSpec = modUtils.enableMixins(mixinProviderSpec, "mixins.${modId}.refmap.json")
api (mixinProviderSpec) {
transitive = false
}
annotationProcessor(mixinProviderSpec) {
transitive = false
}
} else if (forceEnableMixins.toBoolean()) {
runtimeOnly(mixinProviderSpec)
}
if (enableJUnit.toBoolean()) {
testImplementation 'org.hamcrest:hamcrest:2.2'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
if (enableModernJavaSyntax.toBoolean()) {
annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:1.0.0'
compileOnly('com.github.bsideup.jabel:jabel-javac-plugin:1.0.0') {
transitive = false
}
// workaround for https://github.com/bsideup/jabel/issues/174
annotationProcessor 'net.java.dev.jna:jna-platform:5.13.0'
// Allow jdk.unsupported classes like sun.misc.Unsafe, workaround for JDK-8206937 and fixes Forge crashes in tests.
patchedMinecraft 'me.eigenraven.java8unsupported:java-8-unsupported-shim:1.0.0'
// allow Jabel to work in tests
testAnnotationProcessor "com.github.bsideup.jabel:jabel-javac-plugin:1.0.0"
testCompileOnly("com.github.bsideup.jabel:jabel-javac-plugin:1.0.0") {
transitive = false // We only care about the 1 annotation class
}
testCompileOnly "me.eigenraven.java8unsupported:java-8-unsupported-shim:1.0.0"
}
compileOnlyApi 'org.jetbrains:annotations:24.1.0'
annotationProcessor 'org.jetbrains:annotations:24.1.0'
patchedMinecraft('net.minecraft:launchwrapper:1.17.2') {
transitive = false
}
if (includeCommonDevEnvMods.toBoolean()) {
implementation 'mezz.jei:jei_1.12.2:4.16.1.302'
//noinspection DependencyNotationArgument
implementation rfg.deobf('curse.maven:top-245211:2667280') // TOP 1.4.28
}
}
pluginManager.withPlugin('org.jetbrains.kotlin.kapt') {
if (usesMixins.toBoolean()) {
dependencies {
kapt(mixinProviderSpec)
}
}
}
if (getFile('dependencies.gradle').exists()) {
apply from: 'dependencies.gradle'
} else if (getFile('dependencies.gradle.kts').exists()) {
apply from: 'dependencies.gradle.kts'
}
// Test configuration
// Ensure tests have access to minecraft classes
sourceSets {
test {
java {
compileClasspath += patchedMc.output + mcLauncher.output
runtimeClasspath += patchedMc.output + mcLauncher.output
}
}
}
test {
// ensure tests are run with java8
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(8)
}.get()
testLogging {
events TestLogEvent.STARTED, TestLogEvent.PASSED, TestLogEvent.FAILED
exceptionFormat TestExceptionFormat.FULL
showExceptions true
showStackTraces true
showCauses true
showStandardStreams true
}
if (enableJUnit.toBoolean()) {
useJUnitPlatform()
}
}
// Resource processing and jar building
processResources {
// this will ensure that this task is redone when the versions change.
inputs.property 'version', modVersion
inputs.property 'mcversion', minecraftVersion
// Blowdryer puts these files into the resource directory, so
// exclude them from builds (doesn't hurt to exclude even if not present)
exclude('spotless.importorder')
exclude('spotless.eclipseformat.xml')
// replace stuff in mcmod.info, nothing else
filesMatching('mcmod.info') { fcd ->
fcd.expand(
'version': modVersion,
'mcversion': minecraftVersion,
'modid': modId,
'modname': modName
)
}
if (accessTransformersFile) {
String[] ats = accessTransformersFile.split(',')
ats.each { at ->
rename "(${at})", 'META-INF/$1'
}
}
}
// Automatically generate a mixin json file if it does not already exist
tasks.register('generateAssets') {
group = 'GT Buildscript'
description = 'Generates a pack.mcmeta, mcmod.info, or mixins.{modid}.json if needed'
doLast {
// pack.mcmeta
def packMcmetaFile = getFile('src/main/resources/pack.mcmeta')
if (!packMcmetaFile.exists()) {
packMcmetaFile.text = """{
"pack": {
"pack_format": 3,
"description": "${modName} Resource Pack"
}
}
"""
}
// mcmod.info
def mcmodInfoFile = getFile('src/main/resources/mcmod.info')
if (!mcmodInfoFile.exists()) {
mcmodInfoFile.text = """[{
"modid": "\${modid}",
"name": "\${modname}",
"description": "An example mod for Minecraft 1.12.2 with Forge",
"version": "\${version}",
"mcversion": "\${mcversion}",
"logoFile": "",
"url": "",
"authorList": [],
"credits": "",
"dependencies": []
}]
"""
}
// mixins.{modid}.json
if (usesMixins.toBoolean() && generateMixinConfig.toBoolean()) {
def mixinConfigFile = getFile("src/main/resources/mixins.${modId}.json")
if (!mixinConfigFile.exists()) {
def mixinConfigRefmap = "mixins.${modId}.refmap.json"
mixinConfigFile.text = """{
"package": "${modGroup}.${mixinsPackage}",
"refmap": "${mixinConfigRefmap}",
"target": "@env(DEFAULT)",
"minVersion": "0.8",
"compatibilityLevel": "JAVA_8",
"mixins": [],
"client": [],
"server": []
}
"""
}
}
}
}
tasks.named('processResources').configure {
dependsOn('generateAssets')
}
jar {
manifest {
attributes(getManifestAttributes())
}
// Add all embedded dependencies into the jar
from provider {
configurations.embed.collect {
it.isDirectory() ? it : zipTree(it)
}
}
if (useSrcApiPath && apiPackage) {
from sourceSets.api.output
dependsOn apiClasses
include "${modGroupPath}/**"
include "assets/**"
include "mcmod.info"
include "pack.mcmeta"
if (accessTransformersFile) {
include "META-INF/${accessTransformersFile}"
}
}
}
// Configure default run tasks
if (separateRunDirectories.toBoolean()) {
runClient {
workingDir = file('run/client')
}
runServer {
workingDir = file('run/server')
}
}
// Create API library jar
tasks.register('apiJar', Jar) {
archiveClassifier.set 'api'
if (useSrcApiPath) {
from(sourceSets.api.java) {
include "${modGroupPath}/${apiPackagePath}/**"
}
from(sourceSets.api.output) {
include "${modGroupPath}/${apiPackagePath}/**"
}
} else {
from(sourceSets.main.java) {
include "${modGroupPath}/${apiPackagePath}/**"
}
from(sourceSets.main.output) {
include "${modGroupPath}/${apiPackagePath}/**"
}
}
}
// Configure shadow jar task
if (usesShadowedDependencies.toBoolean()) {
tasks.named('shadowJar', ShadowJar).configure {
manifest {
attributes(getManifestAttributes())
}
// Only shadow classes that are actually used, if enabled
if (minimizeShadowedDependencies.toBoolean()) {
minimize()
}
configurations = [
project.configurations.shadowImplementation,
project.configurations.shadowCompile
]
archiveClassifier.set('dev')
if (relocateShadowedDependencies.toBoolean()) {
relocationPrefix = modGroup + '.shadow'
enableRelocation = true
}
}
configurations.runtimeElements.outgoing.artifacts.clear()
configurations.apiElements.outgoing.artifacts.clear()
configurations.runtimeElements.outgoing.artifact(tasks.named('shadowJar', ShadowJar))
configurations.apiElements.outgoing.artifact(tasks.named('shadowJar', ShadowJar))
tasks.named('jar', Jar) {
enabled = false
finalizedBy(tasks.shadowJar)
}
tasks.named('reobfJar', ReobfuscatedJar) {
inputJar.set(tasks.named('shadowJar', ShadowJar).flatMap({it.archiveFile}))
}
AdhocComponentWithVariants javaComponent = (AdhocComponentWithVariants) project.components.findByName('java')
javaComponent.withVariantsFromConfiguration(configurations.shadowRuntimeElements) {
skip()
}
for (runTask in ['runClient', 'runServer']) {
tasks.named(runTask).configure {
dependsOn('shadowJar')
}
}
}
def getManifestAttributes() {
def attributes = [:]
if (coreModClass) {
attributes['FMLCorePlugin'] = "${modGroup}.${coreModClass}"
}
if (!containsMixinsAndOrCoreModOnly.toBoolean() && (usesMixins.toBoolean() || coreModClass)) {
attributes['FMLCorePluginContainsFMLMod'] = true
}
if (accessTransformersFile) {
attributes['FMLAT'] = accessTransformersFile.toString()
}
if (usesMixins.toBoolean()) {
attributes['ForceLoadAsMod'] = !containsMixinsAndOrCoreModOnly.toBoolean()
}
return attributes
}
// IDE Configuration
eclipse {
classpath {
downloadSources = true
downloadJavadoc = true
}
}
idea {
module {
inheritOutputDirs true
downloadJavadoc true
downloadSources true
}
project {
settings {
runConfigurations {
'1. Setup Workspace'(Gradle) {
taskNames = ['setupDecompWorkspace']
}
'2. Run Client'(Gradle) {
taskNames = ['runClient']
}
'3. Run Server'(Gradle) {
taskNames = ['runServer']
}
'4. Run Obfuscated Client'(Gradle) {
taskNames = ['runObfClient']
}
'5. Run Obfuscated Server'(Gradle) {
taskNames = ['runObfServer']
}
if (enableSpotless.toBoolean()) {
'6. Apply Spotless'(Gradle) {
taskNames = ["spotlessApply"]
}
'7. Build Jars'(Gradle) {
taskNames = ['build']
}
} else {
'6. Build Jars'(Gradle) {
taskNames = ['build']
}
}
'Update Buildscript'(Gradle) {
taskNames = ['updateBuildScript']
}
'FAQ'(Gradle) {
taskNames = ['faq']
}
}
compiler.javac {
afterEvaluate {
javacAdditionalOptions = '-encoding utf8'
moduleJavacAdditionalOptions = [
(project.name + '.main'): tasks.compileJava.options.compilerArgs.collect {
'"' + it + '"'
}.join(' ')
]
}
}
}
}
}
// Deployment
def final modrinthApiKey = providers.environmentVariable('MODRINTH_API_KEY')
def final cfApiKey = providers.environmentVariable('CURSEFORGE_API_KEY')
final boolean isCIEnv = providers.environmentVariable('CI').getOrElse('false').toBoolean()
if (isCIEnv || deploymentDebug.toBoolean()) {
artifacts {
if (!noPublishedSources.toBoolean()) {
archives sourcesJar
}
if (apiPackage) {
archives apiJar
}
}
}
// Changelog generation
tasks.register('generateChangelog') {
group = 'GT Buildscript'
description = 'Generate a default changelog of all commits since the last tagged git commit'
onlyIf {
generateDefaultChangelog.toBoolean()
}
doLast {
def lastTag = getLastTag()
def changelog = runShell(([
"git",
"log",
"--date=format:%d %b %Y",
"--pretty=%s - **%an** (%ad)",
"${lastTag}..HEAD"
] + (sourceSets.main.java.srcDirs + sourceSets.main.resources.srcDirs)
.collect { ['--', it] }).flatten())
if (changelog) {
changelog = "Changes since ${lastTag}:\n${{("\n" + changelog).replaceAll("\n", "\n* ")}}"
}
def f = getFile('build/changelog.md')
changelog = changelog ?: 'There have been no changes.'
f.write(changelog, 'UTF-8')
// Set changelog for Modrinth
if (modrinthApiKey.isPresent() || deploymentDebug.toBoolean()) {
modrinth.changelog.set(changelog)
}
}
}
if (cfApiKey.isPresent() || deploymentDebug.toBoolean()) {
apply plugin: 'net.darkhax.curseforgegradle'
//noinspection UnnecessaryQualifiedReference
tasks.register('curseforge', net.darkhax.curseforgegradle.TaskPublishCurseForge) {
disableVersionDetection()
debugMode = deploymentDebug.toBoolean()
apiToken = cfApiKey.getOrElse('debug_token')
doFirst {
def mainFile = upload(curseForgeProjectId, reobfJar)
def changelogFile = getChangelog()
def changelogRaw = changelogFile.exists() ? changelogFile.getText('UTF-8') : ""
mainFile.displayName = versionDisplayFormat.replace('$MOD_NAME', modName).replace('$VERSION', modVersion)
mainFile.releaseType = getReleaseType()
mainFile.changelog = changelogRaw
mainFile.changelogType = 'markdown'
mainFile.addModLoader 'Forge'
mainFile.addJavaVersion "Java 8"
mainFile.addGameVersion minecraftVersion
if (curseForgeRelations.size() != 0) {
String[] deps = curseForgeRelations.split(';')
deps.each { dep ->
if (dep.size() == 0) {
return
}
String[] parts = dep.split(':')
String type = parts[0], slug = parts[1]
def types = [
'req' : 'requiredDependency', 'required': 'requiredDependency',
'opt' : 'optionalDependency', 'optional': 'optionalDependency',
'embed' : 'embeddedLibrary', 'embedded': 'embeddedLibrary',
'incomp': 'incompatible', 'fail' : 'incompatible']
if (types.containsKey(type)) type = types[type]
if (!(type in ['requiredDependency', 'embeddedLibrary', 'optionalDependency', 'tool', 'incompatible'])) {
throw new Exception('Invalid Curseforge dependency type: ' + type)
}
mainFile.addRelation(slug, type)
}
}
for (artifact in getSecondaryArtifacts()) {
def additionalFile = mainFile.withAdditionalFile(artifact)
additionalFile.changelog = changelogRaw
}
}
}
tasks.curseforge.dependsOn(build)
tasks.curseforge.dependsOn('generateChangelog')
}
if (modrinthApiKey.isPresent() || deploymentDebug.toBoolean()) {
apply plugin: 'com.modrinth.minotaur'
def final changelogFile = getChangelog()
modrinth {
token = modrinthApiKey.getOrElse('debug_token')
projectId = modrinthProjectId
versionName = versionDisplayFormat.replace('$MOD_NAME', modName).replace('$VERSION', modVersion)
changelog = changelogFile.exists() ? changelogFile.getText('UTF-8') : ""
versionType = getReleaseType()
versionNumber = modVersion
gameVersions = [minecraftVersion]
loaders = ["forge"]
debugMode = deploymentDebug.toBoolean()
uploadFile = reobfJar
additionalFiles = getSecondaryArtifacts()
}
if (modrinthRelations.size() != 0) {
String[] deps = modrinthRelations.split(';')
deps.each { dep ->
if (dep.size() == 0) {
return
}
String[] parts = dep.split(':')
String[] qual = parts[0].split('-')
addModrinthDep(qual[0], qual.length > 1 ? qual[1] : 'project', parts[1])
}
}
tasks.modrinth.dependsOn(build)
tasks.modrinth.dependsOn('generateChangelog')
}
def addModrinthDep(String scope, String type, String name) {
com.modrinth.minotaur.dependencies.Dependency dep
def types = [
'req' : 'required',
'opt' : 'optional',
'embed' : 'embedded',
'incomp': 'incompatible', 'fail': 'incompatible']
if (types.containsKey(scope)) scope = types[scope]
if (!(scope in ['required', 'optional', 'incompatible', 'embedded'])) {
throw new Exception('Invalid modrinth dependency scope: ' + scope)
}
types = ['proj': 'project', '': 'project', 'p': 'project', 'ver': 'version', 'v': 'version']
if (types.containsKey(type)) type = types[type]
switch (type) {
case 'project':
dep = new ModDependency(name, scope)
break
case 'version':
dep = new VersionDependency(name, scope)
break
default:
throw new Exception('Invalid modrinth dependency type: ' + type)
}
project.modrinth.dependencies.add(dep)
}
if (customMavenPublishUrl) {
String publishedVersion = modVersion
publishing {
publications {
create('maven', MavenPublication) {
//noinspection GroovyAssignabilityCheck
from components.java
if (apiPackage) {
artifact apiJar
}
// providers is not available here, use System for getting env vars
groupId = System.getenv('ARTIFACT_GROUP_ID') ?: project.mavenArtifactGroup
artifactId = System.getenv('ARTIFACT_ID') ?: project.modArchivesBaseName
version = System.getenv('RELEASE_VERSION') ?: publishedVersion
}
}
repositories {
maven {
url = customMavenPublishUrl
allowInsecureProtocol = !customMavenPublishUrl.startsWith('https')
credentials {
username = providers.environmentVariable('MAVEN_USER').getOrElse('NONE')
password = providers.environmentVariable('MAVEN_PASSWORD').getOrElse('NONE')
}
}
}
}
}
def getSecondaryArtifacts() {
def secondaryArtifacts = [usesShadowedDependencies.toBoolean() ? tasks.shadowJar : tasks.jar]
if (!noPublishedSources.toBoolean()) secondaryArtifacts += [sourcesJar]
if (apiPackage) secondaryArtifacts += [apiJar]
return secondaryArtifacts
}
def getReleaseType() {
String type = project.releaseType
if (!(type in ['release', 'beta', 'alpha'])) {
throw new Exception("Release type invalid! Found \"" + type + "\", allowed: \"release\", \"beta\", \"alpha\"")
}
return type
}
/*
* If CHANGELOG_LOCATION env var is set, that takes highest precedence.
* Next, if 'generateDefaultChangelog' option is enabled, use that.
* Otherwise, try to use a CHANGELOG.md file at root directory.
*/
def getChangelog() {
def final changelogEnv = providers.environmentVariable('CHANGELOG_LOCATION')
if (changelogEnv.isPresent()) {
return new File(changelogEnv.get())
}
if (generateDefaultChangelog.toBoolean()) {
return getFile('build/changelog.md')
}
return getFile('CHANGELOG.md')
}
// Buildscript updating
def buildscriptGradleVersion = '8.5'
tasks.named('wrapper', Wrapper).configure {
gradleVersion = buildscriptGradleVersion
}
tasks.register('updateBuildScript') {
group = 'GT Buildscript'
description = 'Updates the build script to the latest version'
if (gradle.gradleVersion != buildscriptGradleVersion && !Boolean.getBoolean('DISABLE_BUILDSCRIPT_GRADLE_UPDATE')) {
dependsOn('wrapper')
}
doLast {
if (performBuildScriptUpdate()) return
print('Build script already up to date!')
}
}
if (!project.getGradle().startParameter.isOffline() && !Boolean.getBoolean('DISABLE_BUILDSCRIPT_UPDATE_CHECK') && isNewBuildScriptVersionAvailable()) {
if (autoUpdateBuildScript.toBoolean()) {
performBuildScriptUpdate()
} else {
out.style(Style.SuccessHeader).println("Build script update available! Run 'gradle updateBuildScript'")
if (gradle.gradleVersion != buildscriptGradleVersion) {
out.style(Style.SuccessHeader).println("updateBuildScript can update gradle from ${gradle.gradleVersion} to ${buildscriptGradleVersion}\n")
}
}
}
static URL availableBuildScriptUrl() {
new URL("https://raw.githubusercontent.com/GregTechCEu/Buildscripts/master/build.gradle")
}
static URL availableSettingsGradleUrl() {
new URL("https://raw.githubusercontent.com/GregTechCEu/Buildscripts/master/settings.gradle")
}
boolean performBuildScriptUpdate() {
if (isNewBuildScriptVersionAvailable()) {
def buildscriptFile = getFile("build.gradle")
def settingsFile = getFile("settings.gradle")
availableBuildScriptUrl().withInputStream { i -> buildscriptFile.withOutputStream { it << i } }
availableSettingsGradleUrl().withInputStream { i -> settingsFile.withOutputStream { it << i } }
def out = services.get(StyledTextOutputFactory).create('buildscript-update-output')
out.style(Style.Success).print("Build script updated. Please REIMPORT the project or RESTART your IDE!")
return true
}
return false
}
boolean isNewBuildScriptVersionAvailable() {
Map parameters = ["connectTimeout": 10000, "readTimeout": 10000]
String currentBuildScript = getFile("build.gradle").getText()
String currentBuildScriptHash = getVersionHash(currentBuildScript)
String availableBuildScript = availableBuildScriptUrl().newInputStream(parameters).getText()
String availableBuildScriptHash = getVersionHash(availableBuildScript)
boolean isUpToDate = currentBuildScriptHash.empty || availableBuildScriptHash.empty || currentBuildScriptHash == availableBuildScriptHash
return !isUpToDate
}
static String getVersionHash(String buildScriptContent) {
String versionLine = buildScriptContent.find("^//version: [a-z0-9]*")
if (versionLine != null) {
return versionLine.split(": ").last()
}
return ""
}
// Faq
tasks.register('faq') {
group = 'GT Buildscript'
description = 'Prints frequently asked questions about building a project'
doLast {
print("\nTo update this buildscript to the latest version, run 'gradlew updateBuildScript' or run the generated run configuration if you are using IDEA.\n" +
"To set up the project, run the 'setupDecompWorkspace' task, which you can run as './gradlew setupDecompWorkspace' in a terminal, or find in the 'modded minecraft' gradle category.\n\n" +
"To add new dependencies to your project, place them in 'dependencies.gradle', NOT in 'build.gradle' as they would be replaced when the script updates.\n" +
"To add new repositories to your project, place them in 'repositories.gradle'.\n" +
"If you need additional gradle code to run, you can place it in a file named 'addon.gradle' (or either of the above, up to you for organization).\n\n" +
"If your build fails to recognize the syntax of newer Java versions, enable Jabel in your 'gradle.properties' under the option name 'enableModernJavaSyntax'.\n" +
"To see information on how to configure your IDE properly for Java 17, see https://github.com/GregTechCEu/Buildscripts/blob/master/docs/jabel.md\n\n" +
"Report any issues or feature requests you have for this build script to https://github.com/GregTechCEu/Buildscripts/issues\n")
}
}
// Helpers
def getDefaultArtifactGroup() {
def lastIndex = project.modGroup.lastIndexOf('.')
return lastIndex < 0 ? project.modGroup : project.modGroup.substring(0, lastIndex)
}
def getFile(String relativePath) {
return new File(projectDir, relativePath)
}
def checkPropertyExists(String propertyName) {
if (!project.hasProperty(propertyName)) {
throw new GradleException("This project requires a property \"" + propertyName + "\"! Please add it your \"gradle.properties\". You can find all properties and their description here: https://github.com/GregTechCEu/Buildscripts/blob/main/gradle.properties")
}
}
def propertyDefaultIfUnset(String propertyName, defaultValue) {
if (!project.hasProperty(propertyName) || project.property(propertyName) == "") {
project.ext.setProperty(propertyName, defaultValue)
}
}
def propertyDefaultIfUnsetWithEnvVar(String propertyName, defaultValue, String envVarName) {
def envVar = providers.environmentVariable(envVarName)
if (envVar.isPresent()) {
project.ext.setProperty(propertyName, envVar.get())
} else {
propertyDefaultIfUnset(propertyName, defaultValue)
}
}
static runShell(command) {
def process = command.execute()
def outputStream = new StringBuffer()
def errorStream = new StringBuffer()
process.waitForProcessOutput(outputStream, errorStream)
errorStream.toString().with {
if (it) {
throw new GradleException("Error executing ${command}:\n> ${it}")
}
}
return outputStream.toString().trim()
}
def getLastTag() {
def githubTag = providers.environmentVariable('GITHUB_TAG')
return runShell('git describe --abbrev=0 --tags ' +
(githubTag.isPresent() ? runShell('git rev-list --tags --skip=1 --max-count=1') : ''))
}