RedPanda-CPP/RedPandaIDE/compiler/compiler.cpp

445 lines
13 KiB
C++
Raw Normal View History

2021-04-20 22:24:33 +08:00
#include "compiler.h"
#include "utils.h"
#include "compilermanager.h"
2021-04-20 22:24:33 +08:00
#include <QFileInfo>
#include <QProcess>
#include <QString>
#include <QTextCodec>
#include <QDebug>
#include <QTime>
2021-06-24 20:43:09 +08:00
#define COMPILE_PROCESS_END "---//END//----"
Compiler::Compiler(const QString &filename, bool silent, bool onlyCheckSyntax):
2021-04-20 22:24:33 +08:00
QThread(),
mSilent(silent),
2021-06-24 20:43:09 +08:00
mOnlyCheckSyntax(onlyCheckSyntax),
2021-06-25 12:40:11 +08:00
mFilename(filename),
mRebuild(false)
2021-04-20 22:24:33 +08:00
{
}
void Compiler::run()
{
emit compileStarted();
try {
2021-06-25 12:40:11 +08:00
if (mRebuild && !prepareForRebuild()) {
throw CompileError(tr("Clean before rebuild failed."));
}
if (prepareForCompile()){
mErrorCount = 0;
mWarningCount = 0;
QElapsedTimer timer;
timer.start();
2021-06-24 20:43:09 +08:00
runCommand(mCompiler, mArguments, QFileInfo(mCompiler).absolutePath(), pipedText());
log("");
log(tr("Compile Result:"));
log("------------------");
log(tr("- Errors: %1").arg(mErrorCount));
log(tr("- Warnings: %1").arg(mWarningCount));
if (!mOutputFile.isEmpty()) {
log(tr("- Output Filename: %1").arg(mOutputFile));
QLocale locale = QLocale::system();
log(tr("- Output Size: %1").arg(locale.formattedDataSize(QFileInfo(mOutputFile).size())));
}
log(tr("- Compilation Time: %1 secs").arg(timer.elapsed() / 1000.0));
}
} catch (CompileError e) {
emit compileErrorOccured(e.reason());
2021-04-20 22:24:33 +08:00
}
emit compileFinished();
}
2021-04-24 15:57:45 +08:00
QString Compiler::getFileNameFromOutputLine(QString &line) {
QString temp;
line = line.trimmed();
while (true) {
int pos;
if (line.length() > 2 && line[1]==':') { // full file path at start, ignore this ':'
pos = line.indexOf(':',2);
} else {
pos = line.indexOf(':');
}
if ( pos < 0) {
break;
}
temp = line.mid(0,pos);
line.remove(0,pos+1);
line=line.trimmed();
2021-06-24 20:43:09 +08:00
if (temp.compare("<stdin>", Qt::CaseInsensitive)==0 ) {
temp = mFilename;
break;
}
2021-04-24 15:57:45 +08:00
if (QFileInfo(temp).fileName() == QLatin1String("ld.exe")) { // skip ld.exe
2021-04-24 15:57:45 +08:00
continue;
} else {
break;
}
}
return temp;
}
int Compiler::getLineNumberFromOutputLine(QString &line)
{
line = line.trimmed();
int pos = line.indexOf(':');
int result=0;
if (pos < 0) {
pos = line.indexOf(',');
}
if (pos>=0) {
result = line.midRef(0,pos).toInt();
if (result > 0)
line.remove(0,pos+1);
2021-04-24 15:57:45 +08:00
}
return result;
}
int Compiler::getColunmnFromOutputLine(QString &line)
{
line = line.trimmed();
int pos = line.indexOf(':');
int result=0;
if (pos < 0) {
pos = line.indexOf(',');
}
if (pos>=0) {
result = line.midRef(0,pos).toInt();
if (result > 0)
line.remove(0,pos+1);
2021-04-24 15:57:45 +08:00
}
return result;
}
CompileIssueType Compiler::getIssueTypeFromOutputLine(QString &line)
{
CompileIssueType result = CompileIssueType::Other;
line = line.trimmed();
int pos = line.indexOf(':');
if (pos>=0) {
QString s=line.mid(0,pos);
if (s == "error" || s == "fatal error") {
mErrorCount += 1;
line = tr("[Error] ")+line.mid(pos+1);
result = CompileIssueType::Error;
} else if (s == "warning") {
mWarningCount += 1;
line = tr("[Warning] ")+line.mid(pos+1);
result = CompileIssueType::Warning;
} else if (s == "info") {
mWarningCount += 1;
line = tr("[Info] ")+line.mid(pos+1);
result = CompileIssueType::Info;
} else if (s == "note") {
mWarningCount += 1;
line = tr("[Note] ")+line.mid(pos+1);
result = CompileIssueType::Note;
}
}
return result;
}
void Compiler::processOutput(QString &line)
{
2021-06-24 20:43:09 +08:00
if (line == COMPILE_PROCESS_END) {
if (mLastIssue) {
emit compileIssue(mLastIssue);
mLastIssue.reset();
}
return;
}
2021-04-24 15:57:45 +08:00
QString inFilePrefix = QString("In file included from ");
QString fromPrefix = QString("from ");
PCompileIssue issue = std::make_shared<CompileIssue>();
issue->type = CompileIssueType::Other;
issue->endColumn = -1;
2021-04-24 15:57:45 +08:00
if (line.startsWith(inFilePrefix)) {
line.remove(0,inFilePrefix.length());
issue->filename = getFileNameFromOutputLine(line);
issue->line = getLineNumberFromOutputLine(line);
if (issue->line > 0)
issue->column = getColunmnFromOutputLine(line);
2021-04-24 15:57:45 +08:00
issue->type = getIssueTypeFromOutputLine(line);
issue->description = inFilePrefix + issue->filename;
emit compileIssue(issue);
return;
} else if(line.startsWith(fromPrefix)) {
line.remove(0,fromPrefix.length());
issue->filename = getFileNameFromOutputLine(line);
issue->line = getLineNumberFromOutputLine(line);
if (issue->line > 0)
issue->column = getColunmnFromOutputLine(line);
2021-04-24 15:57:45 +08:00
issue->type = getIssueTypeFromOutputLine(line);
issue->description = " from " + issue->filename;
emit compileIssue(issue);
return;
}
// Ignore code snippets that GCC produces
// they always start with a space
if (line.length()>0 && line[0] == ' ') {
if (!mLastIssue)
return;
QString s = line.trimmed();
if (s.startsWith('|') && s.indexOf('^')) {
int pos = 0;
while (pos < s.length()) {
if (s[pos]=='^')
break;
pos++;
}
if (pos<s.length()) {
int i=pos+1;
while (i<s.length()) {
if (s[i]!='~' && s[i]!='^')
break;
i++;
}
mLastIssue->endColumn = mLastIssue->column+i-pos;
emit compileIssue(mLastIssue);
mLastIssue.reset();
}
}
2021-04-24 15:57:45 +08:00
return;
}
if (mLastIssue) {
emit compileIssue(mLastIssue);
mLastIssue.reset();
}
2021-04-24 15:57:45 +08:00
// assume regular main.cpp:line:col: message
issue->filename = getFileNameFromOutputLine(line);
issue->line = getLineNumberFromOutputLine(line);
if (issue->line > 0) {
issue->column = getColunmnFromOutputLine(line);
2021-06-24 22:33:57 +08:00
issue->type = getIssueTypeFromOutputLine(line);
if (issue->column<=0 && issue->type == CompileIssueType::Other) {
issue->type = CompileIssueType::Error; //linkage error
2021-06-24 22:33:57 +08:00
mErrorCount += 1;
}
} else {
issue->column = -1;
issue->type = getIssueTypeFromOutputLine(line);
}
2021-04-24 15:57:45 +08:00
issue->description = line.trimmed();
if (issue->line<=0) {
emit compileIssue(issue);
} else
mLastIssue = issue;
2021-04-24 15:57:45 +08:00
}
2021-04-20 22:24:33 +08:00
void Compiler::stopCompile()
{
mStop = true;
}
QString Compiler::getCharsetArgument(const QByteArray& encoding)
{
QString result;
if (compilerSet()->autoAddCharsetParams() && encoding != ENCODING_ASCII) {
QString encodingName;
QString systemEncodingName=QTextCodec::codecForLocale()->name();
if (encoding == ENCODING_SYSTEM_DEFAULT) {
encodingName = systemEncodingName;
} else if (encoding == ENCODING_UTF8_BOM) {
encodingName = "UTF-8";
} else {
encodingName = encoding;
}
result += QString(" -finput-charset=%1 -fexec-charset=%2")
.arg(encodingName,systemEncodingName);
2021-04-20 22:24:33 +08:00
}
return result;
}
QString Compiler::getCCompileArguments(bool checkSyntax)
{
QString result;
if (checkSyntax) {
result += " -fsyntax-only";
}
foreach (const PCompilerOption& pOption, compilerSet()->options()) {
2021-04-20 22:24:33 +08:00
if (pOption->value > 0 && pOption->isC) {
if (pOption->choices.isEmpty()) {
result += " " + pOption->setting;
} else if (pOption->value < pOption->choices.size()) {
QStringList nameValue=pOption->choices[pOption->value].split('=');
if (nameValue.count()==2) {
result += " " + pOption->setting + nameValue[1];
}
}
}
}
if (compilerSet()->useCustomCompileParams() && !compilerSet()->customCompileParams().isEmpty()) {
result += " "+compilerSet()->customCompileParams();
}
return result;
}
QString Compiler::getCppCompileArguments(bool checkSyntax)
{
QString result;
if (checkSyntax) {
result += " -fsyntax-only";
}
foreach (const PCompilerOption& pOption, compilerSet()->options()) {
2021-04-20 22:24:33 +08:00
if (pOption->value > 0 && pOption->isCpp) {
if (pOption->choices.isEmpty()) {
result += " "+pOption->setting;
} else if (pOption->value < pOption->choices.size()) {
QStringList nameValue=pOption->choices[pOption->value].split('=');
if (nameValue.count()==2) {
result += " "+pOption->setting + nameValue[1];
}
}
}
}
if (compilerSet()->useCustomCompileParams() && !compilerSet()->customCompileParams().isEmpty()) {
result += " "+compilerSet()->customCompileParams();
}
return result;
}
QString Compiler::getCIncludeArguments()
{
QString result;
foreach (const QString& folder,compilerSet()->CIncludeDirs()) {
2021-04-20 22:24:33 +08:00
result += QString(" -I\"%1\"").arg(folder);
}
return result;
}
QString Compiler::getCppIncludeArguments()
{
QString result;
foreach (const QString& folder,compilerSet()->CppIncludeDirs()) {
2021-04-20 22:24:33 +08:00
result += QString(" -I\"%1\"").arg(folder);
}
return result;
}
QString Compiler::getLibraryArguments()
{
QString result;
foreach (const QString& folder, compilerSet()->libDirs()) {
2021-04-20 22:24:33 +08:00
result += QString(" -L\"%1\"").arg(folder);
}
// Add global compiler linker extras
if (compilerSet()->useCustomLinkParams() && !compilerSet()->customLinkParams().isEmpty()) {
result += " "+compilerSet()->customCompileParams();
}
//options like "-static" must be added after "-lxxx"
foreach (const PCompilerOption& pOption, compilerSet()->options()) {
2021-04-20 22:24:33 +08:00
if (pOption->value > 0 && pOption->isLinker) {
if (pOption->choices.isEmpty()) {
result += " " + pOption->setting;
} else if (pOption->value < pOption->choices.size()) {
QStringList nameValue=pOption->choices[pOption->value].split('=');
if (nameValue.count()==2) {
result += " " + pOption->setting + nameValue[1];
}
}
}
}
return result;
}
void Compiler::runCommand(const QString &cmd, const QString &arguments, const QString &workingDir, const QString& inputText)
{
QProcess process;
mStop = false;
bool errorOccurred = false;
2021-04-20 22:24:33 +08:00
process.setProgram(cmd);
process.setArguments(QProcess::splitCommand(arguments));
process.setWorkingDirectory(workingDir);
process.connect(&process, &QProcess::errorOccurred,
[&](){
errorOccurred= true;
});
2021-04-20 22:24:33 +08:00
process.connect(&process, &QProcess::readyReadStandardError,[&process,this](){
this->error(QString::fromLocal8Bit( process.readAllStandardError()));
2021-04-20 22:24:33 +08:00
});
process.connect(&process, &QProcess::readyReadStandardOutput,[&process,this](){
this->log(QString::fromLocal8Bit( process.readAllStandardOutput()));
2021-04-20 22:24:33 +08:00
});
process.connect(&process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),[this](){
2021-06-24 20:43:09 +08:00
this->error(COMPILE_PROCESS_END);
});
2021-04-20 22:24:33 +08:00
process.start();
2021-06-24 20:43:09 +08:00
process.waitForStarted(5000);
2021-04-20 22:24:33 +08:00
if (!inputText.isEmpty())
2021-06-24 20:43:09 +08:00
process.write(inputText.toLocal8Bit());
2021-04-20 22:24:33 +08:00
process.closeWriteChannel();
while (true) {
process.waitForFinished(1000);
if (process.state()!=QProcess::Running) {
break;
}
2021-06-25 12:40:11 +08:00
if (mStop) {
process.terminate();
2021-04-20 22:24:33 +08:00
}
2021-06-25 12:40:11 +08:00
if (errorOccurred)
break;
2021-04-20 22:24:33 +08:00
}
if (errorOccurred) {
switch (process.error()) {
case QProcess::FailedToStart:
throw CompileError(tr("The compiler process failed to start."));
break;
case QProcess::Crashed:
2021-06-25 12:40:11 +08:00
if (!mStop)
throw CompileError(tr("The compiler process crashed after starting successfully."));
break;
case QProcess::Timedout:
throw CompileError(tr("The last waitFor...() function timed out."));
break;
case QProcess::WriteError:
throw CompileError(tr("An error occurred when attempting to write to the compiler process."));
break;
case QProcess::ReadError:
throw CompileError(tr("An error occurred when attempting to read from the compiler process."));
break;
default:
throw CompileError(tr("An unknown error occurred."));
}
}
2021-04-20 22:24:33 +08:00
}
2021-06-25 12:40:11 +08:00
bool Compiler::isRebuild() const
{
return mRebuild;
}
void Compiler::setRebuild(bool isRebuild)
{
mRebuild = isRebuild;
}
2021-04-20 22:24:33 +08:00
void Compiler::log(const QString &msg)
{
emit compileOutput(msg);
}
void Compiler::error(const QString &msg)
{
2021-06-24 20:43:09 +08:00
if (msg != COMPILE_PROCESS_END)
emit compileOutput(msg);
2021-04-24 15:57:45 +08:00
for (QString& s:msg.split("\n")) {
if (!s.isEmpty())
processOutput(s);
}
2021-04-20 22:24:33 +08:00
}