- fix: When start parsing and exit app, app may crash

- enhancement: add "Allow parallel build" option in project option dialog's custom compile options page
  - fix: crash when rename project file
  - fix: When remove project file, symbols in it not correctly removed from code parser
  - fix: infos in class browser (structure panel) not correctly updated when add/create/remove/rename project files
This commit is contained in:
Roy Qu 2022-11-06 22:51:14 +08:00
parent d642ff3b74
commit 93751d1b23
15 changed files with 367 additions and 231 deletions

17
NEWS.md
View File

@ -1,3 +1,12 @@
Red Panda C++ Version 2.3
- fix: When start parsing and exit app, app may crash
- enhancement: add "Allow parallel build" option in project option dialog's custom compile options page
- fix: crash when rename project file
- fix: When remove project file, symbols in it not correctly removed from code parser
- fix: infos in class browser (structure panel) not correctly updated when add/create/remove/rename project files
Red Panda C++ Version 2.2 Red Panda C++ Version 2.2
- enhancement: basic code completion support for C++ lambdas - enhancement: basic code completion support for C++ lambdas
@ -10,9 +19,11 @@ Red Panda C++ Version 2.2
- fix: Edting / show context menu when code analysis is turned on may crash app. - fix: Edting / show context menu when code analysis is turned on may crash app.
- fix: Show context menu when edting non c/c++ file may crash app. - fix: Show context menu when edting non c/c++ file may crash app.
- fix: Project Options Dialog's Files panel will crash app. - fix: Project Options Dialog's Files panel will crash app.
- fix: Memory usage of undo system is not correctly calculated - fix: Memory usage of undo system is not correctly calculated, which may cause undo items lost
- fix: Set max undo memory usage to 0 don't really remove limit of undo - fix: Set max undo memory usage to 0 don't really remove the limit for undo
- fix: Set max undo times to 0 don't really remove limit of undo - fix: Set max undo times to 0 don't really remove the limit for undo
- fix: Keep the newest undo info regardless of undo memory usage
Red Panda C++ Version 2.1 Red Panda C++ Version 2.1

View File

