qmake/generators/symbian/initprojectdeploy_symbian.cpp
changeset 0 1918ee327afb
child 1 ae9c8dab0e3e
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 qmake 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 "initprojectdeploy_symbian.h"
       
    43 #include <QDirIterator>
       
    44 #include <project.h>
       
    45 #include <qxmlstream.h>
       
    46 #include <qsettings.h>
       
    47 #include <qdebug.h>
       
    48 
       
    49 #define PLUGIN_STUB_DIR "qmakepluginstubs"
       
    50 #define SYSBIN_DIR "\\sys\\bin"
       
    51 
       
    52 #define SUFFIX_DLL "dll"
       
    53 #define SUFFIX_EXE "exe"
       
    54 #define SUFFIX_QTPLUGIN "qtplugin"
       
    55 
       
    56 static void fixEpocRootStr(QString& path)
       
    57 {
       
    58     path.replace("\\", "/");
       
    59 
       
    60     /* :QTP:QTPROD-154: Raptor needs the drive letter
       
    61     if (path.size() > 1 && path[1] == QChar(':')) {
       
    62         path = path.mid(2);
       
    63     }
       
    64     */
       
    65 
       
    66     if (!path.size() || path[path.size()-1] != QChar('/')) {
       
    67         path += QChar('/');
       
    68     }
       
    69 }
       
    70 
       
    71 #define SYMBIAN_SDKS_KEY "HKEY_LOCAL_MACHINE\\Software\\Symbian\\EPOC SDKs"
       
    72 
       
    73 static QString epocRootStr;
       
    74 
       
    75 QString epocRoot()
       
    76 {
       
    77     if (!epocRootStr.isEmpty()) {
       
    78         return epocRootStr;
       
    79     }
       
    80 
       
    81     // First, check the env variable
       
    82     epocRootStr = qgetenv("EPOCROOT");
       
    83 
       
    84     if (epocRootStr.isEmpty()) {
       
    85         // No EPOCROOT set, check the default device
       
    86         // First check EPOCDEVICE env variable
       
    87         QString defaultDevice = qgetenv("EPOCDEVICE");
       
    88 
       
    89         // Check the windows registry via QSettings for devices.xml path
       
    90         QSettings settings(SYMBIAN_SDKS_KEY, QSettings::NativeFormat);
       
    91         QString devicesXmlPath = settings.value("CommonPath").toString();
       
    92 
       
    93         if (!devicesXmlPath.isEmpty()) {
       
    94             // Parse xml for correct device
       
    95             devicesXmlPath += "/devices.xml";
       
    96             QFile devicesFile(devicesXmlPath);
       
    97             if (devicesFile.open(QIODevice::ReadOnly)) {
       
    98                 QXmlStreamReader xml(&devicesFile);
       
    99                 while (!xml.atEnd()) {
       
   100                     xml.readNext();
       
   101                     if (xml.isStartElement() && xml.name() == "devices") {
       
   102                         if (xml.attributes().value("version") == "1.0") {
       
   103                             // Look for correct device
       
   104                             while (!(xml.isEndElement() && xml.name() == "devices") && !xml.atEnd()) {
       
   105                                 xml.readNext();
       
   106                                 if (xml.isStartElement() && xml.name() == "device") {
       
   107                                     if ((defaultDevice.isEmpty() && xml.attributes().value("default") == "yes") ||
       
   108                                         (!defaultDevice.isEmpty() && (xml.attributes().value("id").toString() + QString(":") + xml.attributes().value("name").toString()) == defaultDevice)) {
       
   109                                         // Found the correct device
       
   110                                         while (!(xml.isEndElement() && xml.name() == "device") && !xml.atEnd()) {
       
   111                                             xml.readNext();
       
   112                                             if (xml.isStartElement() && xml.name() == "epocroot") {
       
   113                                                 epocRootStr = xml.readElementText();
       
   114                                                 fixEpocRootStr(epocRootStr);
       
   115                                                 return epocRootStr;
       
   116                                             }
       
   117                                         }
       
   118                                         xml.raiseError("No epocroot element found");
       
   119                                     }
       
   120                                 }
       
   121                             }
       
   122                         } else {
       
   123                             xml.raiseError("Invalid 'devices' element version");
       
   124                         }
       
   125                     }
       
   126                 }
       
   127                 if (xml.hasError()) {
       
   128                     fprintf(stderr, "ERROR: \"%s\" when parsing devices.xml\n", qPrintable(xml.errorString()));
       
   129                 }
       
   130             } else {
       
   131                 fprintf(stderr, "Could not open devices.xml (%s)\n", qPrintable(devicesXmlPath));
       
   132             }
       
   133         } else {
       
   134             fprintf(stderr, "Could not retrieve " SYMBIAN_SDKS_KEY " setting\n");
       
   135         }
       
   136 
       
   137         fprintf(stderr, "Failed to determine epoc root.\n");
       
   138         if (!defaultDevice.isEmpty())
       
   139             fprintf(stderr, "The device indicated by EPOCDEVICE environment variable (%s) could not be found.\n", qPrintable(defaultDevice));
       
   140         fprintf(stderr, "Either set EPOCROOT or EPOCDEVICE environment variable to a valid value, or provide a default Symbian device.\n");
       
   141 
       
   142         // No valid device found; set epocroot to "/"
       
   143         epocRootStr = QLatin1String("/");
       
   144     }
       
   145 
       
   146     fixEpocRootStr(epocRootStr);
       
   147     return epocRootStr;
       
   148 }
       
   149 
       
   150 
       
   151 static bool isPlugin(const QFileInfo& info, const QString& devicePath)
       
   152 {
       
   153     // Libraries are plugins if deployment path is something else than
       
   154     // SYSBIN_DIR with or without drive letter
       
   155     if (0 == info.suffix().compare(QLatin1String(SUFFIX_DLL), Qt::CaseInsensitive) &&
       
   156             (devicePath.size() < 8 ||
       
   157              (0 != devicePath.compare(QLatin1String(SYSBIN_DIR), Qt::CaseInsensitive) &&
       
   158               0 != devicePath.mid(1).compare(QLatin1String(":" SYSBIN_DIR), Qt::CaseInsensitive)))) {
       
   159         return true;
       
   160     } else {
       
   161         return false;
       
   162     }
       
   163 }
       
   164 
       
   165 static bool isBinary(const QFileInfo& info)
       
   166 {
       
   167     if (0 == info.suffix().compare(QLatin1String(SUFFIX_DLL), Qt::CaseInsensitive) ||
       
   168             0 == info.suffix().compare(QLatin1String(SUFFIX_EXE), Qt::CaseInsensitive)) {
       
   169         return true;
       
   170     } else {
       
   171         return false;
       
   172     }
       
   173 }
       
   174 
       
   175 static void createPluginStub(const QFileInfo& info,
       
   176                              const QString& devicePath,
       
   177                              DeploymentList &deploymentList,
       
   178                              QStringList& generatedDirs,
       
   179                              QStringList& generatedFiles)
       
   180 {
       
   181     QDir().mkpath(QLatin1String(PLUGIN_STUB_DIR "\\"));
       
   182     if (!generatedDirs.contains(PLUGIN_STUB_DIR))
       
   183         generatedDirs << PLUGIN_STUB_DIR;
       
   184     // Plugin stubs must have different name from the actual plugins, because
       
   185     // the toolchain for creating ROM images cannot handle non-binary .dll files properly.
       
   186     QFile stubFile(QLatin1String(PLUGIN_STUB_DIR "\\") + info.completeBaseName() + "." SUFFIX_QTPLUGIN);
       
   187     if (stubFile.open(QIODevice::WriteOnly)) {
       
   188         if (!generatedFiles.contains(stubFile.fileName()))
       
   189             generatedFiles << stubFile.fileName();
       
   190         QTextStream t(&stubFile);
       
   191         // Add note to stub so that people will not wonder what it is.
       
   192         // Creation date is added to make new stub to deploy always to
       
   193         // force plugin cache miss when loading plugins.
       
   194         t << "This file is a Qt plugin stub file. The real Qt plugin is located in " SYSBIN_DIR ". Created:" << QDateTime::currentDateTime().toString(Qt::ISODate) << "\n";
       
   195     } else {
       
   196         fprintf(stderr, "cannot deploy \"%s\" because of plugin stub file creation failed\n", info.fileName().toLatin1().constData());
       
   197     }
       
   198     QFileInfo stubInfo(stubFile);
       
   199     deploymentList.append(CopyItem(Option::fixPathToLocalOS(stubInfo.absoluteFilePath()),
       
   200                                    Option::fixPathToLocalOS(devicePath + "\\" + stubInfo.fileName())));
       
   201 }
       
   202 
       
   203 QString generate_uid(const QString& target)
       
   204 {
       
   205     static QMap<QString, QString> targetToUid;
       
   206 
       
   207     QString tmp = targetToUid[target];
       
   208 
       
   209     if (!tmp.isEmpty()) {
       
   210         return tmp;
       
   211     }
       
   212 
       
   213     unsigned long hash = 5381;
       
   214     int c;
       
   215 
       
   216     for (int i = 0; i < target.size(); ++i) {
       
   217         c = target.at(i).toAscii();
       
   218         hash ^= c + ((c - i) << i % 20) + ((c + i) << (i + 5) % 20) + ((c - 2 * i) << (i + 10) % 20) + ((c + 2 * i) << (i + 15) % 20);
       
   219     }
       
   220 
       
   221     tmp.setNum(hash, 16);
       
   222     for (int i = tmp.size(); i < 8; ++i)
       
   223         tmp.prepend("0");
       
   224 
       
   225     targetToUid[target] = tmp;
       
   226 
       
   227     return tmp;
       
   228 }
       
   229 
       
   230 // UIDs starting with 0xE are test UIDs in symbian
       
   231 QString generate_test_uid(const QString& target)
       
   232 {
       
   233     QString tmp = generate_uid(target);
       
   234     tmp.replace(0, 1, "E");
       
   235     tmp.prepend("0x");
       
   236 
       
   237     return tmp;
       
   238 }
       
   239 
       
   240 
       
   241 void initProjectDeploySymbian(QMakeProject* project,
       
   242                               DeploymentList &deploymentList,
       
   243                               const QString &testPath,
       
   244                               bool deployBinaries,
       
   245                               const QString &platform,
       
   246                               const QString &build,
       
   247                               QStringList& generatedDirs,
       
   248                               QStringList& generatedFiles)
       
   249 {
       
   250     QString targetPath = project->values("deploy.path").join(" ");
       
   251     if (targetPath.isEmpty())
       
   252         targetPath = testPath;
       
   253     if (targetPath.endsWith("/") || targetPath.endsWith("\\"))
       
   254         targetPath = targetPath.mid(0, targetPath.size() - 1);
       
   255 
       
   256     bool targetPathHasDriveLetter = false;
       
   257     if (targetPath.size() > 1) {
       
   258         targetPathHasDriveLetter = targetPath.at(1) == QLatin1Char(':');
       
   259     }
       
   260     QString deploymentDrive = targetPathHasDriveLetter ? targetPath.left(2) : QLatin1String("c:");
       
   261 
       
   262     foreach(QString item, project->values("DEPLOYMENT")) {
       
   263         QString devicePath = project->first(item + ".path");
       
   264         if (!deployBinaries
       
   265                 && !devicePath.isEmpty()
       
   266                 && (0 == devicePath.compare(project->values("APP_RESOURCE_DIR").join(""), Qt::CaseInsensitive)
       
   267                     || 0 == devicePath.compare(project->values("REG_RESOURCE_IMPORT_DIR").join(""), Qt::CaseInsensitive))) {
       
   268             // Do not deploy resources in emulator builds, as that seems to cause conflicts
       
   269             // If there is ever a real need to deploy pre-built resources for emulator,
       
   270             // BLD_INF_RULES.prj_exports can be used as a workaround.
       
   271             continue;
       
   272         }
       
   273 
       
   274         bool devicePathHasDriveLetter = false;
       
   275         if (devicePath.size() > 1) {
       
   276             devicePathHasDriveLetter = devicePath.at(1) == QLatin1Char(':');
       
   277         }
       
   278 
       
   279         if (devicePath.isEmpty() || devicePath == QLatin1String(".")) {
       
   280             devicePath = targetPath;
       
   281         }
       
   282         // check if item.path is relative (! either / or \)
       
   283         else if (!(devicePath.at(0) == QLatin1Char('/')
       
   284                    || devicePath.at(0) == QLatin1Char('\\')
       
   285                    || devicePathHasDriveLetter)) {
       
   286             // create output path
       
   287             devicePath = Option::fixPathToLocalOS(QDir::cleanPath(targetPath + QLatin1Char('\\') + devicePath));
       
   288         } else {
       
   289             if (0 == platform.compare(QLatin1String("winscw"), Qt::CaseInsensitive)) {
       
   290                 if (devicePathHasDriveLetter) {
       
   291                     devicePath = epocRoot() + "epoc32\\winscw\\" + devicePath.remove(1, 1);
       
   292                 } else {
       
   293                     devicePath = epocRoot() + "epoc32\\winscw\\c" + devicePath;
       
   294                 }
       
   295             } else {
       
   296                 // Drive letter needed if targetpath contains one and it is not already in
       
   297                 //:QTP:QTPROD-92 Deployment of plugins requires WINSCW build before ARM build
       
   298                 if (targetPathHasDriveLetter && !devicePathHasDriveLetter) {
       
   299                    //temporary fix for Raptor building for plugins
       
   300                    if (devicePath.indexOf("plugins", Qt::CaseInsensitive) != -1) {
       
   301                     devicePath = deploymentDrive + "\\epoc32\\data\\z" + devicePath;
       
   302 				   } else {
       
   303                     devicePath = deploymentDrive + devicePath;
       
   304 				   }
       
   305                 } else {
       
   306                     devicePath = epocRoot() + "epoc32\\data\\z" + devicePath;
       
   307 				   }
       
   308             }
       
   309         }
       
   310 
       
   311         devicePath.replace(QLatin1String("/"), QLatin1String("\\"));
       
   312 
       
   313         if (!deployBinaries &&
       
   314                 0 == devicePath.right(8).compare(QLatin1String(SYSBIN_DIR), Qt::CaseInsensitive)) {
       
   315             // Skip deploying to SYSBIN_DIR for anything but binary deployments
       
   316             // Note: Deploying pre-built binaries also follow this rule, so emulator builds
       
   317             // will not get those deployed. Since there is no way to differentiate currently
       
   318             // between pre-built binaries for emulator and HW anyway, this is not a major issue.
       
   319             continue;
       
   320         }
       
   321 
       
   322         foreach(QString source, project->values(item + ".sources")) {
       
   323             source = Option::fixPathToLocalOS(source);
       
   324             QString nameFilter;
       
   325             QFileInfo info(source);
       
   326             QString searchPath;
       
   327             bool dirSearch = false;
       
   328 
       
   329             if (info.isDir()) {
       
   330                 nameFilter = QLatin1String("*");
       
   331                 searchPath = info.absoluteFilePath();
       
   332                 dirSearch = true;
       
   333             } else {
       
   334                 if (info.exists() || source.indexOf('*') != -1) {
       
   335                     nameFilter = source.split('\\').last();
       
   336                     searchPath = info.absolutePath();
       
   337                 } else {
       
   338                     // Entry was not found. That is ok if it is a binary, since those do not necessarily yet exist.
       
   339                     // Dlls need to be processed even when not deploying binaries for the stubs
       
   340                     if (isBinary(info)) {
       
   341                         if (deployBinaries) {
       
   342                             // Executables and libraries are deployed to \sys\bin
       
   343                             QFileInfo releasePath(epocRoot() + "epoc32\\release\\" + platform + "\\" + build + "\\");
       
   344                             if(devicePathHasDriveLetter) {
       
   345                                 deploymentList.append(CopyItem(Option::fixPathToLocalOS(releasePath.absolutePath() + "\\" + info.fileName(), false, true),
       
   346                                                                Option::fixPathToLocalOS(devicePath.left(2) + QLatin1String(SYSBIN_DIR "\\") + info.fileName())));
       
   347                             } else {
       
   348                                 deploymentList.append(CopyItem(Option::fixPathToLocalOS(releasePath.absolutePath() + "\\" + info.fileName(), false, true),
       
   349                                                                Option::fixPathToLocalOS(deploymentDrive + QLatin1String(SYSBIN_DIR "\\") + info.fileName())));
       
   350                             }
       
   351                         }
       
   352                         if (isPlugin(info, devicePath)) {
       
   353                             createPluginStub(info, devicePath, deploymentList, generatedDirs, generatedFiles);
       
   354                             continue;
       
   355                         }
       
   356                     } else {
       
   357                         // Generate deployment even if file doesn't exist, as this may be the case
       
   358                         // when generating .pkg files.
       
   359                         deploymentList.append(CopyItem(Option::fixPathToLocalOS(info.absoluteFilePath()),
       
   360                                                        Option::fixPathToLocalOS(devicePath + "\\" + info.fileName())));
       
   361                         continue;
       
   362                     }
       
   363                 }
       
   364             }
       
   365 
       
   366             int pathSize = info.absolutePath().size();
       
   367             QDirIterator iterator(searchPath, QStringList() << nameFilter
       
   368                                   , QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks
       
   369                                   , dirSearch ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags);
       
   370 
       
   371             while (iterator.hasNext()) {
       
   372                 iterator.next();
       
   373                 QFileInfo iteratorInfo(iterator.filePath());
       
   374                 QString absoluteItemPath = Option::fixPathToLocalOS(iteratorInfo.absolutePath());
       
   375                 int diffSize = absoluteItemPath.size() - pathSize;
       
   376 
       
   377                 if (!iteratorInfo.isDir()) {
       
   378                     if (isPlugin(iterator.fileInfo(), devicePath)) {
       
   379                         // This deploys pre-built plugins. Other pre-built binaries will deploy normally,
       
   380                         // as they have SYSBIN_DIR target path.
       
   381                         if (deployBinaries) {
       
   382                             deploymentList.append(CopyItem(Option::fixPathToLocalOS(absoluteItemPath + "\\" + iterator.fileName()),
       
   383                                                            Option::fixPathToLocalOS(deploymentDrive + QLatin1String(SYSBIN_DIR "\\") + iterator.fileName())));
       
   384                         }
       
   385                         createPluginStub(info, devicePath + "\\" + absoluteItemPath.right(diffSize), deploymentList, generatedDirs, generatedFiles);
       
   386                         continue;
       
   387                     } else {
       
   388                         deploymentList.append(CopyItem(Option::fixPathToLocalOS(absoluteItemPath + "\\" + iterator.fileName()),
       
   389                                                        Option::fixPathToLocalOS(devicePath + "\\" + absoluteItemPath.right(diffSize) + "\\" + iterator.fileName())));
       
   390                     }
       
   391                 }
       
   392             }
       
   393         }
       
   394     }
       
   395 }
       
   396 
       
   397 //:QTP:QTPROD-92 Deployment of plugins requires WINSCW build before ARM build
       
   398 void writeSbsDeploymentList(const DeploymentList& depList, QTextStream& t)
       
   399 {
       
   400     for (int i = 0; i < depList.size(); ++i) {
       
   401         t << "START EXTENSION qt/qmake_emulator_deployment" << endl;
       
   402         QString fromItem = depList.at(i).from;
       
   403         QString toItem = depList.at(i).to;
       
   404         fromItem.replace("\\", "/");
       
   405         toItem.replace("\\", "/");
       
   406         t << "OPTION DEPLOY_SOURCE " << fromItem << endl;
       
   407         t << "OPTION DEPLOY_TARGET " << toItem << endl;
       
   408         t << "END" << endl;
       
   409     }
       
   410 }