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.cpptasks.gcc;
21  
22  import java.io.File;
23  import java.util.Vector;
24  
25  import com.github.maven_nar.cpptasks.CCTask;
26  import com.github.maven_nar.cpptasks.CUtil;
27  import com.github.maven_nar.cpptasks.compiler.CaptureStreamHandler;
28  import com.github.maven_nar.cpptasks.compiler.LinkType;
29  import com.github.maven_nar.cpptasks.compiler.Linker;
30  import com.github.maven_nar.cpptasks.types.LibrarySet;
31  
32  /**
33   * Adapter for the g++ variant of the GCC linker
34   *
35   * @author Stephen M. Webb <stephen.webb@bregmasoft.com>
36   */
37  public class GppLinker extends AbstractLdLinker {
38    public static final String GPP_COMMAND = "g++";
39  
40    protected static final String[] discardFiles = new String[0];
41    protected static final String[] objFiles = new String[] {
42        ".o", ".a", ".lib", ".dll", ".so", ".sl"
43    };
44    private final static String libPrefix = "libraries: =";
45    protected static final String[] libtoolObjFiles = new String[] {
46        ".fo", ".a", ".lib", ".dll", ".so", ".sl"
47    };
48    private static String[] linkerOptions = new String[] {
49        "-bundle", "-dylib", "-dynamic", "-dynamiclib", "-nostartfiles", "-nostdlib", "-prebind", "-s", "-static",
50        "-shared", "-symbolic", "-Xlinker", "-static-libgcc", "-shared-libgcc", "-p", "-pg", "-pthread"
51    };
52    // FREEHEP refactored dllLinker into soLinker
53    private static final GppLinker soLinker = new GppLinker(GPP_COMMAND, objFiles, discardFiles, "lib", ".so", false,
54        new GppLinker(GPP_COMMAND, objFiles, discardFiles, "lib", ".so", true, null));
55    private static final GppLinker instance = new GppLinker(GPP_COMMAND, objFiles, discardFiles, "", "", false, null);
56    private static final GppLinker clangInstance = new GppLinker("clang", objFiles, discardFiles, "", "", false, null);
57    private static final GppLinker machDllLinker = new GppLinker(GPP_COMMAND, objFiles, discardFiles, "lib", ".dylib",
58        false, null);
59    private static final GppLinker machPluginLinker = new GppLinker(GPP_COMMAND, objFiles, discardFiles, "lib",
60        ".bundle", false, null);
61    // FREEHEP
62    private static final GppLinker machJNILinker = new GppLinker(GPP_COMMAND, objFiles, discardFiles, "lib", ".jnilib",
63        false, null);
64    // FREEHEP added dllLinker for windows
65    private static final GppLinker dllLinker = new GppLinker(GPP_COMMAND, objFiles, discardFiles, "", ".dll", false, null);
66  
67    public static GppLinker getCLangInstance() {
68      return clangInstance;
69    }
70  
71    public static GppLinker getInstance() {
72      return instance;
73    }
74  
75    private File[] libDirs;
76    private String runtimeLibrary;
77    // FREEEHEP
78    private String gccLibrary, gfortranLibrary, gfortranMainLibrary;
79  
80    protected GppLinker(final String command, final String[] extensions, final String[] ignoredExtensions,
81        final String outputPrefix, final String outputSuffix, final boolean isLibtool, final GppLinker libtoolLinker) {
82      super(command, "-dumpversion", extensions, ignoredExtensions, outputPrefix, outputSuffix, isLibtool, libtoolLinker);
83    }
84  
85    @Override
86    protected void addImpliedArgs(final CCTask task, final boolean debug, final LinkType linkType,
87        final Vector<String> args) {
88      super.addImpliedArgs(task, debug, linkType, args);
89      if (getIdentifier().contains("mingw")) {
90        if (linkType.isSubsystemConsole()) {
91          args.addElement("-mconsole");
92        }
93        if (linkType.isSubsystemGUI()) {
94          args.addElement("-mwindows");
95        }
96      }
97      // BEGINFREEHEP link or not with libstdc++
98      // for MacOS X see:
99      // http://developer.apple.com/documentation/DeveloperTools/Conceptual/CppRuntimeEnv/Articles/LibCPPDeployment.html
100     this.gfortranLibrary = null;
101     if (linkType.linkFortran()) {
102       if (linkType.isStaticRuntime()) {
103         final String[] cmdin = new String[] {
104             "gfortran", "-print-file-name=libgfortran.a"
105         };
106         final String[] cmdout = CaptureStreamHandler.run(cmdin);
107         if (cmdout.length > 0 && cmdout[0].indexOf('/') >= 0) {
108           this.gfortranLibrary = cmdout[0];
109         }
110       } else {
111         this.gfortranLibrary = "-lgfortran";
112       }
113     }
114 
115     this.gfortranMainLibrary = null;
116     if (linkType.linkFortran()) {
117       if (linkType.isExecutable() && linkType.linkFortranMain() && !isDarwin()) {
118         if (linkType.isStaticRuntime()) {
119           final String[] cmdin = new String[] {
120               "gfortran", "-print-file-name=libgfortranbegin.a"
121           };
122           final String[] cmdout = CaptureStreamHandler.run(cmdin);
123           if (cmdout.length > 0 && cmdout[0].indexOf('/') >= 0) {
124             this.gfortranMainLibrary = cmdout[0];
125           }
126         } else {
127           this.gfortranMainLibrary = "-lgfortranbegin";
128         }
129       }
130     }
131 
132     this.runtimeLibrary = null;
133     if (linkType.linkCPP()) {
134       if (linkType.isStaticRuntime()) {
135         if (isDarwin()) {
136           if (isClang()) {
137             task.log("Warning: clang cannot statically link to C++");
138           } else {
139             this.runtimeLibrary = "-lstdc++-static";
140           }
141         } else {
142           final String[] cmdin = new String[] {
143               "g++", "-print-file-name=libstdc++.a"
144           };
145           final String[] cmdout = CaptureStreamHandler.run(cmdin);
146           if (cmdout.length > 0 && cmdout[0].indexOf('/') >= 0) {
147             this.runtimeLibrary = cmdout[0];
148           }
149         }
150       } else {
151         this.runtimeLibrary = "-lstdc++";
152       }
153     }
154 
155     this.gccLibrary = null;
156     if (linkType.isStaticRuntime()) {
157       if (isClang()) {
158         task.log("Warning: clang cannot statically link libgcc");
159       } else {
160         this.gccLibrary = "-static-libgcc";
161       }
162     } else {
163       if (linkType.linkCPP()) {
164         // NOTE: added -fexceptions here for MacOS X
165         this.gccLibrary = "-fexceptions";
166       } else {
167         if (isClang()) {
168           task.log("Warning: clang cannot dynamically link libgcc");
169         } else {
170           this.gccLibrary = "-shared-libgcc";
171         }
172       }
173     }
174     // ENDFREEHEP
175   }
176 
177   @Override
178   protected String[] addLibrarySets(final CCTask task, final LibrarySet[] libsets, final Vector<String> preargs,
179       final Vector<String> midargs, final Vector<String> endargs) {
180     final String[] rs = super.addLibrarySets(task, libsets, preargs, midargs, endargs);
181     // BEGINFREEHEP
182     if (this.gfortranLibrary != null) {
183       endargs.addElement(this.gfortranLibrary);
184     }
185     if (this.gfortranMainLibrary != null) {
186       endargs.addElement(this.gfortranMainLibrary);
187     }
188     if (this.gccLibrary != null) {
189       endargs.addElement(this.gccLibrary);
190     }
191     // ENDFREEHEP
192     if (this.runtimeLibrary != null) {
193       endargs.addElement(this.runtimeLibrary);
194     }
195     return rs;
196   }
197 
198   /**
199    * Allows drived linker to decorate linker option. Override by GppLinker to
200    * prepend a "-Wl," to pass option to through gcc to linker.
201    * 
202    * @param buf
203    *          buffer that may be used and abused in the decoration process,
204    *          must not be null.
205    * @param arg
206    *          linker argument
207    */
208   @Override
209   public String decorateLinkerOption(final StringBuffer buf, final String arg) {
210     String decoratedArg = arg;
211     if (arg.length() > 1 && arg.charAt(0) == '-') {
212       switch (arg.charAt(1)) {
213       //
214       // passed automatically by GCC
215       //
216         case 'g':
217         case 'f':
218         case 'F':
219           /* Darwin */
220         case 'm':
221         case 'O':
222         case 'W':
223         case 'l':
224         case 'L':
225         case 'u':
226         case 'B':
227           break;
228         default:
229           boolean known = false;
230           for (final String linkerOption : linkerOptions) {
231             if (linkerOption.equals(arg)) {
232               known = true;
233               break;
234             }
235           }
236           if (!known) {
237             buf.setLength(0);
238             buf.append("-Wl,");
239             buf.append(arg);
240             decoratedArg = buf.toString();
241           }
242           break;
243       }
244     }
245     return decoratedArg;
246   }
247 
248   /**
249    * Returns library path.
250    * 
251    */
252   @Override
253   public File[] getLibraryPath() {
254     if (this.libDirs == null) {
255       final Vector<String> dirs = new Vector<>();
256       // Ask GCC where it will look for its libraries.
257       final String[] args = new String[] {
258           "g++", "-print-search-dirs"
259       };
260       final String[] cmdout = CaptureStreamHandler.run(args);
261       for (int i = 0; i < cmdout.length; ++i) {
262         final int prefixIndex = cmdout[i].indexOf(libPrefix);
263         if (prefixIndex >= 0) {
264           // Special case DOS-type GCCs like MinGW or Cygwin
265           int s = prefixIndex + libPrefix.length();
266           int t = cmdout[i].indexOf(';', s);
267           while (t > 0) {
268             dirs.addElement(cmdout[i].substring(s, t));
269             s = t + 1;
270             t = cmdout[i].indexOf(';', s);
271           }
272           dirs.addElement(cmdout[i].substring(s));
273           ++i;
274           for (; i < cmdout.length; ++i) {
275             dirs.addElement(cmdout[i]);
276           }
277         }
278       }
279       // Eliminate all but actual directories.
280       final String[] libpath = new String[dirs.size()];
281       dirs.copyInto(libpath);
282       final int count = CUtil.checkDirectoryArray(libpath);
283       // Build return array.
284       this.libDirs = new File[count];
285       int index = 0;
286       for (final String element : libpath) {
287         if (element != null) {
288           this.libDirs[index++] = new File(element);
289         }
290       }
291     }
292     return this.libDirs;
293   }
294 
295   @Override
296   public Linker getLinker(final LinkType type) {
297     if (type.isStaticLibrary()) {
298       return GccLibrarian.getInstance();
299     }
300     // BEGINFREEHEP
301     if (type.isJNIModule()) {
302       return isDarwin() ? machJNILinker : isWindows() ? dllLinker : soLinker;
303     }
304     if (type.isPluginModule()) {
305       return isDarwin() ? machPluginLinker : isWindows() ? dllLinker : soLinker;
306     }
307     if (type.isSharedLibrary()) {
308       return isDarwin() ? machDllLinker : isWindows() ? dllLinker : soLinker;
309     }
310     // ENDFREEHEP
311     return instance;
312   }
313 
314   /**
315    * Checks whether the compiler is actually clang masquerading as gcc
316    * (e.g., the situation on OS X 10.9 Mavericks).
317    */
318   private boolean isClang() {
319     final String command = getCommand();
320     if (command == null) {
321       return false;
322     }
323     if (command.startsWith("clang")) {
324       return true;
325     }
326     if (!GPP_COMMAND.equals(command)) {
327       return false;
328     }
329     final String[] cmd = {
330         command, "--version"
331     };
332     final String[] cmdout = CaptureStreamHandler.execute(cmd).getStdout();
333     return cmdout != null && cmdout.length > 0 && cmdout[0].contains("(clang-");
334   }
335 }