Index: include/clang/Driver/Compilation.h =================================================================== --- include/clang/Driver/Compilation.h +++ include/clang/Driver/Compilation.h @@ -94,7 +94,7 @@ JobList &getJobs() { return Jobs; } const JobList &getJobs() const { return Jobs; } - void addCommand(Command *C) { Jobs.addJob(C); } + void addCommand(Command *C); const llvm::opt::ArgStringList &getTempFiles() const { return TempFiles; } Index: include/clang/Driver/Job.h =================================================================== --- include/clang/Driver/Job.h +++ include/clang/Driver/Job.h @@ -72,6 +72,22 @@ /// argument, which will be the executable). llvm::opt::ArgStringList Arguments; + /// Whether this job will need to write its arguments to a disk file + const bool needsResponseFile; + + /// Response file name + const char *ResponseFile; + + /// The input file list in case we need to emit a file list instead of a + /// proper response file + llvm::opt::ArgStringList InputFileList; + + /// Helps Command::Print to precisely describe the contents of a reponse + /// file, when it is used. + void PrintArgsWithRespFile(llvm::raw_ostream &OS, + const char *Terminator, bool Quote, + bool CrashReport) const; + public: Command(const Action &_Source, const Tool &_Creator, const char *_Executable, const llvm::opt::ArgStringList &_Arguments); @@ -79,6 +95,10 @@ void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, bool CrashReport = false) const override; + /// Checks whether the given command line arguments fit within system + /// limits (the maximum command line length). + bool NeedsResponseFile() const { return needsResponseFile; } + virtual int Execute(const StringRef **Redirects, std::string *ErrMsg, bool *ExecutionFailed) const; @@ -88,6 +108,9 @@ /// getCreator - Return the Tool which caused the creation of this job. const Tool &getCreator() const { return Creator; } + void setResponseFile(const char *FileName) { ResponseFile = FileName; } + void setInputFileList(llvm::opt::ArgStringList List) { InputFileList = List; } + const char *getExecutable() const { return Executable; } const llvm::opt::ArgStringList &getArguments() const { return Arguments; } Index: include/clang/Driver/Tool.h =================================================================== --- include/clang/Driver/Tool.h +++ include/clang/Driver/Tool.h @@ -11,6 +11,7 @@ #define LLVM_CLANG_DRIVER_TOOL_H #include "clang/Basic/LLVM.h" +#include "llvm/Support/Program.h" namespace llvm { namespace opt { @@ -31,6 +32,24 @@ /// Tool - Information on a specific compilation tool. class Tool { +public: + // Documents the level of support for response files in this tool. + // Response files are necessary if the command line gets too large, + // requiring the arguments to be transfered to a file. + enum ResponseFileSupport { + // Provides full support for response files, which means we can transfer + // all tool input arguments to a file. E.g.: clang, gcc, binutils and MSVC + // tools. + RF_Full, + // Input file names can live in a file, but flags can't. E.g.: ld64 (Mac + // OS X linker). + RF_FileList, + // Does not support response files: all arguments must be passed via + // command line. + RF_None + }; + +private: /// The tool name (for debugging). const char *Name; @@ -57,6 +76,30 @@ virtual bool hasIntegratedCPP() const = 0; virtual bool isLinkJob() const { return false; } virtual bool isDsymutilJob() const { return false; } + /// \brief Returns the level of support for response files of this tool, + /// whether it accepts arguments to be passed via a file on disk. + virtual ResponseFileSupport getResponseFilesSupport() const { + return RF_None; + } + /// \brief Returns which encoding the response file should use. This is only + /// relevant on Windows platforms where there are different encodings being + /// accepted for different tools. On UNIX, UTF8 is universal. + /// + /// Windows use cases: - GCC and Binutils on mingw only accept ANSI response + /// files encoded with the system current code page. + /// - MSVC's CL.exe and LINK.exe accept UTF16 on Windows. + /// - Clang accepts both UTF8 and UTF16. + /// + /// FIXME: When GNU tools learn how to parse UTF16 on Windows, we should + /// always use UTF16 for Windows, which is the Windows official encoding for + /// international characters. + virtual llvm::sys::EncodingStrategy getResponseFileEncoding() const { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_UTF8}; + } + /// \brief Returns which prefix to use when passing the name of a response + /// file as a parameter to this tool. + virtual const char *getResponseFileFlag() const { return "@"; } /// \brief Does this tool have "good" standardized diagnostics, or should the /// driver add an additional "command failed" diagnostic on failures. Index: lib/Driver/Compilation.cpp =================================================================== --- lib/Driver/Compilation.cpp +++ lib/Driver/Compilation.cpp @@ -52,6 +52,15 @@ } } +void Compilation::addCommand(Command *C) { + Jobs.addJob(C); + // Verify if we need a response file + if (C->NeedsResponseFile()) { + std::string TmpName = TheDriver.GetTemporaryPath("response", "txt"); + C->setResponseFile(addTempFile(getArgs().MakeArgString(TmpName.c_str()))); + } +} + const DerivedArgList &Compilation::getArgsForToolChain(const ToolChain *TC, const char *BoundArch) { if (!TC) @@ -88,7 +97,7 @@ // Failure is only failure if the file exists and is "regular". We checked // for it being regular before, and llvm::sys::fs::remove ignores ENOENT, // so we don't need to check again. - + if (IssueErrors) getDriver().Diag(clang::diag::err_drv_unable_to_remove_file) << EC.message(); Index: lib/Driver/Job.cpp =================================================================== --- lib/Driver/Job.cpp +++ lib/Driver/Job.cpp @@ -28,7 +28,11 @@ const char *_Executable, const ArgStringList &_Arguments) : Job(CommandClass), Source(_Source), Creator(_Creator), - Executable(_Executable), Arguments(_Arguments) {} + Executable(_Executable), Arguments(_Arguments), + needsResponseFile( + !llvm::sys::argumentsFitWithinSystemLimits(_Arguments) && + Creator.getResponseFilesSupport() != Tool::RF_None), + ResponseFile(nullptr) {} static int skipArgs(const char *Flag) { // These flags are all of the form -Flag and are treated as two @@ -93,12 +97,189 @@ OS << '"'; } +/// ArgNeedsQuotes - Check whether argument needs to be quoted when serializing +/// it to a response file. +static bool ArgNeedsQuotes(const char *Str) { + return Str[0] == '\0' || strpbrk(Str, "\t \"&\'()*<>\\`^|") != 0; +} + +/// Returns the number of backslashes and quotes in this string. These +/// characters need to be escaped in the response files of some tools, but +/// others do not care. Thus, we escape for all response files. +static unsigned int CountBackslashesAndQuotes(const char *Start) { + unsigned int Count = 0; + while (*Start != '\0') { + if (*Start == '\\' || *Start == '\"') + ++Count; + ++Start; + } + return Count; +} + +/// Encodes the array of C strings into a single string separated by whitespace. +/// This function will also put in quotes arguments that have whitespaces and +/// will escape the regular backslashes (used in Windows paths) and quotes. +static std::unique_ptr FlattenArgs(const char * const *Args) { + // First, determine the length of the command line. + unsigned Len = 0; + for (unsigned Cur = 0; Args[Cur]; ++Cur) { + bool Quoted = ArgNeedsQuotes(Args[Cur]); + Len += Quoted ? 2 : 0; + Len += CountBackslashesAndQuotes(Args[Cur]) + strlen(Args[Cur]) + 1; + } + + // Now build the command line. + std::unique_ptr Command(new char[Len + 1]); + char *Cur = Command.get(); + + for (unsigned I = 0; Args[I]; ++I) { + const char *Arg = Args[I]; + + bool NeedsQuoting = ArgNeedsQuotes(Arg); + if (NeedsQuoting) + *Cur++ = '"'; + + while (*Arg != '\0') { + if (*Arg == '\"' || *Arg == '\\') { + *Cur++ = '\\'; + } + *Cur++ = *Arg++; + } + + if (NeedsQuoting) { + *Cur++ = '"'; + } + *Cur++ = ' '; + } + + *Cur = 0; + return Command; +} + +/// Check if string \p Arg is present in the list of inputs. +static bool IsInInputList(const llvm::SmallVectorImpl &Inputs, + const char *Arg) { + for (auto InputName : Inputs) { + if (strcmp(Arg, InputName) == 0) + return true; + } + return false; +} + +/// Read the list of input files in Inputs and adjusts Argv accordingly, +/// removing the elements of Inputs from Argv (since they will be passed via a +/// response file). This function will also Insert the response file name in +/// argv. Finally, it returns a C string with the contents of the response +/// file (the input file list separated by newlines). +static std::unique_ptr +FlattenFileList(const llvm::SmallVectorImpl &Inputs, + llvm::SmallVectorImpl &Argv, + const char *ResponseFlag, const char *ResponseFile) { + llvm::SmallVector NewArgv; + unsigned Len = 0; + for (auto Input : Inputs) { + Len += strlen(Input) + 1; + } + bool FirstInput = true; + for (auto Arg : Argv) { + // End of list? + if (Arg == nullptr) { + NewArgv.push_back(Arg); + break; + } + if (!IsInInputList(Inputs, Arg)) { + NewArgv.push_back(Arg); + } else if (FirstInput) { + FirstInput = false; + NewArgv.push_back(ResponseFlag); + NewArgv.push_back(ResponseFile); + } + } + Argv = NewArgv; + // Now build the file list. + std::unique_ptr Command(new char[Len + 1]); + char *Cur = Command.get(); + for (auto Arg : Inputs) { + const char *Src = Arg; + while (*Src != '\0') + *Cur++ = *Src++; + *Cur++ = '\n'; + } + *Cur = 0; + return Command; +} + +void Command::PrintArgsWithRespFile(raw_ostream &OS, + const char *Terminator, bool Quote, + bool CrashReport) const { + std::string ResponseFlag = Creator.getResponseFileFlag(); + ResponseFlag += ResponseFile; + OS << ' '; + + llvm::opt::ArgStringList Argv = Arguments; + Argv.push_back(nullptr); + std::unique_ptr FlattenedArgs; + Tool::ResponseFileSupport RespFileSup = Creator.getResponseFilesSupport(); + if (RespFileSup == Tool::RF_FileList) { + FlattenedArgs = FlattenFileList(InputFileList, Argv, + Creator.getResponseFileFlag(), + ResponseFile); + } else { + FlattenedArgs = FlattenArgs(Argv.data()); + Argv.clear(); + } + + // Print args that are not in the FileList (if it is a proper response file + // rather than a file list, all arguments go into the response file and Argv + // is empty). + for (size_t Cur = 0, End = Argv.size(); Cur < End; ++Cur) { + const char *const Arg = Argv[Cur]; + if (Arg == nullptr) + break; + + if (CrashReport) { + if (int Skip = skipArgs(Arg)) { + Cur += Skip - 1; + continue; + } + } + + OS << ' '; + PrintArg(OS, Arg, Quote); + + if (CrashReport && quoteNextArg(Arg) && Cur + 1 < End) { + OS << ' '; + PrintArg(OS, Arguments[++Cur], true); + } + } + // Now print our response file flag and print the contents that we will put in + // a response file. If it is a FileList, FlattenFileList() already put the + // flag in Argv and there is no need to print it again. + if (RespFileSup != Tool::RF_FileList) { + PrintArg(OS, ResponseFlag.c_str(), Quote); + } + OS << "\n Arguments passed via response file:\n"; + OS << FlattenedArgs.get(); + // Avoiding duplicated newline terminator, since FileLists are + // newline-separated. + if (RespFileSup != Tool::RF_FileList) { + OS << "\n"; + } + OS << " (end of response file)"; + OS << Terminator; +} + void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, bool CrashReport) const { // Always quote the exe. OS << ' '; PrintArg(OS, Executable, /*Quote=*/true); + if (needsResponseFile && ResponseFile != nullptr) { + PrintArgsWithRespFile(OS, Terminator, Quote, CrashReport); + return; + } + for (size_t i = 0, e = Arguments.size(); i < e; ++i) { const char *const Arg = Arguments[i]; @@ -128,6 +309,46 @@ Argv.push_back(Arguments[i]); Argv.push_back(nullptr); + if (ResponseFile == nullptr || !needsResponseFile) { + return llvm::sys::ExecuteAndWait(Executable, Argv.data(), /*env*/ nullptr, + Redirects, /*secondsToWait*/ 0, + /*memoryLimit*/ 0, ErrMsg, + ExecutionFailed); + } + + // We need to put arguments in a response file (command is too large) + std::string ResponseFlag; + std::unique_ptr FlattenedArgs; + Tool::ResponseFileSupport RespFileSup = Creator.getResponseFilesSupport(); + ResponseFlag = Creator.getResponseFileFlag(); + ResponseFlag += ResponseFile; + if (RespFileSup == Tool::RF_FileList) + FlattenedArgs = FlattenFileList(InputFileList, Argv, + Creator.getResponseFileFlag(), + ResponseFile); + else + FlattenedArgs = FlattenArgs(Argv.data() + 1 /* Skip exec name */); + + if (std::error_code ec = + llvm::sys::writeFileWithEncoding(ResponseFile, FlattenedArgs.get(), + Creator.getResponseFileEncoding())) { + if (ErrMsg) + *ErrMsg = ec.message(); + if (ExecutionFailed) + *ExecutionFailed = true; + return -1; + } + + // Substitute the argument vector with a single argument to supply the + // response file. In case of a FileList, FlattenFileList() already prepared + // Argv with the correct arguments. + if (RespFileSup != Tool::RF_FileList) { + Argv.clear(); + Argv.push_back(Executable); + Argv.push_back(ResponseFlag.c_str()); + Argv.push_back(nullptr); + } + return llvm::sys::ExecuteAndWait(Executable, Argv.data(), /*env*/ nullptr, Redirects, /*secondsToWait*/ 0, /*memoryLimit*/ 0, ErrMsg, ExecutionFailed); Index: lib/Driver/Tools.h =================================================================== --- lib/Driver/Tools.h +++ lib/Driver/Tools.h @@ -95,6 +95,9 @@ bool hasGoodDiagnostics() const override { return true; } bool hasIntegratedAssembler() const override { return true; } bool hasIntegratedCPP() const override { return true; } + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } void ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, @@ -111,6 +114,9 @@ bool hasGoodDiagnostics() const override { return true; } bool hasIntegratedAssembler() const override { return false; } bool hasIntegratedCPP() const override { return false; } + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } void ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, @@ -130,6 +136,13 @@ const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } /// RenderExtraToolArgs - Render any arguments necessary to force /// the particular tool mode. @@ -191,6 +204,13 @@ const InputInfo &Output, const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } }; class LLVM_LIBRARY_VISIBILITY Link : public Tool { @@ -207,6 +227,13 @@ const InputInfo &Output, const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } }; } // end namespace hexagon. @@ -276,6 +303,12 @@ bool hasIntegratedCPP() const override { return false; } bool isLinkJob() const override { return true; } + ResponseFileSupport getResponseFilesSupport() const override { + return RF_FileList; + } + virtual const char *getResponseFileFlag() const override { + return "-filelist"; + } void ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, @@ -339,6 +372,14 @@ const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } }; class LLVM_LIBRARY_VISIBILITY Link : public Tool { public: @@ -351,6 +392,14 @@ const InputInfo &Output, const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } }; } // end namespace openbsd @@ -367,6 +416,14 @@ const InputInfo &Output, const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } }; class LLVM_LIBRARY_VISIBILITY Link : public Tool { public: @@ -379,6 +436,14 @@ const InputInfo &Output, const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } }; } // end namespace bitrig @@ -395,6 +460,14 @@ const InputInfo &Output, const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } }; class LLVM_LIBRARY_VISIBILITY Link : public Tool { public: @@ -407,6 +480,14 @@ const InputInfo &Output, const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } }; } // end namespace freebsd @@ -424,6 +505,14 @@ const InputInfo &Output, const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } }; class LLVM_LIBRARY_VISIBILITY Link : public Tool { @@ -438,6 +527,14 @@ const InputInfo &Output, const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } }; } // end namespace netbsd @@ -454,6 +551,14 @@ const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } }; class LLVM_LIBRARY_VISIBILITY Link : public Tool { public: @@ -461,6 +566,13 @@ bool hasIntegratedCPP() const override { return false; } bool isLinkJob() const override { return true; } + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } void ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, @@ -483,6 +595,14 @@ const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } }; class LLVM_LIBRARY_VISIBILITY Link : public Tool { public: @@ -496,6 +616,14 @@ const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } }; } // end namespace minix @@ -540,6 +668,14 @@ const InputInfo &Output, const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } }; class LLVM_LIBRARY_VISIBILITY Link : public Tool { public: @@ -553,6 +689,14 @@ const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF8, + /*WindowsEncoding*/llvm::sys::EM_CurrentCodePage}; + } }; } // end namespace dragonfly @@ -564,6 +708,13 @@ bool hasIntegratedCPP() const override { return false; } bool isLinkJob() const override { return true; } + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF16, + /*WindowsEncoding*/llvm::sys::EM_UTF16}; + } void ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, @@ -578,6 +729,13 @@ bool hasIntegratedAssembler() const override { return true; } bool hasIntegratedCPP() const override { return true; } bool isLinkJob() const override { return false; } + ResponseFileSupport getResponseFilesSupport() const override { + return RF_Full; + } + llvm::sys::EncodingStrategy getResponseFileEncoding() const override { + return {/*UnixEncoding*/ llvm::sys::EM_UTF16, + /*WindowsEncoding*/llvm::sys::EM_UTF16}; + } void ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -506,7 +506,7 @@ } else D.Diag(diag::err_drv_clang_unsupported) << A->getAsString(Args); } - + // Handle -mfpu=. // // FIXME: Centralize feature selection, defaulting shouldn't be also in the @@ -815,7 +815,7 @@ } } - // Setting -mno-global-merge disables the codegen global merge pass. Setting + // Setting -mno-global-merge disables the codegen global merge pass. Setting // -mglobal-merge has no effect as the pass is enabled by default. if (Arg *A = Args.getLastArg(options::OPT_mglobal_merge, options::OPT_mno_global_merge)) { @@ -2507,7 +2507,7 @@ // Select the appropriate action. RewriteKind rewriteKind = RK_None; - + if (isa(JA)) { assert(JA.getType() == types::TY_Plist && "Invalid output type."); CmdArgs.push_back("-analyze"); @@ -2609,9 +2609,9 @@ if (getToolChain().getTriple().getVendor() == llvm::Triple::Apple) CmdArgs.push_back("-analyzer-checker=osx"); - + CmdArgs.push_back("-analyzer-checker=deadcode"); - + if (types::isCXX(Inputs[0].getType())) CmdArgs.push_back("-analyzer-checker=cplusplus"); @@ -2620,7 +2620,7 @@ "-analyzer-checker=security.insecureAPI.UncheckedReturn"); CmdArgs.push_back("-analyzer-checker=security.insecureAPI.getpw"); CmdArgs.push_back("-analyzer-checker=security.insecureAPI.gets"); - CmdArgs.push_back("-analyzer-checker=security.insecureAPI.mktemp"); + CmdArgs.push_back("-analyzer-checker=security.insecureAPI.mktemp"); CmdArgs.push_back("-analyzer-checker=security.insecureAPI.mkstemp"); CmdArgs.push_back("-analyzer-checker=security.insecureAPI.vfork"); } @@ -2845,7 +2845,7 @@ // This alias option is being used to simplify the getLastArg logic. OptSpecifier FastMathAliasOption = OFastEnabled ? options::OPT_Ofast : options::OPT_ffast_math; - + // Handle various floating point optimization flags, mapping them to the // appropriate LLVM code generation flags. The pattern for all of these is to // default off the codegen optimizations, and if any flag enables them and no @@ -2944,7 +2944,7 @@ CmdArgs.push_back("-menable-unsafe-fp-math"); - // Validate and pass through -fp-contract option. + // Validate and pass through -fp-contract option. if (Arg *A = Args.getLastArg(options::OPT_ffast_math, FastMathAliasOption, options::OPT_fno_fast_math, options::OPT_ffp_contract)) { @@ -3590,7 +3590,7 @@ Args.AddLastArg(CmdArgs, options::OPT_fno_standalone_debug); Args.AddLastArg(CmdArgs, options::OPT_fno_operator_names); // AltiVec language extensions aren't relevant for assembling. - if (!isa(JA) || + if (!isa(JA) || Output.getType() != types::TY_PP_Asm) Args.AddLastArg(CmdArgs, options::OPT_faltivec); Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_show_template_tree); @@ -3776,7 +3776,7 @@ !Args.hasArg(options::OPT_fno_blocks))) { CmdArgs.push_back("-fblocks"); - if (!Args.hasArg(options::OPT_fgnu_runtime) && + if (!Args.hasArg(options::OPT_fgnu_runtime) && !getToolChain().hasBlocksRuntime()) CmdArgs.push_back("-fblocks-runtime-optional"); } @@ -3786,8 +3786,8 @@ // modules implementation is solid for C++/Objective-C++ programs as well. bool HaveModules = false; if (Args.hasFlag(options::OPT_fmodules, options::OPT_fno_modules, false)) { - bool AllowedInCXX = Args.hasFlag(options::OPT_fcxx_modules, - options::OPT_fno_cxx_modules, + bool AllowedInCXX = Args.hasFlag(options::OPT_fcxx_modules, + options::OPT_fno_cxx_modules, false); if (AllowedInCXX || !types::isCXX(InputType)) { CmdArgs.push_back("-fmodules"); @@ -3957,7 +3957,7 @@ CmdArgs.push_back("-fms-extensions"); // -fms-compatibility=0 is default. - if (Args.hasFlag(options::OPT_fms_compatibility, + if (Args.hasFlag(options::OPT_fms_compatibility, options::OPT_fno_ms_compatibility, (IsWindowsMSVC && Args.hasFlag(options::OPT_fms_extensions, options::OPT_fno_ms_extensions, @@ -4044,12 +4044,12 @@ objcRuntime.getKind() == ObjCRuntime::FragileMacOSX && objcRuntime.isNeXTFamily()) CmdArgs.push_back("-fobjc-subscripting-legacy-runtime"); - + // -fencode-extended-block-signature=1 is default. if (getToolChain().IsEncodeExtendedBlockSignatureDefault()) { CmdArgs.push_back("-fencode-extended-block-signature"); } - + // Allow -fno-objc-arr to trump -fobjc-arr/-fobjc-arc. // NOTE: This logic is duplicated in ToolChains.cpp. bool ARC = isObjCAutoRefCount(Args); @@ -4343,7 +4343,7 @@ options::OPT_fno_apple_pragma_pack, false)) CmdArgs.push_back("-fapple-pragma-pack"); - // le32-specific flags: + // le32-specific flags: // -fno-math-builtin: clang should not convert math builtins to intrinsics // by default. if (getToolChain().getArch() == llvm::Triple::le32) { @@ -4384,7 +4384,7 @@ Args.AddLastArg(CmdArgs, options::OPT_dM); Args.AddLastArg(CmdArgs, options::OPT_dD); - + // Handle serialized diagnostics. if (Arg *A = Args.getLastArg(options::OPT__serialize_diags)) { CmdArgs.push_back("-serialize-diagnostic-file"); @@ -4550,8 +4550,8 @@ << value; } else { // Otherwise, determine if we are using the non-fragile ABI. - bool nonFragileABIIsDefault = - (rewriteKind == RK_NonFragile || + bool nonFragileABIIsDefault = + (rewriteKind == RK_NonFragile || (rewriteKind == RK_None && getToolChain().IsObjCNonFragileABIDefault())); if (args.hasFlag(options::OPT_fobjc_nonfragile_abi, @@ -4804,7 +4804,7 @@ // Add the "effective" target triple. CmdArgs.push_back("-triple"); - std::string TripleStr = + std::string TripleStr = getToolChain().ComputeEffectiveClangTriple(Args, Input.getType()); CmdArgs.push_back(Args.MakeArgString(TripleStr)); @@ -5780,6 +5780,12 @@ const char *LinkingOutput) const { assert(Output.getType() == types::TY_Image && "Invalid linker output type."); + // If the number of arguments surpasses the system limits, we will encode the + // input files in a separate file, shortening the command line. To this end, + // build a list of input file names that can be passed via a file with the + // -filelist linker option. + llvm::opt::ArgStringList InputFileList; + // The logic here is derived from gcc's behavior; most of which // comes from specs (starting with link_command). Consult gcc for // more information. @@ -5848,7 +5854,21 @@ } AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); - + // Build the input file for -filelist (list of linker input files) in case we + // need it later + for (const auto &II : Inputs) { + if (II.isFilename()) { + InputFileList.push_back(II.getFilename()); + continue; + } + + // This is a linker input argument. + // We cannot mix input arguments and file names in a -filelist input, thus + // we prematurely stop our list (remaining files shall be passed as + // arguments). + break; + } + if (isObjCRuntimeLinked(Args) && !Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nodefaultlibs)) { @@ -5891,7 +5911,10 @@ const char *Exec = Args.MakeArgString(getToolChain().GetLinkerPath()); - C.addCommand(new Command(JA, *this, Exec, CmdArgs)); + Command *Cmd = new Command(JA, *this, Exec, CmdArgs); + if (Cmd->NeedsResponseFile()) + Cmd->setInputFileList(InputFileList); + C.addCommand(Cmd); } void darwin::Lipo::ConstructJob(Compilation &C, const JobAction &JA, @@ -6209,7 +6232,7 @@ if (!Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nostartfiles)) { if (!Args.hasArg(options::OPT_shared)) { - if (Args.hasArg(options::OPT_pg)) + if (Args.hasArg(options::OPT_pg)) CmdArgs.push_back(Args.MakeArgString( getToolChain().GetFilePath("gcrt0.o"))); else @@ -6243,7 +6266,7 @@ !Args.hasArg(options::OPT_nodefaultlibs)) { if (D.CCCIsCXX()) { getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs); - if (Args.hasArg(options::OPT_pg)) + if (Args.hasArg(options::OPT_pg)) CmdArgs.push_back("-lm_p"); else CmdArgs.push_back("-lm"); @@ -6508,7 +6531,7 @@ const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { - const toolchains::FreeBSD& ToolChain = + const toolchains::FreeBSD& ToolChain = static_cast(getToolChain()); const Driver &D = ToolChain.getDriver(); const bool IsPIE = @@ -6744,7 +6767,7 @@ break; default: - break; + break; } Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, Index: test/Driver/response-file.c =================================================================== --- /dev/null +++ test/Driver/response-file.c @@ -0,0 +1,26 @@ +// REQUIRES: long_tests + +// Check that clang is able to process short response files +// Since this is a short response file, clang must not use a response file +// to pass its parameters to other tools. This is only necessary for a large +// number of parameters. +// RUN: echo "-DTEST" >> %t.0.txt +// RUN: %clang -E @%t.0.txt %s -v 2>&1 | FileCheck %s -check-prefix=SHORT +// SHORT-NOT: Arguments passed via response file +// SHORT: -D TEST +// SHORT: extern int it_works; + +// Check that clang is able to process long response files, routing a long +// sequence of arguments to other tools by using response files as well. +// We generate a 2MB response file to be big enough to surpass any system +// limit. +// RUN: awk "BEGIN { while (count++<300000) string=string \"-DTEST \";\ +// RUN: print string }" > %t.1.txt +// RUN: %clang -E @%t.1.txt %s -v 2>&1 | FileCheck %s -check-prefix=LONG +// LONG: Arguments passed via response file +// LONG: -D TEST +// LONG: extern int it_works; + +#ifdef TEST +extern int it_works; +#endif