llvm.org GIT mirror llvm / 2e1bf78
Fix PR17239 by changing the semantics of the RemainingArgsClass Option kind This patch contains the LLVM side of the fix of PR17239. This bug that happens because the /link (clang-cl.exe argument) is marked as "consume all remaining arguments". However, when inside a response file, /link should only consume all remaining arguments inside the response file where it is located, not the entire command line after expansion. My patch will change the semantics of the RemainingArgsClass kind to always consume only until the end of the response file when the option originally came from a response file. There are only two options in this class: dash dash (--) and /link. Reviewed By: rnk Differential Revision: http://reviews.llvm.org/D4899 Patch by Rafael Auler! git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@216280 91177308-0d34-0410-b5e6-96231b3b80d8 Reid Kleckner 6 years ago
5 changed file(s) with 70 addition(s) and 20 deletion(s). Raw diff Collapse all Expand all
17891789 ///
17901790 /// \param [in] Source The string to be split on whitespace with quotes.
17911791 /// \param [in] Saver Delegates back to the caller for saving parsed strings.
1792 /// \param [in] MarkEOLs true if tokenizing a response file and you want end of
1793 /// lines and end of the response file to be marked with a nullptr string.
17921794 /// \param [out] NewArgv All parsed strings are appended to NewArgv.
17931795 void TokenizeGNUCommandLine(StringRef Source, StringSaver &Saver,
1794 SmallVectorImpl &NewArgv);
1796 SmallVectorImpl &NewArgv,
1797 bool MarkEOLs = false);
17951798
17961799 /// \brief Tokenizes a Windows command line which may contain quotes and escaped
17971800 /// quotes.
18011804 ///
18021805 /// \param [in] Source The string to be split on whitespace with quotes.
18031806 /// \param [in] Saver Delegates back to the caller for saving parsed strings.
1807 /// \param [in] MarkEOLs true if tokenizing a response file and you want end of
1808 /// lines and end of the response file to be marked with a nullptr string.
18041809 /// \param [out] NewArgv All parsed strings are appended to NewArgv.
18051810 void TokenizeWindowsCommandLine(StringRef Source, StringSaver &Saver,
1806 SmallVectorImpl &NewArgv);
1811 SmallVectorImpl &NewArgv,
1812 bool MarkEOLs = false);
18071813
18081814 /// \brief String tokenization function type. Should be compatible with either
18091815 /// Windows or Unix command line tokenizers.
18101816 typedef void (*TokenizerCallback)(StringRef Source, StringSaver &Saver,
1811 SmallVectorImpl &NewArgv);
1817 SmallVectorImpl &NewArgv,
1818 bool MarkEOLs);
18121819
18131820 /// \brief Expand response files on a command line recursively using the given
18141821 /// StringSaver and tokenization strategy. Argv should contain the command line
1815 /// before expansion and will be modified in place.
1822 /// before expansion and will be modified in place. If requested, Argv will
1823 /// also be populated with nullptrs indicating where each response file line
1824 /// ends, which is useful for the "/link" argument that needs to consume all
1825 /// remaining arguments only until the next end of line, when in a response
1826 /// file.
18161827 ///
18171828 /// \param [in] Saver Delegates back to the caller for saving parsed strings.
18181829 /// \param [in] Tokenizer Tokenization strategy. Typically Unix or Windows.
18191830 /// \param [in,out] Argv Command line into which to expand response files.
1831 /// \param [in] MarkEOLs Mark end of lines and the end of the response file
1832 /// with nullptrs in the Argv vector.
18201833 /// \return true if all @files were expanded successfully or there were none.
18211834 bool ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer,
1822 SmallVectorImpl &Argv);
1835 SmallVectorImpl &Argv,
1836 bool MarkEOLs = false);
18231837
18241838 } // End namespace cl
18251839
263263 MissingArgIndex = MissingArgCount = 0;
264264 unsigned Index = 0, End = ArgEnd - ArgBegin;
265265 while (Index < End) {
266 // Ingore nullptrs, they are response file's EOL markers
267 if (Args->getArgString(Index) == nullptr) {
268 ++Index;
269 continue;
270 }
266271 // Ignore empty arguments (other things may still take them as arguments).
267272 StringRef Str = Args->getArgString(Index);
268273 if (Str == "") {
168168 return nullptr;
169169
170170 Index += 2;
171 if (Index > Args.getNumInputArgStrings())
171 if (Index > Args.getNumInputArgStrings() ||
172 Args.getArgString(Index - 1) == nullptr)
172173 return nullptr;
173174
174175 return new Arg(UnaliasedOption, Spelling,
199200
200201 // Otherwise it must be separate.
201202 Index += 2;
202 if (Index > Args.getNumInputArgStrings())
203 if (Index > Args.getNumInputArgStrings() ||
204 Args.getArgString(Index - 1) == nullptr)
203205 return nullptr;
204206
205207 return new Arg(UnaliasedOption, Spelling,
208210 case JoinedAndSeparateClass:
209211 // Always matches.
210212 Index += 2;
211 if (Index > Args.getNumInputArgStrings())
213 if (Index > Args.getNumInputArgStrings() ||
214 Args.getArgString(Index - 1) == nullptr)
212215 return nullptr;
213216
214217 return new Arg(UnaliasedOption, Spelling, Index - 2,
220223 if (ArgSize != strlen(Args.getArgString(Index)))
221224 return nullptr;
222225 Arg *A = new Arg(UnaliasedOption, Spelling, Index++);
223 while (Index < Args.getNumInputArgStrings())
226 while (Index < Args.getNumInputArgStrings() &&
227 Args.getArgString(Index) != nullptr)
224228 A->getValues().push_back(Args.getArgString(Index++));
225229 return A;
226230 }
473473 }
474474
475475 void cl::TokenizeGNUCommandLine(StringRef Src, StringSaver &Saver,
476 SmallVectorImpl &NewArgv) {
476 SmallVectorImpl &NewArgv,
477 bool MarkEOLs) {
477478 SmallString<128> Token;
478479 for (size_t I = 0, E = Src.size(); I != E; ++I) {
479480 // Consume runs of whitespace.
480481 if (Token.empty()) {
481 while (I != E && isWhitespace(Src[I]))
482 while (I != E && isWhitespace(Src[I])) {
483 // Mark the end of lines in response files
484 if (MarkEOLs && Src[I] == '\n')
485 NewArgv.push_back(nullptr);
482486 ++I;
487 }
483488 if (I == E) break;
484489 }
485490
520525 // Append the last token after hitting EOF with no whitespace.
521526 if (!Token.empty())
522527 NewArgv.push_back(Saver.SaveString(Token.c_str()));
528 // Mark the end of response files
529 if (MarkEOLs)
530 NewArgv.push_back(nullptr);
523531 }
524532
525533 /// Backslashes are interpreted in a rather complicated way in the Windows-style
561569 }
562570
563571 void cl::TokenizeWindowsCommandLine(StringRef Src, StringSaver &Saver,
564 SmallVectorImpl &NewArgv) {
572 SmallVectorImpl &NewArgv,
573 bool MarkEOLs) {
565574 SmallString<128> Token;
566575
567576 // This is a small state machine to consume characters until it reaches the
571580 // INIT state indicates that the current input index is at the start of
572581 // the string or between tokens.
573582 if (State == INIT) {
574 if (isWhitespace(Src[I]))
583 if (isWhitespace(Src[I])) {
584 // Mark the end of lines in response files
585 if (MarkEOLs && Src[I] == '\n')
586 NewArgv.push_back(nullptr);
575587 continue;
588 }
576589 if (Src[I] == '"') {
577590 State = QUOTED;
578591 continue;
595608 NewArgv.push_back(Saver.SaveString(Token.c_str()));
596609 Token.clear();
597610 State = INIT;
611 // Mark the end of lines in response files
612 if (MarkEOLs && Src[I] == '\n')
613 NewArgv.push_back(nullptr);
598614 continue;
599615 }
600616 if (Src[I] == '"') {
625641 // Append the last token after hitting EOF with no whitespace.
626642 if (!Token.empty())
627643 NewArgv.push_back(Saver.SaveString(Token.c_str()));
644 // Mark the end of response files
645 if (MarkEOLs)
646 NewArgv.push_back(nullptr);
628647 }
629648
630649 static bool ExpandResponseFile(const char *FName, StringSaver &Saver,
631650 TokenizerCallback Tokenizer,
632 SmallVectorImpl &NewArgv) {
651 SmallVectorImpl &NewArgv,
652 bool MarkEOLs = false) {
633653 ErrorOr> MemBufOrErr =
634654 MemoryBuffer::getFile(FName);
635655 if (!MemBufOrErr)
647667 }
648668
649669 // Tokenize the contents into NewArgv.
650 Tokenizer(Str, Saver, NewArgv);
670 Tokenizer(Str, Saver, NewArgv, MarkEOLs);
651671
652672 return true;
653673 }
655675 /// \brief Expand response files on a command line recursively using the given
656676 /// StringSaver and tokenization strategy.
657677 bool cl::ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer,
658 SmallVectorImpl &Argv) {
678 SmallVectorImpl &Argv,
679 bool MarkEOLs) {
659680 unsigned RspFiles = 0;
660681 bool AllExpanded = true;
661682
662683 // Don't cache Argv.size() because it can change.
663684 for (unsigned I = 0; I != Argv.size(); ) {
664685 const char *Arg = Argv[I];
686 // Check if it is an EOL marker
687 if (Arg == nullptr) {
688 ++I;
689 continue;
690 }
665691 if (Arg[0] != '@') {
666692 ++I;
667693 continue;
677703 // FIXME: If a nested response file uses a relative path, is it relative to
678704 // the cwd of the process or the response file?
679705 SmallVector ExpandedArgv;
680 if (!ExpandResponseFile(Arg + 1, Saver, Tokenizer, ExpandedArgv)) {
706 if (!ExpandResponseFile(Arg + 1, Saver, Tokenizer, ExpandedArgv,
707 MarkEOLs)) {
681708 // We couldn't read this file, so we leave it in the argument stream and
682709 // move on.
683710 AllExpanded = false;
152152 };
153153
154154 typedef void ParserFunction(StringRef Source, llvm::cl::StringSaver &Saver,
155 SmallVectorImpl &NewArgv);
156
155 SmallVectorImpl &NewArgv,
156 bool MarkEOLs);
157157
158158 void testCommandLineTokenizer(ParserFunction *parse, const char *Input,
159159 const char *const Output[], size_t OutputSize) {
160160 SmallVector Actual;
161161 StrDupSaver Saver;
162 parse(Input, Saver, Actual);
162 parse(Input, Saver, Actual, /*MarkEOLs=*/false);
163163 EXPECT_EQ(OutputSize, Actual.size());
164164 for (unsigned I = 0, E = Actual.size(); I != E; ++I) {
165165 if (I < OutputSize)