Remove trailing whitespace
[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     bool parseCompleteItem(std::string& result) {
116       result.clear();
117       while (next_is_real()) {
118         switch (token ) {
119           case STRING :
120           case OPTION :
121             result += ConfigLexerState.StringVal;
122             break;
123           case SEPARATOR:
124             result += ".";
125             break;
126           case SPACE:
127             return true;
128           default:
129             return false;
130         }
131       }
132       return false;
133     }
134
135     std::string parseName() {
136       std::string result;
137       if (next() == EQUALS) {
138         if (parseCompleteItem(result))
139           eatLineRemnant();
140         if (result.empty())
141           error("Name exepected");
142       } else
143         error("Expecting '='");
144       return result;
145     }
146
147     bool parseBoolean() {
148       bool result = true;
149       if (next() == EQUALS) {
150         if (next() == SPACE)
151           next();
152         if (token == FALSETOK) {
153           result = false;
154         } else if (token != TRUETOK) {
155           error("Expecting boolean value");
156           return false;
157         }
158         if (next() != EOLTOK && token != 0) {
159           error("Extraneous tokens after boolean");
160         }
161       }
162       else
163         error("Expecting '='");
164       return result;
165     }
166
167     bool parseSubstitution(CompilerDriver::StringVector& optList) {
168       switch (token) {
169         case ARGS_SUBST:        optList.push_back("%args%"); break;
170         case DEFS_SUBST:        optList.push_back("%defs%"); break;
171         case IN_SUBST:          optList.push_back("%in%"); break;
172         case INCLS_SUBST:       optList.push_back("%incls%"); break;
173         case LIBS_SUBST:        optList.push_back("%libs%"); break;
174         case OPT_SUBST:         optList.push_back("%opt%"); break;
175         case OUT_SUBST:         optList.push_back("%out%"); break;
176         case TARGET_SUBST:      optList.push_back("%target%"); break;
177         case STATS_SUBST:       optList.push_back("%stats%"); break;
178         case TIME_SUBST:        optList.push_back("%time%"); break;
179         case VERBOSE_SUBST:     optList.push_back("%verbose%"); break;
180         case FOPTS_SUBST:       optList.push_back("%fOpts%"); break;
181         case MOPTS_SUBST:       optList.push_back("%Mopts%"); break;
182         case WOPTS_SUBST:       optList.push_back("%Wopts%"); break;
183         default:
184           return false;
185       }
186       return true;
187     }
188
189     void parseOptionList(CompilerDriver::StringVector& optList ) {
190       if (next() == EQUALS) {
191         while (next_is_real()) {
192           if (token == STRING || token == OPTION)
193             optList.push_back(ConfigLexerState.StringVal);
194           else if (!parseSubstitution(optList)) {
195             error("Expecting a program argument or substitution", false);
196             break;
197           }
198         }
199       } else
200         error("Expecting '='");
201     }
202
203     void parseVersion() {
204       if (next() != EQUALS)
205         error("Expecting '='");
206       while (next_is_real()) {
207         if (token == STRING || token == OPTION)
208           confDat->version = ConfigLexerState.StringVal;
209         else
210           error("Expecting a version string");
211       }
212     }
213
214     void parseLibs() {
215       if (next() != EQUALS)
216         error("Expecting '='");
217       std::string lib;
218       while (parseCompleteItem(lib)) {
219         if (!lib.empty()) {
220           confDat->libpaths.push_back(lib);
221         }
222       }
223     }
224
225     void parseLang() {
226       if (next() != SEPARATOR)
227         error("Expecting '.'");
228       switch (next() ) {
229         case LIBS:
230           parseLibs();
231           break;
232         case NAME:
233           confDat->langName = parseName();
234           break;
235         case OPT1:
236           parseOptionList(confDat->opts[CompilerDriver::OPT_FAST_COMPILE]);
237           break;
238         case OPT2:
239           parseOptionList(confDat->opts[CompilerDriver::OPT_SIMPLE]);
240           break;
241         case OPT3:
242           parseOptionList(confDat->opts[CompilerDriver::OPT_AGGRESSIVE]);
243           break;
244         case OPT4:
245           parseOptionList(confDat->opts[CompilerDriver::OPT_LINK_TIME]);
246           break;
247         case OPT5:
248           parseOptionList(
249             confDat->opts[CompilerDriver::OPT_AGGRESSIVE_LINK_TIME]);
250           break;
251         default:
252           error("Expecting 'name' or 'optN' after 'lang.'");
253           break;
254       }
255     }
256
257     bool parseProgramName(std::string& str) {
258       str.clear();
259       do {
260         switch (token) {
261           case OPTION:
262           case STRING:
263           case ARGS_SUBST:
264           case DEFS_SUBST:
265           case IN_SUBST:
266           case INCLS_SUBST:
267           case LIBS_SUBST:
268           case OPT_SUBST:
269           case OUT_SUBST:
270           case STATS_SUBST:
271           case TARGET_SUBST:
272           case TIME_SUBST:
273           case VERBOSE_SUBST:
274           case FOPTS_SUBST:
275           case MOPTS_SUBST:
276           case WOPTS_SUBST:
277             str += ConfigLexerState.StringVal;
278             break;
279           case SEPARATOR:
280             str += ".";
281             break;
282           case ASSEMBLY:
283             str += "assembly";
284             break;
285           case BYTECODE:
286             str += "bytecode";
287             break;
288           case TRUETOK:
289             str += "true";
290             break;
291           case FALSETOK:
292             str += "false";
293             break;
294           default:
295             break;
296         }
297         next();
298       } while (token != SPACE && token != EOFTOK && token != EOLTOK &&
299                token != ERRORTOK);
300       return !str.empty();
301     }
302
303     void parseCommand(CompilerDriver::Action& action) {
304       if (next() != EQUALS)
305         error("Expecting '='");
306       switch (next()) {
307         case EOLTOK:
308           // no value (valid)
309           action.program.clear();
310           action.args.clear();
311           break;
312         case SPACE:
313           next();
314           /* FALL THROUGH */
315         default:
316         {
317           std::string progname;
318           if (parseProgramName(progname))
319             action.program.setFile(progname);
320           else
321             error("Expecting a program name");
322
323           // Get the options
324           std::string anOption;
325           while (next_is_real()) {
326             switch (token) {
327               case STRING:
328               case OPTION:
329                 anOption += ConfigLexerState.StringVal;
330                 break;
331               case ASSEMBLY:
332                 anOption += "assembly";
333                 break;
334               case BYTECODE:
335                 anOption += "bytecode";
336                 break;
337               case TRUETOK:
338                 anOption += "true";
339                 break;
340               case FALSETOK:
341                 anOption += "false";
342                 break;
343               case SEPARATOR:
344                 anOption += ".";
345                 break;
346               case SPACE:
347                 action.args.push_back(anOption);
348                 anOption.clear();
349                 break;
350               default:
351                 if (!parseSubstitution(action.args))
352                   error("Expecting a program argument or substitution", false);
353                 break;
354             }
355           }
356         }
357       }
358     }
359
360     void parsePreprocessor() {
361       if (next() != SEPARATOR)
362         error("Expecting '.'");
363       switch (next()) {
364         case COMMAND:
365           parseCommand(confDat->PreProcessor);
366           break;
367         case REQUIRED:
368           if (parseBoolean())
369             confDat->PreProcessor.set(CompilerDriver::REQUIRED_FLAG);
370           else
371             confDat->PreProcessor.clear(CompilerDriver::REQUIRED_FLAG);
372           break;
373         default:
374           error("Expecting 'command' or 'required' but found '" +
375               ConfigLexerState.StringVal);
376           break;
377       }
378     }
379
380     bool parseOutputFlag() {
381       if (next() == EQUALS) {
382         if (next() == SPACE)
383           next();
384         if (token == ASSEMBLY) {
385           return true;
386         } else if (token == BYTECODE) {
387           return false;
388         } else {
389           error("Expecting output type value");
390           return false;
391         }
392         if (next() != EOLTOK && token != 0) {
393           error("Extraneous tokens after output value");
394         }
395       }
396       else
397         error("Expecting '='");
398       return false;
399     }
400
401     void parseTranslator() {
402       if (next() != SEPARATOR)
403         error("Expecting '.'");
404       switch (next()) {
405         case COMMAND:
406           parseCommand(confDat->Translator);
407           break;
408         case REQUIRED:
409           if (parseBoolean())
410             confDat->Translator.set(CompilerDriver::REQUIRED_FLAG);
411           else
412             confDat->Translator.clear(CompilerDriver::REQUIRED_FLAG);
413           break;
414         case PREPROCESSES:
415           if (parseBoolean())
416             confDat->Translator.set(CompilerDriver::PREPROCESSES_FLAG);
417           else
418             confDat->Translator.clear(CompilerDriver::PREPROCESSES_FLAG);
419           break;
420         case OUTPUT:
421           if (parseOutputFlag())
422             confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
423           else
424             confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
425           break;
426
427         default:
428           error("Expecting 'command', 'required', 'preprocesses', or "
429                 "'output' but found '" + ConfigLexerState.StringVal +
430                 "' instead");
431           break;
432       }
433     }
434
435     void parseOptimizer() {
436       if (next() != SEPARATOR)
437         error("Expecting '.'");
438       switch (next()) {
439         case COMMAND:
440           parseCommand(confDat->Optimizer);
441           break;
442         case PREPROCESSES:
443           if (parseBoolean())
444             confDat->Optimizer.set(CompilerDriver::PREPROCESSES_FLAG);
445           else
446             confDat->Optimizer.clear(CompilerDriver::PREPROCESSES_FLAG);
447           break;
448         case TRANSLATES:
449           if (parseBoolean())
450             confDat->Optimizer.set(CompilerDriver::TRANSLATES_FLAG);
451           else
452             confDat->Optimizer.clear(CompilerDriver::TRANSLATES_FLAG);
453           break;
454         case REQUIRED:
455           if (parseBoolean())
456             confDat->Optimizer.set(CompilerDriver::REQUIRED_FLAG);
457           else
458             confDat->Optimizer.clear(CompilerDriver::REQUIRED_FLAG);
459           break;
460         case OUTPUT:
461           if (parseOutputFlag())
462             confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
463           else
464             confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
465           break;
466         default:
467           error(std::string("Expecting 'command', 'preprocesses', "
468               "'translates' or 'output' but found '") +
469               ConfigLexerState.StringVal + "' instead");
470           break;
471       }
472     }
473
474     void parseAssembler() {
475       if (next() != SEPARATOR)
476         error("Expecting '.'");
477       switch(next()) {
478         case COMMAND:
479           parseCommand(confDat->Assembler);
480           break;
481         default:
482           error("Expecting 'command'");
483           break;
484       }
485     }
486
487     void parseLinker() {
488       if (next() != SEPARATOR)
489         error("Expecting '.'");
490       switch(next()) {
491         case LIBS:
492           break; //FIXME
493         case LIBPATHS:
494           break; //FIXME
495         default:
496           error("Expecting 'libs' or 'libpaths'");
497           break;
498       }
499     }
500
501     void parseAssignment() {
502       switch (token) {
503         case VERSION_TOK:   parseVersion(); break;
504         case LANG:          parseLang(); break;
505         case PREPROCESSOR:  parsePreprocessor(); break;
506         case TRANSLATOR:    parseTranslator(); break;
507         case OPTIMIZER:     parseOptimizer(); break;
508         case ASSEMBLER:     parseAssembler(); break;
509         case LINKER:        parseLinker(); break;
510         case EOLTOK:        break; // just ignore
511         case ERRORTOK:
512         default:
513           error("Invalid top level configuration item");
514           break;
515       }
516     }
517
518     void parseFile() {
519       while ( next() != EOFTOK ) {
520         if (token == ERRORTOK)
521           error("Invalid token");
522         else if (token != EOLTOK)
523           parseAssignment();
524       }
525       provider->checkErrors();
526     }
527   };
528
529 void
530 ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) {
531   Parser p;
532   p.token = EOFTOK;
533   p.provider = &provider;
534   p.confDat = &confDat;
535   p.parseFile();
536   }
537
538 }
539
540 CompilerDriver::ConfigData*
541 LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) {
542   CompilerDriver::ConfigData* result = 0;
543   sys::Path confFile;
544   if (configDir.isEmpty()) {
545     // Try the environment variable
546     const char* conf = getenv("LLVM_CONFIG_DIR");
547     if (conf) {
548       confFile.setDirectory(conf);
549       confFile.appendFile(ftype);
550       if (!confFile.readable())
551         throw std::string("Configuration file for '") + ftype +
552                           "' is not available.";
553     } else {
554       // Try the user's home directory
555       confFile = sys::Path::GetUserHomeDirectory();
556       if (!confFile.isEmpty()) {
557         confFile.appendDirectory(".llvm");
558         confFile.appendDirectory("etc");
559         confFile.appendFile(ftype);
560         if (!confFile.readable())
561           confFile.clear();
562       }
563       if (!confFile.isEmpty()) {
564         // Okay, try the LLVM installation directory
565         confFile = sys::Path::GetLLVMConfigDir();
566         confFile.appendFile(ftype);
567         if (!confFile.readable()) {
568           // Okay, try the "standard" place
569           confFile = sys::Path::GetLLVMDefaultConfigDir();
570           confFile.appendFile(ftype);
571           if (!confFile.readable()) {
572             throw std::string("Configuration file for '") + ftype +
573                               "' is not available.";
574           }
575         }
576       }
577     }
578   } else {
579     confFile = configDir;
580     confFile.appendFile(ftype);
581     if (!confFile.readable())
582       throw std::string("Configuration file for '") + ftype +
583                         "' is not available.";
584   }
585   FileInputProvider fip( confFile.toString() );
586   if (!fip.okay()) {
587     throw std::string("Configuration file for '") + ftype +
588                       "' is not available.";
589   }
590   result = new CompilerDriver::ConfigData();
591   ParseConfigData(fip,*result);
592   return result;
593 }
594
595 LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider()
596 {
597   ConfigDataMap::iterator cIt = Configurations.begin();
598   while (cIt != Configurations.end()) {
599     CompilerDriver::ConfigData* cd = cIt->second;
600     ++cIt;
601     delete cd;
602   }
603   Configurations.clear();
604 }
605
606 CompilerDriver::ConfigData*
607 LLVMC_ConfigDataProvider::ProvideConfigData(const std::string& filetype) {
608   CompilerDriver::ConfigData* result = 0;
609   if (!Configurations.empty()) {
610     ConfigDataMap::iterator cIt = Configurations.find(filetype);
611     if ( cIt != Configurations.end() ) {
612       // We found one in the case, return it.
613       result = cIt->second;
614     }
615   }
616   if (result == 0) {
617     // The configuration data doesn't exist, we have to go read it.
618     result = ReadConfigData(filetype);
619     // If we got one, cache it
620     if (result != 0)
621       Configurations.insert(std::make_pair(filetype,result));
622   }
623   return result; // Might return 0
624 }
625
626 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab