qmake/generators/symbian/symmake.cpp
changeset 3 41300fa6a67c
parent 0 1918ee327afb
child 4 3b1da2848fc7
child 7 f7bc934e204c
child 18 2f34d5167611
--- a/qmake/generators/symbian/symmake.cpp	Tue Jan 26 12:42:25 2010 +0200
+++ b/qmake/generators/symbian/symmake.cpp	Tue Feb 02 00:43:10 2010 +0200
@@ -49,6 +49,8 @@
 #include <qdatetime.h>
 #include <stdlib.h>
 #include <qdebug.h>
+#include <qxmlstream.h>
+#include <qsettings>
 
 #define RESOURCE_DIRECTORY_MMP "/resource/apps"
 #define RESOURCE_DIRECTORY_RESOURCE "\\\\resource\\\\apps\\\\"
@@ -74,6 +76,18 @@
 #define MMP_OPTION_CW "OPTION CW"
 #define MMP_OPTION_ARMCC "OPTION ARMCC"
 #define MMP_OPTION_GCCE "OPTION GCCE"
+#define MMP_LINKEROPTION_CW "LINKEROPTION CW"
+#define MMP_LINKEROPTION_ARMCC "LINKEROPTION ARMCC"
+#define MMP_LINKEROPTION_GCCE "LINKEROPTION GCCE"
+#define MMP_CAPABILITY "CAPABILITY"
+#define MMP_EPOCALLOWDLLDATA "EPOCALLOWDLLDATA"
+#define MMP_EPOCHEAPSIZE "EPOCHEAPSIZE"
+#define MMP_EPOCSTACKSIZE "EPOCSTACKSIZE"
+#define MMP_UID "UID"
+#define MMP_VENDORID "VENDORID"
+#define MMP_VERSION "VERSION"
+#define MMP_START_RESOURCE "START RESOURCE"
+#define MMP_END_RESOURCE "END"
 
 #define SIS_TARGET "sis"
 #define OK_SIS_TARGET "ok_sis"
@@ -172,7 +186,7 @@
 {
     t << "// ============================================================================" << endl;
     t << "// * Makefile for building: " << escapeFilePath(var("TARGET")) << endl;
-    t << "// * Generated by qmake (" << qmake_version() << ") (Qt " << QT_VERSION_STR << ") on: ";
+    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;
@@ -226,7 +240,7 @@
         generatePkgFile(iconFile);
     }
 
-    writeBldInfContent(t, generatePkg);
+    writeBldInfContent(t, generatePkg, iconFile);
 
     // Generate empty wrapper makefile here, because wrapper makefile must exist before writeMkFile,
     // but all required data is not yet available.
@@ -276,6 +290,8 @@
             writeRegRssFile(userRssRules);
             writeRssFile(numberOfIcons, iconFile);
             writeLocFile(symbianLangCodes);
+            if (!project->values("SYMBIANTRANSLATIONS").isEmpty())
+                writeSymbianLocFile(symbianLangCodes);        
         }
     }
 
@@ -401,14 +417,11 @@
                  .arg(installPathRegResource)
                  .arg(fixedTarget + "_reg.rsc") << endl;
 
-            QString myIconFile = iconFile;
-            myIconFile = myIconFile.replace("\\\\", "\\");
-
             if (!iconFile.isEmpty())  {
                 t << QString("\"%1epoc32/data/z%2\"    - \"!:%3\"")
                      .arg(epocRoot())
-                     .arg(QString(myIconFile).replace('\\','/'))
-                     .arg(myIconFile) << endl << endl;
+                     .arg(iconFile)
+                     .arg(QDir::toNativeSeparators(iconFile)) << endl << endl;
             }
         }
     }
@@ -473,7 +486,7 @@
             QTextStream t(&ft);
 
             t << "; ==============================================================================" << endl;
-            t << "; Generated by qmake (" << qmake_version() << ") (Qt " << QT_VERSION_STR << ") on: ";
+            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;
@@ -636,19 +649,10 @@
     incpaths << project->values("UI_HEADERS_DIR");
     incpaths << project->values("UI_DIR");
 
