Clang: Add Process Generator

So far we only compiled the precompiled headers only sequentially. The
process generator is creating, managing a queue of processes to compile
the PCHs parallel.

Change-Id: I8075def4ef9e6b0191bbd51b3631d1c51ec7b361
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Marco Bubke
2017-01-31 14:21:05 +01:00
parent 92263a0535
commit ea4141ee14
23 changed files with 884 additions and 95 deletions

View File

@@ -27,6 +27,7 @@
#include <connectionserver.h>
#include <environment.h>
#include <pchcreator.h>
#include <pchgenerator.h>
#include <pchmanagerserver.h>
#include <pchmanagerclientproxy.h>
#include <projectparts.h>
@@ -36,12 +37,16 @@
#include <QCoreApplication>
#include <QFileSystemWatcher>
#include <QLoggingCategory>
#include <QProcess>
#include <QTemporaryDir>
#include <QTimer>
#include <thread>
using ClangBackEnd::ClangPathWatcher;
using ClangBackEnd::ConnectionServer;
using ClangBackEnd::PchCreator;
using ClangBackEnd::PchGenerator;
using ClangBackEnd::PchManagerClientProxy;
using ClangBackEnd::PchManagerServer;
using ClangBackEnd::ProjectParts;
@@ -60,6 +65,11 @@ public:
return QString(CLANG_COMPILER_PATH);
}
uint hardwareConcurrency() const
{
return std::thread::hardware_concurrency();
}
private:
QTemporaryDir temporaryDirectory;
};
@@ -96,13 +106,17 @@ int main(int argc, char *argv[])
StringCache<Utils::SmallString> filePathCache;
ClangPathWatcher<QFileSystemWatcher, QTimer> includeWatcher(filePathCache);
ApplicationEnvironment environment;
PchGenerator<QProcess> pchGenerator(environment);
PchCreator pchCreator(environment, filePathCache);
pchCreator.setGenerator(&pchGenerator);
ProjectParts projectParts;
PchManagerServer clangPchManagerServer(filePathCache,
includeWatcher,
pchCreator,
projectParts);
includeWatcher.setNotifier(&clangPchManagerServer);
pchGenerator.setNotifier(&clangPchManagerServer);
ConnectionServer<PchManagerServer, PchManagerClientProxy> connectionServer(connection);
connectionServer.start();
connectionServer.setServer(&clangPchManagerServer);

View File

@@ -10,7 +10,9 @@ SOURCES += \
$$PWD/pchcreatorinterface.cpp \
$$PWD/clangpathwatcherinterface.cpp \
$$PWD/projectpartsinterface.cpp \
$$PWD/clangpathwatchernotifier.cpp
$$PWD/clangpathwatchernotifier.cpp \
$$PWD/pchgeneratornotifierinterface.cpp \
$$PWD/pchgeneratorinterface.cpp
HEADERS += \
$$PWD/clangpchmanagerbackend_global.h \
@@ -30,4 +32,7 @@ HEADERS += \
$$PWD/clangpathwatcherinterface.h \
$$PWD/projectpartsinterface.h \
$$PWD/clangpathwatchernotifier.h \
$$PWD/changedfilepathcompressor.h
$$PWD/changedfilepathcompressor.h \
$$PWD/pchgenerator.h \
$$PWD/pchgeneratornotifierinterface.h \
$$PWD/pchgeneratorinterface.h

View File

@@ -64,6 +64,7 @@ public:
llvm::StringRef /*relativePath*/,
const clang::Module */*imported*/) override
{
if (file) {
auto fileUID = file->getUID();
flagIncludeAlreadyRead(file);
@@ -77,6 +78,7 @@ public:
}
}
}
}
bool isNotInExcludedIncludeUID(uint uid) const
{

View File

@@ -34,6 +34,7 @@ class Environment
public:
virtual QString pchBuildDirectory() const = 0;
virtual QString clangCompilerPath() const = 0;
virtual uint hardwareConcurrency() const = 0;
};
} // namespace ClangBackEnd

View File

