View Javadoc

1   /**
2    * Pyx4me framework
3    * Copyright (C) 2006-2008 pyx4j.com.
4    * 
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    * http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing,
12   * software distributed under the License is distributed on an
13   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14   * KIND, either express or implied.  See the License for the
15   * specific language governing permissions and limitations
16   * under the License.
17   * 
18   * @author vlads
19   * @version $Id: ProGuardMojo.java 2804 2008-12-11 18:31:28Z vlads $
20   */
21  package com.pyx4me.maven.j2me;
22  
23  import java.io.File;
24  import java.net.URL;
25  import java.util.ArrayList;
26  import java.util.Iterator;
27  import java.util.List;
28  
29  import org.apache.maven.artifact.Artifact;
30  import org.apache.maven.plugin.MojoExecutionException;
31  import org.apache.maven.plugin.MojoFailureException;
32  import org.apache.tools.ant.Project;
33  import org.apache.tools.ant.Task;
34  import org.apache.tools.ant.taskdefs.Java;
35  
36  import de.pleumann.antenna.misc.Utility;
37  
38  /**
39   * 
40   * <p>
41   * The Obfuscate task provides a stand-alone obfuscation task
42   * </p>
43   * 
44   * @goal obfuscate
45   * @phase package
46   * @description Create JAD files
47   * 
48   */
49  
50  public class ProGuardMojo extends WtkJadMojo {
51  
52  	/**
53  	 * Use ProGuard preverification instead of WTK. (ProGuard option -microedition)
54  	 * 
55  	 * @parameter default-value="false"
56  	 */
57  	private boolean proguardPreverify = false;
58  
59  	/**
60  	 * ProGuard configuration options
61  	 * 
62  	 * @parameter
63  	 */
64  	private ProguardOptions proguardOptions;
65  
66  	/**
67  	 * Recursively reads configuration options from the given file filename
68  	 * 
69  	 * @parameter default-value="${basedir}/proguard.conf"
70  	 */
71  	private File proguardInclude;
72  
73  	/**
74  	 * Apply ProGuard classpathentry Filters to input jar. e.g. <code>!**.gif,!**&#47;awt&#47;**,!**&#47;tests&#47;**'</code>
75  	 * 
76  	 * @parameter
77  	 */
78  	protected String inFilter;
79  
80  	static final String proguardMainClass = "proguard.ProGuard";
81  
82  	/**
83  	 * Specifies not to obfuscate the input class files.
84  	 * 
85  	 * @parameter default-value="true"
86  	 */
87  	private boolean obfuscate;
88  
89  	public void execute() throws MojoExecutionException, MojoFailureException {
90  		executeProGuard(getJarFile(classifier), null, proguardInclude, obfuscate, proguardPreverify, this, false, null,
91  				inFilter, proguardOptions, null);
92  		super.execute();
93  	}
94  
95  	/**
96  	 * ProGuard docs: Names with special characters like spaces and parentheses must be quoted with single or double
97  	 * quotes.
98  	 */
99  	private static String fileNameToString(String fileName) {
100 		return "'" + fileName + "'";
101 	}
102 
103 	private static String fileToString(File file) {
104 		return fileNameToString(file.toString());
105 	}
106 
107 	public static void executeProGuard(File jarFile, File outFile, File proguardInclude, boolean obfuscate,
108 			boolean proguardPreverify, AbstractJadWtkMojo mojo, boolean isTest, File proguardTestInclude,
109 			String inFilter, ProguardOptions options, ProguardOptions optionsTest) throws MojoExecutionException,
110 			MojoFailureException {
111 
112 		ArrayList args = new ArrayList();
113 		if (outFile == null) {
114 			outFile = jarFile;
115 		}
116 
117 		File baseFile;
118 		if (jarFile.isDirectory()) {
119 			baseFile = jarFile;
120 		} else {
121 			baseFile = new File(jarFile.toString() + "_proguard_base.jar");
122 			if (baseFile.exists()) {
123 				if (!baseFile.delete()) {
124 					throw new MojoFailureException("Can't delete " + baseFile);
125 				}
126 			}
127 
128 			if (!jarFile.renameTo(baseFile)) {
129 				throw new MojoFailureException("Can't rename " + jarFile);
130 			}
131 		}
132 
133 		args.add("-injars");
134 		StringBuffer filter = new StringBuffer(fileToString(baseFile));
135 		filter.append("(");
136 		filter.append("!META-INF/maven/**");
137 		if (inFilter != null) {
138 			filter.append(",");
139 			filter.append(inFilter);
140 		}
141 		filter.append(")");
142 
143 		args.add(filter.toString());
144 
145 		args.add("-outjars");
146 		args.add(fileToString(outFile));
147 
148 		if (!obfuscate) {
149 			args.add("-dontobfuscate");
150 		}
151 		if (proguardPreverify) {
152 			args.add("-microedition");
153 		}
154 
155 		if (proguardInclude != null) {
156 			if (proguardInclude.exists()) {
157 				args.add("-include");
158 				args.add(fileToString(proguardInclude));
159 				mojo.getLog().debug("proguardInclude " + proguardInclude);
160 			} else {
161 				mojo.getLog().debug("proguardInclude config does not exists " + proguardInclude);
162 			}
163 		}
164 
165 		if (isTest && proguardTestInclude != null) {
166 			// Can't pass this safle as cms line args beacuse of *
167 			// args.add("-keep class * extends junit.framework.TestCase");
168 			if (proguardTestInclude.exists()) {
169 				args.add("-include");
170 				args.add(fileToString(proguardTestInclude));
171 				mojo.getLog().debug("proguardTestInclude " + proguardTestInclude);
172 			} else {
173 				mojo.getLog().debug("proguardTestInclude config does not exists " + proguardTestInclude);
174 			}
175 		}
176 
177 		if (mojo.useWtkLibs) {
178 			if ((mojo.dependencies != null) && (mojo.dependencies.size() > 0)) {
179 				mojo.getLog().warn("dependencies in configuration ignored since using WTK");
180 			}
181 			// Find MIDP ClassPath from WTK
182 			Project antProject = mojo.getAntProject();
183 			Task dummyTaks = new Task() {
184 			};
185 			dummyTaks.setTaskName("proguard");
186 			dummyTaks.setProject(antProject);
187 
188 			Utility utility = Utility.getInstance(antProject, dummyTaks);
189 
190 			String mdipClassPath = utility.getMidpApi();
191 			args.add("-libraryjars");
192 			args.add(mdipClassPath);
193 		} else {
194 			List dependencies = mojo.mavenProject.getCompileArtifacts();
195 			if ((mojo.dependencies != null) && (mojo.dependencies.size() > 0)) {
196 				// Verify dependencies
197 				for (Iterator iter = mojo.dependencies.iterator(); iter.hasNext();) {
198 					Dependency dep = (Dependency) iter.next();
199 					boolean exclusionFound = false;
200 					for (Iterator i = dependencies.iterator(); i.hasNext();) {
201 						Artifact artifact = (Artifact) i.next();
202 						if (dep.match(artifact)) {
203 							exclusionFound = true;
204 							break;
205 						}
206 					}
207 					if (!exclusionFound) {
208 						mojo.getLog().warn("dependency artifact not found " + dep.toString());
209 					}
210 				}
211 			}
212 			if (mojo.libs != null) {
213 				for (Iterator i = mojo.libs.iterator(); i.hasNext();) {
214 					Object lib = i.next();
215 					args.add("-libraryjars");
216 					args.add(fileNameToString(lib.toString()));
217 				}
218 			}
219 
220 			for (Iterator i = dependencies.iterator(); i.hasNext();) {
221 				Artifact artifact = (Artifact) i.next();
222 
223 				if (!mojo.isUseDependency(artifact)) {
224 					continue;
225 				}
226 
227 				Dependency dependencyConfig = mojo.getDependencyConfig(artifact);
228 
229 				String libraryjar = fileToString(getClasspathElement(artifact, mojo.mavenProject));
230 
231 				if (dependencyConfig != null) {
232 					if (dependencyConfig.filter != null) {
233 						StringBuffer libraryFilter = new StringBuffer(libraryjar);
234 						libraryFilter.append("(");
235 						// libraryFilter.append("!META-INF/maven/**");
236 						libraryFilter.append(dependencyConfig.filter);
237 						libraryFilter.append(")");
238 						libraryjar = libraryFilter.toString();
239 					}
240 				}
241 
242 				args.add("-libraryjars");
243 				args.add(libraryjar);
244 			}
245 		}
246 
247 		// Seeds main classes
248 		if (mojo.midlets != null) {
249 			for (int i = 0; i < mojo.midlets.length; i++) {
250 				MIDlet mp = mojo.midlets[i];
251 				args.add("-keep public class " + mp.cls);
252 			}
253 		}
254 
255 		args.add("-printmapping");
256 		args.add(fileToString((new File(mojo.outputDirectory, "proguard" + (isTest ? "_test" : "") + "_map.txt")
257 				.getAbsoluteFile())));
258 
259 		args.add("-printseeds");
260 		args.add(fileToString((new File(mojo.outputDirectory, "proguard" + (isTest ? "_test" : "") + "_seeds.txt")
261 				.getAbsoluteFile())));
262 
263 		if (mojo.getLog().isDebugEnabled()) {
264 			args.add("-verbose");
265 		}
266 
267 		if ((options != null) && (options.options != null)) {
268 			for (int i = 0; i < options.options.length; i++) {
269 				args.add(options.options[i]);
270 			}
271 		}
272 
273 		if ((isTest) && (optionsTest != null) && (optionsTest.options != null)) {
274 			for (int i = 0; i < optionsTest.options.length; i++) {
275 				args.add(optionsTest.options[i]);
276 			}
277 		}
278 
279 		mojo.getLog().info("execute ProGuard " + args.toString());
280 		proguardMain(getProguardJar(mojo), args, mojo);
281 	}
282 
283 	private static File getProguardJar(AbstractJadWtkMojo mojo) throws MojoExecutionException {
284 
285 		Artifact proguardArtifact = null;
286 		int proguardArtifactDistance = -1;
287 		// This should be solved in Maven 2.1
288 		for (Iterator i = mojo.pluginArtifacts.iterator(); i.hasNext();) {
289 			Artifact artifact = (Artifact) i.next();
290 			mojo.getLog().debug("pluginArtifact: " + artifact.getFile());
291 			if ("proguard".equals(artifact.getArtifactId())) {
292 				int distance = artifact.getDependencyTrail().size();
293 				if ((mojo.proguardVersion != null) && (mojo.proguardVersion.equals(artifact.getVersion()))) {
294 					proguardArtifact = artifact;
295 					break;
296 				} else if (proguardArtifactDistance == -1) {
297 					proguardArtifact = artifact;
298 					proguardArtifactDistance = distance;
299 				} else if (distance < proguardArtifactDistance) {
300 					proguardArtifact = artifact;
301 					proguardArtifactDistance = distance;
302 				}
303 			}
304 		}
305 		if (proguardArtifact != null) {
306 			mojo.getLog().debug("proguardArtifact: " + proguardArtifact.getFile());
307 			return proguardArtifact.getFile().getAbsoluteFile();
308 		}
309 		mojo.getLog().info("proguard jar not found in pluginArtifacts");
310 
311 		ClassLoader cl;
312 		cl = mojo.getClass().getClassLoader();
313 		// cl = Thread.currentThread().getContextClassLoader();
314 		String classResource = "/" + proguardMainClass.replace('.', '/') + ".class";
315 		URL url = cl.getResource(classResource);
316 		if (url == null) {
317 			throw new MojoExecutionException("Obfuscation failed ProGuard (" + proguardMainClass
318 					+ ") not found in classpath");
319 		}
320 		String proguardJar = url.toExternalForm();
321 		if (proguardJar.startsWith("jar:file:")) {
322 			proguardJar = proguardJar.substring("jar:file:".length());
323 			proguardJar = proguardJar.substring(0, proguardJar.indexOf('!'));
324 		} else {
325 			throw new MojoExecutionException("Unrecognized location (" + proguardJar + ") in classpath");
326 		}
327 		return new File(proguardJar);
328 	}
329 
330 	private static void proguardMain(File proguardJar, ArrayList argsList, AbstractJadWtkMojo mojo)
331 			throws MojoExecutionException {
332 
333 		Java java = new Java();
334 
335 		java.setProject(mojo.getAntProject());
336 		java.setTaskName("proguard");
337 
338 		mojo.getLog().info("proguard jar: " + proguardJar);
339 		java.createClasspath().setLocation(proguardJar);
340 		// java.createClasspath().setPath(System.getProperty("java.class.path"));
341 		java.setClassname(proguardMainClass);
342 
343 		java.setFailonerror(true);
344 
345 		java.setFork(true);
346 
347 		for (Iterator i = argsList.iterator(); i.hasNext();) {
348 			String line = i.next().toString();
349 			java.createArg().setValue(line);
350 			mojo.getLog().debug("[" + line + "]");
351 		}
352 
353 		int result = java.executeJava();
354 		if (result != 0) {
355 			throw new MojoExecutionException("Obfuscation failed (result=" + result + ")");
356 		}
357 	}
358 }