/* * #%L * Native ARchive plugin for Maven * %% * Copyright (C) 2002 - 2014 NAR Maven Plugin developers. * %% * 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. * #L% */ package com.github.maven_nar; import java.io.File; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.apache.maven.artifact.Artifact; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; 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.plugins.annotations.ResolutionScope; import org.apache.maven.shared.artifact.filter.collection.ScopeFilter; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import com.github.maven_nar.cpptasks.CCTask; import com.github.maven_nar.cpptasks.CUtil; import com.github.maven_nar.cpptasks.CompilerDef; import com.github.maven_nar.cpptasks.LinkerDef; import com.github.maven_nar.cpptasks.OutputTypeEnum; import com.github.maven_nar.cpptasks.RuntimeType; import com.github.maven_nar.cpptasks.SubsystemEnum; import com.github.maven_nar.cpptasks.types.LibrarySet; import com.github.maven_nar.cpptasks.types.LibraryTypeEnum; import com.github.maven_nar.cpptasks.types.LinkerArgument; import com.github.maven_nar.cpptasks.types.SystemLibrarySet; /** * Compiles native test source files. * * @author Mark Donszelmann */ @Mojo(name = "nar-testCompile", defaultPhase = LifecyclePhase.TEST_COMPILE, requiresDependencyResolution = ResolutionScope.TEST) public class NarTestCompileMojo extends AbstractCompileMojo { /** * Skip running of NAR integration test plugins. */ @Parameter(property = "skipNar") protected boolean skipNar; private void createTest(final Project antProject, final Test test) throws MojoExecutionException, MojoFailureException { final String type = "test"; // configure task final CCTask task = new CCTask(); task.setProject(antProject); // subsystem final SubsystemEnum subSystem = new SubsystemEnum(); subSystem.setValue("console"); task.setSubsystem(subSystem); // outtype final OutputTypeEnum outTypeEnum = new OutputTypeEnum(); outTypeEnum.setValue(test.getType()); task.setOuttype(outTypeEnum); // outDir File outDir = new File(getTestTargetDirectory(), "bin"); outDir = new File(outDir, getAOL().toString()); outDir.mkdirs(); // outFile final File outFile = new File(outDir, test.getName()); getLog().debug("NAR - output: '" + outFile + "'"); task.setOutfile(outFile); // object directory File objDir = new File(getTestTargetDirectory(), "obj"); objDir = new File(objDir, getAOL().toString()); objDir.mkdirs(); task.setObjdir(objDir); // failOnError, libtool task.setFailonerror(failOnError(getAOL())); task.setLibtool(useLibtool(getAOL())); // runtime final RuntimeType runtimeType = new RuntimeType(); runtimeType.setValue(getRuntime(getAOL())); task.setRuntime(runtimeType); // add C++ compiler final Cpp cpp = getCpp(); if (cpp != null) { final CompilerDef cppCompiler = getCpp().getTestCompiler(type, test.getName()); if (cppCompiler != null) { task.addConfiguredCompiler(cppCompiler); } } // add C compiler final C c = getC(); if (c != null) { final CompilerDef cCompiler = c.getTestCompiler(type, test.getName()); if (cCompiler != null) { task.addConfiguredCompiler(cCompiler); } } // add Fortran compiler final Fortran fortran = getFortran(); if (fortran != null) { final CompilerDef fortranCompiler = getFortran().getTestCompiler(type, test.getName()); if (fortranCompiler != null) { task.addConfiguredCompiler(fortranCompiler); } } // add java include paths getJava().addIncludePaths(task, type); getMsvc().configureCCTask(task); List depLibs = getNarArtifacts(); // add dependency include paths for (final Object depLib1 : depLibs) { final Artifact artifact = (Artifact) depLib1; // check if it exists in the normal unpack directory File include = getLayout() .getIncludeDirectory(getUnpackDirectory(), artifact.getArtifactId(), artifact.getBaseVersion()); if (!include.exists()) { // otherwise try the test unpack directory include = getLayout() .getIncludeDirectory(getTestUnpackDirectory(), artifact.getArtifactId(), artifact.getBaseVersion()); } if (include.exists()) { task.createIncludePath().setPath(include.getPath()); } } // add javah generated include path final File jniIncludeDir = getJavah().getJniDirectory(); if (jniIncludeDir.exists()) { task.createIncludePath().setPath(jniIncludeDir.getPath()); } // add linker final LinkerDef linkerDefinition = getLinker().getTestLinker(this, task, getOS(), getAOL().getKey() + ".linker.", type); task.addConfiguredLinker(linkerDefinition); final File includeDir = getLayout().getIncludeDirectory(getTargetDirectory(), getMavenProject().getArtifactId(), getMavenProject().getVersion()); String linkType = test.getLink( getLibraries() ); final File libDir = getLayout().getLibDirectory(getTargetDirectory(), getMavenProject().getArtifactId(), getMavenProject().getVersion(), getAOL().toString(), linkType); // copy shared library // FIXME why do we do this ? /* * Removed in alpha-10 if (test.getLink().equals(Library.SHARED)) { try { // * defaults are Unix String libPrefix * = NarUtil.getDefaults().getProperty( getAOLKey() + "shared.prefix", * "lib"); String libExt = * NarUtil.getDefaults().getProperty( getAOLKey() + "shared.extension", * "so"); File copyDir = new * File(getTargetDirectory(), (getOS().equals( "Windows") ? "bin" : "lib") + * "/" + getAOL() + "/" + * test.getLink()); FileUtils.copyFileToDirectory(new File(libDir, libPrefix * + libName + "." + libExt), * copyDir); if (!getOS().equals(OS.WINDOWS)) { libDir = copyDir; } } catch * (IOException e) { throw new * MojoExecutionException( "NAR: Could not copy shared library", e); } } */ // FIXME what about copying the other shared libs? // add include of this package if (includeDir.exists()) { task.createIncludePath().setLocation(includeDir); } // add library of this package if (libDir.exists()) { final LibrarySet libSet = new LibrarySet(); libSet.setProject(antProject); // String libs = getNarInfo().getLibs( getAOL() ); // using getNarInfo().getLibs( getAOL() ); forces to execute the goal // nar-prepare-package prior to // nar-testCompile in order to set the "output" property in narInfo with // the call : // narInfo.setOutput( null, mojo.getOutput(true) ); (set in // NarLayout21.prepareNarInfo(...)) // narInfo.getLibs(aol) call in fact narInfo.getProperty( aol, // "libs.names", getOutput( aol, artifactId + "-" + version ) ); // where getOutput is the getOutput method in narInfo (which needs the // "output" property). // We call then directly narInfo.getProperty( aol, "libs.names", <output // value>); but we set <output value> // with AbstractCompileMojo.getOutput( boolean versioned ) as it is done // during nar-prepare-package final String libs = getNarInfo().getProperty(getAOL(), "libs.names", getOutput(true)); getLog().debug("Searching for parent to link with " + libs); libSet.setLibs(new CUtil.StringArrayBuilder(libs)); final LibraryTypeEnum libType = new LibraryTypeEnum(); libType.setValue(linkType); libSet.setType(libType); libSet.setDir(libDir); task.addLibset(libSet); } // add dependency libraries final List depLibOrder = getDependencyLibOrder(); // reorder the libraries that come from the nar dependencies // to comply with the order specified by the user if (depLibOrder != null && !depLibOrder.isEmpty()) { final List tmp = new LinkedList(); for (final Object aDepLibOrder : depLibOrder) { final String depToOrderName = (String) aDepLibOrder; for (final Iterator j = depLibs.iterator(); j.hasNext(); ) { final NarArtifact dep = (NarArtifact) j.next(); final String depName = dep.getGroupId() + ":" + dep.getArtifactId(); if (depName.equals(depToOrderName)) { tmp.add(dep); j.remove(); } } } tmp.addAll(depLibs); depLibs = tmp; } for (final Object depLib : depLibs) { final NarArtifact dependency = (NarArtifact) depLib; // FIXME no handling of "local" final String binding = getBinding(test, dependency); getLog().debug("Using Binding: " + binding); AOL aol = getAOL(); aol = dependency.getNarInfo().getAOL(getAOL()); getLog().debug("Using Library AOL: " + aol.toString()); // We dont link against the following library types : // - JNI, they are Java libraries // - executable, they are not libraries // - none, they are not libraries ... I gess // Static libraries should be linked. Even though the libraries // themselves will have been tested already, the test code could // use methods or classes defined in them. if (!binding.equals(Library.JNI) && !binding.equals(Library.NONE) && !binding.equals(Library.EXECUTABLE)) { // check if it exists in the normal unpack directory File dir = getLayout() .getLibDirectory(getUnpackDirectory(), dependency.getArtifactId(), dependency.getBaseVersion(), aol.toString(), binding); getLog().debug("Looking for Library Directory: " + dir); if (!dir.exists()) { getLog().debug("Library Directory " + dir + " does NOT exist."); // otherwise try the test unpack directory dir = getLayout() .getLibDirectory(getTestUnpackDirectory(), dependency.getArtifactId(), dependency.getBaseVersion(), aol.toString(), binding); getLog().debug("Looking for Library Directory: " + dir); } if (dir.exists()) { final LibrarySet libSet = new LibrarySet(); libSet.setProject(antProject); // FIXME, no way to override final String libs = dependency.getNarInfo().getLibs(getAOL()); if (libs != null && !libs.equals("")) { getLog().debug("Using LIBS = " + libs); libSet.setLibs(new CUtil.StringArrayBuilder(libs)); libSet.setDir(dir); task.addLibset(libSet); } } else { getLog().debug("Library Directory " + dir + " does NOT exist."); } // FIXME, look again at this, for multiple dependencies we may need to // remove duplicates final String options = dependency.getNarInfo().getOptions(getAOL()); if (options != null && !options.equals("")) { getLog().debug("Using OPTIONS = " + options); final LinkerArgument arg = new LinkerArgument(); arg.setValue(options); linkerDefinition.addConfiguredLinkerArg(arg); } final String sysLibs = dependency.getNarInfo().getSysLibs(getAOL()); if (sysLibs != null && !sysLibs.equals("")) { getLog().debug("Using SYSLIBS = " + sysLibs); final SystemLibrarySet sysLibSet = new SystemLibrarySet(); sysLibSet.setProject(antProject); sysLibSet.setLibs(new CUtil.StringArrayBuilder(sysLibs)); task.addSyslibset(sysLibSet); } } } // Add JVM to linker getJava().addRuntime(task, getJavaHome(getAOL()), getOS(), getAOL().getKey() + ".java."); // execute try { task.execute(); } catch (final BuildException e) { throw new MojoExecutionException("NAR: Test-Compile failed", e); } } /** * List the dependencies needed for tests compilations, those dependencies are * used to get the include paths needed * for compilation and to get the libraries paths and names needed for * linking. */ @Override protected ScopeFilter getArtifactScopeFilter() { // Was Artifact.SCOPE_TEST - runtime?? return new ScopeFilter( Artifact.SCOPE_TEST, null ); } @Override protected File getUnpackDirectory() { return getTestUnpackDirectory() == null ? super.getUnpackDirectory() : getTestUnpackDirectory(); } @Override public final void narExecute() throws MojoExecutionException, MojoFailureException { if (this.skipTests) { getLog().info("Not compiling test sources"); } else { // make sure destination is there getTestTargetDirectory().mkdirs(); for (final Object o : getTests()) { createTest(getAntProject(), (Test) o); } } } }