@ -10,7 +10,7 @@ isEmpty(APP_NAME) {
} }
isEmpty(APP_VERSION) { isEmpty(APP_VERSION) {
APP_VERSION = 2.2 APP_VERSION = 2.3
} }
macos: { macos: {

View File

@ -210,6 +210,7 @@ void ProjectCompiler::writeMakeDefines(QFile &file)
cCompileArguments += " -D__DEBUG__"; cCompileArguments += " -D__DEBUG__";
cppCompileArguments+= " -D__DEBUG__"; cppCompileArguments+= " -D__DEBUG__";
} }
writeln(file,"CPP = " + extractFileName(compilerSet()->cppCompiler())); writeln(file,"CPP = " + extractFileName(compilerSet()->cppCompiler()));
writeln(file,"CC = " + extractFileName(compilerSet()->CCompiler())); writeln(file,"CC = " + extractFileName(compilerSet()->CCompiler()));
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@ -407,9 +408,10 @@ void ProjectCompiler::writeMakeObjFilesRules(QFile &file)
} }
} else if (unit->encoding()==ENCODING_SYSTEM_DEFAULT) { } else if (unit->encoding()==ENCODING_SYSTEM_DEFAULT) {
sourceEncoding = defaultSystemEncoding; sourceEncoding = defaultSystemEncoding;
} else if (unit->encoding()!=ENCODING_ASCII && !unit->encoding().isEmpty() } else if (unit->encoding()!=ENCODING_ASCII && !unit->encoding().isEmpty()) {
&& unit->encoding()!=targetEncoding) {
sourceEncoding = unit->encoding(); sourceEncoding = unit->encoding();
} else {
sourceEncoding = targetEncoding;
} }
if (sourceEncoding!=targetEncoding) { if (sourceEncoding!=targetEncoding) {
@ -421,14 +423,14 @@ void ProjectCompiler::writeMakeObjFilesRules(QFile &file)
if (mOnlyCheckSyntax) { if (mOnlyCheckSyntax) {
if (unit->compileCpp()) if (unit->compileCpp())
writeln(file, "\t$(CPP) -c " + genMakePath1(unit->fileName()) + " $(CXXFLAGS) " + encodingStr); writeln(file, "\t$(CPP) -c " + genMakePath1(shortFileName) + " $(CXXFLAGS) " + encodingStr);
else else
writeln(file, "\t(CC) -c " + genMakePath1(unit->fileName()) + " $(CFLAGS) " + encodingStr); writeln(file, "\t(CC) -c " + genMakePath1(shortFileName) + " $(CFLAGS) " + encodingStr);
} else { } else {
if (unit->compileCpp()) if (unit->compileCpp())
writeln(file, "\t$(CPP) -c " + genMakePath1(unit->fileName()) + " -o " + ObjFileName2 + " $(CXXFLAGS) " + encodingStr); writeln(file, "\t$(CPP) -c " + genMakePath1(shortFileName) + " -o " + ObjFileName2 + " $(CXXFLAGS) " + encodingStr);
else else
writeln(file, "\t$(CC) -c " + genMakePath1(unit->fileName()) + " -o " + ObjFileName2 + " $(CFLAGS) " + encodingStr); writeln(file, "\t$(CC) -c " + genMakePath1(shortFileName) + " -o " + ObjFileName2 + " $(CFLAGS) " + encodingStr);
} }
} }
} }
@ -526,16 +528,29 @@ bool ProjectCompiler::prepareForCompile()
buildMakeFile(); buildMakeFile();
mCompiler = compilerSet()->make(); mCompiler = compilerSet()->make();
QString parallelParam;
if (mProject->options().allowParallelBuilding) {
if (mProject->options().parellelBuildingJobs==0) {
parallelParam = " --jobs";
} else {
parallelParam = QString(" -j%1").arg(mProject->options().parellelBuildingJobs);
}
}
if (mOnlyClean) { if (mOnlyClean) {
mArguments = QString("-f \"%1\" clean").arg(extractRelativePath( mArguments = QString(" %1 -f \"%2\" clean").arg(parallelParam,
extractRelativePath(
mProject->directory(), mProject->directory(),
mProject->makeFileName())); mProject->makeFileName()));
} else if (mRebuild) { } else if (mRebuild) {
mArguments = QString("-f \"%1\" clean all").arg(extractRelativePath( mArguments = QString(" %1 -f \"%2\" clean all").arg(parallelParam,
extractRelativePath(
mProject->directory(), mProject->directory(),
mProject->makeFileName())); mProject->makeFileName()));
} else { } else {
mArguments = QString("-f \"%1\" all").arg(extractRelativePath( mArguments = QString(" %1 -f \"%2\" all").arg(parallelParam,
extractRelativePath(
mProject->directory(), mProject->directory(),
mProject->makeFileName())); mProject->makeFileName()));
} }

View File

