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