/* Copyright (C) 2009 Arno Rehn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "generatorpreprocessor.h" #include "generatorenvironment.h" #include #include #include #include QList parsedHeaders; Preprocessor::Preprocessor(const QList& includeDirs, const QStringList& defines, const QFileInfo& file) : m_includeDirs(includeDirs), m_defines(defines), m_file(file) { pp = new rpp::pp(this); pp->setEnvironment(new GeneratorEnvironment(pp)); if (file.exists()) m_fileStack.push(file); m_topBlock = new rpp::MacroBlock(0); // some basic definitions rpp::pp_macro* exportMacro = new rpp::pp_macro; exportMacro->name = IndexedString("__cplusplus"); exportMacro->definition.append(IndexedString('1')); exportMacro->function_like = false; exportMacro->variadics = false; m_topBlock->setMacro(exportMacro); exportMacro = new rpp::pp_macro; exportMacro->name = IndexedString("__GNUC__"); exportMacro->definition.append(IndexedString('4')); exportMacro->function_like = false; exportMacro->variadics = false; m_topBlock->setMacro(exportMacro); exportMacro = new rpp::pp_macro; exportMacro->name = IndexedString("__GNUC_MINOR__"); exportMacro->definition.append(IndexedString('1')); exportMacro->function_like = false; exportMacro->variadics = false; m_topBlock->setMacro(exportMacro); exportMacro = new rpp::pp_macro; #if defined(Q_OS_LINUX) exportMacro->name = IndexedString("__linux__"); #elif defined(Q_OS_WIN32) exportMacro->name = IndexedString("WIN32"); #elif defined(Q_OS_WIN64) exportMacro->name = IndexedString("WIN64"); #elif defined(Q_OS_DARWIN) exportMacro->name = IndexedString("__APPLE__"); #elif defined(Q_OS_SOLARIS) exportMacro->name = IndexedString("__sun"); #else // fall back to linux if nothing matches exportMacro->name = IndexedString("__linux__"); #endif exportMacro->function_like = false; exportMacro->variadics = false; m_topBlock->setMacro(exportMacro); #if defined(Q_OS_DARWIN64) exportMacro = new rpp::pp_macro; exportMacro->name = IndexedString("__LP64__"); exportMacro->function_like = false; exportMacro->variadics = false; m_topBlock->setMacro(exportMacro); #endif #if (defined(QT_ARCH_ARM) || defined (QT_ARCH_ARMV6)) && !defined(QT_NO_ARM_EABI) exportMacro = new rpp::pp_macro; exportMacro->name = IndexedString("__ARM_EABI__"); exportMacro->function_like = false; exportMacro->variadics = false; m_topBlock->setMacro(exportMacro); #endif // ansidecl.h will define macros for keywords if we don't define __STDC__ exportMacro = new rpp::pp_macro; exportMacro->name = IndexedString("__STDC__"); exportMacro->function_like = false; exportMacro->variadics = false; m_topBlock->setMacro(exportMacro); // the following macros are gcc specialties exportMacro = new rpp::pp_macro; exportMacro->name = IndexedString("__extension__"); exportMacro->function_like = false; exportMacro->variadics = false; m_topBlock->setMacro(exportMacro); exportMacro = new rpp::pp_macro; exportMacro->name = IndexedString("__restrict"); exportMacro->function_like = false; exportMacro->variadics = false; m_topBlock->setMacro(exportMacro); exportMacro = new rpp::pp_macro; exportMacro->name = IndexedString("__const"); exportMacro->definition.append(IndexedString("const")); exportMacro->function_like = false; exportMacro->variadics = false; m_topBlock->setMacro(exportMacro); exportMacro = new rpp::pp_macro; exportMacro->name = IndexedString("__attribute__"); exportMacro->function_like = true; exportMacro->variadics = false; m_topBlock->setMacro(exportMacro); exportMacro = new rpp::pp_macro; exportMacro->name = IndexedString("__SMOKEGEN_RUN__"); exportMacro->definition.append(IndexedString('1')); exportMacro->function_like = false; exportMacro->variadics = false; m_topBlock->setMacro(exportMacro); foreach (QString define, defines) { exportMacro = new rpp::pp_macro; exportMacro->name = IndexedString(define); exportMacro->function_like = false; exportMacro->variadics = false; m_topBlock->setMacro(exportMacro); } pp->environment()->visitBlock(m_topBlock); } Preprocessor::~Preprocessor() { delete pp; } void Preprocessor::setFile(const QFileInfo& file) { m_fileStack.clear(); m_fileStack.push(file); m_file = file; } QFileInfo Preprocessor::file() { return m_file; } void Preprocessor::setIncludeDirs(QList< QDir > dirs) { m_includeDirs = dirs; } QList< QDir > Preprocessor::includeDirs() { return m_includeDirs; } void Preprocessor::setDefines(QStringList defines) { m_defines = defines; } QStringList Preprocessor::defines() { return m_defines; } PreprocessedContents Preprocessor::lastContents() { return m_contents; } PreprocessedContents Preprocessor::preprocess() { m_contents = pp->processFile(m_file.absoluteFilePath()); return m_contents; } rpp::Stream* Preprocessor::sourceNeeded(QString& fileName, rpp::Preprocessor::IncludeType type, int sourceLine, bool skipCurrentPath) { #ifdef Q_WS_MAC static QRegExp frameworkExpr("([^/]+)/(.*)"); #endif if (m_fileStack.top().fileName() == fileName && type == rpp::Preprocessor::IncludeGlobal) { #ifdef DEBUG qDebug("prevented possible endless loop because of #include <%s>", qPrintable(fileName)); #endif return 0; } // are the contents already cached? if (type == rpp::Preprocessor::IncludeGlobal && m_cache.contains(fileName)) { QPair& cached = m_cache[fileName]; m_fileStack.push(cached.first); return new HeaderStream(&cached.second, &m_fileStack); } QString path; QFileInfo info(fileName); // smokegen chokes on gcc's string.h, so use our simplified version here if (type == rpp::Preprocessor::IncludeGlobal && fileName == "string.h") { static QString customStringHPath = qApp->applicationDirPath() + "/../share/smokegen/string.h"; path = customStringHPath; } else if (info.isAbsolute()) { path = fileName; } else if (type == rpp::Preprocessor::IncludeLocal) { info.setFile(m_fileStack.last().dir(), fileName); if (info.isFile()) path = info.absoluteFilePath(); } if (path.isEmpty()) { #ifdef Q_WS_MAC QString framework; QString header; if (frameworkExpr.exactMatch(fileName)) { framework = frameworkExpr.cap(1); header = frameworkExpr.cap(2); } #endif Q_FOREACH (const QDir& dir, m_includeDirs) { info.setFile(dir, fileName); if (info.isFile()) { path = info.absoluteFilePath(); break; } #ifdef Q_WS_MAC QDir parentDir = dir; parentDir.cdUp(); if (parentDir.dirName() == framework + ".framework" && dir.dirName() == "Headers") { info.setFile(dir, header); if (info.isFile()) { path = info.absoluteFilePath(); break; } } #endif } } if (path.isEmpty()) { #ifdef DEBUG qDebug("PP: File not found: %s", qPrintable(fileName)); #endif return 0; } QFile file(path); file.open(QFile::ReadOnly); QByteArray array = file.readAll(); file.close(); parsedHeaders << path; if (type == rpp::Preprocessor::IncludeGlobal) { // cache the identifier (fileName), the accompanying QFileInfo and the contents QHash >::iterator iter = m_cache.insert(fileName, qMakePair(QFileInfo(path), convertFromByteArray(array))); /* Push the QFileInfo of the included file on the file stack, so if sourceNeeded() is called for that file, we know where to search for headers included with a IncludeLocal. The HeaderStream will be destroyed when the file has been processed and it'll then pop() the stack */ m_fileStack.push(iter.value().first); return new HeaderStream(&iter.value().second, &m_fileStack); } else { /* Don't cache locally included files - the meaning of a file name may change when recursing into other directories. We also don't need to push any files on the fileStack because the directory for local includes is not changing. */ m_localContent.append(convertFromByteArray(array)); m_fileStack.push(QFileInfo(path)); return new HeaderStream(&m_localContent.last(), &m_fileStack); } return 0; }