qt 6.5.1 original

This commit is contained in:
kleuter
2023-10-29 23:33:08 +01:00
parent 71d22ab6b0
commit 85d238dfda
21202 changed files with 5499099 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PBUILDER_PBX_H
#define PBUILDER_PBX_H
#include "unixmake.h"
QT_BEGIN_NAMESPACE
class ProjectBuilderMakefileGenerator : public UnixMakefileGenerator
{
bool writingUnixMakefileGenerator;
mutable QString pbx_dir;
int pbuilderVersion() const;
bool writeSubDirs(QTextStream &);
bool writeMakeParts(QTextStream &);
bool writeMakefile(QTextStream &) override;
bool replaceLibrarySuffix(const QString &lib_file, const ProString &opt, QString &name,
QString &library);
QString pbxbuild();
QHash<QString, QString> keys;
QString keyFor(const QString &file);
QString findProgram(const ProString &prog);
QString fixForOutput(const QString &file);
ProStringList fixListForOutput(const char *where);
ProStringList fixListForOutput(const ProStringList &list);
int reftypeForFile(const QString &where);
QString projectSuffix() const;
enum { SettingsAsList=0x01, SettingsNoQuote=0x02 };
inline QString writeSettings(const QString &var, const char *val, int flags=0, int indent_level=0)
{ return writeSettings(var, ProString(val), flags, indent_level); }
inline QString writeSettings(const QString &var, const ProString &val, int flags=0, int indent_level=0)
{ return writeSettings(var, ProStringList(val), flags, indent_level); }
QString writeSettings(const QString &var, const ProStringList &vals, int flags=0, int indent_level=0);
public:
bool supportsMetaBuild() override { return false; }
bool openOutput(QFile &, const QString &) const override;
protected:
bool doPrecompiledHeaders() const override { return false; }
bool doDepends() const override { return writingUnixMakefileGenerator && UnixMakefileGenerator::doDepends(); }
bool inhibitMakeDirOutPath(const ProKey &path) const override;
};
QT_END_NAMESPACE
#endif // PBUILDER_PBX_H

File diff suppressed because it is too large Load Diff

314
qmake/generators/makefile.h Normal file
View File

@ -0,0 +1,314 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MAKEFILE_H
#define MAKEFILE_H
#include "option.h"
#include "project.h"
#include "makefiledeps.h"
#include <qtextstream.h>
#include <qlist.h>
#include <qhash.h>
#include <qfileinfo.h>
#include <functional>
QT_BEGIN_NAMESPACE
#ifdef Q_OS_WIN32
#define QT_POPEN _popen
#define QT_POPEN_READ "rb"
#define QT_PCLOSE _pclose
#else
#define QT_POPEN popen
#define QT_POPEN_READ "r"
#define QT_PCLOSE pclose
#endif
struct ReplaceExtraCompilerCacheKey;
class MakefileGenerator : protected QMakeSourceFileInfo
{
QString spec;
bool no_io = false;
bool resolveDependenciesInFrameworks = false;
QHash<QString, bool> init_compiler_already;
QString makedir, chkexists;
QString fullBuildArgs();
//internal caches
mutable QHash<QString, QMakeLocalFileName> depHeuristicsCache;
mutable QHash<QString, QStringList> dependsCache;
mutable QHash<ReplaceExtraCompilerCacheKey, QString> extraCompilerVariablesCache;
public:
// We can't make it visible to VCFilter in VS2008 except by making it public or directly friending it.
enum ReplaceFor { NoShell, LocalShell, TargetShell };
protected:
enum TARG_MODE { TARG_UNIX_MODE, TARG_MAC_MODE, TARG_WIN_MODE } target_mode;
ProStringList createObjectList(const ProStringList &sources);
//makefile style generator functions
void writeObj(QTextStream &, const char *src);
void writeInstalls(QTextStream &t, bool noBuild=false);
void writeHeader(QTextStream &t);
void writeSubDirs(QTextStream &t);
void writeMakeQmake(QTextStream &t, bool noDummyQmakeAll = false);
void writeExportedVariables(QTextStream &t);
void writeExtraVariables(QTextStream &t);
void writeExtraTargets(QTextStream &t);
QString resolveDependency(const QDir &outDir, const QString &file);
void callExtraCompilerDependCommand(const ProString &extraCompiler,
const QString &tmp_dep_cmd, const QString &inpf,
const QString &tmp_out, bool dep_lines, QStringList *deps,
bool existingDepsOnly,
bool checkCommandAvailability = false);
void writeExtraCompilerTargets(QTextStream &t);
void writeExtraCompilerVariables(QTextStream &t);
bool writeDummyMakefile(QTextStream &t);
virtual bool writeMakefile(QTextStream &t);
virtual void writeDefaultVariables(QTextStream &t);
QString pkgConfigPrefix() const;
QString pkgConfigFileName(bool fixify=true);
QString pkgConfigFixPath(QString) const;
void writePkgConfigFile(); // for pkg-config
//generating subtarget makefiles
struct SubTarget
{
QString name;
QString in_directory, out_directory;
QString profile, target, makefile;
ProStringList depends;
};
enum SubTargetFlags {
SubTargetInstalls=0x01,
SubTargetOrdered=0x02,
SubTargetSkipDefaultVariables=0x04,
SubTargetSkipDefaultTargets=0x08,
SubTargetsNoFlags=0x00
};
QList<MakefileGenerator::SubTarget*> findSubDirsSubTargets() const;
void writeSubTargetCall(QTextStream &t,
const QString &in_directory, const QString &in, const QString &out_directory, const QString &out,
const QString &out_directory_cdin, const QString &makefilein);
virtual void suppressBuiltinRules(QTextStream &t) const;
virtual void writeSubMakeCall(QTextStream &t, const QString &outDirectory_cdin,
const QString &makeFileIn);
virtual void writeSubTargets(QTextStream &t, QList<SubTarget*> subtargets, int flags);
virtual ProStringList extraSubTargetDependencies() { return {}; }
//extra compiler interface
bool verifyExtraCompiler(const ProString &c, const QString &f);
virtual QString replaceExtraCompilerVariables(const QString &, const QStringList &, const QStringList &, ReplaceFor forShell);
inline QString replaceExtraCompilerVariables(const QString &val, const QString &in, const QString &out, ReplaceFor forShell)
{ return replaceExtraCompilerVariables(val, QStringList(in), QStringList(out), forShell); }
//interface to the source file info
QMakeLocalFileName fixPathForFile(const QMakeLocalFileName &, bool) override;
QMakeLocalFileName findFileForDep(const QMakeLocalFileName &, const QMakeLocalFileName &) override;
QFileInfo findFileInfo(const QMakeLocalFileName &) override;
QMakeProject *project = nullptr;
//escape
virtual QString escapeFilePath(const QString &path) const = 0;
ProString escapeFilePath(const ProString &path) const;
template<typename A, typename B>
QString escapeFilePath(const QStringBuilder<A, B> &path) const
{ return escapeFilePath(QString(path)); }
QStringList escapeFilePaths(const QStringList &paths) const;
ProStringList escapeFilePaths(const ProStringList &paths) const;
virtual QString escapeDependencyPath(const QString &path) const;
ProString escapeDependencyPath(const ProString &path) const;
template<typename A, typename B>
QString escapeDependencyPath(const QStringBuilder<A, B> &path) const
{ return escapeDependencyPath(QString(path)); }
QStringList escapeDependencyPaths(const QStringList &paths) const;
ProStringList escapeDependencyPaths(const ProStringList &paths) const;
QStringList finalizeDependencyPaths(const QStringList &paths) const;
//initialization
void verifyCompilers();
virtual void init();
void initOutPaths();
virtual bool inhibitMakeDirOutPath(const ProKey &path) const;
struct Compiler
{
QString variable_in;
enum CompilerFlag {
CompilerNoFlags = 0x00,
CompilerBuiltin = 0x01,
CompilerNoCheckDeps = 0x02,
CompilerRemoveNoExist = 0x04,
CompilerWarnNoExist = 0x08,
CompilerAddInputsAsMakefileDeps = 0x10
};
uint flags, type;
};
friend class QTypeInfo<Compiler>;
void initCompiler(const Compiler &comp);
enum VPATHFlag {
VPATH_NoFlag = 0x00,
VPATH_WarnMissingFiles = 0x01,
VPATH_RemoveMissingFiles = 0x02,
VPATH_NoFixify = 0x04
};
ProStringList findFilesInVPATH(ProStringList l, uchar flags, const QString &var="");
inline int findExecutable(const QStringList &cmdline)
{ int ret; canExecute(cmdline, &ret); return ret; }
bool canExecute(const QStringList &cmdline, int *argv0) const;
inline bool canExecute(const QString &cmdline) const
{ return canExecute(cmdline.split(' '), nullptr); }
bool mkdir(const QString &dir) const;
QString mkdir_p_asstring(const QString &dir, bool escape=true) const;
QString specdir();
//subclasses can use these to query information about how the generator was "run"
QString buildArgs(bool withExtra);
virtual QStringList &findDependencies(const QString &file);
virtual bool doDepends() const { return Option::mkfile::do_deps; }
void filterIncludedFiles(const char *);
void processSources();
//for installs
virtual QString defaultInstall(const QString &);
virtual QString installRoot() const;
//for prl
QString prlFileName(bool fixify=true);
void writePrlFile();
bool processPrlFile(QString &, bool baseOnly);
virtual void writePrlFile(QTextStream &);
//make sure libraries are found
virtual bool findLibraries(bool linkPrl, bool mergeLflags);
//for retrieving values and lists of values
virtual QString var(const ProKey &var) const;
QString varGlue(const ProKey &var, const QString &before, const QString &glue, const QString &after) const;
QString varList(const ProKey &var) const;
QString fixFileVarGlue(const ProKey &var, const QString &before, const QString &glue, const QString &after) const;
QString fileVarList(const ProKey &var) const;
QString fileVarGlue(const ProKey &var, const QString &before, const QString &glue, const QString &after) const;
QString fileVar(const ProKey &var) const;
QString depVar(const ProKey &var) const;
QString val(const ProStringList &varList) const;
QString val(const QStringList &varList) const;
QString valGlue(const QStringList &varList, const QString &before, const QString &glue, const QString &after) const;
QString valGlue(const ProStringList &varList, const QString &before, const QString &glue, const QString &after) const;
QString valList(const QStringList &varList) const;
QString valList(const ProStringList &varList) const;
QString filePrefixRoot(const QString &, const QString &);
enum LibFlagType { LibFlagLib, LibFlagPath, LibFlagFile, LibFlagOther };
virtual LibFlagType parseLibFlag(const ProString &flag, ProString *arg);
ProStringList fixLibFlags(const ProKey &var);
virtual ProString fixLibFlag(const ProString &lib);
public:
//file fixification to unify all file names into a single pattern
enum FileFixifyType {
FileFixifyFromIndir = 0,
FileFixifyFromOutdir = 1,
FileFixifyToOutDir = 0,
FileFixifyToIndir = 2,
FileFixifyBackwards = FileFixifyFromOutdir | FileFixifyToIndir,
FileFixifyDefault = 0,
FileFixifyAbsolute = 4,
FileFixifyRelative = 8
};
Q_DECLARE_FLAGS(FileFixifyTypes, FileFixifyType)
protected:
QString fileFixify(const QString &file, FileFixifyTypes fix = FileFixifyDefault, bool canon = true) const;
QStringList fileFixify(const QStringList &files, FileFixifyTypes fix = FileFixifyDefault, bool canon = true) const;
QString createSedArgs(const ProKey &replace_rule, const QString &file_type = QString()) const;
QString installMetaFile(const ProKey &replace_rule, const QString &src,
const QString &dst) const;
virtual bool processPrlFileBase(QString &origFile, QStringView origName,
QStringView fixedBase, int slashOff);
bool processPrlFileCore(QString &origFile, QStringView origName,
const QString &fixedFile);
QString createResponseFile(const QString &baseName,
const ProStringList &objList,
const QString &prefix = QString()) const;
struct LinkerResponseFileInfo
{
QString filePath;
bool onlyObjects;
bool isValid() const { return !filePath.isEmpty(); }
};
LinkerResponseFileInfo maybeCreateLinkerResponseFile() const;
public:
QMakeProject *projectFile() const;
void setProjectFile(QMakeProject *p);
void setNoIO(bool o);
bool noIO() const;
inline bool exists(QString file) const { return fileInfo(file).exists(); }
QFileInfo fileInfo(QString file) const;
static MakefileGenerator *create(QMakeProject *);
virtual bool write();
virtual bool writeProjectMakefile();
virtual bool supportsMetaBuild() { return true; }
virtual bool supportsMergedBuilds() { return false; }
virtual bool mergeBuildProject(MakefileGenerator * /*other*/) { return false; }
virtual bool openOutput(QFile &, const QString &build) const;
bool isWindowsShell() const { return Option::dir_sep == QLatin1String("\\"); }
QString shellQuote(const QString &str) const;
virtual ProKey fullTargetVariable() const;
};
Q_DECLARE_TYPEINFO(MakefileGenerator::Compiler, Q_RELOCATABLE_TYPE);
Q_DECLARE_OPERATORS_FOR_FLAGS(MakefileGenerator::FileFixifyTypes)
inline void MakefileGenerator::setNoIO(bool o)
{ no_io = o; }
inline bool MakefileGenerator::noIO() const
{ return no_io; }
inline QString MakefileGenerator::defaultInstall(const QString &)
{ return QString(""); }
inline QString MakefileGenerator::installRoot() const
{ return QStringLiteral("$(INSTALL_ROOT)"); }
inline bool MakefileGenerator::findLibraries(bool, bool)
{ return true; }
struct ReplaceExtraCompilerCacheKey
{
mutable size_t hash;
QString var, in, out, pwd;
MakefileGenerator::ReplaceFor forShell;
ReplaceExtraCompilerCacheKey(const QString &v, const QStringList &i, const QStringList &o, MakefileGenerator::ReplaceFor s);
bool operator==(const ReplaceExtraCompilerCacheKey &f) const;
inline size_t hashCode() const {
if (!hash)
hash = (size_t)forShell ^ qHash(var) ^ qHash(in) ^ qHash(out) /*^ qHash(pwd)*/;
return hash;
}
};
inline size_t qHash(const ReplaceExtraCompilerCacheKey &f) { return f.hashCode(); }
QT_END_NAMESPACE
#endif // MAKEFILE_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MAKEFILEDEPS_H
#define MAKEFILEDEPS_H
#include <proitems.h>
#include <qfileinfo.h>
#include <qlist.h>
#include <qstringlist.h>
QT_BEGIN_NAMESPACE
struct SourceFile;
struct SourceDependChildren;
class SourceFiles;
class QMakeLocalFileName
{
QString real_name;
mutable QString local_name;
public:
QMakeLocalFileName() = default;
QMakeLocalFileName(const QString &);
bool isNull() const { return real_name.isNull(); }
inline const QString &real() const { return real_name; }
const QString &local() const;
bool operator==(const QMakeLocalFileName &other) const {
return (this->real_name == other.real_name);
}
bool operator!=(const QMakeLocalFileName &other) const {
return !(*this == other);
}
};
class QMakeSourceFileInfo
{
private:
//quick project lookups
SourceFiles *files, *includes;
bool files_changed;
QList<QMakeLocalFileName> depdirs;
QStringList systemIncludes;
//sleezy buffer code
char *spare_buffer;
int spare_buffer_size;
char *getBuffer(int s);
//actual guts
bool findMocs(SourceFile *);
bool findDeps(SourceFile *);
void dependTreeWalker(SourceFile *, SourceDependChildren *);
protected:
virtual QMakeLocalFileName fixPathForFile(const QMakeLocalFileName &, bool forOpen=false);
virtual QMakeLocalFileName findFileForDep(const QMakeLocalFileName &, const QMakeLocalFileName &);
virtual QFileInfo findFileInfo(const QMakeLocalFileName &);
public:
QMakeSourceFileInfo();
virtual ~QMakeSourceFileInfo();
QList<QMakeLocalFileName> dependencyPaths() const { return depdirs; }
void setDependencyPaths(const QList<QMakeLocalFileName> &);
enum DependencyMode { Recursive, NonRecursive };
inline void setDependencyMode(DependencyMode mode) { dep_mode = mode; }
inline DependencyMode dependencyMode() const { return dep_mode; }
void setSystemIncludes(const ProStringList &list)
{ systemIncludes = list.toQStringList(); }
enum SourceFileType { TYPE_UNKNOWN, TYPE_C, TYPE_UI, TYPE_QRC };
enum SourceFileSeek { SEEK_DEPS=0x01, SEEK_MOCS=0x02 };
void addSourceFiles(const ProStringList &, uchar seek, SourceFileType type=TYPE_C);
void addSourceFile(const QString &, uchar seek, SourceFileType type=TYPE_C);
bool containsSourceFile(const QString &, SourceFileType type=TYPE_C);
bool isSystemInclude(const QString &);
int included(const QString &file);
QStringList dependencies(const QString &file);
bool mocable(const QString &file);
private:
DependencyMode dep_mode;
};
QT_END_NAMESPACE
#endif // MAKEFILEDEPS_H

View File

