summaryrefslogtreecommitdiffstats
path: root/qmake/generators/symbian/symbiancommon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qmake/generators/symbian/symbiancommon.cpp')
-rw-r--r--qmake/generators/symbian/symbiancommon.cpp1117
1 files changed, 1117 insertions, 0 deletions
diff --git a/qmake/generators/symbian/symbiancommon.cpp b/qmake/generators/symbian/symbiancommon.cpp
new file mode 100644
index 0000000000..32b465b4bf
--- /dev/null
+++ b/qmake/generators/symbian/symbiancommon.cpp
@@ -0,0 +1,1117 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "symbiancommon.h"
+#include <qdebug.h>
+#include <qxmlstream.h>
+
+// Included from tools/shared
+#include <symbian/epocroot_p.h>
+
+#define RESOURCE_DIRECTORY_RESOURCE "\\\\resource\\\\apps\\\\"
+
+#define RSS_RULES "RSS_RULES"
+#define RSS_RULES_BASE "RSS_RULES."
+#define RSS_TAG_NBROFICONS "number_of_icons"
+#define RSS_TAG_ICONFILE "icon_file"
+#define RSS_TAG_HEADER "header"
+#define RSS_TAG_SERVICE_LIST "service_list"
+#define RSS_TAG_FILE_OWNERSHIP_LIST "file_ownership_list"
+#define RSS_TAG_DATATYPE_LIST "datatype_list"
+#define RSS_TAG_FOOTER "footer"
+#define RSS_TAG_DEFAULT "default_rules" // Same as just giving rules without tag
+
+#define PLUGIN_COMMON_DEF_FILE_ACTUAL "plugin_commonu.def"
+
+#define MANUFACTURER_NOTE_FILE "manufacturer_note.txt"
+#define DEFAULT_MANUFACTURER_NOTE \
+ "The package is not supported for devices from this manufacturer. Please try the selfsigned " \
+ "version of the package instead."
+
+SymbianCommonGenerator::SymbianCommonGenerator(MakefileGenerator *generator)
+ : generator(generator)
+{
+}
+
+void SymbianCommonGenerator::init()
+{
+ QMakeProject *project = generator->project;
+ fixedTarget = project->first("QMAKE_ORIG_TARGET");
+ if (fixedTarget.isEmpty())
+ fixedTarget = project->first("TARGET");
+ fixedTarget = generator->unescapeFilePath(fixedTarget);
+ fixedTarget = removePathSeparators(fixedTarget);
+ removeSpecialCharacters(fixedTarget);
+
+ // This should not be empty since the mkspecs are supposed to set it if missing.
+ uid3 = project->first("TARGET.UID3").trimmed();
+
+ if ((project->values("TEMPLATE")).contains("app"))
+ targetType = TypeExe;
+ else if ((project->values("TEMPLATE")).contains("lib")) {
+ // Check CONFIG to see if we are to build staticlib or dll
+ if (project->isActiveConfig("staticlib") || project->isActiveConfig("static"))
+ targetType = TypeLib;
+ else if (project->isActiveConfig("plugin"))
+ targetType = TypePlugin;
+ else
+ targetType = TypeDll;
+ } else {
+ targetType = TypeSubdirs;
+ }
+
+ // UID is valid as either hex or decimal, so just convert it to number and back to hex
+ // to get proper string for private dir
+ bool conversionOk = false;
+ uint uidNum = uid3.toUInt(&conversionOk, 0);
+
+ if (!conversionOk) {
+ fprintf(stderr, "Error: Invalid UID \"%s\".\n", uid3.toUtf8().constData());
+ } else {
+ privateDirUid.setNum(uidNum, 16);
+ while (privateDirUid.length() < 8)
+ privateDirUid.insert(0, QLatin1Char('0'));
+ }
+}
+
+bool SymbianCommonGenerator::containsStartWithItem(const QChar &c, const QStringList& src)
+{
+ bool result = false;
+ foreach(QString str, src) {
+ if (str.startsWith(c)) {
+ result = true;
+ break;
+ }
+ }
+ return result;
+}
+
+void SymbianCommonGenerator::removeSpecialCharacters(QString& str)
+{
+ // When modifying this method check also symbianRemoveSpecialCharacters in symbian.conf
+ QString underscore = QLatin1String("_");
+ str.replace(QLatin1String("/"), underscore);
+ str.replace(QLatin1String("\\"), underscore);
+ str.replace(QLatin1String(" "), underscore);
+ str.replace(QLatin1String(":"), underscore);
+}
+
+QString romPath(const QString& path)
+{
+ if(path.length() > 2 && path[1] == ':')
+ return QLatin1String("z:") + path.mid(2);
+ return QLatin1String("z:") + path;
+}
+
+void SymbianCommonGenerator::generatePkgFile(const QString &iconFile,
+ bool epocBuild,
+ const SymbianLocalizationList &symbianLocalizationList)
+{
+ QMakeProject *project = generator->project;
+ QString pkgFilename = Option::output_dir + QLatin1Char('/') +
+ QString("%1_template.pkg").arg(fixedTarget);
+
+ QFile pkgFile(pkgFilename);
+ if (!pkgFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ PRINT_FILE_CREATE_ERROR(pkgFilename);
+ return;
+ }
+
+ QString stubPkgFileName = Option::output_dir + QLatin1Char('/') +
+ QString("%1_stub.pkg").arg(fixedTarget);
+
+ QFile stubPkgFile(stubPkgFileName);
+ if (!stubPkgFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ PRINT_FILE_CREATE_ERROR(stubPkgFileName);
+ return;
+ }
+
+ generatedFiles << pkgFile.fileName();
+ QTextStream t(&pkgFile);
+ generatedFiles << stubPkgFile.fileName();
+ QTextStream ts(&stubPkgFile);
+
+ QString installerSisHeader = project->values("DEPLOYMENT.installer_header").join("\n");
+ if (installerSisHeader.isEmpty()) {
+ // Use correct protected UID for publishing if application UID is in protected range,
+ // otherwise use self-signable test UID.
+ QRegExp protUidMatcher("0[xX][0-7].*");
+ if (protUidMatcher.exactMatch(uid3))
+ installerSisHeader = QLatin1String("0x2002CCCF");
+ else
+ installerSisHeader = QLatin1String("0xA000D7CE"); // Use default self-signable UID
+ }
+
+ QString wrapperStreamBuffer;
+ QTextStream tw(&wrapperStreamBuffer);
+
+ QString dateStr = QDateTime::currentDateTime().toString(Qt::ISODate);
+
+ // Header info
+ QString wrapperPkgFilename = Option::output_dir + QLatin1Char('/') + QString("%1_installer.%2")
+ .arg(fixedTarget).arg("pkg");
+
+ QString headerComment = "; %1 generated by qmake at %2\n"
+ "; This file is generated by qmake and should not be modified by the user\n"
+ ";\n\n";
+ t << headerComment.arg(pkgFilename).arg(dateStr);
+ tw << headerComment.arg(wrapperPkgFilename).arg(dateStr);
+ ts << headerComment.arg(stubPkgFileName).arg(dateStr);
+
+ QStringList commonRawPreRules;
+ QStringList mainRawPreRules;
+ QStringList instRawPreRules;
+ QStringList stubRawPreRules;
+
+ // Though there can't be more than one language or header line, use stringlists
+ // in case user wants comments to go with the rules.
+ // Note that it makes no sense to have file specific language or header rules,
+ // except what is provided for installer header via "DEPLOYMENT.installer_header" variable,
+ // because stub and main headers should always match. Vendor rules are similarly limited to
+ // make code cleaner as it is unlikely anyone will want different vendor in different files.
+ QStringList languageRules;
+ QStringList headerRules;
+ QStringList vendorRules;
+
+ QStringList commonRawPostRules;
+ QStringList mainRawPostRules;
+ QStringList instRawPostRules;
+ QStringList stubRawPostRules;
+
+ QStringList failList; // Used for detecting incorrect usage
+
+ QString emptySuffix;
+ QString mainSuffix(".main");
+ QString instSuffix(".installer");
+ QString stubSuffix(".stub");
+
+ foreach(QString item, project->values("DEPLOYMENT")) {
+ parsePreRules(item, emptySuffix, &commonRawPreRules, &languageRules, &headerRules, &vendorRules);
+ parsePreRules(item, mainSuffix, &mainRawPreRules, &failList, &failList, &failList);
+ parsePreRules(item, instSuffix, &instRawPreRules, &failList, &failList, &failList);
+ parsePreRules(item, stubSuffix, &stubRawPreRules, &failList, &failList, &failList);
+
+ parsePostRules(item, emptySuffix, &commonRawPostRules);
+ parsePostRules(item, mainSuffix, &mainRawPostRules);
+ parsePostRules(item, instSuffix, &instRawPostRules);
+ parsePostRules(item, stubSuffix, &stubRawPostRules);
+ }
+
+ if (!failList.isEmpty()) {
+ fprintf(stderr, "Warning: Custom language, header, or vendor definitions are not "
+ "supported by file specific pkg_prerules.* variables.\n"
+ "Use plain pkg_prerules and/or DEPLOYMENT.installer_header for customizing "
+ "these items.\n");
+ }
+
+ foreach(QString item, commonRawPreRules) {
+ if (item.startsWith("(")) {
+ // Only regular pkg file should have package dependencies
+ mainRawPreRules << item;
+ } else if (item.startsWith("[")) {
+ // stub pkg file should not have platform dependencies
+ mainRawPreRules << item;
+ instRawPreRules << item;
+ } else {
+ mainRawPreRules << item;
+ instRawPreRules << item;
+ stubRawPreRules << item;
+ }
+ }
+
+ // Currently common postrules only go to main
+ mainRawPostRules << commonRawPostRules;
+
+ // Apply some defaults if specific data does not exist in PKG pre-rules
+ if (languageRules.isEmpty()) {
+ if (symbianLocalizationList.isEmpty()) {
+ languageRules << "; Language\n&EN\n\n";
+ } else {
+ QStringList langCodes;
+ SymbianLocalizationListIterator iter(symbianLocalizationList);
+ while (iter.hasNext()) {
+ const SymbianLocalization &loc = iter.next();
+ langCodes << loc.symbianLanguageCode;
+ }
+ languageRules << QString("; Languages\n&%1\n\n").arg(langCodes.join(","));
+ }
+ } else if (headerRules.isEmpty()) {
+ // In case user defines langs, he must take care also about SIS header
+ fprintf(stderr, "Warning: If language is defined with DEPLOYMENT pkg_prerules, also the SIS header must be defined\n");
+ }
+
+ t << languageRules.join("\n") << endl;
+ tw << languageRules.join("\n") << endl;
+ ts << languageRules.join("\n") << endl;
+
+ // Determine application version. If version has missing component values,
+ // those will default to zero.
+ // If VERSION is missing altogether or is invalid, use "1,0,0"
+ QStringList verNumList = project->first("VERSION").split('.');
+ uint major = 0;
+ uint minor = 0;
+ uint patch = 0;
+ bool success = false;
+
+ if (verNumList.size() > 0) {
+ major = verNumList[0].toUInt(&success);
+ if (success && verNumList.size() > 1) {
+ minor = verNumList[1].toUInt(&success);
+ if (success && verNumList.size() > 2) {
+ patch = verNumList[2].toUInt(&success);
+ }
+ }
+ }
+
+ QString applicationVersion("1,0,0");
+ if (success)
+ applicationVersion = QString("%1,%2,%3").arg(major).arg(minor).arg(patch);
+
+ // Append package build version number if it is set
+ QString pkgBuildVersion = project->first("DEPLOYMENT.pkg_build_version");
+ if (!pkgBuildVersion.isEmpty()) {
+ success = false;
+ uint build = pkgBuildVersion.toUInt(&success);
+ if (success && build < 100) {
+ if (pkgBuildVersion.size() == 1)
+ pkgBuildVersion.prepend(QLatin1Char('0'));
+ applicationVersion.append(pkgBuildVersion);
+ } else {
+ fprintf(stderr, "Warning: Invalid DEPLOYMENT.pkg_build_version (%s), must be a number between 0 - 99\n", qPrintable(pkgBuildVersion));
+ }
+ }
+
+ // Package header
+ QString sisHeader = "; SIS header: name, uid, version\n#{\"%1\"},(%2),%3\n\n";
+
+ QString defaultVisualTarget = project->values("DEPLOYMENT.display_name").join(" ");
+ if (defaultVisualTarget.isEmpty())
+ defaultVisualTarget = generator->escapeFilePath(project->first("TARGET"));
+ defaultVisualTarget = removePathSeparators(defaultVisualTarget);
+
+ QString visualTarget = generatePkgNameForHeader(symbianLocalizationList, defaultVisualTarget, false);
+ QString wrapperTarget = generatePkgNameForHeader(symbianLocalizationList, defaultVisualTarget, true);
+
+ if (installerSisHeader.startsWith("0x", Qt::CaseInsensitive)) {
+ tw << sisHeader.arg(wrapperTarget).arg(installerSisHeader).arg(applicationVersion);
+ } else {
+ tw << installerSisHeader << endl;
+ }
+
+ if (headerRules.isEmpty()) {
+ t << sisHeader.arg(visualTarget).arg(uid3).arg(applicationVersion);
+ ts << sisHeader.arg(visualTarget).arg(uid3).arg(applicationVersion);
+ }
+ else {
+ t << headerRules.join("\n") << endl;
+ ts << headerRules.join("\n") << endl;
+ }
+
+ // Vendor name
+ if (!containsStartWithItem('%', vendorRules)) {
+ QString vendorStr = QLatin1String("\"Vendor\",");
+ QString locVendors = vendorStr;
+ for (int i = 1; i < symbianLocalizationList.size(); i++) {
+ locVendors.append(vendorStr);
+ }
+ locVendors.chop(1);
+ vendorRules << QString("; Default localized vendor name\n%{%1}\n\n").arg(locVendors);
+ }
+ if (!containsStartWithItem(':', vendorRules)) {
+ vendorRules << "; Default unique vendor name\n:\"Vendor\"\n\n";
+ }
+
+ t << vendorRules.join("\n") << endl;
+ tw << vendorRules.join("\n") << endl;
+ ts << vendorRules.join("\n") << endl;
+
+ // PKG pre-rules - these are added before actual file installations i.e. SIS package body
+ QString comment = "\n; Manual PKG pre-rules from PRO files\n";
+
+ if (mainRawPreRules.size()) {
+ t << comment;
+ t << mainRawPreRules.join("\n") << endl;
+ }
+ if (instRawPreRules.size()) {
+ tw << comment;
+ tw << instRawPreRules.join("\n") << endl;
+ }
+ if (stubRawPreRules.size()) {
+ ts << comment;
+ ts << stubRawPreRules.join("\n") << endl;
+ }
+
+ t << endl;
+ tw << endl;
+ ts << endl;
+
+ // Begin Manufacturer block
+ if (!project->values("DEPLOYMENT.manufacturers").isEmpty()) {
+ QString manufacturerStr("IF ");
+ foreach(QString manufacturer, project->values("DEPLOYMENT.manufacturers")) {
+ manufacturerStr.append(QString("(MANUFACTURER)=(%1) OR \n ").arg(manufacturer));
+ }
+ // Remove the final OR
+ manufacturerStr.chop(8);
+ t << manufacturerStr << endl;
+ }
+
+ if (symbianLocalizationList.size()) {
+ // Add localized resources to DEPLOYMENT if default resource deployment is done
+ addLocalizedResourcesToDeployment("default_resource_deployment.files", symbianLocalizationList);
+ addLocalizedResourcesToDeployment("default_reg_deployment.files", symbianLocalizationList);
+ }
+
+ // deploy files specified by DEPLOYMENT variable
+ QString remoteTestPath;
+ QString zDir;
+ remoteTestPath = QString("!:\\private\\%1").arg(privateDirUid);
+ if (epocBuild)
+ zDir = qt_epocRoot() + QLatin1String("epoc32/data/z");
+
+ DeploymentList depList;
+ initProjectDeploySymbian(project, depList, remoteTestPath, true, epocBuild, "$(PLATFORM)", "$(TARGET)", generatedDirs, generatedFiles);
+ if (depList.size())
+ t << "; DEPLOYMENT" << endl;
+ for (int i = 0; i < depList.size(); ++i) {
+ QString from = depList.at(i).from;
+ QString to = depList.at(i).to;
+ QString flags;
+ bool showOnlyFile = false;
+ foreach(QString flag, depList.at(i).flags) {
+ if (flag == QLatin1String("FT")
+ || flag == QLatin1String("FILETEXT")) {
+ showOnlyFile = true;
+ }
+ flags.append(QLatin1Char(',')).append(flag);
+ }
+
+ if (epocBuild) {
+ // Deploy anything not already deployed from under epoc32 instead from under
+ // \epoc32\data\z\ to enable using pkg file without rebuilding
+ // the project, which can be useful for some binary only distributions.
+ if (!from.contains(QLatin1String("epoc32"), Qt::CaseInsensitive)) {
+ from = to;
+ if (from.size() > 1 && from.at(1) == QLatin1Char(':'))
+ from = from.mid(2);
+ from.prepend(zDir);
+ }
+ }
+
+ // Files with "FILETEXT"/"FT" flag are meant for showing only at installation time
+ // and therefore do not belong to the stub package and will not install the file into phone.
+ if (showOnlyFile)
+ to.clear();
+ else
+ ts << QString("\"\" - \"%1\"").arg(romPath(to)) << endl;
+
+ t << QString("\"%1\" - \"%2\"%3").arg(from.replace('\\','/')).arg(to).arg(flags) << endl;
+
+ }
+ t << endl;
+ ts << endl;
+
+ // PKG post-rules - these are added after actual file installations i.e. SIS package body
+ comment = "; Manual PKG post-rules from PRO files\n";
+
+ if (mainRawPostRules.size()) {
+ t << comment;
+ t << mainRawPostRules.join("\n") << endl;
+ }
+ if (instRawPostRules.size()) {
+ tw << comment;
+ tw << instRawPostRules.join("\n") << endl;
+ }
+ if (stubRawPostRules.size()) {
+ ts << comment;
+ ts << stubRawPostRules.join("\n") << endl;
+ }
+
+ // Close Manufacturer block
+ if (!project->values("DEPLOYMENT.manufacturers").isEmpty()) {
+ QString manufacturerFailNoteFile;
+ if (project->values("DEPLOYMENT.manufacturers.fail_note").isEmpty()) {
+ manufacturerFailNoteFile = QString("%1_" MANUFACTURER_NOTE_FILE).arg(uid3);
+ QFile ft(manufacturerFailNoteFile);
+ if (ft.open(QIODevice::WriteOnly)) {
+ generatedFiles << ft.fileName();
+ QTextStream t2(&ft);
+
+ t2 << QString(DEFAULT_MANUFACTURER_NOTE) << endl;
+ } else {
+ PRINT_FILE_CREATE_ERROR(manufacturerFailNoteFile)
+ }
+ } else {
+ manufacturerFailNoteFile = project->values("DEPLOYMENT.manufacturers.fail_note").join("");
+ }
+
+ t << "ELSEIF NOT(0) ; MANUFACTURER" << endl
+ << "\"" << generator->fileInfo(manufacturerFailNoteFile).absoluteFilePath() << "\""
+ << " - \"\", FILETEXT, TEXTEXIT" << endl
+ << "ENDIF ; MANUFACTURER" << endl;
+ }
+
+ // Write wrapper pkg
+ if (!installerSisHeader.isEmpty()) {
+ QFile wrapperPkgFile(wrapperPkgFilename);
+ if (!wrapperPkgFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ PRINT_FILE_CREATE_ERROR(wrapperPkgFilename);
+ return;
+ }
+
+ generatedFiles << wrapperPkgFile.fileName();
+ QTextStream twf(&wrapperPkgFile);
+
+ twf << wrapperStreamBuffer << endl;
+
+ // Wrapped files deployment
+ QString currentPath = qmake_getpwd();
+ QString sisName = QString("%1.sis").arg(fixedTarget);
+ twf << "\"" << currentPath << "/" << sisName << "\" - \"!:\\private\\2002CCCE\\import\\" << sisName << "\"" << endl;
+
+ QString bootStrapPath = QLibraryInfo::location(QLibraryInfo::PrefixPath);
+ bootStrapPath.append("/smartinstaller.sis");
+ QFileInfo fi(generator->fileInfo(bootStrapPath));
+ twf << "@\"" << fi.absoluteFilePath() << "\",(0x2002CCCD)" << endl;
+ }
+}
+
+QString SymbianCommonGenerator::removePathSeparators(QString &file)
+{
+ QString ret = file;
+
+ if (QDir::separator().unicode() != '/')
+ ret.replace(QDir::separator(), QLatin1Char('/'));
+
+ if (ret.indexOf(QLatin1Char('/')) >= 0)
+ ret.remove(0, ret.lastIndexOf(QLatin1Char('/')) + 1);
+
+ return ret;
+}
+
+void SymbianCommonGenerator::writeRegRssFile(QMap<QString, QStringList> &userItems)
+{
+ QString filename(fixedTarget);
+ filename.append("_reg.rss");
+ if (!Option::output_dir.isEmpty())
+ filename = Option::output_dir + '/' + filename;
+ QFile ft(filename);
+ if (ft.open(QIODevice::WriteOnly)) {
+ generatedFiles << ft.fileName();
+ QTextStream t(&ft);
+ t << "// ============================================================================" << endl;
+ t << "// * Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: ";
+ t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl;
+ t << "// * This file is generated by qmake and should not be modified by the" << endl;
+ t << "// * user." << endl;
+ t << "// ============================================================================" << endl;
+ t << endl;
+ t << "#include <" << fixedTarget << ".rsg>" << endl;
+ t << "#include <appinfo.rh>" << endl;
+ foreach(QString item, userItems[RSS_TAG_HEADER])
+ t << item << endl;
+ t << endl;
+ t << "UID2 KUidAppRegistrationResourceFile" << endl;
+ t << "UID3 " << uid3 << endl << endl;
+ t << "RESOURCE APP_REGISTRATION_INFO" << endl;
+ t << "\t{" << endl;
+ t << "\tapp_file=\"" << fixedTarget << "\";" << endl;
+ t << "\tlocalisable_resource_file=\"" RESOURCE_DIRECTORY_RESOURCE << fixedTarget << "\";" << endl;
+
+ writeRegRssList(t, userItems[RSS_TAG_SERVICE_LIST],
+ QLatin1String(RSS_TAG_SERVICE_LIST),
+ QLatin1String("SERVICE_INFO"));
+ writeRegRssList(t, userItems[RSS_TAG_FILE_OWNERSHIP_LIST],
+ QLatin1String(RSS_TAG_FILE_OWNERSHIP_LIST),
+ QLatin1String("FILE_OWNERSHIP_INFO"));
+ writeRegRssList(t, userItems[RSS_TAG_DATATYPE_LIST],
+ QLatin1String(RSS_TAG_DATATYPE_LIST),
+ QLatin1String("DATATYPE"));
+ t << endl;
+
+ foreach(QString item, userItems[RSS_TAG_DEFAULT])
+ t << "\t" << item.replace("\n","\n\t") << endl;
+ t << "\t}" << endl;
+
+ foreach(QString item, userItems[RSS_TAG_FOOTER])
+ t << item << endl;
+ } else {
+ PRINT_FILE_CREATE_ERROR(filename)
+ }
+}
+
+void SymbianCommonGenerator::writeRegRssList(QTextStream &t,
+ QStringList &userList,
+ const QString &listTag,
+ const QString &listItem)
+{
+ int itemCount = userList.count();
+ if (itemCount) {
+ t << "\t" << listTag << " ="<< endl;
+ t << "\t\t{" << endl;
+ foreach(QString item, userList) {
+ t << "\t\t" << listItem << endl;
+ t << "\t\t\t{" << endl;
+ t << "\t\t\t" << item.replace("\n","\n\t\t\t") << endl;
+ t << "\t\t\t}";
+ if (--itemCount)
+ t << ",";
+ t << endl;
+ }
+ t << "\t\t}; "<< endl;
+ }
+}
+
+void SymbianCommonGenerator::writeRssFile(QString &numberOfIcons, QString &iconFile)
+{
+ QString filename(fixedTarget);
+ if (!Option::output_dir.isEmpty())
+ filename = Option::output_dir + '/' + filename;
+ filename.append(".rss");
+ QFile ft(filename);
+ if (ft.open(QIODevice::WriteOnly)) {
+ generatedFiles << ft.fileName();
+ QTextStream t(&ft);
+ t << "// ============================================================================" << endl;
+ t << "// * Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: ";
+ t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl;
+ t << "// * This file is generated by qmake and should not be modified by the" << endl;
+ t << "// * user." << endl;
+ t << "// ============================================================================" << endl;
+ t << endl;
+ t << "CHARACTER_SET UTF8" << endl;
+ t << "#include <appinfo.rh>" << endl;
+ t << "#include \"" << fixedTarget << ".loc\"" << endl;
+ t << endl;
+ t << "RESOURCE LOCALISABLE_APP_INFO r_localisable_app_info" << endl;
+ t << "\t{" << endl;
+ t << "\tshort_caption = STRING_r_short_caption;" << endl;
+ t << "\tcaption_and_icon =" << endl;
+ t << "\tCAPTION_AND_ICON_INFO" << endl;
+ t << "\t\t{" << endl;
+ t << "\t\tcaption = STRING_r_caption;" << endl;
+
+ QString rssIconFile = iconFile;
+ rssIconFile = rssIconFile.replace("/", "\\\\");
+
+ if (numberOfIcons.isEmpty() || rssIconFile.isEmpty()) {
+ // There can be maximum one item in this tag, validated when parsed
+ t << "\t\tnumber_of_icons = 0;" << endl;
+ t << "\t\ticon_file = \"\";" << endl;
+ } else {
+ // There can be maximum one item in this tag, validated when parsed
+ t << "\t\tnumber_of_icons = " << numberOfIcons << ";" << endl;
+ t << "\t\ticon_file = \"" << rssIconFile << "\";" << endl;
+ }
+ t << "\t\t};" << endl;
+ t << "\t}" << endl;
+ t << endl;
+ } else {
+ PRINT_FILE_CREATE_ERROR(filename);
+ }
+}
+
+void SymbianCommonGenerator::writeLocFile(const SymbianLocalizationList &symbianLocalizationList)
+{
+ QString filename = generateLocFileName();
+ QFile ft(filename);
+ if (ft.open(QIODevice::WriteOnly)) {
+ generatedFiles << ft.fileName();
+ QTextStream t(&ft);
+
+ QString displayName = generator->project->values("DEPLOYMENT.display_name").join(" ");
+ if (displayName.isEmpty())
+ displayName = generator->escapeFilePath(generator->project->first("TARGET"));
+
+ t << "// ============================================================================" << endl;
+ t << "// * Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: ";
+ t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl;
+ t << "// * This file is generated by qmake and should not be modified by the" << endl;
+ t << "// * user." << endl;
+ t << "// ============================================================================" << endl;
+ t << endl;
+ t << "#ifdef LANGUAGE_SC" << endl;
+ t << "#define STRING_r_short_caption \"" << displayName << "\"" << endl;
+ t << "#define STRING_r_caption \"" << displayName << "\"" << endl;
+
+ SymbianLocalizationListIterator iter(symbianLocalizationList);
+ while (iter.hasNext()) {
+ const SymbianLocalization &loc = iter.next();
+ QString shortCaption = loc.shortCaption;
+ QString longCaption = loc.longCaption;
+ if (shortCaption.isEmpty())
+ shortCaption = displayName;
+ if (longCaption.isEmpty())
+ longCaption = displayName;
+
+ t << "#elif defined LANGUAGE_" << loc.symbianLanguageCode << endl;
+ t << "#define STRING_r_short_caption \"" << shortCaption << "\"" << endl;
+ t << "#define STRING_r_caption \"" << longCaption << "\"" << endl;
+ }
+
+ t << "#else" << endl;
+ t << "#define STRING_r_short_caption \"" << displayName << "\"" << endl;
+ t << "#define STRING_r_caption \"" << displayName << "\"" << endl;
+ t << "#endif" << endl;
+ } else {
+ PRINT_FILE_CREATE_ERROR(filename);
+ }
+}
+
+void SymbianCommonGenerator::readRssRules(QString &numberOfIcons,
+ QString &iconFile, QMap<QString,
+ QStringList> &userRssRules)
+{
+ QMakeProject *project = generator->project;
+ for (QMap<QString, QStringList>::iterator it = project->variables().begin(); it != project->variables().end(); ++it) {
+ if (it.key().startsWith(RSS_RULES_BASE)) {
+ QString newKey = it.key().mid(sizeof(RSS_RULES_BASE) - 1);
+ if (newKey.isEmpty()) {
+ fprintf(stderr, "Warning: Empty RSS_RULES_BASE key encountered\n");
+ continue;
+ }
+ QStringList newValues;
+ QStringList values = it.value();
+ foreach(QString item, values) {
+ // If there is no stringlist defined for a rule, use rule value directly
+ // This is convenience for defining single line statements
+ if (project->values(item).isEmpty()) {
+ newValues << item;
+ } else {
+ QStringList itemList;
+ foreach(QString itemRow, project->values(item)) {
+ itemList << itemRow;
+ }
+ newValues << itemList.join("\n");
+ }
+ }
+ // Verify that there is exactly one value in RSS_TAG_NBROFICONS
+ if (newKey == RSS_TAG_NBROFICONS) {
+ if (newValues.count() == 1) {
+ numberOfIcons = newValues[0];
+ } else {
+ fprintf(stderr, "Warning: There must be exactly one value in '%s%s'\n",
+ RSS_RULES_BASE, RSS_TAG_NBROFICONS);
+ continue;
+ }
+ // Verify that there is exactly one value in RSS_TAG_ICONFILE
+ } else if (newKey == RSS_TAG_ICONFILE) {
+ if (newValues.count() == 1) {
+ iconFile = newValues[0];
+ } else {
+ fprintf(stderr, "Warning: There must be exactly one value in '%s%s'\n",
+ RSS_RULES_BASE, RSS_TAG_ICONFILE);
+ continue;
+ }
+ } else if (newKey == RSS_TAG_HEADER
+ || newKey == RSS_TAG_SERVICE_LIST
+ || newKey == RSS_TAG_FILE_OWNERSHIP_LIST
+ || newKey == RSS_TAG_DATATYPE_LIST
+ || newKey == RSS_TAG_FOOTER
+ || newKey == RSS_TAG_DEFAULT) {
+ userRssRules[newKey] = newValues;
+ continue;
+ } else {
+ fprintf(stderr, "Warning: Unsupported key:'%s%s'\n",
+ RSS_RULES_BASE, newKey.toLatin1().constData());
+ continue;
+ }
+ }
+ }
+
+ QStringList newValues;
+ foreach(QString item, project->values(RSS_RULES)) {
+ // If there is no stringlist defined for a rule, use rule value directly
+ // This is convenience for defining single line statements
+ if (project->values(item).isEmpty()) {
+ newValues << item;
+ } else {
+ newValues << project->values(item);
+ }
+ }
+ userRssRules[RSS_TAG_DEFAULT] << newValues;
+
+ // Validate that either both RSS_TAG_NBROFICONS and RSS_TAG_ICONFILE keys exist
+ // or neither of them exist
+ if (!((numberOfIcons.isEmpty() && iconFile.isEmpty()) ||
+ (!numberOfIcons.isEmpty() && !iconFile.isEmpty()))) {
+ numberOfIcons.clear();
+ iconFile.clear();
+ fprintf(stderr, "Warning: Both or neither of '%s%s' and '%s%s' keys must exist.\n",
+ RSS_RULES_BASE, RSS_TAG_NBROFICONS, RSS_RULES_BASE, RSS_TAG_ICONFILE);
+ }
+
+ // Validate that RSS_TAG_NBROFICONS contains only numbers
+ if (!numberOfIcons.isEmpty()) {
+ bool ok;
+ numberOfIcons = numberOfIcons.simplified();
+ numberOfIcons.toInt(&ok);
+ if (!ok) {
+ numberOfIcons.clear();
+ iconFile.clear();
+ fprintf(stderr, "Warning: '%s%s' must be integer in decimal format.\n",
+ RSS_RULES_BASE, RSS_TAG_NBROFICONS);
+ }
+ }
+}
+
+void SymbianCommonGenerator::writeCustomDefFile()
+{
+ if (targetType == TypePlugin && !generator->project->isActiveConfig("stdbinary")) {
+ // Create custom def file for plugin
+ QFile ft(Option::output_dir + QLatin1Char('/') + QLatin1String(PLUGIN_COMMON_DEF_FILE_ACTUAL));
+
+ if (ft.open(QIODevice::WriteOnly)) {
+ generatedFiles << ft.fileName();
+ QTextStream t(&ft);
+
+ t << "; ==============================================================================" << endl;
+ t << "; Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: ";
+ t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl;
+ t << "; This file is generated by qmake and should not be modified by the" << endl;
+ t << "; user." << endl;
+ t << "; Name : " PLUGIN_COMMON_DEF_FILE_ACTUAL << endl;
+ t << "; Part of : " << generator->project->values("TARGET").join(" ") << endl;
+ t << "; Description : Fixes common plugin symbols to known ordinals" << endl;
+ t << "; Version : " << endl;
+ t << ";" << endl;
+ t << "; ==============================================================================" << "\n" << endl;
+
+ t << endl;
+
+ t << "EXPORTS" << endl;
+ t << "\tqt_plugin_query_verification_data @ 1 NONAME" << endl;
+ t << "\tqt_plugin_instance @ 2 NONAME" << endl;
+ t << endl;
+ } else {
+ PRINT_FILE_CREATE_ERROR(QString(PLUGIN_COMMON_DEF_FILE_ACTUAL))
+ }
+ }
+}
+
+void SymbianCommonGenerator::parseTsFiles(SymbianLocalizationList *symbianLocalizationList)
+{
+ if (!generator->project->isActiveConfig("localize_deployment")) {
+ return;
+ }
+
+ QStringList symbianTsFiles;
+
+ symbianTsFiles << generator->project->values("SYMBIAN_MATCHED_TRANSLATIONS");
+
+ if (!symbianTsFiles.isEmpty()) {
+ fillQt2SymbianLocalizationList(symbianLocalizationList);
+
+ QMutableListIterator<SymbianLocalization> iter(*symbianLocalizationList);
+ while (iter.hasNext()) {
+ SymbianLocalization &loc = iter.next();
+ static QString matchStrTemplate = QLatin1String(".*_%1\\.ts");
+ QString matchStr = matchStrTemplate.arg(loc.qtLanguageCode);
+
+ foreach (QString file, symbianTsFiles) {
+ QRegExp matcher(matchStr);
+ if (matcher.exactMatch(file) && parseTsContent(file, &loc))
+ break;
+ }
+ }
+ }
+}
+
+void SymbianCommonGenerator::fillQt2SymbianLocalizationList(SymbianLocalizationList *symbianLocalizationList)
+{
+ static QString symbianCodePrefix = QLatin1String("SYMBIAN_LANG.");
+
+ QStringList symbianLanguages = generator->project->values("SYMBIAN_MATCHED_LANGUAGES");
+
+ foreach (QString qtCode, symbianLanguages) {
+ SymbianLocalization newLoc;
+ QString symbianCodeVariable = symbianCodePrefix + qtCode;
+ newLoc.symbianLanguageCode = generator->project->first(symbianCodeVariable);
+ if (!newLoc.symbianLanguageCode.isEmpty()) {
+ newLoc.qtLanguageCode = qtCode;
+ symbianLocalizationList->append(newLoc);
+ }
+ }
+}
+
+void SymbianCommonGenerator::parsePreRules(const QString &deploymentVariable,
+ const QString &variableSuffix,
+ QStringList *rawRuleList,
+ QStringList *languageRuleList,
+ QStringList *headerRuleList,
+ QStringList *vendorRuleList)
+{
+ QMakeProject *project = generator->project;
+ foreach(QString pkgrulesItem, project->values(deploymentVariable + ".pkg_prerules" + variableSuffix)) {
+ QStringList pkgrulesValue = project->values(pkgrulesItem);
+ // If there is no stringlist defined for a rule, use rule name directly
+ // This is convenience for defining single line statements
+ if (pkgrulesValue.isEmpty()) {
+ if (pkgrulesItem.startsWith("&"))
+ *languageRuleList << pkgrulesItem;
+ else if (pkgrulesItem.startsWith("#"))
+ *headerRuleList << pkgrulesItem;
+ else if (pkgrulesItem.startsWith("%") || pkgrulesItem.startsWith(":"))
+ *vendorRuleList << pkgrulesItem;
+ else
+ *rawRuleList << pkgrulesItem;
+ } else {
+ if (containsStartWithItem('&', pkgrulesValue)) {
+ foreach(QString pkgrule, pkgrulesValue) {
+ *languageRuleList << pkgrule;
+ }
+ } else if (containsStartWithItem('#', pkgrulesValue)) {
+ foreach(QString pkgrule, pkgrulesValue) {
+ *headerRuleList << pkgrule;
+ }
+ } else if (containsStartWithItem('%', pkgrulesValue)
+ || containsStartWithItem(':', pkgrulesValue)) {
+ foreach(QString pkgrule, pkgrulesValue) {
+ *vendorRuleList << pkgrule;
+ }
+ } else {
+ foreach(QString pkgrule, pkgrulesValue) {
+ *rawRuleList << pkgrule;
+ }
+ }
+ }
+ }
+}
+
+void SymbianCommonGenerator::parsePostRules(const QString &deploymentVariable,
+ const QString &variableSuffix,
+ QStringList *rawRuleList)
+{
+ QMakeProject *project = generator->project;
+ foreach(QString pkgrulesItem, project->values(deploymentVariable + ".pkg_postrules" + variableSuffix)) {
+ QStringList pkgrulesValue = project->values(pkgrulesItem);
+ // If there is no stringlist defined for a rule, use rule name directly
+ // This is convenience for defining single line statements
+ if (pkgrulesValue.isEmpty()) {
+ *rawRuleList << pkgrulesItem;
+ } else {
+ foreach(QString pkgrule, pkgrulesValue) {
+ *rawRuleList << pkgrule;
+ }
+ }
+ }
+}
+
+bool SymbianCommonGenerator::parseTsContent(const QString &tsFilename, SymbianLocalization *loc)
+{
+ bool retval = true;
+ QMakeProject *project = generator->project;
+ QFile tsFile(tsFilename);
+
+ if (tsFile.exists()) {
+ if (tsFile.open(QIODevice::ReadOnly)) {
+ static QString applicationCaptionsContext = QLatin1String("QtApplicationCaptions");
+ static QString pkgNameContext = QLatin1String("QtPackageNames");
+ static QString tsElement = QLatin1String("TS");
+ static QString contextElement = QLatin1String("context");
+ static QString nameElement = QLatin1String("name");
+ static QString messageElement = QLatin1String("message");
+ static QString sourceElement = QLatin1String("source");
+ static QString translationElement = QLatin1String("translation");
+ static QString shortCaptionId = QLatin1String("Application short caption");
+ static QString longCaptionId = QLatin1String("Application long caption");
+ static QString pkgDisplayNameId = QLatin1String("Package name");
+ static QString installerPkgDisplayNameId = QLatin1String("Smart installer package name");
+ static QString languageAttribute = QLatin1String("language");
+ static QChar underscoreChar = QLatin1Char('_');
+
+ enum CurrentContext {
+ ContextUnknown,
+ ContextUninteresting,
+ ContextInteresting
+ };
+
+ QXmlStreamReader xml(&tsFile);
+
+ while (!xml.atEnd() && xml.name() != tsElement)
+ xml.readNextStartElement();
+
+ while (xml.readNextStartElement()) {
+ if (xml.name() == contextElement) {
+ CurrentContext currentContext = ContextUnknown;
+ while (xml.readNextStartElement()) {
+ if (currentContext == ContextUnknown) {
+ // Expect name element before message elements
+ if (xml.name() == nameElement) {
+ QString nameText = xml.readElementText();
+ if (nameText == applicationCaptionsContext || nameText == pkgNameContext) {
+ currentContext = ContextInteresting;
+ } else {
+ currentContext = ContextUninteresting;
+ }
+ } else {
+ xml.skipCurrentElement();
+ }
+ } else if (currentContext == ContextInteresting) {
+ if (xml.name() == messageElement) {
+ QString source;
+ QString translation;
+ while (xml.readNextStartElement()) {
+ if (xml.name() == sourceElement) {
+ source = xml.readElementText();
+ } else if (xml.name() == translationElement) {
+ translation = xml.readElementText();
+ } else {
+ xml.skipCurrentElement();
+ }
+ }
+
+ if (source == shortCaptionId) {
+ if (loc->shortCaption.isEmpty()) {
+ loc->shortCaption = translation;
+ } else {
+ fprintf(stderr, "Warning: Duplicate application short caption defined in (%s).\n",
+ qPrintable(tsFilename));
+ }
+ } else if (source == longCaptionId) {
+ if (loc->longCaption.isEmpty()) {
+ loc->longCaption = translation;
+ } else {
+ fprintf(stderr, "Warning: Duplicate application long caption defined in (%s).\n",
+ qPrintable(tsFilename));
+ }
+ } else if (source == pkgDisplayNameId) {
+ if (loc->pkgDisplayName.isEmpty()) {
+ loc->pkgDisplayName = translation;
+ } else {
+ fprintf(stderr, "Warning: Duplicate package display name defined in (%s).\n",
+ qPrintable(tsFilename));
+ }
+ } else if (source == installerPkgDisplayNameId) {
+ if (loc->installerPkgDisplayName.isEmpty()) {
+ loc->installerPkgDisplayName = translation;
+ } else {
+ fprintf(stderr, "Warning: Duplicate smart installer package display name defined in (%s).\n",
+ qPrintable(tsFilename));
+ }
+ }
+ } else {
+ xml.skipCurrentElement();
+ }
+ } else {
+ xml.skipCurrentElement();
+ }
+ }
+ } else {
+ xml.skipCurrentElement();
+ }
+ }
+ if (xml.hasError()) {
+ retval = false;
+ fprintf(stderr, "ERROR: Encountered error \"%s\" when parsing ts file (%s).\n",
+ qPrintable(xml.errorString()), qPrintable(tsFilename));
+ }
+ } else {
+ retval = false;
+ fprintf(stderr, "Warning: Could not open ts file (%s).\n", qPrintable(tsFilename));
+ }
+ } else {
+ retval = false;
+ fprintf(stderr, "Warning: ts file does not exist: (%s), unable to parse it.\n",
+ qPrintable(tsFilename));
+ }
+
+ return retval;
+}
+
+QString SymbianCommonGenerator::generatePkgNameForHeader(const SymbianLocalizationList &symbianLocalizationList,
+ const QString &defaultName,
+ bool isForSmartInstaller)
+{
+ QStringList allNames;
+ QString noTranslation = defaultName;
+
+ if (isForSmartInstaller)
+ noTranslation += QLatin1String(" installer");
+
+ SymbianLocalizationListIterator iter(symbianLocalizationList);
+ while (iter.hasNext()) {
+ const SymbianLocalization &loc = iter.next();
+ QString currentName;
+ if (isForSmartInstaller) {
+ currentName = loc.installerPkgDisplayName;
+ } else {
+ currentName = loc.pkgDisplayName;
+ }
+
+ if (currentName.isEmpty())
+ currentName = noTranslation;
+
+ allNames << currentName;
+ }
+
+ if (!allNames.size())
+ allNames << noTranslation;
+
+ return allNames.join("\",\"");
+
+}
+
+void SymbianCommonGenerator::addLocalizedResourcesToDeployment(const QString &deploymentFilesVar,
+ const SymbianLocalizationList &symbianLocalizationList)
+{
+ QStringList locResources;
+ foreach (QString defaultResource, generator->project->values(deploymentFilesVar)) {
+ if (defaultResource.endsWith(".rsc")) {
+ defaultResource.chop(2);
+ SymbianLocalizationListIterator iter(symbianLocalizationList);
+ while (iter.hasNext()) {
+ const SymbianLocalization &loc = iter.next();
+ locResources << QString(defaultResource + loc.symbianLanguageCode);
+ }
+ }
+ }
+ generator->project->values(deploymentFilesVar) << locResources;
+}
+
+QString SymbianCommonGenerator::generateLocFileName()
+{
+ QString fileName(fixedTarget);
+ if (!Option::output_dir.isEmpty())
+ fileName = Option::output_dir + QLatin1Char('/') + fileName;
+ fileName.append(".loc");
+ return fileName;
+}