1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.simpligility.maven.plugins.android.phase08preparepackage;
18
19 import static com.simpligility.maven.plugins.android.InclusionExclusionResolver.filterArtifacts;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Set;
27
28 import org.apache.commons.io.FileUtils;
29 import org.apache.commons.lang3.StringUtils;
30 import org.apache.maven.artifact.Artifact;
31 import org.apache.maven.model.Resource;
32 import org.apache.maven.plugin.MojoExecutionException;
33 import org.apache.maven.plugin.MojoFailureException;
34 import org.apache.maven.plugins.annotations.LifecyclePhase;
35 import org.apache.maven.plugins.annotations.Mojo;
36 import org.apache.maven.plugins.annotations.Parameter;
37 import org.apache.maven.plugins.annotations.ResolutionScope;
38 import org.codehaus.plexus.archiver.ArchiverException;
39 import org.codehaus.plexus.archiver.jar.JarArchiver;
40 import org.codehaus.plexus.archiver.util.DefaultFileSet;
41
42 import com.simpligility.maven.plugins.android.AbstractAndroidMojo;
43 import com.simpligility.maven.plugins.android.CommandExecutor;
44 import com.simpligility.maven.plugins.android.ExecutionException;
45 import com.simpligility.maven.plugins.android.IncludeExcludeSet;
46 import com.simpligility.maven.plugins.android.configuration.D8;
47
48
49
50
51
52
53
54
55
56
57 @Mojo(
58 name = "d8",
59 defaultPhase = LifecyclePhase.PREPARE_PACKAGE,
60 requiresDependencyResolution = ResolutionScope.COMPILE
61 )
62 public class D8Mojo extends AbstractAndroidMojo
63 {
64 private static final String JAR = "jar";
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 @Parameter( property = "android.dex.compiler", defaultValue = "dex" )
95 private String dexCompiler;
96
97 @Parameter
98 private D8 d8;
99
100
101
102
103 @Parameter( property = "android.d8.jvmArguments", defaultValue = "-Xmx1024M" )
104 private String[] d8JvmArguments;
105
106
107
108
109 @Parameter( property = "android.d8.intermediate", defaultValue = "false" )
110 private boolean d8Intermediate;
111
112
113
114
115 @Parameter( property = "android.d8.mainDexList" )
116 private String d8MainDexList;
117
118
119
120
121 @Parameter( property = "android.d8.release", defaultValue = "false" )
122 private boolean d8Release;
123
124
125
126
127 @Parameter( property = "android.d8.minApi" )
128 private Integer d8MinApi;
129
130
131
132
133 @Parameter( property = "android.d8.arguments" )
134 private String[] d8Arguments;
135
136
137
138
139 @Parameter( property = "android.proguard.obfuscatedJar" )
140 private File obfuscatedJar;
141
142
143
144
145
146 @Parameter( property = "skipDependencies", defaultValue = "false" )
147 private boolean skipDependencies;
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164 @Parameter( property = "artifactTypeSet" )
165 private IncludeExcludeSet artifactTypeSet;
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187 @Parameter( property = "artifactSet" )
188 private IncludeExcludeSet artifactSet;
189
190 private String[] parsedJvmArguments;
191 private boolean parsedIntermediate;
192 private String parsedMainDexList;
193 private String[] parsedArguments;
194 private DexCompiler parsedDexCompiler;
195 private boolean parsedRelease;
196 private Integer parsedMinApi;
197
198
199
200
201
202 @Override
203 public void execute() throws MojoExecutionException, MojoFailureException
204 {
205 parseConfiguration();
206
207 getLog().debug( "DexCompiler set to " + parsedDexCompiler );
208 if ( parsedDexCompiler != DexCompiler.D8 )
209 {
210 getLog().info( "Not executing D8Mojo because DEX compiler is set to " + parsedDexCompiler );
211 return;
212 }
213
214 CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor();
215 executor.setLogger( getLog() );
216
217 if ( generateApk )
218 {
219 runD8( executor );
220 }
221
222 if ( attachJar )
223 {
224 File jarFile = new File( targetDirectory + File.separator
225 + finalName + ".jar" );
226 projectHelper.attachArtifact( project, "jar", project.getArtifact().getClassifier(), jarFile );
227 }
228
229 if ( attachSources )
230 {
231
232 final File apksources = createApkSourcesFile();
233 projectHelper.attachArtifact( project, "apksources", apksources );
234 }
235 }
236
237 private List<File> getDependencies()
238 {
239 final List<File> libraries = new ArrayList<>();
240 for ( Artifact artifact : filterArtifacts( getTransitiveDependencyArtifacts(), skipDependencies,
241 artifactTypeSet.getIncludes(), artifactTypeSet.getExcludes(), artifactSet.getIncludes(),
242 artifactSet.getExcludes() ) )
243 {
244 if ( "jar".equals( artifact.getType() ) )
245 {
246 libraries.add( artifact.getFile() );
247 }
248 }
249
250 return libraries;
251 }
252
253
254
255
256 private Set< File > getD8InputFiles()
257 {
258 final Set< File > inputs = new HashSet< File >();
259
260 if ( obfuscatedJar != null && obfuscatedJar.exists() )
261 {
262
263 getLog().debug( "Adding dex input (obfuscatedJar) : " + obfuscatedJar );
264 inputs.add( obfuscatedJar );
265 }
266 else
267 {
268 getLog().debug( "Using non-obfuscated input" );
269 final File classesJar = new File( targetDirectory, finalName + ".jar" );
270 inputs.add( classesJar );
271 getLog().debug( "Adding dex input from : " + classesJar );
272
273 for ( Artifact artifact : filterArtifacts( getTransitiveDependencyArtifacts(), skipDependencies,
274 artifactTypeSet.getIncludes(), artifactTypeSet.getExcludes(), artifactSet.getIncludes(),
275 artifactSet.getExcludes() ) )
276 {
277 if ( artifact.getType().equals( JAR ) )
278 {
279 getLog().debug( "Adding dex input : " + artifact.getFile() );
280 inputs.add( artifact.getFile().getAbsoluteFile() );
281 }
282 }
283 }
284
285 return inputs;
286 }
287
288 private void parseConfiguration()
289 {
290
291 if ( d8 != null )
292 {
293
294
295
296 if ( d8.getJvmArguments() == null )
297 {
298 parsedJvmArguments = d8JvmArguments;
299 }
300 else
301 {
302 parsedJvmArguments = d8.getJvmArguments();
303 }
304 if ( d8.isIntermediate() == null )
305 {
306 parsedIntermediate = d8Intermediate;
307 }
308 else
309 {
310 parsedIntermediate = d8.isIntermediate();
311 }
312 if ( d8.getMainDexList() == null )
313 {
314 parsedMainDexList = d8MainDexList;
315 }
316 else
317 {
318 parsedMainDexList = d8.getMainDexList();
319 }
320 if ( d8.getArguments() == null )
321 {
322 parsedArguments = d8Arguments;
323 }
324 else
325 {
326 parsedArguments = d8.getArguments();
327 }
328 parsedDexCompiler = DexCompiler.valueOfIgnoreCase( dexCompiler );
329 if ( d8.isRelease() == null )
330 {
331 parsedRelease = release;
332 }
333 else
334 {
335 parsedRelease = d8.isRelease();
336 }
337 if ( d8.getMinApi() == null )
338 {
339 parsedMinApi = d8MinApi;
340 }
341 else
342 {
343 parsedMinApi = d8.getMinApi();
344 }
345 }
346 else
347 {
348 parsedJvmArguments = d8JvmArguments;
349 parsedIntermediate = d8Intermediate;
350 parsedMainDexList = d8MainDexList;
351 parsedArguments = d8Arguments;
352 parsedDexCompiler = DexCompiler.valueOfIgnoreCase( dexCompiler );
353 parsedRelease = d8Release;
354 parsedMinApi = d8MinApi;
355 }
356 }
357
358 private List< String > dexDefaultCommands() throws MojoExecutionException
359 {
360 List< String > commands = jarDefaultCommands();
361 commands.add( getAndroidSdk().getD8JarPath() );
362 return commands;
363 }
364
365 private List<String> jarDefaultCommands()
366 {
367 List< String > commands = javaDefaultCommands();
368 commands.add( "-jar" );
369 return commands;
370 }
371
372 private List<String> javaDefaultCommands()
373 {
374 List< String > commands = new ArrayList< String > ();
375 if ( parsedJvmArguments != null )
376 {
377 for ( String jvmArgument : parsedJvmArguments )
378 {
379
380
381
382
383 if ( !jvmArgument.startsWith( "-" ) )
384 {
385 jvmArgument = "-" + jvmArgument;
386 }
387 getLog().debug( "Adding jvm argument " + jvmArgument );
388 commands.add( jvmArgument );
389 }
390 }
391 return commands;
392 }
393
394 private void runD8( CommandExecutor executor )
395 throws MojoExecutionException
396 {
397 final List< String > commands = dexDefaultCommands();
398 final Set< File > inputFiles = getD8InputFiles();
399 if ( parsedIntermediate )
400 {
401 commands.add( "--intermediate" );
402 }
403 if ( parsedMainDexList != null )
404 {
405 commands.add( "--main-dex-list" + parsedMainDexList );
406 }
407 if ( parsedArguments != null )
408 {
409 for ( String argument : parsedArguments )
410 {
411 commands.add( argument );
412 }
413 }
414
415 if ( parsedRelease )
416 {
417 commands.add( "--release" );
418 }
419
420 if ( parsedMinApi != null )
421 {
422 commands.add( "--min-api" );
423 commands.add( parsedMinApi.toString() );
424 }
425
426 commands.add( "--output" );
427 commands.add( targetDirectory.getAbsolutePath() );
428
429 final File androidJar = getAndroidSdk().getAndroidJar();
430 commands.add( "--lib" );
431 commands.add( androidJar.getAbsolutePath() );
432
433
434 final List<File> dependencies = getDependencies();
435 for ( final File file : dependencies )
436 {
437 commands.add( "--classpath" );
438 commands.add( file.getAbsolutePath() );
439 }
440
441 for ( File inputFile : inputFiles )
442 {
443 commands.add( inputFile.getAbsolutePath() );
444 }
445
446 getLog().info( "Convert classes to Dex : " + targetDirectory );
447 executeJava( commands, executor );
448 }
449
450 private String executeJava( final List<String> commands, CommandExecutor executor ) throws MojoExecutionException
451 {
452 final String javaExecutable = getJavaExecutable().getAbsolutePath();
453 getLog().debug( javaExecutable + " " + commands.toString() );
454 try
455 {
456 executor.setCaptureStdOut( true );
457 executor.executeCommand( javaExecutable, commands, project.getBasedir(), false );
458 return executor.getStandardOut();
459 }
460 catch ( ExecutionException e )
461 {
462 throw new MojoExecutionException( "", e );
463 }
464 }
465
466
467
468
469
470
471 private static File getJavaExecutable()
472 {
473 final String javaHome = System.getProperty( "java.home" );
474 final String slash = File.separator;
475 return new File( javaHome + slash + "bin" + slash + "java" );
476 }
477
478
479
480
481
482 protected File createApkSourcesFile() throws MojoExecutionException
483 {
484 final File apksources = new File( targetDirectory, finalName
485 + ".apksources" );
486 FileUtils.deleteQuietly( apksources );
487
488 try
489 {
490 JarArchiver jarArchiver = new JarArchiver();
491 jarArchiver.setDestFile( apksources );
492
493 addDirectory( jarArchiver, assetsDirectory, "assets" );
494 addDirectory( jarArchiver, resourceDirectory, "res" );
495 addDirectory( jarArchiver, sourceDirectory, "src/main/java" );
496 addJavaResources( jarArchiver, resources );
497
498 jarArchiver.createArchive();
499 }
500 catch ( ArchiverException e )
501 {
502 throw new MojoExecutionException( "ArchiverException while creating .apksource file.", e );
503 }
504 catch ( IOException e )
505 {
506 throw new MojoExecutionException( "IOException while creating .apksource file.", e );
507 }
508
509 return apksources;
510 }
511
512
513
514
515
516
517
518
519 protected String endWithSlash( String prefix )
520 {
521 prefix = StringUtils.defaultIfEmpty( prefix, "/" );
522 if ( !prefix.endsWith( "/" ) )
523 {
524 prefix = prefix + "/";
525 }
526 return prefix;
527 }
528
529
530
531
532
533
534
535
536
537
538 protected void addDirectory( JarArchiver jarArchiver, File directory, String prefix )
539 {
540 if ( directory != null && directory.exists() )
541 {
542 final DefaultFileSet fileSet = new DefaultFileSet();
543 fileSet.setPrefix( endWithSlash( prefix ) );
544 fileSet.setDirectory( directory );
545 jarArchiver.addFileSet( fileSet );
546 }
547 }
548
549
550
551
552
553 protected void addJavaResources( JarArchiver jarArchiver, List< Resource > javaResources )
554 {
555 for ( Resource javaResource : javaResources )
556 {
557 addJavaResource( jarArchiver, javaResource );
558 }
559 }
560
561
562
563
564
565
566
567
568 protected void addJavaResource( JarArchiver jarArchiver, Resource javaResource )
569 {
570 if ( javaResource != null )
571 {
572 final File javaResourceDirectory = new File( javaResource.getDirectory() );
573 if ( javaResourceDirectory.exists() )
574 {
575 final DefaultFileSet javaResourceFileSet = new DefaultFileSet();
576 javaResourceFileSet.setDirectory( javaResourceDirectory );
577 javaResourceFileSet.setPrefix( endWithSlash( "src/main/resources" ) );
578 jarArchiver.addFileSet( javaResourceFileSet );
579 }
580 }
581 }
582 }