Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use an in-tree Gradle plugin instead of an external Gradle Plugin to make version-specific changes easier #1485

Merged
merged 39 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
b9894f2
Switch to custom neodev plugin based on moddev
Technici4n Aug 15, 2024
08d5a5f
Directly add dependency
Technici4n Oct 20, 2024
962296e
Simplify ModuleIdentificationVisitor a little
Technici4n Oct 20, 2024
a9dfc94
Fix gradle sync
Technici4n Oct 25, 2024
ed633d7
Make generatePackageInfos config cache compatible
Technici4n Oct 25, 2024
cb30f1f
Back to genPatches
Technici4n Oct 25, 2024
c0a5816
Copy utils from MDG
Technici4n Oct 25, 2024
fb38822
Try to fix compat JAR
Technici4n Oct 26, 2024
120caef
Re-enable changelog and installer publication
shartte Oct 26, 2024
14e29d5
Replace ModuleIdentificationVisitor by ResolvedArtifactResult manipul…
Technici4n Nov 10, 2024
a762a75
Bump MDG, move gson and diffpatch versions to main gradle.properties
Technici4n Nov 10, 2024
6531711
Disable fail on warnings
Technici4n Nov 10, 2024
cd997b9
Fix tests not compiling coremods in nondelegated builds
Technici4n Nov 11, 2024
14510ba
Started on documentation.
shartte Nov 12, 2024
c762e3a
Remove unused forValidation configuration
Technici4n Nov 13, 2024
8a23e4b
Refactor configuration handling (prod not quite working yet)
Technici4n Nov 14, 2024
17ab941
Allow duplicate libraries in the installer profile to make sure they …
Technici4n Nov 14, 2024
4c64c12
Remove neoForm & its tools from userdev libraries
Technici4n Nov 14, 2024
d86cd3a
Remove a bit of whitespace diff
Technici4n Nov 15, 2024
5aae136
Minor fixes
shartte Nov 15, 2024
9087871
Remove modules from BSL ignore list (newer BSL is smarter)
Technici4n Nov 16, 2024
39fd703
Refactor Tool Classpaths into NeoDevConfigurations
shartte Nov 17, 2024
b689ae5
Task Groups
shartte Nov 17, 2024
f9d096a
Slight refactor for configurations
shartte Nov 17, 2024
be62f57
More docs
shartte Nov 17, 2024
9e1672a
Request tools with "shadowed" bundling if possible.
shartte Nov 18, 2024
3323698
Minor comment fixes
Technici4n Nov 18, 2024
7d76110
Deduplicate RemapJar and GenerateBinaryPatches configuration code
Technici4n Nov 18, 2024
34b4501
Refactor tool transitiveness handling a little
Technici4n Nov 18, 2024
2af9644
Add back module path to prod server ignoreList
Technici4n Nov 18, 2024
15cfcaf
A few easy comments
Technici4n Nov 20, 2024
2714291
Make RemapJar property names more generic
Technici4n Nov 21, 2024
9144452
Add back AT validation
Technici4n Nov 21, 2024
0d893b6
Bump JUnit dependency to match fml-junit
Technici4n Nov 21, 2024
5d2a235
Add renovate comments for tool dependencies. Use + version for installer
Technici4n Nov 21, 2024
e8f2275
Remove dynamic installer version
Technici4n Nov 21, 2024
4e4e33e
Remove legacy mergeModules
Technici4n Nov 21, 2024
dad13e8
Revert "Remove dynamic installer version"
Technici4n Nov 22, 2024
a71e50c
Rename MC resources jar and move it to the normal classpath
Technici4n Nov 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check-local-changes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
run: ./gradlew generatePackageInfos

- name: Gen patches
run: ./gradlew :neoforge:unpackSourcePatches
run: ./gradlew :neoforge:genPatches

- name: Run datagen with Gradle
run: ./gradlew :neoforge:runData :tests:runData
Expand Down
19 changes: 11 additions & 8 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import java.util.regex.Pattern
plugins {
id 'net.neoforged.gradleutils' version '3.0.0'
id 'com.diffplug.spotless' version '6.22.0' apply false
id 'net.neoforged.licenser' version '0.7.2'
id 'net.neoforged.licenser' version '0.7.5'
id 'neoforge.formatting-conventions'
id 'neoforge.versioning'
}
Expand All @@ -23,19 +23,22 @@ System.out.println("NeoForge version ${project.version}")
allprojects {
version rootProject.version
group 'net.neoforged'
repositories {
mavenLocal()
}
}

