diff options
author | Tobias Hunger <tobias.hunger@qt.io> | 2020-04-02 14:49:05 +0200 |
---|---|---|
committer | Tobias Hunger <tobias.hunger@qt.io> | 2020-06-09 16:34:00 +0000 |
commit | 01b0d4f8f561328628051f14776d056a4bc023b6 (patch) | |
tree | ad9ec87399761abae79f5bb7ece600eae6fa065c /src | |
parent | c02a0037d52ec72c52fb0f924af417a7f400633c (diff) |
CMake: Remove magic configuration from CMake
Get rid of magic configuration handling in the CMakeProjectManager.
* Use CMakeCache.txt as the sole source of truth, do not keep
a shadow copy of configuration in the .user file
* Have initial CMake arguments that are easy to edit in batch
(Fixes: QTCREATORBUG-18179) used whenever no CMakeCache.txt
file is in the build directory. These allow for any thing that
can be passed to CMake on the command line.
(Fixes: QTCREATORBUG-16296)
* Ask when changes to CMake configuration were not applied
(Fixes: QTCREATORBUG-18504)
* Run cmake with arguments effecting its configuration only when
the CMake settings are changed in the UI, run CMake without any
special arguments in all other cases.
* Get rid of the confusing dialog used to keep settings in sync between
what is in CMakeCache.txt and Creator (Fixes: QTCREATORBUG-23218)
Change-Id: I26d55be7df733f084f5691ecf7d7b4352f58b8e7
Reviewed-by: Cristian Adam <cristian.adam@qt.io>
Diffstat (limited to 'src')
18 files changed, 432 insertions, 504 deletions
diff --git a/src/plugins/cmakeprojectmanager/builddirparameters.cpp b/src/plugins/cmakeprojectmanager/builddirparameters.cpp index 8522029e679..82b4b91d22b 100644 --- a/src/plugins/cmakeprojectmanager/builddirparameters.cpp +++ b/src/plugins/cmakeprojectmanager/builddirparameters.cpp @@ -49,6 +49,17 @@ BuildDirParameters::BuildDirParameters(CMakeBuildConfiguration *bc) { QTC_ASSERT(bc, return ); + const Utils::MacroExpander *expander = bc->macroExpander(); + + initialCMakeArguments = Utils::transform(bc->initialCMakeArguments(), + [expander](const QString &s) { + return expander->expand(s); + }); + extraCMakeArguments = Utils::transform(bc->extraCMakeArguments(), + [expander](const QString &s) { + return expander->expand(s); + }); + const Target *t = bc->target(); const Kit *k = t->kit(); const Project *p = t->project(); @@ -70,23 +81,6 @@ BuildDirParameters::BuildDirParameters(CMakeBuildConfiguration *bc) environment.appendOrSetPath(settings->ninjaPath().toString()); cmakeToolId = CMakeKitAspect::cmakeToolId(k); - - auto tc = ToolChainKitAspect::cxxToolChain(k); - if (tc) - cxxToolChainId = tc->id(); - tc = ToolChainKitAspect::cToolChain(k); - if (tc) - cToolChainId = tc->id(); - - expander = k->macroExpander(); - - configuration = bc->configurationForCMake(); - - generator = CMakeGeneratorKitAspect::generator(k); - extraGenerator = CMakeGeneratorKitAspect::extraGenerator(k); - platform = CMakeGeneratorKitAspect::platform(k); - toolset = CMakeGeneratorKitAspect::toolset(k); - generatorArguments = CMakeGeneratorKitAspect::generatorArguments(k); } bool BuildDirParameters::isValid() const diff --git a/src/plugins/cmakeprojectmanager/builddirparameters.h b/src/plugins/cmakeprojectmanager/builddirparameters.h index 7318ef03d86..5074a62fba3 100644 --- a/src/plugins/cmakeprojectmanager/builddirparameters.h +++ b/src/plugins/cmakeprojectmanager/builddirparameters.h @@ -54,21 +54,13 @@ public: Utils::FilePath sourceDirectory; Utils::FilePath buildDirectory; Utils::FilePath workDirectory; // either buildDirectory or a QTemporaryDirectory! - Utils::Environment environment; - Core::Id cmakeToolId; - - QByteArray cxxToolChainId; - QByteArray cToolChainId; - Utils::MacroExpander *expander = nullptr; + Utils::Environment environment; - CMakeConfig configuration; + Core::Id cmakeToolId; - QString generator; - QString extraGenerator; - QString platform; - QString toolset; - QStringList generatorArguments; + QStringList initialCMakeArguments; + QStringList extraCMakeArguments; }; } // namespace Internal diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 0a28a902ff0..25ba5986e58 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -61,6 +61,41 @@ static Q_LOGGING_CATEGORY(cmakeBuildConfigurationLog, "qtc.cmake.bc", QtWarningM const char CONFIGURATION_KEY[] = "CMake.Configuration"; +// ----------------------------------------------------------------------------- +// Helper: +// ----------------------------------------------------------------------------- + +static QStringList defaultInitialCMakeArguments(const Kit *k, const QString buildType) +{ + // Generator: + QStringList initialArgs = CMakeGeneratorKitAspect::generatorArguments(k); + + // CMAKE_BUILD_TYPE: + if (!buildType.isEmpty()) + initialArgs.append(QString::fromLatin1("-DCMAKE_BUILD_TYPE:String=%1").arg(buildType)); + + // Cross-compilation settings: + const QString sysRoot = SysRootKitAspect::sysRoot(k).toString(); + if (!sysRoot.isEmpty()) { + initialArgs.append(QString::fromLatin1("-DCMAKE_SYSROOT:PATH=%1").arg(sysRoot)); + if (ToolChain *tc = ToolChainKitAspect::cxxToolChain(k)) { + const QString targetTriple = tc->originalTargetTriple(); + initialArgs.append( + QString::fromLatin1("-DCMAKE_C_COMPILER_TARGET:STRING=%1").arg(targetTriple)); + initialArgs.append( + QString::fromLatin1("-DCMAKE_CXX_COMPILER_TARGET:STRING=%1").arg(targetTriple)); + } + } + + initialArgs += CMakeConfigurationKitAspect::toArgumentsList(k); + + return initialArgs; +} + +// ----------------------------------------------------------------------------- +// CMakeBuildConfiguration: +// ----------------------------------------------------------------------------- + CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Core::Id id) : BuildConfiguration(target, id) { @@ -88,41 +123,31 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Core::Id id) return Utils::nullopt; }); + addAspect<InitialCMakeArgumentsAspect>(); + appendInitialBuildStep(Constants::CMAKE_BUILD_STEP_ID); appendInitialCleanStep(Constants::CMAKE_BUILD_STEP_ID); setInitializer([this, target](const BuildInfo &info) { - CMakeConfig config; - config.append({"CMAKE_BUILD_TYPE", info.typeName.toUtf8()}); - - Kit *k = target->kit(); - const QString sysRoot = SysRootKitAspect::sysRoot(k).toString(); - if (!sysRoot.isEmpty()) { - config.append(CMakeConfigItem("CMAKE_SYSROOT", sysRoot.toUtf8())); - if (ToolChain *tc = ToolChainKitAspect::cxxToolChain(k)) { - const QByteArray targetTriple = tc->originalTargetTriple().toUtf8(); - config.append(CMakeConfigItem("CMAKE_C_COMPILER_TARGET", targetTriple)); - config.append(CMakeConfigItem("CMAKE_CXX_COMPILER_TARGET ", targetTriple)); - } - } + const Kit *k = target->kit(); + + QStringList initialArgs = defaultInitialCMakeArguments(k, info.typeName); + // Android magic: if (DeviceTypeKitAspect::deviceTypeId(k) == Android::Constants::ANDROID_DEVICE_TYPE) { buildSteps()->appendStep(Android::Constants::ANDROID_BUILD_APK_ID); const auto &bs = buildSteps()->steps().constLast(); - m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_NATIVE_API_LEVEL", - CMakeProjectManager::CMakeConfigItem::Type::STRING, - "Android native API level", - bs->data(Android::Constants::AndroidNdkPlatform).toString().toUtf8()}); + initialArgs.append( + QString::fromLatin1("-DANDROID_NATIVE_API_LEVEL:STRING=%1") + .arg(bs->data(Android::Constants::AndroidNdkPlatform).toString())); auto ndkLocation = bs->data(Android::Constants::NdkLocation).value<FilePath>(); - m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_NDK", - CMakeProjectManager::CMakeConfigItem::Type::PATH, - "Android NDK PATH", - ndkLocation.toString().toUtf8()}); + initialArgs.append( + QString::fromLatin1("-DANDROID_NDK:PATH=%1").arg(ndkLocation.toString())); - m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"CMAKE_TOOLCHAIN_FILE", - CMakeProjectManager::CMakeConfigItem::Type::PATH, - "Android CMake toolchain file", - ndkLocation.pathAppended("build/cmake/android.toolchain.cmake").toString().toUtf8()}); + initialArgs.append( + QString::fromLatin1("-DCMAKE_TOOLCHAIN_FILE:PATH=%1") + .arg( + ndkLocation.pathAppended("build/cmake/android.toolchain.cmake").toString())); auto androidAbis = bs->data(Android::Constants::AndroidABIs).toStringList(); QString preferredAbi; @@ -133,29 +158,19 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Core::Id id) } else { preferredAbi = androidAbis.first(); } - // FIXME: configmodel doesn't care about our androidAbis list... - m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_ABI", - CMakeProjectManager::CMakeConfigItem::Type::STRING, - "Android ABI", - preferredAbi.toLatin1(), - androidAbis}); + initialArgs.append(QString::fromLatin1("-DANDROID_ABI:STRING=%1").arg(preferredAbi)); QtSupport::BaseQtVersion *qt = QtSupport::QtKitAspect::qtVersion(k); if (qt->qtVersion() >= QtSupport::QtVersionNumber{5, 14, 0}) { auto sdkLocation = bs->data(Android::Constants::SdkLocation).value<FilePath>(); - m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_SDK", - CMakeProjectManager::CMakeConfigItem::Type::PATH, - "Android SDK PATH", - sdkLocation.toString().toUtf8()}); - + initialArgs.append( + QString::fromLatin1("-DANDROID_SDK:PATH=%1").arg(sdkLocation.toString())); } - m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_STL", - CMakeProjectManager::CMakeConfigItem::Type::STRING, - "Android STL", - "c++_shared"}); + initialArgs.append(QString::fromLatin1("-DANDROID_STL:STRING=cxx_shared")); - m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"CMAKE_FIND_ROOT_PATH", "%{Qt:QT_INSTALL_PREFIX}"}); + initialArgs.append( + QString::fromLatin1("-DCMAKE_FIND_ROOT_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}")); } if (info.buildDirectory.isEmpty()) { @@ -165,13 +180,11 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Core::Id id) info.buildType)); } - setConfigurationForCMake(config); + setInitialCMakeArguments(initialArgs); }); const auto qmlDebuggingAspect = addAspect<QtSupport::QmlDebuggingAspect>(); qmlDebuggingAspect->setKit(target->kit()); - connect(qmlDebuggingAspect, &QtSupport::QmlDebuggingAspect::changed, - this, &CMakeBuildConfiguration::configurationForCMakeChanged); } CMakeBuildConfiguration::~CMakeBuildConfiguration() @@ -182,9 +195,6 @@ CMakeBuildConfiguration::~CMakeBuildConfiguration() QVariantMap CMakeBuildConfiguration::toMap() const { QVariantMap map(ProjectExplorer::BuildConfiguration::toMap()); - const QStringList config - = Utils::transform(m_configurationForCMake, [](const CMakeConfigItem &i) { return i.toString(); }); - map.insert(QLatin1String(CONFIGURATION_KEY), config); return map; } @@ -198,7 +208,29 @@ bool CMakeBuildConfiguration::fromMap(const QVariantMap &map) [](const QString &v) { return CMakeConfigItem::fromString(v); }), [](const CMakeConfigItem &c) { return !c.isNull(); }); - setConfigurationForCMake(conf); + // TODO: Upgrade from Qt Creator < 4.13: Remove when no longer supported! + const QString buildTypeName = [this]() { + switch (buildType()) { + case Debug: + return QString("Debug"); + case Profile: + return QString("RelWithDebInfo"); + case Release: + return QString("Release"); + case Unknown: + default: + return QString(""); + } + }(); + if (initialCMakeArguments().isEmpty()) { + QStringList initialArgs = defaultInitialCMakeArguments(target()->kit(), + buildTypeName) + + Utils::transform(conf, [this](const CMakeConfigItem &i) { + return i.toArgument(macroExpander()); + }); + + setInitialCMakeArguments(initialArgs); + } return true; } @@ -244,54 +276,34 @@ CMakeConfig CMakeBuildConfiguration::configurationFromCMake() const return m_configurationFromCMake; } -void CMakeBuildConfiguration::setConfigurationFromCMake(const CMakeConfig &config) +QStringList CMakeBuildConfiguration::extraCMakeArguments() const { - m_configurationFromCMake = config; + return m_extraCMakeArguments; } -void CMakeBuildConfiguration::setConfigurationForCMake(const QList<ConfigModel::DataItem> &items) +QStringList CMakeBuildConfiguration::initialCMakeArguments() const { - const CMakeConfig newConfig = Utils::transform(items, [](const ConfigModel::DataItem &i) { - CMakeConfigItem ni; - ni.key = i.key.toUtf8(); - ni.value = i.value.toUtf8(); - ni.documentation = i.description.toUtf8(); - ni.isAdvanced = i.isAdvanced; - ni.isUnset = i.isUnset; - ni.inCMakeCache = i.inCMakeCache; - ni.values = i.values; - switch (i.type) { - case CMakeProjectManager::ConfigModel::DataItem::BOOLEAN: - ni.type = CMakeConfigItem::BOOL; - break; - case CMakeProjectManager::ConfigModel::DataItem::FILE: - ni.type = CMakeConfigItem::FILEPATH; - break; - case CMakeProjectManager::ConfigModel::DataItem::DIRECTORY: - ni.type = CMakeConfigItem::PATH; - break; - case CMakeProjectManager::ConfigModel::DataItem::STRING: - ni.type = CMakeConfigItem::STRING; - break; - case CMakeProjectManager::ConfigModel::DataItem::UNKNOWN: - default: - ni.type = CMakeConfigItem::INTERNAL; - break; - } - return ni; - }); + return aspect<InitialCMakeArgumentsAspect>()->value().split('\n', Qt::SkipEmptyParts); +} - const CMakeConfig config = configurationForCMake() + newConfig; - setConfigurationForCMake(config); +void CMakeBuildConfiguration::setExtraCMakeArguments(const QStringList &args) +{ + if (m_extraCMakeArguments == args) + return; - if (Utils::indexOf(newConfig, [](const CMakeConfigItem &item){ - return item.key.startsWith("ANDROID_BUILD_ABI_"); - }) != -1) { - // We always need to clean when we change the ANDROID_BUILD_ABI_ variables - BuildManager::buildLists({cleanSteps()}); - } + qCDebug(cmakeBuildConfigurationLog) + << "Extra Args changed from" << m_extraCMakeArguments << "to" << args << "..."; + m_extraCMakeArguments = args; } +void CMakeBuildConfiguration::setConfigurationFromCMake(const CMakeConfig &config) +{ + m_configurationFromCMake = config; +} + +// FIXME: Run clean steps when a setting starting with "ANDROID_BUILD_ABI_" is changed. +// FIXME: Warn when kit settings are overridden by a project. + void CMakeBuildConfiguration::clearError(ForceEnabledChanged fec) { if (!m_error.isEmpty()) { @@ -320,37 +332,9 @@ static CMakeConfig removeDuplicates(const CMakeConfig &config) return result; } -void CMakeBuildConfiguration::setConfigurationForCMake(const CMakeConfig &config) +void CMakeBuildConfiguration::setInitialCMakeArguments(const QStringList &args) { - auto configs = removeDuplicates(config); - if (m_configurationForCMake.isEmpty()) - m_configurationForCMake = removeDuplicates(m_initialConfiguration + - CMakeConfigurationKitAspect::configuration(target()->kit()) + configs); - else - m_configurationForCMake = configs; - - const Kit *k = target()->kit(); - CMakeConfig kitConfig = CMakeConfigurationKitAspect::configuration(k); - bool hasKitOverride = false; - foreach (const CMakeConfigItem &i, m_configurationForCMake) { - const QString b = CMakeConfigItem::expandedValueOf(k, i.key, kitConfig); - if (!b.isNull() && (i.expandedValue(k) != b || i.isUnset)) { - hasKitOverride = true; - break; - } - } - - if (hasKitOverride) - setWarning(tr("CMake configuration set by the kit was overridden in the project.")); - else - setWarning(QString()); - - emit configurationForCMakeChanged(); -} - -CMakeConfig CMakeBuildConfiguration::configurationForCMake() const -{ - return removeDuplicates(CMakeConfigurationKitAspect::configuration(target()->kit()) + m_configurationForCMake); + aspect<InitialCMakeArgumentsAspect>()->setValue(args.join('\n')); } void CMakeBuildConfiguration::setError(const QString &message) @@ -491,19 +475,7 @@ BuildInfo CMakeBuildConfigurationFactory::createBuildInfo(BuildType buildType) ProjectExplorer::BuildConfiguration::BuildType CMakeBuildConfiguration::buildType() const { - QByteArray cmakeBuildTypeName; - QFile cmakeCache(buildDirectory().toString() + QLatin1String("/CMakeCache.txt")); - if (cmakeCache.open(QIODevice::ReadOnly)) { - while (!cmakeCache.atEnd()) { - QByteArray line = cmakeCache.readLine(); - if (line.startsWith("CMAKE_BUILD_TYPE")) { - if (int pos = line.indexOf('=')) - cmakeBuildTypeName = line.mid(pos + 1).trimmed(); - break; - } - } - cmakeCache.close(); - } + QByteArray cmakeBuildTypeName = CMakeConfigItem::valueOf("CMAKE_BUILD_TYPE", m_configurationFromCMake); // Cover all common CMake build types const CMakeBuildConfigurationFactory::BuildType cmakeBuildType @@ -516,5 +488,21 @@ BuildSystem *CMakeBuildConfiguration::buildSystem() const return m_buildSystem; } +void CMakeBuildConfiguration::runCMakeWithExtraArguments() +{ + m_buildSystem->runCMakeWithExtraArguments(); +} + +// ---------------------------------------------------------------------- +// - InitialCMakeParametersAspect: +// ---------------------------------------------------------------------- + +InitialCMakeArgumentsAspect::InitialCMakeArgumentsAspect() +{ + setSettingsKey("CMake.Initial.Parameters"); + setLabelText(tr("Initial CMake Parameters:")); + setDisplayStyle(TextEditDisplay); +} + } // namespace Internal } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h index c3d0f06254f..24a3ca1d43c 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h @@ -28,6 +28,7 @@ #include "cmakeconfigitem.h" #include "configmodel.h" +#include <projectexplorer/buildaspects.h> #include <projectexplorer/buildconfiguration.h> namespace CMakeProjectManager { @@ -47,9 +48,12 @@ class CMakeBuildConfiguration final : public ProjectExplorer::BuildConfiguration ~CMakeBuildConfiguration() final; public: - CMakeConfig configurationForCMake() const; CMakeConfig configurationFromCMake() const; + QStringList extraCMakeArguments() const; + + QStringList initialCMakeArguments() const; + QString error() const; QString warning() const; @@ -61,12 +65,12 @@ public: void buildTarget(const QString &buildTarget); ProjectExplorer::BuildSystem *buildSystem() const final; + void runCMakeWithExtraArguments(); + signals: void errorOccurred(const QString &message); void warningOccurred(const QString &message); - void configurationForCMakeChanged(); - private: QVariantMap toMap() const override; BuildType buildType() const override; @@ -79,13 +83,13 @@ private: void clearError(ForceEnabledChanged fec = ForceEnabledChanged::False); void setConfigurationFromCMake(const CMakeConfig &config); - void setConfigurationForCMake(const QList<ConfigModel::DataItem> &items); - void setConfigurationForCMake(const CMakeConfig &config); + + void setExtraCMakeArguments(const QStringList &args); + void setInitialCMakeArguments(const QStringList &args); void setError(const QString &message); void setWarning(const QString &message); - CMakeConfig m_configurationForCMake; CMakeConfig m_initialConfiguration; QString m_error; QString m_warning; @@ -93,6 +97,8 @@ private: CMakeConfig m_configurationFromCMake; CMakeBuildSystem *m_buildSystem = nullptr; + QStringList m_extraCMakeArguments; + friend class CMakeBuildSettingsWidget; friend class CMakeBuildSystem; friend class CMakeProject; @@ -120,5 +126,13 @@ private: friend class CMakeProjectImporter; }; +class InitialCMakeArgumentsAspect final : public ProjectExplorer::BaseStringAspect +{ + Q_OBJECT + +public: + InitialCMakeArgumentsAspect(); +}; + } // namespace Internal } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp index c1b4d038c8d..a564bb12529 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp @@ -27,6 +27,7 @@ #include "cmakebuildconfiguration.h" #include "cmakebuildsystem.h" +#include "cmakeconfigitem.h" #include "cmakekitinformation.h" #include "configmodel.h" #include "configmodelitemdelegate.h" @@ -37,6 +38,7 @@ #include <projectexplorer/target.h> #include <qtsupport/qtbuildaspects.h> +#include <utils/algorithm.h> #include <utils/categorysortfiltermodel.h> #include <utils/detailswidget.h> #include <utils/headerviewstretcher.h> @@ -101,10 +103,13 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc) connect(buildDirAspect, &ProjectConfigurationAspect::changed, this, [this]() { m_configModel->flush(); // clear out config cache...; }); - auto buildDirWidget = new QWidget; - LayoutBuilder buildDirWidgetBuilder(buildDirWidget); - buildDirAspect->addToLayout(buildDirWidgetBuilder); - mainLayout->addWidget(buildDirWidget, row, 0, 1, 2); + auto initialCMakeAspect = bc->aspect<InitialCMakeArgumentsAspect>(); + auto aspectWidget = new QWidget; + LayoutBuilder aspectWidgetBuilder(aspectWidget); + buildDirAspect->addToLayout(aspectWidgetBuilder); + aspectWidgetBuilder.startNewRow(); + initialCMakeAspect->addToLayout(aspectWidgetBuilder); + mainLayout->addWidget(aspectWidget, row, 0, 1, 2); ++row; auto qmlDebugAspect = bc->aspect<QtSupport::QmlDebuggingAspect>(); @@ -270,9 +275,10 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc) m_configTextFilterModel, &QSortFilterProxyModel::setFilterFixedString); connect(m_resetButton, &QPushButton::clicked, m_configModel, &ConfigModel::resetAllChanges); - connect(m_reconfigureButton, &QPushButton::clicked, this, [this]() { - m_buildConfiguration->setConfigurationForCMake(m_configModel->configurationForCMake()); - }); + connect(m_reconfigureButton, + &QPushButton::clicked, + m_buildConfiguration, + &CMakeBuildConfiguration::runCMakeWithExtraArguments); connect(m_unsetButton, &QPushButton::clicked, this, [this]() { m_configModel->toggleUnsetFlag(mapToSource(m_configView, m_configView->currentIndex())); }); @@ -312,10 +318,7 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc) connect(m_buildConfiguration, &CMakeBuildConfiguration::enabledChanged, this, [this]() { setError(m_buildConfiguration->disabledReason()); - setConfigurationForCMake(); }); - connect(m_buildConfiguration, &CMakeBuildConfiguration::configurationForCMakeChanged, - this, [this]() { setConfigurationForCMake(); }); updateSelection(QModelIndex(), QModelIndex()); } @@ -338,6 +341,42 @@ void CMakeBuildSettingsWidget::updateButtonState() const bool hasChanges = m_configModel->hasChanges(); m_resetButton->setEnabled(hasChanges && !isParsing); m_reconfigureButton->setEnabled((hasChanges || m_configModel->hasCMakeChanges()) && !isParsing); + + // Update extra data in buildconfiguration + const QList<ConfigModel::DataItem> changes = m_configModel->configurationForCMake(); + + const CMakeConfig configChanges = Utils::transform(changes, [](const ConfigModel::DataItem &i) { + CMakeConfigItem ni; + ni.key = i.key.toUtf8(); + ni.value = i.value.toUtf8(); + ni.documentation = i.description.toUtf8(); + ni.isAdvanced = i.isAdvanced; + ni.isUnset = i.isUnset; + ni.inCMakeCache = i.inCMakeCache; + ni.values = i.values; + switch (i.type) { + case CMakeProjectManager::ConfigModel::DataItem::BOOLEAN: + ni.type = CMakeConfigItem::BOOL; + break; + case CMakeProjectManager::ConfigModel::DataItem::FILE: + ni.type = CMakeConfigItem::FILEPATH; + break; + case CMakeProjectManager::ConfigModel::DataItem::DIRECTORY: + ni.type = CMakeConfigItem::PATH; + break; + case CMakeProjectManager::ConfigModel::DataItem::STRING: + ni.type = CMakeConfigItem::STRING; + break; + case CMakeProjectManager::ConfigModel::DataItem::UNKNOWN: + default: + ni.type = CMakeConfigItem::INTERNAL; + break; + } + return ni; + }); + + m_buildConfiguration->setExtraCMakeArguments( + Utils::transform(configChanges, [](const CMakeConfigItem &i) { return i.toArgument(); })); } void CMakeBuildSettingsWidget::updateAdvancedCheckBox() @@ -371,49 +410,39 @@ void CMakeBuildSettingsWidget::handleQmlDebugCxxFlags() const auto aspect = m_buildConfiguration->aspect<QtSupport::QmlDebuggingAspect>(); const bool enable = aspect->setting() == TriState::Enabled; - CMakeConfig changedConfig = m_buildConfiguration->configurationForCMake(); const CMakeConfig configList = m_buildConfiguration->configurationFromCMake(); const QByteArrayList cxxFlags{"CMAKE_CXX_FLAGS", "CMAKE_CXX_FLAGS_DEBUG", "CMAKE_CXX_FLAGS_RELWITHDEBINFO"}; const QByteArray qmlDebug("-DQT_QML_DEBUG"); - for (CMakeConfigItem item : configList) { - CMakeConfigItem it(item); + CMakeConfig changedConfig; + + for (const CMakeConfigItem &item : configList) { + if (!cxxFlags.contains(item.key)) + continue; - if (cxxFlags.contains(it.key)) { - if (enable) { - if (!it.value.contains(qmlDebug)) { - it.value = it.value.append(' ').append(qmlDebug); - changed = true; - } - } else { - int index = it.value.indexOf(qmlDebug); - if (index != -1) { - it.value.remove(index, qmlDebug.length()); - changed = true; - } + CMakeConfigItem it(item); + if (enable) { + if (!it.value.contains(qmlDebug)) { + it.value = it.value.append(' ').append(qmlDebug); + changed = true; + } + } else { + int index = it.value.indexOf(qmlDebug); + if (index != -1) { + it.value.remove(index, qmlDebug.length()); + changed = true; } - it.value = it.value.trimmed(); - changedConfig.append(it); } + it.value = it.value.trimmed(); + changedConfig.append(it); } - if (!changed) - return; - - m_buildConfiguration->setConfigurationForCMake(changedConfig); -} - -void CMakeBuildSettingsWidget::setConfigurationForCMake() -{ - QHash<QString, QString> config; - const CMakeConfig configList = m_buildConfiguration->configurationForCMake(); - for (const CMakeConfigItem &i : configList) { - config.insert(QString::fromUtf8(i.key), - CMakeConfigItem::expandedValueOf(m_buildConfiguration->target()->kit(), - i.key, configList)); + if (changed) { + m_buildConfiguration->setExtraCMakeArguments( + Utils::transform(changedConfig, + [](const CMakeConfigItem &i) { return i.toArgument(); })); } - m_configModel->setConfigurationForCMake(config); } void CMakeBuildSettingsWidget::updateSelection(const QModelIndex ¤t, const QModelIndex &previous) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.h b/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.h index 1014d2f6747..49f945230b9 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.h @@ -68,7 +68,6 @@ private: void updateFromKit(); void handleQmlDebugCxxFlags(); - void setConfigurationForCMake(); void updateSelection(const QModelIndex ¤t, const QModelIndex &previous); QAction *createForceAction(int type, const QModelIndex &idx); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index 48ef90f682a..4501ee7e3af 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -224,6 +224,7 @@ void CMakeBuildSystem::triggerParsing() // is selected while this here is already running. // Stop old parse run and keep that ParseGuard! + qCDebug(cmakeBuildSystemLog) << "Stopping current parsing run!"; stopParsingAndClearState(); } else { // Use new ParseGuard @@ -233,10 +234,15 @@ void CMakeBuildSystem::triggerParsing() qCDebug(cmakeBuildSystemLog) << "ParseGuard acquired."; - if (m_allFiles.isEmpty()) + if (m_allFiles.isEmpty()) { + qCDebug(cmakeBuildSystemLog) + << "No treescanner information available, forcing treescanner run."; updateReparseParameters(REPARSE_SCAN); + } + + int reparseParameters = takeReparseParameters(); - m_waitingForScan = (m_reparseParameters | REPARSE_SCAN) != 0; + m_waitingForScan = (reparseParameters & REPARSE_SCAN) != 0; m_waitingForParse = true; m_combinedScanAndParseResult = true; @@ -254,30 +260,31 @@ void CMakeBuildSystem::triggerParsing() TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); - int reparseParameters = takeReparseParameters(); - qCDebug(cmakeBuildSystemLog) << "Parse called with flags:" << reparseParametersString(reparseParameters); const QString cache = m_parameters.workDirectory.pathAppended("CMakeCache.txt").toString(); if (!QFileInfo::exists(cache)) { - reparseParameters |= REPARSE_FORCE_CONFIGURATION | REPARSE_FORCE_CMAKE_RUN; + reparseParameters |= REPARSE_FORCE_INITIAL_CONFIGURATION | REPARSE_FORCE_CMAKE_RUN; qCDebug(cmakeBuildSystemLog) << "No" << cache << "file found, new flags:" << reparseParametersString(reparseParameters); - } else if (reparseParameters & REPARSE_CHECK_CONFIGURATION) { - if (checkConfiguration()) { - reparseParameters |= REPARSE_FORCE_CONFIGURATION | REPARSE_FORCE_CMAKE_RUN; - qCDebug(cmakeBuildSystemLog) << "Config check triggered flags change:" - << reparseParametersString(reparseParameters); - } } - writeConfigurationIntoBuildDirectory(m_parameters.expander); + if ((0 == (reparseParameters & REPARSE_FORCE_EXTRA_CONFIGURATION)) + && !m_parameters.extraCMakeArguments.isEmpty()) { + if (mustApplyExtraArguments()) + reparseParameters |= REPARSE_FORCE_CMAKE_RUN | REPARSE_FORCE_EXTRA_CONFIGURATION; + } + + // Do not add extra args when doing initial configuration + if (0 != (reparseParameters & REPARSE_FORCE_INITIAL_CONFIGURATION)) + reparseParameters = reparseParameters ^ REPARSE_FORCE_EXTRA_CONFIGURATION; qCDebug(cmakeBuildSystemLog) << "Asking reader to parse"; m_reader.parse(reparseParameters & REPARSE_FORCE_CMAKE_RUN, - reparseParameters & REPARSE_FORCE_CONFIGURATION); + reparseParameters & REPARSE_FORCE_INITIAL_CONFIGURATION, + reparseParameters & REPARSE_FORCE_EXTRA_CONFIGURATION); } bool CMakeBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const @@ -350,10 +357,8 @@ QString CMakeBuildSystem::reparseParametersString(int reparseFlags) result += " URGENT"; if (reparseFlags & REPARSE_FORCE_CMAKE_RUN) result += " FORCE_CMAKE_RUN"; - if (reparseFlags & REPARSE_FORCE_CONFIGURATION) + if (reparseFlags & REPARSE_FORCE_INITIAL_CONFIGURATION) result += " FORCE_CONFIG"; - if (reparseFlags & REPARSE_CHECK_CONFIGURATION) - result += " CHECK_CONFIG"; if (reparseFlags & REPARSE_SCAN) result += " SCAN"; } @@ -363,7 +368,8 @@ QString CMakeBuildSystem::reparseParametersString(int reparseFlags) void CMakeBuildSystem::setParametersAndRequestParse(const BuildDirParameters ¶meters, const int reparseParameters) { - qCDebug(cmakeBuildSystemLog) << "setting parameters and requesting reparse"; + qCDebug(cmakeBuildSystemLog) << "setting parameters and requesting reparse" + << reparseParametersString(reparseParameters); if (!parameters.cmakeTool()) { TaskHub::addTask( BuildSystemTask(Task::Error, @@ -387,42 +393,42 @@ void CMakeBuildSystem::setParametersAndRequestParse(const BuildDirParameters &pa } } -void CMakeBuildSystem::writeConfigurationIntoBuildDirectory(const Utils::MacroExpander *expander) +bool CMakeBuildSystem::mustApplyExtraArguments() const { - QTC_ASSERT(expander, return ); - - const FilePath buildDir = workDirectory(m_parameters); - QTC_ASSERT(buildDir.exists(), return ); - - const FilePath settingsFile = buildDir.pathAppended("qtcsettings.cmake"); - - QByteArray contents; - contents.append("# This file is managed by Qt Creator, do not edit!\n\n"); - contents.append( - transform(m_parameters.configuration, - [expander](const CMakeConfigItem &item) { return item.toCMakeSetLine(expander); }) - .join('\n') - .toUtf8()); + if (m_parameters.extraCMakeArguments.isEmpty()) + return false; - QFile file(settingsFile.toString()); - QTC_ASSERT(file.open(QFile::WriteOnly | QFile::Truncate), return ); - file.write(contents); + auto answer = QMessageBox::question(Core::ICore::mainWindow(), + tr("Apply configuration changes?"), + tr("Run CMake with \"%1\"?") + .arg(m_parameters.extraCMakeArguments.join(" ")), + QMessageBox::Apply | QMessageBox::Discard, + QMessageBox::Apply); + return answer == QMessageBox::Apply; } void CMakeBuildSystem::runCMake() { BuildDirParameters parameters(cmakeBuildConfiguration()); qCDebug(cmakeBuildSystemLog) << "Requesting parse due \"Run CMake\" command"; - setParametersAndRequestParse(parameters, - REPARSE_CHECK_CONFIGURATION | REPARSE_FORCE_CMAKE_RUN - | REPARSE_URGENT); + setParametersAndRequestParse(parameters, REPARSE_FORCE_CMAKE_RUN | REPARSE_URGENT); } void CMakeBuildSystem::runCMakeAndScanProjectTree() { BuildDirParameters parameters(cmakeBuildConfiguration()); qCDebug(cmakeBuildSystemLog) << "Requesting parse due to \"Rescan Project\" command"; - setParametersAndRequestParse(parameters, REPARSE_CHECK_CONFIGURATION | REPARSE_SCAN); + setParametersAndRequestParse(parameters, + REPARSE_FORCE_CMAKE_RUN | REPARSE_SCAN | REPARSE_URGENT); +} + +void CMakeBuildSystem::runCMakeWithExtraArguments() +{ + BuildDirParameters parameters(cmakeBuildConfiguration()); + qCDebug(cmakeBuildSystemLog) << "Requesting parse due to \"Rescan Project\" command"; + setParametersAndRequestParse(parameters, + REPARSE_FORCE_CMAKE_RUN | REPARSE_FORCE_EXTRA_CONFIGURATION + | REPARSE_URGENT); } void CMakeBuildSystem::buildCMakeTarget(const QString &buildTarget) @@ -445,20 +451,39 @@ void CMakeBuildSystem::handleTreeScanningFinished() bool CMakeBuildSystem::persistCMakeState() { - QTC_ASSERT(m_parameters.isValid(), return false); + BuildDirParameters parameters(cmakeBuildConfiguration()); + QTC_ASSERT(parameters.isValid(), return false); + + parameters.workDirectory = workDirectory(parameters); + + int reparseFlags = REPARSE_DEFAULT; + qCDebug(cmakeBuildSystemLog) << "Checking whether build system needs to be persisted:" + << "workdir:" << parameters.workDirectory + << "buildDir:" << parameters.buildDirectory + << "Has extraargs:" << !parameters.extraCMakeArguments.isEmpty() + << "must apply extra Args:" + << mustApplyExtraArguments(); + + if (parameters.workDirectory == parameters.buildDirectory + && !parameters.extraCMakeArguments.isEmpty() && mustApplyExtraArguments()) { + reparseFlags = REPARSE_FORCE_EXTRA_CONFIGURATION; + qCDebug(cmakeBuildSystemLog) << " -> must run CMake with extra arguments."; + } + if (parameters.workDirectory != parameters.buildDirectory + && buildConfiguration()->createBuildDirectory()) { + reparseFlags = REPARSE_FORCE_INITIAL_CONFIGURATION; + qCDebug(cmakeBuildSystemLog) << " -> must run CMake with initial arguments."; + } - if (m_parameters.workDirectory == m_parameters.buildDirectory) + if (reparseFlags == REPARSE_DEFAULT) return false; - if (!buildConfiguration()->createBuildDirectory()) - return false; + if (reparseFlags == REPARSE_FORCE_INITIAL_CONFIGURATION) + parameters.workDirectory.clear(); - BuildDirParameters newParameters = m_parameters; - newParameters.workDirectory.clear(); - qCDebug(cmakeBuildSystemLog) << "Requesting parse due to persisting CMake State"; - setParametersAndRequestParse(newParameters, - REPARSE_URGENT | REPARSE_FORCE_CMAKE_RUN - | REPARSE_FORCE_CONFIGURATION | REPARSE_CHECK_CONFIGURATION); + qCDebug(cmakeBuildSystemLog) << "Requesting parse to persist CMake State"; + setParametersAndRequestParse(parameters, + REPARSE_URGENT | REPARSE_FORCE_CMAKE_RUN | reparseFlags); return true; } @@ -685,22 +710,20 @@ void CMakeBuildSystem::wireUpConnections(const Project *p) connect(KitManager::instance(), &KitManager::kitUpdated, this, [this](Kit *k) { if (k != kit()) return; // not for us... - // Build configuration has not changed, but Kit settings might have: - // reparse and check the configuration, independent of whether the reader has changed + // FIXME: This is no longer correct: QtC now needs to update the initial parameters + // FIXME: and then ask to reconfigure. qCDebug(cmakeBuildSystemLog) << "Requesting parse due to kit being updated"; setParametersAndRequestParse(BuildDirParameters(cmakeBuildConfiguration()), - CMakeBuildSystem::REPARSE_CHECK_CONFIGURATION); + CMakeBuildSystem::REPARSE_FORCE_CMAKE_RUN); }); // Became active/inactive: connect(project(), &Project::activeTargetChanged, this, [this](Target *t) { if (t == target()) { - // Build configuration has switched: - // * Check configuration if reader changes due to it not existing yet:-) - // * run cmake without configuration arguments if the reader stays + // Build configuration has changed along with the target: qCDebug(cmakeBuildSystemLog) << "Requesting parse due to active target changed"; setParametersAndRequestParse(BuildDirParameters(cmakeBuildConfiguration()), - CMakeBuildSystem::REPARSE_CHECK_CONFIGURATION); + CMakeBuildSystem::REPARSE_DEFAULT); } else { stopParsingAndClearState(); } @@ -708,12 +731,10 @@ void CMakeBuildSystem::wireUpConnections(const Project *p) connect(target(), &Target::activeBuildConfigurationChanged, this, [this](BuildConfiguration *bc) { if (cmakeBuildConfiguration()->isActive()) { if (cmakeBuildConfiguration() == bc) { - // Build configuration has switched: - // * Check configuration if reader changes due to it not existing yet:-) - // * run cmake without configuration arguments if the reader stays + // Build configuration has changed: qCDebug(cmakeBuildSystemLog) << "Requesting parse due to active BC changed"; setParametersAndRequestParse(BuildDirParameters(cmakeBuildConfiguration()), - CMakeBuildSystem::REPARSE_CHECK_CONFIGURATION); + CMakeBuildSystem::REPARSE_DEFAULT); } else { stopParsingAndClearState(); } @@ -723,40 +744,22 @@ void CMakeBuildSystem::wireUpConnections(const Project *p) // BuildConfiguration changed: connect(cmakeBuildConfiguration(), &CMakeBuildConfiguration::environmentChanged, this, [this]() { if (cmakeBuildConfiguration()->isActive()) { - // The environment on our BC has changed: - // * Error out if the reader updates, cannot happen since all BCs share a target/kit. - // * run cmake without configuration arguments if the reader stays + // The environment on our BC has changed, force CMake run to catch up with possible changes qCDebug(cmakeBuildSystemLog) << "Requesting parse due to environment change"; setParametersAndRequestParse(BuildDirParameters(cmakeBuildConfiguration()), - CMakeBuildSystem::REPARSE_CHECK_CONFIGURATION); + CMakeBuildSystem::REPARSE_FORCE_CMAKE_RUN); } }); connect(cmakeBuildConfiguration(), &CMakeBuildConfiguration::buildDirectoryChanged, this, [this]() { if (cmakeBuildConfiguration()->isActive()) { // The build directory of our BC has changed: - // * Error out if the reader updates, cannot happen since all BCs share a target/kit. - // * run cmake without configuration arguments if the reader stays - // If no configuration exists, then the arguments will get added automatically by - // the reader. + // Run with initial arguments! qCDebug(cmakeBuildSystemLog) << "Requesting parse due to build directory change"; setParametersAndRequestParse(BuildDirParameters(cmakeBuildConfiguration()), - CMakeBuildSystem::REPARSE_CHECK_CONFIGURATION); + CMakeBuildSystem::REPARSE_FORCE_INITIAL_CONFIGURATION + | CMakeBuildSystem::REPARSE_FORCE_CMAKE_RUN); } }); - connect(cmakeBuildConfiguration(), - &CMakeBuildConfiguration::configurationForCMakeChanged, - this, - [this]() { - if (cmakeBuildConfiguration()->isActive()) { - // The CMake configuration has changed on our BC: - // * Error out if the reader updates, cannot happen since all BCs share a target/kit. - // * run cmake with configuration arguments if the reader stays - qCDebug(cmakeBuildSystemLog) - << "Requesting parse due to cmake configuration change"; - setParametersAndRequestParse(BuildDirParameters(cmakeBuildConfiguration()), - CMakeBuildSystem::REPARSE_FORCE_CONFIGURATION); - } - }); connect(project(), &Project::projectFileIsDirty, this, [this]() { if (cmakeBuildConfiguration()->isActive() && !isParsing()) { @@ -770,9 +773,11 @@ void CMakeBuildSystem::wireUpConnections(const Project *p) }); // Force initial parsing run: - if (cmakeBuildConfiguration()->isActive()) + if (cmakeBuildConfiguration()->isActive()) { + qCDebug(cmakeBuildSystemLog) << "Initial run:"; setParametersAndRequestParse(BuildDirParameters(cmakeBuildConfiguration()), - CMakeBuildSystem::REPARSE_CHECK_CONFIGURATION); + CMakeBuildSystem::REPARSE_DEFAULT); + } } FilePath CMakeBuildSystem::workDirectory(const BuildDirParameters ¶meters) @@ -824,8 +829,7 @@ void CMakeBuildSystem::becameDirty() if (!tool->isAutoRun()) return; - setParametersAndRequestParse(BuildDirParameters(cmakeBuildConfiguration()), - REPARSE_CHECK_CONFIGURATION | REPARSE_SCAN); + setParametersAndRequestParse(BuildDirParameters(cmakeBuildConfiguration()), REPARSE_SCAN); } void CMakeBuildSystem::updateReparseParameters(const int parameters) @@ -840,149 +844,6 @@ int CMakeBuildSystem::takeReparseParameters() return result; } -bool CMakeBuildSystem::hasConfigChanged() -{ - checkConfiguration(); - - const QByteArray GENERATOR_KEY = "CMAKE_GENERATOR"; - const QByteArray EXTRA_GENERATOR_KEY = "CMAKE_EXTRA_GENERATOR"; - const QByteArray CMAKE_COMMAND_KEY = "CMAKE_COMMAND"; - const QByteArray CMAKE_C_COMPILER_KEY = "CMAKE_C_COMPILER"; - const QByteArray CMAKE_CXX_COMPILER_KEY = "CMAKE_CXX_COMPILER"; - - const QByteArrayList criticalKeys = {GENERATOR_KEY, - CMAKE_COMMAND_KEY, - CMAKE_C_COMPILER_KEY, - CMAKE_CXX_COMPILER_KEY}; - - QString errorMessage; - const CMakeConfig currentConfig = cmakeBuildConfiguration()->configurationFromCMake(); - if (!errorMessage.isEmpty()) - return false; - - const CMakeTool *tool = m_parameters.cmakeTool(); - QTC_ASSERT(tool, - return false); // No cmake... we should not have ended up here in the first place - const QString extraKitGenerator = m_parameters.extraGenerator; - const QString mainKitGenerator = m_parameters.generator; - CMakeConfig targetConfig = m_parameters.configuration; - targetConfig.append(CMakeConfigItem(GENERATOR_KEY, - CMakeConfigItem::INTERNAL, - QByteArray(), - mainKitGenerator.toUtf8())); - if (!extraKitGenerator.isEmpty()) - targetConfig.append(CMakeConfigItem(EXTRA_GENERATOR_KEY, - CMakeConfigItem::INTERNAL, - QByteArray(), - extraKitGenerator.toUtf8())); - targetConfig.append(CMakeConfigItem(CMAKE_COMMAND_KEY, - CMakeConfigItem::INTERNAL, - QByteArray(), - tool->cmakeExecutable().toUserOutput().toUtf8())); - Utils::sort(targetConfig, CMakeConfigItem::sortOperator()); - - bool mustReparse = false; - auto ccit = currentConfig.constBegin(); - auto kcit = targetConfig.constBegin(); - - while (ccit != currentConfig.constEnd() && kcit != targetConfig.constEnd()) { - if (ccit->key == kcit->key) { - if (ccit->value != kcit->value) { - if (criticalKeys.contains(kcit->key)) { - clearCMakeCache(); - return false; // no need to trigger a new reader, clearCache will do that - } - mustReparse = true; - } - ++ccit; - ++kcit; - } else { - if (ccit->key < kcit->key) { - ++ccit; - } else { - ++kcit; - mustReparse = true; - } - } - } - - // If we have keys that do not exist yet, then reparse. - // - // The critical keys *must* be set in cmake configuration, so those were already - // handled above. - return mustReparse || kcit != targetConfig.constEnd(); -} - -bool CMakeBuildSystem::checkConfiguration() -{ - if (m_parameters.workDirectory - != m_parameters.buildDirectory) // always throw away changes in the tmpdir! - return false; - - const CMakeConfig cache = cmakeBuildConfiguration()->configurationFromCMake(); - if (cache.isEmpty()) - return false; // No cache file yet. - - CMakeConfig newConfig; - QHash<QString, QPair<QString, QString>> changedKeys; - foreach (const CMakeConfigItem &projectItem, m_parameters.configuration) { - const QString projectKey = QString::fromUtf8(projectItem.key); - const QString projectValue = projectItem.expandedValue(m_parameters.expander); - const CMakeConfigItem &cmakeItem = Utils::findOrDefault(cache, - [&projectItem]( - const CMakeConfigItem &i) { - return i.key == projectItem.key; - }); - const QString iCacheValue = QString::fromUtf8(cmakeItem.value); - if (cmakeItem.isNull()) { - changedKeys.insert(projectKey, qMakePair(tr("<removed>"), projectValue)); - } else if (iCacheValue != projectValue) { - changedKeys.insert(projectKey, qMakePair(iCacheValue, projectValue)); - newConfig.append(cmakeItem); - } else { - newConfig.append(projectItem); - } - } - - if (!changedKeys.isEmpty()) { - QStringList keyList = changedKeys.keys(); - Utils::sort(keyList); - QString table = QString::fromLatin1("<table><tr><th>%1</th><th>%2</th><th>%3</th></tr>") - .arg(tr("Key")) - .arg(tr("%1 Project").arg(Core::Constants::IDE_DISPLAY_NAME)) - .arg(tr("Changed value")); - foreach (const QString &k, keyList) { - const QPair<QString, QString> data = changedKeys.value(k); - table += QString::fromLatin1("\n<tr><td>%1</td><td>%2</td><td>%3</td></tr>") - .arg(k) - .arg(data.second.toHtmlEscaped()) - .arg(data.first.toHtmlEscaped()); - } - table += QLatin1String("\n</table>"); - - QPointer<QMessageBox> box = new QMessageBox(Core::ICore::dialogParent()); - box->setText(tr("The project has been changed outside of %1.") - .arg(Core::Constants::IDE_DISPLAY_NAME)); - box->setInformativeText(table); - auto *defaultButton = box->addButton(tr("Discard External Changes"), - QMessageBox::RejectRole); - auto *applyButton = box->addButton(tr("Adapt %1 Project to Changes") - .arg(Core::Constants::IDE_DISPLAY_NAME), - QMessageBox::ApplyRole); - box->setDefaultButton(defaultButton); - - box->exec(); - if (box->clickedButton() == applyButton) { - m_parameters.configuration = newConfig; - QSignalBlocker blocker(buildConfiguration()); - cmakeBuildConfiguration()->setConfigurationForCMake(newConfig); - return false; - } else if (box->clickedButton() == defaultButton) - return true; - } - return false; -} - CMakeBuildConfiguration *CMakeBuildSystem::cmakeBuildConfiguration() const { return static_cast<CMakeBuildConfiguration *>(BuildSystem::buildConfiguration()); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h index 5ada71513c6..86d77d7a7d7 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h @@ -63,7 +63,7 @@ public: bool supportsAction(ProjectExplorer::Node *context, ProjectExplorer::ProjectAction action, - const ProjectExplorer::Node *node) const override; + const ProjectExplorer::Node *node) const final; bool addFiles(ProjectExplorer::Node *context, const QStringList &filePaths, QStringList *) final; @@ -73,6 +73,7 @@ public: // Actions: void runCMake(); void runCMakeAndScanProjectTree(); + void runCMakeWithExtraArguments(); bool persistCMakeState(); void clearCMakeCache(); @@ -95,19 +96,20 @@ public: private: // Actually ask for parsing: enum ReparseParameters { - REPARSE_DEFAULT = 0, // Nothing special:-) - REPARSE_FORCE_CMAKE_RUN = (1 << 0), // Force cmake to run - REPARSE_FORCE_CONFIGURATION = (1 << 1), // Force configuration arguments to cmake - REPARSE_CHECK_CONFIGURATION - = (1 << 2), // Check for on-disk config and QtC config diff // FIXME: Remove this! - REPARSE_SCAN = (1 << 3), // Run filesystem scan - REPARSE_URGENT = (1 << 4), // Do not delay the parser run by 1s + REPARSE_DEFAULT = 0, // Nothing special:-) + REPARSE_FORCE_CMAKE_RUN + = (1 << 0), // Force cmake to run, apply extraCMakeArguments if non-empty + REPARSE_FORCE_INITIAL_CONFIGURATION + = (1 << 1), // Force initial configuration arguments to cmake + REPARSE_FORCE_EXTRA_CONFIGURATION = (1 << 2), // Force extra configuration arguments to cmake + REPARSE_SCAN = (1 << 3), // Run filesystem scan + REPARSE_URGENT = (1 << 4), // Do not delay the parser run by 1s }; QString reparseParametersString(int reparseFlags); void setParametersAndRequestParse(const BuildDirParameters ¶meters, const int reparseParameters); - void writeConfigurationIntoBuildDirectory(const Utils::MacroExpander *expander); + bool mustApplyExtraArguments() const; // State handling: // Parser states: @@ -155,12 +157,9 @@ private: QList<ProjectExplorer::ExtraCompiler *> m_extraCompilers; QList<CMakeBuildTarget> m_buildTargets; - bool checkConfiguration(); - bool hasConfigChanged(); - // Parsing state: BuildDirParameters m_parameters; - int m_reparseParameters; + int m_reparseParameters = REPARSE_DEFAULT; mutable std::unordered_map<Utils::FilePath, std::unique_ptr<Utils::TemporaryDirectory>> m_buildDirToTempDir; FileApiReader m_reader; diff --git a/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp b/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp index a57d780d453..f979c283f35 100644 --- a/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp @@ -284,6 +284,54 @@ static QByteArrayList splitCMakeCacheLine(const QByteArray &line) { << line.mid(equalPos + 1); } +static CMakeConfigItem setItemFromString(const QString &input) +{ + return CMakeConfigItem::fromString(input); +} + +static CMakeConfigItem unsetItemFromString(const QString &input) +{ + CMakeConfigItem item(input.toUtf8(), QByteArray()); + item.isUnset = true; + return item; +} + +QList<CMakeConfigItem> CMakeConfigItem::itemsFromArguments(const QStringList &list) +{ + CMakeConfig result; + bool inSet = false; + bool inUnset = false; + for (const QString &i : list) { + if (inSet) { + inSet = false; + result.append(setItemFromString(i)); + continue; + } + if (inUnset) { + inUnset = false; + result.append(unsetItemFromString(i)); + continue; + } + if (i == "-U") { + inUnset = true; + continue; + } + if (i == "-D") { + inSet = true; + continue; + } + if (i.startsWith("-U")) { + result.append(unsetItemFromString(i.mid(2))); + continue; + } + if (i.startsWith("-D")) { + result.append(setItemFromString(i.mid(2))); + } + // ignore everything else as that does not define a configuration option + } + return result; +} + QList<CMakeConfigItem> CMakeConfigItem::itemsFromFile(const Utils::FilePath &cacheFile, QString *errorMessage) { CMakeConfig result; diff --git a/src/plugins/cmakeprojectmanager/cmakeconfigitem.h b/src/plugins/cmakeprojectmanager/cmakeconfigitem.h index c99f1e64f7e..10c7651f6f7 100644 --- a/src/plugins/cmakeprojectmanager/cmakeconfigitem.h +++ b/src/plugins/cmakeprojectmanager/cmakeconfigitem.h @@ -64,6 +64,7 @@ public: static std::function<bool(const CMakeConfigItem &a, const CMakeConfigItem &b)> sortOperator(); static CMakeConfigItem fromString(const QString &s); + static QList<CMakeConfigItem> itemsFromArguments(const QStringList &list); static QList<CMakeConfigItem> itemsFromFile(const Utils::FilePath &input, QString *errorMessage); QString toString(const Utils::MacroExpander *expander = nullptr) const; QString toArgument(const Utils::MacroExpander *expander = nullptr) const; diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp index 89ab5a9aab0..86c9f8114b2 100644 --- a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp +++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp @@ -974,6 +974,12 @@ void CMakeConfigurationKitAspect::fromStringList(Kit *k, const QStringList &in) setConfiguration(k, result); } +QStringList CMakeConfigurationKitAspect::toArgumentsList(const Kit *k) +{ + return Utils::transform(CMakeConfigurationKitAspect::configuration(k), + [](const CMakeConfigItem &i) { return i.toArgument(nullptr); }); +} + CMakeConfig CMakeConfigurationKitAspect::defaultConfiguration(const Kit *k) { Q_UNUSED(k) diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.h b/src/plugins/cmakeprojectmanager/cmakekitinformation.h index f2579f3282c..fd6af308cff 100644 --- a/src/plugins/cmakeprojectmanager/cmakekitinformation.h +++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.h @@ -101,6 +101,8 @@ public: static QStringList toStringList(const ProjectExplorer::Kit *k); static void fromStringList(ProjectExplorer::Kit *k, const QStringList &in); + static QStringList toArgumentsList(const ProjectExplorer::Kit *k); + static CMakeConfig defaultConfiguration(const ProjectExplorer::Kit *k); // KitAspect interface diff --git a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp index e500ac8c2ec..ba6b582896a 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp @@ -113,11 +113,7 @@ void CMakeProcess::run(const BuildDirParameters ¶meters, const QStringList & connect(process.get(), QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &CMakeProcess::handleProcessFinished); - QStringList args; - args += parameters.generatorArguments; - args += arguments; - args += srcDir; - Utils::CommandLine commandLine(cmake->cmakeExecutable(), args); + Utils::CommandLine commandLine(cmake->cmakeExecutable(), QStringList({"-S", srcDir, QString("-B"), workDirectory.toString()}) + arguments); TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); diff --git a/src/plugins/cmakeprojectmanager/configmodel.cpp b/src/plugins/cmakeprojectmanager/configmodel.cpp index b6134b14fa0..c0f075fda6b 100644 --- a/src/plugins/cmakeprojectmanager/configmodel.cpp +++ b/src/plugins/cmakeprojectmanager/configmodel.cpp @@ -96,24 +96,6 @@ void ConfigModel::setConfigurationFromKit(const QHash<QString, QString> &kitConf setConfiguration(m_configuration); } -void ConfigModel::setConfigurationForCMake(const QHash<QString, QString> &config) -{ - for (InternalDataItem &i : m_configuration) { - if (!config.contains(i.key)) - continue; - - const QString v = config.value(i.key); - if (i.value == v) { - i.newValue.clear(); - i.isUserChanged = false; - } else { - i.newValue = v; - i.isUserChanged = true; - } - } - setConfiguration(m_configuration); -} - void ConfigModel::flush() { setConfiguration(QList<InternalDataItem>()); diff --git a/src/plugins/cmakeprojectmanager/configmodel.h b/src/plugins/cmakeprojectmanager/configmodel.h index 5844c388f96..7c5cfa205b1 100644 --- a/src/plugins/cmakeprojectmanager/configmodel.h +++ b/src/plugins/cmakeprojectmanager/configmodel.h @@ -72,7 +72,7 @@ public: void setConfiguration(const CMakeConfig &config); void setConfiguration(const QList<DataItem> &config); void setConfigurationFromKit(const QHash<QString, QString> &kitConfig); - void setConfigurationForCMake(const QHash<QString, QString> &config); + void flush(); void resetAllChanges(); diff --git a/src/plugins/cmakeprojectmanager/fileapireader.cpp b/src/plugins/cmakeprojectmanager/fileapireader.cpp index 5b252f62978..1e25125a80a 100644 --- a/src/plugins/cmakeprojectmanager/fileapireader.cpp +++ b/src/plugins/cmakeprojectmanager/fileapireader.cpp @@ -99,47 +99,63 @@ void FileApiReader::resetData() m_knownHeaders.clear(); } -void FileApiReader::parse(bool forceCMakeRun, bool forceConfiguration) +void FileApiReader::parse(bool forceCMakeRun, + bool forceInitialConfiguration, + bool forceExtraConfiguration) { qCDebug(cmakeFileApiMode) << "Parse called with arguments: ForceCMakeRun:" << forceCMakeRun - << " - forceConfiguration:" << forceConfiguration; + << " - forceConfiguration:" << forceInitialConfiguration + << " - forceExtraConfiguration:" << forceExtraConfiguration; startState(); - if (forceConfiguration) { - // Initial create: - qCDebug(cmakeFileApiMode) << "FileApiReader: Starting CMake with forced configuration."; - const FilePath path = m_parameters.workDirectory.pathAppended("qtcsettings.cmake"); - startCMakeState(QStringList({QString("-C"), path.toUserOutput()})); - // Keep m_isParsing enabled! - return; - } + const QStringList args = (forceInitialConfiguration ? m_parameters.initialCMakeArguments + : QStringList()) + + (forceExtraConfiguration ? m_parameters.extraCMakeArguments + : QStringList()); + qCDebug(cmakeFileApiMode) << "Parameters request these CMake arguments:" << args; const QFileInfo replyFi = FileApiParser::scanForCMakeReplyFile(m_parameters.workDirectory); // Only need to update when one of the following conditions is met: - // * The user forces the update, + // * The user forces the cmake run, + // * The user provided arguments, // * There is no reply file, // * One of the cmakefiles is newer than the replyFile and the user asked // for creator to run CMake as needed, // * A query file is newer than the reply file - const bool mustUpdate = forceCMakeRun || !replyFi.exists() - || (m_parameters.cmakeTool() && m_parameters.cmakeTool()->isAutoRun() - && anyOf(m_cmakeFiles, - [&replyFi](const FilePath &f) { - return f.toFileInfo().lastModified() - > replyFi.lastModified(); - })) - || anyOf(FileApiParser::cmakeQueryFilePaths(m_parameters.workDirectory), [&replyFi](const QString &qf) { - return QFileInfo(qf).lastModified() > replyFi.lastModified(); - }); + const bool hasArguments = !args.isEmpty(); + const bool replyFileMissing = !replyFi.exists(); + const bool cmakeFilesChanged = m_parameters.cmakeTool() && m_parameters.cmakeTool()->isAutoRun() + && anyOf(m_cmakeFiles, [&replyFi](const FilePath &f) { + return f.toFileInfo().lastModified() + > replyFi.lastModified(); + }); + const bool queryFileChanged = anyOf(FileApiParser::cmakeQueryFilePaths( + m_parameters.workDirectory), + [&replyFi](const QString &qf) { + return QFileInfo(qf).lastModified() + > replyFi.lastModified(); + }); + + const bool mustUpdate = forceCMakeRun || hasArguments || replyFileMissing || cmakeFilesChanged + || queryFileChanged; + qCDebug(cmakeFileApiMode) << QString("Do I need to run CMake? %1 " + "(force: %2 | args: %3 | missing reply: %4 | " + "cmakeFilesChanged: %5 | " + "queryFileChanged: %6)") + .arg(mustUpdate) + .arg(forceCMakeRun) + .arg(hasArguments) + .arg(replyFileMissing) + .arg(cmakeFilesChanged) + .arg(queryFileChanged); if (mustUpdate) { - qCDebug(cmakeFileApiMode) << "FileApiReader: Starting CMake with no arguments."; - startCMakeState(QStringList()); - // Keep m_isParsing enabled! - return; + qCDebug(cmakeFileApiMode) << QString("FileApiReader: Starting CMake with \"%1\".") + .arg(args.join("\", \"")); + startCMakeState(args); + } else { + endState(replyFi); } - - endState(replyFi); } void FileApiReader::stop() diff --git a/src/plugins/cmakeprojectmanager/fileapireader.h b/src/plugins/cmakeprojectmanager/fileapireader.h index 2319010aa46..0b578511ecb 100644 --- a/src/plugins/cmakeprojectmanager/fileapireader.h +++ b/src/plugins/cmakeprojectmanager/fileapireader.h @@ -58,7 +58,7 @@ public: void setParameters(const BuildDirParameters &p); void resetData(); - void parse(bool forceCMakeRun, bool forceConfiguration); + void parse(bool forceCMakeRun, bool forceInitialConfiguration, bool forceExtraConfiguration); void stop(); bool isParsing() const; diff --git a/src/plugins/projectexplorer/buildsystem.h b/src/plugins/projectexplorer/buildsystem.h index af69ca29e48..5d38e8300c2 100644 --- a/src/plugins/projectexplorer/buildsystem.h +++ b/src/plugins/projectexplorer/buildsystem.h @@ -42,6 +42,7 @@ class Node; // BuildSystem: // -------------------------------------------------------------------- +// Check buildsystem.md for more information class PROJECTEXPLORER_EXPORT BuildSystem : public QObject { Q_OBJECT |