/* * #%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.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Vector; import com.github.maven_nar.cpptasks.*; import org.apache.maven.artifact.Artifact; import org.apache.maven.execution.MavenSession; 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.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 org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.StringUtils; import com.github.maven_nar.cpptasks.types.LibrarySet; import com.github.maven_nar.cpptasks.types.LinkerArgument; import com.github.maven_nar.cpptasks.types.SystemLibrarySet; /** * Compiles native source files. * * @requiresSession * @author Mark Donszelmann */ @Mojo(name = "nar-compile", defaultPhase = LifecyclePhase.COMPILE, requiresProject = true, requiresDependencyResolution = ResolutionScope.COMPILE) public class NarCompileMojo extends AbstractCompileMojo { /** * Specify that the final manifest should be embedded in the output (default * true) or false for side by side. */ @Parameter(property = "nar.embedManifest", defaultValue = "true") protected boolean embedManifest = true; /** * The current build session instance. */ @Component protected MavenSession session; private void copyInclude(final Compiler c) throws IOException, MojoExecutionException, MojoFailureException { if (c == null) { return; } c.copyIncludeFiles( getMavenProject(), getLayout().getIncludeDirectory(getTargetDirectory(), getMavenProject().getArtifactId(), getMavenProject().getVersion())); } private void createLibrary(final Project antProject, final Library library) throws MojoExecutionException, MojoFailureException { getLog().debug("Creating Library " + library); // configure task final CCTask task = new CCTask(); task.setCommandLogLevel(this.commandLogLevel); task.setProject(antProject); task.setDecorateLinkerOptions(this.decorateLinkerOptions); // subsystem final SubsystemEnum subSystem = new SubsystemEnum(); subSystem.setValue(library.getSubSystem()); task.setSubsystem(subSystem); // set max cores task.setMaxCores(getMaxCores(getAOL())); // outtype final OutputTypeEnum outTypeEnum = new OutputTypeEnum(); final String type = library.getType(); outTypeEnum.setValue(type); task.setOuttype(outTypeEnum); // stdc++ task.setLinkCPP(library.linkCPP()); // fortran task.setLinkFortran(library.linkFortran()); task.setLinkFortranMain(library.linkFortranMain()); // outDir File outDir; if (type.equals(Library.EXECUTABLE)) { outDir = getLayout().getBinDirectory(getTargetDirectory(), getMavenProject().getArtifactId(), getMavenProject().getVersion(), getAOL().toString()); } else { outDir = getLayout().getLibDirectory(getTargetDirectory(), getMavenProject().getArtifactId(), getMavenProject().getVersion(), getAOL().toString(), type); } outDir.mkdirs(); // outFile // FIXME NAR-90 we could get the final name from layout final File outFile = new File(outDir, getOutput(getAOL(), type)); getLog().debug("NAR - output: '" + outFile + "'"); task.setOutfile(outFile); // object directory File objDir = new File(getTargetDirectory(), "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); // IDL, MC, RC compilations should probably be 'generate source' type // actions, seperate from main build. // Needs resolution of handling for generate sources. // Order is somewhat important here, IDL and MC generate outputs that are // (often) included in the RC compilation if (getIdl() != null) { final CompilerDef idl = getIdl().getCompiler(Compiler.MAIN, null); if (idl != null) { task.addConfiguredCompiler(idl); task.createIncludePath().setPath(objDir.getPath()); // generated // 'sources' } } if (getMessage() != null) { final CompilerDef mc = getMessage().getCompiler(Compiler.MAIN, null); if (mc != null) { task.addConfiguredCompiler(mc); task.createIncludePath().setPath(objDir.getPath()); // generated // 'sources' } } if (getResource() != null) { final CompilerDef res = getResource().getCompiler(Compiler.MAIN, null); if (res != null) { task.addConfiguredCompiler(res); } } // Darren Sargent Feb 11 2010: Use Compiler.MAIN for "type"...appears the // wrong "type" variable was being used // since getCompiler() expects "main" or "test", whereas the "type" variable // here is "executable", "shared" etc. // add C++ compiler if (getCpp() != null) { final CompilerDef cpp = getCpp().getCompiler(Compiler.MAIN, null); if (cpp != null) { // Set FortifyID attribute cpp.setFortifyID(getfortifyID()); task.addConfiguredCompiler(cpp); } } // add C compiler if (getC() != null) { final CompilerDef c = getC().getCompiler(Compiler.MAIN, null); if (c != null) { // Set FortifyID attribute c.setFortifyID(getfortifyID()); task.addConfiguredCompiler(c); } } // add Fortran compiler if (getFortran() != null) { final CompilerDef fortran = getFortran().getCompiler(Compiler.MAIN, null); if (fortran != null) { task.addConfiguredCompiler(fortran); } } // Add VersionInfo for the Windows binaries if(getOS().equals( OS.WINDOWS ) && getLinker().getName( null, null ).equals( "msvc" )) { NARVersionInfo narVersioninfo = getNARVersionInfo() ; if(narVersioninfo != null) { VersionInfo versionInfo=narVersioninfo.getVersionInfo(getAntProject()); if(versionInfo != null) { task.addConfiguredVersioninfo(versionInfo); } } } // end Darren // add javah include path final File jniDirectory = getJavah().getJniDirectory(); if (jniDirectory.exists()) { task.createIncludePath().setPath(jniDirectory.getPath()); } // add java include paths getJava().addIncludePaths(task, type); getMsvc().configureCCTask(task); final List<NarArtifact> dependencies = getNarArtifacts(); // add dependency include paths for (final Object element : dependencies) { // FIXME, handle multiple includes from one NAR final NarArtifact narDependency = (NarArtifact) element; final String binding = getBinding(library, narDependency); getLog().debug("Looking for " + narDependency + " found binding " + binding); if (!binding.equals(Library.JNI)) { final File unpackDirectory = getUnpackDirectory(); final File include = getLayout().getIncludeDirectory(unpackDirectory, narDependency.getArtifactId(), narDependency.getBaseVersion()); getLog().debug("Looking for include directory: " + include); if (include.exists()) { task.createIncludePath().setPath(include.getPath()); } else { // Ideally includes are used from lib (static or shared) // however it's not required. // make a note in the log if something has gone wrong, // but don't block compilation getLog().warn(String.format("Unable to locate %1$s lib include path '%2$s'", binding, include)); } } } // add linker final LinkerDef linkerDefinition = getLinker().getLinker(this, task, getOS(), getAOL().getKey() + ".linker.", type); task.addConfiguredLinker(linkerDefinition); // add dependency libraries // FIXME: what about PLUGIN and STATIC, depending on STATIC, should we // not add all libraries, see NARPLUGIN-96 final boolean skipDepLink = linkerDefinition.isSkipDepLink(); if (((type.equals(Library.SHARED) || type.equals(Library.JNI) || type.equals(Library.EXECUTABLE))) && !skipDepLink) { final List depLibOrder = getDependencyLibOrder(); List depLibs = dependencies; // 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" // FIXME, no way to override this at this stage final String binding = dependency.getNarInfo().getBinding(getAOL(), Library.NONE); getLog().debug("Using Binding: " + binding); AOL aol = getAOL(); aol = dependency.getNarInfo().getAOL(getAOL()); getLog().debug("Using Library AOL: " + aol.toString()); if (!binding.equals(Library.JNI) && !binding.equals(Library.NONE) && !binding.equals(Library.EXECUTABLE)) { final File unpackDirectory = getUnpackDirectory(); final File dir = getLayout() .getLibDirectory(unpackDirectory, 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: Compile failed", e); } // FIXME, this should be done in CPPTasks at some point // getRuntime(getAOL()).equals("dynamic") && if ((isEmbedManifest() || getLinker().isGenerateManifest()) && getOS().equals(OS.WINDOWS) && getLinker().getName().equals("msvc") && !getLinker().getVersion(this).startsWith("6.")) { final String[] env = new String[] { "PATH=" + getMsvc().getPathVariable().getValue() }; final String libType = library.getType(); if (Library.JNI.equals(libType) || Library.SHARED.equals(libType) || Library.EXECUTABLE.equals(libType)) { Vector<String> commandlineArgs = new Vector<>(); commandlineArgs.add("/manifest"); getManifests(outFile.getPath(), commandlineArgs); if (commandlineArgs.size() == 1) { if (isEmbedManifest()) getLog().warn("Embed manifest requested, no source manifests to embed, no manifest generated"); } else { if (Library.JNI.equals(libType) || Library.SHARED.equals(libType)) { String dll = outFile.getPath() + ".dll"; if (isEmbedManifest()) { commandlineArgs.add("/outputresource:" + dll + ";#2"); } else { commandlineArgs.add("/out:" + dll + ".manifest"); } } else // if (Library.EXECUTABLE.equals( libType )) { String exe = outFile.getPath() + ".exe"; if (isEmbedManifest()) { commandlineArgs.add("/outputresource:" + exe + ";#1"); } else { commandlineArgs.add("/out:" + exe + ".manifest"); } } String[] commandlineArgsArray = commandlineArgs.toArray(new String[0]); String mtexe = "mt.exe"; if (getMsvc().compareVersion( getMsvc().getWindowsSdkVersion(),"7.0")<0 && getLinker().getVersion(this).startsWith("8.")) { // VS2005 VC8 only one that includes mt.exe File mtexeFile = new File(getMsvc().getToolPath(), mtexe); if (mtexeFile.exists()) mtexe = mtexeFile.getAbsolutePath(); } else { File mtexeFile = new File(getMsvc().getSDKToolPath(), mtexe); if (mtexeFile.exists()) mtexe = mtexeFile.getAbsolutePath(); } int result = NarUtil.runCommand(mtexe, commandlineArgsArray, null, null, getLog()); if (result != 0) { throw new MojoFailureException("MT.EXE failed with exit code: " + result); } } } } if( getOS().equals(OS.WINDOWS) && Library.STATIC.equals(library.getType()) ){ // option? should debug symbols always be provided. getLog().debug( "Copy static pdbs from intermediat dir to " + task.getOutfile().getParentFile() ); try { NarUtil.copyDirectoryStructure(task.getObjdir(), task.getOutfile().getParentFile(), "**/*.pdb", NarUtil.DEFAULT_EXCLUDES ); } catch (IOException e) { getLog().info( "Failed to copy pdbs from " + task.getObjdir() + "\nexception" + e.getMessage() ); } } } /** * List the dependencies needed for compilation, 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() { return new ScopeFilter(Artifact.SCOPE_COMPILE, null); } private List getSourcesFor(final Compiler compiler) throws MojoFailureException, MojoExecutionException { if (compiler == null) { return Collections.emptyList(); } try { final List files = new ArrayList(); final List srcDirs = compiler.getSourceDirectories(); for (final Object srcDir : srcDirs) { final File dir = (File) srcDir; if (dir.exists()) { files.addAll(FileUtils.getFiles(dir, StringUtils.join(compiler.getIncludes().iterator(), ","), null)); } } return files; } catch (final IOException e) { return Collections.emptyList(); } } @Override public final void narExecute() throws MojoExecutionException, MojoFailureException { // make sure destination is there getTargetDirectory().mkdirs(); // check for source files int noOfSources = 0; noOfSources += getSourcesFor(getCpp()).size(); noOfSources += getSourcesFor(getC()).size(); noOfSources += getSourcesFor(getFortran()).size(); if (noOfSources > 0) { getLog().info("Compiling " + noOfSources + " native files"); for (final Library library : getLibraries()) { createLibrary(getAntProject(), library); } } else { getLog().info("Nothing to compile"); } try { // FIXME, should the include paths be defined at a higher level ? copyInclude(getCpp()); copyInclude(getC()); copyInclude(getFortran()); } catch (final IOException e) { throw new MojoExecutionException("NAR: could not copy include files", e); } getNarInfo().writeToDirectory(this.classesDirectory); } public boolean isEmbedManifest() { return embedManifest; } private void getManifests(String generated, Vector<String> manifests) { // TODO: /manifest should be followed by the list of manifest files // - the one generated by link, any others provided in source. // search the source for .manifest files. if (getLinker().isGenerateManifest()) manifests.add(generated + ".manifest"); } }