forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/4.6'
Conflicts: src/plugins/android/androidrunnable.h src/plugins/android/androidrunner.cpp src/plugins/qmakeandroidsupport/qmakeandroidrunconfiguration.cpp src/plugins/qmakeprojectmanager/qmakeproject.cpp src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp Change-Id: I68093d44cfd672347eab82459ff70c21a32297ce
This commit is contained in:
@@ -18,6 +18,7 @@ HEADERS += \
|
|||||||
androiderrormessage.h \
|
androiderrormessage.h \
|
||||||
androidglobal.h \
|
androidglobal.h \
|
||||||
androidrunner.h \
|
androidrunner.h \
|
||||||
|
androidrunnerworker.h \
|
||||||
androiddebugsupport.h \
|
androiddebugsupport.h \
|
||||||
androidqtversionfactory.h \
|
androidqtversionfactory.h \
|
||||||
androidqtversion.h \
|
androidqtversion.h \
|
||||||
@@ -66,6 +67,7 @@ SOURCES += \
|
|||||||
androidtoolchain.cpp \
|
androidtoolchain.cpp \
|
||||||
androiderrormessage.cpp \
|
androiderrormessage.cpp \
|
||||||
androidrunner.cpp \
|
androidrunner.cpp \
|
||||||
|
androidrunnerworker.cpp \
|
||||||
androiddebugsupport.cpp \
|
androiddebugsupport.cpp \
|
||||||
androidqtversionfactory.cpp \
|
androidqtversionfactory.cpp \
|
||||||
androidqtversion.cpp \
|
androidqtversion.cpp \
|
||||||
|
@@ -93,6 +93,8 @@ Project {
|
|||||||
"androidrunnable.h",
|
"androidrunnable.h",
|
||||||
"androidrunner.cpp",
|
"androidrunner.cpp",
|
||||||
"androidrunner.h",
|
"androidrunner.h",
|
||||||
|
"androidrunnerworker.cpp",
|
||||||
|
"androidrunnerworker.h",
|
||||||
"androidsdkmanager.cpp",
|
"androidsdkmanager.cpp",
|
||||||
"androidsdkmanager.h",
|
"androidsdkmanager.h",
|
||||||
"androidsdkmanagerwidget.cpp",
|
"androidsdkmanagerwidget.cpp",
|
||||||
|
@@ -164,6 +164,7 @@ bool AndroidDeployQtStep::init(QList<const BuildStep *> &earlierSteps)
|
|||||||
m_filesToPull["/system/" + libDirName + "/libc.so"] = buildDir + "libc.so";
|
m_filesToPull["/system/" + libDirName + "/libc.so"] = buildDir + "libc.so";
|
||||||
|
|
||||||
AndroidManager::setDeviceSerialNumber(target(), m_serialNumber);
|
AndroidManager::setDeviceSerialNumber(target(), m_serialNumber);
|
||||||
|
AndroidManager::setDeviceApiLevel(target(), info.sdk);
|
||||||
|
|
||||||
|
|
||||||
QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target()->kit());
|
QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target()->kit());
|
||||||
@@ -325,11 +326,12 @@ AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy(QFutureInter
|
|||||||
emit addOutput(tr("The process \"%1\" crashed.").arg(m_command), BuildStep::OutputFormat::ErrorMessage);
|
emit addOutput(tr("The process \"%1\" crashed.").arg(m_command), BuildStep::OutputFormat::ErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exitCode == 0 && exitStatus == QProcess::NormalExit) {
|
if (deployError != NoError) {
|
||||||
if (deployError != NoError && m_uninstallPreviousPackageRun) {
|
if (m_uninstallPreviousPackageRun)
|
||||||
deployError = Failure;
|
deployError = Failure; // Even re-install failed. Set to Failure.
|
||||||
}
|
} else if (exitCode != 0 || exitStatus != QProcess::NormalExit) {
|
||||||
} else {
|
// Set the deployError to Failure when no deployError code was detected
|
||||||
|
// but the adb tool failed otherwise relay the detected deployError.
|
||||||
deployError = Failure;
|
deployError = Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -241,6 +241,16 @@ void AndroidManager::setDeviceSerialNumber(ProjectExplorer::Target *target, cons
|
|||||||
target->setNamedSettings(AndroidDeviceSn, deviceSerialNumber);
|
target->setNamedSettings(AndroidDeviceSn, deviceSerialNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int AndroidManager::deviceApiLevel(ProjectExplorer::Target *target)
|
||||||
|
{
|
||||||
|
return target->namedSettings(ApiLevelKey).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidManager::setDeviceApiLevel(ProjectExplorer::Target *target, int level)
|
||||||
|
{
|
||||||
|
target->setNamedSettings(ApiLevelKey, level);
|
||||||
|
}
|
||||||
|
|
||||||
QPair<int, int> AndroidManager::apiLevelRange()
|
QPair<int, int> AndroidManager::apiLevelRange()
|
||||||
{
|
{
|
||||||
return qMakePair(9, 26);
|
return qMakePair(9, 26);
|
||||||
|
@@ -56,6 +56,9 @@ public:
|
|||||||
static QString deviceSerialNumber(ProjectExplorer::Target *target);
|
static QString deviceSerialNumber(ProjectExplorer::Target *target);
|
||||||
static void setDeviceSerialNumber(ProjectExplorer::Target *target, const QString &deviceSerialNumber);
|
static void setDeviceSerialNumber(ProjectExplorer::Target *target, const QString &deviceSerialNumber);
|
||||||
|
|
||||||
|
static int deviceApiLevel(ProjectExplorer::Target *target);
|
||||||
|
static void setDeviceApiLevel(ProjectExplorer::Target *target, int level);
|
||||||
|
|
||||||
static QString buildTargetSDK(ProjectExplorer::Target *target);
|
static QString buildTargetSDK(ProjectExplorer::Target *target);
|
||||||
|
|
||||||
static bool signPackage(ProjectExplorer::Target *target);
|
static bool signPackage(ProjectExplorer::Target *target);
|
||||||
|
@@ -42,6 +42,7 @@ struct ANDROID_EXPORT AndroidRunnable
|
|||||||
QString deviceSerialNumber;
|
QString deviceSerialNumber;
|
||||||
QString extraAppParams;
|
QString extraAppParams;
|
||||||
Utils::Environment extraEnvVars;
|
Utils::Environment extraEnvVars;
|
||||||
|
int apiLevel = -1;
|
||||||
|
|
||||||
QString displayName() const { return packageName; }
|
QString displayName() const { return packageName; }
|
||||||
static void *staticTypeId;
|
static void *staticTypeId;
|
||||||
|
@@ -28,35 +28,17 @@
|
|||||||
|
|
||||||
#include "androiddeployqtstep.h"
|
#include "androiddeployqtstep.h"
|
||||||
#include "androidconfigurations.h"
|
#include "androidconfigurations.h"
|
||||||
#include "androidglobal.h"
|
|
||||||
#include "androidrunconfiguration.h"
|
#include "androidrunconfiguration.h"
|
||||||
#include "androidmanager.h"
|
#include "androidmanager.h"
|
||||||
#include "androidavdmanager.h"
|
#include "androidavdmanager.h"
|
||||||
|
#include "androidrunnerworker.h"
|
||||||
|
|
||||||
#include <debugger/debuggerrunconfigurationaspect.h>
|
|
||||||
#include <coreplugin/messagemanager.h>
|
#include <coreplugin/messagemanager.h>
|
||||||
#include <projectexplorer/projectexplorer.h>
|
#include <projectexplorer/projectexplorer.h>
|
||||||
#include <projectexplorer/projectexplorerconstants.h>
|
|
||||||
#include <projectexplorer/projectexplorersettings.h>
|
#include <projectexplorer/projectexplorersettings.h>
|
||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
#include <qtsupport/qtkitinformation.h>
|
|
||||||
#include <utils/qtcassert.h>
|
|
||||||
#include <utils/runextensions.h>
|
|
||||||
#include <utils/synchronousprocess.h>
|
|
||||||
#include <utils/temporaryfile.h>
|
|
||||||
#include <utils/url.h>
|
#include <utils/url.h>
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <memory>
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QRegExp>
|
|
||||||
#include <QTime>
|
|
||||||
#include <QTcpServer>
|
|
||||||
#include <QTcpSocket>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace std::placeholders;
|
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
@@ -127,568 +109,10 @@ using namespace Utils;
|
|||||||
namespace Android {
|
namespace Android {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
const int MIN_SOCKET_HANDSHAKE_PORT = 20001;
|
AndroidRunner::AndroidRunner(RunControl *runControl,
|
||||||
const int MAX_SOCKET_HANDSHAKE_PORT = 20999;
|
const QString &intentName,
|
||||||
static const QString pidScript = QStringLiteral("for p in /proc/[0-9]*; "
|
const QString &extraAppParams,
|
||||||
"do cat <$p/cmdline && echo :${p##*/}; done");
|
const Utils::Environment &extraEnvVars)
|
||||||
static const QString pidPollingScript = QStringLiteral("while true; do sleep 1; "
|
|
||||||
"cat /proc/%1/cmdline > /dev/null; done");
|
|
||||||
|
|
||||||
static const QString regExpLogcat = QStringLiteral("[0-9\\-]*" // date
|
|
||||||
"\\s+"
|
|
||||||
"[0-9\\-:.]*"// time
|
|
||||||
"\\s*"
|
|
||||||
"(\\d*)" // pid 1. capture
|
|
||||||
"\\s+"
|
|
||||||
"\\d*" // unknown
|
|
||||||
"\\s+"
|
|
||||||
"(\\w)" // message type 2. capture
|
|
||||||
"\\s+"
|
|
||||||
"(.*): " // source 3. capture
|
|
||||||
"(.*)" // message 4. capture
|
|
||||||
"[\\n\\r]*"
|
|
||||||
);
|
|
||||||
static int APP_START_TIMEOUT = 45000;
|
|
||||||
|
|
||||||
static bool isTimedOut(const chrono::high_resolution_clock::time_point &start,
|
|
||||||
int msecs = APP_START_TIMEOUT)
|
|
||||||
{
|
|
||||||
bool timedOut = false;
|
|
||||||
auto end = chrono::high_resolution_clock::now();
|
|
||||||
if (chrono::duration_cast<chrono::milliseconds>(end-start).count() > msecs)
|
|
||||||
timedOut = true;
|
|
||||||
return timedOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
static qint64 extractPID(const QByteArray &output, const QString &packageName)
|
|
||||||
{
|
|
||||||
qint64 pid = -1;
|
|
||||||
foreach (auto tuple, output.split('\n')) {
|
|
||||||
tuple = tuple.simplified();
|
|
||||||
if (!tuple.isEmpty()) {
|
|
||||||
auto parts = tuple.split(':');
|
|
||||||
QString commandName = QString::fromLocal8Bit(parts.first());
|
|
||||||
if (parts.length() == 2 && commandName == packageName) {
|
|
||||||
pid = parts.last().toLongLong();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void findProcessPID(QFutureInterface<qint64> &fi, const QString &adbPath,
|
|
||||||
QStringList selector, const QString &packageName)
|
|
||||||
{
|
|
||||||
if (packageName.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
qint64 processPID = -1;
|
|
||||||
chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now();
|
|
||||||
do {
|
|
||||||
QThread::msleep(200);
|
|
||||||
const QByteArray out = Utils::SynchronousProcess()
|
|
||||||
.runBlocking(adbPath, selector << QStringLiteral("shell") << pidScript)
|
|
||||||
.allRawOutput();
|
|
||||||
processPID = extractPID(out, packageName);
|
|
||||||
} while (processPID == -1 && !isTimedOut(start) && !fi.isCanceled());
|
|
||||||
|
|
||||||
if (!fi.isCanceled())
|
|
||||||
fi.reportResult(processPID);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void deleter(QProcess *p)
|
|
||||||
{
|
|
||||||
p->kill();
|
|
||||||
p->waitForFinished();
|
|
||||||
// Might get deleted from its own signal handler.
|
|
||||||
p->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
class AndroidRunnerWorker : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
enum DebugHandShakeType {
|
|
||||||
PingPongFiles,
|
|
||||||
SocketHandShake
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
AndroidRunnerWorker(RunControl *runControl, const AndroidRunnable &runnable);
|
|
||||||
~AndroidRunnerWorker();
|
|
||||||
|
|
||||||
void asyncStart();
|
|
||||||
void asyncStop();
|
|
||||||
|
|
||||||
void setAndroidRunnable(const AndroidRunnable &runnable);
|
|
||||||
void handleRemoteDebuggerRunning();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void remoteProcessStarted(Utils::Port gdbServerPort, const QUrl &qmlServer, int pid);
|
|
||||||
void remoteProcessFinished(const QString &errString = QString());
|
|
||||||
|
|
||||||
void remoteOutput(const QString &output);
|
|
||||||
void remoteErrorOutput(const QString &output);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void onProcessIdChanged(qint64 pid);
|
|
||||||
void logcatReadStandardError();
|
|
||||||
void logcatReadStandardOutput();
|
|
||||||
void adbKill(qint64 pid);
|
|
||||||
QStringList selector() const;
|
|
||||||
void forceStop();
|
|
||||||
void findPs();
|
|
||||||
void logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError);
|
|
||||||
bool adbShellAmNeedsQuotes();
|
|
||||||
bool runAdb(const QStringList &args, QString *exitMessage = nullptr, int timeoutS = 10);
|
|
||||||
|
|
||||||
// Create the processes and timer in the worker thread, for correct thread affinity
|
|
||||||
std::unique_ptr<QProcess, decltype(&deleter)> m_adbLogcatProcess;
|
|
||||||
std::unique_ptr<QProcess, decltype(&deleter)> m_psIsAlive;
|
|
||||||
QScopedPointer<QTcpSocket> m_socket;
|
|
||||||
|
|
||||||
QByteArray m_stdoutBuffer;
|
|
||||||
QByteArray m_stderrBuffer;
|
|
||||||
|
|
||||||
QFuture<qint64> m_pidFinder;
|
|
||||||
qint64 m_processPID = -1;
|
|
||||||
bool m_useCppDebugger = false;
|
|
||||||
QmlDebug::QmlDebugServicesPreset m_qmlDebugServices;
|
|
||||||
Utils::Port m_localGdbServerPort; // Local end of forwarded debug socket.
|
|
||||||
QUrl m_qmlServer;
|
|
||||||
QString m_pingFile;
|
|
||||||
QString m_pongFile;
|
|
||||||
QString m_gdbserverPath;
|
|
||||||
QString m_gdbserverSocket;
|
|
||||||
QString m_adb;
|
|
||||||
QRegExp m_logCatRegExp;
|
|
||||||
DebugHandShakeType m_handShakeMethod = SocketHandShake;
|
|
||||||
bool m_customPort = false;
|
|
||||||
|
|
||||||
AndroidRunnable m_androidRunnable;
|
|
||||||
int m_socketHandShakePort = MIN_SOCKET_HANDSHAKE_PORT;
|
|
||||||
};
|
|
||||||
|
|
||||||
AndroidRunnerWorker::AndroidRunnerWorker(RunControl *runControl, const AndroidRunnable &runnable)
|
|
||||||
: m_adbLogcatProcess(nullptr, deleter)
|
|
||||||
, m_psIsAlive(nullptr, deleter)
|
|
||||||
, m_logCatRegExp(regExpLogcat)
|
|
||||||
, m_androidRunnable(runnable)
|
|
||||||
{
|
|
||||||
auto runConfig = runControl->runConfiguration();
|
|
||||||
auto aspect = runConfig->extraAspect<Debugger::DebuggerRunConfigurationAspect>();
|
|
||||||
Core::Id runMode = runControl->runMode();
|
|
||||||
const bool debuggingMode = runMode == ProjectExplorer::Constants::DEBUG_RUN_MODE;
|
|
||||||
m_useCppDebugger = debuggingMode && aspect->useCppDebugger();
|
|
||||||
if (debuggingMode && aspect->useQmlDebugger())
|
|
||||||
m_qmlDebugServices = QmlDebug::QmlDebuggerServices;
|
|
||||||
else if (runMode == ProjectExplorer::Constants::QML_PROFILER_RUN_MODE)
|
|
||||||
m_qmlDebugServices = QmlDebug::QmlProfilerServices;
|
|
||||||
else if (runMode == ProjectExplorer::Constants::QML_PREVIEW_RUN_MODE)
|
|
||||||
m_qmlDebugServices = QmlDebug::QmlPreviewServices;
|
|
||||||
else
|
|
||||||
m_qmlDebugServices = QmlDebug::NoQmlDebugServices;
|
|
||||||
m_localGdbServerPort = Utils::Port(5039);
|
|
||||||
QTC_CHECK(m_localGdbServerPort.isValid());
|
|
||||||
if (m_qmlDebugServices != QmlDebug::NoQmlDebugServices) {
|
|
||||||
QTcpServer server;
|
|
||||||
QTC_ASSERT(server.listen(QHostAddress::LocalHost)
|
|
||||||
|| server.listen(QHostAddress::LocalHostIPv6),
|
|
||||||
qDebug() << tr("No free ports available on host for QML debugging."));
|
|
||||||
m_qmlServer.setScheme(Utils::urlTcpScheme());
|
|
||||||
m_qmlServer.setHost(server.serverAddress().toString());
|
|
||||||
m_qmlServer.setPort(server.serverPort());
|
|
||||||
}
|
|
||||||
m_adb = AndroidConfigurations::currentConfig().adbToolPath().toString();
|
|
||||||
|
|
||||||
QString packageDir = "/data/data/" + m_androidRunnable.packageName;
|
|
||||||
m_pingFile = packageDir + "/debug-ping";
|
|
||||||
m_pongFile = "/data/local/tmp/qt/debug-pong-" + m_androidRunnable.packageName;
|
|
||||||
m_gdbserverSocket = packageDir + "/debug-socket";
|
|
||||||
const QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(
|
|
||||||
runConfig->target()->kit());
|
|
||||||
if (version && version->qtVersion() >= QtSupport::QtVersionNumber(5, 4, 0))
|
|
||||||
m_gdbserverPath = packageDir + "/lib/libgdbserver.so";
|
|
||||||
else
|
|
||||||
m_gdbserverPath = packageDir + "/lib/gdbserver";
|
|
||||||
|
|
||||||
if (version && version->qtVersion() >= QtSupport::QtVersionNumber(5, 4, 0)) {
|
|
||||||
if (qEnvironmentVariableIsSet("QTC_ANDROID_USE_FILE_HANDSHAKE"))
|
|
||||||
m_handShakeMethod = PingPongFiles;
|
|
||||||
} else {
|
|
||||||
m_handShakeMethod = PingPongFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qEnvironmentVariableIsSet("QTC_ANDROID_SOCKET_HANDSHAKE_PORT")) {
|
|
||||||
QByteArray envData = qgetenv("QTC_ANDROID_SOCKET_HANDSHAKE_PORT");
|
|
||||||
if (!envData.isEmpty()) {
|
|
||||||
bool ok = false;
|
|
||||||
int port = 0;
|
|
||||||
port = envData.toInt(&ok);
|
|
||||||
if (ok && port > 0 && port < 65535) {
|
|
||||||
m_socketHandShakePort = port;
|
|
||||||
m_customPort = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AndroidRunnerWorker::~AndroidRunnerWorker()
|
|
||||||
{
|
|
||||||
if (!m_pidFinder.isFinished())
|
|
||||||
m_pidFinder.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidRunnerWorker::forceStop()
|
|
||||||
{
|
|
||||||
runAdb({"shell", "am", "force-stop", m_androidRunnable.packageName}, nullptr, 30);
|
|
||||||
|
|
||||||
// try killing it via kill -9
|
|
||||||
const QByteArray out = Utils::SynchronousProcess()
|
|
||||||
.runBlocking(m_adb, selector() << QStringLiteral("shell") << pidScript)
|
|
||||||
.allRawOutput();
|
|
||||||
|
|
||||||
qint64 pid = extractPID(out.simplified(), m_androidRunnable.packageName);
|
|
||||||
if (pid != -1) {
|
|
||||||
adbKill(pid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidRunnerWorker::asyncStart()
|
|
||||||
{
|
|
||||||
forceStop();
|
|
||||||
|
|
||||||
// Start the logcat process before app starts.
|
|
||||||
std::unique_ptr<QProcess, decltype(&deleter)> logcatProcess(new QProcess, deleter);
|
|
||||||
connect(logcatProcess.get(), &QProcess::readyReadStandardOutput,
|
|
||||||
this, &AndroidRunnerWorker::logcatReadStandardOutput);
|
|
||||||
connect(logcatProcess.get(), &QProcess::readyReadStandardError,
|
|
||||||
this, &AndroidRunnerWorker::logcatReadStandardError);
|
|
||||||
// Its assumed that the device or avd returned by selector() is online.
|
|
||||||
logcatProcess->start(m_adb, selector() << "logcat");
|
|
||||||
|
|
||||||
QString errorMessage;
|
|
||||||
|
|
||||||
if (m_useCppDebugger)
|
|
||||||
runAdb({"shell", "rm", m_pongFile}); // Remove pong file.
|
|
||||||
|
|
||||||
for (const QString &entry: m_androidRunnable.beforeStartAdbCommands)
|
|
||||||
runAdb(entry.split(' ', QString::SkipEmptyParts));
|
|
||||||
|
|
||||||
QStringList args({"shell", "am", "start"});
|
|
||||||
args << m_androidRunnable.amStartExtraArgs;
|
|
||||||
args << "-n" << m_androidRunnable.intentName;
|
|
||||||
|
|
||||||
if (m_useCppDebugger) {
|
|
||||||
if (!runAdb({"forward", "--remove", "tcp:" + m_localGdbServerPort.toString()})){
|
|
||||||
QTC_CHECK(false);
|
|
||||||
}
|
|
||||||
if (!runAdb({"forward", "tcp:" + m_localGdbServerPort.toString(),
|
|
||||||
"localfilesystem:" + m_gdbserverSocket}, &errorMessage)) {
|
|
||||||
emit remoteProcessFinished(tr("Failed to forward C++ debugging ports. Reason: %1.").arg(errorMessage));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString pingPongSocket(m_androidRunnable.packageName + ".ping_pong_socket");
|
|
||||||
args << "-e" << "debug_ping" << "true";
|
|
||||||
if (m_handShakeMethod == SocketHandShake) {
|
|
||||||
args << "-e" << "ping_socket" << pingPongSocket;
|
|
||||||
} else if (m_handShakeMethod == PingPongFiles) {
|
|
||||||
args << "-e" << "ping_file" << m_pingFile;
|
|
||||||
args << "-e" << "pong_file" << m_pongFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString gdbserverCommand = QString::fromLatin1(adbShellAmNeedsQuotes() ? "\"%1 --multi +%2\"" : "%1 --multi +%2")
|
|
||||||
.arg(m_gdbserverPath).arg(m_gdbserverSocket);
|
|
||||||
args << "-e" << "gdbserver_command" << gdbserverCommand;
|
|
||||||
args << "-e" << "gdbserver_socket" << m_gdbserverSocket;
|
|
||||||
|
|
||||||
if (m_handShakeMethod == SocketHandShake) {
|
|
||||||
const QString port = QString("tcp:%1").arg(m_socketHandShakePort);
|
|
||||||
if (!runAdb({"forward", port, "localabstract:" + pingPongSocket}, &errorMessage)) {
|
|
||||||
emit remoteProcessFinished(tr("Failed to forward ping pong ports. Reason: %1.")
|
|
||||||
.arg(errorMessage));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_qmlDebugServices != QmlDebug::NoQmlDebugServices) {
|
|
||||||
// currently forward to same port on device and host
|
|
||||||
const QString port = QString("tcp:%1").arg(m_qmlServer.port());
|
|
||||||
if (!runAdb({"forward", port, port}, &errorMessage)) {
|
|
||||||
emit remoteProcessFinished(tr("Failed to forward QML debugging ports. Reason: %1.")
|
|
||||||
.arg(errorMessage));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
args << "-e" << "qml_debug" << "true"
|
|
||||||
<< "-e" << "qmljsdebugger"
|
|
||||||
<< QString("port:%1,block,services:%2")
|
|
||||||
.arg(m_qmlServer.port()).arg(QmlDebug::qmlDebugServices(m_qmlDebugServices));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_androidRunnable.extraAppParams.isEmpty()) {
|
|
||||||
args << "-e" << "extraappparams"
|
|
||||||
<< QString::fromLatin1(m_androidRunnable.extraAppParams.toUtf8().toBase64());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_androidRunnable.extraEnvVars.size() > 0) {
|
|
||||||
args << "-e" << "extraenvvars"
|
|
||||||
<< QString::fromLatin1(m_androidRunnable.extraEnvVars.toStringList().join('\t')
|
|
||||||
.toUtf8().toBase64());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!runAdb(args, &errorMessage)) {
|
|
||||||
emit remoteProcessFinished(tr("Failed to start the activity. Reason: %1.")
|
|
||||||
.arg(errorMessage));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_useCppDebugger) {
|
|
||||||
if (m_handShakeMethod == SocketHandShake) {
|
|
||||||
//Handling socket
|
|
||||||
bool wasSuccess = false;
|
|
||||||
const int maxAttempts = 20; //20 seconds
|
|
||||||
m_socket.reset(new QTcpSocket());
|
|
||||||
for (int i = 0; i < maxAttempts; i++) {
|
|
||||||
|
|
||||||
QThread::sleep(1); // give Android time to start process
|
|
||||||
m_socket->connectToHost(QHostAddress(QStringLiteral("127.0.0.1")),
|
|
||||||
m_socketHandShakePort);
|
|
||||||
if (!m_socket->waitForConnected())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!m_socket->waitForReadyRead()) {
|
|
||||||
m_socket->close();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QByteArray pid = m_socket->readLine();
|
|
||||||
if (pid.isEmpty()) {
|
|
||||||
m_socket->close();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
wasSuccess = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_customPort) {
|
|
||||||
// increment running port to avoid clash when using multiple
|
|
||||||
// debug sessions at the same time
|
|
||||||
m_socketHandShakePort++;
|
|
||||||
// wrap ports around to avoid overflow
|
|
||||||
if (m_socketHandShakePort == MAX_SOCKET_HANDSHAKE_PORT)
|
|
||||||
m_socketHandShakePort = MIN_SOCKET_HANDSHAKE_PORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wasSuccess) {
|
|
||||||
emit remoteProcessFinished(tr("Failed to contact debugging port."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Handling ping.
|
|
||||||
for (int i = 0; ; ++i) {
|
|
||||||
Utils::TemporaryFile tmp("pingpong");
|
|
||||||
tmp.open();
|
|
||||||
tmp.close();
|
|
||||||
|
|
||||||
runAdb({"pull", m_pingFile, tmp.fileName()});
|
|
||||||
|
|
||||||
QFile res(tmp.fileName());
|
|
||||||
const bool doBreak = res.size();
|
|
||||||
res.remove();
|
|
||||||
if (doBreak)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (i == 20) {
|
|
||||||
emit remoteProcessFinished(tr("Unable to start \"%1\".")
|
|
||||||
.arg(m_androidRunnable.packageName));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
qDebug() << "WAITING FOR " << tmp.fileName();
|
|
||||||
QThread::msleep(500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
QTC_ASSERT(!m_adbLogcatProcess, /**/);
|
|
||||||
m_adbLogcatProcess = std::move(logcatProcess);
|
|
||||||
m_pidFinder = Utils::onResultReady(Utils::runAsync(&findProcessPID, m_adb, selector(),
|
|
||||||
m_androidRunnable.packageName),
|
|
||||||
bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AndroidRunnerWorker::adbShellAmNeedsQuotes()
|
|
||||||
{
|
|
||||||
// Between Android SDK Tools version 24.3.1 and 24.3.4 the quoting
|
|
||||||
// needs for the 'adb shell am start ...' parameters changed.
|
|
||||||
// Run a test to find out on what side of the fence we live.
|
|
||||||
// The command will fail with a complaint about the "--dummy"
|
|
||||||
// option on newer SDKs, and with "No intent supplied" on older ones.
|
|
||||||
// In case the test itself fails assume a new SDK.
|
|
||||||
Utils::SynchronousProcess adb;
|
|
||||||
adb.setTimeoutS(10);
|
|
||||||
Utils::SynchronousProcessResponse response
|
|
||||||
= adb.run(m_adb, selector() << "shell" << "am" << "start"
|
|
||||||
<< "-e" << "dummy" << "dummy --dummy");
|
|
||||||
if (response.result == Utils::SynchronousProcessResponse::StartFailed
|
|
||||||
|| response.result != Utils::SynchronousProcessResponse::Finished)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const QString output = response.allOutput();
|
|
||||||
const bool oldSdk = output.contains("Error: No intent supplied");
|
|
||||||
return !oldSdk;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AndroidRunnerWorker::runAdb(const QStringList &args, QString *exitMessage, int timeoutS)
|
|
||||||
{
|
|
||||||
Utils::SynchronousProcess adb;
|
|
||||||
adb.setTimeoutS(timeoutS);
|
|
||||||
Utils::SynchronousProcessResponse response = adb.run(m_adb, selector() + args);
|
|
||||||
if (exitMessage)
|
|
||||||
*exitMessage = response.exitMessage(m_adb, timeoutS);
|
|
||||||
return response.result == Utils::SynchronousProcessResponse::Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidRunnerWorker::handleRemoteDebuggerRunning()
|
|
||||||
{
|
|
||||||
if (m_useCppDebugger) {
|
|
||||||
if (m_handShakeMethod == SocketHandShake) {
|
|
||||||
m_socket->write("OK");
|
|
||||||
m_socket->waitForBytesWritten();
|
|
||||||
m_socket->close();
|
|
||||||
} else {
|
|
||||||
Utils::TemporaryFile tmp("pingpong");
|
|
||||||
tmp.open();
|
|
||||||
|
|
||||||
runAdb({"push", tmp.fileName(), m_pongFile});
|
|
||||||
}
|
|
||||||
QTC_CHECK(m_processPID != -1);
|
|
||||||
}
|
|
||||||
// emit remoteProcessStarted(m_localGdbServerPort, m_qmlPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidRunnerWorker::asyncStop()
|
|
||||||
{
|
|
||||||
if (!m_pidFinder.isFinished())
|
|
||||||
m_pidFinder.cancel();
|
|
||||||
|
|
||||||
if (m_processPID != -1) {
|
|
||||||
forceStop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidRunnerWorker::setAndroidRunnable(const AndroidRunnable &runnable)
|
|
||||||
{
|
|
||||||
m_androidRunnable = runnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidRunnerWorker::logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError)
|
|
||||||
{
|
|
||||||
QList<QByteArray> lines = text.split('\n');
|
|
||||||
// lines always contains at least one item
|
|
||||||
lines[0].prepend(buffer);
|
|
||||||
if (!lines.last().endsWith('\n')) {
|
|
||||||
// incomplete line
|
|
||||||
buffer = lines.last();
|
|
||||||
lines.removeLast();
|
|
||||||
} else {
|
|
||||||
buffer.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString pidString = QString::number(m_processPID);
|
|
||||||
foreach (const QByteArray &msg, lines) {
|
|
||||||
const QString line = QString::fromUtf8(msg).trimmed() + QLatin1Char('\n');
|
|
||||||
if (!line.contains(pidString))
|
|
||||||
continue;
|
|
||||||
if (m_logCatRegExp.exactMatch(line)) {
|
|
||||||
// Android M
|
|
||||||
if (m_logCatRegExp.cap(1) == pidString) {
|
|
||||||
const QString &messagetype = m_logCatRegExp.cap(2);
|
|
||||||
QString output = line.mid(m_logCatRegExp.pos(2));
|
|
||||||
|
|
||||||
if (onlyError
|
|
||||||
|| messagetype == QLatin1String("F")
|
|
||||||
|| messagetype == QLatin1String("E")
|
|
||||||
|| messagetype == QLatin1String("W"))
|
|
||||||
emit remoteErrorOutput(output);
|
|
||||||
else
|
|
||||||
emit remoteOutput(output);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (onlyError || line.startsWith("F/")
|
|
||||||
|| line.startsWith("E/")
|
|
||||||
|| line.startsWith("W/"))
|
|
||||||
emit remoteErrorOutput(line);
|
|
||||||
else
|
|
||||||
emit remoteOutput(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidRunnerWorker::onProcessIdChanged(qint64 pid)
|
|
||||||
{
|
|
||||||
// Don't write to m_psProc from a different thread
|
|
||||||
QTC_ASSERT(QThread::currentThread() == thread(), return);
|
|
||||||
m_processPID = pid;
|
|
||||||
if (pid == -1) {
|
|
||||||
emit remoteProcessFinished(QLatin1String("\n\n") + tr("\"%1\" died.")
|
|
||||||
.arg(m_androidRunnable.packageName));
|
|
||||||
// App died/killed. Reset log and monitor processes.
|
|
||||||
m_adbLogcatProcess.reset();
|
|
||||||
m_psIsAlive.reset();
|
|
||||||
|
|
||||||
// Run adb commands after application quit.
|
|
||||||
for (const QString &entry: m_androidRunnable.afterFinishAdbCommands)
|
|
||||||
runAdb(entry.split(' ', QString::SkipEmptyParts));
|
|
||||||
} else {
|
|
||||||
// In debugging cases this will be funneled to the engine to actually start
|
|
||||||
// and attach gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
|
|
||||||
emit remoteProcessStarted(m_localGdbServerPort, m_qmlServer, m_processPID);
|
|
||||||
logcatReadStandardOutput();
|
|
||||||
QTC_ASSERT(!m_psIsAlive, /**/);
|
|
||||||
m_psIsAlive.reset(new QProcess);
|
|
||||||
m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels);
|
|
||||||
connect(m_psIsAlive.get(), &QProcess::readyRead, [this](){
|
|
||||||
if (!m_psIsAlive->readAll().simplified().isEmpty())
|
|
||||||
onProcessIdChanged(-1);
|
|
||||||
});
|
|
||||||
m_psIsAlive->start(m_adb, selector() << QStringLiteral("shell")
|
|
||||||
<< pidPollingScript.arg(m_processPID));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidRunnerWorker::logcatReadStandardError()
|
|
||||||
{
|
|
||||||
if (m_processPID != -1)
|
|
||||||
logcatProcess(m_adbLogcatProcess->readAllStandardError(), m_stderrBuffer, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidRunnerWorker::logcatReadStandardOutput()
|
|
||||||
{
|
|
||||||
if (m_processPID != -1)
|
|
||||||
logcatProcess(m_adbLogcatProcess->readAllStandardOutput(), m_stdoutBuffer, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidRunnerWorker::adbKill(qint64 pid)
|
|
||||||
{
|
|
||||||
runAdb({"shell", "kill", "-9", QString::number(pid)});
|
|
||||||
runAdb({"shell", "run-as", m_androidRunnable.packageName, "kill", "-9", QString::number(pid)});
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList AndroidRunnerWorker::selector() const
|
|
||||||
{
|
|
||||||
return AndroidDeviceInfo::adbSelector(m_androidRunnable.deviceSerialNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
AndroidRunner::AndroidRunner(RunControl *runControl, const QString &intentName,
|
|
||||||
const QString &extraAppParams, const Utils::Environment &extraEnvVars)
|
|
||||||
: RunWorker(runControl), m_target(runControl->runConfiguration()->target())
|
: RunWorker(runControl), m_target(runControl->runConfiguration()->target())
|
||||||
{
|
{
|
||||||
setDisplayName("AndroidRunner");
|
setDisplayName("AndroidRunner");
|
||||||
@@ -709,6 +133,7 @@ AndroidRunner::AndroidRunner(RunControl *runControl, const QString &intentName,
|
|||||||
m_androidRunnable.extraAppParams = extraAppParams;
|
m_androidRunnable.extraAppParams = extraAppParams;
|
||||||
m_androidRunnable.extraEnvVars = extraEnvVars;
|
m_androidRunnable.extraEnvVars = extraEnvVars;
|
||||||
m_androidRunnable.deviceSerialNumber = AndroidManager::deviceSerialNumber(m_target);
|
m_androidRunnable.deviceSerialNumber = AndroidManager::deviceSerialNumber(m_target);
|
||||||
|
m_androidRunnable.apiLevel = AndroidManager::deviceApiLevel(m_target);
|
||||||
|
|
||||||
if (auto androidRunConfig = qobject_cast<AndroidRunConfiguration *>(
|
if (auto androidRunConfig = qobject_cast<AndroidRunConfiguration *>(
|
||||||
runControl->runConfiguration())) {
|
runControl->runConfiguration())) {
|
||||||
@@ -720,23 +145,26 @@ AndroidRunner::AndroidRunner(RunControl *runControl, const QString &intentName,
|
|||||||
m_androidRunnable.afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd));
|
m_androidRunnable.afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_androidRunnable.apiLevel > 23)
|
||||||
m_worker.reset(new AndroidRunnerWorker(runControl, m_androidRunnable));
|
m_worker.reset(new AndroidRunnerWorker(runControl, m_androidRunnable));
|
||||||
|
else
|
||||||
|
m_worker.reset(new AndroidRunnerWorkerPreNougat(runControl, m_androidRunnable));
|
||||||
m_worker->moveToThread(&m_thread);
|
m_worker->moveToThread(&m_thread);
|
||||||
|
|
||||||
connect(this, &AndroidRunner::asyncStart, m_worker.data(), &AndroidRunnerWorker::asyncStart);
|
connect(this, &AndroidRunner::asyncStart, m_worker.data(), &AndroidRunnerWorkerBase::asyncStart);
|
||||||
connect(this, &AndroidRunner::asyncStop, m_worker.data(), &AndroidRunnerWorker::asyncStop);
|
connect(this, &AndroidRunner::asyncStop, m_worker.data(), &AndroidRunnerWorkerBase::asyncStop);
|
||||||
connect(this, &AndroidRunner::androidRunnableChanged,
|
connect(this, &AndroidRunner::androidRunnableChanged,
|
||||||
m_worker.data(), &AndroidRunnerWorker::setAndroidRunnable);
|
m_worker.data(), &AndroidRunnerWorkerBase::setAndroidRunnable);
|
||||||
connect(this, &AndroidRunner::remoteDebuggerRunning,
|
connect(this, &AndroidRunner::remoteDebuggerRunning,
|
||||||
m_worker.data(), &AndroidRunnerWorker::handleRemoteDebuggerRunning);
|
m_worker.data(), &AndroidRunnerWorkerBase::handleRemoteDebuggerRunning);
|
||||||
|
|
||||||
connect(m_worker.data(), &AndroidRunnerWorker::remoteProcessStarted,
|
connect(m_worker.data(), &AndroidRunnerWorkerBase::remoteProcessStarted,
|
||||||
this, &AndroidRunner::handleRemoteProcessStarted);
|
this, &AndroidRunner::handleRemoteProcessStarted);
|
||||||
connect(m_worker.data(), &AndroidRunnerWorker::remoteProcessFinished,
|
connect(m_worker.data(), &AndroidRunnerWorkerBase::remoteProcessFinished,
|
||||||
this, &AndroidRunner::handleRemoteProcessFinished);
|
this, &AndroidRunner::handleRemoteProcessFinished);
|
||||||
connect(m_worker.data(), &AndroidRunnerWorker::remoteOutput,
|
connect(m_worker.data(), &AndroidRunnerWorkerBase::remoteOutput,
|
||||||
this, &AndroidRunner::remoteOutput);
|
this, &AndroidRunner::remoteOutput);
|
||||||
connect(m_worker.data(), &AndroidRunnerWorker::remoteErrorOutput,
|
connect(m_worker.data(), &AndroidRunnerWorkerBase::remoteErrorOutput,
|
||||||
this, &AndroidRunner::remoteErrorOutput);
|
this, &AndroidRunner::remoteErrorOutput);
|
||||||
|
|
||||||
connect(&m_outputParser, &QmlDebug::QmlOutputParser::waitingForConnectionOnPort,
|
connect(&m_outputParser, &QmlDebug::QmlOutputParser::waitingForConnectionOnPort,
|
||||||
@@ -840,6 +268,7 @@ void AndroidRunner::launchAVD()
|
|||||||
m_target->project(), deviceAPILevel, targetArch);
|
m_target->project(), deviceAPILevel, targetArch);
|
||||||
AndroidManager::setDeviceSerialNumber(m_target, info.serialNumber);
|
AndroidManager::setDeviceSerialNumber(m_target, info.serialNumber);
|
||||||
m_androidRunnable.deviceSerialNumber = info.serialNumber;
|
m_androidRunnable.deviceSerialNumber = info.serialNumber;
|
||||||
|
m_androidRunnable.apiLevel = info.sdk;
|
||||||
emit androidRunnableChanged(m_androidRunnable);
|
emit androidRunnableChanged(m_androidRunnable);
|
||||||
if (info.isValid()) {
|
if (info.isValid()) {
|
||||||
AndroidAvdManager avdManager;
|
AndroidAvdManager avdManager;
|
||||||
@@ -872,5 +301,3 @@ void AndroidRunner::checkAVD()
|
|||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Android
|
} // namespace Android
|
||||||
|
|
||||||
#include "androidrunner.moc"
|
|
||||||
|
@@ -43,7 +43,7 @@
|
|||||||
namespace Android {
|
namespace Android {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class AndroidRunnerWorker;
|
class AndroidRunnerWorkerBase;
|
||||||
|
|
||||||
class AndroidRunner : public ProjectExplorer::RunWorker
|
class AndroidRunner : public ProjectExplorer::RunWorker
|
||||||
{
|
{
|
||||||
@@ -88,7 +88,7 @@ private:
|
|||||||
QString m_launchedAVDName;
|
QString m_launchedAVDName;
|
||||||
QThread m_thread;
|
QThread m_thread;
|
||||||
QTimer m_checkAVDTimer;
|
QTimer m_checkAVDTimer;
|
||||||
QScopedPointer<AndroidRunnerWorker> m_worker;
|
QScopedPointer<AndroidRunnerWorkerBase> m_worker;
|
||||||
QPointer<ProjectExplorer::Target> m_target;
|
QPointer<ProjectExplorer::Target> m_target;
|
||||||
Utils::Port m_gdbServerPort;
|
Utils::Port m_gdbServerPort;
|
||||||
QUrl m_qmlServer;
|
QUrl m_qmlServer;
|
||||||
|
569
src/plugins/android/androidrunnerworker.cpp
Normal file
569
src/plugins/android/androidrunnerworker.cpp
Normal file
@@ -0,0 +1,569 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2018 BogDan Vatra <bog_dan_ro@yahoo.com>
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** 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 The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "androidconfigurations.h"
|
||||||
|
#include "androidrunnerworker.h"
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
#include <debugger/debuggerrunconfigurationaspect.h>
|
||||||
|
#include <projectexplorer/target.h>
|
||||||
|
#include <qtsupport/baseqtversion.h>
|
||||||
|
#include <qtsupport/qtkitinformation.h>
|
||||||
|
#include <utils/hostosinfo.h>
|
||||||
|
#include <utils/runextensions.h>
|
||||||
|
#include <utils/synchronousprocess.h>
|
||||||
|
#include <utils/temporaryfile.h>
|
||||||
|
#include <utils/url.h>
|
||||||
|
|
||||||
|
#include <QTcpServer>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace std::placeholders;
|
||||||
|
using namespace ProjectExplorer;
|
||||||
|
|
||||||
|
namespace Android {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
|
||||||
|
static const QString pidScript = "pidof -s \"%1\"";
|
||||||
|
static const QString pidScriptPreNougat = QStringLiteral("for p in /proc/[0-9]*; "
|
||||||
|
"do cat <$p/cmdline && echo :${p##*/}; done");
|
||||||
|
static const QString pidPollingScript = QStringLiteral("while [ -d /proc/%1 ]; do sleep 1; done");
|
||||||
|
|
||||||
|
static const QString regExpLogcat = QStringLiteral("[0-9\\-]*" // date
|
||||||
|
"\\s+"
|
||||||
|
"[0-9\\-:.]*"// time
|
||||||
|
"\\s*"
|
||||||
|
"(\\d*)" // pid 1. capture
|
||||||
|
"\\s+"
|
||||||
|
"\\d*" // unknown
|
||||||
|
"\\s+"
|
||||||
|
"(\\w)" // message type 2. capture
|
||||||
|
"\\s+"
|
||||||
|
"(.*): " // source 3. capture
|
||||||
|
"(.*)" // message 4. capture
|
||||||
|
"[\\n\\r]*"
|
||||||
|
);
|
||||||
|
static int APP_START_TIMEOUT = 45000;
|
||||||
|
|
||||||
|
static bool isTimedOut(const chrono::high_resolution_clock::time_point &start,
|
||||||
|
int msecs = APP_START_TIMEOUT)
|
||||||
|
{
|
||||||
|
bool timedOut = false;
|
||||||
|
auto end = chrono::high_resolution_clock::now();
|
||||||
|
if (chrono::duration_cast<chrono::milliseconds>(end-start).count() > msecs)
|
||||||
|
timedOut = true;
|
||||||
|
return timedOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
static qint64 extractPID(const QByteArray &output, const QString &packageName)
|
||||||
|
{
|
||||||
|
qint64 pid = -1;
|
||||||
|
foreach (auto tuple, output.split('\n')) {
|
||||||
|
tuple = tuple.simplified();
|
||||||
|
if (!tuple.isEmpty()) {
|
||||||
|
auto parts = tuple.split(':');
|
||||||
|
QString commandName = QString::fromLocal8Bit(parts.first());
|
||||||
|
if (parts.length() == 2 && commandName == packageName) {
|
||||||
|
pid = parts.last().toLongLong();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void findProcessPIDPreNougat(QFutureInterface<qint64> &fi, const QString &adbPath,
|
||||||
|
QStringList selector, const QString &packageName)
|
||||||
|
{
|
||||||
|
if (packageName.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
qint64 processPID = -1;
|
||||||
|
chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now();
|
||||||
|
do {
|
||||||
|
QThread::msleep(200);
|
||||||
|
const QByteArray out = Utils::SynchronousProcess()
|
||||||
|
.runBlocking(adbPath, selector << QStringLiteral("shell") << pidScriptPreNougat)
|
||||||
|
.allRawOutput();
|
||||||
|
processPID = extractPID(out, packageName);
|
||||||
|
} while (processPID == -1 && !isTimedOut(start) && !fi.isCanceled());
|
||||||
|
|
||||||
|
if (!fi.isCanceled())
|
||||||
|
fi.reportResult(processPID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void findProcessPID(QFutureInterface<qint64> &fi, const QString &adbPath,
|
||||||
|
QStringList selector, const QString &packageName)
|
||||||
|
{
|
||||||
|
if (packageName.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
qint64 processPID = -1;
|
||||||
|
chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now();
|
||||||
|
do {
|
||||||
|
QThread::msleep(200);
|
||||||
|
const QByteArray out = Utils::SynchronousProcess()
|
||||||
|
.runBlocking(adbPath, selector << QStringLiteral("shell") << pidScript.arg(packageName))
|
||||||
|
.allRawOutput();
|
||||||
|
if (!out.isEmpty())
|
||||||
|
processPID = out.trimmed().toLongLong();
|
||||||
|
} while (processPID == -1 && !isTimedOut(start) && !fi.isCanceled());
|
||||||
|
|
||||||
|
if (!fi.isCanceled())
|
||||||
|
fi.reportResult(processPID);
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidRunnerWorkerBase::AndroidRunnerWorkerBase(RunControl *runControl, const AndroidRunnable &runnable)
|
||||||
|
: m_androidRunnable(runnable)
|
||||||
|
, m_adbLogcatProcess(nullptr, deleter)
|
||||||
|
, m_psIsAlive(nullptr, deleter)
|
||||||
|
, m_logCatRegExp(regExpLogcat)
|
||||||
|
, m_gdbServerProcess(nullptr, deleter)
|
||||||
|
, m_jdbProcess(nullptr, deleter)
|
||||||
|
|
||||||
|
{
|
||||||
|
auto runConfig = runControl->runConfiguration();
|
||||||
|
auto aspect = runConfig->extraAspect<Debugger::DebuggerRunConfigurationAspect>();
|
||||||
|
Core::Id runMode = runControl->runMode();
|
||||||
|
const bool debuggingMode = runMode == ProjectExplorer::Constants::DEBUG_RUN_MODE;
|
||||||
|
m_useCppDebugger = debuggingMode && aspect->useCppDebugger();
|
||||||
|
if (debuggingMode && aspect->useQmlDebugger())
|
||||||
|
m_qmlDebugServices = QmlDebug::QmlDebuggerServices;
|
||||||
|
else if (runMode == ProjectExplorer::Constants::QML_PROFILER_RUN_MODE)
|
||||||
|
m_qmlDebugServices = QmlDebug::QmlProfilerServices;
|
||||||
|
else if (runMode == ProjectExplorer::Constants::QML_PREVIEW_RUN_MODE)
|
||||||
|
m_qmlDebugServices = QmlDebug::QmlPreviewServices;
|
||||||
|
else
|
||||||
|
m_qmlDebugServices = QmlDebug::NoQmlDebugServices;
|
||||||
|
m_localGdbServerPort = Utils::Port(5039);
|
||||||
|
QTC_CHECK(m_localGdbServerPort.isValid());
|
||||||
|
if (m_qmlDebugServices != QmlDebug::NoQmlDebugServices) {
|
||||||
|
QTcpServer server;
|
||||||
|
QTC_ASSERT(server.listen(QHostAddress::LocalHost)
|
||||||
|
|| server.listen(QHostAddress::LocalHostIPv6),
|
||||||
|
qDebug() << tr("No free ports available on host for QML debugging."));
|
||||||
|
m_qmlServer.setScheme(Utils::urlTcpScheme());
|
||||||
|
m_qmlServer.setHost(server.serverAddress().toString());
|
||||||
|
m_qmlServer.setPort(server.serverPort());
|
||||||
|
}
|
||||||
|
m_adb = AndroidConfigurations::currentConfig().adbToolPath().toString();
|
||||||
|
m_localJdbServerPort = Utils::Port(5038);
|
||||||
|
QTC_CHECK(m_localJdbServerPort.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidRunnerWorkerBase::~AndroidRunnerWorkerBase()
|
||||||
|
{
|
||||||
|
if (m_processPID != -1)
|
||||||
|
forceStop();
|
||||||
|
|
||||||
|
if (!m_pidFinder.isFinished())
|
||||||
|
m_pidFinder.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidRunnerWorkerBase::adbShellAmNeedsQuotes()
|
||||||
|
{
|
||||||
|
// Between Android SDK Tools version 24.3.1 and 24.3.4 the quoting
|
||||||
|
// needs for the 'adb shell am start ...' parameters changed.
|
||||||
|
// Run a test to find out on what side of the fence we live.
|
||||||
|
// The command will fail with a complaint about the "--dummy"
|
||||||
|
// option on newer SDKs, and with "No intent supplied" on older ones.
|
||||||
|
// In case the test itself fails assume a new SDK.
|
||||||
|
Utils::SynchronousProcess adb;
|
||||||
|
adb.setTimeoutS(10);
|
||||||
|
Utils::SynchronousProcessResponse response
|
||||||
|
= adb.run(m_adb, selector() << "shell" << "am" << "start"
|
||||||
|
<< "-e" << "dummy" << "dummy --dummy");
|
||||||
|
if (response.result == Utils::SynchronousProcessResponse::StartFailed
|
||||||
|
|| response.result != Utils::SynchronousProcessResponse::Finished)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const QString output = response.allOutput();
|
||||||
|
const bool oldSdk = output.contains("Error: No intent supplied");
|
||||||
|
return !oldSdk;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidRunnerWorkerBase::runAdb(const QStringList &args, int timeoutS)
|
||||||
|
{
|
||||||
|
Utils::SynchronousProcess adb;
|
||||||
|
adb.setTimeoutS(timeoutS);
|
||||||
|
Utils::SynchronousProcessResponse response = adb.run(m_adb, selector() + args);
|
||||||
|
m_lastRunAdbError = response.exitMessage(m_adb, timeoutS);
|
||||||
|
m_lastRunAdbRawOutput = response.allRawOutput();
|
||||||
|
return response.result == Utils::SynchronousProcessResponse::Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidRunnerWorkerBase::adbKill(qint64 pid)
|
||||||
|
{
|
||||||
|
runAdb({"shell", "kill", "-9", QString::number(pid)});
|
||||||
|
runAdb({"shell", "run-as", m_androidRunnable.packageName, "kill", "-9", QString::number(pid)});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QStringList AndroidRunnerWorkerBase::selector() const
|
||||||
|
{
|
||||||
|
return AndroidDeviceInfo::adbSelector(m_androidRunnable.deviceSerialNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidRunnerWorkerBase::forceStop()
|
||||||
|
{
|
||||||
|
runAdb({"shell", "am", "force-stop", m_androidRunnable.packageName}, 30);
|
||||||
|
|
||||||
|
// try killing it via kill -9
|
||||||
|
const QByteArray out = Utils::SynchronousProcess()
|
||||||
|
.runBlocking(m_adb, selector() << QStringLiteral("shell") << pidScriptPreNougat)
|
||||||
|
.allRawOutput();
|
||||||
|
|
||||||
|
qint64 pid = extractPID(out.simplified(), m_androidRunnable.packageName);
|
||||||
|
if (pid != -1) {
|
||||||
|
adbKill(pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidRunnerWorkerBase::logcatReadStandardError()
|
||||||
|
{
|
||||||
|
if (m_processPID != -1)
|
||||||
|
logcatProcess(m_adbLogcatProcess->readAllStandardError(), m_stderrBuffer, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidRunnerWorkerBase::logcatReadStandardOutput()
|
||||||
|
{
|
||||||
|
if (m_processPID != -1)
|
||||||
|
logcatProcess(m_adbLogcatProcess->readAllStandardOutput(), m_stdoutBuffer, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidRunnerWorkerBase::logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError)
|
||||||
|
{
|
||||||
|
QList<QByteArray> lines = text.split('\n');
|
||||||
|
// lines always contains at least one item
|
||||||
|
lines[0].prepend(buffer);
|
||||||
|
if (!lines.last().endsWith('\n')) {
|
||||||
|
// incomplete line
|
||||||
|
buffer = lines.last();
|
||||||
|
lines.removeLast();
|
||||||
|
} else {
|
||||||
|
buffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString pidString = QString::number(m_processPID);
|
||||||
|
foreach (const QByteArray &msg, lines) {
|
||||||
|
const QString line = QString::fromUtf8(msg).trimmed() + QLatin1Char('\n');
|
||||||
|
if (!line.contains(pidString))
|
||||||
|
continue;
|
||||||
|
if (m_useCppDebugger) {
|
||||||
|
switch (m_jdbState) {
|
||||||
|
case JDBState::Idle:
|
||||||
|
if (msg.trimmed().endsWith("Sending WAIT chunk")) {
|
||||||
|
m_jdbState = JDBState::Waiting;
|
||||||
|
handleJdbWaiting();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JDBState::Waiting:
|
||||||
|
if (msg.indexOf("debugger has settled") > 0) {
|
||||||
|
m_jdbState = JDBState::Settled;
|
||||||
|
handleJdbSettled();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_logCatRegExp.exactMatch(line)) {
|
||||||
|
// Android M
|
||||||
|
if (m_logCatRegExp.cap(1) == pidString) {
|
||||||
|
const QString &messagetype = m_logCatRegExp.cap(2);
|
||||||
|
QString output = line.mid(m_logCatRegExp.pos(2));
|
||||||
|
|
||||||
|
if (onlyError
|
||||||
|
|| messagetype == QLatin1String("F")
|
||||||
|
|| messagetype == QLatin1String("E")
|
||||||
|
|| messagetype == QLatin1String("W"))
|
||||||
|
emit remoteErrorOutput(output);
|
||||||
|
else
|
||||||
|
emit remoteOutput(output);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (onlyError || line.startsWith("F/")
|
||||||
|
|| line.startsWith("E/")
|
||||||
|
|| line.startsWith("W/"))
|
||||||
|
emit remoteErrorOutput(line);
|
||||||
|
else
|
||||||
|
emit remoteOutput(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidRunnerWorkerBase::setAndroidRunnable(const AndroidRunnable &runnable)
|
||||||
|
{
|
||||||
|
m_androidRunnable = runnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidRunnerWorkerBase::asyncStart()
|
||||||
|
{
|
||||||
|
forceStop();
|
||||||
|
|
||||||
|
// Start the logcat process before app starts.
|
||||||
|
std::unique_ptr<QProcess, decltype(&deleter)> logcatProcess(new QProcess, deleter);
|
||||||
|
connect(logcatProcess.get(), &QProcess::readyReadStandardOutput,
|
||||||
|
this, &AndroidRunnerWorkerBase::logcatReadStandardOutput);
|
||||||
|
connect(logcatProcess.get(), &QProcess::readyReadStandardError,
|
||||||
|
this, &AndroidRunnerWorkerBase::logcatReadStandardError);
|
||||||
|
// Its assumed that the device or avd returned by selector() is online.
|
||||||
|
logcatProcess->start(m_adb, selector() << "logcat");
|
||||||
|
QTC_ASSERT(!m_adbLogcatProcess, /**/);
|
||||||
|
m_adbLogcatProcess = std::move(logcatProcess);
|
||||||
|
|
||||||
|
for (const QString &entry: m_androidRunnable.beforeStartAdbCommands)
|
||||||
|
runAdb(entry.split(' ', QString::SkipEmptyParts));
|
||||||
|
|
||||||
|
QStringList args({"shell", "am", "start"});
|
||||||
|
args << m_androidRunnable.amStartExtraArgs;
|
||||||
|
args << "-n" << m_androidRunnable.intentName;
|
||||||
|
if (m_useCppDebugger) {
|
||||||
|
args << "-D";
|
||||||
|
QString gdbServerSocket;
|
||||||
|
// run-as <package-name> pwd fails on API 22 so route the pwd through shell.
|
||||||
|
if (!runAdb({"shell", "run-as", m_androidRunnable.packageName, "/system/bin/sh", "-c", "pwd"})) {
|
||||||
|
emit remoteProcessFinished(tr("Failed to get process path. Reason: %1.").arg(m_lastRunAdbError));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gdbServerSocket = QString::fromUtf8(m_lastRunAdbRawOutput.trimmed()) + "/debug-socket";
|
||||||
|
|
||||||
|
QString gdbServerExecutable;
|
||||||
|
if (!runAdb({"shell", "run-as", m_androidRunnable.packageName, "ls", "lib/"})) {
|
||||||
|
emit remoteProcessFinished(tr("Failed to get process path. Reason: %1.").arg(m_lastRunAdbError));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &line: m_lastRunAdbRawOutput.split('\n')) {
|
||||||
|
if (line.indexOf("gdbserver") != -1/* || line.indexOf("lldb-server") != -1*/) {
|
||||||
|
gdbServerExecutable = QString::fromUtf8(line.trimmed());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gdbServerExecutable.isEmpty()) {
|
||||||
|
emit remoteProcessFinished(tr("Can't find C++ debugger."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
runAdb({"shell", "run-as", m_androidRunnable.packageName, "killall", gdbServerExecutable});
|
||||||
|
runAdb({"shell", "run-as", m_androidRunnable.packageName, "rm", gdbServerSocket});
|
||||||
|
std::unique_ptr<QProcess, decltype(&deleter)> gdbServerProcess(new QProcess, deleter);
|
||||||
|
gdbServerProcess->start(m_adb, selector() << "shell" << "run-as"
|
||||||
|
<< m_androidRunnable.packageName << "lib/" + gdbServerExecutable
|
||||||
|
<< "--multi" << "+" + gdbServerSocket);
|
||||||
|
if (!gdbServerProcess->waitForStarted()) {
|
||||||
|
emit remoteProcessFinished(tr("Failed to start C++ debugger."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_gdbServerProcess = std::move(gdbServerProcess);
|
||||||
|
QStringList removeForward{"forward", "--remove", "tcp:" + m_localGdbServerPort.toString()};
|
||||||
|
runAdb(removeForward);
|
||||||
|
if (!runAdb({"forward", "tcp:" + m_localGdbServerPort.toString(),
|
||||||
|
"localfilesystem:" + gdbServerSocket})) {
|
||||||
|
emit remoteProcessFinished(tr("Failed to forward C++ debugging ports. Reason: %1.").arg(m_lastRunAdbError));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_androidRunnable.afterFinishAdbCommands.push_back(removeForward.join(' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_qmlDebugServices != QmlDebug::NoQmlDebugServices) {
|
||||||
|
// currently forward to same port on device and host
|
||||||
|
const QString port = QString("tcp:%1").arg(m_qmlServer.port());
|
||||||
|
QStringList removeForward{{"forward", "--remove", port}};
|
||||||
|
runAdb(removeForward);
|
||||||
|
if (!runAdb({"forward", port, port})) {
|
||||||
|
emit remoteProcessFinished(tr("Failed to forward QML debugging ports. Reason: %1.")
|
||||||
|
.arg(m_lastRunAdbError));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_androidRunnable.afterFinishAdbCommands.push_back(removeForward.join(' '));
|
||||||
|
|
||||||
|
args << "-e" << "qml_debug" << "true"
|
||||||
|
<< "-e" << "qmljsdebugger"
|
||||||
|
<< QString("port:%1,block,services:%2")
|
||||||
|
.arg(m_qmlServer.port()).arg(QmlDebug::qmlDebugServices(m_qmlDebugServices));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_androidRunnable.extraAppParams.isEmpty()) {
|
||||||
|
args << "-e" << "extraappparams"
|
||||||
|
<< QString::fromLatin1(m_androidRunnable.extraAppParams.toUtf8().toBase64());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_androidRunnable.extraEnvVars.size() > 0) {
|
||||||
|
args << "-e" << "extraenvvars"
|
||||||
|
<< QString::fromLatin1(m_androidRunnable.extraEnvVars.toStringList().join('\t')
|
||||||
|
.toUtf8().toBase64());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!runAdb(args)) {
|
||||||
|
emit remoteProcessFinished(tr("Failed to start the activity. Reason: %1.")
|
||||||
|
.arg(m_lastRunAdbError));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidRunnerWorkerBase::asyncStop()
|
||||||
|
{
|
||||||
|
if (!m_pidFinder.isFinished())
|
||||||
|
m_pidFinder.cancel();
|
||||||
|
|
||||||
|
if (m_processPID != -1)
|
||||||
|
forceStop();
|
||||||
|
|
||||||
|
m_jdbProcess.reset();
|
||||||
|
m_gdbServerProcess.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidRunnerWorkerBase::handleRemoteDebuggerRunning()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void AndroidRunnerWorkerBase::handleJdbWaiting()
|
||||||
|
{
|
||||||
|
QStringList removeForward{"forward", "--remove", "tcp:" + m_localJdbServerPort.toString()};
|
||||||
|
runAdb(removeForward);
|
||||||
|
if (!runAdb({"forward", "tcp:" + m_localJdbServerPort.toString(),
|
||||||
|
"jdwp:" + QString::number(m_processPID)})) {
|
||||||
|
emit remoteProcessFinished(tr("Failed to forward jdb debugging ports. Reason: %1.").arg(m_lastRunAdbError));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_androidRunnable.afterFinishAdbCommands.push_back(removeForward.join(' '));
|
||||||
|
|
||||||
|
auto jdbPath = AndroidConfigurations::currentConfig().openJDKLocation().appendPath("bin");
|
||||||
|
if (Utils::HostOsInfo::isWindowsHost())
|
||||||
|
jdbPath.appendPath("jdb.exe");
|
||||||
|
else
|
||||||
|
jdbPath.appendPath("jdb");
|
||||||
|
|
||||||
|
std::unique_ptr<QProcess, decltype(&deleter)> jdbProcess(new QProcess, deleter);
|
||||||
|
jdbProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||||
|
jdbProcess->start(jdbPath.toString(), QStringList() << "-connect" <<
|
||||||
|
QString("com.sun.jdi.SocketAttach:hostname=localhost,port=%1")
|
||||||
|
.arg(m_localJdbServerPort.toString()));
|
||||||
|
if (!jdbProcess->waitForStarted()) {
|
||||||
|
emit remoteProcessFinished(tr("Failed to start JDB"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_jdbProcess = std::move(jdbProcess);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidRunnerWorkerBase::handleJdbSettled()
|
||||||
|
{
|
||||||
|
auto waitForCommand = [&]() {
|
||||||
|
for (int i= 0; i < 5 && m_jdbProcess->state() == QProcess::Running; ++i) {
|
||||||
|
m_jdbProcess->waitForReadyRead(500);
|
||||||
|
QByteArray lines = m_jdbProcess->readAll();
|
||||||
|
for (const auto &line: lines.split('\n')) {
|
||||||
|
auto msg = line.trimmed();
|
||||||
|
if (msg.startsWith(">"))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (waitForCommand()) {
|
||||||
|
m_jdbProcess->write("cont\n");
|
||||||
|
if (m_jdbProcess->waitForBytesWritten(5000) && waitForCommand()) {
|
||||||
|
m_jdbProcess->write("exit\n");
|
||||||
|
m_jdbProcess->waitForBytesWritten(5000);
|
||||||
|
if (!m_jdbProcess->waitForFinished(5000)) {
|
||||||
|
m_jdbProcess->terminate();
|
||||||
|
if (!m_jdbProcess->waitForFinished(5000)) {
|
||||||
|
m_jdbProcess->kill();
|
||||||
|
m_jdbProcess->waitForFinished();
|
||||||
|
}
|
||||||
|
} else if (m_jdbProcess->exitStatus() == QProcess::NormalExit && m_jdbProcess->exitCode() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit remoteProcessFinished(tr("Can't attach jdb to the running application").arg(m_lastRunAdbError));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidRunnerWorkerBase::onProcessIdChanged(qint64 pid)
|
||||||
|
{
|
||||||
|
// Don't write to m_psProc from a different thread
|
||||||
|
QTC_ASSERT(QThread::currentThread() == thread(), return);
|
||||||
|
m_processPID = pid;
|
||||||
|
if (pid == -1) {
|
||||||
|
emit remoteProcessFinished(QLatin1String("\n\n") + tr("\"%1\" died.")
|
||||||
|
.arg(m_androidRunnable.packageName));
|
||||||
|
// App died/killed. Reset log, monitor, jdb & gdb processes.
|
||||||
|
m_adbLogcatProcess.reset();
|
||||||
|
m_psIsAlive.reset();
|
||||||
|
m_jdbProcess.reset();
|
||||||
|
m_gdbServerProcess.reset();
|
||||||
|
|
||||||
|
// Run adb commands after application quit.
|
||||||
|
for (const QString &entry: m_androidRunnable.afterFinishAdbCommands)
|
||||||
|
runAdb(entry.split(' ', QString::SkipEmptyParts));
|
||||||
|
} else {
|
||||||
|
// In debugging cases this will be funneled to the engine to actually start
|
||||||
|
// and attach gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
|
||||||
|
emit remoteProcessStarted(m_localGdbServerPort, m_qmlServer, m_processPID);
|
||||||
|
logcatReadStandardOutput();
|
||||||
|
QTC_ASSERT(!m_psIsAlive, /**/);
|
||||||
|
m_psIsAlive.reset(new QProcess);
|
||||||
|
m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels);
|
||||||
|
connect(m_psIsAlive.get(), static_cast<void(QProcess::*)(int)>(&QProcess::finished),
|
||||||
|
this, bind(&AndroidRunnerWorkerBase::onProcessIdChanged, this, -1));
|
||||||
|
m_psIsAlive->start(m_adb, selector() << QStringLiteral("shell")
|
||||||
|
<< pidPollingScript.arg(m_processPID));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AndroidRunnerWorker::AndroidRunnerWorker(RunControl *runControl, const AndroidRunnable &runnable)
|
||||||
|
: AndroidRunnerWorkerBase(runControl, runnable)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidRunnerWorker::asyncStart()
|
||||||
|
{
|
||||||
|
AndroidRunnerWorkerBase::asyncStart();
|
||||||
|
m_pidFinder = Utils::onResultReady(Utils::runAsync(&findProcessPID, m_adb, selector(),
|
||||||
|
m_androidRunnable.packageName),
|
||||||
|
bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidRunnerWorkerPreNougat::AndroidRunnerWorkerPreNougat(RunControl *runControl, const AndroidRunnable &runnable)
|
||||||
|
: AndroidRunnerWorkerBase(runControl, runnable)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidRunnerWorkerPreNougat::asyncStart()
|
||||||
|
{
|
||||||
|
AndroidRunnerWorkerBase::asyncStart();
|
||||||
|
m_pidFinder = Utils::onResultReady(Utils::runAsync(&findProcessPIDPreNougat, m_adb, selector(),
|
||||||
|
m_androidRunnable.packageName),
|
||||||
|
bind(&AndroidRunnerWorkerPreNougat::onProcessIdChanged, this, _1));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Android
|
134
src/plugins/android/androidrunnerworker.h
Normal file
134
src/plugins/android/androidrunnerworker.h
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2018 BogDan Vatra <bog_dan_ro@yahoo.com>
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** 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 The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qmldebug/qmldebugcommandlinearguments.h>
|
||||||
|
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QTcpSocket>
|
||||||
|
|
||||||
|
#include "androidrunnable.h"
|
||||||
|
|
||||||
|
namespace ProjectExplorer {
|
||||||
|
class RunControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Android {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
const int MIN_SOCKET_HANDSHAKE_PORT = 20001;
|
||||||
|
|
||||||
|
static void deleter(QProcess *p)
|
||||||
|
{
|
||||||
|
p->terminate();
|
||||||
|
if (!p->waitForFinished(1000)) {
|
||||||
|
p->kill();
|
||||||
|
p->waitForFinished();
|
||||||
|
}
|
||||||
|
// Might get deleted from its own signal handler.
|
||||||
|
p->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
class AndroidRunnerWorkerBase : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
AndroidRunnerWorkerBase(ProjectExplorer::RunControl *runControl, const AndroidRunnable &runnable);
|
||||||
|
~AndroidRunnerWorkerBase() override;
|
||||||
|
bool adbShellAmNeedsQuotes();
|
||||||
|
bool runAdb(const QStringList &args, int timeoutS = 10);
|
||||||
|
void adbKill(qint64 pid);
|
||||||
|
QStringList selector() const;
|
||||||
|
void forceStop();
|
||||||
|
void logcatReadStandardError();
|
||||||
|
void logcatReadStandardOutput();
|
||||||
|
void logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError);
|
||||||
|
void setAndroidRunnable(const AndroidRunnable &runnable);
|
||||||
|
|
||||||
|
virtual void asyncStart();
|
||||||
|
virtual void asyncStop();
|
||||||
|
virtual void handleRemoteDebuggerRunning();
|
||||||
|
virtual void handleJdbWaiting();
|
||||||
|
virtual void handleJdbSettled();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void remoteProcessStarted(Utils::Port gdbServerPort, const QUrl &qmlServer, int pid);
|
||||||
|
void remoteProcessFinished(const QString &errString = QString());
|
||||||
|
|
||||||
|
void remoteOutput(const QString &output);
|
||||||
|
void remoteErrorOutput(const QString &output);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum class JDBState {
|
||||||
|
Idle,
|
||||||
|
Waiting,
|
||||||
|
Settled
|
||||||
|
};
|
||||||
|
virtual void onProcessIdChanged(qint64 pid);
|
||||||
|
|
||||||
|
// Create the processes and timer in the worker thread, for correct thread affinity
|
||||||
|
AndroidRunnable m_androidRunnable;
|
||||||
|
QString m_adb;
|
||||||
|
qint64 m_processPID = -1;
|
||||||
|
std::unique_ptr<QProcess, decltype(&deleter)> m_adbLogcatProcess;
|
||||||
|
std::unique_ptr<QProcess, decltype(&deleter)> m_psIsAlive;
|
||||||
|
QByteArray m_stdoutBuffer;
|
||||||
|
QByteArray m_stderrBuffer;
|
||||||
|
QRegExp m_logCatRegExp;
|
||||||
|
QFuture<qint64> m_pidFinder;
|
||||||
|
bool m_useCppDebugger = false;
|
||||||
|
QmlDebug::QmlDebugServicesPreset m_qmlDebugServices;
|
||||||
|
Utils::Port m_localGdbServerPort; // Local end of forwarded debug socket.
|
||||||
|
QUrl m_qmlServer;
|
||||||
|
QByteArray m_lastRunAdbRawOutput;
|
||||||
|
QString m_lastRunAdbError;
|
||||||
|
JDBState m_jdbState = JDBState::Idle;
|
||||||
|
Utils::Port m_localJdbServerPort;
|
||||||
|
std::unique_ptr<QProcess, decltype(&deleter)> m_gdbServerProcess;
|
||||||
|
std::unique_ptr<QProcess, decltype(&deleter)> m_jdbProcess;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class AndroidRunnerWorker : public AndroidRunnerWorkerBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
AndroidRunnerWorker(ProjectExplorer::RunControl *runControl, const AndroidRunnable &runnable);
|
||||||
|
void asyncStart() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class AndroidRunnerWorkerPreNougat : public AndroidRunnerWorkerBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
AndroidRunnerWorkerPreNougat(ProjectExplorer::RunControl *runControl, const AndroidRunnable &runnable);
|
||||||
|
void asyncStart() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Android
|
@@ -48,16 +48,20 @@ GTestOutputReader::GTestOutputReader(const QFutureInterface<TestResultPtr> &futu
|
|||||||
, m_executable(testApplication ? testApplication->program() : QString())
|
, m_executable(testApplication ? testApplication->program() : QString())
|
||||||
, m_projectFile(projectFile)
|
, m_projectFile(projectFile)
|
||||||
{
|
{
|
||||||
// on Windows abort() will result in normal termination, but exit code will be set to 3
|
|
||||||
if (Utils::HostOsInfo::isWindowsHost()) {
|
|
||||||
connect(m_testApplication,
|
connect(m_testApplication,
|
||||||
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
||||||
this, [this] (int exitCode, QProcess::ExitStatus /*exitStatus*/) {
|
this, [this] (int exitCode, QProcess::ExitStatus /*exitStatus*/) {
|
||||||
if (exitCode == 3)
|
if (exitCode == 1 && !m_description.isEmpty()) {
|
||||||
|
// TODO tr()
|
||||||
|
const QString message{"Running test(s) failed\n" + m_description
|
||||||
|
+ "\nExecutable: " + m_executable};
|
||||||
|
createAndReportResult(message, Result::MessageFatal);
|
||||||
|
}
|
||||||
|
// on Windows abort() will result in normal termination, but exit code will be set to 3
|
||||||
|
if (Utils::HostOsInfo::isWindowsHost() && exitCode == 3)
|
||||||
reportCrash();
|
reportCrash();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void GTestOutputReader::processOutput(const QByteArray &outputLine)
|
void GTestOutputReader::processOutput(const QByteArray &outputLine)
|
||||||
{
|
{
|
||||||
|
@@ -74,6 +74,14 @@ void TestOutputReader::reportCrash()
|
|||||||
m_futureInterface.reportResult(result);
|
m_futureInterface.reportResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestOutputReader::createAndReportResult(const QString &message, Result::Type type)
|
||||||
|
{
|
||||||
|
TestResultPtr result = createDefaultResult();
|
||||||
|
result->setDescription(message);
|
||||||
|
result->setResult(type);
|
||||||
|
reportResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
void TestOutputReader::reportResult(const TestResultPtr &result)
|
void TestOutputReader::reportResult(const TestResultPtr &result)
|
||||||
{
|
{
|
||||||
m_futureInterface.reportResult(result);
|
m_futureInterface.reportResult(result);
|
||||||
|
@@ -45,6 +45,7 @@ public:
|
|||||||
virtual void processOutput(const QByteArray &outputLine) = 0;
|
virtual void processOutput(const QByteArray &outputLine) = 0;
|
||||||
virtual void processStdError(const QByteArray &output);
|
virtual void processStdError(const QByteArray &output);
|
||||||
void reportCrash();
|
void reportCrash();
|
||||||
|
void createAndReportResult(const QString &message, Result::Type type);
|
||||||
bool hadValidOutput() const { return m_hadValidOutput; }
|
bool hadValidOutput() const { return m_hadValidOutput; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@@ -294,7 +294,9 @@
|
|||||||
<tabstop>removeButton</tabstop>
|
<tabstop>removeButton</tabstop>
|
||||||
<tabstop>revertButton</tabstop>
|
<tabstop>revertButton</tabstop>
|
||||||
<tabstop>description</tabstop>
|
<tabstop>description</tabstop>
|
||||||
|
<tabstop>executable</tabstop>
|
||||||
<tabstop>arguments</tabstop>
|
<tabstop>arguments</tabstop>
|
||||||
|
<tabstop>workingDirectory</tabstop>
|
||||||
<tabstop>outputBehavior</tabstop>
|
<tabstop>outputBehavior</tabstop>
|
||||||
<tabstop>errorOutputBehavior</tabstop>
|
<tabstop>errorOutputBehavior</tabstop>
|
||||||
<tabstop>environmentButton</tabstop>
|
<tabstop>environmentButton</tabstop>
|
||||||
|
@@ -477,16 +477,16 @@ void DebuggerRunTool::setCoreFileName(const QString &coreFile, bool isSnapshot)
|
|||||||
|
|
||||||
void DebuggerRunTool::appendInferiorCommandLineArgument(const QString &arg)
|
void DebuggerRunTool::appendInferiorCommandLineArgument(const QString &arg)
|
||||||
{
|
{
|
||||||
if (!m_runParameters.inferior.commandLineArguments.isEmpty())
|
QtcProcess::addArg(&m_runParameters.inferior.commandLineArguments, arg,
|
||||||
m_runParameters.inferior.commandLineArguments.append(' ');
|
device() ? device()->osType() : HostOsInfo::hostOs());
|
||||||
m_runParameters.inferior.commandLineArguments.append(arg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggerRunTool::prependInferiorCommandLineArgument(const QString &arg)
|
void DebuggerRunTool::prependInferiorCommandLineArgument(const QString &arg)
|
||||||
{
|
{
|
||||||
if (!m_runParameters.inferior.commandLineArguments.isEmpty())
|
if (!m_runParameters.inferior.commandLineArguments.isEmpty())
|
||||||
m_runParameters.inferior.commandLineArguments.prepend(' ');
|
m_runParameters.inferior.commandLineArguments.prepend(' ');
|
||||||
m_runParameters.inferior.commandLineArguments.prepend(arg);
|
m_runParameters.inferior.commandLineArguments.prepend(
|
||||||
|
QtcProcess::quoteArg(arg, device() ? device()->osType() : HostOsInfo::hostOs()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggerRunTool::addQmlServerInferiorCommandLineArgumentIfNeeded()
|
void DebuggerRunTool::addQmlServerInferiorCommandLineArgumentIfNeeded()
|
||||||
|
@@ -82,10 +82,12 @@ void QmakeAndroidRunConfiguration::updateDisplayName()
|
|||||||
const QmakeProjectManager::QmakeProFileNode *root = project->rootProjectNode();
|
const QmakeProjectManager::QmakeProFileNode *root = project->rootProjectNode();
|
||||||
if (root) {
|
if (root) {
|
||||||
const QmakeProjectManager::QmakeProFileNode *node = root->findProFileFor(proFilePath());
|
const QmakeProjectManager::QmakeProFileNode *node = root->findProFileFor(proFilePath());
|
||||||
if (node) // should always be found
|
if (node) { // should always be found
|
||||||
|
setDisplayName(node->displayName());
|
||||||
setDefaultDisplayName(node->displayName());
|
setDefaultDisplayName(node->displayName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString QmakeAndroidRunConfiguration::disabledReason() const
|
QString QmakeAndroidRunConfiguration::disabledReason() const
|
||||||
{
|
{
|
||||||
|
@@ -421,7 +421,6 @@ void QmakeProject::scheduleAsyncUpdate(QmakeProFile *file, QmakeProFile::AsyncUp
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emitParsingStarted();
|
|
||||||
file->setParseInProgressRecursive(true);
|
file->setParseInProgressRecursive(true);
|
||||||
setAllBuildConfigurationsEnabled(false);
|
setAllBuildConfigurationsEnabled(false);
|
||||||
|
|
||||||
@@ -478,9 +477,6 @@ void QmakeProject::scheduleAsyncUpdate(QmakeProFile::AsyncUpdateDelay delay)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_asyncUpdateState != Base)
|
|
||||||
emitParsingStarted();
|
|
||||||
|
|
||||||
rootProFile()->setParseInProgressRecursive(true);
|
rootProFile()->setParseInProgressRecursive(true);
|
||||||
setAllBuildConfigurationsEnabled(false);
|
setAllBuildConfigurationsEnabled(false);
|
||||||
|
|
||||||
@@ -503,12 +499,15 @@ void QmakeProject::startAsyncTimer(QmakeProFile::AsyncUpdateDelay delay)
|
|||||||
m_asyncUpdateTimer.stop();
|
m_asyncUpdateTimer.stop();
|
||||||
m_asyncUpdateTimer.setInterval(qMin(m_asyncUpdateTimer.interval(),
|
m_asyncUpdateTimer.setInterval(qMin(m_asyncUpdateTimer.interval(),
|
||||||
delay == QmakeProFile::ParseLater ? UPDATE_INTERVAL : 0));
|
delay == QmakeProFile::ParseLater ? UPDATE_INTERVAL : 0));
|
||||||
|
if (!isParsing())
|
||||||
|
emitParsingStarted();
|
||||||
m_asyncUpdateTimer.start();
|
m_asyncUpdateTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmakeProject::incrementPendingEvaluateFutures()
|
void QmakeProject::incrementPendingEvaluateFutures()
|
||||||
{
|
{
|
||||||
++m_pendingEvaluateFuturesCount;
|
++m_pendingEvaluateFuturesCount;
|
||||||
|
QTC_ASSERT(isParsing(), emitParsingStarted());
|
||||||
m_asyncUpdateFutureInterface->setProgressRange(m_asyncUpdateFutureInterface->progressMinimum(),
|
m_asyncUpdateFutureInterface->setProgressRange(m_asyncUpdateFutureInterface->progressMinimum(),
|
||||||
m_asyncUpdateFutureInterface->progressMaximum() + 1);
|
m_asyncUpdateFutureInterface->progressMaximum() + 1);
|
||||||
}
|
}
|
||||||
|
@@ -45,6 +45,7 @@
|
|||||||
#include <qmldebug/qmldebugcommandlinearguments.h>
|
#include <qmldebug/qmldebugcommandlinearguments.h>
|
||||||
|
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/qtcprocess.h>
|
||||||
#include <utils/url.h>
|
#include <utils/url.h>
|
||||||
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
@@ -253,8 +254,8 @@ LocalQmlProfilerSupport::LocalQmlProfilerSupport(QmlProfilerTool *profilerTool,
|
|||||||
else
|
else
|
||||||
QTC_CHECK(false);
|
QTC_CHECK(false);
|
||||||
|
|
||||||
QString arguments = QmlDebug::qmlDebugCommandLineArguments(QmlDebug::QmlProfilerServices,
|
QString arguments = Utils::QtcProcess::quoteArg(
|
||||||
code, true);
|
QmlDebug::qmlDebugCommandLineArguments(QmlDebug::QmlProfilerServices, code, true));
|
||||||
|
|
||||||
if (!debuggee.commandLineArguments.isEmpty())
|
if (!debuggee.commandLineArguments.isEmpty())
|
||||||
arguments += ' ' + debuggee.commandLineArguments;
|
arguments += ' ' + debuggee.commandLineArguments;
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
#include <qmlprofiler/qmlprofilertool.h>
|
#include <qmlprofiler/qmlprofilertool.h>
|
||||||
|
|
||||||
#include <utils/url.h>
|
#include <utils/url.h>
|
||||||
|
#include <utils/temporaryfile.h>
|
||||||
|
|
||||||
#include <QtTest>
|
#include <QtTest>
|
||||||
#include <QTcpServer>
|
#include <QTcpServer>
|
||||||
@@ -148,6 +149,31 @@ void LocalQmlProfilerRunnerTest::testRunner()
|
|||||||
runControl->initiateFinish();
|
runControl->initiateFinish();
|
||||||
QTRY_VERIFY(runControl.isNull());
|
QTRY_VERIFY(runControl.isNull());
|
||||||
QVERIFY(profiler.isNull());
|
QVERIFY(profiler.isNull());
|
||||||
|
|
||||||
|
debuggee.commandLineArguments = QString("-test QmlProfiler,");
|
||||||
|
serverUrl.setScheme(Utils::urlSocketScheme());
|
||||||
|
{
|
||||||
|
Utils::TemporaryFile file("file with spaces");
|
||||||
|
if (file.open())
|
||||||
|
serverUrl.setPath(file.fileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
runControl = new ProjectExplorer::RunControl(nullptr,
|
||||||
|
ProjectExplorer::Constants::QML_PROFILER_RUN_MODE);
|
||||||
|
runControl->setRunnable(debuggee);
|
||||||
|
profiler = new LocalQmlProfilerSupport(&tool, runControl, serverUrl);
|
||||||
|
connectRunner();
|
||||||
|
runControl->initiateStart();
|
||||||
|
|
||||||
|
QTRY_VERIFY_WITH_TIMEOUT(running, 30000);
|
||||||
|
QTRY_VERIFY_WITH_TIMEOUT(!running, 30000);
|
||||||
|
QCOMPARE(startCount, 4);
|
||||||
|
QCOMPARE(stopCount, 4);
|
||||||
|
QCOMPARE(runCount, 3);
|
||||||
|
|
||||||
|
runControl->initiateFinish();
|
||||||
|
QTRY_VERIFY(runControl.isNull());
|
||||||
|
QVERIFY(profiler.isNull());
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalQmlProfilerRunnerTest::testFindFreePort()
|
void LocalQmlProfilerRunnerTest::testFindFreePort()
|
||||||
|
@@ -75,11 +75,10 @@ void QnxQmlProfilerSupport::start()
|
|||||||
serverUrl.setScheme("tcp");
|
serverUrl.setScheme("tcp");
|
||||||
m_profiler->recordData("QmlServerUrl", serverUrl);
|
m_profiler->recordData("QmlServerUrl", serverUrl);
|
||||||
|
|
||||||
QString args = QmlDebug::qmlDebugTcpArguments(QmlDebug::QmlProfilerServices, qmlPort);
|
|
||||||
auto r = runnable().as<StandardRunnable>();
|
auto r = runnable().as<StandardRunnable>();
|
||||||
if (!r.commandLineArguments.isEmpty())
|
QtcProcess::addArg(&r.commandLineArguments,
|
||||||
r.commandLineArguments.append(' ');
|
QmlDebug::qmlDebugTcpArguments(QmlDebug::QmlProfilerServices, qmlPort),
|
||||||
r.commandLineArguments += args;
|
device()->osType());
|
||||||
|
|
||||||
setRunnable(r);
|
setRunnable(r);
|
||||||
|
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
#include <projectexplorer/runnables.h>
|
#include <projectexplorer/runnables.h>
|
||||||
|
|
||||||
#include <ssh/sshconnection.h>
|
#include <ssh/sshconnection.h>
|
||||||
|
#include <utils/qtcprocess.h>
|
||||||
#include <utils/url.h>
|
#include <utils/url.h>
|
||||||
|
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
@@ -66,11 +67,9 @@ void RemoteLinuxQmlToolingSupport::start()
|
|||||||
serverUrl.setPort(qmlPort.number());
|
serverUrl.setPort(qmlPort.number());
|
||||||
m_runworker->recordData("QmlServerUrl", serverUrl);
|
m_runworker->recordData("QmlServerUrl", serverUrl);
|
||||||
|
|
||||||
QString args = QmlDebug::qmlDebugTcpArguments(m_services, qmlPort);
|
|
||||||
auto r = runnable().as<StandardRunnable>();
|
auto r = runnable().as<StandardRunnable>();
|
||||||
if (!r.commandLineArguments.isEmpty())
|
QtcProcess::addArg(&r.commandLineArguments, QmlDebug::qmlDebugTcpArguments(m_services, qmlPort),
|
||||||
r.commandLineArguments.append(' ');
|
device()->osType());
|
||||||
r.commandLineArguments += args;
|
|
||||||
|
|
||||||
setRunnable(r);
|
setRunnable(r);
|
||||||
|
|
||||||
|
Submodule src/shared/qbs updated: 8da0e97f40...fee6a037b7
@@ -25,30 +25,25 @@ Second - some of the test suites/test cases expect a build of Qt 4.8.7 to be ava
|
|||||||
* Other:
|
* Other:
|
||||||
make -j<number of available cores>
|
make -j<number of available cores>
|
||||||
|
|
||||||
Third - some of the test suites/test cases expect Qt 5.3.1 (default toolchain), Qt 5.4.1 (gcc, Linux and Windows only) and Qt 5.6.1-1 (default toolchain)
|
Third - some of the test suites/test cases expect Qt versions to be installed in their default
|
||||||
installed in their default locations. On Linux/Mac this is ~/Qt5.x.1 and on Windows this is C:\Qt\Qt5.x.1. The default toolchains are gcc on Linux,
|
locations. On Linux/macOS this is ~/Qt5.x.1 and on Windows this is C:\Qt\Qt5.x.1. It's easiest to
|
||||||
clang on Mac and MSVC2010 (Qt <= 5.5) or MSVC2013 (Qt > 5.5) on Windows. It's easiest to use default installations of the official opensource Qt packages.
|
use installations of the official opensource Qt packages. Just install the Qt version for the
|
||||||
|
respective toolchain and if possible the additional module Qt Script. Do not install Qt WebEngine.
|
||||||
|
The exact versions and toolchains are:
|
||||||
|
|
||||||
On macOS it might be necessary to tweak some files of the Qt installation - depending on the installed Xcode and its installed SDKs.
|
Linux:
|
||||||
If Xcode 8+ is installed you will need to update the file
|
Qt 5.4.1 (gcc)
|
||||||
~/Qt5.3.1/5.3/clang_64/mkspecs/features/mac/default_pre.prf
|
Qt 5.6.1-1 (gcc)
|
||||||
Change the line
|
Qt 5.10.1 (gcc)
|
||||||
|
|
||||||
isEmpty($$list($$system("/usr/bin/xcrun -find xcrun 2>/dev/null")))
|
macOS:
|
||||||
|
Qt 5.6.1-1 (clang)
|
||||||
|
Qt 5.10.1 (clang)
|
||||||
|
|
||||||
to
|
Windows:
|
||||||
|
Qt 5.4.1 (gcc)
|
||||||
isEmpty($$list($$system("/usr/bin/xcrun -find xcodebuild 2>/dev/null")))
|
Qt 5.6.1-1 (MSVC2013, 32 bit)
|
||||||
|
Qt 5.10.1 (MSVC2015, 32 bit)
|
||||||
Furthermore - depending on the installed SDK it might be necessary to update the file
|
|
||||||
~/Qt5.3.1/5.3/clang_64/mkspecs/qdevice.pri
|
|
||||||
Search for the following line
|
|
||||||
|
|
||||||
!host_build:QMAKE_MAC_SDK = macosx10.8
|
|
||||||
|
|
||||||
Newer Xcode might miss the 10.8 SDK. Check
|
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs
|
|
||||||
which SDKs are installed and update the version number accordingly.
|
|
||||||
|
|
||||||
Fourth - you'll have to provide some additional repositories (and for the hooking into subprocesses even some more Squish bundles, see below).
|
Fourth - you'll have to provide some additional repositories (and for the hooking into subprocesses even some more Squish bundles, see below).
|
||||||
These additional repositories are located inside ~/squish-data or C:\Users\<user>\squish-data (depending on the OS you're on).
|
These additional repositories are located inside ~/squish-data or C:\Users\<user>\squish-data (depending on the OS you're on).
|
||||||
|
@@ -192,6 +192,7 @@
|
|||||||
:Select a Git Commit.detailsText_QPlainTextEdit {name='detailsText' type='QPlainTextEdit' visible='1' window=':Select a Git Commit_Git::Internal::ChangeSelectionDialog'}
|
:Select a Git Commit.detailsText_QPlainTextEdit {name='detailsText' type='QPlainTextEdit' visible='1' window=':Select a Git Commit_Git::Internal::ChangeSelectionDialog'}
|
||||||
:Select a Git Commit.workingDirectoryEdit_QLineEdit {type='Utils::FancyLineEdit' unnamed='1' visible='1' window=':Select a Git Commit_Git::Internal::ChangeSelectionDialog'}
|
:Select a Git Commit.workingDirectoryEdit_QLineEdit {type='Utils::FancyLineEdit' unnamed='1' visible='1' window=':Select a Git Commit_Git::Internal::ChangeSelectionDialog'}
|
||||||
:Select a Git Commit_Git::Internal::ChangeSelectionDialog {name='Git__Internal__ChangeSelectionDialog' type='Git::Internal::ChangeSelectionDialog' visible='1' windowTitle='Select a Git Commit'}
|
:Select a Git Commit_Git::Internal::ChangeSelectionDialog {name='Git__Internal__ChangeSelectionDialog' type='Git::Internal::ChangeSelectionDialog' visible='1' windowTitle='Select a Git Commit'}
|
||||||
|
:Select signal.signalList_QTreeView {container=':Go to slot.Select signal_QGroupBox' name='signalList' type='QTreeView' visible='1'}
|
||||||
:Select signal.signalList_QTreeWidget {container=':Go to slot.Select signal_QGroupBox' name='signalList' type='QTreeWidget' visible='1'}
|
:Select signal.signalList_QTreeWidget {container=':Go to slot.Select signal_QGroupBox' name='signalList' type='QTreeWidget' visible='1'}
|
||||||
:Send to Codepaster.Cancel_QPushButton {text='Cancel' type='QPushButton' unnamed='1' visible='1' window=':Send to Codepaster_CodePaster::PasteView'}
|
:Send to Codepaster.Cancel_QPushButton {text='Cancel' type='QPushButton' unnamed='1' visible='1' window=':Send to Codepaster_CodePaster::PasteView'}
|
||||||
:Send to Codepaster.Description:_QLabel {name='descriptionLabel' text='Description:' type='QLabel' visible='1' window=':Send to Codepaster_CodePaster::PasteView'}
|
:Send to Codepaster.Description:_QLabel {name='descriptionLabel' text='Description:' type='QLabel' visible='1' window=':Send to Codepaster_CodePaster::PasteView'}
|
||||||
|
@@ -101,7 +101,7 @@
|
|||||||
<value type="QString" key="PE.Profile.Icon">:///DESKTOP///</value>
|
<value type="QString" key="PE.Profile.Icon">:///DESKTOP///</value>
|
||||||
<value type="QString" key="PE.Profile.Id">{a1e860d1-c241-4abf-80fe-cf0c9f0a43b3}</value>
|
<value type="QString" key="PE.Profile.Id">{a1e860d1-c241-4abf-80fe-cf0c9f0a43b3}</value>
|
||||||
<valuelist type="QVariantList" key="PE.Profile.MutableInfo"/>
|
<valuelist type="QVariantList" key="PE.Profile.MutableInfo"/>
|
||||||
<value type="QString" key="PE.Profile.Name">Desktop 5.3.1 default</value>
|
<value type="QString" key="PE.Profile.Name">Desktop 5.10.1 default</value>
|
||||||
<value type="bool" key="PE.Profile.SDK">false</value>
|
<value type="bool" key="PE.Profile.SDK">false</value>
|
||||||
<valuelist type="QVariantList" key="PE.Profile.StickyInfo"/>
|
<valuelist type="QVariantList" key="PE.Profile.StickyInfo"/>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
<variable>QtVersion.1</variable>
|
<variable>QtVersion.1</variable>
|
||||||
<valuemap type="QVariantMap">
|
<valuemap type="QVariantMap">
|
||||||
<value type="int" key="Id">9</value>
|
<value type="int" key="Id">9</value>
|
||||||
<value type="QString" key="Name">Desktop Qt 5.6.1 (SQUISH_DEFAULT_COMPILER)</value>
|
<value type="QString" key="Name">Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER)</value>
|
||||||
<value type="QString" key="QMakePath">~/Qt5.6.1/5.6/SQUISH_DEFAULT_COMPILER/bin/qmake</value>
|
<value type="QString" key="QMakePath">~/Qt5.6.1/5.6/SQUISH_DEFAULT_COMPILER/bin/qmake</value>
|
||||||
<value type="QString" key="QtVersion.Type">Qt4ProjectManager.QtVersion.Desktop</value>
|
<value type="QString" key="QtVersion.Type">Qt4ProjectManager.QtVersion.Desktop</value>
|
||||||
<value type="bool" key="isAutodetected">false</value>
|
<value type="bool" key="isAutodetected">false</value>
|
||||||
@@ -26,8 +26,8 @@
|
|||||||
<variable>QtVersion.2</variable>
|
<variable>QtVersion.2</variable>
|
||||||
<valuemap type="QVariantMap">
|
<valuemap type="QVariantMap">
|
||||||
<value type="int" key="Id">11</value>
|
<value type="int" key="Id">11</value>
|
||||||
<value type="QString" key="Name">Desktop Qt 5.3.1 (SQUISH_DEFAULT_COMPILER)</value>
|
<value type="QString" key="Name">Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER)</value>
|
||||||
<value type="QString" key="QMakePath">~/Qt5.3.1/5.3/SQUISH_DEFAULT_COMPILER/bin/qmake</value>
|
<value type="QString" key="QMakePath">~/Qt5.10.1/5.10.1/SQUISH_DEFAULT_COMPILER/bin/qmake</value>
|
||||||
<value type="QString" key="QtVersion.Type">Qt4ProjectManager.QtVersion.Desktop</value>
|
<value type="QString" key="QtVersion.Type">Qt4ProjectManager.QtVersion.Desktop</value>
|
||||||
<value type="bool" key="isAutodetected">false</value>
|
<value type="bool" key="isAutodetected">false</value>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE QtCreatorProfiles>
|
<!DOCTYPE QtCreatorProfiles>
|
||||||
<!-- Written by QtCreator 4.0.83, 2016-07-21T11:54:33. -->
|
<!-- Written by QtCreator 4.5.1, 2018-01-10T17:26:12. -->
|
||||||
<qtcreator>
|
<qtcreator>
|
||||||
<data>
|
<data>
|
||||||
<variable>Profile.0</variable>
|
<variable>Profile.0</variable>
|
||||||
@@ -50,29 +50,6 @@
|
|||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>Profile.2</variable>
|
<variable>Profile.2</variable>
|
||||||
<valuemap type="QVariantMap">
|
|
||||||
<value type="bool" key="PE.Profile.AutoDetected">false</value>
|
|
||||||
<value type="QString" key="PE.Profile.AutoDetectionSource"></value>
|
|
||||||
<valuemap type="QVariantMap" key="PE.Profile.Data">
|
|
||||||
<value type="QString" key="Android.GdbServer.Information"></value>
|
|
||||||
<value type="QString" key="Debugger.Information">{70e26273-2c0b-4534-bbc0-eb6ca670821a}</value>
|
|
||||||
<value type="QString" key="PE.Profile.Device">Desktop Device</value>
|
|
||||||
<value type="QByteArray" key="PE.Profile.DeviceType">Desktop</value>
|
|
||||||
<value type="QString" key="PE.Profile.SysRoot"></value>
|
|
||||||
<value type="QString" key="PE.Profile.ToolChain">ProjectExplorer.ToolChain.Gcc:{c3f59b87-6997-4bd8-8067-ee04dc536371}</value>
|
|
||||||
<value type="QString" key="QtPM4.mkSpecInformation"></value>
|
|
||||||
<value type="int" key="QtSupport.QtInformation">11</value>
|
|
||||||
</valuemap>
|
|
||||||
<value type="QString" key="PE.Profile.Icon">:///DESKTOP///</value>
|
|
||||||
<value type="QString" key="PE.Profile.Id">{e91398ba-6443-4b02-b416-782a70d9df90}</value>
|
|
||||||
<valuelist type="QVariantList" key="PE.Profile.MutableInfo"/>
|
|
||||||
<value type="QString" key="PE.Profile.Name">Desktop 5.3.1 default</value>
|
|
||||||
<value type="bool" key="PE.Profile.SDK">false</value>
|
|
||||||
<valuelist type="QVariantList" key="PE.Profile.StickyInfo"/>
|
|
||||||
</valuemap>
|
|
||||||
</data>
|
|
||||||
<data>
|
|
||||||
<variable>Profile.3</variable>
|
|
||||||
<valuemap type="QVariantMap">
|
<valuemap type="QVariantMap">
|
||||||
<value type="bool" key="PE.Profile.AutoDetected">false</value>
|
<value type="bool" key="PE.Profile.AutoDetected">false</value>
|
||||||
<value type="QString" key="PE.Profile.AutoDetectionSource"></value>
|
<value type="QString" key="PE.Profile.AutoDetectionSource"></value>
|
||||||
@@ -96,7 +73,7 @@
|
|||||||
</valuemap>
|
</valuemap>
|
||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>Profile.4</variable>
|
<variable>Profile.3</variable>
|
||||||
<valuemap type="QVariantMap">
|
<valuemap type="QVariantMap">
|
||||||
<value type="bool" key="PE.Profile.AutoDetected">false</value>
|
<value type="bool" key="PE.Profile.AutoDetected">false</value>
|
||||||
<value type="QString" key="PE.Profile.AutoDetectionSource"></value>
|
<value type="QString" key="PE.Profile.AutoDetectionSource"></value>
|
||||||
@@ -119,13 +96,41 @@
|
|||||||
<valuelist type="QVariantList" key="PE.Profile.StickyInfo"/>
|
<valuelist type="QVariantList" key="PE.Profile.StickyInfo"/>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
</data>
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>Profile.4</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<value type="bool" key="PE.Profile.AutoDetected">false</value>
|
||||||
|
<value type="QString" key="PE.Profile.AutoDetectionSource"></value>
|
||||||
|
<valuemap type="QVariantMap" key="PE.Profile.Data">
|
||||||
|
<value type="QString"></value>
|
||||||
|
<value type="QString" key="Android.GdbServer.Information"></value>
|
||||||
|
<value type="QString" key="Debugger.Information">{70e26273-2c0b-4534-bbc0-eb6ca670821a}</value>
|
||||||
|
<value type="QString" key="PE.Profile.Device">Desktop Device</value>
|
||||||
|
<value type="QByteArray" key="PE.Profile.DeviceType">Desktop</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.Profile.Environment"/>
|
||||||
|
<value type="QString" key="PE.Profile.SysRoot"></value>
|
||||||
|
<valuemap type="QVariantMap" key="PE.Profile.ToolChainsV3">
|
||||||
|
<value type="QByteArray" key="C">{7bfd4fd4-e64a-417f-b10f-20602e1719d1}</value>
|
||||||
|
<value type="QByteArray" key="Cxx">{c3f59b87-6997-4bd8-8067-ee04dc536371}</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="QString" key="QtPM4.mkSpecInformation"></value>
|
||||||
|
<value type="int" key="QtSupport.QtInformation">17</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="QString" key="PE.Profile.Icon"></value>
|
||||||
|
<value type="QString" key="PE.Profile.Id">{0d32ea2d-f954-4011-8f7c-80da4977c94c}</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.Profile.MutableInfo"/>
|
||||||
|
<value type="QString" key="PE.Profile.Name">Desktop 5.10.1 default</value>
|
||||||
|
<value type="bool" key="PE.Profile.SDK">false</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.Profile.StickyInfo"/>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>Profile.Count</variable>
|
<variable>Profile.Count</variable>
|
||||||
<value type="int">5</value>
|
<value type="int">5</value>
|
||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>Profile.Default</variable>
|
<variable>Profile.Default</variable>
|
||||||
<value type="QString">{e91398ba-6443-4b02-b416-782a70d9df90}</value>
|
<value type="QString">{542217c7-ce0f-48f7-843b-d4fad339688d}</value>
|
||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>Version</variable>
|
<variable>Version</variable>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE QtCreatorQtVersions>
|
<!DOCTYPE QtCreatorQtVersions>
|
||||||
<!-- Written by QtCreator 4.0.83, 2016-07-21T11:54:33. -->
|
<!-- Written by QtCreator 4.5.1, 2018-01-10T17:13:44. -->
|
||||||
<qtcreator>
|
<qtcreator>
|
||||||
<data>
|
<data>
|
||||||
<variable>QtVersion.0</variable>
|
<variable>QtVersion.0</variable>
|
||||||
@@ -14,16 +14,6 @@
|
|||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>QtVersion.1</variable>
|
<variable>QtVersion.1</variable>
|
||||||
<valuemap type="QVariantMap">
|
|
||||||
<value type="int" key="Id">11</value>
|
|
||||||
<value type="QString" key="Name">Qt 5.3.1 (SQUISH_DEFAULT_COMPILER)</value>
|
|
||||||
<value type="QString" key="QMakePath">~/Qt5.3.1/5.3/SQUISH_DEFAULT_COMPILER/bin/qmake</value>
|
|
||||||
<value type="QString" key="QtVersion.Type">Qt4ProjectManager.QtVersion.Desktop</value>
|
|
||||||
<value type="bool" key="isAutodetected">false</value>
|
|
||||||
</valuemap>
|
|
||||||
</data>
|
|
||||||
<data>
|
|
||||||
<variable>QtVersion.2</variable>
|
|
||||||
<valuemap type="QVariantMap">
|
<valuemap type="QVariantMap">
|
||||||
<value type="int" key="Id">13</value>
|
<value type="int" key="Id">13</value>
|
||||||
<value type="QString" key="Name">Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER)</value>
|
<value type="QString" key="Name">Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER)</value>
|
||||||
@@ -33,7 +23,7 @@
|
|||||||
</valuemap>
|
</valuemap>
|
||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>QtVersion.3</variable>
|
<variable>QtVersion.2</variable>
|
||||||
<valuemap type="QVariantMap">
|
<valuemap type="QVariantMap">
|
||||||
<value type="int" key="Id">15</value>
|
<value type="int" key="Id">15</value>
|
||||||
<value type="QString" key="Name">Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER)</value>
|
<value type="QString" key="Name">Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER)</value>
|
||||||
@@ -42,6 +32,16 @@
|
|||||||
<value type="bool" key="isAutodetected">false</value>
|
<value type="bool" key="isAutodetected">false</value>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
</data>
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>QtVersion.3</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<value type="int" key="Id">17</value>
|
||||||
|
<value type="QString" key="Name">Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER)</value>
|
||||||
|
<value type="QString" key="QMakePath">~/Qt5.10.1/5.10.1/SQUISH_DEFAULT_COMPILER/bin/qmake</value>
|
||||||
|
<value type="QString" key="QtVersion.Type">Qt4ProjectManager.QtVersion.Desktop</value>
|
||||||
|
<value type="bool" key="isAutodetected">false</value>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>Version</variable>
|
<variable>Version</variable>
|
||||||
<value type="int">1</value>
|
<value type="int">1</value>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE QtCreatorProfiles>
|
<!DOCTYPE QtCreatorProfiles>
|
||||||
<!-- Written by QtCreator 4.2.2, 2017-02-10T17:44:37. -->
|
<!-- Written by QtCreator 4.5.1, 2018-01-10T14:28:58. -->
|
||||||
<qtcreator>
|
<qtcreator>
|
||||||
<data>
|
<data>
|
||||||
<variable>Profile.0</variable>
|
<variable>Profile.0</variable>
|
||||||
@@ -32,34 +32,6 @@
|
|||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>Profile.1</variable>
|
<variable>Profile.1</variable>
|
||||||
<valuemap type="QVariantMap">
|
|
||||||
<value type="bool" key="PE.Profile.AutoDetected">false</value>
|
|
||||||
<value type="QString" key="PE.Profile.AutoDetectionSource"></value>
|
|
||||||
<valuemap type="QVariantMap" key="PE.Profile.Data">
|
|
||||||
<value type="QString" key="Android.GdbServer.Information"></value>
|
|
||||||
<value type="QString" key="Debugger.Information">{1b25f20a-d584-4fb7-85b3-74dd15b82f6f}</value>
|
|
||||||
<value type="QString" key="PE.Profile.Device">Desktop Device</value>
|
|
||||||
<value type="QByteArray" key="PE.Profile.DeviceType">Desktop</value>
|
|
||||||
<valuelist type="QVariantList" key="PE.Profile.Environment"/>
|
|
||||||
<value type="QString" key="PE.Profile.SysRoot"></value>
|
|
||||||
<value type="QString" key="PE.Profile.ToolChain">ProjectExplorer.ToolChain.Msvc:{1186dad9-c485-4f69-b7e1-aff54c89ecb2}</value>
|
|
||||||
<valuemap type="QVariantMap" key="PE.Profile.ToolChains">
|
|
||||||
<value type="QByteArray" key="C">{93e707bd-236f-4d8d-917d-814aa358024b}</value>
|
|
||||||
<value type="QString" key="Cxx">ProjectExplorer.ToolChain.Msvc:{1186dad9-c485-4f69-b7e1-aff54c89ecb2}</value>
|
|
||||||
</valuemap>
|
|
||||||
<value type="QString" key="QtPM4.mkSpecInformation"></value>
|
|
||||||
<value type="int" key="QtSupport.QtInformation">20</value>
|
|
||||||
</valuemap>
|
|
||||||
<value type="QString" key="PE.Profile.Icon">:///DESKTOP///</value>
|
|
||||||
<value type="QString" key="PE.Profile.Id">{6a95566e-8372-4372-8286-ef73af7de191}</value>
|
|
||||||
<valuelist type="QVariantList" key="PE.Profile.MutableInfo"/>
|
|
||||||
<value type="QString" key="PE.Profile.Name">Desktop 5.3.1 default</value>
|
|
||||||
<value type="bool" key="PE.Profile.SDK">false</value>
|
|
||||||
<valuelist type="QVariantList" key="PE.Profile.StickyInfo"/>
|
|
||||||
</valuemap>
|
|
||||||
</data>
|
|
||||||
<data>
|
|
||||||
<variable>Profile.2</variable>
|
|
||||||
<valuemap type="QVariantMap">
|
<valuemap type="QVariantMap">
|
||||||
<value type="bool" key="PE.Profile.AutoDetected">false</value>
|
<value type="bool" key="PE.Profile.AutoDetected">false</value>
|
||||||
<value type="QString" key="PE.Profile.AutoDetectionSource"></value>
|
<value type="QString" key="PE.Profile.AutoDetectionSource"></value>
|
||||||
@@ -87,7 +59,7 @@
|
|||||||
</valuemap>
|
</valuemap>
|
||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>Profile.3</variable>
|
<variable>Profile.2</variable>
|
||||||
<valuemap type="QVariantMap">
|
<valuemap type="QVariantMap">
|
||||||
<value type="bool" key="PE.Profile.AutoDetected">false</value>
|
<value type="bool" key="PE.Profile.AutoDetected">false</value>
|
||||||
<value type="QString" key="PE.Profile.AutoDetectionSource"></value>
|
<value type="QString" key="PE.Profile.AutoDetectionSource"></value>
|
||||||
@@ -114,13 +86,41 @@
|
|||||||
<valuelist type="QVariantList" key="PE.Profile.StickyInfo"/>
|
<valuelist type="QVariantList" key="PE.Profile.StickyInfo"/>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
</data>
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>Profile.3</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<value type="bool" key="PE.Profile.AutoDetected">false</value>
|
||||||
|
<value type="QString" key="PE.Profile.AutoDetectionSource"></value>
|
||||||
|
<valuemap type="QVariantMap" key="PE.Profile.Data">
|
||||||
|
<value type="QString"></value>
|
||||||
|
<value type="QString" key="Android.GdbServer.Information"></value>
|
||||||
|
<value type="QString" key="Debugger.Information">{1b25f20a-d584-4fb7-85b3-74dd15b82f6f}</value>
|
||||||
|
<value type="QString" key="PE.Profile.Device">Desktop Device</value>
|
||||||
|
<value type="QByteArray" key="PE.Profile.DeviceType">Desktop</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.Profile.Environment"/>
|
||||||
|
<value type="QString" key="PE.Profile.SysRoot"></value>
|
||||||
|
<valuemap type="QVariantMap" key="PE.Profile.ToolChainsV3">
|
||||||
|
<value type="QByteArray" key="C">{c96cfaf3-fb8a-472b-b3c7-e94e8c490f17}</value>
|
||||||
|
<value type="QByteArray" key="Cxx">{ed856706-2a9d-4745-9d85-4e322b6f91d4}</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="QString" key="QtPM4.mkSpecInformation"></value>
|
||||||
|
<value type="int" key="QtSupport.QtInformation">26</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="QString" key="PE.Profile.Icon"></value>
|
||||||
|
<value type="QString" key="PE.Profile.Id">{37fad24d-07f3-442e-8c65-d9ded3633f7e}</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.Profile.MutableInfo"/>
|
||||||
|
<value type="QString" key="PE.Profile.Name">Desktop 5.10.1 default</value>
|
||||||
|
<value type="bool" key="PE.Profile.SDK">false</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.Profile.StickyInfo"/>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>Profile.Count</variable>
|
<variable>Profile.Count</variable>
|
||||||
<value type="int">4</value>
|
<value type="int">4</value>
|
||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>Profile.Default</variable>
|
<variable>Profile.Default</variable>
|
||||||
<value type="QString">{6a95566e-8372-4372-8286-ef73af7de191}</value>
|
<value type="QString">{f9c7858c-d167-4b78-847a-91943bd0af07}</value>
|
||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>Version</variable>
|
<variable>Version</variable>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE QtCreatorQtVersions>
|
<!DOCTYPE QtCreatorQtVersions>
|
||||||
<!-- Written by QtCreator 4.0.83, 2016-07-20T17:07:18. -->
|
<!-- Written by QtCreator 4.5.1, 2018-01-10T14:24:21. -->
|
||||||
<qtcreator>
|
<qtcreator>
|
||||||
<data>
|
<data>
|
||||||
<variable>QtVersion.0</variable>
|
<variable>QtVersion.0</variable>
|
||||||
@@ -14,16 +14,6 @@
|
|||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>QtVersion.1</variable>
|
<variable>QtVersion.1</variable>
|
||||||
<valuemap type="QVariantMap">
|
|
||||||
<value type="int" key="Id">20</value>
|
|
||||||
<value type="QString" key="Name">Qt 5.3.1 (msvc2010_opengl)</value>
|
|
||||||
<value type="QString" key="QMakePath">C:/Qt/Qt5.3.1/5.3/msvc2010_opengl/bin/qmake.exe</value>
|
|
||||||
<value type="QString" key="QtVersion.Type">Qt4ProjectManager.QtVersion.Desktop</value>
|
|
||||||
<value type="bool" key="isAutodetected">false</value>
|
|
||||||
</valuemap>
|
|
||||||
</data>
|
|
||||||
<data>
|
|
||||||
<variable>QtVersion.2</variable>
|
|
||||||
<valuemap type="QVariantMap">
|
<valuemap type="QVariantMap">
|
||||||
<value type="int" key="Id">22</value>
|
<value type="int" key="Id">22</value>
|
||||||
<value type="QString" key="Name">Qt %{Qt:Version} (mingw491_32)</value>
|
<value type="QString" key="Name">Qt %{Qt:Version} (mingw491_32)</value>
|
||||||
@@ -33,7 +23,7 @@
|
|||||||
</valuemap>
|
</valuemap>
|
||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>QtVersion.3</variable>
|
<variable>QtVersion.2</variable>
|
||||||
<valuemap type="QVariantMap">
|
<valuemap type="QVariantMap">
|
||||||
<value type="int" key="Id">24</value>
|
<value type="int" key="Id">24</value>
|
||||||
<value type="QString" key="Name">Qt %{Qt:Version} (msvc2013)</value>
|
<value type="QString" key="Name">Qt %{Qt:Version} (msvc2013)</value>
|
||||||
@@ -42,6 +32,16 @@
|
|||||||
<value type="bool" key="isAutodetected">false</value>
|
<value type="bool" key="isAutodetected">false</value>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
</data>
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>QtVersion.3</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<value type="int" key="Id">26</value>
|
||||||
|
<value type="QString" key="Name">Qt %{Qt:Version} (msvc2015)</value>
|
||||||
|
<value type="QString" key="QMakePath">C:/Qt/Qt5.10.1/5.10.1/msvc2015/bin/qmake.exe</value>
|
||||||
|
<value type="QString" key="QtVersion.Type">Qt4ProjectManager.QtVersion.Desktop</value>
|
||||||
|
<value type="bool" key="isAutodetected">false</value>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>Version</variable>
|
<variable>Version</variable>
|
||||||
<value type="int">1</value>
|
<value type="int">1</value>
|
||||||
|
@@ -4,17 +4,6 @@
|
|||||||
<qtcreator>
|
<qtcreator>
|
||||||
<data>
|
<data>
|
||||||
<variable>ToolChain.0</variable>
|
<variable>ToolChain.0</variable>
|
||||||
<valuemap type="QVariantMap">
|
|
||||||
<value type="QString" key="ProjectExplorer.MsvcToolChain.SupportedAbi">x86-windows-msvc2010-pe-32bit</value>
|
|
||||||
<value type="QString" key="ProjectExplorer.MsvcToolChain.VarsBat">c:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/vcvarsall.bat</value>
|
|
||||||
<value type="QString" key="ProjectExplorer.MsvcToolChain.VarsBatArg">x86</value>
|
|
||||||
<value type="bool" key="ProjectExplorer.ToolChain.Autodetect">true</value>
|
|
||||||
<value type="QString" key="ProjectExplorer.ToolChain.DisplayName">Microsoft Visual C++ Compiler 10.0 (x86)</value>
|
|
||||||
<value type="QString" key="ProjectExplorer.ToolChain.Id">ProjectExplorer.ToolChain.Msvc:{1186dad9-c485-4f69-b7e1-aff54c89ecb2}</value>
|
|
||||||
</valuemap>
|
|
||||||
</data>
|
|
||||||
<data>
|
|
||||||
<variable>ToolChain.1</variable>
|
|
||||||
<valuemap type="QVariantMap">
|
<valuemap type="QVariantMap">
|
||||||
<value type="QString" key="ProjectExplorer.GccToolChain.Path">C:/Qt/Qt5.4.1/Tools/mingw491_32/bin/g++.exe</value>
|
<value type="QString" key="ProjectExplorer.GccToolChain.Path">C:/Qt/Qt5.4.1/Tools/mingw491_32/bin/g++.exe</value>
|
||||||
<valuelist type="QVariantList" key="ProjectExplorer.GccToolChain.PlatformCodeGenFlags"/>
|
<valuelist type="QVariantList" key="ProjectExplorer.GccToolChain.PlatformCodeGenFlags"/>
|
||||||
@@ -29,19 +18,7 @@
|
|||||||
</valuemap>
|
</valuemap>
|
||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>ToolChain.2</variable>
|
<variable>ToolChain.1</variable>
|
||||||
<valuemap type="QVariantMap">
|
|
||||||
<value type="QString" key="ProjectExplorer.MsvcToolChain.SupportedAbi">x86-windows-msvc2010-pe-32bit</value>
|
|
||||||
<value type="QString" key="ProjectExplorer.MsvcToolChain.VarsBat">C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/vcvarsall.bat</value>
|
|
||||||
<value type="QString" key="ProjectExplorer.MsvcToolChain.VarsBatArg">x86</value>
|
|
||||||
<value type="bool" key="ProjectExplorer.ToolChain.Autodetect">true</value>
|
|
||||||
<value type="QString" key="ProjectExplorer.ToolChain.DisplayName">Microsoft Visual C++ Compiler 10.0 (x86)</value>
|
|
||||||
<value type="QString" key="ProjectExplorer.ToolChain.Id">ProjectExplorer.ToolChain.Msvc:{93e707bd-236f-4d8d-917d-814aa358024b}</value>
|
|
||||||
<value type="int" key="ProjectExplorer.ToolChain.Language">1</value>
|
|
||||||
</valuemap>
|
|
||||||
</data>
|
|
||||||
<data>
|
|
||||||
<variable>ToolChain.3</variable>
|
|
||||||
<valuemap type="QVariantMap">
|
<valuemap type="QVariantMap">
|
||||||
<value type="QString" key="ProjectExplorer.MsvcToolChain.SupportedAbi">x86-windows-msvc2013-pe-32bit</value>
|
<value type="QString" key="ProjectExplorer.MsvcToolChain.SupportedAbi">x86-windows-msvc2013-pe-32bit</value>
|
||||||
<value type="QString" key="ProjectExplorer.MsvcToolChain.VarsBat">C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/vcvarsall.bat</value>
|
<value type="QString" key="ProjectExplorer.MsvcToolChain.VarsBat">C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/vcvarsall.bat</value>
|
||||||
@@ -53,7 +30,7 @@
|
|||||||
</valuemap>
|
</valuemap>
|
||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>ToolChain.4</variable>
|
<variable>ToolChain.2</variable>
|
||||||
<valuemap type="QVariantMap">
|
<valuemap type="QVariantMap">
|
||||||
<value type="QString" key="ProjectExplorer.GccToolChain.OriginalTargetTriple">i686-w64-mingw32</value>
|
<value type="QString" key="ProjectExplorer.GccToolChain.OriginalTargetTriple">i686-w64-mingw32</value>
|
||||||
<value type="QString" key="ProjectExplorer.GccToolChain.Path">C:/Qt/Qt5.4.1/Tools/mingw491_32/bin/gcc.exe</value>
|
<value type="QString" key="ProjectExplorer.GccToolChain.Path">C:/Qt/Qt5.4.1/Tools/mingw491_32/bin/gcc.exe</value>
|
||||||
@@ -69,6 +46,32 @@
|
|||||||
<value type="int" key="ProjectExplorer.ToolChain.Language">1</value>
|
<value type="int" key="ProjectExplorer.ToolChain.Language">1</value>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
</data>
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ToolChain.3</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<value type="QString" key="ProjectExplorer.MsvcToolChain.SupportedAbi">x86-windows-msvc2015-pe-32bit</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.MsvcToolChain.VarsBat">C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/vcvarsall.bat</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.MsvcToolChain.VarsBatArg">x86</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.ToolChain.Autodetect">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ToolChain.DisplayName">Microsoft Visual C++ Compiler 14.0 (x86)</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ToolChain.Id">ProjectExplorer.ToolChain.Msvc:{c96cfaf3-fb8a-472b-b3c7-e94e8c490f17}</value>
|
||||||
|
<value type="int" key="ProjectExplorer.ToolChain.Language">1</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ToolChain.LanguageV2">C</value>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ToolChain.4</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<value type="QString" key="ProjectExplorer.MsvcToolChain.SupportedAbi">x86-windows-msvc2015-pe-32bit</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.MsvcToolChain.VarsBat">C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/vcvarsall.bat</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.MsvcToolChain.VarsBatArg">x86</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.ToolChain.Autodetect">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ToolChain.DisplayName">Microsoft Visual C++ Compiler 14.0 (x86)</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ToolChain.Id">ProjectExplorer.ToolChain.Msvc:{ed856706-2a9d-4745-9d85-4e322b6f91d4}</value>
|
||||||
|
<value type="int" key="ProjectExplorer.ToolChain.Language">2</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ToolChain.LanguageV2">Cxx</value>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
<data>
|
<data>
|
||||||
<variable>ToolChain.5</variable>
|
<variable>ToolChain.5</variable>
|
||||||
<valuemap type="QVariantMap">
|
<valuemap type="QVariantMap">
|
||||||
|
@@ -32,9 +32,9 @@ class Targets:
|
|||||||
|
|
||||||
(DESKTOP_4_8_7_DEFAULT,
|
(DESKTOP_4_8_7_DEFAULT,
|
||||||
EMBEDDED_LINUX,
|
EMBEDDED_LINUX,
|
||||||
DESKTOP_5_3_1_DEFAULT,
|
|
||||||
DESKTOP_5_4_1_GCC,
|
DESKTOP_5_4_1_GCC,
|
||||||
DESKTOP_5_6_1_DEFAULT) = ALL_TARGETS
|
DESKTOP_5_6_1_DEFAULT,
|
||||||
|
DESKTOP_5_10_1_DEFAULT) = ALL_TARGETS
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def availableTargetClasses():
|
def availableTargetClasses():
|
||||||
@@ -62,12 +62,12 @@ class Targets:
|
|||||||
return "Desktop 4.8.7 default"
|
return "Desktop 4.8.7 default"
|
||||||
elif target == Targets.EMBEDDED_LINUX:
|
elif target == Targets.EMBEDDED_LINUX:
|
||||||
return "Embedded Linux"
|
return "Embedded Linux"
|
||||||
elif target == Targets.DESKTOP_5_3_1_DEFAULT:
|
|
||||||
return "Desktop 5.3.1 default"
|
|
||||||
elif target == Targets.DESKTOP_5_4_1_GCC:
|
elif target == Targets.DESKTOP_5_4_1_GCC:
|
||||||
return "Desktop 5.4.1 GCC"
|
return "Desktop 5.4.1 GCC"
|
||||||
elif target == Targets.DESKTOP_5_6_1_DEFAULT:
|
elif target == Targets.DESKTOP_5_6_1_DEFAULT:
|
||||||
return "Desktop 5.6.1 default"
|
return "Desktop 5.6.1 default"
|
||||||
|
elif target == Targets.DESKTOP_5_10_1_DEFAULT:
|
||||||
|
return "Desktop 5.10.1 default"
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ class Targets:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getDefaultKit():
|
def getDefaultKit():
|
||||||
return Targets.DESKTOP_5_3_1_DEFAULT
|
return Targets.DESKTOP_5_6_1_DEFAULT
|
||||||
|
|
||||||
# this class holds some constants for easier usage inside the Projects view
|
# this class holds some constants for easier usage inside the Projects view
|
||||||
class ProjectSettings:
|
class ProjectSettings:
|
||||||
@@ -169,7 +169,7 @@ class Qt5Path:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getPaths(pathSpec):
|
def getPaths(pathSpec):
|
||||||
qt5targets = [Targets.DESKTOP_5_3_1_DEFAULT, Targets.DESKTOP_5_6_1_DEFAULT]
|
qt5targets = [Targets.DESKTOP_5_6_1_DEFAULT, Targets.DESKTOP_5_10_1_DEFAULT]
|
||||||
if platform.system() != 'Darwin':
|
if platform.system() != 'Darwin':
|
||||||
qt5targets.append(Targets.DESKTOP_5_4_1_GCC)
|
qt5targets.append(Targets.DESKTOP_5_4_1_GCC)
|
||||||
if pathSpec == Qt5Path.DOCS:
|
if pathSpec == Qt5Path.DOCS:
|
||||||
@@ -223,20 +223,20 @@ class Qt5Path:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def examplesPath(target):
|
def examplesPath(target):
|
||||||
qtMinorVersion, qtPatchVersion = Qt5Path.getQtMinorAndPatchVersion(target)
|
qtMinorVersion, qtPatchVersion = Qt5Path.getQtMinorAndPatchVersion(target)
|
||||||
if qtMinorVersion == 2:
|
if qtMinorVersion < 10:
|
||||||
path = "examples"
|
|
||||||
else:
|
|
||||||
path = "Examples/Qt-5.%d" % qtMinorVersion
|
path = "Examples/Qt-5.%d" % qtMinorVersion
|
||||||
|
else:
|
||||||
|
path = "Examples/Qt-5.%d.%d" % (qtMinorVersion, qtPatchVersion)
|
||||||
|
|
||||||
return os.path.join(Qt5Path.__createPlatformQtPath__(qtMinorVersion), path)
|
return os.path.join(Qt5Path.__createPlatformQtPath__(qtMinorVersion), path)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def docsPath(target):
|
def docsPath(target):
|
||||||
qtMinorVersion, qtPatchVersion = Qt5Path.getQtMinorAndPatchVersion(target)
|
qtMinorVersion, qtPatchVersion = Qt5Path.getQtMinorAndPatchVersion(target)
|
||||||
if qtMinorVersion == 2:
|
if qtMinorVersion < 10:
|
||||||
path = "doc"
|
|
||||||
else:
|
|
||||||
path = "Docs/Qt-5.%d" % qtMinorVersion
|
path = "Docs/Qt-5.%d" % qtMinorVersion
|
||||||
|
else:
|
||||||
|
path = "Docs/Qt-5.%d.%d" % (qtMinorVersion, qtPatchVersion)
|
||||||
|
|
||||||
return os.path.join(Qt5Path.__createPlatformQtPath__(qtMinorVersion), path)
|
return os.path.join(Qt5Path.__createPlatformQtPath__(qtMinorVersion), path)
|
||||||
|
|
||||||
|
@@ -315,7 +315,7 @@ def createNewQtQuickUI(workingDir, qtVersion = "5.6"):
|
|||||||
|
|
||||||
return projectName
|
return projectName
|
||||||
|
|
||||||
def createNewQmlExtension(workingDir, targets=[Targets.DESKTOP_5_3_1_DEFAULT]):
|
def createNewQmlExtension(workingDir, targets=[Targets.DESKTOP_5_6_1_DEFAULT]):
|
||||||
available = __createProjectOrFileSelectType__(" Library", "Qt Quick 2 Extension Plugin")
|
available = __createProjectOrFileSelectType__(" Library", "Qt Quick 2 Extension Plugin")
|
||||||
if workingDir == None:
|
if workingDir == None:
|
||||||
workingDir = tempDir()
|
workingDir = tempDir()
|
||||||
@@ -639,10 +639,11 @@ def __getSupportedPlatforms__(text, templateName, getAsStrings=False):
|
|||||||
version = res.group("version")
|
version = res.group("version")
|
||||||
else:
|
else:
|
||||||
version = None
|
version = None
|
||||||
if 'only available with Qt 5.6' in text:
|
if templateName.startswith("Qt Quick Application - "):
|
||||||
result = [Targets.DESKTOP_5_6_1_DEFAULT]
|
if templateName == "Qt Quick Application - Empty":
|
||||||
elif 'available with Qt 5.7 and later' in text:
|
result = [Targets.DESKTOP_5_6_1_DEFAULT, Targets.DESKTOP_5_10_1_DEFAULT]
|
||||||
result = [] # FIXME we have currently no Qt5.7+ available in predefined settings
|
else:
|
||||||
|
result = [Targets.DESKTOP_5_10_1_DEFAULT]
|
||||||
elif 'Supported Platforms' in text:
|
elif 'Supported Platforms' in text:
|
||||||
supports = text[text.find('Supported Platforms'):].split(":")[1].strip().split(" ")
|
supports = text[text.find('Supported Platforms'):].split(":")[1].strip().split(" ")
|
||||||
result = []
|
result = []
|
||||||
@@ -651,7 +652,7 @@ def __getSupportedPlatforms__(text, templateName, getAsStrings=False):
|
|||||||
result.append(Targets.DESKTOP_4_8_7_DEFAULT)
|
result.append(Targets.DESKTOP_4_8_7_DEFAULT)
|
||||||
if platform.system() in ("Linux", "Darwin"):
|
if platform.system() in ("Linux", "Darwin"):
|
||||||
result.append(Targets.EMBEDDED_LINUX)
|
result.append(Targets.EMBEDDED_LINUX)
|
||||||
result.extend([Targets.DESKTOP_5_3_1_DEFAULT, Targets.DESKTOP_5_6_1_DEFAULT])
|
result.extend([Targets.DESKTOP_5_6_1_DEFAULT, Targets.DESKTOP_5_10_1_DEFAULT])
|
||||||
if platform.system() != 'Darwin':
|
if platform.system() != 'Darwin':
|
||||||
result.append(Targets.DESKTOP_5_4_1_GCC)
|
result.append(Targets.DESKTOP_5_4_1_GCC)
|
||||||
elif 'Platform independent' in text:
|
elif 'Platform independent' in text:
|
||||||
|
@@ -165,7 +165,7 @@ def main():
|
|||||||
with TestSection(getCodeModelString(useClang)):
|
with TestSection(getCodeModelString(useClang)):
|
||||||
if not startCreator(useClang):
|
if not startCreator(useClang):
|
||||||
continue
|
continue
|
||||||
openQmakeProject(examplePath, [Targets.DESKTOP_5_3_1_DEFAULT])
|
openQmakeProject(examplePath, [Targets.DESKTOP_5_6_1_DEFAULT])
|
||||||
checkCodeModelSettings(useClang)
|
checkCodeModelSettings(useClang)
|
||||||
if not openDocument("cplusplus-tools.Sources.main\\.cpp"):
|
if not openDocument("cplusplus-tools.Sources.main\\.cpp"):
|
||||||
earlyExit("Failed to open main.cpp.")
|
earlyExit("Failed to open main.cpp.")
|
||||||
|
@@ -80,7 +80,7 @@ def main():
|
|||||||
if not startedWithoutPluginError():
|
if not startedWithoutPluginError():
|
||||||
return
|
return
|
||||||
# open example project
|
# open example project
|
||||||
openQmakeProject(examplePath, [Targets.DESKTOP_5_3_1_DEFAULT])
|
openQmakeProject(examplePath, [Targets.DESKTOP_5_6_1_DEFAULT])
|
||||||
# open qml file
|
# open qml file
|
||||||
openDocument("animation.Resources.animation\\.qrc./animation.basics.color-animation\\.qml")
|
openDocument("animation.Resources.animation\\.qrc./animation.basics.color-animation\\.qml")
|
||||||
# get editor
|
# get editor
|
||||||
|
@@ -123,7 +123,7 @@ def main():
|
|||||||
for p in proFiles:
|
for p in proFiles:
|
||||||
removePackagingDirectory(os.path.dirname(p))
|
removePackagingDirectory(os.path.dirname(p))
|
||||||
examplesLineEdit = waitForObject(search %(expect[1][0], expect[1][1]))
|
examplesLineEdit = waitForObject(search %(expect[1][0], expect[1][1]))
|
||||||
example = openExample(examplesLineEdit, "address book", "Address Book.*",
|
example = openExample(examplesLineEdit, "address book", "(0000 )?Address Book.*",
|
||||||
"Address Book Example")
|
"Address Book Example")
|
||||||
if example is not None:
|
if example is not None:
|
||||||
# close second example application
|
# close second example application
|
||||||
|
@@ -108,7 +108,7 @@ def performTest(workingDir, projectName, targetCount, availableConfigs):
|
|||||||
colMean, colMedian, colLongest, colShortest) = range(2, 11)
|
colMean, colMedian, colLongest, colShortest) = range(2, 11)
|
||||||
model = waitForObject(":Events.QmlProfilerEventsTable_QmlProfiler::"
|
model = waitForObject(":Events.QmlProfilerEventsTable_QmlProfiler::"
|
||||||
"Internal::QmlProfilerStatisticsMainView").model()
|
"Internal::QmlProfilerStatisticsMainView").model()
|
||||||
compareEventsTab(model, "events_qt5.tsv")
|
compareEventsTab(model, "events_qt%s.tsv" % qtVersion)
|
||||||
test.compare(dumpItems(model, column=colPercent)[0], '100.00 %')
|
test.compare(dumpItems(model, column=colPercent)[0], '100.00 %')
|
||||||
# cannot run following test on colShortest (unstable)
|
# cannot run following test on colShortest (unstable)
|
||||||
for i in [colTotal, colMean, colMedian, colLongest]:
|
for i in [colTotal, colMean, colMedian, colLongest]:
|
||||||
|
10
tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt5.10.1.tsv
vendored
Normal file
10
tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt5.10.1.tsv
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
"0" "1" "6" "11"
|
||||||
|
"<program>" "" "1" "Main Program"
|
||||||
|
"main.qml:15" "Handling Signal" "2" "onTriggered: { runCount += 1; var i; for (i = 1; i < 2500; ++i) { var j = i * i; console.log(j); } }"
|
||||||
|
"main.qml:15" "JavaScript" "2" "onTriggered"
|
||||||
|
"main.qml:4" "Creating" "2" "QtQuick.Window/Window"
|
||||||
|
"main.qml:0" "Compiling" "1" "main.qml"
|
||||||
|
"main.qml:10" "Creating" "2" "QtQuick/Timer"
|
||||||
|
"main.qml:14" "Binding" "3" "running: runCount < 2"
|
||||||
|
"main.qml:14" "JavaScript" "3" "expression for running"
|
||||||
|
"<bytecode>" "Binding" "1" "Source code not available"
|
|
@@ -27,7 +27,6 @@ source("../../shared/qtcreator.py")
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
global tmpSettingsDir, availableBuildSystems
|
global tmpSettingsDir, availableBuildSystems
|
||||||
qtVersionsForQuick = ["5.6"]
|
|
||||||
availableBuildSystems = ["qmake", "Qbs"]
|
availableBuildSystems = ["qmake", "Qbs"]
|
||||||
if which("cmake"):
|
if which("cmake"):
|
||||||
availableBuildSystems.append("CMake")
|
availableBuildSystems.append("CMake")
|
||||||
@@ -72,6 +71,7 @@ def main():
|
|||||||
template = current.values()[0]
|
template = current.values()[0]
|
||||||
displayedPlatforms = __createProject__(category, template)
|
displayedPlatforms = __createProject__(category, template)
|
||||||
if template.startswith("Qt Quick Application - "):
|
if template.startswith("Qt Quick Application - "):
|
||||||
|
qtVersionsForQuick = ["5.6", "5.10"] if template == "Qt Quick Application - Empty" else ["5.10"]
|
||||||
for counter, qtVersion in enumerate(qtVersionsForQuick):
|
for counter, qtVersion in enumerate(qtVersionsForQuick):
|
||||||
def additionalFunc(displayedPlatforms, qtVersion):
|
def additionalFunc(displayedPlatforms, qtVersion):
|
||||||
requiredQtVersion = __createProjectHandleQtQuickSelection__(qtVersion)
|
requiredQtVersion = __createProjectHandleQtQuickSelection__(qtVersion)
|
||||||
@@ -128,9 +128,7 @@ def handleBuildSystemVerifyKits(category, template, kits, displayedPlatforms,
|
|||||||
test.log("Using build system '%s'" % buildSystem)
|
test.log("Using build system '%s'" % buildSystem)
|
||||||
selectFromCombo(combo, buildSystem)
|
selectFromCombo(combo, buildSystem)
|
||||||
clickButton(waitForObject(":Next_QPushButton"))
|
clickButton(waitForObject(":Next_QPushButton"))
|
||||||
if (template.startswith("Qt Quick Application - ")
|
if template == "Qt Quick Application - Scroll":
|
||||||
and template != "Qt Quick Application - Empty"):
|
|
||||||
test.warning("No suitable Qt version available for '%s'" % template)
|
|
||||||
clickButton(waitForObject(":Next_QPushButton"))
|
clickButton(waitForObject(":Next_QPushButton"))
|
||||||
elif specialHandlingFunc:
|
elif specialHandlingFunc:
|
||||||
specialHandlingFunc(displayedPlatforms, *args)
|
specialHandlingFunc(displayedPlatforms, *args)
|
||||||
|
@@ -188,7 +188,8 @@ def __getExpectedCompilers__():
|
|||||||
compilers.extend(findAllFilesInPATH("*g++*"))
|
compilers.extend(findAllFilesInPATH("*g++*"))
|
||||||
compilers.extend(findAllFilesInPATH("*gcc*"))
|
compilers.extend(findAllFilesInPATH("*gcc*"))
|
||||||
if platform.system() == 'Darwin':
|
if platform.system() == 'Darwin':
|
||||||
xcodeClang = getOutputFromCmdline(["xcrun", "--find", "clang++"]).strip("\n")
|
for compilerExe in ('clang++', 'clang'):
|
||||||
|
xcodeClang = getOutputFromCmdline(["xcrun", "--find", compilerExe]).strip("\n")
|
||||||
if xcodeClang and os.path.exists(xcodeClang) and xcodeClang not in expected:
|
if xcodeClang and os.path.exists(xcodeClang) and xcodeClang not in expected:
|
||||||
expected.append(xcodeClang)
|
expected.append(xcodeClang)
|
||||||
for compiler in compilers:
|
for compiler in compilers:
|
||||||
|
@@ -64,7 +64,7 @@ def main():
|
|||||||
openGeneralMessages()
|
openGeneralMessages()
|
||||||
# Verify messages appear once, from using default kit before configuring
|
# Verify messages appear once, from using default kit before configuring
|
||||||
generalMessages = str(waitForObject(":Qt Creator_Core::OutputWindow").plainText)
|
generalMessages = str(waitForObject(":Qt Creator_Core::OutputWindow").plainText)
|
||||||
test.compare(generalMessages.count("Project MESSAGE: Cannot build Qt Creator with Qt version 5.3.1."), 1,
|
test.compare(generalMessages.count("Project MESSAGE: Cannot build Qt Creator with Qt version 5.6.1."), 2,
|
||||||
"Warning about outdated Qt shown?")
|
"Warning about outdated Qt shown?")
|
||||||
test.compare(generalMessages.count("Project ERROR: Use at least Qt 5.6.2."), 2,
|
test.compare(generalMessages.count("Project ERROR: Use at least Qt 5.6.2."), 2,
|
||||||
"Minimum Qt version shown (once when parsing with default kit, once with selected)?")
|
"Minimum Qt version shown (once when parsing with default kit, once with selected)?")
|
||||||
|
@@ -53,6 +53,8 @@ def main():
|
|||||||
["Resources", "adding.qrc"],
|
["Resources", "adding.qrc"],
|
||||||
["QML", "example.qml"]]:
|
["QML", "example.qml"]]:
|
||||||
filenames = ["ABCD" + filename.upper(), "abcd" + filename.lower(), "test", "TEST", filename]
|
filenames = ["ABCD" + filename.upper(), "abcd" + filename.lower(), "test", "TEST", filename]
|
||||||
|
if (filename.endswith(".qrc") and JIRA.isBugStillOpen(20101)):
|
||||||
|
filenames.remove("ABCD" + filename.upper())
|
||||||
previous = filenames[-1]
|
previous = filenames[-1]
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
tempFiletype = filetype
|
tempFiletype = filetype
|
||||||
|
@@ -39,7 +39,7 @@ def main():
|
|||||||
"Verifying window title contains created session name.")
|
"Verifying window title contains created session name.")
|
||||||
checkWelcomePage(sessionName, True)
|
checkWelcomePage(sessionName, True)
|
||||||
for project in projects:
|
for project in projects:
|
||||||
openQmakeProject(project, [Targets.DESKTOP_5_3_1_DEFAULT])
|
openQmakeProject(project, [Targets.DESKTOP_5_6_1_DEFAULT])
|
||||||
progressBarWait(20000)
|
progressBarWait(20000)
|
||||||
checkNavigator(52, "Verifying whether all projects have been opened.")
|
checkNavigator(52, "Verifying whether all projects have been opened.")
|
||||||
openDocument("animation.Resources.animation\\.qrc./animation.basics.animators\\.qml")
|
openDocument("animation.Resources.animation\\.qrc./animation.basics.animators\\.qml")
|
||||||
|
@@ -39,7 +39,7 @@ def main():
|
|||||||
startApplication("qtcreator" + SettingsPath)
|
startApplication("qtcreator" + SettingsPath)
|
||||||
if not startedWithoutPluginError():
|
if not startedWithoutPluginError():
|
||||||
return
|
return
|
||||||
openQmakeProject(os.path.join(templateDir, proFile), [Targets.DESKTOP_5_3_1_DEFAULT])
|
openQmakeProject(os.path.join(templateDir, proFile), [Targets.DESKTOP_5_6_1_DEFAULT])
|
||||||
qmlFiles = [treebase + "focus\\.qml", treebase + "Core.ListMenu\\.qml"]
|
qmlFiles = [treebase + "focus\\.qml", treebase + "Core.ListMenu\\.qml"]
|
||||||
checkOutlineFor(qmlFiles)
|
checkOutlineFor(qmlFiles)
|
||||||
testModify()
|
testModify()
|
||||||
|
@@ -35,14 +35,8 @@ def main():
|
|||||||
# using a temporary directory won't mess up a potentially existing
|
# using a temporary directory won't mess up a potentially existing
|
||||||
workingDir = tempDir()
|
workingDir = tempDir()
|
||||||
projectName = createNewQtQuickUI(workingDir, qtVersion)
|
projectName = createNewQtQuickUI(workingDir, qtVersion)
|
||||||
kit = Targets.getStringForTarget(Targets.DESKTOP_5_6_1_DEFAULT)
|
|
||||||
if addAndActivateKit(Targets.DESKTOP_5_6_1_DEFAULT):
|
|
||||||
quick = "2.6"
|
quick = "2.6"
|
||||||
else:
|
qmlViewer = modifyRunSettingsForHookIntoQtQuickUI(1, 0, workingDir, projectName, 11223, quick)
|
||||||
test.fatal("Failed to activate kit %s" % kit)
|
|
||||||
continue
|
|
||||||
test.log("Running project Qt Quick UI Prototype (%s)" % kit)
|
|
||||||
qmlViewer = modifyRunSettingsForHookIntoQtQuickUI(2, 1, workingDir, projectName, 11223, quick)
|
|
||||||
if qmlViewer!=None:
|
if qmlViewer!=None:
|
||||||
qmlViewerPath = os.path.dirname(qmlViewer)
|
qmlViewerPath = os.path.dirname(qmlViewer)
|
||||||
qmlViewer = os.path.basename(qmlViewer)
|
qmlViewer = os.path.basename(qmlViewer)
|
||||||
|
@@ -29,7 +29,7 @@ def main():
|
|||||||
startApplication("qtcreator" + SettingsPath)
|
startApplication("qtcreator" + SettingsPath)
|
||||||
if not startedWithoutPluginError():
|
if not startedWithoutPluginError():
|
||||||
return
|
return
|
||||||
for target in [Targets.DESKTOP_5_6_1_DEFAULT, Targets.DESKTOP_5_3_1_DEFAULT]:
|
for target in [Targets.DESKTOP_5_6_1_DEFAULT, Targets.DESKTOP_5_10_1_DEFAULT]:
|
||||||
# using a temporary directory won't mess up a potentially existing
|
# using a temporary directory won't mess up a potentially existing
|
||||||
createNewQmlExtension(tempDir(), [target])
|
createNewQmlExtension(tempDir(), [target])
|
||||||
# wait for parsing to complete
|
# wait for parsing to complete
|
||||||
|
@@ -39,7 +39,8 @@ def main():
|
|||||||
":FormEditorStack_qdesigner_internal::FormWindow", 20, widgets[current], Qt.CopyAction)
|
":FormEditorStack_qdesigner_internal::FormWindow", 20, widgets[current], Qt.CopyAction)
|
||||||
connections = []
|
connections = []
|
||||||
for record in testData.dataset("connections.tsv"):
|
for record in testData.dataset("connections.tsv"):
|
||||||
connections.append([testData.field(record, col) for col in ["widget", "signal", "slot"]])
|
connections.append([testData.field(record, col) for col in ["widget", "baseclass",
|
||||||
|
"signal", "slot"]])
|
||||||
for con in connections:
|
for con in connections:
|
||||||
selectFromLocator("mainwindow.ui")
|
selectFromLocator("mainwindow.ui")
|
||||||
openContextMenu(waitForObject(con[0]), 5, 5, 0)
|
openContextMenu(waitForObject(con[0]), 5, 5, 0)
|
||||||
@@ -49,13 +50,21 @@ def main():
|
|||||||
waitFor("macHackActivateContextMenuItem('Go to slot...', con[0])", 6000)
|
waitFor("macHackActivateContextMenuItem('Go to slot...', con[0])", 6000)
|
||||||
else:
|
else:
|
||||||
activateItem(waitForObjectItem("{type='QMenu' unnamed='1' visible='1'}", "Go to slot..."))
|
activateItem(waitForObjectItem("{type='QMenu' unnamed='1' visible='1'}", "Go to slot..."))
|
||||||
waitForObjectItem(":Select signal.signalList_QTreeWidget", con[1])
|
try:
|
||||||
clickItem(":Select signal.signalList_QTreeWidget", con[1], 5, 5, 0, Qt.LeftButton)
|
# Creator built with Qt <= 5.9
|
||||||
|
signalWidgetObject = waitForObject(":Select signal.signalList_QTreeWidget", 5000)
|
||||||
|
signalName = con[2]
|
||||||
|
except:
|
||||||
|
# Creator built with Qt >= 5.10
|
||||||
|
signalWidgetObject = waitForObject(":Select signal.signalList_QTreeView")
|
||||||
|
signalName = con[1] + "." + con[2]
|
||||||
|
waitForObjectItem(signalWidgetObject, signalName)
|
||||||
|
clickItem(signalWidgetObject, signalName, 5, 5, 0, Qt.LeftButton)
|
||||||
clickButton(waitForObject(":Go to slot.OK_QPushButton"))
|
clickButton(waitForObject(":Go to slot.OK_QPushButton"))
|
||||||
editor = waitForObject(":Qt Creator_CppEditor::Internal::CPPEditorWidget")
|
editor = waitForObject(":Qt Creator_CppEditor::Internal::CPPEditorWidget")
|
||||||
type(editor, "<Up>")
|
type(editor, "<Up>")
|
||||||
type(editor, "<Up>")
|
type(editor, "<Up>")
|
||||||
test.verify(waitFor('str(lineUnderCursor(editor)).strip() == con[2]', 1000),
|
test.verify(waitFor('str(lineUnderCursor(editor)).strip() == con[3]', 1000),
|
||||||
'Comparing line "%s" to expected "%s"' % (lineUnderCursor(editor), con[2]))
|
'Comparing line "%s" to expected "%s"' % (lineUnderCursor(editor), con[3]))
|
||||||
invokeMenuItem("File", "Save All")
|
invokeMenuItem("File", "Save All")
|
||||||
invokeMenuItem("File", "Exit")
|
invokeMenuItem("File", "Exit")
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
"widget" "signal" "slot"
|
"widget" "baseclass" "signal" "slot"
|
||||||
":FormEditorStack.PushButton_QPushButton" "clicked()" "void MainWindow::on_pushButton_clicked()"
|
":FormEditorStack.PushButton_QPushButton" "QAbstractButton" "clicked()" "void MainWindow::on_pushButton_clicked()"
|
||||||
":FormEditorStack.CheckBox_QCheckBox" "toggled(bool)" "void MainWindow::on_checkBox_toggled(bool checked)"
|
":FormEditorStack.CheckBox_QCheckBox" "QAbstractButton" "toggled(bool)" "void MainWindow::on_checkBox_toggled(bool checked)"
|
||||||
":FormEditorStack.centralWidget_QDesignerWidget" "destroyed()" "void MainWindow::on_MainWindow_destroyed()"
|
":FormEditorStack.centralWidget_QDesignerWidget" "QObject" "destroyed()" "void MainWindow::on_MainWindow_destroyed()"
|
||||||
|
|
Reference in New Issue
Block a user