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