@ -422,6 +422,8 @@ Editor* EditorList::getOpenedEditorByFilename(QString filename)
bool EditorList::getContentFromOpenedEditor(const QString &filename, QStringList &buffer) bool EditorList::getContentFromOpenedEditor(const QString &filename, QStringList &buffer)
{ {
if (pMainWindow->isQuitting())
return false;
Editor * e= getOpenedEditorByFilename(filename); Editor * e= getOpenedEditorByFilename(filename);
if (!e) if (!e)
return false; return false;

View File

@ -6354,7 +6354,7 @@ void MainWindow::on_actionAdd_to_project_triggered()
PProjectModelNode folderNode = mProject->pointerToNode(node); PProjectModelNode folderNode = mProject->pointerToNode(node);
foreach (const QString& filename, dialog.selectedFiles()) { foreach (const QString& filename, dialog.selectedFiles()) {
PProjectUnit newUnit = mProject->addUnit(filename,folderNode); PProjectUnit newUnit = mProject->addUnit(filename,folderNode);
mProject->cppParser()->addFileToScan(filename); mProject->cppParser()->addFileToScan(filename,true);
QString branch; QString branch;
if (pSettings->vcs().gitOk() && mProject->model()->iconProvider()->VCSRepository()->hasRepository(branch)) { if (pSettings->vcs().gitOk() && mProject->model()->iconProvider()->VCSRepository()->hasRepository(branch)) {
QString output; QString output;
@ -6372,6 +6372,7 @@ void MainWindow::on_actionAdd_to_project_triggered()
} }
} }
} }
mClassBrowserModel.setCurrentFiles(mProject->unitFiles());
mProject->saveAll(); mProject->saveAll();
updateProjectView(); updateProjectView();
parseFileList(mProject->cppParser()); parseFileList(mProject->cppParser());
@ -6399,8 +6400,15 @@ void MainWindow::on_actionRemove_from_project_triggered()
if (!folderNode) if (!folderNode)
continue; continue;
PProjectUnit unit = folderNode->pUnit.lock(); PProjectUnit unit = folderNode->pUnit.lock();
if (mProject->cppParser()) {
mProject->cppParser()->invalidateFile(unit->fileName());
mProject->cppParser()->removeProjectFile(unit->fileName());
}
mProject->removeUnit(unit, true, removeFile); mProject->removeUnit(unit, true, removeFile);
}; };
mClassBrowserModel.beginUpdate();
mClassBrowserModel.setCurrentFiles(mProject->unitFiles());
mClassBrowserModel.endUpdate();
ui->projectView->selectionModel()->clearSelection(); ui->projectView->selectionModel()->clearSelection();
mProject->saveAll(); mProject->saveAll();
updateProjectView(); updateProjectView();
@ -6708,6 +6716,9 @@ void MainWindow::newProjectUnitFile()
setProjectViewCurrentUnit(newUnit); setProjectViewCurrentUnit(newUnit);
mProject->saveAll(); mProject->saveAll();
if (mProject->cppParser())
mProject->cppParser()->addFileToScan(newFileName,true);
mClassBrowserModel.setCurrentFiles(mProject->unitFiles());
Editor * editor = mProject->openUnit(newUnit, false); Editor * editor = mProject->openUnit(newUnit, false);
if (editor) if (editor)
editor->activate(); editor->activate();
@ -6837,6 +6848,7 @@ QString MainWindow::switchHeaderSourceTarget(Editor *editor)
void MainWindow::onProjectViewNodeRenamed() void MainWindow::onProjectViewNodeRenamed()
{ {
mClassBrowserModel.setCurrentFiles(mProject->unitFiles());
updateProjectView(); updateProjectView();
} }
@ -7950,8 +7962,11 @@ void MainWindow::on_actionNew_Header_triggered()
stringsToFile(header, headerFilename); stringsToFile(header, headerFilename);
PProjectUnit newUnit=mProject->addUnit(headerFilename,mProject->rootNode()); PProjectUnit newUnit=mProject->addUnit(headerFilename,mProject->rootNode());
mProject->cppParser()->addFileToScan(headerFilename);
mProject->saveAll(); mProject->saveAll();
mProject->cppParser()->addFileToScan(headerFilename,true);
mClassBrowserModel.setCurrentFiles(mProject->unitFiles());
parseFileList(mProject->cppParser()); parseFileList(mProject->cppParser());
setProjectViewCurrentUnit(newUnit); setProjectViewCurrentUnit(newUnit);
updateProjectView(); updateProjectView();
@ -8024,12 +8039,16 @@ void MainWindow::on_actionNew_Class_triggered()
stringsToFile(source, sourceFilename); stringsToFile(source, sourceFilename);
PProjectUnit newUnit=mProject->addUnit(headerFilename,mProject->rootNode()); PProjectUnit newUnit=mProject->addUnit(headerFilename,mProject->rootNode());
mProject->cppParser()->addFileToScan(headerFilename);
setProjectViewCurrentUnit(newUnit); setProjectViewCurrentUnit(newUnit);
newUnit=mProject->addUnit(sourceFilename,mProject->rootNode()); newUnit=mProject->addUnit(sourceFilename,mProject->rootNode());
mProject->cppParser()->addFileToScan(sourceFilename);
setProjectViewCurrentUnit(newUnit); setProjectViewCurrentUnit(newUnit);
mProject->saveAll(); mProject->saveAll();
mProject->cppParser()->addFileToScan(sourceFilename,true);
mProject->cppParser()->addFileToScan(headerFilename,true);
mClassBrowserModel.setCurrentFiles(mProject->unitFiles());
parseFileList(mProject->cppParser()); parseFileList(mProject->cppParser());
updateProjectView(); updateProjectView();

