[clang] 8a774c3 - [clang] [Driver] Support multiple configuration files
Michał Górny via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 21 04:14:50 PDT 2022
Author: Michał Górny
Date: 2022-09-21T13:14:36+02:00
New Revision: 8a774c35e9f8c6a200a8f4d00aae909840f5004a
URL: https://github.com/llvm/llvm-project/commit/8a774c35e9f8c6a200a8f4d00aae909840f5004a
DIFF: https://github.com/llvm/llvm-project/commit/8a774c35e9f8c6a200a8f4d00aae909840f5004a.diff
LOG: [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
Added:
Modified:
clang/docs/UsersManual.rst
clang/include/clang/Driver/Driver.h
clang/lib/Driver/Driver.cpp
clang/test/Driver/config-file-errs.c
clang/test/Driver/config-file.c
clang/test/Driver/config-file3.c
Removed:
################################################################################
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 0ddbb61f1d540..9c0a916f1a10d 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -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
diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h
index 59c2f1f52cecf..203557a5cfc66 100644
--- a/clang/include/clang/Driver/Driver.h
+++ b/clang/include/clang/Driver/Driver.h
@@ -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 @@ class Driver {
/// 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 @@ class Driver {
/// 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 @@ class Driver {
private:
- /// Tries to load options from configuration file.
+ /// Tries to load options from configuration files.
+ ///
+ /// \returns true if error occurred.
+ bool loadConfigFiles();
+
+ /// Tries to load options from default configuration files (deduced from
+ /// executable filename).
///
/// \returns true if error occurred.
- bool loadConfigFile();
+ bool loadDefaultConfigFiles(ArrayRef<StringRef> CfgFileSearchDirs);
/// Read options from the specified file.
///
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 52990f14e7eac..4d3ab9b7096e2 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -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
diff erent 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
diff erent 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';
}
diff --git a/clang/test/Driver/config-file-errs.c b/clang/test/Driver/config-file-errs.c
index c4ce9df915bf3..497e6cb4a5c2b 100644
--- a/clang/test/Driver/config-file-errs.c
+++ b/clang/test/Driver/config-file-errs.c
@@ -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
diff --git a/clang/test/Driver/config-file.c b/clang/test/Driver/config-file.c
index 265fd49f2e664..5cb2e485e1787 100644
--- a/clang/test/Driver/config-file.c
+++ b/clang/test/Driver/config-file.c
@@ -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
diff --git a/clang/test/Driver/config-file3.c b/clang/test/Driver/config-file3.c
index 086355b382a1d..3158ac0d7f3b7 100644
--- a/clang/test/Driver/config-file3.c
+++ b/clang/test/Driver/config-file3.c
@@ -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
More information about the cfe-commits
mailing list