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
70 QT_BEGIN_NAMESPACE
71 using namespace QMakeInternal; // for IoUtils
72
73 #define fL1S(s) QString::fromLatin1(s)
74
QMakeGlobals()75 QMakeGlobals::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
~QMakeGlobals()91 QMakeGlobals::~QMakeGlobals()
92 {
93 qDeleteAll(baseEnvs);
94 }
95
cleanSpec(QMakeCmdLineParserState & state,const QString & spec)96 QString QMakeGlobals::cleanSpec(QMakeCmdLineParserState &state, const QString &spec)
97 {
98 QString ret = QDir::cleanPath(spec);
99 if (ret.contains(QLatin1Char('/'))) {
100 QString absRet = IoUtils::resolvePath(state.pwd, ret);
101 if (QFile::exists(absRet))
102 ret = absRet;
103 }
104 return ret;
105 }
106
addCommandLineArguments(QMakeCmdLineParserState & state,QStringList & args,int * pos)107 QMakeGlobals::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(*pos);
113 switch (argState) {
114 case ArgConfig:
115 state.configs[state.phase] << arg;
116 break;
117 case ArgSpec:
118 qmakespec = args[*pos] = cleanSpec(state, arg);
119 break;
120 case ArgXSpec:
121 xqmakespec = args[*pos] = cleanSpec(state, 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(state.pwd, arg);
131 break;
132 case ArgQtConf:
133 qtconf = args[*pos] = IoUtils::resolvePath(state.pwd, arg);
134 break;
135 default:
136 if (arg.startsWith(QLatin1Char('-'))) {
137 if (arg == QLatin1String("--")) {
138 state.extraargs = args.mid(*pos + 1);
139 args.erase(args.begin() + *pos, 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(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
commitCommandLineArguments(QMakeCmdLineParserState & state)186 void QMakeGlobals::commitCommandLineArguments(QMakeCmdLineParserState &state)
187 {
188 if (!state.extraargs.isEmpty()) {
189 QString extra = fL1S("QMAKE_EXTRA_ARGS =");
190 for (const QString &ea : qAsConst(state.extraargs))
191 extra += QLatin1Char(' ') + QMakeEvaluator::quoteValue(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(QLatin1Char(' ')));
197 extra_cmds[p] = state.cmds[p].join(QLatin1Char('\n'));
198 }
199
200 if (xqmakespec.isEmpty())
201 xqmakespec = qmakespec;
202 }
203
useEnvironment()204 void 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
setCommandLineArguments(const QString & pwd,const QStringList & _args)215 void 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);
222 commitCommandLineArguments(state);
223 useEnvironment();
224 }
225
setDirectories(const QString & input_dir,const QString & output_dir)226 void 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(QLatin1Char('/')))
231 srcpath += QLatin1Char('/');
232 QString dstpath = output_dir;
233 if (!dstpath.endsWith(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(srcLen) == dstpath.at(dstLen))
240 if (srcpath.at(srcLen) == QLatin1Char('/'))
241 lastSl = 0;
242 source_root = srcpath.left(srcLen + lastSl);
243 build_root = dstpath.left(dstLen + lastSl);
244 }
245 }
246
shadowedPath(const QString & fileName) const247 QString QMakeGlobals::shadowedPath(const QString &fileName) const
248 {
249 if (source_root.isEmpty())
250 return fileName;
251 if (fileName.startsWith(source_root)
252 && (fileName.length() == source_root.length()
253 || fileName.at(source_root.length()) == QLatin1Char('/'))) {
254 return build_root + fileName.mid(source_root.length());
255 }
256 return QString();
257 }
258
splitPathList(const QString & val) const259 QStringList 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(dirlist_sep, Qt::SkipEmptyParts);
265 ret.reserve(vals.length());
266 for (const QString &it : vals)
267 ret << IoUtils::resolvePath(cwd, it);
268 }
269 return ret;
270 }
271
getEnv(const QString & var) const272 QString QMakeGlobals::getEnv(const QString &var) const
273 {
274 #ifdef PROEVALUATOR_SETENV
275 return environment.value(var);
276 #else
277 return QString::fromLocal8Bit(qgetenv(var.toLocal8Bit().constData()));
278 #endif
279 }
280
getPathListEnv(const QString & var) const281 QStringList QMakeGlobals::getPathListEnv(const QString &var) const
282 {
283 return splitPathList(getEnv(var));
284 }
285
expandEnvVars(const QString & str) const286 QString QMakeGlobals::expandEnvVars(const QString &str) const
287 {
288 QString string = str;
289 int startIndex = 0;
290 forever {
291 startIndex = string.indexOf(QLatin1Char('$'), startIndex);
292 if (startIndex < 0)
293 break;
294 if (string.length() < startIndex + 3)
295 break;
296 if (string.at(startIndex + 1) != QLatin1Char('(')) {
297 startIndex++;
298 continue;
299 }
300 int endIndex = string.indexOf(QLatin1Char(')'), startIndex + 2);
301 if (endIndex < 0)
302 break;
303 QString value = getEnv(string.mid(startIndex + 2, endIndex - startIndex - 2));
304 string.replace(startIndex, endIndex - startIndex + 1, value);
305 startIndex += value.length();
306 }
307 return string;
308 }
309
310 #ifndef QT_BUILD_QMAKE
311 #ifdef PROEVALUATOR_INIT_PROPS
initProperties()312 bool QMakeGlobals::initProperties()
313 {
314 QByteArray data;
315 #if QT_CONFIG(process)
316 QProcess proc;
317 proc.start(qmake_abslocation, 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, properties);
331 return true;
332 }
333 #endif
334
parseProperties(const QByteArray & data,QHash<ProKey,ProString> & properties)335 void QMakeGlobals::parseProperties(const QByteArray &data, QHash<ProKey, ProString> &properties)
336 {
337 const auto lines = data.split('\n');
338 for (QByteArray line : lines) {
339 int off = line.indexOf(':');
340 if (off < 0) // huh?
341 continue;
342 if (line.endsWith('\r'))
343 line.chop(1);
344 QString name = QString::fromLatin1(line.left(off));
345 ProString value = ProString(QDir::fromNativeSeparators(
346 QString::fromLocal8Bit(line.mid(off + 1))));
347 if (value.isNull())
348 value = ProString(""); // Make sure it is not null, to discern from missing keys
349 properties.insert(ProKey(name), value);
350 if (name.startsWith(QLatin1String("QT_"))) {
351 enum { PropPut, PropRaw, PropGet } variant;
352 if (name.contains(QLatin1Char('/'))) {
353 if (name.endsWith(QLatin1String("/raw")))
354 variant = PropRaw;
355 else if (name.endsWith(QLatin1String("/get")))
356 variant = PropGet;
357 else // Nothing falls back on /src or /dev.
358 continue;
359 name.chop(4);
360 } else {
361 variant = PropPut;
362 }
363 if (name.startsWith(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(3, 7, QLatin1String("HOST"));
372 properties.insert(ProKey(hname), value);
373 properties.insert(ProKey(hname + QLatin1String("/get")), value);
374 properties.insert(ProKey(hname + QLatin1String("/src")), value);
375 }
376 properties.insert(ProKey(name + QLatin1String("/raw")), value);
377 }
378 if (variant <= PropRaw)
379 properties.insert(ProKey(name + QLatin1String("/dev")), value);
380 } else if (!name.startsWith(QLatin1String("QT_HOST_"))) {
381 continue;
382 }
383 if (variant != PropRaw) {
384 if (variant < PropGet)
385 properties.insert(ProKey(name + QLatin1String("/get")), value);
386 properties.insert(ProKey(name + QLatin1String("/src")), value);
387 }
388 }
389 }
390 }
391 #endif // QT_BUILD_QMAKE
392
393 QT_END_NAMESPACE
394