View File

@ -97,6 +97,14 @@ void CppParser::addIncludePath(const QString &value)
mPreprocessor.addIncludePath(includeTrailingPathDelimiter(value)); mPreprocessor.addIncludePath(includeTrailingPathDelimiter(value));
} }
void CppParser::removeProjectFile(const QString &value)
{
QMutexLocker locker(&mMutex);
mProjectFiles.remove(value);
mFilesToScan.remove(value);
}
void CppParser::addProjectIncludePath(const QString &value) void CppParser::addProjectIncludePath(const QString &value)
{ {
QMutexLocker locker(&mMutex); QMutexLocker locker(&mMutex);
@ -1126,7 +1134,6 @@ void CppParser::addFileToScan(const QString& value, bool inProject)
if (!mPreprocessor.scannedFiles().contains(value)) { if (!mPreprocessor.scannedFiles().contains(value)) {
mFilesToScan.insert(value); mFilesToScan.insert(value);
} }
} }
PStatement CppParser::addInheritedStatement(const PStatement& derived, const PStatement& inherit, StatementClassScope access) PStatement CppParser::addInheritedStatement(const PStatement& derived, const PStatement& inherit, StatementClassScope access)
@ -4719,8 +4726,8 @@ QSet<QString> CppParser::calculateFilesToBeReparsed(const QString &fileName)
QSet<QString> result; QSet<QString> result;
result.insert(fileName); result.insert(fileName);
foreach (const QString& file, mProjectFiles) { foreach (const QString& file, mProjectFiles) {
PFileIncludes fileIncludes = mPreprocessor.includesList()[file]; PFileIncludes fileIncludes = mPreprocessor.includesList().value(file,PFileIncludes());
if (fileIncludes->includeFiles.contains(fileName)) { if (fileIncludes && fileIncludes->includeFiles.contains(fileName)) {
result.insert(file); result.insert(file);
} }
} }

View File

@ -36,6 +36,7 @@ public:
void addHardDefineByLine(const QString& line); void addHardDefineByLine(const QString& line);
void addFileToScan(const QString& value, bool inProject = false); void addFileToScan(const QString& value, bool inProject = false);
void addIncludePath(const QString& value); void addIncludePath(const QString& value);
void removeProjectFile(const QString& value);
void addProjectIncludePath(const QString& value); void addProjectIncludePath(const QString& value);
void clearIncludePaths(); void clearIncludePaths();
void clearProjectIncludePaths(); void clearProjectIncludePaths();

View File

