2023-08-17 19:24:49 +08:00
|
|
|
/*
|
|
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include "sdccprojectcompiler.h"
|
|
|
|
#include "../project.h"
|
|
|
|
#include "compilermanager.h"
|
|
|
|
#include "../systemconsts.h"
|
2024-02-28 19:41:05 +08:00
|
|
|
#include "qt_utils/utils.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "utils/escape.h"
|
2023-08-17 19:24:49 +08:00
|
|
|
|
|
|
|
#include <QDir>
|
|
|
|
|
|
|
|
SDCCProjectCompiler::SDCCProjectCompiler(std::shared_ptr<Project> 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) {
|
2024-02-28 19:41:05 +08:00
|
|
|
writeln(file,"$(BIN_TAR): $(OBJ)");
|
|
|
|
writeln(file,"\t$(CC) $(LIBS) -o $(BIN_ARG) $(LINKOBJ)");
|
2023-08-17 19:24:49 +08:00
|
|
|
} else {
|
2024-02-28 19:41:05 +08:00
|
|
|
writeln(file,"$(IHX_TAR): $(OBJ)\n");
|
|
|
|
writeln(file,"\t$(CC) $(LIBS) -o $(IHX_ARG) $(LINKOBJ)");
|
2023-08-17 19:24:49 +08:00
|
|
|
if (suffix == SDCC_HEX_SUFFIX) {
|
2024-02-28 19:41:05 +08:00
|
|
|
writeln(file,"$(BIN_TAR): $(IHX_DEP)");
|
|
|
|
writeln(file,"\t$(PACKIHX) $(IHX_ARG) > $(BIN_ARG)");
|
2023-08-17 19:24:49 +08:00
|
|
|
} else {
|
2024-02-28 19:41:05 +08:00
|
|
|
writeln(file,"$(BIN_TAR): $(IHX_DEP)");
|
|
|
|
writeln(file,"\t$(MAKEBIN) $(IHX_ARG) $(BIN_ARG)");
|
2023-08-17 19:24:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
2024-02-28 19:41:05 +08:00
|
|
|
QStringList Objects;
|
|
|
|
QStringList LinkObjects;
|
|
|
|
QStringList cleanObjects;
|
2023-08-17 19:24:49 +08:00
|
|
|
|
|
|
|
// 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));
|
2024-02-28 19:41:05 +08:00
|
|
|
Objects << relativeObjFile;
|
|
|
|
cleanObjects << localizePath(relativeObjFile);
|
2023-08-17 19:24:49 +08:00
|
|
|
if (unit->link()) {
|
2024-02-28 19:41:05 +08:00
|
|
|
LinkObjects << relativeObjFile;
|
2023-08-17 19:24:49 +08:00
|
|
|
}
|
|
|
|
} else {
|
2024-02-28 19:41:05 +08:00
|
|
|
Objects << changeFileExt(RelativeName, SDCC_REL_SUFFIX);
|
|
|
|
cleanObjects << localizePath(changeFileExt(RelativeName, SDCC_REL_SUFFIX));
|
2023-08-17 19:24:49 +08:00
|
|
|
if (unit->link())
|
2024-02-28 19:41:05 +08:00
|
|
|
LinkObjects << changeFileExt(RelativeName, SDCC_REL_SUFFIX);
|
2023-08-17 19:24:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-28 19:41:05 +08:00
|
|
|
QString cc = extractFileName(compilerSet()->CCompiler());
|
|
|
|
|
|
|
|
QStringList cCompileArguments = getCCompileArguments(mOnlyCheckSyntax);
|
|
|
|
QStringList libraryArguments = getLibraryArguments(FileType::Project);
|
|
|
|
QStringList cIncludeArguments = getCIncludeArguments() + getProjectIncludeArguments();
|
|
|
|
|
|
|
|
QString executable = extractRelativePath(mProject->makeFileName(), mProject->executable());
|
|
|
|
QString cleanExe = localizePath(executable);
|
|
|
|
QString ihx = extractRelativePath(mProject->makeFileName(), changeFileExt(mProject->executable(), SDCC_IHX_SUFFIX));
|
|
|
|
QString cleanIhx = localizePath(ihx);
|
|
|
|
|
|
|
|
writeln(file, "CC = " + escapeArgumentForMakefileVariableValue(cc, true));
|
|
|
|
writeln(file, "PACKIHX = " PACKIHX_PROGRAM);
|
|
|
|
writeln(file, "MAKEBIN = " MAKEBIN_PROGRAM);
|
|
|
|
|
|
|
|
writeln(file, "OBJ = " + escapeFilenamesForMakefilePrerequisite(Objects));
|
|
|
|
writeln(file, "LINKOBJ = " + escapeArgumentsForMakefileVariableValue(LinkObjects));
|
|
|
|
writeln(file,"CLEANOBJ = " + escapeArgumentsForMakefileVariableValue(cleanObjects) + ' ' +
|
|
|
|
escapeArgumentForMakefileVariableValue(cleanIhx, false) + ' ' +
|
|
|
|
escapeArgumentForMakefileVariableValue(cleanExe, false));
|
|
|
|
writeln(file, "LIBS = " + escapeArgumentsForMakefileVariableValue(libraryArguments));
|
|
|
|
writeln(file, "INCS = " + escapeArgumentsForMakefileVariableValue(cIncludeArguments));
|
|
|
|
writeln(file, "IHX_TAR = " + escapeFilenameForMakefileTarget(ihx));
|
|
|
|
writeln(file, "IHX_DEP = " + escapeFilenameForMakefilePrerequisite(ihx));
|
|
|
|
writeln(file, "IHX_ARG = " + escapeArgumentForMakefileVariableValue(ihx, false));
|
|
|
|
writeln(file, "BIN_TAR = " + escapeFilenameForMakefileTarget(executable));
|
|
|
|
writeln(file, "BIN_DEP = " + escapeFilenameForMakefilePrerequisite(executable));
|
|
|
|
writeln(file, "BIN_ARG = " + escapeArgumentForMakefileVariableValue(executable, false));
|
|
|
|
writeln(file, "CFLAGS = $(INCS) " + escapeArgumentsForMakefileVariableValue(cCompileArguments));
|
|
|
|
writeln(file, "RM = " CLEAN_PROGRAM);
|
2023-08-17 19:24:49 +08:00
|
|
|
|
|
|
|
writeln(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SDCCProjectCompiler::writeMakeTarget(QFile &file)
|
|
|
|
{
|
|
|
|
writeln(file, ".PHONY: all all-before all-after clean clean-custom");
|
|
|
|
writeln(file);
|
2024-02-28 19:41:05 +08:00
|
|
|
writeln(file, "all: all-before $(BIN_DEP) all-after");
|
2023-08-17 19:24:49 +08:00
|
|
|
writeln(file);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void SDCCProjectCompiler::writeMakeIncludes(QFile &file)
|
|
|
|
{
|
|
|
|
foreach(const QString& s, mProject->options().makeIncludes) {
|
2024-02-28 19:41:05 +08:00
|
|
|
writeln(file, "include " + escapeFilenameForMakefileInclude(s));
|
2023-08-17 19:24:49 +08:00
|
|
|
}
|
|
|
|
if (!mProject->options().makeIncludes.isEmpty()) {
|
|
|
|
writeln(file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SDCCProjectCompiler::writeMakeClean(QFile &file)
|
|
|
|
{
|
|
|
|
writeln(file, "clean: clean-custom");
|
2024-02-28 19:41:05 +08:00
|
|
|
writeln(file, QString("\t-$(RM) $(CLEANOBJ) > %1 2>&1").arg(NULL_FILE));
|
2023-08-17 19:24:49 +08:00
|
|
|
writeln(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SDCCProjectCompiler::writeMakeObjFilesRules(QFile &file)
|
|
|
|
{
|
|
|
|
PCppParser parser = mProject->cppParser();
|
|
|
|
|
|
|
|
QList<PProjectUnit> 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);
|
2024-02-28 19:41:05 +08:00
|
|
|
QString objStr = escapeFilenameForMakefilePrerequisite(shortFileName);
|
2023-08-17 19:24:49 +08:00
|
|
|
// if we have scanned it, use scanned info
|
2023-11-05 21:00:52 +08:00
|
|
|
if (parser && parser->fileScanned(unit->fileName())) {
|
|
|
|
QSet<QString> fileIncludes = parser->getIncludedFiles(unit->fileName());
|
2023-08-17 19:24:49 +08:00
|
|
|
foreach(const PProjectUnit &unit2, projectUnits) {
|
|
|
|
if (unit2==unit)
|
|
|
|
continue;
|
|
|
|
if (fileIncludes.contains(unit2->fileName())) {
|
2024-02-28 19:41:05 +08:00
|
|
|
QString header = extractRelativePath(mProject->makeFileName(),unit2->fileName());
|
|
|
|
objStr = objStr + ' ' + escapeFilenameForMakefilePrerequisite(header);
|
2023-08-17 19:24:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
foreach(const PProjectUnit &unit2, projectUnits) {
|
|
|
|
FileType fileType = getFileType(unit2->fileName());
|
2024-02-28 19:41:05 +08:00
|
|
|
if (fileType == FileType::CHeader || fileType==FileType::CppHeader) {
|
|
|
|
QString header = extractRelativePath(mProject->makeFileName(),unit2->fileName());
|
|
|
|
objStr = objStr + ' ' + escapeFilenameForMakefilePrerequisite(header);
|
|
|
|
}
|
2023-08-17 19:24:49 +08:00
|
|
|
}
|
|
|
|
}
|
2024-02-28 19:41:05 +08:00
|
|
|
QString objFileNameTarget;
|
|
|
|
QString objFileNameCommand;
|
2023-08-17 19:24:49 +08:00
|
|
|
if (!mProject->options().objectOutput.isEmpty()) {
|
|
|
|
QString fullObjname = includeTrailingPathDelimiter(mProject->options().objectOutput) +
|
|
|
|
extractFileName(unit->fileName());
|
2024-02-28 19:41:05 +08:00
|
|
|
QString objFile = extractRelativePath(mProject->makeFileName(), changeFileExt(fullObjname, SDCC_REL_SUFFIX));
|
|
|
|
objFileNameTarget = escapeFilenameForMakefileTarget(objFile);
|
|
|
|
objFileNameCommand = escapeArgumentForMakefileRecipe(objFile, false);
|
2023-08-17 19:24:49 +08:00
|
|
|
} else {
|
2024-02-28 19:41:05 +08:00
|
|
|
QString objFile = changeFileExt(shortFileName, SDCC_REL_SUFFIX);
|
|
|
|
objFileNameTarget = escapeFilenameForMakefileTarget(objFile);
|
|
|
|
objFileNameCommand = escapeArgumentForMakefileRecipe(objFile, false);
|
2023-08-17 19:24:49 +08:00
|
|
|
}
|
|
|
|
|
2024-02-28 19:41:05 +08:00
|
|
|
objStr = objFileNameTarget + ": " + objStr;
|
2023-08-17 19:24:49 +08:00
|
|
|
|
2024-02-28 19:41:05 +08:00
|
|
|
writeln(file, objStr);
|
2023-08-17 19:24:49 +08:00
|
|
|
|
|
|
|
// 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 {
|
|
|
|
if (fileType==FileType::CSource) {
|
2024-02-28 19:41:05 +08:00
|
|
|
writeln(file, "\t$(CC) $(CFLAGS) -c " + escapeArgumentForMakefileRecipe(shortFileName, false));
|
2023-08-17 19:24:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
+"<br />"
|
|
|
|
+tr("Please check the \"program\" page of compiler settings."));
|
|
|
|
}
|
|
|
|
|
|
|
|
QString parallelParam;
|
|
|
|
if (mProject->options().allowParallelBuilding) {
|
|
|
|
if (mProject->options().parellelBuildingJobs==0) {
|
2024-02-28 19:41:05 +08:00
|
|
|
parallelParam = "--jobs";
|
2023-08-17 19:24:49 +08:00
|
|
|
} else {
|
2024-02-28 19:41:05 +08:00
|
|
|
parallelParam = QString("-j%1").arg(mProject->options().parellelBuildingJobs);
|
2023-08-17 19:24:49 +08:00
|
|
|
}
|
2024-02-28 19:41:05 +08:00
|
|
|
} else {
|
|
|
|
parallelParam = "-j1";
|
2023-08-17 19:24:49 +08:00
|
|
|
}
|
|
|
|
|
2024-02-28 19:41:05 +08:00
|
|
|
QString makefile =
|
|
|
|
extractRelativePath(mProject->directory(), mProject->makeFileName());
|
|
|
|
QStringList cleanArgs{
|
|
|
|
"-f",
|
|
|
|
makefile,
|
|
|
|
"clean",
|
|
|
|
};
|
|
|
|
QStringList makeAllArgs{
|
|
|
|
parallelParam,
|
|
|
|
"-f",
|
|
|
|
makefile,
|
|
|
|
"all",
|
|
|
|
};
|
2023-08-17 19:24:49 +08:00
|
|
|
if (onlyClean()) {
|
2024-02-28 19:41:05 +08:00
|
|
|
mArguments = cleanArgs;
|
2023-08-17 19:24:49 +08:00
|
|
|
} else if (mRebuild) {
|
2024-02-28 19:41:05 +08:00
|
|
|
mArguments = cleanArgs;
|
|
|
|
mExtraCompilersList << mCompiler;
|
|
|
|
mExtraOutputFilesList << "";
|
|
|
|
mExtraArgumentsList << makeAllArgs;
|
2023-08-17 19:24:49 +08:00
|
|
|
} else {
|
2024-02-28 19:41:05 +08:00
|
|
|
mArguments = makeAllArgs;
|
2023-08-17 19:24:49 +08:00
|
|
|
}
|
|
|
|
mDirectory = mProject->directory();
|
|
|
|
|
|
|
|
log(tr("Processing makefile:"));
|
|
|
|
log("--------");
|
|
|
|
log(tr("- makefile processer: %1").arg(mCompiler));
|
2024-02-28 19:41:05 +08:00
|
|
|
QString command = escapeCommandForLog(mCompiler, mArguments);
|
|
|
|
log(tr("- Command: %1").arg(command));
|
2023-08-17 19:24:49 +08:00
|
|
|
log("");
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|