forked from qt-creator/qt-creator
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:
@@ -35,5 +35,7 @@ add_qtc_plugin(CMakeProjectManager
|
||||
fileapidataextractor.cpp fileapidataextractor.h
|
||||
fileapiparser.cpp fileapiparser.h
|
||||
fileapireader.cpp fileapireader.h
|
||||
presetsparser.cpp presetsparser.h
|
||||
presetsmacros.cpp presetsmacros.h
|
||||
projecttreehelper.cpp projecttreehelper.h
|
||||
)
|
||||
|
||||
@@ -8,12 +8,15 @@
|
||||
#include "cmakebuildsystem.h"
|
||||
#include "cmakeconfigitem.h"
|
||||
#include "cmakekitinformation.h"
|
||||
#include "cmakeproject.h"
|
||||
#include "cmakeprojectconstants.h"
|
||||
#include "cmakeprojectplugin.h"
|
||||
#include "cmakespecificsettings.h"
|
||||
#include "configmodel.h"
|
||||
#include "configmodelitemdelegate.h"
|
||||
#include "fileapiparser.h"
|
||||
#include "presetsmacros.h"
|
||||
#include "presetsparser.h"
|
||||
|
||||
#include <android/androidconstants.h>
|
||||
#include <docker/dockerconstants.h>
|
||||
@@ -1163,6 +1166,164 @@ static CommandLine defaultInitialCMakeCommand(const Kit *k, const QString buildT
|
||||
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:
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -1365,7 +1526,15 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id)
|
||||
if (qt && qt->isQmlDebuggingSupported())
|
||||
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);
|
||||
updateAndEmitConfigureEnvironmentChanged();
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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
|
||||
|
||||
#include "cmakeconfigitem.h"
|
||||
#include "cmakekitinformation.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_QMAKE_KEY[] = "QT_QMAKE_EXECUTABLE";
|
||||
const char CMAKE_PREFIX_PATH_KEY[] = "CMAKE_PREFIX_PATH";
|
||||
const char QTC_CMAKE_PRESET_KEY[] = "QTC_CMAKE_PRESET";
|
||||
|
||||
class CMakeConfigurationKitAspectWidget final : public KitAspectWidget
|
||||
{
|
||||
@@ -1120,6 +1122,23 @@ CMakeConfig CMakeConfigurationKitAspect::defaultConfiguration(const Kit *k)
|
||||
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
|
||||
{
|
||||
// FIXME: Convert preload scripts
|
||||
|
||||
@@ -90,6 +90,9 @@ public:
|
||||
|
||||
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
|
||||
ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *k) const final;
|
||||
void setup(ProjectExplorer::Kit *k) final;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
#include <projectexplorer/projectnodes.h>
|
||||
#include <projectexplorer/target.h>
|
||||
#include <projectexplorer/taskhub.h>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Utils;
|
||||
@@ -34,6 +35,8 @@ CMakeProject::CMakeProject(const FilePath &fileName)
|
||||
setDisplayName(projectDirectory().fileName());
|
||||
setCanBuildProducts();
|
||||
setHasMakeInstallEquivalent(true);
|
||||
|
||||
readPresets();
|
||||
}
|
||||
|
||||
CMakeProject::~CMakeProject()
|
||||
@@ -59,7 +62,7 @@ Tasks CMakeProject::projectIssues(const Kit *k) const
|
||||
ProjectImporter *CMakeProject::projectImporter() const
|
||||
{
|
||||
if (!m_projectImporter)
|
||||
m_projectImporter = new CMakeProjectImporter(projectFilePath());
|
||||
m_projectImporter = new CMakeProjectImporter(projectFilePath(), m_presetsData);
|
||||
return m_projectImporter;
|
||||
}
|
||||
|
||||
@@ -73,6 +76,100 @@ void CMakeProject::clearIssues()
|
||||
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)
|
||||
{
|
||||
t->updateDefaultBuildConfigurations();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "cmake_global.h"
|
||||
#include "presetsparser.h"
|
||||
|
||||
#include <projectexplorer/project.h>
|
||||
|
||||
@@ -27,15 +28,21 @@ public:
|
||||
void addIssue(IssueType type, const QString &text);
|
||||
void clearIssues();
|
||||
|
||||
Internal::PresetsData presetsData() const;
|
||||
void readPresets();
|
||||
|
||||
protected:
|
||||
bool setupTarget(ProjectExplorer::Target *t) final;
|
||||
|
||||
private:
|
||||
ProjectExplorer::DeploymentKnowledge deploymentKnowledge() const override;
|
||||
Internal::PresetsData combinePresets(Internal::PresetsData &cmakePresetsData,
|
||||
Internal::PresetsData &cmakeUserPresetsData);
|
||||
|
||||
mutable Internal::CMakeProjectImporter *m_projectImporter = nullptr;
|
||||
|
||||
ProjectExplorer::Tasks m_issues;
|
||||
Internal::PresetsData m_presetsData;
|
||||
};
|
||||
|
||||
} // namespace CMakeProjectManager
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "cmakekitinformation.h"
|
||||
#include "cmakeprojectconstants.h"
|
||||
#include "cmaketoolmanager.h"
|
||||
#include "presetsmacros.h"
|
||||
|
||||
#include <coreplugin/messagemanager.h>
|
||||
|
||||
@@ -23,6 +24,7 @@
|
||||
#include <utils/stringutils.h>
|
||||
#include <utils/temporarydirectory.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
@@ -43,6 +45,9 @@ struct DirectoryData
|
||||
FilePath cmakeHomeDirectory;
|
||||
bool hasQmlDebugging = false;
|
||||
|
||||
QString cmakePresetDisplayname;
|
||||
QString cmakePreset;
|
||||
|
||||
// Kit Stuff
|
||||
FilePath cmakeBinary;
|
||||
QString generator;
|
||||
@@ -88,7 +93,10 @@ static QString uniqueCMakeToolDisplayName(CMakeTool &tool)
|
||||
|
||||
// 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(),
|
||||
[this](Kit *k, const QVariantList &vl) { cleanupTemporaryCMake(k, vl); },
|
||||
@@ -111,11 +119,102 @@ QStringList CMakeProjectImporter::importCandidates()
|
||||
BuildConfiguration::Unknown);
|
||||
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);
|
||||
qCInfo(cmInputLog) << "import candidates:" << 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)
|
||||
{
|
||||
// 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();
|
||||
const FilePath canQtCMakeDir = FilePath::fromString(qtCMakeDir.toFileInfo().canonicalFilePath());
|
||||
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();
|
||||
const FilePath baseQtDir = canQtCMakeDir.parentDir().parentDir().parentDir(); // Up 3 levels...
|
||||
qCDebug(cmInputLog) << "BaseQtDir:" << baseQtDir.toUserOutput();
|
||||
|
||||
// Run a CMake project that would do qmake probing
|
||||
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()));
|
||||
}
|
||||
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 {
|
||||
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()));
|
||||
}
|
||||
if (!hostPath.isEmpty()) {
|
||||
@@ -279,19 +383,102 @@ static QVector<ToolChainDescription> extractToolChainsFromCache(const CMakeConfi
|
||||
QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
|
||||
QString *warningMessage) const
|
||||
{
|
||||
QList<void *> result;
|
||||
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");
|
||||
|
||||
if (!cacheFile.exists()) {
|
||||
qCDebug(cmInputLog) << cacheFile.toUserOutput() << "does not exist, returning.";
|
||||
return { };
|
||||
return result;
|
||||
}
|
||||
|
||||
QString errorMessage;
|
||||
const CMakeConfig config = CMakeConfig::fromFile(cacheFile, &errorMessage);
|
||||
if (config.isEmpty() || !errorMessage.isEmpty()) {
|
||||
qCDebug(cmInputLog) << "Failed to read configuration from" << cacheFile << errorMessage;
|
||||
return { };
|
||||
return result;
|
||||
}
|
||||
|
||||
QByteArrayList buildConfigurationTypes = {config.valueOf("CMAKE_BUILD_TYPE")};
|
||||
@@ -301,7 +488,6 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
|
||||
buildConfigurationTypes = buildConfigurationTypesString.split(';');
|
||||
}
|
||||
|
||||
QList<void *> result;
|
||||
for (auto const &buildType: qAsConst(buildConfigurationTypes)) {
|
||||
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()
|
||||
<< "matches directoryData for" << data->buildDirectory.toUserOutput();
|
||||
return true;
|
||||
@@ -395,6 +587,13 @@ Kit *CMakeProjectImporter::createKit(void *directoryData) const
|
||||
CMakeGeneratorKitAspect::setPlatform(k, data->platform);
|
||||
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);
|
||||
|
||||
for (const ToolChainDescription &cmtcd : data->toolChains) {
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "presetsparser.h"
|
||||
#include "utils/temporarydirectory.h"
|
||||
|
||||
#include <qtsupport/qtprojectimporter.h>
|
||||
|
||||
namespace CMakeProjectManager {
|
||||
@@ -16,7 +19,7 @@ class CMakeProjectImporter : public QtSupport::QtProjectImporter
|
||||
Q_DECLARE_TR_FUNCTIONS(CMakeProjectManager::Internal::CMakeProjectImporter)
|
||||
|
||||
public:
|
||||
CMakeProjectImporter(const Utils::FilePath &path);
|
||||
CMakeProjectImporter(const Utils::FilePath &path, const Internal::PresetsData &presetsData);
|
||||
|
||||
QStringList importCandidates() final;
|
||||
|
||||
@@ -37,6 +40,9 @@ private:
|
||||
|
||||
void cleanupTemporaryCMake(ProjectExplorer::Kit *k, const QVariantList &vl);
|
||||
void persistTemporaryCMake(ProjectExplorer::Kit *k, const QVariantList &vl);
|
||||
|
||||
Internal::PresetsData m_presetsData;
|
||||
Utils::TemporaryDirectory m_presetsTempDir;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -75,6 +75,10 @@ QtcPlugin {
|
||||
"fileapiparser.h",
|
||||
"fileapireader.cpp",
|
||||
"fileapireader.h",
|
||||
"presetsparser.cpp",
|
||||
"presetsparser.h",
|
||||
"presetsmacros.cpp",
|
||||
"presetsmacros.h",
|
||||
"projecttreehelper.cpp",
|
||||
"projecttreehelper.h"
|
||||
]
|
||||
|
||||
131
src/plugins/cmakeprojectmanager/presetsmacros.cpp
Normal file
131
src/plugins/cmakeprojectmanager/presetsmacros.cpp
Normal 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
|
||||
46
src/plugins/cmakeprojectmanager/presetsmacros.h
Normal file
46
src/plugins/cmakeprojectmanager/presetsmacros.h
Normal 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
|
||||
281
src/plugins/cmakeprojectmanager/presetsparser.cpp
Normal file
281
src/plugins/cmakeprojectmanager/presetsparser.cpp
Normal 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
|
||||
91
src/plugins/cmakeprojectmanager/presetsparser.h
Normal file
91
src/plugins/cmakeprojectmanager/presetsparser.h
Normal 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
|
||||
66
tests/manual/cmakepresets/CMakeLists.txt
Normal file
66
tests/manual/cmakepresets/CMakeLists.txt
Normal 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()
|
||||
44
tests/manual/cmakepresets/CMakePresets.json
Normal file
44
tests/manual/cmakepresets/CMakePresets.json
Normal 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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
14
tests/manual/cmakepresets/main.cpp
Normal file
14
tests/manual/cmakepresets/main.cpp
Normal 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();
|
||||
}
|
||||
18
tests/manual/cmakepresets/mainwindow.cpp
Normal file
18
tests/manual/cmakepresets/mainwindow.cpp
Normal 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;
|
||||
}
|
||||
|
||||
24
tests/manual/cmakepresets/mainwindow.h
Normal file
24
tests/manual/cmakepresets/mainwindow.h
Normal 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
|
||||
22
tests/manual/cmakepresets/mainwindow.ui
Normal file
22
tests/manual/cmakepresets/mainwindow.ui
Normal 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>
|
||||
Reference in New Issue
Block a user