org.jfrog.jade.plugins.natives.plugin.NativeCompileMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.jfrog.jade.plugins.natives.plugin.NativeCompileMojo.java

Source

package org.jfrog.jade.plugins.natives.plugin;

/*
 * The MIT License
 *
 * Copyright (c) 2004, The Codehaus
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Expand;
import org.apache.tools.ant.types.PatternSet;
import org.codehaus.mojo.natives.NativeBuildException;
import org.codehaus.mojo.natives.NativeSources;
import org.codehaus.mojo.natives.compiler.Compiler;
import org.codehaus.mojo.natives.compiler.CompilerConfiguration;
import org.codehaus.mojo.natives.manager.CompilerManager;
import org.codehaus.mojo.natives.manager.NoSuchNativeProviderException;
import org.codehaus.plexus.util.StringUtils;
import org.jfrog.jade.plugins.multijar.Multijar;
import org.jfrog.maven.annomojo.annotations.MojoGoal;
import org.jfrog.maven.annomojo.annotations.MojoParameter;
import org.jfrog.maven.annomojo.annotations.MojoPhase;
import org.jfrog.maven.annomojo.annotations.MojoRequiresDependencyResolution;

import java.io.File;
import java.util.*;

/**
 * Compile source files into native object files
 *
 * @author <a href="dantran@gmail.com">Dan T. Tran</a>
 * @author Fred Simon
 * @version $Id: NativeCompileMojo.java 2436 2006-09-29 13:54:03Z dantran $
 */
@MojoGoal("compile")
@MojoPhase("compile")
@MojoRequiresDependencyResolution
public class NativeCompileMojo extends AbstractNativeMojo {

    @SuppressWarnings({ "UnusedDeclaration" })
    @MojoParameter(description = "Use this field to override object file extension.\n"
            + "The default extenstions are .obj and .o on Windows and Unix respectively")
    private String objectFileExtension;

    @SuppressWarnings({ "UnusedDeclaration" })
    @MojoParameter(description = "Use this field to override provider specific compiler executable")
    private String compilerExecutable;

    @SuppressWarnings({ "UnusedDeclaration" })
    @MojoParameter(description = "Compiler options to produce shared libraries. Activated when shared flag is on.")
    private List<String> compilerSharedOptions;

    @SuppressWarnings({ "UnusedDeclaration" })
    @MojoParameter(description = "Compiler options to compile in debug mode.")
    private List<String> compilerDebugOptions;

    @SuppressWarnings({ "UnusedDeclaration" })
    @MojoParameter(description = "Compiler Start options to produce native object file")
    private List<String> compilerStartOptions;

    @SuppressWarnings({ "UnusedDeclaration" })
    @MojoParameter(description = "Compiler Middle options to produce native object file")
    private List<String> compilerMiddleOptions;

    @SuppressWarnings({ "UnusedDeclaration" })
    @MojoParameter(description = "Compiler End options to produce native object file")
    private List<String> compilerEndOptions;

    @SuppressWarnings({ "UnusedDeclaration" })
    @MojoParameter(description = "Javah OS name.\n"
            + "${jdkIncludePath} and ${jdkIncludePath}/${javaOS} are added to system include path \n"
            + "when this field is set")
    private String javahOS;

    @SuppressWarnings({ "UnusedDeclaration" })
    @MojoParameter(defaultValue = "${java.home}/../include", description = "JDK native include directory")
    private File jdkIncludePath;

    @MojoParameter(description = "Array of NativeSources containing include directories and source files")
    protected List<NativeSources> sources = new ArrayList<NativeSources>();

    @SuppressWarnings({ "UnusedDeclaration" })
    @MojoParameter(expression = "${component.org.codehaus.mojo.natives.manager.CompilerManager}", required = true, readonly = true, description = "Internal compiler manager")
    private CompilerManager manager;

