1 package com.jayway.maven.plugins.android.phase04processclasses;
2
3 import com.jayway.maven.plugins.android.AbstractAndroidMojo;
4 import com.jayway.maven.plugins.android.CommandExecutor;
5 import com.jayway.maven.plugins.android.ExecutionException;
6 import com.jayway.maven.plugins.android.config.ConfigHandler;
7 import com.jayway.maven.plugins.android.config.ConfigPojo;
8 import com.jayway.maven.plugins.android.config.PullParameter;
9 import com.jayway.maven.plugins.android.configuration.Proguard;
10 import org.apache.commons.lang.StringUtils;
11 import org.apache.maven.RepositoryUtils;
12 import org.apache.maven.artifact.Artifact;
13 import org.apache.maven.plugin.MojoExecutionException;
14 import org.apache.maven.plugin.MojoFailureException;
15 import org.sonatype.aether.util.artifact.DefaultArtifact;
16 import org.sonatype.aether.util.artifact.JavaScopes;
17
18 import java.io.File;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.LinkedList;
25 import java.util.List;
26
27
28
29
30
31
32
33
34
35
36
37
38
39 public class ProguardMojo extends AbstractAndroidMojo
40 {
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 @ConfigPojo
75 protected Proguard proguard;
76
77
78
79
80
81
82
83 private Boolean proguardSkip;
84
85 @PullParameter( defaultValue = "true" )
86 private Boolean parsedSkip;
87
88
89
90
91
92
93
94 private String proguardConfig;
95
96 @PullParameter( defaultValue = "proguard.cfg" )
97 private String parsedConfig;
98
99
100
101
102
103
104
105 private String[] proguardConfigs;
106
107 @PullParameter( defaultValueGetterMethod = "getDefaultProguardConfigs" )
108 private String[] parsedConfigs;
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 private String proguardProguardJarPath;
135
136 @PullParameter( defaultValueGetterMethod = "getProguardJarPath" )
137 private String parsedProguardJarPath;
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164 private String outputDirectory;
165
166 @PullParameter( defaultValue = "proguard" )
167 private String parsedOutputDirectory;
168
169
170
171
172
173
174
175
176
177 private String[] proguardJvmArguments;
178
179 @PullParameter( defaultValueGetterMethod = "getDefaultJvmArguments" )
180 private String[] parsedJvmArguments;
181
182
183
184
185
186
187
188 private Boolean proguardFilterMavenDescriptor;
189
190 @PullParameter( defaultValue = "true" )
191 private Boolean parsedFilterMavenDescriptor;
192
193
194
195
196
197
198
199 private Boolean proguardFilterManifest;
200
201 @PullParameter( defaultValue = "true" )
202 private Boolean parsedFilterManifest;
203
204
205
206
207
208
209 private Boolean includeJdkLibs;
210
211 @PullParameter( defaultValue = "true" )
212 private Boolean parsedIncludeJdkLibs;
213
214
215
216
217
218
219
220
221 protected List<Artifact> pluginDependencies;
222
223 public static final String PROGUARD_OBFUSCATED_JAR = "proguard-obfuscated.jar";
224
225 private static final Collection<String> ANDROID_LIBRARY_EXCLUDED_FILTER = Arrays
226 .asList( "org/xml/**", "org/w3c/**", "java/**", "javax/**" );
227
228 private static final Collection<String> MAVEN_DESCRIPTOR = Arrays.asList( "META-INF/maven/**" );
229 private static final Collection<String> META_INF_MANIFEST = Arrays.asList( "META-INF/MANIFEST.MF" );
230
231
232
233
234 private static final String USED_DEPENDENCY_TYPE = "jar";
235
236 private Collection<String> globalInJarExcludes = new HashSet<String>();
237
238 private List<Artifact> artifactBlacklist = new LinkedList<Artifact>();
239 private List<Artifact> artifactsToShift = new LinkedList<Artifact>();
240
241 private List<ProGuardInput> inJars = new LinkedList<ProguardMojo.ProGuardInput>();
242 private List<ProGuardInput> libraryJars = new LinkedList<ProguardMojo.ProGuardInput>();
243
244 private File javaHomeDir;
245 private File javaLibDir;
246 private File altJavaLibDir;
247
248 private static class ProGuardInput
249 {
250
251 private String path;
252 private Collection<String> excludedFilter;
253
254 public ProGuardInput( String path, Collection<String> excludedFilter )
255 {
256 this.path = path;
257 this.excludedFilter = excludedFilter;
258 }
259
260 public String toCommandLine()
261 {
262 if ( excludedFilter != null && ! excludedFilter.isEmpty() )
263 {
264 StringBuilder sb = new StringBuilder( path );
265 sb.append( '(' );
266 for ( Iterator<String> it = excludedFilter.iterator(); it.hasNext(); )
267 {
268 sb.append( '!' ).append( it.next() );
269 if ( it.hasNext() )
270 {
271 sb.append( ',' );
272 }
273 }
274 sb.append( ')' );
275 return sb.toString();
276 }
277 else
278 {
279 return "\'" + path + "\'";
280 }
281 }
282 }
283
284 @Override
285 public void execute() throws MojoExecutionException, MojoFailureException
286 {
287 ConfigHandler configHandler = new ConfigHandler( this );
288 configHandler.parseConfiguration();
289
290 if ( ! parsedSkip )
291 {
292 executeProguard();
293 }
294 }
295
296 private void executeProguard() throws MojoExecutionException
297 {
298
299 final File proguardDir = new File( project.getBuild().getDirectory(), parsedOutputDirectory );
300
301 if ( ! proguardDir.exists() && ! proguardDir.mkdir() )
302 {
303 throw new MojoExecutionException( "Cannot create proguard output directory" );
304 }
305 else
306 {
307 if ( proguardDir.exists() && ! proguardDir.isDirectory() )
308 {
309 throw new MojoExecutionException( "Non-directory exists at " + proguardDir.getAbsolutePath() );
310 }
311 }
312
313 CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor();
314 executor.setLogger( this.getLog() );
315 List<String> commands = new ArrayList<String>();
316
317 collectJvmArguments( commands );
318
319 commands.add( "-jar" );
320 commands.add( parsedProguardJarPath );
321
322 commands.add( "@" + parsedConfig );
323
324 for ( String config : parsedConfigs )
325 {
326 commands.add( "@" + config );
327 }
328
329 if ( proguardFile != null )
330 {
331 commands.add( "@" + proguardFile.getAbsolutePath() );
332 }
333
334 collectInputFiles( commands );
335
336 commands.add( "-outjars" );
337 commands.add( "'" + project.getBuild().getDirectory() + File.separator + PROGUARD_OBFUSCATED_JAR + "'" );
338
339 commands.add( "-dump" );
340 commands.add( "'" + proguardDir + File.separator + "dump.txt'" );
341 commands.add( "-printseeds" );
342 commands.add( "'" + proguardDir + File.separator + "seeds.txt'" );
343 commands.add( "-printusage" );
344 commands.add( "'" + proguardDir + File.separator + "usage.txt'" );
345 commands.add( "-printmapping" );
346 commands.add( "'" + proguardDir + File.separator + "mapping.txt'" );
347
348 final String javaExecutable = getJavaExecutable().getAbsolutePath();
349 getLog().info( javaExecutable + " " + commands.toString() );
350 try
351 {
352 executor.executeCommand( javaExecutable, commands, project.getBasedir(), false );
353 }
354 catch ( ExecutionException e )
355 {
356 throw new MojoExecutionException( "", e );
357 }
358 }
359
360
361
362
363
364
365
366 private void collectJvmArguments( List<String> commands )
367 {
368 if ( parsedJvmArguments != null )
369 {
370 for ( String jvmArgument : parsedJvmArguments )
371 {
372
373
374
375 if ( ! jvmArgument.startsWith( "-" ) )
376 {
377 jvmArgument = "-" + jvmArgument;
378 }
379 commands.add( jvmArgument );
380 }
381 }
382 }
383
384 private void collectInputFiles( List<String> commands )
385 {
386
387
388 skipArtifact( "commons-logging", "commons-logging", true );
389
390 collectProgramInputFiles();
391 for ( ProGuardInput injar : inJars )
392 {
393 commands.add( "-injars" );
394 commands.add( injar.toCommandLine() );
395 }
396
397 collectLibraryInputFiles();
398 for ( ProGuardInput libraryjar : libraryJars )
399 {
400 commands.add( "-libraryjars" );
401 commands.add( libraryjar.toCommandLine() );
402 }
403 }
404
405
406
407
408
409
410 private static File getJavaExecutable()
411 {
412 final String javaHome = System.getProperty( "java.home" );
413 final String slash = File.separator;
414 return new File( javaHome + slash + "bin" + slash + "java" );
415 }
416
417 private void skipArtifact( String groupId, String artifactId, boolean shiftToLibraries )
418 {
419 artifactBlacklist.add( RepositoryUtils.toArtifact( new DefaultArtifact( groupId, artifactId, null, null ) ) );
420 if ( shiftToLibraries )
421 {
422 artifactsToShift
423 .add( RepositoryUtils.toArtifact( new DefaultArtifact( groupId, artifactId, null, null ) ) );
424 }
425 }
426
427 private boolean isBlacklistedArtifact( Artifact artifact )
428 {
429 for ( Artifact artifactToSkip : artifactBlacklist )
430 {
431 if ( artifactToSkip.getGroupId().equals( artifact.getGroupId() ) && artifactToSkip.getArtifactId()
432 .equals( artifact.getArtifactId() ) )
433 {
434 return true;
435 }
436 }
437 return false;
438 }
439
440 private boolean isShiftedArtifact( Artifact artifact )
441 {
442 for ( Artifact artifactToShift : artifactsToShift )
443 {
444 if ( artifactToShift.getGroupId().equals( artifact.getGroupId() ) && artifactToShift.getArtifactId()
445 .equals( artifact.getArtifactId() ) )
446 {
447 return true;
448 }
449 }
450 return false;
451 }
452
453 private void collectProgramInputFiles()
454 {
455 if ( parsedFilterManifest )
456 {
457 globalInJarExcludes.addAll( META_INF_MANIFEST );
458 }
459 if ( parsedFilterMavenDescriptor )
460 {
461 globalInJarExcludes.addAll( MAVEN_DESCRIPTOR );
462 }
463
464
465 addInJar( project.getBuild().getOutputDirectory() );
466
467
468 for ( Artifact artifact : getAllRelevantDependencyArtifacts() )
469 {
470 if ( isBlacklistedArtifact( artifact ) || !USED_DEPENDENCY_TYPE.equals( artifact.getType() ) )
471 {
472 continue;
473 }
474 addInJar( artifact.getFile().getAbsolutePath(), globalInJarExcludes );
475 }
476 }
477
478 private void addInJar( String path, Collection<String> filterExpression )
479 {
480 inJars.add( new ProGuardInput( path, filterExpression ) );
481 }
482
483 private void addInJar( String path )
484 {
485 addInJar( path, null );
486 }
487
488 private void addLibraryJar( String path, Collection<String> filterExpression )
489 {
490 libraryJars.add( new ProGuardInput( path, filterExpression ) );
491 }
492
493 private void addLibraryJar( String path )
494 {
495 addLibraryJar( path, null );
496 }
497
498 private void collectLibraryInputFiles()
499 {
500 if ( parsedIncludeJdkLibs )
501 {
502
503
504
505 File rtJar = getJVMLibrary( "rt.jar" );
506 if ( rtJar == null )
507 {
508 rtJar = getJVMLibrary( "classes.jar" );
509 }
510 if ( rtJar != null )
511 {
512 addLibraryJar( rtJar.getPath() );
513 }
514
515
516 File jsseJar = getJVMLibrary( "jsse.jar" );
517 if ( jsseJar != null )
518 {
519 addLibraryJar( jsseJar.getPath() );
520 }
521
522
523 File jceJar = getJVMLibrary( "jce.jar" );
524 if ( jceJar != null )
525 {
526 addLibraryJar( jceJar.getPath() );
527 }
528 }
529
530
531 for ( Artifact artifact : project.getArtifacts() )
532 {
533 if ( artifact.getScope().equals( JavaScopes.PROVIDED ) )
534 {
535 if ( artifact.getArtifactId().equals( "android" ) && parsedIncludeJdkLibs )
536 {
537 addLibraryJar( artifact.getFile().getAbsolutePath(), ANDROID_LIBRARY_EXCLUDED_FILTER );
538 }
539 else
540 {
541 addLibraryJar( artifact.getFile().getAbsolutePath() );
542 }
543 }
544 else
545 {
546 if ( isShiftedArtifact( artifact ) )
547 {
548
549 addLibraryJar( artifact.getFile().getAbsolutePath() );
550 }
551 }
552 }
553 }
554
555
556
557
558
559
560
561
562 private String getProguardJarPath() throws MojoExecutionException
563 {
564 String proguardJarPath = getProguardJarPathFromDependencies();
565 if ( StringUtils.isEmpty( proguardJarPath ) )
566 {
567 File proguardJarPathFile = new File( getAndroidSdk().getToolsPath(), "proguard/lib/proguard.jar" );
568 return proguardJarPathFile.getAbsolutePath();
569 }
570 return proguardJarPath;
571 }
572
573 private String getProguardJarPathFromDependencies() throws MojoExecutionException
574 {
575 Artifact proguardArtifact = null;
576 int proguardArtifactDistance = - 1;
577 for ( Artifact artifact : pluginDependencies )
578 {
579 getLog().debug( "pluginArtifact: " + artifact.getFile() );
580 if ( ( "proguard".equals( artifact.getArtifactId() ) ) || ( "proguard-base"
581 .equals( artifact.getArtifactId() ) ) )
582 {
583 int distance = artifact.getDependencyTrail().size();
584 getLog().debug( "proguard DependencyTrail: " + distance );
585 if ( proguardArtifactDistance == - 1 )
586 {
587 proguardArtifact = artifact;
588 proguardArtifactDistance = distance;
589 }
590 else
591 {
592 if ( distance < proguardArtifactDistance )
593 {
594 proguardArtifact = artifact;
595 proguardArtifactDistance = distance;
596 }
597 }
598 }
599 }
600 if ( proguardArtifact != null )
601 {
602 getLog().debug( "proguardArtifact: " + proguardArtifact.getFile() );
603 return proguardArtifact.getFile().getAbsoluteFile().toString();
604 }
605 else
606 {
607 return null;
608 }
609
610 }
611
612
613
614
615
616
617
618 private String[] getDefaultJvmArguments()
619 {
620 return new String[]{ "-Xmx512M" };
621 }
622
623
624
625
626
627
628
629 private String[] getDefaultProguardConfigs()
630 {
631 return new String[0];
632 }
633
634
635
636
637
638
639 private File getJVMLibrary( String fileName )
640 {
641 File libFile = new File( getJavaLibDir(), fileName );
642 if ( !libFile.exists() )
643 {
644 libFile = new File( getAltJavaLibDir(), fileName );
645 if ( !libFile.exists() )
646 {
647 libFile = null;
648 }
649 }
650 return libFile;
651 }
652
653
654
655
656
657 private File getJavaHomeDir()
658 {
659 if ( javaHomeDir == null )
660 {
661 javaHomeDir = new File( System.getProperty( "java.home" ) );
662 }
663 return javaHomeDir;
664 }
665
666
667
668
669
670 private File getJavaLibDir()
671 {
672 if ( javaLibDir == null )
673 {
674 javaLibDir = new File( getJavaHomeDir(), "lib" );
675 }
676 return javaLibDir;
677 }
678
679
680
681
682
683
684 private File getAltJavaLibDir()
685 {
686 if ( altJavaLibDir == null )
687 {
688 altJavaLibDir = new File( getJavaHomeDir().getParent(), "Classes" );
689 }
690 return altJavaLibDir;
691 }
692 }