-    QString epocPath("epoc32");
     for (int j = 0; j < incpaths.size(); ++j) {
         QString includepath = canonizePath(incpaths.at(j));
         appendIfnotExist(sysincspaths, includepath);
-        // As a workaround for Symbian toolchain insistence to treat include
-        // statements as relative to source file rather than the file they appear in,
-        // we generate extra temporary include directories to make
-        // relative include paths used in various headers to work properly.
-        // Note that this is not a fix-all solution; it's just a stop-gap measure
-        // to make Qt itself build until toolchain can support relative includes in
-        // a way that Qt expects.
-        if (!includepath.contains(epocPath)) // No temp dirs for epoc includes
-            appendIfnotExist(sysincspaths, includepath + QString("/" QT_EXTRA_INCLUDE_DIR));
+        appendAbldTempDirs(sysincspaths, includepath);
     }
 
     // Remove duplicate include path entries
@@ -684,30 +688,75 @@
 
     // Check MMP_RULES for singleton keywords that are overridden
     QStringList overridableMmpKeywords;
-    overridableMmpKeywords << QLatin1String(MMP_TARGETTYPE) << QLatin1String(MMP_OPTION_CW)
-        << QLatin1String(MMP_OPTION_ARMCC) << QLatin1String(MMP_OPTION_GCCE);
+    QStringList restrictableMmpKeywords;
+    QStringList restrictedMmpKeywords;
+    bool inResourceBlock = false;
+
+    overridableMmpKeywords << QLatin1String(MMP_TARGETTYPE);
+    restrictableMmpKeywords << QLatin1String(MMP_TARGET) << QLatin1String(MMP_SECUREID)
+       << QLatin1String(MMP_OPTION_CW) << QLatin1String(MMP_OPTION_ARMCC)
+       << QLatin1String(MMP_OPTION_GCCE) << QLatin1String(MMP_LINKEROPTION_CW)
+       << QLatin1String(MMP_LINKEROPTION_ARMCC) << QLatin1String(MMP_LINKEROPTION_GCCE)
+       << QLatin1String(MMP_CAPABILITY) << QLatin1String(MMP_EPOCALLOWDLLDATA)
+       << QLatin1String(MMP_EPOCHEAPSIZE) << QLatin1String(MMP_EPOCSTACKSIZE)
+       << QLatin1String(MMP_UID) << QLatin1String(MMP_VENDORID)
+       << QLatin1String(MMP_VERSION);
 
     foreach (QString item, project->values("MMP_RULES")) {
         if (project->values(item).isEmpty()) {
-            checkOverridability(overridableMmpKeywords, item);
+            handleMmpRulesOverrides(item, inResourceBlock, restrictedMmpKeywords,
+                                    restrictableMmpKeywords, overridableMmpKeywords);
         } else {
             foreach (QString itemRow, project->values(item)) {
-                checkOverridability(overridableMmpKeywords, itemRow);
+                handleMmpRulesOverrides(itemRow, inResourceBlock, restrictedMmpKeywords,
+                                        restrictableMmpKeywords, overridableMmpKeywords);
             }
         }
     }
+
+    if (restrictedMmpKeywords.size()) {
+        fprintf(stderr, "Warning: Restricted statements detected in MMP_RULES:\n"
+                "         (%s)\n"
+                "         Use corresponding qmake variable(s) instead.\n",
+                qPrintable(restrictedMmpKeywords.join(", ")));
+        }
+}
+
+void SymbianMakefileGenerator::handleMmpRulesOverrides(QString &checkString,
+                                                       bool &inResourceBlock,
+                                                       QStringList &restrictedMmpKeywords,
+                                                       const QStringList &restrictableMmpKeywords,
+                                                       const QStringList &overridableMmpKeywords)
+{
+    QString simplifiedString = checkString.simplified();
+
+    if (!inResourceBlock && simplifiedString.startsWith(MMP_START_RESOURCE, Qt::CaseInsensitive))
+        inResourceBlock = true;
+    else if (inResourceBlock && simplifiedString.startsWith(MMP_END_RESOURCE, Qt::CaseInsensitive))
+        inResourceBlock = false;
+
+    // Allow restricted and overridable items in RESOURCE blocks as those do not actually
+    // override anything.
+    if (!inResourceBlock) {
+        appendKeywordIfMatchFound(overriddenMmpKeywords, overridableMmpKeywords, simplifiedString);
+        appendKeywordIfMatchFound(restrictedMmpKeywords, restrictableMmpKeywords, simplifiedString);
+    }
+}
+
+void SymbianMakefileGenerator::appendKeywordIfMatchFound(QStringList &list,
+                                                         const QStringList &keywordList,
+                                                         QString &checkString)
+{
+    // Check if checkString starts with any supplied keyword and
+    // add the found keyword to list if it does.
+    foreach (QString item, keywordList) {
+        if (checkString.startsWith(QString(item).append(" "), Qt::CaseInsensitive)
+            || checkString.compare(item, Qt::CaseInsensitive) == 0) {
+            appendIfnotExist(list, item);
+        }
+    }
 }
 