@ -0,0 +1,501 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "metamakefile.h"
#include "qdir.h"
#include "qdebug.h"
#include "makefile.h"
#include "project.h"
#include "cachekeys.h"
#include <algorithm>
#include <iterator>
#include <utility>
#define BUILDSMETATYPE 1
#define SUBDIRSMETATYPE 2
QT_BEGIN_NAMESPACE
MetaMakefileGenerator::~MetaMakefileGenerator()
{
if(own_project)
delete project;
}
#ifndef QT_QMAKE_PARSER_ONLY
class BuildsMetaMakefileGenerator : public MetaMakefileGenerator
{
private:
bool init_flag;
struct Build {
QString name, build;
MakefileGenerator *makefile;
};
QList<Build *> makefiles;
void clearBuilds();
MakefileGenerator *processBuild(const ProString &);
void accumulateVariableFromBuilds(const ProKey &name, Build *build) const;
void checkForConflictingTargets() const;
public:
BuildsMetaMakefileGenerator(QMakeProject *p, const QString &n, bool op) : MetaMakefileGenerator(p, n, op), init_flag(false) { }
~BuildsMetaMakefileGenerator() { clearBuilds(); }
bool init() override;
int type() const override { return BUILDSMETATYPE; }
bool write() override;
};
void
BuildsMetaMakefileGenerator::clearBuilds()
{
for(int i = 0; i < makefiles.size(); i++) {
Build *build = makefiles[i];
if(QMakeProject *p = build->makefile->projectFile()) {
if(p != project)
delete p;
}
delete build->makefile;
delete build;
}
makefiles.clear();
}
bool
BuildsMetaMakefileGenerator::init()
{
if(init_flag)
return false;
init_flag = true;
const ProStringList &builds = project->values("BUILDS");
bool use_single_build = builds.isEmpty();
if(builds.size() > 1 && Option::output.fileName() == "-") {
use_single_build = true;
warn_msg(WarnLogic, "Cannot direct to stdout when using multiple BUILDS.");
}
if(!use_single_build) {
for(int i = 0; i < builds.size(); i++) {
ProString build = builds[i];
MakefileGenerator *makefile = processBuild(build);
if(!makefile)
return false;
if(!makefile->supportsMetaBuild()) {
warn_msg(WarnLogic, "QMAKESPEC does not support multiple BUILDS.");
clearBuilds();
use_single_build = true;
break;
} else {
Build *b = new Build;
b->name = name;
if(builds.size() != 1)
b->build = build.toQString();
b->makefile = makefile;
makefiles += b;
}
}
}
if(use_single_build) {
Build *build = new Build;
build->name = name;
build->makefile = createMakefileGenerator(project, false);
if (build->makefile){
makefiles += build;
}else {
delete build;
return false;
}
}
return true;
}
bool
BuildsMetaMakefileGenerator::write()
{
Build *glue = nullptr;
if(!makefiles.isEmpty() && !makefiles.first()->build.isNull()
&& Option::qmake_mode != Option::QMAKE_GENERATE_PRL) {
glue = new Build;
glue->name = name;
glue->makefile = createMakefileGenerator(project, true);
makefiles += glue;
}
bool ret = true;
const QString &output_name = Option::output.fileName();
for(int i = 0; ret && i < makefiles.size(); i++) {
Option::output.setFileName(output_name);
Build *build = makefiles[i];
bool using_stdout = false;
if(build->makefile && (Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT)
&& (!build->makefile->supportsMergedBuilds()
|| (build->makefile->supportsMergedBuilds() && (!glue || build == glue)))) {
//open output
if(!(Option::output.isOpen())) {
if(Option::output.fileName() == "-") {
Option::output.setFileName("");
Option::output_dir = qmake_getpwd();
Option::output.open(stdout, QIODevice::WriteOnly | QIODevice::Text);
using_stdout = true;
} else {
if(Option::output.fileName().isEmpty() &&
Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE)
Option::output.setFileName(project->first("QMAKE_MAKEFILE").toQString());
QString build_name = build->name;
if(!build->build.isEmpty()) {
if(!build_name.isEmpty())
build_name += ".";
build_name += build->build;
}
if(!build->makefile->openOutput(Option::output, build_name)) {
fprintf(stderr, "Failure to open file: %s\n",
Option::output.fileName().isEmpty() ? "(stdout)" :
Option::output.fileName().toLatin1().constData());
return false;
}
}
}
} else {
using_stdout = true; //kind of..
}
if(!build->makefile) {
ret = false;
} else if(build == glue) {
checkForConflictingTargets();
accumulateVariableFromBuilds("QMAKE_INTERNAL_INCLUDED_FILES", build);
ret = build->makefile->writeProjectMakefile();
} else {
ret = build->makefile->write();
if (glue && glue->makefile->supportsMergedBuilds())
ret = glue->makefile->mergeBuildProject(build->makefile);
}
if(!using_stdout) {
Option::output.close();
if(!ret)
Option::output.remove();
}
}
return ret;
}
MakefileGenerator
*BuildsMetaMakefileGenerator::processBuild(const ProString &build)
{
if(project) {
debug_msg(1, "Meta Generator: Parsing '%s' for build [%s].",
project->projectFile().toLatin1().constData(),build.toLatin1().constData());
//initialize the base
ProValueMap basevars;
ProStringList basecfgs = project->values(ProKey(build + ".CONFIG"));
basecfgs += build;
basecfgs += "build_pass";
basevars["BUILD_PASS"] = ProStringList(build);
ProStringList buildname = project->values(ProKey(build + ".name"));
basevars["BUILD_NAME"] = (buildname.isEmpty() ? ProStringList(build) : buildname);
//create project
QMakeProject *build_proj = new QMakeProject;
build_proj->setExtraVars(basevars);
build_proj->setExtraConfigs(basecfgs);
if (build_proj->read(project->projectFile()))
return createMakefileGenerator(build_proj);
}
return nullptr;
}
void BuildsMetaMakefileGenerator::accumulateVariableFromBuilds(const ProKey &name, Build *dst) const
{
ProStringList &values = dst->makefile->projectFile()->values(name);
for (auto build : makefiles) {
if (build != dst)
values += build->makefile->projectFile()->values(name);
}
values.removeDuplicates();
}
void BuildsMetaMakefileGenerator::checkForConflictingTargets() const
{
if (makefiles.size() < 3) {
// Checking for conflicts only makes sense if we have more than one BUILD,
// and the last entry in makefiles is the "glue" Build.
return;
}
if (!project->isActiveConfig("build_all")) {
// Only complain if we're about to build all configurations.
return;
}
using TargetInfo = std::pair<Build *, ProString>;
QList<TargetInfo> targets;
const int last = makefiles.size() - 1;
targets.resize(last);
for (int i = 0; i < last; ++i) {
Build *b = makefiles.at(i);
auto mkf = b->makefile;
auto prj = mkf->projectFile();
targets[i] = std::make_pair(b, prj->first(mkf->fullTargetVariable()));
}
std::stable_sort(targets.begin(), targets.end(),
[](const TargetInfo &lhs, const TargetInfo &rhs)
{
return lhs.second < rhs.second;
});
for (auto prev = targets.begin(), it = std::next(prev); it != targets.end(); ++prev, ++it) {
if (prev->second == it->second) {
warn_msg(WarnLogic, "Targets of builds '%s' and '%s' conflict: %s.",
qPrintable(prev->first->build),
qPrintable(it->first->build),
qPrintable(prev->second.toQString()));
break;
}
}
}
class SubdirsMetaMakefileGenerator : public MetaMakefileGenerator
{
protected:
bool init_flag;
struct Subdir {
Subdir() : makefile(nullptr), indent(0) { }
~Subdir() { delete makefile; }
QString input_dir;
QString output_dir, output_file;
MetaMakefileGenerator *makefile;
int indent;
};
QList<Subdir *> subs;
MakefileGenerator *processBuild(const QString &);
public:
SubdirsMetaMakefileGenerator(QMakeProject *p, const QString &n, bool op) : MetaMakefileGenerator(p, n, op), init_flag(false) { }
~SubdirsMetaMakefileGenerator();
bool init() override;
int type() const override { return SUBDIRSMETATYPE; }
bool write() override;
};
bool
SubdirsMetaMakefileGenerator::init()
{
if(init_flag)
return false;
init_flag = true;
bool hasError = false;
bool recurse = Option::recursive;
if (recurse && project->isActiveConfig("dont_recurse"))
recurse = false;
if(recurse) {
QString old_output_dir = Option::output_dir;
QString old_output = Option::output.fileName();
QString oldpwd = qmake_getpwd();
QString thispwd = oldpwd;
if(!thispwd.endsWith('/'))
thispwd += '/';
const ProStringList &subdirs = project->values("SUBDIRS");
Q_CONSTINIT static int recurseDepth = -1;
++recurseDepth;
for(int i = 0; i < subdirs.size(); ++i) {
Subdir *sub = new Subdir;
sub->indent = recurseDepth;
QFileInfo subdir(subdirs.at(i).toQString());
const ProKey fkey(subdirs.at(i) + ".file");
if (!project->isEmpty(fkey)) {
subdir = QFileInfo(project->first(fkey).toQString());
} else {
const ProKey skey(subdirs.at(i) + ".subdir");
if (!project->isEmpty(skey))
subdir = QFileInfo(project->first(skey).toQString());
}
QString sub_name;
if(subdir.isDir())
subdir = QFileInfo(subdir.filePath() + "/" + subdir.fileName() + Option::pro_ext);
else
sub_name = subdir.baseName();
if(!subdir.isRelative()) { //we can try to make it relative
QString subdir_path = subdir.filePath();
if(subdir_path.startsWith(thispwd))
subdir = QFileInfo(subdir_path.mid(thispwd.size()));
}
//handle sub project
QMakeProject *sub_proj = new QMakeProject;
for (int ind = 0; ind < sub->indent; ++ind)
printf(" ");
sub->input_dir = subdir.absolutePath();
if(subdir.isRelative() && old_output_dir != oldpwd) {
sub->output_dir = old_output_dir + (subdir.path() != "." ? "/" + subdir.path() : QString());
printf("Reading %s [%s]\n", subdir.absoluteFilePath().toLatin1().constData(), sub->output_dir.toLatin1().constData());
} else { //what about shadow builds?
sub->output_dir = sub->input_dir;
printf("Reading %s\n", subdir.absoluteFilePath().toLatin1().constData());
}
qmake_setpwd(sub->input_dir);
Option::output_dir = sub->output_dir;
bool tmpError = !sub_proj->read(subdir.fileName());
if (!sub_proj->isEmpty("QMAKE_FAILED_REQUIREMENTS")) {
fprintf(stderr, "Project file(%s) not recursed because all requirements not met:\n\t%s\n",
subdir.fileName().toLatin1().constData(),
sub_proj->values("QMAKE_FAILED_REQUIREMENTS").join(' ').toLatin1().constData());
delete sub;
delete sub_proj;
Option::output_dir = old_output_dir;
qmake_setpwd(oldpwd);
continue;
} else {
hasError |= tmpError;
}
sub->makefile = MetaMakefileGenerator::createMetaGenerator(sub_proj, sub_name);
const QString output_name = Option::output.fileName();
Option::output.setFileName(sub->output_file);
hasError |= !sub->makefile->write();
delete sub;
qmakeClearCaches();
sub = nullptr;
Option::output.setFileName(output_name);
Option::output_dir = old_output_dir;
qmake_setpwd(oldpwd);
}
--recurseDepth;
Option::output.setFileName(old_output);
Option::output_dir = old_output_dir;
qmake_setpwd(oldpwd);
}
Subdir *self = new Subdir;
self->input_dir = qmake_getpwd();
self->output_dir = Option::output_dir;
if(!recurse || (!Option::output.fileName().endsWith(Option::dir_sep) && !QFileInfo(Option::output).isDir()))
self->output_file = Option::output.fileName();
self->makefile = new BuildsMetaMakefileGenerator(project, name, false);
self->makefile->init();
subs.append(self);
return !hasError;
}
bool
SubdirsMetaMakefileGenerator::write()
{
bool ret = true;
const QString &pwd = qmake_getpwd();
const QString &output_dir = Option::output_dir;
const QString &output_name = Option::output.fileName();
for(int i = 0; ret && i < subs.size(); i++) {
const Subdir *sub = subs.at(i);
qmake_setpwd(sub->input_dir);
Option::output_dir = QFileInfo(sub->output_dir).absoluteFilePath();
Option::output.setFileName(sub->output_file);
if(i != subs.size()-1) {
for (int ind = 0; ind < sub->indent; ++ind)
printf(" ");
printf("Writing %s\n", QDir::cleanPath(Option::output_dir+"/"+
Option::output.fileName()).toLatin1().constData());
}
if (!(ret = sub->makefile->write()))
break;
//restore because I'm paranoid
qmake_setpwd(pwd);
Option::output.setFileName(output_name);
Option::output_dir = output_dir;
}
return ret;
}
SubdirsMetaMakefileGenerator::~SubdirsMetaMakefileGenerator()
{
for(int i = 0; i < subs.size(); i++)
delete subs[i];
subs.clear();
}
//Factory things
QT_BEGIN_INCLUDE_NAMESPACE
#include "unixmake.h"
#include "mingw_make.h"
#include "projectgenerator.h"
#include "pbuilder_pbx.h"
#include "msvc_nmake.h"
#include "msvc_vcproj.h"
#include "msvc_vcxproj.h"
QT_END_INCLUDE_NAMESPACE
MakefileGenerator *
MetaMakefileGenerator::createMakefileGenerator(QMakeProject *proj, bool noIO)
{
Option::postProcessProject(proj);
MakefileGenerator *mkfile = nullptr;
if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
mkfile = new ProjectGenerator;
mkfile->setProjectFile(proj);
return mkfile;
}
ProString gen = proj->first("MAKEFILE_GENERATOR");
if(gen.isEmpty()) {
fprintf(stderr, "MAKEFILE_GENERATOR variable not set as a result of parsing : %s. Possibly qmake was not able to find files included using \"include(..)\" - enable qmake debugging to investigate more.\n",
proj->projectFile().toLatin1().constData());
} else if(gen == "UNIX") {
mkfile = new UnixMakefileGenerator;
} else if(gen == "MINGW") {
mkfile = new MingwMakefileGenerator;
} else if(gen == "PROJECTBUILDER" || gen == "XCODE") {
#ifdef Q_CC_MSVC
fprintf(stderr, "Generating Xcode projects is not supported with an MSVC build of Qt.\n");
#else
mkfile = new ProjectBuilderMakefileGenerator;
#endif
} else if(gen == "MSVC.NET") {
if (proj->first("TEMPLATE").startsWith("vc"))
mkfile = new VcprojGenerator;
else
mkfile = new NmakeMakefileGenerator;
} else if(gen == "MSBUILD") {
// Visual Studio >= v11.0
if (proj->first("TEMPLATE").startsWith("vc"))
mkfile = new VcxprojGenerator;
else
mkfile = new NmakeMakefileGenerator;
} else {
fprintf(stderr, "Unknown generator specified: %s\n", gen.toLatin1().constData());
}
if (mkfile) {
mkfile->setNoIO(noIO);
mkfile->setProjectFile(proj);
}
return mkfile;
}
MetaMakefileGenerator *
MetaMakefileGenerator::createMetaGenerator(QMakeProject *proj, const QString &name, bool op, bool *success)
{
Option::postProcessProject(proj);
MetaMakefileGenerator *ret = nullptr;
if ((Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
Option::qmake_mode == Option::QMAKE_GENERATE_PRL)) {
if (proj->first("TEMPLATE").endsWith("subdirs"))
ret = new SubdirsMetaMakefileGenerator(proj, name, op);
}
if (!ret)
ret = new BuildsMetaMakefileGenerator(proj, name, op);
bool res = ret->init();
if (success)
*success = res;
return ret;
}
#endif // QT_QMAKE_PARSER_ONLY
QT_END_NAMESPACE

View File

@ -0,0 +1,39 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef METAMAKEFILE_H
#define METAMAKEFILE_H
#include <qlist.h>
#include <qstring.h>
QT_BEGIN_NAMESPACE
class QMakeProject;
class MakefileGenerator;
class MetaMakefileGenerator
{
protected:
MetaMakefileGenerator(QMakeProject *p, const QString &n, bool op=true) : project(p), own_project(op), name(n) { }
QMakeProject *project;
bool own_project;
QString name;
public:
virtual ~MetaMakefileGenerator();
static MetaMakefileGenerator *createMetaGenerator(QMakeProject *proj, const QString &name, bool op=true, bool *success = nullptr);
static MakefileGenerator *createMakefileGenerator(QMakeProject *proj, bool noIO = false);
inline QMakeProject *projectFile() const { return project; }
virtual bool init() = 0;
virtual int type() const { return -1; }
virtual bool write() = 0;
};
QT_END_NAMESPACE
#endif // METAMAKEFILE_H

View File

