diff options
25 files changed, 686 insertions, 12 deletions
diff --git a/docs/UsersManual.rst b/docs/UsersManual.rst index 9596fb1cbd..fda887ac50 100644 --- a/docs/UsersManual.rst +++ b/docs/UsersManual.rst @@ -694,6 +694,77 @@ a special character, which is the convention used by GNU Make. The -MV option tells Clang to put double-quotes around the entire filename, which is the convention used by NMake and Jom. +Configuration files +------------------- + +Configuration files group command-line options and allow all of them to be +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: + +:: + + clang --config /home/user/cfgs/testing.txt + clang --config debug.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 +treated as a file name and is searched for sequentially in the directories: + - user directory, + - system directory, + - the directory where Clang executable resides. +Both user and system directories for configuration files are specified during +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. + +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. + +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 +`x86_64-clang-cl`, Clang first looks for `x86_64-cl.cfg` and if it is not found, +looks for `x86_64.cfg'. + +If the command line contains options that effectively change target architecture +(these are -m32, -EL, and some others) and the configuration file starts with an +architecture name, Clang tries to load the configuration file for the effective +architecture. For example, invocation: + +:: + + x86_64-clang -m32 abc.c + +causes Clang search for a file `i368.cfg` first, and if no such file is found, +Clang looks for the file `x86_64.cfg`. + +The configuration file consists of command-line options specified on one or +more lines. Lines composed of whitespace characters only are ignored as well as +lines in which the first non-blank character is `#`. Long options may be split +between several lines by a trailing backslash. Here is example of a +configuration file: + +:: + + # Several options on line + -c --target=x86_64-unknown-linux-gnu + + # Long option split between lines + -I/usr/lib/gcc/x86_64-linux-gnu/5.4.0/../../../../\ + include/c++/5.4.0 + + # other config files may be included + @linux.options + +Files included by `@file` directives in configuration files are resolved +relative to the including file. For example, if a configuration file +`~/.llvm/target.cfg` contains the directive `@os/linux.opts`, the file +`linux.opts` is searched for in the directory `~/.llvm/os`. Language and Target-Independent Features ======================================== diff --git a/include/clang/Basic/DiagnosticDriverKinds.td b/include/clang/Basic/DiagnosticDriverKinds.td index 41b5e42b44..45cdf82d36 100644 --- a/include/clang/Basic/DiagnosticDriverKinds.td +++ b/include/clang/Basic/DiagnosticDriverKinds.td @@ -112,6 +112,18 @@ def err_drv_invalid_argument_to_fdebug_prefix_map : Error< "invalid argument '%0' to -fdebug-prefix-map">; def err_drv_malformed_sanitizer_blacklist : Error< "malformed sanitizer blacklist: '%0'">; +def err_drv_duplicate_config : Error< + "no more than one option '--config' is allowed">; +def err_drv_config_file_not_exist : Error< + "configuration file '%0' does not exist">; +def err_drv_config_file_not_found : Error< + "configuration file '%0' cannot be found">; +def note_drv_config_file_searched_in : Note< + "was searched for in the directory: %0">; +def err_drv_cannot_read_config_file : Error< + "cannot read configuration file '%0'">; +def err_drv_nested_config_file: Error< + "option '--config' is not allowed inside configuration file">; def err_target_unsupported_arch : Error<"the target architecture '%0' is not supported by the target '%1'">; diff --git a/include/clang/Config/config.h.cmake b/include/clang/Config/config.h.cmake index daac9b7b45..5f420195e8 100644 --- a/include/clang/Config/config.h.cmake +++ b/include/clang/Config/config.h.cmake @@ -35,6 +35,10 @@ /* Directories clang will search for headers */ #define C_INCLUDE_DIRS "${C_INCLUDE_DIRS}" +/* Directories clang will search for configuration files */ +#cmakedefine CLANG_CONFIG_FILE_SYSTEM_DIR "${CLANG_CONFIG_FILE_SYSTEM_DIR}" +#cmakedefine CLANG_CONFIG_FILE_USER_DIR "${CLANG_CONFIG_FILE_USER_DIR}" + /* Default <path> to all compiler invocations for --sysroot=<path>. */ #define DEFAULT_SYSROOT "${DEFAULT_SYSROOT}" diff --git a/include/clang/Driver/Driver.h b/include/clang/Driver/Driver.h index a3662872a9..0713430983 100644 --- a/include/clang/Driver/Driver.h +++ b/include/clang/Driver/Driver.h @@ -19,6 +19,8 @@ #include "clang/Driver/Util.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/StringSaver.h" #include <list> #include <map> @@ -26,14 +28,6 @@ namespace llvm { class Triple; - -namespace opt { - class Arg; - class ArgList; - class DerivedArgList; - class InputArgList; - class OptTable; -} } namespace clang { @@ -138,6 +132,12 @@ public: /// The path to the compiler resource directory. std::string ResourceDir; + /// System directory for config files. + std::string SystemConfigDir; + + /// User directory for config files. + std::string UserConfigDir; + /// A prefix directory used to emulate a limited subset of GCC's '-Bprefix' /// functionality. /// FIXME: This type of customization should be removed in favor of the @@ -208,6 +208,21 @@ private: /// Name to use when invoking gcc/g++. std::string CCCGenericGCCName; + /// Name of configuration file if used. + std::string ConfigFile; + + /// Allocator for string saver. + llvm::BumpPtrAllocator Alloc; + + /// Object that stores strings read from configuration file. + llvm::StringSaver Saver; + + /// Arguments originated from configuration file. + std::unique_ptr<llvm::opt::InputArgList> CfgOptions; + + /// Arguments originated from command line. + std::unique_ptr<llvm::opt::InputArgList> CLOptions; + /// Whether to check that input files exist when constructing compilation /// jobs. unsigned CheckInputsExist : 1; @@ -277,6 +292,8 @@ public: /// Name to use when invoking gcc/g++. const std::string &getCCCGenericGCCName() const { return CCCGenericGCCName; } + const std::string &getConfigFile() const { return ConfigFile; } + const llvm::opt::OptTable &getOpts() const { return *Opts; } const DiagnosticsEngine &getDiags() const { return Diags; } @@ -493,6 +510,18 @@ public: LTOKind getLTOMode() const { return LTOMode; } private: + + /// Tries to load options from configuration file. + /// + /// \returns true if error occurred. + bool loadConfigFile(); + + /// Read options from the specified file. + /// + /// \param [in] FileName File to read. + /// \returns true, if error occurred while reading. + bool readConfigFile(StringRef FileName); + /// Set the driver mode (cl, gcc, etc) from an option string of the form /// --driver-mode=<mode>. void setDriverModeFromOption(StringRef Opt); diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 7dc6632e82..3ee834e685 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -519,6 +519,12 @@ def cl_fp32_correctly_rounded_divide_sqrt : Flag<["-"], "cl-fp32-correctly-round def client__name : JoinedOrSeparate<["-"], "client_name">; def combine : Flag<["-", "--"], "combine">, Flags<[DriverOption, Unsupported]>; def compatibility__version : JoinedOrSeparate<["-"], "compatibility_version">; +def config : Separate<["--"], "config">, Flags<[DriverOption]>, + HelpText<"Specifies configuration file">; +def config_system_dir_EQ : Joined<["--"], "config-system-dir=">, Flags<[DriverOption, HelpHidden]>, + HelpText<"System directory for configuration files">; +def config_user_dir_EQ : Joined<["--"], "config-user-dir=">, Flags<[DriverOption, HelpHidden]>, + HelpText<"User directory for configuration files">; def coverage : Flag<["-", "--"], "coverage">, Flags<[CoreOption]>; def cpp_precomp : Flag<["-"], "cpp-precomp">, Group<clang_ignored_f_Group>; def current__version : JoinedOrSeparate<["-"], "current_version">; diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 9ae33b80f8..e29aa267ae 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -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" @@ -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> @@ -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. @@ -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); @@ -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"); @@ -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)); @@ -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 @@ -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)) diff --git a/test/Driver/Inputs/config-1.cfg b/test/Driver/Inputs/config-1.cfg new file mode 100644 index 0000000000..7d1326fe8b --- /dev/null +++ b/test/Driver/Inputs/config-1.cfg @@ -0,0 +1,6 @@ + +# Empty lines and line started with # are ignored +-Werror + + # Language + -std=c99 diff --git a/test/Driver/Inputs/config-2.cfg b/test/Driver/Inputs/config-2.cfg new file mode 100644 index 0000000000..c803bd2a78 --- /dev/null +++ b/test/Driver/Inputs/config-2.cfg @@ -0,0 +1,2 @@ +# nested inclusion +@config-3.cfg diff --git a/test/Driver/Inputs/config-2a.cfg b/test/Driver/Inputs/config-2a.cfg new file mode 100644 index 0000000000..ecdc555156 --- /dev/null +++ b/test/Driver/Inputs/config-2a.cfg @@ -0,0 +1,2 @@ +# nested inclusion +@config/config-4.cfg diff --git a/test/Driver/Inputs/config-3.cfg b/test/Driver/Inputs/config-3.cfg new file mode 100644 index 0000000000..d5086a8e4e --- /dev/null +++ b/test/Driver/Inputs/config-3.cfg @@ -0,0 +1 @@ +-Wundefined-func-template diff --git a/test/Driver/Inputs/config-4.cfg b/test/Driver/Inputs/config-4.cfg new file mode 100644 index 0000000000..d8a022ae75 --- /dev/null +++ b/test/Driver/Inputs/config-4.cfg @@ -0,0 +1,2 @@ +-L/usr/local/lib +-stdlib=libc++
\ No newline at end of file diff --git a/test/Driver/Inputs/config-5.cfg b/test/Driver/Inputs/config-5.cfg new file mode 100644 index 0000000000..09787ae6c4 --- /dev/null +++ b/test/Driver/Inputs/config-5.cfg @@ -0,0 +1,2 @@ +--serialize-diagnostics diag.ser +-target diff --git a/test/Driver/Inputs/config-6.cfg b/test/Driver/Inputs/config-6.cfg new file mode 100644 index 0000000000..24d93cf8ec --- /dev/null +++ b/test/Driver/Inputs/config-6.cfg @@ -0,0 +1 @@ +--config config-5 diff --git a/test/Driver/Inputs/config/config-4.cfg b/test/Driver/Inputs/config/config-4.cfg new file mode 100644 index 0000000000..033e86a52a --- /dev/null +++ b/test/Driver/Inputs/config/config-4.cfg @@ -0,0 +1 @@ +-isysroot /opt/data diff --git a/test/Driver/Inputs/config/i386-qqq.cfg b/test/Driver/Inputs/config/i386-qqq.cfg new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/Driver/Inputs/config/i386-qqq.cfg diff --git a/test/Driver/Inputs/config/i386-qqq3.cfg b/test/Driver/Inputs/config/i386-qqq3.cfg new file mode 100644 index 0000000000..9ae1735be3 --- /dev/null +++ b/test/Driver/Inputs/config/i386-qqq3.cfg @@ -0,0 +1 @@ +-target i383-unknown-linux
\ No newline at end of file diff --git a/test/Driver/Inputs/config/x86_64-qqq.cfg b/test/Driver/Inputs/config/x86_64-qqq.cfg new file mode 100644 index 0000000000..c198278ec7 --- /dev/null +++ b/test/Driver/Inputs/config/x86_64-qqq.cfg @@ -0,0 +1 @@ +-target x86_64-unknown-linux diff --git a/test/Driver/Inputs/config/x86_64-qqq2.cfg b/test/Driver/Inputs/config/x86_64-qqq2.cfg new file mode 100644 index 0000000000..c198278ec7 --- /dev/null +++ b/test/Driver/Inputs/config/x86_64-qqq2.cfg @@ -0,0 +1 @@ +-target x86_64-unknown-linux diff --git a/test/Driver/Inputs/config/x86_64.cfg b/test/Driver/Inputs/config/x86_64.cfg new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/Driver/Inputs/config/x86_64.cfg diff --git a/test/Driver/Inputs/config2/config-4.cfg b/test/Driver/Inputs/config2/config-4.cfg new file mode 100644 index 0000000000..bd866b6966 --- /dev/null +++ b/test/Driver/Inputs/config2/config-4.cfg @@ -0,0 +1 @@ +-Wall diff --git a/test/Driver/Inputs/config2/i386.cfg b/test/Driver/Inputs/config2/i386.cfg new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/Driver/Inputs/config2/i386.cfg diff --git a/test/Driver/config-file-errs.c b/test/Driver/config-file-errs.c new file mode 100644 index 0000000000..8db2ea4392 --- /dev/null +++ b/test/Driver/config-file-errs.c @@ -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 diff --git a/test/Driver/config-file.c b/test/Driver/config-file.c new file mode 100644 index 0000000000..2d2d7a72b4 --- /dev/null +++ b/test/Driver/config-file.c @@ -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 diff --git a/test/Driver/config-file2.c b/test/Driver/config-file2.c new file mode 100644 index 0000000000..3a93b55eac --- /dev/null +++ b/test/Driver/config-file2.c @@ -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 diff --git a/test/Driver/config-file3.c b/test/Driver/config-file3.c new file mode 100644 index 0000000000..c1b523cbf9 --- /dev/null +++ b/test/Driver/config-file3.c @@ -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 + |