blob: bc538ca9c84029c533a21729c048c1c4a5e0129a [file] [log] [blame]
//===- 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 <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 {
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;
}
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 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 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 BYTECODE:
str += "bytecode";
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 BYTECODE:
anOption += "bytecode";
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 == BYTECODE) {
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
}