001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.cnd.api.compilers;
043:
044: import java.io.File;
045: import java.io.FilenameFilter;
046: import java.util.ArrayList;
047: import java.util.HashSet;
048: import java.util.Iterator;
049: import java.util.List;
050: import java.util.Set;
051: import java.util.regex.Pattern;
052: import java.util.regex.PatternSyntaxException;
053: import org.netbeans.modules.cnd.api.compilers.CompilerSet.CompilerFlavor;
054: import org.netbeans.modules.cnd.api.utils.IpeUtils;
055: import org.netbeans.modules.cnd.api.utils.Path;
056: import org.netbeans.modules.cnd.settings.CppSettings;
057: import org.openide.modules.ModuleInfo;
058: import org.openide.util.Lookup;
059: import org.openide.util.Utilities;
060:
061: /**
062: * Manage a set of CompilerSets. The CompilerSets are dynamically created based on which compilers
063: * are found in the user's $PATH variable.
064: */
065: public class CompilerSetManager {
066:
067: private static final String gcc_pattern = "([a-zA-z][a-zA-Z0-9_]*-)*gcc(([-.]\\d){2,4})?(\\.exe)?"; // NOI18N
068: private static final String gpp_pattern = "([a-zA-z][a-zA-Z0-9_]*-)*g\\+\\+(([-.]\\d){2,4})?(\\.exe)?$"; // NOI18N
069: private static final String cc_pattern = "([a-zA-z][a-zA-Z0-9_]*-)*cc(([-.]\\d){2,4})?(\\.exe)?$"; // NOI18N
070: private static final String CC_pattern = "([a-zA-z][a-zA-Z0-9_]*-)*CC(([-.]\\d){2,4})?$"; // NOI18N
071: private static final String fortran_pattern = "([a-zA-z][a-zA-Z0-9_]*-)*[fg](77|90|95|fortran)(([-.]\\d){2,4})?(\\.exe)?"; // NOI18N
072: private static final String make_pattern = "([a-zA-z][a-zA-Z0-9_]*-)*([dg]make|make)(([-.]\\d){2,4})?(\\.exe)?$"; // NOI18N
073: private static final String debugger_pattern = "([a-zA-z][a-zA-Z0-9_]*-)*(gdb|dbx)(([-.]\\d){2,4})?(\\.exe)?$"; // NOI18N
074:
075: /* Legacy defines for CND 5.5 compiler set definitions */
076: public static final int SUN_COMPILER_SET = 0;
077: public static final int GNU_COMPILER_SET = 1;
078:
079: public static final String Sun12 = "Sun12"; // NOI18N
080: public static final String Sun11 = "Sun11"; // NOI18N
081: public static final String Sun10 = "Sun10"; // NOI18N
082: public static final String Sun = "Sun"; // NOI18N
083: public static final String GNU = "GNU"; // NOI18N
084:
085: private CompilerFilenameFilter gcc_filter;
086: private CompilerFilenameFilter gpp_filter;
087: private CompilerFilenameFilter cc_filter;
088: private CompilerFilenameFilter CC_filter;
089: private CompilerFilenameFilter fortran_filter;
090: private CompilerFilenameFilter make_filter;
091: private CompilerFilenameFilter debugger_filter;
092:
093: private ArrayList<CompilerSet> sets = new ArrayList();
094: private ArrayList<String> dirlist;
095:
096: private static CompilerSetManager instance = null;
097: private static Set<CompilerSetChangeListener> listeners = new HashSet();
098:
099: public CompilerSetManager() {
100: dirlist = Path.getPath();
101: initCompilerFilters();
102: initCompilerSets();
103: }
104:
105: public CompilerSetManager(ArrayList dirlist) {
106: this .dirlist = dirlist;
107: initCompilerFilters();
108: initCompilerSets();
109: }
110:
111: public static CompilerSetManager getDefault() {
112: return getDefault(true);
113: }
114:
115: public static synchronized CompilerSetManager getDefault(
116: boolean doCreate) {
117: if (instance == null && doCreate) {
118: instance = new CompilerSetManager();
119: }
120: return instance;
121: }
122:
123: /**
124: * Replace the default CompilerSetManager. Let registered listeners know its been updated.
125: */
126: public static synchronized void setDefault(CompilerSetManager csm) {
127: instance = csm;
128: fireCompilerSetChangeNotification(csm);
129: }
130:
131: /** Search $PATH for all desired compiler sets and initialize cbCompilerSet and spCompilerSets */
132: private void initCompilerSets() {
133:
134: for (String path : dirlist) {
135: if (path.equals("/usr/ccs/bin")) { // NOI18N
136: // contains only softlinks
137: continue;
138: }
139: File dir = new File(path);
140: if (dir.isDirectory()) {
141: initCompiler(gcc_filter, "gcc", Tool.CCompiler, path); // NOI18N
142: initCompiler(gpp_filter, "g++", Tool.CCCompiler, path); // NOI18N
143: initCompiler(cc_filter, "cc", Tool.CCompiler, path); // NOI18N
144: initFortranCompiler(fortran_filter,
145: Tool.FortranCompiler, path); // NOI18N
146: if (Utilities.isUnix()) { // CC and cc are the same on Windows, so skip this step on Windows
147: initCompiler(CC_filter, "CC", Tool.CCCompiler, path); // NOI18N
148: }
149: initMakeTool(make_filter, Tool.MakeTool, path); // NOI18N
150: initDebuggerTool(debugger_filter, Tool.DebuggerTool,
151: path); // NOI18N
152: }
153: }
154: completeCompilerSets();
155: }
156:
157: private void initCompiler(CompilerFilenameFilter filter,
158: String best, int kind, String path) {
159: File dir = new File(path);
160: String[] list = dir.list(filter);
161:
162: if (list != null && list.length > 0) {
163: CompilerSet cs = CompilerSet.getCompilerSet(dir
164: .getAbsolutePath(), list);
165: add(cs);
166: for (String name : list) {
167: File file = new File(dir, name);
168: if (file.exists()
169: && (name.equals(best) || name.equals(best
170: + ".exe"))) { // NOI18N
171: cs.addTool(name, path, kind);
172: }
173: }
174: }
175: }
176:
177: private void initFortranCompiler(CompilerFilenameFilter filter,
178: int kind, String path) {
179: File dir = new File(path);
180: String[] list = dir.list(filter);
181: String[] best = { "gfortran", // NOI18N
182: "g77", // NOI18N
183: "f95", // NOI18N
184: "f90", // NOI18N
185: "f77", // NOI18N
186: };
187:
188: if (list != null && list.length > 0) {
189: CompilerSet cs = CompilerSet.getCompilerSet(dir
190: .getAbsolutePath(), list);
191: add(cs);
192: for (String name : list) {
193: File file = new File(dir, name);
194: if (file.exists()) {
195: for (int i = 0; i < best.length; i++) {
196: if (name.equals(best[i])
197: || name.equals(best[i] + ".exe")) { // NOI18N
198: cs.addTool(name, path, kind);
199: }
200: }
201: }
202: }
203: }
204: }
205:
206: private void initMakeTool(CompilerFilenameFilter filter, int kind,
207: String path) {
208: File dir = new File(path);
209: String[] list = dir.list(filter);
210: String[] best = { "dmake", // NOI18N
211: "gmake", // NOI18N
212: "make", // NOI18N
213: "make.exe", // NOI18N
214: };
215:
216: if (list != null && list.length > 0) {
217: CompilerSet cs = null;
218: if (path.contains("msys")) { // NOI18N
219: // use minGW cs
220: cs = getCompilerSet(CompilerFlavor.MinGW);
221: }
222: if (cs == null) {
223: cs = CompilerSet.getCompilerSet(dir.getAbsolutePath(),
224: list);
225: add(cs);
226: }
227: for (int i = 0; i < best.length; i++) {
228: File file = new File(dir, best[i]);
229: if (file.exists() && !file.isDirectory()) {
230: cs.addTool(best[i], path, kind);
231: return;
232: }
233: }
234: }
235: }
236:
237: private void initDebuggerTool(CompilerFilenameFilter filter,
238: int kind, String path) {
239: File dir = new File(path);
240: String[] list = dir.list(filter);
241: String[] best;
242: if (IpeUtils.isGdbEnabled()) {
243: best = new String[] { "gdb", "gdb.exe" }; // NOI18N
244: } else {
245: // Assume dbxgui
246: best = new String[] { "dbx" }; // NOI18N
247: }
248:
249: if (list != null && list.length > 0) {
250: CompilerSet cs = CompilerSet.getCompilerSet(dir
251: .getAbsolutePath(), list);
252: add(cs);
253: for (int i = 0; i < best.length; i++) {
254: File file = new File(dir, best[i]);
255: if (file.exists() && !file.isDirectory()) {
256: cs.addTool(best[i], path, kind);
257: return;
258: }
259: }
260: }
261: }
262:
263: /**
264: * If a compiler set doesn't have one of each compiler types, add a "No compiler"
265: * tool. If selected, this will tell the build validation things are OK.
266: */
267: private void completeCompilerSets() {
268: for (CompilerSet cs : sets) {
269: if (cs.getTool(Tool.CCompiler) == null) {
270: cs.addTool("", "", Tool.CCompiler); // NOI18N
271: }
272: if (cs.getTool(Tool.CCCompiler) == null) {
273: cs.addTool("", "", Tool.CCCompiler); // NOI18N
274: }
275: if (cs.getTool(Tool.FortranCompiler) == null) {
276: cs.addTool("", "", Tool.FortranCompiler); // NOI18N
277: }
278: if (cs.getTool(Tool.CustomTool) == null) {
279: cs.addTool("", "", Tool.CustomTool); // NOI18N
280: }
281: if (cs.findTool(Tool.MakeTool) == null) {
282: String path = Path.findCommand("make"); // NOI18N
283: if (path != null)
284: cs.addNewTool(IpeUtils.getBaseName(path), IpeUtils
285: .getDirName(path), Tool.MakeTool); // NOI18N
286: }
287: if (cs.getTool(Tool.MakeTool) == null) {
288: cs.addTool("", "", Tool.MakeTool); // NOI18N
289: }
290: if (cs.findTool(Tool.DebuggerTool) == null) {
291: String path;
292: if (IpeUtils.isGdbEnabled()) {
293: path = Path.findCommand("gdb"); // NOI18N
294: } else {
295: path = Path.findCommand("dbx"); // NOI18N
296: }
297: if (path != null)
298: cs.addNewTool(IpeUtils.getBaseName(path), IpeUtils
299: .getDirName(path), Tool.DebuggerTool); // NOI18N
300: }
301: if (cs.getTool(Tool.DebuggerTool) == null) {
302: cs.addTool("", "", Tool.DebuggerTool); // NOI18N
303: }
304: }
305:
306: if (sets.size() == 0) { // No compilers found
307: add(CompilerSet.createEmptyCompilerSet());
308: }
309: }
310:
311: private void initCompilerFilters() {
312: gcc_filter = new CompilerFilenameFilter(gcc_pattern);
313: gpp_filter = new CompilerFilenameFilter(gpp_pattern);
314: cc_filter = new CompilerFilenameFilter(cc_pattern);
315: fortran_filter = new CompilerFilenameFilter(fortran_pattern);
316: if (Utilities.isUnix()) {
317: CC_filter = new CompilerFilenameFilter(CC_pattern);
318: }
319: make_filter = new CompilerFilenameFilter(make_pattern);
320: debugger_filter = new CompilerFilenameFilter(debugger_pattern);
321: }
322:
323: /**
324: * Add a CompilerSet to this CompilerSetManager. Make sure it doesn't get added multiple times.
325: *
326: * @param cs The CompilerSet to (possibly) add
327: */
328: public void add(CompilerSet cs) {
329: String csdir = cs.getDirectory();
330:
331: if (sets.size() == 1
332: && sets.get(0).getName() == CompilerSet.None) {
333: sets.remove(0);
334: }
335: for (CompilerSet cs2 : sets) {
336: if (cs2.getDirectory().equals(csdir)) {
337: return;
338: }
339: }
340: sets.add(cs);
341: }
342:
343: /**
344: * Remove a CompilerSet from this CompilerSetManager. Use caution with this method. Its primary
345: * use is to remove temporary CompilerSets which were added to represent missing compiler sets. In
346: * that context, they're removed immediately after showing the ToolsPanel after project open.
347: *
348: * @param cs The CompilerSet to (possibly) remove
349: */
350: public void remove(CompilerSet cs) {
351: if (sets.contains(cs)) {
352: sets.remove(cs);
353: if (CppSettings.getDefault().getCompilerSetName().equals(
354: cs.getName())) {
355: CppSettings.getDefault().setCompilerSetName("");
356: }
357: if (this == instance) {
358: fireCompilerSetChangeNotification(instance);
359: }
360: }
361: if (sets.size() == 0) { // No compilers found
362: add(CompilerSet.createEmptyCompilerSet());
363: }
364: }
365:
366: public CompilerSet getCompilerSet(CompilerFlavor flavor) {
367: return getCompilerSet(flavor.toString());
368: }
369:
370: public CompilerSet getCompilerSet(String name) {
371: for (CompilerSet cs : sets) {
372: if (cs.getName().equals(name)) {
373: return cs;
374: }
375: }
376: return null;
377: }
378:
379: public CompilerSet getCompilerSetByDisplayName(String name) {
380: for (CompilerSet cs : sets) {
381: if (cs.getDisplayName().equals(name)) {
382: return cs;
383: }
384: }
385: return null;
386: }
387:
388: public CompilerSet getCompilerSet(String name, String dname) {
389: for (CompilerSet cs : sets) {
390: if (cs.getName().equals(name)
391: && cs.getDisplayName().equals(dname)) {
392: return cs;
393: }
394: }
395: return null;
396: }
397:
398: public CompilerSet getCompilerSet(int idx) {
399: assert idx >= 0 && idx < sets.size();
400: return sets.get(idx);
401: }
402:
403: public List<CompilerSet> getCompilerSets() {
404: return sets;
405: }
406:
407: public List<String> getCompilerSetDisplayNames() {
408: ArrayList<String> names = new ArrayList();
409: for (CompilerSet cs : getCompilerSets()) {
410: names.add(cs.getDisplayName());
411: }
412: return names;
413: }
414:
415: public List<String> getCompilerSetNames() {
416: ArrayList<String> names = new ArrayList();
417: for (CompilerSet cs : getCompilerSets()) {
418: names.add(cs.getName());
419: }
420: return names;
421: }
422:
423: /**
424: * Check if the gdb module is enabled. Don't show the gdb line if it isn't.
425: *
426: * @return true if the gdb module is enabled, false if missing or disabled
427: */
428: protected boolean isGdbEnabled() {
429: Iterator iter = Lookup.getDefault().lookup(
430: new Lookup.Template(ModuleInfo.class)).allInstances()
431: .iterator();
432: while (iter.hasNext()) {
433: ModuleInfo info = (ModuleInfo) iter.next();
434: if (info.getCodeNameBase().equals(
435: "org.netbeans.modules.cnd.debugger.gdb")
436: && info.isEnabled()) { // NOI18N
437: return true;
438: }
439: }
440: return false;
441: }
442:
443: public static void addCompilerSetChangeListener(
444: CompilerSetChangeListener l) {
445: listeners.add(l);
446: }
447:
448: public static void removeCompilerSetChangeListener(
449: CompilerSetChangeListener l) {
450: listeners.remove(l);
451: }
452:
453: private static void fireCompilerSetChangeNotification(
454: CompilerSetManager csm) {
455: for (CompilerSetChangeListener l : listeners) {
456: l.compilerSetChange(new CompilerSetEvent(csm));
457: }
458: }
459:
460: /** Special FilenameFilter which should recognize different variations of supported compilers */
461: private class CompilerFilenameFilter implements FilenameFilter {
462:
463: Pattern pc = null;
464:
465: public CompilerFilenameFilter(String pattern) {
466: try {
467: pc = Pattern.compile(pattern);
468: } catch (PatternSyntaxException ex) {
469: }
470: }
471:
472: public boolean accept(File dir, String name) {
473: return pc != null && pc.matcher(name).matches();
474: }
475: }
476: }
|