mirror of
https://github.com/Kitware/CMake.git
synced 2026-05-06 22:30:07 -05:00
Merge topic 'autogen_moc_uic_single_job_queue'
58f04b6ecfAutogen: Add ManySources testa3f062091fAutogen: Rename `cmQtAutoGeneratorMocUic` class to `cmQtAutoMocUic`8cb26a0a2aAutogen: Factor out concurrency framework to cmWorkerPool class Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !3224
This commit is contained in:
@@ -350,8 +350,8 @@ set(SRCS
|
||||
cmQtAutoGenGlobalInitializer.h
|
||||
cmQtAutoGenInitializer.cxx
|
||||
cmQtAutoGenInitializer.h
|
||||
cmQtAutoGeneratorMocUic.cxx
|
||||
cmQtAutoGeneratorMocUic.h
|
||||
cmQtAutoMocUic.cxx
|
||||
cmQtAutoMocUic.h
|
||||
cmQtAutoRcc.cxx
|
||||
cmQtAutoRcc.h
|
||||
cmRST.cxx
|
||||
@@ -391,6 +391,8 @@ set(SRCS
|
||||
cmVariableWatch.h
|
||||
cmVersion.cxx
|
||||
cmVersion.h
|
||||
cmWorkerPool.cxx
|
||||
cmWorkerPool.h
|
||||
cmWorkingDirectory.cxx
|
||||
cmWorkingDirectory.h
|
||||
cmXMLParser.cxx
|
||||
|
||||
@@ -14,12 +14,6 @@
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmake.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
// -- Class methods
|
||||
|
||||
cmQtAutoGenerator::Logger::Logger()
|
||||
{
|
||||
// Initialize logger
|
||||
@@ -431,232 +425,6 @@ bool cmQtAutoGenerator::FileSystem::MakeParentDirectory(
|
||||
return cmQtAutoGenerator::MakeParentDirectory(filename);
|
||||
}
|
||||
|
||||
int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::init(uv_loop_t* uv_loop,
|
||||
ReadOnlyProcessT* process)
|
||||
{
|
||||
Process_ = process;
|
||||
Target_ = nullptr;
|
||||
return UVPipe_.init(*uv_loop, 0, this);
|
||||
}
|
||||
|
||||
int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::startRead(std::string* target)
|
||||
{
|
||||
Target_ = target;
|
||||
return uv_read_start(uv_stream(), &PipeT::UVAlloc, &PipeT::UVData);
|
||||
}
|
||||
|
||||
void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::reset()
|
||||
{
|
||||
Process_ = nullptr;
|
||||
Target_ = nullptr;
|
||||
UVPipe_.reset();
|
||||
Buffer_.clear();
|
||||
Buffer_.shrink_to_fit();
|
||||
}
|
||||
|
||||
void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVAlloc(uv_handle_t* handle,
|
||||
size_t suggestedSize,
|
||||
uv_buf_t* buf)
|
||||
{
|
||||
auto& pipe = *reinterpret_cast<PipeT*>(handle->data);
|
||||
pipe.Buffer_.resize(suggestedSize);
|
||||
buf->base = pipe.Buffer_.data();
|
||||
buf->len = pipe.Buffer_.size();
|
||||
}
|
||||
|
||||
void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVData(uv_stream_t* stream,
|
||||
ssize_t nread,
|
||||
const uv_buf_t* buf)
|
||||
{
|
||||
auto& pipe = *reinterpret_cast<PipeT*>(stream->data);
|
||||
if (nread > 0) {
|
||||
// Append data to merged output
|
||||
if ((buf->base != nullptr) && (pipe.Target_ != nullptr)) {
|
||||
pipe.Target_->append(buf->base, nread);
|
||||
}
|
||||
} else if (nread < 0) {
|
||||
// EOF or error
|
||||
auto* proc = pipe.Process_;
|
||||
// Check it this an unusual error
|
||||
if (nread != UV_EOF) {
|
||||
if (!proc->Result()->error()) {
|
||||
proc->Result()->ErrorMessage =
|
||||
"libuv reading from pipe failed with error code ";
|
||||
proc->Result()->ErrorMessage += std::to_string(nread);
|
||||
}
|
||||
}
|
||||
// Clear libuv pipe handle and try to finish
|
||||
pipe.reset();
|
||||
proc->UVTryFinish();
|
||||
}
|
||||
}
|
||||
|
||||
void cmQtAutoGenerator::ProcessResultT::reset()
|
||||
{
|
||||
ExitStatus = 0;
|
||||
TermSignal = 0;
|
||||
if (!StdOut.empty()) {
|
||||
StdOut.clear();
|
||||
StdOut.shrink_to_fit();
|
||||
}
|
||||
if (!StdErr.empty()) {
|
||||
StdErr.clear();
|
||||
StdErr.shrink_to_fit();
|
||||
}
|
||||
if (!ErrorMessage.empty()) {
|
||||
ErrorMessage.clear();
|
||||
ErrorMessage.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
void cmQtAutoGenerator::ReadOnlyProcessT::setup(
|
||||
ProcessResultT* result, bool mergedOutput,
|
||||
std::vector<std::string> const& command, std::string const& workingDirectory)
|
||||
{
|
||||
Setup_.WorkingDirectory = workingDirectory;
|
||||
Setup_.Command = command;
|
||||
Setup_.Result = result;
|
||||
Setup_.MergedOutput = mergedOutput;
|
||||
}
|
||||
|
||||
static std::string getUVError(const char* prefixString, int uvErrorCode)
|
||||
{
|
||||
std::ostringstream ost;
|
||||
ost << prefixString << ": " << uv_strerror(uvErrorCode);
|
||||
return ost.str();
|
||||
}
|
||||
|
||||
bool cmQtAutoGenerator::ReadOnlyProcessT::start(
|
||||
uv_loop_t* uv_loop, std::function<void()>&& finishedCallback)
|
||||
{
|
||||
if (IsStarted() || (Result() == nullptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset result before the start
|
||||
Result()->reset();
|
||||
|
||||
// Fill command string pointers
|
||||
if (!Setup().Command.empty()) {
|
||||
CommandPtr_.reserve(Setup().Command.size() + 1);
|
||||
for (std::string const& arg : Setup().Command) {
|
||||
CommandPtr_.push_back(arg.c_str());
|
||||
}
|
||||
CommandPtr_.push_back(nullptr);
|
||||
} else {
|
||||
Result()->ErrorMessage = "Empty command";
|
||||
}
|
||||
|
||||
if (!Result()->error()) {
|
||||
if (UVPipeOut_.init(uv_loop, this) != 0) {
|
||||
Result()->ErrorMessage = "libuv stdout pipe initialization failed";
|
||||
}
|
||||
}
|
||||
if (!Result()->error()) {
|
||||
if (UVPipeErr_.init(uv_loop, this) != 0) {
|
||||
Result()->ErrorMessage = "libuv stderr pipe initialization failed";
|
||||
}
|
||||
}
|
||||
if (!Result()->error()) {
|
||||
// -- Setup process stdio options
|
||||
// stdin
|
||||
UVOptionsStdIO_[0].flags = UV_IGNORE;
|
||||
UVOptionsStdIO_[0].data.stream = nullptr;
|
||||
// stdout
|
||||
UVOptionsStdIO_[1].flags =
|
||||
static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
|
||||
UVOptionsStdIO_[1].data.stream = UVPipeOut_.uv_stream();
|
||||
// stderr
|
||||
UVOptionsStdIO_[2].flags =
|
||||
static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
|
||||
UVOptionsStdIO_[2].data.stream = UVPipeErr_.uv_stream();
|
||||
|
||||
// -- Setup process options
|
||||
std::fill_n(reinterpret_cast<char*>(&UVOptions_), sizeof(UVOptions_), 0);
|
||||
UVOptions_.exit_cb = &ReadOnlyProcessT::UVExit;
|
||||
UVOptions_.file = CommandPtr_[0];
|
||||
UVOptions_.args = const_cast<char**>(CommandPtr_.data());
|
||||
UVOptions_.cwd = Setup_.WorkingDirectory.c_str();
|
||||
UVOptions_.flags = UV_PROCESS_WINDOWS_HIDE;
|
||||
UVOptions_.stdio_count = static_cast<int>(UVOptionsStdIO_.size());
|
||||
UVOptions_.stdio = UVOptionsStdIO_.data();
|
||||
|
||||
// -- Spawn process
|
||||
int uvErrorCode = UVProcess_.spawn(*uv_loop, UVOptions_, this);
|
||||
if (uvErrorCode != 0) {
|
||||
Result()->ErrorMessage =
|
||||
getUVError("libuv process spawn failed ", uvErrorCode);
|
||||
}
|
||||
}
|
||||
// -- Start reading from stdio streams
|
||||
if (!Result()->error()) {
|
||||
if (UVPipeOut_.startRead(&Result()->StdOut) != 0) {
|
||||
Result()->ErrorMessage = "libuv start reading from stdout pipe failed";
|
||||
}
|
||||
}
|
||||
if (!Result()->error()) {
|
||||
if (UVPipeErr_.startRead(Setup_.MergedOutput ? &Result()->StdOut
|
||||
: &Result()->StdErr) != 0) {
|
||||
Result()->ErrorMessage = "libuv start reading from stderr pipe failed";
|
||||
}
|
||||
}
|
||||
|
||||
if (!Result()->error()) {
|
||||
IsStarted_ = true;
|
||||
FinishedCallback_ = std::move(finishedCallback);
|
||||
} else {
|
||||
// Clear libuv handles and finish
|
||||
UVProcess_.reset();
|
||||
UVPipeOut_.reset();
|
||||
UVPipeErr_.reset();
|
||||
CommandPtr_.clear();
|
||||
}
|
||||
|
||||
return IsStarted();
|
||||
}
|
||||
|
||||
void cmQtAutoGenerator::ReadOnlyProcessT::UVExit(uv_process_t* handle,
|
||||
int64_t exitStatus,
|
||||
int termSignal)
|
||||
{
|
||||
auto& proc = *reinterpret_cast<ReadOnlyProcessT*>(handle->data);
|
||||
if (proc.IsStarted() && !proc.IsFinished()) {
|
||||
// Set error message on demand
|
||||
proc.Result()->ExitStatus = exitStatus;
|
||||
proc.Result()->TermSignal = termSignal;
|
||||
if (!proc.Result()->error()) {
|
||||
if (termSignal != 0) {
|
||||
proc.Result()->ErrorMessage = "Process was terminated by signal ";
|
||||
proc.Result()->ErrorMessage +=
|
||||
std::to_string(proc.Result()->TermSignal);
|
||||
} else if (exitStatus != 0) {
|
||||
proc.Result()->ErrorMessage = "Process failed with return value ";
|
||||
proc.Result()->ErrorMessage +=
|
||||
std::to_string(proc.Result()->ExitStatus);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset process handle and try to finish
|
||||
proc.UVProcess_.reset();
|
||||
proc.UVTryFinish();
|
||||
}
|
||||
}
|
||||
|
||||
void cmQtAutoGenerator::ReadOnlyProcessT::UVTryFinish()
|
||||
{
|
||||
// There still might be data in the pipes after the process has finished.
|
||||
// Therefore check if the process is finished AND all pipes are closed
|
||||
// before signaling the worker thread to continue.
|
||||
if (UVProcess_.get() == nullptr) {
|
||||
if (UVPipeOut_.uv_pipe() == nullptr) {
|
||||
if (UVPipeErr_.uv_pipe() == nullptr) {
|
||||
IsFinished_ = true;
|
||||
FinishedCallback_();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmQtAutoGenerator::cmQtAutoGenerator() = default;
|
||||
|
||||
cmQtAutoGenerator::~cmQtAutoGenerator() = default;
|
||||
|
||||
@@ -7,14 +7,8 @@
|
||||
|
||||
#include "cmFilePathChecksum.h"
|
||||
#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>
|
||||
|
||||
@@ -137,102 +131,6 @@ public:
|
||||
cmFilePathChecksum FilePathChecksum_;
|
||||
};
|
||||
|
||||
/// @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;
|
||||
};
|
||||
|
||||
// -- 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_;
|
||||
};
|
||||
|
||||
public:
|
||||
// -- Constructors
|
||||
cmQtAutoGenerator();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,26 +1,22 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#ifndef cmQtAutoGeneratorMocUic_h
|
||||
#define cmQtAutoGeneratorMocUic_h
|
||||
#ifndef cmQtAutoMocUic_h
|
||||
#define cmQtAutoMocUic_h
|
||||
|
||||
#include "cmConfigure.h" // IWYU pragma: keep
|
||||
|
||||
#include "cmQtAutoGen.h"
|
||||
#include "cmQtAutoGenerator.h"
|
||||
#include "cmUVHandlePtr.h"
|
||||
#include "cmUVSignalHackRAII.h" // IWYU pragma: keep
|
||||
#include "cm_uv.h"
|
||||
#include "cmWorkerPool.h"
|
||||
#include "cmsys/RegularExpression.hxx"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <deque>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <memory> // IWYU pragma: keep
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -28,18 +24,18 @@
|
||||
class cmMakefile;
|
||||
|
||||
// @brief AUTOMOC and AUTOUIC generator
|
||||
class cmQtAutoGeneratorMocUic : public cmQtAutoGenerator
|
||||
class cmQtAutoMocUic : public cmQtAutoGenerator
|
||||
{
|
||||
public:
|
||||
cmQtAutoGeneratorMocUic();
|
||||
~cmQtAutoGeneratorMocUic() override;
|
||||
cmQtAutoMocUic();
|
||||
~cmQtAutoMocUic() override;
|
||||
|
||||
cmQtAutoGeneratorMocUic(cmQtAutoGeneratorMocUic const&) = delete;
|
||||
cmQtAutoGeneratorMocUic& operator=(cmQtAutoGeneratorMocUic const&) = delete;
|
||||
cmQtAutoMocUic(cmQtAutoMocUic const&) = delete;
|
||||
cmQtAutoMocUic& operator=(cmQtAutoMocUic const&) = delete;
|
||||
|
||||
public:
|
||||
// -- Types
|
||||
class WorkerT;
|
||||
typedef std::multimap<std::string, std::array<std::string, 2>> IncludesMap;
|
||||
|
||||
/// @brief Search key plus regular expression pair
|
||||
///
|
||||
@@ -173,31 +169,71 @@ public:
|
||||
cmsys::RegularExpression RegExpInclude;
|
||||
};
|
||||
|
||||
/// @brief Abstract job class for threaded processing
|
||||
/// @brief Abstract job class for concurrent job processing
|
||||
///
|
||||
class JobT
|
||||
class JobT : public cmWorkerPool::JobT
|
||||
{
|
||||
public:
|
||||
JobT() = default;
|
||||
virtual ~JobT() = default;
|
||||
protected:
|
||||
/**
|
||||
* @brief Protected default constructor
|
||||
*/
|
||||
JobT(bool fence = false)
|
||||
: cmWorkerPool::JobT(fence)
|
||||
{
|
||||
}
|
||||
|
||||
JobT(JobT const&) = delete;
|
||||
JobT& operator=(JobT const&) = delete;
|
||||
//! Get the generator. Only valid during Process() call!
|
||||
cmQtAutoMocUic* Gen() const
|
||||
{
|
||||
return static_cast<cmQtAutoMocUic*>(UserData());
|
||||
};
|
||||
|
||||
// -- Abstract processing interface
|
||||
virtual void Process(WorkerT& wrk) = 0;
|
||||
//! Get the file system interface. Only valid during Process() call!
|
||||
FileSystem& FileSys() { return Gen()->FileSys(); }
|
||||
//! Get the logger. Only valid during Process() call!
|
||||
Logger& Log() { return Gen()->Log(); }
|
||||
|
||||
// -- Error logging with automatic abort
|
||||
void LogError(GenT genType, std::string const& message) const;
|
||||
void LogFileError(GenT genType, std::string const& filename,
|
||||
std::string const& message) const;
|
||||
void LogCommandError(GenT genType, std::string const& message,
|
||||
std::vector<std::string> const& command,
|
||||
std::string const& output) const;
|
||||
|
||||
/**
|
||||
* @brief Run an external process. Use only during Process() call!
|
||||
*/
|
||||
bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result,
|
||||
std::vector<std::string> const& command);
|
||||
};
|
||||
|
||||
// Job management types
|
||||
typedef std::unique_ptr<JobT> JobHandleT;
|
||||
typedef std::deque<JobHandleT> JobQueueT;
|
||||
/// @brief Fence job utility class
|
||||
///
|
||||
class JobFenceT : public JobT
|
||||
{
|
||||
public:
|
||||
JobFenceT()
|
||||
: JobT(true)
|
||||
{
|
||||
}
|
||||
void Process() override{};
|
||||
};
|
||||
|
||||
/// @brief Parse source job
|
||||
/// @brief Generate moc_predefs.h
|
||||
///
|
||||
class JobMocPredefsT : public JobT
|
||||
{
|
||||
private:
|
||||
void Process() override;
|
||||
};
|
||||
|
||||
/// @brief Parses a source file
|
||||
///
|
||||
class JobParseT : public JobT
|
||||
{
|
||||
public:
|
||||
JobParseT(std::string&& fileName, bool moc, bool uic, bool header = false)
|
||||
JobParseT(std::string fileName, bool moc, bool uic, bool header = false)
|
||||
: FileName(std::move(fileName))
|
||||
, AutoMoc(moc)
|
||||
, AutoUic(uic)
|
||||
@@ -213,18 +249,15 @@ public:
|
||||
std::string FileBase;
|
||||
};
|
||||
|
||||
void Process(WorkerT& wrk) override;
|
||||
bool ParseMocSource(WorkerT& wrk, MetaT const& meta);
|
||||
bool ParseMocHeader(WorkerT& wrk, MetaT const& meta);
|
||||
std::string MocStringHeaders(WorkerT& wrk,
|
||||
std::string const& fileBase) const;
|
||||
std::string MocFindIncludedHeader(WorkerT& wrk,
|
||||
std::string const& includerDir,
|
||||
void Process() override;
|
||||
bool ParseMocSource(MetaT const& meta);
|
||||
bool ParseMocHeader(MetaT const& meta);
|
||||
std::string MocStringHeaders(std::string const& fileBase) const;
|
||||
std::string MocFindIncludedHeader(std::string const& includerDir,
|
||||
std::string const& includeBase);
|
||||
bool ParseUic(WorkerT& wrk, MetaT const& meta);
|
||||
bool ParseUicInclude(WorkerT& wrk, MetaT const& meta,
|
||||
std::string&& includeString);
|
||||
std::string UicFindIncludedFile(WorkerT& wrk, MetaT const& meta,
|
||||
bool ParseUic(MetaT const& meta);
|
||||
bool ParseUicInclude(MetaT const& meta, std::string&& includeString);
|
||||
std::string UicFindIncludedFile(MetaT const& meta,
|
||||
std::string const& includeString);
|
||||
|
||||
private:
|
||||
@@ -234,12 +267,20 @@ public:
|
||||
bool Header = false;
|
||||
};
|
||||
|
||||
/// @brief Generate moc_predefs
|
||||
/// @brief Generates additional jobs after all files have been parsed
|
||||
///
|
||||
class JobMocPredefsT : public JobT
|
||||
class JobPostParseT : public JobFenceT
|
||||
{
|
||||
private:
|
||||
void Process(WorkerT& wrk) override;
|
||||
void Process() override;
|
||||
};
|
||||
|
||||
/// @brief Generate mocs_compilation.cpp
|
||||
///
|
||||
class JobMocsCompilationT : public JobFenceT
|
||||
{
|
||||
private:
|
||||
void Process() override;
|
||||
};
|
||||
|
||||
/// @brief Moc a file job
|
||||
@@ -247,20 +288,20 @@ public:
|
||||
class JobMocT : public JobT
|
||||
{
|
||||
public:
|
||||
JobMocT(std::string&& sourceFile, std::string includerFile,
|
||||
std::string&& includeString)
|
||||
JobMocT(std::string sourceFile, std::string includerFile,
|
||||
std::string includeString)
|
||||
: SourceFile(std::move(sourceFile))
|
||||
, IncluderFile(std::move(includerFile))
|
||||
, IncludeString(std::move(includeString))
|
||||
{
|
||||
}
|
||||
|
||||
void FindDependencies(WorkerT& wrk, std::string const& content);
|
||||
void FindDependencies(std::string const& content);
|
||||
|
||||
private:
|
||||
void Process(WorkerT& wrk) override;
|
||||
bool UpdateRequired(WorkerT& wrk);
|
||||
void GenerateMoc(WorkerT& wrk);
|
||||
void Process() override;
|
||||
bool UpdateRequired();
|
||||
void GenerateMoc();
|
||||
|
||||
public:
|
||||
std::string SourceFile;
|
||||
@@ -276,8 +317,8 @@ public:
|
||||
class JobUicT : public JobT
|
||||
{
|
||||
public:
|
||||
JobUicT(std::string&& sourceFile, std::string includerFile,
|
||||
std::string&& includeString)
|
||||
JobUicT(std::string sourceFile, std::string includerFile,
|
||||
std::string includeString)
|
||||
: SourceFile(std::move(sourceFile))
|
||||
, IncluderFile(std::move(includerFile))
|
||||
, IncludeString(std::move(includeString))
|
||||
@@ -285,9 +326,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void Process(WorkerT& wrk) override;
|
||||
bool UpdateRequired(WorkerT& wrk);
|
||||
void GenerateUic(WorkerT& wrk);
|
||||
void Process() override;
|
||||
bool UpdateRequired();
|
||||
void GenerateUic();
|
||||
|
||||
public:
|
||||
std::string SourceFile;
|
||||
@@ -296,80 +337,12 @@ public:
|
||||
std::string BuildFile;
|
||||
};
|
||||
|
||||
/// @brief Worker Thread
|
||||
/// @brief The last job
|
||||
///
|
||||
class WorkerT
|
||||
class JobFinishT : public JobFenceT
|
||||
{
|
||||
public:
|
||||
WorkerT(cmQtAutoGeneratorMocUic* gen, uv_loop_t* uvLoop);
|
||||
~WorkerT();
|
||||
|
||||
WorkerT(WorkerT const&) = delete;
|
||||
WorkerT& operator=(WorkerT const&) = delete;
|
||||
|
||||
// -- Const accessors
|
||||
cmQtAutoGeneratorMocUic& Gen() const { return *Gen_; }
|
||||
Logger& Log() const { return Gen_->Log(); }
|
||||
FileSystem& FileSys() const { return Gen_->FileSys(); }
|
||||
const BaseSettingsT& Base() const { return Gen_->Base(); }
|
||||
const MocSettingsT& Moc() const { return Gen_->Moc(); }
|
||||
const UicSettingsT& Uic() const { return Gen_->Uic(); }
|
||||
|
||||
// -- Log info
|
||||
void LogInfo(GenT genType, std::string const& message) const;
|
||||
// -- Log warning
|
||||
void LogWarning(GenT genType, std::string const& message) const;
|
||||
void LogFileWarning(GenT genType, std::string const& filename,
|
||||
std::string const& message) const;
|
||||
// -- Log error
|
||||
void LogError(GenT genType, std::string const& message) const;
|
||||
void LogFileError(GenT genType, std::string const& filename,
|
||||
std::string const& message) const;
|
||||
void LogCommandError(GenT genType, std::string const& message,
|
||||
std::vector<std::string> const& command,
|
||||
std::string const& output) const;
|
||||
|
||||
// -- External processes
|
||||
/// @brief Verbose logging version
|
||||
bool RunProcess(GenT genType, ProcessResultT& result,
|
||||
std::vector<std::string> const& command);
|
||||
|
||||
private:
|
||||
/// @brief Thread main loop
|
||||
void Loop();
|
||||
|
||||
// -- Libuv callbacks
|
||||
static void UVProcessStart(uv_async_t* handle);
|
||||
void UVProcessFinished();
|
||||
|
||||
private:
|
||||
// -- Generator
|
||||
cmQtAutoGeneratorMocUic* Gen_;
|
||||
// -- Job handle
|
||||
JobHandleT JobHandle_;
|
||||
// -- Process management
|
||||
std::mutex ProcessMutex_;
|
||||
cm::uv_async_ptr ProcessRequest_;
|
||||
std::condition_variable ProcessCondition_;
|
||||
std::unique_ptr<ReadOnlyProcessT> Process_;
|
||||
// -- System thread
|
||||
std::thread Thread_;
|
||||
};
|
||||
|
||||
/// @brief Processing stage
|
||||
enum class StageT
|
||||
{
|
||||
SETTINGS_READ,
|
||||
CREATE_DIRECTORIES,
|
||||
PARSE_SOURCES,
|
||||
PARSE_HEADERS,
|
||||
MOC_PREDEFS,
|
||||
MOC_PROCESS,
|
||||
MOCS_COMPILATION,
|
||||
UIC_PROCESS,
|
||||
SETTINGS_WRITE,
|
||||
FINISH,
|
||||
END
|
||||
void Process() override;
|
||||
};
|
||||
|
||||
// -- Const settings interface
|
||||
@@ -377,41 +350,39 @@ public:
|
||||
const MocSettingsT& Moc() const { return this->Moc_; }
|
||||
const UicSettingsT& Uic() const { return this->Uic_; }
|
||||
|
||||
// -- Worker thread interface
|
||||
void WorkerSwapJob(JobHandleT& jobHandle);
|
||||
// -- Parallel job processing interface
|
||||
void ParallelRegisterJobError();
|
||||
bool ParallelJobPushMoc(JobHandleT& jobHandle);
|
||||
bool ParallelJobPushUic(JobHandleT& jobHandle);
|
||||
bool ParallelMocIncluded(std::string const& sourceFile);
|
||||
cmWorkerPool& WorkerPool() { return WorkerPool_; }
|
||||
void AbortError() { Abort(true); }
|
||||
void AbortSuccess() { Abort(false); }
|
||||
bool ParallelJobPushMoc(cmWorkerPool::JobHandleT&& jobHandle);
|
||||
bool ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle);
|
||||
|
||||
// -- Mocs compilation include file updated flag
|
||||
void ParallelMocAutoUpdated() { MocAutoFileUpdated_.store(true); }
|
||||
bool MocAutoFileUpdated() const { return MocAutoFileUpdated_.load(); }
|
||||
|
||||
// -- Mocs compilation file register
|
||||
std::string ParallelMocAutoRegister(std::string const& baseName);
|
||||
void ParallelMocAutoUpdated();
|
||||
bool ParallelMocIncluded(std::string const& sourceFile);
|
||||
std::set<std::string> const& MocAutoFiles() const
|
||||
{
|
||||
return this->MocAutoFiles_;
|
||||
}
|
||||
|
||||
private:
|
||||
// -- Utility accessors
|
||||
Logger& Log() { return Logger_; }
|
||||
FileSystem& FileSys() { return FileSys_; }
|
||||
// -- libuv loop accessors
|
||||
uv_loop_t* UVLoop() { return UVLoop_.get(); }
|
||||
cm::uv_async_ptr& UVRequest() { return UVRequest_; }
|
||||
// -- Abstract processing interface
|
||||
bool Init(cmMakefile* makefile) override;
|
||||
bool Process() override;
|
||||
// -- Process stage
|
||||
static void UVPollStage(uv_async_t* handle);
|
||||
void PollStage();
|
||||
void SetStage(StageT stage);
|
||||
// -- Settings file
|
||||
void SettingsFileRead();
|
||||
void SettingsFileWrite();
|
||||
bool SettingsFileWrite();
|
||||
// -- Thread processing
|
||||
bool ThreadsStartJobs(JobQueueT& queue);
|
||||
bool ThreadsJobsDone();
|
||||
void ThreadsStop();
|
||||
void RegisterJobError();
|
||||
void Abort(bool error);
|
||||
// -- Generation
|
||||
void CreateDirectories();
|
||||
void MocGenerateCompilation();
|
||||
bool CreateDirectories();
|
||||
|
||||
private:
|
||||
// -- Utility
|
||||
@@ -421,39 +392,22 @@ private:
|
||||
BaseSettingsT Base_;
|
||||
MocSettingsT Moc_;
|
||||
UicSettingsT Uic_;
|
||||
// -- 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_;
|
||||
StageT Stage_ = StageT::SETTINGS_READ;
|
||||
// -- Job queues
|
||||
std::mutex JobsMutex_;
|
||||
struct
|
||||
{
|
||||
JobQueueT Sources;
|
||||
JobQueueT Headers;
|
||||
JobQueueT MocPredefs;
|
||||
JobQueueT Moc;
|
||||
JobQueueT Uic;
|
||||
} JobQueues_;
|
||||
JobQueueT JobQueue_;
|
||||
std::size_t volatile JobsRemain_ = 0;
|
||||
bool volatile JobError_ = false;
|
||||
bool volatile JobThreadsAbort_ = false;
|
||||
std::condition_variable JobsConditionRead_;
|
||||
// -- Moc meta
|
||||
std::set<std::string> MocIncludedStrings_;
|
||||
std::mutex MocMetaMutex_;
|
||||
std::set<std::string> MocIncludedFiles_;
|
||||
IncludesMap MocIncludes_;
|
||||
std::set<std::string> MocAutoFiles_;
|
||||
bool volatile MocAutoFileUpdated_ = false;
|
||||
std::atomic<bool> MocAutoFileUpdated_ = ATOMIC_VAR_INIT(false);
|
||||
// -- Uic meta
|
||||
std::mutex UicMetaMutex_;
|
||||
IncludesMap UicIncludes_;
|
||||
// -- Settings file
|
||||
std::string SettingsFile_;
|
||||
std::string SettingsStringMoc_;
|
||||
std::string SettingsStringUic_;
|
||||
// -- Threads and loops
|
||||
std::vector<std::unique_ptr<WorkerT>> Workers_;
|
||||
// -- Thread pool and job queue
|
||||
std::atomic<bool> JobError_ = ATOMIC_VAR_INIT(false);
|
||||
cmWorkerPool WorkerPool_;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,770 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#include "cmWorkerPool.h"
|
||||
|
||||
#include "cmRange.h"
|
||||
#include "cmUVHandlePtr.h"
|
||||
#include "cmUVSignalHackRAII.h" // IWYU pragma: keep
|
||||
#include "cm_uv.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <stddef.h>
|
||||
#include <thread>
|
||||
|
||||
/**
|
||||
* @brief libuv pipe buffer class
|
||||
*/
|
||||
class cmUVPipeBuffer
|
||||
{
|
||||
public:
|
||||
typedef cmRange<char const*> DataRange;
|
||||
typedef std::function<void(DataRange)> DataFunction;
|
||||
/// On error the ssize_t argument is a non zero libuv error code
|
||||
typedef std::function<void(ssize_t)> EndFunction;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Reset to construction state
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Initializes uv_pipe(), uv_stream() and uv_handle()
|
||||
* @return true on success
|
||||
*/
|
||||
bool init(uv_loop_t* uv_loop);
|
||||
|
||||
/**
|
||||
* Start reading
|
||||
* @return true on success
|
||||
*/
|
||||
bool startRead(DataFunction dataFunction, EndFunction endFunction);
|
||||
|
||||
//! libuv pipe
|
||||
uv_pipe_t* uv_pipe() const { return UVPipe_.get(); }
|
||||
//! uv_pipe() casted to libuv stream
|
||||
uv_stream_t* uv_stream() const { return static_cast<uv_stream_t*>(UVPipe_); }
|
||||
//! uv_pipe() casted to libuv handle
|
||||
uv_handle_t* uv_handle() { return static_cast<uv_handle_t*>(UVPipe_); }
|
||||
|
||||
private:
|
||||
// -- 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:
|
||||
cm::uv_pipe_ptr UVPipe_;
|
||||
std::vector<char> Buffer_;
|
||||
DataFunction DataFunction_;
|
||||
EndFunction EndFunction_;
|
||||
};
|
||||
|
||||
void cmUVPipeBuffer::reset()
|
||||
{
|
||||
if (UVPipe_.get() != nullptr) {
|
||||
EndFunction_ = nullptr;
|
||||
DataFunction_ = nullptr;
|
||||
Buffer_.clear();
|
||||
Buffer_.shrink_to_fit();
|
||||
UVPipe_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool cmUVPipeBuffer::init(uv_loop_t* uv_loop)
|
||||
{
|
||||
reset();
|
||||
if (uv_loop == nullptr) {
|
||||
return false;
|
||||
}
|
||||
int ret = UVPipe_.init(*uv_loop, 0, this);
|
||||
return (ret == 0);
|
||||
}
|
||||
|
||||
bool cmUVPipeBuffer::startRead(DataFunction dataFunction,
|
||||
EndFunction endFunction)
|
||||
{
|
||||
if (UVPipe_.get() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (!dataFunction || !endFunction) {
|
||||
return false;
|
||||
}
|
||||
DataFunction_ = std::move(dataFunction);
|
||||
EndFunction_ = std::move(endFunction);
|
||||
int ret = uv_read_start(uv_stream(), &cmUVPipeBuffer::UVAlloc,
|
||||
&cmUVPipeBuffer::UVData);
|
||||
return (ret == 0);
|
||||
}
|
||||
|
||||
void cmUVPipeBuffer::UVAlloc(uv_handle_t* handle, size_t suggestedSize,
|
||||
uv_buf_t* buf)
|
||||
{
|
||||
auto& pipe = *reinterpret_cast<cmUVPipeBuffer*>(handle->data);
|
||||
pipe.Buffer_.resize(suggestedSize);
|
||||
buf->base = pipe.Buffer_.data();
|
||||
buf->len = static_cast<unsigned long>(pipe.Buffer_.size());
|
||||
}
|
||||
|
||||
void cmUVPipeBuffer::UVData(uv_stream_t* stream, ssize_t nread,
|
||||
const uv_buf_t* buf)
|
||||
{
|
||||
auto& pipe = *reinterpret_cast<cmUVPipeBuffer*>(stream->data);
|
||||
if (nread > 0) {
|
||||
if (buf->base != nullptr) {
|
||||
// Call data function
|
||||
pipe.DataFunction_(DataRange(buf->base, buf->base + nread));
|
||||
}
|
||||
} else if (nread < 0) {
|
||||
// Save the end function on the stack before resetting the pipe
|
||||
EndFunction efunc;
|
||||
efunc.swap(pipe.EndFunction_);
|
||||
// Reset pipe before calling the end function
|
||||
pipe.reset();
|
||||
// Call end function
|
||||
efunc((nread == UV_EOF) ? 0 : nread);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief External process management class
|
||||
*/
|
||||
class cmUVReadOnlyProcess
|
||||
{
|
||||
public:
|
||||
// -- Types
|
||||
//! @brief Process settings
|
||||
struct SetupT
|
||||
{
|
||||
std::string WorkingDirectory;
|
||||
std::vector<std::string> Command;
|
||||
cmWorkerPool::ProcessResultT* Result = nullptr;
|
||||
bool MergedOutput = false;
|
||||
};
|
||||
|
||||
public:
|
||||
// -- Const accessors
|
||||
SetupT const& Setup() const { return Setup_; }
|
||||
cmWorkerPool::ProcessResultT* Result() const { return Setup_.Result; }
|
||||
bool IsStarted() const { return IsStarted_; }
|
||||
bool IsFinished() const { return IsFinished_; }
|
||||
|
||||
// -- Runtime
|
||||
void setup(cmWorkerPool::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:
|
||||
// -- Libuv callbacks
|
||||
static void UVExit(uv_process_t* handle, int64_t exitStatus, int termSignal);
|
||||
void UVPipeOutData(cmUVPipeBuffer::DataRange data);
|
||||
void UVPipeOutEnd(ssize_t error);
|
||||
void UVPipeErrData(cmUVPipeBuffer::DataRange data);
|
||||
void UVPipeErrEnd(ssize_t error);
|
||||
void UVTryFinish();
|
||||
|
||||
private:
|
||||
// -- 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_;
|
||||
cmUVPipeBuffer UVPipeOut_;
|
||||
cmUVPipeBuffer UVPipeErr_;
|
||||
};
|
||||
|
||||
void cmUVReadOnlyProcess::setup(cmWorkerPool::ProcessResultT* result,
|
||||
bool mergedOutput,
|
||||
std::vector<std::string> const& command,
|
||||
std::string const& workingDirectory)
|
||||
{
|
||||
Setup_.WorkingDirectory = workingDirectory;
|
||||
Setup_.Command = command;
|
||||
Setup_.Result = result;
|
||||
Setup_.MergedOutput = mergedOutput;
|
||||
}
|
||||
|
||||
bool cmUVReadOnlyProcess::start(uv_loop_t* uv_loop,
|
||||
std::function<void()> finishedCallback)
|
||||
{
|
||||
if (IsStarted() || (Result() == nullptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset result before the start
|
||||
Result()->reset();
|
||||
|
||||
// Fill command string pointers
|
||||
if (!Setup().Command.empty()) {
|
||||
CommandPtr_.reserve(Setup().Command.size() + 1);
|
||||
for (std::string const& arg : Setup().Command) {
|
||||
CommandPtr_.push_back(arg.c_str());
|
||||
}
|
||||
CommandPtr_.push_back(nullptr);
|
||||
} else {
|
||||
Result()->ErrorMessage = "Empty command";
|
||||
}
|
||||
|
||||
if (!Result()->error()) {
|
||||
if (!UVPipeOut_.init(uv_loop)) {
|
||||
Result()->ErrorMessage = "libuv stdout pipe initialization failed";
|
||||
}
|
||||
}
|
||||
if (!Result()->error()) {
|
||||
if (!UVPipeErr_.init(uv_loop)) {
|
||||
Result()->ErrorMessage = "libuv stderr pipe initialization failed";
|
||||
}
|
||||
}
|
||||
if (!Result()->error()) {
|
||||
// -- Setup process stdio options
|
||||
// stdin
|
||||
UVOptionsStdIO_[0].flags = UV_IGNORE;
|
||||
UVOptionsStdIO_[0].data.stream = nullptr;
|
||||
// stdout
|
||||
UVOptionsStdIO_[1].flags =
|
||||
static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
|
||||
UVOptionsStdIO_[1].data.stream = UVPipeOut_.uv_stream();
|
||||
// stderr
|
||||
UVOptionsStdIO_[2].flags =
|
||||
static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
|
||||
UVOptionsStdIO_[2].data.stream = UVPipeErr_.uv_stream();
|
||||
|
||||
// -- Setup process options
|
||||
std::fill_n(reinterpret_cast<char*>(&UVOptions_), sizeof(UVOptions_), 0);
|
||||
UVOptions_.exit_cb = &cmUVReadOnlyProcess::UVExit;
|
||||
UVOptions_.file = CommandPtr_[0];
|
||||
UVOptions_.args = const_cast<char**>(CommandPtr_.data());
|
||||
UVOptions_.cwd = Setup_.WorkingDirectory.c_str();
|
||||
UVOptions_.flags = UV_PROCESS_WINDOWS_HIDE;
|
||||
UVOptions_.stdio_count = static_cast<int>(UVOptionsStdIO_.size());
|
||||
UVOptions_.stdio = UVOptionsStdIO_.data();
|
||||
|
||||
// -- Spawn process
|
||||
int uvErrorCode = UVProcess_.spawn(*uv_loop, UVOptions_, this);
|
||||
if (uvErrorCode != 0) {
|
||||
Result()->ErrorMessage = "libuv process spawn failed";
|
||||
if (const char* uvErr = uv_strerror(uvErrorCode)) {
|
||||
Result()->ErrorMessage += ": ";
|
||||
Result()->ErrorMessage += uvErr;
|
||||
}
|
||||
}
|
||||
}
|
||||
// -- Start reading from stdio streams
|
||||
if (!Result()->error()) {
|
||||
if (!UVPipeOut_.startRead(
|
||||
[this](cmUVPipeBuffer::DataRange range) {
|
||||
this->UVPipeOutData(range);
|
||||
},
|
||||
[this](ssize_t error) { this->UVPipeOutEnd(error); })) {
|
||||
Result()->ErrorMessage = "libuv start reading from stdout pipe failed";
|
||||
}
|
||||
}
|
||||
if (!Result()->error()) {
|
||||
if (!UVPipeErr_.startRead(
|
||||
[this](cmUVPipeBuffer::DataRange range) {
|
||||
this->UVPipeErrData(range);
|
||||
},
|
||||
[this](ssize_t error) { this->UVPipeErrEnd(error); })) {
|
||||
Result()->ErrorMessage = "libuv start reading from stderr pipe failed";
|
||||
}
|
||||
}
|
||||
|
||||
if (!Result()->error()) {
|
||||
IsStarted_ = true;
|
||||
FinishedCallback_ = std::move(finishedCallback);
|
||||
} else {
|
||||
// Clear libuv handles and finish
|
||||
UVProcess_.reset();
|
||||
UVPipeOut_.reset();
|
||||
UVPipeErr_.reset();
|
||||
CommandPtr_.clear();
|
||||
}
|
||||
|
||||
return IsStarted();
|
||||
}
|
||||
|
||||
void cmUVReadOnlyProcess::UVExit(uv_process_t* handle, int64_t exitStatus,
|
||||
int termSignal)
|
||||
{
|
||||
auto& proc = *reinterpret_cast<cmUVReadOnlyProcess*>(handle->data);
|
||||
if (proc.IsStarted() && !proc.IsFinished()) {
|
||||
// Set error message on demand
|
||||
proc.Result()->ExitStatus = exitStatus;
|
||||
proc.Result()->TermSignal = termSignal;
|
||||
if (!proc.Result()->error()) {
|
||||
if (termSignal != 0) {
|
||||
proc.Result()->ErrorMessage = "Process was terminated by signal ";
|
||||
proc.Result()->ErrorMessage +=
|
||||
std::to_string(proc.Result()->TermSignal);
|
||||
} else if (exitStatus != 0) {
|
||||
proc.Result()->ErrorMessage = "Process failed with return value ";
|
||||
proc.Result()->ErrorMessage +=
|
||||
std::to_string(proc.Result()->ExitStatus);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset process handle
|
||||
proc.UVProcess_.reset();
|
||||
// Try finish
|
||||
proc.UVTryFinish();
|
||||
}
|
||||
}
|
||||
|
||||
void cmUVReadOnlyProcess::UVPipeOutData(cmUVPipeBuffer::DataRange data)
|
||||
{
|
||||
Result()->StdOut.append(data.begin(), data.end());
|
||||
}
|
||||
|
||||
void cmUVReadOnlyProcess::UVPipeOutEnd(ssize_t error)
|
||||
{
|
||||
// Process pipe error
|
||||
if ((error != 0) && !Result()->error()) {
|
||||
Result()->ErrorMessage =
|
||||
"Reading from stdout pipe failed with libuv error code ";
|
||||
Result()->ErrorMessage += std::to_string(error);
|
||||
}
|
||||
// Try finish
|
||||
UVTryFinish();
|
||||
}
|
||||
|
||||
void cmUVReadOnlyProcess::UVPipeErrData(cmUVPipeBuffer::DataRange data)
|
||||
{
|
||||
std::string* str =
|
||||
Setup_.MergedOutput ? &Result()->StdOut : &Result()->StdErr;
|
||||
str->append(data.begin(), data.end());
|
||||
}
|
||||
|
||||
void cmUVReadOnlyProcess::UVPipeErrEnd(ssize_t error)
|
||||
{
|
||||
// Process pipe error
|
||||
if ((error != 0) && !Result()->error()) {
|
||||
Result()->ErrorMessage =
|
||||
"Reading from stderr pipe failed with libuv error code ";
|
||||
Result()->ErrorMessage += std::to_string(error);
|
||||
}
|
||||
// Try finish
|
||||
UVTryFinish();
|
||||
}
|
||||
|
||||
void cmUVReadOnlyProcess::UVTryFinish()
|
||||
{
|
||||
// There still might be data in the pipes after the process has finished.
|
||||
// Therefore check if the process is finished AND all pipes are closed
|
||||
// before signaling the worker thread to continue.
|
||||
if ((UVProcess_.get() != nullptr) || (UVPipeOut_.uv_pipe() != nullptr) ||
|
||||
(UVPipeErr_.uv_pipe() != nullptr)) {
|
||||
return;
|
||||
}
|
||||
IsFinished_ = true;
|
||||
FinishedCallback_();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Private worker pool internals
|
||||
*/
|
||||
class cmWorkerPoolInternal
|
||||
{
|
||||
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();
|
||||
|
||||
/**
|
||||
* @brief Clear queue and abort threads
|
||||
*/
|
||||
void Abort();
|
||||
|
||||
/**
|
||||
* @brief Push a job to the queue and notify a worker
|
||||
*/
|
||||
bool PushJob(cmWorkerPool::JobHandleT&& jobHandle);
|
||||
|
||||
/**
|
||||
* @brief Worker thread main loop method
|
||||
*/
|
||||
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;
|
||||
|
||||
// -- 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;
|
||||
|
||||
// -- References
|
||||
cmWorkerPool* Pool = nullptr;
|
||||
};
|
||||
|
||||
cmWorkerPoolInternal::WorkerT::WorkerT(unsigned int index)
|
||||
: Index_(index)
|
||||
{
|
||||
}
|
||||
|
||||
cmWorkerPoolInternal::WorkerT::~WorkerT()
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (command.empty()) {
|
||||
return false;
|
||||
}
|
||||
// Create process instance
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(Proc_.Mutex);
|
||||
Proc_.ROP = cm::make_unique<cmUVReadOnlyProcess>();
|
||||
Proc_.ROP->setup(&result, true, command, workingDirectory);
|
||||
}
|
||||
// Send asynchronous process start request to libuv loop
|
||||
Proc_.Request.send();
|
||||
// Wait until the process has been finished and destroyed
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(Proc_.Mutex);
|
||||
while (Proc_.ROP) {
|
||||
Proc_.Condition.wait(ulock);
|
||||
}
|
||||
}
|
||||
return !result.error();
|
||||
}
|
||||
|
||||
void cmWorkerPoolInternal::WorkerT::UVProcessStart(uv_async_t* handle)
|
||||
{
|
||||
auto* wrk = reinterpret_cast<WorkerT*>(handle->data);
|
||||
bool startFailed = false;
|
||||
{
|
||||
auto& Proc = wrk->Proc_;
|
||||
std::lock_guard<std::mutex> lock(Proc.Mutex);
|
||||
if (Proc.ROP && !Proc.ROP->IsStarted()) {
|
||||
startFailed =
|
||||
!Proc.ROP->start(handle->loop, [wrk] { wrk->UVProcessFinished(); });
|
||||
}
|
||||
}
|
||||
// Clean up if starting of the process failed
|
||||
if (startFailed) {
|
||||
wrk->UVProcessFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void cmWorkerPoolInternal::WorkerT::UVProcessFinished()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(Proc_.Mutex);
|
||||
if (Proc_.ROP && (Proc_.ROP->IsFinished() || !Proc_.ROP->IsStarted())) {
|
||||
Proc_.ROP.reset();
|
||||
}
|
||||
}
|
||||
// Notify idling thread
|
||||
Proc_.Condition.notify_one();
|
||||
}
|
||||
|
||||
void cmWorkerPool::ProcessResultT::reset()
|
||||
{
|
||||
ExitStatus = 0;
|
||||
TermSignal = 0;
|
||||
if (!StdOut.empty()) {
|
||||
StdOut.clear();
|
||||
StdOut.shrink_to_fit();
|
||||
}
|
||||
if (!StdErr.empty()) {
|
||||
StdErr.clear();
|
||||
StdErr.shrink_to_fit();
|
||||
}
|
||||
if (!ErrorMessage.empty()) {
|
||||
ErrorMessage.clear();
|
||||
ErrorMessage.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
cmWorkerPoolInternal::cmWorkerPoolInternal(cmWorkerPool* pool)
|
||||
: Pool(pool)
|
||||
{
|
||||
// Initialize libuv loop
|
||||
uv_disable_stdio_inheritance();
|
||||
#ifdef CMAKE_UV_SIGNAL_HACK
|
||||
UVHackRAII = cm::make_unique<cmUVSignalHackRAII>();
|
||||
#endif
|
||||
UVLoop = cm::make_unique<uv_loop_t>();
|
||||
uv_loop_init(UVLoop.get());
|
||||
}
|
||||
|
||||
cmWorkerPoolInternal::~cmWorkerPoolInternal()
|
||||
{
|
||||
uv_loop_close(UVLoop.get());
|
||||
}
|
||||
|
||||
bool cmWorkerPoolInternal::Process()
|
||||
{
|
||||
// Reset state
|
||||
Aborting = false;
|
||||
// Initialize libuv asynchronous request
|
||||
UVRequestBegin.init(*UVLoop, &cmWorkerPoolInternal::UVSlotBegin, this);
|
||||
UVRequestEnd.init(*UVLoop, &cmWorkerPoolInternal::UVSlotEnd, this);
|
||||
// Send begin request
|
||||
UVRequestBegin.send();
|
||||
// Run libuv loop
|
||||
return (uv_run(UVLoop.get(), UV_RUN_DEFAULT) == 0);
|
||||
}
|
||||
|
||||
void cmWorkerPoolInternal::Abort()
|
||||
{
|
||||
bool firstCall = false;
|
||||
// Clear all jobs and set abort flag
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(Mutex);
|
||||
if (!Aborting) {
|
||||
// Register abort and clear queue
|
||||
Aborting = true;
|
||||
Queue.clear();
|
||||
firstCall = true;
|
||||
}
|
||||
}
|
||||
if (firstCall) {
|
||||
// Wake threads
|
||||
Condition.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool cmWorkerPoolInternal::PushJob(cmWorkerPool::JobHandleT&& jobHandle)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(Mutex);
|
||||
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 true;
|
||||
}
|
||||
|
||||
void cmWorkerPoolInternal::UVSlotBegin(uv_async_t* handle)
|
||||
{
|
||||
auto& gint = *reinterpret_cast<cmWorkerPoolInternal*>(handle->data);
|
||||
// Create worker threads
|
||||
{
|
||||
unsigned int const num = gint.Pool->ThreadCount();
|
||||
// Create workers
|
||||
gint.Workers.reserve(num);
|
||||
for (unsigned int ii = 0; ii != num; ++ii) {
|
||||
gint.Workers.emplace_back(cm::make_unique<WorkerT>(ii));
|
||||
}
|
||||
// Start workers
|
||||
for (auto& wrk : gint.Workers) {
|
||||
wrk->Start(&gint);
|
||||
}
|
||||
}
|
||||
// Destroy begin request
|
||||
gint.UVRequestBegin.reset();
|
||||
}
|
||||
|
||||
void cmWorkerPoolInternal::UVSlotEnd(uv_async_t* handle)
|
||||
{
|
||||
auto& gint = *reinterpret_cast<cmWorkerPoolInternal*>(handle->data);
|
||||
// Join and destroy worker threads
|
||||
gint.Workers.clear();
|
||||
// Destroy end request
|
||||
gint.UVRequestEnd.reset();
|
||||
}
|
||||
|
||||
void cmWorkerPoolInternal::Work(WorkerT* worker)
|
||||
{
|
||||
std::unique_lock<std::mutex> uLock(Mutex);
|
||||
// Increment running workers count
|
||||
++WorkersRunning;
|
||||
// Enter worker main loop
|
||||
while (true) {
|
||||
// Abort on request
|
||||
if (Aborting) {
|
||||
break;
|
||||
}
|
||||
// Wait for new jobs
|
||||
if (Queue.empty()) {
|
||||
++WorkersIdle;
|
||||
Condition.wait(uLock);
|
||||
--WorkersIdle;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for fence jobs
|
||||
if (FenceProcessing || Queue.front()->IsFence()) {
|
||||
if (JobsProcessing != 0) {
|
||||
Condition.wait(uLock);
|
||||
continue;
|
||||
}
|
||||
// No jobs get processed. Set the fence job processing flag.
|
||||
FenceProcessing = true;
|
||||
}
|
||||
|
||||
// Pop next job from queue
|
||||
worker->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
|
||||
uLock.lock();
|
||||
}
|
||||
--JobsProcessing;
|
||||
|
||||
// Was this a fence job?
|
||||
if (FenceProcessing) {
|
||||
FenceProcessing = false;
|
||||
Condition.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
// Decrement running workers count
|
||||
if (--WorkersRunning == 0) {
|
||||
// Last worker thread about to finish. Send libuv event.
|
||||
UVRequestEnd.send();
|
||||
}
|
||||
}
|
||||
|
||||
cmWorkerPool::JobT::~JobT() = default;
|
||||
|
||||
bool cmWorkerPool::JobT::RunProcess(ProcessResultT& result,
|
||||
std::vector<std::string> const& command,
|
||||
std::string const& workingDirectory)
|
||||
{
|
||||
// Get worker by index
|
||||
auto* wrk = Pool_->Int_->Workers.at(WorkerIndex_).get();
|
||||
return wrk->RunProcess(result, command, workingDirectory);
|
||||
}
|
||||
|
||||
cmWorkerPool::cmWorkerPool()
|
||||
: Int_(cm::make_unique<cmWorkerPoolInternal>(this))
|
||||
{
|
||||
}
|
||||
|
||||
cmWorkerPool::~cmWorkerPool() = default;
|
||||
|
||||
bool cmWorkerPool::Process(unsigned int threadCount, 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 success;
|
||||
}
|
||||
|
||||
bool cmWorkerPool::PushJob(JobHandleT&& jobHandle)
|
||||
{
|
||||
return Int_->PushJob(std::move(jobHandle));
|
||||
}
|
||||
|
||||
void cmWorkerPool::Abort()
|
||||
{
|
||||
Int_->Abort();
|
||||
}
|
||||
@@ -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 cmWorkerPool_h
|
||||
#define cmWorkerPool_h
|
||||
|
||||
#include "cmConfigure.h" // IWYU pragma: keep
|
||||
|
||||
#include "cmAlgorithms.h" // IWYU pragma: keep
|
||||
|
||||
#include <memory> // IWYU pragma: keep
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// -- Types
|
||||
class cmWorkerPoolInternal;
|
||||
|
||||
/** @class cmWorkerPool
|
||||
* @brief Thread pool with job queue
|
||||
*/
|
||||
class cmWorkerPool
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract job class for concurrent job processing.
|
||||
*/
|
||||
class JobT
|
||||
{
|
||||
public:
|
||||
JobT(JobT const&) = delete;
|
||||
JobT& operator=(JobT const&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor.
|
||||
*/
|
||||
virtual ~JobT();
|
||||
|
||||
/**
|
||||
* @brief Fence job flag
|
||||
*
|
||||
* Fence jobs require that:
|
||||
* - all jobs before in the queue have been processed
|
||||
* - no jobs later in the queue will be processed before this job was
|
||||
* processed
|
||||
*/
|
||||
bool IsFence() const { return Fence_; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Protected default constructor
|
||||
*/
|
||||
JobT(bool fence = false)
|
||||
: Fence_(fence)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract processing interface that must be implement in derived classes.
|
||||
*/
|
||||
virtual void Process() = 0;
|
||||
|
||||
/**
|
||||
* Get the worker pool.
|
||||
* Only valid during the JobT::Process() call!
|
||||
*/
|
||||
cmWorkerPool* Pool() const { return Pool_; }
|
||||
|
||||
/**
|
||||
* Get the user data.
|
||||
* Only valid during the JobT::Process() call!
|
||||
*/
|
||||
void* UserData() const { return Pool_->UserData(); };
|
||||
|
||||
/**
|
||||
* Get the worker index.
|
||||
* This is the index of the thread processing this job and is in the range
|
||||
* [0..ThreadCount).
|
||||
* Concurrently processing jobs will never have the same WorkerIndex().
|
||||
* Only valid during the JobT::Process() call!
|
||||
*/
|
||||
unsigned int WorkerIndex() const { return WorkerIndex_; }
|
||||
|
||||
/**
|
||||
* Run an external read only process.
|
||||
* Use only during JobT::Process() call!
|
||||
*/
|
||||
bool RunProcess(ProcessResultT& result,
|
||||
std::vector<std::string> const& command,
|
||||
std::string const& workingDirectory);
|
||||
|
||||
private:
|
||||
//! Needs access to Work()
|
||||
friend class cmWorkerPoolInternal;
|
||||
//! Worker thread entry method.
|
||||
void Work(cmWorkerPool* pool, unsigned int workerIndex)
|
||||
{
|
||||
Pool_ = pool;
|
||||
WorkerIndex_ = workerIndex;
|
||||
this->Process();
|
||||
}
|
||||
|
||||
private:
|
||||
cmWorkerPool* Pool_ = nullptr;
|
||||
unsigned int WorkerIndex_ = 0;
|
||||
bool Fence_ = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Job handle type
|
||||
*/
|
||||
typedef std::unique_ptr<JobT> JobHandleT;
|
||||
|
||||
/**
|
||||
* @brief Fence job base class
|
||||
*/
|
||||
class JobFenceT : public JobT
|
||||
{
|
||||
public:
|
||||
JobFenceT()
|
||||
: JobT(true)
|
||||
{
|
||||
}
|
||||
//! Does nothing
|
||||
void Process() override{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Fence job that aborts the worker pool.
|
||||
* This class is useful as the last job in the job queue.
|
||||
*/
|
||||
class JobEndT : JobFenceT
|
||||
{
|
||||
public:
|
||||
//! Does nothing
|
||||
void Process() override { Pool()->Abort(); }
|
||||
};
|
||||
|
||||
public:
|
||||
// -- Methods
|
||||
cmWorkerPool();
|
||||
~cmWorkerPool();
|
||||
|
||||
/**
|
||||
* @brief 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.
|
||||
*/
|
||||
bool Process(unsigned int threadCount, void* userData = nullptr);
|
||||
|
||||
/**
|
||||
* Number of worker threads passed to Process().
|
||||
* Only valid during Process().
|
||||
*/
|
||||
unsigned int ThreadCount() const { return ThreadCount_; }
|
||||
|
||||
/**
|
||||
* User data reference passed to Process().
|
||||
* Only valid during Process().
|
||||
*/
|
||||
void* UserData() const { return UserData_; }
|
||||
|
||||
// -- Job processing interface
|
||||
|
||||
/**
|
||||
* @brief 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.
|
||||
*
|
||||
* This method is thread safe and can be called from inside a job or before
|
||||
* Process().
|
||||
*/
|
||||
bool PushJob(JobHandleT&& jobHandle);
|
||||
|
||||
/**
|
||||
* @brief Push job to the queue
|
||||
*
|
||||
* This method is thread safe and can be called from inside a job or before
|
||||
* Process().
|
||||
*/
|
||||
template <class T, typename... Args>
|
||||
bool EmplaceJob(Args&&... args)
|
||||
{
|
||||
return PushJob(cm::make_unique<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
private:
|
||||
void* UserData_ = nullptr;
|
||||
unsigned int ThreadCount_ = 0;
|
||||
std::unique_ptr<cmWorkerPoolInternal> Int_;
|
||||
};
|
||||
|
||||
#endif
|
||||
+2
-2
@@ -7,7 +7,7 @@
|
||||
#include "cmGlobalGenerator.h"
|
||||
#include "cmLocalGenerator.h"
|
||||
#include "cmMakefile.h"
|
||||
#include "cmQtAutoGeneratorMocUic.h"
|
||||
#include "cmQtAutoMocUic.h"
|
||||
#include "cmQtAutoRcc.h"
|
||||
#include "cmRange.h"
|
||||
#include "cmState.h"
|
||||
@@ -1018,7 +1018,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args)
|
||||
|
||||
#ifdef CMAKE_BUILD_WITH_CMAKE
|
||||
if ((args[1] == "cmake_autogen") && (args.size() >= 4)) {
|
||||
cmQtAutoGeneratorMocUic autoGen;
|
||||
cmQtAutoMocUic autoGen;
|
||||
std::string const& infoDir = args[2];
|
||||
std::string const& config = args[3];
|
||||
return autoGen.Run(infoDir, config) ? 0 : 1;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(ManySources)
|
||||
include("../AutogenGuiTest.cmake")
|
||||
|
||||
# Test AUTOMOC and AUTOUIC on many source files to stress the concurrent
|
||||
# parsing and processing framework.
|
||||
|
||||
set(CSD ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(CBD ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(SRCS main.cpp)
|
||||
set(MAIN_INCLUDES "\n// Item includes\n")
|
||||
set(MAIN_ITEMS "\n// Items\n")
|
||||
|
||||
set(NUM 24)
|
||||
foreach(III RANGE 1 ${NUM})
|
||||
configure_file(${CSD}/object.h.in ${CBD}/object_${III}.h)
|
||||
configure_file(${CSD}/item.h.in ${CBD}/item_${III}.h)
|
||||
configure_file(${CSD}/item.cpp.in ${CBD}/item_${III}.cpp)
|
||||
configure_file(${CSD}/view.ui.in ${CBD}/view_${III}.ui)
|
||||
configure_file(${CSD}/data.qrc.in ${CBD}/data_${III}.qrc)
|
||||
|
||||
list(APPEND SRCS ${CBD}/item_${III}.cpp)
|
||||
list(APPEND SRCS ${CBD}/data_${III}.qrc)
|
||||
|
||||
string(APPEND MAIN_INCLUDES "#include \"item_${III}.h\"\n")
|
||||
string(APPEND MAIN_ITEMS "Item_${III} item_${III};\n")
|
||||
string(APPEND MAIN_ITEMS "item_${III}.TheSlot();\n")
|
||||
endforeach()
|
||||
|
||||
configure_file(${CSD}/main.cpp.in ${CBD}/main.cpp)
|
||||
|
||||
add_executable(manySources ${SRCS} ${CBD}/main.cpp)
|
||||
target_link_libraries(manySources ${QT_LIBRARIES})
|
||||
set_target_properties(manySources PROPERTIES AUTOMOC 1 AUTOUIC 1 AUTORCC 1)
|
||||
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file>object_@III@.h</file>
|
||||
<file>item_@III@.h</file>
|
||||
<file>item_@III@.cpp</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "item_@III@.h"
|
||||
#include "object_@III@.h"
|
||||
// AUTOUIC include
|
||||
#include <ui_view_@III@.h>
|
||||
|
||||
class LocalObject_@III@ : public QObject
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
LocalObject_@III@() = default;
|
||||
~LocalObject_@III@() = default;
|
||||
};
|
||||
|
||||
void Item_@III@ ::TheSlot()
|
||||
{
|
||||
LocalObject_@III@ localObject;
|
||||
Object_@III@ obj;
|
||||
obj.ObjectSlot();
|
||||
|
||||
Ui_View_@III@ ui_view;
|
||||
}
|
||||
|
||||
// AUTOMOC includes
|
||||
#include "item_@III@.moc"
|
||||
#include "moc_item_@III@.cpp"
|
||||
#include "moc_object_@III@.cpp"
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef ITEM_@III@HPP
|
||||
#define ITEM_@III@HPP
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class Item_@III@ : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_SLOT
|
||||
void TheSlot();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
@MAIN_INCLUDES@
|
||||
|
||||
int main(int argv, char** args)
|
||||
{
|
||||
@MAIN_ITEMS@
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef OBJECT_@III@H
|
||||
#define OBJECT_@III@H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class Object_@III@ : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_SLOT
|
||||
void ObjectSlot(){};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>View_@III@</class>
|
||||
<widget class="QWidget" name="Base">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QTreeView" name="treeView"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -5,6 +5,7 @@ ADD_AUTOGEN_TEST(AutogenTargetDepends)
|
||||
ADD_AUTOGEN_TEST(Complex QtAutogen)
|
||||
ADD_AUTOGEN_TEST(GlobalAutogenTarget)
|
||||
ADD_AUTOGEN_TEST(LowMinimumVersion lowMinimumVersion)
|
||||
ADD_AUTOGEN_TEST(ManySources manySources)
|
||||
ADD_AUTOGEN_TEST(MocOnly mocOnly)
|
||||
ADD_AUTOGEN_TEST(MocOptions mocOptions)
|
||||
ADD_AUTOGEN_TEST(ObjectLibrary someProgram)
|
||||
|
||||
Reference in New Issue
Block a user