-void SymbianMakefileGenerator::checkOverridability(QStringList &overridableKeywords, QString &checkString)
-{
-    // Check if checkString contains overridable keyword and
-    // add the keyword to overridden keywords list if so.
-    QString simplifiedString = checkString.simplified();
-    foreach (QString item, overridableKeywords) {
-        if (simplifiedString.startsWith(item))
-            appendIfnotExist(overriddenMmpKeywords, item);
-    }
-}
 
 bool SymbianMakefileGenerator::removeDuplicatedStrings(QStringList &stringList)
 {
@@ -729,7 +778,7 @@
 void SymbianMakefileGenerator::writeMmpFileHeader(QTextStream &t)
 {
     t << "// ==============================================================================" << endl;
-    t << "// Generated by qmake (" << qmake_version() << ") (Qt " << QT_VERSION_STR << ") on: ";
+    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;
@@ -818,7 +867,7 @@
 
 void SymbianMakefileGenerator::addMacro(QTextStream& t, const QString& value)
 {
-    t << "MACRO" << "\t\t" <<  value << endl;
+    t << "MACRO\t\t" <<  value << endl;
 }
 
 
@@ -827,28 +876,28 @@
     bool skipTargetType = overriddenMmpKeywords.contains(MMP_TARGETTYPE);
 
     if (targetType == TypeExe) {
-        t << MMP_TARGET << "\t\t" << fixedTarget << ".exe" << endl;
+        t << MMP_TARGET "\t\t" << fixedTarget << ".exe" << endl;
         if (!skipTargetType) {
             if (project->values("CONFIG").contains("stdbinary", Qt::CaseInsensitive))
-                t << MMP_TARGETTYPE << "\t\t" << "STDEXE" << endl;
+                t << MMP_TARGETTYPE "\t\tSTDEXE" << endl;
             else
-                t << MMP_TARGETTYPE << "\t\t" << "EXE" << endl;
+                t << MMP_TARGETTYPE "\t\tEXE" << endl;
         }
     } else if (targetType == TypeDll || targetType == TypePlugin) {
-        t << MMP_TARGET << "\t\t" << fixedTarget << ".dll" << endl;
+        t << MMP_TARGET "\t\t" << fixedTarget << ".dll" << endl;
         if (!skipTargetType) {
             if (project->values("CONFIG").contains("stdbinary", Qt::CaseInsensitive))
-                t << MMP_TARGETTYPE << "\t\t" << "STDDLL" << endl;
+                t << MMP_TARGETTYPE "\t\tSTDDLL" << endl;
             else
-                t << MMP_TARGETTYPE << "\t\t" << "DLL" << endl;
+                t << MMP_TARGETTYPE "\t\tDLL" << endl;
         }
     } else if (targetType == TypeLib) {
-        t << MMP_TARGET << "\t\t" << fixedTarget << ".lib" << endl;
+        t << MMP_TARGET "\t\t" << fixedTarget << ".lib" << endl;
         if (!skipTargetType) {
             if (project->values("CONFIG").contains("stdbinary", Qt::CaseInsensitive))
-                t << MMP_TARGETTYPE << "\t\t" << "STDLIB" << endl;
+                t << MMP_TARGETTYPE "\t\tSTDLIB" << endl;
             else
-                t << MMP_TARGETTYPE << "\t\t" << "LIB" << endl;
+                t << MMP_TARGETTYPE "\t\tLIB" << endl;
         }
     } else {
         fprintf(stderr, "Error: Unexpected targettype (%d) in SymbianMakefileGenerator::writeMmpFileTargetPart\n", targetType);
@@ -856,30 +905,30 @@
 
     t << endl;
 
-    t << "UID" << "\t\t" << uid2 << " " << uid3 << endl;
+    t << MMP_UID "\t\t" << uid2 << " " << uid3 << endl;
 
     if (0 != project->values("TARGET.SID").size()) {
-        t << MMP_SECUREID << "\t\t" << project->values("TARGET.SID").join(" ") << endl;
+        t << MMP_SECUREID "\t\t" << project->values("TARGET.SID").join(" ") << endl;
     } else {
         if (0 == uid3.size())
-            t << MMP_SECUREID << "\t\t" << "0" << endl;
+            t << MMP_SECUREID "\t\t0" << endl;
         else
-            t << MMP_SECUREID << "\t\t" << uid3 << endl;
+            t << MMP_SECUREID "\t\t" << uid3 << endl;
     }
 
     // default value used from mkspecs is 0
     if (0 != project->values("TARGET.VID").size()) {
-        t << "VENDORID" << "\t\t" << project->values("TARGET.VID").join(" ") << endl;
+        t << MMP_VENDORID "\t\t" << project->values("TARGET.VID").join(" ") << endl;
     }
 
     t << endl;
 
     if (0 != project->first("TARGET.EPOCSTACKSIZE").size())
-        t << "EPOCSTACKSIZE" << "\t\t" << project->first("TARGET.EPOCSTACKSIZE") << endl;
+        t << MMP_EPOCSTACKSIZE "\t\t" << project->first("TARGET.EPOCSTACKSIZE") << endl;
     if (0 != project->values("TARGET.EPOCHEAPSIZE").size())
-        t << "EPOCHEAPSIZE" << "\t\t" << project->values("TARGET.EPOCHEAPSIZE").join(" ") << endl;
+        t << MMP_EPOCHEAPSIZE "\t\t" << project->values("TARGET.EPOCHEAPSIZE").join(" ") << endl;
     if (0 != project->values("TARGET.EPOCALLOWDLLDATA").size())
-        t << "EPOCALLOWDLLDATA" << endl;
+        t << MMP_EPOCALLOWDLLDATA << endl;
 
     if (targetType == TypePlugin && !project->values("CONFIG").contains("stdbinary", Qt::CaseInsensitive)) {
         // Use custom def file for Qt plugins
@@ -903,25 +952,34 @@
         locTarget.append(".rss");
 
         t << "SOURCEPATH\t\t\t. " << endl;
-        t << "LANG SC ";    // no endl
-        foreach(QString lang, symbianLangCodes) {
-            t << lang << " "; // no endl
+
+        if (!project->values("SYMBIANTRANSLATIONS").isEmpty()) {
+            t << MMP_START_RESOURCE "\t\t" << locTarget << endl;
+            t << "LANGUAGE_IDS" << endl;
+            t << "HEADER" << endl;
+            t << "TARGETPATH\t\t\t" RESOURCE_DIRECTORY_MMP << endl;
+            t << MMP_END_RESOURCE << endl << endl;
+        } else {
+            t << "LANG SC ";    // no endl
+            foreach(QString lang, symbianLangCodes) {
+                t << lang << " "; // no endl
+            }
+            t << endl;
+            t << MMP_START_RESOURCE "\t\t" << locTarget << endl;
+            t << "HEADER" << endl;
+            t << "TARGETPATH\t\t\t" RESOURCE_DIRECTORY_MMP << endl;
+            t << MMP_END_RESOURCE << endl << endl;
         }
-        t << endl;
-        t << "START RESOURCE\t\t" << locTarget << endl;
-        t << "HEADER" << endl;
-        t << "TARGETPATH\t\t\t" RESOURCE_DIRECTORY_MMP << endl;
-        t << "END" << endl << endl;
 
         QString regTarget = fixedTarget;
         regTarget.append("_reg.rss");
 
         t << "SOURCEPATH\t\t\t." << endl;
-        t << "START RESOURCE\t\t" << regTarget << endl;
+        t << MMP_START_RESOURCE "\t\t" << regTarget << endl;
         if (isForSymbianSbsv2())
             t << "DEPENDS " << fixedTarget << ".rsg" << endl;
         t << "TARGETPATH\t\t" REGISTRATION_RESOURCE_DIRECTORY_HW << endl;
-        t << "END" << endl << endl;
+        t << MMP_END_RESOURCE << endl << endl;
     }
 }
 
@@ -933,7 +991,7 @@
         QStringList values = it.value();
         for (int i = 0; i < values.size(); ++i) {
             QString handledPath = values.at(i);
-            t << "SYSTEMINCLUDE" << "\t\t" << fixPathForMmp(handledPath, current) << endl;
+            t << "SYSTEMINCLUDE\t\t" << fixPathForMmp(handledPath, current) << endl;
         }
     }
 
@@ -989,14 +1047,14 @@
 {
     if (0 != project->first("TARGET.CAPABILITY").size()) {
         QStringList &capabilities = project->values("TARGET.CAPABILITY");
-        t << "CAPABILITY" << "\t\t";
+        t << MMP_CAPABILITY "\t\t";
 
         for (int i = 0; i < capabilities.size(); ++i) {
             QString cap = capabilities.at(i);
             t << cap << " ";
         }
     } else {
-        t << "CAPABILITY" << "\t\t" << "None";
+        t << MMP_CAPABILITY "\t\tNone";
     }
     t << endl << endl;
 }
@@ -1004,6 +1062,7 @@
 void SymbianMakefileGenerator::writeMmpFileCompilerOptionPart(QTextStream& t)
 {
     QString cw, armcc, gcce;
+    QString cwlink, armlink, gccelink;
 
     if (0 != project->values("QMAKE_CXXFLAGS.CW").size()) {
         cw.append(project->values("QMAKE_CXXFLAGS.CW").join(" "));
@@ -1053,21 +1112,58 @@
         gcce.append(" ");
     }
 
+    if (0 != project->values("QMAKE_LFLAGS.CW").size()) {
+        cwlink.append(project->values("QMAKE_LFLAGS.CW").join(" "));
+        cwlink.append(" ");
+    }
+
+    if (0 != project->values("QMAKE_LFLAGS.ARMCC").size()) {
+        armlink.append(project->values("QMAKE_LFLAGS.ARMCC").join(" "));
+        armlink.append(" ");
+    }
+
+    if (0 != project->values("QMAKE_LFLAGS.GCCE").size()) {
+        gccelink.append(project->values("QMAKE_LFLAGS.GCCE").join(" "));
+        gccelink.append(" ");
+    }
+
+    if (0 != project->values("QMAKE_LFLAGS").size()) {
+        cwlink.append(project->values("QMAKE_LFLAGS").join(" "));
+        cwlink.append(" ");
+        armlink.append(project->values("QMAKE_LFLAGS").join(" "));
+        armlink.append(" ");
+        gccelink.append(project->values("QMAKE_LFLAGS").join(" "));
+        gccelink.append(" ");
+    }
+
     if (!cw.isEmpty() && cw[cw.size()-1] == ' ')
         cw.chop(1);
     if (!armcc.isEmpty() && armcc[armcc.size()-1] == ' ')
         armcc.chop(1);
     if (!gcce.isEmpty() && gcce[gcce.size()-1] == ' ')
         gcce.chop(1);
+    if (!cwlink.isEmpty() && cwlink[cwlink.size()-1] == ' ')
+        cwlink.chop(1);
+    if (!armlink.isEmpty() && armlink[armlink.size()-1] == ' ')
+        armlink.chop(1);
+    if (!gccelink.isEmpty() && gccelink[gccelink.size()-1] == ' ')
+        gccelink.chop(1);
 
-    if (!cw.isEmpty() && !overriddenMmpKeywords.contains(MMP_OPTION_CW))
+    if (!cw.isEmpty())
         t << MMP_OPTION_CW " " << cw <<  endl;
-    if (!armcc.isEmpty() && !overriddenMmpKeywords.contains(MMP_OPTION_ARMCC))
+    if (!armcc.isEmpty())
         t << MMP_OPTION_ARMCC " " << armcc <<  endl;
-    if (!gcce.isEmpty() && !overriddenMmpKeywords.contains(MMP_OPTION_GCCE))
+    if (!gcce.isEmpty())
         t << MMP_OPTION_GCCE " " << gcce <<  endl;
 
-    t <<  endl;
+    if (!cwlink.isEmpty())
+        t << MMP_LINKEROPTION_CW " " << cwlink <<  endl;
+    if (!armlink.isEmpty())
+        t << MMP_LINKEROPTION_ARMCC " " << armlink <<  endl;
+    if (!gccelink.isEmpty())
+        t << MMP_LINKEROPTION_GCCE " " << gccelink <<  endl;
+
+    t << endl;
 }
 
 void SymbianMakefileGenerator::writeMmpFileBinaryVersionPart(QTextStream& t)
@@ -1103,7 +1199,7 @@
         mmpVersion = "10.0"; // Default binary version for symbian is 10.0
     }
 
-    t << "VERSION " << mmpVersion  << endl;
+    t << MMP_VERSION " " << mmpVersion  << endl;
 }
 
 void SymbianMakefileGenerator::writeMmpFileRulesPart(QTextStream& t)
