This new feature allows for the optional generation of JarJar compatible Jar in Jar shadow jars.
The feature is optional and needs to be enabled with a call to jarJar.enable() anywhere during project evaluation. Or better said, any call to any of the methods in the jarJar extension (more on it later) which would configure the task, will enable it (for example by pinning a version, also more on that later).
By default this adds a single JarJar task which gets enabled by the call to jarJar.enable(), afterwards the task then processes the registered configurations and generates the additional from(...) statements so the files are included into the resulting jar.
JarJar project extension
This PR adds a new extension known as jarJar which handles the configuration for all JarJar tasks (but a call to jarJar.enable() will only enable the default task named jarJar, as opposed to enabling all, however, all other methods in this class operate on all JarJar instances unless otherwise stated).
JarJar configuration
This PR adds a new configuration which is by default added to all JarJar tasks, named jarJar, and is the source of the dependencies which are embedded into the jar during build.
Filtering the dependencies
The JarJar task has a method: dependencies(Action<DependencyFilter> configurator) which can be used to filter the dependencies which get included into the jar, by setting included or excluded dependency specifications.
The configurations of the included dependencies can also be done on a project wide level by invoking the same method on the jarJar extension.
tasks.register('greeting', net.minecraftforge.gradle.userdev.tasks.JarJar) {
dependencies {
exclude(dependency('com.google.gson.*')) //Will exclude all dependencies whoes group matches the regex.
}
}
Exclusively including a dependency or a group of dependencies
tasks.register('greeting', net.minecraftforge.gradle.userdev.tasks.JarJar) {
dependencies {
include(dependency('com.google.gson.gson:gson:2.9.0'))
include(dependency('com.google.gson.*')) //Will include all dependencies whoes group matches the regex.
}
}
Dependency version pinning
Since JarJar is first and foremost a way for multiple mods to supply dependencies it is likely a common occurance that two mods provide the same dependency. To that effect JarJar does not support fixed version definitions for the dependencies a user tries to include. However, to allow for the user to force JarJar to include a particular version as a preference of the developer version pinning can be used. To pin a version of a dependency pass it and the requested version to jarJar.pin(...) as follows:
Since in many scenarios it is preferable to compile against a fixed version, but JarJar requires a version range to be given the plugin supports setting a range on a dependency which is then included in the generated metadata:
This will tell JarJar to generate a metadata entry for this dependency which supports the 2.x version range of the dependency while your project hard compiles against the 2.8.0 version of the library.
Dependency version changing
If you want to compile against a fixed version in your project, but want to include a different version in the jar for minimal support, this can be achieved by combining the above two techniques:
In this configuration the project would compile against 2.9.0, while including a jar of version 2.8.0, and specifying that it supports the 2.x version range of the artifact.
Implementation details
Tasks of the type JarJar allow for the registration of additional configuration (besides the default jarJar one) via the configuration(Configuration configuration) method.
Tasks of the type JarJar can have their default configuration removed by invoking the setConfiguration(List<Configuration> configurations) method, which overrides all already registered configurations.
Tasks of the type JarJar allow for the filtering of the included dependencies which are pulled from the registered configurations
All tasks of type JarJar are automatically reobfuscated (since they are supposed to be used at runtime).
In practice there is currently no support for multi-project workloads inside of FG, to that end, and eventhough things like the DependencyFilter support filtering on projects, JarJar does not support packaging other projects and will silently ignore them.
Publishing a JarJar artifact to maven.
To publish the artifact and its cleaned dependencies it suffices to setup the following maven publication configuration:
publications {
mavenJava(MavenPublication) {
from components.java
jarJar.component(it)
//Other statements related to configuring the POM go here
}
}
In case you have multiple JarJar tasks you can configure each of the publications to use a particular task as its source:
publications {
mavenJava(MavenPublication) {
from components.java
jarJar.component(it, tasks.jarJar) //From the jarJar task (note jarJar here alone does not suffice since that is an extension object)
//Other statements related to configuring the POM go here
}
mavenJavaOther(MavenPublication) {
from components.java
jarJar.component(it, tasks.jarJarOther) //From the jarJarOther tasks
//Other statements related to configuring the POM go here
}
}
This will add the required artifact from all JarJar tasks (or just from the one given) as well as configure the generated POM file to include the required dependencies.
Invoking jarJar.fromRuntimeConfiguration() (Either on the extension object, or on a JarJar task) will add the runtime configuration to the selected configurations to draw from. Note this requires the user to properly configure the filtering of the dependencies to include to prevent a massive mess from being generated, but this should at least provide a shortcut so that as minimal of a duplication of code in the build.gradle file is generated.
Processing of generated POMs.
Due to the need of handling additional dependencies during POM generation (which requires a cleaned up POM to properly work) FG gains with this PR the ability to clean the POMs generated from Maven artifacts.
This cleaning ability can now also be used when the project does not use JarJar.
By invoking fg.component(it) when configuring a publication like so:
publications {
mavenJava(MavenPublication) {
from components.java
fg.component(it)
//Other statements related to configuring the POM go here
}
}
The given publication will have the POM analyzed during POM generation and then have its dependency on Forge stripped as well as all obfuscated dependencies will be reverted to their original dependency notation (so without the _mapped_ suffix).
JarJar
JarJar Task
This new feature allows for the optional generation of JarJar compatible Jar in Jar shadow jars. The feature is optional and needs to be enabled with a call to
jarJar.enable()
anywhere during project evaluation. Or better said, any call to any of the methods in thejarJar
extension (more on it later) which would configure the task, will enable it (for example by pinning a version, also more on that later). By default this adds a singleJarJar
task which gets enabled by the call tojarJar.enable()
, afterwards the task then processes the registered configurations and generates the additionalfrom(...)
statements so the files are included into the resulting jar.JarJar project extension
This PR adds a new extension known as
jarJar
which handles the configuration for allJarJar
tasks (but a call tojarJar.enable()
will only enable the default task namedjarJar
, as opposed to enabling all, however, all other methods in this class operate on allJarJar
instances unless otherwise stated).JarJar configuration
This PR adds a new configuration which is by default added to all
JarJar
tasks, namedjarJar
, and is the source of the dependencies which are embedded into the jar during build.Filtering the dependencies
The
JarJar
task has a method:dependencies(Action<DependencyFilter> configurator)
which can be used to filter the dependencies which get included into the jar, by setting included or excluded dependency specifications.The configurations of the included dependencies can also be done on a project wide level by invoking the same method on the
jarJar
extension.Example of excluding a specific dependency:
Example of excluding a group of dependencies:
Exclusively including a dependency or a group of dependencies
Dependency version pinning
Since JarJar is first and foremost a way for multiple mods to supply dependencies it is likely a common occurance that two mods provide the same dependency. To that effect JarJar does not support fixed version definitions for the dependencies a user tries to include. However, to allow for the user to force JarJar to include a particular version as a preference of the developer version pinning can be used. To pin a version of a dependency pass it and the requested version to
jarJar.pin(...)
as follows:Dependency range setting
Since in many scenarios it is preferable to compile against a fixed version, but JarJar requires a version range to be given the plugin supports setting a range on a dependency which is then included in the generated metadata:
This will tell JarJar to generate a metadata entry for this dependency which supports the 2.x version range of the dependency while your project hard compiles against the 2.8.0 version of the library.
Dependency version changing
If you want to compile against a fixed version in your project, but want to include a different version in the jar for minimal support, this can be achieved by combining the above two techniques:
In this configuration the project would compile against 2.9.0, while including a jar of version 2.8.0, and specifying that it supports the 2.x version range of the artifact.
Implementation details
configuration(Configuration configuration)
method.setConfiguration(List<Configuration> configurations)
method, which overrides all already registered configurations.DependencyFilter
support filtering on projects, JarJar does not support packaging other projects and will silently ignore them.Publishing a JarJar artifact to maven.
To publish the artifact and its cleaned dependencies it suffices to setup the following maven publication configuration:
In case you have multiple JarJar tasks you can configure each of the publications to use a particular task as its source:
This will add the required artifact from all
JarJar
tasks (or just from the one given) as well as configure the generated POM file to include the required dependencies.Invoking
jarJar.fromRuntimeConfiguration()
(Either on the extension object, or on a JarJar task) will add the runtime configuration to the selected configurations to draw from. Note this requires the user to properly configure the filtering of the dependencies to include to prevent a massive mess from being generated, but this should at least provide a shortcut so that as minimal of a duplication of code in the build.gradle file is generated.Processing of generated POMs.
Due to the need of handling additional dependencies during POM generation (which requires a cleaned up POM to properly work) FG gains with this PR the ability to clean the POMs generated from Maven artifacts. This cleaning ability can now also be used when the project does not use JarJar.
By invoking
fg.component(it)
when configuring a publication like so:The given publication will have the POM analyzed during POM generation and then have its dependency on Forge stripped as well as all obfuscated dependencies will be reverted to their original dependency notation (so without the
_mapped_
suffix).Example of minimal buildscript:
build.gradle
```groovy buildscript { repositories { maven { url = 'https://maven.minecraftforge.net' } mavenCentral() mavenLocal() } dependencies { classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true } } plugins { id 'eclipse' id 'maven-publish' } apply plugin: 'net.minecraftforge.gradle' version = '1.0' group = 'com.yourname.modid' // http://maven.apache.org/guides/mini/guide-naming-conventions.html archivesBaseName = 'modid' // Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17. java.toolchain.languageVersion = JavaLanguageVersion.of(17) println "Java: ${System.getProperty 'java.version'}, JVM: ${System.getProperty 'java.vm.version'} (${System.getProperty 'java.vendor'}), Arch: ${System.getProperty 'os.arch'}" minecraft { mappings channel: 'official', version: '1.18.2' runs { client { workingDirectory project.file('run') property 'forge.logging.markers', 'REGISTRIES' property 'forge.logging.console.level', 'debug' property 'forge.enabledGameTestNamespaces', 'examplemod' mods { examplemod { source sourceSets.main } } } server { workingDirectory project.file('run') property 'forge.logging.markers', 'REGISTRIES' property 'forge.logging.console.level', 'debug' property 'forge.enabledGameTestNamespaces', 'examplemod' mods { examplemod { source sourceSets.main } } } gameTestServer { workingDirectory project.file('run') property 'forge.logging.markers', 'REGISTRIES' property 'forge.logging.console.level', 'debug' property 'forge.enabledGameTestNamespaces', 'examplemod' mods { examplemod { source sourceSets.main } } } data { workingDirectory project.file('run') property 'forge.logging.markers', 'REGISTRIES' property 'forge.logging.console.level', 'debug' args '--mod', 'examplemod', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') mods { examplemod { source sourceSets.main } } } } } sourceSets.main.resources { srcDir 'src/generated/resources' } repositories { mavenCentral() } dependencies { minecraft 'net.minecraftforge:forge:1.18.2-40.1.48' implementation fg.deobf(group: 'com.google.code.gson', name: 'gson', version: '[2.0,3.0)') jarJar fg.deobf(group: 'com.google.code.gson', name: 'gson', version: '[2.0,3.0)') { jarJar.pin(it, "2.8.0") } } jar.finalizedBy('reobfJar') publishing { publications { mavenJava(MavenPublication) { from components.java jarJar.component(it) } mavenJavaSpecific(MavenPublication) { from components.java jarJar.component(it, tasks.jarJar) } mavenJavaCleaned(MavenPublication) { from components.java fg.component(it) } } repositories { maven { url "file://${project.projectDir}/mcmodsrepo" } } } tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation } jarJar{ enable() dependencies { include(dependency(group: 'com.google.code.gson', name: 'gson', version: '[2.0,3.0)')) exclude(dependency(group: 'com.google.code.gson', name: 'gson', version: '2.9.0')) } } ```