2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 Brian McGillion and Hugues Delorme
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
2015-04-27 15:03:07 +02:00
|
|
|
|
2022-08-01 10:49:13 +02:00
|
|
|
#include "vcscommand.h"
|
2015-04-27 15:03:07 +02:00
|
|
|
|
2022-08-01 18:35:07 +02:00
|
|
|
#include "vcsbaseplugin.h"
|
|
|
|
|
#include "vcsoutputwindow.h"
|
|
|
|
|
|
|
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
#include <coreplugin/progressmanager/progressmanager.h>
|
|
|
|
|
|
2022-08-01 10:49:13 +02:00
|
|
|
#include <utils/environment.h>
|
2022-08-01 18:35:07 +02:00
|
|
|
#include <utils/globalfilechangeblocker.h>
|
2022-08-01 10:49:13 +02:00
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
#include <utils/qtcprocess.h>
|
|
|
|
|
#include <utils/runextensions.h>
|
2015-04-27 15:03:07 +02:00
|
|
|
|
|
|
|
|
#include <QFuture>
|
|
|
|
|
#include <QFutureWatcher>
|
2016-01-29 13:55:05 +01:00
|
|
|
#include <QMutex>
|
2015-04-27 15:03:07 +02:00
|
|
|
#include <QTextCodec>
|
2016-07-04 17:20:23 +02:00
|
|
|
#include <QThread>
|
2016-01-29 13:55:05 +01:00
|
|
|
#include <QVariant>
|
2015-04-27 15:03:07 +02:00
|
|
|
|
2017-09-07 11:17:27 +02:00
|
|
|
#include <numeric>
|
|
|
|
|
|
2015-04-27 15:03:07 +02:00
|
|
|
/*!
|
|
|
|
|
\fn void Utils::ProgressParser::parseProgress(const QString &text)
|
|
|
|
|
|
|
|
|
|
Reimplement to parse progress as it appears in the standard output.
|
|
|
|
|
If a progress string is detected, call \c setProgressAndMaximum() to update
|
|
|
|
|
the progress bar accordingly.
|
|
|
|
|
|
|
|
|
|
\sa Utils::ProgressParser::setProgressAndMaximum()
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
\fn void Utils::ProgressParser::setProgressAndMaximum(int value, int maximum)
|
|
|
|
|
|
|
|
|
|
Sets progress \a value and \a maximum for current command. Called by \c parseProgress()
|
|
|
|
|
when a progress string is detected.
|
|
|
|
|
*/
|
|
|
|
|
|
2022-08-01 18:35:07 +02:00
|
|
|
using namespace Core;
|
2022-08-01 10:49:13 +02:00
|
|
|
using namespace Utils;
|
|
|
|
|
|
|
|
|
|
namespace VcsBase {
|
2015-04-27 15:03:07 +02:00
|
|
|
namespace Internal {
|
|
|
|
|
|
2022-09-01 17:13:31 +02:00
|
|
|
class VcsCommandPrivate : public QObject
|
2015-04-27 15:03:07 +02:00
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
struct Job {
|
2019-06-07 15:27:50 +02:00
|
|
|
CommandLine command;
|
2022-08-02 17:28:15 +02:00
|
|
|
int timeoutS = 10;
|
|
|
|
|
FilePath workingDirectory;
|
|
|
|
|
ExitCodeInterpreter exitCodeInterpreter = {};
|
2015-04-27 15:03:07 +02:00
|
|
|
};
|
|
|
|
|
|
2022-09-01 17:13:31 +02:00
|
|
|
VcsCommandPrivate(VcsCommand *vcsCommand, const FilePath &defaultWorkingDirectory,
|
|
|
|
|
const Environment &environment)
|
|
|
|
|
: q(vcsCommand)
|
|
|
|
|
, m_defaultWorkingDirectory(defaultWorkingDirectory)
|
|
|
|
|
, m_environment(environment)
|
2022-08-01 18:35:07 +02:00
|
|
|
{
|
|
|
|
|
VcsBase::setProcessEnvironment(&m_environment);
|
2022-08-26 15:03:22 +02:00
|
|
|
m_futureInterface.setProgressRange(0, 1);
|
2022-08-01 18:35:07 +02:00
|
|
|
}
|
2021-05-04 05:54:54 +02:00
|
|
|
|
2022-08-01 10:49:13 +02:00
|
|
|
~VcsCommandPrivate() { delete m_progressParser; }
|
2015-04-27 15:03:07 +02:00
|
|
|
|
2022-07-13 17:54:59 +02:00
|
|
|
Environment environment()
|
|
|
|
|
{
|
2022-08-01 10:49:13 +02:00
|
|
|
if (!(m_flags & VcsCommand::ForceCLocale))
|
2022-07-13 17:54:59 +02:00
|
|
|
return m_environment;
|
|
|
|
|
|
|
|
|
|
m_environment.set("LANG", "C");
|
|
|
|
|
m_environment.set("LANGUAGE", "C");
|
|
|
|
|
return m_environment;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-01 17:13:31 +02:00
|
|
|
QString displayName() const;
|
|
|
|
|
int timeoutS() const;
|
|
|
|
|
|
2022-08-26 15:03:22 +02:00
|
|
|
void setup();
|
|
|
|
|
void cleanup();
|
2022-09-01 17:35:36 +02:00
|
|
|
void setupProcess(QtcProcess *process, const Job &job);
|
2022-09-08 17:30:07 +02:00
|
|
|
void installStdCallbacks(QtcProcess *process);
|
2022-10-05 11:14:41 +02:00
|
|
|
EventLoopMode eventLoopMode() const;
|
2022-09-01 17:35:36 +02:00
|
|
|
void handleDone(QtcProcess *process);
|
2022-08-25 12:56:03 +02:00
|
|
|
void startAll();
|
|
|
|
|
void startNextJob();
|
|
|
|
|
void processDone();
|
2022-09-01 17:35:36 +02:00
|
|
|
|
2022-09-01 17:13:31 +02:00
|
|
|
VcsCommand *q = nullptr;
|
|
|
|
|
|
2015-04-27 15:03:07 +02:00
|
|
|
QString m_displayName;
|
2021-08-11 10:02:58 +02:00
|
|
|
const FilePath m_defaultWorkingDirectory;
|
2022-07-13 17:54:59 +02:00
|
|
|
Environment m_environment;
|
2016-03-31 23:48:48 +03:00
|
|
|
QTextCodec *m_codec = nullptr;
|
|
|
|
|
ProgressParser *m_progressParser = nullptr;
|
2015-04-27 15:03:07 +02:00
|
|
|
QFutureWatcher<void> m_watcher;
|
|
|
|
|
QList<Job> m_jobs;
|
|
|
|
|
|
2022-08-25 12:56:03 +02:00
|
|
|
int m_currentJob = 0;
|
|
|
|
|
std::unique_ptr<QtcProcess> m_process;
|
|
|
|
|
QString m_stdOut;
|
|
|
|
|
QString m_stdErr;
|
2022-09-19 11:44:07 +02:00
|
|
|
ProcessResult m_result = ProcessResult::StartFailed;
|
2022-08-25 12:56:03 +02:00
|
|
|
QFutureInterface<void> m_futureInterface;
|
|
|
|
|
|
2016-03-31 23:48:48 +03:00
|
|
|
unsigned m_flags = 0;
|
|
|
|
|
|
|
|
|
|
bool m_progressiveOutput = false;
|
2015-04-27 15:03:07 +02:00
|
|
|
};
|
|
|
|
|
|
2022-09-01 17:13:31 +02:00
|
|
|
QString VcsCommandPrivate::displayName() const
|
|
|
|
|
{
|
|
|
|
|
if (!m_displayName.isEmpty())
|
|
|
|
|
return m_displayName;
|
|
|
|
|
if (m_jobs.isEmpty())
|
|
|
|
|
return tr("Unknown");
|
|
|
|
|
const Job &job = m_jobs.at(0);
|
|
|
|
|
QString result = job.command.executable().baseName();
|
|
|
|
|
if (!result.isEmpty())
|
|
|
|
|
result[0] = result.at(0).toTitleCase();
|
|
|
|
|
else
|
|
|
|
|
result = tr("UNKNOWN");
|
|
|
|
|
if (!job.command.arguments().isEmpty())
|
|
|
|
|
result += ' ' + job.command.splitArguments().at(0);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int VcsCommandPrivate::timeoutS() const
|
|
|
|
|
{
|
|
|
|
|
return std::accumulate(m_jobs.cbegin(), m_jobs.cend(), 0,
|
|
|
|
|
[](int sum, const Job &job) { return sum + job.timeoutS; });
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-26 15:03:22 +02:00
|
|
|
void VcsCommandPrivate::setup()
|
|
|
|
|
{
|
|
|
|
|
m_futureInterface.reportStarted();
|
|
|
|
|
if (m_flags & VcsCommand::ExpectRepoChanges) {
|
|
|
|
|
QMetaObject::invokeMethod(GlobalFileChangeBlocker::instance(), [] {
|
|
|
|
|
GlobalFileChangeBlocker::instance()->forceBlocked(true);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (m_progressParser)
|
|
|
|
|
m_progressParser->setFuture(&m_futureInterface);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VcsCommandPrivate::cleanup()
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(m_futureInterface.isRunning(), return);
|
|
|
|
|
m_futureInterface.reportFinished();
|
|
|
|
|
if (m_flags & VcsCommand::ExpectRepoChanges) {
|
|
|
|
|
QMetaObject::invokeMethod(GlobalFileChangeBlocker::instance(), [] {
|
|
|
|
|
GlobalFileChangeBlocker::instance()->forceBlocked(false);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (m_progressParser)
|
|
|
|
|
m_progressParser->setFuture(nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-01 17:35:36 +02:00
|
|
|
void VcsCommandPrivate::setupProcess(QtcProcess *process, const Job &job)
|
|
|
|
|
{
|
|
|
|
|
process->setExitCodeInterpreter(job.exitCodeInterpreter);
|
|
|
|
|
// TODO: Handle it properly in QtcProcess when QtcProcess::runBlocking() isn't used.
|
|
|
|
|
process->setTimeoutS(job.timeoutS);
|
|
|
|
|
if (!job.workingDirectory.isEmpty())
|
|
|
|
|
process->setWorkingDirectory(job.workingDirectory);
|
|
|
|
|
if (!(m_flags & VcsCommand::SuppressCommandLogging))
|
|
|
|
|
emit q->appendCommand(job.workingDirectory, job.command);
|
|
|
|
|
process->setCommand(job.command);
|
|
|
|
|
process->setDisableUnixTerminal();
|
|
|
|
|
process->setEnvironment(environment());
|
|
|
|
|
if (m_flags & VcsCommand::MergeOutputChannels)
|
|
|
|
|
process->setProcessChannelMode(QProcess::MergedChannels);
|
|
|
|
|
if (m_codec)
|
|
|
|
|
process->setCodec(m_codec);
|
2022-09-15 17:08:23 +02:00
|
|
|
|
|
|
|
|
installStdCallbacks(process);
|
2022-09-01 17:35:36 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-08 17:30:07 +02:00
|
|
|
void VcsCommandPrivate::installStdCallbacks(QtcProcess *process)
|
2022-09-01 17:35:36 +02:00
|
|
|
{
|
|
|
|
|
if (!(m_flags & VcsCommand::MergeOutputChannels)
|
|
|
|
|
&& (m_progressiveOutput || !(m_flags & VcsCommand::SuppressStdErr))) {
|
|
|
|
|
process->setStdErrCallback([this](const QString &text) {
|
|
|
|
|
if (m_progressParser)
|
|
|
|
|
m_progressParser->parseProgress(text);
|
|
|
|
|
if (!(m_flags & VcsCommand::SuppressStdErr))
|
|
|
|
|
emit q->appendError(text);
|
|
|
|
|
if (m_progressiveOutput)
|
|
|
|
|
emit q->stdErrText(text);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// connect stdout to the output window if desired
|
|
|
|
|
if (m_progressParser || m_progressiveOutput || (m_flags & VcsCommand::ShowStdOut)) {
|
|
|
|
|
process->setStdOutCallback([this](const QString &text) {
|
|
|
|
|
if (m_progressParser)
|
|
|
|
|
m_progressParser->parseProgress(text);
|
2022-09-15 15:10:48 +02:00
|
|
|
if (m_flags & VcsCommand::ShowStdOut) {
|
|
|
|
|
if (m_flags & VcsCommand::SilentOutput)
|
|
|
|
|
emit q->appendSilently(text);
|
|
|
|
|
else
|
|
|
|
|
emit q->append(text);
|
|
|
|
|
}
|
2022-09-01 17:35:36 +02:00
|
|
|
if (m_progressiveOutput)
|
|
|
|
|
emit q->stdOutText(text);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// TODO: Implement it here
|
|
|
|
|
// m_process->setTimeOutMessageBoxEnabled(true);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-05 11:14:41 +02:00
|
|
|
EventLoopMode VcsCommandPrivate::eventLoopMode() const
|
2022-09-01 17:35:36 +02:00
|
|
|
{
|
2022-10-05 11:14:41 +02:00
|
|
|
if ((m_flags & VcsCommand::UseEventLoop) && QThread::currentThread() == qApp->thread())
|
|
|
|
|
return EventLoopMode::On;
|
|
|
|
|
return EventLoopMode::Off;
|
2022-09-01 17:35:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VcsCommandPrivate::handleDone(QtcProcess *process)
|
|
|
|
|
{
|
2022-09-01 11:49:41 +02:00
|
|
|
// Success/Fail message in appropriate window?
|
|
|
|
|
if (process->result() == ProcessResult::FinishedWithSuccess) {
|
|
|
|
|
if (m_flags & VcsCommand::ShowSuccessMessage)
|
|
|
|
|
emit q->appendMessage(process->exitMessage());
|
|
|
|
|
} else if (!(m_flags & VcsCommand::SuppressFailMessage)) {
|
|
|
|
|
emit q->appendError(process->exitMessage());
|
2022-09-01 17:35:36 +02:00
|
|
|
}
|
|
|
|
|
emit q->runCommandFinished(process->workingDirectory());
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-25 12:56:03 +02:00
|
|
|
void VcsCommandPrivate::startAll()
|
|
|
|
|
{
|
|
|
|
|
// Check that the binary path is not empty
|
|
|
|
|
QTC_ASSERT(!m_jobs.isEmpty(), return);
|
|
|
|
|
QTC_ASSERT(!m_process, return);
|
2022-08-26 15:03:22 +02:00
|
|
|
setup();
|
2022-08-25 12:56:03 +02:00
|
|
|
m_currentJob = 0;
|
|
|
|
|
startNextJob();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VcsCommandPrivate::startNextJob()
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(m_currentJob < m_jobs.count(), return);
|
|
|
|
|
m_process.reset(new QtcProcess);
|
|
|
|
|
connect(m_process.get(), &QtcProcess::done, this, &VcsCommandPrivate::processDone);
|
|
|
|
|
setupProcess(m_process.get(), m_jobs.at(m_currentJob));
|
|
|
|
|
m_process->start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VcsCommandPrivate::processDone()
|
|
|
|
|
{
|
|
|
|
|
handleDone(m_process.get());
|
|
|
|
|
m_stdOut += m_process->cleanedStdOut();
|
|
|
|
|
m_stdErr += m_process->cleanedStdErr();
|
2022-09-19 11:44:07 +02:00
|
|
|
m_result = m_process->result();
|
2022-08-25 12:56:03 +02:00
|
|
|
++m_currentJob;
|
|
|
|
|
const bool success = m_process->result() == ProcessResult::FinishedWithSuccess;
|
|
|
|
|
if (m_currentJob < m_jobs.count() && success) {
|
|
|
|
|
m_process.release()->deleteLater();
|
|
|
|
|
startNextJob();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-09-19 11:44:07 +02:00
|
|
|
emit q->done();
|
2022-09-01 11:49:41 +02:00
|
|
|
if (!success)
|
|
|
|
|
m_futureInterface.reportCanceled();
|
2022-08-26 15:03:22 +02:00
|
|
|
cleanup();
|
2022-08-25 12:56:03 +02:00
|
|
|
// As it is used asynchronously, we need to delete ourselves
|
|
|
|
|
q->deleteLater();
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-27 15:03:07 +02:00
|
|
|
} // namespace Internal
|
|
|
|
|
|
2022-08-01 10:49:13 +02:00
|
|
|
VcsCommand::VcsCommand(const FilePath &workingDirectory, const Environment &environment) :
|
2022-09-01 17:13:31 +02:00
|
|
|
d(new Internal::VcsCommandPrivate(this, workingDirectory, environment))
|
2016-01-25 15:55:33 +01:00
|
|
|
{
|
2022-08-01 10:49:13 +02:00
|
|
|
connect(&d->m_watcher, &QFutureWatcher<void>::canceled, this, &VcsCommand::cancel);
|
2022-08-01 18:35:07 +02:00
|
|
|
|
2022-08-03 13:25:51 +02:00
|
|
|
VcsOutputWindow::setRepository(d->m_defaultWorkingDirectory);
|
2022-08-01 18:35:07 +02:00
|
|
|
VcsOutputWindow *outputWindow = VcsOutputWindow::instance(); // Keep me here, just to be sure it's not instantiated in other thread
|
|
|
|
|
connect(this, &VcsCommand::append, outputWindow, [outputWindow](const QString &t) {
|
|
|
|
|
outputWindow->append(t);
|
|
|
|
|
});
|
|
|
|
|
connect(this, &VcsCommand::appendSilently, outputWindow, &VcsOutputWindow::appendSilently);
|
|
|
|
|
connect(this, &VcsCommand::appendError, outputWindow, &VcsOutputWindow::appendError);
|
|
|
|
|
connect(this, &VcsCommand::appendCommand, outputWindow, &VcsOutputWindow::appendCommand);
|
|
|
|
|
connect(this, &VcsCommand::appendMessage, outputWindow, &VcsOutputWindow::appendMessage);
|
|
|
|
|
const auto connection = connect(this, &VcsCommand::runCommandFinished,
|
|
|
|
|
this, &VcsCommand::postRunCommand);
|
|
|
|
|
connect(ICore::instance(), &ICore::coreAboutToClose, this, [this, connection] {
|
|
|
|
|
disconnect(connection);
|
2022-09-01 11:49:41 +02:00
|
|
|
d->m_process.reset();
|
|
|
|
|
if (d->m_futureInterface.isRunning()) {
|
|
|
|
|
d->m_futureInterface.reportCanceled();
|
|
|
|
|
d->cleanup();
|
|
|
|
|
}
|
2022-08-01 18:35:07 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VcsCommand::postRunCommand(const FilePath &workingDirectory)
|
|
|
|
|
{
|
|
|
|
|
if (!(d->m_flags & VcsCommand::ExpectRepoChanges))
|
|
|
|
|
return;
|
|
|
|
|
// TODO tell the document manager that the directory now received all expected changes
|
|
|
|
|
// Core::DocumentManager::unexpectDirectoryChange(d->m_workingDirectory);
|
|
|
|
|
VcsManager::emitRepositoryChanged(workingDirectory);
|
2016-01-25 15:55:33 +01:00
|
|
|
}
|
2015-04-27 15:03:07 +02:00
|
|
|
|
2022-08-01 10:49:13 +02:00
|
|
|
VcsCommand::~VcsCommand()
|
2015-04-27 15:03:07 +02:00
|
|
|
{
|
2022-08-25 12:56:03 +02:00
|
|
|
if (d->m_futureInterface.isRunning()) {
|
|
|
|
|
d->m_futureInterface.reportCanceled();
|
2022-08-26 15:03:22 +02:00
|
|
|
d->cleanup();
|
2022-08-25 12:56:03 +02:00
|
|
|
}
|
2015-04-27 15:03:07 +02:00
|
|
|
delete d;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-01 10:49:13 +02:00
|
|
|
void VcsCommand::setDisplayName(const QString &name)
|
2015-04-27 15:03:07 +02:00
|
|
|
{
|
|
|
|
|
d->m_displayName = name;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-01 10:49:13 +02:00
|
|
|
void VcsCommand::addFlags(unsigned f)
|
2015-04-27 15:03:07 +02:00
|
|
|
{
|
|
|
|
|
d->m_flags |= f;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-01 10:49:13 +02:00
|
|
|
void VcsCommand::addJob(const CommandLine &command, int timeoutS,
|
2022-08-02 17:28:15 +02:00
|
|
|
const FilePath &workingDirectory,
|
|
|
|
|
const ExitCodeInterpreter &interpreter)
|
2015-04-27 15:03:07 +02:00
|
|
|
{
|
2022-08-25 12:56:03 +02:00
|
|
|
QTC_ASSERT(!command.executable().isEmpty(), return);
|
2022-08-02 17:28:15 +02:00
|
|
|
d->m_jobs.push_back({command, timeoutS, !workingDirectory.isEmpty()
|
|
|
|
|
? workingDirectory : d->m_defaultWorkingDirectory, interpreter});
|
2015-04-27 15:03:07 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-21 07:06:14 +02:00
|
|
|
void VcsCommand::start()
|
2015-04-27 15:03:07 +02:00
|
|
|
{
|
|
|
|
|
if (d->m_jobs.empty())
|
|
|
|
|
return;
|
|
|
|
|
|
2022-08-25 12:56:03 +02:00
|
|
|
d->startAll();
|
|
|
|
|
d->m_watcher.setFuture(d->m_futureInterface.future());
|
|
|
|
|
if ((d->m_flags & VcsCommand::SuppressCommandLogging))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const QString name = d->displayName();
|
|
|
|
|
const auto id = Id::fromString(name + QLatin1String(".action"));
|
|
|
|
|
if (d->m_progressParser)
|
|
|
|
|
ProgressManager::addTask(d->m_futureInterface.future(), name, id);
|
|
|
|
|
else
|
|
|
|
|
ProgressManager::addTimedTask(d->m_futureInterface, name, id, qMax(2, d->timeoutS() / 5));
|
2015-04-27 15:03:07 +02:00
|
|
|
}
|
|
|
|
|
|
2022-08-01 10:49:13 +02:00
|
|
|
void VcsCommand::cancel()
|
2015-04-27 15:03:07 +02:00
|
|
|
{
|
2022-08-25 12:56:03 +02:00
|
|
|
d->m_futureInterface.reportCanceled();
|
|
|
|
|
if (d->m_process) {
|
|
|
|
|
// TODO: we may want to call cancel here...
|
|
|
|
|
d->m_process->stop();
|
|
|
|
|
// TODO: we may want to not wait here...
|
|
|
|
|
// However, VcsBaseDiffEditorController::runCommand() relies on getting finished() signal
|
|
|
|
|
d->m_process->waitForFinished();
|
|
|
|
|
d->m_process.reset();
|
2015-04-27 15:03:07 +02:00
|
|
|
}
|
2022-08-25 12:56:03 +02:00
|
|
|
emit terminate();
|
2015-04-27 15:03:07 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-19 08:27:41 +02:00
|
|
|
QString VcsCommand::cleanedStdOut() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_stdOut;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString VcsCommand::cleanedStdErr() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_stdErr;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-19 11:44:07 +02:00
|
|
|
ProcessResult VcsCommand::result() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_result;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-02 18:35:00 +02:00
|
|
|
CommandResult VcsCommand::runCommand(const CommandLine &command, int timeoutS)
|
2015-04-27 15:03:07 +02:00
|
|
|
{
|
2022-09-15 17:08:23 +02:00
|
|
|
QtcProcess process;
|
2022-07-29 14:41:15 +02:00
|
|
|
if (command.executable().isEmpty())
|
|
|
|
|
return {};
|
|
|
|
|
|
2022-09-15 17:08:23 +02:00
|
|
|
d->setupProcess(&process, {command, timeoutS, d->m_defaultWorkingDirectory, {}});
|
2015-04-27 15:03:07 +02:00
|
|
|
|
2022-10-05 11:14:41 +02:00
|
|
|
const EventLoopMode eventLoopMode = d->eventLoopMode();
|
|
|
|
|
if (eventLoopMode == EventLoopMode::On) {
|
2022-09-15 17:08:23 +02:00
|
|
|
connect(this, &VcsCommand::terminate, &process, [&process] {
|
|
|
|
|
process.stop();
|
|
|
|
|
process.waitForFinished();
|
|
|
|
|
});
|
|
|
|
|
process.setTimeOutMessageBoxEnabled(true);
|
2015-04-27 15:03:07 +02:00
|
|
|
}
|
2022-09-15 17:08:23 +02:00
|
|
|
process.runBlocking(eventLoopMode);
|
|
|
|
|
d->handleDone(&process);
|
2015-04-27 15:03:07 +02:00
|
|
|
|
2022-09-15 17:08:23 +02:00
|
|
|
return CommandResult(process);
|
2016-07-05 13:20:10 +02:00
|
|
|
}
|
|
|
|
|
|
2022-08-01 10:49:13 +02:00
|
|
|
void VcsCommand::setCodec(QTextCodec *codec)
|
2015-04-27 15:03:07 +02:00
|
|
|
{
|
|
|
|
|
d->m_codec = codec;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Use \a parser to parse progress data from stdout. Command takes ownership of \a parser
|
2022-08-01 10:49:13 +02:00
|
|
|
void VcsCommand::setProgressParser(ProgressParser *parser)
|
2015-04-27 15:03:07 +02:00
|
|
|
{
|
|
|
|
|
QTC_ASSERT(!d->m_progressParser, return);
|
|
|
|
|
d->m_progressParser = parser;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-01 10:49:13 +02:00
|
|
|
void VcsCommand::setProgressiveOutput(bool progressive)
|
2015-04-27 15:03:07 +02:00
|
|
|
{
|
|
|
|
|
d->m_progressiveOutput = progressive;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ProgressParser::ProgressParser() :
|
|
|
|
|
m_futureMutex(new QMutex)
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
ProgressParser::~ProgressParser()
|
|
|
|
|
{
|
|
|
|
|
delete m_futureMutex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ProgressParser::setProgressAndMaximum(int value, int maximum)
|
|
|
|
|
{
|
|
|
|
|
QMutexLocker lock(m_futureMutex);
|
|
|
|
|
if (!m_future)
|
|
|
|
|
return;
|
|
|
|
|
m_future->setProgressRange(0, maximum);
|
|
|
|
|
m_future->setProgressValue(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ProgressParser::setFuture(QFutureInterface<void> *future)
|
|
|
|
|
{
|
|
|
|
|
QMutexLocker lock(m_futureMutex);
|
|
|
|
|
m_future = future;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-29 14:41:15 +02:00
|
|
|
CommandResult::CommandResult(const QtcProcess &process)
|
|
|
|
|
: m_result(process.result())
|
|
|
|
|
, m_exitCode(process.exitCode())
|
|
|
|
|
, m_exitMessage(process.exitMessage())
|
|
|
|
|
, m_cleanedStdOut(process.cleanedStdOut())
|
|
|
|
|
, m_cleanedStdErr(process.cleanedStdErr())
|
|
|
|
|
, m_rawStdOut(process.rawStdOut())
|
|
|
|
|
{}
|
|
|
|
|
|
2022-08-01 10:49:13 +02:00
|
|
|
} // namespace VcsBase
|