mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-28 04:07:01 -05:00
Support classpath groups when using configure on demand. (#1392)
* Support classpath groups when using configure on demand. * Cleanup * Work around Gradle 8.14 issue * Another fix * Rename plugin * Fix plugin versioning * Add some docs * More fixes * Ensure backwards compatible.
This commit is contained in:
42
build.gradle
42
build.gradle
@@ -237,6 +237,10 @@ gradlePlugin {
|
||||
id = 'fabric-loom'
|
||||
implementationClass = 'net.fabricmc.loom.LoomGradlePlugin'
|
||||
}
|
||||
fabricLoomCompanion {
|
||||
id = 'net.fabricmc.fabric-loom-companion'
|
||||
implementationClass = 'net.fabricmc.loom.LoomCompanionGradlePlugin'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,25 +296,27 @@ publishing {
|
||||
from components.java
|
||||
}
|
||||
|
||||
// Manually crate the plugin marker for snapshot versions
|
||||
snapshotPlugin(MavenPublication) { publication ->
|
||||
groupId = 'fabric-loom'
|
||||
artifactId = 'fabric-loom.gradle.plugin'
|
||||
version = baseVersion + '-SNAPSHOT'
|
||||
gradlePlugin.plugins.forEach { plugin ->
|
||||
// Manually crate the plugin marker for snapshot versions
|
||||
it.create(plugin.id + "SnapshotMarker", MavenPublication) { publication ->
|
||||
groupId = plugin.id
|
||||
artifactId = plugin.id + '.gradle.plugin'
|
||||
version = baseVersion + '-SNAPSHOT'
|
||||
|
||||
pom.withXml({
|
||||
// Based off org.gradle.plugin.devel.plugins.MavenPluginPublishPlugin
|
||||
Element root = asElement()
|
||||
Document document = root.getOwnerDocument()
|
||||
Node dependencies = root.appendChild(document.createElement('dependencies'))
|
||||
Node dependency = dependencies.appendChild(document.createElement('dependency'))
|
||||
Node groupId = dependency.appendChild(document.createElement('groupId'))
|
||||
groupId.setTextContent('net.fabricmc')
|
||||
Node artifactId = dependency.appendChild(document.createElement('artifactId'))
|
||||
artifactId.setTextContent('fabric-loom')
|
||||
Node version = dependency.appendChild(document.createElement('version'))
|
||||
version.setTextContent(baseVersion + '-SNAPSHOT')
|
||||
})
|
||||
pom.withXml({
|
||||
// Based off org.gradle.plugin.devel.plugins.MavenPluginPublishPlugin
|
||||
Element root = asElement()
|
||||
Document document = root.getOwnerDocument()
|
||||
Node dependencies = root.appendChild(document.createElement('dependencies'))
|
||||
Node dependency = dependencies.appendChild(document.createElement('dependency'))
|
||||
Node groupId = dependency.appendChild(document.createElement('groupId'))
|
||||
groupId.setTextContent('net.fabricmc')
|
||||
Node artifactId = dependency.appendChild(document.createElement('artifactId'))
|
||||
artifactId.setTextContent('fabric-loom')
|
||||
Node version = dependency.appendChild(document.createElement('version'))
|
||||
version.setTextContent(project.version)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2025 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import net.fabricmc.loom.configuration.LoomConfigurations;
|
||||
import net.fabricmc.loom.task.launch.ExportClasspathTask;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
|
||||
public class LoomCompanionGradlePlugin implements Plugin<Project> {
|
||||
public static final String NAME = "net.fabricmc.fabric-loom-companion";
|
||||
|
||||
@Override
|
||||
public void apply(@NotNull Project project) {
|
||||
var exportClassPathTask = project.getTasks().register(Constants.Task.EXPORT_CLASSPATH, ExportClasspathTask.class);
|
||||
project.getConfigurations().register(Constants.Configurations.EXPORTED_CLASSPATH, LoomConfigurations.Role.CONSUMABLE::apply);
|
||||
project.artifacts(artifactHandler -> artifactHandler.add(Constants.Configurations.EXPORTED_CLASSPATH, exportClassPathTask));
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,7 @@ import net.fabricmc.loom.task.RemapTaskConfiguration;
|
||||
import net.fabricmc.loom.util.LibraryLocationLogger;
|
||||
|
||||
public class LoomGradlePlugin implements Plugin<PluginAware> {
|
||||
public static final String NAME = "fabric-loom";
|
||||
public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
public static final String LOOM_VERSION = Objects.requireNonNullElse(LoomGradlePlugin.class.getPackage().getImplementationVersion(), "0.0.0+unknown");
|
||||
|
||||
@@ -92,5 +93,7 @@ public class LoomGradlePlugin implements Plugin<PluginAware> {
|
||||
for (Class<? extends Runnable> jobClass : SETUP_JOBS) {
|
||||
project.getObjects().newInstance(jobClass).run();
|
||||
}
|
||||
|
||||
project.apply(Map.of("plugin", LoomCompanionGradlePlugin.NAME));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
|
||||
package net.fabricmc.loom.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.gradle.api.Named;
|
||||
@@ -35,6 +39,8 @@ import org.gradle.api.provider.ListProperty;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import net.fabricmc.loom.LoomCompanionGradlePlugin;
|
||||
import net.fabricmc.loom.configuration.classpathgroups.ExternalClasspathGroup;
|
||||
import net.fabricmc.loom.util.gradle.SourceSetHelper;
|
||||
import net.fabricmc.loom.util.gradle.SourceSetReference;
|
||||
|
||||
@@ -49,7 +55,7 @@ public abstract class ModSettings implements Named {
|
||||
|
||||
@Inject
|
||||
public ModSettings() {
|
||||
getModSourceSets().finalizeValueOnRead();
|
||||
getExternalGroups().finalizeValueOnRead();
|
||||
getModFiles().finalizeValueOnRead();
|
||||
}
|
||||
|
||||
@@ -82,18 +88,46 @@ public abstract class ModSettings implements Named {
|
||||
|
||||
/**
|
||||
* Add {@link SourceSet}'s output directories from the supplied project to be grouped with the named mod.
|
||||
* @deprecated Replaced with {@link #sourceSet(String, String)} to avoid passing a project reference.
|
||||
*/
|
||||
@Deprecated
|
||||
public void sourceSet(SourceSet sourceSet, Project project) {
|
||||
getModSourceSets().add(new SourceSetReference(sourceSet, project));
|
||||
ensureCompanion(project);
|
||||
|
||||
sourceSet(sourceSet.getName(), project.getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link SourceSet}'s output directories from the supplied project to be grouped with the named mod.
|
||||
*
|
||||
* @param name the name of the source set
|
||||
* @deprecated Replaced with {@link #sourceSet(String, String)} to avoid passing a project reference.
|
||||
*/
|
||||
@Deprecated
|
||||
public void sourceSet(String name, Project project) {
|
||||
sourceSet(SourceSetHelper.getSourceSetByName(name, project), project);
|
||||
ensureCompanion(project);
|
||||
|
||||
sourceSet(name, project.getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link SourceSet}'s output directories from the supplied project to be grouped with the named mod.
|
||||
*
|
||||
* <p>If the other project is not a Loom project you must apply the `net.fabricmc.fabric-loom-companion` plugin.
|
||||
*
|
||||
* @param sourceSetName the name of the source set
|
||||
* @param projectPath the path of the project the source set belongs to
|
||||
*/
|
||||
public void sourceSet(String sourceSetName, String projectPath) {
|
||||
if (projectPath.equals(getProject().getPath())) {
|
||||
// Shortcut for source sets in our own project.
|
||||
SourceSetReference ref = new SourceSetReference(SourceSetHelper.getSourceSetByName(sourceSetName, getProject()), getProject());
|
||||
List<File> classpath = SourceSetHelper.getClasspath(ref);
|
||||
getModFiles().from(classpath);
|
||||
return;
|
||||
}
|
||||
|
||||
getExternalGroups().add(new ExternalClasspathGroup(projectPath, sourceSetName));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,11 +148,10 @@ public abstract class ModSettings implements Named {
|
||||
}
|
||||
|
||||
/**
|
||||
* List of classpath directories, used to populate the `fabric.classPathGroups` Fabric Loader system property.
|
||||
* Use the {@link ModSettings#sourceSet} methods to add to this.
|
||||
* List of {@link ExternalClasspathGroup} that will later be resolved to populate the classpath groups from another Gradle project.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public abstract ListProperty<SourceSetReference> getModSourceSets();
|
||||
public abstract ListProperty<ExternalClasspathGroup> getExternalGroups();
|
||||
|
||||
@Inject
|
||||
public abstract Project getProject();
|
||||
@@ -127,4 +160,12 @@ public abstract class ModSettings implements Named {
|
||||
public String toString() {
|
||||
return "ModSettings '" + getName() + "'";
|
||||
}
|
||||
|
||||
private void ensureCompanion(Project project) {
|
||||
if (project == getProject()) {
|
||||
return;
|
||||
}
|
||||
|
||||
project.apply(Map.of("plugin", LoomCompanionGradlePlugin.NAME));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ package net.fabricmc.loom.configuration;
|
||||
|
||||
import static net.fabricmc.loom.util.Constants.Configurations;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -36,12 +35,13 @@ import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.logging.Logger;
|
||||
import org.gradle.api.logging.Logging;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
@@ -71,6 +71,7 @@ import net.fabricmc.loom.configuration.providers.minecraft.mapped.AbstractMapped
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider;
|
||||
import net.fabricmc.loom.extension.MixinExtension;
|
||||
import net.fabricmc.loom.task.service.ClasspathGroupService;
|
||||
import net.fabricmc.loom.util.Checksum;
|
||||
import net.fabricmc.loom.util.ExceptionUtil;
|
||||
import net.fabricmc.loom.util.ProcessUtil;
|
||||
@@ -266,15 +267,22 @@ public abstract class CompileConfiguration implements Runnable {
|
||||
}
|
||||
|
||||
getProject().getTasks().named(JavaPlugin.TEST_TASK_NAME, Test.class, test -> {
|
||||
String classPathGroups = extension.getMods().stream()
|
||||
.map(modSettings ->
|
||||
SourceSetHelper.getClasspath(modSettings, getProject()).stream()
|
||||
.map(File::getAbsolutePath)
|
||||
.collect(Collectors.joining(File.pathSeparator))
|
||||
)
|
||||
.collect(Collectors.joining(File.pathSeparator+File.pathSeparator));;
|
||||
test.getInputs().property("LoomClassPathGroups", ClasspathGroupService.create(getProject()));
|
||||
test.doFirst(new Action<Task>() {
|
||||
@Override
|
||||
public void execute(Task task) {
|
||||
try (ScopedServiceFactory serviceFactory = new ScopedServiceFactory()) {
|
||||
var options = (ClasspathGroupService.Options) task.getInputs().getProperties().get("LoomClassPathGroups");
|
||||
ClasspathGroupService classpathGroupService = serviceFactory.get(options);
|
||||
|
||||
test.systemProperty("fabric.classPathGroups", classPathGroups);
|
||||
if (classpathGroupService.hasGroups()) {
|
||||
test.systemProperty("fabric.classPathGroups", classpathGroupService.getClasspathGroupsPropertyValue());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to get classpath groups", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ public abstract class LoomConfigurations implements Runnable {
|
||||
getConfigurations().getByName(a, configuration -> configuration.extendsFrom(getConfigurations().getByName(b)));
|
||||
}
|
||||
|
||||
enum Role {
|
||||
public enum Role {
|
||||
NONE(false, false),
|
||||
CONSUMABLE(true, false),
|
||||
RESOLVABLE(false, true);
|
||||
@@ -188,7 +188,7 @@ public abstract class LoomConfigurations implements Runnable {
|
||||
this.canBeResolved = canBeResolved;
|
||||
}
|
||||
|
||||
void apply(Configuration configuration) {
|
||||
public void apply(Configuration configuration) {
|
||||
configuration.setCanBeConsumed(canBeConsumed);
|
||||
configuration.setCanBeResolved(canBeResolved);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2025 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.configuration.classpathgroups;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.fabricmc.loom.api.ModSettings;
|
||||
|
||||
public record ClasspathGroup(List<String> paths, List<ExternalClasspathGroup> externalGroups) implements Serializable {
|
||||
public static List<ClasspathGroup> fromModSettings(Set<ModSettings> modSettings) {
|
||||
return modSettings.stream().map(s -> new ClasspathGroup(getPaths(s), s.getExternalGroups().get())).toList();
|
||||
}
|
||||
|
||||
// TODO remove this constructor when updating to Gradle 9.0, works around an issue where config cache cannot serialize immutable lists
|
||||
public ClasspathGroup(List<String> paths, List<ExternalClasspathGroup> externalGroups) {
|
||||
this.paths = new ArrayList<>(paths);
|
||||
this.externalGroups = new ArrayList<>(externalGroups);
|
||||
}
|
||||
|
||||
private static List<String> getPaths(ModSettings modSettings) {
|
||||
return modSettings.getModFiles()
|
||||
.getFiles()
|
||||
.stream()
|
||||
.map(File::getAbsolutePath)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2025 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.configuration.classpathgroups;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public record ExternalClasspathGroup(String projectPath, String sourceSetName) implements Serializable {
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2025 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.configuration.classpathgroups;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.SourceSetContainer;
|
||||
|
||||
import net.fabricmc.loom.LoomGradlePlugin;
|
||||
import net.fabricmc.loom.util.gradle.SourceSetHelper;
|
||||
import net.fabricmc.loom.util.gradle.SourceSetReference;
|
||||
|
||||
/**
|
||||
* This object is exported by projects as a json file to be consumed by others to correctly populate the classpath groups.
|
||||
*/
|
||||
public record ExternalClasspathGroupDTO(String projectPath, Map<String, List<String>> classpaths) implements Serializable {
|
||||
public static ExternalClasspathGroupDTO createFromProject(Project project) {
|
||||
SourceSetContainer sourceSets = SourceSetHelper.getSourceSets(project);
|
||||
|
||||
Map<String, List<String>> classpaths = new HashMap<>();
|
||||
|
||||
for (SourceSet sourceSet : sourceSets) {
|
||||
SourceSetReference ref = new SourceSetReference(sourceSet, project);
|
||||
List<File> classpath = SourceSetHelper.getClasspath(ref);
|
||||
classpaths.put(sourceSet.getName(), classpath.stream().map(File::getAbsolutePath).toList());
|
||||
}
|
||||
|
||||
return new ExternalClasspathGroupDTO(project.getPath(), Collections.unmodifiableMap(classpaths));
|
||||
}
|
||||
|
||||
public static Map<String, ExternalClasspathGroupDTO> resolveExternal(Set<File> files) {
|
||||
Map<String, ExternalClasspathGroupDTO> map = new HashMap<>();
|
||||
|
||||
for (File file : files) {
|
||||
String json;
|
||||
|
||||
try {
|
||||
json = Files.readString(file.toPath());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to read external classpath group file: " + file, e);
|
||||
}
|
||||
|
||||
ExternalClasspathGroupDTO dto = LoomGradlePlugin.GSON.fromJson(json, ExternalClasspathGroupDTO.class);
|
||||
map.put(dto.projectPath(), dto);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
public List<String> getForSourceSet(String sourceSetName) {
|
||||
return Objects.requireNonNull(classpaths.get(sourceSetName), "No classpath found for source set: " + sourceSetName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2025 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.task.launch;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.gradle.api.UncheckedIOException;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.workers.WorkAction;
|
||||
import org.gradle.workers.WorkParameters;
|
||||
import org.gradle.workers.WorkQueue;
|
||||
import org.gradle.workers.WorkerExecutor;
|
||||
|
||||
import net.fabricmc.loom.LoomGradlePlugin;
|
||||
import net.fabricmc.loom.configuration.classpathgroups.ExternalClasspathGroupDTO;
|
||||
import net.fabricmc.loom.task.AbstractLoomTask;
|
||||
|
||||
public abstract class ExportClasspathTask extends AbstractLoomTask {
|
||||
@Input
|
||||
public abstract Property<String> getClasspathDtoJson();
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getOutput();
|
||||
|
||||
@Inject
|
||||
protected abstract WorkerExecutor getWorkerExecutor();
|
||||
|
||||
@Inject
|
||||
public ExportClasspathTask() {
|
||||
getClasspathDtoJson().set(getProject()
|
||||
.provider(() -> ExternalClasspathGroupDTO.createFromProject(getProject()))
|
||||
.map(LoomGradlePlugin.GSON::toJson));
|
||||
getOutput().set(getProject().getLayout().getBuildDirectory().file("export_classpath.json"));
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void run() {
|
||||
final WorkQueue workQueue = getWorkerExecutor().noIsolation();
|
||||
workQueue.submit(ExportClassPathWorkAction.class, p -> {
|
||||
p.getClasspathDtoJson().set(getClasspathDtoJson());
|
||||
p.getOutput().set(getOutput());
|
||||
});
|
||||
}
|
||||
|
||||
protected interface ExportClassPathWorkParameters extends WorkParameters {
|
||||
Property<String> getClasspathDtoJson();
|
||||
RegularFileProperty getOutput();
|
||||
}
|
||||
|
||||
public abstract static class ExportClassPathWorkAction implements WorkAction<ExportClassPathWorkParameters> {
|
||||
@Override
|
||||
public void execute() {
|
||||
File outputFile = getParameters().getOutput().getAsFile().get();
|
||||
String json = getParameters().getClasspathDtoJson().get();
|
||||
|
||||
try {
|
||||
Files.writeString(outputFile.toPath(), json);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to write classpath groups", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import org.gradle.api.logging.configuration.ConsoleOutput;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.Nested;
|
||||
import org.gradle.api.tasks.Optional;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
@@ -51,7 +52,8 @@ import net.fabricmc.loom.LoomGradlePlugin;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider;
|
||||
import net.fabricmc.loom.task.AbstractLoomTask;
|
||||
import net.fabricmc.loom.util.gradle.SourceSetHelper;
|
||||
import net.fabricmc.loom.task.service.ClasspathGroupService;
|
||||
import net.fabricmc.loom.util.service.ScopedServiceFactory;
|
||||
|
||||
public abstract class GenerateDLIConfigTask extends AbstractLoomTask {
|
||||
@Input
|
||||
@@ -69,10 +71,6 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask {
|
||||
@Input
|
||||
protected abstract Property<Boolean> getANSISupportedIDE();
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
protected abstract Property<String> getClassPathGroups();
|
||||
|
||||
@Input
|
||||
protected abstract Property<String> getLog4jConfigPaths();
|
||||
|
||||
@@ -96,16 +94,16 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask {
|
||||
@OutputFile
|
||||
protected abstract RegularFileProperty getDevLauncherConfig();
|
||||
|
||||
@Nested
|
||||
protected abstract Property<ClasspathGroupService.Options> getClasspathGroupOptions();
|
||||
|
||||
public GenerateDLIConfigTask() {
|
||||
getVersionInfoJson().set(LoomGradlePlugin.GSON.toJson(getExtension().getMinecraftProvider().getVersionInfo()));
|
||||
getMinecraftVersion().set(getExtension().getMinecraftProvider().minecraftVersion());
|
||||
getSplitSourceSets().set(getExtension().areEnvironmentSourceSetsSplit());
|
||||
getANSISupportedIDE().set(ansiSupportedIde(getProject()));
|
||||
getPlainConsole().set(getProject().getGradle().getStartParameter().getConsoleOutput() == ConsoleOutput.Plain);
|
||||
|
||||
if (!getExtension().getMods().isEmpty()) {
|
||||
getClassPathGroups().set(buildClassPathGroups(getProject()));
|
||||
}
|
||||
getClasspathGroupOptions().set(ClasspathGroupService.create(getProject()));
|
||||
|
||||
getLog4jConfigPaths().set(getAllLog4JConfigFiles(getProject()));
|
||||
|
||||
@@ -152,8 +150,12 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask {
|
||||
launchConfig.property("fabric.gameJarPath", getCommonGameJarPath().get());
|
||||
}
|
||||
|
||||
if (getClassPathGroups().isPresent()) {
|
||||
launchConfig.property("fabric.classPathGroups", getClassPathGroups().get());
|
||||
try (ScopedServiceFactory serviceFactory = new ScopedServiceFactory()) {
|
||||
ClasspathGroupService classpathGroupService = serviceFactory.get(getClasspathGroupOptions());
|
||||
|
||||
if (classpathGroupService.hasGroups()) {
|
||||
launchConfig.property("fabric.classPathGroups", classpathGroupService.getClasspathGroupsPropertyValue());
|
||||
}
|
||||
}
|
||||
|
||||
//Enable ansi by default for idea and vscode when gradle is not ran with plain console.
|
||||
@@ -180,19 +182,6 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* See: https://github.com/FabricMC/fabric-loader/pull/585.
|
||||
*/
|
||||
private static String buildClassPathGroups(Project project) {
|
||||
return LoomGradleExtension.get(project).getMods().stream()
|
||||
.map(modSettings ->
|
||||
SourceSetHelper.getClasspath(modSettings, project).stream()
|
||||
.map(File::getAbsolutePath)
|
||||
.collect(Collectors.joining(File.pathSeparator))
|
||||
)
|
||||
.collect(Collectors.joining(File.pathSeparator+File.pathSeparator));
|
||||
}
|
||||
|
||||
private static boolean ansiSupportedIde(Project project) {
|
||||
File rootDir = project.getRootDir();
|
||||
return new File(rootDir, ".vscode").exists()
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2025 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.task.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.gradle.api.NamedDomainObjectContainer;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.provider.ListProperty;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.Optional;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.ModSettings;
|
||||
import net.fabricmc.loom.configuration.classpathgroups.ClasspathGroup;
|
||||
import net.fabricmc.loom.configuration.classpathgroups.ExternalClasspathGroup;
|
||||
import net.fabricmc.loom.configuration.classpathgroups.ExternalClasspathGroupDTO;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.Lazy;
|
||||
import net.fabricmc.loom.util.service.Service;
|
||||
import net.fabricmc.loom.util.service.ServiceFactory;
|
||||
import net.fabricmc.loom.util.service.ServiceType;
|
||||
|
||||
public class ClasspathGroupService extends Service<ClasspathGroupService.Options> {
|
||||
public static ServiceType<Options, ClasspathGroupService> TYPE = new ServiceType<Options, ClasspathGroupService>(Options.class, ClasspathGroupService.class);
|
||||
|
||||
public interface Options extends Service.Options {
|
||||
@Input
|
||||
@Optional
|
||||
ListProperty<ClasspathGroup> getClasspathGroups();
|
||||
|
||||
@InputFiles
|
||||
@Optional
|
||||
ConfigurableFileCollection getExternalClasspathGroups();
|
||||
}
|
||||
|
||||
public static Provider<Options> create(Project project) {
|
||||
return TYPE.create(project, options -> {
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
NamedDomainObjectContainer<ModSettings> modSettings = extension.getMods();
|
||||
|
||||
if (modSettings.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
options.getClasspathGroups().set(ClasspathGroup.fromModSettings(modSettings));
|
||||
|
||||
if (!hasExternalClasspathGroups(modSettings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Dependency> externalDependencies = getExternalDependencies(project, modSettings);
|
||||
Configuration externalClasspathGroups = project.getConfigurations().detachedConfiguration(externalDependencies.toArray(new Dependency[0]));
|
||||
options.getExternalClasspathGroups().from(externalClasspathGroups);
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean hasExternalClasspathGroups(Set<ModSettings> modSettings) {
|
||||
return modSettings.stream()
|
||||
.anyMatch(s ->
|
||||
s.getExternalGroups().isPresent()
|
||||
&& !s.getExternalGroups().get().isEmpty()
|
||||
);
|
||||
}
|
||||
|
||||
private static List<Dependency> getExternalDependencies(Project project, Set<ModSettings> modSettings) {
|
||||
List<String> requiredProjects = modSettings.stream()
|
||||
.flatMap(s -> s.getExternalGroups().get().stream())
|
||||
.map(ExternalClasspathGroup::projectPath)
|
||||
.distinct()
|
||||
.toList();
|
||||
|
||||
List<Dependency> dependencies = new ArrayList<>();
|
||||
|
||||
for (String projectPath : requiredProjects) {
|
||||
Dependency externalDependency = project.getDependencies()
|
||||
.project(Map.of(
|
||||
"path", projectPath,
|
||||
"configuration", Constants.Configurations.EXPORTED_CLASSPATH
|
||||
));
|
||||
dependencies.add(externalDependency);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(dependencies);
|
||||
}
|
||||
|
||||
private final Supplier<Map<String, ExternalClasspathGroupDTO>> externalClasspathGroups = Lazy.of(() -> ExternalClasspathGroupDTO.resolveExternal(getOptions().getExternalClasspathGroups().getFiles()));
|
||||
|
||||
public ClasspathGroupService(Options options, ServiceFactory serviceFactory) {
|
||||
super(options, serviceFactory);
|
||||
}
|
||||
|
||||
public List<File> getClasspath(ClasspathGroup classpathGroup) {
|
||||
final List<String> paths = new ArrayList<>();
|
||||
|
||||
for (ExternalClasspathGroup externalGroup : classpathGroup.externalGroups()) {
|
||||
ExternalClasspathGroupDTO dto = externalClasspathGroups.get().get(externalGroup.projectPath());
|
||||
|
||||
if (dto == null) {
|
||||
throw new IllegalStateException("Could not find resolved external classpath group for project: " + externalGroup.projectPath());
|
||||
}
|
||||
|
||||
paths.addAll(dto.getForSourceSet(externalGroup.sourceSetName()));
|
||||
}
|
||||
|
||||
paths.addAll(classpathGroup.paths());
|
||||
|
||||
return paths.stream().map(File::new).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* See: https://github.com/FabricMC/fabric-loader/pull/585.
|
||||
*/
|
||||
public String getClasspathGroupsPropertyValue() {
|
||||
return getOptions().getClasspathGroups().get()
|
||||
.stream()
|
||||
.map(group ->
|
||||
getClasspath(group).stream()
|
||||
.map(File::getAbsolutePath)
|
||||
.collect(Collectors.joining(File.pathSeparator))
|
||||
)
|
||||
.collect(Collectors.joining(File.pathSeparator+File.pathSeparator));
|
||||
}
|
||||
|
||||
public boolean hasGroups() {
|
||||
return getOptions().getClasspathGroups().isPresent() && !getOptions().getClasspathGroups().get().isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,10 @@ public class Constants {
|
||||
* Mods to be used by {@link net.fabricmc.loom.task.prod.AbstractProductionRunTask} tasks by default.
|
||||
*/
|
||||
public static final String PRODUCTION_RUNTIME_MODS = "productionRuntimeMods";
|
||||
/**
|
||||
* Used to query classpath data across project boundaries.
|
||||
*/
|
||||
public static final String EXPORTED_CLASSPATH = "loomExportedClasspath";
|
||||
|
||||
private Configurations() {
|
||||
}
|
||||
@@ -125,6 +129,7 @@ public class Constants {
|
||||
|
||||
public static final class Task {
|
||||
public static final String PROCESS_INCLUDE_JARS = "processIncludeJars";
|
||||
public static final String EXPORT_CLASSPATH = "exportClasspath";
|
||||
|
||||
private Task() {
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.gradle.api.invocation.Gradle;
|
||||
import org.gradle.api.provider.Provider;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.LoomGradlePlugin;
|
||||
|
||||
public final class GradleUtils {
|
||||
private GradleUtils() {
|
||||
@@ -59,7 +60,7 @@ public final class GradleUtils {
|
||||
}
|
||||
|
||||
public static boolean isLoomProject(Project project) {
|
||||
return project.getPluginManager().hasPlugin("fabric-loom");
|
||||
return project.getPluginManager().hasPlugin(LoomGradlePlugin.NAME);
|
||||
}
|
||||
|
||||
public static Provider<Boolean> getBooleanPropertyProvider(Project project, String key) {
|
||||
|
||||
@@ -52,7 +52,6 @@ import org.jetbrains.annotations.VisibleForTesting;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.ModSettings;
|
||||
import net.fabricmc.loom.configuration.ide.idea.IdeaUtils;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
|
||||
@@ -113,18 +112,8 @@ public final class SourceSetHelper {
|
||||
return it.hasNext() ? it.next().getProject() : null;
|
||||
}
|
||||
|
||||
public static List<File> getClasspath(ModSettings modSettings, Project project) {
|
||||
final List<File> files = new ArrayList<>();
|
||||
|
||||
files.addAll(modSettings.getModSourceSets().get().stream()
|
||||
.flatMap(sourceSet -> getClasspath(sourceSet, project).stream())
|
||||
.toList());
|
||||
files.addAll(modSettings.getModFiles().getFiles());
|
||||
|
||||
return Collections.unmodifiableList(files);
|
||||
}
|
||||
|
||||
public static List<File> getClasspath(SourceSetReference reference, Project project) {
|
||||
public static List<File> getClasspath(SourceSetReference reference) {
|
||||
final Project project = reference.project();
|
||||
final List<File> classpath = getGradleClasspath(reference, project);
|
||||
|
||||
classpath.addAll(getIdeaClasspath(reference, project));
|
||||
|
||||
@@ -57,4 +57,19 @@ class MultiProjectTest extends Specification implements GradleProjectTestTrait {
|
||||
where:
|
||||
version << STANDARD_TEST_VERSIONS
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "classpath groups (gradle #version)"() {
|
||||
setup:
|
||||
def gradle = gradleProject(project: "multiproject", version: version)
|
||||
|
||||
when:
|
||||
def result = gradle.run(tasks: [":generateDLIConfig",])
|
||||
|
||||
then:
|
||||
result.task(":generateDLIConfig").outcome == SUCCESS
|
||||
|
||||
where:
|
||||
version << STANDARD_TEST_VERSIONS
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ plugins {
|
||||
}
|
||||
|
||||
allprojects {
|
||||
if (it.path == ":javalib") return;
|
||||
|
||||
apply plugin: "fabric-loom"
|
||||
|
||||
version = "1.0.0"
|
||||
@@ -42,13 +44,14 @@ allprojects {
|
||||
loom {
|
||||
mods {
|
||||
core {
|
||||
sourceSet project(':core').sourceSets.main
|
||||
sourceSet("main", ":core")
|
||||
}
|
||||
example {
|
||||
sourceSet project(':example').sourceSets.main
|
||||
sourceSet("main", ":example")
|
||||
}
|
||||
root {
|
||||
sourceSet sourceSets.main
|
||||
sourceSet("main", ":javalib")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'net.fabricmc.fabric-loom-companion'
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
rootProject.name = "fabric-example-mod"
|
||||
|
||||
include 'core'
|
||||
include 'example'
|
||||
include 'example'
|
||||
include 'javalib'
|
||||
Reference in New Issue
Block a user