001: package tide.exttools.proguard;
002:
003: import tide.editor.*;
004: import tide.project.*;
005: import tide.utils.*;
006: import snow.utils.gui.*;
007: import snow.utils.storage.*;
008: import snow.utils.*;
009: import javax.swing.*;
010: import javax.swing.event.*;
011: import javax.swing.border.*;
012: import java.awt.event.*;
013: import java.io.*;
014: import java.util.*;
015: import java.util.regex.*;
016: import java.util.jar.*;
017:
018: /** Versions 3.6 and 3.7 beta don't support class version 50 (from java 6)...
019: * version 3.8: very good, but with the special flags (see settings)
020: * v4.0beta3: not ok, I must look TODO...
021: * java.lang.NoSuchMethodError: java.awt.Component.modelToView(I)Ljava/awt/Rectangle
022: *
023: * V4.0.1: OK, seems good.
024: */
025: @net.jcip.annotations.ThreadSafe
026: public final class ProGuardLauncher {
027: /* example:tide
028: -dontskipnonpubliclibraryclasses
029: -overloadaggressively
030: -defaultpackage ''
031: -allowaccessmodification
032: -keepattributes LineNumberTable,Signature
033: -keepclasseswithmembers class $mainclass {
034: public static void main(java.lang.String[]);
035: }
036: -keepclasseswithmembers class TideJarReplacer {
037: public static void main(java.lang.String[]);
038: }
039: -keepclasseswithmembers class tide.execute.StaticStarter {
040: public static void main(java.lang.String[]);
041: }
042: -keep class tide.execute.StaticStarter* # also the inner classes!!
043: -keep class TideJarReplacer* # also the inner classes!!
044: -keep class tide.exttools.PMD.PMDLauncher # because of the xml resource
045: -keep class tide.jmx.**
046: -keep class * extends javax.swing.plaf.ComponentUI {
047: public static javax.swing.plaf.ComponentUI createUI(javax.swing.JComponent);
048: }
049: -keepclassmembers class * extends java.lang.Enum {
050: public static **[] values();
051: public static ** valueOf(java.lang.String);
052: }
053: */
054:
055: private ProGuardLauncher() {
056: }
057:
058: /** As argument: the jar file to obfuscate/optimize/shrink
059: * create a temp destination that will be on success renamed to the jarFile.
060: * creates also a proguard.map allowing to recover the semantics of stacktraces.
061: */
062: public static void obfuscate(final File jarFile,
063: final ProgressModalDialog pmd,
064: final String mainClassJavaName) throws Exception {
065: ProjectSettings actualProject = MainEditorFrame.instance
066: .getActualProject();
067:
068: File csJar = new File(actualProject.getProperty(
069: "ProGuard_path",
070: ProGuardSettingsDialog.defaultPGLocation));
071: if (!csJar.exists())
072: throw new Exception("ProGuard jar not found at "
073: + csJar.getAbsolutePath());
074:
075: File javaExePath = actualProject.getJava_TOOL();
076: String keepArguments = actualProject.getProperty(
077: "ProGuard_Arguments",
078: ProGuardSettingsDialog.DEFAULT_ARGS);
079: boolean shrink = actualProject.getBooleanProperty(
080: "ProGuard_Shrink", true);
081: boolean optimize = actualProject.getBooleanProperty(
082: "ProGuard_Optimize", false); // NOT STABLE !
083: boolean obfuscate = actualProject.getBooleanProperty(
084: "ProGuard_Obfuscate", true);
085:
086: List<String> execCommand = new ArrayList<String>();
087: execCommand.add(javaExePath.getAbsolutePath());
088:
089: //OLD execCommand.add( "-Xmx1024m");
090: execCommand
091: .addAll(ProjectUtils
092: .splitArgs(
093: actualProject
094: .getProperty(
095: "ProGuard_JVMOpts",
096: ProGuardSettingsDialog.defaultPGVMOpts),
097: false));
098:
099: execCommand.add("-jar");
100: execCommand.add(csJar.getAbsolutePath());
101:
102: String jarName = jarFile.getName();
103: File tempDest = new File(jarFile.getAbsolutePath()
104: + "_proguardtemp.jar"); // important: must end with jar, otherwise, creates a directory...
105: File settingsFile = new File(jarFile.getParentFile(),
106: "proguard.pro.temp");
107: settingsFile.deleteOnExit();
108: tempDest.deleteOnExit();
109:
110: File defMapFile = new File(actualProject.getSources_Home()
111: .getParentFile(), "dev/proguard.map");
112: File mapFile = new File(actualProject.getProperty(
113: "ProGuard_mappath", defMapFile.getAbsolutePath()));
114: if (!mapFile.getParentFile().exists()) {
115: mapFile.getParentFile().mkdirs();
116: }
117:
118: Collection<File> classPath = actualProject.getClassPath(true,
119: true); // removes the ignored
120:
121: execCommand.add("-injars");
122: execCommand.add(jarName); // nameIn
123: execCommand.add("-outjars");
124: execCommand.add(tempDest.getName()); // nameOut
125:
126: keepArguments = keepArguments.replace("$mainclass",
127: mainClassJavaName);
128: String settingFileContent = createProSetingsForObfuscation(classPath);
129: FileUtils.saveToFile(settingFileContent + "\r\n\r\n"
130: + keepArguments, settingsFile);
131:
132: // options file
133: execCommand.add("@" + settingsFile.getName());
134:
135: if (!shrink) {
136: execCommand.add("-dontshrink");
137: }
138: if (!optimize) {
139: execCommand.add("-dontoptimize");
140: }
141: if (!obfuscate) {
142: execCommand.add("-dontobfuscate");
143: } else {
144: execCommand.add("-printmapping");
145: execCommand.add(mapFile.getAbsolutePath()); //quote ?
146: }
147:
148: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
149: .appendLine("");
150: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
151: .appendDatedLine("Proguard processing of " + jarFile
152: + ":");
153: MainEditorFrame.instance.outputPanels.selectToolsTab(true);
154:
155: System.out.println("Running ProGuard: " + execCommand); // in the jar directory
156: //System.out.println("config file:\n"+settingFileContent+"\n");
157:
158: StringBuilder descr = new StringBuilder("Proguard: ");
159: if (shrink)
160: descr.append(" shrink &");
161: if (optimize)
162: descr.append(" optimize & ");
163: if (obfuscate)
164: descr.append(" obfuscate & ");
165: descr.setLength(descr.length() - 2); // remove the last "& "
166: if (!shrink && !optimize && !obfuscate) {
167: descr.append(" only analyse");
168: }
169:
170: pmd.setProgressSection(descr.toString());
171:
172: // todo: set the progress label to the read lines "Reading *" and "Preparing *" " Copying *"
173:
174: ProcessBuilder pb = new ProcessBuilder(execCommand);
175: pb.directory(jarFile.getParentFile());
176: Process proc = pb.start();
177:
178: MainEditorFrame.instance.outputPanels.processesManager
179: .addProcess("Proguard processing of " + jarFile + "",
180: proc, true);
181:
182: // Todo: analyse ?
183: StreamGobbler sg = new StreamGobbler(proc.getInputStream());
184: //MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc.createWriterForThisDocument(false),
185: // "ProGuard");
186: sg.start();
187: sg.setLineListener(new StringGobblerLineListener() {
188: //@Override
189: public String lineToMatch() {
190: return null;
191: }
192:
193: public void lineMatch(String line) {
194: }
195:
196: public void lineRead(String line) {
197: String linet = line.trim(); // some have indent?
198: if (linet.startsWith("Reading")
199: || linet.startsWith("Preparing")
200: || linet.startsWith("Copying")) {
201: pmd.setProgressComment(linet);
202: } else {
203: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
204: .appendLine("ProGuard> " + line);
205: }
206: }
207:
208: });
209:
210: // errors
211: StreamGobbler sge = new StreamGobbler(
212: proc.getErrorStream(),
213: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
214: .createWriterForThisDocument(true), "ProGuard");
215: sge.start();
216:
217: // wait until proc completed...
218: proc.waitFor();
219: sg.join();
220: sge.join();
221:
222: // it worked (?)
223: if (tempDest.length() > 0) {
224: System.out.println("Proguard results: ");
225: System.out.println(" Before: \tjar file length="
226: + jarFile.length() + " bytes");
227: System.out.println(" After: \tjar file length="
228: + tempDest.length() + " bytes");
229:
230: int perc = (int) (100 * (jarFile.length() - tempDest
231: .length()) / jarFile.length());
232:
233: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
234: .appendLine("Proguard results: "
235: + "\n Before: \tjar file length="
236: + jarFile.length() + " bytes"
237: + "\n After: \tjar file length="
238: + tempDest.length() + " bytes (shrinked "
239: + perc + "%)");
240:
241: // try the easy way
242: if (!tempDest.renameTo(jarFile)) {
243: // or harder
244: FileUtils.copy(tempDest, jarFile);
245: if (!tempDest.delete()) {
246: tempDest.deleteOnExit();
247: }
248: }
249: } else {
250: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
251: .appendErrorLine("Problem, ProGuard processed jar result is empty.");
252: tempDest.deleteOnExit();
253: }
254:
255: //on exit... but now we don't need it anymore
256: settingsFile.delete();
257: }
258:
259: /** @param classPath should contain all used libs. On doubt, pass all the libs of the jre/lib and
260: * project specific.
261: */
262: public static String createProSetingsForObfuscation(
263: Collection<File> classPath) {
264: //System.out.println("Proguard classpath length: "+classPath.size());
265: StringBuilder sb = new StringBuilder();
266: if (classPath != null && classPath.size() > 0) {
267: sb.append("\r\n-libraryjars ");
268: for (File f : classPath) //<java.home>/lib/rt.jar;<java.home>/lib/javaws.jar");
269: {
270: sb.append("" + f.getAbsolutePath() + ";");
271: }
272: sb.setLength(sb.length() - 1); // remove the last ";".
273: }
274:
275: return sb.toString();
276: }
277: }
|