CMakePM: Add support for configure CMake presets

This patchset will add support for version 1 of the CMakePresets
feature that has been implemented in CMake 3.19

https://cmake.org/cmake/help/v3.19/manual/cmake-presets.7.html

The tests/manual/cmakepresets contains a manual test example for this
feature.

Task-number: QTCREATORBUG-24555
Change-Id: I93aba1ab4f090613d0b21d970b5b651d12c922af
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
Cristian Adam
2022-05-30 19:04:54 +02:00
parent 4726b0da2d
commit 2ab1e76ca9
19 changed files with 1255 additions and 12 deletions

View File

@@ -35,5 +35,7 @@ add_qtc_plugin(CMakeProjectManager
fileapidataextractor.cpp fileapidataextractor.h fileapidataextractor.cpp fileapidataextractor.h
fileapiparser.cpp fileapiparser.h fileapiparser.cpp fileapiparser.h
fileapireader.cpp fileapireader.h fileapireader.cpp fileapireader.h
presetsparser.cpp presetsparser.h
presetsmacros.cpp presetsmacros.h
projecttreehelper.cpp projecttreehelper.h projecttreehelper.cpp projecttreehelper.h
) )

View File

@@ -8,12 +8,15 @@
#include "cmakebuildsystem.h" #include "cmakebuildsystem.h"
#include "cmakeconfigitem.h" #include "cmakeconfigitem.h"
#include "cmakekitinformation.h" #include "cmakekitinformation.h"
#include "cmakeproject.h"
#include "cmakeprojectconstants.h" #include "cmakeprojectconstants.h"
#include "cmakeprojectplugin.h" #include "cmakeprojectplugin.h"
#include "cmakespecificsettings.h" #include "cmakespecificsettings.h"
#include "configmodel.h" #include "configmodel.h"
#include "configmodelitemdelegate.h" #include "configmodelitemdelegate.h"
#include "fileapiparser.h" #include "fileapiparser.h"
#include "presetsmacros.h"
#include "presetsparser.h"
#include <android/androidconstants.h> #include <android/androidconstants.h>
#include <docker/dockerconstants.h> #include <docker/dockerconstants.h>
@@ -1163,6 +1166,164 @@ static CommandLine defaultInitialCMakeCommand(const Kit *k, const QString buildT
return cmd; return cmd;
} }
static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArguments,
const CMakeProject *project,
const Kit *k,
const Utils::Environment &env)
{
const CMakeConfigItem presetItem = CMakeConfigurationKitAspect::cmakePresetConfigItem(k);
if (presetItem.isNull())
return;
// Remove the -DQTC_CMAKE_PRESET argument, which is only used as a kit marker
const QString presetArgument = presetItem.toArgument();
const QString presetName = presetItem.expandedValue(k);
initialArguments.removeIf(
[presetArgument](const QString &item) { return item == presetArgument; });
PresetsDetails::ConfigurePreset configurePreset
= Utils::findOrDefault(project->presetsData().configurePresets,
[presetName](const PresetsDetails::ConfigurePreset &preset) {
return preset.name == presetName;
});
// Add the command line arguments
if (configurePreset.warnings) {
if (configurePreset.warnings.value().dev) {
bool value = configurePreset.warnings.value().dev.value();
initialArguments.append(value ? QString("-Wdev") : QString("-Wno-dev"));
}
if (configurePreset.warnings.value().deprecated) {
bool value = configurePreset.warnings.value().deprecated.value();
initialArguments.append(value ? QString("-Wdeprecated") : QString("-Wno-deprecated"));
}
if (configurePreset.warnings.value().uninitialized
&& configurePreset.warnings.value().uninitialized.value())
initialArguments.append("--warn-uninitialized");
if (configurePreset.warnings.value().unusedCli
&& !configurePreset.warnings.value().unusedCli.value())
initialArguments.append(" --no-warn-unused-cli");
if (configurePreset.warnings.value().systemVars
&& configurePreset.warnings.value().systemVars.value())
initialArguments.append("--check-system-vars");
}
if (configurePreset.errors) {
if (configurePreset.errors.value().dev) {
bool value = configurePreset.errors.value().dev.value();
initialArguments.append(value ? QString("-Werror=dev") : QString("-Wno-error=dev"));
}
if (configurePreset.errors.value().deprecated) {
bool value = configurePreset.errors.value().deprecated.value();
initialArguments.append(value ? QString("-Werror=deprecated")
: QString("-Wno-error=deprecated"));
}
}
if (configurePreset.debug) {
if (configurePreset.debug.value().find && configurePreset.debug.value().find.value())
initialArguments.append("--debug-find");
if (configurePreset.debug.value().tryCompile
&& configurePreset.debug.value().tryCompile.value())
initialArguments.append("--debug-trycompile");
if (configurePreset.debug.value().output && configurePreset.debug.value().output.value())
initialArguments.append("--debug-output");
}
// Merge the presets cache variables
CMakeConfig cache;
if (configurePreset.cacheVariables)
cache = configurePreset.cacheVariables.value();
for (const CMakeConfigItem &presetItemRaw : cache) {
// Expand the CMakePresets Macros
CMakeConfigItem presetItem(presetItemRaw);
QString presetItemValue = QString::fromUtf8(presetItem.value);
CMakePresets::Macros::expand(configurePreset, env, project->projectDirectory(), presetItemValue);
presetItem.value = presetItemValue.toUtf8();
const QString presetItemArg = presetItem.toArgument();
const QString presetItemArgNoType = presetItemArg.left(presetItemArg.indexOf(":"));
auto it = std::find_if(initialArguments.begin(),
initialArguments.end(),
[presetItemArgNoType](const QString &arg) {
return arg.startsWith(presetItemArgNoType);
});
if (it != initialArguments.end()) {
QString &arg = *it;
CMakeConfigItem argItem = CMakeConfigItem::fromString(arg.mid(2)); // skip -D
// For multi value path variables append the non Qt path
if (argItem.key == "CMAKE_PREFIX_PATH" || argItem.key == "CMAKE_FIND_ROOT_PATH") {
QStringList presetValueList = presetItem.expandedValue(k).split(";");
// Remove the expanded Qt path from the presets values
QString argItemExpandedValue = argItem.expandedValue(k);
presetValueList.removeIf([argItemExpandedValue](const QString &presetPath) {
QStringList argItemPaths = argItemExpandedValue.split(";");
for (const QString &argPath : argItemPaths) {
const FilePath argFilePath = FilePath::fromString(argPath);
const FilePath presetFilePath = FilePath::fromString(presetPath);
if (argFilePath == presetFilePath)
return true;
}
return false;
});
// Add the presets values to the final argument
for (const QString &presetPath : presetValueList) {
argItem.value.append(";");
argItem.value.append(presetPath.toUtf8());
}
arg = argItem.toArgument();
} else if (argItem.key == "CMAKE_C_COMPILER" || argItem.key == "CMAKE_CXX_COMPILER"
|| argItem.key == "QT_QMAKE_EXECUTABLE" || argItem.key == "QT_HOST_PATH"
|| argItem.key == "CMAKE_PROJECT_INCLUDE_BEFORE"
|| argItem.key == "CMAKE_TOOLCHAIN_FILE") {
const FilePath argFilePath = FilePath::fromString(argItem.expandedValue(k));
const FilePath presetFilePath = FilePath::fromUtf8(presetItem.value);
if (argFilePath != presetFilePath)
arg = presetItem.toArgument();
} else if (argItem.expandedValue(k) != QString::fromUtf8(presetItem.value)) {
arg = presetItem.toArgument();
}
} else {
initialArguments.append(presetItem.toArgument());
}
}
}
static Utils::EnvironmentItems getEnvironmentItemsFromCMakePreset(const CMakeProject *project,
const Kit *k)
{
Utils::EnvironmentItems envItems;
const CMakeConfigItem presetItem = CMakeConfigurationKitAspect::cmakePresetConfigItem(k);
if (presetItem.isNull())
return envItems;
const QString presetName = presetItem.expandedValue(k);
PresetsDetails::ConfigurePreset configurePreset
= Utils::findOrDefault(project->presetsData().configurePresets,
[presetName](const PresetsDetails::ConfigurePreset &preset) {
return preset.name == presetName;
});
CMakePresets::Macros::expand(configurePreset, envItems, project->projectDirectory());
return envItems;
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// CMakeBuildConfigurationPrivate: // CMakeBuildConfigurationPrivate:
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -1365,7 +1526,15 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id)
if (qt && qt->isQmlDebuggingSupported()) if (qt && qt->isQmlDebuggingSupported())
cmd.addArg("-DCMAKE_CXX_FLAGS_INIT:STRING=%{" + QLatin1String(QT_QML_DEBUG_FLAG) + "}"); cmd.addArg("-DCMAKE_CXX_FLAGS_INIT:STRING=%{" + QLatin1String(QT_QML_DEBUG_FLAG) + "}");
m_buildSystem->setInitialCMakeArguments(cmd.splitArguments()); CMakeProject *cmakeProject = static_cast<CMakeProject *>(target->project());
setUserConfigureEnvironmentChanges(getEnvironmentItemsFromCMakePreset(cmakeProject, k));
QStringList initialCMakeArguments = cmd.splitArguments();
addCMakeConfigurePresetToInitialArguments(initialCMakeArguments,
cmakeProject,
k,
configureEnvironment());
m_buildSystem->setInitialCMakeArguments(initialCMakeArguments);
m_buildSystem->setCMakeBuildType(buildType); m_buildSystem->setCMakeBuildType(buildType);
updateAndEmitConfigureEnvironmentChanged(); updateAndEmitConfigureEnvironmentChanged();
}); });

