1 package com.simpligility.maven.plugins.android.standalonemojos;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.util.ArrayList;
6 import java.util.List;
7 import java.util.Set;
8
9 import com.android.tools.lint.LintCliClient;
10 import com.android.tools.lint.LintCliFlags;
11 import com.android.tools.lint.MultiProjectHtmlReporter;
12 import com.android.tools.lint.XmlReporter;
13 import com.android.tools.lint.checks.BuiltinIssueRegistry;
14 import com.android.tools.lint.client.api.IssueRegistry;
15
16 import org.apache.maven.artifact.Artifact;
17 import org.apache.maven.plugin.MojoExecutionException;
18 import org.apache.maven.plugin.MojoFailureException;
19
20 import com.simpligility.maven.plugins.android.AbstractAndroidMojo;
21 import com.simpligility.maven.plugins.android.CommandExecutor;
22 import com.simpligility.maven.plugins.android.ExecutionException;
23 import com.simpligility.maven.plugins.android.config.ConfigHandler;
24 import com.simpligility.maven.plugins.android.config.ConfigPojo;
25 import com.simpligility.maven.plugins.android.config.PullParameter;
26 import com.simpligility.maven.plugins.android.configuration.Lint;
27
28 import org.apache.maven.plugins.annotations.Mojo;
29 import org.apache.maven.plugins.annotations.Parameter;
30
31
32
33
34
35
36
37
38
39
40 @SuppressWarnings( "unused" )
41 @Mojo( name = "lint", requiresProject = false )
42 public class LintMojo extends AbstractAndroidMojo
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 @Parameter
91 @ConfigPojo
92 private Lint lint;
93
94
95
96
97
98
99 @Parameter( property = "android.lint.failOnError" )
100 private Boolean lintFailOnError;
101
102 @PullParameter( defaultValue = "false" )
103 private Boolean parsedFailOnError;
104
105
106
107
108
109
110 @Parameter( property = "android.lint.skip" )
111 private Boolean lintSkip;
112
113 @PullParameter( defaultValue = "true" )
114 private Boolean parsedSkip;
115
116
117
118
119
120
121
122
123
124
125 @Parameter( property = "android.lint.ignoreWarning" )
126 private Boolean lintIgnoreWarnings;
127
128 @PullParameter( defaultValue = "false" )
129 private Boolean parsedIgnoreWarnings;
130
131
132
133
134
135
136 @Parameter( property = "android.lint.warnAll" )
137 private Boolean lintWarnAll;
138
139 @PullParameter( defaultValue = "false" )
140 private Boolean parsedWarnAll;
141
142
143
144
145
146
147 @Parameter( property = "android.lint.warningsAsErrors" )
148 private Boolean lintWarningsAsErrors;
149
150 @PullParameter( defaultValue = "false" )
151 private Boolean parsedWarningsAsErrors;
152
153
154
155
156
157
158
159
160 @Parameter( property = "android.lint.config" )
161 private String lintConfig;
162
163 @PullParameter( defaultValue = "null" )
164 private String parsedConfig;
165
166
167
168
169
170
171
172
173
174
175 @Parameter( property = "android.lint.fullPath" )
176 private Boolean lintFullPath;
177
178 @PullParameter( defaultValue = "false" )
179 private Boolean parsedFullPath;
180
181
182
183
184
185
186 @Parameter( property = "android.lint.showAll" )
187 private Boolean lintShowAll;
188
189 @PullParameter( defaultValue = "true" )
190 private Boolean parsedShowAll;
191
192
193
194
195
196
197
198 @Parameter( property = "android.lint.disableSourceLines" )
199 private Boolean lintDisableSourceLines;
200
201 @PullParameter( defaultValue = "false" )
202 private Boolean parsedDisableSourceLines;
203
204
205
206
207
208
209
210
211 @Parameter( property = "android.lint.url" )
212 private String lintUrl;
213
214 @PullParameter( defaultValue = "none" )
215 private String parsedUrl;
216
217
218
219
220
221
222 @Parameter( property = "android.lint.enableHtml" )
223 private Boolean lintEnableHtml;
224
225 @PullParameter( defaultValue = "false" )
226 private Boolean parsedEnableHtml;
227
228
229
230
231
232
233
234 @Parameter( property = "android.lint.htmlOutputPath" )
235 private String lintHtmlOutputPath;
236
237 @PullParameter( defaultValueGetterMethod = "getHtmlOutputPath" )
238 private String parsedHtmlOutputPath;
239
240
241
242
243
244
245 @Parameter( property = "android.lint.enableSimpleHtml" )
246 private Boolean lintEnableSimpleHtml;
247
248 @PullParameter( defaultValue = "false" )
249 private Boolean parsedEnableSimpleHtml;
250
251
252
253
254
255
256
257 @Parameter( property = "android.lint.simpleHtmlOutputPath" )
258 private String lintSimpleHtmlOutputPath;
259
260 @PullParameter( defaultValueGetterMethod = "getSimpleHtmlOutputPath" )
261 private String parsedSimpleHtmlOutputPath;
262
263
264
265
266
267
268 @Parameter( property = "android.lint.enableXml" )
269 private Boolean lintEnableXml;
270
271 @PullParameter( defaultValue = "true" )
272 private Boolean parsedEnableXml;
273
274
275
276
277
278
279
280 @Parameter( property = "android.lint.xmlOutputPath" )
281 private String lintXmlOutputPath;
282
283 @PullParameter( defaultValueGetterMethod = "getXmlOutputPath" )
284 private String parsedXmlOutputPath;
285
286
287
288
289
290
291
292
293
294
295 @Parameter( property = "android.lint.enableSources" )
296 private Boolean lintEnableSource;
297
298 @PullParameter( defaultValue = "true" )
299 private Boolean parsedEnableSources;
300
301
302
303
304
305
306
307 @Parameter( property = "android.lint.sources" )
308 private String lintSources;
309
310 @PullParameter( defaultValueGetterMethod = "getSources" )
311 private String parsedSources;
312
313
314
315
316
317
318
319 @Parameter( property = "android.lint.enableSources" )
320 private Boolean lintEnableClasspath;
321
322 @PullParameter( defaultValue = "false" )
323 private Boolean parsedEnableClasspath;
324
325
326
327
328
329
330
331
332 @Parameter( property = "android.lint.classpath" )
333 private String lintClasspath;
334
335 @PullParameter( defaultValueGetterMethod = "getClasspath" )
336 private String parsedClasspath;
337
338
339
340
341
342
343
344 @Parameter( property = "android.lint.enableSources" )
345 private Boolean lintEnableLibraries;
346
347 @PullParameter( defaultValue = "false" )
348 private Boolean parsedEnableLibraries;
349
350
351
352
353
354
355
356
357 @Parameter( property = "android.lint.libraries" )
358 private String lintLibraries;
359
360 @PullParameter( defaultValueGetterMethod = "getLibraries" )
361 private String parsedLibraries;
362
363
364 @Parameter( property = "android.lint.legacy" )
365 private Boolean legacy;
366
367 @PullParameter( defaultValue = "true" )
368 private Boolean parsedLegacy;
369
370 @Parameter( property = "android.lint.quiet" )
371 private Boolean quiet;
372
373 @PullParameter( defaultValue = "true" )
374 private Boolean parsedQuiet;
375
376
377
378
379
380
381
382 @Override
383 public void execute() throws MojoExecutionException, MojoFailureException
384 {
385 ConfigHandler configHandler = new ConfigHandler( this, this.session, this.execution );
386 configHandler.parseConfiguration();
387 getLog().debug( "Parsed values for Android Lint invocation: " );
388 getLog().debug( "failOnError:" + parsedFailOnError );
389 getLog().debug( "skip:" + parsedSkip );
390 getLog().debug( "legacy:" + parsedLegacy );
391 getLog().debug( "quiet:" + parsedQuiet );
392 getLog().debug( "ignoreWarnings:" + parsedIgnoreWarnings );
393 getLog().debug( "warnAll:" + parsedWarnAll );
394 getLog().debug( "warningsAsErrors:" + parsedWarningsAsErrors );
395 getLog().debug( "config2:" + parsedConfig );
396
397 getLog().debug( "fullPath:" + parsedFullPath );
398 getLog().debug( "showAll:" + parsedShowAll );
399 getLog().debug( "disableSourceLines:" + parsedDisableSourceLines );
400
401 getLog().debug( "enablehtml: " + parsedEnableHtml );
402 getLog().debug( "htmlOutputPath:" + parsedHtmlOutputPath );
403
404 getLog().debug( "enableSimpleHtml: " + parsedEnableSimpleHtml );
405 getLog().debug( "simpleHtmlOutputPath:" + parsedSimpleHtmlOutputPath );
406
407 getLog().debug( "enableXml: " + parsedEnableXml );
408 getLog().debug( "xmlOutputPath:" + parsedXmlOutputPath );
409
410 getLog().debug( "sources:" + parsedSources );
411 getLog().debug( "classpath:" + parsedClasspath );
412 getLog().debug( "libraries:" + parsedLibraries );
413
414 if ( parsedSkip )
415 {
416 getLog().info( "Skipping lint analysis." );
417 }
418 else
419 {
420 getLog().info( "Performing lint analysis." );
421
422 if ( parsedLegacy )
423 {
424 getLog().info( "Using Lint from the Android SDK." );
425 executeWhenConfigured();
426 }
427 else
428 {
429 getLog().info( "Using Lint dependency library." );
430 runLint();
431 }
432 }
433 }
434
435 private void executeWhenConfigured() throws MojoExecutionException
436 {
437 CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor();
438 executor.setLogger( this.getLog() );
439
440 String command = getAndroidSdk().getLintPath();
441
442 List< String > parameters = new ArrayList< String >();
443
444 if ( isNotNullAndTrue( parsedIgnoreWarnings ) )
445 {
446 parameters.add( "-w" );
447 }
448 if ( isNotNullAndTrue( parsedWarnAll ) )
449 {
450 parameters.add( "-Wall" );
451 }
452 if ( isNotNullAndTrue( parsedWarningsAsErrors ) )
453 {
454 parameters.add( "-Werror" );
455 }
456
457 if ( isNotNullAndNotEquals( parsedConfig, "null" ) )
458 {
459 parameters.add( "--config" );
460 parameters.add( parsedConfig );
461 }
462
463 if ( isNotNullAndTrue( parsedFullPath ) )
464 {
465 parameters.add( "--fullpath" );
466 }
467 if ( isNotNullAndTrue( parsedShowAll ) )
468 {
469 parameters.add( "--showall" );
470 }
471 if ( isNotNullAndTrue( parsedDisableSourceLines ) )
472 {
473 parameters.add( "--nolines" );
474 }
475 if ( isNotNullAndTrue( parsedEnableHtml ) )
476 {
477 parameters.add( "--html" );
478 parameters.add( parsedHtmlOutputPath );
479 getLog().info( "Writing Lint HTML report in " + parsedHtmlOutputPath );
480 }
481 if ( isNotNullAndNotEquals( parsedUrl, "none" ) )
482 {
483 parameters.add( "--url" );
484 parameters.add( parsedUrl );
485 }
486 if ( isNotNullAndTrue( parsedEnableSimpleHtml ) )
487 {
488 parameters.add( "--simplehtml" );
489 parameters.add( parsedSimpleHtmlOutputPath );
490 getLog().info( "Writing Lint simple HTML report in " + parsedSimpleHtmlOutputPath );
491 }
492 if ( isNotNullAndTrue( parsedEnableXml ) )
493 {
494 parameters.add( "--xml" );
495 parameters.add( parsedXmlOutputPath );
496 getLog().info( "Writing Lint XML report in " + parsedXmlOutputPath );
497 }
498 if ( isNotNullAndTrue( parsedEnableSources ) )
499 {
500 parameters.add( "--sources" );
501 parameters.add( parsedSources );
502 }
503 if ( isNotNullAndTrue( parsedEnableClasspath ) )
504 {
505 parameters.add( "--classpath" );
506 parameters.add( parsedClasspath );
507 }
508 if ( isNotNullAndTrue( parsedEnableLibraries ) )
509 {
510 parameters.add( "--libraries" );
511 parameters.add( parsedLibraries );
512 }
513
514 parameters.add( project.getBasedir().getAbsolutePath() );
515
516
517
518
519 parameters.add( "--exitcode" );
520 try
521 {
522 getLog().info( "Running command: " + command );
523 getLog().info( "with parameters: " + parameters );
524 executor.executeCommand( command, parameters, false );
525 }
526 catch ( ExecutionException e )
527 {
528 if ( isNotNullAndTrue( parsedFailOnError ) )
529 {
530 getLog().info( "Lint analysis produced errors and project is configured to fail on error." );
531 getLog().info( "Inspect lint reports or re-run with -X to see lint errors in log" );
532 getLog().info( "Failing build as configured. Ignore following error message." );
533 throw new MojoExecutionException( "", e );
534 }
535 }
536 getLog().info( "Lint analysis completed successfully." );
537 }
538
539 private void runLint() throws MojoExecutionException
540 {
541 IssueRegistry registry = new BuiltinIssueRegistry();
542
543 LintCliFlags flags = new LintCliFlags();
544
545 LintCliClient client = new LintCliClient( flags, "AndroidMaven" );
546
547 try
548 {
549 if ( isNotNull( parsedQuiet ) )
550 {
551 flags.setQuiet( parsedQuiet );
552 }
553 if ( isNotNull( parsedIgnoreWarnings ) )
554 {
555 flags.setIgnoreWarnings( parsedIgnoreWarnings );
556 }
557 if ( isNotNull( parsedWarnAll ) )
558 {
559 flags.setCheckAllWarnings( parsedWarnAll );
560 }
561 if ( isNotNull( parsedWarningsAsErrors ) )
562 {
563 flags.setWarningsAsErrors( parsedWarningsAsErrors );
564 }
565
566 if ( isNotNullAndNotEquals( parsedConfig, "null" ) )
567 {
568 flags.setDefaultConfiguration( new File( parsedConfig ) );
569 }
570
571 if ( isNotNull( parsedFullPath ) )
572 {
573 flags.setFullPath( parsedFullPath );
574 }
575 if ( isNotNull( parsedShowAll ) )
576 {
577 flags.setShowEverything( parsedShowAll );
578 }
579 if ( isNotNull( parsedDisableSourceLines ) )
580 {
581 flags.setShowSourceLines( !parsedDisableSourceLines );
582 }
583 if ( isNotNullAndTrue( parsedEnableHtml ) )
584 {
585 File outHtml = new File( parsedHtmlOutputPath );
586 flags.getReporters().add( new MultiProjectHtmlReporter( client, outHtml, flags ) );
587
588 getLog().info( "Writing Lint HTML report in " + parsedHtmlOutputPath );
589 }
590 if ( isNotNullAndNotEquals( parsedUrl, "none" ) )
591 {
592
593
594
595 }
596 if ( isNotNullAndTrue( parsedEnableSimpleHtml ) )
597 {
598 File outSimpleHtml = new File( parsedSimpleHtmlOutputPath );
599 flags.getReporters().add( new MultiProjectHtmlReporter( client, outSimpleHtml, flags ) );
600
601 getLog().info( "Writing Lint simple HTML report in " + parsedSimpleHtmlOutputPath );
602 }
603 if ( isNotNullAndTrue( parsedEnableXml ) )
604 {
605 flags.getReporters().add( new XmlReporter( client, new File( parsedXmlOutputPath ) ) );
606
607 getLog().info( "Writing Lint XML report in " + parsedXmlOutputPath );
608 }
609 if ( isNotNullAndTrue( parsedEnableSources ) )
610 {
611
612
613
614 }
615 if ( isNotNullAndTrue( parsedEnableClasspath ) )
616 {
617
618
619
620 }
621 if ( isNotNullAndTrue( parsedEnableLibraries ) )
622 {
623
624
625
626 }
627
628 List< File > files = new ArrayList< File >();
629 files.add( resourceDirectory );
630 files.add( destinationManifestFile );
631 files.add( sourceDirectory );
632 files.add( assetsDirectory );
633
634 client.run( registry, files );
635 }
636 catch ( IOException ex )
637 {
638 throw new MojoExecutionException( ex.getMessage(), ex );
639 }
640 }
641
642 private boolean isNotNull( Boolean b )
643 {
644 return b != null;
645 }
646
647 private boolean isNotNullAndTrue( Boolean b )
648 {
649 return b != null && b;
650 }
651
652 private boolean isNotNullAndNotEquals( String underTest, String compared )
653 {
654 return underTest != null && !underTest.equals( compared );
655 }
656
657
658 private String getHtmlOutputPath()
659 {
660 if ( parsedHtmlOutputPath == null )
661 {
662 File reportPath = new File( targetDirectory, "lint-results/lint-results-html" );
663 createReportDirIfNeeded( reportPath );
664 return reportPath.getAbsolutePath();
665 }
666 return parsedHtmlOutputPath;
667 }
668
669
670 private String getSimpleHtmlOutputPath()
671 {
672 if ( parsedSimpleHtmlOutputPath == null )
673 {
674 File reportPath = new File( targetDirectory, "lint-results/lint-results-simple-html" );
675 createReportDirIfNeeded( reportPath );
676 return reportPath.getAbsolutePath();
677 }
678 return parsedSimpleHtmlOutputPath;
679 }
680
681
682 private String getXmlOutputPath()
683 {
684 getLog().debug( "get parsed xml output path:" + parsedXmlOutputPath );
685
686 if ( parsedXmlOutputPath == null )
687 {
688 File reportPath = new File( targetDirectory, "lint-results/lint-results.xml" );
689 createReportDirIfNeeded( reportPath );
690 return reportPath.getAbsolutePath();
691 }
692 return parsedXmlOutputPath;
693 }
694
695 private void createReportDirIfNeeded( File reportPath )
696 {
697 if ( !reportPath.getParentFile().exists() )
698 {
699 reportPath.getParentFile().mkdirs();
700 }
701 }
702
703
704 private String getSources()
705 {
706 if ( parsedSources == null )
707 {
708 parsedSources = sourceDirectory.getAbsolutePath();
709 }
710 return parsedSources;
711 }
712
713
714 private String getClasspath()
715 {
716 if ( parsedClasspath == null )
717 {
718 parsedClasspath = projectOutputDirectory.getAbsolutePath();
719 }
720 return parsedClasspath;
721 }
722
723 private String getLibraries()
724 {
725 if ( parsedLibraries == null )
726 {
727 StringBuilder defaultClasspathBuilder = new StringBuilder();
728 Set< Artifact > artifacts = project.getDependencyArtifacts();
729 if ( artifacts != null )
730 {
731 for ( Artifact artifact : artifacts )
732 {
733 if ( !Artifact.SCOPE_PROVIDED.equals( artifact.getScope() ) && artifact.isResolved() )
734 {
735 defaultClasspathBuilder.append( artifact.getFile().getPath() );
736 defaultClasspathBuilder.append( File.pathSeparator );
737 }
738 }
739 if ( defaultClasspathBuilder.length() > 0 )
740 {
741 defaultClasspathBuilder.deleteCharAt( defaultClasspathBuilder.length() - 1 );
742 parsedLibraries = defaultClasspathBuilder.toString();
743 }
744 }
745 }
746 return parsedLibraries;
747 }
748 }