Coverage Report - com.simpligility.maven.plugins.androidndk.phase05compile.NdkBuildMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
NdkBuildMojo
0%
0/292
0%
0/138
3.806
NdkBuildMojo$1
0%
0/10
0%
0/18
3.806
NdkBuildMojo$2
0%
0/7
0%
0/14
3.806
NdkBuildMojo$3
0%
0/8
0%
0/4
3.806
NdkBuildMojo$CompileCommand
0%
0/3
N/A
3.806
 
 1  
 /*
 2  
  * Licensed under the Apache License, Version 2.0 (the "License");
 3  
  * you may not use this file except in compliance with the License.
 4  
  * You may obtain a copy of the License at
 5  
  *
 6  
  *      http://www.apache.org/licenses/LICENSE-2.0
 7  
  *
 8  
  * Unless required by applicable law or agreed to in writing, software
 9  
  * distributed under the License is distributed on an "AS IS" BASIS,
 10  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 11  
  * See the License for the specific language governing permissions and
 12  
  * limitations under the License.
 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  
  * @author Johan Lindquist <johanlindquist@gmail.com>
 62  
  */
 63  
 @Mojo( name = "ndk-build", defaultPhase = LifecyclePhase.COMPILE )
 64  0
 public class NdkBuildMojo extends AbstractMojo
 65  
 {
 66  
     /**
 67  
      * The <code>ANDROID_NDK_HOME</code> environment variable name.
 68  
      */
 69  
     public static final String ENV_ANDROID_NDK_HOME = "ANDROID_NDK_HOME";
 70  
 
 71  
     /**
 72  
      * <p>Parameter designed to pick up <code>-Dandroid.ndk.ndkPath</code> in case there is no pom with an
 73  
      * <code>&lt;ndk&gt;</code> configuration tag.</p>
 74  
      */
 75  
     @Parameter( property = "android.ndk.ndkPath", readonly = true )
 76  
     private File ndkPath;
 77  
 
 78  
     /**
 79  
      * Allows for overriding the default ndk-build executable.
 80  
      */
 81  
     @Parameter( property = "android.ndk.ndkBuildExecutable" )
 82  
     private String ndkBuildExecutable;
 83  
 
 84  
     /**
 85  
      * Folder in which the NDK makefiles are constructed.
 86  
      */
 87  
     @Parameter( property = "android.ndk.buildDirectory", defaultValue = "${project.build.directory}/android-ndk-maven-plugin", readonly = true )
 88  
     private File buildDirectory;
 89  
 
 90  
     /** Folder in which the ndk-build command is executed.  This is using the -C command line flag.
 91  
      */
 92  
     @Parameter( property = "android.ndk.workingDirectory", defaultValue = "${project.basedir}", readonly = true )
 93  
     private File workingDirectory;
 94  
 
 95  
     /** Specifies the classifier with which the artifact should be stored in the repository
 96  
      */
 97  
     @Parameter( property = "android.ndk.classifier" )
 98  
     private String classifier;
 99  
 
 100  
     /** Specifies additional command line parameters to pass to ndk-build
 101  
      */
 102  
     @Parameter( property = "android.ndk.additionalCommandline" )
 103  
     protected String additionalCommandline;
 104  
 
 105  
     /**
 106  
      * <p>Folder containing native, static libraries compiled and linked by the NDK.</p>
 107  
      * <p/>
 108  
      */
 109  
     @Parameter( property = "android.ndk.objectsOutputDirectory", defaultValue = "${project.build.directory}/obj" )
 110  
     private File objectsOutputDirectory;
 111  
 
 112  
     /**
 113  
      * <p>Folder containing native, static libraries compiled and linked by the NDK.</p>
 114  
      * <p/>
 115  
      */
 116  
     @Parameter( property = "android.ndk.librariesOutputDirectory", defaultValue = "${project.build.directory}/ndk-libs" )
 117  
     private File librariesOutputDirectory;
 118  
 
 119  
     /**
 120  
      * Folder in which AAR/APKLIB library dependencies will be unpacked.
 121  
      */
 122  
     @Parameter( property = "unpackedLibsFolder", defaultValue = "${project.build.directory}/unpacked-libs" )
 123  
     private File unpackedLibsFolder;
 124  
 
 125  
     /**
 126  
      * <p>Target to invoke on the native makefile.</p>
 127  
      */
 128  
     @Parameter( property = "android.ndk.target" )
 129  
     private String target;
 130  
 
 131  
     /**
 132  
      * Defines the architectures for the NDK build - this is a space separated list (i.e x86 armeabi)
 133  
      */
 134  
     @Parameter( property = "android.ndk.architectures" )
 135  
     private String architectures;
 136  
 
 137  
     /**
 138  
      * Defines the architecture to toolchain mappings for the NDK build
 139  
      * &lt;architectureToolchainMappings&gt;
 140  
      * &lt;x86&gt;x86-4.7&lt;/x86&gt;
 141  
      * &lt;armeabi&gt;arm-linux-androideabi-4.7&lt;/armeabi&gt;
 142  
      * &lt;/architectureToolchainMappings&gt;
 143  
      */
 144  
     @Parameter
 145  
     private ArchitectureToolchainMappings architectureToolchainMappings;
 146  
 
 147  
     /**
 148  
      * Flag indicating whether the header files used in the build should be included and attached to the build as
 149  
      * an additional artifact.
 150  
      */
 151  
     @Parameter( property = "android.ndk.attachHeaderFiles", defaultValue = "true" )
 152  
     private Boolean attachHeaderFiles;
 153  
 
 154  
     /**
 155  
      * Flag indicating whether the final artifacts should be included and attached to the build as an artifact.
 156  
      */
 157  
     @Parameter( property = "android.ndk.attachLibrariesArtifacts", defaultValue = "true" )
 158  
     private Boolean attachLibrariesArtifacts;
 159  
 
 160  
     /**
 161  
      * Flag indicating whether temporary build artifacts are removed after a build
 162  
      */
 163  
     @Parameter( property = "android.ndk.leaveTemporaryBuildArtifacts", defaultValue = "false" )
 164  
     private Boolean leaveTemporaryBuildArtifacts;
 165  
 
 166  
     /**
 167  
      * Flag indicating whether the make files last LOCAL_SRC_INCLUDES should be used for determining what header
 168  
      * files to include.  Setting this flag to true, overrides any defined header files directives.
 169  
      * <strong>Note: </strong> By setting this flag to true, all header files used in the project will be
 170  
      * added to the resulting header archive.  This may be undesirable in most cases and is therefore turned off by
 171  
      * default.
 172  
      */
 173  
     @Parameter( property = "android.ndk.useLocalSrcIncludePaths", defaultValue = "false" )
 174  
     private Boolean useLocalSrcIncludePaths;
 175  
 
 176  
     /**
 177  
      * Specifies the set of header files includes/excludes which should be used for bundling the exported header
 178  
      * files.  The below shows an example of how this can be used.
 179  
      * <p/>
 180  
      * <pre>
 181  
      * &lt;headerFilesDirectives&gt;
 182  
      *   &lt;headerFilesDirective&gt;
 183  
      *     &lt;directory&gt;${basedir}/jni/include&lt;/directory&gt;
 184  
      *     &lt;includes&gt;
 185  
      *       &lt;includes&gt;**\/*.h&lt;/include&gt;
 186  
      *     &lt;/includes&gt;
 187  
      *   &lt;headerFilesDirective&gt;
 188  
      * &lt;/headerFilesDirectives&gt;
 189  
      * </pre>
 190  
      * <br/>
 191  
      * If no <code>headerFilesDirectives</code> is specified, the default includes will be defined as shown below:
 192  
      * <br/>
 193  
      * <pre>
 194  
      * &lt;headerFilesDirectives&gt;
 195  
      *   &lt;headerFilesDirective&gt;
 196  
      *     &lt;directory&gt;${basedir}/jni&lt;/directory&gt;
 197  
      *     &lt;includes&gt;
 198  
      *       &lt;includes&gt;**\/*.h&lt;/include&gt;
 199  
      *     &lt;/includes&gt;
 200  
      *     &lt;excludes&gt;
 201  
      *       &lt;exclude&gt;**\/*.c&lt;/exclude&gt;
 202  
      *     &lt;/excludes&gt;
 203  
      *   &lt;headerFilesDirective&gt;
 204  
      *   [..]
 205  
      * &lt;/headerFilesDirectives&gt;
 206  
      * </pre>
 207  
      */
 208  
     @Parameter
 209  
     private List<HeaderFilesDirective> headerFilesDirectives;
 210  
 
 211  
     /**
 212  
      * Flag indicating whether the header files for native, static library dependencies should be used.  If true,
 213  
      * the header archive for each statically linked dependency will be resolved.
 214  
      */
 215  
     @Parameter( property = "android.ndk.build.use-header-archive", defaultValue = "true" )
 216  
     private Boolean useHeaderArchives;
 217  
 
 218  
     /** Specifies a set of group/artifact identifiers for which header archives should not be attempted to be resolved.
 219  
      * This is useful when a static library dependcy on other static libraries but the headers of those libraries are not necessarily
 220  
      * available.  This allows the plugin to exclude the retrieval of those header archives
 221  
      * <p/>
 222  
      * <pre>
 223  
      *   &lt;ignoreHeaderFilesArchives&gt;
 224  
      *     &lt;ignoreHeaderFilesArchive&gt;
 225  
      *       &lt;groupId&gt;com.insidesecure.drm.agent.android&lt;/groupId&gt;
 226  
      *       &lt;artifactId&gt;expat-static-lib&lt;/artifactId&gt;
 227  
      *     &lt;/ignoreHeaderFilesArchive&gt;
 228  
      *     &lt;ignoreHeaderFilesArchive&gt;
 229  
      *       &lt;groupId&gt;com.insidesecure.drm.agent.android&lt;/groupId&gt;
 230  
      *       &lt;artifactId&gt;pcre-static-&lt;/artifactId&gt;
 231  
      *     &lt;/ignoreHeaderFilesArchive&gt;
 232  
      *   &lt;/ignoreHeaderFilesArchives&gt;
 233  
      * </pre>
 234  
      *
 235  
      */
 236  
     @Parameter
 237  
     private List<IgnoreHeaderFilesArchive> ignoreHeaderFilesArchives;
 238  
 
 239  
     /**
 240  
      * Defines additional system properties which should be exported to the ndk-build script.  This
 241  
      * <br/>
 242  
      * <pre>
 243  
      * &lt;systemProperties&gt;
 244  
      *   &lt;propertyName&gt;propertyValue&lt;/propertyName&gt;
 245  
      *   &lt;build-target&gt;android&lt;/build-target&gt;
 246  
      *   [..]
 247  
      * &lt;/systemProperties&gt;
 248  
      * </pre>     *
 249  
      */
 250  
     @Parameter
 251  
     private Map<String, String> systemProperties;
 252  
 
 253  
     /**
 254  
      * Flag indicating whether warnings should be ignored while compiling.  If true,
 255  
      * the build will not fail if warning are found during compile.
 256  
      */
 257  
     @Parameter( property = "android.ndk.ignoreBuildWarnings", defaultValue = "true" )
 258  
     private Boolean ignoreBuildWarnings;
 259  
 
 260  
     /**
 261  
      * Defines the regular expression used to detect whether error/warning output from ndk-build is a minor compile
 262  
      * warning or is actually an error which should cause the build to fail.
 263  
      * <p/>
 264  
      * If the pattern matches, the output from the compiler will <strong>not</strong> be considered an error and compile
 265  
      * will be successful.
 266  
      */
 267  
     @Parameter( property = "android.ndk.buildWarningsRegularExpression", defaultValue = ".*[warning|note]: .*" )
 268  
     private String buildWarningsRegularExpression;
 269  
 
 270  
     /** Specifies the NDK toolchain to use for the build.  This will be using the NDK_TOOLCHAIN define on the ndk-build commandline.
 271  
      */
 272  
     @Parameter( property = "android.ndk.build.ndk-toolchain" )
 273  
     private String ndkToolchain;
 274  
 
 275  
     /**
 276  
      * Specifies the final name of the library output by the build (this allows the pom to override the default artifact name).
 277  
      * The value should not include the 'lib' prefix or filename extension (e.g. '.so').
 278  
      */
 279  
     @Parameter( property = "android.ndk.finalLibraryName" )
 280  
     private String finalLibraryName;
 281  
 
 282  
     /**
 283  
      * Specifies the makefile to use for the build (if other than the default Android.mk).
 284  
      */
 285  
     @Parameter( property = "android.ndk.makefile" )
 286  
     private String makefile;
 287  
 
 288  
     /**
 289  
      * Specifies the application makefile to use for the build (if other than the default Application.mk).
 290  
      */
 291  
     @Parameter( property = "android.ndk.applicationMakefile" )
 292  
     private String applicationMakefile;
 293  
 
 294  
     /**
 295  
      * Flag indicating whether to use the max available jobs for the host machine
 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  
      * Flag indicating whether or not the build should be skipped entirely
 308  
      */
 309  
     @Parameter( defaultValue = "false" )
 310  
     private boolean skip;
 311  
 
 312  
     /**
 313  
      * Flag indicating whether or not multiple number of native libraries should be included
 314  
      */
 315  
     @Parameter( defaultValue = "false" )
 316  
     private boolean allowMultiArtifacts;
 317  
 
 318  
     /**
 319  
      * The Jar archiver.
 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  
      * The maven project.
 329  
      */
 330  
     @Parameter( defaultValue = "${project}", readonly = true, required = true )
 331  
     protected MavenProject project;
 332  
 
 333  
     /**
 334  
      * Maven ProjectHelper.
 335  
      */
 336  
     @Component
 337  
     protected MavenProjectHelper projectHelper;
 338  
 
 339  
     @Component
 340  
     private ArtifactResolver artifactResolver;
 341  
 
 342  
     /**
 343  
      * Dependency graph builder component.
 344  
      */
 345  
     @Component( hint = "default" )
 346  
     protected DependencyGraphBuilder dependencyGraphBuilder;
 347  
 
 348  
     private ArtifactResolverHelper artifactResolverHelper;
 349  
     private NativeHelper nativeHelper;
 350  
 
 351  
     /**
 352  
      * @parameter expression="${mojoExecution}"
 353  
      */
 354  
     @Component
 355  
     private org.apache.maven.plugin.MojoExecution execution;
 356  
 
 357  
     /**
 358  
      * @throws MojoExecutionException
 359  
      * @throws MojoFailureException
 360  
      */
 361  
     public void execute() throws MojoExecutionException, MojoFailureException
 362  
     {
 363  0
         if ( skip )
 364  
         {
 365  0
             getLog().info( "Skipping execution as per configuration" );
 366  0
             return;
 367  
         }
 368  
 
 369  0
         if ( !attachLibrariesArtifacts && NativeHelper.isNativeArtifactProject( project ) )
 370  
         {
 371  0
             getLog().warn( "Configured to not attach artifacts, this may cause an error at install/deploy time" );
 372  
         }
 373  
 
 374  
         // Validate the NDK
 375  0
         final File ndkBuildFile = new File( getAndroidNdk().getNdkBuildPath() );
 376  0
         NativeHelper.validateNDKVersion( ndkBuildFile.getParentFile() );
 377  
 
 378  0
         validateMakefile( project, makefile );
 379  
 
 380  0
         final String[] resolvedNDKArchitectures = NativeHelper.getNdkArchitectures( architectures, applicationMakefile, project.getBasedir() );
 381  
 
 382  
         // Resolve all dependencies
 383  
 
 384  0
         final Set<Artifact> nativeLibraryArtifacts = findNativeLibraryDependencies();
 385  
 
 386  
         // If there are any static libraries the code needs to link to, include those in the make file
 387  0
         final Set<Artifact> resolvedNativeLibraryArtifacts = getArtifactResolverHelper().resolveArtifacts( nativeLibraryArtifacts );
 388  
 
 389  0
         getLog().debug( "resolveArtifacts found " + resolvedNativeLibraryArtifacts.size() + ": " + resolvedNativeLibraryArtifacts.toString() );
 390  
 
 391  0
         CompileCommand compileCommand = new CompileCommand ();
 392  0
         compileCommand.nativeLibraryDepedencies = resolvedNativeLibraryArtifacts;
 393  0
         compileCommand.resolvedArchitectures = resolvedNDKArchitectures;
 394  
 
 395  0
         setupOutputDirectories( compileCommand );
 396  
 
 397  0
         compile ( compileCommand );
 398  
 
 399  0
     }
 400  
 
 401  
     private void setupOutputDirectories ( final CompileCommand compileCommand )
 402  
     {
 403  0
         compileCommand.librariesOutputDirectory = librariesOutputDirectory;
 404  0
         compileCommand.objectsOutputDirectory = objectsOutputDirectory;
 405  
 
 406  
         // This indicates that the build is either the default build (by extension)
 407  
         // or an execution within an (for example) APKLIB build.
 408  
         // The execution can currently not be named since it would differ
 409  
         // for APKLIB once that is executed.
 410  
         //
 411  
         // TODO: Once the AAR/APKLIB can pull the attached artifacts from the
 412  
         // TODO: Maven session, this can be sorted out better.
 413  
         //
 414  0
         if ( ! "default-ndk-build".equals( execution.getExecutionId () ) && ! "default".equals ( execution.getExecutionId () ) )
 415  
         {
 416  0
             String libsOut = librariesOutputDirectory.getAbsolutePath();
 417  0
             String out = objectsOutputDirectory.getAbsolutePath();
 418  
 
 419  0
             libsOut = libsOut + "/" + execution.getExecutionId ();
 420  0
             out = out + "/" + execution.getExecutionId ();
 421  
 
 422  
             // FIXME: Will this actually work - what happens if the execution is the single one & it has an ID?
 423  
             // compileCommand.librariesOutputDirectory = new File( libsOut );
 424  
             // compileCommand.objectsOutputDirectory = new File( out );
 425  
 
 426  
         }
 427  
 
 428  0
         getLog ().debug ( "Setting library out to " + compileCommand.librariesOutputDirectory.getAbsolutePath () );
 429  0
         getLog ().debug ( "Setting out to " + compileCommand.objectsOutputDirectory.getAbsolutePath () );
 430  
 
 431  0
     }
 432  
 
 433  0
     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  0
             return nativeLibraryDepedencies;
 444  
         }
 445  
 
 446  
         public String[] getResolvedArchitectures ()
 447  
         {
 448  0
             return resolvedArchitectures;
 449  
         }
 450  
     }
 451  
 
 452  
     private void compile ( CompileCommand compileCommand ) throws MojoExecutionException
 453  
     {
 454  0
         MakefileHelper.MakefileResponse makefileResponse = null;
 455  
         try
 456  
         {
 457  
             // Start setting up the command line to be executed
 458  0
             final CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor ();
 459  
 
 460  
             // Add an error listener to the build - this allows the build to conditionally fail
 461  
             // depending on a) the output of the build b) whether or not build errors (output on stderr) should be
 462  
             // ignored and c) whether the pattern matches or not
 463  0
             executor.setErrorListener ( getNdkErrorListener () );
 464  
 
 465  0
             final Set<Artifact> nativeLibraryArtifacts = compileCommand.getNativeLibraryDepedencies ();
 466  
             // If there are any static libraries the code needs to link to, include those in the make file
 467  0
             final Set<Artifact> resolvedNativeLibraryArtifacts = getArtifactResolverHelper ().resolveArtifacts ( nativeLibraryArtifacts );
 468  
 
 469  0
             getLog ().debug ( "resolveArtifacts found " + resolvedNativeLibraryArtifacts.size () + ": " + resolvedNativeLibraryArtifacts.toString () );
 470  
 
 471  0
             final File buildFolder = new File ( buildDirectory, "makefile" );
 472  0
             buildFolder.mkdirs ();
 473  
 
 474  0
             final File androidMavenMakefile = new File ( buildFolder, "android_maven_plugin_makefile.mk" );
 475  0
             final MakefileHelper makefileHelper = new MakefileHelper ( project, getLog (), getArtifactResolverHelper (), harArtifactHandler, unpackedLibsFolder,
 476  
                     buildDirectory );
 477  
 
 478  0
             MakefileHelper.MakefileRequest makefileRequest = new MakefileHelper.MakefileRequest ();
 479  0
             makefileRequest.artifacts = resolvedNativeLibraryArtifacts;
 480  0
             makefileRequest.defaultNDKArchitecture = "armeabi";
 481  0
             makefileRequest.useHeaderArchives = useHeaderArchives;
 482  0
             makefileRequest.ignoreHeaderFilesArchives = ignoreHeaderFilesArchives;
 483  0
             makefileRequest.leaveTemporaryBuildArtifacts = leaveTemporaryBuildArtifacts;
 484  0
             makefileRequest.architectures = compileCommand.getResolvedArchitectures ();
 485  
 
 486  0
             makefileResponse = makefileHelper.createMakefileFromArtifacts ( makefileRequest );
 487  
 
 488  0
             final FileOutputStream output = new FileOutputStream ( androidMavenMakefile );
 489  
             try
 490  
             {
 491  0
                 IOUtil.copy ( makefileResponse.getMakeFile (), output );
 492  
             }
 493  
             finally
 494  
             {
 495  0
                 output.close ();
 496  0
             }
 497  
 
 498  
             // Add the path to the generated makefile - this is picked up by the build (by an include from the user)
 499  0
             executor.addEnvironment ( "ANDROID_MAVEN_PLUGIN_MAKEFILE", androidMavenMakefile.getAbsolutePath () );
 500  
 
 501  0
             setupNativeLibraryEnvironment ( executor, makefileResponse );
 502  
 
 503  
             // Adds the location of the Makefile capturer file - this file will after the build include
 504  
             // things like header files, flags etc.  It is processed after the build to retrieve the headers
 505  
             // and also capture flags etc ...
 506  0
             final File makefileCaptureFile = File.createTempFile ( "android_maven_plugin_makefile_captures", ".tmp", buildDirectory );
 507  
 
 508  0
             if ( !leaveTemporaryBuildArtifacts )
 509  
             {
 510  0
                 makefileCaptureFile.deleteOnExit ();
 511  
             }
 512  
 
 513  0
             executor.addEnvironment ( MakefileHelper.MAKEFILE_CAPTURE_FILE, makefileCaptureFile.getAbsolutePath () );
 514  
 
 515  
             // Add any defined system properties
 516  0
             if ( systemProperties != null && !systemProperties.isEmpty() )
 517  
             {
 518  0
                 for ( Map.Entry<String, String> entry : systemProperties.entrySet() )
 519  
                 {
 520  0
                     executor.addEnvironment( entry.getKey(), entry.getValue() );
 521  0
                 }
 522  
             }
 523  
 
 524  0
             executor.setLogger( this.getLog() );
 525  
 
 526  
             // Setup the command line for the make
 527  0
             final List<String> commands = new ArrayList<String>();
 528  
 
 529  0
             configureArchitectures( commands, compileCommand.getResolvedArchitectures () );
 530  
 
 531  0
             configureBuildDirectory( compileCommand, commands );
 532  
 
 533  0
             configureMakefile( commands );
 534  
 
 535  0
             configureApplicationMakefile( commands );
 536  
 
 537  0
             configureMaxJobs( commands );
 538  
 
 539  
             // Only allow configuration of the toolchain if the architecture being built is a single one!
 540  0
             if ( compileCommand.getResolvedArchitectures ().length == 1 )
 541  
             {
 542  0
                 configureNdkToolchain ( compileCommand.getResolvedArchitectures ()[0], commands );
 543  
             }
 544  
 
 545  0
             configureAdditionalCommands( commands );
 546  
 
 547  
             // If a build target is specified, tag that onto the command line as the very last of the parameters
 548  0
             commands.add ( target != null ? target : "all" );
 549  
 
 550  0
             final String ndkBuildPath = resolveNdkBuildExecutable ();
 551  0
             getLog ().debug ( ndkBuildPath + " " + commands.toString () );
 552  0
             getLog ().info ( "Executing NDK make at : " + buildDirectory );
 553  
 
 554  0
             executor.setCaptureStdOut ( true );
 555  0
             executor.executeCommand ( ndkBuildPath, commands, buildDirectory, true );
 556  0
             getLog ().debug ( "Executed NDK  make at : " + buildDirectory );
 557  
 
 558  0
             if ( attachLibrariesArtifacts )
 559  
             {
 560  
                 // Attempt to attach the native libraries (shared only)
 561  0
                 for ( int i = 0; i < compileCommand.getResolvedArchitectures ().length; i++ )
 562  
                 {
 563  0
                     String architecture = compileCommand.getResolvedArchitectures ()[ i ];
 564  0
                     processCompiledArtifacts ( compileCommand, architecture, makefileCaptureFile );
 565  
                 }
 566  
             }
 567  
             else
 568  
             {
 569  0
                 getLog ().info ( "Will skip attaching compiled libraries as per configuration" );
 570  
             }
 571  
 
 572  
         }
 573  0
         catch ( Exception e )
 574  
         {
 575  0
             throw new MojoExecutionException ( "Failure during build: " + e.getMessage (), e );
 576  
         }
 577  
         finally
 578  
         {
 579  0
             cleanupAfterBuild( makefileResponse );
 580  0
         }
 581  
 
 582  0
     }
 583  
 
 584  
     private void configureArchitectures ( final List<String> commands, final String[] resolvedArchitectures )
 585  
     {
 586  0
         StringBuilder sb = new StringBuilder ( );
 587  
 
 588  0
         for ( int i = 0; i < resolvedArchitectures.length; i++ )
 589  
         {
 590  0
             sb.append ( resolvedArchitectures[i] );
 591  0
             if ( ( i + 1 ) < resolvedArchitectures.length )
 592  
             {
 593  0
                 sb.append ( " " );
 594  
             }
 595  
         }
 596  
 
 597  
         // We always for the APP_ABI onto the command line
 598  0
         commands.add ( "APP_ABI=" + sb.toString () );
 599  
 
 600  0
     }
 601  
 
 602  
 
 603  
     private void configureBuildDirectory( final CompileCommand compileCommand, final List<String> commands )
 604  
     {
 605  
         // Setup the build directory (defaults to the current directory) but may be different depending
 606  
         // on user configuration
 607  0
         commands.add( "-C" );
 608  0
         commands.add( workingDirectory.getAbsolutePath() );
 609  
 
 610  
         // Next, configure the output directories
 611  0
         commands.add( "NDK_LIBS_OUT=" + compileCommand.librariesOutputDirectory.getAbsolutePath () );
 612  0
         commands.add( "NDK_OUT=" + compileCommand.objectsOutputDirectory.getAbsolutePath ()  );
 613  
 
 614  0
     }
 615  
 
 616  
     private void configureMakefile( final List<String> commands ) throws MojoExecutionException
 617  
     {
 618  
         // If the build should use a custom makefile or not - some validation is done to ensure
 619  
         // this exists and all
 620  0
         if ( makefile != null )
 621  
         {
 622  0
             File makeFile = new File( project.getBasedir(), makefile );
 623  0
             if ( !makeFile.exists() )
 624  
             {
 625  0
                 getLog().error( "Specified makefile " + makeFile + " does not exist" );
 626  0
                 throw new MojoExecutionException( "Specified makefile " + makeFile + " does not exist" );
 627  
             }
 628  0
             commands.add( "APP_BUILD_SCRIPT=" + makefile );
 629  
         }
 630  0
     }
 631  
 
 632  
     private void cleanupAfterBuild ( final MakefileHelper.MakefileResponse makefileResponse )
 633  
     {
 634  
         // directories after we're done
 635  0
         if ( makefileResponse != null )
 636  
         {
 637  0
             getLog().info( "Cleaning up extracted include directories used for build" );
 638  0
             MakefileHelper.cleanupAfterBuild( makefileResponse );
 639  
         }
 640  0
     }
 641  
 
 642  
     private void configureAdditionalCommands( final List<String> commands )
 643  
     {
 644  
         // Anything else on the command line the user wants to add - simply splice it up and
 645  
         // add it one by one to the command line
 646  0
         if ( additionalCommandline != null )
 647  
         {
 648  0
             final String[] additionalCommands = additionalCommandline.split( " " );
 649  0
             commands.addAll( Arrays.asList( additionalCommands ) );
 650  
         }
 651  0
     }
 652  
 
 653  
     private void configureApplicationMakefile( List<String> commands )
 654  
             throws MojoExecutionException
 655  
     {
 656  0
         if ( applicationMakefile != null )
 657  
         {
 658  0
             File appMK = new File( project.getBasedir(), applicationMakefile );
 659  0
             if ( !appMK.exists() )
 660  
             {
 661  0
                 getLog().error( "Specified application makefile " + appMK + " does not exist" );
 662  0
                 throw new MojoExecutionException( "Specified application makefile " + appMK + " does not exist" );
 663  
             }
 664  0
             commands.add( "NDK_APPLICATION_MK=" + applicationMakefile );
 665  
         }
 666  0
     }
 667  
 
 668  
     private void configureMaxJobs( List<String> commands )
 669  
     {
 670  0
         if ( maxJobs )
 671  
         {
 672  0
             String jobs = String.valueOf( Runtime.getRuntime().availableProcessors() );
 673  0
             getLog().info( "executing " + jobs + " parallel jobs" );
 674  0
             commands.add( "-j" );
 675  0
             commands.add( jobs );
 676  
         }
 677  0
     }
 678  
 
 679  
     private void configureNdkToolchain( String architecture, List<String> commands )
 680  
             throws MojoExecutionException
 681  
     {
 682  0
         if ( ndkToolchain != null )
 683  
         {
 684  
             // Setup the correct toolchain to use
 685  
             // FIXME: perform a validation that this toolchain exists in the NDK and is valid for the specified
 686  
             // FIXME: architecture!
 687  0
             commands.add( "NDK_TOOLCHAIN=" + ndkToolchain );
 688  0
             commands.add( "APP_ABI=" + architecture );
 689  
         }
 690  
         else
 691  
         {
 692  
             // Resolve the toolchain from the architecture
 693  
             // <architectures>
 694  
             //   <x86>x86-4.6</x86>
 695  
             //   <armeabi>x86-4.6</armeabi>
 696  
             // </architectures>
 697  0
             final String toolchainFromArchitecture = getAndroidNdk().getToolchainFromArchitecture( architecture, architectureToolchainMappings );
 698  0
             getLog().debug( "Resolved toolchain for " + architecture + " to " + toolchainFromArchitecture );
 699  0
             commands.add( "NDK_TOOLCHAIN=" + toolchainFromArchitecture );
 700  0
             commands.add( "APP_ABI=" + architecture );
 701  
         }
 702  0
     }
 703  
 
 704  
     /**
 705  
      * Attaches native libs to project.
 706  
      */
 707  
     private void processCompiledArtifacts ( final CompileCommand compileCommand, String architecture, final File makefileCaptureFile ) throws IOException, MojoExecutionException
 708  
     {
 709  
         // Where the NDK build creates the libs.
 710  0
         final File nativeLibraryDirectory = new File( compileCommand.librariesOutputDirectory, architecture );
 711  
 
 712  
         // Where the NDK build creates the object files - static files end up here
 713  0
         final File nativeObjDirectory = new File( new File( compileCommand.objectsOutputDirectory, "local" ), architecture );
 714  
 
 715  0
         final List<String> classifiers = new ArrayList<String>();
 716  0
         if ( allowMultiArtifacts )
 717  
         {
 718  0
             attachManyArtifacts( nativeLibraryDirectory, architecture, nativeObjDirectory, classifiers );
 719  
         }
 720  
         else
 721  
         {
 722  0
             attachOneArtifact( nativeLibraryDirectory, architecture, nativeObjDirectory, classifiers );
 723  
         }
 724  
 
 725  0
         if ( additionallyBuiltModules != null && !additionallyBuiltModules.isEmpty() )
 726  
         {
 727  0
             for ( AdditionallyBuiltModule additionallyBuiltModule : additionallyBuiltModules )
 728  
             {
 729  0
                 File additionalBuiltModuleFile = nativeLibraryFromName( true, nativeLibraryDirectory, additionallyBuiltModule.getName() );
 730  
 
 731  
                 // If it doesnt exist, check the object directory
 732  0
                 if ( !additionalBuiltModuleFile.exists() )
 733  
                 {
 734  0
                     additionalBuiltModuleFile = nativeLibraryFromName( true, nativeObjDirectory, additionallyBuiltModule.getName() );
 735  
                 }
 736  
 
 737  
                 // FIMXE: This should be validated
 738  0
                 final String additionallyBuiltArtifactType = resolveArtifactType( additionalBuiltModuleFile );
 739  
 
 740  0
                 String additionallyBuiltClassifier = architecture + "-" + additionallyBuiltModule.getClassifier();
 741  0
                 projectHelper.attachArtifact( this.project, additionallyBuiltArtifactType, additionallyBuiltClassifier, additionalBuiltModuleFile );
 742  0
                 classifiers.add( additionallyBuiltClassifier );
 743  0
             }
 744  
         }
 745  
 
 746  
         // Process conditionally any of the headers to include into the header archive file
 747  0
         if ( attachHeaderFiles )
 748  
         {
 749  0
             attachHeaderFiles( compileCommand, makefileCaptureFile, classifiers );
 750  
         }
 751  
 
 752  
 
 753  0
     }
 754  
 
 755  
     private void attachManyArtifacts( File nativeLibraryDirectory, String architecture, File nativeObjDirectory, List<String> classifiers ) throws MojoExecutionException
 756  
     {
 757  0
         List<File> artifacts = Arrays.asList( findNativeLibrary( nativeLibraryDirectory, nativeObjDirectory ) );
 758  0
         for ( File file : artifacts )
 759  
         {
 760  0
             attachArtifactFile( architecture, classifiers, file );
 761  0
         }
 762  0
     }
 763  
 
 764  
     private void attachOneArtifact( File nativeLibraryDirectory, String architecture, File nativeObjDirectory, List<String> classifiers ) throws MojoExecutionException
 765  
     {
 766  
         final File nativeArtifactFile;
 767  0
         if ( finalLibraryName == null )
 768  
         {
 769  0
             nativeArtifactFile = findNativeLibrary( nativeLibraryDirectory, nativeObjDirectory )[0];
 770  
         }
 771  
         else
 772  
         {
 773  0
             nativeArtifactFile = nativeLibraryFromName( nativeLibraryDirectory, nativeObjDirectory, finalLibraryName );
 774  
         }
 775  
 
 776  0
         attachArtifactFile( architecture, classifiers, nativeArtifactFile );
 777  0
     }
 778  
 
 779  
     private void attachArtifactFile( String architecture, List<String> classifiers, File nativeArtifactFile )
 780  
     {
 781  0
         final String artifactType = resolveArtifactType( nativeArtifactFile );
 782  0
         getLog().debug( "Adding native compiled artifact: " + nativeArtifactFile );
 783  
 
 784  0
         final String actualClassifier = ( classifier == null ) ? architecture : architecture + "-" + classifier;
 785  0
         projectHelper.attachArtifact( this.project, artifactType, actualClassifier, nativeArtifactFile );
 786  0
         classifiers.add( actualClassifier );
 787  0
     }
 788  
 
 789  
     /**
 790  
      * Search the specified directory for native artifacts that match the artifact Id
 791  
      */
 792  
     private File[] findNativeLibrary( File nativeLibDirectory, final File nativeObjDirectory ) throws MojoExecutionException
 793  
     {
 794  0
         getLog().info( "Searching " + nativeLibDirectory + " for built shared library" );
 795  
         // FIXME: Should really just look for shared libraries in here really ....
 796  0
         File[] files = nativeLibDirectory.listFiles( new FilenameFilter()
 797  0
         {
 798  
             public boolean accept( final File dir, final String name )
 799  
             {
 800  0
                 String libraryName = finalLibraryName;
 801  
 
 802  0
                 if ( libraryName == null || libraryName.isEmpty() )
 803  
                 {
 804  0
                     libraryName = project.getArtifactId();
 805  
                 }
 806  
 
 807  
                 // FIXME: The following logic won't work for an APKLIB building a static library
 808  0
                 final String extension = Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE.equals( project.getPackaging() ) ? ".a" : ".so";
 809  0
                 boolean found = name.startsWith( "lib" + libraryName ) && name.endsWith( extension );
 810  0
                 if ( !found )
 811  
                 {
 812  
                     // Issue #14 : Work-around issue where the project is actually called "lib" something
 813  0
                     if ( libraryName.startsWith( "lib" ) )
 814  
                     {
 815  0
                         found = name.startsWith( libraryName ) && name.endsWith( extension );
 816  
                     }
 817  
                 }
 818  0
                 return found;
 819  
             }
 820  
         } );
 821  
 
 822  
         // Check the object output directory as well
 823  
         // FIXME: Should really just look for static libraries in here really ....
 824  
 
 825  0
         if ( files == null || files.length == 0 )
 826  
         {
 827  0
             getLog().info( "Searching " + nativeObjDirectory + " for built static library" );
 828  0
             files = nativeObjDirectory.listFiles( new FilenameFilter()
 829  0
             {
 830  
                 public boolean accept( final File dir, final String name )
 831  
                 {
 832  0
                     String libraryName = finalLibraryName;
 833  
 
 834  0
                     if ( libraryName == null || libraryName.isEmpty() )
 835  
                     {
 836  0
                         libraryName = project.getArtifactId();
 837  
                     }
 838  
 
 839  
                     // FIXME: The following logic won't work for an APKLIB building a static library
 840  0
                     if ( Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE.equals( project.getPackaging() ) )
 841  
                     {
 842  0
                         return name.startsWith( "lib" + libraryName ) && name.endsWith( ".a" );
 843  
                     }
 844  
                     else
 845  
                     {
 846  0
                         return name.startsWith( "lib" + libraryName ) && name.endsWith( ".so" );
 847  
                     }
 848  
                 }
 849  
             } );
 850  
 
 851  
         }
 852  
 
 853  
         // slight limitation at this stage - we only handle a single .so artifact
 854  0
         if ( ( files == null || files.length != 1 )  && !allowMultiArtifacts )
 855  
         {
 856  0
             getLog().warn( "Error while detecting native compile artifacts: " + ( files == null || files.length == 0 ? "None found" : "Found more than 1 artifact" ) );
 857  0
             if ( target != null )
 858  
             {
 859  0
                 getLog().warn( "Using the 'target' configuration option to specify the output file name is no longer supported, use 'finalLibraryName' instead." );
 860  
             }
 861  
 
 862  0
             if ( files != null && files.length > 1 )
 863  
             {
 864  0
                 getLog().debug( "List of files found: " + Arrays.asList( files ) );
 865  0
                 getLog().error( "Currently, only a single, final native library is supported by the build" );
 866  0
                 throw new MojoExecutionException( "Currently, only a single, final native library is supported by the build" );
 867  
             }
 868  
             else
 869  
             {
 870  0
                 getLog().error( "No native compiled library found, did the native compile complete successfully?" );
 871  0
                 throw new MojoExecutionException( "No native compiled library found, did the native compile complete successfully?" );
 872  
             }
 873  
         }
 874  0
         return files;
 875  
     }
 876  
 
 877  
     private File nativeLibraryFromName( File nativeLibDirectory, final File nativeObjDirectory, final String libraryName ) throws MojoExecutionException
 878  
     {
 879  
         try
 880  
         {
 881  0
             return nativeLibraryFromName( false, nativeLibDirectory, libraryName );
 882  
         }
 883  0
         catch ( MojoExecutionException e )
 884  
         {
 885  
             // Try the obj directory
 886  0
             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  
         // Find the nativeArtifactFile in the nativeLibDirectory/finalLibraryName
 894  0
         if ( Const.ArtifactType.NATIVE_SYMBOL_OBJECT.equals( project.getPackaging() ) || Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE.equals( project.getPackaging() ) )
 895  
         {
 896  0
             libraryFile = new File( directory, "lib" + libraryName + "." + project.getPackaging() );
 897  
         }
 898  
         else
 899  
         {
 900  0
             final File staticLib = new File( directory, "lib" + libraryName + ".a" );
 901  0
             if ( staticLib.exists() )
 902  
             {
 903  0
                 libraryFile = staticLib;
 904  
             }
 905  
             else
 906  
             {
 907  0
                 libraryFile = new File( directory, "lib" + libraryName + ".so" );
 908  
             }
 909  
         }
 910  0
         if ( !libraryFile.exists() )
 911  
         {
 912  0
             if ( logErrors )
 913  
             {
 914  0
                 getLog().error( "Could not locate final native library using the provided finalLibraryName " + libraryName + " (tried " + libraryFile.getAbsolutePath() + ")" );
 915  
             }
 916  0
             throw new MojoExecutionException( "Could not locate final native library using the provided finalLibraryName " + libraryName + " (tried " + libraryFile.getAbsolutePath() + ")" );
 917  
         }
 918  
 
 919  0
         return libraryFile;
 920  
     }
 921  
 
 922  
 
 923  
     private CommandExecutor.ErrorListener getNdkErrorListener()
 924  
     {
 925  0
         return new CommandExecutor.ErrorListener()
 926  0
         {
 927  
             @Override
 928  
             public boolean isError( String error )
 929  
             {
 930  
 
 931  
                 // Unconditionally ignore *All* build warning if configured to
 932  0
                 if ( ignoreBuildWarnings )
 933  
                 {
 934  0
                     return false;
 935  
                 }
 936  
 
 937  0
                 final Pattern pattern = Pattern.compile( buildWarningsRegularExpression );
 938  0
                 final Matcher matcher = pattern.matcher( error );
 939  
 
 940  
                 // If the the reg.exp actually matches, we can safely say this is not an error
 941  
                 // since in theory the user told us so
 942  0
                 if ( matcher.matches() )
 943  
                 {
 944  0
                     return false;
 945  
                 }
 946  
 
 947  
                 // Otherwise, it is just another error
 948  0
                 return true;
 949  
             }
 950  
         };
 951  
     }
 952  
 
 953  
     /**
 954  
      * Validate the makefile - if our packaging type is so (for example) and there are
 955  
      * dependencies on .a files (or shared files for that matter) the makefile should include
 956  
      * the include of our Android Maven plugin generated makefile.
 957  
      */
 958  
     private void validateMakefile( MavenProject project, String file )
 959  
     {
 960  
         // TODO: actually perform validation
 961  0
     }
 962  
 
 963  
     private String resolveNdkBuildExecutable() throws MojoExecutionException
 964  
     {
 965  0
         if ( ndkBuildExecutable != null )
 966  
         {
 967  0
             getLog().debug( "ndk-build overriden, using " + ndkBuildExecutable );
 968  0
             return ndkBuildExecutable;
 969  
         }
 970  0
         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  0
         final List<HeaderFilesDirective> finalHeaderFilesDirectives = new ArrayList<HeaderFilesDirective>();
 977  
 
 978  0
         if ( useLocalSrcIncludePaths )
 979  
         {
 980  0
             Properties props = new Properties();
 981  0
             props.load( new FileInputStream( localCIncludesFile ) );
 982  0
             String localCIncludes = props.getProperty( "LOCAL_C_INCLUDES" );
 983  0
             if ( localCIncludes != null && !localCIncludes.trim().isEmpty() )
 984  
             {
 985  0
                 String[] includes = localCIncludes.split( " " );
 986  0
                 for ( String include : includes )
 987  
                 {
 988  0
                     final HeaderFilesDirective headerFilesDirective = new HeaderFilesDirective();
 989  0
                     File includeDir = new File( project.getBasedir(), include );
 990  0
                     headerFilesDirective.setDirectory( includeDir.getAbsolutePath() );
 991  0
                     headerFilesDirective.setIncludes( new String[]{ "**/*.h", "**/*.hpp" } );
 992  0
                     finalHeaderFilesDirectives.add( headerFilesDirective );
 993  
                 }
 994  
             }
 995  0
         }
 996  
         else
 997  
         {
 998  0
             if ( headerFilesDirectives != null )
 999  
             {
 1000  0
                 finalHeaderFilesDirectives.addAll( headerFilesDirectives );
 1001  
             }
 1002  
         }
 1003  0
         if ( finalHeaderFilesDirectives.isEmpty() )
 1004  
         {
 1005  0
             getLog().debug( "No header files included, will add default set" );
 1006  0
             final HeaderFilesDirective e = new HeaderFilesDirective();
 1007  0
             final File folder = new File( project.getBasedir() + "/jni" );
 1008  0
             if ( folder.exists() )
 1009  
             {
 1010  0
                 e.setDirectory( folder.getAbsolutePath() );
 1011  0
                 e.setIncludes( new String[] { "**/*.h", "**/*.hpp" } );
 1012  0
                 finalHeaderFilesDirectives.add( e );
 1013  
             }
 1014  
         }
 1015  0
         createHeaderArchive( compileCommand, finalHeaderFilesDirectives, classifiers );
 1016  0
     }
 1017  
 
 1018  
     private void createHeaderArchive ( final CompileCommand compileCommand, final List<HeaderFilesDirective> finalHeaderFilesDirectives, final List<String> classifiers ) throws MojoExecutionException
 1019  
     {
 1020  
         try
 1021  
         {
 1022  0
             MavenArchiver mavenArchiver = new MavenArchiver();
 1023  0
             mavenArchiver.setArchiver( jarArchiver );
 1024  
 
 1025  0
             final File jarFile = File.createTempFile( "tmp", ".har", buildDirectory );
 1026  
 
 1027  0
             mavenArchiver.setOutputFile( jarFile );
 1028  
 
 1029  0
             for ( HeaderFilesDirective headerFilesDirective : finalHeaderFilesDirectives )
 1030  
             {
 1031  0
                 mavenArchiver.getArchiver().addDirectory( new File( headerFilesDirective.getDirectory() ), headerFilesDirective.getIncludes(), headerFilesDirective.getExcludes() );
 1032  0
             }
 1033  
 
 1034  0
             final MavenArchiveConfiguration mavenArchiveConfiguration = new MavenArchiveConfiguration();
 1035  0
             mavenArchiveConfiguration.setAddMavenDescriptor( false );
 1036  
 
 1037  0
             mavenArchiver.createArchive( project, mavenArchiveConfiguration );
 1038  
 
 1039  0
             for ( String classifier : classifiers )
 1040  
             {
 1041  0
                 getLog().debug( "Attaching 'har' classifier=" + classifier + " file=" + jarFile );
 1042  0
                 projectHelper.attachArtifact( project, Const.ArtifactType.NATIVE_HEADER_ARCHIVE, classifier, jarFile );
 1043  0
             }
 1044  
 
 1045  
         }
 1046  0
         catch ( Exception e )
 1047  
         {
 1048  0
             throw new MojoExecutionException( e.getMessage() );
 1049  0
         }
 1050  0
     }
 1051  
 
 1052  
     private void setupNativeLibraryEnvironment ( final CommandExecutor executor,
 1053  
                                                  final MakefileHelper.MakefileResponse makefileResponse )
 1054  
     {
 1055  0
         if ( makefileResponse.hasStaticLibraryDepdendencies() )
 1056  
         {
 1057  0
             String staticlibs = makefileResponse.getStaticLibraryList();
 1058  0
             executor.addEnvironment( "ANDROID_MAVEN_PLUGIN_LOCAL_STATIC_LIBRARIES", staticlibs );
 1059  0
             getLog().debug( "Set ANDROID_MAVEN_PLUGIN_LOCAL_STATIC_LIBRARIES = " + staticlibs );
 1060  
         }
 1061  
 
 1062  0
         if ( makefileResponse.hasSharedLibraryDepdendencies() )
 1063  
         {
 1064  0
             String staticlibs = makefileResponse.getSharedLibraryList();
 1065  0
             executor.addEnvironment( "ANDROID_MAVEN_PLUGIN_LOCAL_SHARED_LIBRARIES", staticlibs );
 1066  0
             getLog().debug( "Set ANDROID_MAVEN_PLUGIN_LOCAL_SHARED_LIBRARIES = " + staticlibs );
 1067  
         }
 1068  0
     }
 1069  
 
 1070  
     private Set<Artifact> findNativeLibraryDependencies() throws MojoExecutionException
 1071  
     {
 1072  0
         final NativeHelper nativeHelper = getNativeHelper();
 1073  0
         final Set<Artifact> staticLibraryArtifacts = nativeHelper.getNativeDependenciesArtifacts( false );
 1074  0
         final Set<Artifact> sharedLibraryArtifacts = nativeHelper.getNativeDependenciesArtifacts( true );
 1075  
 
 1076  0
         final Set<Artifact> mergedArtifacts = new LinkedHashSet<Artifact>();
 1077  0
         filterNativeDependencies( mergedArtifacts, staticLibraryArtifacts );
 1078  0
         filterNativeDependencies( mergedArtifacts, sharedLibraryArtifacts );
 1079  
 
 1080  0
         getLog().debug( "findNativeLibraryDependencies found " + mergedArtifacts.size() + ": " + mergedArtifacts.toString() );
 1081  
 
 1082  0
         return mergedArtifacts;
 1083  
     }
 1084  
 
 1085  
     /**
 1086  
      * Selectively add artifacts from source to target excluding any whose groupId and artifactId match
 1087  
      * the current build.
 1088  
      * <p/>
 1089  
      * Introduced to work around an issue when the ndk-build is executed twice by maven for example when
 1090  
      * invoking maven 'install site'. In this case the artifacts attached by the first invocation are
 1091  
      * found but are not valid dependencies and must be excluded.
 1092  
      *
 1093  
      * @param targetSet artifact Set to copy in to
 1094  
      * @param source    artifact Set to filter
 1095  
      */
 1096  
     private void filterNativeDependencies( Set<Artifact> targetSet, Set<Artifact> source )
 1097  
     {
 1098  0
         for ( Artifact a : source )
 1099  
         {
 1100  0
             if ( project.getGroupId().equals( a.getGroupId() ) && project.getArtifactId().equals( a.getArtifactId() ) )
 1101  
             {
 1102  0
                 getLog().warn( "Excluding native dependency attached by this build" );
 1103  
             }
 1104  
             else
 1105  
             {
 1106  0
                 targetSet.add( a );
 1107  
             }
 1108  0
         }
 1109  0
     }
 1110  
 
 1111  
     /**
 1112  
      * Resolve the artifact type from the current project and the specified file.  If the project packaging is
 1113  
      * either 'a' or 'so' it will use the packaging, otherwise it checks the file for the extension
 1114  
      *
 1115  
      * @param file The file being added as an artifact
 1116  
      * @return The artifact type (so or a)
 1117  
      */
 1118  
     private String resolveArtifactType( File file )
 1119  
     {
 1120  0
         if ( Const.ArtifactType.NATIVE_SYMBOL_OBJECT.equals( project.getPackaging() ) || Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE.equals( project.getPackaging() ) )
 1121  
         {
 1122  0
             return project.getPackaging();
 1123  
         }
 1124  
         else
 1125  
         {
 1126  
             // At this point, the file (as found by our filtering previously will end with either 'so' or 'a'
 1127  0
             return file.getName().endsWith( Const.ArtifactType.NATIVE_SYMBOL_OBJECT ) ? Const.ArtifactType.NATIVE_SYMBOL_OBJECT : Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE;
 1128  
         }
 1129  
     }
 1130  
 
 1131  
     /**
 1132  
      * <p>Returns the Android NDK to use.</p>
 1133  
      * <p/>
 1134  
      * <p>Current implementation looks for <code>&lt;ndk&gt;&lt;path&gt;</code> configuration in pom, then System
 1135  
      * property <code>android.ndk.path</code>, then environment variable <code>ANDROID_NDK_HOME</code>.
 1136  
      * <p/>
 1137  
      * <p>This is where we collect all logic for how to lookup where it is, and which one to choose. The lookup is
 1138  
      * based on available parameters. This method should be the only one you should need to look at to understand how
 1139  
      * the Android NDK is chosen, and from where on disk.</p>
 1140  
      *
 1141  
      * @return the Android NDK to use.
 1142  
      * @throws org.apache.maven.plugin.MojoExecutionException if no Android NDK path configuration is available at all.
 1143  
      */
 1144  
     protected AndroidNdk getAndroidNdk() throws MojoExecutionException
 1145  
     {
 1146  
         final File chosenNdkPath;
 1147  
 
 1148  0
         if ( ndkPath != null )
 1149  
         {
 1150  
             // -Dandroid.ndk.path is set on command line, or via <properties><ndk.path>...
 1151  0
             chosenNdkPath = ndkPath;
 1152  
         }
 1153  
         else
 1154  
         {
 1155  
             // No -Dandroid.ndk.path is set on command line, or via <properties><ndk.path>...
 1156  0
             chosenNdkPath = new File( getAndroidNdkHomeOrThrow() );
 1157  
         }
 1158  
 
 1159  0
         return new AndroidNdk( chosenNdkPath );
 1160  
     }
 1161  
 
 1162  
 
 1163  
     /**
 1164  
      * @return
 1165  
      * @throws MojoExecutionException
 1166  
      */
 1167  
     private String getAndroidNdkHomeOrThrow() throws MojoExecutionException
 1168  
     {
 1169  0
         final String androidHome = System.getenv( ENV_ANDROID_NDK_HOME );
 1170  0
         if ( StringUtils.isBlank( androidHome ) )
 1171  
         {
 1172  0
             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  0
         return androidHome;
 1177  
     }
 1178  
 
 1179  
     protected final ArtifactResolverHelper getArtifactResolverHelper()
 1180  
     {
 1181  0
         if ( artifactResolverHelper == null )
 1182  
         {
 1183  0
             artifactResolverHelper = new ArtifactResolverHelper( artifactResolver, new MavenToPlexusLogAdapter( getLog() ), project.getRemoteArtifactRepositories() );
 1184  
         }
 1185  0
         return artifactResolverHelper;
 1186  
     }
 1187  
 
 1188  
     protected final NativeHelper getNativeHelper()
 1189  
     {
 1190  0
         if ( nativeHelper == null )
 1191  
         {
 1192  0
             nativeHelper = new NativeHelper( project, dependencyGraphBuilder, getLog() );
 1193  
         }
 1194  0
         return nativeHelper;
 1195  
     }
 1196  
 
 1197  
 
 1198  
 }