Skip to content

Commit 399709c

Browse files
committed
Optionally separate Tasks` stderr from stdout.
Fixes a serious problem where spurious output from xcrun breaks swift's discovery of libarclite. <rdar://problem/28573949>
1 parent 240ab32 commit 399709c

File tree

7 files changed

+122
-42
lines changed

7 files changed

+122
-42
lines changed

include/swift/Basic/TaskQueue.h

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,15 @@ class TaskQueue {
7272
/// \param ReturnCode the return code of the task which finished execution.
7373
/// \param Output the output from the task which finished execution,
7474
/// if available. (This may not be available on all platforms.)
75+
/// \param Errors the errors from the task which finished execution, if
76+
/// available and SeparateErrors was true. (This may not be available on all
77+
/// platforms.)
7578
/// \param Context the context which was passed when the task was added
7679
///
7780
/// \returns true if further execution of tasks should stop,
7881
/// false if execution should continue
7982
typedef std::function<TaskFinishedResponse(ProcessId Pid, int ReturnCode,
80-
StringRef Output, void *Context)>
83+
StringRef Output, StringRef Errors, void *Context)>
8184
TaskFinishedCallback;
8285

8386
/// \brief A callback which will be executed if a task exited abnormally due
@@ -88,12 +91,15 @@ class TaskQueue {
8891
/// no reason could be deduced, this may be empty.
8992
/// \param Output the output from the task which exited abnormally, if
9093
/// available. (This may not be available on all platforms.)
94+
/// \param Errors the errors from the task which exited abnormally, if
95+
/// available and SeparateErrors was true. (This may not be available on all
96+
/// platforms.)
9197
/// \param Context the context which was passed when the task was added
9298
///
9399
/// \returns a TaskFinishedResponse indicating whether or not execution
94100
/// should proceed
95101
typedef std::function<TaskFinishedResponse(ProcessId Pid, StringRef ErrorMsg,
96-
StringRef Output, void *Context)>
102+
StringRef Output, StringRef Errors, void *Context)>
97103
TaskSignalledCallback;
98104
#pragma clang diagnostic pop
99105

@@ -120,9 +126,10 @@ class TaskQueue {
120126
/// \param Env the environment which should be used for the task;
121127
/// must be null-terminated. If empty, inherits the parent's environment.
122128
/// \param Context an optional context which will be associated with the task
129+
/// \param SeparateErrors Controls whether error output is reported separately
123130
virtual void addTask(const char *ExecPath, ArrayRef<const char *> Args,
124131
ArrayRef<const char *> Env = llvm::None,
125-
void *Context = nullptr);
132+
void *Context = nullptr, bool SeparateErrors = false);
126133

127134
/// \brief Synchronously executes the tasks in the TaskQueue.
128135
///
@@ -153,10 +160,13 @@ class DummyTaskQueue : public TaskQueue {
153160
ArrayRef<const char *> Args;
154161
ArrayRef<const char *> Env;
155162
void *Context;
163+
bool SeparateErrors;
156164

157165
DummyTask(const char *ExecPath, ArrayRef<const char *> Args,
158-
ArrayRef<const char *> Env = llvm::None, void *Context = nullptr)
159-
: ExecPath(ExecPath), Args(Args), Env(Env), Context(Context) {}
166+
ArrayRef<const char *> Env = llvm::None, void *Context = nullptr,
167+
bool SeparateErrors = false)
168+
: ExecPath(ExecPath), Args(Args), Env(Env), Context(Context),
169+
SeparateErrors(SeparateErrors) {}
160170
};
161171

162172
std::queue<std::unique_ptr<DummyTask>> QueuedTasks;
@@ -168,7 +178,7 @@ class DummyTaskQueue : public TaskQueue {
168178

169179
virtual void addTask(const char *ExecPath, ArrayRef<const char *> Args,
170180
ArrayRef<const char *> Env = llvm::None,
171-
void *Context = nullptr);
181+
void *Context = nullptr, bool SeparateErrors = false);
172182

173183
virtual bool
174184
execute(TaskBeganCallback Began = TaskBeganCallback(),

lib/Basic/Default/TaskQueue.inc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ bool TaskQueue::execute(TaskBeganCallback Began, TaskFinishedCallback Finished,
112112
// Wait() returning a return code of -2 indicates the process received
113113
// a signal during execution.
114114
if (Signalled) {
115-
TaskFinishedResponse Response = Signalled(PI.Pid, ErrMsg, StringRef(),
116-
T->Context);
115+
TaskFinishedResponse Response =
116+
Signalled(PI.Pid, ErrMsg, StringRef(), StringRef(), T->Context);
117117
ContinueExecution = Response != TaskFinishedResponse::StopExecution;
118118
} else {
119119
// If we don't have a Signalled callback, unconditionally stop.
@@ -124,7 +124,7 @@ bool TaskQueue::execute(TaskBeganCallback Began, TaskFinishedCallback Finished,
124124
// finished.
125125
if (Finished) {
126126
TaskFinishedResponse Response = Finished(PI.Pid, PI.ReturnCode,
127-
StringRef(), T->Context);
127+
StringRef(), StringRef(), T->Context);
128128
ContinueExecution = Response != TaskFinishedResponse::StopExecution;
129129
} else if (PI.ReturnCode != 0) {
130130
ContinueExecution = false;

lib/Basic/TaskQueue.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ DummyTaskQueue::DummyTaskQueue(unsigned NumberOfParallelTasks)
4242
DummyTaskQueue::~DummyTaskQueue() = default;
4343

4444
void DummyTaskQueue::addTask(const char *ExecPath, ArrayRef<const char *> Args,
45-
ArrayRef<const char *> Env, void *Context) {
46-
QueuedTasks.emplace(
47-
std::unique_ptr<DummyTask>(new DummyTask(ExecPath, Args, Env, Context)));
45+
ArrayRef<const char *> Env, void *Context,
46+
bool SeparateErrors) {
47+
QueuedTasks.emplace(std::unique_ptr<DummyTask>(
48+
new DummyTask(ExecPath, Args, Env, Context, SeparateErrors)));
4849
}
4950

5051
bool DummyTaskQueue::execute(TaskQueue::TaskBeganCallback Began,
@@ -80,9 +81,11 @@ bool DummyTaskQueue::execute(TaskQueue::TaskBeganCallback Began,
8081

8182
if (Finished) {
8283
std::string Output = "Output placeholder\n";
83-
if (Finished(P.first, 0, Output, P.second->Context) ==
84-
TaskFinishedResponse::StopExecution)
85-
SubtaskFailed = true;
84+
std::string Errors =
85+
P.second->SeparateErrors ? "Error placeholder\n" : "";
86+
if (Finished(P.first, 0, Output, Errors, P.second->Context) ==
87+
TaskFinishedResponse::StopExecution)
88+
SubtaskFailed = true;
8689
}
8790
}
8891

lib/Basic/Unix/TaskQueue.inc

Lines changed: 80 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,18 @@ class Task {
5858
/// Context which should be associated with this task.
5959
void *Context;
6060

61+
/// True if the errors of the Task should be stored in Errors instead of Output.
62+
bool SeparateErrors;
63+
6164
/// The pid of this Task when executing.
6265
pid_t Pid;
6366

6467
/// A pipe for reading output from the child process.
6568
int Pipe;
6669

70+
/// A pipe for reading errors from the child prcess, if SeparateErrors is true.
71+
int ErrorPipe;
72+
6773
/// The current state of the Task.
6874
enum {
6975
Preparing,
@@ -74,29 +80,36 @@ class Task {
7480
/// Once the Task has finished, this contains the buffered output of the Task.
7581
std::string Output;
7682

83+
/// Once the Task has finished, if SeparateErrors is true, this contains the errors
84+
/// from the Task.
85+
std::string Errors;
86+
7787
public:
7888
Task(const char *ExecPath, ArrayRef<const char *> Args,
79-
ArrayRef<const char *> Env, void *Context)
89+
ArrayRef<const char *> Env, void *Context, bool SeparateErrors)
8090
: ExecPath(ExecPath), Args(Args), Env(Env), Context(Context),
81-
Pid(-1), Pipe(-1), State(Preparing) {
91+
SeparateErrors(SeparateErrors), Pid(-1), Pipe(-1), ErrorPipe(-1),
92+
State(Preparing) {
8293
assert((Env.empty() || Env.back() == nullptr) &&
8394
"Env must either be empty or null-terminated!");
8495
}
8596

8697
const char *getExecPath() const { return ExecPath; }
8798
ArrayRef<const char *> getArgs() const { return Args; }
8899
StringRef getOutput() const { return Output; }
100+
StringRef getErrors() const { return Errors; }
89101
void *getContext() const { return Context; }
90102
pid_t getPid() const { return Pid; }
91103
int getPipe() const { return Pipe; }
104+
int getErrorPipe() const { return ErrorPipe; }
92105

93106
/// \brief Begins execution of this Task.
94107
/// \returns true on error, false on success
95108
bool execute();
96109

97-
/// \brief Reads data from the pipe, if any is available.
110+
/// \brief Reads data from the pipes, if any is available.
98111
/// \returns true on error, false on success
99-
bool readFromPipe();
112+
bool readFromPipes();
100113

101114
/// \brief Performs any post-execution work for this Task, such as reading
102115
/// piped output and closing the pipe.
@@ -121,6 +134,12 @@ bool Task::execute() {
121134
pipe(FullPipe);
122135
Pipe = FullPipe[0];
123136

137+
int FullErrorPipe[2];
138+
if (SeparateErrors) {
139+
pipe(FullErrorPipe);
140+
ErrorPipe = FullErrorPipe[0];
141+
}
142+
124143
// Get the environment to pass down to the subtask.
125144
const char *const *envp = Env.empty() ? nullptr : Env.data();
126145
if (!envp) {
@@ -138,8 +157,19 @@ bool Task::execute() {
138157
posix_spawn_file_actions_init(&FileActions);
139158

140159
posix_spawn_file_actions_adddup2(&FileActions, FullPipe[1], STDOUT_FILENO);
141-
posix_spawn_file_actions_adddup2(&FileActions, STDOUT_FILENO, STDERR_FILENO);
160+
161+
if (SeparateErrors) {
162+
posix_spawn_file_actions_adddup2(&FileActions, FullErrorPipe[1],
163+
STDERR_FILENO);
164+
} else {
165+
posix_spawn_file_actions_adddup2(&FileActions, STDOUT_FILENO,
166+
STDERR_FILENO);
167+
}
168+
142169
posix_spawn_file_actions_addclose(&FileActions, FullPipe[0]);
170+
if (SeparateErrors) {
171+
posix_spawn_file_actions_addclose(&FileActions, FullErrorPipe[0]);
172+
}
143173

144174
// Spawn the subtask.
145175
int spawnErr = posix_spawn(&Pid, ExecPath, &FileActions, nullptr,
@@ -148,9 +178,15 @@ bool Task::execute() {
148178

149179
posix_spawn_file_actions_destroy(&FileActions);
150180
close(FullPipe[1]);
181+
if (SeparateErrors) {
182+
close(FullErrorPipe[1]);
183+
}
151184

152185
if (spawnErr != 0 || Pid == 0) {
153186
close(FullPipe[0]);
187+
if (SeparateErrors) {
188+
close(FullErrorPipe[0]);
189+
}
154190
State = Finished;
155191
return true;
156192
}
@@ -159,15 +195,25 @@ bool Task::execute() {
159195
switch (Pid) {
160196
case -1: {
161197
close(FullPipe[0]);
198+
if (SeparateErrors) {
199+
close(FullErrorPipe[0]);
200+
}
162201
State = Finished;
163202
Pid = 0;
164203
break;
165204
}
166205
case 0: {
167206
// Child process: Execute the program.
168207
dup2(FullPipe[1], STDOUT_FILENO);
169-
dup2(STDOUT_FILENO, STDERR_FILENO);
208+
if (SeparateErrors) {
209+
dup2(FullErrorPipe[1], STDERR_FILENO);
210+
} else {
211+
dup2(STDOUT_FILENO, STDERR_FILENO);
212+
}
170213
close(FullPipe[0]);
214+
if (SeparateErrors) {
215+
close(FullErrorPipe[0]);
216+
}
171217
execve(ExecPath, const_cast<char **>(argvp), const_cast<char **>(envp));
172218

173219
// If the execve() failed, we should exit. Follow Unix protocol and
@@ -184,6 +230,9 @@ bool Task::execute() {
184230
}
185231

186232
close(FullPipe[1]);
233+
if (SeparateErrors) {
234+
close(FullErrorPipe[1]);
235+
}
187236

188237
if (Pid == 0)
189238
return true;
@@ -192,7 +241,7 @@ bool Task::execute() {
192241
return false;
193242
}
194243

195-
bool Task::readFromPipe() {
244+
static bool readFromAPipe(int Pipe, std::string &Output) {
196245
char outputBuffer[1024];
197246
ssize_t readBytes = 0;
198247
while ((readBytes = read(Pipe, outputBuffer, sizeof(outputBuffer))) != 0) {
@@ -209,16 +258,27 @@ bool Task::readFromPipe() {
209258
return false;
210259
}
211260

261+
bool Task::readFromPipes() {
262+
bool Ret = readFromAPipe(Pipe, Output);
263+
if (SeparateErrors) {
264+
Ret |= readFromAPipe(ErrorPipe, Errors);
265+
}
266+
return Ret;
267+
}
268+
212269
void Task::finishExecution() {
213270
assert(State == Executing &&
214271
"This Task must be executing to finish execution!");
215272

216273
State = Finished;
217274

218275
// Read the output of the command, so we can use it later.
219-
readFromPipe();
276+
readFromPipes();
220277

221278
close(Pipe);
279+
if (SeparateErrors) {
280+
close(ErrorPipe);
281+
}
222282
}
223283

224284
bool TaskQueue::supportsBufferingOutput() {
@@ -239,8 +299,10 @@ unsigned TaskQueue::getNumberOfParallelTasks() const {
239299
}
240300

241301
void TaskQueue::addTask(const char *ExecPath, ArrayRef<const char *> Args,
242-
ArrayRef<const char *> Env, void *Context) {
243-
std::unique_ptr<Task> T(new Task(ExecPath, Args, Env, Context));
302+
ArrayRef<const char *> Env, void *Context,
303+
bool SeparateErrors) {
304+
std::unique_ptr<Task> T(
305+
new Task(ExecPath, Args, Env, Context, SeparateErrors));
244306
QueuedTasks.push(std::move(T));
245307
}
246308

@@ -279,6 +341,8 @@ bool TaskQueue::execute(TaskBeganCallback Began, TaskFinishedCallback Finished,
279341
}
280342

281343
PollFds.push_back({ T->getPipe(), POLLIN | POLLPRI | POLLHUP, 0 });
344+
// We should also poll T->getErrorPipe(), but this intrroduces timing
345+
// issues with shutting down the task after reading getPipe().
282346
ExecutingTasks[Pid] = std::move(T);
283347
}
284348

@@ -299,7 +363,7 @@ bool TaskQueue::execute(TaskBeganCallback Began, TaskFinishedCallback Finished,
299363
if (fd.revents & POLLIN || fd.revents & POLLPRI || fd.revents & POLLHUP ||
300364
fd.revents & POLLERR) {
301365
// An event which we care about occurred. Find the appropriate Task.
302-
auto predicate = [&fd] (PidToTaskMap::value_type &value) -> bool {
366+
auto predicate = [&fd](PidToTaskMap::value_type &value) -> bool {
303367
return value.second->getPipe() == fd.fd;
304368
};
305369

@@ -310,7 +374,7 @@ bool TaskQueue::execute(TaskBeganCallback Began, TaskFinishedCallback Finished,
310374
Task &T = *iter->second;
311375
if (fd.revents & POLLIN || fd.revents & POLLPRI) {
312376
// There's data available to read.
313-
T.readFromPipe();
377+
T.readFromPipes();
314378
}
315379

316380
if (fd.revents & POLLHUP || fd.revents & POLLERR) {
@@ -339,7 +403,7 @@ bool TaskQueue::execute(TaskBeganCallback Began, TaskFinishedCallback Finished,
339403
// If we have a TaskFinishedCallback, only set SubtaskFailed to
340404
// true if the callback returns StopExecution.
341405
SubtaskFailed = Finished(T.getPid(), Result, T.getOutput(),
342-
T.getContext()) ==
406+
T.getErrors(), T.getContext()) ==
343407
TaskFinishedResponse::StopExecution;
344408
} else if (Result != 0) {
345409
// Since we don't have a TaskFinishedCallback, treat a subtask
@@ -353,9 +417,9 @@ bool TaskQueue::execute(TaskBeganCallback Began, TaskFinishedCallback Finished,
353417
StringRef ErrorMsg = strsignal(Signal);
354418

355419
if (Signalled) {
356-
TaskFinishedResponse Response = Signalled(T.getPid(), ErrorMsg,
357-
T.getOutput(),
358-
T.getContext());
420+
TaskFinishedResponse Response =
421+
Signalled(T.getPid(), ErrorMsg, T.getOutput(), T.getErrors(),
422+
T.getContext());
359423
if (Response == TaskFinishedResponse::StopExecution)
360424
// If we have a TaskCrashedCallback, only set SubtaskFailed to
361425
// true if the callback returns StopExecution.

lib/Driver/Compilation.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -481,8 +481,9 @@ int Compilation::performJobsImpl() {
481481
// continue (if execution should stop, this callback should return true), and
482482
// it should also schedule any additional commands which we now know need
483483
// to run.
484-
auto taskFinished = [&] (ProcessId Pid, int ReturnCode, StringRef Output,
485-
void *Context) -> TaskFinishedResponse {
484+
auto taskFinished = [&](ProcessId Pid, int ReturnCode, StringRef Output,
485+
StringRef Errors,
486+
void *Context) -> TaskFinishedResponse {
486487
const Job *FinishedCmd = (const Job *)Context;
487488

488489
if (ShowDriverTimeCompilation) {
@@ -612,8 +613,9 @@ int Compilation::performJobsImpl() {
612613
return TaskFinishedResponse::ContinueExecution;
613614
};
614615

615-
auto taskSignalled = [&] (ProcessId Pid, StringRef ErrorMsg, StringRef Output,
616-
void *Context) -> TaskFinishedResponse {
616+
auto taskSignalled = [&](ProcessId Pid, StringRef ErrorMsg, StringRef Output,
617+
StringRef Errors,
618+
void *Context) -> TaskFinishedResponse {
617619
const Job *SignalledCmd = (const Job *)Context;
618620

619621
if (ShowDriverTimeCompilation) {

lib/Driver/Driver.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,7 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args,
12041204
[&OI](sys::ProcessId PID,
12051205
int returnCode,
12061206
StringRef output,
1207+
StringRef errors,
12071208
void *unused) -> sys::TaskFinishedResponse {
12081209
if (returnCode == 0) {
12091210
output = output.rtrim();

0 commit comments

Comments
 (0)