@ -699,13 +699,20 @@ void Project::renameUnit(PProjectUnit& unit, const QString &sFileName)
return; return;
if (sFileName.compare(unit->fileName(),PATH_SENSITIVITY)==0) if (sFileName.compare(unit->fileName(),PATH_SENSITIVITY)==0)
return; return;
if (mParser) {
mParser->removeProjectFile(unit->fileName());
mParser->addFileToScan(sFileName,true);
}
Editor * editor=unitEditor(unit); Editor * editor=unitEditor(unit);
if (editor) { if (editor) {
//prevent recurse //prevent recurse
editor->saveAs(sFileName,true); editor->saveAs(sFileName,true);
} else { } else {
if (mParser)
mParser->invalidateFile(unit->fileName());
copyFile(unit->fileName(),sFileName,true); copyFile(unit->fileName(),sFileName,true);
if (mParser)
mParser->parseFile(sFileName,true);
} }
removeUnit(unit,false,true); removeUnit(unit,false,true);
PProjectModelNode parentNode = unit->node()->parent.lock(); PProjectModelNode parentNode = unit->node()->parent.lock();
@ -1135,6 +1142,10 @@ void Project::saveOptions()
ini.SetValue("Project","Encoding",toByteArray(mOptions.encoding)); ini.SetValue("Project","Encoding",toByteArray(mOptions.encoding));
ini.SetLongValue("Project","ModelType", (int)mOptions.modelType); ini.SetLongValue("Project","ModelType", (int)mOptions.modelType);
ini.SetLongValue("Project","ClassBrowserType", (int)mOptions.classBrowserType); ini.SetLongValue("Project","ClassBrowserType", (int)mOptions.classBrowserType);
ini.SetBoolValue("Project","AllowParallelBuilding",mOptions.allowParallelBuilding);
ini.SetLongValue("Project","ParellelBuildingJobs",mOptions.parellelBuildingJobs);
//for Red Panda Dev C++ 6 compatibility //for Red Panda Dev C++ 6 compatibility
ini.SetLongValue("Project","UseUTF8",mOptions.encoding == ENCODING_UTF8); ini.SetLongValue("Project","UseUTF8",mOptions.encoding == ENCODING_UTF8);
@ -2056,6 +2067,10 @@ void Project::loadOptions(SimpleIni& ini)
mOptions.encoding = fromByteArray(ini.GetValue("Project","Encoding", ENCODING_AUTO_DETECT)); mOptions.encoding = fromByteArray(ini.GetValue("Project","Encoding", ENCODING_AUTO_DETECT));
} }
mOptions.allowParallelBuilding = ini.GetBoolValue("Project","AllowParallelBuilding");
mOptions.parellelBuildingJobs = ini.GetLongValue("Project","ParellelBuildingJobs");
mOptions.versionInfo.major = ini.GetLongValue("VersionInfo", "Major", 0); mOptions.versionInfo.major = ini.GetLongValue("VersionInfo", "Major", 0);
mOptions.versionInfo.minor = ini.GetLongValue("VersionInfo", "Minor", 1); mOptions.versionInfo.minor = ini.GetLongValue("VersionInfo", "Minor", 1);
mOptions.versionInfo.release = ini.GetLongValue("VersionInfo", "Release", 1); mOptions.versionInfo.release = ini.GetLongValue("VersionInfo", "Release", 1);

View File

@ -56,4 +56,6 @@ ProjectOptions::ProjectOptions()
modelType = ProjectModelType::FileSystem; modelType = ProjectModelType::FileSystem;
classBrowserType = ProjectClassBrowserType::CurrentFile; classBrowserType = ProjectClassBrowserType::CurrentFile;
execEncoding = ENCODING_SYSTEM_DEFAULT; execEncoding = ENCODING_SYSTEM_DEFAULT;
allowParallelBuilding=false;
parellelBuildingJobs=0;
} }

View File

@ -99,5 +99,7 @@ struct ProjectOptions{
QString encoding; QString encoding;
ProjectModelType modelType; ProjectModelType modelType;
ProjectClassBrowserType classBrowserType; ProjectClassBrowserType classBrowserType;
bool allowParallelBuilding;
int parellelBuildingJobs;
}; };
#endif // PROJECTOPTIONS_H #endif // PROJECTOPTIONS_H

View File

