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 | |