diff --git a/src/main/java/net/neoforged/minecraftdependencies/DefaultValueDisambiguationRule.java b/src/main/java/net/neoforged/minecraftdependencies/DefaultValueDisambiguationRule.java new file mode 100644 index 00000000..a82992a5 --- /dev/null +++ b/src/main/java/net/neoforged/minecraftdependencies/DefaultValueDisambiguationRule.java @@ -0,0 +1,33 @@ +package net.neoforged.minecraftdependencies; + +import org.gradle.api.attributes.AttributeDisambiguationRule; +import org.gradle.api.attributes.MultipleCandidatesDetails; + +import javax.inject.Inject; + +/** + * Sets a default value for an attribute if no value is requested. + */ +abstract class DefaultValueDisambiguationRule implements AttributeDisambiguationRule { + private final T defaultValue; + + @Inject + public DefaultValueDisambiguationRule(T defaultValue) { + this.defaultValue = defaultValue; + } + + @Override + public void execute(MultipleCandidatesDetails details) { + var consumerValue = details.getConsumerValue(); + if (consumerValue != null && details.getCandidateValues().contains(consumerValue)) { + details.closestMatch(consumerValue); + } else { + for (var candidateValue : details.getCandidateValues()) { + if (candidateValue.equals(defaultValue)) { + details.closestMatch(candidateValue); + return; + } + } + } + } +} diff --git a/src/main/java/net/neoforged/minecraftdependencies/DistributionDisambiguationRule.java b/src/main/java/net/neoforged/minecraftdependencies/DistributionDisambiguationRule.java new file mode 100644 index 00000000..b4d33c17 --- /dev/null +++ b/src/main/java/net/neoforged/minecraftdependencies/DistributionDisambiguationRule.java @@ -0,0 +1,10 @@ +package net.neoforged.minecraftdependencies; + +import javax.inject.Inject; + +abstract class DistributionDisambiguationRule extends DefaultValueDisambiguationRule { + @Inject + public DistributionDisambiguationRule(MinecraftDistribution defaultValue) { + super(defaultValue); + } +} diff --git a/src/main/java/net/neoforged/minecraftdependencies/MinecraftDependenciesPlugin.java b/src/main/java/net/neoforged/minecraftdependencies/MinecraftDependenciesPlugin.java new file mode 100644 index 00000000..20ba9a51 --- /dev/null +++ b/src/main/java/net/neoforged/minecraftdependencies/MinecraftDependenciesPlugin.java @@ -0,0 +1,40 @@ +package net.neoforged.minecraftdependencies; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; + +/** + * Applies defaults for the Gradle attributes introduced by the Minecraft Dependencies modules. + *

+ * The defaults are: + *

    + *
  • {@code net.neoforged.distribution} defaults to {@code client}
  • + *
  • {@code net.neoforged.operatingsystem} defaults to the current operating system
  • + *
+ */ +public class MinecraftDependenciesPlugin implements Plugin { + @Override + public void apply(Project project) { + project.getDependencies().attributesSchema(attributesSchema -> { + // Set up a disambiguation that by default selects the client distribution libraries + // This happens under the assumption that client is usually a superset of server. + var defaultDistribution = project.getObjects().named(MinecraftDistribution.class, MinecraftDistribution.CLIENT); + attributesSchema.attribute(MinecraftDistribution.ATTRIBUTE).getDisambiguationRules().add(DistributionDisambiguationRule.class, spec -> spec.params( + defaultDistribution + )); + + var defaultOperatingSystem = project.getObjects().named(OperatingSystem.class, getDefaultOperatingSystem()); + attributesSchema.attribute(OperatingSystem.ATTRIBUTE).getDisambiguationRules().add(OperatingSystemDisambiguationRule.class, spec -> spec.params( + defaultOperatingSystem + )); + }); + } + + private static String getDefaultOperatingSystem() { + return switch (net.neoforged.moddevgradle.internal.utils.OperatingSystem.current()) { + case LINUX -> OperatingSystem.LINUX; + case MACOS -> OperatingSystem.MACOSX; + case WINDOWS -> OperatingSystem.WINDOWS; + }; + } +} diff --git a/src/main/java/net/neoforged/minecraftdependencies/MinecraftDistribution.java b/src/main/java/net/neoforged/minecraftdependencies/MinecraftDistribution.java new file mode 100644 index 00000000..76b3b3ea --- /dev/null +++ b/src/main/java/net/neoforged/minecraftdependencies/MinecraftDistribution.java @@ -0,0 +1,17 @@ +package net.neoforged.minecraftdependencies; + +import org.gradle.api.Named; +import org.gradle.api.attributes.Attribute; + +/** + * The source of this attribute is the list of dependencies declared by the server and client Minecraft distributions. + *

+ * + * @see GradleMinecraftDependencies project + */ +public interface MinecraftDistribution extends Named { + Attribute ATTRIBUTE = Attribute.of("net.neoforged.distribution", MinecraftDistribution.class); + + String CLIENT = "client"; + String SERVER = "server"; +} diff --git a/src/main/java/net/neoforged/minecraftdependencies/OperatingSystem.java b/src/main/java/net/neoforged/minecraftdependencies/OperatingSystem.java new file mode 100644 index 00000000..35913541 --- /dev/null +++ b/src/main/java/net/neoforged/minecraftdependencies/OperatingSystem.java @@ -0,0 +1,20 @@ +package net.neoforged.minecraftdependencies; + +import org.gradle.api.Named; +import org.gradle.api.attributes.Attribute; + +/** + * This attribute is used to differentiate between the different native libraries used by Minecraft. + *

+ * The client in particular uses a rule-based system to declare dependencies that only apply to certain + * operating systems. We model libraries that are declared using such rules by using this attribute. + * + * @see GradleMinecraftDependencies + */ +public interface OperatingSystem extends Named { + Attribute ATTRIBUTE = Attribute.of("net.neoforged.operatingsystem", OperatingSystem.class); + + String LINUX = "linux"; + String MACOSX = "osx"; + String WINDOWS = "windows"; +} diff --git a/src/main/java/net/neoforged/minecraftdependencies/OperatingSystemDisambiguationRule.java b/src/main/java/net/neoforged/minecraftdependencies/OperatingSystemDisambiguationRule.java new file mode 100644 index 00000000..7ee982de --- /dev/null +++ b/src/main/java/net/neoforged/minecraftdependencies/OperatingSystemDisambiguationRule.java @@ -0,0 +1,10 @@ +package net.neoforged.minecraftdependencies; + +import javax.inject.Inject; + +abstract class OperatingSystemDisambiguationRule extends DefaultValueDisambiguationRule { + @Inject + public OperatingSystemDisambiguationRule(OperatingSystem defaultValue) { + super(defaultValue); + } +} diff --git a/src/main/java/net/neoforged/moddevgradle/internal/DistributionDisambiguation.java b/src/main/java/net/neoforged/moddevgradle/internal/DistributionDisambiguation.java deleted file mode 100644 index bc5d84fb..00000000 --- a/src/main/java/net/neoforged/moddevgradle/internal/DistributionDisambiguation.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.neoforged.moddevgradle.internal; - -import org.gradle.api.attributes.AttributeDisambiguationRule; -import org.gradle.api.attributes.MultipleCandidatesDetails; - -/** - * We generally will use "client" dependencies when we have to decide between client and server, - * since client libraries will usually be a superset of the server libraries. - */ -public abstract class DistributionDisambiguation implements AttributeDisambiguationRule { - @Override - public void execute(MultipleCandidatesDetails details) { - details.closestMatch("client"); - } -} diff --git a/src/main/java/net/neoforged/moddevgradle/internal/ModDevPlugin.java b/src/main/java/net/neoforged/moddevgradle/internal/ModDevPlugin.java index ef7b9604..89cb81ad 100644 --- a/src/main/java/net/neoforged/moddevgradle/internal/ModDevPlugin.java +++ b/src/main/java/net/neoforged/moddevgradle/internal/ModDevPlugin.java @@ -3,6 +3,8 @@ import net.neoforged.elc.configs.GradleLaunchConfig; import net.neoforged.elc.configs.JavaApplicationLaunchConfig; import net.neoforged.elc.configs.LaunchGroup; +import net.neoforged.minecraftdependencies.MinecraftDependenciesPlugin; +import net.neoforged.minecraftdependencies.MinecraftDistribution; import net.neoforged.moddevgradle.dsl.DataFileCollection; import net.neoforged.moddevgradle.dsl.InternalModelHelper; import net.neoforged.moddevgradle.dsl.NeoForgeExtension; @@ -21,6 +23,7 @@ import net.neoforged.vsclc.attribute.ShortCmdBehaviour; import net.neoforged.vsclc.writer.WritingMode; import org.gradle.api.GradleException; +import org.gradle.api.Named; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; @@ -29,6 +32,7 @@ import org.gradle.api.artifacts.ExternalModuleDependency; import org.gradle.api.artifacts.ModuleDependency; import org.gradle.api.attributes.Attribute; +import org.gradle.api.attributes.AttributeContainer; import org.gradle.api.attributes.Category; import org.gradle.api.attributes.DocsType; import org.gradle.api.attributes.Usage; @@ -36,6 +40,7 @@ import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.Directory; import org.gradle.api.file.RegularFile; +import org.gradle.api.model.ObjectFactory; import org.gradle.api.plugins.ExtensionAware; import org.gradle.api.plugins.JavaLibraryPlugin; import org.gradle.api.plugins.JavaPlugin; @@ -85,9 +90,6 @@ public class ModDevPlugin implements Plugin { private static final Logger LOG = LoggerFactory.getLogger(ModDevPlugin.class); - private static final Attribute ATTRIBUTE_DISTRIBUTION = Attribute.of("net.neoforged.distribution", String.class); - private static final Attribute ATTRIBUTE_OPERATING_SYSTEM = Attribute.of("net.neoforged.operatingsystem", String.class); - /** * This must be relative to the project directory since we can only set this to the same project-relative * directory across all subprojects due to IntelliJ limitations. @@ -109,12 +111,20 @@ public class ModDevPlugin implements Plugin { */ public static final String CONFIGURATION_COMPILE_DEPENDENCIES = "neoForgeCompileDependencies"; + private final ObjectFactory objectFactory; + private Runnable configureTesting = null; + @Inject + public ModDevPlugin(ObjectFactory objectFactory) { + this.objectFactory = objectFactory; + } + @Override public void apply(Project project) { project.getPlugins().apply(JavaLibraryPlugin.class); project.getPlugins().apply(NeoFormRuntimePlugin.class); + project.getPlugins().apply(MinecraftDependenciesPlugin.class); // Do not apply the repositories automatically if they have been applied at the settings-level. // It's still possible to apply them manually, though. @@ -184,11 +194,6 @@ public void apply(Project project) { }); })); - project.getDependencies().attributesSchema(attributesSchema -> { - attributesSchema.attribute(ATTRIBUTE_DISTRIBUTION).getDisambiguationRules().add(DistributionDisambiguation.class); - attributesSchema.attribute(ATTRIBUTE_OPERATING_SYSTEM).getDisambiguationRules().add(OperatingSystemDisambiguation.class); - }); - var createManifestConfigurations = configureArtifactManifestConfigurations(project, extension); // Add a filtered parchment repository automatically if enabled @@ -343,7 +348,10 @@ public void apply(Project project) { spec.setCanBeConsumed(false); spec.shouldResolveConsistentlyWith(runtimeClasspathConfig.get()); spec.attributes(attributes -> { - attributes.attributeProvider(ATTRIBUTE_DISTRIBUTION, type.map(t -> t.equals("client") || t.equals("data") ? "client" : "server")); + attributes.attributeProvider(MinecraftDistribution.ATTRIBUTE, type.map(t -> { + var name = t.equals("client") || t.equals("data") ? MinecraftDistribution.CLIENT : MinecraftDistribution.SERVER; + return project.getObjects().named(MinecraftDistribution.class, name); + })); attributes.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME)); }); spec.getDependencies().addLater(neoForgeModDevLibrariesDependency); @@ -517,8 +525,8 @@ private List configureArtifactManifestConfigurations(Project proj caps.requireCapability("net.neoforged:neoform-dependencies"); }))); spec.attributes(attributes -> { - attributes.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_API)); - attributes.attribute(ATTRIBUTE_DISTRIBUTION, "client"); + setNamedAttribute(attributes, Usage.USAGE_ATTRIBUTE, Usage.JAVA_API); + setNamedAttribute(attributes, MinecraftDistribution.ATTRIBUTE, MinecraftDistribution.CLIENT); }); }); @@ -538,8 +546,8 @@ private List configureArtifactManifestConfigurations(Project proj caps.requireCapability("net.neoforged:neoform-dependencies"); }))); spec.attributes(attributes -> { - attributes.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME)); - attributes.attribute(ATTRIBUTE_DISTRIBUTION, "client"); + setNamedAttribute(attributes, Usage.USAGE_ATTRIBUTE, Usage.JAVA_RUNTIME); + setNamedAttribute(attributes, MinecraftDistribution.ATTRIBUTE, MinecraftDistribution.CLIENT); }); }); @@ -622,8 +630,8 @@ private void setupTesting(Project project, spec.setCanBeConsumed(false); spec.shouldResolveConsistentlyWith(testRuntimeClasspathConfig.get()); spec.attributes(attributes -> { - attributes.attribute(ATTRIBUTE_DISTRIBUTION, "client"); - attributes.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME)); + setNamedAttribute(project, attributes, MinecraftDistribution.ATTRIBUTE, MinecraftDistribution.CLIENT); + setNamedAttribute(project, attributes, Usage.USAGE_ATTRIBUTE, Usage.JAVA_RUNTIME); }); spec.getDependencies().addLater(neoForgeModDevLibrariesDependency); }); @@ -1013,14 +1021,14 @@ private static DataFileCollectionWrapper dataFileConfiguration(Project project, spec.setDescription(description); spec.setCanBeConsumed(false); spec.setCanBeResolved(true); - spec.attributes(attributes -> attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, category))); + spec.attributes(attributes -> setNamedAttribute(project, attributes, Category.CATEGORY_ATTRIBUTE, category)); }); var elementsConfiguration = project.getConfigurations().create(name + "Elements", spec -> { spec.setDescription("Published data files for " + name); spec.setCanBeConsumed(true); spec.setCanBeResolved(false); - spec.attributes(attributes -> attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, category))); + spec.attributes(attributes -> setNamedAttribute(project, attributes, Category.CATEGORY_ATTRIBUTE, category)); }); // Set up the variant publishing conditionally @@ -1061,5 +1069,12 @@ public void accept(Object artifactNotation) { return new DataFileCollectionWrapper(extension, configuration); } -} + private void setNamedAttribute(AttributeContainer attributes, Attribute attribute, String value) { + attributes.attribute(attribute, objectFactory.named(attribute.getType(), value)); + } + + private static void setNamedAttribute(Project project, AttributeContainer attributes, Attribute attribute, String value) { + attributes.attribute(attribute, project.getObjects().named(attribute.getType(), value)); + } +} diff --git a/src/main/java/net/neoforged/moddevgradle/internal/OperatingSystemDisambiguation.java b/src/main/java/net/neoforged/moddevgradle/internal/OperatingSystemDisambiguation.java deleted file mode 100644 index 5ee2aa27..00000000 --- a/src/main/java/net/neoforged/moddevgradle/internal/OperatingSystemDisambiguation.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.neoforged.moddevgradle.internal; - -import net.neoforged.moddevgradle.internal.utils.OperatingSystem; -import org.gradle.api.attributes.AttributeDisambiguationRule; -import org.gradle.api.attributes.MultipleCandidatesDetails; - -/** - * This disambiguation rule will select native dependencies based on the operating system Gradle is currently running on. - */ -public abstract class OperatingSystemDisambiguation implements AttributeDisambiguationRule { - @Override - public void execute(MultipleCandidatesDetails details) { - details.closestMatch(switch (OperatingSystem.current()) { - case LINUX -> "linux"; - case MACOS -> "osx"; - case WINDOWS -> "windows"; - }); - } -}