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: Tobias Hunger <tobias.hunger@theqtcompany.com>
This commit is contained in:
Tobias Hunger
2016-02-25 14:18:05 +01:00
parent 76a050cb83
commit 420e54281c
6 changed files with 129 additions and 26 deletions

View File

@@ -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,7 +260,32 @@ CMakeConfig BuildDirManager::configuration() const
void BuildDirManager::stopProcess()
{
if (m_cmakeProcess) {
if (!m_cmakeProcess)
return;
m_cmakeProcess->disconnect();
if (m_cmakeProcess->state() == QProcess::Running) {
m_cmakeProcess->terminate();
if (!m_cmakeProcess->waitForFinished(500))
m_cmakeProcess->kill();
}
cleanUpProcess();
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) {
@@ -250,7 +301,6 @@ void BuildDirManager::stopProcess()
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();

View File

@@ -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;

View File

@@ -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())

View File

@@ -75,6 +75,7 @@ public:
void parse();
void resetData();
bool persistCMakeState();
signals:
void errorOccured(const QString &message);

View File

@@ -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);

View File

@@ -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;