|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the qt3to4 porting application of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
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 |
|
56 QT_BEGIN_NAMESPACE |
|
57 |
|
58 using namespace TokenEngine; |
|
59 |
|
60 ProjectPorter::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 |
|
70 void ProjectPorter::enableCppParsing(bool enable) |
|
71 { |
|
72 analyze = enable; |
|
73 } |
|
74 |
|
75 void ProjectPorter::enableMissingFilesWarnings(bool enable) |
|
76 { |
|
77 warnings = enable; |
|
78 } |
|
79 |
|
80 void ProjectPorter::portProject(QString fileName) |
|
81 { |
|
82 QFileInfo fileInfo(fileName); |
|
83 portProject(fileInfo.path(), fileInfo.fileName()); |
|
84 } |
|
85 |
|
86 /* |
|
87 Port a single file |
|
88 */ |
|
89 void 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 |
|
112 void ProjectPorter::error(QString type, QString text) |
|
113 { |
|
114 if (warnings && type == QLatin1String("Error")) |
|
115 printf("Warning: %s\n", text.toLocal8Bit().constData()); |
|
116 } |
|
117 |
|
118 void 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 */ |
|
212 void 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 |
|
237 void 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 */ |
|
401 void 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 |
|
414 QT_END_NAMESPACE |