[Cmake-commits] CMake branch, next, updated. v2.8.8-3079-g5c59e6e
Peter Kuemmel
syntheticpp at gmx.net
Fri Jun 8 12:03:22 EDT 2012
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "CMake".
The branch, next has been updated
via 5c59e6e8d5babbc9b0941d4dec0a9ebc2a2f9b8d (commit)
via 033a687acd828ad6667d154939ffdbc482ab047f (commit)
via 1d40729eaa35dd643efdf5e793e6a541e890f33a (commit)
from d8eed5c615ece7f1fb46f14e404501833239cdee (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=5c59e6e8d5babbc9b0941d4dec0a9ebc2a2f9b8d
commit 5c59e6e8d5babbc9b0941d4dec0a9ebc2a2f9b8d
Merge: d8eed5c 033a687
Author: Peter Kuemmel <syntheticpp at gmx.net>
AuthorDate: Fri Jun 8 12:03:20 2012 -0400
Commit: CMake Topic Stage <kwrobot at kitware.com>
CommitDate: Fri Jun 8 12:03:20 2012 -0400
Merge topic 'ninja-cldeps' into next
033a687 Ninja: add wrapper for cl to extract dependencies
1d40729 Ninja: add dependency tracking for msvc with cldeps
diff --cc Source/cmNinjaTargetGenerator.cxx
index a362d13,6518727..c266525
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@@ -361,10 -373,16 +370,16 @@@ cmNinjaTargetGenerato
std::string cmdLine =
this->GetLocalGenerator()->BuildCommandLine(compileCmds);
+ 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;
+ cmOStringStream comment;
comment << "Rule for compiling " << language << " files.";
- std::ostringstream description;
+ cmOStringStream description;
description << "Building " << language << " object $out";
this->GetGlobalGenerator()->AddRule(this->LanguageCompilerRule(language),
cmdLine,
http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=033a687acd828ad6667d154939ffdbc482ab047f
commit 033a687acd828ad6667d154939ffdbc482ab047f
Author: Peter Kuemmel <syntheticpp at gmx.net>
AuthorDate: Fri Jun 8 16:31:54 2012 +0200
Commit: Peter Kuemmel <syntheticpp at gmx.net>
CommitDate: Fri Jun 8 17:42:11 2012 +0200
Ninja: add wrapper for cl to extract dependencies
cmcldeps wraps cl and adds /showInclude before calling cl.
It parses the output of cl for used headers, drops system
headers and writes them to a GCC like dependency file.
cmcldeps uses ATM ninja code for process handling,
but could be ported later to SystemTools.
TODO: Why needs ninja multiple calls in the BuildDepends test?
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)
http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=1d40729eaa35dd643efdf5e793e6a541e890f33a
commit 1d40729eaa35dd643efdf5e793e6a541e890f33a
Author: Peter Kuemmel <syntheticpp at gmx.net>
AuthorDate: Thu Jun 7 23:34:48 2012 +0200
Commit: Peter Kuemmel <syntheticpp at gmx.net>
CommitDate: Thu Jun 7 23:34:48 2012 +0200
Ninja: add dependency tracking for msvc with cldeps
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index e419a4d..74b5c92 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -330,12 +330,17 @@ cmNinjaTargetGenerator
vars.Defines = "$DEFINES";
vars.TargetPDB = "$TARGET_PDB";
+ bool cldeps = false;
+ const char* cc = this->GetMakefile()->GetDefinition("CMAKE_C_COMPILER");
+ if(cc && std::string(cc).find("cl.exe") != std::string::npos)
+ cldeps = true;
+
std::string depfile;
std::string depfileFlagsName = "CMAKE_DEPFILE_FLAGS_" + language;
const char *depfileFlags =
this->GetMakefile()->GetDefinition(depfileFlagsName.c_str());
- if (depfileFlags) {
- std::string depfileFlagsStr = depfileFlags;
+ if (depfileFlags || cldeps) {
+ std::string depfileFlagsStr = depfileFlags ? depfileFlags : "";
depfile = "$out.d";
cmSystemTools::ReplaceString(depfileFlagsStr, "<DEPFILE>",
depfile.c_str());
@@ -364,6 +369,9 @@ cmNinjaTargetGenerator
std::string cmdLine =
this->GetLocalGenerator()->BuildCommandLine(compileCmds);
+ if(cldeps)
+ cmdLine = "cldeps.exe $out.d $out " + cmdLine;
+
// Write the rule for compiling file of the given language.
std::ostringstream comment;
comment << "Rule for compiling " << language << " files.";
-----------------------------------------------------------------------
Summary of changes:
Modules/Platform/Windows-cl.cmake | 18 +
Source/CMakeLists.txt | 4 +
Source/cmGlobalNinjaGenerator.cxx | 15 +-
Source/cmNinjaTargetGenerator.cxx | 19 +-
Source/cmcldeps.cxx | 644 +++++++++++++++++++++++++++++++++++++
Tests/BuildDepends/CMakeLists.txt | 8 +-
6 files changed, 702 insertions(+), 6 deletions(-)
create mode 100644 Source/cmcldeps.cxx
hooks/post-receive
--
CMake
More information about the Cmake-commits
mailing list