subprojects {
apply plugin: 'java'

java.toolchain.languageVersion.set(JavaLanguageVersion.of(project.java_version))
}

repositories {
mavenCentral()
// Remove src/ sources from the root project. They are used in the neoforge subproject.
sourceSets {
main {
java {
srcDirs = []
}
resources {
srcDirs = []
}
}
}

// Put licenser here otherwise it tries to license all source sets including decompiled MC sources
Expand Down
81 changes: 81 additions & 0 deletions buildSrc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# NeoForge Development Gradle Plugin

## NeoForge Project Structure

Before understanding the `buildSrc` plugin, one should understand the structure of the NeoForge Gradle project it is
applied to.

The project consists of a project tree with the following structure:

| Folder Path | Gradle Project Path | Applied Plugins | Description |
|------------------------------------------------------------------------|----------------------|:------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`/build.gradle`](../build.gradle) | `:` | — | The root project. Since this project is reused for Kits, the root project name is based on the checkout folder, which actually can lead to issues if it is called `NeoForge`. |
| [`/projects/neoforge/build.gradle`](../projects/neoforge/build.gradle) | `:neoforge` | [NeoDevPlugin](#neodevplugin) | The core NeoForge project, which produces the artifacts that will be published. |
| [`/projects/base/build.gradle`](../projects/base/build.gradle) | `:base` | [NeoDevBasePlugin](#neodevbaseplugin) | A utility project that contains the Minecraft sources without any NeoForge additions. Can be used to quickly compare what NeoForge has changed. |
| [`/tests/build.gradle`](../tests/build.gradle) | `:tests` | [NeoDevExtraPlugin](#neodevextraplugin) | Contains the game and unit tests for NeoForge. |
| [`/testframework/build.gradle`](../testframework/build.gradle) | `:testframework` | [MinecraftDependenciesPlugin](#minecraftdependenciesplugin) | A library providing support classes around writing game tests. |
| [`/coremods/build.gradle`](../coremods/build.gradle) | `:neoforge-coremods` | — | Java Bytecode transformers that are embedded into NeoForge as a nested Jar file. |
|

## Plugins

### NeoDevBasePlugin

Sources: [NeoDevBasePlugin.java](src/main/java/net/neoforged/neodev/NeoDevBasePlugin.java)

Implicitly applies: [MinecraftDependenciesPlugin](#minecraftdependenciesplugin).

Sets up a `setup` task that reuses code from [NeoDevPlugin](#neodevplugin) to decompile Minecraft and place the
decompiled sources in `projects/base/src/main/java`.

### NeoDevPlugin

Sources: [NeoDevPlugin.java](src/main/java/net/neoforged/neodev/NeoDevPlugin.java)

Implicitly applies: [MinecraftDependenciesPlugin](#minecraftdependenciesplugin).

This is the primary of this repository and is used to configure the `neoforge` subproject.

#### Setup

It creates a `setup` task that performs the following actions via various subtasks:

- Decompile Minecraft using the [NeoForm Runtime](https://github.com/neoforged/neoformruntime) and Minecraft version specific [NeoForm data](https://github.com/neoforged/NeoForm).
- Applies [Access Transformers](../src/main/resources/META-INF/accesstransformer.cfg) to Minecraft sources.
- Applies [NeoForge patches](../patches) to Minecraft sources. Any rejects are saved to the `/rejects` folder in the repository for manual inspection. During updates to new versions, the task can be run with `-Pupdating=true` to apply patches more leniently.
- Unpacks the patched sources to `projects/neoforge/src/main/java`.

#### Config Generation

The plugin creates and configures the tasks to create various configuration files used downstream to develop
mods with this version of NeoForge ([CreateUserDevConfig](src/main/java/net/neoforged/neodev/CreateUserDevConfig.java)), or install it ([CreateInstallerProfile](src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java) and [CreateLauncherProfile](src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java)).

A separate userdev profile is created for use by other subprojects in this repository.
The only difference is that it uses the FML launch types ending in `dev` rather than `userdev`.

#### Patch Generation

NeoForge injects its hooks into Minecraft by patching the decompiled source code.
Changes are made locally to the decompiled and patched source.
Since that source cannot be published, patches need to be generated before checking in.
The plugin configures the necessary task to do this
([GenerateSourcePatches](src/main/java/net/neoforged/neodev/GenerateSourcePatches.java)).

The source patches are only used during development of NeoForge itself and development of mods that use Gradle plugins implementing the decompile/patch/recompile pipeline.
For use by the installer intended for players as well as Gradle plugins wanting to replicate the production artifacts more closely, binary patches are generated using the ([GenerateBinaryPatches](src/main/java/net/neoforged/neodev/GenerateBinaryPatches.java)) task.

### NeoDevExtraPlugin

Sources: [NeoDevExtraPlugin.java](src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java)

This plugin can be applied to obtain a dependency on the `neoforge` project to depend on NeoForge including Minecraft
itself. Besides wiring up the dependency, it also creates run configurations based on the run-types defined in the
`neoforge` project.

### MinecraftDependenciesPlugin

This plugin is reused from [ModDevGradle](https://github.com/neoforged/ModDevGradle/).

It sets up repositories and attributes such that
the [libraries that Minecraft itself depends upon](https://github.com/neoforged/GradleMinecraftDependencies) can be
used.
23 changes: 23 additions & 0 deletions buildSrc/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
plugins {
id 'java-gradle-plugin'
id 'groovy-gradle-plugin'
}

repositories {
gradlePluginPortal()
mavenCentral()
maven {
name = "NeoForged"
url = "https://maven.neoforged.net/releases"
content {
includeGroup "codechicken"
includeGroup "net.neoforged"
}
}
}

dependencies {
// buildSrc is an includedbuild of the parent directory (gradle.parent)
// ../settings.gradle sets these version properties accordingly
implementation "net.neoforged:moddev-gradle:${gradle.parent.ext.moddevgradle_plugin_version}"

implementation "com.google.code.gson:gson:${gradle.parent.ext.gson_version}"
implementation "io.codechicken:DiffPatch:${gradle.parent.ext.diffpatch_version}"
}
Empty file added buildSrc/settings.gradle
Empty file.
14 changes: 11 additions & 3 deletions buildSrc/src/main/groovy/neoforge.formatting-conventions.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ import java.util.regex.Matcher

project.plugins.apply('com.diffplug.spotless')

final generatePackageInfos = tasks.register('generatePackageInfos', Task) {
doLast {
fileTree('src/main/java').each { javaFile ->
abstract class GeneratePackageInfos extends DefaultTask {
@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
abstract ConfigurableFileCollection getFiles();

@TaskAction
void generatePackageInfos() {
getFiles().each { javaFile ->
def packageInfoFile = new File(javaFile.parent, 'package-info.java')
if (!packageInfoFile.exists()) {
def pkgName = javaFile.toString().replaceAll(Matcher.quoteReplacement(File.separator), '/')
Expand All @@ -27,6 +32,9 @@ final generatePackageInfos = tasks.register('generatePackageInfos', Task) {
}
}
}
final generatePackageInfos = tasks.register('generatePackageInfos', GeneratePackageInfos) {
it.files.from fileTree("src/main/java")
}

spotless {
java {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package net.neoforged.neodev;

import net.neoforged.neodev.utils.FileUtils;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Classpath;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.JavaExec;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;

import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;

/**
* Runs <a href="https://github.com/neoforged/JavaSourceTransformer">JavaSourceTransformer</a> to apply
* access transformers to the Minecraft source code for extending the access level of existing classes/methods/etc.
* <p>
* Note that at runtime, FML also applies access transformers.
*/
abstract class ApplyAccessTransformer extends JavaExec {
@InputFile
public abstract RegularFileProperty getInputJar();

@InputFile
public abstract RegularFileProperty getAccessTransformer();

@Input
public abstract Property<Boolean> getValidate();

@OutputFile
public abstract RegularFileProperty getOutputJar();

// Used to give JST more information about the classes.
@Classpath
public abstract ConfigurableFileCollection getLibraries();

@Internal
public abstract RegularFileProperty getLibrariesFile();

@Inject
public ApplyAccessTransformer() {}

@Override
@TaskAction
public void exec() {
try {
FileUtils.writeLinesSafe(
getLibrariesFile().getAsFile().get().toPath(),
getLibraries().getFiles().stream().map(File::getAbsolutePath).toList(),
StandardCharsets.UTF_8);
} catch (IOException exception) {
throw new UncheckedIOException("Failed to write libraries for JST.", exception);
}

args(
"--enable-accesstransformers",
"--access-transformer", getAccessTransformer().getAsFile().get().getAbsolutePath(),
"--access-transformer-validation", getValidate().get() ? "error" : "log",
"--libraries-list", getLibrariesFile().getAsFile().get().getAbsolutePath(),
getInputJar().getAsFile().get().getAbsolutePath(),
getOutputJar().getAsFile().get().getAbsolutePath());

super.exec();
}
}
79 changes: 79 additions & 0 deletions buildSrc/src/main/java/net/neoforged/neodev/ApplyPatches.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package net.neoforged.neodev;

import io.codechicken.diffpatch.cli.PatchOperation;
import io.codechicken.diffpatch.util.Input.MultiInput;
import io.codechicken.diffpatch.util.Output.MultiOutput;
import io.codechicken.diffpatch.util.PatchMode;
import org.gradle.api.DefaultTask;
import org.gradle.api.Project;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputDirectory;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.PathSensitive;
import org.gradle.api.tasks.PathSensitivity;
import org.gradle.api.tasks.TaskAction;
import org.gradle.work.DisableCachingByDefault;

import javax.inject.Inject;
import java.io.IOException;

/**
* Applies Java source patches to a source jar and produces a patched source jar as an output.
* It can optionally store rejected hunks into a given folder, which is primarily used for updating
* when the original sources changed and some hunks are expected to fail.
*/
@DisableCachingByDefault(because = "Not worth caching")
abstract class ApplyPatches extends DefaultTask {
@InputFile
public abstract RegularFileProperty getOriginalJar();

@InputDirectory
@PathSensitive(PathSensitivity.NONE)
public abstract DirectoryProperty getPatchesFolder();

@OutputFile
public abstract RegularFileProperty getPatchedJar();

@OutputDirectory
public abstract DirectoryProperty getRejectsFolder();

@Input
protected abstract Property<Boolean> getIsUpdating();

@Inject
public ApplyPatches(Project project) {
getIsUpdating().set(project.getProviders().gradleProperty("updating").map(Boolean::parseBoolean).orElse(false));
}

@TaskAction
public void applyPatches() throws IOException {
var isUpdating = getIsUpdating().get();

var builder = PatchOperation.builder()
.logTo(getLogger()::lifecycle)
.baseInput(MultiInput.detectedArchive(getOriginalJar().get().getAsFile().toPath()))
.patchesInput(MultiInput.folder(getPatchesFolder().get().getAsFile().toPath()))
.patchedOutput(MultiOutput.detectedArchive(getPatchedJar().get().getAsFile().toPath()))
.rejectsOutput(MultiOutput.folder(getRejectsFolder().get().getAsFile().toPath()))
.mode(isUpdating ? PatchMode.FUZZY : PatchMode.ACCESS)
.aPrefix("a/")
.bPrefix("b/")
.level(isUpdating ? io.codechicken.diffpatch.util.LogLevel.ALL : io.codechicken.diffpatch.util.LogLevel.WARN)
.minFuzz(0.9f); // The 0.5 default in DiffPatch is too low.

var result = builder.build().operate();

int exit = result.exit;
if (exit != 0 && exit != 1) {
throw new RuntimeException("DiffPatch failed with exit code: " + exit);
}
if (exit != 0 && !isUpdating) {
throw new RuntimeException("Patches failed to apply.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package net.neoforged.neodev;

import net.neoforged.nfrtgradle.CreateMinecraftArtifacts;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.tasks.OutputFile;

import javax.inject.Inject;

abstract class CreateCleanArtifacts extends CreateMinecraftArtifacts {
@OutputFile
abstract RegularFileProperty getCleanClientJar();

@OutputFile
abstract RegularFileProperty getRawServerJar();

@OutputFile
abstract RegularFileProperty getCleanServerJar();

@OutputFile
abstract RegularFileProperty getCleanJoinedJar();

@OutputFile
abstract RegularFileProperty getMergedMappings();

@Inject
public CreateCleanArtifacts() {
getAdditionalResults().put("node.stripClient.output.output", getCleanClientJar().getAsFile());
getAdditionalResults().put("node.downloadServer.output.output", getRawServerJar().getAsFile());
getAdditionalResults().put("node.stripServer.output.output", getCleanServerJar().getAsFile());
getAdditionalResults().put("node.rename.output.output", getCleanJoinedJar().getAsFile());
getAdditionalResults().put("node.mergeMappings.output.output", getMergedMappings().getAsFile());

// TODO: does anyone care about this? they should be contained in the client mappings
//"--write-result", "node.downloadServerMappings.output.output:" + getServerMappings().get().getAsFile().getAbsolutePath()
}
}
Loading