Skip to content

Commit 8dcc3e0

Browse files
allevatojrose-apple
authored andcommitted
Merge pull request #24008 from allevato/batch-mode-response-files (#24829)
Fix response file support for batch jobs.
1 parent 0fb1a50 commit 8dcc3e0

File tree

6 files changed

+98
-51
lines changed

6 files changed

+98
-51
lines changed

include/swift/Driver/Job.h

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,20 @@ class Job {
244244
NewlyAdded
245245
};
246246

247+
/// Packs together information about response file usage for a job.
248+
///
249+
/// The strings in this struct must be kept alive as long as the Job is alive
250+
/// (e.g., by calling MakeArgString on the arg list associated with the
251+
/// Compilation).
252+
struct ResponseFileInfo {
253+
/// The path to the response file that a job should use.
254+
const char *path;
255+
256+
/// The '@'-prefixed argument string that should be passed to the tool to
257+
/// use the response file.
258+
const char *argString;
259+
};
260+
247261
using EnvironmentVector = std::vector<std::pair<const char *, const char *>>;
248262

249263
/// If positive, contains llvm::ProcessID for a real Job on the host OS. If
@@ -279,34 +293,25 @@ class Job {
279293
/// Whether the job wants a list of input or output files created.
280294
std::vector<FilelistInfo> FilelistFileInfos;
281295

282-
/// Response file path
283-
const char *ResponseFilePath;
284-
285-
/// This contains a single argument pointing to the response file path with
286-
/// the '@' prefix.
287-
/// The argument string must be kept alive as long as the Job is alive.
288-
const char *ResponseFileArg;
296+
/// The path and argument string to use for the response file if the job's
297+
/// arguments should be passed using one.
298+
Optional<ResponseFileInfo> ResponseFile;
289299

290300
/// The modification time of the main input file, if any.
291301
llvm::sys::TimePoint<> InputModTime = llvm::sys::TimePoint<>::max();
292302

293303
public:
294-
Job(const JobAction &Source,
295-
SmallVectorImpl<const Job *> &&Inputs,
296-
std::unique_ptr<CommandOutput> Output,
297-
const char *Executable,
304+
Job(const JobAction &Source, SmallVectorImpl<const Job *> &&Inputs,
305+
std::unique_ptr<CommandOutput> Output, const char *Executable,
298306
llvm::opt::ArgStringList Arguments,
299307
EnvironmentVector ExtraEnvironment = {},
300308
std::vector<FilelistInfo> Infos = {},
301-
const char *ResponseFilePath = nullptr,
302-
const char *ResponseFileArg = nullptr)
309+
Optional<ResponseFileInfo> ResponseFile = None)
303310
: SourceAndCondition(&Source, Condition::Always),
304311
Inputs(std::move(Inputs)), Output(std::move(Output)),
305312
Executable(Executable), Arguments(std::move(Arguments)),
306313
ExtraEnvironment(std::move(ExtraEnvironment)),
307-
FilelistFileInfos(std::move(Infos)),
308-
ResponseFilePath(ResponseFilePath),
309-
ResponseFileArg(ResponseFileArg) {}
314+
FilelistFileInfos(std::move(Infos)), ResponseFile(ResponseFile) {}
310315

311316
virtual ~Job();
312317

@@ -316,7 +321,10 @@ class Job {
316321

317322
const char *getExecutable() const { return Executable; }
318323
const llvm::opt::ArgStringList &getArguments() const { return Arguments; }
319-
ArrayRef<const char *> getResponseFileArg() const { return ResponseFileArg; }
324+
ArrayRef<const char *> getResponseFileArg() const {
325+
assert(hasResponseFile());
326+
return ResponseFile->argString;
327+
}
320328
ArrayRef<FilelistInfo> getFilelistInfos() const { return FilelistFileInfos; }
321329
ArrayRef<const char *> getArgumentsForTaskExecution() const;
322330

@@ -370,7 +378,7 @@ class Job {
370378
static void printArguments(raw_ostream &Stream,
371379
const llvm::opt::ArgStringList &Args);
372380

373-
bool hasResponseFile() const { return ResponseFilePath != nullptr; }
381+
bool hasResponseFile() const { return ResponseFile.hasValue(); }
374382

375383
bool writeArgsToResponseFile() const;
376384
};
@@ -398,9 +406,9 @@ class BatchJob : public Job {
398406
BatchJob(const JobAction &Source, SmallVectorImpl<const Job *> &&Inputs,
399407
std::unique_ptr<CommandOutput> Output, const char *Executable,
400408
llvm::opt::ArgStringList Arguments,
401-
EnvironmentVector ExtraEnvironment,
402-
std::vector<FilelistInfo> Infos,
403-
ArrayRef<const Job *> Combined, Job::PID &NextQuasiPID);
409+
EnvironmentVector ExtraEnvironment, std::vector<FilelistInfo> Infos,
410+
ArrayRef<const Job *> Combined, Job::PID &NextQuasiPID,
411+
Optional<ResponseFileInfo> ResponseFile = None);
404412

405413
ArrayRef<const Job*> getCombinedJobs() const {
406414
return CombinedJobs;

include/swift/Driver/ToolChain.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,14 @@ class ToolChain {
209209
/// set to match the behavior of Clang.
210210
virtual bool shouldStoreInvocationInDebugInfo() const { return false; }
211211

212+
/// Gets the response file path and command line argument for an invocation
213+
/// if the tool supports response files and if the command line length would
214+
/// exceed system limits.
215+
Optional<Job::ResponseFileInfo>
216+
getResponseFileInfo(const Compilation &C, const char *executablePath,
217+
const InvocationInfo &invocationInfo,
218+
const JobContext &context) const;
219+
212220
public:
213221
virtual ~ToolChain() = default;
214222

lib/Driver/Job.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ void Job::printCommandLine(raw_ostream &os, StringRef Terminator) const {
363363
escapeAndPrintString(os, Executable);
364364
os << ' ';
365365
if (hasResponseFile()) {
366-
printArguments(os, {ResponseFileArg});
366+
printArguments(os, {ResponseFile->argString});
367367
os << " # ";
368368
}
369369
printArguments(os, Arguments);
@@ -419,8 +419,9 @@ void Job::printSummary(raw_ostream &os) const {
419419
}
420420

421421
bool Job::writeArgsToResponseFile() const {
422+
assert(hasResponseFile());
422423
std::error_code EC;
423-
llvm::raw_fd_ostream OS(ResponseFilePath, EC, llvm::sys::fs::F_None);
424+
llvm::raw_fd_ostream OS(ResponseFile->path, EC, llvm::sys::fs::F_None);
424425
if (EC) {
425426
return true;
426427
}
@@ -438,9 +439,10 @@ BatchJob::BatchJob(const JobAction &Source,
438439
const char *Executable, llvm::opt::ArgStringList Arguments,
439440
EnvironmentVector ExtraEnvironment,
440441
std::vector<FilelistInfo> Infos,
441-
ArrayRef<const Job *> Combined, int64_t &NextQuasiPID)
442+
ArrayRef<const Job *> Combined, int64_t &NextQuasiPID,
443+
Optional<ResponseFileInfo> ResponseFile)
442444
: Job(Source, std::move(Inputs), std::move(Output), Executable, Arguments,
443-
ExtraEnvironment, Infos),
445+
ExtraEnvironment, Infos, ResponseFile),
444446
CombinedJobs(Combined.begin(), Combined.end()),
445447
QuasiPIDBase(NextQuasiPID) {
446448

lib/Driver/ToolChain.cpp

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,27 @@ ToolChain::JobContext::getTemporaryFilePath(const llvm::Twine &name,
6262
return C.getArgs().MakeArgString(buffer.str());
6363
}
6464

65+
Optional<Job::ResponseFileInfo>
66+
ToolChain::getResponseFileInfo(const Compilation &C, const char *executablePath,
67+
const ToolChain::InvocationInfo &invocationInfo,
68+
const ToolChain::JobContext &context) const {
69+
const bool forceResponseFiles =
70+
C.getArgs().hasArg(options::OPT_driver_force_response_files);
71+
assert((invocationInfo.allowsResponseFiles || !forceResponseFiles) &&
72+
"Cannot force response file if platform does not allow it");
73+
74+
if (forceResponseFiles || (invocationInfo.allowsResponseFiles &&
75+
!llvm::sys::commandLineFitsWithinSystemLimits(
76+
executablePath, invocationInfo.Arguments))) {
77+
const char *responseFilePath =
78+
context.getTemporaryFilePath("arguments", "resp");
79+
const char *responseFileArg =
80+
C.getArgs().MakeArgString(Twine("@") + responseFilePath);
81+
return {{responseFilePath, responseFileArg}};
82+
}
83+
return None;
84+
}
85+
6586
std::unique_ptr<Job> ToolChain::constructJob(
6687
const JobAction &JA, Compilation &C, SmallVectorImpl<const Job *> &&inputs,
6788
ArrayRef<const Action *> inputActions,
@@ -114,28 +135,16 @@ std::unique_ptr<Job> ToolChain::constructJob(
114135
}
115136
}
116137

117-
const char *responseFilePath = nullptr;
118-
const char *responseFileArg = nullptr;
119-
120-
const bool forceResponseFiles =
121-
C.getArgs().hasArg(options::OPT_driver_force_response_files);
122-
assert((invocationInfo.allowsResponseFiles || !forceResponseFiles) &&
123-
"Cannot force response file if platform does not allow it");
124-
125-
if (forceResponseFiles || (invocationInfo.allowsResponseFiles &&
126-
!llvm::sys::commandLineFitsWithinSystemLimits(
127-
executablePath, invocationInfo.Arguments))) {
128-
responseFilePath = context.getTemporaryFilePath("arguments", "resp");
129-
responseFileArg = C.getArgs().MakeArgString(Twine("@") + responseFilePath);
130-
}
138+
// Determine if the argument list is so long that it needs to be written into
139+
// a response file.
140+
auto responseFileInfo =
141+
getResponseFileInfo(C, executablePath, invocationInfo, context);
131142

132-
return llvm::make_unique<Job>(JA, std::move(inputs), std::move(output),
133-
executablePath,
134-
std::move(invocationInfo.Arguments),
135-
std::move(invocationInfo.ExtraEnvironment),
136-
std::move(invocationInfo.FilelistInfos),
137-
responseFilePath,
138-
responseFileArg);
143+
return llvm::make_unique<Job>(
144+
JA, std::move(inputs), std::move(output), executablePath,
145+
std::move(invocationInfo.Arguments),
146+
std::move(invocationInfo.ExtraEnvironment),
147+
std::move(invocationInfo.FilelistInfos), responseFileInfo);
139148
}
140149

141150
std::string
@@ -345,20 +354,26 @@ ToolChain::constructBatchJob(ArrayRef<const Job *> unsortedJobs,
345354
*output, OI};
346355
auto invocationInfo = constructInvocation(*batchCJA, context);
347356
// Batch mode can produce quite long command lines; in almost every case these
348-
// will trigger use of supplementary output file maps, but in some rare corner
349-
// cases (very few files, very long paths) they might not. However, in those
350-
// cases we _should_ degrade to using response files to pass arguments to the
351-
// frontend, which is done automatically by code elsewhere.
357+
// will trigger use of supplementary output file maps. However, if the driver
358+
// command line is long for reasons unrelated to the number of input files,
359+
// such as passing a large number of flags, then the individual batch jobs are
360+
// also likely to overflow. We have to check for that explicitly here, because
361+
// the BatchJob created here does not go through the same code path in
362+
// constructJob above.
352363
//
353364
// The `allowsResponseFiles` flag on the `invocationInfo` we have here exists
354365
// only to model external tools that don't know about response files, such as
355366
// platform linkers; when talking to the frontend (which we control!) it
356367
// should always be true. But double check with an assert here in case someone
357368
// failed to set it in `constructInvocation`.
358369
assert(invocationInfo.allowsResponseFiles);
370+
auto responseFileInfo =
371+
getResponseFileInfo(C, executablePath, invocationInfo, context);
372+
359373
return llvm::make_unique<BatchJob>(
360374
*batchCJA, inputJobs.takeVector(), std::move(output), executablePath,
361375
std::move(invocationInfo.Arguments),
362376
std::move(invocationInfo.ExtraEnvironment),
363-
std::move(invocationInfo.FilelistInfos), sortedJobs, NextQuasiPID);
377+
std::move(invocationInfo.FilelistInfos), sortedJobs, NextQuasiPID,
378+
responseFileInfo);
364379
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Batch jobs go through a different code path than other jobs, so make sure
2+
// that they also use response files correctly when their argument lists are
3+
// too long.
4+
5+
// RUN: %{python} -c 'for i in range(500001): print "-DTEST_" + str(i)' > %t.resp
6+
// RUN: %swiftc_driver -driver-print-jobs -module-name batch -enable-batch-mode -j 1 -c %S/Inputs/main.swift %S/Inputs/lib.swift @%t.resp 2>&1 > %t.jobs.txt
7+
// RUN: %FileCheck %s < %t.jobs.txt -check-prefix=BATCH
8+
9+
// BATCH: bin{{/|\\\\}}swift{{c?}}
10+
// BATCH: @{{[^ ]*}}arguments-{{[0-9a-zA-Z]+}}.resp{{"?}} # -frontend -c -primary-file {{[^ ]+}}/Inputs/main.swift{{"?}} -primary-file {{[^ ]+}}/Inputs/lib.swift

test/Driver/force-response-files.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,7 @@
44
// RUN: %swiftc_driver -driver-force-response-files -typecheck %S/../Inputs/empty.swift -### 2>&1 | %FileCheck %s
55
// CHECK: @
66
// CHECK: .resp
7+
8+
// RUN: %swiftc_driver -enable-batch-mode -driver-force-response-files -typecheck %S/../Inputs/empty.swift -### 2>&1 | %FileCheck %s -check-prefix=BATCH
9+
// BATCH: @
10+
// BATCH: .resp

0 commit comments

Comments
 (0)