com.photon.maven.plugins.android.common.NativeHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.photon.maven.plugins.android.common.NativeHelper.java

Source

/*
 * ###
 * Android Maven Plugin - android-maven-plugin
 * 
 * Copyright (C) 1999 - 2012 Photon Infotech Inc.
 * 
 * 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.
 * ###
 */
/*
 * Copyright (C) 2009 Jayway AB
 *
 * 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.photon.maven.plugins.android.common;

import static com.photon.maven.plugins.android.common.AndroidExtension.APKLIB;
import static org.apache.maven.RepositoryUtils.toDependency;

import java.io.File;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.sonatype.aether.RepositorySystem;
import org.sonatype.aether.RepositorySystemSession;
import org.sonatype.aether.collection.CollectRequest;
import org.sonatype.aether.graph.Dependency;
import org.sonatype.aether.graph.DependencyFilter;
import org.sonatype.aether.graph.DependencyNode;
import org.sonatype.aether.repository.RemoteRepository;
import org.sonatype.aether.resolution.DependencyRequest;
import org.sonatype.aether.util.filter.AndDependencyFilter;
import org.sonatype.aether.util.filter.ScopeDependencyFilter;
import org.sonatype.aether.util.graph.PreorderNodeListGenerator;

import com.photon.maven.plugins.android.AbstractAndroidMojo;
import com.photon.maven.plugins.android.AndroidNdk;

/**
 */
public class NativeHelper {

    public static final int NDK_REQUIRED_VERSION = 7;

    private MavenProject project;
    private RepositorySystemSession repoSession;
    private RepositorySystem repoSystem;
    private ArtifactFactory artifactFactory;
    private Log log;
    private List<RemoteRepository> projectRepos;

    public NativeHelper(MavenProject project, List<RemoteRepository> projectRepos,
            RepositorySystemSession repoSession, RepositorySystem repoSystem, ArtifactFactory artifactFactory,
            Log log) {
        this.project = project;
        this.projectRepos = projectRepos;
        this.repoSession = repoSession;
        this.repoSystem = repoSystem;
        this.artifactFactory = artifactFactory;
        this.log = log;
    }

    public static boolean hasStaticNativeLibraryArtifact(Set<Artifact> resolveNativeLibraryArtifacts) {
        for (Artifact resolveNativeLibraryArtifact : resolveNativeLibraryArtifacts) {
            if ("a".equals(resolveNativeLibraryArtifact.getType())) {
                return true;
            }
        }
        return false;
    }

    public static boolean hasSharedNativeLibraryArtifact(Set<Artifact> resolveNativeLibraryArtifacts) {
        for (Artifact resolveNativeLibraryArtifact : resolveNativeLibraryArtifacts) {
            if ("so".equals(resolveNativeLibraryArtifact.getType())) {
                return true;
            }
        }
        return false;
    }

    public Set<Artifact> getNativeDependenciesArtifacts(File unpackDirectory, boolean sharedLibraries)
            throws MojoExecutionException {
        final Set<Artifact> filteredArtifacts = new LinkedHashSet<Artifact>();

        // Add all dependent artifacts declared in the pom file
        @SuppressWarnings("unchecked")
        final Set<Artifact> allArtifacts = project.getDependencyArtifacts();

        // Add all attached artifacts as well - this could come from the NDK mojo for example
        boolean result = allArtifacts.addAll(project.getAttachedArtifacts());

        for (Artifact artifact : allArtifacts) {
            // A null value in the scope indicates that the artifact has been attached
            // as part of a previous build step (NDK mojo)
            if (isNativeLibrary(sharedLibraries, artifact.getType()) && artifact.getScope() == null) {
                // Including attached artifact
                log.debug("Including attached artifact: " + artifact.getArtifactId() + "(" + artifact.getGroupId()
                        + ")");
                filteredArtifacts.add(artifact);
            } else if (isNativeLibrary(sharedLibraries, artifact.getType())
                    && (Artifact.SCOPE_COMPILE.equals(artifact.getScope())
                            || Artifact.SCOPE_RUNTIME.equals(artifact.getScope()))) {
                filteredArtifacts.add(artifact);
            } else if (APKLIB.equals(artifact.getType())) {
                // Check if the artifact contains a libs folder - if so, include it in the list
                File libsFolder = new File(
                        AbstractAndroidMojo.getLibraryUnpackDirectory(unpackDirectory, artifact) + "/libs");
                if (libsFolder.exists()) {
                    filteredArtifacts.add(artifact);
                }
            }
        }

        Set<Artifact> transientArtifacts = processTransientDependencies(project.getDependencies(), sharedLibraries);

        filteredArtifacts.addAll(transientArtifacts);

        return filteredArtifacts;
    }

