|
| 1 | +# Essential Gradle Toolkit |
| 2 | +A Gradle plugin providing various utility methods and common code required to set up multi-version Minecraft mods via [architectury-loom] and [preprocessor]. |
| 3 | + |
| 4 | +### Dependency |
| 5 | +<img alt="version badge" src="https://badges.modcore.net/maven-metadata/v?metadataUrl=https://repo.essential.gg/repository/maven-public/gg/essential/essential-gradle-toolkit/maven-metadata.xml"> |
| 6 | + |
| 7 | +To use essential-gradle-toolkit in your project, you need to add the following repositories to your `settings.gradle(.kts)` file: |
| 8 | +```kotlin |
| 9 | +pluginManagement { |
| 10 | + repositories { |
| 11 | + gradlePluginPortal() |
| 12 | + mavenCentral() |
| 13 | + maven("https://repo.essential.gg/repository/maven-public") |
| 14 | + maven("https://maven.architectury.dev") |
| 15 | + maven("https://maven.fabricmc.net") |
| 16 | + maven("https://maven.minecraftforge.net") |
| 17 | + } |
| 18 | + // We also recommend specifying your desired version here if you're using more than one of the plugins, |
| 19 | + // so you do not have to change the version in multilpe places when updating. |
| 20 | + plugins { |
| 21 | + val egtVersion = "0.1.0" // should be whatever is displayed in above badge |
| 22 | + id("gg.essential.multi-version.root") version egtVersion |
| 23 | + id("gg.essential.multi-version.api-validation") version egtVersion |
| 24 | + } |
| 25 | +} |
| 26 | +``` |
| 27 | + |
| 28 | +## Plugins |
| 29 | + |
| 30 | +## gg.essential.multi-version |
| 31 | +This is the main plugin enabling multi-version mods. |
| 32 | +To create a project which gets compiled for multiple versions, create multiple sub-projects within your main Gradle project: |
| 33 | +<details> |
| 34 | +<summary>settings.gradle.kts</summary> |
| 35 | + |
| 36 | +```kotlin |
| 37 | +listOf( |
| 38 | + "1.8.9-forge", |
| 39 | + "1.12.2-forge", |
| 40 | + "1.16.2-forge", |
| 41 | + "1.16.2-fabric", |
| 42 | +).forEach { version -> |
| 43 | + include(":$version") |
| 44 | + project(":$version").apply { |
| 45 | + // This is where the `build` folder and per-version overwrites will reside |
| 46 | + projectDir = file("versions/$version") |
| 47 | + // All sub-projects get configured by the same `build.gradle.kts` file, the string is relative to projectDir |
| 48 | + // You could use separate build files for each project, but usually that would just be duplicating lots of code |
| 49 | + buildFileName = "../../build.gradle.kts" |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +// We use the `build.gradle.kts` file for all the sub-projects (cause that's where most the interesting stuff lives), |
| 54 | +// so we need to use a different build file for the original root project. |
| 55 | +rootProject.buildFileName = "root.gradle.kts" |
| 56 | +``` |
| 57 | +</details> |
| 58 | +<details> |
| 59 | +<summary>root.gradle.kts</summary> |
| 60 | + |
| 61 | +```kotlin |
| 62 | +plugins { |
| 63 | + // This marks the current project as the root of a multi-version project. |
| 64 | + // Any project using `gg.essential.multi-version` must have a parent with this root plugin applied. |
| 65 | + // Advanced users may use multiple (potentially independent) multi-version trees in different sub-projects. |
| 66 | + // This is currently equivalent to applying `com.replaymod.preprocess-root`. |
| 67 | + id("gg.essential.multi-version.root") |
| 68 | +} |
| 69 | + |
| 70 | +preprocess { |
| 71 | + // Here you first need to create a node per version you support and assign it an integer Minecraft version. |
| 72 | + // The mappings value is currently meaningless. |
| 73 | + val fabric11602 = createNode("1.16.2-fabric", 11602, "yarn") |
| 74 | + val forge11602 = createNode("1.16.2-forge", 11602, "mcp") |
| 75 | + val forge11202 = createNode("1.12.2-forge", 11202, "mcp") |
| 76 | + val forge10809 = createNode("1.8.9-forge", 10809, "mcp") |
| 77 | + |
| 78 | + // And then you need to tell the preprocessor which versions it should directly convert between. |
| 79 | + // This should form a directed graph with no cycles (i.e. a tree), which the preprocessor will then traverse to |
| 80 | + // produce source code for all versions from the main version. |
| 81 | + // Do note that the preprocessor can only convert between two projects when they are either on the same Minecraft |
| 82 | + // version (but use different mappings, e.g. 1.16.2 forge to fabric), or when they are using the same intermediary |
| 83 | + // mappings (but on different Minecraft versions, e.g. 1.12.2 forge to 1.8.9 forge, or 1.16.2 fabric to 1.18 fabric) |
| 84 | + // but not both at the same time, i.e. you cannot go straight from 1.12.2 forge to 1.16.2 fabric, you need to go via |
| 85 | + // an intermediary 1.16.2 forge project which has something in common with both. |
| 86 | + fabric11602.link(forge11602) |
| 87 | + forge11602.link(forge11202) |
| 88 | + // For any link, you can optionally specify a file containing extra mappings which the preprocessor cannot infer by |
| 89 | + // itself, e.g. forge intermediary names do not contain class names, so you may need to supply mappings for those |
| 90 | + // manually. |
| 91 | + forge11202.link(forge10809, file("versions/1.12.2-1.8.9.txt")) |
| 92 | +} |
| 93 | +``` |
| 94 | +</details> |
| 95 | +<details> |
| 96 | +<summary>build.gradle.kts</summary> |
| 97 | + |
| 98 | +```kotlin |
| 99 | +plugins { |
| 100 | + // If you're using Kotlin, it needs to be applied before the multi-version plugin |
| 101 | + kotlin("jvm") |
| 102 | + // Apply the multi-version plugin, this does all the configuration necessary for the preprocessor to |
| 103 | + // work. In particular it also applies `com.replaymod.preprocess`. |
| 104 | + // In addition it primarily also provides a `platform` extension which you can use in this build script |
| 105 | + // to get the version and mod loader of the current project. |
| 106 | + id("gg.essential.multi-version") |
| 107 | + // If you do not care too much about the details, you can just apply essential-gradle-toolkits' defaults for |
| 108 | + // Minecraft, fabric-loader, forge, mappings, etc. versions. |
| 109 | + // You can also overwrite some of these if need be. See the `gg.essential.defaults.loom` README section. |
| 110 | + // Otherwise you'll need to configure those as usual for (architectury) loom. |
| 111 | + id("gg.essential.defaults") |
| 112 | +} |
| 113 | + |
| 114 | +dependencies { |
| 115 | + // If you are depending on a multi-version library following the same scheme as the Essential libraries (that is |
| 116 | + // e.g. `elementa-1.8.9-forge`), you can `toString` `platform` directly to get the respective artifact id. |
| 117 | + modImplementation("gg.essential:elementa-$platform:428") |
| 118 | +} |
| 119 | + |
| 120 | +tasks.processResources { |
| 121 | + // Expansions are already set up for `version` (or `file.jarVersion`) and `mcVersionStr`. |
| 122 | + // You do not need to set those up manually. |
| 123 | +} |
| 124 | + |
| 125 | +loom { |
| 126 | + // If you need to use a tweaker on legacy (1.12.2 and below) forge: |
| 127 | + if (platform.isLegacyForge) { |
| 128 | + launchConfigs.named("client") { |
| 129 | + arg("--tweakClass", "gg.essential.loader.stage0.EssentialSetupTweaker") |
| 130 | + // And maybe a core mod? |
| 131 | + property("fml.coreMods.load", "com.example.asm.CoreMod") |
| 132 | + } |
| 133 | + } |
| 134 | + // Mixin on forge? (for legacy forge you will still need to register a tweaker to set up mixin) |
| 135 | + if (platform.isForge) { |
| 136 | + forge { |
| 137 | + mixinConfig("example.mixins.json") |
| 138 | + // And maybe an access transformer? |
| 139 | + // Though try to avoid these, cause they are not automatically translated to Fabric's access widener |
| 140 | + accessTransformer(project.parent.file("src/main/resources/example_at.cfg")) |
| 141 | + } |
| 142 | + } |
| 143 | +} |
| 144 | +``` |
| 145 | +</details> |
| 146 | + |
| 147 | +Finally you'll have to create a file at `/versions/mainProject` which contains the name of your main project (the one with its sources in `/src`), and you should be good to go: |
| 148 | +<details> |
| 149 | +<summary>versions/mainProject</summary> |
| 150 | + |
| 151 | +``` |
| 152 | +1.12.2-forge |
| 153 | +``` |
| 154 | +</details> |
| 155 | + |
| 156 | +### gg.essential.multi-version.root |
| 157 | +See the comments in the `root.gradle.kts` file above. |
| 158 | + |
| 159 | +### gg.essential.multi-version.api-validation |
| 160 | +This plugin builds on Kotlin's [binary-compatibility-validator] to prevent accidental changes to your public ABI. |
| 161 | + |
| 162 | +It combines all the per-version api files generated by the base plugin into a single file in `/api/Example.api`, thereby |
| 163 | +avoiding the redundancy you would have if you were to use the [binary-compatibility-validator] plugin directly in all |
| 164 | +the sub-projects. |
| 165 | +It takes in a sense the same role as the [preprocessor] takes for Java/Kotlin files. |
| 166 | + |
| 167 | +It should be applied and configured in the root project and will configure the sub-projects by itself: |
| 168 | +<details> |
| 169 | +<summary>root.gradle.kts</summary> |
| 170 | + |
| 171 | +```kotlin |
| 172 | +plugins { |
| 173 | + id("gg.essential.multi-version.root") |
| 174 | + id("gg.essential.multi-version.api-validation") |
| 175 | +} |
| 176 | + |
| 177 | +apiValidation { |
| 178 | + ignoredPackages.add("com.example") |
| 179 | +} |
| 180 | +``` |
| 181 | +</details> |
| 182 | + |
| 183 | +The per-project api files which the base plugin generates should be added to your `.gitignore`: |
| 184 | +<details> |
| 185 | +<summary>.gitignore</summary> |
| 186 | + |
| 187 | +```gitignore |
| 188 | +versions/*/api/ |
| 189 | +``` |
| 190 | +</details> |
| 191 | + |
| 192 | +## gg.essential.defaults |
| 193 | + |
| 194 | +Applies various (partially opinionated) defaults to your project: |
| 195 | +- [repo](#ggessentialdefaultsrepo) |
| 196 | +- [mixin-extras](#ggessentialdefaultsmixin-extras) |
| 197 | +- [java](#ggessentialdefaultsjava) (if the `java` plugin is applied) |
| 198 | +- [loom](#ggessentialdefaultsloom) (if the `gg.essential.loom` plugin is applied) |
| 199 | + |
| 200 | +Does not apply: |
| 201 | +- [maven-publish](#ggessentialdefaultsmaven-publish) |
| 202 | + |
| 203 | +### gg.essential.defaults.java |
| 204 | + |
| 205 | +Sets defaults related to the `java` Gradle plugin: |
| 206 | +- encoding to UTF-8 |
| 207 | + |
| 208 | +### gg.essential.defaults.loom |
| 209 | + |
| 210 | +Sets defaults related to the `gg.essential.loom` ([architectury-loom]) Gradle plugin. |
| 211 | +You can overwrite all of these by setting the given property in the project's `gradle.properties`: |
| 212 | +- Minecraft version (`essential.defaults.loom.minecraft`) |
| 213 | +- Mappings (`essential.defaults.loom.mappings`), special values: |
| 214 | + - `official`/`mojang`/`mojmap`: Uses `loom.officialMojangMappings()` |
| 215 | + - empty string: skips mappings completely so you can configure layered mappings |
| 216 | +- Fabric-Loader version (`essential.defaults.loom.fabric-loader`) |
| 217 | +- Forge version (`essential.defaults.loom.forge`) |
| 218 | + |
| 219 | +Note that these may change frequently. To avoid your build breaking when they do, you need to set the |
| 220 | +`essential.defaults.loom` property in the (root) project's `gradle.properties` file to a specific revision. |
| 221 | +If you build without specifying this property, the build will fail and it will tell you which revision is the one |
| 222 | +currently recommended. |
| 223 | + |
| 224 | +### gg.essential.defaults.mixin-extras |
| 225 | + |
| 226 | +Enables use of Essential's version of [MixinExtras], requires Essential to be present at compile time and runtime to function. |
| 227 | + |
| 228 | +### gg.essential.defaults.repo |
| 229 | + |
| 230 | +Adds Essential and MavenCentral repos to the project. |
| 231 | + |
| 232 | +### gg.essential.defaults.maven-publish |
| 233 | + |
| 234 | +Configures the maven-publish plugin for use with Essential's maven repository. This is likely only useful for libraries |
| 235 | +published on Essential's maven. |
| 236 | + |
| 237 | +If the multi-version plugin is applied, the artifactId is set to follow the `name-version-loader` scheme (e.g. |
| 238 | +`elementa-1.12.2-forge`) where `name` is inferred from the root project's name. Make sure to set it in your |
| 239 | +`settings.gradle.kts` because otherwise Gradle will default to the directory name, which may not be reliable. |
| 240 | + |
| 241 | +Also configures Loom to publish the named jars for legacy Forge versions, rather than the intermediary-mapped ones, |
| 242 | +because that seems to be common practice for those versions. |
| 243 | + |
| 244 | +## Non-plugins |
| 245 | + |
| 246 | +Various utility functions are provided in the `gg.essential.gradle.util` package. |
| 247 | + |
| 248 | +### Prebundle |
| 249 | + |
| 250 | +Bundles all dependencies from a given Gradle configuration into a single, dedicated jar and returns a file collection |
| 251 | +containing that jar. |
| 252 | + |
| 253 | +Primarily for use in dependency declarations, so fat jars of certain dependencies (with potentially relocated |
| 254 | +transitive dependencies) can be created and then depended upon as usual. Compared to simply relocating in a later |
| 255 | +shadow task, this has the advantage that IDEA will see the relocated dependency rather than the original, which e.g. |
| 256 | +allows one to use two different versions of the same dependency at dev time. |
| 257 | + |
| 258 | +This may also be useful if you wish to have custom class loaders, the content of which you need to control precisely. |
| 259 | +See [the method docs](src/main/kotlin/gg/essential/gradle/util/prebundle.kt) for more details. |
| 260 | + |
| 261 | +```kotlin |
| 262 | +import gg.essential.gradle.util.prebundle |
| 263 | + |
| 264 | +dependencies { |
| 265 | + // Creating a named configuration because the bundled jar will take its name from it, e.g. `bothLibs.jar` |
| 266 | + val bothLibs by configurations.creating |
| 267 | + bothLibs("com.google.code.gson:gson:2.0.0") |
| 268 | + bothLibs("com.example:libRequiringAnAncientGson:1.0.0") |
| 269 | + implementation(prebundle(bothLibs)) |
| 270 | +} |
| 271 | +``` |
| 272 | + |
| 273 | +### RelocationTransform |
| 274 | + |
| 275 | +A [Gradle artifact transform](https://docs.gradle.org/current/userguide/artifact_transforms.html) which relocates packages and files. |
| 276 | +Usually used with [prebundle](#prebundle). |
| 277 | + |
| 278 | +See [the docs on the class](src/main/kotlin/gg/essential/gradle/util/RelocationTransform.kt) for more details. |
| 279 | + |
| 280 | +```kotlin |
| 281 | +import gg.essential.gradle.util.RelocationTransform.Companion.registerRelocationAttribute |
| 282 | + |
| 283 | +val relocated = registerRelocationAttribute("relocate-ancient-gson") { |
| 284 | + relocate("com.google.gson", "com.example.lib.gson") |
| 285 | +} |
| 286 | + |
| 287 | +val ancientGson by configurations.creating { |
| 288 | + attributes { attribute(relocated, true) } |
| 289 | +} |
| 290 | + |
| 291 | +dependencies { |
| 292 | + ancientGson("com.google.code.gson:gson:2.0.0") |
| 293 | + ancientGson("com.example:libRequiringAnAncientGson:1.0.0") |
| 294 | + implementation(prebundle(ancientGson)) |
| 295 | +} |
| 296 | +``` |
| 297 | + |
| 298 | +### versionFromBuildIdAndBranch |
| 299 | + |
| 300 | +Generates a simple project version based on the current branch and the `BUILD_ID` property (for CI builds) according to the following schema. |
| 301 | + |
| 302 | +| branch | CI build | Local build | |
| 303 | +|:---------|------------|-------------------| |
| 304 | +| `master` | `42` | `master-SNAPSHOT` | |
| 305 | +| other | `42+other` | `other-SNAPSHOT` | |
| 306 | + |
| 307 | +### extensions |
| 308 | + |
| 309 | +Miscellaneous extension functions to avoid having to write the same thing in multiple projects. |
| 310 | +See [the file](src/main/kotlin/gg/essential/gradle/util/extensions.kt) for details. |
| 311 | + |
| 312 | +## License |
| 313 | +The essential-gradle-toolkit is provided under the terms of the GNU General Public License Version 3 or (at your option) any later version. |
| 314 | +See `LICENSE.md` for the full license text. |
| 315 | + |
| 316 | +[architectury-loom]: https://github.com/Sk1erLLC/architectury-loom |
| 317 | +[preprocessor]: https://github.com/ReplayMod/preprocessor |
| 318 | +[binary-compatibility-validator]: https://github.com/Kotlin/binary-compatibility-validator |
| 319 | +[MixinExtras]: https://github.com/LlamaLad7/MixinExtras |
0 commit comments