Skip to content

Commit 0f3d5d2

Browse files
committed
Initial commit
0 parents  commit 0f3d5d2

28 files changed

+2358
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.gradle/
2+
build/

LICENSE.md

+675
Large diffs are not rendered by default.

README.md

+319
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
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

build.gradle.kts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
plugins {
2+
`kotlin-dsl`
3+
`maven-publish`
4+
}
5+
6+
group = "gg.essential"
7+
version = "0.1.0"
8+
9+
java.withSourcesJar()
10+
11+
repositories {
12+
mavenCentral()
13+
gradlePluginPortal()
14+
maven(url = "https://maven.fabricmc.net/")
15+
maven(url = "https://maven.minecraftforge.net")
16+
maven(url = "https://jitpack.io")
17+
maven(url = "https://maven.architectury.dev/")
18+
maven(url = "https://repo.essential.gg/repository/maven-public")
19+
}
20+
21+
dependencies {
22+
implementation(gradleApi())
23+
implementation(localGroovy())
24+
25+
api(libs.archloom)
26+
implementation(libs.archloomPack200)
27+
28+
compileOnly(libs.kotlin.gradlePlugin)
29+
implementation(libs.kotlinx.binaryCompatibilityValidator)
30+
implementation(libs.proguard) {
31+
exclude(group = "org.jetbrains.kotlin")
32+
}
33+
implementation("gradle.plugin.com.github.jengelman.gradle.plugins:shadow:7.0.0")
34+
api(libs.preprocessor)
35+
implementation(libs.asm)
36+
implementation(libs.guava)
37+
}
38+
39+
publishing {
40+
repositories {
41+
val nexusUser = project.findProperty("nexus_user")
42+
val nexusPassword = project.findProperty("nexus_password")
43+
if (nexusUser != null && nexusPassword != null) {
44+
maven("https://repo.essential.gg/repository/maven-releases/") {
45+
name = "nexus-public"
46+
credentials {
47+
username = nexusUser.toString()
48+
password = nexusPassword.toString()
49+
}
50+
}
51+
}
52+
}
53+
}

gradle/libs.versions.toml

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[versions]
2+
archloom = "0.10.0.1"
3+
archloomPack200 = "0.1.3"
4+
kotlin = "1.6.10"
5+
kotlinx-binaryCompatibilityValidator = "0.8.0"
6+
proguard = "7.2.0"
7+
preprocessor = "0ab22d2"
8+
asm = "9.2"
9+
guava = "30.1.1-jre"
10+
11+
[libraries]
12+
kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
13+
kotlinx-binaryCompatibilityValidator = { module = "org.jetbrains.kotlinx:binary-compatibility-validator", version.ref = "kotlinx-binaryCompatibilityValidator" }
14+
proguard = { module = "com.guardsquare:proguard-gradle", version.ref = "proguard" }
15+
preprocessor = { module = "com.github.replaymod:preprocessor", version.ref = "preprocessor" }
16+
asm = { module = "org.ow2.asm:asm-commons", version.ref = "asm" }
17+
guava = { module = "com.google.guava:guava", version.ref = "guava" }
18+
archloom = { module = "gg.essential.loom:gg.essential.loom.gradle.plugin", version.ref = "archloom" }
19+
archloomPack200 = { module = "dev.architectury.architectury-pack200:dev.architectury.architectury-pack200.gradle.plugin", version.ref = "archloomPack200" }

gradle/wrapper/gradle-wrapper.jar

58.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)