View File

@@ -1,6 +1,7 @@
// Copyright (C) 2016 Canonical Ltd. // Copyright (C) 2016 Canonical Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "cmakeconfigitem.h"
#include "cmakekitinformation.h" #include "cmakekitinformation.h"
#include "cmakeprojectconstants.h" #include "cmakeprojectconstants.h"
@@ -875,6 +876,7 @@ const char CMAKE_C_TOOLCHAIN_KEY[] = "CMAKE_C_COMPILER";
const char CMAKE_CXX_TOOLCHAIN_KEY[] = "CMAKE_CXX_COMPILER"; const char CMAKE_CXX_TOOLCHAIN_KEY[] = "CMAKE_CXX_COMPILER";
const char CMAKE_QMAKE_KEY[] = "QT_QMAKE_EXECUTABLE"; const char CMAKE_QMAKE_KEY[] = "QT_QMAKE_EXECUTABLE";
const char CMAKE_PREFIX_PATH_KEY[] = "CMAKE_PREFIX_PATH"; const char CMAKE_PREFIX_PATH_KEY[] = "CMAKE_PREFIX_PATH";
const char QTC_CMAKE_PRESET_KEY[] = "QTC_CMAKE_PRESET";
class CMakeConfigurationKitAspectWidget final : public KitAspectWidget class CMakeConfigurationKitAspectWidget final : public KitAspectWidget
{ {
@@ -1120,6 +1122,23 @@ CMakeConfig CMakeConfigurationKitAspect::defaultConfiguration(const Kit *k)
return config; return config;
} }
void CMakeConfigurationKitAspect::setCMakePreset(Kit *k, const QString &presetName)
{
CMakeConfig config = configuration(k);
config.prepend(
CMakeConfigItem(QTC_CMAKE_PRESET_KEY, CMakeConfigItem::INTERNAL, presetName.toUtf8()));
setConfiguration(k, config);
}
CMakeConfigItem CMakeConfigurationKitAspect::cmakePresetConfigItem(const ProjectExplorer::Kit *k)
{
const CMakeConfig config = configuration(k);
return Utils::findOrDefault(config, [](const CMakeConfigItem &item) {
return item.key == QTC_CMAKE_PRESET_KEY;
});
}
QVariant CMakeConfigurationKitAspect::defaultValue(const Kit *k) const QVariant CMakeConfigurationKitAspect::defaultValue(const Kit *k) const
{ {
// FIXME: Convert preload scripts // FIXME: Convert preload scripts

View File

@@ -90,6 +90,9 @@ public:
static CMakeConfig defaultConfiguration(const ProjectExplorer::Kit *k); static CMakeConfig defaultConfiguration(const ProjectExplorer::Kit *k);
static void setCMakePreset(ProjectExplorer::Kit *k, const QString &presetName);
static CMakeConfigItem cmakePresetConfigItem(const ProjectExplorer::Kit *k);
// KitAspect interface // KitAspect interface
ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *k) const final; ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *k) const final;
void setup(ProjectExplorer::Kit *k) final; void setup(ProjectExplorer::Kit *k) final;

View File

