1 //===- Configuration.cpp - Configuration Data Mgmt --------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
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.
8 //===----------------------------------------------------------------------===//
10 // This file implements the parsing of configuration files for the LLVM Compiler
13 //===------------------------------------------------------------------------===
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"
27 // From CompilerDriver.cpp (for now)
28 extern bool FileIsReadable(const std::string& fname);
32 ConfigLexerInfo ConfigLexerState;
33 InputProvider* ConfigLexerInput = 0;
35 InputProvider::~InputProvider() {}
36 void InputProvider::error(const std::string& msg) {
37 std::cerr << name << ":" << ConfigLexerState.lineNum << ": Error: " <<
42 void InputProvider::checkErrors() {
44 std::cerr << name << " had " << errCount << " errors. Terminating.\n";
53 class FileInputProvider : public InputProvider {
55 FileInputProvider(const std::string & fname)
56 : InputProvider(fname)
58 ConfigLexerInput = this;
60 virtual ~FileInputProvider() { F.close(); ConfigLexerInput = 0; }
61 virtual unsigned read(char *buffer, unsigned max_size) {
63 F.read(buffer,max_size);
64 if ( F.gcount() ) return F.gcount() - 1;
69 bool okay() { return F.good(); }
74 cl::opt<bool> DumpTokens("dump-tokens", cl::Optional, cl::Hidden, cl::init(false),
75 cl::desc("Dump lexical tokens (debug use only)."));
83 ConfigLexerState.lineNum = 1;
84 ConfigLexerState.in_value = false;
85 ConfigLexerState.StringVal.clear();
86 ConfigLexerState.IntegerVal = 0;
89 ConfigLexerTokens token;
90 InputProvider* provider;
91 CompilerDriver::ConfigData* confDat;
96 std::cerr << token << "\n";
100 inline bool next_is_real() {
102 return (token != EOLTOK) && (token != ERRORTOK) && (token != 0);
105 inline void eatLineRemnant() {
106 while (next_is_real()) ;
109 void error(const std::string& msg, bool skip = true) {
110 provider->error(msg);
115 std::string parseName() {
117 if (next() == EQUALS) {
118 while (next_is_real()) {
122 result += ConfigLexerState.StringVal + " ";
125 error("Invalid name");
130 error("Name exepected");
132 result.erase(result.size()-1,1);
138 bool parseBoolean() {
140 if (next() == EQUALS) {
141 if (next() == FALSETOK) {
143 } else if (token != TRUETOK) {
144 error("Expecting boolean value");
147 if (next() != EOLTOK && token != 0) {
148 error("Extraneous tokens after boolean");
152 error("Expecting '='");
156 bool parseSubstitution(CompilerDriver::StringVector& optList) {
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;
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);
187 error("Expecting '='");
190 void parseVersion() {
191 if (next() == EQUALS) {
192 while (next_is_real()) {
193 if (token == STRING || token == OPTION)
194 confDat->version = ConfigLexerState.StringVal;
196 error("Expecting a version string");
199 error("Expecting '='");
205 confDat->langName = parseName();
208 parseOptionList(confDat->opts[CompilerDriver::OPT_FAST_COMPILE]);
211 parseOptionList(confDat->opts[CompilerDriver::OPT_SIMPLE]);
214 parseOptionList(confDat->opts[CompilerDriver::OPT_AGGRESSIVE]);
217 parseOptionList(confDat->opts[CompilerDriver::OPT_LINK_TIME]);
221 confDat->opts[CompilerDriver::OPT_AGGRESSIVE_LINK_TIME]);
224 error("Expecting 'name' or 'optN' after 'lang.'");
229 void parseCommand(CompilerDriver::Action& action) {
230 if (next() == EQUALS) {
231 if (next() == EOLTOK) {
233 action.program.clear();
236 if (token == STRING || token == OPTION) {
237 action.program.set_file(ConfigLexerState.StringVal);
239 error("Expecting a program name");
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);
253 void parsePreprocessor() {
256 parseCommand(confDat->PreProcessor);
260 confDat->PreProcessor.set(CompilerDriver::REQUIRED_FLAG);
262 confDat->PreProcessor.clear(CompilerDriver::REQUIRED_FLAG);
265 error("Expecting 'command' or 'required' but found '" +
266 ConfigLexerState.StringVal);
271 bool parseOutputFlag() {
272 if (next() == EQUALS) {
273 if (next() == ASSEMBLY) {
275 } else if (token == BYTECODE) {
278 error("Expecting output type value");
281 if (next() != EOLTOK && token != 0) {
282 error("Extraneous tokens after output value");
286 error("Expecting '='");
290 void parseTranslator() {
293 parseCommand(confDat->Translator);
297 confDat->Translator.set(CompilerDriver::REQUIRED_FLAG);
299 confDat->Translator.clear(CompilerDriver::REQUIRED_FLAG);
303 confDat->Translator.set(CompilerDriver::PREPROCESSES_FLAG);
305 confDat->Translator.clear(CompilerDriver::PREPROCESSES_FLAG);
308 if (parseOutputFlag())
309 confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
311 confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
315 error("Expecting 'command', 'required', 'preprocesses', or "
316 "'output' but found '" + ConfigLexerState.StringVal +
322 void parseOptimizer() {
325 parseCommand(confDat->Optimizer);
329 confDat->Optimizer.set(CompilerDriver::PREPROCESSES_FLAG);
331 confDat->Optimizer.clear(CompilerDriver::PREPROCESSES_FLAG);
335 confDat->Optimizer.set(CompilerDriver::TRANSLATES_FLAG);
337 confDat->Optimizer.clear(CompilerDriver::TRANSLATES_FLAG);
341 confDat->Optimizer.set(CompilerDriver::REQUIRED_FLAG);
343 confDat->Optimizer.clear(CompilerDriver::REQUIRED_FLAG);
346 if (parseOutputFlag())
347 confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
349 confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
352 error(std::string("Expecting 'command', 'preprocesses', ") +
353 "'translates' or 'output' but found '" +
354 ConfigLexerState.StringVal + "' instead");
359 void parseAssembler() {
362 parseCommand(confDat->Assembler);
365 error("Expecting 'command'");
377 error("Expecting 'libs' or 'libpaths'");
382 void parseAssignment() {
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
394 error("Invalid top level configuration item");
400 while ( next() != EOFTOK ) {
401 if (token == ERRORTOK)
402 error("Invalid token");
403 else if (token != EOLTOK)
406 provider->checkErrors();
411 ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) {
414 p.provider = &provider;
415 p.confDat = &confDat;
420 CompilerDriver::ConfigData*
421 LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) {
422 CompilerDriver::ConfigData* result = 0;
424 if (configDir.is_empty()) {
425 // Try the environment variable
426 const char* conf = getenv("LLVM_CONFIG_DIR");
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.";
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())
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.";
459 confFile = configDir;
460 confFile.append_file(ftype);
461 if (!confFile.readable())
462 throw std::string("Configuration file for '") + ftype +
463 "' is not available.";
465 FileInputProvider fip( confFile.get() );
467 throw std::string("Configuration file for '") + ftype +
468 "' is not available.";
470 result = new CompilerDriver::ConfigData();
471 ParseConfigData(fip,*result);
475 LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider()
477 ConfigDataMap::iterator cIt = Configurations.begin();
478 while (cIt != Configurations.end()) {
479 CompilerDriver::ConfigData* cd = cIt->second;
483 Configurations.clear();
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;
497 // The configuration data doesn't exist, we have to go read it.
498 result = ReadConfigData(filetype);
499 // If we got one, cache it
501 Configurations.insert(std::make_pair(filetype,result));
503 return result; // Might return 0
506 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab