/* * A Gradle plugin for the creation of Minecraft mods and MinecraftForge plugins. * Copyright (C) 2013 Minecraft Forge * * 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.tasks; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.LinkedList; import java.util.List; import java.util.Map; import net.minecraftforge.gradle.common.Constants; import net.minecraftforge.gradle.util.SequencedInputSupplier; import net.minecraftforge.gradle.util.SourceDirSetSupplier; import net.minecraftforge.srg2source.rangeapplier.RangeApplier; import net.minecraftforge.srg2source.util.io.FolderSupplier; import net.minecraftforge.srg2source.util.io.InputSupplier; import net.minecraftforge.srg2source.util.io.OutputSupplier; import net.minecraftforge.srg2source.util.io.ZipInputSupplier; import net.minecraftforge.srg2source.util.io.ZipOutputSupplier; import org.gradle.api.DefaultTask; import org.gradle.api.file.FileCollection; import org.gradle.api.file.SourceDirectorySet; import org.gradle.api.tasks.*; import com.google.common.base.Charsets; import com.google.common.base.Joiner; import com.google.common.base.Strings; import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.io.Files; public class ApplyS2STask extends DefaultTask { @InputFiles private final List<Object> srg = new LinkedList<Object>(); @Optional @InputFiles private final List<Object> exc = new LinkedList<Object>(); @InputFile private Object rangeMap; @Optional @InputFile private Object excModifiers; // stuff defined on the tasks.. private final List<Object> in = new LinkedList<Object>(); private Object out; @TaskAction public void doTask() throws IOException { File out = getOut(); File rangemap = getRangeMap(); File rangelog = File.createTempFile("rangelog", ".txt", this.getTemporaryDir()); FileCollection srg = getSrgs(); FileCollection exc = getExcs(); InputSupplier inSup; if (in.size() == 1) { // just 1 supplier. inSup = getInput(in.get(0)); } else { // multinput inSup = new SequencedInputSupplier(); for (Object o : in) ((SequencedInputSupplier) inSup).add(getInput(o)); } OutputSupplier outSup; if (in.size() == 1 && in.get(0).equals(out) && in instanceof FolderSupplier) outSup = (OutputSupplier) inSup; else outSup = getOutput(out); if (getExcModifiers() != null) { getLogger().lifecycle("creating default param names"); exc = generateDefaultExc(getExcModifiers(), exc, srg); } getLogger().lifecycle("remapping source..."); applyRangeMap(inSup, outSup, srg, exc, rangemap, rangelog); inSup.close(); outSup.close(); } private InputSupplier getInput(Object o) throws IOException { if (o instanceof SourceDirectorySet) { return new SourceDirSetSupplier((SourceDirectorySet) o); } File f = getProject().file(o); if (f.isDirectory()) return new FolderSupplier(f); else if (f.getPath().endsWith(".jar") || f.getPath().endsWith(".zip")) { ZipInputSupplier supp = new ZipInputSupplier(); supp.readZip(f); return supp; } else throw new IllegalArgumentException("Can only make suppliers out of directories, zips, and SourceDirectorySets right now!"); } private OutputSupplier getOutput(File f) throws IOException { if (f.isDirectory()) return new FolderSupplier(f); else if (f.getPath().endsWith(".jar") || f.getPath().endsWith(".zip")) { return new ZipOutputSupplier(f); } else throw new IllegalArgumentException("Can only make suppliers out of directories and zips right now!"); } private void applyRangeMap(InputSupplier inSup, OutputSupplier outSup, FileCollection srg, FileCollection exc, File rangeMap, File rangeLog) throws IOException { RangeApplier app = new RangeApplier().readSrg(srg.getFiles()); app.setOutLogger(Constants.getTaskLogStream(getProject(), this.getName() + ".log")); if (!exc.isEmpty()) { app.readParamMap(exc); } // for debugging. app.dumpRenameMap(); app.remapSources(inSup, outSup, rangeMap, false); } private FileCollection generateDefaultExc(File modifiers, FileCollection currentExcs, FileCollection srgs) { if (modifiers == null || !modifiers.exists()) return currentExcs; Map<String, Boolean> statics = Maps.newHashMap(); try { getLogger().debug(" Reading Modifiers:"); for (String line : Files.readLines(modifiers, Charset.defaultCharset())) { if (Strings.isNullOrEmpty(line) || line.startsWith("#")) continue; String[] args = line.split("="); statics.put(args[0], "static".equals(args[1])); } File temp = new File(this.getTemporaryDir(), "generated.exc"); if (temp.exists()) temp.delete(); temp.getParentFile().mkdirs(); temp.createNewFile(); BufferedWriter writer = Files.newWriter(temp, Charsets.UTF_8); for (File f : srgs) { getLogger().debug(" Reading SRG: " + f); for (String line : Files.readLines(f, Charset.defaultCharset())) { if (Strings.isNullOrEmpty(line) || line.startsWith("#")) continue; String type = line.substring(0, 2); line = line.substring(4); String[] pts = line.split(" "); if (type.equals("MD")) { String name = pts[2].substring(pts[2].lastIndexOf('/') + 1); if (name.startsWith("func_")) { Boolean isStatic = statics.get(pts[0] + pts[1]); getLogger().debug(" MD: " + line); name = name.substring(5, name.indexOf('_', 5)); List<String> params = Lists.newArrayList(); int idx = isStatic == null || !isStatic.booleanValue() ? 1 : 0; getLogger().debug(" Name: " + name + " Idx: " + idx); int i = 0; boolean inArray = false; while (i < pts[1].length()) { char c = pts[1].charAt(i); switch (c) { case '(': //Start break; case ')': //End i = pts[1].length(); break; case '[': //Array inArray = true; break; case 'L': //Class String right = pts[1].substring(i); String className = right.substring(1, right.indexOf(';')); i += className.length() + 1; params.add("p_" + name + "_" + idx++ + "_"); inArray = false; break; case 'B': case 'C': case 'D': case 'F': case 'I': case 'J': case 'S': case 'Z': params.add("p_" + name + "_" + idx++ + "_"); if ((c == 'D' || c == 'J') && !inArray) idx++; inArray = false; break; default: throw new IllegalArgumentException("Unrecognized type in method descriptor: " + c); } i++; } if (params.size() > 0) { writer.write(pts[2].substring(0, pts[2].lastIndexOf('/'))); writer.write('.'); writer.write(pts[2].substring(pts[2].lastIndexOf('/') + 1)); writer.write(pts[3]); writer.write("=|"); writer.write(Joiner.on(',').join(params)); writer.newLine(); } } } } } writer.close(); List<File> files = Lists.newArrayList(); files.add(temp);//Make sure the new one is first to allow others to override for (File f : currentExcs) files.add(f); return getProject().files(files.toArray()); } catch (IOException e) { Throwables.propagate(e); } return null; } @InputFiles @SkipWhenEmpty public FileCollection getSources() { FileCollection collection = null; for (Object o: this.in) { FileCollection col; if (o instanceof SourceDirectorySet) { col = (FileCollection) o; } else { File f = getProject().file(o); if (f.isDirectory()) { col = getProject().fileTree(f); } else { col = getProject().files(f); } } if (collection == null) { collection = col; } else { collection = collection.plus(col); } } return collection; } public void addSource(Object in) { this.in.add(in); } @OutputFiles @Optional public FileCollection getOuts() { File outFile = getOut(); if (outFile.isDirectory()) return getProject().fileTree(outFile); else return getProject().files(outFile); } public File getOut() { return getProject().file(out); } public void setOut(Object out) { this.out = out; } public FileCollection getSrgs() { return getProject().files(srg); } public void addSrg(Object srg) { this.srg.add(srg); } public void addSrg(String srg) { this.srg.add(srg); } public void addSrg(File srg) { this.srg.add(srg); } public FileCollection getExcs() { return getProject().files(exc); } public void addExc(Object exc) { this.exc.add(exc); } public void addExc(String exc) { this.exc.add(exc); } public void addExc(File exc) { this.exc.add(exc); } public File getRangeMap() { return getProject().file(rangeMap); } public void setRangeMap(Object rangeMap) { this.rangeMap = rangeMap; } public void setExcModifiers(Object value) { this.excModifiers = value; } public File getExcModifiers() { return this.excModifiers == null ? null : this.getProject().file(excModifiers); } }