sepavloff updated this revision to Diff 81227.
sepavloff added a comment.

Addressed review notes.


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
  lib/Driver/Driver.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.c
  test/Driver/config-file2.c
  test/Driver/lit.local.cfg
  tools/driver/driver.cpp

Index: tools/driver/driver.cpp
===================================================================
--- tools/driver/driver.cpp
+++ tools/driver/driver.cpp
@@ -305,6 +305,41 @@
   return 1;
 }
 
+// Directories searched for configuration specified by option '--config'.
+static const ArrayRef<const char *> SearchDirs = {
+#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
+};
+
+/// Deduce configuration name if it is encoded in the executable name.
+///
+/// \param ConfigFile [out] Is assigned configuration file path.
+/// \param ProgramName [in] clang executable path.
+/// \return True if configuration file was found.
+///
+/// If clang executable is named e.g. 'armv7l-clang' the function tries to
+/// find config file 'armv7l.cfg'. If it is found, its path is put into
+/// ConfigFile and the function returns true.
+///
+static bool findConfigFileFromProgramName(
+    llvm::SmallVectorImpl<char> &ConfigFile, StringRef ProgramName) {
+  ConfigFile.clear();
+  StringRef PName = llvm::sys::path::stem(ProgramName);
+  size_t Pos = PName.find("-clang");
+  if (Pos == StringRef::npos)
+    return false;
+
+  ConfigFile.append(PName.begin(), PName.begin() + Pos);
+  const StringRef Ext(".cfg");
+  ConfigFile.append(Ext.begin(), Ext.end());
+  std::string CName(ConfigFile.begin(), ConfigFile.size());
+  return llvm::cl::searchForFile(ConfigFile, SearchDirs, ProgramName, CName);
+}
+
 int main(int argc_, const char **argv_) {
   llvm::sys::PrintStackTraceOnErrorSignal(argv_[0]);
   llvm::PrettyStackTraceProgram X(argc_, argv_);
@@ -330,6 +365,31 @@
   llvm::BumpPtrAllocator A;
   llvm::StringSaver Saver(A);
 
+  // Try reading options from configuration file.
+  llvm::SmallString<128> ConfigFile;
+  llvm::cl::SearchResult SRes;
+
+  // First try config file specified in command line. It has higher priority
+  // than any other way to specify configuration.
+  SRes = llvm::cl::findConfigFileFromArgs(ConfigFile, argv, SearchDirs, true);
+  if (llvm::cl::checkConfigFileSearchResult(SRes, ConfigFile, SearchDirs, true,
+                                            ProgName))
+    return 1;
+
+  // If config file is not specified explicitly, try to determine configuration
+  // implicitly. First try to deduce configuration from executable name. For
+  // instance, a file 'armv7l-clang' applies config file 'armv7l.cfg'. Second,
+  // try to find file 'clang.cfg'.
+  if (SRes == llvm::cl::SearchResult::NotSpecified) {
+    if (findConfigFileFromProgramName(ConfigFile, ProgName))
+      SRes = llvm::cl::SearchResult::Successful;
+  }
+
+  // If config file is found, read options from it.
+  unsigned NumConfigOptions = 0;
+  if (SRes == llvm::cl::SearchResult::Successful)
+    llvm::cl::readConfigFile(ConfigFile, Saver, argv, NumConfigOptions);
+
   // Parse response files using the GNU syntax, unless we're in CL mode. There
   // are two ways to put clang in CL compatibility mode: argv[0] is either
   // clang-cl or cl, or --driver-mode=cl is on the command line. The normal
@@ -400,8 +460,12 @@
       SmallVector<const char *, 8> PrependedOpts;
       getCLEnvVarOptions(OptCL.getValue(), Saver, PrependedOpts);
 
-      // Insert right after the program name to prepend to the argument list.
-      argv.insert(argv.begin() + 1, PrependedOpts.begin(), PrependedOpts.end());
+      // Insert right after the program name to prepend to the argument list. If
+      // there are options read from config file, put the options from "CL"
+      // after them, - config file is considered as a "patch" to compiler
+      // defaults.
+      argv.insert(argv.begin() + 1 + NumConfigOptions,
+                  PrependedOpts.begin(), PrependedOpts.end());
     }
     // Arguments in "_CL_" are appended.
     llvm::Optional<std::string> Opt_CL_ = llvm::sys::Process::GetEnv("_CL_");
@@ -446,6 +510,8 @@
   ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);
 
   Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags);
