source: trunk/tools/porting/src/projectporter.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 16.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the qt3to4 porting application of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "projectporter.h"
43#include "proparser.h"
44#include "textreplacement.h"
45#include "fileporter.h"
46#include "logger.h"
47#include "translationunit.h"
48#include "codemodelattributes.h"
49#include <QtDebug>
50#include <QFile>
51#include <QDir>
52#include <QStringList>
53#include <QFileInfo>
54#include <QBuffer>
55
56QT_BEGIN_NAMESPACE
57
58using namespace TokenEngine;
59
60ProjectPorter::ProjectPorter(QString basePath, QStringList includeDirectories, QStringList qt3HeadersFilenames)
61:basePath(basePath)
62,includeDirectories(includeDirectories)
63,defaultDefinitions(defaultMacros(preprocessorCache))
64,filePorter(preprocessorCache)
65,qt3HeadersFilenames(qt3HeadersFilenames)
66,analyze(true)
67,warnings(false)
68{}
69
70void ProjectPorter::enableCppParsing(bool enable)
71{
72 analyze = enable;
73}
74
75void ProjectPorter::enableMissingFilesWarnings(bool enable)
76{
77 warnings = enable;
78}
79
80void ProjectPorter::portProject(QString fileName)
81{
82 QFileInfo fileInfo(fileName);
83 portProject(fileInfo.path(), fileInfo.fileName());
84}
85
86/*
87 Port a single file
88*/
89void ProjectPorter::portFile(QString fileName)
90{
91 if (analyze) {
92 IncludeFiles includeFiles(basePath, includeDirectories);
93
94 PreprocessorController preprocessor(includeFiles, preprocessorCache, qt3HeadersFilenames);
95 connect(&preprocessor, SIGNAL(error(QString,QString)), SLOT(error(QString,QString)));
96
97 Rpp::DefineMap definitionsCopy = *defaultDefinitions;
98 // Preprocess
99 TokenSectionSequence translationUnit = preprocessor.evaluate(fileName, &definitionsCopy);
100 // Parse
101 TranslationUnit translationUnitData = TranslationUnitAnalyzer().analyze(translationUnit);
102
103 // Enable attribute generation for this file.
104 enableAttributes(includeFiles, fileName);
105 // Generate attributes.
106 CodeModelAttributes().createAttributes(translationUnitData);
107 }
108
109 portFiles(QString(), QStringList() << fileName);
110}
111
112void ProjectPorter::error(QString type, QString text)
113{
114 if (warnings && type == QLatin1String("Error"))
115 printf("Warning: %s\n", text.toLocal8Bit().constData());
116}
117
118void ProjectPorter::portProject(QString basePath, QString proFileName)
119{
120 QString fullInFileName = basePath + QLatin1String("/") + proFileName;
121 QFileInfo infileInfo(fullInFileName);
122 if (!infileInfo.exists()) {
123 printf("Could not open file: %s\n", QDir::toNativeSeparators(fullInFileName).toLocal8Bit().constData());
124 return;
125 }
126
127 QString proFileContents = loadFile(fullInFileName);
128 QMap<QString, QString> proFileMap = proFileTagMap(proFileContents, QDir(basePath).absolutePath());
129
130
131 // Check if this is a TEMPLATE = subdirs .pro file, in that case we
132 // process each subdir (recursively).
133
134 QString templateTag = proFileMap[QLatin1String("TEMPLATE")];
135 if (templateTag == QLatin1String("subdirs")) {
136 QStringList subdirs = proFileMap[QLatin1String("SUBDIRS")].split(QLatin1String(" "), QString::SkipEmptyParts);
137 foreach(QString subdir, subdirs) {
138 QString newBasePath = basePath + QLatin1String("/") + subdir;
139 QStringList dirsInSubdir = subdir.split(QRegExp(QLatin1String("/|\\\\")), QString::SkipEmptyParts);
140 QString newProFileName = dirsInSubdir.last() + QLatin1String(".pro");
141 portProject(newBasePath, newProFileName);
142 }
143 return;
144 }
145
146 // Get headers and sources file names from .pro file.
147 QStringList sources = proFileMap[QLatin1String("SOURCES")].split(QLatin1String(" "), QString::SkipEmptyParts);
148 QStringList headers = proFileMap[QLatin1String("HEADERS")].split(QLatin1String(" "), QString::SkipEmptyParts);
149 QStringList forms = proFileMap[QLatin1String("FORMS")].split(QLatin1String(" "), QString::SkipEmptyParts);
150 QStringList uidoth;
151 for (int i = 0; i < forms.size(); ++i) {
152 QString ui_h = forms.at(i) + QLatin1String(".h");
153 if (QFile::exists(basePath + QLatin1String("/") + ui_h))
154 uidoth += ui_h;
155 }
156
157 if (analyze) {
158 printf("Parsing");
159 // Get include paths from the pro file.
160 QStringList includeProPaths = proFileMap[QLatin1String("INCLUDEPATH")].split(QLatin1String(" "), QString::SkipEmptyParts);
161 QStringList dependProPaths = proFileMap[QLatin1String("DEPENDPATH")].split(QLatin1String(" "), QString::SkipEmptyParts);
162 IncludeFiles includeFiles(basePath, includeDirectories + includeProPaths + dependProPaths);
163
164 PreprocessorController preprocessorController(includeFiles, preprocessorCache, qt3HeadersFilenames);
165 connect(&preprocessorController, SIGNAL(error(QString,QString)), SLOT(error(QString,QString)));
166
167 TranslationUnitAnalyzer translationUnitAnalyzer;
168 CodeModelAttributes codeModelAttributes;
169
170 // Enable attribute generation for header files.
171 foreach(QString headerFile, headers)
172 enableAttributes(includeFiles, headerFile);
173
174 // Enable attribute generation for ui.h files.
175 foreach(QString headerFile, uidoth)
176 enableAttributes(includeFiles, headerFile);
177
178 // Analyze each translation unit. (one per cpp file)
179 foreach(QString sourceFile, sources) {
180 printf(".");
181 fflush(stdout);
182 Rpp::DefineMap definitionsCopy = *defaultDefinitions;
183 TokenSectionSequence translationUnit =
184 preprocessorController.evaluate(sourceFile, &definitionsCopy);
185 TranslationUnit translationUnitData =
186 translationUnitAnalyzer.analyze(translationUnit);
187
188 // Enable attribute generation for this file.
189 enableAttributes(includeFiles, sourceFile);
190
191 codeModelAttributes.createAttributes(translationUnitData);
192 }
193 puts("");
194 }
195
196
197 // Port files.
198 portFiles(basePath, sources);
199 portFiles(basePath, headers);
200 if (!uidoth.isEmpty())
201 portFiles(basePath, uidoth);
202
203 Logger::instance()->globalState[QLatin1String("currentFileName")] = proFileName;
204 Logger::instance()->beginSection();
205 portProFile(fullInFileName, proFileMap);
206}
207
208/*
209 Port each file given in the fileNames list. If a file name is relative
210 it is assumed to be relative to basePath.
211*/
212void ProjectPorter::portFiles(QString basePath, QStringList fileNames)
213{
214 foreach(QString fileName, fileNames) {
215 QString fullFilePath;
216 QFileInfo fileInfo(fileName);
217 if (fileInfo.isAbsolute()) {
218 fullFilePath = QDir::cleanPath(fileName);
219 } else {
220 fullFilePath = QDir::cleanPath(basePath + QLatin1String("/") + fileName);
221 }
222
223 QFileInfo fullFilePathInfo(fullFilePath);
224 if (!fullFilePathInfo.exists()) {
225 printf("Could not find file: %s\n", QDir::toNativeSeparators(fullFilePath).toLocal8Bit().constData());
226 continue;
227 }
228
229 if (!processedFilesSet.contains(fullFilePath)){
230 Logger::instance()->globalState[QLatin1String("currentFileName")] = fullFilePath;
231 filePorter.port(fullFilePath);
232 processedFilesSet.insert(fullFilePath);
233 }
234 }
235}
236
237void ProjectPorter::portProFile(QString fileName, QMap<QString, QString> tagMap)
238{
239 // Read pro file.
240 QFile proFile(fileName);
241 if (!proFile.open(QIODevice::ReadOnly))
242 return;
243
244 const QByteArray contents = proFile.readAll();
245 const QByteArray lineEnding = detectLineEndings(contents);
246 proFile.seek(0);
247
248 QTextStream proTextStream(&proFile);
249 QStringList lines;
250 while (!proTextStream.atEnd())
251 lines += proTextStream.readLine();
252
253 proFile.close();
254
255 // Find out what modules we should add to the QT variable.
256 QSet<QByteArray> qtModules;
257
258 // Add qt3support to the Qt tag
259 qtModules.insert(QByteArray("qt3support"));
260
261 // Read CONFIG and add other modules.
262 QStringList config = tagMap[QLatin1String("CONFIG")].split(QLatin1String(" "), QString::SkipEmptyParts);
263 if (config.contains(QLatin1String("opengl")))
264 qtModules.insert(QByteArray("opengl"));
265 if (config.contains(QLatin1String("xml")))
266 qtModules.insert(QByteArray("xml"));
267 if (config.contains(QLatin1String("sql")))
268 qtModules.insert(QByteArray("sql"));
269 if (config.contains(QLatin1String("network")))
270 qtModules.insert(QByteArray("network"));
271
272 // Get set of used modules from the file porter.
273 qtModules += filePorter.usedQtModules();
274
275 // Remove gui and core.
276 qtModules.remove(QByteArray("gui"));
277 qtModules.remove(QByteArray("core"));
278
279 // Qt3Support is already added.
280 qtModules.remove(QByteArray("3support"));
281
282 // Remove modules already present in the QT variable.
283 QStringList qt = tagMap[QLatin1String("QT")].split(QLatin1String(" "), QString::SkipEmptyParts);
284 foreach(QString name, qt) {
285 qtModules.remove(name.toLatin1());
286 }
287
288 Logger *logger = Logger::instance();
289 bool changesMade = false;
290
291 if (!qtModules.isEmpty()) {
292 changesMade = true;
293 QString insertText = QLatin1String("QT += ");
294 foreach(QByteArray module, qtModules) {
295 insertText += QString::fromLatin1(module) + QLatin1Char(' ');
296 }
297 lines += QString(QLatin1String("#The following line was inserted by qt3to4"));
298 lines += insertText;
299 QString logText = QLatin1String("In file ")
300 + logger->globalState.value(QLatin1String("currentFileName"))
301 + QLatin1String(": Added entry ")
302 + insertText;
303 logger->addEntry(new PlainLogEntry(QLatin1String("Info"), QLatin1String("Porting"), logText));
304 }
305
306 // Add uic3 if we have forms, and change FORMS and INTERFACES to FORMS3
307 if (!tagMap[QLatin1String("FORMS")].isEmpty() || !tagMap[QLatin1String("INTERFACES")].isEmpty()) {
308 changesMade = true;
309 lines += QString(QLatin1String("#The following line was inserted by qt3to4"));
310 QString insertText = QLatin1String("CONFIG += uic3") + QString::fromLatin1(lineEnding.constData());
311 lines += insertText;
312 QString logText = QLatin1String("In file ")
313 + logger->globalState.value(QLatin1String("currentFileName"))
314 + QLatin1String(": Added entry ")
315 + insertText;
316 logger->addEntry(new PlainLogEntry(QLatin1String("Info"), QLatin1String("Porting"), logText));
317
318 const QString formsToForms3(QLatin1String("#The following line was changed from FORMS to FORMS3 by qt3to4"));
319 const QString interfacesToForms3(QLatin1String("#The following line was changed from INTERFACES to FORMS3 by qt3to4"));
320 for (int i = 0; i < lines.count(); ++i) {
321 QString cLine = lines.at(i);
322 cLine = cLine.trimmed();
323 if (cLine.startsWith(QLatin1String("FORMS"))) {
324 lines[i].replace(QLatin1String("FORMS"), QLatin1String("FORMS3"));
325 lines.insert(i, formsToForms3);
326 ++i;
327 QString logText = QLatin1String("In file ")
328 + logger->globalState.value(QLatin1String("currentFileName"))
329 + QLatin1String(": Renamed FORMS to FORMS3");
330 logger->addEntry(new PlainLogEntry(QLatin1String("Info"), QLatin1String("Porting"), logText));
331 } else if (cLine.startsWith(QLatin1String("INTERFACES"))) {
332 lines[i].replace(QLatin1String("INTERFACES"), QLatin1String("FORMS3"));
333 lines.insert(i, interfacesToForms3);
334 ++i;
335 QString logText = QLatin1String("In file ")
336 + logger->globalState.value(QLatin1String("currentFileName"))
337 + QLatin1String(": Renamed INTERFACES to FORMS3");
338 logger->addEntry(new PlainLogEntry(QLatin1String("Info"), QLatin1String("Porting"), logText));
339 }
340 }
341 }
342
343 // Comment out any REQUIRES tag.
344 if (!tagMap[QLatin1String("REQUIRES")].isEmpty()) {
345 changesMade = true;
346 QString insertText(QLatin1String("#The following line was commented out by qt3to4"));
347 for (int i = 0; i < lines.count(); ++i) {
348 if (lines.at(i).startsWith(QLatin1String("REQUIRES"))) {
349 QString lineCopy = lines.at(i);
350 lineCopy.prepend(QLatin1Char('#'));
351 lines[i] = lineCopy;
352 lines.insert(i, insertText);
353 ++i; //skip ahead, we just insertet a line at i.
354 QString logText = QLatin1String("In file ")
355 + logger->globalState.value(QLatin1String("currentFileName"))
356 + QLatin1String(": Commented out REQUIRES section");
357 logger->addEntry(new PlainLogEntry(QLatin1String("Info"), QLatin1String("Porting"), logText));
358 }
359 }
360 }
361
362 // Check if any changes has been made.
363 if (!changesMade) {
364 Logger::instance()->addEntry(
365 new PlainLogEntry(QLatin1String("Info"), QLatin1String("Porting"), QLatin1String("No changes made to file ") + fileName));
366 Logger::instance()->commitSection();
367 return;
368 }
369
370 // Write lines to array.
371 QByteArray bob;
372 QTextStream outProFileStream(&bob);
373 foreach(QString line, lines)
374 outProFileStream << line << lineEnding;
375 outProFileStream.flush();
376
377 // Write array to file, commit log if write was successful.
378 FileWriter::WriteResult result = FileWriter::instance()->writeFileVerbously(fileName, bob);
379 if (result == FileWriter::WriteSucceeded) {
380 logger->commitSection();
381 } else if (result == FileWriter::WriteFailed) {
382 logger->revertSection();
383 logger->addEntry(
384 new PlainLogEntry(QLatin1String("Error"), QLatin1String("Porting"), QLatin1String("Error writing to file ") + fileName));
385 } else if (result == FileWriter::WriteSkipped) {
386 logger->revertSection();
387 logger->addEntry(
388 new PlainLogEntry(QLatin1String("Error"), QLatin1String("Porting"), QLatin1String("User skipped file ") + fileName));
389 } else {
390 // Internal error.
391 logger->revertSection();
392 const QString errorString = QLatin1String("Internal error in qt3to4 - FileWriter returned invalid result code while writing to ") + fileName;
393 logger->addEntry(new PlainLogEntry(QLatin1String("Error"), QLatin1String("Porting"), errorString));
394 }
395}
396
397/*
398 Enables attribute generation for fileName. The file is looked up using the
399 provied includeFiles object.
400*/
401void ProjectPorter::enableAttributes(const IncludeFiles &includeFiles, const QString &fileName)
402{
403 QString resolvedFilePath = includeFiles.resolve(fileName);
404 if (!QFile::exists(resolvedFilePath))
405 resolvedFilePath = includeFiles.angleBracketLookup(fileName);
406 if (!QFile::exists(resolvedFilePath))
407 return;
408
409 TokenContainer tokenContainer = preprocessorCache.sourceTokens(resolvedFilePath);
410 TokenAttributes *attributes = tokenContainer.tokenAttributes();
411 attributes->addAttribute("CreateAttributes", "True");
412}
413
414QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.