All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jayway.maven.plugins.android.phase05compile.NdkBuildMojo Maven / Gradle / Ivy

There is a newer version: 4.6.0
Show newest version
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jayway.maven.plugins.android.phase05compile;

import com.jayway.maven.plugins.android.AbstractAndroidMojo;
import com.jayway.maven.plugins.android.CommandExecutor;
import com.jayway.maven.plugins.android.ExecutionException;
import com.jayway.maven.plugins.android.common.Const;
import com.jayway.maven.plugins.android.common.NativeHelper;
import com.jayway.maven.plugins.android.config.PullParameter;
import com.jayway.maven.plugins.android.configuration.HeaderFilesDirective;
import com.jayway.maven.plugins.android.configuration.NDKArchitectureToolchainMappings;
import org.apache.commons.io.FileUtils;
import org.apache.maven.archiver.MavenArchiveConfiguration;
import org.apache.maven.archiver.MavenArchiver;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.archiver.jar.JarArchiver;
import org.codehaus.plexus.util.IOUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Johan Lindquist 
 */
@Mojo( name = "ndk-build", defaultPhase = LifecyclePhase.COMPILE )
public class NdkBuildMojo extends AbstractAndroidMojo
{
    /**
     * Allows for overriding the default ndk-build executable.
     */
    @Parameter( property = "android.ndk.ndk-build-executable" )
    @PullParameter
    private String ndkBuildExecutable;

    /**
     * Folder in which the Ndk makefiles are constructed and the build is executed.
     */
    @Parameter(
            property = "android.ndk.ndk-build-directory",
            defaultValue = "${project.build.directory}/ndk-build",
            readonly = true
    )
    @PullParameter
    private File ndkBuildDirectory;

    /**
     * Specifies the classifier with which the artifact should be stored in the repository
     */
    @Parameter( property = "android.ndk.build.native-classifier" )
    @PullParameter
    private String ndkClassifier;

    /**
     * Specifies additional command line parameters to pass to ndk-build
     */
    @Parameter( property = "android.ndk.build.command-line" )
    @PullParameter
    protected String ndkBuildAdditionalCommandline;

    /**
     * Flag indicating whether the resulting native library should be attached as an artifact to the build.  This
     * means the resulting .so is installed into the repository as well as being included in the final APK.
     */
    @Parameter( property = "android.ndk.build.attach-native-artifact", defaultValue = "false" )
    @PullParameter( defaultValue = "false" )
    private Boolean attachNativeArtifacts;

