diff options
Diffstat (limited to 'qmake/generators/symbian/symbiancommon.cpp')
-rw-r--r-- | qmake/generators/symbian/symbiancommon.cpp | 1117 |
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; +} |