Commit 420e5428 authored by Tobias Hunger's avatar Tobias Hunger

CMake: Keep cmake state in a temporary directory till first build

This avoids creating lots of build directories as the user types
in something into the builddirectory line of the build settings.

Change-Id: Ib08a0f65e08bce104e4baf9e19fb01730d2f5f08
Reviewed-by: default avatarTobias Hunger <tobias.hunger@theqtcompany.com>
parent 76a050cb
......@@ -100,7 +100,7 @@ BuildDirManager::BuildDirManager(const CMakeBuildConfiguration *bc) :
m_reparseTimer.setSingleShot(true);
m_reparseTimer.setInterval(500);
connect(&m_reparseTimer, &QTimer::timeout, this, &BuildDirManager::forceReparse);
connect(&m_reparseTimer, &QTimer::timeout, this, &BuildDirManager::parse);
connect(m_watcher, &QFileSystemWatcher::fileChanged, this, [this]() {
if (!isParsing())
......@@ -111,6 +111,7 @@ BuildDirManager::BuildDirManager(const CMakeBuildConfiguration *bc) :
BuildDirManager::~BuildDirManager()
{
resetData();
delete m_tempDir;
}
const ProjectExplorer::Kit *BuildDirManager::kit() const
......@@ -123,6 +124,16 @@ const Utils::FileName BuildDirManager::buildDirectory() const
return m_buildConfiguration->buildDirectory();
}
const Utils::FileName BuildDirManager::workDirectory() const
{
const Utils::FileName bdir = buildDirectory();
if (bdir.exists())
return bdir;
if (m_tempDir)
return Utils::FileName::fromString(m_tempDir->path());
return bdir;
}
const Utils::FileName BuildDirManager::sourceDirectory() const
{
return m_buildConfiguration->target()->project()->projectDirectory();
......@@ -168,6 +179,21 @@ void BuildDirManager::resetData()
m_watcher->removePaths(watchedFiles);
}
bool BuildDirManager::persistCMakeState()
{
if (!m_tempDir)
return false;
QDir dir(buildDirectory().toString());
dir.mkpath(buildDirectory().toString());
delete m_tempDir;
m_tempDir = nullptr;
parse();
return true;
}
void BuildDirManager::parse()
{
CMakeTool *tool = CMakeKitInformation::cmakeTool(kit());
......@@ -177,7 +203,7 @@ void BuildDirManager::parse()
QTC_ASSERT(!generator.isEmpty(), return);
// Pop up a dialog asking the user to rerun cmake
QString cbpFile = CMakeManager::findCbpFile(QDir(buildDirectory().toString()));
QString cbpFile = CMakeManager::findCbpFile(QDir(workDirectory().toString()));
QFileInfo cbpFileFi(cbpFile);
if (!cbpFileFi.exists()) {
......@@ -234,22 +260,46 @@ CMakeConfig BuildDirManager::configuration() const
void BuildDirManager::stopProcess()
{
if (m_cmakeProcess) {
m_cmakeProcess->disconnect();
if (!m_cmakeProcess)
return;
if (m_cmakeProcess->state() == QProcess::Running) {
m_cmakeProcess->terminate();
if (!m_cmakeProcess->waitForFinished(500))
m_cmakeProcess->kill();
}
delete m_cmakeProcess;
m_cmakeProcess = nullptr;
m_cmakeProcess->disconnect();
if (m_cmakeProcess->state() == QProcess::Running) {
m_cmakeProcess->terminate();
if (!m_cmakeProcess->waitForFinished(500))
m_cmakeProcess->kill();
}
cleanUpProcess();
// Delete issue parser:
m_parser->flush();
delete m_parser;
m_parser = nullptr;
m_future->reportCanceled();
m_future->reportFinished();
delete m_future;
m_future = nullptr;
}
void BuildDirManager::cleanUpProcess()
{
if (!m_cmakeProcess)
return;
QTC_ASSERT(m_cmakeProcess->state() == QProcess::NotRunning, return);
m_cmakeProcess->disconnect();
if (m_cmakeProcess->state() == QProcess::Running) {
m_cmakeProcess->terminate();
if (!m_cmakeProcess->waitForFinished(500))
m_cmakeProcess->kill();
}
delete m_cmakeProcess;
m_cmakeProcess = nullptr;
// Delete issue parser:
m_parser->flush();
delete m_parser;
m_parser = nullptr;
}
void BuildDirManager::extractData()
......@@ -264,7 +314,7 @@ void BuildDirManager::extractData()
m_watchedFiles.insert(topCMake);
// Find cbp file
QString cbpFile = CMakeManager::findCbpFile(buildDirectory().toString());
QString cbpFile = CMakeManager::findCbpFile(workDirectory().toString());
if (cbpFile.isEmpty())
return;
......@@ -301,14 +351,20 @@ void BuildDirManager::startCMake(CMakeTool *tool, const QString &generator,
const CMakeConfig &config)
{
QTC_ASSERT(tool && tool->isValid(), return);
QTC_ASSERT(!m_cmakeProcess, return);
QTC_ASSERT(!m_parser, return);
QTC_ASSERT(!m_future, return);
// Make sure m_buildDir exists:
const QString buildDirStr = buildDirectory().toString();
QDir bDir = QDir(buildDirStr);
bDir.mkpath(buildDirStr);
// Find a directory to set up into:
if (!buildDirectory().exists()) {
if (!m_tempDir)
m_tempDir = new QTemporaryDir(QDir::tempPath() + QLatin1String("/qtc-cmake-XXXXXX"));
QTC_ASSERT(m_tempDir->isValid(), return);
}
// Make sure work directory exists:
QTC_ASSERT(workDirectory().exists(), return);
m_parser = new CMakeParser;
QDir source = QDir(sourceDirectory().toString());
......@@ -328,7 +384,7 @@ void BuildDirManager::startCMake(CMakeTool *tool, const QString &generator,
const QString srcDir = sourceDirectory().toString();
m_cmakeProcess = new Utils::QtcProcess(this);
m_cmakeProcess->setWorkingDirectory(buildDirStr);
m_cmakeProcess->setWorkingDirectory(workDirectory().toString());
m_cmakeProcess->setEnvironment(m_buildConfiguration->environment());
connect(m_cmakeProcess, &QProcess::readyReadStandardOutput,
......@@ -349,7 +405,7 @@ void BuildDirManager::startCMake(CMakeTool *tool, const QString &generator,
Core::MessageManager::write(tr("Running '%1 %2' in %3.")
.arg(tool->cmakeExecutable().toUserOutput())
.arg(args)
.arg(buildDirectory().toUserOutput()));
.arg(workDirectory().toUserOutput()));
m_future = new QFutureInterface<void>();
m_future->setProgressRange(0, 1);
......@@ -370,7 +426,7 @@ void BuildDirManager::cmakeFinished(int code, QProcess::ExitStatus status)
processCMakeOutput();
processCMakeError();
stopProcess();
cleanUpProcess();
extractData(); // try even if cmake failed...
......@@ -391,7 +447,7 @@ void BuildDirManager::cmakeFinished(int code, QProcess::ExitStatus status)
m_future->reportFinished();
delete m_future;
m_future = 0;
m_future = nullptr;
m_hasData = true;
emit dataAvailable();
......@@ -464,7 +520,7 @@ static CMakeConfigItem::Type fromByteArray(const QByteArray &type) {
CMakeConfig BuildDirManager::parseConfiguration() const
{
CMakeConfig result;
const QString cacheFile = QDir(buildDirectory().toString()).absoluteFilePath(QLatin1String("CMakeCache.txt"));
const QString cacheFile = QDir(workDirectory().toString()).absoluteFilePath(QLatin1String("CMakeCache.txt"));
QFile cache(cacheFile);
if (!cache.open(QIODevice::ReadOnly | QIODevice::Text))
return CMakeConfig();
......
......@@ -69,6 +69,7 @@ public:
const ProjectExplorer::Kit *kit() const;
const Utils::FileName buildDirectory() const;
const Utils::FileName workDirectory() const;
const Utils::FileName sourceDirectory() const;
const CMakeConfig cmakeConfiguration() const;
bool isParsing() const;
......@@ -76,6 +77,7 @@ public:
void parse();
void forceReparse();
void resetData();
bool persistCMakeState();
bool isProjectFile(const Utils::FileName &fileName) const;
QString projectName() const;
......@@ -91,6 +93,7 @@ signals:
private:
void stopProcess();
void cleanUpProcess();
void extractData();
void startCMake(CMakeTool *tool, const QString &generator, const CMakeConfig &config);
......@@ -105,6 +108,7 @@ private:
const CMakeBuildConfiguration *m_buildConfiguration = nullptr;
Utils::QtcProcess *m_cmakeProcess = nullptr;
QTemporaryDir *m_tempDir = nullptr;
QSet<Utils::FileName> m_watchedFiles;
QString m_projectName;
......
......@@ -92,7 +92,7 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(ProjectExplorer::Target *parent
connect(this, &CMakeBuildConfiguration::environmentChanged,
m_buildDirManager, &BuildDirManager::forceReparse);
connect(this, &CMakeBuildConfiguration::buildDirectoryChanged,
m_buildDirManager, &BuildDirManager::parse);
m_buildDirManager, &BuildDirManager::forceReparse);
connect(target(), &Target::kitChanged, m_buildDirManager, &BuildDirManager::forceReparse);
connect(this, &CMakeBuildConfiguration::parsingStarted, project, &CMakeProject::handleParsingStarted);
......@@ -182,6 +182,11 @@ void CMakeBuildConfiguration::resetData()
m_buildDirManager->resetData();
}
bool CMakeBuildConfiguration::persistCMakeState()
{
return m_buildDirManager->persistCMakeState();
}
QList<ConfigModel::DataItem> CMakeBuildConfiguration::completeCMakeConfiguration() const
{
if (m_buildDirManager->isParsing())
......
......@@ -75,6 +75,7 @@ public:
void parse();
void resetData();
bool persistCMakeState();
signals:
void errorOccured(const QString &message);
......
......@@ -222,6 +222,37 @@ bool CMakeBuildStep::init(QList<const BuildStep *> &earlierSteps)
return AbstractProcessStep::init(earlierSteps);
}
void CMakeBuildStep::run(QFutureInterface<bool> &fi)
{
// Make sure CMake state was written to disk before trying to build:
CMakeBuildConfiguration *bc = cmakeBuildConfiguration();
if (!bc)
bc = qobject_cast<CMakeBuildConfiguration *>(target()->activeBuildConfiguration());
QTC_ASSERT(bc, return);
if (bc->persistCMakeState()) {
emit addOutput(tr("Persisting CMake state..."), BuildStep::MessageOutput);
m_runTrigger = connect(bc, &CMakeBuildConfiguration::dataAvailable,
this, [this, &fi]() { runImpl(fi); });
m_errorTrigger = connect(bc, &CMakeBuildConfiguration::errorOccured,
this, [this, &fi]() {
fi.reportResult(false);
});
} else {
runImpl(fi);
}
}
void CMakeBuildStep::runImpl(QFutureInterface<bool> &fi)
{
// Do the actual build:
disconnect(m_runTrigger);
disconnect(m_errorTrigger);
AbstractProcessStep::run(fi);
}
BuildStepConfigWidget *CMakeBuildStep::createConfigWidget()
{
return new CMakeBuildStepConfigWidget(this);
......
......@@ -56,6 +56,7 @@ public:
CMakeBuildConfiguration *targetsActiveBuildConfiguration() const;
bool init(QList<const BuildStep *> &earlierSteps) override;
void run(QFutureInterface<bool> &fi) override;
ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override;
bool immutable() const override;
......@@ -100,9 +101,14 @@ protected:
private:
void ctor(ProjectExplorer::BuildStepList *bsl);
void runImpl(QFutureInterface<bool> &fi);
void handleBuildTargetChanges();
CMakeRunConfiguration *targetsActiveRunConfiguration() const;
QMetaObject::Connection m_runTrigger;
QMetaObject::Connection m_errorTrigger;
QRegExp m_percentProgress;
QRegExp m_ninjaProgress;
QString m_ninjaProgressString;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment