sepavloff updated this revision to Diff 98330.
sepavloff added a comment.
Herald added subscribers: krytarowski, rengolin.

Updated patch


https://reviews.llvm.org/D24933

Files:
  docs/UsersManual.rst
  include/clang/Basic/DiagnosticDriverKinds.td
  include/clang/Config/config.h.cmake
  include/clang/Driver/Driver.h
  include/clang/Driver/Options.td
  include/clang/Driver/ToolChain.h
  lib/Driver/Driver.cpp
  lib/Driver/ToolChain.cpp
  lib/Tooling/Tooling.cpp
  test/Driver/Inputs/config-1.cfg
  test/Driver/Inputs/config-2.cfg
  test/Driver/Inputs/config-2a.cfg
  test/Driver/Inputs/config-3.cfg
  test/Driver/Inputs/config-4.cfg
  test/Driver/Inputs/config-5.cfg
  test/Driver/Inputs/config/config-4.cfg
  test/Driver/config-file-errs.c
  test/Driver/config-file.c
  test/Driver/config-file2.c
  tools/driver/driver.cpp

Index: tools/driver/driver.cpp
===================================================================
--- tools/driver/driver.cpp
+++ tools/driver/driver.cpp
@@ -199,23 +199,19 @@
 extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0,
                       void *MainAddr);
 
-static void insertTargetAndModeArgs(StringRef Target, StringRef Mode,
+static void insertTargetAndModeArgs(const ParsedClangName &NameParts,
                                     SmallVectorImpl<const char *> &ArgVector,
                                     std::set<std::string> &SavedStrings) {
-  if (!Mode.empty()) {
+  if (!NameParts.ModeSuffix.empty()) {
     // Add the mode flag to the arguments.
-    auto it = ArgVector.begin();
-    if (it != ArgVector.end())
-      ++it;
-    ArgVector.insert(it, GetStableCStr(SavedStrings, Mode));
+    ArgVector.insert(ArgVector.end(),
+                     GetStableCStr(SavedStrings, NameParts.ModeSuffix));
   }
 
-  if (!Target.empty()) {
-    auto it = ArgVector.begin();
-    if (it != ArgVector.end())
-      ++it;
-    const char *arr[] = {"-target", GetStableCStr(SavedStrings, Target)};
-    ArgVector.insert(it, std::begin(arr), std::end(arr));
+  if (NameParts.TargetIsValid) {
+    const char *arr[] = {"-target", GetStableCStr(SavedStrings,
+                                                  NameParts.TargetPrefix)};
+    ArgVector.insert(ArgVector.end(), std::begin(arr), std::end(arr));
   }
 }
 
@@ -323,9 +319,7 @@
   }
 
   llvm::InitializeAllTargets();
-  std::string ProgName = argv[0];
-  std::pair<std::string, std::string> TargetAndMode =
-      ToolChain::getTargetAndModeFromProgramName(ProgName);
+  auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(argv[0]);
 
   llvm::BumpPtrAllocator A;
   llvm::StringSaver Saver(A);
@@ -338,7 +332,7 @@
   // Finally, our -cc1 tools don't care which tokenization mode we use because
   // response files written by clang will tokenize the same way in either mode.
   bool ClangCLMode = false;
-  if (TargetAndMode.second == "--driver-mode=cl" ||
+  if (TargetAndMode.ModeSuffix == "--driver-mode=cl" ||
       std::find_if(argv.begin(), argv.end(), [](const char *F) {
         return F && strcmp(F, "--driver-mode=cl") == 0;
       }) != argv.end()) {
@@ -447,9 +441,9 @@
 
   Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags);
   SetInstallDir(argv, TheDriver, CanonicalPrefixes);
+  TheDriver.setTargetAndMode(TargetAndMode);
 
-  insertTargetAndModeArgs(TargetAndMode.first, TargetAndMode.second, argv,
-                          SavedStrings);
+  insertTargetAndModeArgs(TargetAndMode, argv, SavedStrings);
 
   SetBackdoorDriverOutputsFromEnvVars(TheDriver);
 
@@ -491,7 +485,6 @@
       }
     }
   }
-
   Diags.getClient()->finish();
 
   // If any timers were active but haven't been destroyed yet, print their