@ -0,0 +1,462 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "projectgenerator.h"
#include "option.h"
#include <qdatetime.h>
#include <qdir.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qregularexpression.h>
QT_BEGIN_NAMESPACE
static QString project_builtin_regx() //calculate the builtin regular expression..
{
QString ret;
QStringList builtin_exts;
builtin_exts << Option::c_ext << Option::ui_ext << Option::yacc_ext << Option::lex_ext << ".ts" << ".xlf" << ".qrc";
builtin_exts += Option::h_ext + Option::cpp_ext;
for(int i = 0; i < builtin_exts.size(); ++i) {
if(!ret.isEmpty())
ret += "; ";
ret += QString("*") + builtin_exts[i];
}
return ret;
}
void
ProjectGenerator::init()
{
int file_count = 0;
verifyCompilers();
project->loadSpec();
project->evaluateFeatureFile("default_pre.prf");
project->evaluateFeatureFile("default_post.prf");
project->evaluateConfigFeatures();
project->values("CONFIG").clear();
Option::postProcessProject(project);
ProValueMap &v = project->variables();
QString templ = Option::globals->user_template.isEmpty() ? QString("app") : Option::globals->user_template;
if (!Option::globals->user_template_prefix.isEmpty())
templ.prepend(Option::globals->user_template_prefix);
v["TEMPLATE_ASSIGN"] += templ;
//the scary stuff
if(project->first("TEMPLATE_ASSIGN") != "subdirs") {
QString builtin_regex = project_builtin_regx();
QStringList dirs = Option::projfile::project_dirs;
if(Option::projfile::do_pwd) {
if(!v["INCLUDEPATH"].contains("."))
v["INCLUDEPATH"] += ".";
dirs.prepend(qmake_getpwd());
}
for(int i = 0; i < dirs.size(); ++i) {
QString dir, regex, pd = dirs.at(i);
bool add_depend = false;
if(exists(pd)) {
QFileInfo fi(fileInfo(pd));
if(fi.isDir()) {
dir = pd;
add_depend = true;
if(dir.right(1) != Option::dir_sep)
dir += Option::dir_sep;
if (Option::recursive) {
QStringList files = QDir(dir).entryList(QDir::Files);
for (int i = 0; i < files.size(); i++)
dirs.append(dir + files[i] + QDir::separator() + builtin_regex);
}
regex = builtin_regex;
} else {
QString file = pd;
int s = file.lastIndexOf(Option::dir_sep);
if(s != -1)
dir = file.left(s+1);
if(addFile(file)) {
add_depend = true;
file_count++;
}
}
} else { //regexp
regex = pd;
}
if(!regex.isEmpty()) {
int s = regex.lastIndexOf(Option::dir_sep);
if(s != -1) {
dir = regex.left(s+1);
regex = regex.right(regex.size() - (s+1));
}
const QDir d(dir);
if (Option::recursive) {
QStringList entries = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
for (int i = 0; i < entries.size(); i++)
dirs.append(dir + entries[i] + QDir::separator() + regex);
}
QStringList files = d.entryList(QDir::nameFiltersFromString(regex));
for(int i = 0; i < (int)files.size(); i++) {
QString file = d.absoluteFilePath(files[i]);
if (addFile(file)) {
add_depend = true;
file_count++;
}
}
}
if(add_depend && !dir.isEmpty() && !v["DEPENDPATH"].contains(dir, Qt::CaseInsensitive)) {
QFileInfo fi(fileInfo(dir));
if(fi.absoluteFilePath() != qmake_getpwd())
v["DEPENDPATH"] += fileFixify(dir);
}
}
}
if(!file_count) { //shall we try a subdir?
QStringList knownDirs = Option::projfile::project_dirs;
if(Option::projfile::do_pwd)
knownDirs.prepend(".");
const QString out_file = fileFixify(Option::output.fileName());
for(int i = 0; i < knownDirs.size(); ++i) {
QString pd = knownDirs.at(i);
if(exists(pd)) {
QString newdir = pd;
QFileInfo fi(fileInfo(newdir));
if(fi.isDir()) {
newdir = fileFixify(newdir, FileFixifyFromOutdir);
ProStringList &subdirs = v["SUBDIRS"];
if(exists(fi.filePath() + QDir::separator() + fi.fileName() + Option::pro_ext) &&
!subdirs.contains(newdir, Qt::CaseInsensitive)) {
subdirs.append(newdir);
} else {
QStringList profiles = QDir(newdir).entryList(QStringList("*" + Option::pro_ext), QDir::Files);
for(int i = 0; i < (int)profiles.size(); i++) {
QString nd = newdir;
if(nd == ".")
nd = "";
else if (!nd.isEmpty() && !nd.endsWith(QDir::separator()))
nd += QDir::separator();
nd += profiles[i];
fileFixify(nd);
if (!subdirs.contains(nd, Qt::CaseInsensitive) && !out_file.endsWith(nd))
subdirs.append(nd);
}
}
if (Option::recursive) {
QStringList dirs = QDir(newdir).entryList(QDir::Dirs | QDir::NoDotAndDotDot);
for(int i = 0; i < (int)dirs.size(); i++) {
QString nd = fileFixify(newdir + QDir::separator() + dirs[i]);
if (!knownDirs.contains(nd, Qt::CaseInsensitive))
knownDirs.append(nd);
}
}
}
} else { //regexp
QString regx = pd, dir;
int s = regx.lastIndexOf(Option::dir_sep);
if(s != -1) {
dir = regx.left(s+1);
regx = regx.right(regx.size() - (s+1));
}
QStringList files = QDir(dir).entryList(QDir::nameFiltersFromString(regx),
QDir::Dirs | QDir::NoDotAndDotDot);
ProStringList &subdirs = v["SUBDIRS"];
for(int i = 0; i < (int)files.size(); i++) {
QString newdir(dir + files[i]);
QFileInfo fi(fileInfo(newdir));
{
newdir = fileFixify(newdir);
if(exists(fi.filePath() + QDir::separator() + fi.fileName() + Option::pro_ext) &&
!subdirs.contains(newdir)) {
subdirs.append(newdir);
} else {
QStringList profiles = QDir(newdir).entryList(QStringList("*" + Option::pro_ext), QDir::Files);
for(int i = 0; i < (int)profiles.size(); i++) {
QString nd = newdir + QDir::separator() + files[i];
fileFixify(nd);
if(files[i] != "." && files[i] != ".." && !subdirs.contains(nd, Qt::CaseInsensitive)) {
if(newdir + files[i] != Option::output_dir + Option::output.fileName())
subdirs.append(nd);
}
}
}
if (Option::recursive && !knownDirs.contains(newdir, Qt::CaseInsensitive))
knownDirs.append(newdir);
}
}
}
}
v["TEMPLATE_ASSIGN"] = ProStringList("subdirs");
return;
}
//setup deplist
QList<QMakeLocalFileName> deplist;
{
const ProStringList &d = v["DEPENDPATH"];
for(int i = 0; i < d.size(); ++i)
deplist.append(QMakeLocalFileName(d[i].toQString()));
}
setDependencyPaths(deplist);
ProStringList &h = v["HEADERS"];
bool no_qt_files = true;
static const char *srcs[] = { "SOURCES", "YACCSOURCES", "LEXSOURCES", "FORMS", nullptr };
for (int i = 0; srcs[i]; i++) {
const ProStringList &l = v[srcs[i]];
QMakeSourceFileInfo::SourceFileType type = QMakeSourceFileInfo::TYPE_C;
QMakeSourceFileInfo::addSourceFiles(l, QMakeSourceFileInfo::SEEK_DEPS, type);
for(int i = 0; i < l.size(); ++i) {
QStringList tmp = QMakeSourceFileInfo::dependencies(l.at(i).toQString());
if(!tmp.isEmpty()) {
for(int dep_it = 0; dep_it < tmp.size(); ++dep_it) {
QString dep = tmp[dep_it];
dep = fixPathToQmake(dep);
QString file_dir = dep.section(Option::dir_sep, 0, -2),
file_no_path = dep.section(Option::dir_sep, -1);
if(!file_dir.isEmpty()) {
for(int inc_it = 0; inc_it < deplist.size(); ++inc_it) {
QMakeLocalFileName inc = deplist[inc_it];
if(inc.local() == file_dir && !v["INCLUDEPATH"].contains(inc.real(), Qt::CaseInsensitive))
v["INCLUDEPATH"] += inc.real();
}
}
if (no_qt_files && file_no_path.contains(QRegularExpression("^q[a-z_0-9].h$")))
no_qt_files = false;
QString h_ext;
for(int hit = 0; hit < Option::h_ext.size(); ++hit) {
if(dep.endsWith(Option::h_ext.at(hit))) {
h_ext = Option::h_ext.at(hit);
break;
}
}
if(!h_ext.isEmpty()) {
for(int cppit = 0; cppit < Option::cpp_ext.size(); ++cppit) {
QString src(dep.left(dep.size() - h_ext.size()) +
Option::cpp_ext.at(cppit));
if(exists(src)) {
ProStringList &srcl = v["SOURCES"];
if(!srcl.contains(src, Qt::CaseInsensitive))
srcl.append(src);
}
}
} else if(dep.endsWith(Option::lex_ext) &&
file_no_path.startsWith(Option::lex_mod)) {
addConfig("lex_included");
}
if(!h.contains(dep, Qt::CaseInsensitive))
h += dep;
}
}
}
}
//strip out files that are actually output from internal compilers (ie temporary files)
const ProStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
QString tmp_out = project->first(ProKey(*it + ".output")).toQString();
if(tmp_out.isEmpty())
continue;
ProStringList var_out = project->values(ProKey(*it + ".variable_out"));
bool defaults = var_out.isEmpty();
for(int i = 0; i < var_out.size(); ++i) {
ProString v = var_out.at(i);
if(v.startsWith("GENERATED_")) {
defaults = true;
break;
}
}
if(defaults) {
var_out << "SOURCES";
var_out << "HEADERS";
var_out << "FORMS";
}
const ProStringList &tmp = project->values(ProKey(*it + ".input"));
for (ProStringList::ConstIterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
ProStringList &inputs = project->values((*it2).toKey());
for (ProStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
QString path = replaceExtraCompilerVariables(tmp_out, (*input).toQString(), QString(), NoShell);
path = fixPathToQmake(path).section('/', -1);
for(int i = 0; i < var_out.size(); ++i) {
ProString v = var_out.at(i);
ProStringList &list = project->values(v.toKey());
for(int src = 0; src < list.size(); ) {
if(list[src] == path || list[src].endsWith("/" + path))
list.removeAt(src);
else
++src;
}
}
}
}
}
}
bool
ProjectGenerator::writeMakefile(QTextStream &t)
{
t << "######################################################################" << Qt::endl;
t << "# Automatically generated by qmake (" QMAKE_VERSION_STR ") " << QDateTime::currentDateTime().toString() << Qt::endl;
t << "######################################################################" << Qt::endl << Qt::endl;
if (!Option::globals->extra_cmds[QMakeEvalBefore].isEmpty())
t << Option::globals->extra_cmds[QMakeEvalBefore] << Qt::endl;
t << getWritableVar("TEMPLATE_ASSIGN", false);
if(project->first("TEMPLATE_ASSIGN") == "subdirs") {
t << Qt::endl << "# Directories" << "\n"
<< getWritableVar("SUBDIRS");
} else {
//figure out target
QString ofn = QFileInfo(static_cast<QFile *>(t.device())->fileName()).completeBaseName();
if (ofn.isEmpty() || ofn == "-")
ofn = "unknown";
project->values("TARGET_ASSIGN") = ProStringList(ofn);
t << getWritableVar("TARGET_ASSIGN")
<< getWritableVar("CONFIG", false)
<< getWritableVar("CONFIG_REMOVE", false)
<< getWritableVar("INCLUDEPATH") << Qt::endl;
t << "# You can make your code fail to compile if you use deprecated APIs.\n"
"# In order to do so, uncomment the following line.\n"
"# Please consult the documentation of the deprecated API in order to know\n"
"# how to port your code away from it.\n"
"# You can also select to disable deprecated APIs only up to a certain version of Qt.\n"
"#DEFINES += QT_DISABLE_DEPRECATED_UP_TO=0x060000 # disables all APIs deprecated in Qt 6.0.0 and earlier\n\n";
t << "# Input" << "\n";
t << getWritableVar("HEADERS")
<< getWritableVar("FORMS")
<< getWritableVar("LEXSOURCES")
<< getWritableVar("YACCSOURCES")
<< getWritableVar("SOURCES")
<< getWritableVar("RESOURCES")
<< getWritableVar("TRANSLATIONS");
}
if (!Option::globals->extra_cmds[QMakeEvalAfter].isEmpty())
t << Option::globals->extra_cmds[QMakeEvalAfter] << Qt::endl;
return true;
}
bool
ProjectGenerator::addConfig(const QString &cfg, bool add)
{
ProKey where = "CONFIG";
if(!add)
where = "CONFIG_REMOVE";
if (!project->values(where).contains(cfg)) {
project->values(where) += cfg;
return true;
}
return false;
}
bool
ProjectGenerator::addFile(QString file)
{
file = fileFixify(file, FileFixifyToIndir);
QString dir;
int s = file.lastIndexOf(Option::dir_sep);
if(s != -1)
dir = file.left(s+1);
if(file.mid(dir.size(), Option::h_moc_mod.size()) == Option::h_moc_mod)
return false;
ProKey where;
for(int cppit = 0; cppit < Option::cpp_ext.size(); ++cppit) {
if(file.endsWith(Option::cpp_ext[cppit])) {
where = "SOURCES";
break;
}
}
if(where.isEmpty()) {
for(int hit = 0; hit < Option::h_ext.size(); ++hit)
if(file.endsWith(Option::h_ext.at(hit))) {
where = "HEADERS";
break;
}
}
if(where.isEmpty()) {
for(int cit = 0; cit < Option::c_ext.size(); ++cit) {
if(file.endsWith(Option::c_ext[cit])) {
where = "SOURCES";
break;
}
}
}
if(where.isEmpty()) {
if(file.endsWith(Option::ui_ext))
where = "FORMS";
else if(file.endsWith(Option::lex_ext))
where = "LEXSOURCES";
else if(file.endsWith(Option::yacc_ext))
where = "YACCSOURCES";
else if(file.endsWith(".ts") || file.endsWith(".xlf"))
where = "TRANSLATIONS";
else if(file.endsWith(".qrc"))
where = "RESOURCES";
}
QString newfile = fixPathToQmake(fileFixify(file));
ProStringList &endList = project->values(where);
if(!endList.contains(newfile, Qt::CaseInsensitive)) {
endList += newfile;
return true;
}
return false;
}
QString
ProjectGenerator::getWritableVar(const char *vk, bool)
{
const ProKey v(vk);
ProStringList &vals = project->values(v);
if(vals.isEmpty())
return "";
// If values contain spaces, ensure that they are quoted
for (ProStringList::iterator it = vals.begin(); it != vals.end(); ++it) {
if ((*it).contains(' ') && !(*it).startsWith(' '))
*it = "\"" + *it + "\"";
}
QString ret;
if(v.endsWith("_REMOVE"))
ret = v.left(v.length() - 7) + " -= ";
else if(v.endsWith("_ASSIGN"))
ret = v.left(v.length() - 7) + " = ";
else
ret = v + " += ";
QString join = vals.join(' ');
if(ret.size() + join.size() > 80) {
QString spaces;
for(int i = 0; i < ret.size(); i++)
spaces += " ";
join = vals.join(" \\\n" + spaces);
}
return ret + join + "\n";
}
bool
ProjectGenerator::openOutput(QFile &file, const QString &build) const
{
ProString fileName = file.fileName();
if (!fileName.endsWith(Option::pro_ext)) {
if (fileName.isEmpty())
fileName = fileInfo(Option::output_dir).fileName();
file.setFileName(fileName + Option::pro_ext);
}
return MakefileGenerator::openOutput(file, build);
}
QString
ProjectGenerator::fixPathToQmake(const QString &file)
{
QString ret = file;
if(Option::dir_sep != QLatin1String("/"))
ret.replace(Option::dir_sep, QLatin1String("/"));
return ret;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,30 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PROJECTGENERATOR_H
#define PROJECTGENERATOR_H
#include "makefile.h"
QT_BEGIN_NAMESPACE
class ProjectGenerator : public MakefileGenerator
{
bool addFile(QString);
bool addConfig(const QString &, bool add=true);
QString getWritableVar(const char *, bool fixPath=true);
QString fixPathToQmake(const QString &file);
protected:
void init() override;
bool writeMakefile(QTextStream &) override;
QString escapeFilePath(const QString &) const override { Q_ASSERT(false); return QString(); }
public:
bool supportsMetaBuild() override { return false; }
bool openOutput(QFile &, const QString &) const override;
};
QT_END_NAMESPACE
#endif // PROJECTGENERATOR_H

View File

@ -0,0 +1,750 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "unixmake.h"
#include "option.h"
#include <qfile.h>
#include <qhash.h>
#include <qdir.h>
#include <time.h>
#include <qdebug.h>
QT_BEGIN_NAMESPACE
ProStringList UnixMakefileGenerator::libdirToFlags(const ProKey &key)
{
ProStringList results;
for (const auto &libdir : std::as_const(project->values(key))) {
if (!project->isEmpty("QMAKE_LFLAGS_RPATH") && project->isActiveConfig("rpath_libdirs"))
project->values("QMAKE_LFLAGS") += var("QMAKE_LFLAGS_RPATH") + libdir;
results.append("-L" + escapeFilePath(libdir));
}
return results;
}
void
UnixMakefileGenerator::init()
{
ProStringList &configs = project->values("CONFIG");
if(project->isEmpty("ICON") && !project->isEmpty("RC_FILE"))
project->values("ICON") = project->values("RC_FILE");
if(project->isEmpty("QMAKE_EXTENSION_PLUGIN"))
project->values("QMAKE_EXTENSION_PLUGIN").append(project->first("QMAKE_EXTENSION_SHLIB"));
project->values("QMAKE_ORIG_TARGET") = project->values("TARGET");
//version handling
if (project->isEmpty("VERSION")) {
project->values("VERSION").append(
"1.0." + (project->isEmpty("VER_PAT") ? QString("0") : project->first("VER_PAT")));
}
QStringList l = project->first("VERSION").toQString().split('.');
l << "0" << "0"; //make sure there are three
project->values("VER_MAJ").append(l[0]);
project->values("VER_MIN").append(l[1]);
project->values("VER_PAT").append(l[2]);
QString sroot = project->sourceRoot();
for (const ProString &iif : project->values("QMAKE_INTERNAL_INCLUDED_FILES")) {
if (iif == project->cacheFile())
continue;
if (iif.startsWith(sroot) && iif.at(sroot.size()) == QLatin1Char('/'))
project->values("DISTFILES") += fileFixify(iif.toQString(), FileFixifyRelative);
}
/* this should probably not be here, but I'm using it to wrap the .t files */
if(project->first("TEMPLATE") == "app")
project->values("QMAKE_APP_FLAG").append("1");
else if(project->first("TEMPLATE") == "lib")
project->values("QMAKE_LIB_FLAG").append("1");
else if(project->first("TEMPLATE") == "subdirs") {
MakefileGenerator::init();
if(project->isEmpty("MAKEFILE"))
project->values("MAKEFILE").append("Makefile");
return; /* subdirs is done */
}
project->values("QMAKE_ORIG_DESTDIR") = project->values("DESTDIR");
if((!project->isEmpty("QMAKE_LIB_FLAG") && !project->isActiveConfig("staticlib")) ||
(project->isActiveConfig("qt") && project->isActiveConfig("plugin"))) {
if(configs.indexOf("dll") == -1) configs.append("dll");
} else if(!project->isEmpty("QMAKE_APP_FLAG") || project->isActiveConfig("dll")) {
configs.removeAll("staticlib");
}
if(!project->isEmpty("QMAKE_INCREMENTAL"))
project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_INCREMENTAL");
else if(!project->isEmpty("QMAKE_LFLAGS_PREBIND") &&
!project->values("QMAKE_LIB_FLAG").isEmpty() &&
project->isActiveConfig("dll"))
project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_PREBIND");
project->values("QMAKE_INCDIR") += project->values("QMAKE_INCDIR_POST");
project->values("QMAKE_RPATHDIR") += project->values("QMAKE_RPATHDIR_POST");
project->values("QMAKE_RPATHLINKDIR") += project->values("QMAKE_RPATHLINKDIR_POST");
if(!project->isEmpty("QMAKE_INCDIR"))
project->values("INCLUDEPATH") += project->values("QMAKE_INCDIR");
// The order of the next two lines is relevant due to side effect on QMAKE_LFLAGS.
ProStringList ldadd = project->values("QMAKE_LIBDIR_FLAGS") + libdirToFlags("QMAKE_LIBDIR");
ProStringList ldaddpost = libdirToFlags("QMAKE_LIBDIR_POST");
if (project->isActiveConfig("mac")) {
if (!project->isEmpty("QMAKE_FRAMEWORKPATH")) {
const ProStringList &fwdirs = project->values("QMAKE_FRAMEWORKPATH");
for (int i = 0; i < fwdirs.size(); ++i)
project->values("QMAKE_FRAMEWORKPATH_FLAGS") += "-F" + escapeFilePath(fwdirs[i]);
}
ldadd += project->values("QMAKE_FRAMEWORKPATH_FLAGS");
}
ProStringList &qmklibs = project->values("LIBS");
qmklibs = ldadd + qmklibs;
ProStringList &qmklibspost = project->values("QMAKE_LIBS");
qmklibspost = ldaddpost + qmklibspost;
if (!project->isEmpty("QMAKE_RPATHDIR") && !project->isEmpty("QMAKE_LFLAGS_RPATH")) {
const ProStringList &rpathdirs = project->values("QMAKE_RPATHDIR");
for (int i = 0; i < rpathdirs.size(); ++i) {
QString rpathdir = rpathdirs[i].toQString();
if (rpathdir.size() > 1 && rpathdir.at(0) == '$' && rpathdir.at(1) != '(') {
rpathdir.replace(0, 1, "\\$$"); // Escape from make and the shell
} else if (!rpathdir.startsWith('@') && fileInfo(rpathdir).isRelative()) {
QString rpathbase = project->first("QMAKE_REL_RPATH_BASE").toQString();
if (rpathbase.isEmpty()) {
fprintf(stderr, "Error: This platform does not support relative paths in QMAKE_RPATHDIR (%s)\n",
rpathdir.toLatin1().constData());
continue;
}
if (rpathbase.startsWith('$'))
rpathbase.replace(0, 1, "\\$$"); // Escape from make and the shell
if (rpathdir == ".")
rpathdir = rpathbase;
else
rpathdir.prepend(rpathbase + '/');
project->values("QMAKE_LFLAGS").insertUnique(project->values("QMAKE_LFLAGS_REL_RPATH"));
}
project->values("QMAKE_LFLAGS") += var("QMAKE_LFLAGS_RPATH") + escapeFilePath(rpathdir);
}
}
if (!project->isEmpty("QMAKE_RPATHLINKDIR")) {
const ProStringList &rpathdirs = project->values("QMAKE_RPATHLINKDIR");
for (int i = 0; i < rpathdirs.size(); ++i) {
if (!project->isEmpty("QMAKE_LFLAGS_RPATHLINK"))
project->values("QMAKE_LFLAGS") += var("QMAKE_LFLAGS_RPATHLINK") + escapeFilePath(QFileInfo(rpathdirs[i].toQString()).absoluteFilePath());
}
}
if(project->isActiveConfig("GNUmake") && !project->isEmpty("QMAKE_CFLAGS_DEPS"))
include_deps = true; //do not generate deps
MakefileGenerator::init();
if (project->isActiveConfig("objective_c"))
project->values("QMAKE_BUILTIN_COMPILERS") << "OBJC" << "OBJCXX";
for (const ProString &compiler : project->values("QMAKE_BUILTIN_COMPILERS")) {
QString compile_flag = var("QMAKE_COMPILE_FLAG");
if(compile_flag.isEmpty())
compile_flag = "-c";
if(doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER")) {
QString pchFlags = var(ProKey("QMAKE_" + compiler + "FLAGS_USE_PRECOMPILE"));
QString pchBaseName;
if(!project->isEmpty("PRECOMPILED_DIR")) {
pchBaseName = Option::fixPathToTargetOS(project->first("PRECOMPILED_DIR").toQString());
if(!pchBaseName.endsWith(Option::dir_sep))
pchBaseName += Option::dir_sep;
}
pchBaseName += project->first("QMAKE_ORIG_TARGET").toQString();
// replace place holders
pchFlags.replace(QLatin1String("${QMAKE_PCH_INPUT}"),
escapeFilePath(project->first("PRECOMPILED_HEADER").toQString()));
pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT_BASE}"), escapeFilePath(pchBaseName));
if (project->isActiveConfig("icc_pch_style")) {
// icc style
pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT}"),
escapeFilePath(pchBaseName + project->first("QMAKE_PCH_OUTPUT_EXT")));
const ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS");
for (const ProString &arch : pchArchs) {
QString suffix = project->first("QMAKE_PCH_OUTPUT_EXT").toQString();
suffix.replace(QLatin1String("${QMAKE_PCH_ARCH}"), arch.toQString());
pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT_") + arch + QLatin1Char('}'),
escapeFilePath(pchBaseName + suffix));
}
} else {
// gcc style (including clang_pch_style)
QString headerSuffix;
if (project->isActiveConfig("clang_pch_style"))
headerSuffix = project->first("QMAKE_PCH_OUTPUT_EXT").toQString();
pchBaseName += project->first("QMAKE_PCH_OUTPUT_EXT").toQString();
pchBaseName += Option::dir_sep;
ProString language = project->first(ProKey("QMAKE_LANGUAGE_" + compiler));
if (!language.isEmpty()) {
pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT}"),
escapeFilePath(pchBaseName + language + headerSuffix));
const ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS");
for (const ProString &arch : pchArchs) {
QString file = pchBaseName + language + headerSuffix;
file.replace(QLatin1String("${QMAKE_PCH_ARCH}"), arch.toQString());
if (project->isActiveConfig("clang_pch_style")
&& (file.endsWith(QLatin1String(".pch"))
|| file.endsWith(QLatin1String(".gch")))) {
file.chop(4); // must omit header suffix for -include to recognize the PCH
}
pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT_") + arch + QLatin1Char('}'),
escapeFilePath(file));
}
}
}
if (!pchFlags.isEmpty())
compile_flag += " " + pchFlags;
}
QString compilerExecutable;
if (compiler == "C" || compiler == "OBJC") {
compilerExecutable = "$(CC)";
compile_flag += " $(CFLAGS)";
} else {
compilerExecutable = "$(CXX)";
compile_flag += " $(CXXFLAGS)";
}
compile_flag += " $(INCPATH)";
ProString compilerVariable = compiler;
if (compilerVariable == "C")
compilerVariable = ProString("CC");
const ProKey runComp("QMAKE_RUN_" + compilerVariable);
if(project->isEmpty(runComp))
project->values(runComp).append(compilerExecutable + " " + compile_flag + " " + var("QMAKE_CC_O_FLAG") + "$obj $src");
const ProKey runCompImp("QMAKE_RUN_" + compilerVariable + "_IMP");
if(project->isEmpty(runCompImp))
project->values(runCompImp).append(compilerExecutable + " " + compile_flag + " " + var("QMAKE_CC_O_FLAG") + "\"$@\" \"$<\"");
}
if (project->isActiveConfig("mac") && !project->isEmpty("TARGET") &&
((project->isActiveConfig("build_pass") || project->isEmpty("BUILDS")))) {
ProString bundle;
if(project->isActiveConfig("bundle") && !project->isEmpty("QMAKE_BUNDLE_EXTENSION")) {
bundle = project->first("TARGET");
if(!project->isEmpty("QMAKE_BUNDLE_NAME"))
bundle = project->first("QMAKE_BUNDLE_NAME");
if(!bundle.endsWith(project->first("QMAKE_BUNDLE_EXTENSION")))
bundle += project->first("QMAKE_BUNDLE_EXTENSION");
} else if(project->first("TEMPLATE") == "app" && project->isActiveConfig("app_bundle")) {
bundle = project->first("TARGET");
if(!project->isEmpty("QMAKE_APPLICATION_BUNDLE_NAME"))
bundle = project->first("QMAKE_APPLICATION_BUNDLE_NAME");
if(!bundle.endsWith(".app"))
bundle += ".app";
if(project->isEmpty("QMAKE_BUNDLE_LOCATION"))
project->values("QMAKE_BUNDLE_LOCATION").append("Contents/MacOS");
project->values("QMAKE_PKGINFO").append(project->first("DESTDIR") + bundle + "/Contents/PkgInfo");
} else if(project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib") &&
((!project->isActiveConfig("plugin") && project->isActiveConfig("lib_bundle")) ||
(project->isActiveConfig("plugin") && project->isActiveConfig("plugin_bundle")))) {
bundle = project->first("TARGET");
if(project->isActiveConfig("plugin")) {
if(!project->isEmpty("QMAKE_PLUGIN_BUNDLE_NAME"))
bundle = project->first("QMAKE_PLUGIN_BUNDLE_NAME");
if (project->isEmpty("QMAKE_BUNDLE_EXTENSION"))
project->values("QMAKE_BUNDLE_EXTENSION").append(".plugin");
if (!bundle.endsWith(project->first("QMAKE_BUNDLE_EXTENSION")))
bundle += project->first("QMAKE_BUNDLE_EXTENSION");
if(project->isEmpty("QMAKE_BUNDLE_LOCATION"))
project->values("QMAKE_BUNDLE_LOCATION").append("Contents/MacOS");
} else {
if(!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME"))
bundle = project->first("QMAKE_FRAMEWORK_BUNDLE_NAME");
if (project->isEmpty("QMAKE_BUNDLE_EXTENSION"))
project->values("QMAKE_BUNDLE_EXTENSION").append(".framework");
if (!bundle.endsWith(project->first("QMAKE_BUNDLE_EXTENSION")))
bundle += project->first("QMAKE_BUNDLE_EXTENSION");
}
}
if(!bundle.isEmpty()) {
project->values("QMAKE_BUNDLE") = ProStringList(bundle);
} else {
project->values("QMAKE_BUNDLE").clear();
project->values("QMAKE_BUNDLE_LOCATION").clear();
}
} else { //no bundling here
project->values("QMAKE_BUNDLE").clear();
project->values("QMAKE_BUNDLE_LOCATION").clear();
}
init2();
ProString target = project->first("TARGET");
int slsh = target.lastIndexOf(Option::dir_sep);
if (slsh != -1)
target.chopFront(slsh + 1);
project->values("LIB_TARGET").prepend(target);
}
QStringList
&UnixMakefileGenerator::findDependencies(const QString &f)
{
QStringList &ret = MakefileGenerator::findDependencies(f);
if (doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER")) {
ProString file = f;
QString header_prefix;
if(!project->isEmpty("PRECOMPILED_DIR"))
header_prefix = project->first("PRECOMPILED_DIR").toQString();
header_prefix += project->first("QMAKE_ORIG_TARGET").toQString();
header_prefix += project->first("QMAKE_PCH_OUTPUT_EXT").toQString();
if (project->isActiveConfig("icc_pch_style")) {
// icc style
ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS");
if (pchArchs.isEmpty())
pchArchs << ProString(); // normal single-arch PCH
for (const ProString &arch : std::as_const(pchArchs)) {
auto pfx = header_prefix;
if (!arch.isEmpty())
pfx.replace(QLatin1String("${QMAKE_PCH_ARCH}"), arch.toQString());
for (QStringList::Iterator it = Option::cpp_ext.begin();
it != Option::cpp_ext.end(); ++it) {
if (file.endsWith(*it)) {
ret += pfx;
break;
}
}
}
} else {
// gcc style (including clang_pch_style)
QString header_suffix = project->isActiveConfig("clang_pch_style")
? project->first("QMAKE_PCH_OUTPUT_EXT").toQString() : "";
header_prefix += Option::dir_sep + project->first("QMAKE_PRECOMP_PREFIX");
for (const ProString &compiler : project->values("QMAKE_BUILTIN_COMPILERS")) {
if (project->isEmpty(ProKey("QMAKE_" + compiler + "FLAGS_PRECOMPILE")))
continue;
ProString language = project->first(ProKey("QMAKE_LANGUAGE_" + compiler));
if (language.isEmpty())
continue;
// Unfortunately we were not consistent about the C++ naming
ProString extensionSuffix = compiler;
if (extensionSuffix == "CXX")
extensionSuffix = ProString("CPP");
for (const ProString &extension : project->values(ProKey("QMAKE_EXT_" + extensionSuffix))) {
if (!file.endsWith(extension.toQString()))
continue;
ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS");
if (pchArchs.isEmpty())
pchArchs << ProString(); // normal single-arch PCH
for (const ProString &arch : std::as_const(pchArchs)) {
QString precompiledHeader = header_prefix + language + header_suffix;
if (!arch.isEmpty()) {
precompiledHeader.replace(QLatin1String("${QMAKE_PCH_ARCH}"),
arch.toQString());
}
if (!ret.contains(precompiledHeader))
ret += precompiledHeader;
}
goto foundPrecompiledDependency;
}
}
foundPrecompiledDependency:
; // Hurray!!
}
}
return ret;
}
ProString
UnixMakefileGenerator::fixLibFlag(const ProString &lib)
{
return escapeFilePath(lib);
}
bool
UnixMakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags)
{
QList<QMakeLocalFileName> libdirs, frameworkdirs;
int libidx = 0, fwidx = 0;
for (const ProString &dlib : project->values("QMAKE_DEFAULT_LIBDIRS"))
libdirs.append(QMakeLocalFileName(dlib.toQString()));
frameworkdirs.append(QMakeLocalFileName("/System/Library/Frameworks"));
frameworkdirs.append(QMakeLocalFileName("/Library/Frameworks"));
ProStringList extens;
extens << project->first("QMAKE_EXTENSION_SHLIB") << "a";
static const char * const lflags[] = { "LIBS", "LIBS_PRIVATE",
"QMAKE_LIBS", "QMAKE_LIBS_PRIVATE", nullptr };
for (int i = 0; lflags[i]; i++) {
ProStringList &l = project->values(lflags[i]);
for (ProStringList::Iterator it = l.begin(); it != l.end(); ) {
QString opt = (*it).toQString();
if(opt.startsWith("-")) {
if(opt.startsWith("-L")) {
QString lib = opt.mid(2);
QMakeLocalFileName f(lib);
int idx = libdirs.indexOf(f);
if (idx >= 0 && idx < libidx) {
it = l.erase(it);
continue;
}
libdirs.insert(libidx++, f);
} else if(opt.startsWith("-l")) {
QString lib = opt.mid(2);
for (const QMakeLocalFileName &libdir : std::as_const(libdirs)) {
QString libBase = libdir.local() + '/'
+ project->first("QMAKE_PREFIX_SHLIB") + lib;
if (linkPrl && processPrlFile(libBase, true))
goto found;
for (ProStringList::Iterator extit = extens.begin(); extit != extens.end(); ++extit) {
if (exists(libBase + '.' + (*extit)))
goto found;
}
}
found: ;
} else if (target_mode == TARG_MAC_MODE && opt.startsWith("-F")) {
QMakeLocalFileName f(opt.mid(2));
if (!frameworkdirs.contains(f))
frameworkdirs.insert(fwidx++, f);
} else if (target_mode == TARG_MAC_MODE && opt == "-framework") {
if (linkPrl) {
opt = (*++it).toQString();
static const QChar suffixMarker = ',';
const int suffixPosition = opt.indexOf(suffixMarker);
const bool hasSuffix = suffixPosition >= 0;
QString frameworkName = opt;
if (hasSuffix) {
frameworkName.truncate(suffixPosition);
opt.remove(suffixMarker); // Apply suffix by removing marker
}
for (const QMakeLocalFileName &dir : std::as_const(frameworkdirs)) {
auto processPrlIfFound = [&](QString directory) {
QString suffixedPrl = directory + opt;
if (processPrlFile(suffixedPrl, true))
return true;
if (hasSuffix) {
QString unsuffixedPrl = directory + frameworkName;
if (processPrlFile(unsuffixedPrl, true))
return true;
}
return false;
};
QString frameworkDirectory = dir.local() + "/" + frameworkName + + ".framework/";
if (processPrlIfFound(frameworkDirectory + "Resources/")
|| processPrlIfFound(frameworkDirectory))
break;
}
} else {
if (opt.size() == 10)
++it;
// Skip
}
}
} else if (linkPrl) {
processPrlFile(opt, false);
}
ProStringList &prl_libs = project->values("QMAKE_CURRENT_PRL_LIBS");
for (int prl = 0; prl < prl_libs.size(); ++prl)
it = l.insert(++it, prl_libs.at(prl));
prl_libs.clear();
++it;
}
if (mergeLflags) {
QHash<ProKey, ProStringList> lflags;
for(int lit = 0; lit < l.size(); ++lit) {
ProKey arch("default");
ProString opt = l.at(lit);
if (opt.startsWith('-')) {
if (target_mode == TARG_MAC_MODE && opt.startsWith("-Xarch")) {
if (opt.length() > 7) {
arch = opt.mid(7).toKey();
opt = l.at(++lit);
}
}
if (opt.startsWith("-L")
|| (target_mode == TARG_MAC_MODE && opt.startsWith("-F"))) {
if (!lflags[arch].contains(opt))
lflags[arch].append(opt);
} else if (opt.startsWith("-l") || opt == "-pthread") {
// Make sure we keep the dependency order of libraries
lflags[arch].removeAll(opt);
lflags[arch].append(opt);
} else if (target_mode == TARG_MAC_MODE
&& (opt == "-framework" || opt == "-force_load")) {
// Handle space separated options
ProString dashOpt = opt;
opt = l.at(++lit);
if (opt.startsWith("-Xarch"))
opt = l.at(++lit); // The user has done the right thing and prefixed each part
for(int x = 0; x < lflags[arch].size(); ++x) {
if (lflags[arch].at(x) == dashOpt && lflags[arch].at(++x) == opt) {
lflags[arch].remove(x - 1, 2);
break;
}
}
lflags[arch].append(dashOpt);
lflags[arch].append(opt);
} else {
lflags[arch].append(opt);
}
} else if(!opt.isNull()) {
for (const ProString &ext : extens) {
if (opt.size() > ext.size() && opt.endsWith(ext)
&& opt.at(opt.size() - ext.size() - 1) == '.') {
// Make sure we keep the dependency order of libraries
lflags[arch].removeAll(opt);
lflags[arch].append(opt);
goto found2;
}
}
if(!lflags[arch].contains(opt))
lflags[arch].append(opt);
found2: ;
}
}
l = lflags.take("default");
// Process architecture specific options (Xarch)
QHash<ProKey, ProStringList>::const_iterator archIterator = lflags.constBegin();
while (archIterator != lflags.constEnd()) {
const ProStringList &archOptions = archIterator.value();
for (int i = 0; i < archOptions.size(); ++i) {
l.append(QLatin1String("-Xarch_") + archIterator.key());
l.append(archOptions.at(i));
}
++archIterator;
}
}
}
return false;
}
#ifdef Q_OS_WIN // MinGW x-compiling for QNX
QString UnixMakefileGenerator::installRoot() const
{
/*
We include a magic prefix on the path to bypass mingw-make's "helpful"
intervention in the environment, recognising variables that look like
paths and adding the msys system root as prefix, which we don't want.
Once this hack has smuggled INSTALL_ROOT into make's variable space, we
can trivially strip the magic prefix back off to get the path we meant.
*/
return QStringLiteral("$(INSTALL_ROOT:@msyshack@%=%)");
}
#endif
QString
UnixMakefileGenerator::defaultInstall(const QString &t)
{
if(t != "target" || project->first("TEMPLATE") == "subdirs")
return QString();
enum { NoBundle, SolidBundle, SlicedBundle } bundle = NoBundle;
bool isAux = (project->first("TEMPLATE") == "aux");
const QString root = installRoot();
ProStringList &uninst = project->values(ProKey(t + ".uninstall"));
QString ret, destdir = project->first("DESTDIR").toQString();
if(!destdir.isEmpty() && destdir.right(1) != Option::dir_sep)
destdir += Option::dir_sep;
QString targetdir = fileFixify(project->first("target.path").toQString(), FileFixifyAbsolute);
if(targetdir.right(1) != Option::dir_sep)
targetdir += Option::dir_sep;
ProStringList links;
QString target="$(TARGET)";
const ProStringList &targets = project->values(ProKey(t + ".targets"));
if(!project->isEmpty("QMAKE_BUNDLE")) {
target = project->first("QMAKE_BUNDLE").toQString();
bundle = project->isActiveConfig("sliced_bundle") ? SlicedBundle : SolidBundle;
} else if(project->first("TEMPLATE") == "app") {
target = "$(QMAKE_TARGET)";
} else if(project->first("TEMPLATE") == "lib") {
if (!project->isActiveConfig("staticlib")
&& !project->isActiveConfig("plugin")
&& !project->isActiveConfig("unversioned_libname")) {
if(project->isEmpty("QMAKE_HPUX_SHLIB")) {
links << "$(TARGET0)" << "$(TARGET1)" << "$(TARGET2)";
} else {
links << "$(TARGET0)";
}
}
}
for(int i = 0; i < targets.size(); ++i) {
QString src = targets.at(i).toQString(),
dst = escapeFilePath(filePrefixRoot(root, targetdir + src.section('/', -1)));
if(!ret.isEmpty())
ret += "\n\t";
ret += "$(QINSTALL) " + escapeFilePath(Option::fixPathToTargetOS(src, false)) + ' ' + dst;
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + dst);
}
{
QString src_targ = target;
if(!destdir.isEmpty())
src_targ = Option::fixPathToTargetOS(destdir + target, false);
QString plain_targ = filePrefixRoot(root, fileFixify(targetdir + target, FileFixifyAbsolute));
QString dst_targ = plain_targ;
plain_targ = escapeFilePath(plain_targ);
if (bundle != NoBundle) {
QString suffix;
if (project->first("TEMPLATE") == "lib") {
if (!project->isActiveConfig("shallow_bundle"))
suffix += "/Versions/" + project->first("QMAKE_FRAMEWORK_VERSION");
suffix += "/$(TARGET)";
} else {
suffix = "/" + project->first("QMAKE_BUNDLE_LOCATION") + "/$(QMAKE_TARGET)";
}
dst_targ += suffix;
if (bundle == SolidBundle) {
if (!ret.isEmpty())
ret += "\n\t";
ret += "$(DEL_FILE) -r " + plain_targ + "\n\t";
} else {
src_targ += suffix;
}
}
src_targ = escapeFilePath(src_targ);
dst_targ = escapeFilePath(dst_targ);
QString copy_cmd;
if (bundle == SolidBundle) {
copy_cmd += "$(QINSTALL) " + src_targ + ' ' + plain_targ;
} else if (project->first("TEMPLATE") == "lib" && project->isActiveConfig("staticlib")) {
copy_cmd += "$(QINSTALL) " + src_targ + ' ' + dst_targ;
} else if (!isAux) {
if (bundle == SlicedBundle) {
if (!ret.isEmpty())
ret += "\n\t";
ret += mkdir_p_asstring("\"`dirname " + dst_targ + "`\"", false);
}
copy_cmd += "$(QINSTALL_PROGRAM) " + src_targ + ' ' + dst_targ;
}
if(project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib")
&& project->values(ProKey(t + ".CONFIG")).indexOf("fix_rpath") != -1) {
if (!ret.isEmpty())
ret += "\n\t";
if(!project->isEmpty("QMAKE_FIX_RPATH")) {
ret += copy_cmd;
ret += "\n\t-" + var("QMAKE_FIX_RPATH") + ' ' + dst_targ + ' ' + dst_targ;
} else if(!project->isEmpty("QMAKE_LFLAGS_RPATH")) {
ret += "-$(LINK) $(LFLAGS) " + var("QMAKE_LFLAGS_RPATH") + targetdir + " -o " +
dst_targ + " $(OBJECTS) $(LIBS) $(OBJCOMP)";
} else {
ret += copy_cmd;
}
} else if (!copy_cmd.isEmpty()) {
if (!ret.isEmpty())
ret += "\n\t";
ret += copy_cmd;
}
if (isAux) {
} else if (project->first("TEMPLATE") == "lib" && project->isActiveConfig("staticlib")) {
if(!project->isEmpty("QMAKE_RANLIB"))
ret += QString("\n\t$(RANLIB) ") + dst_targ;
} else if (!project->isActiveConfig("debug_info") && !project->isActiveConfig("nostrip")
&& !project->isEmpty("QMAKE_STRIP")) {
ret += "\n\t-$(STRIP)";
if (project->first("TEMPLATE") == "lib") {
if (!project->isEmpty("QMAKE_STRIPFLAGS_LIB"))
ret += " " + var("QMAKE_STRIPFLAGS_LIB");
} else if (project->first("TEMPLATE") == "app") {
if (!project->isEmpty("QMAKE_STRIPFLAGS_APP"))
ret += " " + var("QMAKE_STRIPFLAGS_APP");
}
ret += ' ' + dst_targ;
}
if(!uninst.isEmpty())
uninst.append("\n\t");
if (bundle == SolidBundle)
uninst.append("-$(DEL_FILE) -r " + plain_targ);
else if (!isAux)
uninst.append("-$(DEL_FILE) " + dst_targ);
if (bundle == SlicedBundle) {
int dstlen = project->first("DESTDIR").length();
for (const ProString &src : project->values("QMAKE_BUNDLED_FILES")) {
ProString file = src.mid(dstlen);
QString dst = escapeFilePath(
filePrefixRoot(root, fileFixify(targetdir + file, FileFixifyAbsolute)));
if (!ret.isEmpty())
ret += "\n\t";
ret += mkdir_p_asstring("\"`dirname " + dst + "`\"", false) + "\n\t";
ret += "-$(DEL_FILE) " + dst + "\n\t"; // Can't overwrite symlinks to directories
ret += "$(QINSTALL) " + escapeFilePath(src) + " " + dst;
if (!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + dst);
}
}
if(!links.isEmpty()) {
for(int i = 0; i < links.size(); ++i) {
if (target_mode == TARG_UNIX_MODE || target_mode == TARG_MAC_MODE) {
QString link = Option::fixPathToTargetOS(destdir + links[i], false);
int lslash = link.lastIndexOf(Option::dir_sep);
if(lslash != -1)
link = link.right(link.size() - (lslash + 1));
QString dst_link = escapeFilePath(
filePrefixRoot(root, fileFixify(targetdir + link, FileFixifyAbsolute)));
ret += "\n\t-$(SYMLINK) $(TARGET) " + dst_link;
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + dst_link);
}
}
}
}
if (isAux || project->first("TEMPLATE") == "lib") {
QStringList types;
types << "prl" << "libtool" << "pkgconfig";
for(int i = 0; i < types.size(); ++i) {
const QString type = types.at(i);
QString meta;
if(type == "prl" && project->isActiveConfig("create_prl") && !project->isActiveConfig("no_install_prl") &&
!project->isEmpty("QMAKE_INTERNAL_PRL_FILE"))
meta = prlFileName(false);
if (type == "libtool" && project->isActiveConfig("create_libtool"))
meta = libtoolFileName(false);
if(type == "pkgconfig" && project->isActiveConfig("create_pc"))
meta = pkgConfigFileName(false);
if(!meta.isEmpty()) {
QString src_meta = meta;
if(!destdir.isEmpty())
src_meta = Option::fixPathToTargetOS(destdir + meta, false);
QString dst_meta = filePrefixRoot(root, fileFixify(targetdir + meta, FileFixifyAbsolute));
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + escapeFilePath(dst_meta));
const QString dst_meta_dir = fileInfo(dst_meta).path();
if(!dst_meta_dir.isEmpty()) {
if(!ret.isEmpty())
ret += "\n\t";
ret += mkdir_p_asstring(dst_meta_dir, true);
}
if (!ret.isEmpty())
ret += "\n\t";
ret += installMetaFile(ProKey("QMAKE_" + type.toUpper() + "_INSTALL_REPLACE"), src_meta, dst_meta);
}
}
}
return ret;
}
QString
UnixMakefileGenerator::escapeFilePath(const QString &path) const
{
QString ret = path;
if(!ret.isEmpty()) {
ret.replace(QLatin1Char(' '), QLatin1String("\\ "))
.replace(QLatin1Char('\t'), QLatin1String("\\\t"));
debug_msg(2, "EscapeFilePath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData());
}
return ret;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,44 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef UNIXMAKE_H
#define UNIXMAKE_H
#include "makefile.h"
QT_BEGIN_NAMESPACE
class UnixMakefileGenerator : public MakefileGenerator
{
bool include_deps = false;
QString libtoolFileName(bool fixify=true);
void writeLibtoolFile(); // for libtool
void writePrlFile(QTextStream &) override;
protected:
virtual bool doPrecompiledHeaders() const { return project->isActiveConfig("precompile_header"); }
#ifdef Q_OS_WIN // MinGW x-compiling for QNX
QString installRoot() const override;
#endif
QString defaultInstall(const QString &) override;
ProString fixLibFlag(const ProString &lib) override;
bool findLibraries(bool linkPrl, bool mergeLflags) override;
QString escapeFilePath(const QString &path) const override;
using MakefileGenerator::escapeFilePath;
QStringList &findDependencies(const QString &) override;
void init() override;
void writeDefaultVariables(QTextStream &t) override;
void writeSubTargets(QTextStream &t, QList<SubTarget*> subtargets, int flags) override;
void writeMakeParts(QTextStream &);
bool writeMakefile(QTextStream &) override;
bool writeObjectsPart(QTextStream &, bool do_incremental);
private:
void init2();
ProStringList libdirToFlags(const ProKey &key);
};
QT_END_NAMESPACE
#endif // UNIXMAKE_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,326 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "mingw_make.h"
#include "option.h"
#include <proitems.h>
#include <qregularexpression.h>
#include <qdir.h>
#include <stdlib.h>
#include <time.h>
QT_BEGIN_NAMESPACE
QString MingwMakefileGenerator::escapeDependencyPath(const QString &path) const
{
QString ret = path;
ret.replace('\\', "/"); // ### this shouldn't be here
return MakefileGenerator::escapeDependencyPath(ret);
}
ProString MingwMakefileGenerator::fixLibFlag(const ProString &lib)
{
if (lib.startsWith("-l")) // Fallback for unresolved -l libs.
return QLatin1String("-l") + escapeFilePath(lib.mid(2));
if (lib.startsWith("-L")) // Lib search path. Needed only by -l above.
return QLatin1String("-L")
+ escapeFilePath(Option::fixPathToTargetOS(lib.mid(2).toQString(), false));
if (lib.startsWith("lib")) // Fallback for unresolved MSVC-style libs.
return QLatin1String("-l") + escapeFilePath(lib.mid(3).toQString());
return escapeFilePath(Option::fixPathToTargetOS(lib.toQString(), false));
}
MakefileGenerator::LibFlagType
MingwMakefileGenerator::parseLibFlag(const ProString &flag, ProString *arg)
{
// Skip MSVC handling from Win32MakefileGenerator
return MakefileGenerator::parseLibFlag(flag, arg);
}
bool MingwMakefileGenerator::processPrlFileBase(QString &origFile, QStringView origName,
QStringView fixedBase, int slashOff)
{
if (origName.startsWith(u"lib")) {
QString newFixedBase = fixedBase.left(slashOff) + fixedBase.mid(slashOff + 3);
if (Win32MakefileGenerator::processPrlFileBase(origFile, origName,
QStringView(newFixedBase), slashOff)) {
return true;
}
}
return Win32MakefileGenerator::processPrlFileBase(origFile, origName, fixedBase, slashOff);
}
bool MingwMakefileGenerator::writeMakefile(QTextStream &t)
{
writeHeader(t);
if (writeDummyMakefile(t))
return true;
if(project->first("TEMPLATE") == "app" ||
project->first("TEMPLATE") == "lib" ||
project->first("TEMPLATE") == "aux") {
if(project->isActiveConfig("create_pc") && project->first("TEMPLATE") == "lib")
writePkgConfigFile();
writeMingwParts(t);
return MakefileGenerator::writeMakefile(t);
}
else if(project->first("TEMPLATE") == "subdirs") {
writeSubDirs(t);
return true;
}
return false;
}
QString MingwMakefileGenerator::installRoot() const
{
/*
We include a magic prefix on the path to bypass mingw-make's "helpful"
intervention in the environment, recognising variables that look like
paths and adding the msys system root as prefix, which we don't want.
Once this hack has smuggled INSTALL_ROOT into make's variable space, we
can trivially strip the magic prefix back off to get the path we meant.
*/
return QStringLiteral("$(INSTALL_ROOT:@msyshack@%=%)");
}
void MingwMakefileGenerator::writeMingwParts(QTextStream &t)
{
writeStandardParts(t);
if (!preCompHeaderOut.isEmpty()) {
QString header = project->first("PRECOMPILED_HEADER").toQString();
QString cHeader = preCompHeaderOut + Option::dir_sep + "c";
t << escapeDependencyPath(cHeader) << ": " << escapeDependencyPath(header) << " "
<< finalizeDependencyPaths(findDependencies(header)).join(" \\\n\t\t")
<< "\n\t" << mkdir_p_asstring(preCompHeaderOut)
<< "\n\t$(CC) -x c-header -c $(CFLAGS) $(INCPATH) -o " << escapeFilePath(cHeader)
<< ' ' << escapeFilePath(header) << Qt::endl << Qt::endl;
QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++";
t << escapeDependencyPath(cppHeader) << ": " << escapeDependencyPath(header) << " "
<< finalizeDependencyPaths(findDependencies(header)).join(" \\\n\t\t")
<< "\n\t" << mkdir_p_asstring(preCompHeaderOut)
<< "\n\t$(CXX) -x c++-header -c $(CXXFLAGS) $(INCPATH) -o " << escapeFilePath(cppHeader)
<< ' ' << escapeFilePath(header) << Qt::endl << Qt::endl;
}
}
void MingwMakefileGenerator::init()
{
/* this should probably not be here, but I'm using it to wrap the .t files */
if(project->first("TEMPLATE") == "app")
project->values("QMAKE_APP_FLAG").append("1");
else if(project->first("TEMPLATE") == "lib")
project->values("QMAKE_LIB_FLAG").append("1");
else if(project->first("TEMPLATE") == "subdirs") {
MakefileGenerator::init();
if(project->values("MAKEFILE").isEmpty())
project->values("MAKEFILE").append("Makefile");
return;
}
processVars();
project->values("LIBS") += project->values("RES_FILE");
if (project->isActiveConfig("dll")) {
QString destDir = "";
if(!project->first("DESTDIR").isEmpty())
destDir = Option::fixPathToTargetOS(project->first("DESTDIR") + Option::dir_sep, false, false);
project->values("MINGW_IMPORT_LIB").prepend(destDir + project->first("LIB_TARGET"));
project->values("QMAKE_LFLAGS").append(QString("-Wl,--out-implib,") + fileVar("MINGW_IMPORT_LIB"));
}
if (!project->values("DEF_FILE").isEmpty()) {
QString defFileName = fileFixify(project->first("DEF_FILE").toQString());
project->values("QMAKE_LFLAGS").append(QString("-Wl,") + escapeFilePath(defFileName));
}
if (project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib")
project->values("QMAKE_LFLAGS").append("-static");
MakefileGenerator::init();
// precomp
if (!project->first("PRECOMPILED_HEADER").isEmpty()
&& project->isActiveConfig("precompile_header")) {
QString preCompHeader = var("PRECOMPILED_DIR")
+ QFileInfo(project->first("PRECOMPILED_HEADER").toQString()).fileName();
preCompHeaderOut = preCompHeader + ".gch";
project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c");
project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c++");
preCompHeader = escapeFilePath(preCompHeader);
project->values("QMAKE_RUN_CC").clear();
project->values("QMAKE_RUN_CC").append("$(CC) -c -include " + preCompHeader +
" $(CFLAGS) $(INCPATH) " + var("QMAKE_CC_O_FLAG") + "$obj $src");
project->values("QMAKE_RUN_CC_IMP").clear();
project->values("QMAKE_RUN_CC_IMP").append("$(CC) -c -include " + preCompHeader +
" $(CFLAGS) $(INCPATH) " + var("QMAKE_CC_O_FLAG") + "$@ $<");
project->values("QMAKE_RUN_CXX").clear();
project->values("QMAKE_RUN_CXX").append("$(CXX) -c -include " + preCompHeader +
" $(CXXFLAGS) $(INCPATH) " + var("QMAKE_CC_O_FLAG") + "$obj $src");
project->values("QMAKE_RUN_CXX_IMP").clear();
project->values("QMAKE_RUN_CXX_IMP").append("$(CXX) -c -include " + preCompHeader +
" $(CXXFLAGS) $(INCPATH) " + var("QMAKE_CC_O_FLAG") + "$@ $<");
}
if(project->isActiveConfig("dll")) {
project->values("QMAKE_DISTCLEAN").append(project->first("MINGW_IMPORT_LIB"));
}
}
void MingwMakefileGenerator::writeIncPart(QTextStream &t)
{
t << "INCPATH = ";
const ProStringList &incs = project->values("INCLUDEPATH");
QFile responseFile;
QTextStream responseStream;
QChar sep(' ');
int totalLength = std::accumulate(incs.constBegin(), incs.constEnd(), 0,
[](int total, const ProString &inc) {
return total + inc.size() + 2;
});
if (totalLength > project->intValue("QMAKE_RESPONSEFILE_THRESHOLD", 8000)) {
const QString fileName = createResponseFile("incpath", incs, "-I");
if (!fileName.isEmpty()) {
t << '@' + fileName;
t << Qt::endl;
return;
}
}
for (const ProString &incit: std::as_const(incs)) {
QString inc = incit.toQString();
inc.replace(QRegularExpression("\\\\$"), "");
inc.replace('\\', '/');
t << "-I" << escapeFilePath(inc) << sep;
}
t << Qt::endl;
}
void MingwMakefileGenerator::writeLibsPart(QTextStream &t)
{
if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
t << "LIB = " << var("QMAKE_LIB") << Qt::endl;
} else {
t << "LINKER = " << var("QMAKE_LINK") << Qt::endl;
t << "LFLAGS = " << var("QMAKE_LFLAGS") << Qt::endl;
t << "LIBS = "
<< fixLibFlags("LIBS").join(' ') << ' '
<< fixLibFlags("LIBS_PRIVATE").join(' ') << ' '
<< fixLibFlags("QMAKE_LIBS").join(' ') << ' '
<< fixLibFlags("QMAKE_LIBS_PRIVATE").join(' ') << Qt::endl;
}
}
void MingwMakefileGenerator::writeObjectsPart(QTextStream &t)
{
linkerResponseFile = maybeCreateLinkerResponseFile();
if (!linkerResponseFile.isValid()) {
objectsLinkLine = "$(OBJECTS)";
} else if (project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
// QMAKE_LIB is used for win32, including mingw, whereas QMAKE_AR is used on Unix.
QString ar_cmd = var("QMAKE_LIB");
if (ar_cmd.isEmpty())
ar_cmd = "ar -rc";
objectsLinkLine = ar_cmd + ' ' + var("DEST_TARGET") + " @"
+ escapeFilePath(linkerResponseFile.filePath);
} else {
objectsLinkLine = "@" + escapeFilePath(linkerResponseFile.filePath);
}
Win32MakefileGenerator::writeObjectsPart(t);
}
void MingwMakefileGenerator::writeBuildRulesPart(QTextStream &t)
{
t << "first: all\n";
t << "all: " << escapeDependencyPath(fileFixify(Option::output.fileName()))
<< ' ' << depVar("ALL_DEPS") << ' ' << depVar("DEST_TARGET") << "\n\n";
t << depVar("DEST_TARGET") << ": "
<< depVar("PRE_TARGETDEPS") << " $(OBJECTS) " << depVar("POST_TARGETDEPS");
if (project->first("TEMPLATE") == "aux") {
t << "\n\n";
return;
}
if(!project->isEmpty("QMAKE_PRE_LINK"))
t << "\n\t" <<var("QMAKE_PRE_LINK");
if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
t << "\n\t-$(DEL_FILE) $(DESTDIR_TARGET) 2>" << var("QMAKE_SHELL_NULL_DEVICE");
const ProString &objmax = project->first("QMAKE_LINK_OBJECT_MAX");
if (objmax.isEmpty() || project->values("OBJECTS").size() < objmax.toInt()) {
t << "\n\t$(LIB) $(DESTDIR_TARGET) " << objectsLinkLine << " " ;
} else {
t << "\n\t" << objectsLinkLine << " " ;
}
} else {
t << "\n\t$(LINKER) $(LFLAGS) " << var("QMAKE_LINK_O_FLAG") << "$(DESTDIR_TARGET) "
<< objectsLinkLine;
if (!linkerResponseFile.isValid() || linkerResponseFile.onlyObjects)
t << " $(LIBS)";
}
if(!project->isEmpty("QMAKE_POST_LINK"))
t << "\n\t" <<var("QMAKE_POST_LINK");
t << Qt::endl;
}
void MingwMakefileGenerator::writeRcFilePart(QTextStream &t)
{
const QString rc_file = fileFixify(project->first("RC_FILE").toQString());
ProStringList rcIncPaths = project->values("RC_INCLUDEPATH");
rcIncPaths.prepend(fileInfo(rc_file).path());
QString incPathStr;
for (int i = 0; i < rcIncPaths.size(); ++i) {
const ProString &path = rcIncPaths.at(i);
if (path.isEmpty())
continue;
incPathStr += QStringLiteral(" --include-dir=");
if (path != "." && QDir::isRelativePath(path.toQString()))
incPathStr += "./";
incPathStr += escapeFilePath(path);
}
if (!rc_file.isEmpty()) {
ProString defines = varGlue("RC_DEFINES", " -D", " -D", "");
if (defines.isEmpty())
defines = ProString(" $(DEFINES)");
addSourceFile(rc_file, QMakeSourceFileInfo::SEEK_DEPS);
const QStringList rcDeps = QStringList(rc_file) << dependencies(rc_file);
t << escapeDependencyPath(var("RES_FILE")) << ": "
<< escapeDependencyPaths(rcDeps).join(' ') << "\n\t"
<< var("QMAKE_RC") << " -i " << escapeFilePath(rc_file) << " -o " << fileVar("RES_FILE")
<< incPathStr << defines << "\n\n";
}
}
QStringList &MingwMakefileGenerator::findDependencies(const QString &file)
{
QStringList &aList = MakefileGenerator::findDependencies(file);
if (preCompHeaderOut.isEmpty())
return aList;
for (QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) {
if (file.endsWith(*it)) {
QString cHeader = preCompHeaderOut + Option::dir_sep + "c";
if (!aList.contains(cHeader))
aList += cHeader;
break;
}
}
for (QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) {
if (file.endsWith(*it)) {
QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++";
if (!aList.contains(cppHeader))
aList += cppHeader;
break;
}
}
return aList;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,42 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MINGW_MAKE_H
#define MINGW_MAKE_H
#include "winmakefile.h"
QT_BEGIN_NAMESPACE
class MingwMakefileGenerator : public Win32MakefileGenerator
{
protected:
using MakefileGenerator::escapeDependencyPath;
QString escapeDependencyPath(const QString &path) const override;
ProString fixLibFlag(const ProString &lib) override;
bool processPrlFileBase(QString &origFile, QStringView origName,
QStringView fixedBase, int slashOff) override;
bool writeMakefile(QTextStream &) override;
void init() override;
QString installRoot() const override;
private:
void writeMingwParts(QTextStream &);
void writeIncPart(QTextStream &t) override;
void writeLibsPart(QTextStream &t) override;
void writeObjectsPart(QTextStream &t) override;
void writeBuildRulesPart(QTextStream &t) override;
void writeRcFilePart(QTextStream &t) override;
QStringList &findDependencies(const QString &file) override;
QString preCompHeaderOut;
LibFlagType parseLibFlag(const ProString &flag, ProString *arg) override;
QString objectsLinkLine;
LinkerResponseFileInfo linkerResponseFile;
};
QT_END_NAMESPACE
#endif // MINGW_MAKE_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,168 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MSBUILD_OBJECTMODEL_H
#define MSBUILD_OBJECTMODEL_H
#include "project.h"
#include "xmloutput.h"
#include "msvc_objectmodel.h"
#include <qlist.h>
#include <qstring.h>
#include <qmap.h>
QT_BEGIN_NAMESPACE
// Tree & Flat view of files --------------------------------------------------
class XNode
{
public:
virtual ~XNode() { }
void addElement(const VCFilterFile &file) {
addElement(file.file, file);
}
virtual void addElement(const QString &filepath, const VCFilterFile &allInfo) = 0;
virtual void removeElements()= 0;
virtual void generateXML(XmlOutput &xml, XmlOutput &xmlFilter, const QString &tagName,
VCProject &tool, const QString &filter) = 0;
virtual bool hasElements() = 0;
};
class XTreeNode : public XNode
{
typedef QMap<QString, XTreeNode*> ChildrenMap;
VCFilterFile info;
ChildrenMap children;
public:
virtual ~XTreeNode() { removeElements(); }
int pathIndex(const QString &filepath) {
int Windex = filepath.indexOf("\\");
int Uindex = filepath.indexOf("/");
if (Windex != -1 && Uindex != -1)
return qMin(Windex, Uindex);
else if (Windex != -1)
return Windex;
return Uindex;
}
void addElement(const QString &filepath, const VCFilterFile &allInfo) override {
QString newNodeName(filepath);
int index = pathIndex(filepath);
if (index != -1)
newNodeName = filepath.left(index);
XTreeNode *n = children.value(newNodeName);
if (!n) {
n = new XTreeNode;
n->info = allInfo;
children.insert(newNodeName, n);
}
if (index != -1)
n->addElement(filepath.mid(index+1), allInfo);
}
void removeElements() override {
ChildrenMap::ConstIterator it = children.constBegin();
ChildrenMap::ConstIterator end = children.constEnd();
for( ; it != end; it++) {
(*it)->removeElements();
delete it.value();
}
children.clear();
}
void generateXML(XmlOutput &xml, XmlOutput &xmlFilter, const QString &tagName, VCProject &tool,
const QString &filter) override;
bool hasElements() override {
return children.size() != 0;
}
};
class XFlatNode : public XNode
{
typedef QMap<QString, VCFilterFile> ChildrenMapFlat;
ChildrenMapFlat children;
public:
virtual ~XFlatNode() { removeElements(); }
int pathIndex(const QString &filepath) {
int Windex = filepath.lastIndexOf("\\");
int Uindex = filepath.lastIndexOf("/");
if (Windex != -1 && Uindex != -1)
return qMax(Windex, Uindex);
else if (Windex != -1)
return Windex;
return Uindex;
}
void addElement(const QString &filepath, const VCFilterFile &allInfo) override {
QString newKey(filepath);
int index = pathIndex(filepath);
if (index != -1)
newKey = filepath.mid(index+1);
// Key designed to sort files with same
// name in different paths correctly
children.insert(newKey + "\0" + allInfo.file, allInfo);
}
void removeElements() override {
children.clear();
}
void generateXML(XmlOutput &xml, XmlOutput &xmlFilter, const QString &tagName, VCProject &proj,
const QString &filter) override;
bool hasElements() override {
return children.size() != 0;
}
};
class VCXProjectWriter : public VCProjectWriter
{
public:
void write(XmlOutput &, VCProjectSingleConfig &) override;
void write(XmlOutput &, VCProject &) override;
void write(XmlOutput &, const VCCLCompilerTool &) override;
void write(XmlOutput &, const VCLinkerTool &) override;
void write(XmlOutput &, const VCMIDLTool &) override;
void write(XmlOutput &, const VCCustomBuildTool &) override;
void write(XmlOutput &, const VCLibrarianTool &) override;
void write(XmlOutput &, const VCResourceCompilerTool &) override;
void write(XmlOutput &, const VCEventTool &) override;
void write(XmlOutput &, const VCDeploymentTool &) override;
void write(XmlOutput &, const VCWinDeployQtTool &) override;
void write(XmlOutput &, const VCConfiguration &) override;
void write(XmlOutput &, VCFilter &) override;
private:
struct OutputFilterData
{
VCFilter filter;
VCFilterFile info;
bool inBuild;
};
static void addFilters(VCProject &project, XmlOutput &xmlFilter, const QString &filterName);
static void outputFilter(VCProject &project, XmlOutput &xml, XmlOutput &xmlFilter, const QString &filtername);
static void outputFileConfigs(VCProject &project, XmlOutput &xml, XmlOutput &xmlFilter,
const VCFilterFile &info, const QString &filtername);
static bool outputFileConfig(OutputFilterData *d, XmlOutput &xml, XmlOutput &xmlFilter,
const QString &filename, const QString &fullFilterName,
bool fileAdded, bool hasCustomBuildStep);
static void outputFileConfig(XmlOutput &xml, XmlOutput &xmlFilter, const QString &fileName, const QString &filterName);
static QString generateCondition(const VCConfiguration &config);
static XmlOutput::xml_output attrTagToolsVersion(const VCConfiguration &config);
friend class XTreeNode;
friend class XFlatNode;
};
QT_END_NAMESPACE
#endif // MSVC_OBJECTMODEL_H

View File

@ -0,0 +1,515 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "msvc_nmake.h"
#include "option.h"
#include <qregularexpression.h>
#include <qdir.h>
#include <qdiriterator.h>
#include <qset.h>
#include <time.h>
QT_BEGIN_NAMESPACE
bool
NmakeMakefileGenerator::writeMakefile(QTextStream &t)
{
writeHeader(t);
if (writeDummyMakefile(t))
return true;
if(project->first("TEMPLATE") == "app" ||
project->first("TEMPLATE") == "lib" ||
project->first("TEMPLATE") == "aux") {
writeNmakeParts(t);
return MakefileGenerator::writeMakefile(t);
}
else if(project->first("TEMPLATE") == "subdirs") {
writeSubDirs(t);
return true;
}
return false;
}
void NmakeMakefileGenerator::writeSubMakeCall(QTextStream &t, const QString &callPrefix,
const QString &makeArguments)
{
// Pass MAKEFLAGS as environment variable to sub-make calls.
// Unlike other make tools nmake doesn't do this automatically.
t << "\n\t@set MAKEFLAGS=$(MAKEFLAGS)";
Win32MakefileGenerator::writeSubMakeCall(t, callPrefix, makeArguments);
}
ProStringList NmakeMakefileGenerator::extraSubTargetDependencies()
{
return { "$(MAKEFILE)" };
}
QString NmakeMakefileGenerator::defaultInstall(const QString &t)
{
QString ret = Win32MakefileGenerator::defaultInstall(t);
if (ret.isEmpty())
return ret;
const QString root = installRoot();
ProStringList &uninst = project->values(ProKey(t + ".uninstall"));
QString targetdir = fileFixify(project->first(ProKey(t + ".path")).toQString(), FileFixifyAbsolute);
if(targetdir.right(1) != Option::dir_sep)
targetdir += Option::dir_sep;
if (project->isActiveConfig("debug_info")) {
if (t == "dlltarget" || project->values(ProKey(t + ".CONFIG")).indexOf("no_dll") == -1) {
const QFileInfo targetFileInfo(project->first("DESTDIR") + project->first("TARGET")
+ project->first("TARGET_EXT"));
const QString pdb_target = targetFileInfo.completeBaseName() + ".pdb";
QString src_targ = (project->isEmpty("DESTDIR") ? QString("$(DESTDIR)") : project->first("DESTDIR")) + pdb_target;
QString dst_targ = filePrefixRoot(root, fileFixify(targetdir + pdb_target, FileFixifyAbsolute));
if(!ret.isEmpty())
ret += "\n\t";
ret += QString("-$(INSTALL_FILE) ") + escapeFilePath(src_targ) + ' ' + escapeFilePath(dst_targ);
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + escapeFilePath(dst_targ));
}
}
return ret;
}
QStringList &NmakeMakefileGenerator::findDependencies(const QString &file)
{
QStringList &aList = MakefileGenerator::findDependencies(file);
for(QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) {
if(file.endsWith(*it)) {
if(!precompObj.isEmpty() && !aList.contains(precompObj))
aList += precompObj;
break;
}
}
for (QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) {
if (file.endsWith(*it)) {
if (!precompObjC.isEmpty() && !aList.contains(precompObjC))
aList += precompObjC;
break;
}
}
return aList;
}
void NmakeMakefileGenerator::writeNmakeParts(QTextStream &t)
{
writeStandardParts(t);
// precompiled header
if(usePCH) {
QString precompRule = QString("-c -Yc -Fp%1 -Fo%2")
.arg(escapeFilePath(precompPch), escapeFilePath(precompObj));
t << escapeDependencyPath(precompObj) << ": " << escapeDependencyPath(precompH) << ' '
<< finalizeDependencyPaths(findDependencies(precompH)).join(" \\\n\t\t")
<< "\n\t$(CXX) " + precompRule +" $(CXXFLAGS) $(INCPATH) -TP "
<< escapeFilePath(precompH) << Qt::endl << Qt::endl;
}
if (usePCHC) {
QString precompRuleC = QString("-c -Yc -Fp%1 -Fo%2")
.arg(escapeFilePath(precompPchC), escapeFilePath(precompObjC));
t << escapeDependencyPath(precompObjC) << ": " << escapeDependencyPath(precompH) << ' '
<< finalizeDependencyPaths(findDependencies(precompH)).join(" \\\n\t\t")
<< "\n\t$(CC) " + precompRuleC +" $(CFLAGS) $(INCPATH) -TC "
<< escapeFilePath(precompH) << Qt::endl << Qt::endl;
}
}
QString NmakeMakefileGenerator::var(const ProKey &value) const
{
if (usePCH || usePCHC) {
const bool isRunC = (value == "QMAKE_RUN_CC_IMP_BATCH"
|| value == "QMAKE_RUN_CC_IMP"
|| value == "QMAKE_RUN_CC");
const bool isRunCpp = (value == "QMAKE_RUN_CXX_IMP_BATCH"
|| value == "QMAKE_RUN_CXX_IMP"
|| value == "QMAKE_RUN_CXX");
if ((isRunCpp && usePCH) || (isRunC && usePCHC)) {
QString precompH_f = escapeFilePath(fileFixify(precompH, FileFixifyBackwards));
QString precompRule = QString("-c -FI%1 -Yu%2 -Fp%3")
.arg(precompH_f, precompH_f, escapeFilePath(isRunC ? precompPchC : precompPch));
// ### For clang_cl 8 we force inline methods to be compiled here instead
// linking them from a pch.o file. We do this by pretending we are also doing
// the pch.o generation step.
if (project->isActiveConfig("clang_cl"))
precompRule += QString(" -Xclang -building-pch-with-obj");
QString p = MakefileGenerator::var(value);
p.replace(QLatin1String("-c"), precompRule);
return p;
}
}
// Normal val
return MakefileGenerator::var(value);
}
void NmakeMakefileGenerator::suppressBuiltinRules(QTextStream &) const
{
}
void NmakeMakefileGenerator::init()
{
/* this should probably not be here, but I'm using it to wrap the .t files */
if(project->first("TEMPLATE") == "app")
project->values("QMAKE_APP_FLAG").append("1");
else if(project->first("TEMPLATE") == "lib")
project->values("QMAKE_LIB_FLAG").append("1");
else if(project->first("TEMPLATE") == "subdirs") {
MakefileGenerator::init();
if(project->values("MAKEFILE").isEmpty())
project->values("MAKEFILE").append("Makefile");
return;
}
processVars();
project->values("LIBS") += project->values("RES_FILE");
if (!project->values("DEF_FILE").isEmpty()) {
QString defFileName = fileFixify(project->first("DEF_FILE").toQString());
project->values("QMAKE_LFLAGS").append(QString("/DEF:") + escapeFilePath(defFileName));
}
// set /VERSION for EXE/DLL header
ProString major_minor = project->first("VERSION_PE_HEADER");
if (major_minor.isEmpty()) {
ProString version = project->first("VERSION");
if (!version.isEmpty()) {
int firstDot = version.indexOf(".");
int secondDot = version.indexOf(".", firstDot + 1);
major_minor = version.left(secondDot);
}
}
if (!major_minor.isEmpty())
project->values("QMAKE_LFLAGS").append("/VERSION:" + major_minor);
if (project->isEmpty("QMAKE_LINK_O_FLAG"))
project->values("QMAKE_LINK_O_FLAG").append("/OUT:");
// Base class init!
MakefileGenerator::init();
// Setup PCH variables
precompH = project->first("PRECOMPILED_HEADER").toQString();
usePCH = !precompH.isEmpty() && project->isActiveConfig("precompile_header");
usePCHC = !precompH.isEmpty() && project->isActiveConfig("precompile_header_c");
if (usePCH) {
// Created files
precompObj = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch" + Option::obj_ext;
precompPch = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch.pch";
// Add linking of precompObj (required for whole precompiled classes)
// ### For clang_cl we currently let inline methods be generated in the normal objects,
// since the PCH object is buggy (as of clang 8.0.0)
if (!project->isActiveConfig("clang_cl"))
project->values("OBJECTS") += precompObj;
// Add pch file to cleanup
project->values("QMAKE_CLEAN") += precompPch;
// Return to variable pool
project->values("PRECOMPILED_OBJECT") = ProStringList(precompObj);
project->values("PRECOMPILED_PCH") = ProStringList(precompPch);
}
if (usePCHC) {
precompObjC = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch_c" + Option::obj_ext;
precompPchC = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch_c.pch";
if (!project->isActiveConfig("clang_cl"))
project->values("OBJECTS") += precompObjC;
project->values("QMAKE_CLEAN") += precompPchC;
project->values("PRECOMPILED_OBJECT_C") = ProStringList(precompObjC);
project->values("PRECOMPILED_PCH_C") = ProStringList(precompPchC);
}
const QFileInfo targetFileInfo(project->first("DESTDIR") + project->first("TARGET")
+ project->first("TARGET_EXT"));
const ProString targetBase = targetFileInfo.path() + '/' + targetFileInfo.completeBaseName();
if (project->first("TEMPLATE") == "lib" && project->isActiveConfig("shared")) {
project->values("QMAKE_CLEAN").append(targetBase + ".exp");
project->values("QMAKE_DISTCLEAN").append(targetBase + ".lib");
}
if (project->isActiveConfig("debug_info")) {
QString pdbfile;
QString distPdbFile = targetBase + ".pdb";
if (project->isActiveConfig("staticlib")) {
// For static libraries, the compiler's pdb file and the dist pdb file are the same.
pdbfile = distPdbFile;
} else {
// Use $${TARGET}.vc.pdb in the OBJECTS_DIR for the compiler and
// $${TARGET}.pdb (the default) for the linker.
pdbfile = var("OBJECTS_DIR") + project->first("TARGET") + ".vc.pdb";
}
QString escapedPdbFile = escapeFilePath(pdbfile);
project->values("QMAKE_CFLAGS").append("/Fd" + escapedPdbFile);
project->values("QMAKE_CXXFLAGS").append("/Fd" + escapedPdbFile);
project->values("QMAKE_CLEAN").append(pdbfile);
project->values("QMAKE_DISTCLEAN").append(distPdbFile);
}
if (project->isActiveConfig("debug")) {
project->values("QMAKE_CLEAN").append(targetBase + ".ilk");
project->values("QMAKE_CLEAN").append(targetBase + ".idb");
}
if (project->values("QMAKE_APP_FLAG").isEmpty() && project->isActiveConfig("dll")) {
ProStringList &defines = project->values("DEFINES");
if (!defines.contains("_WINDLL"))
defines.append("_WINDLL");
}
}
QStringList NmakeMakefileGenerator::sourceFilesForImplicitRulesFilter()
{
QStringList filter;
const QChar wildcard = QLatin1Char('*');
for (const QString &ext : std::as_const(Option::c_ext))
filter << wildcard + ext;
for (const QString &ext : std::as_const(Option::cpp_ext))
filter << wildcard + ext;
return filter;
}
void NmakeMakefileGenerator::writeImplicitRulesPart(QTextStream &t)
{
t << "####### Implicit rules\n\n";
t << ".SUFFIXES:";
for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit)
t << " " << (*cit);
for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit)
t << " " << (*cppit);
t << Qt::endl << Qt::endl;
bool useInferenceRules = !project->isActiveConfig("no_batch");
QSet<QString> source_directories;
if (useInferenceRules) {
source_directories.insert(".");
static const char * const directories[] = { "UI_SOURCES_DIR", "UI_DIR", nullptr };
for (int y = 0; directories[y]; y++) {
QString dirTemp = project->first(directories[y]).toQString();
if (dirTemp.endsWith("\\"))
dirTemp.truncate(dirTemp.size()-1);
if(!dirTemp.isEmpty())
source_directories.insert(dirTemp);
}
static const char * const srcs[] = { "SOURCES", "GENERATED_SOURCES", nullptr };
for (int x = 0; srcs[x]; x++) {
const ProStringList &l = project->values(srcs[x]);
for (ProStringList::ConstIterator sit = l.begin(); sit != l.end(); ++sit) {
QString sep = "\\";
if((*sit).indexOf(sep) == -1)
sep = "/";
QString dir = (*sit).toQString().section(sep, 0, -2);
if (!dir.isEmpty())
source_directories.insert(dir);
}
}
// nmake's inference rules might pick up the wrong files when encountering source files with
// the same name in different directories. In this situation, turn inference rules off.
QHash<QString, QString> fileNames;
bool duplicatesFound = false;
const QStringList sourceFilesFilter = sourceFilesForImplicitRulesFilter();
QStringList fixifiedSourceDirs = fileFixify(QList<QString>(source_directories.constBegin(), source_directories.constEnd()), FileFixifyAbsolute);
fixifiedSourceDirs.removeDuplicates();
for (const QString &sourceDir : std::as_const(fixifiedSourceDirs)) {
QDirIterator dit(sourceDir, sourceFilesFilter, QDir::Files | QDir::NoDotAndDotDot);
while (dit.hasNext()) {
const QFileInfo fi = dit.nextFileInfo();
QString &duplicate = fileNames[fi.completeBaseName()];
if (duplicate.isNull()) {
duplicate = fi.filePath();
} else {
warn_msg(WarnLogic, "%s conflicts with %s", qPrintable(duplicate),
qPrintable(fi.filePath()));
duplicatesFound = true;
}
}
}
if (duplicatesFound) {
useInferenceRules = false;
warn_msg(WarnLogic, "Automatically turning off nmake's inference rules. (CONFIG += no_batch)");
}
}
if (useInferenceRules) {
// Batchmode doesn't use the non implicit rules QMAKE_RUN_CXX & QMAKE_RUN_CC
project->variables().remove("QMAKE_RUN_CXX");
project->variables().remove("QMAKE_RUN_CC");
for (const QString &sourceDir : std::as_const(source_directories)) {
if (sourceDir.isEmpty())
continue;
QString objDir = var("OBJECTS_DIR");
if (objDir == ".\\")
objDir = "";
for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit)
t << '{' << escapeDependencyPath(sourceDir) << '}' << (*cppit)
<< '{' << escapeDependencyPath(objDir) << '}' << Option::obj_ext << "::\n\t"
<< var("QMAKE_RUN_CXX_IMP_BATCH").replace(QRegularExpression("\\$@"), fileVar("OBJECTS_DIR"))
<< "\n\t$<\n<<\n\n";
for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit)
t << '{' << escapeDependencyPath(sourceDir) << '}' << (*cit)
<< '{' << escapeDependencyPath(objDir) << '}' << Option::obj_ext << "::\n\t"
<< var("QMAKE_RUN_CC_IMP_BATCH").replace(QRegularExpression("\\$@"), fileVar("OBJECTS_DIR"))
<< "\n\t$<\n<<\n\n";
}
} else {
for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit)
t << (*cppit) << Option::obj_ext << ":\n\t" << var("QMAKE_RUN_CXX_IMP") << Qt::endl << Qt::endl;
for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit)
t << (*cit) << Option::obj_ext << ":\n\t" << var("QMAKE_RUN_CC_IMP") << Qt::endl << Qt::endl;
}
}
void NmakeMakefileGenerator::writeBuildRulesPart(QTextStream &t)
{
const ProString templateName = project->first("TEMPLATE");
t << "first: all\n";
t << "all: " << escapeDependencyPath(fileFixify(Option::output.fileName()))
<< ' ' << depVar("ALL_DEPS") << ' ' << depVar("DEST_TARGET") << "\n\n";
t << depVar("DEST_TARGET") << ": "
<< depVar("PRE_TARGETDEPS") << " $(OBJECTS) " << depVar("POST_TARGETDEPS");
if (templateName == "aux") {
t << "\n\n";
return;
}
if(!project->isEmpty("QMAKE_PRE_LINK"))
t << "\n\t" <<var("QMAKE_PRE_LINK");
if(project->isActiveConfig("staticlib")) {
t << "\n\t$(LIBAPP) $(LIBFLAGS) " << var("QMAKE_LINK_O_FLAG") << "$(DESTDIR_TARGET) @<<\n\t ";
writeResponseFileFiles(t, project->values("OBJECTS"));
t << "<<";
} else {
const bool embedManifest = ((templateName == "app" && project->isActiveConfig("embed_manifest_exe"))
|| (templateName == "lib" && project->isActiveConfig("embed_manifest_dll")
&& !(project->isActiveConfig("plugin") && project->isActiveConfig("no_plugin_manifest"))
));
if (embedManifest) {
bool generateManifest = false;
const QString target = var("DEST_TARGET");
QString manifest = project->first("QMAKE_MANIFEST").toQString();
QString extraLFlags;
const bool linkerSupportsEmbedding = (msvcVersion() >= 1200);
if (manifest.isEmpty()) {
generateManifest = true;
if (linkerSupportsEmbedding) {
extraLFlags = "/MANIFEST:embed";
} else {
manifest = target + ".embed.manifest";
extraLFlags += "/MANIFEST /MANIFESTFILE:" + escapeFilePath(manifest);
project->values("QMAKE_CLEAN") << manifest;
}
} else {
manifest = fileFixify(manifest);
if (linkerSupportsEmbedding)
extraLFlags = "/MANIFEST:embed /MANIFESTINPUT:" + escapeFilePath(manifest);
}
const QString resourceId = (templateName == "app") ? "1" : "2";
const bool incrementalLinking = project->values("QMAKE_LFLAGS").toQStringList().filter(QRegularExpression("(/|-)INCREMENTAL:NO")).isEmpty();
if (incrementalLinking && !linkerSupportsEmbedding) {
// Link a resource that contains the manifest without modifying the exe/dll after linking.
QString manifest_rc = target + "_manifest.rc";
QString manifest_res = target + "_manifest.res";
project->values("QMAKE_CLEAN") << manifest_rc << manifest_res;
manifest_rc = escapeFilePath(manifest_rc);
manifest_res = escapeFilePath(manifest_res);
t << "\n\techo " << resourceId
<< " /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 24 /* RT_MANIFEST */ "
<< cQuoted(manifest) << '>' << manifest_rc;
if (generateManifest) {
manifest = escapeFilePath(manifest);
QString manifest_bak = escapeFilePath(target + "_manifest.bak");
project->values("QMAKE_CLEAN") << manifest_bak;
t << "\n\tif not exist $(DESTDIR_TARGET) if exist " << manifest
<< " del " << manifest;
t << "\n\tif exist " << manifest << " copy /Y " << manifest << ' ' << manifest_bak;
const QString extraInlineFileContent = "\n!IF EXIST(" + manifest_res + ")\n" + manifest_res + "\n!ENDIF";
t << "\n\t";
writeLinkCommand(t, extraLFlags, extraInlineFileContent);
t << "\n\tif exist " << manifest_bak << " fc /b " << manifest << ' ' << manifest_bak << " >NUL || del " << manifest_bak;
t << "\n\tif not exist " << manifest_bak << " rc.exe /fo" << manifest_res << ' ' << manifest_rc;
t << "\n\tif not exist " << manifest_bak << ' ';
writeLinkCommand(t, extraLFlags, manifest_res);
t << "\n\tif exist " << manifest_bak << " del " << manifest_bak;
} else {
t << "\n\trc.exe /fo" << manifest_res << " " << manifest_rc;
t << "\n\t";
writeLinkCommand(t, extraLFlags, manifest_res);
}
} else {
// directly embed the manifest in the executable after linking
t << "\n\t";
writeLinkCommand(t, extraLFlags);
if (!linkerSupportsEmbedding) {
t << "\n\tmt.exe /nologo /manifest " << escapeFilePath(manifest)
<< " /outputresource:$(DESTDIR_TARGET);" << resourceId;
}
}
} else {
t << "\n\t";
writeLinkCommand(t);
}
}
if(!project->isEmpty("QMAKE_POST_LINK")) {
t << "\n\t" << var("QMAKE_POST_LINK");
}
t << Qt::endl;
}
void NmakeMakefileGenerator::writeLinkCommand(QTextStream &t, const QString &extraFlags, const QString &extraInlineFileContent)
{
t << "$(LINKER) $(LFLAGS)";
if (!extraFlags.isEmpty())
t << ' ' << extraFlags;
t << " " << var("QMAKE_LINK_O_FLAG") << "$(DESTDIR_TARGET) @<<\n";
writeResponseFileFiles(t, project->values("OBJECTS"));
t << "$(LIBS)\n";
if (!extraInlineFileContent.isEmpty())
t << extraInlineFileContent << '\n';
t << "<<";
}
void NmakeMakefileGenerator::writeResponseFileFiles(QTextStream &t, const ProStringList &files)
{
// Add line breaks in file lists in reponse files to work around LNK1170.
// The actual line length limit is 131070, but let's use a smaller limit
// in case other tools are similarly hampered.
const int maxLineLength = 1000;
int len = 0;
for (const ProString &file : files) {
const ProString escapedFilePath = escapeFilePath(file);
if (len) {
if (len + escapedFilePath.length() > maxLineLength) {
t << '\n';
len = 0;
} else {
t << ' ';
len++;
}
}
t << escapedFilePath;
len += escapedFilePath.length();
}
t << '\n';
}
int NmakeMakefileGenerator::msvcVersion() const
{
const int fallbackVersion = 800; // Visual Studio 2005
const QString ver = project->first(ProKey("MSVC_VER")).toQString();
bool ok;
float f = ver.toFloat(&ok);
return ok ? int(f * 100) : fallbackVersion;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,39 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MSVC_NMAKE_H
#define MSVC_NMAKE_H
#include "winmakefile.h"
QT_BEGIN_NAMESPACE
class NmakeMakefileGenerator : public Win32MakefileGenerator
{
void writeNmakeParts(QTextStream &);
bool writeMakefile(QTextStream &) override;
void writeImplicitRulesPart(QTextStream &t) override;
void writeBuildRulesPart(QTextStream &t) override;
void writeLinkCommand(QTextStream &t, const QString &extraFlags = QString(), const QString &extraInlineFileContent = QString());
void writeResponseFileFiles(QTextStream &t, const ProStringList &files);
int msvcVersion() const;
void init() override;
static QStringList sourceFilesForImplicitRulesFilter();
protected:
void writeSubMakeCall(QTextStream &t, const QString &callPrefix,
const QString &makeArguments) override;
ProStringList extraSubTargetDependencies() override;
QString defaultInstall(const QString &t) override;
QStringList &findDependencies(const QString &file) override;
QString var(const ProKey &value) const override;
void suppressBuiltinRules(QTextStream &t) const override;
QString precompH, precompObj, precompPch;
QString precompObjC, precompPchC;
bool usePCH = false;
bool usePCHC = false;
};
QT_END_NAMESPACE
#endif // MSVC_NMAKE_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,125 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MSVC_VCPROJ_H
#define MSVC_VCPROJ_H
#include "winmakefile.h"
#include "msvc_objectmodel.h"
QT_BEGIN_NAMESPACE
enum Target {
Application,
SharedLib,
StaticLib
};
class QUuid;
struct VcsolutionDepend;
class VcprojGenerator : public Win32MakefileGenerator
{
bool is64Bit;
bool writeVcprojParts(QTextStream &);
bool writeMakefile(QTextStream &) override;
bool writeProjectMakefile() override;
void init() override;
public:
VcprojGenerator();
~VcprojGenerator();
QString defaultMakefile() const;
QString precompH, precompHFilename, precompSource,
precompObj, precompPch;
bool autogenPrecompSource;
static bool hasBuiltinCompiler(const QString &file);
QHash<QString, QStringList> extraCompilerSources;
QHash<QString, QString> extraCompilerOutputs;
const QString customBuildToolFilterFileSuffix;
bool usePCH;
bool pchIsCFile = false;
VCProjectWriter *projectWriter;
using Win32MakefileGenerator::callExtraCompilerDependCommand;
protected:
virtual VCProjectWriter *createProjectWriter();
bool doDepends() const override { return false; } // Never necessary
using Win32MakefileGenerator::replaceExtraCompilerVariables;
QString replaceExtraCompilerVariables(const QString &, const QStringList &, const QStringList &, ReplaceFor) override;
QString extraCompilerName(const ProString &extraCompiler, const QStringList &inputs,
const QStringList &outputs);
bool supportsMetaBuild() override { return true; }
bool supportsMergedBuilds() override { return true; }
bool mergeBuildProject(MakefileGenerator *other) override;
bool openOutput(QFile &file, const QString &build) const override;
virtual void initProject();
void initConfiguration();
void initCompilerTool();
void initLinkerTool();
void initLibrarianTool();
void initManifestTool();
void initResourceTool();
void initIDLTool();
void initCustomBuildTool();
void initPreBuildEventTools();
void initPostBuildEventTools();
void initDeploymentTool();
void initWinDeployQtTool();
void initPreLinkEventTools();
void initRootFiles();
void initSourceFiles();
void initHeaderFiles();
void initGeneratedFiles();
void initTranslationFiles();
void initFormFiles();
void initResourceFiles();
void initDeploymentFiles();
void initDistributionFiles();
void initLexYaccFiles();
void initExtraCompilerOutputs();
void writeSubDirs(QTextStream &t); // Called from VCXProj backend
QUuid getProjectUUID(const QString &filename=QString()); // Called from VCXProj backend
Target projectTarget;
// Used for single project
VCProjectSingleConfig vcProject;
// Holds all configurations for glue (merged) project
QList<VcprojGenerator*> mergedProjects;
private:
ProStringList collectDependencies(QMakeProject *proj, QHash<QString, QString> &projLookup,
QHash<QString, QString> &projGuids,
QHash<VcsolutionDepend *, QStringList> &extraSubdirs,
QHash<QString, VcsolutionDepend*> &solution_depends,
QList<VcsolutionDepend*> &solution_cleanup,
QTextStream &t,
QHash<QString, ProStringList> &subdirProjectLookup,
const ProStringList &allDependencies = ProStringList());
QUuid increaseUUID(const QUuid &id);
QString retrievePlatformToolSet() const;
bool isStandardSuffix(const QString &suffix) const;
ProString firstInputFileName(const ProString &extraCompilerName) const;
QString firstExpandedOutputFileName(const ProString &extraCompilerName);
void createCustomBuildToolFakeFile(const QString &cbtFilePath, const QString &realOutFilePath);
bool otherFiltersContain(const QString &fileName) const;
friend class VCFilter;
};
inline QString VcprojGenerator::defaultMakefile() const
{
return project->first("TARGET") + project->first("VCPROJ_EXTENSION");
}
QT_END_NAMESPACE
#endif // MSVC_VCPROJ_H

View File

@ -0,0 +1,18 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "msvc_vcxproj.h"
#include "msbuild_objectmodel.h"
QT_BEGIN_NAMESPACE
VcxprojGenerator::VcxprojGenerator() : VcprojGenerator()
{
}
VCProjectWriter *VcxprojGenerator::createProjectWriter()
{
return new VCXProjectWriter;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,22 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MSVC_VCXPROJ_H
#define MSVC_VCXPROJ_H
#include "msvc_vcproj.h"
QT_BEGIN_NAMESPACE
class VcxprojGenerator : public VcprojGenerator
{
public:
VcxprojGenerator();
protected:
VCProjectWriter *createProjectWriter() override;
};
QT_END_NAMESPACE
#endif // MSVC_VCXPROJ_H

View File

@ -0,0 +1,861 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "winmakefile.h"
#include "option.h"
#include "project.h"
#include "meta.h"
#include <qtextstream.h>
#include <qstring.h>
#include <qhash.h>
#include <qregularexpression.h>
#include <qstringlist.h>
#include <qdir.h>
#include <stdlib.h>
#include <algorithm>
QT_BEGIN_NAMESPACE
ProString Win32MakefileGenerator::fixLibFlag(const ProString &lib)
{
if (lib.startsWith("-l")) // Fallback for unresolved -l libs.
return escapeFilePath(lib.mid(2) + QLatin1String(".lib"));
if (lib.startsWith("-L")) // Lib search path. Needed only by -l above.
return QLatin1String("/LIBPATH:")
+ escapeFilePath(Option::fixPathToTargetOS(lib.mid(2).toQString(), false));
return escapeFilePath(Option::fixPathToTargetOS(lib.toQString(), false));
}
MakefileGenerator::LibFlagType
Win32MakefileGenerator::parseLibFlag(const ProString &flag, ProString *arg)
{
LibFlagType ret = MakefileGenerator::parseLibFlag(flag, arg);
if (ret != LibFlagFile)
return ret;
// MSVC compatibility. This should be deprecated.
if (flag.startsWith("/LIBPATH:")) {
*arg = flag.mid(9);
return LibFlagPath;
}
// These are pure qmake inventions. They *really* should be deprecated.
if (flag.startsWith("/L")) {
*arg = flag.mid(2);
return LibFlagPath;
}
if (flag.startsWith("/l")) {
*arg = flag.mid(2);
return LibFlagLib;
}
return LibFlagFile;
}
class LibrarySearchPath : public QMakeLocalFileName
{
public:
LibrarySearchPath() = default;
LibrarySearchPath(const QString &s)
: QMakeLocalFileName(s)
{
}
LibrarySearchPath(QString &&s, bool isDefault = false)
: QMakeLocalFileName(std::move(s)), _default(isDefault)
{
}
bool isDefault() const { return _default; }
private:
bool _default = false;
};
bool
Win32MakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags)
{
ProStringList impexts = project->values("QMAKE_LIB_EXTENSIONS");
if (impexts.isEmpty())
impexts = project->values("QMAKE_EXTENSION_STATICLIB");
QList<LibrarySearchPath> dirs;
int libidx = 0;
for (const ProString &dlib : project->values("QMAKE_DEFAULT_LIBDIRS"))
dirs.append(LibrarySearchPath(dlib.toQString(), true));
static const char * const lflags[] = { "LIBS", "LIBS_PRIVATE",
"QMAKE_LIBS", "QMAKE_LIBS_PRIVATE", nullptr };
for (int i = 0; lflags[i]; i++) {
ProStringList &l = project->values(lflags[i]);
for (ProStringList::Iterator it = l.begin(); it != l.end();) {
const ProString &opt = *it;
ProString arg;
LibFlagType type = parseLibFlag(opt, &arg);
if (type == LibFlagPath) {
const QString argqstr = arg.toQString();
auto dit = std::find_if(dirs.cbegin(), dirs.cend(),
[&argqstr](const LibrarySearchPath &p)
{
return p.real() == argqstr;
});
int idx = dit == dirs.cend()
? -1
: std::distance(dirs.cbegin(), dit);
if (idx >= 0 && idx < libidx) {
it = l.erase(it);
continue;
}
const LibrarySearchPath lp(argqstr);
dirs.insert(libidx++, lp);
(*it) = "-L" + lp.real();
} else if (type == LibFlagLib) {
QString lib = arg.toQString();
ProString verovr =
project->first(ProKey("QMAKE_" + lib.toUpper() + "_VERSION_OVERRIDE"));
for (auto dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) {
QString cand = (*dir_it).real() + Option::dir_sep + lib;
if (linkPrl && processPrlFile(cand, true)) {
(*it) = cand;
goto found;
}
QString libBase = (*dir_it).local() + '/' + lib + verovr;
for (ProStringList::ConstIterator extit = impexts.cbegin();
extit != impexts.cend(); ++extit) {
if (exists(libBase + '.' + *extit)) {
*it = (dir_it->isDefault() ? lib : cand)
+ verovr + '.' + *extit;
goto found;
}
}
}
// We assume if it never finds it that it's correct
found: ;
} else if (linkPrl && type == LibFlagFile) {
QString lib = opt.toQString();
if (fileInfo(lib).isAbsolute()) {
if (processPrlFile(lib, false))
(*it) = lib;
} else {
for (auto dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) {
QString cand = (*dir_it).real() + Option::dir_sep + lib;
if (processPrlFile(cand, false)) {
(*it) = cand;
break;
}
}
}
}
ProStringList &prl_libs = project->values("QMAKE_CURRENT_PRL_LIBS");
for (int prl = 0; prl < prl_libs.size(); ++prl)
it = l.insert(++it, prl_libs.at(prl));
prl_libs.clear();
++it;
}
if (mergeLflags) {
ProStringList lopts;
for (int lit = 0; lit < l.size(); ++lit) {
ProString opt = l.at(lit);
if (opt.startsWith(QLatin1String("-L"))) {
if (!lopts.contains(opt))
lopts.append(opt);
} else {
// Make sure we keep the dependency order of libraries
lopts.removeAll(opt);
lopts.append(opt);
}
}
l = lopts;
}
}
return true;
}
bool Win32MakefileGenerator::processPrlFileBase(QString &origFile, QStringView origName,
QStringView fixedBase, int slashOff)
{
if (MakefileGenerator::processPrlFileBase(origFile, origName, fixedBase, slashOff))
return true;
for (int off = fixedBase.size(); off > slashOff; off--) {
if (!fixedBase.at(off - 1).isDigit()) {
if (off != fixedBase.size()) {
return MakefileGenerator::processPrlFileBase(
origFile, origName, fixedBase.left(off), slashOff);
}
break;
}
}
return false;
}
void Win32MakefileGenerator::processVars()
{
if (project->first("TEMPLATE").endsWith("aux"))
return;
project->values("PRL_TARGET") =
project->values("QMAKE_ORIG_TARGET") = project->values("TARGET");
if (project->isEmpty("QMAKE_PROJECT_NAME"))
project->values("QMAKE_PROJECT_NAME") = project->values("QMAKE_ORIG_TARGET");
else if (project->first("TEMPLATE").startsWith("vc"))
project->values("MAKEFILE") = project->values("QMAKE_PROJECT_NAME");
project->values("QMAKE_INCDIR") += project->values("QMAKE_INCDIR_POST");
project->values("QMAKE_LIBDIR") += project->values("QMAKE_LIBDIR_POST");
if (!project->values("QMAKE_INCDIR").isEmpty())
project->values("INCLUDEPATH") += project->values("QMAKE_INCDIR");
if (!project->values("VERSION").isEmpty()) {
QStringList l = project->first("VERSION").toQString().split('.');
if (l.size() > 0)
project->values("VER_MAJ").append(l[0]);
if (l.size() > 1)
project->values("VER_MIN").append(l[1]);
}
// TARGET_VERSION_EXT will be used to add a version number onto the target name
if (!project->isActiveConfig("skip_target_version_ext")
&& project->values("TARGET_VERSION_EXT").isEmpty()
&& !project->values("VER_MAJ").isEmpty())
project->values("TARGET_VERSION_EXT").append(project->first("VER_MAJ"));
fixTargetExt();
processRcFileVar();
ProStringList libs;
ProStringList &libDir = project->values("QMAKE_LIBDIR");
for (ProStringList::Iterator libDir_it = libDir.begin(); libDir_it != libDir.end(); ++libDir_it) {
QString lib = (*libDir_it).toQString();
if (!lib.isEmpty()) {
if (lib.endsWith('\\'))
lib.chop(1);
libs << QLatin1String("-L") + lib;
}
}
ProStringList &qmklibs = project->values("LIBS");
qmklibs = libs + qmklibs;
if (project->values("TEMPLATE").contains("app")) {
project->values("QMAKE_CFLAGS") += project->values("QMAKE_CFLAGS_APP");
project->values("QMAKE_CXXFLAGS") += project->values("QMAKE_CXXFLAGS_APP");
project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_APP");
} else if (project->values("TEMPLATE").contains("lib") && project->isActiveConfig("dll")) {
if(!project->isActiveConfig("plugin") || !project->isActiveConfig("plugin_no_share_shlib_cflags")) {
project->values("QMAKE_CFLAGS") += project->values("QMAKE_CFLAGS_SHLIB");
project->values("QMAKE_CXXFLAGS") += project->values("QMAKE_CXXFLAGS_SHLIB");
}
if (project->isActiveConfig("plugin")) {
project->values("QMAKE_CFLAGS") += project->values("QMAKE_CFLAGS_PLUGIN");
project->values("QMAKE_CXXFLAGS") += project->values("QMAKE_CXXFLAGS_PLUGIN");
project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_PLUGIN");
} else {
project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_SHLIB");
}
}
}
void Win32MakefileGenerator::fixTargetExt()
{
if (!project->values("QMAKE_APP_FLAG").isEmpty()) {
project->values("TARGET_EXT").append(".exe");
} else if (project->isActiveConfig("shared")) {
project->values("LIB_TARGET").prepend(project->first("QMAKE_PREFIX_STATICLIB")
+ project->first("TARGET") + project->first("TARGET_VERSION_EXT")
+ '.' + project->first("QMAKE_EXTENSION_STATICLIB"));
project->values("TARGET_EXT").append(project->first("TARGET_VERSION_EXT") + "."
+ project->first("QMAKE_EXTENSION_SHLIB"));
project->values("TARGET").first() = project->first("QMAKE_PREFIX_SHLIB") + project->first("TARGET");
} else {
project->values("TARGET_EXT").append("." + project->first("QMAKE_EXTENSION_STATICLIB"));
project->values("TARGET").first() = project->first("QMAKE_PREFIX_STATICLIB") + project->first("TARGET");
project->values("LIB_TARGET").prepend(project->first("TARGET") + project->first("TARGET_EXT")); // for the .prl only
}
}
void Win32MakefileGenerator::processRcFileVar()
{
if (Option::qmake_mode == Option::QMAKE_GENERATE_NOTHING)
return;
const QString manifestFile = project->first("QMAKE_MANIFEST").toQString();
if (((!project->values("VERSION").isEmpty() || !project->values("RC_ICONS").isEmpty() || !manifestFile.isEmpty())
&& project->values("RC_FILE").isEmpty()
&& project->values("RES_FILE").isEmpty()
&& !project->isActiveConfig("no_generated_target_info")
&& (project->isActiveConfig("shared") || !project->values("QMAKE_APP_FLAG").isEmpty()))
|| !project->values("QMAKE_WRITE_DEFAULT_RC").isEmpty()){
QByteArray rcString;
QTextStream ts(&rcString, QFile::WriteOnly);
QStringList vers = project->first("VERSION").toQString().split(".", Qt::SkipEmptyParts);
for (int i = vers.size(); i < 4; i++)
vers += "0";
QString versionString = vers.join('.');
QStringList rcIcons;
const auto icons = project->values("RC_ICONS");
rcIcons.reserve(icons.size());
for (const ProString &icon : icons)
rcIcons.append(fileFixify(icon.toQString(), FileFixifyAbsolute));
QString companyName;
if (!project->values("QMAKE_TARGET_COMPANY").isEmpty())
companyName = project->values("QMAKE_TARGET_COMPANY").join(' ');
QString description;
if (!project->values("QMAKE_TARGET_DESCRIPTION").isEmpty())
description = project->values("QMAKE_TARGET_DESCRIPTION").join(' ');
QString copyright;
if (!project->values("QMAKE_TARGET_COPYRIGHT").isEmpty())
copyright = project->values("QMAKE_TARGET_COPYRIGHT").join(' ');
QString productName;
if (!project->values("QMAKE_TARGET_PRODUCT").isEmpty())
productName = project->values("QMAKE_TARGET_PRODUCT").join(' ');
else
productName = project->first("TARGET").toQString();
QString originalName;
if (!project->values("QMAKE_TARGET_ORIGINAL_FILENAME").isEmpty())
originalName = project->values("QMAKE_TARGET_ORIGINAL_FILENAME").join(' ');
else
originalName = project->first("TARGET") + project->first("TARGET_EXT");
QString internalName;
if (!project->values("QMAKE_TARGET_INTERNALNAME").isEmpty())
internalName = project->values("QMAKE_TARGET_INTERNALNAME").join(' ');
QString comments;
if (!project->values("QMAKE_TARGET_COMMENTS").isEmpty())
comments = project->values("QMAKE_TARGET_COMMENTS").join(' ');
QString trademarks;
if (!project->values("QMAKE_TARGET_TRADEMARKS").isEmpty())
trademarks = project->values("QMAKE_TARGET_TRADEMARKS").join(' ');
int rcLang = project->intValue("RC_LANG", 1033); // default: English(USA)
int rcCodePage = project->intValue("RC_CODEPAGE", 1200); // default: Unicode
ts << "#include <windows.h>\n";
ts << Qt::endl;
if (!rcIcons.isEmpty()) {
for (int i = 0; i < rcIcons.size(); ++i)
ts << QString("IDI_ICON%1\tICON\t%2").arg(i + 1).arg(cQuoted(rcIcons[i])) << Qt::endl;
ts << Qt::endl;
}
if (!manifestFile.isEmpty()) {
QString manifestResourceId;
if (project->first("TEMPLATE") == "lib")
manifestResourceId = QStringLiteral("ISOLATIONAWARE_MANIFEST_RESOURCE_ID");
else
manifestResourceId = QStringLiteral("CREATEPROCESS_MANIFEST_RESOURCE_ID");
ts << manifestResourceId << " RT_MANIFEST \"" << manifestFile << "\"\n";
}
ts << "VS_VERSION_INFO VERSIONINFO\n";
ts << "\tFILEVERSION " << QString(versionString).replace(".", ",") << Qt::endl;
ts << "\tPRODUCTVERSION " << QString(versionString).replace(".", ",") << Qt::endl;
ts << "\tFILEFLAGSMASK 0x3fL\n";
ts << "#ifdef _DEBUG\n";
ts << "\tFILEFLAGS VS_FF_DEBUG\n";
ts << "#else\n";
ts << "\tFILEFLAGS 0x0L\n";
ts << "#endif\n";
ts << "\tFILEOS VOS_NT_WINDOWS32\n";
if (project->isActiveConfig("shared"))
ts << "\tFILETYPE VFT_DLL\n";
else
ts << "\tFILETYPE VFT_APP\n";
ts << "\tFILESUBTYPE VFT2_UNKNOWN\n";
ts << "\tBEGIN\n";
ts << "\t\tBLOCK \"StringFileInfo\"\n";
ts << "\t\tBEGIN\n";
ts << "\t\t\tBLOCK \""
<< QString("%1%2").arg(rcLang, 4, 16, QLatin1Char('0')).arg(rcCodePage, 4, 16, QLatin1Char('0'))
<< "\"\n";
ts << "\t\t\tBEGIN\n";
ts << "\t\t\t\tVALUE \"CompanyName\", \"" << companyName << "\\0\"\n";
ts << "\t\t\t\tVALUE \"FileDescription\", \"" << description << "\\0\"\n";
ts << "\t\t\t\tVALUE \"FileVersion\", \"" << versionString << "\\0\"\n";
ts << "\t\t\t\tVALUE \"LegalCopyright\", \"" << copyright << "\\0\"\n";
ts << "\t\t\t\tVALUE \"OriginalFilename\", \"" << originalName << "\\0\"\n";
ts << "\t\t\t\tVALUE \"ProductName\", \"" << productName << "\\0\"\n";
ts << "\t\t\t\tVALUE \"ProductVersion\", \"" << versionString << "\\0\"\n";
ts << "\t\t\t\tVALUE \"InternalName\", \"" << internalName << "\\0\"\n";
ts << "\t\t\t\tVALUE \"Comments\", \"" << comments << "\\0\"\n";
ts << "\t\t\t\tVALUE \"LegalTrademarks\", \"" << trademarks << "\\0\"\n";
ts << "\t\t\tEND\n";
ts << "\t\tEND\n";
ts << "\t\tBLOCK \"VarFileInfo\"\n";
ts << "\t\tBEGIN\n";
ts << "\t\t\tVALUE \"Translation\", "
<< QString("0x%1").arg(rcLang, 4, 16, QLatin1Char('0'))
<< ", " << QString("%1").arg(rcCodePage, 4) << Qt::endl;
ts << "\t\tEND\n";
ts << "\tEND\n";
ts << "/* End of Version info */\n";
ts << Qt::endl;
ts.flush();
QString rcFilename = project->first("OUT_PWD")
+ "/"
+ project->first("TARGET")
+ "_resource"
+ ".rc";
QFile rcFile(QDir::cleanPath(rcFilename));
bool writeRcFile = true;
if (rcFile.exists() && rcFile.open(QFile::ReadOnly)) {
writeRcFile = rcFile.readAll() != rcString;
rcFile.close();
}
if (writeRcFile) {
bool ok;
ok = rcFile.open(QFile::WriteOnly);
if (!ok) {
// The file can't be opened... try creating the containing
// directory first (needed for clean shadow builds)
QDir().mkpath(QFileInfo(rcFile).path());
ok = rcFile.open(QFile::WriteOnly);
}
if (!ok) {
::fprintf(stderr, "Cannot open for writing: %s", rcFile.fileName().toLatin1().constData());
::exit(1);
}
rcFile.write(rcString);
rcFile.close();
}
if (project->values("QMAKE_WRITE_DEFAULT_RC").isEmpty())
project->values("RC_FILE").insert(0, rcFile.fileName());
}
if (!project->values("RC_FILE").isEmpty()) {
if (!project->values("RES_FILE").isEmpty()) {
fprintf(stderr, "Both rc and res file specified.\n");
fprintf(stderr, "Please specify one of them, not both.");
exit(1);
}
QString resFile = project->first("RC_FILE").toQString();
// if this is a shadow build then use the absolute path of the rc file
if (Option::output_dir != qmake_getpwd()) {
QFileInfo fi(resFile);
project->values("RC_FILE").first() = fi.absoluteFilePath();
}
resFile.replace(QLatin1String(".rc"), Option::res_ext);
project->values("RES_FILE").prepend(fileInfo(resFile).fileName());
QString resDestDir;
if (project->isActiveConfig("staticlib"))
resDestDir = project->first("DESTDIR").toQString();
else
resDestDir = project->first("OBJECTS_DIR").toQString();
if (!resDestDir.isEmpty()) {
resDestDir.append(Option::dir_sep);
project->values("RES_FILE").first().prepend(resDestDir);
}
project->values("RES_FILE").first() = Option::fixPathToTargetOS(
project->first("RES_FILE").toQString(), false);
project->values("POST_TARGETDEPS") += project->values("RES_FILE");
project->values("CLEAN_FILES") += project->values("RES_FILE");
}
}
void Win32MakefileGenerator::writeCleanParts(QTextStream &t)
{
t << "clean: compiler_clean " << depVar("CLEAN_DEPS");
{
const char *clean_targets[] = { "OBJECTS", "QMAKE_CLEAN", "CLEAN_FILES", nullptr };
for(int i = 0; clean_targets[i]; ++i) {
const ProStringList &list = project->values(clean_targets[i]);
const QString del_statement("-$(DEL_FILE)");
if(project->isActiveConfig("no_delete_multiple_files")) {
for (ProStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
t << "\n\t" << del_statement
<< ' ' << escapeFilePath(Option::fixPathToTargetOS((*it).toQString()));
} else {
QString files, file;
const int commandlineLimit = 2047; // NT limit, expanded
for (ProStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
file = ' ' + escapeFilePath(Option::fixPathToTargetOS((*it).toQString()));
if(del_statement.size() + files.size() +
qMax(fixEnvVariables(file).size(), file.size()) > commandlineLimit) {
t << "\n\t" << del_statement << files;
files.clear();
}
files += file;
}
if(!files.isEmpty())
t << "\n\t" << del_statement << files;
}
}
}
t << Qt::endl << Qt::endl;
t << "distclean: clean " << depVar("DISTCLEAN_DEPS");
{
const char *clean_targets[] = { "QMAKE_DISTCLEAN", nullptr };
for(int i = 0; clean_targets[i]; ++i) {
const ProStringList &list = project->values(clean_targets[i]);
const QString del_statement("-$(DEL_FILE)");
if(project->isActiveConfig("no_delete_multiple_files")) {
for (ProStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
t << "\n\t" << del_statement << " "
<< escapeFilePath(Option::fixPathToTargetOS((*it).toQString()));
} else {
QString files, file;
const int commandlineLimit = 2047; // NT limit, expanded
for (ProStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
file = " " + escapeFilePath(Option::fixPathToTargetOS((*it).toQString()));
if(del_statement.size() + files.size() +
qMax(fixEnvVariables(file).size(), file.size()) > commandlineLimit) {
t << "\n\t" << del_statement << files;
files.clear();
}
files += file;
}
if(!files.isEmpty())
t << "\n\t" << del_statement << files;
}
}
}
t << "\n\t-$(DEL_FILE) $(DESTDIR_TARGET)\n";
{
QString ofile = fileFixify(Option::output.fileName());
if(!ofile.isEmpty())
t << "\t-$(DEL_FILE) " << escapeFilePath(ofile) << Qt::endl;
}
t << Qt::endl;
}
void Win32MakefileGenerator::writeIncPart(QTextStream &t)
{
t << "INCPATH = ";
const ProStringList &incs = project->values("INCLUDEPATH");
for(int i = 0; i < incs.size(); ++i) {
QString inc = incs.at(i).toQString();
inc.replace(QRegularExpression("\\\\$"), "");
if(!inc.isEmpty())
t << "-I" << escapeFilePath(inc) << ' ';
}
t << Qt::endl;
}
void Win32MakefileGenerator::writeStandardParts(QTextStream &t)
{
writeExportedVariables(t);
t << "####### Compiler, tools and options\n\n";
t << "CC = " << var("QMAKE_CC") << Qt::endl;
t << "CXX = " << var("QMAKE_CXX") << Qt::endl;
t << "DEFINES = "
<< varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ")
<< varGlue("DEFINES","-D"," -D","") << Qt::endl;
t << "CFLAGS = " << var("QMAKE_CFLAGS") << " $(DEFINES)\n";
t << "CXXFLAGS = " << var("QMAKE_CXXFLAGS") << " $(DEFINES)\n";
writeIncPart(t);
writeLibsPart(t);
writeDefaultVariables(t);
t << Qt::endl;
t << "####### Output directory\n\n";
if(!project->values("OBJECTS_DIR").isEmpty())
t << "OBJECTS_DIR = " << escapeFilePath(var("OBJECTS_DIR").remove(QRegularExpression("\\\\$"))) << Qt::endl;
else
t << "OBJECTS_DIR = . \n";
t << Qt::endl;
t << "####### Files\n\n";
t << "SOURCES = " << valList(escapeFilePaths(project->values("SOURCES")))
<< " " << valList(escapeFilePaths(project->values("GENERATED_SOURCES"))) << Qt::endl;
// do this here so we can set DEST_TARGET to be the complete path to the final target if it is needed.
QString orgDestDir = var("DESTDIR");
QString destDir = Option::fixPathToTargetOS(orgDestDir, false);
if (!destDir.isEmpty() && (orgDestDir.endsWith('/') || orgDestDir.endsWith(Option::dir_sep)))
destDir += Option::dir_sep;
QString target = QString(project->first("TARGET")+project->first("TARGET_EXT"));
project->values("DEST_TARGET").prepend(destDir + target);
writeObjectsPart(t);
writeExtraCompilerVariables(t);
writeExtraVariables(t);
t << "DIST = " << fileVarList("DISTFILES") << ' '
<< fileVarList("HEADERS") << ' ' << fileVarList("SOURCES") << Qt::endl;
t << "QMAKE_TARGET = " << fileVar("QMAKE_ORIG_TARGET") << Qt::endl; // unused
// The comment is important to maintain variable compatibility with Unix
// Makefiles, while not interpreting a trailing-slash as a linebreak
t << "DESTDIR = " << escapeFilePath(destDir) << " #avoid trailing-slash linebreak\n";
t << "TARGET = " << escapeFilePath(target) << Qt::endl;
t << "DESTDIR_TARGET = " << fileVar("DEST_TARGET") << Qt::endl;
t << Qt::endl;
writeImplicitRulesPart(t);
t << "####### Build rules\n\n";
writeBuildRulesPart(t);
if (project->first("TEMPLATE") != "aux") {
if (project->isActiveConfig("shared") && !project->values("DLLDESTDIR").isEmpty()) {
const ProStringList &dlldirs = project->values("DLLDESTDIR");
for (ProStringList::ConstIterator dlldir = dlldirs.begin(); dlldir != dlldirs.end(); ++dlldir) {
t << "\t-$(COPY_FILE) $(DESTDIR_TARGET) "
<< escapeFilePath(Option::fixPathToTargetOS((*dlldir).toQString(), false)) << Qt::endl;
}
}
t << Qt::endl;
writeRcFilePart(t);
}
writeMakeQmake(t);
QStringList dist_files = fileFixify(Option::mkfile::project_files);
if(!project->isEmpty("QMAKE_INTERNAL_INCLUDED_FILES"))
dist_files += project->values("QMAKE_INTERNAL_INCLUDED_FILES").toQStringList();
if(!project->isEmpty("TRANSLATIONS"))
dist_files << var("TRANSLATIONS");
if(!project->isEmpty("FORMS")) {
const ProStringList &forms = project->values("FORMS");
for (ProStringList::ConstIterator formit = forms.begin(); formit != forms.end(); ++formit) {
QString ui_h = fileFixify((*formit) + Option::h_ext.first());
if(exists(ui_h))
dist_files << ui_h;
}
}
t << "dist:\n\t"
<< "$(ZIP) " << var("QMAKE_ORIG_TARGET") << ".zip $(SOURCES) $(DIST) "
<< escapeFilePaths(dist_files).join(' ') << ' ' << fileVar("TRANSLATIONS") << ' ';
if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) {
const ProStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
const ProStringList &inputs = project->values(ProKey(*it + ".input"));
for (ProStringList::ConstIterator input = inputs.begin(); input != inputs.end(); ++input) {
const ProStringList &val = project->values((*input).toKey());
t << escapeFilePaths(val).join(' ') << ' ';
}
}
}
t << Qt::endl << Qt::endl;
writeCleanParts(t);
writeExtraTargets(t);
writeExtraCompilerTargets(t);
t << Qt::endl << Qt::endl;
}
void Win32MakefileGenerator::writeLibsPart(QTextStream &t)
{
if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
t << "LIBAPP = " << var("QMAKE_LIB") << Qt::endl;
t << "LIBFLAGS = " << var("QMAKE_LIBFLAGS") << Qt::endl;
} else {
t << "LINKER = " << var("QMAKE_LINK") << Qt::endl;
t << "LFLAGS = " << var("QMAKE_LFLAGS") << Qt::endl;
t << "LIBS = " << fixLibFlags("LIBS").join(' ') << ' '
<< fixLibFlags("LIBS_PRIVATE").join(' ') << ' '
<< fixLibFlags("QMAKE_LIBS").join(' ') << ' '
<< fixLibFlags("QMAKE_LIBS_PRIVATE").join(' ') << Qt::endl;
}
}
void Win32MakefileGenerator::writeObjectsPart(QTextStream &t)
{
// Used in both deps and commands.
t << "OBJECTS = " << valList(escapeDependencyPaths(project->values("OBJECTS"))) << Qt::endl;
}
void Win32MakefileGenerator::writeImplicitRulesPart(QTextStream &)
{
}
void Win32MakefileGenerator::writeBuildRulesPart(QTextStream &)
{
}
void Win32MakefileGenerator::writeRcFilePart(QTextStream &t)
{
if(!project->values("RC_FILE").isEmpty()) {
const ProString res_file = project->first("RES_FILE");
const QString rc_file = fileFixify(project->first("RC_FILE").toQString());
const ProStringList rcIncPaths = project->values("RC_INCLUDEPATH");
QString incPathStr;
for (int i = 0; i < rcIncPaths.size(); ++i) {
const ProString &path = rcIncPaths.at(i);
if (path.isEmpty())
continue;
incPathStr += QStringLiteral(" /i ");
incPathStr += escapeFilePath(path);
}
addSourceFile(rc_file, QMakeSourceFileInfo::SEEK_DEPS);
const QStringList rcDeps = QStringList(rc_file) << dependencies(rc_file);
// The resource tool may use defines. This might be the same defines passed in as the
// compiler, since you may use these defines in the .rc file itself.
// As the escape syntax for the command line defines for RC is different from that for CL,
// we might have to set specific defines for RC.
ProString defines = varGlue("RC_DEFINES", " -D", " -D", "");
if (defines.isEmpty())
defines = ProString(" $(DEFINES)");
// Also, we need to add the _DEBUG define manually since the compiler defines this symbol
// by itself, and we use it in the automatically created rc file when VERSION is defined
// in the .pro file.
t << escapeDependencyPath(res_file) << ": "
<< escapeDependencyPaths(rcDeps).join(' ') << "\n\t"
<< var("QMAKE_RC") << (project->isActiveConfig("debug") ? " -D_DEBUG" : "")
<< defines << incPathStr << " -fo " << escapeFilePath(res_file)
<< ' ' << escapeFilePath(rc_file);
t << Qt::endl << Qt::endl;
}
}
QString Win32MakefileGenerator::defaultInstall(const QString &t)
{
if((t != "target" && t != "dlltarget") ||
(t == "dlltarget" && (project->first("TEMPLATE") != "lib" || !project->isActiveConfig("shared"))) ||
project->first("TEMPLATE") == "subdirs" || project->first("TEMPLATE") == "aux")
return QString();
const QString root = installRoot();
ProStringList &uninst = project->values(ProKey(t + ".uninstall"));
QString ret;
QString targetdir = fileFixify(project->first(ProKey(t + ".path")).toQString(), FileFixifyAbsolute);
if(targetdir.right(1) != Option::dir_sep)
targetdir += Option::dir_sep;
const ProStringList &targets = project->values(ProKey(t + ".targets"));
for (int i = 0; i < targets.size(); ++i) {
QString src = targets.at(i).toQString(),
dst = escapeFilePath(filePrefixRoot(root, targetdir + src.section('/', -1)));
if (!ret.isEmpty())
ret += "\n\t";
ret += "$(QINSTALL) " + escapeFilePath(Option::fixPathToTargetOS(src, false)) + ' ' + dst;
if (!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + dst);
}
if(t == "target" && project->first("TEMPLATE") == "lib") {
if(project->isActiveConfig("create_prl") && !project->isActiveConfig("no_install_prl") &&
!project->isEmpty("QMAKE_INTERNAL_PRL_FILE")) {
QString dst_prl = Option::fixPathToTargetOS(project->first("QMAKE_INTERNAL_PRL_FILE").toQString());
int slsh = dst_prl.lastIndexOf(Option::dir_sep);
if(slsh != -1)
dst_prl = dst_prl.right(dst_prl.size() - slsh - 1);
dst_prl = filePrefixRoot(root, targetdir + dst_prl);
if (!ret.isEmpty())
ret += "\n\t";
ret += installMetaFile(ProKey("QMAKE_PRL_INSTALL_REPLACE"), project->first("QMAKE_INTERNAL_PRL_FILE").toQString(), dst_prl);
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + escapeFilePath(dst_prl));
}
if(project->isActiveConfig("create_pc")) {
QString dst_pc = pkgConfigFileName(false);
if (!dst_pc.isEmpty()) {
dst_pc = filePrefixRoot(root, targetdir + dst_pc);
const QString dst_pc_dir = Option::fixPathToTargetOS(fileInfo(dst_pc).path(), false);
if (!dst_pc_dir.isEmpty()) {
if (!ret.isEmpty())
ret += "\n\t";
ret += mkdir_p_asstring(dst_pc_dir, true);
}
if(!ret.isEmpty())
ret += "\n\t";
ret += installMetaFile(ProKey("QMAKE_PKGCONFIG_INSTALL_REPLACE"), pkgConfigFileName(true), dst_pc);
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + escapeFilePath(dst_pc));
}
}
if(project->isActiveConfig("shared") && !project->isActiveConfig("plugin")) {
ProString lib_target = project->first("LIB_TARGET");
QString src_targ = escapeFilePath(
(project->isEmpty("DESTDIR") ? QString("$(DESTDIR)") : project->first("DESTDIR"))
+ lib_target);
QString dst_targ = escapeFilePath(
filePrefixRoot(root, fileFixify(targetdir + lib_target, FileFixifyAbsolute)));
if(!ret.isEmpty())
ret += "\n\t";
ret += QString("-$(INSTALL_FILE) ") + src_targ + ' ' + dst_targ;
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + dst_targ);
}
}
if (t == "dlltarget" || project->values(ProKey(t + ".CONFIG")).indexOf("no_dll") == -1) {
QString src_targ = "$(DESTDIR_TARGET)";
QString dst_targ = escapeFilePath(
filePrefixRoot(root, fileFixify(targetdir + "$(TARGET)", FileFixifyAbsolute)));
if(!ret.isEmpty())
ret += "\n\t";
ret += QString("-$(INSTALL_FILE) ") + src_targ + ' ' + dst_targ;
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + dst_targ);
}
return ret;
}
void Win32MakefileGenerator::writeDefaultVariables(QTextStream &t)
{
MakefileGenerator::writeDefaultVariables(t);
t << "IDC = " << (project->isEmpty("QMAKE_IDC") ? QString("idc") : var("QMAKE_IDC"))
<< Qt::endl;
t << "IDL = " << (project->isEmpty("QMAKE_IDL") ? QString("midl") : var("QMAKE_IDL"))
<< Qt::endl;
t << "ZIP = " << var("QMAKE_ZIP") << Qt::endl;
t << "DEF_FILE = " << fileVar("DEF_FILE") << Qt::endl;
t << "RES_FILE = " << fileVar("RES_FILE") << Qt::endl; // Not on mingw, can't see why not though...
t << "SED = " << var("QMAKE_STREAM_EDITOR") << Qt::endl;
t << "MOVE = " << var("QMAKE_MOVE") << Qt::endl;
}
QString Win32MakefileGenerator::escapeFilePath(const QString &path) const
{
QString ret = path;
if(!ret.isEmpty()) {
if (ret.contains(' ') || ret.contains('\t'))
ret = "\"" + ret + "\"";
debug_msg(2, "EscapeFilePath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData());
}
return ret;
}
QString Win32MakefileGenerator::escapeDependencyPath(const QString &path) const
{
QString ret = path;
if (!ret.isEmpty()) {
static const QRegularExpression criticalChars(QStringLiteral("([\t #])"));
if (ret.contains(criticalChars))
ret = "\"" + ret + "\"";
debug_msg(2, "EscapeDependencyPath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData());
}
return ret;
}
QString Win32MakefileGenerator::cQuoted(const QString &str)
{
QString ret = str;
ret.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
ret.replace(QLatin1Char('"'), QLatin1String("\\\""));
ret.prepend(QLatin1Char('"'));
ret.append(QLatin1Char('"'));
return ret;
}
ProKey Win32MakefileGenerator::fullTargetVariable() const
{
return "DEST_TARGET";
}
QT_END_NAMESPACE

View File

@ -0,0 +1,48 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef WINMAKEFILE_H
#define WINMAKEFILE_H
#include "makefile.h"
QT_BEGIN_NAMESPACE
class Win32MakefileGenerator : public MakefileGenerator
{
protected:
QString defaultInstall(const QString &) override;
void writeDefaultVariables(QTextStream &t) override;
virtual void writeCleanParts(QTextStream &t);
virtual void writeStandardParts(QTextStream &t);
virtual void writeIncPart(QTextStream &t);
virtual void writeLibsPart(QTextStream &t);
virtual void writeObjectsPart(QTextStream &t);
virtual void writeImplicitRulesPart(QTextStream &t);
virtual void writeBuildRulesPart(QTextStream &);
using MakefileGenerator::escapeFilePath;
QString escapeFilePath(const QString &path) const override;
using MakefileGenerator::escapeDependencyPath;
QString escapeDependencyPath(const QString &path) const override;
virtual void writeRcFilePart(QTextStream &t);
bool findLibraries(bool linkPrl, bool mergeLflags) override;
LibFlagType parseLibFlag(const ProString &flag, ProString *arg) override;
ProString fixLibFlag(const ProString &lib) override;
bool processPrlFileBase(QString &origFile, QStringView origName,
QStringView fixedBase, int slashOff) override;
void processVars();
void fixTargetExt();
void processRcFileVar();
static QString cQuoted(const QString &str);
public:
ProKey fullTargetVariable() const override;
};
QT_END_NAMESPACE
#endif // WINMAKEFILE_H

View File

@ -0,0 +1,345 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "xmloutput.h"
QT_BEGIN_NAMESPACE
XmlOutput::XmlOutput(QTextStream &file, ConverstionType type)
: xmlFile(file), indent("\t"), currentLevel(0), currentState(Bare), format(NewLine),
conversion(type)
{
tagStack.clear();
}
XmlOutput::~XmlOutput()
{
closeAll();
}
// Settings ------------------------------------------------------------------
void XmlOutput::setIndentString(const QString &indentString)
{
indent = indentString;
}
QString XmlOutput::indentString()
{
return indent;
}
void XmlOutput::setIndentLevel(int level)
{
currentLevel = level;
}
int XmlOutput::indentLevel()
{
return currentLevel;
}
void XmlOutput::setState(XMLState state)
{
currentState = state;
}
void XmlOutput::setFormat(XMLFormat newFormat)
{
format = newFormat;
}
XmlOutput::XMLState XmlOutput::state()
{
return currentState;
}
void XmlOutput::updateIndent()
{
currentIndent.clear();
currentIndent.reserve(currentLevel);
for (int i = 0; i < currentLevel; ++i)
currentIndent.append(indent);
}
void XmlOutput::increaseIndent()
{
++currentLevel;
updateIndent();
}
void XmlOutput::decreaseIndent()
{
if (currentLevel)
--currentLevel;
updateIndent();
if (!currentLevel)
currentState = Bare;
}
QString XmlOutput::doConversion(const QString &text)
{
if (!text.size())
return QString();
else if (conversion == NoConversion)
return text;
QString output;
if (conversion == XMLConversion) {
// this is a way to escape characters that shouldn't be converted
for (int i=0; i<text.size(); ++i) {
const QChar c = text.at(i);
if (c == QLatin1Char('&')) {
if ( (i + 7) < text.size() &&
text.at(i + 1) == QLatin1Char('#') &&
text.at(i + 2) == QLatin1Char('x') &&
text.at(i + 7) == QLatin1Char(';') ) {
output += text.at(i);
} else {
output += QLatin1String("&amp;");
}
} else if (c == QLatin1Char('<')) {
output += QLatin1String("&lt;");
} else if (c == QLatin1Char('>')) {
output += QLatin1String("&gt;");
} else {
if (c.unicode() < 0x20) {
output += QString("&#x%1;").arg(c.unicode(), 2, 16, QLatin1Char('0'));
} else {
output += c;
}
}
}
} else {
output = text;
}
if (conversion == XMLConversion) {
output.replace('\"', QLatin1String("&quot;"));
output.replace('\'', QLatin1String("&apos;"));
} else if (conversion == EscapeConversion) {
output.replace('\"', QLatin1String("\\\""));
output.replace('\'', QLatin1String("\\\'"));
}
return output;
}
// Stream functions ----------------------------------------------------------
XmlOutput& XmlOutput::operator<<(const QString& o)
{
return operator<<(data(o));
}
XmlOutput& XmlOutput::operator<<(const xml_output& o)
{
switch(o.xo_type) {
case tNothing:
break;
case tRaw:
addRaw(o.xo_text);
break;
case tDeclaration:
addDeclaration(o.xo_text, o.xo_value);
break;
case tTag:
newTagOpen(o.xo_text);
break;
case tTagValue:
addRaw(QString("\n%1<%2>").arg(currentIndent).arg(o.xo_text));
addRaw(doConversion(o.xo_value));
addRaw(QString("</%1>").arg(o.xo_text));
break;
case tValueTag:
addRaw(doConversion(o.xo_text));
setFormat(NoNewLine);
closeTag();
setFormat(NewLine);
break;
case tImport:
addRaw(QString("\n%1<Import %2=\"%3\" />").arg(currentIndent).arg(o.xo_text).arg(o.xo_value));
break;
case tCloseTag:
if (o.xo_value.size())
closeAll();
else if (o.xo_text.size())
closeTo(o.xo_text);
else
closeTag();
break;
case tAttribute:
addAttribute(o.xo_text, o.xo_value);
break;
case tAttributeTag:
addAttributeTag(o.xo_text, o.xo_value);
break;
case tData:
{
// Special case to be able to close tag in normal
// way ("</tag>", not "/>") without using addRaw()..
if (!o.xo_text.size()) {
closeOpen();
break;
}
QString output = doConversion(o.xo_text);
output.replace('\n', "\n" + currentIndent);
addRaw(QString("\n%1%2").arg(currentIndent).arg(output));
}
break;
case tComment:
{
QString output("<!--%1-->");
addRaw(output.arg(o.xo_text));
}
break;
case tCDATA:
{
QString output("<![CDATA[\n%1\n]]>");
addRaw(output.arg(o.xo_text));
}
break;
}
return *this;
}
// Output functions ----------------------------------------------------------
void XmlOutput::newTag(const QString &tag)
{
Q_ASSERT_X(tag.size(), "XmlOutput", "Cannot open an empty tag");
newTagOpen(tag);
closeOpen();
}
void XmlOutput::newTagOpen(const QString &tag)
{
Q_ASSERT_X(tag.size(), "XmlOutput", "Cannot open an empty tag");
closeOpen();
if (format == NewLine)
xmlFile << Qt::endl << currentIndent;
xmlFile << '<' << doConversion(tag);
currentState = Attribute;
tagStack.append(tag);
increaseIndent(); // ---> indent
}
void XmlOutput::closeOpen()
{
switch(currentState) {
case Bare:
case Tag:
return;
case Attribute:
break;
}
xmlFile << '>';
currentState = Tag;
}
void XmlOutput::closeTag()
{
switch(currentState) {
case Bare:
if (tagStack.size())
//warn_msg(WarnLogic, "<Root>: Cannot close tag in Bare state, %d tags on stack", tagStack.count());
qDebug("<Root>: Cannot close tag in Bare state, %d tags on stack", int(tagStack.size()));
else
//warn_msg(WarnLogic, "<Root>: Cannot close tag, no tags on stack");
qDebug("<Root>: Cannot close tag, no tags on stack");
return;
case Tag:
decreaseIndent(); // <--- Pre-decrease indent
if (format == NewLine)
xmlFile << Qt::endl << currentIndent;
xmlFile << "</" << doConversion(tagStack.last()) << '>';
tagStack.pop_back();
break;
case Attribute:
xmlFile << " />";
tagStack.pop_back();
currentState = Tag;
decreaseIndent(); // <--- Post-decrease indent
break;
}
}
void XmlOutput::closeTo(const QString &tag)
{
bool cont = true;
if (!tagStack.contains(tag) && !tag.isNull()) {
//warn_msg(WarnLogic, "<%s>: Cannot close to tag <%s>, not on stack", tagStack.last().latin1(), tag.latin1());
qDebug("<%s>: Cannot close to tag <%s>, not on stack", tagStack.last().toLatin1().constData(), tag.toLatin1().constData());
return;
}
int left = tagStack.size();
while (left-- && cont) {
cont = tagStack.last().compare(tag) != 0;
closeTag();
}
}
void XmlOutput::closeAll()
{
if (!tagStack.size())
return;
closeTo(QString());
}
void XmlOutput::addDeclaration(const QString &version, const QString &encoding)
{
switch(currentState) {
case Bare:
break;
case Tag:
case Attribute:
//warn_msg(WarnLogic, "<%s>: Cannot add declaration when not in bare state", tagStack.last().toLatin1().constData());
qDebug("<%s>: Cannot add declaration when not in bare state", tagStack.last().toLatin1().constData());
return;
}
QString outData = QString("<?xml version=\"%1\" encoding=\"%2\"?>")
.arg(doConversion(version))
.arg(doConversion(encoding));
addRaw(outData);
}
void XmlOutput::addRaw(const QString &rawText)
{
closeOpen();
xmlFile << rawText;
}
void XmlOutput::addAttribute(const QString &attribute, const QString &value)
{
switch(currentState) {
case Bare:
case Tag:
//warn_msg(WarnLogic, "<%s>: Cannot add attribute since tags not open", tagStack.last().toLatin1().constData());
qDebug("<%s>: Cannot add attribute (%s) since tag's not open",
(tagStack.size() ? tagStack.last().toLatin1().constData() : "Root"),
attribute.toLatin1().constData());
return;
case Attribute:
break;
}
if (format == NewLine)
xmlFile << Qt::endl;
xmlFile << currentIndent << doConversion(attribute) << "=\"" << doConversion(value) << "\"";
}
void XmlOutput::addAttributeTag(const QString &attribute, const QString &value)
{
switch(currentState) {
case Bare:
case Tag:
//warn_msg(WarnLogic, "<%s>: Cannot add attribute since tags not open", tagStack.last().toLatin1().constData());
qDebug("<%s>: Cannot add attribute (%s) since tag's not open",
(tagStack.size() ? tagStack.last().toLatin1().constData() : "Root"),
attribute.toLatin1().constData());
return;
case Attribute:
break;
}
xmlFile << " " << doConversion(attribute) << "=\"" << doConversion(value) << "\"";
}
QT_END_NAMESPACE

View File

@ -0,0 +1,206 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef XMLOUTPUT_H
#define XMLOUTPUT_H
#include <qtextstream.h>
#include <qstack.h>
QT_BEGIN_NAMESPACE
class XmlOutput
{
public:
enum ConverstionType {
NoConversion, // No change
EscapeConversion, // Use '\"'
XMLConversion // Use &quot;
};
enum XMLFormat {
NoNewLine, // No new lines, unless added manually
NewLine // All properties & tags indented on new lines
};
enum XMLState {
Bare, // Not in tag or attribute
Tag, // <tagname attribute1="value"
Attribute // attribute2="value">
};
enum XMLType {
tNothing, // No XML output, and not state change
tRaw, // Raw text (no formating)
tDeclaration, // <?xml version="x.x" encoding="xxx"?>
tTag, // <tagname attribute1="value"
tTagValue, // <tagname>value</tagname>
tValueTag, // value</tagname>
tCloseTag, // Closes an open tag
tAttribute, // attribute2="value">
tAttributeTag, // attribute on the same line as a tag
tData, // Tag data (formating done)
tImport, // <import "type"="path" />
tComment, // <!-- Comment -->
tCDATA // <![CDATA[ ... ]]>
};
XmlOutput(QTextStream &file, ConverstionType type = XMLConversion);
~XmlOutput();
// Settings
void setIndentString(const QString &indentString);
QString indentString();
void setIndentLevel(int level);
int indentLevel();
void setState(XMLState state);
void setFormat(XMLFormat newFormat);
XMLState state();
struct xml_output {
XMLType xo_type; // Type of struct instance
QString xo_text; // Tag/Attribute name/xml version
QString xo_value; // Value of attributes/xml encoding
xml_output(XMLType type, const QString &text, const QString &value)
: xo_type(type), xo_text(text), xo_value(value) {}
xml_output(const xml_output &xo)
: xo_type(xo.xo_type), xo_text(xo.xo_text), xo_value(xo.xo_value) {}
};
// Streams
XmlOutput& operator<<(const QString& o);
XmlOutput& operator<<(const xml_output& o);
private:
void increaseIndent();
void decreaseIndent();
void updateIndent();
QString doConversion(const QString &text);
// Output functions
void newTag(const QString &tag);
void newTagOpen(const QString &tag);
void closeOpen();
void closeTag();
void closeTo(const QString &tag);
void closeAll();
void addDeclaration(const QString &version, const QString &encoding);
void addRaw(const QString &rawText);
void addAttribute(const QString &attribute, const QString &value);
void addAttributeTag(const QString &attribute, const QString &value);
void addData(const QString &data);
// Data
QTextStream &xmlFile;
QString indent;
QString currentIndent;
int currentLevel;
XMLState currentState;
XMLFormat format;
ConverstionType conversion;
QStack<QString> tagStack;
};
inline XmlOutput::xml_output noxml()
{
return XmlOutput::xml_output(XmlOutput::tNothing, QString(), QString());
}
inline XmlOutput::xml_output raw(const QString &rawText)
{
return XmlOutput::xml_output(XmlOutput::tRaw, rawText, QString());
}
inline XmlOutput::xml_output declaration(const QString &version = QString("1.0"),
const QString &encoding = QString())
{
return XmlOutput::xml_output(XmlOutput::tDeclaration, version, encoding);
}
inline XmlOutput::xml_output decl(const QString &version = QString("1.0"),
const QString &encoding = QString())
{
return declaration(version, encoding);
}
inline XmlOutput::xml_output tag(const QString &name)
{
return XmlOutput::xml_output(XmlOutput::tTag, name, QString());
}
inline XmlOutput::xml_output valueTag(const QString &value)
{
return XmlOutput::xml_output(XmlOutput::tValueTag, value, QString());
}
inline XmlOutput::xml_output tagValue(const QString &tagName, const QString &value)
{
return XmlOutput::xml_output(XmlOutput::tTagValue, tagName, value);
}
inline XmlOutput::xml_output import(const QString &tagName, const QString &value)
{
return XmlOutput::xml_output(XmlOutput::tImport, tagName, value);
}
inline XmlOutput::xml_output closetag()
{
return XmlOutput::xml_output(XmlOutput::tCloseTag, QString(), QString());
}
inline XmlOutput::xml_output closetag(const QString &toTag)
{
return XmlOutput::xml_output(XmlOutput::tCloseTag, toTag, QString());
}
inline XmlOutput::xml_output closeall()
{
return XmlOutput::xml_output(XmlOutput::tCloseTag, QString(), QString("all"));
}
inline XmlOutput::xml_output attribute(const QString &name,
const QString &value)
{
return XmlOutput::xml_output(XmlOutput::tAttribute, name, value);
}
inline XmlOutput::xml_output attributeTag(const QString &name,
const QString &value)
{
return XmlOutput::xml_output(XmlOutput::tAttributeTag, name, value);
}
inline XmlOutput::xml_output attr(const QString &name,
const QString &value)
{
return attribute(name, value);
}
inline XmlOutput::xml_output attrTag(const QString &name,
const QString &value)
{
return attributeTag(name, value);
}
inline XmlOutput::xml_output data(const QString &text = QString())
{
return XmlOutput::xml_output(XmlOutput::tData, text, QString());
}
inline XmlOutput::xml_output comment(const QString &text)
{
return XmlOutput::xml_output(XmlOutput::tComment, text, QString());
}
inline XmlOutput::xml_output cdata(const QString &text)
{
return XmlOutput::xml_output(XmlOutput::tCDATA, text, QString());
}
QT_END_NAMESPACE
#endif // XMLOUTPUT_H