@@ -15,6 +15,7 @@
#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectnodes.h> #include <projectexplorer/projectnodes.h>
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
using namespace ProjectExplorer; using namespace ProjectExplorer;
using namespace Utils; using namespace Utils;
@@ -34,6 +35,8 @@ CMakeProject::CMakeProject(const FilePath &fileName)
setDisplayName(projectDirectory().fileName()); setDisplayName(projectDirectory().fileName());
setCanBuildProducts(); setCanBuildProducts();
setHasMakeInstallEquivalent(true); setHasMakeInstallEquivalent(true);
readPresets();
} }
CMakeProject::~CMakeProject() CMakeProject::~CMakeProject()
@@ -59,7 +62,7 @@ Tasks CMakeProject::projectIssues(const Kit *k) const
ProjectImporter *CMakeProject::projectImporter() const ProjectImporter *CMakeProject::projectImporter() const
{ {
if (!m_projectImporter) if (!m_projectImporter)
m_projectImporter = new CMakeProjectImporter(projectFilePath()); m_projectImporter = new CMakeProjectImporter(projectFilePath(), m_presetsData);
return m_projectImporter; return m_projectImporter;
} }
@@ -73,6 +76,100 @@ void CMakeProject::clearIssues()
m_issues.clear(); m_issues.clear();
} }
PresetsData CMakeProject::presetsData() const
{
return m_presetsData;
}
Internal::PresetsData CMakeProject::combinePresets(Internal::PresetsData &cmakePresetsData,
Internal::PresetsData &cmakeUserPresetsData)
{
Internal::PresetsData result;
result.version = cmakePresetsData.version;
result.cmakeMinimimRequired = cmakePresetsData.cmakeMinimimRequired;
QHash<QString, PresetsDetails::ConfigurePreset> configurePresets;
// Populate the hash map with the CMakePresets
for (const PresetsDetails::ConfigurePreset &p: cmakePresetsData.configurePresets)
configurePresets.insert(p.name, p);
auto resolveInherits =
[configurePresets](std::vector<PresetsDetails::ConfigurePreset> &configurePresetsList) {
for (PresetsDetails::ConfigurePreset &cp : configurePresetsList) {
if (!cp.inherits)
continue;
for (const QString &inheritFromName : cp.inherits.value())
if (configurePresets.contains(inheritFromName))
cp.inheritFrom(configurePresets[inheritFromName]);
}
};
// First resolve the CMakePresets
resolveInherits(cmakePresetsData.configurePresets);
// Add the CMakeUserPresets to the resolve hash map
for (const PresetsDetails::ConfigurePreset &cp : cmakeUserPresetsData.configurePresets) {
if (configurePresets.contains(cp.name)) {
TaskHub::addTask(BuildSystemTask(
Task::TaskType::Error,
tr("CMakeUserPresets.json cannot re-define the configure preset: %1").arg(cp.name),
"CMakeUserPresets.json"));
TaskHub::requestPopup();
} else {
configurePresets.insert(cp.name, cp);
}
}
// Then resolve the CMakeUserPresets
resolveInherits(cmakeUserPresetsData.configurePresets);
// Get both CMakePresets and CMakeUserPresets into the result
result.configurePresets = cmakePresetsData.configurePresets;
// std::vector doesn't have append
std::copy(cmakeUserPresetsData.configurePresets.begin(),
cmakeUserPresetsData.configurePresets.end(),
std::back_inserter(result.configurePresets));
return result;
}
void CMakeProject::readPresets()
{
auto parsePreset = [](const Utils::FilePath &presetFile) -> Internal::PresetsData {
Internal::PresetsData data;
Internal::PresetsParser parser;
QString errorMessage;
int errorLine = -1;
if (presetFile.exists()) {
if (parser.parse(presetFile, errorMessage, errorLine)) {
data = parser.presetsData();
} else {
TaskHub::addTask(BuildSystemTask(Task::TaskType::Error,
tr("Failed to load %1: %2")
.arg(presetFile.fileName())
.arg(errorMessage),
presetFile,
errorLine));
TaskHub::requestPopup();
}
}
return data;
};
const Utils::FilePath cmakePresetsJson = projectDirectory().pathAppended("CMakePresets.json");
const Utils::FilePath cmakeUserPresetsJson = projectDirectory().pathAppended("CMakeUserPresets.json");
Internal::PresetsData cmakePresetsData = parsePreset(cmakePresetsJson);
Internal::PresetsData cmakeUserPresetsData = parsePreset(cmakeUserPresetsJson);
m_presetsData = combinePresets(cmakePresetsData, cmakeUserPresetsData);
}
bool CMakeProject::setupTarget(Target *t) bool CMakeProject::setupTarget(Target *t)
{ {
t->updateDefaultBuildConfigurations(); t->updateDefaultBuildConfigurations();

View File

@@ -4,6 +4,7 @@
#pragma once #pragma once
#include "cmake_global.h" #include "cmake_global.h"
#include "presetsparser.h"
#include <projectexplorer/project.h> #include <projectexplorer/project.h>
@@ -27,15 +28,21 @@ public:
void addIssue(IssueType type, const QString &text); void addIssue(IssueType type, const QString &text);
void clearIssues(); void clearIssues();
Internal::PresetsData presetsData() const;
void readPresets();
protected: protected:
bool setupTarget(ProjectExplorer::Target *t) final; bool setupTarget(ProjectExplorer::Target *t) final;
private: private:
ProjectExplorer::DeploymentKnowledge deploymentKnowledge() const override; ProjectExplorer::DeploymentKnowledge deploymentKnowledge() const override;
Internal::PresetsData combinePresets(Internal::PresetsData &cmakePresetsData,
Internal::PresetsData &cmakeUserPresetsData);
mutable Internal::CMakeProjectImporter *m_projectImporter = nullptr; mutable Internal::CMakeProjectImporter *m_projectImporter = nullptr;
ProjectExplorer::Tasks m_issues; ProjectExplorer::Tasks m_issues;
Internal::PresetsData m_presetsData;
}; };
} // namespace CMakeProjectManager } // namespace CMakeProjectManager

View File

