1 package com.simpligility.maven.plugins.androidndk.phase05compile;
2
3 import com.simpligility.maven.plugins.androidndk.common.AndroidExtension;
4 import com.simpligility.maven.plugins.androidndk.common.ArtifactResolverHelper;
5 import com.simpligility.maven.plugins.androidndk.common.Const;
6 import com.simpligility.maven.plugins.androidndk.common.JarHelper;
7 import com.simpligility.maven.plugins.androidndk.common.MavenToPlexusLogAdapter;
8 import com.simpligility.maven.plugins.androidndk.common.NativeHelper;
9 import com.simpligility.maven.plugins.androidndk.common.UnpackedLibHelper;
10 import com.simpligility.maven.plugins.androidndk.configuration.IgnoreHeaderFilesArchive;
11 import org.apache.commons.io.FileUtils;
12 import org.apache.commons.io.FilenameUtils;
13 import org.apache.commons.io.filefilter.TrueFileFilter;
14 import org.apache.maven.artifact.Artifact;
15 import org.apache.maven.artifact.DefaultArtifact;
16 import org.apache.maven.artifact.handler.ArtifactHandler;
17 import org.apache.maven.plugin.MojoExecutionException;
18 import org.apache.maven.plugin.logging.Log;
19 import org.apache.maven.project.MavenProject;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Set;
28 import java.util.jar.JarEntry;
29 import java.util.jar.JarFile;
30
31
32
33
34
35
36 public class MakefileHelper
37 {
38 public static class MakefileRequest
39 {
40 Set<Artifact> artifacts;
41 String defaultNDKArchitecture;
42 boolean useHeaderArchives;
43 boolean leaveTemporaryBuildArtifacts;
44 String[] architectures;
45 List<IgnoreHeaderFilesArchive> ignoreHeaderFilesArchives;
46 }
47
48
49 private class LibraryDetails
50 {
51 Artifact artifact;
52 Artifact harArtifact;
53
54 String architecture;
55 String localModule;
56 File libraryPath;
57 String localSrcFiles;
58 String localModuleFileName;
59
60 boolean useHeaderArchives;
61 boolean leaveTemporaryBuildArtifacts;
62
63 List<File> includeDirectories;
64 MakefileResponse makefileResponse;
65 }
66
67 public static final String MAKEFILE_CAPTURE_FILE = "ANDROID_MAVEN_PLUGIN_LOCAL_C_INCLUDES_FILE";
68
69
70
71
72
73 public static class MakefileResponse
74 {
75 final boolean leaveTemporaryBuildArtifacts;
76 final StringBuilder makeFile;
77 final List<File> includeDirectories;
78 private Set<String> staticLibraryList = new HashSet<String> ( );
79 private Set<String> sharedLibraryList = new HashSet<String> ( );
80
81 public MakefileResponse ( List<File> includeDirectories, StringBuilder makeFile, boolean leaveTemporaryBuildArtifacts )
82 {
83 this.includeDirectories = includeDirectories;
84 this.makeFile = makeFile;
85 this.leaveTemporaryBuildArtifacts = leaveTemporaryBuildArtifacts;
86 }
87
88 public List<File> getIncludeDirectories()
89 {
90 return includeDirectories;
91 }
92
93 public String getMakeFile()
94 {
95 return makeFile.toString ();
96 }
97
98 public boolean isLeaveTemporaryBuildArtifacts()
99 {
100 return leaveTemporaryBuildArtifacts;
101 }
102
103 public boolean hasStaticLibraryDepdendencies ()
104 {
105 return !staticLibraryList.isEmpty ();
106 }
107
108 public String getStaticLibraryList ()
109 {
110 StringBuilder sb = new StringBuilder ( );
111 for ( String staticLibraryName : staticLibraryList )
112 {
113 sb.append ( staticLibraryName );
114 sb.append ( " " );
115 }
116 return sb.toString ();
117 }
118
119 public void addStaticLibraryName( final String staticLibraryName )
120 {
121 staticLibraryList.add ( staticLibraryName );
122 }
123
124 public boolean hasSharedLibraryDepdendencies ()
125 {
126 return !sharedLibraryList.isEmpty ();
127 }
128
129 public String getSharedLibraryList ()
130 {
131 StringBuilder sb = new StringBuilder ( );
132 for ( String sharedLibraryName : sharedLibraryList )
133 {
134 sb.append ( sharedLibraryName );
135 sb.append ( " " );
136 }
137 return sb.toString ();
138 }
139
140 public void addSharedLibraryName( final String sharedLibraryName )
141 {
142 sharedLibraryList.add ( sharedLibraryName );
143 }
144
145 }
146
147 private final MavenProject project;
148 private final Log log;
149 private final ArtifactResolverHelper artifactResolverHelper;
150 private final ArtifactHandler harArtifactHandler;
151 private final File unpackedApkLibsDirectory;
152 private final File ndkBuildDirectory;
153
154
155
156
157
158
159
160
161 public MakefileHelper( final MavenProject project, final Log log, final ArtifactResolverHelper artifactResolverHelper,
162 final ArtifactHandler harHandler, final File unpackedApkLibsDirectory, final File ndkBuildDirectory )
163 {
164 this.project = project;
165 this.log = log;
166 this.artifactResolverHelper = artifactResolverHelper;
167 this.harArtifactHandler = harHandler;
168 this.unpackedApkLibsDirectory = unpackedApkLibsDirectory;
169 this.ndkBuildDirectory = ndkBuildDirectory;
170 }
171
172
173
174
175
176
177
178 public static void cleanupAfterBuild( final MakefileResponse makefileResponse )
179 {
180 if ( !makefileResponse.isLeaveTemporaryBuildArtifacts() )
181 {
182 if ( makefileResponse.getIncludeDirectories() != null )
183 {
184 for ( File file : makefileResponse.getIncludeDirectories() )
185 {
186 try
187 {
188 FileUtils.deleteDirectory( file );
189 }
190 catch ( IOException e )
191 {
192 e.printStackTrace();
193 }
194 }
195 }
196 }
197 }
198
199
200
201
202
203
204
205
206
207 public MakefileResponse createMakefileFromArtifacts ( MakefileRequest makefileRequest )
208 throws IOException, MojoExecutionException
209 {
210 final List<File> includeDirectories = new ArrayList<File>();
211 final StringBuilder makeFile = new StringBuilder( "# Generated by Android Maven Plugin\n" );
212
213 final MakefileResponse makefileResponse = new MakefileResponse ( includeDirectories, makeFile, makefileRequest.leaveTemporaryBuildArtifacts );
214
215 final Set<Artifact> artifacts = makefileRequest.artifacts;
216
217
218
219 makeFile.append( "$(shell echo \"LOCAL_C_INCLUDES=$(LOCAL_C_INCLUDES)\" > $(" + MAKEFILE_CAPTURE_FILE + "))" );
220 makeFile.append( '\n' );
221 makeFile.append( "$(shell echo \"LOCAL_PATH=$(LOCAL_PATH)\" >> $(" + MAKEFILE_CAPTURE_FILE + "))" );
222 makeFile.append( '\n' );
223 makeFile.append( "$(shell echo \"LOCAL_MODULE=$(LOCAL_MODULE)\" >> $(" + MAKEFILE_CAPTURE_FILE + "))" );
224 makeFile.append( '\n' );
225 makeFile.append( "$(shell echo \"LOCAL_MODULE_FILENAME=$(LOCAL_MODULE_FILENAME)\" >> $("
226 + MAKEFILE_CAPTURE_FILE + "))" );
227 makeFile.append( '\n' );
228 makeFile.append( "$(shell echo \"LOCAL_CFLAGS=$(LOCAL_CFLAGS)\" >> $(" + MAKEFILE_CAPTURE_FILE + "))" );
229 makeFile.append( '\n' );
230 makeFile.append( "$(shell echo \"LOCAL_SHARED_LIBRARIES=$(LOCAL_SHARED_LIBRARIES)\" >> $(" + MAKEFILE_CAPTURE_FILE + "))" );
231 makeFile.append( '\n' );
232 makeFile.append( "$(shell echo \"LOCAL_STATIC_LIBRARIES=$(LOCAL_STATIC_LIBRARIES)\" >> $(" + MAKEFILE_CAPTURE_FILE + "))" );
233 makeFile.append( '\n' );
234 makeFile.append( "$(shell echo \"LOCAL_EXPORT_C_INCLUDES=$(LOCAL_EXPORT_C_INCLUDES)\" >> $(" + MAKEFILE_CAPTURE_FILE + "))" );
235 makeFile.append( '\n' );
236 makeFile.append( "$(shell echo \"LOCAL_SRC_FILES=$(LOCAL_SRC_FILES)\" >> $(" + MAKEFILE_CAPTURE_FILE + "))" );
237 makeFile.append( '\n' );
238
239 if ( ! artifacts.isEmpty() )
240 {
241 for ( Artifact artifact : artifacts )
242 {
243
244 if ( !isLibraryBundle( artifact ) )
245 {
246 final String architecture = NativeHelper.extractArchitectureFromArtifact ( artifact, makefileRequest.defaultNDKArchitecture );
247
248 final LibraryDetails libraryDetails = new LibraryDetails ();
249
250 libraryDetails.makefileResponse = makefileResponse;
251 libraryDetails.artifact = artifact;
252
253 libraryDetails.architecture = architecture;
254 libraryDetails.localModule = artifact.getArtifactId ();
255 libraryDetails.libraryPath = artifact.getFile ();
256
257 libraryDetails.useHeaderArchives = useHeaderArchives( artifact, makefileRequest.useHeaderArchives, makefileRequest.ignoreHeaderFilesArchives );
258 libraryDetails.leaveTemporaryBuildArtifacts = makefileRequest.leaveTemporaryBuildArtifacts;
259
260 libraryDetails.includeDirectories = includeDirectories;
261
262 libraryDetails.harArtifact = new DefaultArtifact ( artifact.getGroupId (), artifact.getArtifactId (),
263 artifact.getVersion (), artifact.getScope (),
264 Const.ArtifactType.NATIVE_HEADER_ARCHIVE, artifact.getClassifier (), harArtifactHandler );
265
266 addLocalModule( libraryDetails );
267 }
268 else
269 {
270 final LibraryDetails libraryDetails = new LibraryDetails ();
271
272 libraryDetails.makefileResponse = makefileResponse;
273 libraryDetails.artifact = artifact;
274 libraryDetails.useHeaderArchives = makefileRequest.useHeaderArchives;
275 libraryDetails.leaveTemporaryBuildArtifacts = makefileRequest.leaveTemporaryBuildArtifacts;
276
277
278 libraryDetails.includeDirectories = includeDirectories;
279
280
281 addLibraryBundleDetails ( libraryDetails, makefileRequest.architectures );
282 }
283 }
284 }
285 return makefileResponse;
286 }
287
288 private boolean useHeaderArchives ( final Artifact artifact, final boolean useHeaderArchives, final List<IgnoreHeaderFilesArchive> ignoreHeaderFilesArchives )
289 {
290 if ( !useHeaderArchives )
291 {
292 return false;
293 }
294
295 if ( ignoreHeaderFilesArchives != null )
296 {
297 for ( IgnoreHeaderFilesArchive directive : ignoreHeaderFilesArchives )
298 {
299 if ( directive.getGroupId ().equals ( artifact.getGroupId () ) && directive.getArtifactId ().equals ( artifact.getArtifactId () ) )
300 {
301 return false;
302 }
303 }
304 }
305 return true;
306 }
307
308
309 private void addLocalModule ( final LibraryDetails libraryDetails ) throws MojoExecutionException, IOException
310 {
311 final Artifact artifact = libraryDetails.artifact;
312 final StringBuilder makeFile = libraryDetails.makefileResponse.makeFile;
313 final boolean isStaticLibrary = Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE.equals ( libraryDetails.artifact.getType () );
314
315
316 makeFile.append ( '\n' );
317 makeFile.append ( "ifeq ($(TARGET_ARCH_ABI)," ).append ( libraryDetails.architecture ).append ( ")\n" );
318
319 makeFile.append ( "#\n" );
320 makeFile.append ( "# Group ID: " );
321 makeFile.append ( artifact.getGroupId () );
322 makeFile.append ( '\n' );
323 makeFile.append ( "# Artifact ID: " );
324 makeFile.append ( artifact.getArtifactId () );
325 makeFile.append ( '\n' );
326 makeFile.append ( "# Artifact Type: " );
327 makeFile.append ( artifact.getType () );
328 makeFile.append ( '\n' );
329 makeFile.append ( "# Version: " );
330 makeFile.append ( artifact.getVersion () );
331 makeFile.append ( '\n' );
332 makeFile.append ( "include $(CLEAR_VARS)" );
333 makeFile.append ( '\n' );
334 makeFile.append ( "LOCAL_MODULE := " );
335 makeFile.append ( libraryDetails.localModule );
336 makeFile.append ( '\n' );
337
338 if ( isStaticLibrary )
339 {
340 libraryDetails.makefileResponse.addStaticLibraryName ( libraryDetails.localModule );
341 }
342 else
343 {
344 libraryDetails.makefileResponse.addSharedLibraryName ( libraryDetails.localModule );
345 }
346
347
348 addLibraryDetails ( makeFile, libraryDetails.libraryPath, artifact.getArtifactId() );
349
350 if ( libraryDetails.useHeaderArchives )
351 {
352 try
353 {
354 final String classifier = artifact.getClassifier ();
355
356 File resolvedHarArtifactFile = artifactResolverHelper.resolveArtifactToFile ( libraryDetails.harArtifact );
357 log.debug ( "Resolved har artifact file : " + resolvedHarArtifactFile );
358
359 final File includeDir = new File ( ndkBuildDirectory, "android_maven_plugin_native_includes" + System.currentTimeMillis () + "_"
360 + libraryDetails.harArtifact.getArtifactId () );
361
362 if ( !libraryDetails.leaveTemporaryBuildArtifacts )
363 {
364 includeDir.deleteOnExit ();
365 }
366
367 libraryDetails.includeDirectories.add ( includeDir );
368
369 JarHelper.unjar ( new JarFile ( resolvedHarArtifactFile ), includeDir,
370 new JarHelper.UnjarListener ()
371 {
372 @Override
373 public boolean include ( JarEntry jarEntry )
374 {
375 return !jarEntry.getName ().startsWith ( "META-INF" );
376 }
377 } );
378
379 makeFile.append ( "LOCAL_EXPORT_C_INCLUDES := " );
380 makeFile.append ( includeDir.getAbsolutePath () );
381 makeFile.append ( '\n' );
382
383 if ( log.isDebugEnabled () )
384 {
385 Collection<File> includes = FileUtils.listFiles ( includeDir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE );
386 log.debug ( "Listing LOCAL_EXPORT_C_INCLUDES for " + artifact.getId () + ": " + includes );
387 }
388 }
389 catch ( RuntimeException e )
390 {
391 throw new MojoExecutionException ( "Error while resolving header archive file for: " + artifact.getArtifactId (), e );
392 }
393 }
394 if ( isStaticLibrary )
395 {
396 makeFile.append ( "include $(PREBUILT_STATIC_LIBRARY)\n" );
397 }
398 else
399 {
400 makeFile.append ( "include $(PREBUILT_SHARED_LIBRARY)\n" );
401 }
402
403 makeFile.append ( "endif #" ).append ( artifact.getClassifier () ).append ( '\n' );
404 makeFile.append ( '\n' );
405
406
407
408 }
409
410 private boolean isLibraryBundle( Artifact artifact )
411 {
412 return artifact.getType ().equals ( AndroidExtension.APKLIB ) || artifact.getType ().equals ( AndroidExtension.AAR );
413 }
414
415 private void addLibraryBundleDetails( final LibraryDetails libraryDetails, final String[] architectures ) throws MojoExecutionException, IOException
416 {
417 final Artifact artifact = libraryDetails.artifact;
418
419
420
421
422
423
424 UnpackedLibHelper unpackedLibHelper = new UnpackedLibHelper ( artifactResolverHelper, project, new MavenToPlexusLogAdapter ( log ), unpackedApkLibsDirectory );
425
426 if ( artifact.getType ().equals ( AndroidExtension.AAR ) )
427 {
428 unpackedLibHelper.extractAarLib ( artifact );
429 }
430 else if ( artifact.getType ().equals ( AndroidExtension.APKLIB ) )
431 {
432 unpackedLibHelper.extractApklib ( artifact );
433 }
434
435 for ( int i = 0; i < architectures.length; i++ )
436 {
437 String architecture = architectures[ i ];
438
439 final File[] staticLibs = NativeHelper.listNativeFiles ( artifact, unpackedLibHelper.getUnpackedLibNativesFolder ( artifact ), true, architecture );
440 processBundledLibraries( architecture, libraryDetails, staticLibs );
441
442 final File[] sharedLibs = NativeHelper.listNativeFiles ( artifact, unpackedLibHelper.getUnpackedLibNativesFolder ( artifact ), false, architecture );
443 processBundledLibraries( architecture, libraryDetails, sharedLibs );
444
445 }
446
447 }
448
449 private void processBundledLibraries ( final String architecture, final LibraryDetails libraryDetails, final File[] staticLibs ) throws IOException, MojoExecutionException
450 {
451 final Artifact artifact = libraryDetails.artifact;
452
453 for ( File staticLib : staticLibs )
454 {
455
456 libraryDetails.architecture = architecture;
457 libraryDetails.localModule = artifact.getArtifactId ();
458
459 libraryDetails.libraryPath = staticLib;
460
461 final String classifier = artifact.getClassifier () == null ? architecture : architecture + "-" + artifact.getClassifier ();
462
463 libraryDetails.harArtifact = new DefaultArtifact ( artifact.getGroupId (), artifact.getArtifactId (),
464 artifact.getVersion (), artifact.getScope (),
465 Const.ArtifactType.NATIVE_HEADER_ARCHIVE, classifier, harArtifactHandler );
466
467 libraryDetails.localModuleFileName = artifact.getArtifactId ();
468
469
470 addLocalModule ( libraryDetails );
471
472 }
473 }
474
475 private void addLibraryDetails( StringBuilder makeFile, File libFile, String outputName ) throws IOException
476 {
477 makeFile.append( "LOCAL_PATH := " );
478 makeFile.append( libFile.getParentFile().getAbsolutePath() );
479 makeFile.append( '\n' );
480 makeFile.append( "LOCAL_SRC_FILES := " );
481 makeFile.append( libFile.getName() );
482 makeFile.append( '\n' );
483 makeFile.append( "LOCAL_MODULE_FILENAME := " );
484 if ( "".equals( outputName ) )
485 {
486 makeFile.append( FilenameUtils.removeExtension( libFile.getName() ) );
487 }
488 else
489 {
490 makeFile.append( outputName );
491 }
492 makeFile.append( '\n' );
493 }
494
495 }