source: trunk/qmake/generators/symbian/symbiancommon.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 46.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "symbiancommon.h"
43#include <qdebug.h>
44#include <qxmlstream.h>
45
46// Included from tools/shared
47#include <symbian/epocroot_p.h>
48
49#define RESOURCE_DIRECTORY_RESOURCE "\\\\resource\\\\apps\\\\"
50
51#define RSS_RULES "RSS_RULES"
52#define RSS_RULES_BASE "RSS_RULES."
53#define RSS_TAG_NBROFICONS "number_of_icons"
54#define RSS_TAG_ICONFILE "icon_file"
55#define RSS_TAG_HEADER "header"
56#define RSS_TAG_SERVICE_LIST "service_list"
57#define RSS_TAG_FILE_OWNERSHIP_LIST "file_ownership_list"
58#define RSS_TAG_DATATYPE_LIST "datatype_list"
59#define RSS_TAG_FOOTER "footer"
60#define RSS_TAG_DEFAULT "default_rules" // Same as just giving rules without tag
61
62#define PLUGIN_COMMON_DEF_FILE_ACTUAL "plugin_commonu.def"
63
64#define MANUFACTURER_NOTE_FILE "manufacturer_note.txt"
65#define DEFAULT_MANUFACTURER_NOTE \
66 "The package is not supported for devices from this manufacturer. Please try the selfsigned " \
67 "version of the package instead."
68
69SymbianCommonGenerator::SymbianCommonGenerator(MakefileGenerator *generator)
70 : generator(generator)
71{
72}
73
74void SymbianCommonGenerator::init()
75{
76 QMakeProject *project = generator->project;
77 fixedTarget = project->first("QMAKE_ORIG_TARGET");
78 if (fixedTarget.isEmpty())
79 fixedTarget = project->first("TARGET");
80 fixedTarget = generator->unescapeFilePath(fixedTarget);
81 fixedTarget = removePathSeparators(fixedTarget);
82 removeSpecialCharacters(fixedTarget);
83
84 // This should not be empty since the mkspecs are supposed to set it if missing.
85 uid3 = project->first("TARGET.UID3").trimmed();
86
87 if ((project->values("TEMPLATE")).contains("app"))
88 targetType = TypeExe;
89 else if ((project->values("TEMPLATE")).contains("lib")) {
90 // Check CONFIG to see if we are to build staticlib or dll
91 if (project->isActiveConfig("staticlib") || project->isActiveConfig("static"))
92 targetType = TypeLib;
93 else if (project->isActiveConfig("plugin"))
94 targetType = TypePlugin;
95 else
96 targetType = TypeDll;
97 } else {
98 targetType = TypeSubdirs;
99 }
100
101 // UID is valid as either hex or decimal, so just convert it to number and back to hex
102 // to get proper string for private dir
103 bool conversionOk = false;
104 uint uidNum = uid3.toUInt(&conversionOk, 0);
105
106 if (!conversionOk) {
107 fprintf(stderr, "Error: Invalid UID \"%s\".\n", uid3.toUtf8().constData());
108 } else {
109 privateDirUid.setNum(uidNum, 16);
110 while (privateDirUid.length() < 8)
111 privateDirUid.insert(0, QLatin1Char('0'));
112 }
113}
114
115bool SymbianCommonGenerator::containsStartWithItem(const QChar &c, const QStringList& src)
116{
117 bool result = false;
118 foreach(QString str, src) {
119 if (str.startsWith(c)) {
120 result = true;
121 break;
122 }
123 }
124 return result;
125}
126
127void SymbianCommonGenerator::removeSpecialCharacters(QString& str)
128{
129 // When modifying this method check also symbianRemoveSpecialCharacters in symbian.conf
130 QString underscore = QLatin1String("_");
131 str.replace(QLatin1String("/"), underscore);
132 str.replace(QLatin1String("\\"), underscore);
133 str.replace(QLatin1String(" "), underscore);
134 str.replace(QLatin1String(":"), underscore);
135}
136
137QString romPath(const QString& path)
138{
139 if(path.length() > 2 && path[1] == ':')
140 return QLatin1String("z:") + path.mid(2);
141 return QLatin1String("z:") + path;
142}
143
144void SymbianCommonGenerator::generatePkgFile(const QString &iconFile,
145 bool epocBuild,
146 const SymbianLocalizationList &symbianLocalizationList)
147{
148 QMakeProject *project = generator->project;
149 QString pkgFilename = Option::output_dir + QLatin1Char('/') +
150 QString("%1_template.pkg").arg(fixedTarget);
151
152 QFile pkgFile(pkgFilename);
153 if (!pkgFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
154 PRINT_FILE_CREATE_ERROR(pkgFilename);
155 return;
156 }
157
158 QString stubPkgFileName = Option::output_dir + QLatin1Char('/') +
159 QString("%1_stub.pkg").arg(fixedTarget);
160
161 QFile stubPkgFile(stubPkgFileName);
162 if (!stubPkgFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
163 PRINT_FILE_CREATE_ERROR(stubPkgFileName);
164 return;
165 }
166
167 generatedFiles << pkgFile.fileName();
168 QTextStream t(&pkgFile);
169 generatedFiles << stubPkgFile.fileName();
170 QTextStream ts(&stubPkgFile);
171
172 QString installerSisHeader = project->values("DEPLOYMENT.installer_header").join("\n");
173 if (installerSisHeader.isEmpty()) {
174 // Use correct protected UID for publishing if application UID is in protected range,
175 // otherwise use self-signable test UID.
176 QRegExp protUidMatcher("0[xX][0-7].*");
177 if (protUidMatcher.exactMatch(uid3))
178 installerSisHeader = QLatin1String("0x2002CCCF");
179 else
180 installerSisHeader = QLatin1String("0xA000D7CE"); // Use default self-signable UID
181 }
182
183 QString wrapperStreamBuffer;
184 QTextStream tw(&wrapperStreamBuffer);
185
186 QString dateStr = QDateTime::currentDateTime().toString(Qt::ISODate);
187
188 // Header info
189 QString wrapperPkgFilename = Option::output_dir + QLatin1Char('/') + QString("%1_installer.%2")
190 .arg(fixedTarget).arg("pkg");
191
192 QString headerComment = "; %1 generated by qmake at %2\n"
193 "; This file is generated by qmake and should not be modified by the user\n"
194 ";\n\n";
195 t << headerComment.arg(pkgFilename).arg(dateStr);
196 tw << headerComment.arg(wrapperPkgFilename).arg(dateStr);
197 ts << headerComment.arg(stubPkgFileName).arg(dateStr);
198
199 QStringList commonRawPreRules;
200 QStringList mainRawPreRules;
201 QStringList instRawPreRules;
202 QStringList stubRawPreRules;
203
204 // Though there can't be more than one language or header line, use stringlists
205 // in case user wants comments to go with the rules.
206 // Note that it makes no sense to have file specific language or header rules,
207 // except what is provided for installer header via "DEPLOYMENT.installer_header" variable,
208 // because stub and main headers should always match. Vendor rules are similarly limited to
209 // make code cleaner as it is unlikely anyone will want different vendor in different files.
210 QStringList languageRules;
211 QStringList headerRules;
212 QStringList vendorRules;
213
214 QStringList commonRawPostRules;
215 QStringList mainRawPostRules;
216 QStringList instRawPostRules;
217 QStringList stubRawPostRules;
218
219 QStringList failList; // Used for detecting incorrect usage
220
221 QString emptySuffix;
222 QString mainSuffix(".main");
223 QString instSuffix(".installer");
224 QString stubSuffix(".stub");
225
226 foreach(QString item, project->values("DEPLOYMENT")) {
227 parsePreRules(item, emptySuffix, &commonRawPreRules, &languageRules, &headerRules, &vendorRules);
228 parsePreRules(item, mainSuffix, &mainRawPreRules, &failList, &failList, &failList);
229 parsePreRules(item, instSuffix, &instRawPreRules, &failList, &failList, &failList);
230 parsePreRules(item, stubSuffix, &stubRawPreRules, &failList, &failList, &failList);
231
232 parsePostRules(item, emptySuffix, &commonRawPostRules);
233 parsePostRules(item, mainSuffix, &mainRawPostRules);
234 parsePostRules(item, instSuffix, &instRawPostRules);
235 parsePostRules(item, stubSuffix, &stubRawPostRules);
236 }
237
238 if (!failList.isEmpty()) {
239 fprintf(stderr, "Warning: Custom language, header, or vendor definitions are not "
240 "supported by file specific pkg_prerules.* variables.\n"
241 "Use plain pkg_prerules and/or DEPLOYMENT.installer_header for customizing "
242 "these items.\n");
243 }
244
245 foreach(QString item, commonRawPreRules) {
246 if (item.startsWith("(")) {
247 // Only regular pkg file should have package dependencies
248 mainRawPreRules << item;
249 } else if (item.startsWith("[")) {
250 // stub pkg file should not have platform dependencies
251 mainRawPreRules << item;
252 instRawPreRules << item;
253 } else {
254 mainRawPreRules << item;
255 instRawPreRules << item;
256 stubRawPreRules << item;
257 }
258 }
259
260 // Currently common postrules only go to main
261 mainRawPostRules << commonRawPostRules;
262
263 // Apply some defaults if specific data does not exist in PKG pre-rules
264 if (languageRules.isEmpty()) {
265 if (symbianLocalizationList.isEmpty()) {
266 languageRules << "; Language\n&EN\n\n";
267 } else {
268 QStringList langCodes;
269 SymbianLocalizationListIterator iter(symbianLocalizationList);
270 while (iter.hasNext()) {
271 const SymbianLocalization &loc = iter.next();
272 langCodes << loc.symbianLanguageCode;
273 }
274 languageRules << QString("; Languages\n&%1\n\n").arg(langCodes.join(","));
275 }
276 } else if (headerRules.isEmpty()) {
277 // In case user defines langs, he must take care also about SIS header
278 fprintf(stderr, "Warning: If language is defined with DEPLOYMENT pkg_prerules, also the SIS header must be defined\n");
279 }
280
281 t << languageRules.join("\n") << endl;
282 tw << languageRules.join("\n") << endl;
283 ts << languageRules.join("\n") << endl;
284
285 // Determine application version. If version has missing component values,
286 // those will default to zero.
287 // If VERSION is missing altogether or is invalid, use "1,0,0"
288 QStringList verNumList = project->first("VERSION").split('.');
289 uint major = 0;
290 uint minor = 0;
291 uint patch = 0;
292 bool success = false;
293
294 if (verNumList.size() > 0) {
295 major = verNumList[0].toUInt(&success);
296 if (success && verNumList.size() > 1) {
297 minor = verNumList[1].toUInt(&success);
298 if (success && verNumList.size() > 2) {
299 patch = verNumList[2].toUInt(&success);
300 }
301 }
302 }
303
304 QString applicationVersion("1,0,0");
305 if (success)
306 applicationVersion = QString("%1,%2,%3").arg(major).arg(minor).arg(patch);
307
308 // Append package build version number if it is set
309 QString pkgBuildVersion = project->first("DEPLOYMENT.pkg_build_version");
310 if (!pkgBuildVersion.isEmpty()) {
311 success = false;
312 uint build = pkgBuildVersion.toUInt(&success);
313 if (success && build < 100) {
314 if (pkgBuildVersion.size() == 1)
315 pkgBuildVersion.prepend(QLatin1Char('0'));
316 applicationVersion.append(pkgBuildVersion);
317 } else {
318 fprintf(stderr, "Warning: Invalid DEPLOYMENT.pkg_build_version (%s), must be a number between 0 - 99\n", qPrintable(pkgBuildVersion));
319 }
320 }
321
322 // Package header
323 QString sisHeader = "; SIS header: name, uid, version\n#{\"%1\"},(%2),%3\n\n";
324
325 QString defaultVisualTarget = project->values("DEPLOYMENT.display_name").join(" ");
326 if (defaultVisualTarget.isEmpty())
327 defaultVisualTarget = generator->escapeFilePath(project->first("TARGET"));
328 defaultVisualTarget = removePathSeparators(defaultVisualTarget);
329
330 QString visualTarget = generatePkgNameForHeader(symbianLocalizationList, defaultVisualTarget, false);
331 QString wrapperTarget = generatePkgNameForHeader(symbianLocalizationList, defaultVisualTarget, true);
332
333 if (installerSisHeader.startsWith("0x", Qt::CaseInsensitive)) {
334 tw << sisHeader.arg(wrapperTarget).arg(installerSisHeader).arg(applicationVersion);
335 } else {
336 tw << installerSisHeader << endl;
337 }
338
339 if (headerRules.isEmpty()) {
340 t << sisHeader.arg(visualTarget).arg(uid3).arg(applicationVersion);
341 ts << sisHeader.arg(visualTarget).arg(uid3).arg(applicationVersion);
342 }
343 else {
344 t << headerRules.join("\n") << endl;
345 ts << headerRules.join("\n") << endl;
346 }
347
348 // Vendor name
349 if (!containsStartWithItem('%', vendorRules)) {
350 QString vendorStr = QLatin1String("\"Vendor\",");
351 QString locVendors = vendorStr;
352 for (int i = 1; i < symbianLocalizationList.size(); i++) {
353 locVendors.append(vendorStr);
354 }
355 locVendors.chop(1);
356 vendorRules << QString("; Default localized vendor name\n%{%1}\n\n").arg(locVendors);
357 }
358 if (!containsStartWithItem(':', vendorRules)) {
359 vendorRules << "; Default unique vendor name\n:\"Vendor\"\n\n";
360 }
361
362 t << vendorRules.join("\n") << endl;
363 tw << vendorRules.join("\n") << endl;
364 ts << vendorRules.join("\n") << endl;
365
366 // PKG pre-rules - these are added before actual file installations i.e. SIS package body
367 QString comment = "\n; Manual PKG pre-rules from PRO files\n";
368
369 if (mainRawPreRules.size()) {
370 t << comment;
371 t << mainRawPreRules.join("\n") << endl;
372 }
373 if (instRawPreRules.size()) {
374 tw << comment;
375 tw << instRawPreRules.join("\n") << endl;
376 }
377 if (stubRawPreRules.size()) {
378 ts << comment;
379 ts << stubRawPreRules.join("\n") << endl;
380 }
381
382 t << endl;
383 tw << endl;
384 ts << endl;
385
386 // Begin Manufacturer block
387 if (!project->values("DEPLOYMENT.manufacturers").isEmpty()) {
388 QString manufacturerStr("IF ");
389 foreach(QString manufacturer, project->values("DEPLOYMENT.manufacturers")) {
390 manufacturerStr.append(QString("(MANUFACTURER)=(%1) OR \n ").arg(manufacturer));
391 }
392 // Remove the final OR
393 manufacturerStr.chop(8);
394 t << manufacturerStr << endl;
395 }
396
397 // ### FIXME: remove epocBuild check once makefile based mkspecs support localized resource generation
398 if (epocBuild && symbianLocalizationList.size()) {
399 // Add localized resources to DEPLOYMENT if default resource deployment is done
400 addLocalizedResourcesToDeployment("default_resource_deployment.sources", symbianLocalizationList);
401 addLocalizedResourcesToDeployment("default_reg_deployment.sources", symbianLocalizationList);
402 }
403
404 // deploy files specified by DEPLOYMENT variable
405 QString remoteTestPath;
406 QString zDir;
407 remoteTestPath = QString("!:\\private\\%1").arg(privateDirUid);
408 if (epocBuild)
409 zDir = qt_epocRoot() + QLatin1String("epoc32/data/z");
410
411 DeploymentList depList;
412 initProjectDeploySymbian(project, depList, remoteTestPath, true, epocBuild, "$(PLATFORM)", "$(TARGET)", generatedDirs, generatedFiles);
413 if (depList.size())
414 t << "; DEPLOYMENT" << endl;
415 for (int i = 0; i < depList.size(); ++i) {
416 QString from = depList.at(i).from;
417 QString to = depList.at(i).to;
418 QString flags;
419 bool showOnlyFile = false;
420 foreach(QString flag, depList.at(i).flags) {
421 if (flag == QLatin1String("FT")
422 || flag == QLatin1String("FILETEXT")) {
423 showOnlyFile = true;
424 }
425 flags.append(QLatin1Char(',')).append(flag);
426 }
427
428 if (epocBuild) {
429 // Deploy anything not already deployed from under epoc32 instead from under
430 // \epoc32\data\z\ to enable using pkg file without rebuilding
431 // the project, which can be useful for some binary only distributions.
432 if (!from.contains(QLatin1String("epoc32"), Qt::CaseInsensitive)) {
433 from = to;
434 if (from.size() > 1 && from.at(1) == QLatin1Char(':'))
435 from = from.mid(2);
436 from.prepend(zDir);
437 }
438 }
439
440 // Files with "FILETEXT"/"FT" flag are meant for showing only at installation time
441 // and therefore do not belong to the stub package and will not install the file into phone.
442 if (showOnlyFile)
443 to.clear();
444 else
445 ts << QString("\"\" - \"%1\"").arg(romPath(to)) << endl;
446
447 t << QString("\"%1\" - \"%2\"%3").arg(from.replace('\\','/')).arg(to).arg(flags) << endl;
448
449 }
450 t << endl;
451 ts << endl;
452
453 // PKG post-rules - these are added after actual file installations i.e. SIS package body
454 comment = "; Manual PKG post-rules from PRO files\n";
455
456 if (mainRawPostRules.size()) {
457 t << comment;
458 t << mainRawPostRules.join("\n") << endl;
459 }
460 if (instRawPostRules.size()) {
461 tw << comment;
462 tw << instRawPostRules.join("\n") << endl;
463 }
464 if (stubRawPostRules.size()) {
465 ts << comment;
466 ts << stubRawPostRules.join("\n") << endl;
467 }
468
469 // Close Manufacturer block
470 if (!project->values("DEPLOYMENT.manufacturers").isEmpty()) {
471 QString manufacturerFailNoteFile;
472 if (project->values("DEPLOYMENT.manufacturers.fail_note").isEmpty()) {
473 manufacturerFailNoteFile = QString("%1_" MANUFACTURER_NOTE_FILE).arg(uid3);
474 QFile ft(manufacturerFailNoteFile);
475 if (ft.open(QIODevice::WriteOnly)) {
476 generatedFiles << ft.fileName();
477 QTextStream t2(&ft);
478
479 t2 << QString(DEFAULT_MANUFACTURER_NOTE) << endl;
480 } else {
481 PRINT_FILE_CREATE_ERROR(manufacturerFailNoteFile)
482 }
483 } else {
484 manufacturerFailNoteFile = project->values("DEPLOYMENT.manufacturers.fail_note").join("");
485 }
486
487 t << "ELSEIF NOT(0) ; MANUFACTURER" << endl
488 << "\"" << generator->fileInfo(manufacturerFailNoteFile).absoluteFilePath() << "\""
489 << " - \"\", FILETEXT, TEXTEXIT" << endl
490 << "ENDIF ; MANUFACTURER" << endl;
491 }
492
493 // Write wrapper pkg
494 if (!installerSisHeader.isEmpty()) {
495 QFile wrapperPkgFile(wrapperPkgFilename);
496 if (!wrapperPkgFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
497 PRINT_FILE_CREATE_ERROR(wrapperPkgFilename);
498 return;
499 }
500
501 generatedFiles << wrapperPkgFile.fileName();
502 QTextStream twf(&wrapperPkgFile);
503
504 twf << wrapperStreamBuffer << endl;
505
506 // Wrapped files deployment
507 QString currentPath = qmake_getpwd();
508 QString sisName = QString("%1.sis").arg(fixedTarget);
509 twf << "\"" << currentPath << "/" << sisName << "\" - \"!:\\private\\2002CCCE\\import\\" << sisName << "\"" << endl;
510
511 QString bootStrapPath = QLibraryInfo::location(QLibraryInfo::PrefixPath);
512 bootStrapPath.append("/smartinstaller.sis");
513 QFileInfo fi(generator->fileInfo(bootStrapPath));
514 twf << "@\"" << fi.absoluteFilePath() << "\",(0x2002CCCD)" << endl;
515 }
516}
517
518QString SymbianCommonGenerator::removePathSeparators(QString &file)
519{
520 QString ret = file;
521
522 if (QDir::separator().unicode() != '/')
523 ret.replace(QDir::separator(), QLatin1Char('/'));
524
525 if (ret.indexOf(QLatin1Char('/')) >= 0)
526 ret.remove(0, ret.lastIndexOf(QLatin1Char('/')) + 1);
527
528 return ret;
529}
530
531void SymbianCommonGenerator::writeRegRssFile(QMap<QString, QStringList> &userItems)
532{
533 QString filename(fixedTarget);
534 filename.append("_reg.rss");
535 if (!Option::output_dir.isEmpty())
536 filename = Option::output_dir + '/' + filename;
537 QFile ft(filename);
538 if (ft.open(QIODevice::WriteOnly)) {
539 generatedFiles << ft.fileName();
540 QTextStream t(&ft);
541 t << "// ============================================================================" << endl;
542 t << "// * Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: ";
543 t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl;
544 t << "// * This file is generated by qmake and should not be modified by the" << endl;
545 t << "// * user." << endl;
546 t << "// ============================================================================" << endl;
547 t << endl;
548 t << "#include <" << fixedTarget << ".rsg>" << endl;
549 t << "#include <appinfo.rh>" << endl;
550 foreach(QString item, userItems[RSS_TAG_HEADER])
551 t << item << endl;
552 t << endl;
553 t << "UID2 KUidAppRegistrationResourceFile" << endl;
554 t << "UID3 " << uid3 << endl << endl;
555 t << "RESOURCE APP_REGISTRATION_INFO" << endl;
556 t << "\t{" << endl;
557 t << "\tapp_file=\"" << fixedTarget << "\";" << endl;
558 t << "\tlocalisable_resource_file=\"" RESOURCE_DIRECTORY_RESOURCE << fixedTarget << "\";" << endl;
559
560 writeRegRssList(t, userItems[RSS_TAG_SERVICE_LIST],
561 QLatin1String(RSS_TAG_SERVICE_LIST),
562 QLatin1String("SERVICE_INFO"));
563 writeRegRssList(t, userItems[RSS_TAG_FILE_OWNERSHIP_LIST],
564 QLatin1String(RSS_TAG_FILE_OWNERSHIP_LIST),
565 QLatin1String("FILE_OWNERSHIP_INFO"));
566 writeRegRssList(t, userItems[RSS_TAG_DATATYPE_LIST],
567 QLatin1String(RSS_TAG_DATATYPE_LIST),
568 QLatin1String("DATATYPE"));
569 t << endl;
570
571 foreach(QString item, userItems[RSS_TAG_DEFAULT])
572 t << "\t" << item.replace("\n","\n\t") << endl;
573 t << "\t}" << endl;
574
575 foreach(QString item, userItems[RSS_TAG_FOOTER])
576 t << item << endl;
577 } else {
578 PRINT_FILE_CREATE_ERROR(filename)
579 }
580}
581
582void SymbianCommonGenerator::writeRegRssList(QTextStream &t,
583 QStringList &userList,
584 const QString &listTag,
585 const QString &listItem)
586{
587 int itemCount = userList.count();
588 if (itemCount) {
589 t << "\t" << listTag << " ="<< endl;
590 t << "\t\t{" << endl;
591 foreach(QString item, userList) {
592 t << "\t\t" << listItem << endl;
593 t << "\t\t\t{" << endl;
594 t << "\t\t\t" << item.replace("\n","\n\t\t\t") << endl;
595 t << "\t\t\t}";
596 if (--itemCount)
597 t << ",";
598 t << endl;
599 }
600 t << "\t\t}; "<< endl;
601 }
602}
603
604void SymbianCommonGenerator::writeRssFile(QString &numberOfIcons, QString &iconFile)
605{
606 QString filename(fixedTarget);
607 if (!Option::output_dir.isEmpty())
608 filename = Option::output_dir + '/' + filename;
609 filename.append(".rss");
610 QFile ft(filename);
611 if (ft.open(QIODevice::WriteOnly)) {
612 generatedFiles << ft.fileName();
613 QTextStream t(&ft);
614 t << "// ============================================================================" << endl;
615 t << "// * Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: ";
616 t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl;
617 t << "// * This file is generated by qmake and should not be modified by the" << endl;
618 t << "// * user." << endl;
619 t << "// ============================================================================" << endl;
620 t << endl;
621 t << "CHARACTER_SET UTF8" << endl;
622 t << "#include <appinfo.rh>" << endl;
623 t << "#include \"" << fixedTarget << ".loc\"" << endl;
624 t << endl;
625 t << "RESOURCE LOCALISABLE_APP_INFO r_localisable_app_info" << endl;
626 t << "\t{" << endl;
627 t << "\tshort_caption = STRING_r_short_caption;" << endl;
628 t << "\tcaption_and_icon =" << endl;
629 t << "\tCAPTION_AND_ICON_INFO" << endl;
630 t << "\t\t{" << endl;
631 t << "\t\tcaption = STRING_r_caption;" << endl;
632
633 QString rssIconFile = iconFile;
634 rssIconFile = rssIconFile.replace("/", "\\\\");
635
636 if (numberOfIcons.isEmpty() || rssIconFile.isEmpty()) {
637 // There can be maximum one item in this tag, validated when parsed
638 t << "\t\tnumber_of_icons = 0;" << endl;
639 t << "\t\ticon_file = \"\";" << endl;
640 } else {
641 // There can be maximum one item in this tag, validated when parsed
642 t << "\t\tnumber_of_icons = " << numberOfIcons << ";" << endl;
643 t << "\t\ticon_file = \"" << rssIconFile << "\";" << endl;
644 }
645 t << "\t\t};" << endl;
646 t << "\t}" << endl;
647 t << endl;
648 } else {
649 PRINT_FILE_CREATE_ERROR(filename);
650 }
651}
652
653void SymbianCommonGenerator::writeLocFile(const SymbianLocalizationList &symbianLocalizationList)
654{
655 QString filename = generateLocFileName();
656 QFile ft(filename);
657 if (ft.open(QIODevice::WriteOnly)) {
658 generatedFiles << ft.fileName();
659 QTextStream t(&ft);
660
661 QString displayName = generator->project->values("DEPLOYMENT.display_name").join(" ");
662 if (displayName.isEmpty())
663 displayName = generator->escapeFilePath(generator->project->first("TARGET"));
664
665 t << "// ============================================================================" << endl;
666 t << "// * Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: ";
667 t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl;
668 t << "// * This file is generated by qmake and should not be modified by the" << endl;
669 t << "// * user." << endl;
670 t << "// ============================================================================" << endl;
671 t << endl;
672 t << "#ifdef LANGUAGE_SC" << endl;
673 t << "#define STRING_r_short_caption \"" << displayName << "\"" << endl;
674 t << "#define STRING_r_caption \"" << displayName << "\"" << endl;
675
676 SymbianLocalizationListIterator iter(symbianLocalizationList);
677 while (iter.hasNext()) {
678 const SymbianLocalization &loc = iter.next();
679 QString shortCaption = loc.shortCaption;
680 QString longCaption = loc.longCaption;
681 if (shortCaption.isEmpty())
682 shortCaption = displayName;
683 if (longCaption.isEmpty())
684 longCaption = displayName;
685
686 t << "#elif defined LANGUAGE_" << loc.symbianLanguageCode << endl;
687 t << "#define STRING_r_short_caption \"" << shortCaption << "\"" << endl;
688 t << "#define STRING_r_caption \"" << longCaption << "\"" << endl;
689 }
690
691 t << "#else" << endl;
692 t << "#define STRING_r_short_caption \"" << displayName << "\"" << endl;
693 t << "#define STRING_r_caption \"" << displayName << "\"" << endl;
694 t << "#endif" << endl;
695 } else {
696 PRINT_FILE_CREATE_ERROR(filename);
697 }
698}
699
700void SymbianCommonGenerator::readRssRules(QString &numberOfIcons,
701 QString &iconFile, QMap<QString,
702 QStringList> &userRssRules)
703{
704 QMakeProject *project = generator->project;
705 for (QMap<QString, QStringList>::iterator it = project->variables().begin(); it != project->variables().end(); ++it) {
706 if (it.key().startsWith(RSS_RULES_BASE)) {
707 QString newKey = it.key().mid(sizeof(RSS_RULES_BASE) - 1);
708 if (newKey.isEmpty()) {
709 fprintf(stderr, "Warning: Empty RSS_RULES_BASE key encountered\n");
710 continue;
711 }
712 QStringList newValues;
713 QStringList values = it.value();
714 foreach(QString item, values) {
715 // If there is no stringlist defined for a rule, use rule value directly
716 // This is convenience for defining single line statements
717 if (project->values(item).isEmpty()) {
718 newValues << item;
719 } else {
720 QStringList itemList;
721 foreach(QString itemRow, project->values(item)) {
722 itemList << itemRow;
723 }
724 newValues << itemList.join("\n");
725 }
726 }
727 // Verify that there is exactly one value in RSS_TAG_NBROFICONS
728 if (newKey == RSS_TAG_NBROFICONS) {
729 if (newValues.count() == 1) {
730 numberOfIcons = newValues[0];
731 } else {
732 fprintf(stderr, "Warning: There must be exactly one value in '%s%s'\n",
733 RSS_RULES_BASE, RSS_TAG_NBROFICONS);
734 continue;
735 }
736 // Verify that there is exactly one value in RSS_TAG_ICONFILE
737 } else if (newKey == RSS_TAG_ICONFILE) {
738 if (newValues.count() == 1) {
739 iconFile = newValues[0];
740 } else {
741 fprintf(stderr, "Warning: There must be exactly one value in '%s%s'\n",
742 RSS_RULES_BASE, RSS_TAG_ICONFILE);
743 continue;
744 }
745 } else if (newKey == RSS_TAG_HEADER
746 || newKey == RSS_TAG_SERVICE_LIST
747 || newKey == RSS_TAG_FILE_OWNERSHIP_LIST
748 || newKey == RSS_TAG_DATATYPE_LIST
749 || newKey == RSS_TAG_FOOTER
750 || newKey == RSS_TAG_DEFAULT) {
751 userRssRules[newKey] = newValues;
752 continue;
753 } else {
754 fprintf(stderr, "Warning: Unsupported key:'%s%s'\n",
755 RSS_RULES_BASE, newKey.toLatin1().constData());
756 continue;
757 }
758 }
759 }
760
761 QStringList newValues;
762 foreach(QString item, project->values(RSS_RULES)) {
763 // If there is no stringlist defined for a rule, use rule value directly
764 // This is convenience for defining single line statements
765 if (project->values(item).isEmpty()) {
766 newValues << item;
767 } else {
768 newValues << project->values(item);
769 }
770 }
771 userRssRules[RSS_TAG_DEFAULT] << newValues;
772
773 // Validate that either both RSS_TAG_NBROFICONS and RSS_TAG_ICONFILE keys exist
774 // or neither of them exist
775 if (!((numberOfIcons.isEmpty() && iconFile.isEmpty()) ||
776 (!numberOfIcons.isEmpty() && !iconFile.isEmpty()))) {
777 numberOfIcons.clear();
778 iconFile.clear();
779 fprintf(stderr, "Warning: Both or neither of '%s%s' and '%s%s' keys must exist.\n",
780 RSS_RULES_BASE, RSS_TAG_NBROFICONS, RSS_RULES_BASE, RSS_TAG_ICONFILE);
781 }
782
783 // Validate that RSS_TAG_NBROFICONS contains only numbers
784 if (!numberOfIcons.isEmpty()) {
785 bool ok;
786 numberOfIcons = numberOfIcons.simplified();
787 numberOfIcons.toInt(&ok);
788 if (!ok) {
789 numberOfIcons.clear();
790 iconFile.clear();
791 fprintf(stderr, "Warning: '%s%s' must be integer in decimal format.\n",
792 RSS_RULES_BASE, RSS_TAG_NBROFICONS);
793 }
794 }
795}
796
797void SymbianCommonGenerator::writeCustomDefFile()
798{
799 if (targetType == TypePlugin && !generator->project->isActiveConfig("stdbinary")) {
800 // Create custom def file for plugin
801 QFile ft(Option::output_dir + QLatin1Char('/') + QLatin1String(PLUGIN_COMMON_DEF_FILE_ACTUAL));
802
803 if (ft.open(QIODevice::WriteOnly)) {
804 generatedFiles << ft.fileName();
805 QTextStream t(&ft);
806
807 t << "; ==============================================================================" << endl;
808 t << "; Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: ";
809 t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl;
810 t << "; This file is generated by qmake and should not be modified by the" << endl;
811 t << "; user." << endl;
812 t << "; Name : " PLUGIN_COMMON_DEF_FILE_ACTUAL << endl;
813 t << "; Part of : " << generator->project->values("TARGET").join(" ") << endl;
814 t << "; Description : Fixes common plugin symbols to known ordinals" << endl;
815 t << "; Version : " << endl;
816 t << ";" << endl;
817 t << "; ==============================================================================" << "\n" << endl;
818
819 t << endl;
820
821 t << "EXPORTS" << endl;
822 t << "\tqt_plugin_query_verification_data @ 1 NONAME" << endl;
823 t << "\tqt_plugin_instance @ 2 NONAME" << endl;
824 t << endl;
825 } else {
826 PRINT_FILE_CREATE_ERROR(QString(PLUGIN_COMMON_DEF_FILE_ACTUAL))
827 }
828 }
829}
830
831void SymbianCommonGenerator::parseTsFiles(SymbianLocalizationList *symbianLocalizationList)
832{
833 if (!generator->project->isActiveConfig("localize_deployment")) {
834 return;
835 }
836
837 QStringList symbianTsFiles;
838
839 symbianTsFiles << generator->project->values("SYMBIAN_MATCHED_TRANSLATIONS");
840
841 if (!symbianTsFiles.isEmpty()) {
842 fillQt2SymbianLocalizationList(symbianLocalizationList);
843
844 QMutableListIterator<SymbianLocalization> iter(*symbianLocalizationList);
845 while (iter.hasNext()) {
846 SymbianLocalization &loc = iter.next();
847 static QString matchStrTemplate = QLatin1String(".*_%1\\.ts");
848 QString matchStr = matchStrTemplate.arg(loc.qtLanguageCode);
849
850 foreach (QString file, symbianTsFiles) {
851 QRegExp matcher(matchStr);
852 if (matcher.exactMatch(file) && parseTsContent(file, &loc))
853 break;
854 }
855 }
856 }
857}
858
859void SymbianCommonGenerator::fillQt2SymbianLocalizationList(SymbianLocalizationList *symbianLocalizationList)
860{
861 static QString symbianCodePrefix = QLatin1String("SYMBIAN_LANG.");
862
863 QStringList symbianLanguages = generator->project->values("SYMBIAN_MATCHED_LANGUAGES");
864
865 foreach (QString qtCode, symbianLanguages) {
866 SymbianLocalization newLoc;
867 QString symbianCodeVariable = symbianCodePrefix + qtCode;
868 newLoc.symbianLanguageCode = generator->project->first(symbianCodeVariable);
869 if (!newLoc.symbianLanguageCode.isEmpty()) {
870 newLoc.qtLanguageCode = qtCode;
871 symbianLocalizationList->append(newLoc);
872 }
873 }
874}
875
876void SymbianCommonGenerator::parsePreRules(const QString &deploymentVariable,
877 const QString &variableSuffix,
878 QStringList *rawRuleList,
879 QStringList *languageRuleList,
880 QStringList *headerRuleList,
881 QStringList *vendorRuleList)
882{
883 QMakeProject *project = generator->project;
884 foreach(QString pkgrulesItem, project->values(deploymentVariable + ".pkg_prerules" + variableSuffix)) {
885 QStringList pkgrulesValue = project->values(pkgrulesItem);
886 // If there is no stringlist defined for a rule, use rule name directly
887 // This is convenience for defining single line statements
888 if (pkgrulesValue.isEmpty()) {
889 if (pkgrulesItem.startsWith("&"))
890 *languageRuleList << pkgrulesItem;
891 else if (pkgrulesItem.startsWith("#"))
892 *headerRuleList << pkgrulesItem;
893 else if (pkgrulesItem.startsWith("%") || pkgrulesItem.startsWith(":"))
894 *vendorRuleList << pkgrulesItem;
895 else
896 *rawRuleList << pkgrulesItem;
897 } else {
898 if (containsStartWithItem('&', pkgrulesValue)) {
899 foreach(QString pkgrule, pkgrulesValue) {
900 *languageRuleList << pkgrule;
901 }
902 } else if (containsStartWithItem('#', pkgrulesValue)) {
903 foreach(QString pkgrule, pkgrulesValue) {
904 *headerRuleList << pkgrule;
905 }
906 } else if (containsStartWithItem('%', pkgrulesValue)
907 || containsStartWithItem(':', pkgrulesValue)) {
908 foreach(QString pkgrule, pkgrulesValue) {
909 *vendorRuleList << pkgrule;
910 }
911 } else {
912 foreach(QString pkgrule, pkgrulesValue) {
913 *rawRuleList << pkgrule;
914 }
915 }
916 }
917 }
918}
919
920void SymbianCommonGenerator::parsePostRules(const QString &deploymentVariable,
921 const QString &variableSuffix,
922 QStringList *rawRuleList)
923{
924 QMakeProject *project = generator->project;
925 foreach(QString pkgrulesItem, project->values(deploymentVariable + ".pkg_postrules" + variableSuffix)) {
926 QStringList pkgrulesValue = project->values(pkgrulesItem);
927 // If there is no stringlist defined for a rule, use rule name directly
928 // This is convenience for defining single line statements
929 if (pkgrulesValue.isEmpty()) {
930 *rawRuleList << pkgrulesItem;
931 } else {
932 foreach(QString pkgrule, pkgrulesValue) {
933 *rawRuleList << pkgrule;
934 }
935 }
936 }
937}
938
939bool SymbianCommonGenerator::parseTsContent(const QString &tsFilename, SymbianLocalization *loc)
940{
941 bool retval = true;
942 QMakeProject *project = generator->project;
943 QFile tsFile(tsFilename);
944
945 if (tsFile.exists()) {
946 if (tsFile.open(QIODevice::ReadOnly)) {
947 static QString applicationCaptionsContext = QLatin1String("QtApplicationCaptions");
948 static QString pkgNameContext = QLatin1String("QtPackageNames");
949 static QString tsElement = QLatin1String("TS");
950 static QString contextElement = QLatin1String("context");
951 static QString nameElement = QLatin1String("name");
952 static QString messageElement = QLatin1String("message");
953 static QString sourceElement = QLatin1String("source");
954 static QString translationElement = QLatin1String("translation");
955 static QString shortCaptionId = QLatin1String("Application short caption");
956 static QString longCaptionId = QLatin1String("Application long caption");
957 static QString pkgDisplayNameId = QLatin1String("Package name");
958 static QString installerPkgDisplayNameId = QLatin1String("Smart installer package name");
959 static QString languageAttribute = QLatin1String("language");
960 static QChar underscoreChar = QLatin1Char('_');
961
962 enum CurrentContext {
963 ContextUnknown,
964 ContextUninteresting,
965 ContextInteresting
966 };
967
968 QXmlStreamReader xml(&tsFile);
969
970 while (!xml.atEnd() && xml.name() != tsElement)
971 xml.readNextStartElement();
972
973 while (xml.readNextStartElement()) {
974 if (xml.name() == contextElement) {
975 CurrentContext currentContext = ContextUnknown;
976 while (xml.readNextStartElement()) {
977 if (currentContext == ContextUnknown) {
978 // Expect name element before message elements
979 if (xml.name() == nameElement) {
980 QString nameText = xml.readElementText();
981 if (nameText == applicationCaptionsContext || nameText == pkgNameContext) {
982 currentContext = ContextInteresting;
983 } else {
984 currentContext = ContextUninteresting;
985 }
986 } else {
987 xml.skipCurrentElement();
988 }
989 } else if (currentContext == ContextInteresting) {
990 if (xml.name() == messageElement) {
991 QString source;
992 QString translation;
993 while (xml.readNextStartElement()) {
994 if (xml.name() == sourceElement) {
995 source = xml.readElementText();
996 } else if (xml.name() == translationElement) {
997 translation = xml.readElementText();
998 } else {
999 xml.skipCurrentElement();
1000 }
1001 }
1002
1003 if (source == shortCaptionId) {
1004 if (loc->shortCaption.isEmpty()) {
1005 loc->shortCaption = translation;
1006 } else {
1007 fprintf(stderr, "Warning: Duplicate application short caption defined in (%s).\n",
1008 qPrintable(tsFilename));
1009 }
1010 } else if (source == longCaptionId) {
1011 if (loc->longCaption.isEmpty()) {
1012 loc->longCaption = translation;
1013 } else {
1014 fprintf(stderr, "Warning: Duplicate application long caption defined in (%s).\n",
1015 qPrintable(tsFilename));
1016 }
1017 } else if (source == pkgDisplayNameId) {
1018 if (loc->pkgDisplayName.isEmpty()) {
1019 loc->pkgDisplayName = translation;
1020 } else {
1021 fprintf(stderr, "Warning: Duplicate package display name defined in (%s).\n",
1022 qPrintable(tsFilename));
1023 }
1024 } else if (source == installerPkgDisplayNameId) {
1025 if (loc->installerPkgDisplayName.isEmpty()) {
1026 loc->installerPkgDisplayName = translation;
1027 } else {
1028 fprintf(stderr, "Warning: Duplicate smart installer package display name defined in (%s).\n",
1029 qPrintable(tsFilename));
1030 }
1031 }
1032 } else {
1033 xml.skipCurrentElement();
1034 }
1035 } else {
1036 xml.skipCurrentElement();
1037 }
1038 }
1039 } else {
1040 xml.skipCurrentElement();
1041 }
1042 }
1043 if (xml.hasError()) {
1044 retval = false;
1045 fprintf(stderr, "ERROR: Encountered error \"%s\" when parsing ts file (%s).\n",
1046 qPrintable(xml.errorString()), qPrintable(tsFilename));
1047 }
1048 } else {
1049 retval = false;
1050 fprintf(stderr, "Warning: Could not open ts file (%s).\n", qPrintable(tsFilename));
1051 }
1052 } else {
1053 retval = false;
1054 fprintf(stderr, "Warning: ts file does not exist: (%s), unable to parse it.\n",
1055 qPrintable(tsFilename));
1056 }
1057
1058 return retval;
1059}
1060
1061QString SymbianCommonGenerator::generatePkgNameForHeader(const SymbianLocalizationList &symbianLocalizationList,
1062 const QString &defaultName,
1063 bool isForSmartInstaller)
1064{
1065 QStringList allNames;
1066 QString noTranslation = defaultName;
1067
1068 if (isForSmartInstaller)
1069 noTranslation += QLatin1String(" installer");
1070
1071 SymbianLocalizationListIterator iter(symbianLocalizationList);
1072 while (iter.hasNext()) {
1073 const SymbianLocalization &loc = iter.next();
1074 QString currentName;
1075 if (isForSmartInstaller) {
1076 currentName = loc.installerPkgDisplayName;
1077 } else {
1078 currentName = loc.pkgDisplayName;
1079 }
1080
1081 if (currentName.isEmpty())
1082 currentName = noTranslation;
1083
1084 allNames << currentName;
1085 }
1086
1087 if (!allNames.size())
1088 allNames << noTranslation;
1089
1090 return allNames.join("\",\"");
1091
1092}
1093
1094void SymbianCommonGenerator::addLocalizedResourcesToDeployment(const QString &deploymentFilesVar,
1095 const SymbianLocalizationList &symbianLocalizationList)
1096{
1097 QStringList locResources;
1098 foreach (QString defaultResource, generator->project->values(deploymentFilesVar)) {
1099 if (defaultResource.endsWith(".rsc")) {
1100 defaultResource.chop(2);
1101 SymbianLocalizationListIterator iter(symbianLocalizationList);
1102 while (iter.hasNext()) {
1103 const SymbianLocalization &loc = iter.next();
1104 locResources << QString(defaultResource + loc.symbianLanguageCode);
1105 }
1106 }
1107 }
1108 generator->project->values(deploymentFilesVar) << locResources;
1109}
1110
1111QString SymbianCommonGenerator::generateLocFileName()
1112{
1113 QString fileName(fixedTarget);
1114 if (!Option::output_dir.isEmpty())
1115 fileName = Option::output_dir + QLatin1Char('/') + fileName;
1116 fileName.append(".loc");
1117 return fileName;
1118}
Note: See TracBrowser for help on using the repository browser.