| 1 |
/* |
| 2 |
* This file is part of the API Extractor project. |
| 3 |
* |
| 4 |
* Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
| 5 |
* |
| 6 |
* Contact: PySide team <contact@pyside.org> |
| 7 |
* |
| 8 |
* This program is free software; you can redistribute it and/or |
| 9 |
* modify it under the terms of the GNU General Public License |
| 10 |
* version 2 as published by the Free Software Foundation. |
| 11 |
* |
| 12 |
* This program is distributed in the hope that it will be useful, but |
| 13 |
* WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 |
* General Public License for more details. |
| 16 |
* |
| 17 |
* You should have received a copy of the GNU General Public License |
| 18 |
* along with this program; if not, write to the Free Software |
| 19 |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
| 20 |
* 02110-1301 USA |
| 21 |
* |
| 22 |
*/ |
| 23 |
|
| 24 |
#include "apiextractor.h" |
| 25 |
#include <QDir> |
| 26 |
#include <QDebug> |
| 27 |
#include <QTemporaryFile> |
| 28 |
#include <iostream> |
| 29 |
|
| 30 |
#include "reporthandler.h" |
| 31 |
#include "typesystem.h" |
| 32 |
#include "fileout.h" |
| 33 |
#include "parser/rpp/pp.h" |
| 34 |
#include "abstractmetabuilder.h" |
| 35 |
#include "apiextractorversion.h" |
| 36 |
#include "typedatabase.h" |
| 37 |
|
| 38 |
static bool preprocess(const QString& sourceFile, |
| 39 |
QFile& targetFile, |
| 40 |
const QStringList& includes); |
| 41 |
|
| 42 |
ApiExtractor::ApiExtractor() : m_builder(0) |
| 43 |
{ |
| 44 |
// Environment TYPESYSTEMPATH |
| 45 |
QString envTypesystemPaths = getenv("TYPESYSTEMPATH"); |
| 46 |
if (!envTypesystemPaths.isEmpty()) |
| 47 |
TypeDatabase::instance()->addTypesystemPath(envTypesystemPaths); |
| 48 |
ReportHandler::setContext("ApiExtractor"); |
| 49 |
} |
| 50 |
|
| 51 |
ApiExtractor::~ApiExtractor() |
| 52 |
{ |
| 53 |
delete m_builder; |
| 54 |
} |
| 55 |
|
| 56 |
void ApiExtractor::addTypesystemSearchPath (const QString& path) |
| 57 |
{ |
| 58 |
TypeDatabase::instance()->addTypesystemPath(path); |
| 59 |
} |
| 60 |
|
| 61 |
void ApiExtractor::addTypesystemSearchPath(const QStringList& paths) |
| 62 |
{ |
| 63 |
foreach (QString path, paths) |
| 64 |
addTypesystemSearchPath(path); |
| 65 |
} |
| 66 |
|
| 67 |
void ApiExtractor::addIncludePath(const QString& path) |
| 68 |
{ |
| 69 |
m_includePaths << path; |
| 70 |
} |
| 71 |
|
| 72 |
void ApiExtractor::addIncludePath(const QStringList& paths) |
| 73 |
{ |
| 74 |
m_includePaths << paths; |
| 75 |
} |
| 76 |
|
| 77 |
void ApiExtractor::setLogDirectory(const QString& logDir) |
| 78 |
{ |
| 79 |
m_logDirectory = logDir; |
| 80 |
} |
| 81 |
|
| 82 |
void ApiExtractor::setCppFileName(const QString& cppFileName) |
| 83 |
{ |
| 84 |
m_cppFileName = cppFileName; |
| 85 |
} |
| 86 |
|
| 87 |
void ApiExtractor::setTypeSystem(const QString& typeSystemFileName) |
| 88 |
{ |
| 89 |
m_typeSystemFileName = typeSystemFileName; |
| 90 |
} |
| 91 |
|
| 92 |
void ApiExtractor::setDebugLevel(ReportHandler::DebugLevel debugLevel) |
| 93 |
{ |
| 94 |
ReportHandler::setDebugLevel(debugLevel); |
| 95 |
} |
| 96 |
|
| 97 |
void ApiExtractor::setSuppressWarnings ( bool value ) |
| 98 |
{ |
| 99 |
TypeDatabase::instance()->setSuppressWarnings(value); |
| 100 |
} |
| 101 |
|
| 102 |
void ApiExtractor::setSilent ( bool value ) |
| 103 |
{ |
| 104 |
ReportHandler::setSilent(value); |
| 105 |
} |
| 106 |
|
| 107 |
void ApiExtractor::setApiVersion(double version) |
| 108 |
{ |
| 109 |
TypeDatabase::instance()->setApiVersion("*", QByteArray::number(version)); |
| 110 |
} |
| 111 |
|
| 112 |
void ApiExtractor::setApiVersion(const QString& package, const QByteArray& version) |
| 113 |
{ |
| 114 |
TypeDatabase::instance()->setApiVersion(package, version); |
| 115 |
} |
| 116 |
|
| 117 |
void ApiExtractor::setDropTypeEntries(QString dropEntries) |
| 118 |
{ |
| 119 |
dropEntries.remove(' '); |
| 120 |
QStringList entries = dropEntries.split(';'); |
| 121 |
TypeDatabase::instance()->setDropTypeEntries(entries); |
| 122 |
} |
| 123 |
|
| 124 |
AbstractMetaEnumList ApiExtractor::globalEnums() const |
| 125 |
{ |
| 126 |
Q_ASSERT(m_builder); |
| 127 |
return m_builder->globalEnums(); |
| 128 |
} |
| 129 |
|
| 130 |
AbstractMetaFunctionList ApiExtractor::globalFunctions() const |
| 131 |
{ |
| 132 |
Q_ASSERT(m_builder); |
| 133 |
return m_builder->globalFunctions(); |
| 134 |
} |
| 135 |
|
| 136 |
AbstractMetaClassList ApiExtractor::classes() const |
| 137 |
{ |
| 138 |
Q_ASSERT(m_builder); |
| 139 |
return m_builder->classes(); |
| 140 |
} |
| 141 |
|
| 142 |
PrimitiveTypeEntryList ApiExtractor::primitiveTypes() const |
| 143 |
{ |
| 144 |
return TypeDatabase::instance()->primitiveTypes(); |
| 145 |
} |
| 146 |
|
| 147 |
ContainerTypeEntryList ApiExtractor::containerTypes() const |
| 148 |
{ |
| 149 |
return TypeDatabase::instance()->containerTypes(); |
| 150 |
} |
| 151 |
|
| 152 |
QSet<QString> ApiExtractor::qtMetaTypeDeclaredTypeNames() const |
| 153 |
{ |
| 154 |
Q_ASSERT(m_builder); |
| 155 |
return m_builder->qtMetaTypeDeclaredTypeNames(); |
| 156 |
} |
| 157 |
|
| 158 |
static const AbstractMetaEnum* findEnumOnClasses(AbstractMetaClassList metaClasses, const EnumTypeEntry* typeEntry) |
| 159 |
{ |
| 160 |
const AbstractMetaEnum* result = 0; |
| 161 |
foreach (const AbstractMetaClass* metaClass, metaClasses) { |
| 162 |
foreach (const AbstractMetaEnum* metaEnum, metaClass->enums()) { |
| 163 |
if (metaEnum->typeEntry() == typeEntry) { |
| 164 |
result = metaEnum; |
| 165 |
break; |
| 166 |
} |
| 167 |
} |
| 168 |
if (result) |
| 169 |
break; |
| 170 |
result = findEnumOnClasses(metaClass->innerClasses(), typeEntry); |
| 171 |
} |
| 172 |
return result; |
| 173 |
} |
| 174 |
|
| 175 |
const AbstractMetaEnum* ApiExtractor::findAbstractMetaEnum(const EnumTypeEntry* typeEntry) const |
| 176 |
{ |
| 177 |
if (!typeEntry) |
| 178 |
return 0; |
| 179 |
foreach (AbstractMetaEnum* metaEnum, m_builder->globalEnums()) { |
| 180 |
if (metaEnum->typeEntry() == typeEntry) |
| 181 |
return metaEnum; |
| 182 |
} |
| 183 |
return findEnumOnClasses(m_builder->classes(), typeEntry); |
| 184 |
} |
| 185 |
|
| 186 |
const AbstractMetaEnum* ApiExtractor::findAbstractMetaEnum(const TypeEntry* typeEntry) const |
| 187 |
{ |
| 188 |
if (!typeEntry) |
| 189 |
return 0; |
| 190 |
if (typeEntry->isFlags()) |
| 191 |
return findAbstractMetaEnum(reinterpret_cast<const FlagsTypeEntry*>(typeEntry)); |
| 192 |
if (typeEntry->isEnum()) |
| 193 |
return findAbstractMetaEnum(reinterpret_cast<const EnumTypeEntry*>(typeEntry)); |
| 194 |
return 0; |
| 195 |
} |
| 196 |
|
| 197 |
const AbstractMetaEnum* ApiExtractor::findAbstractMetaEnum(const FlagsTypeEntry* typeEntry) const |
| 198 |
{ |
| 199 |
if (!typeEntry) |
| 200 |
return 0; |
| 201 |
return findAbstractMetaEnum(typeEntry->originator()); |
| 202 |
} |
| 203 |
|
| 204 |
const AbstractMetaEnum* ApiExtractor::findAbstractMetaEnum(const AbstractMetaType* metaType) const |
| 205 |
{ |
| 206 |
if (!metaType) |
| 207 |
return 0; |
| 208 |
return findAbstractMetaEnum(metaType->typeEntry()); |
| 209 |
} |
| 210 |
|
| 211 |
int ApiExtractor::classCount() const |
| 212 |
{ |
| 213 |
Q_ASSERT(m_builder); |
| 214 |
return m_builder->classes().count(); |
| 215 |
} |
| 216 |
|
| 217 |
bool ApiExtractor::run() |
| 218 |
{ |
| 219 |
if (m_builder) |
| 220 |
return false; |
| 221 |
|
| 222 |
if (m_typeSystemFileName.isEmpty()) { |
| 223 |
std::cerr << "You must specify a Type System file." << std::endl; |
| 224 |
return false; |
| 225 |
} else if (!TypeDatabase::instance()->parseFile(m_typeSystemFileName)) { |
| 226 |
std::cerr << "Cannot parse file: " << qPrintable(m_typeSystemFileName); |
| 227 |
return false; |
| 228 |
} |
| 229 |
|
| 230 |
QTemporaryFile ppFile; |
| 231 |
#ifndef NDEBUG |
| 232 |
ppFile.setAutoRemove(false); |
| 233 |
#endif |
| 234 |
// run rpp pre-processor |
| 235 |
if (!preprocess(m_cppFileName, ppFile, m_includePaths)) { |
| 236 |
std::cerr << "Preprocessor failed on file: " << qPrintable(m_cppFileName); |
| 237 |
return false; |
| 238 |
} |
| 239 |
ppFile.seek(0); |
| 240 |
m_builder = new AbstractMetaBuilder; |
| 241 |
m_builder->setLogDirectory(m_logDirectory); |
| 242 |
m_builder->setGlobalHeader(m_cppFileName); |
| 243 |
m_builder->build(&ppFile); |
| 244 |
|
| 245 |
return true; |
| 246 |
} |
| 247 |
|
| 248 |
static bool preprocess(const QString& sourceFile, |
| 249 |
QFile& targetFile, |
| 250 |
const QStringList& includes) |
| 251 |
{ |
| 252 |
rpp::pp_environment env; |
| 253 |
rpp::pp preprocess(env); |
| 254 |
|
| 255 |
rpp::pp_null_output_iterator null_out; |
| 256 |
|
| 257 |
const char *ppconfig = ":/trolltech/generator/pp-qt-configuration"; |
| 258 |
|
| 259 |
QFile file(ppconfig); |
| 260 |
if (!file.open(QFile::ReadOnly)) { |
| 261 |
std::cerr << "Preprocessor configuration file not found " << ppconfig << std::endl; |
| 262 |
return false; |
| 263 |
} |
| 264 |
|
| 265 |
QByteArray ba = file.readAll(); |
| 266 |
file.close(); |
| 267 |
preprocess.operator()(ba.constData(), ba.constData() + ba.size(), null_out); |
| 268 |
|
| 269 |
preprocess.push_include_path("."); |
| 270 |
foreach (QString include, includes) |
| 271 |
preprocess.push_include_path(QDir::convertSeparators(include).toStdString()); |
| 272 |
preprocess.push_include_path("/usr/include"); |
| 273 |
|
| 274 |
QString currentDir = QDir::current().absolutePath(); |
| 275 |
QFileInfo sourceInfo(sourceFile); |
| 276 |
if (!sourceInfo.exists()) { |
| 277 |
std::cerr << "File not found " << qPrintable(sourceFile) << std::endl; |
| 278 |
return false; |
| 279 |
} |
| 280 |
QDir::setCurrent(sourceInfo.absolutePath()); |
| 281 |
|
| 282 |
std::string result; |
| 283 |
result.reserve(20 * 1024); // 20K |
| 284 |
|
| 285 |
result += "# 1 \"builtins\"\n"; |
| 286 |
result += "# 1 \""; |
| 287 |
result += sourceFile.toStdString(); |
| 288 |
result += "\"\n"; |
| 289 |
|
| 290 |
preprocess.file(sourceInfo.fileName().toStdString(), |
| 291 |
rpp::pp_output_iterator<std::string> (result)); |
| 292 |
|
| 293 |
QDir::setCurrent(currentDir); |
| 294 |
|
| 295 |
if (!targetFile.open(QIODevice::ReadWrite | QIODevice::Text)) { |
| 296 |
std::cerr << "Failed to write preprocessed file: " << qPrintable(targetFile.fileName()) << std::endl; |
| 297 |
return false; |
| 298 |
} |
| 299 |
|
| 300 |
targetFile.write(result.c_str(), result.length()); |
| 301 |
return true; |
| 302 |
} |