qt5base-lts/qmake/library/qmakeglobals.cpp
Mat Sutcliffe 23bce6b169 qmake: Fix missing newlines in generated vcxproj files
A bug in the Windows C Runtime causes text mode pipes to drop newlines
sometimes. This bug was hidden because of another bug in rcc which
caused newlines to be redundantly duplicated. When the latter bug was
fixed (commit 53d5811b) the former bug was exposed, causing invalid
vcxproj files to be generated. The Windows bug is described here:
https://connect.microsoft.com/VisualStudio/feedback/details/1902345
The workaround is to avoid text mode, and do the conversion of "\r\n"
to "\n" ourselves (which we were already doing anyway).

Change-Id: I792599a4cd7822f109fa921f02207fb1b144b1d1
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
2016-06-16 22:46:54 +00:00

372 lines
12 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 "qmakeglobals.h"
#include "qmakeevaluator.h"
#include "ioutils.h"
#include <qbytearray.h>
#include <qdatetime.h>
#include <qdebug.h>
#include <qdir.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qlist.h>
#include <qregexp.h>
#include <qset.h>
#include <qstack.h>
#include <qstring.h>
#include <qstringlist.h>
#include <qtextstream.h>
#ifdef PROEVALUATOR_THREAD_SAFE
# include <qthreadpool.h>
#endif
#ifdef Q_OS_UNIX
#include <unistd.h>
#include <sys/utsname.h>
#else
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#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
QT_BEGIN_NAMESPACE
#define fL1S(s) QString::fromLatin1(s)
QMakeGlobals::QMakeGlobals()
{
do_cache = true;
#ifdef PROEVALUATOR_DEBUG
debugLevel = 0;
#endif
#ifdef Q_OS_WIN
dirlist_sep = QLatin1Char(';');
dir_sep = QLatin1Char('\\');
#else
dirlist_sep = QLatin1Char(':');
dir_sep = QLatin1Char('/');
#endif
}
QMakeGlobals::~QMakeGlobals()
{
qDeleteAll(baseEnvs);
}
QString QMakeGlobals::cleanSpec(QMakeCmdLineParserState &state, const QString &spec)
{
QString ret = QDir::cleanPath(spec);
if (ret.contains(QLatin1Char('/'))) {
QString absRet = QDir(state.pwd).absoluteFilePath(ret);
if (QFile::exists(absRet))
ret = QDir::cleanPath(absRet);
}
return ret;
}
QMakeGlobals::ArgumentReturn QMakeGlobals::addCommandLineArguments(
QMakeCmdLineParserState &state, QStringList &args, int *pos)
{
enum { ArgNone, ArgConfig, ArgSpec, ArgXSpec, ArgTmpl, ArgTmplPfx, ArgCache } argState = ArgNone;
for (; *pos < args.count(); (*pos)++) {
QString arg = args.at(*pos);
switch (argState) {
case ArgConfig:
if (state.after)
state.postconfigs << arg;
else
state.preconfigs << arg;
break;
case ArgSpec:
qmakespec = args[*pos] = cleanSpec(state, arg);
break;
case ArgXSpec:
xqmakespec = args[*pos] = cleanSpec(state, arg);
break;
case ArgTmpl:
user_template = arg;
break;
case ArgTmplPfx:
user_template_prefix = arg;
break;
case ArgCache:
cachefile = args[*pos] = QDir::cleanPath(QDir(state.pwd).absoluteFilePath(arg));
break;
default:
if (arg.startsWith(QLatin1Char('-'))) {
if (arg == QLatin1String("-after"))
state.after = true;
else if (arg == QLatin1String("-config"))
argState = ArgConfig;
else if (arg == QLatin1String("-nocache"))
do_cache = false;
else if (arg == QLatin1String("-cache"))
argState = ArgCache;
else if (arg == QLatin1String("-platform") || arg == QLatin1String("-spec"))
argState = ArgSpec;
else if (arg == QLatin1String("-xplatform") || arg == QLatin1String("-xspec"))
argState = ArgXSpec;
else if (arg == QLatin1String("-template") || arg == QLatin1String("-t"))
argState = ArgTmpl;
else if (arg == QLatin1String("-template_prefix") || arg == QLatin1String("-tp"))
argState = ArgTmplPfx;
else if (arg == QLatin1String("-win32"))
dir_sep = QLatin1Char('\\');
else if (arg == QLatin1String("-unix"))
dir_sep = QLatin1Char('/');
else
return ArgumentUnknown;
} else if (arg.contains(QLatin1Char('='))) {
if (state.after)
state.postcmds << arg;
else
state.precmds << arg;
} else {
return ArgumentUnknown;
}
continue;
}
argState = ArgNone;
}
if (argState != ArgNone)
return ArgumentMalformed;
return ArgumentsOk;
}
void QMakeGlobals::commitCommandLineArguments(QMakeCmdLineParserState &state)
{
if (!state.preconfigs.isEmpty())
state.precmds << (fL1S("CONFIG += ") + state.preconfigs.join(QLatin1Char(' ')));
precmds = state.precmds.join(QLatin1Char('\n'));
if (!state.postconfigs.isEmpty())
state.postcmds << (fL1S("CONFIG += ") + state.postconfigs.join(QLatin1Char(' ')));
postcmds = state.postcmds.join(QLatin1Char('\n'));
if (xqmakespec.isEmpty())
xqmakespec = qmakespec;
}
void QMakeGlobals::useEnvironment()
{
if (xqmakespec.isEmpty())
xqmakespec = getEnv(QLatin1String("XQMAKESPEC"));
if (qmakespec.isEmpty()) {
qmakespec = getEnv(QLatin1String("QMAKESPEC"));
if (xqmakespec.isEmpty())
xqmakespec = qmakespec;
}
}
void QMakeGlobals::setCommandLineArguments(const QString &pwd, const QStringList &_args)
{
QStringList args = _args;
QMakeCmdLineParserState state(pwd);
for (int pos = 0; pos < args.size(); pos++)
addCommandLineArguments(state, args, &pos);
commitCommandLineArguments(state);
useEnvironment();
}
void QMakeGlobals::setDirectories(const QString &input_dir, const QString &output_dir)
{
if (input_dir != output_dir && !output_dir.isEmpty()) {
QString srcpath = input_dir;
if (!srcpath.endsWith(QLatin1Char('/')))
srcpath += QLatin1Char('/');
QString dstpath = output_dir;
if (!dstpath.endsWith(QLatin1Char('/')))
dstpath += QLatin1Char('/');
int srcLen = srcpath.length();
int dstLen = dstpath.length();
int lastSl = -1;
while (++lastSl, --srcLen, --dstLen,
srcLen && dstLen && srcpath.at(srcLen) == dstpath.at(dstLen))
if (srcpath.at(srcLen) == QLatin1Char('/'))
lastSl = 0;
source_root = srcpath.left(srcLen + lastSl);
build_root = dstpath.left(dstLen + lastSl);
}
}
QString QMakeGlobals::shadowedPath(const QString &fileName) const
{
if (source_root.isEmpty())
return fileName;
if (fileName.startsWith(source_root)
&& (fileName.length() == source_root.length()
|| fileName.at(source_root.length()) == QLatin1Char('/'))) {
return build_root + fileName.mid(source_root.length());
}
return QString();
}
QStringList QMakeGlobals::splitPathList(const QString &val) const
{
QStringList ret;
if (!val.isEmpty()) {
QDir bdir;
QStringList vals = val.split(dirlist_sep);
ret.reserve(vals.length());
foreach (const QString &it, vals)
ret << QDir::cleanPath(bdir.absoluteFilePath(it));
}
return ret;
}
QString QMakeGlobals::getEnv(const QString &var) const
{
#ifdef PROEVALUATOR_SETENV
return environment.value(var);
#else
return QString::fromLocal8Bit(qgetenv(var.toLocal8Bit().constData()));
#endif
}
QStringList QMakeGlobals::getPathListEnv(const QString &var) const
{
return splitPathList(getEnv(var));
}
QString QMakeGlobals::expandEnvVars(const QString &str) const
{
QString string = str;
int startIndex = 0;
forever {
startIndex = string.indexOf(QLatin1Char('$'), startIndex);
if (startIndex < 0)
break;
if (string.length() < startIndex + 3)
break;
if (string.at(startIndex + 1) != QLatin1Char('(')) {
startIndex++;
continue;
}
int endIndex = string.indexOf(QLatin1Char(')'), startIndex + 2);
if (endIndex < 0)
break;
QString value = getEnv(string.mid(startIndex + 2, endIndex - startIndex - 2));
string.replace(startIndex, endIndex - startIndex + 1, value);
startIndex += value.length();
}
return string;
}
#ifndef QT_BUILD_QMAKE
#ifdef PROEVALUATOR_INIT_PROPS
bool QMakeGlobals::initProperties()
{
QByteArray data;
#ifndef QT_BOOTSTRAPPED
QProcess proc;
proc.start(qmake_abslocation, QStringList() << QLatin1String("-query"));
if (!proc.waitForFinished())
return false;
data = proc.readAll();
#else
if (FILE *proc = QT_POPEN(QString(QMakeInternal::IoUtils::shellQuote(qmake_abslocation)
+ QLatin1String(" -query")).toLocal8Bit(), QT_POPEN_READ)) {
char buff[1024];
while (!feof(proc))
data.append(buff, int(fread(buff, 1, 1023, proc)));
QT_PCLOSE(proc);
}
#endif
foreach (QByteArray line, data.split('\n')) {
int off = line.indexOf(':');
if (off < 0) // huh?
continue;
if (line.endsWith('\r'))
line.chop(1);
QString name = QString::fromLatin1(line.left(off));
ProString value = ProString(QDir::fromNativeSeparators(
QString::fromLocal8Bit(line.mid(off + 1))));
properties.insert(ProKey(name), value);
if (name.startsWith(QLatin1String("QT_"))) {
bool plain = !name.contains(QLatin1Char('/'));
if (!plain) {
if (!name.endsWith(QLatin1String("/get")))
continue;
name.chop(4);
}
if (name.startsWith(QLatin1String("QT_INSTALL_"))) {
if (plain) {
properties.insert(ProKey(name + QLatin1String("/raw")), value);
properties.insert(ProKey(name + QLatin1String("/get")), value);
}
properties.insert(ProKey(name + QLatin1String("/src")), value);
if (name == QLatin1String("QT_INSTALL_PREFIX")
|| name == QLatin1String("QT_INSTALL_DATA")
|| name == QLatin1String("QT_INSTALL_BINS")) {
name.replace(3, 7, QLatin1String("HOST"));
if (plain) {
properties.insert(ProKey(name), value);
properties.insert(ProKey(name + QLatin1String("/get")), value);
}
properties.insert(ProKey(name + QLatin1String("/src")), value);
}
} else if (name.startsWith(QLatin1String("QT_HOST_"))) {
if (plain)
properties.insert(ProKey(name + QLatin1String("/get")), value);
properties.insert(ProKey(name + QLatin1String("/src")), value);
}
}
}
return true;
}
#else
void QMakeGlobals::setProperties(const QHash<QString, QString> &props)
{
QHash<QString, QString>::ConstIterator it = props.constBegin(), eit = props.constEnd();
for (; it != eit; ++it)
properties.insert(ProKey(it.key()), ProString(it.value()));
}
#endif
#endif // QT_BUILD_QMAKE
QT_END_NAMESPACE