/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "QCMake.h" #include #include #include #include "cmExternalMakefileProjectGenerator.h" #include "cmGlobalGenerator.h" #include "cmState.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #ifdef Q_OS_WIN # include "qt_windows.h" // For SetErrorMode #endif QCMake::QCMake(QObject* p) : QObject(p) { this->WarnUninitializedMode = false; qRegisterMetaType(); qRegisterMetaType(); cmSystemTools::DisableRunCommandOutput(); cmSystemTools::SetRunCommandHideConsole(true); cmSystemTools::SetMessageCallback( [this](std::string const& msg, const char* title) { this->messageCallback(msg, title); }); cmSystemTools::SetStdoutCallback( [this](std::string const& msg) { this->stdoutCallback(msg); }); cmSystemTools::SetStderrCallback( [this](std::string const& msg) { this->stderrCallback(msg); }); this->CMakeInstance = cm::make_unique(cmake::RoleProject, cmState::Project); this->CMakeInstance->SetCMakeEditCommand( cmSystemTools::GetCMakeGUICommand()); this->CMakeInstance->SetProgressCallback( [this](const std::string& msg, float percent) { this->progressCallback(msg, percent); }); cmSystemTools::SetInterruptCallback( [this] { return this->interruptCallback(); }); std::vector generators; this->CMakeInstance->GetRegisteredGenerators( generators, /*includeNamesWithPlatform=*/false); for (cmake::GeneratorInfo const& gen : generators) { this->AvailableGenerators.push_back(gen); } } QCMake::~QCMake() = default; void QCMake::loadCache(const QString& dir) { this->setBinaryDirectory(dir); } void QCMake::setSourceDirectory(const QString& _dir) { QString dir = QString::fromLocal8Bit( cmSystemTools::GetActualCaseForPath(_dir.toLocal8Bit().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 = QString::fromLocal8Bit( cmSystemTools::GetActualCaseForPath(_dir.toLocal8Bit().data()).c_str()); if (this->BinaryDirectory != dir) { this->BinaryDirectory = QDir::fromNativeSeparators(dir); emit this->binaryDirChanged(this->BinaryDirectory); cmState* state = this->CMakeInstance->GetState(); this->setGenerator(QString()); this->setToolset(QString()); this->setPlatform(QString()); if (!this->CMakeInstance->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); cmProp homeDir = state->GetCacheEntryValue("CMAKE_HOME_DIRECTORY"); if (homeDir) { setSourceDirectory(QString::fromLocal8Bit(homeDir->c_str())); } cmProp gen = state->GetCacheEntryValue("CMAKE_GENERATOR"); if (gen) { const std::string* extraGen = state->GetInitializedCacheValue("CMAKE_EXTRA_GENERATOR"); std::string curGen = cmExternalMakefileProjectGenerator::CreateFullGeneratorName( *gen, extraGen ? *extraGen : ""); this->setGenerator(QString::fromLocal8Bit(curGen.c_str())); } cmProp platform = state->GetCacheEntryValue("CMAKE_GENERATOR_PLATFORM"); if (platform) { this->setPlatform(QString::fromLocal8Bit(platform->c_str())); } cmProp toolset = state->GetCacheEntryValue("CMAKE_GENERATOR_TOOLSET"); if (toolset) { this->setToolset(QString::fromLocal8Bit(toolset->c_str())); } checkOpenPossible(); } } void QCMake::setGenerator(const QString& gen) { if (this->Generator != gen) { this->Generator = gen; emit this->generatorChanged(this->Generator); } } void QCMake::setPlatform(const QString& platform) { if (this->Platform != platform) { this->Platform = platform; emit this->platformChanged(this->Platform); } } void QCMake::setToolset(const QString& toolset) { if (this->Toolset != toolset) { this->Toolset = toolset; emit this->toolsetChanged(this->Toolset); } } void QCMake::configure() { #ifdef Q_OS_WIN UINT lastErrorMode = SetErrorMode(0); #endif this->CMakeInstance->SetHomeDirectory( this->SourceDirectory.toLocal8Bit().data()); this->CMakeInstance->SetHomeOutputDirectory( this->BinaryDirectory.toLocal8Bit().data()); this->CMakeInstance->SetGlobalGenerator( this->CMakeInstance->CreateGlobalGenerator( this->Generator.toLocal8Bit().data())); this->CMakeInstance->SetGeneratorPlatform( this->Platform.toLocal8Bit().data()); this->CMakeInstance->SetGeneratorToolset(this->Toolset.toLocal8Bit().data()); this->CMakeInstance->LoadCache(); this->CMakeInstance->SetWarnUninitialized(this->WarnUninitializedMode); 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); checkOpenPossible(); } void QCMake::open() { #ifdef Q_OS_WIN UINT lastErrorMode = SetErrorMode(0); #endif InterruptFlag = 0; cmSystemTools::ResetErrorOccuredFlag(); auto successful = this->CMakeInstance->Open( this->BinaryDirectory.toLocal8Bit().data(), false); #ifdef Q_OS_WIN SetErrorMode(lastErrorMode); #endif emit this->openDone(successful); } void QCMake::setProperties(const QCMakePropertyList& newProps) { QCMakePropertyList props = newProps; QStringList toremove; // set the value of properties cmState* state = this->CMakeInstance->GetState(); std::vector cacheKeys = state->GetCacheEntryKeys(); for (std::string const& key : cacheKeys) { cmStateEnums::CacheEntryType t = state->GetCacheEntryType(key); if (t == cmStateEnums::INTERNAL || t == cmStateEnums::STATIC) { continue; } QCMakeProperty prop; prop.Key = QString::fromLocal8Bit(key.c_str()); int idx = props.indexOf(prop); if (idx == -1) { toremove.append(QString::fromLocal8Bit(key.c_str())); } else { prop = props[idx]; if (prop.Value.type() == QVariant::Bool) { state->SetCacheEntryValue(key, prop.Value.toBool() ? "ON" : "OFF"); } else { state->SetCacheEntryValue(key, prop.Value.toString().toLocal8Bit().data()); } props.removeAt(idx); } } // remove some properties foreach (QString const& s, toremove) { this->CMakeInstance->UnwatchUnusedCli(s.toLocal8Bit().data()); state->RemoveCacheEntry(s.toLocal8Bit().data()); } // add some new properties foreach (QCMakeProperty const& s, props) { this->CMakeInstance->WatchUnusedCli(s.Key.toLocal8Bit().data()); if (s.Type == QCMakeProperty::BOOL) { this->CMakeInstance->AddCacheEntry( s.Key.toLocal8Bit().data(), s.Value.toBool() ? "ON" : "OFF", s.Help.toLocal8Bit().data(), cmStateEnums::BOOL); } else if (s.Type == QCMakeProperty::STRING) { this->CMakeInstance->AddCacheEntry( s.Key.toLocal8Bit().data(), s.Value.toString().toLocal8Bit().data(), s.Help.toLocal8Bit().data(), cmStateEnums::STRING); } else if (s.Type == QCMakeProperty::PATH) { this->CMakeInstance->AddCacheEntry( s.Key.toLocal8Bit().data(), s.Value.toString().toLocal8Bit().data(), s.Help.toLocal8Bit().data(), cmStateEnums::PATH); } else if (s.Type == QCMakeProperty::FILEPATH) { this->CMakeInstance->AddCacheEntry( s.Key.toLocal8Bit().data(), s.Value.toString().toLocal8Bit().data(), s.Help.toLocal8Bit().data(), cmStateEnums::FILEPATH); } } this->CMakeInstance->SaveCache(this->BinaryDirectory.toLocal8Bit().data()); } QCMakePropertyList QCMake::properties() const { QCMakePropertyList ret; cmState* state = this->CMakeInstance->GetState(); std::vector cacheKeys = state->GetCacheEntryKeys(); for (std::string const& key : cacheKeys) { cmStateEnums::CacheEntryType t = state->GetCacheEntryType(key); if (t == cmStateEnums::INTERNAL || t == cmStateEnums::STATIC || t == cmStateEnums::UNINITIALIZED) { continue; } cmProp cachedValue = state->GetCacheEntryValue(key); QCMakeProperty prop; prop.Key = QString::fromLocal8Bit(key.c_str()); if (cmProp hs = state->GetCacheEntryProperty(key, "HELPSTRING")) { prop.Help = QString::fromLocal8Bit(hs->c_str()); } prop.Value = QString::fromLocal8Bit(cachedValue->c_str()); prop.Advanced = state->GetCacheEntryPropertyAsBool(key, "ADVANCED"); if (t == cmStateEnums::BOOL) { prop.Type = QCMakeProperty::BOOL; prop.Value = cmIsOn(*cachedValue); } else if (t == cmStateEnums::PATH) { prop.Type = QCMakeProperty::PATH; } else if (t == cmStateEnums::FILEPATH) { prop.Type = QCMakeProperty::FILEPATH; } else if (t == cmStateEnums::STRING) { prop.Type = QCMakeProperty::STRING; cmProp stringsProperty = state->GetCacheEntryProperty(key, "STRINGS"); if (stringsProperty) { prop.Strings = QString::fromLocal8Bit(stringsProperty->c_str()).split(";"); } } ret.append(prop); } return ret; } void QCMake::interrupt() { this->InterruptFlag.ref(); } bool QCMake::interruptCallback() { #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) return this->InterruptFlag.load(); #else return this->InterruptFlag.loadRelaxed(); #endif } void QCMake::progressCallback(const std::string& msg, float percent) { if (percent >= 0) { emit this->progressChanged(QString::fromStdString(msg), percent); } else { emit this->outputMessage(QString::fromStdString(msg)); } QCoreApplication::processEvents(); } void QCMake::messageCallback(std::string const& msg, const char* /*title*/) { emit this->errorMessage(QString::fromStdString(msg)); QCoreApplication::processEvents(); } void QCMake::stdoutCallback(std::string const& msg) { emit this->outputMessage(QString::fromStdString(msg)); QCoreApplication::processEvents(); } void QCMake::stderrCallback(std::string const& msg) { emit this->outputMessage(QString::fromStdString(msg)); QCoreApplication::processEvents(); } QString QCMake::binaryDirectory() const { return this->BinaryDirectory; } QString QCMake::sourceDirectory() const { return this->SourceDirectory; } QString QCMake::generator() const { return this->Generator; } std::vector const& QCMake::availableGenerators() const { return AvailableGenerators; } void QCMake::deleteCache() { // delete cache this->CMakeInstance->DeleteCache(this->BinaryDirectory.toLocal8Bit().data()); // reload to make our cache empty this->CMakeInstance->LoadCache(this->BinaryDirectory.toLocal8Bit().data()); // emit no generator and no properties this->setGenerator(QString()); this->setToolset(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->LoadCache(this->BinaryDirectory.toLocal8Bit().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(); } bool QCMake::getSuppressDevWarnings() { return this->CMakeInstance->GetSuppressDevWarnings(); } void QCMake::setSuppressDevWarnings(bool value) { this->CMakeInstance->SetSuppressDevWarnings(value); } bool QCMake::getSuppressDeprecatedWarnings() { return this->CMakeInstance->GetSuppressDeprecatedWarnings(); } void QCMake::setSuppressDeprecatedWarnings(bool value) { this->CMakeInstance->SetSuppressDeprecatedWarnings(value); } bool QCMake::getDevWarningsAsErrors() { return this->CMakeInstance->GetDevWarningsAsErrors(); } void QCMake::setDevWarningsAsErrors(bool value) { this->CMakeInstance->SetDevWarningsAsErrors(value); } bool QCMake::getDeprecatedWarningsAsErrors() { return this->CMakeInstance->GetDeprecatedWarningsAsErrors(); } void QCMake::setDeprecatedWarningsAsErrors(bool value) { this->CMakeInstance->SetDeprecatedWarningsAsErrors(value); } void QCMake::setWarnUninitializedMode(bool value) { this->WarnUninitializedMode = value; } void QCMake::checkOpenPossible() { std::string data = this->BinaryDirectory.toLocal8Bit().data(); auto possible = this->CMakeInstance->Open(data, true); emit openPossible(possible); }