RedPanda-CPP/RedPandaIDE/compiler/projectcompiler.cpp

455 lines
17 KiB
C++
Raw Normal View History

2021-09-13 07:49:36 +08:00
#include "projectcompiler.h"
2021-09-13 22:45:50 +08:00
#include "../project.h"
2021-09-13 10:48:44 +08:00
#include "compilermanager.h"
#include "../systemconsts.h"
2021-09-13 22:45:50 +08:00
#include "../platform.h"
#include "../editor.h"
2021-09-13 10:48:44 +08:00
#include <QDir>
2021-09-13 07:49:36 +08:00
ProjectCompiler::ProjectCompiler(std::shared_ptr<Project> project, bool silent, bool onlyCheckSyntax):
Compiler("",silent,onlyCheckSyntax)
{
setProject(project);
}
void ProjectCompiler::buildMakeFile()
{
2021-09-13 10:48:44 +08:00
//we are using custom make file, don't overwrite it
if (!mProject->options().customMakefile.isEmpty())
return;
2021-09-13 07:49:36 +08:00
switch(mProject->options().type) {
case ProjectType::StaticLib:
createStaticMakeFile();
break;
case ProjectType::DynamicLib:
createDynamicMakeFile();
break;
default:
createStandardMakeFile();
}
}
2021-09-13 10:48:44 +08:00
void ProjectCompiler::createStandardMakeFile()
{
QFile file(mProject->makeFileName());
newMakeFile(file);
file.write("$(BIN): $(OBJ)\n");
if (!mOnlyCheckSyntax) {
if (mProject->options().useGPP) {
writeln(file,"\t$(CPP) $(LINKOBJ) -o \"$(BIN)\" $(LIBS)");
} else
writeln(file,"\t$(CC) $(LINKOBJ) -o \"$(BIN)\" $(LIBS)");
}
writeMakeObjFilesRules(file);
}
2021-09-13 22:45:50 +08:00
void ProjectCompiler::createStaticMakeFile()
{
QFile file(mProject->makeFileName());
newMakeFile(file);
file.write("$(BIN): $(LINKOBJ)");
if (!mOnlyCheckSyntax) {
writeln(file,"\tar r $(BIN) $(LINKOBJ)");
writeln(file,"\tranlib $(BIN)");
}
writeMakeObjFilesRules(file);
}
void ProjectCompiler::createDynamicMakeFile()
{
QFile file(mProject->makeFileName());
newMakeFile(file);
file.write("$(BIN): $(LINKOBJ)");
if (!mOnlyCheckSyntax) {
if (mProject->options().useGPP) {
file.write("\t$(CPP) -mdll $(LINKOBJ) -o \"$(BIN)\" $(LIBS) -Wl,--output-def,$(DEF),--out-implib,$(STATIC)");
} else {
file.write("\t$(CC) -mdll $(LINKOBJ) -o \"$(BIN)\" $(LIBS) -Wl,--output-def,$(DEF),--out-implib,$(STATIC)");
}
}
writeMakeObjFilesRules(file);
}
2021-09-13 10:48:44 +08:00
void ProjectCompiler::newMakeFile(QFile& file)
{
// Create OBJ output directory
if (!mProject->options().objectOutput.isEmpty()) {
QDir(mProject->directory()).mkpath(mProject->options().objectOutput);
}
// 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))
2021-09-13 22:45:50 +08:00
throw CompileError(tr("Can't open '%1' for write!").arg(mProject->makeFileName()));
2021-09-13 10:48:44 +08:00
// 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 ProjectCompiler::writeMakeHeader(QFile &file)
{
writeln(file,"# Project: " + mProject->name());
writeln(file,QString("# Makefile created by Red Panda Dev-C++ ") + DEVCPP_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)
{
// Get list of object files
QString Objects;
QString LinkObjects;
// Create a list of object files
for (int i=0;i<mProject->units().count();i++) {
2021-09-13 19:09:15 +08:00
PProjectUnit unit = mProject->units()[i];
2021-09-13 10:48:44 +08:00
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) {
if (!mProject->options().objectOutput.isEmpty()) {
// ofile = C:\MyProgram\obj\main.o
QString ObjFile = includeTrailingPathDelimiter(mProject->options().objectOutput)
+ extractFileName(unit->fileName());
ObjFile = genMakePath1(extractRelativePath(mProject->directory(), changeFileExt(ObjFile, OBJ_EXT)));
Objects += ' ' + ObjFile;
if (unit->link())
LinkObjects += ' ' + ObjFile;
} else {
Objects += ' ' + genMakePath1(changeFileExt(RelativeName, OBJ_EXT));
if (unit->link())
LinkObjects = LinkObjects + ' ' + genMakePath1(changeFileExt(RelativeName, OBJ_EXT));
}
}
}
Objects = Objects.trimmed();
LinkObjects = LinkObjects.trimmed();
// Get windres file
QString ObjResFile;
if (!mProject->options().privateResource.isEmpty()) {
if (!mProject->options().objectOutput.isEmpty()) {
ObjResFile = includeTrailingPathDelimiter(mProject->options().objectOutput) +
changeFileExt(mProject->options().privateResource, RES_EXT);
} else
ObjResFile = changeFileExt(mProject->options().privateResource, RES_EXT);
}
// Mention progress in the logs
if (!ObjResFile.isEmpty()) {
log(tr("- Resource File: %1").arg(QDir(mProject->directory()).absoluteFilePath(ObjResFile)));
}
log("");
// Get list of applicable flags
QString cCompileArguments = getCCompileArguments(mOnlyCheckSyntax);
QString cppCompileArguments = getCppCompileArguments(mOnlyCheckSyntax);
2021-09-13 19:09:15 +08:00
QString libraryArguments = getLibraryArguments(FileType::Project);
QString cIncludeArguments = getCIncludeArguments() + " " + getProjectIncludeArguments();
QString cppIncludeArguments = getCppIncludeArguments() + " " +getProjectIncludeArguments();
if (cCompileArguments.indexOf(" -g3")>=0
|| cCompileArguments.startsWith("-g3")) {
cCompileArguments += " -D__DEBUG__";
cppCompileArguments+= " -D__DEBUG__";
}
2021-09-13 22:45:50 +08:00
writeln(file,"CPP = " + extractFileName(compilerSet()->cppCompiler()));
writeln(file,"CC = " + extractFileName(compilerSet()->CCompiler()));
writeln(file,"WINDRES = " + extractFileName(compilerSet()->resourceCompiler()));
2021-09-13 19:09:15 +08:00
if (!ObjResFile.isEmpty()) {
writeln(file,"RES = " + genMakePath1(ObjResFile));
writeln(file,"OBJ = " + Objects + " $(RES)");
writeln(file,"LINKOBJ = " + LinkObjects + " $(RES)");
} else {
writeln(file,"OBJ = " + Objects);
writeln(file,"LINKOBJ = " + LinkObjects);
};
libraryArguments.replace('\\', '/');
writeln(file,"LIBS = " + libraryArguments);
cIncludeArguments.replace('\\', '/');
writeln(file,"INCS = " + cIncludeArguments);
cppIncludeArguments.replace('\\', '/');
writeln(file,"CXXINCS = " + cppIncludeArguments);
writeln(file,"BIN = " + genMakePath1(extractRelativePath(mProject->makeFileName(), mProject->executable())));
cppCompileArguments.replace('\\', '/');
writeln(file,"CXXFLAGS = $(CXXINCS) " + cppCompileArguments);
//writeln(file,"ENCODINGS = -finput-charset=utf-8 -fexec-charset='+GetSystemCharsetName);
cCompileArguments.replace('\\', '/');
writeln(file,"CFLAGS = $(INCS) " + cCompileArguments);
writeln(file, QString("RM = ") + CLEAN_PROGRAM );
if (mProject->options().usePrecompiledHeader){
writeln(file,"PCH_H = " + mProject->options().precompiledHeader );
writeln(file,"PCH = " + changeFileExt(mProject->options().precompiledHeader, GCH_EXT));
}
2021-09-13 10:48:44 +08:00
// This needs to be put in before the clean command.
2021-09-13 19:09:15 +08:00
if (mProject->options().type == ProjectType::DynamicLib) {
QString OutputFileDir = extractFilePath(mProject->executable());
QString libOutputFile = includeTrailingPathDelimiter(OutputFileDir) + "lib" + extractFileName(mProject->executable());
if (QFileInfo(libOutputFile).absoluteFilePath()
== mProject->directory())
libOutputFile = extractFileName(libOutputFile);
else
libOutputFile = extractRelativePath(mProject->makeFileName(), libOutputFile);
writeln(file,"DEF = " + genMakePath1(changeFileExt(libOutputFile, DEF_EXT)));
writeln(file,"STATIC = " + genMakePath1(changeFileExt(libOutputFile, LIB_EXT)));
}
writeln(file);
2021-09-13 10:48:44 +08:00
}
2021-09-13 22:45:50 +08:00
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);
writeln(file, "all: all-before $(BIN) all-after");
writeln(file);
if (mProject->options().usePrecompiledHeader) {
writeln(file, "$(PCH) : $(PCH_H)");
writeln(file, " $(CPP) -x c++-header \"$(PCH_H)\" -o \"$(PCH)\" $(CXXFLAGS)");
writeln(file);
}
}
void ProjectCompiler::writeMakeIncludes(QFile &file)
{
foreach(const QString& s, mProject->options().makeIncludes) {
writeln(file, "include " + genMakePath1(s));
}
if (!mProject->options().makeIncludes.isEmpty()) {
writeln(file);
}
}
void ProjectCompiler::writeMakeClean(QFile &file)
{
writeln(file, "clean: clean-custom");
if (mProject->options().type == ProjectType::DynamicLib)
writeln(file, "\t${RM} $(OBJ) $(BIN) $(DEF) $(STATIC)");
else
writeln(file, "\t${RM} $(OBJ) $(BIN)");
writeln(file);
}
void ProjectCompiler::writeMakeObjFilesRules(QFile &file)
{
PCppParser parser = mProject->cppParser();
QString precompileStr;
if (mProject->options().usePrecompiledHeader)
precompileStr = " $(PCH) ";
for (int i = 0;i<mProject->units().count();i++) {
PProjectUnit unit = mProject->units()[i];
FileType fileType = getFileType(unit->fileName());
// Only process source files
if (fileType!=FileType::CSource && fileType!=FileType::CppSource)
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<QString> fileIncludes = parser->getFileIncludes(unit->fileName());
foreach (const QString& headerName, fileIncludes) {
if (headerName == unit->fileName())
continue;
if (!parser->isSystemHeaderFile(headerName)
&& ! parser->isProjectHeaderFile(headerName)) {
objStr = objStr + ' ' + genMakePath2(extractRelativePath(mProject->makeFileName(),headerName));
}
}
} else {
foreach (const PProjectUnit& u, mProject->units()) {
FileType fileType = getFileType(u->fileName());
if (fileType == FileType::CHeader || fileType==FileType::CppHeader)
objStr = objStr + ' ' + genMakePath2(extractRelativePath(mProject->makeFileName(),u->fileName()));
}
}
QString ObjFileName;
if (!mProject->options().objectOutput.isEmpty()) {
ObjFileName = includeTrailingPathDelimiter(mProject->options().objectOutput) +
extractFileName(unit->fileName());
ObjFileName = genMakePath1(extractRelativePath(mProject->makeFileName(), changeFileExt(ObjFileName, OBJ_EXT)));
if (!extractFileDir(ObjFileName).isEmpty()) {
objStr = genMakePath2(includeTrailingPathDelimiter(extractFileDir(ObjFileName))) + objStr;
}
} else {
ObjFileName = genMakePath1(changeFileExt(shortFileName, OBJ_EXT));
}
objStr = ObjFileName + ": "+objStr+precompileStr;
writeln(file,objStr);
// Write custom build command
if (unit->overrideBuildCmd() && !unit->buildCmd().isEmpty()) {
QString BuildCmd = unit->buildCmd();
BuildCmd.replace("<CRTAB>", "\n\t");
writeln(file, '\t' + BuildCmd);
// Or roll our own
} else {
QString encodingStr;
if (mProject->options().addCharset) {
if (unit->encoding() == ENCODING_AUTO_DETECT) {
if (unit->editor())
encodingStr = QString(" -finput-charset=%1 -fexec-charset=%2")
.arg(unit->editor()->fileEncoding(),getDefaultSystemEncoding());
} else {
encodingStr = QString(" -finput-charset=%1 -fexec-charset=%2")
.arg(unit->encoding(),getDefaultSystemEncoding());
}
}
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 " + ObjFileName + " $(CXXFLAGS) " + encodingStr);
else
writeln(file, "\t$(CC) -c " + genMakePath1(shortFileName) + " -o " + ObjFileName + " $(CFLAGS) " + encodingStr);
}
}
}
if (!mProject->options().privateResource.isEmpty()) {
// Concatenate all resource include directories
QString ResIncludes(" ");
for (int i=0;i<mProject->options().resourceIncludes.count();i++) {
QString filename = mProject->options().resourceIncludes[i];
if (!filename.isEmpty())
ResIncludes = ResIncludes + " --include-dir " + genMakePath1(filename);
}
QString ResFiles;
// Concatenate all resource filenames (not created when syntax checking)
if (!mOnlyCheckSyntax) {
foreach(const PProjectUnit& unit, mProject->units()) {
if (getFileType(unit->fileName())!=FileType::WindowsResourceSource)
continue;
if (QFile(unit->fileName()).exists()) {
QString ResFile = extractRelativePath(mProject->makeFileName(), unit->fileName());
ResFiles = ResFiles + genMakePath2(ResFile) + ' ';
}
}
ResFiles = ResFiles.trimmed();
}
// Determine resource output file
QString ObjFileName;
if (!mProject->options().objectOutput.isEmpty()) {
ObjFileName = includeTrailingPathDelimiter(mProject->options().objectOutput) +
changeFileExt(mProject->options().privateResource, RES_EXT);
} else {
ObjFileName = changeFileExt(mProject->options().privateResource, RES_EXT);
}
ObjFileName = genMakePath1(extractRelativePath(mProject->filename(), ObjFileName));
QString PrivResName = genMakePath1(extractRelativePath(mProject->filename(), mProject->options().privateResource));
// Build final cmd
QString WindresArgs;
if (getCCompileArguments(mOnlyCheckSyntax).contains("-m32"))
WindresArgs = " -F pe-i386";
if (mOnlyCheckSyntax) {
writeln(file);
writeln(file, ObjFileName + ':');
writeln(file, "\t$(WINDRES) -i " + PrivResName + WindresArgs + " --input-format=rc -o nul -O coff" + ResIncludes);
} else {
writeln(file);
writeln(file, ObjFileName + ": " + PrivResName + ' ' + ResFiles);
writeln(file, "\t$(WINDRES) -i " + PrivResName + WindresArgs + " --input-format=rc -o " + ObjFileName + " -O coff"
+ ResIncludes);
}
writeln(file);
}
}
2021-09-13 10:48:44 +08:00
void ProjectCompiler::writeln(QFile &file, const QString &s)
{
if (!s.isEmpty())
file.write(s.toLocal8Bit());
file.write("\n");
}
2021-09-13 07:49:36 +08:00
QString ProjectCompiler::pipedText()
{
return QString();
}
2021-09-13 10:48:44 +08:00
bool ProjectCompiler::prepareForRebuild()
{
//we use make argument to clean
return true;
}
2021-09-13 07:49:36 +08:00
bool ProjectCompiler::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();
2021-09-13 10:48:44 +08:00
mCompiler = compilerSet()->make();
if (mRebuild) {
2021-09-13 22:45:50 +08:00
mArguments = QString("-f \"%1\" clean all").arg(extractRelativePath(
mProject->directory(),
mProject->makeFileName()));
2021-09-13 10:48:44 +08:00
} else {
2021-09-13 22:45:50 +08:00
mArguments = QString("-f \"%1\" all").arg(extractRelativePath(
mProject->directory(),
mProject->makeFileName()));
2021-09-13 10:48:44 +08:00
}
2021-09-13 22:45:50 +08:00
mDirectory = mProject->directory();
log(tr("Processing makefile:"));
log("--------");
log(tr("- makefile processer: %1").arg(mCompiler));
log(tr("- Command: %1 %2").arg(mCompiler).arg(mArguments));
log("");
return true;
2021-09-13 07:49:36 +08:00
}