Index: include/clang/Driver/Driver.h =================================================================== --- include/clang/Driver/Driver.h +++ include/clang/Driver/Driver.h @@ -187,6 +187,9 @@ /// Name to use when invoking gcc/g++. std::string CCCGenericGCCName; + /// Name of configuration file if used. + std::string ConfigFile; + /// Whether to check that input files exist when constructing compilation /// jobs. unsigned CheckInputsExist : 1; @@ -238,6 +241,9 @@ /// Name to use when invoking gcc/g++. const std::string &getCCCGenericGCCName() const { return CCCGenericGCCName; } + const std::string &getConfigFile() const { return ConfigFile; } + void setConfigFile(StringRef x) { ConfigFile = x; } + const llvm::opt::OptTable &getOpts() const { return *Opts; } const DiagnosticsEngine &getDiags() const { return Diags; } Index: lib/Driver/Driver.cpp =================================================================== --- lib/Driver/Driver.cpp +++ lib/Driver/Driver.cpp @@ -895,6 +895,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: 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: test/Driver/Inputs/config-2.cfg =================================================================== --- /dev/null +++ test/Driver/Inputs/config-2.cfg @@ -0,0 +1,2 @@ +# target +-target x86_64-unknown-linux-gnu Index: test/Driver/config-file.c =================================================================== --- /dev/null +++ test/Driver/config-file.c @@ -0,0 +1,30 @@ +// 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 + +// 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 + +// RUN: not %clang --config inexistent.cfg -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-INEX-NOSEARCH +// CHECK-INEX-NOSEARCH: Configuration {{.*}}inexistent.cfg' specified by option '--config' cannot be found in directories: +// CHECK-INEX-NOSEARCH: ~/.llvm +// CHECK-INEX-NOSEARCH: /etc/llvm + +// RUN: %clang --config %S/Inputs/config-2.cfg -### 2>&1 | FileCheck %s -check-prefix CHECK-HHH-NOFILE +// CHECK-HHH-NOFILE: Target: x86_64-unknown-linux +// CHECK-HHH-NOFILE: Configuration file: {{.*}}/Inputs/config-2.cfg + +// 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- + +// RUN: env CLANGCFG=%S/Inputs/config-1.cfg %clang -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-ENV +// CHECK-ENV: Target: x86_64-apple-darwin +// CHECK-ENV: Configuration file: {{.*}}/Inputs/config-1.cfg +// CHECK-ENV: -Werror + +// RUN: env CLANGCFG=inexistent.cfg not %clang -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-ENV-INEX +// CHECK-ENV-INEX: Configuration file 'inexistent.cfg' specified by environment variable cannot be found Index: test/Driver/config-file2.c =================================================================== --- /dev/null +++ test/Driver/config-file2.c @@ -0,0 +1,13 @@ +// Due to ln -sf: +// REQUIRES: shell + +// RUN: mkdir -p %t.cfgtest +// RUN: PWD=`pwd` +// RUN: cd %t.cfgtest +// RUN: ln -sf %clang test123-clang +// RUN: echo "-ferror-limit=666" > test123.cfg +// RUN: cd $PWD +// RUN: %t.cfgtest/test123-clang -v -c %s -o - 2>&1 | FileCheck %s + +// CHECK: Configuration file:{{.*}}test123.cfg +// CHECK: -ferror-limit{{.*}}666 Index: tools/driver/driver.cpp =================================================================== --- tools/driver/driver.cpp +++ tools/driver/driver.cpp @@ -305,6 +305,28 @@ return 1; } +// Directories searched for configuration specified by option '--config'. +static const ArrayRef SearchDirs({ "~/.llvm", "/etc/llvm" }); + +// Directories searched for default configuration. +static const ArrayRef DefSearchDirs; + +static llvm::cl::SearchResult findConfigFileFromProgramName( + llvm::SmallVectorImpl &ConfigName, StringRef ProgramName) { + ConfigName.clear(); + StringRef PName = llvm::sys::path::stem(ProgramName); + size_t Pos = PName.find("-clang"); + if (Pos != StringRef::npos) { + ConfigName.append(PName.begin(), PName.begin() + Pos); + const StringRef Ext(".cfg"); + ConfigName.append(Ext.begin(), Ext.end()); + std::string CName(ConfigName.begin(), ConfigName.size()); + return llvm::cl::findDefaultCfgFile(ConfigName, DefSearchDirs, ProgramName, + CName); + } + return llvm::cl::SearchResult::NotSpecified; +} + int main(int argc_, const char **argv_) { llvm::sys::PrintStackTraceOnErrorSignal(argv_[0]); llvm::PrettyStackTraceProgram X(argc_, argv_); @@ -330,6 +352,47 @@ llvm::BumpPtrAllocator A; llvm::StringSaver Saver(A); + // Try reading options from configuration file. + llvm::SmallString<128> ConfigFile; + + // First try config file specified in command line. It has higher priority + // than any other way to specify configuration. + auto SRes = llvm::cl::findConfigFileFromArgs(ConfigFile, argv, SearchDirs); + if (llvm::cl::checkConfigFileSearchResult(SRes, ConfigFile, SearchDirs, + ProgName)) + return 1; + + // Environment variable has the next priority. It also specifies config file + // explicitly. + if (SRes == llvm::cl::SearchResult::NotSpecified) { + SRes = llvm::cl::findConfigFileFromEnv(ConfigFile, "CLANGCFG"); + if (llvm::cl::checkConfigFileSearchResult(SRes, ConfigFile, SearchDirs, + ProgName)) + return 1; + } + + // If config file is not specified explicitly, try determine configuration + // implicitly. First try deduce configuration from executable. For instance, + // file 'armv7l-clang' applies config file 'armv7l.cfg'. + if (SRes == llvm::cl::SearchResult::NotSpecified) { + SRes = findConfigFileFromProgramName(ConfigFile, ProgName); + if (llvm::cl::checkConfigFileSearchResult(SRes, ConfigFile, DefSearchDirs, + ProgName)) + return 1; + } + + // Finally try to find file 'clang.cfg'. + if (SRes == llvm::cl::SearchResult::NotSpecified) { + SRes = llvm::cl::findDefaultCfgFile(ConfigFile, DefSearchDirs, ProgName, + "clang.cfg"); + if (llvm::cl::checkConfigFileSearchResult(SRes, ConfigFile, DefSearchDirs, + ProgName)) + return 1; + } + + if (SRes == llvm::cl::SearchResult::Successful) + llvm::cl::readConfigFile(ConfigFile, Saver, argv); + // 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 @@ -446,6 +509,8 @@ ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false); Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags); + if (!ConfigFile.empty()) + TheDriver.setConfigFile(ConfigFile.str()); SetInstallDir(argv, TheDriver, CanonicalPrefixes); insertTargetAndModeArgs(TargetAndMode.first, TargetAndMode.second, argv,