+  if (!ConfigFile.empty())
+    TheDriver.setConfigFile(ConfigFile.str(), NumConfigOptions);
   SetInstallDir(argv, TheDriver, CanonicalPrefixes);
 
   insertTargetAndModeArgs(TargetAndMode.first, TargetAndMode.second, argv,
Index: test/Driver/lit.local.cfg
===================================================================
--- test/Driver/lit.local.cfg
+++ test/Driver/lit.local.cfg
@@ -4,6 +4,8 @@
 config.substitutions.insert(0,
     ('%clang_cc1',
      """*** Do not use 'clang -cc1' in Driver tests. ***""") )
+config.substitutions.append( ('%bindir',
+                              os.path.dirname(config.clang)) )
 
 # Remove harmful environmental variables for clang Driver tests.
 # Some might be useful for other tests so they are only removed here.
Index: test/Driver/config-file2.c
===================================================================
--- /dev/null
+++ test/Driver/config-file2.c
@@ -0,0 +1,29 @@
+// 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 %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 %s -### 2>&1 | FileCheck %s -check-prefix CHECK-BIN
+// CHECK-BIN: Configuration file: {{.*}}/testbin/aaa.cfg
+// CHECK-BIN: -Werror
+
+// 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
+
Index: test/Driver/config-file.c
===================================================================
--- /dev/null
+++ test/Driver/config-file.c
@@ -0,0 +1,50 @@
+//--- 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
+
+//--- Unexistent config file (full path) in output of -###
+//
+// RUN: not %clang --config %S/Inputs/inexistent.cfg -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-INEX
+// CHECK-INEX: Configuration file {{.*}}/Inputs/inexistent.cfg' specified by option '--config' cannot be found
+
+//--- Unexistent config file (file name) in output of -###
+//
+// RUN: not %clang --config inexistent-config-file-for-tests.cfg -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-INEX-NOSEARCH
+// CHECK-INEX-NOSEARCH: Configuration {{.*}}inexistent-config-file-for-tests.cfg' specified by option '--config' cannot be found in directories:
+//
+// RUN: not %clang --config inexistent-config-file-for-tests.cfg -c %s -### 2>&1 | grep '%bindir'
+
+//--- 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:
+
+//--- 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: config file does not contain argument for the option '-target'
+
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/Driver/Driver.cpp
===================================================================
--- lib/Driver/Driver.cpp
+++ lib/Driver/Driver.cpp
@@ -61,7 +61,8 @@
       CCPrintHeadersFilename(nullptr), CCLogDiagnosticsFilename(nullptr),
       CCCPrintBindings(false), CCPrintHeaders(false), CCLogDiagnostics(false),
       CCGenDiagnostics(false), DefaultTargetTriple(DefaultTargetTriple),
-      CCCGenericGCCName(""), CheckInputsExist(true), CCCUsePCH(true),
+      CCCGenericGCCName(""), NumConfigOptions(0), NumConfigArgs(0),
+      CheckInputsExist(true), CCCUsePCH(true),
       SuppressMissingInputWarning(false) {
 
   // Provide a sane fallback if no VFS is specified.
@@ -135,9 +136,11 @@
       getIncludeExcludeOptionFlagMasks();
 
   unsigned MissingArgIndex, MissingArgCount;
+  int NumCfgArgs = static_cast<int>(NumConfigOptions);
   InputArgList Args =
       getOpts().ParseArgs(ArgStrings, MissingArgIndex, MissingArgCount,
-                          IncludedFlagsBitmask, ExcludedFlagsBitmask);
+                          IncludedFlagsBitmask, ExcludedFlagsBitmask,
+                          &NumCfgArgs);
 
   // Check for missing argument error.
   if (MissingArgCount)
@@ -157,6 +160,17 @@
     }
   }
 
+
+  // Check if the last option specified in config file takes a separate
+  // argument, the argument comes from the config file also.
+  // from outside of the file.
+  if (NumCfgArgs < 0) {
+    NumConfigArgs = static_cast<unsigned>(-NumCfgArgs);
+    Diag(diag::err_drv_incomplete_config) << ArgStrings[NumConfigOptions - 1];
+  } else {
+    NumConfigArgs = static_cast<unsigned>(NumCfgArgs);
+  }
+
   for (const Arg *A : Args.filtered(options::OPT_UNKNOWN))
     Diags.Report(IsCLMode() ? diag::warn_drv_unknown_argument_clang_cl :
                               diag::err_drv_unknown_argument)
@@ -1086,6 +1100,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
@@ -2674,6 +2692,11 @@
 void Driver::BuildJobs(Compilation &C) const {
   llvm::PrettyStackTraceString CrashInfo("Building compilation jobs");
 
+  // Clain all arguments that come from a configuration file so that the driver
+  // does not warn on any that are unused.
+  for (unsigned I = 0; I < NumConfigArgs; ++I)
+    C.getArgs().getArgs()[I]->claim();
+
   Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o);
 
   // It is an error to provide a -o option if we are making multiple output
Index: include/clang/Driver/Driver.h
===================================================================
--- include/clang/Driver/Driver.h
+++ include/clang/Driver/Driver.h
@@ -207,6 +207,17 @@
   /// Name to use when invoking gcc/g++.
   std::string CCCGenericGCCName;
 
+  /// Name of configuration file if used.
+  std::string ConfigFile;
+
+  /// Number of items in command line that came from configuration file.
+  unsigned NumConfigOptions;
+
+  /// Number of arguments originated from configuration file.
+  /// This number may be smaller that \c NumConfigOptions if some options
+  /// requires separate arguments.
+  unsigned NumConfigArgs;
+
   /// Whether to check that input files exist when constructing compilation
   /// jobs.
   unsigned CheckInputsExist : 1;
@@ -272,6 +283,13 @@
   /// Name to use when invoking gcc/g++.
   const std::string &getCCCGenericGCCName() const { return CCCGenericGCCName; }
 
+  const std::string &getConfigFile() const { return ConfigFile; }
+  void setConfigFile(StringRef FileName, unsigned N) {
+    ConfigFile = FileName;
+    NumConfigOptions = N;
+    NumConfigArgs = 0;
+  }
+
   const llvm::opt::OptTable &getOpts() const { return *Opts; }
 
   const DiagnosticsEngine &getDiags() const { return Diags; }
Index: include/clang/Config/config.h.cmake
===================================================================
--- include/clang/Config/config.h.cmake
+++ include/clang/Config/config.h.cmake
@@ -26,6 +26,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
@@ -101,6 +101,8 @@
   "invalid argument '%0' to -fdebug-prefix-map">;
 def err_drv_malformed_sanitizer_blacklist : Error<
   "malformed sanitizer blacklist: '%0'">;
+def err_drv_incomplete_config : Error<
+  "config file does not contain argument for the option '%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
@@ -644,6 +644,59 @@
 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.
+
+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.
+
+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