diff options
-rw-r--r-- | Modules/Platform/Windows-cl.cmake | 18 | ||||
-rw-r--r-- | Source/CMakeLists.txt | 4 | ||||
-rw-r--r-- | Source/cmGlobalNinjaGenerator.cxx | 15 | ||||
-rw-r--r-- | Source/cmNinjaTargetGenerator.cxx | 17 | ||||
-rw-r--r-- | Source/cmcldeps.cxx | 644 | ||||
-rw-r--r-- | Tests/BuildDepends/CMakeLists.txt | 8 |
6 files changed, 697 insertions, 9 deletions
diff --git a/Modules/Platform/Windows-cl.cmake b/Modules/Platform/Windows-cl.cmake index be6abb6..eed4e52 100644 --- a/Modules/Platform/Windows-cl.cmake +++ b/Modules/Platform/Windows-cl.cmake @@ -251,3 +251,21 @@ IF(NOT EXISTS "${CMAKE_PLATFORM_ROOT_BIN}/CMakeCXXPlatform.cmake") CONFIGURE_FILE(${CMAKE_ROOT}/Modules/Platform/Windows-cl.cmake.in ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeCXXPlatform.cmake IMMEDIATE) ENDIF(NOT EXISTS "${CMAKE_PLATFORM_ROOT_BIN}/CMakeCXXPlatform.cmake") + + +IF(CMAKE_GENERATOR MATCHES "Ninja" AND CMAKE_C_COMPILER) + # TODO try_compile doesn't need cmcldeps, find a better solution + if(NOT EXISTS ${CMAKE_TRY_COMPILE_SOURCE_DIR}/../ShowIncludes) + SET(showdir ${CMAKE_BINARY_DIR}/CMakeFiles/ShowIncludes) + FILE(WRITE ${showdir}/foo.h "\n") + FILE(WRITE ${showdir}/main.c "#include \"foo.h\" \nint main(){}\n") + EXECUTE_PROCESS(COMMAND ${CMAKE_C_COMPILER} /nologo /showIncludes ${showdir}/main.c + WORKING_DIRECTORY ${showdir} OUTPUT_VARIABLE showOut) + STRING(REPLACE main.c "" showOut1 ${showOut}) + STRING(REPLACE "/" "\\" header1 ${showdir}/foo.h) + STRING(TOLOWER ${header1} header2) + STRING(REPLACE ${header2} "" showOut2 ${showOut1}) + STRING(REPLACE "\n" "" showOut3 ${showOut2}) + SET(CMAKE_CL_SHOWINCLUDE_PREFIX ${showOut3}) + ENDIF() +ENDIF() diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 46bdec6..ebeda56 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -383,6 +383,10 @@ IF(CMAKE_ENABLE_NINJA) cmNinjaUtilityTargetGenerator.h ) ADD_DEFINITIONS(-DCMAKE_USE_NINJA) + IF(MSVC) # TODO WIN32 + ADD_EXECUTABLE(cmcldeps cmcldeps.cxx) + INSTALL_TARGETS(/bin cmcldeps) + ENDIF() ELSE() MESSAGE(STATUS "Ninja generator disabled, enforce with -DCMAKE_ENABLE_NINJA=ON") ENDIF() diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index c4b1cc9..9a2597b 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -460,17 +460,28 @@ void cmGlobalNinjaGenerator // check if mingw is used const char* cc = mf->GetDefinition("CMAKE_C_COMPILER"); if(cc && std::string(cc).find("gcc.exe") != std::string::npos) - { + { UsingMinGW = true; std::string rc = cmSystemTools::FindProgram("windres"); if(rc.empty()) rc = "windres.exe";; mf->AddDefinition("CMAKE_RC_COMPILER", rc.c_str()); } + else if (cc && std::string(cc).find("cl.exe") != std::string::npos) + { + const char* cmake = mf->GetDefinition("CMAKE_COMMAND"); + std::string bindir = cmake ? cmake : ""; + cmSystemTools::ReplaceString(bindir, "cmake.exe", ""); + std::vector<std::string> locations; + locations.push_back(bindir); + std::string cldeps = cmSystemTools::FindProgram("cmcldeps", locations); + if(!cldeps.empty()) + mf->AddDefinition("CMAKE_CMCLDEPS_EXECUTABLE", cldeps.c_str()); + } } this->cmGlobalGenerator::EnableLanguage(language, mf, optional); this->ResolveLanguageCompiler(*l, mf, optional); - } + } } bool cmGlobalNinjaGenerator::UsingMinGW = false; diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 74b5c92..6518727 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -330,16 +330,20 @@ cmNinjaTargetGenerator vars.Defines = "$DEFINES"; vars.TargetPDB = "$TARGET_PDB"; - bool cldeps = false; + const char* cldeps = 0; + const char* showIncludePrefix = 0; const char* cc = this->GetMakefile()->GetDefinition("CMAKE_C_COMPILER"); if(cc && std::string(cc).find("cl.exe") != std::string::npos) - cldeps = true; + { + cldeps = this->GetMakefile()->GetDefinition("CMAKE_CMCLDEPS_EXECUTABLE"); + showIncludePrefix = this->GetMakefile()->GetDefinition("CMAKE_CL_SHOWINCLUDE_PREFIX"); + } std::string depfile; std::string depfileFlagsName = "CMAKE_DEPFILE_FLAGS_" + language; const char *depfileFlags = this->GetMakefile()->GetDefinition(depfileFlagsName.c_str()); - if (depfileFlags || cldeps) { + if (depfileFlags || (cldeps && showIncludePrefix)) { std::string depfileFlagsStr = depfileFlags ? depfileFlags : ""; depfile = "$out.d"; cmSystemTools::ReplaceString(depfileFlagsStr, "<DEPFILE>", @@ -369,8 +373,11 @@ cmNinjaTargetGenerator std::string cmdLine = this->GetLocalGenerator()->BuildCommandLine(compileCmds); - if(cldeps) - cmdLine = "cldeps.exe $out.d $out " + cmdLine; + if(cldeps && showIncludePrefix) + { + std::string prefix = showIncludePrefix; + cmdLine = std::string(cldeps) + " $in $out.d $out " + "\"" + prefix + "\" " + cmdLine; + } // Write the rule for compiling file of the given language. std::ostringstream comment; diff --git a/Source/cmcldeps.cxx b/Source/cmcldeps.cxx new file mode 100644 index 0000000..48f2cfd --- /dev/null +++ b/Source/cmcldeps.cxx @@ -0,0 +1,644 @@ +/* + 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> +using namespace std; + +#ifdef _WIN32 +#include <windows.h> +#else +#include <signal.h> +#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 string& GetOutput() const; + + private: + Subprocess(); + bool Start(struct SubprocessSet* set, const string& command); + void OnPipeReady(); + + 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); + + HANDLE child_; + HANDLE pipe_; + OVERLAPPED overlapped_; + char overlapped_buf_[4 << 10]; + bool is_reading_; +#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 string& command); + bool DoWork(); + Subprocess* NextFinished(); + void Clear(); + + vector<Subprocess*> running_; + 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 +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); + 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() : child_(NULL) , overlapped_(), is_reading_(false) { +} + +Subprocess::~Subprocess() { + if (pipe_) { + if (!CloseHandle(pipe_)) + Win32Fatal("CloseHandle"); + } + // Reap child if forgotten. + if (child_) + 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, (ULONG_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 string& command) { + 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, NULL, + &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.hProcess; + + 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_) + return ExitFailure; + + // TODO: add error handling for all of these. + WaitForSingleObject(child_, INFINITE); + + DWORD exit_code = 0; + GetExitCodeProcess(child_, &exit_code); + + CloseHandle(child_); + child_ = NULL; + + return exit_code == 0 ? ExitSuccess : + exit_code == CONTROL_C_EXIT ? ExitInterrupted : + ExitFailure; +} + +bool Subprocess::Done() const { + return pipe_ == NULL; +} + +const 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 string& command) { + Subprocess *subprocess = new Subprocess; + if (!subprocess->Start(this, command)) { + delete subprocess; + return 0; + } + if (subprocess->child_) + 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, (PULONG_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()) { + 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() { + for (vector<Subprocess*>::iterator i = running_.begin(); + i != running_.end(); ++i) { + if ((*i)->child_) + if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, GetProcessId((*i)->child_))) + Win32Fatal("GenerateConsoleCtrlEvent"); + } + for (vector<Subprocess*>::iterator i = running_.begin(); + i != running_.end(); ++i) + delete *i; + running_.clear(); +} + + +// Copyright 2011 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. + + +// Wrapper around cl that adds /showIncludes to command line, and uses that to +// generate .d files that match the style from gcc -MD. +// +// /showIncludes is equivalent to -MD, not -MMD, that is, system headers are +// included. + + +#include <windows.h> +#include <sstream> +//#include "subprocess.h" +//#include "util.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 usage(const char* msg) { + Fatal("%s\n\nusage:\n" + " cldeps " + "<source file> " + "<output path for *.d file> " + "<output path for *.obj file> " + "<prefix of /showIncludes> " + "<path to cl> " + "<rest of command ...>\n", msg); +} + +static string trimLeadingSpace(const string& cmdline) { + int i = 0; + for (; cmdline[i] == ' '; ++i) + ; + return cmdline.substr(i); +} + +static void doEscape(string& str, const string& search, const string& repl) { + string::size_type pos = 0; + while ((pos = str.find(search, pos)) != string::npos) { + str.replace(pos, search.size(), repl); + pos += repl.size(); + } +} + +// Strips one argument from the cmdline and returns it. "surrounding quotes" +// are removed from the argument if there were any. +static string getArg(string& cmdline) { + string ret; + bool in_quoted = false; + unsigned int i = 0; + + cmdline = trimLeadingSpace(cmdline); + + for (;; ++i) { + if (i >= cmdline.size()) + usage("Couldn't parse arguments."); + if (!in_quoted && cmdline[i] == ' ') + break; + if (cmdline[i] == '"') + in_quoted = !in_quoted; + } + + ret = cmdline.substr(0, i); + if (ret[0] == '"' && ret[i - 1] == '"') + ret = ret.substr(1, ret.size() - 2); + cmdline = cmdline.substr(i); + return ret; +} + +static void parseCommandLine(LPTSTR wincmdline, + string& srcfile, string& dfile, string& objfile, string& prefix, string& clpath, string& rest) { + string cmdline(wincmdline); + /* self */ getArg(cmdline); + srcfile = getArg(cmdline); + std::string::size_type pos = srcfile.rfind("\\"); + if (pos != string::npos) { + srcfile = srcfile.substr(pos + 1); + } else { + srcfile = ""; + } + dfile = getArg(cmdline); + objfile = getArg(cmdline); + prefix = getArg(cmdline); + clpath = getArg(cmdline); + rest = trimLeadingSpace(cmdline); +} + +static void outputDepFile(const string& dfile, const string& objfile, + vector<string>& incs) { + + // strip duplicates + sort(incs.begin(), incs.end()); + incs.erase(unique(incs.begin(), incs.end()), incs.end()); + + FILE* out = fopen(dfile.c_str(), "wb"); + + // FIXME should this be fatal or not? delete obj? delete d? + if (!out) + return; + + fprintf(out, "%s: \\\n", objfile.c_str()); + for (vector<string>::iterator i(incs.begin()); i != incs.end(); ++i) { + string tmp = *i; + doEscape(tmp, "\\", "\\\\"); + doEscape(tmp, " ", "\\ "); + //doEscape(tmp, "(", "("); // TODO ninja cant read ( and ) + //doEscape(tmp, ")", ")"); + fprintf(out, "%s \\\n", tmp.c_str()); + } + + fprintf(out, "\n"); + fclose(out); +} + + +bool startsWith(const std::string& str, const std::string& what) { + return str.compare(0, what.size(), what) == 0; +} + +bool contains(const std::string& str, const std::string& what) { + return str.find(what) != std::string::npos; +} + +int main() { + + // Use the Win32 api instead of argc/argv so we can avoid interpreting the + // rest of command line after the .d and .obj. Custom parsing seemed + // preferable to the ugliness you get into in trying to re-escape quotes for + // subprocesses, so by avoiding argc/argv, the subprocess is called with + // the same command line verbatim. + + string srcfile, dfile, objfile, prefix, clpath, rest; + parseCommandLine(GetCommandLine(), srcfile, dfile, objfile, prefix, clpath, rest); + + //fprintf(stderr, "D: %s\n", dfile.c_str()); + //fprintf(stderr, "OBJ: %s\n", objfile.c_str()); + //fprintf(stderr, "CL: %s\n", clpath.c_str()); + //fprintf(stderr, "REST: %s\n", rest.c_str()); + + SubprocessSet subprocs; + Subprocess* subproc = subprocs.Add(clpath + " /showIncludes " + rest); + if(!subproc) + return 2; + + while ((subproc = subprocs.NextFinished()) == NULL) { + subprocs.DoWork(); + } + + bool success = subproc->Finish() == ExitSuccess; + string output = subproc->GetOutput(); + + delete subproc; + + // process the include directives and output everything else + stringstream ss(output); + string line; + vector<string> includes; + bool isFirstLine = true; // cl prints always first the source filename + std::string sysHeadersCamel = "Program Files (x86)\\Microsoft "; + std::string sysHeadersLower = "program files (x86)\\microsoft "; + while (getline(ss, line)) { + if (startsWith(line, prefix)) { + if (!contains(line, sysHeadersCamel) && !contains(line, sysHeadersLower)) { + string inc = trimLeadingSpace(line.substr(prefix.size()).c_str()); + if (inc[inc.size() - 1] == '\r') // blech, stupid \r\n + inc = inc.substr(0, inc.size() - 1); + includes.push_back(inc); + } + } else { + if (!isFirstLine || !startsWith(line, srcfile)) { + fprintf(stdout, "%s\n", line.c_str()); + } else { + isFirstLine = false; + } + } + } + + if (!success) + return 3; + + // don't update .d until/unless we succeed compilation + outputDepFile(dfile, objfile, includes); + + return 0; +} diff --git a/Tests/BuildDepends/CMakeLists.txt b/Tests/BuildDepends/CMakeLists.txt index aa32d67..5e36d11 100644 --- a/Tests/BuildDepends/CMakeLists.txt +++ b/Tests/BuildDepends/CMakeLists.txt @@ -28,6 +28,10 @@ function(help_xcode_depends) endif(HELP_XCODE) endfunction(help_xcode_depends) +if("${CMAKE_GENERATOR}" MATCHES "Ninja") + set(HELP_NINJA 1) # TODO Why is this needed? +endif() + # The Intel compiler causes the MSVC linker to crash during # incremental linking, so avoid the /INCREMENTAL:YES flag. if(WIN32 AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Intel") @@ -154,7 +158,7 @@ try_compile(RESULT OUTPUT_VARIABLE OUTPUT) # Xcode is in serious need of help here -if(HELP_XCODE) +if(HELP_XCODE OR HELP_NINJA) try_compile(RESULT ${BuildDepends_BINARY_DIR}/Project ${BuildDepends_SOURCE_DIR}/Project @@ -165,7 +169,7 @@ if(HELP_XCODE) ${BuildDepends_SOURCE_DIR}/Project testRebuild OUTPUT_VARIABLE OUTPUT) -endif(HELP_XCODE) +endif() message("Output from second build:\n${OUTPUT}") if(NOT RESULT) |