summaryrefslogtreecommitdiff
path: root/src/plugins/ios/iostoolhandler.cpp
diff options
context:
space:
mode:
authorFawzi Mohamed <fawzi.mohamed@digia.com>2013-04-25 16:02:17 +0200
committerEike Ziller <eike.ziller@digia.com>2013-10-02 13:15:49 +0200
commit8d96ce557ef5b49d0cfea2b1633e09354948088c (patch)
tree8810583d9685f954b0d7ea6689b314fb2f2eb4bb /src/plugins/ios/iostoolhandler.cpp
parent3a7d91ca4404937889986c54b730d7b01208cc27 (diff)
downloadqt-creator-8d96ce557ef5b49d0cfea2b1633e09354948088c.tar.gz
ios: preliminary support for ios
first work in progress support for ios * separate iosTool using xml communication used for device info and run * iossim tool to handle the simulator * debug prepared but not working * separate gcc toolchain detection fix for simulator 1) add a QT built for ios 2) open a project, for example qtbase/examples/widgets/animation/animatedtiles/animatedtiles.pro 3) build/run... Change-Id: I7e01604e416338cbe4692dfb34f5d3f31312702d Reviewed-by: Eike Ziller <eike.ziller@digia.com>
Diffstat (limited to 'src/plugins/ios/iostoolhandler.cpp')
-rw-r--r--src/plugins/ios/iostoolhandler.cpp977
1 files changed, 977 insertions, 0 deletions
diff --git a/src/plugins/ios/iostoolhandler.cpp b/src/plugins/ios/iostoolhandler.cpp
new file mode 100644
index 0000000000..e91276b6c0
--- /dev/null
+++ b/src/plugins/ios/iostoolhandler.cpp
@@ -0,0 +1,977 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "iostoolhandler.h"
+#include "iosconfigurations.h"
+
+#include <utils/qtcassert.h>
+#include <utils/fileutils.h>
+
+#include <QProcess>
+#include <QXmlStreamReader>
+#include <QSocketNotifier>
+#include <QDebug>
+#include <QCoreApplication>
+#include <QList>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static const bool debugToolHandler = false;
+
+namespace Ios {
+
+namespace Internal {
+
+class MyProcess: public QProcess
+{
+ Q_OBJECT
+public:
+ explicit MyProcess(QObject *parent = 0);
+ int processOutput();
+ QSocketNotifier *notifier();
+protected:
+ virtual void setupChildProcess();
+private:
+ int m_sockets[2];
+ QSocketNotifier *m_notifier;
+};
+
+struct ParserState {
+ enum Kind {
+ Msg,
+ DeviceId,
+ Key,
+ Value,
+ QueryResult,
+ AppOutput,
+ AppStarted,
+ InferiorPid,
+ Item,
+ Status,
+ AppTransfer,
+ DeviceInfo,
+ Exit
+ };
+ Kind kind;
+ QString elName;
+ QString chars;
+ QString key;
+ QString value;
+ QMap<QString,QString> info;
+ int progress, maxProgress;
+ bool collectChars() {
+ switch (kind) {
+ case Msg:
+ case DeviceId:
+ case Key:
+ case Value:
+ case Status:
+ case InferiorPid:
+ return true;
+ case QueryResult:
+ case AppOutput:
+ case AppStarted:
+ case AppTransfer:
+ case Item:
+ case DeviceInfo:
+ case Exit:
+ return false;
+ }
+ }
+
+ ParserState(Kind kind) :
+ kind(kind) { }
+};
+
+class IosToolHandlerPrivate
+{
+public:
+ enum State {
+ NonStarted,
+ Starting,
+ StartedInferior,
+ XmlEndSeenNotProcessed,
+ XmlEndProcessed,
+ Stopped
+ };
+ enum Op {
+ OpNone,
+ OpAppTransfer,
+ OpDeviceInfo,
+ OpAppRun
+ };
+
+ explicit IosToolHandlerPrivate(IosToolHandler::DeviceType devType, IosToolHandler *q);
+ virtual void requestTransferApp(const QString &bundlePath, const QString &deviceId,
+ int timeout = 1000) = 0;
+ virtual void requestRunApp(const QString &bundlePath, const QStringList &extraArgs,
+ IosToolHandler::RunKind runKind,
+ const QString &deviceId, int timeout = 1000) = 0;
+ virtual void requestDeviceInfo(const QString &deviceId, int timeout = 1000) = 0;
+ bool isRunning();
+ void start(const QString &exe, const QStringList &args);
+ void stop();
+
+ // signals
+ void isTransferringApp(const QString &bundlePath, const QString &deviceId, int progress,
+ int maxProgress, const QString &info);
+ void didTransferApp(const QString &bundlePath, const QString &deviceId,
+ IosToolHandler::OpStatus status);
+ void didStartApp(const QString &bundlePath, const QString &deviceId,
+ IosToolHandler::OpStatus status);
+ void gotGdbserverSocket(const QString &bundlePath, const QString &deviceId, int gdbFd);
+ void gotInferiorPid(const QString &bundlePath, const QString &deviceId, pid_t pid);
+ void deviceInfo(const QString &deviceId, const IosToolHandler::Dict &info);
+ void appOutput(const QString &output);
+ void errorMsg(const QString &msg);
+ void toolExited(int code);
+ // slots
+ void subprocessError(QProcess::ProcessError error);
+ void subprocessFinished(int exitCode, QProcess::ExitStatus exitStatus);
+ void subprocessHasData(int socket);
+ virtual bool expectsFileDescriptor() = 0;
+protected:
+ int checkForXmlEnd();
+ void processXml();
+
+ IosToolHandler *q;
+ MyProcess process;
+ QXmlStreamReader outputParser;
+ QString deviceId;
+ QString bundlePath;
+ IosToolHandler::RunKind runKind;
+ State state;
+ Op op;
+ IosToolHandler::DeviceType devType;
+ static const int lookaheadSize = 67;
+ QByteArray buffer;
+ QByteArray currentData;
+ int iBegin, iEnd, gdbSocket;
+ QList<ParserState> stack;
+};
+
+class IosDeviceToolHandlerPrivate : public IosToolHandlerPrivate
+{
+public:
+ explicit IosDeviceToolHandlerPrivate(IosToolHandler::DeviceType devType, IosToolHandler *q);
+ virtual void requestTransferApp(const QString &bundlePath, const QString &deviceId,
+ int timeout = 1000);
+ virtual void requestRunApp(const QString &bundlePath, const QStringList &extraArgs,
+ IosToolHandler::RunKind runKind,
+ const QString &deviceId, int timeout = 1000);
+ virtual void requestDeviceInfo(const QString &deviceId, int timeout = 1000);
+ virtual bool expectsFileDescriptor();
+};
+
+class IosSimulatorToolHandlerPrivate : public IosToolHandlerPrivate
+{
+public:
+ explicit IosSimulatorToolHandlerPrivate(IosToolHandler::DeviceType devType, IosToolHandler *q);
+ virtual void requestTransferApp(const QString &bundlePath, const QString &deviceId,
+ int timeout = 1000);
+ virtual void requestRunApp(const QString &bundlePath, const QStringList &extraArgs,
+ IosToolHandler::RunKind runKind,
+ const QString &deviceId, int timeout = 1000);
+ virtual void requestDeviceInfo(const QString &deviceId, int timeout = 1000);
+ virtual bool expectsFileDescriptor();
+private:
+ void addDeviceArguments(QStringList &args) const;
+};
+
+MyProcess::MyProcess(QObject *parent) : QProcess(parent)
+{
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, &m_sockets[0]) == -1) {
+ qDebug() << "IosToolHandler socketpair failed ";
+ }
+ shutdown(m_sockets[0], SHUT_WR);
+ m_notifier = new QSocketNotifier(m_sockets[0], QSocketNotifier::Read, this);
+}
+
+int MyProcess::processOutput()
+{
+ return m_sockets[0];
+}
+
+QSocketNotifier *MyProcess::notifier()
+{
+ return m_notifier;
+}
+
+void MyProcess::setupChildProcess()
+{
+ if (dup2(m_sockets[1], 1) == -1) { // use the unix socket as stdout
+ qDebug() << "IosToolHandler dup2 call failed";
+ emit finished(-1, QProcess::CrashExit);
+ exit(-1);
+ }
+ shutdown(1, SHUT_RD); // leave open for handshake when transferring fd?
+}
+
+IosToolHandlerPrivate::IosToolHandlerPrivate(IosToolHandler::DeviceType devType,
+ Ios::IosToolHandler *q) :
+ q(q), state(NonStarted), devType(devType), buffer(4*lookaheadSize, 0), iBegin(0), iEnd(0),
+ gdbSocket(-1)
+{
+ QObject::connect(process.notifier(), SIGNAL(activated(int)), q, SLOT(subprocessHasData(int)));
+ QObject::connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)),
+ q, SLOT(subprocessFinished(int,QProcess::ExitStatus)));
+ QObject::connect(&process, SIGNAL(error(QProcess::ProcessError)),
+ q, SLOT(subprocessError(QProcess::ProcessError)));
+ int accessFlags = fcntl(process.processOutput(), F_GETFL);
+ if (fcntl(process.processOutput(), F_SETFL, accessFlags | O_NONBLOCK) == -1)
+ qDebug() << "IosToolHandler fcntl F_SETFL failed to set non blocking mode"
+ << qt_error_string(errno);
+}
+
+bool IosToolHandlerPrivate::isRunning()
+{
+ return process.state() != QProcess::NotRunning;
+}
+
+void IosToolHandlerPrivate::start(const QString &exe, const QStringList &args)
+{
+ QTC_CHECK(state == NonStarted);
+ state = Starting;
+ if (debugToolHandler)
+ qDebug() << "running " << exe << args;
+ process.start(exe, args);
+ state = StartedInferior;
+}
+
+void IosToolHandlerPrivate::stop()
+{
+ if (debugToolHandler)
+ qDebug() << "IosToolHandlerPrivate::stop";
+ if (process.state() != QProcess::NotRunning) {
+ close(process.processOutput());
+ process.close();
+ process.kill();
+ }
+ if (state != Stopped) {
+ state = Stopped;
+ emit q->finished(q);
+ }
+}
+
+// signals
+void IosToolHandlerPrivate::isTransferringApp(const QString &bundlePath, const QString &deviceId,
+ int progress, int maxProgress, const QString &info)
+{
+ emit q->isTransferringApp(q, bundlePath, deviceId, progress, maxProgress, info);
+}
+
+void IosToolHandlerPrivate::didTransferApp(const QString &bundlePath, const QString &deviceId,
+ Ios::IosToolHandler::OpStatus status)
+{
+ emit q->didTransferApp(q, bundlePath, deviceId, status);
+}
+
+void IosToolHandlerPrivate::didStartApp(const QString &bundlePath, const QString &deviceId,
+ IosToolHandler::OpStatus status)
+{
+ emit q->didStartApp(q, bundlePath, deviceId, status);
+}
+
+void IosToolHandlerPrivate::gotGdbserverSocket(const QString &bundlePath, const QString &deviceId,
+ int gdbFd)
+{
+ emit q->gotGdbserverSocket(q, bundlePath, deviceId, gdbFd);
+}
+
+void IosToolHandlerPrivate::gotInferiorPid(const QString &bundlePath, const QString &deviceId,
+ pid_t pid)
+{
+ emit q->gotInferiorPid(q, bundlePath, deviceId, pid);
+}
+
+void IosToolHandlerPrivate::deviceInfo(const QString &deviceId,
+ const Ios::IosToolHandler::Dict &info)
+{
+ emit q->deviceInfo(q, deviceId, info);
+}
+
+void IosToolHandlerPrivate::appOutput(const QString &output)
+{
+ emit q->appOutput(q, output);
+}
+
+void IosToolHandlerPrivate::errorMsg(const QString &msg)
+{
+ emit q->errorMsg(q, msg);
+}
+
+void IosToolHandlerPrivate::toolExited(int code)
+{
+ emit q->toolExited(q, code);
+}
+
+void IosToolHandlerPrivate::subprocessError(QProcess::ProcessError error)
+{
+ switch (state) {
+ case NonStarted:
+ qDebug() << "subprocessError() when state was NonStarted";
+ // pass
+ case Starting:
+ switch (op){
+ case OpNone:
+ qDebug() << "subprocessError() when op is OpNone";
+ break;
+ case OpAppTransfer:
+ didTransferApp(bundlePath, deviceId, IosToolHandler::Failure);
+ break;
+ case OpAppRun:
+ didStartApp(bundlePath, deviceId, IosToolHandler::Failure);
+ break;
+ case OpDeviceInfo:
+ break;
+ }
+ // pass
+ case StartedInferior:
+ case XmlEndSeenNotProcessed:
+ case XmlEndProcessed:
+ errorMsg(q->tr("Subprocess Error %1").arg(error));
+ toolExited(-1);
+ break;
+ case Stopped:
+ qDebug() << "IosToolHandler, subprocessError() in an already stopped process";
+ }
+}
+
+void IosToolHandlerPrivate::subprocessFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+ // process potentially pending data
+ subprocessHasData(process.processOutput());
+ switch (state) {
+ case NonStarted:
+ qDebug() << "subprocessFinished() when state was NonStarted";
+ // pass
+ case Starting:
+ switch (op){
+ case OpNone:
+ qDebug() << "subprocessFinished() when op was OpNone";
+ break;
+ case OpAppTransfer:
+ didTransferApp(bundlePath, deviceId, IosToolHandler::Failure);
+ break;
+ case OpAppRun:
+ didStartApp(bundlePath, deviceId, IosToolHandler::Failure);
+ break;
+ case OpDeviceInfo:
+ break;
+ }
+ // pass
+ case StartedInferior:
+ case XmlEndSeenNotProcessed:
+ case XmlEndProcessed:
+ toolExited((exitStatus == QProcess::CrashExit && exitCode == 0) ? -1 : exitCode);
+ break;
+ case Stopped:
+ if (debugToolHandler)
+ qDebug() << "IosToolHandler, subprocessFinished() in an already stopped process (normal)";
+ break;
+ }
+}
+
+int recv_fd(int socket)
+{
+ int sent_fd;
+ char message_buffer[1];
+
+ iovec io_vector[1];
+ memset(&io_vector[0], 0, sizeof(iovec));
+ /* setup a place to fill in message contents */
+ io_vector[0].iov_base = message_buffer;
+ io_vector[0].iov_len = 1;
+
+ msghdr socket_message;
+ memset(&socket_message, 0, sizeof(struct msghdr));
+ socket_message.msg_iov = io_vector;
+ socket_message.msg_iovlen = 1;
+
+ /* provide space for the ancillary data */
+ char ancillary_element_buffer[CMSG_SPACE(sizeof(int))];
+ memset(ancillary_element_buffer, 0, CMSG_SPACE(sizeof(int)));
+ socket_message.msg_control = ancillary_element_buffer;
+ socket_message.msg_controllen = CMSG_SPACE(sizeof(int));
+
+ int flags = 0;
+#ifdef MSG_CMSG_CLOEXEC
+ flags = MSG_CMSG_CLOEXEC;
+#endif
+ if (recvmsg(socket, &socket_message, flags) < 0)
+ return -1;
+
+ if (message_buffer[0] != '.') {
+ qDebug() << "IosToolHandler, unexpected inband data when receiving socket";
+ return -1;
+ }
+
+ if ((socket_message.msg_flags & MSG_CTRUNC) == MSG_CTRUNC) {
+ qDebug() << "IosToolHandler, not provide enough space for the ancillary element array";
+ return -1;
+ }
+
+ /* iterate ancillary elements */
+ cmsghdr *control_message = NULL;
+ for (control_message = CMSG_FIRSTHDR(&socket_message);
+ control_message != NULL;
+ control_message = CMSG_NXTHDR(&socket_message, control_message)) {
+ if ( (control_message->cmsg_level == SOL_SOCKET) &&
+ (control_message->cmsg_type == SCM_RIGHTS) ) {
+ sent_fd = *((int *) CMSG_DATA(control_message));
+ return sent_fd;
+ }
+ }
+
+ return -1;
+}
+
+int IosToolHandlerPrivate::checkForXmlEnd()
+{
+ const char *xmlEnd = "</query_result>";
+ int lenXmlEnd = 15;
+ int i = 0, j = 0;
+ while (i < lenXmlEnd && j < iEnd) {
+ if (buffer.at(j) != xmlEnd[i]) {
+ if (i == 0) {
+ ++j;
+ if (j + lenXmlEnd > iEnd)
+ break;
+ } else {
+ i = 0;
+ }
+ } else {
+ ++i;
+ ++j;
+ }
+ }
+ if (i == lenXmlEnd)
+ return j;
+ return -1;
+}
+
+void IosToolHandlerPrivate::processXml()
+{
+ while (!outputParser.atEnd()) {
+ QXmlStreamReader::TokenType tt = outputParser.readNext();
+ //qDebug() << "processXml, tt=" << tt;
+ switch (tt) {
+ case QXmlStreamReader::NoToken:
+ // The reader has not yet read anything.
+ continue;
+ case QXmlStreamReader::Invalid:
+ // An error has occurred, reported in error() and errorString().
+ break;
+ case QXmlStreamReader::StartDocument:
+ // The reader reports the XML version number in documentVersion(), and the encoding
+ // as specified in the XML document in documentEncoding(). If the document is declared
+ // standalone, isStandaloneDocument() returns true; otherwise it returns false.
+ break;
+ case QXmlStreamReader::EndDocument:
+ // The reader reports the end of the document.
+ // state = XmlEndProcessed;
+ break;
+ case QXmlStreamReader::StartElement:
+ // The reader reports the start of an element with namespaceUri() and name(). Empty
+ // elements are also reported as StartElement, followed directly by EndElement.
+ // The convenience function readElementText() can be called to concatenate all content
+ // until the corresponding EndElement. Attributes are reported in attributes(),
+ // namespace declarations in namespaceDeclarations().
+ {
+ QStringRef elName = outputParser.name();
+ if (elName == QLatin1String("msg")) {
+ stack.append(ParserState(ParserState::Msg));
+ } else if (elName == QLatin1String("exit")) {
+ stack.append(ParserState(ParserState::Exit));
+ toolExited(outputParser.attributes().value(QLatin1String("code"))
+ .toString().toInt());
+ } else if (elName == QLatin1String("device_id")) {
+ stack.append(ParserState(ParserState::DeviceId));
+ } else if (elName == QLatin1String("key")) {
+ stack.append(ParserState(ParserState::Key));
+ } else if (elName == QLatin1String("value")) {
+ stack.append(ParserState(ParserState::Value));
+ } else if (elName == QLatin1String("query_result")) {
+ stack.append(ParserState(ParserState::QueryResult));
+ } else if (elName == QLatin1String("app_output")) {
+ stack.append(ParserState(ParserState::AppOutput));
+ } else if (elName == QLatin1String("item")) {
+ stack.append(ParserState(ParserState::Item));
+ } else if (elName == QLatin1String("status")) {
+ ParserState pState(ParserState::Status);
+ QXmlStreamAttributes attributes = outputParser.attributes();
+ pState.progress = attributes.value(QLatin1String("progress")).toString().toInt();
+ pState.maxProgress = attributes.value(QLatin1String("max_progress")).toString().toInt();
+ stack.append(pState);
+ } else if (elName == QLatin1String("app_started")) {
+ stack.append(ParserState(ParserState::AppStarted));
+ QXmlStreamAttributes attributes = outputParser.attributes();
+ QStringRef statusStr = attributes.value(QLatin1String("status"));
+ Ios::IosToolHandler::OpStatus status = Ios::IosToolHandler::Unknown;
+ if (statusStr.compare(QLatin1String("success"), Qt::CaseInsensitive) == 0)
+ status = Ios::IosToolHandler::Success;
+ else if (statusStr.compare(QLatin1String("failure"), Qt::CaseInsensitive) == 0)
+ status = Ios::IosToolHandler::Failure;
+ didStartApp(bundlePath, deviceId, status);
+ } else if (elName == QLatin1String("app_transfer")) {
+ stack.append(ParserState(ParserState::AppTransfer));
+ QXmlStreamAttributes attributes = outputParser.attributes();
+ QStringRef statusStr = attributes.value(QLatin1String("status"));
+ Ios::IosToolHandler::OpStatus status = Ios::IosToolHandler::Unknown;
+ if (statusStr.compare(QLatin1String("success"), Qt::CaseInsensitive) == 0)
+ status = Ios::IosToolHandler::Success;
+ else if (statusStr.compare(QLatin1String("failure"), Qt::CaseInsensitive) == 0)
+ status = Ios::IosToolHandler::Failure;
+ emit didTransferApp(bundlePath, deviceId, status);
+ } else if (elName == QLatin1String("device_info")) {
+ stack.append(ParserState(ParserState::DeviceInfo));
+ } else if (elName == QLatin1String("inferior_pid")) {
+ stack.append(ParserState(ParserState::InferiorPid));
+ } else {
+ qDebug() << "unexpected element " << elName;
+ }
+ break;
+ }
+ case QXmlStreamReader::EndElement:
+ // The reader reports the end of an element with namespaceUri() and name().
+ {
+ ParserState p = stack.last();
+ stack.removeLast();
+ switch (p.kind) {
+ case ParserState::Msg:
+ errorMsg(p.chars);
+ break;
+ case ParserState::DeviceId:
+ if (deviceId.isEmpty())
+ deviceId = p.chars;
+ else
+ QTC_CHECK(deviceId.compare(p.chars, Qt::CaseInsensitive) == 0);
+ break;
+ case ParserState::Key:
+ stack.last().key = p.chars;
+ break;
+ case ParserState::Value:
+ stack.last().value = p.chars;
+ break;
+ case ParserState::Status:
+ isTransferringApp(bundlePath, deviceId, p.progress, p.maxProgress, p.chars);
+ break;
+ case ParserState::QueryResult:
+ state = XmlEndProcessed;
+ break;
+ case ParserState::AppOutput:
+ break;
+ case ParserState::AppStarted:
+ break;
+ case ParserState::AppTransfer:
+ break;
+ case ParserState::Item:
+ stack.last().info.insert(p.key, p.value);
+ break;
+ case ParserState::DeviceInfo:
+ deviceInfo(deviceId, p.info);
+ break;
+ case ParserState::Exit:
+ break;
+ case ParserState::InferiorPid:
+ gotInferiorPid(bundlePath, deviceId, pid_t(p.chars.toInt()));
+ break;
+ }
+ break;
+ }
+ case QXmlStreamReader::Characters:
+ // The reader reports characters in text(). If the characters are all white-space,
+ // isWhitespace() returns true. If the characters stem from a CDATA section,
+ // isCDATA() returns true.
+ if (stack.isEmpty())
+ break;
+ if (stack.last().kind == ParserState::AppOutput)
+ emit appOutput(outputParser.text().toString());
+ else if (stack.last().collectChars())
+ stack.last().chars.append(outputParser.text());
+ break;
+ case QXmlStreamReader::Comment:
+ // The reader reports a comment in text().
+ break;
+ case QXmlStreamReader::DTD:
+ // The reader reports a DTD in text(), notation declarations in notationDeclarations(),
+ // and entity declarations in entityDeclarations(). Details of the DTD declaration are
+ // reported in in dtdName(), dtdPublicId(), and dtdSystemId().
+ break;
+ case QXmlStreamReader::EntityReference:
+ // The reader reports an entity reference that could not be resolved. The name of
+ // the reference is reported in name(), the replacement text in text().
+ break;
+ case QXmlStreamReader::ProcessingInstruction:
+ break;
+ }
+ }
+ if (outputParser.hasError()
+ && outputParser.error() != QXmlStreamReader::PrematureEndOfDocumentError) {
+ qDebug() << "error parsing iosTool output:" << outputParser.errorString();
+ stop();
+ }
+}
+
+void IosToolHandlerPrivate::subprocessHasData(int socket)
+{
+ if (debugToolHandler)
+ qDebug() << "subprocessHasData, state:" << state;
+ process.notifier()->setEnabled(false);
+ while (true) {
+ switch (state) {
+ case NonStarted:
+ qDebug() << "IosToolHandler unexpected state in subprocessHasData: NonStarted";
+ // pass
+ case Starting:
+ case StartedInferior:
+ // read some data
+ {
+ if (iEnd + lookaheadSize > buffer.size()) {
+ memmove(buffer.data(), buffer.data() + (iEnd - lookaheadSize), lookaheadSize);
+ iBegin = lookaheadSize;
+ iEnd = iBegin;
+ } else {
+ iBegin = iEnd;
+ }
+ currentData.clear();
+ ssize_t reallyRead = recv(socket, buffer.data() + iBegin, lookaheadSize, 0);
+ if (reallyRead == 0) { // eof
+ stop();
+ return;
+ }
+ if (reallyRead == -1) {
+ if (errno == EAGAIN) { // read all so far
+ if (debugToolHandler)
+ qDebug() << "read all for now";
+ process.notifier()->setEnabled(true);
+ return;
+ }
+ if (errno == EINTR)
+ continue;
+ qDebug() << "IosToolHandlerPrivate::subprocessHasData " << qt_error_string(errno);
+ stop();
+ return;
+ }
+ iEnd = iBegin + reallyRead;
+ int xmlEnd = checkForXmlEnd();
+ if (xmlEnd != -1) {
+ state = XmlEndSeenNotProcessed;
+ currentData = buffer.mid(iBegin, xmlEnd - iBegin);
+ } else {
+ currentData = buffer.mid(iBegin, reallyRead);
+ }
+ if (debugToolHandler)
+ qDebug() << "subprocessHasData read " << currentData;
+ outputParser.addData(currentData);
+ processXml();
+ break;
+ }
+ case XmlEndSeenNotProcessed:
+ qDebug() << "IosToolHandler unexpected state in subprocessHasData: XmlEndSeenNotProcessed";
+ // pass
+ case XmlEndProcessed:
+ {
+ // check for sent fd
+ if (!expectsFileDescriptor()) {
+ stop();
+ return;
+ }
+ int lenToRead = lookaheadSize;
+ int spacerStart = iBegin + currentData.size();
+ while (spacerStart < iEnd && buffer.at(spacerStart) != 'n')
+ ++spacerStart;
+ if (iEnd - (iBegin + currentData.size()) < lenToRead) {
+ int lastXmlSize = currentData.size();
+ if (iBegin > 0) {
+ memmove(buffer.data(), buffer.data() + iBegin, iEnd - iBegin);
+ iEnd -= iBegin;
+ iBegin = 0;
+ spacerStart -= iBegin;
+ currentData = buffer.mid(0, lastXmlSize); // remove this??
+ }
+ ssize_t toRead = lookaheadSize - (iEnd - spacerStart);
+ ssize_t reallyRead = recv(socket, buffer.data() + iBegin, toRead, 0);
+ if (reallyRead == 0) { // eof
+ stop();
+ return;
+ }
+ if (reallyRead == -1) {
+ if (errno == EAGAIN) { // read all so far
+ if (debugToolHandler)
+ qDebug() << "read all for now2";
+ process.notifier()->setEnabled(true);
+ return;
+ }
+ if (errno == EINTR)
+ continue;
+ if (debugToolHandler)
+ qDebug() << "IosToolHandlerPrivate::subprocessHasData " << qt_error_string(errno);
+ stop();
+ return;
+ }
+ iEnd += reallyRead;
+ if (reallyRead != toRead)
+ continue;
+ if (spacerStart < iEnd && buffer.at(spacerStart) != 'n') {
+ ++spacerStart;
+ while (spacerStart < iEnd && buffer.at(spacerStart) != 'n')
+ ++spacerStart;
+ continue;
+ }
+ }
+ gdbSocket = recv_fd(socket);
+ gotGdbserverSocket(bundlePath, deviceId, gdbSocket);
+ stop();
+ return;
+ }
+ case Stopped:
+ return;
+ }
+ }
+ process.notifier()->setEnabled(true);
+}
+
+// IosDeviceToolHandlerPrivate
+
+IosDeviceToolHandlerPrivate::IosDeviceToolHandlerPrivate(IosToolHandler::DeviceType devType,
+ IosToolHandler *q)
+ : IosToolHandlerPrivate(devType, q)
+{ }
+
+void IosDeviceToolHandlerPrivate::requestTransferApp(const QString &bundlePath,
+ const QString &deviceId, int timeout)
+{
+ this->bundlePath = bundlePath;
+ this->deviceId = deviceId;
+ QStringList args;
+ args << QLatin1String("-device-id") << deviceId << QLatin1String("-bundle")
+ << bundlePath << QLatin1String("-timeout") << QString::number(timeout)
+ << QLatin1String("-deploy");
+ start(IosToolHandler::iosDeviceToolPath(), args);
+}
+
+void IosDeviceToolHandlerPrivate::requestRunApp(const QString &bundlePath,
+ const QStringList &extraArgs,
+ IosToolHandler::RunKind runType,
+ const QString &deviceId, int timeout)
+{
+ this->bundlePath = bundlePath;
+ this->deviceId = deviceId;
+ this->runKind = runType;
+ QStringList args;
+ args << QLatin1String("-device-id") << deviceId << QLatin1String("-bundle")
+ << bundlePath << QLatin1String("-timeout") << QString::number(timeout);
+ //args << QLatin1String("--deploy"); // to remove when the separate deploy step is functional
+ switch (runType) {
+ case IosToolHandler::NormalRun:
+ args << QLatin1String("-run");
+ break;
+ case IosToolHandler::DebugRun:
+ args << QLatin1String("-debug");
+ break;
+ }
+ args << QLatin1String("-extra-args") << extraArgs;
+ op = OpAppRun;
+ start(IosToolHandler::iosDeviceToolPath(), args);
+}
+
+void IosDeviceToolHandlerPrivate::requestDeviceInfo(const QString &deviceId, int timeout)
+{
+ this->deviceId = deviceId;
+ QStringList args;
+ args << QLatin1String("-device-id") << deviceId << QLatin1String("-device-info")
+ << QLatin1String("-timeout") << QString::number(timeout);
+ op = OpDeviceInfo;
+ start(IosToolHandler::iosDeviceToolPath(), args);
+}
+
+bool IosDeviceToolHandlerPrivate::expectsFileDescriptor()
+{
+ return op == OpAppRun && runKind == IosToolHandler::DebugRun;
+}
+
+// IosSimulatorToolHandlerPrivate
+
+IosSimulatorToolHandlerPrivate::IosSimulatorToolHandlerPrivate(IosToolHandler::DeviceType devType,
+ IosToolHandler *q)
+ : IosToolHandlerPrivate(devType, q)
+{ }
+
+void IosSimulatorToolHandlerPrivate::requestTransferApp(const QString &bundlePath,
+ const QString &deviceId, int timeout)
+{
+ Q_UNUSED(timeout);
+ this->bundlePath = bundlePath;
+ this->deviceId = deviceId;
+ emit didTransferApp(bundlePath, deviceId, IosToolHandler::Success);
+}
+
+void IosSimulatorToolHandlerPrivate::requestRunApp(const QString &bundlePath,
+ const QStringList &extraArgs,
+ IosToolHandler::RunKind runType,
+ const QString &deviceId, int timeout)
+{
+ Q_UNUSED(timeout);
+ this->bundlePath = bundlePath;
+ this->deviceId = deviceId;
+ this->runKind = runType;
+ QStringList args;
+
+ args << QLatin1String("launch") << bundlePath;
+ Utils::FileName devPath = IosConfigurations::instance().config().developerPath;
+ if (!devPath.isEmpty())
+ args << QLatin1String("--developer-path") << devPath.toString();
+ addDeviceArguments(args);
+ switch (runType) {
+ case IosToolHandler::NormalRun:
+ break;
+ case IosToolHandler::DebugRun:
+ args << QLatin1String("--wait-for-debugger");
+ break;
+ }
+ args << QLatin1String("--args") << extraArgs;
+ op = OpAppRun;
+ start(IosToolHandler::iosSimulatorToolPath(), args);
+}
+
+void IosSimulatorToolHandlerPrivate::requestDeviceInfo(const QString &deviceId, int timeout)
+{
+ Q_UNUSED(timeout);
+ this->deviceId = deviceId;
+ QStringList args;
+ args << QLatin1String("showsdks");
+ op = OpDeviceInfo;
+ start(IosToolHandler::iosSimulatorToolPath(), args);
+}
+
+bool IosSimulatorToolHandlerPrivate::expectsFileDescriptor()
+{
+ return false;
+}
+
+void IosSimulatorToolHandlerPrivate::addDeviceArguments(QStringList &args) const
+{
+ switch (devType) {
+ case IosToolHandler::IosDeviceType:
+ qDebug() << "IosSimulatorToolHandlerPrivate has device type IosDeviceType";
+ break;
+ case IosToolHandler::IosSimulatedIphoneType:
+ args << QLatin1String("--family") << QLatin1String("iphone");
+ break;
+ case IosToolHandler::IosSimulatedIpadType:
+ args << QLatin1String("--family") << QLatin1String("ipad");
+ break;
+ case IosToolHandler::IosSimulatedIphoneRetina4InchType:
+ args << QLatin1String("--family") << QLatin1String("iphone")
+ << QLatin1String("--retina") << QLatin1String("--tall");
+ break;
+ case IosToolHandler::IosSimulatedIphoneRetina3_5InchType:
+ args << QLatin1String("--family") << QLatin1String("iphone") << QLatin1String("--retina");
+ break;
+ case IosToolHandler::IosSimulatedIpadRetinaType:
+ args << QLatin1String("--family") << QLatin1String("ipad") << QLatin1String("--retina");
+ break;
+ }
+}
+
+} // namespace Internal
+
+QString IosToolHandler::iosDeviceToolPath()
+{
+ QString res = QCoreApplication::applicationDirPath() + QLatin1String("/iosTool");
+ return res;
+}
+
+QString IosToolHandler::iosSimulatorToolPath()
+{
+ QString res = QCoreApplication::applicationDirPath() + QLatin1String("/iossim");
+ return res;
+}
+
+IosToolHandler::IosToolHandler(DeviceType devType, QObject *parent) :
+ QObject(parent)
+{
+ if (devType == IosDeviceType)
+ d = new Internal::IosDeviceToolHandlerPrivate(devType, this);
+ else
+ d = new Internal::IosSimulatorToolHandlerPrivate(devType, this);
+}
+
+void IosToolHandler::stop()
+{
+ d->stop();
+}
+
+void IosToolHandler::requestTransferApp(const QString &bundlePath, const QString &deviceId,
+ int timeout)
+{
+ d->requestTransferApp(bundlePath, deviceId, timeout);
+}
+
+void IosToolHandler::requestRunApp(const QString &bundlePath, const QStringList &extraArgs,
+ RunKind runType, const QString &deviceId, int timeout)
+{
+ d->requestRunApp(bundlePath, extraArgs, runType, deviceId, timeout);
+}
+
+void IosToolHandler::requestDeviceInfo(const QString &deviceId, int timeout)
+{
+ d->requestDeviceInfo(deviceId, timeout);
+}
+
+bool IosToolHandler::isRunning()
+{
+ return d->isRunning();
+}
+
+void IosToolHandler::subprocessError(QProcess::ProcessError error)
+{
+ d->subprocessError(error);
+}
+
+void IosToolHandler::subprocessFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+ d->subprocessFinished(exitCode, exitStatus);
+}
+
+void IosToolHandler::subprocessHasData(int socket)
+{
+ d->subprocessHasData(socket);
+}
+
+} // namespace Ios
+
+#include "iostoolhandler.moc"