2019-06-28 14:30:32 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator.
|
|
|
|
|
**
|
|
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
|
|
|
**
|
|
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "qbsprofilemanager.h"
|
|
|
|
|
|
|
|
|
|
#include "defaultpropertyprovider.h"
|
|
|
|
|
#include "qbsproject.h"
|
|
|
|
|
#include "qbsprojectmanagerconstants.h"
|
|
|
|
|
#include "qbsprojectmanagerplugin.h"
|
|
|
|
|
#include "qbssettings.h"
|
|
|
|
|
|
|
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
#include <coreplugin/messagemanager.h>
|
|
|
|
|
#include <projectexplorer/kit.h>
|
|
|
|
|
#include <projectexplorer/kitmanager.h>
|
|
|
|
|
#include <projectexplorer/projectexplorer.h>
|
|
|
|
|
#include <qmljstools/qmljstoolsconstants.h>
|
|
|
|
|
#include <qtsupport/baseqtversion.h>
|
|
|
|
|
#include <qtsupport/qtkitinformation.h>
|
|
|
|
|
#include <utils/qtcassert.h>
|
2021-11-03 15:48:22 +01:00
|
|
|
#include <utils/qtcprocess.h>
|
2019-06-28 14:30:32 +02:00
|
|
|
|
|
|
|
|
#include <QCryptographicHash>
|
|
|
|
|
#include <QJSEngine>
|
2020-07-14 11:20:22 +02:00
|
|
|
#include <QRegularExpression>
|
2019-06-28 14:30:32 +02:00
|
|
|
#include <QVariantMap>
|
|
|
|
|
|
|
|
|
|
namespace QbsProjectManager {
|
|
|
|
|
|
|
|
|
|
static QList<PropertyProvider *> g_propertyProviders;
|
|
|
|
|
|
|
|
|
|
PropertyProvider::PropertyProvider()
|
|
|
|
|
{
|
|
|
|
|
g_propertyProviders.append(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PropertyProvider::~PropertyProvider()
|
|
|
|
|
{
|
|
|
|
|
g_propertyProviders.removeOne(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
static QString toJSLiteral(const bool b)
|
|
|
|
|
{
|
|
|
|
|
return QLatin1String(b ? "true" : "false");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString toJSLiteral(const QString &str)
|
|
|
|
|
{
|
|
|
|
|
QString js = str;
|
2020-07-14 11:20:22 +02:00
|
|
|
js.replace(QRegularExpression("([\\\\\"])"), "\\\\1");
|
2019-06-28 14:30:32 +02:00
|
|
|
js.prepend('"');
|
|
|
|
|
js.append('"');
|
|
|
|
|
return js;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString toJSLiteral(const QVariant &val)
|
|
|
|
|
{
|
|
|
|
|
if (!val.isValid())
|
|
|
|
|
return QString("undefined");
|
|
|
|
|
if (val.type() == QVariant::List || val.type() == QVariant::StringList) {
|
|
|
|
|
QString res;
|
|
|
|
|
const auto list = val.toList();
|
|
|
|
|
for (const QVariant &child : list) {
|
2020-01-15 19:10:34 +01:00
|
|
|
if (!res.isEmpty() ) res.append(", ");
|
2019-06-28 14:30:32 +02:00
|
|
|
res.append(toJSLiteral(child));
|
|
|
|
|
}
|
|
|
|
|
res.prepend('[');
|
|
|
|
|
res.append(']');
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
if (val.type() == QVariant::Map) {
|
|
|
|
|
const QVariantMap &vm = val.toMap();
|
|
|
|
|
QString str("{");
|
|
|
|
|
for (auto it = vm.begin(); it != vm.end(); ++it) {
|
|
|
|
|
if (it != vm.begin())
|
|
|
|
|
str += ',';
|
|
|
|
|
str += toJSLiteral(it.key()) + ':' + toJSLiteral(it.value());
|
|
|
|
|
}
|
|
|
|
|
str += '}';
|
|
|
|
|
return str;
|
|
|
|
|
}
|
|
|
|
|
if (val.type() == QVariant::Bool)
|
|
|
|
|
return toJSLiteral(val.toBool());
|
|
|
|
|
if (val.canConvert(QVariant::String))
|
|
|
|
|
return toJSLiteral(val.toString());
|
|
|
|
|
return QString::fromLatin1("Unconvertible type %1").arg(QLatin1String(val.typeName()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static QbsProfileManager *m_instance = nullptr;
|
|
|
|
|
|
|
|
|
|
static QString kitNameKeyInQbsSettings(const ProjectExplorer::Kit *kit)
|
|
|
|
|
{
|
|
|
|
|
return "preferences.qtcreator.kit." + kit->id().toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QbsProfileManager::QbsProfileManager() : m_defaultPropertyProvider(new DefaultPropertyProvider)
|
|
|
|
|
{
|
|
|
|
|
m_instance = this;
|
|
|
|
|
|
|
|
|
|
setObjectName(QLatin1String("QbsProjectManager"));
|
|
|
|
|
connect(ProjectExplorer::KitManager::instance(), &ProjectExplorer::KitManager::kitsLoaded, this,
|
|
|
|
|
[this]() { m_kitsToBeSetupForQbs = ProjectExplorer::KitManager::kits(); } );
|
|
|
|
|
connect(ProjectExplorer::KitManager::instance(), &ProjectExplorer::KitManager::kitAdded, this,
|
|
|
|
|
&QbsProfileManager::addProfileFromKit);
|
|
|
|
|
connect(ProjectExplorer::KitManager::instance(), &ProjectExplorer::KitManager::kitUpdated, this,
|
|
|
|
|
&QbsProfileManager::handleKitUpdate);
|
|
|
|
|
connect(ProjectExplorer::KitManager::instance(), &ProjectExplorer::KitManager::kitRemoved, this,
|
|
|
|
|
&QbsProfileManager::handleKitRemoval);
|
|
|
|
|
connect(&QbsSettings::instance(), &QbsSettings::settingsChanged,
|
|
|
|
|
this, &QbsProfileManager::updateAllProfiles);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QbsProfileManager::~QbsProfileManager()
|
|
|
|
|
{
|
|
|
|
|
delete m_defaultPropertyProvider;
|
|
|
|
|
m_instance = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QbsProfileManager *QbsProfileManager::instance()
|
|
|
|
|
{
|
|
|
|
|
return m_instance;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-05 15:15:35 +01:00
|
|
|
QString QbsProfileManager::ensureProfileForKit(const ProjectExplorer::Kit *k)
|
2019-06-28 14:30:32 +02:00
|
|
|
{
|
|
|
|
|
if (!k)
|
|
|
|
|
return QString();
|
2020-11-18 22:42:51 +01:00
|
|
|
updateProfileIfNecessary(k);
|
2019-12-05 15:15:35 +01:00
|
|
|
return profileNameForKit(k);
|
2019-06-28 14:30:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QbsProfileManager::updateProfileIfNecessary(const ProjectExplorer::Kit *kit)
|
|
|
|
|
{
|
|
|
|
|
// kit in list <=> profile update is necessary
|
|
|
|
|
// Note that the const_cast is safe, as we do not call any non-const methods on the object.
|
|
|
|
|
if (m_instance->m_kitsToBeSetupForQbs.removeOne(const_cast<ProjectExplorer::Kit *>(kit)))
|
|
|
|
|
m_instance->addProfileFromKit(kit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QbsProfileManager::updateAllProfiles()
|
|
|
|
|
{
|
|
|
|
|
for (const auto * const kit : ProjectExplorer::KitManager::kits())
|
|
|
|
|
addProfileFromKit(kit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QbsProfileManager::addProfileFromKit(const ProjectExplorer::Kit *k)
|
|
|
|
|
{
|
2019-12-05 15:15:35 +01:00
|
|
|
const QString name = profileNameForKit(k);
|
2019-06-28 14:30:32 +02:00
|
|
|
runQbsConfig(QbsConfigOp::Unset, "profiles." + name);
|
2021-06-24 12:13:26 +02:00
|
|
|
runQbsConfig(QbsConfigOp::Set, kitNameKeyInQbsSettings(k), name);
|
2019-06-28 14:30:32 +02:00
|
|
|
|
|
|
|
|
// set up properties:
|
|
|
|
|
QVariantMap data = m_defaultPropertyProvider->properties(k, QVariantMap());
|
2021-02-15 10:03:57 +01:00
|
|
|
for (PropertyProvider *provider : qAsConst(g_propertyProviders)) {
|
2019-06-28 14:30:32 +02:00
|
|
|
if (provider->canHandle(k))
|
|
|
|
|
data = provider->properties(k, data);
|
|
|
|
|
}
|
2022-01-21 16:06:36 +01:00
|
|
|
if (const QtSupport::QtVersion * const qt = QtSupport::QtKitAspect::qtVersion(k))
|
2021-07-13 09:41:26 +02:00
|
|
|
data.insert("moduleProviders.Qt.qmakeFilePaths", qt->qmakeFilePath().toString());
|
2019-06-28 14:30:32 +02:00
|
|
|
|
2021-06-24 12:49:17 +02:00
|
|
|
if (QbsSettings::qbsVersion() < QVersionNumber({1, 20})) {
|
|
|
|
|
const QString keyPrefix = "profiles." + name + ".";
|
|
|
|
|
for (auto it = data.begin(); it != data.end(); ++it)
|
|
|
|
|
runQbsConfig(QbsConfigOp::Set, keyPrefix + it.key(), it.value());
|
|
|
|
|
} else {
|
|
|
|
|
runQbsConfig(QbsConfigOp::AddProfile, name, data);
|
|
|
|
|
}
|
2021-06-24 12:13:26 +02:00
|
|
|
emit qbsProfilesUpdated();
|
2019-06-28 14:30:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QbsProfileManager::handleKitUpdate(ProjectExplorer::Kit *kit)
|
|
|
|
|
{
|
|
|
|
|
m_kitsToBeSetupForQbs.removeOne(kit);
|
|
|
|
|
addProfileFromKit(kit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QbsProfileManager::handleKitRemoval(ProjectExplorer::Kit *kit)
|
|
|
|
|
{
|
|
|
|
|
m_kitsToBeSetupForQbs.removeOne(kit);
|
|
|
|
|
runQbsConfig(QbsConfigOp::Unset, kitNameKeyInQbsSettings(kit));
|
2019-12-05 15:15:35 +01:00
|
|
|
runQbsConfig(QbsConfigOp::Unset, "profiles." + profileNameForKit(kit));
|
2019-06-28 14:30:32 +02:00
|
|
|
emit qbsProfilesUpdated();
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-05 15:15:35 +01:00
|
|
|
QString QbsProfileManager::profileNameForKit(const ProjectExplorer::Kit *kit)
|
|
|
|
|
{
|
|
|
|
|
if (!kit)
|
|
|
|
|
return QString();
|
|
|
|
|
return QString::fromLatin1("qtc_%1_%2").arg(kit->fileSystemFriendlyName().left(8),
|
|
|
|
|
QString::fromLatin1(QCryptographicHash::hash(
|
|
|
|
|
kit->id().name(), QCryptographicHash::Sha1).toHex().left(8)));
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-28 14:30:32 +02:00
|
|
|
QString QbsProfileManager::runQbsConfig(QbsConfigOp op, const QString &key, const QVariant &value)
|
|
|
|
|
{
|
2021-11-03 15:48:22 +01:00
|
|
|
Utils::QtcProcess qbsConfig;
|
2019-06-28 14:30:32 +02:00
|
|
|
QStringList args("config");
|
|
|
|
|
if (QbsSettings::useCreatorSettingsDirForQbs())
|
|
|
|
|
args << "--settings-dir" << QbsSettings::qbsSettingsBaseDir();
|
|
|
|
|
switch (op) {
|
|
|
|
|
case QbsConfigOp::Get:
|
|
|
|
|
args << key;
|
|
|
|
|
break;
|
|
|
|
|
case QbsConfigOp::Set:
|
|
|
|
|
args << key << toJSLiteral(value);
|
|
|
|
|
break;
|
|
|
|
|
case QbsConfigOp::Unset:
|
|
|
|
|
args << "--unset" << key;
|
|
|
|
|
break;
|
2021-06-24 12:49:17 +02:00
|
|
|
case QbsConfigOp::AddProfile: {
|
|
|
|
|
args << "--add-profile" << key;
|
|
|
|
|
const QVariantMap props = value.toMap();
|
|
|
|
|
for (auto it = props.begin(); it != props.end(); ++it)
|
|
|
|
|
args << it.key() << toJSLiteral(it.value());
|
2021-08-09 13:23:44 +02:00
|
|
|
if (props.isEmpty()) // Make sure we still create a profile for "empty" kits.
|
|
|
|
|
args << "qbs.optimization" << toJSLiteral(QString("none"));
|
2021-06-24 12:49:17 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2019-06-28 14:30:32 +02:00
|
|
|
}
|
2019-12-17 18:25:21 +01:00
|
|
|
const Utils::FilePath qbsExe = QbsSettings::qbsExecutableFilePath();
|
|
|
|
|
if (qbsExe.isEmpty() || !qbsExe.exists())
|
|
|
|
|
return {};
|
2021-11-03 15:48:22 +01:00
|
|
|
qbsConfig.setCommand({qbsExe, args});
|
|
|
|
|
qbsConfig.start();
|
2019-06-28 14:30:32 +02:00
|
|
|
if (!qbsConfig.waitForStarted(3000) || !qbsConfig.waitForFinished(5000)) {
|
2020-12-16 14:59:25 +01:00
|
|
|
Core::MessageManager::writeFlashing(
|
|
|
|
|
tr("Failed to run qbs config: %1").arg(qbsConfig.errorString()));
|
2019-06-28 14:30:32 +02:00
|
|
|
} else if (qbsConfig.exitCode() != 0) {
|
2020-12-16 14:59:25 +01:00
|
|
|
Core::MessageManager::writeFlashing(
|
|
|
|
|
tr("Failed to run qbs config: %1")
|
|
|
|
|
.arg(QString::fromLocal8Bit(qbsConfig.readAllStandardError())));
|
2019-06-28 14:30:32 +02:00
|
|
|
}
|
|
|
|
|
return QString::fromLocal8Bit(qbsConfig.readAllStandardOutput()).trimmed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVariant fromJSLiteral(const QString &str)
|
|
|
|
|
{
|
|
|
|
|
QJSEngine engine;
|
|
|
|
|
QJSValue sv = engine.evaluate("(function(){return " + str + ";})()");
|
|
|
|
|
return sv.isError() ? str : sv.toVariant();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace QbsProjectManager
|