1
2
3
4
5
6
7
8
9
10
11
12
13
14 package com.simpligility.maven.plugins.androidndk.phase05compile;
15
16 import com.simpligility.maven.plugins.androidndk.AndroidNdk;
17 import com.simpligility.maven.plugins.androidndk.CommandExecutor;
18 import com.simpligility.maven.plugins.androidndk.common.ArtifactResolverHelper;
19 import com.simpligility.maven.plugins.androidndk.common.Const;
20 import com.simpligility.maven.plugins.androidndk.common.MavenToPlexusLogAdapter;
21 import com.simpligility.maven.plugins.androidndk.common.NativeHelper;
22 import com.simpligility.maven.plugins.androidndk.configuration.AdditionallyBuiltModule;
23 import com.simpligility.maven.plugins.androidndk.configuration.HeaderFilesDirective;
24 import com.simpligility.maven.plugins.androidndk.configuration.ArchitectureToolchainMappings;
25 import com.simpligility.maven.plugins.androidndk.configuration.IgnoreHeaderFilesArchive;
26 import org.apache.commons.lang3.StringUtils;
27 import org.apache.maven.archiver.MavenArchiveConfiguration;
28 import org.apache.maven.archiver.MavenArchiver;
29 import org.apache.maven.artifact.Artifact;
30 import org.apache.maven.artifact.handler.ArtifactHandler;
31 import org.apache.maven.artifact.resolver.ArtifactResolver;
32 import org.apache.maven.plugin.AbstractMojo;
33 import org.apache.maven.plugin.MojoExecutionException;
34 import org.apache.maven.plugin.MojoFailureException;
35 import org.apache.maven.plugins.annotations.Component;
36 import org.apache.maven.plugins.annotations.LifecyclePhase;
37 import org.apache.maven.plugins.annotations.Mojo;
38 import org.apache.maven.plugins.annotations.Parameter;
39 import org.apache.maven.project.MavenProject;
40 import org.apache.maven.project.MavenProjectHelper;
41 import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
42 import org.codehaus.plexus.archiver.jar.JarArchiver;
43 import org.codehaus.plexus.util.IOUtil;
44
45 import java.io.File;
46 import java.io.FileInputStream;
47 import java.io.FileOutputStream;
48 import java.io.FilenameFilter;
49 import java.io.IOException;
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.LinkedHashSet;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.Properties;
56 import java.util.Set;
57 import java.util.regex.Matcher;
58 import java.util.regex.Pattern;
59
60
61
62
63 @Mojo( name = "ndk-build", defaultPhase = LifecyclePhase.COMPILE )
64 public class NdkBuildMojo extends AbstractMojo
65 {
66
67
68
69 public static final String ENV_ANDROID_NDK_HOME = "ANDROID_NDK_HOME";
70
71
72
73
74
75 @Parameter( property = "android.ndk.ndkPath", readonly = true )
76 private File ndkPath;
77
78
79
80
81 @Parameter( property = "android.ndk.ndkBuildExecutable" )
82 private String ndkBuildExecutable;
83
84
85
86
87 @Parameter( property = "android.ndk.buildDirectory", defaultValue = "${project.build.directory}/android-ndk-maven-plugin", readonly = true )
88 private File buildDirectory;
89
90
91
92 @Parameter( property = "android.ndk.workingDirectory", defaultValue = "${project.basedir}", readonly = true )
93 private File workingDirectory;
94
95
96
97 @Parameter( property = "android.ndk.classifier" )
98 private String classifier;
99
100
101
102 @Parameter( property = "android.ndk.additionalCommandline" )
103 protected String additionalCommandline;
104
105
106
107
108
109 @Parameter( property = "android.ndk.objectsOutputDirectory", defaultValue = "${project.build.directory}/obj" )
110 private File objectsOutputDirectory;
111
112
113
114
115
116 @Parameter( property = "android.ndk.librariesOutputDirectory", defaultValue = "${project.build.directory}/ndk-libs" )
117 private File librariesOutputDirectory;
118
119
120
121
122 @Parameter( property = "unpackedLibsFolder", defaultValue = "${project.build.directory}/unpacked-libs" )
123 private File unpackedLibsFolder;
124
125
126
127
128 @Parameter( property = "android.ndk.target" )
129 private String target;
130
131
132
133
134 @Parameter( property = "android.ndk.architectures" )
135 private String architectures;
136
137
138
139
140
141
142
143
144 @Parameter
145 private ArchitectureToolchainMappings architectureToolchainMappings;
146
147
148
149
150
151 @Parameter( property = "android.ndk.attachHeaderFiles", defaultValue = "true" )
152 private Boolean attachHeaderFiles;
153
154
155
156
157 @Parameter( property = "android.ndk.attachLibrariesArtifacts", defaultValue = "true" )
158 private Boolean attachLibrariesArtifacts;
159
160
161
162
163 @Parameter( property = "android.ndk.leaveTemporaryBuildArtifacts", defaultValue = "false" )
164 private Boolean leaveTemporaryBuildArtifacts;
165
166
167
168
169
170
171
172
173 @Parameter( property = "android.ndk.useLocalSrcIncludePaths", defaultValue = "false" )
174 private Boolean useLocalSrcIncludePaths;
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208 @Parameter
209 private List<HeaderFilesDirective> headerFilesDirectives;
210
211
212
213
214
215 @Parameter( property = "android.ndk.build.use-header-archive", defaultValue = "true" )
216 private Boolean useHeaderArchives;
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236 @Parameter
237 private List<IgnoreHeaderFilesArchive> ignoreHeaderFilesArchives;
238
239
240
241
242
243
244
245
246
247
248
249
250 @Parameter
251 private Map<String, String> systemProperties;
252
253
254
255
256
257 @Parameter( property = "android.ndk.ignoreBuildWarnings", defaultValue = "true" )
258 private Boolean ignoreBuildWarnings;
259
260
261
262
263
264
265
266
267 @Parameter( property = "android.ndk.buildWarningsRegularExpression", defaultValue = ".*[warning|note]: .*" )
268 private String buildWarningsRegularExpression;
269
270
271
272 @Parameter( property = "android.ndk.build.ndk-toolchain" )
273 private String ndkToolchain;
274
275
276
277
278
279 @Parameter( property = "android.ndk.finalLibraryName" )
280 private String finalLibraryName;
281
282
283
284
285 @Parameter( property = "android.ndk.makefile" )
286 private String makefile;
287
288
289
290
291 @Parameter( property = "android.ndk.applicationMakefile" )
292 private String applicationMakefile;
293
294
295
296
297 @Parameter( property = "android.ndk.maxJobs", defaultValue = "false" )
298 private Boolean maxJobs;
299
300
301
302
303 @Parameter()
304 private List<AdditionallyBuiltModule> additionallyBuiltModules;
305
306
307
308
309 @Parameter( defaultValue = "false" )
310 private boolean skip;
311
312
313
314
315 @Parameter( defaultValue = "false" )
316 private boolean allowMultiArtifacts;
317
318
319
320
321 @Component( role = org.codehaus.plexus.archiver.Archiver.class, hint = "jar" )
322 private JarArchiver jarArchiver;
323
324 @Component( role = org.apache.maven.artifact.handler.ArtifactHandler.class, hint = "har" )
325 private ArtifactHandler harArtifactHandler;
326
327
328
329
330 @Parameter( defaultValue = "${project}", readonly = true, required = true )
331 protected MavenProject project;
332
333
334
335
336 @Component
337 protected MavenProjectHelper projectHelper;
338
339 @Component
340 private ArtifactResolver artifactResolver;
341
342
343
344
345 @Component( hint = "default" )
346 protected DependencyGraphBuilder dependencyGraphBuilder;
347
348 private ArtifactResolverHelper artifactResolverHelper;
349 private NativeHelper nativeHelper;
350
351
352
353
354 @Component
355 private org.apache.maven.plugin.MojoExecution execution;
356
357
358
359
360
361 public void execute() throws MojoExecutionException, MojoFailureException
362 {
363 if ( skip )
364 {
365 getLog().info( "Skipping execution as per configuration" );
366 return;
367 }
368
369 if ( !attachLibrariesArtifacts && NativeHelper.isNativeArtifactProject( project ) )
370 {
371 getLog().warn( "Configured to not attach artifacts, this may cause an error at install/deploy time" );
372 }
373
374
375 final File ndkBuildFile = new File( getAndroidNdk().getNdkBuildPath() );
376 NativeHelper.validateNDKVersion( ndkBuildFile.getParentFile() );
377
378 validateMakefile( project, makefile );
379
380 final String[] resolvedNDKArchitectures = NativeHelper.getNdkArchitectures( architectures, applicationMakefile, project.getBasedir() );
381
382
383
384 final Set<Artifact> nativeLibraryArtifacts = findNativeLibraryDependencies();
385
386
387 final Set<Artifact> resolvedNativeLibraryArtifacts = getArtifactResolverHelper().resolveArtifacts( nativeLibraryArtifacts );
388
389 getLog().debug( "resolveArtifacts found " + resolvedNativeLibraryArtifacts.size() + ": " + resolvedNativeLibraryArtifacts.toString() );
390
391 CompileCommand compileCommand = new CompileCommand ();
392 compileCommand.nativeLibraryDepedencies = resolvedNativeLibraryArtifacts;
393 compileCommand.resolvedArchitectures = resolvedNDKArchitectures;
394
395 setupOutputDirectories( compileCommand );
396
397 compile ( compileCommand );
398
399 }
400
401 private void setupOutputDirectories ( final CompileCommand compileCommand )
402 {
403 compileCommand.librariesOutputDirectory = librariesOutputDirectory;
404 compileCommand.objectsOutputDirectory = objectsOutputDirectory;
405
406
407
408
409
410
411
412
413
414 if ( ! "default-ndk-build".equals( execution.getExecutionId () ) && ! "default".equals ( execution.getExecutionId () ) )
415 {
416 String libsOut = librariesOutputDirectory.getAbsolutePath();
417 String out = objectsOutputDirectory.getAbsolutePath();
418
419 libsOut = libsOut + "/" + execution.getExecutionId ();
420 out = out + "/" + execution.getExecutionId ();
421
422
423
424
425
426 }
427
428 getLog ().debug ( "Setting library out to " + compileCommand.librariesOutputDirectory.getAbsolutePath () );
429 getLog ().debug ( "Setting out to " + compileCommand.objectsOutputDirectory.getAbsolutePath () );
430
431 }
432
433 private class CompileCommand
434 {
435 private File objectsOutputDirectory;
436 private File librariesOutputDirectory;
437
438 private Set<Artifact> nativeLibraryDepedencies;
439 private String[] resolvedArchitectures;
440
441 public Set<Artifact> getNativeLibraryDepedencies ()
442 {
443 return nativeLibraryDepedencies;
444 }
445
446 public String[] getResolvedArchitectures ()
447 {
448 return resolvedArchitectures;
449 }
450 }
451
452 private void compile ( CompileCommand compileCommand ) throws MojoExecutionException
453 {
454 MakefileHelper.MakefileResponse makefileResponse = null;
455 try
456 {
457
458 final CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor ();
459
460
461
462
463 executor.setErrorListener ( getNdkErrorListener () );
464
465 final Set<Artifact> nativeLibraryArtifacts = compileCommand.getNativeLibraryDepedencies ();
466
467 final Set<Artifact> resolvedNativeLibraryArtifacts = getArtifactResolverHelper ().resolveArtifacts ( nativeLibraryArtifacts );
468
469 getLog ().debug ( "resolveArtifacts found " + resolvedNativeLibraryArtifacts.size () + ": " + resolvedNativeLibraryArtifacts.toString () );
470
471 final File buildFolder = new File ( buildDirectory, "makefile" );
472 buildFolder.mkdirs ();
473
474 final File androidMavenMakefile = new File ( buildFolder, "android_maven_plugin_makefile.mk" );
475 final MakefileHelper makefileHelper = new MakefileHelper ( project, getLog (), getArtifactResolverHelper (), harArtifactHandler, unpackedLibsFolder,
476 buildDirectory );
477
478 MakefileHelper.MakefileRequest makefileRequest = new MakefileHelper.MakefileRequest ();
479 makefileRequest.artifacts = resolvedNativeLibraryArtifacts;
480 makefileRequest.defaultNDKArchitecture = "armeabi";
481 makefileRequest.useHeaderArchives = useHeaderArchives;
482 makefileRequest.ignoreHeaderFilesArchives = ignoreHeaderFilesArchives;
483 makefileRequest.leaveTemporaryBuildArtifacts = leaveTemporaryBuildArtifacts;
484 makefileRequest.architectures = compileCommand.getResolvedArchitectures ();
485
486 makefileResponse = makefileHelper.createMakefileFromArtifacts ( makefileRequest );
487
488 final FileOutputStream output = new FileOutputStream ( androidMavenMakefile );
489 try
490 {
491 IOUtil.copy ( makefileResponse.getMakeFile (), output );
492 }
493 finally
494 {
495 output.close ();
496 }
497
498
499 executor.addEnvironment ( "ANDROID_MAVEN_PLUGIN_MAKEFILE", androidMavenMakefile.getAbsolutePath () );
500
501 setupNativeLibraryEnvironment ( executor, makefileResponse );
502
503
504
505
506 final File makefileCaptureFile = File.createTempFile ( "android_maven_plugin_makefile_captures", ".tmp", buildDirectory );
507
508 if ( !leaveTemporaryBuildArtifacts )
509 {
510 makefileCaptureFile.deleteOnExit ();
511 }
512
513 executor.addEnvironment ( MakefileHelper.MAKEFILE_CAPTURE_FILE, makefileCaptureFile.getAbsolutePath () );
514
515
516 if ( systemProperties != null && !systemProperties.isEmpty() )
517 {
518 for ( Map.Entry<String, String> entry : systemProperties.entrySet() )
519 {
520 executor.addEnvironment( entry.getKey(), entry.getValue() );
521 }
522 }
523
524 executor.setLogger( this.getLog() );
525
526
527 final List<String> commands = new ArrayList<String>();
528
529 configureArchitectures( commands, compileCommand.getResolvedArchitectures () );
530
531 configureBuildDirectory( compileCommand, commands );
532
533 configureMakefile( commands );
534
535 configureApplicationMakefile( commands );
536
537 configureMaxJobs( commands );
538
539
540 if ( compileCommand.getResolvedArchitectures ().length == 1 )
541 {
542 configureNdkToolchain ( compileCommand.getResolvedArchitectures ()[0], commands );
543 }
544
545 configureAdditionalCommands( commands );
546
547
548 commands.add ( target != null ? target : "all" );
549
550 final String ndkBuildPath = resolveNdkBuildExecutable ();
551 getLog ().debug ( ndkBuildPath + " " + commands.toString () );
552 getLog ().info ( "Executing NDK make at : " + buildDirectory );
553
554 executor.setCaptureStdOut ( true );
555 executor.executeCommand ( ndkBuildPath, commands, buildDirectory, true );
556 getLog ().debug ( "Executed NDK make at : " + buildDirectory );
557
558 if ( attachLibrariesArtifacts )
559 {
560
561 for ( int i = 0; i < compileCommand.getResolvedArchitectures ().length; i++ )
562 {
563 String architecture = compileCommand.getResolvedArchitectures ()[ i ];
564 processCompiledArtifacts ( compileCommand, architecture, makefileCaptureFile );
565 }
566 }
567 else
568 {
569 getLog ().info ( "Will skip attaching compiled libraries as per configuration" );
570 }
571
572 }
573 catch ( Exception e )
574 {
575 throw new MojoExecutionException ( "Failure during build: " + e.getMessage (), e );
576 }
577 finally
578 {
579 cleanupAfterBuild( makefileResponse );
580 }
581
582 }
583
584 private void configureArchitectures ( final List<String> commands, final String[] resolvedArchitectures )
585 {
586 StringBuilder sb = new StringBuilder ( );
587
588 for ( int i = 0; i < resolvedArchitectures.length; i++ )
589 {
590 sb.append ( resolvedArchitectures[i] );
591 if ( ( i + 1 ) < resolvedArchitectures.length )
592 {
593 sb.append ( " " );
594 }
595 }
596
597
598 commands.add ( "APP_ABI=" + sb.toString () );
599
600 }
601
602
603 private void configureBuildDirectory( final CompileCommand compileCommand, final List<String> commands )
604 {
605
606
607 commands.add( "-C" );
608 commands.add( workingDirectory.getAbsolutePath() );
609
610
611 commands.add( "NDK_LIBS_OUT=" + compileCommand.librariesOutputDirectory.getAbsolutePath () );
612 commands.add( "NDK_OUT=" + compileCommand.objectsOutputDirectory.getAbsolutePath () );
613
614 }
615
616 private void configureMakefile( final List<String> commands ) throws MojoExecutionException
617 {
618
619
620 if ( makefile != null )
621 {
622 File makeFile = new File( project.getBasedir(), makefile );
623 if ( !makeFile.exists() )
624 {
625 getLog().error( "Specified makefile " + makeFile + " does not exist" );
626 throw new MojoExecutionException( "Specified makefile " + makeFile + " does not exist" );
627 }
628 commands.add( "APP_BUILD_SCRIPT=" + makefile );
629 }
630 }
631
632 private void cleanupAfterBuild ( final MakefileHelper.MakefileResponse makefileResponse )
633 {
634
635 if ( makefileResponse != null )
636 {
637 getLog().info( "Cleaning up extracted include directories used for build" );
638 MakefileHelper.cleanupAfterBuild( makefileResponse );
639 }
640 }
641
642 private void configureAdditionalCommands( final List<String> commands )
643 {
644
645
646 if ( additionalCommandline != null )
647 {
648 final String[] additionalCommands = additionalCommandline.split( " " );
649 commands.addAll( Arrays.asList( additionalCommands ) );
650 }
651 }
652
653 private void configureApplicationMakefile( List<String> commands )
654 throws MojoExecutionException
655 {
656 if ( applicationMakefile != null )
657 {
658 File appMK = new File( project.getBasedir(), applicationMakefile );
659 if ( !appMK.exists() )
660 {
661 getLog().error( "Specified application makefile " + appMK + " does not exist" );
662 throw new MojoExecutionException( "Specified application makefile " + appMK + " does not exist" );
663 }
664 commands.add( "NDK_APPLICATION_MK=" + applicationMakefile );
665 }
666 }
667
668 private void configureMaxJobs( List<String> commands )
669 {
670 if ( maxJobs )
671 {
672 String jobs = String.valueOf( Runtime.getRuntime().availableProcessors() );
673 getLog().info( "executing " + jobs + " parallel jobs" );
674 commands.add( "-j" );
675 commands.add( jobs );
676 }
677 }
678
679 private void configureNdkToolchain( String architecture, List<String> commands )
680 throws MojoExecutionException
681 {
682 if ( ndkToolchain != null )
683 {
684
685
686
687 commands.add( "NDK_TOOLCHAIN=" + ndkToolchain );
688 commands.add( "APP_ABI=" + architecture );
689 }
690 else
691 {
692
693
694
695
696
697 final String toolchainFromArchitecture = getAndroidNdk().getToolchainFromArchitecture( architecture, architectureToolchainMappings );
698 getLog().debug( "Resolved toolchain for " + architecture + " to " + toolchainFromArchitecture );
699 commands.add( "NDK_TOOLCHAIN=" + toolchainFromArchitecture );
700 commands.add( "APP_ABI=" + architecture );
701 }
702 }
703
704
705
706
707 private void processCompiledArtifacts ( final CompileCommand compileCommand, String architecture, final File makefileCaptureFile ) throws IOException, MojoExecutionException
708 {
709
710 final File nativeLibraryDirectory = new File( compileCommand.librariesOutputDirectory, architecture );
711
712
713 final File nativeObjDirectory = new File( new File( compileCommand.objectsOutputDirectory, "local" ), architecture );
714
715 final List<String> classifiers = new ArrayList<String>();
716 if ( allowMultiArtifacts )
717 {
718 attachManyArtifacts( nativeLibraryDirectory, architecture, nativeObjDirectory, classifiers );
719 }
720 else
721 {
722 attachOneArtifact( nativeLibraryDirectory, architecture, nativeObjDirectory, classifiers );
723 }
724
725 if ( additionallyBuiltModules != null && !additionallyBuiltModules.isEmpty() )
726 {
727 for ( AdditionallyBuiltModule additionallyBuiltModule : additionallyBuiltModules )
728 {
729 File additionalBuiltModuleFile = nativeLibraryFromName( true, nativeLibraryDirectory, additionallyBuiltModule.getName() );
730
731
732 if ( !additionalBuiltModuleFile.exists() )
733 {
734 additionalBuiltModuleFile = nativeLibraryFromName( true, nativeObjDirectory, additionallyBuiltModule.getName() );
735 }
736
737
738 final String additionallyBuiltArtifactType = resolveArtifactType( additionalBuiltModuleFile );
739
740 String additionallyBuiltClassifier = architecture + "-" + additionallyBuiltModule.getClassifier();
741 projectHelper.attachArtifact( this.project, additionallyBuiltArtifactType, additionallyBuiltClassifier, additionalBuiltModuleFile );
742 classifiers.add( additionallyBuiltClassifier );
743 }
744 }
745
746
747 if ( attachHeaderFiles )
748 {
749 attachHeaderFiles( compileCommand, makefileCaptureFile, classifiers );
750 }
751
752
753 }
754
755 private void attachManyArtifacts( File nativeLibraryDirectory, String architecture, File nativeObjDirectory, List<String> classifiers ) throws MojoExecutionException
756 {
757 List<File> artifacts = Arrays.asList( findNativeLibrary( nativeLibraryDirectory, nativeObjDirectory ) );
758 for ( File file : artifacts )
759 {
760 attachArtifactFile( architecture, classifiers, file );
761 }
762 }
763
764 private void attachOneArtifact( File nativeLibraryDirectory, String architecture, File nativeObjDirectory, List<String> classifiers ) throws MojoExecutionException
765 {
766 final File nativeArtifactFile;
767 if ( finalLibraryName == null )
768 {
769 nativeArtifactFile = findNativeLibrary( nativeLibraryDirectory, nativeObjDirectory )[0];
770 }
771 else
772 {
773 nativeArtifactFile = nativeLibraryFromName( nativeLibraryDirectory, nativeObjDirectory, finalLibraryName );
774 }
775
776 attachArtifactFile( architecture, classifiers, nativeArtifactFile );
777 }
778
779 private void attachArtifactFile( String architecture, List<String> classifiers, File nativeArtifactFile )
780 {
781 final String artifactType = resolveArtifactType( nativeArtifactFile );
782 getLog().debug( "Adding native compiled artifact: " + nativeArtifactFile );
783
784 final String actualClassifier = ( classifier == null ) ? architecture : architecture + "-" + classifier;
785 projectHelper.attachArtifact( this.project, artifactType, actualClassifier, nativeArtifactFile );
786 classifiers.add( actualClassifier );
787 }
788
789
790
791
792 private File[] findNativeLibrary( File nativeLibDirectory, final File nativeObjDirectory ) throws MojoExecutionException
793 {
794 getLog().info( "Searching " + nativeLibDirectory + " for built shared library" );
795
796 File[] files = nativeLibDirectory.listFiles( new FilenameFilter()
797 {
798 public boolean accept( final File dir, final String name )
799 {
800 String libraryName = finalLibraryName;
801
802 if ( libraryName == null || libraryName.isEmpty() )
803 {
804 libraryName = project.getArtifactId();
805 }
806
807
808 final String extension = Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE.equals( project.getPackaging() ) ? ".a" : ".so";
809 boolean found = name.startsWith( "lib" + libraryName ) && name.endsWith( extension );
810 if ( !found )
811 {
812
813 if ( libraryName.startsWith( "lib" ) )
814 {
815 found = name.startsWith( libraryName ) && name.endsWith( extension );
816 }
817 }
818 return found;
819 }
820 } );
821
822
823
824
825 if ( files == null || files.length == 0 )
826 {
827 getLog().info( "Searching " + nativeObjDirectory + " for built static library" );
828 files = nativeObjDirectory.listFiles( new FilenameFilter()
829 {
830 public boolean accept( final File dir, final String name )
831 {
832 String libraryName = finalLibraryName;
833
834 if ( libraryName == null || libraryName.isEmpty() )
835 {
836 libraryName = project.getArtifactId();
837 }
838
839
840 if ( Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE.equals( project.getPackaging() ) )
841 {
842 return name.startsWith( "lib" + libraryName ) && name.endsWith( ".a" );
843 }
844 else
845 {
846 return name.startsWith( "lib" + libraryName ) && name.endsWith( ".so" );
847 }
848 }
849 } );
850
851 }
852
853
854 if ( ( files == null || files.length != 1 ) && !allowMultiArtifacts )
855 {
856 getLog().warn( "Error while detecting native compile artifacts: " + ( files == null || files.length == 0 ? "None found" : "Found more than 1 artifact" ) );
857 if ( target != null )
858 {
859 getLog().warn( "Using the 'target' configuration option to specify the output file name is no longer supported, use 'finalLibraryName' instead." );
860 }
861
862 if ( files != null && files.length > 1 )
863 {
864 getLog().debug( "List of files found: " + Arrays.asList( files ) );
865 getLog().error( "Currently, only a single, final native library is supported by the build" );
866 throw new MojoExecutionException( "Currently, only a single, final native library is supported by the build" );
867 }
868 else
869 {
870 getLog().error( "No native compiled library found, did the native compile complete successfully?" );
871 throw new MojoExecutionException( "No native compiled library found, did the native compile complete successfully?" );
872 }
873 }
874 return files;
875 }
876
877 private File nativeLibraryFromName( File nativeLibDirectory, final File nativeObjDirectory, final String libraryName ) throws MojoExecutionException
878 {
879 try
880 {
881 return nativeLibraryFromName( false, nativeLibDirectory, libraryName );
882 }
883 catch ( MojoExecutionException e )
884 {
885
886 return nativeLibraryFromName( true, nativeObjDirectory, libraryName );
887 }
888 }
889
890 private File nativeLibraryFromName( boolean logErrors, File directory, final String libraryName ) throws MojoExecutionException
891 {
892 final File libraryFile;
893
894 if ( Const.ArtifactType.NATIVE_SYMBOL_OBJECT.equals( project.getPackaging() ) || Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE.equals( project.getPackaging() ) )
895 {
896 libraryFile = new File( directory, "lib" + libraryName + "." + project.getPackaging() );
897 }
898 else
899 {
900 final File staticLib = new File( directory, "lib" + libraryName + ".a" );
901 if ( staticLib.exists() )
902 {
903 libraryFile = staticLib;
904 }
905 else
906 {
907 libraryFile = new File( directory, "lib" + libraryName + ".so" );
908 }
909 }
910 if ( !libraryFile.exists() )
911 {
912 if ( logErrors )
913 {
914 getLog().error( "Could not locate final native library using the provided finalLibraryName " + libraryName + " (tried " + libraryFile.getAbsolutePath() + ")" );
915 }
916 throw new MojoExecutionException( "Could not locate final native library using the provided finalLibraryName " + libraryName + " (tried " + libraryFile.getAbsolutePath() + ")" );
917 }
918
919 return libraryFile;
920 }
921
922
923 private CommandExecutor.ErrorListener getNdkErrorListener()
924 {
925 return new CommandExecutor.ErrorListener()
926 {
927 @Override
928 public boolean isError( String error )
929 {
930
931
932 if ( ignoreBuildWarnings )
933 {
934 return false;
935 }
936
937 final Pattern pattern = Pattern.compile( buildWarningsRegularExpression );
938 final Matcher matcher = pattern.matcher( error );
939
940
941
942 if ( matcher.matches() )
943 {
944 return false;
945 }
946
947
948 return true;
949 }
950 };
951 }
952
953
954
955
956
957
958 private void validateMakefile( MavenProject project, String file )
959 {
960
961 }
962
963 private String resolveNdkBuildExecutable() throws MojoExecutionException
964 {
965 if ( ndkBuildExecutable != null )
966 {
967 getLog().debug( "ndk-build overriden, using " + ndkBuildExecutable );
968 return ndkBuildExecutable;
969 }
970 return getAndroidNdk().getNdkBuildPath();
971 }
972
973 private void attachHeaderFiles ( final CompileCommand compileCommand, final File localCIncludesFile, final List<String> classifiers ) throws MojoExecutionException, IOException
974 {
975
976 final List<HeaderFilesDirective> finalHeaderFilesDirectives = new ArrayList<HeaderFilesDirective>();
977
978 if ( useLocalSrcIncludePaths )
979 {
980 Properties props = new Properties();
981 props.load( new FileInputStream( localCIncludesFile ) );
982 String localCIncludes = props.getProperty( "LOCAL_C_INCLUDES" );
983 if ( localCIncludes != null && !localCIncludes.trim().isEmpty() )
984 {
985 String[] includes = localCIncludes.split( " " );
986 for ( String include : includes )
987 {
988 final HeaderFilesDirective headerFilesDirective = new HeaderFilesDirective();
989 File includeDir = new File( project.getBasedir(), include );
990 headerFilesDirective.setDirectory( includeDir.getAbsolutePath() );
991 headerFilesDirective.setIncludes( new String[]{ "**/*.h", "**/*.hpp" } );
992 finalHeaderFilesDirectives.add( headerFilesDirective );
993 }
994 }
995 }
996 else
997 {
998 if ( headerFilesDirectives != null )
999 {
1000 finalHeaderFilesDirectives.addAll( headerFilesDirectives );
1001 }
1002 }
1003 if ( finalHeaderFilesDirectives.isEmpty() )
1004 {
1005 getLog().debug( "No header files included, will add default set" );
1006 final HeaderFilesDirective e = new HeaderFilesDirective();
1007 final File folder = new File( project.getBasedir() + "/jni" );
1008 if ( folder.exists() )
1009 {
1010 e.setDirectory( folder.getAbsolutePath() );
1011 e.setIncludes( new String[] { "**/*.h", "**/*.hpp" } );
1012 finalHeaderFilesDirectives.add( e );
1013 }
1014 }
1015 createHeaderArchive( compileCommand, finalHeaderFilesDirectives, classifiers );
1016 }
1017
1018 private void createHeaderArchive ( final CompileCommand compileCommand, final List<HeaderFilesDirective> finalHeaderFilesDirectives, final List<String> classifiers ) throws MojoExecutionException
1019 {
1020 try
1021 {
1022 MavenArchiver mavenArchiver = new MavenArchiver();
1023 mavenArchiver.setArchiver( jarArchiver );
1024
1025 final File jarFile = File.createTempFile( "tmp", ".har", buildDirectory );
1026
1027 mavenArchiver.setOutputFile( jarFile );
1028
1029 for ( HeaderFilesDirective headerFilesDirective : finalHeaderFilesDirectives )
1030 {
1031 mavenArchiver.getArchiver().addDirectory( new File( headerFilesDirective.getDirectory() ), headerFilesDirective.getIncludes(), headerFilesDirective.getExcludes() );
1032 }
1033
1034 final MavenArchiveConfiguration mavenArchiveConfiguration = new MavenArchiveConfiguration();
1035 mavenArchiveConfiguration.setAddMavenDescriptor( false );
1036
1037 mavenArchiver.createArchive( project, mavenArchiveConfiguration );
1038
1039 for ( String classifier : classifiers )
1040 {
1041 getLog().debug( "Attaching 'har' classifier=" + classifier + " file=" + jarFile );
1042 projectHelper.attachArtifact( project, Const.ArtifactType.NATIVE_HEADER_ARCHIVE, classifier, jarFile );
1043 }
1044
1045 }
1046 catch ( Exception e )
1047 {
1048 throw new MojoExecutionException( e.getMessage() );
1049 }
1050 }
1051
1052 private void setupNativeLibraryEnvironment ( final CommandExecutor executor,
1053 final MakefileHelper.MakefileResponse makefileResponse )
1054 {
1055 if ( makefileResponse.hasStaticLibraryDepdendencies() )
1056 {
1057 String staticlibs = makefileResponse.getStaticLibraryList();
1058 executor.addEnvironment( "ANDROID_MAVEN_PLUGIN_LOCAL_STATIC_LIBRARIES", staticlibs );
1059 getLog().debug( "Set ANDROID_MAVEN_PLUGIN_LOCAL_STATIC_LIBRARIES = " + staticlibs );
1060 }
1061
1062 if ( makefileResponse.hasSharedLibraryDepdendencies() )
1063 {
1064 String staticlibs = makefileResponse.getSharedLibraryList();
1065 executor.addEnvironment( "ANDROID_MAVEN_PLUGIN_LOCAL_SHARED_LIBRARIES", staticlibs );
1066 getLog().debug( "Set ANDROID_MAVEN_PLUGIN_LOCAL_SHARED_LIBRARIES = " + staticlibs );
1067 }
1068 }
1069
1070 private Set<Artifact> findNativeLibraryDependencies() throws MojoExecutionException
1071 {
1072 final NativeHelper nativeHelper = getNativeHelper();
1073 final Set<Artifact> staticLibraryArtifacts = nativeHelper.getNativeDependenciesArtifacts( false );
1074 final Set<Artifact> sharedLibraryArtifacts = nativeHelper.getNativeDependenciesArtifacts( true );
1075
1076 final Set<Artifact> mergedArtifacts = new LinkedHashSet<Artifact>();
1077 filterNativeDependencies( mergedArtifacts, staticLibraryArtifacts );
1078 filterNativeDependencies( mergedArtifacts, sharedLibraryArtifacts );
1079
1080 getLog().debug( "findNativeLibraryDependencies found " + mergedArtifacts.size() + ": " + mergedArtifacts.toString() );
1081
1082 return mergedArtifacts;
1083 }
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096 private void filterNativeDependencies( Set<Artifact> targetSet, Set<Artifact> source )
1097 {
1098 for ( Artifact a : source )
1099 {
1100 if ( project.getGroupId().equals( a.getGroupId() ) && project.getArtifactId().equals( a.getArtifactId() ) )
1101 {
1102 getLog().warn( "Excluding native dependency attached by this build" );
1103 }
1104 else
1105 {
1106 targetSet.add( a );
1107 }
1108 }
1109 }
1110
1111
1112
1113
1114
1115
1116
1117
1118 private String resolveArtifactType( File file )
1119 {
1120 if ( Const.ArtifactType.NATIVE_SYMBOL_OBJECT.equals( project.getPackaging() ) || Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE.equals( project.getPackaging() ) )
1121 {
1122 return project.getPackaging();
1123 }
1124 else
1125 {
1126
1127 return file.getName().endsWith( Const.ArtifactType.NATIVE_SYMBOL_OBJECT ) ? Const.ArtifactType.NATIVE_SYMBOL_OBJECT : Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE;
1128 }
1129 }
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144 protected AndroidNdk getAndroidNdk() throws MojoExecutionException
1145 {
1146 final File chosenNdkPath;
1147
1148 if ( ndkPath != null )
1149 {
1150
1151 chosenNdkPath = ndkPath;
1152 }
1153 else
1154 {
1155
1156 chosenNdkPath = new File( getAndroidNdkHomeOrThrow() );
1157 }
1158
1159 return new AndroidNdk( chosenNdkPath );
1160 }
1161
1162
1163
1164
1165
1166
1167 private String getAndroidNdkHomeOrThrow() throws MojoExecutionException
1168 {
1169 final String androidHome = System.getenv( ENV_ANDROID_NDK_HOME );
1170 if ( StringUtils.isBlank( androidHome ) )
1171 {
1172 throw new MojoExecutionException( "No Android NDK path could be found. You may configure it in the pom using <ndkPath>...</ndkPath> or "
1173 + "<properties><android.ndk.path>...</android.ndk.path></properties> or on command-line using -Dandroid.ndk.path=... "
1174 + "or by setting environment variable " + ENV_ANDROID_NDK_HOME );
1175 }
1176 return androidHome;
1177 }
1178
1179 protected final ArtifactResolverHelper getArtifactResolverHelper()
1180 {
1181 if ( artifactResolverHelper == null )
1182 {
1183 artifactResolverHelper = new ArtifactResolverHelper( artifactResolver, new MavenToPlexusLogAdapter( getLog() ), project.getRemoteArtifactRepositories() );
1184 }
1185 return artifactResolverHelper;
1186 }
1187
1188 protected final NativeHelper getNativeHelper()
1189 {
1190 if ( nativeHelper == null )
1191 {
1192 nativeHelper = new NativeHelper( project, dependencyGraphBuilder, getLog() );
1193 }
1194 return nativeHelper;
1195 }
1196
1197
1198 }