Revision control
++ // 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 "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 <qt_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;
++ }
++
++ /*
++ * Return value meanings:
++ * ArgumentUnknown The argument at *pos was not handled by this function.
++ * Leave it to the caller to handle this argument.
++ * ArgumentMalformed There was an error detected.
++ * ArgumentsOk All arguments were known. There are no arguments left to handle.
++ */
++ QMakeGlobals::ArgumentReturn QMakeGlobals::addCommandLineArguments(
++ QMakeCmdLineParserState &state, QStringList &args, int *pos)
++ {
++ enum { ArgNone, ArgConfig, ArgSpec, ArgXSpec, ArgTmpl, ArgTmplPfx, ArgCache, ArgQtConf } argState = ArgNone;
++ for (; *pos < args.size(); (*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 : std::as_const(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.size();
++ int dstLen = dstpath.size();
++ 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.size() == source_root.size()
++ || fileName.at(source_root.size()) == QLatin1Char('/'))) {
++ return build_root + fileName.mid(source_root.size());
++ }
++ 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.size());
++ 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.size() < 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.size();
++ }
++ 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