diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h --- a/llvm/include/llvm/Support/CommandLine.h +++ b/llvm/include/llvm/Support/CommandLine.h @@ -31,6 +31,7 @@ #include "llvm/ADT/iterator_range.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -2111,6 +2112,31 @@ bool RelativeNames, llvm::Optional CurrentDir, llvm::vfs::FileSystem &FS); +/// Expand response files on a command line recursively using the given +/// StringSaver and tokenization strategy. Argv should contain the command line +/// before expansion and will be modified in place. If requested, Argv will +/// also be populated with nullptrs indicating where each response file line +/// ends, which is useful for the "/link" argument that needs to consume all +/// remaining arguments only until the next end of line, when in a response +/// file. +/// +/// \param [in] Saver Delegates back to the caller for saving parsed strings. +/// \param [in] Tokenizer Tokenization strategy. Typically Unix or Windows. +/// \param [in,out] Argv Command line into which to expand response files. +/// \param [in] MarkEOLs Mark end of lines and the end of the response file +/// with nullptrs in the Argv vector. +/// \param [in] RelativeNames true if names of nested response files must be +/// resolved relative to including file. +/// \param [in] FS File system used for all file access when running the tool. +/// \param [in] CurrentDir Path used to resolve relative rsp files. If set to +/// None, process' cwd is used instead. +/// \return success if all @files were expanded successfully or there were none. +llvm::Error ExpandResponseFilesWithError( + StringSaver &Saver, TokenizerCallback Tokenizer, + SmallVectorImpl &Argv, bool MarkEOLs = false, + bool RelativeNames = false, + llvm::Optional CurrentDir = llvm::None, + llvm::vfs::FileSystem &FS = *llvm::vfs::getRealFileSystem()); /// An overload of ExpandResponseFiles() that uses /// llvm::vfs::getRealFileSystem(). diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp --- a/llvm/lib/Support/CommandLine.cpp +++ b/llvm/lib/Support/CommandLine.cpp @@ -1136,12 +1136,10 @@ /// Expand response files on a command line recursively using the given /// StringSaver and tokenization strategy. -bool cl::ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer, - SmallVectorImpl &Argv, bool MarkEOLs, - bool RelativeNames, - llvm::Optional CurrentDir, - llvm::vfs::FileSystem &FS) { - bool AllExpanded = true; +llvm::Error cl::ExpandResponseFilesWithError( + StringSaver &Saver, TokenizerCallback Tokenizer, + SmallVectorImpl &Argv, bool MarkEOLs, bool RelativeNames, + llvm::Optional CurrentDir, llvm::vfs::FileSystem &FS) { struct ResponseFileRecord { std::string File; size_t End; @@ -1156,6 +1154,7 @@ // the need to check for an empty list. FileStack.push_back({"", Argv.size()}); + llvm::Error FirstErr = llvm::Error::success(); // Don't cache Argv.size() because it can change. for (unsigned I = 0; I != Argv.size();) { while (I == FileStack.back().End) { @@ -1188,46 +1187,48 @@ llvm::sys::path::append(CurrDir, FName); FName = CurrDir.c_str(); } - auto IsEquivalent = [FName, &FS](const ResponseFileRecord &RFile) { + + SmallVector ExpandedArgv; + auto DoExpandResponseFile = [&]() -> llvm::Error { + // Check for recursive response files. llvm::ErrorOr LHS = FS.status(FName); - if (!LHS) { - // TODO: The error should be propagated up the stack. - llvm::consumeError(llvm::errorCodeToError(LHS.getError())); - return false; + if (!LHS) + return llvm::createStringError(LHS.getError(), + Twine("cannot read response file: ") + + Twine(FName)); + + for (const auto &RFile : drop_begin(FileStack)) { + llvm::ErrorOr RHS = FS.status(RFile.File); + if (!RHS) + return llvm::createStringError(RHS.getError(), + Twine("cannot to read file: ") + + Twine(RFile.File)); + + if (LHS->equivalent(*RHS)) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + Twine("cannot expand recursive response file: ") + Twine(FName)); } - llvm::ErrorOr RHS = FS.status(RFile.File); - if (!RHS) { - // TODO: The error should be propagated up the stack. - llvm::consumeError(llvm::errorCodeToError(RHS.getError())); - return false; - } - return LHS->equivalent(*RHS); - }; - // Check for recursive response files. - if (any_of(drop_begin(FileStack), IsEquivalent)) { - // This file is recursive, so we leave it in the argument stream and - // move on. - AllExpanded = false; - ++I; - continue; - } + // Replace this response file argument with the tokenization of its + // contents. Nested response files are expanded in subsequent iterations. + // If we fail to read this file, then give up and return an error. + if (llvm::Error Err = + ExpandResponseFile(FName, Saver, Tokenizer, ExpandedArgv, + MarkEOLs, RelativeNames, FS)) + return std::move(Err); + return llvm::Error::success(); + }; - // Replace this response file argument with the tokenization of its - // contents. Nested response files are expanded in subsequent iterations. - SmallVector ExpandedArgv; - if (llvm::Error Err = - ExpandResponseFile(FName, Saver, Tokenizer, ExpandedArgv, MarkEOLs, - RelativeNames, FS)) { - // We couldn't read this file, so we leave it in the argument stream and - // move on. - // TODO: The error should be propagated up the stack. - llvm::consumeError(std::move(Err)); - AllExpanded = false; + if (llvm::Error Err = DoExpandResponseFile()) { + // Leave this on the argument stream and move on. ++I; + if (!FirstErr) + FirstErr = std::move(Err); + else + consumeError(std::move(Err)); continue; } - for (ResponseFileRecord &Record : FileStack) { // Increase the end of all active records by the number of newly expanded // arguments, minus the response file itself. @@ -1245,9 +1246,24 @@ // don't have a chance to pop the stack when encountering recursive files at // the end of the stream, so seeing that doesn't indicate a bug. assert(FileStack.size() > 0 && Argv.size() == FileStack.back().End); - return AllExpanded; + return std::move(FirstErr); } +/// Expand response files on a command line recursively using the given +/// StringSaver and tokenization strategy. +bool cl::ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer, + SmallVectorImpl &Argv, bool MarkEOLs, + bool RelativeNames, + llvm::Optional CurrentDir, + llvm::vfs::FileSystem &FS) { + if (Error err = ExpandResponseFilesWithError( + Saver, std::move(Tokenizer), Argv, MarkEOLs, RelativeNames, + std::move(CurrentDir), *vfs::getRealFileSystem())) { + consumeError(std::move(err)); + return false; + } + return true; +} bool cl::ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer, SmallVectorImpl &Argv, bool MarkEOLs, bool RelativeNames,