Merge branch 'master' of github.com:royqh1979/RedPanda-CPP
This commit is contained in:
commit
5eff32cee9
|
@ -0,0 +1,30 @@
|
||||||
|
name: Unit
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu_makefile_escape:
|
||||||
|
name: Unix makefile escape
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup xmake
|
||||||
|
uses: xmake-io/github-action-setup-xmake@v1
|
||||||
|
with:
|
||||||
|
xmake-version: '2.8.6'
|
||||||
|
|
||||||
|
- name: Setup Qt
|
||||||
|
uses: ConorMacBride/install-package@v1
|
||||||
|
with:
|
||||||
|
apt: libqt5svg5-dev qtbase5-dev qtbase5-dev-tools qttools5-dev-tools
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
xmake f --qt=/usr
|
||||||
|
xmake b test-escape
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
export QT_ASSUME_STDERR_HAS_CONSOLE=1
|
||||||
|
xmake r test-escape
|
3
NEWS.md
3
NEWS.md
|
@ -13,6 +13,9 @@ Red Panda C++ Version 2.27
|
||||||
- enhancement: Display ascii control chars.
|
- enhancement: Display ascii control chars.
|
||||||
- fix: Parser: invalidating file may lost class inheritance infos.
|
- fix: Parser: invalidating file may lost class inheritance infos.
|
||||||
- fix: Function argument infos are not correctly parsed.
|
- fix: Function argument infos are not correctly parsed.
|
||||||
|
- enhancement: Migrate external calls from command string to argv array to improve safety and security.
|
||||||
|
- enhancement: Support POSIX shell-like escaping in user inputs for compiler arguments.
|
||||||
|
- fix: (Hopefully) properly escape filenames and arguments in makefile generation.
|
||||||
|
|
||||||
Red Panda C++ Version 2.26
|
Red Panda C++ Version 2.26
|
||||||
- enhancement: Code suggestion for embedded std::vectors.
|
- enhancement: Code suggestion for embedded std::vectors.
|
||||||
|
|
|
@ -197,6 +197,8 @@ SOURCES += \
|
||||||
settingsdialog/settingswidget.cpp \
|
settingsdialog/settingswidget.cpp \
|
||||||
systemconsts.cpp \
|
systemconsts.cpp \
|
||||||
utils.cpp \
|
utils.cpp \
|
||||||
|
utils/escape.cpp \
|
||||||
|
utils/parsearg.cpp \
|
||||||
widgets/coloredit.cpp \
|
widgets/coloredit.cpp \
|
||||||
widgets/compileargumentswidget.cpp \
|
widgets/compileargumentswidget.cpp \
|
||||||
widgets/consolewidget.cpp \
|
widgets/consolewidget.cpp \
|
||||||
|
@ -324,6 +326,8 @@ HEADERS += \
|
||||||
settingsdialog/settingswidget.h \
|
settingsdialog/settingswidget.h \
|
||||||
systemconsts.h \
|
systemconsts.h \
|
||||||
utils.h \
|
utils.h \
|
||||||
|
utils/escape.h \
|
||||||
|
utils/parsearg.h \
|
||||||
common.h \
|
common.h \
|
||||||
widgets/coloredit.h \
|
widgets/coloredit.h \
|
||||||
widgets/compileargumentswidget.h \
|
widgets/compileargumentswidget.h \
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
*/
|
*/
|
||||||
#include "compiler.h"
|
#include "compiler.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "utils/escape.h"
|
||||||
|
#include "utils/parsearg.h"
|
||||||
#include "compilermanager.h"
|
#include "compilermanager.h"
|
||||||
#include "../systemconsts.h"
|
#include "../systemconsts.h"
|
||||||
|
|
||||||
|
@ -68,10 +70,11 @@ void Compiler::run()
|
||||||
for(int i=0;i<mExtraArgumentsList.count();i++) {
|
for(int i=0;i<mExtraArgumentsList.count();i++) {
|
||||||
if (!beforeRunExtraCommand(i))
|
if (!beforeRunExtraCommand(i))
|
||||||
break;
|
break;
|
||||||
|
QString command = escapeCommandForLog(mExtraCompilersList[i], mExtraArgumentsList[i]);
|
||||||
if (mExtraOutputFilesList[i].isEmpty()) {
|
if (mExtraOutputFilesList[i].isEmpty()) {
|
||||||
log(tr(" - Command: %1 %2").arg(extractFileName(mExtraCompilersList[i]),mExtraArgumentsList[i]));
|
log(tr(" - Command: %1").arg(command));
|
||||||
} else {
|
} else {
|
||||||
log(tr(" - Command: %1 %2 > \"%3\"").arg(extractFileName(mExtraCompilersList[i]), mExtraArgumentsList[i], mExtraOutputFilesList[i]));
|
log(tr(" - Command: %1 > %2").arg(command, escapeArgumentForPlatformShell(mExtraOutputFilesList[i], false)));
|
||||||
}
|
}
|
||||||
runCommand(mExtraCompilersList[i],mExtraArgumentsList[i],mDirectory, pipedText(),mExtraOutputFilesList[i]);
|
runCommand(mExtraCompilersList[i],mExtraArgumentsList[i],mDirectory, pipedText(),mExtraOutputFilesList[i]);
|
||||||
}
|
}
|
||||||
|
@ -331,9 +334,9 @@ void Compiler::stopCompile()
|
||||||
mStop = true;
|
mStop = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Compiler::getCharsetArgument(const QByteArray& encoding,FileType fileType, bool checkSyntax)
|
QStringList Compiler::getCharsetArgument(const QByteArray& encoding,FileType fileType, bool checkSyntax)
|
||||||
{
|
{
|
||||||
QString result;
|
QStringList result;
|
||||||
bool forceExecUTF8=false;
|
bool forceExecUTF8=false;
|
||||||
// test if force utf8 from autolink infos
|
// test if force utf8 from autolink infos
|
||||||
if ((fileType == FileType::CSource ||
|
if ((fileType == FileType::CSource ||
|
||||||
|
@ -383,21 +386,22 @@ QString Compiler::getCharsetArgument(const QByteArray& encoding,FileType fileTyp
|
||||||
}
|
}
|
||||||
//qDebug()<<encodingName<<execEncodingName;
|
//qDebug()<<encodingName<<execEncodingName;
|
||||||
if (checkSyntax) {
|
if (checkSyntax) {
|
||||||
result += QString(" -finput-charset=%1")
|
result << "-finput-charset=" + encodingName;
|
||||||
.arg(encodingName);
|
|
||||||
} else if (encodingName!=execEncodingName) {
|
} else if (encodingName!=execEncodingName) {
|
||||||
result += QString(" -finput-charset=%1 -fexec-charset=%2")
|
result += {
|
||||||
.arg(encodingName, execEncodingName);
|
"-finput-charset=" + encodingName,
|
||||||
|
"-fexec-charset=" + execEncodingName,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Compiler::getCCompileArguments(bool checkSyntax)
|
QStringList Compiler::getCCompileArguments(bool checkSyntax)
|
||||||
{
|
{
|
||||||
QString result;
|
QStringList result;
|
||||||
if (checkSyntax) {
|
if (checkSyntax) {
|
||||||
result += " -fsyntax-only";
|
result << "-fsyntax-only";
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<QString, QString> compileOptions;
|
QMap<QString, QString> compileOptions;
|
||||||
|
@ -412,38 +416,36 @@ QString Compiler::getCCompileArguments(bool checkSyntax)
|
||||||
PCompilerOption pOption = CompilerInfoManager::getCompilerOption(compilerSet()->compilerType(), key);
|
PCompilerOption pOption = CompilerInfoManager::getCompilerOption(compilerSet()->compilerType(), key);
|
||||||
if (pOption && pOption->isC && !pOption->isLinker) {
|
if (pOption && pOption->isC && !pOption->isLinker) {
|
||||||
if (pOption->type == CompilerOptionType::Checkbox)
|
if (pOption->type == CompilerOptionType::Checkbox)
|
||||||
result += " " + pOption->setting;
|
result << pOption->setting;
|
||||||
else if (pOption->type == CompilerOptionType::Input)
|
else if (pOption->type == CompilerOptionType::Input)
|
||||||
result += " " + pOption->setting + " " + compileOptions[key];
|
result += {pOption->setting, compileOptions[key]};
|
||||||
else {
|
else {
|
||||||
result += " " + pOption->setting + compileOptions[key];
|
result << pOption->setting + compileOptions[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QMap<QString, QString> macros = devCppMacroVariables();
|
||||||
|
|
||||||
if (compilerSet()->useCustomCompileParams() && !compilerSet()->customCompileParams().isEmpty()) {
|
if (compilerSet()->useCustomCompileParams() && !compilerSet()->customCompileParams().isEmpty()) {
|
||||||
QStringList params = textToLines(compilerSet()->customCompileParams());
|
result << parseArguments(compilerSet()->customCompileParams(), macros, true);
|
||||||
foreach(const QString& param, params)
|
|
||||||
result += " "+ parseMacros(param);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mProject) {
|
if (mProject) {
|
||||||
QString s = mProject->options().compilerCmd;
|
QString s = mProject->options().compilerCmd;
|
||||||
if (!s.isEmpty()) {
|
if (!s.isEmpty()) {
|
||||||
s.replace("_@@_", " ");
|
s.replace("_@@_", " ");
|
||||||
QStringList params = textToLines(s);
|
result << parseArguments(s, macros, true);
|
||||||
foreach(const QString& param, params)
|
|
||||||
result += " "+ parseMacros(param);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Compiler::getCppCompileArguments(bool checkSyntax)
|
QStringList Compiler::getCppCompileArguments(bool checkSyntax)
|
||||||
{
|
{
|
||||||
QString result;
|
QStringList result;
|
||||||
if (checkSyntax) {
|
if (checkSyntax) {
|
||||||
result += " -fsyntax-only";
|
result << "-fsyntax-only";
|
||||||
}
|
}
|
||||||
QMap<QString, QString> compileOptions;
|
QMap<QString, QString> compileOptions;
|
||||||
if (mProject && !mProject->options().compilerOptions.isEmpty()) {
|
if (mProject && !mProject->options().compilerOptions.isEmpty()) {
|
||||||
|
@ -457,75 +459,73 @@ QString Compiler::getCppCompileArguments(bool checkSyntax)
|
||||||
PCompilerOption pOption = CompilerInfoManager::getCompilerOption(compilerSet()->compilerType(), key);
|
PCompilerOption pOption = CompilerInfoManager::getCompilerOption(compilerSet()->compilerType(), key);
|
||||||
if (pOption && pOption->isCpp && !pOption->isLinker) {
|
if (pOption && pOption->isCpp && !pOption->isLinker) {
|
||||||
if (pOption->type == CompilerOptionType::Checkbox)
|
if (pOption->type == CompilerOptionType::Checkbox)
|
||||||
result += " " + pOption->setting;
|
result << pOption->setting;
|
||||||
else if (pOption->type == CompilerOptionType::Input)
|
else if (pOption->type == CompilerOptionType::Input)
|
||||||
result += " " + pOption->setting + " " + compileOptions[key];
|
result += {pOption->setting, compileOptions[key]};
|
||||||
else {
|
else {
|
||||||
result += " " + pOption->setting + compileOptions[key];
|
result << pOption->setting + compileOptions[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QMap<QString, QString> macros = devCppMacroVariables();
|
||||||
if (compilerSet()->useCustomCompileParams() && !compilerSet()->customCompileParams().isEmpty()) {
|
if (compilerSet()->useCustomCompileParams() && !compilerSet()->customCompileParams().isEmpty()) {
|
||||||
QStringList params = textToLines(compilerSet()->customCompileParams());
|
result << parseArguments(compilerSet()->customCompileParams(), macros, true);
|
||||||
foreach(const QString& param, params)
|
|
||||||
result += " "+ parseMacros(param);
|
|
||||||
}
|
}
|
||||||
if (mProject) {
|
if (mProject) {
|
||||||
QString s = mProject->options().cppCompilerCmd;
|
QString s = mProject->options().cppCompilerCmd;
|
||||||
if (!s.isEmpty()) {
|
if (!s.isEmpty()) {
|
||||||
s.replace("_@@_", " ");
|
s.replace("_@@_", " ");
|
||||||
QStringList params = textToLines(s);
|
result << parseArguments(s, macros, true);
|
||||||
foreach(const QString& param, params)
|
|
||||||
result += " "+ parseMacros(param);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QString Compiler::getCIncludeArguments()
|
QStringList Compiler::getCIncludeArguments()
|
||||||
{
|
{
|
||||||
QString result;
|
QStringList result;
|
||||||
foreach (const QString& folder,compilerSet()->CIncludeDirs()) {
|
foreach (const QString& folder,compilerSet()->CIncludeDirs()) {
|
||||||
result += QString(" -I\"%1\"").arg(folder);
|
result << "-I" + folder;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Compiler::getProjectIncludeArguments()
|
QStringList Compiler::getProjectIncludeArguments()
|
||||||
{
|
{
|
||||||
QString result;
|
QStringList result;
|
||||||
if (mProject) {
|
if (mProject) {
|
||||||
foreach (const QString& folder,mProject->options().includeDirs) {
|
foreach (const QString& folder,mProject->options().includeDirs) {
|
||||||
result += QString(" -I\"%1\"").arg(folder);
|
result << "-I" + folder;
|
||||||
}
|
}
|
||||||
// result += QString(" -I\"%1\"").arg(extractFilePath(mProject->filename()));
|
// result += QString(" -I\"%1\"").arg(extractFilePath(mProject->filename()));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Compiler::getCppIncludeArguments()
|
QStringList Compiler::getCppIncludeArguments()
|
||||||
{
|
{
|
||||||
QString result;
|
QStringList result;
|
||||||
foreach (const QString& folder,compilerSet()->CppIncludeDirs()) {
|
foreach (const QString& folder,compilerSet()->CppIncludeDirs()) {
|
||||||
result += QString(" -I\"%1\"").arg(folder);
|
result << "-I" + folder;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Compiler::getLibraryArguments(FileType fileType)
|
QStringList Compiler::getLibraryArguments(FileType fileType)
|
||||||
{
|
{
|
||||||
QString result;
|
QStringList result;
|
||||||
|
|
||||||
//Add libraries
|
//Add libraries
|
||||||
foreach (const QString& folder, compilerSet()->libDirs()) {
|
foreach (const QString& folder, compilerSet()->libDirs()) {
|
||||||
result += QString(" -L\"%1\"").arg(folder);
|
result << "-L" + folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
//add libs added via project
|
//add libs added via project
|
||||||
if (mProject) {
|
if (mProject) {
|
||||||
foreach (const QString& folder, mProject->options().libDirs){
|
foreach (const QString& folder, mProject->options().libDirs){
|
||||||
result += QString(" -L\"%1\"").arg(folder);
|
result << "-L" + folder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,9 +547,7 @@ QString Compiler::getLibraryArguments(FileType fileType)
|
||||||
}
|
}
|
||||||
if (waitCount<=10) {
|
if (waitCount<=10) {
|
||||||
QSet<QString> parsedFiles;
|
QSet<QString> parsedFiles;
|
||||||
result += parseFileIncludesForAutolink(
|
result += parseFileIncludesForAutolink(mFilename, parsedFiles);
|
||||||
mFilename,
|
|
||||||
parsedFiles);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -567,11 +565,11 @@ QString Compiler::getLibraryArguments(FileType fileType)
|
||||||
PCompilerOption pOption = CompilerInfoManager::getCompilerOption(compilerSet()->compilerType(), key);
|
PCompilerOption pOption = CompilerInfoManager::getCompilerOption(compilerSet()->compilerType(), key);
|
||||||
if (pOption && pOption->isLinker) {
|
if (pOption && pOption->isLinker) {
|
||||||
if (pOption->type == CompilerOptionType::Checkbox)
|
if (pOption->type == CompilerOptionType::Checkbox)
|
||||||
result += " " + pOption->setting;
|
result << pOption->setting;
|
||||||
else if (pOption->type == CompilerOptionType::Input)
|
else if (pOption->type == CompilerOptionType::Input)
|
||||||
result += " " + pOption->setting + " " + compileOptions[key];
|
result += {pOption->setting, compileOptions[key]};
|
||||||
else {
|
else {
|
||||||
result += " " + pOption->setting + compileOptions[key];
|
result << pOption->setting + compileOptions[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -581,47 +579,43 @@ QString Compiler::getLibraryArguments(FileType fileType)
|
||||||
QStringList params = textToLines(compilerSet()->customLinkParams());
|
QStringList params = textToLines(compilerSet()->customLinkParams());
|
||||||
if (!params.isEmpty()) {
|
if (!params.isEmpty()) {
|
||||||
foreach(const QString& param, params)
|
foreach(const QString& param, params)
|
||||||
result += " " + param;
|
result << param;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mProject) {
|
if (mProject) {
|
||||||
if (mProject->options().type == ProjectType::GUI) {
|
if (mProject->options().type == ProjectType::GUI) {
|
||||||
result += " -mwindows";
|
result << "-mwindows";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mProject->options().linkerCmd.isEmpty()) {
|
if (!mProject->options().linkerCmd.isEmpty()) {
|
||||||
QString s = mProject->options().linkerCmd;
|
QString s = mProject->options().linkerCmd;
|
||||||
if (!s.isEmpty()) {
|
if (!s.isEmpty()) {
|
||||||
s.replace("_@@_", " ");
|
s.replace("_@@_", " "); // historical reason
|
||||||
QStringList params = textToLines(s);
|
result += parseArguments(s, {}, true);
|
||||||
if (!params.isEmpty()) {
|
|
||||||
foreach(const QString& param, params)
|
|
||||||
result += " " + param;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mProject->options().staticLink)
|
if (mProject->options().staticLink)
|
||||||
result += " -static";
|
result << "-static";
|
||||||
} else {
|
} else {
|
||||||
if (compilerSet()->staticLink()) {
|
if (compilerSet()->staticLink()) {
|
||||||
result += " -static";
|
result << "-static";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Compiler::parseFileIncludesForAutolink(
|
QStringList Compiler::parseFileIncludesForAutolink(
|
||||||
const QString &filename,
|
const QString &filename,
|
||||||
QSet<QString>& parsedFiles)
|
QSet<QString>& parsedFiles)
|
||||||
{
|
{
|
||||||
QString result;
|
QStringList result;
|
||||||
if (parsedFiles.contains(filename))
|
if (parsedFiles.contains(filename))
|
||||||
return result;
|
return result;
|
||||||
parsedFiles.insert(filename);
|
parsedFiles.insert(filename);
|
||||||
PAutolink autolink = pAutolinkManager->getLink(filename);
|
PAutolink autolink = pAutolinkManager->getLink(filename);
|
||||||
if (autolink) {
|
if (autolink) {
|
||||||
result += ' '+autolink->linkOption;
|
result += parseArgumentsWithoutVariables(autolink->linkOption);
|
||||||
}
|
}
|
||||||
QStringList includedFiles = mParserForFile->getFileDirectIncludes(filename);
|
QStringList includedFiles = mParserForFile->getFileDirectIncludes(filename);
|
||||||
// log(QString("File %1 included:").arg(filename));
|
// log(QString("File %1 included:").arg(filename));
|
||||||
|
@ -632,9 +626,7 @@ QString Compiler::parseFileIncludesForAutolink(
|
||||||
|
|
||||||
for (int i=includedFiles.size()-1;i>=0;i--) {
|
for (int i=includedFiles.size()-1;i>=0;i--) {
|
||||||
QString includeFilename = includedFiles[i];
|
QString includeFilename = includedFiles[i];
|
||||||
result += parseFileIncludesForAutolink(
|
result += parseFileIncludesForAutolink(includeFilename, parsedFiles);
|
||||||
includeFilename,
|
|
||||||
parsedFiles);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -667,7 +659,7 @@ bool Compiler::parseForceUTF8ForAutolink(const QString &filename, QSet<QString>
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::runCommand(const QString &cmd, const QString &arguments, const QString &workingDir, const QByteArray& inputText, const QString& outputFile)
|
void Compiler::runCommand(const QString &cmd, const QStringList &arguments, const QString &workingDir, const QByteArray& inputText, const QString& outputFile)
|
||||||
{
|
{
|
||||||
QProcess process;
|
QProcess process;
|
||||||
mStop = false;
|
mStop = false;
|
||||||
|
@ -692,7 +684,7 @@ void Compiler::runCommand(const QString &cmd, const QString &arguments, const Q
|
||||||
env.insert("CFLAGS","");
|
env.insert("CFLAGS","");
|
||||||
env.insert("CXXFLAGS","");
|
env.insert("CXXFLAGS","");
|
||||||
process.setProcessEnvironment(env);
|
process.setProcessEnvironment(env);
|
||||||
process.setArguments(splitProcessCommand(arguments));
|
process.setArguments(arguments);
|
||||||
process.setWorkingDirectory(workingDir);
|
process.setWorkingDirectory(workingDir);
|
||||||
QFile output;
|
QFile output;
|
||||||
if (!outputFile.isEmpty()) {
|
if (!outputFile.isEmpty()) {
|
||||||
|
@ -773,6 +765,11 @@ void Compiler::runCommand(const QString &cmd, const QString &arguments, const Q
|
||||||
output.close();
|
output.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Compiler::escapeCommandForLog(const QString &cmd, const QStringList &arguments)
|
||||||
|
{
|
||||||
|
return escapeCommandForPlatformShell(extractFileName(cmd), arguments);
|
||||||
|
}
|
||||||
|
|
||||||
PCppParser Compiler::parser() const
|
PCppParser Compiler::parser() const
|
||||||
{
|
{
|
||||||
return mParserForFile;
|
return mParserForFile;
|
||||||
|
|
|
@ -70,14 +70,14 @@ protected:
|
||||||
virtual QByteArray pipedText();
|
virtual QByteArray pipedText();
|
||||||
virtual bool prepareForRebuild() = 0;
|
virtual bool prepareForRebuild() = 0;
|
||||||
virtual bool beforeRunExtraCommand(int idx);
|
virtual bool beforeRunExtraCommand(int idx);
|
||||||
virtual QString getCharsetArgument(const QByteArray& encoding, FileType fileType, bool onlyCheckSyntax);
|
virtual QStringList getCharsetArgument(const QByteArray& encoding, FileType fileType, bool onlyCheckSyntax);
|
||||||
virtual QString getCCompileArguments(bool checkSyntax);
|
virtual QStringList getCCompileArguments(bool checkSyntax);
|
||||||
virtual QString getCppCompileArguments(bool checkSyntax);
|
virtual QStringList getCppCompileArguments(bool checkSyntax);
|
||||||
virtual QString getCIncludeArguments();
|
virtual QStringList getCIncludeArguments();
|
||||||
virtual QString getProjectIncludeArguments();
|
virtual QStringList getProjectIncludeArguments();
|
||||||
virtual QString getCppIncludeArguments();
|
virtual QStringList getCppIncludeArguments();
|
||||||
virtual QString getLibraryArguments(FileType fileType);
|
virtual QStringList getLibraryArguments(FileType fileType);
|
||||||
virtual QString parseFileIncludesForAutolink(
|
virtual QStringList parseFileIncludesForAutolink(
|
||||||
const QString& filename,
|
const QString& filename,
|
||||||
QSet<QString>& parsedFiles);
|
QSet<QString>& parsedFiles);
|
||||||
virtual bool parseForceUTF8ForAutolink(
|
virtual bool parseForceUTF8ForAutolink(
|
||||||
|
@ -85,15 +85,16 @@ protected:
|
||||||
QSet<QString>& parsedFiles);
|
QSet<QString>& parsedFiles);
|
||||||
void log(const QString& msg);
|
void log(const QString& msg);
|
||||||
void error(const QString& msg);
|
void error(const QString& msg);
|
||||||
void runCommand(const QString& cmd, const QString& arguments, const QString& workingDir, const QByteArray& inputText=QByteArray(), const QString& outputFile=QString());
|
void runCommand(const QString& cmd, const QStringList& arguments, const QString& workingDir, const QByteArray& inputText=QByteArray(), const QString& outputFile=QString());
|
||||||
|
QString escapeCommandForLog(const QString &cmd, const QStringList &arguments);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool mOnlyCheckSyntax;
|
bool mOnlyCheckSyntax;
|
||||||
QString mCompiler;
|
QString mCompiler;
|
||||||
QString mArguments;
|
QStringList mArguments;
|
||||||
QStringList mExtraCompilersList;
|
QList<QString> mExtraCompilersList;
|
||||||
QStringList mExtraArgumentsList;
|
QList<QStringList> mExtraArgumentsList;
|
||||||
QStringList mExtraOutputFilesList;
|
QList<QString> mExtraOutputFilesList;
|
||||||
QString mOutputFile;
|
QString mOutputFile;
|
||||||
int mErrorCount;
|
int mErrorCount;
|
||||||
int mWarningCount;
|
int mWarningCount;
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "executablerunner.h"
|
#include "executablerunner.h"
|
||||||
#include "ojproblemcasesrunner.h"
|
#include "ojproblemcasesrunner.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "utils/parsearg.h"
|
||||||
#include "../systemconsts.h"
|
#include "../systemconsts.h"
|
||||||
#include "../settings.h"
|
#include "../settings.h"
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
@ -267,7 +268,7 @@ void CompilerManager::run(
|
||||||
QString::number(consoleFlag),
|
QString::number(consoleFlag),
|
||||||
sharedMemoryId,
|
sharedMemoryId,
|
||||||
localizePath(filename)
|
localizePath(filename)
|
||||||
} + splitProcessCommand(arguments);
|
} + parseArgumentsWithoutVariables(arguments);
|
||||||
if (pSettings->environment().useCustomTerminal()) {
|
if (pSettings->environment().useCustomTerminal()) {
|
||||||
auto [filename, args, fileOwner] = wrapCommandForTerminalEmulator(
|
auto [filename, args, fileOwner] = wrapCommandForTerminalEmulator(
|
||||||
pSettings->environment().terminalPath(),
|
pSettings->environment().terminalPath(),
|
||||||
|
@ -285,7 +286,7 @@ void CompilerManager::run(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//delete when thread finished
|
//delete when thread finished
|
||||||
execRunner = new ExecutableRunner(filename,splitProcessCommand(arguments),workDir);
|
execRunner = new ExecutableRunner(filename, parseArgumentsWithoutVariables(arguments), workDir);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
QStringList execArgs;
|
QStringList execArgs;
|
||||||
|
@ -310,19 +311,19 @@ void CompilerManager::run(
|
||||||
sharedMemoryId,
|
sharedMemoryId,
|
||||||
redirectInputFilename,
|
redirectInputFilename,
|
||||||
localizePath(filename),
|
localizePath(filename),
|
||||||
} + splitProcessCommand(arguments);
|
} + parseArgumentsWithoutVariables(arguments);
|
||||||
} else {
|
} else {
|
||||||
execArgs = QStringList{
|
execArgs = QStringList{
|
||||||
consolePauserPath,
|
consolePauserPath,
|
||||||
QString::number(consoleFlag),
|
QString::number(consoleFlag),
|
||||||
sharedMemoryId,
|
sharedMemoryId,
|
||||||
localizePath(filename),
|
localizePath(filename),
|
||||||
} + splitProcessCommand(arguments);
|
} + parseArgumentsWithoutVariables(arguments);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
execArgs = QStringList{
|
execArgs = QStringList{
|
||||||
localizePath(filename),
|
localizePath(filename),
|
||||||
} + splitProcessCommand(arguments);
|
} + parseArgumentsWithoutVariables(arguments);
|
||||||
}
|
}
|
||||||
auto [filename, args, fileOwner] = wrapCommandForTerminalEmulator(
|
auto [filename, args, fileOwner] = wrapCommandForTerminalEmulator(
|
||||||
pSettings->environment().terminalPath(),
|
pSettings->environment().terminalPath(),
|
||||||
|
@ -336,7 +337,7 @@ void CompilerManager::run(
|
||||||
execRunner->setStartConsole(true);
|
execRunner->setStartConsole(true);
|
||||||
} else {
|
} else {
|
||||||
//delete when thread finished
|
//delete when thread finished
|
||||||
execRunner = new ExecutableRunner(filename,splitProcessCommand(arguments),workDir);
|
execRunner = new ExecutableRunner(filename, parseArgumentsWithoutVariables(arguments), workDir);
|
||||||
}
|
}
|
||||||
if (redirectInput) {
|
if (redirectInput) {
|
||||||
execRunner->setRedirectInput(true);
|
execRunner->setRedirectInput(true);
|
||||||
|
@ -380,7 +381,7 @@ void CompilerManager::doRunProblem(const QString &filename, const QString &argum
|
||||||
if (mRunner!=nullptr) {
|
if (mRunner!=nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
OJProblemCasesRunner * execRunner = new OJProblemCasesRunner(filename,splitProcessCommand(arguments),workDir,problemCases);
|
OJProblemCasesRunner * execRunner = new OJProblemCasesRunner(filename, parseArgumentsWithoutVariables(arguments), workDir, problemCases);
|
||||||
mRunner = execRunner;
|
mRunner = execRunner;
|
||||||
if (pSettings->executor().enableCaseLimit()) {
|
if (pSettings->executor().enableCaseLimit()) {
|
||||||
execRunner->setExecTimeout(pSettings->executor().caseTimeout());
|
execRunner->setExecTimeout(pSettings->executor().caseTimeout());
|
||||||
|
|
|
@ -66,20 +66,20 @@ bool FileCompiler::prepareForCompile()
|
||||||
log(tr("- Compiler Set Name: %1").arg(compilerSet()->name()));
|
log(tr("- Compiler Set Name: %1").arg(compilerSet()->name()));
|
||||||
log("");
|
log("");
|
||||||
FileType fileType = getFileType(mFilename);
|
FileType fileType = getFileType(mFilename);
|
||||||
mArguments = QString(" \"%1\"").arg(mFilename);
|
mArguments = QStringList{mFilename};
|
||||||
if (!mOnlyCheckSyntax) {
|
if (!mOnlyCheckSyntax) {
|
||||||
switch(compilerSet()->compilationStage()) {
|
switch(compilerSet()->compilationStage()) {
|
||||||
case Settings::CompilerSet::CompilationStage::PreprocessingOnly:
|
case Settings::CompilerSet::CompilationStage::PreprocessingOnly:
|
||||||
mOutputFile=changeFileExt(mFilename,compilerSet()->preprocessingSuffix());
|
mOutputFile=changeFileExt(mFilename,compilerSet()->preprocessingSuffix());
|
||||||
mArguments+=" -E";
|
mArguments << "-E";
|
||||||
break;
|
break;
|
||||||
case Settings::CompilerSet::CompilationStage::CompilationProperOnly:
|
case Settings::CompilerSet::CompilationStage::CompilationProperOnly:
|
||||||
mOutputFile=changeFileExt(mFilename,compilerSet()->compilationProperSuffix());
|
mOutputFile=changeFileExt(mFilename,compilerSet()->compilationProperSuffix());
|
||||||
mArguments+=" -S -fverbose-asm";
|
mArguments += {"-S", "-fverbose-asm"};
|
||||||
break;
|
break;
|
||||||
case Settings::CompilerSet::CompilationStage::AssemblingOnly:
|
case Settings::CompilerSet::CompilationStage::AssemblingOnly:
|
||||||
mOutputFile=changeFileExt(mFilename,compilerSet()->assemblingSuffix());
|
mOutputFile=changeFileExt(mFilename,compilerSet()->assemblingSuffix());
|
||||||
mArguments+=" -c";
|
mArguments << "-c";
|
||||||
break;
|
break;
|
||||||
case Settings::CompilerSet::CompilationStage::GenerateExecutable:
|
case Settings::CompilerSet::CompilationStage::GenerateExecutable:
|
||||||
mOutputFile = changeFileExt(mFilename,compilerSet()->executableSuffix());
|
mOutputFile = changeFileExt(mFilename,compilerSet()->executableSuffix());
|
||||||
|
@ -91,14 +91,14 @@ bool FileCompiler::prepareForCompile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
mArguments+=QString(" -o \"%1\"").arg(mOutputFile);
|
mArguments += {"-o", mOutputFile};
|
||||||
|
|
||||||
#if defined(ARCH_X86_64) || defined(ARCH_X86)
|
#if defined(ARCH_X86_64) || defined(ARCH_X86)
|
||||||
if (mCompileType == CppCompileType::GenerateAssemblyOnly) {
|
if (mCompileType == CppCompileType::GenerateAssemblyOnly) {
|
||||||
if (pSettings->languages().noSEHDirectivesWhenGenerateASM())
|
if (pSettings->languages().noSEHDirectivesWhenGenerateASM())
|
||||||
mArguments+=" -fno-asynchronous-unwind-tables";
|
mArguments << "-fno-asynchronous-unwind-tables";
|
||||||
if (pSettings->languages().x86DialectOfASMGenerated()==Settings::Languages::X86ASMDialect::Intel)
|
if (pSettings->languages().x86DialectOfASMGenerated()==Settings::Languages::X86ASMDialect::Intel)
|
||||||
mArguments+=" -masm=intel";
|
mArguments << "-masm=intel";
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
//remove the old file if it exists
|
//remove the old file if it exists
|
||||||
|
@ -171,7 +171,7 @@ bool FileCompiler::prepareForCompile()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (hasStart) {
|
if (hasStart) {
|
||||||
mArguments+=" -nostartfiles";
|
mArguments << "-nostartfiles";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +185,8 @@ bool FileCompiler::prepareForCompile()
|
||||||
log(tr("Processing %1 source file:").arg(strFileType));
|
log(tr("Processing %1 source file:").arg(strFileType));
|
||||||
log("------------------");
|
log("------------------");
|
||||||
log(tr("%1 Compiler: %2").arg(strFileType).arg(mCompiler));
|
log(tr("%1 Compiler: %2").arg(strFileType).arg(mCompiler));
|
||||||
log(tr("Command: %1 %2").arg(extractFileName(mCompiler)).arg(mArguments));
|
QString command = escapeCommandForLog(mCompiler, mArguments);
|
||||||
|
log(tr("Command: %1").arg(command));
|
||||||
mDirectory = extractFileDir(mFilename);
|
mDirectory = extractFileDir(mFilename);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,10 @@
|
||||||
#include "../systemconsts.h"
|
#include "../systemconsts.h"
|
||||||
#include "qt_utils/charsetinfo.h"
|
#include "qt_utils/charsetinfo.h"
|
||||||
#include "../editor.h"
|
#include "../editor.h"
|
||||||
|
#include "qt_utils/utils.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "utils/escape.h"
|
||||||
|
#include "utils/parsearg.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
|
@ -51,12 +55,15 @@ void ProjectCompiler::createStandardMakeFile()
|
||||||
{
|
{
|
||||||
QFile file(mProject->makeFileName());
|
QFile file(mProject->makeFileName());
|
||||||
newMakeFile(file);
|
newMakeFile(file);
|
||||||
file.write("$(BIN): $(OBJ)\n");
|
QString executable = extractRelativePath(mProject->makeFileName(), mProject->executable());
|
||||||
|
QString exeTarget = escapeFilenameForMakefileTarget(executable);
|
||||||
|
QString exeCommand = escapeArgumentForMakefileRecipe(executable, false);
|
||||||
|
writeln(file, exeTarget + ": $(OBJ)\n");
|
||||||
if (!mOnlyCheckSyntax) {
|
if (!mOnlyCheckSyntax) {
|
||||||
if (mProject->options().isCpp) {
|
if (mProject->options().isCpp) {
|
||||||
writeln(file,"\t$(CPP) $(LINKOBJ) -o $(BIN) $(LIBS)");
|
writeln(file, "\t$(CXX) $(LINKOBJ) -o " + exeCommand + " $(LIBS)");
|
||||||
} else
|
} else
|
||||||
writeln(file,"\t$(CC) $(LINKOBJ) -o $(BIN) $(LIBS)");
|
writeln(file, "\t$(CC) $(LINKOBJ) -o " + exeCommand + " $(LIBS)");
|
||||||
}
|
}
|
||||||
writeMakeObjFilesRules(file);
|
writeMakeObjFilesRules(file);
|
||||||
}
|
}
|
||||||
|
@ -65,9 +72,12 @@ void ProjectCompiler::createStaticMakeFile()
|
||||||
{
|
{
|
||||||
QFile file(mProject->makeFileName());
|
QFile file(mProject->makeFileName());
|
||||||
newMakeFile(file);
|
newMakeFile(file);
|
||||||
writeln(file,"$(BIN): $(LINKOBJ)");
|
QString executable = extractRelativePath(mProject->makeFileName(), mProject->executable());
|
||||||
writeln(file,"\tar r $(BIN) $(LINKOBJ)");
|
QString exeTarget = escapeFilenameForMakefileTarget(executable);
|
||||||
writeln(file,"\tranlib $(BIN)");
|
QString exeCommand = escapeArgumentForMakefileRecipe(executable, false);
|
||||||
|
writeln(file, exeTarget + ": $(LINKOBJ)");
|
||||||
|
writeln(file, "\tar r " + exeCommand + " $(LINKOBJ)");
|
||||||
|
writeln(file, "\tranlib " + exeCommand);
|
||||||
writeMakeObjFilesRules(file);
|
writeMakeObjFilesRules(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,11 +85,14 @@ void ProjectCompiler::createDynamicMakeFile()
|
||||||
{
|
{
|
||||||
QFile file(mProject->makeFileName());
|
QFile file(mProject->makeFileName());
|
||||||
newMakeFile(file);
|
newMakeFile(file);
|
||||||
writeln(file,"$(BIN): $(LINKOBJ)");
|
QString executable = extractRelativePath(mProject->makeFileName(), mProject->executable());
|
||||||
|
QString exeTarget = escapeFilenameForMakefileTarget(executable);
|
||||||
|
QString exeCommand = escapeArgumentForMakefileRecipe(executable, false);
|
||||||
|
writeln(file, exeTarget + ": $(LINKOBJ)");
|
||||||
if (mProject->options().isCpp) {
|
if (mProject->options().isCpp) {
|
||||||
writeln(file, "\t$(CPP) -mdll $(LINKOBJ) -o $(BIN) $(LIBS) -Wl,--output-def,$(DEF),--out-implib,$(STATIC)");
|
writeln(file, "\t$(CXX) -mdll $(LINKOBJ) -o " + exeCommand + " $(LIBS) -Wl,--output-def,$(DEF),--out-implib,$(STATIC)");
|
||||||
} else {
|
} else {
|
||||||
writeln(file, "\t$(CC) -mdll $(LINKOBJ) -o $(BIN) $(LIBS) -Wl,--output-def,$(DEF),--out-implib,$(STATIC)");
|
writeln(file, "\t$(CC) -mdll $(LINKOBJ) -o " + exeCommand + " $(LIBS) -Wl,--output-def,$(DEF),--out-implib,$(STATIC)");
|
||||||
}
|
}
|
||||||
writeMakeObjFilesRules(file);
|
writeMakeObjFilesRules(file);
|
||||||
}
|
}
|
||||||
|
@ -121,8 +134,13 @@ void ProjectCompiler::newMakeFile(QFile& file)
|
||||||
// PCH
|
// PCH
|
||||||
if (mProject->options().usePrecompiledHeader
|
if (mProject->options().usePrecompiledHeader
|
||||||
&& fileExists(mProject->options().precompiledHeader)) {
|
&& fileExists(mProject->options().precompiledHeader)) {
|
||||||
writeln(file, "$(PCH) : $(PCH_H)");
|
QString pchH = extractRelativePath(mProject->makeFileName(), mProject->options().precompiledHeader);
|
||||||
writeln(file, "\t$(CPP) -c $(PCH_H) -o $(PCH) $(CXXFLAGS)");
|
QString pchHCommand = escapeArgumentForMakefileRecipe(pchH, false);
|
||||||
|
QString pch = extractRelativePath(mProject->makeFileName(), mProject->options().precompiledHeader + "." GCH_EXT);
|
||||||
|
QString pchTarget = escapeFilenameForMakefileTarget(pch);
|
||||||
|
QString pchCommand = escapeArgumentForMakefileRecipe(pch, false);
|
||||||
|
writeln(file, pchTarget + ": $(PCH_H)");
|
||||||
|
writeln(file, "\t$(CXX) -c " + pchHCommand + " -o " + pchCommand + " $(CXXFLAGS)");
|
||||||
writeln(file);
|
writeln(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,9 +155,9 @@ void ProjectCompiler::writeMakeHeader(QFile &file)
|
||||||
void ProjectCompiler::writeMakeDefines(QFile &file)
|
void ProjectCompiler::writeMakeDefines(QFile &file)
|
||||||
{
|
{
|
||||||
// Get list of object files
|
// Get list of object files
|
||||||
QString Objects;
|
QStringList Objects;
|
||||||
QString LinkObjects;
|
QStringList LinkObjects;
|
||||||
QString cleanObjects;
|
QStringList cleanObjects;
|
||||||
|
|
||||||
// Create a list of object files
|
// Create a list of object files
|
||||||
foreach(const PProjectUnit &unit, mProject->unitList()) {
|
foreach(const PProjectUnit &unit, mProject->unitList()) {
|
||||||
|
@ -157,35 +175,22 @@ void ProjectCompiler::writeMakeDefines(QFile &file)
|
||||||
QString fullObjFile = includeTrailingPathDelimiter(mProject->options().objectOutput)
|
QString fullObjFile = includeTrailingPathDelimiter(mProject->options().objectOutput)
|
||||||
+ extractFileName(unit->fileName());
|
+ extractFileName(unit->fileName());
|
||||||
QString relativeObjFile = extractRelativePath(mProject->directory(), changeFileExt(fullObjFile, OBJ_EXT));
|
QString relativeObjFile = extractRelativePath(mProject->directory(), changeFileExt(fullObjFile, OBJ_EXT));
|
||||||
QString objFile = genMakePath2(relativeObjFile);
|
Objects << relativeObjFile;
|
||||||
Objects += ' ' + objFile;
|
cleanObjects << localizePath(relativeObjFile);
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
cleanObjects += ' ' + genMakePath1(relativeObjFile).replace("/",QDir::separator());
|
|
||||||
#else
|
|
||||||
cleanObjects += ' ' + genMakePath1(relativeObjFile);
|
|
||||||
#endif
|
|
||||||
if (unit->link()) {
|
if (unit->link()) {
|
||||||
LinkObjects += ' ' + genMakePath1(relativeObjFile);
|
LinkObjects << relativeObjFile;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Objects += ' ' + genMakePath2(changeFileExt(RelativeName, OBJ_EXT));
|
Objects << changeFileExt(RelativeName, OBJ_EXT);
|
||||||
#ifdef Q_OS_WIN
|
cleanObjects << localizePath(changeFileExt(RelativeName, OBJ_EXT));
|
||||||
cleanObjects += ' ' + genMakePath1(changeFileExt(RelativeName, OBJ_EXT)).replace("/",QDir::separator());
|
|
||||||
#else
|
|
||||||
cleanObjects += ' ' + genMakePath1(changeFileExt(RelativeName, OBJ_EXT));
|
|
||||||
#endif
|
|
||||||
if (unit->link())
|
if (unit->link())
|
||||||
LinkObjects = LinkObjects + ' ' + genMakePath1(changeFileExt(RelativeName, OBJ_EXT));
|
LinkObjects << changeFileExt(RelativeName, OBJ_EXT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Objects = Objects.trimmed();
|
|
||||||
LinkObjects = LinkObjects.trimmed();
|
|
||||||
|
|
||||||
// Get windres file
|
// Get windres file
|
||||||
QString objResFile;
|
QString objResFile;
|
||||||
QString objResFile2;
|
|
||||||
QString cleanRes;
|
QString cleanRes;
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
if (!mProject->options().privateResource.isEmpty()) {
|
if (!mProject->options().privateResource.isEmpty()) {
|
||||||
|
@ -194,86 +199,90 @@ void ProjectCompiler::writeMakeDefines(QFile &file)
|
||||||
changeFileExt(mProject->options().privateResource, RES_EXT);
|
changeFileExt(mProject->options().privateResource, RES_EXT);
|
||||||
|
|
||||||
QString relativeResFile = extractRelativePath(mProject->directory(), fullResFile);
|
QString relativeResFile = extractRelativePath(mProject->directory(), fullResFile);
|
||||||
objResFile = genMakePath1(relativeResFile);
|
objResFile = relativeResFile;
|
||||||
objResFile2 = genMakePath2(relativeResFile);
|
cleanRes = localizePath(changeFileExt(relativeResFile, RES_EXT));
|
||||||
cleanRes += ' ' + genMakePath1(changeFileExt(relativeResFile, RES_EXT)).replace("/",QDir::separator());
|
|
||||||
} else {
|
} else {
|
||||||
objResFile = genMakePath1(changeFileExt(mProject->options().privateResource, RES_EXT));
|
objResFile = changeFileExt(mProject->options().privateResource, RES_EXT);
|
||||||
objResFile2 = genMakePath2(changeFileExt(mProject->options().privateResource, RES_EXT));
|
cleanRes = localizePath(changeFileExt(mProject->options().privateResource, RES_EXT));
|
||||||
cleanRes += ' ' + genMakePath1(changeFileExt(mProject->options().privateResource, RES_EXT)).replace("/",QDir::separator());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Mention progress in the logs
|
// Mention progress in the logs
|
||||||
if (!objResFile.isEmpty()) {
|
if (!objResFile.isEmpty()) {
|
||||||
log(tr("- Resource File: %1").arg(generateAbsolutePath(mProject->directory(),objResFile)));
|
QString absolutePath = generateAbsolutePath(mProject->directory(),objResFile);
|
||||||
|
QString escaped = escapeArgumentForPlatformShell(absolutePath, false);
|
||||||
|
log(tr("- Resource File: %1").arg(escaped));
|
||||||
}
|
}
|
||||||
log("");
|
log("");
|
||||||
|
|
||||||
|
QString cxx = extractFileName(compilerSet()->cppCompiler());
|
||||||
|
QString cc = extractFileName(compilerSet()->CCompiler());
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
QString windres = extractFileName(compilerSet()->resourceCompiler());
|
||||||
|
#endif
|
||||||
|
|
||||||
// Get list of applicable flags
|
// Get list of applicable flags
|
||||||
QString cCompileArguments = getCCompileArguments(false);
|
QStringList cCompileArguments = getCCompileArguments(false);
|
||||||
QString cppCompileArguments = getCppCompileArguments(false);
|
QStringList cxxCompileArguments = getCppCompileArguments(false);
|
||||||
QString libraryArguments = getLibraryArguments(FileType::Project);
|
if (cCompileArguments.contains("-g3")) {
|
||||||
QString cIncludeArguments = getCIncludeArguments() + " " + getProjectIncludeArguments();
|
cCompileArguments << "-D__DEBUG__";
|
||||||
QString cppIncludeArguments = getCppIncludeArguments() + " " +getProjectIncludeArguments();
|
cxxCompileArguments << "-D__DEBUG__";
|
||||||
|
|
||||||
if (cCompileArguments.indexOf(" -g3")>=0
|
|
||||||
|| cCompileArguments.startsWith("-g3")) {
|
|
||||||
cCompileArguments += " -D__DEBUG__";
|
|
||||||
cppCompileArguments+= " -D__DEBUG__";
|
|
||||||
}
|
}
|
||||||
|
QStringList libraryArguments = getLibraryArguments(FileType::Project);
|
||||||
|
QStringList cIncludeArguments = getCIncludeArguments();
|
||||||
|
QStringList cxxIncludeArguments = getCppIncludeArguments();
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
QStringList resourceArguments = parseArguments(mProject->options().resourceCmd, devCppMacroVariables(), true);
|
||||||
|
#endif
|
||||||
|
|
||||||
writeln(file,"CPP = " + extractFileName(compilerSet()->cppCompiler()));
|
QString executable = extractRelativePath(mProject->makeFileName(), mProject->executable());
|
||||||
writeln(file,"CC = " + extractFileName(compilerSet()->CCompiler()));
|
QString cleanExe = localizePath(executable);
|
||||||
|
QString pchH = extractRelativePath(mProject->makeFileName(), mProject->options().precompiledHeader);
|
||||||
|
QString pch = extractRelativePath(mProject->makeFileName(), mProject->options().precompiledHeader + "." GCH_EXT);
|
||||||
|
|
||||||
|
// programs
|
||||||
|
writeln(file, "CXX = " + escapeArgumentForMakefileVariableValue(cxx, true));
|
||||||
|
writeln(file, "CC = " + escapeArgumentForMakefileVariableValue(cc, true));
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
writeln(file,"WINDRES = " + extractFileName(compilerSet()->resourceCompiler()));
|
writeln(file, "WINDRES = " + escapeArgumentForMakefileVariableValue(windres, true));
|
||||||
#endif
|
#endif
|
||||||
|
writeln(file, "RM = " CLEAN_PROGRAM);
|
||||||
|
|
||||||
|
// compiler flags
|
||||||
|
writeln(file, "LIBS = " + escapeArgumentsForMakefileVariableValue(libraryArguments));
|
||||||
|
writeln(file, "INCS = " + escapeArgumentsForMakefileVariableValue(cIncludeArguments));
|
||||||
|
writeln(file, "CXXINCS = " + escapeArgumentsForMakefileVariableValue(cxxIncludeArguments));
|
||||||
|
writeln(file, "CXXFLAGS = $(CXXINCS) " + escapeArgumentsForMakefileVariableValue(cxxCompileArguments));
|
||||||
|
writeln(file, "CFLAGS = $(INCS) " + escapeArgumentsForMakefileVariableValue(cCompileArguments));
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
writeln(file, "WINDRESFLAGS = " + escapeArgumentsForMakefileVariableValue(resourceArguments));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// objects referenced in prerequisites
|
||||||
|
// do not use them in targets or command arguments, they have different escaping rules
|
||||||
if (!objResFile.isEmpty()) {
|
if (!objResFile.isEmpty()) {
|
||||||
writeln(file,"RES = " + objResFile2);
|
writeln(file, "RES = " + escapeFilenameForMakefilePrerequisite(objResFile));
|
||||||
writeln(file,"OBJ = " + Objects + " $(RES)");
|
writeln(file, "OBJ = " + escapeFilenamesForMakefilePrerequisite(Objects) + " $(RES)");
|
||||||
writeln(file,"LINKOBJ = " + LinkObjects + " " + objResFile);
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
writeln(file,"CLEANOBJ = " + cleanObjects +
|
|
||||||
" " + cleanRes
|
|
||||||
+ " " + genMakePath1(extractRelativePath(mProject->makeFileName(), mProject->executable())).replace("/",QDir::separator()) );
|
|
||||||
#else
|
|
||||||
writeln(file,"CLEANOBJ = " + cleanObjects +
|
|
||||||
" " + cleanRes
|
|
||||||
+ " " + genMakePath1(extractRelativePath(mProject->makeFileName(), mProject->executable())));
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
writeln(file,"OBJ = " + Objects);
|
writeln(file, "OBJ = " + escapeFilenamesForMakefilePrerequisite(Objects));
|
||||||
writeln(file,"LINKOBJ = " + LinkObjects);
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
writeln(file,"CLEANOBJ = " + cleanObjects +
|
|
||||||
+ " " + 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, "BIN = " + escapeFilenameForMakefilePrerequisite(executable));
|
||||||
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
|
if (mProject->options().usePrecompiledHeader
|
||||||
&& fileExists(mProject->options().precompiledHeader)){
|
&& fileExists(mProject->options().precompiledHeader)){
|
||||||
writeln(file,"PCH_H = " + genMakePath1(extractRelativePath(mProject->makeFileName(), mProject->options().precompiledHeader )));
|
writeln(file, "PCH_H = " + escapeFilenameForMakefilePrerequisite(pchH));
|
||||||
writeln(file,"PCH = " + genMakePath1(extractRelativePath(mProject->makeFileName(), mProject->options().precompiledHeader+"."+GCH_EXT)));
|
writeln(file, "PCH = " + escapeFilenameForMakefilePrerequisite(pch));
|
||||||
}
|
}
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
writeln(file,"WINDRESFLAGS = " + mProject->options().resourceCmd);
|
// object referenced in command arguments
|
||||||
#endif
|
// use them in targets or prerequisites, they have different escaping rules
|
||||||
|
if (!objResFile.isEmpty()) {
|
||||||
|
writeln(file, "LINKOBJ = " + escapeArgumentsForMakefileVariableValue(LinkObjects) + " " + escapeArgumentForMakefileVariableValue(objResFile, false));
|
||||||
|
writeln(file, "CLEANOBJ = " + escapeArgumentsForMakefileVariableValue(cleanObjects) + " " + escapeArgumentForMakefileVariableValue(cleanRes, false) + " " + escapeArgumentForMakefileVariableValue(cleanExe, false));
|
||||||
|
} else {
|
||||||
|
writeln(file, "LINKOBJ = " + escapeArgumentsForMakefileVariableValue(LinkObjects));
|
||||||
|
writeln(file, "CLEANOBJ = " + escapeArgumentsForMakefileVariableValue(cleanObjects) + " " + escapeArgumentForMakefileVariableValue(cleanExe, false));
|
||||||
|
};
|
||||||
|
|
||||||
// This needs to be put in before the clean command.
|
// This needs to be put in before the clean command.
|
||||||
if (mProject->options().type == ProjectType::DynamicLib) {
|
if (mProject->options().type == ProjectType::DynamicLib) {
|
||||||
|
@ -290,15 +299,12 @@ void ProjectCompiler::writeMakeDefines(QFile &file)
|
||||||
libOutputFile = extractFileName(libOutputFile);
|
libOutputFile = extractFileName(libOutputFile);
|
||||||
else
|
else
|
||||||
libOutputFile = extractRelativePath(mProject->makeFileName(), libOutputFile);
|
libOutputFile = extractRelativePath(mProject->makeFileName(), libOutputFile);
|
||||||
writeln(file,"DEF = " + genMakePath1(changeFileExt(libOutputFile, DEF_EXT)));
|
|
||||||
writeln(file,"STATIC = " + genMakePath1(changeFileExt(libOutputFile, LIB_EXT)));
|
QString defFile = localizePath(changeFileExt(libOutputFile, DEF_EXT));
|
||||||
#ifdef Q_OS_WIN
|
QString staticFile = localizePath(changeFileExt(libOutputFile, LIB_EXT));
|
||||||
writeln(file,"CLEAN_DEF = " + genMakePath1(changeFileExt(libOutputFile, DEF_EXT)).replace("/",QDir::separator()));
|
|
||||||
writeln(file,"CLEAN_STATIC = " + genMakePath1(changeFileExt(libOutputFile, LIB_EXT)).replace("/",QDir::separator()));
|
writeln(file,"DEF = " + escapeArgumentForMakefileVariableValue(defFile, false));
|
||||||
#else
|
writeln(file,"STATIC = " + escapeArgumentForMakefileVariableValue(staticFile, false));
|
||||||
writeln(file,"CLEAN_DEF = " + genMakePath1(changeFileExt(libOutputFile, DEF_EXT)));
|
|
||||||
writeln(file,"CLEAN_STATIC = " + genMakePath1(changeFileExt(libOutputFile, LIB_EXT)));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
writeln(file);
|
writeln(file);
|
||||||
}
|
}
|
||||||
|
@ -315,7 +321,7 @@ void ProjectCompiler::writeMakeTarget(QFile &file)
|
||||||
void ProjectCompiler::writeMakeIncludes(QFile &file)
|
void ProjectCompiler::writeMakeIncludes(QFile &file)
|
||||||
{
|
{
|
||||||
foreach(const QString& s, mProject->options().makeIncludes) {
|
foreach(const QString& s, mProject->options().makeIncludes) {
|
||||||
writeln(file, "include " + genMakePath1(s));
|
writeln(file, "include " + escapeFilenameForMakefileInclude(s));
|
||||||
}
|
}
|
||||||
if (!mProject->options().makeIncludes.isEmpty()) {
|
if (!mProject->options().makeIncludes.isEmpty()) {
|
||||||
writeln(file);
|
writeln(file);
|
||||||
|
@ -328,11 +334,12 @@ void ProjectCompiler::writeMakeClean(QFile &file)
|
||||||
QString target="$(CLEANOBJ)";
|
QString target="$(CLEANOBJ)";
|
||||||
if (mProject->options().usePrecompiledHeader
|
if (mProject->options().usePrecompiledHeader
|
||||||
&& fileExists(mProject->options().precompiledHeader)) {
|
&& fileExists(mProject->options().precompiledHeader)) {
|
||||||
target += " $(PCH)";
|
QString pch = extractRelativePath(mProject->makeFileName(), mProject->options().precompiledHeader + "." GCH_EXT);
|
||||||
|
target += ' ' + escapeArgumentForMakefileRecipe(pch, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mProject->options().type == ProjectType::DynamicLib) {
|
if (mProject->options().type == ProjectType::DynamicLib) {
|
||||||
target +=" $(CLEAN_DEF) $(CLEAN_STATIC)";
|
target +=" $(DEF) $(STATIC)";
|
||||||
}
|
}
|
||||||
writeln(file, QString("\t-$(RM) %1 > %2 2>&1").arg(target,NULL_FILE));
|
writeln(file, QString("\t-$(RM) %1 > %2 2>&1").arg(target,NULL_FILE));
|
||||||
writeln(file);
|
writeln(file);
|
||||||
|
@ -356,7 +363,7 @@ void ProjectCompiler::writeMakeObjFilesRules(QFile &file)
|
||||||
QString shortFileName = extractRelativePath(mProject->makeFileName(),unit->fileName());
|
QString shortFileName = extractRelativePath(mProject->makeFileName(),unit->fileName());
|
||||||
|
|
||||||
writeln(file);
|
writeln(file);
|
||||||
QString objStr=genMakePath2(shortFileName);
|
QString objStr = escapeFilenameForMakefilePrerequisite(shortFileName);
|
||||||
// if we have scanned it, use scanned info
|
// if we have scanned it, use scanned info
|
||||||
if (parser && parser->fileScanned(unit->fileName())) {
|
if (parser && parser->fileScanned(unit->fileName())) {
|
||||||
QSet<QString> fileIncludes = parser->getIncludedFiles(unit->fileName());
|
QSet<QString> fileIncludes = parser->getIncludedFiles(unit->fileName());
|
||||||
|
@ -367,33 +374,36 @@ void ProjectCompiler::writeMakeObjFilesRules(QFile &file)
|
||||||
if (mProject->options().usePrecompiledHeader &&
|
if (mProject->options().usePrecompiledHeader &&
|
||||||
unit2->fileName() == mProject->options().precompiledHeader)
|
unit2->fileName() == mProject->options().precompiledHeader)
|
||||||
precompileStr = " $(PCH) ";
|
precompileStr = " $(PCH) ";
|
||||||
else
|
else {
|
||||||
objStr = objStr + ' ' + genMakePath2(extractRelativePath(mProject->makeFileName(),unit2->fileName()));
|
QString prereq = extractRelativePath(mProject->makeFileName(), unit2->fileName());
|
||||||
|
objStr = objStr + ' ' + escapeFilenameForMakefilePrerequisite(prereq);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
foreach(const PProjectUnit &unit2, projectUnits) {
|
foreach(const PProjectUnit &unit2, projectUnits) {
|
||||||
FileType fileType = getFileType(unit2->fileName());
|
FileType fileType = getFileType(unit2->fileName());
|
||||||
if (fileType == FileType::CHeader || fileType==FileType::CppHeader)
|
if (fileType == FileType::CHeader || fileType==FileType::CppHeader) {
|
||||||
objStr = objStr + ' ' + genMakePath2(extractRelativePath(mProject->makeFileName(),unit2->fileName()));
|
QString prereq = extractRelativePath(mProject->makeFileName(), unit2->fileName());
|
||||||
|
objStr = objStr + ' ' + escapeFilenameForMakefilePrerequisite(prereq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QString objFileName;
|
}
|
||||||
QString objFileName2;
|
QString objFileNameTarget;
|
||||||
|
QString objFileNameCommand;
|
||||||
if (!mProject->options().objectOutput.isEmpty()) {
|
if (!mProject->options().objectOutput.isEmpty()) {
|
||||||
QString fullObjname = includeTrailingPathDelimiter(mProject->options().objectOutput) +
|
QString fullObjname = includeTrailingPathDelimiter(mProject->options().objectOutput) +
|
||||||
extractFileName(unit->fileName());
|
extractFileName(unit->fileName());
|
||||||
objFileName = genMakePath2(extractRelativePath(mProject->makeFileName(), changeFileExt(fullObjname, OBJ_EXT)));
|
QString objectFile = extractRelativePath(mProject->makeFileName(), changeFileExt(fullObjname, OBJ_EXT));
|
||||||
objFileName2 = genMakePath1(extractRelativePath(mProject->makeFileName(), changeFileExt(fullObjname, OBJ_EXT)));
|
objFileNameTarget = escapeFilenameForMakefileTarget(objectFile);
|
||||||
// if (!extractFileDir(ObjFileName).isEmpty()) {
|
objFileNameCommand = escapeArgumentForMakefileRecipe(objectFile, false);
|
||||||
// objStr = genMakePath2(includeTrailingPathDelimiter(extractFileDir(ObjFileName))) + objStr;
|
|
||||||
// }
|
|
||||||
} else {
|
} else {
|
||||||
objFileName = genMakePath2(changeFileExt(shortFileName, OBJ_EXT));
|
QString objectFile = changeFileExt(shortFileName, OBJ_EXT);
|
||||||
objFileName2 = genMakePath1(changeFileExt(shortFileName, OBJ_EXT));
|
objFileNameTarget = escapeFilenameForMakefileTarget(objectFile);
|
||||||
|
objFileNameCommand = escapeArgumentForMakefileRecipe(objectFile, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
objStr = objFileName + ": "+objStr+precompileStr;
|
objStr = objFileNameTarget + ": " + objStr + precompileStr;
|
||||||
|
|
||||||
writeln(file,objStr);
|
writeln(file,objStr);
|
||||||
|
|
||||||
|
@ -457,11 +467,11 @@ void ProjectCompiler::writeMakeObjFilesRules(QFile &file)
|
||||||
|
|
||||||
if (fileType==FileType::CSource || fileType==FileType::CppSource) {
|
if (fileType==FileType::CSource || fileType==FileType::CppSource) {
|
||||||
if (unit->compileCpp())
|
if (unit->compileCpp())
|
||||||
writeln(file, "\t$(CPP) -c " + genMakePath1(shortFileName) + " -o " + objFileName2 + " $(CXXFLAGS) " + encodingStr);
|
writeln(file, "\t$(CXX) -c " + escapeArgumentForMakefileRecipe(shortFileName, false) + " -o " + objFileNameCommand + " $(CXXFLAGS) " + encodingStr);
|
||||||
else
|
else
|
||||||
writeln(file, "\t$(CC) -c " + genMakePath1(shortFileName) + " -o " + objFileName2 + " $(CFLAGS) " + encodingStr);
|
writeln(file, "\t$(CC) -c " + escapeArgumentForMakefileRecipe(shortFileName, false) + " -o " + objFileNameCommand + " $(CFLAGS) " + encodingStr);
|
||||||
} else if (fileType==FileType::GAS) {
|
} else if (fileType==FileType::GAS) {
|
||||||
writeln(file, "\t$(CC) -c " + genMakePath1(shortFileName) + " -o " + objFileName2 + " $(CFLAGS) " + encodingStr);
|
writeln(file, "\t$(CC) -c " + escapeArgumentForMakefileRecipe(shortFileName, false) + " -o " + objFileNameCommand + " $(CFLAGS) " + encodingStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -473,7 +483,7 @@ void ProjectCompiler::writeMakeObjFilesRules(QFile &file)
|
||||||
for (int i=0;i<mProject->options().resourceIncludes.count();i++) {
|
for (int i=0;i<mProject->options().resourceIncludes.count();i++) {
|
||||||
QString filename = mProject->options().resourceIncludes[i];
|
QString filename = mProject->options().resourceIncludes[i];
|
||||||
if (!filename.isEmpty())
|
if (!filename.isEmpty())
|
||||||
ResIncludes = ResIncludes + " --include-dir " + genMakePath1(filename);
|
ResIncludes += " --include-dir " + escapeArgumentForMakefileRecipe(filename, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString resFiles;
|
QString resFiles;
|
||||||
|
@ -483,10 +493,9 @@ void ProjectCompiler::writeMakeObjFilesRules(QFile &file)
|
||||||
continue;
|
continue;
|
||||||
if (fileExists(unit->fileName())) {
|
if (fileExists(unit->fileName())) {
|
||||||
QString ResFile = extractRelativePath(mProject->makeFileName(), unit->fileName());
|
QString ResFile = extractRelativePath(mProject->makeFileName(), unit->fileName());
|
||||||
resFiles = resFiles + genMakePath2(ResFile) + ' ';
|
resFiles = resFiles + escapeFilenameForMakefilePrerequisite(ResFile) + ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resFiles = resFiles.trimmed();
|
|
||||||
|
|
||||||
// Determine resource output file
|
// Determine resource output file
|
||||||
QString fullName;
|
QString fullName;
|
||||||
|
@ -496,10 +505,12 @@ void ProjectCompiler::writeMakeObjFilesRules(QFile &file)
|
||||||
} else {
|
} else {
|
||||||
fullName = changeFileExt(mProject->options().privateResource, RES_EXT);
|
fullName = changeFileExt(mProject->options().privateResource, RES_EXT);
|
||||||
}
|
}
|
||||||
QString objFileName = genMakePath1(extractRelativePath(mProject->filename(), fullName));
|
QString objFile = extractRelativePath(mProject->filename(), fullName);
|
||||||
QString objFileName2 = genMakePath2(extractRelativePath(mProject->filename(), fullName));
|
QString objFileNameCommand = escapeArgumentForMakefileRecipe(objFile, false);
|
||||||
QString privResName = genMakePath1(extractRelativePath(mProject->filename(), mProject->options().privateResource));
|
QString objFileNameTarget = escapeFilenameForMakefileTarget(objFile);
|
||||||
QString privResName2 = genMakePath2(extractRelativePath(mProject->filename(), mProject->options().privateResource));
|
QString privRes = extractRelativePath(mProject->filename(), mProject->options().privateResource);
|
||||||
|
QString privResNameCommand = escapeArgumentForMakefileRecipe(privRes, false);
|
||||||
|
QString privResNamePrereq = escapeFilenameForMakefilePrerequisite(privRes);
|
||||||
|
|
||||||
// Build final cmd
|
// Build final cmd
|
||||||
QString windresArgs;
|
QString windresArgs;
|
||||||
|
@ -508,8 +519,8 @@ void ProjectCompiler::writeMakeObjFilesRules(QFile &file)
|
||||||
windresArgs = " -F pe-i386";
|
windresArgs = " -F pe-i386";
|
||||||
|
|
||||||
writeln(file);
|
writeln(file);
|
||||||
writeln(file, objFileName2 + ": " + privResName2 + ' ' + resFiles);
|
writeln(file, objFileNameTarget + ": " + privResNamePrereq + ' ' + resFiles);
|
||||||
writeln(file, "\t$(WINDRES) -i " + privResName + windresArgs + " --input-format=rc -o " + objFileName + " -O coff $(WINDRESFLAGS)"
|
writeln(file, "\t$(WINDRES) -i " + privResNameCommand + windresArgs + " --input-format=rc -o " + objFileNameCommand + " -O coff $(WINDRESFLAGS)"
|
||||||
+ ResIncludes);
|
+ ResIncludes);
|
||||||
writeln(file);
|
writeln(file);
|
||||||
}
|
}
|
||||||
|
@ -568,35 +579,39 @@ bool ProjectCompiler::prepareForCompile()
|
||||||
} else {
|
} else {
|
||||||
parallelParam = QString("-j%1").arg(mProject->options().parellelBuildingJobs);
|
parallelParam = QString("-j%1").arg(mProject->options().parellelBuildingJobs);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
parallelParam = "-j1";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString makefile = extractRelativePath(mProject->directory(), mProject->makeFileName());
|
||||||
|
QStringList cleanArgs{
|
||||||
|
"-f",
|
||||||
|
makefile,
|
||||||
|
"clean",
|
||||||
|
};
|
||||||
|
QStringList makeAllArgs{
|
||||||
|
parallelParam,
|
||||||
|
"-f",
|
||||||
|
makefile,
|
||||||
|
"all",
|
||||||
|
};
|
||||||
if (mOnlyClean) {
|
if (mOnlyClean) {
|
||||||
mArguments = QString(" %1 -f \"%2\" clean").arg(parallelParam,
|
mArguments = cleanArgs;
|
||||||
extractRelativePath(
|
|
||||||
mProject->directory(),
|
|
||||||
mProject->makeFileName()));
|
|
||||||
} else if (mRebuild) {
|
} else if (mRebuild) {
|
||||||
mArguments = QString(" -f \"%1\" clean").arg(extractRelativePath(
|
mArguments = cleanArgs;
|
||||||
mProject->directory(),
|
mExtraCompilersList << mCompiler;
|
||||||
mProject->makeFileName()));
|
mExtraOutputFilesList << "";
|
||||||
mExtraCompilersList.append(mCompiler);
|
mExtraArgumentsList << makeAllArgs;
|
||||||
mExtraOutputFilesList.append("");
|
|
||||||
mExtraArgumentsList.append(QString(" %1 -f \"%2\" all").arg(parallelParam,
|
|
||||||
extractRelativePath(
|
|
||||||
mProject->directory(),
|
|
||||||
mProject->makeFileName())));
|
|
||||||
} else {
|
} else {
|
||||||
mArguments = QString(" %1 -f \"%2\" all").arg(parallelParam,
|
mArguments = makeAllArgs;
|
||||||
extractRelativePath(
|
|
||||||
mProject->directory(),
|
|
||||||
mProject->makeFileName()));
|
|
||||||
}
|
}
|
||||||
mDirectory = mProject->directory();
|
mDirectory = mProject->directory();
|
||||||
|
|
||||||
log(tr("Processing makefile:"));
|
log(tr("Processing makefile:"));
|
||||||
log("--------");
|
log("--------");
|
||||||
log(tr("- makefile processer: %1").arg(mCompiler));
|
log(tr("- makefile processer: %1").arg(mCompiler));
|
||||||
log(tr("- Command: %1 %2").arg(extractFileName(mCompiler)).arg(mArguments));
|
QString command = escapeCommandForLog(mCompiler, mArguments);
|
||||||
|
log(tr("- Command: %1").arg(command));
|
||||||
log("");
|
log("");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -43,7 +43,7 @@ bool SDCCFileCompiler::prepareForCompile()
|
||||||
mArguments += getCCompileArguments(false);
|
mArguments += getCCompileArguments(false);
|
||||||
mArguments += getCIncludeArguments();
|
mArguments += getCIncludeArguments();
|
||||||
mArguments += getProjectIncludeArguments();
|
mArguments += getProjectIncludeArguments();
|
||||||
mArguments = QString("--syntax-only \"%1\"").arg(mFilename);
|
mArguments += {"--syntax-only", mFilename};
|
||||||
mDirectory = extractFileDir(mFilename);
|
mDirectory = extractFileDir(mFilename);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -73,16 +73,15 @@ bool SDCCFileCompiler::prepareForCompile()
|
||||||
mNoStartup = (val==COMPILER_OPTION_ON);
|
mNoStartup = (val==COMPILER_OPTION_ON);
|
||||||
if (mNoStartup) {
|
if (mNoStartup) {
|
||||||
mRelFilename = changeFileExt(mFilename,SDCC_REL_SUFFIX);
|
mRelFilename = changeFileExt(mFilename,SDCC_REL_SUFFIX);
|
||||||
mArguments += QString(" -c \"%1\"").arg(mFilename);
|
mArguments += {"-c", mFilename};
|
||||||
mExtraCompilersList.append(mCompiler);
|
mExtraCompilersList << mCompiler;
|
||||||
QString args = getLibraryArguments(FileType::CSource);
|
QStringList args = getLibraryArguments(FileType::CSource);
|
||||||
args += QString(" -o \"%1\" \"%2\" ").arg(mIhxFilename, mRelFilename);
|
args += {"-o", mIhxFilename, mRelFilename};
|
||||||
mExtraArgumentsList.append(args);
|
mExtraArgumentsList << args;
|
||||||
mExtraOutputFilesList.append("");
|
mExtraOutputFilesList << "";
|
||||||
} else {
|
} else {
|
||||||
mArguments += getLibraryArguments(FileType::CSource);
|
mArguments += getLibraryArguments(FileType::CSource);
|
||||||
mArguments += QString(" \"%1\"").arg(mFilename);
|
mArguments += {mFilename, "-o", mIhxFilename};
|
||||||
mArguments+=QString(" -o \"%1\"").arg(mIhxFilename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compilerSet()->executableSuffix() == SDCC_HEX_SUFFIX) {
|
if (compilerSet()->executableSuffix() == SDCC_HEX_SUFFIX) {
|
||||||
|
@ -92,28 +91,26 @@ bool SDCCFileCompiler::prepareForCompile()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
mExtraCompilersList.append(packihx);
|
mExtraCompilersList.append(packihx);
|
||||||
QString args;
|
QStringList args{mIhxFilename};
|
||||||
args = QString(" \"%1\"").arg(mIhxFilename);
|
mExtraArgumentsList << args;
|
||||||
mExtraArgumentsList.append(args);
|
mExtraOutputFilesList << mOutputFile;
|
||||||
mExtraOutputFilesList.append(mOutputFile);
|
|
||||||
} else if (compilerSet()->executableSuffix() == SDCC_BIN_SUFFIX) {
|
} else if (compilerSet()->executableSuffix() == SDCC_BIN_SUFFIX) {
|
||||||
QString makebin = compilerSet()->findProgramInBinDirs(MAKEBIN_PROGRAM);
|
QString makebin = compilerSet()->findProgramInBinDirs(MAKEBIN_PROGRAM);
|
||||||
if (makebin.isEmpty()) {
|
if (makebin.isEmpty()) {
|
||||||
error(tr("Can't find \"%1\".\n").arg(PACKIHX_PROGRAM));
|
error(tr("Can't find \"%1\".\n").arg(PACKIHX_PROGRAM));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
mExtraCompilersList.push_back(makebin);
|
mExtraCompilersList << makebin;
|
||||||
QString args;
|
QStringList args{mIhxFilename, mOutputFile};
|
||||||
args = QString(" \"%1\"").arg(mIhxFilename);
|
mExtraArgumentsList << args;
|
||||||
args+=QString(" \"%1\"").arg(mOutputFile);
|
mExtraOutputFilesList << "";
|
||||||
mExtraArgumentsList.push_back(args);
|
|
||||||
mExtraOutputFilesList.append("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log(tr("Processing %1 source file:").arg(strFileType));
|
log(tr("Processing %1 source file:").arg(strFileType));
|
||||||
log("------------------");
|
log("------------------");
|
||||||
log(tr("- %1 Compiler: %2").arg(strFileType).arg(mCompiler));
|
log(tr("- %1 Compiler: %2").arg(strFileType).arg(mCompiler));
|
||||||
log(tr("- Command: %1 %2").arg(extractFileName(mCompiler)).arg(mArguments));
|
QString command = escapeCommandForLog(mCompiler, mArguments);
|
||||||
|
log(tr("- Command: %1").arg(command));
|
||||||
mDirectory = extractFileDir(mFilename);
|
mDirectory = extractFileDir(mFilename);
|
||||||
mStartCompileTime = QDateTime::currentDateTime();
|
mStartCompileTime = QDateTime::currentDateTime();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
#include "../project.h"
|
#include "../project.h"
|
||||||
#include "compilermanager.h"
|
#include "compilermanager.h"
|
||||||
#include "../systemconsts.h"
|
#include "../systemconsts.h"
|
||||||
|
#include "qt_utils/utils.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "utils/escape.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
|
@ -40,17 +43,17 @@ void SDCCProjectCompiler::createStandardMakeFile()
|
||||||
newMakeFile(file);
|
newMakeFile(file);
|
||||||
QString suffix = compilerSet()->executableSuffix();
|
QString suffix = compilerSet()->executableSuffix();
|
||||||
if (suffix == SDCC_IHX_SUFFIX) {
|
if (suffix == SDCC_IHX_SUFFIX) {
|
||||||
writeln(file,"$(BIN): $(OBJ)");
|
writeln(file,"$(BIN_TAR): $(OBJ)");
|
||||||
writeln(file,"\t$(CC) $(LIBS) -o $(BIN) $(LINKOBJ)");
|
writeln(file,"\t$(CC) $(LIBS) -o $(BIN_ARG) $(LINKOBJ)");
|
||||||
} else {
|
} else {
|
||||||
writeln(file,"$(IHX): $(OBJ)\n");
|
writeln(file,"$(IHX_TAR): $(OBJ)\n");
|
||||||
writeln(file,"\t$(CC) $(LIBS) -o $(IHX) $(LINKOBJ)");
|
writeln(file,"\t$(CC) $(LIBS) -o $(IHX_ARG) $(LINKOBJ)");
|
||||||
if (suffix == SDCC_HEX_SUFFIX) {
|
if (suffix == SDCC_HEX_SUFFIX) {
|
||||||
writeln(file,"$(BIN): $(IHX)");
|
writeln(file,"$(BIN_TAR): $(IHX_DEP)");
|
||||||
writeln(file,"\t$(PACKIHX) $(IHX) > $(BIN)");
|
writeln(file,"\t$(PACKIHX) $(IHX_ARG) > $(BIN_ARG)");
|
||||||
} else {
|
} else {
|
||||||
writeln(file,"$(BIN): $(IHX)");
|
writeln(file,"$(BIN_TAR): $(IHX_DEP)");
|
||||||
writeln(file,"\t$(MAKEBIN) $(IHX) $(BIN)");
|
writeln(file,"\t$(MAKEBIN) $(IHX_ARG) $(BIN_ARG)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writeMakeObjFilesRules(file);
|
writeMakeObjFilesRules(file);
|
||||||
|
@ -102,9 +105,9 @@ void SDCCProjectCompiler::writeMakeHeader(QFile &file)
|
||||||
void SDCCProjectCompiler::writeMakeDefines(QFile &file)
|
void SDCCProjectCompiler::writeMakeDefines(QFile &file)
|
||||||
{
|
{
|
||||||
// Get list of object files
|
// Get list of object files
|
||||||
QString Objects;
|
QStringList Objects;
|
||||||
QString LinkObjects;
|
QStringList LinkObjects;
|
||||||
QString cleanObjects;
|
QStringList cleanObjects;
|
||||||
|
|
||||||
// Create a list of object files
|
// Create a list of object files
|
||||||
foreach(const PProjectUnit &unit, mProject->unitList()) {
|
foreach(const PProjectUnit &unit, mProject->unitList()) {
|
||||||
|
@ -122,67 +125,52 @@ void SDCCProjectCompiler::writeMakeDefines(QFile &file)
|
||||||
QString fullObjFile = includeTrailingPathDelimiter(mProject->options().objectOutput)
|
QString fullObjFile = includeTrailingPathDelimiter(mProject->options().objectOutput)
|
||||||
+ extractFileName(unit->fileName());
|
+ extractFileName(unit->fileName());
|
||||||
QString relativeObjFile = extractRelativePath(mProject->directory(), changeFileExt(fullObjFile, SDCC_REL_SUFFIX));
|
QString relativeObjFile = extractRelativePath(mProject->directory(), changeFileExt(fullObjFile, SDCC_REL_SUFFIX));
|
||||||
QString objFile = genMakePath2(relativeObjFile);
|
Objects << relativeObjFile;
|
||||||
Objects += ' ' + objFile;
|
cleanObjects << localizePath(relativeObjFile);
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
cleanObjects += ' ' + genMakePath1(relativeObjFile).replace("/",QDir::separator());
|
|
||||||
#else
|
|
||||||
cleanObjects += ' ' + genMakePath1(relativeObjFile);
|
|
||||||
#endif
|
|
||||||
if (unit->link()) {
|
if (unit->link()) {
|
||||||
LinkObjects += ' ' + genMakePath1(relativeObjFile);
|
LinkObjects << relativeObjFile;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Objects += ' ' + genMakePath2(changeFileExt(RelativeName, SDCC_REL_SUFFIX));
|
Objects << changeFileExt(RelativeName, SDCC_REL_SUFFIX);
|
||||||
#ifdef Q_OS_WIN
|
cleanObjects << localizePath(changeFileExt(RelativeName, SDCC_REL_SUFFIX));
|
||||||
cleanObjects += ' ' + genMakePath1(changeFileExt(RelativeName, SDCC_REL_SUFFIX)).replace("/",QDir::separator());
|
|
||||||
#else
|
|
||||||
cleanObjects += ' ' + genMakePath1(changeFileExt(RelativeName, SDCC_REL_SUFFIX));
|
|
||||||
#endif
|
|
||||||
if (unit->link())
|
if (unit->link())
|
||||||
LinkObjects = LinkObjects + ' ' + genMakePath1(changeFileExt(RelativeName, SDCC_REL_SUFFIX));
|
LinkObjects << changeFileExt(RelativeName, SDCC_REL_SUFFIX);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Objects = Objects.trimmed();
|
QString cc = extractFileName(compilerSet()->CCompiler());
|
||||||
LinkObjects = LinkObjects.trimmed();
|
|
||||||
|
|
||||||
|
QStringList cCompileArguments = getCCompileArguments(mOnlyCheckSyntax);
|
||||||
|
if (cCompileArguments.contains("-g3"))
|
||||||
|
cCompileArguments << "-D__DEBUG__";
|
||||||
|
QStringList libraryArguments = getLibraryArguments(FileType::Project);
|
||||||
|
QStringList cIncludeArguments = getCIncludeArguments() + getProjectIncludeArguments();
|
||||||
|
|
||||||
// Get list of applicable flags
|
QString executable = extractRelativePath(mProject->makeFileName(), mProject->executable());
|
||||||
QString cCompileArguments = getCCompileArguments(mOnlyCheckSyntax);
|
QString cleanExe = localizePath(executable);
|
||||||
QString libraryArguments = getLibraryArguments(FileType::Project);
|
QString ihx = extractRelativePath(mProject->makeFileName(), changeFileExt(mProject->executable(), SDCC_IHX_SUFFIX));
|
||||||
QString cIncludeArguments = getCIncludeArguments() + " " + getProjectIncludeArguments();
|
QString cleanIhx = localizePath(ihx);
|
||||||
|
|
||||||
if (cCompileArguments.indexOf(" -g3")>=0
|
writeln(file, "CC = " + escapeArgumentForMakefileVariableValue(cc, true));
|
||||||
|| cCompileArguments.startsWith("-g3")) {
|
writeln(file, "PACKIHX = " PACKIHX_PROGRAM);
|
||||||
cCompileArguments += " -D__DEBUG__";
|
writeln(file, "MAKEBIN = " MAKEBIN_PROGRAM);
|
||||||
}
|
|
||||||
|
|
||||||
writeln(file,"CC = " + extractFileName(compilerSet()->CCompiler()));
|
writeln(file, "OBJ = " + escapeFilenamesForMakefilePrerequisite(Objects));
|
||||||
writeln(file,QString("PACKIHX = ") + PACKIHX_PROGRAM);
|
writeln(file, "LINKOBJ = " + escapeArgumentsForMakefileVariableValue(LinkObjects));
|
||||||
writeln(file,QString("MAKEBIN = ") + MAKEBIN_PROGRAM);
|
writeln(file,"CLEANOBJ = " + escapeArgumentsForMakefileVariableValue(cleanObjects) + ' ' +
|
||||||
|
escapeArgumentForMakefileVariableValue(cleanIhx, false) + ' ' +
|
||||||
writeln(file,"OBJ = " + Objects);
|
escapeArgumentForMakefileVariableValue(cleanExe, false));
|
||||||
writeln(file,"LINKOBJ = " + LinkObjects);
|
writeln(file, "LIBS = " + escapeArgumentsForMakefileVariableValue(libraryArguments));
|
||||||
#ifdef Q_OS_WIN
|
writeln(file, "INCS = " + escapeArgumentsForMakefileVariableValue(cIncludeArguments));
|
||||||
writeln(file,"CLEANOBJ = " + cleanObjects +
|
writeln(file, "IHX_TAR = " + escapeFilenameForMakefileTarget(ihx));
|
||||||
+ " " + genMakePath1(extractRelativePath(mProject->makeFileName(), changeFileExt(mProject->executable(),SDCC_IHX_SUFFIX))).replace("/",QDir::separator())
|
writeln(file, "IHX_DEP = " + escapeFilenameForMakefilePrerequisite(ihx));
|
||||||
+ " " + genMakePath1(extractRelativePath(mProject->makeFileName(), mProject->executable())).replace("/",QDir::separator()) );
|
writeln(file, "IHX_ARG = " + escapeArgumentForMakefileVariableValue(ihx, false));
|
||||||
#else
|
writeln(file, "BIN_TAR = " + escapeFilenameForMakefileTarget(executable));
|
||||||
writeln(file,"CLEANOBJ = " + cleanObjects +
|
writeln(file, "BIN_DEP = " + escapeFilenameForMakefilePrerequisite(executable));
|
||||||
+ " " + genMakePath1(extractRelativePath(mProject->makeFileName(), mProject->executable())));
|
writeln(file, "BIN_ARG = " + escapeArgumentForMakefileVariableValue(executable, false));
|
||||||
#endif
|
writeln(file, "CFLAGS = $(INCS) " + escapeArgumentsForMakefileVariableValue(cCompileArguments));
|
||||||
libraryArguments.replace('\\', '/');
|
writeln(file, "RM = " CLEAN_PROGRAM);
|
||||||
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);
|
writeln(file);
|
||||||
}
|
}
|
||||||
|
@ -191,7 +179,7 @@ void SDCCProjectCompiler::writeMakeTarget(QFile &file)
|
||||||
{
|
{
|
||||||
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);
|
||||||
writeln(file, "all: all-before $(BIN) all-after");
|
writeln(file, "all: all-before $(BIN_DEP) all-after");
|
||||||
writeln(file);
|
writeln(file);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -199,7 +187,7 @@ void SDCCProjectCompiler::writeMakeTarget(QFile &file)
|
||||||
void SDCCProjectCompiler::writeMakeIncludes(QFile &file)
|
void SDCCProjectCompiler::writeMakeIncludes(QFile &file)
|
||||||
{
|
{
|
||||||
foreach(const QString& s, mProject->options().makeIncludes) {
|
foreach(const QString& s, mProject->options().makeIncludes) {
|
||||||
writeln(file, "include " + genMakePath1(s));
|
writeln(file, "include " + escapeFilenameForMakefileInclude(s));
|
||||||
}
|
}
|
||||||
if (!mProject->options().makeIncludes.isEmpty()) {
|
if (!mProject->options().makeIncludes.isEmpty()) {
|
||||||
writeln(file);
|
writeln(file);
|
||||||
|
@ -209,16 +197,13 @@ void SDCCProjectCompiler::writeMakeIncludes(QFile &file)
|
||||||
void SDCCProjectCompiler::writeMakeClean(QFile &file)
|
void SDCCProjectCompiler::writeMakeClean(QFile &file)
|
||||||
{
|
{
|
||||||
writeln(file, "clean: clean-custom");
|
writeln(file, "clean: clean-custom");
|
||||||
QString target="$(CLEANOBJ)";
|
writeln(file, QString("\t-$(RM) $(CLEANOBJ) > %1 2>&1").arg(NULL_FILE));
|
||||||
|
|
||||||
writeln(file, QString("\t-$(RM) %1 > %2 2>&1").arg(target,NULL_FILE));
|
|
||||||
writeln(file);
|
writeln(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDCCProjectCompiler::writeMakeObjFilesRules(QFile &file)
|
void SDCCProjectCompiler::writeMakeObjFilesRules(QFile &file)
|
||||||
{
|
{
|
||||||
PCppParser parser = mProject->cppParser();
|
PCppParser parser = mProject->cppParser();
|
||||||
QString precompileStr;
|
|
||||||
|
|
||||||
QList<PProjectUnit> projectUnits=mProject->unitList();
|
QList<PProjectUnit> projectUnits=mProject->unitList();
|
||||||
foreach(const PProjectUnit &unit, projectUnits) {
|
foreach(const PProjectUnit &unit, projectUnits) {
|
||||||
|
@ -233,7 +218,7 @@ void SDCCProjectCompiler::writeMakeObjFilesRules(QFile &file)
|
||||||
QString shortFileName = extractRelativePath(mProject->makeFileName(),unit->fileName());
|
QString shortFileName = extractRelativePath(mProject->makeFileName(),unit->fileName());
|
||||||
|
|
||||||
writeln(file);
|
writeln(file);
|
||||||
QString objStr=genMakePath2(shortFileName);
|
QString objStr = escapeFilenameForMakefilePrerequisite(shortFileName);
|
||||||
// if we have scanned it, use scanned info
|
// if we have scanned it, use scanned info
|
||||||
if (parser && parser->fileScanned(unit->fileName())) {
|
if (parser && parser->fileScanned(unit->fileName())) {
|
||||||
QSet<QString> fileIncludes = parser->getIncludedFiles(unit->fileName());
|
QSet<QString> fileIncludes = parser->getIncludedFiles(unit->fileName());
|
||||||
|
@ -241,32 +226,34 @@ void SDCCProjectCompiler::writeMakeObjFilesRules(QFile &file)
|
||||||
if (unit2==unit)
|
if (unit2==unit)
|
||||||
continue;
|
continue;
|
||||||
if (fileIncludes.contains(unit2->fileName())) {
|
if (fileIncludes.contains(unit2->fileName())) {
|
||||||
objStr = objStr + ' ' + genMakePath2(extractRelativePath(mProject->makeFileName(),unit2->fileName()));
|
QString header = extractRelativePath(mProject->makeFileName(),unit2->fileName());
|
||||||
|
objStr = objStr + ' ' + escapeFilenameForMakefilePrerequisite(header);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
foreach(const PProjectUnit &unit2, projectUnits) {
|
foreach(const PProjectUnit &unit2, projectUnits) {
|
||||||
FileType fileType = getFileType(unit2->fileName());
|
FileType fileType = getFileType(unit2->fileName());
|
||||||
if (fileType == FileType::CHeader || fileType==FileType::CppHeader)
|
if (fileType == FileType::CHeader || fileType==FileType::CppHeader) {
|
||||||
objStr = objStr + ' ' + genMakePath2(extractRelativePath(mProject->makeFileName(),unit2->fileName()));
|
QString header = extractRelativePath(mProject->makeFileName(),unit2->fileName());
|
||||||
|
objStr = objStr + ' ' + escapeFilenameForMakefilePrerequisite(header);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QString objFileName;
|
}
|
||||||
QString objFileName2;
|
QString objFileNameTarget;
|
||||||
|
QString objFileNameCommand;
|
||||||
if (!mProject->options().objectOutput.isEmpty()) {
|
if (!mProject->options().objectOutput.isEmpty()) {
|
||||||
QString fullObjname = includeTrailingPathDelimiter(mProject->options().objectOutput) +
|
QString fullObjname = includeTrailingPathDelimiter(mProject->options().objectOutput) +
|
||||||
extractFileName(unit->fileName());
|
extractFileName(unit->fileName());
|
||||||
objFileName = genMakePath2(extractRelativePath(mProject->makeFileName(), changeFileExt(fullObjname, SDCC_REL_SUFFIX)));
|
QString objFile = extractRelativePath(mProject->makeFileName(), changeFileExt(fullObjname, SDCC_REL_SUFFIX));
|
||||||
objFileName2 = genMakePath1(extractRelativePath(mProject->makeFileName(), changeFileExt(fullObjname, SDCC_REL_SUFFIX)));
|
objFileNameTarget = escapeFilenameForMakefileTarget(objFile);
|
||||||
// if (!extractFileDir(ObjFileName).isEmpty()) {
|
objFileNameCommand = escapeArgumentForMakefileRecipe(objFile, false);
|
||||||
// objStr = genMakePath2(includeTrailingPathDelimiter(extractFileDir(ObjFileName))) + objStr;
|
|
||||||
// }
|
|
||||||
} else {
|
} else {
|
||||||
objFileName = genMakePath2(changeFileExt(shortFileName, SDCC_REL_SUFFIX));
|
QString objFile = changeFileExt(shortFileName, SDCC_REL_SUFFIX);
|
||||||
objFileName2 = genMakePath1(changeFileExt(shortFileName, SDCC_REL_SUFFIX));
|
objFileNameTarget = escapeFilenameForMakefileTarget(objFile);
|
||||||
|
objFileNameCommand = escapeArgumentForMakefileRecipe(objFile, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
objStr = objFileName + ": "+objStr+precompileStr;
|
objStr = objFileNameTarget + ": " + objStr;
|
||||||
|
|
||||||
writeln(file, objStr);
|
writeln(file, objStr);
|
||||||
|
|
||||||
|
@ -278,7 +265,7 @@ void SDCCProjectCompiler::writeMakeObjFilesRules(QFile &file)
|
||||||
// Or roll our own
|
// Or roll our own
|
||||||
} else {
|
} else {
|
||||||
if (fileType==FileType::CSource) {
|
if (fileType==FileType::CSource) {
|
||||||
writeln(file, "\t$(CC) $(CFLAGS) -c " + genMakePath1(shortFileName));
|
writeln(file, "\t$(CC) $(CFLAGS) -c " + escapeArgumentForMakefileRecipe(shortFileName, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,35 +314,40 @@ bool SDCCProjectCompiler::prepareForCompile()
|
||||||
} else {
|
} else {
|
||||||
parallelParam = QString("-j%1").arg(mProject->options().parellelBuildingJobs);
|
parallelParam = QString("-j%1").arg(mProject->options().parellelBuildingJobs);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
parallelParam = "-j1";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString makefile =
|
||||||
|
extractRelativePath(mProject->directory(), mProject->makeFileName());
|
||||||
|
QStringList cleanArgs{
|
||||||
|
"-f",
|
||||||
|
makefile,
|
||||||
|
"clean",
|
||||||
|
};
|
||||||
|
QStringList makeAllArgs{
|
||||||
|
parallelParam,
|
||||||
|
"-f",
|
||||||
|
makefile,
|
||||||
|
"all",
|
||||||
|
};
|
||||||
if (onlyClean()) {
|
if (onlyClean()) {
|
||||||
mArguments = QString(" %1 -f \"%2\" clean").arg(parallelParam,
|
mArguments = cleanArgs;
|
||||||
extractRelativePath(
|
|
||||||
mProject->directory(),
|
|
||||||
mProject->makeFileName()));
|
|
||||||
} else if (mRebuild) {
|
} else if (mRebuild) {
|
||||||
mArguments = QString(" -f \"%1\" clean").arg(extractRelativePath(
|
mArguments = cleanArgs;
|
||||||
mProject->directory(),
|
mExtraCompilersList << mCompiler;
|
||||||
mProject->makeFileName()));
|
mExtraOutputFilesList << "";
|
||||||
mExtraCompilersList.append(mCompiler);
|
mExtraArgumentsList << makeAllArgs;
|
||||||
mExtraOutputFilesList.append("");
|
|
||||||
mExtraArgumentsList.append(QString(" %1 -f \"%2\" all").arg(parallelParam,
|
|
||||||
extractRelativePath(
|
|
||||||
mProject->directory(),
|
|
||||||
mProject->makeFileName())));
|
|
||||||
} else {
|
} else {
|
||||||
mArguments = QString(" %1 -f \"%2\" all").arg(parallelParam,
|
mArguments = makeAllArgs;
|
||||||
extractRelativePath(
|
|
||||||
mProject->directory(),
|
|
||||||
mProject->makeFileName()));
|
|
||||||
}
|
}
|
||||||
mDirectory = mProject->directory();
|
mDirectory = mProject->directory();
|
||||||
|
|
||||||
log(tr("Processing makefile:"));
|
log(tr("Processing makefile:"));
|
||||||
log("--------");
|
log("--------");
|
||||||
log(tr("- makefile processer: %1").arg(mCompiler));
|
log(tr("- makefile processer: %1").arg(mCompiler));
|
||||||
log(tr("- Command: %1 %2").arg(extractFileName(mCompiler)).arg(mArguments));
|
QString command = escapeCommandForLog(mCompiler, mArguments);
|
||||||
|
log(tr("- Command: %1").arg(command));
|
||||||
log("");
|
log("");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -46,7 +46,7 @@ bool StdinCompiler::prepareForCompile()
|
||||||
}
|
}
|
||||||
switch(fileType) {
|
switch(fileType) {
|
||||||
case FileType::CSource:
|
case FileType::CSource:
|
||||||
mArguments += " -x c - ";
|
mArguments += {"-x", "c", "-"};
|
||||||
mArguments += getCCompileArguments(mOnlyCheckSyntax);
|
mArguments += getCCompileArguments(mOnlyCheckSyntax);
|
||||||
mArguments += getCIncludeArguments();
|
mArguments += getCIncludeArguments();
|
||||||
mArguments += getProjectIncludeArguments();
|
mArguments += getProjectIncludeArguments();
|
||||||
|
@ -54,7 +54,7 @@ bool StdinCompiler::prepareForCompile()
|
||||||
mCompiler = compilerSet()->CCompiler();
|
mCompiler = compilerSet()->CCompiler();
|
||||||
break;
|
break;
|
||||||
case FileType::GAS:
|
case FileType::GAS:
|
||||||
mArguments += " -x assembler - ";
|
mArguments += {"-x", "assembler", "-"};
|
||||||
mArguments += getCCompileArguments(mOnlyCheckSyntax);
|
mArguments += getCCompileArguments(mOnlyCheckSyntax);
|
||||||
mArguments += getCIncludeArguments();
|
mArguments += getCIncludeArguments();
|
||||||
mArguments += getProjectIncludeArguments();
|
mArguments += getProjectIncludeArguments();
|
||||||
|
@ -64,7 +64,7 @@ bool StdinCompiler::prepareForCompile()
|
||||||
case FileType::CppSource:
|
case FileType::CppSource:
|
||||||
case FileType::CppHeader:
|
case FileType::CppHeader:
|
||||||
case FileType::CHeader:
|
case FileType::CHeader:
|
||||||
mArguments += " -x c++ - ";
|
mArguments += {"-x", "c++", "-"};
|
||||||
mArguments += getCppCompileArguments(mOnlyCheckSyntax);
|
mArguments += getCppCompileArguments(mOnlyCheckSyntax);
|
||||||
mArguments += getCppIncludeArguments();
|
mArguments += getCppIncludeArguments();
|
||||||
mArguments += getProjectIncludeArguments();
|
mArguments += getProjectIncludeArguments();
|
||||||
|
@ -87,7 +87,8 @@ bool StdinCompiler::prepareForCompile()
|
||||||
log(tr("Processing %1 source file:").arg(strFileType));
|
log(tr("Processing %1 source file:").arg(strFileType));
|
||||||
log("------------------");
|
log("------------------");
|
||||||
log(tr("%1 Compiler: %2").arg(strFileType).arg(mCompiler));
|
log(tr("%1 Compiler: %2").arg(strFileType).arg(mCompiler));
|
||||||
log(tr("Command: %1 %2").arg(extractFileName(mCompiler)).arg(mArguments));
|
QString command = escapeCommandForLog(mCompiler, mArguments);
|
||||||
|
log(tr("Command: %1").arg(command));
|
||||||
mDirectory = extractFileDir(mFilename);
|
mDirectory = extractFileDir(mFilename);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
#include "debugger.h"
|
#include "debugger.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "utils/parsearg.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "editor.h"
|
#include "editor.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
@ -140,7 +141,7 @@ bool Debugger::start(int compilerSetIndex, const QString& inferior, const QStrin
|
||||||
//deleted when thread finished
|
//deleted when thread finished
|
||||||
QStringList params;
|
QStringList params;
|
||||||
if (pSettings->executor().useParams())
|
if (pSettings->executor().useParams())
|
||||||
params = splitProcessCommand(pSettings->executor().params());
|
params = parseArgumentsWithoutVariables(pSettings->executor().params());
|
||||||
mTarget = new DebugTarget(inferior,compilerSet->debugServer(),pSettings->debugger().GDBServerPort(),params);
|
mTarget = new DebugTarget(inferior,compilerSet->debugServer(),pSettings->debugger().GDBServerPort(),params);
|
||||||
if (pSettings->executor().redirectInput())
|
if (pSettings->executor().redirectInput())
|
||||||
mTarget->setInputFile(pSettings->executor().inputFilename());
|
mTarget->setInputFile(pSettings->executor().inputFilename());
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
#include <QLockFile>
|
#include <QLockFile>
|
||||||
|
#include <QFontDatabase>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "colorscheme.h"
|
#include "colorscheme.h"
|
||||||
#include "iconsmanager.h"
|
#include "iconsmanager.h"
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "qsynedit/constants.h"
|
#include "qsynedit/constants.h"
|
||||||
#include "debugger.h"
|
#include "debugger.h"
|
||||||
|
#include "utils/escape.h"
|
||||||
|
#include "utils/parsearg.h"
|
||||||
#include "widgets/cpudialog.h"
|
#include "widgets/cpudialog.h"
|
||||||
#include "widgets/filepropertiesdialog.h"
|
#include "widgets/filepropertiesdialog.h"
|
||||||
#include "widgets/filenameeditdelegate.h"
|
#include "widgets/filenameeditdelegate.h"
|
||||||
|
@ -3376,28 +3378,29 @@ void MainWindow::updateTools()
|
||||||
QAction* action = new QAction(item->title,ui->menuTools);
|
QAction* action = new QAction(item->title,ui->menuTools);
|
||||||
connect(action, &QAction::triggered,
|
connect(action, &QAction::triggered,
|
||||||
[item] (){
|
[item] (){
|
||||||
QString program = parseMacros(item->program);
|
QMap<QString, QString> macros = devCppMacroVariables();
|
||||||
QString workDir = parseMacros(item->workingDirectory);
|
QString program = parseMacros(item->program, macros);
|
||||||
QString params = parseMacros(item->parameters);
|
QString workDir = parseMacros(item->workingDirectory, macros);
|
||||||
|
QStringList params = parseArguments(item->parameters, macros, true);
|
||||||
if (!program.endsWith(".bat",Qt::CaseInsensitive)) {
|
if (!program.endsWith(".bat",Qt::CaseInsensitive)) {
|
||||||
QTemporaryFile file(QDir::tempPath()+QDir::separator()+"XXXXXX.bat");
|
QTemporaryFile file(QDir::tempPath()+QDir::separator()+"XXXXXX.bat");
|
||||||
file.setAutoRemove(false);
|
file.setAutoRemove(false);
|
||||||
if (file.open()) {
|
if (file.open()) {
|
||||||
file.write(QString("cd /d \"%1\"")
|
file.write(escapeCommandForPlatformShell(
|
||||||
.arg(localizePath(workDir))
|
"cd", {"/d", localizePath(workDir)}
|
||||||
.toLocal8Bit()+LINE_BREAKER);
|
).toLocal8Bit() + LINE_BREAKER);
|
||||||
file.write((program+" "+params).toLocal8Bit()
|
file.write(escapeCommandForPlatformShell(program, params).toLocal8Bit()
|
||||||
+ LINE_BREAKER);
|
+ LINE_BREAKER);
|
||||||
file.close();
|
file.close();
|
||||||
if (item->pauseAfterExit) {
|
if (item->pauseAfterExit) {
|
||||||
executeFile(
|
executeFile(
|
||||||
includeTrailingPathDelimiter(pSettings->dirs().appLibexecDir())+CONSOLE_PAUSER,
|
includeTrailingPathDelimiter(pSettings->dirs().appLibexecDir())+CONSOLE_PAUSER,
|
||||||
" 1 \""+localizePath(file.fileName())+"\" ",
|
{"1", localizePath(file.fileName())},
|
||||||
workDir, file.fileName());
|
workDir, file.fileName());
|
||||||
} else {
|
} else {
|
||||||
executeFile(
|
executeFile(
|
||||||
file.fileName(),
|
file.fileName(),
|
||||||
"",
|
{},
|
||||||
workDir, file.fileName());
|
workDir, file.fileName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3405,7 +3408,7 @@ void MainWindow::updateTools()
|
||||||
if (item->pauseAfterExit) {
|
if (item->pauseAfterExit) {
|
||||||
executeFile(
|
executeFile(
|
||||||
includeTrailingPathDelimiter(pSettings->dirs().appLibexecDir())+CONSOLE_PAUSER,
|
includeTrailingPathDelimiter(pSettings->dirs().appLibexecDir())+CONSOLE_PAUSER,
|
||||||
" 1 \""+program+"\" "+params,
|
QStringList{"1", program} + params,
|
||||||
workDir, "");
|
workDir, "");
|
||||||
} else {
|
} else {
|
||||||
executeFile(
|
executeFile(
|
||||||
|
|
|
@ -1423,11 +1423,7 @@ void Project::buildPrivateResource()
|
||||||
if (
|
if (
|
||||||
(getFileType(unit->fileName()) == FileType::WindowsResourceSource)
|
(getFileType(unit->fileName()) == FileType::WindowsResourceSource)
|
||||||
&& unit->compile() )
|
&& unit->compile() )
|
||||||
contents.append("#include \"" +
|
contents.append("#include \"" + extractRelativePath(directory(), unit->fileName()) + "\"");
|
||||||
genMakePath(
|
|
||||||
extractRelativePath(directory(), unit->fileName()),
|
|
||||||
false,
|
|
||||||
false) + "\"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mOptions.icon.isEmpty()) {
|
if (!mOptions.icon.isEmpty()) {
|
||||||
|
@ -1450,11 +1446,8 @@ void Project::buildPrivateResource()
|
||||||
contents.append("//");
|
contents.append("//");
|
||||||
if (!mOptions.exeOutput.isEmpty())
|
if (!mOptions.exeOutput.isEmpty())
|
||||||
contents.append(
|
contents.append(
|
||||||
"1 24 \"" +
|
"1 24 \"" + includeTrailingPathDelimiter(mOptions.exeOutput)
|
||||||
genMakePath2(
|
+ extractFileName(executable()) + ".Manifest\"");
|
||||||
includeTrailingPathDelimiter(mOptions.exeOutput)
|
|
||||||
+ extractFileName(executable()))
|
|
||||||
+ ".Manifest\"");
|
|
||||||
else
|
else
|
||||||
contents.append("1 24 \"" + extractFileName(executable()) + ".Manifest\"");
|
contents.append("1 24 \"" + extractFileName(executable()) + ".Manifest\"");
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
{
|
{
|
||||||
"name": "CoreTerminal",
|
"name": "CoreTerminal",
|
||||||
"path": "coreterminal",
|
"path": "coreterminal",
|
||||||
"argsPattern": "$term -e \"$command\"",
|
"argsPattern": "$term -e \"$unix_command\"",
|
||||||
"comment": "The pair of quotation mark around `$command` is only a visual symbol, not actually required."
|
"comment": "The pair of quotation mark around `$unix_command` is only a visual symbol, not actually required."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "iTerm2",
|
"name": "iTerm2",
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
{
|
{
|
||||||
"name": "kermit",
|
"name": "kermit",
|
||||||
"path": "kermit",
|
"path": "kermit",
|
||||||
"argsPattern": "$term -e \"$command\""
|
"argsPattern": "$term -e \"$unix_command\""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Kitty",
|
"name": "Kitty",
|
||||||
|
@ -36,12 +36,12 @@
|
||||||
{
|
{
|
||||||
"name": "ROXTerm",
|
"name": "ROXTerm",
|
||||||
"path": "roxterm",
|
"path": "roxterm",
|
||||||
"argsPattern": "$term -e \"$command\""
|
"argsPattern": "$term -e \"$unix_command\""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sakura",
|
"name": "sakura",
|
||||||
"path": "sakura",
|
"path": "sakura",
|
||||||
"argsPattern": "$term -e \"$command\""
|
"argsPattern": "$term -e \"$unix_command\""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Termit",
|
"name": "Termit",
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
{
|
{
|
||||||
"name": "Termite",
|
"name": "Termite",
|
||||||
"path": "termite",
|
"path": "termite",
|
||||||
"argsPattern": "$term -e \"$command\""
|
"argsPattern": "$term -e \"$unix_command\""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Tilix",
|
"name": "Tilix",
|
||||||
|
@ -116,7 +116,7 @@
|
||||||
{
|
{
|
||||||
"name": "Terminology (Enlightenment)",
|
"name": "Terminology (Enlightenment)",
|
||||||
"path": "terminology",
|
"path": "terminology",
|
||||||
"argsPattern": "$term -e \"$command\""
|
"argsPattern": "$term -e \"$unix_command\""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Xfce Terminal",
|
"name": "Xfce Terminal",
|
||||||
|
@ -131,7 +131,7 @@
|
||||||
{
|
{
|
||||||
"name": "Elementary Terminal",
|
"name": "Elementary Terminal",
|
||||||
"path": "io.elementary.terminal",
|
"path": "io.elementary.terminal",
|
||||||
"argsPattern": "$term -e \"$command\"",
|
"argsPattern": "$term -e \"$unix_command\"",
|
||||||
"comment": "confirm to quit"
|
"comment": "confirm to quit"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
#include <QTextCodec>
|
#include <QTextCodec>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "utils/escape.h"
|
||||||
|
#include "utils/parsearg.h"
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include "systemconsts.h"
|
#include "systemconsts.h"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
@ -1934,14 +1936,14 @@ Settings::CompilerSet::CompilerSet(const QJsonObject &set) :
|
||||||
mFullLoaded = false;
|
mFullLoaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList escapedCompileParams;
|
QStringList compileParams;
|
||||||
for (const QJsonValue ¶m : set["customCompileParams"].toArray())
|
for (const QJsonValue ¶m : set["customCompileParams"].toArray())
|
||||||
escapedCompileParams.append(escapeArgument(param.toString(), false));
|
compileParams << param.toString();
|
||||||
mCustomCompileParams = escapedCompileParams.join(' ');
|
mCustomCompileParams = escapeArgumentsForInputField(compileParams);
|
||||||
QStringList escapedLinkParams;
|
QStringList linkParams;
|
||||||
for (const QJsonValue ¶m : set["customLinkParams"].toArray())
|
for (const QJsonValue ¶m : set["customLinkParams"].toArray())
|
||||||
escapedLinkParams.append(escapeArgument(param.toString(), false));
|
linkParams << param.toString();
|
||||||
mCustomLinkParams = escapedLinkParams.join(' ');
|
mCustomLinkParams = escapeArgumentsForInputField(linkParams);
|
||||||
|
|
||||||
if (!mAutoAddCharsetParams)
|
if (!mAutoAddCharsetParams)
|
||||||
mExecCharset = "UTF-8";
|
mExecCharset = "UTF-8";
|
||||||
|
@ -2597,7 +2599,7 @@ QStringList Settings::CompilerSet::defines(bool isCpp) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (mUseCustomCompileParams) {
|
if (mUseCustomCompileParams) {
|
||||||
QStringList extraParams = splitProcessCommand(mCustomCompileParams);
|
QStringList extraParams = parseArgumentsWithoutVariables(mCustomCompileParams);
|
||||||
arguments.append(extraParams);
|
arguments.append(extraParams);
|
||||||
}
|
}
|
||||||
arguments.append(NULL_FILE);
|
arguments.append(NULL_FILE);
|
||||||
|
@ -4125,6 +4127,11 @@ void Settings::Environment::checkAndSetTerminal()
|
||||||
QCoreApplication::translate("Settings","Can't find terminal program!"));
|
QCoreApplication::translate("Settings","Can't find terminal program!"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QMap<QString, QString> Settings::Environment::terminalArgsPatternMagicVariables()
|
||||||
|
{
|
||||||
|
return mTerminalArgsPatternMagicVariables;
|
||||||
|
}
|
||||||
|
|
||||||
QList<Settings::Environment::TerminalItem> Settings::Environment::loadTerminalList() const
|
QList<Settings::Environment::TerminalItem> Settings::Environment::loadTerminalList() const
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
|
@ -4165,7 +4172,7 @@ bool Settings::Environment::isTerminalValid()
|
||||||
// terminal patter is empty
|
// terminal patter is empty
|
||||||
if (mTerminalArgumentsPattern.isEmpty()) return false;
|
if (mTerminalArgumentsPattern.isEmpty()) return false;
|
||||||
|
|
||||||
QStringList patternItems = splitProcessCommand(mTerminalArgumentsPattern);
|
QStringList patternItems = parseArguments(mTerminalArgumentsPattern, mTerminalArgsPatternMagicVariables, false);
|
||||||
|
|
||||||
if (!(patternItems.contains("$argv")
|
if (!(patternItems.contains("$argv")
|
||||||
|| patternItems.contains("$command")
|
|| patternItems.contains("$command")
|
||||||
|
@ -4242,6 +4249,17 @@ void Settings::Environment::setTheme(const QString &theme)
|
||||||
mTheme = theme;
|
mTheme = theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QMap<QString, QString> Settings::Environment::mTerminalArgsPatternMagicVariables = {
|
||||||
|
{"term", "$term"},
|
||||||
|
{"integrated_term", "$integrated_term"},
|
||||||
|
{"argv", "$argv"},
|
||||||
|
{"command", "$command"},
|
||||||
|
{"unix_command", "$unix_command"},
|
||||||
|
{"dos_command", "$dos_command"},
|
||||||
|
{"lpCommandLine", "$lpCommandLine"},
|
||||||
|
{"tmpfile", "$tmpfile"},
|
||||||
|
};
|
||||||
|
|
||||||
Settings::Executor::Executor(Settings *settings):_Base(settings, SETTING_EXECUTOR)
|
Settings::Executor::Executor(Settings *settings):_Base(settings, SETTING_EXECUTOR)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -6708,21 +6726,39 @@ std::tuple<QString, QStringList, std::unique_ptr<QTemporaryFile>> wrapCommandFor
|
||||||
wrappedArgs.append(includeTrailingPathDelimiter(pSettings->dirs().appDir())+terminal);
|
wrappedArgs.append(includeTrailingPathDelimiter(pSettings->dirs().appDir())+terminal);
|
||||||
else if (patternItem == "$argv")
|
else if (patternItem == "$argv")
|
||||||
wrappedArgs.append(payloadArgsWithArgv0);
|
wrappedArgs.append(payloadArgsWithArgv0);
|
||||||
else if (patternItem == "$command") {
|
else if (patternItem == "$command" || patternItem == "$unix_command") {
|
||||||
|
// “$command” is for compatibility; previously used on multiple Unix terms
|
||||||
QStringList escapedArgs;
|
QStringList escapedArgs;
|
||||||
for (int i = 0; i < payloadArgsWithArgv0.length(); i++) {
|
for (int i = 0; i < payloadArgsWithArgv0.length(); i++) {
|
||||||
auto &arg = payloadArgsWithArgv0[i];
|
auto &arg = payloadArgsWithArgv0[i];
|
||||||
auto escaped = escapeArgument(arg, i == 0);
|
auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::BourneAgainShellPretty);
|
||||||
escapedArgs.append(escaped);
|
escapedArgs.append(escaped);
|
||||||
}
|
}
|
||||||
wrappedArgs.push_back(escapedArgs.join(' '));
|
wrappedArgs.push_back(escapedArgs.join(' '));
|
||||||
} else if (patternItem == "$tmpfile") {
|
} else if (patternItem == "$dos_command") {
|
||||||
|
QStringList escapedArgs;
|
||||||
|
for (int i = 0; i < payloadArgsWithArgv0.length(); i++) {
|
||||||
|
auto &arg = payloadArgsWithArgv0[i];
|
||||||
|
auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::WindowsCommandPrompt);
|
||||||
|
escapedArgs.append(escaped);
|
||||||
|
}
|
||||||
|
wrappedArgs.push_back(escapedArgs.join(' '));
|
||||||
|
} else if (patternItem == "$lpCommandLine") {
|
||||||
|
QStringList escapedArgs;
|
||||||
|
for (int i = 0; i < payloadArgsWithArgv0.length(); i++) {
|
||||||
|
auto &arg = payloadArgsWithArgv0[i];
|
||||||
|
auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::WindowsCreateProcess);
|
||||||
|
escapedArgs.append(escaped);
|
||||||
|
}
|
||||||
|
wrappedArgs.push_back(escapedArgs.join(' '));
|
||||||
|
} else if (patternItem == "$tmpfile" || patternItem == "$tmpfile.command") {
|
||||||
|
// “$tmpfile” is for compatibility; previously used on macOS Terminal.app
|
||||||
temproryFile = std::make_unique<QTemporaryFile>(QDir::tempPath() + "/redpanda_XXXXXX.command");
|
temproryFile = std::make_unique<QTemporaryFile>(QDir::tempPath() + "/redpanda_XXXXXX.command");
|
||||||
if (temproryFile->open()) {
|
if (temproryFile->open()) {
|
||||||
QStringList escapedArgs;
|
QStringList escapedArgs;
|
||||||
for (int i = 0; i < payloadArgsWithArgv0.length(); i++) {
|
for (int i = 0; i < payloadArgsWithArgv0.length(); i++) {
|
||||||
auto &arg = payloadArgsWithArgv0[i];
|
auto &arg = payloadArgsWithArgv0[i];
|
||||||
auto escaped = escapeArgument(arg, i == 0);
|
auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::BourneAgainShellPretty);
|
||||||
escapedArgs.append(escaped);
|
escapedArgs.append(escaped);
|
||||||
}
|
}
|
||||||
temproryFile->write(escapedArgs.join(' ').toUtf8());
|
temproryFile->write(escapedArgs.join(' ').toUtf8());
|
||||||
|
@ -6731,6 +6767,21 @@ std::tuple<QString, QStringList, std::unique_ptr<QTemporaryFile>> wrapCommandFor
|
||||||
QFile(temproryFile->fileName()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner);
|
QFile(temproryFile->fileName()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner);
|
||||||
}
|
}
|
||||||
wrappedArgs.push_back(temproryFile->fileName());
|
wrappedArgs.push_back(temproryFile->fileName());
|
||||||
|
} else if (patternItem == "$tmpfile.bat") {
|
||||||
|
temproryFile = std::make_unique<QTemporaryFile>(QDir::tempPath() + "/redpanda_XXXXXX.bat");
|
||||||
|
if (temproryFile->open()) {
|
||||||
|
QStringList escapedArgs;
|
||||||
|
for (int i = 0; i < payloadArgsWithArgv0.length(); i++) {
|
||||||
|
auto &arg = payloadArgsWithArgv0[i];
|
||||||
|
auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::WindowsCommandPrompt);
|
||||||
|
escapedArgs.append(escaped);
|
||||||
|
}
|
||||||
|
temproryFile->write(escapedArgs.join(' ').toLocal8Bit());
|
||||||
|
temproryFile->write("\r\n");
|
||||||
|
temproryFile->flush();
|
||||||
|
QFile(temproryFile->fileName()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner);
|
||||||
|
}
|
||||||
|
wrappedArgs.push_back(temproryFile->fileName());
|
||||||
} else
|
} else
|
||||||
wrappedArgs.push_back(patternItem);
|
wrappedArgs.push_back(patternItem);
|
||||||
}
|
}
|
||||||
|
@ -6741,5 +6792,5 @@ std::tuple<QString, QStringList, std::unique_ptr<QTemporaryFile>> wrapCommandFor
|
||||||
|
|
||||||
std::tuple<QString, QStringList, std::unique_ptr<QTemporaryFile>> wrapCommandForTerminalEmulator(const QString &terminal, const QString &argsPattern, const QStringList &payloadArgsWithArgv0)
|
std::tuple<QString, QStringList, std::unique_ptr<QTemporaryFile>> wrapCommandForTerminalEmulator(const QString &terminal, const QString &argsPattern, const QStringList &payloadArgsWithArgv0)
|
||||||
{
|
{
|
||||||
return wrapCommandForTerminalEmulator(terminal, splitProcessCommand(argsPattern), payloadArgsWithArgv0);
|
return wrapCommandForTerminalEmulator(terminal, parseArguments(argsPattern, Settings::Environment::terminalArgsPatternMagicVariables(), false), payloadArgsWithArgv0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -631,6 +631,8 @@ public:
|
||||||
|
|
||||||
QList<TerminalItem> loadTerminalList() const;
|
QList<TerminalItem> loadTerminalList() const;
|
||||||
|
|
||||||
|
static QMap<QString, QString> terminalArgsPatternMagicVariables();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isTerminalValid();
|
bool isTerminalValid();
|
||||||
void checkAndSetTerminal();
|
void checkAndSetTerminal();
|
||||||
|
@ -653,6 +655,8 @@ public:
|
||||||
bool mUseCustomTerminal;
|
bool mUseCustomTerminal;
|
||||||
bool mHideNonSupportFilesInFileView;
|
bool mHideNonSupportFilesInFileView;
|
||||||
bool mOpenFilesInSingleInstance;
|
bool mOpenFilesInSingleInstance;
|
||||||
|
|
||||||
|
static const QMap<QString, QString> mTerminalArgsPatternMagicVariables;
|
||||||
// _Base interface
|
// _Base interface
|
||||||
protected:
|
protected:
|
||||||
void doSave() override;
|
void doSave() override;
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "../iconsmanager.h"
|
#include "../iconsmanager.h"
|
||||||
#include "../systemconsts.h"
|
#include "../systemconsts.h"
|
||||||
#include "../compiler/executablerunner.h"
|
#include "../compiler/executablerunner.h"
|
||||||
|
#include "utils/escape.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
@ -43,18 +44,16 @@ EnvironmentProgramsWidget::~EnvironmentProgramsWidget()
|
||||||
auto EnvironmentProgramsWidget::resolveExecArguments(const QString &terminalPath, const QString &argsPattern)
|
auto EnvironmentProgramsWidget::resolveExecArguments(const QString &terminalPath, const QString &argsPattern)
|
||||||
-> std::tuple<QString, QStringList, std::unique_ptr<QTemporaryFile>>
|
-> std::tuple<QString, QStringList, std::unique_ptr<QTemporaryFile>>
|
||||||
{
|
{
|
||||||
QString shell = defaultShell();
|
return wrapCommandForTerminalEmulator(terminalPath, argsPattern, platformCommandForTerminalArgsPreview());
|
||||||
QStringList payloadArgs{shell, "-c", "echo hello; sleep 3"};
|
|
||||||
return wrapCommandForTerminalEmulator(terminalPath, argsPattern, payloadArgs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnvironmentProgramsWidget::updateCommandPreview(const QString &terminalPath, const QString &argsPattern)
|
void EnvironmentProgramsWidget::updateCommandPreview(const QString &terminalPath, const QString &argsPattern)
|
||||||
{
|
{
|
||||||
auto [filename, arguments, fileOwner] = resolveExecArguments(terminalPath, argsPattern);
|
auto [filename, arguments, fileOwner] = resolveExecArguments(terminalPath, argsPattern);
|
||||||
for (auto &arg : arguments)
|
for (auto &arg : arguments)
|
||||||
arg = escapeArgument(arg, false);
|
arg = escapeArgument(arg, false, platformShellEscapeArgumentRule());
|
||||||
|
|
||||||
ui->labelCmdPreviewResult->setPlainText(escapeArgument(filename, true) + " " + arguments.join(' '));
|
ui->labelCmdPreviewResult->setPlainText(escapeArgument(filename, true, platformShellEscapeArgumentRule()) + " " + arguments.join(' '));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnvironmentProgramsWidget::autoDetectAndUpdateArgumentsPattern(const QString &terminalPath)
|
void EnvironmentProgramsWidget::autoDetectAndUpdateArgumentsPattern(const QString &terminalPath)
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "../settings.h"
|
#include "../settings.h"
|
||||||
#include "../iconsmanager.h"
|
#include "../iconsmanager.h"
|
||||||
#include "../systemconsts.h"
|
#include "../systemconsts.h"
|
||||||
|
#include "utils/parsearg.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
@ -90,7 +91,7 @@ void ExecutorGeneralWidget::updateIcons(const QSize &/*size*/)
|
||||||
|
|
||||||
void ExecutorGeneralWidget::on_txtExecuteParamaters_textChanged(const QString &commandLine)
|
void ExecutorGeneralWidget::on_txtExecuteParamaters_textChanged(const QString &commandLine)
|
||||||
{
|
{
|
||||||
QStringList parsed = splitProcessCommand(commandLine);
|
QStringList parsed = parseArgumentsWithoutVariables(commandLine);
|
||||||
QJsonArray obj = QJsonArray::fromStringList(parsed);
|
QJsonArray obj = QJsonArray::fromStringList(parsed);
|
||||||
ui->txtParsedArgsInJson->setText(QJsonDocument{obj}.toJson());
|
ui->txtParsedArgsInJson->setText(QJsonDocument{obj}.toJson());
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
#include "../mainwindow.h"
|
#include "../mainwindow.h"
|
||||||
#include "../project.h"
|
#include "../project.h"
|
||||||
#include "../iconsmanager.h"
|
#include "../iconsmanager.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "utils/escape.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include <sysinfoapi.h>
|
#include <sysinfoapi.h>
|
||||||
|
@ -83,7 +85,7 @@ void ProjectCompileParamatersWidget::on_btnChooseLib_clicked()
|
||||||
);
|
);
|
||||||
if (!files.isEmpty()) {
|
if (!files.isEmpty()) {
|
||||||
foreach (const QString& file,files) {
|
foreach (const QString& file,files) {
|
||||||
ui->txtLinker->appendPlainText(" "+genMakePath1(file));
|
ui->txtLinker->appendPlainText(" " + escapeArgument(file, false, EscapeArgumentRule::BourneAgainShellPretty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
#include "../mainwindow.h"
|
#include "../mainwindow.h"
|
||||||
#include "../settings.h"
|
#include "../settings.h"
|
||||||
#include "../iconsmanager.h"
|
#include "../iconsmanager.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "utils/escape.h"
|
||||||
|
#include "utils/parsearg.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
@ -132,9 +135,11 @@ void ToolsGeneralWidget::onEdited()
|
||||||
|
|
||||||
void ToolsGeneralWidget::updateDemo()
|
void ToolsGeneralWidget::updateDemo()
|
||||||
{
|
{
|
||||||
ui->txtDemo->setText(
|
QMap<QString,QString> macros = devCppMacroVariables();
|
||||||
parseMacros(ui->txtProgram->text())+ " " +
|
ui->txtDemo->setText(escapeCommandForPlatformShell(
|
||||||
parseMacros(ui->txtParameters->text()));
|
parseMacros(ui->txtProgram->text(), macros),
|
||||||
|
parseArguments(ui->txtParameters->text(), macros, true)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolsModel::ToolsModel(QObject *parent):QAbstractListModel(parent)
|
ToolsModel::ToolsModel(QObject *parent):QAbstractListModel(parent)
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "utils/escape.h"
|
||||||
|
|
||||||
|
int testIndex = 0;
|
||||||
|
|
||||||
|
QByteArray content = "main(){}";
|
||||||
|
|
||||||
|
void testMake(QString name)
|
||||||
|
{
|
||||||
|
++testIndex;
|
||||||
|
auto dir = QString("test-escape-%1").arg(testIndex);
|
||||||
|
auto srcName = name + ".c";
|
||||||
|
auto objName = name + ".o";
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
auto binName = name + ".exe";
|
||||||
|
#else
|
||||||
|
auto binName = name;
|
||||||
|
#endif
|
||||||
|
auto includeMfName = name + ".mf";
|
||||||
|
|
||||||
|
auto fail = [&name](const QString& msg) {
|
||||||
|
qDebug() << "Error in test" << testIndex << name << ":" << msg;
|
||||||
|
exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// create directory
|
||||||
|
{
|
||||||
|
auto cwd = QDir();
|
||||||
|
if (cwd.exists(dir)) {
|
||||||
|
if (!QDir(dir).removeRecursively())
|
||||||
|
fail("cannot remove directory");
|
||||||
|
}
|
||||||
|
if (!cwd.mkdir(dir))
|
||||||
|
fail("cannot create directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
// create source file
|
||||||
|
{
|
||||||
|
auto srcPath = QString("%1/%2").arg(dir, srcName);
|
||||||
|
QFile f(srcPath);
|
||||||
|
if (!f.open(QIODevice::WriteOnly))
|
||||||
|
fail("cannot create source file");
|
||||||
|
if (f.write(content) != content.size())
|
||||||
|
fail("cannot write to source file");
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// create included makefile
|
||||||
|
{
|
||||||
|
auto file = QString("%1/%2").arg(dir, includeMfName);
|
||||||
|
QFile f(file);
|
||||||
|
if (!f.open(QIODevice::WriteOnly))
|
||||||
|
fail("cannot create included makefile");
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// create makefile
|
||||||
|
{
|
||||||
|
QStringList mf;
|
||||||
|
mf << "BIN_DEP = " + escapeFilenameForMakefilePrerequisite(binName);
|
||||||
|
mf << "BIN_TAR = " + escapeFilenameForMakefileTarget(binName);
|
||||||
|
mf << "BIN_ARG = " + escapeArgumentForMakefileVariableValue(binName, false);
|
||||||
|
mf << "OBJS_DEP = " + escapeFilenameForMakefilePrerequisite(objName);
|
||||||
|
mf << "OBJS_ARG = " + escapeArgumentForMakefileVariableValue(objName, false);
|
||||||
|
mf << "include " + escapeFilenameForMakefileInclude(includeMfName);
|
||||||
|
mf << ".PHONY: all clean";
|
||||||
|
mf << "all: $(BIN_DEP)";
|
||||||
|
mf << "$(BIN_TAR): $(OBJS_DEP)";
|
||||||
|
mf << "\tgcc -o $(BIN_ARG) $(OBJS_ARG)";
|
||||||
|
mf << escapeFilenameForMakefileTarget(objName) + ": " + escapeFilenameForMakefilePrerequisite(srcName);
|
||||||
|
mf << "\tgcc -o " + escapeArgumentForMakefileRecipe(objName, false) + " -c " + escapeArgumentForMakefileRecipe(srcName, false);
|
||||||
|
mf << "clean:";
|
||||||
|
mf << "\trm -f $(BIN_ARG) $(OBJS_ARG)";
|
||||||
|
|
||||||
|
auto file = QString("%1/makefile").arg(dir);
|
||||||
|
QFile f(file);
|
||||||
|
if (!f.open(QIODevice::WriteOnly))
|
||||||
|
fail("cannot create makefile");
|
||||||
|
f.write(mf.join("\n").toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
// run make
|
||||||
|
{
|
||||||
|
QProcess p;
|
||||||
|
p.setWorkingDirectory(dir);
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
p.setProgram("mingw32-make.exe");
|
||||||
|
#else
|
||||||
|
p.setProgram("make");
|
||||||
|
#endif
|
||||||
|
p.start();
|
||||||
|
p.waitForFinished();
|
||||||
|
if (p.exitCode() != 0) {
|
||||||
|
qDebug() << "Error in test" << testIndex << name << ": make failed";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
auto binFile = QString("%1/%2").arg(dir, binName);
|
||||||
|
if (!QFile(binFile).exists()) {
|
||||||
|
qDebug() << "Error in test" << testIndex << name << ": executable not properly created";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
testMake("simple");
|
||||||
|
testMake("dollar$dollar");
|
||||||
|
testMake("paren(paren");
|
||||||
|
testMake("paren)paren");
|
||||||
|
testMake("pair(of)paren");
|
||||||
|
testMake("bracket[bracket");
|
||||||
|
testMake("bracket]bracket");
|
||||||
|
testMake("pair[of]brackets");
|
||||||
|
testMake("brace{brace");
|
||||||
|
testMake("brace}brace");
|
||||||
|
testMake("pair{of}braces");
|
||||||
|
testMake("hash#hash");
|
||||||
|
testMake("percent%percent");
|
||||||
|
testMake("ampersand&ersand");
|
||||||
|
testMake("space space");
|
||||||
|
testMake("quote'quote");
|
||||||
|
testMake("complex$(complex)complex");
|
||||||
|
testMake("complex${complex}complex");
|
||||||
|
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
testMake("colon:colon");
|
||||||
|
testMake("less<less");
|
||||||
|
testMake("greater>greater");
|
||||||
|
testMake("pair<of>angle");
|
||||||
|
testMake("asterisk*asterisk");
|
||||||
|
testMake("question?question");
|
||||||
|
testMake("escape\033escape");
|
||||||
|
testMake(R"(quote"quote)");
|
||||||
|
testMake(R"(backslash\backslash)");
|
||||||
|
testMake(R"(complex\#complex)");
|
||||||
|
testMake(R"(complex\ complex)");
|
||||||
|
testMake(R"(weird\)");
|
||||||
|
testMake(R"(weird\\)");
|
||||||
|
testMake(R"(weird\\\)");
|
||||||
|
testMake(R"(weird\\\\)");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// seems impossible:
|
||||||
|
// testMake("tab\ttab");
|
||||||
|
// testMake("newline\nnewline");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -4,6 +4,8 @@
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
|
#include <QSysInfo>
|
||||||
|
#include <QVersionNumber>
|
||||||
#include "editor.h"
|
#include "editor.h"
|
||||||
#include "editorlist.h"
|
#include "editorlist.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
@ -23,72 +25,6 @@ using pIsWow64Process2_t = BOOL (WINAPI *)(
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QStringList splitProcessCommand(const QString &cmd)
|
|
||||||
{
|
|
||||||
QStringList result;
|
|
||||||
SplitProcessCommandQuoteType quoteType = SplitProcessCommandQuoteType::None;
|
|
||||||
int i=0;
|
|
||||||
QString current;
|
|
||||||
while (i<cmd.length()) {
|
|
||||||
switch (cmd[i].unicode()) {
|
|
||||||
case ' ':
|
|
||||||
case '\t':
|
|
||||||
case '\r':
|
|
||||||
case '\n':
|
|
||||||
if (quoteType == SplitProcessCommandQuoteType::None) {
|
|
||||||
if (!current.isEmpty()) {
|
|
||||||
result.append(current);
|
|
||||||
}
|
|
||||||
current = "";
|
|
||||||
} else {
|
|
||||||
current += cmd[i];
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
break;
|
|
||||||
case '\"':
|
|
||||||
switch(quoteType) {
|
|
||||||
case SplitProcessCommandQuoteType::None:
|
|
||||||
quoteType = SplitProcessCommandQuoteType::Double;
|
|
||||||
break;
|
|
||||||
case SplitProcessCommandQuoteType::Double:
|
|
||||||
quoteType = SplitProcessCommandQuoteType::None;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
current+=cmd[i];
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
break;
|
|
||||||
case '\'':
|
|
||||||
switch(quoteType) {
|
|
||||||
case SplitProcessCommandQuoteType::None:
|
|
||||||
quoteType = SplitProcessCommandQuoteType::Single;
|
|
||||||
break;
|
|
||||||
case SplitProcessCommandQuoteType::Single:
|
|
||||||
quoteType = SplitProcessCommandQuoteType::None;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
current+=cmd[i];
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
break;
|
|
||||||
case '\\':
|
|
||||||
current += cmd[i];
|
|
||||||
i++;
|
|
||||||
if (i<cmd.length()) {
|
|
||||||
current += cmd[i];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
current += cmd[i];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!current.isEmpty())
|
|
||||||
result.append(current);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileType getFileType(const QString &filename)
|
FileType getFileType(const QString &filename)
|
||||||
{
|
{
|
||||||
if (filename.endsWith(".s",PATH_SENSITIVITY)) {
|
if (filename.endsWith(".s",PATH_SENSITIVITY)) {
|
||||||
|
@ -176,31 +112,6 @@ FileType getFileType(const QString &filename)
|
||||||
return FileType::Other;
|
return FileType::Other;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString genMakePath(const QString &fileName, bool escapeSpaces, bool encloseInQuotes)
|
|
||||||
{
|
|
||||||
QString result = fileName;
|
|
||||||
|
|
||||||
// Convert backslashes to slashes
|
|
||||||
result.replace('\\','/');
|
|
||||||
if (escapeSpaces) {
|
|
||||||
result.replace(' ',"\\ ");
|
|
||||||
}
|
|
||||||
if (encloseInQuotes)
|
|
||||||
if (result.contains(' '))
|
|
||||||
result = '"'+result+'"';
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString genMakePath1(const QString &fileName)
|
|
||||||
{
|
|
||||||
return genMakePath(fileName, false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString genMakePath2(const QString &fileName)
|
|
||||||
{
|
|
||||||
return genMakePath(fileName, true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool programHasConsole(const QString & filename)
|
bool programHasConsole(const QString & filename)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
@ -229,33 +140,47 @@ bool programHasConsole(const QString & filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
QString parseMacros(const QString &s)
|
QString parseMacros(const QString &s)
|
||||||
|
{
|
||||||
|
return parseMacros(s, devCppMacroVariables());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString parseMacros(const QString &s, const QMap<QString, QString> ¯os)
|
||||||
{
|
{
|
||||||
QString result = s;
|
QString result = s;
|
||||||
|
for (auto it = macros.begin(); it != macros.end(); ++it) {
|
||||||
|
QString key = it.key();
|
||||||
|
QString value = it.value();
|
||||||
|
result.replace('<' + key + '>', value);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, QString> devCppMacroVariables()
|
||||||
|
{
|
||||||
Editor *e = pMainWindow->editorList()->getEditor();
|
Editor *e = pMainWindow->editorList()->getEditor();
|
||||||
|
|
||||||
result.replace("<DEFAULT>", localizePath(QDir::currentPath()));
|
QMap<QString, QString> result = {
|
||||||
result.replace("<DEVCPP>", localizePath(pSettings->dirs().executable()));
|
{"DEFAULT", localizePath(QDir::currentPath())},
|
||||||
result.replace("<DEVCPPVERSION>", REDPANDA_CPP_VERSION);
|
{"DEVCPP", localizePath(pSettings->dirs().executable())},
|
||||||
result.replace("<EXECPATH>", localizePath(pSettings->dirs().appDir()));
|
{"DEVCPPVERSION", REDPANDA_CPP_VERSION},
|
||||||
QDate today = QDate::currentDate();
|
{"EXECPATH", localizePath(pSettings->dirs().appDir())},
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
{"DATE", QDate::currentDate().toString("yyyy-MM-dd")},
|
||||||
|
{"DATETIME", QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")}
|
||||||
result.replace("<DATE>", today.toString("yyyy-MM-dd"));
|
};
|
||||||
result.replace("<DATETIME>", now.toString("yyyy-MM-dd hh:mm:ss"));
|
|
||||||
|
|
||||||
Settings::PCompilerSet compilerSet = pSettings->compilerSets().defaultSet();
|
Settings::PCompilerSet compilerSet = pSettings->compilerSets().defaultSet();
|
||||||
if (compilerSet) {
|
if (compilerSet) {
|
||||||
// Only provide the first cpp include dir
|
// Only provide the first cpp include dir
|
||||||
if (compilerSet->defaultCppIncludeDirs().count() > 0)
|
if (compilerSet->defaultCppIncludeDirs().count() > 0)
|
||||||
result.replace("<INCLUDE>", localizePath(compilerSet->defaultCppIncludeDirs().front()));
|
result["INCLUDE"] = localizePath(compilerSet->defaultCppIncludeDirs().front());
|
||||||
else
|
else
|
||||||
result.replace("<INCLUDE>","");
|
result["INCLUDE"] = "";
|
||||||
|
|
||||||
// Only provide the first lib dir
|
// Only provide the first lib dir
|
||||||
if (compilerSet->defaultLibDirs().count() > 0)
|
if (compilerSet->defaultLibDirs().count() > 0)
|
||||||
result.replace("<LIB>", localizePath(compilerSet->defaultLibDirs().front()));
|
result["LIB"] = localizePath(compilerSet->defaultLibDirs().front());
|
||||||
else
|
else
|
||||||
result.replace("<LIB>","");
|
result["LIB"] = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e != nullptr && !e->inProject()) { // Non-project editor macros
|
if (e != nullptr && !e->inProject()) { // Non-project editor macros
|
||||||
|
@ -266,40 +191,41 @@ QString parseMacros(const QString &s)
|
||||||
} else {
|
} else {
|
||||||
exeSuffix = DEFAULT_EXECUTABLE_SUFFIX;
|
exeSuffix = DEFAULT_EXECUTABLE_SUFFIX;
|
||||||
}
|
}
|
||||||
result.replace("<EXENAME>", extractFileName(changeFileExt(e->filename(), exeSuffix)));
|
result["EXENAME"] = extractFileName(changeFileExt(e->filename(), exeSuffix));
|
||||||
result.replace("<EXEFILE>", localizePath(changeFileExt(e->filename(), exeSuffix)));
|
result["EXEFILE"] = localizePath(changeFileExt(e->filename(), exeSuffix));
|
||||||
result.replace("<PROJECTNAME>", extractFileName(e->filename()));
|
result["PROJECTNAME"] = extractFileName(e->filename());
|
||||||
result.replace("<PROJECTFILE>", localizePath(e->filename()));
|
result["PROJECTFILE"] = localizePath(e->filename());
|
||||||
result.replace("<PROJECTFILENAME>", extractFileName(e->filename()));
|
result["PROJECTFILENAME"] = extractFileName(e->filename());
|
||||||
result.replace("<PROJECTPATH>", localizePath(extractFileDir(e->filename())));
|
result["PROJECTPATH"] = localizePath(extractFileDir(e->filename()));
|
||||||
} else if (pMainWindow->project()) {
|
} else if (pMainWindow->project()) {
|
||||||
result.replace("<EXENAME>", extractFileName(pMainWindow->project()->executable()));
|
result["EXENAME"] = extractFileName(pMainWindow->project()->executable());
|
||||||
result.replace("<EXEFILE>", localizePath(pMainWindow->project()->executable()));
|
result["EXEFILE"] = localizePath(pMainWindow->project()->executable());
|
||||||
result.replace("<PROJECTNAME>", pMainWindow->project()->name());
|
result["PROJECTNAME"] = pMainWindow->project()->name();
|
||||||
result.replace("<PROJECTFILE>", localizePath(pMainWindow->project()->filename()));
|
result["PROJECTFILE"] = localizePath(pMainWindow->project()->filename());
|
||||||
result.replace("<PROJECTFILENAME>", extractFileName(pMainWindow->project()->filename()));
|
result["PROJECTFILENAME"] = extractFileName(pMainWindow->project()->filename());
|
||||||
result.replace("<PROJECTPATH>", localizePath(pMainWindow->project()->directory()));
|
result["PROJECTPATH"] = localizePath(pMainWindow->project()->directory());
|
||||||
} else {
|
} else {
|
||||||
result.replace("<EXENAME>", "");
|
result["EXENAME"] = "";
|
||||||
result.replace("<EXEFILE>", "");
|
result["EXEFILE"] = "";
|
||||||
result.replace("<PROJECTNAME>", "");
|
result["PROJECTNAME"] = "";
|
||||||
result.replace("<PROJECTFILE>", "");
|
result["PROJECTFILE"] = "";
|
||||||
result.replace("<PROJECTFILENAME>", "");
|
result["PROJECTFILENAME"] = "";
|
||||||
result.replace("<PROJECTPATH>", "");
|
result["PROJECTPATH"] = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Editor macros
|
// Editor macros
|
||||||
if (e != nullptr) {
|
if (e != nullptr) {
|
||||||
result.replace("<SOURCENAME>", extractFileName(e->filename()));
|
result["SOURCENAME"] = extractFileName(e->filename());
|
||||||
result.replace("<SOURCEFILE>", localizePath(e->filename()));
|
result["SOURCEFILE"] = localizePath(e->filename());
|
||||||
result.replace("<SOURCEPATH>", localizePath(extractFileDir(e->filename())));
|
result["SOURCEPATH"] = localizePath(extractFileDir(e->filename()));
|
||||||
result.replace("<WORDXY>", e->wordAtCursor());
|
result["WORDXY"] = e->wordAtCursor();
|
||||||
} else {
|
} else {
|
||||||
result.replace("<SOURCENAME>", "");
|
result["SOURCENAME"] = "";
|
||||||
result.replace("<SOURCEFILE>", "");
|
result["SOURCEFILE"] = "";
|
||||||
result.replace("<SOURCEPATH>", "");
|
result["SOURCEPATH"] = "";
|
||||||
result.replace("<WORDXY>", "");
|
result["WORDXY"] = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,11 +373,11 @@ QByteArray runAndGetOutput(const QString &cmd, const QString& workingDir, const
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void executeFile(const QString &fileName, const QString ¶ms, const QString &workingDir, const QString &tempFile)
|
void executeFile(const QString &fileName, const QStringList ¶ms, const QString &workingDir, const QString &tempFile)
|
||||||
{
|
{
|
||||||
ExecutableRunner* runner=new ExecutableRunner(
|
ExecutableRunner* runner=new ExecutableRunner(
|
||||||
fileName,
|
fileName,
|
||||||
splitProcessCommand(params),
|
params,
|
||||||
workingDir);
|
workingDir);
|
||||||
runner->connect(runner, &QThread::finished,
|
runner->connect(runner, &QThread::finished,
|
||||||
[runner,tempFile](){
|
[runner,tempFile](){
|
||||||
|
@ -630,125 +556,16 @@ QStringList getExecutableSearchPaths()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QString escapeArgument(const QString &arg, [[maybe_unused]] bool isFirstArg)
|
QStringList platformCommandForTerminalArgsPreview()
|
||||||
{
|
|
||||||
auto argContainsOneOf = [&arg](auto... ch) { return (arg.contains(ch) || ...); };
|
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
|
||||||
// See https://stackoverflow.com/questions/31838469/how-do-i-convert-argv-to-lpcommandline-parameter-of-createprocess ,
|
|
||||||
// and https://learn.microsoft.com/en-gb/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way .
|
|
||||||
|
|
||||||
// TODO: investigate whether we need escaping for cmd.
|
|
||||||
|
|
||||||
if (!arg.isEmpty() && !argContainsOneOf(' ', '\t', '\n', '\v', '"'))
|
|
||||||
return arg;
|
|
||||||
|
|
||||||
QString result = "\"";
|
|
||||||
for (auto it = arg.begin(); ; ++it) {
|
|
||||||
int nBackslash = 0;
|
|
||||||
while (it != arg.end() && *it == '\\') {
|
|
||||||
++it;
|
|
||||||
++nBackslash;
|
|
||||||
}
|
|
||||||
if (it == arg.end()) {
|
|
||||||
// Escape all backslashes, but let the terminating double quotation mark we add below be interpreted as a metacharacter.
|
|
||||||
result.append(QString('\\').repeated(nBackslash * 2));
|
|
||||||
break;
|
|
||||||
} else if (*it == '"') {
|
|
||||||
// Escape all backslashes and the following double quotation mark.
|
|
||||||
result.append(QString('\\').repeated(nBackslash * 2 + 1));
|
|
||||||
result.push_back(*it);
|
|
||||||
} else {
|
|
||||||
// Backslashes aren't special here.
|
|
||||||
result.append(QString('\\').repeated(nBackslash));
|
|
||||||
result.push_back(*it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.push_back('"');
|
|
||||||
return result;
|
|
||||||
#else
|
|
||||||
/* be speculative, but keep readability.
|
|
||||||
* ref. https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/V3_chap02.html
|
|
||||||
*/
|
|
||||||
if (arg.isEmpty())
|
|
||||||
return R"("")";
|
|
||||||
|
|
||||||
/* POSIX say the following reserved words (may) have special meaning:
|
|
||||||
* !, {, }, case, do, done, elif, else, esac, fi, for, if, in, then, until, while,
|
|
||||||
* [[, ]], function, select,
|
|
||||||
* only if used as the _first word_ of a command (or somewhere we dot not care).
|
|
||||||
*/
|
|
||||||
const static QSet<QString> reservedWord{
|
|
||||||
"!", "{", "}", "case", "do", "done", "elif", "else", "esac", "fi", "for", "if", "in", "then", "until", "while",
|
|
||||||
"[[", "]]", "function", "select",
|
|
||||||
};
|
|
||||||
if (isFirstArg && reservedWord.contains(arg))
|
|
||||||
return QString(R"("%1")").arg(arg);
|
|
||||||
|
|
||||||
/* POSIX say “shall quote”:
|
|
||||||
* '|', '&', ';', '<', '>', '(', ')', '$', '`', '\\', '"', '\'', ' ', '\t', '\n';
|
|
||||||
* and “may need to be quoted”:
|
|
||||||
* '*', '?', '[', '#', '~', '=', '%'.
|
|
||||||
* among which “may need to be quoted” there are 4 kinds:
|
|
||||||
* - wildcards '*', '?', '[' are “danger anywhere” (handle it as if “shall quote”);
|
|
||||||
* - comment '#', home '~', is “danger at first char in any word”;
|
|
||||||
* - (environment) variable '=' is “danger at any char in first word”;
|
|
||||||
* - foreground '%' is “danger at first char in first word”.
|
|
||||||
* although not mentioned by POSIX, bash’s brace expansion '{', '}' are also “danger anywhere”.
|
|
||||||
*/
|
|
||||||
bool isDoubleQuotingDanger = argContainsOneOf('$', '`', '\\', '"');
|
|
||||||
bool isSingleQuotingDanger = arg.contains('\'');
|
|
||||||
bool isDangerAnyChar = isDoubleQuotingDanger || isSingleQuotingDanger || argContainsOneOf(
|
|
||||||
'|', '&', ';', '<', '>', '(', ')', ' ', '\t', '\n',
|
|
||||||
'*', '?', '[',
|
|
||||||
'{', '}'
|
|
||||||
);
|
|
||||||
bool isDangerFirstChar = (arg[0] == '#') || (arg[0] == '~');
|
|
||||||
if (isFirstArg) {
|
|
||||||
isDangerAnyChar = isDangerAnyChar || arg.contains('=');
|
|
||||||
isDangerFirstChar = isDangerFirstChar || (arg[0] == '%');
|
|
||||||
}
|
|
||||||
|
|
||||||
// a “safe” string
|
|
||||||
if (!isDangerAnyChar && !isDangerFirstChar)
|
|
||||||
return arg;
|
|
||||||
|
|
||||||
// prefer more-common double quoting
|
|
||||||
if (!isDoubleQuotingDanger)
|
|
||||||
return QString(R"("%1")").arg(arg);
|
|
||||||
|
|
||||||
// and then check the opportunity of single quoting
|
|
||||||
if (!isSingleQuotingDanger)
|
|
||||||
return QString("'%1'").arg(arg);
|
|
||||||
|
|
||||||
// escaping is necessary
|
|
||||||
// use double quoting since it’s less tricky
|
|
||||||
QString result = "\"";
|
|
||||||
for (auto ch : arg) {
|
|
||||||
if (ch == '$' || ch == '`' || ch == '\\' || ch == '"')
|
|
||||||
result.push_back('\\');
|
|
||||||
result.push_back(ch);
|
|
||||||
}
|
|
||||||
result.push_back('"');
|
|
||||||
return result;
|
|
||||||
|
|
||||||
/* single quoting, which is roughly raw string, is possible and quite simple in programming:
|
|
||||||
* 1. replace each single quote with `'\''`, which contains
|
|
||||||
* - a single quote to close quoting,
|
|
||||||
* - an escaped single quote representing the single quote itself, and
|
|
||||||
* - a single quote to open quoting again;
|
|
||||||
* 2. enclose the string with a pair of single quotes.
|
|
||||||
* e.g. `o'clock` => `'o'\''clock'`, really tricky and hard to read.
|
|
||||||
*/
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QString defaultShell()
|
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
return "powershell.exe";
|
QVersionNumber currentVersion = QVersionNumber::fromString(QSysInfo::kernelVersion());
|
||||||
|
if (currentVersion >= QVersionNumber(6, 1))
|
||||||
|
return {"powershell.exe", "-c", "echo hello; sleep 3"};
|
||||||
|
else
|
||||||
|
return {"cmd.exe", "/c", "echo hello & ping 127.0.0.1"};
|
||||||
#else
|
#else
|
||||||
return "sh";
|
return {"sh", "-c", "echo hello; sleep 3"};
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,14 +116,12 @@ enum class ProblemCaseValidateType {
|
||||||
};
|
};
|
||||||
|
|
||||||
FileType getFileType(const QString& filename);
|
FileType getFileType(const QString& filename);
|
||||||
QStringList splitProcessCommand(const QString& cmd);
|
|
||||||
|
|
||||||
QString genMakePath(const QString& fileName,bool escapeSpaces, bool encloseInQuotes);
|
|
||||||
QString genMakePath1(const QString& fileName);
|
|
||||||
QString genMakePath2(const QString& fileName);
|
|
||||||
bool programHasConsole(const QString& filename);
|
bool programHasConsole(const QString& filename);
|
||||||
|
|
||||||
QString parseMacros(const QString& s);
|
QString parseMacros(const QString& s);
|
||||||
|
QString parseMacros(const QString& s, const QMap<QString, QString>& variables);
|
||||||
|
QMap<QString, QString> devCppMacroVariables();
|
||||||
|
|
||||||
class CppParser;
|
class CppParser;
|
||||||
void resetCppParser(std::shared_ptr<CppParser> parser, int compilerSetIndex=-1);
|
void resetCppParser(std::shared_ptr<CppParser> parser, int compilerSetIndex=-1);
|
||||||
|
@ -138,7 +136,7 @@ QByteArray runAndGetOutput(const QString& cmd, const QString& workingDir, const
|
||||||
void openFileFolderInExplorer(const QString& path);
|
void openFileFolderInExplorer(const QString& path);
|
||||||
|
|
||||||
void executeFile(const QString& fileName,
|
void executeFile(const QString& fileName,
|
||||||
const QString& params,
|
const QStringList& params,
|
||||||
const QString& workingDir,
|
const QString& workingDir,
|
||||||
const QString& tempFile);
|
const QString& tempFile);
|
||||||
|
|
||||||
|
@ -169,9 +167,7 @@ QColor alphaBlend(const QColor &lower, const QColor &upper);
|
||||||
|
|
||||||
QStringList getExecutableSearchPaths();
|
QStringList getExecutableSearchPaths();
|
||||||
|
|
||||||
QString escapeArgument(const QString &arg, bool isFirstArg);
|
QStringList platformCommandForTerminalArgsPreview();
|
||||||
|
|
||||||
QString defaultShell();
|
|
||||||
|
|
||||||
QString appArch();
|
QString appArch();
|
||||||
QString osArch();
|
QString osArch();
|
||||||
|
|
|
@ -0,0 +1,305 @@
|
||||||
|
/*
|
||||||
|
* 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 "utils/escape.h"
|
||||||
|
|
||||||
|
#include <QSet>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define __builtin_unreachable() (__assume(0))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static QString contextualBackslashEscaping(const QString &arg, const QSet<QChar> &needsEscaping, bool escapeFinal = true)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
for (auto it = arg.begin(); ; ++it) {
|
||||||
|
int nBackSlash = 0;
|
||||||
|
while (it != arg.end() && *it == '\\') {
|
||||||
|
++it;
|
||||||
|
++nBackSlash;
|
||||||
|
}
|
||||||
|
if (it == arg.end()) {
|
||||||
|
if (escapeFinal) {
|
||||||
|
// escape all backslashes, but leave following character unescaped
|
||||||
|
// (terminating double quote for CreateProcess, or LF or space in makefile)
|
||||||
|
result.append(QString('\\').repeated(nBackSlash * 2));
|
||||||
|
} else {
|
||||||
|
// leave all backslashes unescaped, and add a space to protect LF
|
||||||
|
result.append(QString('\\').repeated(nBackSlash));
|
||||||
|
if (nBackSlash > 0)
|
||||||
|
result.push_back(' ');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else if (needsEscaping.contains(*it)) {
|
||||||
|
// escape all backslashes and the following character
|
||||||
|
result.append(QString('\\').repeated(nBackSlash * 2 + 1));
|
||||||
|
result.push_back(*it);
|
||||||
|
} else {
|
||||||
|
// backslashes aren't special here
|
||||||
|
result.append(QString('\\').repeated(nBackSlash));
|
||||||
|
result.push_back(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeArgumentImplBourneAgainShellPretty(const QString &arg, bool isFirstArg)
|
||||||
|
{
|
||||||
|
// ref. https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/V3_chap02.html
|
||||||
|
|
||||||
|
if (arg.isEmpty())
|
||||||
|
return R"("")";
|
||||||
|
|
||||||
|
/* POSIX say the following reserved words (may) have special meaning:
|
||||||
|
* !, {, }, case, do, done, elif, else, esac, fi, for, if, in, then, until, while,
|
||||||
|
* [[, ]], function, select,
|
||||||
|
* only if used as the _first word_ of a command (or somewhere we dot not care).
|
||||||
|
*/
|
||||||
|
const static QSet<QString> reservedWord{
|
||||||
|
"!", "{", "}", "case", "do", "done", "elif", "else", "esac", "fi", "for", "if", "in", "then", "until", "while",
|
||||||
|
"[[", "]]", "function", "select",
|
||||||
|
};
|
||||||
|
if (isFirstArg && reservedWord.contains(arg))
|
||||||
|
return QString(R"("%1")").arg(arg);
|
||||||
|
|
||||||
|
/* POSIX say “shall quote”:
|
||||||
|
* '|', '&', ';', '<', '>', '(', ')', '$', '`', '\\', '"', '\'', ' ', '\t', '\n';
|
||||||
|
* and “may need to be quoted”:
|
||||||
|
* '*', '?', '[', '#', '~', '=', '%'.
|
||||||
|
* among which “may need to be quoted” there are 4 kinds:
|
||||||
|
* - wildcards '*', '?', '[' are “danger anywhere” (handle it as if “shall quote”);
|
||||||
|
* - comment '#', home '~', is “danger at first char in any word”;
|
||||||
|
* - (environment) variable '=' is “danger at any char in first word”;
|
||||||
|
* - foreground '%' is “danger at first char in first word”.
|
||||||
|
* although not mentioned by POSIX, bash’s brace expansion '{', '}' are also “danger anywhere”.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static QRegularExpression doubleQuotingDangerChars(R"([`$\\"])");
|
||||||
|
static QRegularExpression otherDangerChars(R"([|&;<>() \t\n*?\[\{\}])");
|
||||||
|
bool isDoubleQuotingDanger = arg.contains(doubleQuotingDangerChars);
|
||||||
|
bool isSingleQuotingDanger = arg.contains('\'');
|
||||||
|
bool isDangerAnyChar = isDoubleQuotingDanger || isSingleQuotingDanger || arg.contains(otherDangerChars);
|
||||||
|
bool isDangerFirstChar = (arg[0] == '#') || (arg[0] == '~');
|
||||||
|
if (isFirstArg) {
|
||||||
|
isDangerAnyChar = isDangerAnyChar || arg.contains('=');
|
||||||
|
isDangerFirstChar = isDangerFirstChar || (arg[0] == '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// a “safe” string
|
||||||
|
if (!isDangerAnyChar && !isDangerFirstChar)
|
||||||
|
return arg;
|
||||||
|
|
||||||
|
// prefer more-common double quoting
|
||||||
|
if (!isDoubleQuotingDanger)
|
||||||
|
return QString(R"("%1")").arg(arg);
|
||||||
|
|
||||||
|
// and then check the opportunity of single quoting
|
||||||
|
if (!isSingleQuotingDanger)
|
||||||
|
return QString("'%1'").arg(arg);
|
||||||
|
|
||||||
|
// escaping is necessary
|
||||||
|
// use double quoting since it’s less tricky
|
||||||
|
QString result = "\"";
|
||||||
|
for (auto ch : arg) {
|
||||||
|
if (ch == '$' || ch == '`' || ch == '\\' || ch == '"')
|
||||||
|
result.push_back('\\');
|
||||||
|
result.push_back(ch);
|
||||||
|
}
|
||||||
|
result.push_back('"');
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeArgumentImplBourneAgainShellFast(QString arg)
|
||||||
|
{
|
||||||
|
/* 1. replace each single quote with `'\''`, which contains
|
||||||
|
* - a single quote to close quoting,
|
||||||
|
* - an escaped single quote representing the single quote itself, and
|
||||||
|
* - a single quote to open quoting again. */
|
||||||
|
arg.replace('\'', R"('\'')");
|
||||||
|
/* 2. enclose the string with a pair of single quotes. */
|
||||||
|
return '\'' + arg + '\'';
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeArgumentImplWindowsCreateProcess(const QString &arg, bool forceQuote)
|
||||||
|
{
|
||||||
|
// See https://stackoverflow.com/questions/31838469/how-do-i-convert-argv-to-lpcommandline-parameter-of-createprocess ,
|
||||||
|
// and https://learn.microsoft.com/en-gb/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way .
|
||||||
|
|
||||||
|
static QRegularExpression needQuoting(R"([ \t\n\v"])");
|
||||||
|
if (!arg.isEmpty() && !forceQuote && !arg.contains(needQuoting))
|
||||||
|
return arg;
|
||||||
|
|
||||||
|
return '"' + contextualBackslashEscaping(arg, {'"'}) + '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeArgumentImplWindowsCommandPrompt(const QString &arg)
|
||||||
|
{
|
||||||
|
static QRegularExpression metaChars(R"([()%!^"<>&|])");
|
||||||
|
bool containsMeta = arg.contains(metaChars);
|
||||||
|
if (containsMeta) {
|
||||||
|
QString quoted = escapeArgumentImplWindowsCreateProcess(arg, false);
|
||||||
|
quoted.replace('^', "^^"); // handle itself first
|
||||||
|
quoted.replace('(', "^(");
|
||||||
|
quoted.replace(')', "^)");
|
||||||
|
quoted.replace('%', "^%");
|
||||||
|
quoted.replace('!', "^!");
|
||||||
|
quoted.replace('"', "^\"");
|
||||||
|
quoted.replace('<', "^<");
|
||||||
|
quoted.replace('>', "^>");
|
||||||
|
quoted.replace('&', "^&");
|
||||||
|
quoted.replace('|', "^|");
|
||||||
|
return quoted;
|
||||||
|
} else
|
||||||
|
return escapeArgumentImplWindowsCreateProcess(arg, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeArgument(const QString &arg, bool isFirstArg, EscapeArgumentRule rule)
|
||||||
|
{
|
||||||
|
switch (rule) {
|
||||||
|
case EscapeArgumentRule::BourneAgainShellPretty:
|
||||||
|
return escapeArgumentImplBourneAgainShellPretty(arg, isFirstArg);
|
||||||
|
case EscapeArgumentRule::BourneAgainShellFast:
|
||||||
|
return escapeArgumentImplBourneAgainShellFast(arg);
|
||||||
|
case EscapeArgumentRule::WindowsCreateProcess:
|
||||||
|
return escapeArgumentImplWindowsCreateProcess(arg, false);
|
||||||
|
case EscapeArgumentRule::WindowsCreateProcessForceQuote:
|
||||||
|
return escapeArgumentImplWindowsCreateProcess(arg, true);
|
||||||
|
case EscapeArgumentRule::WindowsCommandPrompt:
|
||||||
|
return escapeArgumentImplWindowsCommandPrompt(arg);
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EscapeArgumentRule platformShellEscapeArgumentRule()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
return EscapeArgumentRule::WindowsCommandPrompt;
|
||||||
|
#else
|
||||||
|
return EscapeArgumentRule::BourneAgainShellPretty;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeArgumentForPlatformShell(const QString &arg, bool isFirstArg)
|
||||||
|
{
|
||||||
|
return escapeArgument(arg, isFirstArg, platformShellEscapeArgumentRule());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeCommandForPlatformShell(const QString &prog, const QStringList &args)
|
||||||
|
{
|
||||||
|
QStringList escapedArgs{escapeArgumentForPlatformShell(prog, true)};
|
||||||
|
for (int i = 0; i < args.size(); ++i)
|
||||||
|
escapedArgs << escapeArgumentForPlatformShell(args[i], false);
|
||||||
|
return escapedArgs.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
EscapeArgumentRule makefileRecipeEscapeArgumentRule()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
/* Lord knows why.
|
||||||
|
|
||||||
|
standard CreateProcess or CMD escaping:
|
||||||
|
child.exe -c main'.c -o main'.o
|
||||||
|
yielding:
|
||||||
|
0: [child.exe]
|
||||||
|
1: [-c]
|
||||||
|
2: [main.c -o main.o]
|
||||||
|
that's not what we want.
|
||||||
|
|
||||||
|
however, if CMD escaping a malformed argument
|
||||||
|
child.exe -c main'.c -o main'.o ^"mal \^" ^& calc^"
|
||||||
|
yielding:
|
||||||
|
0: [child.exe]
|
||||||
|
1: [-c]
|
||||||
|
2: [main'.c]
|
||||||
|
3: [-o]
|
||||||
|
4: [main'.o]
|
||||||
|
5: [mal " & calc]
|
||||||
|
it works?!?!?!
|
||||||
|
|
||||||
|
force-quoted CreateProcess escaping seems work on most cases.
|
||||||
|
*/
|
||||||
|
return EscapeArgumentRule::WindowsCreateProcessForceQuote;
|
||||||
|
#else
|
||||||
|
return EscapeArgumentRule::BourneAgainShellPretty;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeArgumentForMakefileVariableValue(const QString &arg, bool isFirstArg)
|
||||||
|
{
|
||||||
|
static QSet<QChar> needsMfEscaping = {'#'};
|
||||||
|
QString recipeEscaped = escapeArgument(arg, isFirstArg, makefileRecipeEscapeArgumentRule());
|
||||||
|
QString mfEscaped = contextualBackslashEscaping(recipeEscaped, needsMfEscaping);
|
||||||
|
return mfEscaped.replace('$', "$$");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeArgumentsForMakefileVariableValue(const QStringList &args)
|
||||||
|
{
|
||||||
|
QStringList escapedArgs;
|
||||||
|
for (int i = 0; i < args.size(); ++i)
|
||||||
|
escapedArgs << escapeArgumentForMakefileVariableValue(args[i], false);
|
||||||
|
return escapedArgs.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeFilenameForMakefileInclude(const QString &filename)
|
||||||
|
{
|
||||||
|
static QSet<QChar> needsEscaping{'#', ' '};
|
||||||
|
QString result = contextualBackslashEscaping(filename, needsEscaping);
|
||||||
|
return result.replace('$', "$$");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeFilenameForMakefileTarget(const QString &filename)
|
||||||
|
{
|
||||||
|
static QSet<QChar> needsEscaping{'#', ' ', ':', '%'};
|
||||||
|
QString result = contextualBackslashEscaping(filename, needsEscaping);
|
||||||
|
return result.replace('$', "$$");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeFilenameForMakefilePrerequisite(const QString &filename)
|
||||||
|
{
|
||||||
|
static QSet<QChar> needsEscaping{'#', ' ', ':', '?', '*'};
|
||||||
|
QString result = contextualBackslashEscaping(filename, needsEscaping, false);
|
||||||
|
return result.replace('$', "$$");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeFilenamesForMakefilePrerequisite(const QStringList &filenames)
|
||||||
|
{
|
||||||
|
QStringList escapedFilenames;
|
||||||
|
for (int i = 0; i < filenames.size(); ++i)
|
||||||
|
escapedFilenames << escapeFilenameForMakefilePrerequisite(filenames[i]);
|
||||||
|
return escapedFilenames.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeArgumentForMakefileRecipe(const QString &arg, bool isFirstArg)
|
||||||
|
{
|
||||||
|
QString shellEscaped = escapeArgument(arg, isFirstArg, makefileRecipeEscapeArgumentRule());
|
||||||
|
return shellEscaped.replace('$', "$$");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeArgumentForInputField(const QString &arg, bool isFirstArg)
|
||||||
|
{
|
||||||
|
return escapeArgument(arg, isFirstArg, EscapeArgumentRule::BourneAgainShellPretty);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString escapeArgumentsForInputField(const QStringList &args)
|
||||||
|
{
|
||||||
|
QStringList escapedArgs;
|
||||||
|
for (int i = 0; i < args.size(); ++i)
|
||||||
|
escapedArgs << escapeArgumentForInputField(args[i], false);
|
||||||
|
return escapedArgs.join(' ');
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#ifndef ESCAPE_H
|
||||||
|
#define ESCAPE_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
enum class EscapeArgumentRule {
|
||||||
|
BourneAgainShellPretty,
|
||||||
|
BourneAgainShellFast,
|
||||||
|
WindowsCreateProcess,
|
||||||
|
WindowsCreateProcessForceQuote,
|
||||||
|
WindowsCommandPrompt,
|
||||||
|
};
|
||||||
|
|
||||||
|
QString escapeArgument(const QString &arg, bool isFirstArg, EscapeArgumentRule rule);
|
||||||
|
|
||||||
|
EscapeArgumentRule platformShellEscapeArgumentRule();
|
||||||
|
QString escapeArgumentForPlatformShell(const QString &arg, bool isFirstArg);
|
||||||
|
QString escapeCommandForPlatformShell(const QString &prog, const QStringList &args);
|
||||||
|
|
||||||
|
EscapeArgumentRule makefileRecipeEscapeArgumentRule();
|
||||||
|
QString escapeArgumentForMakefileVariableValue(const QString &arg, bool isFirstArg);
|
||||||
|
QString escapeArgumentsForMakefileVariableValue(const QStringList &args);
|
||||||
|
QString escapeFilenameForMakefileInclude(const QString &filename);
|
||||||
|
QString escapeFilenameForMakefileTarget(const QString &filename);
|
||||||
|
QString escapeFilenameForMakefilePrerequisite(const QString &filename);
|
||||||
|
QString escapeFilenamesForMakefilePrerequisite(const QStringList &filenames);
|
||||||
|
QString escapeArgumentForMakefileRecipe(const QString &arg, bool isFirstArg);
|
||||||
|
|
||||||
|
QString escapeArgumentForInputField(const QString &arg, bool isFirstArg);
|
||||||
|
QString escapeArgumentsForInputField(const QStringList &args);
|
||||||
|
|
||||||
|
#endif // ESCAPE_H
|
|
@ -0,0 +1,468 @@
|
||||||
|
/*
|
||||||
|
* 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 "parsearg.h"
|
||||||
|
|
||||||
|
namespace ParseArgumentsDetail
|
||||||
|
{
|
||||||
|
|
||||||
|
/*Before:
|
||||||
|
'blahblah'
|
||||||
|
^pos
|
||||||
|
After:
|
||||||
|
'blahblah'
|
||||||
|
^pos */
|
||||||
|
QString singleQuoted(const QString &command, int &pos)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
while (pos < command.length() && command[pos] != '\'') {
|
||||||
|
result.push_back(command[pos]);
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
if (pos < command.length())
|
||||||
|
++pos; // eat closing quote
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read up to 3 octal digits
|
||||||
|
QString readOctal(const QString &command, int &pos)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
if (pos < command.length() && command[pos] >= '0' && command[pos] <= '7') {
|
||||||
|
result.push_back(command[pos]);
|
||||||
|
++pos;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read up to maxDigits hex digits
|
||||||
|
QString readHex(const QString &command, int &pos, int maxDigits)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
for (int i = 0; i < maxDigits; ++i) {
|
||||||
|
if (pos < command.length() && (command[pos].isDigit() || (command[pos].toLower() >= 'a' && command[pos].toLower() <= 'f'))) {
|
||||||
|
result.push_back(command[pos]);
|
||||||
|
++pos;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Case 1: braced variable name (ingore POSIX operators, no nested braces)
|
||||||
|
Before:
|
||||||
|
${VARNAME.abc$}
|
||||||
|
^pos
|
||||||
|
After:
|
||||||
|
${VARNAME.abc$}
|
||||||
|
^pos
|
||||||
|
Returns: value of `VARNAME.abc$`, "" if not found
|
||||||
|
Case 2: command or arithmetic (no expansion, nested parentheses ok)
|
||||||
|
Before:
|
||||||
|
$(echo 1)
|
||||||
|
^pos
|
||||||
|
$((1+1))
|
||||||
|
^pos
|
||||||
|
After:
|
||||||
|
$(echo 1)
|
||||||
|
^pos
|
||||||
|
$((1+1))
|
||||||
|
^pos
|
||||||
|
Returns: as is
|
||||||
|
Case 3: ANSI-C quoting
|
||||||
|
Before:
|
||||||
|
$'blah\nblah\'blah'
|
||||||
|
^pos
|
||||||
|
After:
|
||||||
|
$'blah\nblah\'blah'
|
||||||
|
^pos
|
||||||
|
Returns: unescaped string
|
||||||
|
Case 4: normal variable name
|
||||||
|
Before:
|
||||||
|
$VAR_NAME-x
|
||||||
|
^pos
|
||||||
|
After:
|
||||||
|
$VAR_NAME-x
|
||||||
|
^pos
|
||||||
|
Returns: value of `VAR_NAME`, "" if not found
|
||||||
|
Case 5: all other invalid cases (though they may be valid in shell)
|
||||||
|
Before:
|
||||||
|
$123
|
||||||
|
^pos
|
||||||
|
After:
|
||||||
|
$123
|
||||||
|
^pos
|
||||||
|
Returns: as is */
|
||||||
|
QString variableExpansion(const QString &command, int &pos, const QMap<QString, QString> &variables, bool ansiCQuotingPermitted)
|
||||||
|
{
|
||||||
|
if (pos >= command.length())
|
||||||
|
return "$";
|
||||||
|
if (command[pos] == '{') {
|
||||||
|
// case 1, read to closing brace
|
||||||
|
QString varName;
|
||||||
|
QString result;
|
||||||
|
++pos; // eat opening brace
|
||||||
|
while (pos < command.length() && command[pos] != '}') {
|
||||||
|
varName.push_back(command[pos]);
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
if (pos < command.length()) {
|
||||||
|
++pos; // eat closing brace
|
||||||
|
if (variables.contains(varName))
|
||||||
|
return variables[varName];
|
||||||
|
else
|
||||||
|
return {};
|
||||||
|
} else {
|
||||||
|
// unterminated
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
} else if (command[pos] == '(') {
|
||||||
|
// case 2, read to matching closing paren
|
||||||
|
QString result = "$(";
|
||||||
|
++pos; // eat opening paren
|
||||||
|
int level = 1;
|
||||||
|
while (pos < command.length() && level > 0) {
|
||||||
|
if (command[pos] == '(')
|
||||||
|
++level;
|
||||||
|
else if (command[pos] == ')')
|
||||||
|
--level;
|
||||||
|
result.push_back(command[pos]);
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else if (ansiCQuotingPermitted && command[pos] == '\'') {
|
||||||
|
// case 3, parse ANSI-C quoting
|
||||||
|
QByteArray unescaped;
|
||||||
|
++pos; // eat opening quote
|
||||||
|
while (pos < command.length()) {
|
||||||
|
if (command[pos] == '\\') {
|
||||||
|
++pos;
|
||||||
|
if (pos < command.length()) {
|
||||||
|
switch (command[pos].unicode()) {
|
||||||
|
case 'a':
|
||||||
|
++pos;
|
||||||
|
unescaped.push_back('\a');
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
++pos;
|
||||||
|
unescaped.push_back('\b');
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
case 'E':
|
||||||
|
++pos;
|
||||||
|
unescaped.push_back('\x1B');
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
++pos;
|
||||||
|
unescaped.push_back('\f');
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
++pos;
|
||||||
|
unescaped.push_back('\n');
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
++pos;
|
||||||
|
unescaped.push_back('\r');
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
++pos;
|
||||||
|
unescaped.push_back('\t');
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
++pos;
|
||||||
|
unescaped.push_back('\v');
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
++pos;
|
||||||
|
unescaped.push_back('\\');
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
++pos;
|
||||||
|
unescaped.push_back('\'');
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
++pos;
|
||||||
|
unescaped.push_back('"');
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
++pos;
|
||||||
|
unescaped.push_back('?');
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7': {
|
||||||
|
QString digits = readOctal(command, pos);
|
||||||
|
int octal = digits.toUInt(nullptr, 8);
|
||||||
|
unescaped.push_back(octal);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'x': {
|
||||||
|
++pos; // eat 'x'
|
||||||
|
QString digits = readHex(command, pos, 2);
|
||||||
|
if (digits.isEmpty()) {
|
||||||
|
// normal character
|
||||||
|
unescaped.append("\\x");
|
||||||
|
} else {
|
||||||
|
int hex = digits.toUInt(nullptr, 16);
|
||||||
|
unescaped.push_back(hex);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'u': {
|
||||||
|
++pos; // eat 'u'
|
||||||
|
QString digits = readHex(command, pos, 4);
|
||||||
|
if (digits.isEmpty()) {
|
||||||
|
// normal character
|
||||||
|
unescaped.append("\\u");
|
||||||
|
} else {
|
||||||
|
int hex = digits.toUInt(nullptr, 16);
|
||||||
|
QByteArray encoded = QString(hex).toUtf8();
|
||||||
|
unescaped.append(encoded);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'U': {
|
||||||
|
++pos; // eat 'U'
|
||||||
|
QString digits = readHex(command, pos, 8);
|
||||||
|
if (digits.isEmpty()) {
|
||||||
|
// normal character
|
||||||
|
unescaped.append("\\U");
|
||||||
|
} else {
|
||||||
|
int hex = digits.toUInt(nullptr, 16);
|
||||||
|
QByteArray encoded = QString(hex).toUtf8();
|
||||||
|
unescaped.append(encoded);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// normal character
|
||||||
|
unescaped.push_back('\\');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (command[pos] == '\'') {
|
||||||
|
++pos; // eat closing quote
|
||||||
|
return unescaped;
|
||||||
|
} else {
|
||||||
|
QChar c = command[pos];
|
||||||
|
QByteArray encoded = QString(c).toUtf8();
|
||||||
|
unescaped.append(encoded);
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// unterminated
|
||||||
|
return unescaped;
|
||||||
|
} else if (command[pos].isLetter() || command[pos] == '_') {
|
||||||
|
// case 4, read variable name
|
||||||
|
QString varName;
|
||||||
|
while (pos < command.length() && (command[pos].isLetterOrNumber() || command[pos] == '_')) {
|
||||||
|
varName.push_back(command[pos]);
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
if (variables.contains(varName))
|
||||||
|
return variables[varName];
|
||||||
|
else
|
||||||
|
return {};
|
||||||
|
} else {
|
||||||
|
// case 5, return as is
|
||||||
|
return "$";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Before:
|
||||||
|
<VARNAME.abc$>
|
||||||
|
^pos
|
||||||
|
After:
|
||||||
|
<VARNAME.abc$>
|
||||||
|
^pos */
|
||||||
|
QString devCppExpansion(const QString &command, int &pos, const QMap<QString, QString> &variables)
|
||||||
|
{
|
||||||
|
QString varName;
|
||||||
|
QString result;
|
||||||
|
while (pos < command.length() && command[pos] != '>') {
|
||||||
|
varName.push_back(command[pos]);
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
if (pos < command.length()) {
|
||||||
|
++pos; // eat closing bracket
|
||||||
|
if (variables.contains(varName))
|
||||||
|
return variables[varName];
|
||||||
|
else
|
||||||
|
// not a variable
|
||||||
|
return '<' + varName + '>';
|
||||||
|
} else {
|
||||||
|
// unterminated, treat it as a normal string
|
||||||
|
return '<' + varName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Before:
|
||||||
|
"blah\"blah"
|
||||||
|
^pos
|
||||||
|
After:
|
||||||
|
"blah\"blah"
|
||||||
|
^pos */
|
||||||
|
QString doubleQuoted(const QString &command, int &pos, const QMap<QString, QString> &variables, bool enableDevCppVariableExpansion)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
while (pos < command.length()) {
|
||||||
|
switch (command[pos].unicode()) {
|
||||||
|
case '$':
|
||||||
|
++pos; // eat '$'
|
||||||
|
result += variableExpansion(command, pos, variables, false);
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
++pos; // eat backslash
|
||||||
|
if (pos < command.length()) {
|
||||||
|
switch (command[pos].unicode()) {
|
||||||
|
case '$':
|
||||||
|
case '`':
|
||||||
|
case '"':
|
||||||
|
case '\\':
|
||||||
|
result.push_back(command[pos]);
|
||||||
|
++pos;
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
++pos; // eat newline
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// normal character
|
||||||
|
result.push_back('\\');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// unterminated
|
||||||
|
result.push_back('\\');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
++pos; // eat closing quote
|
||||||
|
return result;
|
||||||
|
case '<':
|
||||||
|
if (enableDevCppVariableExpansion) {
|
||||||
|
++pos; // eat '<'
|
||||||
|
result += devCppExpansion(command, pos, variables);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
case '`': // not supported
|
||||||
|
default:
|
||||||
|
result.push_back(command[pos]);
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// unterminated
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ParseArgumentsDetail
|
||||||
|
|
||||||
|
QStringList parseArguments(const QString &command, const QMap<QString, QString> &variables, bool enableDevCppVariableExpansion)
|
||||||
|
{
|
||||||
|
using namespace ParseArgumentsDetail;
|
||||||
|
|
||||||
|
QStringList result;
|
||||||
|
QString current;
|
||||||
|
bool currentPolluted = false;
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
while (pos < command.length()) {
|
||||||
|
switch (command[pos].unicode()) {
|
||||||
|
case ' ':
|
||||||
|
case '\t':
|
||||||
|
case '\n':
|
||||||
|
if (currentPolluted) {
|
||||||
|
result.push_back(current);
|
||||||
|
current.clear();
|
||||||
|
currentPolluted = false;
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
break;
|
||||||
|
case '#':
|
||||||
|
if (currentPolluted) {
|
||||||
|
// normal character
|
||||||
|
current.push_back(command[pos]);
|
||||||
|
++pos;
|
||||||
|
} else {
|
||||||
|
// comment, eat to newline
|
||||||
|
while (pos < command.length() && command[pos] != '\n')
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
++pos; // eat opening quote
|
||||||
|
current += singleQuoted(command, pos);
|
||||||
|
currentPolluted = true;
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
++pos; // eat opening quote
|
||||||
|
current += doubleQuoted(command, pos, variables, enableDevCppVariableExpansion);
|
||||||
|
currentPolluted = true;
|
||||||
|
break;
|
||||||
|
case '$':
|
||||||
|
++pos; // eat '$'
|
||||||
|
current += variableExpansion(command, pos, variables, true);
|
||||||
|
currentPolluted = true;
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
++pos; // eat backslash
|
||||||
|
if (pos < command.length()) {
|
||||||
|
if (command[pos] != '\n')
|
||||||
|
++pos; // eat newline
|
||||||
|
else {
|
||||||
|
// normal character
|
||||||
|
current.push_back(command[pos]);
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
currentPolluted = true;
|
||||||
|
} else {
|
||||||
|
// unterminated
|
||||||
|
current.push_back('\\');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
if (enableDevCppVariableExpansion) {
|
||||||
|
++pos; // eat '<'
|
||||||
|
current += devCppExpansion(command, pos, variables);
|
||||||
|
currentPolluted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
default:
|
||||||
|
current.push_back(command[pos]);
|
||||||
|
++pos;
|
||||||
|
currentPolluted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentPolluted)
|
||||||
|
result.push_back(current);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList parseArgumentsWithoutVariables(const QString &command)
|
||||||
|
{
|
||||||
|
return parseArguments(command, {}, false);
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#ifndef PARSEARG_H
|
||||||
|
#define PARSEARG_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
QStringList parseArguments(const QString &command, const QMap<QString, QString> &variables, bool enableDevCppVariableExpansion);
|
||||||
|
QStringList parseArgumentsWithoutVariables(const QString &command);
|
||||||
|
|
||||||
|
#endif // PARSEARG_H
|
|
@ -47,7 +47,9 @@ target("RedPandaIDE")
|
||||||
-- problems
|
-- problems
|
||||||
"problems/freeprojectsetformat.cpp",
|
"problems/freeprojectsetformat.cpp",
|
||||||
"problems/ojproblemset.cpp",
|
"problems/ojproblemset.cpp",
|
||||||
"problems/problemcasevalidator.cpp")
|
"problems/problemcasevalidator.cpp",
|
||||||
|
"utils/escape.cpp",
|
||||||
|
"utils/parsearg.cpp")
|
||||||
|
|
||||||
add_moc_classes(
|
add_moc_classes(
|
||||||
"caretlist",
|
"caretlist",
|
||||||
|
@ -236,3 +238,13 @@ target("RedPandaIDE")
|
||||||
if is_xdg() then
|
if is_xdg() then
|
||||||
on_install(install_bin)
|
on_install(install_bin)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
target("test-escape")
|
||||||
|
set_kind("binary")
|
||||||
|
add_rules("qt.console")
|
||||||
|
|
||||||
|
set_default(false)
|
||||||
|
add_tests("test-escape")
|
||||||
|
|
||||||
|
add_files("utils/escape.cpp", "test/escape.cpp")
|
||||||
|
add_includedirs(".")
|
||||||
|
|
|
@ -539,7 +539,7 @@ QString changeFileExt(const QString& filename, QString ext)
|
||||||
path = includeTrailingPathDelimiter(fileInfo.path());
|
path = includeTrailingPathDelimiter(fileInfo.path());
|
||||||
}
|
}
|
||||||
if (suffix.isEmpty()) {
|
if (suffix.isEmpty()) {
|
||||||
return path+filename+ext;
|
return path+name+ext;
|
||||||
} else {
|
} else {
|
||||||
return path+fileInfo.completeBaseName()+ext;
|
return path+fileInfo.completeBaseName()+ext;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue