Provide support for retaining the version number found in a config file.
[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 parseVersion() {
185       if (next() == EQUALS) {
186         while (next_is_real()) {
187           if (token == STRING || token == OPTION)
188             confDat->version = ConfigLexerState.StringVal;
189           else
190             error("Expecting a version string");
191         }
192       } else
193         error("Expecting '='");
194     }
195
196     void parseLang() {
197       switch (next() ) {
198         case NAME: 
199           confDat->langName = parseName(); 
200           break;
201         case OPT1: 
202           parseOptionList(confDat->opts[CompilerDriver::OPT_FAST_COMPILE]); 
203           break;
204         case OPT2: 
205           parseOptionList(confDat->opts[CompilerDriver::OPT_SIMPLE]); 
206           break;
207         case OPT3: 
208           parseOptionList(confDat->opts[CompilerDriver::OPT_AGGRESSIVE]); 
209           break;
210         case OPT4: 
211           parseOptionList(confDat->opts[CompilerDriver::OPT_LINK_TIME]); 
212           break;
213         case OPT5: 
214           parseOptionList(
215             confDat->opts[CompilerDriver::OPT_AGGRESSIVE_LINK_TIME]);
216           break;
217         default:   
218           error("Expecting 'name' or 'optN' after 'lang.'"); 
219           break;
220       }
221     }
222
223     void parseCommand(CompilerDriver::Action& action) {
224       if (next() == EQUALS) {
225         if (next() == EOLTOK) {
226           // no value (valid)
227           action.program.clear();
228           action.args.clear();
229         } else {
230           if (token == STRING || token == OPTION) {
231             action.program = ConfigLexerState.StringVal;
232           } else {
233             error("Expecting a program name");
234           }
235           while (next_is_real()) {
236             if (token == STRING || token == OPTION) {
237               action.args.push_back(ConfigLexerState.StringVal);
238             } else if (!parseSubstitution(action.args)) {
239               error("Expecting a program argument or substitution", false);
240               break;
241             }
242           }
243         }
244       }
245     }
246
247     void parsePreprocessor() {
248       switch (next()) {
249         case COMMAND:
250           parseCommand(confDat->PreProcessor);
251           break;
252         case REQUIRED:
253           if (parseBoolean())
254             confDat->PreProcessor.set(CompilerDriver::REQUIRED_FLAG);
255           else
256             confDat->PreProcessor.clear(CompilerDriver::REQUIRED_FLAG);
257           break;
258         default:
259           error("Expecting 'command' or 'required'");
260           break;
261       }
262     }
263
264     void parseTranslator() {
265       switch (next()) {
266         case COMMAND: 
267           parseCommand(confDat->Translator);
268           break;
269         case REQUIRED:
270           if (parseBoolean())
271             confDat->Translator.set(CompilerDriver::REQUIRED_FLAG);
272           else
273             confDat->Translator.clear(CompilerDriver::REQUIRED_FLAG);
274           break;
275         case PREPROCESSES:
276           if (parseBoolean())
277             confDat->Translator.set(CompilerDriver::PREPROCESSES_FLAG);
278           else 
279             confDat->Translator.clear(CompilerDriver::PREPROCESSES_FLAG);
280           break;
281         case OPTIMIZES:
282           if (parseBoolean())
283             confDat->Translator.set(CompilerDriver::OPTIMIZES_FLAG);
284           else
285             confDat->Translator.clear(CompilerDriver::OPTIMIZES_FLAG);
286           break;
287         case GROKS_DASH_O:
288           if (parseBoolean())
289             confDat->Translator.set(CompilerDriver::GROKS_DASH_O_FLAG);
290           else
291             confDat->Translator.clear(CompilerDriver::GROKS_DASH_O_FLAG);
292           break;
293         case OUTPUT_IS_ASM:
294           if (parseBoolean())
295             confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
296           else
297             confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
298           break;
299
300         default:
301           error("Expecting 'command', 'required', 'preprocesses', "
302                 "'groks_dash_O' or 'optimizes'");
303           break;
304       }
305     }
306
307     void parseOptimizer() {
308       switch (next()) {
309         case COMMAND:
310           parseCommand(confDat->Optimizer);
311           break;
312         case PREPROCESSES:
313           if (parseBoolean())
314             confDat->Optimizer.set(CompilerDriver::PREPROCESSES_FLAG);
315           else
316             confDat->Optimizer.clear(CompilerDriver::PREPROCESSES_FLAG);
317           break;
318         case TRANSLATES:
319           if (parseBoolean())
320             confDat->Optimizer.set(CompilerDriver::TRANSLATES_FLAG);
321           else
322             confDat->Optimizer.clear(CompilerDriver::TRANSLATES_FLAG);
323           break;
324         case GROKS_DASH_O:
325           if (parseBoolean())
326             confDat->Optimizer.set(CompilerDriver::GROKS_DASH_O_FLAG);
327           else
328             confDat->Optimizer.clear(CompilerDriver::GROKS_DASH_O_FLAG);
329           break;
330         case OUTPUT_IS_ASM:
331           if (parseBoolean())
332             confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
333           else
334             confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
335           break;
336         default:
337           error("Expecting 'command' or 'groks_dash_O'");
338           break;
339       }
340     }
341
342     void parseAssembler() {
343       switch(next()) {
344         case COMMAND:
345           parseCommand(confDat->Assembler);
346           break;
347         default:
348           error("Expecting 'command'");
349           break;
350       }
351     }
352
353     void parseLinker() {
354       switch(next()) {
355         case COMMAND:
356           parseCommand(confDat->Linker);
357           break;
358         case GROKS_DASH_O:
359           if (parseBoolean())
360             confDat->Linker.set(CompilerDriver::GROKS_DASH_O_FLAG);
361           else
362             confDat->Linker.clear(CompilerDriver::GROKS_DASH_O_FLAG);
363           break;
364         default:
365           error("Expecting 'command'");
366           break;
367       }
368     }
369
370     void parseAssignment() {
371       switch (token) {
372         case VERSION:       parseVersion(); break;
373         case LANG:          parseLang(); break;
374         case PREPROCESSOR:  parsePreprocessor(); break;
375         case TRANSLATOR:    parseTranslator(); break;
376         case OPTIMIZER:     parseOptimizer(); break;
377         case ASSEMBLER:     parseAssembler(); break;
378         case LINKER:        parseLinker(); break;
379         case EOLTOK:        break; // just ignore
380         case ERRORTOK:
381         default:          
382           error("Invalid top level configuration item");
383           break;
384       }
385     }
386
387     void parseFile() {
388       while ( next() != EOFTOK ) {
389         if (token == ERRORTOK)
390           error("Invalid token");
391         else if (token != EOLTOK)
392           parseAssignment();
393       }
394       provider->checkErrors();
395     }
396   };
397
398   void
399   ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) {
400     Parser p;
401     p.token = EOFTOK;
402     p.provider = &provider;
403     p.confDat = &confDat;
404     p.parseFile();
405   }
406 }
407
408 CompilerDriver::ConfigData*
409 LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) {
410   CompilerDriver::ConfigData* result = 0;
411   std::string dir_name;
412   if (configDir.empty()) {
413     // Try the environment variable
414     const char* conf = getenv("LLVM_CONFIG_DIR");
415     if (conf) {
416       dir_name = conf;
417       dir_name += "/";
418       if (!::sys::FileReadable(dir_name + ftype))
419         throw "Configuration file for '" + ftype + "' is not available.";
420     } else {
421       // Try the user's home directory
422       const char* home = getenv("HOME");
423       if (home) {
424         dir_name = home;
425         dir_name += "/.llvm/etc/";
426         if (!::sys::FileReadable(dir_name + ftype)) {
427           // Okay, try the LLVM installation directory
428           dir_name = LLVM_ETCDIR;
429           dir_name += "/";
430           if (!::sys::FileReadable(dir_name + ftype)) {
431             // Okay, try the "standard" place
432             dir_name = "/etc/llvm/";
433             if (!::sys::FileReadable(dir_name + ftype)) {
434               throw "Configuration file for '" + ftype + "' is not available.";
435             }
436           }
437         }
438       }
439     }
440   } else {
441     dir_name = configDir + "/";
442     if (!::sys::FileReadable(dir_name + ftype)) {
443       throw "Configuration file for '" + ftype + "' is not available.";
444     }
445   }
446   FileInputProvider fip( dir_name + ftype );
447   if (!fip.okay()) {
448     throw "Configuration file for '" + ftype + "' is not available.";
449   }
450   result = new CompilerDriver::ConfigData();
451   ParseConfigData(fip,*result);
452   return result;
453 }
454
455 LLVMC_ConfigDataProvider::LLVMC_ConfigDataProvider() 
456   : Configurations() 
457   , configDir() 
458 {
459   Configurations.clear();
460 }
461
462 LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider()
463 {
464   ConfigDataMap::iterator cIt = Configurations.begin();
465   while (cIt != Configurations.end()) {
466     CompilerDriver::ConfigData* cd = cIt->second;
467     ++cIt;
468     delete cd;
469   }
470   Configurations.clear();
471 }
472
473 CompilerDriver::ConfigData* 
474 LLVMC_ConfigDataProvider::ProvideConfigData(const std::string& filetype) {
475   CompilerDriver::ConfigData* result = 0;
476   if (!Configurations.empty()) {
477     ConfigDataMap::iterator cIt = Configurations.find(filetype);
478     if ( cIt != Configurations.end() ) {
479       // We found one in the case, return it.
480       result = cIt->second;
481     }
482   }
483   if (result == 0) {
484     // The configuration data doesn't exist, we have to go read it.
485     result = ReadConfigData(filetype);
486     // If we got one, cache it
487     if ( result != 0 )
488       Configurations.insert(std::make_pair(filetype,result));
489   }
490   return result; // Might return 0
491 }
492
493 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab