qmake/generators/makefile.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
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 "makefile.h"
       
    43 #include "option.h"
       
    44 #include "cachekeys.h"
       
    45 #include "meta.h"
       
    46 #include <qdir.h>
       
    47 #include <qfile.h>
       
    48 #include <qtextstream.h>
       
    49 #include <qregexp.h>
       
    50 #include <qhash.h>
       
    51 #include <qdebug.h>
       
    52 #include <qbuffer.h>
       
    53 #include <qsettings.h>
       
    54 #include <qdatetime.h>
       
    55 #if defined(Q_OS_UNIX)
       
    56 #include <unistd.h>
       
    57 #else
       
    58 #include <io.h>
       
    59 #endif
       
    60 #include <qdebug.h>
       
    61 #include <stdio.h>
       
    62 #include <stdlib.h>
       
    63 #include <time.h>
       
    64 #include <fcntl.h>
       
    65 #include <sys/types.h>
       
    66 #include <sys/stat.h>
       
    67 
       
    68 QT_BEGIN_NAMESPACE
       
    69 
       
    70 // Well, Windows doesn't have this, so here's the macro
       
    71 #ifndef S_ISDIR
       
    72 #  define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
       
    73 #endif
       
    74 
       
    75 bool MakefileGenerator::canExecute(const QStringList &cmdline, int *a) const
       
    76 {
       
    77     int argv0 = -1;
       
    78     for(int i = 0; i < cmdline.count(); ++i) {
       
    79         if(!cmdline.at(i).contains('=')) {
       
    80             argv0 = i;
       
    81             break;
       
    82         }
       
    83     }
       
    84     if(a)
       
    85         *a = argv0;
       
    86     if(argv0 != -1) {
       
    87         const QString c = Option::fixPathToLocalOS(cmdline.at(argv0), true);
       
    88         if(exists(c))
       
    89             return true;
       
    90     }
       
    91     return false;
       
    92 }
       
    93 
       
    94 QString MakefileGenerator::mkdir_p_asstring(const QString &dir, bool escape) const
       
    95 {
       
    96     QString ret =  "@$(CHK_DIR_EXISTS) ";
       
    97     if(escape)
       
    98         ret += escapeFilePath(dir);
       
    99     else
       
   100         ret += dir;
       
   101     ret += " ";
       
   102     if(isWindowsShell())
       
   103         ret += "$(MKDIR)";
       
   104     else
       
   105         ret += "|| $(MKDIR)";
       
   106     ret += " ";
       
   107     if(escape)
       
   108         ret += escapeFilePath(dir);
       
   109     else
       
   110         ret += dir;
       
   111     ret += " ";
       
   112     return ret;
       
   113 }
       
   114 
       
   115 bool MakefileGenerator::mkdir(const QString &in_path) const
       
   116 {
       
   117     QString path = Option::fixPathToLocalOS(in_path);
       
   118     if(QFile::exists(path))
       
   119         return true;
       
   120 
       
   121     QDir d;
       
   122     if(path.startsWith(QDir::separator())) {
       
   123         d.cd(QString(QDir::separator()));
       
   124         path.remove(0, 1);
       
   125     }
       
   126     bool ret = true;
       
   127 #ifdef Q_OS_WIN
       
   128     bool driveExists = true;
       
   129     if(!QDir::isRelativePath(path)) {
       
   130         if(QFile::exists(path.left(3))) {
       
   131             d.cd(path.left(3));
       
   132             path.remove(0, 3);
       
   133         } else {
       
   134             warn_msg(WarnLogic, "Cannot access drive '%s' (%s)",
       
   135                      path.left(3).toLatin1().data(), path.toLatin1().data());
       
   136             driveExists = false;
       
   137         }
       
   138     }
       
   139     if(driveExists)
       
   140 #endif
       
   141     {
       
   142         QStringList subs = path.split(QDir::separator());
       
   143         for(QStringList::Iterator subit = subs.begin(); subit != subs.end(); ++subit) {
       
   144             if(!d.cd(*subit)) {
       
   145                 d.mkdir((*subit));
       
   146                 if(d.exists((*subit))) {
       
   147                     d.cd((*subit));
       
   148                 } else {
       
   149                     ret = false;
       
   150                     break;
       
   151                 }
       
   152             }
       
   153         }
       
   154     }
       
   155     return ret;
       
   156 }
       
   157 
       
   158 // ** base makefile generator
       
   159 MakefileGenerator::MakefileGenerator() :
       
   160     init_opath_already(false), init_already(false), no_io(false), project(0)
       
   161 {
       
   162 }
       
   163 
       
   164 
       
   165 void
       
   166 MakefileGenerator::verifyCompilers()
       
   167 {
       
   168     QMap<QString, QStringList> &v = project->variables();
       
   169     QStringList &quc = v["QMAKE_EXTRA_COMPILERS"];
       
   170     for(int i = 0; i < quc.size(); ) {
       
   171         bool error = false;
       
   172         QString comp = quc.at(i);
       
   173         if(v[comp + ".output"].isEmpty()) {
       
   174             if(!v[comp + ".output_function"].isEmpty()) {
       
   175                 v[comp + ".output"].append("${QMAKE_FUNC_FILE_IN_" + v[comp + ".output_function"].first() + "}");
       
   176             } else {
       
   177                 error = true;
       
   178                 warn_msg(WarnLogic, "Compiler: %s: No output file specified", comp.toLatin1().constData());
       
   179             }
       
   180         } else if(v[comp + ".input"].isEmpty()) {
       
   181             error = true;
       
   182             warn_msg(WarnLogic, "Compiler: %s: No input variable specified", comp.toLatin1().constData());
       
   183         }
       
   184         if(error)
       
   185             quc.removeAt(i);
       
   186         else
       
   187             ++i;
       
   188     }
       
   189 }
       
   190 
       
   191 void
       
   192 MakefileGenerator::initOutPaths()
       
   193 {
       
   194     if(init_opath_already)
       
   195         return;
       
   196     verifyCompilers();
       
   197     init_opath_already = true;
       
   198     QMap<QString, QStringList> &v = project->variables();
       
   199     //for shadow builds
       
   200     if(!v.contains("QMAKE_ABSOLUTE_SOURCE_PATH")) {
       
   201         if(Option::mkfile::do_cache && !Option::mkfile::cachefile.isEmpty() &&
       
   202            v.contains("QMAKE_ABSOLUTE_SOURCE_ROOT")) {
       
   203             QString root = v["QMAKE_ABSOLUTE_SOURCE_ROOT"].first();
       
   204             root = QDir::fromNativeSeparators(root);
       
   205             if(!root.isEmpty()) {
       
   206                 QFileInfo fi = fileInfo(Option::mkfile::cachefile);
       
   207                 if(!fi.makeAbsolute()) {
       
   208                     QString cache_r = fi.path(), pwd = Option::output_dir;
       
   209                     if(pwd.startsWith(cache_r) && !pwd.startsWith(root)) {
       
   210                         pwd = root + pwd.mid(cache_r.length());
       
   211                         if(exists(pwd))
       
   212                             v.insert("QMAKE_ABSOLUTE_SOURCE_PATH", QStringList(pwd));
       
   213                     }
       
   214                 }
       
   215             }
       
   216         }
       
   217     }
       
   218     if(!v["QMAKE_ABSOLUTE_SOURCE_PATH"].isEmpty()) {
       
   219         QString &asp = v["QMAKE_ABSOLUTE_SOURCE_PATH"].first();
       
   220         asp = QDir::fromNativeSeparators(asp);
       
   221         if(asp.isEmpty() || asp == Option::output_dir) //if they're the same, why bother?
       
   222             v["QMAKE_ABSOLUTE_SOURCE_PATH"].clear();
       
   223     }
       
   224 
       
   225     QString currentDir = qmake_getpwd(); //just to go back to
       
   226 
       
   227     //some builtin directories
       
   228     if(project->isEmpty("PRECOMPILED_DIR") && !project->isEmpty("OBJECTS_DIR"))
       
   229         v["PRECOMPILED_DIR"] = v["OBJECTS_DIR"];
       
   230     QString dirs[] = { QString("OBJECTS_DIR"), QString("DESTDIR"), QString("QMAKE_PKGCONFIG_DESTDIR"),
       
   231                        QString("SUBLIBS_DIR"), QString("DLLDESTDIR"), QString("QMAKE_LIBTOOL_DESTDIR"),
       
   232                        QString("PRECOMPILED_DIR"), QString() };
       
   233     for(int x = 0; !dirs[x].isEmpty(); x++) {
       
   234         if(v[dirs[x]].isEmpty())
       
   235             continue;
       
   236         const QString orig_path = v[dirs[x]].first();
       
   237 
       
   238         QString &pathRef = v[dirs[x]].first();
       
   239         pathRef = fileFixify(pathRef, Option::output_dir, Option::output_dir);
       
   240 
       
   241 #ifdef Q_OS_WIN
       
   242         // We don't want to add a separator for DLLDESTDIR on Windows (###why?)
       
   243         if(!(dirs[x] == "DLLDESTDIR"))
       
   244 #endif
       
   245         {
       
   246             if(!pathRef.endsWith(Option::dir_sep))
       
   247                 pathRef += Option::dir_sep;
       
   248         }
       
   249 
       
   250         if(noIO())
       
   251             continue;
       
   252 
       
   253         QString path = project->first(dirs[x]); //not to be changed any further
       
   254         path = fileFixify(path, currentDir, Option::output_dir);
       
   255         debug_msg(3, "Fixed output_dir %s (%s) into %s", dirs[x].toLatin1().constData(),
       
   256                   orig_path.toLatin1().constData(), path.toLatin1().constData());
       
   257         if(!mkdir(path))
       
   258             warn_msg(WarnLogic, "%s: Cannot access directory '%s'", dirs[x].toLatin1().constData(),
       
   259                      path.toLatin1().constData());
       
   260     }
       
   261 
       
   262     //out paths from the extra compilers
       
   263     const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
       
   264     for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
       
   265         QString tmp_out = project->values((*it) + ".output").first();
       
   266         if(tmp_out.isEmpty())
       
   267             continue;
       
   268         const QStringList &tmp = project->values((*it) + ".input");
       
   269         for(QStringList::ConstIterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
       
   270             QStringList &inputs = project->values((*it2));
       
   271             for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
       
   272                 (*input) = fileFixify((*input), Option::output_dir, Option::output_dir);
       
   273                 QString path = unescapeFilePath(replaceExtraCompilerVariables(tmp_out, (*input), QString()));
       
   274                 path = Option::fixPathToTargetOS(path);
       
   275                 int slash = path.lastIndexOf(Option::dir_sep);
       
   276                 if(slash != -1) {
       
   277                     path = path.left(slash);
       
   278                     // Make out path only if it does not contain makefile variables
       
   279                     if(!path.contains("${"))
       
   280                         if(path != "." &&
       
   281                            !mkdir(fileFixify(path, qmake_getpwd(), Option::output_dir)))
       
   282                             warn_msg(WarnLogic, "%s: Cannot access directory '%s'",
       
   283                                      (*it).toLatin1().constData(), path.toLatin1().constData());
       
   284                 }
       
   285             }
       
   286         }
       
   287     }
       
   288 
       
   289     if(!v["DESTDIR"].isEmpty()) {
       
   290         QDir d(v["DESTDIR"].first());
       
   291         if(Option::fixPathToLocalOS(d.absolutePath()) == Option::fixPathToLocalOS(Option::output_dir))
       
   292             v.remove("DESTDIR");
       
   293     }
       
   294 }
       
   295 
       
   296 QMakeProject
       
   297 *MakefileGenerator::projectFile() const
       
   298 {
       
   299     return project;
       
   300 }
       
   301 
       
   302 void
       
   303 MakefileGenerator::setProjectFile(QMakeProject *p)
       
   304 {
       
   305     if(project)
       
   306         return;
       
   307     project = p;
       
   308     init();
       
   309     usePlatformDir();
       
   310     findLibraries();
       
   311     if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE &&
       
   312        project->isActiveConfig("link_prl")) //load up prl's'
       
   313         processPrlFiles();
       
   314 }
       
   315 
       
   316 QStringList
       
   317 MakefileGenerator::findFilesInVPATH(QStringList l, uchar flags, const QString &vpath_var)
       
   318 {
       
   319     QStringList vpath;
       
   320     QMap<QString, QStringList> &v = project->variables();
       
   321     for(int val_it = 0; val_it < l.count(); ) {
       
   322         bool remove_file = false;
       
   323         QString &val = l[val_it];
       
   324         if(!val.isEmpty()) {
       
   325             QString file = fixEnvVariables(val);
       
   326             if(!(flags & VPATH_NoFixify))
       
   327                 file = fileFixify(file, qmake_getpwd(), Option::output_dir);
       
   328             if (file.at(0) == '\"' && file.at(file.length() - 1) == '\"')
       
   329                 file = file.mid(1, file.length() - 2);
       
   330 
       
   331             if(exists(file)) {
       
   332                 ++val_it;
       
   333                 continue;
       
   334             }
       
   335             bool found = false;
       
   336             if(QDir::isRelativePath(val)) {
       
   337                 if(vpath.isEmpty()) {
       
   338                     if(!vpath_var.isEmpty())
       
   339                         vpath = v[vpath_var];
       
   340                     vpath += v["VPATH"] + v["QMAKE_ABSOLUTE_SOURCE_PATH"] + v["DEPENDPATH"];
       
   341                     if(Option::output_dir != qmake_getpwd())
       
   342                         vpath += Option::output_dir;
       
   343                 }
       
   344                 for(QStringList::Iterator vpath_it = vpath.begin();
       
   345                     vpath_it != vpath.end(); ++vpath_it) {
       
   346                     QString real_dir = Option::fixPathToLocalOS((*vpath_it));
       
   347                     if(exists(real_dir + QDir::separator() + val)) {
       
   348                         QString dir = (*vpath_it);
       
   349                         if(!dir.endsWith(Option::dir_sep))
       
   350                             dir += Option::dir_sep;
       
   351                         val = dir + val;
       
   352                         if(!(flags & VPATH_NoFixify))
       
   353                             val = fileFixify(val);
       
   354                         found = true;
       
   355                         debug_msg(1, "Found file through vpath %s -> %s",
       
   356                                   file.toLatin1().constData(), val.toLatin1().constData());
       
   357                         break;
       
   358                     }
       
   359                 }
       
   360             }
       
   361             if(!found) {
       
   362                 QString dir, regex = val, real_dir;
       
   363                 if(regex.lastIndexOf(Option::dir_sep) != -1) {
       
   364                     dir = regex.left(regex.lastIndexOf(Option::dir_sep) + 1);
       
   365                     real_dir = dir;
       
   366                     if(!(flags & VPATH_NoFixify))
       
   367                         real_dir = fileFixify(real_dir, qmake_getpwd(), Option::output_dir);
       
   368                     regex.remove(0, dir.length());
       
   369                 }
       
   370                 if(real_dir.isEmpty() || exists(real_dir)) {
       
   371                     QStringList files = QDir(real_dir).entryList(QStringList(regex));
       
   372                     if(files.isEmpty()) {
       
   373                         debug_msg(1, "%s:%d Failure to find %s in vpath (%s)",
       
   374                                   __FILE__, __LINE__,
       
   375                                   val.toLatin1().constData(), vpath.join("::").toLatin1().constData());
       
   376                         if(flags & VPATH_RemoveMissingFiles)
       
   377                             remove_file = true;
       
   378                         else if(flags & VPATH_WarnMissingFiles)
       
   379                             warn_msg(WarnLogic, "Failure to find: %s", val.toLatin1().constData());
       
   380                     } else {
       
   381                         l.removeAt(val_it);
       
   382                         QString a;
       
   383                         for(int i = (int)files.count()-1; i >= 0; i--) {
       
   384                             if(files[i] == "." || files[i] == "..")
       
   385                                 continue;
       
   386                             a = dir + files[i];
       
   387                             if(!(flags & VPATH_NoFixify))
       
   388                                 a = fileFixify(a);
       
   389                             l.insert(val_it, a);
       
   390                         }
       
   391                     }
       
   392                 } else {
       
   393                     debug_msg(1, "%s:%d Cannot match %s%c%s, as %s does not exist.",
       
   394                               __FILE__, __LINE__, real_dir.toLatin1().constData(),
       
   395                               QDir::separator().toLatin1(),
       
   396                               regex.toLatin1().constData(), real_dir.toLatin1().constData());
       
   397                     if(flags & VPATH_RemoveMissingFiles)
       
   398                         remove_file = true;
       
   399                     else if(flags & VPATH_WarnMissingFiles)
       
   400                         warn_msg(WarnLogic, "Failure to find: %s", val.toLatin1().constData());
       
   401                 }
       
   402             }
       
   403         }
       
   404         if(remove_file)
       
   405             l.removeAt(val_it);
       
   406         else
       
   407             ++val_it;
       
   408     }
       
   409     return l;
       
   410 }
       
   411 
       
   412 void
       
   413 MakefileGenerator::initCompiler(const MakefileGenerator::Compiler &comp)
       
   414 {
       
   415     QMap<QString, QStringList> &v = project->variables();
       
   416     QStringList &l = v[comp.variable_in];
       
   417     // find all the relevant file inputs
       
   418     if(!init_compiler_already.contains(comp.variable_in)) {
       
   419         init_compiler_already.insert(comp.variable_in, true);
       
   420         if(!noIO())
       
   421             l = findFilesInVPATH(l, (comp.flags & Compiler::CompilerRemoveNoExist) ?
       
   422                                  VPATH_RemoveMissingFiles : VPATH_WarnMissingFiles, "VPATH_" + comp.variable_in);
       
   423     }
       
   424 }
       
   425 
       
   426 void
       
   427 MakefileGenerator::init()
       
   428 {
       
   429     initOutPaths();
       
   430     if(init_already)
       
   431         return;
       
   432     verifyCompilers();
       
   433     init_already = true;
       
   434 
       
   435     QMap<QString, QStringList> &v = project->variables();
       
   436     QStringList &quc = v["QMAKE_EXTRA_COMPILERS"];
       
   437 
       
   438     //make sure the COMPILERS are in the correct input/output chain order
       
   439     for(int comp_out = 0, jump_count = 0; comp_out < quc.size(); ++comp_out) {
       
   440     continue_compiler_chain:
       
   441         if(jump_count > quc.size()) //just to avoid an infinite loop here
       
   442             break;
       
   443         if(project->variables().contains(quc.at(comp_out) + ".variable_out")) {
       
   444             const QStringList &outputs = project->variables().value(quc.at(comp_out) + ".variable_out");
       
   445             for(int out = 0; out < outputs.size(); ++out) {
       
   446                 for(int comp_in = 0; comp_in < quc.size(); ++comp_in) {
       
   447                     if(comp_in == comp_out)
       
   448                         continue;
       
   449                     if(project->variables().contains(quc.at(comp_in) + ".input")) {
       
   450                         const QStringList &inputs = project->variables().value(quc.at(comp_in) + ".input");
       
   451                         for(int in = 0; in < inputs.size(); ++in) {
       
   452                             if(inputs.at(in) == outputs.at(out) && comp_out > comp_in) {
       
   453                                 ++jump_count;
       
   454                                 //move comp_out to comp_in and continue the compiler chain
       
   455                                 quc.move(comp_out, comp_in);
       
   456                                 comp_out = comp_in;
       
   457                                 goto continue_compiler_chain;
       
   458                             }
       
   459                         }
       
   460                     }
       
   461                 }
       
   462             }
       
   463         }
       
   464     }
       
   465 
       
   466     if(!project->isEmpty("QMAKE_SUBSTITUTES")) {
       
   467         const QStringList &subs = v["QMAKE_SUBSTITUTES"];
       
   468         for(int i = 0; i < subs.size(); ++i) {
       
   469             if(!subs.at(i).endsWith(".in")) {
       
   470                 warn_msg(WarnLogic, "Substitute '%s' does not end with '.in'",
       
   471                          subs.at(i).toLatin1().constData());
       
   472                 continue;
       
   473             }
       
   474             QFile in(fileFixify(subs.at(i))), out(fileInfo(subs.at(i)).fileName());
       
   475             if(out.fileName().endsWith(".in"))
       
   476                 out.setFileName(out.fileName().left(out.fileName().length()-3));
       
   477             if(in.open(QFile::ReadOnly)) {
       
   478                 QString contents;
       
   479                 QStack<int> state;
       
   480                 enum { IN_CONDITION, MET_CONDITION, PENDING_CONDITION };
       
   481                 for(int count = 1; !in.atEnd(); ++count) {
       
   482                     QString line = QString::fromUtf8(in.readLine());
       
   483                     if(line.startsWith("!!IF ")) {
       
   484                         if(state.isEmpty() || state.top() == IN_CONDITION) {
       
   485                             QString test = line.mid(5, line.length()-(5+1));
       
   486                             if(project->test(test))
       
   487                                 state.push(IN_CONDITION);
       
   488                             else
       
   489                                 state.push(PENDING_CONDITION);
       
   490                         } else {
       
   491                             state.push(MET_CONDITION);
       
   492                         }
       
   493                     } else if(line.startsWith("!!ELIF ")) {
       
   494                         if(state.isEmpty()) {
       
   495                             warn_msg(WarnLogic, "(%s:%d): Unexpected else condition",
       
   496                                      in.fileName().toLatin1().constData(), count);
       
   497                         } else if(state.top() == PENDING_CONDITION) {
       
   498                             QString test = line.mid(7, line.length()-(7+1));
       
   499                             if(project->test(test))  {
       
   500                                 state.pop();
       
   501                                 state.push(IN_CONDITION);
       
   502                             }
       
   503                         } else if(state.top() == IN_CONDITION) {
       
   504                             state.pop();
       
   505                             state.push(MET_CONDITION);
       
   506                         }
       
   507                     } else if(line.startsWith("!!ELSE")) {
       
   508                         if(state.isEmpty()) {
       
   509                             warn_msg(WarnLogic, "(%s:%d): Unexpected else condition",
       
   510                                      in.fileName().toLatin1().constData(), count);
       
   511                         } else if(state.top() == PENDING_CONDITION) {
       
   512                             state.pop();
       
   513                             state.push(IN_CONDITION);
       
   514                         } else if(state.top() == IN_CONDITION) {
       
   515                             state.pop();
       
   516                             state.push(MET_CONDITION);
       
   517                         }
       
   518                     } else if(line.startsWith("!!ENDIF")) {
       
   519                         if(state.isEmpty())
       
   520                             warn_msg(WarnLogic, "(%s:%d): Unexpected endif",
       
   521                                      in.fileName().toLatin1().constData(), count);
       
   522                         else
       
   523                             state.pop();
       
   524                     } else if(state.isEmpty() || state.top() == IN_CONDITION) {
       
   525                         contents += project->expand(line).join(QString(Option::field_sep));
       
   526                     }
       
   527                 }
       
   528                 if(out.exists() && out.open(QFile::ReadOnly)) {
       
   529                     QString old = QString::fromUtf8(out.readAll());
       
   530                     if(contents == old) {
       
   531                         v["QMAKE_INTERNAL_INCLUDED_FILES"].append(subs.at(i));
       
   532                         continue;
       
   533                     }
       
   534                     out.close();
       
   535                     if(!out.remove()) {
       
   536                         warn_msg(WarnLogic, "Cannot clear substitute '%s'",
       
   537                                  out.fileName().toLatin1().constData());
       
   538                         continue;
       
   539                     }
       
   540                 }
       
   541                 if(out.open(QFile::WriteOnly)) {
       
   542                     v["QMAKE_INTERNAL_INCLUDED_FILES"].append(subs.at(i));
       
   543                     out.write(contents.toUtf8());
       
   544                 } else {
       
   545                     warn_msg(WarnLogic, "Cannot open substitute for output '%s'",
       
   546                              out.fileName().toLatin1().constData());
       
   547                 }
       
   548             } else {
       
   549                 warn_msg(WarnLogic, "Cannot open substitute for input '%s'",
       
   550                          in.fileName().toLatin1().constData());
       
   551             }
       
   552         }
       
   553     }
       
   554 
       
   555     int x;
       
   556 
       
   557     //build up a list of compilers
       
   558     QList<Compiler> compilers;
       
   559     {
       
   560         const char *builtins[] = { "OBJECTS", "SOURCES", "PRECOMPILED_HEADER", 0 };
       
   561         for(x = 0; builtins[x]; ++x) {
       
   562             Compiler compiler;
       
   563             compiler.variable_in = builtins[x];
       
   564             compiler.flags = Compiler::CompilerBuiltin;
       
   565             compiler.type = QMakeSourceFileInfo::TYPE_C;
       
   566             if(!strcmp(builtins[x], "OBJECTS"))
       
   567                 compiler.flags |= Compiler::CompilerNoCheckDeps;
       
   568             compilers.append(compiler);
       
   569         }
       
   570         for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
       
   571             const QStringList &inputs = v[(*it) + ".input"];
       
   572             for(x = 0; x < inputs.size(); ++x) {
       
   573                 Compiler compiler;
       
   574                 compiler.variable_in = inputs.at(x);
       
   575                 compiler.flags = Compiler::CompilerNoFlags;
       
   576                 if(v[(*it) + ".CONFIG"].indexOf("ignore_no_exist") != -1)
       
   577                     compiler.flags |= Compiler::CompilerRemoveNoExist;
       
   578                 if(v[(*it) + ".CONFIG"].indexOf("no_dependencies") != -1)
       
   579                     compiler.flags |= Compiler::CompilerNoCheckDeps;
       
   580 
       
   581                 QString dep_type;
       
   582                 if(!project->isEmpty((*it) + ".dependency_type"))
       
   583                     dep_type = project->first((*it) + ".dependency_type");
       
   584                 if (dep_type.isEmpty())
       
   585                     compiler.type = QMakeSourceFileInfo::TYPE_UNKNOWN;
       
   586                 else if(dep_type == "TYPE_UI")
       
   587                     compiler.type = QMakeSourceFileInfo::TYPE_UI;
       
   588                 else
       
   589                     compiler.type = QMakeSourceFileInfo::TYPE_C;
       
   590                 compilers.append(compiler);
       
   591             }
       
   592         }
       
   593     }
       
   594     { //do the path fixifying
       
   595         QStringList paths;
       
   596         for(x = 0; x < compilers.count(); ++x) {
       
   597             if(!paths.contains(compilers.at(x).variable_in))
       
   598                 paths << compilers.at(x).variable_in;
       
   599         }
       
   600         paths << "INCLUDEPATH" << "QMAKE_INTERNAL_INCLUDED_FILES" << "PRECOMPILED_HEADER";
       
   601         for(int y = 0; y < paths.count(); y++) {
       
   602             QStringList &l = v[paths[y]];
       
   603             for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
       
   604                 if((*it).isEmpty())
       
   605                     continue;
       
   606                 if(exists((*it)))
       
   607                     (*it) = fileFixify((*it));
       
   608             }
       
   609         }
       
   610     }
       
   611 
       
   612     if(noIO() || !doDepends())
       
   613         QMakeSourceFileInfo::setDependencyMode(QMakeSourceFileInfo::NonRecursive);
       
   614     for(x = 0; x < compilers.count(); ++x)
       
   615         initCompiler(compilers.at(x));
       
   616 
       
   617     //merge actual compiler outputs into their variable_out. This is done last so that
       
   618     //files are already properly fixified.
       
   619     for(QStringList::Iterator it = quc.begin(); it != quc.end(); ++it) {
       
   620         QString tmp_out = project->values((*it) + ".output").first();
       
   621         if(tmp_out.isEmpty())
       
   622             continue;
       
   623         if(project->values((*it) + ".CONFIG").indexOf("combine") != -1) {
       
   624             QStringList &compilerInputs = project->values((*it) + ".input");
       
   625             // Don't generate compiler output if it doesn't have input.
       
   626             if (compilerInputs.isEmpty() || project->values(compilerInputs.first()).isEmpty())
       
   627                 continue;
       
   628             if(tmp_out.indexOf("$") == -1) {
       
   629                 if(!verifyExtraCompiler((*it), QString())) //verify
       
   630                     continue;
       
   631                 QString out = fileFixify(tmp_out, Option::output_dir, Option::output_dir);
       
   632                 bool pre_dep = (project->values((*it) + ".CONFIG").indexOf("target_predeps") != -1);
       
   633                 if(project->variables().contains((*it) + ".variable_out")) {
       
   634                     const QStringList &var_out = project->variables().value((*it) + ".variable_out");
       
   635                     for(int i = 0; i < var_out.size(); ++i) {
       
   636                         QString v = var_out.at(i);
       
   637                         if(v == QLatin1String("SOURCES"))
       
   638                             v = "GENERATED_SOURCES";
       
   639                         else if(v == QLatin1String("OBJECTS"))
       
   640                             pre_dep = false;
       
   641                         QStringList &list = project->values(v);
       
   642                         if(!list.contains(out))
       
   643                             list.append(out);
       
   644                     }
       
   645                 } else if(project->values((*it) + ".CONFIG").indexOf("no_link") == -1) {
       
   646                     QStringList &list = project->values("OBJECTS");
       
   647                     pre_dep = false;
       
   648                     if(!list.contains(out))
       
   649                         list.append(out);
       
   650                 } else {
       
   651                         QStringList &list = project->values("UNUSED_SOURCES");
       
   652                         if(!list.contains(out))
       
   653                             list.append(out);
       
   654                 }
       
   655                 if(pre_dep) {
       
   656                     QStringList &list = project->variables()["PRE_TARGETDEPS"];
       
   657                     if(!list.contains(out))
       
   658                         list.append(out);
       
   659                 }
       
   660             }
       
   661         } else {
       
   662             QStringList &tmp = project->values((*it) + ".input");
       
   663             for(QStringList::Iterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
       
   664                 const QStringList inputs = project->values((*it2));
       
   665                 for(QStringList::ConstIterator input = inputs.constBegin(); input != inputs.constEnd(); ++input) {
       
   666                     if((*input).isEmpty())
       
   667                         continue;
       
   668                     QString in = Option::fixPathToTargetOS((*input), false);
       
   669                     if(!verifyExtraCompiler((*it), in)) //verify
       
   670                         continue;
       
   671                     QString out = replaceExtraCompilerVariables(tmp_out, (*input), QString());
       
   672                     out = fileFixify(out, Option::output_dir, Option::output_dir);
       
   673                     bool pre_dep = (project->values((*it) + ".CONFIG").indexOf("target_predeps") != -1);
       
   674                     if(project->variables().contains((*it) + ".variable_out")) {
       
   675                         const QStringList &var_out = project->variables().value((*it) + ".variable_out");
       
   676                         for(int i = 0; i < var_out.size(); ++i) {
       
   677                             QString v = var_out.at(i);
       
   678                             if(v == QLatin1String("SOURCES"))
       
   679                                 v = "GENERATED_SOURCES";
       
   680                             else if(v == QLatin1String("OBJECTS"))
       
   681                                 pre_dep = false;
       
   682                             QStringList &list = project->values(v);
       
   683                             if(!list.contains(out))
       
   684                                 list.append(out);
       
   685                         }
       
   686                     } else if(project->values((*it) + ".CONFIG").indexOf("no_link") == -1) {
       
   687                         pre_dep = false;
       
   688                         QStringList &list = project->values("OBJECTS");
       
   689                         if(!list.contains(out))
       
   690                             list.append(out);
       
   691                     } else {
       
   692                         QStringList &list = project->values("UNUSED_SOURCES");
       
   693                         if(!list.contains(out))
       
   694                             list.append(out);
       
   695                     }
       
   696                     if(pre_dep) {
       
   697                         QStringList &list = project->variables()["PRE_TARGETDEPS"];
       
   698                         if(!list.contains(out))
       
   699                             list.append(out);
       
   700                     }
       
   701                 }
       
   702             }
       
   703         }
       
   704     }
       
   705 
       
   706     //handle dependencies
       
   707     depHeuristicsCache.clear();
       
   708     if(!noIO()) {
       
   709         // dependency paths
       
   710         QStringList incDirs = v["DEPENDPATH"] + v["QMAKE_ABSOLUTE_SOURCE_PATH"];
       
   711         if(project->isActiveConfig("depend_includepath"))
       
   712             incDirs += v["INCLUDEPATH"];
       
   713         if(!project->isActiveConfig("no_include_pwd")) {
       
   714             QString pwd = qmake_getpwd();
       
   715             if(pwd.isEmpty())
       
   716                 pwd = ".";
       
   717             incDirs += pwd;
       
   718         }
       
   719         QList<QMakeLocalFileName> deplist;
       
   720         for(QStringList::Iterator it = incDirs.begin(); it != incDirs.end(); ++it)
       
   721             deplist.append(QMakeLocalFileName(unescapeFilePath((*it))));
       
   722         QMakeSourceFileInfo::setDependencyPaths(deplist);
       
   723         debug_msg(1, "Dependency Directories: %s", incDirs.join(" :: ").toLatin1().constData());
       
   724         //cache info
       
   725         if(project->isActiveConfig("qmake_cache")) {
       
   726             QString cache_file;
       
   727             if(!project->isEmpty("QMAKE_INTERNAL_CACHE_FILE")) {
       
   728                 cache_file = QDir::fromNativeSeparators(project->first("QMAKE_INTERNAL_CACHE_FILE"));
       
   729             } else {
       
   730                 cache_file = ".qmake.internal.cache";
       
   731                 if(project->isActiveConfig("build_pass"))
       
   732                     cache_file += ".BUILD." + project->first("BUILD_PASS");
       
   733             }
       
   734             if(cache_file.indexOf('/') == -1)
       
   735                 cache_file.prepend(Option::output_dir + '/');
       
   736             QMakeSourceFileInfo::setCacheFile(cache_file);
       
   737         }
       
   738 
       
   739         //add to dependency engine
       
   740         for(x = 0; x < compilers.count(); ++x) {
       
   741             const MakefileGenerator::Compiler &comp = compilers.at(x);
       
   742             if(!(comp.flags & Compiler::CompilerNoCheckDeps))
       
   743                 addSourceFiles(v[comp.variable_in], QMakeSourceFileInfo::SEEK_DEPS,
       
   744                                (QMakeSourceFileInfo::SourceFileType)comp.type);
       
   745         }
       
   746     }
       
   747 
       
   748     processSources(); //remove anything in SOURCES which is included (thus it need not be linked in)
       
   749 
       
   750     //all sources and generated sources must be turned into objects at some point (the one builtin compiler)
       
   751     v["OBJECTS"] += createObjectList(v["SOURCES"]) + createObjectList(v["GENERATED_SOURCES"]);
       
   752 
       
   753     //Translation files
       
   754     if(!project->isEmpty("TRANSLATIONS")) {
       
   755         QStringList &trf = project->values("TRANSLATIONS");
       
   756         for(QStringList::Iterator it = trf.begin(); it != trf.end(); ++it)
       
   757             (*it) = Option::fixPathToLocalOS((*it));
       
   758     }
       
   759 
       
   760     { //get the output_dir into the pwd
       
   761         if(fileFixify(Option::output_dir) != fileFixify(qmake_getpwd()))
       
   762             project->values("INCLUDEPATH").append(fileFixify(Option::output_dir,
       
   763                                                                   Option::output_dir,
       
   764                                                                   Option::output_dir));
       
   765     }
       
   766 
       
   767     //fix up the target deps
       
   768     QString fixpaths[] = { QString("PRE_TARGETDEPS"), QString("POST_TARGETDEPS"), QString() };
       
   769     for(int path = 0; !fixpaths[path].isNull(); path++) {
       
   770         QStringList &l = v[fixpaths[path]];
       
   771         for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it) {
       
   772             if(!(*val_it).isEmpty())
       
   773                 (*val_it) = escapeDependencyPath(Option::fixPathToTargetOS((*val_it), false, false));
       
   774         }
       
   775     }
       
   776 
       
   777     //extra depends
       
   778     if(!project->isEmpty("DEPENDS")) {
       
   779         QStringList &l = v["DEPENDS"];
       
   780         for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
       
   781             QStringList files = v[(*it) + ".file"] + v[(*it) + ".files"]; //why do I support such evil things?
       
   782             for(QStringList::Iterator file_it = files.begin(); file_it != files.end(); ++file_it) {
       
   783                 QStringList &out_deps = findDependencies(*file_it);
       
   784                 QStringList &in_deps = v[(*it) + ".depends"]; //even more evilness..
       
   785                 for(QStringList::Iterator dep_it = in_deps.begin(); dep_it != in_deps.end(); ++dep_it) {
       
   786                     if(exists(*dep_it)) {
       
   787                         out_deps.append(*dep_it);
       
   788                     } else {
       
   789                         QString dir, regex = Option::fixPathToLocalOS((*dep_it));
       
   790                         if(regex.lastIndexOf(Option::dir_sep) != -1) {
       
   791                             dir = regex.left(regex.lastIndexOf(Option::dir_sep) + 1);
       
   792                             regex.remove(0, dir.length());
       
   793                         }
       
   794                         QStringList files = QDir(dir).entryList(QStringList(regex));
       
   795                         if(files.isEmpty()) {
       
   796                             warn_msg(WarnLogic, "Dependency for [%s]: Not found %s", (*file_it).toLatin1().constData(),
       
   797                                      (*dep_it).toLatin1().constData());
       
   798                         } else {
       
   799                             for(int i = 0; i < files.count(); i++)
       
   800                                 out_deps.append(dir + files[i]);
       
   801                         }
       
   802                     }
       
   803                 }
       
   804             }
       
   805         }
       
   806     }
       
   807 
       
   808     // escape qmake command
       
   809     if (!project->isEmpty("QMAKE_QMAKE")) {
       
   810         project->values("QMAKE_QMAKE") = escapeFilePaths(project->values("QMAKE_QMAKE"));
       
   811     }
       
   812 }
       
   813 
       
   814 bool
       
   815 MakefileGenerator::processPrlFile(QString &file)
       
   816 {
       
   817     bool ret = false, try_replace_file=false;
       
   818     QString meta_file, orig_file = file;
       
   819     if(QMakeMetaInfo::libExists(file)) {
       
   820         try_replace_file = true;
       
   821         meta_file = file;
       
   822         file = "";
       
   823     } else {
       
   824         QString tmp = file;
       
   825         int ext = tmp.lastIndexOf('.');
       
   826         if(ext != -1)
       
   827             tmp = tmp.left(ext);
       
   828         meta_file = tmp;
       
   829     }
       
   830 //    meta_file = fileFixify(meta_file);
       
   831     QString real_meta_file = Option::fixPathToLocalOS(meta_file);
       
   832     if(!meta_file.isEmpty()) {
       
   833         QString f = fileFixify(real_meta_file, qmake_getpwd(), Option::output_dir);
       
   834         if(QMakeMetaInfo::libExists(f)) {
       
   835             QMakeMetaInfo libinfo;
       
   836             debug_msg(1, "Processing PRL file: %s", real_meta_file.toLatin1().constData());
       
   837             if(!libinfo.readLib(f)) {
       
   838                 fprintf(stderr, "Error processing meta file: %s\n", real_meta_file.toLatin1().constData());
       
   839             } else if(project->isActiveConfig("no_read_prl_" + libinfo.type().toLower())) {
       
   840                 debug_msg(2, "Ignored meta file %s [%s]", real_meta_file.toLatin1().constData(), libinfo.type().toLatin1().constData());
       
   841             } else {
       
   842                 ret = true;
       
   843                 QMap<QString, QStringList> &vars = libinfo.variables();
       
   844                 for(QMap<QString, QStringList>::Iterator it = vars.begin(); it != vars.end(); ++it)
       
   845                     processPrlVariable(it.key(), it.value());
       
   846                 if(try_replace_file && !libinfo.isEmpty("QMAKE_PRL_TARGET")) {
       
   847                     QString dir;
       
   848                     int slsh = real_meta_file.lastIndexOf(Option::dir_sep);
       
   849                     if(slsh != -1)
       
   850                         dir = real_meta_file.left(slsh+1);
       
   851                     file = libinfo.first("QMAKE_PRL_TARGET");
       
   852                     if(QDir::isRelativePath(file))
       
   853                         file.prepend(dir);
       
   854                 }
       
   855             }
       
   856         }
       
   857         if(ret) {
       
   858             QString mf = QMakeMetaInfo::findLib(meta_file);
       
   859             if(project->values("QMAKE_PRL_INTERNAL_FILES").indexOf(mf) == -1)
       
   860                project->values("QMAKE_PRL_INTERNAL_FILES").append(mf);
       
   861             if(project->values("QMAKE_INTERNAL_INCLUDED_FILES").indexOf(mf) == -1)
       
   862                project->values("QMAKE_INTERNAL_INCLUDED_FILES").append(mf);
       
   863         }
       
   864     }
       
   865     if(try_replace_file && file.isEmpty()) {
       
   866 #if 0
       
   867         warn_msg(WarnLogic, "Found prl [%s] file with no target [%s]!", meta_file.toLatin1().constData(),
       
   868                  orig_file.toLatin1().constData());
       
   869 #endif
       
   870         file = orig_file;
       
   871     }
       
   872     return ret;
       
   873 }
       
   874 
       
   875 void
       
   876 MakefileGenerator::filterIncludedFiles(const QString &var)
       
   877 {
       
   878     QStringList &inputs = project->values(var);
       
   879     for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ) {
       
   880         if(QMakeSourceFileInfo::included((*input)) > 0)
       
   881             input = inputs.erase(input);
       
   882         else
       
   883             ++input;
       
   884     }
       
   885 }
       
   886 
       
   887 void
       
   888 MakefileGenerator::processPrlVariable(const QString &var, const QStringList &l)
       
   889 {
       
   890     if(var == "QMAKE_PRL_LIBS") {
       
   891         QString where = "QMAKE_LIBS";
       
   892         if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS"))
       
   893             where = project->first("QMAKE_INTERNAL_PRL_LIBS");
       
   894         QStringList &out = project->values(where);
       
   895         for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
       
   896             if(out.indexOf((*it)) == -1)
       
   897                 out.append((*it));
       
   898         }
       
   899     } else if(var == "QMAKE_PRL_DEFINES") {
       
   900         QStringList &out = project->values("DEFINES");
       
   901         for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
       
   902             if(out.indexOf((*it)) == -1 &&
       
   903                project->values("PRL_EXPORT_DEFINES").indexOf((*it)) == -1)
       
   904                 out.append((*it));
       
   905         }
       
   906     }
       
   907 }
       
   908 
       
   909 void
       
   910 MakefileGenerator::processPrlFiles()
       
   911 {
       
   912     QHash<QString, bool> processed;
       
   913     for(bool ret = false; true; ret = false) {
       
   914         //read in any prl files included..
       
   915         QStringList l_out;
       
   916         QString where = "QMAKE_LIBS";
       
   917         if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS"))
       
   918             where = project->first("QMAKE_INTERNAL_PRL_LIBS");
       
   919         QStringList &l = project->values(where);
       
   920         for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
       
   921             QString file = (*it);
       
   922             if(!processed.contains(file) && processPrlFile(file)) {
       
   923                 processed.insert(file, true);
       
   924                 ret = true;
       
   925             }
       
   926             if(!file.isEmpty())
       
   927                 l_out.append(file);
       
   928         }
       
   929         if(ret)
       
   930             l = l_out;
       
   931         else
       
   932             break;
       
   933     }
       
   934 }
       
   935 
       
   936 void
       
   937 MakefileGenerator::writePrlFile(QTextStream &t)
       
   938 {
       
   939     QString target = project->first("TARGET");
       
   940     int slsh = target.lastIndexOf(Option::dir_sep);
       
   941     if(slsh != -1)
       
   942         target.remove(0, slsh + 1);
       
   943     QString bdir = Option::output_dir;
       
   944     if(bdir.isEmpty())
       
   945         bdir = qmake_getpwd();
       
   946     t << "QMAKE_PRL_BUILD_DIR = " << bdir << endl;
       
   947 
       
   948     if(!project->projectFile().isEmpty() && project->projectFile() != "-")
       
   949         t << "QMAKE_PRO_INPUT = " << project->projectFile().section('/', -1) << endl;
       
   950 
       
   951     if(!project->isEmpty("QMAKE_ABSOLUTE_SOURCE_PATH"))
       
   952         t << "QMAKE_PRL_SOURCE_DIR = " << project->first("QMAKE_ABSOLUTE_SOURCE_PATH") << endl;
       
   953     t << "QMAKE_PRL_TARGET = " << target << endl;
       
   954     if(!project->isEmpty("PRL_EXPORT_DEFINES"))
       
   955         t << "QMAKE_PRL_DEFINES = " << project->values("PRL_EXPORT_DEFINES").join(" ") << endl;
       
   956     if(!project->isEmpty("PRL_EXPORT_CFLAGS"))
       
   957         t << "QMAKE_PRL_CFLAGS = " << project->values("PRL_EXPORT_CFLAGS").join(" ") << endl;
       
   958     if(!project->isEmpty("PRL_EXPORT_CXXFLAGS"))
       
   959         t << "QMAKE_PRL_CXXFLAGS = " << project->values("PRL_EXPORT_CXXFLAGS").join(" ") << endl;
       
   960     if(!project->isEmpty("CONFIG"))
       
   961         t << "QMAKE_PRL_CONFIG = " << project->values("CONFIG").join(" ") << endl;
       
   962     if(!project->isEmpty("TARGET_VERSION_EXT"))
       
   963         t << "QMAKE_PRL_VERSION = " << project->first("TARGET_VERSION_EXT") << endl;
       
   964     else if(!project->isEmpty("VERSION"))
       
   965         t << "QMAKE_PRL_VERSION = " << project->first("VERSION") << endl;
       
   966     if(project->isActiveConfig("staticlib") || project->isActiveConfig("explicitlib")) {
       
   967         QStringList libs;
       
   968         if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS"))
       
   969             libs = project->values("QMAKE_INTERNAL_PRL_LIBS");
       
   970         else
       
   971             libs << "QMAKE_LIBS"; //obvious one
       
   972         if(project->isActiveConfig("staticlib"))
       
   973             libs << "QMAKE_LIBS_PRIVATE";
       
   974         t << "QMAKE_PRL_LIBS = ";
       
   975         for(QStringList::Iterator it = libs.begin(); it != libs.end(); ++it)
       
   976             t << project->values((*it)).join(" ") << " ";
       
   977         t << endl;
       
   978     }
       
   979 }
       
   980 
       
   981 bool
       
   982 MakefileGenerator::writeProjectMakefile()
       
   983 {
       
   984     usePlatformDir();
       
   985     QTextStream t(&Option::output);
       
   986 
       
   987     //header
       
   988     writeHeader(t);
       
   989 
       
   990     QList<SubTarget*> targets;
       
   991     {
       
   992         QStringList builds = project->values("BUILDS");
       
   993         for(QStringList::Iterator it = builds.begin(); it != builds.end(); ++it) {
       
   994             SubTarget *st = new SubTarget;
       
   995             targets.append(st);
       
   996             st->makefile = "$(MAKEFILE)." + (*it);
       
   997             st->name = (*it);
       
   998             st->target = project->isEmpty((*it) + ".target") ? (*it) : project->first((*it) + ".target");
       
   999         }
       
  1000     }
       
  1001     if(project->isActiveConfig("build_all")) {
       
  1002         t << "first: all" << endl;
       
  1003         QList<SubTarget*>::Iterator it;
       
  1004 
       
  1005         //install
       
  1006         t << "install: ";
       
  1007         for(it = targets.begin(); it != targets.end(); ++it)
       
  1008             t << (*it)->target << "-install ";
       
  1009         t << endl;
       
  1010 
       
  1011         //uninstall
       
  1012         t << "uninstall: ";
       
  1013         for(it = targets.begin(); it != targets.end(); ++it)
       
  1014             t << (*it)->target << "-uninstall ";
       
  1015         t << endl;
       
  1016     } else {
       
  1017         t << "first: " << targets.first()->target << endl
       
  1018           << "install: " << targets.first()->target << "-install" << endl
       
  1019           << "uninstall: " << targets.first()->target << "-uninstall" << endl;
       
  1020     }
       
  1021 
       
  1022     writeSubTargets(t, targets, SubTargetsNoFlags);
       
  1023     if(!project->isActiveConfig("no_autoqmake")) {
       
  1024         for(QList<SubTarget*>::Iterator it = targets.begin(); it != targets.end(); ++it)
       
  1025             t << (*it)->makefile << ": " <<
       
  1026                 Option::fixPathToTargetOS(fileFixify(Option::output.fileName())) << endl;
       
  1027     }
       
  1028     qDeleteAll(targets);
       
  1029     return true;
       
  1030 }
       
  1031 
       
  1032 bool
       
  1033 MakefileGenerator::write()
       
  1034 {
       
  1035     if(!project)
       
  1036         return false;
       
  1037     writePrlFile();
       
  1038     if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE || //write makefile
       
  1039        Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
       
  1040         QTextStream t(&Option::output);
       
  1041         if(!writeMakefile(t)) {
       
  1042 #if 1
       
  1043             warn_msg(WarnLogic, "Unable to generate output for: %s [TEMPLATE %s]",
       
  1044                      Option::output.fileName().toLatin1().constData(),
       
  1045                      project->first("TEMPLATE").toLatin1().constData());
       
  1046             if(Option::output.exists())
       
  1047                 Option::output.remove();
       
  1048 #endif
       
  1049         }
       
  1050     }
       
  1051     return true;
       
  1052 }
       
  1053 
       
  1054 QString
       
  1055 MakefileGenerator::prlFileName(bool fixify)
       
  1056 {
       
  1057     QString ret = project->first("TARGET_PRL");;
       
  1058     if(ret.isEmpty())
       
  1059         ret = project->first("TARGET");
       
  1060     int slsh = ret.lastIndexOf(Option::dir_sep);
       
  1061     if(slsh != -1)
       
  1062         ret.remove(0, slsh);
       
  1063     if(!ret.endsWith(Option::prl_ext)) {
       
  1064         int dot = ret.indexOf('.');
       
  1065         if(dot != -1)
       
  1066             ret.truncate(dot);
       
  1067         ret += Option::prl_ext;
       
  1068     }
       
  1069     if(!project->isEmpty("QMAKE_BUNDLE"))
       
  1070         ret.prepend(project->first("QMAKE_BUNDLE") + Option::dir_sep);
       
  1071     if(fixify) {
       
  1072         if(!project->isEmpty("DESTDIR"))
       
  1073             ret.prepend(project->first("DESTDIR"));
       
  1074         ret = Option::fixPathToLocalOS(fileFixify(ret, qmake_getpwd(), Option::output_dir));
       
  1075     }
       
  1076     return ret;
       
  1077 }
       
  1078 
       
  1079 void
       
  1080 MakefileGenerator::writePrlFile()
       
  1081 {
       
  1082     if((Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
       
  1083             Option::qmake_mode == Option::QMAKE_GENERATE_PRL)
       
  1084        && project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()
       
  1085        && project->isActiveConfig("create_prl")
       
  1086        && (project->first("TEMPLATE") == "lib"
       
  1087        || project->first("TEMPLATE") == "vclib")
       
  1088        && !project->isActiveConfig("plugin")) { //write prl file
       
  1089         QString local_prl = prlFileName();
       
  1090         QString prl = fileFixify(local_prl);
       
  1091         mkdir(fileInfo(local_prl).path());
       
  1092         QFile ft(local_prl);
       
  1093         if(ft.open(QIODevice::WriteOnly)) {
       
  1094             project->values("ALL_DEPS").append(prl);
       
  1095             project->values("QMAKE_INTERNAL_PRL_FILE").append(prl);
       
  1096             QTextStream t(&ft);
       
  1097             writePrlFile(t);
       
  1098         }
       
  1099     }
       
  1100 }
       
  1101 
       
  1102 // Manipulate directories, so it's possible to build
       
  1103 // several cross-platform targets concurrently
       
  1104 void
       
  1105 MakefileGenerator::usePlatformDir()
       
  1106 {
       
  1107     QString pltDir(project->first("QMAKE_PLATFORM_DIR"));
       
  1108     if(pltDir.isEmpty())
       
  1109         return;
       
  1110     QChar sep = QDir::separator();
       
  1111     QString slashPltDir = sep + pltDir;
       
  1112 
       
  1113     QString dirs[] = { QString("OBJECTS_DIR"), QString("DESTDIR"), QString("QMAKE_PKGCONFIG_DESTDIR"),
       
  1114                        QString("SUBLIBS_DIR"), QString("DLLDESTDIR"), QString("QMAKE_LIBTOOL_DESTDIR"),
       
  1115                        QString("PRECOMPILED_DIR"), QString("QMAKE_LIBDIR_QT"), QString() };
       
  1116     for(int i = 0; !dirs[i].isEmpty(); ++i) {
       
  1117         QString filePath = project->first(dirs[i]);
       
  1118         project->values(dirs[i]) = QStringList(filePath + (filePath.isEmpty() ? pltDir : slashPltDir));
       
  1119     }
       
  1120 
       
  1121     QString libs[] = { QString("QMAKE_LIBS_QT"), QString("QMAKE_LIBS_QT_THREAD"), QString("QMAKE_LIBS_QT_ENTRY"), QString() };
       
  1122     for(int i = 0; !libs[i].isEmpty(); ++i) {
       
  1123         QString filePath = project->first(libs[i]);
       
  1124         int fpi = filePath.lastIndexOf(sep);
       
  1125         if(fpi == -1)
       
  1126             project->values(libs[i]).prepend(pltDir + sep);
       
  1127         else
       
  1128             project->values(libs[i]) = QStringList(filePath.left(fpi) + slashPltDir + filePath.mid(fpi));
       
  1129     }
       
  1130 }
       
  1131 
       
  1132 void
       
  1133 MakefileGenerator::writeObj(QTextStream &t, const QString &src)
       
  1134 {
       
  1135     QStringList &srcl = project->values(src);
       
  1136     QStringList objl = createObjectList(srcl);
       
  1137 
       
  1138     QStringList::Iterator oit = objl.begin();
       
  1139     QStringList::Iterator sit = srcl.begin();
       
  1140     QString stringSrc("$src");
       
  1141     QString stringObj("$obj");
       
  1142     for(;sit != srcl.end() && oit != objl.end(); ++oit, ++sit) {
       
  1143         if((*sit).isEmpty())
       
  1144             continue;
       
  1145 
       
  1146         t << escapeDependencyPath((*oit)) << ": " << escapeDependencyPath((*sit)) << " " << escapeDependencyPaths(findDependencies((*sit))).join(" \\\n\t\t");
       
  1147 
       
  1148         QString comp, cimp;
       
  1149         for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) {
       
  1150             if((*sit).endsWith((*cppit))) {
       
  1151                 comp = "QMAKE_RUN_CXX";
       
  1152                 cimp = "QMAKE_RUN_CXX_IMP";
       
  1153                 break;
       
  1154             }
       
  1155         }
       
  1156         if(comp.isEmpty()) {
       
  1157             comp = "QMAKE_RUN_CC";
       
  1158             cimp = "QMAKE_RUN_CC_IMP";
       
  1159         }
       
  1160         bool use_implicit_rule = !project->isEmpty(cimp);
       
  1161         use_implicit_rule = false;
       
  1162         if(use_implicit_rule) {
       
  1163             if(!project->isEmpty("OBJECTS_DIR")) {
       
  1164                 use_implicit_rule = false;
       
  1165             } else {
       
  1166                 int dot = (*sit).lastIndexOf('.');
       
  1167                 if(dot == -1 || ((*sit).left(dot) + Option::obj_ext != (*oit)))
       
  1168                     use_implicit_rule = false;
       
  1169             }
       
  1170         }
       
  1171         if (!use_implicit_rule && !project->isEmpty(comp)) {
       
  1172             QString p = var(comp), srcf(*sit);
       
  1173             p.replace(stringSrc, escapeFilePath(srcf));
       
  1174             p.replace(stringObj, escapeFilePath((*oit)));
       
  1175             t << "\n\t" << p;
       
  1176         }
       
  1177         t << endl << endl;
       
  1178     }
       
  1179 }
       
  1180 
       
  1181 QString
       
  1182 MakefileGenerator::filePrefixRoot(const QString &root, const QString &path)
       
  1183 {
       
  1184     QString ret(root + path);
       
  1185     if(path.length() > 2 && path[1] == ':') //c:\foo
       
  1186         ret = QString(path.mid(0, 2) + root + path.mid(2));
       
  1187     while(ret.endsWith("\\"))
       
  1188         ret = ret.left(ret.length()-1);
       
  1189     return ret;
       
  1190 }
       
  1191 
       
  1192 void
       
  1193 MakefileGenerator::writeInstalls(QTextStream &t, const QString &installs, bool noBuild)
       
  1194 {
       
  1195     QString rm_dir_contents("-$(DEL_FILE)");
       
  1196     if (!isWindowsShell()) //ick
       
  1197         rm_dir_contents = "-$(DEL_FILE) -r";
       
  1198 
       
  1199     QString all_installs, all_uninstalls;
       
  1200     QStringList &l = project->values(installs);
       
  1201     for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
       
  1202         QString pvar = (*it) + ".path";
       
  1203         if(project->values((*it) + ".CONFIG").indexOf("no_path") == -1 &&
       
  1204            project->values((*it) + ".CONFIG").indexOf("dummy_install") == -1 &&
       
  1205            project->values(pvar).isEmpty()) {
       
  1206             warn_msg(WarnLogic, "%s is not defined: install target not created\n", pvar.toLatin1().constData());
       
  1207             continue;
       
  1208         }
       
  1209 
       
  1210         bool do_default = true;
       
  1211         const QString root = "$(INSTALL_ROOT)";
       
  1212         QString target, dst;
       
  1213         if(project->values((*it) + ".CONFIG").indexOf("no_path") == -1 &&
       
  1214            project->values((*it) + ".CONFIG").indexOf("dummy_install") == -1) {
       
  1215             dst = fileFixify(unescapeFilePath(project->values(pvar).first()), FileFixifyAbsolute, false);
       
  1216             if(!dst.endsWith(Option::dir_sep))
       
  1217                 dst += Option::dir_sep;
       
  1218         }
       
  1219         dst = escapeFilePath(dst);
       
  1220 
       
  1221         QStringList tmp, uninst = project->values((*it) + ".uninstall");
       
  1222         //other
       
  1223         tmp = project->values((*it) + ".extra");
       
  1224         if(tmp.isEmpty())
       
  1225             tmp = project->values((*it) + ".commands"); //to allow compatible name
       
  1226         if(!tmp.isEmpty()) {
       
  1227             do_default = false;
       
  1228             if(!target.isEmpty())
       
  1229                 target += "\n\t";
       
  1230             target += tmp.join(" ");
       
  1231         }
       
  1232         //masks
       
  1233         tmp = findFilesInVPATH(project->values((*it) + ".files"), VPATH_NoFixify);
       
  1234         tmp = fileFixify(tmp, FileFixifyAbsolute);
       
  1235         if(!tmp.isEmpty()) {
       
  1236             if(!target.isEmpty())
       
  1237                 target += "\n";
       
  1238             do_default = false;
       
  1239             for(QStringList::Iterator wild_it = tmp.begin(); wild_it != tmp.end(); ++wild_it) {
       
  1240                 QString wild = Option::fixPathToLocalOS((*wild_it), false, false);
       
  1241                 QString dirstr = qmake_getpwd(), filestr = wild;
       
  1242                 int slsh = filestr.lastIndexOf(Option::dir_sep);
       
  1243                 if(slsh != -1) {
       
  1244                     dirstr = filestr.left(slsh+1);
       
  1245                     filestr.remove(0, slsh+1);
       
  1246                 }
       
  1247                 if(!dirstr.endsWith(Option::dir_sep))
       
  1248                     dirstr += Option::dir_sep;
       
  1249                 if(exists(wild)) { //real file
       
  1250                     QString file = wild;
       
  1251                     QFileInfo fi(fileInfo(wild));
       
  1252                     if(!target.isEmpty())
       
  1253                         target += "\t";
       
  1254                     QString dst_file = filePrefixRoot(root, dst);
       
  1255                     if(fi.isDir() && project->isActiveConfig("copy_dir_files")) {
       
  1256                         if(!dst_file.endsWith(Option::dir_sep))
       
  1257                             dst_file += Option::dir_sep;
       
  1258                         dst_file += fi.fileName();
       
  1259                     }
       
  1260                     QString cmd;
       
  1261                     if (fi.isDir())
       
  1262                        cmd = "-$(INSTALL_DIR)";
       
  1263                     else if (fi.isExecutable())
       
  1264                        cmd = "-$(INSTALL_PROGRAM)";
       
  1265                     else
       
  1266                        cmd = "-$(INSTALL_FILE)";
       
  1267                     cmd += " " + escapeFilePath(wild) + " " + dst_file + "\n";
       
  1268                     target += cmd;
       
  1269                     if(!project->isActiveConfig("debug") && !project->isActiveConfig("nostrip") &&
       
  1270                        !fi.isDir() && fi.isExecutable() && !project->isEmpty("QMAKE_STRIP"))
       
  1271                         target += QString("\t-") + var("QMAKE_STRIP") + " " +
       
  1272                                   filePrefixRoot(root, fileFixify(dst + filestr, FileFixifyAbsolute, false)) + "\n";
       
  1273                     if(!uninst.isEmpty())
       
  1274                         uninst.append("\n\t");
       
  1275                     uninst.append(rm_dir_contents + " " + filePrefixRoot(root, fileFixify(dst + filestr, FileFixifyAbsolute, false)));
       
  1276                     continue;
       
  1277                 }
       
  1278                 QString local_dirstr = Option::fixPathToLocalOS(dirstr, true);
       
  1279                 QStringList files = QDir(local_dirstr).entryList(QStringList(filestr));
       
  1280                 if(project->values((*it) + ".CONFIG").indexOf("no_check_exist") != -1 && files.isEmpty()) {
       
  1281                     if(!target.isEmpty())
       
  1282                         target += "\t";
       
  1283                     QString dst_file = filePrefixRoot(root, dst);
       
  1284                     QFileInfo fi(fileInfo(wild));
       
  1285                     QString cmd =  QString(fi.isExecutable() ? "-$(INSTALL_PROGRAM)" : "-$(INSTALL_FILE)") + " " +
       
  1286                                    wild + " " + dst_file + "\n";
       
  1287                     target += cmd;
       
  1288                     if(!uninst.isEmpty())
       
  1289                         uninst.append("\n\t");
       
  1290                     uninst.append(rm_dir_contents + " " + filePrefixRoot(root, fileFixify(dst + filestr, FileFixifyAbsolute, false)));
       
  1291                 }
       
  1292                 for(int x = 0; x < files.count(); x++) {
       
  1293                     QString file = files[x];
       
  1294                     if(file == "." || file == "..") //blah
       
  1295                         continue;
       
  1296                     if(!uninst.isEmpty())
       
  1297                         uninst.append("\n\t");
       
  1298                     uninst.append(rm_dir_contents + " " + filePrefixRoot(root, fileFixify(dst + file, FileFixifyAbsolute, false)));
       
  1299                     QFileInfo fi(fileInfo(dirstr + file));
       
  1300                     if(!target.isEmpty())
       
  1301                         target += "\t";
       
  1302                     QString dst_file = filePrefixRoot(root, fileFixify(dst, FileFixifyAbsolute, false));
       
  1303                     if(fi.isDir() && project->isActiveConfig("copy_dir_files")) {
       
  1304                         if(!dst_file.endsWith(Option::dir_sep))
       
  1305                             dst_file += Option::dir_sep;
       
  1306                         dst_file += fi.fileName();
       
  1307                     }
       
  1308                     QString cmd = QString(fi.isDir() ? "-$(INSTALL_DIR)" : "-$(INSTALL_FILE)") + " " +
       
  1309                                   dirstr + file + " " + dst_file + "\n";
       
  1310                     target += cmd;
       
  1311                     if(!project->isActiveConfig("debug") && !project->isActiveConfig("nostrip") &&
       
  1312                        !fi.isDir() && fi.isExecutable() && !project->isEmpty("QMAKE_STRIP"))
       
  1313                         target += QString("\t-") + var("QMAKE_STRIP") + " " +
       
  1314                                   filePrefixRoot(root, fileFixify(dst + file, FileFixifyAbsolute, false)) +
       
  1315                                   "\n";
       
  1316                 }
       
  1317             }
       
  1318         }
       
  1319         //default?
       
  1320         if(do_default) {
       
  1321             target = defaultInstall((*it));
       
  1322             uninst = project->values((*it) + ".uninstall");
       
  1323         }
       
  1324 
       
  1325         if(!target.isEmpty() || project->values((*it) + ".CONFIG").indexOf("dummy_install") != -1) {
       
  1326             if(noBuild || project->values((*it) + ".CONFIG").indexOf("no_build") != -1)
       
  1327                 t << "install_" << (*it) << ":";
       
  1328             else if(project->isActiveConfig("build_all"))
       
  1329                 t << "install_" << (*it) << ": all";
       
  1330             else
       
  1331                 t << "install_" << (*it) << ": first";
       
  1332             const QStringList &deps = project->values((*it) + ".depends");
       
  1333             if(!deps.isEmpty()) {
       
  1334                 for(QStringList::ConstIterator dep_it = deps.begin(); dep_it != deps.end(); ++dep_it) {
       
  1335                     QString targ = var((*dep_it) + ".target");
       
  1336                     if(targ.isEmpty())
       
  1337                         targ = (*dep_it);
       
  1338                     t << " " << escapeDependencyPath(targ);
       
  1339                 }
       
  1340             }
       
  1341             if(project->isEmpty("QMAKE_NOFORCE"))
       
  1342                 t <<  " FORCE";
       
  1343             t << "\n\t";
       
  1344             const QStringList &dirs = project->values(pvar);
       
  1345             for(QStringList::ConstIterator pit = dirs.begin(); pit != dirs.end(); ++pit) {
       
  1346                 QString tmp_dst = fileFixify((*pit), FileFixifyAbsolute, false);
       
  1347                 if (!isWindowsShell() && !tmp_dst.endsWith(Option::dir_sep))
       
  1348                     tmp_dst += Option::dir_sep;
       
  1349                 t << mkdir_p_asstring(filePrefixRoot(root, tmp_dst)) << "\n\t";
       
  1350             }
       
  1351             t << target << endl << endl;
       
  1352             if(!uninst.isEmpty()) {
       
  1353                 t << "uninstall_" << (*it) << ": ";
       
  1354                 if(project->isEmpty("QMAKE_NOFORCE"))
       
  1355                     t <<  " FORCE";
       
  1356                 t << "\n\t"
       
  1357                   << uninst.join(" ") << "\n\t"
       
  1358                   << "-$(DEL_DIR) " << filePrefixRoot(root, dst) << " " << endl << endl;
       
  1359             }
       
  1360             t << endl;
       
  1361 
       
  1362             if(project->values((*it) + ".CONFIG").indexOf("no_default_install") == -1) {
       
  1363                 all_installs += QString("install_") + (*it) + " ";
       
  1364                 if(!uninst.isEmpty())
       
  1365                     all_uninstalls += "uninstall_" + (*it) + " ";
       
  1366             }
       
  1367         }   else {
       
  1368             debug_msg(1, "no definition for install %s: install target not created",(*it).toLatin1().constData());
       
  1369         }
       
  1370     }
       
  1371     t << "install: " << var("INSTALLDEPS") << " " << all_installs;
       
  1372     if(project->isEmpty("QMAKE_NOFORCE"))
       
  1373         t <<  " FORCE";
       
  1374     t << "\n\n";
       
  1375     t << "uninstall: " << all_uninstalls << " " << var("UNINSTALLDEPS");
       
  1376     if(project->isEmpty("QMAKE_NOFORCE"))
       
  1377         t <<  " FORCE";
       
  1378     t << "\n\n";
       
  1379 }
       
  1380 
       
  1381 QString
       
  1382 MakefileGenerator::var(const QString &var)
       
  1383 {
       
  1384     return val(project->values(var));
       
  1385 }
       
  1386 
       
  1387 QString
       
  1388 MakefileGenerator::val(const QStringList &varList)
       
  1389 {
       
  1390     return valGlue(varList, "", " ", "");
       
  1391 }
       
  1392 
       
  1393 QString
       
  1394 MakefileGenerator::varGlue(const QString &var, const QString &before, const QString &glue, const QString &after)
       
  1395 {
       
  1396     return valGlue(project->values(var), before, glue, after);
       
  1397 }
       
  1398 
       
  1399 QString
       
  1400 MakefileGenerator::valGlue(const QStringList &varList, const QString &before, const QString &glue, const QString &after)
       
  1401 {
       
  1402     QString ret;
       
  1403     for(QStringList::ConstIterator it = varList.begin(); it != varList.end(); ++it) {
       
  1404         if(!(*it).isEmpty()) {
       
  1405             if(!ret.isEmpty())
       
  1406                 ret += glue;
       
  1407             ret += (*it);
       
  1408         }
       
  1409     }
       
  1410     return ret.isEmpty() ? QString("") : before + ret + after;
       
  1411 }
       
  1412 
       
  1413 
       
  1414 QString
       
  1415 MakefileGenerator::varList(const QString &var)
       
  1416 {
       
  1417     return valList(project->values(var));
       
  1418 }
       
  1419 
       
  1420 QString
       
  1421 MakefileGenerator::valList(const QStringList &varList)
       
  1422 {
       
  1423     return valGlue(varList, "", " \\\n\t\t", "");
       
  1424 }
       
  1425 
       
  1426 QStringList
       
  1427 MakefileGenerator::createObjectList(const QStringList &sources)
       
  1428 {
       
  1429     QStringList ret;
       
  1430     QString objdir;
       
  1431     if(!project->values("OBJECTS_DIR").isEmpty())
       
  1432         objdir = project->first("OBJECTS_DIR");
       
  1433     for(QStringList::ConstIterator it = sources.begin(); it != sources.end(); ++it) {
       
  1434         QFileInfo fi(fileInfo(Option::fixPathToLocalOS((*it))));
       
  1435         QString dir;
       
  1436         if(objdir.isEmpty() && project->isActiveConfig("object_with_source")) {
       
  1437             QString fName = Option::fixPathToTargetOS((*it), false);
       
  1438             int dl = fName.lastIndexOf(Option::dir_sep);
       
  1439             if(dl != -1)
       
  1440                 dir = fName.left(dl + 1);
       
  1441         } else {
       
  1442             dir = objdir;
       
  1443         }
       
  1444         ret.append(dir + fi.completeBaseName() + Option::obj_ext);
       
  1445     }
       
  1446     return ret;
       
  1447 }
       
  1448 
       
  1449 void MakefileGenerator::generatePrint(const QString pathName)
       
  1450 {
       
  1451     QTextStream cout(stdout);
       
  1452     cout << "Generating " << pathName << "\n";
       
  1453 }
       
  1454 
       
  1455 ReplaceExtraCompilerCacheKey::ReplaceExtraCompilerCacheKey(const QString &v, const QStringList &i, const QStringList &o)
       
  1456 {
       
  1457     hash = 0;
       
  1458     pwd = qmake_getpwd();
       
  1459     var = v;
       
  1460     {
       
  1461         QStringList il = i;
       
  1462         il.sort();
       
  1463         in = il.join("::");
       
  1464     }
       
  1465     {
       
  1466         QStringList ol = o;
       
  1467         ol.sort();
       
  1468         out = ol.join("::");
       
  1469     }
       
  1470 }
       
  1471 
       
  1472 bool ReplaceExtraCompilerCacheKey::operator==(const ReplaceExtraCompilerCacheKey &f) const
       
  1473 {
       
  1474     return (hashCode() == f.hashCode() &&
       
  1475             f.in == in &&
       
  1476             f.out == out &&
       
  1477             f.var == var &&
       
  1478             f.pwd == pwd);
       
  1479 }
       
  1480 
       
  1481 
       
  1482 QString
       
  1483 MakefileGenerator::replaceExtraCompilerVariables(const QString &orig_var, const QStringList &in, const QStringList &out)
       
  1484 {
       
  1485     //lazy cache
       
  1486     ReplaceExtraCompilerCacheKey cacheKey(orig_var, in, out);
       
  1487     QString cacheVal = extraCompilerVariablesCache.value(cacheKey);
       
  1488     if(!cacheVal.isNull())
       
  1489         return cacheVal;
       
  1490 
       
  1491     //do the work
       
  1492     QString ret = orig_var;
       
  1493     QRegExp reg_var("\\$\\{.*\\}");
       
  1494     reg_var.setMinimal(true);
       
  1495     for(int rep = 0; (rep = reg_var.indexIn(ret, rep)) != -1; ) {
       
  1496         QStringList val;
       
  1497         const QString var = ret.mid(rep + 2, reg_var.matchedLength() - 3);
       
  1498         bool filePath = false;
       
  1499         if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_VAR_"))) {
       
  1500             const QString varname = var.mid(10);
       
  1501             val += project->values(varname);
       
  1502         }
       
  1503         if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_VAR_FIRST_"))) {
       
  1504             const QString varname = var.mid(16);
       
  1505             val += project->first(varname);
       
  1506         }
       
  1507 
       
  1508         if(val.isEmpty() && !in.isEmpty()) {
       
  1509             if(var.startsWith(QLatin1String("QMAKE_FUNC_FILE_IN_"))) {
       
  1510                 filePath = true;
       
  1511                 const QString funcname = var.mid(19);
       
  1512                 val += project->expand(funcname, QList<QStringList>() << in);
       
  1513             } else if(var == QLatin1String("QMAKE_FILE_BASE") || var == QLatin1String("QMAKE_FILE_IN_BASE")) {
       
  1514                 //filePath = true;
       
  1515                 for(int i = 0; i < in.size(); ++i) {
       
  1516                     QFileInfo fi(fileInfo(Option::fixPathToLocalOS(in.at(i))));
       
  1517                     QString base = fi.completeBaseName();
       
  1518                     if(base.isNull())
       
  1519                         base = fi.fileName();
       
  1520                     val += base;
       
  1521                 }
       
  1522             } else if(var == QLatin1String("QMAKE_FILE_PATH") || var == QLatin1String("QMAKE_FILE_IN_PATH")) {
       
  1523                 filePath = true;
       
  1524                 for(int i = 0; i < in.size(); ++i)
       
  1525                     val += fileInfo(Option::fixPathToLocalOS(in.at(i))).path();
       
  1526             } else if(var == QLatin1String("QMAKE_FILE_NAME") || var == QLatin1String("QMAKE_FILE_IN")) {
       
  1527                 filePath = true;
       
  1528                 for(int i = 0; i < in.size(); ++i)
       
  1529                     val += fileInfo(Option::fixPathToLocalOS(in.at(i))).filePath();
       
  1530 
       
  1531             }
       
  1532         }
       
  1533         if(val.isEmpty() && !out.isEmpty()) {
       
  1534             if(var.startsWith(QLatin1String("QMAKE_FUNC_FILE_OUT_"))) {
       
  1535                 filePath = true;
       
  1536                 const QString funcname = var.mid(20);
       
  1537                 val += project->expand(funcname, QList<QStringList>() << out);
       
  1538             } else if(var == QLatin1String("QMAKE_FILE_OUT")) {
       
  1539                 filePath = true;
       
  1540                 for(int i = 0; i < out.size(); ++i)
       
  1541                     val += fileInfo(Option::fixPathToLocalOS(out.at(i))).filePath();
       
  1542             } else if(var == QLatin1String("QMAKE_FILE_OUT_BASE")) {
       
  1543                 //filePath = true;
       
  1544                 for(int i = 0; i < out.size(); ++i) {
       
  1545                     QFileInfo fi(fileInfo(Option::fixPathToLocalOS(out.at(i))));
       
  1546                     QString base = fi.completeBaseName();
       
  1547                     if(base.isNull())
       
  1548                         base = fi.fileName();
       
  1549                     val += base;
       
  1550                 }
       
  1551             }
       
  1552         }
       
  1553         if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_FUNC_"))) {
       
  1554             const QString funcname = var.mid(11);
       
  1555             val += project->expand(funcname, QList<QStringList>() << in << out);
       
  1556         }
       
  1557 
       
  1558         if(!val.isEmpty()) {
       
  1559             QString fullVal;
       
  1560             if(filePath) {
       
  1561                 for(int i = 0; i < val.size(); ++i) {
       
  1562                     const QString file = Option::fixPathToTargetOS(unescapeFilePath(val.at(i)), false);
       
  1563                     if(!fullVal.isEmpty())
       
  1564                         fullVal += " ";
       
  1565                     fullVal += escapeFilePath(file);
       
  1566                 }
       
  1567             } else {
       
  1568                 fullVal = val.join(" ");
       
  1569             }
       
  1570             ret.replace(rep, reg_var.matchedLength(), fullVal);
       
  1571             rep += fullVal.length();
       
  1572         } else {
       
  1573             rep += reg_var.matchedLength();
       
  1574         }
       
  1575     }
       
  1576 
       
  1577     //cache the value
       
  1578     extraCompilerVariablesCache.insert(cacheKey, ret);
       
  1579     return ret;
       
  1580 }
       
  1581 
       
  1582 bool
       
  1583 MakefileGenerator::verifyExtraCompiler(const QString &comp, const QString &file_unfixed)
       
  1584 {
       
  1585     if(noIO())
       
  1586         return false;
       
  1587     const QString file = Option::fixPathToLocalOS(file_unfixed);
       
  1588 
       
  1589     if(project->values(comp + ".CONFIG").indexOf("moc_verify") != -1) {
       
  1590         if(!file.isNull()) {
       
  1591             QMakeSourceFileInfo::addSourceFile(file, QMakeSourceFileInfo::SEEK_MOCS);
       
  1592             if(!mocable(file)) {
       
  1593                 return false;
       
  1594             } else {
       
  1595                 project->values("MOCABLES").append(file);
       
  1596             }
       
  1597         }
       
  1598     } else if(project->values(comp + ".CONFIG").indexOf("function_verify") != -1) {
       
  1599         QString tmp_out = project->values(comp + ".output").first();
       
  1600         if(tmp_out.isEmpty())
       
  1601             return false;
       
  1602         QStringList verify_function = project->values(comp + ".verify_function");
       
  1603         if(verify_function.isEmpty())
       
  1604             return false;
       
  1605 
       
  1606         for(int i = 0; i < verify_function.size(); ++i) {
       
  1607             bool invert = false;
       
  1608             QString verify = verify_function.at(i);
       
  1609             if(verify.at(0) == QLatin1Char('!')) {
       
  1610                 invert = true;
       
  1611                 verify = verify.mid(1);
       
  1612             }
       
  1613 
       
  1614             if(project->values(comp + ".CONFIG").indexOf("combine") != -1) {
       
  1615                 bool pass = project->test(verify, QList<QStringList>() << QStringList(tmp_out) << QStringList(file));
       
  1616                 if(invert)
       
  1617                     pass = !pass;
       
  1618                 if(!pass)
       
  1619                     return false;
       
  1620             } else {
       
  1621                 QStringList &tmp = project->values(comp + ".input");
       
  1622                 for(QStringList::Iterator it = tmp.begin(); it != tmp.end(); ++it) {
       
  1623                     QStringList &inputs = project->values((*it));
       
  1624                     for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
       
  1625                         if((*input).isEmpty())
       
  1626                             continue;
       
  1627                         QString in = fileFixify(Option::fixPathToTargetOS((*input), false));
       
  1628                         if(in == file) {
       
  1629                             bool pass = project->test(verify,
       
  1630                                                       QList<QStringList>() << QStringList(replaceExtraCompilerVariables(tmp_out, (*input), QString())) <<
       
  1631                                                       QStringList(file));
       
  1632                             if(invert)
       
  1633                                 pass = !pass;
       
  1634                             if(!pass)
       
  1635                                 return false;
       
  1636                             break;
       
  1637                         }
       
  1638                     }
       
  1639                 }
       
  1640             }
       
  1641         }
       
  1642     } else if(project->values(comp + ".CONFIG").indexOf("verify") != -1) {
       
  1643         QString tmp_out = project->values(comp + ".output").first();
       
  1644         if(tmp_out.isEmpty())
       
  1645             return false;
       
  1646         QString tmp_cmd;
       
  1647         if(!project->isEmpty(comp + ".commands")) {
       
  1648             int argv0 = -1;
       
  1649             QStringList cmdline = project->values(comp + ".commands");
       
  1650             for(int i = 0; i < cmdline.count(); ++i) {
       
  1651                 if(!cmdline.at(i).contains('=')) {
       
  1652                     argv0 = i;
       
  1653                     break;
       
  1654                 }
       
  1655             }
       
  1656             if(argv0 != -1) {
       
  1657                 cmdline[argv0] = Option::fixPathToTargetOS(cmdline.at(argv0), false);
       
  1658                 tmp_cmd = cmdline.join(" ");
       
  1659             }
       
  1660         }
       
  1661 
       
  1662         if(project->values(comp + ".CONFIG").indexOf("combine") != -1) {
       
  1663             QString cmd = replaceExtraCompilerVariables(tmp_cmd, QString(), tmp_out);
       
  1664             if(system(cmd.toLatin1().constData()))
       
  1665                 return false;
       
  1666         } else {
       
  1667             QStringList &tmp = project->values(comp + ".input");
       
  1668             for(QStringList::Iterator it = tmp.begin(); it != tmp.end(); ++it) {
       
  1669                 QStringList &inputs = project->values((*it));
       
  1670                 for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
       
  1671                     if((*input).isEmpty())
       
  1672                         continue;
       
  1673                     QString in = fileFixify(Option::fixPathToTargetOS((*input), false));
       
  1674                     if(in == file) {
       
  1675                         QString out = replaceExtraCompilerVariables(tmp_out, (*input), QString());
       
  1676                         QString cmd = replaceExtraCompilerVariables(tmp_cmd, in, out);
       
  1677                         if(system(cmd.toLatin1().constData()))
       
  1678                             return false;
       
  1679                         break;
       
  1680                     }
       
  1681                 }
       
  1682             }
       
  1683         }
       
  1684     }
       
  1685     return true;
       
  1686 }
       
  1687 
       
  1688 void
       
  1689 MakefileGenerator::writeExtraTargets(QTextStream &t)
       
  1690 {
       
  1691     QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
       
  1692     for(QStringList::Iterator it = qut.begin(); it != qut.end(); ++it) {
       
  1693         QString targ = var((*it) + ".target"),
       
  1694                  cmd = var((*it) + ".commands"), deps;
       
  1695         if(targ.isEmpty())
       
  1696             targ = (*it);
       
  1697         QStringList &deplist = project->values((*it) + ".depends");
       
  1698         for(QStringList::Iterator dep_it = deplist.begin(); dep_it != deplist.end(); ++dep_it) {
       
  1699             QString dep = var((*dep_it) + ".target");
       
  1700             if(dep.isEmpty())
       
  1701                 dep = (*dep_it);
       
  1702             deps += " " + escapeDependencyPath(dep);
       
  1703         }
       
  1704         if(project->values((*it) + ".CONFIG").indexOf("fix_target") != -1)
       
  1705             targ = fileFixify(targ);
       
  1706         if(project->isEmpty("QMAKE_NOFORCE") &&
       
  1707            project->values((*it) + ".CONFIG").indexOf("phony") != -1)
       
  1708             deps += QString(" ") + "FORCE";
       
  1709         t << escapeDependencyPath(targ) << ":" << deps;
       
  1710         if(!cmd.isEmpty())
       
  1711             t << "\n\t" << cmd;
       
  1712         t << endl << endl;
       
  1713 
       
  1714 		project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(targ);
       
  1715 		project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(targ)) << deps.split(" ", QString::SkipEmptyParts);
       
  1716 		project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(targ)) << cmd;
       
  1717     }
       
  1718 }
       
  1719 
       
  1720 void
       
  1721 MakefileGenerator::writeExtraCompilerTargets(QTextStream &t)
       
  1722 {
       
  1723     QString clean_targets;
       
  1724     const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
       
  1725     for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
       
  1726         QString tmp_out = fileFixify(project->values((*it) + ".output").first(),
       
  1727                                      Option::output_dir, Option::output_dir);
       
  1728         QString tmp_cmd;
       
  1729         if(!project->isEmpty((*it) + ".commands")) {
       
  1730             QStringList cmdline = project->values((*it) + ".commands");
       
  1731             int argv0 = findExecutable(cmdline);
       
  1732             if(argv0 != -1) {
       
  1733                 cmdline[argv0] = escapeFilePath(Option::fixPathToTargetOS(cmdline.at(argv0), false));
       
  1734                 tmp_cmd = cmdline.join(" ");
       
  1735             }
       
  1736         }
       
  1737         QStringList tmp_dep = project->values((*it) + ".depends");
       
  1738         QString tmp_dep_cmd;
       
  1739         if(!project->isEmpty((*it) + ".depend_command")) {
       
  1740             int argv0 = -1;
       
  1741             QStringList cmdline = project->values((*it) + ".depend_command");
       
  1742             for(int i = 0; i < cmdline.count(); ++i) {
       
  1743                 if(!cmdline.at(i).contains('=')) {
       
  1744                     argv0 = i;
       
  1745                     break;
       
  1746                 }
       
  1747             }
       
  1748             if(argv0 != -1) {
       
  1749                 const QString c = Option::fixPathToLocalOS(cmdline.at(argv0), true);
       
  1750                 if(exists(c)) {
       
  1751                     cmdline[argv0] = escapeFilePath(Option::fixPathToLocalOS(cmdline.at(argv0), false));
       
  1752                     tmp_dep_cmd = cmdline.join(" ");
       
  1753                 } else {
       
  1754                     cmdline[argv0] = escapeFilePath(cmdline.at(argv0));
       
  1755                 }
       
  1756             }
       
  1757         }
       
  1758         QStringList &vars = project->values((*it) + ".variables");
       
  1759         if(tmp_out.isEmpty() || tmp_cmd.isEmpty())
       
  1760             continue;
       
  1761         QStringList tmp_inputs;
       
  1762         {
       
  1763             const QStringList &comp_inputs = project->values((*it) + ".input");
       
  1764             for(QStringList::ConstIterator it2 = comp_inputs.begin(); it2 != comp_inputs.end(); ++it2) {
       
  1765                 const QStringList &tmp = project->values((*it2));
       
  1766                 for(QStringList::ConstIterator input = tmp.begin(); input != tmp.end(); ++input) {
       
  1767                     QString in = Option::fixPathToTargetOS((*input), false);
       
  1768                     if(verifyExtraCompiler((*it), in))
       
  1769                         tmp_inputs.append((*input));
       
  1770                 }
       
  1771             }
       
  1772         }
       
  1773 
       
  1774         t << "compiler_" << (*it) << "_make_all:";
       
  1775         if(project->values((*it) + ".CONFIG").indexOf("combine") != -1) {
       
  1776             // compilers with a combined input only have one output
       
  1777             QString input = project->values((*it) + ".output").first();
       
  1778             t << " " << escapeDependencyPath(replaceExtraCompilerVariables(tmp_out, input, QString()));
       
  1779         } else {
       
  1780             for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
       
  1781                 QString in = Option::fixPathToTargetOS((*input), false);
       
  1782                 t << " " << escapeDependencyPath(replaceExtraCompilerVariables(tmp_out, (*input), QString()));
       
  1783             }
       
  1784         }
       
  1785         t << endl;
       
  1786 
       
  1787         if(project->values((*it) + ".CONFIG").indexOf("no_clean") == -1) {
       
  1788             QString tmp_clean = project->values((*it) + ".clean").join(" ");
       
  1789             QString tmp_clean_cmds = project->values((*it) + ".clean_commands").join(" ");
       
  1790             if(!tmp_inputs.isEmpty())
       
  1791                 clean_targets += QString("compiler_" + (*it) + "_clean ");
       
  1792             t << "compiler_" << (*it) << "_clean:";
       
  1793             bool wrote_clean_cmds = false, wrote_clean = false;
       
  1794             if(tmp_clean_cmds.isEmpty()) {
       
  1795                 wrote_clean_cmds = true;
       
  1796             } else if(tmp_clean_cmds.indexOf("${QMAKE_") == -1) {
       
  1797                 t << "\n\t" << tmp_clean_cmds;
       
  1798                 wrote_clean_cmds = true;
       
  1799             }
       
  1800             if(tmp_clean.isEmpty())
       
  1801                 tmp_clean = tmp_out;
       
  1802             if(tmp_clean.indexOf("${QMAKE_") == -1) {
       
  1803                 t << "\n\t" << "-$(DEL_FILE) " << tmp_clean;
       
  1804                 if (isForSymbian())
       
  1805                     t << " 2> NUL"; // Eliminate unnecessary warnings
       
  1806                 wrote_clean = true;
       
  1807             }
       
  1808             if(!wrote_clean_cmds || !wrote_clean) {
       
  1809                 QStringList cleans;
       
  1810                 const QString del_statement("-$(DEL_FILE)");
       
  1811                 if(!wrote_clean) {
       
  1812                     if(project->isActiveConfig("no_delete_multiple_files")) {
       
  1813                         for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input)
       
  1814                             cleans.append(" " + replaceExtraCompilerVariables(tmp_clean, (*input),
       
  1815                                                 replaceExtraCompilerVariables(tmp_out, (*input), QString())));
       
  1816                     } else {
       
  1817                         QString files, file;
       
  1818                         const int commandlineLimit = 2047; // NT limit, expanded
       
  1819                         for(int input = 0; input < tmp_inputs.size(); ++input) {
       
  1820                             file = " " + replaceExtraCompilerVariables(tmp_clean, tmp_inputs.at(input),
       
  1821                                            replaceExtraCompilerVariables(tmp_out, tmp_inputs.at(input), QString()));
       
  1822                             if(del_statement.length() + files.length() +
       
  1823                                qMax(fixEnvVariables(file).length(), file.length()) > commandlineLimit) {
       
  1824                                 cleans.append(files);
       
  1825                                 files.clear();
       
  1826                             }
       
  1827                             files += file;
       
  1828                         }
       
  1829                         if(!files.isEmpty())
       
  1830                             cleans.append(files);
       
  1831                     }
       
  1832                 }
       
  1833                 if(!cleans.isEmpty())
       
  1834                     if (isForSymbian())
       
  1835                         t << valGlue(cleans, "\n\t" + del_statement, " 2> NUL\n\t" + del_statement, " 2> NUL");
       
  1836                     else
       
  1837                         t << valGlue(cleans, "\n\t" + del_statement, "\n\t" + del_statement, "");
       
  1838                 if(!wrote_clean_cmds) {
       
  1839                     for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
       
  1840                         t << "\n\t" << replaceExtraCompilerVariables(tmp_clean_cmds, (*input),
       
  1841                                          replaceExtraCompilerVariables(tmp_out, (*input), QString()));
       
  1842                     }
       
  1843                 }
       
  1844             }
       
  1845             t << endl;
       
  1846         }
       
  1847         if(project->values((*it) + ".CONFIG").indexOf("combine") != -1) {
       
  1848             if(tmp_out.indexOf("${QMAKE_") != -1) {
       
  1849                 warn_msg(WarnLogic, "QMAKE_EXTRA_COMPILERS(%s) with combine has variable output.",
       
  1850                          (*it).toLatin1().constData());
       
  1851                 continue;
       
  1852             }
       
  1853             QStringList deps, inputs;
       
  1854             if(!tmp_dep.isEmpty())
       
  1855                 deps += fileFixify(tmp_dep, Option::output_dir, Option::output_dir);
       
  1856             for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
       
  1857                 deps += findDependencies((*input));
       
  1858                 inputs += Option::fixPathToTargetOS((*input), false);
       
  1859                 if(!tmp_dep_cmd.isEmpty() && doDepends()) {
       
  1860                     char buff[256];
       
  1861                     QString dep_cmd = replaceExtraCompilerVariables(tmp_dep_cmd, (*input),
       
  1862                                                                     tmp_out);
       
  1863                     dep_cmd = fixEnvVariables(dep_cmd);
       
  1864                     if(FILE *proc = QT_POPEN(dep_cmd.toLatin1().constData(), "r")) {
       
  1865                         QString indeps;
       
  1866                         while(!feof(proc)) {
       
  1867                             int read_in = (int)fread(buff, 1, 255, proc);
       
  1868                             if(!read_in)
       
  1869                                 break;
       
  1870                             indeps += QByteArray(buff, read_in);
       
  1871                         }
       
  1872                         QT_PCLOSE(proc);
       
  1873                         if(!indeps.isEmpty()) {
       
  1874                             QStringList dep_cmd_deps = indeps.replace('\n', ' ').simplified().split(' ');
       
  1875                             for(int i = 0; i < dep_cmd_deps.count(); ++i) {
       
  1876                                 QString &file = dep_cmd_deps[i];
       
  1877                                 if(!exists(file)) {
       
  1878                                     QString localFile;
       
  1879                                     QList<QMakeLocalFileName> depdirs = QMakeSourceFileInfo::dependencyPaths();
       
  1880                                     for(QList<QMakeLocalFileName>::Iterator it = depdirs.begin();
       
  1881                                         it != depdirs.end(); ++it) {
       
  1882                                         if(exists((*it).real() + Option::dir_sep + file)) {
       
  1883                                             localFile = (*it).local() + Option::dir_sep + file;
       
  1884                                             break;
       
  1885                                         }
       
  1886                                     }
       
  1887                                     file = localFile;
       
  1888                                 }
       
  1889                                 if(!file.isEmpty())
       
  1890                                     file = fileFixify(file);
       
  1891                             }
       
  1892                             deps += dep_cmd_deps;
       
  1893                         }
       
  1894                     }
       
  1895                 }
       
  1896             }
       
  1897             for(int i = 0; i < inputs.size(); ) {
       
  1898                 if(tmp_out == inputs.at(i))
       
  1899                     inputs.removeAt(i);
       
  1900                 else
       
  1901                     ++i;
       
  1902             }
       
  1903             for(int i = 0; i < deps.size(); ) {
       
  1904                 if(tmp_out == deps.at(i))
       
  1905                     deps.removeAt(i);
       
  1906                 else
       
  1907                     ++i;
       
  1908             }
       
  1909             if (inputs.isEmpty())
       
  1910                 continue;
       
  1911 
       
  1912             QString cmd;
       
  1913             if (isForSymbianSbsv2()) {
       
  1914                 // In sbsv2 the command inputs and outputs need to use absolute paths
       
  1915                 cmd = replaceExtraCompilerVariables(tmp_cmd,
       
  1916                     fileFixify(escapeFilePaths(inputs), FileFixifyAbsolute),
       
  1917                     fileFixify(QStringList(tmp_out), FileFixifyAbsolute));
       
  1918             } else {
       
  1919                 cmd = replaceExtraCompilerVariables(tmp_cmd, escapeFilePaths(inputs), QStringList(tmp_out));
       
  1920             }
       
  1921 
       
  1922             t << escapeDependencyPath(tmp_out) << ":";
       
  1923             project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(tmp_out);
       
  1924             // compiler.CONFIG+=explicit_dependencies means that ONLY compiler.depends gets to cause Makefile dependencies
       
  1925             if(project->values((*it) + ".CONFIG").indexOf("explicit_dependencies") != -1) {
       
  1926                 t << " " << valList(escapeDependencyPaths(fileFixify(tmp_dep, Option::output_dir, Option::output_dir)));
       
  1927                 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(tmp_out)) << tmp_dep;
       
  1928             } else {
       
  1929                 t << " " << valList(escapeDependencyPaths(inputs)) << " " << valList(escapeDependencyPaths(deps));
       
  1930                 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(tmp_out)) << inputs << deps;
       
  1931             }
       
  1932             t << "\n\t" << cmd << endl << endl;
       
  1933             project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(tmp_out)) << cmd;
       
  1934             continue;
       
  1935         }
       
  1936         for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
       
  1937             QString in = Option::fixPathToTargetOS((*input), false);
       
  1938             QStringList deps = findDependencies((*input));
       
  1939             deps += escapeDependencyPath(in);
       
  1940             QString out = replaceExtraCompilerVariables(tmp_out, (*input), QString());
       
  1941             if(!tmp_dep.isEmpty()) {
       
  1942                 QStringList pre_deps = fileFixify(tmp_dep, Option::output_dir, Option::output_dir);
       
  1943                 for(int i = 0; i < pre_deps.size(); ++i)
       
  1944                    deps += replaceExtraCompilerVariables(pre_deps.at(i), (*input), out);
       
  1945             }
       
  1946             QString cmd = replaceExtraCompilerVariables(tmp_cmd, (*input), out);
       
  1947             // NOTE: The var -> QMAKE_COMP_var replace feature is unsupported, do not use!
       
  1948             if (isForSymbianSbsv2()) {
       
  1949                 // In sbsv2 the command inputs and outputs need to use absolute paths
       
  1950                 cmd = replaceExtraCompilerVariables(tmp_cmd,
       
  1951                     fileFixify((*input), FileFixifyAbsolute),
       
  1952                     fileFixify(out, FileFixifyAbsolute));
       
  1953             } else {
       
  1954                 cmd = replaceExtraCompilerVariables(tmp_cmd, (*input), out);
       
  1955             }
       
  1956             for(QStringList::ConstIterator it3 = vars.constBegin(); it3 != vars.constEnd(); ++it3)
       
  1957                 cmd.replace("$(" + (*it3) + ")", "$(QMAKE_COMP_" + (*it3)+")");
       
  1958             if(!tmp_dep_cmd.isEmpty() && doDepends()) {
       
  1959                 char buff[256];
       
  1960                 QString dep_cmd = replaceExtraCompilerVariables(tmp_dep_cmd, (*input), out);
       
  1961                 dep_cmd = fixEnvVariables(dep_cmd);
       
  1962                 if(FILE *proc = QT_POPEN(dep_cmd.toLatin1().constData(), "r")) {
       
  1963                     QString indeps;
       
  1964                     while(!feof(proc)) {
       
  1965                         int read_in = (int)fread(buff, 1, 255, proc);
       
  1966                         if(!read_in)
       
  1967                             break;
       
  1968                         indeps += QByteArray(buff, read_in);
       
  1969                     }
       
  1970                     QT_PCLOSE(proc);
       
  1971                     if(!indeps.isEmpty()) {
       
  1972                         QStringList dep_cmd_deps = indeps.replace('\n', ' ').simplified().split(' ');
       
  1973                         for(int i = 0; i < dep_cmd_deps.count(); ++i) {
       
  1974                             QString &file = dep_cmd_deps[i];
       
  1975                             if(!exists(file)) {
       
  1976                                 QString localFile;
       
  1977                                 QList<QMakeLocalFileName> depdirs = QMakeSourceFileInfo::dependencyPaths();
       
  1978                                 for(QList<QMakeLocalFileName>::Iterator it = depdirs.begin();
       
  1979                                     it != depdirs.end(); ++it) {
       
  1980                                     if(exists((*it).real() + Option::dir_sep + file)) {
       
  1981                                         localFile = (*it).local() + Option::dir_sep + file;
       
  1982                                         break;
       
  1983                                     }
       
  1984                                 }
       
  1985                                 file = localFile;
       
  1986                             }
       
  1987                             if(!file.isEmpty())
       
  1988                                 file = fileFixify(file);
       
  1989                         }
       
  1990                         deps += dep_cmd_deps;
       
  1991                     }
       
  1992                 }
       
  1993                 //use the depend system to find includes of these included files
       
  1994                 QStringList inc_deps;
       
  1995                 for(int i = 0; i < deps.size(); ++i) {
       
  1996                     const QString dep = deps.at(i);
       
  1997                     if(QFile::exists(dep)) {
       
  1998                         SourceFileType type = TYPE_UNKNOWN;
       
  1999                         if(type == TYPE_UNKNOWN) {
       
  2000                             for(QStringList::Iterator cit = Option::c_ext.begin();
       
  2001                                 cit != Option::c_ext.end(); ++cit) {
       
  2002                                 if(dep.endsWith((*cit))) {
       
  2003                                    type = TYPE_C;
       
  2004                                    break;
       
  2005                                 }
       
  2006                             }
       
  2007                         }
       
  2008                         if(type == TYPE_UNKNOWN) {
       
  2009                             for(QStringList::Iterator cppit = Option::cpp_ext.begin();
       
  2010                                 cppit != Option::cpp_ext.end(); ++cppit) {
       
  2011                                 if(dep.endsWith((*cppit))) {
       
  2012                                     type = TYPE_C;
       
  2013                                     break;
       
  2014                                 }
       
  2015                             }
       
  2016                         }
       
  2017                         if(type == TYPE_UNKNOWN) {
       
  2018                             for(QStringList::Iterator hit = Option::h_ext.begin();
       
  2019                                 type == TYPE_UNKNOWN && hit != Option::h_ext.end(); ++hit) {
       
  2020                                 if(dep.endsWith((*hit))) {
       
  2021                                     type = TYPE_C;
       
  2022                                     break;
       
  2023                                 }
       
  2024                             }
       
  2025                         }
       
  2026                         if(type != TYPE_UNKNOWN) {
       
  2027                             if(!QMakeSourceFileInfo::containsSourceFile(dep, type))
       
  2028                                 QMakeSourceFileInfo::addSourceFile(dep, type);
       
  2029                             inc_deps += QMakeSourceFileInfo::dependencies(dep);
       
  2030                         }
       
  2031                     }
       
  2032                 }
       
  2033                 deps += inc_deps;
       
  2034             }
       
  2035             for(int i = 0; i < deps.size(); ) {
       
  2036                 QString &dep = deps[i];
       
  2037                 dep = Option::fixPathToTargetOS(unescapeFilePath(dep), false);
       
  2038                 if(out == dep)
       
  2039                     deps.removeAt(i);
       
  2040                 else
       
  2041                     ++i;
       
  2042             }
       
  2043             t << escapeDependencyPath(out) << ": " << valList(escapeDependencyPaths(deps)) << "\n\t"
       
  2044               << cmd << endl << endl;
       
  2045             project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(out);
       
  2046             project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(out)) << deps;
       
  2047             project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(out)) << cmd;
       
  2048         }
       
  2049     }
       
  2050     t << "compiler_clean: " << clean_targets << endl << endl;
       
  2051 }
       
  2052 
       
  2053 void
       
  2054 MakefileGenerator::writeExtraCompilerVariables(QTextStream &t)
       
  2055 {
       
  2056     bool first = true;
       
  2057     const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
       
  2058     for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
       
  2059         const QStringList &vars = project->values((*it) + ".variables");
       
  2060         for(QStringList::ConstIterator varit = vars.begin(); varit != vars.end(); ++varit) {
       
  2061             if(first) {
       
  2062                 t << "\n####### Custom Compiler Variables" << endl;
       
  2063                 first = false;
       
  2064             }
       
  2065             t << "QMAKE_COMP_" << (*varit) << " = "
       
  2066               << valList(project->values((*varit))) << endl;
       
  2067         }
       
  2068     }
       
  2069     if(!first)
       
  2070         t << endl;
       
  2071 }
       
  2072 
       
  2073 void
       
  2074 MakefileGenerator::writeExtraVariables(QTextStream &t)
       
  2075 {
       
  2076     bool first = true;
       
  2077     QMap<QString, QStringList> &vars = project->variables();
       
  2078     QStringList &exports = project->values("QMAKE_EXTRA_VARIABLES");
       
  2079     for(QMap<QString, QStringList>::Iterator it = vars.begin(); it != vars.end(); ++it) {
       
  2080         for(QStringList::Iterator exp_it = exports.begin(); exp_it != exports.end(); ++exp_it) {
       
  2081             QRegExp rx((*exp_it), Qt::CaseInsensitive, QRegExp::Wildcard);
       
  2082             if(rx.exactMatch(it.key())) {
       
  2083                 if(first) {
       
  2084                     t << "\n####### Custom Variables" << endl;
       
  2085                     first = false;
       
  2086                 }
       
  2087                 t << "EXPORT_" << it.key() << " = " << it.value().join(" ") << endl;
       
  2088             }
       
  2089         }
       
  2090     }
       
  2091     if(!first)
       
  2092         t << endl;
       
  2093 }
       
  2094 
       
  2095 bool
       
  2096 MakefileGenerator::writeStubMakefile(QTextStream &t)
       
  2097 {
       
  2098     t << "QMAKE    = "        << (project->isEmpty("QMAKE_QMAKE") ? QString("qmake") : var("QMAKE_QMAKE")) << endl;
       
  2099     QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
       
  2100     for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it)
       
  2101         t << *it << " ";
       
  2102     //const QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
       
  2103     t << "first all clean install distclean uninstall: " << "qmake" << endl
       
  2104       << "qmake_all:" << endl;
       
  2105     writeMakeQmake(t);
       
  2106     if(project->isEmpty("QMAKE_NOFORCE"))
       
  2107         t << "FORCE:" << endl << endl;
       
  2108     return true;
       
  2109 }
       
  2110 
       
  2111 bool
       
  2112 MakefileGenerator::writeMakefile(QTextStream &t)
       
  2113 {
       
  2114     t << "####### Compile" << endl << endl;
       
  2115     writeObj(t, "SOURCES");
       
  2116     writeObj(t, "GENERATED_SOURCES");
       
  2117 
       
  2118     t << "####### Install" << endl << endl;
       
  2119     writeInstalls(t, "INSTALLS");
       
  2120 
       
  2121     if(project->isEmpty("QMAKE_NOFORCE"))
       
  2122         t << "FORCE:" << endl << endl;
       
  2123     return true;
       
  2124 }
       
  2125 
       
  2126 QString MakefileGenerator::buildArgs(const QString &outdir)
       
  2127 {
       
  2128     QString ret;
       
  2129     //special variables
       
  2130     if(!project->isEmpty("QMAKE_ABSOLUTE_SOURCE_PATH"))
       
  2131         ret += " QMAKE_ABSOLUTE_SOURCE_PATH=" + escapeFilePath(project->first("QMAKE_ABSOLUTE_SOURCE_PATH"));
       
  2132 
       
  2133     //warnings
       
  2134     else if(Option::warn_level == WarnNone)
       
  2135         ret += " -Wnone";
       
  2136     else if(Option::warn_level == WarnAll)
       
  2137         ret += " -Wall";
       
  2138     else if(Option::warn_level & WarnParser)
       
  2139         ret += " -Wparser";
       
  2140     //other options
       
  2141     if(!Option::user_template.isEmpty())
       
  2142         ret += " -t " + Option::user_template;
       
  2143     if(!Option::user_template_prefix.isEmpty())
       
  2144         ret += " -tp " + Option::user_template_prefix;
       
  2145     if(!Option::mkfile::do_cache)
       
  2146         ret += " -nocache";
       
  2147     if(!Option::mkfile::do_deps)
       
  2148         ret += " -nodepend";
       
  2149     if(!Option::mkfile::do_dep_heuristics)
       
  2150         ret += " -nodependheuristics";
       
  2151     if(!Option::mkfile::qmakespec_commandline.isEmpty())
       
  2152         ret += " -spec " + specdir(outdir);
       
  2153     if(Option::target_mode == Option::TARG_MAC9_MODE)
       
  2154         ret += " -mac9";
       
  2155     else if(Option::target_mode == Option::TARG_MACX_MODE)
       
  2156         ret += " -macx";
       
  2157     else if(Option::target_mode == Option::TARG_UNIX_MODE)
       
  2158         ret += " -unix";
       
  2159     else if(Option::target_mode == Option::TARG_WIN_MODE)
       
  2160         ret += " -win32";
       
  2161 
       
  2162     //configs
       
  2163     for(QStringList::Iterator it = Option::user_configs.begin();
       
  2164         it != Option::user_configs.end(); ++it)
       
  2165         ret += " -config " + (*it);
       
  2166     //arguments
       
  2167     for(QStringList::Iterator it = Option::before_user_vars.begin();
       
  2168         it != Option::before_user_vars.end(); ++it) {
       
  2169         if((*it).left(qstrlen("QMAKE_ABSOLUTE_SOURCE_PATH")) != "QMAKE_ABSOLUTE_SOURCE_PATH")
       
  2170             ret += " " + escapeFilePath((*it));
       
  2171     }
       
  2172     if(Option::after_user_vars.count()) {
       
  2173         ret += " -after ";
       
  2174         for(QStringList::Iterator it = Option::after_user_vars.begin();
       
  2175             it != Option::after_user_vars.end(); ++it) {
       
  2176             if((*it).left(qstrlen("QMAKE_ABSOLUTE_SOURCE_PATH")) != "QMAKE_ABSOLUTE_SOURCE_PATH")
       
  2177                 ret += " " + escapeFilePath((*it));
       
  2178         }
       
  2179     }
       
  2180     return ret;
       
  2181 }
       
  2182 
       
  2183 //could get stored argv, but then it would have more options than are
       
  2184 //probably necesary this will try to guess the bare minimum..
       
  2185 QString MakefileGenerator::build_args(const QString &outdir)
       
  2186 {
       
  2187     QString ret = "$(QMAKE)";
       
  2188 
       
  2189     // general options and arguments
       
  2190     ret += buildArgs(outdir);
       
  2191 
       
  2192     //output
       
  2193     QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
       
  2194     if(!ofile.isEmpty() && ofile != project->first("QMAKE_MAKEFILE"))
       
  2195         ret += " -o " + escapeFilePath(ofile);
       
  2196 
       
  2197     //inputs
       
  2198     ret += " " + escapeFilePath(fileFixify(project->projectFile(), outdir));
       
  2199 
       
  2200     return ret;
       
  2201 }
       
  2202 
       
  2203 void
       
  2204 MakefileGenerator::writeHeader(QTextStream &t)
       
  2205 {
       
  2206     t << "#############################################################################" << endl;
       
  2207     t << "# Makefile for building: " << escapeFilePath(var("TARGET")) << endl;
       
  2208     t << "# Generated by qmake (" << qmake_version() << ") (Qt " << QT_VERSION_STR << ") on: ";
       
  2209     t << QDateTime::currentDateTime().toString() << endl;
       
  2210     t << "# Project:  " << fileFixify(project->projectFile()) << endl;
       
  2211     t << "# Template: " << var("TEMPLATE") << endl;
       
  2212     if(!project->isActiveConfig("build_pass"))
       
  2213         t << "# Command: " << build_args().replace("$(QMAKE)",
       
  2214                       (project->isEmpty("QMAKE_QMAKE") ? QString("qmake") : var("QMAKE_QMAKE"))) << endl;
       
  2215     t << "#############################################################################" << endl;
       
  2216     t << endl;
       
  2217 }
       
  2218 
       
  2219 QList<MakefileGenerator::SubTarget*>
       
  2220 MakefileGenerator::findSubDirsSubTargets() const
       
  2221 {
       
  2222     QList<SubTarget*> targets;
       
  2223     {
       
  2224         const QStringList subdirs = project->values("SUBDIRS");
       
  2225         for(int subdir = 0; subdir < subdirs.size(); ++subdir) {
       
  2226             QString fixedSubdir = subdirs[subdir];
       
  2227             fixedSubdir = fixedSubdir.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
       
  2228 
       
  2229             SubTarget *st = new SubTarget;
       
  2230             st->name = subdirs[subdir];
       
  2231             targets.append(st);
       
  2232 
       
  2233             bool fromFile = false;
       
  2234             QString file = subdirs[subdir];
       
  2235             if(!project->isEmpty(fixedSubdir + ".file")) {
       
  2236                 if(!project->isEmpty(fixedSubdir + ".subdir"))
       
  2237                     warn_msg(WarnLogic, "Cannot assign both file and subdir for subdir %s",
       
  2238                              subdirs[subdir].toLatin1().constData());
       
  2239                 file = project->first(fixedSubdir + ".file");
       
  2240                 fromFile = true;
       
  2241             } else if(!project->isEmpty(fixedSubdir + ".subdir")) {
       
  2242                 file = project->first(fixedSubdir + ".subdir");
       
  2243                 fromFile = false;
       
  2244             } else {
       
  2245                 fromFile = file.endsWith(Option::pro_ext);
       
  2246             }
       
  2247             file = Option::fixPathToTargetOS(file);
       
  2248 
       
  2249             if(fromFile) {
       
  2250                 int slsh = file.lastIndexOf(Option::dir_sep);
       
  2251                 if(slsh != -1) {
       
  2252                     st->in_directory = file.left(slsh+1);
       
  2253                     st->profile = file.mid(slsh+1);
       
  2254                 } else {
       
  2255                     st->profile = file;
       
  2256                 }
       
  2257             } else {
       
  2258                 if(!file.isEmpty() && !project->isActiveConfig("subdir_first_pro"))
       
  2259                     st->profile = file.section(Option::dir_sep, -1) + Option::pro_ext;
       
  2260                 st->in_directory = file;
       
  2261             }
       
  2262             while(st->in_directory.endsWith(Option::dir_sep))
       
  2263                 st->in_directory.chop(1);
       
  2264             if(fileInfo(st->in_directory).isRelative())
       
  2265                 st->out_directory = st->in_directory;
       
  2266             else
       
  2267                 st->out_directory = fileFixify(st->in_directory, qmake_getpwd(), Option::output_dir);
       
  2268             if(!project->isEmpty(fixedSubdir + ".makefile")) {
       
  2269                 st->makefile = project->first(fixedSubdir + ".makefile");
       
  2270             } else {
       
  2271                 st->makefile = "$(MAKEFILE)";
       
  2272                 if(!st->profile.isEmpty()) {
       
  2273                     QString basename = st->in_directory;
       
  2274                     int new_slsh = basename.lastIndexOf(Option::dir_sep);
       
  2275                     if(new_slsh != -1)
       
  2276                         basename = basename.mid(new_slsh+1);
       
  2277                     if(st->profile != basename + Option::pro_ext)
       
  2278                         st->makefile += "." + st->profile.left(st->profile.length() - Option::pro_ext.length());
       
  2279                 }
       
  2280             }
       
  2281             if(!project->isEmpty(fixedSubdir + ".depends")) {
       
  2282                 const QStringList depends = project->values(fixedSubdir + ".depends");
       
  2283                 for(int depend = 0; depend < depends.size(); ++depend) {
       
  2284                     bool found = false;
       
  2285                     for(int subDep = 0; subDep < subdirs.size(); ++subDep) {
       
  2286                         if(subdirs[subDep] == depends.at(depend)) {
       
  2287                             QString fixedSubDep = subdirs[subDep];
       
  2288                             fixedSubDep = fixedSubDep.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
       
  2289                             if(!project->isEmpty(fixedSubDep + ".target")) {
       
  2290                                 st->depends += project->first(fixedSubDep + ".target");
       
  2291                             } else {
       
  2292                                 QString d = Option::fixPathToLocalOS(subdirs[subDep]);
       
  2293                                 if(!project->isEmpty(fixedSubDep + ".file"))
       
  2294                                     d = project->first(fixedSubDep + ".file");
       
  2295                                 else if(!project->isEmpty(fixedSubDep + ".subdir"))
       
  2296                                     d = project->first(fixedSubDep + ".subdir");
       
  2297                                 st->depends += "sub-" + d.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
       
  2298                             }
       
  2299                             found = true;
       
  2300                             break;
       
  2301                         }
       
  2302                     }
       
  2303                     if(!found) {
       
  2304                         QString depend_str = depends.at(depend);
       
  2305                         st->depends += depend_str.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
       
  2306                     }
       
  2307                 }
       
  2308             }
       
  2309             if(!project->isEmpty(fixedSubdir + ".target")) {
       
  2310                 st->target = project->first(fixedSubdir + ".target");
       
  2311             } else {
       
  2312                 st->target = "sub-" + file;
       
  2313         st->target = st->target.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
       
  2314             }
       
  2315         }
       
  2316     }
       
  2317     return targets;
       
  2318 }
       
  2319 
       
  2320 void
       
  2321 MakefileGenerator::writeSubDirs(QTextStream &t)
       
  2322 {
       
  2323     QList<SubTarget*> targets = findSubDirsSubTargets();
       
  2324     t << "first: make_default" << endl;
       
  2325     int flags = SubTargetInstalls;
       
  2326     if(project->isActiveConfig("ordered"))
       
  2327         flags |= SubTargetOrdered;
       
  2328     writeSubTargets(t, targets, flags);
       
  2329     qDeleteAll(targets);
       
  2330 }
       
  2331 
       
  2332 void
       
  2333 MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubTarget*> targets, int flags)
       
  2334 {
       
  2335     // blasted includes
       
  2336     QStringList &qeui = project->values("QMAKE_EXTRA_INCLUDES");
       
  2337     for(QStringList::Iterator qeui_it = qeui.begin(); qeui_it != qeui.end(); ++qeui_it)
       
  2338         t << "include " << (*qeui_it) << endl;
       
  2339 
       
  2340     if (!(flags & SubTargetSkipDefaultVariables)) {
       
  2341         QString ofile = Option::fixPathToTargetOS(Option::output.fileName());
       
  2342         if(ofile.lastIndexOf(Option::dir_sep) != -1)
       
  2343             ofile.remove(0, ofile.lastIndexOf(Option::dir_sep) +1);
       
  2344         t << "MAKEFILE      = " << ofile << endl;
       
  2345         /* Calling Option::fixPathToTargetOS() is necessary for MinGW/MSYS, which requires
       
  2346          * back-slashes to be turned into slashes. */
       
  2347         t << "QMAKE         = " << Option::fixPathToTargetOS(var("QMAKE_QMAKE")) << endl;
       
  2348         t << "DEL_FILE      = " << var("QMAKE_DEL_FILE") << endl;
       
  2349         t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl;
       
  2350         t << "MKDIR         = " << var("QMAKE_MKDIR") << endl;
       
  2351         t << "COPY          = " << var("QMAKE_COPY") << endl;
       
  2352         t << "COPY_FILE     = " << var("QMAKE_COPY_FILE") << endl;
       
  2353         t << "COPY_DIR      = " << var("QMAKE_COPY_DIR") << endl;
       
  2354         t << "INSTALL_FILE  = " << var("QMAKE_INSTALL_FILE") << endl;
       
  2355         t << "INSTALL_PROGRAM = " << var("QMAKE_INSTALL_PROGRAM") << endl;
       
  2356         t << "INSTALL_DIR   = " << var("QMAKE_INSTALL_DIR") << endl;
       
  2357         t << "DEL_FILE      = " << var("QMAKE_DEL_FILE") << endl;
       
  2358         t << "SYMLINK       = " << var("QMAKE_SYMBOLIC_LINK") << endl;
       
  2359         t << "DEL_DIR       = " << var("QMAKE_DEL_DIR") << endl;
       
  2360         t << "MOVE          = " << var("QMAKE_MOVE") << endl;
       
  2361         t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl;
       
  2362         t << "MKDIR         = " << var("QMAKE_MKDIR") << endl;
       
  2363         t << "SUBTARGETS    = ";     // subtargets are sub-directory
       
  2364         for(int target = 0; target < targets.size(); ++target)
       
  2365             t << " \\\n\t\t" << targets.at(target)->target;
       
  2366         t << endl << endl;
       
  2367     }
       
  2368     writeExtraVariables(t);
       
  2369 
       
  2370     QStringList targetSuffixes;
       
  2371     const QString abs_source_path = project->first("QMAKE_ABSOLUTE_SOURCE_PATH");
       
  2372     if (!(flags & SubTargetSkipDefaultTargets)) {
       
  2373         targetSuffixes << "make_default" << "make_first" << "all" << "clean" << "distclean"
       
  2374                        << QString((flags & SubTargetInstalls) ? "install_subtargets" : "install")
       
  2375                        << QString((flags & SubTargetInstalls) ? "uninstall_subtargets" : "uninstall");
       
  2376     }
       
  2377 
       
  2378     // generate target rules
       
  2379     for(int target = 0; target < targets.size(); ++target) {
       
  2380         SubTarget *subtarget = targets.at(target);
       
  2381         QString in_directory = subtarget->in_directory;
       
  2382         if(!in_directory.isEmpty() && !in_directory.endsWith(Option::dir_sep))
       
  2383             in_directory += Option::dir_sep;
       
  2384         QString out_directory = subtarget->out_directory;
       
  2385         if(!out_directory.isEmpty() && !out_directory.endsWith(Option::dir_sep))
       
  2386             out_directory += Option::dir_sep;
       
  2387         if(!abs_source_path.isEmpty() && out_directory.startsWith(abs_source_path))
       
  2388             out_directory = Option::output_dir + out_directory.mid(abs_source_path.length());
       
  2389 
       
  2390         QString mkfile = subtarget->makefile;
       
  2391         if(!in_directory.isEmpty())
       
  2392             mkfile.prepend(out_directory);
       
  2393 
       
  2394         QString in_directory_cdin, in_directory_cdout, out_directory_cdin, out_directory_cdout;
       
  2395 #define MAKE_CD_IN_AND_OUT(directory) \
       
  2396         if(!directory.isEmpty()) {               \
       
  2397             if(project->isActiveConfig("cd_change_global")) { \
       
  2398                 directory ## _cdin = "\n\tcd " + directory + "\n\t";        \
       
  2399                 QDir pwd(Option::output_dir); \
       
  2400                 QStringList in = directory.split(Option::dir_sep), out; \
       
  2401                 for(int i = 0; i < in.size(); i++) { \
       
  2402                     if(in.at(i) == "..") \
       
  2403                         out.prepend(fileInfo(pwd.path()).fileName()); \
       
  2404                     else if(in.at(i) != ".") \
       
  2405                         out.prepend(".."); \
       
  2406                     pwd.cd(in.at(i)); \
       
  2407                 } \
       
  2408                 directory ## _cdout = "\n\t@cd " + out.join(Option::dir_sep); \
       
  2409             } else { \
       
  2410                 directory ## _cdin = "\n\tcd " + directory + " && ";  \
       
  2411             } \
       
  2412         } else { \
       
  2413             directory ## _cdin = "\n\t"; \
       
  2414         }
       
  2415         MAKE_CD_IN_AND_OUT(in_directory);
       
  2416         MAKE_CD_IN_AND_OUT(out_directory);
       
  2417 
       
  2418         //qmake it
       
  2419         if(!subtarget->profile.isEmpty()) {
       
  2420             QString out = out_directory + subtarget->makefile,
       
  2421                      in = fileFixify(in_directory + subtarget->profile, in_directory);
       
  2422             if(in.startsWith(in_directory))
       
  2423                 in = in.mid(in_directory.length());
       
  2424             if(out.startsWith(in_directory))
       
  2425                 out = out.mid(in_directory.length());
       
  2426             t << mkfile << ": " << "\n\t";
       
  2427             if(!in_directory.isEmpty()) {
       
  2428                 t << mkdir_p_asstring(in_directory)
       
  2429                   << in_directory_cdin
       
  2430                   << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out
       
  2431                   << in_directory_cdout << endl;
       
  2432             } else {
       
  2433                 t << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out << endl;
       
  2434             }
       
  2435             t << subtarget->target << "-qmake_all: ";
       
  2436             if(project->isEmpty("QMAKE_NOFORCE"))
       
  2437                 t <<  " FORCE";
       
  2438             t << "\n\t";
       
  2439             if(!in_directory.isEmpty()) {
       
  2440                 t << mkdir_p_asstring(in_directory)
       
  2441                   << in_directory_cdin
       
  2442                   << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out
       
  2443                   << in_directory_cdout << endl;
       
  2444             } else {
       
  2445                 t << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out << endl;
       
  2446             }
       
  2447         }
       
  2448 
       
  2449         QString makefilein = " -f " + subtarget->makefile;
       
  2450 
       
  2451         { //actually compile
       
  2452             t << subtarget->target << ": " << mkfile;
       
  2453             if(!subtarget->depends.isEmpty())
       
  2454                 t << " " << valList(subtarget->depends);
       
  2455             if(project->isEmpty("QMAKE_NOFORCE"))
       
  2456                 t <<  " FORCE";
       
  2457             t << out_directory_cdin
       
  2458               << "$(MAKE)" << makefilein
       
  2459               << out_directory_cdout << endl;
       
  2460         }
       
  2461 
       
  2462         for(int suffix = 0; suffix < targetSuffixes.size(); ++suffix) {
       
  2463             QString s = targetSuffixes.at(suffix);
       
  2464             if(s == "install_subtargets")
       
  2465                 s = "install";
       
  2466             else if(s == "uninstall_subtargets")
       
  2467                 s = "uninstall";
       
  2468             else if(s == "make_first")
       
  2469                 s = "first";
       
  2470             else if(s == "make_default")
       
  2471                 s = QString();
       
  2472 
       
  2473             if(flags & SubTargetOrdered) {
       
  2474                 t << subtarget->target << "-" << targetSuffixes.at(suffix) << "-ordered: " << mkfile;
       
  2475                 if(target)
       
  2476                     t << " " << targets.at(target-1)->target << "-" << targetSuffixes.at(suffix) << "-ordered ";
       
  2477                 if(project->isEmpty("QMAKE_NOFORCE"))
       
  2478                     t <<  " FORCE";
       
  2479                 t << out_directory_cdin
       
  2480                   << "$(MAKE)" << makefilein << " " << s
       
  2481                   << out_directory_cdout << endl;
       
  2482             }
       
  2483             t << subtarget->target << "-" << targetSuffixes.at(suffix) << ": " << mkfile;
       
  2484             if(!subtarget->depends.isEmpty())
       
  2485                 t << " " << valGlue(subtarget->depends, QString(), "-" + targetSuffixes.at(suffix) + " ",
       
  2486                                     "-"+targetSuffixes.at(suffix));
       
  2487             if(project->isEmpty("QMAKE_NOFORCE"))
       
  2488                 t <<  " FORCE";
       
  2489             t << out_directory_cdin
       
  2490               << "$(MAKE)" << makefilein << " " << s
       
  2491               << out_directory_cdout << endl;
       
  2492         }
       
  2493     }
       
  2494     t << endl;
       
  2495 
       
  2496     if (!(flags & SubTargetSkipDefaultTargets)) {
       
  2497         if(project->values("QMAKE_INTERNAL_QMAKE_DEPS").indexOf("qmake_all") == -1)
       
  2498             project->values("QMAKE_INTERNAL_QMAKE_DEPS").append("qmake_all");
       
  2499 
       
  2500         writeMakeQmake(t);
       
  2501 
       
  2502         t << "qmake_all:";
       
  2503         if(!targets.isEmpty()) {
       
  2504             for(QList<SubTarget*>::Iterator it = targets.begin(); it != targets.end(); ++it) {
       
  2505                 if(!(*it)->profile.isEmpty())
       
  2506                     t << " " << (*it)->target << "-" << "qmake_all";
       
  2507             }
       
  2508         }
       
  2509         if(project->isEmpty("QMAKE_NOFORCE"))
       
  2510             t <<  " FORCE";
       
  2511         if(project->isActiveConfig("no_empty_targets"))
       
  2512             t << "\n\t" << "@cd .";
       
  2513         t << endl << endl;
       
  2514     }
       
  2515 
       
  2516     for(int s = 0; s < targetSuffixes.size(); ++s) {
       
  2517         QString suffix = targetSuffixes.at(s);
       
  2518         if(!(flags & SubTargetInstalls) && suffix.endsWith("install"))
       
  2519             continue;
       
  2520 
       
  2521         t << suffix << ":";
       
  2522         for(int target = 0; target < targets.size(); ++target) {
       
  2523             SubTarget *subTarget = targets.at(target);
       
  2524             if((suffix == "make_first" || suffix == "make_default")
       
  2525                 && project->values(subTarget->name + ".CONFIG").indexOf("no_default_target") != -1) {
       
  2526                 continue;
       
  2527             }
       
  2528             QString targetRule = subTarget->target + "-" + suffix;
       
  2529             if(flags & SubTargetOrdered)
       
  2530                 targetRule += "-ordered";
       
  2531             t << " " << targetRule;
       
  2532         }
       
  2533         if(suffix == "all" || suffix == "make_first")
       
  2534             t << varGlue("ALL_DEPS"," "," ","");
       
  2535         if(suffix == "clean")
       
  2536             t << varGlue("CLEAN_DEPS"," "," ","");
       
  2537         if(project->isEmpty("QMAKE_NOFORCE"))
       
  2538             t <<  " FORCE";
       
  2539         t << endl;
       
  2540         if(suffix == "clean") {
       
  2541             t << varGlue("QMAKE_CLEAN","\t-$(DEL_FILE) ","\n\t-$(DEL_FILE) ", "\n");
       
  2542         } else if(suffix == "distclean") {
       
  2543             QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
       
  2544             if(!ofile.isEmpty())
       
  2545                 t << "\t-$(DEL_FILE) " << ofile << endl;
       
  2546         } else if(project->isActiveConfig("no_empty_targets")) {
       
  2547             t << "\t" << "@cd ." << endl;
       
  2548         }
       
  2549     }
       
  2550 
       
  2551     // user defined targets
       
  2552     QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
       
  2553     for(QStringList::Iterator qut_it = qut.begin(); qut_it != qut.end(); ++qut_it) {
       
  2554         QString targ = var((*qut_it) + ".target"),
       
  2555                  cmd = var((*qut_it) + ".commands"), deps;
       
  2556         if(targ.isEmpty())
       
  2557             targ = (*qut_it);
       
  2558         t << endl;
       
  2559 
       
  2560         QStringList &deplist = project->values((*qut_it) + ".depends");
       
  2561         for(QStringList::Iterator dep_it = deplist.begin(); dep_it != deplist.end(); ++dep_it) {
       
  2562             QString dep = var((*dep_it) + ".target");
       
  2563             if(dep.isEmpty())
       
  2564                 dep = Option::fixPathToTargetOS(*dep_it, false);
       
  2565             deps += " " + dep;
       
  2566         }
       
  2567         if(project->values((*qut_it) + ".CONFIG").indexOf("recursive") != -1) {
       
  2568             QSet<QString> recurse;
       
  2569             if(project->isSet((*qut_it) + ".recurse")) {
       
  2570                 recurse = project->values((*qut_it) + ".recurse").toSet();
       
  2571             } else {
       
  2572                 for(int target = 0; target < targets.size(); ++target)
       
  2573                     recurse.insert(targets.at(target)->name);
       
  2574             }
       
  2575             for(int target = 0; target < targets.size(); ++target) {
       
  2576                 SubTarget *subtarget = targets.at(target);
       
  2577                 QString in_directory = subtarget->in_directory;
       
  2578                 if(!in_directory.isEmpty() && !in_directory.endsWith(Option::dir_sep))
       
  2579                     in_directory += Option::dir_sep;
       
  2580                 QString out_directory = subtarget->out_directory;
       
  2581                 if(!out_directory.isEmpty() && !out_directory.endsWith(Option::dir_sep))
       
  2582                     out_directory += Option::dir_sep;
       
  2583                 if(!abs_source_path.isEmpty() && out_directory.startsWith(abs_source_path))
       
  2584                     out_directory = Option::output_dir + out_directory.mid(abs_source_path.length());
       
  2585 
       
  2586                 if(!recurse.contains(subtarget->name))
       
  2587                     continue;
       
  2588                 QString mkfile = subtarget->makefile;
       
  2589                 if(!in_directory.isEmpty()) {
       
  2590                     if(!out_directory.endsWith(Option::dir_sep))
       
  2591                         mkfile.prepend(out_directory + Option::dir_sep);
       
  2592                     else
       
  2593                         mkfile.prepend(out_directory);
       
  2594                 }
       
  2595                 QString out_directory_cdin, out_directory_cdout;
       
  2596                 MAKE_CD_IN_AND_OUT(out_directory);
       
  2597 
       
  2598                 //don't need the makefile arg if it isn't changed
       
  2599                 QString makefilein;
       
  2600                 if(subtarget->makefile != "$(MAKEFILE)")
       
  2601                     makefilein = " -f " + subtarget->makefile;
       
  2602 
       
  2603                 //write the rule/depends
       
  2604                 if(flags & SubTargetOrdered) {
       
  2605                     const QString dep = subtarget->target + "-" + (*qut_it) + "_ordered";
       
  2606                     t << dep << ": " << mkfile;
       
  2607                     if(target)
       
  2608                         t << " " << targets.at(target-1)->target << "-" << (*qut_it) << "_ordered ";
       
  2609                     deps += " " + dep;
       
  2610                 } else {
       
  2611                     const QString dep = subtarget->target + "-" + (*qut_it);
       
  2612                     t << dep << ": " << mkfile;
       
  2613                     if(!subtarget->depends.isEmpty())
       
  2614                         t << " " << valGlue(subtarget->depends, QString(), "-" + (*qut_it) + " ", "-" + (*qut_it));
       
  2615                     deps += " " + dep;
       
  2616                 }
       
  2617 
       
  2618                 QString sub_targ = targ;
       
  2619                 if(project->isSet((*qut_it) + ".recurse_target"))
       
  2620                     sub_targ = project->first((*qut_it) + ".recurse_target");
       
  2621 
       
  2622                 //write the commands
       
  2623                 if(!out_directory.isEmpty()) {
       
  2624                     t << out_directory_cdin
       
  2625                       << "$(MAKE)" << makefilein << " " << sub_targ
       
  2626                       << out_directory_cdout << endl;
       
  2627                 } else {
       
  2628                     t << "\n\t"
       
  2629                       << "$(MAKE)" << makefilein << " " << sub_targ << endl;
       
  2630                 }
       
  2631             }
       
  2632         }
       
  2633         if(project->isEmpty("QMAKE_NOFORCE") &&
       
  2634            project->values((*qut_it) + ".CONFIG").indexOf("phony") != -1)
       
  2635             deps += " FORCE";
       
  2636         t << targ << ":" << deps << "\n";
       
  2637         if(!cmd.isEmpty())
       
  2638             t << "\t" << cmd << endl;
       
  2639     }
       
  2640 
       
  2641     if(flags & SubTargetInstalls) {
       
  2642         project->values("INSTALLDEPS")   += "install_subtargets";
       
  2643         project->values("UNINSTALLDEPS") += "uninstall_subtargets";
       
  2644         writeInstalls(t, "INSTALLS", true);
       
  2645     }
       
  2646 
       
  2647     if(project->isEmpty("QMAKE_NOFORCE"))
       
  2648         t << "FORCE:" << endl << endl;
       
  2649 }
       
  2650 
       
  2651 void
       
  2652 MakefileGenerator::writeMakeQmake(QTextStream &t)
       
  2653 {
       
  2654     QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
       
  2655     if(project->isEmpty("QMAKE_FAILED_REQUIREMENTS") && !project->isEmpty("QMAKE_INTERNAL_PRL_FILE")) {
       
  2656         QStringList files = fileFixify(Option::mkfile::project_files);
       
  2657         t << escapeDependencyPath(project->first("QMAKE_INTERNAL_PRL_FILE")) << ": " << "\n\t"
       
  2658           << "@$(QMAKE) -prl " << buildArgs() << " " << files.join(" ") << endl;
       
  2659     }
       
  2660 
       
  2661     QString pfile = project->projectFile();
       
  2662     if(pfile != "(stdin)") {
       
  2663         QString qmake = build_args();
       
  2664         if(!ofile.isEmpty() && !project->isActiveConfig("no_autoqmake")) {
       
  2665             t << escapeFilePath(ofile) << ": " << escapeDependencyPath(fileFixify(pfile)) << " ";
       
  2666             if(Option::mkfile::do_cache)
       
  2667                 t <<  escapeDependencyPath(fileFixify(Option::mkfile::cachefile)) << " ";
       
  2668             if(!specdir().isEmpty()) {
       
  2669                 if(exists(Option::fixPathToLocalOS(specdir()+QDir::separator()+"qmake.conf")))
       
  2670                     t << escapeDependencyPath(specdir() + Option::dir_sep + "qmake.conf") << " ";
       
  2671                 else if(exists(Option::fixPathToLocalOS(specdir()+QDir::separator()+"tmake.conf")))
       
  2672                     t << escapeDependencyPath(specdir() + Option::dir_sep + "tmake.conf") << " ";
       
  2673             }
       
  2674             const QStringList &included = project->values("QMAKE_INTERNAL_INCLUDED_FILES");
       
  2675             t << escapeDependencyPaths(included).join(" \\\n\t\t") << "\n\t"
       
  2676               << qmake << endl;
       
  2677             for(int include = 0; include < included.size(); ++include) {
       
  2678                 const QString i(included.at(include));
       
  2679                 if(!i.isEmpty())
       
  2680                     t << i << ":" << endl;
       
  2681             }
       
  2682         }
       
  2683         if(project->first("QMAKE_ORIG_TARGET") != "qmake") {
       
  2684             t << "qmake: " <<
       
  2685                 project->values("QMAKE_INTERNAL_QMAKE_DEPS").join(" \\\n\t\t");
       
  2686             if(project->isEmpty("QMAKE_NOFORCE"))
       
  2687                 t <<  " FORCE";
       
  2688             t << "\n\t" << "@" << qmake << endl << endl;
       
  2689         }
       
  2690     }
       
  2691 }
       
  2692 
       
  2693 QFileInfo
       
  2694 MakefileGenerator::fileInfo(QString file) const
       
  2695 {
       
  2696     static QHash<FileInfoCacheKey, QFileInfo> *cache = 0;
       
  2697     static QFileInfo noInfo = QFileInfo();
       
  2698     if(!cache) {
       
  2699         cache = new QHash<FileInfoCacheKey, QFileInfo>;
       
  2700         qmakeAddCacheClear(qmakeDeleteCacheClear_QHashFileInfoCacheKeyQFileInfo, (void**)&cache);
       
  2701     }
       
  2702     FileInfoCacheKey cacheKey(file);
       
  2703     QFileInfo value = cache->value(cacheKey, noInfo);
       
  2704     if (value != noInfo)
       
  2705         return value;
       
  2706 
       
  2707     QFileInfo fi(file);
       
  2708     if (fi.exists())
       
  2709         cache->insert(cacheKey, fi);
       
  2710     return fi;
       
  2711 }
       
  2712 
       
  2713 QString
       
  2714 MakefileGenerator::unescapeFilePath(const QString &path) const
       
  2715 {
       
  2716     QString ret = path;
       
  2717     if(!ret.isEmpty()) {
       
  2718         if(ret.contains(QLatin1String("\\ ")))
       
  2719             ret.replace(QLatin1String("\\ "), QLatin1String(" "));
       
  2720         if(ret.contains(QLatin1Char('\"')))
       
  2721             ret.remove(QLatin1Char('\"'));
       
  2722     }
       
  2723     return ret;
       
  2724 }
       
  2725 
       
  2726 QStringList
       
  2727 MakefileGenerator::escapeFilePaths(const QStringList &paths) const
       
  2728 {
       
  2729     QStringList ret;
       
  2730     for(int i = 0; i < paths.size(); ++i)
       
  2731         ret.append(escapeFilePath(paths.at(i)));
       
  2732     return ret;
       
  2733 }
       
  2734 
       
  2735 QStringList
       
  2736 MakefileGenerator::escapeDependencyPaths(const QStringList &paths) const
       
  2737 {
       
  2738     QStringList ret;
       
  2739     for(int i = 0; i < paths.size(); ++i)
       
  2740         ret.append(escapeDependencyPath(paths.at(i)));
       
  2741     return ret;
       
  2742 }
       
  2743 
       
  2744 QStringList
       
  2745 MakefileGenerator::unescapeFilePaths(const QStringList &paths) const
       
  2746 {
       
  2747     QStringList ret;
       
  2748     for(int i = 0; i < paths.size(); ++i)
       
  2749         ret.append(unescapeFilePath(paths.at(i)));
       
  2750     return ret;
       
  2751 }
       
  2752 
       
  2753 QStringList
       
  2754 MakefileGenerator::fileFixify(const QStringList& files, const QString &out_dir, const QString &in_dir,
       
  2755                               FileFixifyType fix, bool canon) const
       
  2756 {
       
  2757     if(files.isEmpty())
       
  2758         return files;
       
  2759     QStringList ret;
       
  2760     for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
       
  2761         if(!(*it).isEmpty())
       
  2762             ret << fileFixify((*it), out_dir, in_dir, fix, canon);
       
  2763     }
       
  2764     return ret;
       
  2765 }
       
  2766 
       
  2767 QString
       
  2768 MakefileGenerator::fileFixify(const QString& file, const QString &out_d, const QString &in_d,
       
  2769                               FileFixifyType fix, bool canon) const
       
  2770 {
       
  2771     if(file.isEmpty())
       
  2772         return file;
       
  2773     QString ret = unescapeFilePath(file);
       
  2774 
       
  2775     //setup the cache
       
  2776     static QHash<FileFixifyCacheKey, QString> *cache = 0;
       
  2777     if(!cache) {
       
  2778         cache = new QHash<FileFixifyCacheKey, QString>;
       
  2779         qmakeAddCacheClear(qmakeDeleteCacheClear_QHashFileFixifyCacheKeyQString, (void**)&cache);
       
  2780     }
       
  2781     FileFixifyCacheKey cacheKey(ret, out_d, in_d, fix, canon);
       
  2782     QString cacheVal = cache->value(cacheKey);
       
  2783     if(!cacheVal.isNull())
       
  2784         return cacheVal;
       
  2785 
       
  2786     //do the fixin'
       
  2787     QString pwd = qmake_getpwd();
       
  2788     if (!pwd.endsWith('/'))
       
  2789         pwd += '/';
       
  2790     QString orig_file = ret;
       
  2791     if(ret.startsWith(QLatin1Char('~'))) {
       
  2792         if(ret.startsWith(QLatin1String("~/")))
       
  2793             ret = QDir::homePath() + ret.mid(1);
       
  2794         else
       
  2795             warn_msg(WarnLogic, "Unable to expand ~ in %s", ret.toLatin1().constData());
       
  2796     }
       
  2797     if(fix == FileFixifyAbsolute || (fix == FileFixifyDefault && project->isActiveConfig("no_fixpath"))) {
       
  2798         if(fix == FileFixifyAbsolute && QDir::isRelativePath(ret)) //already absolute
       
  2799             ret.prepend(pwd);
       
  2800         ret = Option::fixPathToTargetOS(ret, false, canon);
       
  2801     } else { //fix it..
       
  2802         QString out_dir = QDir(Option::output_dir).absoluteFilePath(out_d);
       
  2803         QString in_dir  = QDir(pwd).absoluteFilePath(in_d);
       
  2804         {
       
  2805             QFileInfo in_fi(fileInfo(in_dir));
       
  2806             if(in_fi.exists())
       
  2807                 in_dir = in_fi.canonicalFilePath();
       
  2808             QFileInfo out_fi(fileInfo(out_dir));
       
  2809             if(out_fi.exists())
       
  2810                 out_dir = out_fi.canonicalFilePath();
       
  2811         }
       
  2812 
       
  2813         QString qfile(Option::fixPathToLocalOS(ret, true, canon));
       
  2814         QFileInfo qfileinfo(fileInfo(qfile));
       
  2815         if(out_dir != in_dir || !qfileinfo.isRelative()) {
       
  2816             if(qfileinfo.isRelative()) {
       
  2817                 ret = in_dir + "/" + qfile;
       
  2818                 qfileinfo.setFile(ret);
       
  2819             }
       
  2820             ret = Option::fixPathToTargetOS(ret, false, canon);
       
  2821             if(canon && qfileinfo.exists() &&
       
  2822                file == Option::fixPathToTargetOS(ret, true, canon))
       
  2823                 ret = Option::fixPathToTargetOS(qfileinfo.canonicalFilePath());
       
  2824             QString match_dir = Option::fixPathToTargetOS(out_dir, false, canon);
       
  2825             if(ret == match_dir) {
       
  2826                 ret = "";
       
  2827             } else if(ret.startsWith(match_dir + Option::dir_sep)) {
       
  2828                 ret = ret.mid(match_dir.length() + Option::dir_sep.length());
       
  2829             } else {
       
  2830                 //figure out the depth
       
  2831                 int depth = 4;
       
  2832                 if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
       
  2833                    Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
       
  2834                     if(project && !project->isEmpty("QMAKE_PROJECT_DEPTH"))
       
  2835                         depth = project->first("QMAKE_PROJECT_DEPTH").toInt();
       
  2836                     else if(Option::mkfile::cachefile_depth != -1)
       
  2837                         depth = Option::mkfile::cachefile_depth;
       
  2838                 }
       
  2839                 //calculate how much can be removed
       
  2840                 QString dot_prefix;
       
  2841                 for(int i = 1; i <= depth; i++) {
       
  2842                     int sl = match_dir.lastIndexOf(Option::dir_sep);
       
  2843                     if(sl == -1)
       
  2844                         break;
       
  2845                     match_dir = match_dir.left(sl);
       
  2846                     if(match_dir.isEmpty())
       
  2847                         break;
       
  2848                     if(ret.startsWith(match_dir + Option::dir_sep)) {
       
  2849                         //concat
       
  2850                         int remlen = ret.length() - (match_dir.length() + 1);
       
  2851                         if(remlen < 0)
       
  2852                             remlen = 0;
       
  2853                         ret = ret.right(remlen);
       
  2854                         //prepend
       
  2855                         for(int o = 0; o < i; o++)
       
  2856                             dot_prefix += ".." + Option::dir_sep;
       
  2857                     }
       
  2858                 }
       
  2859                 ret.prepend(dot_prefix);
       
  2860             }
       
  2861         } else {
       
  2862             ret = Option::fixPathToTargetOS(ret, false, canon);
       
  2863         }
       
  2864     }
       
  2865     if(ret.isEmpty())
       
  2866         ret = ".";
       
  2867     debug_msg(3, "Fixed[%d,%d] %s :: to :: %s [%s::%s] [%s::%s]", fix, canon, orig_file.toLatin1().constData(),
       
  2868               ret.toLatin1().constData(), in_d.toLatin1().constData(), out_d.toLatin1().constData(),
       
  2869               pwd.toLatin1().constData(), Option::output_dir.toLatin1().constData());
       
  2870     cache->insert(cacheKey, ret);
       
  2871     return ret;
       
  2872 }
       
  2873 
       
  2874 void
       
  2875 MakefileGenerator::checkMultipleDefinition(const QString &f, const QString &w)
       
  2876 {
       
  2877     if(!(Option::warn_level & WarnLogic))
       
  2878         return;
       
  2879     QString file = f;
       
  2880     int slsh = f.lastIndexOf(Option::dir_sep);
       
  2881     if(slsh != -1)
       
  2882         file.remove(0, slsh + 1);
       
  2883     QStringList &l = project->values(w);
       
  2884     for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it) {
       
  2885         QString file2((*val_it));
       
  2886         slsh = file2.lastIndexOf(Option::dir_sep);
       
  2887         if(slsh != -1)
       
  2888             file2.remove(0, slsh + 1);
       
  2889         if(file2 == file) {
       
  2890             warn_msg(WarnLogic, "Found potential symbol conflict of %s (%s) in %s",
       
  2891                      file.toLatin1().constData(), (*val_it).toLatin1().constData(), w.toLatin1().constData());
       
  2892             break;
       
  2893         }
       
  2894     }
       
  2895 }
       
  2896 
       
  2897 QMakeLocalFileName
       
  2898 MakefileGenerator::fixPathForFile(const QMakeLocalFileName &file, bool forOpen)
       
  2899 {
       
  2900     if(forOpen)
       
  2901         return QMakeLocalFileName(fileFixify(file.real(), qmake_getpwd(), Option::output_dir));
       
  2902     return QMakeLocalFileName(fileFixify(file.real()));
       
  2903 }
       
  2904 
       
  2905 QFileInfo
       
  2906 MakefileGenerator::findFileInfo(const QMakeLocalFileName &file)
       
  2907 {
       
  2908     return fileInfo(file.local());
       
  2909 }
       
  2910 
       
  2911 QMakeLocalFileName
       
  2912 MakefileGenerator::findFileForDep(const QMakeLocalFileName &dep, const QMakeLocalFileName &file)
       
  2913 {
       
  2914     QMakeLocalFileName ret;
       
  2915     if(!project->isEmpty("SKIP_DEPENDS")) {
       
  2916         bool found = false;
       
  2917         QStringList &nodeplist = project->values("SKIP_DEPENDS");
       
  2918         for(QStringList::Iterator it = nodeplist.begin();
       
  2919             it != nodeplist.end(); ++it) {
       
  2920             QRegExp regx((*it));
       
  2921             if(regx.indexIn(dep.local()) != -1) {
       
  2922                 found = true;
       
  2923                 break;
       
  2924             }
       
  2925         }
       
  2926         if(found)
       
  2927             return ret;
       
  2928     }
       
  2929 
       
  2930     ret = QMakeSourceFileInfo::findFileForDep(dep, file);
       
  2931     if(!ret.isNull())
       
  2932         return ret;
       
  2933 
       
  2934     //these are some "hacky" heuristics it will try to do on an include
       
  2935     //however these can be turned off at runtime, I'm not sure how
       
  2936     //reliable these will be, most likely when problems arise turn it off
       
  2937     //and see if they go away..
       
  2938     if(Option::mkfile::do_dep_heuristics) {
       
  2939         if(depHeuristicsCache.contains(dep.real()))
       
  2940             return depHeuristicsCache[dep.real()];
       
  2941 
       
  2942         if(Option::output_dir != qmake_getpwd()
       
  2943            && QDir::isRelativePath(dep.real())) { //is it from the shadow tree
       
  2944             QList<QMakeLocalFileName> depdirs = QMakeSourceFileInfo::dependencyPaths();
       
  2945             depdirs.prepend(fileInfo(file.real()).absoluteDir().path());
       
  2946             QString pwd = qmake_getpwd();
       
  2947             if(pwd.at(pwd.length()-1) != '/')
       
  2948                 pwd += '/';
       
  2949             for(int i = 0; i < depdirs.count(); i++) {
       
  2950                 QString dir = depdirs.at(i).real();
       
  2951                 if(!QDir::isRelativePath(dir) && dir.startsWith(pwd))
       
  2952                     dir = dir.mid(pwd.length());
       
  2953                 if(QDir::isRelativePath(dir)) {
       
  2954                     if(!dir.endsWith(Option::dir_sep))
       
  2955                         dir += Option::dir_sep;
       
  2956                     QString shadow = fileFixify(dir + dep.local(), pwd, Option::output_dir);
       
  2957                     if(exists(shadow)) {
       
  2958                         ret = QMakeLocalFileName(shadow);
       
  2959                         goto found_dep_from_heuristic;
       
  2960                     }
       
  2961                 }
       
  2962             }
       
  2963         }
       
  2964         { //is it from an EXTRA_TARGET
       
  2965             const QString dep_basename = dep.local().section(Option::dir_sep, -1);
       
  2966             QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
       
  2967             for(QStringList::Iterator it = qut.begin(); it != qut.end(); ++it) {
       
  2968                 QString targ = var((*it) + ".target");
       
  2969                 if(targ.isEmpty())
       
  2970                     targ = (*it);
       
  2971                 QString out = Option::fixPathToTargetOS(targ);
       
  2972                 if(out == dep.real() || out.section(Option::dir_sep, -1) == dep_basename) {
       
  2973                     ret = QMakeLocalFileName(out);
       
  2974                     goto found_dep_from_heuristic;
       
  2975                 }
       
  2976             }
       
  2977         }
       
  2978         { //is it from an EXTRA_COMPILER
       
  2979             const QString dep_basename = dep.local().section(Option::dir_sep, -1);
       
  2980             const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
       
  2981             for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
       
  2982                 QString tmp_out = project->values((*it) + ".output").first();
       
  2983                 if(tmp_out.isEmpty())
       
  2984                     continue;
       
  2985                 QStringList &tmp = project->values((*it) + ".input");
       
  2986                 for(QStringList::Iterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
       
  2987                     QStringList &inputs = project->values((*it2));
       
  2988                     for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
       
  2989                         QString out = Option::fixPathToTargetOS(unescapeFilePath(replaceExtraCompilerVariables(tmp_out, (*input), QString())));
       
  2990               if(out == dep.real() || out.section(Option::dir_sep, -1) == dep_basename) {
       
  2991                             ret = QMakeLocalFileName(fileFixify(out, qmake_getpwd(), Option::output_dir));
       
  2992                             goto found_dep_from_heuristic;
       
  2993                         }
       
  2994                     }
       
  2995                 }
       
  2996             }
       
  2997         }
       
  2998     found_dep_from_heuristic:
       
  2999         depHeuristicsCache.insert(dep.real(), ret);
       
  3000     }
       
  3001     return ret;
       
  3002 }
       
  3003 
       
  3004 QStringList
       
  3005 &MakefileGenerator::findDependencies(const QString &file)
       
  3006 {
       
  3007     const QString fixedFile = fileFixify(file);
       
  3008     if(!dependsCache.contains(fixedFile)) {
       
  3009 #if 1
       
  3010         QStringList deps = QMakeSourceFileInfo::dependencies(file);
       
  3011         if(file != fixedFile)
       
  3012             deps += QMakeSourceFileInfo::dependencies(fixedFile);
       
  3013 #else
       
  3014         QStringList deps = QMakeSourceFileInfo::dependencies(fixedFile);
       
  3015 #endif
       
  3016         dependsCache.insert(fixedFile, deps);
       
  3017     }
       
  3018     return dependsCache[fixedFile];
       
  3019 }
       
  3020 
       
  3021 QString
       
  3022 MakefileGenerator::specdir(const QString &outdir)
       
  3023 {
       
  3024 #if 0
       
  3025     if(!spec.isEmpty())
       
  3026         return spec;
       
  3027 #endif
       
  3028     spec = fileFixify(Option::mkfile::qmakespec, outdir);
       
  3029     return spec;
       
  3030 }
       
  3031 
       
  3032 bool
       
  3033 MakefileGenerator::openOutput(QFile &file, const QString &build) const
       
  3034 {
       
  3035     {
       
  3036         QString outdir;
       
  3037         if(!file.fileName().isEmpty()) {
       
  3038             if(QDir::isRelativePath(file.fileName()))
       
  3039                 file.setFileName(Option::output_dir + "/" + file.fileName()); //pwd when qmake was run
       
  3040             QFileInfo fi(fileInfo(file.fileName()));
       
  3041             if(fi.isDir())
       
  3042                 outdir = file.fileName() + '/';
       
  3043         }
       
  3044         if(!outdir.isEmpty() || file.fileName().isEmpty()) {
       
  3045             QString fname = "Makefile";
       
  3046             if(!project->isEmpty("MAKEFILE"))
       
  3047                fname = project->first("MAKEFILE");
       
  3048             file.setFileName(outdir + fname);
       
  3049         }
       
  3050     }
       
  3051     if(QDir::isRelativePath(file.fileName())) {
       
  3052         QString fname = Option::output_dir;  //pwd when qmake was run
       
  3053         if(!fname.endsWith("/"))
       
  3054             fname += "/";
       
  3055         fname += file.fileName();
       
  3056         file.setFileName(fname);
       
  3057     }
       
  3058     if(!build.isEmpty())
       
  3059         file.setFileName(file.fileName() + "." + build);
       
  3060     if(project->isEmpty("QMAKE_MAKEFILE"))
       
  3061         project->values("QMAKE_MAKEFILE").append(file.fileName());
       
  3062     int slsh = file.fileName().lastIndexOf('/');
       
  3063     if(slsh != -1)
       
  3064         mkdir(file.fileName().left(slsh));
       
  3065     if(file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
       
  3066         QFileInfo fi(fileInfo(Option::output.fileName()));
       
  3067         QString od;
       
  3068         if(fi.isSymLink())
       
  3069             od = fileInfo(fi.readLink()).absolutePath();
       
  3070         else
       
  3071             od = fi.path();
       
  3072         od = QDir::fromNativeSeparators(od);
       
  3073         if(QDir::isRelativePath(od)) {
       
  3074             QString dir = Option::output_dir;
       
  3075             if (!dir.endsWith('/') && !od.isEmpty())
       
  3076                 dir += '/';
       
  3077             od.prepend(dir);
       
  3078         }
       
  3079         Option::output_dir = od;
       
  3080         return true;
       
  3081     }
       
  3082     return false;
       
  3083 }
       
  3084 
       
  3085 QT_END_NAMESPACE