/**
  * Attempts to resolve an {@link Artifact} to a {@link File}.
  *
  * @param artifact to resolve
  * @return a {@link File} to the resolved artifact, never <code>null</code>.
  * @throws MojoExecutionException if the artifact could not be resolved.
  */
 protected File resolveArtifactToFile(Artifact artifact) throws MojoExecutionException {
   Artifact resolvedArtifact =
       AetherHelper.resolveArtifact(artifact, repoSystem, repoSession, projectRepos);
   final File jar = resolvedArtifact.getFile();
   if (jar == null) {
     throw new MojoExecutionException(
         "Could not resolve artifact "
             + artifact.getId()
             + ". Please install it with \"mvn install:install-file ...\" or deploy it to a repository "
             + "with \"mvn deploy:deploy-file ...\"");
   }
   return jar;
 }
  /**
   * @throws MojoExecutionException
   * @throws MojoFailureException
   */
  public void execute() throws MojoExecutionException, MojoFailureException {
    try {
      // Validate the NDK
      final File ndkBuildFile = new File(getAndroidNdk().getNdkBuildPath());
      NativeHelper.validateNDKVersion(ndkBuildFile.getParentFile());

      // 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.
      validateMakefile(project, makefile);

      String[] ndkArchitectures =
          getAndroidNdk()
              .getNdkArchitectures(
                  ndkClassifier, ndkArchitecture, applicationMakefile, project.getBasedir());
      for (String ndkArchitecture : ndkArchitectures) {
        Preparation preparation = new Preparation().invoke(ndkArchitecture);
        boolean libsDirectoryExists = preparation.isLibsDirectoryExists();
        File directoryToRemove = preparation.getDirectoryToRemove();

        // 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<Artifact> nativeLibraryArtifacts = findNativeLibraryDependencies();

        // If there are any static libraries the code needs to link to, include those in the make
        // file
        final Set<Artifact> resolveNativeLibraryArtifacts =
            AetherHelper.resolveArtifacts(
                nativeLibraryArtifacts, repoSystem, repoSession, projectRepos);
        if (getLog().isDebugEnabled()) {
          getLog()
              .debug(
                  "resolveArtifacts found "
                      + resolveNativeLibraryArtifacts.size()
                      + ": "
                      + resolveNativeLibraryArtifacts.toString());
        }

        final File makefileDir = new File(project.getBuild().getDirectory(), NDK_MAKFILE_DIRECTORY);
        makefileDir.mkdirs();
        final File androidMavenMakefile = new File(makefileDir, "android_maven_plugin_makefile.mk");

        // set the ndk build directory
        if (ndkBuildDirectory == null) {
          ndkBuildDirectory = project.getBasedir().getAbsolutePath();
        }

        final MakefileHelper makefileHelper =
            new MakefileHelper(
                getLog(), repoSystem, repoSession, projectRepos, unpackedApkLibsDirectory);
        final MakefileHelper.MakefileHolder makefileHolder =
            makefileHelper.createMakefileFromArtifacts(
                new File(ndkBuildDirectory),
                resolveNativeLibraryArtifacts,
                ndkArchitecture,
                useHeaderArchives);
        IOUtil.copy(makefileHolder.getMakeFile(), new FileOutputStream(androidMavenMakefile));

        // 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, ndkArchitecture);

        // 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<String, String> entry : systemProperties.entrySet()) {
            executor.addEnvironment(entry.getKey(), entry.getValue());
          }
        }
        executor.setLogger(this.getLog());
        // Setup the command line for the make
        final List<String> commands = new ArrayList<String>();
        // Setup the build directory (defaults to the current directory) but may be different
        // depending
        // on user configuration
        commands.add("-C");
        commands.add(ndkBuildDirectory);

        // 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(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) {
          String[] additionalCommands = ndkBuildAdditionalCommandline.split(" ");
          for (final String command : additionalCommands) {
            commands.add(command);
          }
        }
        // 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 ( "a".equals( project.getPackaging() ) )*/ {
          commands.add(project.getArtifactId());
        }

        final String ndkBuildPath = resolveNdkBuildExecutable();
        getLog().info(ndkBuildPath + " " + commands.toString());

        executor.executeCommand(ndkBuildPath, commands, project.getBasedir(), true);

        cleanUp(
            preparation.getNativeLibDirectory(),
            ndkArchitecture,
            libsDirectoryExists,
            directoryToRemove,
            makefileHolder,
            makefileCaptureFile);
      }
    } catch (MojoExecutionException e) {
      getLog().error("Error during build: " + e.getMessage(), e);
      throw e;
    } catch (Exception e) {
      getLog().error("Error while executing: " + e.getMessage());
      throw new MojoExecutionException(e.getMessage(), e);
    }
  }