Implement the "setIncludePaths" and "setSymbolDefines" interface methods.
[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 DEFS_SUBST:        optList.push_back("%defs%"); break;
160         case FORCE_SUBST:       optList.push_back("%force%"); break;
161         case IN_SUBST:          optList.push_back("%in%"); break;
162         case INCLS_SUBST:       optList.push_back("%incls%"); break;
163         case LIBS_SUBST:        optList.push_back("%libs%"); break;
164         case OPT_SUBST:         optList.push_back("%opt%"); break;
165         case OUT_SUBST:         optList.push_back("%out%"); break;
166         case TARGET_SUBST:      optList.push_back("%target%"); break;
167         case STATS_SUBST:       optList.push_back("%stats%"); break;
168         case TIME_SUBST:        optList.push_back("%time%"); break;
169         case VERBOSE_SUBST:     optList.push_back("%verbose%"); break;
170         default:
171           return false;
172       }
173       return true;
174     }
175
176     void parseOptionList(CompilerDriver::StringVector& optList ) {
177       if (next() == EQUALS) {
178         while (next_is_real()) {
179           if (token == STRING || token == OPTION)
180             optList.push_back(ConfigLexerState.StringVal);
181           else if (!parseSubstitution(optList)) {
182             error("Expecting a program argument or substitution", false);
183             break;
184           }
185         }
186       } else
187         error("Expecting '='");
188     }
189
190     void parseVersion() {
191       if (next() == EQUALS) {
192         while (next_is_real()) {
193           if (token == STRING || token == OPTION)
194             confDat->version = ConfigLexerState.StringVal;
195           else
196             error("Expecting a version string");
197         }
198       } else
199         error("Expecting '='");
200     }
201
202     void parseLang() {
203       switch (next() ) {
204         case NAME: 
205           confDat->langName = parseName(); 
206           break;
207         case OPT1: 
208           parseOptionList(confDat->opts[CompilerDriver::OPT_FAST_COMPILE]); 
209           break;
210         case OPT2: 
211           parseOptionList(confDat->opts[CompilerDriver::OPT_SIMPLE]); 
212           break;
213         case OPT3: 
214           parseOptionList(confDat->opts[CompilerDriver::OPT_AGGRESSIVE]); 
215           break;
216         case OPT4: 
217           parseOptionList(confDat->opts[CompilerDriver::OPT_LINK_TIME]); 
218           break;
219         case OPT5: 
220           parseOptionList(
221             confDat->opts[CompilerDriver::OPT_AGGRESSIVE_LINK_TIME]);
222           break;
223         default:   
224           error("Expecting 'name' or 'optN' after 'lang.'"); 
225           break;
226       }
227     }
228
229     void parseCommand(CompilerDriver::Action& action) {
230       if (next() == EQUALS) {
231         if (next() == EOLTOK) {
232           // no value (valid)
233           action.program.clear();
234           action.args.clear();
235         } else {
236           if (token == STRING || token == OPTION) {
237             action.program.set_file(ConfigLexerState.StringVal);
238           } else {
239             error("Expecting a program name");
240           }
241           while (next_is_real()) {
242             if (token == STRING || token == OPTION) {
243               action.args.push_back(ConfigLexerState.StringVal);
244             } else if (!parseSubstitution(action.args)) {
245               error("Expecting a program argument or substitution", false);
246               break;
247             }
248           }
249         }
250       }
251     }
252
253     void parsePreprocessor() {
254       switch (next()) {
255         case COMMAND:
256           parseCommand(confDat->PreProcessor);
257           break;
258         case REQUIRED:
259           if (parseBoolean())
260             confDat->PreProcessor.set(CompilerDriver::REQUIRED_FLAG);
261           else
262             confDat->PreProcessor.clear(CompilerDriver::REQUIRED_FLAG);
263           break;
264         default:
265           error("Expecting 'command' or 'required' but found '" +
266               ConfigLexerState.StringVal);
267           break;
268       }
269     }
270
271     bool parseOutputFlag() {
272       if (next() == EQUALS) {
273         if (next() == ASSEMBLY) {
274           return true;
275         } else if (token == BYTECODE) {
276           return false;
277         } else {
278           error("Expecting output type value");
279           return false;
280         }
281         if (next() != EOLTOK && token != 0) {
282           error("Extraneous tokens after output value");
283         }
284       }
285       else
286         error("Expecting '='");
287       return false;
288     }
289
290     void parseTranslator() {
291       switch (next()) {
292         case COMMAND: 
293           parseCommand(confDat->Translator);
294           break;
295         case REQUIRED:
296           if (parseBoolean())
297             confDat->Translator.set(CompilerDriver::REQUIRED_FLAG);
298           else
299             confDat->Translator.clear(CompilerDriver::REQUIRED_FLAG);
300           break;
301         case PREPROCESSES:
302           if (parseBoolean())
303             confDat->Translator.set(CompilerDriver::PREPROCESSES_FLAG);
304           else 
305             confDat->Translator.clear(CompilerDriver::PREPROCESSES_FLAG);
306           break;
307         case OUTPUT:
308           if (parseOutputFlag())
309             confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
310           else
311             confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
312           break;
313
314         default:
315           error("Expecting 'command', 'required', 'preprocesses', or "
316                 "'output' but found '" + ConfigLexerState.StringVal +
317                 "' instead");
318           break;
319       }
320     }
321
322     void parseOptimizer() {
323       switch (next()) {
324         case COMMAND:
325           parseCommand(confDat->Optimizer);
326           break;
327         case PREPROCESSES:
328           if (parseBoolean())
329             confDat->Optimizer.set(CompilerDriver::PREPROCESSES_FLAG);
330           else
331             confDat->Optimizer.clear(CompilerDriver::PREPROCESSES_FLAG);
332           break;
333         case TRANSLATES:
334           if (parseBoolean())
335             confDat->Optimizer.set(CompilerDriver::TRANSLATES_FLAG);
336           else
337             confDat->Optimizer.clear(CompilerDriver::TRANSLATES_FLAG);
338           break;
339         case REQUIRED:
340           if (parseBoolean())
341             confDat->Optimizer.set(CompilerDriver::REQUIRED_FLAG);
342           else
343             confDat->Optimizer.clear(CompilerDriver::REQUIRED_FLAG);
344           break;
345         case OUTPUT:
346           if (parseOutputFlag())
347             confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
348           else
349             confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
350           break;
351         default:
352           error(std::string("Expecting 'command', 'preprocesses', ") +
353               "'translates' or 'output' but found '" + 
354               ConfigLexerState.StringVal + "' instead");
355           break;
356       }
357     }
358
359     void parseAssembler() {
360       switch(next()) {
361         case COMMAND:
362           parseCommand(confDat->Assembler);
363           break;
364         default:
365           error("Expecting 'command'");
366           break;
367       }
368     }
369
370     void parseLinker() {
371       switch(next()) {
372         case LIBS:
373           break; //FIXME
374         case LIBPATHS:
375           break; //FIXME
376         default:
377           error("Expecting 'libs' or 'libpaths'");
378           break;
379       }
380     }
381
382     void parseAssignment() {
383       switch (token) {
384         case VERSION:       parseVersion(); break;
385         case LANG:          parseLang(); break;
386         case PREPROCESSOR:  parsePreprocessor(); break;
387         case TRANSLATOR:    parseTranslator(); break;
388         case OPTIMIZER:     parseOptimizer(); break;
389         case ASSEMBLER:     parseAssembler(); break;
390         case LINKER:        parseLinker(); break;
391         case EOLTOK:        break; // just ignore
392         case ERRORTOK:
393         default:          
394           error("Invalid top level configuration item");
395           break;
396       }
397     }
398
399     void parseFile() {
400       while ( next() != EOFTOK ) {
401         if (token == ERRORTOK)
402           error("Invalid token");
403         else if (token != EOLTOK)
404           parseAssignment();
405       }
406       provider->checkErrors();
407     }
408   };
409
410   void
411   ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) {
412     Parser p;
413     p.token = EOFTOK;
414     p.provider = &provider;
415     p.confDat = &confDat;
416     p.parseFile();
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