@@ -1122,7 +1218,7 @@
     }
 }
 
-void SymbianMakefileGenerator::writeBldInfContent(QTextStream &t, bool addDeploymentExtension)
+void SymbianMakefileGenerator::writeBldInfContent(QTextStream &t, bool addDeploymentExtension, const QString &iconFile)
 {
     // Read user defined bld inf rules
 
@@ -1244,7 +1340,7 @@
 
     // Generate extension rules
 
-    writeBldInfExtensionRulesPart(t);
+    writeBldInfExtensionRulesPart(t, iconFile);
 
     userItems = userBldInfRules.value(BLD_INF_TAG_EXTENSIONS);
     foreach(QString item, userItems)
@@ -1344,7 +1440,7 @@
         generatedFiles << ft.fileName();
         QTextStream t(&ft);
         t << "// ============================================================================" << endl;
-        t << "// * Generated by qmake (" << qmake_version() << ") (Qt " << QT_VERSION_STR << ") on: ";
+        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;
@@ -1353,8 +1449,7 @@
         t << "#include <" << fixedTarget << ".rsg>" << endl;
         t << "#include <appinfo.rh>" << endl;
         t << endl;
-        //t << "#include <data_caging_paths.hrh>" << "\n" << endl;
-        t << "UID2 " << "KUidAppRegistrationResourceFile" << endl;
+        t << "UID2 KUidAppRegistrationResourceFile" << endl;
         t << "UID3 " << uid3 << endl << endl;
         t << "RESOURCE APP_REGISTRATION_INFO" << endl;
         t << "\t{" << endl;
@@ -1382,14 +1477,17 @@
         generatedFiles << ft.fileName();
         QTextStream t(&ft);
         t << "// ============================================================================" << endl;
-        t << "// * Generated by qmake (" << qmake_version() << ") (Qt " << QT_VERSION_STR << ") on: ";
+        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 <appinfo.rh>" << endl;
-        t << "#include \"" << fixedTarget << ".loc\"" << endl;
+        if (!project->values("SYMBIANTRANSLATIONS").isEmpty())
+            t << "#include <" << fixedTarget << ".loc>" << endl;
+        else
+            t << "#include \"" << fixedTarget << ".loc\"" << endl;
         t << endl;
         t << "RESOURCE LOCALISABLE_APP_INFO r_localisable_app_info" << endl;
         t << "\t{" << endl;
@@ -1399,14 +1497,17 @@
         t << "\t\t{" << endl;
         t << "\t\tcaption = STRING_r_caption;" << endl;
 
-        if (numberOfIcons.isEmpty() || iconFile.isEmpty()) {
+        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 = \"" << iconFile << "\";" << endl;
+            t << "\t\ticon_file = \"" << rssIconFile << "\";" << endl;
         }
         t << "\t\t};" << endl;
         t << "\t}" << endl;
@@ -1419,7 +1520,17 @@
 void SymbianMakefileGenerator::writeLocFile(QStringList &symbianLangCodes)
 {
     QString filename(fixedTarget);
-    filename.append(".loc");
+    if (project->values("SYMBIANTRANSLATIONS").isEmpty()) {
+        filename.append(".loc");
+    } else { 
+        if (!project->first("SYMBIANLOCFILESDIR").isEmpty()) { 
+            filename.insert(0,project->first("SYMBIANLOCFILESDIR"));         
+        } else {	
+            filename.insert(0,"/epoc32/include/platform/app/loc/");    
+        }
+        filename.append(".loc");
+    }
+    
     QFile ft(filename);
     if (ft.open(QIODevice::WriteOnly)) {
         if (Option::mkfile::listgen) {
@@ -1428,12 +1539,13 @@
         generatedFiles << ft.fileName();
         QTextStream t(&ft);
         t << "// ============================================================================" << endl;
-        t << "// * Generated by qmake (" << qmake_version() << ") (Qt " << QT_VERSION_STR << ") on: ";
+        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;
+        if (project->values("SYMBIANTRANSLATIONS").isEmpty()) {
         t << "#ifdef LANGUAGE_SC" << endl;
         t << "#define STRING_r_short_caption \"" << fixedTarget  << "\"" << endl;
         t << "#define STRING_r_caption \"" << fixedTarget  << "\"" << endl;
@@ -1447,10 +1559,122 @@
         t << "#define STRING_r_caption \"" << fixedTarget  << "\"" << endl;
         t << "#endif" << endl;
     } else {
+            t << "#if LANGUAGE_01" << endl;    
+            t << "#include <" << "01/" << fixedTarget  << "_01.loc>" << endl;
+            foreach(QString lang, symbianLangCodes) {
+                if (lang.localeAwareCompare("01") != 0) {
+                    t << "#elif LANGUAGE_" << lang << endl;
+                    t << "#include <" << lang << "/" << fixedTarget  << "_" << lang << ".loc>" << ">" << endl;
+                }
+            }
+            t << "#endif" << endl;
+        }
+    } else {
         PRINT_FILE_CREATE_ERROR(filename);
     }
 }
 
+void SymbianMakefileGenerator::writeSymbianLocFile(QStringList &symbianLangCodes)
+{	
+	QString filename(fixedTarget); 
+	foreach(QString lang, symbianLangCodes) {
+        
+		QString tsFilename(filename);  
+		QString language = qt2S60LangMapTable.key(lang, QString("en"));
+		tsFilename.append("_"+language+".ts");
+		tsFilename.insert(0,project->first("SYMBIANTRANSLATIONDIR")); 	
+		
+        QString locFilename(filename); 
+        locFilename.append("_"+lang+".loc");
+        if (!project->first("SYMBIANLOCFILESDIR").isEmpty()) {    
+            locFilename.insert(0,lang+"/");
+            locFilename.insert(0,project->first("SYMBIANLOCFILESDIR"));
+        } else {	
+            locFilename.insert(0,"/epoc32/include/platform/app/loc/"+lang+"/");
+        }
+                
+        QString shortCaption;
+        QString longCaption;
+        
+        // get captions from ts file
+        QFile tsFile(tsFilename);
+        if (tsFile.exists()) {
+            if (tsFile.open(QIODevice::ReadOnly)) {
+                QString shortCaptionId = QLatin1String("txt_short_caption_");
+                shortCaptionId.append(filename);
+                QString longCaptionId = QLatin1String("txt_long_caption_");
+                longCaptionId.append(filename);
+                QXmlStreamReader xml(&tsFile);
+                while (!xml.atEnd()) {
+                    xml.readNext();
+                    if (xml.isStartElement() && xml.name() == "context") {
+                        while (!(xml.isEndElement() && xml.name() == "context") && !xml.atEnd()) {
+                            xml.readNext();  
+                            if (xml.isStartElement() && xml.name() == "message" && 
+                                xml.attributes().value("numerus") == "no" && 
+                                xml.attributes().value("id") == shortCaptionId) {
+                                while (!(xml.isEndElement() && xml.name() == "message") && !xml.atEnd()) {
+                                    xml.readNext();
+                                    if (xml.isStartElement() && xml.name() == "translation") {
+                            		   shortCaption = xml.readElementText();
+                                    }
+                                }
+                            }
+                            if (xml.isStartElement() && xml.name() == "message" && 
+                                xml.attributes().value("numerus") == "no" && 
+                                xml.attributes().value("id") == longCaptionId) {
+                                while (!(xml.isEndElement() && xml.name() == "message") && !xml.atEnd()) {   
+                                    xml.readNext();
+                                    if (xml.isStartElement() && xml.name() == "translation") {
+                                        longCaption = xml.readElementText();
+                                    }
+                                }
+                            }
+                        }
+                    }         
+                }
+                if (shortCaption.isEmpty())
+                    fprintf(stderr, "Warning: STRING_r_short_caption not generated from file '%s'.\n", qPrintable(tsFilename));
+                if (longCaption.isEmpty())
+                    fprintf(stderr, "Warning: STRING_r_caption not generated from file '%s'.\n", qPrintable(tsFilename));
+                if (xml.hasError())
+                    fprintf(stderr, "ERROR: \"%s\" when parsing ts file\n", qPrintable(xml.errorString()));
+            } else {
+                fprintf(stderr, "Could not open ts file (%s)\n", qPrintable(tsFilename));
+            }
+        } else {
+            fprintf(stderr, "ts file does not exist: (%s)\n", qPrintable(tsFilename));
+        }
+
+        // generate language specific caption loc file
+        QFile ft(locFilename);
+        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;
+            if (!shortCaption.isEmpty()) {
+                t << "#define STRING_r_short_caption \"" << shortCaption  << "\"" << endl;    
+            } else {
+                t << "#define STRING_r_short_caption \"" << fixedTarget  << "\"" << endl;
+            }    
+            if (!longCaption.isEmpty()) {
+                t << "#define STRING_r_caption \"" << longCaption  << "\"" << endl;  
+            } else {    
+                t << "#define STRING_r_caption \"" << fixedTarget  << "\"" << endl;
+            }
+            ft.close();
+        } else {
+            PRINT_FILE_CREATE_ERROR(locFilename);	
+	    }
+    }
+}
+
 void SymbianMakefileGenerator::readRssRules(QString &numberOfIcons, QString &iconFile, QStringList &userRssRules)
 {
     for (QMap<QString, QStringList>::iterator it = project->variables().begin(); it != project->variables().end(); ++it) {
@@ -1541,17 +1765,26 @@
 
     fillQt2S60LangMapTable();
 
-    foreach(QString file, tsfiles) {
-        int extIndex = file.lastIndexOf(".");
-        int langIndex = file.lastIndexOf("_", (extIndex - file.length()));
-        langIndex += 1;
-        QString qtlang = file.mid(langIndex, extIndex - langIndex);
-        QString s60lang = qt2S60LangMapTable.value(qtlang, QString("SC"));
+    if (project->values("SYMBIANTRANSLATIONS").isEmpty()) {
+        foreach(QString file, tsfiles) {
+            int extIndex = file.lastIndexOf(".");
+            int langIndex = file.lastIndexOf("_", (extIndex - file.length()));
+            langIndex += 1;
+            QString qtlang = file.mid(langIndex, extIndex - langIndex);
+            QString s60lang = qt2S60LangMapTable.value(qtlang, QString("SC"));
+            if (!symbianLangCodes.contains(s60lang) && s60lang != "SC")
+                symbianLangCodes += s60lang;
+        }
+    } else {
+        modifyQt2S60LangMapTable(); // Modify the table according to the system_languages.ini file 
+        foreach(QString symbianTranslation, project->values("SYMBIANTRANSLATIONS")) {
+            QString s60lang = qt2S60LangMapTable.value(symbianTranslation, QString("SC"));
 
-        if (!symbianLangCodes.contains(s60lang) && s60lang != "SC")
-            symbianLangCodes += s60lang;
+            if (!symbianLangCodes.contains(s60lang) && s60lang != "SC")
+                symbianLangCodes += s60lang;
+        }
     }
-
+        
     return symbianLangCodes;
 }
 
@@ -1724,6 +1957,24 @@
     qt2S60LangMapTable.insert("ny", "SC");            //Chewa                         //
 }
 
+void SymbianMakefileGenerator::modifyQt2S60LangMapTable()
+{
+	QString systemLanguagesIniFile = project->first("SYMBIANTRANSLATIONDIR").
+                                     append(QLatin1String("system_languages.ini"));
+    QFileInfo systemLangFileInfo(systemLanguagesIniFile);
+    if (systemLangFileInfo.exists()) {
+        QSettings systemLanguages(systemLanguagesIniFile, QSettings::IniFormat);
+        QStringList keys = systemLanguages.allKeys();
+        foreach (const QString &qtlang, keys){
+            QString s60lang = systemLanguages.value(qtlang).toString();
+            if (s60lang.localeAwareCompare(qt2S60LangMapTable.value(qtlang, QString("SC"))) != 0) {
+                qt2S60LangMapTable.insert(qtlang, s60lang);
+                fprintf(stderr, "Inserted: s60lang '%s' for qtlang '%s'.\n", qPrintable(s60lang), qPrintable(qtlang));
+            }
+        }
+    }
+}
+
 void SymbianMakefileGenerator::appendIfnotExist(QStringList &list, QString value)
 {
     if (!list.contains(value))
@@ -1779,6 +2030,7 @@
 
 void SymbianMakefileGenerator::removeSpecialCharacters(QString& str)
 {
+    // When modifying this method check also application_icon.prf
     str.replace(QString("/"), QString("_"));
     str.replace(QString("\\"), QString("_"));
     str.replace(QString("-"), QString("_"));