20 import fnmatch |
20 import fnmatch |
21 import copy |
21 import copy |
22 import zipfile |
22 import zipfile |
23 |
23 |
24 from PyQt5.QtCore import pyqtSlot, QFile, QFileInfo, pyqtSignal, \ |
24 from PyQt5.QtCore import pyqtSlot, QFile, QFileInfo, pyqtSignal, \ |
25 QCryptographicHash, QIODevice, QByteArray, QObject, Qt |
25 QCryptographicHash, QIODevice, QByteArray, QObject, Qt, QProcess |
26 from PyQt5.QtGui import QCursor, QKeySequence |
26 from PyQt5.QtGui import QCursor, QKeySequence |
27 from PyQt5.QtWidgets import QLineEdit, QToolBar, QDialog, QInputDialog, \ |
27 from PyQt5.QtWidgets import QLineEdit, QToolBar, QDialog, QInputDialog, \ |
28 QApplication, QMenu, QAction |
28 QApplication, QMenu, QAction |
29 from PyQt5.Qsci import QsciScintilla |
29 from PyQt5.Qsci import QsciScintilla |
30 |
30 |
4153 self.makeTestAct.setWhatsThis(self.tr( |
4154 self.makeTestAct.setWhatsThis(self.tr( |
4154 """<b>Test for Changes</b>""" |
4155 """<b>Test for Changes</b>""" |
4155 """<p>This questions 'make', if a rebuild of the configured""" |
4156 """<p>This questions 'make', if a rebuild of the configured""" |
4156 """ target is necessary.</p>""" |
4157 """ target is necessary.</p>""" |
4157 )) |
4158 )) |
4158 self.makeTestAct.triggered.connect(self.__questionMake) |
4159 self.makeTestAct.triggered.connect( |
|
4160 lambda: self.__executeMake(questionOnly=True)) |
4159 self.actions.append(self.makeTestAct) |
4161 self.actions.append(self.makeTestAct) |
4160 |
4162 |
4161 self.closeAct.setEnabled(False) |
4163 self.closeAct.setEnabled(False) |
4162 self.saveAct.setEnabled(False) |
4164 self.saveAct.setEnabled(False) |
4163 self.saveasAct.setEnabled(False) |
4165 self.saveasAct.setEnabled(False) |
5456 |
5432 |
5457 @return flag indicating enabled make support |
5433 @return flag indicating enabled make support |
5458 @rtype bool |
5434 @rtype bool |
5459 """ |
5435 """ |
5460 return self.pdata["MAKEPARAMS"]["MakeEnabled"] |
5436 return self.pdata["MAKEPARAMS"]["MakeEnabled"] |
|
5437 |
|
5438 @pyqtSlot() |
|
5439 def executeMake(self): |
|
5440 """ |
|
5441 Public slot to execute a project specific make run (auto-run) |
|
5442 (execute or question). |
|
5443 """ |
|
5444 self.__executeMake( |
|
5445 questionOnly=self.pdata["MAKEPARAMS"]["MakeTestOnly"], |
|
5446 interactive=False) |
|
5447 |
|
5448 @pyqtSlot() |
|
5449 def __executeMake(self, questionOnly=False, interactive=True): |
|
5450 """ |
|
5451 Private method to execute a project specific make run. |
|
5452 |
|
5453 @param questionOnly flag indicating to ask make for changes only |
|
5454 @type bool |
|
5455 @param interactive flag indicating an interactive invocation (i.e. |
|
5456 through a menu action) |
|
5457 @type bool |
|
5458 """ |
|
5459 if not self.pdata["MAKEPARAMS"]["MakeEnabled"] or \ |
|
5460 self.__makeProcess is not None: |
|
5461 return |
|
5462 |
|
5463 if self.pdata["MAKEPARAMS"]["MakeExecutable"]: |
|
5464 prog = self.pdata["MAKEPARAMS"]["MakeExecutable"] |
|
5465 else: |
|
5466 prog = Project.DefaultMake |
|
5467 |
|
5468 args = [] |
|
5469 if self.pdata["MAKEPARAMS"]["MakeParameters"]: |
|
5470 args.extend(Utilities.parseOptionString( |
|
5471 self.pdata["MAKEPARAMS"]["MakeParameters"])) |
|
5472 |
|
5473 if self.pdata["MAKEPARAMS"]["MakeFile"]: |
|
5474 args.append("--makefile={0}".format( |
|
5475 self.pdata["MAKEPARAMS"]["MakeFile"])) |
|
5476 |
|
5477 if questionOnly: |
|
5478 args.append("--question") |
|
5479 |
|
5480 if self.pdata["MAKEPARAMS"]["MakeTarget"]: |
|
5481 args.append(self.pdata["MAKEPARAMS"]["MakeTarget"]) |
|
5482 |
|
5483 self.__makeProcess = QProcess(self) |
|
5484 self.__makeProcess.readyReadStandardOutput.connect( |
|
5485 self.__makeReadStdOut) |
|
5486 self.__makeProcess.readyReadStandardError.connect( |
|
5487 self.__makeReadStdErr) |
|
5488 self.__makeProcess.finished.connect( |
|
5489 lambda exitCode, exitStatus: self.__makeFinished( |
|
5490 exitCode, exitStatus, questionOnly, interactive)) |
|
5491 self.__makeProcess.setWorkingDirectory(self.getProjectPath()) |
|
5492 self.__makeProcess.start(prog, args) |
|
5493 |
|
5494 if not self.__makeProcess.waitForStarted(): |
|
5495 E5MessageBox.critical( |
|
5496 self.ui, |
|
5497 self.tr("Execute Make"), |
|
5498 self.tr("""The make process did not start.""")) |
|
5499 |
|
5500 self.__cleanupMake() |
|
5501 |
|
5502 @pyqtSlot() |
|
5503 def __makeReadStdOut(self): |
|
5504 """ |
|
5505 Private slot to process process output received via stdout. |
|
5506 """ |
|
5507 if self.__makeProcess is not None: |
|
5508 output = str(self.__makeProcess.readAllStandardOutput(), |
|
5509 Preferences.getSystem("IOEncoding"), |
|
5510 'replace') |
|
5511 self.appendStdout.emit(output) |
|
5512 |
|
5513 @pyqtSlot() |
|
5514 def __makeReadStdErr(self): |
|
5515 """ |
|
5516 Private slot to process process output received via stderr. |
|
5517 """ |
|
5518 if self.__makeProcess is not None: |
|
5519 error = str(self.__makeProcess.readAllStandardError(), |
|
5520 Preferences.getSystem("IOEncoding"), |
|
5521 'replace') |
|
5522 self.appendStderr.emit(error) |
|
5523 |
|
5524 def __makeFinished(self, exitCode, exitStatus, questionOnly, |
|
5525 interactive=True): |
|
5526 """ |
|
5527 Private slot handling the make process finished signal. |
|
5528 |
|
5529 @param exitCode exit code of the make process |
|
5530 @type int |
|
5531 @param exitStatus exit status of the make process |
|
5532 @type QProcess.ExitStatus |
|
5533 @param questionOnly flag indicating a test only run |
|
5534 @type bool |
|
5535 @param interactive flag indicating an interactive invocation (i.e. |
|
5536 through a menu action) |
|
5537 @type bool |
|
5538 """ |
|
5539 if exitStatus == QProcess.CrashExit: |
|
5540 E5MessageBox.critical( |
|
5541 self.ui, |
|
5542 self.tr("Execute Make"), |
|
5543 self.tr("""The make process crashed.""")) |
|
5544 else: |
|
5545 if questionOnly and exitCode == 1: |
|
5546 # a rebuild is needed |
|
5547 title = self.tr("Test for Changes") |
|
5548 |
|
5549 if self.pdata["MAKEPARAMS"]["MakeTarget"]: |
|
5550 message = self.tr( |
|
5551 """<p>There are changes that require the configured""" |
|
5552 """ make target <b>{0}</b> to be rebuilt.</p>""")\ |
|
5553 .format(self.pdata["MAKEPARAMS"]["MakeTarget"]) |
|
5554 else: |
|
5555 message = self.tr( |
|
5556 """<p>There are changes that require the default""" |
|
5557 """ make target to be rebuilt.</p>""") |
|
5558 |
|
5559 if self.ui.notificationsEnabled() and not interactive: |
|
5560 self.ui.showNotification( |
|
5561 UI.PixmapCache.getPixmap("makefile48.png"), |
|
5562 title, |
|
5563 message) |
|
5564 else: |
|
5565 E5MessageBox.information(self.ui, title, message) |
|
5566 elif exitCode > 1: |
|
5567 E5MessageBox.critical( |
|
5568 self.ui, |
|
5569 self.tr("Execute Make"), |
|
5570 self.tr("""The makefile contains errors.""")) |
|
5571 |
|
5572 self.__cleanupMake() |
|
5573 |
|
5574 def __cleanupMake(self): |
|
5575 """ |
|
5576 Private method to clean up make related stuff. |
|
5577 """ |
|
5578 self.__makeProcess.readyReadStandardOutput.disconnect() |
|
5579 self.__makeProcess.readyReadStandardError.disconnect() |
|
5580 self.__makeProcess.finished.disconnect() |
|
5581 self.__makeProcess.deleteLater() |
|
5582 self.__makeProcess = None |