From 5f12424ebc9f810ef279d09f1e660e20558dd535 Mon Sep 17 00:00:00 2001 From: Bill Hoffman Date: Wed, 27 Jun 2012 12:28:12 -0400 Subject: [PATCH] Remove process execution code from cmcldeps and have it use cmake code. This simplifies the code in cmcldeps and avoids having yet another set of process execution code. --- Source/CMakeLists.txt | 1 + Source/cmcldeps.cxx | 524 +++--------------------------------------- 2 files changed, 39 insertions(+), 486 deletions(-) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 2c6bc7665..14af796a6 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -386,6 +386,7 @@ IF(CMAKE_ENABLE_NINJA) IF(WIN32 AND NOT CYGWIN AND NOT BORLAND) SET_SOURCE_FILES_PROPERTIES(cmcldeps.cxx PROPERTIES COMPILE_DEFINITIONS _WIN32_WINNT=0x0501) ADD_EXECUTABLE(cmcldeps cmcldeps.cxx) + TARGET_LINK_LIBRARIES(cmcldeps CMakeLib) INSTALL_TARGETS(/bin cmcldeps) ENDIF() ELSE() diff --git a/Source/cmcldeps.cxx b/Source/cmcldeps.cxx index 7d3c4bd65..39f696f0c 100644 --- a/Source/cmcldeps.cxx +++ b/Source/cmcldeps.cxx @@ -1,470 +1,3 @@ -/* - ninja's subprocess.h -*/ - -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef NINJA_SUBPROCESS_H_ -#define NINJA_SUBPROCESS_H_ - -#include -#include -#include -#include -#include - - -#ifdef _WIN32 -#include -#else -#include -#endif - - -#if defined(_WIN64) -typedef unsigned __int64 cmULONG_PTR; -#else -typedef unsigned long cmULONG_PTR; -#endif - -//#include "exit_status.h" -enum ExitStatus { - ExitSuccess, - ExitFailure, - ExitInterrupted -}; - -/// Subprocess wraps a single async subprocess. It is entirely -/// passive: it expects the caller to notify it when its fds are ready -/// for reading, as well as call Finish() to reap the child once done() -/// is true. -struct Subprocess { - ~Subprocess(); - - /// Returns ExitSuccess on successful process exit, ExitInterrupted if - /// the process was interrupted, ExitFailure if it otherwise failed. - ExitStatus Finish(); - - bool Done() const; - - const std::string& GetOutput() const; - - int ExitCode() const { return exit_code_; } - - private: - Subprocess(); - bool Start(struct SubprocessSet* set, const std::string& command, - const std::string& dir); - void OnPipeReady(); - - std::string buf_; - -#ifdef _WIN32 - /// Set up pipe_ as the parent-side pipe of the subprocess; return the - /// other end of the pipe, usable in the child process. - HANDLE SetupPipe(HANDLE ioport); - - PROCESS_INFORMATION child_; - HANDLE pipe_; - OVERLAPPED overlapped_; - char overlapped_buf_[4 << 10]; - bool is_reading_; - int exit_code_; -#else - int fd_; - pid_t pid_; -#endif - - friend struct SubprocessSet; -}; - -/// SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses. -/// DoWork() waits for any state change in subprocesses; finished_ -/// is a queue of subprocesses as they finish. -struct SubprocessSet { - SubprocessSet(); - ~SubprocessSet(); - - Subprocess* Add(const std::string& command, const std::string& dir); - bool DoWork(); - Subprocess* NextFinished(); - void Clear(); - - std::vector running_; - std::queue finished_; - -#ifdef _WIN32 - static BOOL WINAPI NotifyInterrupted(DWORD dwCtrlType); - static HANDLE ioport_; -#else - static void SetInterruptedFlag(int signum); - static bool interrupted_; - - struct sigaction old_act_; - sigset_t old_mask_; -#endif -}; - -#endif // NINJA_SUBPROCESS_H_ - - -/* - ninja's util functions -*/ - - -static void Fatal(const char* msg, ...) { - va_list ap; - fprintf(stderr, "ninja: FATAL: "); - va_start(ap, msg); - vfprintf(stderr, msg, ap); - va_end(ap); - fprintf(stderr, "\n"); -#ifdef _WIN32 - // On Windows, some tools may inject extra threads. - // exit() may block on locks held by those threads, so forcibly exit. - fflush(stderr); - fflush(stdout); - ExitProcess(1); -#else - exit(1); -#endif -} - - -#ifdef _WIN32 -std::string GetLastErrorString() { - DWORD err = GetLastError(); - - char* msg_buf; - FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - err, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (char*)&msg_buf, - 0, - NULL); - std::string msg = msg_buf; - LocalFree(msg_buf); - return msg; -} -#endif - -#define snprintf _snprintf - - -/* - ninja's subprocess-win32.cc -*/ - -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//#include "subprocess.h" - -#include - -#include - -//#include "util.h" - -namespace { - -void Win32Fatal(const char* function) { - Fatal("%s: %s", function, GetLastErrorString().c_str()); -} - -} // anonymous namespace - -Subprocess::Subprocess() : overlapped_(), is_reading_(false), - exit_code_(1) { - child_.hProcess = NULL; -} - -Subprocess::~Subprocess() { - if (pipe_) { - if (!CloseHandle(pipe_)) - Win32Fatal("CloseHandle"); - } - // Reap child if forgotten. - if (child_.hProcess) - Finish(); -} - -HANDLE Subprocess::SetupPipe(HANDLE ioport) { - char pipe_name[100]; - snprintf(pipe_name, sizeof(pipe_name), - "\\\\.\\pipe\\ninja_pid%u_sp%p", GetCurrentProcessId(), this); - - pipe_ = ::CreateNamedPipeA(pipe_name, - PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE, - PIPE_UNLIMITED_INSTANCES, - 0, 0, INFINITE, NULL); - if (pipe_ == INVALID_HANDLE_VALUE) - Win32Fatal("CreateNamedPipe"); - - if (!CreateIoCompletionPort(pipe_, ioport, (cmULONG_PTR)this, 0)) - Win32Fatal("CreateIoCompletionPort"); - - memset(&overlapped_, 0, sizeof(overlapped_)); - if (!ConnectNamedPipe(pipe_, &overlapped_) && - GetLastError() != ERROR_IO_PENDING) { - Win32Fatal("ConnectNamedPipe"); - } - - // Get the write end of the pipe as a handle inheritable across processes. - HANDLE output_write_handle = CreateFile(pipe_name, GENERIC_WRITE, 0, - NULL, OPEN_EXISTING, 0, NULL); - HANDLE output_write_child; - if (!DuplicateHandle(GetCurrentProcess(), output_write_handle, - GetCurrentProcess(), &output_write_child, - 0, TRUE, DUPLICATE_SAME_ACCESS)) { - Win32Fatal("DuplicateHandle"); - } - CloseHandle(output_write_handle); - - return output_write_child; -} - -bool Subprocess::Start(SubprocessSet* set, const std::string& command, - const std::string& dir) { - HANDLE child_pipe = SetupPipe(set->ioport_); - - SECURITY_ATTRIBUTES security_attributes; - memset(&security_attributes, 0, sizeof(SECURITY_ATTRIBUTES)); - security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); - security_attributes.bInheritHandle = TRUE; - // Must be inheritable so subprocesses can dup to children. - HANDLE nul = CreateFile("NUL", GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - &security_attributes, OPEN_EXISTING, 0, NULL); - if (nul == INVALID_HANDLE_VALUE) - Fatal("couldn't open nul"); - - STARTUPINFOA startup_info; - memset(&startup_info, 0, sizeof(startup_info)); - startup_info.cb = sizeof(STARTUPINFO); - startup_info.dwFlags = STARTF_USESTDHANDLES; - startup_info.hStdInput = nul; - startup_info.hStdOutput = child_pipe; - startup_info.hStdError = child_pipe; - - PROCESS_INFORMATION process_info; - memset(&process_info, 0, sizeof(process_info)); - - // Do not prepend 'cmd /c' on Windows, this breaks command - // lines greater than 8,191 chars. - if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL, - /* inherit handles */ TRUE, CREATE_NEW_PROCESS_GROUP, - NULL, (dir.empty() ? NULL : dir.c_str()), - &startup_info, &process_info)) { - DWORD error = GetLastError(); - if (error == ERROR_FILE_NOT_FOUND) { - // file (program) not found error is treated - // as a normal build action failure - if (child_pipe) - CloseHandle(child_pipe); - CloseHandle(pipe_); - CloseHandle(nul); - pipe_ = NULL; - // child_ is already NULL; - buf_ = - "CreateProcess failed: The system cannot find the file specified.\n"; - return true; - } else { - Win32Fatal("CreateProcess"); // pass all other errors to Win32Fatal - } - } - - // Close pipe channel only used by the child. - if (child_pipe) - CloseHandle(child_pipe); - CloseHandle(nul); - - CloseHandle(process_info.hThread); - child_ = process_info; - - return true; -} - -void Subprocess::OnPipeReady() { - DWORD bytes; - if (!GetOverlappedResult(pipe_, &overlapped_, &bytes, TRUE)) { - if (GetLastError() == ERROR_BROKEN_PIPE) { - CloseHandle(pipe_); - pipe_ = NULL; - return; - } - Win32Fatal("GetOverlappedResult"); - } - - if (is_reading_ && bytes) - buf_.append(overlapped_buf_, bytes); - - memset(&overlapped_, 0, sizeof(overlapped_)); - is_reading_ = true; - if (!::ReadFile(pipe_, overlapped_buf_, sizeof(overlapped_buf_), - &bytes, &overlapped_)) { - if (GetLastError() == ERROR_BROKEN_PIPE) { - CloseHandle(pipe_); - pipe_ = NULL; - return; - } - if (GetLastError() != ERROR_IO_PENDING) - Win32Fatal("ReadFile"); - } - - // Even if we read any bytes in the readfile call, we'll enter this - // function again later and get them at that point. -} - -ExitStatus Subprocess::Finish() { - if (!child_.hProcess) - return ExitFailure; - - // TODO: add error handling for all of these. - WaitForSingleObject(child_.hProcess, INFINITE); - - DWORD exit_code = 0; - GetExitCodeProcess(child_.hProcess, &exit_code); - - CloseHandle(child_.hProcess); - child_.hProcess = NULL; - exit_code_ = exit_code; - return exit_code == 0 ? ExitSuccess : - exit_code == CONTROL_C_EXIT ? ExitInterrupted : - ExitFailure; -} - -bool Subprocess::Done() const { - return pipe_ == NULL; -} - -const std::string& Subprocess::GetOutput() const { - return buf_; -} - -HANDLE SubprocessSet::ioport_; - -SubprocessSet::SubprocessSet() { - ioport_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); - if (!ioport_) - Win32Fatal("CreateIoCompletionPort"); - if (!SetConsoleCtrlHandler(NotifyInterrupted, TRUE)) - Win32Fatal("SetConsoleCtrlHandler"); -} - -SubprocessSet::~SubprocessSet() { - Clear(); - - SetConsoleCtrlHandler(NotifyInterrupted, FALSE); - CloseHandle(ioport_); -} - -BOOL WINAPI SubprocessSet::NotifyInterrupted(DWORD dwCtrlType) { - if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) { - if (!PostQueuedCompletionStatus(ioport_, 0, 0, NULL)) - Win32Fatal("PostQueuedCompletionStatus"); - return TRUE; - } - - return FALSE; -} - -Subprocess *SubprocessSet::Add(const std::string& command, - const std::string& dir) { - Subprocess *subprocess = new Subprocess; - if (!subprocess->Start(this, command, dir)) { - delete subprocess; - return 0; - } - if (subprocess->child_.hProcess) - running_.push_back(subprocess); - else - finished_.push(subprocess); - return subprocess; -} - -bool SubprocessSet::DoWork() { - DWORD bytes_read; - Subprocess* subproc; - OVERLAPPED* overlapped; - - if (!GetQueuedCompletionStatus(ioport_, &bytes_read, (cmULONG_PTR*)&subproc, - &overlapped, INFINITE)) { - if (GetLastError() != ERROR_BROKEN_PIPE) - Win32Fatal("GetQueuedCompletionStatus"); - } - - if (!subproc) // A NULL subproc indicates that we were interrupted and is - // delivered by NotifyInterrupted above. - return true; - - subproc->OnPipeReady(); - - if (subproc->Done()) { - std::vector::iterator end = - std::remove(running_.begin(), running_.end(), subproc); - if (running_.end() != end) { - finished_.push(subproc); - running_.resize(end - running_.begin()); - } - } - - return false; -} - -Subprocess* SubprocessSet::NextFinished() { - if (finished_.empty()) - return NULL; - Subprocess* subproc = finished_.front(); - finished_.pop(); - return subproc; -} - -void SubprocessSet::Clear() { - std::vector::iterator it = running_.begin(); - for (; it != running_.end(); ++it) { - if ((*it)->child_.hProcess) { - if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, - (*it)->child_.dwProcessId)) - Win32Fatal("GenerateConsoleCtrlEvent"); - } - } - it = running_.begin(); - for (; it != running_.end(); ++it) - delete *it; - running_.clear(); -} - - // Copyright 2011 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -489,13 +22,26 @@ void SubprocessSet::Clear() { #include #include -//#include "subprocess.h" -//#include "util.h" +#include // We don't want any wildcard expansion. // See http://msdn.microsoft.com/en-us/library/zay8tzh6(v=vs.85).aspx void _setargv() {} +static void Fatal(const char* msg, ...) { + va_list ap; + fprintf(stderr, "ninja: FATAL: "); + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); + fprintf(stderr, "\n"); + // On Windows, some tools may inject extra threads. + // exit() may block on locks held by those threads, so forcibly exit. + fflush(stderr); + fflush(stdout); + ExitProcess(1); +} + static void usage(const char* msg) { Fatal("%s\n\nusage:\n " "cmcldeps " @@ -629,24 +175,30 @@ static int process( const std::string& srcfilename, const std::string& prefix, const std::string& cmd, const std::string& dir = "", - bool quiet = false) { - - SubprocessSet subprocs; - Subprocess* subproc = subprocs.Add(cmd, dir); - - if(!subproc) + bool quiet = false) +{ + std::string output; + int ret = 0; + // break up command line into a vector + std::vector args; + cmSystemTools::ParseWindowsCommandLine(cmd.c_str(), + args); + // convert to correct vector type for RunSingleCommand + std::vector command; + for(std::vector::iterator i = args.begin(); + i != args.end(); ++i) + { + command.push_back(i->c_str()); + } + // run the command + bool success = + cmSystemTools::RunSingleCommand(command, &output, &ret, dir.c_str(), + cmSystemTools::OUTPUT_NONE); + if(ret!= 0) + { return 2; - - while ((subproc = subprocs.NextFinished()) == NULL) { - subprocs.DoWork(); - } - - bool success = subproc->Finish() == ExitSuccess; - int exit_code = subproc->ExitCode(); - - std::string output = subproc->GetOutput(); - delete subproc; - + } + int exit_code = ret; // process the include directives and output everything else std::stringstream ss(output); std::string line;