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

[Feature/Fix]: Interface injections, and minor bugfixes #249

Merged
merged 12 commits into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,31 @@ dependencies {
}
```

#### <a id="userdev-access-transformer" /> Access Transformers
The userdev plugin provides a way to configure access transformers for your mod.
You need to create an access transformer configuration file in your resources directory, and then configure the userdev plugin to use it.
```groovy
userdev {
accessTransformer {
file 'src/main/resources/META-INF/accesstransformer.cfg'
}
}
```
The path here is up to you, and does not need to be included in your final jar.

#### <a id="userdev-interface-injections" /> Interface Injections
The userdev plugin provides a way to configure interface injections for your mod.
This allows you to have a decompiled minecraft artifact that contains the interfaces you want to inject via mixins already statically applied.
The advantage of this approach is that you can use the interfaces in your code, and the mixins will be applied to the interfaces, and not the classes that implement them.
```groovy
userdev {
interfaceInjection {
file 'src/main/resources/META-INF/interfaceinjection.json'
}
}
```
You can find more information on the format of the file [here](https://github.com/neoforged/JavaSourceTransformer?tab=readme-ov-file#interface-injection).

#### Dependency management by the userdev plugin
When this plugin detects a dependency on NeoForge, it will spring into action and create the necessary NeoForm runtime tasks to build a usable Minecraft JAR-file that contains the requested NeoForge version.
It additionally (if configured to do so via conventions, which is the default) will create runs for your project, and add the necessary dependencies to the classpath of the run.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import net.neoforged.gradle.common.dependency.ExtraJarDependencyManager;
import net.neoforged.gradle.common.extensions.*;
import net.neoforged.gradle.common.extensions.dependency.replacement.ReplacementLogic;
import net.neoforged.gradle.common.extensions.problems.ProblemReportingConfigurator;
import net.neoforged.gradle.common.extensions.repository.IvyRepository;
import net.neoforged.gradle.common.extensions.sourcesets.SourceSetDependencyExtensionImpl;
import net.neoforged.gradle.common.extensions.sourcesets.SourceSetInheritanceExtensionImpl;
import net.neoforged.gradle.common.extensions.subsystems.SubsystemsExtension;
import net.neoforged.gradle.common.interfaceinjection.InterfaceInjectionPublishing;
import net.neoforged.gradle.common.rules.LaterAddedReplacedDependencyRule;
import net.neoforged.gradle.common.runs.ide.IdeRunIntegrationManager;
import net.neoforged.gradle.common.runs.run.RunManagerImpl;
Expand All @@ -21,6 +23,7 @@
import net.neoforged.gradle.common.services.caching.CachedExecutionService;
import net.neoforged.gradle.common.tasks.CleanCache;
import net.neoforged.gradle.common.tasks.DisplayMappingsLicenseTask;
import net.neoforged.gradle.common.util.CommonRuntimeTaskUtils;
import net.neoforged.gradle.common.util.ConfigurationUtils;
import net.neoforged.gradle.common.util.run.RunsUtil;
import net.neoforged.gradle.dsl.common.extensions.*;
Expand All @@ -40,7 +43,9 @@
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.problems.Problems;
import org.gradle.api.tasks.Delete;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.jvm.tasks.Jar;
import org.gradle.plugins.ide.eclipse.EclipsePlugin;
import org.gradle.plugins.ide.idea.IdeaPlugin;
import org.jetbrains.gradle.ext.IdeaExtPlugin;
Expand All @@ -49,8 +54,6 @@

public class CommonProjectPlugin implements Plugin<Project> {

public static final String PROBLEM_NAMESPACE = "neoforged.gradle";
public static final String PROBLEM_REPORTER_EXTENSION_NAME = "neogradleProblems";

private final Problems problems;

Expand Down Expand Up @@ -82,15 +85,17 @@ public void apply(Project project) {
project.getExtensions().create(Repository.class, "ivyDummyRepository", IvyRepository.class, project);
project.getExtensions().create(MinecraftArtifactCache.class, "minecraftArtifactCache", MinecraftArtifactCacheExtension.class, project);
project.getExtensions().create(DependencyReplacement.class, "dependencyReplacements", ReplacementLogic.class, project);
project.getExtensions().create(NeoGradleProblemReporter.class, PROBLEM_REPORTER_EXTENSION_NAME, NeoGradleProblemReporter.class, problems.forNamespace(PROBLEM_NAMESPACE));
project.getExtensions().create(AccessTransformers.class, "accessTransformers", AccessTransformersExtension.class, project);
project.getExtensions().create(InterfaceInjections.class, "interfaceInjections", InterfaceInjectionsExtension.class, project);

project.getExtensions().create(Minecraft.class, "minecraft", MinecraftExtension.class, project);
project.getExtensions().create(Mappings.class,"mappings", MappingsExtension.class, project);
project.getExtensions().create(RunTypeManager.class, "runTypeManager", RunTypeManagerImpl.class, project);
project.getExtensions().create(ExtraJarDependencyManager.class, "clientExtraJarDependencyManager", ExtraJarDependencyManager.class, project);
project.getExtensions().create(RunManager.class, "runManager", RunManagerImpl.class, project);

ProblemReportingConfigurator.configureProblemReporting(project, problems);

final ConfigurationData configurationData = project.getExtensions().create(ConfigurationData.class, "configurationData", ConfigurationDataExtension.class, project);

OfficialNamingChannelConfigurator.getInstance().configure(project);
Expand Down Expand Up @@ -131,6 +136,9 @@ public void apply(Project project) {
//Set up publishing for access transformer elements
AccessTransformerPublishing.setup(project);

//Set up publishing for interface injection elements
InterfaceInjectionPublishing.setup(project);

//Set up the IDE run integration manager
IdeRunIntegrationManager.getInstance().setup(project);

Expand All @@ -145,6 +153,10 @@ public void apply(Project project) {
//Set up reporting tasks
project.getTasks().register("runs", RunsReport.class);

//Configure sdk configurations
final SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
sourceSets.all(ConfigurationUtils::getSdkConfiguration);

//Needs to be before after evaluate
ConventionConfigurator.configureConventions(project);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import net.neoforged.gradle.dsl.common.extensions.subsystems.conventions.Runs;
import net.neoforged.gradle.dsl.common.extensions.subsystems.conventions.SourceSets;
import net.neoforged.gradle.dsl.common.extensions.subsystems.conventions.ide.IDEA;
import net.neoforged.gradle.dsl.common.runs.run.Run;
import net.neoforged.gradle.dsl.common.runs.run.RunManager;
import org.gradle.StartParameter;
import org.gradle.TaskExecutionRequest;
Expand Down Expand Up @@ -62,11 +63,23 @@ private static void configureRunConventions(Project project, Conventions convent
final Configuration runRuntimeConfiguration = project.getConfigurations().maybeCreate(configurations.getRunRuntimeConfigurationName().get());

project.getExtensions().configure(RunManager.class, runContainer -> runContainer.configureAll(run -> {
run.getDependencies().getRuntime().add(runRuntimeConfiguration);
}));

final RunManager runManager = project.getExtensions().getByType(RunManager.class);
project.getConfigurations().addRule("Create run specific runtime configuration", configurationName -> {
if (!configurationName.endsWith(configurations.getPerRunRuntimeConfigurationPostFix().get()))
return;

final String runName = configurationName.substring(0, configurationName.length() - configurations.getPerRunRuntimeConfigurationPostFix().get().length());
if (runManager.findByName(runName) == null)
return;

final Run run = runManager.getByName(runName);
final Configuration runSpecificRuntimeConfiguration = project.getConfigurations().maybeCreate(ConfigurationUtils.getRunName(run, configurations.getPerRunRuntimeConfigurationPostFix().get()));

run.getDependencies().getRuntime().add(runRuntimeConfiguration);
run.getDependencies().getRuntime().add(runSpecificRuntimeConfiguration);
}));
});
}

private static void configureIDEConventions(Project project, Conventions conventions) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package net.neoforged.gradle.common.dependency;

import com.google.common.collect.Maps;
import net.neoforged.gradle.common.extensions.NeoGradleProblemReporter;
import net.neoforged.gradle.common.extensions.problems.IProblemReporter;
import net.neoforged.gradle.common.runtime.tasks.GenerateExtraJar;
import net.neoforged.gradle.dsl.common.extensions.MinecraftArtifactCache;
import net.neoforged.gradle.dsl.common.extensions.dependency.replacement.DependencyReplacement;
Expand Down Expand Up @@ -77,7 +77,7 @@ private boolean hasMatchingArtifact(ExternalModuleDependency externalModuleDepen
private ReplacementResult generateReplacement(final Project project, final Dependency dependency) {
final String minecraftVersion = dependency.getVersion();
if (minecraftVersion == null) {
final NeoGradleProblemReporter problemReporter = project.getExtensions().getByType(NeoGradleProblemReporter.class);
final IProblemReporter problemReporter = project.getExtensions().getByType(IProblemReporter.class);
throw problemReporter.throwing(spec -> {
spec.id("dependencies.extra-jar", "missingVersion")
.contextualLabel("Client-Extra Jar: Missing Version")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package net.neoforged.gradle.common.dependency;

import net.neoforged.gradle.common.extensions.JarJarExtension;
import net.neoforged.gradle.common.extensions.NeoGradleProblemReporter;
import net.neoforged.gradle.common.extensions.problems.IProblemReporter;
import net.neoforged.gradle.dsl.common.dependency.DependencyFilter;
import net.neoforged.gradle.dsl.common.dependency.DependencyManagementObject;
import net.neoforged.gradle.dsl.common.dependency.DependencyVersionInformationHandler;
Expand Down Expand Up @@ -42,7 +42,7 @@ public abstract class JarJarArtifacts {
private transient final SetProperty<ResolvedComponentResult> includedRootComponents;
private transient final SetProperty<ResolvedArtifactResult> includedArtifacts;

private final NeoGradleProblemReporter reporter;
private final IProblemReporter reporter;
private final DependencyFilter dependencyFilter;
private final DependencyVersionInformationHandler dependencyVersionInformationHandler;

Expand Down Expand Up @@ -74,7 +74,7 @@ public DependencyVersionInformationHandler getDependencyVersionInformationHandle
}

@Inject
public JarJarArtifacts(NeoGradleProblemReporter reporter) {
public JarJarArtifacts(IProblemReporter reporter) {
this.reporter = reporter;
dependencyFilter = getObjectFactory().newInstance(DefaultDependencyFilter.class);
dependencyVersionInformationHandler = getObjectFactory().newInstance(DefaultDependencyVersionInformationHandler.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package net.neoforged.gradle.common.extensions;

import net.neoforged.gradle.common.CommonProjectPlugin;
import net.neoforged.gradle.common.accesstransformers.AccessTransformerPublishing;
import net.neoforged.gradle.common.extensions.base.BaseFilesWithEntriesExtension;
import net.neoforged.gradle.dsl.common.extensions.AccessTransformers;
import org.gradle.api.Action;
import org.gradle.api.Project;
Expand All @@ -13,14 +11,16 @@

import javax.inject.Inject;

public abstract class AccessTransformersExtension extends BaseFilesWithEntriesExtension<AccessTransformers> implements AccessTransformers {
public abstract class AccessTransformersExtension implements AccessTransformers {
private transient final DependencyHandler projectDependencies;
private transient final ArtifactHandler projectArtifacts;

private final Project project;

@SuppressWarnings("UnstableApiUsage")
@Inject
public AccessTransformersExtension(Project project) {
super(project);
this.project = project;

this.projectDependencies = project.getDependencies();
this.projectArtifacts = project.getArtifacts();
Expand All @@ -32,9 +32,14 @@ public AccessTransformersExtension(Project project) {
});
}

@Override
public Project getProject() {
return project;
}

@Override
public void expose(Object path, Action<ConfigurablePublishArtifact> action) {
file(path);
getFiles().from(path);
projectArtifacts.add(AccessTransformerPublishing.ACCESS_TRANSFORMER_ELEMENTS_CONFIGURATION, path, action);
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.gradle.plugins.ide.eclipse.model.EclipseModel;
import org.gradle.plugins.ide.idea.model.IdeaModel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.gradle.ext.IdeaExtPlugin;
import org.jetbrains.gradle.ext.ProjectSettings;
import org.jetbrains.gradle.ext.TaskTriggersConfig;
Expand Down Expand Up @@ -261,22 +262,30 @@ private void onCommonEclipse(final BiConsumer<Project, EclipseModel> toPerform)
if (!isEclipseImport()) {
return;
}

//Grab the eclipse model so we can extend it. -> Done on the root project so that the model is available to all subprojects.
//And so that post sync tasks are only ran once for all subprojects.
EclipseModel model = project.getExtensions().findByType(EclipseModel.class);
if (model == null) {
model = rootProject.getExtensions().findByType(EclipseModel.class);
if (model == null) {
return;
}
}


EclipseModel model = getEclipseModel();
if (model == null) return;

//Configure the project, passing the model and the relevant project. Which does not need to be the root, but can be.
toPerform.accept(project, model);
});
}


/**
* Get the eclipse model from the extensions project.
*
* @return the eclipse model, or {@code null} if not found
*/
public @Nullable EclipseModel getEclipseModel() {
//Grab the eclipse model so we can extend it. -> Done on the root project so that the model is available to all subprojects.
//And so that post sync tasks are only ran once for all subprojects.
EclipseModel model = project.getExtensions().findByType(EclipseModel.class);
if (model == null) {
model = project.getRootProject().getExtensions().findByType(EclipseModel.class);
}
return model;
}

/**
* Applies the specified configuration action to configure gradle run projects only.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package net.neoforged.gradle.common.extensions;

import net.neoforged.gradle.common.interfaceinjection.InterfaceInjectionPublishing;
import net.neoforged.gradle.dsl.common.extensions.InterfaceInjections;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.artifacts.ConfigurablePublishArtifact;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.dsl.ArtifactHandler;
import org.gradle.api.artifacts.dsl.DependencyHandler;

import javax.inject.Inject;

public abstract class InterfaceInjectionsExtension implements InterfaceInjections {
private transient final DependencyHandler projectDependencies;
private transient final ArtifactHandler projectArtifacts;

private final Project project;

@SuppressWarnings("UnstableApiUsage")
@Inject
public InterfaceInjectionsExtension(Project project) {
this.project = project;

this.projectDependencies = project.getDependencies();
this.projectArtifacts = project.getArtifacts();

// We have to add these after project evaluation because of dependency replacement making configurations non-lazy; adding them earlier would prevent further addition of dependencies
project.afterEvaluate(p -> {
p.getConfigurations().maybeCreate(InterfaceInjectionPublishing.INTERFACE_INJECTION_CONFIGURATION).fromDependencyCollector(getConsume());
p.getConfigurations().maybeCreate(InterfaceInjectionPublishing.INTERFACE_INJECTION_API_CONFIGURATION).fromDependencyCollector(getConsumeApi());
});
}

@Override
public Project getProject() {
return project;
}

@Override
public void expose(Object path, Action<ConfigurablePublishArtifact> action) {
getFiles().from(path);
projectArtifacts.add(InterfaceInjectionPublishing.INTERFACE_INJECTION_ELEMENTS_CONFIGURATION, path, action);
}

@Override
public void expose(Object path) {
expose(path, artifacts -> {
});
}

@Override
public void expose(Dependency dependency) {
projectDependencies.add(InterfaceInjectionPublishing.INTERFACE_INJECTION_API_CONFIGURATION, dependency);
}
}
Loading
Loading