| 1 | /**************************************************************************** | 
|---|
| 2 | ** | 
|---|
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. | 
|---|
| 4 | ** Contact: https://www.qt.io/licensing/ | 
|---|
| 5 | ** | 
|---|
| 6 | ** This file is part of the qmake application of the Qt Toolkit. | 
|---|
| 7 | ** | 
|---|
| 8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ | 
|---|
| 9 | ** Commercial License Usage | 
|---|
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
|---|
| 11 | ** accordance with the commercial license agreement provided with the | 
|---|
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
|---|
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
|---|
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
|---|
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
|---|
| 16 | ** | 
|---|
| 17 | ** GNU General Public License Usage | 
|---|
| 18 | ** Alternatively, this file may be used under the terms of the GNU | 
|---|
| 19 | ** General Public License version 3 as published by the Free Software | 
|---|
| 20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT | 
|---|
| 21 | ** included in the packaging of this file. Please review the following | 
|---|
| 22 | ** information to ensure the GNU General Public License requirements will | 
|---|
| 23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | 
|---|
| 24 | ** | 
|---|
| 25 | ** $QT_END_LICENSE$ | 
|---|
| 26 | ** | 
|---|
| 27 | ****************************************************************************/ | 
|---|
| 28 |  | 
|---|
| 29 | #include "qmakeglobals.h" | 
|---|
| 30 |  | 
|---|
| 31 | #include "qmakeevaluator.h" | 
|---|
| 32 | #include "ioutils.h" | 
|---|
| 33 |  | 
|---|
| 34 | #include <qbytearray.h> | 
|---|
| 35 | #include <qdatetime.h> | 
|---|
| 36 | #include <qdebug.h> | 
|---|
| 37 | #include <qdir.h> | 
|---|
| 38 | #include <qfile.h> | 
|---|
| 39 | #include <qfileinfo.h> | 
|---|
| 40 | #include <qlist.h> | 
|---|
| 41 | #include <qset.h> | 
|---|
| 42 | #include <qstack.h> | 
|---|
| 43 | #include <qstring.h> | 
|---|
| 44 | #include <qstringlist.h> | 
|---|
| 45 | #include <qtextstream.h> | 
|---|
| 46 | #ifdef PROEVALUATOR_THREAD_SAFE | 
|---|
| 47 | # include <qthreadpool.h> | 
|---|
| 48 | #endif | 
|---|
| 49 |  | 
|---|
| 50 | #ifdef Q_OS_UNIX | 
|---|
| 51 | #include <unistd.h> | 
|---|
| 52 | #include <sys/utsname.h> | 
|---|
| 53 | #else | 
|---|
| 54 | #include <windows.h> | 
|---|
| 55 | #endif | 
|---|
| 56 | #include <stdio.h> | 
|---|
| 57 | #include <stdlib.h> | 
|---|
| 58 |  | 
|---|
| 59 | #ifdef Q_OS_WIN32 | 
|---|
| 60 | #define QT_POPEN _popen | 
|---|
| 61 | #define QT_POPEN_READ "rb" | 
|---|
| 62 | #define QT_PCLOSE _pclose | 
|---|
| 63 | #else | 
|---|
| 64 | #define QT_POPEN popen | 
|---|
| 65 | #define QT_POPEN_READ "r" | 
|---|
| 66 | #define QT_PCLOSE pclose | 
|---|
| 67 | #endif | 
|---|
| 68 |  | 
|---|
| 69 | QT_BEGIN_NAMESPACE | 
|---|
| 70 | using namespace QMakeInternal; // for IoUtils | 
|---|
| 71 |  | 
|---|
| 72 | #define fL1S(s) QString::fromLatin1(s) | 
|---|
| 73 |  | 
|---|
| 74 | QMakeGlobals::QMakeGlobals() | 
|---|
| 75 | { | 
|---|
| 76 | do_cache = true; | 
|---|
| 77 |  | 
|---|
| 78 | #ifdef PROEVALUATOR_DEBUG | 
|---|
| 79 | debugLevel = 0; | 
|---|
| 80 | #endif | 
|---|
| 81 | #ifdef Q_OS_WIN | 
|---|
| 82 | dirlist_sep = QLatin1Char(';'); | 
|---|
| 83 | dir_sep = QLatin1Char('\\'); | 
|---|
| 84 | #else | 
|---|
| 85 | dirlist_sep = QLatin1Char(':'); | 
|---|
| 86 | dir_sep = QLatin1Char('/'); | 
|---|
| 87 | #endif | 
|---|
| 88 | } | 
|---|
| 89 |  | 
|---|
| 90 | QMakeGlobals::~QMakeGlobals() | 
|---|
| 91 | { | 
|---|
| 92 | qDeleteAll(baseEnvs); | 
|---|
| 93 | } | 
|---|
| 94 |  | 
|---|
| 95 | QString QMakeGlobals::cleanSpec(QMakeCmdLineParserState &state, const QString &spec) | 
|---|
| 96 | { | 
|---|
| 97 | QString ret = QDir::cleanPath(spec); | 
|---|
| 98 | if (ret.contains(QLatin1Char('/'))) { | 
|---|
| 99 | QString absRet = IoUtils::resolvePath(state.pwd, ret); | 
|---|
| 100 | if (QFile::exists(absRet)) | 
|---|
| 101 | ret = absRet; | 
|---|
| 102 | } | 
|---|
| 103 | return ret; | 
|---|
| 104 | } | 
|---|
| 105 |  | 
|---|
| 106 | QMakeGlobals::ArgumentReturn QMakeGlobals::addCommandLineArguments( | 
|---|
| 107 | QMakeCmdLineParserState &state, QStringList &args, int *pos) | 
|---|
| 108 | { | 
|---|
| 109 | enum { ArgNone, ArgConfig, ArgSpec, ArgXSpec, ArgTmpl, ArgTmplPfx, ArgCache, ArgQtConf } argState = ArgNone; | 
|---|
| 110 | for (; *pos < args.count(); (*pos)++) { | 
|---|
| 111 | QString arg = args.at(*pos); | 
|---|
| 112 | switch (argState) { | 
|---|
| 113 | case ArgConfig: | 
|---|
| 114 | state.configs[state.phase] << arg; | 
|---|
| 115 | break; | 
|---|
| 116 | case ArgSpec: | 
|---|
| 117 | qmakespec = args[*pos] = cleanSpec(state, arg); | 
|---|
| 118 | break; | 
|---|
| 119 | case ArgXSpec: | 
|---|
| 120 | xqmakespec = args[*pos] = cleanSpec(state, arg); | 
|---|
| 121 | break; | 
|---|
| 122 | case ArgTmpl: | 
|---|
| 123 | user_template = arg; | 
|---|
| 124 | break; | 
|---|
| 125 | case ArgTmplPfx: | 
|---|
| 126 | user_template_prefix = arg; | 
|---|
| 127 | break; | 
|---|
| 128 | case ArgCache: | 
|---|
| 129 | cachefile = args[*pos] = IoUtils::resolvePath(state.pwd, arg); | 
|---|
| 130 | break; | 
|---|
| 131 | case ArgQtConf: | 
|---|
| 132 | qtconf = args[*pos] = IoUtils::resolvePath(state.pwd, arg); | 
|---|
| 133 | break; | 
|---|
| 134 | default: | 
|---|
| 135 | if (arg.startsWith(QLatin1Char('-'))) { | 
|---|
| 136 | if (arg == QLatin1String( "--")) { | 
|---|
| 137 | state.extraargs = args.mid(*pos + 1); | 
|---|
| 138 | args.erase(args.begin() + *pos, args.end()); | 
|---|
| 139 | return ArgumentsOk; | 
|---|
| 140 | } | 
|---|
| 141 | if (arg == QLatin1String( "-early")) | 
|---|
| 142 | state.phase = QMakeEvalEarly; | 
|---|
| 143 | else if (arg == QLatin1String( "-before")) | 
|---|
| 144 | state.phase = QMakeEvalBefore; | 
|---|
| 145 | else if (arg == QLatin1String( "-after")) | 
|---|
| 146 | state.phase = QMakeEvalAfter; | 
|---|
| 147 | else if (arg == QLatin1String( "-late")) | 
|---|
| 148 | state.phase = QMakeEvalLate; | 
|---|
| 149 | else if (arg == QLatin1String( "-config")) | 
|---|
| 150 | argState = ArgConfig; | 
|---|
| 151 | else if (arg == QLatin1String( "-nocache")) | 
|---|
| 152 | do_cache = false; | 
|---|
| 153 | else if (arg == QLatin1String( "-cache")) | 
|---|
| 154 | argState = ArgCache; | 
|---|
| 155 | else if (arg == QLatin1String( "-qtconf")) | 
|---|
| 156 | argState = ArgQtConf; | 
|---|
| 157 | else if (arg == QLatin1String( "-platform") || arg == QLatin1String( "-spec")) | 
|---|
| 158 | argState = ArgSpec; | 
|---|
| 159 | else if (arg == QLatin1String( "-xplatform") || arg == QLatin1String( "-xspec")) | 
|---|
| 160 | argState = ArgXSpec; | 
|---|
| 161 | else if (arg == QLatin1String( "-template") || arg == QLatin1String( "-t")) | 
|---|
| 162 | argState = ArgTmpl; | 
|---|
| 163 | else if (arg == QLatin1String( "-template_prefix") || arg == QLatin1String( "-tp")) | 
|---|
| 164 | argState = ArgTmplPfx; | 
|---|
| 165 | else if (arg == QLatin1String( "-win32")) | 
|---|
| 166 | dir_sep = QLatin1Char('\\'); | 
|---|
| 167 | else if (arg == QLatin1String( "-unix")) | 
|---|
| 168 | dir_sep = QLatin1Char('/'); | 
|---|
| 169 | else | 
|---|
| 170 | return ArgumentUnknown; | 
|---|
| 171 | } else if (arg.contains(QLatin1Char('='))) { | 
|---|
| 172 | state.cmds[state.phase] << arg; | 
|---|
| 173 | } else { | 
|---|
| 174 | return ArgumentUnknown; | 
|---|
| 175 | } | 
|---|
| 176 | continue; | 
|---|
| 177 | } | 
|---|
| 178 | argState = ArgNone; | 
|---|
| 179 | } | 
|---|
| 180 | if (argState != ArgNone) | 
|---|
| 181 | return ArgumentMalformed; | 
|---|
| 182 | return ArgumentsOk; | 
|---|
| 183 | } | 
|---|
| 184 |  | 
|---|
| 185 | void QMakeGlobals::commitCommandLineArguments(QMakeCmdLineParserState &state) | 
|---|
| 186 | { | 
|---|
| 187 | if (!state.extraargs.isEmpty()) { | 
|---|
| 188 | QString  = fL1S( "QMAKE_EXTRA_ARGS ="); | 
|---|
| 189 | for (const QString &ea : qAsConst(state.extraargs)) | 
|---|
| 190 | extra += QLatin1Char(' ') + QMakeEvaluator::quoteValue(ProString(ea)); | 
|---|
| 191 | state.cmds[QMakeEvalBefore] << extra; | 
|---|
| 192 | } | 
|---|
| 193 | for (int p = 0; p < 4; p++) { | 
|---|
| 194 | if (!state.configs[p].isEmpty()) | 
|---|
| 195 | state.cmds[p] << (fL1S( "CONFIG += ") + state.configs[p].join(QLatin1Char(' '))); | 
|---|
| 196 | extra_cmds[p] = state.cmds[p].join(QLatin1Char('\n')); | 
|---|
| 197 | } | 
|---|
| 198 |  | 
|---|
| 199 | if (xqmakespec.isEmpty()) | 
|---|
| 200 | xqmakespec = qmakespec; | 
|---|
| 201 | } | 
|---|
| 202 |  | 
|---|
| 203 | void QMakeGlobals::useEnvironment() | 
|---|
| 204 | { | 
|---|
| 205 | if (xqmakespec.isEmpty()) | 
|---|
| 206 | xqmakespec = getEnv(QLatin1String( "XQMAKESPEC")); | 
|---|
| 207 | if (qmakespec.isEmpty()) { | 
|---|
| 208 | qmakespec = getEnv(QLatin1String( "QMAKESPEC")); | 
|---|
| 209 | if (xqmakespec.isEmpty()) | 
|---|
| 210 | xqmakespec = qmakespec; | 
|---|
| 211 | } | 
|---|
| 212 | } | 
|---|
| 213 |  | 
|---|
| 214 | void QMakeGlobals::setCommandLineArguments(const QString &pwd, const QStringList &_args) | 
|---|
| 215 | { | 
|---|
| 216 | QStringList args = _args; | 
|---|
| 217 |  | 
|---|
| 218 | QMakeCmdLineParserState state(pwd); | 
|---|
| 219 | for (int pos = 0; pos < args.size(); pos++) | 
|---|
| 220 | addCommandLineArguments(state, args, &pos); | 
|---|
| 221 | commitCommandLineArguments(state); | 
|---|
| 222 | useEnvironment(); | 
|---|
| 223 | } | 
|---|
| 224 |  | 
|---|
| 225 | void QMakeGlobals::setDirectories(const QString &input_dir, const QString &output_dir) | 
|---|
| 226 | { | 
|---|
| 227 | if (input_dir != output_dir && !output_dir.isEmpty()) { | 
|---|
| 228 | QString srcpath = input_dir; | 
|---|
| 229 | if (!srcpath.endsWith(QLatin1Char('/'))) | 
|---|
| 230 | srcpath += QLatin1Char('/'); | 
|---|
| 231 | QString dstpath = output_dir; | 
|---|
| 232 | if (!dstpath.endsWith(QLatin1Char('/'))) | 
|---|
| 233 | dstpath += QLatin1Char('/'); | 
|---|
| 234 | int srcLen = srcpath.length(); | 
|---|
| 235 | int dstLen = dstpath.length(); | 
|---|
| 236 | int lastSl = -1; | 
|---|
| 237 | while (++lastSl, --srcLen, --dstLen, | 
|---|
| 238 | srcLen && dstLen && srcpath.at(srcLen) == dstpath.at(dstLen)) | 
|---|
| 239 | if (srcpath.at(srcLen) == QLatin1Char('/')) | 
|---|
| 240 | lastSl = 0; | 
|---|
| 241 | source_root = srcpath.left(srcLen + lastSl); | 
|---|
| 242 | build_root = dstpath.left(dstLen + lastSl); | 
|---|
| 243 | } | 
|---|
| 244 | } | 
|---|
| 245 |  | 
|---|
| 246 | QString QMakeGlobals::shadowedPath(const QString &fileName) const | 
|---|
| 247 | { | 
|---|
| 248 | if (source_root.isEmpty()) | 
|---|
| 249 | return fileName; | 
|---|
| 250 | if (fileName.startsWith(source_root) | 
|---|
| 251 | && (fileName.length() == source_root.length() | 
|---|
| 252 | || fileName.at(source_root.length()) == QLatin1Char('/'))) { | 
|---|
| 253 | return build_root + fileName.mid(source_root.length()); | 
|---|
| 254 | } | 
|---|
| 255 | return QString(); | 
|---|
| 256 | } | 
|---|
| 257 |  | 
|---|
| 258 | QStringList QMakeGlobals::splitPathList(const QString &val) const | 
|---|
| 259 | { | 
|---|
| 260 | QStringList ret; | 
|---|
| 261 | if (!val.isEmpty()) { | 
|---|
| 262 | QString cwd(QDir::currentPath()); | 
|---|
| 263 | const QStringList vals = val.split(dirlist_sep, Qt::SkipEmptyParts); | 
|---|
| 264 | ret.reserve(vals.length()); | 
|---|
| 265 | for (const QString &it : vals) | 
|---|
| 266 | ret << IoUtils::resolvePath(cwd, it); | 
|---|
| 267 | } | 
|---|
| 268 | return ret; | 
|---|
| 269 | } | 
|---|
| 270 |  | 
|---|
| 271 | QString QMakeGlobals::getEnv(const QString &var) const | 
|---|
| 272 | { | 
|---|
| 273 | #ifdef PROEVALUATOR_SETENV | 
|---|
| 274 | return environment.value(var); | 
|---|
| 275 | #else | 
|---|
| 276 | return QString::fromLocal8Bit(qgetenv(var.toLocal8Bit().constData())); | 
|---|
| 277 | #endif | 
|---|
| 278 | } | 
|---|
| 279 |  | 
|---|
| 280 | QStringList QMakeGlobals::getPathListEnv(const QString &var) const | 
|---|
| 281 | { | 
|---|
| 282 | return splitPathList(getEnv(var)); | 
|---|
| 283 | } | 
|---|
| 284 |  | 
|---|
| 285 | QString QMakeGlobals::expandEnvVars(const QString &str) const | 
|---|
| 286 | { | 
|---|
| 287 | QString string = str; | 
|---|
| 288 | int startIndex = 0; | 
|---|
| 289 | forever { | 
|---|
| 290 | startIndex = string.indexOf(QLatin1Char('$'), startIndex); | 
|---|
| 291 | if (startIndex < 0) | 
|---|
| 292 | break; | 
|---|
| 293 | if (string.length() < startIndex + 3) | 
|---|
| 294 | break; | 
|---|
| 295 | if (string.at(startIndex + 1) != QLatin1Char('(')) { | 
|---|
| 296 | startIndex++; | 
|---|
| 297 | continue; | 
|---|
| 298 | } | 
|---|
| 299 | int endIndex = string.indexOf(QLatin1Char(')'), startIndex + 2); | 
|---|
| 300 | if (endIndex < 0) | 
|---|
| 301 | break; | 
|---|
| 302 | QString value = getEnv(string.mid(startIndex + 2, endIndex - startIndex - 2)); | 
|---|
| 303 | string.replace(startIndex, endIndex - startIndex + 1, value); | 
|---|
| 304 | startIndex += value.length(); | 
|---|
| 305 | } | 
|---|
| 306 | return string; | 
|---|
| 307 | } | 
|---|
| 308 |  | 
|---|
| 309 | #ifndef QT_BUILD_QMAKE | 
|---|
| 310 | #ifdef PROEVALUATOR_INIT_PROPS | 
|---|
| 311 | bool QMakeGlobals::initProperties() | 
|---|
| 312 | { | 
|---|
| 313 | QByteArray data; | 
|---|
| 314 | #ifndef QT_BOOTSTRAPPED | 
|---|
| 315 | QProcess proc; | 
|---|
| 316 | proc.start(qmake_abslocation, QStringList() << QLatin1String( "-query")); | 
|---|
| 317 | if (!proc.waitForFinished()) | 
|---|
| 318 | return false; | 
|---|
| 319 | data = proc.readAll(); | 
|---|
| 320 | #else | 
|---|
| 321 | if (FILE *proc = QT_POPEN(QString(IoUtils::shellQuote(qmake_abslocation) | 
|---|
| 322 | + QLatin1String( " -query")).toLocal8Bit(), QT_POPEN_READ)) { | 
|---|
| 323 | char buff[1024]; | 
|---|
| 324 | while (!feof(proc)) | 
|---|
| 325 | data.append(buff, int(fread(buff, 1, 1023, proc))); | 
|---|
| 326 | QT_PCLOSE(proc); | 
|---|
| 327 | } | 
|---|
| 328 | #endif | 
|---|
| 329 | parseProperties(data, properties); | 
|---|
| 330 | return true; | 
|---|
| 331 | } | 
|---|
| 332 | #endif | 
|---|
| 333 |  | 
|---|
| 334 | void QMakeGlobals::parseProperties(const QByteArray &data, QHash<ProKey, ProString> &properties) | 
|---|
| 335 | { | 
|---|
| 336 | const auto lines = data.split('\n'); | 
|---|
| 337 | for (QByteArray line : lines) { | 
|---|
| 338 | int off = line.indexOf(':'); | 
|---|
| 339 | if (off < 0) // huh? | 
|---|
| 340 | continue; | 
|---|
| 341 | if (line.endsWith('\r')) | 
|---|
| 342 | line.chop(1); | 
|---|
| 343 | QString name = QString::fromLatin1(line.left(off)); | 
|---|
| 344 | ProString value = ProString(QDir::fromNativeSeparators( | 
|---|
| 345 | QString::fromLocal8Bit(line.mid(off + 1)))); | 
|---|
| 346 | if (value.isNull()) | 
|---|
| 347 | value = ProString( ""); // Make sure it is not null, to discern from missing keys | 
|---|
| 348 | properties.insert(ProKey(name), value); | 
|---|
| 349 | if (name.startsWith(QLatin1String( "QT_"))) { | 
|---|
| 350 | enum { PropPut, PropRaw, PropGet } variant; | 
|---|
| 351 | if (name.contains(QLatin1Char('/'))) { | 
|---|
| 352 | if (name.endsWith(QLatin1String( "/raw"))) | 
|---|
| 353 | variant = PropRaw; | 
|---|
| 354 | else if (name.endsWith(QLatin1String( "/get"))) | 
|---|
| 355 | variant = PropGet; | 
|---|
| 356 | else  // Nothing falls back on /src or /dev. | 
|---|
| 357 | continue; | 
|---|
| 358 | name.chop(4); | 
|---|
| 359 | } else { | 
|---|
| 360 | variant = PropPut; | 
|---|
| 361 | } | 
|---|
| 362 | if (name.startsWith(QLatin1String( "QT_INSTALL_"))) { | 
|---|
| 363 | if (variant < PropRaw) { | 
|---|
| 364 | if (name == QLatin1String( "QT_INSTALL_PREFIX") | 
|---|
| 365 | || name == QLatin1String( "QT_INSTALL_DATA") | 
|---|
| 366 | || name == QLatin1String( "QT_INSTALL_LIBS") | 
|---|
| 367 | || name == QLatin1String( "QT_INSTALL_BINS")) { | 
|---|
| 368 | // Qt4 fallback | 
|---|
| 369 | QString hname = name; | 
|---|
| 370 | hname.replace(3, 7, QLatin1String( "HOST")); | 
|---|
| 371 | properties.insert(ProKey(hname), value); | 
|---|
| 372 | properties.insert(ProKey(hname + QLatin1String( "/get")), value); | 
|---|
| 373 | properties.insert(ProKey(hname + QLatin1String( "/src")), value); | 
|---|
| 374 | } | 
|---|
| 375 | properties.insert(ProKey(name + QLatin1String( "/raw")), value); | 
|---|
| 376 | } | 
|---|
| 377 | if (variant <= PropRaw) | 
|---|
| 378 | properties.insert(ProKey(name + QLatin1String( "/dev")), value); | 
|---|
| 379 | } else if (!name.startsWith(QLatin1String( "QT_HOST_"))) { | 
|---|
| 380 | continue; | 
|---|
| 381 | } | 
|---|
| 382 | if (variant != PropRaw) { | 
|---|
| 383 | if (variant < PropGet) | 
|---|
| 384 | properties.insert(ProKey(name + QLatin1String( "/get")), value); | 
|---|
| 385 | properties.insert(ProKey(name + QLatin1String( "/src")), value); | 
|---|
| 386 | } | 
|---|
| 387 | } | 
|---|
| 388 | } | 
|---|
| 389 | } | 
|---|
| 390 | #endif // QT_BUILD_QMAKE | 
|---|
| 391 |  | 
|---|
| 392 | QT_END_NAMESPACE | 
|---|
| 393 |  | 
|---|