Merge topic 'make_cldep_use_cmake_process_exec'
24a35ce
Ninja: print error message when command failed5f12424
Remove process execution code from cmcldeps and have it use cmake code.
This commit is contained in:
commit
45a687d307
|
@ -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()
|
||||
|
|
|
@ -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 <string>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <cstdio>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <signal.h>
|
||||
#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<Subprocess*> running_;
|
||||
std::queue<Subprocess*> 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 <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
//#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<Subprocess*>::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<Subprocess*>::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 <windows.h>
|
||||
#include <sstream>
|
||||
//#include "subprocess.h"
|
||||
//#include "util.h"
|
||||
#include <cmSystemTools.h>
|
||||
|
||||
// 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,23 +175,23 @@ 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)
|
||||
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;
|
||||
bool quiet = false)
|
||||
{
|
||||
std::string output;
|
||||
// break up command line into a vector
|
||||
std::vector<std::string> args;
|
||||
cmSystemTools::ParseWindowsCommandLine(cmd.c_str(), args);
|
||||
// convert to correct vector type for RunSingleCommand
|
||||
std::vector<cmStdString> command;
|
||||
for(std::vector<std::string>::iterator i = args.begin();
|
||||
i != args.end(); ++i)
|
||||
{
|
||||
command.push_back(i->c_str());
|
||||
}
|
||||
// run the command
|
||||
int exit_code = 0;
|
||||
bool run = cmSystemTools::RunSingleCommand(command, &output, &exit_code,
|
||||
dir.c_str(), cmSystemTools::OUTPUT_NONE);
|
||||
|
||||
// process the include directives and output everything else
|
||||
std::stringstream ss(output);
|
||||
|
@ -669,14 +215,11 @@ static int process( const std::string& srcfilename,
|
|||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
// don't update .d until/unless we succeed compilation
|
||||
outputDepFile(dfile, objfile, includes);
|
||||
if (run && exit_code == 0)
|
||||
outputDepFile(dfile, objfile, includes);
|
||||
|
||||
return 0;
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue