summaryrefslogtreecommitdiff
path: root/Source/cmQtAutoGenerator.h
diff options
context:
space:
mode:
authorSebastian Holtermann <sebholt@xwmw.org>2018-01-03 16:59:40 +0100
committerSebastian Holtermann <sebholt@xwmw.org>2018-01-17 17:23:49 +0100
commita008578deebfa71b38786281450e3d9cf84f5847 (patch)
tree70173006b0adc6a62626e59d9cc653826f950336 /Source/cmQtAutoGenerator.h
parent488baaf0d6144cd7cedfbbd3bb6eadcc72257fc4 (diff)
downloadcmake-a008578deebfa71b38786281450e3d9cf84f5847.tar.gz
Autogen: Process files concurrently in AUTOMOC and AUTOUIC
This introduces concurrent thread processing in the `_autogen` target wich processes AUTOMOC and AUTOUIC. Source file parsing is distributed among the threads by using a job queue from which the threads pull new parse jobs. Each thread might start an independent ``moc`` or ``uic`` process. Altogether this roughly speeds up the AUTOMOC and AUTOUIC build process by the number of physical CPUs on the host system. The exact number of threads to start in the `_autogen` target is controlled by the new AUTOGEN_PARALLEL target property which is initialized by the new CMAKE_AUTOGEN_PARALLEL variable. If AUTOGEN_PARALLEL is empty or unset (which is the default) the thread count is set to the number of physical CPUs on the host system. The AUTOMOC/AUTOUIC generator and the AUTORCC generator are refactored to use a libuv loop internally. Closes #17422.
Diffstat (limited to 'Source/cmQtAutoGenerator.h')
-rw-r--r--Source/cmQtAutoGenerator.h308
1 files changed, 260 insertions, 48 deletions
diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h
index 285340d398..6b35234254 100644
--- a/Source/cmQtAutoGenerator.h
+++ b/Source/cmQtAutoGenerator.h
@@ -6,71 +6,283 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include "cmQtAutoGen.h"
+#include "cmUVHandlePtr.h"
+#include "cm_uv.h"
+#include <array>
+#include <functional>
+#include <mutex>
+#include <stddef.h>
+#include <stdint.h>
#include <string>
#include <vector>
class cmMakefile;
-class cmQtAutoGenerator
+/// @brief Base class for QtAutoGen gernerators
+class cmQtAutoGenerator : public cmQtAutoGen
{
CM_DISABLE_COPY(cmQtAutoGenerator)
public:
+ // -- Types
+
+ /// @brief Thread safe logging
+ class Logger
+ {
+ public:
+ // -- Verbosity
+ bool Verbose() const { return this->Verbose_; }
+ void SetVerbose(bool value);
+ bool ColorOutput() const { return this->ColorOutput_; }
+ void SetColorOutput(bool value);
+ // -- Log info
+ void Info(GeneratorT genType, std::string const& message);
+ // -- Log warning
+ void Warning(GeneratorT genType, std::string const& message);
+ void WarningFile(GeneratorT genType, std::string const& filename,
+ std::string const& message);
+ // -- Log error
+ void Error(GeneratorT genType, std::string const& message);
+ void ErrorFile(GeneratorT genType, std::string const& filename,
+ std::string const& message);
+ void ErrorCommand(GeneratorT genType, std::string const& message,
+ std::vector<std::string> const& command,
+ std::string const& output);
+
+ private:
+ static std::string HeadLine(std::string const& title);
+
+ private:
+ std::mutex Mutex_;
+ bool volatile Verbose_ = false;
+ bool volatile ColorOutput_ = false;
+ };
+
+ /// @brief Thread safe file system interface
+ class FileSystem
+ {
+ public:
+ FileSystem(Logger* log)
+ : Log_(log)
+ {
+ }
+
+ Logger* Log() const { return Log_; }
+ std::string RealPath(std::string const& filename);
+ bool FileExists(std::string const& filename);
+ bool FileIsOlderThan(std::string const& buildFile,
+ std::string const& sourceFile,
+ std::string* error = nullptr);
+
+ bool FileRead(std::string& content, std::string const& filename,
+ std::string* error = nullptr);
+ /// @brief Error logging version
+ bool FileRead(GeneratorT genType, std::string& content,
+ std::string const& filename);
+
+ bool FileWrite(std::string const& filename, std::string const& content,
+ std::string* error = nullptr);
+ /// @brief Error logging version
+ bool FileWrite(GeneratorT genType, std::string const& filename,
+ std::string const& content);
+
+ bool FileDiffers(std::string const& filename, std::string const& content);
+
+ bool FileRemove(std::string const& filename);
+ bool Touch(std::string const& filename);
+
+ bool MakeDirectory(std::string const& dirname);
+ /// @brief Error logging version
+ bool MakeDirectory(GeneratorT genType, std::string const& dirname);
+
+ bool MakeParentDirectory(std::string const& filename);
+ /// @brief Error logging version
+ bool MakeParentDirectory(GeneratorT genType, std::string const& filename);
+
+ private:
+ std::mutex Mutex_;
+ Logger* Log_;
+ };
+
+ /// @brief Return value and output of an external process
+ struct ProcessResultT
+ {
+ void reset();
+ bool error() const
+ {
+ return (ExitStatus != 0) || (TermSignal != 0) || !ErrorMessage.empty();
+ }
+
+ std::int64_t ExitStatus = 0;
+ int TermSignal = 0;
+ std::string StdOut;
+ std::string StdErr;
+ std::string ErrorMessage;
+ };
+
+ /// @brief External process management class
+ struct ReadOnlyProcessT
+ {
+ // -- Types
+
+ /// @brief libuv pipe buffer class
+ class PipeT
+ {
+ public:
+ int init(uv_loop_t* uv_loop, ReadOnlyProcessT* process);
+ int startRead(std::string* target);
+ void reset();
+
+ // -- Libuv casts
+ uv_pipe_t* uv_pipe() { return UVPipe_.get(); }
+ uv_stream_t* uv_stream()
+ {
+ return reinterpret_cast<uv_stream_t*>(uv_pipe());
+ }
+ uv_handle_t* uv_handle()
+ {
+ return reinterpret_cast<uv_handle_t*>(uv_pipe());
+ }
+
+ // -- Libuv callbacks
+ static void UVAlloc(uv_handle_t* handle, size_t suggestedSize,
+ uv_buf_t* buf);
+ static void UVData(uv_stream_t* stream, ssize_t nread,
+ const uv_buf_t* buf);
+
+ private:
+ ReadOnlyProcessT* Process_ = nullptr;
+ std::string* Target_ = nullptr;
+ std::vector<char> Buffer_;
+ cm::uv_pipe_ptr UVPipe_;
+ };
+
+ /// @brief Process settings
+ struct SetupT
+ {
+ std::string WorkingDirectory;
+ std::vector<std::string> Command;
+ ProcessResultT* Result = nullptr;
+ bool MergedOutput = false;
+ };
+
+ // -- Constructor
+ ReadOnlyProcessT() = default;
+
+ // -- Const accessors
+ const SetupT& Setup() const { return Setup_; }
+ ProcessResultT* Result() const { return Setup_.Result; }
+ bool IsStarted() const { return IsStarted_; }
+ bool IsFinished() const { return IsFinished_; }
+
+ // -- Runtime
+ void setup(ProcessResultT* result, bool mergedOutput,
+ std::vector<std::string> const& command,
+ std::string const& workingDirectory = std::string());
+ bool start(uv_loop_t* uv_loop, std::function<void()>&& finishedCallback);
+
+ private:
+ // -- Friends
+ friend class PipeT;
+ // -- Libuv callbacks
+ static void UVExit(uv_process_t* handle, int64_t exitStatus,
+ int termSignal);
+ void UVTryFinish();
+
+ // -- Setup
+ SetupT Setup_;
+ // -- Runtime
+ bool IsStarted_ = false;
+ bool IsFinished_ = false;
+ std::function<void()> FinishedCallback_;
+ std::vector<const char*> CommandPtr_;
+ std::array<uv_stdio_container_t, 3> UVOptionsStdIO_;
+ uv_process_options_t UVOptions_;
+ cm::uv_process_ptr UVProcess_;
+ PipeT UVPipeOut_;
+ PipeT UVPipeErr_;
+ };
+
+#if defined(CMAKE_USE_SYSTEM_LIBUV) && !defined(_WIN32) && \
+ UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR < 19
+#define CMAKE_UV_SIGNAL_HACK
+ /*
+ libuv does not use SA_RESTART on its signal handler, but C++ streams
+ depend on it for reliable i/o operations. This RAII helper convinces
+ libuv to install its handler, and then revises the handler to add the
+ SA_RESTART flag. We use a distinct uv loop that never runs to avoid
+ ever really getting a callback. libuv may fill the hack loop's signal
+ pipe and then stop writing, but that won't break any real loops.
+ */
+ class cmUVSignalHackRAII
+ {
+ uv_loop_t HackLoop;
+ cm::uv_signal_ptr HackSignal;
+ static void HackCB(uv_signal_t*, int) {}
+ public:
+ cmUVSignalHackRAII()
+ {
+ uv_loop_init(&this->HackLoop);
+ this->HackSignal.init(this->HackLoop);
+ this->HackSignal.start(HackCB, SIGCHLD);
+ struct sigaction hack_sa;
+ sigaction(SIGCHLD, NULL, &hack_sa);
+ if (!(hack_sa.sa_flags & SA_RESTART)) {
+ hack_sa.sa_flags |= SA_RESTART;
+ sigaction(SIGCHLD, &hack_sa, NULL);
+ }
+ }
+ ~cmUVSignalHackRAII()
+ {
+ this->HackSignal.stop();
+ uv_loop_close(&this->HackLoop);
+ }
+ };
+#endif
+
+public:
+ // -- Constructors
cmQtAutoGenerator();
- virtual ~cmQtAutoGenerator() = default;
+ virtual ~cmQtAutoGenerator();
+
+ // -- Run
bool Run(std::string const& infoFile, std::string const& config);
- std::string const& GetInfoFile() const { return InfoFile; }
- std::string const& GetInfoDir() const { return InfoDir; }
- std::string const& GetInfoConfig() const { return InfoConfig; }
- bool GetVerbose() const { return Verbose; }
+ // -- Accessors
+ // Logging
+ Logger& Log() { return Logger_; }
+ // File System
+ FileSystem& FileSys() { return FileSys_; }
+ // InfoFile
+ std::string const& InfoFile() const { return InfoFile_; }
+ std::string const& InfoDir() const { return InfoDir_; }
+ std::string const& InfoConfig() const { return InfoConfig_; }
+ // libuv loop
+ uv_loop_t* UVLoop() { return UVLoop_.get(); }
+ cm::uv_async_ptr& UVRequest() { return UVRequest_; }
-protected:
- // -- Central processing
- virtual bool Process(cmMakefile* makefile) = 0;
-
- // -- Log info
- void LogBold(std::string const& message) const;
- void LogInfo(cmQtAutoGen::Generator genType,
- std::string const& message) const;
- // -- Log warning
- void LogWarning(cmQtAutoGen::Generator genType,
- std::string const& message) const;
- void LogFileWarning(cmQtAutoGen::Generator genType,
- std::string const& filename,
- std::string const& message) const;
- // -- Log error
- void LogError(cmQtAutoGen::Generator genType,
- std::string const& message) const;
- void LogFileError(cmQtAutoGen::Generator genType,
- std::string const& filename,
- std::string const& message) const;
- void LogCommandError(cmQtAutoGen::Generator genType,
- std::string const& message,
- std::vector<std::string> const& command,
- std::string const& output) const;
// -- Utility
- bool MakeParentDirectory(cmQtAutoGen::Generator genType,
- std::string const& filename) const;
- bool FileIsOlderThan(std::string const& buildFile,
- std::string const& sourceFile,
- std::string* error = nullptr);
- bool FileRead(std::string& content, std::string const& filename,
- std::string* error = nullptr);
- bool FileWrite(cmQtAutoGen::Generator genType, std::string const& filename,
- std::string const& content);
- bool FileDiffers(std::string const& filename, std::string const& content);
- bool RunCommand(std::vector<std::string> const& command,
- std::string& output) const;
+ static std::string SettingsFind(std::string const& content, const char* key);
+
+protected:
+ // -- Abstract processing interface
+ virtual bool Init(cmMakefile* makefile) = 0;
+ virtual bool Process() = 0;
private:
+ // -- Logging
+ Logger Logger_;
+ FileSystem FileSys_;
// -- Info settings
- std::string InfoFile;
- std::string InfoDir;
- std::string InfoConfig;
- // -- Settings
- bool Verbose;
- bool ColorOutput;
+ std::string InfoFile_;
+ std::string InfoDir_;
+ std::string InfoConfig_;
+// -- libuv loop
+#ifdef CMAKE_UV_SIGNAL_HACK
+ std::unique_ptr<cmUVSignalHackRAII> UVHackRAII_;
+#endif
+ std::unique_ptr<uv_loop_t> UVLoop_;
+ cm::uv_async_ptr UVRequest_;
};
#endif