forked from qt-creator/qt-creator
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:
@@ -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();
|
||||
|
||||
// Delete issue parser:
|
||||
m_parser->flush();
|
||||
delete m_parser;
|
||||
m_parser = nullptr;
|
||||
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) {
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user