mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-29 10:32:47 +00:00
dd04df0ec3
Get rid of the difference between file paths and directory paths. The Path class now simply stores a path that can refer to either a file or a directory. This required various changes in the implementation and interface of the class with the corresponding impact to its users. Doxygen comments were also updated to reflect these changes. Interface changes are: appendDirectory -> appendComponent appendFile -> appendComponent elideDirectory -> eraseComponent elideFile -> eraseComponent elideSuffix -> eraseSuffix renameFile -> rename setDirectory -> set setFile -> set Changes pass Dejagnu and llvm-test/SingleSource tests. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@22349 91177308-0d34-0410-b5e6-96231b3b80d8
627 lines
18 KiB
C++
627 lines
18 KiB
C++
//===- 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
|
|
}
|