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:
Christian Kandeler
2019-06-28 14:30:32 +02:00
parent c2127c9ec4
commit 6913947986
38 changed files with 2339 additions and 1972 deletions

View File

@@ -1,10 +1,6 @@
find_package(Qbs)
add_qtc_plugin(QbsProjectManager add_qtc_plugin(QbsProjectManager
CONDITION TARGET Qbs::QbsCore DEPENDS Qt5::Qml Qt5::Widgets qmljs
DEPENDS Qbs::QbsCore Qt5::Widgets qmljs
DEFINES DEFINES
QBS_INSTALL_DIR="${QBS_INSTALL_DIR}"
IDE_LIBRARY_BASENAME="${IDE_LIBRARY_BASE_PATH}" IDE_LIBRARY_BASENAME="${IDE_LIBRARY_BASE_PATH}"
PLUGIN_DEPENDS Core ProjectExplorer CppTools QtSupport QmlJSTools PLUGIN_DEPENDS Core ProjectExplorer CppTools QtSupport QmlJSTools
SOURCES SOURCES
@@ -17,19 +13,20 @@ add_qtc_plugin(QbsProjectManager
qbscleanstepconfigwidget.ui qbscleanstepconfigwidget.ui
qbsinstallstep.cpp qbsinstallstep.h qbsinstallstep.cpp qbsinstallstep.h
qbskitinformation.cpp qbskitinformation.h qbskitinformation.cpp qbskitinformation.h
qbslogsink.cpp qbslogsink.h
qbsnodes.cpp qbsnodes.h qbsnodes.cpp qbsnodes.h
qbsnodetreebuilder.cpp qbsnodetreebuilder.h qbsnodetreebuilder.cpp qbsnodetreebuilder.h
qbsparser.cpp qbsparser.h qbsparser.cpp qbsparser.h
qbspmlogging.cpp qbspmlogging.h qbspmlogging.cpp qbspmlogging.h
qbsprofilemanager.cpp qbsprofilemanager.h
qbsprofilessettingspage.cpp qbsprofilessettingspage.h qbsprofilessettingspage.cpp qbsprofilessettingspage.h
qbsprofilessettingswidget.ui qbsprofilessettingswidget.ui
qbsproject.cpp qbsproject.h qbsproject.cpp qbsproject.h
qbsprojectimporter.cpp qbsprojectimporter.h qbsprojectimporter.cpp qbsprojectimporter.h
qbsprojectmanager.cpp qbsprojectmanager.h qbsprojectmanager.qrc qbsprojectmanager.qrc
qbsprojectmanager_global.h qbsprojectmanager_global.h
qbsprojectmanagerconstants.h qbsprojectmanagerconstants.h
qbsprojectmanagerplugin.cpp qbsprojectmanagerplugin.h qbsprojectmanagerplugin.cpp qbsprojectmanagerplugin.h
qbsprojectmanagersettings.cpp qbsprojectmanagersettings.h
qbsprojectparser.cpp qbsprojectparser.h qbsprojectparser.cpp qbsprojectparser.h
qbssession.cpp qbssession.h
qbssettings.cpp qbssettings.h
) )

View File

@@ -26,7 +26,7 @@
#include "customqbspropertiesdialog.h" #include "customqbspropertiesdialog.h"
#include "ui_customqbspropertiesdialog.h" #include "ui_customqbspropertiesdialog.h"
#include <qbs.h> #include "qbsprofilemanager.h"
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -49,7 +49,7 @@ CustomQbsPropertiesDialog::CustomQbsPropertiesDialog(const QVariantMap &properti
nameItem->setData(Qt::DisplayRole, it.key()); nameItem->setData(Qt::DisplayRole, it.key());
m_ui->propertiesTable->setItem(currentRow, 0, nameItem); m_ui->propertiesTable->setItem(currentRow, 0, nameItem);
auto * const valueItem = new QTableWidgetItem; 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); m_ui->propertiesTable->setItem(currentRow, 1, valueItem);
++currentRow; ++currentRow;
} }
@@ -70,8 +70,7 @@ QVariantMap CustomQbsPropertiesDialog::properties() const
const QString name = nameItem->text(); const QString name = nameItem->text();
if (name.isEmpty()) if (name.isEmpty())
continue; continue;
const QString rawString = m_ui->propertiesTable->item(row, 1)->text(); properties.insert(name, fromJSLiteral(m_ui->propertiesTable->item(row, 1)->text()));
properties.insert(name, qbs::representationToSettingsValue(rawString));
} }
return properties; return properties;
} }

View File

@@ -41,8 +41,6 @@
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <qbs.h>
#include <android/androidconstants.h> #include <android/androidconstants.h>
#include <ios/iosconstants.h> #include <ios/iosconstants.h>
#include <qtsupport/baseqtversion.h> #include <qtsupport/baseqtversion.h>

View File

@@ -30,7 +30,7 @@
#include "qbsinstallstep.h" #include "qbsinstallstep.h"
#include "qbsproject.h" #include "qbsproject.h"
#include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagerconstants.h"
#include "qbsprojectmanagersettings.h" #include "qbssettings.h"
#include <coreplugin/documentmanager.h> #include <coreplugin/documentmanager.h>
@@ -167,7 +167,7 @@ bool QbsBuildConfiguration::fromMap(const QVariantMap &map)
return false; return false;
if (m_configurationName->value().isEmpty()) { // pre-4.4 backwards compatibility 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() const QString buildVariant = qbsConfiguration()
.value(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)).toString(); .value(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)).toString();
m_configurationName->setValue(profileName + '-' + buildVariant); m_configurationName->setValue(profileName + '-' + buildVariant);
@@ -328,9 +328,9 @@ QString QbsBuildConfiguration::equivalentCommandLine(const BuildStep *buildStep)
const QString buildDir = buildDirectory().toUserOutput(); const QString buildDir = buildDirectory().toUserOutput();
commandLine.addArgs({"-d", buildDir}); commandLine.addArgs({"-d", buildDir});
commandLine.addArgs({"-f", buildStep->project()->projectFilePath().toUserOutput()}); commandLine.addArgs({"-f", buildStep->project()->projectFilePath().toUserOutput()});
if (QbsProjectManagerSettings::useCreatorSettingsDirForQbs()) { if (QbsSettings::useCreatorSettingsDirForQbs()) {
commandLine.addArgs({"--settings-dir", commandLine.addArgs({"--settings-dir",
QDir::toNativeSeparators(QbsProjectManagerSettings::qbsSettingsBaseDir())}); QDir::toNativeSeparators(QbsSettings::qbsSettingsBaseDir())});
} }
if (stepProxy.dryRun()) if (stepProxy.dryRun())
commandLine.addArg("--dry-run"); commandLine.addArg("--dry-run");
@@ -350,7 +350,7 @@ QString QbsBuildConfiguration::equivalentCommandLine(const BuildStep *buildStep)
if (jobCount > 0) if (jobCount > 0)
commandLine.addArgs({"--jobs", QString::number(jobCount)}); 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() const QString buildVariant = qbsConfiguration()
.value(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)).toString(); .value(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)).toString();
commandLine.addArg("config:" + configurationName()); commandLine.addArg("config:" + configurationName());

View File

