264 changes: 260 additions & 4 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
#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"
Expand All @@ -70,6 +71,7 @@
#include "llvm/Support/Program.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/StringSaver.h"
#include <map>
#include <memory>
#include <utility>
Expand All @@ -92,7 +94,8 @@ Driver::Driver(StringRef ClangExecutable, StringRef DefaultTargetTriple,
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.
Expand All @@ -103,6 +106,13 @@ Driver::Driver(StringRef ClangExecutable, StringRef DefaultTargetTriple,
Dir = llvm::sys::path::parent_path(ClangExecutable);
InstalledDir = Dir; // Provide a sensible default installed dir.

#if defined(CLANG_CONFIG_FILE_SYSTEM_DIR)
SystemConfigDir = CLANG_CONFIG_FILE_SYSTEM_DIR;
#endif
#if defined(CLANG_CONFIG_FILE_USER_DIR)
UserConfigDir = CLANG_CONFIG_FILE_USER_DIR;
#endif

// Compute the path to the resource directory.
StringRef ClangResourceDir(CLANG_RESOURCE_DIR);
SmallString<128> P(Dir);
Expand Down Expand Up @@ -600,6 +610,216 @@ void Driver::CreateOffloadingDeviceToolChains(Compilation &C,
//
}

/// Looks the given directories for the specified file.
///
/// \param[out] FilePath File path, if the file was found.
/// \param[in] Dirs Directories used for the search.
/// \param[in] FileName 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.
///
static bool searchForFile(SmallVectorImpl<char> &FilePath,
ArrayRef<std::string> Dirs,
StringRef FileName) {
SmallString<128> WPath;
for (const StringRef &Dir : Dirs) {
if (Dir.empty())
continue;
WPath.clear();
llvm::sys::path::append(WPath, Dir, FileName);
llvm::sys::path::native(WPath);
if (llvm::sys::fs::is_regular_file(WPath)) {
FilePath = std::move(WPath);
return true;
}
}
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;
}

// Read options from config file.
llvm::SmallString<128> CfgFileName(FileName);
llvm::sys::path::native(CfgFileName);
ConfigFile = CfgFileName.str();
bool ContainErrors;
CfgOptions = llvm::make_unique<InputArgList>(
ParseArgStrings(NewCfgArgs, ContainErrors));
if (ContainErrors) {
CfgOptions.reset();
return true;
}

if (CfgOptions->hasArg(options::OPT_config)) {
CfgOptions.reset();
Diag(diag::err_drv_nested_config_file);
return true;
}

// Claim all arguments that come from a configuration file so that the driver
// does not warn on any that is unused.
for (Arg *A : *CfgOptions)
A->claim();
return false;
}

bool Driver::loadConfigFile() {
std::string CfgFileName;
bool FileSpecifiedExplicitly = false;

// Process options that change search path for config files.
if (CLOptions) {
if (CLOptions->hasArg(options::OPT_config_system_dir_EQ)) {
SmallString<128> CfgDir;
CfgDir.append(
CLOptions->getLastArgValue(options::OPT_config_system_dir_EQ));
if (!CfgDir.empty()) {
if (std::error_code EC = llvm::sys::fs::make_absolute(CfgDir))
SystemConfigDir.clear();
else
SystemConfigDir = std::string(CfgDir.begin(), CfgDir.end());
}
}
if (CLOptions->hasArg(options::OPT_config_user_dir_EQ)) {
SmallString<128> CfgDir;
CfgDir.append(
CLOptions->getLastArgValue(options::OPT_config_user_dir_EQ));
if (!CfgDir.empty()) {
if (std::error_code EC = llvm::sys::fs::make_absolute(CfgDir))
UserConfigDir.clear();
else
UserConfigDir = std::string(CfgDir.begin(), CfgDir.end());
}
}
}

// 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, an executable 'armv7l-clang' will
// search for config file 'armv7l-clang.cfg'.
if (CfgFileName.empty() && !ClangNameParts.TargetPrefix.empty())
CfgFileName = ClangNameParts.TargetPrefix + '-' + ClangNameParts.ModeSuffix;

if (CfgFileName.empty())
return false;

// Determine architecture part of the file name, if it is present.
StringRef CfgFileArch = CfgFileName;
size_t ArchPrefixLen = CfgFileArch.find('-');
if (ArchPrefixLen == StringRef::npos)
ArchPrefixLen = CfgFileArch.size();
llvm::Triple CfgTriple;
CfgFileArch.take_front(ArchPrefixLen);
CfgTriple = llvm::Triple(llvm::Triple::normalize(CfgFileArch));
if (CfgTriple.getArch() == llvm::Triple::ArchType::UnknownArch)
ArchPrefixLen = 0;

if (!StringRef(CfgFileName).endswith(".cfg"))
CfgFileName += ".cfg";

// If config file starts with architecture name and command line options
// redefine architecture (with options like -m32 -LE etc), try finding new
// config file with that architecture.
SmallString<128> FixedConfigFile;
size_t FixedArchPrefixLen = 0;
if (ArchPrefixLen) {
// Get architecture name from config file name like 'i386.cfg' or
// 'armv7l-clang.cfg'.
// Check if command line options changes effective triple.
llvm::Triple EffectiveTriple = computeTargetTriple(*this,
CfgTriple.getTriple(), *CLOptions);
if (CfgTriple.getArch() != EffectiveTriple.getArch()) {
FixedConfigFile = EffectiveTriple.getArchName();
FixedArchPrefixLen = FixedConfigFile.size();
// Append the rest of original file name so that file name transforms
// like: i386-clang.cfg -> x86_64-clang.cfg.
if (ArchPrefixLen < CfgFileName.size())
FixedConfigFile += CfgFileName.substr(ArchPrefixLen);
}
}

// Prepare list of directories where config file is searched for.
SmallVector<std::string, 3> CfgFileSearchDirs;
CfgFileSearchDirs.push_back(UserConfigDir);
CfgFileSearchDirs.push_back(SystemConfigDir);
CfgFileSearchDirs.push_back(Dir);

// Try to find config file. First try file with corrected architecture.
llvm::SmallString<128> CfgFilePath;
if (!FixedConfigFile.empty()) {
if (searchForFile(CfgFilePath, CfgFileSearchDirs, FixedConfigFile))
return readConfigFile(CfgFilePath);
// If 'x86_64-clang.cfg' was not found, try 'x86_64.cfg'.
FixedConfigFile.resize(FixedArchPrefixLen);
FixedConfigFile.append(".cfg");
if (searchForFile(CfgFilePath, CfgFileSearchDirs, FixedConfigFile))
return readConfigFile(CfgFilePath);
}

// Then try original file name.
if (searchForFile(CfgFilePath, CfgFileSearchDirs, CfgFileName))
return readConfigFile(CfgFilePath);

// Finally try removing driver mode part: 'x86_64-clang.cfg' -> 'x86_64.cfg'.
if (!ClangNameParts.ModeSuffix.empty() &&
!ClangNameParts.TargetPrefix.empty()) {
CfgFileName.assign(ClangNameParts.TargetPrefix);
CfgFileName.append(".cfg");
if (searchForFile(CfgFilePath, CfgFileSearchDirs, CfgFileName))
return readConfigFile(CfgFilePath);
}

// Report error but only if config file was specified explicitly, by option
// --config. If it was deduced from executable name, it is not an error.
if (FileSpecifiedExplicitly) {
Diag(diag::err_drv_config_file_not_found) << CfgFileName;
for (const std::string &SearchDir : CfgFileSearchDirs)
if (!SearchDir.empty())
Diag(diag::note_drv_config_file_searched_in) << SearchDir;
return true;
}

return false;
}

Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
llvm::PrettyStackTraceString CrashInfo("Compilation construction");

Expand All @@ -623,12 +843,35 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {

// FIXME: What are we going to do with -V and -b?

// Arguments specified in command line.
bool ContainsError;
CLOptions = llvm::make_unique<InputArgList>(
ParseArgStrings(ArgList.slice(1), ContainsError));

// Try parsing configuration file.
if (!ContainsError)
ContainsError = loadConfigFile();
bool HasConfigFile = !ContainsError && (CfgOptions.get() != nullptr);

// 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;

bool ContainsError;
InputArgList Args = ParseArgStrings(ArgList.slice(1), ContainsError);

// Silence driver warnings if requested
Diags.setIgnoreAllWarnings(Args.hasArg(options::OPT_w));

Expand Down Expand Up @@ -1144,6 +1387,10 @@ void Driver::PrintVersion(const Compilation &C, raw_ostream &OS) const {

// 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
Expand Down Expand Up @@ -1250,6 +1497,15 @@ bool Driver::HandleImmediateArgs(const Compilation &C) {
SuppressMissingInputWarning = true;
}

if (C.getArgs().hasArg(options::OPT_v)) {
if (!SystemConfigDir.empty())
llvm::errs() << "System configuration file directory: "
<< SystemConfigDir << "\n";
if (!UserConfigDir.empty())
llvm::errs() << "User configuration file directory: "
<< UserConfigDir << "\n";
}

const ToolChain &TC = C.getDefaultToolChain();

if (C.getArgs().hasArg(options::OPT_v))
Expand Down
6 changes: 6 additions & 0 deletions clang/test/Driver/Inputs/config-1.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

# Empty lines and line started with # are ignored
-Werror

# Language
-std=c99
2 changes: 2 additions & 0 deletions clang/test/Driver/Inputs/config-2.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# nested inclusion
@config-3.cfg
2 changes: 2 additions & 0 deletions clang/test/Driver/Inputs/config-2a.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# nested inclusion
@config/config-4.cfg
1 change: 1 addition & 0 deletions clang/test/Driver/Inputs/config-3.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-Wundefined-func-template
2 changes: 2 additions & 0 deletions clang/test/Driver/Inputs/config-4.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-L/usr/local/lib
-stdlib=libc++
2 changes: 2 additions & 0 deletions clang/test/Driver/Inputs/config-5.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--serialize-diagnostics diag.ser
-target
1 change: 1 addition & 0 deletions clang/test/Driver/Inputs/config-6.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--config config-5
1 change: 1 addition & 0 deletions clang/test/Driver/Inputs/config/config-4.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-isysroot /opt/data
Empty file.
1 change: 1 addition & 0 deletions clang/test/Driver/Inputs/config/i386-qqq3.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-target i383-unknown-linux
1 change: 1 addition & 0 deletions clang/test/Driver/Inputs/config/x86_64-qqq.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-target x86_64-unknown-linux
1 change: 1 addition & 0 deletions clang/test/Driver/Inputs/config/x86_64-qqq2.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-target x86_64-unknown-linux
Empty file.
1 change: 1 addition & 0 deletions clang/test/Driver/Inputs/config2/config-4.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-Wall
Empty file.
54 changes: 54 additions & 0 deletions clang/test/Driver/config-file-errs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//--- 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)


//--- '--config' must not be found in config files.
//
// RUN: not %clang --config %S/Inputs/config-6.cfg 2>&1 | FileCheck %s -check-prefix CHECK-NESTED
// CHECK-NESTED: option '--config' is not allowed inside configuration file


//--- 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 in well-known directories, if it is specified by bare name.
//
// RUN: not %clang --config-system-dir= --config-user-dir= --config nonexistent-config-file 2>&1 | FileCheck %s -check-prefix CHECK-NOTFOUND0
// CHECK-NOTFOUND0: configuration file 'nonexistent-config-file.cfg' cannot be found
// CHECK-NOTFOUND0-NEXT: was searched for in the directory:
// CHECK-NOTFOUND0-NOT: was searched for in the directory:
//
// RUN: not %clang --config-system-dir= --config-user-dir=%S/Inputs/config2 --config nonexistent-config-file 2>&1 | FileCheck %s -check-prefix CHECK-NOTFOUND1
// CHECK-NOTFOUND1: configuration file 'nonexistent-config-file.cfg' cannot be found
// CHECK-NOTFOUND1-NEXT: was searched for in the directory: {{.*}}/Inputs/config2
// CHECK-NOTFOUND1-NEXT: was searched for in the directory:
// CHECK-NOTFOUND1-NOT: was searched for in the directory:
//
// RUN: not %clang --config-system-dir=%S/Inputs/config --config-user-dir= --config nonexistent-config-file 2>&1 | FileCheck %s -check-prefix CHECK-NOTFOUND2
// CHECK-NOTFOUND2: configuration file 'nonexistent-config-file.cfg' cannot be found
// CHECK-NOTFOUND2-NEXT: was searched for in the directory: {{.*}}/Inputs/config
// CHECK-NOTFOUND2-NEXT: was searched for in the directory:
// CHECK-NOTFOUND2-NOT: was searched for in the directory:
//
// RUN: not %clang --config-system-dir=%S/Inputs/config --config-user-dir=%S/Inputs/config2 --config nonexistent-config-file 2>&1 | FileCheck %s -check-prefix CHECK-NOTFOUND3
// CHECK-NOTFOUND3: configuration file 'nonexistent-config-file.cfg' cannot be found
// CHECK-NOTFOUND3-NEXT: was searched for in the directory: {{.*}}/Inputs/config2
// CHECK-NOTFOUND3-NEXT: was searched for in the directory: {{.*}}/Inputs/config
// CHECK-NOTFOUND3-NEXT: 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
72 changes: 72 additions & 0 deletions clang/test/Driver/config-file.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//--- Config file search directories
//
// RUN: %clang --config-system-dir=%S/Inputs/config --config-user-dir=%S/Inputs/config2 -v 2>&1 | FileCheck %s -check-prefix CHECK-DIRS
// CHECK-DIRS: System configuration file directory: {{.*}}/Inputs/config
// CHECK-DIRS: User configuration file directory: {{.*}}/Inputs/config2


//--- 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: Configuration file: {{.*}}Inputs{{.}}config-1.cfg
// CHECK-HHH: -Werror
// CHECK-HHH: -std=c99


//--- 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: Configuration file: {{.*}}Inputs{{.}}config-1.cfg
// CHECK-V: -Werror
// CHECK-V: -std=c99


//--- Config file in output of -###
//
// RUN: %clang --config-system-dir=%S/Inputs --config-user-dir= --config config-1.cfg -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-HHH2
// CHECK-HHH2: Configuration file: {{.*}}Inputs{{.}}config-1.cfg
// CHECK-HHH2: -Werror
// CHECK-HHH2: -std=c99


//--- Config file in output of -v
//
// RUN: %clang --config-system-dir=%S/Inputs --config-user-dir= --config config-1.cfg -c %s -v 2>&1 | FileCheck %s -check-prefix CHECK-V2
// CHECK-V2: Configuration file: {{.*}}Inputs{{.}}config-1.cfg
// CHECK-V2: -Werror
// CHECK-V2: -std=c99


//--- Nested config files
//
// RUN: %clang --config-system-dir=%S/Inputs --config-user-dir= --config config-2.cfg -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NESTED
// CHECK-NESTED: Configuration file: {{.*}}Inputs{{.}}config-2.cfg
// CHECK-NESTED: -Wundefined-func-template

// RUN: %clang --config-system-dir=%S/Inputs --config-user-dir= --config config-2.cfg -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NESTED2
// CHECK-NESTED2: Configuration file: {{.*}}Inputs{{.}}config-2.cfg
// CHECK-NESTED2: -Wundefined-func-template


// RUN: %clang --config %S/Inputs/config-2a.cfg -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NESTEDa
// CHECK-NESTEDa: Configuration file: {{.*}}Inputs{{.}}config-2a.cfg
// CHECK-NESTEDa: -isysroot
// CHECK-NESTEDa-SAME: /opt/data

// RUN: %clang --config-system-dir=%S/Inputs --config-user-dir= --config config-2a.cfg -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NESTED2a
// CHECK-NESTED2a: Configuration file: {{.*}}Inputs{{.}}config-2a.cfg
// CHECK-NESTED2a: -isysroot
// CHECK-NESTED2a-SAME: /opt/data


//--- 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:


//--- User directory is searched first.
//
// RUN: %clang --config-system-dir=%S/Inputs/config --config-user-dir=%S/Inputs/config2 --config config-4 -c %s -v 2>&1 | FileCheck %s -check-prefix CHECK-PRECEDENCE
// CHECK-PRECEDENCE: Configuration file: {{.*}}Inputs{{.}}config2{{.}}config-4.cfg
// CHECK-PRECEDENCE: -Wall
51 changes: 51 additions & 0 deletions clang/test/Driver/config-file2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// REQUIRES: x86-registered-target

//--- Invocation `clang --config x86_64-qqq -m32` loads `i386-qqq.cfg` if the latter exists.
//
// RUN: %clang --config-system-dir=%S/Inputs/config --config-user-dir= --config x86_64-qqq -m32 -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-RELOAD
// CHECK-RELOAD: Target: i386-unknown-linux
// CHECK-RELOAD: Configuration file: {{.*}}Inputs{{.}}config{{.}}i386-qqq.cfg


//--- Invocation `clang --config x86_64-qqq2 -m32` loads `i386.cfg` if the latter exists in another search directory.
//
// RUN: %clang --config-system-dir=%S/Inputs/config --config-user-dir=%S/Inputs/config2 --config x86_64-qqq2 -m32 -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-RELOAD1
// CHECK-RELOAD1: Target: i386-unknown-linux
// CHECK-RELOAD1: Configuration file: {{.*}}Inputs{{.}}config2{{.}}i386.cfg


//--- Invocation `clang --config x86_64-qqq2 -m32` loads `x86_64-qqq2.cfg` if `i386-qqq2.cfg` and `i386.cfg` do not exist.
//
// RUN: %clang --config-system-dir=%S/Inputs/config --config-user-dir= --config x86_64-qqq2 -m32 -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-RELOAD2
// note: target is overriden due to -m32
// CHECK-RELOAD2: Target: i386-unknown-linux
// CHECK-RELOAD2: Configuration file: {{.*}}Inputs{{.}}config{{.}}x86_64-qqq2.cfg


//--- Invocation `clang --config i386-qqq3 -m64` loads `x86_64.cfg` if `x86_64-qqq3.cfg` does not exist.
//
// RUN: %clang --config-system-dir=%S/Inputs/config --config-user-dir= --config i386-qqq3 -m64 -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-RELOAD3
// CHECK-RELOAD3: Target: x86_64-unknown-linux
// CHECK-RELOAD3: Configuration file: {{.*}}Inputs{{.}}config{{.}}x86_64.cfg


//--- Invocation `clang --config x86_64-qqq -target i386` loads `i386-qqq.cfg` if the latter exists.
//
// RUN: %clang --config-system-dir=%S/Inputs/config --config-user-dir= --config x86_64-qqq -target i386 -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-RELOAD4
// CHECK-RELOAD4: Target: i386
// CHECK-RELOAD4: Configuration file: {{.*}}Inputs{{.}}config{{.}}i386-qqq.cfg


//--- Invocation `clang --config x86_64-qqq2 -target i386` loads `x86_64-qqq2.cfg` if `i386-qqq2.cfg` and `i386.cfg` do not exist.
//
// RUN: %clang --config-system-dir=%S/Inputs/config --config-user-dir= --config x86_64-qqq2 -target i386 -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-RELOAD5
// note: target is overriden due to -target i386
// CHECK-RELOAD5: Target: i386
// CHECK-RELOAD5: Configuration file: {{.*}}Inputs{{.}}config{{.}}x86_64-qqq2.cfg


//--- Invocation `clang --config x86_64-qqq -target i386 -m64` loads `x86_64-qqq.cfg`.
//
// RUN: %clang --config-system-dir=%S/Inputs/config --config-user-dir= --config x86_64-qqq -target i386 -m64 -c %s -### 2>&1 | FileCheck %s -check-prefix CHECK-RELOAD6
// CHECK-RELOAD6: Target: x86_64
// CHECK-RELOAD6: Configuration file: {{.*}}Inputs{{.}}config{{.}}x86_64-qqq.cfg
98 changes: 98 additions & 0 deletions clang/test/Driver/config-file3.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// REQUIRES: shell
// REQUIRES: x86-registered-target

//--- 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-clang-g++ tries to find config file qqq-clang-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-clang-g++.cfg
// RUN: echo "-Werror" > %T/testdmode/qqq.cfg
// RUN: %T/testdmode/qqq-clang-g++ --config-system-dir= --config-user-dir= -c -no-canonical-prefixes %s -### 2>&1 | FileCheck %s -check-prefix FULL-NAME
//
// FULL-NAME: Configuration file: {{.*}}/testdmode/qqq-clang-g++.cfg
// FULL-NAME: -Wundefined-func-template
// FULL-NAME-NOT: -Werror
//
//--- File specified by --config overrides config inferred from clang executable.
//
// RUN: %T/testdmode/qqq-clang-g++ --config-system-dir=%S/Inputs/config --config-user-dir= --config i386-qqq -c -no-canonical-prefixes %s -### 2>&1 | FileCheck %s -check-prefix CHECK-EXPLICIT
//
// CHECK-EXPLICIT: Configuration file: {{.*}}/Inputs/config/i386-qqq.cfg
//
//--- Invocation qqq-clang-g++ tries to find config file qqq.cfg if qqq-clang-g++.cfg is not found.
//
// RUN: rm %T/testdmode/qqq-clang-g++.cfg
// RUN: %T/testdmode/qqq-clang-g++ --config-system-dir= --config-user-dir= -c -no-canonical-prefixes %s -### 2>&1 | FileCheck %s -check-prefix SHORT-NAME
//
// SHORT-NAME: Configuration file: {{.*}}/testdmode/qqq.cfg
// SHORT-NAME: -Werror
// SHORT-NAME-NOT: -Wundefined-func-template


//--- Config files are searched for in binary directory as well.
//
// RUN: mkdir -p %T/testbin
// 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-system-dir= --config-user-dir= --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 command line contains options that change triple (for instance, -m32), clang tries
// reloading config file.

//--- When reloading config file, x86_64-clang-g++ tries to find config i386-clang-g++.cfg first.
//
// RUN: mkdir -p %T/testreload
// RUN: [ ! -s %T/testreload/x86_64-clang-g++ ] || rm %T/testreload/x86_64-clang-g++
// RUN: ln -s %clang %T/testreload/x86_64-clang-g++
// RUN: echo "-Wundefined-func-template" > %T/testreload/i386-clang-g++.cfg
// RUN: echo "-Werror" > %T/testreload/i386.cfg
// RUN: %T/testreload/x86_64-clang-g++ --config-system-dir= --config-user-dir= -c -m32 -no-canonical-prefixes %s -### 2>&1 | FileCheck %s -check-prefix CHECK-RELOAD
//
// CHECK-RELOAD: Configuration file: {{.*}}/testreload/i386-clang-g++.cfg
// CHECK-RELOAD: -Wundefined-func-template
// CHECK-RELOAD-NOT: -Werror

//--- If config file is specified by --config and its name does not start with architecture, it is used without reloading.
//
// RUN: %T/testreload/x86_64-clang-g++ --config-system-dir=%S/Inputs --config-user-dir= --config config-3 -c -m32 -no-canonical-prefixes %s -### 2>&1 | FileCheck %s -check-prefix CHECK-RELOAD1a
//
// CHECK-RELOAD1a: Configuration file: {{.*}}/Inputs/config-3.cfg
//
// RUN: %T/testreload/x86_64-clang-g++ --config-system-dir=%S/Inputs --config-user-dir= --config config-3 -c -target i386 -no-canonical-prefixes %s -### 2>&1 | FileCheck %s -check-prefix CHECK-RELOAD1b
//
// CHECK-RELOAD1b: Configuration file: {{.*}}/Inputs/config-3.cfg

//--- If config file is specified by --config and its name starts with architecture, it is reloaded.
//
// RUN: %T/testreload/x86_64-clang-g++ --config-system-dir=%S/Inputs/config --config-user-dir= --config x86_64-qqq -c -m32 -no-canonical-prefixes %s -### 2>&1 | FileCheck %s -check-prefix CHECK-RELOAD1c
//
// CHECK-RELOAD1c: Configuration file: {{.*}}/Inputs/config/i386-qqq.cfg

//--- x86_64-clang-g++ tries to find config i386.cfg if i386-clang-g++.cfg is not found.
//
// RUN: rm %T/testreload/i386-clang-g++.cfg
// RUN: %T/testreload/x86_64-clang-g++ --config-system-dir= --config-user-dir= -c -m32 -no-canonical-prefixes %s -### 2>&1 | FileCheck %s -check-prefix CHECK-RELOAD1d
//
// CHECK-RELOAD1d: Configuration file: {{.*}}/testreload/i386.cfg
// CHECK-RELOAD1d: -Werror
// CHECK-RELOAD1d-NOT: -Wundefined-func-template