@@ -33,7 +33,6 @@
#include <QCryptographicHash>
#include <QFile>
#include <QProcess>
namespace ClangBackEnd {
@@ -45,10 +44,12 @@ PchCreator::PchCreator(Environment &environment, StringCache<Utils::SmallString>
PchCreator::PchCreator(V2::ProjectPartContainers &&projectsParts,
Environment &environment,
StringCache<Utils::SmallString> &filePathCache)
StringCache<Utils::SmallString> &filePathCache,
PchGeneratorInterface *pchGenerator)
: m_projectParts(std::move(projectsParts)),
m_environment(environment),
m_filePathCache(filePathCache)
m_filePathCache(filePathCache),
m_pchGenerator(pchGenerator)
{
}
@@ -258,23 +259,17 @@ std::unique_ptr<QFile> PchCreator::generateGlobalPchHeaderFile()
generateGlobalPchHeaderFileContent());
}
void PchCreator::generatePch(const Utils::SmallStringVector &clangCompilerArguments)
void PchCreator::generatePch(Utils::SmallStringVector &&compilerArguments,
ProjectPartPch &&projectPartPch)
{
QProcess process;
process.setProcessChannelMode(QProcess::ForwardedChannels);
process.start(m_environment.clangCompilerPath(),
convertToQStringList(clangCompilerArguments));
process.waitForFinished(100000);
checkIfProcessHasError(process);
m_pchGenerator->startTask(std::move(compilerArguments), std::move(projectPartPch));
}
void PchCreator::generateGlobalPch()
{
generateGlobalPchHeaderFile();
generatePch(generateGlobalClangCompilerArguments());
generatePch(generateGlobalClangCompilerArguments(), ProjectPartPch());
}
QStringList PchCreator::convertToQStringList(const Utils::SmallStringVector &compilerArguments)
@@ -310,14 +305,6 @@ QByteArray PchCreator::globalProjectHash() const
return result.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
}
void PchCreator::checkIfProcessHasError(const QProcess &process)
{
if (process.exitCode()) {
const std::string errorString = process.errorString().toStdString();
throw PchNotCreatedError(errorString);
}
}
Utils::SmallString PchCreator::generateGlobalPchFilePathWithoutExtension() const
{
QByteArray fileName = m_environment.pchBuildDirectory().toUtf8();
@@ -449,27 +436,25 @@ Utils::SmallStringVector PchCreator::generateProjectPartClangCompilerArguments(
return compilerArguments;
}
std::pair<ProjectPartPch, IdPaths> PchCreator::generateProjectPartPch(
const V2::ProjectPartContainer &projectPart)
IdPaths PchCreator::generateProjectPartPch(const V2::ProjectPartContainer &projectPart)
{
auto includes = generateProjectPartPchIncludes(projectPart);
auto content = generatePchIncludeFileContent(includes);
auto pchIncludeFilePath = generateProjectPathPchHeaderFilePath(projectPart);
auto pchFilePath = generateProjectPartPchFilePath(projectPart);
auto file = generatePchHeaderFile(pchIncludeFilePath, content);
generatePchHeaderFile(pchIncludeFilePath, content);
generatePch(generateProjectPartClangCompilerArguments(projectPart));
generatePch(generateProjectPartClangCompilerArguments(projectPart),
{projectPart.projectPartId().clone(), std::move(pchFilePath)});
return {{projectPart.projectPartId().clone(), std::move(pchFilePath)},
{projectPart.projectPartId().clone(), includes}};
return {projectPart.projectPartId().clone(), std::move(includes)};
}
void PchCreator::generatePchs()
{
for (const V2::ProjectPartContainer &projectPart : m_projectParts) {
auto projectInfos = generateProjectPartPch(projectPart);
m_projectPartPchs.push_back(projectInfos.first);
m_projectsIncludeIds.push_back(projectInfos.second);
auto includePaths = generateProjectPartPch(projectPart);
m_projectsIncludeIds.push_back(std::move(includePaths));
}
}
@@ -480,16 +465,16 @@ void PchCreator::generatePchs(V2::ProjectPartContainers &&projectsParts)
generatePchs();
}
std::vector<ProjectPartPch> PchCreator::takeProjectPartPchs()
{
return std::move(m_projectPartPchs);
}
std::vector<IdPaths> PchCreator::takeProjectsIncludes()
{
return std::move(m_projectsIncludeIds);
}
void PchCreator::setGenerator(PchGeneratorInterface *pchGenerator)
{
m_pchGenerator = pchGenerator;
}
std::unique_ptr<QFile> PchCreator::generatePchHeaderFile(
const Utils::SmallString &filePath,
const Utils::SmallString &content)

View File

