CMakeProjectManager: Support for multi-config generators

CMake has multi-config generators like:

  * Visual Studio
  * Xcode
  * Ninja Multi-Config

The first two have different special targets for "all", "install",
"package", "test" namely: "ALL_BUILD", "INSTALL", "PACKAGE",
"RUN_TESTS".

All of them need to get the build type passed via "--config <build-
type>" and not via "CMAKE_BUILD_TYPE".

The multi-config generators will use only one build directory.

Fixes: QTCREATORBUG-24984
Change-Id: I8aa7ff73ce2af1e163b21a6504d26fcf95530edf
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Cristian Adam
2021-01-14 16:38:55 +01:00
parent f3d7717b31
commit 6cbdae8070
22 changed files with 238 additions and 140 deletions

View File

@@ -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.

View File

@@ -54,6 +54,7 @@ public:
Utils::FilePath sourceDirectory;
Utils::FilePath buildDirectory;
Utils::FilePath workDirectory; // either buildDirectory or a QTemporaryDirectory!
QString cmakeBuildType;
Utils::Environment environment;

View File

@@ -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<SourceDirectoryAspect>();
addAspect<BuildTypeAspect>();
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<QtSupport::QmlDebuggingAspect>();
@@ -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<SourceDirectoryAspect>()->value());
}
QString CMakeBuildConfiguration::cmakeBuildType() const
{
return aspect<BuildTypeAspect>()->value();
}
void CMakeBuildConfiguration::setCMakeBuildType(const QString &cmakeBuildType)
{
aspect<BuildTypeAspect>()->setValue(cmakeBuildType);
}
// ----------------------------------------------------------------------
// - InitialCMakeParametersAspect:
// ----------------------------------------------------------------------
@@ -523,5 +544,13 @@ SourceDirectoryAspect::SourceDirectoryAspect()
setSettingsKey("CMake.Source.Directory");
}
// -----------------------------------------------------------------------------
// BuildTypeAspect:
// -----------------------------------------------------------------------------
BuildTypeAspect::BuildTypeAspect()
{
setSettingsKey("CMake.Build.Type");
}
} // namespace Internal
} // namespace CMakeProjectManager

View File

@@ -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

View File

@@ -390,6 +390,13 @@ CommandLine CMakeBuildStep::cmakeCommand() const
return s;
}));
auto bs = qobject_cast<CMakeBuildSystem*>(buildSystem());
auto bc = qobject_cast<CMakeBuildConfiguration*>(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<CMakeBuildSystem *>(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();
}

View File

@@ -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<Utils::TreeItem, CMakeTargetItem> m_buildTargetModel;
};

View File

@@ -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<TestCaseInfo> CMakeBuildSystem::testcasesInfo() const
{
return m_testNames;

View File

@@ -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 {

View File

@@ -449,8 +449,8 @@ private:
generatorCombo->setCurrentText(CMakeGeneratorKitAspect::generator(kit()));
extraGeneratorCombo->setCurrentText(CMakeGeneratorKitAspect::extraGenerator(kit()));
platformEdit->setText(platformEdit->isEnabled() ? CMakeGeneratorKitAspect::platform(kit()) : QLatin1String("<unsupported>"));
toolsetEdit->setText(toolsetEdit->isEnabled() ? CMakeGeneratorKitAspect::toolset(kit()) : QLatin1String("<unsupported>"));
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());

View File

@@ -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;

View File

@@ -71,7 +71,7 @@ void CMakeTargetLocatorFilter::prepareSearch(const QString &entry)
const QList<CMakeBuildTarget> 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) {

View File

@@ -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<CMakeBuildSystem*>(target->buildSystem());
auto bc = qobject_cast<CMakeBuildConfiguration*>(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;
}

View File

@@ -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

View File

@@ -287,43 +287,57 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
qCDebug(cmInputLog) << "Failed to read configuration from" << cacheFile << errorMessage;
return { };
}
auto data = std::make_unique<DirectoryData>();
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<void *> result;
for (auto const &buildType: qAsConst(buildConfigurationTypes)) {
auto data = std::make_unique<DirectoryData>();
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<void *>(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<void *>(data.release()));
}
return result;
}
bool CMakeProjectImporter::matchKit(void *directoryData, const Kit *k) const

View File

@@ -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:<br><b>\"%2\"</b>")
.arg(QDir::toNativeSeparators(replyFile))
.arg(errorMessage);
QMessageBox::information(Core::ICore::mainWindow(), tr("Parsing Result"), message);
}
void CMakeManager::buildFile(Node *node)
{
if (!node) {

View File

@@ -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;
};

View File

@@ -102,22 +102,6 @@ CMakeFileResult extractCMakeFilesData(const std::vector<FileApiDetails::CMakeFil
return result;
}
Configuration extractConfiguration(std::vector<Configuration> &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;
}

View File

@@ -55,6 +55,8 @@ public:
std::unique_ptr<CMakeProjectNode> rootProjectNode;
QSet<Utils::FilePath> knownHeaders;
QString ctestPath;
bool isMultiConfig = false;
bool usesAllCapsTargets = false;
};
FileApiQtcData extractData(FileApiData &data,

View File

@@ -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<Configuration> &configs)
static QStringList uniqueTargetFiles(const Configuration &config)
{
QSet<QString> 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) {

View File

@@ -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<FileApiDetails::CMakeFileInfo> cmakeFiles;
std::vector<FileApiDetails::Configuration> codemodel;
FileApiDetails::Configuration codemodel;
std::vector<FileApiDetails::TargetDetails> 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);

View File

@@ -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<CMakeProjectNode> FileApiReader::generateProjectTree(
const QList<const FileNode *> &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<FileApiQtcData>();
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();

View File

@@ -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<CMakeProjectNode> m_rootProjectNode;
QSet<Utils::FilePath> m_knownHeaders;
QString m_ctestPath;
bool m_isMultiConfig = false;
bool m_usesAllCapsTargets = false;
int m_lastCMakeExitCode = 0;
Utils::optional<QFuture<FileApiQtcData *>> m_future;