1 | /* |
2 | * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com) |
3 | * |
4 | * This program is free software: you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by |
6 | * the Free Software Foundation, either version 3 of the License, or |
7 | * (at your option) any later version. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
16 | */ |
17 | #include "compiler.h" |
18 | #include "utils.h" |
19 | #include "compilermanager.h" |
20 | #include "../systemconsts.h" |
21 | |
22 | #include <QFileInfo> |
23 | #include <QProcess> |
24 | #include <QString> |
25 | #include <QTextCodec> |
26 | #include <QTime> |
27 | #include <QApplication> |
28 | #include "../editor.h" |
29 | #include "../mainwindow.h" |
30 | #include "../editorlist.h" |
31 | #include "../parser/cppparser.h" |
32 | #include "../autolinkmanager.h" |
33 | #include "qt_utils/charsetinfo.h" |
34 | #include "../project.h" |
35 | |
36 | #define COMPILE_PROCESS_END "---//END//----" |
37 | |
38 | Compiler::Compiler(const QString &filename, bool silent, bool onlyCheckSyntax): |
39 | QThread(), |
40 | mSilent(silent), |
41 | mOnlyCheckSyntax(onlyCheckSyntax), |
42 | mFilename(filename), |
43 | mRebuild(false) |
44 | { |
45 | |
46 | } |
47 | |
48 | void Compiler::run() |
49 | { |
50 | emit compileStarted(); |
51 | auto action = finally([this]{ |
52 | emit compileFinished(); |
53 | }); |
54 | try { |
55 | if (!prepareForCompile()){ |
56 | return; |
57 | } |
58 | if (mRebuild && !prepareForRebuild()) { |
59 | throw CompileError(tr("Clean before rebuild failed." )); |
60 | } |
61 | mErrorCount = 0; |
62 | mWarningCount = 0; |
63 | QElapsedTimer timer; |
64 | timer.start(); |
65 | runCommand(mCompiler, mArguments, mDirectory, pipedText()); |
66 | log("" ); |
67 | log(tr("Compile Result:" )); |
68 | log("------------------" ); |
69 | log(tr("- Errors: %1" ).arg(mErrorCount)); |
70 | log(tr("- Warnings: %1" ).arg(mWarningCount)); |
71 | if (!mOutputFile.isEmpty()) { |
72 | log(tr("- Output Filename: %1" ).arg(mOutputFile)); |
73 | QLocale locale = QLocale::system(); |
74 | log(tr("- Output Size: %1" ).arg(locale.formattedDataSize(QFileInfo(mOutputFile).size()))); |
75 | } |
76 | log(tr("- Compilation Time: %1 secs" ).arg(timer.elapsed() / 1000.0)); |
77 | } catch (CompileError e) { |
78 | emit compileErrorOccured(e.reason()); |
79 | } |
80 | |
81 | } |
82 | |
83 | QString Compiler::getFileNameFromOutputLine(QString &line) { |
84 | QString temp; |
85 | line = line.trimmed(); |
86 | while (true) { |
87 | int pos; |
88 | if (line.length() > 2 && line[1]==':') { // full file path at start, ignore this ':' |
89 | pos = line.indexOf(':',2); |
90 | } else { |
91 | pos = line.indexOf(':'); |
92 | } |
93 | if ( pos < 0) { |
94 | break; |
95 | } |
96 | temp = line.mid(0,pos); |
97 | line.remove(0,pos+1); |
98 | line=line.trimmed(); |
99 | if (temp.compare("<stdin>" , Qt::CaseInsensitive)==0 ) { |
100 | temp = mFilename; |
101 | break; |
102 | } |
103 | |
104 | if (QFileInfo(temp).fileName() == QLatin1String("ld.exe" )) { // skip ld.exe |
105 | continue; |
106 | } else if (QFileInfo(temp).suffix()=="o" ) { // skip obj file |
107 | continue; |
108 | } else { |
109 | break; |
110 | } |
111 | } |
112 | return temp; |
113 | } |
114 | |
115 | int Compiler::getLineNumberFromOutputLine(QString &line) |
116 | { |
117 | line = line.trimmed(); |
118 | int pos = line.indexOf(':'); |
119 | int result=0; |
120 | if (pos < 0) { |
121 | pos = line.indexOf(','); |
122 | } |
123 | if (pos>=0) { |
124 | result = line.midRef(0,pos).toInt(); |
125 | if (result > 0) |
126 | line.remove(0,pos+1); |
127 | } else { |
128 | result = line.toInt(); |
129 | if (result > 0) |
130 | line="" ; |
131 | } |
132 | return result; |
133 | } |
134 | |
135 | int Compiler::getColunmnFromOutputLine(QString &line) |
136 | { |
137 | line = line.trimmed(); |
138 | int pos = line.indexOf(':'); |
139 | int result=0; |
140 | if (pos < 0) { |
141 | pos = line.indexOf(','); |
142 | } |
143 | if (pos>=0) { |
144 | result = line.midRef(0,pos).toInt(); |
145 | if (result > 0) |
146 | line.remove(0,pos+1); |
147 | } |
148 | return result; |
149 | } |
150 | |
151 | CompileIssueType Compiler::getIssueTypeFromOutputLine(QString &line) |
152 | { |
153 | CompileIssueType result = CompileIssueType::Other; |
154 | line = line.trimmed(); |
155 | int pos = line.indexOf(':'); |
156 | if (pos>=0) { |
157 | QString s=line.mid(0,pos); |
158 | if (s == "error" || s == "fatal error" ) { |
159 | mErrorCount += 1; |
160 | line = tr("[Error] " )+line.mid(pos+1); |
161 | result = CompileIssueType::Error; |
162 | } else if (s == "warning" ) { |
163 | mWarningCount += 1; |
164 | line = tr("[Warning] " )+line.mid(pos+1); |
165 | result = CompileIssueType::Warning; |
166 | } else if (s == "info" ) { |
167 | mWarningCount += 1; |
168 | line = tr("[Info] " )+line.mid(pos+1); |
169 | result = CompileIssueType::Info; |
170 | } else if (s == "note" ) { |
171 | mWarningCount += 1; |
172 | line = tr("[Note] " )+line.mid(pos+1); |
173 | result = CompileIssueType::Note; |
174 | } |
175 | } |
176 | return result; |
177 | } |
178 | |
179 | Settings::PCompilerSet Compiler::compilerSet() |
180 | { |
181 | if (mProject) { |
182 | int index = mProject->options().compilerSet; |
183 | Settings::PCompilerSet set = pSettings->compilerSets().getSet(index); |
184 | if (set) |
185 | return set; |
186 | } |
187 | return pSettings->compilerSets().defaultSet(); |
188 | } |
189 | |
190 | QByteArray Compiler::pipedText() |
191 | { |
192 | return QByteArray(); |
193 | } |
194 | |
195 | void Compiler::processOutput(QString &line) |
196 | { |
197 | if (line == COMPILE_PROCESS_END) { |
198 | if (mLastIssue) { |
199 | emit compileIssue(mLastIssue); |
200 | mLastIssue.reset(); |
201 | } |
202 | return; |
203 | } |
204 | if (line.startsWith(">>>" )) |
205 | line.remove(0,3); |
206 | QString referencePrefix = QString(" referenced by " ); |
207 | if(mLastIssue && line.startsWith(referencePrefix)) { |
208 | line.remove(0,referencePrefix.length()); |
209 | mLastIssue->filename = getFileNameFromOutputLine(line); |
210 | qDebug()<<line; |
211 | mLastIssue->line = getLineNumberFromOutputLine(line); |
212 | emit compileIssue(mLastIssue); |
213 | mLastIssue.reset(); |
214 | return; |
215 | } |
216 | QString inFilePrefix = QString("In file included from " ); |
217 | QString fromPrefix = QString("from " ); |
218 | PCompileIssue issue = std::make_shared<CompileIssue>(); |
219 | issue->type = CompileIssueType::Other; |
220 | issue->endColumn = -1; |
221 | if (line.startsWith(inFilePrefix)) { |
222 | line.remove(0,inFilePrefix.length()); |
223 | issue->filename = getFileNameFromOutputLine(line); |
224 | issue->line = getLineNumberFromOutputLine(line); |
225 | if (issue->line > 0) |
226 | issue->column = getColunmnFromOutputLine(line); |
227 | issue->type = getIssueTypeFromOutputLine(line); |
228 | issue->description = inFilePrefix + issue->filename; |
229 | emit compileIssue(issue); |
230 | return; |
231 | } else if(line.startsWith(fromPrefix)) { |
232 | line.remove(0,fromPrefix.length()); |
233 | issue->filename = getFileNameFromOutputLine(line); |
234 | issue->line = getLineNumberFromOutputLine(line); |
235 | if (issue->line > 0) |
236 | issue->column = getColunmnFromOutputLine(line); |
237 | issue->type = getIssueTypeFromOutputLine(line); |
238 | issue->description = " from " + issue->filename; |
239 | emit compileIssue(issue); |
240 | return; |
241 | } |
242 | |
243 | // Ignore code snippets that GCC produces |
244 | // they always start with a space |
245 | if (line.length()>0 && line[0] == ' ') { |
246 | if (!mLastIssue) |
247 | return; |
248 | QString s = line.trimmed(); |
249 | if (s.startsWith('|') && s.indexOf('^')) { |
250 | int pos = 0; |
251 | while (pos < s.length()) { |
252 | if (s[pos]=='^') |
253 | break; |
254 | pos++; |
255 | } |
256 | if (pos<s.length()) { |
257 | int i=pos+1; |
258 | while (i<s.length()) { |
259 | if (s[i]!='~' && s[i]!='^') |
260 | break; |
261 | i++; |
262 | } |
263 | mLastIssue->endColumn = mLastIssue->column+i-pos; |
264 | emit compileIssue(mLastIssue); |
265 | mLastIssue.reset(); |
266 | } |
267 | } |
268 | return; |
269 | } |
270 | |
271 | if (mLastIssue) { |
272 | emit compileIssue(mLastIssue); |
273 | mLastIssue.reset(); |
274 | } |
275 | |
276 | // assume regular main.cpp:line:col: message |
277 | issue->filename = getFileNameFromOutputLine(line); |
278 | issue->line = getLineNumberFromOutputLine(line); |
279 | if (issue->line > 0) { |
280 | issue->column = getColunmnFromOutputLine(line); |
281 | issue->type = getIssueTypeFromOutputLine(line); |
282 | if (issue->column<=0 && issue->type == CompileIssueType::Other) { |
283 | issue->type = CompileIssueType::Error; //linkage error |
284 | mErrorCount += 1; |
285 | } |
286 | } else { |
287 | issue->column = -1; |
288 | issue->type = getIssueTypeFromOutputLine(line); |
289 | } |
290 | issue->description = line.trimmed(); |
291 | if (issue->line<=0 && (issue->filename=="ld" || issue->filename=="lld" )) { |
292 | mLastIssue = issue; |
293 | } else if (issue->line<=0) { |
294 | emit compileIssue(issue); |
295 | } else |
296 | mLastIssue = issue; |
297 | } |
298 | |
299 | void Compiler::stopCompile() |
300 | { |
301 | mStop = true; |
302 | } |
303 | |
304 | QString Compiler::getCharsetArgument(const QByteArray& encoding,FileType fileType, bool checkSyntax) |
305 | { |
306 | QString result; |
307 | bool forceExecUTF8=false; |
308 | // test if force utf8 from autolink infos |
309 | if ((fileType == FileType::CSource || |
310 | fileType == FileType::CppSource) && pSettings->editor().enableAutolink() ){ |
311 | Editor* editor = pMainWindow->editorList()->getEditor(); |
312 | if (editor) { |
313 | PCppParser parser = editor->parser(); |
314 | if (parser) { |
315 | int waitCount = 0; |
316 | //wait parsing ends, at most 1 second |
317 | while(parser->parsing()) { |
318 | if (waitCount>10) |
319 | break; |
320 | waitCount++; |
321 | QThread::msleep(100); |
322 | QApplication *app=dynamic_cast<QApplication*>( |
323 | QApplication::instance()); |
324 | app->processEvents(); |
325 | } |
326 | QSet<QString> parsedFiles; |
327 | forceExecUTF8 = parseForceUTF8ForAutolink( |
328 | editor->filename(), |
329 | parsedFiles, |
330 | parser); |
331 | } |
332 | } |
333 | |
334 | } |
335 | if ((forceExecUTF8 || compilerSet()->autoAddCharsetParams()) && encoding != ENCODING_ASCII |
336 | && compilerSet()->compilerType()!=COMPILER_CLANG) { |
337 | QString encodingName; |
338 | QString execEncodingName; |
339 | QString compilerSetExecCharset = compilerSet()->execCharset(); |
340 | QString systemEncodingName=pCharsetInfoManager->getDefaultSystemEncoding(); |
341 | if (encoding == ENCODING_SYSTEM_DEFAULT) { |
342 | encodingName = systemEncodingName; |
343 | } else if (encoding == ENCODING_UTF8_BOM) { |
344 | encodingName = "UTF-8" ; |
345 | } else { |
346 | encodingName = encoding; |
347 | } |
348 | if (forceExecUTF8) { |
349 | execEncodingName = "UTF-8" ; |
350 | } else if (compilerSetExecCharset == ENCODING_SYSTEM_DEFAULT || compilerSetExecCharset.isEmpty()) { |
351 | execEncodingName = systemEncodingName; |
352 | } else { |
353 | execEncodingName = compilerSetExecCharset; |
354 | } |
355 | qDebug()<<encodingName<<execEncodingName; |
356 | if (checkSyntax) { |
357 | result += QString(" -finput-charset=%1" ) |
358 | .arg(encodingName); |
359 | } else if (encodingName!=execEncodingName) { |
360 | result += QString(" -finput-charset=%1 -fexec-charset=%2" ) |
361 | .arg(encodingName, execEncodingName); |
362 | } |
363 | } |
364 | return result; |
365 | } |
366 | |
367 | QString Compiler::getCCompileArguments(bool checkSyntax) |
368 | { |
369 | QString result; |
370 | if (checkSyntax) { |
371 | result += " -fsyntax-only" ; |
372 | } |
373 | |
374 | QMap<QString, QString> compileOptions; |
375 | if (mProject && !mProject->options().compilerOptions.isEmpty()) { |
376 | compileOptions = mProject->options().compilerOptions; |
377 | } else { |
378 | compileOptions = compilerSet()->compileOptions(); |
379 | } |
380 | foreach (const QString& key, compileOptions.keys()) { |
381 | if (compileOptions[key]=="" ) |
382 | continue; |
383 | PCompilerOption pOption = CompilerInfoManager::getCompilerOption(compilerSet()->compilerType(), key); |
384 | if (pOption && pOption->isC && !pOption->isLinker) { |
385 | if (pOption->choices.isEmpty()) |
386 | result += " " + pOption->setting; |
387 | else |
388 | result += " " + pOption->setting + compilerSet()->getCompileOptionValue(key); |
389 | } |
390 | } |
391 | |
392 | if (compilerSet()->useCustomCompileParams() && !compilerSet()->customCompileParams().isEmpty()) { |
393 | QStringList params = textToLines(compilerSet()->customCompileParams()); |
394 | foreach(const QString& param, params) |
395 | result += " " + parseMacros(param); |
396 | } |
397 | |
398 | if (mProject) { |
399 | QString s = mProject->options().compilerCmd; |
400 | if (!s.isEmpty()) { |
401 | s.replace("_@@_" , " " ); |
402 | QStringList params = textToLines(s); |
403 | foreach(const QString& param, params) |
404 | result += " " + parseMacros(param); |
405 | } |
406 | } |
407 | return result; |
408 | } |
409 | |
410 | QString Compiler::getCppCompileArguments(bool checkSyntax) |
411 | { |
412 | QString result; |
413 | if (checkSyntax) { |
414 | result += " -fsyntax-only" ; |
415 | } |
416 | QMap<QString, QString> compileOptions; |
417 | if (mProject && !mProject->options().compilerOptions.isEmpty()) { |
418 | compileOptions = mProject->options().compilerOptions; |
419 | } else { |
420 | compileOptions = compilerSet()->compileOptions(); |
421 | } |
422 | foreach (const QString& key, compileOptions.keys()) { |
423 | if (compileOptions[key]=="" ) |
424 | continue; |
425 | PCompilerOption pOption = CompilerInfoManager::getCompilerOption(compilerSet()->compilerType(), key); |
426 | if (pOption && pOption->isCpp && !pOption->isLinker) { |
427 | if (pOption->choices.isEmpty()) |
428 | result += " " + pOption->setting; |
429 | else |
430 | result += " " + pOption->setting + compilerSet()->getCompileOptionValue(key); |
431 | } |
432 | } |
433 | if (compilerSet()->useCustomCompileParams() && !compilerSet()->customCompileParams().isEmpty()) { |
434 | QStringList params = textToLines(compilerSet()->customCompileParams()); |
435 | foreach(const QString& param, params) |
436 | result += " " + parseMacros(param); |
437 | } |
438 | if (mProject) { |
439 | QString s = mProject->options().cppCompilerCmd; |
440 | if (!s.isEmpty()) { |
441 | s.replace("_@@_" , " " ); |
442 | QStringList params = textToLines(s); |
443 | foreach(const QString& param, params) |
444 | result += " " + parseMacros(param); |
445 | } |
446 | } |
447 | return result; |
448 | } |
449 | |
450 | |
451 | QString Compiler::getCIncludeArguments() |
452 | { |
453 | QString result; |
454 | foreach (const QString& folder,compilerSet()->CIncludeDirs()) { |
455 | result += QString(" -I\"%1\"" ).arg(folder); |
456 | } |
457 | return result; |
458 | } |
459 | |
460 | QString Compiler::getProjectIncludeArguments() |
461 | { |
462 | QString result; |
463 | if (mProject) { |
464 | foreach (const QString& folder,mProject->options().includeDirs) { |
465 | result += QString(" -I\"%1\"" ).arg(folder); |
466 | } |
467 | // result += QString(" -I\"%1\"").arg(extractFilePath(mProject->filename())); |
468 | } |
469 | return result; |
470 | } |
471 | |
472 | QString Compiler::getCppIncludeArguments() |
473 | { |
474 | QString result; |
475 | foreach (const QString& folder,compilerSet()->CppIncludeDirs()) { |
476 | result += QString(" -I\"%1\"" ).arg(folder); |
477 | } |
478 | return result; |
479 | } |
480 | |
481 | QString Compiler::getLibraryArguments(FileType fileType) |
482 | { |
483 | QString result; |
484 | |
485 | //Add libraries |
486 | foreach (const QString& folder, compilerSet()->libDirs()) { |
487 | result += QString(" -L\"%1\"" ).arg(folder); |
488 | } |
489 | |
490 | //add libs added via project |
491 | if (mProject) { |
492 | foreach (const QString& folder, mProject->options().libDirs){ |
493 | result += QString(" -L\"%1\"" ).arg(folder); |
494 | } |
495 | } |
496 | |
497 | //Add auto links |
498 | // is file and auto link enabled |
499 | if (pSettings->editor().enableAutolink() && (fileType == FileType::CSource || |
500 | fileType == FileType::CppSource)){ |
501 | Editor* editor = pMainWindow->editorList()->getEditor(); |
502 | if (editor) { |
503 | PCppParser parser = editor->parser(); |
504 | if (parser) { |
505 | int waitCount = 0; |
506 | //wait parsing ends, at most 1 second |
507 | while(parser->parsing()) { |
508 | if (waitCount>10) |
509 | break; |
510 | waitCount++; |
511 | QThread::msleep(100); |
512 | QApplication *app=dynamic_cast<QApplication*>( |
513 | QApplication::instance()); |
514 | app->processEvents(); |
515 | } |
516 | QSet<QString> parsedFiles; |
517 | result += parseFileIncludesForAutolink( |
518 | editor->filename(), |
519 | parsedFiles, |
520 | parser); |
521 | } |
522 | } |
523 | |
524 | } |
525 | |
526 | //add compiler set link options |
527 | //options like "-static" must be added after "-lxxx" |
528 | QMap<QString, QString> compileOptions; |
529 | if (mProject && !mProject->options().compilerOptions.isEmpty()) { |
530 | compileOptions = mProject->options().compilerOptions; |
531 | } else { |
532 | compileOptions = compilerSet()->compileOptions(); |
533 | } |
534 | foreach (const QString& key, compileOptions.keys()) { |
535 | if (compileOptions[key]=="" ) |
536 | continue; |
537 | PCompilerOption pOption = CompilerInfoManager::getCompilerOption(compilerSet()->compilerType(), key); |
538 | if (pOption && pOption->isLinker) { |
539 | if (pOption->choices.isEmpty()) |
540 | result += " " + pOption->setting; |
541 | else |
542 | result += " " + pOption->setting + compilerSet()->getCompileOptionValue(key); |
543 | } |
544 | } |
545 | |
546 | |
547 | // Add global compiler linker extras |
548 | if (compilerSet()->useCustomLinkParams() && !compilerSet()->customLinkParams().isEmpty()) { |
549 | QStringList params = textToLines(compilerSet()->customLinkParams()); |
550 | if (!params.isEmpty()) { |
551 | foreach(const QString& param, params) |
552 | result += " " + param; |
553 | } |
554 | } |
555 | |
556 | if (mProject) { |
557 | if (mProject->options().type == ProjectType::GUI) { |
558 | result += " -mwindows" ; |
559 | } |
560 | |
561 | if (!mProject->options().linkerCmd.isEmpty()) { |
562 | QString s = mProject->options().linkerCmd; |
563 | if (!s.isEmpty()) { |
564 | s.replace("_@@_" , " " ); |
565 | QStringList params = textToLines(s); |
566 | if (!params.isEmpty()) { |
567 | foreach(const QString& param, params) |
568 | result += " " + param; |
569 | } |
570 | } |
571 | } |
572 | if (mProject->options().staticLink) |
573 | result += " -static" ; |
574 | } else if (compilerSet()->staticLink()) { |
575 | result += " -static" ; |
576 | } |
577 | return result; |
578 | } |
579 | |
580 | QString Compiler::parseFileIncludesForAutolink( |
581 | const QString &filename, |
582 | QSet<QString>& parsedFiles, |
583 | PCppParser& parser) |
584 | { |
585 | QString result; |
586 | if (parsedFiles.contains(filename)) |
587 | return result; |
588 | parsedFiles.insert(filename); |
589 | PAutolink autolink = pAutolinkManager->getLink(filename); |
590 | if (autolink) { |
591 | result += ' '+autolink->linkOption; |
592 | } |
593 | QStringList includedFiles = parser->getFileDirectIncludes(filename); |
594 | // log(QString("File %1 included:").arg(filename)); |
595 | // for (int i=includedFiles.size()-1;i>=0;i--) { |
596 | // QString includeFilename = includedFiles[i]; |
597 | // log(includeFilename); |
598 | // } |
599 | |
600 | for (int i=includedFiles.size()-1;i>=0;i--) { |
601 | QString includeFilename = includedFiles[i]; |
602 | result += parseFileIncludesForAutolink( |
603 | includeFilename, |
604 | parsedFiles, |
605 | parser); |
606 | } |
607 | return result; |
608 | } |
609 | |
610 | bool Compiler::parseForceUTF8ForAutolink(const QString &filename, QSet<QString> &parsedFiles, PCppParser &parser) |
611 | { |
612 | bool result; |
613 | if (parsedFiles.contains(filename)) |
614 | return false; |
615 | parsedFiles.insert(filename); |
616 | PAutolink autolink = pAutolinkManager->getLink(filename); |
617 | if (autolink && autolink->execUseUTF8) { |
618 | return true; |
619 | } |
620 | QStringList includedFiles = parser->getFileDirectIncludes(filename); |
621 | // log(QString("File %1 included:").arg(filename)); |
622 | // for (int i=includedFiles.size()-1;i>=0;i--) { |
623 | // QString includeFilename = includedFiles[i]; |
624 | // log(includeFilename); |
625 | // } |
626 | |
627 | for (int i=includedFiles.size()-1;i>=0;i--) { |
628 | QString includeFilename = includedFiles[i]; |
629 | result = parseForceUTF8ForAutolink( |
630 | includeFilename, |
631 | parsedFiles, |
632 | parser); |
633 | if (result) |
634 | return true; |
635 | } |
636 | return false; |
637 | } |
638 | |
639 | void Compiler::runCommand(const QString &cmd, const QString &arguments, const QString &workingDir, const QByteArray& inputText) |
640 | { |
641 | QProcess process; |
642 | mStop = false; |
643 | bool errorOccurred = false; |
644 | process.setProgram(cmd); |
645 | QString cmdDir = extractFileDir(cmd); |
646 | QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); |
647 | if (!cmdDir.isEmpty()) { |
648 | QString path = env.value("PATH" ); |
649 | if (path.isEmpty()) { |
650 | path = cmdDir; |
651 | } else { |
652 | path = cmdDir + PATH_SEPARATOR + path; |
653 | } |
654 | env.insert("PATH" ,path); |
655 | } |
656 | env.insert("LANG" ,"en" ); |
657 | env.insert("LDFLAGS" ,"-Wl,--stack,12582912" ); |
658 | env.insert("CFLAGS" ,"" ); |
659 | env.insert("CXXFLAGS" ,"" ); |
660 | process.setProcessEnvironment(env); |
661 | process.setArguments(splitProcessCommand(arguments)); |
662 | process.setWorkingDirectory(workingDir); |
663 | |
664 | process.connect(&process, &QProcess::errorOccurred, |
665 | [&](){ |
666 | errorOccurred= true; |
667 | }); |
668 | process.connect(&process, &QProcess::readyReadStandardError,[&process,this](){ |
669 | if (compilerSet()->compilerType() == COMPILER_CLANG) |
670 | this->error(QString::fromUtf8(process.readAllStandardError())); |
671 | else |
672 | this->error(QString::fromLocal8Bit( process.readAllStandardError())); |
673 | }); |
674 | process.connect(&process, &QProcess::readyReadStandardOutput,[&process,this](){ |
675 | if (compilerSet()->compilerType() == COMPILER_CLANG) |
676 | this->log(QString::fromUtf8(process.readAllStandardOutput())); |
677 | else |
678 | this->log(QString::fromLocal8Bit( process.readAllStandardOutput())); |
679 | }); |
680 | process.connect(&process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),[this](){ |
681 | this->error(COMPILE_PROCESS_END); |
682 | }); |
683 | process.start(); |
684 | process.waitForStarted(5000); |
685 | if (!inputText.isEmpty()) { |
686 | process.write(inputText); |
687 | process.waitForFinished(0); |
688 | } |
689 | bool writeChannelClosed = false; |
690 | while (true) { |
691 | if (process.bytesToWrite()==0 && !writeChannelClosed ) { |
692 | writeChannelClosed=true; |
693 | process.closeWriteChannel(); |
694 | } |
695 | process.waitForFinished(100); |
696 | if (process.state()!=QProcess::Running) { |
697 | break; |
698 | } |
699 | if (mStop) { |
700 | process.terminate(); |
701 | } |
702 | if (errorOccurred) |
703 | break; |
704 | } |
705 | if (errorOccurred) { |
706 | switch (process.error()) { |
707 | case QProcess::FailedToStart: |
708 | throw CompileError(tr("The compiler process for '%1' failed to start." ).arg(mFilename)); |
709 | break; |
710 | case QProcess::Crashed: |
711 | if (!mStop) |
712 | throw CompileError(tr("The compiler process crashed after starting successfully." )); |
713 | break; |
714 | case QProcess::Timedout: |
715 | throw CompileError(tr("The last waitFor...() function timed out." )); |
716 | break; |
717 | case QProcess::WriteError: |
718 | throw CompileError(tr("An error occurred when attempting to write to the compiler process." )); |
719 | break; |
720 | case QProcess::ReadError: |
721 | throw CompileError(tr("An error occurred when attempting to read from the compiler process." )); |
722 | break; |
723 | default: |
724 | throw CompileError(tr("An unknown error occurred." )); |
725 | } |
726 | } |
727 | } |
728 | |
729 | const std::shared_ptr<Project> &Compiler::project() const |
730 | { |
731 | return mProject; |
732 | } |
733 | |
734 | void Compiler::setProject(const std::shared_ptr<Project> &newProject) |
735 | { |
736 | mProject = newProject; |
737 | } |
738 | |
739 | bool Compiler::isRebuild() const |
740 | { |
741 | return mRebuild; |
742 | } |
743 | |
744 | void Compiler::setRebuild(bool isRebuild) |
745 | { |
746 | mRebuild = isRebuild; |
747 | } |
748 | |
749 | void Compiler::log(const QString &msg) |
750 | { |
751 | emit compileOutput(msg); |
752 | } |
753 | |
754 | void Compiler::error(const QString &msg) |
755 | { |
756 | if (msg != COMPILE_PROCESS_END) |
757 | emit compileOutput(msg); |
758 | for (QString& s:msg.split("\n" )) { |
759 | if (!s.isEmpty()) |
760 | processOutput(s); |
761 | } |
762 | } |
763 | |