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.HashSet;
25  import java.util.LinkedList;
26  import java.util.List;
27  import java.util.Set;
28  import java.util.regex.Matcher;
29  import java.util.regex.Pattern;
30  
31  import org.apache.maven.plugin.MojoExecutionException;
32  import org.apache.maven.plugin.MojoFailureException;
33  import org.apache.maven.plugin.logging.Log;
34  import org.apache.maven.plugins.annotations.Parameter;
35  import org.apache.tools.ant.Project;
36  import org.codehaus.plexus.util.FileUtils;
37  
38  import com.github.maven_nar.cpptasks.CCTask;
39  import com.github.maven_nar.cpptasks.CUtil;
40  import com.github.maven_nar.cpptasks.LinkerDef;
41  import com.github.maven_nar.cpptasks.LinkerEnum;
42  import com.github.maven_nar.cpptasks.types.LibrarySet;
43  import com.github.maven_nar.cpptasks.types.LibraryTypeEnum;
44  import com.github.maven_nar.cpptasks.types.LinkerArgument;
45  import com.github.maven_nar.cpptasks.types.SystemLibrarySet;
46  
47  /**
48   * Linker tag
49   * 
50   * @author Mark Donszelmann
51   */
52  public class Linker {
53  
54    /**
55     * The Linker Some choices are: "msvc", "g++", "CC", "icpc", ... Default is
56     * Architecture-OS-Linker specific: FIXME:
57     * table missing
58     */
59    @Parameter
60    private String name;
61  
62    /**
63     * The prefix for the linker.
64     */
65    @Parameter
66    private String prefix;
67  
68    /**
69     * Path location of the linker tool
70     */
71    @Parameter
72    private String toolPath;
73  
74    /**
75     * Enables or disables incremental linking.
76     */
77    @Parameter(required = true)
78    private boolean incremental = false;
79  
80    /**
81     * Enables or disables the production of a map file.
82     */
83    @Parameter(required = true)
84    private boolean map = false;
85  
86    @Parameter(required = true)
87    private boolean skipDepLink = false;
88    
89    /**
90     * Options for the linker Defaults to Architecture-OS-Linker specific values.
91     * FIXME table missing
92     */
93    @Parameter
94    private List options;
95  
96    /**
97     * Additional options for the linker when running in the nar-testCompile
98     * phase.
99     * 
100    */
101   @Parameter
102   private List testOptions;
103 
104   /**
105    * Options for the linker as a whitespace separated list. Defaults to
106    * Architecture-OS-Linker specific values. Will
107    * work in combination with <options>.
108    */
109   @Parameter
110   private String optionSet;
111 
112   /**
113    * Clears default options
114    */
115   @Parameter(required = true)
116   private boolean clearDefaultOptions;
117 
118   /**
119    * Adds libraries to the linker.
120    */
121   @Parameter
122   private List/* <Lib> */ libs;
123 
124   /**
125    * Adds libraries to the linker. Will work in combination with &lt;libs&gt;.
126    * The format is comma separated,
127    * colon-delimited values (name:type:dir), like
128    * "myLib:shared:/home/me/libs/, otherLib:static:/some/path".
129    */
130   @Parameter
131   private String libSet;
132 
133   /**
134    * Adds system libraries to the linker.
135    */
136   @Parameter
137   private List/* <SysLib> */ sysLibs;
138 
139   /**
140    * Adds system libraries to the linker. Will work in combination with
141    * &lt;sysLibs&gt;. The format is comma
142    * separated, colon-delimited values (name:type), like
143    * "dl:shared, pthread:shared".
144    */
145   @Parameter
146   private String sysLibSet;
147 
148   /**
149    * <p>
150    * Specifies the link ordering of libraries that come from nar dependencies.
151    * The format is a comma separated list of dependency names, given as
152    * groupId:artifactId.
153    * </p>
154    * <p>
155    * Example: &lt;narDependencyLibOrder&gt;someGroup:myProduct,
156    * other.group:productB&lt;narDependencyLibOrder&gt;
157    * </p>
158    */
159   @Parameter
160   private String narDependencyLibOrder;
161 
162   /**
163    * Specify that the linker should generate an intermediate manifest based on
164    * the inputs.
165    */
166   @Parameter(property = "nar.generateManifest", defaultValue = "true")
167   private boolean generateManifest = true;
168 
169   private final Log log;
170 
171   public Linker() {
172     // default constructor for use as TAG
173     this(null);
174   }
175 
176   public Linker(final Log log) {
177     this.log = log;
178   }
179 
180   /**
181    * For use with specific named linker.
182    * 
183    * @param name
184    */
185   public Linker(final String name, final Log log) {
186     this.name = name;
187     this.log = log;
188   }
189 
190   private void addLibraries(final String libraryList, final LinkerDef linker, final Project antProject,
191       final boolean isSystem) {
192 
193     if (libraryList == null) {
194       return;
195     }
196 
197     final String[] lib = libraryList.split(",");
198 
199     for (final String element : lib) {
200 
201       final String[] libInfo = element.trim().split(":", 3);
202 
203       LibrarySet librarySet = new LibrarySet();
204 
205       if (isSystem) {
206         librarySet = new SystemLibrarySet();
207       }
208 
209       librarySet.setProject(antProject);
210       librarySet.setLibs(new CUtil.StringArrayBuilder(libInfo[0]));
211 
212       if (libInfo.length > 1) {
213 
214         final LibraryTypeEnum libType = new LibraryTypeEnum();
215 
216         libType.setValue(libInfo[1]);
217         librarySet.setType(libType);
218 
219         if (!isSystem && libInfo.length > 2) {
220           librarySet.setDir(new File(libInfo[2]));
221         }
222       }
223 
224       if (!isSystem) {
225         linker.addLibset(librarySet);
226       } else {
227         linker.addSyslibset((SystemLibrarySet) librarySet);
228       }
229     }
230   }
231 
232   /**
233    *  
234    **/
235   public boolean isGenerateManifest() {
236     return generateManifest;
237   }
238 
239   public final LinkerDef getLinker(final AbstractCompileMojo mojo, final CCTask task, final String os,
240       final String prefix, final String type) throws MojoFailureException, MojoExecutionException {
241     Project antProject = task.getProject();
242     if (this.name == null) {
243       throw new MojoFailureException("NAR: Please specify a <Name> as part of <Linker>");
244     }
245 
246     final LinkerDef linker = new LinkerDef();
247     linker.setProject(antProject);
248     final LinkerEnum linkerEnum = new LinkerEnum();
249     linkerEnum.setValue(this.name);
250     linker.setName(linkerEnum);
251 
252     // tool path
253     if (this.toolPath != null) {
254       linker.setToolPath(this.toolPath);
255     } else if ("msvc".equalsIgnoreCase(name)) {
256       linker.setToolPath(mojo.getMsvc().getToolPath());
257     }
258 
259     linker.setSkipDepLink(this.skipDepLink);
260     
261     // incremental, map
262     linker.setLinkerPrefix(this.prefix);
263     linker.setIncremental(this.incremental);
264     linker.setMap(this.map);
265 
266     // Add definitions (Window only)
267     if (os.equals(OS.WINDOWS) && getName(null, null).equals("msvc")
268         && (type.equals(Library.SHARED) || type.equals(Library.JNI))) {
269       final Set defs = new HashSet();
270       try {
271         if (mojo.getC() != null) {
272           final List cSrcDirs = mojo.getC().getSourceDirectories();
273           for (final Object cSrcDir : cSrcDirs) {
274             final File dir = (File) cSrcDir;
275             if (dir.exists()) {
276               defs.addAll(FileUtils.getFiles(dir, "**/*.def", null));
277             }
278           }
279         }
280       } catch (final IOException e) {
281       }
282       try {
283         if (mojo.getCpp() != null) {
284           final List cppSrcDirs = mojo.getCpp().getSourceDirectories();
285           for (final Object cppSrcDir : cppSrcDirs) {
286             final File dir = (File) cppSrcDir;
287             if (dir.exists()) {
288               defs.addAll(FileUtils.getFiles(dir, "**/*.def", null));
289             }
290           }
291         }
292       } catch (final IOException e) {
293       }
294       try {
295         if (mojo.getFortran() != null) {
296           final List fortranSrcDirs = mojo.getFortran().getSourceDirectories();
297           for (final Object fortranSrcDir : fortranSrcDirs) {
298             final File dir = (File) fortranSrcDir;
299             if (dir.exists()) {
300               defs.addAll(FileUtils.getFiles(dir, "**/*.def", null));
301             }
302           }
303         }
304       } catch (final IOException e) {
305       }
306 
307       for (final Object def : defs) {
308         final LinkerArgument arg = new LinkerArgument();
309         arg.setValue("/def:" + def);
310         linker.addConfiguredLinkerArg(arg);
311       }
312     }
313 
314     // FIXME, this should be done in CPPTasks at some point, and may not be
315     // necessary, but was for VS 2010 beta 2
316     if (os.equals(OS.WINDOWS) && getName(null, null).equals("msvc") && !getVersion(mojo).startsWith("6.")
317         && (type.equals(Library.SHARED) || type.equals(Library.JNI) || type.equals(Library.EXECUTABLE))) {
318       final LinkerArgument arg = new LinkerArgument();
319       if (isGenerateManifest())
320         arg.setValue("/MANIFEST");
321       else
322         arg.setValue("/MANIFEST:NO");
323       linker.addConfiguredLinkerArg(arg);
324 
325       if (isGenerateManifest()) {
326         final LinkerArgument arg2 = new LinkerArgument();
327         arg2.setValue("/MANIFESTFILE:" + task.getOutfile() + ".manifest");
328         linker.addConfiguredLinkerArg(arg2);
329       }
330 
331     }
332 
333     // Add options to linker
334     if (this.options != null) {
335       for (final Object option : this.options) {
336         final LinkerArgument arg = new LinkerArgument();
337         arg.setValue((String) option);
338         linker.addConfiguredLinkerArg(arg);
339       }
340     }
341 
342     if (this.optionSet != null) {
343 
344       final String[] opts = this.optionSet.split("\\s");
345 
346       for (final String opt : opts) {
347 
348         final LinkerArgument arg = new LinkerArgument();
349 
350         arg.setValue(opt);
351         linker.addConfiguredLinkerArg(arg);
352       }
353     }
354 
355     if (!this.clearDefaultOptions) {
356       final String option = NarProperties.getInstance(mojo.getMavenProject()).getProperty(prefix + "options");
357       if (option != null) {
358         final String[] opt = option.split(" ");
359         for (final String element : opt) {
360           final LinkerArgument arg = new LinkerArgument();
361           arg.setValue(element);
362           linker.addConfiguredLinkerArg(arg);
363         }
364       }
365     }
366 
367     // record the preference for nar dependency library link order
368     if (this.narDependencyLibOrder != null) {
369 
370       final List libOrder = new LinkedList();
371 
372       final String[] lib = this.narDependencyLibOrder.split(",");
373 
374       for (final String element : lib) {
375         libOrder.add(element.trim());
376       }
377 
378       mojo.setDependencyLibOrder(libOrder);
379     }
380 
381     // Add Libraries to linker
382     if (this.libs != null || this.libSet != null) {
383 
384       if (this.libs != null) {
385 
386         for (final Object lib1 : this.libs) {
387 
388           final Lib lib = (Lib) lib1;
389           lib.addLibSet(mojo, linker, antProject);
390         }
391       }
392 
393       if (this.libSet != null) {
394         addLibraries(this.libSet, linker, antProject, false);
395       }
396     } else {
397 
398       final String libsList = NarProperties.getInstance(mojo.getMavenProject()).getProperty(prefix + "libs");
399 
400       addLibraries(libsList, linker, antProject, false);
401     }
402 
403     // Add System Libraries to linker
404     if (this.sysLibs != null || this.sysLibSet != null) {
405 
406       if (this.sysLibs != null) {
407 
408         for (final Object sysLib1 : this.sysLibs) {
409 
410           final SysLib sysLib = (SysLib) sysLib1;
411           linker.addSyslibset(sysLib.getSysLibSet(antProject));
412         }
413       }
414 
415       if (this.sysLibSet != null) {
416         addLibraries(this.sysLibSet, linker, antProject, true);
417       }
418     } else {
419 
420       final String sysLibsList = NarProperties.getInstance(mojo.getMavenProject()).getProperty(prefix + "sysLibs");
421 
422       addLibraries(sysLibsList, linker, antProject, true);
423     }
424 
425     mojo.getMsvc().configureLinker(linker);
426 
427     return linker;
428   }
429 
430   public final String getName() {
431     return this.name;
432   }
433 
434   public final String getName(final NarProperties properties, final String prefix)
435       throws MojoFailureException, MojoExecutionException {
436     if (this.name == null && properties != null && prefix != null) {
437       this.name = properties.getProperty(prefix + "linker");
438     }
439     if (this.name == null) {
440       throw new MojoExecutionException("NAR: One of two things may be wrong here:\n\n"
441           + "1. <Name> tag is missing inside the <Linker> tag of your NAR configuration\n\n"
442           + "2. no linker is defined in the aol.properties file for '" + prefix + "linker'\n");
443     }
444     return this.name;
445   }
446 
447   /**
448    * @return The standard Linker configuration with 'testOptions' added to the
449    *         argument list.
450    */
451   public final LinkerDef getTestLinker(final AbstractCompileMojo mojo, final CCTask task, final String os,
452       final String prefix, final String type) throws MojoFailureException, MojoExecutionException {
453     final LinkerDef linker = getLinker(mojo, task, os, prefix, type);
454     if (this.testOptions != null) {
455       for (final Object testOption : this.testOptions) {
456         final LinkerArgument arg = new LinkerArgument();
457         arg.setValue((String) testOption);
458         linker.addConfiguredLinkerArg(arg);
459       }
460     }
461     return linker;
462   }
463 
464   public final String getVersion() throws MojoFailureException, MojoExecutionException {
465     return getVersion(new NarCompileMojo());
466   }
467 
468   public final String getVersion(final AbstractNarMojo mojo) throws MojoFailureException, MojoExecutionException {
469     if (this.name == null) {
470       throw new MojoFailureException("Cannot deduce linker version if name is null");
471     }
472 
473     String version = null;
474     String linkerPrefix = "";
475 
476     if (this.prefix != null && (!this.prefix.isEmpty())) {
477       linkerPrefix = this.prefix;
478     }
479 
480     final TextStream out = new StringTextStream();
481     final TextStream err = new StringTextStream();
482     final TextStream dbg = new StringTextStream();
483 
484     if (this.name.equals("g++") || this.name.equals("gcc")) {
485       NarUtil.runCommand(linkerPrefix+"gcc", new String[] {
486         "--version"
487       }, null, null, out, err, dbg, this.log);
488       final Pattern p = Pattern.compile("\\d+\\.\\d+\\.\\d+");
489       final Matcher m = p.matcher(out.toString());
490       if (m.find()) {
491         version = m.group(0);
492       }
493     } else if (this.name.equals("msvc")) {
494       version = mojo.getMsvc().getVersion();
495     } else if (this.name.equals("icc") || this.name.equals("icpc")) {
496       NarUtil.runCommand("icc", new String[] {
497           "--version"
498       }, null, null, out, err, dbg, this.log);
499       final Pattern p = Pattern.compile("\\d+\\.\\d+");
500       final Matcher m = p.matcher(out.toString());
501       if (m.find()) {
502         version = m.group(0);
503       }
504     } else if (this.name.equals("icl")) {
505       NarUtil.runCommand("icl", new String[] {
506           "/QV"
507       }, null, null, out, err, dbg, this.log);
508       final Pattern p = Pattern.compile("\\d+\\.\\d+");
509       final Matcher m = p.matcher(err.toString());
510       if (m.find()) {
511         version = m.group(0);
512       }
513     } else if (this.name.equals("CC")) {
514       NarUtil.runCommand("CC", new String[] {
515           "-V"
516       }, null, null, out, err, dbg, this.log);
517       final Pattern p = Pattern.compile("\\d+\\.d+");
518       final Matcher m = p.matcher(err.toString());
519       if (m.find()) {
520         version = m.group(0);
521       }
522     } else if (this.name.equals("xlC")) {
523       NarUtil.runCommand("/usr/vacpp/bin/xlC", new String[] {
524           "-qversion"
525       }, null, null, out, err, dbg, this.log);
526       final Pattern p = Pattern.compile("\\d+\\.\\d+");
527       final Matcher m = p.matcher(out.toString());
528       if (m.find()) {
529         version = m.group(0);
530       }
531     } else if (name.equals("clang") || name.equals("clang++")) {
532       NarUtil.runCommand("clang", new String[] {
533           "--version"
534       }, null, null, out, err, dbg, log);
535       final Pattern p = Pattern.compile("\\d+\\.\\d+\\.\\d+");
536       final Matcher m = p.matcher(out.toString());
537       if (m.find()) {
538         version = m.group(0);
539       }
540     } else {
541       if (!this.prefix.isEmpty()) {
542         NarUtil.runCommand(linkerPrefix+this.name, new String[] {
543           "--version"
544         }, null, null, out, err, dbg, this.log);
545         final Pattern p = Pattern.compile("\\d+\\.\\d+\\.\\d+");
546         final Matcher m = p.matcher(out.toString());
547         if (m.find()) {
548           version = m.group(0);
549         }
550       } else {
551         throw new MojoFailureException("Cannot find version number for linker '" + this.name + "'");
552       }
553     }
554 
555     if (version == null) {
556       if (!err.toString().isEmpty())
557         mojo.getLog().debug("linker returned error stream: " + err.toString());
558       throw new MojoFailureException("Cannot deduce version number from: " + out.toString());
559     }
560     return version;
561   }
562 }