From a33142a5dbc99f365f77fd68bf4f7c197a625e5b Mon Sep 17 00:00:00 2001
From: Gamebuster19901
Date: Fri, 9 Aug 2024 21:21:38 -0400
Subject: [PATCH] Retrieve JarInJar dependencies
---
build.gradle | 1 +
gradle.properties | 2 +-
.../workspace/PluginDependency.java | 1 +
.../workspace/ProjectDependencyType.java | 2 +
.../workspace/WWProjectDependency.java | 18 +-
.../workspace/WilderWorkspacePlugin.java | 366 +------------
.../workspace/WilderWorkspacePluginImpl.java | 493 ++++++++++++++++++
7 files changed, 513 insertions(+), 370 deletions(-)
create mode 100644 src/main/java/com/wildermods/workspace/WilderWorkspacePluginImpl.java
diff --git a/build.gradle b/build.gradle
index 257122d..4568918 100644
--- a/build.gradle
+++ b/build.gradle
@@ -26,6 +26,7 @@ dependencies {
implementation group: 'commons-io', name: 'commons-io', version: commonsIOVersion
implementation group: 'net.fabricmc', name: 'fabric-loom', version: loomVersion
implementation group: 'org.vineflower', name: 'vineflower', version: vineFlowerVersion
+ implementation group: 'com.google.code.gson', name: 'gson', version: gsonVersion
}
import org.apache.tools.ant.filters.ReplaceTokens
diff --git a/gradle.properties b/gradle.properties
index e3e9290..73bc51d 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,7 +6,6 @@ accessWidenerVersion = 2.1.0
tinyMappingsVersion = 0.3.0+
mixinVersion = 0.15.0+mixin.0.8.7
guavaVersion = 33.2.1-jre
-gsonVersion = 2.11.0
providerVersion = 1.4.0.0
asmVersion = 9.7
fabricLoaderVersion = 0.16.0
@@ -15,5 +14,6 @@ fabricLoaderVersion = 0.16.0
commonsIOVersion = 2.16.1
commonsLangVersion = 3.14.0
commonsTextVersion = 1.12.0
+gsonVersion = 2.10.1
vineFlowerVersion = 1.10.1
loomVersion = 1.7.2
\ No newline at end of file
diff --git a/src/main/java/com/wildermods/workspace/PluginDependency.java b/src/main/java/com/wildermods/workspace/PluginDependency.java
index 6b67d06..d6620d1 100644
--- a/src/main/java/com/wildermods/workspace/PluginDependency.java
+++ b/src/main/java/com/wildermods/workspace/PluginDependency.java
@@ -14,6 +14,7 @@ enum PluginDependency implements Dependency {
COMMONS_IO("commons-io", "commons-io", "@commonsIOVersion@"),
COMMONS_LANG("org.apache.commons", "commons-lang3", "@commonsLangVersion@"),
COMMONS_TEXT("org.apache.commons", "commons-text", "@commonsTextVersion@"),
+ GSON("com.google.code.gson", "gson", "@gsonVersion@"),
LOOM("net.fabricmc", "fabric-loom", "@loomVersion@"),
VINEFLOWER("org.vineflower", "vineflower", "@vineFlowerVersion@");
diff --git a/src/main/java/com/wildermods/workspace/ProjectDependencyType.java b/src/main/java/com/wildermods/workspace/ProjectDependencyType.java
index 7b16e98..7ec8ac6 100644
--- a/src/main/java/com/wildermods/workspace/ProjectDependencyType.java
+++ b/src/main/java/com/wildermods/workspace/ProjectDependencyType.java
@@ -45,4 +45,6 @@ public enum ProjectDependencyType {
* project's dependencies to the workspace
*/
excludedFabricDeps,
+
+ retrieveJson
}
diff --git a/src/main/java/com/wildermods/workspace/WWProjectDependency.java b/src/main/java/com/wildermods/workspace/WWProjectDependency.java
index 7eae777..5054f63 100644
--- a/src/main/java/com/wildermods/workspace/WWProjectDependency.java
+++ b/src/main/java/com/wildermods/workspace/WWProjectDependency.java
@@ -32,23 +32,11 @@
*
*/
public enum WWProjectDependency implements Dependency {
-
- commonsText(fabricDep, "org.apache.commons", "commons-text", "@commonsTextVersion@"),
- accessWidener(fabricDep, "net.fabricmc", "access-widener", "@accessWidenerVersion@"),
- tinyMappingsParser(fabricDep, "net.fabricmc", "tiny-mappings-parser", "@tinyMappingsVersion@"),
- mixin(fabricDep, "net.fabricmc", "sponge-mixin", "@mixinVersion@"),
- guava(fabricDep, "com.google.guava", "guava", "@guavaVersion@"),
- gson(fabricDep, "com.google.code.gson", "gson", "@gsonVersion@"),
-
- asm(fabricDep, "org.ow2.asm", "asm", "@asmVersion@"),
- asmAnalysis(fabricDep, "org.ow2.asm", "asm-analysis", "@asmVersion@"),
- asmCommons(fabricDep, "org.ow2.asm", "asm-commons", "@asmVersion@"),
- asmTree(fabricDep, "org.ow2.asm", "asm-tree", "@asmVersion@"),
- asmUtil(fabricDep, "org.ow2.asm", "asm-util", "@asmVersion@"),
-
-
+
fabricLoader(fabricImpl, "net.fabricmc", "fabric-loader", "@fabricLoaderVersion@"),
+ fabricLoaderDepsJson(retrieveJson, "net.fabricmc", "fabric-loader", "@fabricLoaderVersion@"),
gameProvider(fabricImpl, "com.wildermods", "provider", "@providerVersion@", "https://wildermods.com/WildermythGameProvider.git"),
+
;
private final ProjectDependencyType type;
diff --git a/src/main/java/com/wildermods/workspace/WilderWorkspacePlugin.java b/src/main/java/com/wildermods/workspace/WilderWorkspacePlugin.java
index ccb9c78..472f642 100644
--- a/src/main/java/com/wildermods/workspace/WilderWorkspacePlugin.java
+++ b/src/main/java/com/wildermods/workspace/WilderWorkspacePlugin.java
@@ -1,368 +1,26 @@
package com.wildermods.workspace;
-import org.gradle.api.Project;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.dsl.DependencyHandler;
-import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
-import org.gradle.api.initialization.Settings;
-import org.gradle.api.initialization.dsl.ScriptHandler;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.api.tasks.Copy;
-import org.gradle.internal.classloader.VisitableURLClassLoader;
-import org.gradle.plugins.ide.eclipse.EclipsePlugin;
-import org.gradle.plugins.ide.eclipse.model.Classpath;
-import org.gradle.plugins.ide.eclipse.model.ClasspathEntry;
-import org.gradle.plugins.ide.eclipse.model.EclipseClasspath;
-import org.gradle.plugins.ide.eclipse.model.EclipseModel;
-import org.gradle.plugins.ide.eclipse.model.FileReference;
-import org.gradle.plugins.ide.eclipse.model.Library;
-import org.gradle.plugins.ide.idea.IdeaPlugin;
-import org.gradle.vcs.SourceControl;
-
-import com.wildermods.workspace.tasks.ClearLocalRuntimeTask;
-import com.wildermods.workspace.tasks.CopyLocalDependenciesToWorkspaceTask;
-import com.wildermods.workspace.tasks.DecompileJarsTask;
-import com.wildermods.workspace.util.ExceptionUtil;
-
-import java.io.File;
-import java.net.MalformedURLException;
-import java.nio.file.Path;
-import java.util.Set;
-
import org.gradle.api.Plugin;
/**
* A Gradle plugin for setting up and managing the WilderWorkspace development environment.
- *
- * This plugin configures various tasks and settings required for working with the WilderWorkspace project,
- * including setting up dependencies, configuring tasks, and integrating with IDEs like Eclipse and IntelliJ IDEA.
- *
- *
- * It supports:
- *
- * Adding plugin dependencies
- * Setting up configurations and tasks
- * Configuring IDE plugins (Eclipse and IntelliJ IDEA)
- * Handling post-evaluation of the project for dependency management
- *
- *
+ *
+ * This is a dummy class. The actual implementation is in {@link WilderWorkspacePluginImpl}
+ *
+ * This workaround is needed because gradle cannot directly launch and decorate a plugin
+ * that directly classloads dependencies (I.E. Gson, apache commons, etc)
+ *
+ * Basically, if any external dependency required by the plugin is referenced by the
+ * class gradle directly launches, a NoClassDefFoundError occurs.
+ *
*/
public class WilderWorkspacePlugin implements Plugin {
-
- /** The version of the WilderWorkspace plugin. */
- public static final String VERSION = "@workspaceVersion@";
-
- /**
- * Applies the plugin to either a {@link Project} or {@link Settings}.
- *
- * @param object the object to apply the plugin to
- * @throws Error if the object is neither a {@link Project} nor a {@link Settings}
- */
- public void apply(Object object) {
- if(object instanceof Project) {
- apply((Project)object);
- }
- else if (object instanceof Settings) {
- apply((Settings)object);
- }
- else {
- throw new Error("WilderWorkspacePlugin can only be applied to projects and settings");
- }
- }
-
- /**
- * Applies the plugin to the project.
- *
- * This method initializes the plugin, sets up dependencies, creates the WilderWorkspace extension,
- * configures tasks, and handles post-evaluation tasks.
- *
- *
- * @param project the Gradle project to apply the plugin to
- */
- public void apply(Project project) {
-
- project.getLogger().log(LogLevel.INFO, "Initializing WilderWorkspace PROJECT plugin version " + VERSION);
- try {
- addPluginDependencies(project);
-
- WilderWorkspaceExtension extension = project.getExtensions().create("wilderWorkspace", WilderWorkspaceExtension.class);
- extension.loadUserConfig();
-
- WWProjectContext context = new WWProjectContext(project, extension) {};
-
- setupConfigurations(context);
-
- setupTasks(context);
-
- setupPostEvaluations(context);
- }
- catch(Throwable t) {
- Throwable cause = ExceptionUtil.getInitialCause(t);
- if(cause instanceof NoClassDefFoundError) {
- throw new LinkageError("Required class not in classpath.", t);
- }
- throw new LinkageError("Failed to initialize WilderWorkspace " + VERSION, t);
- }
-
- project.getLogger().log(LogLevel.INFO, "Initialized WilderWorkspace plugin version " + VERSION);
- }
-
- /**
- * Applies the plugin to the given settings.
- *
- * This method sets up source control repositories for project dependencies as defined in {@link WWProjectDependency}.
- *
- *
- * @param settings the Gradle settings to apply the plugin to
- */
- public void apply(Settings settings) {
- SourceControl sourceControl = settings.getSourceControl();
- for(WWProjectDependency dependency : WWProjectDependency.values()) {
- if(dependency.getRepo() != null) {
- sourceControl.gitRepository(dependency.getRepo(), repo -> {
- repo.producesModule(dependency.getModule());
- });
- }
- }
- }
-
- /**
- * Adds plugin dependencies to the project's build script classpath.
- *
- * This method configures repositories and adds dependencies required for the plugin.
- *
- *
- * @param project the Gradle project
- */
- private static void addPluginDependencies(Project project) {
- ScriptHandler buildscript = project.getBuildscript();
- {
-
- MavenArtifactRepository fabricRepository = buildscript.getRepositories().maven((c) -> {
- c.setUrl("https://maven.fabricmc.net");
- });
- MavenArtifactRepository mavenCentral = buildscript.getRepositories().mavenCentral();
- MavenArtifactRepository mavenLocal = buildscript.getRepositories().mavenLocal();
-
- DependencyHandler dependencies = buildscript.getDependencies();
- for(PluginDependency dependency : PluginDependency.values()) {
- dependencies.add(ScriptHandler.CLASSPATH_CONFIGURATION, dependency.toString());
- }
- }
-
- buildscript.getConfigurations().forEach((config -> {
- Set dependencies = config.resolve();
- VisitableURLClassLoader classLoader = (VisitableURLClassLoader) WilderWorkspacePlugin.class.getClassLoader();
- for(File dependency : dependencies) {
- try {
- classLoader.addURL(dependency.toURI().toURL());
- } catch (MalformedURLException e) {
- throw new LinkageError("Could not resolve dependency", e);
- }
- }
- }));
- }
-
- /**
- * Sets up configurations required by the plugin.
- *
- * This method creates configurations for Fabric dependencies and implementations.
- *
- *
- * @param context the project context
- */
- private static void setupConfigurations(WWProjectContext context) {
- Project project = context.getProject();
- Configuration fabricDep = project.getConfigurations().create(ProjectDependencyType.fabricDep.name());
- Configuration fabricImpl = project.getConfigurations().create(ProjectDependencyType.fabricImpl.name());
-
- project.getConfigurations().create(ProjectDependencyType.resolvableImplementation.name(), configuration -> {
- configuration.extendsFrom(project.getConfigurations().getByName("implementation"));
- configuration.setCanBeResolved(true);
- configuration.setCanBeConsumed(false);
- });
-
- project.getConfigurations().create(ProjectDependencyType.excludedFabricDeps.name(), configuration -> {
- configuration.extendsFrom(project.getConfigurations().getByName(ProjectDependencyType.fabricDep.name()));
- configuration.extendsFrom(project.getConfigurations().getByName(ProjectDependencyType.fabricImpl.name()));
- configuration.setCanBeResolved(true);
- configuration.setCanBeConsumed(false);
- });
-
- DependencyHandler dependencies = project.getDependencies();
- for(WWProjectDependency dependency : WWProjectDependency.values()) {
- dependencies.add("implementation", dependency.toString());
- }
+ @Override
+ public void apply(Object target) {
+ new WilderWorkspacePluginImpl().apply((Object)target);
}
-
- /**
- * Sets up tasks for the plugin.
- *
- * This method registers tasks for copying local dependencies, decompiling JARs, and configuring the workspace.
- *
- *
- * @param context the project context
- */
- private static void setupTasks(WWProjectContext context) {
- Project project = context.getProject();
- WilderWorkspaceExtension extension = context.getWWExtension();
-
- project.getTasks().register("copyLocalDependenciesToWorkspace", CopyLocalDependenciesToWorkspaceTask.class, task -> {
- task.setPlatform(extension.getPlatform());
- task.setPatchline(extension.getPatchline());
- task.setDestDir(extension.getGameDestDir());
- task.finalizedBy(project.getTasks().getByName("copyFabricDependencies"));
- task.finalizedBy(project.getTasks().getByName("copyFabricImplementors"));
- task.finalizedBy(project.getTasks().getByName("copyProjectDependencies"));
- });
-
- project.getTasks().register("decompileJars", DecompileJarsTask.class, task -> {
- task.setCompiledDir(extension.getGameDestDir());
- task.setDecompDir(extension.getDecompDir());
- });
-
- project.getTasks().register("clearLocalRuntime", ClearLocalRuntimeTask.class, task -> {
- task.setDecompDir(extension.getDecompDir());
- task.setDestDir(extension.getGameDestDir());
- });
-
- project.getTasks().register("setupDecompWorkspace", CopyLocalDependenciesToWorkspaceTask.class, task -> {
- task.setOverwrite(false);
- project.getPlugins().withType(EclipsePlugin.class, eclipsePlugin -> {
- task.dependsOn(project.getTasks().named("eclipse"));
- });
- project.getPlugins().withType(IdeaPlugin.class, ideaPlugin -> {
- task.dependsOn(project.getTasks().named("idea"));
- });
- task.finalizedBy(project.provider(() -> {
- DecompileJarsTask decompileTask = (DecompileJarsTask)project.getTasks().named("decompileJars").get();
- return decompileTask;
- }));
- task.finalizedBy(project.getTasks().getByName("copyFabricDependencies"));
- task.finalizedBy(project.getTasks().getByName("copyFabricImplementors"));
- });
-
- project.getTasks().register("updateDecompWorkspace", CopyLocalDependenciesToWorkspaceTask.class, task -> {
- task.setPlatform(extension.getPlatform());
- task.setPatchline(extension.getPatchline());
- task.setDestDir(extension.getGameDestDir());
- task.setOverwrite(true);
- task.dependsOn(project.getTasks().named("clearLocalDependencies"));
- task.finalizedBy(project.provider(() -> {
- CopyLocalDependenciesToWorkspaceTask copyTask = (CopyLocalDependenciesToWorkspaceTask) project.getTasks().named("copyLocalDependenciesToWorkspace").get();
- copyTask.setOverwrite(true);
- copyTask.finalizedBy(project.provider(() -> {
- DecompileJarsTask decompileTask = (DecompileJarsTask)project.getTasks().named("decompileJars").get();
- return decompileTask;
- }));
- return copyTask;
- }));
- });
-
- project.getTasks().register("copyFabricDependencies", Copy.class, task -> {
- Configuration fabricDep = context.getProject().getConfigurations().getByName(ProjectDependencyType.fabricDep.name());
- task.from(fabricDep);
- task.into(Path.of(extension.getGameDestDir()).resolve("fabric"));
- });
-
- project.getTasks().register("copyFabricImplementors", Copy.class, task -> {
- Configuration fabricImpl = context.getProject().getConfigurations().getByName(ProjectDependencyType.fabricImpl.name());
- task.from(fabricImpl);
- task.into(extension.getGameDestDir());
- });
-
- project.getTasks().register("copyProjectDependencies", Copy.class, task -> {
- Configuration resolvableImplementation = project.getConfigurations().getByName(ProjectDependencyType.resolvableImplementation.name());
- Configuration exclusionConfiguration = project.getConfigurations().getByName(ProjectDependencyType.excludedFabricDeps.name());
-
- // Get the files from the exclusion configuration
- Set exclusionFiles = exclusionConfiguration.getResolvedConfiguration().getFiles();
-
- task.from(resolvableImplementation, copySpec -> {
- // Exclude files that are in the exclusion configuration
- copySpec.exclude(fileTreeElement -> exclusionFiles.contains(fileTreeElement.getFile()));
- });
-
- task.into(Path.of(extension.getGameDestDir()).resolve("fabric"));
- });
- }
-
- /**
- * Sets up post-evaluation tasks for the plugin.
- *
- * This method adds workspace dependencies and configures IDE plugins.
- *
- *
- * @param context the project context
- */
- private static void setupPostEvaluations(WWProjectContext context) {
- addWorkspaceDependencies(context);
- setupEclipsePlugin(context);
- }
-
- /**
- * Adds workspace dependencies to the project after evaluation.
- *
- * This method ensures that project dependencies are added to the correct configurations.
- *
- *
- * @param context the project context
- */
- private static void addWorkspaceDependencies(WWProjectContext context) {
- Project project = context.getProject();
- project.afterEvaluate((proj -> {
- DependencyHandler dependencyHandler = proj.getDependencies();
-
-
-
- for(WWProjectDependency dependency : WWProjectDependency.values()) {
- dependencyHandler.add(dependency.getType().toString(), dependency.toString());
- }
- }));
- }
-
- /**
- * Configures the Eclipse plugin after project evaluation.
- *
- * This method integrates with the Eclipse plugin to set source paths for game JARs.
- *
- *
- * @param context the project context
- */
- @SuppressWarnings({ "unchecked"})
- private static void setupEclipsePlugin(WWProjectContext context) {
- Project project = context.getProject();
- WilderWorkspaceExtension extension = context.getWWExtension();
- project.afterEvaluate(proj -> {
- if (project.getPlugins().hasPlugin("eclipse")) {
- EclipseModel eclipseModel = proj.getExtensions().getByType(EclipseModel.class);
- EclipseClasspath classpath = eclipseModel.getClasspath();
-
- classpath.file(xmlFileContent -> {
- xmlFileContent.getWhenMerged().add((classPathMerged) -> {
- Classpath c = (Classpath) classPathMerged;
- for (ClasspathEntry entry : c.getEntries()) {
- project.getLogger().warn(entry.getClass().getCanonicalName());
- if(entry instanceof Library) {
- Library lib = (Library) entry;
- GameJars gameJar = GameJars.fromPathString(lib.getPath());
- if(gameJar != null) {
- project.getLogger().info("Found a game jar to add sources to: " + lib.getPath());
- FileReference source = c.fileReference(Path.of(extension.getDecompDir()).resolve("decomp").resolve(gameJar.getJarName()).normalize().toAbsolutePath().toFile());
- lib.setSourcePath(source);
- project.getLogger().info("Setting sources of " + gameJar + " to " + source.getPath());
- }
- }
- }
- });
- });
- } else {
- project.getLogger().warn("Eclipse plugin is not applied. The eclipse source attachment will not be configured.");
- }
- });
- }
}
diff --git a/src/main/java/com/wildermods/workspace/WilderWorkspacePluginImpl.java b/src/main/java/com/wildermods/workspace/WilderWorkspacePluginImpl.java
new file mode 100644
index 0000000..7ddfddd
--- /dev/null
+++ b/src/main/java/com/wildermods/workspace/WilderWorkspacePluginImpl.java
@@ -0,0 +1,493 @@
+package com.wildermods.workspace;
+
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
+import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
+import org.gradle.api.initialization.Settings;
+import org.gradle.api.initialization.dsl.ScriptHandler;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.tasks.Copy;
+import org.gradle.internal.classloader.VisitableURLClassLoader;
+import org.gradle.plugins.ide.eclipse.EclipsePlugin;
+import org.gradle.plugins.ide.eclipse.model.Classpath;
+import org.gradle.plugins.ide.eclipse.model.ClasspathEntry;
+import org.gradle.plugins.ide.eclipse.model.EclipseClasspath;
+import org.gradle.plugins.ide.eclipse.model.EclipseModel;
+import org.gradle.plugins.ide.eclipse.model.FileReference;
+import org.gradle.plugins.ide.eclipse.model.Library;
+import org.gradle.plugins.ide.idea.IdeaPlugin;
+import org.gradle.vcs.SourceControl;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.wildermods.workspace.tasks.ClearLocalRuntimeTask;
+import com.wildermods.workspace.tasks.CopyLocalDependenciesToWorkspaceTask;
+import com.wildermods.workspace.tasks.DecompileJarsTask;
+import com.wildermods.workspace.util.ExceptionUtil;
+
+import java.io.File;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import org.gradle.api.Plugin;
+
+/**
+ * A Gradle plugin for setting up and managing the WilderWorkspace development environment.
+ *
+ * This plugin configures various tasks and settings required for working with the WilderWorkspace project,
+ * including setting up dependencies, configuring tasks, and integrating with IDEs like Eclipse and IntelliJ IDEA.
+ *
+ *
+ * It supports:
+ *
+ * Adding plugin dependencies
+ * Setting up configurations and tasks
+ * Configuring IDE plugins (Eclipse and IntelliJ IDEA)
+ * Handling post-evaluation of the project for dependency management
+ *
+ *
+ */
+
+public class WilderWorkspacePluginImpl implements Plugin {
+
+ /** The version of the WilderWorkspace plugin. */
+ public static final String VERSION = "@workspaceVersion@";
+
+ private Configuration implementation;
+ private Configuration fabricDep;
+ private Configuration fabricImpl;
+ private Configuration retrieveJson;
+ private Configuration resolvableImplementation;
+ private Configuration excludedFabricDeps;
+
+ /**
+ * Applies the plugin to either a {@link Project} or {@link Settings}.
+ *
+ * @param object the object to apply the plugin to
+ * @throws Error if the object is neither a {@link Project} nor a {@link Settings}
+ */
+ public void apply(Object object) {
+ if(object instanceof Project) {
+ apply((Project)object);
+ }
+ else if (object instanceof Settings) {
+ apply((Settings)object);
+ }
+ else {
+ throw new Error("WilderWorkspacePlugin can only be applied to projects and settings");
+ }
+ }
+
+ /**
+ * Applies the plugin to the project.
+ *
+ * This method initializes the plugin, sets up dependencies, creates the WilderWorkspace extension,
+ * configures tasks, and handles post-evaluation tasks.
+ *
+ *
+ * @param project the Gradle project to apply the plugin to
+ */
+ public void apply(Project project) {
+
+ project.getLogger().log(LogLevel.INFO, "Initializing WilderWorkspace PROJECT plugin version " + VERSION);
+ try {
+
+ addPluginDependencies(project);
+
+ WilderWorkspaceExtension extension = project.getExtensions().create("wilderWorkspace", WilderWorkspaceExtension.class);
+ extension.loadUserConfig();
+
+ WWProjectContext context = new WWProjectContext(project, extension) {};
+
+ setupRepositories(context);
+
+ project.getLogger().log(LogLevel.INFO, "SETTING UP CONFIGURATIONS ");
+ setupConfigurations(context);
+
+ setupTasks(context);
+
+ setupPostEvaluations(context);
+ }
+ catch(Throwable t) {
+ Throwable cause = ExceptionUtil.getInitialCause(t);
+ if(cause instanceof NoClassDefFoundError) {
+ throw new LinkageError("Required class not in classpath.", t);
+ }
+ throw new LinkageError("Failed to apply WilderWorkspace " + VERSION, t);
+ }
+ }
+
+ /**
+ * Applies the plugin to the given settings.
+ *
+ * This method sets up source control repositories for project dependencies as defined in {@link WWProjectDependency}.
+ *
+ *
+ * @param settings the Gradle settings to apply the plugin to
+ */
+ public void apply(Settings settings) {
+ SourceControl sourceControl = settings.getSourceControl();
+ for(WWProjectDependency dependency : WWProjectDependency.values()) {
+ if(dependency.getRepo() != null) {
+ sourceControl.gitRepository(dependency.getRepo(), repo -> {
+ repo.producesModule(dependency.getModule());
+ });
+ }
+ }
+ }
+
+ /**
+ * Adds plugin dependencies to the project's build script classpath.
+ *
+ * This method configures repositories and adds dependencies required for the plugin.
+ *
+ *
+ * @param project the Gradle project
+ */
+ private void addPluginDependencies(Project project) {
+ ScriptHandler buildscript = project.getBuildscript();
+ {
+
+ MavenArtifactRepository fabricRepository = buildscript.getRepositories().maven((c) -> {
+ c.setUrl("https://maven.fabricmc.net");
+ });
+ MavenArtifactRepository mavenCentral = buildscript.getRepositories().mavenCentral();
+ MavenArtifactRepository mavenLocal = buildscript.getRepositories().mavenLocal();
+
+ DependencyHandler dependencies = buildscript.getDependencies();
+ for(PluginDependency dependency : PluginDependency.values()) {
+ dependencies.add(ScriptHandler.CLASSPATH_CONFIGURATION, dependency.toString());
+ }
+ }
+
+ buildscript.getConfigurations().forEach((config -> {
+ Set dependencies = config.resolve();
+ VisitableURLClassLoader classLoader = (VisitableURLClassLoader) WilderWorkspacePlugin.class.getClassLoader();
+ for(File dependency : dependencies) {
+ try {
+ classLoader.addURL(dependency.toURI().toURL());
+ } catch (MalformedURLException e) {
+ throw new LinkageError("Could not resolve dependency", e);
+ }
+ }
+ }));
+ }
+
+ private void setupRepositories(WWProjectContext context) {
+ Project project = context.getProject();
+ RepositoryHandler repositories = project.getRepositories();
+ repositories.add(repositories.mavenCentral());
+ repositories.add(repositories.maven((repo) -> {
+ repo.setUrl("https://maven.fabricmc.net/");
+ }));
+ }
+
+ /**
+ * Sets up configurations required by the plugin.
+ *
+ * This method creates configurations for Fabric dependencies and implementations.
+ *
+ *
+ * @param context the project context
+ */
+ private void setupConfigurations(WWProjectContext context) {
+ Project project = context.getProject();
+ implementation = project.getConfigurations().getByName(ProjectDependencyType.implementation.name());
+ fabricDep = project.getConfigurations().create(ProjectDependencyType.fabricDep.name());
+ fabricImpl = project.getConfigurations().create(ProjectDependencyType.fabricImpl.name());
+ retrieveJson = project.getConfigurations().create(ProjectDependencyType.retrieveJson.name(), configuration -> {
+ configuration.setCanBeResolved(true);
+ configuration.setCanBeConsumed(false);
+ });
+
+ resolvableImplementation = project.getConfigurations().create(ProjectDependencyType.resolvableImplementation.name(), configuration -> {
+ configuration.extendsFrom(implementation);
+ configuration.setCanBeResolved(true);
+ configuration.setCanBeConsumed(false);
+ });
+
+ excludedFabricDeps = project.getConfigurations().create(ProjectDependencyType.excludedFabricDeps.name(), configuration -> {
+ configuration.extendsFrom(project.getConfigurations().getByName(ProjectDependencyType.fabricDep.name()));
+ configuration.extendsFrom(project.getConfigurations().getByName(ProjectDependencyType.fabricImpl.name()));
+ configuration.setCanBeResolved(true);
+ configuration.setCanBeConsumed(false);
+ });
+
+ DependencyHandler dependencies = project.getDependencies();
+ for(WWProjectDependency dependency : WWProjectDependency.values()) {
+ project.getLogger().log(LogLevel.INFO, "ADDING " + dependency.getModule() + ":" + dependency.getVersion() + " TO " + dependency.getType() + " CONFIGURATION");
+ dependencies.add(dependency.getType().name(), dependency.toString());
+ project.getLogger().log(LogLevel.INFO, "ADDING " + dependency.getModule() + ":" + dependency.getVersion() + " TO " + implementation + " CONFIGURATION");
+ dependencies.add(implementation.getName(), dependency.toString());
+ }
+
+ project.getLogger().log(LogLevel.INFO, "ADDING JSON DEPENDENCIES " + VERSION);
+ addJsonDependencies(context);
+
+ }
+
+ private void addJsonDependencies(WWProjectContext context) {
+ Project project = context.getProject();
+ RepositoryHandler repositories = project.getRepositories();
+ Set addedRepositories = new HashSet<>();
+
+ project.getLogger().log(LogLevel.INFO, "Searching for json dependencies...");
+
+ // Iterate over dependencies in retrieveJson configuration
+ for (Dependency jsonDependency : retrieveJson.getAllDependencies()) {
+ project.getLogger().info("Found json dependency: " + jsonDependency);
+ // Construct the JSON file URL
+ String jsonUrlPath = jsonDependency.getGroup().replace('.', '/') + "/"
+ + jsonDependency.getName() + "/" + jsonDependency.getVersion() + "/"
+ + jsonDependency.getName() + "-" + jsonDependency.getVersion() + ".json";
+
+ Optional repositoryOpt = repositories.withType(MavenArtifactRepository.class).stream()
+ .filter(repo -> canResolveUrl(context, repo.getUrl().toString() + jsonUrlPath))
+ .findFirst();
+
+ if (!repositoryOpt.isPresent()) {
+ throw new RuntimeException("Unable to determine the repository URL for the JSON dependency: "
+ + jsonDependency.getGroup() + ":" + jsonDependency.getName() + ":" + jsonDependency.getVersion());
+ }
+
+ // Get the repository URL
+ MavenArtifactRepository repository = repositoryOpt.get();
+ String jsonUrl = repository.getUrl().toString() + jsonUrlPath;
+
+ try {
+ // Download and parse the JSON file
+ InputStreamReader reader = new InputStreamReader(new URL(jsonUrl).openStream());
+ JsonObject dependencyJsonData = JsonParser.parseReader(reader).getAsJsonObject();
+
+ JsonObject libraries = dependencyJsonData.getAsJsonObject("libraries");
+ libraries.entrySet().forEach((entry) -> {
+ if(entry.getValue().isJsonArray()) {
+ JsonArray dependencyGroup = entry.getValue().getAsJsonArray();
+ addDependenciesToFabricDep(project, jsonDependency, dependencyGroup);
+ }
+ });
+
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to process JSON dependency: " + jsonUrl, e);
+ }
+ }
+
+ project.getLogger().log(LogLevel.INFO, "Found all json dependnecies");
+ }
+
+ private void addDependenciesToFabricDep(Project project, Dependency superDependency, JsonArray libraries) {
+ DependencyHandler dependencies = project.getDependencies();
+ for (JsonElement element : libraries) {
+ JsonObject lib = element.getAsJsonObject();
+ String dependencyNotation = lib.get("name").getAsString();
+
+ // Add dependency to fabricDep and implementation configuration
+ try {
+ dependencies.add(fabricDep.getName(), dependencyNotation);
+ dependencies.add(implementation.getName(), dependencyNotation);
+ project.getLogger().log(LogLevel.INFO, "[" + superDependency.getGroup() + ":" + superDependency.getName() + "]: Registered subdependency " + dependencyNotation);
+
+ }
+ catch(Exception e) {
+ throw new RuntimeException("[" + superDependency.getGroup() + ":" + superDependency.getName() + "]: Could not register dependency " + dependencyNotation, e);
+ }
+ }
+ }
+
+ /**
+ * Sets up tasks for the plugin.
+ *
+ * This method registers tasks for copying local dependencies, decompiling JARs, and configuring the workspace.
+ *
+ *
+ * @param context the project context
+ */
+ private void setupTasks(WWProjectContext context) {
+ Project project = context.getProject();
+ WilderWorkspaceExtension extension = context.getWWExtension();
+
+ project.getTasks().register("copyLocalDependenciesToWorkspace", CopyLocalDependenciesToWorkspaceTask.class, task -> {
+ task.setPlatform(extension.getPlatform());
+ task.setPatchline(extension.getPatchline());
+ task.setDestDir(extension.getGameDestDir());
+ task.finalizedBy(project.getTasks().getByName("copyFabricDependencies"));
+ task.finalizedBy(project.getTasks().getByName("copyFabricImplementors"));
+ task.finalizedBy(project.getTasks().getByName("copyProjectDependencies"));
+ });
+
+ project.getTasks().register("decompileJars", DecompileJarsTask.class, task -> {
+ task.setCompiledDir(extension.getGameDestDir());
+ task.setDecompDir(extension.getDecompDir());
+ });
+
+ project.getTasks().register("clearLocalRuntime", ClearLocalRuntimeTask.class, task -> {
+ task.setDecompDir(extension.getDecompDir());
+ task.setDestDir(extension.getGameDestDir());
+ });
+
+ project.getTasks().register("setupDecompWorkspace", CopyLocalDependenciesToWorkspaceTask.class, task -> {
+ task.setOverwrite(false);
+ task.finalizedBy(project.provider(() -> {
+ DecompileJarsTask decompileTask = (DecompileJarsTask)project.getTasks().named("decompileJars").get();
+ return decompileTask;
+ }));
+ task.finalizedBy(project.getTasks().getByName("copyLocalDependenciesToWorkspace"));
+ project.getPlugins().withType(EclipsePlugin.class, eclipsePlugin -> {
+ task.finalizedBy(project.getTasks().named("eclipse"));
+ });
+ project.getPlugins().withType(IdeaPlugin.class, ideaPlugin -> {
+ task.finalizedBy(project.getTasks().named("idea"));
+ });
+ });
+
+ //This task isn't implemented correctly yet, so we're not including it.
+
+ /**
+ project.getTasks().register("updateDecompWorkspace", CopyLocalDependenciesToWorkspaceTask.class, task -> {
+ task.setPlatform(extension.getPlatform());
+ task.setPatchline(extension.getPatchline());
+ task.setDestDir(extension.getGameDestDir());
+ task.setOverwrite(true);
+ task.dependsOn(project.getTasks().named("clearLocalDependencies"));
+ task.finalizedBy(project.provider(() -> {
+ CopyLocalDependenciesToWorkspaceTask copyTask = (CopyLocalDependenciesToWorkspaceTask) project.getTasks().named("copyLocalDependenciesToWorkspace").get();
+ copyTask.setOverwrite(true);
+ copyTask.finalizedBy(project.provider(() -> {
+ DecompileJarsTask decompileTask = (DecompileJarsTask)project.getTasks().named("decompileJars").get();
+ return decompileTask;
+ }));
+ return copyTask;
+ }));
+ });
+ */
+
+ project.getTasks().register("copyFabricDependencies", Copy.class, task -> {
+ task.from(fabricDep);
+ task.from(retrieveJson);
+ task.into(Path.of(extension.getGameDestDir()).resolve("fabric"));
+ });
+
+ project.getTasks().register("copyFabricImplementors", Copy.class, task -> {
+ task.from(fabricImpl);
+ task.into(extension.getGameDestDir());
+ });
+
+ project.getTasks().register("copyProjectDependencies", Copy.class, task -> {
+ task.doFirst(t -> {
+ // Get the files from the exclusion configuration
+ Set exclusionFiles = excludedFabricDeps.getResolvedConfiguration().getFiles();
+
+ task.from(resolvableImplementation, copySpec -> {
+ // Exclude files that are in the exclusion configuration
+ copySpec.exclude(fileTreeElement -> exclusionFiles.contains(fileTreeElement.getFile()));
+ });
+
+ task.into(Path.of(extension.getGameDestDir()).resolve("fabric"));
+ });
+ });
+
+ }
+
+ /**
+ * Sets up post-evaluation tasks for the plugin.
+ *
+ * This method adds workspace dependencies and configures IDE plugins.
+ *
+ *
+ * @param context the project context
+ */
+ private void setupPostEvaluations(WWProjectContext context) {
+ addWorkspaceDependencies(context);
+ setupEclipsePlugin(context);
+ }
+
+ /**
+ * Adds workspace dependencies to the project after evaluation.
+ *
+ * This method ensures that project dependencies are added to the correct configurations.
+ *
+ *
+ * @param context the project context
+ */
+ private void addWorkspaceDependencies(WWProjectContext context) {
+ Project project = context.getProject();
+ project.afterEvaluate((proj -> {
+ DependencyHandler dependencyHandler = proj.getDependencies();
+
+
+
+ for(WWProjectDependency dependency : WWProjectDependency.values()) {
+ dependencyHandler.add(dependency.getType().toString(), dependency.toString());
+ }
+ }));
+ }
+
+ /**
+ * Configures the Eclipse plugin after project evaluation.
+ *
+ * This method integrates with the Eclipse plugin to set source paths for game JARs.
+ *
+ *
+ * @param context the project context
+ */
+ @SuppressWarnings({ "unchecked"})
+ private void setupEclipsePlugin(WWProjectContext context) {
+ Project project = context.getProject();
+ WilderWorkspaceExtension extension = context.getWWExtension();
+ project.afterEvaluate(proj -> {
+ if (project.getPlugins().hasPlugin("eclipse")) {
+ EclipseModel eclipseModel = proj.getExtensions().getByType(EclipseModel.class);
+ EclipseClasspath classpath = eclipseModel.getClasspath();
+
+ classpath.file(xmlFileContent -> {
+ xmlFileContent.getWhenMerged().add((classPathMerged) -> {
+ Classpath c = (Classpath) classPathMerged;
+ for (ClasspathEntry entry : c.getEntries()) {
+ project.getLogger().warn(entry.getClass().getCanonicalName());
+ if(entry instanceof Library) {
+ Library lib = (Library) entry;
+ GameJars gameJar = GameJars.fromPathString(lib.getPath());
+ if(gameJar != null) {
+ project.getLogger().info("Found a game jar to add sources to: " + lib.getPath());
+ FileReference source = c.fileReference(Path.of(extension.getDecompDir()).resolve("decomp").resolve(gameJar.getJarName()).normalize().toAbsolutePath().toFile());
+ lib.setSourcePath(source);
+ project.getLogger().info("Setting sources of " + gameJar + " to " + source.getPath());
+ }
+ }
+ }
+ });
+ });
+
+ } else {
+ project.getLogger().warn("Eclipse plugin is not applied. The eclipse source attachment will not be configured.");
+ }
+ });
+ }
+
+ private boolean canResolveUrl(WWProjectContext context, String url) {
+ try {
+ context.getProject().getLogger().log(LogLevel.INFO, "Checking " + url);
+ HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
+ connection.setRequestMethod("HEAD");
+ int responseCode = connection.getResponseCode();
+ context.getProject().getLogger().log(LogLevel.INFO, "Got response " + responseCode);
+ return responseCode == HttpsURLConnection.HTTP_OK;
+ } catch (Exception e) {
+ context.getProject().getLogger().log(LogLevel.INFO, "Could not open " + url, e);
+ return false;
+ }
+ }
+
+}