Eliminate the force flag, configuration item, and related support
[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 "llvm/Config/config.h"
19 #include "llvm/Support/CommandLine.h"
20 #include "llvm/ADT/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 FileIsReadable(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, 
75     cl::init(false), 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     inline int next() { 
94       token = Configlex();
95       if (DumpTokens) 
96         std::cerr << token << "\n";
97       return token;
98     }
99
100     inline bool next_is_real() { 
101       next();
102       return (token != EOLTOK) && (token != ERRORTOK) && (token != 0);
103     }
104
105     inline 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 ARGS_SUBST:        optList.push_back("%args%"); break;
159         case DEFS_SUBST:        optList.push_back("%defs%"); break;
160         case IN_SUBST:          optList.push_back("%in%"); break;
161         case INCLS_SUBST:       optList.push_back("%incls%"); break;
162         case LIBS_SUBST:        optList.push_back("%libs%"); break;
163         case OPT_SUBST:         optList.push_back("%opt%"); break;
164         case OUT_SUBST:         optList.push_back("%out%"); break;
165         case TARGET_SUBST:      optList.push_back("%target%"); break;
166         case STATS_SUBST:       optList.push_back("%stats%"); break;
167         case TIME_SUBST:        optList.push_back("%time%"); break;
168         case VERBOSE_SUBST:     optList.push_back("%verbose%"); break;
169         default:
170           return false;
171       }
172       return true;
173     }
174
175     void parseOptionList(CompilerDriver::StringVector& optList ) {
176       if (next() == EQUALS) {
177         while (next_is_real()) {
178           if (token == STRING || token == OPTION)
179             optList.push_back(ConfigLexerState.StringVal);
180           else if (!parseSubstitution(optList)) {
181             error("Expecting a program argument or substitution", false);
182             break;
183           }
184         }
185       } else
186         error("Expecting '='");
187     }
188
189     void parseVersion() {
190       if (next() == EQUALS) {
191         while (next_is_real()) {
192           if (token == STRING || token == OPTION)
193             confDat->version = ConfigLexerState.StringVal;
194           else
195             error("Expecting a version string");
196         }
197       } else
198         error("Expecting '='");
199     }
200
201     void parseLang() {
202       switch (next() ) {
203         case NAME: 
204           confDat->langName = parseName(); 
205           break;
206         case OPT1: 
207           parseOptionList(confDat->opts[CompilerDriver::OPT_FAST_COMPILE]); 
208           break;
209         case OPT2: 
210           parseOptionList(confDat->opts[CompilerDriver::OPT_SIMPLE]); 
211           break;
212         case OPT3: 
213           parseOptionList(confDat->opts[CompilerDriver::OPT_AGGRESSIVE]); 
214           break;
215         case OPT4: 
216           parseOptionList(confDat->opts[CompilerDriver::OPT_LINK_TIME]); 
217           break;
218         case OPT5: 
219           parseOptionList(
220             confDat->opts[CompilerDriver::OPT_AGGRESSIVE_LINK_TIME]);
221           break;
222         default:   
223           error("Expecting 'name' or 'optN' after 'lang.'"); 
224           break;
225       }
226     }
227
228     void parseCommand(CompilerDriver::Action& action) {
229       if (next() == EQUALS) {
230         if (next() == EOLTOK) {
231           // no value (valid)
232           action.program.clear();
233           action.args.clear();
234         } else {
235           if (token == STRING || token == OPTION) {
236             action.program.set_file(ConfigLexerState.StringVal);
237           } else {
238             error("Expecting a program name");
239           }
240           while (next_is_real()) {
241             if (token == STRING || token == OPTION) {
242               action.args.push_back(ConfigLexerState.StringVal);
243             } else if (!parseSubstitution(action.args)) {
244               error("Expecting a program argument or substitution", false);
245               break;
246             }
247           }
248         }
249       }
250     }
251
252     void parsePreprocessor() {
253       switch (next()) {
254         case COMMAND:
255           parseCommand(confDat->PreProcessor);
256           break;
257         case REQUIRED:
258           if (parseBoolean())
259             confDat->PreProcessor.set(CompilerDriver::REQUIRED_FLAG);
260           else
261             confDat->PreProcessor.clear(CompilerDriver::REQUIRED_FLAG);
262           break;
263         default:
264           error("Expecting 'command' or 'required' but found '" +
265               ConfigLexerState.StringVal);
266           break;
267       }
268     }
269
270     bool parseOutputFlag() {
271       if (next() == EQUALS) {
272         if (next() == ASSEMBLY) {
273           return true;
274         } else if (token == BYTECODE) {
275           return false;
276         } else {
277           error("Expecting output type value");
278           return false;
279         }
280         if (next() != EOLTOK && token != 0) {
281           error("Extraneous tokens after output value");
282         }
283       }
284       else
285         error("Expecting '='");
286       return false;
287     }
288
289     void parseTranslator() {
290       switch (next()) {
291         case COMMAND: 
292           parseCommand(confDat->Translator);
293           break;
294         case REQUIRED:
295           if (parseBoolean())
296             confDat->Translator.set(CompilerDriver::REQUIRED_FLAG);
297           else
298             confDat->Translator.clear(CompilerDriver::REQUIRED_FLAG);
299           break;
300         case PREPROCESSES:
301           if (parseBoolean())
302             confDat->Translator.set(CompilerDriver::PREPROCESSES_FLAG);
303           else 
304             confDat->Translator.clear(CompilerDriver::PREPROCESSES_FLAG);
305           break;
306         case OUTPUT:
307           if (parseOutputFlag())
308             confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
309           else
310             confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
311           break;
312
313         default:
314           error("Expecting 'command', 'required', 'preprocesses', or "
315                 "'output' but found '" + ConfigLexerState.StringVal +
316                 "' instead");
317           break;
318       }
319     }
320
321     void parseOptimizer() {
322       switch (next()) {
323         case COMMAND:
324           parseCommand(confDat->Optimizer);
325           break;
326         case PREPROCESSES:
327           if (parseBoolean())
328             confDat->Optimizer.set(CompilerDriver::PREPROCESSES_FLAG);
329           else
330             confDat->Optimizer.clear(CompilerDriver::PREPROCESSES_FLAG);
331           break;
332         case TRANSLATES:
333           if (parseBoolean())
334             confDat->Optimizer.set(CompilerDriver::TRANSLATES_FLAG);
335           else
336             confDat->Optimizer.clear(CompilerDriver::TRANSLATES_FLAG);
337           break;
338         case REQUIRED:
339           if (parseBoolean())
340             confDat->Optimizer.set(CompilerDriver::REQUIRED_FLAG);
341           else
342             confDat->Optimizer.clear(CompilerDriver::REQUIRED_FLAG);
343           break;
344         case OUTPUT:
345           if (parseOutputFlag())
346             confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
347           else
348             confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
349           break;
350         default:
351           error(std::string("Expecting 'command', 'preprocesses', " 
352               "'translates' or 'output' but found '") + 
353               ConfigLexerState.StringVal + "' instead");
354           break;
355       }
356     }
357
358     void parseAssembler() {
359       switch(next()) {
360         case COMMAND:
361           parseCommand(confDat->Assembler);
362           break;
363         default:
364           error("Expecting 'command'");
365           break;
366       }
367     }
368
369     void parseLinker() {
370       switch(next()) {
371         case LIBS:
372           break; //FIXME
373         case LIBPATHS:
374           break; //FIXME
375         default:
376           error("Expecting 'libs' or 'libpaths'");
377           break;
378       }
379     }
380
381     void parseAssignment() {
382       switch (token) {
383         case VERSION_TOK:   parseVersion(); break;
384         case LANG:          parseLang(); break;
385         case PREPROCESSOR:  parsePreprocessor(); break;
386         case TRANSLATOR:    parseTranslator(); break;
387         case OPTIMIZER:     parseOptimizer(); break;
388         case ASSEMBLER:     parseAssembler(); break;
389         case LINKER:        parseLinker(); break;
390         case EOLTOK:        break; // just ignore
391         case ERRORTOK:
392         default:          
393           error("Invalid top level configuration item");
394           break;
395       }
396     }
397
398     void parseFile() {
399       while ( next() != EOFTOK ) {
400         if (token == ERRORTOK)
401           error("Invalid token");
402         else if (token != EOLTOK)
403           parseAssignment();
404       }
405       provider->checkErrors();
406     }
407   };
408
409 void
410 ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) {
411   Parser p;
412   p.token = EOFTOK;
413   p.provider = &provider;
414   p.confDat = &confDat;
415   p.parseFile();
416   }
417
418 }
419
420 CompilerDriver::ConfigData*
421 LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) {
422   CompilerDriver::ConfigData* result = 0;
423   sys::Path confFile;
424   if (configDir.is_empty()) {
425     // Try the environment variable
426     const char* conf = getenv("LLVM_CONFIG_DIR");
427     if (conf) {
428       confFile.set_directory(conf);
429       confFile.append_file(ftype);
430       if (!confFile.readable())
431         throw std::string("Configuration file for '") + ftype + 
432                           "' is not available.";
433     } else {
434       // Try the user's home directory
435       confFile = sys::Path::GetUserHomeDirectory();
436       if (!confFile.is_empty()) {
437         confFile.append_directory(".llvm");
438         confFile.append_directory("etc");
439         confFile.append_file(ftype);
440         if (!confFile.readable())
441           confFile.clear();
442       }
443       if (!confFile.is_empty()) {
444         // Okay, try the LLVM installation directory
445         confFile = sys::Path::GetLLVMConfigDir();
446         confFile.append_file(ftype);
447         if (!confFile.readable()) {
448           // Okay, try the "standard" place
449           confFile = sys::Path::GetLLVMDefaultConfigDir();
450           confFile.append_file(ftype);
451           if (!confFile.readable()) {
452             throw std::string("Configuration file for '") + ftype + 
453                               "' is not available.";
454           }
455         }
456       }
457     }
458   } else {
459     confFile = configDir;
460     confFile.append_file(ftype);
461     if (!confFile.readable())
462       throw std::string("Configuration file for '") + ftype + 
463                         "' is not available.";
464   }
465   FileInputProvider fip( confFile.get() );
466   if (!fip.okay()) {
467     throw std::string("Configuration file for '") + ftype + 
468                       "' is not available.";
469   }
470   result = new CompilerDriver::ConfigData();
471   ParseConfigData(fip,*result);
472   return result;
473 }
474
475 LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider()
476 {
477   ConfigDataMap::iterator cIt = Configurations.begin();
478   while (cIt != Configurations.end()) {
479     CompilerDriver::ConfigData* cd = cIt->second;
480     ++cIt;
481     delete cd;
482   }
483   Configurations.clear();
484 }
485
486 CompilerDriver::ConfigData* 
487 LLVMC_ConfigDataProvider::ProvideConfigData(const std::string& filetype) {
488   CompilerDriver::ConfigData* result = 0;
489   if (!Configurations.empty()) {
490     ConfigDataMap::iterator cIt = Configurations.find(filetype);
491     if ( cIt != Configurations.end() ) {
492       // We found one in the case, return it.
493       result = cIt->second;
494     }
495   }
496   if (result == 0) {
497     // The configuration data doesn't exist, we have to go read it.
498     result = ReadConfigData(filetype);
499     // If we got one, cache it
500     if (result != 0)
501       Configurations.insert(std::make_pair(filetype,result));
502   }
503   return result; // Might return 0
504 }
505
506 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab