tools/porting/src/projectporter.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     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