qt5base-lts/qmake/library/qmakeglobals.cpp
Joerg Bornemann 032c536644 qmake: Sync back changes from qttools
Copy back changes to qmake internals that were done in qttools.
This makes it easier to keep the qmakelib copies in sync.

The code guarded by PROEVALUATOR_CUMULATIVE is not used by qmake itself,
but only by lupdate and Qt Creator. It seems to be sensible to have the
same behavior with regards to feature file evaluation in both
projects. It was originally introduced in qttools for QTBUG-62478.

The change regarding QT_BOOTSTRAPPED is safe, because the 'process'
feature is available and off in the boostrapped case.

Change-Id: I030ca8b093c017040a9ff9f4bb09a7cca4aa8964
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2020-11-26 20:05:41 +01:00

393 lines
13 KiB
C++

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the qmake application of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** 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 https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $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 <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
using namespace QMakeInternal; // for IoUtils
#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 = IoUtils::resolvePath(state.pwd, ret);
if (QFile::exists(absRet))
ret = absRet;
}
return ret;
}
QMakeGlobals::ArgumentReturn QMakeGlobals::addCommandLineArguments(
QMakeCmdLineParserState &state, QStringList &args, int *pos)
{
enum { ArgNone, ArgConfig, ArgSpec, ArgXSpec, ArgTmpl, ArgTmplPfx, ArgCache, ArgQtConf } argState = ArgNone;
for (; *pos < args.count(); (*pos)++) {
QString arg = args.at(*pos);
switch (argState) {
case ArgConfig:
state.configs[state.phase] << 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] = IoUtils::resolvePath(state.pwd, arg);
break;
case ArgQtConf:
qtconf = args[*pos] = IoUtils::resolvePath(state.pwd, arg);
break;
default:
if (arg.startsWith(QLatin1Char('-'))) {
if (arg == QLatin1String("--")) {
state.extraargs = args.mid(*pos + 1);
args.erase(args.begin() + *pos, args.end());
return ArgumentsOk;
}
if (arg == QLatin1String("-early"))
state.phase = QMakeEvalEarly;
else if (arg == QLatin1String("-before"))
state.phase = QMakeEvalBefore;
else if (arg == QLatin1String("-after"))
state.phase = QMakeEvalAfter;
else if (arg == QLatin1String("-late"))
state.phase = QMakeEvalLate;
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("-qtconf"))
argState = ArgQtConf;
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('='))) {
state.cmds[state.phase] << arg;
} else {
return ArgumentUnknown;
}
continue;
}
argState = ArgNone;
}
if (argState != ArgNone)
return ArgumentMalformed;
return ArgumentsOk;
}
void QMakeGlobals::commitCommandLineArguments(QMakeCmdLineParserState &state)
{
if (!state.extraargs.isEmpty()) {
QString extra = fL1S("QMAKE_EXTRA_ARGS =");
for (const QString &ea : qAsConst(state.extraargs))
extra += QLatin1Char(' ') + QMakeEvaluator::quoteValue(ProString(ea));
state.cmds[QMakeEvalBefore] << extra;
}
for (int p = 0; p < 4; p++) {
if (!state.configs[p].isEmpty())
state.cmds[p] << (fL1S("CONFIG += ") + state.configs[p].join(QLatin1Char(' ')));
extra_cmds[p] = state.cmds[p].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()) {
QString cwd(QDir::currentPath());
const QStringList vals = val.split(dirlist_sep, Qt::SkipEmptyParts);
ret.reserve(vals.length());
for (const QString &it : vals)
ret << IoUtils::resolvePath(cwd, 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;
#if QT_CONFIG(process)
QProcess proc;
proc.start(qmake_abslocation, QStringList() << QLatin1String("-query"));
if (!proc.waitForFinished())
return false;
data = proc.readAll();
#else
if (FILE *proc = QT_POPEN(QString(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
parseProperties(data, properties);
return true;
}
#endif
void QMakeGlobals::parseProperties(const QByteArray &data, QHash<ProKey, ProString> &properties)
{
const auto lines = data.split('\n');
for (QByteArray line : lines) {
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))));
if (value.isNull())
value = ProString(""); // Make sure it is not null, to discern from missing keys
properties.insert(ProKey(name), value);
if (name.startsWith(QLatin1String("QT_"))) {
enum { PropPut, PropRaw, PropGet } variant;
if (name.contains(QLatin1Char('/'))) {
if (name.endsWith(QLatin1String("/raw")))
variant = PropRaw;
else if (name.endsWith(QLatin1String("/get")))
variant = PropGet;
else // Nothing falls back on /src or /dev.
continue;
name.chop(4);
} else {
variant = PropPut;
}
if (name.startsWith(QLatin1String("QT_INSTALL_"))) {
if (variant < PropRaw) {
if (name == QLatin1String("QT_INSTALL_PREFIX")
|| name == QLatin1String("QT_INSTALL_DATA")
|| name == QLatin1String("QT_INSTALL_LIBS")
|| name == QLatin1String("QT_INSTALL_BINS")) {
// Qt4 fallback
QString hname = name;
hname.replace(3, 7, QLatin1String("HOST"));
properties.insert(ProKey(hname), value);
properties.insert(ProKey(hname + QLatin1String("/get")), value);
properties.insert(ProKey(hname + QLatin1String("/src")), value);
}
properties.insert(ProKey(name + QLatin1String("/raw")), value);
}
if (variant <= PropRaw)
properties.insert(ProKey(name + QLatin1String("/dev")), value);
} else if (!name.startsWith(QLatin1String("QT_HOST_"))) {
continue;
}
if (variant != PropRaw) {
if (variant < PropGet)
properties.insert(ProKey(name + QLatin1String("/get")), value);
properties.insert(ProKey(name + QLatin1String("/src")), value);
}
}
}
}
#endif // QT_BUILD_QMAKE
QT_END_NAMESPACE