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 | |
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 | |
91 | QMakeGlobals::~QMakeGlobals() |
92 | { |
93 | qDeleteAll(c: baseEnvs); |
94 | } |
95 | |
96 | QString 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 | |
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(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 | |
186 | void QMakeGlobals::commitCommandLineArguments(QMakeCmdLineParserState &state) |
187 | { |
188 | if (!state.extraargs.isEmpty()) { |
189 | QString = 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 | |
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 | |
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: &pos); |
222 | commitCommandLineArguments(state); |
223 | useEnvironment(); |
224 | } |
225 | |
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(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 | |
247 | QString 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 | |
259 | 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(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 | |
272 | QString 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 | |
281 | QStringList QMakeGlobals::getPathListEnv(const QString &var) const |
282 | { |
283 | return splitPathList(val: getEnv(var)); |
284 | } |
285 | |
286 | QString 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 |
312 | bool 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 | |
335 | void 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 | |
393 | QT_END_NAMESPACE |
394 | |