~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to tools/porting/src/preprocessorcontrol.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

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