1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "winrtrunnerhelper.h"
27 
28 #include "winrtconstants.h"
29 #include "winrtrunconfiguration.h"
30 
31 #include <coreplugin/idocument.h>
32 
33 #include <projectexplorer/buildtargetinfo.h>
34 #include <projectexplorer/devicesupport/idevice.h>
35 #include <projectexplorer/buildconfiguration.h>
36 #include <projectexplorer/project.h>
37 #include <projectexplorer/runcontrol.h>
38 #include <projectexplorer/target.h>
39 
40 #include <qtsupport/baseqtversion.h>
41 #include <qtsupport/qtkitinformation.h>
42 #include <utils/qtcprocess.h>
43 
44 #include <QDir>
45 
46 using namespace ProjectExplorer;
47 
48 using namespace WinRt;
49 using namespace WinRt::Internal;
50 
WinRtRunnerHelper(ProjectExplorer::RunWorker * runWorker,QString * errorMessage)51 WinRtRunnerHelper::WinRtRunnerHelper(ProjectExplorer::RunWorker *runWorker, QString *errorMessage)
52     : QObject(runWorker)
53     , m_worker(runWorker)
54 {
55     auto runControl = runWorker->runControl();
56 
57     m_device = runWorker->device().dynamicCast<const WinRtDevice>();
58 
59     const QtSupport::BaseQtVersion *qt = QtSupport::QtKitAspect::qtVersion(runControl->kit());
60     if (!qt) {
61         *errorMessage = tr("The current kit has no Qt version.");
62         return;
63     }
64 
65     m_runnerFilePath = qt->hostBinPath().toString() + QStringLiteral("/winrtrunner.exe");
66     if (!QFile::exists(m_runnerFilePath)) {
67         *errorMessage = tr("Cannot find winrtrunner.exe in \"%1\".").arg(
68                     QDir::toNativeSeparators(qt->hostBinPath().toString()));
69         return;
70     }
71 
72     m_executableFilePath = runControl->targetFilePath().toString();
73 
74     if (m_executableFilePath.isEmpty()) {
75         *errorMessage = tr("Cannot determine the executable file path for \"%1\".")
76                 .arg(runControl->projectFilePath().toUserOutput());
77         return;
78     }
79 
80     // ### we should not need to append ".exe" here.
81     if (!m_executableFilePath.endsWith(QLatin1String(".exe")))
82         m_executableFilePath += QStringLiteral(".exe");
83 
84 
85     bool loopbackExemptClient = false;
86     bool loopbackExemptServer = false;
87     if (auto aspect = runControl->aspect<ArgumentsAspect>())
88         m_arguments = aspect->arguments(runControl->macroExpander());
89     if (auto aspect = runControl->aspect<UninstallAfterStopAspect>())
90         m_uninstallAfterStop = aspect->value();
91     if (auto aspect = runControl->aspect<LoopbackExemptClientAspect>())
92         loopbackExemptClient = aspect->value();
93     if (auto aspect = runControl->aspect<LoopbackExemptServerAspect>())
94         loopbackExemptServer = aspect->value();
95     if (loopbackExemptClient && loopbackExemptServer)
96         m_loopbackArguments = QStringList{"--loopbackexempt", "clientserver"};
97     else if (loopbackExemptClient)
98         m_loopbackArguments = QStringList{"--loopbackexempt", "client"};
99     else if (loopbackExemptServer)
100         m_loopbackArguments = QStringList{"--loopbackexempt", "server"};
101 }
102 
appendMessage(const QString & message,Utils::OutputFormat format)103 void WinRtRunnerHelper::appendMessage(const QString &message, Utils::OutputFormat format)
104 {
105     QTC_ASSERT(m_worker, return);
106     m_worker->appendMessage(message, format);
107 }
108 
debug(const QString & debuggerExecutable,const QString & debuggerArguments)109 void WinRtRunnerHelper::debug(const QString &debuggerExecutable, const QString &debuggerArguments)
110 {
111     m_debuggerExecutable = debuggerExecutable;
112     m_debuggerArguments = debuggerArguments;
113     startWinRtRunner(Debug);
114 }
115 
start()116 void WinRtRunnerHelper::start()
117 {
118     startWinRtRunner(Start);
119 }
120 
stop()121 void WinRtRunnerHelper::stop()
122 {
123     if (m_process)
124         m_process->interrupt();
125     else
126         startWinRtRunner(Stop);
127 }
128 
waitForStarted(int msecs)129 bool WinRtRunnerHelper::waitForStarted(int msecs)
130 {
131     QTC_ASSERT(m_process, return false);
132     return m_process->waitForStarted(msecs);
133 }
134 
onProcessReadyReadStdOut()135 void WinRtRunnerHelper::onProcessReadyReadStdOut()
136 {
137     QTC_ASSERT(m_process, return);
138     appendMessage(QString::fromLocal8Bit(m_process->readAllStandardOutput()), Utils::StdOutFormat);
139 }
140 
onProcessReadyReadStdErr()141 void WinRtRunnerHelper::onProcessReadyReadStdErr()
142 {
143     QTC_ASSERT(m_process, return);
144     appendMessage(QString::fromLocal8Bit(m_process->readAllStandardError()), Utils::StdErrFormat);
145 }
146 
onProcessFinished()147 void WinRtRunnerHelper::onProcessFinished()
148 {
149     QTC_ASSERT(m_process, return);
150     m_process->disconnect();
151     m_process->deleteLater();
152     m_process = nullptr;
153     emit finished();
154 }
155 
onProcessError(QProcess::ProcessError processError)156 void WinRtRunnerHelper::onProcessError(QProcess::ProcessError processError)
157 {
158     QTC_ASSERT(m_process, return);
159     appendMessage(tr("Error while executing the WinRT Runner Tool: %1\n").arg(
160                       m_process->errorString()), Utils::ErrorMessageFormat);
161     m_process->disconnect();
162     m_process->deleteLater();
163     m_process = nullptr;
164     emit error(processError);
165 }
166 
startWinRtRunner(const RunConf & conf)167 void WinRtRunnerHelper::startWinRtRunner(const RunConf &conf)
168 {
169     using namespace Utils;
170     CommandLine cmdLine(FilePath::fromString(m_runnerFilePath), {});
171     if (m_device) {
172         cmdLine.addArg("--device");
173         cmdLine.addArg(QString::number(m_device->deviceId()));
174     }
175 
176     QtcProcess *process = nullptr;
177     bool connectProcess = false;
178 
179     switch (conf) {
180     case Debug:
181         cmdLine.addArg("--debug");
182         cmdLine.addArg(m_debuggerExecutable);
183         if (!m_debuggerArguments.isEmpty()) {
184             cmdLine.addArg("--debugger-arguments");
185             cmdLine.addArg(m_debuggerArguments);
186         }
187         Q_FALLTHROUGH();
188     case Start:
189         cmdLine.addArgs({"--start", "--stop", "--wait", "0"});
190         connectProcess = true;
191         QTC_ASSERT(!m_process, m_process->deleteLater());
192         m_process = new QtcProcess(this);
193         process = m_process;
194         break;
195     case Stop:
196         cmdLine.addArg("--stop");
197         process = new QtcProcess(this);
198         break;
199     }
200 
201     if (m_device->type() == Constants::WINRT_DEVICE_TYPE_LOCAL)
202         cmdLine.addArgs({"--profile", "appx"});
203     else if (m_device->type() == Constants::WINRT_DEVICE_TYPE_PHONE ||
204              m_device->type() == Constants::WINRT_DEVICE_TYPE_EMULATOR)
205         cmdLine.addArgs({"--profile", "appxphone"});
206 
207     cmdLine.addArgs(m_loopbackArguments);
208     cmdLine.addArg(m_executableFilePath);
209     cmdLine.addArgs(m_arguments, CommandLine::Raw);
210 
211     appendMessage(cmdLine.toUserOutput(), NormalMessageFormat);
212 
213     if (connectProcess) {
214         connect(process, &QtcProcess::started, this, &WinRtRunnerHelper::started);
215         connect(process, &QtcProcess::finished, this, &WinRtRunnerHelper::onProcessFinished);
216         connect(process, &QtcProcess::errorOccurred, this, &WinRtRunnerHelper::onProcessError);
217         connect(process, &QtcProcess::readyReadStandardOutput, this, &WinRtRunnerHelper::onProcessReadyReadStdOut);
218         connect(process, &QtcProcess::readyReadStandardError, this, &WinRtRunnerHelper::onProcessReadyReadStdErr);
219     }
220 
221     process->setUseCtrlCStub(true);
222     process->setCommand(cmdLine);
223     process->setEnvironment(m_worker->runControl()->buildEnvironment());
224     process->setWorkingDirectory(QFileInfo(m_executableFilePath).absolutePath());
225     process->start();
226 }
227