summaryrefslogtreecommitdiffstats
path: root/tools/porting/src/preprocessorcontrol.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/porting/src/preprocessorcontrol.cpp')
-rw-r--r--tools/porting/src/preprocessorcontrol.cpp430
1 files changed, 430 insertions, 0 deletions
diff --git a/tools/porting/src/preprocessorcontrol.cpp b/tools/porting/src/preprocessorcontrol.cpp
new file mode 100644
index 0000000..4bfe197
--- /dev/null
+++ b/tools/porting/src/preprocessorcontrol.cpp
@@ -0,0 +1,430 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the qt3to4 porting application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "preprocessorcontrol.h"
+#include <QDir>
+#include <QFile>
+#include <QFileInfo>
+#include <QTemporaryFile>
+
+QT_BEGIN_NAMESPACE
+using namespace TokenEngine;
+using namespace Rpp;
+
+IncludeFiles::IncludeFiles(const QString &basePath, const QStringList &searchPaths)
+:m_basePath(basePath)
+{
+ //prepend basePath to all relative paths in searchPaths
+ foreach (QString path, searchPaths) {
+ QString finalPath;
+ if (QDir::isAbsolutePath(path))
+ finalPath = QDir::cleanPath(path);
+ else
+ finalPath = QDir::cleanPath(m_basePath + QLatin1String("/") + path);
+
+ if(QFile::exists(finalPath))
+ m_searchPaths.append(finalPath);
+ }
+}
+
+/*
+ Performs an #include "..." style file lookup.
+ Aboslute filenames are checked directly. Relative filenames are first
+ looked for relative to the current file path, then the includepaths
+ are searched if not found.
+*/
+QString IncludeFiles::quoteLookup(const QString &currentFile,
+ const QString &includeFile) const
+{
+ //if includeFile is absolute, check if it exists
+ if (QDir::isAbsolutePath(includeFile)) {
+ if(QFile::exists(includeFile))
+ return includeFile;
+ else
+ return QString();
+ }
+
+ //If currentFile is not an absolute file path, make it one by
+ //prepending m_baspath
+ QString currentFilePath;
+ if(QDir::isAbsolutePath(currentFile))
+ currentFilePath = currentFile;
+ else
+ currentFilePath = QDir::cleanPath(m_basePath + QLatin1String("/") + currentFile);
+
+ //Check if it includeFile exists in the same dir as currentFilePath
+ const QString currentPath = QFileInfo(currentFilePath).path();
+ QString localFile = QDir::cleanPath(currentPath + QLatin1String("/") + includeFile);
+ if(QFile::exists(localFile))
+ return localFile;
+
+ return searchIncludePaths(includeFile);
+}
+
+/*
+ Performs an #include <...> style file lookup.
+ Aboslute filenames are checked directly.
+ Relative paths are searched for in the includepaths.
+*/
+QString IncludeFiles::angleBracketLookup(const QString &includeFile) const
+{
+ //if includeFile is absolute, check if it exists
+ if (QDir::isAbsolutePath(includeFile)) {
+ if(QFile::exists(includeFile))
+ return includeFile;
+ else
+ return QString();
+ }
+
+ return searchIncludePaths(includeFile);
+}
+
+QString IncludeFiles::resolve(const QString &filename) const
+{
+ if(QDir::isAbsolutePath(filename))
+ return filename;
+
+ QString prepended = QDir::cleanPath(m_basePath + QLatin1String("/") + filename);
+ if(QFile::exists(prepended))
+ return prepended;
+ else
+ return QString();
+}
+
+
+/*
+ Searches for includeFile paths by appending it to all includePaths
+ and checking if the file exists. Returns QString() if the file is not
+ found.
+*/
+QString IncludeFiles::searchIncludePaths(const QString &includeFile) const
+{
+ QString foundFile;
+ foreach(QString includePath, m_searchPaths) {
+ QString testFile = includePath + QLatin1String("/") + includeFile;
+ if(QFile::exists(testFile)){
+ foundFile = testFile;
+ break;
+ }
+ }
+ return foundFile;
+}
+
+QByteArray PreprocessorCache::readFile(const QString &filename) const
+{
+ // If anybody is connected to the readFile signal we tell them to
+ // read the file for us.
+ if (receivers(SIGNAL(readFile(QByteArray&,QString))) > 0) {
+ QByteArray array;
+ // Workaround for "not beeing able to emit from const function"
+ PreprocessorCache *cache = const_cast<PreprocessorCache *>(this);
+ emit cache->readFile(array, filename);
+ return array;
+ }
+
+ QFile f(filename);
+ if (!f.exists())
+ return QByteArray();
+ f.open(QIODevice::ReadOnly);
+ if (!f.isOpen())
+ return QByteArray();
+ return f.readAll();
+}
+
+PreprocessorCache::PreprocessorCache()
+{
+ connect(&m_preprocessor, SIGNAL(error(QString,QString)),
+ this, SIGNAL(error(QString,QString)));
+}
+
+
+/*
+ Return a TokenSequence with the contents of filname.
+ Assumens filename exists and is readable, returns a empty
+ TokenSequence if not.
+
+ The result is cached.
+*/
+TokenContainer PreprocessorCache::sourceTokens(const QString &filename)
+{
+ // Check if the source tokens are already in the cache.
+ if(m_sourceTokens.contains(filename))
+ return m_sourceTokens.value(filename);
+
+ // Read and tokenize file.
+ QByteArray fileContents = readFile(filename);
+ if(fileContents == QByteArray())
+ return TokenContainer();
+
+ QVector<TokenEngine::Token> tokenList = m_tokenizer.tokenize(fileContents);
+
+ // Create a FileInfo object that holds the filename for this container.
+ FileInfo *containterFileInfo = new FileInfo;
+ containterFileInfo->filename = filename;
+
+ // Create container.
+ TokenContainer tokenContainer(fileContents, tokenList, containterFileInfo);
+
+ // Insert into cache.
+ m_sourceTokens.insert(filename, tokenContainer);
+ return tokenContainer;
+}
+
+/*
+ Return a Source* tree representing the contents of filename.
+ Assumens filename exists and is readable, returns a empty
+ Source object if not.
+
+ The result is cached.
+*/
+Source *PreprocessorCache::sourceTree(const QString &filename)
+{
+ // Check if the Rpp tree for this file is already in the cache.
+ if(m_sourceTrees.contains(filename))
+ return m_sourceTrees.value(filename);
+
+ // Get the tokens for the contents of this file.
+ TokenContainer tokenContainer = sourceTokens(filename);
+
+ // Run lexer and the preprocessor-parser.
+ QVector<Type> tokenTypes = m_lexer.lex(tokenContainer);
+ Source *source = m_preprocessor.parse(tokenContainer, tokenTypes, &m_memoryPool);
+ source->setFileName(filename);
+
+ // Insert into cache.
+ if(tokenContainer.count() > 0) //don't cache empty files.
+ m_sourceTrees.insert(filename, source);
+
+ return source;
+}
+
+
+/*
+ Returns whether the cache contains a TokenContainer for the given filename.
+*/
+bool PreprocessorCache::containsSourceTokens(const QString &filename)
+{
+ return m_sourceTokens.contains(filename);
+}
+
+/*
+ Returns whether the cache contains a Preprocessor tree for the given filename.
+*/
+bool PreprocessorCache::containsSourceTree(const QString &filename)
+{
+ return m_sourceTrees.contains(filename);
+}
+
+PreprocessorController::PreprocessorController(IncludeFiles includeFiles,
+ PreprocessorCache &preprocessorCache,
+ QStringList preLoadFilesFilenames)
+:m_includeFiles(includeFiles),
+ m_preprocessorCache(preprocessorCache)
+ {
+ // Load qt3 headers from resources. The headers are stored as
+ // QHash<QString, QByteArray>, serialized using QDataStream. The hash
+ // maps filename -> contents.
+ if (preLoadFilesFilenames != QStringList()) {
+ foreach (QString filename, preLoadFilesFilenames) {
+ QFile f(filename);
+ if (f.open(QIODevice::ReadOnly)) {
+ QByteArray buffer = f.readAll();
+ f.close();
+ QDataStream stream(buffer);
+ QHash<QString, QByteArray> files;
+ stream >> files;
+ m_preLoadFiles.unite(files);
+ }
+ }
+ }
+
+ //connect include callback
+ connect(&m_rppTreeEvaluator,
+ SIGNAL(includeCallback(Rpp::Source *&, const Rpp::Source *,
+ const QString &, Rpp::RppTreeEvaluator::IncludeType)),
+ SLOT(includeSlot(Rpp::Source *&, const Rpp::Source *,
+ const QString &, Rpp::RppTreeEvaluator::IncludeType)));
+
+ // connect readFile callback
+ connect(&m_preprocessorCache, SIGNAL(readFile(QByteArray&,QString)),
+ SLOT(readFile(QByteArray&,QString)));
+
+ //connect error handlers
+ connect(&m_preprocessorCache , SIGNAL(error(QString,QString)),
+ this, SIGNAL(error(QString,QString)));
+}
+
+/*
+ Callback from RppTreeEvaluator, called when we evaluate an #include
+ directive. We do a filename lookup based on the type of include, and then ask
+ the cache to give us the source tree for that file.
+*/
+void PreprocessorController::includeSlot(Source *&includee,
+ const Source *includer,
+ const QString &filename,
+ RppTreeEvaluator::IncludeType includeType)
+{
+ QString newFilename;
+ if(includeType == RppTreeEvaluator::QuoteInclude)
+ newFilename = m_includeFiles.quoteLookup(includer->fileName(), filename);
+ else //AngleBracketInclude
+ newFilename = m_includeFiles.angleBracketLookup(filename);
+
+ if (QFile::exists(newFilename)) {
+ includee = m_preprocessorCache.sourceTree(newFilename);
+ return;
+ }
+
+ if (m_preLoadFiles.contains(filename)) {
+ includee = m_preprocessorCache.sourceTree(filename);
+ return;
+ }
+
+ includee = m_preprocessorCache.sourceTree(newFilename);
+ emit error(QLatin1String("Error"), QLatin1String("Could not find file ") + filename);
+}
+
+/*
+ Callback connected to preprocessorCache. Tries to load a file from
+ m_preLoadFiles before going to disk.
+*/
+void PreprocessorController::readFile(QByteArray &contents, QString filename)
+{
+ if (m_preLoadFiles.contains(filename)) {
+ contents = m_preLoadFiles.value(filename);
+ return;
+ }
+
+ QFile f(filename);
+ if (!f.exists())
+ return;
+ f.open(QIODevice::ReadOnly);
+ if (!f.isOpen())
+ return;
+ contents = f.readAll();
+}
+
+/*
+ Preprocess file give by filename. Filename is resloved agains the basepath
+ set in IncludeFiles.
+*/
+TokenSectionSequence PreprocessorController::evaluate(const QString &filename, Rpp::DefineMap *activedefinitions)
+{
+ QString resolvedFilename = m_includeFiles.resolve(filename);
+ if(!QFile::exists(resolvedFilename))
+ emit error(QLatin1String("Error"), QLatin1String("Could not find file: ") + filename);
+ Source *source = m_preprocessorCache.sourceTree(resolvedFilename);
+
+ return m_rppTreeEvaluator.evaluate(source, activedefinitions);
+}
+
+QByteArray defaultDefines =
+ "#define __attribute__(a...) \n \
+ #define __attribute__ \n \
+ #define __extension \n \
+ #define __extension__ \n \
+ #define __restrict \n \
+ #define __restrict__ \n \
+ #define __volatile volatile\n \
+ #define __volatile__ volatile\n \
+ #define __inline inline\n \
+ #define __inline__ inline\n \
+ #define __const const\n \
+ #define __const__ const\n \
+ #define __asm asm\n \
+ #define __asm__ asm\n \
+ #define __GNUC__ 2\n \
+ #define __GNUC_MINOR__ 95\n \
+ #define __cplusplus \n \
+ #define __linux__ \n";
+
+
+/*
+ Returns a DefineMap containing the above macro definitions. The DefineMap
+ will contain pointers to data stored in the provided cache object.
+*/
+Rpp::DefineMap *defaultMacros(PreprocessorCache &cache)
+{
+ DefineMap *defineMap = new DefineMap();
+ //write out default macros to a temp file
+ QTemporaryFile tempfile;
+ tempfile.open();
+ tempfile.write(defaultDefines);
+ tempfile.flush();
+
+ IncludeFiles *includeFiles = new IncludeFiles(QString(), QStringList());
+ PreprocessorController preprocessorController(*includeFiles, cache);
+ //evaluate default macro file.
+ preprocessorController.evaluate(tempfile.fileName(), defineMap);
+ delete includeFiles;
+ return defineMap;
+}
+
+void StandardOutErrorHandler::error(QString type, QString text)
+{
+ Q_UNUSED(type);
+ puts(qPrintable(text));
+}
+
+/*
+ RppPreprocessor is a convenience class that contains all the components
+ needed to preprocess files. Error messages are printed to standard out.
+*/
+RppPreprocessor::RppPreprocessor(QString basePath, QStringList includePaths, QStringList preLoadFilesFilenames)
+:m_includeFiles(basePath, includePaths)
+,m_activeDefinitions(defaultMacros(m_cache))
+,m_controller(m_includeFiles, m_cache, preLoadFilesFilenames)
+{
+ QObject::connect(&m_controller, SIGNAL(error(QString,QString)), &m_errorHandler, SLOT(error(QString,QString)));
+}
+
+RppPreprocessor::~RppPreprocessor()
+{
+ delete m_activeDefinitions;
+}
+
+TokenEngine::TokenSectionSequence RppPreprocessor::evaluate(const QString &filename)
+{
+ DefineMap defMap = *m_activeDefinitions;
+ return m_controller.evaluate(filename, &defMap);
+}
+
+QT_END_NAMESPACE