    @MojoParameter(description = "Flag enabling the deployment and retrieval of include zip files")
    private boolean deployInclude = false;

    @MojoParameter(description = "The pattern for creating the zip file containing the \".h\" files.\nThe default is \"**/*.h\"", defaultValue = "**/*.h")
    private String includesPattern;

    @MojoParameter(description = "The file extension containing the .h files.", defaultValue = "zip")
    private String includesExtension;

    @MojoParameter(expression = "${includesRootDir}", defaultValue = "${project.build.directory}/includes", description = "The root directory where the XXX-include.zip files will be extracted")
    private File includesRootDir;

    @MojoParameter(expression = "${reactorProjects}", required = true, readonly = true)
    private List<MavenProject> reactorProjects;

    @MojoParameter(description = "List of options that are inserted when compiling with debug information.\n"
            + "It should be declared the following way:\n"
            + "<debugOptions>\n\t<option>-option1</option>\n...\n</debugOptions>")
    private boolean debugOptions = true;

    @MojoParameter(description = "The maximum amount of paralell threads to activated when doing compilation")
    private int nbParallelCompilation = 0;

    @MojoParameter(description = "Additional environments values used before execution of the compile Command.")
    private Map<String, String> compileSystemProperties = new HashMap<String, String>();

    @MojoParameter(description = "The root folder for ALL object files output. The default value is ${project.build.directory}/obj.\n"
            + "If it is set to something else the plugin will create all obj files under ${objOutputDir}/$artifactId-$version/[debug,release,test]", expression = "${objRootOutputDir}", required = true, defaultValue = "${project.build.directory}/obj")
    private File objRootOutputDir;

    @MojoParameter(description = "Flag to activate the transitive parsing of C/C++ files for #include statements in "
            + "order to generate real up-to-date flag. If obj file older than ALL .h files than up-to-date.\n"
            + "Deactivating this falg will make the native plugin use the configured up-to-date manager which will check the date"
            + " between the obj and C file like in standard make (faster).", defaultValue = "true", expression = "${useTransitiveScanning}")
    private boolean useTransitiveScanning = true;

    private File objProjectOutputDir;

    private File objFinalOutputDir;

    private static final String INCLUDE_CLASSIFIER = "include";

    public void execute() throws MojoExecutionException {

        if (isSkipped()) {
            return;
        }

        File sourceDir = getCompileSourceDir();
        addCppSourceDirectory(sourceDir);
        List sourceRoots = getProject().getCompileSourceRoots();
        if (sourceRoots != null) {
            for (Object path : sourceRoots) {
                String sPath = (String) path;
                if (sPath != null && sPath.length() > 0) {
                    File dir = new File(sPath);
                    if (dir.exists())
                        addCppSourceDirectory(dir);
                }
            }
        }

        List<Multijar> includesBuilder = null;

        if (isDeployInclude()) {
            File jarFile = null;
            for (NativeSources source : sources) {
                if (source.getDependencyAnalysisParticipation()) {
                    if (includesBuilder == null) {
                        includesBuilder = new ArrayList<Multijar>();
                    }
                    Multijar includesZipper = createIncludesZipper(source, jarFile);
                    includesBuilder.add(includesZipper);
                    includesZipper.initFromMojo(this, null);
                    if (jarFile == null) {
                        jarFile = includesZipper.getJarfile();
                    }
                }
            }
            if (jarFile == null) {
                throw new MojoExecutionException(
                        "Cannot create zip of include files if no sourcedirectory are defined");
            }
        }

        Compiler compiler;

        try {
            compiler = this.manager.getCompiler(getCompilerProvider());
        } catch (NoSuchNativeProviderException pe) {
            throw new MojoExecutionException(pe.getMessage(), pe);
        }

        if (this.javahOS != null) {
            this.addJavaHIncludePaths();
        }

        this.addDependantIncludePath(getIncludesArtifactFilter());

        CompilerConfiguration config = this.createProviderConfiguration();

        List<File> objectFiles;
        try {
            objectFiles = compiler.compile(config, NativeSources.getAllSourceFiles(this.sources));
        } catch (NativeBuildException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }

        this.saveCompilerOutputFilePaths(objectFiles);

        if (includesBuilder != null) {
            for (Multijar multijar : includesBuilder) {
                multijar.execute();
            }
        }
    }

