Since several tools and examples want JIT support, factor out the process of
[oota-llvm.git] / tools / llvmc / Configuration.cpp
index d2e788bf12b9bba6271e76792a2dbab515b4d7c3..6b5d33975f2c0a4f2a57c0e5317bf772f5661dc5 100644 (file)
@@ -1,4 +1,4 @@
-//===- 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;
 
-extern int ::Configlex();
+namespace sys {
+  // From CompilerDriver.cpp (for now)
+  extern bool FileIsReadable(const std::string& fname);
+}
 
 namespace llvm {
-  ConfigLexerInfo ConfigLexerData;
+  ConfigLexerInfo ConfigLexerState;
   InputProvider* ConfigLexerInput = 0;
-  unsigned ConfigLexerLine = 1;
 
   InputProvider::~InputProvider() {}
   void InputProvider::error(const std::string& msg) {
-    std::cerr << name << ":" << ConfigLexerLine << ": Error: " << msg << "\n";
+    std::cerr << name << ":" << ConfigLexerState.lineNum << ": Error: " << 
+      msg << "\n";
     errCount++;
   }
 
@@ -66,21 +71,38 @@ namespace {
       std::ifstream F;
   };
 
-  struct ParseContext
+  cl::opt<bool> DumpTokens("dump-tokens", cl::Optional, cl::Hidden, cl::init(false),
+    cl::desc("Dump lexical tokens (debug use only)."));
+
+  struct Parser
   {
-    int token;
+    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;
-    CompilerDriver::Action* action;
 
-    int next() { return token = Configlex(); }
+    inline int next() { 
+      token = Configlex();
+      if (DumpTokens) 
+        std::cerr << token << "\n";
+      return token;
+    }
 
-    bool next_is_real() { 
-      token = Configlex(); 
+    inline bool next_is_real() { 
+      next();
       return (token != EOLTOK) && (token != ERRORTOK) && (token != 0);
     }
 
-    void eatLineRemnant() {
+    inline void eatLineRemnant() {
       while (next_is_real()) ;
     }
 
@@ -97,7 +119,7 @@ namespace {
           switch (token ) {
             case STRING :
             case OPTION : 
-              result += ConfigLexerData.StringVal + " ";
+              result += ConfigLexerState.StringVal + " ";
               break;
             default:
               error("Invalid name");
@@ -131,123 +153,255 @@ namespace {
       return result;
     }
 
-    void parseLang() {
-      if ( next() == NAME ) {
-        confDat->langName = parseName();
-      } else if (token == TRANSLATOR) {
-        switch (next()) {
-          case PREPROCESSES:
-            confDat->TranslatorPreprocesses = parseBoolean();
-            break;
-          case OPTIMIZES:
-            confDat->TranslatorOptimizes = parseBoolean();
-            break;
-          case GROKS_DASH_O:
-            confDat->TranslatorGroksDashO = parseBoolean();
-            break;
-          default:
-            error("Invalid lang.translator identifier");
+    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;
+    }
+
+    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 if (token == PREPROCESSOR) {
-        if (next() == NEEDED)
-          confDat->PreprocessorNeeded = parseBoolean();
-      }
-      else {
-        error("Expecting valid identifier after 'lang.'");
+      } else
+        error("Expecting '='");
+    }
+
+    void parseVersion() {
+      if (next() == EQUALS) {
+        while (next_is_real()) {
+          if (token == STRING || token == OPTION)
+            confDat->version = ConfigLexerState.StringVal;
+          else
+            error("Expecting a version string");
+        }
+      } 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) {
-        next();
-        if (token == EOLTOK) {
+        if (next() == EOLTOK) {
           // no value (valid)
           action.program.clear();
           action.args.clear();
-          action.inputAt = 0;
-          action.outputAt = 0;
         } else {
           if (token == STRING || token == OPTION) {
-            action.program = ConfigLexerData.StringVal;
+            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(ConfigLexerData.StringVal);
-            else if (token == IN_SUBST) {
-              action.inputAt = action.args.size();
-              action.args.push_back("in");
-            } else if (token == OUT_SUBST) {
-              action.outputAt = action.args.size();
-              action.args.push_back("out");
-            } else
-              error("Expecting a program argument", false);
+            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;
+            }
           }
         }
       }
     }
 
-    void parsePreProcessor() {
-      if (next() != COMMAND) {
-        error("Expecting 'command'");
-        return;
+    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;
       }
-      parseCommand(confDat->PreProcessor);
+    }
+
+    bool parseOutputFlag() {
+      if (next() == EQUALS) {
+        if (next() == 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() != COMMAND) {
-        error("Expecting 'command'");
-        return;
+      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;
       }
-      parseCommand(confDat->Translator);
     }
 
     void parseOptimizer() {
-      if (next() != COMMAND) {
-        error("Expecting 'command'");
-        return;
+      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;
       }
-      parseCommand(confDat->Optimizer);
     }
 
     void parseAssembler() {
-      if (next() != COMMAND) {
-        error("Expecting 'command'");
-        return;
+      switch(next()) {
+        case COMMAND:
+          parseCommand(confDat->Assembler);
+          break;
+        default:
+          error("Expecting 'command'");
+          break;
       }
-      parseCommand(confDat->Assembler);
     }
 
     void parseLinker() {
-      if (next() != COMMAND) {
-        error("Expecting 'command'");
-        return;
+      switch(next()) {
+        case LIBS:
+          break; //FIXME
+        case LIBPATHS:
+          break; //FIXME
+        default:
+          error("Expecting 'libs' or 'libpaths'");
+          break;
       }
-      parseCommand(confDat->Linker);
     }
 
     void parseAssignment() {
       switch (token) {
-        case LANG:          return parseLang();
-        case PREPROCESSOR:  return parsePreProcessor();
-        case TRANSLATOR:    return parseTranslator();
-        case OPTIMIZER:     return parseOptimizer();
-        case ASSEMBLER:     return parseAssembler();
-        case LINKER:        return parseLinker();
+        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 identifier");
+          error("Invalid top level configuration item");
+          break;
       }
     }
 
     void parseFile() {
-      while ( next() != 0 ) {
-        parseAssignment();
+      while ( next() != EOFTOK ) {
+        if (token == ERRORTOK)
+          error("Invalid token");
+        else if (token != EOLTOK)
+          parseAssignment();
       }
       provider->checkErrors();
     }
@@ -255,49 +409,69 @@ namespace {
 
   void
   ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) {
-    ParseContext ctxt;
-    ctxt.token = 0;
-    ctxt.provider = &provider;
-    ctxt.confDat = &confDat;
-    ctxt.action = 0;
-    ctxt.parseFile();
+    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;
-  if (configDir.empty()) {
-    FileInputProvider fip( std::string("/etc/llvm/") + ftype );
-    if (!fip.okay()) {
-      fip.error("Configuration for '" + ftype + "' is not available.");
-      fip.checkErrors();
-    }
-    else {
-      result = new CompilerDriver::ConfigData();
-      ParseConfigData(fip,*result);
+  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 {
-    FileInputProvider fip( configDir + "/" + ftype );
-    if (!fip.okay()) {
-      fip.error("Configuration for '" + ftype + "' is not available.");
-      fip.checkErrors();
-    }
-    else {
-      result = new CompilerDriver::ConfigData();
-      ParseConfigData(fip,*result);
-    }
+    confFile = configDir;
+    confFile.append_file(ftype);
+    if (!confFile.readable())
+      throw std::string("Configuration file for '") + ftype + 
+                        "' is not available.";
+  }
+  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() 
-  : Configurations() 
-  , configDir() 
-{
-  Configurations.clear();
-}
-
 LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider()
 {
   ConfigDataMap::iterator cIt = Configurations.begin();
@@ -323,7 +497,7 @@ LLVMC_ConfigDataProvider::ProvideConfigData(const std::string& filetype) {
     // 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