    /**
     * 

Folder containing native, static libraries compiled and linked by the NDK.

* * The NDK build executable seems determined to create the native libs in the root folder. * TODO work out how to create them in /target. */ @Parameter( property = "android.nativeLibrariesOutputDirectory", defaultValue = "${project.basedir}/obj/local", readonly = true ) private File nativeLibrariesOutputDirectory; /** *

Target to invoke on the native makefile.

*/ @Parameter( property = "android.nativeTarget" ) @PullParameter private String target; /** * Defines the architecture for the NDK build * * @deprecated Use {@link NdkBuildMojo#ndkArchitectures} instead */ @Parameter( property = "android.ndk.build.architecture" ) @PullParameter private String ndkArchitecture; /** * Defines the architectures for the NDK build - this is a space separated list (i.e x86 armeabi) */ @Parameter( property = "android.ndk.build.architectures" ) @PullParameter private String ndkArchitectures; /** * Defines the architecture to toolchain mappings for the NDK build * <ndkArchitectureToolchainMappings> * <x86>x86-4.7</x86> * <armeabi>arm-linux-androideabi-4.7</armeabi> * </ndkArchitectureToolchainMappings> */ @Parameter @PullParameter private NDKArchitectureToolchainMappings ndkArchitectureToolchainMappings; /** * Flag indicating whether the header files used in the build should be included and attached to the build as * an additional artifact. */ @Parameter( property = "android.ndk.build.attach-header-files", defaultValue = "true" ) @PullParameter( defaultValue = "true" ) private Boolean attachHeaderFiles; /** * Flag indicating whether the make files last LOCAL_SRC_INCLUDES should be used for determing what header * files to include. Setting this flag to true, overrides any defined header files directives. * Note: By setting this flag to true, all header files used in the project will be * added to the resulting header archive. This may be undesirable in most cases and is therefore turned off by * default. */ @Parameter( property = "android.ndk.build.use-local-src-include-paths", defaultValue = "false" ) @PullParameter( defaultValue = "false" ) private Boolean useLocalSrcIncludePaths; /** * Specifies the set of header files includes/excludes which should be used for bundling the exported header * files. The below shows an example of how this can be used. *

*

     * <headerFilesDirectives>
     *   <headerFilesDirective>
     *     <directory>${basedir}/jni/include</directory>
     *     <includes>
     *       <includes>**\/*.h</include>
     *     </includes>
     *   <headerFilesDirective>
     * </headerFilesDirectives>
     * 
*
* If no headerFilesDirectives is specified, the default includes will be defined as shown below: *
*
     * <headerFilesDirectives>
     *   <headerFilesDirective>
     *     <directory>${basedir}/jni</directory>
     *     <includes>
     *       <includes>**\/*.h</include>
     *     </includes>
     *     <excludes>
     *       <exclude>**\/*.c</exclude>
     *     </excludes>
     *   <headerFilesDirective>
     *   [..]
     * </headerFilesDirectives>
     * 
*/ @Parameter @PullParameter private List headerFilesDirectives; /** * The Jar archiver. */ @Component( role = org.codehaus.plexus.archiver.Archiver.class, hint = "jar" ) private JarArchiver jarArchiver; @Component( role = org.apache.maven.artifact.handler.ArtifactHandler.class, hint = "har" ) private ArtifactHandler harArtifactHandler; /** * Flag indicating whether the header files for native, static library dependencies should be used. If true, * the header archive for each statically linked dependency will be resolved. */ @Parameter( property = "android.ndk.build.use-header-archive", defaultValue = "true" ) @PullParameter( defaultValue = "true" ) private Boolean useHeaderArchives; /** * Defines additional system properties which should be exported to the ndk-build script. This *
*
     * <systemProperties>
     *   <propertyName>propertyValue</propertyName>
     *   <build-target>android</build-target>
     *   [..]
     * </systemProperties>
     * 
* */ @Parameter @PullParameter private Map systemProperties; /** * Flag indicating whether warnings should be ignored while compiling. If true, * the build will not fail if warning are found during compile. */ @Parameter( property = "android.ndk.build.ignore-build-warnings", defaultValue = "true" ) @PullParameter( defaultValue = "true" ) private Boolean ignoreBuildWarnings; /** * Defines the regular expression used to detect whether error/warning output from ndk-build is a minor compile * warning or is actually an error which should cause the build to fail. *

* If the pattern matches, the output from the compiler will not be considered an error and compile * will be successful. */ @Parameter( property = "android.ndk.build.build-warnings-regular-expression", defaultValue = ".*[warning|note]: .*" ) @PullParameter( defaultValue = ".*[warning|note]: .*" ) private String buildWarningsRegularExpression; @Parameter( property = "android.ndk.build.skip-native-library-stripping", defaultValue = "false" ) @PullParameter( defaultValue = "false" ) private Boolean skipStripping; @Parameter( property = "android.ndk.build.ndk-toolchain" ) @PullParameter private String ndkToolchain; /** * Specifies the final name of the library output by the build (this allows * the pom to override the default artifact name). The value should not * include the 'lib' prefix or filename extension (e.g. '.so'). */ @Parameter( property = "android.ndk.build.build.final-library-name" ) @PullParameter private String ndkFinalLibraryName; /** * Specifies the makefile to use for the build (if other than the default Android.mk). */ @Parameter @PullParameter private String makefile; /** * Specifies the application makefile to use for the build (if other than the default Application.mk). */ @Parameter @PullParameter private String applicationMakefile; /** * Flag indicating whether to use the max available jobs for the host machine */ @Parameter( property = "android.ndk.build.maxJobs", defaultValue = "false" ) @PullParameter( defaultValue = "false" ) private Boolean maxJobs; /** * @throws MojoExecutionException * @throws MojoFailureException */ public void execute() throws MojoExecutionException, MojoFailureException { // Validate the NDK final File ndkBuildFile = new File( getAndroidNdk().getNdkBuildPath() ); NativeHelper.validateNDKVersion( ndkBuildFile.getParentFile() ); validateMakefile( project, makefile ); final String[] resolvedNDKArchitectures = NativeHelper.getNdkArchitectures( ndkArchitecture != null ? ndkArchitecture : ndkArchitectures, applicationMakefile, project.getBasedir() ); for ( String architecture : resolvedNDKArchitectures ) { try { compileForArchitecture( architecture ); } catch ( IOException e ) { getLog().error( "Error while executing: " + e.getMessage() ); throw new MojoExecutionException( e.getMessage(), e ); } catch ( ExecutionException e ) { getLog().error( "Error while executing: " + e.getMessage() ); throw new MojoExecutionException( e.getMessage(), e ); } } } private void compileForArchitecture( String architecture ) throws MojoExecutionException, IOException, ExecutionException { getLog().debug( "Resolving for NDK architecture : " + architecture ); // Start setting up the command line to be executed final CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor(); // Add an error listener to the build - this allows the build to conditionally fail // depending on a) the output of the build b) whether or not build errors (output on stderr) should be // ignored and c) whether the pattern matches or not executor.setErrorListener( getNdkErrorListener() ); final Set nativeLibraryArtifacts = findNativeLibraryDependencies(); // If there are any static libraries the code needs to link to, include those in the make file final Set resolveNativeLibraryArtifacts = getArtifactResolverHelper().resolveArtifacts( nativeLibraryArtifacts ); getLog().debug( "resolveArtifacts found " + resolveNativeLibraryArtifacts.size() + ": " + resolveNativeLibraryArtifacts.toString() ); final File buildFolder = new File( ndkBuildDirectory, architecture ); buildFolder.mkdirs(); final File androidMavenMakefile = new File( buildFolder, "android_maven_plugin_makefile.mk" ); final MakefileHelper makefileHelper = new MakefileHelper( getLog(), getArtifactResolverHelper(), harArtifactHandler, getUnpackedLibsDirectory() ); final MakefileHelper.MakefileHolder makefileHolder = makefileHelper .createMakefileFromArtifacts( resolveNativeLibraryArtifacts, architecture, "armeabi", useHeaderArchives ); final FileOutputStream output = new FileOutputStream( androidMavenMakefile ); try { IOUtil.copy( makefileHolder.getMakeFile(), output ); } finally { output.close(); } // Add the path to the generated makefile - this is picked up by the build (by an include from the user) executor.addEnvironment( "ANDROID_MAVEN_PLUGIN_MAKEFILE", androidMavenMakefile.getAbsolutePath() ); setupNativeLibraryEnvironment( makefileHelper, executor, resolveNativeLibraryArtifacts, architecture ); // Adds the location of the Makefile capturer file - this file will after the build include // things like header files, flags etc. It is processed after the build to retrieve the headers // and also capture flags etc ... final File makefileCaptureFile = File.createTempFile( "android_maven_plugin_makefile_captures", ".tmp" ); makefileCaptureFile.deleteOnExit(); executor.addEnvironment( MakefileHelper.MAKEFILE_CAPTURE_FILE, makefileCaptureFile.getAbsolutePath() ); // Add any defined system properties if ( systemProperties != null && ! systemProperties.isEmpty() ) { for ( Map.Entry entry : systemProperties.entrySet() ) { executor.addEnvironment( entry.getKey(), entry.getValue() ); } } executor.setLogger( this.getLog() ); // Setup the command line for the make final List commands = new ArrayList(); // Setup the build directory (defaults to the current directory) but may be different depending // on user configuration commands.add( "-C" ); commands.add( project.getBasedir().getAbsolutePath() ); // If the build should use a custom makefile or not - some validation is done to ensure // this exists and all if ( makefile != null ) { File makeFile = new File( project.getBasedir(), makefile ); if ( ! makeFile.exists() ) { getLog().error( "Specified makefile " + makeFile + " does not exist" ); throw new MojoExecutionException( "Specified makefile " + makeFile + " does not exist" ); } commands.add( "-f" ); commands.add( makefile ); } configureApplicationMakefile( commands ); configureMaxJobs( commands ); configureNdkToolchain( architecture, commands ); configureAdditionalCommands( commands ); // If a build target is specified, tag that onto the command line as the very last of the parameters if ( target != null ) { commands.add( target ); } else /*if ( Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE.equals( project.getPackaging() ) )*/ { commands.add( project.getArtifactId() ); } final String ndkBuildPath = resolveNdkBuildExecutable(); getLog().debug( ndkBuildPath + " " + commands.toString() ); getLog().info( "Executing NDK " + architecture + " make at : " + ndkBuildDirectory ); executor.setCaptureStdOut( true ); executor.executeCommand( ndkBuildPath, commands, ndkBuildDirectory, true ); getLog().debug( "Executed NDK " + architecture + " make at : " + ndkBuildDirectory ); // Where the NDK build creates the libs. final File nativeLibOutputDirectory = new File( nativeLibrariesOutputDirectory, architecture ); nativeLibOutputDirectory.mkdirs(); // Move the built native libs into the packaging folder. // We don't create them there to start with because the NDK build seems determined to create them in the root. final File destinationDirectory = new File( ndkOutputDirectory, architecture ); FileUtils.moveDirectory( nativeLibOutputDirectory, destinationDirectory ); // Attempt to attach the native library if the project is defined as a "pure" native Android library // (packaging is 'so' or 'a') or if the plugin has been configured to attach the native library to the build if ( Const.ArtifactType.NATIVE_SYMBOL_OBJECT.equals( project.getPackaging() ) || Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE.equals( project.getPackaging() ) || attachNativeArtifacts ) { attachNativeLib( destinationDirectory, architecture ); } // Process conditionally any of the headers to include into the header archive file if ( attachHeaderFiles ) { attachHeaderFiles( makefileCaptureFile, architecture ); } // If we created a makefile for the build we should be polite and remove any extracted include // directories after we're done getLog().info( "Cleaning up extracted include directories used for build" ); MakefileHelper.cleanupAfterBuild( makefileHolder ); } private void configureAdditionalCommands( final List commands ) { // Anything else on the command line the user wants to add - simply splice it up and // add it one by one to the command line if ( ndkBuildAdditionalCommandline != null ) { final String[] additionalCommands = ndkBuildAdditionalCommandline.split( " " ); commands.addAll( Arrays.asList( additionalCommands ) ); } } private void configureApplicationMakefile( List commands ) throws MojoExecutionException { if ( applicationMakefile != null ) { File appMK = new File( project.getBasedir(), applicationMakefile ); if ( ! appMK.exists() ) { getLog().error( "Specified application makefile " + appMK + " does not exist" ); throw new MojoExecutionException( "Specified application makefile " + appMK + " does not exist" ); } commands.add( "NDK_APPLICATION_MK=" + applicationMakefile ); } } private void configureMaxJobs( List commands ) { if ( maxJobs ) { String jobs = String.valueOf( Runtime.getRuntime().availableProcessors() ); getLog().info( "executing " + jobs + " parallel jobs" ); commands.add( "-j" ); commands.add( jobs ); } } private void configureNdkToolchain( String architecture, List commands ) throws MojoExecutionException { if ( ndkToolchain != null ) { // Setup the correct toolchain to use // FIXME: perform a validation that this toolchain exists in the NDK commands.add( "NDK_TOOLCHAIN=" + ndkToolchain ); } else { // Resolve the toolchain from the architecture // // x86-4.6 // x86-4.6 // final String toolchainFromArchitecture = getAndroidNdk().getToolchainFromArchitecture( architecture, ndkArchitectureToolchainMappings ); getLog().debug( "Resolved toolchain for " + architecture + " to " + toolchainFromArchitecture ); commands.add( "NDK_TOOLCHAIN=" + toolchainFromArchitecture ); commands.add( "APP_ABI=" + architecture ); } } /** * Attaches native libs to project. */ private void attachNativeLib( File destinationDirectory, String architecture ) throws IOException, MojoExecutionException { final File nativeArtifactFile; if ( ndkFinalLibraryName == null ) { nativeArtifactFile = findNativeLibrary( destinationDirectory ); } else { nativeArtifactFile = nativeLibraryFromName( destinationDirectory ); } final String artifactType = resolveArtifactType( nativeArtifactFile ); if ( nativeArtifactFile.getName().endsWith( ".so" ) && ! skipStripping ) { getLog().debug( "Post processing (stripping) native compiled artifact: " + nativeArtifactFile ); invokeNDKStripper( nativeArtifactFile ); } getLog().debug( "Adding native compiled artifact: " + nativeArtifactFile ); final String classifier = ( ndkClassifier == null ) ? architecture : architecture + "-" + ndkClassifier; projectHelper.attachArtifact( this.project, artifactType, classifier, nativeArtifactFile ); } /** * Search the specified directory for native artifacts that match the artifact Id */ private File findNativeLibrary( File nativeLibDirectory ) throws MojoExecutionException { getLog().info( "Searching " + nativeLibDirectory + " for built library" ); File[] files = nativeLibDirectory.listFiles( new FilenameFilter() { public boolean accept( final File dir, final String name ) { String libraryName = ndkFinalLibraryName; if ( libraryName == null || libraryName.isEmpty() ) { libraryName = project.getArtifactId(); } // FIXME: The following logic won't work for an APKLIB building a static library if ( Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE.equals( project.getPackaging() ) ) { return name.startsWith( "lib" + libraryName ) && name.endsWith( ".a" ); } else { return name.startsWith( "lib" + libraryName ) && name.endsWith( ".so" ); } } } ); // slight limitation at this stage - we only handle a single .so artifact if ( files == null || files.length != 1 ) { getLog().warn( "Error while detecting native compile artifacts: " + ( files == null || files.length == 0 ? "None found" : "Found more than 1 artifact" ) ); if ( target != null ) { getLog().warn( "Using the 'target' configuration option to specify the output file name " + "is no longer supported, use 'ndkFinalLibraryName' instead." ); } if ( files != null && files.length > 1 ) { getLog().debug( "List of files found: " + Arrays.asList( files ) ); getLog().error( "Currently, only a single, final native library is supported by the build" ); throw new MojoExecutionException( "Currently, only a single, final native library is supported by the build" ); } else { getLog().error( "No native compiled library found, did the native compile complete " + "successfully?" ); throw new MojoExecutionException( "No native compiled library found, did the native " + "compile complete successfully?" ); } } return files[ 0 ]; } private File nativeLibraryFromName( File nativeLibDirectory ) throws MojoExecutionException { final File libraryFile; // Find the nativeArtifactFile in the nativeLibDirectory/ndkFinalLibraryName if ( Const.ArtifactType.NATIVE_SYMBOL_OBJECT.equals( project.getPackaging() ) || Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE.equals( project.getPackaging() ) ) { libraryFile = new File( nativeLibDirectory, "lib" + ndkFinalLibraryName + "." + project.getPackaging() ); } else { final File staticLib = new File( nativeLibDirectory, "lib" + ndkFinalLibraryName + ".a" ); if ( staticLib.exists() ) { libraryFile = staticLib; } else { libraryFile = new File( nativeLibDirectory, "lib" + ndkFinalLibraryName + ".so" ); } } if ( ! libraryFile.exists() ) { getLog().error( "Could not locate final native library using the provided ndkFinalLibraryName " + ndkFinalLibraryName + " (tried " + libraryFile.getAbsolutePath() + ")" ); throw new MojoExecutionException( "Could not locate final native library using the provided ndkFinalLibraryName " + ndkFinalLibraryName + " (tried " + libraryFile.getAbsolutePath() + ")" ); } return libraryFile; } private CommandExecutor.ErrorListener getNdkErrorListener() { return new CommandExecutor.ErrorListener() { @Override public boolean isError( String error ) { // Unconditionally ignore *All* build warning if configured to if ( ignoreBuildWarnings ) { return false; } final Pattern pattern = Pattern.compile( buildWarningsRegularExpression ); final Matcher matcher = pattern.matcher( error ); // If the the reg.exp actually matches, we can safely say this is not an error // since in theory the user told us so if ( matcher.matches() ) { return false; } // Otherwise, it is just another error return true; } }; } /** * Validate the makefile - if our packaging type is so (for example) and there are * dependencies on .a files (or shared files for that matter) the makefile should include * the include of our Android Maven plugin generated makefile. */ private void validateMakefile( MavenProject project, String file ) { // TODO: actually perform validation } private void invokeNDKStripper( File file ) throws MojoExecutionException { try { getLog().debug( "Detected shared library artifact, will now strip it" ); // Execute the strip command final CommandExecutor stripCommandExecutor = CommandExecutor.Factory.createDefaultCommmandExecutor(); stripCommandExecutor.setErrorListener( new CommandExecutor.ErrorListener() { public boolean isError( String error ) { getLog().error( "Error while stripping binary: " + error ); return true; } } ); stripCommandExecutor.setLogger( getLog() ); stripCommandExecutor.setCaptureStdOut( true ); stripCommandExecutor.executeCommand( resolveNdkStripper( file ).getAbsolutePath(), Arrays.asList( file.getAbsolutePath() ) ); } catch ( ExecutionException e ) { getLog().error( "Error while attempting to strip shared library", e ); throw new MojoExecutionException( "Error while attempting to strip shared library" ); } } private String resolveNdkBuildExecutable() throws MojoExecutionException { if ( ndkBuildExecutable != null ) { getLog().debug( "ndk-build overriden, using " + ndkBuildExecutable ); return ndkBuildExecutable; } return getAndroidNdk().getNdkBuildPath(); } private File resolveNdkStripper( File nativeLibrary ) throws MojoExecutionException { if ( ndkToolchain != null ) { return getAndroidNdk().getStripper( ndkToolchain ); } else { return getAndroidNdk().getStripper( getAndroidNdk().getToolchain( nativeLibrary ) ); } } private void attachHeaderFiles( File localCIncludesFile, String architecture ) throws MojoExecutionException, IOException { final List finalHeaderFilesDirectives = new ArrayList(); if ( useLocalSrcIncludePaths ) { Properties props = new Properties(); props.load( new FileInputStream( localCIncludesFile ) ); String localCIncludes = props.getProperty( "LOCAL_C_INCLUDES" ); if ( localCIncludes != null && ! localCIncludes.trim().isEmpty() ) { String[] includes = localCIncludes.split( " " ); for ( String include : includes ) { final HeaderFilesDirective headerFilesDirective = new HeaderFilesDirective(); File includeDir = new File( project.getBasedir(), include ); headerFilesDirective.setDirectory( includeDir.getAbsolutePath() ); headerFilesDirective.setIncludes( new String[]{ "**/*.h" } ); finalHeaderFilesDirectives.add( headerFilesDirective ); } } } else { if ( headerFilesDirectives != null ) { finalHeaderFilesDirectives.addAll( headerFilesDirectives ); } } if ( finalHeaderFilesDirectives.isEmpty() ) { getLog().debug( "No header files included, will add default set" ); final HeaderFilesDirective e = new HeaderFilesDirective(); e.setDirectory( new File( project.getBasedir() + "/jni" ).getAbsolutePath() ); e.setIncludes( new String[]{ "**/*.h" } ); finalHeaderFilesDirectives.add( e ); } createHeaderArchive( finalHeaderFilesDirectives, architecture ); } private void createHeaderArchive( List finalHeaderFilesDirectives, String architecture ) throws MojoExecutionException { try { MavenArchiver mavenArchiver = new MavenArchiver(); mavenArchiver.setArchiver( jarArchiver ); final File jarFile = new File( targetDirectory, finalName + ".har" ); mavenArchiver.setOutputFile( jarFile ); for ( HeaderFilesDirective headerFilesDirective : finalHeaderFilesDirectives ) { mavenArchiver.getArchiver().addDirectory( new File( headerFilesDirective.getDirectory() ), headerFilesDirective.getIncludes(), headerFilesDirective.getExcludes() ); } final MavenArchiveConfiguration mavenArchiveConfiguration = new MavenArchiveConfiguration(); mavenArchiveConfiguration.setAddMavenDescriptor( false ); mavenArchiver.createArchive( project, mavenArchiveConfiguration ); String classifier = architecture; if ( ndkClassifier != null ) { classifier += "-" + ndkClassifier; } getLog().debug( "Attaching 'har' classifier=" + classifier + " file=" + jarFile ); projectHelper.attachArtifact( project, Const.ArtifactType.NATIVE_HEADER_ARCHIVE, classifier, jarFile ); } catch ( Exception e ) { throw new MojoExecutionException( e.getMessage() ); } } private void setupNativeLibraryEnvironment( MakefileHelper makefileHelper, CommandExecutor executor, Set resolveNativeLibraryArtifacts, String architecture ) { // Only add the LOCAL_STATIC_LIBRARIES if ( NativeHelper.hasStaticNativeLibraryArtifact( resolveNativeLibraryArtifacts, getUnpackedLibsDirectory(), architecture ) ) { String staticlibs = makefileHelper.createLibraryList( resolveNativeLibraryArtifacts, architecture, true ); executor.addEnvironment( "ANDROID_MAVEN_PLUGIN_LOCAL_STATIC_LIBRARIES", staticlibs ); getLog().debug( "Set ANDROID_MAVEN_PLUGIN_LOCAL_STATIC_LIBRARIES = " + staticlibs ); } // Only add the LOCAL_SHARED_LIBRARIES if ( NativeHelper.hasSharedNativeLibraryArtifact( resolveNativeLibraryArtifacts, getUnpackedLibsDirectory(), architecture ) ) { String sharedlibs = makefileHelper.createLibraryList( resolveNativeLibraryArtifacts, architecture, false ); executor.addEnvironment( "ANDROID_MAVEN_PLUGIN_LOCAL_SHARED_LIBRARIES", sharedlibs ); getLog().debug( "Set ANDROID_MAVEN_PLUGIN_LOCAL_SHARED_LIBRARIES = " + sharedlibs ); } } private Set findNativeLibraryDependencies() throws MojoExecutionException { final NativeHelper nativeHelper = getNativeHelper(); final Set staticLibraryArtifacts = nativeHelper .getNativeDependenciesArtifacts( this, getUnpackedLibsDirectory(), false ); final Set sharedLibraryArtifacts = nativeHelper .getNativeDependenciesArtifacts( this, getUnpackedLibsDirectory(), true ); final Set mergedArtifacts = new LinkedHashSet(); filterNativeDependencies( mergedArtifacts, staticLibraryArtifacts ); filterNativeDependencies( mergedArtifacts, sharedLibraryArtifacts ); getLog().debug( "findNativeLibraryDependencies found " + mergedArtifacts.size() + ": " + mergedArtifacts.toString() ); return mergedArtifacts; } /** * Selectively add artifacts from source to target excluding any whose groupId and artifactId match * the current build. * * Introduced to work around an issue when the ndk-build is executed twice by maven for example when * invoking maven 'install site'. In this case the artifacts attached by the first invocation are * found but are not valid dependencies and must be excluded. * * @param targetSet artifact Set to copy in to * @param source artifact Set to filter */ private void filterNativeDependencies( Set targetSet, Set source ) { for ( Artifact a : source ) { if ( project.getGroupId().equals( a.getGroupId() ) && project.getArtifactId().equals( a.getArtifactId() ) ) { getLog().warn( "Excluding native dependency attached by this build" ); } else { targetSet.add( a ); } } } /** * Resolve the artifact type from the current project and the specified file. If the project packaging is * either 'a' or 'so' it will use the packaging, otherwise it checks the file for the extension * * @param file The file being added as an artifact * @return The artifact type (so or a) */ private String resolveArtifactType( File file ) { if ( Const.ArtifactType.NATIVE_SYMBOL_OBJECT.equals( project.getPackaging() ) || Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE.equals( project.getPackaging() ) ) { return project.getPackaging(); } else { // At this point, the file (as found by our filtering previously will end with either 'so' or 'a' return file.getName().endsWith( Const.ArtifactType.NATIVE_SYMBOL_OBJECT ) ? Const.ArtifactType.NATIVE_SYMBOL_OBJECT : Const.ArtifactType.NATIVE_IMPLEMENTATION_ARCHIVE; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy