qmake/generators/win32/winmakefile.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
child 7 f7bc934e204c
child 18 2f34d5167611
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the qmake application of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "winmakefile.h"
       
    43 #include "option.h"
       
    44 #include "project.h"
       
    45 #include "meta.h"
       
    46 #include <qtextstream.h>
       
    47 #include <qstring.h>
       
    48 #include <qhash.h>
       
    49 #include <qregexp.h>
       
    50 #include <qstringlist.h>
       
    51 #include <qdir.h>
       
    52 #include <stdlib.h>
       
    53 
       
    54 QT_BEGIN_NAMESPACE
       
    55 
       
    56 Win32MakefileGenerator::Win32MakefileGenerator() : MakefileGenerator()
       
    57 {
       
    58 }
       
    59 
       
    60 int
       
    61 Win32MakefileGenerator::findHighestVersion(const QString &d, const QString &stem, const QString &ext)
       
    62 {
       
    63     QString bd = Option::fixPathToLocalOS(d, true);
       
    64     if(!exists(bd))
       
    65         return -1;
       
    66 
       
    67     QString dllStem = stem + QTDLL_POSTFIX;
       
    68     QMakeMetaInfo libinfo;
       
    69     bool libInfoRead = libinfo.readLib(bd + Option::dir_sep + dllStem);
       
    70 
       
    71     // If the library, for which we're trying to find the highest version
       
    72     // number, is a static library
       
    73     if (libInfoRead && libinfo.values("QMAKE_PRL_CONFIG").contains("staticlib"))
       
    74         return -1;
       
    75 
       
    76     if(!project->values("QMAKE_" + stem.toUpper() + "_VERSION_OVERRIDE").isEmpty())
       
    77         return project->values("QMAKE_" + stem.toUpper() + "_VERSION_OVERRIDE").first().toInt();
       
    78 
       
    79     int biggest=-1;
       
    80     if(!project->isActiveConfig("no_versionlink")) {
       
    81         QDir dir(bd);
       
    82         QStringList entries = dir.entryList();
       
    83         QRegExp regx(QString("((lib)?%1([0-9]*)).(%2|prl)$").arg(dllStem).arg(ext), Qt::CaseInsensitive);
       
    84         for(QStringList::Iterator it = entries.begin(); it != entries.end(); ++it) {
       
    85             if(regx.exactMatch((*it))) {
       
    86 				if (!regx.cap(3).isEmpty()) {
       
    87 					bool ok = true;
       
    88 					int num = regx.cap(3).toInt(&ok);
       
    89 					biggest = qMax(biggest, (!ok ? -1 : num));
       
    90 				}
       
    91 			}
       
    92         }
       
    93     }
       
    94     if(libInfoRead
       
    95        && !libinfo.values("QMAKE_PRL_CONFIG").contains("staticlib")
       
    96        && !libinfo.isEmpty("QMAKE_PRL_VERSION"))
       
    97        biggest = qMax(biggest, libinfo.first("QMAKE_PRL_VERSION").replace(".", "").toInt());
       
    98     return biggest;
       
    99 }
       
   100 
       
   101 bool
       
   102 Win32MakefileGenerator::findLibraries(const QString &where)
       
   103 {
       
   104     QStringList &l = project->values(where);
       
   105     QList<QMakeLocalFileName> dirs;
       
   106     {
       
   107         QStringList &libpaths = project->values("QMAKE_LIBDIR");
       
   108         for(QStringList::Iterator libpathit = libpaths.begin();
       
   109             libpathit != libpaths.end(); ++libpathit)
       
   110             dirs.append(QMakeLocalFileName((*libpathit)));
       
   111     }
       
   112     for(QStringList::Iterator it = l.begin(); it != l.end();) {
       
   113         QChar quote;
       
   114         bool modified_opt = false, remove = false;
       
   115         QString opt = (*it).trimmed();
       
   116         if((opt[0] == '\'' || opt[0] == '"') && opt[(int)opt.length()-1] == opt[0]) {
       
   117             quote = opt[0];
       
   118             opt = opt.mid(1, opt.length()-2);
       
   119         }
       
   120         if(opt.startsWith("/LIBPATH:")) {
       
   121             dirs.append(QMakeLocalFileName(opt.mid(9)));
       
   122         } else if(opt.startsWith("-L") || opt.startsWith("/L")) {
       
   123             QString libpath = opt.mid(2);
       
   124             QMakeLocalFileName l(libpath);
       
   125             if(!dirs.contains(l)) {
       
   126                 dirs.append(l);
       
   127                 modified_opt = true;
       
   128                 if (!quote.isNull()) {
       
   129                     libpath = quote + libpath + quote;
       
   130                     quote = QChar();
       
   131                 }
       
   132                 (*it) = "/LIBPATH:" + libpath;
       
   133             } else {
       
   134                 remove = true;
       
   135             }
       
   136         } else if(opt.startsWith("-l") || opt.startsWith("/l")) {
       
   137             QString lib = opt.right(opt.length() - 2), out;
       
   138             if(!lib.isEmpty()) {
       
   139                 QString suffix;
       
   140                 if(!project->isEmpty("QMAKE_" + lib.toUpper() + "_SUFFIX"))
       
   141                     suffix = project->first("QMAKE_" + lib.toUpper() + "_SUFFIX");
       
   142                 for(QList<QMakeLocalFileName>::Iterator it = dirs.begin();
       
   143                     it != dirs.end(); ++it) {
       
   144                     QString extension;
       
   145                     int ver = findHighestVersion((*it).local(), lib);
       
   146                     if(ver > 0)
       
   147                         extension += QString::number(ver);
       
   148                     extension += suffix;
       
   149                     extension += ".lib";
       
   150                     if(QMakeMetaInfo::libExists((*it).local() + Option::dir_sep + lib) ||
       
   151                        exists((*it).local() + Option::dir_sep + lib + extension)) {
       
   152                         out = (*it).real() + Option::dir_sep + lib + extension;
       
   153                         break;
       
   154                     }
       
   155                 }
       
   156             }
       
   157             if(out.isEmpty())
       
   158                 out = lib + ".lib";
       
   159             modified_opt = true;
       
   160             (*it) = out;
       
   161         } else if(!exists(Option::fixPathToLocalOS(opt))) {
       
   162             QList<QMakeLocalFileName> lib_dirs;
       
   163             QString file = opt;
       
   164             int slsh = file.lastIndexOf(Option::dir_sep);
       
   165             if(slsh != -1) {
       
   166                 lib_dirs.append(QMakeLocalFileName(file.left(slsh+1)));
       
   167                 file = file.right(file.length() - slsh - 1);
       
   168             } else {
       
   169                 lib_dirs = dirs;
       
   170             }
       
   171             if(file.endsWith(".lib")) {
       
   172                 file = file.left(file.length() - 4);
       
   173                 if(!file.at(file.length()-1).isNumber()) {
       
   174                     QString suffix;
       
   175                     if(!project->isEmpty("QMAKE_" + file.section(Option::dir_sep, -1).toUpper() + "_SUFFIX"))
       
   176                         suffix = project->first("QMAKE_" + file.section(Option::dir_sep, -1).toUpper() + "_SUFFIX");
       
   177                     for(QList<QMakeLocalFileName>::Iterator dep_it = lib_dirs.begin(); dep_it != lib_dirs.end(); ++dep_it) {
       
   178                         QString lib_tmpl(file + "%1" + suffix + ".lib");
       
   179                         int ver = findHighestVersion((*dep_it).local(), file);
       
   180                         if(ver != -1) {
       
   181                             if(ver)
       
   182                                 lib_tmpl = lib_tmpl.arg(ver);
       
   183                             else
       
   184                                 lib_tmpl = lib_tmpl.arg("");
       
   185                             if(slsh != -1) {
       
   186                                 QString dir = (*dep_it).real();
       
   187                                 if(!dir.endsWith(Option::dir_sep))
       
   188                                     dir += Option::dir_sep;
       
   189                                 lib_tmpl.prepend(dir);
       
   190                             }
       
   191                             modified_opt = true;
       
   192                             (*it) = lib_tmpl;
       
   193                             break;
       
   194                         }
       
   195                     }
       
   196                 }
       
   197             }
       
   198         }
       
   199         if(remove) {
       
   200             it = l.erase(it);
       
   201         } else {
       
   202             if(!quote.isNull() && modified_opt)
       
   203                 (*it) = quote + (*it) + quote;
       
   204             ++it;
       
   205         }
       
   206     }
       
   207     return true;
       
   208 }
       
   209 
       
   210 void
       
   211 Win32MakefileGenerator::processPrlFiles()
       
   212 {
       
   213     QHash<QString, bool> processed;
       
   214     QList<QMakeLocalFileName> libdirs;
       
   215     {
       
   216         QStringList &libpaths = project->values("QMAKE_LIBDIR");
       
   217         for(QStringList::Iterator libpathit = libpaths.begin(); libpathit != libpaths.end(); ++libpathit)
       
   218             libdirs.append(QMakeLocalFileName((*libpathit)));
       
   219     }
       
   220     for(bool ret = false; true; ret = false) {
       
   221         //read in any prl files included..
       
   222         QStringList l_out;
       
   223         QString where = "QMAKE_LIBS";
       
   224         if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS"))
       
   225             where = project->first("QMAKE_INTERNAL_PRL_LIBS");
       
   226         QStringList l = project->values(where);
       
   227         for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
       
   228             QString opt = (*it).trimmed();
       
   229             if((opt[0] == '\'' || opt[0] == '"') && opt[(int)opt.length()-1] == opt[0])
       
   230                 opt = opt.mid(1, opt.length()-2);
       
   231             if(opt.startsWith("/")) {
       
   232                 if(opt.startsWith("/LIBPATH:")) {
       
   233                     QMakeLocalFileName l(opt.mid(9));
       
   234                     if(!libdirs.contains(l))
       
   235                         libdirs.append(l);
       
   236                 }
       
   237             } else if(!processed.contains(opt)) {
       
   238                 if(processPrlFile(opt)) {
       
   239                     processed.insert(opt, true);
       
   240                     ret = true;
       
   241                 } else if(QDir::isRelativePath(opt) || opt.startsWith("-l")) {
       
   242                     QString tmp;
       
   243                     if (opt.startsWith("-l"))
       
   244                         tmp = opt.mid(2);
       
   245                     else
       
   246                         tmp = opt;
       
   247                     for(QList<QMakeLocalFileName>::Iterator it = libdirs.begin(); it != libdirs.end(); ++it) {
       
   248                         QString prl = (*it).local() + Option::dir_sep + tmp;
       
   249                         // the original is used as the key
       
   250                         QString orgprl = prl;
       
   251                         if(processed.contains(prl)) {
       
   252                             break;
       
   253                         } else if(processPrlFile(prl)) {
       
   254                             processed.insert(orgprl, true);
       
   255                             ret = true;
       
   256                             break;
       
   257                         }
       
   258                     }
       
   259                 }
       
   260             }
       
   261             if(!opt.isEmpty())
       
   262                 l_out.append(opt);
       
   263         }
       
   264         if(ret)
       
   265             l = l_out;
       
   266         else
       
   267             break;
       
   268     }
       
   269 }
       
   270 
       
   271 
       
   272 void Win32MakefileGenerator::processVars()
       
   273 {
       
   274     //If the TARGET looks like a path split it into DESTDIR and the resulting TARGET
       
   275     if(!project->isEmpty("TARGET")) {
       
   276         QString targ = project->first("TARGET");
       
   277         int slsh = qMax(targ.lastIndexOf('/'), targ.lastIndexOf(Option::dir_sep));
       
   278         if(slsh != -1) {
       
   279             if(project->isEmpty("DESTDIR"))
       
   280                 project->values("DESTDIR").append("");
       
   281             else if(project->first("DESTDIR").right(1) != Option::dir_sep)
       
   282                 project->values("DESTDIR") = QStringList(project->first("DESTDIR") + Option::dir_sep);
       
   283             project->values("DESTDIR") = QStringList(project->first("DESTDIR") + targ.left(slsh+1));
       
   284             project->values("TARGET") = QStringList(targ.mid(slsh+1));
       
   285         }
       
   286     }
       
   287 
       
   288     project->values("QMAKE_ORIG_TARGET") = project->values("TARGET");
       
   289     if (!project->values("QMAKE_INCDIR").isEmpty())
       
   290         project->values("INCLUDEPATH") += project->values("QMAKE_INCDIR");
       
   291 
       
   292     if (!project->values("VERSION").isEmpty()) {
       
   293         QStringList l = project->first("VERSION").split('.');
       
   294         if (l.size() > 0)
       
   295             project->values("VER_MAJ").append(l[0]);
       
   296         if (l.size() > 1)
       
   297             project->values("VER_MIN").append(l[1]);
       
   298     }
       
   299 
       
   300     // TARGET_VERSION_EXT will be used to add a version number onto the target name
       
   301     if (project->values("TARGET_VERSION_EXT").isEmpty()
       
   302         && !project->values("VER_MAJ").isEmpty())
       
   303         project->values("TARGET_VERSION_EXT").append(project->values("VER_MAJ").first());
       
   304 
       
   305     if(project->isEmpty("QMAKE_COPY_FILE"))
       
   306         project->values("QMAKE_COPY_FILE").append("$(COPY)");
       
   307     if(project->isEmpty("QMAKE_COPY_DIR"))
       
   308         project->values("QMAKE_COPY_DIR").append("xcopy /s /q /y /i");
       
   309     if(project->isEmpty("QMAKE_INSTALL_FILE"))
       
   310         project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)");
       
   311     if(project->isEmpty("QMAKE_INSTALL_PROGRAM"))
       
   312         project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)");
       
   313     if(project->isEmpty("QMAKE_INSTALL_DIR"))
       
   314         project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)");
       
   315 
       
   316     fixTargetExt();
       
   317     processRcFileVar();
       
   318     processFileTagsVar();
       
   319 
       
   320     QStringList &incDir = project->values("INCLUDEPATH");
       
   321     for(QStringList::Iterator incDir_it = incDir.begin(); incDir_it != incDir.end(); ++incDir_it) {
       
   322     if(!(*incDir_it).isEmpty())
       
   323         (*incDir_it) = Option::fixPathToTargetOS((*incDir_it), false, false);
       
   324     }
       
   325     QStringList &libDir = project->values("QMAKE_LIBDIR");
       
   326     for(QStringList::Iterator libDir_it = libDir.begin(); libDir_it != libDir.end(); ++libDir_it) {
       
   327     if(!(*libDir_it).isEmpty())
       
   328         (*libDir_it) = Option::fixPathToTargetOS((*libDir_it), false, false);
       
   329     }
       
   330 }
       
   331 
       
   332 void Win32MakefileGenerator::fixTargetExt()
       
   333 {
       
   334     if (!project->values("QMAKE_APP_FLAG").isEmpty())
       
   335         project->values("TARGET_EXT").append(".exe");
       
   336     else if (project->isActiveConfig("shared"))
       
   337         project->values("TARGET_EXT").append(project->first("TARGET_VERSION_EXT") + ".dll");
       
   338     else
       
   339         project->values("TARGET_EXT").append(".lib");
       
   340 }
       
   341 
       
   342 void Win32MakefileGenerator::processRcFileVar()
       
   343 {
       
   344     if (Option::qmake_mode == Option::QMAKE_GENERATE_NOTHING)
       
   345         return;
       
   346 
       
   347     if (((!project->values("VERSION").isEmpty())
       
   348         && project->values("RC_FILE").isEmpty()
       
   349         && project->values("RES_FILE").isEmpty()
       
   350         && !project->isActiveConfig("no_generated_target_info")
       
   351         && (project->isActiveConfig("shared") || !project->values("QMAKE_APP_FLAG").isEmpty()))
       
   352         || !project->values("QMAKE_WRITE_DEFAULT_RC").isEmpty()){
       
   353 
       
   354         QByteArray rcString;
       
   355         QTextStream ts(&rcString, QFile::WriteOnly);
       
   356 
       
   357         QStringList vers = project->values("VERSION").first().split(".");
       
   358         for (int i = vers.size(); i < 4; i++)
       
   359             vers += "0";
       
   360         QString versionString = vers.join(".");
       
   361 
       
   362         QString companyName;
       
   363         if (!project->values("QMAKE_TARGET_COMPANY").isEmpty())
       
   364             companyName = project->values("QMAKE_TARGET_COMPANY").join(" ");
       
   365 
       
   366         QString description;
       
   367         if (!project->values("QMAKE_TARGET_DESCRIPTION").isEmpty())
       
   368             description = project->values("QMAKE_TARGET_DESCRIPTION").join(" ");
       
   369 
       
   370         QString copyright;
       
   371         if (!project->values("QMAKE_TARGET_COPYRIGHT").isEmpty())
       
   372             copyright = project->values("QMAKE_TARGET_COPYRIGHT").join(" ");
       
   373 
       
   374         QString productName;
       
   375         if (!project->values("QMAKE_TARGET_PRODUCT").isEmpty())
       
   376             productName = project->values("QMAKE_TARGET_PRODUCT").join(" ");
       
   377         else
       
   378             productName = project->values("TARGET").first();
       
   379 
       
   380         QString originalName = project->values("TARGET").first() + project->values("TARGET_EXT").first();
       
   381 
       
   382         ts << "# if defined(UNDER_CE)" << endl;
       
   383         ts << "#  include <winbase.h>" << endl;
       
   384         ts << "# else" << endl;
       
   385         ts << "#  include <winver.h>" << endl;
       
   386         ts << "# endif" << endl;
       
   387         ts << endl;
       
   388         ts << "VS_VERSION_INFO VERSIONINFO" << endl;
       
   389         ts << "\tFILEVERSION " << QString(versionString).replace(".", ",") << endl;
       
   390         ts << "\tPRODUCTVERSION " << QString(versionString).replace(".", ",") << endl;
       
   391         ts << "\tFILEFLAGSMASK 0x3fL" << endl;
       
   392         ts << "#ifdef _DEBUG" << endl;
       
   393         ts << "\tFILEFLAGS VS_FF_DEBUG" << endl;
       
   394         ts << "#else" << endl;
       
   395         ts << "\tFILEFLAGS 0x0L" << endl;
       
   396         ts << "#endif" << endl;
       
   397         ts << "\tFILEOS VOS__WINDOWS32" << endl;
       
   398         if (project->isActiveConfig("shared"))
       
   399             ts << "\tFILETYPE VFT_DLL" << endl;
       
   400         else
       
   401             ts << "\tFILETYPE VFT_APP" << endl;
       
   402         ts << "\tFILESUBTYPE 0x0L" << endl;
       
   403         ts << "\tBEGIN" << endl;
       
   404         ts << "\t\tBLOCK \"StringFileInfo\"" << endl;
       
   405         ts << "\t\tBEGIN" << endl;
       
   406         ts << "\t\t\tBLOCK \"040904B0\"" << endl;
       
   407         ts << "\t\t\tBEGIN" << endl;
       
   408         ts << "\t\t\t\tVALUE \"CompanyName\", \"" << companyName << "\\0\"" << endl;
       
   409         ts << "\t\t\t\tVALUE \"FileDescription\", \"" <<  description << "\\0\"" << endl;
       
   410         ts << "\t\t\t\tVALUE \"FileVersion\", \"" << versionString << "\\0\"" << endl;
       
   411         ts << "\t\t\t\tVALUE \"LegalCopyright\", \"" << copyright << "\\0\"" << endl;
       
   412         ts << "\t\t\t\tVALUE \"OriginalFilename\", \"" << originalName << "\\0\"" << endl;
       
   413         ts << "\t\t\t\tVALUE \"ProductName\", \"" << productName << "\\0\"" << endl;
       
   414         ts << "\t\t\tEND" << endl;
       
   415         ts << "\t\tEND" << endl;
       
   416         ts << "\tEND" << endl;
       
   417         ts << "/* End of Version info */" << endl;
       
   418         ts << endl;
       
   419 
       
   420         ts.flush();
       
   421 
       
   422 
       
   423         QString rcFilename = project->values("OUT_PWD").first()
       
   424                            + "/"
       
   425                            + project->values("TARGET").first()
       
   426                            + "_resource"
       
   427                            + ".rc";
       
   428         QFile rcFile(QDir::cleanPath(rcFilename));
       
   429 
       
   430         bool writeRcFile = true;
       
   431         if (rcFile.exists() && rcFile.open(QFile::ReadOnly)) {
       
   432             writeRcFile = rcFile.readAll() != rcString;
       
   433             rcFile.close();
       
   434         }
       
   435         if (writeRcFile) {
       
   436 	    bool ok;
       
   437 	    ok = rcFile.open(QFile::WriteOnly);
       
   438 	    if (!ok) {
       
   439 		// The file can't be opened... try creating the containing
       
   440 		// directory first (needed for clean shadow builds)
       
   441 		QDir().mkpath(QFileInfo(rcFile).path());
       
   442 		ok = rcFile.open(QFile::WriteOnly);
       
   443 	    }
       
   444 	    if (!ok) {
       
   445 		::fprintf(stderr, "Cannot open for writing: %s", rcFile.fileName().toLatin1().constData());
       
   446 		::exit(1);
       
   447 	    }
       
   448             if (Option::mkfile::listgen) {
       
   449                 generatePrint(fileInfo(rcFile.fileName()).absoluteFilePath());
       
   450             }
       
   451             rcFile.write(rcString);
       
   452 	    rcFile.close();
       
   453         }
       
   454         if (project->values("QMAKE_WRITE_DEFAULT_RC").isEmpty())
       
   455             project->values("RC_FILE").insert(0, rcFile.fileName());
       
   456     }
       
   457     if (!project->values("RC_FILE").isEmpty()) {
       
   458         if (!project->values("RES_FILE").isEmpty()) {
       
   459             fprintf(stderr, "Both rc and res file specified.\n");
       
   460             fprintf(stderr, "Please specify one of them, not both.");
       
   461             exit(1);
       
   462         }
       
   463         QString resFile = project->values("RC_FILE").first();
       
   464 
       
   465         // if this is a shadow build then use the absolute path of the rc file
       
   466         if (Option::output_dir != qmake_getpwd()) {
       
   467             QFileInfo fi(resFile);
       
   468             project->values("RC_FILE").first() = fi.absoluteFilePath();
       
   469         }
       
   470 
       
   471         resFile.replace(".rc", Option::res_ext);
       
   472         project->values("RES_FILE").prepend(fileInfo(resFile).fileName());
       
   473         if (!project->values("OBJECTS_DIR").isEmpty())
       
   474             if(project->isActiveConfig("staticlib"))
       
   475                 project->values("RES_FILE").first().prepend(fileInfo(project->values("DESTDIR").first()).absoluteFilePath() + Option::dir_sep);
       
   476             else
       
   477               project->values("RES_FILE").first().prepend(project->values("OBJECTS_DIR").first() + Option::dir_sep);
       
   478         project->values("RES_FILE").first() = Option::fixPathToTargetOS(project->values("RES_FILE").first(), false, false);
       
   479 	project->values("POST_TARGETDEPS") += project->values("RES_FILE");
       
   480         project->values("CLEAN_FILES") += project->values("RES_FILE");
       
   481     }
       
   482 }
       
   483 
       
   484 void Win32MakefileGenerator::processFileTagsVar()
       
   485 {
       
   486     QStringList tags;
       
   487     tags << "SOURCES" << "GENERATED_SOURCES" << "DEF_FILE" << "RC_FILE"
       
   488          << "TARGET" << "QMAKE_LIBS" << "DESTDIR" << "DLLDESTDIR" << "INCLUDEPATH";
       
   489     if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) {
       
   490         const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
       
   491         for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it)
       
   492             tags += project->values((*it)+".input");
       
   493     }
       
   494 
       
   495     //clean path
       
   496     QStringList &filetags = project->values("QMAKE_FILETAGS");
       
   497     for(int i = 0; i < tags.size(); ++i)
       
   498         filetags += Option::fixPathToTargetOS(tags.at(i), false);
       
   499 }
       
   500 
       
   501 void Win32MakefileGenerator::writeCleanParts(QTextStream &t)
       
   502 {
       
   503     t << "clean: compiler_clean " << var("CLEAN_DEPS");
       
   504     {
       
   505         const char *clean_targets[] = { "OBJECTS", "QMAKE_CLEAN", "CLEAN_FILES", 0 };
       
   506         for(int i = 0; clean_targets[i]; ++i) {
       
   507             const QStringList &list = project->values(clean_targets[i]);
       
   508             const QString del_statement("-$(DEL_FILE)");
       
   509             if(project->isActiveConfig("no_delete_multiple_files")) {
       
   510                 for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
       
   511                     t << "\n\t" << del_statement << " " << escapeFilePath((*it));
       
   512             } else {
       
   513                 QString files, file;
       
   514                 const int commandlineLimit = 2047; // NT limit, expanded
       
   515                 for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
       
   516                     file = " " + escapeFilePath((*it));
       
   517                     if(del_statement.length() + files.length() +
       
   518                        qMax(fixEnvVariables(file).length(), file.length()) > commandlineLimit) {
       
   519                         t << "\n\t" << del_statement << files;
       
   520                         files.clear();
       
   521                     }
       
   522                     files += file;
       
   523                 }
       
   524                 if(!files.isEmpty())
       
   525                     t << "\n\t" << del_statement << files;
       
   526             }
       
   527         }
       
   528     }
       
   529     t << endl << endl;
       
   530 
       
   531     t << "distclean: clean";
       
   532     {
       
   533         const char *clean_targets[] = { "QMAKE_DISTCLEAN", 0 };
       
   534         for(int i = 0; clean_targets[i]; ++i) {
       
   535             const QStringList &list = project->values(clean_targets[i]);
       
   536             const QString del_statement("-$(DEL_FILE)");
       
   537             if(project->isActiveConfig("no_delete_multiple_files")) {
       
   538                 for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
       
   539                     t << "\n\t" << del_statement << " " << escapeFilePath((*it));
       
   540             } else {
       
   541                 QString files, file;
       
   542                 const int commandlineLimit = 2047; // NT limit, expanded
       
   543                 for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
       
   544                     file = " " + escapeFilePath((*it));
       
   545                     if(del_statement.length() + files.length() +
       
   546                        qMax(fixEnvVariables(file).length(), file.length()) > commandlineLimit) {
       
   547                         t << "\n\t" << del_statement << files;
       
   548                         files.clear();
       
   549                     }
       
   550                     files += file;
       
   551                 }
       
   552                 if(!files.isEmpty())
       
   553                     t << "\n\t" << del_statement << files;
       
   554             }
       
   555         }
       
   556     }
       
   557     t << "\n\t-$(DEL_FILE) $(DESTDIR_TARGET)" << endl;
       
   558     {
       
   559         QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
       
   560         if(!ofile.isEmpty())
       
   561             t << "\t-$(DEL_FILE) " << ofile << endl;
       
   562     }
       
   563     t << endl;
       
   564 }
       
   565 
       
   566 void Win32MakefileGenerator::writeIncPart(QTextStream &t)
       
   567 {
       
   568     t << "INCPATH       = ";
       
   569 
       
   570     const QStringList &incs = project->values("INCLUDEPATH");
       
   571     for(int i = 0; i < incs.size(); ++i) {
       
   572         QString inc = incs.at(i);
       
   573         inc.replace(QRegExp("\\\\$"), "");
       
   574         inc.replace(QRegExp("\""), "");
       
   575         if(!inc.isEmpty())
       
   576             t << "-I" << "\"" << inc << "\" ";
       
   577     }
       
   578     t << "-I\"" << specdir() << "\""
       
   579       << endl;
       
   580 }
       
   581 
       
   582 void Win32MakefileGenerator::writeStandardParts(QTextStream &t)
       
   583 {
       
   584     t << "####### Compiler, tools and options" << endl << endl;
       
   585     t << "CC            = " << var("QMAKE_CC") << endl;
       
   586     t << "CXX           = " << var("QMAKE_CXX") << endl;
       
   587     t << "DEFINES       = "
       
   588       << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ")
       
   589       << varGlue("DEFINES","-D"," -D","") << endl;
       
   590     t << "CFLAGS        = " << var("QMAKE_CFLAGS") << " $(DEFINES)" << endl;
       
   591     t << "CXXFLAGS      = " << var("QMAKE_CXXFLAGS") << " $(DEFINES)" << endl;
       
   592 
       
   593     writeIncPart(t);
       
   594     writeLibsPart(t);
       
   595 
       
   596     t << "QMAKE         = " << (project->isEmpty("QMAKE_QMAKE") ? QString("qmake") :
       
   597                               Option::fixPathToTargetOS(var("QMAKE_QMAKE"), false)) << endl;
       
   598     t << "IDC           = " << (project->isEmpty("QMAKE_IDC") ? QString("idc") :
       
   599                               Option::fixPathToTargetOS(var("QMAKE_IDC"), false)) << endl;
       
   600     t << "IDL           = " << (project->isEmpty("QMAKE_IDL") ? QString("midl") :
       
   601                               Option::fixPathToTargetOS(var("QMAKE_IDL"), false)) << endl;
       
   602     t << "ZIP           = " << var("QMAKE_ZIP") << endl;
       
   603     t << "DEF_FILE      = " << varList("DEF_FILE") << endl;
       
   604     t << "RES_FILE      = " << varList("RES_FILE") << endl; // Not on mingw, can't see why not though...
       
   605     t << "COPY          = " << var("QMAKE_COPY") << endl;
       
   606     t << "COPY_FILE     = " << var("QMAKE_COPY_FILE") << endl;
       
   607     t << "COPY_DIR      = " << var("QMAKE_COPY_DIR") << endl;
       
   608     t << "DEL_FILE      = " << var("QMAKE_DEL_FILE") << endl;
       
   609     t << "DEL_DIR       = " << var("QMAKE_DEL_DIR") << endl;
       
   610     t << "MOVE          = " << var("QMAKE_MOVE") << endl;
       
   611     t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl;
       
   612     t << "MKDIR         = " << var("QMAKE_MKDIR") << endl;
       
   613     t << "INSTALL_FILE    = " << var("QMAKE_INSTALL_FILE") << endl;
       
   614     t << "INSTALL_PROGRAM = " << var("QMAKE_INSTALL_PROGRAM") << endl;
       
   615     t << "INSTALL_DIR     = " << var("QMAKE_INSTALL_DIR") << endl;
       
   616     t << endl;
       
   617 
       
   618     t << "####### Output directory" << endl << endl;
       
   619     if(!project->values("OBJECTS_DIR").isEmpty())
       
   620         t << "OBJECTS_DIR   = " << var("OBJECTS_DIR").replace(QRegExp("\\\\$"),"") << endl;
       
   621     else
       
   622         t << "OBJECTS_DIR   = . " << endl;
       
   623     t << endl;
       
   624 
       
   625     t << "####### Files" << endl << endl;
       
   626     t << "SOURCES       = " << valList(escapeFilePaths(project->values("SOURCES")))
       
   627       << " " << valList(escapeFilePaths(project->values("GENERATED_SOURCES"))) << endl;
       
   628 
       
   629     // do this here so we can set DEST_TARGET to be the complete path to the final target if it is needed.
       
   630     QString orgDestDir = var("DESTDIR");
       
   631     QString destDir = Option::fixPathToTargetOS(orgDestDir, false);
       
   632     if (!destDir.isEmpty() && (orgDestDir.endsWith('/') || orgDestDir.endsWith(Option::dir_sep)))
       
   633         destDir += Option::dir_sep;
       
   634     QString target = QString(project->first("TARGET")+project->first("TARGET_EXT"));
       
   635     target.remove("\"");
       
   636     project->values("DEST_TARGET").prepend(destDir + target);
       
   637 
       
   638     writeObjectsPart(t);
       
   639 
       
   640     writeExtraCompilerVariables(t);
       
   641     writeExtraVariables(t);
       
   642 
       
   643     t << "DIST          = " << varList("DISTFILES") << endl;
       
   644     t << "QMAKE_TARGET  = " << var("QMAKE_ORIG_TARGET") << endl;
       
   645     // The comment is important to maintain variable compatability with Unix
       
   646     // Makefiles, while not interpreting a trailing-slash as a linebreak
       
   647     t << "DESTDIR        = " << escapeFilePath(destDir) << " #avoid trailing-slash linebreak" << endl;
       
   648     t << "TARGET         = " << escapeFilePath(target) << endl;
       
   649     t << "DESTDIR_TARGET = " << escapeFilePath(var("DEST_TARGET")) << endl;
       
   650     t << endl;
       
   651 
       
   652     t << "####### Implicit rules" << endl << endl;
       
   653     writeImplicitRulesPart(t);
       
   654 
       
   655     t << "####### Build rules" << endl << endl;
       
   656     writeBuildRulesPart(t);
       
   657 
       
   658     if(project->isActiveConfig("shared") && !project->values("DLLDESTDIR").isEmpty()) {
       
   659         QStringList dlldirs = project->values("DLLDESTDIR");
       
   660         for (QStringList::Iterator dlldir = dlldirs.begin(); dlldir != dlldirs.end(); ++dlldir) {
       
   661             t << "\n\t" << "-$(COPY_FILE) \"$(DESTDIR_TARGET)\" " << Option::fixPathToTargetOS(*dlldir, false);
       
   662         }
       
   663     }
       
   664     t << endl << endl;
       
   665 
       
   666     writeRcFilePart(t);
       
   667 
       
   668     writeMakeQmake(t);
       
   669 
       
   670     QStringList dist_files = fileFixify(Option::mkfile::project_files);
       
   671     if(!project->isEmpty("QMAKE_INTERNAL_INCLUDED_FILES"))
       
   672         dist_files += project->values("QMAKE_INTERNAL_INCLUDED_FILES");
       
   673     if(!project->isEmpty("TRANSLATIONS"))
       
   674         dist_files << var("TRANSLATIONS");
       
   675     if(!project->isEmpty("FORMS")) {
       
   676         QStringList &forms = project->values("FORMS");
       
   677         for(QStringList::Iterator formit = forms.begin(); formit != forms.end(); ++formit) {
       
   678             QString ui_h = fileFixify((*formit) + Option::h_ext.first());
       
   679             if(exists(ui_h))
       
   680                 dist_files << ui_h;
       
   681         }
       
   682     }
       
   683     t << "dist:" << "\n\t"
       
   684       << "$(ZIP) " << var("QMAKE_ORIG_TARGET") << ".zip " << "$(SOURCES) $(DIST) "
       
   685       << dist_files.join(" ") << " " << var("TRANSLATIONS") << " ";
       
   686     if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) {
       
   687         const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
       
   688         for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
       
   689             const QStringList &inputs = project->values((*it)+".input");
       
   690             for(QStringList::ConstIterator input = inputs.begin(); input != inputs.end(); ++input) {
       
   691                 t << (*input) << " ";
       
   692             }
       
   693         }
       
   694     }
       
   695     t << endl << endl;
       
   696 
       
   697     writeCleanParts(t);
       
   698     writeExtraTargets(t);
       
   699     writeExtraCompilerTargets(t);
       
   700     t << endl << endl;
       
   701 }
       
   702 
       
   703 void Win32MakefileGenerator::writeLibsPart(QTextStream &t)
       
   704 {
       
   705     if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
       
   706         t << "LIBAPP        = " << var("QMAKE_LIB") << endl;
       
   707         t << "LIBFLAGS      = " << var("QMAKE_LIBFLAGS") << endl;
       
   708     } else {
       
   709         t << "LINK          = " << var("QMAKE_LINK") << endl;
       
   710         t << "LFLAGS        = ";
       
   711         if(!project->values("QMAKE_LIBDIR").isEmpty())
       
   712             writeLibDirPart(t);
       
   713         t << var("QMAKE_LFLAGS") << endl;
       
   714         t << "LIBS          = " << var("QMAKE_LIBS") << " " << var("QMAKE_LIBS_PRIVATE") << endl;
       
   715     }
       
   716 }
       
   717 
       
   718 void Win32MakefileGenerator::writeLibDirPart(QTextStream &t)
       
   719 {
       
   720     QStringList libDirs = project->values("QMAKE_LIBDIR");
       
   721     for (int i = 0; i < libDirs.size(); ++i)
       
   722         libDirs[i].remove("\"");
       
   723     t << valGlue(libDirs,"-L\"","\" -L\"","\"") << " ";
       
   724 }
       
   725 
       
   726 void Win32MakefileGenerator::writeObjectsPart(QTextStream &t)
       
   727 {
       
   728     t << "OBJECTS       = " << valList(escapeDependencyPaths(project->values("OBJECTS"))) << endl;
       
   729 }
       
   730 
       
   731 void Win32MakefileGenerator::writeImplicitRulesPart(QTextStream &t)
       
   732 {
       
   733     t << ".SUFFIXES:";
       
   734     for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit)
       
   735         t << " " << (*cppit);
       
   736     for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit)
       
   737         t << " " << (*cit);
       
   738     t << endl << endl;
       
   739     for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit)
       
   740         t << (*cppit) << Option::obj_ext << ":\n\t" << var("QMAKE_RUN_CXX_IMP") << endl << endl;
       
   741     for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit)
       
   742         t << (*cit) << Option::obj_ext << ":\n\t" << var("QMAKE_RUN_CC_IMP") << endl << endl;
       
   743 }
       
   744 
       
   745 void Win32MakefileGenerator::writeBuildRulesPart(QTextStream &)
       
   746 {
       
   747 }
       
   748 
       
   749 void Win32MakefileGenerator::writeRcFilePart(QTextStream &t)
       
   750 {
       
   751     if(!project->values("RC_FILE").isEmpty()) {
       
   752         const QString res_file = project->first("RES_FILE"),
       
   753                        rc_file = fileFixify(project->first("RC_FILE"));
       
   754         // The resource tool needs to have the same defines passed in as the compiler, since you may
       
   755         // use these defines in the .rc file itself. Also, we need to add the _DEBUG define manually
       
   756         // since the compiler defines this symbol by itself, and we use it in the automatically
       
   757         // created rc file when VERSION is define the .pro file.
       
   758         t << res_file << ": " << rc_file << "\n\t"
       
   759           << var("QMAKE_RC") << (project->isActiveConfig("debug") ? " -D_DEBUG" : "") << " $(DEFINES) -fo " << res_file << " " << rc_file;
       
   760         t << endl << endl;
       
   761     }
       
   762 }
       
   763 
       
   764 QString Win32MakefileGenerator::getLibTarget()
       
   765 {
       
   766     return QString(project->first("TARGET") + project->first("TARGET_VERSION_EXT") + ".lib");
       
   767 }
       
   768 
       
   769 QString Win32MakefileGenerator::defaultInstall(const QString &t)
       
   770 {
       
   771     if((t != "target" && t != "dlltarget") ||
       
   772        (t == "dlltarget" && (project->first("TEMPLATE") != "lib" || !project->isActiveConfig("shared"))) ||
       
   773         project->first("TEMPLATE") == "subdirs")
       
   774        return QString();
       
   775 
       
   776     const QString root = "$(INSTALL_ROOT)";
       
   777     QStringList &uninst = project->values(t + ".uninstall");
       
   778     QString ret;
       
   779     QString targetdir = Option::fixPathToTargetOS(project->first(t + ".path"), false);
       
   780     targetdir = fileFixify(targetdir, FileFixifyAbsolute);
       
   781     if(targetdir.right(1) != Option::dir_sep)
       
   782         targetdir += Option::dir_sep;
       
   783 
       
   784     if(t == "target" && project->first("TEMPLATE") == "lib") {
       
   785         if(project->isActiveConfig("create_prl") && !project->isActiveConfig("no_install_prl") &&
       
   786            !project->isEmpty("QMAKE_INTERNAL_PRL_FILE")) {
       
   787             QString dst_prl = Option::fixPathToTargetOS(project->first("QMAKE_INTERNAL_PRL_FILE"));
       
   788             int slsh = dst_prl.lastIndexOf(Option::dir_sep);
       
   789             if(slsh != -1)
       
   790                 dst_prl = dst_prl.right(dst_prl.length() - slsh - 1);
       
   791             dst_prl = filePrefixRoot(root, targetdir + dst_prl);
       
   792             ret += "-$(INSTALL_FILE) \"" + project->first("QMAKE_INTERNAL_PRL_FILE") + "\" \"" + dst_prl + "\"";
       
   793             if(!uninst.isEmpty())
       
   794                 uninst.append("\n\t");
       
   795             uninst.append("-$(DEL_FILE) \"" + dst_prl + "\"");
       
   796         }
       
   797         if(project->isActiveConfig("shared") && !project->isActiveConfig("plugin")) {
       
   798             QString lib_target = getLibTarget();
       
   799             lib_target.remove('"');
       
   800             QString src_targ = (project->isEmpty("DESTDIR") ? QString("$(DESTDIR)") : project->first("DESTDIR")) + lib_target;
       
   801             QString dst_targ = filePrefixRoot(root, fileFixify(targetdir + lib_target, FileFixifyAbsolute));
       
   802             if(!ret.isEmpty())
       
   803                 ret += "\n\t";
       
   804             ret += QString("-$(INSTALL_FILE)") + " \"" + src_targ + "\" \"" + dst_targ + "\"";
       
   805             if(!uninst.isEmpty())
       
   806                 uninst.append("\n\t");
       
   807             uninst.append("-$(DEL_FILE) \"" + dst_targ + "\"");
       
   808         }
       
   809     }
       
   810 
       
   811     if(t == "dlltarget" || project->values(t + ".CONFIG").indexOf("no_dll") == -1) {
       
   812         QString src_targ = "$(DESTDIR_TARGET)";
       
   813         QString dst_targ = filePrefixRoot(root, fileFixify(targetdir + "$(TARGET)", FileFixifyAbsolute));
       
   814         if(!ret.isEmpty())
       
   815             ret += "\n\t";
       
   816         ret += QString("-$(INSTALL_FILE)") + " \"" + src_targ + "\" \"" + dst_targ + "\"";
       
   817         if(!uninst.isEmpty())
       
   818             uninst.append("\n\t");
       
   819         uninst.append("-$(DEL_FILE) \"" + dst_targ + "\"");
       
   820     }
       
   821     return ret;
       
   822 }
       
   823 
       
   824 QString Win32MakefileGenerator::escapeFilePath(const QString &path) const
       
   825 {
       
   826     QString ret = path;
       
   827     if(!ret.isEmpty()) {
       
   828         ret = unescapeFilePath(ret);
       
   829         if(ret.contains(" "))
       
   830             ret = "\"" + ret + "\"";
       
   831         debug_msg(2, "EscapeFilePath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData());
       
   832     }
       
   833     return ret;
       
   834 }
       
   835 
       
   836 QT_END_NAMESPACE