tools/porting/src/preprocessorcontrol.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the qt3to4 porting application of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "preprocessorcontrol.h"
       
    43 #include <QDir>
       
    44 #include <QFile>
       
    45 #include <QFileInfo>
       
    46 #include <QTemporaryFile>
       
    47 
       
    48 QT_BEGIN_NAMESPACE
       
    49 using namespace TokenEngine;
       
    50 using namespace Rpp;
       
    51 
       
    52 IncludeFiles::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 */
       
    74 QString 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 */
       
   107 QString 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 
       
   120 QString 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 */
       
   138 QString 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 
       
   151 QByteArray 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 
       
   172 PreprocessorCache::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 */
       
   186 TokenContainer 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 */
       
   218 Source *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 */
       
   243 bool 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 */
       
   251 bool PreprocessorCache::containsSourceTree(const QString &filename)
       
   252 {
       
   253     return m_sourceTrees.contains(filename);
       
   254 }
       
   255 
       
   256 PreprocessorController::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 */
       
   300 void 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 */
       
   329 void 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 */
       
   349 TokenSectionSequence 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 
       
   359 QByteArray 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 */
       
   384 Rpp::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 
       
   401 void 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 */
       
   411 RppPreprocessor::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 
       
   419 RppPreprocessor::~RppPreprocessor()
       
   420 {
       
   421     delete m_activeDefinitions;
       
   422 }
       
   423 
       
   424 TokenEngine::TokenSectionSequence RppPreprocessor::evaluate(const QString &filename)
       
   425 {
       
   426     DefineMap defMap = *m_activeDefinitions;
       
   427     return m_controller.evaluate(filename, &defMap);
       
   428 }
       
   429 
       
   430 QT_END_NAMESPACE