@@ -27,6 +27,7 @@
#include "pchcreatorinterface.h"
#include "pchgeneratorinterface.h"
#include "stringcache.h"
#include "idpaths.h"
@@ -50,12 +51,14 @@ public:
StringCache<Utils::SmallString> &filePathCache);
PchCreator(V2::ProjectPartContainers &&projectsParts,
Environment &environment,
StringCache<Utils::SmallString> &filePathCache);
StringCache<Utils::SmallString> &filePathCache,
PchGeneratorInterface *pchGenerator);
void generatePchs(V2::ProjectPartContainers &&projectsParts) override;
std::vector<ProjectPartPch> takeProjectPartPchs() override;
std::vector<IdPaths> takeProjectsIncludes() override;
void setGenerator(PchGeneratorInterface *pchGenerator);
unitttest_public:
Utils::SmallStringVector generateGlobalHeaderPaths() const;
Utils::SmallStringVector generateGlobalSourcePaths() const;
@@ -70,7 +73,8 @@ unitttest_public:
Utils::SmallString generatePchIncludeFileContent(const std::vector<uint> &includeIds) const;
Utils::SmallString generateGlobalPchHeaderFileContent() const;
std::unique_ptr<QFile> generateGlobalPchHeaderFile();
void generatePch(const Utils::SmallStringVector &commandLineArguments);
void generatePch(Utils::SmallStringVector &&commandLineArguments,
ProjectPartPch &&projectPartPch);
void generateGlobalPch();
Utils::SmallString globalPchContent() const;
@@ -97,7 +101,7 @@ unitttest_public:
const V2::ProjectPartContainer &projectPart) const;
Utils::SmallStringVector generateProjectPartClangCompilerArguments(
const V2::ProjectPartContainer &projectPart) const;
std::pair<ProjectPartPch, IdPaths> generateProjectPartPch(
IdPaths generateProjectPartPch(
const V2::ProjectPartContainer &projectPart);
static std::unique_ptr<QFile> generatePchHeaderFile(
const Utils::SmallString &filePath,
@@ -108,7 +112,6 @@ unitttest_public:
private:
static QByteArray projectPartHash(const V2::ProjectPartContainer &projectPart);
QByteArray globalProjectHash() const;
static void checkIfProcessHasError(const QProcess &process);
private:
V2::ProjectPartContainers m_projectParts;
@@ -116,6 +119,7 @@ private:
std::vector<IdPaths> m_projectsIncludeIds;
Environment &m_environment;
StringCache<Utils::SmallString> &m_filePathCache;
PchGeneratorInterface *m_pchGenerator = nullptr;
};
} // namespace ClangBackEnd

View File

@@ -38,7 +38,6 @@ public:
virtual ~PchCreatorInterface();
virtual void generatePchs(V2::ProjectPartContainers &&projectsParts) = 0;
virtual std::vector<ProjectPartPch> takeProjectPartPchs() = 0;
virtual std::vector<IdPaths> takeProjectsIncludes() = 0;
};

View File

@@ -0,0 +1,165 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "environment.h"
#include "pchgeneratorinterface.h"
#include "pchgeneratornotifierinterface.h"
#include <projectpartpch.h>
#include <QProcess>
#include <queue>
namespace ClangBackEnd {
template <typename Process>
class PchGenerator final : public PchGeneratorInterface
{
public:
PchGenerator(Environment &environment,
PchGeneratorNotifierInterface *notifier=nullptr)
: m_environment(environment),
m_notifier(notifier)
{
}
~PchGenerator()
{
cleanupAllProcesses();
}
void startTask(Utils::SmallStringVector &&compilerArguments, ProjectPartPch &&projectPartPch) override
{
addTask(std::move(compilerArguments), std::move(projectPartPch));
}
void setNotifier(PchGeneratorNotifierInterface *notifier)
{
m_notifier = notifier;
}
unitttest_public:
Process *addTask(Utils::SmallStringVector &&compilerArguments, ProjectPartPch &&projectPartPch)
{
auto process = std::make_unique<Process>();
Process *processPointer = process.get();
process->setProcessChannelMode(QProcess::ForwardedChannels);
process->setArguments(compilerArguments);
process->setProgram(m_environment.clangCompilerPath());
connectProcess(processPointer, std::move(projectPartPch));
if (!deferProcess())
startProcess(std::move(process));
else
m_deferredProcesses.push(std::move(process));
return processPointer;
}
void connectProcess(Process *process, ProjectPartPch &&projectPartPch)
{
auto finishedCallback = [=,projectPartPch=std::move(projectPartPch)] (int exitCode, QProcess::ExitStatus exitStatus) {
deleteProcess(process);
activateNextDeferredProcess();
m_notifier->taskFinished(generateTaskFinishStatus(exitCode, exitStatus), projectPartPch);
};
QObject::connect(process,
static_cast<void (Process::*)(int, QProcess::ExitStatus)>(&Process::finished),
std::move(finishedCallback));
}
void startProcess(std::unique_ptr<Process> &&process)
{
process->start();
m_runningProcesses.push_back(std::move(process));
}
const std::vector<std::unique_ptr<Process>> &runningProcesses() const
{
return m_runningProcesses;
}
const std::queue<std::unique_ptr<Process>> &deferredProcesses() const
{
return m_deferredProcesses;
}
void deleteProcess(Process *process)
{
auto found = std::find_if(m_runningProcesses.begin(),
m_runningProcesses.end(),
[=] (const std::unique_ptr<Process> &entry) {
return entry.get() == process;
});
if (found != m_runningProcesses.end()) {
std::unique_ptr<Process> avoidDoubleDeletedProcess = std::move(*found);
m_runningProcesses.erase(found);
}
}
void cleanupAllProcesses()
{
std::vector<std::unique_ptr<Process>> runningProcesses = std::move(m_runningProcesses);
std::queue<std::unique_ptr<Process>> deferredProcesses = std::move(m_deferredProcesses);
}
static TaskFinishStatus generateTaskFinishStatus(int exitCode, QProcess::ExitStatus exitStatus)
{
if (exitCode != 0 || exitStatus != QProcess::NormalExit)
return TaskFinishStatus::Unsuccessfully;
else
return TaskFinishStatus::Successfully;
}
bool deferProcess() const
{
return m_environment.hardwareConcurrency() <= m_runningProcesses.size();
}
void activateNextDeferredProcess()
{
if (!m_deferredProcesses.empty()) {
std::unique_ptr<Process> process = std::move(m_deferredProcesses.front());
m_deferredProcesses.pop();
startProcess(std::move(process));
}
}
private:
std::vector<std::unique_ptr<Process>> m_runningProcesses;
std::queue<std::unique_ptr<Process>> m_deferredProcesses;
Environment &m_environment;
PchGeneratorNotifierInterface *m_notifier=nullptr;
};
} // namespace ClangBackEnd

