//===- Configuration.cpp - Configuration Data Mgmt --------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file was developed by Reid Spencer and is distributed under the // University of Illinois Open Source License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements the parsing of configuration files for the LLVM Compiler // Driver (llvmc). // //===----------------------------------------------------------------------===// #include "Configuration.h" #include "ConfigLexer.h" #include "CompilerDriver.h" #include "llvm/Config/config.h" #include "llvm/Support/CommandLine.h" #include "llvm/ADT/StringExtras.h" #include #include using namespace llvm; namespace sys { // From CompilerDriver.cpp (for now) extern bool FileIsReadable(const std::string& fname); } namespace llvm { ConfigLexerInfo ConfigLexerState; InputProvider* ConfigLexerInput = 0; InputProvider::~InputProvider() {} void InputProvider::error(const std::string& msg) { std::cerr << name << ":" << ConfigLexerState.lineNum << ": Error: " << msg << "\n"; errCount++; } void InputProvider::checkErrors() { if (errCount > 0) { std::cerr << name << " had " << errCount << " errors. Terminating.\n"; exit(errCount); } } } namespace { class FileInputProvider : public InputProvider { public: FileInputProvider(const std::string & fname) : InputProvider(fname) , F(fname.c_str()) { ConfigLexerInput = this; } virtual ~FileInputProvider() { F.close(); ConfigLexerInput = 0; } virtual unsigned read(char *buffer, unsigned max_size) { if (F.good()) { F.read(buffer,max_size); if ( F.gcount() ) return F.gcount() - 1; } return 0; } bool okay() { return F.good(); } private: std::ifstream F; }; cl::opt DumpTokens("dump-tokens", cl::Optional, cl::Hidden, cl::init(false), cl::desc("Dump lexical tokens (debug use only).")); struct Parser { Parser() { token = EOFTOK; provider = 0; confDat = 0; ConfigLexerState.lineNum = 1; ConfigLexerState.in_value = false; ConfigLexerState.StringVal.clear(); ConfigLexerState.IntegerVal = 0; }; ConfigLexerTokens token; InputProvider* provider; CompilerDriver::ConfigData* confDat; inline int next() { token = Configlex(); if (DumpTokens) std::cerr << token << "\n"; return token; } inline bool next_is_real() { next(); return (token != EOLTOK) && (token != ERRORTOK) && (token != 0); } inline void eatLineRemnant() { while (next_is_real()) ; } void error(const std::string& msg, bool skip = true) { provider->error(msg); if (skip) eatLineRemnant(); } bool parseCompleteItem(std::string& result) { result.clear(); while (next_is_real()) { switch (token ) { case LLVMGCCDIR_SUBST: case LLVMGCCARCH_SUBST: case STRING : case OPTION : result += ConfigLexerState.StringVal; break; case SEPARATOR: result += "."; break; case SPACE: return true; default: return false; } } return false; } std::string parseName() { std::string result; if (next() == EQUALS) { if (parseCompleteItem(result)) eatLineRemnant(); if (result.empty()) error("Name exepected"); } else error("Expecting '='"); return result; } bool parseBoolean() { bool result = true; if (next() == EQUALS) { if (next() == SPACE) next(); if (token == FALSETOK) { result = false; } else if (token != TRUETOK) { error("Expecting boolean value"); return false; } if (next() != EOLTOK && token != 0) { error("Extraneous tokens after boolean"); } } else error("Expecting '='"); return result; } bool parseSubstitution(CompilerDriver::StringVector& optList) { switch (token) { case ARGS_SUBST: optList.push_back("%args%"); break; case BINDIR_SUBST: optList.push_back("%bindir%"); break; case DEFS_SUBST: optList.push_back("%defs%"); break; case IN_SUBST: optList.push_back("%in%"); break; case INCLS_SUBST: optList.push_back("%incls%"); break; case LIBDIR_SUBST: optList.push_back("%libdir%"); break; case LIBS_SUBST: optList.push_back("%libs%"); break; case OPT_SUBST: optList.push_back("%opt%"); break; case OUT_SUBST: optList.push_back("%out%"); break; case TARGET_SUBST: optList.push_back("%target%"); break; case STATS_SUBST: optList.push_back("%stats%"); break; case TIME_SUBST: optList.push_back("%time%"); break; case VERBOSE_SUBST: optList.push_back("%verbose%"); break; case FOPTS_SUBST: optList.push_back("%fOpts%"); break; case MOPTS_SUBST: optList.push_back("%Mopts%"); break; case WOPTS_SUBST: optList.push_back("%Wopts%"); break; default: return false; } return true; } void parseOptionList(CompilerDriver::StringVector& optList ) { if (next() == EQUALS) { while (next_is_real()) { if (token == STRING || token == OPTION) optList.push_back(ConfigLexerState.StringVal); else if (!parseSubstitution(optList)) { error("Expecting a program argument or substitution", false); break; } } } else error("Expecting '='"); } void parseVersion() { if (next() != EQUALS) error("Expecting '='"); while (next_is_real()) { if (token == STRING || token == OPTION) confDat->version = ConfigLexerState.StringVal; else error("Expecting a version string"); } } void parseLibs() { if (next() != EQUALS) error("Expecting '='"); std::string lib; while (parseCompleteItem(lib)) { if (!lib.empty()) { confDat->libpaths.push_back(lib); } } } void parseLang() { if (next() != SEPARATOR) error("Expecting '.'"); switch (next() ) { case LIBS: parseLibs(); break; case NAME: confDat->langName = parseName(); break; case OPT1: parseOptionList(confDat->opts[CompilerDriver::OPT_FAST_COMPILE]); break; case OPT2: parseOptionList(confDat->opts[CompilerDriver::OPT_SIMPLE]); break; case OPT3: parseOptionList(confDat->opts[CompilerDriver::OPT_AGGRESSIVE]); break; case OPT4: parseOptionList(confDat->opts[CompilerDriver::OPT_LINK_TIME]); break; case OPT5: parseOptionList( confDat->opts[CompilerDriver::OPT_AGGRESSIVE_LINK_TIME]); break; default: error("Expecting 'name' or 'optN' after 'lang.'"); break; } } bool parseProgramName(std::string& str) { str.clear(); do { switch (token) { case BINDIR_SUBST: case LLVMGCC_SUBST: case LLVMGXX_SUBST: case LLVMCC1_SUBST: case LLVMCC1PLUS_SUBST: case OPTION: case STRING: case ARGS_SUBST: case DEFS_SUBST: case IN_SUBST: case INCLS_SUBST: case LIBS_SUBST: case OPT_SUBST: case OUT_SUBST: case STATS_SUBST: case TARGET_SUBST: case TIME_SUBST: case VERBOSE_SUBST: case FOPTS_SUBST: case MOPTS_SUBST: case WOPTS_SUBST: str += ConfigLexerState.StringVal; break; case SEPARATOR: str += "."; break; case ASSEMBLY: str += "assembly"; break; case BITCODE: str += "bitcode"; break; case TRUETOK: str += "true"; break; case FALSETOK: str += "false"; break; default: break; } next(); } while (token != SPACE && token != EOFTOK && token != EOLTOK && token != ERRORTOK); return !str.empty(); } void parseCommand(CompilerDriver::Action& action) { if (next() != EQUALS) error("Expecting '='"); switch (next()) { case EOLTOK: // no value (valid) action.program.clear(); action.args.clear(); break; case SPACE: next(); /* FALL THROUGH */ default: { std::string progname; if (parseProgramName(progname)) action.program.set(progname); else error("Expecting a program name"); // Get the options std::string anOption; while (next_is_real()) { switch (token) { case STRING: case OPTION: anOption += ConfigLexerState.StringVal; break; case ASSEMBLY: anOption += "assembly"; break; case BITCODE: anOption += "bitcode"; break; case TRUETOK: anOption += "true"; break; case FALSETOK: anOption += "false"; break; case SEPARATOR: anOption += "."; break; case SPACE: action.args.push_back(anOption); anOption.clear(); break; default: if (!parseSubstitution(action.args)) error("Expecting a program argument or substitution", false); break; } } } } } void parsePreprocessor() { if (next() != SEPARATOR) error("Expecting '.'"); switch (next()) { case COMMAND: parseCommand(confDat->PreProcessor); break; case REQUIRED: if (parseBoolean()) confDat->PreProcessor.set(CompilerDriver::REQUIRED_FLAG); else confDat->PreProcessor.clear(CompilerDriver::REQUIRED_FLAG); break; default: error("Expecting 'command' or 'required' but found '" + ConfigLexerState.StringVal); break; } } bool parseOutputFlag() { if (next() == EQUALS) { if (next() == SPACE) next(); if (token == ASSEMBLY) { return true; } else if (token == BITCODE) { return false; } else { error("Expecting output type value"); return false; } if (next() != EOLTOK && token != 0) { error("Extraneous tokens after output value"); } } else error("Expecting '='"); return false; } void parseTranslator() { if (next() != SEPARATOR) error("Expecting '.'"); switch (next()) { case COMMAND: parseCommand(confDat->Translator); break; case REQUIRED: if (parseBoolean()) confDat->Translator.set(CompilerDriver::REQUIRED_FLAG); else confDat->Translator.clear(CompilerDriver::REQUIRED_FLAG); break; case PREPROCESSES: if (parseBoolean()) confDat->Translator.set(CompilerDriver::PREPROCESSES_FLAG); else confDat->Translator.clear(CompilerDriver::PREPROCESSES_FLAG); break; case OUTPUT: if (parseOutputFlag()) confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG); else confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG); break; default: error("Expecting 'command', 'required', 'preprocesses', or " "'output' but found '" + ConfigLexerState.StringVal + "' instead"); break; } } void parseOptimizer() { if (next() != SEPARATOR) error("Expecting '.'"); switch (next()) { case COMMAND: parseCommand(confDat->Optimizer); break; case PREPROCESSES: if (parseBoolean()) confDat->Optimizer.set(CompilerDriver::PREPROCESSES_FLAG); else confDat->Optimizer.clear(CompilerDriver::PREPROCESSES_FLAG); break; case TRANSLATES: if (parseBoolean()) confDat->Optimizer.set(CompilerDriver::TRANSLATES_FLAG); else confDat->Optimizer.clear(CompilerDriver::TRANSLATES_FLAG); break; case REQUIRED: if (parseBoolean()) confDat->Optimizer.set(CompilerDriver::REQUIRED_FLAG); else confDat->Optimizer.clear(CompilerDriver::REQUIRED_FLAG); break; case OUTPUT: if (parseOutputFlag()) confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG); else confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG); break; default: error(std::string("Expecting 'command', 'preprocesses', " "'translates' or 'output' but found '") + ConfigLexerState.StringVal + "' instead"); break; } } void parseAssembler() { if (next() != SEPARATOR) error("Expecting '.'"); switch(next()) { case COMMAND: parseCommand(confDat->Assembler); break; default: error("Expecting 'command'"); break; } } void parseLinker() { if (next() != SEPARATOR) error("Expecting '.'"); switch(next()) { case LIBS: break; //FIXME case LIBPATHS: break; //FIXME default: error("Expecting 'libs' or 'libpaths'"); break; } } void parseAssignment() { switch (token) { case VERSION_TOK: parseVersion(); break; case LANG: parseLang(); break; case PREPROCESSOR: parsePreprocessor(); break; case TRANSLATOR: parseTranslator(); break; case OPTIMIZER: parseOptimizer(); break; case ASSEMBLER: parseAssembler(); break; case LINKER: parseLinker(); break; case EOLTOK: break; // just ignore case ERRORTOK: default: error("Invalid top level configuration item"); break; } } void parseFile() { while ( next() != EOFTOK ) { if (token == ERRORTOK) error("Invalid token"); else if (token != EOLTOK) parseAssignment(); } provider->checkErrors(); } }; void ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) { Parser p; p.token = EOFTOK; p.provider = &provider; p.confDat = &confDat; p.parseFile(); } } CompilerDriver::ConfigData* LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) { CompilerDriver::ConfigData* result = 0; sys::Path confFile; if (configDir.isEmpty()) { // Try the environment variable const char* conf = getenv("LLVM_CONFIG_DIR"); if (conf) { confFile.set(conf); confFile.appendComponent(ftype); if (!confFile.canRead()) throw std::string("Configuration file for '") + ftype + "' is not available."; } else { // Try the user's home directory confFile = sys::Path::GetUserHomeDirectory(); if (!confFile.isEmpty()) { confFile.appendComponent(".llvm"); confFile.appendComponent("etc"); confFile.appendComponent(ftype); if (!confFile.canRead()) confFile.clear(); } if (confFile.isEmpty()) { // Okay, try the LLVM installation directory confFile = sys::Path::GetLLVMConfigDir(); confFile.appendComponent(ftype); if (!confFile.canRead()) { // Okay, try the "standard" place confFile = sys::Path::GetLLVMDefaultConfigDir(); confFile.appendComponent(ftype); if (!confFile.canRead()) { throw std::string("Configuration file for '") + ftype + "' is not available."; } } } } } else { confFile = configDir; confFile.appendComponent(ftype); if (!confFile.canRead()) throw std::string("Configuration file for '") + ftype + "' is not available."; } FileInputProvider fip( confFile.toString() ); if (!fip.okay()) { throw std::string("Configuration file for '") + ftype + "' is not available."; } result = new CompilerDriver::ConfigData(); ParseConfigData(fip,*result); return result; } LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider() { ConfigDataMap::iterator cIt = Configurations.begin(); while (cIt != Configurations.end()) { CompilerDriver::ConfigData* cd = cIt->second; ++cIt; delete cd; } Configurations.clear(); } CompilerDriver::ConfigData* LLVMC_ConfigDataProvider::ProvideConfigData(const std::string& filetype) { CompilerDriver::ConfigData* result = 0; if (!Configurations.empty()) { ConfigDataMap::iterator cIt = Configurations.find(filetype); if ( cIt != Configurations.end() ) { // We found one in the case, return it. result = cIt->second; } } if (result == 0) { // The configuration data doesn't exist, we have to go read it. result = ReadConfigData(filetype); // If we got one, cache it if (result != 0) Configurations.insert(std::make_pair(filetype,result)); } return result; // Might return 0 }