Skip to content

Commit 8a774c3

Browse files
committed
[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
1 parent 9474709 commit 8a774c3

File tree

6 files changed

+125
-95
lines changed

6 files changed

+125
-95
lines changed

clang/docs/UsersManual.rst

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -883,13 +883,18 @@ specified just by referencing the configuration file. They may be used, for
883883
example, to collect options required to tune compilation for particular
884884
target, such as ``-L``, ``-I``, ``-l``, ``--sysroot``, codegen options, etc.
885885

886-
The command line option ``--config`` can be used to specify configuration
887-
file in a Clang invocation. For example:
886+
Configuration files can be either specified on the command line or loaded
887+
from default locations. If both variants are present, the default configuration
888+
files are loaded first.
889+
890+
The command line option ``--config`` can be used to specify explicit
891+
configuration files in a Clang invocation. If the option is used multiple times,
892+
all specified files are loaded, in order. For example:
888893

889894
::
890895

891896
clang --config /home/user/cfgs/testing.txt
892-
clang --config debug.cfg
897+
clang --config debug.cfg --config runtimes.cfg
893898

894899
If the provided argument contains a directory separator, it is considered as
895900
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
904909
``CLANG_CONFIG_FILE_SYSTEM_DIR`` respectively. The first file found is used.
905910
It is an error if the required file cannot be found.
906911

907-
If no explicit configuration file is specified, Clang searches for a default
908-
configuration file following the rules described in the next paragraphs.
909-
To disable this behavior, ``--no-default-config`` flag can be used.
912+
The default configuration files are searched for in the same directories
913+
following the rules described in the next paragraphs. Loading default
914+
configuration files can be disabled entirely via passing
915+
the ``--no-default-config`` flag.
910916

911-
Another way to specify a configuration file is to encode it in executable name.
912-
For example, if the Clang executable is named ``armv7l-clang`` (it may be a
913-
symbolic link to ``clang``), then Clang will search for file ``armv7l.cfg``
914-
in the directory where Clang resides.
917+
The name of the default configuration file is deduced from the clang executable
918+
name. For example, if the Clang executable is named ``armv7l-clang`` (it may
919+
be a symbolic link to ``clang``), then Clang will search for file
920+
``armv7l.cfg`` in the directory where Clang resides.
915921

916922
If a driver mode is specified in invocation, Clang tries to find a file specific
917923
for the specified mode. For example, if the executable file is named

clang/include/clang/Driver/Driver.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "clang/Driver/ToolChain.h"
2020
#include "clang/Driver/Types.h"
2121
#include "clang/Driver/Util.h"
22+
#include "llvm/ADT/ArrayRef.h"
2223
#include "llvm/ADT/StringMap.h"
2324
#include "llvm/ADT/StringRef.h"
2425
#include "llvm/Option/Arg.h"
@@ -28,6 +29,7 @@
2829
#include <list>
2930
#include <map>
3031
#include <string>
32+
#include <vector>
3133

3234
namespace llvm {
3335
class Triple;
@@ -258,8 +260,8 @@ class Driver {
258260
/// Name to use when invoking gcc/g++.
259261
std::string CCCGenericGCCName;
260262

261-
/// Name of configuration file if used.
262-
std::string ConfigFile;
263+
/// Paths to configuration files used.
264+
std::vector<std::string> ConfigFiles;
263265

264266
/// Allocator for string saver.
265267
llvm::BumpPtrAllocator Alloc;
@@ -353,7 +355,9 @@ class Driver {
353355
/// Name to use when invoking gcc/g++.
354356
const std::string &getCCCGenericGCCName() const { return CCCGenericGCCName; }
355357

356-
const std::string &getConfigFile() const { return ConfigFile; }
358+
llvm::ArrayRef<std::string> getConfigFiles() const {
359+
return ConfigFiles;
360+
}
357361

358362
const llvm::opt::OptTable &getOpts() const { return getDriverOptTable(); }
359363

@@ -664,10 +668,16 @@ class Driver {
664668

665669
private:
666670

667-
/// Tries to load options from configuration file.
671+
/// Tries to load options from configuration files.
672+
///
673+
/// \returns true if error occurred.
674+
bool loadConfigFiles();
675+
676+
/// Tries to load options from default configuration files (deduced from
677+
/// executable filename).
668678
///
669679
/// \returns true if error occurred.
670-
bool loadConfigFile();
680+
bool loadDefaultConfigFiles(ArrayRef<StringRef> CfgFileSearchDirs);
671681

672682
/// Read options from the specified file.
673683
///

clang/lib/Driver/Driver.cpp

Lines changed: 71 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,22 @@ static bool searchForFile(SmallVectorImpl<char> &FilePath,
929929
return false;
930930
}
931931

932+
static void appendOneArg(InputArgList &Args, const Arg *Opt,
933+
const Arg *BaseArg) {
934+
// The args for config files or /clang: flags belong to different InputArgList
935+
// objects than Args. This copies an Arg from one of those other InputArgLists
936+
// to the ownership of Args.
937+
unsigned Index = Args.MakeIndex(Opt->getSpelling());
938+
Arg *Copy = new llvm::opt::Arg(Opt->getOption(), Args.getArgString(Index),
939+
Index, BaseArg);
940+
Copy->getValues() = Opt->getValues();
941+
if (Opt->isClaimed())
942+
Copy->claim();
943+
Copy->setOwnsValues(Opt->getOwnsValues());
944+
Opt->setOwnsValues(false);
945+
Args.append(Copy);
946+
}
947+
932948
bool Driver::readConfigFile(StringRef FileName) {
933949
// Try reading the given file.
934950
SmallVector<const char *, 32> NewCfgArgs;
@@ -940,31 +956,38 @@ bool Driver::readConfigFile(StringRef FileName) {
940956
// Read options from config file.
941957
llvm::SmallString<128> CfgFileName(FileName);
942958
llvm::sys::path::native(CfgFileName);
943-
ConfigFile = std::string(CfgFileName);
944959
bool ContainErrors;
945-
CfgOptions = std::make_unique<InputArgList>(
960+
std::unique_ptr<InputArgList> NewOptions = std::make_unique<InputArgList>(
946961
ParseArgStrings(NewCfgArgs, IsCLMode(), ContainErrors));
947-
if (ContainErrors) {
948-
CfgOptions.reset();
962+
if (ContainErrors)
949963
return true;
950-
}
951964

952-
if (CfgOptions->hasArg(options::OPT_config)) {
953-
CfgOptions.reset();
965+
if (NewOptions->hasArg(options::OPT_config)) {
954966
Diag(diag::err_drv_nested_config_file);
955967
return true;
956968
}
957969

958970
// Claim all arguments that come from a configuration file so that the driver
959971
// does not warn on any that is unused.
960-
for (Arg *A : *CfgOptions)
972+
for (Arg *A : *NewOptions)
961973
A->claim();
974+
975+
if (!CfgOptions)
976+
CfgOptions = std::move(NewOptions);
977+
else {
978+
// If this is a subsequent config file, append options to the previous one.
979+
for (auto *Opt : *NewOptions) {
980+
const Arg *BaseArg = &Opt->getBaseArg();
981+
if (BaseArg == Opt)
982+
BaseArg = nullptr;
983+
appendOneArg(*CfgOptions, Opt, BaseArg);
984+
}
985+
}
986+
ConfigFiles.push_back(std::string(CfgFileName));
962987
return false;
963988
}
964989

965-
bool Driver::loadConfigFile() {
966-
std::string CfgFileName;
967-
990+
bool Driver::loadConfigFiles() {
968991
// Process options that change search path for config files.
969992
if (CLOptions) {
970993
if (CLOptions->hasArg(options::OPT_config_system_dir_EQ)) {
@@ -990,26 +1013,18 @@ bool Driver::loadConfigFile() {
9901013
// Prepare list of directories where config file is searched for.
9911014
StringRef CfgFileSearchDirs[] = {UserConfigDir, SystemConfigDir, Dir};
9921015

993-
// First try to find config file specified in command line.
1016+
// First try to load configuration from the default files, return on error.
1017+
if (loadDefaultConfigFiles(CfgFileSearchDirs))
1018+
return true;
1019+
1020+
// Then load configuration files specified explicitly.
9941021
llvm::SmallString<128> CfgFilePath;
9951022
if (CLOptions) {
996-
std::vector<std::string> ConfigFiles =
997-
CLOptions->getAllArgValues(options::OPT_config);
998-
if (ConfigFiles.size() > 1) {
999-
if (!llvm::all_equal(ConfigFiles)) {
1000-
Diag(diag::err_drv_duplicate_config);
1001-
return true;
1002-
}
1003-
}
1004-
1005-
if (!ConfigFiles.empty()) {
1006-
CfgFileName = ConfigFiles.front();
1007-
assert(!CfgFileName.empty());
1008-
1023+
for (auto CfgFileName : CLOptions->getAllArgValues(options::OPT_config)) {
10091024
// If argument contains directory separator, treat it as a path to
10101025
// configuration file.
10111026
if (llvm::sys::path::has_parent_path(CfgFileName)) {
1012-
SmallString<128> CfgFilePath(CfgFileName);
1027+
CfgFilePath = CfgFileName;
10131028
if (llvm::sys::path::is_relative(CfgFilePath)) {
10141029
if (getVFS().makeAbsolute(CfgFilePath))
10151030
return true;
@@ -1020,35 +1035,37 @@ bool Driver::loadConfigFile() {
10201035
return true;
10211036
}
10221037
}
1023-
return readConfigFile(CfgFilePath);
1038+
} else if (!searchForFile(CfgFilePath, CfgFileSearchDirs, CfgFileName,
1039+
getVFS())) {
1040+
// Report an error that the config file could not be found.
1041+
Diag(diag::err_drv_config_file_not_found) << CfgFileName;
1042+
for (const StringRef &SearchDir : CfgFileSearchDirs)
1043+
if (!SearchDir.empty())
1044+
Diag(diag::note_drv_config_file_searched_in) << SearchDir;
1045+
return true;
10241046
}
10251047

1026-
// Look for the configuration file in the usual locations.
1027-
if (searchForFile(CfgFilePath, CfgFileSearchDirs, CfgFileName, getVFS()))
1028-
return readConfigFile(CfgFilePath);
1029-
1030-
// Report error but only if config file was specified explicitly, by
1031-
// option --config. If it was deduced from executable name, it is not an
1032-
// error.
1033-
Diag(diag::err_drv_config_file_not_found) << CfgFileName;
1034-
for (const StringRef &SearchDir : CfgFileSearchDirs)
1035-
if (!SearchDir.empty())
1036-
Diag(diag::note_drv_config_file_searched_in) << SearchDir;
1037-
return true;
1048+
// Try to read the config file, return on error.
1049+
if (readConfigFile(CfgFilePath))
1050+
return true;
10381051
}
10391052
}
10401053

1041-
if (!(CLOptions && CLOptions->hasArg(options::OPT_no_default_config))) {
1042-
// If config file is not specified explicitly, try to deduce configuration
1043-
// from executable name. For instance, an executable 'armv7l-clang' will
1044-
// search for config file 'armv7l-clang.cfg'.
1045-
if (CfgFileName.empty() && !ClangNameParts.TargetPrefix.empty())
1046-
CfgFileName =
1047-
ClangNameParts.TargetPrefix + '-' + ClangNameParts.ModeSuffix;
1048-
}
1054+
// No error occurred.
1055+
return false;
1056+
}
10491057

1050-
if (CfgFileName.empty())
1058+
bool Driver::loadDefaultConfigFiles(ArrayRef<StringRef> CfgFileSearchDirs) {
1059+
if (CLOptions && CLOptions->hasArg(options::OPT_no_default_config))
10511060
return false;
1061+
if (ClangNameParts.TargetPrefix.empty())
1062+
return false;
1063+
1064+
// If config file is not specified explicitly, try to deduce configuration
1065+
// from executable name. For instance, an executable 'armv7l-clang' will
1066+
// search for config file 'armv7l-clang.cfg'.
1067+
std::string CfgFileName =
1068+
ClangNameParts.TargetPrefix + '-' + ClangNameParts.ModeSuffix;
10521069

10531070
// Determine architecture part of the file name, if it is present.
10541071
StringRef CfgFileArch = CfgFileName;
@@ -1086,6 +1103,7 @@ bool Driver::loadConfigFile() {
10861103
}
10871104

10881105
// Try to find config file. First try file with corrected architecture.
1106+
llvm::SmallString<128> CfgFilePath;
10891107
if (!FixedConfigFile.empty()) {
10901108
if (searchForFile(CfgFilePath, CfgFileSearchDirs, FixedConfigFile,
10911109
getVFS()))
@@ -1138,36 +1156,21 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
11381156

11391157
// Try parsing configuration file.
11401158
if (!ContainsError)
1141-
ContainsError = loadConfigFile();
1159+
ContainsError = loadConfigFiles();
11421160
bool HasConfigFile = !ContainsError && (CfgOptions.get() != nullptr);
11431161

11441162
// All arguments, from both config file and command line.
11451163
InputArgList Args = std::move(HasConfigFile ? std::move(*CfgOptions)
11461164
: std::move(*CLOptions));
11471165

1148-
// The args for config files or /clang: flags belong to different InputArgList
1149-
// objects than Args. This copies an Arg from one of those other InputArgLists
1150-
// to the ownership of Args.
1151-
auto appendOneArg = [&Args](const Arg *Opt, const Arg *BaseArg) {
1152-
unsigned Index = Args.MakeIndex(Opt->getSpelling());
1153-
Arg *Copy = new llvm::opt::Arg(Opt->getOption(), Args.getArgString(Index),
1154-
Index, BaseArg);
1155-
Copy->getValues() = Opt->getValues();
1156-
if (Opt->isClaimed())
1157-
Copy->claim();
1158-
Copy->setOwnsValues(Opt->getOwnsValues());
1159-
Opt->setOwnsValues(false);
1160-
Args.append(Copy);
1161-
};
1162-
11631166
if (HasConfigFile)
11641167
for (auto *Opt : *CLOptions) {
11651168
if (Opt->getOption().matches(options::OPT_config))
11661169
continue;
11671170
const Arg *BaseArg = &Opt->getBaseArg();
11681171
if (BaseArg == Opt)
11691172
BaseArg = nullptr;
1170-
appendOneArg(Opt, BaseArg);
1173+
appendOneArg(Args, Opt, BaseArg);
11711174
}
11721175

11731176
// In CL mode, look for any pass-through arguments
@@ -1186,7 +1189,7 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
11861189

11871190
if (!ContainsError)
11881191
for (auto *Opt : *CLModePassThroughOptions) {
1189-
appendOneArg(Opt, nullptr);
1192+
appendOneArg(Args, Opt, nullptr);
11901193
}
11911194
}
11921195
}
@@ -1880,8 +1883,8 @@ void Driver::PrintVersion(const Compilation &C, raw_ostream &OS) const {
18801883
// Print out the install directory.
18811884
OS << "InstalledDir: " << InstalledDir << '\n';
18821885

1883-
// If configuration file was used, print its path.
1884-
if (!ConfigFile.empty())
1886+
// If configuration files were used, print their paths.
1887+
for (auto ConfigFile : ConfigFiles)
18851888
OS << "Configuration file: " << ConfigFile << '\n';
18861889
}
18871890

clang/test/Driver/config-file-errs.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
//--- No more than one '--config' may be specified.
2-
//
3-
// RUN: not %clang --config 1.cfg --config 2.cfg 2>&1 | FileCheck %s -check-prefix CHECK-DUPLICATE
4-
// CHECK-DUPLICATE: no more than one option '--config' is allowed
5-
6-
71
//--- '--config' must be followed by config file name.
82
//
93
// RUN: not %clang --config 2>&1 | FileCheck %s -check-prefix CHECK-MISSING-FILE
@@ -22,6 +16,11 @@
2216
// CHECK-NONEXISTENT: configuration file '{{.*}}somewhere/nonexistent-config-file' does not exist
2317

2418

19+
//--- All '--config' arguments must be existing files.
20+
//
21+
// RUN: not %clang --config %S/Inputs/config-4.cfg --config somewhere/nonexistent-config-file 2>&1 | FileCheck %s -check-prefix CHECK-NONEXISTENT
22+
23+
2524
//--- Argument of '--config' must exist somewhere in well-known directories, if it is specified by bare name.
2625
//
2726
// RUN: not %clang --config-system-dir= --config-user-dir= --config nonexistent-config-file.cfg 2>&1 | FileCheck %s -check-prefix CHECK-NOTFOUND0

clang/test/Driver/config-file.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@
7373
// CHECK-PRECEDENCE: -Wall
7474

7575

76-
//--- Duplicate --config options are allowed if the value is the same
77-
// 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
78-
// CHECK-SAME-CONFIG: Configuration file: {{.*}}Inputs{{.}}config2{{.}}config-4.cfg
76+
//--- Multiple configuration files can be specified.
77+
// 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
78+
// CHECK-TWO-CONFIGS: Configuration file: {{.*}}Inputs{{.}}config{{.}}config-4.cfg
79+
// CHECK-TWO-CONFIGS-NEXT: Configuration file: {{.*}}Inputs{{.}}config2{{.}}config-4.cfg
80+
// CHECK-TWO-CONFIGS: -isysroot
81+
// CHECK-TWO-CONFIGS-SAME: /opt/data
82+
// CHECK-TWO-CONFIGS-SAME: -Wall

0 commit comments

Comments
 (0)