    public Multijar createIncludesZipper(NativeSources source, File jarFile) {
        Multijar includesZipper = new Multijar();
        if (source.getExcludes() != null && !source.getExcludes().isEmpty()) {
            includesZipper.setExcludes(StringUtils.join(source.getExcludes().iterator(), ","));
        }
        includesZipper.setIncludes(includesPattern);
        includesZipper.setSkippedIfUpToDate(true);
        if (jarFile == null) {
            includesZipper.setClassifier(INCLUDE_CLASSIFIER);
            includesZipper.setExtension(includesExtension);
            includesZipper.setDeploymentType(INCLUDE_CLASSIFIER);
        } else {
            includesZipper.setJarfile(jarFile);
            includesZipper.setClassifier(null);
            includesZipper.setExtension(includesExtension);
            includesZipper.setDeploymentType(null);
            includesZipper.setUpdateExisting(true);
        }
        includesZipper.setSkippedIfEmpty(false);
        includesZipper.setSourceDir(source.getDirectory());
        return includesZipper;
    }

    private void addJavaHIncludePaths() {
        NativeSources jdkIncludeSource = new NativeSources();

        jdkIncludeSource.setDirectory(this.jdkIncludePath);

        jdkIncludeSource.setDependencyAnalysisParticipation(false);

        sources.add(jdkIncludeSource);

        File jdkOsIncludeDir = new File(this.jdkIncludePath, this.javahOS);

        NativeSources jdkIncludeOsSource = new NativeSources();

        jdkIncludeOsSource.setDirectory(jdkOsIncludeDir);

        jdkIncludeOsSource.setDependencyAnalysisParticipation(false);

        sources.add(jdkIncludeOsSource);
    }

    /**
     * Add include path from the list of dependant artifacts
     *
     * @param filter
     * @throws MojoExecutionException
     */
    private void addDependantIncludePath(ArtifactFilter filter) throws MojoExecutionException {
        Collection<Artifact> allArtifacts = getProject().getArtifacts();

        if (allArtifacts == null || allArtifacts.isEmpty()) {
            return;
        }

        // If no sources nothing to do?
        if (this.sources == null) {
            return;
        }

        for (Artifact artifact : allArtifacts) {
            if (filter.include(artifact)) {
                MavenProject reactorProject = findInReactorList(artifact);
                if (reactorProject != null) {
                    List<Artifact> attachedArtifacts = reactorProject.getAttachedArtifacts();
                    for (Artifact attachedArtifact : attachedArtifacts) {
                        if (attachedArtifact.getClassifier() != null
                                && attachedArtifact.getClassifier().equals(INCLUDE_CLASSIFIER)) {
                            addIncludeDirectory(
                                    extractIncludeZipFile(attachedArtifact, attachedArtifact.getFile()));
                            break;
                        }
                    }
                } else {
                    addIncludeDirectory(findAndExtractInclude(artifact));
                }
            }
        }
    }

    public MavenProject findInReactorList(Artifact artifact) {
        for (MavenProject reactorProject : reactorProjects) {
            if (reactorProject.getGroupId().equals(artifact.getGroupId())
                    && reactorProject.getArtifactId().equals(artifact.getArtifactId())
                    && reactorProject.getVersion().equals(artifact.getVersion())) {
                return reactorProject;
            }
        }
        return null;
    }

    public void addIncludeDirectory(File includeDirectory) {
        if (includeDirectory == null || !includeDirectory.exists()) {
            return;
        }
        NativeSources genIncludeSource = new NativeSources();
        genIncludeSource.setDirectory(includeDirectory);
        sources.add(genIncludeSource);
    }

