ExtraCompiler: Run extra compiler in a thread

and make sure there are not too many of these threads
running at any time. This stops the massive process
startup when loading a project with many UI files, etc.

Task-number: QTCREATORBUG-15795
Change-Id: Icfcddd80d04e36b61ecafbbefe5a1a8b7ea02ec6
Reviewed-by: Tobias Hunger <tobias.hunger@theqtcompany.com>
Reviewed-by: Ulf Hermann <ulf.hermann@theqtcompany.com>
This commit is contained in:
Tobias Hunger
2016-03-03 13:56:05 +01:00
parent 119a7dfd20
commit 972ea4cba0
7 changed files with 282 additions and 129 deletions

View File

@@ -98,7 +98,7 @@ void GeneratedCodeModelSupport::onContentsChanged(const Utils::FileName &file)
void GeneratedCodeModelSupport::init() const void GeneratedCodeModelSupport::init() const
{ {
connect(m_generator, &ProjectExplorer::ExtraCompiler::contentsChanged, connect(m_generator, &ProjectExplorer::ExtraCompiler::contentsChanged,
this, &GeneratedCodeModelSupport::onContentsChanged); this, &GeneratedCodeModelSupport::onContentsChanged, Qt::QueuedConnection);
} }
QByteArray GeneratedCodeModelSupport::contents() const QByteArray GeneratedCodeModelSupport::contents() const

View File

