push bytecode decompressor out through APIs. Now the bytecode reader
[oota-llvm.git] / tools / llvmc / Configuration.cpp
index 1463ecaa7eea1b23e6a8c1f9c1463f4e4fe7ac64..1364e9b2ebfc4c101fe11c65e40e55c839ce03f5 100644 (file)
@@ -1,34 +1,40 @@
 //===- Configuration.cpp - Configuration Data Mgmt --------------*- C++ -*-===//
-// 
+//
 //                     The LLVM Compiler Infrastructure
 //
-// This file was developed by Reid Spencer and is distributed under the 
+// 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 "Support/CommandLine.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: " << 
+    std::cerr << name << ":" << ConfigLexerState.lineNum << ": Error: " <<
       msg << "\n";
     errCount++;
   }
@@ -47,7 +53,7 @@ namespace {
   class FileInputProvider : public InputProvider {
     public:
       FileInputProvider(const std::string & fname)
-        : InputProvider(fname) 
+        : InputProvider(fname)
         , F(fname.c_str()) {
         ConfigLexerInput = this;
       }
@@ -65,8 +71,8 @@ namespace {
       std::ifstream F;
   };
 
-  cl::opt<bool> DumpTokens("dump-tokens", cl::Optional, cl::Hidden, cl::init(false),
-    cl::desc("Dump lexical tokens (debug use only)."));
+  cl::opt<bool> DumpTokens("dump-tokens", cl::Optional, cl::Hidden,
+    cl::init(false), cl::desc("Dump lexical tokens (debug use only)."));
 
   struct Parser
   {
@@ -84,19 +90,19 @@ namespace {
     InputProvider* provider;
     CompilerDriver::ConfigData* confDat;
 
-    int next() { 
+    inline int next() {
       token = Configlex();
-      if (DumpTokens) 
+      if (DumpTokens)
         std::cerr << token << "\n";
       return token;
     }
 
-    bool next_is_real() { 
+    inline bool next_is_real() {
       next();
       return (token != EOLTOK) && (token != ERRORTOK) && (token != 0);
     }
 
-    void eatLineRemnant() {
+    inline void eatLineRemnant() {
       while (next_is_real()) ;
     }
 
@@ -106,33 +112,46 @@ namespace {
         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) {
-        while (next_is_real()) {
-          switch (token ) {
-            case STRING :
-            case OPTION : 
-              result += ConfigLexerState.StringVal + " ";
-              break;
-            default:
-              error("Invalid name");
-              break;
-          }
-        }
+        if (parseCompleteItem(result))
+          eatLineRemnant();
         if (result.empty())
           error("Name exepected");
-        else
-          result.erase(result.size()-1,1);
       } else
-        error("= expected");
+        error("Expecting '='");
       return result;
     }
 
     bool parseBoolean() {
       bool result = true;
       if (next() == EQUALS) {
-        if (next() == FALSETOK) {
+        if (next() == SPACE)
+          next();
+        if (token == FALSETOK) {
           result = false;
         } else if (token != TRUETOK) {
           error("Expecting boolean value");
@@ -149,12 +168,22 @@ namespace {
 
     bool parseSubstitution(CompilerDriver::StringVector& optList) {
       switch (token) {
-        case IN_SUBST:          optList.push_back("@in@"); break;
-        case OUT_SUBST:         optList.push_back("@out@"); break;
-        case TIME_SUBST:        optList.push_back("@time@"); break;
-        case STATS_SUBST:       optList.push_back("@stats@"); break;
-        case OPT_SUBST:         optList.push_back("@opt@"); break;
-        case TARGET_SUBST:      optList.push_back("@target@"); break;
+        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;
       }
@@ -175,51 +204,162 @@ namespace {
         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 NAME: 
-          confDat->langName = parseName(); 
+        case LIBS:
+          parseLibs();
           break;
-        case OPT1: 
-          parseOptionList(confDat->opts[CompilerDriver::OPT_FAST_COMPILE]); 
+        case NAME:
+          confDat->langName = parseName();
           break;
-        case OPT2: 
-          parseOptionList(confDat->opts[CompilerDriver::OPT_SIMPLE]); 
+        case OPT1:
+          parseOptionList(confDat->opts[CompilerDriver::OPT_FAST_COMPILE]);
           break;
-        case OPT3: 
-          parseOptionList(confDat->opts[CompilerDriver::OPT_AGGRESSIVE]); 
+        case OPT2:
+          parseOptionList(confDat->opts[CompilerDriver::OPT_SIMPLE]);
           break;
-        case OPT4: 
-          parseOptionList(confDat->opts[CompilerDriver::OPT_LINK_TIME]); 
+        case OPT3:
+          parseOptionList(confDat->opts[CompilerDriver::OPT_AGGRESSIVE]);
           break;
-        case OPT5: 
+        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.'"); 
+        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 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) {
-        if (next() == EOLTOK) {
+      if (next() != EQUALS)
+        error("Expecting '='");
+      switch (next()) {
+        case EOLTOK:
           // no value (valid)
           action.program.clear();
           action.args.clear();
-        } else {
-          if (token == STRING || token == OPTION) {
-            action.program = ConfigLexerState.StringVal;
-          } else {
+          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()) {
-            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;
+            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;
             }
           }
         }
@@ -227,6 +367,8 @@ namespace {
     }
 
     void parsePreprocessor() {
+      if (next() != SEPARATOR)
+        error("Expecting '.'");
       switch (next()) {
         case COMMAND:
           parseCommand(confDat->PreProcessor);
@@ -238,14 +380,38 @@ namespace {
             confDat->PreProcessor.clear(CompilerDriver::REQUIRED_FLAG);
           break;
         default:
-          error("Expecting 'command' or 'required'");
+          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: 
+        case COMMAND:
           parseCommand(confDat->Translator);
           break;
         case REQUIRED:
@@ -257,36 +423,27 @@ namespace {
         case PREPROCESSES:
           if (parseBoolean())
             confDat->Translator.set(CompilerDriver::PREPROCESSES_FLAG);
-          else 
-            confDat->Translator.clear(CompilerDriver::PREPROCESSES_FLAG);
-          break;
-        case OPTIMIZES:
-          if (parseBoolean())
-            confDat->Translator.set(CompilerDriver::OPTIMIZES_FLAG);
           else
-            confDat->Translator.clear(CompilerDriver::OPTIMIZES_FLAG);
-          break;
-        case GROKS_DASH_O:
-          if (parseBoolean())
-            confDat->Translator.set(CompilerDriver::GROKS_DASH_O_FLAG);
-          else
-            confDat->Translator.clear(CompilerDriver::GROKS_DASH_O_FLAG);
+            confDat->Translator.clear(CompilerDriver::PREPROCESSES_FLAG);
           break;
-        case OUTPUT_IS_ASM:
-          if (parseBoolean())
+        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', "
-                "'groks_dash_O' or 'optimizes'");
+          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);
@@ -303,25 +460,29 @@ namespace {
           else
             confDat->Optimizer.clear(CompilerDriver::TRANSLATES_FLAG);
           break;
-        case GROKS_DASH_O:
+        case REQUIRED:
           if (parseBoolean())
-            confDat->Optimizer.set(CompilerDriver::GROKS_DASH_O_FLAG);
+            confDat->Optimizer.set(CompilerDriver::REQUIRED_FLAG);
           else
-            confDat->Optimizer.clear(CompilerDriver::GROKS_DASH_O_FLAG);
+            confDat->Optimizer.clear(CompilerDriver::REQUIRED_FLAG);
           break;
-        case OUTPUT_IS_ASM:
-          if (parseBoolean())
+        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' or 'groks_dash_O'");
+          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);
@@ -333,24 +494,22 @@ namespace {
     }
 
     void parseLinker() {
+      if (next() != SEPARATOR)
+        error("Expecting '.'");
       switch(next()) {
-        case COMMAND:
-          parseCommand(confDat->Linker);
-          break;
-        case GROKS_DASH_O:
-          if (parseBoolean())
-            confDat->Linker.set(CompilerDriver::GROKS_DASH_O_FLAG);
-          else
-            confDat->Linker.clear(CompilerDriver::GROKS_DASH_O_FLAG);
-          break;
+        case LIBS:
+          break; //FIXME
+        case LIBPATHS:
+          break; //FIXME
         default:
-          error("Expecting 'command'");
+          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;
@@ -359,7 +518,7 @@ namespace {
         case LINKER:        parseLinker(); break;
         case EOLTOK:        break; // just ignore
         case ERRORTOK:
-        default:          
+        default:
           error("Invalid top level configuration item");
           break;
       }
@@ -376,50 +535,72 @@ namespace {
     }
   };
 
-  void
-  ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) {
-    Parser p;
-    p.token = EOFTOK;
-    p.provider = &provider;
-    p.confDat = &confDat;
-    p.parseFile();
+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;
-  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.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 {
-    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.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() 
-  : Configurations() 
-  , configDir() 
-{
-  Configurations.clear();
-}
-
 LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider()
 {
   ConfigDataMap::iterator cIt = Configurations.begin();
@@ -431,7 +612,7 @@ LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider()
   Configurations.clear();
 }
 
-CompilerDriver::ConfigData* 
+CompilerDriver::ConfigData*
 LLVMC_ConfigDataProvider::ProvideConfigData(const std::string& filetype) {
   CompilerDriver::ConfigData* result = 0;
   if (!Configurations.empty()) {
@@ -445,10 +626,8 @@ 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
 }
-
-// vim: sw=2 smartindent smarttab tw=80 autoindent expandtab