    private File findAndExtractInclude(Artifact artifact) throws MojoExecutionException {
        File includeZipFile = null;

        try {
            // Resolve the artifact of include zip file
            Artifact includeZip = null;
            includeZip = getArtifactFactory().createArtifactWithClassifier(artifact.getGroupId(),
                    artifact.getArtifactId(), artifact.getVersion(), includesExtension, INCLUDE_CLASSIFIER);
            getArtifactResolver().resolve(includeZip, getProject().getRemoteArtifactRepositories(),
                    getLocalRepository());
            if (includeZip != null) {
                includeZipFile = includeZip.getFile();
            }
        } catch (ArtifactResolutionException e) {
            getLog().info("Dependency " + artifact + " does not have an include zip file");
            getLog().debug("Dependency " + artifact + " does not have an include zip file", e);
        } catch (ArtifactNotFoundException e) {
            getLog().info("Dependency " + artifact + " does not have an include zip file");
            getLog().debug("Dependency " + artifact + " does not have an include zip file", e);
        }

        return extractIncludeZipFile(artifact, includeZipFile);
    }

    private File extractIncludeZipFile(Artifact artifact, File includeZipFile) throws MojoExecutionException {
        if (includeZipFile == null || !includeZipFile.exists()) {
            return null;
        }

        File includeDirectory;
        if (includesRootDir == null) {
            includesRootDir = new File(getProject().getBuild().getDirectory(), "includes");
        }
        String versionName = getVersionName(artifact);
        includeDirectory = new File(includesRootDir, artifact.getArtifactId() + "-" + versionName);
        boolean executeUnzip = !includeDirectory.exists();
        createFolder(includeDirectory);

        if (!executeUnzip) {
            // Check that the zip file is newer
            executeUnzip = includeZipFile.lastModified() >= includeDirectory.lastModified();
        }

        if (executeUnzip) {
            // We use the Ant expand task
            Project antProject = getAntProject();
            Expand expand = new Expand();
            expand.setProject(antProject);
            expand.setDest(includeDirectory);
            expand.setSrc(includeZipFile);
            PatternSet noMetaInf = new PatternSet();
            noMetaInf.setProject(antProject);
            PatternSet.NameEntry nameEntry = noMetaInf.createExclude();
            nameEntry.setName("META-INF");
            expand.addPatternset(noMetaInf);
            expand.execute();
            includeDirectory.setLastModified(System.currentTimeMillis());
        }
        return includeDirectory;
    }

    public File getObjRootOutputDir() {
        return objRootOutputDir;
    }

    public void setObjRootOutputDir(File objRootOutputDir) {
        this.objRootOutputDir = objRootOutputDir;
    }

    protected File getObjProjectOutputDir() throws MojoExecutionException {
        if (objProjectOutputDir == null) {
            File outputDir = getObjRootOutputDir();
            if (outputDir == null) {
                outputDir = getOutputDirectory();
                objProjectOutputDir = new File(outputDir, "obj");
            } else {
                String versionName = getVersionName(getProject().getVersion());
                objProjectOutputDir = new File(outputDir, getProject().getArtifactId() + "-" + versionName);
            }
            createFolder(objProjectOutputDir);
        }
        return objProjectOutputDir;
    }

    public File getObjFinalOutputDir() throws MojoExecutionException {
        if (objFinalOutputDir == null) {
            if (addDebugOptions())
                createObjOutputSubDir("debug");
            else
                createObjOutputSubDir("release");
        }
        createFolder(objFinalOutputDir);
        return objFinalOutputDir;
    }

    protected void createObjOutputSubDir(String objOutputSubDir) throws MojoExecutionException {
        objFinalOutputDir = new File(getObjProjectOutputDir(), objOutputSubDir);
    }

