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
}