Implement the correct search for configuration files. llvmc will now try
[oota-llvm.git] / tools / llvmc / Configuration.cpp
1 //===- Configuration.cpp - Configuration Data Mgmt --------------*- C++ -*-===//
2 // 
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file was developed by Reid Spencer and is distributed under the 
6 // University of Illinois Open Source License. See LICENSE.TXT for details.
7 // 
8 //===----------------------------------------------------------------------===//
9 //
10 // This file implements the parsing of configuration files for the LLVM Compiler
11 // Driver (llvmc).
12 //
13 //===------------------------------------------------------------------------===
14
15 #include "Configuration.h"
16 #include "ConfigLexer.h"
17 #include "CompilerDriver.h"
18 #include "Config/config.h"
19 #include "Support/CommandLine.h"
20 #include "Support/StringExtras.h"
21 #include <iostream>
22 #include <fstream>
23
24 using namespace llvm;
25
26 namespace sys {
27   // From CompilerDriver.cpp (for now)
28   extern bool FileReadable(const std::string& fname);
29 }
30
31 namespace llvm {
32   ConfigLexerInfo ConfigLexerState;
33   InputProvider* ConfigLexerInput = 0;
34
35   InputProvider::~InputProvider() {}
36   void InputProvider::error(const std::string& msg) {
37     std::cerr << name << ":" << ConfigLexerState.lineNum << ": Error: " << 
38       msg << "\n";
39     errCount++;
40   }
41
42   void InputProvider::checkErrors() {
43     if (errCount > 0) {
44       std::cerr << name << " had " << errCount << " errors. Terminating.\n";
45       exit(errCount);
46     }
47   }
48
49 }
50
51 namespace {
52
53   class FileInputProvider : public InputProvider {
54     public:
55       FileInputProvider(const std::string & fname)
56         : InputProvider(fname) 
57         , F(fname.c_str()) {
58         ConfigLexerInput = this;
59       }
60       virtual ~FileInputProvider() { F.close(); ConfigLexerInput = 0; }
61       virtual unsigned read(char *buffer, unsigned max_size) {
62         if (F.good()) {
63           F.read(buffer,max_size);
64           if ( F.gcount() ) return F.gcount() - 1;
65         }
66         return 0;
67       }
68
69       bool okay() { return F.good(); }
70     private:
71       std::ifstream F;
72   };
73
74   cl::opt<bool> DumpTokens("dump-tokens", cl::Optional, cl::Hidden, cl::init(false),
75     cl::desc("Dump lexical tokens (debug use only)."));
76
77   struct Parser
78   {
79     Parser() {
80       token = EOFTOK;
81       provider = 0;
82       confDat = 0;
83       ConfigLexerState.lineNum = 1;
84       ConfigLexerState.in_value = false;
85       ConfigLexerState.StringVal.clear();
86       ConfigLexerState.IntegerVal = 0;
87     };
88
89     ConfigLexerTokens token;
90     InputProvider* provider;
91     CompilerDriver::ConfigData* confDat;
92
93     int next() { 
94       token = Configlex();
95       if (DumpTokens) 
96         std::cerr << token << "\n";
97       return token;
98     }
99
100     bool next_is_real() { 
101       next();
102       return (token != EOLTOK) && (token != ERRORTOK) && (token != 0);
103     }
104
105     void eatLineRemnant() {
106       while (next_is_real()) ;
107     }
108
109     void error(const std::string& msg, bool skip = true) {
110       provider->error(msg);
111       if (skip)
112         eatLineRemnant();
113     }
114
115     std::string parseName() {
116       std::string result;
117       if (next() == EQUALS) {
118         while (next_is_real()) {
119           switch (token ) {
120             case STRING :
121             case OPTION : 
122               result += ConfigLexerState.StringVal + " ";
123               break;
124             default:
125               error("Invalid name");
126               break;
127           }
128         }
129         if (result.empty())
130           error("Name exepected");
131         else
132           result.erase(result.size()-1,1);
133       } else
134         error("= expected");
135       return result;
136     }
137
138     bool parseBoolean() {
139       bool result = true;
140       if (next() == EQUALS) {
141         if (next() == FALSETOK) {
142           result = false;
143         } else if (token != TRUETOK) {
144           error("Expecting boolean value");
145           return false;
146         }
147         if (next() != EOLTOK && token != 0) {
148           error("Extraneous tokens after boolean");
149         }
150       }
151       else
152         error("Expecting '='");
153       return result;
154     }
155
156     bool parseSubstitution(CompilerDriver::StringVector& optList) {
157       switch (token) {
158         case IN_SUBST:          optList.push_back("@in@"); break;
159         case OUT_SUBST:         optList.push_back("@out@"); break;
160         case TIME_SUBST:        optList.push_back("@time@"); break;
161         case STATS_SUBST:       optList.push_back("@stats@"); break;
162         case OPT_SUBST:         optList.push_back("@opt@"); break;
163         case TARGET_SUBST:      optList.push_back("@target@"); break;
164         default:
165           return false;
166       }
167       return true;
168     }
169
170     void parseOptionList(CompilerDriver::StringVector& optList ) {
171       if (next() == EQUALS) {
172         while (next_is_real()) {
173           if (token == STRING || token == OPTION)
174             optList.push_back(ConfigLexerState.StringVal);
175           else if (!parseSubstitution(optList)) {
176             error("Expecting a program argument or substitution", false);
177             break;
178           }
179         }
180       } else
181         error("Expecting '='");
182     }
183
184     void parseLang() {
185       switch (next() ) {
186         case NAME: 
187           confDat->langName = parseName(); 
188           break;
189         case OPT1: 
190           parseOptionList(confDat->opts[CompilerDriver::OPT_FAST_COMPILE]); 
191           break;
192         case OPT2: 
193           parseOptionList(confDat->opts[CompilerDriver::OPT_SIMPLE]); 
194           break;
195         case OPT3: 
196           parseOptionList(confDat->opts[CompilerDriver::OPT_AGGRESSIVE]); 
197           break;
198         case OPT4: 
199           parseOptionList(confDat->opts[CompilerDriver::OPT_LINK_TIME]); 
200           break;
201         case OPT5: 
202           parseOptionList(
203             confDat->opts[CompilerDriver::OPT_AGGRESSIVE_LINK_TIME]);
204           break;
205         default:   
206           error("Expecting 'name' or 'optN' after 'lang.'"); 
207           break;
208       }
209     }
210
211     void parseCommand(CompilerDriver::Action& action) {
212       if (next() == EQUALS) {
213         if (next() == EOLTOK) {
214           // no value (valid)
215           action.program.clear();
216           action.args.clear();
217         } else {
218           if (token == STRING || token == OPTION) {
219             action.program = ConfigLexerState.StringVal;
220           } else {
221             error("Expecting a program name");
222           }
223           while (next_is_real()) {
224             if (token == STRING || token == OPTION) {
225               action.args.push_back(ConfigLexerState.StringVal);
226             } else if (!parseSubstitution(action.args)) {
227               error("Expecting a program argument or substitution", false);
228               break;
229             }
230           }
231         }
232       }
233     }
234
235     void parsePreprocessor() {
236       switch (next()) {
237         case COMMAND:
238           parseCommand(confDat->PreProcessor);
239           break;
240         case REQUIRED:
241           if (parseBoolean())
242             confDat->PreProcessor.set(CompilerDriver::REQUIRED_FLAG);
243           else
244             confDat->PreProcessor.clear(CompilerDriver::REQUIRED_FLAG);
245           break;
246         default:
247           error("Expecting 'command' or 'required'");
248           break;
249       }
250     }
251
252     void parseTranslator() {
253       switch (next()) {
254         case COMMAND: 
255           parseCommand(confDat->Translator);
256           break;
257         case REQUIRED:
258           if (parseBoolean())
259             confDat->Translator.set(CompilerDriver::REQUIRED_FLAG);
260           else
261             confDat->Translator.clear(CompilerDriver::REQUIRED_FLAG);
262           break;
263         case PREPROCESSES:
264           if (parseBoolean())
265             confDat->Translator.set(CompilerDriver::PREPROCESSES_FLAG);
266           else 
267             confDat->Translator.clear(CompilerDriver::PREPROCESSES_FLAG);
268           break;
269         case OPTIMIZES:
270           if (parseBoolean())
271             confDat->Translator.set(CompilerDriver::OPTIMIZES_FLAG);
272           else
273             confDat->Translator.clear(CompilerDriver::OPTIMIZES_FLAG);
274           break;
275         case GROKS_DASH_O:
276           if (parseBoolean())
277             confDat->Translator.set(CompilerDriver::GROKS_DASH_O_FLAG);
278           else
279             confDat->Translator.clear(CompilerDriver::GROKS_DASH_O_FLAG);
280           break;
281         case OUTPUT_IS_ASM:
282           if (parseBoolean())
283             confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
284           else
285             confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
286           break;
287
288         default:
289           error("Expecting 'command', 'required', 'preprocesses', "
290                 "'groks_dash_O' or 'optimizes'");
291           break;
292       }
293     }
294
295     void parseOptimizer() {
296       switch (next()) {
297         case COMMAND:
298           parseCommand(confDat->Optimizer);
299           break;
300         case PREPROCESSES:
301           if (parseBoolean())
302             confDat->Optimizer.set(CompilerDriver::PREPROCESSES_FLAG);
303           else
304             confDat->Optimizer.clear(CompilerDriver::PREPROCESSES_FLAG);
305           break;
306         case TRANSLATES:
307           if (parseBoolean())
308             confDat->Optimizer.set(CompilerDriver::TRANSLATES_FLAG);
309           else
310             confDat->Optimizer.clear(CompilerDriver::TRANSLATES_FLAG);
311           break;
312         case GROKS_DASH_O:
313           if (parseBoolean())
314             confDat->Optimizer.set(CompilerDriver::GROKS_DASH_O_FLAG);
315           else
316             confDat->Optimizer.clear(CompilerDriver::GROKS_DASH_O_FLAG);
317           break;
318         case OUTPUT_IS_ASM:
319           if (parseBoolean())
320             confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
321           else
322             confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
323           break;
324         default:
325           error("Expecting 'command' or 'groks_dash_O'");
326           break;
327       }
328     }
329
330     void parseAssembler() {
331       switch(next()) {
332         case COMMAND:
333           parseCommand(confDat->Assembler);
334           break;
335         default:
336           error("Expecting 'command'");
337           break;
338       }
339     }
340
341     void parseLinker() {
342       switch(next()) {
343         case COMMAND:
344           parseCommand(confDat->Linker);
345           break;
346         case GROKS_DASH_O:
347           if (parseBoolean())
348             confDat->Linker.set(CompilerDriver::GROKS_DASH_O_FLAG);
349           else
350             confDat->Linker.clear(CompilerDriver::GROKS_DASH_O_FLAG);
351           break;
352         default:
353           error("Expecting 'command'");
354           break;
355       }
356     }
357
358     void parseAssignment() {
359       switch (token) {
360         case LANG:          parseLang(); break;
361         case PREPROCESSOR:  parsePreprocessor(); break;
362         case TRANSLATOR:    parseTranslator(); break;
363         case OPTIMIZER:     parseOptimizer(); break;
364         case ASSEMBLER:     parseAssembler(); break;
365         case LINKER:        parseLinker(); break;
366         case EOLTOK:        break; // just ignore
367         case ERRORTOK:
368         default:          
369           error("Invalid top level configuration item");
370           break;
371       }
372     }
373
374     void parseFile() {
375       while ( next() != EOFTOK ) {
376         if (token == ERRORTOK)
377           error("Invalid token");
378         else if (token != EOLTOK)
379           parseAssignment();
380       }
381       provider->checkErrors();
382     }
383   };
384
385   void
386   ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) {
387     Parser p;
388     p.token = EOFTOK;
389     p.provider = &provider;
390     p.confDat = &confDat;
391     p.parseFile();
392   }
393 }
394
395 CompilerDriver::ConfigData*
396 LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) {
397   CompilerDriver::ConfigData* result = 0;
398   std::string dir_name;
399   if (configDir.empty()) {
400     // Try the environment variable
401     const char* conf = getenv("LLVM_CONFIG_DIR");
402     if (conf) {
403       dir_name = conf;
404       dir_name += "/";
405       if (!::sys::FileReadable(dir_name + ftype))
406         throw "Configuration file for '" + ftype + "' is not available.";
407     } else {
408       // Try the user's home directory
409       const char* home = getenv("HOME");
410       if (home) {
411         dir_name = home;
412         dir_name += "/.llvm/etc/";
413         if (!::sys::FileReadable(dir_name + ftype)) {
414           // Okay, try the LLVM installation directory
415           dir_name = LLVM_ETCDIR;
416           dir_name += "/";
417           if (!::sys::FileReadable(dir_name + ftype)) {
418             // Okay, try the "standard" place
419             dir_name = "/etc/llvm/";
420             if (!::sys::FileReadable(dir_name + ftype)) {
421               throw "Configuration file for '" + ftype + "' is not available.";
422             }
423           }
424         }
425       }
426     }
427   } else {
428     dir_name = configDir + "/";
429     if (!::sys::FileReadable(dir_name + ftype)) {
430       throw "Configuration file for '" + ftype + "' is not available.";
431     }
432   }
433   FileInputProvider fip( dir_name + ftype );
434   if (!fip.okay()) {
435     throw "Configuration file for '" + ftype + "' is not available.";
436   }
437   result = new CompilerDriver::ConfigData();
438   ParseConfigData(fip,*result);
439   return result;
440 }
441
442 LLVMC_ConfigDataProvider::LLVMC_ConfigDataProvider() 
443   : Configurations() 
444   , configDir() 
445 {
446   Configurations.clear();
447 }
448
449 LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider()
450 {
451   ConfigDataMap::iterator cIt = Configurations.begin();
452   while (cIt != Configurations.end()) {
453     CompilerDriver::ConfigData* cd = cIt->second;
454     ++cIt;
455     delete cd;
456   }
457   Configurations.clear();
458 }
459
460 CompilerDriver::ConfigData* 
461 LLVMC_ConfigDataProvider::ProvideConfigData(const std::string& filetype) {
462   CompilerDriver::ConfigData* result = 0;
463   if (!Configurations.empty()) {
464     ConfigDataMap::iterator cIt = Configurations.find(filetype);
465     if ( cIt != Configurations.end() ) {
466       // We found one in the case, return it.
467       result = cIt->second;
468     }
469   }
470   if (result == 0) {
471     // The configuration data doesn't exist, we have to go read it.
472     result = ReadConfigData(filetype);
473     // If we got one, cache it
474     if ( result != 0 )
475       Configurations.insert(std::make_pair(filetype,result));
476   }
477   return result; // Might return 0
478 }
479
480 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab