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#ifndef PROITEMS_H
30#define PROITEMS_H
31
32#include "qmake_global.h"
33
34#include <qdebug.h>
35#include <qhash.h>
36#include <qlist.h>
37#include <qmap.h>
38#include <qstring.h>
39
40QT_BEGIN_NAMESPACE
41
42class QTextStream;
43
44#ifdef PROPARSER_THREAD_SAFE
45typedef QAtomicInt ProItemRefCount;
46#else
47class ProItemRefCount {
48public:
49 ProItemRefCount(int cnt = 0) : m_cnt(cnt) {}
50 bool ref() { return ++m_cnt != 0; }
51 bool deref() { return --m_cnt != 0; }
52 ProItemRefCount &operator=(int value) { m_cnt = value; return *this; }
53private:
54 int m_cnt;
55};
56#endif
57
58#ifndef QT_BUILD_QMAKE
59# define PROITEM_EXPLICIT explicit
60#else
61# define PROITEM_EXPLICIT
62#endif
63
64class ProKey;
65class ProStringList;
66class ProFile;
67
68class ProString {
69public:
70 ProString();
71 ProString(const ProString &other);
72 ProString &operator=(const ProString &) = default;
73 template<typename A, typename B>
74 ProString &operator=(const QStringBuilder<A, B> &str)
75 { return *this = QString(str); }
76 ProString(const QString &str);
77 PROITEM_EXPLICIT ProString(QStringView str);
78 PROITEM_EXPLICIT ProString(const char *str);
79 template<typename A, typename B>
80 ProString(const QStringBuilder<A, B> &str)
81 : ProString(QString(str))
82 {}
83 ProString(const QString &str, int offset, int length);
84 void setValue(const QString &str);
85 void clear() { m_string.clear(); m_length = 0; }
86 ProString &setSource(const ProString &other) { m_file = other.m_file; return *this; }
87 ProString &setSource(int id) { m_file = id; return *this; }
88 int sourceFile() const { return m_file; }
89
90 ProString &prepend(const ProString &other);
91 ProString &append(const ProString &other, bool *pending = nullptr);
92 ProString &append(const QString &other) { return append(ProString(other)); }
93 template<typename A, typename B>
94 ProString &append(const QStringBuilder<A, B> &other) { return append(QString(other)); }
95 ProString &append(const QLatin1String other);
96 ProString &append(const char *other) { return append(QLatin1String(other)); }
97 ProString &append(QChar other);
98 ProString &append(const ProStringList &other, bool *pending = nullptr, bool skipEmpty1st = false);
99 ProString &operator+=(const ProString &other) { return append(other); }
100 ProString &operator+=(const QString &other) { return append(other); }
101 template<typename A, typename B>
102 ProString &operator+=(const QStringBuilder<A, B> &other) { return append(QString(other)); }
103 ProString &operator+=(const QLatin1String other) { return append(other); }
104 ProString &operator+=(const char *other) { return append(other); }
105 ProString &operator+=(QChar other) { return append(other); }
106
107 void chop(int n) { Q_ASSERT(n <= m_length); m_length -= n; }
108 void chopFront(int n) { Q_ASSERT(n <= m_length); m_offset += n; m_length -= n; }
109
110 bool operator==(const ProString &other) const { return toQStringView() == other.toQStringView(); }
111 bool operator==(const QString &other) const { return toQStringView() == other; }
112 bool operator==(QStringView other) const { return toQStringView() == other; }
113 bool operator==(QLatin1String other) const { return toQStringView() == other; }
114 bool operator==(const char *other) const { return toQStringView() == QLatin1String(other); }
115 bool operator!=(const ProString &other) const { return !(*this == other); }
116 bool operator!=(const QString &other) const { return !(*this == other); }
117 bool operator!=(QLatin1String other) const { return !(*this == other); }
118 bool operator!=(const char *other) const { return !(*this == other); }
119 bool operator<(const ProString &other) const { return toQStringView() < other.toQStringView(); }
120 bool isNull() const { return m_string.isNull(); }
121 bool isEmpty() const { return !m_length; }
122 int length() const { return m_length; }
123 int size() const { return m_length; }
124 QChar at(int i) const { Q_ASSERT((uint)i < (uint)m_length); return constData()[i]; }
125 const QChar *constData() const { return m_string.constData() + m_offset; }
126 ProString mid(int off, int len = -1) const;
127 ProString left(int len) const { return mid(0, len); }
128 ProString right(int len) const { return mid(qMax(0, size() - len)); }
129 ProString trimmed() const;
130 int compare(const ProString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().compare(sub.toQStringView(), cs); }
131 int compare(const QString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().compare(sub, cs); }
132 int compare(const char *sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().compare(QLatin1String(sub), cs); }
133 bool startsWith(const ProString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().startsWith(sub.toQStringView(), cs); }
134 bool startsWith(const QString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().startsWith(sub, cs); }
135 bool startsWith(const char *sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().startsWith(QLatin1String(sub), cs); }
136 bool startsWith(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().startsWith(c, cs); }
137 template<typename A, typename B>
138 bool startsWith(const QStringBuilder<A, B> &str) { return startsWith(QString(str)); }
139 bool endsWith(const ProString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().endsWith(sub.toQStringView(), cs); }
140 bool endsWith(const QString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().endsWith(sub, cs); }
141 bool endsWith(const char *sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().endsWith(QLatin1String(sub), cs); }
142 template<typename A, typename B>
143 bool endsWith(const QStringBuilder<A, B> &str) { return endsWith(QString(str)); }
144 bool endsWith(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().endsWith(c, cs); }
145 int indexOf(const QString &s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().indexOf(s, from, cs); }
146 int indexOf(const char *s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().indexOf(QLatin1String(s), from, cs); }
147 int indexOf(QChar c, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().indexOf(c, from, cs); }
148 int lastIndexOf(const QString &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().lastIndexOf(s, from, cs); }
149 int lastIndexOf(const char *s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().lastIndexOf(QLatin1String(s), from, cs); }
150 int lastIndexOf(QChar c, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().lastIndexOf(c, from, cs); }
151 bool contains(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(s, 0, cs) >= 0; }
152 bool contains(const char *s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(QLatin1String(s), 0, cs) >= 0; }
153 bool contains(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(c, 0, cs) >= 0; }
154 qlonglong toLongLong(bool *ok = nullptr, int base = 10) const { return toQStringView().toLongLong(ok, base); }
155 int toInt(bool *ok = nullptr, int base = 10) const { return toQStringView().toInt(ok, base); }
156 short toShort(bool *ok = nullptr, int base = 10) const { return toQStringView().toShort(ok, base); }
157
158 size_t hash() const { return m_hash; }
159 static size_t hash(const QChar *p, int n);
160
161 ALWAYS_INLINE QStringView toQStringView() const { return QStringView(m_string).mid(m_offset, m_length); }
162
163 ALWAYS_INLINE ProKey &toKey() { return *(ProKey *)this; }
164 ALWAYS_INLINE const ProKey &toKey() const { return *(const ProKey *)this; }
165
166 QString toQString() const;
167 QString &toQString(QString &tmp) const;
168
169 QByteArray toLatin1() const { return toQStringView().toLatin1(); }
170
171private:
172 ProString(const ProKey &other);
173 ProString &operator=(const ProKey &other);
174
175 enum OmitPreHashing { NoHash };
176 ProString(const ProString &other, OmitPreHashing);
177
178 enum DoPreHashing { DoHash };
179 ALWAYS_INLINE ProString(const QString &str, DoPreHashing);
180 ALWAYS_INLINE ProString(const char *str, DoPreHashing);
181 ALWAYS_INLINE ProString(const QString &str, int offset, int length, DoPreHashing);
182 ALWAYS_INLINE ProString(const QString &str, int offset, int length, uint hash);
183
184 QString m_string;
185 int m_offset, m_length;
186 int m_file;
187 mutable size_t m_hash;
188 size_t updatedHash() const;
189 friend size_t qHash(const ProString &str);
190 friend QString operator+(const ProString &one, const ProString &two);
191 friend class ProKey;
192};
193Q_DECLARE_TYPEINFO(ProString, Q_MOVABLE_TYPE);
194
195
196class ProKey : public ProString {
197public:
198 ALWAYS_INLINE ProKey() : ProString() {}
199 explicit ProKey(const QString &str);
200 template<typename A, typename B>
201 ProKey(const QStringBuilder<A, B> &str)
202 : ProString(str)
203 {}
204 PROITEM_EXPLICIT ProKey(const char *str);
205 ProKey(const QString &str, int off, int len);
206 ProKey(const QString &str, int off, int len, uint hash);
207 void setValue(const QString &str);
208
209#ifdef Q_CC_MSVC
210 // Workaround strange MSVC behaviour when exporting classes with ProKey members.
211 ALWAYS_INLINE ProKey(const ProKey &other) : ProString(other.toString()) {}
212 ALWAYS_INLINE ProKey &operator=(const ProKey &other)
213 {
214 toString() = other.toString();
215 return *this;
216 }
217#endif
218
219 ALWAYS_INLINE ProString &toString() { return *(ProString *)this; }
220 ALWAYS_INLINE const ProString &toString() const { return *(const ProString *)this; }
221
222private:
223 ProKey(const ProString &other);
224};
225Q_DECLARE_TYPEINFO(ProKey, Q_MOVABLE_TYPE);
226
227template <> struct QConcatenable<ProString> : private QAbstractConcatenable
228{
229 typedef ProString type;
230 typedef QString ConvertTo;
231 enum { ExactSize = true };
232 static int size(const ProString &a) { return a.length(); }
233 static inline void appendTo(const ProString &a, QChar *&out)
234 {
235 const auto n = a.size();
236 memcpy(out, a.toQStringView().data(), sizeof(QChar) * n);
237 out += n;
238 }
239};
240
241template <> struct QConcatenable<ProKey> : private QAbstractConcatenable
242{
243 typedef ProKey type;
244 typedef QString ConvertTo;
245 enum { ExactSize = true };
246 static int size(const ProKey &a) { return a.length(); }
247 static inline void appendTo(const ProKey &a, QChar *&out)
248 {
249 const auto n = a.size();
250 memcpy(out, a.toQStringView().data(), sizeof(QChar) * n);
251 out += n;
252 }
253};
254
255
256size_t qHash(const ProString &str);
257
258inline QString &operator+=(QString &that, const ProString &other)
259 { return that += other.toQStringView(); }
260
261QTextStream &operator<<(QTextStream &t, const ProString &str);
262template<typename A, typename B>
263QTextStream &operator<<(QTextStream &t, const QStringBuilder<A, B> &str) { return t << QString(str); }
264
265// This class manages read-only access to a ProString via a raw data QString
266// temporary, ensuring that the latter is accessed exclusively.
267class ProStringRoUser
268{
269public:
270 ProStringRoUser(QString &rs)
271 {
272 m_rs = &rs;
273 }
274 ProStringRoUser(const ProString &ps, QString &rs)
275 : ProStringRoUser(rs)
276 {
277 ps.toQString(rs);
278 }
279 // No destructor, as a RAII pattern cannot be used: references to the
280 // temporary string can legitimately outlive instances of this class
281 // (if they are held by Qt, e.g. in QRegExp).
282 QString &set(const ProString &ps) { return ps.toQString(*m_rs); }
283 QString &str() { return *m_rs; }
284
285protected:
286 QString *m_rs;
287};
288
289// This class manages read-write access to a ProString via a raw data QString
290// temporary, ensuring that the latter is accessed exclusively, and that raw
291// data does not leak outside its source's refcounting.
292class ProStringRwUser : public ProStringRoUser
293{
294public:
295 ProStringRwUser(QString &rs)
296 : ProStringRoUser(rs), m_ps(nullptr) {}
297 ProStringRwUser(const ProString &ps, QString &rs)
298 : ProStringRoUser(ps, rs), m_ps(&ps) {}
299 QString &set(const ProString &ps) { m_ps = &ps; return ProStringRoUser::set(ps); }
300 ProString extract(const QString &s) const
301 { return s.isSharedWith(*m_rs) ? *m_ps : ProString(s).setSource(*m_ps); }
302 ProString extract(const QString &s, const ProStringRwUser &other) const
303 {
304 if (other.m_ps && s.isSharedWith(*other.m_rs))
305 return *other.m_ps;
306 return extract(s);
307 }
308
309private:
310 const ProString *m_ps;
311};
312
313class ProStringList : public QList<ProString> {
314public:
315 ProStringList() {}
316 ProStringList(const ProString &str) { *this << str; }
317 explicit ProStringList(const QStringList &list);
318 QStringList toQStringList() const;
319
320 ProStringList &operator<<(const ProString &str)
321 { QList<ProString>::operator<<(str); return *this; }
322
323 int length() const { return size(); }
324
325 QString join(const ProString &sep) const;
326 QString join(const QString &sep) const;
327 QString join(QChar sep) const;
328 template<typename A, typename B>
329 QString join(const QStringBuilder<A, B> &str) { return join(QString(str)); }
330
331 void insertUnique(const ProStringList &value);
332
333 void removeAll(const ProString &str);
334 void removeAll(const char *str);
335 void removeEach(const ProStringList &value);
336 void removeAt(int idx) { remove(idx); }
337 void removeEmpty();
338 void removeDuplicates();
339
340 bool contains(const ProString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
341 bool contains(QStringView str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
342 bool contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
343 { return contains(ProString(str), cs); }
344 bool contains(const char *str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
345};
346Q_DECLARE_TYPEINFO(ProStringList, Q_MOVABLE_TYPE);
347
348inline ProStringList operator+(const ProStringList &one, const ProStringList &two)
349 { ProStringList ret = one; ret += two; return ret; }
350
351typedef QMap<ProKey, ProStringList> ProValueMap;
352
353// These token definitions affect both ProFileEvaluator and ProWriter
354enum ProToken {
355 TokTerminator = 0, // end of stream (possibly not included in length; must be zero)
356 TokLine, // line marker:
357 // - line (1)
358 TokAssign, // variable =
359 TokAppend, // variable +=
360 TokAppendUnique, // variable *=
361 TokRemove, // variable -=
362 TokReplace, // variable ~=
363 // previous literal/expansion is a variable manipulation
364 // - lower bound for expected output length (1)
365 // - value expression + TokValueTerminator
366 TokValueTerminator, // assignment value terminator
367 TokLiteral, // literal string (fully dequoted)
368 // - length (1)
369 // - string data (length; unterminated)
370 TokHashLiteral, // literal string with hash (fully dequoted)
371 // - hash (2)
372 // - length (1)
373 // - string data (length; unterminated)
374 TokVariable, // qmake variable expansion
375 // - hash (2)
376 // - name length (1)
377 // - name (name length; unterminated)
378 TokProperty, // qmake property expansion
379 // - hash (2)
380 // - name length (1)
381 // - name (name length; unterminated)
382 TokEnvVar, // environment variable expansion
383 // - name length (1)
384 // - name (name length; unterminated)
385 TokFuncName, // replace function expansion
386 // - hash (2)
387 // - name length (1)
388 // - name (name length; unterminated)
389 // - ((nested expansion + TokArgSeparator)* + nested expansion)?
390 // - TokFuncTerminator
391 TokArgSeparator, // function argument separator
392 TokFuncTerminator, // function argument list terminator
393 TokCondition, // previous literal/expansion is a conditional
394 TokTestCall, // previous literal/expansion is a test function call
395 // - ((nested expansion + TokArgSeparator)* + nested expansion)?
396 // - TokFuncTerminator
397 TokReturn, // previous literal/expansion is a return value
398 TokBreak, // break loop
399 TokNext, // shortcut to next loop iteration
400 TokNot, // '!' operator
401 TokAnd, // ':' operator
402 TokOr, // '|' operator
403 TokBranch, // branch point:
404 // - then block length (2)
405 // - then block + TokTerminator (then block length)
406 // - else block length (2)
407 // - else block + TokTerminator (else block length)
408 TokForLoop, // for loop:
409 // - variable name: hash (2), length (1), chars (length)
410 // - expression: length (2), bytes + TokValueTerminator (length)
411 // - body length (2)
412 // - body + TokTerminator (body length)
413 TokTestDef, // test function definition:
414 TokReplaceDef, // replace function definition:
415 // - function name: hash (2), length (1), chars (length)
416 // - body length (2)
417 // - body + TokTerminator (body length)
418 TokBypassNesting, // escape from function local variable scopes:
419 // - block length (2)
420 // - block + TokTerminator (block length)
421 TokMask = 0xff,
422 TokQuoted = 0x100, // The expression is quoted => join expanded stringlist
423 TokNewStr = 0x200 // Next stringlist element
424};
425
426class QMAKE_EXPORT ProFile
427{
428public:
429 ProFile(int id, const QString &fileName);
430 ~ProFile();
431
432 int id() const { return m_id; }
433 QString fileName() const { return m_fileName; }
434 QString directoryName() const { return m_directoryName; }
435 const QString &items() const { return m_proitems; }
436 QString *itemsRef() { return &m_proitems; }
437 const ushort *tokPtr() const { return (const ushort *)m_proitems.constData(); }
438 const ushort *tokPtrEnd() const { return (const ushort *)m_proitems.constData() + m_proitems.size(); }
439
440 void ref() { m_refCount.ref(); }
441 void deref() { if (!m_refCount.deref()) delete this; }
442
443 bool isOk() const { return m_ok; }
444 void setOk(bool ok) { m_ok = ok; }
445
446 bool isHostBuild() const { return m_hostBuild; }
447 void setHostBuild(bool host_build) { m_hostBuild = host_build; }
448
449 ProString getStr(const ushort *&tPtr);
450 ProKey getHashStr(const ushort *&tPtr);
451
452private:
453 ProItemRefCount m_refCount;
454 QString m_proitems;
455 QString m_fileName;
456 QString m_directoryName;
457 int m_id;
458 bool m_ok;
459 bool m_hostBuild;
460};
461
462class ProFunctionDef {
463public:
464 ProFunctionDef(ProFile *pro, int offset) : m_pro(pro), m_offset(offset) { m_pro->ref(); }
465 ProFunctionDef(const ProFunctionDef &o) : m_pro(o.m_pro), m_offset(o.m_offset) { m_pro->ref(); }
466 ProFunctionDef(ProFunctionDef &&other) noexcept
467 : m_pro(other.m_pro), m_offset(other.m_offset) { other.m_pro = nullptr; }
468 ~ProFunctionDef() { if (m_pro) m_pro->deref(); }
469 ProFunctionDef &operator=(const ProFunctionDef &o)
470 {
471 if (this != &o) {
472 if (m_pro)
473 m_pro->deref();
474 m_pro = o.m_pro;
475 m_pro->ref();
476 m_offset = o.m_offset;
477 }
478 return *this;
479 }
480 ProFunctionDef &operator=(ProFunctionDef &&other) noexcept
481 {
482 ProFunctionDef moved(std::move(other));
483 swap(moved);
484 return *this;
485 }
486 void swap(ProFunctionDef &other) noexcept
487 {
488 qSwap(m_pro, other.m_pro);
489 qSwap(m_offset, other.m_offset);
490 }
491
492 ProFile *pro() const { return m_pro; }
493 const ushort *tokPtr() const { return m_pro->tokPtr() + m_offset; }
494private:
495 ProFile *m_pro;
496 int m_offset;
497};
498
499Q_DECLARE_TYPEINFO(ProFunctionDef, Q_MOVABLE_TYPE);
500
501struct ProFunctionDefs {
502 QHash<ProKey, ProFunctionDef> testFunctions;
503 QHash<ProKey, ProFunctionDef> replaceFunctions;
504};
505
506QDebug operator<<(QDebug debug, const ProString &str);
507
508QT_END_NAMESPACE
509
510#endif // PROITEMS_H
511