source: trunk/tools/porting/src/preprocessorcontrol.cpp@ 348

Last change on this file since 348 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 14.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the qt3to4 porting application of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "preprocessorcontrol.h"
43#include <QDir>
44#include <QFile>
45#include <QFileInfo>
46#include <QTemporaryFile>
47
48QT_BEGIN_NAMESPACE
49using namespace TokenEngine;
50using namespace Rpp;
51
52IncludeFiles::IncludeFiles(const QString &basePath, const QStringList &searchPaths)
53:m_basePath(basePath)
54{
55 //prepend basePath to all relative paths in searchPaths
56 foreach (QString path, searchPaths) {
57 QString finalPath;
58 if (QDir::isAbsolutePath(path))
59 finalPath = QDir::cleanPath(path);
60 else
61 finalPath = QDir::cleanPath(m_basePath + QLatin1String("/") + path);
62
63 if(QFile::exists(finalPath))
64 m_searchPaths.append(finalPath);
65 }
66}
67
68/*
69 Performs an #include "..." style file lookup.
70 Aboslute filenames are checked directly. Relative filenames are first
71 looked for relative to the current file path, then the includepaths
72 are searched if not found.
73*/
74QString IncludeFiles::quoteLookup(const QString &currentFile,
75 const QString &includeFile) const
76{
77 //if includeFile is absolute, check if it exists
78 if (QDir::isAbsolutePath(includeFile)) {
79 if(QFile::exists(includeFile))
80 return includeFile;
81 else
82 return QString();
83 }
84
85 //If currentFile is not an absolute file path, make it one by
86 //prepending m_baspath
87 QString currentFilePath;
88 if(QDir::isAbsolutePath(currentFile))
89 currentFilePath = currentFile;
90 else
91 currentFilePath = QDir::cleanPath(m_basePath + QLatin1String("/") + currentFile);
92
93 //Check if it includeFile exists in the same dir as currentFilePath
94 const QString currentPath = QFileInfo(currentFilePath).path();
95 QString localFile = QDir::cleanPath(currentPath + QLatin1String("/") + includeFile);
96 if(QFile::exists(localFile))
97 return localFile;
98
99 return searchIncludePaths(includeFile);
100}
101
102/*
103 Performs an #include <...> style file lookup.
104 Aboslute filenames are checked directly.
105 Relative paths are searched for in the includepaths.
106*/
107QString IncludeFiles::angleBracketLookup(const QString &includeFile) const
108{
109 //if includeFile is absolute, check if it exists
110 if (QDir::isAbsolutePath(includeFile)) {
111 if(QFile::exists(includeFile))
112 return includeFile;
113 else
114 return QString();
115 }
116
117 return searchIncludePaths(includeFile);
118}
119
120QString IncludeFiles::resolve(const QString &filename) const
121{
122 if(QDir::isAbsolutePath(filename))
123 return filename;
124
125 QString prepended = QDir::cleanPath(m_basePath + QLatin1String("/") + filename);
126 if(QFile::exists(prepended))
127 return prepended;
128 else
129 return QString();
130}
131
132
133/*
134 Searches for includeFile paths by appending it to all includePaths
135 and checking if the file exists. Returns QString() if the file is not
136 found.
137*/
138QString IncludeFiles::searchIncludePaths(const QString &includeFile) const
139{
140 QString foundFile;
141 foreach(QString includePath, m_searchPaths) {
142 QString testFile = includePath + QLatin1String("/") + includeFile;
143 if(QFile::exists(testFile)){
144 foundFile = testFile;
145 break;
146 }
147 }
148 return foundFile;
149}
150
151QByteArray PreprocessorCache::readFile(const QString &filename) const
152{
153 // If anybody is connected to the readFile signal we tell them to
154 // read the file for us.
155 if (receivers(SIGNAL(readFile(QByteArray&,QString))) > 0) {
156 QByteArray array;
157 // Workaround for "not beeing able to emit from const function"
158 PreprocessorCache *cache = const_cast<PreprocessorCache *>(this);
159 emit cache->readFile(array, filename);
160 return array;
161 }
162
163 QFile f(filename);
164 if (!f.exists())
165 return QByteArray();
166 f.open(QIODevice::ReadOnly);
167 if (!f.isOpen())
168 return QByteArray();
169 return f.readAll();
170}
171
172PreprocessorCache::PreprocessorCache()
173{
174 connect(&m_preprocessor, SIGNAL(error(QString,QString)),
175 this, SIGNAL(error(QString,QString)));
176}
177
178
179/*
180 Return a TokenSequence with the contents of filname.
181 Assumens filename exists and is readable, returns a empty
182 TokenSequence if not.
183
184 The result is cached.
185*/
186TokenContainer PreprocessorCache::sourceTokens(const QString &filename)
187{
188 // Check if the source tokens are already in the cache.
189 if(m_sourceTokens.contains(filename))
190 return m_sourceTokens.value(filename);
191
192 // Read and tokenize file.
193 QByteArray fileContents = readFile(filename);
194 if(fileContents == QByteArray())
195 return TokenContainer();
196
197 QVector<TokenEngine::Token> tokenList = m_tokenizer.tokenize(fileContents);
198
199 // Create a FileInfo object that holds the filename for this container.
200 FileInfo *containterFileInfo = new FileInfo;
201 containterFileInfo->filename = filename;
202
203 // Create container.
204 TokenContainer tokenContainer(fileContents, tokenList, containterFileInfo);
205
206 // Insert into cache.
207 m_sourceTokens.insert(filename, tokenContainer);
208 return tokenContainer;
209}
210
211/*
212 Return a Source* tree representing the contents of filename.
213 Assumens filename exists and is readable, returns a empty
214 Source object if not.
215
216 The result is cached.
217*/
218Source *PreprocessorCache::sourceTree(const QString &filename)
219{
220 // Check if the Rpp tree for this file is already in the cache.
221 if(m_sourceTrees.contains(filename))
222 return m_sourceTrees.value(filename);
223
224 // Get the tokens for the contents of this file.
225 TokenContainer tokenContainer = sourceTokens(filename);
226
227 // Run lexer and the preprocessor-parser.
228 QVector<Type> tokenTypes = m_lexer.lex(tokenContainer);
229 Source *source = m_preprocessor.parse(tokenContainer, tokenTypes, &m_memoryPool);
230 source->setFileName(filename);
231
232 // Insert into cache.
233 if(tokenContainer.count() > 0) //don't cache empty files.
234 m_sourceTrees.insert(filename, source);
235
236 return source;
237}
238
239
240/*
241 Returns whether the cache contains a TokenContainer for the given filename.
242*/
243bool PreprocessorCache::containsSourceTokens(const QString &filename)
244{
245 return m_sourceTokens.contains(filename);
246}
247
248/*
249 Returns whether the cache contains a Preprocessor tree for the given filename.
250*/
251bool PreprocessorCache::containsSourceTree(const QString &filename)
252{
253 return m_sourceTrees.contains(filename);
254}
255
256PreprocessorController::PreprocessorController(IncludeFiles includeFiles,
257 PreprocessorCache &preprocessorCache,
258 QStringList preLoadFilesFilenames)
259:m_includeFiles(includeFiles),
260 m_preprocessorCache(preprocessorCache)
261 {
262 // Load qt3 headers from resources. The headers are stored as
263 // QHash<QString, QByteArray>, serialized using QDataStream. The hash
264 // maps filename -> contents.
265 if (preLoadFilesFilenames != QStringList()) {
266 foreach (QString filename, preLoadFilesFilenames) {
267 QFile f(filename);
268 if (f.open(QIODevice::ReadOnly)) {
269 QByteArray buffer = f.readAll();
270 f.close();
271 QDataStream stream(buffer);
272 QHash<QString, QByteArray> files;
273 stream >> files;
274 m_preLoadFiles.unite(files);
275 }
276 }
277 }
278
279 //connect include callback
280 connect(&m_rppTreeEvaluator,
281 SIGNAL(includeCallback(Rpp::Source *&, const Rpp::Source *,
282 const QString &, Rpp::RppTreeEvaluator::IncludeType)),
283 SLOT(includeSlot(Rpp::Source *&, const Rpp::Source *,
284 const QString &, Rpp::RppTreeEvaluator::IncludeType)));
285
286 // connect readFile callback
287 connect(&m_preprocessorCache, SIGNAL(readFile(QByteArray&,QString)),
288 SLOT(readFile(QByteArray&,QString)));
289
290 //connect error handlers
291 connect(&m_preprocessorCache , SIGNAL(error(QString,QString)),
292 this, SIGNAL(error(QString,QString)));
293}
294
295/*
296 Callback from RppTreeEvaluator, called when we evaluate an #include
297 directive. We do a filename lookup based on the type of include, and then ask
298 the cache to give us the source tree for that file.
299*/
300void PreprocessorController::includeSlot(Source *&includee,
301 const Source *includer,
302 const QString &filename,
303 RppTreeEvaluator::IncludeType includeType)
304{
305 QString newFilename;
306 if(includeType == RppTreeEvaluator::QuoteInclude)
307 newFilename = m_includeFiles.quoteLookup(includer->fileName(), filename);
308 else //AngleBracketInclude
309 newFilename = m_includeFiles.angleBracketLookup(filename);
310
311 if (QFile::exists(newFilename)) {
312 includee = m_preprocessorCache.sourceTree(newFilename);
313 return;
314 }
315
316 if (m_preLoadFiles.contains(filename)) {
317 includee = m_preprocessorCache.sourceTree(filename);
318 return;
319 }
320
321 includee = m_preprocessorCache.sourceTree(newFilename);
322 emit error(QLatin1String("Error"), QLatin1String("Could not find file ") + filename);
323}
324
325/*
326 Callback connected to preprocessorCache. Tries to load a file from
327 m_preLoadFiles before going to disk.
328*/
329void PreprocessorController::readFile(QByteArray &contents, QString filename)
330{
331 if (m_preLoadFiles.contains(filename)) {
332 contents = m_preLoadFiles.value(filename);
333 return;
334 }
335
336 QFile f(filename);
337 if (!f.exists())
338 return;
339 f.open(QIODevice::ReadOnly);
340 if (!f.isOpen())
341 return;
342 contents = f.readAll();
343}
344
345/*
346 Preprocess file give by filename. Filename is resloved agains the basepath
347 set in IncludeFiles.
348*/
349TokenSectionSequence PreprocessorController::evaluate(const QString &filename, Rpp::DefineMap *activedefinitions)
350{
351 QString resolvedFilename = m_includeFiles.resolve(filename);
352 if(!QFile::exists(resolvedFilename))
353 emit error(QLatin1String("Error"), QLatin1String("Could not find file: ") + filename);
354 Source *source = m_preprocessorCache.sourceTree(resolvedFilename);
355
356 return m_rppTreeEvaluator.evaluate(source, activedefinitions);
357}
358
359QByteArray defaultDefines =
360 "#define __attribute__(a...) \n \
361 #define __attribute__ \n \
362 #define __extension \n \
363 #define __extension__ \n \
364 #define __restrict \n \
365 #define __restrict__ \n \
366 #define __volatile volatile\n \
367 #define __volatile__ volatile\n \
368 #define __inline inline\n \
369 #define __inline__ inline\n \
370 #define __const const\n \
371 #define __const__ const\n \
372 #define __asm asm\n \
373 #define __asm__ asm\n \
374 #define __GNUC__ 2\n \
375 #define __GNUC_MINOR__ 95\n \
376 #define __cplusplus \n \
377 #define __linux__ \n";
378
379
380/*
381 Returns a DefineMap containing the above macro definitions. The DefineMap
382 will contain pointers to data stored in the provided cache object.
383*/
384Rpp::DefineMap *defaultMacros(PreprocessorCache &cache)
385{
386 DefineMap *defineMap = new DefineMap();
387 //write out default macros to a temp file
388 QTemporaryFile tempfile;
389 tempfile.open();
390 tempfile.write(defaultDefines);
391 tempfile.flush();
392
393 IncludeFiles *includeFiles = new IncludeFiles(QString(), QStringList());
394 PreprocessorController preprocessorController(*includeFiles, cache);
395 //evaluate default macro file.
396 preprocessorController.evaluate(tempfile.fileName(), defineMap);
397 delete includeFiles;
398 return defineMap;
399}
400
401void StandardOutErrorHandler::error(QString type, QString text)
402{
403 Q_UNUSED(type);
404 puts(qPrintable(text));
405}
406
407/*
408 RppPreprocessor is a convenience class that contains all the components
409 needed to preprocess files. Error messages are printed to standard out.
410*/
411RppPreprocessor::RppPreprocessor(QString basePath, QStringList includePaths, QStringList preLoadFilesFilenames)
412:m_includeFiles(basePath, includePaths)
413,m_activeDefinitions(defaultMacros(m_cache))
414,m_controller(m_includeFiles, m_cache, preLoadFilesFilenames)
415{
416 QObject::connect(&m_controller, SIGNAL(error(QString,QString)), &m_errorHandler, SLOT(error(QString,QString)));
417}
418
419RppPreprocessor::~RppPreprocessor()
420{
421 delete m_activeDefinitions;
422}
423
424TokenEngine::TokenSectionSequence RppPreprocessor::evaluate(const QString &filename)
425{
426 DefineMap defMap = *m_activeDefinitions;
427 return m_controller.evaluate(filename, &defMap);
428}
429
430QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.