1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the porting application of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
34
#include "preprocessorcontrol.h"
35
#include "fileporter.h"
36
#include "replacetoken.h"
38
#include "tokenizer.h"
40
using namespace TokenEngine;
45
FilePorter::FilePorter(PreprocessorCache &preprocessorCache)
46
:preprocessorCache(preprocessorCache)
47
,tokenReplacementRules(PortingRules::instance()->getTokenReplacementRules())
48
,replaceToken(tokenReplacementRules)
50
foreach(QString headerName, PortingRules::instance()->getHeaderList(PortingRules::Qt4)) {
51
qt4HeaderNames.insert(headerName.toLatin1());
56
Ports a file given by fileName, which should be an aboslute file path.
58
void FilePorter::port(QString fileName)
60
//Get file tokens from cache.
61
TokenContainer sourceTokens = preprocessorCache.sourceTokens(fileName);
62
if(sourceTokens.count() == 0)
65
Logger::instance()->beginSection();
67
//Perform token replacements.
68
QByteArray portedContents =
69
replaceToken.getTokenTextReplacements(sourceTokens).apply(sourceTokens.fullText());
71
//This step needs to be done after the token replacements, since
72
//we need to know which new class names that has been inserted in the source.
73
portedContents = includeAnalyse(portedContents);
75
//check if any changes has been made.
76
if(portedContents == sourceTokens.fullText()) {
77
Logger::instance()->addEntry(
78
new PlainLogEntry("Info", "Porting", QLatin1String("No changes made to file ") + fileName));
79
Logger::instance()->commitSection();
83
//Write file, commit log if write was successful
84
FileWriter::WriteResult result = FileWriter::instance()->writeFileVerbously(fileName, portedContents);
85
Logger *logger = Logger::instance();
86
if (result == FileWriter::WriteSucceeded) {
87
logger->commitSection();
88
} else if (result == FileWriter::WriteFailed) {
89
logger->revertSection();
91
new PlainLogEntry("Error", "Porting", QLatin1String("Error writing to file ") + fileName));
92
} else if (result == FileWriter::WriteSkipped) {
93
logger->revertSection();
95
new PlainLogEntry("Error", "Porting", QLatin1String("User skipped file ") + fileName));
98
logger->revertSection();
99
const QString errorString = QLatin1String("Internal error in qt3to4 - FileWriter returned invalid result code while writing to ") + fileName;
100
logger->addEntry(new PlainLogEntry("Error", "Porting", errorString));
104
QSet<QByteArray> FilePorter::usedQtModules()
106
return m_usedQtModules;
109
QByteArray FilePorter::includeAnalyse(QByteArray fileContents)
111
// Tokenize file contents agein, since it has changed.
112
QVector<TokenEngine::Token> tokens = tokenizer.tokenize(fileContents);
113
TokenEngine::TokenContainer tokenContainer(fileContents, tokens);
114
IncludeDirectiveAnalyzer includeDirectiveAnalyzer(tokenContainer);
116
// Get list of used classes.
117
QSet<QByteArray> classes = includeDirectiveAnalyzer.usedClasses();
119
// Iterate class list and find which modules are used. This info is used elswhere
120
// by when porting the .pro file.
121
const QHash<QByteArray, QByteArray> classLibraryList = PortingRules::instance()->getClassLibraryList();
122
foreach(const QByteArray className, classes) {
123
m_usedQtModules.insert(classLibraryList.value(className));
126
// Get list of included headers.
127
QSet<QByteArray> headers = includeDirectiveAnalyzer.includedHeaders();
129
// Find classes that is missing an include directive and that has a needHeader rule.
130
const QHash<QByteArray, QByteArray> neededHeaders = PortingRules::instance()->getNeededHeaders();
131
QList<QByteArray> insertHeaders;
132
foreach(const QByteArray className, classes) {
133
if (!headers.contains(className.toLower() + ".h") &&
134
!headers.contains(className)) {
135
const QByteArray insertHeader = neededHeaders.value(className);
136
if (insertHeader != QByteArray())
137
insertHeaders.append("#include <" + insertHeader + ">");
141
// Insert include directives undeclared classes.
142
int insertCount = insertHeaders.count();
143
if (insertCount > 0) {
144
QByteArray insertText;
147
insertText += "//Added by qt3to4:\n";
148
logText += "In file ";
149
logText += Logger::instance()->globalState.value("currentFileName");
150
logText += ": Added the following include directives:\n";
151
foreach (QByteArray headerName, insertHeaders) {
152
insertText = insertText + headerName + "\n";
154
logText += headerName + " ";
157
const int insertLine = 0;
158
Logger::instance()->updateLineNumbers(insertLine, insertCount + 1);
159
const int insertPos = includeDirectiveAnalyzer.insertPos();
160
fileContents.insert(insertPos, insertText);
161
Logger::instance()->addEntry(new PlainLogEntry("Info", "Porting", logText));
167
IncludeDirectiveAnalyzer::IncludeDirectiveAnalyzer(const TokenEngine::TokenContainer &fileContents)
168
:fileContents(fileContents)
170
const QVector<Type> lexical = RppLexer().lex(fileContents);
171
source = Preprocessor().parse(fileContents, lexical, &mempool);
172
foundInsertPos = false;
173
foundQtHeader = false;
175
insertTokenIndex = 0;
177
evaluateItem(source);
181
Returns a position indicating where new include directives should be inserted.
183
int IncludeDirectiveAnalyzer::insertPos()
185
const TokenEngine::Token insertToken = fileContents.token(insertTokenIndex);
186
return insertToken.start;
190
Returns a set of all headers included from this file.
192
QSet<QByteArray> IncludeDirectiveAnalyzer::includedHeaders()
194
return m_includedHeaders;
198
Returns a list of used Qt classes.
200
QSet<QByteArray> IncludeDirectiveAnalyzer::usedClasses()
202
return m_usedClasses;
206
Set insetionTokenindex to a token near other #include directives, preferably
207
just after a block of include directives that includes other Qt headers.
209
void IncludeDirectiveAnalyzer::evaluateIncludeDirective(const IncludeDirective *directive)
211
const QByteArray filename = directive->filename();
212
if (filename.isEmpty())
215
m_includedHeaders.insert(filename);
217
if (foundInsertPos || ifSectionCount > 1)
220
const bool isQtHeader = (filename.at(0) == 'q' || filename.at(0) == 'Q');
221
if (!isQtHeader && foundQtHeader) {
222
foundInsertPos = true;
227
foundQtHeader = true;
229
// Get the last token for this directive.
230
TokenEngine::TokenSection tokenSection = directive->text();
231
const int newLineToken = 1;
232
insertTokenIndex = tokenSection.containerIndex(tokenSection.count() - 1) + newLineToken;
236
Avoid inserting inside IfSections, except in the first one
237
we see, which probably is the header multiple inclusion guard.
239
void IncludeDirectiveAnalyzer::evaluateIfSection(const IfSection *ifSection)
242
RppTreeWalker::evaluateIfSection(ifSection);
247
Read all IdTokens and look for Qt class names. Also, on
248
the first IdToken set foundInsertPos to true
251
void IncludeDirectiveAnalyzer::evaluateText(const Text *textLine)
253
const int numTokens = textLine->count();
254
for (int t = 0; t < numTokens; ++t) {
255
Rpp::Token *token = textLine->token(t);
256
if (token->toIdToken()) {
257
foundInsertPos = true;
258
const int containerIndex = token->index();
259
const QByteArray tokenText = fileContents.text(containerIndex);
260
if (tokenText[0] == 'Q')
261
m_usedClasses.insert(tokenText);