More Functionality:
[oota-llvm.git] / tools / llvmc / Configuration.cpp
1 //===- ConfigData.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 "ConfigData.h"
16 #include "ConfigLexer.h"
17 #include "CompilerDriver.h"
18 #include "Support/StringExtras.h"
19 #include <iostream>
20 #include <fstream>
21
22 using namespace llvm;
23
24 extern int ::Configlineno;
25
26 namespace llvm {
27   ConfigLexerInfo ConfigLexerData;
28   InputProvider* ConfigLexerInput = 0;
29
30   InputProvider::~InputProvider() {}
31   void InputProvider::error(const std::string& msg) {
32     std::cerr << name << ":" << Configlineno << ": Error: " << msg << "\n";
33     errCount++;
34   }
35
36   void InputProvider::checkErrors() {
37     if (errCount > 0) {
38       std::cerr << name << " had " << errCount << " errors. Terminating.\n";
39       exit(errCount);
40     }
41   }
42
43 }
44
45 namespace {
46
47   class FileInputProvider : public InputProvider {
48     public:
49       FileInputProvider(const std::string & fname)
50         : InputProvider(fname) 
51         , F(fname.c_str()) {
52         ConfigLexerInput = this;
53       }
54       virtual ~FileInputProvider() { F.close(); ConfigLexerInput = 0; }
55       virtual unsigned read(char *buffer, unsigned max_size) {
56         if (F.good()) {
57           F.read(buffer,max_size);
58           if ( F.gcount() ) return F.gcount() - 1;
59         }
60         return 0;
61       }
62
63       bool okay() { return F.good(); }
64     private:
65       std::ifstream F;
66   };
67
68   struct Parser
69   {
70     ConfigLexerTokens token;
71     InputProvider* provider;
72     CompilerDriver::ConfigData* confDat;
73     CompilerDriver::Action* action;
74
75     int next() { return token = Configlex(); }
76
77     bool next_is_real() { 
78       token = Configlex(); 
79       return (token != EOLTOK) && (token != ERRORTOK) && (token != 0);
80     }
81
82     void eatLineRemnant() {
83       while (next_is_real()) ;
84     }
85
86     void error(const std::string& msg, bool skip = true) {
87       provider->error(msg);
88       if (skip)
89         eatLineRemnant();
90     }
91
92     std::string parseName() {
93       std::string result;
94       if (next() == EQUALS) {
95         while (next_is_real()) {
96           switch (token ) {
97             case STRING :
98             case OPTION : 
99               result += ConfigLexerData.StringVal + " ";
100               break;
101             default:
102               error("Invalid name");
103               break;
104           }
105         }
106         if (result.empty())
107           error("Name exepected");
108         else
109           result.erase(result.size()-1,1);
110       } else
111         error("= expected");
112       return result;
113     }
114
115     bool parseBoolean() {
116       bool result = true;
117       if (next() == EQUALS) {
118         if (next() == FALSETOK) {
119           result = false;
120         } else if (token != TRUETOK) {
121           error("Expecting boolean value");
122           return false;
123         }
124         if (next() != EOLTOK && token != 0) {
125           error("Extraneous tokens after boolean");
126         }
127       }
128       else
129         error("Expecting '='");
130       return result;
131     }
132
133     void parseOptionList(CompilerDriver::StringVector& optList ) {
134       while (next_is_real()) {
135         if (token == STRING || token == OPTION)
136           optList.push_back(ConfigLexerData.StringVal);
137         else {
138           error("Expecting a program option", false);
139           break;
140         }
141       }
142     }
143
144     void parseLang() {
145       switch (next() ) {
146         case NAME: 
147           confDat->langName = parseName(); 
148           break;
149         case OPT1: 
150           parseOptionList(confDat->opts[CompilerDriver::OPT_FAST_COMPILE]); 
151           break;
152         case OPT2: 
153           parseOptionList(confDat->opts[CompilerDriver::OPT_SIMPLE]); 
154           break;
155         case OPT3: 
156           parseOptionList(confDat->opts[CompilerDriver::OPT_AGGRESSIVE]); 
157           break;
158         case OPT4: 
159           parseOptionList(confDat->opts[CompilerDriver::OPT_LINK_TIME]); 
160           break;
161         case OPT5: 
162           parseOptionList(
163             confDat->opts[CompilerDriver::OPT_AGGRESSIVE_LINK_TIME]);
164           break;
165         default:   
166           error("Expecting 'name' or 'optN' after 'lang.'"); 
167           break;
168       }
169     }
170
171     void parseCommand(CompilerDriver::Action& action) {
172       if (next() == EQUALS) {
173         if (next() == EOLTOK) {
174           // no value (valid)
175           action.program.clear();
176           action.args.clear();
177           action.inputAt = 0;
178           action.outputAt = 0;
179         } else {
180           if (token == STRING || token == OPTION) {
181             action.program = ConfigLexerData.StringVal;
182           } else {
183             error("Expecting a program name");
184           }
185           while (next_is_real()) {
186             if (token == STRING || token == OPTION) {
187               action.args.push_back(ConfigLexerData.StringVal);
188             } else if (token == IN_SUBST) {
189               action.inputAt = action.args.size();
190               action.args.push_back("@in@");
191             } else if (token == OUT_SUBST) {
192               action.outputAt = action.args.size();
193               action.args.push_back("@out@");
194             } else {
195               error("Expecting a program argument", false);
196               break;
197             }
198           }
199         }
200       }
201     }
202
203     void parsePreprocessor() {
204       switch (next()) {
205         case COMMAND:
206           parseCommand(confDat->PreProcessor);
207           break;
208         case REQUIRED:
209           if (parseBoolean())
210             confDat->PreProcessor.set(CompilerDriver::REQUIRED_FLAG);
211           else
212             confDat->PreProcessor.clear(CompilerDriver::REQUIRED_FLAG);
213           break;
214         default:
215           error("Expecting 'command' or 'required'");
216           break;
217       }
218     }
219
220     void parseTranslator() {
221       switch (next()) {
222         case COMMAND: 
223           parseCommand(confDat->Translator);
224           break;
225         case REQUIRED:
226           if (parseBoolean())
227             confDat->Translator.set(CompilerDriver::REQUIRED_FLAG);
228           else
229             confDat->Translator.clear(CompilerDriver::REQUIRED_FLAG);
230           break;
231         case PREPROCESSES:
232           if (parseBoolean())
233             confDat->Translator.set(CompilerDriver::PREPROCESSES_FLAG);
234           else 
235             confDat->Translator.clear(CompilerDriver::PREPROCESSES_FLAG);
236           break;
237         case OPTIMIZES:
238           if (parseBoolean())
239             confDat->Translator.set(CompilerDriver::OPTIMIZES_FLAG);
240           else
241             confDat->Translator.clear(CompilerDriver::OPTIMIZES_FLAG);
242           break;
243         case GROKS_DASH_O:
244           if (parseBoolean())
245             confDat->Translator.set(CompilerDriver::GROKS_DASH_O_FLAG);
246           else
247             confDat->Translator.clear(CompilerDriver::GROKS_DASH_O_FLAG);
248           break;
249         case GROKS_O10N:
250           if (parseBoolean())
251             confDat->Translator.set(CompilerDriver::GROKS_O10N_FLAG);
252           else
253             confDat->Translator.clear(CompilerDriver::GROKS_O10N_FLAG);
254           break;
255         default:
256           error("Expecting 'command', 'required', 'preprocesses', "
257                 "'groks_dash_O' or 'optimizes'");
258           break;
259       }
260     }
261
262     void parseOptimizer() {
263       switch (next()) {
264         case COMMAND:
265           parseCommand(confDat->Optimizer);
266           break;
267         case GROKS_DASH_O:
268           if (parseBoolean())
269             confDat->Optimizer.set(CompilerDriver::GROKS_DASH_O_FLAG);
270           else
271             confDat->Optimizer.clear(CompilerDriver::GROKS_DASH_O_FLAG);
272           break;
273         case GROKS_O10N:
274           if (parseBoolean())
275             confDat->Optimizer.set(CompilerDriver::GROKS_O10N_FLAG);
276           else
277             confDat->Optimizer.clear(CompilerDriver::GROKS_O10N_FLAG);
278           break;
279         default:
280           error("Expecting 'command' or 'groks_dash_O'");
281           break;
282       }
283     }
284
285     void parseAssembler() {
286       switch(next()) {
287         case COMMAND:
288           parseCommand(confDat->Assembler);
289           break;
290         default:
291           error("Expecting 'command'");
292           break;
293       }
294     }
295
296     void parseLinker() {
297       switch(next()) {
298         case COMMAND:
299           parseCommand(confDat->Linker);
300           break;
301         case GROKS_DASH_O:
302           if (parseBoolean())
303             confDat->Linker.set(CompilerDriver::GROKS_DASH_O_FLAG);
304           else
305             confDat->Linker.clear(CompilerDriver::GROKS_DASH_O_FLAG);
306           break;
307         case GROKS_O10N:
308           if (parseBoolean())
309             confDat->Linker.set(CompilerDriver::GROKS_O10N_FLAG);
310           else
311             confDat->Linker.clear(CompilerDriver::GROKS_O10N_FLAG);
312           break;
313         default:
314           error("Expecting 'command'");
315           break;
316       }
317     }
318
319     void parseAssignment() {
320       switch (token) {
321         case LANG:          parseLang(); break;
322         case PREPROCESSOR:  parsePreprocessor(); break;
323         case TRANSLATOR:    parseTranslator(); break;
324         case OPTIMIZER:     parseOptimizer(); break;
325         case ASSEMBLER:     parseAssembler(); break;
326         case LINKER:        parseLinker(); break;
327         case EOLTOK:        break; // just ignore
328         case ERRORTOK:
329         default:          
330           error("Invalid top level configuration item");
331           break;
332       }
333     }
334
335     void parseFile() {
336       while ( next() != EOFTOK ) {
337         if (token == ERRORTOK)
338           error("Invalid token");
339         else if (token != EOLTOK)
340           parseAssignment();
341       }
342       provider->checkErrors();
343     }
344   };
345
346   void
347   ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) {
348     Parser p;
349     p.token = EOFTOK;
350     p.provider = &provider;
351     p.confDat = &confDat;
352     p.action = 0;
353     p.parseFile();
354   }
355 }
356
357 CompilerDriver::ConfigData*
358 LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) {
359   CompilerDriver::ConfigData* result = 0;
360   if (configDir.empty()) {
361     FileInputProvider fip( std::string("/etc/llvm/") + ftype );
362     if (!fip.okay()) {
363       fip.error("Configuration for '" + ftype + "' is not available.");
364       fip.checkErrors();
365     }
366     else {
367       result = new CompilerDriver::ConfigData();
368       ParseConfigData(fip,*result);
369     }
370   } else {
371     FileInputProvider fip( configDir + "/" + ftype );
372     if (!fip.okay()) {
373       fip.error("Configuration for '" + ftype + "' is not available.");
374       fip.checkErrors();
375     }
376     else {
377       result = new CompilerDriver::ConfigData();
378       ParseConfigData(fip,*result);
379     }
380   }
381   return result;
382 }
383
384 LLVMC_ConfigDataProvider::LLVMC_ConfigDataProvider() 
385   : Configurations() 
386   , configDir() 
387 {
388   Configurations.clear();
389 }
390
391 LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider()
392 {
393   ConfigDataMap::iterator cIt = Configurations.begin();
394   while (cIt != Configurations.end()) {
395     CompilerDriver::ConfigData* cd = cIt->second;
396     ++cIt;
397     delete cd;
398   }
399   Configurations.clear();
400 }
401
402 CompilerDriver::ConfigData* 
403 LLVMC_ConfigDataProvider::ProvideConfigData(const std::string& filetype) {
404   CompilerDriver::ConfigData* result = 0;
405   if (!Configurations.empty()) {
406     ConfigDataMap::iterator cIt = Configurations.find(filetype);
407     if ( cIt != Configurations.end() ) {
408       // We found one in the case, return it.
409       result = cIt->second;
410     }
411   }
412   if (result == 0) {
413     // The configuration data doesn't exist, we have to go read it.
414     result = ReadConfigData(filetype);
415     // If we got one, cache it
416     if ( result != 0 )
417       Configurations.insert(std::make_pair(filetype,result));
418   }
419   return result; // Might return 0
420 }
421
422 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab