forked from qt-creator/qt-creator
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:
@@ -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.
|
||||
|
||||
@@ -54,6 +54,7 @@ public:
|
||||
Utils::FilePath sourceDirectory;
|
||||
Utils::FilePath buildDirectory;
|
||||
Utils::FilePath workDirectory; // either buildDirectory or a QTemporaryDirectory!
|
||||
QString cmakeBuildType;
|
||||
|
||||
Utils::Environment environment;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user