1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package com.pyx4me.maven.proguard;
22
23 import java.io.File;
24 import java.net.URL;
25 import java.util.ArrayList;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Set;
30
31 import org.apache.maven.archiver.MavenArchiveConfiguration;
32 import org.apache.maven.archiver.MavenArchiver;
33 import org.apache.maven.artifact.Artifact;
34 import org.apache.maven.model.Dependency;
35 import org.apache.maven.plugin.AbstractMojo;
36 import org.apache.maven.plugin.MojoExecutionException;
37 import org.apache.maven.plugin.MojoFailureException;
38 import org.apache.maven.plugin.logging.Log;
39 import org.apache.maven.project.MavenProject;
40 import org.apache.maven.project.MavenProjectHelper;
41 import org.apache.tools.ant.DefaultLogger;
42 import org.apache.tools.ant.Project;
43 import org.apache.tools.ant.taskdefs.Java;
44 import org.codehaus.plexus.archiver.jar.JarArchiver;
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class ProGuardMojo extends AbstractMojo {
59
60
61
62
63
64
65 private boolean skip;
66
67
68
69
70
71
72 private File proguardInclude;
73
74
75
76
77
78
79 private String proguardVersion;
80
81
82
83
84
85
86 private String[] options;
87
88
89
90
91
92
93 private boolean obfuscate;
94
95
96
97
98
99
100
101 private boolean includeDependency;
102
103
104
105
106
107
108 private Assembly assembly;
109
110
111
112
113
114
115
116 private List libs;
117
118
119
120
121
122
123 private List exclusions;
124
125
126
127
128
129
130
131
132
133
134
135
136 protected String injar;
137
138
139
140
141
142
143 private boolean injarNotExistsSkip;
144
145
146
147
148
149
150 protected String inFilter;
151
152
153
154
155
156
157
158 protected String outjar;
159
160
161
162
163
164
165 private boolean attach = false;
166
167
168
169
170
171
172 private String attachArtifactType;
173
174
175
176
177
178
179 private String attachArtifactClassifier;
180
181
182
183
184
185
186 private boolean appendClassifier;
187
188
189
190
191
192
193 private boolean addMavenDescriptor;
194
195
196
197
198
199
200
201 protected File outputDirectory;
202
203
204
205
206
207
208
209
210
211 protected MavenProject mavenProject;
212
213
214
215
216
217
218
219
220 protected List pluginArtifacts;
221
222
223
224
225 private MavenProjectHelper projectHelper;
226
227
228
229
230
231
232
233 private JarArchiver jarArchiver;
234
235
236
237
238
239
240 protected MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
241
242
243
244
245
246
247 protected String maxMemory;
248
249
250
251
252
253
254 protected String proguardMainClass = "proguard.ProGuard";
255
256 private Log log;
257
258
259
260
261
262 private static String fileNameToString(String fileName) {
263 return "'" + fileName + "'";
264 }
265
266 private static String fileToString(File file) {
267 return fileNameToString(file.toString());
268 }
269
270 private boolean useArtifactClassifier() {
271 return appendClassifier && ((attachArtifactClassifier != null) && (attachArtifactClassifier.length() > 0));
272 }
273
274 public void execute() throws MojoExecutionException, MojoFailureException {
275
276 log = getLog();
277
278 if (skip) {
279 log.info("Bypass ProGuard processing because \"proguard.skip=true\"");
280 return;
281 }
282
283 boolean mainIsJar = mavenProject.getPackaging().equals("jar");
284 boolean mainIsPom = mavenProject.getPackaging().equals("pom");
285
286 File inJarFile = new File(outputDirectory, injar);
287 if (mainIsJar && (!inJarFile.exists())) {
288 if (injarNotExistsSkip) {
289 log.info("Bypass ProGuard processing because \"injar\" dos not exist");
290 return;
291 }
292 throw new MojoFailureException("Can't find file " + inJarFile);
293 }
294
295 if (mainIsPom && (!inJarFile.exists()) && injarNotExistsSkip) {
296 log.info("Bypass ProGuard processing because \"injar\" dos not exist");
297 return;
298 }
299
300 if (!outputDirectory.exists()) {
301 if (!outputDirectory.mkdirs()) {
302 throw new MojoFailureException("Can't create " + outputDirectory);
303 }
304 }
305
306 File outJarFile;
307 boolean sameArtifact;
308
309 if (attach) {
310 outjar = nameNoType(injar);
311 if (useArtifactClassifier()) {
312 outjar += "-" + attachArtifactClassifier;
313 }
314 outjar += "." + attachArtifactType;
315 }
316
317 if ((outjar != null) && (!outjar.equals(injar))) {
318 sameArtifact = false;
319 outJarFile = (new File(outputDirectory, outjar)).getAbsoluteFile();
320 if (outJarFile.exists()) {
321 if (!deleteFileOrDirectory(outJarFile)) {
322 throw new MojoFailureException("Can't delete " + outJarFile);
323 }
324 }
325 } else {
326 sameArtifact = true;
327 outJarFile = inJarFile.getAbsoluteFile();
328 File baseFile;
329 if (inJarFile.isDirectory()) {
330 baseFile = new File(outputDirectory, nameNoType(injar) + "_proguard_base");
331 } else {
332 baseFile = new File(outputDirectory, nameNoType(injar) + "_proguard_base.jar");
333 }
334 if (baseFile.exists()) {
335 if (!deleteFileOrDirectory(baseFile)) {
336 throw new MojoFailureException("Can't delete " + baseFile);
337 }
338 }
339 if (inJarFile.exists()) {
340 if (!inJarFile.renameTo(baseFile)) {
341 throw new MojoFailureException("Can't rename " + inJarFile);
342 }
343 }
344 inJarFile = baseFile;
345 }
346
347 ArrayList args = new ArrayList();
348
349 if (log.isDebugEnabled()) {
350 List dependancy = mavenProject.getCompileArtifacts();
351 for (Iterator i = dependancy.iterator(); i.hasNext();) {
352 Artifact artifact = (Artifact) i.next();
353 log.debug("--- compile artifact " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
354 + artifact.getType() + ":" + artifact.getClassifier() + " Scope:" + artifact.getScope());
355 }
356 for (Iterator i = mavenProject.getArtifacts().iterator(); i.hasNext();) {
357 Artifact artifact = (Artifact) i.next();
358 log.debug("--- artifact " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
359 + artifact.getType() + ":" + artifact.getClassifier() + " Scope:" + artifact.getScope());
360 }
361 for (Iterator i = mavenProject.getDependencies().iterator(); i.hasNext();) {
362 Dependency artifact = (Dependency) i.next();
363 log.debug("--- dependency " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
364 + artifact.getType() + ":" + artifact.getClassifier() + " Scope:" + artifact.getScope());
365 }
366 }
367
368 Set inPath = new HashSet();
369 boolean hasInclusionLibrary = false;
370 if (assembly != null) {
371 for (Iterator iter = assembly.inclusions.iterator(); iter.hasNext();) {
372 Inclusion inc = (Inclusion) iter.next();
373 if (!inc.library) {
374 File file = getClasspathElement(getDependancy(inc, mavenProject), mavenProject);
375 inPath.add(file.toString());
376 log.debug("--- ADD injars:" + inc.artifactId);
377 StringBuffer filter = new StringBuffer(fileToString(file));
378 filter.append("(!META-INF/MANIFEST.MF");
379 if (!addMavenDescriptor) {
380 filter.append(",");
381 filter.append("!META-INF/maven/**");
382 }
383 if (inc.filter != null) {
384 filter.append(",").append(inc.filter);
385 }
386 filter.append(")");
387 args.add("-injars");
388 args.add(filter.toString());
389 } else {
390 hasInclusionLibrary = true;
391 log.debug("--- ADD libraryjars:" + inc.artifactId);
392
393 File file = getClasspathElement(getDependancy(inc, mavenProject), mavenProject);
394 inPath.add(file.toString());
395 args.add("-libraryjars");
396 args.add(fileToString(file));
397 }
398 }
399 }
400
401 if ((!mainIsPom) && inJarFile.exists()) {
402 args.add("-injars");
403 StringBuffer filter = new StringBuffer(fileToString(inJarFile));
404 if ((inFilter != null) || (!addMavenDescriptor)) {
405 filter.append("(");
406 boolean coma = false;
407
408 if (!addMavenDescriptor) {
409 coma = true;
410 filter.append("!META-INF/maven/**");
411 }
412
413 if (inFilter != null) {
414 if (coma) {
415 filter.append(",");
416 }
417 filter.append(inFilter);
418 }
419
420 filter.append(")");
421 }
422 args.add(filter.toString());
423 }
424 args.add("-outjars");
425 args.add(fileToString(outJarFile));
426
427 if (!obfuscate) {
428 args.add("-dontobfuscate");
429 }
430
431 if (proguardInclude != null) {
432 if (proguardInclude.exists()) {
433 args.add("-include");
434 args.add(fileToString(proguardInclude));
435 log.debug("proguardInclude " + proguardInclude);
436 } else {
437 log.debug("proguardInclude config does not exists " + proguardInclude);
438 }
439 }
440
441 if (includeDependency) {
442 List dependency = this.mavenProject.getCompileArtifacts();
443 for (Iterator i = dependency.iterator(); i.hasNext();) {
444 Artifact artifact = (Artifact) i.next();
445
446 if (isExclusion(artifact)) {
447 continue;
448 }
449 File file = getClasspathElement(artifact, mavenProject);
450
451 if (inPath.contains(file.toString())) {
452 log.debug("--- ignore libraryjars since one in injar:" + artifact.getArtifactId());
453 continue;
454 }
455 log.debug("--- ADD libraryjars:" + artifact.getArtifactId());
456 args.add("-libraryjars");
457 args.add(fileToString(file));
458 }
459 }
460
461 if (libs != null) {
462 for (Iterator i = libs.iterator(); i.hasNext();) {
463 Object lib = i.next();
464 args.add("-libraryjars");
465 args.add(fileNameToString(lib.toString()));
466 }
467 }
468
469 args.add("-printmapping");
470 args.add(fileToString((new File(outputDirectory, "proguard_map.txt").getAbsoluteFile())));
471
472 args.add("-printseeds");
473 args.add(fileToString((new File(outputDirectory, "proguard_seeds.txt").getAbsoluteFile())));
474
475 if (log.isDebugEnabled()) {
476 args.add("-verbose");
477 }
478
479 if (options != null) {
480 for (int i = 0; i < options.length; i++) {
481 args.add(options[i]);
482 }
483 }
484
485 log.info("execute ProGuard " + args.toString());
486 proguardMain(getProguardJar(this), args, this);
487
488 if ((assembly != null) && (hasInclusionLibrary)) {
489
490 log.info("creating assembly");
491
492 File baseFile = new File(outputDirectory, nameNoType(injar) + "_proguard_result.jar");
493 if (baseFile.exists()) {
494 if (!baseFile.delete()) {
495 throw new MojoFailureException("Can't delete " + baseFile);
496 }
497 }
498 File archiverFile = outJarFile.getAbsoluteFile();
499 if (!outJarFile.renameTo(baseFile)) {
500 throw new MojoFailureException("Can't rename " + outJarFile);
501 }
502
503 MavenArchiver archiver = new MavenArchiver();
504 archiver.setArchiver(jarArchiver);
505 archiver.setOutputFile(archiverFile);
506 archive.setAddMavenDescriptor(addMavenDescriptor);
507
508 try {
509 jarArchiver.addArchivedFileSet(baseFile);
510
511 for (Iterator iter = assembly.inclusions.iterator(); iter.hasNext();) {
512 Inclusion inc = (Inclusion) iter.next();
513 if (inc.library) {
514 File file;
515 Artifact artifact = getDependancy(inc, mavenProject);
516 file = getClasspathElement(artifact, mavenProject);
517 if (file.isDirectory()) {
518 getLog().info("merge project: " + artifact.getArtifactId() + " " + file);
519 jarArchiver.addDirectory(file);
520 } else {
521 getLog().info("merge artifact: " + artifact.getArtifactId());
522 jarArchiver.addArchivedFileSet(file);
523 }
524 }
525 }
526
527 archiver.createArchive(mavenProject, archive);
528
529 } catch (Exception e) {
530 throw new MojoExecutionException("Unable to create jar", e);
531 }
532
533 }
534
535 if (attach && !sameArtifact) {
536 if (useArtifactClassifier()) {
537 projectHelper.attachArtifact(mavenProject, attachArtifactType, attachArtifactClassifier, outJarFile);
538 } else {
539 projectHelper.attachArtifact(mavenProject, attachArtifactType, null, outJarFile);
540 }
541 }
542 }
543
544 private static File getProguardJar(ProGuardMojo mojo) throws MojoExecutionException {
545
546 Artifact proguardArtifact = null;
547 int proguardArtifactDistance = -1;
548
549 for (Iterator i = mojo.pluginArtifacts.iterator(); i.hasNext();) {
550 Artifact artifact = (Artifact) i.next();
551 mojo.getLog().debug("pluginArtifact: " + artifact.getFile());
552 if ("proguard".equals(artifact.getArtifactId())) {
553 int distance = artifact.getDependencyTrail().size();
554 mojo.getLog().debug("proguard DependencyTrail: " + distance);
555 if ((mojo.proguardVersion != null) && (mojo.proguardVersion.equals(artifact.getVersion()))) {
556 proguardArtifact = artifact;
557 break;
558 } else if (proguardArtifactDistance == -1) {
559 proguardArtifact = artifact;
560 proguardArtifactDistance = distance;
561 } else if (distance < proguardArtifactDistance) {
562 proguardArtifact = artifact;
563 proguardArtifactDistance = distance;
564 }
565 }
566 }
567 if (proguardArtifact != null) {
568 mojo.getLog().debug("proguardArtifact: " + proguardArtifact.getFile());
569 return proguardArtifact.getFile().getAbsoluteFile();
570 }
571 mojo.getLog().info("proguard jar not found in pluginArtifacts");
572
573 ClassLoader cl;
574 cl = mojo.getClass().getClassLoader();
575
576 String classResource = "/" + mojo.proguardMainClass.replace('.', '/') + ".class";
577 URL url = cl.getResource(classResource);
578 if (url == null) {
579 throw new MojoExecutionException("Obfuscation failed ProGuard (" + mojo.proguardMainClass
580 + ") not found in classpath");
581 }
582 String proguardJar = url.toExternalForm();
583 if (proguardJar.startsWith("jar:file:")) {
584 proguardJar = proguardJar.substring("jar:file:".length());
585 proguardJar = proguardJar.substring(0, proguardJar.indexOf('!'));
586 } else {
587 throw new MojoExecutionException("Unrecognized location (" + proguardJar + ") in classpath");
588 }
589 return new File(proguardJar);
590 }
591
592 private static void proguardMain(File proguardJar, ArrayList argsList, ProGuardMojo mojo)
593 throws MojoExecutionException {
594
595 Java java = new Java();
596
597 Project antProject = new Project();
598 antProject.setName(mojo.mavenProject.getName());
599 antProject.init();
600
601 DefaultLogger antLogger = new DefaultLogger();
602 antLogger.setOutputPrintStream(System.out);
603 antLogger.setErrorPrintStream(System.err);
604 antLogger.setMessageOutputLevel(mojo.log.isDebugEnabled() ? Project.MSG_DEBUG : Project.MSG_INFO);
605
606 antProject.addBuildListener(antLogger);
607 antProject.setBaseDir(mojo.mavenProject.getBasedir());
608
609 java.setProject(antProject);
610 java.setTaskName("proguard");
611
612 mojo.getLog().info("proguard jar: " + proguardJar);
613
614 java.createClasspath().setLocation(proguardJar);
615
616 java.setClassname(mojo.proguardMainClass);
617
618 java.setFailonerror(true);
619
620 java.setFork(true);
621
622
623 if (mojo.maxMemory != null) {
624 java.setMaxmemory(mojo.maxMemory);
625 }
626
627 for (Iterator i = argsList.iterator(); i.hasNext();) {
628 java.createArg().setValue(i.next().toString());
629 }
630
631 int result = java.executeJava();
632 if (result != 0) {
633 throw new MojoExecutionException("Obfuscation failed (result=" + result + ")");
634 }
635 }
636
637 private static String nameNoType(String fileName) {
638 int extStart = fileName.lastIndexOf('.');
639 if (extStart == -1) {
640 return fileName;
641 }
642 return fileName.substring(0, extStart);
643 }
644
645 private static boolean deleteFileOrDirectory(File path) throws MojoFailureException {
646 if (path.isDirectory()) {
647 File[] files = path.listFiles();
648 for (int i = 0; i < files.length; i++) {
649 if (files[i].isDirectory()) {
650 if (!deleteFileOrDirectory(files[i])) {
651 throw new MojoFailureException("Can't delete dir " + files[i]);
652 }
653 } else {
654 if (!files[i].delete()) {
655 throw new MojoFailureException("Can't delete file " + files[i]);
656 }
657 }
658 }
659 return path.delete();
660 } else {
661 return path.delete();
662 }
663 }
664
665
666 private static Artifact getDependancy(Inclusion inc, MavenProject mavenProject) throws MojoExecutionException {
667 Set dependancy = mavenProject.getArtifacts();
668 for (Iterator i = dependancy.iterator(); i.hasNext();) {
669 Artifact artifact = (Artifact) i.next();
670 if (inc.match(artifact)) {
671 return artifact;
672 }
673 }
674 throw new MojoExecutionException("artifactId Not found " + inc.artifactId);
675 }
676
677 private boolean isExclusion(Artifact artifact) {
678 if (exclusions == null) {
679 return false;
680 }
681 for (Iterator iter = exclusions.iterator(); iter.hasNext();) {
682 Exclusion excl = (Exclusion) iter.next();
683 if (excl.match(artifact)) {
684 return true;
685 }
686 }
687 return false;
688 }
689
690 private static File getClasspathElement(Artifact artifact, MavenProject mavenProject) throws MojoExecutionException {
691 if (artifact.getClassifier() != null) {
692 return artifact.getFile();
693 }
694 String refId = artifact.getGroupId() + ":" + artifact.getArtifactId();
695 MavenProject project = (MavenProject) mavenProject.getProjectReferences().get(refId);
696 if (project != null) {
697 return new File(project.getBuild().getOutputDirectory());
698 } else {
699 File file = artifact.getFile();
700 if ((file == null) || (!file.exists())) {
701 throw new MojoExecutionException("Dependency Resolution Required " + artifact);
702 }
703 return file;
704 }
705 }
706 }