CMake: Allow to run staging installation in CMakeInstallStep

User configurable, on by default (only) for cases where build and run
device are different.

The staging dir is by default a randomly named directory on the build
device, but can be changed by the user if needed.

Overall, this does not change anything for a pure local setup (but
would let the user opt-in into staging, too)

Change-Id: Ic1c5fd1f1261e067692710c9e3aa9d821897478d
Reviewed-by: Cristian Adam <cristian.adam@qt.io>
This commit is contained in:
hjk
2023-04-05 14:16:00 +02:00
parent 388e516200
commit 4753b658bb
3 changed files with 111 additions and 5 deletions

View File

@@ -31,6 +31,7 @@
#include <utils/layoutbuilder.h> #include <utils/layoutbuilder.h>
#include <QListWidget> #include <QListWidget>
#include <QRandomGenerator>
#include <QRegularExpression> #include <QRegularExpression>
#include <QTreeView> #include <QTreeView>
#include <QCheckBox> #include <QCheckBox>
@@ -44,6 +45,8 @@ namespace CMakeProjectManager::Internal {
const char BUILD_TARGETS_KEY[] = "CMakeProjectManager.MakeStep.BuildTargets"; const char BUILD_TARGETS_KEY[] = "CMakeProjectManager.MakeStep.BuildTargets";
const char CMAKE_ARGUMENTS_KEY[] = "CMakeProjectManager.MakeStep.CMakeArguments"; const char CMAKE_ARGUMENTS_KEY[] = "CMakeProjectManager.MakeStep.CMakeArguments";
const char TOOL_ARGUMENTS_KEY[] = "CMakeProjectManager.MakeStep.AdditionalArguments"; const char TOOL_ARGUMENTS_KEY[] = "CMakeProjectManager.MakeStep.AdditionalArguments";
const char USE_STAGING_KEY[] = "CMakeProjectManager.MakeStep.UseStaging";
const char STAGING_DIR_KEY[] = "CMakeProjectManager.MakeStep.StagingDir";
const char IOS_AUTOMATIC_PROVISIONG_UPDATES_ARGUMENTS_KEY[] = const char IOS_AUTOMATIC_PROVISIONG_UPDATES_ARGUMENTS_KEY[] =
"CMakeProjectManager.MakeStep.iOSAutomaticProvisioningUpdates"; "CMakeProjectManager.MakeStep.iOSAutomaticProvisioningUpdates";
const char CLEAR_SYSTEM_ENVIRONMENT_KEY[] = "CMakeProjectManager.MakeStep.ClearSystemEnvironment"; const char CLEAR_SYSTEM_ENVIRONMENT_KEY[] = "CMakeProjectManager.MakeStep.ClearSystemEnvironment";
@@ -156,7 +159,25 @@ Qt::ItemFlags CMakeTargetItem::flags(int) const
// CMakeBuildStep // CMakeBuildStep
CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Utils::Id id) : static QString initialStagingDir()
{
// Avoid actual file accesses.
auto rg = QRandomGenerator::global();
const qulonglong rand = rg->generate64();
char buf[sizeof(rand)];
memcpy(&buf, &rand, sizeof(rand));
const QByteArray ba = QByteArray(buf, sizeof(buf)).toHex();
return QString::fromUtf8("/tmp/Qt-Creator-staging-" + ba);
}
static bool buildAndRunOnSameDevice(Kit *kit)
{
IDeviceConstPtr runDevice = DeviceKitAspect::device(kit);
IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(kit);
return runDevice->id() == buildDevice->id();
}
CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Id id) :
CMakeAbstractProcessStep(bsl, id) CMakeAbstractProcessStep(bsl, id)
{ {
m_cmakeArguments = addAspect<StringAspect>(); m_cmakeArguments = addAspect<StringAspect>();
@@ -169,6 +190,17 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Utils::Id id) :
m_toolArguments->setLabelText(Tr::tr("Tool arguments:")); m_toolArguments->setLabelText(Tr::tr("Tool arguments:"));
m_toolArguments->setDisplayStyle(StringAspect::LineEditDisplay); m_toolArguments->setDisplayStyle(StringAspect::LineEditDisplay);
m_useStaging = addAspect<BoolAspect>();
m_useStaging->setSettingsKey(USE_STAGING_KEY);
m_useStaging->setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
m_useStaging->setDefaultValue(!buildAndRunOnSameDevice(kit()));
m_stagingDir = addAspect<StringAspect>();
m_stagingDir->setSettingsKey(STAGING_DIR_KEY);
m_stagingDir->setLabelText(Tr::tr("Staging directory:"));
m_stagingDir->setDisplayStyle(StringAspect::PathChooserDisplay);
m_stagingDir->setDefaultValue(initialStagingDir());
Kit *kit = buildConfiguration()->kit(); Kit *kit = buildConfiguration()->kit();
if (CMakeBuildConfiguration::isIos(kit)) { if (CMakeBuildConfiguration::isIos(kit)) {
m_useiOSAutomaticProvisioningUpdates = addAspect<BoolAspect>(); m_useiOSAutomaticProvisioningUpdates = addAspect<BoolAspect>();
@@ -199,6 +231,9 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Utils::Id id) :
if (!env.expandedValueForKey("NINJA_STATUS").startsWith(ninjaProgressString)) if (!env.expandedValueForKey("NINJA_STATUS").startsWith(ninjaProgressString))
env.set("NINJA_STATUS", ninjaProgressString + "%o/sec] "); env.set("NINJA_STATUS", ninjaProgressString + "%o/sec] ");
env.modify(m_userEnvironmentChanges); env.modify(m_userEnvironmentChanges);
if (m_useStaging)
env.set("DESTDIR", currentStagingDir());
}); });
connect(target(), &Target::parsingFinished, this, [this](bool success) { connect(target(), &Target::parsingFinished, this, [this](bool success) {
@@ -210,7 +245,6 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Utils::Id id) :
this, &CMakeBuildStep::updateBuildTargetsModel); this, &CMakeBuildStep::updateBuildTargetsModel);
} }
QVariantMap CMakeBuildStep::toMap() const QVariantMap CMakeBuildStep::toMap() const
{ {
QVariantMap map(CMakeAbstractProcessStep::toMap()); QVariantMap map(CMakeAbstractProcessStep::toMap());
@@ -375,9 +409,7 @@ void CMakeBuildStep::setBuildTargets(const QStringList &buildTargets)
CommandLine CMakeBuildStep::cmakeCommand() const CommandLine CMakeBuildStep::cmakeCommand() const
{ {
CommandLine cmd; CommandLine cmd{cmakeExecutable()};
if (CMakeTool *tool = CMakeKitAspect::cmakeTool(kit()))
cmd.setExecutable(tool->cmakeExecutable());
FilePath buildDirectory = "."; FilePath buildDirectory = ".";
if (buildConfiguration()) if (buildConfiguration())
@@ -406,6 +438,9 @@ CommandLine CMakeBuildStep::cmakeCommand() const
if (!m_cmakeArguments->value().isEmpty()) if (!m_cmakeArguments->value().isEmpty())
cmd.addArgs(m_cmakeArguments->value(), CommandLine::Raw); cmd.addArgs(m_cmakeArguments->value(), CommandLine::Raw);
if (m_useStaging->value())
cmd.addArg("install");
bool toolArgumentsSpecified = false; bool toolArgumentsSpecified = false;
if (!m_toolArguments->value().isEmpty()) { if (!m_toolArguments->value().isEmpty()) {
cmd.addArg("--"); cmd.addArg("--");
@@ -466,6 +501,12 @@ QWidget *CMakeBuildStep::createConfigWidget()
QString summaryText = param.summary(displayName()); QString summaryText = param.summary(displayName());
m_stagingDir->setEnabled(m_useStaging->value());
if (m_useStaging->value()) {
summaryText.append(" " + Tr::tr("and stage at %2 for %3")
.arg(currentStagingDir(), currentInstallPrefix()));
}
if (!m_buildPreset.isEmpty()) { if (!m_buildPreset.isEmpty()) {
const CMakeProject *cp = static_cast<const CMakeProject *>(project()); const CMakeProject *cp = static_cast<const CMakeProject *>(project());
@@ -526,6 +567,7 @@ QWidget *CMakeBuildStep::createConfigWidget()
Layouting::Form builder; Layouting::Form builder;
builder.addRow(m_cmakeArguments); builder.addRow(m_cmakeArguments);
builder.addRow(m_toolArguments); builder.addRow(m_toolArguments);
builder.addRow({Tr::tr("Stage for installation:"), Layouting::Row{m_useStaging, m_stagingDir}});
if (m_useiOSAutomaticProvisioningUpdates) if (m_useiOSAutomaticProvisioningUpdates)
builder.addRow(m_useiOSAutomaticProvisioningUpdates); builder.addRow(m_useiOSAutomaticProvisioningUpdates);
@@ -541,6 +583,8 @@ QWidget *CMakeBuildStep::createConfigWidget()
connect(m_cmakeArguments, &StringAspect::changed, this, updateDetails); connect(m_cmakeArguments, &StringAspect::changed, this, updateDetails);
connect(m_toolArguments, &StringAspect::changed, this, updateDetails); connect(m_toolArguments, &StringAspect::changed, this, updateDetails);
connect(m_useStaging, &BoolAspect::changed, this, updateDetails);
connect(m_stagingDir, &StringAspect::changed, this, updateDetails);
if (m_useiOSAutomaticProvisioningUpdates) if (m_useiOSAutomaticProvisioningUpdates)
connect(m_useiOSAutomaticProvisioningUpdates, &BoolAspect::changed, this, updateDetails); connect(m_useiOSAutomaticProvisioningUpdates, &BoolAspect::changed, this, updateDetails);
@@ -683,8 +727,62 @@ QString CMakeBuildStep::baseEnvironmentText() const
return Tr::tr("System Environment"); return Tr::tr("System Environment");
} }
QString CMakeBuildStep::currentInstallPrefix() const
{
auto bs = qobject_cast<CMakeBuildSystem *>(buildSystem());
QTC_ASSERT(bs, return {});
const CMakeConfig config = bs->configurationFromCMake();
return QString::fromUtf8(config.valueOf("CMAKE_INSTALL_PREFIX"));
}
QString CMakeBuildStep::currentStagingDir() const
{
return m_stagingDir->filePath().path();
}
FilePath CMakeBuildStep::cmakeExecutable() const
{
CMakeTool *tool = CMakeKitAspect::cmakeTool(kit());
return tool ? tool->cmakeExecutable() : FilePath();
}
void CMakeBuildStep::updateDeploymentData()
{
if (!m_useStaging->value())
return;
QString install = currentInstallPrefix();
QString stagingDir = currentStagingDir();
FilePath rootDir = cmakeExecutable().withNewPath(stagingDir);
Q_UNUSED(install);
DeploymentData deploymentData;
deploymentData.setLocalInstallRoot(rootDir);
const int startPos = rootDir.path().length();
const auto appFileNames = transform<QSet<QString>>(buildSystem()->applicationTargets(),
[](const BuildTargetInfo &appTarget) { return appTarget.targetFilePath.fileName(); });
auto handleFile = [this, &appFileNames, startPos, &deploymentData](const FilePath &filePath) {
const DeployableFile::Type type = appFileNames.contains(filePath.fileName())
? DeployableFile::TypeExecutable
: DeployableFile::TypeNormal;
const QString targetDir = filePath.parentDir().path().mid(startPos);
deploymentData.addFile(filePath, targetDir, type);
return IterationPolicy::Continue;
};
rootDir.iterateDirectory(handleFile,
{{}, QDir::Files | QDir::Hidden, QDirIterator::Subdirectories});
buildSystem()->setDeploymentData(deploymentData);
}
void CMakeBuildStep::finish(ProcessResult result) void CMakeBuildStep::finish(ProcessResult result)
{ {
updateDeploymentData();
emit progress(100, {}); emit progress(100, {});
AbstractProcessStep::finish(result); AbstractProcessStep::finish(result);
} }

View File

@@ -85,6 +85,10 @@ private:
void doRun() override; void doRun() override;
QWidget *createConfigWidget() override; QWidget *createConfigWidget() override;
Utils::FilePath cmakeExecutable() const;
QString currentInstallPrefix() const;
QString currentStagingDir() const;
QString defaultBuildTarget() const; QString defaultBuildTarget() const;
bool isCleanStep() const; bool isCleanStep() const;
@@ -94,6 +98,7 @@ private:
void handleBuildTargetsChanges(bool success); void handleBuildTargetsChanges(bool success);
void recreateBuildTargetsModel(); void recreateBuildTargetsModel();
void updateBuildTargetsModel(); void updateBuildTargetsModel();
void updateDeploymentData();
QMetaObject::Connection m_runTrigger; QMetaObject::Connection m_runTrigger;
@@ -102,6 +107,8 @@ private:
Utils::StringAspect *m_cmakeArguments = nullptr; Utils::StringAspect *m_cmakeArguments = nullptr;
Utils::StringAspect *m_toolArguments = nullptr; Utils::StringAspect *m_toolArguments = nullptr;
Utils::BoolAspect *m_useiOSAutomaticProvisioningUpdates = nullptr; Utils::BoolAspect *m_useiOSAutomaticProvisioningUpdates = nullptr;
Utils::BoolAspect *m_useStaging = nullptr;
Utils::StringAspect *m_stagingDir = nullptr;
QString m_allTarget = "all"; QString m_allTarget = "all";
QString m_installTarget = "install"; QString m_installTarget = "install";

View File

@@ -93,6 +93,7 @@ void CMakeInstallStep::finish(ProcessResult result)
emit progress(100, {}); emit progress(100, {});
AbstractProcessStep::finish(result); AbstractProcessStep::finish(result);
} }
QWidget *CMakeInstallStep::createConfigWidget() QWidget *CMakeInstallStep::createConfigWidget()
{ {
auto updateDetails = [this] { auto updateDetails = [this] {