-//===- ConfigData.cpp - Configuration Data Mgmt -----------------*- C++ -*-===//
+//===- Configuration.cpp - Configuration Data Mgmt --------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
//
//===------------------------------------------------------------------------===
-#include "ConfigData.h"
+#include "Configuration.h"
+#include "ConfigLexer.h"
#include "CompilerDriver.h"
-#include "Support/StringExtras.h"
+#include "llvm/Config/config.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/ADT/StringExtras.h"
#include <iostream>
+#include <fstream>
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 {
-// This array of strings provides the input for ".ll" files (LLVM Assembly)
-// to the configuration file parser. This data is just "built-in" to
-// llvmc so it doesn't have to be read from a configuration file.
-static const char* LL_Data[] = {
- "lang.name=LLVM Assembly",
- "lang.translator.preprocesses=false",
- "lang.translator.optimizes=No",
- "lang.translator.groks_dash_O=No",
- "lang.preprocessor.needed=0",
- "preprocessor.prog=",
- "preprocessor.args=",
- "translator.prog=llvm-as",
- "translator.args=@in@ -o @out@",
- "optimizer.prog=opt",
- "optimizer.args=@in@ -o @out@",
- "assembler.prog=llc",
- "assembler.args=@in@ -o @out@",
- "linker.prog=llvm-link",
- "linker.args=@in@ -o @out@"
-};
-
-// This array of strings provides the input for ".st" files (Stacker).
-static const char* ST_Data[] = {
- "lang.name=Stacker",
- "lang.translator.preprocesses=false",
- "lang.translator.optimizes=true",
- "lang.translator.groks_dash_O=0",
- "lang.preprocessor.needed=0",
- "preprocessor.prog=cp",
- "preprocessor.args=@in@ @out@",
- "translator.prog=stkrc",
- "translator.args=@in@ -o @out@ -S 2048",
- "optimizer.prog=opt",
- "optimizer.args=@in@ -o @out@",
- "assembler.prog=llc",
- "assembler.args=@in@ -o @out@",
- "linker.prog=llvm-link",
- "linker.args=@in@ -o @out@"
-};
-
-class InputProvider {
- public:
- virtual bool getLine(std::string& line) = 0;
- virtual void error(const std::string& msg) = 0;
- virtual bool errorOccurred() = 0;
-};
-
-class StaticInputProvider : public InputProvider {
- public:
- StaticInputProvider(const char *data[], size_t count,
- const std::string& nam) {
- TheData = data;
- limit = count;
- where = 0;
- name = nam;
- errCount = 0;
+ 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<bool> 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;
}
- virtual ~StaticInputProvider() {}
- virtual bool getLine(std::string& line) {
- if ( where >= limit ) return false;
- line = TheData[where++];
- return true;
+
+ inline bool next_is_real() {
+ next();
+ return (token != EOLTOK) && (token != ERRORTOK) && (token != 0);
}
- virtual void error(const std::string& msg) {
- std::cerr << name << ":" << where << ": Error: " << msg << "\n";
- errCount++;
+ inline void eatLineRemnant() {
+ while (next_is_real()) ;
}
- virtual bool errorOccurred() { return errCount > 0; };
+ void error(const std::string& msg, bool skip = true) {
+ provider->error(msg);
+ if (skip)
+ eatLineRemnant();
+ }
- private:
- const char**TheData;
- size_t limit;
- size_t where;
- std::string name;
- size_t errCount;
-};
+ std::string parseName() {
+ std::string result;
+ if (next() == EQUALS) {
+ while (next_is_real()) {
+ switch (token ) {
+ case STRING :
+ case OPTION :
+ result += ConfigLexerState.StringVal + " ";
+ break;
+ default:
+ error("Invalid name");
+ break;
+ }
+ }
+ if (result.empty())
+ error("Name exepected");
+ else
+ result.erase(result.size()-1,1);
+ } else
+ error("= expected");
+ return result;
+ }
-inline bool recognize(const char*& p, const char*token) {
- while (*p == *token && *token != '\0')
- ++token, p++;
- return *token == '\0' && ((*p == '\0') || ( *p == '.' ) || (*p == '='));
-}
+ bool parseBoolean() {
+ bool result = true;
+ if (next() == EQUALS) {
+ if (next() == 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;
+ }
-inline bool getBoolean(const std::string& value) {
- switch (value[0]) {
- case 't':
- case 'T':
- case '1':
- case 'y':
- case 'Y':
+ bool parseSubstitution(CompilerDriver::StringVector& optList) {
+ switch (token) {
+ case ARGS_SUBST: optList.push_back("%args%"); break;
+ case DEFS_SUBST: optList.push_back("%defs%"); break;
+ case FORCE_SUBST: optList.push_back("%force%"); break;
+ case IN_SUBST: optList.push_back("%in%"); break;
+ case INCLS_SUBST: optList.push_back("%incls%"); 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;
+ default:
+ return false;
+ }
return true;
- default :
- return false;
- }
- return false;
-}
-
-inline void skipWhitespace( size_t& pos, const std::string& line ) {
- while (pos < line.size() && (
- line[pos] == ' ' || // Space
- line[pos] == '\t' || // Horizontal Tab
- line[pos] == '\n' || // New Line
- line[pos] == '\v' || // Vertical Tab
- line[pos] == '\f' || // Form Feed
- line[pos] == '\r') // Carriate Return
- )
- pos++;
-}
+ }
-inline void parseArgs(CompilerDriver::Action& pat,
- const std::string& value,
- InputProvider& provider )
-{
- const char* p = value.c_str();
- const char* argStart = p;
- while (*p != '\0') {
- switch (*p) {
- case ' ':
- if (argStart != p)
- pat.args.push_back(std::string(argStart, p-argStart));
- argStart = ++p;
- break;
- case '@' :
- {
- if (argStart != p)
- pat.args.push_back(std::string(argStart,p-argStart));
- const char* token = ++p;
- while (*p != '@' && *p != 0)
- p++;
- if ( *p != '@' ) {
- provider.error("Unterminated substitution token");
- return;
- } else {
- p++;
- bool legal = false;
- switch (token[0]) {
- case 'i':
- if (token[1] == 'n' && token[2] == '@' ) {
- pat.inputAt = pat.args.size();
- pat.args.push_back("in");
- legal = true;
- argStart = p;
- }
- break;
- case 'o':
- if (token[1] == 'u' && token[2] == 't' && token[3] == '@') {
- pat.outputAt = pat.args.size();
- pat.args.push_back("out");
- legal = true;
- argStart = p;
- }
- break;
- default:
- break;
- }
- if (!legal) {
- provider.error("Invalid substitution token");
- return;
- }
+ 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;
}
}
- break;
- default :
- p++;
- break;
+ } else
+ error("Expecting '='");
}
- }
-}
-CompilerDriver::ConfigData*
-ParseConfigData(InputProvider& provider) {
- std::string line;
- CompilerDriver::ConfigData data;
- while ( provider.getLine(line) ) {
- // Check line length first
- size_t lineLen = line.size();
- if (lineLen > 4096)
- provider.error("length of input line (" + utostr(lineLen) +
- ") is too long");
-
- // First, skip whitespace
- size_t stPos = 0;
- skipWhitespace(stPos, line);
-
- // See if there's a hash mark. It and everything after it is
- // ignored so lets delete that now.
- size_t hashPos = line.find('#');
- if (hashPos != std::string::npos)
- line.erase(hashPos);
-
- // Make sure we have something left to parse
- if (line.size() == 0)
- continue; // ignore full-line comment or whitespace line
-
- // Find the equals sign
- size_t eqPos = line.find('=');
- if (eqPos == std::string::npos)
- provider.error("Configuration directive is missing an =");
-
- // extract the item name
- std::string name(line, stPos, eqPos-stPos);
-
- // directives without names are illegal
- if (name.empty())
- provider.error("Configuration directive name is empty");
-
- // Skip whitespace in the value
- size_t valPos = eqPos + 1;
- skipWhitespace(valPos, line);
-
- // Skip white space at end of value
- size_t endPos = line.length() - 1;
- while (line[endPos] == ' ')
- endPos--;
-
- // extract the item value
- std::string value(line, valPos, endPos-valPos+1);
-
- // Get the configuration item as a char pointer
- const char*p = name.c_str();
-
- // Indicate we haven't found an invalid item yet.
- bool invalidItem = false;
-
- // Parse the contents by examining first character and
- // using the recognize function strategically
- switch (*p++) {
- case 'l' :
- // could it be "lang."
- if (*p == 'a') { // "lang." ?
- if (recognize(p,"ang")) {
- p++;
- switch (*p++) {
- case 'n':
- if (recognize(p,"ame"))
- data.langName = value;
- else
- invalidItem = true;
- break;
- case 't':
- if (recognize(p,"ranslator")) {
- p++;
- if (recognize(p,"preprocesses"))
- data.TranslatorPreprocesses = getBoolean(value);
- else if (recognize(p, "optimizes"))
- data.TranslatorOptimizes = getBoolean(value);
- else if (recognize(p, "groks_dash_O"))
- data.TranslatorGroksDashO = getBoolean(value);
- else
- invalidItem = true;
- }
- else
- invalidItem = true;
- break;
- case 'p':
- if (recognize(p,"reprocessor")) {
- p++;
- if (recognize(p,"needed")) {
- data.PreprocessorNeeded = getBoolean(value);
- } else
- invalidItem = true;
- }
- else
- invalidItem = true;
- break;
-
- default:
- invalidItem = true;
- break;
- }
- }
- } else if (*p == 'i') { // "linker." ?
- if (recognize(p,"inker")) {
- p++;
- if (recognize(p,"prog"))
- data.Linker.program = value;
- else if (recognize(p,"args"))
- parseArgs(data.Linker,value,provider);
- else
- invalidItem = true;
- }
+ void parseVersion() {
+ if (next() == EQUALS) {
+ while (next_is_real()) {
+ if (token == STRING || token == OPTION)
+ confDat->version = ConfigLexerState.StringVal;
else
- invalidItem = true;
- } else {
- invalidItem = true;
- }
- break;
-
- case 'p' :
- if (*p == 'r') { // "preprocessor." ?
- if (recognize(p, "reprocessor")) {
- p++;
- if (recognize(p,"prog"))
- data.PreProcessor.program = value;
- else if (recognize(p,"args"))
- parseArgs(data.PreProcessor,value,provider);
- else
- invalidItem = true;
- } else
- invalidItem = true;
- } else {
- invalidItem = true;
+ error("Expecting a version string");
}
- break;
-
- case 't' :
- if (*p == 'r') { // "translator." ?
- if (recognize(p, "ranslator")) {
- p++;
- if (recognize(p,"prog"))
- data.Translator.program = value;
- else if (recognize(p,"args"))
- parseArgs(data.Translator,value,provider);
- else
- invalidItem = true;
- } else
- invalidItem = true;
+ } else
+ error("Expecting '='");
+ }
+
+ void parseLang() {
+ switch (next() ) {
+ 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;
+ }
+ }
+
+ void parseCommand(CompilerDriver::Action& action) {
+ if (next() == EQUALS) {
+ if (next() == EOLTOK) {
+ // no value (valid)
+ action.program.clear();
+ action.args.clear();
} else {
- invalidItem = true;
+ if (token == STRING || token == OPTION) {
+ action.program.set_file(ConfigLexerState.StringVal);
+ } else {
+ error("Expecting a program name");
+ }
+ while (next_is_real()) {
+ if (token == STRING || token == OPTION) {
+ action.args.push_back(ConfigLexerState.StringVal);
+ } else if (!parseSubstitution(action.args)) {
+ error("Expecting a program argument or substitution", false);
+ break;
+ }
+ }
}
- break;
-
- case 'o' :
- if (*p == 'p') { // "optimizer." ?
- if (recognize(p, "ptimizer")) {
- p++;
- if (recognize(p,"prog"))
- data.Optimizer.program = value;
- else if (recognize(p,"args"))
- parseArgs(data.Optimizer,value,provider);
- else
- invalidItem = true;
- } else
- invalidItem = true;
+ }
+ }
+
+ void parsePreprocessor() {
+ 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() == ASSEMBLY) {
+ return true;
+ } else if (token == BYTECODE) {
+ return false;
} else {
- invalidItem = true;
+ error("Expecting output type value");
+ return false;
}
- break;
- case 'a' :
- if (*p == 's') { // "assembler." ?
- if (recognize(p, "ssembler")) {
- p++;
- if (recognize(p,"prog"))
- data.Assembler.program = value;
- else if (recognize(p,"args"))
- parseArgs(data.Assembler,value,provider);
- else
- invalidItem = true;
- } else
- invalidItem = true;
- } else {
- invalidItem = true;
+ if (next() != EOLTOK && token != 0) {
+ error("Extraneous tokens after output value");
}
- break;
+ }
+ else
+ error("Expecting '='");
+ return false;
+ }
+
+ void parseTranslator() {
+ 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() {
+ 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() {
+ switch(next()) {
+ case COMMAND:
+ parseCommand(confDat->Assembler);
+ break;
+ default:
+ error("Expecting 'command'");
+ break;
+ }
+ }
+
+ void parseLinker() {
+ switch(next()) {
+ case LIBS:
+ break; //FIXME
+ case LIBPATHS:
+ break; //FIXME
+ default:
+ error("Expecting 'libs' or 'libpaths'");
+ break;
+ }
}
- if (invalidItem)
- provider.error("Invalid configuration item: " + line.substr(stPos, eqPos-stPos));
+
+ void parseAssignment() {
+ switch (token) {
+ case VERSION: 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();
}
- return new CompilerDriver::ConfigData(data);
}
CompilerDriver::ConfigData*
-ReadConfigData(const std::string& ftype) {
- if ( ftype == "ll" ) {
- StaticInputProvider sip(LL_Data, sizeof(LL_Data)/sizeof(LL_Data[0]),
- "LLVM Assembly (internal)");
- return ParseConfigData(sip);
- } else if (ftype == "st") {
- StaticInputProvider sip(ST_Data, sizeof(ST_Data)/sizeof(ST_Data[0]),
- "Stacker (internal)");
- return ParseConfigData(sip);
+LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) {
+ CompilerDriver::ConfigData* result = 0;
+ sys::Path confFile;
+ if (configDir.is_empty()) {
+ // Try the environment variable
+ const char* conf = getenv("LLVM_CONFIG_DIR");
+ if (conf) {
+ confFile.set_directory(conf);
+ confFile.append_file(ftype);
+ if (!confFile.readable())
+ throw std::string("Configuration file for '") + ftype +
+ "' is not available.";
+ } else {
+ // Try the user's home directory
+ confFile = sys::Path::GetUserHomeDirectory();
+ if (!confFile.is_empty()) {
+ confFile.append_directory(".llvm");
+ confFile.append_directory("etc");
+ confFile.append_file(ftype);
+ if (!confFile.readable())
+ confFile.clear();
+ }
+ if (!confFile.is_empty()) {
+ // Okay, try the LLVM installation directory
+ confFile = sys::Path::GetLLVMConfigDir();
+ confFile.append_file(ftype);
+ if (!confFile.readable()) {
+ // Okay, try the "standard" place
+ confFile = sys::Path::GetLLVMDefaultConfigDir();
+ confFile.append_file(ftype);
+ if (!confFile.readable()) {
+ throw std::string("Configuration file for '") + ftype +
+ "' is not available.";
+ }
+ }
+ }
+ }
+ } else {
+ confFile = configDir;
+ confFile.append_file(ftype);
+ if (!confFile.readable())
+ throw std::string("Configuration file for '") + ftype +
+ "' is not available.";
}
- return 0;
-}
-
-}
-
-LLVMC_ConfigDataProvider::LLVMC_ConfigDataProvider()
- : Configurations()
- , configDir()
-{
- Configurations.clear();
+ FileInputProvider fip( confFile.get() );
+ 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()
// The configuration data doesn't exist, we have to go read it.
result = ReadConfigData(filetype);
// If we got one, cache it
- if ( result != 0 )
+ if (result != 0)
Configurations.insert(std::make_pair(filetype,result));
}
return result; // Might return 0