@@ -24,26 +24,33 @@
****************************************************************************/ ****************************************************************************/
#include "extracompiler.h" #include "extracompiler.h"
#include "buildconfiguration.h"
#include "buildmanager.h" #include "buildmanager.h"
#include "kitinformation.h"
#include "session.h" #include "session.h"
#include "target.h" #include "target.h"
#include "buildconfiguration.h"
#include "kitinformation.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/idocument.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <texteditor/texteditorsettings.h> #include <texteditor/texteditorsettings.h>
#include <texteditor/texteditorconstants.h> #include <texteditor/texteditorconstants.h>
#include <texteditor/fontsettings.h> #include <texteditor/fontsettings.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <coreplugin/idocument.h> #include <utils/runextensions.h>
#include <coreplugin/editormanager/editormanager.h>
#include <QDateTime> #include <QDateTime>
#include <QFutureWatcher>
#include <QProcess>
#include <QThreadPool>
#include <QTimer> #include <QTimer>
#include <QTextBlock> #include <QTextBlock>
namespace ProjectExplorer { namespace ProjectExplorer {
Q_GLOBAL_STATIC(QThreadPool, s_extraCompilerThreadPool);
Q_GLOBAL_STATIC(QList<ExtraCompilerFactory *>, factories); Q_GLOBAL_STATIC(QList<ExtraCompilerFactory *>, factories);
class ExtraCompilerPrivate class ExtraCompilerPrivate
@@ -55,7 +62,7 @@ public:
Utils::FileNameList targets; Utils::FileNameList targets;
QList<Task> issues; QList<Task> issues;
QDateTime compileTime; QDateTime compileTime;
Core::IEditor *lastEditor = 0; Core::IEditor *lastEditor = nullptr;
QMetaObject::Connection activeBuildConfigConnection; QMetaObject::Connection activeBuildConfigConnection;
QMetaObject::Connection activeEnvironmentConnection; QMetaObject::Connection activeEnvironmentConnection;
bool dirty = false; bool dirty = false;
@@ -80,8 +87,8 @@ ExtraCompiler::ExtraCompiler(const Project *project, const Utils::FileName &sour
connect(&d->timer, &QTimer::timeout, this, [this](){ connect(&d->timer, &QTimer::timeout, this, [this](){
if (d->dirty && d->lastEditor) { if (d->dirty && d->lastEditor) {
run(d->lastEditor->document()->contents());
d->dirty = false; d->dirty = false;
run(d->lastEditor->document()->contents());
} }
}); });
@@ -122,13 +129,8 @@ ExtraCompiler::ExtraCompiler(const Project *project, const Utils::FileName &sour
} }
if (d->dirty) { if (d->dirty) {
// Run in the next event loop, as run() is not available yet in the ctor. d->dirty = false;
QTimer::singleShot(0, this, [this](){ QTimer::singleShot(0, this, [this]() { run(d->source); }); // delay till available.
QFile file(d->source.toString());
if (file.open(QFile::ReadOnly | QFile::Text))
run(file.readAll());
d->dirty = false;
});
} }
} }
@@ -167,6 +169,11 @@ QDateTime ExtraCompiler::compileTime() const
return d->compileTime; return d->compileTime;
} }
QThreadPool *ExtraCompiler::extraCompilerThreadPool()
{
return s_extraCompilerThreadPool();
}
void ExtraCompiler::onTargetsBuilt(Project *project) void ExtraCompiler::onTargetsBuilt(Project *project)
{ {
if (project != d->project || BuildManager::isBuilding(project)) if (project != d->project || BuildManager::isBuilding(project))
@@ -203,8 +210,8 @@ void ExtraCompiler::onEditorChanged(Core::IEditor *editor)
this, &ExtraCompiler::setDirty); this, &ExtraCompiler::setDirty);
if (d->dirty) { if (d->dirty) {
run(doc->contents());
d->dirty = false; d->dirty = false;
run(doc->contents());
} }
} }
@@ -216,7 +223,7 @@ void ExtraCompiler::onEditorChanged(Core::IEditor *editor)
connect(d->lastEditor->document(), &Core::IDocument::contentsChanged, connect(d->lastEditor->document(), &Core::IDocument::contentsChanged,
this, &ExtraCompiler::setDirty); this, &ExtraCompiler::setDirty);
} else { } else {
d->lastEditor = 0; d->lastEditor = nullptr;
} }
} }
@@ -237,10 +244,10 @@ void ExtraCompiler::onEditorAboutToClose(Core::IEditor *editor)
disconnect(doc, &Core::IDocument::contentsChanged, disconnect(doc, &Core::IDocument::contentsChanged,
this, &ExtraCompiler::setDirty); this, &ExtraCompiler::setDirty);
if (d->dirty) { if (d->dirty) {
run(doc->contents());
d->dirty = false; d->dirty = false;
run(doc->contents());
} }
d->lastEditor = 0; d->lastEditor = nullptr;
} }
void ExtraCompiler::onActiveTargetChanged() void ExtraCompiler::onActiveTargetChanged()
@@ -355,4 +362,114 @@ QList<ExtraCompilerFactory *> ExtraCompilerFactory::extraCompilerFactories()
return *factories(); return *factories();
} }
ProcessExtraCompiler::ProcessExtraCompiler(const Project *project, const Utils::FileName &source,
const Utils::FileNameList &targets, QObject *parent) :
ExtraCompiler(project, source, targets, parent)
{ }
void ProcessExtraCompiler::run(const QByteArray &sourceContents)
{
ContentProvider contents = [this, sourceContents]() { return sourceContents; };
runImpl(contents);
}
void ProcessExtraCompiler::run(const Utils::FileName &fileName)
{
ContentProvider contents = [this, fileName]() {
QFile file(fileName.toString());
if (!file.open(QFile::ReadOnly | QFile::Text))
return QByteArray();
return file.readAll();
};
runImpl(contents);
}
Utils::FileName ProcessExtraCompiler::workingDirectory() const
{
return Utils::FileName();
}
QStringList ProcessExtraCompiler::arguments() const
{
return QStringList();
}
bool ProcessExtraCompiler::prepareToRun(const QByteArray &sourceContents)
{
Q_UNUSED(sourceContents);
return true;
}
QList<Task> ProcessExtraCompiler::parseIssues(const QByteArray &stdErr)
{
Q_UNUSED(stdErr);
return QList<Task>();
}
void ProcessExtraCompiler::runImpl(const ContentProvider &provider)
{
if (m_watcher)
delete m_watcher;
m_watcher = new QFutureWatcher<QList<QByteArray>>();
connect(m_watcher, &QFutureWatcher<QList<QByteArray>>::finished,
this, &ProcessExtraCompiler::cleanUp);
m_watcher->setFuture(Utils::runAsync(extraCompilerThreadPool(),
&ProcessExtraCompiler::runInThread, this,
command(), workingDirectory(), arguments(), provider,
buildEnvironment()));
}
QList<QByteArray> ProcessExtraCompiler::runInThread(const Utils::FileName &cmd, const Utils::FileName &workDir,
const QStringList &args, const ContentProvider &provider,
const Utils::Environment &env)
{
if (cmd.isEmpty() || !cmd.toFileInfo().isExecutable())
return QList<QByteArray>();
const QByteArray sourceContents = provider();
if (sourceContents.isNull() || !prepareToRun(sourceContents))
return QList<QByteArray>();
QProcess process;
process.setProcessEnvironment(env.toProcessEnvironment());
if (!workDir.isEmpty())
process.setWorkingDirectory(workDir.toString());
process.start(cmd.toString(), args, QIODevice::ReadWrite);
if (!process.waitForStarted()) {
handleProcessError(&process);
return QList<QByteArray>();
}
handleProcessStarted(&process, sourceContents);
process.waitForFinished();
if (process.state() == QProcess::Running) {
process.kill();
process.waitForFinished(3000);
}
return handleProcessFinished(&process);
}
void ProcessExtraCompiler::cleanUp()
{
QTC_ASSERT(m_watcher, return);
const QList<QByteArray> data = m_watcher->future().result();
delete m_watcher;
m_watcher = nullptr;
if (data.isEmpty())
return; // There was some kind of error...
const Utils::FileNameList targetList = targets();
QTC_ASSERT(data.count() == targetList.count(), return);
for (int i = 0; i < targetList.count(); ++i)
setContent(targetList.at(i), data.at(i));
setCompileTime(QDateTime::currentDateTime());
}
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -33,6 +33,13 @@
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <QByteArray>
#include <QFuture>
#include <QList>
QT_FORWARD_DECLARE_CLASS(QProcess);
QT_FORWARD_DECLARE_CLASS(QThreadPool);
namespace ProjectExplorer { namespace ProjectExplorer {
class ExtraCompilerPrivate; class ExtraCompilerPrivate;
@@ -42,8 +49,8 @@ class PROJECTEXPLORER_EXPORT ExtraCompiler : public QObject
public: public:
ExtraCompiler(const Project *project, const Utils::FileName &source, ExtraCompiler(const Project *project, const Utils::FileName &source,
const Utils::FileNameList &targets, QObject *parent = 0); const Utils::FileNameList &targets, QObject *parent = nullptr);
virtual ~ExtraCompiler() override; ~ExtraCompiler() override;
const Project *project() const; const Project *project() const;
Utils::FileName source() const; Utils::FileName source() const;
@@ -58,6 +65,8 @@ public:
void setCompileTime(const QDateTime &time); void setCompileTime(const QDateTime &time);
QDateTime compileTime() const; QDateTime compileTime() const;
static QThreadPool *extraCompilerThreadPool();
signals: signals:
void contentsChanged(const Utils::FileName &file); void contentsChanged(const Utils::FileName &file);
@@ -72,16 +81,60 @@ private:
void onActiveTargetChanged(); void onActiveTargetChanged();
void onActiveBuildConfigurationChanged(); void onActiveBuildConfigurationChanged();
void setDirty(); void setDirty();
// This method may not block!
virtual void run(const QByteArray &sourceContent) = 0; virtual void run(const QByteArray &sourceContent) = 0;
virtual void run(const Utils::FileName &file) = 0;
ExtraCompilerPrivate *const d; ExtraCompilerPrivate *const d;
}; };
class PROJECTEXPLORER_EXPORT ProcessExtraCompiler : public ExtraCompiler
{
Q_OBJECT
public:
ProcessExtraCompiler(const Project *project, const Utils::FileName &source,
const Utils::FileNameList &targets, QObject *parent = nullptr);
protected:
// This will run a process in a thread, if
// * command() does not return an empty file name
// * command() is exectuable
// * prepareToRun returns true
// * The process is not yet running
void run(const QByteArray &sourceContents) override;
void run(const Utils::FileName &fileName) override;
// Information about the process to run:
virtual Utils::FileName workingDirectory() const;
virtual Utils::FileName command() const = 0;
virtual QStringList arguments() const;
virtual bool prepareToRun(const QByteArray &sourceContents);
virtual void handleProcessError(QProcess *process) { Q_UNUSED(process); }
virtual void handleProcessStarted(QProcess *process, const QByteArray &sourceContents)
{ Q_UNUSED(process); Q_UNUSED(sourceContents); }
virtual QList<QByteArray> handleProcessFinished(QProcess *process) = 0;
virtual QList<Task> parseIssues(const QByteArray &stdErr);
private:
using ContentProvider = std::function<QByteArray()>;
void runImpl(const ContentProvider &sourceContents);
QList<QByteArray> runInThread(const Utils::FileName &cmd, const Utils::FileName &workDir,
const QStringList &args, const ContentProvider &provider,
const Utils::Environment &env);
void cleanUp();
QFutureWatcher<QList<QByteArray>> *m_watcher = nullptr;
};
class PROJECTEXPLORER_EXPORT ExtraCompilerFactory : public QObject class PROJECTEXPLORER_EXPORT ExtraCompilerFactory : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit ExtraCompilerFactory(QObject *parent = 0); explicit ExtraCompilerFactory(QObject *parent = nullptr);
virtual FileType sourceType() const = 0; virtual FileType sourceType() const = 0;
virtual QString sourceTag() const = 0; virtual QString sourceTag() const = 0;

