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

source code of qttools/src/linguist/shared/qmakeglobals.cpp