View File

@@ -0,0 +1,31 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "pchgeneratorinterface.h"
ClangBackEnd::PchGeneratorInterface::~PchGeneratorInterface()
{
}

View File

@@ -0,0 +1,45 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <utils/smallstringvector.h>
namespace ClangBackEnd {
class ProjectPartPch;
class PchGeneratorInterface
{
public:
virtual ~PchGeneratorInterface();
virtual void startTask(Utils::SmallStringVector &&compilerArguments,
ProjectPartPch &&projectPartPch) = 0;
};
} // namespace ClangBackEnd

View File

@@ -0,0 +1,53 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "pchgeneratornotifierinterface.h"
#include <ostream>
namespace ClangBackEnd {
PchGeneratorNotifierInterface::~PchGeneratorNotifierInterface()
{
}
std::ostream &operator<<(std::ostream &out, TaskFinishStatus status)
{
enum class TaskFinishStatus
{
Successfully,
Unsuccessfully
};
if (status == ClangBackEnd::TaskFinishStatus::Successfully)
out << "Successfully";
else
out << "Unsuccessfully";
return out;
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,50 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <iosfwd>
namespace ClangBackEnd {
class ProjectPartPch;
enum class TaskFinishStatus
{
Successfully,
Unsuccessfully
};
class PchGeneratorNotifierInterface
{
public:
virtual ~PchGeneratorNotifierInterface();
virtual void taskFinished(TaskFinishStatus status, const ProjectPartPch &projectPartPch) = 0;
};
std::ostream &operator<<(std::ostream &out, TaskFinishStatus status);
} // namespace ClangBackEnd

View File

@@ -58,8 +58,6 @@ void PchManagerServer::updatePchProjectParts(UpdatePchProjectPartsMessage &&mess
{
m_pchCreator.generatePchs(m_projectParts.update(message.takeProjectsParts()));
client()->precompiledHeadersUpdated(PrecompiledHeadersUpdatedMessage(m_pchCreator.takeProjectPartPchs()));
m_fileSystemWatcher.updateIdPaths(m_pchCreator.takeProjectsIncludes());
}
@@ -74,9 +72,13 @@ void PchManagerServer::pathsWithIdsChanged(const Utils::SmallStringVector &ids)
{
m_pchCreator.generatePchs(m_projectParts.projects(ids));
client()->precompiledHeadersUpdated(PrecompiledHeadersUpdatedMessage(m_pchCreator.takeProjectPartPchs()));
m_fileSystemWatcher.updateIdPaths(m_pchCreator.takeProjectsIncludes());
}
void PchManagerServer::taskFinished(TaskFinishStatus status, const ProjectPartPch &projectPartPch)
{
if (status == TaskFinishStatus::Successfully)
client()->precompiledHeadersUpdated(PrecompiledHeadersUpdatedMessage({projectPartPch.clone()}));
}
} // namespace ClangBackEnd

View File

@@ -28,6 +28,7 @@
#include "clangpathwatcherinterface.h"
#include "clangpathwatchernotifier.h"
#include "pchcreatorinterface.h"
#include "pchgeneratornotifierinterface.h"
#include "pchmanagerserverinterface.h"
#include "projectpartsinterface.h"
#include "stringcache.h"
@@ -36,7 +37,9 @@ namespace ClangBackEnd {
class SourceRangesAndDiagnosticsForQueryMessage;
class PchManagerServer : public PchManagerServerInterface, public ClangPathWatcherNotifier
class PchManagerServer : public PchManagerServerInterface,
public ClangPathWatcherNotifier,
public PchGeneratorNotifierInterface
{
public:
PchManagerServer(StringCache<Utils::SmallString> &filePathCache,
@@ -50,6 +53,7 @@ public:
void removePchProjectParts(RemovePchProjectPartsMessage &&message) override;
void pathsWithIdsChanged(const Utils::SmallStringVector &ids) override;
void taskFinished(TaskFinishStatus status, const ProjectPartPch &projectPartPch) override;
private:
StringCache<Utils::SmallString> &m_filePathCache;

View File

@@ -0,0 +1,88 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "fakeprocess.h"
FakeProcess::FakeProcess()
{
}
FakeProcess::~FakeProcess()
{
if (m_isStarted && !m_isFinished)
emit finished(0, QProcess::NormalExit);
}
void FakeProcess::finishUnsuccessfully()
{
m_isFinished = true;
emit finished(1, QProcess::NormalExit);
}
void FakeProcess::finishByCrash()
{
m_isFinished = true;
emit finished(0, QProcess::CrashExit);
}
void FakeProcess::finish()
{
m_isFinished = true;
emit finished(0, QProcess::NormalExit);
}
void FakeProcess::setArguments(const QStringList &arguments)
{
m_arguments = arguments;
}
void FakeProcess::setProgram(const QString &program)
{
m_applicationPath = program;
}
void FakeProcess::setProcessChannelMode(QProcess::ProcessChannelMode)
{
}
void FakeProcess::start()
{
m_isStarted = true;
}
bool FakeProcess::isStarted() const
{
return m_isStarted;
}
const QStringList &FakeProcess::arguments() const
{
return m_arguments;
}
const QString &FakeProcess::applicationPath() const
{
return m_applicationPath;
}

View File

@@ -0,0 +1,62 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QProcess>
class FakeProcess : public QObject
{
Q_OBJECT
public:
FakeProcess();
~FakeProcess();
void finishUnsuccessfully();
void finishByCrash();
void finish();
void start();
void setArguments(const QStringList &arguments);
void setProgram(const QString &program);
void setProcessChannelMode(QProcess::ProcessChannelMode mode);
bool isStarted() const;
const QStringList &arguments() const;
const QString &applicationPath() const;
signals:
void finished(int exitCode, QProcess::ExitStatus exitStatus);
private:
QStringList m_arguments;
QString m_applicationPath;
bool m_isFinished = false;
bool m_isStarted = false;
};

View File

@@ -36,8 +36,6 @@ class MockPchCreator : public ClangBackEnd::PchCreatorInterface
public:
MOCK_METHOD1(generatePchs,
void(const std::vector<ClangBackEnd::V2::ProjectPartContainer> &projectParts));
MOCK_METHOD0(takeProjectPartPchs,
std::vector<ClangBackEnd::ProjectPartPch>());
MOCK_METHOD0(takeProjectsIncludes,
std::vector<ClangBackEnd::IdPaths>());

View File

@@ -0,0 +1,38 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "googletest.h"
#include <pchgeneratornotifierinterface.h>
class MockPchGeneratorNotifier : public ClangBackEnd::PchGeneratorNotifierInterface
{
public:
MOCK_METHOD2(taskFinished,
void (ClangBackEnd::TaskFinishStatus status,
const ClangBackEnd::ProjectPartPch &projectPartPch));
};

View File

@@ -25,9 +25,12 @@
#include "googletest.h"
#include "fakeprocess.h"
#include "mockpchgeneratornotifier.h"
#include "testenvironment.h"
#include <pchcreator.h>
#include <pchgenerator.h>
#include <stringcache.h>
#include <QFileInfo>
@@ -70,9 +73,12 @@ protected:
{header2Path.clone()},
{main2Path.clone()}};
TestEnvironment environment;
NiceMock<MockPchGeneratorNotifier> mockPchGeneratorNotifier;
ClangBackEnd::PchGenerator<FakeProcess> generator{environment, &mockPchGeneratorNotifier};
ClangBackEnd::PchCreator creator{{projectPart1.clone(),projectPart2.clone()},
environment,
filePathCache};
filePathCache,
&generator};
};
using PchCreatorSlowTest = PchCreator;
@@ -116,7 +122,7 @@ TEST_F(PchCreator, CreateGlobalCommandLine)
ASSERT_THAT(arguments, ElementsAre(environment.clangCompilerPath(), "-I", TESTDATA_DIR, "-Wno-pragma-once-outside-header", "-I", TESTDATA_DIR, "-x" , "c++-header", "-Wno-pragma-once-outside-header"));
}
TEST_F(PchCreator, CreateGlobalPchIncludes)
TEST_F(PchCreatorVerySlowTest, CreateGlobalPchIncludes)
{
auto includeIds = creator.generateGlobalPchIncludeIds();
@@ -167,13 +173,6 @@ TEST_F(PchCreator, CreateGlobalClangCompilerArguments)
Not(Contains(environment.clangCompilerPath()))));
}
TEST_F(PchCreatorVerySlowTest, CreateGlobalPch)
{
creator.generateGlobalPch();
ASSERT_TRUE(QFileInfo::exists(creator.generateGlobalPchFilePath()));
}
TEST_F(PchCreator, CreateProjectPartCommandLine)
{
auto commandLine = creator.generateProjectPartCommandLine(projectPart1);
@@ -258,23 +257,34 @@ TEST_F(PchCreatorVerySlowTest, CreatePartPchs)
{
creator.generateGlobalPch();
auto projectPartPchAndIdPath = creator.generateProjectPartPch(projectPart1);
auto includePaths = creator.generateProjectPartPch(projectPart1);
ASSERT_THAT(projectPartPchAndIdPath.first.id(), projectPart1.projectPartId());
ASSERT_THAT(projectPartPchAndIdPath.first.path(), creator.generateProjectPartPchFilePath(projectPart1));
ASSERT_THAT(projectPartPchAndIdPath.second.id, projectPart1.projectPartId());
ASSERT_THAT(projectPartPchAndIdPath.second.paths, UnorderedElementsAre(1, 2, 3));
ASSERT_THAT(includePaths.id, projectPart1.projectPartId());
ASSERT_THAT(includePaths.paths, UnorderedElementsAre(1, 2, 3));
}
TEST_F(PchCreatorVerySlowTest, IncludesForCreatePchsForProjectParts)
{
creator.generatePchs();
ASSERT_THAT(creator.takeProjectsIncludes(),
ElementsAre(Field(&IdPaths::id, "project1"),
Field(&IdPaths::id, "project2")));
}
TEST_F(PchCreatorVerySlowTest, ProjectPartPchsForCreatePchsForProjectParts)
{
creator.generatePchs();
ASSERT_THAT(creator.takeProjectPartPchs(),
ElementsAre(Property(&ProjectPartPch::id, "project1"),
EXPECT_CALL(mockPchGeneratorNotifier,
taskFinished(ClangBackEnd::TaskFinishStatus::Successfully,
Property(&ProjectPartPch::id, "project1")));
EXPECT_CALL(mockPchGeneratorNotifier,
taskFinished(ClangBackEnd::TaskFinishStatus::Successfully,
Property(&ProjectPartPch::id, "project2")));
creator.generatePchs();
}
TEST_F(PchCreatorVerySlowTest, IdPathsForCreatePchsForProjectParts)
{
creator.generatePchs();

View File

@@ -0,0 +1,224 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "googletest.h"
#include "fakeprocess.h"
#include "testenvironment.h"
#include "mockpchgeneratornotifier.h"
#include <pchgenerator.h>
namespace {
using testing::_;
using testing::Contains;
using testing::Eq;
using testing::NiceMock;
using testing::Not;
using testing::PrintToString;
using ClangBackEnd::TaskFinishStatus;
MATCHER_P(ContainsProcess, process,
std::string(negation ? "isn't" : "is")
+ " process " + PrintToString(process))
{
auto found = std::find_if(arg.begin(),
arg.end(),
[&] (const std::unique_ptr<FakeProcess> &processOwner) {
return processOwner.get() == process;
});
return found != arg.end();
}
class PchGenerator : public testing::Test
{
protected:
TestEnvironment environment;
NiceMock<MockPchGeneratorNotifier> mockNotifier;
ClangBackEnd::PchGenerator<FakeProcess> generator{environment, &mockNotifier};
Utils::SmallStringVector compilerArguments = {"-DXXXX", "-Ifoo"};
ClangBackEnd::ProjectPartPch projectPartPch{"projectPartId", "/path/to/pch"};
};
bool operator==(const std::unique_ptr<FakeProcess> &first, FakeProcess *const second)
{
return first.get() == second;
}
TEST_F(PchGenerator, ProcessFinished)
{
EXPECT_CALL(mockNotifier, taskFinished(TaskFinishStatus::Successfully, std::move(projectPartPch)));
generator.startTask(compilerArguments.clone(), projectPartPch.clone());
}
TEST_F(PchGenerator, ProcessFinishedForDeferredProcess)
{
auto process = generator.addTask(compilerArguments.clone(), projectPartPch.clone());
generator.startTask(compilerArguments.clone(), projectPartPch.clone());
EXPECT_CALL(mockNotifier, taskFinished(TaskFinishStatus::Successfully, std::move(projectPartPch)))
.Times(3);
generator.startTask(compilerArguments.clone(), projectPartPch.clone());
process->finish();
}
TEST_F(PchGenerator, ProcessSuccessfullyFinished)
{
EXPECT_CALL(mockNotifier, taskFinished(TaskFinishStatus::Unsuccessfully, std::move(projectPartPch)));
auto process = generator.addTask(compilerArguments.clone(), projectPartPch.clone());
process->finishUnsuccessfully();
}
TEST_F(PchGenerator, ProcessSuccessfullyFinishedByWrongExitCode)
{
EXPECT_CALL(mockNotifier, taskFinished(TaskFinishStatus::Unsuccessfully, std::move(projectPartPch)));
auto process = generator.addTask(compilerArguments.clone(), projectPartPch.clone());
process->finishUnsuccessfully();
}
TEST_F(PchGenerator, AddTaskAddsProcessToProcesses)
{
auto process = generator.addTask(compilerArguments.clone(), projectPartPch.clone());
ASSERT_THAT(generator.runningProcesses(), ContainsProcess(process));
}
TEST_F(PchGenerator, RemoveProcessAfterFinishingProcess)
{
auto process = generator.addTask(compilerArguments.clone(), projectPartPch.clone());
process->finish();
ASSERT_THAT(generator.runningProcesses(), Not(ContainsProcess(process)));
}
TEST_F(PchGenerator, ProcessSuccessfullyFinishedByCrash)
{
EXPECT_CALL(mockNotifier, taskFinished(TaskFinishStatus::Unsuccessfully, std::move(projectPartPch)));
auto process = generator.addTask(compilerArguments.clone(), projectPartPch.clone());
process->finishByCrash();
}
TEST_F(PchGenerator, CreateProcess)
{
auto process = generator.addTask(compilerArguments.clone(), projectPartPch.clone());
ASSERT_THAT(generator.runningProcesses(), ContainsProcess(process));
}
TEST_F(PchGenerator, DeleteProcess)
{
auto process = generator.addTask(compilerArguments.clone(), projectPartPch.clone());
generator.deleteProcess(process);
ASSERT_THAT(generator.runningProcesses(), Not(ContainsProcess(process)));
}
TEST_F(PchGenerator, StartProcessApplicationPath)
{
auto process = generator.addTask(compilerArguments.clone(), projectPartPch.clone());
ASSERT_THAT(process->applicationPath(), environment.clangCompilerPath());
}
TEST_F(PchGenerator, SetCompilerArguments)
{
auto process = generator.addTask(compilerArguments.clone(), projectPartPch.clone());
ASSERT_THAT(process->arguments(), compilerArguments);
}
TEST_F(PchGenerator, ProcessIsStartedAfterAddingTask)
{
auto process = generator.addTask(compilerArguments.clone(), projectPartPch.clone());
ASSERT_TRUE(process->isStarted());
}
TEST_F(PchGenerator, DeferProcess)
{
generator.addTask(compilerArguments.clone(), projectPartPch.clone());
generator.addTask(compilerArguments.clone(), projectPartPch.clone());
auto deferProcess = generator.deferProcess();
ASSERT_TRUE(deferProcess);
}
TEST_F(PchGenerator, ThirdTaskIsDeferred)
{
generator.addTask(compilerArguments.clone(), projectPartPch.clone());
generator.addTask(compilerArguments.clone(), projectPartPch.clone());
auto process = generator.addTask(compilerArguments.clone(), projectPartPch.clone());
ASSERT_THAT(process, generator.deferredProcesses().back().get());
}
TEST_F(PchGenerator, ThirdTaskIsNotRunning)
{
generator.addTask(compilerArguments.clone(), projectPartPch.clone());
generator.addTask(compilerArguments.clone(), projectPartPch.clone());
auto process = generator.addTask(compilerArguments.clone(), projectPartPch.clone());
ASSERT_THAT(generator.runningProcesses(), Not(ContainsProcess(process)));
}
TEST_F(PchGenerator, DoNotDeferProcess)
{
generator.addTask(compilerArguments.clone(), projectPartPch.clone());
auto deferProcess = generator.deferProcess();
ASSERT_FALSE(deferProcess);
}
TEST_F(PchGenerator, DoNotActivateIfNothingIsDeferred)
{
generator.activateNextDeferredProcess();
}
TEST_F(PchGenerator, AfterActivationProcessIsRunning)
{
generator.addTask(compilerArguments.clone(), projectPartPch.clone());
generator.addTask(compilerArguments.clone(), projectPartPch.clone());
auto process = generator.addTask(compilerArguments.clone(), projectPartPch.clone());
generator.activateNextDeferredProcess();
ASSERT_THAT(generator.runningProcesses(), ContainsProcess(process));
}
}

View File

@@ -47,6 +47,7 @@ using testing::IsEmpty;
using Utils::SmallString;
using ClangBackEnd::V2::ProjectPartContainer;
using ClangBackEnd::TaskFinishStatus;
class PchManagerServer : public ::testing::Test
{
@@ -76,18 +77,28 @@ protected:
{main2Path.clone()}};
std::vector<ClangBackEnd::V2::ProjectPartContainer> projectParts{projectPart1, projectPart2};
ClangBackEnd::UpdatePchProjectPartsMessage updatePchProjectPartsMessage{Utils::clone(projectParts)};
std::vector<ClangBackEnd::ProjectPartPch> projectPartPchs{{projectPart1.projectPartId().clone(), "/path1/to/pch"},
{projectPart2.projectPartId().clone(), "/path2/to/pch"}};
ClangBackEnd::PrecompiledHeadersUpdatedMessage precompiledHeaderUpdatedMessage{Utils::clone(projectPartPchs)};
ClangBackEnd::ProjectPartPch projectPartPch1{projectPart1.projectPartId().clone(), "/path1/to/pch"};
ClangBackEnd::ProjectPartPch projectPartPch2{projectPart2.projectPartId().clone(), "/path2/to/pch"};
std::vector<ClangBackEnd::ProjectPartPch> projectPartPchs{projectPartPch1, projectPartPch2};
ClangBackEnd::PrecompiledHeadersUpdatedMessage precompiledHeaderUpdatedMessage1{{projectPartPch1}};
ClangBackEnd::PrecompiledHeadersUpdatedMessage precompiledHeaderUpdatedMessage2{{projectPartPch2}};
ClangBackEnd::RemovePchProjectPartsMessage removePchProjectPartsMessage{{projectPart1.projectPartId().clone(),
projectPart2.projectPartId().clone()}};
};
TEST_F(PchManagerServer, CallPrecompiledHeadersUpdatedInClientForUpdate)
TEST_F(PchManagerServer, CallPrecompiledHeadersForSuccessfullyFinishedTask)
{
EXPECT_CALL(mockPchManagerClient, precompiledHeadersUpdated(precompiledHeaderUpdatedMessage));
EXPECT_CALL(mockPchManagerClient, precompiledHeadersUpdated(precompiledHeaderUpdatedMessage1));
server.updatePchProjectParts(updatePchProjectPartsMessage.clone());
server.taskFinished(TaskFinishStatus::Successfully, projectPartPch1);
}
TEST_F(PchManagerServer, DoNotCallPrecompiledHeadersForUnsuccessfullyFinishedTask)
{
EXPECT_CALL(mockPchManagerClient, precompiledHeadersUpdated(precompiledHeaderUpdatedMessage1))
.Times(0);
server.taskFinished(TaskFinishStatus::Unsuccessfully, projectPartPch1);
}
TEST_F(PchManagerServer, CallBuildInPchCreator)
@@ -150,15 +161,6 @@ TEST_F(PchManagerServer, CallGeneratePchsInPchCreatorForIncludeChange)
server.pathsWithIdsChanged({projectPartId1});
}
TEST_F(PchManagerServer, CallPrecompiledHeadersUpdatedInClientForIncludeChange)
{
server.updatePchProjectParts(updatePchProjectPartsMessage.clone());
EXPECT_CALL(mockPchManagerClient, precompiledHeadersUpdated(precompiledHeaderUpdatedMessage));
server.pathsWithIdsChanged({projectPartId1});
}
TEST_F(PchManagerServer, CallUpdateIdPathsInFileSystemWatcherForIncludeChange)
{
server.updatePchProjectParts(updatePchProjectPartsMessage.clone());
@@ -172,8 +174,6 @@ void PchManagerServer::SetUp()
{
server.setClient(&mockPchManagerClient);
ON_CALL(mockPchCreator, takeProjectPartPchs())
.WillByDefault(Return(projectPartPchs));
ON_CALL(mockProjectParts, update(projectParts))
.WillByDefault(Return(projectParts));
ON_CALL(mockProjectParts, projects(Utils::SmallStringVector{{projectPartId1}}))

View File

@@ -45,6 +45,11 @@ public:
return QString::fromUtf8(CLANG_COMPILER_PATH);
}
uint hardwareConcurrency() const
{
return 2;
}
private:
QTemporaryDir temporaryDirectory;
};

View File

@@ -49,7 +49,9 @@ SOURCES += \
projectparts-test.cpp \
stringcache-test.cpp \
changedfilepathcompressor-test.cpp \
faketimer.cpp
faketimer.cpp \
pchgenerator-test.cpp \
fakeprocess.cpp
!isEmpty(LIBCLANG_LIBS) {
SOURCES += \
@@ -148,7 +150,9 @@ HEADERS += \
mockprojectparts.h \
mockclangpathwatchernotifier.h \
mockchangedfilepathcompressor.h \
faketimer.h
faketimer.h \
mockpchgeneratornotifier.h \
fakeprocess.h
!isEmpty(LIBCLANG_LIBS) {
HEADERS += \