Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(117)

Unified Diff: tools/gn/exec_process.cc

Issue 646013003: GN: Capture and print stderr from exec_script (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Maybe fix windows compile Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/gn/exec_process.cc
diff --git a/tools/gn/exec_process.cc b/tools/gn/exec_process.cc
new file mode 100644
index 0000000000000000000000000000000000000000..51fa474e4331d4a0c6da0d090ea4f7622531d5b1
--- /dev/null
+++ b/tools/gn/exec_process.cc
@@ -0,0 +1,255 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/process/kill.h"
+#include "base/process/launch.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#endif
+
+#if defined(OS_POSIX)
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/file_descriptor_shuffle.h"
+#endif
+
+namespace internal {
+
+#if defined(OS_WIN)
+bool ExecProcess(const CommandLine& cmdline,
+ const base::FilePath& startup_dir,
+ std::string* std_out,
+ std::string* std_err,
+ int* exit_code) {
+ SECURITY_ATTRIBUTES sa_attr;
+ // Set the bInheritHandle flag so pipe handles are inherited.
+ sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa_attr.bInheritHandle = TRUE;
+ sa_attr.lpSecurityDescriptor = NULL;
+
+ // Create the pipe for the child process's STDOUT.
+ HANDLE out_read = NULL;
+ HANDLE out_write = NULL;
+ if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) {
+ NOTREACHED() << "Failed to create pipe";
+ return false;
+ }
+ base::win::ScopedHandle scoped_out_read(out_read);
+ base::win::ScopedHandle scoped_out_write(out_write);
+
+ // Create the pipe for the child process's STDERR.
+ HANDLE err_read = NULL;
+ HANDLE err_write = NULL;
+ if (!CreatePipe(&err_read, &err_write, &sa_attr, 0)) {
+ NOTREACHED() << "Failed to create pipe";
+ return false;
+ }
+ base::win::ScopedHandle scoped_err_read(err_read);
+ base::win::ScopedHandle scoped_err_write(err_write);
+
+ // Ensure the read handle to the pipe for STDOUT/STDERR is not inherited.
+ if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
+ NOTREACHED() << "Failed to disabled pipe inheritance";
+ return false;
+ }
+ if (!SetHandleInformation(err_read, HANDLE_FLAG_INHERIT, 0)) {
+ NOTREACHED() << "Failed to disabled pipe inheritance";
+ return false;
+ }
+
+ base::FilePath::StringType cmdline_str(cmdline.GetCommandLineString());
+
+ STARTUPINFO start_info = {};
+
+ start_info.cb = sizeof(STARTUPINFO);
+ start_info.hStdOutput = out_write;
+ // Keep the normal stdin.
+ start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ // FIXME(brettw) set stderr here when we actually read it below.
+ //start_info.hStdError = err_write;
+ start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ start_info.dwFlags |= STARTF_USESTDHANDLES;
+
+ // Create the child process.
+ PROCESS_INFORMATION temp_process_info = {};
+ if (!CreateProcess(NULL,
+ &cmdline_str[0],
+ NULL, NULL,
+ TRUE, // Handles are inherited.
+ 0, NULL,
+ startup_dir.value().c_str(),
+ &start_info, &temp_process_info)) {
+ return false;
+ }
+ base::win::ScopedProcessInformation proc_info(temp_process_info);
+
+ // Close our writing end of pipes now. Otherwise later read would not be able
+ // to detect end of child's output.
+ scoped_out_write.Close();
+ scoped_err_write.Close();
+
+ // Read output from the child process's pipe for STDOUT
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
+
+ // FIXME(brettw) read from stderr here! This is complicated because we want
+ // to read both of them at the same time, probably need overlapped I/O.
+ // Also uncomment start_info code above.
+ for (;;) {
+ DWORD bytes_read = 0;
+ BOOL success = ReadFile(out_read, buffer, kBufferSize, &bytes_read, NULL);
+ if (!success || bytes_read == 0)
+ break;
+ std_out->append(buffer, bytes_read);
+ }
+
+ // Let's wait for the process to finish.
+ WaitForSingleObject(proc_info.process_handle(), INFINITE);
+
+ DWORD dw_exit_code;
+ GetExitCodeProcess(proc_info.process_handle(), &dw_exit_code);
+ *exit_code = static_cast<int>(dw_exit_code);
+
+ return true;
+}
+#else
+// Reads from the provided file descriptor and appends to output. Returns false
+// if the fd is closed or there is an unexpected error (not
+// EINTR/EAGAIN/EWOULDBLOCK).
+bool ReadFromPipe(int fd, std::string* output) {
+ char buffer[256];
+ int bytes_read = HANDLE_EINTR(read(fd, buffer, sizeof(buffer)));
+ if (bytes_read == -1) {
+ return errno == EAGAIN || errno == EWOULDBLOCK;
+ } else if (bytes_read <= 0) {
+ return false;
+ }
+ output->append(buffer, bytes_read);
+ return true;
+}
+
+
+bool ExecProcess(const CommandLine& cmdline,
+ const base::FilePath& startup_dir,
+ std::string* std_out,
+ std::string* std_err,
+ int* exit_code) {
+ *exit_code = EXIT_FAILURE;
+
+ std::vector<std::string> argv = cmdline.argv();
+
+ int out_fd[2], err_fd[2];
+ pid_t pid;
+ base::InjectiveMultimap fd_shuffle1, fd_shuffle2;
+ scoped_ptr<char*[]> argv_cstr(new char*[argv.size() + 1]);
+
+ fd_shuffle1.reserve(3);
+ fd_shuffle2.reserve(3);
+
+ if (pipe(out_fd) < 0)
+ return false;
+ base::ScopedFD out_read(out_fd[0]), out_write(out_fd[1]);
+
+ if (pipe(err_fd) < 0)
+ return false;
+ base::ScopedFD err_read(err_fd[0]), err_write(err_fd[1]);
+
+ if (out_read.get() > FD_SETSIZE || err_read.get() > FD_SETSIZE) {
+ return false;
+ }
+
+ switch (pid = fork()) {
+ case -1: // error
+ return false;
+ case 0: // child
+ {
+ // DANGER: no calls to malloc are allowed from now on:
+ // http://crbug.com/36678
+
+ // Obscure fork() rule: in the child, if you don't end up doing exec*(),
+ // you call _exit() instead of exit(). This is because _exit() does not
+ // call any previously-registered (in the parent) exit handlers, which
+ // might do things like block waiting for threads that don't even exist
+ // in the child.
+ int dev_null = open("/dev/null", O_WRONLY);
+ if (dev_null < 0)
+ _exit(127);
+
+ fd_shuffle1.push_back(
+ base::InjectionArc(out_write.get(), STDOUT_FILENO, true));
+ fd_shuffle1.push_back(
+ base::InjectionArc(err_write.get(), STDERR_FILENO, true));
+ //fd_shuffle1.push_back(
+ //base::InjectionArc(STDERR_FILENO, STDERR_FILENO, true));
+ fd_shuffle1.push_back(
+ base::InjectionArc(dev_null, STDIN_FILENO, true));
+ // Adding another element here? Remeber to increase the argument to
+ // reserve(), above.
+
+ for (size_t i = 0; i < fd_shuffle1.size(); ++i)
+ fd_shuffle2.push_back(fd_shuffle1[i]);
+
+ if (!ShuffleFileDescriptors(&fd_shuffle1))
+ _exit(127);
+
+ base::SetCurrentDirectory(startup_dir);
+
+ // TODO(brettw) the base version GetAppOutput does a
+ // CloseSuperfluousFds call here. Do we need this?
+
+ for (size_t i = 0; i < argv.size(); i++)
+ argv_cstr[i] = const_cast<char*>(argv[i].c_str());
+ argv_cstr[argv.size()] = NULL;
+ execvp(argv_cstr[0], argv_cstr.get());
+ _exit(127);
+ }
+ default: // parent
+ {
+ // Close our writing end of pipe now. Otherwise later read would not
+ // be able to detect end of child's output (in theory we could still
+ // write to the pipe).
+ out_write.reset();
+ err_write.reset();
+
+ bool out_open = true, err_open = true;
+ while (out_open || err_open) {
+ fd_set read_fds;
+ FD_ZERO(&read_fds);
+ FD_SET(out_read.get(), &read_fds);
+ FD_SET(err_read.get(), &read_fds);
+ int res =
+ HANDLE_EINTR(select(std::max(out_read.get(), err_read.get()) + 1,
+ &read_fds,
+ NULL,
+ NULL,
+ NULL));
+ if (res <= 0)
+ break;
+ if (FD_ISSET(out_read.get(), &read_fds)) {
+ out_open = ReadFromPipe(out_read.get(), std_out);
+ }
+ if (FD_ISSET(err_read.get(), &read_fds)) {
+ err_open = ReadFromPipe(err_read.get(), std_err);
+ }
+ }
+
+ return base::WaitForExitCode(pid, exit_code);
+ }
+ }
+
+ return false;
+}
+#endif
+
+} // namespace internal
+

Powered by Google App Engine
This is Rietveld 408576698