265 lines
8.1 KiB
C++
265 lines
8.1 KiB
C++
/*
|
|
* Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "executablerunner.h"
|
|
|
|
#include <QDebug>
|
|
#include "compilermanager.h"
|
|
#include "../settings.h"
|
|
#include "../systemconsts.h"
|
|
#ifdef Q_OS_WIN
|
|
#include <windows.h>
|
|
#elif defined(Q_OS_LINUX)
|
|
#include <sys/mman.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h> /* For mode constants */
|
|
#include <fcntl.h> /* For O_* constants */
|
|
#endif
|
|
|
|
|
|
ExecutableRunner::ExecutableRunner(const QString &filename, const QString &arguments, const QString &workDir
|
|
,QObject* parent):
|
|
Runner(filename,arguments,workDir,parent),
|
|
mRedirectInput(false),
|
|
mStartConsole(false),
|
|
mQuitSemaphore(0)
|
|
{
|
|
}
|
|
|
|
bool ExecutableRunner::startConsole() const
|
|
{
|
|
return mStartConsole;
|
|
}
|
|
|
|
void ExecutableRunner::setStartConsole(bool newStartConsole)
|
|
{
|
|
mStartConsole = newStartConsole;
|
|
}
|
|
|
|
bool ExecutableRunner::redirectInput() const
|
|
{
|
|
return mRedirectInput;
|
|
}
|
|
|
|
void ExecutableRunner::setRedirectInput(bool isRedirect)
|
|
{
|
|
mRedirectInput = isRedirect;
|
|
}
|
|
|
|
const QString &ExecutableRunner::redirectInputFilename() const
|
|
{
|
|
return mRedirectInputFilename;
|
|
}
|
|
|
|
void ExecutableRunner::setRedirectInputFilename(const QString &newDataFile)
|
|
{
|
|
mRedirectInputFilename = newDataFile;
|
|
}
|
|
|
|
void ExecutableRunner::run()
|
|
{
|
|
emit started();
|
|
auto action = finally([this]{
|
|
mProcess.reset();
|
|
setPausing(false);
|
|
emit terminated();
|
|
});
|
|
mStop = false;
|
|
bool errorOccurred = false;
|
|
|
|
mProcess = std::make_shared<QProcess>();
|
|
mProcess->setProgram(mFilename);
|
|
mProcess->setArguments(splitProcessCommand(mArguments));
|
|
mProcess->setWorkingDirectory(mWorkDir);
|
|
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
|
QString path = env.value("PATH");
|
|
QStringList pathAdded;
|
|
if (pSettings->compilerSets().defaultSet()) {
|
|
foreach(const QString& dir, pSettings->compilerSets().defaultSet()->binDirs()) {
|
|
pathAdded.append(dir);
|
|
}
|
|
}
|
|
pathAdded.append(pSettings->dirs().appDir());
|
|
if (!path.isEmpty()) {
|
|
path+= PATH_SEPARATOR + pathAdded.join(PATH_SEPARATOR);
|
|
} else {
|
|
path = pathAdded.join(PATH_SEPARATOR);
|
|
}
|
|
env.insert("PATH",path);
|
|
mProcess->setProcessEnvironment(env);
|
|
connect(
|
|
mProcess.get(), &QProcess::errorOccurred,
|
|
[&errorOccurred](){
|
|
errorOccurred= true;
|
|
});
|
|
#ifdef Q_OS_WIN
|
|
mProcess->setCreateProcessArgumentsModifier([this](QProcess::CreateProcessArguments * args){
|
|
if (mStartConsole) {
|
|
args->flags |= CREATE_NEW_CONSOLE;
|
|
args->flags &= ~CREATE_NO_WINDOW;
|
|
}
|
|
if (!mRedirectInput) {
|
|
args->startupInfo -> dwFlags &= ~STARTF_USESTDHANDLES;
|
|
}
|
|
});
|
|
HANDLE hSharedMemory=INVALID_HANDLE_VALUE;
|
|
int BUF_SIZE=1024;
|
|
char* pBuf=nullptr;
|
|
if (mStartConsole) {
|
|
hSharedMemory = CreateFileMappingA(
|
|
INVALID_HANDLE_VALUE,
|
|
NULL,
|
|
PAGE_READWRITE,
|
|
0,
|
|
100,
|
|
"RED_PANDA_IDE_CONSOLE_PAUSER20211223"
|
|
);
|
|
if (hSharedMemory != NULL)
|
|
{
|
|
pBuf = (char*) MapViewOfFile(hSharedMemory, // handle to map object
|
|
FILE_MAP_ALL_ACCESS, // read/write permission
|
|
0,
|
|
0,
|
|
BUF_SIZE);
|
|
if (pBuf) {
|
|
pBuf[0]=0;
|
|
}
|
|
}
|
|
}
|
|
#elif defined(Q_OS_LINUX)
|
|
int BUF_SIZE=1024;
|
|
char* pBuf=nullptr;
|
|
int fd_shm = shm_open("/REDPANDAIDECONSOLEPAUSER20211223",O_RDWR | O_CREAT,S_IRWXU);
|
|
if (fd_shm==-1) {
|
|
qDebug()<<QString("shm open failed %1:%2").arg(errno).arg(strerror(errno));
|
|
} else {
|
|
if (ftruncate(fd_shm,BUF_SIZE)==-1){
|
|
qDebug()<<QString("truncate failed %1:%2").arg(errno).arg(strerror(errno));
|
|
} else {
|
|
pBuf = (char*)mmap(NULL,BUF_SIZE,PROT_READ | PROT_WRITE, MAP_SHARED, fd_shm,0);
|
|
if (pBuf == MAP_FAILED) {
|
|
qDebug()<<QString("mmap failed %1:%2").arg(errno).arg(strerror(errno));
|
|
pBuf = nullptr;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
// if (!redirectInput()) {
|
|
// process.closeWriteChannel();
|
|
// }
|
|
mProcess->start();
|
|
mProcess->waitForStarted(5000);
|
|
if (mProcess->state()==QProcess::Running && redirectInput()) {
|
|
mProcess->write(readFileToByteArray(redirectInputFilename()));
|
|
mProcess->closeWriteChannel();
|
|
}
|
|
|
|
while (true) {
|
|
mProcess->waitForFinished(1000);
|
|
if (mProcess->state()!=QProcess::Running) {
|
|
break;
|
|
}
|
|
if (mStop) {
|
|
mProcess->closeReadChannel(QProcess::StandardOutput);
|
|
mProcess->closeReadChannel(QProcess::StandardError);
|
|
mProcess->closeWriteChannel();
|
|
mProcess->terminate();
|
|
if (mProcess->waitForFinished(1000)) {
|
|
break;
|
|
}
|
|
for (int i=0;i<10;i++) {
|
|
mProcess->kill();
|
|
if (mProcess->waitForFinished(500)) {
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
|
|
if (mStartConsole && !mPausing && pBuf) {
|
|
if (strncmp(pBuf,"FINISHED",sizeof("FINISHED"))==0) {
|
|
#ifdef Q_OS_WIN
|
|
if (pBuf) {
|
|
UnmapViewOfFile(pBuf);
|
|
pBuf = nullptr;
|
|
}
|
|
if (hSharedMemory!=INVALID_HANDLE_VALUE) {
|
|
hSharedMemory = INVALID_HANDLE_VALUE;
|
|
CloseHandle(hSharedMemory);
|
|
}
|
|
#elif defined(Q_OS_LINUX)
|
|
if (pBuf) {
|
|
munmap(pBuf,BUF_SIZE);
|
|
pBuf = nullptr;
|
|
}
|
|
if (fd_shm!=-1) {
|
|
shm_unlink("/REDPANDAIDECONSOLEPAUSER20211223");
|
|
fd_shm = -1;
|
|
}
|
|
#endif
|
|
setPausing(true);
|
|
emit pausingForFinish();
|
|
}
|
|
}
|
|
#endif
|
|
if (errorOccurred)
|
|
break;
|
|
}
|
|
#ifdef Q_OS_WIN
|
|
if (pBuf)
|
|
UnmapViewOfFile(pBuf);
|
|
if (hSharedMemory!=INVALID_HANDLE_VALUE)
|
|
CloseHandle(hSharedMemory);
|
|
#elif defined(Q_OS_LINUX)
|
|
if (pBuf) {
|
|
munmap(pBuf,BUF_SIZE);
|
|
}
|
|
if (fd_shm!=-1) {
|
|
shm_unlink("/REDPANDAIDECONSOLEPAUSER20211223");
|
|
}
|
|
#endif
|
|
if (errorOccurred) {
|
|
//qDebug()<<"process error:"<<process.error();
|
|
switch (mProcess->error()) {
|
|
case QProcess::FailedToStart:
|
|
emit runErrorOccurred(tr("The runner process '%1' failed to start.").arg(mFilename));
|
|
break;
|
|
// case QProcess::Crashed:
|
|
// if (!mStop)
|
|
// emit runErrorOccurred(tr("The runner process crashed after starting successfully."));
|
|
// break;
|
|
case QProcess::Timedout:
|
|
emit runErrorOccurred(tr("The last waitFor...() function timed out."));
|
|
break;
|
|
case QProcess::WriteError:
|
|
emit runErrorOccurred(tr("An error occurred when attempting to write to the runner process."));
|
|
break;
|
|
case QProcess::ReadError:
|
|
emit runErrorOccurred(tr("An error occurred when attempting to read from the runner process."));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
mQuitSemaphore.release(1);
|
|
}
|
|
|
|
void ExecutableRunner::doStop()
|
|
{
|
|
mQuitSemaphore.acquire(1);
|
|
}
|