[clang] [Driver] Support multiple configuration files
Support specifying multiple configuration files via multiple `--config` options. When multiple files are specified, the options from subsequent files are appended to the options from the initial file. While at it, remove the incorrect assertion about CfgFileName being non-empty. It can be empty if `--config ""` is passed, and it makes sense to report it as non-existing file rather than crash. Differential Revision: https://reviews.llvm.org/D134270
This commit is contained in:
parent
94747094c7
commit
8a774c35e9
6 changed files with 125 additions and 95 deletions
|
|
@ -883,13 +883,18 @@ specified just by referencing the configuration file. They may be used, for
|
|||
example, 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 example:
|
||||
Configuration files can be either specified on the command line or loaded
|
||||
from default locations. If both variants are present, the default configuration
|
||||
files are loaded first.
|
||||
|
||||
The command line option ``--config`` can be used to specify explicit
|
||||
configuration files in a Clang invocation. If the option is used multiple times,
|
||||
all specified files are loaded, in order. For example:
|
||||
|
||||
::
|
||||
|
||||
clang --config /home/user/cfgs/testing.txt
|
||||
clang --config debug.cfg
|
||||
clang --config debug.cfg --config runtimes.cfg
|
||||
|
||||
If the provided argument contains a directory separator, it is considered as
|
||||
a file path, and options are read from that file. Otherwise the argument is
|
||||
|
|
@ -904,14 +909,15 @@ clang build using CMake parameters, ``CLANG_CONFIG_FILE_USER_DIR`` and
|
|||
``CLANG_CONFIG_FILE_SYSTEM_DIR`` respectively. The first file found is used.
|
||||
It is an error if the required file cannot be found.
|
||||
|
||||
If no explicit configuration file is specified, Clang searches for a default
|
||||
configuration file following the rules described in the next paragraphs.
|
||||
To disable this behavior, ``--no-default-config`` flag can be used.
|
||||
The default configuration files are searched for in the same directories
|
||||
following the rules described in the next paragraphs. Loading default
|
||||
configuration files can be disabled entirely via passing
|
||||
the ``--no-default-config`` flag.
|
||||
|
||||
Another way to specify a configuration file is to encode it in executable name.
|
||||
For example, if the Clang executable is named ``armv7l-clang`` (it may be a
|
||||
symbolic link to ``clang``), then Clang will search for file ``armv7l.cfg``
|
||||
in the directory where Clang resides.
|
||||
The name of the default configuration file is deduced from the clang executable
|
||||
name. For example, if the Clang executable is named ``armv7l-clang`` (it may
|
||||
be a symbolic link to ``clang``), then Clang will search for file
|
||||
``armv7l.cfg`` in the directory where Clang resides.
|
||||
|
||||
If a driver mode is specified in invocation, Clang tries to find a file specific
|
||||
for the specified mode. For example, if the executable file is named
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "clang/Driver/ToolChain.h"
|
||||
#include "clang/Driver/Types.h"
|
||||
#include "clang/Driver/Util.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Option/Arg.h"
|
||||
|
|
@ -28,6 +29,7 @@
|
|||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
class Triple;
|
||||
|
|
@ -258,8 +260,8 @@ private:
|
|||
/// Name to use when invoking gcc/g++.
|
||||
std::string CCCGenericGCCName;
|
||||
|
||||
/// Name of configuration file if used.
|
||||
std::string ConfigFile;
|
||||
/// Paths to configuration files used.
|
||||
std::vector<std::string> ConfigFiles;
|
||||
|
||||
/// Allocator for string saver.
|
||||
llvm::BumpPtrAllocator Alloc;
|
||||
|
|
@ -353,7 +355,9 @@ public:
|
|||
/// Name to use when invoking gcc/g++.
|
||||
const std::string &getCCCGenericGCCName() const { return CCCGenericGCCName; }
|
||||
|
||||
const std::string &getConfigFile() const { return ConfigFile; }
|
||||
llvm::ArrayRef<std::string> getConfigFiles() const {
|
||||
return ConfigFiles;
|
||||
}
|
||||
|
||||
const llvm::opt::OptTable &getOpts() const { return getDriverOptTable(); }
|
||||
|
||||
|
|
@ -664,10 +668,16 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
/// Tries to load options from configuration file.
|
||||
/// Tries to load options from configuration files.
|
||||
///
|
||||
/// \returns true if error occurred.
|
||||
bool loadConfigFile();
|
||||
bool loadConfigFiles();
|
||||
|
||||
/// Tries to load options from default configuration files (deduced from
|
||||
/// executable filename).
|
||||
///
|
||||
/// \returns true if error occurred.
|
||||
bool loadDefaultConfigFiles(ArrayRef<StringRef> CfgFileSearchDirs);
|
||||
|
||||
/// Read options from the specified file.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -929,6 +929,22 @@ static bool searchForFile(SmallVectorImpl<char> &FilePath,
|
|||
return false;
|
||||
}
|
||||
|
||||
static void appendOneArg(InputArgList &Args, const Arg *Opt,
|
||||
const Arg *BaseArg) {
|
||||
// The args for config files or /clang: flags belong to different InputArgList
|
||||
// objects than Args. This copies an Arg from one of those other InputArgLists
|
||||
// to the ownership of Args.
|
||||
unsigned Index = Args.MakeIndex(Opt->getSpelling());
|
||||
Arg *Copy = new llvm::opt::Arg(Opt->getOption(), Args.getArgString(Index),
|
||||
Index, BaseArg);
|
||||
Copy->getValues() = Opt->getValues();
|
||||
if (Opt->isClaimed())
|
||||
Copy->claim();
|
||||
Copy->setOwnsValues(Opt->getOwnsValues());
|
||||
Opt->setOwnsValues(false);
|
||||
Args.append(Copy);
|
||||
}
|
||||
|
||||
bool Driver::readConfigFile(StringRef FileName) {
|
||||
// Try reading the given file.
|
||||
SmallVector<const char *, 32> NewCfgArgs;
|
||||
|
|
@ -940,31 +956,38 @@ bool Driver::readConfigFile(StringRef FileName) {
|
|||
// Read options from config file.
|
||||
llvm::SmallString<128> CfgFileName(FileName);
|
||||
llvm::sys::path::native(CfgFileName);
|
||||
ConfigFile = std::string(CfgFileName);
|
||||
bool ContainErrors;
|
||||
CfgOptions = std::make_unique<InputArgList>(
|
||||
std::unique_ptr<InputArgList> NewOptions = std::make_unique<InputArgList>(
|
||||
ParseArgStrings(NewCfgArgs, IsCLMode(), ContainErrors));
|
||||
if (ContainErrors) {
|
||||
CfgOptions.reset();
|
||||
if (ContainErrors)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CfgOptions->hasArg(options::OPT_config)) {
|
||||
CfgOptions.reset();
|
||||
if (NewOptions->hasArg(options::OPT_config)) {
|
||||
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)
|
||||
for (Arg *A : *NewOptions)
|
||||
A->claim();
|
||||
|
||||
if (!CfgOptions)
|
||||
CfgOptions = std::move(NewOptions);
|
||||
else {
|
||||
// If this is a subsequent config file, append options to the previous one.
|
||||
for (auto *Opt : *NewOptions) {
|
||||
const Arg *BaseArg = &Opt->getBaseArg();
|
||||
if (BaseArg == Opt)
|
||||
BaseArg = nullptr;
|
||||
appendOneArg(*CfgOptions, Opt, BaseArg);
|
||||
}
|
||||
}
|
||||
ConfigFiles.push_back(std::string(CfgFileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Driver::loadConfigFile() {
|
||||
std::string CfgFileName;
|
||||
|
||||
bool Driver::loadConfigFiles() {
|
||||
// Process options that change search path for config files.
|
||||
if (CLOptions) {
|
||||
if (CLOptions->hasArg(options::OPT_config_system_dir_EQ)) {
|
||||
|
|
@ -990,26 +1013,18 @@ bool Driver::loadConfigFile() {
|
|||
// Prepare list of directories where config file is searched for.
|
||||
StringRef CfgFileSearchDirs[] = {UserConfigDir, SystemConfigDir, Dir};
|
||||
|
||||
// First try to find config file specified in command line.
|
||||
// First try to load configuration from the default files, return on error.
|
||||
if (loadDefaultConfigFiles(CfgFileSearchDirs))
|
||||
return true;
|
||||
|
||||
// Then load configuration files specified explicitly.
|
||||
llvm::SmallString<128> CfgFilePath;
|
||||
if (CLOptions) {
|
||||
std::vector<std::string> ConfigFiles =
|
||||
CLOptions->getAllArgValues(options::OPT_config);
|
||||
if (ConfigFiles.size() > 1) {
|
||||
if (!llvm::all_equal(ConfigFiles)) {
|
||||
Diag(diag::err_drv_duplicate_config);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ConfigFiles.empty()) {
|
||||
CfgFileName = ConfigFiles.front();
|
||||
assert(!CfgFileName.empty());
|
||||
|
||||
for (auto CfgFileName : CLOptions->getAllArgValues(options::OPT_config)) {
|
||||
// If argument contains directory separator, treat it as a path to
|
||||
// configuration file.
|
||||
if (llvm::sys::path::has_parent_path(CfgFileName)) {
|
||||
SmallString<128> CfgFilePath(CfgFileName);
|
||||
CfgFilePath = CfgFileName;
|
||||
if (llvm::sys::path::is_relative(CfgFilePath)) {
|
||||
if (getVFS().makeAbsolute(CfgFilePath))
|
||||
return true;
|
||||
|
|
@ -1020,35 +1035,37 @@ bool Driver::loadConfigFile() {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
return readConfigFile(CfgFilePath);
|
||||
} else if (!searchForFile(CfgFilePath, CfgFileSearchDirs, CfgFileName,
|
||||
getVFS())) {
|
||||
// Report an error that the config file could not be found.
|
||||
Diag(diag::err_drv_config_file_not_found) << CfgFileName;
|
||||
for (const StringRef &SearchDir : CfgFileSearchDirs)
|
||||
if (!SearchDir.empty())
|
||||
Diag(diag::note_drv_config_file_searched_in) << SearchDir;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Look for the configuration file in the usual locations.
|
||||
if (searchForFile(CfgFilePath, CfgFileSearchDirs, CfgFileName, getVFS()))
|
||||
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.
|
||||
Diag(diag::err_drv_config_file_not_found) << CfgFileName;
|
||||
for (const StringRef &SearchDir : CfgFileSearchDirs)
|
||||
if (!SearchDir.empty())
|
||||
Diag(diag::note_drv_config_file_searched_in) << SearchDir;
|
||||
return true;
|
||||
// Try to read the config file, return on error.
|
||||
if (readConfigFile(CfgFilePath))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(CLOptions && CLOptions->hasArg(options::OPT_no_default_config))) {
|
||||
// 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;
|
||||
}
|
||||
// No error occurred.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CfgFileName.empty())
|
||||
bool Driver::loadDefaultConfigFiles(ArrayRef<StringRef> CfgFileSearchDirs) {
|
||||
if (CLOptions && CLOptions->hasArg(options::OPT_no_default_config))
|
||||
return false;
|
||||
if (ClangNameParts.TargetPrefix.empty())
|
||||
return false;
|
||||
|
||||
// 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'.
|
||||
std::string CfgFileName =
|
||||
ClangNameParts.TargetPrefix + '-' + ClangNameParts.ModeSuffix;
|
||||
|
||||
// Determine architecture part of the file name, if it is present.
|
||||
StringRef CfgFileArch = CfgFileName;
|
||||
|
|
@ -1086,6 +1103,7 @@ bool Driver::loadConfigFile() {
|
|||
}
|
||||
|
||||
// Try to find config file. First try file with corrected architecture.
|
||||
llvm::SmallString<128> CfgFilePath;
|
||||
if (!FixedConfigFile.empty()) {
|
||||
if (searchForFile(CfgFilePath, CfgFileSearchDirs, FixedConfigFile,
|
||||
getVFS()))
|
||||
|
|
@ -1138,28 +1156,13 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
|
|||
|
||||
// Try parsing configuration file.
|
||||
if (!ContainsError)
|
||||
ContainsError = loadConfigFile();
|
||||
ContainsError = loadConfigFiles();
|
||||
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));
|
||||
|
||||
// The args for config files or /clang: flags belong to different InputArgList
|
||||
// objects than Args. This copies an Arg from one of those other InputArgLists
|
||||
// to the ownership of Args.
|
||||
auto appendOneArg = [&Args](const Arg *Opt, const Arg *BaseArg) {
|
||||
unsigned Index = Args.MakeIndex(Opt->getSpelling());
|
||||
Arg *Copy = new llvm::opt::Arg(Opt->getOption(), Args.getArgString(Index),
|
||||
Index, BaseArg);
|
||||
Copy->getValues() = Opt->getValues();
|
||||
if (Opt->isClaimed())
|
||||
Copy->claim();
|
||||
Copy->setOwnsValues(Opt->getOwnsValues());
|
||||
Opt->setOwnsValues(false);
|
||||
Args.append(Copy);
|
||||
};
|
||||
|
||||
if (HasConfigFile)
|
||||
for (auto *Opt : *CLOptions) {
|
||||
if (Opt->getOption().matches(options::OPT_config))
|
||||
|
|
@ -1167,7 +1170,7 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
|
|||
const Arg *BaseArg = &Opt->getBaseArg();
|
||||
if (BaseArg == Opt)
|
||||
BaseArg = nullptr;
|
||||
appendOneArg(Opt, BaseArg);
|
||||
appendOneArg(Args, Opt, BaseArg);
|
||||
}
|
||||
|
||||
// In CL mode, look for any pass-through arguments
|
||||
|
|
@ -1186,7 +1189,7 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
|
|||
|
||||
if (!ContainsError)
|
||||
for (auto *Opt : *CLModePassThroughOptions) {
|
||||
appendOneArg(Opt, nullptr);
|
||||
appendOneArg(Args, Opt, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1880,8 +1883,8 @@ 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())
|
||||
// If configuration files were used, print their paths.
|
||||
for (auto ConfigFile : ConfigFiles)
|
||||
OS << "Configuration file: " << ConfigFile << '\n';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
//--- 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
|
||||
|
|
@ -22,6 +16,11 @@
|
|||
// CHECK-NONEXISTENT: configuration file '{{.*}}somewhere/nonexistent-config-file' does not exist
|
||||
|
||||
|
||||
//--- All '--config' arguments must be existing files.
|
||||
//
|
||||
// RUN: not %clang --config %S/Inputs/config-4.cfg --config somewhere/nonexistent-config-file 2>&1 | FileCheck %s -check-prefix CHECK-NONEXISTENT
|
||||
|
||||
|
||||
//--- 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.cfg 2>&1 | FileCheck %s -check-prefix CHECK-NOTFOUND0
|
||||
|
|
|
|||
|
|
@ -73,6 +73,10 @@
|
|||
// CHECK-PRECEDENCE: -Wall
|
||||
|
||||
|
||||
//--- Duplicate --config options are allowed if the value is the same
|
||||
// RUN: %clang --config-system-dir=%S/Inputs/config --config-user-dir=%S/Inputs/config2 --config config-4.cfg --config config-4.cfg -S %s -o /dev/null -v 2>&1 | FileCheck %s -check-prefix CHECK-SAME-CONFIG
|
||||
// CHECK-SAME-CONFIG: Configuration file: {{.*}}Inputs{{.}}config2{{.}}config-4.cfg
|
||||
//--- Multiple configuration files can be specified.
|
||||
// RUN: %clang --config-system-dir=%S/Inputs/config --config-user-dir= --config config-4.cfg --config %S/Inputs/config2/config-4.cfg -S %s -o /dev/null -v 2>&1 | FileCheck %s -check-prefix CHECK-TWO-CONFIGS
|
||||
// CHECK-TWO-CONFIGS: Configuration file: {{.*}}Inputs{{.}}config{{.}}config-4.cfg
|
||||
// CHECK-TWO-CONFIGS-NEXT: Configuration file: {{.*}}Inputs{{.}}config2{{.}}config-4.cfg
|
||||
// CHECK-TWO-CONFIGS: -isysroot
|
||||
// CHECK-TWO-CONFIGS-SAME: /opt/data
|
||||
// CHECK-TWO-CONFIGS-SAME: -Wall
|
||||
|
|
|
|||
|
|
@ -34,12 +34,13 @@
|
|||
// RUN: %t/testdmode/qqq-clang-g++ --config-system-dir= --config-user-dir=%t/testdmode -c -### %s 2>&1 | FileCheck %s -check-prefix SYMLINK
|
||||
//
|
||||
// SYMLINK: Configuration file: {{.*}}/testdmode/qqq-clang-g++.cfg
|
||||
//
|
||||
//--- File specified by --config overrides config inferred from clang executable.
|
||||
|
||||
//--- File specified by --config is loaded after the one inferred from the executable.
|
||||
//
|
||||
// RUN: %t/testdmode/qqq-clang-g++ --config-system-dir=%S/Inputs/config --config-user-dir= --config i386-qqq.cfg -c -no-canonical-prefixes -### %s 2>&1 | FileCheck %s -check-prefix CHECK-EXPLICIT
|
||||
//
|
||||
// CHECK-EXPLICIT: Configuration file: {{.*}}/Inputs/config/i386-qqq.cfg
|
||||
// CHECK-EXPLICIT: Configuration file: {{.*}}/testdmode/qqq-clang-g++.cfg
|
||||
// CHECK-EXPLICIT-NEXT: Configuration file: {{.*}}/Inputs/config/i386-qqq.cfg
|
||||
|
||||
//--- --no-default-config disables config search.
|
||||
//
|
||||
|
|
@ -47,6 +48,13 @@
|
|||
//
|
||||
// NO-DEFAULT-CONFIG-NOT: Configuration file:
|
||||
|
||||
//--- Explicit --config works with --no-default-config.
|
||||
//
|
||||
// RUN: %t/testdmode/qqq-clang-g++ --config-system-dir=%S/Inputs/config --config-user-dir= --no-default-config --config i386-qqq.cfg -c -no-canonical-prefixes -### %s 2>&1 | FileCheck %s -check-prefix CHECK-EXPLICIT-NO-DEFAULT
|
||||
//
|
||||
// CHECK-EXPLICIT-NO-DEFAULT-NOT: Configuration file: {{.*}}/testdmode/qqq-clang-g++.cfg
|
||||
// CHECK-EXPLICIT-NO-DEFAULT: 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
|
||||
|
|
|
|||
Loading…
Reference in a new issue