diff options
Diffstat (limited to 'Source')
-rw-r--r-- | Source/CMakeLists.txt | 3 | ||||
-rw-r--r-- | Source/CMakeVersion.cmake | 2 | ||||
-rw-r--r-- | Source/CTest/cmCTestUpdateHandler.cxx | 4 | ||||
-rw-r--r-- | Source/CTest/cmCTestUploadHandler.cxx | 2 | ||||
-rw-r--r-- | Source/CTest/cmProcess.cxx | 46 | ||||
-rw-r--r-- | Source/cmCTest.cxx | 2 | ||||
-rw-r--r-- | Source/cmExecuteProcessCommand.cxx | 57 | ||||
-rw-r--r-- | Source/cmGeneratorExpressionNode.cxx | 10 | ||||
-rw-r--r-- | Source/cmGetPipes.cxx | 48 | ||||
-rw-r--r-- | Source/cmGetPipes.h | 8 | ||||
-rw-r--r-- | Source/cmMessageCommand.cxx | 52 | ||||
-rw-r--r-- | Source/cmQtAutoMocUic.cxx | 6 | ||||
-rw-r--r-- | Source/cmServerProtocol.cxx | 5 | ||||
-rw-r--r-- | Source/cmSourceGroupCommand.cxx | 5 | ||||
-rw-r--r-- | Source/cmSystemTools.cxx | 8 | ||||
-rw-r--r-- | Source/cmSystemTools.h | 3 | ||||
-rw-r--r-- | Source/cmTargetLinkLibrariesCommand.cxx | 4 | ||||
-rw-r--r-- | Source/cmUVHandlePtr.cxx | 54 | ||||
-rw-r--r-- | Source/cmUVHandlePtr.h | 40 | ||||
-rw-r--r-- | Source/cmUVStreambuf.h | 219 | ||||
-rw-r--r-- | Source/cmWorkerPool.cxx | 261 | ||||
-rw-r--r-- | Source/cmWorkerPool.h | 47 | ||||
-rw-r--r-- | Source/cmake.cxx | 27 | ||||
-rw-r--r-- | Source/cmake.h | 20 | ||||
-rw-r--r-- | Source/cmakemain.cxx | 2 |
25 files changed, 698 insertions, 237 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 49f237f..01c6cd7 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -264,6 +264,8 @@ set(SRCS cmGeneratorExpression.h cmGeneratorTarget.cxx cmGeneratorTarget.h + cmGetPipes.cxx + cmGetPipes.h cmGlobalCommonGenerator.cxx cmGlobalCommonGenerator.h cmGlobalGenerator.cxx @@ -386,6 +388,7 @@ set(SRCS cmUuid.cxx cmUVHandlePtr.cxx cmUVHandlePtr.h + cmUVStreambuf.h cmUVSignalHackRAII.h cmVariableWatch.cxx cmVariableWatch.h diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index af246df..1b34ba5 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 14) -set(CMake_VERSION_PATCH 20190422) +set(CMake_VERSION_PATCH 20190502) #set(CMake_VERSION_RC 1) diff --git a/Source/CTest/cmCTestUpdateHandler.cxx b/Source/CTest/cmCTestUpdateHandler.cxx index e3b7e9e..5cfc4a7 100644 --- a/Source/CTest/cmCTestUpdateHandler.cxx +++ b/Source/CTest/cmCTestUpdateHandler.cxx @@ -199,6 +199,10 @@ int cmCTestUpdateHandler::ProcessHandler() xml.Element("UpdateCommand", vc->GetUpdateCommandLine()); xml.Element("UpdateType", cmCTestUpdateHandlerUpdateToString(this->UpdateType)); + std::string changeId = this->CTest->GetCTestConfiguration("ChangeId"); + if (!changeId.empty()) { + xml.Element("ChangeId", changeId); + } bool loadedMods = vc->WriteXML(xml); diff --git a/Source/CTest/cmCTestUploadHandler.cxx b/Source/CTest/cmCTestUploadHandler.cxx index ee9ee91..9efdf70 100644 --- a/Source/CTest/cmCTestUploadHandler.cxx +++ b/Source/CTest/cmCTestUploadHandler.cxx @@ -51,7 +51,7 @@ int cmCTestUploadHandler::ProcessHandler() this->CTest->GetTestModelString()); xml.Attribute("Name", this->CTest->GetCTestConfiguration("Site")); xml.Attribute("Generator", - std::string("ctest") + cmVersion::GetCMakeVersion()); + std::string("ctest-") + cmVersion::GetCMakeVersion()); this->CTest->AddSiteProperties(xml); xml.StartElement("Upload"); diff --git a/Source/CTest/cmProcess.cxx b/Source/CTest/cmProcess.cxx index bfe8d70..a2c30bb 100644 --- a/Source/CTest/cmProcess.cxx +++ b/Source/CTest/cmProcess.cxx @@ -5,61 +5,19 @@ #include "cmCTest.h" #include "cmCTestRunTest.h" #include "cmCTestTestHandler.h" +#include "cmGetPipes.h" #include "cmsys/Process.h" -#include <fcntl.h> #include <iostream> #include <signal.h> #include <string> #if defined(_WIN32) # include "cm_kwiml.h" -#else -# include <unistd.h> #endif #include <utility> #define CM_PROCESS_BUF_SIZE 65536 -#if defined(_WIN32) && !defined(__CYGWIN__) -# include <io.h> - -static int cmProcessGetPipes(int* fds) -{ - SECURITY_ATTRIBUTES attr; - HANDLE readh, writeh; - attr.nLength = sizeof(attr); - attr.lpSecurityDescriptor = nullptr; - attr.bInheritHandle = FALSE; - if (!CreatePipe(&readh, &writeh, &attr, 0)) - return uv_translate_sys_error(GetLastError()); - fds[0] = _open_osfhandle((intptr_t)readh, 0); - fds[1] = _open_osfhandle((intptr_t)writeh, 0); - if (fds[0] == -1 || fds[1] == -1) { - CloseHandle(readh); - CloseHandle(writeh); - return uv_translate_sys_error(GetLastError()); - } - return 0; -} -#else -# include <errno.h> - -static int cmProcessGetPipes(int* fds) -{ - if (pipe(fds) == -1) { - return uv_translate_sys_error(errno); - } - - if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || - fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { - close(fds[0]); - close(fds[1]); - return uv_translate_sys_error(errno); - } - return 0; -} -#endif - cmProcess::cmProcess(cmCTestRunTest& runner) : Runner(runner) , Conv(cmProcessOutput::UTF8, CM_PROCESS_BUF_SIZE) @@ -120,7 +78,7 @@ bool cmProcess::StartProcess(uv_loop_t& loop, std::vector<size_t>* affinity) pipe_reader.init(loop, 0, this); int fds[2] = { -1, -1 }; - status = cmProcessGetPipes(fds); + status = cmGetPipes(fds); if (status != 0) { cmCTestLog(this->Runner.GetCTest(), ERROR_MESSAGE, "Error initializing pipe: " << uv_strerror(status) diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 709feac..003ebdc 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -1523,7 +1523,7 @@ int cmCTest::GenerateCTestNotesOutput(cmXMLWriter& xml, this->Impl->CurrentTag + "-" + this->GetTestModelString()); xml.Attribute("Name", this->GetCTestConfiguration("Site")); xml.Attribute("Generator", - std::string("ctest") + cmVersion::GetCMakeVersion()); + std::string("ctest-") + cmVersion::GetCMakeVersion()); this->AddSiteProperties(xml); xml.StartElement("Notes"); diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index ff6340f..0d9859e 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx @@ -6,11 +6,13 @@ #include "cmsys/Process.h" #include <algorithm> #include <ctype.h> /* isspace */ +#include <iostream> #include <stdio.h> #include "cmAlgorithms.h" #include "cmArgumentParser.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmProcessOutput.h" #include "cmSystemTools.h" @@ -47,6 +49,7 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args, std::string OutputFile; std::string ErrorFile; std::string Timeout; + std::string CommandEcho; bool OutputQuiet = false; bool ErrorQuiet = false; bool OutputStripTrailingWhitespace = false; @@ -57,6 +60,7 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args, static auto const parser = cmArgumentParser<Arguments>{} .Bind("COMMAND"_s, &Arguments::Commands) + .Bind("COMMAND_ECHO"_s, &Arguments::CommandEcho) .Bind("OUTPUT_VARIABLE"_s, &Arguments::OutputVariable) .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable) .Bind("RESULT_VARIABLE"_s, &Arguments::ResultVariable) @@ -117,9 +121,10 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args, return false; } } - // Create a process instance. - cmsysProcess* cp = cmsysProcess_New(); + std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr( + cmsysProcess_New(), cmsysProcess_Delete); + cmsysProcess* cp = cp_ptr.get(); // Set the command sequence. for (std::vector<std::string> const& cmd : arguments.Commands) { @@ -169,6 +174,51 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args, cmsysProcess_SetTimeout(cp, timeout); } + bool echo_stdout = false; + bool echo_stderr = false; + bool echo_output_from_variable = true; + std::string echo_output = + this->Makefile->GetSafeDefinition("CMAKE_EXECUTE_PROCESS_COMMAND_ECHO"); + if (!arguments.CommandEcho.empty()) { + echo_output_from_variable = false; + echo_output = arguments.CommandEcho; + } + + if (!echo_output.empty()) { + if (echo_output == "STDERR") { + echo_stderr = true; + } else if (echo_output == "STDOUT") { + echo_stdout = true; + } else if (echo_output != "NONE") { + std::string error; + if (echo_output_from_variable) { + error = "CMAKE_EXECUTE_PROCESS_COMMAND_ECHO set to '"; + } else { + error = " called with '"; + } + error += echo_output; + error += "' expected STDERR|STDOUT|NONE"; + if (!echo_output_from_variable) { + error += " for COMMAND_ECHO."; + } + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, error); + return true; + } + } + if (echo_stdout || echo_stderr) { + std::string command; + for (auto& cmd : arguments.Commands) { + command += "'"; + command += cmJoin(cmd, "' '"); + command += "'"; + command += "\n"; + } + if (echo_stdout) { + std::cout << command; + } else if (echo_stderr) { + std::cerr << command; + } + } // Start the process. cmsysProcess_Execute(cp); @@ -297,9 +347,6 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args, } } - // Delete the process instance. - cmsysProcess_Delete(cp); - return true; } diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 8b3d9d6..8803830 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -1600,10 +1600,16 @@ static const struct TargetObjectsNode : public cmGeneratorExpressionNode reportError(context, content->GetOriginalExpression(), e.str()); return std::string(); } - if (gt->GetType() != cmStateEnums::OBJECT_LIBRARY) { + cmStateEnums::TargetType type = gt->GetType(); + if (type != cmStateEnums::EXECUTABLE && + type != cmStateEnums::STATIC_LIBRARY && + type != cmStateEnums::SHARED_LIBRARY && + type != cmStateEnums::MODULE_LIBRARY && + type != cmStateEnums::OBJECT_LIBRARY) { std::ostringstream e; e << "Objects of target \"" << tgtName - << "\" referenced but is not an OBJECT library."; + << "\" referenced but is not an allowed library types (EXECUTABLE, " + << "STATIC, SHARED, MODULE, OBJECT)."; reportError(context, content->GetOriginalExpression(), e.str()); return std::string(); } diff --git a/Source/cmGetPipes.cxx b/Source/cmGetPipes.cxx new file mode 100644 index 0000000..ad323f7 --- /dev/null +++ b/Source/cmGetPipes.cxx @@ -0,0 +1,48 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmGetPipes.h" + +#include "cm_uv.h" + +#include <fcntl.h> + +#if defined(_WIN32) && !defined(__CYGWIN__) +# include <io.h> + +int cmGetPipes(int* fds) +{ + SECURITY_ATTRIBUTES attr; + HANDLE readh, writeh; + attr.nLength = sizeof(attr); + attr.lpSecurityDescriptor = nullptr; + attr.bInheritHandle = FALSE; + if (!CreatePipe(&readh, &writeh, &attr, 0)) + return uv_translate_sys_error(GetLastError()); + fds[0] = _open_osfhandle((intptr_t)readh, 0); + fds[1] = _open_osfhandle((intptr_t)writeh, 0); + if (fds[0] == -1 || fds[1] == -1) { + CloseHandle(readh); + CloseHandle(writeh); + return uv_translate_sys_error(GetLastError()); + } + return 0; +} +#else +# include <errno.h> +# include <unistd.h> + +int cmGetPipes(int* fds) +{ + if (pipe(fds) == -1) { + return uv_translate_sys_error(errno); + } + + if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || + fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { + close(fds[0]); + close(fds[1]); + return uv_translate_sys_error(errno); + } + return 0; +} +#endif diff --git a/Source/cmGetPipes.h b/Source/cmGetPipes.h new file mode 100644 index 0000000..2a46b51 --- /dev/null +++ b/Source/cmGetPipes.h @@ -0,0 +1,8 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmGetPipes_h +#define cmGetPipes_h + +int cmGetPipes(int* fds); + +#endif diff --git a/Source/cmMessageCommand.cxx b/Source/cmMessageCommand.cxx index 2724030..5320ec5 100644 --- a/Source/cmMessageCommand.cxx +++ b/Source/cmMessageCommand.cxx @@ -8,6 +8,9 @@ #include "cmMessenger.h" #include "cmRange.h" #include "cmSystemTools.h" +#include "cmake.h" + +#include <cassert> class cmExecutionStatus; @@ -19,49 +22,88 @@ bool cmMessageCommand::InitialPass(std::vector<std::string> const& args, this->SetError("called with incorrect number of arguments"); return false; } - std::vector<std::string>::const_iterator i = args.begin(); + auto i = args.cbegin(); - MessageType type = MessageType::MESSAGE; - bool status = false; - bool fatal = false; + auto type = MessageType::MESSAGE; + auto status = false; + auto fatal = false; + auto level = cmake::LogLevel::LOG_UNDEFINED; if (*i == "SEND_ERROR") { type = MessageType::FATAL_ERROR; + level = cmake::LogLevel::LOG_ERROR; ++i; } else if (*i == "FATAL_ERROR") { fatal = true; type = MessageType::FATAL_ERROR; + level = cmake::LogLevel::LOG_ERROR; ++i; } else if (*i == "WARNING") { type = MessageType::WARNING; + level = cmake::LogLevel::LOG_WARNING; ++i; } else if (*i == "AUTHOR_WARNING") { if (this->Makefile->IsSet("CMAKE_SUPPRESS_DEVELOPER_ERRORS") && !this->Makefile->IsOn("CMAKE_SUPPRESS_DEVELOPER_ERRORS")) { fatal = true; type = MessageType::AUTHOR_ERROR; + level = cmake::LogLevel::LOG_ERROR; } else if (!this->Makefile->IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) { type = MessageType::AUTHOR_WARNING; + level = cmake::LogLevel::LOG_WARNING; } else { return true; } ++i; } else if (*i == "STATUS") { status = true; + level = cmake::LogLevel::LOG_STATUS; + ++i; + } else if (*i == "VERBOSE") { + status = true; + level = cmake::LogLevel::LOG_VERBOSE; + ++i; + } else if (*i == "DEBUG") { + status = true; + level = cmake::LogLevel::LOG_DEBUG; + ++i; + } else if (*i == "TRACE") { + status = true; + level = cmake::LogLevel::LOG_TRACE; ++i; } else if (*i == "DEPRECATION") { if (this->Makefile->IsOn("CMAKE_ERROR_DEPRECATED")) { fatal = true; type = MessageType::DEPRECATION_ERROR; + level = cmake::LogLevel::LOG_ERROR; } else if ((!this->Makefile->IsSet("CMAKE_WARN_DEPRECATED") || this->Makefile->IsOn("CMAKE_WARN_DEPRECATED"))) { type = MessageType::DEPRECATION_WARNING; + level = cmake::LogLevel::LOG_WARNING; } else { return true; } ++i; + } else if (*i == "NOTICE") { + // `NOTICE` message type is going to be output to stderr + level = cmake::LogLevel::LOG_NOTICE; + ++i; + } else { + // Messages w/o any type are `NOTICE`s + level = cmake::LogLevel::LOG_NOTICE; + } + assert("Message log level expected to be set" && + level != cmake::LogLevel::LOG_UNDEFINED); + + auto desiredLevel = this->Makefile->GetCMakeInstance()->GetLogLevel(); + assert("Expected a valid log level here" && + desiredLevel != cmake::LogLevel::LOG_UNDEFINED); + + if (desiredLevel < level) { + // Suppress the message + return true; } - std::string message = cmJoin(cmMakeRange(i, args.end()), std::string()); + auto message = cmJoin(cmMakeRange(i, args.cend()), ""); if (type != MessageType::MESSAGE) { // we've overridden the message type, above, so display it directly diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index 75c5d8a..005c27d 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -1186,6 +1186,7 @@ bool cmQtAutoMocUic::Init(cmMakefile* makefile) num = std::min<unsigned long>(num, ParallelMax); Base_.NumThreads = static_cast<unsigned int>(num); } + WorkerPool_.SetThreadCount(Base_.NumThreads); } // - Files and directories @@ -1482,15 +1483,12 @@ bool cmQtAutoMocUic::Process() if (!CreateDirectories()) { return false; } - - if (!WorkerPool_.Process(Base().NumThreads, this)) { + if (!WorkerPool_.Process(this)) { return false; } - if (JobError_) { return false; } - return SettingsFileWrite(); } diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index 203ee93..dad8821 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -8,6 +8,7 @@ #include "cmGlobalGenerator.h" #include "cmJsonObjectDictionary.h" #include "cmJsonObjects.h" +#include "cmMessageType.h" #include "cmServer.h" #include "cmServerDictionary.h" #include "cmState.h" @@ -600,6 +601,10 @@ cmServerResponse cmServerProtocol1::ProcessConfigure( } int ret = cm->Configure(); + cm->IssueMessage( + MessageType::DEPRECATION_WARNING, + "The 'cmake-server(7)' is deprecated. " + "Please port clients to use the 'cmake-file-api(7)' instead."); if (ret < 0) { return request.ReportError("Configuration failed."); } diff --git a/Source/cmSourceGroupCommand.cxx b/Source/cmSourceGroupCommand.cxx index 2bc4c39..5cdacaa 100644 --- a/Source/cmSourceGroupCommand.cxx +++ b/Source/cmSourceGroupCommand.cxx @@ -80,7 +80,10 @@ std::vector<std::string> prepareFilesPathsForTree( prepared.reserve(filesPaths.size()); for (auto const& filePath : filesPaths) { - prepared.push_back(prepareFilePathForTree(filePath, currentSourceDir)); + // If provided file path is actually not a file, silently ignore it. + if (cmSystemTools::FileExists(filePath, /*isFile=*/true)) { + prepared.push_back(prepareFilePathForTree(filePath, currentSourceDir)); + } } return prepared; diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 212608d..bc853b7 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -168,7 +168,6 @@ bool cmSystemTools::s_RunCommandHideConsole = false; bool cmSystemTools::s_DisableRunCommandOutput = false; bool cmSystemTools::s_ErrorOccured = false; bool cmSystemTools::s_FatalErrorOccured = false; -bool cmSystemTools::s_DisableMessages = false; bool cmSystemTools::s_ForceUnixPaths = false; // replace replace with with as many times as it shows up in source. @@ -326,14 +325,11 @@ void cmSystemTools::Stdout(const std::string& s) void cmSystemTools::Message(const std::string& m, const char* title) { - if (s_DisableMessages) { - return; - } if (s_MessageCallback) { s_MessageCallback(m, title); - return; + } else { + std::cerr << m << std::endl; } - std::cerr << m << std::endl << std::flush; } void cmSystemTools::ReportLastSystemError(const char* msg) diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index a8b2d37..05bd351 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -266,8 +266,6 @@ public: static size_t CalculateCommandLineLengthLimit(); - static void EnableMessages() { s_DisableMessages = false; } - static void DisableMessages() { s_DisableMessages = true; } static void DisableRunCommandOutput() { s_DisableRunCommandOutput = true; } static void EnableRunCommandOutput() { s_DisableRunCommandOutput = false; } static bool GetRunCommandOutput() { return s_DisableRunCommandOutput; } @@ -540,7 +538,6 @@ private: static bool s_RunCommandHideConsole; static bool s_ErrorOccured; static bool s_FatalErrorOccured; - static bool s_DisableMessages; static bool s_DisableRunCommandOutput; }; diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx index 5c7b95c..3883b52 100644 --- a/Source/cmTargetLinkLibrariesCommand.cxx +++ b/Source/cmTargetLinkLibrariesCommand.cxx @@ -456,8 +456,8 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, this->Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { std::string configLib = this->Target->GetDebugGeneratorExpressions(libRef, llt); - if (cmGeneratorExpression::IsValidTargetName(libRef) || - cmGeneratorExpression::Find(libRef) != std::string::npos) { + if (cmGeneratorExpression::IsValidTargetName(lib) || + cmGeneratorExpression::Find(lib) != std::string::npos) { configLib = "$<LINK_ONLY:" + configLib + ">"; } this->Target->AppendProperty("INTERFACE_LINK_LIBRARIES", diff --git a/Source/cmUVHandlePtr.cxx b/Source/cmUVHandlePtr.cxx index fd07d2d..27069ee 100644 --- a/Source/cmUVHandlePtr.cxx +++ b/Source/cmUVHandlePtr.cxx @@ -11,19 +11,59 @@ namespace cm { -static void close_delete(uv_handle_t* h) +struct uv_loop_deleter { - free(h); + void operator()(uv_loop_t* loop) const; +}; + +void uv_loop_deleter::operator()(uv_loop_t* loop) const +{ + uv_run(loop, UV_RUN_DEFAULT); + int result = uv_loop_close(loop); + (void)result; + assert(result >= 0); + free(loop); +} + +int uv_loop_ptr::init(void* data) +{ + this->reset(); + + this->loop.reset(static_cast<uv_loop_t*>(calloc(1, sizeof(uv_loop_t))), + uv_loop_deleter()); + this->loop->data = data; + + return uv_loop_init(this->loop.get()); +} + +void uv_loop_ptr::reset() +{ + this->loop.reset(); +} + +uv_loop_ptr::operator uv_loop_t*() +{ + return this->loop.get(); +} + +uv_loop_t* uv_loop_ptr::operator->() const noexcept +{ + return this->loop.get(); +} + +uv_loop_t* uv_loop_ptr::get() const +{ + return this->loop.get(); } template <typename T> -static void default_delete(T* type_handle) +static void handle_default_delete(T* type_handle) { auto handle = reinterpret_cast<uv_handle_t*>(type_handle); if (handle) { assert(!uv_is_closing(handle)); if (!uv_is_closing(handle)) { - uv_close(handle, &close_delete); + uv_close(handle, [](uv_handle_t* h) { free(h); }); } } } @@ -34,7 +74,7 @@ static void default_delete(T* type_handle) template <typename T> struct uv_handle_deleter { - void operator()(T* type_handle) const { default_delete(type_handle); } + void operator()(T* type_handle) const { handle_default_delete(type_handle); } }; template <typename T> @@ -107,7 +147,7 @@ struct uv_handle_deleter<uv_async_t> void operator()(uv_async_t* handle) { std::lock_guard<std::mutex> lock(*handleMutex); - default_delete(handle); + handle_default_delete(handle); } }; @@ -136,7 +176,7 @@ struct uv_handle_deleter<uv_signal_t> { if (handle) { uv_signal_stop(handle); - default_delete(handle); + handle_default_delete(handle); } } }; diff --git a/Source/cmUVHandlePtr.h b/Source/cmUVHandlePtr.h index 992c429..c09e4bf 100644 --- a/Source/cmUVHandlePtr.h +++ b/Source/cmUVHandlePtr.h @@ -30,7 +30,45 @@ namespace cm { /*** - * RAII class to simplify and insure the safe usage of uv_*_t types. This + * RAII class to simplify and ensure the safe usage of uv_loop_t. This includes + * making sure resources are properly freed. + */ +class uv_loop_ptr +{ +protected: + std::shared_ptr<uv_loop_t> loop; + +public: + uv_loop_ptr(uv_loop_ptr const&) = delete; + uv_loop_ptr& operator=(uv_loop_ptr const&) = delete; + uv_loop_ptr(uv_loop_ptr&&) noexcept; + uv_loop_ptr& operator=(uv_loop_ptr&&) noexcept; + + // Dtor and ctor need to be inline defined like this for default ctors and + // dtors to work. Some compilers do not like '= default' here. + uv_loop_ptr() {} // NOLINT(modernize-use-equals-default) + uv_loop_ptr(std::nullptr_t) {} + ~uv_loop_ptr() { this->reset(); } + + int init(void* data = nullptr); + + /** + * Properly close the handle if needed and sets the inner handle to nullptr + */ + void reset(); + + /** + * Allow less verbose calling of uv_loop_* functions + * @return reinterpreted handle + */ + operator uv_loop_t*(); + + uv_loop_t* get() const; + uv_loop_t* operator->() const noexcept; +}; + +/*** + * RAII class to simplify and ensure the safe usage of uv_*_t types. This * includes making sure resources are properly freed and contains casting * operators which allow for passing into relevant uv_* functions. * diff --git a/Source/cmUVStreambuf.h b/Source/cmUVStreambuf.h new file mode 100644 index 0000000..29e4fde --- /dev/null +++ b/Source/cmUVStreambuf.h @@ -0,0 +1,219 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmUVStreambuf_h +#define cmUVStreambuf_h + +#include "cmUVHandlePtr.h" + +#include "cm_uv.h" + +#include <algorithm> +#include <cstring> +#include <streambuf> +#include <vector> + +/* + * This file is based on example code from: + * + * http://www.voidcn.com/article/p-vjnlygmc-gy.html + * + * The example code was distributed under the following license: + * + * Copyright 2007 Edd Dawson. + * Distributed under the Boost Software License, Version 1.0. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +template <typename CharT, typename Traits = std::char_traits<CharT>> +class cmBasicUVStreambuf : public std::basic_streambuf<CharT, Traits> +{ +public: + cmBasicUVStreambuf(std::size_t bufSize = 256, std::size_t putBack = 8); + ~cmBasicUVStreambuf() override; + + bool is_open() const; + + cmBasicUVStreambuf* open(uv_stream_t* stream); + + cmBasicUVStreambuf* close(); + +protected: + typename cmBasicUVStreambuf::int_type underflow() override; + std::streamsize showmanyc() override; + + // FIXME: Add write support + +private: + uv_stream_t* Stream = nullptr; + void* OldStreamData; + const std::size_t PutBack; + std::vector<CharT> InputBuffer; + bool EndOfFile; + + void StreamReadStartStop(); + + void StreamRead(ssize_t nread); + void HandleAlloc(uv_buf_t* buf); +}; + +template <typename CharT, typename Traits> +cmBasicUVStreambuf<CharT, Traits>::cmBasicUVStreambuf(std::size_t bufSize, + std::size_t putBack) + : PutBack(std::max<std::size_t>(putBack, 1)) + , InputBuffer(std::max<std::size_t>(this->PutBack, bufSize) + this->PutBack) +{ + this->close(); +} + +template <typename CharT, typename Traits> +cmBasicUVStreambuf<CharT, Traits>::~cmBasicUVStreambuf() +{ + this->close(); +} + +template <typename CharT, typename Traits> +bool cmBasicUVStreambuf<CharT, Traits>::is_open() const +{ + return this->Stream != nullptr; +} + +template <typename CharT, typename Traits> +cmBasicUVStreambuf<CharT, Traits>* cmBasicUVStreambuf<CharT, Traits>::open( + uv_stream_t* stream) +{ + this->close(); + this->Stream = stream; + this->EndOfFile = false; + if (this->Stream) { + this->OldStreamData = this->Stream->data; + this->Stream->data = this; + } + this->StreamReadStartStop(); + return this; +} + +template <typename CharT, typename Traits> +cmBasicUVStreambuf<CharT, Traits>* cmBasicUVStreambuf<CharT, Traits>::close() +{ + if (this->Stream) { + uv_read_stop(this->Stream); + this->Stream->data = this->OldStreamData; + } + this->Stream = nullptr; + CharT* readEnd = this->InputBuffer.data() + this->InputBuffer.size(); + this->setg(readEnd, readEnd, readEnd); + return this; +} + +template <typename CharT, typename Traits> +typename cmBasicUVStreambuf<CharT, Traits>::int_type +cmBasicUVStreambuf<CharT, Traits>::underflow() +{ + if (!this->is_open()) { + return Traits::eof(); + } + + if (this->gptr() < this->egptr()) { + return Traits::to_int_type(*this->gptr()); + } + + this->StreamReadStartStop(); + while (this->in_avail() == 0) { + uv_run(this->Stream->loop, UV_RUN_ONCE); + } + if (this->in_avail() == -1) { + return Traits::eof(); + } + return Traits::to_int_type(*this->gptr()); +} + +template <typename CharT, typename Traits> +std::streamsize cmBasicUVStreambuf<CharT, Traits>::showmanyc() +{ + if (!this->is_open() || this->EndOfFile) { + return -1; + } + return 0; +} + +template <typename CharT, typename Traits> +void cmBasicUVStreambuf<CharT, Traits>::StreamReadStartStop() +{ + if (this->Stream) { + uv_read_stop(this->Stream); + if (this->gptr() >= this->egptr()) { + uv_read_start( + this->Stream, + [](uv_handle_t* handle, size_t /* unused */, uv_buf_t* buf) { + auto streambuf = + static_cast<cmBasicUVStreambuf<CharT, Traits>*>(handle->data); + streambuf->HandleAlloc(buf); + }, + [](uv_stream_t* stream2, ssize_t nread, const uv_buf_t* /* unused */) { + auto streambuf = + static_cast<cmBasicUVStreambuf<CharT, Traits>*>(stream2->data); + streambuf->StreamRead(nread); + }); + } + } +} + +template <typename CharT, typename Traits> +void cmBasicUVStreambuf<CharT, Traits>::HandleAlloc(uv_buf_t* buf) +{ + auto size = this->egptr() - this->gptr(); + std::memmove(this->InputBuffer.data(), this->gptr(), + this->egptr() - this->gptr()); + this->setg(this->InputBuffer.data(), this->InputBuffer.data(), + this->InputBuffer.data() + size); + buf->base = this->egptr(); +#ifdef _WIN32 +# define BUF_LEN_TYPE ULONG +#else +# define BUF_LEN_TYPE size_t +#endif + buf->len = BUF_LEN_TYPE( + (this->InputBuffer.data() + this->InputBuffer.size() - this->egptr()) * + sizeof(CharT)); +#undef BUF_LEN_TYPE +} + +template <typename CharT, typename Traits> +void cmBasicUVStreambuf<CharT, Traits>::StreamRead(ssize_t nread) +{ + if (nread > 0) { + this->setg(this->eback(), this->gptr(), + this->egptr() + nread / sizeof(CharT)); + uv_read_stop(this->Stream); + } else if (nread < 0 || nread == UV_EOF) { + this->EndOfFile = true; + uv_read_stop(this->Stream); + } +} + +using cmUVStreambuf = cmBasicUVStreambuf<char>; + +#endif diff --git a/Source/cmWorkerPool.cxx b/Source/cmWorkerPool.cxx index 464182c..cbf070e 100644 --- a/Source/cmWorkerPool.cxx +++ b/Source/cmWorkerPool.cxx @@ -371,137 +371,62 @@ void cmUVReadOnlyProcess::UVTryFinish() } /** - * @brief Private worker pool internals + * @brief Worker pool worker thread */ -class cmWorkerPoolInternal +class cmWorkerPoolWorker { public: - // -- Types - - /** - * @brief Worker thread - */ - class WorkerT - { - public: - WorkerT(unsigned int index); - ~WorkerT(); - - WorkerT(WorkerT const&) = delete; - WorkerT& operator=(WorkerT const&) = delete; - - /** - * Start the thread - */ - void Start(cmWorkerPoolInternal* internal); - - /** - * @brief Run an external process - */ - bool RunProcess(cmWorkerPool::ProcessResultT& result, - std::vector<std::string> const& command, - std::string const& workingDirectory); - - // -- Accessors - unsigned int Index() const { return Index_; } - cmWorkerPool::JobHandleT& JobHandle() { return JobHandle_; } - - private: - // -- Libuv callbacks - static void UVProcessStart(uv_async_t* handle); - void UVProcessFinished(); - - private: - //! @brief Job handle - cmWorkerPool::JobHandleT JobHandle_; - //! @brief Worker index - unsigned int Index_; - // -- Process management - struct - { - std::mutex Mutex; - cm::uv_async_ptr Request; - std::condition_variable Condition; - std::unique_ptr<cmUVReadOnlyProcess> ROP; - } Proc_; - // -- System thread - std::thread Thread_; - }; - -public: - // -- Constructors - cmWorkerPoolInternal(cmWorkerPool* pool); - ~cmWorkerPoolInternal(); - - /** - * @brief Runs the libuv loop - */ - bool Process(); + cmWorkerPoolWorker(uv_loop_t& uvLoop); + ~cmWorkerPoolWorker(); - /** - * @brief Clear queue and abort threads - */ - void Abort(); + cmWorkerPoolWorker(cmWorkerPoolWorker const&) = delete; + cmWorkerPoolWorker& operator=(cmWorkerPoolWorker const&) = delete; /** - * @brief Push a job to the queue and notify a worker + * Set the internal thread */ - bool PushJob(cmWorkerPool::JobHandleT&& jobHandle); + void SetThread(std::thread&& aThread) { Thread_ = std::move(aThread); } /** - * @brief Worker thread main loop method + * Run an external process */ - void Work(WorkerT* worker); - - // -- Request slots - static void UVSlotBegin(uv_async_t* handle); - static void UVSlotEnd(uv_async_t* handle); - -public: - // -- UV loop -#ifdef CMAKE_UV_SIGNAL_HACK - std::unique_ptr<cmUVSignalHackRAII> UVHackRAII; -#endif - std::unique_ptr<uv_loop_t> UVLoop; - cm::uv_async_ptr UVRequestBegin; - cm::uv_async_ptr UVRequestEnd; + bool RunProcess(cmWorkerPool::ProcessResultT& result, + std::vector<std::string> const& command, + std::string const& workingDirectory); - // -- Thread pool and job queue - std::mutex Mutex; - bool Aborting = false; - bool FenceProcessing = false; - unsigned int WorkersRunning = 0; - unsigned int WorkersIdle = 0; - unsigned int JobsProcessing = 0; - std::deque<cmWorkerPool::JobHandleT> Queue; - std::condition_variable Condition; - std::vector<std::unique_ptr<WorkerT>> Workers; +private: + // -- Libuv callbacks + static void UVProcessStart(uv_async_t* handle); + void UVProcessFinished(); - // -- References - cmWorkerPool* Pool = nullptr; +private: + // -- Process management + struct + { + std::mutex Mutex; + cm::uv_async_ptr Request; + std::condition_variable Condition; + std::unique_ptr<cmUVReadOnlyProcess> ROP; + } Proc_; + // -- System thread + std::thread Thread_; }; -cmWorkerPoolInternal::WorkerT::WorkerT(unsigned int index) - : Index_(index) +cmWorkerPoolWorker::cmWorkerPoolWorker(uv_loop_t& uvLoop) { + Proc_.Request.init(uvLoop, &cmWorkerPoolWorker::UVProcessStart, this); } -cmWorkerPoolInternal::WorkerT::~WorkerT() +cmWorkerPoolWorker::~cmWorkerPoolWorker() { if (Thread_.joinable()) { Thread_.join(); } } -void cmWorkerPoolInternal::WorkerT::Start(cmWorkerPoolInternal* internal) -{ - Proc_.Request.init(*(internal->UVLoop), &WorkerT::UVProcessStart, this); - Thread_ = std::thread(&cmWorkerPoolInternal::Work, internal, this); -} - -bool cmWorkerPoolInternal::WorkerT::RunProcess( - cmWorkerPool::ProcessResultT& result, - std::vector<std::string> const& command, std::string const& workingDirectory) +bool cmWorkerPoolWorker::RunProcess(cmWorkerPool::ProcessResultT& result, + std::vector<std::string> const& command, + std::string const& workingDirectory) { if (command.empty()) { return false; @@ -524,9 +449,9 @@ bool cmWorkerPoolInternal::WorkerT::RunProcess( return !result.error(); } -void cmWorkerPoolInternal::WorkerT::UVProcessStart(uv_async_t* handle) +void cmWorkerPoolWorker::UVProcessStart(uv_async_t* handle) { - auto* wrk = reinterpret_cast<WorkerT*>(handle->data); + auto* wrk = reinterpret_cast<cmWorkerPoolWorker*>(handle->data); bool startFailed = false; { auto& Proc = wrk->Proc_; @@ -542,7 +467,7 @@ void cmWorkerPoolInternal::WorkerT::UVProcessStart(uv_async_t* handle) } } -void cmWorkerPoolInternal::WorkerT::UVProcessFinished() +void cmWorkerPoolWorker::UVProcessFinished() { { std::lock_guard<std::mutex> lock(Proc_.Mutex); @@ -554,6 +479,65 @@ void cmWorkerPoolInternal::WorkerT::UVProcessFinished() Proc_.Condition.notify_one(); } +/** + * @brief Private worker pool internals + */ +class cmWorkerPoolInternal +{ +public: + // -- Constructors + cmWorkerPoolInternal(cmWorkerPool* pool); + ~cmWorkerPoolInternal(); + + /** + * Runs the libuv loop. + */ + bool Process(); + + /** + * Clear queue and abort threads. + */ + void Abort(); + + /** + * Push a job to the queue and notify a worker. + */ + bool PushJob(cmWorkerPool::JobHandleT&& jobHandle); + + /** + * Worker thread main loop method. + */ + void Work(unsigned int workerIndex); + + // -- Request slots + static void UVSlotBegin(uv_async_t* handle); + static void UVSlotEnd(uv_async_t* handle); + +public: + // -- UV loop +#ifdef CMAKE_UV_SIGNAL_HACK + std::unique_ptr<cmUVSignalHackRAII> UVHackRAII; +#endif + std::unique_ptr<uv_loop_t> UVLoop; + cm::uv_async_ptr UVRequestBegin; + cm::uv_async_ptr UVRequestEnd; + + // -- Thread pool and job queue + std::mutex Mutex; + bool Processing = false; + bool Aborting = false; + bool FenceProcessing = false; + unsigned int WorkersRunning = 0; + unsigned int WorkersIdle = 0; + unsigned int JobsProcessing = 0; + std::deque<cmWorkerPool::JobHandleT> Queue; + std::condition_variable Condition; + std::vector<std::unique_ptr<cmWorkerPoolWorker>> Workers; + + // -- References + cmWorkerPool* Pool = nullptr; +}; + void cmWorkerPool::ProcessResultT::reset() { ExitStatus = 0; @@ -591,7 +575,8 @@ cmWorkerPoolInternal::~cmWorkerPoolInternal() bool cmWorkerPoolInternal::Process() { - // Reset state + // Reset state flags + Processing = true; Aborting = false; // Initialize libuv asynchronous request UVRequestBegin.init(*UVLoop, &cmWorkerPoolInternal::UVSlotBegin, this); @@ -599,23 +584,27 @@ bool cmWorkerPoolInternal::Process() // Send begin request UVRequestBegin.send(); // Run libuv loop - return (uv_run(UVLoop.get(), UV_RUN_DEFAULT) == 0); + bool success = (uv_run(UVLoop.get(), UV_RUN_DEFAULT) == 0); + // Update state flags + Processing = false; + Aborting = false; + return success; } void cmWorkerPoolInternal::Abort() { - bool firstCall = false; + bool notifyThreads = false; // Clear all jobs and set abort flag { std::lock_guard<std::mutex> guard(Mutex); - if (!Aborting) { + if (Processing && !Aborting) { // Register abort and clear queue Aborting = true; Queue.clear(); - firstCall = true; + notifyThreads = true; } } - if (firstCall) { + if (notifyThreads) { // Wake threads Condition.notify_all(); } @@ -627,15 +616,13 @@ inline bool cmWorkerPoolInternal::PushJob(cmWorkerPool::JobHandleT&& jobHandle) if (Aborting) { return false; } - // Append the job to the queue Queue.emplace_back(std::move(jobHandle)); - // Notify an idle worker if there's one if (WorkersIdle != 0) { Condition.notify_one(); } - + // Return success return true; } @@ -648,11 +635,13 @@ void cmWorkerPoolInternal::UVSlotBegin(uv_async_t* handle) // Create workers gint.Workers.reserve(num); for (unsigned int ii = 0; ii != num; ++ii) { - gint.Workers.emplace_back(cm::make_unique<WorkerT>(ii)); + gint.Workers.emplace_back( + cm::make_unique<cmWorkerPoolWorker>(*gint.UVLoop)); } - // Start workers - for (auto& wrk : gint.Workers) { - wrk->Start(&gint); + // Start worker threads + for (unsigned int ii = 0; ii != num; ++ii) { + gint.Workers[ii]->SetThread( + std::thread(&cmWorkerPoolInternal::Work, &gint, ii)); } } // Destroy begin request @@ -668,8 +657,9 @@ void cmWorkerPoolInternal::UVSlotEnd(uv_async_t* handle) gint.UVRequestEnd.reset(); } -void cmWorkerPoolInternal::Work(WorkerT* worker) +void cmWorkerPoolInternal::Work(unsigned int workerIndex) { + cmWorkerPool::JobHandleT jobHandle; std::unique_lock<std::mutex> uLock(Mutex); // Increment running workers count ++WorkersRunning; @@ -698,15 +688,15 @@ void cmWorkerPoolInternal::Work(WorkerT* worker) } // Pop next job from queue - worker->JobHandle() = std::move(Queue.front()); + jobHandle = std::move(Queue.front()); Queue.pop_front(); // Unlocked scope for job processing ++JobsProcessing; { uLock.unlock(); - worker->JobHandle()->Work(Pool, worker->Index()); // Process job - worker->JobHandle().reset(); // Destroy job + jobHandle->Work(Pool, workerIndex); // Process job + jobHandle.reset(); // Destroy job uLock.lock(); } --JobsProcessing; @@ -743,19 +733,22 @@ cmWorkerPool::cmWorkerPool() cmWorkerPool::~cmWorkerPool() = default; -bool cmWorkerPool::Process(unsigned int threadCount, void* userData) +void cmWorkerPool::SetThreadCount(unsigned int threadCount) +{ + if (!Int_->Processing) { + ThreadCount_ = (threadCount > 0) ? threadCount : 1u; + } +} + +bool cmWorkerPool::Process(void* userData) { // Setup user data UserData_ = userData; - ThreadCount_ = (threadCount > 0) ? threadCount : 1u; - // Run libuv loop bool success = Int_->Process(); - // Clear user data UserData_ = nullptr; - ThreadCount_ = 0; - + // Return return success; } diff --git a/Source/cmWorkerPool.h b/Source/cmWorkerPool.h index 71c7d84..f08bb4f 100644 --- a/Source/cmWorkerPool.h +++ b/Source/cmWorkerPool.h @@ -50,12 +50,12 @@ public: JobT& operator=(JobT const&) = delete; /** - * @brief Virtual destructor. + * Virtual destructor. */ virtual ~JobT(); /** - * @brief Fence job flag + * Fence job flag * * Fence jobs require that: * - all jobs before in the queue have been processed @@ -66,7 +66,7 @@ public: protected: /** - * @brief Protected default constructor + * Protected default constructor */ JobT(bool fence = false) : Fence_(fence) @@ -125,12 +125,12 @@ public: }; /** - * @brief Job handle type + * Job handle type */ typedef std::unique_ptr<JobT> JobHandleT; /** - * @brief Fence job base class + * Fence job base class */ class JobFenceT : public JobT { @@ -144,8 +144,9 @@ public: }; /** - * @brief Fence job that aborts the worker pool. - * This class is useful as the last job in the job queue. + * Fence job that aborts the worker pool. + * + * Useful as the last job in the job queue. */ class JobEndT : JobFenceT { @@ -160,23 +161,29 @@ public: ~cmWorkerPool(); /** - * @brief Blocking function that starts threads to process all Jobs in - * the queue. + * Number of worker threads. + */ + unsigned int ThreadCount() const { return ThreadCount_; } + + /** + * Set the number of worker threads. * - * This method blocks until a job calls the Abort() method. - * @arg threadCount Number of threads to process jobs. - * @arg userData Common user data pointer available in all Jobs. + * Calling this method during Process() has no effect. */ - bool Process(unsigned int threadCount, void* userData = nullptr); + void SetThreadCount(unsigned int threadCount); /** - * Number of worker threads passed to Process(). - * Only valid during Process(). + * Blocking function that starts threads to process all Jobs in the queue. + * + * This method blocks until a job calls the Abort() method. + * @arg threadCount Number of threads to process jobs. + * @arg userData Common user data pointer available in all Jobs. */ - unsigned int ThreadCount() const { return ThreadCount_; } + bool Process(void* userData = nullptr); /** * User data reference passed to Process(). + * * Only valid during Process(). */ void* UserData() const { return UserData_; } @@ -184,14 +191,14 @@ public: // -- Job processing interface /** - * @brief Clears the job queue and aborts all worker threads. + * Clears the job queue and aborts all worker threads. * * This method is thread safe and can be called from inside a job. */ void Abort(); /** - * @brief Push job to the queue. + * Push job to the queue. * * This method is thread safe and can be called from inside a job or before * Process(). @@ -199,7 +206,7 @@ public: bool PushJob(JobHandleT&& jobHandle); /** - * @brief Push job to the queue + * Push job to the queue * * This method is thread safe and can be called from inside a job or before * Process(). @@ -212,7 +219,7 @@ public: private: void* UserData_ = nullptr; - unsigned int ThreadCount_ = 0; + unsigned int ThreadCount_ = 1; std::unique_ptr<cmWorkerPoolInternal> Int_; }; diff --git a/Source/cmake.cxx b/Source/cmake.cxx index fc24ac0..121d12d 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -718,6 +718,14 @@ void cmake::SetArgs(const std::vector<std::string>& args) } else if (arg.find("--debug-output", 0) == 0) { std::cout << "Running with debug output on.\n"; this->SetDebugOutputOn(true); + } else if (arg.find("--loglevel=", 0) == 0) { + const auto logLevel = + StringToLogLevel(arg.substr(sizeof("--loglevel=") - 1)); + if (logLevel == LogLevel::LOG_UNDEFINED) { + cmSystemTools::Error("Invalid level specified for --loglevel"); + return; + } + this->SetLogLevel(logLevel); } else if (arg.find("--trace-expand", 0) == 0) { std::cout << "Running with expanded trace output on.\n"; this->SetTrace(true); @@ -828,6 +836,25 @@ void cmake::SetArgs(const std::vector<std::string>& args) } } +cmake::LogLevel cmake::StringToLogLevel(const std::string& levelStr) +{ + using LevelsPair = std::pair<std::string, LogLevel>; + static const std::vector<LevelsPair> levels = { + { "error", LogLevel::LOG_ERROR }, { "warning", LogLevel::LOG_WARNING }, + { "notice", LogLevel::LOG_NOTICE }, { "status", LogLevel::LOG_STATUS }, + { "verbose", LogLevel::LOG_VERBOSE }, { "debug", LogLevel::LOG_DEBUG }, + { "trace", LogLevel::LOG_TRACE } + }; + + const auto levelStrLowCase = cmSystemTools::LowerCase(levelStr); + + const auto it = std::find_if(levels.cbegin(), levels.cend(), + [&levelStrLowCase](const LevelsPair& p) { + return p.first == levelStrLowCase; + }); + return (it != levels.cend()) ? it->second : LogLevel::LOG_UNDEFINED; +} + void cmake::SetDirectoriesFromFile(const char* arg) { // Check if the argument refers to a CMakeCache.txt or diff --git a/Source/cmake.h b/Source/cmake.h index 8b4b396..4a345cf 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -96,6 +96,19 @@ public: FIND_PACKAGE_MODE }; + /** \brief Define log level constants. */ + enum LogLevel + { + LOG_UNDEFINED, + LOG_ERROR, + LOG_WARNING, + LOG_NOTICE, + LOG_STATUS, + LOG_VERBOSE, + LOG_DEBUG, + LOG_TRACE + }; + struct GeneratorInfo { std::string name; @@ -331,6 +344,11 @@ public: */ cmFileTimeCache* GetFileTimeCache() { return this->FileTimeCache; } + // Get the selected log level for `message()` commands during the cmake run. + LogLevel GetLogLevel() const { return this->MessageLogLevel; } + void SetLogLevel(LogLevel level) { this->MessageLogLevel = level; } + static LogLevel StringToLogLevel(const std::string& levelStr); + // Do we want debug output during the cmake run. bool GetDebugOutput() { return this->DebugOutput; } void SetDebugOutputOn(bool b) { this->DebugOutput = b; } @@ -524,6 +542,8 @@ private: std::vector<std::string> TraceOnlyThisSources; + LogLevel MessageLogLevel = LogLevel::LOG_STATUS; + void UpdateConversionPathTable(); // Print a list of valid generators to stderr. diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index d70c9d9..5631d10 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -95,6 +95,8 @@ static const char* cmDocumentationOptions[][2] = { "Generate graphviz of dependencies, see " "CMakeGraphVizOptions.cmake for more." }, { "--system-information [file]", "Dump information about this system." }, + { "--loglevel=<error|warn|notice|status|verbose|debug|trace>", + "Set the verbosity of messages from CMake files." }, { "--debug-trycompile", "Do not delete the try_compile build tree. Only " "useful on one try_compile at a time." }, |