mirror of
https://github.com/FabricMC/fabric.git
synced 2024-11-22 23:58:02 -05:00
9ff28f4026
A common source of crashes on modded Minecraft servers comes from modders accidently calling client only code from the client, this PR is another large step towards elimitating that. This PR has been months in the making and years in the planning, requiring major changes to Loom & Loader. In recent Minecraft versions Mojang has made it easier than ever to cleanly split the jar, going against the status-quo of merging the client and server into one jar. From the start we have designed Fabric to have a very clear split between client and common (client & server) code. Fabric has always encoraged keeping client only code seprate from the server, this can be seen at a fundamental level with the entrypoints in Loader. Fabric API's have all been designed with this mind. This PR provides a compile safety net around Fabric API using client only code on the server. Even though there are almost 400 changed files, minimal changes beyond moving the files were required to achieve this in Fabric API, thanks to the effort of all contributors in the past. These changes should not affect modders or players in anyway, a single "universal" jar is still produced. Im happy to awnswer any questions.
567 lines
14 KiB
Groovy
567 lines
14 KiB
Groovy
buildscript {
|
|
dependencies {
|
|
classpath 'org.kohsuke:github-api:1.135'
|
|
}
|
|
}
|
|
|
|
plugins {
|
|
id "java-library"
|
|
id "eclipse"
|
|
id "idea"
|
|
id "maven-publish"
|
|
id "fabric-loom" version "0.12.38" apply false
|
|
id "com.diffplug.spotless" version "6.5.1"
|
|
id "org.ajoberstar.grgit" version "3.1.0"
|
|
id "com.matthewprenger.cursegradle" version "1.4.0"
|
|
id "com.modrinth.minotaur" version "1.1.0"
|
|
id "me.modmuss50.remotesign" version "0.2.4" apply false
|
|
}
|
|
|
|
def ENV = System.getenv()
|
|
def signingEnabled = ENV.SIGNING_SERVER
|
|
|
|
version = project.version + "+" + (ENV.GITHUB_RUN_NUMBER ? "" : "local-") + getBranch()
|
|
logger.lifecycle("Building Fabric: " + version)
|
|
|
|
import org.apache.commons.codec.digest.DigestUtils
|
|
|
|
def getSubprojectVersion(project) {
|
|
// Get the version from the gradle.properties file
|
|
def version = project.properties["${project.name}-version"]
|
|
|
|
if (!version) {
|
|
throw new NullPointerException("Could not find version for " + project.name)
|
|
}
|
|
|
|
if (grgit == null) {
|
|
return version + "+nogit"
|
|
}
|
|
|
|
def latestCommits = grgit.log(paths: [project.name], maxCommits: 1)
|
|
|
|
if (latestCommits.isEmpty()) {
|
|
return version + "+uncommited"
|
|
}
|
|
|
|
return version + "+" + latestCommits.get(0).id.substring(0, 8) + DigestUtils.sha256Hex(project.rootProject.minecraft_version).substring(0, 2)
|
|
}
|
|
|
|
def getBranch() {
|
|
def ENV = System.getenv()
|
|
if (ENV.GITHUB_REF) {
|
|
def branch = ENV.GITHUB_REF
|
|
return branch.substring(branch.lastIndexOf("/") + 1)
|
|
}
|
|
|
|
if (grgit == null) {
|
|
return "unknown"
|
|
}
|
|
|
|
def branch = grgit.branch.current().name
|
|
return branch.substring(branch.lastIndexOf("/") + 1)
|
|
}
|
|
|
|
def moduleDependencies(project, List<String> depNames) {
|
|
def deps = depNames.iterator().collect { project.dependencies.project(path: ":$it", configuration: 'namedElements') }
|
|
def clientOutputs = depNames.iterator().collect { findProject(":$it").sourceSets.client.output }
|
|
|
|
project.dependencies {
|
|
deps.each {
|
|
api it
|
|
}
|
|
|
|
clientOutputs.each {
|
|
clientImplementation it
|
|
}
|
|
}
|
|
|
|
// As we manually handle the maven artifacts, we need to also manually specify the deps.
|
|
project.publishing {
|
|
publications {
|
|
mavenJava(MavenPublication) {
|
|
pom.withXml {
|
|
def depsNode = asNode().appendNode("dependencies")
|
|
deps.each {
|
|
def depNode = depsNode.appendNode("dependency")
|
|
depNode.appendNode("groupId", it.group)
|
|
depNode.appendNode("artifactId", it.name)
|
|
depNode.appendNode("version", it.version)
|
|
depNode.appendNode("scope", "compile")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
def testDependencies(project, List<String> depNames) {
|
|
def deps = depNames.iterator().collect { project.dependencies.project(path: ":$it", configuration: 'namedElements') }
|
|
def clientOutputs = depNames.iterator().collect { findProject(":$it").sourceSets.client.output }
|
|
|
|
project.dependencies {
|
|
deps.each {
|
|
testmodImplementation it
|
|
}
|
|
|
|
clientOutputs.each {
|
|
testmodImplementation it
|
|
}
|
|
}
|
|
}
|
|
|
|
allprojects {
|
|
group = "net.fabricmc.fabric-api"
|
|
|
|
apply plugin: "maven-publish"
|
|
apply plugin: "me.modmuss50.remotesign"
|
|
|
|
tasks.withType(GenerateModuleMetadata) {
|
|
enabled = false
|
|
}
|
|
|
|
if (signingEnabled) {
|
|
remoteSign {
|
|
requestUrl = ENV.SIGNING_SERVER
|
|
pgpAuthKey = ENV.SIGNING_PGP_KEY
|
|
jarAuthKey = ENV.SIGNING_JAR_KEY
|
|
|
|
afterEvaluate {
|
|
// PGP sign all maven publications.
|
|
sign publishing.publications.mavenJava
|
|
}
|
|
}
|
|
}
|
|
|
|
publishing {
|
|
setupRepositories(repositories)
|
|
}
|
|
|
|
if (it.name == "deprecated") return
|
|
|
|
apply plugin: "java-library"
|
|
apply plugin: "checkstyle"
|
|
apply plugin: "fabric-loom"
|
|
apply plugin: "com.diffplug.spotless"
|
|
|
|
tasks.withType(JavaCompile).configureEach {
|
|
it.options.release = 17
|
|
}
|
|
|
|
java {
|
|
// Must be added before the split source sets are setup.
|
|
withSourcesJar()
|
|
}
|
|
|
|
loom {
|
|
splitEnvironmentSourceSets()
|
|
}
|
|
|
|
sourceSets {
|
|
testmod {
|
|
compileClasspath += main.compileClasspath
|
|
runtimeClasspath += main.runtimeClasspath
|
|
|
|
compileClasspath += client.compileClasspath
|
|
runtimeClasspath += client.runtimeClasspath
|
|
}
|
|
}
|
|
|
|
loom {
|
|
runtimeOnlyLog4j = true
|
|
|
|
runs {
|
|
testmodClient {
|
|
client()
|
|
ideConfigGenerated project.rootProject == project
|
|
name = "Testmod Client"
|
|
source sourceSets.testmod
|
|
}
|
|
testmodServer {
|
|
server()
|
|
ideConfigGenerated project.rootProject == project
|
|
name = "Testmod Server"
|
|
source sourceSets.testmod
|
|
}
|
|
}
|
|
}
|
|
|
|
allprojects.each { p ->
|
|
if (project.name == "deprecated") return
|
|
|
|
loom.mods.register(p.name) {
|
|
sourceSet p.sourceSets.main
|
|
sourceSet p.sourceSets.client
|
|
}
|
|
|
|
loom.mods.register(p.name + "-testmod") {
|
|
sourceSet p.sourceSets.testmod
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
minecraft "com.mojang:minecraft:$rootProject.minecraft_version"
|
|
mappings "net.fabricmc:yarn:${rootProject.minecraft_version}${project.yarn_version}:v2"
|
|
modApi "net.fabricmc:fabric-loader:${project.loader_version}"
|
|
|
|
testmodImplementation sourceSets.main.output
|
|
testmodImplementation sourceSets.client.output
|
|
}
|
|
|
|
loom {
|
|
shareRemapCaches = true
|
|
}
|
|
|
|
processResources {
|
|
inputs.property "version", project.version
|
|
|
|
filesMatching("fabric.mod.json") {
|
|
expand "version": project.version
|
|
}
|
|
}
|
|
|
|
spotless {
|
|
java {
|
|
licenseHeaderFile(rootProject.file("HEADER"))
|
|
}
|
|
}
|
|
|
|
checkstyle {
|
|
configFile = rootProject.file("checkstyle.xml")
|
|
toolVersion = "9.1"
|
|
}
|
|
|
|
tasks.withType(AbstractArchiveTask) {
|
|
preserveFileTimestamps = false
|
|
reproducibleFileOrder = true
|
|
}
|
|
|
|
if (signingEnabled) {
|
|
remoteSign {
|
|
sign remapJar
|
|
}
|
|
}
|
|
|
|
// Run this task after updating minecraft to regenerate any required resources
|
|
task generateResources {
|
|
group = "fabric"
|
|
}
|
|
|
|
task testmodJar(type: Jar) {
|
|
from sourceSets.testmod.output
|
|
destinationDirectory = new File(project.buildDir, "devlibs")
|
|
archiveClassifier = "testmod"
|
|
}
|
|
|
|
task remapTestmodJar(type: net.fabricmc.loom.task.RemapJarTask, dependsOn: testmodJar) {
|
|
input = testmodJar.archiveFile
|
|
archiveClassifier = "testmod"
|
|
addNestedDependencies = false
|
|
}
|
|
build.dependsOn remapTestmodJar
|
|
}
|
|
|
|
// Apply auxiliary buildscripts to submodules
|
|
// This must be done after all plugins are applied to subprojects
|
|
apply from: "gradle/module-validation.gradle"
|
|
apply from: "gradle/module-versioning.gradle"
|
|
|
|
javadoc {
|
|
options {
|
|
source = "17"
|
|
encoding = "UTF-8"
|
|
charSet = "UTF-8"
|
|
memberLevel = JavadocMemberLevel.PACKAGE
|
|
links(
|
|
"https://guava.dev/releases/21.0/api/docs/",
|
|
"https://asm.ow2.io/javadoc/",
|
|
"https://docs.oracle.com/javase/8/docs/api/",
|
|
"http://jenkins.liteloader.com/job/Mixin/javadoc/",
|
|
"https://logging.apache.org/log4j/2.x/log4j-api/apidocs/"
|
|
// Need to add minecraft jd publication etc once there is one available
|
|
)
|
|
// Disable the crazy super-strict doclint tool in Java 8
|
|
addStringOption("Xdoclint:none", "-quiet")
|
|
}
|
|
|
|
allprojects.each {
|
|
if (it.name == "deprecated") return
|
|
|
|
source(it.sourceSets.main.allJava.srcDirs)
|
|
source(it.sourceSets.client.allJava.srcDirs)
|
|
}
|
|
|
|
classpath = files(sourceSets.main.compileClasspath, sourceSets.client.compileClasspath)
|
|
include("**/api/**")
|
|
failOnError false
|
|
}
|
|
|
|
task javadocJar(type: Jar) {
|
|
dependsOn javadoc
|
|
from javadoc.destinationDir
|
|
//Set as `fatjavadoc` to prevent an ide form trying to use this javadoc, over using the modules javadoc
|
|
archiveClassifier = "fatjavadoc"
|
|
}
|
|
|
|
build.dependsOn javadocJar
|
|
|
|
loom {
|
|
runs {
|
|
gametest {
|
|
inherit testmodServer
|
|
|
|
name "Game Test"
|
|
|
|
// Enable the gametest runner
|
|
vmArg "-Dfabric-api.gametest"
|
|
vmArg "-Dfabric-api.gametest.report-file=${project.buildDir}/junit.xml"
|
|
runDir "build/gametest"
|
|
}
|
|
autoTestServer {
|
|
inherit testmodServer
|
|
|
|
name "Auto Test Server"
|
|
|
|
vmArg "-Dfabric.autoTest"
|
|
}
|
|
}
|
|
}
|
|
test.dependsOn runGametest
|
|
|
|
subprojects {
|
|
if (it.name == "deprecated") return
|
|
|
|
dependencies {
|
|
testmodImplementation sourceSets.main.output
|
|
|
|
// Make all modules depend on the gametest api (and thus res loader) to try and promote its usage.
|
|
if (project.name != "fabric-gametest-api-v1") {
|
|
testmodImplementation project(path: ':fabric-gametest-api-v1', configuration: 'namedElements')
|
|
testmodImplementation project(path: ':fabric-resource-loader-v0', configuration: 'namedElements')
|
|
}
|
|
|
|
// Make all testmods run with registry-sync-v0 as it is required to register new objects.
|
|
if (project.name != "fabric-registry-sync-v0") {
|
|
testmodRuntimeOnly project(path: ':fabric-registry-sync-v0', configuration: 'namedElements')
|
|
}
|
|
}
|
|
|
|
publishing {
|
|
publications {
|
|
mavenJava(MavenPublication) {
|
|
artifact(signingEnabled ? signRemapJar.output : remapJar) {
|
|
builtBy(signingEnabled ? signRemapJar : remapJar)
|
|
}
|
|
|
|
artifact(remapSourcesJar) {
|
|
builtBy remapSourcesJar
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// We manually handle the pom generation
|
|
loom.disableDeprecatedPomGeneration(publishing.publications.mavenJava)
|
|
|
|
javadoc.enabled = false
|
|
|
|
afterEvaluate {
|
|
// Disable the gen sources task on sub projects
|
|
genClientOnlySourcesWithFernFlower.enabled = false
|
|
genClientOnlySourcesWithCfr.enabled = false
|
|
genCommonSourcesWithCfr.enabled = false
|
|
genCommonSourcesWithFernFlower.enabled = false
|
|
unpickClientOnlyJar.enabled = false
|
|
unpickCommonJar.enabled = false
|
|
}
|
|
}
|
|
|
|
task remapMavenJar(type: net.fabricmc.loom.task.RemapJarTask, dependsOn: jar) {
|
|
input = jar.archiveFile
|
|
archiveFileName = "${archivesBaseName}-${project.version}-maven.jar"
|
|
addNestedDependencies = false
|
|
}
|
|
build.dependsOn remapMavenJar
|
|
|
|
if (signingEnabled) {
|
|
remoteSign {
|
|
sign remapMavenJar
|
|
}
|
|
}
|
|
|
|
publishing {
|
|
publications {
|
|
mavenJava(MavenPublication) {
|
|
artifact(signingEnabled ? signRemapMavenJar.output : remapMavenJar) {
|
|
builtBy(signingEnabled ? signRemapMavenJar : remapMavenJar)
|
|
}
|
|
|
|
artifact(sourcesJar) {
|
|
builtBy remapSourcesJar
|
|
}
|
|
|
|
artifact javadocJar
|
|
|
|
pom.withXml {
|
|
def depsNode = asNode().appendNode("dependencies")
|
|
subprojects.each {
|
|
// Dont depend on the deprecated modules in the main artifact.
|
|
if (it.path.startsWith(":deprecated")) return
|
|
|
|
def depNode = depsNode.appendNode("dependency")
|
|
depNode.appendNode("groupId", it.group)
|
|
depNode.appendNode("artifactId", it.name)
|
|
depNode.appendNode("version", it.version)
|
|
depNode.appendNode("scope", "compile")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Required until the deprecation is removed. Fabric API's main jar that is published to maven does not contain sub modules.
|
|
loom.disableDeprecatedPomGeneration(publishing.publications.mavenJava)
|
|
|
|
void setupRepositories(RepositoryHandler repositories) {
|
|
//repositories.mavenLocal() // uncomment for testing
|
|
def ENV = System.getenv()
|
|
if (ENV.MAVEN_URL) {
|
|
repositories.maven {
|
|
url ENV.MAVEN_URL
|
|
if (ENV.MAVEN_USERNAME) {
|
|
credentials {
|
|
username ENV.MAVEN_USERNAME
|
|
password ENV.MAVEN_PASSWORD
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
subprojects.each {
|
|
if (it.name == "deprecated") return
|
|
|
|
remapJar.dependsOn("${it.path}:remapJar")
|
|
}
|
|
|
|
sourceSets {
|
|
testmod
|
|
}
|
|
|
|
// These modules are not included in the fat jar, maven will resolve them via the pom.
|
|
def devOnlyModules = [
|
|
"fabric-gametest-api-v1",
|
|
"fabric-data-generation-api-v1"
|
|
]
|
|
|
|
dependencies {
|
|
afterEvaluate {
|
|
subprojects.each {
|
|
if (it.name == "deprecated") return
|
|
|
|
api project(path: "${it.path}", configuration: "namedElements")
|
|
clientImplementation project("${it.path}:").sourceSets.client.output
|
|
|
|
testmodImplementation project("${it.path}:").sourceSets.testmod.output
|
|
}
|
|
}
|
|
}
|
|
|
|
remapJar {
|
|
afterEvaluate {
|
|
subprojects.each {
|
|
if (it.name in devOnlyModules || it.name == "deprecated") return
|
|
|
|
// Include the signed or none signed jar from the sub project.
|
|
nestedJars.from project("${it.path}").tasks.getByName(signingEnabled ? "signRemapJar" : "remapJar")
|
|
}
|
|
}
|
|
}
|
|
|
|
curseforge {
|
|
if (ENV.CURSEFORGE_API_KEY) {
|
|
apiKey = ENV.CURSEFORGE_API_KEY
|
|
}
|
|
|
|
project {
|
|
id = "306612"
|
|
changelog = ENV.CHANGELOG ?: "No changelog provided"
|
|
releaseType = project.prerelease == "true" ? "beta" : "release"
|
|
addGameVersion "1.19-Snapshot"
|
|
addGameVersion "Fabric"
|
|
|
|
mainArtifact(signingEnabled ? signRemapJar.output : remapJar) {
|
|
displayName = "[$project.minecraft_version] Fabric API $project.version"
|
|
}
|
|
|
|
afterEvaluate {
|
|
uploadTask.dependsOn("remapJar")
|
|
}
|
|
}
|
|
|
|
options {
|
|
forgeGradleIntegration = false
|
|
}
|
|
}
|
|
|
|
if (signingEnabled) {
|
|
project.tasks.curseforge.dependsOn signRemapJar
|
|
build.dependsOn signRemapJar
|
|
}
|
|
|
|
import org.kohsuke.github.GHReleaseBuilder
|
|
import org.kohsuke.github.GitHub
|
|
|
|
task github(dependsOn: (signingEnabled ? signRemapJar : remapJar)) {
|
|
onlyIf {
|
|
ENV.GITHUB_TOKEN
|
|
}
|
|
|
|
doLast {
|
|
def github = GitHub.connectUsingOAuth(ENV.GITHUB_TOKEN as String)
|
|
def repository = github.getRepository(ENV.GITHUB_REPOSITORY)
|
|
|
|
def releaseBuilder = new GHReleaseBuilder(repository, version as String)
|
|
releaseBuilder.name("[$project.minecraft_version] Fabric API $project.version")
|
|
releaseBuilder.body(ENV.CHANGELOG ?: "No changelog provided")
|
|
releaseBuilder.commitish(getBranch())
|
|
releaseBuilder.prerelease(project.prerelease == "true")
|
|
|
|
def ghRelease = releaseBuilder.create()
|
|
ghRelease.uploadAsset(signingEnabled ? signRemapJar.output.get().getAsFile() : remapJar.archiveFile.get().getAsFile(), "application/java-archive");
|
|
}
|
|
}
|
|
|
|
task modrinth(type: com.modrinth.minotaur.TaskModrinthUpload, dependsOn: (signingEnabled ? signRemapJar : remapJar)) {
|
|
onlyIf {
|
|
ENV.MODRINTH_TOKEN
|
|
}
|
|
|
|
token = ENV.MODRINTH_TOKEN
|
|
projectId = "P7dR8mSH"
|
|
versionNumber = version
|
|
versionName = "[$project.minecraft_version] Fabric API $project.version"
|
|
releaseType = project.prerelease == "true" ? "beta" : "release"
|
|
changelog = ENV.CHANGELOG ?: "No changelog provided"
|
|
|
|
uploadFile = signingEnabled ? signRemapJar.output : remapJar
|
|
|
|
addGameVersion(project.minecraft_version)
|
|
addLoader('fabric')
|
|
}
|
|
|
|
// A task to ensure that the version being released has not already been released.
|
|
task checkVersion {
|
|
doFirst {
|
|
def xml = new URL("https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api/maven-metadata.xml").text
|
|
def metadata = new XmlSlurper().parseText(xml)
|
|
def versions = metadata.versioning.versions.version*.text();
|
|
if (versions.contains(version)) {
|
|
throw new RuntimeException("${version} has already been released!")
|
|
}
|
|
}
|
|
}
|
|
|
|
github.mustRunAfter checkVersion
|
|
modrinth.mustRunAfter checkVersion
|
|
publish.mustRunAfter checkVersion
|
|
project.tasks.curseforge.mustRunAfter checkVersion
|