@@ -29,7 +29,8 @@
#include "qbsparser.h" #include "qbsparser.h"
#include "qbsproject.h" #include "qbsproject.h"
#include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagerconstants.h"
#include "qbsprojectmanagersettings.h" #include "qbssession.h"
#include "qbssettings.h"
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/variablechooser.h> #include <coreplugin/variablechooser.h>
@@ -48,11 +49,12 @@
#include <QCheckBox> #include <QCheckBox>
#include <QComboBox> #include <QComboBox>
#include <QFormLayout> #include <QFormLayout>
#include <QJsonArray>
#include <QJsonObject>
#include <QLabel> #include <QLabel>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QSpinBox> #include <QSpinBox>
#include <QThread>
#include <qbs.h>
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// Constants: // Constants:
@@ -161,16 +163,14 @@ QbsBuildStep::QbsBuildStep(ProjectExplorer::BuildStepList *bsl) :
QbsBuildStep::~QbsBuildStep() QbsBuildStep::~QbsBuildStep()
{ {
doCancel(); doCancel();
if (m_job) { if (m_session)
m_job->deleteLater(); m_session->disconnect(this);
m_job = nullptr;
}
delete m_parser; delete m_parser;
} }
bool QbsBuildStep::init() bool QbsBuildStep::init()
{ {
if (qbsBuildSystem()->isParsing() || m_job) if (qbsBuildSystem()->isParsing() || m_session)
return false; return false;
auto bc = static_cast<QbsBuildConfiguration *>(buildConfiguration()); 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 // 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). // right before building (but before the delay has elapsed).
m_parsingAfterBuild = false;
parseProject(); parseProject();
} }
@@ -213,8 +214,8 @@ void QbsBuildStep::doCancel()
{ {
if (m_parsingProject) if (m_parsingProject)
qbsBuildSystem()->cancelParsing(); qbsBuildSystem()->cancelParsing();
else if (m_job) else if (m_session)
m_job->cancel(); m_session->cancelCurrentJob();
} }
QVariantMap QbsBuildStep::qbsConfiguration(VariableHandling variableHandling) const 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) { for (auto it = config.begin(), end = config.end(); it != end; ++it) {
const QString rawString = it.value().toString(); const QString rawString = it.value().toString();
const QString expandedString = expander->expand(rawString); const QString expandedString = expander->expand(rawString);
it.value() = qbs::representationToSettingsValue(expandedString); it.value() = expandedString;
} }
} }
return config; return config;
@@ -252,26 +253,6 @@ void QbsBuildStep::setQbsConfiguration(const QVariantMap &config)
emit qbsConfigurationChanged(); 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 bool QbsBuildStep::hasCustomInstallRoot() const
{ {
return m_qbsConfiguration.contains(Constants::QBS_INSTALL_ROOT_KEY); return m_qbsConfiguration.contains(Constants::QBS_INSTALL_ROOT_KEY);
@@ -286,15 +267,14 @@ Utils::FilePath QbsBuildStep::installRoot(VariableHandling variableHandling) con
const QbsBuildConfiguration * const bc const QbsBuildConfiguration * const bc
= static_cast<QbsBuildConfiguration *>(buildConfiguration()); = static_cast<QbsBuildConfiguration *>(buildConfiguration());
return bc->buildDirectory().pathAppended(bc->configurationName()) return bc->buildDirectory().pathAppended(bc->configurationName()).pathAppended("install-root");
.pathAppended(qbs::InstallOptions::defaultInstallRoot());
} }
int QbsBuildStep::maxJobs() const int QbsBuildStep::maxJobs() const
{ {
if (m_qbsBuildOptions.maxJobCount() > 0) if (m_maxJobCount > 0)
return m_qbsBuildOptions.maxJobCount(); return m_maxJobCount;
return qbs::BuildOptions::defaultMaxJobCount(); return QThread::idealThreadCount();
} }
static QString forceProbesKey() { return QLatin1String("Qbs.forceProbesKey"); } static QString forceProbesKey() { return QLatin1String("Qbs.forceProbesKey"); }
@@ -306,15 +286,11 @@ bool QbsBuildStep::fromMap(const QVariantMap &map)
return false; return false;
setQbsConfiguration(map.value(QBS_CONFIG).toMap()); setQbsConfiguration(map.value(QBS_CONFIG).toMap());
m_qbsBuildOptions.setDryRun(map.value(QBS_DRY_RUN).toBool()); m_keepGoing = map.value(QBS_KEEP_GOING).toBool();
m_qbsBuildOptions.setKeepGoing(map.value(QBS_KEEP_GOING).toBool()); m_maxJobCount = map.value(QBS_MAXJOBCOUNT).toInt();
m_qbsBuildOptions.setMaxJobCount(map.value(QBS_MAXJOBCOUNT).toInt()); m_showCommandLines = map.value(QBS_SHOWCOMMANDLINES).toBool();
const bool showCommandLines = map.value(QBS_SHOWCOMMANDLINES).toBool(); m_install = map.value(QBS_INSTALL, true).toBool();
m_qbsBuildOptions.setEchoMode(showCommandLines ? qbs::CommandEchoModeCommandLine m_cleanInstallDir = map.value(QBS_CLEAN_INSTALL_ROOT).toBool();
: qbs::CommandEchoModeSummary);
m_qbsBuildOptions.setInstall(map.value(QBS_INSTALL, true).toBool());
m_qbsBuildOptions.setRemoveExistingInstallation(map.value(QBS_CLEAN_INSTALL_ROOT)
.toBool());
m_forceProbes = map.value(forceProbesKey()).toBool(); m_forceProbes = map.value(forceProbesKey()).toBool();
m_enableQmlDebugging = map.value(enableQmlDebuggingKey()).toBool(); m_enableQmlDebugging = map.value(enableQmlDebuggingKey()).toBool();
return true; return true;
@@ -324,43 +300,47 @@ QVariantMap QbsBuildStep::toMap() const
{ {
QVariantMap map = ProjectExplorer::BuildStep::toMap(); QVariantMap map = ProjectExplorer::BuildStep::toMap();
map.insert(QBS_CONFIG, m_qbsConfiguration); map.insert(QBS_CONFIG, m_qbsConfiguration);
map.insert(QBS_DRY_RUN, m_qbsBuildOptions.dryRun()); map.insert(QBS_KEEP_GOING, m_keepGoing);
map.insert(QBS_KEEP_GOING, m_qbsBuildOptions.keepGoing()); map.insert(QBS_MAXJOBCOUNT, m_maxJobCount);
map.insert(QBS_MAXJOBCOUNT, m_qbsBuildOptions.maxJobCount()); map.insert(QBS_SHOWCOMMANDLINES, m_showCommandLines);
map.insert(QBS_SHOWCOMMANDLINES, map.insert(QBS_INSTALL, m_install);
m_qbsBuildOptions.echoMode() == qbs::CommandEchoModeCommandLine); map.insert(QBS_CLEAN_INSTALL_ROOT, m_cleanInstallDir);
map.insert(QBS_INSTALL, m_qbsBuildOptions.install());
map.insert(QBS_CLEAN_INSTALL_ROOT,
m_qbsBuildOptions.removeExistingInstallation());
map.insert(forceProbesKey(), m_forceProbes); map.insert(forceProbesKey(), m_forceProbes);
map.insert(enableQmlDebuggingKey(), m_enableQmlDebugging); map.insert(enableQmlDebuggingKey(), m_enableQmlDebugging);
return map; return map;
} }
void QbsBuildStep::buildingDone(bool success) void QbsBuildStep::buildingDone(const ErrorInfo &error)
{ {
m_lastWasSuccess = success; m_session->disconnect(this);
// Report errors: m_session = nullptr;
foreach (const qbs::ErrorItem &item, m_job->error().items()) m_lastWasSuccess = !error.hasError();
createTaskAndOutput(ProjectExplorer::Task::Error, item.description(), for (const ErrorInfoItem &item : qAsConst(error.items)) {
item.codeLocation().filePath(), item.codeLocation().line()); createTaskAndOutput(
ProjectExplorer::Task::Error,
item.description,
item.filePath.toString(),
item.line);
}
// Building can uncover additional target artifacts. // Building can uncover additional target artifacts.
qbsBuildSystem()->updateAfterBuild(); qbsBuildSystem()->updateAfterBuild();
// The reparsing, if it is necessary, has to be done before finished() is emitted, as // 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. // otherwise a potential additional build step could conflict with the parsing step.
if (qbsBuildSystem()->parsingScheduled()) if (qbsBuildSystem()->parsingScheduled()) {
m_parsingAfterBuild = true;
parseProject(); parseProject();
else } else {
finish(); finish();
} }
}
void QbsBuildStep::reparsingDone(bool success) void QbsBuildStep::reparsingDone(bool success)
{ {
disconnect(target(), &Target::parsingFinished, this, &QbsBuildStep::reparsingDone); disconnect(target(), &Target::parsingFinished, this, &QbsBuildStep::reparsingDone);
m_parsingProject = false; m_parsingProject = false;
if (m_job) { // This was a scheduled reparsing after building. if (m_parsingAfterBuild) {
finish(); finish();
} else if (!success) { } else if (!success) {
m_lastWasSuccess = false; m_lastWasSuccess = false;
@@ -382,30 +362,31 @@ void QbsBuildStep::handleProgress(int value)
emit progress(value * 100 / m_maxProgress, m_currentTask); 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); 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(); const bool hasOutput = !stdOut.isEmpty() || !stdErr.isEmpty();
if (success && !hasOutput)
if (result.success() && !hasOutput)
return; return;
m_parser->setWorkingDirectory(result.workingDirectory()); m_parser->setWorkingDirectory(workingDir.toString());
emit addOutput(executable.toUserOutput() + ' ' + QtcProcess::joinArgs(arguments),
QString commandline = result.executableFilePath() + ' ' OutputFormat::Stdout);
+ Utils::QtcProcess::joinArgs(result.arguments()); for (const QString &line : stdErr) {
emit addOutput(commandline, OutputFormat::Stdout);
foreach (const QString &line, result.stdErr()) {
m_parser->stdError(line); m_parser->stdError(line);
emit addOutput(line, OutputFormat::Stderr); emit addOutput(line, OutputFormat::Stderr);
} }
foreach (const QString &line, result.stdOut()) { for (const QString &line : stdOut) {
m_parser->stdOutput(line); m_parser->stdOutput(line);
emit addOutput(line, OutputFormat::Stdout); emit addOutput(line, OutputFormat::Stdout);
} }
@@ -449,17 +430,17 @@ QString QbsBuildStep::profile() const
void QbsBuildStep::setKeepGoing(bool kg) void QbsBuildStep::setKeepGoing(bool kg)
{ {
if (m_qbsBuildOptions.keepGoing() == kg) if (m_keepGoing == kg)
return; return;
m_qbsBuildOptions.setKeepGoing(kg); m_keepGoing = kg;
emit qbsBuildOptionsChanged(); emit qbsBuildOptionsChanged();
} }
void QbsBuildStep::setMaxJobs(int jobcount) void QbsBuildStep::setMaxJobs(int jobcount)
{ {
if (m_qbsBuildOptions.maxJobCount() == jobcount) if (m_maxJobCount == jobcount)
return; return;
m_qbsBuildOptions.setMaxJobCount(jobcount); m_maxJobCount = jobcount;
emit qbsBuildOptionsChanged(); emit qbsBuildOptionsChanged();
} }
@@ -467,24 +448,23 @@ void QbsBuildStep::setShowCommandLines(bool show)
{ {
if (showCommandLines() == show) if (showCommandLines() == show)
return; return;
m_qbsBuildOptions.setEchoMode(show ? qbs::CommandEchoModeCommandLine m_showCommandLines = show;
: qbs::CommandEchoModeSummary);
emit qbsBuildOptionsChanged(); emit qbsBuildOptionsChanged();
} }
void QbsBuildStep::setInstall(bool install) void QbsBuildStep::setInstall(bool install)
{ {
if (m_qbsBuildOptions.install() == install) if (m_install == install)
return; return;
m_qbsBuildOptions.setInstall(install); m_install = install;
emit qbsBuildOptionsChanged(); emit qbsBuildOptionsChanged();
} }
void QbsBuildStep::setCleanInstallRoot(bool clean) void QbsBuildStep::setCleanInstallRoot(bool clean)
{ {
if (m_qbsBuildOptions.removeExistingInstallation() == clean) if (m_cleanInstallDir == clean)
return; return;
m_qbsBuildOptions.setRemoveExistingInstallation(clean); m_cleanInstallDir = clean;
emit qbsBuildOptionsChanged(); emit qbsBuildOptionsChanged();
} }
@@ -497,41 +477,49 @@ void QbsBuildStep::parseProject()
void QbsBuildStep::build() void QbsBuildStep::build()
{ {
qbs::BuildOptions options(m_qbsBuildOptions); m_session = qbsBuildSystem()->session();
options.setChangedFiles(m_changedFiles); if (!m_session) {
options.setFilesToConsider(m_changedFiles); emit addOutput(tr("No qbs session exists for this target."), OutputFormat::ErrorMessage);
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);
emit finished(false); emit finished(false);
return; 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; m_maxProgress = 0;
connect(m_session, &QbsSession::projectBuilt, this, &QbsBuildStep::buildingDone);
connect(m_job, &qbs::AbstractJob::finished, this, &QbsBuildStep::buildingDone); connect(m_session, &QbsSession::taskStarted, this, &QbsBuildStep::handleTaskStarted);
connect(m_job, &qbs::AbstractJob::taskStarted, connect(m_session, &QbsSession::taskProgress, this, &QbsBuildStep::handleProgress);
this, &QbsBuildStep::handleTaskStarted); connect(m_session, &QbsSession::commandDescription,
connect(m_job, &qbs::AbstractJob::taskProgress, this, &QbsBuildStep::handleCommandDescription);
this, &QbsBuildStep::handleProgress); connect(m_session, &QbsSession::processResult, this, &QbsBuildStep::handleProcessResult);
connect(m_job, &qbs::BuildJob::reportCommandDescription, connect(m_session, &QbsSession::errorOccurred, this, [this] {
this, &QbsBuildStep::handleCommandDescriptionReport); buildingDone(ErrorInfo(tr("Build canceled: Qbs session failed.")));
connect(m_job, &qbs::BuildJob::reportProcessResult, });
this, &QbsBuildStep::handleProcessResultReport);
} }
void QbsBuildStep::finish() void QbsBuildStep::finish()
{ {
m_session = nullptr;
emit finished(m_lastWasSuccess); emit finished(m_lastWasSuccess);
if (m_job) {
m_job->deleteLater();
m_job = nullptr;
}
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
@@ -548,7 +536,7 @@ QbsBuildStepConfigWidget::QbsBuildStepConfigWidget(QbsBuildStep *step) :
this, &QbsBuildStepConfigWidget::updateState); this, &QbsBuildStepConfigWidget::updateState);
connect(step, &QbsBuildStep::qbsBuildOptionsChanged, connect(step, &QbsBuildStep::qbsBuildOptionsChanged,
this, &QbsBuildStepConfigWidget::updateState); this, &QbsBuildStepConfigWidget::updateState);
connect(&QbsProjectManagerSettings::instance(), &QbsProjectManagerSettings::settingsBaseChanged, connect(&QbsSettings::instance(), &QbsSettings::settingsChanged,
this, &QbsBuildStepConfigWidget::updateState); this, &QbsBuildStepConfigWidget::updateState);
connect(step->buildConfiguration(), &BuildConfiguration::buildDirectoryChanged, connect(step->buildConfiguration(), &BuildConfiguration::buildDirectoryChanged,
this, &QbsBuildStepConfigWidget::updateState); this, &QbsBuildStepConfigWidget::updateState);

View File

@@ -30,13 +30,13 @@
#include <projectexplorer/buildstep.h> #include <projectexplorer/buildstep.h>
#include <projectexplorer/task.h> #include <projectexplorer/task.h>
#include <qbs.h>
namespace Utils { class FancyLineEdit; } namespace Utils { class FancyLineEdit; }
namespace QbsProjectManager { namespace QbsProjectManager {
namespace Internal { namespace Internal {
class ErrorInfo;
class QbsProject; class QbsProject;
class QbsSession;
class QbsBuildStepConfigWidget; class QbsBuildStepConfigWidget;
@@ -61,10 +61,10 @@ public:
QVariantMap qbsConfiguration(VariableHandling variableHandling) const; QVariantMap qbsConfiguration(VariableHandling variableHandling) const;
void setQbsConfiguration(const QVariantMap &config); void setQbsConfiguration(const QVariantMap &config);
bool keepGoing() const; bool keepGoing() const { return m_keepGoing; }
bool showCommandLines() const; bool showCommandLines() const { return m_showCommandLines; }
bool install() const; bool install() const { return m_install; }
bool cleanInstallRoot() const; bool cleanInstallRoot() const { return m_cleanInstallDir; }
bool hasCustomInstallRoot() const; bool hasCustomInstallRoot() const;
Utils::FilePath installRoot(VariableHandling variableHandling = ExpandVariables) const; Utils::FilePath installRoot(VariableHandling variableHandling = ExpandVariables) const;
int maxJobs() const; int maxJobs() const;
@@ -93,12 +93,18 @@ private:
bool fromMap(const QVariantMap &map) override; bool fromMap(const QVariantMap &map) override;
QVariantMap toMap() const override; QVariantMap toMap() const override;
void buildingDone(bool success); void buildingDone(const ErrorInfo &error);
void reparsingDone(bool success); void reparsingDone(bool success);
void handleTaskStarted(const QString &desciption, int max); void handleTaskStarted(const QString &desciption, int max);
void handleProgress(int value); void handleProgress(int value);
void handleCommandDescriptionReport(const QString &highlight, const QString &message); void handleCommandDescription(const QString &message);
void handleProcessResultReport(const qbs::ProcessResult &result); 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, void createTaskAndOutput(ProjectExplorer::Task::TaskType type,
const QString &message, const QString &file, int line); const QString &message, const QString &file, int line);
@@ -117,7 +123,11 @@ private:
void finish(); void finish();
QVariantMap m_qbsConfiguration; 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_forceProbes = false;
bool m_enableQmlDebugging; bool m_enableQmlDebugging;
@@ -126,12 +136,14 @@ private:
QStringList m_activeFileTags; QStringList m_activeFileTags;
QStringList m_products; QStringList m_products;
qbs::BuildJob *m_job = nullptr; QbsSession *m_session = nullptr;
QString m_currentTask; QString m_currentTask;
int m_maxProgress; int m_maxProgress;
bool m_lastWasSuccess; bool m_lastWasSuccess;
ProjectExplorer::IOutputParser *m_parser = nullptr; ProjectExplorer::IOutputParser *m_parser = nullptr;
bool m_parsingProject = false; bool m_parsingProject = false;
bool m_parsingAfterBuild = false;
friend class QbsBuildStepConfigWidget; friend class QbsBuildStepConfigWidget;
}; };

View File

@@ -28,6 +28,7 @@
#include "qbsbuildconfiguration.h" #include "qbsbuildconfiguration.h"
#include "qbsproject.h" #include "qbsproject.h"
#include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagerconstants.h"
#include "qbssession.h"
#include <projectexplorer/buildsteplist.h> #include <projectexplorer/buildsteplist.h>
#include <projectexplorer/kit.h> #include <projectexplorer/kit.h>
@@ -36,6 +37,9 @@
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QJsonArray>
#include <QJsonObject>
using namespace ProjectExplorer; using namespace ProjectExplorer;
namespace QbsProjectManager { namespace QbsProjectManager {
@@ -73,66 +77,60 @@ QbsCleanStep::QbsCleanStep(ProjectExplorer::BuildStepList *bsl) :
QbsCleanStep::~QbsCleanStep() QbsCleanStep::~QbsCleanStep()
{ {
doCancel(); doCancel();
if (m_job) { if (m_session)
m_job->deleteLater(); m_session->disconnect(this);
m_job = nullptr;
}
} }
bool QbsCleanStep::init() bool QbsCleanStep::init()
{ {
if (buildSystem()->isParsing() || m_job) if (buildSystem()->isParsing() || m_session)
return false; return false;
const auto bc = static_cast<QbsBuildConfiguration *>(buildConfiguration());
auto bc = static_cast<QbsBuildConfiguration *>(buildConfiguration());
if (!bc) if (!bc)
return false; return false;
m_products = bc->products(); m_products = bc->products();
return true; return true;
} }
void QbsCleanStep::doRun() void QbsCleanStep::doRun()
{ {
qbs::CleanOptions options; m_session = static_cast<QbsBuildSystem*>(buildSystem())->session();
options.setDryRun(m_dryRunAspect->value()); if (!m_session) {
options.setKeepGoing(m_keepGoingAspect->value()); emit addOutput(tr("No qbs session exists for this target."), OutputFormat::ErrorMessage);
QString error;
m_job = qbsBuildSystem()->clean(options, m_products, error);
if (!m_job) {
emit addOutput(error, OutputFormat::ErrorMessage);
emit finished(false); emit finished(false);
return; 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; m_maxProgress = 0;
connect(m_session, &QbsSession::projectCleaned, this, &QbsCleanStep::cleaningDone);
connect(m_job, &qbs::AbstractJob::finished, this, &QbsCleanStep::cleaningDone); connect(m_session, &QbsSession::taskStarted, this, &QbsCleanStep::handleTaskStarted);
connect(m_job, &qbs::AbstractJob::taskStarted, connect(m_session, &QbsSession::taskProgress, this, &QbsCleanStep::handleProgress);
this, &QbsCleanStep::handleTaskStarted); connect(m_session, &QbsSession::errorOccurred, this, [this] {
connect(m_job, &qbs::AbstractJob::taskProgress, cleaningDone(ErrorInfo(tr("Cleaning canceled: Qbs session failed.")));
this, &QbsCleanStep::handleProgress); });
} }
void QbsCleanStep::doCancel() void QbsCleanStep::doCancel()
{ {
if (m_job) if (m_session)
m_job->cancel(); m_session->cancelCurrentJob();
} }
void QbsCleanStep::cleaningDone(bool success) void QbsCleanStep::cleaningDone(const ErrorInfo &error)
{ {
// Report errors: m_session->disconnect(this);
foreach (const qbs::ErrorItem &item, m_job->error().items()) { m_session = nullptr;
createTaskAndOutput(ProjectExplorer::Task::Error, item.description(),
item.codeLocation().filePath(), item.codeLocation().line());
}
emit finished(success); for (const ErrorInfoItem &item : error.items)
m_job->deleteLater(); createTaskAndOutput(Task::Error, item.description, item.filePath.toString(), item.line);
m_job = nullptr; emit finished(!error.hasError());
} }
void QbsCleanStep::handleTaskStarted(const QString &desciption, int max) 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) void QbsCleanStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message, const QString &file, int line)
{ {
ProjectExplorer::Task task = ProjectExplorer::Task(type, message, Task task(type, message, Utils::FilePath::fromString(file), line,
Utils::FilePath::fromString(file), line,
ProjectExplorer::Constants::TASK_CATEGORY_COMPILE); ProjectExplorer::Constants::TASK_CATEGORY_COMPILE);
emit addTask(task, 1); emit addTask(task, 1);
emit addOutput(message, OutputFormat::Stdout); emit addOutput(message, OutputFormat::Stdout);

View File

@@ -31,10 +31,10 @@
#include <projectexplorer/projectconfigurationaspects.h> #include <projectexplorer/projectconfigurationaspects.h>
#include <projectexplorer/task.h> #include <projectexplorer/task.h>
#include <qbs.h>
namespace QbsProjectManager { namespace QbsProjectManager {
namespace Internal { namespace Internal {
class ErrorInfo;
class QbsSession;
class QbsCleanStep : public ProjectExplorer::BuildStep class QbsCleanStep : public ProjectExplorer::BuildStep
{ {
@@ -52,7 +52,7 @@ private:
void doRun() override; void doRun() override;
void doCancel() override; void doCancel() override;
void cleaningDone(bool success); void cleaningDone(const ErrorInfo &error);
void handleTaskStarted(const QString &desciption, int max); void handleTaskStarted(const QString &desciption, int max);
void handleProgress(int value); void handleProgress(int value);
@@ -65,8 +65,7 @@ private:
QbsBuildSystem *qbsBuildSystem() const; QbsBuildSystem *qbsBuildSystem() const;
QStringList m_products; QStringList m_products;
QbsSession *m_session = nullptr;
qbs::CleanJob *m_job = nullptr;
QString m_description; QString m_description;
int m_maxProgress; int m_maxProgress;
bool m_showCompilerOutput = true; bool m_showCompilerOutput = true;

View File

@@ -29,6 +29,7 @@
#include "qbsbuildstep.h" #include "qbsbuildstep.h"
#include "qbsproject.h" #include "qbsproject.h"
#include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagerconstants.h"
#include "qbssession.h"
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <projectexplorer/buildsteplist.h> #include <projectexplorer/buildsteplist.h>
@@ -41,6 +42,7 @@
#include <QCheckBox> #include <QCheckBox>
#include <QFileInfo> #include <QFileInfo>
#include <QFormLayout> #include <QFormLayout>
#include <QJsonObject>
#include <QLabel> #include <QLabel>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QSpacerItem> #include <QSpacerItem>
@@ -91,45 +93,45 @@ QbsInstallStep::QbsInstallStep(ProjectExplorer::BuildStepList *bsl) :
setDisplayName(tr("Qbs Install")); setDisplayName(tr("Qbs Install"));
const QbsBuildConfiguration * const bc = buildConfig(); const QbsBuildConfiguration * const bc = buildConfig();
connect(bc, &QbsBuildConfiguration::qbsConfigurationChanged, connect(bc, &QbsBuildConfiguration::qbsConfigurationChanged, this, &QbsInstallStep::changed);
this, &QbsInstallStep::handleBuildConfigChanged);
if (bc->qbsStep()) { if (bc->qbsStep()) {
connect(bc->qbsStep(), &QbsBuildStep::qbsBuildOptionsChanged, connect(bc->qbsStep(), &QbsBuildStep::qbsBuildOptionsChanged,
this, &QbsInstallStep::handleBuildConfigChanged); this, &QbsInstallStep::changed);
} }
} }
QbsInstallStep::~QbsInstallStep() QbsInstallStep::~QbsInstallStep()
{ {
doCancel(); doCancel();
if (m_job) if (m_session)
m_job->deleteLater(); m_session->disconnect(this);
m_job = nullptr;
} }
bool QbsInstallStep::init() bool QbsInstallStep::init()
{ {
QTC_ASSERT(!buildConfiguration()->buildSystem()->isParsing() && !m_job, return false); QTC_ASSERT(!buildConfiguration()->buildSystem()->isParsing() && !m_session, return false);
return true; return true;
} }
void QbsInstallStep::doRun() void QbsInstallStep::doRun()
{ {
auto bs = static_cast<QbsBuildSystem *>(buildSystem()); m_session = static_cast<QbsBuildSystem *>(buildSystem())->session();
m_job = bs->install(m_qbsInstallOptions);
if (!m_job) { QJsonObject request;
emit finished(false); request.insert("type", "install");
return; 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; m_maxProgress = 0;
connect(m_session, &QbsSession::projectInstalled, this, &QbsInstallStep::installDone);
connect(m_job, &qbs::AbstractJob::finished, this, &QbsInstallStep::installDone); connect(m_session, &QbsSession::taskStarted, this, &QbsInstallStep::handleTaskStarted);
connect(m_job, &qbs::AbstractJob::taskStarted, connect(m_session, &QbsSession::taskProgress, this, &QbsInstallStep::handleProgress);
this, &QbsInstallStep::handleTaskStarted); connect(m_session, &QbsSession::errorOccurred, this, [this] {
connect(m_job, &qbs::AbstractJob::taskProgress, installDone(ErrorInfo(tr("Installing canceled: Qbs session failed.")));
this, &QbsInstallStep::handleProgress); });
} }
ProjectExplorer::BuildStepConfigWidget *QbsInstallStep::createConfigWidget() ProjectExplorer::BuildStepConfigWidget *QbsInstallStep::createConfigWidget()
@@ -139,8 +141,8 @@ ProjectExplorer::BuildStepConfigWidget *QbsInstallStep::createConfigWidget()
void QbsInstallStep::doCancel() void QbsInstallStep::doCancel()
{ {
if (m_job) if (m_session)
m_job->cancel(); m_session->cancelCurrentJob();
} }
QString QbsInstallStep::installRoot() const QString QbsInstallStep::installRoot() const
@@ -149,21 +151,6 @@ QString QbsInstallStep::installRoot() const
return bs ? bs->installRoot().toString() : QString(); 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 const QbsBuildConfiguration *QbsInstallStep::buildConfig() const
{ {
return static_cast<QbsBuildConfiguration *>(buildConfiguration()); return static_cast<QbsBuildConfiguration *>(buildConfiguration());
@@ -174,40 +161,30 @@ bool QbsInstallStep::fromMap(const QVariantMap &map)
if (!ProjectExplorer::BuildStep::fromMap(map)) if (!ProjectExplorer::BuildStep::fromMap(map))
return false; return false;
m_qbsInstallOptions.setInstallRoot(installRoot()); m_cleanInstallRoot = map.value(QBS_REMOVE_FIRST, false).toBool();
m_qbsInstallOptions.setRemoveExistingInstallation( m_dryRun = map.value(QBS_DRY_RUN, false).toBool();
map.value(QLatin1String(QBS_REMOVE_FIRST), false).toBool()); m_keepGoing = map.value(QBS_KEEP_GOING, false).toBool();
m_qbsInstallOptions.setDryRun(map.value(QLatin1String(QBS_DRY_RUN), false).toBool());
m_qbsInstallOptions.setKeepGoing(map.value(QLatin1String(QBS_KEEP_GOING), false).toBool());
return true; return true;
} }
QVariantMap QbsInstallStep::toMap() const QVariantMap QbsInstallStep::toMap() const
{ {
QVariantMap map = ProjectExplorer::BuildStep::toMap(); QVariantMap map = ProjectExplorer::BuildStep::toMap();
map.insert(QLatin1String(QBS_REMOVE_FIRST), m_qbsInstallOptions.removeExistingInstallation()); map.insert(QBS_REMOVE_FIRST, m_cleanInstallRoot);
map.insert(QLatin1String(QBS_DRY_RUN), m_qbsInstallOptions.dryRun()); map.insert(QBS_DRY_RUN, m_dryRun);
map.insert(QLatin1String(QBS_KEEP_GOING), m_qbsInstallOptions.keepGoing()); map.insert(QBS_KEEP_GOING, m_keepGoing);
return map; 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) for (const ErrorInfoItem &item : error.items)
{ createTaskAndOutput(Task::Error, item.description, item.filePath.toString(), item.line);
// Report errors:
foreach (const qbs::ErrorItem &item, m_job->error().items()) {
createTaskAndOutput(ProjectExplorer::Task::Error, item.description(),
item.codeLocation().filePath(), item.codeLocation().line());
}
emit finished(success); emit finished(!error.hasError());
m_job->deleteLater();
m_job = nullptr;
} }
void QbsInstallStep::handleTaskStarted(const QString &desciption, int max) void QbsInstallStep::handleTaskStarted(const QString &desciption, int max)
@@ -234,31 +211,25 @@ void QbsInstallStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type,
void QbsInstallStep::setRemoveFirst(bool rf) void QbsInstallStep::setRemoveFirst(bool rf)
{ {
if (m_qbsInstallOptions.removeExistingInstallation() == rf) if (m_cleanInstallRoot == rf)
return; return;
m_qbsInstallOptions.setRemoveExistingInstallation(rf); m_cleanInstallRoot = rf;
emit changed(); emit changed();
} }
void QbsInstallStep::setDryRun(bool dr) void QbsInstallStep::setDryRun(bool dr)
{ {
if (m_qbsInstallOptions.dryRun() == dr) if (m_dryRun == dr)
return; return;
m_qbsInstallOptions.setDryRun(dr); m_dryRun = dr;
emit changed(); emit changed();
} }
void QbsInstallStep::setKeepGoing(bool kg) void QbsInstallStep::setKeepGoing(bool kg)
{ {
if (m_qbsInstallOptions.keepGoing() == kg) if (m_keepGoing == kg)
return; return;
m_qbsInstallOptions.setKeepGoing(kg); m_keepGoing = kg;
emit changed();
}
void QbsInstallStep::handleBuildConfigChanged()
{
m_qbsInstallOptions.setInstallRoot(installRoot());
emit changed(); emit changed();
} }

View File

@@ -26,14 +26,15 @@
#pragma once #pragma once
#include "qbsbuildconfiguration.h" #include "qbsbuildconfiguration.h"
#include "qbssession.h"
#include <projectexplorer/buildstep.h> #include <projectexplorer/buildstep.h>
#include <projectexplorer/task.h> #include <projectexplorer/task.h>
#include <qbs.h>
namespace QbsProjectManager { namespace QbsProjectManager {
namespace Internal { namespace Internal {
class ErrorInfo;
class QbsSession;
class QbsInstallStep : public ProjectExplorer::BuildStep class QbsInstallStep : public ProjectExplorer::BuildStep
{ {
@@ -43,11 +44,10 @@ public:
explicit QbsInstallStep(ProjectExplorer::BuildStepList *bsl); explicit QbsInstallStep(ProjectExplorer::BuildStepList *bsl);
~QbsInstallStep() override; ~QbsInstallStep() override;
qbs::InstallOptions installOptions() const;
QString installRoot() const; QString installRoot() const;
bool removeFirst() const; bool removeFirst() const { return m_cleanInstallRoot; }
bool dryRun() const; bool dryRun() const { return m_dryRun; }
bool keepGoing() const; bool keepGoing() const { return m_keepGoing; }
signals: signals:
void changed(); void changed();
@@ -61,7 +61,7 @@ private:
QVariantMap toMap() const override; QVariantMap toMap() const override;
const QbsBuildConfiguration *buildConfig() const; const QbsBuildConfiguration *buildConfig() const;
void installDone(bool success); void installDone(const ErrorInfo &error);
void handleTaskStarted(const QString &desciption, int max); void handleTaskStarted(const QString &desciption, int max);
void handleProgress(int value); void handleProgress(int value);
@@ -71,14 +71,14 @@ private:
void setRemoveFirst(bool rf); void setRemoveFirst(bool rf);
void setDryRun(bool dr); void setDryRun(bool dr);
void setKeepGoing(bool kg); 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; QString m_description;
int m_maxProgress; int m_maxProgress;
bool m_showCompilerOutput = true;
ProjectExplorer::IOutputParser *m_parser = nullptr; ProjectExplorer::IOutputParser *m_parser = nullptr;
friend class QbsInstallStepConfigWidget; friend class QbsInstallStepConfigWidget;

View File

@@ -26,14 +26,13 @@
#include "qbskitinformation.h" #include "qbskitinformation.h"
#include "customqbspropertiesdialog.h" #include "customqbspropertiesdialog.h"
#include "qbsprofilemanager.h"
#include <projectexplorer/kitmanager.h> #include <projectexplorer/kitmanager.h>
#include <utils/elidinglabel.h> #include <utils/elidinglabel.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <qbs.h>
#include <QPushButton> #include <QPushButton>
using namespace ProjectExplorer; using namespace ProjectExplorer;
@@ -85,7 +84,7 @@ QString QbsKitAspect::representation(const Kit *kit)
for (auto it = props.begin(); it != props.end(); ++it) { for (auto it = props.begin(); it != props.end(); ++it) {
if (!repr.isEmpty()) if (!repr.isEmpty())
repr += ' '; repr += ' ';
repr += it.key() + ':' + qbs::settingsValueToRepresentation(it.value()); repr += it.key() + ':' + toJSLiteral(it.value());
} }
return repr; return repr;
} }

View File

@@ -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

View File

@@ -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

View File

@@ -29,6 +29,7 @@
#include "qbsproject.h" #include "qbsproject.h"
#include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagerconstants.h"
#include "qbsprojectmanagerplugin.h" #include "qbsprojectmanagerplugin.h"
#include "qbssession.h"
#include <android/androidconstants.h> #include <android/androidconstants.h>
#include <coreplugin/fileiconprovider.h> #include <coreplugin/fileiconprovider.h>
@@ -44,8 +45,10 @@
#include <QtDebug> #include <QtDebug>
#include <QDir> #include <QDir>
#include <QIcon> #include <QIcon>
#include <QJsonArray>
using namespace ProjectExplorer; using namespace ProjectExplorer;
using namespace Utils;
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// Helpers: // Helpers:
@@ -54,147 +57,26 @@ using namespace ProjectExplorer;
namespace QbsProjectManager { namespace QbsProjectManager {
namespace Internal { namespace Internal {
class FileTreeNode { const QbsProductNode *parentQbsProductNode(const ProjectExplorer::Node *node)
public:
explicit FileTreeNode(const QString &n = QString(), FileTreeNode *p = nullptr, bool f = false) :
parent(p), name(n), m_isFile(f)
{ {
if (p) for (; node; node = node->parentFolderNode()) {
p->children.append(this); 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; 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::QbsGroupNode(const qbs::GroupData &grp, const QString &productPath) : QbsGroupNode::QbsGroupNode(const QJsonObject &grp) : ProjectNode(FilePath()), m_groupData(grp)
ProjectNode(Utils::FilePath())
{ {
static QIcon groupIcon = QIcon(QString(Constants::QBS_GROUP_ICON)); static QIcon groupIcon = QIcon(QString(Constants::QBS_GROUP_ICON));
setIcon(groupIcon); setIcon(groupIcon);
setDisplayName(grp.value("name").toString());
m_productPath = productPath; setEnabled(grp.value("is-enabled").toBool());
m_qbsGroupData = grp;
} }
FolderNode::AddNewInformation QbsGroupNode::addNewInformation(const QStringList &files, FolderNode::AddNewInformation QbsGroupNode::addNewInformation(const QStringList &files,
@@ -208,8 +90,15 @@ FolderNode::AddNewInformation QbsGroupNode::addNewInformation(const QStringList
QVariant QbsGroupNode::data(Core::Id role) const QVariant QbsGroupNode::data(Core::Id role) const
{ {
if (role == ProjectExplorer::Constants::QT_KEYWORDS_ENABLED) if (role == ProjectExplorer::Constants::QT_KEYWORDS_ENABLED) {
return m_qbsGroupData.properties().getModuleProperty("Qt.core", "enableKeywords"); 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(); return QVariant();
} }
@@ -217,106 +106,104 @@ QVariant QbsGroupNode::data(Core::Id role) const
// QbsProductNode: // QbsProductNode:
// -------------------------------------------------------------------- // --------------------------------------------------------------------
QbsProductNode::QbsProductNode(const qbs::ProductData &prd) : QbsProductNode::QbsProductNode(const QJsonObject &prd) : ProjectNode(FilePath()), m_productData(prd)
ProjectNode(Utils::FilePath::fromString(prd.location().filePath())),
m_qbsProductData(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); setIcon(productIcon);
if (m_qbsProductData.isRunnable()) { if (prd.value("is-runnable").toBool()) {
setProductType(ProductType::App); setProductType(ProductType::App);
} else if (m_qbsProductData.type().contains("dynamiclibrary")
|| m_qbsProductData.type().contains("staticlibrary")) {
setProductType(ProductType::Lib);
} else { } else {
const QJsonArray type = prd.value("type").toArray();
if (type.contains("dynamiclibrary") || type.contains("staticlibrary"))
setProductType(ProductType::Lib);
else
setProductType(ProductType::Other); setProductType(ProductType::Other);
} }
setEnabled(prd.value("is-enabled").toBool());
setDisplayName(prd.value("full-display-name").toString());
} }
void QbsProductNode::build() void QbsProductNode::build()
{ {
QbsProjectManagerPlugin::buildNamedProduct(static_cast<QbsProject *>(getProject()), QbsProjectManagerPlugin::buildNamedProduct(static_cast<QbsProject *>(getProject()),
QbsProject::uniqueProductName(qbsProductData())); m_productData.value("full-display-name").toString());
} }
QStringList QbsProductNode::targetApplications() const QStringList QbsProductNode::targetApplications() const
{ {
return QStringList{m_qbsProductData.targetExecutable()}; return QStringList{m_productData.value("target-executable").toString()};
} }
QString QbsProductNode::buildKey() const QString QbsProductNode::buildKey() const
{ {
return QbsProject::uniqueProductName(m_qbsProductData); return m_productData.value("full-display-name").toString();
} }
QVariant QbsProductNode::data(Core::Id role) const QVariant QbsProductNode::data(Core::Id role) const
{ {
if (role == Android::Constants::AndroidDeploySettingsFile) { if (role == Android::Constants::AndroidDeploySettingsFile) {
for (const auto &artifact : m_qbsProductData.generatedArtifacts()) { for (const auto &a : m_productData.value("generated-artifacts").toArray()) {
if (artifact.fileTags().contains("qt_androiddeployqt_input")) const QJsonObject artifact = a.toObject();
return artifact.filePath(); if (artifact.value("file-tags").toArray().contains("qt_androiddeployqt_input"))
return artifact.value("file-path").toString();
} }
return {}; return {};
} }
if (role == Android::Constants::AndroidSoLibPath) { if (role == Android::Constants::AndroidSoLibPath) {
QStringList ret{m_qbsProductData.buildDirectory()}; QStringList ret{m_productData.value("build-directory").toString()};
for (const auto &artifact : m_qbsProductData.generatedArtifacts()) { forAllArtifacts(m_productData, ArtifactType::Generated, [&ret](const QJsonObject &artifact) {
if (artifact.fileTags().contains("dynamiclibrary")) { if (artifact.value("file-tags").toArray().contains("dynamiclibrary"))
ret << QFileInfo(artifact.filePath()).path(); ret << QFileInfo(artifact.value("file-path").toString()).path();
} });
}
ret.removeDuplicates(); ret.removeDuplicates();
return ret; return ret;
} }
if (role == Android::Constants::AndroidManifest) { if (role == Android::Constants::AndroidManifest) {
for (const auto &artifact : m_qbsProductData.generatedArtifacts()) { for (const auto &a : m_productData.value("generated-artifacts").toArray()) {
if (artifact.fileTags().contains("android.manifest_final")) const QJsonObject artifact = a.toObject();
return artifact.filePath(); if (artifact.value("file-tags").toArray().contains("android.manifest_final"))
return artifact.value("file-path").toString();
} }
return {}; return {};
} }
if (role == Android::Constants::AndroidApk) if (role == Android::Constants::AndroidApk)
return m_qbsProductData.targetExecutable(); return m_productData.value("target-executable").toString();
if (role == ProjectExplorer::Constants::QT_KEYWORDS_ENABLED) 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 {}; 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::QbsProjectNode(const Utils::FilePath &projectDirectory) : QbsProjectNode::QbsProjectNode(const QJsonObject &projectData)
ProjectNode(projectDirectory) : 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); 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 Internal
} // namespace QbsProjectManager } // namespace QbsProjectManager

View File

@@ -28,91 +28,60 @@
#include <projectexplorer/buildsystem.h> #include <projectexplorer/buildsystem.h>
#include <projectexplorer/projectnodes.h> #include <projectexplorer/projectnodes.h>
#include <qbs.h> #include <QJsonObject>
namespace QbsProjectManager { namespace QbsProjectManager {
namespace Internal { namespace Internal {
class QbsNodeTreeBuilder;
class QbsProject; class QbsProject;
class QbsBuildSystem; class QbsBuildSystem;
// --------------------------------------------------------------------
// QbsGroupNode:
// --------------------------------------------------------------------
class QbsGroupNode : public ProjectExplorer::ProjectNode class QbsGroupNode : public ProjectExplorer::ProjectNode
{ {
public: public:
QbsGroupNode(const qbs::GroupData &grp, const QString &productPath); QbsGroupNode(const QJsonObject &grp);
bool showInSimpleTree() const final { return false; } bool showInSimpleTree() const final { return false; }
QJsonObject groupData() const { return m_groupData; }
private: private:
friend class QbsBuildSystem; friend class QbsBuildSystem;
AddNewInformation addNewInformation(const QStringList &files, Node *context) const override; AddNewInformation addNewInformation(const QStringList &files, Node *context) const override;
QVariant data(Core::Id role) const override; QVariant data(Core::Id role) const override;
qbs::GroupData m_qbsGroupData; const QJsonObject m_groupData;
QString m_productPath;
}; };
// --------------------------------------------------------------------
// QbsProductNode:
// --------------------------------------------------------------------
class QbsProductNode : public ProjectExplorer::ProjectNode class QbsProductNode : public ProjectExplorer::ProjectNode
{ {
public: public:
explicit QbsProductNode(const qbs::ProductData &prd); explicit QbsProductNode(const QJsonObject &prd);
void build() override; void build() override;
QStringList targetApplications() const override; QStringList targetApplications() const override;
QString buildKey() 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; QVariant data(Core::Id role) const override;
private: private:
const qbs::ProductData m_qbsProductData; const QJsonObject m_productData;
}; };
// ---------------------------------------------------------------------------
// QbsProjectNode:
// ---------------------------------------------------------------------------
class QbsProjectNode : public ProjectExplorer::ProjectNode class QbsProjectNode : public ProjectExplorer::ProjectNode
{ {
public: public:
explicit QbsProjectNode(const Utils::FilePath &projectDirectory); explicit QbsProjectNode(const QJsonObject &projectData);
virtual ProjectExplorer::Project *project() const; const QJsonObject projectData() const { return m_projectData; }
const qbs::ProjectData qbsProjectData() const { return m_projectData; }
void setProjectData(const qbs::ProjectData &data); // FIXME: Needed?
private: private:
qbs::ProjectData m_projectData; const QJsonObject 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 QbsProductNode *parentQbsProductNode(const ProjectExplorer::Node *node);
} // namespace Internal } // namespace Internal
} // namespace QbsProjectManager } // namespace QbsProjectManager

View File

@@ -25,7 +25,13 @@
#include "qbsnodetreebuilder.h" #include "qbsnodetreebuilder.h"
#include "qbsnodes.h"
#include "qbsproject.h" #include "qbsproject.h"
#include "qbssession.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -36,13 +42,9 @@ using namespace Utils;
namespace QbsProjectManager { namespace QbsProjectManager {
namespace Internal { namespace Internal {
namespace { static FileType fileType(const QJsonObject &artifact)
FileType fileType(const qbs::ArtifactData &artifact)
{ {
QTC_ASSERT(artifact.isValid(), return FileType::Unknown); const QJsonArray fileTags = artifact.value("file-tags").toArray();
const QStringList fileTags = artifact.fileTags();
if (fileTags.contains("c") if (fileTags.contains("c")
|| fileTags.contains("cpp") || fileTags.contains("cpp")
|| fileTags.contains("objc") || fileTags.contains("objc")
@@ -62,160 +64,160 @@ FileType fileType(const qbs::ArtifactData &artifact)
return FileType::Unknown; 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(artifact.value("file-path").toString());
const FilePath path = FilePath::fromString(ad.filePath()); const FileType type = fileType(artifact);
const FileType type = fileType(ad); const bool isGenerated = artifact.value("is-generated").toBool();
const bool isGenerated = ad.isGenerated();
// A list of human-readable file types that we can reasonably expect // A list of human-readable file types that we can reasonably expect
// to get generated during a build. Extend as needed. // to get generated during a build. Extend as needed.
static const QSet<QString> sourceTags = { static const QSet<QString> sourceTags = {
QLatin1String("c"), QLatin1String("cpp"), QLatin1String("hpp"), "c", "cpp", "hpp", "objc", "objcpp", "c_pch_src", "cpp_pch_src", "objc_pch_src",
QLatin1String("objc"), QLatin1String("objcpp"), "objcpp_pch_src", "asm", "asm_cpp", "linkerscript", "qrc", "java.java"
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")
}; };
auto node = std::make_unique<FileNode>(path, type); auto node = std::make_unique<FileNode>(path, type);
node->setIsGenerated(isGenerated); 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)); root->addNestedNode(std::move(node));
} }
static void setupArtifactsForGroup(FolderNode *root, const QJsonObject &group)
{
forAllArtifacts(group, [root](const QJsonObject &artifact) { setupArtifact(root, artifact); });
root->compress(); root->compress();
} }
std::unique_ptr<QbsGroupNode> static void setupGeneratedArtifacts(FolderNode *root, const QJsonObject &product)
buildGroupNodeTree(const qbs::GroupData &grp, const QString &productPath, bool productIsEnabled)
{ {
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); static std::unique_ptr<QbsGroupNode> buildGroupNodeTree(const QJsonObject &grp)
{
result->setEnabled(productIsEnabled && grp.isEnabled()); const Location location = locationFromObject(grp);
result->setAbsoluteFilePathAndLine( FilePath baseDir = location.filePath.parentDir();
FilePath::fromString(grp.location().filePath()).parentDir(), -1); QString prefix = grp.value("prefix").toString();
result->setDisplayName(grp.name()); 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)); result->addNode(std::move(fileNode));
setupArtifactsForGroup(result.get(), grp);
setupArtifacts(result.get(), grp.allSourceArtifacts());
return result; 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()), const Location location = locationFromObject(prd);
FileType::Project); auto result = std::make_unique<QbsProductNode>(prd);
fileNode->setLine(prd.location().line()); result->setAbsoluteFilePathAndLine(location.filePath.parentDir(), -1);
auto fileNode = std::make_unique<FileNode>(FilePath(), FileType::Project);
node->setEnabled(prd.isEnabled()); fileNode->setAbsoluteFilePathAndLine(location.filePath, location.line);
node->setDisplayName(prd.fullDisplayName()); result->addNode(std::move(fileNode));
node->setAbsoluteFilePathAndLine(FilePath::fromString(prd.location().filePath()).parentDir(), -1); for (const QJsonValue &v : prd.value("groups").toArray()) {
node->addNode(std::move(fileNode)); const QJsonObject grp = v.toObject();
if (grp.value("name") == prd.value("name")
const QString &productPath = QFileInfo(prd.location().filePath()).absolutePath(); && grp.value("location") == prd.value("location")) {
foreach (const qbs::GroupData &grp, prd.groups()) {
if (grp.name() == prd.name() && grp.location() == prd.location()) {
// Set implicit product group right onto this node: // Set implicit product group right onto this node:
setupArtifacts(node, grp.allSourceArtifacts()); setupArtifactsForGroup(result.get(), grp);
continue; continue;
} }
node->addNode(buildGroupNodeTree(grp, productPath, prd.isEnabled())); result->addNode(buildGroupNodeTree(grp));
} }
// Add "Generated Files" Node: // 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")); genFiles->setDisplayName(QCoreApplication::translate("QbsProductNode", "Generated files"));
setupArtifacts(genFiles.get(), prd.generatedArtifacts()); setupGeneratedArtifacts(genFiles.get(), prd);
node->addNode(std::move(genFiles)); result->addNode(std::move(genFiles));
}
std::unique_ptr<QbsProductNode> buildProductNodeTree(const qbs::ProductData &prd)
{
auto result = std::make_unique<QbsProductNode>(prd);
setupQbsProductData(result.get(), prd);
return result; return result;
} }
void setupProjectNode(QbsProjectNode *node, const qbs::ProjectData &prjData, static void setupProjectNode(QbsProjectNode *node)
const qbs::Project &qbsProject)
{ {
auto fileNode = std::make_unique<FileNode>(FilePath::fromString(prjData.location().filePath()), const Location loc = locationFromObject(node->projectData());
FileType::Project); node->setAbsoluteFilePathAndLine(loc.filePath.parentDir(), -1);
fileNode->setLine(prjData.location().line()); auto fileNode = std::make_unique<FileNode>(node->filePath(), FileType::Project);
fileNode->setAbsoluteFilePathAndLine(loc.filePath, loc.line);
node->addNode(std::move(fileNode)); node->addNode(std::move(fileNode));
foreach (const qbs::ProjectData &subData, prjData.subProjects()) {
auto subProject = std::make_unique<QbsProjectNode>( for (const QJsonValue &v : node->projectData().value("sub-projects").toArray()) {
FilePath::fromString(subData.location().filePath()).parentDir()); auto subProject = std::make_unique<QbsProjectNode>(v.toObject());
setupProjectNode(subProject.get(), subData, qbsProject); setupProjectNode(subProject.get());
node->addNode(std::move(subProject)); node->addNode(std::move(subProject));
} }
foreach (const qbs::ProductData &prd, prjData.products()) for (const QJsonValue &v : node->projectData().value("products").toArray())
node->addNode(buildProductNodeTree(prd)); node->addNode(buildProductNodeTree(v.toObject()));
if (!prjData.name().isEmpty())
node->setDisplayName(prjData.name());
else
node->setDisplayName(node->project()->displayName());
node->setProjectData(prjData);
} }
QSet<QString> referencedBuildSystemFiles(const qbs::ProjectData &data) static QSet<QString> referencedBuildSystemFiles(const QJsonObject &prjData)
{ {
QSet<QString> result; QSet<QString> result;
result.insert(data.location().filePath()); result.insert(prjData.value("location").toObject().value("file-path").toString());
foreach (const qbs::ProjectData &subProject, data.subProjects()) for (const QJsonValue &v : prjData.value("sub-projects").toArray())
result.unite(referencedBuildSystemFiles(subProject)); result.unite(referencedBuildSystemFiles(v.toObject()));
foreach (const qbs::ProductData &product, data.products()) { for (const QJsonValue &v : prjData.value("products").toArray()) {
result.insert(product.location().filePath()); const QJsonObject product = v.toObject();
foreach (const qbs::GroupData &group, product.groups()) result.insert(product.value("location").toObject().value("file-path").toString());
result.insert(group.location().filePath()); for (const QJsonValue &v : product.value("groups").toArray())
result.insert(v.toObject().value("location").toObject().value("file-path").toString());
} }
return result; return result;
} }
QStringList unreferencedBuildSystemFiles(const qbs::Project &p) static QStringList unreferencedBuildSystemFiles(const QJsonObject &project)
{ {
QStringList result; QStringList unreferenced = arrayToStringList(project.value("build-system-files"));
if (!p.isValid()) const QList<QString> referenced = toList(referencedBuildSystemFiles(project));
return result; for (auto it = unreferenced.begin(); it != unreferenced.end(); ) {
if (referenced.contains(*it))
const std::set<QString> &available = p.buildSystemFiles(); it = unreferenced.erase(it);
QList<QString> referenced = Utils::toList(referencedBuildSystemFiles(p.projectData())); else
Utils::sort(referenced); ++it;
std::set_difference(available.begin(), available.end(), referenced.begin(), referenced.end(), }
std::back_inserter(result)); return unreferenced;
return result;
} }
} // namespace std::unique_ptr<QbsProjectNode> QbsNodeTreeBuilder::buildTree(const QbsBuildSystem *buildSystem)
std::unique_ptr<QbsRootProjectNode> QbsNodeTreeBuilder::buildTree(QbsBuildSystem *buildSystem)
{ {
if (!buildSystem->qbsProjectData().isValid()) const Project * const project = buildSystem->project();
return {}; auto root = std::make_unique<QbsProjectNode>(buildSystem->projectData());
auto root = std::make_unique<QbsRootProjectNode>(buildSystem->project()); // If we have no project information at all (i.e. it could not be properly parsed),
setupProjectNode(root.get(), buildSystem->qbsProjectData(), buildSystem->qbsProject()); // 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()); if (root->displayName().isEmpty())
buildSystemFiles->setDisplayName(QCoreApplication::translate("QbsRootProjectNode", "Qbs files")); root->setDisplayName(project->displayName());
if (root->displayName().isEmpty())
root->setDisplayName(project->projectFilePath().toFileInfo().completeBaseName());
const FilePath base = buildSystem->projectDirectory(); auto buildSystemFiles = std::make_unique<FolderNode>(project->projectDirectory());
const QStringList files = unreferencedBuildSystemFiles(buildSystem->qbsProject()); buildSystemFiles->setDisplayName(QCoreApplication::translate("QbsProjectNode", "Qbs files"));
const FilePath base = project->projectDirectory();
const QStringList files = unreferencedBuildSystemFiles(buildSystem->projectData());
for (const QString &f : files) { for (const QString &f : files) {
const FilePath filePath = FilePath::fromString(f); const FilePath filePath = FilePath::fromString(f);
if (filePath.isChildOf(base)) if (filePath.isChildOf(base))
@@ -223,7 +225,6 @@ std::unique_ptr<QbsRootProjectNode> QbsNodeTreeBuilder::buildTree(QbsBuildSystem
} }
buildSystemFiles->compress(); buildSystemFiles->compress();
root->addNode(std::move(buildSystemFiles)); root->addNode(std::move(buildSystemFiles));
return root; return root;
} }

View File

@@ -25,21 +25,18 @@
#pragma once #pragma once
#include "qbsnodes.h" #include <memory>
#include <qbs.h>
namespace QbsProjectManager { namespace QbsProjectManager {
namespace Internal { namespace Internal {
// ---------------------------------------------------------------------- class QbsBuildSystem;
// QbsNodeTreeBuilder: class QbsProjectNode;
// ----------------------------------------------------------------------
class QbsNodeTreeBuilder class QbsNodeTreeBuilder
{ {
public: public:
static std::unique_ptr<QbsRootProjectNode> buildTree(QbsBuildSystem *project); static std::unique_ptr<QbsProjectNode> buildTree(const QbsBuildSystem *buildSystem);
}; };
} // namespace Internal } // namespace Internal

View 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

View File

@@ -28,32 +28,33 @@
#include "qbsprojectmanager_global.h" #include "qbsprojectmanager_global.h"
#include <QList> #include <QList>
#include <QString> #include <QVariant>
#include <QVariantMap>
namespace qbs { class Settings; }
namespace ProjectExplorer { class Kit; } namespace ProjectExplorer { class Kit; }
namespace QbsProjectManager { namespace QbsProjectManager {
namespace Internal { namespace Internal {
class DefaultPropertyProvider; class DefaultPropertyProvider;
class QbsLogSink;
class QbsManager : public QObject QString toJSLiteral(const QVariant &val);
QVariant fromJSLiteral(const QString &str);
class QbsProfileManager : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
QbsManager(); QbsProfileManager();
~QbsManager() override; ~QbsProfileManager() override;
static QbsProfileManager *instance();
// QBS profiles management:
static QString profileForKit(const ProjectExplorer::Kit *k); static QString profileForKit(const ProjectExplorer::Kit *k);
static void updateProfileIfNecessary(const ProjectExplorer::Kit *kit); 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(); signals:
static Internal::QbsLogSink *logSink(); void qbsProfilesUpdated();
private: private:
void setProfileForKit(const QString &name, const ProjectExplorer::Kit *k); void setProfileForKit(const QString &name, const ProjectExplorer::Kit *k);

View File

@@ -26,50 +26,111 @@
#include "qbsprofilessettingspage.h" #include "qbsprofilessettingspage.h"
#include "ui_qbsprofilessettingswidget.h" #include "ui_qbsprofilessettingswidget.h"
#include "qbsprojectmanager.h" #include "qbsprofilemanager.h"
#include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagerconstants.h"
#include "qbsprojectmanagersettings.h" #include "qbssettings.h"
#include <app/app_version.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <projectexplorer/kit.h> #include <projectexplorer/kit.h>
#include <projectexplorer/kitmanager.h> #include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectexplorericons.h> #include <projectexplorer/projectexplorericons.h>
#include <projectexplorer/taskhub.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/treemodel.h>
#include <qbs.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QHash> #include <QHash>
#include <QWidget> #include <QWidget>
using namespace ProjectExplorer;
namespace QbsProjectManager { namespace QbsProjectManager {
namespace Internal { 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 class QbsProfilesSettingsWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
QbsProfilesSettingsWidget(); QbsProfilesSettingsWidget();
void apply();
private: private:
void refreshKitsList(); void refreshKitsList();
void displayCurrentProfile(); void displayCurrentProfile();
Ui::QbsProfilesSettingsWidget m_ui; Ui::QbsProfilesSettingsWidget m_ui;
qbs::SettingsModel m_model; ProfileModel m_model;
}; };
QbsProfilesSettingsPage::QbsProfilesSettingsPage() QbsProfilesSettingsPage::QbsProfilesSettingsPage()
: m_useQtcSettingsDirPersistent(QbsProjectManagerSettings::useCreatorSettingsDirForQbs())
{ {
setId("Y.QbsProfiles"); setId("Y.QbsProfiles");
setDisplayName(QCoreApplication::translate("QbsProjectManager", "Qbs")); setDisplayName(QCoreApplication::translate("QbsProjectManager", "Profiles"));
setCategory(ProjectExplorer::Constants::KITS_SETTINGS_CATEGORY); setCategory(Constants::QBS_SETTINGS_CATEGORY);
} }
QWidget *QbsProfilesSettingsPage::widget() QWidget *QbsProfilesSettingsPage::widget()
@@ -79,38 +140,17 @@ QWidget *QbsProfilesSettingsPage::widget()
return m_widget; return m_widget;
} }
void QbsProfilesSettingsPage::apply()
{
if (m_widget)
m_widget->apply();
m_useQtcSettingsDirPersistent = QbsProjectManagerSettings::useCreatorSettingsDirForQbs();
}
void QbsProfilesSettingsPage::finish() void QbsProfilesSettingsPage::finish()
{ {
delete m_widget; delete m_widget;
m_widget = nullptr; m_widget = nullptr;
QbsProjectManagerSettings::setUseCreatorSettingsDirForQbs(m_useQtcSettingsDirPersistent);
QbsProjectManagerSettings::writeSettings();
} }
QbsProfilesSettingsWidget::QbsProfilesSettingsWidget() QbsProfilesSettingsWidget::QbsProfilesSettingsWidget()
: m_model(QbsProjectManagerSettings::qbsSettingsBaseDir(), qbs::Settings::UserScope)
{ {
m_model.setEditable(false);
m_ui.setupUi(this); m_ui.setupUi(this);
m_ui.settingsDirCheckBox->setText(tr("Store profiles in %1 settings directory") connect(QbsProfileManager::instance(), &QbsProfileManager::qbsProfilesUpdated,
.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,
this, &QbsProfilesSettingsWidget::refreshKitsList); 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, connect(m_ui.expandButton, &QAbstractButton::clicked,
m_ui.propertiesView, &QTreeView::expandAll); m_ui.propertiesView, &QTreeView::expandAll);
connect(m_ui.collapseButton, &QAbstractButton::clicked, connect(m_ui.collapseButton, &QAbstractButton::clicked,
@@ -118,12 +158,6 @@ QbsProfilesSettingsWidget::QbsProfilesSettingsWidget()
refreshKitsList(); refreshKitsList();
} }
void QbsProfilesSettingsWidget::apply()
{
m_model.reload();
displayCurrentProfile();
}
void QbsProfilesSettingsWidget::refreshKitsList() void QbsProfilesSettingsWidget::refreshKitsList()
{ {
m_ui.kitsComboBox->disconnect(this); m_ui.kitsComboBox->disconnect(this);
@@ -135,10 +169,10 @@ void QbsProfilesSettingsWidget::refreshKitsList()
currentId = Core::Id::fromSetting(m_ui.kitsComboBox->currentData()); currentId = Core::Id::fromSetting(m_ui.kitsComboBox->currentData());
m_ui.kitsComboBox->clear(); m_ui.kitsComboBox->clear();
int newCurrentIndex = -1; int newCurrentIndex = -1;
QList<ProjectExplorer::Kit *> validKits = ProjectExplorer::KitManager::kits(); QList<Kit *> validKits = KitManager::kits();
Utils::erase(validKits, [](const ProjectExplorer::Kit *k) { return !k->isValid(); }); Utils::erase(validKits, [](const Kit *k) { return !k->isValid(); });
const bool hasKits = !validKits.isEmpty(); const bool hasKits = !validKits.isEmpty();
foreach (const ProjectExplorer::Kit * const kit, validKits) { for (const Kit * const kit : qAsConst(validKits)) {
if (kit->id() == currentId) if (kit->id() == currentId)
newCurrentIndex = m_ui.kitsComboBox->count(); newCurrentIndex = m_ui.kitsComboBox->count();
m_ui.kitsComboBox->addItem(kit->displayName(), kit->id().toSetting()); m_ui.kitsComboBox->addItem(kit->displayName(), kit->id().toSetting());
@@ -159,26 +193,20 @@ void QbsProfilesSettingsWidget::displayCurrentProfile()
if (m_ui.kitsComboBox->currentIndex() == -1) if (m_ui.kitsComboBox->currentIndex() == -1)
return; return;
const Core::Id kitId = Core::Id::fromSetting(m_ui.kitsComboBox->currentData()); 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); QTC_ASSERT(kit, return);
const QString profileName = QbsManager::profileForKit(kit); const QString profileName = QbsProfileManager::profileForKit(kit);
m_ui.profileValueLabel->setText(profileName); m_ui.profileValueLabel->setText(profileName);
for (int i = 0; i < m_model.rowCount(); ++i) { for (int i = 0; i < m_model.rowCount(); ++i) {
const QModelIndex profilesIndex = m_model.index(i, 0); const QModelIndex currentProfileIndex = m_model.index(i, 0);
if (m_model.data(profilesIndex).toString() != QLatin1String("profiles")) if (m_model.data(currentProfileIndex, Qt::DisplayRole).toString() != profileName)
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)
continue; continue;
m_ui.propertiesView->setModel(&m_model); m_ui.propertiesView->setModel(&m_model);
m_ui.propertiesView->header()->setSectionResizeMode(m_model.keyColumn(), m_ui.propertiesView->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
QHeaderView::ResizeToContents);
m_ui.propertiesView->setRootIndex(currentProfileIndex); m_ui.propertiesView->setRootIndex(currentProfileIndex);
return; return;
} }
} }
}
} // namespace Internal } // namespace Internal
} // namespace QbsProjectManager } // namespace QbsProjectManager

View File

@@ -38,11 +38,10 @@ public:
private: private:
QWidget *widget() override; QWidget *widget() override;
void apply() override; void apply() override { }
void finish() override; void finish() override;
QbsProfilesSettingsWidget *m_widget = nullptr; QbsProfilesSettingsWidget *m_widget = nullptr;
bool m_useQtcSettingsDirPersistent;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -14,23 +14,16 @@
<string/> <string/>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <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> <item>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
<item row="1" column="0"> <item row="0" column="0">
<widget class="QLabel" name="kitLabel"> <widget class="QLabel" name="kitLabel">
<property name="text"> <property name="text">
<string>Kit:</string> <string>Kit:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QComboBox" name="kitsComboBox"/> <widget class="QComboBox" name="kitsComboBox"/>
@@ -50,14 +43,14 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="2" column="0"> <item row="1" column="0">
<widget class="QLabel" name="profileKeyLabel"> <widget class="QLabel" name="profileKeyLabel">
<property name="text"> <property name="text">
<string>Associated profile:</string> <string>Associated profile:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="1" column="1">
<widget class="QLabel" name="profileValueLabel"> <widget class="QLabel" name="profileValueLabel">
<property name="text"> <property name="text">
<string/> <string/>
@@ -67,23 +60,6 @@
</property> </property>
</widget> </widget>
</item> </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> </layout>
</item> </item>
<item> <item>

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,7 @@
#pragma once #pragma once
#include "qbsprojectmanager.h" #include "qbsprofilemanager.h"
#include "qbsnodes.h" #include "qbsnodes.h"
@@ -36,9 +36,8 @@
#include <utils/environment.h> #include <utils/environment.h>
#include <qbs.h>
#include <QHash> #include <QHash>
#include <QJsonObject>
#include <QTimer> #include <QTimer>
namespace CppTools { class CppProjectUpdater; } namespace CppTools { class CppProjectUpdater; }
@@ -46,8 +45,10 @@ namespace CppTools { class CppProjectUpdater; }
namespace QbsProjectManager { namespace QbsProjectManager {
namespace Internal { namespace Internal {
class ErrorInfo;
class QbsBuildConfiguration; class QbsBuildConfiguration;
class QbsProjectParser; class QbsProjectParser;
class QbsSession;
class QbsProject : public ProjectExplorer::Project class QbsProject : public ProjectExplorer::Project
{ {
@@ -63,8 +64,6 @@ public:
void configureAsExampleProject() final; void configureAsExampleProject() final;
static QString uniqueProductName(const qbs::ProductData &product);
private: private:
mutable ProjectExplorer::ProjectImporter *m_importer = nullptr; mutable ProjectExplorer::ProjectImporter *m_importer = nullptr;
}; };
@@ -94,24 +93,17 @@ public:
QVariant additionalData(Core::Id id) const final; QVariant additionalData(Core::Id id) const final;
bool isProjectEditable() const; bool isProjectEditable() const;
// qbs::ProductData and qbs::GroupData are held by the nodes in the project tree. bool addFilesToProduct(const QStringList &filePaths,
// These methods change those trees and invalidate the lot, so pass in copies of const QJsonObject &product,
// the data we are interested in! const QJsonObject &group,
// The overhead is not as big as it seems at first glance: These all are handles QStringList *notAdded);
// for shared data.
bool addFilesToProduct(const QStringList &filePaths, const qbs::ProductData productData,
const qbs::GroupData groupData, QStringList *notAdded);
ProjectExplorer::RemovedFilesFromProject removeFilesFromProduct(const QStringList &filePaths, ProjectExplorer::RemovedFilesFromProject removeFilesFromProduct(const QStringList &filePaths,
const qbs::ProductData &productData, const qbs::GroupData &groupData, const QJsonObject &product,
const QJsonObject &group,
QStringList *notRemoved); QStringList *notRemoved);
bool renameFileInProduct(const QString &oldPath, bool renameFileInProduct(const QString &oldPath,
const QString &newPath, const qbs::ProductData productData, const QString &newPath, const QJsonObject &product,
const qbs::GroupData groupData); const QJsonObject &group);
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);
static ProjectExplorer::FileType fileTypeFor(const QSet<QString> &tags); static ProjectExplorer::FileType fileTypeFor(const QSet<QString> &tags);
@@ -122,15 +114,16 @@ public:
void cancelParsing(); void cancelParsing();
void updateAfterBuild(); void updateAfterBuild();
qbs::Project qbsProject() const; QbsSession *session() const { return m_session; }
qbs::ProjectData qbsProjectData() const; QJsonObject projectData() const { return m_projectData; }
void generateErrors(const qbs::ErrorInfo &e); void generateErrors(const ErrorInfo &e);
void delayParsing(); void delayParsing();
private: private:
friend class QbsProject; friend class QbsProject;
void handleQbsParsingDone(bool success); void handleQbsParsingDone(bool success);
void rebuildProjectTree(); void rebuildProjectTree();
@@ -138,13 +131,12 @@ private:
void changeActiveTarget(ProjectExplorer::Target *t); void changeActiveTarget(ProjectExplorer::Target *t);
void prepareForParsing(); void prepareForParsing();
void updateDocuments(const std::set<QString> &files); void updateDocuments();
void updateCppCodeModel(); void updateCppCodeModel();
void updateQmlJsCodeModel(); void updateQmlJsCodeModel();
void updateApplicationTargets(); void updateApplicationTargets();
void updateDeploymentInfo(); void updateDeploymentInfo();
void updateBuildTargetData(); void updateBuildTargetData();
void handleRuleExecutionDone();
bool checkCancelStatus(); bool checkCancelStatus();
void updateAfterParse(); void updateAfterParse();
void delayedUpdateAfterParse(); void delayedUpdateAfterParse();
@@ -153,16 +145,14 @@ private:
static bool ensureWriteableQbsFile(const QString &file); static bool ensureWriteableQbsFile(const QString &file);
template<typename Options> qbs::AbstractJob *buildOrClean(const Options &opts, QbsSession * const m_session;
const QStringList &productNames, QString &error); QSet<Core::IDocument *> m_qbsDocuments;
QJsonObject m_projectData; // TODO: Perhaps store this in the root project node instead?
qbs::Project m_qbsProject;
qbs::ProjectData m_projectData; // Cached m_qbsProject.projectData()
Utils::Environment m_lastParseEnv;
QTimer m_parsingDelay;
QbsProjectParser *m_qbsProjectParser = nullptr; QbsProjectParser *m_qbsProjectParser = nullptr;
QFutureInterface<bool> *m_qbsUpdateFutureInterface = nullptr; QFutureInterface<bool> *m_qbsUpdateFutureInterface = nullptr;
Utils::Environment m_lastParseEnv;
bool m_parsingScheduled = false; bool m_parsingScheduled = false;
enum CancelStatus { enum CancelStatus {
@@ -173,9 +163,8 @@ private:
CppTools::CppProjectUpdater *m_cppCodeModelUpdater = nullptr; CppTools::CppProjectUpdater *m_cppCodeModelUpdater = nullptr;
QTimer m_parsingDelay; QHash<ProjectExplorer::ExtraCompilerFactory *, QStringList> m_sourcesForGeneratedFiles;
QList<ProjectExplorer::ExtraCompiler *> m_extraCompilers; QList<ProjectExplorer::ExtraCompiler *> m_extraCompilers;
bool m_extraCompilersPending = false;
QHash<QString, Utils::Environment> m_envCache; QHash<QString, Utils::Environment> m_envCache;

View File

@@ -27,6 +27,7 @@
#include "qbsbuildconfiguration.h" #include "qbsbuildconfiguration.h"
#include "qbspmlogging.h" #include "qbspmlogging.h"
#include "qbssession.h"
#include <coreplugin/documentmanager.h> #include <coreplugin/documentmanager.h>
#include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildconfiguration.h>
@@ -42,8 +43,6 @@
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <qbs.h>
#include <QFileInfo> #include <QFileInfo>
using namespace ProjectExplorer; using namespace ProjectExplorer;
@@ -62,10 +61,10 @@ struct BuildGraphData
FilePath sysroot; FilePath sysroot;
QString buildVariant; QString buildVariant;
}; };
static BuildGraphData extractBgData(const qbs::Project::BuildGraphInfo &bgInfo) static BuildGraphData extractBgData(const QbsSession::BuildGraphInfo &bgInfo)
{ {
BuildGraphData bgData; BuildGraphData bgData;
bgData.bgFilePath = FilePath::fromString(bgInfo.bgFilePath); bgData.bgFilePath = bgInfo.bgFilePath;
bgData.overriddenProperties = bgInfo.overriddenProperties; bgData.overriddenProperties = bgInfo.overriddenProperties;
const QVariantMap &moduleProps = bgInfo.requestedProperties; const QVariantMap &moduleProps = bgInfo.requestedProperties;
const QVariantMap prjCompilerPathByLanguage const QVariantMap prjCompilerPathByLanguage
@@ -139,15 +138,15 @@ QList<void *> QbsProjectImporter::examineDirectory(const FilePath &importPath) c
{ {
qCDebug(qbsPmLog) << "examining build directory" << importPath.toUserOutput(); qCDebug(qbsPmLog) << "examining build directory" << importPath.toUserOutput();
QList<void *> data; QList<void *> data;
const QString bgFilePath = importPath.toString() + QLatin1Char('/') + importPath.fileName() const FilePath bgFilePath = importPath.pathAppended(importPath.fileName() + ".bg");
+ QLatin1String(".bg");
const QStringList relevantProperties({ const QStringList relevantProperties({
"qbs.buildVariant", "qbs.sysroot", "qbs.toolchain", "qbs.buildVariant", "qbs.sysroot", "qbs.toolchain",
"cpp.compilerPath", "cpp.compilerPathByLanguage", "cpp.compilerPath", "cpp.compilerPathByLanguage",
"Qt.core.binPath" "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()) { if (bgInfo.error.hasError()) {
qCDebug(qbsPmLog) << "error getting build graph info:" << bgInfo.error.toString(); qCDebug(qbsPmLog) << "error getting build graph info:" << bgInfo.error.toString();
return data; return data;

View File

@@ -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

View File

@@ -1,20 +1,10 @@
include(../../qtcreatorplugin.pri) 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 += \ DEFINES += \
QBSPROJECTMANAGER_LIBRARY QBSPROJECTMANAGER_LIBRARY
QT += qml
HEADERS = \ HEADERS = \
customqbspropertiesdialog.h \ customqbspropertiesdialog.h \
defaultpropertyprovider.h \ defaultpropertyprovider.h \
@@ -24,20 +14,20 @@ HEADERS = \
qbscleanstep.h \ qbscleanstep.h \
qbskitinformation.h \ qbskitinformation.h \
qbsinstallstep.h \ qbsinstallstep.h \
qbslogsink.h \
qbsnodes.h \ qbsnodes.h \
qbsnodetreebuilder.h \ qbsnodetreebuilder.h \
qbsparser.h \ qbsparser.h \
qbspmlogging.h \ qbspmlogging.h \
qbsprofilemanager.h \
qbsprofilessettingspage.h \ qbsprofilessettingspage.h \
qbsproject.h \ qbsproject.h \
qbsprojectimporter.h \ qbsprojectimporter.h \
qbsprojectmanager.h \
qbsprojectmanager_global.h \ qbsprojectmanager_global.h \
qbsprojectmanagerconstants.h \ qbsprojectmanagerconstants.h \
qbsprojectmanagerplugin.h \ qbsprojectmanagerplugin.h \
qbsprojectmanagersettings.h \ qbsprojectparser.h \
qbsprojectparser.h qbssession.h \
qbssettings.h
SOURCES = \ SOURCES = \
customqbspropertiesdialog.cpp \ customqbspropertiesdialog.cpp \
@@ -47,18 +37,18 @@ SOURCES = \
qbscleanstep.cpp \ qbscleanstep.cpp \
qbsinstallstep.cpp \ qbsinstallstep.cpp \
qbskitinformation.cpp \ qbskitinformation.cpp \
qbslogsink.cpp \
qbsnodes.cpp \ qbsnodes.cpp \
qbsnodetreebuilder.cpp \ qbsnodetreebuilder.cpp \
qbsparser.cpp \ qbsparser.cpp \
qbspmlogging.cpp \ qbspmlogging.cpp \
qbsprofilemanager.cpp \
qbsprofilessettingspage.cpp \ qbsprofilessettingspage.cpp \
qbsproject.cpp \ qbsproject.cpp \
qbsprojectimporter.cpp \ qbsprojectimporter.cpp \
qbsprojectmanager.cpp \
qbsprojectmanagerplugin.cpp \ qbsprojectmanagerplugin.cpp \
qbsprojectmanagersettings.cpp \ qbsprojectparser.cpp \
qbsprojectparser.cpp qbssession.cpp \
qbssettings.cpp
FORMS = \ FORMS = \
customqbspropertiesdialog.ui \ customqbspropertiesdialog.ui \

View File

@@ -6,33 +6,8 @@ QtcPlugin {
name: "QbsProjectManager" name: "QbsProjectManager"
type: base.concat(["qmltype-update"]) type: base.concat(["qmltype-update"])
property var externalQbsIncludes: project.useExternalQbs Depends { name: "Qt"; submodules: [ "qml", "widgets" ] }
? [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
}
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: "QmlJS" }
Depends { name: "Utils" } Depends { name: "Utils" }
@@ -43,20 +18,6 @@ QtcPlugin {
Depends { name: "QmlJSTools" } Depends { name: "QmlJSTools" }
Depends { name: "app_version_header" } 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: [ files: [
"customqbspropertiesdialog.h", "customqbspropertiesdialog.h",
"customqbspropertiesdialog.cpp", "customqbspropertiesdialog.cpp",
@@ -75,8 +36,6 @@ QtcPlugin {
"qbsinstallstep.h", "qbsinstallstep.h",
"qbskitinformation.cpp", "qbskitinformation.cpp",
"qbskitinformation.h", "qbskitinformation.h",
"qbslogsink.cpp",
"qbslogsink.h",
"qbsnodes.cpp", "qbsnodes.cpp",
"qbsnodes.h", "qbsnodes.h",
"qbsnodetreebuilder.cpp", "qbsnodetreebuilder.cpp",
@@ -85,6 +44,8 @@ QtcPlugin {
"qbsparser.h", "qbsparser.h",
"qbspmlogging.cpp", "qbspmlogging.cpp",
"qbspmlogging.h", "qbspmlogging.h",
"qbsprofilemanager.cpp",
"qbsprofilemanager.h",
"qbsprofilessettingspage.cpp", "qbsprofilessettingspage.cpp",
"qbsprofilessettingspage.h", "qbsprofilessettingspage.h",
"qbsprofilessettingswidget.ui", "qbsprofilessettingswidget.ui",
@@ -92,23 +53,21 @@ QtcPlugin {
"qbsproject.h", "qbsproject.h",
"qbsprojectimporter.cpp", "qbsprojectimporter.cpp",
"qbsprojectimporter.h", "qbsprojectimporter.h",
"qbsprojectmanager.cpp",
"qbsprojectmanager.h",
"qbsprojectmanager.qrc", "qbsprojectmanager.qrc",
"qbsprojectmanager_global.h", "qbsprojectmanager_global.h",
"qbsprojectmanagerconstants.h", "qbsprojectmanagerconstants.h",
"qbsprojectmanagerplugin.cpp", "qbsprojectmanagerplugin.cpp",
"qbsprojectmanagerplugin.h", "qbsprojectmanagerplugin.h",
"qbsprojectmanagersettings.cpp",
"qbsprojectmanagersettings.h",
"qbsprojectparser.cpp", "qbsprojectparser.cpp",
"qbsprojectparser.h", "qbsprojectparser.h",
"qbssession.cpp",
"qbssession.h",
"qbssettings.cpp",
"qbssettings.h",
] ]
// QML typeinfo stuff // QML typeinfo stuff
property bool updateQmlTypeInfo: useInternalQbsProducts
Group { Group {
condition: !updateQmlTypeInfo
name: "qbs qml type info" name: "qbs qml type info"
qbs.install: true qbs.install: true
qbs.installDir: FileInfo.joinPaths(qtc.ide_data_path, "qtcreator", 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 { Rule {
condition: updateQmlTypeInfo condition: project.qbsSubModuleExists
inputsFromDependencies: ["qbs qml type descriptions", "qbs qml type bundle"] inputsFromDependencies: ["qbs qml type descriptions", "qbs qml type bundle"]
Artifact { Artifact {
filePath: "dummy." + input.fileName filePath: "dummy." + input.fileName

View File

@@ -93,7 +93,7 @@ const char XCODE_DEVELOPERPATH[] = "xcode.developerPath";
const char XCODE_SDK[] = "xcode.sdk"; const char XCODE_SDK[] = "xcode.sdk";
// Settings page // 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_TR_CATEGORY[] = QT_TRANSLATE_NOOP("QbsProjectManager", "Qbs");
const char QBS_SETTINGS_CATEGORY_ICON[] = ":/projectexplorer/images/build.png"; const char QBS_SETTINGS_CATEGORY_ICON[] = ":/projectexplorer/images/build.png";

View File

@@ -31,10 +31,11 @@
#include "qbsinstallstep.h" #include "qbsinstallstep.h"
#include "qbskitinformation.h" #include "qbskitinformation.h"
#include "qbsnodes.h" #include "qbsnodes.h"
#include "qbsprofilemanager.h"
#include "qbsprofilessettingspage.h" #include "qbsprofilessettingspage.h"
#include "qbsproject.h" #include "qbsproject.h"
#include "qbsprojectmanager.h"
#include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagerconstants.h"
#include "qbssettings.h"
#include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
@@ -85,12 +86,13 @@ static QbsProject *currentEditorProject()
class QbsProjectManagerPluginPrivate class QbsProjectManagerPluginPrivate
{ {
public: public:
QbsManager manager; QbsProfileManager manager;
QbsBuildConfigurationFactory buildConfigFactory; QbsBuildConfigurationFactory buildConfigFactory;
QbsBuildStepFactory buildStepFactory; QbsBuildStepFactory buildStepFactory;
QbsCleanStepFactory cleanStepFactory; QbsCleanStepFactory cleanStepFactory;
QbsInstallStepFactory installStepFactory; QbsInstallStepFactory installStepFactory;
QbsProfilesSettingsPage profilesSettingsPage; QbsSettingsPage settingsPage;
QbsProfilesSettingsPage profilesSetttingsPage;
QbsKitAspect qbsKitAspect; QbsKitAspect qbsKitAspect;
}; };
@@ -419,7 +421,7 @@ void QbsProjectManagerPlugin::runStepsForProductContextMenu(const QList<Core::Id
const auto * const productNode = dynamic_cast<const QbsProductNode *>(node); const auto * const productNode = dynamic_cast<const QbsProductNode *>(node);
QTC_ASSERT(productNode, return); QTC_ASSERT(productNode, return);
runStepsForProducts(project, {QbsProject::uniqueProductName(productNode->qbsProductData())}, runStepsForProducts(project, {productNode->productData().value("full-display-name").toString()},
{stepTypes}); {stepTypes});
} }
@@ -446,13 +448,13 @@ void QbsProjectManagerPlugin::runStepsForProduct(const QList<Core::Id> &stepType
Node *node = currentEditorNode(); Node *node = currentEditorNode();
if (!node) if (!node)
return; return;
auto product = dynamic_cast<QbsProductNode *>(node->parentProjectNode()); auto productNode = dynamic_cast<QbsProductNode *>(node->parentProjectNode());
if (!product) if (!productNode)
return; return;
QbsProject *project = currentEditorProject(); QbsProject *project = currentEditorProject();
if (!project) if (!project)
return; return;
runStepsForProducts(project, {QbsProject::uniqueProductName(product->qbsProductData())}, runStepsForProducts(project, {productNode->productData().value("full-display-name").toString()},
{stepTypes}); {stepTypes});
} }
@@ -485,9 +487,9 @@ void QbsProjectManagerPlugin::runStepsForSubprojectContextMenu(const QList<Core:
QTC_ASSERT(subProject, return); QTC_ASSERT(subProject, return);
QStringList toBuild; QStringList toBuild;
foreach (const qbs::ProductData &data, subProject->qbsProjectData().allProducts()) forAllProducts(subProject->projectData(), [&toBuild](const QJsonObject &data) {
toBuild << QbsProject::uniqueProductName(data); toBuild << data.value("full-display-name").toString();
});
runStepsForProducts(project, toBuild, {stepTypes}); runStepsForProducts(project, toBuild, {stepTypes});
} }

View File

@@ -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

View File

@@ -25,19 +25,16 @@
#include "qbsprojectparser.h" #include "qbsprojectparser.h"
#include "qbslogsink.h"
#include "qbsproject.h" #include "qbsproject.h"
#include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagerconstants.h"
#include "qbssettings.h"
#include <coreplugin/icore.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <qbs.h>
#include <QCoreApplication>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
#include <QFutureWatcher> #include <QFutureWatcher>
#include <QJsonArray>
using namespace Utils; using namespace Utils;
@@ -48,11 +45,11 @@ namespace Internal {
// QbsProjectParser: // 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_fi(fi)
{ {
m_project = buildSystem->qbsProject();
m_projectFilePath = buildSystem->projectFilePath().toString();
auto * const watcher = new QFutureWatcher<bool>(this); auto * const watcher = new QFutureWatcher<bool>(this);
connect(watcher, &QFutureWatcher<bool>::canceled, this, &QbsProjectParser::cancel); connect(watcher, &QFutureWatcher<bool>::canceled, this, &QbsProjectParser::cancel);
watcher->setFuture(fi->future()); watcher->setFuture(fi->future());
@@ -60,171 +57,77 @@ QbsProjectParser::QbsProjectParser(QbsBuildSystem *buildSystem, QFutureInterface
QbsProjectParser::~QbsProjectParser() QbsProjectParser::~QbsProjectParser()
{ {
const auto deleteJob = [this](qbs::AbstractJob *job) { if (m_session && m_parsing)
if (!job) m_session->cancelCurrentJob();
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);
m_fi = nullptr; // we do not own m_fi, do not delete m_fi = nullptr; // we do not own m_fi, do not delete
} }
void QbsProjectParser::parse(const QVariantMap &config, const Environment &env, const QString &dir, void QbsProjectParser::parse(const QVariantMap &config, const Environment &env, const QString &dir,
const QString &configName) const QString &configName)
{ {
QTC_ASSERT(!m_qbsSetupProjectJob, return); QTC_ASSERT(m_session, return);
QTC_ASSERT(!dir.isEmpty(), return); QTC_ASSERT(!dir.isEmpty(), return);
m_currentProgressBase = 0;
m_environment = env; m_environment = env;
QJsonObject request;
qbs::SetupProjectParameters params; request.insert("type", "resolve-project");
QVariantMap userConfig = config; QVariantMap userConfig = config;
QString specialKey = QLatin1String(Constants::QBS_CONFIG_PROFILE_KEY); request.insert("top-level-profile",
const QString profileName = userConfig.take(specialKey).toString(); userConfig.take(Constants::QBS_CONFIG_PROFILE_KEY).toString());
params.setTopLevelProfile(profileName); request.insert("configuration-name", configName);
params.setConfigurationName(configName); request.insert("force-probe-execution",
specialKey = QLatin1String(Constants::QBS_FORCE_PROBES_KEY); userConfig.take(Constants::QBS_FORCE_PROBES_KEY).toBool());
params.setForceProbeExecution(userConfig.take(specialKey).toBool()); if (QbsSettings::useCreatorSettingsDirForQbs())
params.setSettingsDirectory(QbsManager::settings()->baseDirectory()); request.insert("settings-directory", QbsSettings::qbsSettingsBaseDir());
params.setOverriddenValues(userConfig); 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. // so do not store the build graph if the build directory does not exist yet.
m_dryRun = !QFileInfo::exists(dir); request.insert("dry-run", !QFileInfo::exists(dir));
params.setDryRun(m_dryRun);
params.setBuildRoot(dir); request.insert("build-root", dir);
params.setProjectFilePath(m_projectFilePath); request.insert("project-file-path", m_projectFilePath);
params.setOverrideBuildGraphData(true); request.insert("override-build-graph-data", true);
params.setEnvironment(env.toProcessEnvironment()); static const auto envToJson = [](const Environment &env) {
const qbs::Preferences prefs(QbsManager::settings(), profileName); QJsonObject envObj;
params.setSearchPaths(prefs.searchPaths(resourcesBaseDirectory())); for (auto it = env.constBegin(); it != env.constEnd(); ++it) {
params.setPluginPaths(prefs.pluginPaths(pluginsBaseDirectory())); if (env.isEnabled(it))
params.setLibexecPath(libExecDirectory()); envObj.insert(env.key(it), env.value(it));
params.setProductErrorMode(qbs::ErrorHandlingMode::Relaxed); }
params.setPropertyCheckingMode(qbs::ErrorHandlingMode::Relaxed); return envObj;
params.setLogElapsedTime(!qEnvironmentVariableIsEmpty(Constants::QBS_PROFILING_ENV)); };
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_session, &QbsSession::projectResolved, this, [this](const ErrorInfo &error) {
m_error = error;
connect(m_qbsSetupProjectJob, &qbs::AbstractJob::finished, m_projectData = m_session->projectData();
this, &QbsProjectParser::handleQbsParsingDone); emit done(!m_error.hasError());
connect(m_qbsSetupProjectJob, &qbs::AbstractJob::taskStarted, });
this, &QbsProjectParser::handleQbsParsingTaskSetup); connect(m_session, &QbsSession::errorOccurred, this, [this] { emit done(false); });
connect(m_qbsSetupProjectJob, &qbs::AbstractJob::taskProgress, connect(m_session, &QbsSession::taskStarted, this,
this, &QbsProjectParser::handleQbsParsingProgress); [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() void QbsProjectParser::cancel()
{ {
QTC_ASSERT(m_qbsSetupProjectJob, return); if (m_session)
if (m_ruleExecutionJob) m_session->cancelCurrentJob();
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"));
} }
} // namespace Internal } // namespace Internal

View File

@@ -25,13 +25,14 @@
#pragma once #pragma once
#include "qbssession.h"
#include <utils/environment.h> #include <utils/environment.h>
#include <QFutureInterface> #include <QFutureInterface>
#include <QJsonObject>
#include <QObject> #include <QObject>
#include <qbs.h>
namespace QbsProjectManager { namespace QbsProjectManager {
namespace Internal { namespace Internal {
@@ -47,38 +48,24 @@ public:
void parse(const QVariantMap &config, const Utils::Environment &env, const QString &dir, void parse(const QVariantMap &config, const Utils::Environment &env, const QString &dir,
const QString &configName); const QString &configName);
void startRuleExecution();
void cancel(); void cancel();
Utils::Environment environment() const { return m_environment; } Utils::Environment environment() const { return m_environment; }
qbs::Project qbsProject() const; QbsSession *session() const { return m_session; }
qbs::ErrorInfo error(); QJsonObject projectData() const { return m_projectData; }
ErrorInfo error() const { return m_error; }
signals: signals:
void done(bool success); void done(bool success);
void ruleExecutionDone();
private: 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; Utils::Environment m_environment;
QString m_projectFilePath; const QString m_projectFilePath;
qbs::SetupProjectJob *m_qbsSetupProjectJob = nullptr; QbsSession * const m_session;
qbs::BuildJob *m_ruleExecutionJob = nullptr; ErrorInfo m_error;
qbs::ErrorInfo m_error; QJsonObject m_projectData;
qbs::Project m_project; bool m_parsing = false;
bool m_dryRun;
QFutureInterface<bool> *m_fi = nullptr; QFutureInterface<bool> *m_fi = nullptr;
int m_currentProgressBase = 0;
}; };
} // namespace Internal } // namespace Internal

View 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>

View 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

View 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

View File

@@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of Qt Creator. ** This file is part of Qt Creator.
@@ -25,32 +25,57 @@
#pragma once #pragma once
#include <coreplugin/dialogs/ioptionspage.h>
#include <utils/fileutils.h>
#include <QObject> #include <QObject>
#include <QPointer>
namespace QbsProjectManager { namespace QbsProjectManager {
namespace Internal { namespace Internal {
class QbsProjectManagerSettings : public QObject class QbsSettingsData {
public:
Utils::FilePath qbsExecutableFilePath;
bool useCreatorSettings = true;
};
class QbsSettings : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
static QbsProjectManagerSettings &instance(); static QbsSettings &instance();
static void setUseCreatorSettingsDirForQbs(bool useCreatorDir); static Utils::FilePath qbsExecutableFilePath();
static bool useCreatorSettingsDirForQbs(); static bool useCreatorSettingsDirForQbs();
static QString qbsSettingsBaseDir(); static QString qbsSettingsBaseDir();
static void writeSettings() { instance().doWriteSettings(); }
static void setSettingsData(const QbsSettingsData &settings);
signals: signals:
void settingsBaseChanged(); void settingsChanged();
private: private:
QbsProjectManagerSettings(); QbsSettings();
void loadSettings();
void storeSettings() const;
void readSettings(); QbsSettingsData m_settings;
void doWriteSettings(); };
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 } // namespace Internal

View File

@@ -17,14 +17,11 @@ Project {
] ]
property bool qbsSubModuleExists: File.exists(qbsProject.qbsBaseDir + "/qbs.qbs") 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 { Project {
name: "qbs project" name: "qbs project"
id: qbsProject id: qbsProject
property string qbsBaseDir: project.sharedSourcesDir + "/qbs" property string qbsBaseDir: project.sharedSourcesDir + "/qbs"
condition: qbsSubModuleExists && !useExternalQbs condition: qbsSubModuleExists
// The first entry is for overriding qbs' own qbsbuildconfig module. // The first entry is for overriding qbs' own qbsbuildconfig module.
qbsSearchPaths: [project.ide_source_tree + "/qbs", qbsBaseDir + "/qbs-resources"] qbsSearchPaths: [project.ide_source_tree + "/qbs", qbsBaseDir + "/qbs-resources"]