forked from qt-creator/qt-creator
Ios: Transform DeviceCtlRunner into recipe
Task-number: QTCREATORBUG-29168 Change-Id: Ib3b6d213986dfef3cbf53603bcd429595297016f Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
#include <utils/url.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
#include <solutions/tasking/barrier.h>
|
||||
#include <solutions/tasking/tasktree.h>
|
||||
|
||||
#include <QDateTime>
|
||||
@@ -141,24 +142,6 @@ private:
|
||||
QTimer m_pollTimer;
|
||||
};
|
||||
|
||||
class DeviceCtlRunner final : public DeviceCtlRunnerBase
|
||||
{
|
||||
public:
|
||||
DeviceCtlRunner(RunControl *runControl);
|
||||
|
||||
void stop();
|
||||
|
||||
void setStartStopped(bool startStopped) { m_startStopped = startStopped; }
|
||||
|
||||
private:
|
||||
GroupItem launchTask(const Storage<AppInfo> &appInfo);
|
||||
|
||||
Process m_process;
|
||||
std::unique_ptr<TemporaryFile> m_deviceCtlOutput;
|
||||
std::unique_ptr<TaskTree> m_processIdTask;
|
||||
bool m_startStopped = false;
|
||||
};
|
||||
|
||||
DeviceCtlRunnerBase::DeviceCtlRunnerBase(RunControl *runControl)
|
||||
: RunWorker(runControl)
|
||||
{
|
||||
@@ -449,29 +432,12 @@ void DeviceCtlPollingRunner::checkProcess()
|
||||
m_pollTask->start();
|
||||
}
|
||||
|
||||
DeviceCtlRunner::DeviceCtlRunner(RunControl *runControl)
|
||||
: DeviceCtlRunnerBase(runControl)
|
||||
{}
|
||||
|
||||
void DeviceCtlRunner::stop()
|
||||
static Group deviceCtlTask(RunControl *runControl, const Storage<AppInfo> &appInfo,
|
||||
const Storage<TemporaryFile> tempFileStorage, bool startStopped)
|
||||
{
|
||||
if (m_process.isRunning())
|
||||
m_process.stop();
|
||||
}
|
||||
|
||||
GroupItem DeviceCtlRunner::launchTask(const Storage<AppInfo> &appInfo)
|
||||
{
|
||||
const auto onSetup = [this, appInfo] {
|
||||
if (!m_device) {
|
||||
reportFailure(Tr::tr("Running failed. No iOS device found."));
|
||||
return false;
|
||||
}
|
||||
m_deviceCtlOutput.reset(new TemporaryFile("devicectl"));
|
||||
if (!m_deviceCtlOutput->open() || m_deviceCtlOutput->fileName().isEmpty()) {
|
||||
reportFailure(Tr::tr("Running failed. Failed to create the temporary output file."));
|
||||
return false;
|
||||
}
|
||||
const QStringList startStoppedArg = m_startStopped ? QStringList("--start-stopped")
|
||||
const auto launchApp = [runControl, appInfo, tempFileStorage, startStopped](const SingleBarrier &barrier) {
|
||||
const auto onSetup = [runControl, appInfo, tempFileStorage, startStopped, barrier](Process &process) {
|
||||
const QStringList startStoppedArg = startStopped ? QStringList("--start-stopped")
|
||||
: QStringList();
|
||||
const QStringList args = QStringList(
|
||||
{"devicectl",
|
||||
@@ -482,50 +448,86 @@ GroupItem DeviceCtlRunner::launchTask(const Storage<AppInfo> &appInfo)
|
||||
appInfo->device->uniqueInternalDeviceId(),
|
||||
"--quiet",
|
||||
"--json-output",
|
||||
m_deviceCtlOutput->fileName()})
|
||||
tempFileStorage->fileName()})
|
||||
+ startStoppedArg
|
||||
+ QStringList({"--console", appInfo->bundleIdentifier})
|
||||
+ appInfo->arguments;
|
||||
m_process.setCommand({FilePath::fromString("/usr/bin/xcrun"), args});
|
||||
connect(&m_process, &Process::started, this, [this, appInfoCopy = *appInfo] {
|
||||
// devicectl does report the process ID in its json output, but that is broken
|
||||
// for --console. When that is used, the json output is only written after the process
|
||||
// finished, which is not helpful.
|
||||
// Manually find the process ID for the bundle identifier.
|
||||
const Storage<AppInfo> appInfo{appInfoCopy};
|
||||
m_processIdTask.reset(new TaskTree(Group{
|
||||
sequential,
|
||||
appInfo,
|
||||
findApp(runControl(), appInfo),
|
||||
findProcess(runControl(), appInfo),
|
||||
onGroupDone([this, appInfo](DoneWith doneWith) {
|
||||
if (doneWith == DoneWith::Success) {
|
||||
runControl()->setAttachPid(ProcessHandle(appInfo->processIdentifier));
|
||||
reportStarted();
|
||||
} else {
|
||||
reportFailure(Tr::tr("Failed to retrieve process ID."));
|
||||
}
|
||||
})}));
|
||||
m_processIdTask->start();
|
||||
});
|
||||
connect(&m_process, &Process::done, this, [this] {
|
||||
if (m_process.error() != QProcess::UnknownError)
|
||||
reportFailure(Tr::tr("Failed to run devicectl: %1.").arg(m_process.errorString()));
|
||||
m_deviceCtlOutput->reset();
|
||||
m_processIdTask.reset();
|
||||
reportStoppedImpl();
|
||||
});
|
||||
connect(&m_process, &Process::readyReadStandardError, this, [this] {
|
||||
appendMessage(m_process.readAllStandardError(), StdErrFormat, false);
|
||||
});
|
||||
connect(&m_process, &Process::readyReadStandardOutput, this, [this] {
|
||||
appendMessage(m_process.readAllStandardOutput(), StdOutFormat, false);
|
||||
});
|
||||
process.setCommand({FilePath::fromString("/usr/bin/xcrun"), args});
|
||||
QObject::connect(&process, &Process::started, barrier->barrier(), &Barrier::advance);
|
||||
|
||||
m_process.start();
|
||||
QObject::connect(&process, &Process::readyReadStandardError, runControl,
|
||||
[runControl, process = &process] {
|
||||
runControl->postMessage(process->readAllStandardError(), StdErrFormat, false);
|
||||
});
|
||||
QObject::connect(&process, &Process::readyReadStandardOutput, runControl,
|
||||
[runControl, process = &process] {
|
||||
runControl->postMessage(process->readAllStandardOutput(), StdOutFormat, false);
|
||||
});
|
||||
QObject::connect(runStorage().activeStorage(), &RunInterface::canceled,
|
||||
&process, &Process::stop);
|
||||
};
|
||||
const auto onDone = [runControl, appInfo](const Process &process) {
|
||||
if (process.error() != QProcess::UnknownError) {
|
||||
runControl->postMessage(Tr::tr("Failed to run devicectl: %1.").arg(process.errorString()),
|
||||
ErrorMessageFormat);
|
||||
} else {
|
||||
runControl->postMessage(Tr::tr("\"%1\" exited.").arg(appInfo->bundlePath.toUserOutput()),
|
||||
NormalMessageFormat);
|
||||
}
|
||||
};
|
||||
|
||||
return ProcessTask(onSetup, onDone);
|
||||
};
|
||||
|
||||
const auto onDone = [runControl, appInfo](DoneWith result) {
|
||||
if (result == DoneWith::Success) {
|
||||
runControl->setAttachPid(ProcessHandle(appInfo->processIdentifier));
|
||||
emit runStorage()->started();
|
||||
} else {
|
||||
runControl->postMessage(Tr::tr("Failed to retrieve process ID."), ErrorMessageFormat);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
When (launchApp) >> Do {
|
||||
findApp(runControl, appInfo),
|
||||
findProcess(runControl, appInfo),
|
||||
onGroupDone(onDone)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Group deviceCtlRecipe(RunControl *runControl, bool startStopped)
|
||||
{
|
||||
const Storage<AppInfo> appInfo;
|
||||
const Storage<TemporaryFile> tempFileStorage{QString("devicectl")};
|
||||
|
||||
const auto onSetup = [runControl, appInfo, tempFileStorage] {
|
||||
if (!appInfo->device) {
|
||||
runControl->postMessage(Tr::tr("Running failed. No iOS device found."),
|
||||
ErrorMessageFormat);
|
||||
return false;
|
||||
}
|
||||
if (!tempFileStorage->open() || tempFileStorage->fileName().isEmpty()) {
|
||||
runControl->postMessage(Tr::tr("Running failed. Failed to create the temporary output file."),
|
||||
ErrorMessageFormat);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
return Sync(onSetup);
|
||||
|
||||
return {
|
||||
appInfo,
|
||||
tempFileStorage,
|
||||
Group {
|
||||
initSetup(runControl, appInfo),
|
||||
Sync(onSetup),
|
||||
findApp(runControl, appInfo),
|
||||
findProcess(runControl, appInfo),
|
||||
killProcess(appInfo)
|
||||
}.withCancel(canceler()),
|
||||
deviceCtlTask(runControl, appInfo, tempFileStorage, startStopped)
|
||||
};
|
||||
}
|
||||
|
||||
class IosRunner : public RunWorker
|
||||
@@ -793,19 +795,19 @@ static Result<FilePath> findDeviceSdk(IosDevice::ConstPtr dev)
|
||||
|
||||
IosRunWorkerFactory::IosRunWorkerFactory()
|
||||
{
|
||||
setProducer([](RunControl *control) -> RunWorker * {
|
||||
IosDevice::ConstPtr iosdevice = std::dynamic_pointer_cast<const IosDevice>(control->device());
|
||||
setProducer([](RunControl *runControl) -> RunWorker * {
|
||||
IosDevice::ConstPtr iosdevice = std::dynamic_pointer_cast<const IosDevice>(runControl->device());
|
||||
if (iosdevice && iosdevice->handler() == IosDevice::Handler::DeviceCtl) {
|
||||
if (IosDeviceManager::isDeviceCtlOutputSupported())
|
||||
return new DeviceCtlRunner(control);
|
||||
return new RecipeRunner(runControl, deviceCtlRecipe(runControl, /*startStopped=*/ false));
|
||||
// TODO Remove the polling runner when we decide not to support iOS 17+ devices
|
||||
// with Xcode < 16 at all
|
||||
return new DeviceCtlPollingRunner(control);
|
||||
return new DeviceCtlPollingRunner(runControl);
|
||||
}
|
||||
control->setIcon(Icons::RUN_SMALL_TOOLBAR);
|
||||
control->setDisplayName(QString("Run on %1")
|
||||
runControl->setIcon(Icons::RUN_SMALL_TOOLBAR);
|
||||
runControl->setDisplayName(QString("Run on %1")
|
||||
.arg(iosdevice ? iosdevice->displayName() : QString()));
|
||||
return new IosRunner(control);
|
||||
return new IosRunner(runControl);
|
||||
});
|
||||
addSupportedRunMode(ProjectExplorer::Constants::NORMAL_RUN_MODE);
|
||||
addSupportedRunConfig(Constants::IOS_RUNCONFIG_ID);
|
||||
@@ -866,7 +868,6 @@ static RunWorker *createWorker(RunControl *runControl)
|
||||
runControl->requestQmlChannel();
|
||||
|
||||
IosRunner *iosRunner = nullptr;
|
||||
DeviceCtlRunner *deviceCtlRunner = nullptr;
|
||||
RunWorker *runner = nullptr;
|
||||
if (!isIosDeviceInstance /*== simulator */ || dev->handler() == IosDevice::Handler::IosTool) {
|
||||
runner = iosRunner = new IosRunner(runControl);
|
||||
@@ -874,8 +875,7 @@ static RunWorker *createWorker(RunControl *runControl)
|
||||
iosRunner->setQmlDebugging(rp.isQmlDebugging() ? QmlDebuggerServices : NoQmlDebugServices);
|
||||
} else {
|
||||
QTC_CHECK(rp.isCppDebugging());
|
||||
runner = deviceCtlRunner = new DeviceCtlRunner(runControl);
|
||||
deviceCtlRunner->setStartStopped(true);
|
||||
runner = new RecipeRunner(runControl, deviceCtlRecipe(runControl, /*startStopped=*/ true));
|
||||
}
|
||||
|
||||
if (isIosDeviceInstance) {
|
||||
|
Reference in New Issue
Block a user