Files
qt-creator/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp
hjk 3c41b91d44 ProjectExplorer: Settle on fixed set of build step lists
Even if the implementation allowed different lists than 'build'
and 'clean', that's the only set that has been used, ever.

If really needed, this could be re-instated, but for now having
them fixed removes part of the neeed for a two-phase construction
and helps to simplify user code.

Change-Id: I3df09a1829a7d020ef8963d358ea80f8d199ba13
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2019-12-06 14:49:59 +00:00

475 lines
16 KiB
C++

/****************************************************************************
**
** 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 "qbsbuildconfiguration.h"
#include "qbsbuildstep.h"
#include "qbscleanstep.h"
#include "qbsinstallstep.h"
#include "qbsproject.h"
#include "qbsprojectmanagerconstants.h"
#include "qbssettings.h"
#include <coreplugin/documentmanager.h>
#include <projectexplorer/buildinfo.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/deployconfiguration.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmacroexpander.h>
#include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h>
#include <qtsupport/qtkitinformation.h>
#include <utils/mimetypes/mimedatabase.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <QCoreApplication>
#include <QCryptographicHash>
using namespace ProjectExplorer;
using namespace Utils;
namespace QbsProjectManager {
namespace Internal {
static FilePath defaultBuildDirectory(const FilePath &projectFilePath, const Kit *k,
const QString &bcName,
BuildConfiguration::BuildType buildType)
{
const QString projectName = projectFilePath.toFileInfo().completeBaseName();
ProjectMacroExpander expander(projectFilePath, projectName, k, bcName, buildType);
FilePath projectDir = Project::projectDirectory(projectFilePath);
QString buildPath = expander.expand(ProjectExplorerPlugin::buildDirectoryTemplate());
return projectDir.resolvePath(buildPath);
}
// ---------------------------------------------------------------------------
// QbsBuildConfiguration:
// ---------------------------------------------------------------------------
QbsBuildConfiguration::QbsBuildConfiguration(Target *target, Core::Id id)
: BuildConfiguration(target, id)
{
setConfigWidgetHasFrame(true);
m_configurationName = addAspect<BaseStringAspect>();
m_configurationName->setLabelText(tr("Configuration name:"));
m_configurationName->setSettingsKey("Qbs.configName");
m_configurationName->setDisplayStyle(BaseStringAspect::LineEditDisplay);
connect(m_configurationName, &BaseStringAspect::changed,
this, &BuildConfiguration::buildDirectoryChanged);
const auto separateDebugInfoAspect = addAspect<SeparateDebugInfoAspect>();
connect(separateDebugInfoAspect, &SeparateDebugInfoAspect::changed,
this, &QbsBuildConfiguration::qbsConfigurationChanged);
const auto qmlDebuggingAspect = addAspect<QtSupport::QmlDebuggingAspect>();
qmlDebuggingAspect->setKit(target->kit());
connect(qmlDebuggingAspect, &QtSupport::QmlDebuggingAspect::changed,
this, &QbsBuildConfiguration::qbsConfigurationChanged);
const auto qtQuickCompilerAspect = addAspect<QtSupport::QtQuickCompilerAspect>();
qtQuickCompilerAspect->setKit(target->kit());
connect(qtQuickCompilerAspect, &QtSupport::QtQuickCompilerAspect::changed,
this, &QbsBuildConfiguration::qbsConfigurationChanged);
connect(this, &BuildConfiguration::environmentChanged,
this, &QbsBuildConfiguration::triggerReparseIfActive);
connect(this, &BuildConfiguration::buildDirectoryChanged,
this, &QbsBuildConfiguration::triggerReparseIfActive);
connect(this, &QbsBuildConfiguration::qbsConfigurationChanged,
this, &QbsBuildConfiguration::triggerReparseIfActive);
macroExpander()->registerVariable("CurrentBuild:QbsBuildRoot", tr("The qbs project build root"),
[this] { return buildDirectory().pathAppended(configurationName()).toUserOutput(); });
m_buildSystem = new QbsBuildSystem(this);
}
QbsBuildConfiguration::~QbsBuildConfiguration()
{
delete m_buildSystem;
}
BuildSystem *QbsBuildConfiguration::buildSystem() const
{
return m_buildSystem;
}
void QbsBuildConfiguration::initialize()
{
QVariantMap configData = extraInfo().value<QVariantMap>();
configData.insert(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY),
(initialBuildType() == BuildConfiguration::Debug)
? QLatin1String(Constants::QBS_VARIANT_DEBUG)
: QLatin1String(Constants::QBS_VARIANT_RELEASE));
Utils::FilePath buildDir = initialBuildDirectory();
if (buildDir.isEmpty())
buildDir = defaultBuildDirectory(target()->project()->projectFilePath(),
target()->kit(), initialDisplayName(),
initialBuildType());
setBuildDirectory(buildDir);
// Add the build configuration.
QVariantMap bd = configData;
QString configName = bd.take("configName").toString();
if (configName.isEmpty()) {
configName = "qtc_" + target()->kit()->fileSystemFriendlyName() + '_'
+ Utils::FileUtils::fileSystemFriendlyName(initialDisplayName());
}
const Kit *kit = target()->kit();
const QString kitName = kit->displayName();
const QByteArray kitHash = QCryptographicHash::hash(kitName.toUtf8(), QCryptographicHash::Sha1);
const QString uniqueConfigName = configName
+ '_' + kit->fileSystemFriendlyName().left(8)
+ '_' + kitHash.toHex().left(16);
m_configurationName->setValue(uniqueConfigName);
auto bs = new QbsBuildStep(buildSteps());
bs->setQbsConfiguration(bd);
buildSteps()->appendStep(bs);
cleanSteps()->appendStep(Constants::QBS_CLEANSTEP_ID);
emit qbsConfigurationChanged();
}
void QbsBuildConfiguration::triggerReparseIfActive()
{
if (isActive())
m_buildSystem->delayParsing();
}
bool QbsBuildConfiguration::fromMap(const QVariantMap &map)
{
if (!BuildConfiguration::fromMap(map))
return false;
if (m_configurationName->value().isEmpty()) { // pre-4.4 backwards compatibility
const QString profileName = QbsProfileManager::profileNameForKit(target()->kit());
const QString buildVariant = qbsConfiguration()
.value(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)).toString();
m_configurationName->setValue(profileName + '-' + buildVariant);
}
return true;
}
void QbsBuildConfiguration::restrictNextBuild(const RunConfiguration *rc)
{
if (!rc) {
setProducts({});
return;
}
const auto productNode = dynamic_cast<QbsProductNode *>(rc->productNode());
QTC_ASSERT(productNode, return);
setProducts({productNode->fullDisplayName()});
}
QbsBuildStep *QbsBuildConfiguration::qbsStep() const
{
return buildSteps()->firstOfType<QbsBuildStep>();
}
QVariantMap QbsBuildConfiguration::qbsConfiguration() const
{
QVariantMap config;
QbsBuildStep *qbsBs = qbsStep();
if (qbsBs)
config = qbsBs->qbsConfiguration(QbsBuildStep::ExpandVariables);
return config;
}
BuildConfiguration::BuildType QbsBuildConfiguration::buildType() const
{
QString variant;
if (qbsStep())
variant = qbsStep()->buildVariant();
if (variant == QLatin1String(Constants::QBS_VARIANT_DEBUG))
return Debug;
if (variant == QLatin1String(Constants::QBS_VARIANT_RELEASE))
return Release;
return Unknown;
}
void QbsBuildConfiguration::setChangedFiles(const QStringList &files)
{
m_changedFiles = files;
}
QStringList QbsBuildConfiguration::changedFiles() const
{
return m_changedFiles;
}
void QbsBuildConfiguration::setActiveFileTags(const QStringList &fileTags)
{
m_activeFileTags = fileTags;
}
QStringList QbsBuildConfiguration::activeFileTags() const
{
return m_activeFileTags;
}
void QbsBuildConfiguration::setProducts(const QStringList &products)
{
m_products = products;
}
QStringList QbsBuildConfiguration::products() const
{
return m_products;
}
QString QbsBuildConfiguration::configurationName() const
{
return m_configurationName->value();
}
class StepProxy
{
public:
StepProxy(const BuildStep *buildStep)
: m_qbsBuildStep(qobject_cast<const QbsBuildStep *>(buildStep))
, m_qbsCleanStep(qobject_cast<const QbsCleanStep *>(buildStep))
, m_qbsInstallStep(qobject_cast<const QbsInstallStep *>(buildStep))
{
}
QString command() const {
if (m_qbsBuildStep)
return QLatin1String("build");
if (m_qbsInstallStep)
return QLatin1String("install");
return QLatin1String("clean");
}
bool dryRun() const {
if (m_qbsBuildStep)
return false;
if (m_qbsInstallStep)
return m_qbsInstallStep->dryRun();
return m_qbsCleanStep->dryRun();
}
bool keepGoing() const {
if (m_qbsBuildStep)
return m_qbsBuildStep->keepGoing();
if (m_qbsInstallStep)
return m_qbsInstallStep->keepGoing();
return m_qbsCleanStep->keepGoing();
}
bool forceProbeExecution() const { return m_qbsBuildStep && m_qbsBuildStep->forceProbes(); }
bool showCommandLines() const {
return m_qbsBuildStep ? m_qbsBuildStep->showCommandLines() : false;
}
bool noInstall() const {
return m_qbsBuildStep ? !m_qbsBuildStep->install() : false;
}
bool noBuild() const { return m_qbsInstallStep; }
bool cleanInstallRoot() const {
if (m_qbsBuildStep)
return m_qbsBuildStep->cleanInstallRoot();
if (m_qbsInstallStep)
return m_qbsInstallStep->removeFirst();
return false;
}
int jobCount() const {
return m_qbsBuildStep ? m_qbsBuildStep->maxJobs() : 0;
}
Utils::FilePath installRoot() const {
const QbsBuildStep *bs = nullptr;
if (m_qbsBuildStep) {
bs = m_qbsBuildStep;
} else if (m_qbsInstallStep) {
bs = static_cast<QbsBuildConfiguration *>(m_qbsInstallStep->deployConfiguration()
->target()->activeBuildConfiguration())->qbsStep();
}
if (bs)
return bs->installRoot();
return Utils::FilePath();
}
private:
const QbsBuildStep * const m_qbsBuildStep;
const QbsCleanStep * const m_qbsCleanStep;
const QbsInstallStep * const m_qbsInstallStep;
};
QString QbsBuildConfiguration::equivalentCommandLine(const BuildStep *buildStep) const
{
CommandLine commandLine;
const QString qbsInstallDir = QString::fromLocal8Bit(qgetenv("QBS_INSTALL_DIR"));
const QString qbsFilePath = HostOsInfo::withExecutableSuffix(!qbsInstallDir.isEmpty()
? qbsInstallDir + QLatin1String("/bin/qbs")
: QCoreApplication::applicationDirPath() + QLatin1String("/qbs"));
commandLine.addArg(QDir::toNativeSeparators(qbsFilePath));
const StepProxy stepProxy(buildStep);
commandLine.addArg(stepProxy.command());
const QString buildDir = buildDirectory().toUserOutput();
commandLine.addArgs({"-d", buildDir});
commandLine.addArgs({"-f", buildStep->project()->projectFilePath().toUserOutput()});
if (QbsSettings::useCreatorSettingsDirForQbs()) {
commandLine.addArgs({"--settings-dir",
QDir::toNativeSeparators(QbsSettings::qbsSettingsBaseDir())});
}
if (stepProxy.dryRun())
commandLine.addArg("--dry-run");
if (stepProxy.keepGoing())
commandLine.addArg("--keep-going");
if (stepProxy.forceProbeExecution())
commandLine.addArg("--force-probe-execution");
if (stepProxy.showCommandLines())
commandLine.addArgs({"--command-echo-mode", "command-line"});
if (stepProxy.noInstall())
commandLine.addArg("--no-install");
if (stepProxy.noBuild())
commandLine.addArg("--no-build");
if (stepProxy.cleanInstallRoot())
commandLine.addArg("--clean-install-root");
const int jobCount = stepProxy.jobCount();
if (jobCount > 0)
commandLine.addArgs({"--jobs", QString::number(jobCount)});
const QString profileName = QbsProfileManager::profileNameForKit(buildStep->target()->kit());
const QString buildVariant = qbsConfiguration()
.value(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)).toString();
commandLine.addArg("config:" + configurationName());
commandLine.addArg(QString(Constants::QBS_CONFIG_VARIANT_KEY) + ':' + buildVariant);
const FilePath installRoot = stepProxy.installRoot();
if (!installRoot.isEmpty()) {
commandLine.addArg(QString(Constants::QBS_INSTALL_ROOT_KEY) + ':' + installRoot.toUserOutput());
if (qobject_cast<const QbsInstallStep *>(buildStep))
commandLine.addArgs({"--installRoot", installRoot.toUserOutput()});
}
commandLine.addArg("profile:" + profileName);
return commandLine.arguments();
}
bool QbsBuildConfiguration::isQmlDebuggingEnabled() const
{
return qmlDebuggingSetting() == TriState::Enabled;
}
TriState QbsBuildConfiguration::qmlDebuggingSetting() const
{
return aspect<QtSupport::QmlDebuggingAspect>()->setting();
}
TriState QbsBuildConfiguration::qtQuickCompilerSetting() const
{
return aspect<QtSupport::QtQuickCompilerAspect>()->setting();
}
TriState QbsBuildConfiguration::separateDebugInfoSetting() const
{
return aspect<SeparateDebugInfoAspect>()->setting();
}
// ---------------------------------------------------------------------------
// QbsBuildConfigurationFactory:
// ---------------------------------------------------------------------------
QbsBuildConfigurationFactory::QbsBuildConfigurationFactory()
{
registerBuildConfiguration<QbsBuildConfiguration>(Constants::QBS_BC_ID);
setSupportedProjectType(Constants::PROJECT_ID);
setSupportedProjectMimeTypeName(Constants::MIME_TYPE);
setIssueReporter([](Kit *k, const QString &projectPath, const QString &buildDir) -> Tasks {
const QtSupport::BaseQtVersion * const version = QtSupport::QtKitAspect::qtVersion(k);
return version ? version->reportIssues(projectPath, buildDir)
: Tasks();
});
}
QList<BuildInfo> QbsBuildConfigurationFactory::availableBuilds(const Kit *k, const FilePath &projectPath, bool forSetup) const
{
QList<BuildInfo> result;
if (forSetup) {
BuildInfo info = createBuildInfo(k, BuildConfiguration::Debug);
//: The name of the debug build configuration created by default for a qbs project.
info.displayName = tr("Debug");
//: Non-ASCII characters in directory suffix may cause build issues.
info.buildDirectory
= defaultBuildDirectory(projectPath, k, tr("Debug", "Shadow build directory suffix"),
info.buildType);
result << info;
info = createBuildInfo(k, BuildConfiguration::Release);
//: The name of the release build configuration created by default for a qbs project.
info.displayName = tr("Release");
//: Non-ASCII characters in directory suffix may cause build issues.
info.buildDirectory
= defaultBuildDirectory(projectPath, k, tr("Release", "Shadow build directory suffix"),
info.buildType);
result << info;
} else {
result << createBuildInfo(k, BuildConfiguration::Debug);
}
return result;
}
BuildInfo QbsBuildConfigurationFactory::createBuildInfo(const Kit *k,
BuildConfiguration::BuildType type) const
{
BuildInfo info(this);
info.kitId = k->id();
info.buildType = type;
info.typeName = tr("Build");
QVariantMap config;
config.insert("configName", type == BuildConfiguration::Debug ? "Debug" : "Release");
info.extraInfo = config;
return info;
}
} // namespace Internal
} // namespace QbsProjectManager