Skip to content

Commit

Permalink
Share vanilla installations across projects
Browse files Browse the repository at this point in the history
  • Loading branch information
lukebemish committed Jan 6, 2025
1 parent 9b53014 commit fc7f73e
Show file tree
Hide file tree
Showing 24 changed files with 721 additions and 318 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class CrochetPlugin implements Plugin<Project> {
public static final Attribute<String> NEO_DISTRIBUTION_ATTRIBUTE = Attribute.of("net.neoforged.distribution", String.class);
public static final Attribute<String> NEO_OPERATING_SYSTEM_ATTRIBUTE = Attribute.of("net.neoforged.operatingsystem", String.class);
public static final Attribute<String> CROCHET_DISTRIBUTION_ATTRIBUTE = Attribute.of("dev.lukebemish.crochet.distribution", String.class);
public static final Attribute<String> LOCAL_DISTRIBUTION_ATTRIBUTE = Attribute.of("dev.lukebemish.crochet.local.distribution", String.class);
// This attribute SHOULD NOT be published -- it is for use only in internal pre-remapping-collecting setups
public static final Attribute<String> CROCHET_REMAP_TYPE_ATTRIBUTE = Attribute.of("dev.lukebemish.crochet.remap", String.class);
// Dependencies on things that'll need to be remapped when it's all said and done
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package dev.lukebemish.crochet.model;

import javax.inject.Inject;

public abstract class AbstractExternalVanillaInstallation extends ExternalMinecraftInstallation {
@Inject
public AbstractExternalVanillaInstallation(String name, CrochetExtension extension) {
super(name, extension);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,20 @@
import javax.inject.Inject;

@SuppressWarnings("UnstableApiUsage")
public abstract class AbstractInstallationDependencies implements Dependencies {
private final MinecraftInstallation installation;
public abstract class AbstractLocalInstallationDependencies<T extends AbstractLocalInstallationDependencies<T>> implements Dependencies {
private final LocalMinecraftInstallation installation;

@Inject
public AbstractInstallationDependencies(MinecraftInstallation installation) {
public AbstractLocalInstallationDependencies(LocalMinecraftInstallation installation) {
this.installation = installation;
}

@SuppressWarnings("unchecked")
public T configure(Action<? super T> action) {
action.execute((T) this);
return (T) this;
}

public abstract DependencyCollector getAccessTransformers();

public abstract DependencyCollector getAccessTransformersApi();
Expand Down Expand Up @@ -46,9 +52,4 @@ public Dependency publishAccessTransformers(Object path, Action<ConfigurablePubl
public Dependency publishAccessTransformers(Object path) {
return publishAccessTransformers(path, artifact -> {});
}

public AbstractInstallationDependencies configure(Action<? super AbstractInstallationDependencies> action) {
action.execute(this);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,16 @@
import java.util.Locale;
import java.util.Map;

public abstract class AbstractVanillaInstallation extends MinecraftInstallation {
public abstract class AbstractVanillaInstallation extends LocalMinecraftInstallation {
final Project project;
final VanillaInstallationArtifacts vanillaConfigMaker;

final Property<String> minecraftVersionProperty;

@Inject
public AbstractVanillaInstallation(String name, CrochetExtension extension) {
super(name, extension);

this.project = extension.project;

// Create early so getMinecraft provider works right
this.minecraftVersionProperty = project.getObjects().property(String.class);
this.minecraftVersionProperty.set(minecraftDependencies.getIncoming().getResolutionResult().getRootComponent().map(ConfigurationUtils::extractMinecraftVersion));

var minecraftPistonMeta = project.getConfigurations().dependencyScope("crochet"+StringUtils.capitalize(name)+"PistonMetaDownloads");
var clientJarPistonMeta = project.getConfigurations().resolvable("crochet"+StringUtils.capitalize(name)+"ClientJarPistonMetaDownloads", c -> {
c.extendsFrom(minecraftPistonMeta.get());
Expand Down Expand Up @@ -81,7 +75,7 @@ public AbstractVanillaInstallation(String name, CrochetExtension extension) {
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, "versionjson"));
});
});
project.getDependencies().addProvider(minecraftPistonMeta.getName(), minecraftVersionProperty.map(v -> CrochetRepositoriesPlugin.MOJANG_STUBS_GROUP+":"+PistonMetaMetadataRule.MINECRAFT+":"+v));
project.getDependencies().addProvider(minecraftPistonMeta.getName(), getMinecraft().map(v -> CrochetRepositoriesPlugin.MOJANG_STUBS_GROUP+":"+PistonMetaMetadataRule.MINECRAFT+":"+v));

this.vanillaConfigMaker = project.getObjects().newInstance(VanillaInstallationArtifacts.class);
vanillaConfigMaker.getMinecraftVersion().set(getMinecraft());
Expand Down Expand Up @@ -156,51 +150,13 @@ public void setMinecraft(Provider<String> string) {
);
}

public Provider<String> getMinecraft() {
return this.minecraftVersionProperty;
}

@Override
public void forFeature(SourceSet sourceSet) {
super.forFeature(sourceSet);
sharedFeature(sourceSet);
FeatureUtils.forSourceSetFeature(project, sourceSet.getName(), context -> {
Action<AttributeContainer> attributesAction = attributes -> {
var dist = getDistribution().get();
if (dist != InstallationDistribution.JOINED) {
attributes.attribute(CrochetPlugin.CROCHET_DISTRIBUTION_ATTRIBUTE, dist.name().toLowerCase(Locale.ROOT));
}
};
context.getRuntimeElements().attributes(attributesAction);
context.getApiElements().attributes(attributesAction);
project.getConfigurations().getByName(context.getSourceSet().getCompileClasspathConfigurationName()).attributes(attributesAction);
project.getConfigurations().getByName(context.getSourceSet().getRuntimeClasspathConfigurationName()).attributes(attributesAction);
});
}

private void sharedFeature(SourceSet sourceSet) {
Action<AttributeContainer> attributesAction = attributes -> {
var dist = getDistribution().get();
if (dist != InstallationDistribution.JOINED) {
attributes.attribute(CrochetPlugin.CROCHET_DISTRIBUTION_ATTRIBUTE, dist.name().toLowerCase(Locale.ROOT));
}
};
project.getConfigurations().named(sourceSet.getTaskName(null, JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME), config -> {
config.extendsFrom(minecraft);
config.shouldResolveConsistentlyWith(switch (getDistribution().get()) {
case CLIENT, JOINED -> nonUpgradableClientCompileDependencies;
case SERVER, COMMON -> nonUpgradableServerCompileDependencies;
});
config.attributes(attributesAction);
});
project.getConfigurations().named(sourceSet.getTaskName(null, JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME), config -> {
config.attributes(attributesAction);
});
}

@Override
public void forLocalFeature(SourceSet sourceSet) {
super.forLocalFeature(sourceSet);
sharedFeature(sourceSet);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package dev.lukebemish.crochet.model;

interface AbstractVanillaInstallationData extends InstallationData {
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

import javax.inject.Inject;

public abstract class AbstractVanillaInstallationDependencies extends AbstractInstallationDependencies implements Mappings {
public abstract class AbstractVanillaInstallationDependencies<T extends AbstractVanillaInstallationDependencies<T>> extends AbstractLocalInstallationDependencies<T> implements Mappings {
@Inject
public AbstractVanillaInstallationDependencies(MinecraftInstallation installation) {
public AbstractVanillaInstallationDependencies(AbstractVanillaInstallation installation) {
super(installation);
}

Expand Down
11 changes: 11 additions & 0 deletions src/main/java/dev/lukebemish/crochet/model/CrochetExtension.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ public CrochetExtension(Project project) {
NeoFormInstallation.class,
name -> objects.newInstance(NeoFormInstallation.class, name, this)
);

// External stuff
this.installations.registerFactory(
ExternalVanillaInstallation.class,
name -> objects.newInstance(ExternalVanillaInstallation.class, name, this)
);

// This collection should be non-lazy as it configures other lazy things (namely, tasks)
this.installations.whenObjectAdded(o -> {});

Expand Down Expand Up @@ -117,6 +124,10 @@ public NamedDomainObjectProvider<VanillaInstallation> vanillaInstallation(String
return installations.register(name, VanillaInstallation.class, action);
}

public NamedDomainObjectProvider<ExternalVanillaInstallation> externalVanillaInstallation(String name, Action<ExternalVanillaInstallation> action) {
return installations.register(name, ExternalVanillaInstallation.class, action);
}

public abstract NamedDomainObjectContainer<Run> getRuns();

public void runs(Action<NamedDomainObjectContainer<Run>> action) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package dev.lukebemish.crochet.model;

import org.gradle.api.tasks.SourceSet;

import javax.inject.Inject;

public abstract class ExternalAbstractVanillaInstallation extends ExternalMinecraftInstallation {

@Inject
public ExternalAbstractVanillaInstallation(String name, CrochetExtension extension) {
super(name, extension);
}

@Override
public void forFeature(SourceSet sourceSet) {
super.forFeature(sourceSet);
}

@Override
public void forLocalFeature(SourceSet sourceSet) {
super.forLocalFeature(sourceSet);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package dev.lukebemish.crochet.model;

import dev.lukebemish.crochet.internal.ConfigurationUtils;
import dev.lukebemish.crochet.internal.CrochetPlugin;
import org.apache.commons.lang3.StringUtils;
import org.gradle.api.Action;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.ModuleDependency;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;

import javax.inject.Inject;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;

public abstract class ExternalMinecraftInstallation extends MinecraftInstallation {
final Configuration assetsProperties;

@Inject
public ExternalMinecraftInstallation(String name, CrochetExtension extension) {
super(name, extension);

var project = extension.project;

assetsProperties = project.getConfigurations().maybeCreate("crochet"+ StringUtils.capitalize(name)+"AssetsProperties");
assetsPropertiesFiles.from(assetsProperties);
}

boolean linked = false;

protected abstract String sharingInstallationTypeTag();

protected Map<String, Configuration> getConfigurationsToLink() {
return Map.of(
"assets-properties", assetsProperties,
"minecraft", minecraft,
"minecraft-dependencies", minecraftDependencies,
"minecraft-resources", minecraftResources,
"minecraft-line-mapped", minecraftLineMapped,
"non-upgradable", nonUpgradableDependencies
);
}

public void consume(String project, String name) {
if (linked) {
throw new IllegalStateException("External Minecraft installation already linked");
}
linked = true;
var dependencies = this.crochetExtension.project.getDependencies();
Function<String, Action<ModuleDependency>> capabilitiesFunction = tag -> dependency -> {
dependency.capabilities(capabilities -> {
var group = CROSS_PROJECT_SHARING_CAPABILITY_GROUP + sharingInstallationTypeTag();
var module = tag + "-" + name;
capabilities.requireCapability(group + ":" + module);
});
dependency.attributes(attribute -> {
attribute.attributeProvider(CrochetPlugin.LOCAL_DISTRIBUTION_ATTRIBUTE, getDistribution().map(it -> it.name().toLowerCase(Locale.ROOT)));
});
};
Supplier<Provider<Dependency>> projectDependencyProvider = () -> crochetExtension.project.provider(() -> dependencies.project(Map.of("path", project)));
for (var entry : getConfigurationsToLink().entrySet()) {
var configuration = entry.getValue();
configuration.getAttributes().attributeProvider(CrochetPlugin.LOCAL_DISTRIBUTION_ATTRIBUTE, getDistribution().map(it -> it.name().toLowerCase(Locale.ROOT)));
var tag = entry.getKey();
dependencies.addProvider(
configuration.getName(),
projectDependencyProvider.get(),
capabilitiesFunction.apply(tag)
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package dev.lukebemish.crochet.model;

import dev.lukebemish.crochet.internal.CrochetPlugin;

import javax.inject.Inject;

public abstract class ExternalVanillaInstallation extends AbstractExternalVanillaInstallation {
@Inject
public ExternalVanillaInstallation(String name, CrochetExtension extension) {
super(name, extension);
}

@Override
void forRun(Run run, RunType runType) {
super.forRun(run, runType);
run.argFilesTask.configure(task -> task.getMinecraftVersion().set(getMinecraft()));

run.classpath.fromDependencyCollector(run.getImplementation());

switch (runType) {
case CLIENT -> {
run.getMainClass().convention("net.minecraft.client.main.Main");
run.classpath.attributes(attributes -> attributes.attribute(CrochetPlugin.NEO_DISTRIBUTION_ATTRIBUTE, "client"));
crochetExtension.project.afterEvaluate(p -> {
if (run.getAvoidNeedlessDecompilation().get()) {
run.classpath.extendsFrom(minecraft);
} else {
run.classpath.extendsFrom(minecraftLineMapped);
}
});
run.getArgs().addAll(
"--gameDir", ".",
"--assetIndex", "${assets_index_name}",
"--assetsDir", "${assets_root}",
"--accessToken", "NotValid",
"--version", "${minecraft_version}"
);
}
case SERVER -> {
run.classpath.attributes(attributes -> attributes.attribute(CrochetPlugin.NEO_DISTRIBUTION_ATTRIBUTE, "server"));
crochetExtension.project.afterEvaluate(p -> {
if (run.getAvoidNeedlessDecompilation().get()) {
run.classpath.extendsFrom(minecraft);
} else {
run.classpath.extendsFrom(minecraftLineMapped);
}
});
run.getMainClass().convention("net.minecraft.server.Main");
}
case DATA -> {
// TODO: what's the right stuff to go here?
run.classpath.attributes(attributes -> attributes.attribute(CrochetPlugin.NEO_DISTRIBUTION_ATTRIBUTE, "client"));
crochetExtension.project.afterEvaluate(p -> {
if (run.getAvoidNeedlessDecompilation().get()) {
run.classpath.extendsFrom(minecraft);
} else {
run.classpath.extendsFrom(minecraftLineMapped);
}
});
run.getMainClass().convention("net.minecraft.data.Main");
}
}
}

@Override
protected String sharingInstallationTypeTag() {
return "vanilla";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.gradle.api.artifacts.ProjectDependency;
import org.gradle.api.artifacts.type.ArtifactTypeDefinition;
import org.gradle.api.attributes.Category;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.RegularFile;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.provider.Provider;
Expand Down Expand Up @@ -64,7 +65,7 @@ public abstract class FabricInstallation extends AbstractVanillaInstallation {
final Configuration loaderConfiguration;
final Configuration intermediaryMinecraft;
final Configuration mappingsClasspath;
final TaskProvider<WriteFile> writeLog4jConfig;
final FileCollection writeLog4jConfig;
final FabricInstallationArtifacts fabricConfigMaker;

private final CrochetExtension extension;
Expand All @@ -81,12 +82,13 @@ public FabricInstallation(String name, CrochetExtension extension) {

this.extension = extension;

this.writeLog4jConfig = project.getTasks().register("writeCrochet"+StringUtils.capitalize(name)+"Log4jConfig", WriteFile.class, task -> {
var writeLog4jConfigTask = project.getTasks().register("writeCrochet"+StringUtils.capitalize(name)+"Log4jConfig", WriteFile.class, task -> {
task.getContents().convention(
Log4jSetup.FABRIC_CONFIG
);
task.getOutputFile().convention(project.getLayout().getBuildDirectory().file("crochet/installations/"+this.getName()+"/log4j2.xml"));
});
this.writeLog4jConfig = extension.project.files(writeLog4jConfigTask.flatMap(WriteFile::getOutputFile)).builtBy(writeLog4jConfigTask);

this.vanillaConfigMaker.getSidedAnnotation().set(SingleVersionGenerator.Options.SidedAnnotation.FABRIC);
this.fabricConfigMaker = project.getObjects().newInstance(FabricInstallationArtifacts.class);
Expand Down Expand Up @@ -1010,4 +1012,9 @@ void forRun(Run run, RunType runType) {
default -> throw new IllegalArgumentException("Unsupported run type: "+runType);
}
}

@Override
protected String sharingInstallationTypeTag() {
return "fabric";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package dev.lukebemish.crochet.model;

interface FabricInstallationData extends AbstractVanillaInstallationData {
}
Loading

0 comments on commit fc7f73e

Please sign in to comment.