2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2019 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2019-07-22 14:39:01 +02:00
|
|
|
|
2019-10-01 13:16:17 +02:00
|
|
|
#include "pythonrunconfiguration.h"
|
|
|
|
|
|
2022-03-16 09:35:02 +01:00
|
|
|
#include "pyside.h"
|
2023-11-01 15:05:03 +01:00
|
|
|
#include "pythonbuildconfiguration.h"
|
2019-07-22 14:39:01 +02:00
|
|
|
#include "pythonconstants.h"
|
2023-11-01 15:05:03 +01:00
|
|
|
#include "pythoneditor.h"
|
2023-11-01 09:42:48 +01:00
|
|
|
#include "pythonkitaspect.h"
|
2021-12-13 14:19:30 +01:00
|
|
|
#include "pythonlanguageclient.h"
|
2019-07-22 14:39:01 +02:00
|
|
|
#include "pythonproject.h"
|
|
|
|
|
#include "pythonsettings.h"
|
2022-07-15 11:49:34 +02:00
|
|
|
#include "pythontr.h"
|
2019-07-22 14:39:01 +02:00
|
|
|
|
|
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2023-11-01 15:05:03 +01:00
|
|
|
#include <coreplugin/icore.h>
|
2019-07-22 14:39:01 +02:00
|
|
|
|
2023-04-25 15:32:10 +02:00
|
|
|
#include <extensionsystem/pluginmanager.h>
|
|
|
|
|
|
2022-03-30 14:42:33 +02:00
|
|
|
#include <projectexplorer/buildsteplist.h>
|
2019-11-15 15:44:45 +01:00
|
|
|
#include <projectexplorer/buildsystem.h>
|
2022-10-18 10:30:06 +02:00
|
|
|
#include <projectexplorer/devicesupport/idevice.h>
|
2023-08-11 09:18:56 +02:00
|
|
|
#include <projectexplorer/kitaspects.h>
|
2019-07-22 14:39:01 +02:00
|
|
|
#include <projectexplorer/runconfigurationaspects.h>
|
|
|
|
|
#include <projectexplorer/target.h>
|
|
|
|
|
#include <projectexplorer/taskhub.h>
|
|
|
|
|
|
2019-10-01 13:16:17 +02:00
|
|
|
#include <texteditor/textdocument.h>
|
|
|
|
|
|
2020-09-18 12:11:40 +02:00
|
|
|
#include <utils/aspects.h>
|
2019-07-22 14:39:01 +02:00
|
|
|
#include <utils/fileutils.h>
|
2023-04-04 13:39:56 +02:00
|
|
|
#include <utils/futuresynchronizer.h>
|
2020-09-18 12:11:40 +02:00
|
|
|
#include <utils/layoutbuilder.h>
|
2019-07-22 14:39:01 +02:00
|
|
|
#include <utils/outputformatter.h>
|
2023-11-01 15:05:03 +01:00
|
|
|
#include <utils/qtcassert.h>
|
2019-07-22 14:39:01 +02:00
|
|
|
#include <utils/theme/theme.h>
|
|
|
|
|
|
|
|
|
|
#include <QComboBox>
|
|
|
|
|
#include <QPlainTextEdit>
|
|
|
|
|
#include <QPushButton>
|
|
|
|
|
|
|
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
using namespace Utils;
|
|
|
|
|
|
2022-07-15 11:49:34 +02:00
|
|
|
namespace Python::Internal {
|
2019-07-22 14:39:01 +02:00
|
|
|
|
2020-04-14 15:28:44 +02:00
|
|
|
class PythonOutputLineParser : public OutputLineParser
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2020-04-14 15:28:44 +02:00
|
|
|
PythonOutputLineParser()
|
2019-07-22 14:39:01 +02:00
|
|
|
// Note that moc dislikes raw string literals.
|
|
|
|
|
: filePattern("^(\\s*)(File \"([^\"]+)\", line (\\d+), .*$)")
|
|
|
|
|
{
|
|
|
|
|
TaskHub::clearTasks(PythonErrorTaskCategory);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2020-04-14 15:28:44 +02:00
|
|
|
Result handleLine(const QString &text, OutputFormat format) final
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
2020-03-18 12:56:35 +01:00
|
|
|
if (!m_inTraceBack) {
|
|
|
|
|
m_inTraceBack = format == StdErrFormat
|
|
|
|
|
&& text.startsWith("Traceback (most recent call last):");
|
2020-04-09 17:47:01 +02:00
|
|
|
if (m_inTraceBack)
|
2020-03-19 16:00:37 +01:00
|
|
|
return Status::InProgress;
|
|
|
|
|
return Status::NotHandled;
|
2020-03-18 12:56:35 +01:00
|
|
|
}
|
|
|
|
|
|
2022-07-15 11:49:34 +02:00
|
|
|
const Id category(PythonErrorTaskCategory);
|
2020-03-18 12:56:35 +01:00
|
|
|
const QRegularExpressionMatch match = filePattern.match(text);
|
|
|
|
|
if (match.hasMatch()) {
|
2020-04-09 17:47:01 +02:00
|
|
|
const LinkSpec link(match.capturedStart(2), match.capturedLength(2), match.captured(2));
|
2020-03-18 12:56:35 +01:00
|
|
|
const auto fileName = FilePath::fromString(match.captured(3));
|
2020-06-23 13:00:43 +02:00
|
|
|
const int lineNumber = match.captured(4).toInt();
|
2020-03-18 12:56:35 +01:00
|
|
|
m_tasks.append({Task::Warning, QString(), fileName, lineNumber, category});
|
2020-04-09 17:47:01 +02:00
|
|
|
return {Status::InProgress, {link}};
|
2019-07-22 14:39:01 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-19 16:00:37 +01:00
|
|
|
Status status = Status::InProgress;
|
2020-03-18 12:56:35 +01:00
|
|
|
if (text.startsWith(' ')) {
|
|
|
|
|
// Neither traceback start, nor file, nor error message line.
|
|
|
|
|
// Not sure if that can actually happen.
|
|
|
|
|
if (m_tasks.isEmpty()) {
|
|
|
|
|
m_tasks.append({Task::Warning, text.trimmed(), {}, -1, category});
|
2019-07-22 14:39:01 +02:00
|
|
|
} else {
|
2020-03-18 12:56:35 +01:00
|
|
|
Task &task = m_tasks.back();
|
2020-05-12 16:26:34 +02:00
|
|
|
if (!task.summary.isEmpty())
|
|
|
|
|
task.summary += ' ';
|
|
|
|
|
task.summary += text.trimmed();
|
2019-07-22 14:39:01 +02:00
|
|
|
}
|
2020-03-18 12:56:35 +01:00
|
|
|
} else {
|
|
|
|
|
// The actual exception. This ends the traceback.
|
|
|
|
|
TaskHub::addTask({Task::Error, text, {}, -1, category});
|
|
|
|
|
for (auto rit = m_tasks.crbegin(), rend = m_tasks.crend(); rit != rend; ++rit)
|
2019-07-22 14:39:01 +02:00
|
|
|
TaskHub::addTask(*rit);
|
2020-03-18 12:56:35 +01:00
|
|
|
m_tasks.clear();
|
|
|
|
|
m_inTraceBack = false;
|
2020-03-19 16:00:37 +01:00
|
|
|
status = Status::Done;
|
2019-07-22 14:39:01 +02:00
|
|
|
}
|
2020-03-19 16:00:37 +01:00
|
|
|
return status;
|
2019-07-22 14:39:01 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-19 16:00:37 +01:00
|
|
|
bool handleLink(const QString &href) final
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
|
|
|
|
const QRegularExpressionMatch match = filePattern.match(href);
|
|
|
|
|
if (!match.hasMatch())
|
2020-03-19 16:00:37 +01:00
|
|
|
return false;
|
2019-07-22 14:39:01 +02:00
|
|
|
const QString fileName = match.captured(3);
|
2020-06-23 13:00:43 +02:00
|
|
|
const int lineNumber = match.captured(4).toInt();
|
2021-11-01 17:02:02 +01:00
|
|
|
Core::EditorManager::openEditorAt({FilePath::fromString(fileName), lineNumber});
|
2020-03-19 16:00:37 +01:00
|
|
|
return true;
|
2019-07-22 14:39:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QRegularExpression filePattern;
|
2020-03-18 12:56:35 +01:00
|
|
|
QList<Task> m_tasks;
|
|
|
|
|
bool m_inTraceBack;
|
2019-07-22 14:39:01 +02:00
|
|
|
};
|
|
|
|
|
|
2023-07-07 18:28:29 +02:00
|
|
|
// RunConfiguration
|
|
|
|
|
|
|
|
|
|
class PythonRunConfiguration : public RunConfiguration
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
PythonRunConfiguration(Target *target, Id id)
|
|
|
|
|
: RunConfiguration(target, id)
|
|
|
|
|
{
|
|
|
|
|
buffered.setSettingsKey("PythonEditor.RunConfiguation.Buffered");
|
|
|
|
|
buffered.setLabelText(Tr::tr("Buffered output"));
|
|
|
|
|
buffered.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
|
|
|
|
|
buffered.setToolTip(Tr::tr("Enabling improves output performance, "
|
|
|
|
|
"but results in delayed output."));
|
|
|
|
|
|
|
|
|
|
mainScript.setSettingsKey("PythonEditor.RunConfiguation.Script");
|
|
|
|
|
mainScript.setLabelText(Tr::tr("Script:"));
|
2023-07-13 07:34:37 +02:00
|
|
|
mainScript.setReadOnly(true);
|
2023-07-07 18:28:29 +02:00
|
|
|
|
|
|
|
|
environment.setSupportForBuildEnvironment(target);
|
|
|
|
|
|
|
|
|
|
arguments.setMacroExpander(macroExpander());
|
|
|
|
|
|
|
|
|
|
workingDir.setMacroExpander(macroExpander());
|
|
|
|
|
|
|
|
|
|
x11Forwarding.setMacroExpander(macroExpander());
|
|
|
|
|
x11Forwarding.setVisible(HostOsInfo::isAnyUnixHost());
|
|
|
|
|
|
2023-11-01 15:05:03 +01:00
|
|
|
interpreter.setLabelText(Tr::tr("Python:"));
|
|
|
|
|
interpreter.setReadOnly(true);
|
2023-11-01 09:42:48 +01:00
|
|
|
|
2023-07-07 18:28:29 +02:00
|
|
|
setCommandLineGetter([this] {
|
2023-11-01 15:05:03 +01:00
|
|
|
CommandLine cmd;
|
|
|
|
|
cmd.setExecutable(interpreter());
|
|
|
|
|
if (interpreter().isEmpty())
|
|
|
|
|
return cmd;
|
2023-07-07 18:28:29 +02:00
|
|
|
if (!buffered())
|
|
|
|
|
cmd.addArg("-u");
|
2023-07-13 07:34:37 +02:00
|
|
|
cmd.addArg(mainScript().fileName());
|
2023-07-07 18:28:29 +02:00
|
|
|
cmd.addArgs(arguments(), CommandLine::Raw);
|
|
|
|
|
return cmd;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
setUpdater([this] {
|
|
|
|
|
const BuildTargetInfo bti = buildTargetInfo();
|
2023-11-01 15:05:03 +01:00
|
|
|
const auto python = FilePath::fromSettings(bti.additionalData.toMap().value("python"));
|
|
|
|
|
interpreter.setValue(python);
|
|
|
|
|
|
2023-07-13 07:34:37 +02:00
|
|
|
setDefaultDisplayName(Tr::tr("Run %1").arg(bti.targetFilePath.toUserOutput()));
|
|
|
|
|
mainScript.setValue(bti.targetFilePath);
|
2023-07-07 18:28:29 +02:00
|
|
|
workingDir.setDefaultWorkingDirectory(bti.targetFilePath.parentDir());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-01 15:05:03 +01:00
|
|
|
FilePathAspect interpreter{this};
|
2023-07-07 18:28:29 +02:00
|
|
|
BoolAspect buffered{this};
|
|
|
|
|
MainScriptAspect mainScript{this};
|
|
|
|
|
EnvironmentAspect environment{this};
|
|
|
|
|
ArgumentsAspect arguments{this};
|
|
|
|
|
WorkingDirectoryAspect workingDir{this};
|
|
|
|
|
TerminalAspect terminal{this};
|
|
|
|
|
X11ForwardingAspect x11Forwarding{this};
|
|
|
|
|
};
|
|
|
|
|
|
2023-07-05 09:30:51 +02:00
|
|
|
// Factories
|
|
|
|
|
|
2019-07-22 14:39:01 +02:00
|
|
|
PythonRunConfigurationFactory::PythonRunConfigurationFactory()
|
|
|
|
|
{
|
2022-04-13 12:26:54 +02:00
|
|
|
registerRunConfiguration<PythonRunConfiguration>(Constants::C_PYTHONRUNCONFIGURATION_ID);
|
2019-07-22 14:39:01 +02:00
|
|
|
addSupportedProjectType(PythonProjectId);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-01 11:04:31 +01:00
|
|
|
void setupPythonOutputParser()
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
2024-02-01 11:04:31 +01:00
|
|
|
addOutputParserFactory([](Target *t) -> OutputLineParser * {
|
2023-11-01 15:05:03 +01:00
|
|
|
if (t && t->project()->mimeType() == Constants::C_PY_PROJECT_MIME_TYPE)
|
2024-02-01 11:04:31 +01:00
|
|
|
return new PythonOutputLineParser;
|
|
|
|
|
return nullptr;
|
2019-07-22 14:39:01 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-15 11:49:34 +02:00
|
|
|
} // Python::Internal
|