@ -20,6 +20,10 @@
#include "../project.h" #include "../project.h"
#include "../iconsmanager.h" #include "../iconsmanager.h"
#ifdef Q_OS_WIN
#include <sysinfoapi.h>
#endif
#include <QFileDialog> #include <QFileDialog>
ProjectCompileParamatersWidget::ProjectCompileParamatersWidget(const QString &name, const QString &group, QWidget *parent) : ProjectCompileParamatersWidget::ProjectCompileParamatersWidget(const QString &name, const QString &group, QWidget *parent) :
@ -27,6 +31,11 @@ ProjectCompileParamatersWidget::ProjectCompileParamatersWidget(const QString &na
ui(new Ui::ProjectCompileParamatersWidget) ui(new Ui::ProjectCompileParamatersWidget)
{ {
ui->setupUi(this); ui->setupUi(this);
#ifdef Q_OS_WIN
SYSTEM_INFO info;
GetSystemInfo(&info);
ui->spinParallelJobs->setMaximum(info.dwNumberOfProcessors);
#endif
} }
ProjectCompileParamatersWidget::~ProjectCompileParamatersWidget() ProjectCompileParamatersWidget::~ProjectCompileParamatersWidget()
@ -39,6 +48,8 @@ void ProjectCompileParamatersWidget::doLoad()
ui->txtCCompiler->setPlainText(pMainWindow->project()->options().compilerCmd); ui->txtCCompiler->setPlainText(pMainWindow->project()->options().compilerCmd);
ui->txtCPPCompiler->setPlainText(pMainWindow->project()->options().cppCompilerCmd); ui->txtCPPCompiler->setPlainText(pMainWindow->project()->options().cppCompilerCmd);
ui->txtLinker->setPlainText(pMainWindow->project()->options().linkerCmd); ui->txtLinker->setPlainText(pMainWindow->project()->options().linkerCmd);
ui->grpAllowParallelBuilding->setChecked(pMainWindow->project()->options().allowParallelBuilding);
ui->spinParallelJobs->setValue(pMainWindow->project()->options().parellelBuildingJobs);
} }
void ProjectCompileParamatersWidget::doSave() void ProjectCompileParamatersWidget::doSave()
@ -46,6 +57,8 @@ void ProjectCompileParamatersWidget::doSave()
pMainWindow->project()->options().compilerCmd = ui->txtCCompiler->toPlainText(); pMainWindow->project()->options().compilerCmd = ui->txtCCompiler->toPlainText();
pMainWindow->project()->options().cppCompilerCmd = ui->txtCPPCompiler->toPlainText(); pMainWindow->project()->options().cppCompilerCmd = ui->txtCPPCompiler->toPlainText();
pMainWindow->project()->options().linkerCmd = ui->txtLinker->toPlainText(); pMainWindow->project()->options().linkerCmd = ui->txtLinker->toPlainText();
pMainWindow->project()->options().allowParallelBuilding = ui->grpAllowParallelBuilding->isChecked();
pMainWindow->project()->options().parellelBuildingJobs = ui->spinParallelJobs->value();
pMainWindow->project()->saveOptions(); pMainWindow->project()->saveOptions();
} }

View File

@ -14,13 +14,41 @@
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="grpAllowParallelBuilding">
<property name="title">
<string>Parallel Build</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item> <item>
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>Additional build options:</string> <string>Parallel Jobs(0 means infinite):</string>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QSpinBox" name="spinParallelJobs"/>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QTabWidget" name="tabCommands"> <widget class="QTabWidget" name="tabCommands">
<property name="currentIndex"> <property name="currentIndex">

View File

@ -5103,7 +5103,7 @@
</message> </message>
<message> <message>
<source>Additional build options:</source> <source>Additional build options:</source>
<translation>Opções de montagem adicionais:</translation> <translation type="vanished">Opções de montagem adicionais:</translation>
</message> </message>
<message> <message>
<source>C Compiler</source> <source>C Compiler</source>
@ -5125,6 +5125,14 @@
<source>Library Files</source> <source>Library Files</source>
<translation>Arquivos de bibliotecas</translation> <translation>Arquivos de bibliotecas</translation>
</message> </message>
<message>
<source>Parallel Build</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Parallel Jobs(0 means infinite):</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ProjectCompiler</name> <name>ProjectCompiler</name>

File diff suppressed because it is too large Load Diff

View File

@ -4914,10 +4914,6 @@
<source>Form</source> <source>Form</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Additional build options:</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>C Compiler</source> <source>C Compiler</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@ -4938,6 +4934,14 @@
<source>Library Files</source> <source>Library Files</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Parallel Build</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Parallel Jobs(0 means infinite):</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ProjectCompiler</name> <name>ProjectCompiler</name>