Index: clang/include/clang/Driver/Driver.h =================================================================== --- clang/include/clang/Driver/Driver.h +++ 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 #include #include +#include namespace llvm { class Triple; @@ -258,8 +260,8 @@ /// 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 ConfigFiles; /// Allocator for string saver. llvm::BumpPtrAllocator Alloc; @@ -353,7 +355,9 @@ /// Name to use when invoking gcc/g++. const std::string &getCCCGenericGCCName() const { return CCCGenericGCCName; } - const std::string &getConfigFile() const { return ConfigFile; } + llvm::ArrayRef getConfigFiles() const { + return ConfigFiles; + } const llvm::opt::OptTable &getOpts() const { return getDriverOptTable(); } Index: clang/lib/Driver/Driver.cpp =================================================================== --- clang/lib/Driver/Driver.cpp +++ clang/lib/Driver/Driver.cpp @@ -929,6 +929,22 @@ 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 NewCfgArgs; @@ -940,31 +956,38 @@ // 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( + std::unique_ptr NewOptions = std::make_unique( 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; - // Process options that change search path for config files. if (CLOptions) { if (CLOptions->hasArg(options::OPT_config_system_dir_EQ)) { @@ -995,21 +1018,12 @@ if (CLOptions) { std::vector 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 : ConfigFiles) { // 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 +1034,31 @@ 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; - } - - if (CfgFileName.empty()) + 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; @@ -1145,21 +1155,6 @@ 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 +1162,7 @@ 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 +1181,7 @@ if (!ContainsError) for (auto *Opt : *CLModePassThroughOptions) { - appendOneArg(Opt, nullptr); + appendOneArg(Args, Opt, nullptr); } } } @@ -1880,8 +1875,8 @@ // 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'; } Index: clang/test/Driver/config-file-errs.c =================================================================== --- clang/test/Driver/config-file-errs.c +++ 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 Index: clang/test/Driver/config-file.c =================================================================== --- clang/test/Driver/config-file.c +++ 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: Configuration file: {{.*}}Inputs{{.}}config2{{.}}config-4.cfg +// CHECK-TWO-CONFIGS: -isysroot +// CHECK-TWO-CONFIGS-SAME: /opt/data +// CHECK-TWO-CONFIGS-SAME: -Wall