CMake/Source/QtDialog/QCMake.cxx
Clinton Stimpson 131eed6607 cmake-gui: Improve interrupt granularity to fix bug 12649.
Instead of enabling interrupt only when a progress or message callback is called, add a new callback specifically for
interrupt.  This new callback is called from GetFatalErrorOccured() so cmake-gui can immediately report interrupt status
instead of calling queuing a call to cmSystemTools::SetFatalErrorOccured() and waiting for the progress or message
callback to be called to process that queued call.
2012-01-02 11:07:43 -07:00

449 lines
12 KiB
C++

/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "QCMake.h"
#include <QDir>
#include <QCoreApplication>
#include "cmake.h"
#include "cmCacheManager.h"
#include "cmSystemTools.h"
#include "cmExternalMakefileProjectGenerator.h"
#ifdef Q_OS_WIN
#include "qt_windows.h" // For SetErrorMode
#endif
QCMake::QCMake(QObject* p)
: QObject(p)
{
this->SuppressDevWarnings = false;
this->WarnUninitializedMode = false;
this->WarnUnusedMode = false;
qRegisterMetaType<QCMakeProperty>();
qRegisterMetaType<QCMakePropertyList>();
QDir execDir(QCoreApplication::applicationDirPath());
#if defined(Q_OS_MAC)
if(execDir.exists("../bin/cmake"))
{
execDir.cd("../bin");
}
else
{
execDir.cd("../../../"); // path to cmake in build directory (need to fix for deployment)
}
#endif
QString cmakeCommand = QString("cmake")+cmSystemTools::GetExecutableExtension();
cmakeCommand = execDir.filePath(cmakeCommand);
cmSystemTools::DisableRunCommandOutput();
cmSystemTools::SetRunCommandHideConsole(true);
cmSystemTools::SetErrorCallback(QCMake::errorCallback, this);
cmSystemTools::FindExecutableDirectory(cmakeCommand.toAscii().data());
this->CMakeInstance = new cmake;
this->CMakeInstance->SetCMakeCommand(cmakeCommand.toAscii().data());
#if defined(Q_OS_MAC)
this->CMakeInstance->SetCMakeEditCommand("cmake-gui.app/Contents/MacOS/cmake-gui");
#else
this->CMakeInstance->SetCMakeEditCommand("cmake-gui");
#endif
this->CMakeInstance->SetProgressCallback(QCMake::progressCallback, this);
cmSystemTools::SetInterruptCallback(QCMake::interruptCallback, this);
std::vector<std::string> generators;
this->CMakeInstance->GetRegisteredGenerators(generators);
std::vector<std::string>::iterator iter;
for(iter = generators.begin(); iter != generators.end(); ++iter)
{
// Skip the generator "KDevelop3", since there is also
// "KDevelop3 - Unix Makefiles", which is the full and official name.
// The short name is actually only still there since this was the name
// in CMake 2.4, to keep "command line argument compatibility", but
// this is not necessary in the GUI.
if (*iter == "KDevelop3")
{
continue;
}
this->AvailableGenerators.append(iter->c_str());
}
}
QCMake::~QCMake()
{
delete this->CMakeInstance;
//cmDynamicLoader::FlushCache();
}
void QCMake::loadCache(const QString& dir)
{
this->setBinaryDirectory(dir);
}
void QCMake::setSourceDirectory(const QString& _dir)
{
QString dir =
cmSystemTools::GetActualCaseForPath(_dir.toAscii().data()).c_str();
if(this->SourceDirectory != dir)
{
this->SourceDirectory = QDir::fromNativeSeparators(dir);
emit this->sourceDirChanged(this->SourceDirectory);
}
}
void QCMake::setBinaryDirectory(const QString& _dir)
{
QString dir =
cmSystemTools::GetActualCaseForPath(_dir.toAscii().data()).c_str();
if(this->BinaryDirectory != dir)
{
this->BinaryDirectory = QDir::fromNativeSeparators(dir);
emit this->binaryDirChanged(this->BinaryDirectory);
cmCacheManager *cachem = this->CMakeInstance->GetCacheManager();
this->setGenerator(QString());
if(!this->CMakeInstance->GetCacheManager()->LoadCache(
this->BinaryDirectory.toLocal8Bit().data()))
{
QDir testDir(this->BinaryDirectory);
if(testDir.exists("CMakeCache.txt"))
{
cmSystemTools::Error("There is a CMakeCache.txt file for the current binary "
"tree but cmake does not have permission to read it. "
"Please check the permissions of the directory you are trying to run CMake on.");
}
}
QCMakePropertyList props = this->properties();
emit this->propertiesChanged(props);
cmCacheManager::CacheIterator itm = cachem->NewIterator();
if ( itm.Find("CMAKE_HOME_DIRECTORY"))
{
setSourceDirectory(itm.GetValue());
}
if ( itm.Find("CMAKE_GENERATOR"))
{
const char* extraGen = cachem->GetCacheValue("CMAKE_EXTRA_GENERATOR");
std::string curGen = cmExternalMakefileProjectGenerator::
CreateFullGeneratorName(itm.GetValue(), extraGen);
this->setGenerator(curGen.c_str());
}
}
}
void QCMake::setGenerator(const QString& gen)
{
if(this->Generator != gen)
{
this->Generator = gen;
emit this->generatorChanged(this->Generator);
}
}
void QCMake::configure()
{
#ifdef Q_OS_WIN
UINT lastErrorMode = SetErrorMode(0);
#endif
this->CMakeInstance->SetHomeDirectory(this->SourceDirectory.toAscii().data());
this->CMakeInstance->SetStartDirectory(this->SourceDirectory.toAscii().data());
this->CMakeInstance->SetHomeOutputDirectory(this->BinaryDirectory.toAscii().data());
this->CMakeInstance->SetStartOutputDirectory(this->BinaryDirectory.toAscii().data());
this->CMakeInstance->SetGlobalGenerator(
this->CMakeInstance->CreateGlobalGenerator(this->Generator.toAscii().data()));
this->CMakeInstance->LoadCache();
this->CMakeInstance->SetSuppressDevWarnings(this->SuppressDevWarnings);
this->CMakeInstance->SetWarnUninitialized(this->WarnUninitializedMode);
this->CMakeInstance->SetWarnUnused(this->WarnUnusedMode);
this->CMakeInstance->PreLoadCMakeFiles();
InterruptFlag = 0;
cmSystemTools::ResetErrorOccuredFlag();
int err = this->CMakeInstance->Configure();
#ifdef Q_OS_WIN
SetErrorMode(lastErrorMode);
#endif
emit this->propertiesChanged(this->properties());
emit this->configureDone(err);
}
void QCMake::generate()
{
#ifdef Q_OS_WIN
UINT lastErrorMode = SetErrorMode(0);
#endif
InterruptFlag = 0;
cmSystemTools::ResetErrorOccuredFlag();
int err = this->CMakeInstance->Generate();
#ifdef Q_OS_WIN
SetErrorMode(lastErrorMode);
#endif
emit this->generateDone(err);
}
void QCMake::setProperties(const QCMakePropertyList& newProps)
{
QCMakePropertyList props = newProps;
QStringList toremove;
// set the value of properties
cmCacheManager *cachem = this->CMakeInstance->GetCacheManager();
for(cmCacheManager::CacheIterator i = cachem->NewIterator();
!i.IsAtEnd(); i.Next())
{
if(i.GetType() == cmCacheManager::INTERNAL ||
i.GetType() == cmCacheManager::STATIC)
{
continue;
}
QCMakeProperty prop;
prop.Key = i.GetName();
int idx = props.indexOf(prop);
if(idx == -1)
{
toremove.append(i.GetName());
}
else
{
prop = props[idx];
if(prop.Value.type() == QVariant::Bool)
{
i.SetValue(prop.Value.toBool() ? "ON" : "OFF");
}
else
{
i.SetValue(prop.Value.toString().toAscii().data());
}
props.removeAt(idx);
}
}
// remove some properites
foreach(QString s, toremove)
{
this->CMakeInstance->UnwatchUnusedCli(s.toAscii().data());
cachem->RemoveCacheEntry(s.toAscii().data());
}
// add some new properites
foreach(QCMakeProperty s, props)
{
this->CMakeInstance->WatchUnusedCli(s.Key.toAscii().data());
if(s.Type == QCMakeProperty::BOOL)
{
this->CMakeInstance->AddCacheEntry(s.Key.toAscii().data(),
s.Value.toBool() ? "ON" : "OFF",
s.Help.toAscii().data(),
cmCacheManager::BOOL);
}
else if(s.Type == QCMakeProperty::STRING)
{
this->CMakeInstance->AddCacheEntry(s.Key.toAscii().data(),
s.Value.toString().toAscii().data(),
s.Help.toAscii().data(),
cmCacheManager::STRING);
}
else if(s.Type == QCMakeProperty::PATH)
{
this->CMakeInstance->AddCacheEntry(s.Key.toAscii().data(),
s.Value.toString().toAscii().data(),
s.Help.toAscii().data(),
cmCacheManager::PATH);
}
else if(s.Type == QCMakeProperty::FILEPATH)
{
this->CMakeInstance->AddCacheEntry(s.Key.toAscii().data(),
s.Value.toString().toAscii().data(),
s.Help.toAscii().data(),
cmCacheManager::FILEPATH);
}
}
cachem->SaveCache(this->BinaryDirectory.toAscii().data());
}
QCMakePropertyList QCMake::properties() const
{
QCMakePropertyList ret;
cmCacheManager *cachem = this->CMakeInstance->GetCacheManager();
for(cmCacheManager::CacheIterator i = cachem->NewIterator();
!i.IsAtEnd(); i.Next())
{
if(i.GetType() == cmCacheManager::INTERNAL ||
i.GetType() == cmCacheManager::STATIC ||
i.GetType() == cmCacheManager::UNINITIALIZED)
{
continue;
}
QCMakeProperty prop;
prop.Key = i.GetName();
prop.Help = i.GetProperty("HELPSTRING");
prop.Value = i.GetValue();
prop.Advanced = i.GetPropertyAsBool("ADVANCED");
if(i.GetType() == cmCacheManager::BOOL)
{
prop.Type = QCMakeProperty::BOOL;
prop.Value = cmSystemTools::IsOn(i.GetValue());
}
else if(i.GetType() == cmCacheManager::PATH)
{
prop.Type = QCMakeProperty::PATH;
}
else if(i.GetType() == cmCacheManager::FILEPATH)
{
prop.Type = QCMakeProperty::FILEPATH;
}
else if(i.GetType() == cmCacheManager::STRING)
{
prop.Type = QCMakeProperty::STRING;
if (i.PropertyExists("STRINGS"))
{
prop.Strings = QString(i.GetProperty("STRINGS")).split(";");
}
}
ret.append(prop);
}
return ret;
}
void QCMake::interrupt()
{
this->InterruptFlag.ref();
}
bool QCMake::interruptCallback(void* cd)
{
QCMake* self = reinterpret_cast<QCMake*>(cd);
return self->InterruptFlag;
}
void QCMake::progressCallback(const char* msg, float percent, void* cd)
{
QCMake* self = reinterpret_cast<QCMake*>(cd);
if(percent >= 0)
{
emit self->progressChanged(msg, percent);
}
else
{
emit self->outputMessage(msg);
}
QCoreApplication::processEvents();
}
void QCMake::errorCallback(const char* msg, const char* /*title*/,
bool& /*stop*/, void* cd)
{
QCMake* self = reinterpret_cast<QCMake*>(cd);
emit self->errorMessage(msg);
QCoreApplication::processEvents();
}
QString QCMake::binaryDirectory() const
{
return this->BinaryDirectory;
}
QString QCMake::sourceDirectory() const
{
return this->SourceDirectory;
}
QString QCMake::generator() const
{
return this->Generator;
}
QStringList QCMake::availableGenerators() const
{
return this->AvailableGenerators;
}
void QCMake::deleteCache()
{
// delete cache
this->CMakeInstance->GetCacheManager()->DeleteCache(this->BinaryDirectory.toAscii().data());
// reload to make our cache empty
this->CMakeInstance->GetCacheManager()->LoadCache(this->BinaryDirectory.toAscii().data());
// emit no generator and no properties
this->setGenerator(QString());
QCMakePropertyList props = this->properties();
emit this->propertiesChanged(props);
}
void QCMake::reloadCache()
{
// emit that the cache was cleaned out
QCMakePropertyList props;
emit this->propertiesChanged(props);
// reload
this->CMakeInstance->GetCacheManager()->LoadCache(this->BinaryDirectory.toAscii().data());
// emit new cache properties
props = this->properties();
emit this->propertiesChanged(props);
}
void QCMake::setDebugOutput(bool flag)
{
if(flag != this->CMakeInstance->GetDebugOutput())
{
this->CMakeInstance->SetDebugOutputOn(flag);
emit this->debugOutputChanged(flag);
}
}
bool QCMake::getDebugOutput() const
{
return this->CMakeInstance->GetDebugOutput();
}
void QCMake::setSuppressDevWarnings(bool value)
{
this->SuppressDevWarnings = value;
}
void QCMake::setWarnUninitializedMode(bool value)
{
this->WarnUninitializedMode = value;
}
void QCMake::setWarnUnusedMode(bool value)
{
this->WarnUnusedMode = value;
}