forked from qt-creator/qt-creator
iOS: Avoid iostool for info gathering if possible
Previously we had a small workaround for retrieving the state of developer mode for iOS 17 devices integrated into our own iostool. Instead use devicectl for gathering device information for devices that it can handle, and only fall back to iostool for the devices that it cannot handle. Since iostool cannot handle deployment, running, and debugging for iOS 17 devices anyway, the end goal would be to only use devicectl for these. Also add a TaskTree wrapper for IosToolHandler for convenience. Change-Id: I5bcd09eb354c2dce9b21e62e140de16f2e740d6e Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
@@ -16,8 +16,13 @@
|
||||
#include <projectexplorer/kitaspects.h>
|
||||
|
||||
#include <utils/portlist.h>
|
||||
#include <utils/process.h>
|
||||
|
||||
#include <solutions/tasking/tasktree.h>
|
||||
|
||||
#include <QFormLayout>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
|
||||
@@ -72,7 +77,14 @@ static QString CFStringRef2QString(CFStringRef s)
|
||||
namespace Ios::Internal {
|
||||
|
||||
const char kDeviceName[] = "deviceName";
|
||||
const char kDeveloperStatus[] = "developerStatus";
|
||||
const char kDeviceConnected[] = "deviceConnected";
|
||||
const char kOsVersion[] = "osVersion";
|
||||
const char kCpuArchitecture[] = "cpuArchitecture";
|
||||
const char kUniqueDeviceId[] = "uniqueDeviceId";
|
||||
const char vOff[] = "*off*";
|
||||
const char vDevelopment[] = "Development";
|
||||
const char vYes[] = "YES";
|
||||
|
||||
class IosDeviceInfoWidget : public IDeviceWidget
|
||||
{
|
||||
@@ -168,12 +180,12 @@ QString IosDevice::name()
|
||||
|
||||
QString IosDevice::osVersion() const
|
||||
{
|
||||
return m_extraInfo.value(QLatin1String("osVersion"));
|
||||
return m_extraInfo.value(kOsVersion);
|
||||
}
|
||||
|
||||
QString IosDevice::cpuArchitecture() const
|
||||
{
|
||||
return m_extraInfo.value("cpuArchitecture");
|
||||
return m_extraInfo.value(kCpuArchitecture);
|
||||
}
|
||||
|
||||
Utils::Port IosDevice::nextPort() const
|
||||
@@ -194,13 +206,12 @@ IosDeviceManager::TranslationMap IosDeviceManager::translationMap()
|
||||
TranslationMap &tMap = *new TranslationMap;
|
||||
tMap[kDeviceName] = Tr::tr("Device name");
|
||||
//: Whether the device is in developer mode.
|
||||
tMap[QLatin1String("developerStatus")] = Tr::tr("Developer status");
|
||||
tMap[QLatin1String("deviceConnected")] = Tr::tr("Connected");
|
||||
tMap[QLatin1String("YES")] = Tr::tr("yes");
|
||||
tMap[kDeveloperStatus] = Tr::tr("Developer status");
|
||||
tMap[kDeviceConnected] = Tr::tr("Connected");
|
||||
tMap[vYes] = Tr::tr("yes");
|
||||
tMap[QLatin1String("NO")] = Tr::tr("no");
|
||||
tMap[QLatin1String("YES")] = Tr::tr("yes");
|
||||
tMap[QLatin1String("*unknown*")] = Tr::tr("unknown");
|
||||
tMap[QLatin1String("osVersion")] = Tr::tr("OS version");
|
||||
tMap[kOsVersion] = Tr::tr("OS version");
|
||||
translationMap = &tMap;
|
||||
return tMap;
|
||||
}
|
||||
@@ -253,12 +264,61 @@ void IosDeviceManager::deviceDisconnected(const QString &uid)
|
||||
|
||||
void IosDeviceManager::updateInfo(const QString &devId)
|
||||
{
|
||||
IosToolHandler *requester = new IosToolHandler(IosDeviceType(IosDeviceType::IosDevice), this);
|
||||
connect(requester, &IosToolHandler::deviceInfo,
|
||||
this, &IosDeviceManager::deviceInfo, Qt::QueuedConnection);
|
||||
connect(requester, &IosToolHandler::finished,
|
||||
this, &IosDeviceManager::infoGathererFinished);
|
||||
requester->requestDeviceInfo(devId);
|
||||
using namespace Tasking;
|
||||
|
||||
const auto infoFromDeviceCtl = ProcessTask(
|
||||
[](Process &process) {
|
||||
process.setCommand({FilePath::fromString("/usr/bin/xcrun"),
|
||||
{"devicectl", "list", "devices", "--quiet", "--json-output", "-"}});
|
||||
},
|
||||
[this, devId](const Process &process, DoneWith) -> DoneResult {
|
||||
auto jsonOutput = QJsonDocument::fromJson(process.rawStdOut());
|
||||
// find device
|
||||
const QJsonArray deviceList = jsonOutput["result"]["devices"].toArray();
|
||||
for (const QJsonValue &device : deviceList) {
|
||||
const QString udid = device["hardwareProperties"]["udid"].toString();
|
||||
// USB identifiers don't have dashes, but iOS device udids can. Remove.
|
||||
if (QString(udid).remove('-') == devId) {
|
||||
// fill in the map that we use for the iostool data
|
||||
QMap<QString, QString> info;
|
||||
info[kDeviceName] = device["deviceProperties"]["name"].toString();
|
||||
info[kDeveloperStatus] = QLatin1String(
|
||||
device["deviceProperties"]["developerModeStatus"] == "enabled"
|
||||
? vDevelopment
|
||||
: vOff);
|
||||
info[kDeviceConnected] = vYes; // that's the assumption
|
||||
info[kOsVersion]
|
||||
= QLatin1String("%1 (%2)")
|
||||
.arg(device["deviceProperties"]["osVersionNumber"].toString(),
|
||||
device["deviceProperties"]["osBuildUpdate"].toString());
|
||||
info[kCpuArchitecture]
|
||||
= device["hardwareProperties"]["cpuType"]["name"].toString();
|
||||
info[kUniqueDeviceId] = udid;
|
||||
deviceInfo(nullptr, devId, info);
|
||||
return DoneResult::Success;
|
||||
}
|
||||
}
|
||||
// device not found, not handled by devicectl
|
||||
return DoneResult::Error;
|
||||
},
|
||||
CallDoneIf::Success);
|
||||
|
||||
const auto infoFromIosTool = IosToolTask([this, devId](IosToolRunner &runner) {
|
||||
runner.setDeviceType(IosDeviceType::IosDevice);
|
||||
runner.setStartHandler([this, devId](IosToolHandler *handler) {
|
||||
connect(handler,
|
||||
&IosToolHandler::deviceInfo,
|
||||
this,
|
||||
&IosDeviceManager::deviceInfo,
|
||||
Qt::QueuedConnection);
|
||||
handler->requestDeviceInfo(devId);
|
||||
});
|
||||
});
|
||||
|
||||
const Group root{sequential, stopOnSuccess, infoFromDeviceCtl, infoFromIosTool};
|
||||
auto task = new TaskTree(root);
|
||||
connect(task, &TaskTree::done, task, [task] { task->deleteLater(); });
|
||||
task->start();
|
||||
}
|
||||
|
||||
void IosDeviceManager::deviceInfo(IosToolHandler *, const QString &uid,
|
||||
@@ -294,14 +354,14 @@ void IosDeviceManager::deviceInfo(IosToolHandler *, const QString &uid,
|
||||
QLatin1String devStatusKey = QLatin1String("developerStatus");
|
||||
if (info.contains(devStatusKey)) {
|
||||
QString devStatus = info.value(devStatusKey);
|
||||
if (devStatus == QLatin1String("Development")) {
|
||||
if (devStatus == vDevelopment) {
|
||||
devManager->setDeviceState(newDev->id(), IDevice::DeviceReadyToUse);
|
||||
m_userModeDeviceIds.removeOne(uid);
|
||||
} else {
|
||||
devManager->setDeviceState(newDev->id(), IDevice::DeviceConnected);
|
||||
bool shouldIgnore = newDev->m_ignoreDevice;
|
||||
newDev->m_ignoreDevice = true;
|
||||
if (devStatus == QLatin1String("*off*")) {
|
||||
if (devStatus == vOff) {
|
||||
if (!shouldIgnore && !IosConfigurations::ignoreAllDevices()) {
|
||||
QMessageBox mBox;
|
||||
mBox.setText(Tr::tr("An iOS device in user mode has been detected."));
|
||||
@@ -331,11 +391,6 @@ void IosDeviceManager::deviceInfo(IosToolHandler *, const QString &uid,
|
||||
}
|
||||
}
|
||||
|
||||
void IosDeviceManager::infoGathererFinished(IosToolHandler *gatherer)
|
||||
{
|
||||
gatherer->deleteLater();
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
namespace {
|
||||
io_iterator_t gAddedIter;
|
||||
|
||||
@@ -75,7 +75,6 @@ public:
|
||||
void updateInfo(const QString &devId);
|
||||
void deviceInfo(Ios::IosToolHandler *gatherer, const QString &deviceId,
|
||||
const Ios::IosToolHandler::Dict &info);
|
||||
void infoGathererFinished(Ios::IosToolHandler *gatherer);
|
||||
void monitorAvailableDevices();
|
||||
|
||||
private:
|
||||
|
||||
@@ -973,6 +973,28 @@ bool IosToolHandler::isRunning() const
|
||||
return d->isRunning();
|
||||
}
|
||||
|
||||
void IosToolRunner::setStartHandler(const StartHandler &startHandler)
|
||||
{
|
||||
m_startHandler = startHandler;
|
||||
}
|
||||
|
||||
void IosToolRunner::setDeviceType(const Internal::IosDeviceType &type)
|
||||
{
|
||||
m_deviceType = type;
|
||||
}
|
||||
|
||||
IosToolTaskAdapter::IosToolTaskAdapter() {}
|
||||
|
||||
void IosToolTaskAdapter::start()
|
||||
{
|
||||
task()->m_iosToolHandler = new IosToolHandler(Internal::IosDeviceType(task()->m_deviceType));
|
||||
connect(task()->m_iosToolHandler, &IosToolHandler::finished, this, [this] {
|
||||
task()->m_iosToolHandler->deleteLater();
|
||||
emit done(Tasking::DoneResult::Success);
|
||||
});
|
||||
task()->m_startHandler(task()->m_iosToolHandler);
|
||||
}
|
||||
|
||||
} // namespace Ios
|
||||
|
||||
#include "iostoolhandler.moc"
|
||||
|
||||
@@ -3,9 +3,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "iossimulator.h"
|
||||
|
||||
#include <utils/filepath.h>
|
||||
#include <utils/port.h>
|
||||
|
||||
#include <solutions/tasking/tasktree.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
@@ -67,4 +71,29 @@ private:
|
||||
Ios::Internal::IosToolHandlerPrivate *d;
|
||||
};
|
||||
|
||||
// for Tasking:
|
||||
|
||||
class IosToolRunner
|
||||
{
|
||||
public:
|
||||
using StartHandler = std::function<void(IosToolHandler *)>;
|
||||
void setStartHandler(const StartHandler &startHandler);
|
||||
void setDeviceType(const Internal::IosDeviceType &type);
|
||||
|
||||
private:
|
||||
friend class IosToolTaskAdapter;
|
||||
IosToolHandler *m_iosToolHandler = nullptr;
|
||||
StartHandler m_startHandler;
|
||||
Internal::IosDeviceType m_deviceType = Internal::IosDeviceType::IosDevice;
|
||||
};
|
||||
|
||||
class IosToolTaskAdapter : public Tasking::TaskAdapter<IosToolRunner>
|
||||
{
|
||||
public:
|
||||
IosToolTaskAdapter();
|
||||
void start() final;
|
||||
};
|
||||
|
||||
using IosToolTask = Tasking::CustomTask<IosToolTaskAdapter>;
|
||||
|
||||
} // namespace Ios
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
#include <QVersionNumber>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
@@ -105,20 +104,6 @@ static bool findXcodePath(QString *xcodePath)
|
||||
return (process.exitStatus() == QProcess::NormalExit && QFile::exists(*xcodePath));
|
||||
}
|
||||
|
||||
static bool checkDevelopmentStatusViaDeviceCtl(const QString &deviceId)
|
||||
{
|
||||
QProcess process;
|
||||
process.start("/usr/bin/xcrun", QStringList({"devicectl",
|
||||
"device", "info", "details", "--quiet", "--device", deviceId, "-j", "-"}));
|
||||
if (!process.waitForFinished(3000)) {
|
||||
qCWarning(loggingCategory) << "Failed to launch devicectl:" << process.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto jsonOutput = QJsonDocument::fromJson(process.readAllStandardOutput());
|
||||
return jsonOutput["result"]["deviceProperties"]["developerModeStatus"] == "enabled";
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Finds the \e DeveloperDiskImage.dmg path corresponding to \a versionStr and \a buildStr.
|
||||
*
|
||||
@@ -1685,17 +1670,10 @@ void DevInfoSession::deviceCallbackReturned()
|
||||
res[deviceNameKey] = getStringValue(device, nullptr, CFSTR("DeviceName"));
|
||||
res[uniqueDeviceId] = getStringValue(device, nullptr, CFSTR("UniqueDeviceID"));
|
||||
const QString productVersion = getStringValue(device, nullptr, CFSTR("ProductVersion"));
|
||||
|
||||
if (productVersion.startsWith("17.")) {
|
||||
res[developerStatusKey] = checkDevelopmentStatusViaDeviceCtl(res[uniqueDeviceId])
|
||||
? QLatin1String("Development") : QLatin1String("*off*");
|
||||
} else {
|
||||
res[developerStatusKey] = getStringValue(device,
|
||||
res[developerStatusKey] = getStringValue(device,
|
||||
CFSTR("com.apple.xcode.developerdomain"),
|
||||
CFSTR("DeveloperStatus"),
|
||||
"*off*");
|
||||
}
|
||||
|
||||
res[cpuArchitectureKey] = getStringValue(device, nullptr, CFSTR("CPUArchitecture"));
|
||||
const QString buildVersion = getStringValue(device, nullptr, CFSTR("BuildVersion"));
|
||||
if (!productVersion.isEmpty() && !buildVersion.isEmpty())
|
||||
|
||||
Reference in New Issue
Block a user