Index: test/Driver/config-file2.c
===================================================================
--- /dev/null
+++ test/Driver/config-file2.c
@@ -0,0 +1,107 @@
+// REQUIRES: shell
+
+//--- Invocation qqq-clang tries to find config file qqq.cfg
+//
+// RUN: mkdir -p %T/testbin
+// RUN: [ ! -s %T/testbin/qqq-clang ] || rm %T/testbin/qqq-clang
+// RUN: ln -s %clang %T/testbin/qqq-clang
+// RUN: echo "-Wundefined-func-template" > %T/testbin/qqq.cfg
+//
+// RUN: %T/testbin/qqq-clang -c -no-canonical-prefixes %s -### 2>&1 | FileCheck %s
+//
+// CHECK: Configuration file: {{.*}}/testbin/qqq.cfg
+// CHECK: -Wundefined-func-template
+
+
+//--- Config files are searched for in binary directory as well.
+//
+// RUN: [ ! -s %T/testbin/clang ] || rm %T/testbin/clang
+// RUN: ln -s %clang %T/testbin/clang
+// RUN: echo "-Werror" > %T/testbin/aaa.cfg
+//
+// RUN: %T/testbin/clang --config aaa.cfg -c -no-canonical-prefixes %s -### 2>&1 | FileCheck %s -check-prefix CHECK-BIN
+//
+// CHECK-BIN: Configuration file: {{.*}}/testbin/aaa.cfg
+// CHECK-BIN: -Werror
+
+
+//--- If config file is specified by relative path (workdir/cfg-s2), it is searched for by
+//    that path.
+//
+// RUN: mkdir -p %T/workdir
+// RUN: echo "@subdir/cfg-s2" > %T/workdir/cfg-1
+// RUN: mkdir -p %T/workdir/subdir
+// RUN: echo "-Wundefined-var-template" > %T/workdir/subdir/cfg-s2
+//
+// RUN: ( cd %T && %clang --config workdir/cfg-1 -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-REL )
+//
+// CHECK-REL: Configuration file: {{.*}}/workdir/cfg-1
+// CHECK-REL: -Wundefined-var-template
+
+
+//--- Invocation qqq-g++ tries to find config file qqq-g++.cfg first ...
+//
+// RUN: mkdir -p %T/testdmode
+// RUN: [ ! -s %T/testdmode/qqq-clang-g++ ] || rm %T/testdmode/qqq-clang-g++
+// RUN: ln -s %clang %T/testdmode/qqq-clang-g++
+// RUN: echo "-Wundefined-func-template" > %T/testdmode/qqq-g++.cfg
+// RUN: echo "-Werror" > %T/testdmode/qqq.cfg
+//
+// RUN: %T/testdmode/qqq-clang-g++ -c -no-canonical-prefixes %s -### 2>&1 | FileCheck %s -check-prefix CHECK-DMODE
+//
+// CHECK-DMODE: Configuration file: {{.*}}/testdmode/qqq-g++.cfg
+// CHECK-DMODE: -Wundefined-func-template
+// CHECK-DMODE-NOT: -Werror
+//
+//--- ... and qqq.cfg if qqq-g++.cfg is not found.
+//
+// RUN: rm %T/testdmode/qqq-g++.cfg
+//
+// RUN: %T/testdmode/qqq-clang-g++ -c -no-canonical-prefixes %s -### 2>&1 | FileCheck %s -check-prefix CHECK-DMODE2
+//
+// CHECK-DMODE2: Configuration file: {{.*}}/testdmode/qqq.cfg
+// CHECK-DMODE2: -Werror
+// CHECK-DMODE2-NOT: -Wundefined-func-template
+
+
+//--- If command line contains options that change triple (-m32), clang tries
+//    reloading config file.
+//
+// RUN: mkdir -p %T/testreload
+// RUN: [ ! -s %T/testreload/x86_64-clang ] || rm %T/testreload/x86_64-clang
+// RUN: ln -s %clang %T/testreload/x86_64-clang
+// RUN: echo "-Wundefined-func-template" > %T/testreload/x86_64.cfg
+// RUN: echo "-Werror" > %T/testreload/i386.cfg
+//
+// RUN: %T/testreload/x86_64-clang -c -m32 -no-canonical-prefixes %s -### 2>&1 | FileCheck %s -check-prefix CHECK-RELOAD
+//
+// CHECK-RELOAD: Target: i386
+// CHECK-RELOAD: Configuration file: {{.*}}/testreload/i386.cfg
+// CHECK-RELOAD-NOT: -Wundefined-func-template
+// CHECK-RELOAD: -Werror
+
+
+//--- When reloading config file, target-clang-g++ tries to find config target32-g++.cfg first ...
+//
+// RUN: mkdir -p %T/testreload2
+// RUN: [ ! -s %T/testreload2/x86_64-clang-g++ ] || rm %T/testreload2/x86_64-clang-g++
+// RUN: ln -s %clang %T/testreload2/x86_64-clang-g++
+// RUN: echo "-Wundefined-func-template" > %T/testreload2/i386-g++.cfg
+// RUN: echo "-Werror" > %T/testreload2/i386.cfg
+//
+// RUN: %T/testreload2/x86_64-clang-g++ -c -m32 -no-canonical-prefixes %s -### 2>&1 | FileCheck %s -check-prefix CHECK-RELOAD2
+//
+// CHECK-RELOAD2: Configuration file: {{.*}}/testreload2/i386-g++.cfg
+// CHECK-RELOAD2: -Wundefined-func-template
+// CHECK-RELOAD2-NOT: -Werror
+//
+//--- and target32.cfg if target32-g++.cfg is not found.
+//
+// RUN: rm %T/testreload2/i386-g++.cfg
+//
+// RUN: %T/testreload2/x86_64-clang-g++ -c -m32 -no-canonical-prefixes %s -### 2>&1 | FileCheck %s -check-prefix CHECK-RELOAD3
+//
+// CHECK-RELOAD3: Configuration file: {{.*}}/testreload2/i386.cfg
+// CHECK-RELOAD3: -Werror
+// CHECK-RELOAD3-NOT: -Wundefined-func-template
+
Index: test/Driver/config-file.c
===================================================================
--- /dev/null
+++ test/Driver/config-file.c
@@ -0,0 +1,37 @@
+//--- Config file (full path) in output of -###
+//
+// RUN: %clang --config %S/Inputs/config-1.cfg -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-HHH
+// CHECK-HHH: Target: x86_64-apple-darwin
+// CHECK-HHH: Configuration file: {{.*}}Inputs{{.}}config-1.cfg
+// CHECK-HHH: -Werror
+// CHECK-HHH: -std=c99
+
+
+//--- Nested config files
+//
+// RUN: %clang --config %S/Inputs/config-2.cfg -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-HHH2
+// CHECK-HHH2: Target: x86_64-unknown-linux
+// CHECK-HHH2: Configuration file: {{.*}}Inputs{{.}}config-2.cfg
+// CHECK-HHH2: -Wundefined-func-template
+//
+
+// RUN: %clang --config %S/Inputs/config-2a.cfg -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-HHH2a
+// CHECK-HHH2a: Target: x86_64-unknown-linux
+// CHECK-HHH2a: Configuration file: {{.*}}Inputs{{.}}config-2a.cfg
+// CHECK-HHH2a: -isysroot
+// CHECK-HHH2a-SAME: /opt/data
+
+
+//--- Config file (full path) in output of -v
+//
+// RUN: %clang --config %S/Inputs/config-1.cfg -c %s -v 2>&1 | FileCheck %s -check-prefix CHECK-V
+// CHECK-V: Target: x86_64-apple-darwin
+// CHECK-V: Configuration file: {{.*}}Inputs{{.}}config-1.cfg
+// CHECK-V: -triple{{.*}}x86_64-apple-
+
+
+//--- Unused options in config file do not produce warnings
+//
+// RUN: %clang --config %S/Inputs/config-4.cfg -c %s -v 2>&1 | FileCheck %s -check-prefix CHECK-UNUSED
+// CHECK-UNUSED-NOT: argument unused during compilation:
+
Index: test/Driver/config-file-errs.c
===================================================================
--- /dev/null
+++ test/Driver/config-file-errs.c
@@ -0,0 +1,29 @@
+//--- No more than one '--config' may be specified.
+//
+// RUN: not %clang --config 1.cfg --config 2.cfg 2>&1 | FileCheck %s -check-prefix CHECK-DUPLICATE
+// CHECK-DUPLICATE: no more than one option '--config' is allowed
+
+
+//--- '--config' must be followed by config file name.
+//
+// RUN: not %clang --config 2>&1 | FileCheck %s -check-prefix CHECK-MISSING-FILE
+// CHECK-MISSING-FILE: argument to '--config' is missing (expected 1 value)
+
+
+//--- Argument of '--config' must be existing file, if it is specified by path.
+//
+// RUN: not %clang --config somewhere/nonexistent-config-file 2>&1 | FileCheck %s -check-prefix CHECK-NONEXISTENT
+// CHECK-NONEXISTENT: configuration file '{{.*}}somewhere/nonexistent-config-file' does not exist
+
+
+//--- Argument of '--config' must exist somewhere is well-known directories, it is is specified by bare name.
+//
+// RUN: not %clang --config nonexistent-config-file 2>&1 | FileCheck %s -check-prefix CHECK-NOTFOUND
+// CHECK-NOTFOUND: configuration file 'nonexistent-config-file' cannot be found
+// CHECK-NOTFOUND: was searched for in the directory:
+
+
+//--- Argument in config file cannot cross the file boundary
+//
+// RUN: not %clang --config %S/Inputs/config-5.cfg x86_64-unknown-linux-gnu -c %s 2>&1 | FileCheck %s -check-prefix CHECK-CROSS
+// CHECK-CROSS: error: argument to '-target' is missing
Index: test/Driver/Inputs/config/config-4.cfg
===================================================================
--- /dev/null
+++ test/Driver/Inputs/config/config-4.cfg
@@ -0,0 +1 @@
+-isysroot /opt/data
Index: test/Driver/Inputs/config-5.cfg
===================================================================
--- /dev/null
+++ test/Driver/Inputs/config-5.cfg
@@ -0,0 +1,2 @@
+--serialize-diagnostics diag.ser
+-target
Index: test/Driver/Inputs/config-4.cfg
===================================================================
--- /dev/null
+++ test/Driver/Inputs/config-4.cfg
@@ -0,0 +1,2 @@
+-L/usr/local/lib
+-stdlib=libc++
\ No newline at end of file
Index: test/Driver/Inputs/config-3.cfg
===================================================================
--- /dev/null
+++ test/Driver/Inputs/config-3.cfg
@@ -0,0 +1 @@
+-Wundefined-func-template
Index: test/Driver/Inputs/config-2a.cfg
===================================================================
--- /dev/null
+++ test/Driver/Inputs/config-2a.cfg
@@ -0,0 +1,5 @@
+# target
+-target x86_64-unknown-linux-gnu
+
+# nested inclusion
+@config/config-4.cfg
Index: test/Driver/Inputs/config-2.cfg
===================================================================
--- /dev/null
+++ test/Driver/Inputs/config-2.cfg
@@ -0,0 +1,5 @@
+# target
+-target x86_64-unknown-linux-gnu
+
+# nested inclusion
+@config-3.cfg
Index: test/Driver/Inputs/config-1.cfg
===================================================================
--- /dev/null
+++ test/Driver/Inputs/config-1.cfg
@@ -0,0 +1,7 @@
+
+    
+# Empty lines and line started with # are ignored
+-Werror -std=\
+c99
+    # Target
+    -target x86_64-apple-darwin
\ No newline at end of file
Index: lib/Tooling/Tooling.cpp
===================================================================
--- lib/Tooling/Tooling.cpp
+++ lib/Tooling/Tooling.cpp
@@ -189,11 +189,12 @@
     }
     auto TargetMode =
         clang::driver::ToolChain::getTargetAndModeFromProgramName(InvokedAs);