View File

@@ -42,13 +42,10 @@ static const char TaskCategory[] = "Task.Category.ExtraCompiler.QScxmlc";
QScxmlcGenerator::QScxmlcGenerator(const ProjectExplorer::Project *project, QScxmlcGenerator::QScxmlcGenerator(const ProjectExplorer::Project *project,
const Utils::FileName &source, const Utils::FileName &source,
const Utils::FileNameList &targets, QObject *parent) : const Utils::FileNameList &targets, QObject *parent) :
ProjectExplorer::ExtraCompiler(project, source, targets, parent) ProjectExplorer::ProcessExtraCompiler(project, source, targets, parent)
{ { }
connect(&m_process, static_cast<void(QProcess::*)(int)>(&QProcess::finished),
this, &QScxmlcGenerator::finishProcess);
}
void QScxmlcGenerator::parseIssues(const QByteArray &processStderr) QList<ProjectExplorer::Task> QScxmlcGenerator::parseIssues(const QByteArray &processStderr)
{ {
QList<ProjectExplorer::Task> issues; QList<ProjectExplorer::Task> issues;
foreach (const QByteArray &line, processStderr.split('\n')) { foreach (const QByteArray &line, processStderr.split('\n')) {
@@ -64,30 +61,13 @@ void QScxmlcGenerator::parseIssues(const QByteArray &processStderr)
issues.append(ProjectExplorer::Task(type, message, file, line, TaskCategory)); issues.append(ProjectExplorer::Task(type, message, file, line, TaskCategory));
} }
} }
setCompileIssues(issues); return issues;
} }
void QScxmlcGenerator::finishProcess()
Utils::FileName QScxmlcGenerator::command() const
{ {
parseIssues(m_process.readAllStandardError()); QtSupport::BaseQtVersion *version = nullptr;
setCompileTime(QDateTime::currentDateTime());
foreach (const Utils::FileName &target, targets()) {
QFile generated(m_tmpdir.path() + QLatin1Char('/') + target.fileName());
if (!generated.open(QIODevice::ReadOnly))
continue;
setContent(target, generated.readAll());
}
}
void QScxmlcGenerator::run(const QByteArray &sourceContent)
{
if (m_process.state() != QProcess::NotRunning) {
m_process.kill();
m_process.waitForFinished(3000);
}
QtSupport::BaseQtVersion *version = 0;
ProjectExplorer::Target *target; ProjectExplorer::Target *target;
if ((target = project()->activeTarget())) if ((target = project()->activeTarget()))
version = QtSupport::QtKitInformation::qtVersion(target->kit()); version = QtSupport::QtKitInformation::qtVersion(target->kit());
@@ -95,28 +75,61 @@ void QScxmlcGenerator::run(const QByteArray &sourceContent)
version = QtSupport::QtKitInformation::qtVersion(ProjectExplorer::KitManager::defaultKit()); version = QtSupport::QtKitInformation::qtVersion(ProjectExplorer::KitManager::defaultKit());
if (!version) if (!version)
return; return Utils::FileName();
const QString generator = version->qscxmlcCommand(); return Utils::FileName::fromString(version->qscxmlcCommand());
if (!QFileInfo(generator).isExecutable()) }
return;
m_process.setProcessEnvironment(buildEnvironment().toProcessEnvironment()); QStringList QScxmlcGenerator::arguments() const
m_process.setWorkingDirectory(m_tmpdir.path()); {
QTC_ASSERT(targets().count() == 2, return QStringList());
QFile input(m_tmpdir.path() + QLatin1Char('/') + source().fileName()); const Utils::FileName fn = tmpFile();
const QString header = m_tmpdir.path() + QLatin1Char('/') + targets()[0].fileName();
const QString impl = m_tmpdir.path() + QLatin1Char('/') + targets()[1].fileName();
return QStringList({ QLatin1String("--header"), header, QLatin1String("--impl"), impl,
fn.fileName() });
}
Utils::FileName QScxmlcGenerator::workingDirectory() const
{
return Utils::FileName::fromString(m_tmpdir.path());
}
bool QScxmlcGenerator::prepareToRun(const QByteArray &sourceContents)
{
const Utils::FileName fn = tmpFile();
QFile input(fn.toString());
if (!input.open(QIODevice::WriteOnly)) if (!input.open(QIODevice::WriteOnly))
return; return false;
input.write(sourceContent); input.write(sourceContents);
input.close(); input.close();
qCDebug(log) << " QScxmlcGenerator::run " << generator << " on " return true;
<< sourceContent.size() << " bytes"; }
m_process.start(generator, QStringList({ QList<QByteArray> QScxmlcGenerator::handleProcessFinished(QProcess *process)
QLatin1String("--header"), m_tmpdir.path() + QLatin1Char('/') + targets()[0].fileName(), {
QLatin1String("--impl"), m_tmpdir.path() + QLatin1Char('/') + targets()[1].fileName(), Q_UNUSED(process);
input.fileName()})); const Utils::FileName wd = workingDirectory();
QList<QByteArray> result;
foreach (const Utils::FileName &target, targets()) {
Utils::FileName file = wd;
file.appendPath(target.fileName());
QFile generated(file.toString());
if (!generated.open(QIODevice::ReadOnly))
continue;
result << generated.readAll();
}
return result;
}
Utils::FileName QScxmlcGenerator::tmpFile() const
{
Utils::FileName wd = workingDirectory();
wd.appendPath(source().fileName());
return wd;
} }
ProjectExplorer::FileType QScxmlcGeneratorFactory::sourceType() const ProjectExplorer::FileType QScxmlcGeneratorFactory::sourceType() const

