// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "cmakebuildstep.h" #include "cmakeautogenparser.h" #include "cmakebuildconfiguration.h" #include "cmakebuildsystem.h" #include "cmakekitaspect.h" #include "cmakeoutputparser.h" #include "cmakeproject.h" #include "cmakeprojectconstants.h" #include "cmakeprojectmanagertr.h" #include "cmaketool.h" #include "cmaketoolmanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Core; using namespace ProjectExplorer; using namespace Tasking; using namespace Utils; namespace CMakeProjectManager::Internal { const char BUILD_TARGETS_KEY[] = "CMakeProjectManager.MakeStep.BuildTargets"; const char CMAKE_ARGUMENTS_KEY[] = "CMakeProjectManager.MakeStep.CMakeArguments"; 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[] = "CMakeProjectManager.MakeStep.iOSAutomaticProvisioningUpdates"; const char CLEAR_SYSTEM_ENVIRONMENT_KEY[] = "CMakeProjectManager.MakeStep.ClearSystemEnvironment"; const char USER_ENVIRONMENT_CHANGES_KEY[] = "CMakeProjectManager.MakeStep.UserEnvironmentChanges"; const char BUILD_PRESET_KEY[] = "CMakeProjectManager.MakeStep.BuildPreset"; class ProjectParserTaskAdapter final : public TaskAdapter> { public: void start() final { BuildSystem *bs = *task(); if (!bs) { emit done(DoneResult::Error); return; } connect(bs, &BuildSystem::parsingFinished, this, [this](bool success) { emit done(toDoneResult(success)); }); } }; using ProjectParserTask = CustomTask; class CMakeProgressParser : public Utils::OutputLineParser { Q_OBJECT signals: void progress(int percentage); private: Result handleLine(const QString &line, Utils::OutputFormat format) override { if (format != Utils::StdOutFormat) return Status::NotHandled; static const QRegularExpression percentProgress("^\\[\\s*(\\d*)%\\]"); static const QRegularExpression ninjaProgress("^\\[\\s*(\\d*)/\\s*(\\d*)"); QRegularExpressionMatch match = percentProgress.match(line); if (match.hasMatch()) { bool ok = false; const int percent = match.captured(1).toInt(&ok); if (ok) emit progress(percent); return Status::Done; } match = ninjaProgress.match(line); if (match.hasMatch()) { m_useNinja = true; bool ok = false; const int done = match.captured(1).toInt(&ok); if (ok) { const int all = match.captured(2).toInt(&ok); if (ok && all != 0) { const int percent = static_cast(100.0 * done / all); emit progress(percent); } } return Status::Done; } return Status::NotHandled; } bool hasDetectedRedirection() const override { return m_useNinja; } // TODO: Shouldn't we know the backend in advance? Then we could merge this class // with CmakeParser. bool m_useNinja = false; }; // CmakeTargetItem CMakeTargetItem::CMakeTargetItem(const QString &target, CMakeBuildStep *step, bool special) : m_target(target), m_step(step), m_special(special) { } QVariant CMakeTargetItem::data(int column, int role) const { if (column == 0) { if (role == Qt::DisplayRole) { if (m_target.isEmpty()) return Tr::tr("Current executable"); return m_target; } if (role == Qt::ToolTipRole) { if (m_target.isEmpty()) { return Tr::tr("Build the executable used in the active run " "configuration. Currently: %1") .arg(m_step->activeRunConfigTarget()); } return Tr::tr("Target: %1").arg(m_target); } if (role == Qt::CheckStateRole) return m_step->buildsBuildTarget(m_target) ? Qt::Checked : Qt::Unchecked; if (role == Qt::FontRole) { if (m_special) { QFont italics; italics.setItalic(true); return italics; } } } return QVariant(); } bool CMakeTargetItem::setData(int column, const QVariant &data, int role) { if (column == 0 && role == Qt::CheckStateRole) { m_step->setBuildsBuildTarget(m_target, data.value() == Qt::Checked); return true; } return TreeItem::setData(column, data, role); } Qt::ItemFlags CMakeTargetItem::flags(int) const { return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; } // CMakeBuildStep static QString initialStagingDir(Kit *kit) { // 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(); IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(kit); if (buildDevice && buildDevice->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) return TemporaryDirectory::masterDirectoryPath() + "/staging-" + ba; return QString::fromUtf8("/tmp/Qt-Creator-staging-" + ba); } static bool supportsStageForInstallation(const Kit *kit) { IDeviceConstPtr runDevice = RunDeviceKitAspect::device(kit); Id runDeviceType = RunDeviceTypeKitAspect::deviceTypeId(kit); IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(kit); QTC_ASSERT(runDeviceType.isValid(), return false); QTC_ASSERT(buildDevice, return false); return (!runDevice || runDevice->id() != buildDevice->id()) && runDeviceType != Android::Constants::ANDROID_DEVICE_TYPE && runDeviceType != Ios::Constants::IOS_DEVICE_TYPE && runDeviceType != Ios::Constants::IOS_SIMULATOR_TYPE && runDeviceType != BareMetal::Constants::BareMetalOsType && runDeviceType != WebAssembly::Constants::WEBASSEMBLY_DEVICE_TYPE; } CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Id id) : CMakeAbstractProcessStep(bsl, id) { cmakeArguments.setSettingsKey(CMAKE_ARGUMENTS_KEY); cmakeArguments.setLabelText(Tr::tr("CMake arguments:")); cmakeArguments.setDisplayStyle(StringAspect::LineEditDisplay); toolArguments.setSettingsKey(TOOL_ARGUMENTS_KEY); toolArguments.setLabelText(Tr::tr("Tool arguments:")); toolArguments.setDisplayStyle(StringAspect::LineEditDisplay); useStaging.setSettingsKey(USE_STAGING_KEY); useStaging.setLabel(Tr::tr("Stage for installation"), BoolAspect::LabelPlacement::AtCheckBox); useStaging.setDefaultValue(supportsStageForInstallation(kit()) && !isCleanStep()); useStaging.setEnabled(!isCleanStep()); stagingDir.setSettingsKey(STAGING_DIR_KEY); stagingDir.setLabelText(Tr::tr("Staging directory:")); stagingDir.setDefaultValue(initialStagingDir(kit())); stagingDir.setExpectedKind(PathChooser::Kind::Directory); Kit *kit = this->kit(); if (CMakeBuildConfiguration::isIos(kit) && CMakeGeneratorKitAspect::generator(kit) == "Xcode") { useiOSAutomaticProvisioningUpdates.setDefaultValue(true); useiOSAutomaticProvisioningUpdates.setSettingsKey( IOS_AUTOMATIC_PROVISIONG_UPDATES_ARGUMENTS_KEY); useiOSAutomaticProvisioningUpdates.setLabel( Tr::tr("Enable automatic provisioning updates:")); useiOSAutomaticProvisioningUpdates.setToolTip( Tr::tr("Tells xcodebuild to create and download a provisioning profile " "if a valid one does not exist.")); } else { useiOSAutomaticProvisioningUpdates.setVisible(false); } m_buildTargetModel.setHeader({Tr::tr("Target")}); setBuildTargets({defaultBuildTarget()}); auto *bs = qobject_cast(buildSystem()); if (bs && !bs->buildTargets().isEmpty()) recreateBuildTargetsModel(); setLowPriority(); setCommandLineProvider([this] { return cmakeCommand(); }); setEnvironmentModifier([this](Environment &env) { const QString ninjaProgressString = "[%f/%t "; // ninja: [33/100 env.setupEnglishOutput(); if (!env.expandedValueForKey("NINJA_STATUS").startsWith(ninjaProgressString)) env.set("NINJA_STATUS", ninjaProgressString + "%o/sec] "); env.modify(m_userEnvironmentChanges); env.setFallback("CLICOLOR_FORCE", "1"); if (useStaging()) env.set("DESTDIR", stagingDir().path()); }); connect(buildSystem(), &BuildSystem::parsingFinished, this, [this](bool success) { if (success) // Do not change when parsing failed. recreateBuildTargetsModel(); }); connect(buildConfiguration(), &BuildConfiguration::activeRunConfigurationChanged, this, &CMakeBuildStep::updateBuildTargetsModel); } void CMakeBuildStep::toMap(Utils::Store &map) const { CMakeAbstractProcessStep::toMap(map); map.insert(BUILD_TARGETS_KEY, m_buildTargets); map.insert(CLEAR_SYSTEM_ENVIRONMENT_KEY, m_clearSystemEnvironment); map.insert(USER_ENVIRONMENT_CHANGES_KEY, EnvironmentItem::toStringList(m_userEnvironmentChanges)); map.insert(BUILD_PRESET_KEY, m_buildPreset); } void CMakeBuildStep::fromMap(const Utils::Store &map) { setBuildTargets(map.value(BUILD_TARGETS_KEY).toStringList()); m_clearSystemEnvironment = map.value(CLEAR_SYSTEM_ENVIRONMENT_KEY).toBool(); m_userEnvironmentChanges = EnvironmentItem::fromStringList( map.value(USER_ENVIRONMENT_CHANGES_KEY).toStringList()); updateAndEmitEnvironmentChanged(); m_buildPreset = map.value(BUILD_PRESET_KEY).toString(); BuildStep::fromMap(map); } bool CMakeBuildStep::init() { if (!CMakeAbstractProcessStep::init()) return false; if (m_buildTargets.contains(QString())) { RunConfiguration *rc = buildConfiguration()->activeRunConfiguration(); if (!rc || rc->buildKey().isEmpty()) { emit addTask(BuildSystemTask(Task::Error, ::ProjectExplorer::Tr::tr( "You asked to build the current Run Configuration's build target only, " "but it is not associated with a build target. " "Update the Make Step in your build settings."))); emitFaultyConfigurationMessage(); return false; } } setIgnoreReturnValue(m_buildTargets == QStringList(CMakeBuildStep::cleanTarget())); return true; } void CMakeBuildStep::setupOutputFormatter(Utils::OutputFormatter *formatter) { CMakeOutputParser *cmakeOutputParser = new CMakeOutputParser; CMakeProgressParser * const progressParser = new CMakeProgressParser; connect(progressParser, &CMakeProgressParser::progress, this, [this](int percent) { emit progress(percent, {}); }); formatter->addLineParser(progressParser); cmakeOutputParser->setSourceDirectory(project()->projectDirectory()); formatter->addLineParsers({new CMakeAutogenParser, cmakeOutputParser, new GnuMakeParser}); Toolchain *tc = ToolchainKitAspect::cxxToolchain(kit()); OutputTaskParser *xcodeBuildParser = nullptr; if (tc && tc->targetAbi().os() == Abi::DarwinOS) { xcodeBuildParser = new XcodebuildParser; formatter->addLineParser(xcodeBuildParser); progressParser->setRedirectionDetector(xcodeBuildParser); } const QList additionalParsers = kit()->createOutputParsers(); for (Utils::OutputLineParser * const p : additionalParsers) p->setRedirectionDetector(progressParser); formatter->addLineParsers(additionalParsers); formatter->addSearchDir(processParameters()->effectiveWorkingDirectory()); CMakeAbstractProcessStep::setupOutputFormatter(formatter); } GroupItem CMakeBuildStep::runRecipe() { const auto onParserSetup = [this](QPointer &buildSystem) { // Make sure CMake state was written to disk before trying to build: auto bs = qobject_cast(this->buildSystem()); QTC_ASSERT(bs, return SetupResult::StopWithError); QString message; if (bs->persistCMakeState()) message = Tr::tr("Persisting CMake state..."); else if (bs->isWaitingForParse()) message = Tr::tr("Running CMake in preparation to build..."); else return SetupResult::StopWithSuccess; emit addOutput(message, OutputFormat::NormalMessage); buildSystem = bs; return SetupResult::Continue; }; const auto onParserError = [this] { emit addOutput(Tr::tr("Project did not parse successfully, cannot build."), OutputFormat::ErrorMessage); }; Group root { ignoreReturnValue() ? finishAllAndSuccess : stopOnError, ProjectParserTask(onParserSetup, onParserError, CallDoneIf::Error), defaultProcessTask(), onGroupDone([this] { updateDeploymentData(); }) }; return root; } QString CMakeBuildStep::defaultBuildTarget() const { const BuildStepList *const bsl = stepList(); QTC_ASSERT(bsl, return {}); const Utils::Id parentId = bsl->id(); if (parentId == ProjectExplorer::Constants::BUILDSTEPS_CLEAN) return cleanTarget(); if (parentId == ProjectExplorer::Constants::BUILDSTEPS_DEPLOY) return installTarget(); return allTarget(); } bool CMakeBuildStep::isCleanStep() const { const BuildStepList *const bsl = stepList(); QTC_ASSERT(bsl, return false); const Utils::Id parentId = bsl->id(); return parentId == ProjectExplorer::Constants::BUILDSTEPS_CLEAN; } QStringList CMakeBuildStep::buildTargets() const { return m_buildTargets; } bool CMakeBuildStep::buildsBuildTarget(const QString &target) const { return m_buildTargets.contains(target); } void CMakeBuildStep::setBuildsBuildTarget(const QString &target, bool on) { QStringList targets = m_buildTargets; if (on && !m_buildTargets.contains(target)) targets.append(target); if (!on) targets.removeAll(target); setBuildTargets(targets); } void CMakeBuildStep::setBuildTargets(const QStringList &buildTargets) { if (buildTargets.isEmpty()) m_buildTargets = QStringList(defaultBuildTarget()); else m_buildTargets = buildTargets; updateBuildTargetsModel(); } CommandLine CMakeBuildStep::cmakeCommand() const { CommandLine cmd{cmakeExecutable()}; FilePath buildDirectory = "."; Project *project = nullptr; if (buildConfiguration()) { buildDirectory = buildConfiguration()->buildDirectory(); project = buildConfiguration()->project(); } auto bs = qobject_cast(buildSystem()); const bool hasSubprojectBuild = bs && bs->hasSubprojectBuildSupport(); bool ninjaSubprojectClean = false; // Subprojects have subdir/ structure if (m_buildTargets.size() == 1 && m_buildTargets.front().contains("/") && hasSubprojectBuild) { QString target = m_buildTargets.front(); const auto separator = target.lastIndexOf("/"); const QString path = target.left(separator); const QString operation = target.mid(separator + 1); if (bs->cmakeGenerator().contains("Makefiles")) { cmd.addArgs( {"--build", CMakeToolManager::mappedFilePath(project, buildDirectory.pathAppended(path)) .path()}); cmd.addArg("--target"); cmd.addArg(operation); } else { cmd.addArgs( {"--build", CMakeToolManager::mappedFilePath(project, buildDirectory).path()}); cmd.addArg("--target"); if (operation == "clean") { target = path + "/" + "all"; ninjaSubprojectClean = true; } cmd.addArg(target); } } else { cmd.addArgs({"--build", CMakeToolManager::mappedFilePath(project, buildDirectory).path()}); cmd.addArg("--target"); cmd.addArgs(Utils::transform(m_buildTargets, [this](const QString &s) { if (s.isEmpty()) { if (RunConfiguration *rc = buildConfiguration()->activeRunConfiguration()) return rc->buildKey(); } return s; })); } if (useStaging()) cmd.addArg("install"); if (bs && bs->isMultiConfigReader()) { cmd.addArg("--config"); if (m_configuration) cmd.addArg(*m_configuration); else cmd.addArg(bs->cmakeBuildType()); } cmd.addArgs(cmakeArguments(), CommandLine::Raw); bool toolArgumentsSpecified = false; if (!toolArguments().isEmpty()) { cmd.addArg("--"); cmd.addArgs(toolArguments(), CommandLine::Raw); toolArgumentsSpecified = true; } if (useiOSAutomaticProvisioningUpdates()) { // Only add the double dash if it wasn't added before. if (!toolArgumentsSpecified) cmd.addArg("--"); cmd.addArgs("-allowProvisioningUpdates", CommandLine::Raw); } if (ninjaSubprojectClean) { if (!toolArgumentsSpecified) cmd.addArg("--"); cmd.addArgs({"-t", "clean"}); } return cmd; } QString CMakeBuildStep::cleanTarget() const { return QString("clean"); } QString CMakeBuildStep::allTarget() const { return m_allTarget; } QString CMakeBuildStep::installTarget() const { return m_installTarget; } QStringList CMakeBuildStep::specialTargets(bool allCapsTargets) { if (!allCapsTargets) return {"all", "clean", "install", "install/strip", "package", "test"}; else return {"ALL_BUILD", "clean", "INSTALL", "PACKAGE", "RUN_TESTS"}; } QString CMakeBuildStep::activeRunConfigTarget() const { RunConfiguration *rc = buildConfiguration()->activeRunConfiguration(); return rc ? rc->buildKey() : QString(); } void CMakeBuildStep::setBuildPreset(const QString &preset) { m_buildPreset = preset; } QWidget *CMakeBuildStep::createConfigWidget() { auto updateDetails = [this] { const bool haveCleanTarget = m_buildTargets.contains(cleanTarget()); useStaging.setEnabled(!haveCleanTarget); if (useStaging() && haveCleanTarget) useStaging.setValue(false); ProcessParameters param; setupProcessParameters(¶m); param.setCommandLine(cmakeCommand()); QString summaryText = param.summary(displayName()); stagingDir.setEnabled(useStaging()); if (useStaging()) { //: Stage (for installation) at for summaryText.append( "; " + Tr::tr("Stage at %2 for %3").arg(stagingDir().path(), currentInstallPrefix())); } if (!m_buildPreset.isEmpty()) { const CMakeProject *cp = static_cast(project()); const auto buildPresets = cp->presetsData().buildPresets; const PresetsDetails::BuildPreset preset = Utils::findOrDefault(buildPresets, [this](const PresetsDetails::BuildPreset &bp) { return bp.name == m_buildPreset; }); const QString presetDisplayName = preset.displayName ? *preset.displayName : preset.name; if (!presetDisplayName.isEmpty()) summaryText.append(QString("
Preset: %1").arg(presetDisplayName)); } setSummaryText(summaryText); }; setDisplayName(Tr::tr("Build", "ConfigWidget display name.")); auto buildTargetsView = new QTreeView; buildTargetsView->setMinimumHeight(200); buildTargetsView->setModel(&m_buildTargetModel); buildTargetsView->setRootIsDecorated(false); buildTargetsView->setHeaderHidden(true); auto frame = ItemViewFind::createSearchableWrapper(buildTargetsView, ItemViewFind::LightColored); auto createAndAddEnvironmentWidgets = [this](Layouting::Form &builder) { auto clearBox = new QCheckBox(Tr::tr("Clear system environment")); clearBox->setChecked(useClearEnvironment()); auto envWidget = new EnvironmentWidget(nullptr, EnvironmentWidget::TypeLocal, clearBox); envWidget->setBaseEnvironment(baseEnvironment()); envWidget->setBaseEnvironmentText(baseEnvironmentText()); envWidget->setUserChanges(userEnvironmentChanges()); connect(envWidget, &EnvironmentWidget::userChangesChanged, this, [this, envWidget] { setUserEnvironmentChanges(envWidget->userChanges()); }); connect(clearBox, &QAbstractButton::toggled, this, [this, envWidget](bool checked) { setUseClearEnvironment(checked); envWidget->setBaseEnvironment(baseEnvironment()); envWidget->setBaseEnvironmentText(baseEnvironmentText()); }); connect(this, &CMakeBuildStep::environmentChanged, this, [this, envWidget] { envWidget->setBaseEnvironment(baseEnvironment()); envWidget->setBaseEnvironmentText(baseEnvironmentText()); }); builder.addRow({clearBox}); builder.addRow({envWidget}); }; Layouting::Form builder; builder.addRow({cmakeArguments}); builder.addRow({toolArguments}); builder.addRow({useStaging}); builder.addRow({stagingDir}); builder.addRow({useiOSAutomaticProvisioningUpdates}); builder.addRow({new QLabel(Tr::tr("Targets:")), frame}); if (!isCleanStep() && !m_buildPreset.isEmpty()) createAndAddEnvironmentWidgets(builder); builder.setNoMargins(); auto widget = builder.emerge(); updateDetails(); cmakeArguments.addOnChanged(this, updateDetails); toolArguments.addOnChanged(this, updateDetails); useStaging.addOnChanged(this, updateDetails); stagingDir.addOnChanged(this, updateDetails); useiOSAutomaticProvisioningUpdates.addOnChanged(this, updateDetails); connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged, this, updateDetails); connect(buildConfiguration(), &BuildConfiguration::environmentChanged, this, updateDetails); connect(this, &CMakeBuildStep::buildTargetsChanged, widget, updateDetails); return widget; } void CMakeBuildStep::recreateBuildTargetsModel() { auto addItem = [this](const QString &target, bool special = false) { auto item = new CMakeTargetItem(target, this, special); m_buildTargetModel.rootItem()->appendChild(item); }; m_buildTargetModel.clear(); auto bs = qobject_cast(buildSystem()); QStringList targetList = bs ? bs->buildTargetTitles() : QStringList(); bool usesAllCapsTargets = bs ? bs->usesAllCapsTargets() : false; if (usesAllCapsTargets) { m_allTarget = "ALL_BUILD"; m_installTarget = "INSTALL"; int idx = m_buildTargets.indexOf(QString("all")); if (idx != -1) m_buildTargets[idx] = QString("ALL_BUILD"); idx = m_buildTargets.indexOf(QString("install")); if (idx != -1) m_buildTargets[idx] = QString("INSTALL"); } targetList.removeDuplicates(); addItem(QString(), true); // Remove the targets that do not exist in the build system // This can result when selected targets get renamed if (!targetList.empty()) { Utils::erase(m_buildTargets, [targetList](const QString &bt) { return !bt.isEmpty() /* "current executable" */ && !targetList.contains(bt); }); if (m_buildTargets.empty()) m_buildTargets.push_back(m_allTarget); } for (const QString &buildTarget : std::as_const(targetList)) addItem(buildTarget, specialTargets(usesAllCapsTargets).contains(buildTarget)); updateBuildTargetsModel(); } void CMakeBuildStep::updateBuildTargetsModel() { emit m_buildTargetModel.layoutChanged(); emit buildTargetsChanged(); } void CMakeBuildStep::setConfiguration(const QString &configuration) { m_configuration = configuration; } void CMakeBuildStep::setToolArguments(const QStringList &nativeToolArguments) { toolArguments.setValue(nativeToolArguments.join(" ")); } void CMakeBuildStep::setCMakeArguments(const QStringList &cmakeArgs) { cmakeArguments.setValue(cmakeArgs.join(" ")); } Environment CMakeBuildStep::environment() const { return m_environment; } void CMakeBuildStep::setUserEnvironmentChanges(const Utils::EnvironmentItems &diff) { if (m_userEnvironmentChanges == diff) return; m_userEnvironmentChanges = diff; updateAndEmitEnvironmentChanged(); } EnvironmentItems CMakeBuildStep::userEnvironmentChanges() const { return m_userEnvironmentChanges; } bool CMakeBuildStep::useClearEnvironment() const { return m_clearSystemEnvironment; } void CMakeBuildStep::setUseClearEnvironment(bool b) { if (useClearEnvironment() == b) return; m_clearSystemEnvironment = b; updateAndEmitEnvironmentChanged(); } void CMakeBuildStep::updateAndEmitEnvironmentChanged() { Environment env = baseEnvironment(); env.modify(userEnvironmentChanges()); if (env == m_environment) return; m_environment = env; emit environmentChanged(); } Environment CMakeBuildStep::baseEnvironment() const { Environment result; if (!useClearEnvironment()) { ProjectExplorer::IDevice::ConstPtr devicePtr = BuildDeviceKitAspect::device(kit()); result = devicePtr ? devicePtr->systemEnvironment() : Environment::systemEnvironment(); } if (buildConfiguration()) buildConfiguration()->addToEnvironment(result); kit()->addToBuildEnvironment(result); result.modify(project()->additionalEnvironment()); return result; } QString CMakeBuildStep::baseEnvironmentText() const { if (useClearEnvironment()) return Tr::tr("Clean Environment"); else return Tr::tr("System Environment"); } QString CMakeBuildStep::currentInstallPrefix() const { auto bs = qobject_cast(buildSystem()); QTC_ASSERT(bs, return {}); const CMakeConfig config = bs->configurationFromCMake(); return QString::fromUtf8(config.valueOf("CMAKE_INSTALL_PREFIX")); } FilePath CMakeBuildStep::cmakeExecutable() const { CMakeTool *tool = CMakeKitAspect::cmakeTool(kit()); return tool ? tool->cmakeExecutable() : FilePath(); } void CMakeBuildStep::updateDeploymentData() { if (!useStaging()) return; QString install = currentInstallPrefix(); FilePath rootDir = cmakeExecutable().withNewPath(stagingDir().path()); Q_UNUSED(install) DeploymentData deploymentData; deploymentData.setLocalInstallRoot(rootDir); IDeviceConstPtr runDevice = RunDeviceKitAspect::device(buildSystem()->kit()); if (!runDevice) return; const auto appFileNames = transform>(buildSystem()->applicationTargets(), [](const BuildTargetInfo &appTarget) { return appTarget.targetFilePath.fileName(); }); auto handleFile = [&appFileNames, rootDir, &deploymentData, runDevice](const FilePath &filePath) { const DeployableFile::Type type = appFileNames.contains(filePath.fileName()) ? DeployableFile::TypeExecutable : DeployableFile::TypeNormal; FilePath targetDirPath = filePath.parentDir().relativePathFromDir(rootDir); const FilePath targetDir = runDevice->rootPath().pathAppended(targetDirPath.path()); deploymentData.addFile(filePath, targetDir.nativePath(), type); return IterationPolicy::Continue; }; rootDir.iterateDirectory(handleFile, {{}, QDir::Files | QDir::Hidden, QDirIterator::Subdirectories}); buildSystem()->setDeploymentData(deploymentData); } // CMakeBuildStepFactory class CMakeBuildStepFactory final : public BuildStepFactory { public: CMakeBuildStepFactory() { registerStep(Constants::CMAKE_BUILD_STEP_ID); setDisplayName(Tr::tr("CMake Build", "Display name for CMakeProjectManager::CMakeBuildStep id.")); setSupportedProjectType(Constants::CMAKE_PROJECT_ID); } }; void setupCMakeBuildStep() { static CMakeBuildStepFactory theCMakeBuildStepFactory; } } // CMakeProjectManager::Internal #include