forked from qt-creator/qt-creator
QbsProjectManager: Switch to an out-of-process approach
That is, do not link to the qbscore library anymore. Instead, use the JSON-based API. Advantages: - We can build Qt Creator with qbs support without qbs being present on the build machine. - Smaller memory footprint for Qt Creator, as the qbs build graphs are now being managed by a separate process. - Potential crashes in qbs will not kill the Qt Creator process. Fixes: QTCREATORBUG-20622 Task-number: QTCREATORBUG-22904 Change-Id: If7d344b0ac65a99ff0a3a3db215d61b8d903e47e Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
This commit is contained in:
@@ -1,10 +1,6 @@
|
||||
find_package(Qbs)
|
||||
|
||||
add_qtc_plugin(QbsProjectManager
|
||||
CONDITION TARGET Qbs::QbsCore
|
||||
DEPENDS Qbs::QbsCore Qt5::Widgets qmljs
|
||||
DEPENDS Qt5::Qml Qt5::Widgets qmljs
|
||||
DEFINES
|
||||
QBS_INSTALL_DIR="${QBS_INSTALL_DIR}"
|
||||
IDE_LIBRARY_BASENAME="${IDE_LIBRARY_BASE_PATH}"
|
||||
PLUGIN_DEPENDS Core ProjectExplorer CppTools QtSupport QmlJSTools
|
||||
SOURCES
|
||||
@@ -17,19 +13,20 @@ add_qtc_plugin(QbsProjectManager
|
||||
qbscleanstepconfigwidget.ui
|
||||
qbsinstallstep.cpp qbsinstallstep.h
|
||||
qbskitinformation.cpp qbskitinformation.h
|
||||
qbslogsink.cpp qbslogsink.h
|
||||
qbsnodes.cpp qbsnodes.h
|
||||
qbsnodetreebuilder.cpp qbsnodetreebuilder.h
|
||||
qbsparser.cpp qbsparser.h
|
||||
qbspmlogging.cpp qbspmlogging.h
|
||||
qbsprofilemanager.cpp qbsprofilemanager.h
|
||||
qbsprofilessettingspage.cpp qbsprofilessettingspage.h
|
||||
qbsprofilessettingswidget.ui
|
||||
qbsproject.cpp qbsproject.h
|
||||
qbsprojectimporter.cpp qbsprojectimporter.h
|
||||
qbsprojectmanager.cpp qbsprojectmanager.h qbsprojectmanager.qrc
|
||||
qbsprojectmanager.qrc
|
||||
qbsprojectmanager_global.h
|
||||
qbsprojectmanagerconstants.h
|
||||
qbsprojectmanagerplugin.cpp qbsprojectmanagerplugin.h
|
||||
qbsprojectmanagersettings.cpp qbsprojectmanagersettings.h
|
||||
qbsprojectparser.cpp qbsprojectparser.h
|
||||
qbssession.cpp qbssession.h
|
||||
qbssettings.cpp qbssettings.h
|
||||
)
|
||||
|
@@ -26,7 +26,7 @@
|
||||
#include "customqbspropertiesdialog.h"
|
||||
#include "ui_customqbspropertiesdialog.h"
|
||||
|
||||
#include <qbs.h>
|
||||
#include "qbsprofilemanager.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
@@ -49,7 +49,7 @@ CustomQbsPropertiesDialog::CustomQbsPropertiesDialog(const QVariantMap &properti
|
||||
nameItem->setData(Qt::DisplayRole, it.key());
|
||||
m_ui->propertiesTable->setItem(currentRow, 0, nameItem);
|
||||
auto * const valueItem = new QTableWidgetItem;
|
||||
valueItem->setData(Qt::DisplayRole, qbs::settingsValueToRepresentation(it.value()));
|
||||
valueItem->setData(Qt::DisplayRole, toJSLiteral(it.value()));
|
||||
m_ui->propertiesTable->setItem(currentRow, 1, valueItem);
|
||||
++currentRow;
|
||||
}
|
||||
@@ -70,8 +70,7 @@ QVariantMap CustomQbsPropertiesDialog::properties() const
|
||||
const QString name = nameItem->text();
|
||||
if (name.isEmpty())
|
||||
continue;
|
||||
const QString rawString = m_ui->propertiesTable->item(row, 1)->text();
|
||||
properties.insert(name, qbs::representationToSettingsValue(rawString));
|
||||
properties.insert(name, fromJSLiteral(m_ui->propertiesTable->item(row, 1)->text()));
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
@@ -41,8 +41,6 @@
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <qbs.h>
|
||||
|
||||
#include <android/androidconstants.h>
|
||||
#include <ios/iosconstants.h>
|
||||
#include <qtsupport/baseqtversion.h>
|
||||
|
@@ -30,7 +30,7 @@
|
||||
#include "qbsinstallstep.h"
|
||||
#include "qbsproject.h"
|
||||
#include "qbsprojectmanagerconstants.h"
|
||||
#include "qbsprojectmanagersettings.h"
|
||||
#include "qbssettings.h"
|
||||
|
||||
#include <coreplugin/documentmanager.h>
|
||||
|
||||
@@ -167,7 +167,7 @@ bool QbsBuildConfiguration::fromMap(const QVariantMap &map)
|
||||
return false;
|
||||
|
||||
if (m_configurationName->value().isEmpty()) { // pre-4.4 backwards compatibility
|
||||
const QString profileName = QbsManager::profileForKit(target()->kit());
|
||||
const QString profileName = QbsProfileManager::profileForKit(target()->kit());
|
||||
const QString buildVariant = qbsConfiguration()
|
||||
.value(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)).toString();
|
||||
m_configurationName->setValue(profileName + '-' + buildVariant);
|
||||
@@ -328,9 +328,9 @@ QString QbsBuildConfiguration::equivalentCommandLine(const BuildStep *buildStep)
|
||||
const QString buildDir = buildDirectory().toUserOutput();
|
||||
commandLine.addArgs({"-d", buildDir});
|
||||
commandLine.addArgs({"-f", buildStep->project()->projectFilePath().toUserOutput()});
|
||||
if (QbsProjectManagerSettings::useCreatorSettingsDirForQbs()) {
|
||||
if (QbsSettings::useCreatorSettingsDirForQbs()) {
|
||||
commandLine.addArgs({"--settings-dir",
|
||||
QDir::toNativeSeparators(QbsProjectManagerSettings::qbsSettingsBaseDir())});
|
||||
QDir::toNativeSeparators(QbsSettings::qbsSettingsBaseDir())});
|
||||
}
|
||||
if (stepProxy.dryRun())
|
||||
commandLine.addArg("--dry-run");
|
||||
@@ -350,7 +350,7 @@ QString QbsBuildConfiguration::equivalentCommandLine(const BuildStep *buildStep)
|
||||
if (jobCount > 0)
|
||||
commandLine.addArgs({"--jobs", QString::number(jobCount)});
|
||||
|
||||
const QString profileName = QbsManager::profileForKit(buildStep->target()->kit());
|
||||
const QString profileName = QbsProfileManager::profileForKit(buildStep->target()->kit());
|
||||
const QString buildVariant = qbsConfiguration()
|
||||
.value(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)).toString();
|
||||
commandLine.addArg("config:" + configurationName());
|
||||
|
@@ -29,7 +29,8 @@
|
||||
#include "qbsparser.h"
|
||||
#include "qbsproject.h"
|
||||
#include "qbsprojectmanagerconstants.h"
|
||||
#include "qbsprojectmanagersettings.h"
|
||||
#include "qbssession.h"
|
||||
#include "qbssettings.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/variablechooser.h>
|
||||
@@ -48,11 +49,12 @@
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QFormLayout>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QLabel>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QSpinBox>
|
||||
|
||||
#include <qbs.h>
|
||||
#include <QThread>
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Constants:
|
||||
@@ -161,16 +163,14 @@ QbsBuildStep::QbsBuildStep(ProjectExplorer::BuildStepList *bsl) :
|
||||
QbsBuildStep::~QbsBuildStep()
|
||||
{
|
||||
doCancel();
|
||||
if (m_job) {
|
||||
m_job->deleteLater();
|
||||
m_job = nullptr;
|
||||
}
|
||||
if (m_session)
|
||||
m_session->disconnect(this);
|
||||
delete m_parser;
|
||||
}
|
||||
|
||||
bool QbsBuildStep::init()
|
||||
{
|
||||
if (qbsBuildSystem()->isParsing() || m_job)
|
||||
if (qbsBuildSystem()->isParsing() || m_session)
|
||||
return false;
|
||||
|
||||
auto bc = static_cast<QbsBuildConfiguration *>(buildConfiguration());
|
||||
@@ -201,6 +201,7 @@ void QbsBuildStep::doRun()
|
||||
{
|
||||
// We need a pre-build parsing step in order not to lose project file changes done
|
||||
// right before building (but before the delay has elapsed).
|
||||
m_parsingAfterBuild = false;
|
||||
parseProject();
|
||||
}
|
||||
|
||||
@@ -213,8 +214,8 @@ void QbsBuildStep::doCancel()
|
||||
{
|
||||
if (m_parsingProject)
|
||||
qbsBuildSystem()->cancelParsing();
|
||||
else if (m_job)
|
||||
m_job->cancel();
|
||||
else if (m_session)
|
||||
m_session->cancelCurrentJob();
|
||||
}
|
||||
|
||||
QVariantMap QbsBuildStep::qbsConfiguration(VariableHandling variableHandling) const
|
||||
@@ -230,7 +231,7 @@ QVariantMap QbsBuildStep::qbsConfiguration(VariableHandling variableHandling) co
|
||||
for (auto it = config.begin(), end = config.end(); it != end; ++it) {
|
||||
const QString rawString = it.value().toString();
|
||||
const QString expandedString = expander->expand(rawString);
|
||||
it.value() = qbs::representationToSettingsValue(expandedString);
|
||||
it.value() = expandedString;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
@@ -252,26 +253,6 @@ void QbsBuildStep::setQbsConfiguration(const QVariantMap &config)
|
||||
emit qbsConfigurationChanged();
|
||||
}
|
||||
|
||||
bool QbsBuildStep::keepGoing() const
|
||||
{
|
||||
return m_qbsBuildOptions.keepGoing();
|
||||
}
|
||||
|
||||
bool QbsBuildStep::showCommandLines() const
|
||||
{
|
||||
return m_qbsBuildOptions.echoMode() == qbs::CommandEchoModeCommandLine;
|
||||
}
|
||||
|
||||
bool QbsBuildStep::install() const
|
||||
{
|
||||
return m_qbsBuildOptions.install();
|
||||
}
|
||||
|
||||
bool QbsBuildStep::cleanInstallRoot() const
|
||||
{
|
||||
return m_qbsBuildOptions.removeExistingInstallation();
|
||||
}
|
||||
|
||||
bool QbsBuildStep::hasCustomInstallRoot() const
|
||||
{
|
||||
return m_qbsConfiguration.contains(Constants::QBS_INSTALL_ROOT_KEY);
|
||||
@@ -286,15 +267,14 @@ Utils::FilePath QbsBuildStep::installRoot(VariableHandling variableHandling) con
|
||||
|
||||
const QbsBuildConfiguration * const bc
|
||||
= static_cast<QbsBuildConfiguration *>(buildConfiguration());
|
||||
return bc->buildDirectory().pathAppended(bc->configurationName())
|
||||
.pathAppended(qbs::InstallOptions::defaultInstallRoot());
|
||||
return bc->buildDirectory().pathAppended(bc->configurationName()).pathAppended("install-root");
|
||||
}
|
||||
|
||||
int QbsBuildStep::maxJobs() const
|
||||
{
|
||||
if (m_qbsBuildOptions.maxJobCount() > 0)
|
||||
return m_qbsBuildOptions.maxJobCount();
|
||||
return qbs::BuildOptions::defaultMaxJobCount();
|
||||
if (m_maxJobCount > 0)
|
||||
return m_maxJobCount;
|
||||
return QThread::idealThreadCount();
|
||||
}
|
||||
|
||||
static QString forceProbesKey() { return QLatin1String("Qbs.forceProbesKey"); }
|
||||
@@ -306,15 +286,11 @@ bool QbsBuildStep::fromMap(const QVariantMap &map)
|
||||
return false;
|
||||
|
||||
setQbsConfiguration(map.value(QBS_CONFIG).toMap());
|
||||
m_qbsBuildOptions.setDryRun(map.value(QBS_DRY_RUN).toBool());
|
||||
m_qbsBuildOptions.setKeepGoing(map.value(QBS_KEEP_GOING).toBool());
|
||||
m_qbsBuildOptions.setMaxJobCount(map.value(QBS_MAXJOBCOUNT).toInt());
|
||||
const bool showCommandLines = map.value(QBS_SHOWCOMMANDLINES).toBool();
|
||||
m_qbsBuildOptions.setEchoMode(showCommandLines ? qbs::CommandEchoModeCommandLine
|
||||
: qbs::CommandEchoModeSummary);
|
||||
m_qbsBuildOptions.setInstall(map.value(QBS_INSTALL, true).toBool());
|
||||
m_qbsBuildOptions.setRemoveExistingInstallation(map.value(QBS_CLEAN_INSTALL_ROOT)
|
||||
.toBool());
|
||||
m_keepGoing = map.value(QBS_KEEP_GOING).toBool();
|
||||
m_maxJobCount = map.value(QBS_MAXJOBCOUNT).toInt();
|
||||
m_showCommandLines = map.value(QBS_SHOWCOMMANDLINES).toBool();
|
||||
m_install = map.value(QBS_INSTALL, true).toBool();
|
||||
m_cleanInstallDir = map.value(QBS_CLEAN_INSTALL_ROOT).toBool();
|
||||
m_forceProbes = map.value(forceProbesKey()).toBool();
|
||||
m_enableQmlDebugging = map.value(enableQmlDebuggingKey()).toBool();
|
||||
return true;
|
||||
@@ -324,43 +300,47 @@ QVariantMap QbsBuildStep::toMap() const
|
||||
{
|
||||
QVariantMap map = ProjectExplorer::BuildStep::toMap();
|
||||
map.insert(QBS_CONFIG, m_qbsConfiguration);
|
||||
map.insert(QBS_DRY_RUN, m_qbsBuildOptions.dryRun());
|
||||
map.insert(QBS_KEEP_GOING, m_qbsBuildOptions.keepGoing());
|
||||
map.insert(QBS_MAXJOBCOUNT, m_qbsBuildOptions.maxJobCount());
|
||||
map.insert(QBS_SHOWCOMMANDLINES,
|
||||
m_qbsBuildOptions.echoMode() == qbs::CommandEchoModeCommandLine);
|
||||
map.insert(QBS_INSTALL, m_qbsBuildOptions.install());
|
||||
map.insert(QBS_CLEAN_INSTALL_ROOT,
|
||||
m_qbsBuildOptions.removeExistingInstallation());
|
||||
map.insert(QBS_KEEP_GOING, m_keepGoing);
|
||||
map.insert(QBS_MAXJOBCOUNT, m_maxJobCount);
|
||||
map.insert(QBS_SHOWCOMMANDLINES, m_showCommandLines);
|
||||
map.insert(QBS_INSTALL, m_install);
|
||||
map.insert(QBS_CLEAN_INSTALL_ROOT, m_cleanInstallDir);
|
||||
map.insert(forceProbesKey(), m_forceProbes);
|
||||
map.insert(enableQmlDebuggingKey(), m_enableQmlDebugging);
|
||||
return map;
|
||||
}
|
||||
|
||||
void QbsBuildStep::buildingDone(bool success)
|
||||
void QbsBuildStep::buildingDone(const ErrorInfo &error)
|
||||
{
|
||||
m_lastWasSuccess = success;
|
||||
// Report errors:
|
||||
foreach (const qbs::ErrorItem &item, m_job->error().items())
|
||||
createTaskAndOutput(ProjectExplorer::Task::Error, item.description(),
|
||||
item.codeLocation().filePath(), item.codeLocation().line());
|
||||
m_session->disconnect(this);
|
||||
m_session = nullptr;
|
||||
m_lastWasSuccess = !error.hasError();
|
||||
for (const ErrorInfoItem &item : qAsConst(error.items)) {
|
||||
createTaskAndOutput(
|
||||
ProjectExplorer::Task::Error,
|
||||
item.description,
|
||||
item.filePath.toString(),
|
||||
item.line);
|
||||
}
|
||||
|
||||
// Building can uncover additional target artifacts.
|
||||
qbsBuildSystem()->updateAfterBuild();
|
||||
|
||||
// The reparsing, if it is necessary, has to be done before finished() is emitted, as
|
||||
// otherwise a potential additional build step could conflict with the parsing step.
|
||||
if (qbsBuildSystem()->parsingScheduled())
|
||||
if (qbsBuildSystem()->parsingScheduled()) {
|
||||
m_parsingAfterBuild = true;
|
||||
parseProject();
|
||||
else
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
void QbsBuildStep::reparsingDone(bool success)
|
||||
{
|
||||
disconnect(target(), &Target::parsingFinished, this, &QbsBuildStep::reparsingDone);
|
||||
m_parsingProject = false;
|
||||
if (m_job) { // This was a scheduled reparsing after building.
|
||||
if (m_parsingAfterBuild) {
|
||||
finish();
|
||||
} else if (!success) {
|
||||
m_lastWasSuccess = false;
|
||||
@@ -382,30 +362,31 @@ void QbsBuildStep::handleProgress(int value)
|
||||
emit progress(value * 100 / m_maxProgress, m_currentTask);
|
||||
}
|
||||
|
||||
void QbsBuildStep::handleCommandDescriptionReport(const QString &highlight, const QString &message)
|
||||
void QbsBuildStep::handleCommandDescription(const QString &message)
|
||||
{
|
||||
Q_UNUSED(highlight)
|
||||
emit addOutput(message, OutputFormat::Stdout);
|
||||
}
|
||||
|
||||
void QbsBuildStep::handleProcessResultReport(const qbs::ProcessResult &result)
|
||||
void QbsBuildStep::handleProcessResult(
|
||||
const FilePath &executable,
|
||||
const QStringList &arguments,
|
||||
const FilePath &workingDir,
|
||||
const QStringList &stdOut,
|
||||
const QStringList &stdErr,
|
||||
bool success)
|
||||
{
|
||||
bool hasOutput = !result.stdOut().isEmpty() || !result.stdErr().isEmpty();
|
||||
|
||||
if (result.success() && !hasOutput)
|
||||
const bool hasOutput = !stdOut.isEmpty() || !stdErr.isEmpty();
|
||||
if (success && !hasOutput)
|
||||
return;
|
||||
|
||||
m_parser->setWorkingDirectory(result.workingDirectory());
|
||||
|
||||
QString commandline = result.executableFilePath() + ' '
|
||||
+ Utils::QtcProcess::joinArgs(result.arguments());
|
||||
emit addOutput(commandline, OutputFormat::Stdout);
|
||||
|
||||
foreach (const QString &line, result.stdErr()) {
|
||||
m_parser->setWorkingDirectory(workingDir.toString());
|
||||
emit addOutput(executable.toUserOutput() + ' ' + QtcProcess::joinArgs(arguments),
|
||||
OutputFormat::Stdout);
|
||||
for (const QString &line : stdErr) {
|
||||
m_parser->stdError(line);
|
||||
emit addOutput(line, OutputFormat::Stderr);
|
||||
}
|
||||
foreach (const QString &line, result.stdOut()) {
|
||||
for (const QString &line : stdOut) {
|
||||
m_parser->stdOutput(line);
|
||||
emit addOutput(line, OutputFormat::Stdout);
|
||||
}
|
||||
@@ -449,17 +430,17 @@ QString QbsBuildStep::profile() const
|
||||
|
||||
void QbsBuildStep::setKeepGoing(bool kg)
|
||||
{
|
||||
if (m_qbsBuildOptions.keepGoing() == kg)
|
||||
if (m_keepGoing == kg)
|
||||
return;
|
||||
m_qbsBuildOptions.setKeepGoing(kg);
|
||||
m_keepGoing = kg;
|
||||
emit qbsBuildOptionsChanged();
|
||||
}
|
||||
|
||||
void QbsBuildStep::setMaxJobs(int jobcount)
|
||||
{
|
||||
if (m_qbsBuildOptions.maxJobCount() == jobcount)
|
||||
if (m_maxJobCount == jobcount)
|
||||
return;
|
||||
m_qbsBuildOptions.setMaxJobCount(jobcount);
|
||||
m_maxJobCount = jobcount;
|
||||
emit qbsBuildOptionsChanged();
|
||||
}
|
||||
|
||||
@@ -467,24 +448,23 @@ void QbsBuildStep::setShowCommandLines(bool show)
|
||||
{
|
||||
if (showCommandLines() == show)
|
||||
return;
|
||||
m_qbsBuildOptions.setEchoMode(show ? qbs::CommandEchoModeCommandLine
|
||||
: qbs::CommandEchoModeSummary);
|
||||
m_showCommandLines = show;
|
||||
emit qbsBuildOptionsChanged();
|
||||
}
|
||||
|
||||
void QbsBuildStep::setInstall(bool install)
|
||||
{
|
||||
if (m_qbsBuildOptions.install() == install)
|
||||
if (m_install == install)
|
||||
return;
|
||||
m_qbsBuildOptions.setInstall(install);
|
||||
m_install = install;
|
||||
emit qbsBuildOptionsChanged();
|
||||
}
|
||||
|
||||
void QbsBuildStep::setCleanInstallRoot(bool clean)
|
||||
{
|
||||
if (m_qbsBuildOptions.removeExistingInstallation() == clean)
|
||||
if (m_cleanInstallDir == clean)
|
||||
return;
|
||||
m_qbsBuildOptions.setRemoveExistingInstallation(clean);
|
||||
m_cleanInstallDir = clean;
|
||||
emit qbsBuildOptionsChanged();
|
||||
}
|
||||
|
||||
@@ -497,41 +477,49 @@ void QbsBuildStep::parseProject()
|
||||
|
||||
void QbsBuildStep::build()
|
||||
{
|
||||
qbs::BuildOptions options(m_qbsBuildOptions);
|
||||
options.setChangedFiles(m_changedFiles);
|
||||
options.setFilesToConsider(m_changedFiles);
|
||||
options.setActiveFileTags(m_activeFileTags);
|
||||
options.setLogElapsedTime(!qEnvironmentVariableIsEmpty(Constants::QBS_PROFILING_ENV));
|
||||
|
||||
QString error;
|
||||
m_job = qbsBuildSystem()->build(options, m_products, error);
|
||||
if (!m_job) {
|
||||
emit addOutput(error, OutputFormat::ErrorMessage);
|
||||
m_session = qbsBuildSystem()->session();
|
||||
if (!m_session) {
|
||||
emit addOutput(tr("No qbs session exists for this target."), OutputFormat::ErrorMessage);
|
||||
emit finished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject request;
|
||||
request.insert("type", "build-project");
|
||||
request.insert("max-job-count", maxJobs());
|
||||
request.insert("keep-going", keepGoing());
|
||||
request.insert("command-echo-mode", showCommandLines() ? "command-line" : "summary");
|
||||
request.insert("install", install());
|
||||
QbsSession::insertRequestedModuleProperties(request);
|
||||
request.insert("clean-install-root", cleanInstallRoot());
|
||||
if (!m_products.isEmpty())
|
||||
request.insert("products", QJsonArray::fromStringList(m_products));
|
||||
if (!m_changedFiles.isEmpty()) {
|
||||
const auto changedFilesArray = QJsonArray::fromStringList(m_changedFiles);
|
||||
request.insert("changed-files", changedFilesArray);
|
||||
request.insert("files-to-consider", changedFilesArray);
|
||||
}
|
||||
if (!m_activeFileTags.isEmpty())
|
||||
request.insert("active-file-tags", QJsonArray::fromStringList(m_activeFileTags));
|
||||
request.insert("data-mode", "only-if-changed");
|
||||
|
||||
m_session->sendRequest(request);
|
||||
m_maxProgress = 0;
|
||||
|
||||
connect(m_job, &qbs::AbstractJob::finished, this, &QbsBuildStep::buildingDone);
|
||||
connect(m_job, &qbs::AbstractJob::taskStarted,
|
||||
this, &QbsBuildStep::handleTaskStarted);
|
||||
connect(m_job, &qbs::AbstractJob::taskProgress,
|
||||
this, &QbsBuildStep::handleProgress);
|
||||
connect(m_job, &qbs::BuildJob::reportCommandDescription,
|
||||
this, &QbsBuildStep::handleCommandDescriptionReport);
|
||||
connect(m_job, &qbs::BuildJob::reportProcessResult,
|
||||
this, &QbsBuildStep::handleProcessResultReport);
|
||||
|
||||
connect(m_session, &QbsSession::projectBuilt, this, &QbsBuildStep::buildingDone);
|
||||
connect(m_session, &QbsSession::taskStarted, this, &QbsBuildStep::handleTaskStarted);
|
||||
connect(m_session, &QbsSession::taskProgress, this, &QbsBuildStep::handleProgress);
|
||||
connect(m_session, &QbsSession::commandDescription,
|
||||
this, &QbsBuildStep::handleCommandDescription);
|
||||
connect(m_session, &QbsSession::processResult, this, &QbsBuildStep::handleProcessResult);
|
||||
connect(m_session, &QbsSession::errorOccurred, this, [this] {
|
||||
buildingDone(ErrorInfo(tr("Build canceled: Qbs session failed.")));
|
||||
});
|
||||
}
|
||||
|
||||
void QbsBuildStep::finish()
|
||||
{
|
||||
m_session = nullptr;
|
||||
emit finished(m_lastWasSuccess);
|
||||
if (m_job) {
|
||||
m_job->deleteLater();
|
||||
m_job = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -548,7 +536,7 @@ QbsBuildStepConfigWidget::QbsBuildStepConfigWidget(QbsBuildStep *step) :
|
||||
this, &QbsBuildStepConfigWidget::updateState);
|
||||
connect(step, &QbsBuildStep::qbsBuildOptionsChanged,
|
||||
this, &QbsBuildStepConfigWidget::updateState);
|
||||
connect(&QbsProjectManagerSettings::instance(), &QbsProjectManagerSettings::settingsBaseChanged,
|
||||
connect(&QbsSettings::instance(), &QbsSettings::settingsChanged,
|
||||
this, &QbsBuildStepConfigWidget::updateState);
|
||||
connect(step->buildConfiguration(), &BuildConfiguration::buildDirectoryChanged,
|
||||
this, &QbsBuildStepConfigWidget::updateState);
|
||||
|
@@ -30,13 +30,13 @@
|
||||
#include <projectexplorer/buildstep.h>
|
||||
#include <projectexplorer/task.h>
|
||||
|
||||
#include <qbs.h>
|
||||
|
||||
namespace Utils { class FancyLineEdit; }
|
||||
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
class ErrorInfo;
|
||||
class QbsProject;
|
||||
class QbsSession;
|
||||
|
||||
class QbsBuildStepConfigWidget;
|
||||
|
||||
@@ -61,10 +61,10 @@ public:
|
||||
QVariantMap qbsConfiguration(VariableHandling variableHandling) const;
|
||||
void setQbsConfiguration(const QVariantMap &config);
|
||||
|
||||
bool keepGoing() const;
|
||||
bool showCommandLines() const;
|
||||
bool install() const;
|
||||
bool cleanInstallRoot() const;
|
||||
bool keepGoing() const { return m_keepGoing; }
|
||||
bool showCommandLines() const { return m_showCommandLines; }
|
||||
bool install() const { return m_install; }
|
||||
bool cleanInstallRoot() const { return m_cleanInstallDir; }
|
||||
bool hasCustomInstallRoot() const;
|
||||
Utils::FilePath installRoot(VariableHandling variableHandling = ExpandVariables) const;
|
||||
int maxJobs() const;
|
||||
@@ -93,12 +93,18 @@ private:
|
||||
bool fromMap(const QVariantMap &map) override;
|
||||
QVariantMap toMap() const override;
|
||||
|
||||
void buildingDone(bool success);
|
||||
void buildingDone(const ErrorInfo &error);
|
||||
void reparsingDone(bool success);
|
||||
void handleTaskStarted(const QString &desciption, int max);
|
||||
void handleProgress(int value);
|
||||
void handleCommandDescriptionReport(const QString &highlight, const QString &message);
|
||||
void handleProcessResultReport(const qbs::ProcessResult &result);
|
||||
void handleCommandDescription(const QString &message);
|
||||
void handleProcessResult(
|
||||
const Utils::FilePath &executable,
|
||||
const QStringList &arguments,
|
||||
const Utils::FilePath &workingDir,
|
||||
const QStringList &stdOut,
|
||||
const QStringList &stdErr,
|
||||
bool success);
|
||||
|
||||
void createTaskAndOutput(ProjectExplorer::Task::TaskType type,
|
||||
const QString &message, const QString &file, int line);
|
||||
@@ -117,7 +123,11 @@ private:
|
||||
void finish();
|
||||
|
||||
QVariantMap m_qbsConfiguration;
|
||||
qbs::BuildOptions m_qbsBuildOptions;
|
||||
int m_maxJobCount = 0;
|
||||
bool m_keepGoing = false;
|
||||
bool m_showCommandLines = false;
|
||||
bool m_install = true;
|
||||
bool m_cleanInstallDir = false;
|
||||
bool m_forceProbes = false;
|
||||
bool m_enableQmlDebugging;
|
||||
|
||||
@@ -126,12 +136,14 @@ private:
|
||||
QStringList m_activeFileTags;
|
||||
QStringList m_products;
|
||||
|
||||
qbs::BuildJob *m_job = nullptr;
|
||||
QbsSession *m_session = nullptr;
|
||||
|
||||
QString m_currentTask;
|
||||
int m_maxProgress;
|
||||
bool m_lastWasSuccess;
|
||||
ProjectExplorer::IOutputParser *m_parser = nullptr;
|
||||
bool m_parsingProject = false;
|
||||
bool m_parsingAfterBuild = false;
|
||||
|
||||
friend class QbsBuildStepConfigWidget;
|
||||
};
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include "qbsbuildconfiguration.h"
|
||||
#include "qbsproject.h"
|
||||
#include "qbsprojectmanagerconstants.h"
|
||||
#include "qbssession.h"
|
||||
|
||||
#include <projectexplorer/buildsteplist.h>
|
||||
#include <projectexplorer/kit.h>
|
||||
@@ -36,6 +37,9 @@
|
||||
#include <projectexplorer/target.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace QbsProjectManager {
|
||||
@@ -73,66 +77,60 @@ QbsCleanStep::QbsCleanStep(ProjectExplorer::BuildStepList *bsl) :
|
||||
QbsCleanStep::~QbsCleanStep()
|
||||
{
|
||||
doCancel();
|
||||
if (m_job) {
|
||||
m_job->deleteLater();
|
||||
m_job = nullptr;
|
||||
}
|
||||
if (m_session)
|
||||
m_session->disconnect(this);
|
||||
}
|
||||
|
||||
bool QbsCleanStep::init()
|
||||
{
|
||||
if (buildSystem()->isParsing() || m_job)
|
||||
if (buildSystem()->isParsing() || m_session)
|
||||
return false;
|
||||
|
||||
auto bc = static_cast<QbsBuildConfiguration *>(buildConfiguration());
|
||||
|
||||
const auto bc = static_cast<QbsBuildConfiguration *>(buildConfiguration());
|
||||
if (!bc)
|
||||
return false;
|
||||
|
||||
m_products = bc->products();
|
||||
return true;
|
||||
}
|
||||
|
||||
void QbsCleanStep::doRun()
|
||||
{
|
||||
qbs::CleanOptions options;
|
||||
options.setDryRun(m_dryRunAspect->value());
|
||||
options.setKeepGoing(m_keepGoingAspect->value());
|
||||
|
||||
QString error;
|
||||
m_job = qbsBuildSystem()->clean(options, m_products, error);
|
||||
if (!m_job) {
|
||||
emit addOutput(error, OutputFormat::ErrorMessage);
|
||||
m_session = static_cast<QbsBuildSystem*>(buildSystem())->session();
|
||||
if (!m_session) {
|
||||
emit addOutput(tr("No qbs session exists for this target."), OutputFormat::ErrorMessage);
|
||||
emit finished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject request;
|
||||
request.insert("type", "clean-project");
|
||||
if (!m_products.isEmpty())
|
||||
request.insert("products", QJsonArray::fromStringList(m_products));
|
||||
request.insert("dry-run", m_dryRunAspect->value());
|
||||
request.insert("keep-going", m_keepGoingAspect->value());
|
||||
m_session->sendRequest(request);
|
||||
m_maxProgress = 0;
|
||||
|
||||
connect(m_job, &qbs::AbstractJob::finished, this, &QbsCleanStep::cleaningDone);
|
||||
connect(m_job, &qbs::AbstractJob::taskStarted,
|
||||
this, &QbsCleanStep::handleTaskStarted);
|
||||
connect(m_job, &qbs::AbstractJob::taskProgress,
|
||||
this, &QbsCleanStep::handleProgress);
|
||||
connect(m_session, &QbsSession::projectCleaned, this, &QbsCleanStep::cleaningDone);
|
||||
connect(m_session, &QbsSession::taskStarted, this, &QbsCleanStep::handleTaskStarted);
|
||||
connect(m_session, &QbsSession::taskProgress, this, &QbsCleanStep::handleProgress);
|
||||
connect(m_session, &QbsSession::errorOccurred, this, [this] {
|
||||
cleaningDone(ErrorInfo(tr("Cleaning canceled: Qbs session failed.")));
|
||||
});
|
||||
}
|
||||
|
||||
void QbsCleanStep::doCancel()
|
||||
{
|
||||
if (m_job)
|
||||
m_job->cancel();
|
||||
if (m_session)
|
||||
m_session->cancelCurrentJob();
|
||||
}
|
||||
|
||||
void QbsCleanStep::cleaningDone(bool success)
|
||||
void QbsCleanStep::cleaningDone(const ErrorInfo &error)
|
||||
{
|
||||
// Report errors:
|
||||
foreach (const qbs::ErrorItem &item, m_job->error().items()) {
|
||||
createTaskAndOutput(ProjectExplorer::Task::Error, item.description(),
|
||||
item.codeLocation().filePath(), item.codeLocation().line());
|
||||
}
|
||||
m_session->disconnect(this);
|
||||
m_session = nullptr;
|
||||
|
||||
emit finished(success);
|
||||
m_job->deleteLater();
|
||||
m_job = nullptr;
|
||||
for (const ErrorInfoItem &item : error.items)
|
||||
createTaskAndOutput(Task::Error, item.description, item.filePath.toString(), item.line);
|
||||
emit finished(!error.hasError());
|
||||
}
|
||||
|
||||
void QbsCleanStep::handleTaskStarted(const QString &desciption, int max)
|
||||
@@ -149,8 +147,7 @@ void QbsCleanStep::handleProgress(int value)
|
||||
|
||||
void QbsCleanStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message, const QString &file, int line)
|
||||
{
|
||||
ProjectExplorer::Task task = ProjectExplorer::Task(type, message,
|
||||
Utils::FilePath::fromString(file), line,
|
||||
Task task(type, message, Utils::FilePath::fromString(file), line,
|
||||
ProjectExplorer::Constants::TASK_CATEGORY_COMPILE);
|
||||
emit addTask(task, 1);
|
||||
emit addOutput(message, OutputFormat::Stdout);
|
||||
|
@@ -31,10 +31,10 @@
|
||||
#include <projectexplorer/projectconfigurationaspects.h>
|
||||
#include <projectexplorer/task.h>
|
||||
|
||||
#include <qbs.h>
|
||||
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
class ErrorInfo;
|
||||
class QbsSession;
|
||||
|
||||
class QbsCleanStep : public ProjectExplorer::BuildStep
|
||||
{
|
||||
@@ -52,7 +52,7 @@ private:
|
||||
void doRun() override;
|
||||
void doCancel() override;
|
||||
|
||||
void cleaningDone(bool success);
|
||||
void cleaningDone(const ErrorInfo &error);
|
||||
void handleTaskStarted(const QString &desciption, int max);
|
||||
void handleProgress(int value);
|
||||
|
||||
@@ -65,8 +65,7 @@ private:
|
||||
QbsBuildSystem *qbsBuildSystem() const;
|
||||
|
||||
QStringList m_products;
|
||||
|
||||
qbs::CleanJob *m_job = nullptr;
|
||||
QbsSession *m_session = nullptr;
|
||||
QString m_description;
|
||||
int m_maxProgress;
|
||||
bool m_showCompilerOutput = true;
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include "qbsbuildstep.h"
|
||||
#include "qbsproject.h"
|
||||
#include "qbsprojectmanagerconstants.h"
|
||||
#include "qbssession.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <projectexplorer/buildsteplist.h>
|
||||
@@ -41,6 +42,7 @@
|
||||
#include <QCheckBox>
|
||||
#include <QFileInfo>
|
||||
#include <QFormLayout>
|
||||
#include <QJsonObject>
|
||||
#include <QLabel>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QSpacerItem>
|
||||
@@ -91,45 +93,45 @@ QbsInstallStep::QbsInstallStep(ProjectExplorer::BuildStepList *bsl) :
|
||||
setDisplayName(tr("Qbs Install"));
|
||||
|
||||
const QbsBuildConfiguration * const bc = buildConfig();
|
||||
connect(bc, &QbsBuildConfiguration::qbsConfigurationChanged,
|
||||
this, &QbsInstallStep::handleBuildConfigChanged);
|
||||
connect(bc, &QbsBuildConfiguration::qbsConfigurationChanged, this, &QbsInstallStep::changed);
|
||||
if (bc->qbsStep()) {
|
||||
connect(bc->qbsStep(), &QbsBuildStep::qbsBuildOptionsChanged,
|
||||
this, &QbsInstallStep::handleBuildConfigChanged);
|
||||
this, &QbsInstallStep::changed);
|
||||
}
|
||||
}
|
||||
|
||||
QbsInstallStep::~QbsInstallStep()
|
||||
{
|
||||
doCancel();
|
||||
if (m_job)
|
||||
m_job->deleteLater();
|
||||
m_job = nullptr;
|
||||
if (m_session)
|
||||
m_session->disconnect(this);
|
||||
}
|
||||
|
||||
bool QbsInstallStep::init()
|
||||
{
|
||||
QTC_ASSERT(!buildConfiguration()->buildSystem()->isParsing() && !m_job, return false);
|
||||
QTC_ASSERT(!buildConfiguration()->buildSystem()->isParsing() && !m_session, return false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void QbsInstallStep::doRun()
|
||||
{
|
||||
auto bs = static_cast<QbsBuildSystem *>(buildSystem());
|
||||
m_job = bs->install(m_qbsInstallOptions);
|
||||
m_session = static_cast<QbsBuildSystem *>(buildSystem())->session();
|
||||
|
||||
if (!m_job) {
|
||||
emit finished(false);
|
||||
return;
|
||||
}
|
||||
QJsonObject request;
|
||||
request.insert("type", "install");
|
||||
request.insert("install-root", installRoot());
|
||||
request.insert("clean-install-root", m_cleanInstallRoot);
|
||||
request.insert("keep-going", m_keepGoing);
|
||||
request.insert("dry-run", m_dryRun);
|
||||
m_session->sendRequest(request);
|
||||
|
||||
m_maxProgress = 0;
|
||||
|
||||
connect(m_job, &qbs::AbstractJob::finished, this, &QbsInstallStep::installDone);
|
||||
connect(m_job, &qbs::AbstractJob::taskStarted,
|
||||
this, &QbsInstallStep::handleTaskStarted);
|
||||
connect(m_job, &qbs::AbstractJob::taskProgress,
|
||||
this, &QbsInstallStep::handleProgress);
|
||||
connect(m_session, &QbsSession::projectInstalled, this, &QbsInstallStep::installDone);
|
||||
connect(m_session, &QbsSession::taskStarted, this, &QbsInstallStep::handleTaskStarted);
|
||||
connect(m_session, &QbsSession::taskProgress, this, &QbsInstallStep::handleProgress);
|
||||
connect(m_session, &QbsSession::errorOccurred, this, [this] {
|
||||
installDone(ErrorInfo(tr("Installing canceled: Qbs session failed.")));
|
||||
});
|
||||
}
|
||||
|
||||
ProjectExplorer::BuildStepConfigWidget *QbsInstallStep::createConfigWidget()
|
||||
@@ -139,8 +141,8 @@ ProjectExplorer::BuildStepConfigWidget *QbsInstallStep::createConfigWidget()
|
||||
|
||||
void QbsInstallStep::doCancel()
|
||||
{
|
||||
if (m_job)
|
||||
m_job->cancel();
|
||||
if (m_session)
|
||||
m_session->cancelCurrentJob();
|
||||
}
|
||||
|
||||
QString QbsInstallStep::installRoot() const
|
||||
@@ -149,21 +151,6 @@ QString QbsInstallStep::installRoot() const
|
||||
return bs ? bs->installRoot().toString() : QString();
|
||||
}
|
||||
|
||||
bool QbsInstallStep::removeFirst() const
|
||||
{
|
||||
return m_qbsInstallOptions.removeExistingInstallation();
|
||||
}
|
||||
|
||||
bool QbsInstallStep::dryRun() const
|
||||
{
|
||||
return m_qbsInstallOptions.dryRun();
|
||||
}
|
||||
|
||||
bool QbsInstallStep::keepGoing() const
|
||||
{
|
||||
return m_qbsInstallOptions.keepGoing();
|
||||
}
|
||||
|
||||
const QbsBuildConfiguration *QbsInstallStep::buildConfig() const
|
||||
{
|
||||
return static_cast<QbsBuildConfiguration *>(buildConfiguration());
|
||||
@@ -174,40 +161,30 @@ bool QbsInstallStep::fromMap(const QVariantMap &map)
|
||||
if (!ProjectExplorer::BuildStep::fromMap(map))
|
||||
return false;
|
||||
|
||||
m_qbsInstallOptions.setInstallRoot(installRoot());
|
||||
m_qbsInstallOptions.setRemoveExistingInstallation(
|
||||
map.value(QLatin1String(QBS_REMOVE_FIRST), false).toBool());
|
||||
m_qbsInstallOptions.setDryRun(map.value(QLatin1String(QBS_DRY_RUN), false).toBool());
|
||||
m_qbsInstallOptions.setKeepGoing(map.value(QLatin1String(QBS_KEEP_GOING), false).toBool());
|
||||
|
||||
m_cleanInstallRoot = map.value(QBS_REMOVE_FIRST, false).toBool();
|
||||
m_dryRun = map.value(QBS_DRY_RUN, false).toBool();
|
||||
m_keepGoing = map.value(QBS_KEEP_GOING, false).toBool();
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariantMap QbsInstallStep::toMap() const
|
||||
{
|
||||
QVariantMap map = ProjectExplorer::BuildStep::toMap();
|
||||
map.insert(QLatin1String(QBS_REMOVE_FIRST), m_qbsInstallOptions.removeExistingInstallation());
|
||||
map.insert(QLatin1String(QBS_DRY_RUN), m_qbsInstallOptions.dryRun());
|
||||
map.insert(QLatin1String(QBS_KEEP_GOING), m_qbsInstallOptions.keepGoing());
|
||||
map.insert(QBS_REMOVE_FIRST, m_cleanInstallRoot);
|
||||
map.insert(QBS_DRY_RUN, m_dryRun);
|
||||
map.insert(QBS_KEEP_GOING, m_keepGoing);
|
||||
return map;
|
||||
}
|
||||
|
||||
qbs::InstallOptions QbsInstallStep::installOptions() const
|
||||
void QbsInstallStep::installDone(const ErrorInfo &error)
|
||||
{
|
||||
return m_qbsInstallOptions;
|
||||
}
|
||||
m_session->disconnect(this);
|
||||
m_session = nullptr;
|
||||
|
||||
void QbsInstallStep::installDone(bool success)
|
||||
{
|
||||
// Report errors:
|
||||
foreach (const qbs::ErrorItem &item, m_job->error().items()) {
|
||||
createTaskAndOutput(ProjectExplorer::Task::Error, item.description(),
|
||||
item.codeLocation().filePath(), item.codeLocation().line());
|
||||
}
|
||||
for (const ErrorInfoItem &item : error.items)
|
||||
createTaskAndOutput(Task::Error, item.description, item.filePath.toString(), item.line);
|
||||
|
||||
emit finished(success);
|
||||
m_job->deleteLater();
|
||||
m_job = nullptr;
|
||||
emit finished(!error.hasError());
|
||||
}
|
||||
|
||||
void QbsInstallStep::handleTaskStarted(const QString &desciption, int max)
|
||||
@@ -234,31 +211,25 @@ void QbsInstallStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type,
|
||||
|
||||
void QbsInstallStep::setRemoveFirst(bool rf)
|
||||
{
|
||||
if (m_qbsInstallOptions.removeExistingInstallation() == rf)
|
||||
if (m_cleanInstallRoot == rf)
|
||||
return;
|
||||
m_qbsInstallOptions.setRemoveExistingInstallation(rf);
|
||||
m_cleanInstallRoot = rf;
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QbsInstallStep::setDryRun(bool dr)
|
||||
{
|
||||
if (m_qbsInstallOptions.dryRun() == dr)
|
||||
if (m_dryRun == dr)
|
||||
return;
|
||||
m_qbsInstallOptions.setDryRun(dr);
|
||||
m_dryRun = dr;
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QbsInstallStep::setKeepGoing(bool kg)
|
||||
{
|
||||
if (m_qbsInstallOptions.keepGoing() == kg)
|
||||
if (m_keepGoing == kg)
|
||||
return;
|
||||
m_qbsInstallOptions.setKeepGoing(kg);
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QbsInstallStep::handleBuildConfigChanged()
|
||||
{
|
||||
m_qbsInstallOptions.setInstallRoot(installRoot());
|
||||
m_keepGoing = kg;
|
||||
emit changed();
|
||||
}
|
||||
|
||||
|
@@ -26,14 +26,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "qbsbuildconfiguration.h"
|
||||
#include "qbssession.h"
|
||||
|
||||
#include <projectexplorer/buildstep.h>
|
||||
#include <projectexplorer/task.h>
|
||||
|
||||
#include <qbs.h>
|
||||
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
class ErrorInfo;
|
||||
class QbsSession;
|
||||
|
||||
class QbsInstallStep : public ProjectExplorer::BuildStep
|
||||
{
|
||||
@@ -43,11 +44,10 @@ public:
|
||||
explicit QbsInstallStep(ProjectExplorer::BuildStepList *bsl);
|
||||
~QbsInstallStep() override;
|
||||
|
||||
qbs::InstallOptions installOptions() const;
|
||||
QString installRoot() const;
|
||||
bool removeFirst() const;
|
||||
bool dryRun() const;
|
||||
bool keepGoing() const;
|
||||
bool removeFirst() const { return m_cleanInstallRoot; }
|
||||
bool dryRun() const { return m_dryRun; }
|
||||
bool keepGoing() const { return m_keepGoing; }
|
||||
|
||||
signals:
|
||||
void changed();
|
||||
@@ -61,7 +61,7 @@ private:
|
||||
QVariantMap toMap() const override;
|
||||
|
||||
const QbsBuildConfiguration *buildConfig() const;
|
||||
void installDone(bool success);
|
||||
void installDone(const ErrorInfo &error);
|
||||
void handleTaskStarted(const QString &desciption, int max);
|
||||
void handleProgress(int value);
|
||||
|
||||
@@ -71,14 +71,14 @@ private:
|
||||
void setRemoveFirst(bool rf);
|
||||
void setDryRun(bool dr);
|
||||
void setKeepGoing(bool kg);
|
||||
void handleBuildConfigChanged();
|
||||
|
||||
qbs::InstallOptions m_qbsInstallOptions;
|
||||
bool m_cleanInstallRoot = false;
|
||||
bool m_dryRun = false;
|
||||
bool m_keepGoing = false;
|
||||
|
||||
qbs::InstallJob *m_job = nullptr;
|
||||
QbsSession *m_session = nullptr;
|
||||
QString m_description;
|
||||
int m_maxProgress;
|
||||
bool m_showCompilerOutput = true;
|
||||
ProjectExplorer::IOutputParser *m_parser = nullptr;
|
||||
|
||||
friend class QbsInstallStepConfigWidget;
|
||||
|
@@ -26,14 +26,13 @@
|
||||
#include "qbskitinformation.h"
|
||||
|
||||
#include "customqbspropertiesdialog.h"
|
||||
#include "qbsprofilemanager.h"
|
||||
|
||||
#include <projectexplorer/kitmanager.h>
|
||||
|
||||
#include <utils/elidinglabel.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <qbs.h>
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
@@ -85,7 +84,7 @@ QString QbsKitAspect::representation(const Kit *kit)
|
||||
for (auto it = props.begin(); it != props.end(); ++it) {
|
||||
if (!repr.isEmpty())
|
||||
repr += ' ';
|
||||
repr += it.key() + ':' + qbs::settingsValueToRepresentation(it.value());
|
||||
repr += it.key() + ':' + toJSLiteral(it.value());
|
||||
}
|
||||
return repr;
|
||||
}
|
||||
|
@@ -1,94 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "qbslogsink.h"
|
||||
|
||||
#include <qbs.h>
|
||||
|
||||
#include <coreplugin/messagemanager.h>
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
#include <projectexplorer/taskhub.h>
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QMutexLocker>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// QbsLogSink:
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
QbsLogSink::QbsLogSink(QObject *parent) : QObject(parent)
|
||||
{
|
||||
connect(this, &QbsLogSink::newTask,
|
||||
TaskHub::instance(),
|
||||
[](const Task &task) { TaskHub::addTask(task); }, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void QbsLogSink::sendMessages()
|
||||
{
|
||||
QStringList toSend;
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
toSend = m_messages;
|
||||
m_messages.clear();
|
||||
}
|
||||
|
||||
foreach (const QString &msg, toSend)
|
||||
Core::MessageManager::write(msg, Core::MessageManager::Silent);
|
||||
}
|
||||
|
||||
void QbsLogSink::doPrintWarning(const qbs::ErrorInfo &warning)
|
||||
{
|
||||
foreach (const qbs::ErrorItem &item, warning.items())
|
||||
emit newTask(Task(Task::Warning,
|
||||
item.description(),
|
||||
Utils::FilePath::fromString(item.codeLocation().filePath()),
|
||||
item.codeLocation().line(),
|
||||
Constants::TASK_CATEGORY_BUILDSYSTEM));
|
||||
}
|
||||
|
||||
void QbsLogSink::doPrintMessage(qbs::LoggerLevel level, const QString &message, const QString &tag)
|
||||
{
|
||||
Q_UNUSED(tag)
|
||||
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
if (level <= qbs::LoggerWarning) {
|
||||
doPrintWarning(qbs::ErrorInfo(message));
|
||||
return;
|
||||
}
|
||||
m_messages.append(qbs::logLevelTag(level) + message);
|
||||
}
|
||||
QMetaObject::invokeMethod(this, "sendMessages", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QbsProjectManager
|
@@ -1,60 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <projectexplorer/task.h>
|
||||
|
||||
#include <qbs.h>
|
||||
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
class QbsLogSink : public QObject, public qbs::ILogSink
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QbsLogSink(QObject *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void newTask(const ProjectExplorer::Task &task);
|
||||
|
||||
private:
|
||||
Q_INVOKABLE void sendMessages();
|
||||
|
||||
void doPrintWarning(const qbs::ErrorInfo &warning) override;
|
||||
void doPrintMessage(qbs::LoggerLevel level, const QString &message,
|
||||
const QString &tag) override;
|
||||
|
||||
QStringList m_messages;
|
||||
QMutex m_mutex;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QbsProjectManager
|
@@ -29,6 +29,7 @@
|
||||
#include "qbsproject.h"
|
||||
#include "qbsprojectmanagerconstants.h"
|
||||
#include "qbsprojectmanagerplugin.h"
|
||||
#include "qbssession.h"
|
||||
|
||||
#include <android/androidconstants.h>
|
||||
#include <coreplugin/fileiconprovider.h>
|
||||
@@ -44,8 +45,10 @@
|
||||
#include <QtDebug>
|
||||
#include <QDir>
|
||||
#include <QIcon>
|
||||
#include <QJsonArray>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Utils;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helpers:
|
||||
@@ -54,147 +57,26 @@ using namespace ProjectExplorer;
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
class FileTreeNode {
|
||||
public:
|
||||
explicit FileTreeNode(const QString &n = QString(), FileTreeNode *p = nullptr, bool f = false) :
|
||||
parent(p), name(n), m_isFile(f)
|
||||
const QbsProductNode *parentQbsProductNode(const ProjectExplorer::Node *node)
|
||||
{
|
||||
if (p)
|
||||
p->children.append(this);
|
||||
for (; node; node = node->parentFolderNode()) {
|
||||
const auto prdNode = dynamic_cast<const QbsProductNode *>(node);
|
||||
if (prdNode)
|
||||
return prdNode;
|
||||
}
|
||||
|
||||
~FileTreeNode()
|
||||
{
|
||||
qDeleteAll(children);
|
||||
}
|
||||
|
||||
FileTreeNode *addPart(const QString &n, bool isFile)
|
||||
{
|
||||
foreach (FileTreeNode *c, children) {
|
||||
if (c->name == n)
|
||||
return c;
|
||||
}
|
||||
return new FileTreeNode(n, this, isFile);
|
||||
}
|
||||
|
||||
bool isFile() const { return m_isFile; }
|
||||
|
||||
static FileTreeNode *moveChildrenUp(FileTreeNode *node)
|
||||
{
|
||||
QTC_ASSERT(node, return nullptr);
|
||||
|
||||
FileTreeNode *newParent = node->parent;
|
||||
if (!newParent)
|
||||
return nullptr;
|
||||
|
||||
// disconnect node and parent:
|
||||
node->parent = nullptr;
|
||||
newParent->children.removeOne(node);
|
||||
|
||||
foreach (FileTreeNode *c, node->children) {
|
||||
// update path, make sure there will be no / before "C:" on windows:
|
||||
if (Utils::HostOsInfo::isWindowsHost() && node->name.isEmpty())
|
||||
c->name = node->name;
|
||||
else
|
||||
c->name = node->name + QLatin1Char('/') + c->name;
|
||||
|
||||
newParent->children.append(c);
|
||||
c->parent = newParent;
|
||||
}
|
||||
|
||||
// Delete node
|
||||
node->children.clear();
|
||||
delete node;
|
||||
return newParent;
|
||||
}
|
||||
|
||||
// Moves the children of the node pointing to basedir to the root of the tree.
|
||||
static void reorder(FileTreeNode *node, const QString &basedir)
|
||||
{
|
||||
QTC_CHECK(!basedir.isEmpty());
|
||||
QString prefix = basedir;
|
||||
if (basedir.startsWith(QLatin1Char('/')))
|
||||
prefix = basedir.mid(1);
|
||||
prefix.append(QLatin1Char('/'));
|
||||
|
||||
if (node->path() == basedir) {
|
||||
// Find root node:
|
||||
FileTreeNode *root = node;
|
||||
while (root->parent)
|
||||
root = root->parent;
|
||||
|
||||
foreach (FileTreeNode *c, node->children) {
|
||||
// Update children names by prepending basedir
|
||||
c->name = prefix + c->name;
|
||||
// Update parent information:
|
||||
c->parent = root;
|
||||
|
||||
root->children.append(c);
|
||||
}
|
||||
|
||||
// Clean up node:
|
||||
node->children.clear();
|
||||
node->parent->children.removeOne(node);
|
||||
node->parent = nullptr;
|
||||
delete node;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (FileTreeNode *n, node->children)
|
||||
reorder(n, basedir);
|
||||
}
|
||||
|
||||
static void simplify(FileTreeNode *node)
|
||||
{
|
||||
foreach (FileTreeNode *c, node->children)
|
||||
simplify(c);
|
||||
|
||||
if (!node->parent)
|
||||
return;
|
||||
|
||||
if (node->children.isEmpty() && !node->isFile()) {
|
||||
// Clean up empty folder nodes:
|
||||
node->parent->children.removeOne(node);
|
||||
node->parent = nullptr;
|
||||
delete node;
|
||||
} else if (node->children.count() == 1 && !node->children.at(0)->isFile()) {
|
||||
// Compact folder nodes with one child only:
|
||||
moveChildrenUp(node);
|
||||
}
|
||||
}
|
||||
|
||||
QString path() const
|
||||
{
|
||||
QString p = name;
|
||||
FileTreeNode *node = parent;
|
||||
while (node) {
|
||||
if (!Utils::HostOsInfo::isWindowsHost() || !node->name.isEmpty())
|
||||
p = node->name + QLatin1Char('/') + p;
|
||||
node = node->parent;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
QList<FileTreeNode *> children;
|
||||
FileTreeNode *parent;
|
||||
QString name;
|
||||
bool m_isFile;
|
||||
};
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// QbsGroupNode:
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
QbsGroupNode::QbsGroupNode(const qbs::GroupData &grp, const QString &productPath) :
|
||||
ProjectNode(Utils::FilePath())
|
||||
QbsGroupNode::QbsGroupNode(const QJsonObject &grp) : ProjectNode(FilePath()), m_groupData(grp)
|
||||
{
|
||||
static QIcon groupIcon = QIcon(QString(Constants::QBS_GROUP_ICON));
|
||||
setIcon(groupIcon);
|
||||
|
||||
m_productPath = productPath;
|
||||
m_qbsGroupData = grp;
|
||||
setDisplayName(grp.value("name").toString());
|
||||
setEnabled(grp.value("is-enabled").toBool());
|
||||
}
|
||||
|
||||
FolderNode::AddNewInformation QbsGroupNode::addNewInformation(const QStringList &files,
|
||||
@@ -208,8 +90,15 @@ FolderNode::AddNewInformation QbsGroupNode::addNewInformation(const QStringList
|
||||
|
||||
QVariant QbsGroupNode::data(Core::Id role) const
|
||||
{
|
||||
if (role == ProjectExplorer::Constants::QT_KEYWORDS_ENABLED)
|
||||
return m_qbsGroupData.properties().getModuleProperty("Qt.core", "enableKeywords");
|
||||
if (role == ProjectExplorer::Constants::QT_KEYWORDS_ENABLED) {
|
||||
QJsonObject modProps = m_groupData.value("module-properties").toObject();
|
||||
if (modProps.isEmpty()) {
|
||||
const QbsProductNode * const prdNode = parentQbsProductNode(this);
|
||||
QTC_ASSERT(prdNode, return QVariant());
|
||||
modProps = prdNode->productData().value("module-properties").toObject();
|
||||
}
|
||||
return modProps.value("Qt.core.enableKeywords").toBool();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
@@ -217,106 +106,104 @@ QVariant QbsGroupNode::data(Core::Id role) const
|
||||
// QbsProductNode:
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
QbsProductNode::QbsProductNode(const qbs::ProductData &prd) :
|
||||
ProjectNode(Utils::FilePath::fromString(prd.location().filePath())),
|
||||
m_qbsProductData(prd)
|
||||
QbsProductNode::QbsProductNode(const QJsonObject &prd) : ProjectNode(FilePath()), m_productData(prd)
|
||||
{
|
||||
static QIcon productIcon = Core::FileIconProvider::directoryIcon(Constants::QBS_PRODUCT_OVERLAY_ICON);
|
||||
static QIcon productIcon = Core::FileIconProvider::directoryIcon(
|
||||
Constants::QBS_PRODUCT_OVERLAY_ICON);
|
||||
setIcon(productIcon);
|
||||
if (m_qbsProductData.isRunnable()) {
|
||||
if (prd.value("is-runnable").toBool()) {
|
||||
setProductType(ProductType::App);
|
||||
} else if (m_qbsProductData.type().contains("dynamiclibrary")
|
||||
|| m_qbsProductData.type().contains("staticlibrary")) {
|
||||
setProductType(ProductType::Lib);
|
||||
} else {
|
||||
const QJsonArray type = prd.value("type").toArray();
|
||||
if (type.contains("dynamiclibrary") || type.contains("staticlibrary"))
|
||||
setProductType(ProductType::Lib);
|
||||
else
|
||||
setProductType(ProductType::Other);
|
||||
}
|
||||
setEnabled(prd.value("is-enabled").toBool());
|
||||
setDisplayName(prd.value("full-display-name").toString());
|
||||
}
|
||||
|
||||
void QbsProductNode::build()
|
||||
{
|
||||
QbsProjectManagerPlugin::buildNamedProduct(static_cast<QbsProject *>(getProject()),
|
||||
QbsProject::uniqueProductName(qbsProductData()));
|
||||
m_productData.value("full-display-name").toString());
|
||||
}
|
||||
|
||||
QStringList QbsProductNode::targetApplications() const
|
||||
{
|
||||
return QStringList{m_qbsProductData.targetExecutable()};
|
||||
return QStringList{m_productData.value("target-executable").toString()};
|
||||
}
|
||||
|
||||
QString QbsProductNode::buildKey() const
|
||||
{
|
||||
return QbsProject::uniqueProductName(m_qbsProductData);
|
||||
return m_productData.value("full-display-name").toString();
|
||||
}
|
||||
|
||||
QVariant QbsProductNode::data(Core::Id role) const
|
||||
{
|
||||
if (role == Android::Constants::AndroidDeploySettingsFile) {
|
||||
for (const auto &artifact : m_qbsProductData.generatedArtifacts()) {
|
||||
if (artifact.fileTags().contains("qt_androiddeployqt_input"))
|
||||
return artifact.filePath();
|
||||
for (const auto &a : m_productData.value("generated-artifacts").toArray()) {
|
||||
const QJsonObject artifact = a.toObject();
|
||||
if (artifact.value("file-tags").toArray().contains("qt_androiddeployqt_input"))
|
||||
return artifact.value("file-path").toString();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
if (role == Android::Constants::AndroidSoLibPath) {
|
||||
QStringList ret{m_qbsProductData.buildDirectory()};
|
||||
for (const auto &artifact : m_qbsProductData.generatedArtifacts()) {
|
||||
if (artifact.fileTags().contains("dynamiclibrary")) {
|
||||
ret << QFileInfo(artifact.filePath()).path();
|
||||
}
|
||||
}
|
||||
QStringList ret{m_productData.value("build-directory").toString()};
|
||||
forAllArtifacts(m_productData, ArtifactType::Generated, [&ret](const QJsonObject &artifact) {
|
||||
if (artifact.value("file-tags").toArray().contains("dynamiclibrary"))
|
||||
ret << QFileInfo(artifact.value("file-path").toString()).path();
|
||||
});
|
||||
ret.removeDuplicates();
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (role == Android::Constants::AndroidManifest) {
|
||||
for (const auto &artifact : m_qbsProductData.generatedArtifacts()) {
|
||||
if (artifact.fileTags().contains("android.manifest_final"))
|
||||
return artifact.filePath();
|
||||
for (const auto &a : m_productData.value("generated-artifacts").toArray()) {
|
||||
const QJsonObject artifact = a.toObject();
|
||||
if (artifact.value("file-tags").toArray().contains("android.manifest_final"))
|
||||
return artifact.value("file-path").toString();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
if (role == Android::Constants::AndroidApk)
|
||||
return m_qbsProductData.targetExecutable();
|
||||
return m_productData.value("target-executable").toString();
|
||||
|
||||
if (role == ProjectExplorer::Constants::QT_KEYWORDS_ENABLED)
|
||||
return m_qbsProductData.moduleProperties().getModuleProperty("Qt.core", "enableKeywords");
|
||||
return m_productData.value("module-properties").toObject()
|
||||
.value("Qt.core.enableKeywords").toBool();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QJsonObject QbsProductNode::mainGroup() const
|
||||
{
|
||||
for (const QJsonValue &g : m_productData.value("groups").toArray()) {
|
||||
const QJsonObject grp = g.toObject();
|
||||
if (grp.value("name") == m_productData.value("name")
|
||||
&& grp.value("location") == m_productData.value("location")) {
|
||||
return grp;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// QbsProjectNode:
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
QbsProjectNode::QbsProjectNode(const Utils::FilePath &projectDirectory) :
|
||||
ProjectNode(projectDirectory)
|
||||
QbsProjectNode::QbsProjectNode(const QJsonObject &projectData)
|
||||
: ProjectNode(FilePath()), m_projectData(projectData)
|
||||
{
|
||||
static QIcon projectIcon = Core::FileIconProvider::directoryIcon(ProjectExplorer::Constants::FILEOVERLAY_QT);
|
||||
static QIcon projectIcon = Core::FileIconProvider::directoryIcon(
|
||||
ProjectExplorer::Constants::FILEOVERLAY_QT);
|
||||
setIcon(projectIcon);
|
||||
setDisplayName(projectData.value("name").toString());
|
||||
}
|
||||
|
||||
Project *QbsProjectNode::project() const
|
||||
{
|
||||
return static_cast<QbsProjectNode *>(parentFolderNode())->project();
|
||||
}
|
||||
|
||||
void QbsProjectNode::setProjectData(const qbs::ProjectData &data)
|
||||
{
|
||||
m_projectData = data;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// QbsRootProjectNode:
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
QbsRootProjectNode::QbsRootProjectNode(Project *project) :
|
||||
QbsProjectNode(project->projectDirectory()),
|
||||
m_project(project)
|
||||
{ }
|
||||
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QbsProjectManager
|
||||
|
@@ -28,91 +28,60 @@
|
||||
#include <projectexplorer/buildsystem.h>
|
||||
#include <projectexplorer/projectnodes.h>
|
||||
|
||||
#include <qbs.h>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
class QbsNodeTreeBuilder;
|
||||
class QbsProject;
|
||||
class QbsBuildSystem;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// QbsGroupNode:
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class QbsGroupNode : public ProjectExplorer::ProjectNode
|
||||
{
|
||||
public:
|
||||
QbsGroupNode(const qbs::GroupData &grp, const QString &productPath);
|
||||
QbsGroupNode(const QJsonObject &grp);
|
||||
|
||||
bool showInSimpleTree() const final { return false; }
|
||||
QJsonObject groupData() const { return m_groupData; }
|
||||
|
||||
private:
|
||||
friend class QbsBuildSystem;
|
||||
AddNewInformation addNewInformation(const QStringList &files, Node *context) const override;
|
||||
QVariant data(Core::Id role) const override;
|
||||
|
||||
qbs::GroupData m_qbsGroupData;
|
||||
QString m_productPath;
|
||||
const QJsonObject m_groupData;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// QbsProductNode:
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class QbsProductNode : public ProjectExplorer::ProjectNode
|
||||
{
|
||||
public:
|
||||
explicit QbsProductNode(const qbs::ProductData &prd);
|
||||
explicit QbsProductNode(const QJsonObject &prd);
|
||||
|
||||
void build() override;
|
||||
QStringList targetApplications() const override;
|
||||
|
||||
QString buildKey() const override;
|
||||
|
||||
const qbs::ProductData qbsProductData() const { return m_qbsProductData; }
|
||||
const QJsonObject productData() const { return m_productData; }
|
||||
QJsonObject mainGroup() const;
|
||||
QVariant data(Core::Id role) const override;
|
||||
|
||||
private:
|
||||
const qbs::ProductData m_qbsProductData;
|
||||
const QJsonObject m_productData;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// QbsProjectNode:
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class QbsProjectNode : public ProjectExplorer::ProjectNode
|
||||
{
|
||||
public:
|
||||
explicit QbsProjectNode(const Utils::FilePath &projectDirectory);
|
||||
explicit QbsProjectNode(const QJsonObject &projectData);
|
||||
|
||||
virtual ProjectExplorer::Project *project() const;
|
||||
const qbs::ProjectData qbsProjectData() const { return m_projectData; }
|
||||
|
||||
void setProjectData(const qbs::ProjectData &data); // FIXME: Needed?
|
||||
const QJsonObject projectData() const { return m_projectData; }
|
||||
|
||||
private:
|
||||
qbs::ProjectData m_projectData;
|
||||
|
||||
friend class QbsNodeTreeBuilder;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// QbsRootProjectNode:
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class QbsRootProjectNode : public QbsProjectNode
|
||||
{
|
||||
public:
|
||||
explicit QbsRootProjectNode(ProjectExplorer::Project *project);
|
||||
|
||||
ProjectExplorer::Project *project() const override { return m_project; }
|
||||
|
||||
private:
|
||||
ProjectExplorer::Project *const m_project;
|
||||
const QJsonObject m_projectData;
|
||||
};
|
||||
|
||||
const QbsProductNode *parentQbsProductNode(const ProjectExplorer::Node *node);
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QbsProjectManager
|
||||
|
@@ -25,7 +25,13 @@
|
||||
|
||||
#include "qbsnodetreebuilder.h"
|
||||
|
||||
#include "qbsnodes.h"
|
||||
#include "qbsproject.h"
|
||||
#include "qbssession.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
@@ -36,13 +42,9 @@ using namespace Utils;
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
namespace {
|
||||
|
||||
FileType fileType(const qbs::ArtifactData &artifact)
|
||||
static FileType fileType(const QJsonObject &artifact)
|
||||
{
|
||||
QTC_ASSERT(artifact.isValid(), return FileType::Unknown);
|
||||
|
||||
const QStringList fileTags = artifact.fileTags();
|
||||
const QJsonArray fileTags = artifact.value("file-tags").toArray();
|
||||
if (fileTags.contains("c")
|
||||
|| fileTags.contains("cpp")
|
||||
|| fileTags.contains("objc")
|
||||
@@ -62,160 +64,160 @@ FileType fileType(const qbs::ArtifactData &artifact)
|
||||
return FileType::Unknown;
|
||||
}
|
||||
|
||||
void setupArtifacts(FolderNode *root, const QList<qbs::ArtifactData> &artifacts)
|
||||
void setupArtifact(FolderNode *root, const QJsonObject &artifact)
|
||||
{
|
||||
for (const qbs::ArtifactData &ad : artifacts) {
|
||||
const FilePath path = FilePath::fromString(ad.filePath());
|
||||
const FileType type = fileType(ad);
|
||||
const bool isGenerated = ad.isGenerated();
|
||||
const FilePath path = FilePath::fromString(artifact.value("file-path").toString());
|
||||
const FileType type = fileType(artifact);
|
||||
const bool isGenerated = artifact.value("is-generated").toBool();
|
||||
|
||||
// A list of human-readable file types that we can reasonably expect
|
||||
// to get generated during a build. Extend as needed.
|
||||
static const QSet<QString> sourceTags = {
|
||||
QLatin1String("c"), QLatin1String("cpp"), QLatin1String("hpp"),
|
||||
QLatin1String("objc"), QLatin1String("objcpp"),
|
||||
QLatin1String("c_pch_src"), QLatin1String("cpp_pch_src"),
|
||||
QLatin1String("objc_pch_src"), QLatin1String("objcpp_pch_src"),
|
||||
QLatin1String("asm"), QLatin1String("asm_cpp"),
|
||||
QLatin1String("linkerscript"),
|
||||
QLatin1String("qrc"), QLatin1String("java.java")
|
||||
"c", "cpp", "hpp", "objc", "objcpp", "c_pch_src", "cpp_pch_src", "objc_pch_src",
|
||||
"objcpp_pch_src", "asm", "asm_cpp", "linkerscript", "qrc", "java.java"
|
||||
};
|
||||
auto node = std::make_unique<FileNode>(path, type);
|
||||
node->setIsGenerated(isGenerated);
|
||||
node->setListInProject(!isGenerated || Utils::toSet(ad.fileTags()).intersects(sourceTags));
|
||||
QSet<QString> fileTags = toSet(transform<QStringList, QJsonArray>(
|
||||
artifact.value("file-tags").toArray(),
|
||||
[](const QJsonValue &v) { return v.toString(); }));
|
||||
node->setListInProject(!isGenerated || fileTags.intersects(sourceTags));
|
||||
root->addNestedNode(std::move(node));
|
||||
}
|
||||
|
||||
static void setupArtifactsForGroup(FolderNode *root, const QJsonObject &group)
|
||||
{
|
||||
forAllArtifacts(group, [root](const QJsonObject &artifact) { setupArtifact(root, artifact); });
|
||||
root->compress();
|
||||
}
|
||||
|
||||
std::unique_ptr<QbsGroupNode>
|
||||
buildGroupNodeTree(const qbs::GroupData &grp, const QString &productPath, bool productIsEnabled)
|
||||
static void setupGeneratedArtifacts(FolderNode *root, const QJsonObject &product)
|
||||
{
|
||||
QTC_ASSERT(grp.isValid(), return nullptr);
|
||||
forAllArtifacts(product, ArtifactType::Generated,
|
||||
[root](const QJsonObject &artifact) { setupArtifact(root, artifact); });
|
||||
root->compress();
|
||||
}
|
||||
|
||||
auto fileNode = std::make_unique<FileNode>(FilePath::fromString(grp.location().filePath()),
|
||||
FileType::Project);
|
||||
fileNode->setLine(grp.location().line());
|
||||
|
||||
auto result = std::make_unique<QbsGroupNode>(grp, productPath);
|
||||
|
||||
result->setEnabled(productIsEnabled && grp.isEnabled());
|
||||
result->setAbsoluteFilePathAndLine(
|
||||
FilePath::fromString(grp.location().filePath()).parentDir(), -1);
|
||||
result->setDisplayName(grp.name());
|
||||
static std::unique_ptr<QbsGroupNode> buildGroupNodeTree(const QJsonObject &grp)
|
||||
{
|
||||
const Location location = locationFromObject(grp);
|
||||
FilePath baseDir = location.filePath.parentDir();
|
||||
QString prefix = grp.value("prefix").toString();
|
||||
if (prefix.endsWith('/')) {
|
||||
prefix.chop(1);
|
||||
if (QFileInfo(prefix).isAbsolute())
|
||||
baseDir = FilePath::fromString(prefix);
|
||||
else
|
||||
baseDir = baseDir.pathAppended(prefix);
|
||||
}
|
||||
auto result = std::make_unique<QbsGroupNode>(grp);
|
||||
result->setAbsoluteFilePathAndLine(baseDir, -1);
|
||||
auto fileNode = std::make_unique<FileNode>(FilePath(), FileType::Project);
|
||||
fileNode->setAbsoluteFilePathAndLine(location.filePath, location.line);
|
||||
result->addNode(std::move(fileNode));
|
||||
|
||||
setupArtifacts(result.get(), grp.allSourceArtifacts());
|
||||
|
||||
setupArtifactsForGroup(result.get(), grp);
|
||||
return result;
|
||||
}
|
||||
|
||||
void setupQbsProductData(QbsProductNode *node, const qbs::ProductData &prd)
|
||||
static std::unique_ptr<QbsProductNode> buildProductNodeTree(const QJsonObject &prd)
|
||||
{
|
||||
auto fileNode = std::make_unique<FileNode>(FilePath::fromString(prd.location().filePath()),
|
||||
FileType::Project);
|
||||
fileNode->setLine(prd.location().line());
|
||||
|
||||
node->setEnabled(prd.isEnabled());
|
||||
node->setDisplayName(prd.fullDisplayName());
|
||||
node->setAbsoluteFilePathAndLine(FilePath::fromString(prd.location().filePath()).parentDir(), -1);
|
||||
node->addNode(std::move(fileNode));
|
||||
|
||||
const QString &productPath = QFileInfo(prd.location().filePath()).absolutePath();
|
||||
foreach (const qbs::GroupData &grp, prd.groups()) {
|
||||
if (grp.name() == prd.name() && grp.location() == prd.location()) {
|
||||
const Location location = locationFromObject(prd);
|
||||
auto result = std::make_unique<QbsProductNode>(prd);
|
||||
result->setAbsoluteFilePathAndLine(location.filePath.parentDir(), -1);
|
||||
auto fileNode = std::make_unique<FileNode>(FilePath(), FileType::Project);
|
||||
fileNode->setAbsoluteFilePathAndLine(location.filePath, location.line);
|
||||
result->addNode(std::move(fileNode));
|
||||
for (const QJsonValue &v : prd.value("groups").toArray()) {
|
||||
const QJsonObject grp = v.toObject();
|
||||
if (grp.value("name") == prd.value("name")
|
||||
&& grp.value("location") == prd.value("location")) {
|
||||
// Set implicit product group right onto this node:
|
||||
setupArtifacts(node, grp.allSourceArtifacts());
|
||||
setupArtifactsForGroup(result.get(), grp);
|
||||
continue;
|
||||
}
|
||||
node->addNode(buildGroupNodeTree(grp, productPath, prd.isEnabled()));
|
||||
result->addNode(buildGroupNodeTree(grp));
|
||||
}
|
||||
|
||||
// Add "Generated Files" Node:
|
||||
auto genFiles = std::make_unique<VirtualFolderNode>(FilePath::fromString(prd.buildDirectory()));
|
||||
auto genFiles = std::make_unique<VirtualFolderNode>(
|
||||
FilePath::fromString(prd.value("build-directory").toString()));
|
||||
genFiles->setDisplayName(QCoreApplication::translate("QbsProductNode", "Generated files"));
|
||||
setupArtifacts(genFiles.get(), prd.generatedArtifacts());
|
||||
node->addNode(std::move(genFiles));
|
||||
}
|
||||
|
||||
std::unique_ptr<QbsProductNode> buildProductNodeTree(const qbs::ProductData &prd)
|
||||
{
|
||||
auto result = std::make_unique<QbsProductNode>(prd);
|
||||
|
||||
setupQbsProductData(result.get(), prd);
|
||||
setupGeneratedArtifacts(genFiles.get(), prd);
|
||||
result->addNode(std::move(genFiles));
|
||||
return result;
|
||||
}
|
||||
|
||||
void setupProjectNode(QbsProjectNode *node, const qbs::ProjectData &prjData,
|
||||
const qbs::Project &qbsProject)
|
||||
static void setupProjectNode(QbsProjectNode *node)
|
||||
{
|
||||
auto fileNode = std::make_unique<FileNode>(FilePath::fromString(prjData.location().filePath()),
|
||||
FileType::Project);
|
||||
fileNode->setLine(prjData.location().line());
|
||||
|
||||
const Location loc = locationFromObject(node->projectData());
|
||||
node->setAbsoluteFilePathAndLine(loc.filePath.parentDir(), -1);
|
||||
auto fileNode = std::make_unique<FileNode>(node->filePath(), FileType::Project);
|
||||
fileNode->setAbsoluteFilePathAndLine(loc.filePath, loc.line);
|
||||
node->addNode(std::move(fileNode));
|
||||
foreach (const qbs::ProjectData &subData, prjData.subProjects()) {
|
||||
auto subProject = std::make_unique<QbsProjectNode>(
|
||||
FilePath::fromString(subData.location().filePath()).parentDir());
|
||||
setupProjectNode(subProject.get(), subData, qbsProject);
|
||||
|
||||
for (const QJsonValue &v : node->projectData().value("sub-projects").toArray()) {
|
||||
auto subProject = std::make_unique<QbsProjectNode>(v.toObject());
|
||||
setupProjectNode(subProject.get());
|
||||
node->addNode(std::move(subProject));
|
||||
}
|
||||
|
||||
foreach (const qbs::ProductData &prd, prjData.products())
|
||||
node->addNode(buildProductNodeTree(prd));
|
||||
|
||||
if (!prjData.name().isEmpty())
|
||||
node->setDisplayName(prjData.name());
|
||||
else
|
||||
node->setDisplayName(node->project()->displayName());
|
||||
|
||||
node->setProjectData(prjData);
|
||||
for (const QJsonValue &v : node->projectData().value("products").toArray())
|
||||
node->addNode(buildProductNodeTree(v.toObject()));
|
||||
}
|
||||
|
||||
QSet<QString> referencedBuildSystemFiles(const qbs::ProjectData &data)
|
||||
static QSet<QString> referencedBuildSystemFiles(const QJsonObject &prjData)
|
||||
{
|
||||
QSet<QString> result;
|
||||
result.insert(data.location().filePath());
|
||||
foreach (const qbs::ProjectData &subProject, data.subProjects())
|
||||
result.unite(referencedBuildSystemFiles(subProject));
|
||||
foreach (const qbs::ProductData &product, data.products()) {
|
||||
result.insert(product.location().filePath());
|
||||
foreach (const qbs::GroupData &group, product.groups())
|
||||
result.insert(group.location().filePath());
|
||||
result.insert(prjData.value("location").toObject().value("file-path").toString());
|
||||
for (const QJsonValue &v : prjData.value("sub-projects").toArray())
|
||||
result.unite(referencedBuildSystemFiles(v.toObject()));
|
||||
for (const QJsonValue &v : prjData.value("products").toArray()) {
|
||||
const QJsonObject product = v.toObject();
|
||||
result.insert(product.value("location").toObject().value("file-path").toString());
|
||||
for (const QJsonValue &v : product.value("groups").toArray())
|
||||
result.insert(v.toObject().value("location").toObject().value("file-path").toString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList unreferencedBuildSystemFiles(const qbs::Project &p)
|
||||
static QStringList unreferencedBuildSystemFiles(const QJsonObject &project)
|
||||
{
|
||||
QStringList result;
|
||||
if (!p.isValid())
|
||||
return result;
|
||||
|
||||
const std::set<QString> &available = p.buildSystemFiles();
|
||||
QList<QString> referenced = Utils::toList(referencedBuildSystemFiles(p.projectData()));
|
||||
Utils::sort(referenced);
|
||||
std::set_difference(available.begin(), available.end(), referenced.begin(), referenced.end(),
|
||||
std::back_inserter(result));
|
||||
return result;
|
||||
QStringList unreferenced = arrayToStringList(project.value("build-system-files"));
|
||||
const QList<QString> referenced = toList(referencedBuildSystemFiles(project));
|
||||
for (auto it = unreferenced.begin(); it != unreferenced.end(); ) {
|
||||
if (referenced.contains(*it))
|
||||
it = unreferenced.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
return unreferenced;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<QbsRootProjectNode> QbsNodeTreeBuilder::buildTree(QbsBuildSystem *buildSystem)
|
||||
std::unique_ptr<QbsProjectNode> QbsNodeTreeBuilder::buildTree(const QbsBuildSystem *buildSystem)
|
||||
{
|
||||
if (!buildSystem->qbsProjectData().isValid())
|
||||
return {};
|
||||
const Project * const project = buildSystem->project();
|
||||
auto root = std::make_unique<QbsProjectNode>(buildSystem->projectData());
|
||||
|
||||
auto root = std::make_unique<QbsRootProjectNode>(buildSystem->project());
|
||||
setupProjectNode(root.get(), buildSystem->qbsProjectData(), buildSystem->qbsProject());
|
||||
// If we have no project information at all (i.e. it could not be properly parsed),
|
||||
// create the main project file node "manually".
|
||||
if (buildSystem->projectData().isEmpty()) {
|
||||
auto fileNode = std::make_unique<FileNode>(project->projectFilePath(), FileType::Project);
|
||||
root->addNode(std::move(fileNode));
|
||||
} else {
|
||||
setupProjectNode(root.get());
|
||||
}
|
||||
|
||||
auto buildSystemFiles = std::make_unique<FolderNode>(buildSystem->projectDirectory());
|
||||
buildSystemFiles->setDisplayName(QCoreApplication::translate("QbsRootProjectNode", "Qbs files"));
|
||||
if (root->displayName().isEmpty())
|
||||
root->setDisplayName(project->displayName());
|
||||
if (root->displayName().isEmpty())
|
||||
root->setDisplayName(project->projectFilePath().toFileInfo().completeBaseName());
|
||||
|
||||
const FilePath base = buildSystem->projectDirectory();
|
||||
const QStringList files = unreferencedBuildSystemFiles(buildSystem->qbsProject());
|
||||
auto buildSystemFiles = std::make_unique<FolderNode>(project->projectDirectory());
|
||||
buildSystemFiles->setDisplayName(QCoreApplication::translate("QbsProjectNode", "Qbs files"));
|
||||
|
||||
const FilePath base = project->projectDirectory();
|
||||
const QStringList files = unreferencedBuildSystemFiles(buildSystem->projectData());
|
||||
for (const QString &f : files) {
|
||||
const FilePath filePath = FilePath::fromString(f);
|
||||
if (filePath.isChildOf(base))
|
||||
@@ -223,7 +225,6 @@ std::unique_ptr<QbsRootProjectNode> QbsNodeTreeBuilder::buildTree(QbsBuildSystem
|
||||
}
|
||||
buildSystemFiles->compress();
|
||||
root->addNode(std::move(buildSystemFiles));
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
|
@@ -25,21 +25,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "qbsnodes.h"
|
||||
|
||||
#include <qbs.h>
|
||||
#include <memory>
|
||||
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// QbsNodeTreeBuilder:
|
||||
// ----------------------------------------------------------------------
|
||||
class QbsBuildSystem;
|
||||
class QbsProjectNode;
|
||||
|
||||
class QbsNodeTreeBuilder
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<QbsRootProjectNode> buildTree(QbsBuildSystem *project);
|
||||
static std::unique_ptr<QbsProjectNode> buildTree(const QbsBuildSystem *buildSystem);
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
266
src/plugins/qbsprojectmanager/qbsprofilemanager.cpp
Normal file
266
src/plugins/qbsprojectmanager/qbsprofilemanager.cpp
Normal file
@@ -0,0 +1,266 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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>
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QJSEngine>
|
||||
#include <QProcess>
|
||||
#include <QRegExp>
|
||||
#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;
|
||||
js.replace(QRegExp("([\\\\\"])"), "\\\\1");
|
||||
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) {
|
||||
if (res.length()) res.append(", ");
|
||||
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;
|
||||
}
|
||||
|
||||
QString QbsProfileManager::profileForKit(const ProjectExplorer::Kit *k)
|
||||
{
|
||||
if (!k)
|
||||
return QString();
|
||||
m_instance->updateProfileIfNecessary(k);
|
||||
const QString output = runQbsConfig(QbsConfigOp::Get, kitNameKeyInQbsSettings(k));
|
||||
const int endQuoteIdx = output.lastIndexOf('"');
|
||||
QTC_ASSERT(endQuoteIdx != -1, return QString());
|
||||
const int startQuoteIdx = output.lastIndexOf('"', endQuoteIdx - 1);
|
||||
QTC_ASSERT(startQuoteIdx != -1, return QString());
|
||||
return output.mid(startQuoteIdx + 1, endQuoteIdx - startQuoteIdx - 1);
|
||||
}
|
||||
|
||||
void QbsProfileManager::setProfileForKit(const QString &name, const ProjectExplorer::Kit *k)
|
||||
{
|
||||
runQbsConfig(QbsConfigOp::Set, kitNameKeyInQbsSettings(k), name);
|
||||
}
|
||||
|
||||
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::addProfile(const QString &name, const QVariantMap &data)
|
||||
{
|
||||
const QString keyPrefix = "profiles." + name + ".";
|
||||
for (auto it = data.begin(); it != data.end(); ++it)
|
||||
runQbsConfig(QbsConfigOp::Set, keyPrefix + it.key(), it.value());
|
||||
emit qbsProfilesUpdated();
|
||||
}
|
||||
|
||||
void QbsProfileManager::addQtProfileFromKit(const QString &profileName, const ProjectExplorer::Kit *k)
|
||||
{
|
||||
if (const QtSupport::BaseQtVersion * const qt = QtSupport::QtKitAspect::qtVersion(k)) {
|
||||
runQbsConfig(QbsConfigOp::Set,
|
||||
"profiles." + profileName + ".moduleProviders.Qt.qmakeFilePaths",
|
||||
qt->qmakeCommand().toString());
|
||||
}
|
||||
}
|
||||
|
||||
void QbsProfileManager::addProfileFromKit(const ProjectExplorer::Kit *k)
|
||||
{
|
||||
const QString name = QString::fromLatin1("qtc_%1_%2").arg(k->fileSystemFriendlyName().left(8),
|
||||
QString::fromLatin1(QCryptographicHash::hash(k->id().name(),
|
||||
QCryptographicHash::Sha1).toHex().left(8)));
|
||||
runQbsConfig(QbsConfigOp::Unset, "profiles." + name);
|
||||
setProfileForKit(name, k);
|
||||
addQtProfileFromKit(name, k);
|
||||
|
||||
// set up properties:
|
||||
QVariantMap data = m_defaultPropertyProvider->properties(k, QVariantMap());
|
||||
for (PropertyProvider *provider : g_propertyProviders) {
|
||||
if (provider->canHandle(k))
|
||||
data = provider->properties(k, data);
|
||||
}
|
||||
|
||||
addProfile(name, data);
|
||||
}
|
||||
|
||||
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));
|
||||
runQbsConfig(QbsConfigOp::Unset, "profiles." + profileForKit(kit));
|
||||
emit qbsProfilesUpdated();
|
||||
}
|
||||
|
||||
QString QbsProfileManager::runQbsConfig(QbsConfigOp op, const QString &key, const QVariant &value)
|
||||
{
|
||||
QProcess qbsConfig;
|
||||
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;
|
||||
}
|
||||
qbsConfig.start(QbsSettings::qbsExecutableFilePath().toString(), args);
|
||||
if (!qbsConfig.waitForStarted(3000) || !qbsConfig.waitForFinished(5000)) {
|
||||
Core::MessageManager::write(tr("Failed run qbs config: %1").arg(qbsConfig.errorString()));
|
||||
} else if (qbsConfig.exitCode() != 0) {
|
||||
Core::MessageManager::write(tr("Failed to run qbs config: %1")
|
||||
.arg(QString::fromLocal8Bit(qbsConfig.readAllStandardError())));
|
||||
}
|
||||
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
|
@@ -28,32 +28,33 @@
|
||||
#include "qbsprojectmanager_global.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QVariantMap>
|
||||
|
||||
namespace qbs { class Settings; }
|
||||
#include <QVariant>
|
||||
|
||||
namespace ProjectExplorer { class Kit; }
|
||||
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
class DefaultPropertyProvider;
|
||||
class QbsLogSink;
|
||||
|
||||
class QbsManager : public QObject
|
||||
QString toJSLiteral(const QVariant &val);
|
||||
QVariant fromJSLiteral(const QString &str);
|
||||
|
||||
class QbsProfileManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QbsManager();
|
||||
~QbsManager() override;
|
||||
QbsProfileManager();
|
||||
~QbsProfileManager() override;
|
||||
|
||||
static QbsProfileManager *instance();
|
||||
|
||||
// QBS profiles management:
|
||||
static QString profileForKit(const ProjectExplorer::Kit *k);
|
||||
static void updateProfileIfNecessary(const ProjectExplorer::Kit *kit);
|
||||
enum class QbsConfigOp { Get, Set, Unset }; static QString runQbsConfig(QbsConfigOp op, const QString &key, const QVariant &value = {});
|
||||
|
||||
static qbs::Settings *settings();
|
||||
static Internal::QbsLogSink *logSink();
|
||||
signals:
|
||||
void qbsProfilesUpdated();
|
||||
|
||||
private:
|
||||
void setProfileForKit(const QString &name, const ProjectExplorer::Kit *k);
|
@@ -26,50 +26,111 @@
|
||||
#include "qbsprofilessettingspage.h"
|
||||
#include "ui_qbsprofilessettingswidget.h"
|
||||
|
||||
#include "qbsprojectmanager.h"
|
||||
#include "qbsprofilemanager.h"
|
||||
#include "qbsprojectmanagerconstants.h"
|
||||
#include "qbsprojectmanagersettings.h"
|
||||
#include "qbssettings.h"
|
||||
|
||||
#include <app/app_version.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <projectexplorer/kit.h>
|
||||
#include <projectexplorer/kitmanager.h>
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
#include <projectexplorer/projectexplorericons.h>
|
||||
#include <projectexplorer/taskhub.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <qbs.h>
|
||||
#include <utils/treemodel.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QHash>
|
||||
#include <QWidget>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
class ProfileTreeItem : public Utils::TypedTreeItem<ProfileTreeItem, ProfileTreeItem>
|
||||
{
|
||||
public:
|
||||
ProfileTreeItem() = default;
|
||||
ProfileTreeItem(const QString &key, const QString &value) : m_key(key), m_value(value) { }
|
||||
|
||||
private:
|
||||
QVariant data(int column, int role) const override
|
||||
{
|
||||
if (role != Qt::DisplayRole)
|
||||
return {};
|
||||
if (column == 0)
|
||||
return m_key;
|
||||
if (column == 1)
|
||||
return m_value;
|
||||
return {};
|
||||
}
|
||||
|
||||
const QString m_key;
|
||||
const QString m_value;
|
||||
};
|
||||
|
||||
class ProfileModel : public Utils::TreeModel<ProfileTreeItem>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ProfileModel() : TreeModel(static_cast<QObject *>(nullptr))
|
||||
{
|
||||
setHeader(QStringList{tr("Key"), tr("Value")});
|
||||
reload();
|
||||
}
|
||||
|
||||
void reload()
|
||||
{
|
||||
ProfileTreeItem * const newRoot = new ProfileTreeItem(QString(), QString());
|
||||
QHash<QStringList, ProfileTreeItem *> itemMap;
|
||||
const QStringList output = QbsProfileManager::runQbsConfig(
|
||||
QbsProfileManager::QbsConfigOp::Get, "profiles").split('\n', QString::SkipEmptyParts);
|
||||
for (QString line : output) {
|
||||
line = line.trimmed();
|
||||
line = line.mid(QString("profiles.").length());
|
||||
const int colonIndex = line.indexOf(':');
|
||||
if (colonIndex == -1)
|
||||
continue;
|
||||
const QStringList key = line.left(colonIndex).trimmed()
|
||||
.split('.', QString::SkipEmptyParts);
|
||||
const QString value = line.mid(colonIndex + 1).trimmed();
|
||||
QStringList partialKey;
|
||||
ProfileTreeItem *parent = newRoot;
|
||||
for (const QString &keyComponent : key) {
|
||||
partialKey << keyComponent;
|
||||
ProfileTreeItem *&item = itemMap[partialKey];
|
||||
if (!item) {
|
||||
item = new ProfileTreeItem(keyComponent, partialKey == key ? value : QString());
|
||||
parent->appendChild(item);
|
||||
}
|
||||
parent = item;
|
||||
}
|
||||
}
|
||||
setRootItem(newRoot);
|
||||
}
|
||||
};
|
||||
|
||||
class QbsProfilesSettingsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QbsProfilesSettingsWidget();
|
||||
|
||||
void apply();
|
||||
|
||||
private:
|
||||
void refreshKitsList();
|
||||
void displayCurrentProfile();
|
||||
|
||||
Ui::QbsProfilesSettingsWidget m_ui;
|
||||
qbs::SettingsModel m_model;
|
||||
ProfileModel m_model;
|
||||
};
|
||||
|
||||
QbsProfilesSettingsPage::QbsProfilesSettingsPage()
|
||||
: m_useQtcSettingsDirPersistent(QbsProjectManagerSettings::useCreatorSettingsDirForQbs())
|
||||
{
|
||||
setId("Y.QbsProfiles");
|
||||
setDisplayName(QCoreApplication::translate("QbsProjectManager", "Qbs"));
|
||||
setCategory(ProjectExplorer::Constants::KITS_SETTINGS_CATEGORY);
|
||||
setDisplayName(QCoreApplication::translate("QbsProjectManager", "Profiles"));
|
||||
setCategory(Constants::QBS_SETTINGS_CATEGORY);
|
||||
}
|
||||
|
||||
QWidget *QbsProfilesSettingsPage::widget()
|
||||
@@ -79,38 +140,17 @@ QWidget *QbsProfilesSettingsPage::widget()
|
||||
return m_widget;
|
||||
}
|
||||
|
||||
void QbsProfilesSettingsPage::apply()
|
||||
{
|
||||
if (m_widget)
|
||||
m_widget->apply();
|
||||
m_useQtcSettingsDirPersistent = QbsProjectManagerSettings::useCreatorSettingsDirForQbs();
|
||||
}
|
||||
|
||||
void QbsProfilesSettingsPage::finish()
|
||||
{
|
||||
delete m_widget;
|
||||
m_widget = nullptr;
|
||||
QbsProjectManagerSettings::setUseCreatorSettingsDirForQbs(m_useQtcSettingsDirPersistent);
|
||||
QbsProjectManagerSettings::writeSettings();
|
||||
}
|
||||
|
||||
|
||||
QbsProfilesSettingsWidget::QbsProfilesSettingsWidget()
|
||||
: m_model(QbsProjectManagerSettings::qbsSettingsBaseDir(), qbs::Settings::UserScope)
|
||||
{
|
||||
m_model.setEditable(false);
|
||||
m_ui.setupUi(this);
|
||||
m_ui.settingsDirCheckBox->setText(tr("Store profiles in %1 settings directory")
|
||||
.arg(Core::Constants::IDE_DISPLAY_NAME));
|
||||
m_ui.settingsDirCheckBox->setChecked(QbsProjectManagerSettings::useCreatorSettingsDirForQbs());
|
||||
m_ui.versionValueLabel->setText(qbs::LanguageInfo::qbsVersion().toString());
|
||||
connect(ProjectExplorer::KitManager::instance(), &ProjectExplorer::KitManager::kitsChanged,
|
||||
connect(QbsProfileManager::instance(), &QbsProfileManager::qbsProfilesUpdated,
|
||||
this, &QbsProfilesSettingsWidget::refreshKitsList);
|
||||
connect(m_ui.settingsDirCheckBox, &QCheckBox::stateChanged, [this]() {
|
||||
QbsProjectManagerSettings::setUseCreatorSettingsDirForQbs(m_ui.settingsDirCheckBox->isChecked());
|
||||
m_model.updateSettingsDir(QbsProjectManagerSettings::qbsSettingsBaseDir());
|
||||
displayCurrentProfile();
|
||||
});
|
||||
connect(m_ui.expandButton, &QAbstractButton::clicked,
|
||||
m_ui.propertiesView, &QTreeView::expandAll);
|
||||
connect(m_ui.collapseButton, &QAbstractButton::clicked,
|
||||
@@ -118,12 +158,6 @@ QbsProfilesSettingsWidget::QbsProfilesSettingsWidget()
|
||||
refreshKitsList();
|
||||
}
|
||||
|
||||
void QbsProfilesSettingsWidget::apply()
|
||||
{
|
||||
m_model.reload();
|
||||
displayCurrentProfile();
|
||||
}
|
||||
|
||||
void QbsProfilesSettingsWidget::refreshKitsList()
|
||||
{
|
||||
m_ui.kitsComboBox->disconnect(this);
|
||||
@@ -135,10 +169,10 @@ void QbsProfilesSettingsWidget::refreshKitsList()
|
||||
currentId = Core::Id::fromSetting(m_ui.kitsComboBox->currentData());
|
||||
m_ui.kitsComboBox->clear();
|
||||
int newCurrentIndex = -1;
|
||||
QList<ProjectExplorer::Kit *> validKits = ProjectExplorer::KitManager::kits();
|
||||
Utils::erase(validKits, [](const ProjectExplorer::Kit *k) { return !k->isValid(); });
|
||||
QList<Kit *> validKits = KitManager::kits();
|
||||
Utils::erase(validKits, [](const Kit *k) { return !k->isValid(); });
|
||||
const bool hasKits = !validKits.isEmpty();
|
||||
foreach (const ProjectExplorer::Kit * const kit, validKits) {
|
||||
for (const Kit * const kit : qAsConst(validKits)) {
|
||||
if (kit->id() == currentId)
|
||||
newCurrentIndex = m_ui.kitsComboBox->count();
|
||||
m_ui.kitsComboBox->addItem(kit->displayName(), kit->id().toSetting());
|
||||
@@ -159,26 +193,20 @@ void QbsProfilesSettingsWidget::displayCurrentProfile()
|
||||
if (m_ui.kitsComboBox->currentIndex() == -1)
|
||||
return;
|
||||
const Core::Id kitId = Core::Id::fromSetting(m_ui.kitsComboBox->currentData());
|
||||
const ProjectExplorer::Kit * const kit = ProjectExplorer::KitManager::kit(kitId);
|
||||
const Kit * const kit = KitManager::kit(kitId);
|
||||
QTC_ASSERT(kit, return);
|
||||
const QString profileName = QbsManager::profileForKit(kit);
|
||||
const QString profileName = QbsProfileManager::profileForKit(kit);
|
||||
m_ui.profileValueLabel->setText(profileName);
|
||||
for (int i = 0; i < m_model.rowCount(); ++i) {
|
||||
const QModelIndex profilesIndex = m_model.index(i, 0);
|
||||
if (m_model.data(profilesIndex).toString() != QLatin1String("profiles"))
|
||||
continue;
|
||||
for (int i = 0; i < m_model.rowCount(profilesIndex); ++i) {
|
||||
const QModelIndex currentProfileIndex = m_model.index(i, 0, profilesIndex);
|
||||
if (m_model.data(currentProfileIndex).toString() != profileName)
|
||||
const QModelIndex currentProfileIndex = m_model.index(i, 0);
|
||||
if (m_model.data(currentProfileIndex, Qt::DisplayRole).toString() != profileName)
|
||||
continue;
|
||||
m_ui.propertiesView->setModel(&m_model);
|
||||
m_ui.propertiesView->header()->setSectionResizeMode(m_model.keyColumn(),
|
||||
QHeaderView::ResizeToContents);
|
||||
m_ui.propertiesView->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
||||
m_ui.propertiesView->setRootIndex(currentProfileIndex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QbsProjectManager
|
||||
|
@@ -38,11 +38,10 @@ public:
|
||||
|
||||
private:
|
||||
QWidget *widget() override;
|
||||
void apply() override;
|
||||
void apply() override { }
|
||||
void finish() override;
|
||||
|
||||
QbsProfilesSettingsWidget *m_widget = nullptr;
|
||||
bool m_useQtcSettingsDirPersistent;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -14,23 +14,16 @@
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="settingsDirCheckBox">
|
||||
<property name="text">
|
||||
<string>Store profiles in Qt Creator settings directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="1" column="0">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="kitLabel">
|
||||
<property name="text">
|
||||
<string>Kit:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="kitsComboBox"/>
|
||||
@@ -50,14 +43,14 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="profileKeyLabel">
|
||||
<property name="text">
|
||||
<string>Associated profile:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="profileValueLabel">
|
||||
<property name="text">
|
||||
<string/>
|
||||
@@ -67,23 +60,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="versionKeyLabel">
|
||||
<property name="text">
|
||||
<string>Qbs version:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="versionValueLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "qbsprojectmanager.h"
|
||||
#include "qbsprofilemanager.h"
|
||||
|
||||
#include "qbsnodes.h"
|
||||
|
||||
@@ -36,9 +36,8 @@
|
||||
|
||||
#include <utils/environment.h>
|
||||
|
||||
#include <qbs.h>
|
||||
|
||||
#include <QHash>
|
||||
#include <QJsonObject>
|
||||
#include <QTimer>
|
||||
|
||||
namespace CppTools { class CppProjectUpdater; }
|
||||
@@ -46,8 +45,10 @@ namespace CppTools { class CppProjectUpdater; }
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
class ErrorInfo;
|
||||
class QbsBuildConfiguration;
|
||||
class QbsProjectParser;
|
||||
class QbsSession;
|
||||
|
||||
class QbsProject : public ProjectExplorer::Project
|
||||
{
|
||||
@@ -63,8 +64,6 @@ public:
|
||||
|
||||
void configureAsExampleProject() final;
|
||||
|
||||
static QString uniqueProductName(const qbs::ProductData &product);
|
||||
|
||||
private:
|
||||
mutable ProjectExplorer::ProjectImporter *m_importer = nullptr;
|
||||
};
|
||||
@@ -94,24 +93,17 @@ public:
|
||||
QVariant additionalData(Core::Id id) const final;
|
||||
|
||||
bool isProjectEditable() const;
|
||||
// qbs::ProductData and qbs::GroupData are held by the nodes in the project tree.
|
||||
// These methods change those trees and invalidate the lot, so pass in copies of
|
||||
// the data we are interested in!
|
||||
// The overhead is not as big as it seems at first glance: These all are handles
|
||||
// for shared data.
|
||||
bool addFilesToProduct(const QStringList &filePaths, const qbs::ProductData productData,
|
||||
const qbs::GroupData groupData, QStringList *notAdded);
|
||||
bool addFilesToProduct(const QStringList &filePaths,
|
||||
const QJsonObject &product,
|
||||
const QJsonObject &group,
|
||||
QStringList *notAdded);
|
||||
ProjectExplorer::RemovedFilesFromProject removeFilesFromProduct(const QStringList &filePaths,
|
||||
const qbs::ProductData &productData, const qbs::GroupData &groupData,
|
||||
const QJsonObject &product,
|
||||
const QJsonObject &group,
|
||||
QStringList *notRemoved);
|
||||
bool renameFileInProduct(const QString &oldPath,
|
||||
const QString &newPath, const qbs::ProductData productData,
|
||||
const qbs::GroupData groupData);
|
||||
|
||||
qbs::BuildJob *build(const qbs::BuildOptions &opts, QStringList products, QString &error);
|
||||
qbs::CleanJob *clean(const qbs::CleanOptions &opts, const QStringList &productNames,
|
||||
QString &error);
|
||||
qbs::InstallJob *install(const qbs::InstallOptions &opts);
|
||||
const QString &newPath, const QJsonObject &product,
|
||||
const QJsonObject &group);
|
||||
|
||||
static ProjectExplorer::FileType fileTypeFor(const QSet<QString> &tags);
|
||||
|
||||
@@ -122,15 +114,16 @@ public:
|
||||
void cancelParsing();
|
||||
void updateAfterBuild();
|
||||
|
||||
qbs::Project qbsProject() const;
|
||||
qbs::ProjectData qbsProjectData() const;
|
||||
QbsSession *session() const { return m_session; }
|
||||
QJsonObject projectData() const { return m_projectData; }
|
||||
|
||||
void generateErrors(const qbs::ErrorInfo &e);
|
||||
void generateErrors(const ErrorInfo &e);
|
||||
|
||||
void delayParsing();
|
||||
|
||||
private:
|
||||
friend class QbsProject;
|
||||
|
||||
void handleQbsParsingDone(bool success);
|
||||
|
||||
void rebuildProjectTree();
|
||||
@@ -138,13 +131,12 @@ private:
|
||||
void changeActiveTarget(ProjectExplorer::Target *t);
|
||||
|
||||
void prepareForParsing();
|
||||
void updateDocuments(const std::set<QString> &files);
|
||||
void updateDocuments();
|
||||
void updateCppCodeModel();
|
||||
void updateQmlJsCodeModel();
|
||||
void updateApplicationTargets();
|
||||
void updateDeploymentInfo();
|
||||
void updateBuildTargetData();
|
||||
void handleRuleExecutionDone();
|
||||
bool checkCancelStatus();
|
||||
void updateAfterParse();
|
||||
void delayedUpdateAfterParse();
|
||||
@@ -153,16 +145,14 @@ private:
|
||||
|
||||
static bool ensureWriteableQbsFile(const QString &file);
|
||||
|
||||
template<typename Options> qbs::AbstractJob *buildOrClean(const Options &opts,
|
||||
const QStringList &productNames, QString &error);
|
||||
|
||||
qbs::Project m_qbsProject;
|
||||
qbs::ProjectData m_projectData; // Cached m_qbsProject.projectData()
|
||||
Utils::Environment m_lastParseEnv;
|
||||
QbsSession * const m_session;
|
||||
QSet<Core::IDocument *> m_qbsDocuments;
|
||||
QJsonObject m_projectData; // TODO: Perhaps store this in the root project node instead?
|
||||
|
||||
QTimer m_parsingDelay;
|
||||
QbsProjectParser *m_qbsProjectParser = nullptr;
|
||||
|
||||
QFutureInterface<bool> *m_qbsUpdateFutureInterface = nullptr;
|
||||
Utils::Environment m_lastParseEnv;
|
||||
bool m_parsingScheduled = false;
|
||||
|
||||
enum CancelStatus {
|
||||
@@ -173,9 +163,8 @@ private:
|
||||
|
||||
CppTools::CppProjectUpdater *m_cppCodeModelUpdater = nullptr;
|
||||
|
||||
QTimer m_parsingDelay;
|
||||
QHash<ProjectExplorer::ExtraCompilerFactory *, QStringList> m_sourcesForGeneratedFiles;
|
||||
QList<ProjectExplorer::ExtraCompiler *> m_extraCompilers;
|
||||
bool m_extraCompilersPending = false;
|
||||
|
||||
QHash<QString, Utils::Environment> m_envCache;
|
||||
|
||||
|
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "qbsbuildconfiguration.h"
|
||||
#include "qbspmlogging.h"
|
||||
#include "qbssession.h"
|
||||
|
||||
#include <coreplugin/documentmanager.h>
|
||||
#include <projectexplorer/buildconfiguration.h>
|
||||
@@ -42,8 +43,6 @@
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
|
||||
#include <qbs.h>
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
@@ -62,10 +61,10 @@ struct BuildGraphData
|
||||
FilePath sysroot;
|
||||
QString buildVariant;
|
||||
};
|
||||
static BuildGraphData extractBgData(const qbs::Project::BuildGraphInfo &bgInfo)
|
||||
static BuildGraphData extractBgData(const QbsSession::BuildGraphInfo &bgInfo)
|
||||
{
|
||||
BuildGraphData bgData;
|
||||
bgData.bgFilePath = FilePath::fromString(bgInfo.bgFilePath);
|
||||
bgData.bgFilePath = bgInfo.bgFilePath;
|
||||
bgData.overriddenProperties = bgInfo.overriddenProperties;
|
||||
const QVariantMap &moduleProps = bgInfo.requestedProperties;
|
||||
const QVariantMap prjCompilerPathByLanguage
|
||||
@@ -139,15 +138,15 @@ QList<void *> QbsProjectImporter::examineDirectory(const FilePath &importPath) c
|
||||
{
|
||||
qCDebug(qbsPmLog) << "examining build directory" << importPath.toUserOutput();
|
||||
QList<void *> data;
|
||||
const QString bgFilePath = importPath.toString() + QLatin1Char('/') + importPath.fileName()
|
||||
+ QLatin1String(".bg");
|
||||
const FilePath bgFilePath = importPath.pathAppended(importPath.fileName() + ".bg");
|
||||
const QStringList relevantProperties({
|
||||
"qbs.buildVariant", "qbs.sysroot", "qbs.toolchain",
|
||||
"cpp.compilerPath", "cpp.compilerPathByLanguage",
|
||||
"Qt.core.binPath"
|
||||
});
|
||||
const qbs::Project::BuildGraphInfo bgInfo
|
||||
= qbs::Project::getBuildGraphInfo(bgFilePath, relevantProperties);
|
||||
|
||||
const QbsSession::BuildGraphInfo bgInfo = QbsSession::getBuildGraphInfo(
|
||||
bgFilePath, relevantProperties);
|
||||
if (bgInfo.error.hasError()) {
|
||||
qCDebug(qbsPmLog) << "error getting build graph info:" << bgInfo.error.toString();
|
||||
return data;
|
||||
|
@@ -1,209 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "qbsprojectmanager.h"
|
||||
|
||||
#include "defaultpropertyprovider.h"
|
||||
#include "qbslogsink.h"
|
||||
#include "qbsproject.h"
|
||||
#include "qbsprojectmanagerconstants.h"
|
||||
#include "qbsprojectmanagerplugin.h"
|
||||
#include "qbsprojectmanagersettings.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 <QCryptographicHash>
|
||||
#include <QVariantMap>
|
||||
|
||||
#include <qbs.h>
|
||||
|
||||
const QChar sep = QLatin1Char('.');
|
||||
|
||||
static QString qtcProfileGroup() { return QLatin1String("preferences.qtcreator.kit"); }
|
||||
static QString qtcProfilePrefix() { return qtcProfileGroup() + sep; }
|
||||
|
||||
namespace QbsProjectManager {
|
||||
|
||||
static QList<PropertyProvider *> g_propertyProviders;
|
||||
|
||||
PropertyProvider::PropertyProvider()
|
||||
{
|
||||
g_propertyProviders.append(this);
|
||||
}
|
||||
|
||||
PropertyProvider::~PropertyProvider()
|
||||
{
|
||||
g_propertyProviders.removeOne(this);
|
||||
}
|
||||
|
||||
namespace Internal {
|
||||
|
||||
static qbs::Settings *m_settings = nullptr;
|
||||
static Internal::QbsLogSink *m_logSink = nullptr;
|
||||
static QbsManager *m_instance = nullptr;
|
||||
|
||||
QbsManager::QbsManager() : 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,
|
||||
&QbsManager::addProfileFromKit);
|
||||
connect(ProjectExplorer::KitManager::instance(), &ProjectExplorer::KitManager::kitUpdated, this,
|
||||
&QbsManager::handleKitUpdate);
|
||||
connect(ProjectExplorer::KitManager::instance(), &ProjectExplorer::KitManager::kitRemoved, this,
|
||||
&QbsManager::handleKitRemoval);
|
||||
connect(&QbsProjectManagerSettings::instance(), &QbsProjectManagerSettings::settingsBaseChanged,
|
||||
this, &QbsManager::updateAllProfiles);
|
||||
|
||||
m_logSink = new QbsLogSink(this);
|
||||
int level = qbs::LoggerInfo;
|
||||
const QString levelEnv = QString::fromLocal8Bit(qgetenv("QBS_LOG_LEVEL"));
|
||||
if (!levelEnv.isEmpty()) {
|
||||
bool ok = false;
|
||||
int tmp = levelEnv.toInt(&ok);
|
||||
if (ok) {
|
||||
if (tmp < static_cast<int>(qbs::LoggerMinLevel))
|
||||
tmp = static_cast<int>(qbs::LoggerMinLevel);
|
||||
if (tmp > static_cast<int>(qbs::LoggerMaxLevel))
|
||||
tmp = static_cast<int>(qbs::LoggerMaxLevel);
|
||||
level = tmp;
|
||||
}
|
||||
}
|
||||
m_logSink->setLogLevel(static_cast<qbs::LoggerLevel>(level));
|
||||
}
|
||||
|
||||
QbsManager::~QbsManager()
|
||||
{
|
||||
delete m_defaultPropertyProvider;
|
||||
delete m_settings;
|
||||
m_instance = nullptr;
|
||||
}
|
||||
|
||||
QString QbsManager::profileForKit(const ProjectExplorer::Kit *k)
|
||||
{
|
||||
if (!k)
|
||||
return QString();
|
||||
m_instance->updateProfileIfNecessary(k);
|
||||
return settings()->value(qtcProfilePrefix() + k->id().toString(), qbs::Settings::UserScope)
|
||||
.toString();
|
||||
}
|
||||
|
||||
void QbsManager::setProfileForKit(const QString &name, const ProjectExplorer::Kit *k)
|
||||
{
|
||||
settings()->setValue(qtcProfilePrefix() + k->id().toString(), name);
|
||||
}
|
||||
|
||||
void QbsManager::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 QbsManager::updateAllProfiles()
|
||||
{
|
||||
foreach (const auto * const kit, ProjectExplorer::KitManager::kits())
|
||||
addProfileFromKit(kit);
|
||||
}
|
||||
|
||||
qbs::Settings *QbsManager::settings()
|
||||
{
|
||||
if (!m_settings
|
||||
|| m_settings->baseDirectory() != QbsProjectManagerSettings::qbsSettingsBaseDir()) {
|
||||
delete m_settings;
|
||||
m_settings = new qbs::Settings(QbsProjectManagerSettings::qbsSettingsBaseDir());
|
||||
}
|
||||
return m_settings;
|
||||
}
|
||||
|
||||
QbsLogSink *QbsManager::logSink()
|
||||
{
|
||||
return m_logSink;
|
||||
}
|
||||
|
||||
void QbsManager::addProfile(const QString &name, const QVariantMap &data)
|
||||
{
|
||||
qbs::Profile profile(name, settings());
|
||||
const QVariantMap::ConstIterator cend = data.constEnd();
|
||||
for (QVariantMap::ConstIterator it = data.constBegin(); it != cend; ++it)
|
||||
profile.setValue(it.key(), it.value());
|
||||
}
|
||||
|
||||
void QbsManager::addQtProfileFromKit(const QString &profileName, const ProjectExplorer::Kit *k)
|
||||
{
|
||||
if (const QtSupport::BaseQtVersion * const qt = QtSupport::QtKitAspect::qtVersion(k)) {
|
||||
qbs::Profile(profileName, settings()).setValue("moduleProviders.Qt.qmakeFilePaths",
|
||||
qt->qmakeCommand().toString());
|
||||
}
|
||||
}
|
||||
|
||||
void QbsManager::addProfileFromKit(const ProjectExplorer::Kit *k)
|
||||
{
|
||||
const QString name = QString::fromLatin1("qtc_%1_%2").arg(k->fileSystemFriendlyName().left(8),
|
||||
QString::fromLatin1(QCryptographicHash::hash(k->id().name(),
|
||||
QCryptographicHash::Sha1).toHex().left(8)));
|
||||
qbs::Profile(name, settings()).removeProfile();
|
||||
setProfileForKit(name, k);
|
||||
addQtProfileFromKit(name, k);
|
||||
|
||||
// set up properties:
|
||||
QVariantMap data = m_defaultPropertyProvider->properties(k, QVariantMap());
|
||||
for (PropertyProvider *provider : g_propertyProviders) {
|
||||
if (provider->canHandle(k))
|
||||
data = provider->properties(k, data);
|
||||
}
|
||||
|
||||
addProfile(name, data);
|
||||
}
|
||||
|
||||
void QbsManager::handleKitUpdate(ProjectExplorer::Kit *kit)
|
||||
{
|
||||
m_kitsToBeSetupForQbs.removeOne(kit);
|
||||
addProfileFromKit(kit);
|
||||
}
|
||||
|
||||
void QbsManager::handleKitRemoval(ProjectExplorer::Kit *kit)
|
||||
{
|
||||
m_kitsToBeSetupForQbs.removeOne(kit);
|
||||
const QString key = qtcProfilePrefix() + kit->id().toString();
|
||||
const QString profileName = settings()->value(key, qbs::Settings::UserScope).toString();
|
||||
settings()->remove(key);
|
||||
qbs::Profile(profileName, settings()).removeProfile();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QbsProjectManager
|
@@ -1,20 +1,10 @@
|
||||
include(../../qtcreatorplugin.pri)
|
||||
|
||||
# Look for qbs in the environment
|
||||
isEmpty(QBS_INSTALL_DIR): QBS_INSTALL_DIR = $$(QBS_INSTALL_DIR)
|
||||
isEmpty(QBS_INSTALL_DIR) {
|
||||
QBS_SOURCE_DIR = $$PWD/../../shared/qbs
|
||||
include($$QBS_SOURCE_DIR/src/lib/corelib/use_corelib.pri)
|
||||
osx:QMAKE_LFLAGS += -Wl,-rpath,@loader_path/../Frameworks # OS X: fix rpath for qbscore soname
|
||||
} else {
|
||||
include($${QBS_INSTALL_DIR}/include/qbs/use_installed_corelib.pri)
|
||||
}
|
||||
QBS_INSTALL_DIR_FWD_SLASHES = $$replace(QBS_INSTALL_DIR, \\\\, /)
|
||||
DEFINES += QBS_INSTALL_DIR=\\\"$$QBS_INSTALL_DIR_FWD_SLASHES\\\"
|
||||
|
||||
DEFINES += \
|
||||
QBSPROJECTMANAGER_LIBRARY
|
||||
|
||||
QT += qml
|
||||
|
||||
HEADERS = \
|
||||
customqbspropertiesdialog.h \
|
||||
defaultpropertyprovider.h \
|
||||
@@ -24,20 +14,20 @@ HEADERS = \
|
||||
qbscleanstep.h \
|
||||
qbskitinformation.h \
|
||||
qbsinstallstep.h \
|
||||
qbslogsink.h \
|
||||
qbsnodes.h \
|
||||
qbsnodetreebuilder.h \
|
||||
qbsparser.h \
|
||||
qbspmlogging.h \
|
||||
qbsprofilemanager.h \
|
||||
qbsprofilessettingspage.h \
|
||||
qbsproject.h \
|
||||
qbsprojectimporter.h \
|
||||
qbsprojectmanager.h \
|
||||
qbsprojectmanager_global.h \
|
||||
qbsprojectmanagerconstants.h \
|
||||
qbsprojectmanagerplugin.h \
|
||||
qbsprojectmanagersettings.h \
|
||||
qbsprojectparser.h
|
||||
qbsprojectparser.h \
|
||||
qbssession.h \
|
||||
qbssettings.h
|
||||
|
||||
SOURCES = \
|
||||
customqbspropertiesdialog.cpp \
|
||||
@@ -47,18 +37,18 @@ SOURCES = \
|
||||
qbscleanstep.cpp \
|
||||
qbsinstallstep.cpp \
|
||||
qbskitinformation.cpp \
|
||||
qbslogsink.cpp \
|
||||
qbsnodes.cpp \
|
||||
qbsnodetreebuilder.cpp \
|
||||
qbsparser.cpp \
|
||||
qbspmlogging.cpp \
|
||||
qbsprofilemanager.cpp \
|
||||
qbsprofilessettingspage.cpp \
|
||||
qbsproject.cpp \
|
||||
qbsprojectimporter.cpp \
|
||||
qbsprojectmanager.cpp \
|
||||
qbsprojectmanagerplugin.cpp \
|
||||
qbsprojectmanagersettings.cpp \
|
||||
qbsprojectparser.cpp
|
||||
qbsprojectparser.cpp \
|
||||
qbssession.cpp \
|
||||
qbssettings.cpp
|
||||
|
||||
FORMS = \
|
||||
customqbspropertiesdialog.ui \
|
||||
|
@@ -6,33 +6,8 @@ QtcPlugin {
|
||||
name: "QbsProjectManager"
|
||||
type: base.concat(["qmltype-update"])
|
||||
|
||||
property var externalQbsIncludes: project.useExternalQbs
|
||||
? [project.qbs_install_dir + "/include/qbs"] : []
|
||||
property var externalQbsLibraryPaths: project.useExternalQbs
|
||||
? [project.qbs_install_dir + '/' + qtc.libDirName] : []
|
||||
property var externalQbsDynamicLibraries: {
|
||||
var libs = []
|
||||
if (!project.useExternalQbs)
|
||||
return libs;
|
||||
var suffix = "";
|
||||
if (qbs.targetOS.contains("windows")) {
|
||||
libs.push("shell32")
|
||||
if (qbs.enableDebugCode)
|
||||
suffix = "d";
|
||||
}
|
||||
libs.push("qbscore" + suffix);
|
||||
return libs
|
||||
}
|
||||
Depends { name: "Qt"; submodules: [ "qml", "widgets" ] }
|
||||
|
||||
condition: project.buildQbsProjectManager
|
||||
|
||||
property bool useInternalQbsProducts: project.qbsSubModuleExists && !project.useExternalQbs
|
||||
|
||||
Depends { name: "Qt"; submodules: [ "widgets" ] }
|
||||
Depends {
|
||||
name: "qbscore"
|
||||
condition: product.useInternalQbsProducts
|
||||
}
|
||||
Depends { name: "QmlJS" }
|
||||
Depends { name: "Utils" }
|
||||
|
||||
@@ -43,20 +18,6 @@ QtcPlugin {
|
||||
Depends { name: "QmlJSTools" }
|
||||
Depends { name: "app_version_header" }
|
||||
|
||||
cpp.defines: base.concat([
|
||||
'QML_BUILD_STATIC_LIB',
|
||||
'QBS_ENABLE_PROJECT_FILE_UPDATES', // TODO: Take from installed qbscore module
|
||||
'QBS_INSTALL_DIR="'
|
||||
+ (project.useExternalQbs
|
||||
? FileInfo.fromWindowsSeparators(project.qbs_install_dir)
|
||||
: '')
|
||||
+ '"'
|
||||
])
|
||||
cpp.includePaths: base.concat(externalQbsIncludes)
|
||||
cpp.libraryPaths: base.concat(externalQbsLibraryPaths)
|
||||
cpp.rpaths: base.concat(externalQbsLibraryPaths)
|
||||
cpp.dynamicLibraries: base.concat(externalQbsDynamicLibraries)
|
||||
|
||||
files: [
|
||||
"customqbspropertiesdialog.h",
|
||||
"customqbspropertiesdialog.cpp",
|
||||
@@ -75,8 +36,6 @@ QtcPlugin {
|
||||
"qbsinstallstep.h",
|
||||
"qbskitinformation.cpp",
|
||||
"qbskitinformation.h",
|
||||
"qbslogsink.cpp",
|
||||
"qbslogsink.h",
|
||||
"qbsnodes.cpp",
|
||||
"qbsnodes.h",
|
||||
"qbsnodetreebuilder.cpp",
|
||||
@@ -85,6 +44,8 @@ QtcPlugin {
|
||||
"qbsparser.h",
|
||||
"qbspmlogging.cpp",
|
||||
"qbspmlogging.h",
|
||||
"qbsprofilemanager.cpp",
|
||||
"qbsprofilemanager.h",
|
||||
"qbsprofilessettingspage.cpp",
|
||||
"qbsprofilessettingspage.h",
|
||||
"qbsprofilessettingswidget.ui",
|
||||
@@ -92,23 +53,21 @@ QtcPlugin {
|
||||
"qbsproject.h",
|
||||
"qbsprojectimporter.cpp",
|
||||
"qbsprojectimporter.h",
|
||||
"qbsprojectmanager.cpp",
|
||||
"qbsprojectmanager.h",
|
||||
"qbsprojectmanager.qrc",
|
||||
"qbsprojectmanager_global.h",
|
||||
"qbsprojectmanagerconstants.h",
|
||||
"qbsprojectmanagerplugin.cpp",
|
||||
"qbsprojectmanagerplugin.h",
|
||||
"qbsprojectmanagersettings.cpp",
|
||||
"qbsprojectmanagersettings.h",
|
||||
"qbsprojectparser.cpp",
|
||||
"qbsprojectparser.h",
|
||||
"qbssession.cpp",
|
||||
"qbssession.h",
|
||||
"qbssettings.cpp",
|
||||
"qbssettings.h",
|
||||
]
|
||||
|
||||
// QML typeinfo stuff
|
||||
property bool updateQmlTypeInfo: useInternalQbsProducts
|
||||
Group {
|
||||
condition: !updateQmlTypeInfo
|
||||
name: "qbs qml type info"
|
||||
qbs.install: true
|
||||
qbs.installDir: FileInfo.joinPaths(qtc.ide_data_path, "qtcreator",
|
||||
@@ -121,9 +80,9 @@ QtcPlugin {
|
||||
]
|
||||
}
|
||||
|
||||
Depends { name: "qbs resources"; condition: updateQmlTypeInfo }
|
||||
Depends { name: "qbs resources"; condition: project.qbsSubModuleExists }
|
||||
Rule {
|
||||
condition: updateQmlTypeInfo
|
||||
condition: project.qbsSubModuleExists
|
||||
inputsFromDependencies: ["qbs qml type descriptions", "qbs qml type bundle"]
|
||||
Artifact {
|
||||
filePath: "dummy." + input.fileName
|
||||
|
@@ -93,7 +93,7 @@ const char XCODE_DEVELOPERPATH[] = "xcode.developerPath";
|
||||
const char XCODE_SDK[] = "xcode.sdk";
|
||||
|
||||
// Settings page
|
||||
const char QBS_SETTINGS_CATEGORY[] = "YM.qbs";
|
||||
const char QBS_SETTINGS_CATEGORY[] = "K.Qbs";
|
||||
const char QBS_SETTINGS_TR_CATEGORY[] = QT_TRANSLATE_NOOP("QbsProjectManager", "Qbs");
|
||||
const char QBS_SETTINGS_CATEGORY_ICON[] = ":/projectexplorer/images/build.png";
|
||||
|
||||
|
@@ -31,10 +31,11 @@
|
||||
#include "qbsinstallstep.h"
|
||||
#include "qbskitinformation.h"
|
||||
#include "qbsnodes.h"
|
||||
#include "qbsprofilemanager.h"
|
||||
#include "qbsprofilessettingspage.h"
|
||||
#include "qbsproject.h"
|
||||
#include "qbsprojectmanager.h"
|
||||
#include "qbsprojectmanagerconstants.h"
|
||||
#include "qbssettings.h"
|
||||
|
||||
#include <coreplugin/actionmanager/actioncontainer.h>
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
@@ -85,12 +86,13 @@ static QbsProject *currentEditorProject()
|
||||
class QbsProjectManagerPluginPrivate
|
||||
{
|
||||
public:
|
||||
QbsManager manager;
|
||||
QbsProfileManager manager;
|
||||
QbsBuildConfigurationFactory buildConfigFactory;
|
||||
QbsBuildStepFactory buildStepFactory;
|
||||
QbsCleanStepFactory cleanStepFactory;
|
||||
QbsInstallStepFactory installStepFactory;
|
||||
QbsProfilesSettingsPage profilesSettingsPage;
|
||||
QbsSettingsPage settingsPage;
|
||||
QbsProfilesSettingsPage profilesSetttingsPage;
|
||||
QbsKitAspect qbsKitAspect;
|
||||
};
|
||||
|
||||
@@ -419,7 +421,7 @@ void QbsProjectManagerPlugin::runStepsForProductContextMenu(const QList<Core::Id
|
||||
const auto * const productNode = dynamic_cast<const QbsProductNode *>(node);
|
||||
QTC_ASSERT(productNode, return);
|
||||
|
||||
runStepsForProducts(project, {QbsProject::uniqueProductName(productNode->qbsProductData())},
|
||||
runStepsForProducts(project, {productNode->productData().value("full-display-name").toString()},
|
||||
{stepTypes});
|
||||
}
|
||||
|
||||
@@ -446,13 +448,13 @@ void QbsProjectManagerPlugin::runStepsForProduct(const QList<Core::Id> &stepType
|
||||
Node *node = currentEditorNode();
|
||||
if (!node)
|
||||
return;
|
||||
auto product = dynamic_cast<QbsProductNode *>(node->parentProjectNode());
|
||||
if (!product)
|
||||
auto productNode = dynamic_cast<QbsProductNode *>(node->parentProjectNode());
|
||||
if (!productNode)
|
||||
return;
|
||||
QbsProject *project = currentEditorProject();
|
||||
if (!project)
|
||||
return;
|
||||
runStepsForProducts(project, {QbsProject::uniqueProductName(product->qbsProductData())},
|
||||
runStepsForProducts(project, {productNode->productData().value("full-display-name").toString()},
|
||||
{stepTypes});
|
||||
}
|
||||
|
||||
@@ -485,9 +487,9 @@ void QbsProjectManagerPlugin::runStepsForSubprojectContextMenu(const QList<Core:
|
||||
QTC_ASSERT(subProject, return);
|
||||
|
||||
QStringList toBuild;
|
||||
foreach (const qbs::ProductData &data, subProject->qbsProjectData().allProducts())
|
||||
toBuild << QbsProject::uniqueProductName(data);
|
||||
|
||||
forAllProducts(subProject->projectData(), [&toBuild](const QJsonObject &data) {
|
||||
toBuild << data.value("full-display-name").toString();
|
||||
});
|
||||
runStepsForProducts(project, toBuild, {stepTypes});
|
||||
}
|
||||
|
||||
|
@@ -1,82 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "qbsprojectmanagersettings.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
static QString settingsId() { return QLatin1String("QbsProjectManager"); }
|
||||
static QString useCreatorDirKey() { return QLatin1String("useCreatorDir"); }
|
||||
|
||||
QbsProjectManagerSettings::QbsProjectManagerSettings()
|
||||
{
|
||||
readSettings();
|
||||
}
|
||||
|
||||
void QbsProjectManagerSettings::readSettings()
|
||||
{
|
||||
QSettings * const settings = Core::ICore::settings();
|
||||
settings->beginGroup(settingsId());
|
||||
m_useCreatorSettings = settings->value(useCreatorDirKey(), true).toBool();
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
void QbsProjectManagerSettings::doWriteSettings()
|
||||
{
|
||||
QSettings * const settings = Core::ICore::settings();
|
||||
settings->beginGroup(settingsId());
|
||||
settings->setValue(useCreatorDirKey(), m_useCreatorSettings);
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
QbsProjectManagerSettings &QbsProjectManagerSettings::instance()
|
||||
{
|
||||
static QbsProjectManagerSettings settings;
|
||||
return settings;
|
||||
}
|
||||
|
||||
void QbsProjectManagerSettings::setUseCreatorSettingsDirForQbs(bool useCreatorDir)
|
||||
{
|
||||
if (instance().m_useCreatorSettings == useCreatorDir)
|
||||
return;
|
||||
instance().m_useCreatorSettings = useCreatorDir;
|
||||
emit instance().settingsBaseChanged();
|
||||
}
|
||||
|
||||
bool QbsProjectManagerSettings::useCreatorSettingsDirForQbs()
|
||||
{
|
||||
return instance().m_useCreatorSettings;
|
||||
}
|
||||
|
||||
QString QbsProjectManagerSettings::qbsSettingsBaseDir()
|
||||
{
|
||||
return instance().m_useCreatorSettings ? Core::ICore::userResourcePath() : QString();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QbsProjectManager
|
@@ -25,19 +25,16 @@
|
||||
|
||||
#include "qbsprojectparser.h"
|
||||
|
||||
#include "qbslogsink.h"
|
||||
#include "qbsproject.h"
|
||||
#include "qbsprojectmanagerconstants.h"
|
||||
#include "qbssettings.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <qbs.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QFutureWatcher>
|
||||
#include <QJsonArray>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
@@ -48,11 +45,11 @@ namespace Internal {
|
||||
// QbsProjectParser:
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
QbsProjectParser::QbsProjectParser(QbsBuildSystem *buildSystem, QFutureInterface<bool> *fi) :
|
||||
QbsProjectParser::QbsProjectParser(QbsBuildSystem *buildSystem, QFutureInterface<bool> *fi)
|
||||
: m_projectFilePath(buildSystem->project()->projectFilePath().toString()),
|
||||
m_session(buildSystem->session()),
|
||||
m_fi(fi)
|
||||
{
|
||||
m_project = buildSystem->qbsProject();
|
||||
m_projectFilePath = buildSystem->projectFilePath().toString();
|
||||
auto * const watcher = new QFutureWatcher<bool>(this);
|
||||
connect(watcher, &QFutureWatcher<bool>::canceled, this, &QbsProjectParser::cancel);
|
||||
watcher->setFuture(fi->future());
|
||||
@@ -60,171 +57,77 @@ QbsProjectParser::QbsProjectParser(QbsBuildSystem *buildSystem, QFutureInterface
|
||||
|
||||
QbsProjectParser::~QbsProjectParser()
|
||||
{
|
||||
const auto deleteJob = [this](qbs::AbstractJob *job) {
|
||||
if (!job)
|
||||
return;
|
||||
if (job->state() == qbs::AbstractJob::StateFinished) {
|
||||
job->deleteLater();
|
||||
return;
|
||||
}
|
||||
connect(job, &qbs::AbstractJob::finished, job, &qbs::AbstractJob::deleteLater);
|
||||
job->cancel();
|
||||
};
|
||||
deleteJob(m_qbsSetupProjectJob);
|
||||
deleteJob(m_ruleExecutionJob);
|
||||
if (m_session && m_parsing)
|
||||
m_session->cancelCurrentJob();
|
||||
m_fi = nullptr; // we do not own m_fi, do not delete
|
||||
}
|
||||
|
||||
void QbsProjectParser::parse(const QVariantMap &config, const Environment &env, const QString &dir,
|
||||
const QString &configName)
|
||||
{
|
||||
QTC_ASSERT(!m_qbsSetupProjectJob, return);
|
||||
QTC_ASSERT(m_session, return);
|
||||
QTC_ASSERT(!dir.isEmpty(), return);
|
||||
|
||||
m_currentProgressBase = 0;
|
||||
m_environment = env;
|
||||
|
||||
qbs::SetupProjectParameters params;
|
||||
QJsonObject request;
|
||||
request.insert("type", "resolve-project");
|
||||
QVariantMap userConfig = config;
|
||||
QString specialKey = QLatin1String(Constants::QBS_CONFIG_PROFILE_KEY);
|
||||
const QString profileName = userConfig.take(specialKey).toString();
|
||||
params.setTopLevelProfile(profileName);
|
||||
params.setConfigurationName(configName);
|
||||
specialKey = QLatin1String(Constants::QBS_FORCE_PROBES_KEY);
|
||||
params.setForceProbeExecution(userConfig.take(specialKey).toBool());
|
||||
params.setSettingsDirectory(QbsManager::settings()->baseDirectory());
|
||||
params.setOverriddenValues(userConfig);
|
||||
request.insert("top-level-profile",
|
||||
userConfig.take(Constants::QBS_CONFIG_PROFILE_KEY).toString());
|
||||
request.insert("configuration-name", configName);
|
||||
request.insert("force-probe-execution",
|
||||
userConfig.take(Constants::QBS_FORCE_PROBES_KEY).toBool());
|
||||
if (QbsSettings::useCreatorSettingsDirForQbs())
|
||||
request.insert("settings-directory", QbsSettings::qbsSettingsBaseDir());
|
||||
request.insert("overridden-properties", QJsonObject::fromVariantMap(userConfig));
|
||||
|
||||
// Some people don't like it when files are created as a side effect of opening a project,
|
||||
// People don't like it when files are created as a side effect of opening a project,
|
||||
// so do not store the build graph if the build directory does not exist yet.
|
||||
m_dryRun = !QFileInfo::exists(dir);
|
||||
params.setDryRun(m_dryRun);
|
||||
request.insert("dry-run", !QFileInfo::exists(dir));
|
||||
|
||||
params.setBuildRoot(dir);
|
||||
params.setProjectFilePath(m_projectFilePath);
|
||||
params.setOverrideBuildGraphData(true);
|
||||
params.setEnvironment(env.toProcessEnvironment());
|
||||
const qbs::Preferences prefs(QbsManager::settings(), profileName);
|
||||
params.setSearchPaths(prefs.searchPaths(resourcesBaseDirectory()));
|
||||
params.setPluginPaths(prefs.pluginPaths(pluginsBaseDirectory()));
|
||||
params.setLibexecPath(libExecDirectory());
|
||||
params.setProductErrorMode(qbs::ErrorHandlingMode::Relaxed);
|
||||
params.setPropertyCheckingMode(qbs::ErrorHandlingMode::Relaxed);
|
||||
params.setLogElapsedTime(!qEnvironmentVariableIsEmpty(Constants::QBS_PROFILING_ENV));
|
||||
request.insert("build-root", dir);
|
||||
request.insert("project-file-path", m_projectFilePath);
|
||||
request.insert("override-build-graph-data", true);
|
||||
static const auto envToJson = [](const Environment &env) {
|
||||
QJsonObject envObj;
|
||||
for (auto it = env.constBegin(); it != env.constEnd(); ++it) {
|
||||
if (env.isEnabled(it))
|
||||
envObj.insert(env.key(it), env.value(it));
|
||||
}
|
||||
return envObj;
|
||||
};
|
||||
request.insert("environment", envToJson(env));
|
||||
request.insert("error-handling-mode", "relaxed");
|
||||
request.insert("data-mode", "only-if-changed");
|
||||
QbsSession::insertRequestedModuleProperties(request);
|
||||
|
||||
m_qbsSetupProjectJob = m_project.setupProject(params, QbsManager::logSink(), nullptr);
|
||||
|
||||
connect(m_qbsSetupProjectJob, &qbs::AbstractJob::finished,
|
||||
this, &QbsProjectParser::handleQbsParsingDone);
|
||||
connect(m_qbsSetupProjectJob, &qbs::AbstractJob::taskStarted,
|
||||
this, &QbsProjectParser::handleQbsParsingTaskSetup);
|
||||
connect(m_qbsSetupProjectJob, &qbs::AbstractJob::taskProgress,
|
||||
this, &QbsProjectParser::handleQbsParsingProgress);
|
||||
connect(m_session, &QbsSession::projectResolved, this, [this](const ErrorInfo &error) {
|
||||
m_error = error;
|
||||
m_projectData = m_session->projectData();
|
||||
emit done(!m_error.hasError());
|
||||
});
|
||||
connect(m_session, &QbsSession::errorOccurred, this, [this] { emit done(false); });
|
||||
connect(m_session, &QbsSession::taskStarted, this,
|
||||
[this](const QString &description, int maxProgress) {
|
||||
Q_UNUSED(description)
|
||||
if (m_fi)
|
||||
m_fi->setProgressRange(0, maxProgress);
|
||||
});
|
||||
connect(m_session, &QbsSession::maxProgressChanged, this, [this](int maxProgress) {
|
||||
if (m_fi)
|
||||
m_fi->setProgressRange(0, maxProgress);
|
||||
});
|
||||
connect(m_session, &QbsSession::taskProgress, this, [this](int progress) {
|
||||
if (m_fi)
|
||||
m_fi->setProgressValue(progress);
|
||||
});
|
||||
m_session->sendRequest(request);
|
||||
}
|
||||
|
||||
void QbsProjectParser::cancel()
|
||||
{
|
||||
QTC_ASSERT(m_qbsSetupProjectJob, return);
|
||||
if (m_ruleExecutionJob)
|
||||
m_ruleExecutionJob->cancel();
|
||||
else
|
||||
m_qbsSetupProjectJob->cancel();
|
||||
}
|
||||
|
||||
qbs::Project QbsProjectParser::qbsProject() const
|
||||
{
|
||||
return m_project;
|
||||
}
|
||||
|
||||
qbs::ErrorInfo QbsProjectParser::error()
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
void QbsProjectParser::handleQbsParsingDone(bool success)
|
||||
{
|
||||
QTC_ASSERT(m_qbsSetupProjectJob, return);
|
||||
|
||||
m_project = m_qbsSetupProjectJob->project();
|
||||
m_error = m_qbsSetupProjectJob->error();
|
||||
|
||||
// Do not report the operation as canceled here, as we might want to make overlapping
|
||||
// parses appear atomic to the user.
|
||||
|
||||
emit done(success);
|
||||
}
|
||||
|
||||
void QbsProjectParser::startRuleExecution()
|
||||
{
|
||||
qbs::BuildOptions options;
|
||||
options.setDryRun(m_dryRun);
|
||||
options.setExecuteRulesOnly(true);
|
||||
m_ruleExecutionJob = m_project.buildAllProducts(
|
||||
options, qbs::Project::ProductSelectionWithNonDefault, nullptr);
|
||||
connect(m_ruleExecutionJob, &qbs::AbstractJob::finished,
|
||||
this, &QbsProjectParser::handleRuleExecutionDone);
|
||||
connect(m_ruleExecutionJob, &qbs::AbstractJob::taskStarted,
|
||||
this, &QbsProjectParser::handleQbsParsingTaskSetup);
|
||||
connect(m_ruleExecutionJob, &qbs::AbstractJob::taskProgress,
|
||||
this, &QbsProjectParser::handleQbsParsingProgress);
|
||||
}
|
||||
|
||||
void QbsProjectParser::handleRuleExecutionDone()
|
||||
{
|
||||
QTC_ASSERT(m_ruleExecutionJob, return);
|
||||
|
||||
// Execution of some very dynamic rules might fail due
|
||||
// to artifacts not being present. No genuine errors will get lost, as they will re-appear
|
||||
// on the next build attempt.
|
||||
emit ruleExecutionDone();
|
||||
}
|
||||
|
||||
void QbsProjectParser::handleQbsParsingProgress(int progress)
|
||||
{
|
||||
if (m_fi)
|
||||
m_fi->setProgressValue(m_currentProgressBase + progress);
|
||||
}
|
||||
|
||||
void QbsProjectParser::handleQbsParsingTaskSetup(const QString &description, int maximumProgressValue)
|
||||
{
|
||||
Q_UNUSED(description)
|
||||
if (m_fi) {
|
||||
m_currentProgressBase = m_fi->progressValue();
|
||||
m_fi->setProgressRange(0, m_currentProgressBase + maximumProgressValue);
|
||||
}
|
||||
}
|
||||
|
||||
QString QbsProjectParser::resourcesBaseDirectory() const
|
||||
{
|
||||
const QString qbsInstallDir = QLatin1String(QBS_INSTALL_DIR);
|
||||
if (!qbsInstallDir.isEmpty())
|
||||
return qbsInstallDir;
|
||||
return Core::ICore::resourcePath() + QLatin1String("/qbs");
|
||||
}
|
||||
|
||||
QString QbsProjectParser::libExecDirectory() const
|
||||
{
|
||||
const QString qbsInstallDir = QLatin1String(QBS_INSTALL_DIR);
|
||||
if (!qbsInstallDir.isEmpty())
|
||||
return qbsInstallDir + QLatin1String("/libexec");
|
||||
return Core::ICore::libexecPath();
|
||||
}
|
||||
|
||||
QString QbsProjectParser::pluginsBaseDirectory() const
|
||||
{
|
||||
const QString qbsInstallDir = QLatin1String(QBS_INSTALL_DIR);
|
||||
|
||||
// Note: We assume here that Qt Creator and qbs use the same name for their library dir.
|
||||
const QString qbsLibDirName = QLatin1String(IDE_LIBRARY_BASENAME);
|
||||
|
||||
if (!qbsInstallDir.isEmpty())
|
||||
return qbsInstallDir + QLatin1Char('/') + qbsLibDirName;
|
||||
if (HostOsInfo::isMacHost())
|
||||
return QDir::cleanPath(QCoreApplication::applicationDirPath()
|
||||
+ QLatin1String("/../PlugIns"));
|
||||
else
|
||||
return QDir::cleanPath(QCoreApplication::applicationDirPath()
|
||||
+ QLatin1String("/../" IDE_LIBRARY_BASENAME "/qtcreator/plugins"));
|
||||
if (m_session)
|
||||
m_session->cancelCurrentJob();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -25,13 +25,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "qbssession.h"
|
||||
|
||||
#include <utils/environment.h>
|
||||
|
||||
#include <QFutureInterface>
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
|
||||
#include <qbs.h>
|
||||
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
@@ -47,38 +48,24 @@ public:
|
||||
|
||||
void parse(const QVariantMap &config, const Utils::Environment &env, const QString &dir,
|
||||
const QString &configName);
|
||||
void startRuleExecution();
|
||||
void cancel();
|
||||
Utils::Environment environment() const { return m_environment; }
|
||||
|
||||
qbs::Project qbsProject() const;
|
||||
qbs::ErrorInfo error();
|
||||
QbsSession *session() const { return m_session; }
|
||||
QJsonObject projectData() const { return m_projectData; }
|
||||
ErrorInfo error() const { return m_error; }
|
||||
|
||||
signals:
|
||||
void done(bool success);
|
||||
void ruleExecutionDone();
|
||||
|
||||
private:
|
||||
void handleQbsParsingDone(bool success);
|
||||
void handleQbsParsingProgress(int progress);
|
||||
void handleQbsParsingTaskSetup(const QString &description, int maximumProgressValue);
|
||||
|
||||
QString pluginsBaseDirectory() const;
|
||||
QString resourcesBaseDirectory() const;
|
||||
QString libExecDirectory() const;
|
||||
|
||||
void handleRuleExecutionDone();
|
||||
|
||||
Utils::Environment m_environment;
|
||||
QString m_projectFilePath;
|
||||
qbs::SetupProjectJob *m_qbsSetupProjectJob = nullptr;
|
||||
qbs::BuildJob *m_ruleExecutionJob = nullptr;
|
||||
qbs::ErrorInfo m_error;
|
||||
qbs::Project m_project;
|
||||
bool m_dryRun;
|
||||
|
||||
const QString m_projectFilePath;
|
||||
QbsSession * const m_session;
|
||||
ErrorInfo m_error;
|
||||
QJsonObject m_projectData;
|
||||
bool m_parsing = false;
|
||||
QFutureInterface<bool> *m_fi = nullptr;
|
||||
int m_currentProgressBase = 0;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
687
src/plugins/qbsprojectmanager/qbssession.cpp
Normal file
687
src/plugins/qbsprojectmanager/qbssession.cpp
Normal file
@@ -0,0 +1,687 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 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 "qbssession.h"
|
||||
|
||||
#include "qbspmlogging.h"
|
||||
#include "qbsprojectmanagerconstants.h"
|
||||
#include "qbssettings.h"
|
||||
|
||||
#include <coreplugin/messagemanager.h>
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
#include <projectexplorer/taskhub.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QEventLoop>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Utils;
|
||||
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
QStringList arrayToStringList(const QJsonValue &array)
|
||||
{
|
||||
return transform<QStringList>(array.toArray(),
|
||||
[](const QJsonValue &v) { return v.toString(); });
|
||||
}
|
||||
|
||||
const QByteArray packetStart = "qbsmsg:";
|
||||
|
||||
class Packet
|
||||
{
|
||||
public:
|
||||
enum class Status { Incomplete, Complete, Invalid };
|
||||
Status parseInput(QByteArray &input)
|
||||
{
|
||||
if (m_expectedPayloadLength == -1) {
|
||||
const int packetStartOffset = input.indexOf(packetStart);
|
||||
if (packetStartOffset == -1)
|
||||
return Status::Incomplete;
|
||||
const int numberOffset = packetStartOffset + packetStart.length();
|
||||
const int newLineOffset = input.indexOf('\n', numberOffset);
|
||||
if (newLineOffset == -1)
|
||||
return Status::Incomplete;
|
||||
const QByteArray sizeString = input.mid(numberOffset, newLineOffset - numberOffset);
|
||||
bool isNumber;
|
||||
const int payloadLen = sizeString.toInt(&isNumber);
|
||||
if (!isNumber || payloadLen < 0)
|
||||
return Status::Invalid;
|
||||
m_expectedPayloadLength = payloadLen;
|
||||
input.remove(0, newLineOffset + 1);
|
||||
}
|
||||
const int bytesToAdd = m_expectedPayloadLength - m_payload.length();
|
||||
QTC_ASSERT(bytesToAdd >= 0, return Status::Invalid);
|
||||
m_payload += input.left(bytesToAdd);
|
||||
input.remove(0, bytesToAdd);
|
||||
return isComplete() ? Status::Complete : Status::Incomplete;
|
||||
}
|
||||
|
||||
QJsonObject retrievePacket()
|
||||
{
|
||||
QTC_ASSERT(isComplete(), return QJsonObject());
|
||||
const auto packet = QJsonDocument::fromJson(QByteArray::fromBase64(m_payload)).object();
|
||||
m_payload.clear();
|
||||
m_expectedPayloadLength = -1;
|
||||
return packet;
|
||||
}
|
||||
|
||||
static QByteArray createPacket(const QJsonObject &packet)
|
||||
{
|
||||
const QByteArray jsonData = QJsonDocument(packet).toJson().toBase64();
|
||||
return QByteArray(packetStart).append(QByteArray::number(jsonData.length())).append('\n')
|
||||
.append(jsonData);
|
||||
}
|
||||
|
||||
private:
|
||||
bool isComplete() const { return m_payload.length() == m_expectedPayloadLength; }
|
||||
|
||||
QByteArray m_payload;
|
||||
int m_expectedPayloadLength = -1;
|
||||
};
|
||||
|
||||
class PacketReader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PacketReader(QObject *parent) : QObject(parent) {}
|
||||
|
||||
void handleData(const QByteArray &data)
|
||||
{
|
||||
m_incomingData += data;
|
||||
handleData();
|
||||
}
|
||||
|
||||
signals:
|
||||
void packetReceived(const QJsonObject &packet);
|
||||
void errorOccurred(const QString &msg);
|
||||
|
||||
private:
|
||||
void handleData()
|
||||
{
|
||||
switch (m_currentPacket.parseInput(m_incomingData)) {
|
||||
case Packet::Status::Invalid:
|
||||
emit errorOccurred(tr("Received invalid input."));
|
||||
break;
|
||||
case Packet::Status::Complete:
|
||||
emit packetReceived(m_currentPacket.retrievePacket());
|
||||
handleData();
|
||||
break;
|
||||
case Packet::Status::Incomplete:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray m_incomingData;
|
||||
Packet m_currentPacket;
|
||||
};
|
||||
|
||||
class QbsSession::Private
|
||||
{
|
||||
public:
|
||||
QProcess *qbsProcess = nullptr;
|
||||
PacketReader *packetReader = nullptr;
|
||||
QJsonObject currentRequest;
|
||||
QJsonObject projectData;
|
||||
QEventLoop eventLoop;
|
||||
QJsonObject reply;
|
||||
QHash<QString, QStringList> generatedFilesForSources;
|
||||
optional<Error> lastError;
|
||||
State state = State::Inactive;
|
||||
};
|
||||
|
||||
QbsSession::QbsSession(QObject *parent) : QObject(parent), d(new Private)
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
|
||||
void QbsSession::initialize()
|
||||
{
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert("QT_FORCE_STDERR_LOGGING", "1");
|
||||
d->packetReader = new PacketReader(this);
|
||||
d->qbsProcess = new QProcess(this);
|
||||
d->qbsProcess->setProcessEnvironment(env);
|
||||
connect(d->qbsProcess, &QProcess::readyReadStandardOutput, this, [this] {
|
||||
d->packetReader->handleData(d->qbsProcess->readAllStandardOutput());
|
||||
});
|
||||
connect(d->qbsProcess, &QProcess::readyReadStandardError, this, [this] {
|
||||
qCDebug(qbsPmLog) << "[qbs stderr]: " << d->qbsProcess->readAllStandardError();
|
||||
});
|
||||
connect(d->qbsProcess, &QProcess::errorOccurred, this, [this](QProcess::ProcessError e) {
|
||||
d->eventLoop.exit(1);
|
||||
if (state() == State::ShuttingDown || state() == State::Inactive)
|
||||
return;
|
||||
switch (e) {
|
||||
case QProcess::FailedToStart:
|
||||
setError(Error::QbsFailedToStart);
|
||||
break;
|
||||
case QProcess::WriteError:
|
||||
case QProcess::ReadError:
|
||||
setError(Error::ProtocolError);
|
||||
break;
|
||||
case QProcess::Crashed:
|
||||
case QProcess::Timedout:
|
||||
case QProcess::UnknownError:
|
||||
break;
|
||||
}
|
||||
});
|
||||
connect(d->qbsProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this,
|
||||
[this] {
|
||||
d->qbsProcess->deleteLater();
|
||||
switch (state()) {
|
||||
case State::Inactive:
|
||||
break;
|
||||
case State::ShuttingDown:
|
||||
setInactive();
|
||||
break;
|
||||
case State::Active:
|
||||
setError(Error::QbsQuit);
|
||||
break;
|
||||
case State::Initializing:
|
||||
setError(Error::ProtocolError);
|
||||
break;
|
||||
}
|
||||
d->qbsProcess = nullptr;
|
||||
});
|
||||
connect(d->packetReader, &PacketReader::errorOccurred, this, [this](const QString &msg) {
|
||||
qCDebug(qbsPmLog) << "session error" << msg;
|
||||
setError(Error::ProtocolError);
|
||||
});
|
||||
connect(d->packetReader, &PacketReader::packetReceived, this, &QbsSession::handlePacket);
|
||||
d->state = State::Initializing;
|
||||
d->qbsProcess->start(QbsSettings::qbsExecutableFilePath().toString(), {"session"});
|
||||
}
|
||||
|
||||
void QbsSession::sendQuitPacket()
|
||||
{
|
||||
d->qbsProcess->write(Packet::createPacket(QJsonObject{qMakePair(QString("type"),
|
||||
QJsonValue("quit"))}));
|
||||
}
|
||||
|
||||
QbsSession::~QbsSession()
|
||||
{
|
||||
if (d->packetReader)
|
||||
d->packetReader->disconnect(this);
|
||||
if (d->qbsProcess) {
|
||||
d->qbsProcess->disconnect(this);
|
||||
quit();
|
||||
if (d->qbsProcess->state() == QProcess::Running && !d->qbsProcess->waitForFinished(10000))
|
||||
d->qbsProcess->terminate();
|
||||
if (d->qbsProcess->state() == QProcess::Running && !d->qbsProcess->waitForFinished(10000))
|
||||
d->qbsProcess->kill();
|
||||
d->qbsProcess->waitForFinished(1000);
|
||||
}
|
||||
delete d;
|
||||
}
|
||||
|
||||
QbsSession::State QbsSession::state() const
|
||||
{
|
||||
return d->state;
|
||||
}
|
||||
|
||||
optional<QbsSession::Error> QbsSession::lastError() const
|
||||
{
|
||||
return d->lastError;
|
||||
}
|
||||
|
||||
QString QbsSession::errorString(QbsSession::Error error)
|
||||
{
|
||||
switch (error) {
|
||||
case Error::QbsQuit:
|
||||
return tr("The qbs process quit unexpectedly");
|
||||
case Error::QbsFailedToStart:
|
||||
return tr("The qbs process failed to start.");
|
||||
case Error::ProtocolError:
|
||||
return tr("The qbs process sent invalid data.");
|
||||
case Error::VersionMismatch:
|
||||
return tr("The qbs API level is not compatible with what Qt Creator expects.");
|
||||
}
|
||||
return QString(); // For dumb compilers.
|
||||
}
|
||||
|
||||
QJsonObject QbsSession::projectData() const
|
||||
{
|
||||
return d->projectData;
|
||||
}
|
||||
|
||||
void QbsSession::sendRequest(const QJsonObject &request)
|
||||
{
|
||||
QTC_ASSERT(d->currentRequest.isEmpty(),
|
||||
qDebug() << request.value("type").toString()
|
||||
<< d->currentRequest.value("type").toString(); return);
|
||||
d->currentRequest = request;
|
||||
const QString logLevelFromEnv = qEnvironmentVariable("QBS_LOG_LEVEL");
|
||||
if (!logLevelFromEnv.isEmpty())
|
||||
d->currentRequest.insert("log-level", logLevelFromEnv);
|
||||
if (!qEnvironmentVariableIsEmpty(Constants::QBS_PROFILING_ENV))
|
||||
d->currentRequest.insert("log-time", true);
|
||||
if (d->state == State::Active)
|
||||
sendQueuedRequest();
|
||||
else if (d->state == State::Inactive)
|
||||
initialize();
|
||||
}
|
||||
|
||||
void QbsSession::cancelCurrentJob()
|
||||
{
|
||||
if (d->state == State::Active)
|
||||
sendRequest(QJsonObject{qMakePair(QString("type"), QJsonValue("cancel-job"))});
|
||||
}
|
||||
|
||||
void QbsSession::quit()
|
||||
{
|
||||
if (d->state == State::ShuttingDown || d->state == State::Inactive)
|
||||
return;
|
||||
d->state = State::ShuttingDown;
|
||||
sendQuitPacket();
|
||||
}
|
||||
|
||||
void QbsSession::requestFilesGeneratedFrom(const QHash<QString, QStringList> &sourceFilesPerProduct)
|
||||
{
|
||||
QJsonObject request;
|
||||
request.insert("type", "get-generated-files-for-sources");
|
||||
QJsonArray products;
|
||||
for (auto it = sourceFilesPerProduct.cbegin(); it != sourceFilesPerProduct.cend(); ++it) {
|
||||
QJsonObject product;
|
||||
product.insert("full-display-name", it->first());
|
||||
QJsonArray requests;
|
||||
for (const QString &sourceFile : it.value())
|
||||
requests << QJsonObject({qMakePair(QString("source-file"), sourceFile)});
|
||||
product.insert("requests", requests);
|
||||
products << product;
|
||||
}
|
||||
request.insert("products", products);
|
||||
sendRequest(request);
|
||||
}
|
||||
|
||||
QStringList QbsSession::filesGeneratedFrom(const QString &sourceFile) const
|
||||
{
|
||||
return d->generatedFilesForSources.value(sourceFile);
|
||||
}
|
||||
|
||||
FileChangeResult QbsSession::addFiles(const QStringList &files, const QString &product,
|
||||
const QString &group)
|
||||
{
|
||||
return updateFileList("add-files", files, product, group);
|
||||
}
|
||||
|
||||
FileChangeResult QbsSession::removeFiles(const QStringList &files, const QString &product,
|
||||
const QString &group)
|
||||
{
|
||||
return updateFileList("remove-files", files, product, group);
|
||||
}
|
||||
|
||||
RunEnvironmentResult QbsSession::getRunEnvironment(
|
||||
const QString &product,
|
||||
const QProcessEnvironment &baseEnv,
|
||||
const QStringList &config)
|
||||
{
|
||||
d->reply = QJsonObject();
|
||||
QJsonObject request;
|
||||
request.insert("type", "get-run-environment");
|
||||
request.insert("product", product);
|
||||
QJsonObject inEnv;
|
||||
const QStringList baseEnvKeys = baseEnv.keys();
|
||||
for (const QString &key : baseEnvKeys)
|
||||
inEnv.insert(key, baseEnv.value(key));
|
||||
request.insert("base-environment", inEnv);
|
||||
request.insert("config", QJsonArray::fromStringList(config));
|
||||
sendRequest(request);
|
||||
QTimer::singleShot(10000, this, [this] { d->eventLoop.exit(1); });
|
||||
if (d->eventLoop.exec(QEventLoop::ExcludeUserInputEvents) == 1)
|
||||
return RunEnvironmentResult(ErrorInfo(tr("Request timed out.")));
|
||||
QProcessEnvironment env;
|
||||
const QJsonObject outEnv = d->reply.value("full-environment").toObject();
|
||||
for (auto it = outEnv.begin(); it != outEnv.end(); ++it)
|
||||
env.insert(it.key(), it.value().toString());
|
||||
return RunEnvironmentResult(env, getErrorInfo(d->reply));
|
||||
}
|
||||
|
||||
void QbsSession::insertRequestedModuleProperties(QJsonObject &request)
|
||||
{
|
||||
request.insert("module-properties", QJsonArray::fromStringList({
|
||||
"cpp.commonCompilerFlags",
|
||||
"cpp.compilerVersionMajor",
|
||||
"cpp.compilerVersionMinor",
|
||||
"cpp.cLanguageVersion",
|
||||
"cpp.cxxLanguageVersion",
|
||||
"cpp.cxxStandardLibrary",
|
||||
"cpp.defines",
|
||||
"cpp.driverFlags",
|
||||
"cpp.enableExceptions",
|
||||
"cpp.enableRtti",
|
||||
"cpp.exceptionHandlingModel",
|
||||
"cpp.frameworkPaths",
|
||||
"cpp.includePaths",
|
||||
"cpp.machineType",
|
||||
"cpp.minimumDarwinVersion",
|
||||
"cpp.minimumDarwinVersionCompilerFlag",
|
||||
"cpp.platformCommonCompilerFlags",
|
||||
"cpp.platformDriverFlags",
|
||||
"cpp.positionIndependentCode",
|
||||
"cpp.systemFrameworkPaths",
|
||||
"cpp.systemIncludePaths",
|
||||
"cpp.target",
|
||||
"cpp.targetArch",
|
||||
"cpp.useCPrecompiledHeader",
|
||||
"cpp.useCxxPrecompiledHeader",
|
||||
"cpp.useObjcPrecompiledHeader",
|
||||
"cpp.useObjcxxPrecompiledHeader",
|
||||
"qbs.targetOS",
|
||||
"qbs.toolchain",
|
||||
"Qt.core.enableKeywords",
|
||||
"Qt.core.version",
|
||||
}));
|
||||
}
|
||||
|
||||
// TODO: We can do better: Give out a (managed) session pointer here. Then we can re-use it
|
||||
// if the user chooses the imported project, saving the second build graph load.
|
||||
QbsSession::BuildGraphInfo QbsSession::getBuildGraphInfo(const FilePath &bgFilePath,
|
||||
const QStringList &requestedProperties)
|
||||
{
|
||||
const QFileInfo bgFi = bgFilePath.toFileInfo();
|
||||
QDir buildRoot = bgFi.dir();
|
||||
buildRoot.cdUp();
|
||||
QJsonObject request;
|
||||
request.insert("type", "resolve-project");
|
||||
request.insert("restore-behavior", "restore-only");
|
||||
request.insert("configuration-name", bgFi.completeBaseName());
|
||||
if (QbsSettings::useCreatorSettingsDirForQbs())
|
||||
request.insert("settings-directory", QbsSettings::qbsSettingsBaseDir());
|
||||
request.insert("build-root", buildRoot.path());
|
||||
request.insert("error-handling-mode", "relaxed");
|
||||
request.insert("data-mode", "only-if-changed");
|
||||
request.insert("module-properties", QJsonArray::fromStringList(requestedProperties));
|
||||
QbsSession session(nullptr);
|
||||
session.sendRequest(request);
|
||||
QJsonObject reply;
|
||||
BuildGraphInfo bgInfo;
|
||||
bgInfo.bgFilePath = bgFilePath;
|
||||
QTimer::singleShot(10000, &session, [&session] { session.d->eventLoop.exit(1); });
|
||||
connect(&session, &QbsSession::errorOccurred, [&] {
|
||||
bgInfo.error = ErrorInfo(tr("Failed to load qbs build graph."));
|
||||
});
|
||||
connect(&session, &QbsSession::projectResolved, [&](const ErrorInfo &error) {
|
||||
bgInfo.error = error;
|
||||
session.d->eventLoop.quit();
|
||||
});
|
||||
if (session.d->eventLoop.exec(QEventLoop::ExcludeUserInputEvents) == 1) {
|
||||
bgInfo.error = ErrorInfo(tr("Request timed out."));
|
||||
return bgInfo;
|
||||
}
|
||||
if (bgInfo.error.hasError())
|
||||
return bgInfo;
|
||||
bgInfo.profileData = session.projectData().value("profile-data").toObject().toVariantMap();
|
||||
bgInfo.overriddenProperties = session.projectData().value("overridden-properties").toObject()
|
||||
.toVariantMap();
|
||||
QStringList props = requestedProperties;
|
||||
forAllProducts(session.projectData(), [&](const QJsonObject &product) {
|
||||
if (props.empty())
|
||||
return;
|
||||
for (auto it = props.begin(); it != props.end();) {
|
||||
const QVariant value = product.value("module-properties").toObject().value(*it);
|
||||
if (value.isValid()) {
|
||||
bgInfo.requestedProperties.insert(*it, value);
|
||||
it = props.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
});
|
||||
return bgInfo;
|
||||
}
|
||||
|
||||
void QbsSession::handlePacket(const QJsonObject &packet)
|
||||
{
|
||||
const QString type = packet.value("type").toString();
|
||||
if (type == "hello") {
|
||||
QTC_CHECK(d->state == State::Initializing);
|
||||
if (packet.value("api-compat-level").toInt() != 1) {
|
||||
setError(Error::VersionMismatch);
|
||||
return;
|
||||
}
|
||||
d->state = State::Active;
|
||||
sendQueuedRequest();
|
||||
} else if (type == "project-resolved") {
|
||||
setProjectDataFromReply(packet, true);
|
||||
emit projectResolved(getErrorInfo(packet));
|
||||
} else if (type == "project-built") {
|
||||
setProjectDataFromReply(packet, false);
|
||||
emit projectBuilt(getErrorInfo(packet));
|
||||
} else if (type == "project-cleaned") {
|
||||
emit projectCleaned(getErrorInfo(packet));
|
||||
} else if (type == "install-done") {
|
||||
emit projectInstalled(getErrorInfo(packet));
|
||||
} else if (type == "log-data") {
|
||||
Core::MessageManager::write("[qbs] " + packet.value("message").toString(),
|
||||
Core::MessageManager::Silent);
|
||||
} else if (type == "warning") {
|
||||
const ErrorInfo errorInfo = ErrorInfo(packet.value("warning").toObject());
|
||||
|
||||
// TODO: This loop occurs a lot. Factor it out.
|
||||
for (const ErrorInfoItem &item : errorInfo.items) {
|
||||
TaskHub::addTask(Task::Warning, item.description,
|
||||
ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM,
|
||||
item.filePath, item.line);
|
||||
}
|
||||
} else if (type == "task-started") {
|
||||
emit taskStarted(packet.value("description").toString(),
|
||||
packet.value("max-progress").toInt());
|
||||
} else if (type == "task-progress") {
|
||||
emit taskProgress(packet.value("progress").toInt());
|
||||
} else if (type == "new-max-progress") {
|
||||
emit maxProgressChanged(packet.value("max-progress").toInt());
|
||||
} else if (type == "generated-files-for-source") {
|
||||
QHash<QString, QStringList> generatedFiles;
|
||||
for (const QJsonValue &product : packet.value("products").toArray()) {
|
||||
for (const QJsonValue &r : product.toObject().value("results").toArray()) {
|
||||
const QJsonObject result = r.toObject();
|
||||
generatedFiles[result.value("source-file").toString()]
|
||||
<< arrayToStringList(result.value("generated-files").toArray());
|
||||
}
|
||||
}
|
||||
if (generatedFiles != d->generatedFilesForSources) {
|
||||
d->generatedFilesForSources = generatedFiles;
|
||||
emit newGeneratedFilesForSources(generatedFiles);
|
||||
}
|
||||
} else if (type == "command-description") {
|
||||
emit commandDescription(packet.value("message").toString());
|
||||
} else if (type == "files-added" || type == "files-removed") {
|
||||
handleFileListUpdated(packet);
|
||||
} else if (type == "process-result") {
|
||||
emit processResult(
|
||||
FilePath::fromString(packet.value("executable-file-path").toString()),
|
||||
arrayToStringList(packet.value("arguments")),
|
||||
FilePath::fromString(packet.value("working-directory").toString()),
|
||||
arrayToStringList(packet.value("stdout")),
|
||||
arrayToStringList(packet.value("stderr")),
|
||||
packet.value("success").toBool());
|
||||
} else if (type == "run-environment") {
|
||||
d->reply = packet;
|
||||
d->eventLoop.quit();
|
||||
}
|
||||
}
|
||||
|
||||
void QbsSession::sendQueuedRequest()
|
||||
{
|
||||
sendRequestNow(d->currentRequest);
|
||||
d->currentRequest = QJsonObject();
|
||||
}
|
||||
|
||||
void QbsSession::sendRequestNow(const QJsonObject &request)
|
||||
{
|
||||
QTC_ASSERT(d->state == State::Active, return);
|
||||
if (!request.isEmpty())
|
||||
d->qbsProcess->write(Packet::createPacket(request));
|
||||
}
|
||||
|
||||
ErrorInfo QbsSession::getErrorInfo(const QJsonObject &packet)
|
||||
{
|
||||
return ErrorInfo(packet.value("error").toObject());
|
||||
}
|
||||
|
||||
void QbsSession::setProjectDataFromReply(const QJsonObject &packet, bool withBuildSystemFiles)
|
||||
{
|
||||
const QJsonObject projectData = packet.value("project-data").toObject();
|
||||
if (!projectData.isEmpty()) {
|
||||
const QJsonValue buildSystemFiles = d->projectData.value("build-system-files");
|
||||
d->projectData = projectData;
|
||||
if (!withBuildSystemFiles)
|
||||
d->projectData.insert("build-system-files", buildSystemFiles);
|
||||
}
|
||||
}
|
||||
|
||||
void QbsSession::setError(QbsSession::Error error)
|
||||
{
|
||||
d->lastError = error;
|
||||
setInactive();
|
||||
emit errorOccurred(error);
|
||||
}
|
||||
|
||||
void QbsSession::setInactive()
|
||||
{
|
||||
if (d->state == State::Inactive)
|
||||
return;
|
||||
d->state = State::Inactive;
|
||||
d->qbsProcess->disconnect(this);
|
||||
d->currentRequest = QJsonObject();
|
||||
d->packetReader->disconnect(this);
|
||||
d->packetReader->deleteLater();
|
||||
d->packetReader = nullptr;
|
||||
if (d->qbsProcess->state() == QProcess::Running)
|
||||
sendQuitPacket();
|
||||
d->qbsProcess = nullptr;
|
||||
}
|
||||
|
||||
FileChangeResult QbsSession::updateFileList(const char *action, const QStringList &files,
|
||||
const QString &product, const QString &group)
|
||||
{
|
||||
if (d->state != State::Active)
|
||||
return FileChangeResult(files, tr("The qbs session is not in a valid state."));
|
||||
sendRequestNow(QJsonObject{
|
||||
{"type", QLatin1String(action)},
|
||||
{"files", QJsonArray::fromStringList(files)},
|
||||
{"product", product},
|
||||
{"group", group}
|
||||
});
|
||||
return FileChangeResult(QStringList());
|
||||
}
|
||||
|
||||
void QbsSession::handleFileListUpdated(const QJsonObject &reply)
|
||||
{
|
||||
setProjectDataFromReply(reply, false);
|
||||
const QStringList failedFiles = arrayToStringList(reply.value("failed-files"));
|
||||
if (!failedFiles.isEmpty()) {
|
||||
Core::MessageManager::write(tr("Failed to update files in Qbs project: %1.\n"
|
||||
"The affected files are: \n\t%2")
|
||||
.arg(getErrorInfo(reply).toString(),
|
||||
failedFiles.join("\n\t")),
|
||||
Core::MessageManager::ModeSwitch);
|
||||
}
|
||||
emit fileListUpdated();
|
||||
}
|
||||
|
||||
ErrorInfoItem::ErrorInfoItem(const QJsonObject &data)
|
||||
{
|
||||
description = data.value("description").toString();
|
||||
const QJsonObject locationData = data.value("location").toObject();
|
||||
filePath = FilePath::fromString(locationData.value("file-path").toString());
|
||||
line = locationData.value("line").toInt(-1);
|
||||
}
|
||||
|
||||
QString ErrorInfoItem::toString() const
|
||||
{
|
||||
QString s = filePath.toUserOutput();
|
||||
if (!s.isEmpty() && line != -1)
|
||||
s.append(':').append(QString::number(line));
|
||||
if (!s.isEmpty())
|
||||
s.append(':');
|
||||
return s.append(description);
|
||||
}
|
||||
|
||||
ErrorInfo::ErrorInfo(const QJsonObject &data)
|
||||
{
|
||||
for (const QJsonValue &v : data.value("items").toArray())
|
||||
items << ErrorInfoItem(v.toObject());
|
||||
}
|
||||
|
||||
ErrorInfo::ErrorInfo(const QString &msg)
|
||||
{
|
||||
items << ErrorInfoItem(msg);
|
||||
}
|
||||
|
||||
QString ErrorInfo::toString() const
|
||||
{
|
||||
return transform<QStringList>(items, [](const ErrorInfoItem &i) { return i.toString(); })
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
void forAllProducts(const QJsonObject &project, const WorkerFunction &productFunction)
|
||||
{
|
||||
for (const QJsonValue &p : project.value("products").toArray())
|
||||
productFunction(p.toObject());
|
||||
for (const QJsonValue &p : project.value("sub-projects").toArray())
|
||||
forAllProducts(p.toObject(), productFunction);
|
||||
}
|
||||
|
||||
void forAllArtifacts(const QJsonObject &product, ArtifactType type,
|
||||
const WorkerFunction &artifactFunction)
|
||||
{
|
||||
if (type == ArtifactType::Source || type == ArtifactType::All) {
|
||||
for (const QJsonValue &g : product.value("groups").toArray())
|
||||
forAllArtifacts(g.toObject(), artifactFunction);
|
||||
}
|
||||
if (type == ArtifactType::Generated || type == ArtifactType::All) {
|
||||
for (const QJsonValue &v : product.value("generated-artifacts").toArray())
|
||||
artifactFunction(v.toObject());
|
||||
}
|
||||
}
|
||||
|
||||
void forAllArtifacts(const QJsonObject &group, const WorkerFunction &artifactFunction)
|
||||
{
|
||||
for (const QJsonValue &v : group.value("source-artifacts").toArray())
|
||||
artifactFunction(v.toObject());
|
||||
for (const QJsonValue &v : group.value("source-artifacts-from-wildcards").toArray())
|
||||
artifactFunction(v.toObject());
|
||||
}
|
||||
|
||||
Location locationFromObject(const QJsonObject &o)
|
||||
{
|
||||
const QJsonObject loc = o.value("location").toObject();
|
||||
return Location(FilePath::fromString(loc.value("file-path").toString()),
|
||||
loc.value("line").toInt());
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QbsProjectManager
|
||||
|
||||
#include <qbssession.moc>
|
198
src/plugins/qbsprojectmanager/qbssession.h
Normal file
198
src/plugins/qbsprojectmanager/qbssession.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/optional.h>
|
||||
|
||||
#include <QHash>
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace ProjectExplorer { class Target; }
|
||||
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
class ErrorInfoItem
|
||||
{
|
||||
public:
|
||||
ErrorInfoItem(const QJsonObject &data);
|
||||
ErrorInfoItem(const QString &msg) : description(msg) {}
|
||||
|
||||
QString toString() const;
|
||||
|
||||
QString description;
|
||||
Utils::FilePath filePath;
|
||||
int line = -1;
|
||||
};
|
||||
|
||||
class ErrorInfo
|
||||
{
|
||||
public:
|
||||
ErrorInfo() = default;
|
||||
ErrorInfo(const QJsonObject &data);
|
||||
ErrorInfo(const QString &msg);
|
||||
|
||||
QString toString() const;
|
||||
bool hasError() const { return !items.isEmpty(); }
|
||||
|
||||
QList<ErrorInfoItem> items;
|
||||
};
|
||||
|
||||
template<typename Data> class SynchronousRequestResult
|
||||
{
|
||||
public:
|
||||
ErrorInfo error() const { return m_error; }
|
||||
SynchronousRequestResult(const Data &d, const ErrorInfo &e = {}) : m_error(e), m_data(d) {}
|
||||
SynchronousRequestResult(const ErrorInfo &e) : SynchronousRequestResult(Data(), e) {}
|
||||
|
||||
protected:
|
||||
const ErrorInfo m_error;
|
||||
const Data m_data;
|
||||
};
|
||||
|
||||
class FileChangeResult : public SynchronousRequestResult<QStringList>
|
||||
{
|
||||
public:
|
||||
using SynchronousRequestResult::SynchronousRequestResult;
|
||||
QStringList failedFiles () const { return m_data; }
|
||||
};
|
||||
|
||||
class RunEnvironmentResult : public SynchronousRequestResult<QProcessEnvironment>
|
||||
{
|
||||
public:
|
||||
using SynchronousRequestResult::SynchronousRequestResult;
|
||||
QProcessEnvironment environment() { return m_data; }
|
||||
};
|
||||
|
||||
// TODO: Put the helper function somewhere else. E.g. in qbsnodes.cpp we don't want a session include.
|
||||
QStringList arrayToStringList(const QJsonValue &array);
|
||||
|
||||
using WorkerFunction = std::function<void(const QJsonObject &)>;
|
||||
void forAllProducts(const QJsonObject &projectData, const WorkerFunction &productFunction);
|
||||
|
||||
enum class ArtifactType { Source, Generated, All };
|
||||
void forAllArtifacts(const QJsonObject &product, ArtifactType type,
|
||||
const WorkerFunction &artifactFunction);
|
||||
void forAllArtifacts(const QJsonObject &group, const WorkerFunction &artifactFunction);
|
||||
|
||||
class Location
|
||||
{
|
||||
public:
|
||||
Location(const Utils::FilePath &p, int l) : filePath(p), line (l) {}
|
||||
const Utils::FilePath filePath;
|
||||
const int line;
|
||||
};
|
||||
Location locationFromObject(const QJsonObject &o); // Project, Product or Group
|
||||
|
||||
class QbsSession : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QbsSession(QObject *parent = nullptr);
|
||||
~QbsSession() override;
|
||||
|
||||
enum class State { Initializing, Active, ShuttingDown, Inactive };
|
||||
enum class Error { QbsFailedToStart, QbsQuit, ProtocolError, VersionMismatch };
|
||||
|
||||
State state() const;
|
||||
Utils::optional<Error> lastError() const;
|
||||
static QString errorString(Error error);
|
||||
QJsonObject projectData() const;
|
||||
|
||||
void sendRequest(const QJsonObject &request);
|
||||
void cancelCurrentJob();
|
||||
void quit();
|
||||
void requestFilesGeneratedFrom(const QHash<QString, QStringList> &sourceFilesPerProduct);
|
||||
QStringList filesGeneratedFrom(const QString &sourceFile) const;
|
||||
FileChangeResult addFiles(const QStringList &files, const QString &product,
|
||||
const QString &group);
|
||||
FileChangeResult removeFiles(const QStringList &files, const QString &product,
|
||||
const QString &group);
|
||||
RunEnvironmentResult getRunEnvironment(const QString &product,
|
||||
const QProcessEnvironment &baseEnv,
|
||||
const QStringList &config);
|
||||
|
||||
static void insertRequestedModuleProperties(QJsonObject &request);
|
||||
|
||||
class BuildGraphInfo
|
||||
{
|
||||
public:
|
||||
Utils::FilePath bgFilePath;
|
||||
QVariantMap overriddenProperties;
|
||||
QVariantMap profileData;
|
||||
QVariantMap requestedProperties;
|
||||
ErrorInfo error;
|
||||
};
|
||||
static BuildGraphInfo getBuildGraphInfo(const Utils::FilePath &bgFilePath,
|
||||
const QStringList &requestedProperties);
|
||||
|
||||
signals:
|
||||
void errorOccurred(Error lastError);
|
||||
void projectResolved(const ErrorInfo &error);
|
||||
void projectBuilt(const ErrorInfo &error);
|
||||
void projectCleaned(const ErrorInfo &error);
|
||||
void projectInstalled(const ErrorInfo &error);
|
||||
void newGeneratedFilesForSources(const QHash<QString, QStringList> &generatedFiles);
|
||||
void taskStarted(const QString &description, int maxProgress);
|
||||
void maxProgressChanged(int maxProgress);
|
||||
void taskProgress(int progress);
|
||||
void commandDescription(const QString &description);
|
||||
void processResult(
|
||||
const Utils::FilePath &executable,
|
||||
const QStringList &arguments,
|
||||
const Utils::FilePath &workingDir,
|
||||
const QStringList &stdOut,
|
||||
const QStringList &stdErr,
|
||||
bool success);
|
||||
void fileListUpdated();
|
||||
|
||||
private:
|
||||
void initialize();
|
||||
void sendQuitPacket();
|
||||
void handlePacket(const QJsonObject &packet);
|
||||
void sendQueuedRequest();
|
||||
void sendRequestNow(const QJsonObject &request);
|
||||
ErrorInfo getErrorInfo(const QJsonObject &packet);
|
||||
void setProjectDataFromReply(const QJsonObject &packet, bool withBuildSystemFiles);
|
||||
void setError(Error error);
|
||||
void setInactive();
|
||||
FileChangeResult updateFileList(const char *action, const QStringList &files,
|
||||
const QString &product, const QString &group);
|
||||
void handleFileListUpdated(const QJsonObject &reply);
|
||||
|
||||
class Private;
|
||||
Private * const d;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QbsProjectManager
|
192
src/plugins/qbsprojectmanager/qbssettings.cpp
Normal file
192
src/plugins/qbsprojectmanager/qbssettings.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 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 "qbssettings.h"
|
||||
|
||||
#include "qbsprojectmanagerconstants.h"
|
||||
|
||||
#include <app/app_version.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/pathchooser.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QFormLayout>
|
||||
#include <QLabel>
|
||||
#include <QProcess>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
const char QBS_EXE_KEY[] = "QbsProjectManager/QbsExecutable";
|
||||
const char USE_CREATOR_SETTINGS_KEY[] = "QbsProjectManager/useCreatorDir";
|
||||
|
||||
static bool operator==(const QbsSettingsData &s1, const QbsSettingsData &s2)
|
||||
{
|
||||
return s1.qbsExecutableFilePath == s2.qbsExecutableFilePath
|
||||
&& s1.useCreatorSettings == s2.useCreatorSettings;
|
||||
}
|
||||
static bool operator!=(const QbsSettingsData &s1, const QbsSettingsData &s2)
|
||||
{
|
||||
return !(s1 == s2);
|
||||
}
|
||||
|
||||
FilePath QbsSettings::qbsExecutableFilePath()
|
||||
{
|
||||
const QString fileName = HostOsInfo::withExecutableSuffix("qbs");
|
||||
FilePath candidate = instance().m_settings.qbsExecutableFilePath;
|
||||
if (!candidate.exists()) {
|
||||
candidate = FilePath::fromString(QCoreApplication::applicationDirPath())
|
||||
.pathAppended(fileName);
|
||||
}
|
||||
if (!candidate.exists())
|
||||
candidate = Environment::systemEnvironment().searchInPath(fileName);
|
||||
return candidate;
|
||||
}
|
||||
|
||||
bool QbsSettings::useCreatorSettingsDirForQbs()
|
||||
{
|
||||
return instance().m_settings.useCreatorSettings;
|
||||
}
|
||||
|
||||
QString QbsSettings::qbsSettingsBaseDir()
|
||||
{
|
||||
return useCreatorSettingsDirForQbs() ? Core::ICore::userResourcePath() : QString();
|
||||
}
|
||||
|
||||
QbsSettings &QbsSettings::instance()
|
||||
{
|
||||
static QbsSettings theSettings;
|
||||
return theSettings;
|
||||
}
|
||||
|
||||
void QbsSettings::setSettingsData(const QbsSettingsData &settings)
|
||||
{
|
||||
if (instance().m_settings != settings) {
|
||||
instance().m_settings = settings;
|
||||
instance().storeSettings();
|
||||
emit instance().settingsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QbsSettings::QbsSettings()
|
||||
{
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
void QbsSettings::loadSettings()
|
||||
{
|
||||
QSettings * const s = Core::ICore::settings();
|
||||
m_settings.qbsExecutableFilePath = FilePath::fromString(s->value(QBS_EXE_KEY).toString());
|
||||
m_settings.useCreatorSettings = s->value(USE_CREATOR_SETTINGS_KEY, true).toBool();
|
||||
}
|
||||
|
||||
void QbsSettings::storeSettings() const
|
||||
{
|
||||
QSettings * const s = Core::ICore::settings();
|
||||
s->setValue(QBS_EXE_KEY, m_settings.qbsExecutableFilePath.toString());
|
||||
s->setValue(USE_CREATOR_SETTINGS_KEY, m_settings.useCreatorSettings);
|
||||
}
|
||||
|
||||
class QbsSettingsPage::SettingsWidget : public QWidget
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(QbsProjectManager::Internal::QbsSettingsPage)
|
||||
public:
|
||||
SettingsWidget()
|
||||
{
|
||||
m_qbsExePathChooser.setExpectedKind(PathChooser::ExistingCommand);
|
||||
m_qbsExePathChooser.setFileName(QbsSettings::qbsExecutableFilePath());
|
||||
m_versionLabel.setText(getQbsVersion());
|
||||
m_settingsDirCheckBox.setText(tr("Use %1 settings directory for Qbs")
|
||||
.arg(Core::Constants::IDE_DISPLAY_NAME));
|
||||
m_settingsDirCheckBox.setChecked(QbsSettings::useCreatorSettingsDirForQbs());
|
||||
|
||||
const auto layout = new QFormLayout(this);
|
||||
layout->addRow(&m_settingsDirCheckBox);
|
||||
layout->addRow(tr("Path to qbs executable:"), &m_qbsExePathChooser);
|
||||
layout->addRow(tr("Qbs version:"), &m_versionLabel);
|
||||
}
|
||||
|
||||
void apply()
|
||||
{
|
||||
QbsSettingsData settings;
|
||||
if (m_qbsExePathChooser.isValid())
|
||||
settings.qbsExecutableFilePath = m_qbsExePathChooser.fileName();
|
||||
settings.useCreatorSettings = m_settingsDirCheckBox.isChecked();
|
||||
QbsSettings::setSettingsData(settings);
|
||||
}
|
||||
|
||||
private:
|
||||
static QString getQbsVersion()
|
||||
{
|
||||
QProcess qbsProc;
|
||||
qbsProc.start(QbsSettings::qbsExecutableFilePath().toString(), {"--version"});
|
||||
if (!qbsProc.waitForStarted(3000) || !qbsProc.waitForFinished(5000)
|
||||
|| qbsProc.exitCode() != 0) {
|
||||
return tr("Failed to retrieve version.");
|
||||
}
|
||||
return QString::fromLocal8Bit(qbsProc.readAllStandardOutput()).trimmed();
|
||||
}
|
||||
|
||||
PathChooser m_qbsExePathChooser;
|
||||
QLabel m_versionLabel;
|
||||
QCheckBox m_settingsDirCheckBox;
|
||||
};
|
||||
|
||||
QbsSettingsPage::QbsSettingsPage()
|
||||
{
|
||||
setId("A.QbsProjectManager.QbsSettings");
|
||||
setDisplayName(tr("General"));
|
||||
setCategory(Constants::QBS_SETTINGS_CATEGORY);
|
||||
setDisplayCategory(QCoreApplication::translate("QbsProjectManager",
|
||||
Constants::QBS_SETTINGS_TR_CATEGORY));
|
||||
}
|
||||
|
||||
QWidget *QbsSettingsPage::widget()
|
||||
{
|
||||
if (!m_widget)
|
||||
m_widget = new SettingsWidget;
|
||||
return m_widget;
|
||||
|
||||
}
|
||||
|
||||
void QbsSettingsPage::apply()
|
||||
{
|
||||
if (m_widget)
|
||||
m_widget->apply();
|
||||
}
|
||||
|
||||
void QbsSettingsPage::finish()
|
||||
{
|
||||
delete m_widget;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QbsProjectManager
|
@@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
@@ -25,32 +25,57 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <coreplugin/dialogs/ioptionspage.h>
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
|
||||
namespace QbsProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
class QbsProjectManagerSettings : public QObject
|
||||
class QbsSettingsData {
|
||||
public:
|
||||
Utils::FilePath qbsExecutableFilePath;
|
||||
bool useCreatorSettings = true;
|
||||
};
|
||||
|
||||
class QbsSettings : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static QbsProjectManagerSettings &instance();
|
||||
static QbsSettings &instance();
|
||||
|
||||
static void setUseCreatorSettingsDirForQbs(bool useCreatorDir);
|
||||
static Utils::FilePath qbsExecutableFilePath();
|
||||
static bool useCreatorSettingsDirForQbs();
|
||||
static QString qbsSettingsBaseDir();
|
||||
static void writeSettings() { instance().doWriteSettings(); }
|
||||
|
||||
static void setSettingsData(const QbsSettingsData &settings);
|
||||
|
||||
signals:
|
||||
void settingsBaseChanged();
|
||||
void settingsChanged();
|
||||
|
||||
private:
|
||||
QbsProjectManagerSettings();
|
||||
QbsSettings();
|
||||
void loadSettings();
|
||||
void storeSettings() const;
|
||||
|
||||
void readSettings();
|
||||
void doWriteSettings();
|
||||
QbsSettingsData m_settings;
|
||||
};
|
||||
|
||||
bool m_useCreatorSettings;
|
||||
class QbsSettingsPage : public Core::IOptionsPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QbsSettingsPage();
|
||||
|
||||
private:
|
||||
QWidget *widget() override;
|
||||
void apply() override;
|
||||
void finish() override;
|
||||
|
||||
class SettingsWidget;
|
||||
QPointer<SettingsWidget> m_widget;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
@@ -17,14 +17,11 @@ Project {
|
||||
]
|
||||
|
||||
property bool qbsSubModuleExists: File.exists(qbsProject.qbsBaseDir + "/qbs.qbs")
|
||||
property path qbs_install_dir: Environment.getEnv("QBS_INSTALL_DIR")
|
||||
property bool useExternalQbs: qbs_install_dir
|
||||
property bool buildQbsProjectManager: useExternalQbs || qbsSubModuleExists
|
||||
Project {
|
||||
name: "qbs project"
|
||||
id: qbsProject
|
||||
property string qbsBaseDir: project.sharedSourcesDir + "/qbs"
|
||||
condition: qbsSubModuleExists && !useExternalQbs
|
||||
condition: qbsSubModuleExists
|
||||
|
||||
// The first entry is for overriding qbs' own qbsbuildconfig module.
|
||||
qbsSearchPaths: [project.ide_source_tree + "/qbs", qbsBaseDir + "/qbs-resources"]
|
||||
|
Reference in New Issue
Block a user