diff --git a/src/plugins/cmakeprojectmanager/builddirparameters.cpp b/src/plugins/cmakeprojectmanager/builddirparameters.cpp index 131ad540018..47af2ff68af 100644 --- a/src/plugins/cmakeprojectmanager/builddirparameters.cpp +++ b/src/plugins/cmakeprojectmanager/builddirparameters.cpp @@ -73,6 +73,18 @@ BuildDirParameters::BuildDirParameters(CMakeBuildConfiguration *bc) sourceDirectory = p->projectDirectory(); buildDirectory = bc->buildDirectory(); + cmakeBuildType = bc->cmakeBuildType(); + if (cmakeBuildType.isEmpty()) { + // The empty build type might be just a case of loading of an existing project + // that doesn't have the "CMake.Build.Type" aspect saved + const CMakeConfig config = CMakeConfigItem::itemsFromArguments(initialCMakeArguments); + if (!config.isEmpty()) { + cmakeBuildType = QString::fromLatin1(CMakeConfigItem::valueOf("CMAKE_BUILD_TYPE", config)); + if (!cmakeBuildType.isEmpty()) + bc->setCMakeBuildType(cmakeBuildType); + } + } + environment = bc->environment(); // Disable distributed building for configuration runs. CMake does not do those in parallel, // so there is no win in sending data over the network. diff --git a/src/plugins/cmakeprojectmanager/builddirparameters.h b/src/plugins/cmakeprojectmanager/builddirparameters.h index 05da7f126ac..375562d8ea2 100644 --- a/src/plugins/cmakeprojectmanager/builddirparameters.h +++ b/src/plugins/cmakeprojectmanager/builddirparameters.h @@ -54,6 +54,7 @@ public: Utils::FilePath sourceDirectory; Utils::FilePath buildDirectory; Utils::FilePath workDirectory; // either buildDirectory or a QTemporaryDirectory! + QString cmakeBuildType; Utils::Environment environment; diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 114b93739ab..d9496fad466 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -73,8 +73,9 @@ static QStringList defaultInitialCMakeArguments(const Kit *k, const QString buil QStringList initialArgs = CMakeGeneratorKitAspect::generatorArguments(k); // CMAKE_BUILD_TYPE: - if (!buildType.isEmpty()) + if (!buildType.isEmpty() && !CMakeGeneratorKitAspect::isMultiConfigGenerator(k)) { initialArgs.append(QString::fromLatin1("-DCMAKE_BUILD_TYPE:String=%1").arg(buildType)); + } // Cross-compilation settings: const QString sysRoot = SysRootKitAspect::sysRoot(k).toString(); @@ -130,6 +131,7 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Utils::Id id) initialCMakeArgumentsAspect->setMacroExpanderProvider([this]{ return macroExpander(); }); addAspect(); + addAspect(); appendInitialBuildStep(Constants::CMAKE_BUILD_STEP_ID); appendInitialCleanStep(Constants::CMAKE_BUILD_STEP_ID); @@ -198,6 +200,7 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Utils::Id id) } setInitialCMakeArguments(initialArgs); + setCMakeBuildType(info.typeName); }); const auto qmlDebuggingAspect = addAspect(); @@ -264,6 +267,10 @@ FilePath CMakeBuildConfiguration::shadowBuildDirectory(const FilePath &projectFi QDir projectDir = QDir(Project::projectDirectory(projectFilePath).toString()); QString buildPath = expander.expand(ProjectExplorerPlugin::buildDirectoryTemplate()); buildPath.replace(" ", "-"); + + if (CMakeGeneratorKitAspect::isMultiConfigGenerator(k)) + buildPath = buildPath.left(buildPath.lastIndexOf(QString("-%1").arg(bcName))); + return FilePath::fromUserInput(projectDir.absoluteFilePath(buildPath)); } @@ -476,7 +483,11 @@ BuildInfo CMakeBuildConfigurationFactory::createBuildInfo(BuildType buildType) ProjectExplorer::BuildConfiguration::BuildType CMakeBuildConfiguration::buildType() const { QByteArray cmakeBuildTypeName = CMakeConfigItem::valueOf("CMAKE_BUILD_TYPE", m_configurationFromCMake); - + if (cmakeBuildTypeName.isEmpty()) { + QByteArray cmakeCfgTypes = CMakeConfigItem::valueOf("CMAKE_CONFIGURATION_TYPES", m_configurationFromCMake); + if (!cmakeCfgTypes.isEmpty()) + cmakeBuildTypeName = cmakeBuildType().toUtf8(); + } // Cover all common CMake build types const CMakeBuildConfigurationFactory::BuildType cmakeBuildType = CMakeBuildConfigurationFactory::buildTypeFromByteArray(cmakeBuildTypeName); @@ -503,6 +514,16 @@ Utils::FilePath CMakeBuildConfiguration::sourceDirectory() const return Utils::FilePath::fromString(aspect()->value()); } +QString CMakeBuildConfiguration::cmakeBuildType() const +{ + return aspect()->value(); +} + +void CMakeBuildConfiguration::setCMakeBuildType(const QString &cmakeBuildType) +{ + aspect()->setValue(cmakeBuildType); +} + // ---------------------------------------------------------------------- // - InitialCMakeParametersAspect: // ---------------------------------------------------------------------- @@ -523,5 +544,13 @@ SourceDirectoryAspect::SourceDirectoryAspect() setSettingsKey("CMake.Source.Directory"); } +// ----------------------------------------------------------------------------- +// BuildTypeAspect: +// ----------------------------------------------------------------------------- +BuildTypeAspect::BuildTypeAspect() +{ + setSettingsKey("CMake.Build.Type"); +} + } // namespace Internal } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h index 256e2c022c9..6db6610da1c 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h @@ -70,6 +70,9 @@ public: void setSourceDirectory(const Utils::FilePath& path); Utils::FilePath sourceDirectory() const; + QString cmakeBuildType() const; + void setCMakeBuildType(const QString &cmakeBuildType); + signals: void errorOccurred(const QString &message); void warningOccurred(const QString &message); @@ -144,5 +147,14 @@ public: SourceDirectoryAspect(); }; +class BuildTypeAspect final : public Utils::StringAspect +{ + Q_OBJECT + +public: + BuildTypeAspect(); +}; + + } // namespace Internal } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp index 46db12d593f..924333f107e 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp @@ -390,6 +390,13 @@ CommandLine CMakeBuildStep::cmakeCommand() const return s; })); + auto bs = qobject_cast(buildSystem()); + auto bc = qobject_cast(buildConfiguration()); + if (bc && bs && bs->isMultiConfig()) { + cmd.addArg("--config"); + cmd.addArg(bc->cmakeBuildType()); + } + if (!m_cmakeArguments->value().isEmpty()) cmd.addArgs(m_cmakeArguments->value(), CommandLine::Raw); @@ -401,40 +408,27 @@ CommandLine CMakeBuildStep::cmakeCommand() const return cmd; } -QString CMakeBuildStep::cleanTarget() +QString CMakeBuildStep::cleanTarget() const { return QString("clean"); } -QString CMakeBuildStep::allTarget() +QString CMakeBuildStep::allTarget() const { - return QString("all"); + return m_allTarget; } -QString CMakeBuildStep::installTarget() +QString CMakeBuildStep::installTarget() const { - return QString("install"); + return m_installTarget; } -QString CMakeBuildStep::installStripTarget() +QStringList CMakeBuildStep::specialTargets(bool allCapsTargets) { - return QString("install/strip"); -} - -QString CMakeBuildStep::packageTarget() -{ - return QString("package"); -} - -QString CMakeBuildStep::testTarget() -{ - return QString("test"); -} - -QStringList CMakeBuildStep::specialTargets() -{ - return { allTarget(), cleanTarget(), installTarget(), installStripTarget(), - packageTarget(), testTarget() }; + if (!allCapsTargets) + return {"all", "clean", "install", "install/strip", "package", "test"}; + else + return {"ALL_BUILD", "clean", "INSTALL", "PACKAGE", "RUN_TESTS"}; } QString CMakeBuildStep::activeRunConfigTarget() const @@ -523,12 +517,26 @@ void CMakeBuildStep::recreateBuildTargetsModel() auto bs = qobject_cast(buildSystem()); QStringList targetList = bs ? bs->buildTargetTitles() : QStringList(); + bool usesAllCapsTargets = bs ? bs->usesAllCapsTargets() : false; + if (usesAllCapsTargets) { + m_allTarget = "ALL_BUILD"; + m_installTarget = "INSTALL"; + + int idx = m_buildTargets.indexOf(QString("all")); + if (idx != -1) + m_buildTargets[idx] = QString("ALL_BUILD"); + idx = m_buildTargets.indexOf(QString("install")); + if (idx != -1) + m_buildTargets[idx] = QString("INSTALL"); + } + targetList.sort(); + targetList.removeDuplicates(); addItem(QString(), true); for (const QString &buildTarget : qAsConst(targetList)) - addItem(buildTarget, specialTargets().contains(buildTarget)); + addItem(buildTarget, specialTargets(usesAllCapsTargets).contains(buildTarget)); updateBuildTargetsModel(); } diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.h b/src/plugins/cmakeprojectmanager/cmakebuildstep.h index 68d239e81eb..5b5e17eb5dd 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.h @@ -69,13 +69,10 @@ public: QVariantMap toMap() const override; - static QString cleanTarget(); - static QString allTarget(); - static QString installTarget(); - static QString installStripTarget(); - static QString packageTarget(); - static QString testTarget(); - static QStringList specialTargets(); + QString cleanTarget() const; + QString allTarget() const ; + QString installTarget() const; + static QStringList specialTargets(bool allCapsTargets); QString activeRunConfigTarget() const; @@ -110,6 +107,9 @@ private: Utils::StringAspect *m_toolArguments = nullptr; bool m_waiting = false; + QString m_allTarget = "all"; + QString m_installTarget = "install"; + Utils::TreeModel m_buildTargetModel; }; diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index e053a15da7d..1bdc82d327e 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -705,7 +705,7 @@ void CMakeBuildSystem::handleParsingSucceeded() QString errorMessage; { - m_buildTargets = Utils::transform(CMakeBuildStep::specialTargets(), [this](const QString &t) { + m_buildTargets = Utils::transform(CMakeBuildStep::specialTargets(m_reader.usesAllCapsTargets()), [this](const QString &t) { CMakeBuildTarget result; result.title = t; result.workingDirectory = m_parameters.workDirectory; @@ -1016,6 +1016,16 @@ CMakeConfig CMakeBuildSystem::parseCMakeCacheDotTxt(const Utils::FilePath &cache return result; } +bool CMakeBuildSystem::isMultiConfig() const +{ + return m_reader.isMultiConfig(); +} + +bool CMakeBuildSystem::usesAllCapsTargets() const +{ + return m_reader.usesAllCapsTargets(); +} + const QList CMakeBuildSystem::testcasesInfo() const { return m_testNames; diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h index a496d9ba351..47e2a7a822c 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h @@ -97,6 +97,9 @@ public: static CMakeConfig parseCMakeCacheDotTxt(const Utils::FilePath &cacheFile, QString *errorMessage); + bool isMultiConfig() const; + bool usesAllCapsTargets() const; + private: // Actually ask for parsing: enum ReparseParameters { diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp index 6f32eb5be10..eff5367ab39 100644 --- a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp +++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp @@ -449,8 +449,8 @@ private: generatorCombo->setCurrentText(CMakeGeneratorKitAspect::generator(kit())); extraGeneratorCombo->setCurrentText(CMakeGeneratorKitAspect::extraGenerator(kit())); - platformEdit->setText(platformEdit->isEnabled() ? CMakeGeneratorKitAspect::platform(kit()) : QLatin1String("")); - toolsetEdit->setText(toolsetEdit->isEnabled() ? CMakeGeneratorKitAspect::toolset(kit()) : QLatin1String("")); + platformEdit->setText(platformEdit->isEnabled() ? CMakeGeneratorKitAspect::platform(kit()) : QString()); + toolsetEdit->setText(toolsetEdit->isEnabled() ? CMakeGeneratorKitAspect::toolset(kit()) : QString()); connect(generatorCombo, &QComboBox::currentTextChanged, updateDialog); @@ -619,6 +619,14 @@ QStringList CMakeGeneratorKitAspect::generatorArguments(const Kit *k) return result; } +bool CMakeGeneratorKitAspect::isMultiConfigGenerator(const Kit *k) +{ + const QString generator = CMakeGeneratorKitAspect::generator(k); + return generator.indexOf("Visual Studio") != -1 || + generator == "Xcode" || + generator == "Ninja Multi-Config"; +} + QVariant CMakeGeneratorKitAspect::defaultValue(const Kit *k) const { QTC_ASSERT(k, return QVariant()); diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.h b/src/plugins/cmakeprojectmanager/cmakekitinformation.h index 71444463a45..5d3456892d0 100644 --- a/src/plugins/cmakeprojectmanager/cmakekitinformation.h +++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.h @@ -77,6 +77,7 @@ public: static void set(ProjectExplorer::Kit *k, const QString &generator, const QString &extraGenerator, const QString &platform, const QString &toolset); static QStringList generatorArguments(const ProjectExplorer::Kit *k); + static bool isMultiConfigGenerator(const ProjectExplorer::Kit *k); // KitAspect interface ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *k) const final; diff --git a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp index 9cd00b92051..a8e768d6152 100644 --- a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp @@ -71,7 +71,7 @@ void CMakeTargetLocatorFilter::prepareSearch(const QString &entry) const QList buildTargets = bs->buildTargets(); for (const CMakeBuildTarget &target : buildTargets) { - if (target.targetType == UtilityType && !CMakeBuildStep::specialTargets().contains(target.title)) + if (target.targetType == UtilityType && !CMakeBuildStep::specialTargets(bs->usesAllCapsTargets()).contains(target.title)) continue; const int index = target.title.indexOf(entry); if (index >= 0) { diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index 381f25e37bd..8e0014c87bf 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -25,6 +25,8 @@ #include "cmakeproject.h" +#include "cmakebuildconfiguration.h" +#include "cmakebuildsystem.h" #include "cmakebuildstep.h" #include "cmakekitinformation.h" #include "cmakeprojectconstants.h" @@ -119,7 +121,21 @@ MakeInstallCommand CMakeProject::makeInstallCommand(const Target *target, cmd.command = tool->cmakeExecutable(); } } - cmd.arguments << "--build" << "." << "--target" << "install"; + + QString installTarget = "install"; + QStringList config; + + auto bs = qobject_cast(target->buildSystem()); + auto bc = qobject_cast(target->activeBuildConfiguration()); + if (bs && bc) { + if (bs->usesAllCapsTargets()) + installTarget = "INSTALL"; + if (bs->isMultiConfig()) + config << "--config" << bc->cmakeBuildType(); + } + + cmd.arguments << "--build" << "." << "--target" << installTarget << config; + cmd.environment.set("DESTDIR", QDir::toNativeSeparators(installRoot)); return cmd; } diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h index c96242b674a..c10bcbd32e1 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.h +++ b/src/plugins/cmakeprojectmanager/cmakeproject.h @@ -33,6 +33,7 @@ namespace CMakeProjectManager { namespace Internal { class CMakeProjectImporter; +class CMakeBuildSystem; } class CMAKE_EXPORT CMakeProject final : public ProjectExplorer::Project @@ -57,7 +58,7 @@ private: mutable Internal::CMakeProjectImporter *m_projectImporter = nullptr; - friend class CMakeBuildSystem; + friend class Internal::CMakeBuildSystem; }; } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp index 5b89c3866dd..a4356876b7b 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp @@ -287,43 +287,57 @@ QList CMakeProjectImporter::examineDirectory(const FilePath &importPath, qCDebug(cmInputLog) << "Failed to read configuration from" << cacheFile << errorMessage; return { }; } - auto data = std::make_unique(); - data->cmakeHomeDirectory = FilePath::fromUserInput( - QString::fromUtf8( - CMakeConfigItem::valueOf("CMAKE_HOME_DIRECTORY", config))) - .canonicalPath(); - const FilePath canonicalProjectDirectory = projectDirectory().canonicalPath(); - if (data->cmakeHomeDirectory != canonicalProjectDirectory) { - *warningMessage = tr("Unexpected source directory \"%1\", expected \"%2\". " - "This can be correct in some situations, for example when " - "importing a standalone Qt test, but usually this is an error. " - "Import the build anyway?") - .arg(data->cmakeHomeDirectory.toUserOutput(), - canonicalProjectDirectory.toUserOutput()); + QByteArrayList buildConfigurationTypes = {CMakeConfigItem::valueOf("CMAKE_BUILD_TYPE", config)}; + if (buildConfigurationTypes.front().isEmpty()) { + QByteArray buildConfigurationTypesString = + CMakeConfigItem::valueOf("CMAKE_CONFIGURATION_TYPES", config); + if (!buildConfigurationTypesString.isEmpty()) { + buildConfigurationTypes = buildConfigurationTypesString.split(';'); + } } - data->buildDirectory = importPath; - data->cmakeBuildType = CMakeConfigItem::valueOf("CMAKE_BUILD_TYPE", config); + QList result; + for (auto const &buildType: qAsConst(buildConfigurationTypes)) { + auto data = std::make_unique(); - data->cmakeBinary = FilePath::fromUtf8(CMakeConfigItem::valueOf("CMAKE_COMMAND", config)); - data->generator = CMakeConfigItem::valueOf("CMAKE_GENERATOR", config); - data->extraGenerator = CMakeConfigItem::valueOf("CMAKE_EXTRA_GENERATOR", config); - data->platform = CMakeConfigItem::valueOf("CMAKE_GENERATOR_PLATFORM", config); - data->toolset = CMakeConfigItem::valueOf("CMAKE_GENERATOR_TOOLSET", config); + data->cmakeHomeDirectory = FilePath::fromUserInput( + QString::fromUtf8( + CMakeConfigItem::valueOf("CMAKE_HOME_DIRECTORY", config))) + .canonicalPath(); + const FilePath canonicalProjectDirectory = projectDirectory().canonicalPath(); + if (data->cmakeHomeDirectory != canonicalProjectDirectory) { + *warningMessage = tr("Unexpected source directory \"%1\", expected \"%2\". " + "This can be correct in some situations, for example when " + "importing a standalone Qt test, but usually this is an error. " + "Import the build anyway?") + .arg(data->cmakeHomeDirectory.toUserOutput(), + canonicalProjectDirectory.toUserOutput()); + } - data->sysroot = CMakeConfigItem::valueOf("CMAKE_SYSROOT", config); + data->buildDirectory = importPath; + data->cmakeBuildType = buildType; - // Qt: - const FilePath qmake = qmakeFromCMakeCache(config); - if (!qmake.isEmpty()) - data->qt = findOrCreateQtVersion(qmake); + data->cmakeBinary = FilePath::fromUtf8(CMakeConfigItem::valueOf("CMAKE_COMMAND", config)); + data->generator = CMakeConfigItem::valueOf("CMAKE_GENERATOR", config); + data->extraGenerator = CMakeConfigItem::valueOf("CMAKE_EXTRA_GENERATOR", config); + data->platform = CMakeConfigItem::valueOf("CMAKE_GENERATOR_PLATFORM", config); + data->toolset = CMakeConfigItem::valueOf("CMAKE_GENERATOR_TOOLSET", config); - // ToolChains: - data->toolChains = extractToolChainsFromCache(config); + data->sysroot = CMakeConfigItem::valueOf("CMAKE_SYSROOT", config); - qCInfo(cmInputLog) << "Offering to import" << importPath.toUserOutput(); - return {static_cast(data.release())}; + // Qt: + const FilePath qmake = qmakeFromCMakeCache(config); + if (!qmake.isEmpty()) + data->qt = findOrCreateQtVersion(qmake); + + // ToolChains: + data->toolChains = extractToolChainsFromCache(config); + + qCInfo(cmInputLog) << "Offering to import" << importPath.toUserOutput(); + result.push_back(static_cast(data.release())); + } + return result; } bool CMakeProjectImporter::matchKit(void *directoryData, const Kit *k) const diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp index 566d1e30f19..f5e165b7950 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp @@ -59,8 +59,6 @@ CMakeManager::CMakeManager() , m_clearCMakeCacheAction(new QAction(QIcon(), tr("Clear CMake Configuration"), this)) , m_runCMakeActionContextMenu(new QAction(QIcon(), tr("Run CMake"), this)) , m_rescanProjectAction(new QAction(QIcon(), tr("Rescan Project"), this)) - , m_parseAndValidateCMakeReplyFileAction( - new QAction(QIcon(), tr("Parse and verify a CMake reply file."), this)) { Core::ActionContainer *mbuild = Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT); @@ -132,13 +130,6 @@ CMakeManager::CMakeManager() mbuild->addAction(command, ProjectExplorer::Constants::G_BUILD_BUILD); connect(m_buildFileAction, &QAction::triggered, this, [this] { buildFile(); }); - command = Core::ActionManager::registerAction(m_parseAndValidateCMakeReplyFileAction, - "CMakeProject.Debug.ParseAndVerifyReplyFile"); - connect(m_parseAndValidateCMakeReplyFileAction, - &QAction::triggered, - this, - &CMakeManager::parseAndValidateCMakeReplyFile); - connect(SessionManager::instance(), &SessionManager::startupProjectChanged, this, &CMakeManager::updateCmakeActions); connect(BuildManager::instance(), &BuildManager::buildStateChanged, @@ -227,29 +218,6 @@ void CMakeManager::enableBuildFileMenus(Node *node) } } -void CMakeManager::parseAndValidateCMakeReplyFile() -{ - QString replyFile = QFileDialog::getOpenFileName(Core::ICore::mainWindow(), - tr("Select a CMake Reply File"), - QString(), - QString("index*.json")); - if (replyFile.isEmpty()) - return; - - QString errorMessage; - auto result = FileApiParser::parseData(QFileInfo(replyFile), errorMessage); - - const QString message - = errorMessage.isEmpty() - ? tr("The reply file \"%1\" and referenced data parsed OK and passed validation.") - .arg(QDir::toNativeSeparators(replyFile)) - : tr("The reply file \"%1\" failed to parse or validate with error " - "message:
\"%2\"") - .arg(QDir::toNativeSeparators(replyFile)) - .arg(errorMessage); - QMessageBox::information(Core::ICore::mainWindow(), tr("Parsing Result"), message); -} - void CMakeManager::buildFile(Node *node) { if (!node) { diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h index 22c0c30edc6..db3b10b6e2c 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h @@ -53,15 +53,11 @@ private: void updateBuildFileAction(); void enableBuildFileMenus(ProjectExplorer::Node *node); - // Debugging helper: - void parseAndValidateCMakeReplyFile(); - QAction *m_runCMakeAction; QAction *m_clearCMakeCacheAction; QAction *m_runCMakeActionContextMenu; QAction *m_rescanProjectAction; QAction *m_buildFileContextMenu; - QAction *m_parseAndValidateCMakeReplyFileAction; Utils::ParameterAction *m_buildFileAction; }; diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp index 2ca34b68ad9..1c161ab8105 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp @@ -102,22 +102,6 @@ CMakeFileResult extractCMakeFilesData(const std::vector &codemodel, QString &errorMessage) -{ - if (codemodel.size() == 0) { - qWarning() << "No configuration found!"; - errorMessage = "No configuration found!"; - return {}; - } - if (codemodel.size() > 1) - qWarning() << "Multi-configuration generator found, ignoring all but first configuration"; - - Configuration result = std::move(codemodel[0]); - codemodel.clear(); - - return result; -} - class PreprocessedData { public: @@ -143,12 +127,7 @@ PreprocessedData preprocess(FileApiData &data, result.cache = std::move(data.cache); // Make sure this is available, even when nothing else is - // Simplify to only one configuration: - result.codemodel = extractConfiguration(data.codemodel, errorMessage); - - if (!errorMessage.isEmpty()) { - return result; - } + result.codemodel = std::move(data.codemodel); CMakeFileResult cmakeFileResult = extractCMakeFilesData(data.cmakeFiles, sourceDirectory, @@ -717,6 +696,9 @@ FileApiQtcData extractData(FileApiData &input, setupLocationInfoForTargets(result.rootProjectNode.get(), result.buildTargets); result.ctestPath = input.replyFile.ctestExecutable; + result.isMultiConfig = input.replyFile.isMultiConfig; + if (input.replyFile.isMultiConfig && input.replyFile.generator != "Ninja Multi-Config") + result.usesAllCapsTargets = true; return result; } diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.h b/src/plugins/cmakeprojectmanager/fileapidataextractor.h index 41e116df273..bd57e194664 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.h +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.h @@ -55,6 +55,8 @@ public: std::unique_ptr rootProjectNode; QSet knownHeaders; QString ctestPath; + bool isMultiConfig = false; + bool usesAllCapsTargets = false; }; FileApiQtcData extractData(FileApiData &data, diff --git a/src/plugins/cmakeprojectmanager/fileapiparser.cpp b/src/plugins/cmakeprojectmanager/fileapiparser.cpp index b62bb2c943f..3e0cdc56d89 100644 --- a/src/plugins/cmakeprojectmanager/fileapiparser.cpp +++ b/src/plugins/cmakeprojectmanager/fileapiparser.cpp @@ -142,6 +142,7 @@ static ReplyFileContents readReplyFile(const QFileInfo &fi, QString &errorMessag const QJsonObject generator = cmakeObject.value("generator").toObject(); { result.generator = generator.value("name").toString(); + result.isMultiConfig = generator.value("multiConfig").toBool(); } } } @@ -855,23 +856,22 @@ bool FileApiParser::setupCMakeFileApi(const FilePath &buildDirectory, Utils::Fil return true; } -static QStringList uniqueTargetFiles(const std::vector &configs) +static QStringList uniqueTargetFiles(const Configuration &config) { QSet knownIds; QStringList files; - for (const Configuration &config : configs) { - for (const Target &t : config.targets) { - const int knownCount = knownIds.count(); - knownIds.insert(t.id); - if (knownIds.count() > knownCount) { - files.append(t.jsonFile); - } + for (const Target &t : config.targets) { + const int knownCount = knownIds.count(); + knownIds.insert(t.id); + if (knownIds.count() > knownCount) { + files.append(t.jsonFile); } } return files; } -FileApiData FileApiParser::parseData(const QFileInfo &replyFileInfo, QString &errorMessage) +FileApiData FileApiParser::parseData(const QFileInfo &replyFileInfo, const QString &cmakeBuildType, + QString &errorMessage) { QTC_CHECK(errorMessage.isEmpty()); const QDir replyDir = replyFileInfo.dir(); @@ -882,9 +882,24 @@ FileApiData FileApiParser::parseData(const QFileInfo &replyFileInfo, QString &er result.cache = readCacheFile(result.replyFile.jsonFile("cache", replyDir), errorMessage); result.cmakeFiles = readCMakeFilesFile(result.replyFile.jsonFile("cmakeFiles", replyDir), errorMessage); - result.codemodel = readCodemodelFile(result.replyFile.jsonFile("codemodel", replyDir), + auto codeModels = readCodemodelFile(result.replyFile.jsonFile("codemodel", replyDir), errorMessage); + if (codeModels.size() == 0) { + errorMessage = "No CMake configuration found!"; + qWarning() << errorMessage; + return result; + } + + auto it = std::find_if(codeModels.cbegin(), codeModels.cend(), + [cmakeBuildType](const Configuration& cfg) { return cfg.name == cmakeBuildType; }); + if (it == codeModels.cend()) { + errorMessage = QString("No '%1' CMake configuration found!").arg(cmakeBuildType); + qWarning() << errorMessage; + return result; + } + result.codemodel = std::move(*it); + const QStringList targetFiles = uniqueTargetFiles(result.codemodel); for (const QString &targetFile : targetFiles) { diff --git a/src/plugins/cmakeprojectmanager/fileapiparser.h b/src/plugins/cmakeprojectmanager/fileapiparser.h index b277b0c3a41..b790251db66 100644 --- a/src/plugins/cmakeprojectmanager/fileapiparser.h +++ b/src/plugins/cmakeprojectmanager/fileapiparser.h @@ -56,6 +56,7 @@ class ReplyFileContents { public: QString generator; + bool isMultiConfig = false; QString cmakeExecutable; QString ctestExecutable; QString cmakeRoot; @@ -238,14 +239,15 @@ public: FileApiDetails::ReplyFileContents replyFile; CMakeConfig cache; std::vector cmakeFiles; - std::vector codemodel; + FileApiDetails::Configuration codemodel; std::vector targetDetails; }; class FileApiParser { public: - static FileApiData parseData(const QFileInfo &replyFileInfo, QString &errorMessage); + static FileApiData parseData(const QFileInfo &replyFileInfo, const QString& cmakeBuildType, + QString &errorMessage); static bool setupCMakeFileApi(const Utils::FilePath &buildDirectory, Utils::FileSystemWatcher &watcher); diff --git a/src/plugins/cmakeprojectmanager/fileapireader.cpp b/src/plugins/cmakeprojectmanager/fileapireader.cpp index 56c9806a112..71fa5601a17 100644 --- a/src/plugins/cmakeprojectmanager/fileapireader.cpp +++ b/src/plugins/cmakeprojectmanager/fileapireader.cpp @@ -198,6 +198,16 @@ QString FileApiReader::ctestPath() const return m_lastCMakeExitCode == 0 ? m_ctestPath : QString(); } +bool FileApiReader::isMultiConfig() const +{ + return m_isMultiConfig; +} + +bool FileApiReader::usesAllCapsTargets() const +{ + return m_usesAllCapsTargets; +} + std::unique_ptr FileApiReader::generateProjectTree( const QList &allFiles, QString &errorMessage, bool includeHeaderNodes) { @@ -240,13 +250,14 @@ void FileApiReader::endState(const QFileInfo &replyFi) const FilePath sourceDirectory = m_parameters.sourceDirectory; const FilePath buildDirectory = m_parameters.workDirectory; const FilePath topCmakeFile = m_cmakeFiles.size() == 1 ? *m_cmakeFiles.begin() : FilePath{}; + const QString cmakeBuildType = m_parameters.cmakeBuildType; m_lastReplyTimestamp = replyFi.lastModified(); m_future = runAsync(ProjectExplorerPlugin::sharedThreadPool(), - [replyFi, sourceDirectory, buildDirectory, topCmakeFile]() { + [replyFi, sourceDirectory, buildDirectory, topCmakeFile, cmakeBuildType]() { auto result = std::make_unique(); - FileApiData data = FileApiParser::parseData(replyFi, result->errorMessage); + FileApiData data = FileApiParser::parseData(replyFi, cmakeBuildType, result->errorMessage); if (!result->errorMessage.isEmpty()) { qWarning() << result->errorMessage; *result = generateFallbackData(topCmakeFile, @@ -274,6 +285,8 @@ void FileApiReader::endState(const QFileInfo &replyFi) m_rootProjectNode = std::move(value->rootProjectNode); m_knownHeaders = std::move(value->knownHeaders); m_ctestPath = std::move(value->ctestPath); + m_isMultiConfig = std::move(value->isMultiConfig); + m_usesAllCapsTargets = std::move(value->usesAllCapsTargets); if (value->errorMessage.isEmpty()) { emit this->dataAvailable(); diff --git a/src/plugins/cmakeprojectmanager/fileapireader.h b/src/plugins/cmakeprojectmanager/fileapireader.h index 97bd6232699..ef210ea2a73 100644 --- a/src/plugins/cmakeprojectmanager/fileapireader.h +++ b/src/plugins/cmakeprojectmanager/fileapireader.h @@ -73,6 +73,9 @@ public: bool includeHeaderNodes); ProjectExplorer::RawProjectParts createRawProjectParts(QString &errorMessage); + bool isMultiConfig() const; + bool usesAllCapsTargets() const; + signals: void configurationStarted() const; void dataAvailable() const; @@ -97,6 +100,8 @@ private: std::unique_ptr m_rootProjectNode; QSet m_knownHeaders; QString m_ctestPath; + bool m_isMultiConfig = false; + bool m_usesAllCapsTargets = false; int m_lastCMakeExitCode = 0; Utils::optional> m_future;