View File

@@ -33,20 +33,25 @@
namespace QtSupport { namespace QtSupport {
class QScxmlcGenerator : public ProjectExplorer::ExtraCompiler class QScxmlcGenerator : public ProjectExplorer::ProcessExtraCompiler
{ {
Q_OBJECT Q_OBJECT
public: public:
QScxmlcGenerator(const ProjectExplorer::Project *project, const Utils::FileName &source, QScxmlcGenerator(const ProjectExplorer::Project *project, const Utils::FileName &source,
const Utils::FileNameList &targets, QObject *parent = 0); const Utils::FileNameList &targets, QObject *parent = 0);
private: protected:
void finishProcess(); Utils::FileName command() const override;
void run(const QByteArray &sourceContent) override; QStringList arguments() const override;
Utils::FileName workingDirectory() const override;
private:
Utils::FileName tmpFile() const;
QList<QByteArray> handleProcessFinished(QProcess *process) override;
bool prepareToRun(const QByteArray &sourceContents) override;
QList<ProjectExplorer::Task> parseIssues(const QByteArray &processStderr) override;
QProcess m_process;
QTemporaryDir m_tmpdir; QTemporaryDir m_tmpdir;
void parseIssues(const QByteArray &processStderr);
}; };
class QScxmlcGeneratorFactory : public ProjectExplorer::ExtraCompilerFactory class QScxmlcGeneratorFactory : public ProjectExplorer::ExtraCompilerFactory

View File

@@ -39,43 +39,14 @@
namespace QtSupport { namespace QtSupport {
QLoggingCategory UicGenerator::m_log("qtc.uicgenerator");
UicGenerator::UicGenerator(const ProjectExplorer::Project *project, const Utils::FileName &source, UicGenerator::UicGenerator(const ProjectExplorer::Project *project, const Utils::FileName &source,
const Utils::FileNameList &targets, QObject *parent) : const Utils::FileNameList &targets, QObject *parent) :
ProjectExplorer::ExtraCompiler(project, source, targets, parent) ProjectExplorer::ProcessExtraCompiler(project, source, targets, parent)
{ }
Utils::FileName UicGenerator::command() const
{ {
connect(&m_process, static_cast<void(QProcess::*)(int)>(&QProcess::finished), QtSupport::BaseQtVersion *version = nullptr;
this, &UicGenerator::finishProcess);
}
void UicGenerator::finishProcess()
{
if (!m_process.waitForFinished(3000)
&& m_process.exitStatus() != QProcess::NormalExit
&& m_process.exitCode() != 0) {
qCDebug(m_log) << "finish process: failed" << m_process.readAllStandardError();
m_process.kill();
return;
}
// As far as I can discover in the UIC sources, it writes out local 8-bit encoding. The
// conversion below is to normalize both the encoding, and the line terminators.
QByteArray normalized = QString::fromLocal8Bit(m_process.readAllStandardOutput()).toUtf8();
qCDebug(m_log) << "finish process: ok" << normalized.size() << "bytes.";
setCompileTime(QDateTime::currentDateTime());
setContent(targets()[0], normalized);
}
void UicGenerator::run(const QByteArray &sourceContent)
{
if (m_process.state() != QProcess::NotRunning) {
m_process.kill();
m_process.waitForFinished(3000);
}
QtSupport::BaseQtVersion *version = 0;
ProjectExplorer::Target *target; ProjectExplorer::Target *target;
if ((target = project()->activeTarget())) if ((target = project()->activeTarget()))
version = QtSupport::QtKitInformation::qtVersion(target->kit()); version = QtSupport::QtKitInformation::qtVersion(target->kit());
@@ -83,28 +54,25 @@ void UicGenerator::run(const QByteArray &sourceContent)
version = QtSupport::QtKitInformation::qtVersion(ProjectExplorer::KitManager::defaultKit()); version = QtSupport::QtKitInformation::qtVersion(ProjectExplorer::KitManager::defaultKit());
if (!version) if (!version)
return; return Utils::FileName();
const QString generator = version->uicCommand(); return Utils::FileName::fromString(version->uicCommand());
if (generator.isEmpty()) }
return;
m_process.setProcessEnvironment(buildEnvironment().toProcessEnvironment()); void UicGenerator::handleProcessStarted(QProcess *process, const QByteArray &sourceContents)
{
process->write(sourceContents);
process->closeWriteChannel();
}
qCDebug(m_log) << " UicGenerator::run " << generator << " on " QList<QByteArray> UicGenerator::handleProcessFinished(QProcess *process)
<< sourceContent.size() << " bytes"; {
m_process.start(generator, QStringList(), QIODevice::ReadWrite); if (process->exitStatus() != QProcess::NormalExit && process->exitCode() != 0)
if (!m_process.waitForStarted()) return QList<QByteArray>();
return;
m_process.write(sourceContent); // As far as I can discover in the UIC sources, it writes out local 8-bit encoding. The
if (!m_process.waitForBytesWritten(3000)) { // conversion below is to normalize both the encoding, and the line terminators.
qCDebug(m_log) << "failed" << m_process.readAllStandardError(); return { QString::fromLocal8Bit(process->readAllStandardOutput()).toUtf8() };
m_process.kill();
return;
}
m_process.closeWriteChannel();
} }
ProjectExplorer::FileType UicGeneratorFactory::sourceType() const ProjectExplorer::FileType UicGeneratorFactory::sourceType() const

View File

@@ -33,20 +33,17 @@
namespace QtSupport { namespace QtSupport {
class UicGenerator : public ProjectExplorer::ExtraCompiler class UicGenerator : public ProjectExplorer::ProcessExtraCompiler
{ {
Q_OBJECT Q_OBJECT
public: public:
UicGenerator(const ProjectExplorer::Project *project, const Utils::FileName &source, UicGenerator(const ProjectExplorer::Project *project, const Utils::FileName &source,
const Utils::FileNameList &targets, QObject *parent = 0); const Utils::FileNameList &targets, QObject *parent = 0);
private slots: protected:
void finishProcess(); Utils::FileName command() const override;
void run(const QByteArray &sourceContent) override; void handleProcessStarted(QProcess *process, const QByteArray &sourceContents) override;
QList<QByteArray> handleProcessFinished(QProcess *process) override;
private:
QProcess m_process;
static QLoggingCategory m_log;
}; };
class UicGeneratorFactory : public ProjectExplorer::ExtraCompilerFactory class UicGeneratorFactory : public ProjectExplorer::ExtraCompilerFactory