View Javadoc

1   /*
2    * #%L
3    * Native ARchive plugin for Maven
4    * %%
5    * Copyright (C) 2002 - 2014 NAR Maven Plugin developers.
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   * 
11   * http://www.apache.org/licenses/LICENSE-2.0
12   * 
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package com.github.maven_nar;
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.util.Iterator;
25  import java.util.LinkedList;
26  import java.util.List;
27  
28  import org.apache.maven.artifact.Artifact;
29  import org.apache.maven.model.Profile;
30  import org.apache.maven.plugin.MojoExecutionException;
31  import org.apache.maven.plugin.MojoFailureException;
32  import org.apache.maven.plugins.annotations.LifecyclePhase;
33  import org.apache.maven.plugins.annotations.Mojo;
34  import org.apache.maven.plugins.annotations.ResolutionScope;
35  import org.apache.maven.shared.artifact.filter.collection.ScopeFilter;
36  import org.apache.tools.ant.BuildException;
37  import org.apache.tools.ant.Project;
38  
39  import com.github.maven_nar.cpptasks.CCTask;
40  import com.github.maven_nar.cpptasks.CUtil;
41  import com.github.maven_nar.cpptasks.CompilerDef;
42  import com.github.maven_nar.cpptasks.LinkerDef;
43  import com.github.maven_nar.cpptasks.OutputTypeEnum;
44  import com.github.maven_nar.cpptasks.RuntimeType;
45  import com.github.maven_nar.cpptasks.SubsystemEnum;
46  import com.github.maven_nar.cpptasks.ide.ProjectDef;
47  import com.github.maven_nar.cpptasks.ide.ProjectWriterEnum;
48  import com.github.maven_nar.cpptasks.types.DefineArgument;
49  import com.github.maven_nar.cpptasks.types.DefineSet;
50  import com.github.maven_nar.cpptasks.types.LibrarySet;
51  import com.github.maven_nar.cpptasks.types.LinkerArgument;
52  import com.github.maven_nar.cpptasks.types.SystemLibrarySet;
53  
54  /**
55   * Generates a Visual Studio 2005 project file (vcproj) Heavily inspired by
56   * NarCompileMojo.
57   *
58   * @author Darren Sargent
59   *
60   */
61  @Mojo(name = "nar-vcproj", defaultPhase = LifecyclePhase.GENERATE_RESOURCES,
62    requiresDependencyResolution = ResolutionScope.COMPILE)
63  public class NarVcprojMojo extends AbstractCompileMojo {
64  
65    // FIXME: code duplication with NarCompileMojo
66    private void createVcProjFile(final Project antProject, final Library library)
67        throws MojoExecutionException, MojoFailureException {
68  
69      // configure task
70      final CCTask task = new CCTask();
71      task.setProject(antProject);
72      task.setDebug(true);
73  
74      // subsystem (console)
75      final SubsystemEnum subSystem = new SubsystemEnum();
76      subSystem.setValue(library.getSubSystem());
77      task.setSubsystem(subSystem);
78  
79      // outtype
80      final OutputTypeEnum outTypeEnum = new OutputTypeEnum();
81      final String type = library.getType();
82      outTypeEnum.setValue(type);
83      task.setOuttype(outTypeEnum);
84  
85      // stdc++
86      task.setLinkCPP(library.linkCPP());
87  
88      // TODO: this should match the standard NAR location defined by layout
89      // similar to Nar Compile
90      // outDir
91      File outDir = new File(getTargetDirectory(), "bin");
92      outDir = new File(outDir, getAOL().toString());
93      outDir.mkdirs();
94  
95      // outFile
96      final File outFile = new File(outDir, getOutput(getAOL(), type));
97  
98      getLog().debug("NAR - output: '" + outFile + "'");
99      task.setOutfile(outFile);
100 
101     // object directory
102     File objDir = new File(getTargetDirectory(), "obj");
103     objDir = new File(objDir, getAOL().toString());
104     objDir.mkdirs();
105     task.setObjdir(objDir);
106 
107     // failOnError, libtool
108     task.setFailonerror(failOnError(getAOL()));
109     task.setLibtool(useLibtool(getAOL()));
110 
111     // runtime
112     final RuntimeType runtimeType = new RuntimeType();
113     runtimeType.setValue(getRuntime(getAOL()));
114     task.setRuntime(runtimeType);
115 
116     // add C++ compiler
117     final CompilerDef cpp = getCpp().getCompiler(Compiler.MAIN, null);
118     if (cpp != null) {
119       task.addConfiguredCompiler(cpp);
120     }
121 
122     // add VCPROJ_MOJO def (see UnitTestDriverImpl.cpp generated by Krusoe
123     // plugin)
124     final DefineSet defineSet = new DefineSet();
125     final DefineArgument defineArgument = new DefineArgument();
126     defineArgument.setName("VCPROJ_MOJO");
127     defineSet.addDefine(defineArgument);
128     cpp.addConfiguredDefineset(defineSet);
129 
130     // add javah include path
131     final File jniDirectory = getJavah().getJniDirectory();
132     if (jniDirectory.exists()) {
133       task.createIncludePath().setPath(jniDirectory.getPath());
134     }
135 
136     // add java include paths
137     getJava().addIncludePaths(task, Library.EXECUTABLE);
138 
139     getMsvc().configureCCTask(task);
140 
141     final List<NarArtifact> dependencies = getNarArtifacts();
142     // add dependency include paths
143     for (final Object element : dependencies) {
144       // FIXME, handle multiple includes from one NAR
145       final NarArtifact narDependency = (NarArtifact) element;
146       final String binding = narDependency.getNarInfo().getBinding(getAOL(), Library.STATIC);
147       getLog().debug("Looking for " + narDependency + " found binding " + binding);
148       if (!binding.equals(Library.JNI)) {
149         final File unpackDirectory = getUnpackDirectory();
150         final File include = getLayout().getIncludeDirectory(unpackDirectory, narDependency.getArtifactId(),
151             narDependency.getBaseVersion());
152         getLog().debug("Looking for directory: " + include);
153         if (include.exists()) {
154           task.createIncludePath().setPath(include.getPath());
155         } else {
156           getLog().warn(String.format("Unable to locate %1$s lib include path '%2$s'", binding, include));
157         }
158       }
159     }
160 
161     // add linker
162     final LinkerDef linkerDefinition = getLinker().getLinker(this, task, getOS(), getAOL().getKey() + ".linker.",
163         type);
164     task.addConfiguredLinker(linkerDefinition);
165 
166     // add dependency libraries
167     // FIXME: what about PLUGIN and STATIC, depending on STATIC, should we
168     // not add all libraries, see NARPLUGIN-96
169     if (type.equals(Library.SHARED) || type.equals(Library.JNI) || type.equals(Library.EXECUTABLE)) {
170 
171       final List depLibOrder = getDependencyLibOrder();
172       List depLibs = dependencies;
173 
174       // reorder the libraries that come from the nar dependencies
175       // to comply with the order specified by the user
176       if (depLibOrder != null && !depLibOrder.isEmpty()) {
177 
178         final List tmp = new LinkedList();
179 
180         for (final Object aDepLibOrder : depLibOrder) {
181 
182           final String depToOrderName = (String) aDepLibOrder;
183 
184           for (final Iterator j = depLibs.iterator(); j.hasNext(); ) {
185 
186             final NarArtifact dep = (NarArtifact) j.next();
187             final String depName = dep.getGroupId() + ":" + dep.getArtifactId();
188 
189             if (depName.equals(depToOrderName)) {
190 
191               tmp.add(dep);
192               j.remove();
193             }
194           }
195         }
196 
197         tmp.addAll(depLibs);
198         depLibs = tmp;
199       }
200 
201       for (final Object depLib : depLibs) {
202 
203         final NarArtifact dependency = (NarArtifact) depLib;
204 
205         // FIXME no handling of "local"
206 
207         // FIXME, no way to override this at this stage
208         final String binding = dependency.getNarInfo().getBinding(getAOL(), Library.STATIC);
209         getLog().debug("Using Binding: " + binding);
210         AOL aol = getAOL();
211         aol = dependency.getNarInfo().getAOL(getAOL());
212         getLog().debug("Using Library AOL: " + aol.toString());
213 
214         if (!binding.equals(Library.JNI) && !binding.equals(Library.NONE) && !binding.equals(Library.EXECUTABLE)) {
215           final File unpackDirectory = getUnpackDirectory();
216           final File dir = getLayout()
217               .getLibDirectory(unpackDirectory, dependency.getArtifactId(), dependency.getBaseVersion(), aol.toString(),
218                   binding);
219           getLog().debug("Looking for Library Directory: " + dir);
220           if (dir.exists()) {
221             final LibrarySet libSet = new LibrarySet();
222             libSet.setProject(antProject);
223 
224             // FIXME, no way to override
225             final String libs = dependency.getNarInfo().getLibs(getAOL());
226             if (libs != null && !libs.equals("")) {
227               getLog().debug("Using LIBS = " + libs);
228               libSet.setLibs(new CUtil.StringArrayBuilder(libs));
229               libSet.setDir(dir);
230               task.addLibset(libSet);
231             }
232           } else {
233             getLog().debug("Library Directory " + dir + " does NOT exist.");
234           }
235 
236           // FIXME, look again at this, for multiple dependencies we
237           // may need to remove duplicates
238           final String options = dependency.getNarInfo().getOptions(getAOL());
239           if (options != null && !options.equals("")) {
240             getLog().debug("Using OPTIONS = " + options);
241             final LinkerArgument arg = new LinkerArgument();
242             arg.setValue(options);
243             linkerDefinition.addConfiguredLinkerArg(arg);
244           }
245 
246           final String sysLibs = dependency.getNarInfo().getSysLibs(getAOL());
247 
248           if (sysLibs != null && !sysLibs.equals("")) {
249             getLog().debug("Using SYSLIBS = " + sysLibs);
250             final SystemLibrarySet sysLibSet = new SystemLibrarySet();
251             sysLibSet.setProject(antProject);
252 
253             sysLibSet.setLibs(new CUtil.StringArrayBuilder(sysLibs));
254 
255             task.addSyslibset(sysLibSet);
256           }
257         }
258       }
259 
260       // Add JVM to linker
261       getJava().addRuntime(task, getJavaHome(getAOL()), getOS(), getAOL().getKey() + "java.");
262 
263       // DS: generate project file
264       getLog().debug("NAR: Writing project file...");
265       final ProjectWriterEnum projectWriterEnum = new ProjectWriterEnum();
266       projectWriterEnum.setValue("msvc8");
267       final ProjectDef projectDef = new ProjectDef();
268       projectDef.setType(projectWriterEnum);
269       String filename = null;
270       try {
271         final File outputDir = new File(getTargetDirectory(), "vcproj");
272         if (!outputDir.exists()) {
273           final boolean succeeded = outputDir.mkdir();
274           if (!succeeded) {
275             throw new MojoExecutionException("Unable to create directory: " + outputDir);
276           }
277         }
278         filename = outputDir + "/" + getMavenProject().getArtifactId();
279         final File projFile = new File(filename);
280         projectDef.setOutfile(projFile.getCanonicalFile());
281       } catch (final IOException e) {
282         throw new MojoExecutionException("Unable to create file: " + filename, e);
283       }
284       task.addProject(projectDef);
285       task.setProjectsOnly(true);
286 
287       // we always want an EXE for debugging
288       task.setOuttype(new OutputTypeEnum());
289 
290       // execute
291       try {
292         task.execute();
293         getLog().info("Wrote project file: " + filename + ".vcproj");
294       } catch (final BuildException e) {
295         throw new MojoExecutionException("NAR: Compile failed", e);
296       }
297     }
298   }
299 
300   /**
301    * List the dependencies needed for compilation, those dependencies are used
302    * to get the include paths needed for
303    * compilation and to get the libraries paths and names needed for linking.
304    */
305   @Override
306   protected ScopeFilter getArtifactScopeFilter() {
307     return new ScopeFilter( Artifact.SCOPE_COMPILE, null );
308   }
309 
310   @Override
311   public void narExecute() throws MojoExecutionException, MojoFailureException {
312 
313     // Only do this if MSVC++ compiler is being used.
314     if (!getOS().equals(OS.WINDOWS)) {
315       getLog().debug("Skipping -- not running on Windows");
316       return;
317     }
318 
319     // need to run with profile "windows-debug". No other profiles are valid
320     // for vcproj generation.
321     boolean debug = false;
322 
323     final List profiles = NarUtil.collectActiveProfiles(getMavenProject());
324     for (final Object profile1 : profiles) {
325       final Profile profile = (Profile) profile1;
326       if (profile.getId().equalsIgnoreCase("windows-debug")) {
327         debug = true;
328         break;
329       }
330     }
331 
332     if (!debug) {
333       getLog().info("NAR: Skipping vcproj generation.  Run with -P windows-debug to enable this step.");
334       return;
335     }
336 
337     if (getLibraries().isEmpty()) {
338       getLog().info("NAR: Skipping vcproj generation.  No libraries to be built.");
339       return;
340     }
341 
342     // super.narExecute();
343 
344     // arbitrarily grab the first library -- we're going to make treat it as
345     // an exe anyway, whatever type it's supposed to be.
346     createVcProjFile(getAntProject(), getLibraries().get(0));
347   }
348 }