net.minecraftforge.gradle.userdev.UserDevPlugin Maven / Gradle / Ivy
/*
* ForgeGradle
* Copyright (C) 2018 Forge Development LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
package net.minecraftforge.gradle.userdev;
import net.minecraftforge.gradle.common.tasks.ApplyMappings;
import net.minecraftforge.gradle.common.tasks.ApplyRangeMap;
import net.minecraftforge.gradle.common.tasks.DownloadAssets;
import net.minecraftforge.gradle.common.tasks.DownloadMCMeta;
import net.minecraftforge.gradle.common.tasks.DownloadMavenArtifact;
import net.minecraftforge.gradle.common.tasks.ExtractExistingFiles;
import net.minecraftforge.gradle.common.tasks.ExtractMCPData;
import net.minecraftforge.gradle.common.tasks.ExtractNatives;
import net.minecraftforge.gradle.common.tasks.ExtractRangeMap;
import net.minecraftforge.gradle.common.util.BaseRepo;
import net.minecraftforge.gradle.common.util.EnvironmentChecks;
import net.minecraftforge.gradle.common.util.MinecraftRepo;
import net.minecraftforge.gradle.common.util.MojangLicenseHelper;
import net.minecraftforge.gradle.common.util.Utils;
import net.minecraftforge.gradle.common.util.VersionJson;
import net.minecraftforge.gradle.mcp.ChannelProvidersExtension;
import net.minecraftforge.gradle.mcp.MCPRepo;
import net.minecraftforge.gradle.mcp.tasks.DownloadMCPMappings;
import net.minecraftforge.gradle.mcp.tasks.GenerateSRG;
import net.minecraftforge.gradle.userdev.tasks.RenameJarInPlace;
import net.minecraftforge.gradle.userdev.util.DeobfuscatingRepo;
import net.minecraftforge.gradle.userdev.util.Deobfuscator;
import net.minecraftforge.gradle.userdev.util.DependencyRemapper;
import net.minecraftforge.srgutils.IMappingFile;
import org.apache.commons.lang3.StringUtils;
import org.gradle.api.DefaultTask;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.DependencySet;
import org.gradle.api.artifacts.ExternalModuleDependency;
import org.gradle.api.artifacts.repositories.MavenArtifactRepository.MetadataSources;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.logging.Logger;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.TaskContainer;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
import org.gradle.api.tasks.bundling.Jar;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.language.base.plugins.LifecycleBasePlugin;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
public class UserDevPlugin implements Plugin {
private static final String MINECRAFT = "minecraft";
public static final String OBF = "__obfuscated";
@Override
public void apply(@Nonnull Project project) {
EnvironmentChecks.checkEnvironment(project);
Utils.addRepoFilters(project);
final Logger logger = project.getLogger();
final UserDevExtension extension = project.getExtensions().create(UserDevExtension.EXTENSION_NAME, UserDevExtension.class, project);
project.getExtensions().create(ChannelProvidersExtension.EXTENSION_NAME, ChannelProvidersExtension.class);
project.getPluginManager().apply(JavaPlugin.class);
final File nativesFolder = project.file("build/natives/");
final NamedDomainObjectContainer reobfExtension = createReobfExtension(project);
final Configuration minecraft = project.getConfigurations().create(MINECRAFT);
project.getConfigurations().named(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME)
.configure(c -> c.extendsFrom(minecraft));
// Let gradle handle the downloading by giving it a configuration to dl. We'll focus on applying mappings to it.
final Configuration internalObfConfiguration = project.getConfigurations().create(OBF);
internalObfConfiguration.setDescription("Generated scope for obfuscated dependencies");
// Create extension for dependency remapping
// Can't create at top-level or put in `minecraft` ext due to configuration name conflict
final Deobfuscator deobfuscator = new Deobfuscator(project, Utils.getCache(project, "deobf_dependencies"));
final DependencyRemapper remapper = new DependencyRemapper(project, deobfuscator);
project.getExtensions().create(DependencyManagementExtension.EXTENSION_NAME, DependencyManagementExtension.class, project, remapper);
final TaskContainer tasks = project.getTasks();
final TaskProvider downloadMcpConfig = tasks.register("downloadMcpConfig", DownloadMavenArtifact.class);
final TaskProvider extractSrg = tasks.register("extractSrg", ExtractMCPData.class);
final TaskProvider createSrgToMcp = tasks.register("createSrgToMcp", GenerateSRG.class);
final TaskProvider createMcpToSrg = tasks.register("createMcpToSrg", GenerateSRG.class);
final TaskProvider downloadMCMeta = tasks.register("downloadMCMeta", DownloadMCMeta.class);
final TaskProvider extractNatives = tasks.register("extractNatives", ExtractNatives.class);
final TaskProvider downloadAssets = tasks.register("downloadAssets", DownloadAssets.class);
final TaskProvider hideLicense = tasks.register(MojangLicenseHelper.HIDE_LICENSE, DefaultTask.class);
final TaskProvider showLicense = tasks.register(MojangLicenseHelper.SHOW_LICENSE, DefaultTask.class);
hideLicense.configure(task -> task.doLast(_task ->
MojangLicenseHelper.hide(project, extension.getMappingChannel().get(), extension.getMappingVersion().get())));
showLicense.configure(task -> task.doLast(_t ->
MojangLicenseHelper.show(project, extension.getMappingChannel().get(), extension.getMappingVersion().get())));
extractSrg.configure(task -> task.getConfig().set(downloadMcpConfig.flatMap(DownloadMavenArtifact::getOutput)));
createSrgToMcp.configure(task -> {
task.setReverse(false);
task.getSrg().set(extractSrg.flatMap(ExtractMCPData::getOutput));
task.getMappings().set(extension.getMappings());
task.getFormat().set(IMappingFile.Format.SRG);
task.getOutput().set(project.getLayout().getBuildDirectory()
.dir(task.getName()).map(s -> s.file("output.srg")));
});
createMcpToSrg.configure(task -> {
task.setReverse(true);
task.getSrg().set(extractSrg.flatMap(ExtractMCPData::getOutput));
task.getMappings().set(extension.getMappings());
});
extractNatives.configure(task -> {
task.getMeta().set(downloadMCMeta.flatMap(DownloadMCMeta::getOutput));
task.getOutput().set(nativesFolder);
});
downloadAssets.configure(task -> task.getMeta().set(downloadMCMeta.flatMap(DownloadMCMeta::getOutput)));
final boolean doingUpdate = project.hasProperty("UPDATE_MAPPINGS");
final String updateVersion = doingUpdate ? (String) project.property("UPDATE_MAPPINGS") : null;
final String updateChannel = doingUpdate
? (project.hasProperty("UPDATE_MAPPINGS_CHANNEL") ? (String) project.property("UPDATE_MAPPINGS_CHANNEL") : "snapshot")
: null;
if (doingUpdate) {
logger.lifecycle("This process uses Srg2Source for java source file renaming. Please forward relevant bug reports to https://github.com/MinecraftForge/Srg2Source/issues.");
final String[] updateSourceSets = project.hasProperty("UPDATE_SOURCESETS") ? ((String) project.property("UPDATE_SOURCESETS")).split(";") : new String[] { SourceSet.MAIN_SOURCE_SET_NAME };
final TaskProvider javaCompile = tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile.class);
final JavaPluginExtension javaConv = project.getExtensions().getByType(JavaPluginExtension.class);
final Provider mainJavaSources = Stream.of(updateSourceSets).map(sourceSet -> javaConv.getSourceSets().named(SourceSet.MAIN_SOURCE_SET_NAME).map(SourceSet::getJava).map(SourceDirectorySet::getSourceDirectories)).reduce((a, b) -> a.zip(b, FileCollection::plus)).get();
final TaskProvider dlMappingsNew = tasks.register("downloadMappingsNew", DownloadMCPMappings.class);
final TaskProvider extractRangeConfig = tasks.register("extractRangeMap", ExtractRangeMap.class);
final TaskProvider applyRangeConfig = tasks.register("applyRangeMap", ApplyRangeMap.class);
final TaskProvider toMCPNew = tasks.register("srg2mcpNew", ApplyMappings.class);
final TaskProvider extractMappedNew = tasks.register("extractMappedNew", ExtractExistingFiles.class);
final TaskProvider updateMappings = tasks.register("updateMappings", DefaultTask.class);
extractRangeConfig.configure(task -> {
task.getSources().from(mainJavaSources);
task.getDependencies().from(javaCompile.map(JavaCompile::getClasspath));
});
applyRangeConfig.configure(task -> {
task.getRangeMap().set(extractRangeConfig.flatMap(ExtractRangeMap::getOutput));
task.getSrgFiles().from(createMcpToSrg.flatMap(GenerateSRG::getOutput));
task.getSources().from(mainJavaSources);
});
dlMappingsNew.configure(task -> {
task.getMappings().set(updateChannel + "_" + updateVersion);
task.getOutput().set(project.getLayout().getBuildDirectory().file("mappings_new.zip"));
});
toMCPNew.configure(task -> {
task.getInput().set(applyRangeConfig.flatMap(ApplyRangeMap::getOutput));
task.getMappings().set(dlMappingsNew.flatMap(DownloadMCPMappings::getOutput));
});
extractMappedNew.configure(task -> {
task.getArchive().set(toMCPNew.flatMap(ApplyMappings::getOutput));
task.getTargets().from(mainJavaSources);
});
updateMappings.configure(task -> task.dependsOn(extractMappedNew));
}
project.afterEvaluate(p -> {
MinecraftUserRepo mcrepo = null;
DeobfuscatingRepo deobfrepo = null;
DependencySet mcDependencies = minecraft.getDependencies();
for (Dependency dep : new ArrayList<>(mcDependencies)) { // Copied to new list to avoid ConcurrentModificationException
if (!(dep instanceof ExternalModuleDependency)) {
throw new IllegalArgumentException(minecraft.getName() + " configuration must contain a Maven dependency");
}
if (mcrepo != null) {
throw new IllegalArgumentException(minecraft.getName() + " configuration must contain exactly one dependency");
}
mcDependencies.remove(dep);
mcrepo = new MinecraftUserRepo(p, dep.getGroup(), dep.getName(), dep.getVersion(), new ArrayList<>(extension.getAccessTransformers().getFiles()), extension.getMappings().get());
String newDep = mcrepo.getDependencyString();
//p.getLogger().lifecycle("New Dep: " + newDep);
ExternalModuleDependency ext = (ExternalModuleDependency) p.getDependencies().create(newDep);
if (MinecraftUserRepo.CHANGING_USERDEV) {
ext.setChanging(true);
}
minecraft.resolutionStrategy(strat -> strat.cacheChangingModulesFor(10, TimeUnit.SECONDS));
minecraft.getDependencies().add(ext);
}
if (mcrepo == null) {
throw new IllegalStateException("Missing '" + minecraft.getName() + "' dependency.");
}
project.getRepositories().maven(e -> {
e.setUrl(Utils.MAVEN_CENTRAL);
e.metadataSources(m -> {
m.gradleMetadata();
m.mavenPom();
m.artifact();
});
});
project.getRepositories().maven(e -> {
e.setUrl(Utils.AIKAR_MAVEN);
e.metadataSources(m -> {
m.gradleMetadata();
m.mavenPom();
m.artifact();
});
});
project.getRepositories().maven(e -> {
e.setUrl(Utils.FORGE_MAVEN);
e.metadataSources(m -> {
m.gradleMetadata();
m.mavenPom();
m.artifact();
});
});
if (!internalObfConfiguration.getDependencies().isEmpty()) {
deobfrepo = new DeobfuscatingRepo(project, internalObfConfiguration, deobfuscator);
if (deobfrepo.getResolvedOrigin() == null) {
project.getLogger().error("DeobfRepo attempted to resolve an origin repo early but failed, this may cause issues with some IDEs");
}
}
remapper.attachMappings(extension.getMappings().get());
// We have to add these AFTER our repo so that we get called first, this is annoying...
new BaseRepo.Builder()
.add(mcrepo)
.add(deobfrepo)
.add(MCPRepo.create(project))
.add(MinecraftRepo.create(project)) //Provides vanilla extra/slim/data jars. These don't care about OBF names.
.attach(project);
MojangLicenseHelper.displayWarning(p, extension.getMappingChannel().get(), extension.getMappingVersion().get(), updateChannel, updateVersion);
project.getRepositories().maven(e -> {
e.setUrl(Utils.MOJANG_MAVEN);
e.metadataSources(MetadataSources::artifact);
});
project.getRepositories().mavenCentral(); //Needed for MCP Deps
mcrepo.validate(minecraft, extension.getRuns().getAsMap(), extractNatives.get(), downloadAssets.get(), createSrgToMcp.get()); //This will set the MC_VERSION property.
String mcVer = (String) project.getExtensions().getExtraProperties().get("MC_VERSION");
String mcpVer = (String) project.getExtensions().getExtraProperties().get("MCP_VERSION");
// TODO: convert to constant and use String.format
downloadMcpConfig.configure(t -> t.setArtifact("de.oceanlabs.mcp:mcp_config:" + mcpVer + "@zip"));
downloadMCMeta.configure(t -> t.getMCVersion().convention(mcVer));
// Register reobfJar for the 'jar' task
reobfExtension.create(JavaPlugin.JAR_TASK_NAME);
String assetIndex = mcVer;
try {
// Check meta exists
final DownloadMCMeta downloadMCMetaTask = downloadMCMeta.get();
final File metaOutput = downloadMCMetaTask.getOutput().get().getAsFile();
if (!metaOutput.exists()) {
// Force download meta
downloadMCMetaTask.downloadMCMeta();
}
VersionJson json = Utils.loadJson(metaOutput, VersionJson.class);
assetIndex = json.assetIndex.id;
} catch (IOException e) {
project.getLogger().warn("Failed to retrieve asset index ID", e);
}
// Finalize asset index
final String finalAssetIndex = assetIndex;
extension.getRuns().forEach(runConfig -> runConfig.token("asset_index", finalAssetIndex));
Utils.createRunConfigTasks(extension, extractNatives, downloadAssets, createSrgToMcp);
});
}
private NamedDomainObjectContainer createReobfExtension(Project project) {
final JavaPluginExtension javaConv = project.getExtensions().getByType(JavaPluginExtension.class);
final NamedDomainObjectContainer reobfExtension = project.container(RenameJarInPlace.class, jarName -> {
String name = StringUtils.capitalize(jarName);
final RenameJarInPlace task = project.getTasks().maybeCreate("reobf" + name, RenameJarInPlace.class);
task.getClasspath().from(javaConv.getSourceSets().named(SourceSet.MAIN_SOURCE_SET_NAME).map(SourceSet::getCompileClasspath));
task.getMappings().set(project.getTasks().named("createMcpToSrg", GenerateSRG.class).flatMap(GenerateSRG::getOutput));
project.getTasks().named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).configure(t -> t.dependsOn(task));
final TaskProvider jarTask = project.getTasks().named(jarName, Jar.class);
task.getInput().set(jarTask.flatMap(AbstractArchiveTask::getArchiveFile));
return task;
});
project.getExtensions().add("reobf", reobfExtension);
return reobfExtension;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy