qt5base-lts/qmake/generators/win32/mingw_make.cpp
M. Moellney d83bd9c6f5 Introduce qmake RC_DEFINES for RC preprocessor defines
qmake win32 generators by default used values of DEFINES for RC
preprocessor defines, too. For simple defines this works. For
string literals this failed for the .vcxproj files:

DEFINES += "FOO=\"BAR BAR\"" works for CL but fails for RC.

DEFINES += "FOO=\\\"BAR BAR\\\"" works for RC but fails for CL.

This patch introduces the qmake variable RC_DEFINES. The variable
contains the preprocessor defines, that are used for RC. If the
varible is not set, the DEFINES values are used for RC.

Task-number: QTBUG-44184
Change-Id: I4202271759d29de8c1829347ae3ef117eda54b38
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
2015-06-02 03:37:07 +00:00

444 lines
18 KiB
C++

/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the qmake application of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "mingw_make.h"
#include "option.h"
#include "meta.h"
#include <proitems.h>
#include <qregexp.h>
#include <qdir.h>
#include <stdlib.h>
#include <time.h>
QT_BEGIN_NAMESPACE
MingwMakefileGenerator::MingwMakefileGenerator() : Win32MakefileGenerator()
{
}
QString MingwMakefileGenerator::escapeDependencyPath(const QString &path) const
{
QString ret = path;
ret.replace('\\', "/"); // ### this shouldn't be here
ret.replace(' ', "\\ ");
return ret;
}
QString MingwMakefileGenerator::getLibTarget()
{
return QString("lib" + project->first("TARGET") + project->first("TARGET_VERSION_EXT") + ".a");
}
QString MingwMakefileGenerator::getManifestFileForRcFile() const
{
return project->first("QMAKE_MANIFEST").toQString();
}
ProString MingwMakefileGenerator::fixLibFlag(const ProString &lib)
{
if (lib.startsWith("lib"))
return QStringLiteral("-l") + escapeFilePath(lib.mid(3));
return escapeFilePath(lib);
}
bool MingwMakefileGenerator::findLibraries()
{
QList<QMakeLocalFileName> dirs;
static const char * const lflags[] = { "QMAKE_LIBS", "QMAKE_LIBS_PRIVATE", 0 };
for (int i = 0; lflags[i]; i++) {
ProStringList &l = project->values(lflags[i]);
ProStringList::Iterator it = l.begin();
while (it != l.end()) {
if ((*it).startsWith("-l")) {
QString steam = (*it).mid(2).toQString();
ProString out;
QString suffix = project->first(ProKey("QMAKE_" + steam.toUpper() + "_SUFFIX")).toQString();
for (QList<QMakeLocalFileName>::Iterator dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) {
QString extension;
int ver = findHighestVersion((*dir_it).local(), steam, "dll.a|a");
if (ver > 0)
extension += QString::number(ver);
extension += suffix;
if (QMakeMetaInfo::libExists((*dir_it).local() + '/' + steam)
|| exists((*dir_it).local() + '/' + steam + extension + ".a")
|| exists((*dir_it).local() + '/' + steam + extension + ".dll.a")) {
out = *it + extension;
break;
}
}
if (!out.isEmpty()) // We assume if it never finds it that its correct
(*it) = out;
} else if ((*it).startsWith("-L")) {
QMakeLocalFileName f((*it).mid(2).toQString());
dirs.append(f);
*it = "-L" + f.real();
}
++it;
}
}
return true;
}
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();
if(Option::mkfile::do_stub_makefile) {
t << "QMAKE = " << var("QMAKE_QMAKE") << endl;
const ProStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
for (ProStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it)
t << escapeDependencyPath(*it) << ' ';
t << "first all clean install distclean uninstall: qmake\n"
<< "qmake_all:\n";
writeMakeQmake(t);
t << "FORCE:\n\n";
return true;
}
writeMingwParts(t);
return MakefileGenerator::writeMakefile(t);
}
else if(project->first("TEMPLATE") == "subdirs") {
writeSubDirs(t);
return true;
}
return false;
}
void createLdObjectScriptFile(const QString &fileName, const ProStringList &objList)
{
QString filePath = Option::output_dir + QDir::separator() + fileName;
QFile file(filePath);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream t(&file);
t << "INPUT(\n";
for (ProStringList::ConstIterator it = objList.constBegin(); it != objList.constEnd(); ++it) {
QString path = (*it).toQString();
// ### quoting?
if (QDir::isRelativePath(path))
t << "./" << path << endl;
else
t << path << endl;
}
t << ");\n";
t.flush();
file.close();
}
}
void createArObjectScriptFile(const QString &fileName, const QString &target, const ProStringList &objList)
{
QString filePath = Option::output_dir + QDir::separator() + fileName;
QFile file(filePath);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream t(&file);
// ### quoting?
t << "CREATE " << target << endl;
for (ProStringList::ConstIterator it = objList.constBegin(); it != objList.constEnd(); ++it) {
t << "ADDMOD " << *it << endl;
}
t << "SAVE\n";
t.flush();
file.close();
}
}
void createRvctObjectScriptFile(const QString &fileName, const ProStringList &objList)
{
QString filePath = Option::output_dir + QDir::separator() + fileName;
QFile file(filePath);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream t(&file);
for (ProStringList::ConstIterator it = objList.constBegin(); it != objList.constEnd(); ++it) {
QString path = (*it).toQString();
// ### quoting?
if (QDir::isRelativePath(path))
t << "./" << path << endl;
else
t << path << endl;
}
t.flush();
file.close();
}
}
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) << " "
<< escapeDependencyPaths(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) << endl << endl;
QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++";
t << escapeDependencyPath(cppHeader) << ": " << escapeDependencyPath(header) << " "
<< escapeDependencyPaths(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) << endl << 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;
}
project->values("TARGET_PRL").append(project->first("TARGET"));
project->values("QMAKE_L_FLAG") << "-L";
processVars();
project->values("QMAKE_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 + "lib" + project->first("TARGET")
+ project->first("TARGET_VERSION_EXT") + ".a");
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_CLEAN").append(project->first("MINGW_IMPORT_LIB"));
}
}
void MingwMakefileGenerator::writeIncPart(QTextStream &t)
{
t << "INCPATH = ";
QString isystem = var("QMAKE_CFLAGS_ISYSTEM");
const ProStringList &incs = project->values("INCLUDEPATH");
for (ProStringList::ConstIterator incit = incs.begin(); incit != incs.end(); ++incit) {
QString inc = (*incit).toQString();
inc.replace(QRegExp("\\\\$"), "");
if (!isystem.isEmpty() && isSystemInclude(inc))
t << isystem << ' ';
else
t << "-I";
t << escapeFilePath(inc) << ' ';
}
t << endl;
}
void MingwMakefileGenerator::writeLibsPart(QTextStream &t)
{
if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
t << "LIB = " << var("QMAKE_LIB") << endl;
} else {
t << "LINKER = " << var("QMAKE_LINK") << endl;
t << "LFLAGS = " << var("QMAKE_LFLAGS") << endl;
t << "LIBS = "
<< fixLibFlags("QMAKE_LIBS").join(' ') << ' '
<< fixLibFlags("QMAKE_LIBS_PRIVATE").join(' ') << endl;
}
}
void MingwMakefileGenerator::writeObjectsPart(QTextStream &t)
{
if (project->values("OBJECTS").count() < var("QMAKE_LINK_OBJECT_MAX").toInt()) {
objectsLinkLine = "$(OBJECTS)";
} else if (project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
QString ar_script_file = var("QMAKE_LINK_OBJECT_SCRIPT") + "." + var("TARGET");
if (!var("BUILD_NAME").isEmpty()) {
ar_script_file += "." + var("BUILD_NAME");
}
// QMAKE_LIB is used for win32, including mingw, whereas QMAKE_AR is used on Unix.
if (project->isActiveConfig("rvct_linker")) {
createRvctObjectScriptFile(ar_script_file, project->values("OBJECTS"));
QString ar_cmd = project->values("QMAKE_LIB").join(' ');
if (ar_cmd.isEmpty())
ar_cmd = "armar --create";
objectsLinkLine = ar_cmd + ' ' + fileVar("DEST_TARGET") + " --via " + escapeFilePath(ar_script_file);
} else {
// Strip off any options since the ar commands will be read from file.
QString ar_cmd = var("QMAKE_LIB").section(" ", 0, 0);;
if (ar_cmd.isEmpty())
ar_cmd = "ar";
createArObjectScriptFile(ar_script_file, var("DEST_TARGET"), project->values("OBJECTS"));
objectsLinkLine = ar_cmd + " -M < " + escapeFilePath(ar_script_file);
}
} else {
QString ld_script_file = var("QMAKE_LINK_OBJECT_SCRIPT") + "." + var("TARGET");
if (!var("BUILD_NAME").isEmpty()) {
ld_script_file += "." + var("BUILD_NAME");
}
if (project->isActiveConfig("rvct_linker")) {
createRvctObjectScriptFile(ld_script_file, project->values("OBJECTS"));
objectsLinkLine = QString::fromLatin1("--via ") + escapeFilePath(ld_script_file);
} else {
createLdObjectScriptFile(ld_script_file, project->values("OBJECTS"));
objectsLinkLine = escapeFilePath(ld_script_file);
}
}
Win32MakefileGenerator::writeObjectsPart(t);
}
void MingwMakefileGenerator::writeBuildRulesPart(QTextStream &t)
{
t << "first: all\n";
t << "all: " << escapeDependencyPath(fileFixify(Option::output.fileName()))
<< ' ' << depVar("ALL_DEPS") << " $(DESTDIR_TARGET)\n\n";
t << "$(DESTDIR_TARGET): " << depVar("PRE_TARGETDEPS") << " $(OBJECTS) " << depVar("POST_TARGETDEPS");
if(!project->isEmpty("QMAKE_PRE_LINK"))
t << "\n\t" <<var("QMAKE_PRE_LINK");
if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
if (project->values("OBJECTS").count() < var("QMAKE_LINK_OBJECT_MAX").toInt()) {
t << "\n\t$(LIB) $(DESTDIR_TARGET) " << objectsLinkLine << " " ;
} else {
t << "\n\t" << objectsLinkLine << " " ;
}
} else if (project->first("TEMPLATE") != "aux") {
t << "\n\t$(LINKER) $(LFLAGS) " << var("QMAKE_LINK_O_FLAG") << "$(DESTDIR_TARGET) " << objectsLinkLine << " $(LIBS)";
}
if(!project->isEmpty("QMAKE_POST_LINK"))
t << "\n\t" <<var("QMAKE_POST_LINK");
t << 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.count(); ++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)");
t << escapeDependencyPath(var("RES_FILE")) << ": " << escapeDependencyPath(rc_file) << "\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