-    if (!AlreadyHasMode && !TargetMode.second.empty()) {
-      CommandLine.insert(++CommandLine.begin(), TargetMode.second);
+    if (!AlreadyHasMode && !TargetMode.ModeSuffix.empty()) {
+      CommandLine.insert(++CommandLine.begin(), TargetMode.ModeSuffix);
     }
-    if (!AlreadyHasTarget && !TargetMode.first.empty()) {
-      CommandLine.insert(++CommandLine.begin(), {"-target", TargetMode.first});
+    if (!AlreadyHasTarget && TargetMode.TargetIsValid) {
+      CommandLine.insert(++CommandLine.begin(), {"-target",
+                                                 TargetMode.TargetPrefix});
     }
   }
 }
Index: lib/Driver/ToolChain.cpp
===================================================================
--- lib/Driver/ToolChain.cpp
+++ lib/Driver/ToolChain.cpp
@@ -176,28 +176,25 @@
 }
 } // anonymous namespace
 
-std::pair<std::string, std::string>
+ParsedClangName
 ToolChain::getTargetAndModeFromProgramName(StringRef PN) {
   std::string ProgName = normalizeProgramName(PN);
   const DriverSuffix *DS = parseDriverSuffix(ProgName);
   if (!DS)
-    return std::make_pair("", "");
+    return ParsedClangName();
   std::string ModeFlag = DS->ModeFlag == nullptr ? "" : DS->ModeFlag;
 
   std::string::size_type LastComponent =
       ProgName.rfind('-', ProgName.size() - strlen(DS->Suffix));
   if (LastComponent == std::string::npos)
-    return std::make_pair("", ModeFlag);
+    return ParsedClangName(ModeFlag);
 
   // Infer target from the prefix.
   StringRef Prefix(ProgName);
   Prefix = Prefix.slice(0, LastComponent);
   std::string IgnoredError;
-  std::string Target;
-  if (llvm::TargetRegistry::lookupTarget(Prefix, IgnoredError)) {
-    Target = Prefix;
-  }
-  return std::make_pair(Target, ModeFlag);
+  bool IsRegistered = llvm::TargetRegistry::lookupTarget(Prefix, IgnoredError);
+  return ParsedClangName{Prefix, ModeFlag, IsRegistered};
 }
 
 StringRef ToolChain::getDefaultUniversalArchName() const {
Index: lib/Driver/Driver.cpp
===================================================================
--- lib/Driver/Driver.cpp
+++ lib/Driver/Driver.cpp
@@ -61,13 +61,15 @@
 #include "llvm/Option/OptSpecifier.h"
 #include "llvm/Option/OptTable.h"
 #include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/PrettyStackTrace.h"
 #include "llvm/Support/Process.h"
 #include "llvm/Support/Program.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/StringSaver.h"
 #include <map>
 #include <memory>
 #include <utility>
@@ -90,7 +92,8 @@
       CCPrintHeadersFilename(nullptr), CCLogDiagnosticsFilename(nullptr),
       CCCPrintBindings(false), CCPrintHeaders(false), CCLogDiagnostics(false),
       CCGenDiagnostics(false), DefaultTargetTriple(DefaultTargetTriple),
-      CCCGenericGCCName(""), CheckInputsExist(true), CCCUsePCH(true),
+      CCCGenericGCCName(""), Saver(Alloc),
+      CheckInputsExist(true), CCCUsePCH(true),
       GenReproducer(false), SuppressMissingInputWarning(false) {
 
   // Provide a sane fallback if no VFS is specified.
@@ -117,9 +120,8 @@
 
 void Driver::ParseDriverMode(StringRef ProgramName,
                              ArrayRef<const char *> Args) {
-  auto Default = ToolChain::getTargetAndModeFromProgramName(ProgramName);
-  StringRef DefaultMode(Default.second);
-  setDriverModeFromOption(DefaultMode);
+  ClangNameParts = ToolChain::getTargetAndModeFromProgramName(ProgramName);
+  setDriverModeFromOption(ClangNameParts.ModeSuffix);
 
   for (const char *ArgPtr : Args) {
     // Ingore nullptrs, they are response file's EOL markers
@@ -571,6 +573,213 @@
   return;
 }
 
+/// Directories searched for configuration files.
+///
+static const ArrayRef<const char *> CfgFileSearchDirs = {
+#if defined(CLANG_CONFIG_FILE_USER_DIR)
+  CLANG_CONFIG_FILE_USER_DIR,
+#endif
+#if defined(CLANG_CONFIG_FILE_SYSTEM_DIR)
+  CLANG_CONFIG_FILE_SYSTEM_DIR
+#endif
+};
+
+/// Search the given directories for the specified file.
+///
+/// \param FilePath [out] Full file path, if it was found.
+/// \param FileName [in] The file name to search for.
+/// \param Directories [in] List of directories to search.
+/// \return True if the file was found.
+///
+static bool searchDirectoriesForFile(SmallVectorImpl<char> &FilePath,
+                                     std::string FileName,
+                                     ArrayRef<const char *> Directories) {
+  for (const char *Dir : Directories) {
+    assert(Dir);
+    FilePath.clear();
+    llvm::sys::path::append(FilePath, Dir, FileName);
+    llvm::sys::path::native(FilePath);
+    if (llvm::sys::fs::is_regular_file(FilePath))
+      return true;
+  }
+  return false;
+}
+
+/// Looks for the specified file in well-known directories.
+///
+/// \param FilePath [out] File path, if the file was found.
+/// \param Dirs [in] Directories used for the search.
+/// \param BinDirectory [in] Path to the directory where executable
+/// resides or empty string.
+/// \param FileName [in] Name of the file to search for.
+/// \return True if file was found.
+///
+/// Looks for file specified by FileName sequentially in directories specified
+/// by Dirs and BinDirectory.
+///
+static bool searchForFile(SmallVectorImpl<char> &FilePath,
+                          ArrayRef<const char *> Dirs,
+                          StringRef BinDirectory,
+                          StringRef FileName) {
+  FilePath.clear();
+  if (searchDirectoriesForFile(FilePath, FileName, Dirs))
+    return true;
+
+  // If not found, try searching the directory where executable resides.
+  FilePath.clear();
+  if (!BinDirectory.empty()) {
+    llvm::sys::path::append(FilePath, BinDirectory, FileName);
+    if (llvm::sys::fs::is_regular_file(FilePath))
+      return true;
+  }
+  FilePath.clear();
+  return false;
+}
+
+bool Driver::readConfigFile(StringRef FileName) {
+  // Try reading the given file.
+  SmallVector<const char *, 32> NewCfgArgs;
+  if (!llvm::cl::readConfigFile(FileName, Saver, NewCfgArgs)) {
+    Diag(diag::err_drv_cannot_read_config_file) << FileName;
+    return true;
+  }
+
+  // Update Driver state.
+  llvm::SmallString<128> CfgFileName(FileName);
+  llvm::sys::path::native(CfgFileName);
+  ConfigFile = CfgFileName.str();
+  CfgOptions = llvm::make_unique<InputArgList>(ParseArgStrings(NewCfgArgs));
+
+  // Claim all arguments that come from a configuration file so that the driver
+  // does not warn on any that is unused.
+  for (auto *A : *CfgOptions)
+    A->claim();
+  return false;
+}
+
+bool Driver::loadConfigFile() {
+  std::string CfgFileName;
+  bool FileSpecifiedExplicitly = false;
+
+  // First try to find config file specified in command line.
+  if (CLOptions) {
+    std::vector<std::string> ConfigFiles =
+        CLOptions->getAllArgValues(options::OPT_config);
+    if (ConfigFiles.size() > 1) {
+      Diag(diag::err_drv_duplicate_config);
+      return true;
+    }
+
+    if (!ConfigFiles.empty()) {
+      CfgFileName = ConfigFiles.front();
+      assert(!CfgFileName.empty());
+
+      // If argument contains directory separator, treat it as a path to
+      // configuration file.
+      if (llvm::sys::path::has_parent_path(CfgFileName)) {
+        SmallString<128> CfgFilePath;
+        if (llvm::sys::path::is_relative(CfgFileName))
+          llvm::sys::fs::current_path(CfgFilePath);
+        llvm::sys::path::append(CfgFilePath, CfgFileName);
+        if (!llvm::sys::fs::is_regular_file(CfgFilePath)) {
+          Diag(diag::err_drv_config_file_not_exist) << CfgFilePath;
+          return true;
+        }
+        return readConfigFile(CfgFilePath);
+      }
+
+      FileSpecifiedExplicitly = true;
+    }
+  }
+
+  // If config file is not specified explicitly, try to deduce configuration
+  // from executable name. For instance, a file 'armv7l-clang' will search for
+  // config file 'armv7l.cfg'.
+  if (CfgFileName.empty() && !ClangNameParts.TargetPrefix.empty())
+    CfgFileName = ClangNameParts.TargetPrefix;
+
+  if (CfgFileName.empty())
+    return false;
+
+  // If driver mode is specified, explicitly or encoded in executable name, try
+  // first to find config file for this driver mode. For instance, if executable
+  // file is 'armv7l-clang-g++', try first to find 'armv7l-g++.cfg', if it is
+  // not found, look for 'armv7l.cfg'.
+  SmallString<32> ModeSuffix;
+  if (Arg *ModeArg = CLOptions->getLastArg(options::OPT_driver_mode)) {
+    ModeSuffix = "-";
+    ModeSuffix += ModeArg->getValue();
+    if (!FileSpecifiedExplicitly)
+      ModeSuffix += ".cfg";
+  }
+
+
+  // If config file starts with architecture name and command line options
+  // redefine the architecture (with options like -m32 -LE etc), try finding
+  // new config file with that architecture.
+  SmallString<128> FixedConfigFile;
+  {
+    StringRef FileName(llvm::sys::path::filename(CfgFileName));
+    StringRef CfgFileArch = FileName;
+    size_t ArchLen = CfgFileArch.find_first_of('-');
+    if (ArchLen != StringRef::npos) {
+      CfgFileArch.take_front(ArchLen);
+    } else {
+      ArchLen = CfgFileArch.find_last_of('.');
+      if (ArchLen == StringRef::npos)
+        ArchLen = CfgFileArch.size();
+    }
+    llvm::Triple CfgTriple(llvm::Triple::normalize(CfgFileArch));
+    if (CfgTriple.getArch() != llvm::Triple::ArchType::UnknownArch) {
+      llvm::Triple EffectiveTriple = computeTargetTriple(*this,
+                                             CfgTriple.getTriple(), *CLOptions);
+      if (CfgTriple.getArch() != EffectiveTriple.getArch()) {
+        FixedConfigFile = EffectiveTriple.getArchName();
+        if (ArchLen < FileName.size())
+          FixedConfigFile += FileName.substr(ArchLen);
+      }
+    }
+  }
+
+  // Try to find config file. For invocation 'x86_64-clang-cl -m32 abc.cpp' we
+  // have variants: 'x86_64-cl.cfg', 'x86_64.cfg', 'i386-cl.cfg' and 'i386.cfg'.
+  llvm::SmallString<128> CfgFilePath;
+  SmallString<128> FName(FixedConfigFile);
+  if (!FixedConfigFile.empty()) {
+    if (!ModeSuffix.empty()) {
+      FName.append(ModeSuffix);
+      if (searchForFile(CfgFilePath, CfgFileSearchDirs, Dir, FName))
+        return readConfigFile(CfgFilePath);
+    }
+    FName.set_size(FixedConfigFile.size());
+    if (!FileSpecifiedExplicitly)
+      FName.append(".cfg");
+    if (searchForFile(CfgFilePath, CfgFileSearchDirs, Dir, FName))
+      return readConfigFile(CfgFilePath);
+  }
+  FName = CfgFileName;
+  if (!ModeSuffix.empty()) {
+    FName += ModeSuffix;
+    if (searchForFile(CfgFilePath, CfgFileSearchDirs, Dir, FName))
+      return readConfigFile(CfgFilePath);
+  }
+  FName.set_size(CfgFileName.size());
+  if (!FileSpecifiedExplicitly)
+    FName.append(".cfg");
+  if (searchForFile(CfgFilePath, CfgFileSearchDirs, Dir, FName))
+    return readConfigFile(CfgFilePath);
+
+  if (FileSpecifiedExplicitly) {
+    Diag(diag::err_drv_config_file_not_found) << CfgFileName;
+    for (auto SearchDir : CfgFileSearchDirs)
+      Diag(diag::note_drv_config_file_searched_in) << SearchDir;
+    Diag(diag::note_drv_config_file_searched_in) << Dir;
+    return true;
+  }
+
+  return false;
+}
+
 Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
   llvm::PrettyStackTraceString CrashInfo("Compilation construction");
 
@@ -592,15 +801,37 @@
   // how other options are parsed.
   ParseDriverMode(ClangExecutable, ArgList.slice(1));
 
+  // Arguments specified in command line.
+  CLOptions = llvm::make_unique<InputArgList>(ParseArgStrings(ArgList.slice(1)));
+  if (Diags.hasErrorOccurred())
+    return nullptr;
+
+  // Try parsing configuration file.
+  if (loadConfigFile())
+      return nullptr;
+  bool HasConfigFile = CfgOptions.get() != nullptr;
+
   // FIXME: What are we going to do with -V and -b?
 
+  // All arguments, from both config file and command line.
+  InputArgList Args = std::move(HasConfigFile ? std::move(*CfgOptions)
+                                              : std::move(*CLOptions));
+  if (HasConfigFile)
+    for (auto *Opt : *CLOptions) {
+      const Arg *BaseArg = &Opt->getBaseArg();
+      if (BaseArg == Opt)
+        BaseArg = nullptr;
+      Arg *Copy = new llvm::opt::Arg(Opt->getOption(), Opt->getSpelling(),
+                                     Args.size(), BaseArg);
+      Copy->getValues() = Opt->getValues();
+      if (Opt->isClaimed())
+        Copy->claim();
+      Args.append(Copy);
+    }
+
   // FIXME: This stuff needs to go into the Compilation, not the driver.
   bool CCCPrintPhases;
 
-  InputArgList Args = ParseArgStrings(ArgList.slice(1));
-  if (Diags.hasErrorOccurred())
-    return nullptr;
-
   // Silence driver warnings if requested
   Diags.setIgnoreAllWarnings(Args.hasArg(options::OPT_w));
 
@@ -1115,6 +1346,10 @@
 
   // Print out the install directory.
   OS << "InstalledDir: " << InstalledDir << '\n';
+
+  // If configuration file was used, print its path.
+  if (!ConfigFile.empty())
+    OS << "Configuration file: " << ConfigFile << '\n';
 }
 
 /// PrintDiagnosticCategories - Implement the --print-diagnostic-categories
Index: include/clang/Driver/ToolChain.h
===================================================================
--- include/clang/Driver/ToolChain.h
+++ include/clang/Driver/ToolChain.h
@@ -46,6 +46,23 @@
   class Tool;
   class XRayArgs;
 
+/// Helper structure used to pass information extracted from clang executable
+/// name such as `i686-linux-android-g++`.
+///
+struct ParsedClangName {
+  /// Target part of the executable name, as `i686-linux-android`.
+  std::string TargetPrefix;
+  /// Driver mode part of the executable name, as `g++`.
+  std::string ModeSuffix;
+  /// True if TargetPrefix is recognized as a registered target name.
+  bool TargetIsValid;
+
+  ParsedClangName() : TargetIsValid(false) {}
+  ParsedClangName(std::string Mode) : ModeSuffix(Mode), TargetIsValid(false) {}
+  ParsedClangName(std::string Target, std::string Mode, bool IsRegistered)
+    : TargetPrefix(Target), ModeSuffix(Mode), TargetIsValid(IsRegistered) {}
+};
+
 /// ToolChain - Access to tools for a single platform.
 class ToolChain {
 public:
@@ -193,13 +210,16 @@
   /// For example, when called with i686-linux-android-g++, the first element
   /// of the return value will be set to `"i686-linux-android"` and the second
   /// will be set to "--driver-mode=g++"`.
+  /// It is OK if the target name is not registered. In this case the return
+  /// value contains false in the field TargetIsValid.
   ///
   /// \pre `llvm::InitializeAllTargets()` has been called.
   /// \param ProgName The name the Clang driver was invoked with (from,
-  /// e.g., argv[0])
-  /// \return A pair of (`target`, `mode-flag`), where one or both may be empty.
-  static std::pair<std::string, std::string>
-  getTargetAndModeFromProgramName(StringRef ProgName);
+  /// e.g., argv[0]).
+  /// \return A structure of type ParsedClangName that contains the executable
+  /// name parts.
+  ///
+  static ParsedClangName getTargetAndModeFromProgramName(StringRef ProgName);
 
   // Tool access.
 
Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -500,6 +500,7 @@
 def client__name : JoinedOrSeparate<["-"], "client_name">;
 def combine : Flag<["-", "--"], "combine">, Flags<[DriverOption, Unsupported]>;
 def compatibility__version : JoinedOrSeparate<["-"], "compatibility_version">;
+def config : JoinedOrSeparate<["--"], "config">, HelpText<"Specifies configuration file">;
 def coverage : Flag<["-", "--"], "coverage">;
 def cpp_precomp : Flag<["-"], "cpp-precomp">, Group<clang_ignored_f_Group>;
 def current__version : JoinedOrSeparate<["-"], "current_version">;
Index: include/clang/Driver/Driver.h
===================================================================
--- include/clang/Driver/Driver.h
+++ include/clang/Driver/Driver.h
@@ -19,21 +19,15 @@
 #include "clang/Driver/Util.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/StringSaver.h"
 
 #include <list>
 #include <map>
 #include <string>
 
 namespace llvm {
 class Triple;
-
-namespace opt {
-  class Arg;
-  class ArgList;
-  class DerivedArgList;
-  class InputArgList;
-  class OptTable;
-}
 }
 
 namespace clang {
@@ -129,6 +123,9 @@
   /// The original path to the clang executable.
   std::string ClangExecutable;
 
+  /// Target and driver mode components extracted from clang executable name.
+  ParsedClangName ClangNameParts;
+
   /// The path to the installed clang directory, if any.
   std::string InstalledDir;
 
@@ -208,6 +205,21 @@
   /// Name to use when invoking gcc/g++.
   std::string CCCGenericGCCName;
 
+  /// Name of configuration file if used.
+  std::string ConfigFile;
+
+  /// Allocator for string saver.
+  llvm::BumpPtrAllocator Alloc;
+
+  /// Object that stores strings read from configuration file.
+  llvm::StringSaver Saver;
+
+  /// Arguments originated from configuration file.
+  std::unique_ptr<llvm::opt::InputArgList> CfgOptions;
+
+  /// Arguments originated from command line.
+  std::unique_ptr<llvm::opt::InputArgList> CLOptions;
+
   /// Whether to check that input files exist when constructing compilation
   /// jobs.
   unsigned CheckInputsExist : 1;
@@ -277,6 +289,8 @@
   /// Name to use when invoking gcc/g++.
   const std::string &getCCCGenericGCCName() const { return CCCGenericGCCName; }
 
+  const std::string &getConfigFile() const { return ConfigFile; }
+
   const llvm::opt::OptTable &getOpts() const { return *Opts; }
 
   const DiagnosticsEngine &getDiags() const { return Diags; }
@@ -287,6 +301,8 @@
 
   void setCheckInputsExist(bool Value) { CheckInputsExist = Value; }
 
+  void setTargetAndMode(const ParsedClangName &TM) { ClangNameParts = TM; }
+
   const std::string &getTitle() { return DriverTitle; }
   void setTitle(std::string Value) { DriverTitle = std::move(Value); }
 
@@ -486,6 +502,18 @@
   LTOKind getLTOMode() const { return LTOMode; }
 
 private:
+
+  /// Tries to load options from configuration file.
+  ///
+  /// \returns true if error occurred.
+  bool loadConfigFile();
+
+  /// Read options from the specified file.
+  ///
+  /// \param [in] FileName File to read.
+  /// \returns true, if error occurred while reading.
+  bool readConfigFile(StringRef FileName);
+
   /// Set the driver mode (cl, gcc, etc) from an option string of the form
   /// --driver-mode=<mode>.
   void setDriverModeFromOption(StringRef Opt);
Index: include/clang/Config/config.h.cmake
===================================================================
--- include/clang/Config/config.h.cmake
+++ include/clang/Config/config.h.cmake
@@ -29,6 +29,10 @@
 /* Directories clang will search for headers */
 #define C_INCLUDE_DIRS "${C_INCLUDE_DIRS}"
 
+/* Directories clang will search for configuration files */
+#cmakedefine CLANG_CONFIG_FILE_SYSTEM_DIR ${CLANG_CONFIG_FILE_SYSTEM_DIR}
+#cmakedefine CLANG_CONFIG_FILE_USER_DIR ${CLANG_CONFIG_FILE_USER_DIR}
+
 /* Default <path> to all compiler invocations for --sysroot=<path>. */
 #define DEFAULT_SYSROOT "${DEFAULT_SYSROOT}"
 
Index: include/clang/Basic/DiagnosticDriverKinds.td
===================================================================
--- include/clang/Basic/DiagnosticDriverKinds.td
+++ include/clang/Basic/DiagnosticDriverKinds.td
@@ -103,6 +103,16 @@
   "invalid argument '%0' to -fdebug-prefix-map">;
 def err_drv_malformed_sanitizer_blacklist : Error<
   "malformed sanitizer blacklist: '%0'">;
+def err_drv_duplicate_config : Error<
+  "no more than one option '--config' is allowed">;
+def err_drv_config_file_not_exist : Error<
+  "configuration file '%0' does not exist">;
+def err_drv_config_file_not_found : Error<
+  "configuration file '%0' cannot be found">;
+def note_drv_config_file_searched_in : Note<
+  "was searched for in the directory: %0">;
+def err_drv_cannot_read_config_file : Error<
+  "cannot read configuration file '%0'">;
 
 def err_target_unsupported_arch
   : Error<"the target architecture '%0' is not supported by the target '%1'">;
Index: docs/UsersManual.rst
===================================================================
--- docs/UsersManual.rst
+++ docs/UsersManual.rst
@@ -655,6 +655,75 @@
 option tells Clang to put double-quotes around the entire filename, which
 is the convention used by NMake and Jom.
 
+Configuration files
+-------------------
+
+Configuration files group command line options and allow to specify all of
+them just by referencing the configuration file. They may be used, for
+instance, to collect options required to tune compilation for particular
+target, such as -L, -I, -l, --sysroot, codegen options etc.
+
+The command line option `--config` can be used to specify configuration
+file in a Clang invocation. For instance:
+
+::
+
+    clang --config /home/user/cfgs/testing.txt
+    clang --config debug.cfg
+
+If the provided argument contains a directory separator, it is considered as
+a file path, options are read from that file. Otherwise the argument is treated
+as a file name and is searched for sequentially in the directories:
+    - user directory,
+    - system directory,
+    - the directory where Clang executable resides.
+Both user and system directory for configuration files are specified during
+clang build using cmake parameters, CLANG_CONFIG_FILE_USER_DIR and
+CLANG_CONFIG_FILE_SYSTEM_DIR respectively. The first found file is used. It is
+an error if the required file cannot be found.
+
+Another way to specify configuration file is to encode it in executable name. For
+instance, if Clang executable is named `armv7l-clang` (it may be a symbolic link
+to `clang`), then Clang will search file `armv7l.cfg` in the directory where Clang
+resides.
+
+If driver mode is specified in invocation, Clang tries to find file specific for
+the specified mode. For instance, if executable file is `x86_64-clang-cl`, Clang
+first looks for `x86_64-cl.cfg` and if it is not found, looks for `x86_64.cfg'.
+
+If command line contains options that effectively changes target architecture
+(these are -m32, -EL and some other) and configuration file starts with architecture
+name, Clang tries to load config file for effective architecture. For instance,
+invocation:
+
+::
+
+    x86_64-clang -m32 abc.c
+
+makes Clang to search file `i368.cfg` first, and if it is not found, Clang looks
+for the file `x86_64.cfg`.
+
+The configuration file consists of command line options specified on one or several
+lines. Lines composed of whitespace characters only are ignored as well as lines in
+which the first non-blank character is `#`. Long options may be split between several
+lines by trailing backslash. Here is an example of config file:
+
+::
+
+    # Several options on line
+    -c --target=x86_64-unknown-linux-gnu
+
+    # Long option split between lines
+    -I/usr/lib/gcc/x86_64-linux-gnu/5.4.0/../../../../\
+    include/c++/5.4.0
+
+    # other config files may be included
+    @linux.options
+
+Files included by directives `@file` in configuration files are resolved relative to
+the including file. For instance if a config file `~/.llvm/target.cfg` contains
+directive `@os/linux.opts`, the file `linux.opts` is searched for in the directory
+`~/.llvm/os`.
 
 Language and Target-Independent Features
 ========================================
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to