-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Wrap Command Line Arguments in a Response File if System Limits are Exceeded #16362
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
5976901
da19625
2297f75
2b5e3c7
62813f4
fe4f21d
2a30e25
3fe62b9
3b38fa6
6592b42
8e6abee
e65cb23
d71373c
f6b5cf5
9d51350
d6ec8fa
be229fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -271,6 +271,14 @@ class Job { | |
/// Whether the job wants a list of input or output files created. | ||
std::vector<FilelistInfo> FilelistFileInfos; | ||
|
||
/// Response file path | ||
const char *ResponseFilePath; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A bit of a nitpick, but I would prefer it with another "const" after the star ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still like the const after the star here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I count about 100 lines of code in methods of that class. If I had to maintain this code and needed to know if that instance variable ever changed, I would have to read them. Adding the "cost" would save me that work. Therefore, I would not approve this PR without this "const". (Of course you don't need my approval to land it & that's OK. I would just feel derelict in my duty to favor consistency over maintainability in this case.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Meta: At this point you've expressed a strong enough opinion that Austin does need your approval to land the PR. If you want to rescind that, you'd have to signify that, perhaps by "dismissing" your X review. Personally, I'm with Austin on this one. Being inconsistent can imply that the inconsistency is deliberate, and therefore it is better to be consistent with existing code in a PR that's adding functionality. (That's especially true for someone who's a relative newcomer.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Meta: @jrose-apple Please help me out here. Since the merge box is green, I figured he did not need my approval to land the PR. (I seem to recall reading verbiage to that effect on github, too.) Is there some policy (or implicit understanding) that I've missed? Where are you thinking that I express such a strong enough opinion? Was the need expressed, or implied? I don't understand. You have a fair point about inconsistency, but I weigh that point against the benefits of this particular proposed inconsistency. In this case I would argue (on your side) that if the const is added to one variable, a future reader might infer that the other variables are not const. However, the counter argument would be that it would be clear for anyone with a passing familiarity with the code base that const is often not used when it could be. Thus, it seems to me that the consistency benefit is outweighed by the not-having-to-read-100-lines-of-code benefit of the const. My experience as a novitiate to this code base has been that such consts would save me significant time. Ironically, it was my experience with adopting Swift for my code that convinced me of the significance of the benefits of write-once variables. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We aren't using GitHub's built-in gating for reviewers, true. But only a little above the green merge button is the part where "@davidungar requested changes", with a red X. Even without that, though, I think we want to take expressed concerns seriously unless the reviewer explicitly says something like "this is fine as is (but I think it could be better in these ways)". |
||
|
||
/// Argument vector containing a single argument pointing to the response file | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At this point, I think the comment is inaccurate. It's not an argument vector. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that property is no longer a |
||
/// path with the '@' prefix. | ||
/// The argument string must be kept alive as long as the Job is alive. | ||
const char *ResponseFileArg; | ||
|
||
/// The modification time of the main input file, if any. | ||
llvm::sys::TimePoint<> InputModTime = llvm::sys::TimePoint<>::max(); | ||
|
||
|
@@ -281,12 +289,16 @@ class Job { | |
const char *Executable, | ||
llvm::opt::ArgStringList Arguments, | ||
EnvironmentVector ExtraEnvironment = {}, | ||
std::vector<FilelistInfo> Infos = {}) | ||
std::vector<FilelistInfo> Infos = {}, | ||
const char *ResponseFilePath = nullptr, | ||
const char *ResponseFileArg = nullptr) | ||
: SourceAndCondition(&Source, Condition::Always), | ||
Inputs(std::move(Inputs)), Output(std::move(Output)), | ||
Executable(Executable), Arguments(std::move(Arguments)), | ||
ExtraEnvironment(std::move(ExtraEnvironment)), | ||
FilelistFileInfos(std::move(Infos)) {} | ||
FilelistFileInfos(std::move(Infos)), | ||
ResponseFilePath(ResponseFilePath), | ||
ResponseFileArg(ResponseFileArg) {} | ||
|
||
virtual ~Job(); | ||
|
||
|
@@ -296,6 +308,7 @@ class Job { | |
|
||
const char *getExecutable() const { return Executable; } | ||
const llvm::opt::ArgStringList &getArguments() const { return Arguments; } | ||
const char * const &getResponseFileArg() const { return ResponseFileArg; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is a reference involved here? Why not just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Returning a pointer wouldn't actually break any of that, but I guess using a const reference for a value that won't change signifies that the lifetime is tied to something else a little more strongly? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We did try just returning a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Returning a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's horrible; nice work. Can you add that as a comment so that a later refactorer like me doesn't try to change it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternately I suppose you could return an ArrayRef here even though the backing store is the one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I think this version is okay. Though I personally would have kept using the accessor methods in the other function. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I went ahead and replaced them. |
||
ArrayRef<FilelistInfo> getFilelistInfos() const { return FilelistFileInfos; } | ||
|
||
ArrayRef<const Job *> getInputs() const { return Inputs; } | ||
|
@@ -347,6 +360,10 @@ class Job { | |
|
||
static void printArguments(raw_ostream &Stream, | ||
const llvm::opt::ArgStringList &Args); | ||
|
||
bool hasResponseFile() const { return ResponseFilePath != nullptr; } | ||
|
||
bool writeArgsToResponseFile() const; | ||
}; | ||
|
||
/// A BatchJob comprises a _set_ of jobs, each of which is sufficiently similar | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -122,6 +122,11 @@ class ToolChain { | |
std::vector<std::pair<const char *, const char *>> ExtraEnvironment; | ||
std::vector<FilelistInfo> FilelistInfos; | ||
|
||
// Not all platforms and jobs support the use of response files, so assume | ||
// "false" by default. This can be overridden as needed on a per | ||
// platform/job basis. | ||
bool allowsResponseFiles = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this variable needs a comment explaining why it would ever be true or false. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not just a per-platform decision, so it's not that, but I agree that a comment would be nice. |
||
|
||
InvocationInfo(const char *name, llvm::opt::ArgStringList args = {}, | ||
decltype(ExtraEnvironment) extraEnv = {}) | ||
: ExecutableName(name), Arguments(std::move(args)), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -291,8 +291,14 @@ namespace driver { | |
"not implemented for compilations with multiple jobs"); | ||
if (Comp.ShowJobLifecycle) | ||
llvm::outs() << "Added to TaskQueue: " << LogJob(Cmd) << "\n"; | ||
TQ->addTask(Cmd->getExecutable(), Cmd->getArguments(), llvm::None, | ||
(void *)Cmd); | ||
if (Cmd->hasResponseFile()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer a style that puts the shorter arm of the if-then-else first, even though it is the do-less arm. I feel that such a choice reduces the max depth of the reader's mental stack. But this is just a suggestion. |
||
Cmd->writeArgsToResponseFile(); | ||
TQ->addTask(Cmd->getExecutable(), Cmd->getResponseFileArg(), | ||
llvm::None, (void *)Cmd); | ||
} else { | ||
TQ->addTask(Cmd->getExecutable(), Cmd->getArguments(), llvm::None, | ||
(void *)Cmd); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In fact, as I read further down, I noticed I had to stare at the two There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair point, but I still want the factoring. How about defining a new function, called |
||
} | ||
|
||
/// When a task finishes, check other Jobs that may be blocked. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
#include "llvm/ADT/STLExtras.h" | ||
#include "llvm/Option/Arg.h" | ||
#include "llvm/Support/Compiler.h" | ||
#include "llvm/Support/FileSystem.h" | ||
#include "llvm/Support/Path.h" | ||
#include "llvm/Support/Program.h" | ||
#include "llvm/Support/raw_ostream.h" | ||
|
@@ -352,7 +353,12 @@ void Job::printCommandLineAndEnvironment(raw_ostream &Stream, | |
void Job::printCommandLine(raw_ostream &os, StringRef Terminator) const { | ||
escapeAndPrintString(os, Executable); | ||
os << ' '; | ||
if (hasResponseFile()) { | ||
printArguments(os, {ResponseFileArg}); | ||
os << " # "; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the above is right, then I think it should be: |
||
printArguments(os, Arguments); | ||
|
||
os << Terminator; | ||
} | ||
|
||
|
@@ -403,6 +409,21 @@ void Job::printSummary(raw_ostream &os) const { | |
os << "}"; | ||
} | ||
|
||
bool Job::writeArgsToResponseFile() const { | ||
std::error_code EC; | ||
llvm::raw_fd_ostream OS(ResponseFilePath, EC, llvm::sys::fs::F_None); | ||
if (EC) { | ||
return true; | ||
} | ||
for (const char *arg : Arguments) { | ||
OS << "\""; | ||
escapeAndPrintString(OS, arg); | ||
OS << "\" "; | ||
} | ||
OS.flush(); | ||
return false; | ||
} | ||
|
||
BatchJob::BatchJob(const JobAction &Source, | ||
SmallVectorImpl<const Job *> &&Inputs, | ||
std::unique_ptr<CommandOutput> Output, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -247,6 +247,7 @@ ToolChain::constructInvocation(const CompileJobAction &job, | |
const JobContext &context) const { | ||
InvocationInfo II{SWIFT_EXECUTABLE_NAME}; | ||
ArgStringList &Arguments = II.Arguments; | ||
II.allowsResponseFiles = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is it true in this case? Comment? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I remain puzzled by that line. Please add a comment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Never mind, see below. |
||
|
||
Arguments.push_back("-frontend"); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -325,7 +325,10 @@ toolchains::GenericUnix::constructInvocation(const LinkJobAction &job, | |
Arguments.push_back( | ||
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename())); | ||
|
||
return {Clang, Arguments}; | ||
InvocationInfo II{Clang, Arguments}; | ||
II.allowsResponseFiles = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why true? Comment? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there are a bunch of similar lines, could you please factor them into an intention-revealing method? Or add a comment to each? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Never mind, see below. |
||
|
||
return II; | ||
} | ||
|
||
std::string toolchains::Android::getTargetForLinker() const { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -168,5 +168,8 @@ toolchains::Windows::constructInvocation(const LinkJobAction &job, | |
Arguments.push_back( | ||
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename())); | ||
|
||
return {Clang, Arguments}; | ||
InvocationInfo II{Clang, Arguments}; | ||
II.allowsResponseFiles = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, this is the third time I'm seeing the setting to true. Is there something in common that the code should be reifying? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope. It's all based on what other tool is getting invoked, which is not necessarily the same across each toolchain and certainly not the same across each job. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is, this is the customization point, so that the rest of the code can treat everything the same. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, but how about comments explaining why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comment would read: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am still with Austin on this. If the executable specified in the InvocationInfo constructor supports response files, you can put There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I updated the comment in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for updating the comment. In thinking about this item, the comment you added is good and it is sufficient. I'm not asking for the following, but if I were to try to make it even clearer, I would use a one-line intention-revealing function. But, never mind. |
||
|
||
return II; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -111,8 +111,6 @@ static bool shouldRunAsSubcommand(StringRef ExecName, | |
extern int apinotes_main(ArrayRef<const char *> Args); | ||
|
||
int main(int argc_, const char **argv_) { | ||
PROGRAM_START(argc_, argv_); | ||
|
||
SmallVector<const char *, 256> argv; | ||
llvm::SpecificBumpPtrAllocator<char> ArgAllocator; | ||
std::error_code EC = llvm::sys::Process::GetArgumentVector( | ||
|
@@ -134,6 +132,10 @@ int main(int argc_, const char **argv_) { | |
llvm::cl::TokenizeGNUCommandLine, | ||
argv); | ||
|
||
// Initialize the stack trace using the parsed argument vector with expanded | ||
// response files. | ||
PROGRAM_START(argv.size(), argv.data()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like this can be affected by subsequent operations on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm. Maybe we can do it the other way, and make sure that no one else changes argv after this point? The case where that does happen, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To make sure I understand your suggestion, the signature for
I'm not sure if there is a stronger way to ensure that the argument vector doesn't get modified, aside from:
Unfortunately, I don't think we can just make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I didn't realize that
What do you think for that? (Sorry for the delayed responses. The run-up to WWDC has been slamming a bunch of us!) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jrose-apple About WWDC: no worries! I attempted to implement your suggestion, and created a commit for it. It was necessary to factor out the lower half of |
||
|
||
// Check if this invocation should execute a subcommand. | ||
StringRef ExecName = llvm::sys::path::stem(argv[0]); | ||
SmallString<256> SubcommandName; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would remove this comment. It is completely redundant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the point of this comment? @jrose-apple Is there some coding standard I don't know about? Why not remove it entirely?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This purpose of the comment is to remain consistent with the rest of the file. A few lines above reads:
for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand. Since I believe that this sort of comment degrades maintainability (yes, it's a tiny cut but there are virtually thousands of them), and since I try to enhance the maintainabilty of the code with each review, I asked for its deletion. However, a consistent-but-redundant comment is only a mere bagatelle.