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 QMAKEPARSER_H
30#define QMAKEPARSER_H
31
32#include "qmake_global.h"
33#include "qmakevfs.h"
34#include "proitems.h"
35
36#include <qhash.h>
37#include <qstack.h>
38#ifdef PROPARSER_THREAD_SAFE
39# include <qmutex.h>
40# include <qwaitcondition.h>
41#endif
42
43QT_BEGIN_NAMESPACE
44class QMAKE_EXPORT QMakeParserHandler
45{
46public:
47 enum {
48 CategoryMask = 0xf00,
49 InfoMessage = 0x100,
50 WarningMessage = 0x200,
51 ErrorMessage = 0x300,
52
53 SourceMask = 0xf0,
54 SourceParser = 0,
55
56 CodeMask = 0xf,
57 WarnLanguage = 0,
58 WarnDeprecated,
59
60 ParserWarnLanguage = SourceParser | WarningMessage | WarnLanguage,
61 ParserWarnDeprecated = SourceParser | WarningMessage | WarnDeprecated,
62
63 ParserIoError = ErrorMessage | SourceParser,
64 ParserError
65 };
66 virtual void message(int type, const QString &msg,
67 const QString &fileName = QString(), int lineNo = 0) = 0;
68};
69
70class ProFileCache;
71class QMakeVfs;
72
73class QMAKE_EXPORT QMakeParser
74{
75public:
76 // Call this from a concurrency-free context
77 static void initialize();
78
79 enum ParseFlag {
80 ParseDefault = 0,
81 ParseUseCache = 1,
82 ParseReportMissing = 4,
83#ifdef PROEVALUATOR_DUAL_VFS
84 ParseCumulative = 8
85#else
86 ParseCumulative = 0
87#endif
88 };
89 Q_DECLARE_FLAGS(ParseFlags, ParseFlag)
90
91 QMakeParser(ProFileCache *cache, QMakeVfs *vfs, QMakeParserHandler *handler);
92
93 enum SubGrammar { FullGrammar, TestGrammar, ValueGrammar };
94 // fileName is expected to be absolute and cleanPath()ed.
95 ProFile *parsedProFile(const QString &fileName, ParseFlags flags = ParseDefault);
96 ProFile *parsedProBlock(QStringView contents, int id, const QString &name, int line = 0,
97 SubGrammar grammar = FullGrammar);
98
99 void discardFileFromCache(int id);
100
101#ifdef PROPARSER_DEBUG
102 static QString formatProBlock(const QString &block);
103#endif
104
105private:
106 enum ScopeNesting {
107 NestNone = 0,
108 NestLoop = 1,
109 NestFunction = 2
110 };
111
112 struct BlockScope {
113 BlockScope() : start(nullptr), braceLevel(0), special(false), inBranch(false), nest(NestNone) {}
114 ushort *start; // Where this block started; store length here
115 int braceLevel; // Nesting of braces in scope
116 bool special; // Single-line conditionals inside loops, etc. cannot have else branches
117 bool inBranch; // The 'else' branch of the previous TokBranch is still open
118 uchar nest; // Into what control structures we are nested
119 };
120
121 enum ScopeState {
122 StNew, // Fresh scope
123 StCtrl, // Control statement (for or else) met on current line
124 StCond // Conditionals met on current line
125 };
126
127 enum Context { CtxTest, CtxValue, CtxPureValue, CtxArgs };
128 struct ParseCtx {
129 int parens; // Nesting of non-functional parentheses
130 int argc; // Number of arguments in current function call
131 int wordCount; // Number of words in current expression
132 Context context;
133 ushort quote; // Enclosing quote type
134 ushort terminator; // '}' if replace function call is braced, ':' if test function
135 };
136
137 bool readFile(int id, QMakeParser::ParseFlags flags, QString *contents);
138 void read(ProFile *pro, QStringView content, int line, SubGrammar grammar);
139
140 ALWAYS_INLINE void putTok(ushort *&tokPtr, ushort tok);
141 ALWAYS_INLINE void putBlockLen(ushort *&tokPtr, uint len);
142 ALWAYS_INLINE void putBlock(ushort *&tokPtr, const ushort *buf, uint len);
143 void putHashStr(ushort *&pTokPtr, const ushort *buf, uint len);
144 void finalizeHashStr(ushort *buf, uint len);
145 void putLineMarker(ushort *&tokPtr);
146 ALWAYS_INLINE bool resolveVariable(ushort *xprPtr, int tlen, int needSep, ushort **ptr,
147 ushort **buf, QString *xprBuff,
148 ushort **tokPtr, QString *tokBuff,
149 const ushort *cur, QStringView in);
150 void finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int wordCount);
151 void finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int argc);
152 void warnOperator(const char *msg);
153 bool failOperator(const char *msg);
154 bool acceptColon(const char *msg);
155 void putOperator(ushort *&tokPtr);
156 void finalizeTest(ushort *&tokPtr);
157 void bogusTest(ushort *&tokPtr, const QString &msg);
158 void enterScope(ushort *&tokPtr, bool special, ScopeState state);
159 void leaveScope(ushort *&tokPtr);
160 void flushCond(ushort *&tokPtr);
161 void flushScopes(ushort *&tokPtr);
162
163 void message(int type, const QString &msg) const;
164 void parseError(const QString &msg) const
165 {
166 message(QMakeParserHandler::ParserError, msg);
167 m_proFile->setOk(false);
168 }
169 void languageWarning(const QString &msg) const
170 { message(QMakeParserHandler::ParserWarnLanguage, msg); }
171 void deprecationWarning(const QString &msg) const
172 { message(QMakeParserHandler::ParserWarnDeprecated, msg); }
173
174 // Current location
175 ProFile *m_proFile;
176 int m_lineNo;
177
178 QStack<BlockScope> m_blockstack;
179 ScopeState m_state;
180 int m_markLine; // Put marker for this line
181 bool m_inError; // Current line had a parsing error; suppress followup error messages
182 bool m_canElse; // Conditionals met on previous line, but no scope was opened
183 int m_invert; // Pending conditional is negated
184 enum { NoOperator, AndOperator, OrOperator } m_operator; // Pending conditional is ORed/ANDed
185
186 QString m_tmp; // Temporary for efficient toQString
187
188 ProFileCache *m_cache;
189 QMakeParserHandler *m_handler;
190 QMakeVfs *m_vfs;
191
192 // This doesn't help gcc 3.3 ...
193 template<typename T> friend class QTypeInfo;
194
195 friend class ProFileCache;
196};
197
198Q_DECLARE_OPERATORS_FOR_FLAGS(QMakeParser::ParseFlags)
199
200class QMAKE_EXPORT ProFileCache
201{
202public:
203 ProFileCache();
204 ~ProFileCache();
205
206 void discardFile(int id);
207 void discardFile(const QString &fileName, QMakeVfs *vfs);
208 void discardFiles(const QString &prefix, QMakeVfs *vfs);
209
210private:
211 struct Entry {
212 ProFile *pro;
213#ifdef PROPARSER_THREAD_SAFE
214 struct Locker {
215 Locker() : waiters(0), done(false) {}
216 QWaitCondition cond;
217 int waiters;
218 bool done;
219 };
220 Locker *locker;
221#endif
222 };
223
224 QHash<int, Entry> parsed_files;
225#ifdef PROPARSER_THREAD_SAFE
226 QMutex mutex;
227#endif
228
229 friend class QMakeParser;
230};
231
232#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
233Q_DECLARE_TYPEINFO(QMakeParser::BlockScope, Q_MOVABLE_TYPE);
234Q_DECLARE_TYPEINFO(QMakeParser::Context, Q_PRIMITIVE_TYPE);
235#endif
236
237QT_END_NAMESPACE
238
239#endif // PROFILEPARSER_H
240