    /*
    * use protected scope for unit test purpose
    */
    protected CompilerConfiguration createProviderConfiguration() throws MojoExecutionException {
        this.config = new CompilerConfiguration();
        config.setWorkingDirectory(getProject().getBasedir());
        config.setExecutable(getCompilerExecutable());
        List<String> startOptions = compilerStartOptions;
        if (addSharedOptions() && compilerSharedOptions != null) {
            startOptions = new ArrayList<String>();
            if (compilerStartOptions != null) {
                startOptions.addAll(compilerStartOptions);
            }
            startOptions.addAll(compilerSharedOptions);
        }
        config.setStartOptions(removeEmptyOptions(startOptions));
        List<String> middleOptions = compilerMiddleOptions;
        if (addDebugOptions() && compilerDebugOptions != null) {
            middleOptions = new ArrayList<String>();
            if (compilerMiddleOptions != null) {
                middleOptions.addAll(compilerMiddleOptions);
            }
            middleOptions.addAll(compilerDebugOptions);
        }
        config.setMiddleOptions(removeEmptyOptions(middleOptions));
        config.setEndOptions(removeEmptyOptions(this.compilerEndOptions));
        config.setIncludePaths(NativeSources.getIncludePaths(sources));
        config.setSystemIncludePaths(NativeSources.getSystemIncludePaths(sources));
        config.setOutputDirectory(this.getObjFinalOutputDir());
        config.setEnvFactoryName(this.envFactoryName);
        config.setSystemProperties(this.compileSystemProperties);
        config.setObjectFileExtension(this.objectFileExtension);
        config.setNbParallelCompilation(this.nbParallelCompilation);
        if (useTransitiveScanning)
            config.setUpToDateAnalyzer(null);
        else
            config.setUpToDateAnalyzer(this.getUpToDateManager().getUpToDateAnalyzer(this));

        return config;
    }

    //////////////// Getter/Setters for plugin reuse ////////////////////////////////

    public List<NativeSources> getSources() {
        return sources;
    }

    public void setReactorProjects(List<MavenProject> reactorProjects) {
        this.reactorProjects = reactorProjects;
    }

    protected ArtifactFilter getIncludesArtifactFilter() {
        return new ArtifactFilter() {
            public boolean include(Artifact artifact) {
                String scope = getFailSafeScope(artifact);
                return NativeNameProvider.isLibrary(artifact) && !scope.equals(Artifact.SCOPE_RUNTIME)
                        && !scope.equals(Artifact.SCOPE_TEST);
            }
        };
    }

    protected File getCompileSourceDir() {
        String pathname = getProject().getBuild().getSourceDirectory();
        if (pathname == null || pathname.length() == 0) {
            return null;
        }
        return new File(pathname);
    }

    public boolean isDeployInclude() {
        return deployInclude;
    }

    public boolean addDebugOptions() {
        return debugOptions;
    }

    public void addCppSourceDirectory(File sourceDir) {
        if (sourceDir == null) {
            return;
        }

        // First check if not already there
        for (NativeSources source : sources) {
            if (source.getDirectory().getAbsoluteFile().equals(sourceDir.getAbsoluteFile())) {
                return;
            }
        }

        NativeSources projectSources = new NativeSources();
        projectSources.setDirectory(sourceDir);
        projectSources.addInclude("**/*.c");
        projectSources.addInclude("**/*.cpp");
        projectSources.setDependencyAnalysisParticipation(true);
        sources.add(projectSources);
    }

    public String getCompilerExecutable() {
        return compilerExecutable;
    }

    ////////////////////////////////////// UNIT TEST HELPERS ////////////////////////////////

    /**
     * For unittest only
     */
    private CompilerConfiguration config;

    /**
     * Internal only for test harness purpose
     *
     * @return
     */
    protected CompilerConfiguration getCompilerConfiguration() {
        return this.config;
    }

}