diff options
author | Serge Pavlov <sepavloff@gmail.com> | 2017-12-30 17:59:26 +0000 |
---|---|---|
committer | Serge Pavlov <sepavloff@gmail.com> | 2017-12-30 17:59:26 +0000 |
commit | 67245e0d36c6dd3d02f8434dffa29de19b97e620 (patch) | |
tree | febb8c2a24cf0263aee14706972a4b0d7a600d56 | |
parent | 0682d12e455d31b91fa41bc562ae3fa53388a820 (diff) | |
download | clang-67245e0d36c6dd3d02f8434dffa29de19b97e620.tar.gz |
Enable configuration files in clang
Clang is inherently a cross compiler and can generate code for any target
enabled during build. It however requires to specify many parameters in the
invocation, which could be hardcoded during configuration process in the
case of single-target compiler. The purpose of configuration files is to
make specifying clang arguments easier.
A configuration file is a collection of driver options, which are inserted
into command line before other options specified in the clang invocation.
It groups related options together and allows specifying them in simpler,
more flexible and less error prone way than just listing the options
somewhere in build scripts. Configuration file may be thought as a "macro"
that names an option set and is expanded when the driver is called.
Use of configuration files is described in `UserManual.rst`.
Differential Revision: https://reviews.llvm.org/D24933
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@321587 91177308-0d34-0410-b5e6-96231b3b80d8
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 + |