    private boolean isNativeLibrary(boolean sharedLibraries, String artifactType) {
        return (sharedLibraries ? "so".equals(artifactType) : "a".equals(artifactType));
    }

    private Set<Artifact> processTransientDependencies(List<org.apache.maven.model.Dependency> dependencies,
            boolean sharedLibraries) throws MojoExecutionException {

        Set<Artifact> transientArtifacts = new LinkedHashSet<Artifact>();
        for (org.apache.maven.model.Dependency dependency : dependencies) {
            if (!"provided".equals(dependency.getScope()) && !dependency.isOptional()) {
                transientArtifacts.addAll(processTransientDependencies(
                        toDependency(dependency, repoSession.getArtifactTypeRegistry()), sharedLibraries));
            }
        }

        return transientArtifacts;

    }

    private Set<Artifact> processTransientDependencies(Dependency dependency, boolean sharedLibraries)
            throws MojoExecutionException {
        try {
            final Set<Artifact> artifacts = new LinkedHashSet<Artifact>();

            final CollectRequest collectRequest = new CollectRequest();

            collectRequest.setRoot(dependency);
            collectRequest.setRepositories(projectRepos);
            final DependencyNode node = repoSystem.collectDependencies(repoSession, collectRequest).getRoot();

            final DependencyRequest dependencyRequest = new DependencyRequest(node,
                    new AndDependencyFilter(
                            new ScopeDependencyFilter(Arrays.asList("compile", "runtime"), Arrays.asList("test")),
                            // Also exclude any optional dependencies
                            new DependencyFilter() {
                                @Override
                                public boolean accept(DependencyNode dependencyNode,
                                        List<DependencyNode> dependencyNodes) {
                                    return !dependencyNode.getDependency().isOptional();
                                }
                            }));

            repoSystem.resolveDependencies(repoSession, dependencyRequest);

            PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
            node.accept(nlg);

            final List<Dependency> dependencies = nlg.getDependencies(false);

            for (Dependency dep : dependencies) {
                final org.sonatype.aether.artifact.Artifact depAetherArtifact = dep.getArtifact();
                if (isNativeLibrary(sharedLibraries, depAetherArtifact.getExtension())) {
                    final Artifact mavenArtifact = artifactFactory.createDependencyArtifact(
                            depAetherArtifact.getGroupId(), depAetherArtifact.getArtifactId(),
                            VersionRange.createFromVersion(depAetherArtifact.getVersion()),
                            depAetherArtifact.getExtension(), depAetherArtifact.getClassifier(), dep.getScope());
                    mavenArtifact.setFile(depAetherArtifact.getFile());
                    artifacts.add(mavenArtifact);
                }
            }

            return artifacts;
        } catch (Exception e) {
            throw new MojoExecutionException("Error while processing transient dependencies", e);
        }
    }

    public static void validateNDKVersion(File ndkHomeDir) throws MojoExecutionException {
        final File ndkVersionFile = new File(ndkHomeDir, "RELEASE.TXT");

        if (!ndkVersionFile.exists()) {
            throw new MojoExecutionException("Could not locate RELEASE.TXT in the Android NDK base directory '"
                    + ndkHomeDir.getAbsolutePath() + "'.  Please verify your setup! "
                    + AndroidNdk.PROPER_NDK_HOME_DIRECTORY_MESSAGE);
        }

        try {
            String versionStr = FileUtils.readFileToString(ndkVersionFile);
            validateNDKVersion(NDK_REQUIRED_VERSION, versionStr);
        } catch (Exception e) {
            throw new MojoExecutionException(
                    "Error while extracting NDK version from '" + ndkVersionFile.getAbsolutePath()
                            + "'. Please verify your setup! " + AndroidNdk.PROPER_NDK_HOME_DIRECTORY_MESSAGE);
        }
    }

    public static void validateNDKVersion(int desiredVersion, String versionStr) throws MojoExecutionException {

        int version = 0;

        if (versionStr != null) {
            versionStr = versionStr.trim();
            Pattern pattern = Pattern.compile("[r]([0-9]{1,3})([a-z]{0,1}).*");
            Matcher m = pattern.matcher(versionStr);
            if (m.matches()) {
                final String group = m.group(1);
                version = Integer.parseInt(group);
            }
        }

        if (version < desiredVersion) {
            throw new MojoExecutionException("You are running an old NDK (version " + versionStr
                    + "), please update to at least r'" + desiredVersion + "' or later");
        }
    }

}