forked from qt-creator/qt-creator
It's conceptionally const, and would be useful to use as such, but the current implementations often modifie mutable state. To ease the transition pain, rename the previous non-const version to addToLayoutImpl() and call it after const_cast'ing. Change-Id: Ic8ca38e40f5df69c3d11121f96ea37648cf4feb7 Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
255 lines
8.6 KiB
C++
255 lines
8.6 KiB
C++
// Copyright (C) 2020 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
#include "webassemblyrunconfiguration.h"
|
|
|
|
#include "webassemblyconstants.h"
|
|
#include "webassemblytr.h"
|
|
|
|
#include <projectexplorer/buildconfiguration.h>
|
|
#include <projectexplorer/buildsystem.h>
|
|
#include <projectexplorer/devicesupport/devicemanager.h>
|
|
#include <projectexplorer/devicesupport/deviceusedportsgatherer.h>
|
|
#include <projectexplorer/project.h>
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
|
#include <projectexplorer/runconfigurationaspects.h>
|
|
#include <projectexplorer/runconfiguration.h>
|
|
#include <projectexplorer/runcontrol.h>
|
|
#include <projectexplorer/target.h>
|
|
|
|
#include <utils/layoutbuilder.h>
|
|
#include <utils/qtcprocess.h>
|
|
#include <utils/qtcassert.h>
|
|
|
|
#include <QComboBox>
|
|
#include <QTextStream>
|
|
|
|
using namespace ProjectExplorer;
|
|
using namespace Utils;
|
|
|
|
namespace WebAssembly::Internal {
|
|
|
|
WebBrowserEntries parseEmrunOutput(const QByteArray &output)
|
|
{
|
|
WebBrowserEntries result;
|
|
QTextStream ts(output);
|
|
QString line;
|
|
static const QRegularExpression regExp(R"( - (.*):\s*(.*))"); // ' - firefox: Mozilla Firefox'
|
|
// ^__1__^ ^______2______^
|
|
while (ts.readLineInto(&line)) {
|
|
const QRegularExpressionMatch match = regExp.match(line);
|
|
if (match.hasMatch())
|
|
result.push_back({match.captured(1), match.captured(2)});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static FilePath pythonInterpreter(const Environment &env)
|
|
{
|
|
const QString emsdkPythonEnvVarKey("EMSDK_PYTHON");
|
|
if (env.hasKey(emsdkPythonEnvVarKey))
|
|
return FilePath::fromUserInput(env.value(emsdkPythonEnvVarKey));
|
|
|
|
// FIXME: Centralize addPythonsFromPath() from the Python plugin and use that
|
|
for (const char *interpreterCandidate : {"python3", "python", "python2"}) {
|
|
const FilePath interpereter = env.searchInPath(QLatin1String(interpreterCandidate));
|
|
if (interpereter.isExecutableFile())
|
|
return interpereter;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
static CommandLine emrunCommand(const Target *target,
|
|
const QString &buildKey,
|
|
const QString &browser,
|
|
const QString &port)
|
|
{
|
|
if (BuildConfiguration *bc = target->activeBuildConfiguration()) {
|
|
const Environment env = bc->environment();
|
|
const FilePath emrun = env.searchInPath("emrun");
|
|
const FilePath emrunPy = emrun.absolutePath().pathAppended(emrun.baseName() + ".py");
|
|
const FilePath targetPath = bc->buildSystem()->buildTarget(buildKey).targetFilePath;
|
|
const FilePath html = targetPath.absolutePath() / (targetPath.baseName() + ".html");
|
|
|
|
QStringList args(emrunPy.path());
|
|
if (!browser.isEmpty()) {
|
|
args.append("--browser");
|
|
args.append(browser);
|
|
}
|
|
args.append("--port");
|
|
args.append(port);
|
|
args.append("--no_emrun_detect");
|
|
args.append("--serve_after_close");
|
|
args.append(html.toString());
|
|
|
|
return CommandLine(pythonInterpreter(env), args);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
static const char BROWSER_KEY[] = "WASM.WebBrowserSelectionAspect.Browser";
|
|
|
|
static WebBrowserEntries emrunBrowsers(Target *target)
|
|
{
|
|
WebBrowserEntries result;
|
|
result.append(qMakePair(QString(), Tr::tr("Default Browser")));
|
|
if (auto bc = target->activeBuildConfiguration()) {
|
|
const Environment environment = bc->environment();
|
|
const FilePath emrunPath = environment.searchInPath("emrun");
|
|
|
|
Process browserLister;
|
|
browserLister.setEnvironment(environment);
|
|
browserLister.setCommand({emrunPath, {"--list_browsers"}});
|
|
browserLister.start();
|
|
|
|
if (browserLister.waitForFinished())
|
|
result.append(parseEmrunOutput(browserLister.rawStdOut()));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
class WebBrowserSelectionAspect : public BaseAspect
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
WebBrowserSelectionAspect(AspectContainer *container)
|
|
: BaseAspect(container)
|
|
{}
|
|
|
|
void setTarget(Target *target)
|
|
{
|
|
m_availableBrowsers = emrunBrowsers(target);
|
|
if (!m_availableBrowsers.isEmpty()) {
|
|
const int defaultIndex = qBound(0, m_availableBrowsers.count() - 1, 1);
|
|
m_currentBrowser = m_availableBrowsers.at(defaultIndex).first;
|
|
}
|
|
setDisplayName(Tr::tr("Web Browser"));
|
|
setId("WebBrowserAspect");
|
|
setSettingsKey("RunConfiguration.WebBrowser");
|
|
|
|
addDataExtractor(this, &WebBrowserSelectionAspect::currentBrowser, &Data::currentBrowser);
|
|
}
|
|
|
|
void addToLayoutImpl(Layouting::Layout &parent) override
|
|
{
|
|
QTC_CHECK(!m_webBrowserComboBox);
|
|
m_webBrowserComboBox = new QComboBox;
|
|
for (const WebBrowserEntry &be : m_availableBrowsers)
|
|
m_webBrowserComboBox->addItem(be.second, be.first);
|
|
m_webBrowserComboBox->setCurrentIndex(m_webBrowserComboBox->findData(m_currentBrowser));
|
|
connect(m_webBrowserComboBox, &QComboBox::currentIndexChanged, this, [this] {
|
|
m_currentBrowser = m_webBrowserComboBox->currentData().toString();
|
|
emit changed();
|
|
});
|
|
parent.addItems({Tr::tr("Web browser:"), m_webBrowserComboBox});
|
|
}
|
|
|
|
void fromMap(const Store &map) override
|
|
{
|
|
if (!m_availableBrowsers.isEmpty())
|
|
m_currentBrowser = map.value(BROWSER_KEY, m_availableBrowsers.first().first).toString();
|
|
}
|
|
|
|
void toMap(Store &map) const override
|
|
{
|
|
map.insert(BROWSER_KEY, m_currentBrowser);
|
|
}
|
|
|
|
QString currentBrowser() const { return m_currentBrowser; }
|
|
|
|
struct Data : BaseAspect::Data
|
|
{
|
|
QString currentBrowser;
|
|
};
|
|
|
|
private:
|
|
QComboBox *m_webBrowserComboBox = nullptr;
|
|
QString m_currentBrowser;
|
|
WebBrowserEntries m_availableBrowsers;
|
|
};
|
|
|
|
// Runs a webassembly application via emscripten's "emrun" tool
|
|
// https://emscripten.org/docs/compiling/Running-html-files-with-emrun.html
|
|
class EmrunRunConfiguration : public RunConfiguration
|
|
{
|
|
public:
|
|
EmrunRunConfiguration(Target *target, Id id)
|
|
: RunConfiguration(target, id)
|
|
{
|
|
webBrowser.setTarget(target);
|
|
|
|
effectiveEmrunCall.setLabelText(Tr::tr("Effective emrun call:"));
|
|
effectiveEmrunCall.setDisplayStyle(StringAspect::TextEditDisplay);
|
|
effectiveEmrunCall.setReadOnly(true);
|
|
|
|
setUpdater([this, target] {
|
|
effectiveEmrunCall.setValue(emrunCommand(target,
|
|
buildKey(),
|
|
webBrowser.currentBrowser(),
|
|
"<port>").toUserOutput());
|
|
});
|
|
|
|
connect(&webBrowser, &BaseAspect::changed, this, &RunConfiguration::update);
|
|
connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update);
|
|
}
|
|
|
|
private:
|
|
WebBrowserSelectionAspect webBrowser{this};
|
|
StringAspect effectiveEmrunCall{this};
|
|
};
|
|
|
|
class EmrunRunWorker : public SimpleTargetRunner
|
|
{
|
|
public:
|
|
EmrunRunWorker(RunControl *runControl)
|
|
: SimpleTargetRunner(runControl)
|
|
{
|
|
auto portsGatherer = new PortsGatherer(runControl);
|
|
addStartDependency(portsGatherer);
|
|
|
|
setStartModifier([this, runControl, portsGatherer] {
|
|
const QString browserId =
|
|
runControl->aspectData<WebBrowserSelectionAspect>()->currentBrowser;
|
|
setCommandLine(emrunCommand(runControl->target(),
|
|
runControl->buildKey(),
|
|
browserId,
|
|
QString::number(portsGatherer->findEndPoint().port())));
|
|
setEnvironment(runControl->buildEnvironment());
|
|
});
|
|
}
|
|
};
|
|
|
|
// Factories
|
|
|
|
class EmrunRunConfigurationFactory final : public ProjectExplorer::RunConfigurationFactory
|
|
{
|
|
public:
|
|
EmrunRunConfigurationFactory()
|
|
{
|
|
registerRunConfiguration<EmrunRunConfiguration>(Constants::WEBASSEMBLY_RUNCONFIGURATION_EMRUN);
|
|
addSupportedTargetDeviceType(Constants::WEBASSEMBLY_DEVICE_TYPE);
|
|
}
|
|
};
|
|
|
|
class EmrunRunWorkerFactory final : public ProjectExplorer::RunWorkerFactory
|
|
{
|
|
public:
|
|
EmrunRunWorkerFactory()
|
|
{
|
|
setProduct<EmrunRunWorker>();
|
|
addSupportedRunMode(ProjectExplorer::Constants::NORMAL_RUN_MODE);
|
|
addSupportedRunConfig(Constants::WEBASSEMBLY_RUNCONFIGURATION_EMRUN);
|
|
}
|
|
};
|
|
|
|
void setupEmrunRunSupport()
|
|
{
|
|
static EmrunRunConfigurationFactory theEmrunRunConfigurationFactory;
|
|
static EmrunRunWorkerFactory theEmrunRunWorkerFactory;
|
|
}
|
|
|
|
} // Webassembly::Internal
|
|
|
|
#include "webassemblyrunconfiguration.moc"
|