diff --git a/NEWS.md b/NEWS.md index bed26140..47803aaa 100644 --- a/NEWS.md +++ b/NEWS.md @@ -33,6 +33,7 @@ Red Panda C++ Version 2.24 - fix: Can't suggest header filename starting with numbers. - enhancement: Better layout for compiler options page. - enhancement: False branches are displayed as comments. + - enhancement: Support SDCC Project. Red Panda C++ Version 2.23 diff --git a/RedPandaIDE/RedPandaIDE.pro b/RedPandaIDE/RedPandaIDE.pro index c57e629b..d9fae4bc 100644 --- a/RedPandaIDE/RedPandaIDE.pro +++ b/RedPandaIDE/RedPandaIDE.pro @@ -397,10 +397,12 @@ ENABLE_SDCC { DEFINES += ENABLE_SDCC SOURCES += \ - compiler/sdccfilecompiler.cpp + compiler/sdccfilecompiler.cpp \ + compiler/sdccprojectcompiler.cpp HEADERS += \ - compiler/sdccfilecompiler.h + compiler/sdccfilecompiler.h \ + compiler/sdccprojectcompiler.h } diff --git a/RedPandaIDE/compiler/compilermanager.cpp b/RedPandaIDE/compiler/compilermanager.cpp index ce4af2e4..255474fc 100644 --- a/RedPandaIDE/compiler/compilermanager.cpp +++ b/RedPandaIDE/compiler/compilermanager.cpp @@ -16,8 +16,10 @@ */ #include "compilermanager.h" #include "filecompiler.h" +#include "../project.h" #ifdef ENABLE_SDCC #include "sdccfilecompiler.h" +#include "sdccprojectcompiler.h" #endif #include "stdincompiler.h" #include "../mainwindow.h" @@ -126,7 +128,7 @@ void CompilerManager::compileProject(std::shared_ptr project, bool rebu mCompileErrorCount = 0; mCompileIssueCount = 0; //deleted when thread finished - mCompiler = new ProjectCompiler(project,false); + mCompiler = createProjectCompiler(project); mCompiler->setRebuild(rebuild); connect(mCompiler, &Compiler::finished, mCompiler, &QObject::deleteLater); connect(mCompiler, &Compiler::compileFinished, this, &CompilerManager::onCompileFinished); @@ -158,7 +160,7 @@ void CompilerManager::cleanProject(std::shared_ptr project) mCompileErrorCount = 0; mCompileIssueCount = 0; //deleted when thread finished - ProjectCompiler* compiler = new ProjectCompiler(project,false); + ProjectCompiler* compiler = createProjectCompiler(project); compiler->setOnlyClean(true); mCompiler = compiler; mCompiler->setRebuild(false); @@ -189,10 +191,10 @@ void CompilerManager::buildProjectMakefile(std::shared_ptr project) if (mCompiler!=nullptr) { return; } - ProjectCompiler compiler(project,false); - compiler.buildMakeFile(); + ProjectCompiler* pCompiler=createProjectCompiler(project); + pCompiler->buildMakeFile(); + delete pCompiler; } - } void CompilerManager::checkSyntax(const QString &filename, const QByteArray& encoding, const QString &content, std::shared_ptr project) @@ -461,6 +463,14 @@ void CompilerManager::onSyntaxCheckIssue(PCompileIssue issue) mSyntaxCheckIssueCount++; } +ProjectCompiler *CompilerManager::createProjectCompiler(std::shared_ptr project) +{ + if (project->options().type==ProjectType::MicroController) + return new SDCCProjectCompiler(project); + else + return new ProjectCompiler(project); +} + int CompilerManager::syntaxCheckIssueCount() const { return mSyntaxCheckIssueCount; diff --git a/RedPandaIDE/compiler/compilermanager.h b/RedPandaIDE/compiler/compilermanager.h index 6f1773c4..7bfb209c 100644 --- a/RedPandaIDE/compiler/compilermanager.h +++ b/RedPandaIDE/compiler/compilermanager.h @@ -25,6 +25,7 @@ class Runner; class Project; class Compiler; +class ProjectCompiler; struct OJProblem; using POJProblem = std::shared_ptr; struct OJProblemCase; @@ -85,7 +86,8 @@ private slots: void onCompileIssue(PCompileIssue issue); void onSyntaxCheckFinished(QString filename); void onSyntaxCheckIssue(PCompileIssue issue); - +private: + ProjectCompiler* createProjectCompiler(std::shared_ptr project); private: Compiler* mCompiler; int mCompileErrorCount; diff --git a/RedPandaIDE/compiler/projectcompiler.cpp b/RedPandaIDE/compiler/projectcompiler.cpp index bc43bf2e..5fa82921 100644 --- a/RedPandaIDE/compiler/projectcompiler.cpp +++ b/RedPandaIDE/compiler/projectcompiler.cpp @@ -23,8 +23,8 @@ #include -ProjectCompiler::ProjectCompiler(std::shared_ptr project, bool onlyCheckSyntax): - Compiler("",onlyCheckSyntax), +ProjectCompiler::ProjectCompiler(std::shared_ptr project): + Compiler("",false), mOnlyClean(false) { setProject(project); @@ -66,10 +66,8 @@ void ProjectCompiler::createStaticMakeFile() QFile file(mProject->makeFileName()); newMakeFile(file); writeln(file,"$(BIN): $(LINKOBJ)"); - if (!mOnlyCheckSyntax) { - writeln(file,"\tar r $(BIN) $(LINKOBJ)"); - writeln(file,"\tranlib $(BIN)"); - } + writeln(file,"\tar r $(BIN) $(LINKOBJ)"); + writeln(file,"\tranlib $(BIN)"); writeMakeObjFilesRules(file); } @@ -78,12 +76,10 @@ void ProjectCompiler::createDynamicMakeFile() QFile file(mProject->makeFileName()); newMakeFile(file); writeln(file,"$(BIN): $(LINKOBJ)"); - if (!mOnlyCheckSyntax) { - if (mProject->options().isCpp) { - writeln(file, "\t$(CPP) -mdll $(LINKOBJ) -o $(BIN) $(LIBS) -Wl,--output-def,$(DEF),--out-implib,$(STATIC)"); - } else { - writeln(file, "\t$(CC) -mdll $(LINKOBJ) -o $(BIN) $(LIBS) -Wl,--output-def,$(DEF),--out-implib,$(STATIC)"); - } + if (mProject->options().isCpp) { + writeln(file, "\t$(CPP) -mdll $(LINKOBJ) -o $(BIN) $(LIBS) -Wl,--output-def,$(DEF),--out-implib,$(STATIC)"); + } else { + writeln(file, "\t$(CC) -mdll $(LINKOBJ) -o $(BIN) $(LIBS) -Wl,--output-def,$(DEF),--out-implib,$(STATIC)"); } writeMakeObjFilesRules(file); } @@ -136,11 +132,6 @@ void ProjectCompiler::writeMakeHeader(QFile &file) writeln(file,"# Project: " + mProject->name()); writeln(file,QString("# Makefile created by Red Panda C++ ") + REDPANDA_CPP_VERSION); writeln(file); - if (mOnlyCheckSyntax) { - writeln(file,"# This Makefile is written for syntax check!"); - writeln(file,"# Regenerate it if you want to use this Makefile to build."); - writeln(file); - } } void ProjectCompiler::writeMakeDefines(QFile &file) @@ -221,8 +212,8 @@ void ProjectCompiler::writeMakeDefines(QFile &file) log(""); // Get list of applicable flags - QString cCompileArguments = getCCompileArguments(mOnlyCheckSyntax); - QString cppCompileArguments = getCppCompileArguments(mOnlyCheckSyntax); + QString cCompileArguments = getCCompileArguments(false); + QString cppCompileArguments = getCppCompileArguments(false); QString libraryArguments = getLibraryArguments(FileType::Project); QString cIncludeArguments = getCIncludeArguments() + " " + getProjectIncludeArguments(); QString cppIncludeArguments = getCppIncludeArguments() + " " +getProjectIncludeArguments(); @@ -240,7 +231,7 @@ void ProjectCompiler::writeMakeDefines(QFile &file) #endif if (!objResFile.isEmpty()) { writeln(file,"RES = " + objResFile2); - writeln(file,"OBJ = " + Objects + " $(RES)"); + writeln(file,"OBJ = " + Objects); writeln(file,"LINKOBJ = " + LinkObjects + " " + objResFile); #ifdef Q_OS_WIN writeln(file,"CLEANOBJ = " + cleanObjects + @@ -314,10 +305,7 @@ void ProjectCompiler::writeMakeDefines(QFile &file) void ProjectCompiler::writeMakeTarget(QFile &file) { - if (mOnlyCheckSyntax) - writeln(file, ".PHONY: all all-before all-after clean clean-custom $(OBJ) $(BIN)"); - else - writeln(file, ".PHONY: all all-before all-after clean clean-custom"); + writeln(file, ".PHONY: all all-before all-after clean clean-custom"); writeln(file); writeln(file, "all: all-before $(BIN) all-after"); writeln(file); @@ -468,21 +456,12 @@ void ProjectCompiler::writeMakeObjFilesRules(QFile &file) } if (fileType==FileType::CSource || fileType==FileType::CppSource) { - if (mOnlyCheckSyntax) { - if (unit->compileCpp()) - writeln(file, "\t$(CPP) -c " + genMakePath1(shortFileName) + " $(CXXFLAGS) " + encodingStr); - else - writeln(file, "\t$(CC) -c " + genMakePath1(shortFileName) + " $(CFLAGS) " + encodingStr); - } else { - if (unit->compileCpp()) - writeln(file, "\t$(CPP) -c " + genMakePath1(shortFileName) + " -o " + objFileName2 + " $(CXXFLAGS) " + encodingStr); - else - writeln(file, "\t$(CC) -c " + genMakePath1(shortFileName) + " -o " + objFileName2 + " $(CFLAGS) " + encodingStr); - } - } else if (fileType==FileType::GAS) { - if (!mOnlyCheckSyntax) { + if (unit->compileCpp()) + writeln(file, "\t$(CPP) -c " + genMakePath1(shortFileName) + " -o " + objFileName2 + " $(CXXFLAGS) " + encodingStr); + else writeln(file, "\t$(CC) -c " + genMakePath1(shortFileName) + " -o " + objFileName2 + " $(CFLAGS) " + encodingStr); - } + } else if (fileType==FileType::GAS) { + writeln(file, "\t$(CC) -c " + genMakePath1(shortFileName) + " -o " + objFileName2 + " $(CFLAGS) " + encodingStr); } } } @@ -499,17 +478,15 @@ void ProjectCompiler::writeMakeObjFilesRules(QFile &file) QString resFiles; // Concatenate all resource filenames (not created when syntax checking) - if (!mOnlyCheckSyntax) { - foreach(const PProjectUnit& unit, mProject->unitList()) { - if (getFileType(unit->fileName())!=FileType::WindowsResourceSource) - continue; - if (fileExists(unit->fileName())) { - QString ResFile = extractRelativePath(mProject->makeFileName(), unit->fileName()); - resFiles = resFiles + genMakePath2(ResFile) + ' '; - } + foreach(const PProjectUnit& unit, mProject->unitList()) { + if (getFileType(unit->fileName())!=FileType::WindowsResourceSource) + continue; + if (fileExists(unit->fileName())) { + QString ResFile = extractRelativePath(mProject->makeFileName(), unit->fileName()); + resFiles = resFiles + genMakePath2(ResFile) + ' '; } - resFiles = resFiles.trimmed(); } + resFiles = resFiles.trimmed(); // Determine resource output file QString fullName; @@ -530,16 +507,10 @@ void ProjectCompiler::writeMakeObjFilesRules(QFile &file) if (mProject->getCompileOption(CC_CMD_OPT_POINTER_SIZE)=="32") windresArgs = " -F pe-i386"; - if (mOnlyCheckSyntax) { - writeln(file); - writeln(file, objFileName2 + ':'); - writeln(file, "\t$(WINDRES) -i " + privResName + windresArgs + " --input-format=rc -o nul -O coff $(WINDRESFLAGS)" + ResIncludes); - } else { - writeln(file); - writeln(file, objFileName2 + ": " + privResName2 + ' ' + resFiles); - writeln(file, "\t$(WINDRES) -i " + privResName + windresArgs + " --input-format=rc -o " + objFileName + " -O coff $(WINDRESFLAGS)" - + ResIncludes); - } + writeln(file); + writeln(file, objFileName2 + ": " + privResName2 + ' ' + resFiles); + writeln(file, "\t$(WINDRES) -i " + privResName + windresArgs + " --input-format=rc -o " + objFileName + " -O coff $(WINDRESFLAGS)" + + ResIncludes); writeln(file); } #endif diff --git a/RedPandaIDE/compiler/projectcompiler.h b/RedPandaIDE/compiler/projectcompiler.h index 20406c32..552f76fc 100644 --- a/RedPandaIDE/compiler/projectcompiler.h +++ b/RedPandaIDE/compiler/projectcompiler.h @@ -26,10 +26,10 @@ class ProjectCompiler : public Compiler { Q_OBJECT public: - ProjectCompiler(std::shared_ptr project, bool onlyCheckSyntax); + ProjectCompiler(std::shared_ptr project); ProjectCompiler(const ProjectCompiler&)=delete; ProjectCompiler& operator=(const ProjectCompiler&)=delete; - void buildMakeFile(); + virtual void buildMakeFile(); bool onlyClean() const; void setOnlyClean(bool newOnlyClean); diff --git a/RedPandaIDE/compiler/sdccprojectcompiler.cpp b/RedPandaIDE/compiler/sdccprojectcompiler.cpp new file mode 100644 index 00000000..e360985e --- /dev/null +++ b/RedPandaIDE/compiler/sdccprojectcompiler.cpp @@ -0,0 +1,362 @@ +/* + * 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 . + */ +#include "sdccprojectcompiler.h" +#include "../project.h" +#include "compilermanager.h" +#include "../systemconsts.h" + +#include + +SDCCProjectCompiler::SDCCProjectCompiler(std::shared_ptr project): + ProjectCompiler(project) +{ +} + +void SDCCProjectCompiler::buildMakeFile() +{ + //we are using custom make file, don't overwrite it + if (mProject->options().useCustomMakefile && !mProject->options().customMakefile.isEmpty()) + return; + createStandardMakeFile(); +} + +void SDCCProjectCompiler::createStandardMakeFile() +{ + QFile file(mProject->makeFileName()); + newMakeFile(file); + QString suffix = compilerSet()->executableSuffix(); + if (suffix == SDCC_IHX_SUFFIX) { + writeln(file,"$(BIN): $(OBJ)"); + writeln(file,"\t$(CC) $(LIBS) -o $(BIN) $(LINKOBJ)"); + } else { + writeln(file,"$(IHX): $(OBJ)\n"); + writeln(file,"\t$(CC) $(LIBS) -o $(IHX) $(LINKOBJ)"); + if (suffix == SDCC_HEX_SUFFIX) { + writeln(file,"$(BIN): $(IHX)"); + writeln(file,"\t$(PACKIHX) $(IHX) > $(BIN)"); + } else { + writeln(file,"$(BIN): $(IHX)"); + writeln(file,"\t$(MAKEBIN) $(IHX) $(BIN)"); + } + } + writeMakeObjFilesRules(file); +} + +void SDCCProjectCompiler::newMakeFile(QFile& file) +{ + // Create OBJ output directory + if (!mProject->options().objectOutput.isEmpty()) { + QDir(mProject->directory()).mkpath(mProject->options().objectOutput); + } + // Create executable output directory + if (!mProject->options().exeOutput.isEmpty()) { + QDir(mProject->directory()).mkpath(mProject->options().exeOutput); + } + // Write more information to the log file than before + log(tr("Building makefile...")); + log("--------"); + log(tr("- Filename: %1").arg(mProject->makeFileName())); + + // Create the actual file + if (!file.open(QFile::WriteOnly | QFile::Truncate)) + throw CompileError(tr("Can't open '%1' for write!").arg(mProject->makeFileName())); + + // Write header + writeMakeHeader(file); + + // Writes definition list + writeMakeDefines(file); + + // Write PHONY and all targets + writeMakeTarget(file); + + // Write list of includes + writeMakeIncludes(file); + + // Write clean command + writeMakeClean(file); + +} + +void SDCCProjectCompiler::writeMakeHeader(QFile &file) +{ + writeln(file,"# Project: " + mProject->name()); + writeln(file,QString("# Makefile created by Red Panda C++ ") + REDPANDA_CPP_VERSION); + writeln(file); +} + +void SDCCProjectCompiler::writeMakeDefines(QFile &file) +{ + // Get list of object files + QString Objects; + QString LinkObjects; + QString cleanObjects; + + // Create a list of object files + foreach(const PProjectUnit &unit, mProject->unitList()) { + if (!unit->compile() && !unit->link()) + continue; + + // Only process source files + QString RelativeName = extractRelativePath(mProject->directory(), unit->fileName()); + FileType fileType = getFileType(RelativeName); + + if (fileType == FileType::CSource || fileType == FileType::CppSource + || fileType==FileType::GAS) { + if (!mProject->options().objectOutput.isEmpty()) { + // ofile = C:\MyProgram\obj\main.o + QString fullObjFile = includeTrailingPathDelimiter(mProject->options().objectOutput) + + extractFileName(unit->fileName()); + QString relativeObjFile = extractRelativePath(mProject->directory(), changeFileExt(fullObjFile, SDCC_REL_SUFFIX)); + QString objFile = genMakePath2(relativeObjFile); + Objects += ' ' + objFile; +#ifdef Q_OS_WIN + cleanObjects += ' ' + genMakePath1(relativeObjFile).replace("/",QDir::separator()); +#else + cleanObjects += ' ' + genMakePath1(relativeObjFile); +#endif + if (unit->link()) { + LinkObjects += ' ' + genMakePath1(relativeObjFile); + } + } else { + Objects += ' ' + genMakePath2(changeFileExt(RelativeName, SDCC_REL_SUFFIX)); +#ifdef Q_OS_WIN + cleanObjects += ' ' + genMakePath1(changeFileExt(RelativeName, SDCC_REL_SUFFIX)).replace("/",QDir::separator()); +#else + cleanObjects += ' ' + genMakePath1(changeFileExt(RelativeName, SDCC_REL_SUFFIX)); +#endif + if (unit->link()) + LinkObjects = LinkObjects + ' ' + genMakePath1(changeFileExt(RelativeName, SDCC_REL_SUFFIX)); + } + } + } + + Objects = Objects.trimmed(); + LinkObjects = LinkObjects.trimmed(); + + + // Get list of applicable flags + QString cCompileArguments = getCCompileArguments(mOnlyCheckSyntax); + QString libraryArguments = getLibraryArguments(FileType::Project); + QString cIncludeArguments = getCIncludeArguments() + " " + getProjectIncludeArguments(); + + if (cCompileArguments.indexOf(" -g3")>=0 + || cCompileArguments.startsWith("-g3")) { + cCompileArguments += " -D__DEBUG__"; + } + + writeln(file,"CC = " + extractFileName(compilerSet()->CCompiler())); + writeln(file,QString("PACKIHX = ") + PACKIHX_PROGRAM); + writeln(file,QString("MAKEBIN = ") + MAKEBIN_PROGRAM); + + writeln(file,"OBJ = " + Objects); + writeln(file,"LINKOBJ = " + LinkObjects); +#ifdef Q_OS_WIN + writeln(file,"CLEANOBJ = " + cleanObjects + + + " " + genMakePath1(extractRelativePath(mProject->makeFileName(), changeFileExt(mProject->executable(),SDCC_IHX_SUFFIX))).replace("/",QDir::separator()) + + " " + genMakePath1(extractRelativePath(mProject->makeFileName(), mProject->executable())).replace("/",QDir::separator()) ); +#else + writeln(file,"CLEANOBJ = " + cleanObjects + + + " " + genMakePath1(extractRelativePath(mProject->makeFileName(), mProject->executable()))); +#endif + libraryArguments.replace('\\', '/'); + writeln(file,"LIBS = " + libraryArguments); + cIncludeArguments.replace('\\', '/'); + writeln(file,"INCS = " + cIncludeArguments); + writeln(file,"IHX = " + genMakePath1(extractRelativePath(mProject->makeFileName(), changeFileExt(mProject->executable(), SDCC_IHX_SUFFIX)))); + writeln(file,"BIN = " + genMakePath1(extractRelativePath(mProject->makeFileName(), mProject->executable()))); + //writeln(file,"ENCODINGS = -finput-charset=utf-8 -fexec-charset='+GetSystemCharsetName); + cCompileArguments.replace('\\', '/'); + writeln(file,"CFLAGS = $(INCS) " + cCompileArguments); + writeln(file, QString("RM = ") + CLEAN_PROGRAM ); + + writeln(file); +} + +void SDCCProjectCompiler::writeMakeTarget(QFile &file) +{ + writeln(file, ".PHONY: all all-before all-after clean clean-custom"); + writeln(file); + writeln(file, "all: all-before $(BIN) all-after"); + writeln(file); + +} + +void SDCCProjectCompiler::writeMakeIncludes(QFile &file) +{ + foreach(const QString& s, mProject->options().makeIncludes) { + writeln(file, "include " + genMakePath1(s)); + } + if (!mProject->options().makeIncludes.isEmpty()) { + writeln(file); + } +} + +void SDCCProjectCompiler::writeMakeClean(QFile &file) +{ + writeln(file, "clean: clean-custom"); + QString target="$(CLEANOBJ)"; + + writeln(file, QString("\t-$(RM) %1 > %2 2>&1").arg(target,NULL_FILE)); + writeln(file); +} + +void SDCCProjectCompiler::writeMakeObjFilesRules(QFile &file) +{ + PCppParser parser = mProject->cppParser(); + QString precompileStr; + + QList projectUnits=mProject->unitList(); + foreach(const PProjectUnit &unit, projectUnits) { + if (!unit->compile()) + continue; + FileType fileType = getFileType(unit->fileName()); + // Only process source files + if (fileType!=FileType::CSource && fileType!=FileType::CppSource + && fileType!=FileType::GAS) + continue; + + QString shortFileName = extractRelativePath(mProject->makeFileName(),unit->fileName()); + + writeln(file); + QString objStr=genMakePath2(shortFileName); + // if we have scanned it, use scanned info + if (parser && parser->scannedFiles().contains(unit->fileName())) { + QSet fileIncludes = parser->getFileIncludes(unit->fileName()); + foreach(const PProjectUnit &unit2, projectUnits) { + if (unit2==unit) + continue; + if (fileIncludes.contains(unit2->fileName())) { + objStr = objStr + ' ' + genMakePath2(extractRelativePath(mProject->makeFileName(),unit2->fileName())); + } + } + } else { + foreach(const PProjectUnit &unit2, projectUnits) { + FileType fileType = getFileType(unit2->fileName()); + if (fileType == FileType::CHeader || fileType==FileType::CppHeader) + objStr = objStr + ' ' + genMakePath2(extractRelativePath(mProject->makeFileName(),unit2->fileName())); + } + } + QString objFileName; + QString objFileName2; + if (!mProject->options().objectOutput.isEmpty()) { + QString fullObjname = includeTrailingPathDelimiter(mProject->options().objectOutput) + + extractFileName(unit->fileName()); + objFileName = genMakePath2(extractRelativePath(mProject->makeFileName(), changeFileExt(fullObjname, SDCC_REL_SUFFIX))); + objFileName2 = genMakePath1(extractRelativePath(mProject->makeFileName(), changeFileExt(fullObjname, SDCC_REL_SUFFIX))); +// if (!extractFileDir(ObjFileName).isEmpty()) { +// objStr = genMakePath2(includeTrailingPathDelimiter(extractFileDir(ObjFileName))) + objStr; +// } + } else { + objFileName = genMakePath2(changeFileExt(shortFileName, SDCC_REL_SUFFIX)); + objFileName2 = genMakePath1(changeFileExt(shortFileName, SDCC_REL_SUFFIX)); + } + + objStr = objFileName + ": "+objStr+precompileStr; + + writeln(file,objStr); + + // Write custom build command + if (unit->overrideBuildCmd() && !unit->buildCmd().isEmpty()) { + QString BuildCmd = unit->buildCmd(); + BuildCmd.replace("", "\n\t"); + writeln(file, '\t' + BuildCmd); + // Or roll our own + } else { + if (fileType==FileType::CSource) { + writeln(file, "\t$(CC) $(CFLAGS) -c " + genMakePath1(shortFileName)); + } + } + } + +} + +void SDCCProjectCompiler::writeln(QFile &file, const QString &s) +{ + if (!s.isEmpty()) + file.write(s.toLocal8Bit()); + file.write("\n"); +} + +bool SDCCProjectCompiler::prepareForRebuild() +{ + //we use make argument to clean + return true; +} + +bool SDCCProjectCompiler::prepareForCompile() +{ + if (!mProject) + return false; + //initProgressForm(); + log(tr("Compiling project changes...")); + log("--------"); + log(tr("- Project Filename: %1").arg(mProject->filename())); + log(tr("- Compiler Set Name: %1").arg(compilerSet()->name())); + log(""); + + buildMakeFile(); + + mCompiler = compilerSet()->make(); + + if (!fileExists(mCompiler)) { + throw CompileError( + tr("Make program '%1' doesn't exists!").arg(mCompiler) + +"
" + +tr("Please check the \"program\" page of compiler settings.")); + } + + QString parallelParam; + if (mProject->options().allowParallelBuilding) { + if (mProject->options().parellelBuildingJobs==0) { + parallelParam = " --jobs"; + } else { + parallelParam = QString(" -j%1").arg(mProject->options().parellelBuildingJobs); + } + } + + if (onlyClean()) { + mArguments = QString(" %1 -f \"%2\" clean").arg(parallelParam, + extractRelativePath( + mProject->directory(), + mProject->makeFileName())); + } else if (mRebuild) { + mArguments = QString(" -f \"%1\" clean").arg(extractRelativePath( + mProject->directory(), + mProject->makeFileName())); + mExtraCompilersList.append(mCompiler); + mExtraOutputFilesList.append(""); + mExtraArgumentsList.append(QString(" %1 -f \"%2\" all").arg(parallelParam, + extractRelativePath( + mProject->directory(), + mProject->makeFileName()))); + } else { + mArguments = QString(" %1 -f \"%2\" all").arg(parallelParam, + extractRelativePath( + mProject->directory(), + mProject->makeFileName())); + } + mDirectory = mProject->directory(); + + log(tr("Processing makefile:")); + log("--------"); + log(tr("- makefile processer: %1").arg(mCompiler)); + log(tr("- Command: %1 %2").arg(extractFileName(mCompiler)).arg(mArguments)); + log(""); + + return true; +} diff --git a/RedPandaIDE/compiler/sdccprojectcompiler.h b/RedPandaIDE/compiler/sdccprojectcompiler.h new file mode 100644 index 00000000..550e5e1a --- /dev/null +++ b/RedPandaIDE/compiler/sdccprojectcompiler.h @@ -0,0 +1,50 @@ +/* + * 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 . + */ +#ifndef SDCCPROJECTCOMPILER_H +#define SDCCPROJECTCOMPILER_H + +#include "projectcompiler.h" +#include +#include + +class Project; +class SDCCProjectCompiler : public ProjectCompiler +{ + Q_OBJECT +public: + SDCCProjectCompiler(std::shared_ptr project); + SDCCProjectCompiler(const SDCCProjectCompiler&)=delete; + SDCCProjectCompiler& operator=(const SDCCProjectCompiler&)=delete; + void buildMakeFile(); + +private: + void createStandardMakeFile(); + void newMakeFile(QFile& file); + void writeMakeHeader(QFile& file); + void writeMakeDefines(QFile& file); + void writeMakeTarget(QFile& file); + void writeMakeIncludes(QFile& file); + void writeMakeClean(QFile& file); + void writeMakeObjFilesRules(QFile& file); + void writeln(QFile& file, const QString& s=""); + // Compiler interface +protected: + bool prepareForCompile() override; + bool prepareForRebuild() override; +}; + +#endif // PROJECTCOMPILER_H diff --git a/RedPandaIDE/parser/cppparser.cpp b/RedPandaIDE/parser/cppparser.cpp index fe0203c6..ef96dba7 100644 --- a/RedPandaIDE/parser/cppparser.cpp +++ b/RedPandaIDE/parser/cppparser.cpp @@ -855,6 +855,9 @@ QString CppParser::getHeaderFileName(const QString &relativeTo, const QString &h bool CppParser::isLineVisible(const QString &fileName, int line) { QMutexLocker locker(&mMutex); + if (mParsing) { + return true; + } PFileIncludes fileIncludes = mPreprocessor.includesList().value(fileName); if (!fileIncludes) return true; diff --git a/RedPandaIDE/project.cpp b/RedPandaIDE/project.cpp index f3d938ee..3fa5190c 100644 --- a/RedPandaIDE/project.cpp +++ b/RedPandaIDE/project.cpp @@ -130,6 +130,14 @@ QString Project::executable() const exeFileName = mOptions.overridenOutput; } else { switch(mOptions.type) { + case ProjectType::MicroController: { + Settings::PCompilerSet pSet=pSettings->compilerSets().getSet(mOptions.compilerSet); + if (pSet) + exeFileName = changeFileExt(extractFileName(mFilename),pSet->executableSuffix()); + else + exeFileName = changeFileExt(extractFileName(mFilename),SDCC_HEX_SUFFIX); + } + break; case ProjectType::StaticLib: exeFileName = changeFileExt(extractFileName(mFilename),STATIC_LIB_EXT); if (!exeFileName.startsWith("lib")) diff --git a/RedPandaIDE/projectoptions.h b/RedPandaIDE/projectoptions.h index d18ed671..b71a468a 100644 --- a/RedPandaIDE/projectoptions.h +++ b/RedPandaIDE/projectoptions.h @@ -35,7 +35,8 @@ enum class ProjectType { GUI=0, Console=1, StaticLib=2, - DynamicLib=3 + DynamicLib=3, + MicroController=4 }; struct ProjectVersionInfo{ diff --git a/RedPandaIDE/settings.cpp b/RedPandaIDE/settings.cpp index d68a1443..3177e82e 100644 --- a/RedPandaIDE/settings.cpp +++ b/RedPandaIDE/settings.cpp @@ -2456,7 +2456,34 @@ void Settings::CompilerSet::setExecutables() mDebugServer = findProgramInBinDirs(GDB_SERVER_PROGRAM); } mMake = findProgramInBinDirs(MAKE_PROGRAM); +#ifdef Q_OS_WIN mResourceCompiler = findProgramInBinDirs(WINDRES_PROGRAM); + if (mMake.isEmpty()) { + mMake = findProgramInBinDirs(MAKE2_PROGRAM); + } + if (mMake.isEmpty()) { + QSet searched; + + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + QString path = env.value("PATH"); + QStringList pathList = path.split(PATH_SEPARATOR); + QString folder; + for (int i=pathList.count()-1;i>=0;i--) { + folder = pathList[i]; + if (searched.contains(folder)) + continue; + searched.insert(folder); + QDir dir(folder); + if (dir.exists(MAKE_PROGRAM)) { + mMake = dir.absoluteFilePath(MAKE_PROGRAM); + break; + } else if (dir.exists(MAKE2_PROGRAM)) { + mMake = dir.absoluteFilePath(MAKE2_PROGRAM); + break; + } + } + } +#endif } void Settings::CompilerSet::setDirectories(const QString& binDir) diff --git a/RedPandaIDE/settingsdialog/projectcompilerwidget.cpp b/RedPandaIDE/settingsdialog/projectcompilerwidget.cpp index 243b67ab..e4f7646b 100644 --- a/RedPandaIDE/settingsdialog/projectcompilerwidget.cpp +++ b/RedPandaIDE/settingsdialog/projectcompilerwidget.cpp @@ -127,12 +127,34 @@ void ProjectCompilerWidget::on_cbCompilerSet_currentIndexChanged(int index) if (!mInitialized || index==project->options().compilerSet) { return; } + Settings::PCompilerSet pSet=pSettings->compilerSets().getSet(index); + if (pSet) { + if (project->options().type==ProjectType::MicroController) { + if (pSet->compilerType()!=CompilerType::SDCC) { + QMessageBox::information(this, + tr("Wrong Compiler Type"), + tr("Compiler %1 can't compile a microcontroller project.").arg(pSet->name()) + ); + ui->cbCompilerSet->setCurrentIndex(project->options().compilerSet); + return; + } + } else { + if (pSet->compilerType()==CompilerType::SDCC) { + QMessageBox::information(this, + tr("Wrong Compiler Type"), + tr("Compiler %1 can only compile microcontroller project.").arg(pSet->name()) + ); + ui->cbCompilerSet->setCurrentIndex(project->options().compilerSet); + return; + } + } + } if (QMessageBox::warning( this, - MainWindow::tr("Change Project Compiler Set"), - MainWindow::tr("Change the project's compiler set will lose all custom compiler set options.") + tr("Change Project Compiler Set"), + tr("Change the project's compiler set will lose all custom compiler set options.") +"
" - + MainWindow::tr("Do you really want to do that?"), + + tr("Do you really want to do that?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) { ui->cbCompilerSet->setCurrentIndex(project->options().compilerSet); diff --git a/RedPandaIDE/settingsdialog/projectgeneralwidget.cpp b/RedPandaIDE/settingsdialog/projectgeneralwidget.cpp index 8a8b4986..2f48cf1e 100644 --- a/RedPandaIDE/settingsdialog/projectgeneralwidget.cpp +++ b/RedPandaIDE/settingsdialog/projectgeneralwidget.cpp @@ -53,6 +53,14 @@ void ProjectGeneralWidget::doLoad() std::shared_ptr project = pMainWindow->project(); if (!project) return; + + bool isMicroControllerProject=(project->options().type==ProjectType::MicroController); + + ui->grpType->setVisible(!isMicroControllerProject); + ui->grpIcon->setVisible(!isMicroControllerProject); + ui->lblEncoding->setVisible(!isMicroControllerProject); + ui->panelEncoding->setVisible(!isMicroControllerProject); + ui->txtName->setText(project->name()); ui->txtFileName->setText(project->filename()); ui->txtOutputFile->setText(project->executable()); diff --git a/RedPandaIDE/settingsdialog/projectgeneralwidget.ui b/RedPandaIDE/settingsdialog/projectgeneralwidget.ui index 7a2d223b..bd4a35ed 100644 --- a/RedPandaIDE/settingsdialog/projectgeneralwidget.ui +++ b/RedPandaIDE/settingsdialog/projectgeneralwidget.ui @@ -60,7 +60,7 @@ - + Default encoding: @@ -70,7 +70,7 @@ - + 0 @@ -172,7 +172,7 @@ - + Type @@ -279,7 +279,7 @@ - + 0 diff --git a/RedPandaIDE/settingsdialog/settingsdialog.cpp b/RedPandaIDE/settingsdialog/settingsdialog.cpp index 5c3c8132..08b8bdd2 100644 --- a/RedPandaIDE/settingsdialog/settingsdialog.cpp +++ b/RedPandaIDE/settingsdialog/settingsdialog.cpp @@ -14,6 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include "../mainwindow.h" #include "settingsdialog.h" #include "ui_settingsdialog.h" #include "settingswidget.h" @@ -40,7 +41,6 @@ #include "debuggeneralwidget.h" #include "formattergeneralwidget.h" #include "languageasmgenerationwidget.h" -#include "languagecformatwidget.h" #include "projectgeneralwidget.h" #include "projectfileswidget.h" #include "projectcompilerwidget.h" @@ -252,6 +252,13 @@ PSettingsDialog SettingsDialog::projectOptionDialog() { PSettingsDialog dialog = std::make_shared(); + + bool isMicroControllerProject=false; + std::shared_ptr project = pMainWindow->project(); + if (project) + isMicroControllerProject=(project->options().type==ProjectType::MicroController); + + dialog->setWindowTitle(tr("Project Options")); SettingsWidget* widget = new ProjectGeneralWidget(tr("General"),tr("Project")); @@ -269,8 +276,10 @@ PSettingsDialog SettingsDialog::projectOptionDialog() widget = new ProjectDirectoriesWidget(tr("Directories"),tr("Project")); dialog->addWidget(widget); - widget = new ProjectPreCompileWidget(tr("Precompiled Header"),tr("Project")); - dialog->addWidget(widget); + if (!isMicroControllerProject) { + widget = new ProjectPreCompileWidget(tr("Precompiled Header"),tr("Project")); + dialog->addWidget(widget); + } widget = new ProjectMakefileWidget(tr("Makefile"),tr("Project")); dialog->addWidget(widget); @@ -278,12 +287,16 @@ PSettingsDialog SettingsDialog::projectOptionDialog() widget = new ProjectOutputWidget(tr("Output"),tr("Project")); dialog->addWidget(widget); - widget = new ProjectDLLHostWidget(tr("DLL host"),tr("Project")); - dialog->addWidget(widget); + if (!isMicroControllerProject) { + widget = new ProjectDLLHostWidget(tr("DLL host"),tr("Project")); + dialog->addWidget(widget); + } #ifdef Q_OS_WIN - widget = new ProjectVersionInfoWidget(tr("Version info"),tr("Project")); - dialog->addWidget(widget); + if (!isMicroControllerProject) { + widget = new ProjectVersionInfoWidget(tr("Version info"),tr("Project")); + dialog->addWidget(widget); + } #endif dialog->selectFirstWidget(); diff --git a/RedPandaIDE/systemconsts.h b/RedPandaIDE/systemconsts.h index 26df5b62..c2267554 100644 --- a/RedPandaIDE/systemconsts.h +++ b/RedPandaIDE/systemconsts.h @@ -40,6 +40,7 @@ #define SDCC_PROGRAM "sdcc.exe" #define PACKIHX_PROGRAM "packihx.exe" #define MAKEBIN_PROGRAM "makebin.exe" +#define MAKE2_PROGRAM "make.exe" #elif defined(Q_OS_LINUX) #define CONSOLE_PAUSER "consolepauser" #define ASSEMBLER "nasm" diff --git a/RedPandaIDE/widgets/newprojectdialog.cpp b/RedPandaIDE/widgets/newprojectdialog.cpp index 52caa826..10364414 100644 --- a/RedPandaIDE/widgets/newprojectdialog.cpp +++ b/RedPandaIDE/widgets/newprojectdialog.cpp @@ -121,7 +121,17 @@ void NewProjectDialog::addTemplate(const QString &filename) return; PProjectTemplate t = std::make_shared(); t->readTemplateFile(filename); - mTemplates.append(t); + Settings::PCompilerSet pSet=pSettings->compilerSets().defaultSet(); + if (pSet) { + if (pSet->compilerType()==CompilerType::SDCC) { + if (t->options().type==ProjectType::MicroController) + mTemplates.append(t); + } else { + if (t->options().type!=ProjectType::MicroController) + mTemplates.append(t); + } + } else + mTemplates.append(t); } void NewProjectDialog::readTemplateDirs() diff --git a/platform/linux/templates/sdcc-mcs51/info.template b/platform/linux/templates/sdcc-mcs51/info.template new file mode 100644 index 00000000..638d521c --- /dev/null +++ b/platform/linux/templates/sdcc-mcs51/info.template @@ -0,0 +1,18 @@ +[Template] +ver=2 +Name=MCS51 +Name[zh_CN]=MCS51 +Icon=microcontroller.ico +Description=A simple MCS51 program +Description[zh_CN]=MCS51单片机程序 +Category=SDCC +Category[zh_CN]=SDCC + +[Unit0] +CName=main.c +C=main.c + +[Project] +UnitCount=1 +Type=4 +IsCpp=0 diff --git a/platform/linux/templates/sdcc-mcs51/main.c b/platform/linux/templates/sdcc-mcs51/main.c new file mode 100644 index 00000000..2ddb35dd --- /dev/null +++ b/platform/linux/templates/sdcc-mcs51/main.c @@ -0,0 +1,7 @@ +#include <8051.h> + +void main(void) { + for(;;) { + P1--; + } +} \ No newline at end of file diff --git a/platform/linux/templates/sdcc-mcs51/microcontroller.ico b/platform/linux/templates/sdcc-mcs51/microcontroller.ico new file mode 100644 index 00000000..9bda4643 Binary files /dev/null and b/platform/linux/templates/sdcc-mcs51/microcontroller.ico differ diff --git a/platform/windows/templates/sdcc-mcs51/info.template b/platform/windows/templates/sdcc-mcs51/info.template new file mode 100644 index 00000000..638d521c --- /dev/null +++ b/platform/windows/templates/sdcc-mcs51/info.template @@ -0,0 +1,18 @@ +[Template] +ver=2 +Name=MCS51 +Name[zh_CN]=MCS51 +Icon=microcontroller.ico +Description=A simple MCS51 program +Description[zh_CN]=MCS51单片机程序 +Category=SDCC +Category[zh_CN]=SDCC + +[Unit0] +CName=main.c +C=main.c + +[Project] +UnitCount=1 +Type=4 +IsCpp=0 diff --git a/platform/windows/templates/sdcc-mcs51/main.c b/platform/windows/templates/sdcc-mcs51/main.c new file mode 100644 index 00000000..2ddb35dd --- /dev/null +++ b/platform/windows/templates/sdcc-mcs51/main.c @@ -0,0 +1,7 @@ +#include <8051.h> + +void main(void) { + for(;;) { + P1--; + } +} \ No newline at end of file diff --git a/platform/windows/templates/sdcc-mcs51/microcontroller.ico b/platform/windows/templates/sdcc-mcs51/microcontroller.ico new file mode 100644 index 00000000..9bda4643 Binary files /dev/null and b/platform/windows/templates/sdcc-mcs51/microcontroller.ico differ