@@ -7,6 +7,7 @@
#include "cmakekitinformation.h" #include "cmakekitinformation.h"
#include "cmakeprojectconstants.h" #include "cmakeprojectconstants.h"
#include "cmaketoolmanager.h" #include "cmaketoolmanager.h"
#include "presetsmacros.h"
#include <coreplugin/messagemanager.h> #include <coreplugin/messagemanager.h>
@@ -23,6 +24,7 @@
#include <utils/stringutils.h> #include <utils/stringutils.h>
#include <utils/temporarydirectory.h> #include <utils/temporarydirectory.h>
#include <QApplication>
#include <QDir> #include <QDir>
#include <QLoggingCategory> #include <QLoggingCategory>
@@ -43,6 +45,9 @@ struct DirectoryData
FilePath cmakeHomeDirectory; FilePath cmakeHomeDirectory;
bool hasQmlDebugging = false; bool hasQmlDebugging = false;
QString cmakePresetDisplayname;
QString cmakePreset;
// Kit Stuff // Kit Stuff
FilePath cmakeBinary; FilePath cmakeBinary;
QString generator; QString generator;
@@ -88,7 +93,10 @@ static QString uniqueCMakeToolDisplayName(CMakeTool &tool)
// CMakeProjectImporter // CMakeProjectImporter
CMakeProjectImporter::CMakeProjectImporter(const FilePath &path) : QtProjectImporter(path) CMakeProjectImporter::CMakeProjectImporter(const FilePath &path, const PresetsData &presetsData)
: QtProjectImporter(path)
, m_presetsData(presetsData)
, m_presetsTempDir("qtc-cmake-presets-XXXXXXXX")
{ {
useTemporaryKitAspect(CMakeKitAspect::id(), useTemporaryKitAspect(CMakeKitAspect::id(),
[this](Kit *k, const QVariantList &vl) { cleanupTemporaryCMake(k, vl); }, [this](Kit *k, const QVariantList &vl) { cleanupTemporaryCMake(k, vl); },
@@ -111,11 +119,102 @@ QStringList CMakeProjectImporter::importCandidates()
BuildConfiguration::Unknown); BuildConfiguration::Unknown);
candidates << scanDirectory(shadowBuildDirectory.absolutePath(), QString()); candidates << scanDirectory(shadowBuildDirectory.absolutePath(), QString());
} }
for (const auto &configPreset : m_presetsData.configurePresets) {
if (configPreset.hidden.value())
continue;
const QString presetDirName = m_presetsTempDir.filePath(configPreset.name).toString();
const QDir presetDir;
presetDir.mkpath(presetDirName);
candidates << presetDirName;
}
const QStringList finalists = Utils::filteredUnique(candidates); const QStringList finalists = Utils::filteredUnique(candidates);
qCInfo(cmInputLog) << "import candidates:" << finalists; qCInfo(cmInputLog) << "import candidates:" << finalists;
return finalists; return finalists;
} }
static CMakeConfig configurationFromPresetProbe(
const FilePath &importPath, const PresetsDetails::ConfigurePreset &configurePreset)
{
QFile cmakeListTxt(importPath.pathAppended("CMakeLists.txt").toString());
if (!cmakeListTxt.open(QIODevice::WriteOnly)) {
return {};
}
cmakeListTxt.write(QByteArray("cmake_minimum_required(VERSION 3.15)\n"
"\n"
"project(preset-probe)\n"
"\n"));
cmakeListTxt.close();
QtcProcess cmake;
cmake.setTimeoutS(30);
cmake.setDisableUnixTerminal();
Environment env = Environment::systemEnvironment();
CMakePresets::Macros::expand(configurePreset, env, importPath);
env.setupEnglishOutput();
cmake.setEnvironment(env);
cmake.setTimeOutMessageBoxEnabled(false);
const FilePath cmakeExecutable = FilePath::fromString(configurePreset.cmakeExecutable.value());
QStringList args;
args.emplace_back("-S");
args.emplace_back(importPath.path());
args.emplace_back("-B");
args.emplace_back(importPath.pathAppended("build/").path());
if (configurePreset.generator) {
args.emplace_back("-G");
args.emplace_back(configurePreset.generator.value());
}
if (configurePreset.cacheVariables) {
const CMakeConfig cache = configurePreset.cacheVariables
? configurePreset.cacheVariables.value()
: CMakeConfig();
const FilePath cmakeMakeProgram = cache.filePathValueOf(QByteArray("CMAKE_MAKE_PROGRAM"));
const FilePath toolchainFile = cache.filePathValueOf(QByteArray("CMAKE_TOOLCHAIN_FILE"));
const QString prefixPath = cache.stringValueOf(QByteArray("CMAKE_PREFIX_PATH"));
const QString findRootPath = cache.stringValueOf(QByteArray("CMAKE_FIND_ROOT_PATH"));
const QString qtHostPath = cache.stringValueOf(QByteArray("QT_HOST_PATH"));
if (!cmakeMakeProgram.isEmpty()) {
args.emplace_back(
QStringLiteral("-DCMAKE_MAKE_PROGRAM=%1").arg(cmakeMakeProgram.toString()));
}
if (!toolchainFile.isEmpty()) {
args.emplace_back(
QStringLiteral("-DCMAKE_TOOLCHAIN_FILE=%1").arg(toolchainFile.toString()));
}
if (!prefixPath.isEmpty()) {
args.emplace_back(QStringLiteral("-DCMAKE_PREFIX_PATH=%1").arg(prefixPath));
}
if (!findRootPath.isEmpty()) {
args.emplace_back(QStringLiteral("-DCMAKE_FIND_ROOT_PATH=%1").arg(findRootPath));
}
if (!qtHostPath.isEmpty()) {
args.emplace_back(QStringLiteral("-DQT_HOST_PATH=%1").arg(qtHostPath));
}
}
qCDebug(cmInputLog) << "CMake probing for compilers: " << cmakeExecutable.toUserOutput()
<< args;
cmake.setCommand({cmakeExecutable, args});
cmake.runBlocking();
QString errorMessage;
const CMakeConfig config = CMakeConfig::fromFile(importPath.pathAppended(
"build/CMakeCache.txt"),
&errorMessage);
return config;
}
static FilePath qmakeFromCMakeCache(const CMakeConfig &config) static FilePath qmakeFromCMakeCache(const CMakeConfig &config)
{ {
// Qt4 way to define things (more convenient for us, so try this first;-) // Qt4 way to define things (more convenient for us, so try this first;-)
@@ -134,10 +233,15 @@ static FilePath qmakeFromCMakeCache(const CMakeConfig &config)
qCDebug(cmInputLog) << "QtXCore_DIR=" << qtCMakeDir.toUserOutput(); qCDebug(cmInputLog) << "QtXCore_DIR=" << qtCMakeDir.toUserOutput();
const FilePath canQtCMakeDir = FilePath::fromString(qtCMakeDir.toFileInfo().canonicalFilePath()); const FilePath canQtCMakeDir = FilePath::fromString(qtCMakeDir.toFileInfo().canonicalFilePath());
qCInfo(cmInputLog) << "QtXCore_DIR (canonical)=" << canQtCMakeDir.toUserOutput(); qCInfo(cmInputLog) << "QtXCore_DIR (canonical)=" << canQtCMakeDir.toUserOutput();
if (qtCMakeDir.isEmpty()) QString prefixPath;
if (!qtCMakeDir.isEmpty()) {
prefixPath = canQtCMakeDir.parentDir().parentDir().parentDir().toString(); // Up 3 levels...
} else {
prefixPath = config.stringValueOf("CMAKE_PREFIX_PATH");
}
qCDebug(cmInputLog) << "PrefixPath:" << prefixPath;
if (prefixPath.isEmpty())
return FilePath(); return FilePath();
const FilePath baseQtDir = canQtCMakeDir.parentDir().parentDir().parentDir(); // Up 3 levels...
qCDebug(cmInputLog) << "BaseQtDir:" << baseQtDir.toUserOutput();
// Run a CMake project that would do qmake probing // Run a CMake project that would do qmake probing
TemporaryDirectory qtcQMakeProbeDir("qtc-cmake-qmake-probe-XXXXXXXX"); TemporaryDirectory qtcQMakeProbeDir("qtc-cmake-qmake-probe-XXXXXXXX");
@@ -204,9 +308,9 @@ static FilePath qmakeFromCMakeCache(const CMakeConfig &config)
args.push_back(QStringLiteral("-DCMAKE_MAKE_PROGRAM=%1").arg(cmakeMakeProgram.toString())); args.push_back(QStringLiteral("-DCMAKE_MAKE_PROGRAM=%1").arg(cmakeMakeProgram.toString()));
} }
if (toolchainFile.isEmpty()) { if (toolchainFile.isEmpty()) {
args.push_back(QStringLiteral("-DCMAKE_PREFIX_PATH=%1").arg(baseQtDir.toString())); args.push_back(QStringLiteral("-DCMAKE_PREFIX_PATH=%1").arg(prefixPath));
} else { } else {
args.push_back(QStringLiteral("-DCMAKE_FIND_ROOT_PATH=%1").arg(baseQtDir.toString())); args.push_back(QStringLiteral("-DCMAKE_FIND_ROOT_PATH=%1").arg(prefixPath));
args.push_back(QStringLiteral("-DCMAKE_TOOLCHAIN_FILE=%1").arg(toolchainFile.toString())); args.push_back(QStringLiteral("-DCMAKE_TOOLCHAIN_FILE=%1").arg(toolchainFile.toString()));
} }
if (!hostPath.isEmpty()) { if (!hostPath.isEmpty()) {
@@ -279,19 +383,102 @@ static QVector<ToolChainDescription> extractToolChainsFromCache(const CMakeConfi
QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath, QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
QString *warningMessage) const QString *warningMessage) const
{ {
QList<void *> result;
qCInfo(cmInputLog) << "Examining directory:" << importPath.toUserOutput(); qCInfo(cmInputLog) << "Examining directory:" << importPath.toUserOutput();
if (importPath.isChildOf(m_presetsTempDir.path())) {
auto data = std::make_unique<DirectoryData>();
const QString presetName = importPath.fileName();
PresetsDetails::ConfigurePreset configurePreset
= Utils::findOrDefault(m_presetsData.configurePresets,
[presetName](const PresetsDetails::ConfigurePreset &preset) {
return preset.name == presetName;
});
if (configurePreset.displayName)
data->cmakePresetDisplayname = configurePreset.displayName.value();
else
data->cmakePresetDisplayname = configurePreset.name;
data->cmakePreset = configurePreset.name;
if (!configurePreset.cmakeExecutable) {
const CMakeTool *cmakeTool = CMakeToolManager::defaultCMakeTool();
if (cmakeTool)
configurePreset.cmakeExecutable = cmakeTool->cmakeExecutable().toString();
}
data->cmakeBinary = Utils::FilePath::fromString(configurePreset.cmakeExecutable.value());
if (configurePreset.generator)
data->generator = configurePreset.generator.value();
if (configurePreset.architecture && configurePreset.architecture.value().value)
data->platform = configurePreset.architecture.value().value.value();
if (configurePreset.toolset && configurePreset.toolset.value().value)
data->toolset = configurePreset.toolset.value().value.value();
QString binaryDir = importPath.toString();
if (configurePreset.binaryDir) {
binaryDir = configurePreset.binaryDir.value();
CMakePresets::Macros::expand(configurePreset,
Environment::systemEnvironment(),
projectDirectory(),
binaryDir);
}
data->buildDirectory = Utils::FilePath::fromString(binaryDir);
const CMakeConfig cache = configurePreset.cacheVariables
? configurePreset.cacheVariables.value()
: CMakeConfig();
data->cmakeBuildType = cache.valueOf("CMAKE_BUILD_TYPE");
if (data->cmakeBuildType.isEmpty())
data->cmakeBuildType = "Debug";
data->sysroot = cache.filePathValueOf("CMAKE_SYSROOT");
CMakeConfig config;
if (cache.valueOf("CMAKE_C_COMPILER").isEmpty()
&& cache.valueOf("CMAKE_CXX_COMPILER").isEmpty()) {
QApplication::setOverrideCursor(Qt::WaitCursor);
config = configurationFromPresetProbe(importPath, configurePreset);
QApplication::restoreOverrideCursor();
} else {
config = cache;
config << CMakeConfigItem("CMAKE_COMMAND",
CMakeConfigItem::PATH,
configurePreset.cmakeExecutable.value().toUtf8());
if (configurePreset.generator)
config << CMakeConfigItem("CMAKE_GENERATOR",
CMakeConfigItem::STRING,
configurePreset.generator.value().toUtf8());
}
const FilePath qmake = qmakeFromCMakeCache(config);
if (!qmake.isEmpty())
data->qt = findOrCreateQtVersion(qmake);
// ToolChains:
data->toolChains = extractToolChainsFromCache(config);
result.push_back(static_cast<void *>(data.release()));
return result;
}
const FilePath cacheFile = importPath.pathAppended("CMakeCache.txt"); const FilePath cacheFile = importPath.pathAppended("CMakeCache.txt");
if (!cacheFile.exists()) { if (!cacheFile.exists()) {
qCDebug(cmInputLog) << cacheFile.toUserOutput() << "does not exist, returning."; qCDebug(cmInputLog) << cacheFile.toUserOutput() << "does not exist, returning.";
return { }; return result;
} }
QString errorMessage; QString errorMessage;
const CMakeConfig config = CMakeConfig::fromFile(cacheFile, &errorMessage); const CMakeConfig config = CMakeConfig::fromFile(cacheFile, &errorMessage);
if (config.isEmpty() || !errorMessage.isEmpty()) { if (config.isEmpty() || !errorMessage.isEmpty()) {
qCDebug(cmInputLog) << "Failed to read configuration from" << cacheFile << errorMessage; qCDebug(cmInputLog) << "Failed to read configuration from" << cacheFile << errorMessage;
return { }; return result;
} }
QByteArrayList buildConfigurationTypes = {config.valueOf("CMAKE_BUILD_TYPE")}; QByteArrayList buildConfigurationTypes = {config.valueOf("CMAKE_BUILD_TYPE")};
@@ -301,7 +488,6 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
buildConfigurationTypes = buildConfigurationTypesString.split(';'); buildConfigurationTypes = buildConfigurationTypesString.split(';');
} }
QList<void *> result;
for (auto const &buildType: qAsConst(buildConfigurationTypes)) { for (auto const &buildType: qAsConst(buildConfigurationTypes)) {
auto data = std::make_unique<DirectoryData>(); auto data = std::make_unique<DirectoryData>();
@@ -374,6 +560,12 @@ bool CMakeProjectImporter::matchKit(void *directoryData, const Kit *k) const
} }
} }
if (!data->cmakePreset.isEmpty()) {
auto presetConfigItem = CMakeConfigurationKitAspect::cmakePresetConfigItem(k);
if (data->cmakePreset != presetConfigItem.expandedValue(k))
return false;
}
qCDebug(cmInputLog) << k->displayName() qCDebug(cmInputLog) << k->displayName()
<< "matches directoryData for" << data->buildDirectory.toUserOutput(); << "matches directoryData for" << data->buildDirectory.toUserOutput();
return true; return true;
@@ -395,6 +587,13 @@ Kit *CMakeProjectImporter::createKit(void *directoryData) const
CMakeGeneratorKitAspect::setPlatform(k, data->platform); CMakeGeneratorKitAspect::setPlatform(k, data->platform);
CMakeGeneratorKitAspect::setToolset(k, data->toolset); CMakeGeneratorKitAspect::setToolset(k, data->toolset);
if (!data->cmakePresetDisplayname.isEmpty()) {
k->setUnexpandedDisplayName(
QString("%1 (CMake preset)").arg(data->cmakePresetDisplayname));
CMakeConfigurationKitAspect::setCMakePreset(k, data->cmakePreset);
}
SysRootKitAspect::setSysRoot(k, data->sysroot); SysRootKitAspect::setSysRoot(k, data->sysroot);
for (const ToolChainDescription &cmtcd : data->toolChains) { for (const ToolChainDescription &cmtcd : data->toolChains) {

View File

@@ -3,6 +3,9 @@
#pragma once #pragma once
#include "presetsparser.h"
#include "utils/temporarydirectory.h"
#include <qtsupport/qtprojectimporter.h> #include <qtsupport/qtprojectimporter.h>
namespace CMakeProjectManager { namespace CMakeProjectManager {
@@ -16,7 +19,7 @@ class CMakeProjectImporter : public QtSupport::QtProjectImporter
Q_DECLARE_TR_FUNCTIONS(CMakeProjectManager::Internal::CMakeProjectImporter) Q_DECLARE_TR_FUNCTIONS(CMakeProjectManager::Internal::CMakeProjectImporter)
public: public:
CMakeProjectImporter(const Utils::FilePath &path); CMakeProjectImporter(const Utils::FilePath &path, const Internal::PresetsData &presetsData);
QStringList importCandidates() final; QStringList importCandidates() final;
@@ -37,6 +40,9 @@ private:
void cleanupTemporaryCMake(ProjectExplorer::Kit *k, const QVariantList &vl); void cleanupTemporaryCMake(ProjectExplorer::Kit *k, const QVariantList &vl);
void persistTemporaryCMake(ProjectExplorer::Kit *k, const QVariantList &vl); void persistTemporaryCMake(ProjectExplorer::Kit *k, const QVariantList &vl);
Internal::PresetsData m_presetsData;
Utils::TemporaryDirectory m_presetsTempDir;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -75,6 +75,10 @@ QtcPlugin {
"fileapiparser.h", "fileapiparser.h",
"fileapireader.cpp", "fileapireader.cpp",
"fileapireader.h", "fileapireader.h",
"presetsparser.cpp",
"presetsparser.h",
"presetsmacros.cpp",
"presetsmacros.h",
"projecttreehelper.cpp", "projecttreehelper.cpp",
"projecttreehelper.h" "projecttreehelper.h"
] ]

View File

@@ -0,0 +1,131 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "presetsmacros.h"
#include "presetsparser.h"
#include <utils/environment.h>
#include <utils/filepath.h>
#include <utils/hostosinfo.h>
#include <QRegularExpression>
namespace CMakeProjectManager::Internal::CMakePresets::Macros {
static void expandAllButEnv(const PresetsDetails::ConfigurePreset &configurePreset,
const Utils::FilePath &sourceDirectory,
QString &value)
{
value.replace("${dollar}", "$");
value.replace("${sourceDir}", sourceDirectory.toString());
value.replace("${sourceParentDir}", sourceDirectory.parentDir().toString());
value.replace("${sourceDirName}", sourceDirectory.fileName());
value.replace("${presetName}", configurePreset.name);
if (configurePreset.generator)
value.replace("${generator}", configurePreset.generator.value());
}
void expand(const PresetsDetails::ConfigurePreset &configurePreset,
Utils::Environment &env,
const Utils::FilePath &sourceDirectory)
{
const QHash<QString, QString> presetEnv = configurePreset.environment
? configurePreset.environment.value()
: QHash<QString, QString>();
for (auto it = presetEnv.constKeyValueBegin(); it != presetEnv.constKeyValueEnd(); ++it) {
const QString key = it->first;
QString value = it->second;
expandAllButEnv(configurePreset, sourceDirectory, value);
QRegularExpression envRegex(R"((\$env\{(\w+)\}))");
for (const QRegularExpressionMatch &match : envRegex.globalMatch(value)) {
if (match.captured(2) != key)
value.replace(match.captured(1), presetEnv.value(match.captured(2)));
else
value.replace(match.captured(1), "");
}
QString sep;
bool append = true;
if (key.compare("PATH", Qt::CaseInsensitive) == 0) {
sep = Utils::OsSpecificAspects::pathListSeparator(env.osType());
const int index = value.indexOf("$penv{PATH}", 0, Qt::CaseInsensitive);
if (index != 0)
append = false;
value.replace("$penv{PATH}", "", Qt::CaseInsensitive);
}
QRegularExpression penvRegex(R"((\$penv\{(\w+)\}))");
for (const QRegularExpressionMatch &match : penvRegex.globalMatch(value))
value.replace(match.captured(1), env.value(match.captured(2)));
if (append)
env.appendOrSet(key, value, sep);
else
env.prependOrSet(key, value, sep);
}
}
void expand(const PresetsDetails::ConfigurePreset &configurePreset,
Utils::EnvironmentItems &envItems,
const Utils::FilePath &sourceDirectory)
{
const QHash<QString, QString> presetEnv = configurePreset.environment
? configurePreset.environment.value()
: QHash<QString, QString>();
for (auto it = presetEnv.constKeyValueBegin(); it != presetEnv.constKeyValueEnd(); ++it) {
const QString key = it->first;
QString value = it->second;
expandAllButEnv(configurePreset, sourceDirectory, value);
QRegularExpression envRegex(R"((\$env\{(\w+)\}))");
for (const QRegularExpressionMatch &match : envRegex.globalMatch(value)) {
if (match.captured(2) != key)
value.replace(match.captured(1), presetEnv.value(match.captured(2)));
else
value.replace(match.captured(1), "");
}
auto operation = Utils::EnvironmentItem::Operation::SetEnabled;
if (key.compare("PATH", Qt::CaseInsensitive) == 0) {
operation = Utils::EnvironmentItem::Operation::Append;
const int index = value.indexOf("$penv{PATH}", 0, Qt::CaseInsensitive);
if (index != 0)
operation = Utils::EnvironmentItem::Operation::Prepend;
value.replace("$penv{PATH}", "", Qt::CaseInsensitive);
}
QRegularExpression penvRegex(R"((\$penv\{(\w+)\}))");
for (const QRegularExpressionMatch &match : penvRegex.globalMatch(value))
value.replace(match.captured(1), QString("${%1}").arg(match.captured(2)));
envItems.emplace_back(Utils::EnvironmentItem(key, value, operation));
}
}
void expand(const PresetsDetails::ConfigurePreset &configurePreset,
const Utils::Environment &env,
const Utils::FilePath &sourceDirectory,
QString &value)
{
expandAllButEnv(configurePreset, sourceDirectory, value);
const QHash<QString, QString> presetEnv = configurePreset.environment
? configurePreset.environment.value()
: QHash<QString, QString>();
QRegularExpression envRegex(R"((\$env\{(\w+)\}))");
for (const QRegularExpressionMatch &match : envRegex.globalMatch(value))
value.replace(match.captured(1), presetEnv.value(match.captured(2)));
QRegularExpression penvRegex(R"((\$penv\{(\w+)\}))");
for (const QRegularExpressionMatch &match : penvRegex.globalMatch(value))
value.replace(match.captured(1), env.value(match.captured(2)));
}
} // namespace CMakeProjectManager::Internal::CMakePresets::Macros

View File

@@ -0,0 +1,46 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#pragma once
#include <utils/environmentfwd.h>
namespace Utils {
class Environment;
class FilePath;
}
namespace CMakeProjectManager::Internal {
namespace PresetsDetails {
class ConfigurePreset;
}
namespace CMakePresets::Macros {
/**
* Expands the CMakePresets Macros using Utils::Environment as target and source for parent environment values.
* $penv{PATH} is taken from Utils::Environment
*/
void expand(const PresetsDetails::ConfigurePreset &configurePreset,
Utils::Environment &env,
const Utils::FilePath &sourceDirectory);
/**
* Expands the CMakePresets Macros using Utils::Environment as target
* $penv{PATH} is replaced with Qt Creator macros ${PATH}
*/
void expand(const PresetsDetails::ConfigurePreset &configurePreset,
Utils::EnvironmentItems &envItems,
const Utils::FilePath &sourceDirectory);
/**
* Expands the CMakePresets macros inside the @value QString parameter.
*/
void expand(const PresetsDetails::ConfigurePreset &configurePreset,
const Utils::Environment &env,
const Utils::FilePath &sourceDirectory,
QString &value);
} // namespace CMakePresets::Macros
} // namespace CMakeProjectManager::Internal

View File

@@ -0,0 +1,281 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "presetsparser.h"
#include <QCoreApplication>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace CMakeProjectManager {
namespace Internal {
bool parseVersion(const QJsonValue &jsonValue, int &version)
{
if (jsonValue.isNull())
return false;
const int invalidVersion = -1;
version = jsonValue.toInt(invalidVersion);
return version != invalidVersion;
}
bool parseCMakeMinimumRequired(const QJsonValue &jsonValue, QVersionNumber &versionNumber)
{
if (jsonValue.isNull() || !jsonValue.isObject())
return false;
QJsonObject object = jsonValue.toObject();
versionNumber = QVersionNumber(object.value("major").toInt(),
object.value("minor").toInt(),
object.value("patch").toInt());
return true;
}
bool parseConfigurePresets(const QJsonValue &jsonValue,
std::vector<PresetsDetails::ConfigurePreset> &configurePresets)
{
// The whole section is optional
if (jsonValue.isNull())
return true;
if (!jsonValue.isArray())
return false;
const QJsonArray configurePresetsArray = jsonValue.toArray();
for (const QJsonValue &presetJson : configurePresetsArray) {
if (!presetJson.isObject())
continue;
QJsonObject object = presetJson.toObject();
PresetsDetails::ConfigurePreset preset;
preset.name = object.value("name").toString();
preset.hidden = object.value("hidden").toBool();
QJsonValue inherits = object.value("inherits");
if (!inherits.isNull()) {
preset.inherits = QStringList();
if (inherits.isArray()) {
const QJsonArray inheritsArray = inherits.toArray();
for (const QJsonValue &inheritsValue : inheritsArray)
preset.inherits.value() << inheritsValue.toString();
} else {
QString inheritsValue = inherits.toString();
if (!inheritsValue.isEmpty())
preset.inherits.value() << inheritsValue;
}
}
if (object.contains("displayName"))
preset.displayName = object.value("displayName").toString();
if (object.contains("description"))
preset.description = object.value("description").toString();
if (object.contains("generator"))
preset.generator = object.value("generator").toString();
if (object.contains("binaryDir"))
preset.binaryDir = object.value("binaryDir").toString();
if (object.contains("cmakeExecutable"))
preset.cmakeExecutable = object.value("cmakeExecutable").toString();
const QJsonObject cacheVariablesObj = object.value("cacheVariables").toObject();
for (const QString &cacheKey : cacheVariablesObj.keys()) {
if (!preset.cacheVariables)
preset.cacheVariables = CMakeConfig();
QJsonValue cacheValue = cacheVariablesObj.value(cacheKey);
if (cacheValue.isObject()) {
QJsonObject cacheVariableObj = cacheValue.toObject();
CMakeConfigItem item;
item.key = cacheKey.toUtf8();
item.type = CMakeConfigItem::typeStringToType(
cacheVariableObj.value("type").toString().toUtf8());
item.value = cacheVariableObj.value("type").toString().toUtf8();
preset.cacheVariables.value() << item;
} else {
preset.cacheVariables.value()
<< CMakeConfigItem(cacheKey.toUtf8(), cacheValue.toString().toUtf8());
}
}
const QJsonObject environmentObj = object.value("environment").toObject();
for (const QString &envKey : environmentObj.keys()) {
if (!preset.environment)
preset.environment = QHash<QString, QString>();
QJsonValue envValue = environmentObj.value(envKey);
preset.environment.value().insert(envKey, envValue.toString());
}
const QJsonObject warningsObj = object.value("warnings").toObject();
if (!warningsObj.isEmpty()) {
preset.warnings = PresetsDetails::Warnings();
if (warningsObj.contains("dev"))
preset.warnings->dev = warningsObj.value("dev").toBool();
if (warningsObj.contains("deprecated"))
preset.warnings->deprecated = warningsObj.value("deprecated").toBool();
if (warningsObj.contains("uninitialized"))
preset.warnings->uninitialized = warningsObj.value("uninitialized").toBool();
if (warningsObj.contains("unusedCli"))
preset.warnings->unusedCli = warningsObj.value("unusedCli").toBool();
if (warningsObj.contains("systemVars"))
preset.warnings->systemVars = warningsObj.value("systemVars").toBool();
}
const QJsonObject errorsObj = object.value("errors").toObject();
if (!errorsObj.isEmpty()) {
preset.errors = PresetsDetails::Errors();
if (errorsObj.contains("dev"))
preset.errors->dev = errorsObj.value("dev").toBool();
if (errorsObj.contains("deprecated"))
preset.errors->deprecated = errorsObj.value("deprecated").toBool();
}
const QJsonObject debugObj = object.value("debug").toObject();
if (!debugObj.isEmpty()) {
preset.debug = PresetsDetails::Debug();
if (debugObj.contains("output"))
preset.debug->output = debugObj.value("output").toBool();
if (debugObj.contains("tryCompile"))
preset.debug->tryCompile = debugObj.value("tryCompile").toBool();
if (debugObj.contains("find"))
preset.debug->find = debugObj.value("find").toBool();
}
const QJsonObject architectureObj = object.value("architecture").toObject();
if (!architectureObj.isEmpty()) {
preset.architecture = PresetsDetails::ValueStrategyPair();
if (architectureObj.contains("value"))
preset.architecture->value = architectureObj.value("value").toString();
if (architectureObj.contains("strategy")) {
const QString strategy = architectureObj.value("strategy").toString();
if (strategy == "set")
preset.architecture->strategy = PresetsDetails::ValueStrategyPair::Strategy::set;
if (strategy == "external")
preset.architecture->strategy
= PresetsDetails::ValueStrategyPair::Strategy::external;
}
}
const QJsonObject toolsetObj = object.value("toolset").toObject();
if (!toolsetObj.isEmpty()) {
preset.toolset = PresetsDetails::ValueStrategyPair();
if (toolsetObj.contains("value"))
preset.toolset->value = toolsetObj.value("value").toString();
if (toolsetObj.contains("strategy")) {
const QString strategy = toolsetObj.value("strategy").toString();
if (strategy == "set")
preset.toolset->strategy = PresetsDetails::ValueStrategyPair::Strategy::set;
if (strategy == "external")
preset.toolset->strategy = PresetsDetails::ValueStrategyPair::Strategy::external;
}
}
configurePresets.emplace_back(preset);
}
return true;
}
const PresetsData &PresetsParser::presetsData() const
{
return m_presetsData;
}
bool PresetsParser::parse(const Utils::FilePath &jsonFile, QString &errorMessage, int &errorLine)
{
const std::optional<QByteArray> jsonContents = jsonFile.fileContents();
if (!jsonContents) {
errorMessage = QCoreApplication::translate("CMakeProjectManager::Internal",
"Failed to read %1 file")
.arg(jsonFile.fileName());
return false;
}
QJsonParseError error;
const QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonContents.value(), &error);
if (jsonDoc.isNull()) {
errorLine = 1;
for (int i = 0; i < error.offset; ++i)
if (jsonContents.value().at(i) == '\n')
++errorLine;
errorMessage = error.errorString();
return false;
}
if (!jsonDoc.isObject()) {
errorMessage = QCoreApplication::translate("CMakeProjectManager::Internal",
"Invalid %1 file")
.arg(jsonFile.fileName());
return false;
}
QJsonObject root = jsonDoc.object();
if (!parseVersion(root.value("version"), m_presetsData.version)) {
errorMessage = QCoreApplication::translate("CMakeProjectManager::Internal",
"Invalid \"version\" in %1 file")
.arg(jsonFile.fileName());
return false;
}
// optional
parseCMakeMinimumRequired(root.value("cmakeMinimumRequired"),
m_presetsData.cmakeMinimimRequired);
// optional
if (!parseConfigurePresets(root.value("configurePresets"), m_presetsData.configurePresets)) {
errorMessage = QCoreApplication::translate(
"CMakeProjectManager::Internal",
"Invalid \"configurePresets\" section in %1 file").arg(jsonFile.fileName());
return false;
}
return true;
}
void PresetsDetails::ConfigurePreset::inheritFrom(const ConfigurePreset &other)
{
if (!vendor && other.vendor)
vendor = other.vendor;
if (!generator && other.generator)
generator = other.generator;
if (!architecture && other.architecture)
architecture = other.architecture;
if (!toolset && other.toolset)
toolset = other.toolset;
if (!binaryDir && other.binaryDir)
binaryDir = other.binaryDir;
if (!cmakeExecutable && other.cmakeExecutable)
cmakeExecutable = other.cmakeExecutable;
if (!cacheVariables && other.cacheVariables)
cacheVariables = other.cacheVariables;
if (!environment && other.environment)
environment = other.environment;
if (!warnings && other.warnings)
warnings = other.warnings;
if (!errors && other.errors)
errors = other.errors;
if (!debug && other.debug)
debug = other.debug;
}
} // namespace Internal
} // namespace CMakeProjectManager

View File

@@ -0,0 +1,91 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#pragma once
#include <utils/filepath.h>
#include "cmakeconfigitem.h"
#include <QHash>
#include <QString>
#include <QVersionNumber>
namespace CMakeProjectManager {
namespace Internal {
namespace PresetsDetails {
class ValueStrategyPair {
public:
std::optional<QString> value;
enum class Strategy : bool { set, external };
std::optional<Strategy> strategy;
};
class Warnings {
public:
std::optional<bool> dev;
std::optional<bool> deprecated;
std::optional<bool> uninitialized = false;
std::optional<bool> unusedCli = true;
std::optional<bool> systemVars = false;
};
class Errors {
public:
std::optional<bool> dev;
std::optional<bool> deprecated;
};
class Debug {
public:
std::optional<bool> output = false;
std::optional<bool> tryCompile = false;
std::optional<bool> find = false;
};
class ConfigurePreset {
public:
void inheritFrom(const ConfigurePreset &other);
QString name;
std::optional<bool> hidden = false;
std::optional<QStringList> inherits;
std::optional<QHash<QString, QString>> vendor;
std::optional<QString> displayName;
std::optional<QString> description;
std::optional<QString> generator;
std::optional<ValueStrategyPair> architecture;
std::optional<ValueStrategyPair> toolset;
std::optional<QString> binaryDir;
std::optional<QString> cmakeExecutable;
std::optional<CMakeConfig> cacheVariables;
std::optional<QHash<QString, QString>> environment;
std::optional<Warnings> warnings;
std::optional<Errors> errors;
std::optional<Debug> debug;
};
} // namespace PresetsDetails
class PresetsData
{
public:
int version = 0;
QVersionNumber cmakeMinimimRequired;
QHash<QString, QString> vendor;
std::vector<PresetsDetails::ConfigurePreset> configurePresets;
};
class PresetsParser
{
PresetsData m_presetsData;
public:
bool parse(Utils::FilePath const &jsonFile, QString &errorMessage, int &errorLine);
const PresetsData &presetsData() const;
};
} // namespace Internal
} // namespace CMakeProjectManager

View File

@@ -0,0 +1,66 @@
cmake_minimum_required(VERSION 3.5)
project(cmakepresets VERSION 0.1 LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check https://doc.qt.io/qt/deployment-android.html for more information.
# They need to be set before the find_package( ...) calls below.
#if(ANDROID)
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
# if (ANDROID_ABI STREQUAL "armeabi-v7a")
# set(ANDROID_EXTRA_LIBS
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
# endif()
#endif()
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)
set(PROJECT_SOURCES
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(cmakepresets
MANUAL_FINALIZATION
${PROJECT_SOURCES}
)
else()
if(ANDROID)
add_library(cmakepresets SHARED
${PROJECT_SOURCES}
)
else()
add_executable(cmakepresets WIN32
${PROJECT_SOURCES}
)
endif()
endif()
set_target_properties(cmakepresets PROPERTIES FOLDER qtc_runnable)
target_link_libraries(cmakepresets PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
set_target_properties(cmakepresets PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
)
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(cmakepresets)
endif()

View File

@@ -0,0 +1,44 @@
{
"version": 1,
"cmakeMinimumRequired": {
"major": 3,
"minor": 19,
"patch": 0
},
"configurePresets": [
{
"name": "mingw",
"displayName": "MinGW 11.2.0",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build-${presetName}-release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_PREFIX_PATH": "c:/Qt/6.3.1/mingw_64"
},
"environment": {
"PATH": "c:/mingw64/bin;$penv{PATH}"
},
"debug" : {
"find" : true
}
},
{
"name": "mingw-make",
"displayName": "MinGW 11.2.0 Makefiles",
"generator": "MinGW Makefiles",
"inherits" : "mingw"
},
{
"name": "visualc",
"displayName": "Visual C++ 2019 x64",
"generator": "Visual Studio 16 2019",
"binaryDir": "${sourceDir}/build-${presetName}",
"architecture" : {
"value": "x64"
},
"cacheVariables": {
"CMAKE_PREFIX_PATH": "c:/Qt/6.3.1/msvc2019_64"
}
}
]
}

View File

@@ -0,0 +1,14 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

View File

@@ -0,0 +1,18 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "mainwindow.h"
#include "./ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}

View File

@@ -0,0 +1,24 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget"/>
<widget class="QMenuBar" name="menubar"/>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>