Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

projectgenerator.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** 
00003 **
00004 ** Implementation of ProjectGenerator class.
00005 **
00006 ** Copyright (C) 1992-2003 Trolltech AS.  All rights reserved.
00007 **
00008 ** This file is part of qmake.
00009 **
00010 ** This file may be distributed under the terms of the Q Public License
00011 ** as defined by Trolltech AS of Norway and appearing in the file
00012 ** LICENSE.QPL included in the packaging of this file.
00013 **
00014 ** This file may be distributed and/or modified under the terms of the
00015 ** GNU General Public License version 2 as published by the Free Software
00016 ** Foundation and appearing in the file LICENSE.GPL included in the
00017 ** packaging of this file.
00018 **
00019 ** Licensees holding valid Qt Enterprise Edition licenses may use this
00020 ** file in accordance with the Qt Commercial License Agreement provided
00021 ** with the Software.
00022 **
00023 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00024 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00025 **
00026 ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
00027 **   information about Qt Commercial License Agreements.
00028 ** See http://www.trolltech.com/qpl/ for QPL licensing information.
00029 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00030 **
00031 ** Contact info@trolltech.com if any conditions of this licensing are
00032 ** not clear to you.
00033 **
00034 **********************************************************************/
00035 
00036 #include "projectgenerator.h"
00037 #include "option.h"
00038 #include <qdir.h>
00039 #include <qfile.h>
00040 #include <qfileinfo.h>
00041 #include <qregexp.h>
00042 
00043 QString project_builtin_regx() //calculate the builtin regular expression..
00044 { 
00045     QString ret;
00046     QStringList builtin_exts(".c");
00047     builtin_exts << Option::ui_ext << Option::yacc_ext << Option::lex_ext << ".ts";
00048     builtin_exts += Option::h_ext + Option::cpp_ext;
00049     for(QStringList::Iterator ext_it = builtin_exts.begin(); 
00050         ext_it != builtin_exts.end(); ++ext_it) {
00051         if(!ret.isEmpty())
00052             ret += "; ";
00053         ret += QString("*") + (*ext_it);
00054     }
00055     return ret;
00056 }
00057 
00058 
00059 
00060 ProjectGenerator::ProjectGenerator(QMakeProject *p) : MakefileGenerator(p), init_flag(FALSE)
00061 {
00062 }
00063 
00064 void
00065 ProjectGenerator::init()
00066 {
00067     if(init_flag)
00068         return;
00069     int file_count = 0;
00070     init_flag = TRUE;
00071 
00072     QMap<QString, QStringList> &v = project->variables();
00073     QString templ = Option::user_template.isEmpty() ? QString("app") : Option::user_template;
00074     if(!Option::user_template_prefix.isEmpty())
00075         templ.prepend(Option::user_template_prefix);
00076     v["TEMPLATE_ASSIGN"] += templ;
00077 
00078     //figure out target
00079     if(Option::output.name() == "-" || Option::output.name().isEmpty())
00080         v["TARGET"] = QStringList("unknown");
00081 
00082     //the scary stuff
00083     if(project->first("TEMPLATE_ASSIGN") != "subdirs") {
00084         QString builtin_regex = project_builtin_regx();
00085         QStringList dirs = Option::projfile::project_dirs;
00086         if(Option::projfile::do_pwd) {
00087             if(!v["INCLUDEPATH"].contains("."))
00088                 v["INCLUDEPATH"] += ".";
00089             dirs.prepend(QDir::currentDirPath());
00090         }
00091 
00092         for(QStringList::Iterator pd = dirs.begin(); pd != dirs.end(); pd++) {
00093             QString dir, regex;
00094             bool add_depend = FALSE;
00095             if(QFile::exists((*pd))) {
00096                 QFileInfo fi((*pd));
00097                 if(fi.isDir()) {
00098                     dir = (*pd);
00099                     add_depend = TRUE;
00100                     if(dir.right(1) != Option::dir_sep)
00101                         dir += Option::dir_sep;
00102                     if(Option::projfile::do_recursive) {
00103                         QDir d(dir);
00104                         d.setFilter(QDir::Dirs);
00105                         for(int i = 0; i < (int)d.count(); i++) {
00106                             if(d[i] != "." && d[i] != "..") 
00107                                 dirs.append(dir + d[i] + QDir::separator() + builtin_regex);
00108                         }
00109                     }
00110                     regex = builtin_regex;
00111                 } else {
00112                     QString file = (*pd);
00113                     int s = file.findRev(Option::dir_sep);
00114                     if(s != -1)
00115                         dir = file.left(s+1);
00116                     if(addFile(file)) {
00117                         add_depend = TRUE;
00118                         file_count++;
00119                     }
00120                 }
00121             } else { //regexp
00122                 regex = (*pd);
00123             }
00124             if(!regex.isEmpty()) {
00125                 int s = regex.findRev(Option::dir_sep);
00126                 if(s != -1) {
00127                     dir = regex.left(s+1);
00128                     regex = regex.right(regex.length() - (s+1));
00129                 }
00130                 if(Option::projfile::do_recursive) {
00131                     QDir d(dir);
00132                     d.setFilter(QDir::Dirs);
00133                     for(int i = 0; i < (int)d.count(); i++) {
00134                         if(d[i] != "." && d[i] != "..") 
00135                             dirs.append(dir + d[i] + QDir::separator() + regex);
00136                     }
00137                 }
00138                 QDir d(dir, regex);
00139                 for(int i = 0; i < (int)d.count(); i++) {
00140                     QString file = dir + d[i];
00141                     if (addFile(file)) {
00142                         add_depend = TRUE;
00143                         file_count++;
00144                     }
00145                 }
00146             }
00147             if(add_depend && !dir.isEmpty() && !v["DEPENDPATH"].contains(dir)) {
00148                 QFileInfo fi(dir);
00149                 if(fi.absFilePath() != QDir::currentDirPath()) 
00150                     v["DEPENDPATH"] += fileFixify(dir);
00151             }
00152         }
00153     }
00154     if(!file_count) { //shall we try a subdir?
00155         QStringList dirs = Option::projfile::project_dirs;
00156         if(Option::projfile::do_pwd)
00157             dirs.prepend(".");
00158         const QString out_file = fileFixify(Option::output.name());
00159         for(QStringList::Iterator pd = dirs.begin(); pd != dirs.end(); pd++) {
00160             if(QFile::exists((*pd))) {
00161                 QString newdir = (*pd);
00162                 QFileInfo fi(newdir);
00163                 if(fi.isDir()) {
00164                     newdir = fileFixify(newdir);
00165                     QStringList &subdirs = v["SUBDIRS"];
00166                     if(QFile::exists(fi.filePath() + QDir::separator() + fi.fileName() + ".pro") &&
00167                        !subdirs.contains(newdir)) {
00168                         subdirs.append(newdir);
00169                     } else {
00170                         QDir d(newdir, "*.pro");
00171                         d.setFilter(QDir::Files);
00172                         for(int i = 0; i < (int)d.count(); i++) {
00173                             QString nd = newdir;
00174                             if(nd == ".")
00175                                 nd = "";
00176                             else if(!nd.isEmpty() && !nd.endsWith(QString(QChar(QDir::separator()))))
00177                                 nd += QDir::separator();
00178                             nd += d[i];
00179                             fileFixify(nd);
00180                             if(d[i] != "." && d[i] != ".." && !subdirs.contains(nd) && !out_file.endsWith(nd)) 
00181                                 subdirs.append(nd);
00182                         }
00183                     }
00184                     if(Option::projfile::do_recursive) {
00185                         QDir d(newdir);
00186                         d.setFilter(QDir::Dirs);
00187                         for(int i = 0; i < (int)d.count(); i++) {
00188                             QString nd = fileFixify(newdir + QDir::separator() + d[i]);
00189                             if(d[i] != "." && d[i] != ".." && !dirs.contains(nd))
00190                                 dirs.append(nd);
00191                         }
00192                     }
00193                 }
00194             } else { //regexp
00195                 QString regx = (*pd), dir;
00196                 int s = regx.findRev(Option::dir_sep);
00197                 if(s != -1) {
00198                     dir = regx.left(s+1);
00199                     regx = regx.right(regx.length() - (s+1));
00200                 }
00201                 QDir d(dir, regx);
00202                 d.setFilter(QDir::Dirs);
00203                 QStringList &subdirs = v["SUBDIRS"];
00204                 for(int i = 0; i < (int)d.count(); i++) {
00205                     QString newdir(dir + d[i]);
00206                     QFileInfo fi(newdir);
00207                     if(fi.fileName() != "." && fi.fileName() != "..") {
00208                         newdir = fileFixify(newdir);
00209                         if(QFile::exists(fi.filePath() + QDir::separator() + fi.fileName() + ".pro") &&
00210                            !subdirs.contains(newdir)) {
00211                            subdirs.append(newdir);
00212                         } else {
00213                             QDir d(newdir, "*.pro");
00214                             d.setFilter(QDir::Files);
00215                             for(int i = 0; i < (int)d.count(); i++) {
00216                                 QString nd = newdir + QDir::separator() + d[i];
00217                                 fileFixify(nd);
00218                                 if(d[i] != "." && d[i] != ".." && !subdirs.contains(nd)) {
00219                                     if(newdir + d[i] != Option::output_dir + Option::output.name())
00220                                         subdirs.append(nd);
00221                                 }
00222                             }
00223                         }
00224                         if(Option::projfile::do_recursive && !dirs.contains(newdir))
00225                             dirs.append(newdir);
00226                     }
00227                 }
00228             }
00229         }
00230         v["TEMPLATE_ASSIGN"] = "subdirs";
00231         return;
00232     }
00233 
00234     QPtrList<MakefileDependDir> deplist;
00235     deplist.setAutoDelete(TRUE);
00236     {
00237         QStringList &d = v["DEPENDPATH"];
00238         for(QStringList::Iterator it = d.begin(); it != d.end(); ++it) {
00239             QString r = (*it), l = Option::fixPathToLocalOS((*it));
00240             deplist.append(new MakefileDependDir(r, l));
00241         }
00242     }
00243     QStringList &h = v["HEADERS"];
00244     bool no_qt_files = TRUE;
00245     QString srcs[] = { "SOURCES", "YACCSOURCES", "LEXSOURCES", "INTERFACES", QString::null };
00246     for(int i = 0; !srcs[i].isNull(); i++) {
00247         QStringList &l = v[srcs[i]];
00248         for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it) {
00249             if(generateDependencies(deplist, (*val_it), TRUE)) {
00250                 QStringList &tmp = findDependencies((*val_it));
00251                 if(!tmp.isEmpty()) {
00252                     for(QStringList::Iterator dep_it = tmp.begin(); dep_it != tmp.end(); ++dep_it) {
00253                         QString file_dir = (*dep_it).section(Option::dir_sep, 0, -2),
00254                             file_no_path = (*dep_it).section(Option::dir_sep, -1);
00255                         if(!file_dir.isEmpty()) {
00256                             for(MakefileDependDir *mdd = deplist.first(); mdd; mdd = deplist.next()) {
00257                                 if(mdd->local_dir == file_dir && !v["INCLUDEPATH"].contains(mdd->real_dir))
00258                                     v["INCLUDEPATH"] += mdd->real_dir;
00259                             }
00260                         }
00261                         if(no_qt_files && file_no_path.find(QRegExp("^q[a-z_0-9].h$")) != -1)
00262                             no_qt_files = FALSE;
00263                         QString h_ext;
00264                         for(QStringList::Iterator hit = Option::h_ext.begin(); 
00265                             hit != Option::h_ext.end(); ++hit) {
00266                             if((*dep_it).endsWith((*hit))) {
00267                                 h_ext = (*hit);
00268                                 break;
00269                             }
00270                         }
00271                         if(!h_ext.isEmpty()) {
00272                             if((*dep_it).left(1).lower() == "q") {
00273                                 QString qhdr = (*dep_it).lower();
00274                                 if(file_no_path == "qthread.h")
00275                                     addConfig("thread");
00276                             }
00277                             for(QStringList::Iterator cppit = Option::cpp_ext.begin();
00278                                 cppit != Option::cpp_ext.end(); ++cppit) {
00279                                 QString src((*dep_it).left((*dep_it).length() - h_ext.length()) + 
00280                                             (*cppit));
00281                                 if(QFile::exists(src)) {
00282                                     bool exists = FALSE;
00283                                     QStringList &srcl = v["SOURCES"];
00284                                     for(QStringList::Iterator src_it = srcl.begin(); 
00285                                         src_it != srcl.end(); ++src_it) {
00286                                         if((*src_it).lower() == src.lower()) {
00287                                             exists = TRUE;
00288                                             break;
00289                                         }
00290                                     }
00291                                     if(!exists)
00292                                         srcl.append(src);
00293                                 }
00294                             }
00295                         } else if((*dep_it).endsWith(Option::lex_ext) &&
00296                                   file_no_path.startsWith(Option::lex_mod)) {
00297                             addConfig("lex_included");
00298                         }
00299                         if(!h.contains((*dep_it))) {
00300                             if(generateMocList((*dep_it)) && !findMocDestination((*dep_it)).isEmpty())
00301                                 h += (*dep_it);
00302                         }
00303                     }
00304                 }
00305             }
00306         }
00307     }
00308     if(h.isEmpty())
00309         addConfig("moc", FALSE);
00310 
00311     //if we find a file that matches an forms it needn't be included in the project
00312     QStringList &u = v["INTERFACES"];
00313     QString no_ui[] = { "SOURCES", "HEADERS", QString::null };
00314     {
00315         for(int i = 0; !no_ui[i].isNull(); i++) {
00316             QStringList &l = v[no_ui[i]];
00317             for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ) {
00318                 bool found = FALSE;
00319                 for(QStringList::Iterator ui_it = u.begin(); ui_it != u.end(); ++ui_it) {
00320                     QString s1 = (*val_it).right((*val_it).length() - ((*val_it).findRev(Option::dir_sep) + 1));
00321                     if(s1.findRev('.') != -1)
00322                         s1 = s1.left(s1.findRev('.')) + Option::ui_ext;
00323                     QString u1 = (*ui_it).right((*ui_it).length() - ((*ui_it).findRev(Option::dir_sep) + 1));
00324                     if(s1 == u1) {
00325                         found = TRUE;
00326                         break;
00327                     }
00328                 }
00329                 if(!found && (*val_it).endsWith(Option::cpp_moc_ext))
00330                     found = TRUE;
00331                 if(found)
00332                     val_it = l.remove(val_it);
00333                 else
00334                     ++val_it;
00335             }
00336         }
00337     }
00338 }
00339 
00340 
00341 bool
00342 ProjectGenerator::writeMakefile(QTextStream &t)
00343 {
00344     t << "######################################################################" << endl;
00345     t << "# Automatically generated by qmake (" << qmake_version() << ") " << QDateTime::currentDateTime().toString() << endl;
00346     t << "######################################################################" << endl << endl;
00347     QStringList::Iterator it;
00348     for(it = Option::before_user_vars.begin(); it != Option::before_user_vars.end(); ++it)
00349         t << (*it) << endl;
00350     t << getWritableVar("TEMPLATE_ASSIGN", FALSE);
00351     if(project->first("TEMPLATE_ASSIGN") == "subdirs") {
00352         t << endl << "# Directories" << "\n"
00353           << getWritableVar("SUBDIRS");
00354     } else {
00355         t << getWritableVar("TARGET")
00356           << getWritableVar("CONFIG", FALSE)
00357           << getWritableVar("CONFIG_REMOVE", FALSE)
00358           << getWritableVar("DEPENDPATH")
00359           << getWritableVar("INCLUDEPATH") << endl;
00360 
00361         t << "# Input" << "\n";
00362         t << getWritableVar("HEADERS") 
00363           << getWritableVar("INTERFACES") 
00364           << getWritableVar("LEXSOURCES") 
00365           << getWritableVar("YACCSOURCES") 
00366           << getWritableVar("SOURCES")
00367           << getWritableVar("TRANSLATIONS");
00368     }
00369     for(it = Option::after_user_vars.begin(); it != Option::after_user_vars.end(); ++it)
00370         t << (*it) << endl;
00371     return TRUE;
00372 }
00373 
00374 bool
00375 ProjectGenerator::addConfig(const QString &cfg, bool add)
00376 {
00377     QString where = "CONFIG";
00378     if(!add)
00379         where = "CONFIG_REMOVE";
00380     if(!project->variables()[where].contains(cfg)) {
00381         project->variables()[where] += cfg;
00382         return TRUE;
00383     }
00384     return FALSE;
00385 }
00386 
00387 
00388 bool
00389 ProjectGenerator::addFile(QString file)
00390 {
00391     file = fileFixify(file, QDir::currentDirPath());
00392     QString dir;
00393     int s = file.findRev(Option::dir_sep);
00394     if(s != -1)
00395         dir = file.left(s+1);
00396     if(file.mid(dir.length(), Option::h_moc_mod.length()) == Option::h_moc_mod)
00397         return FALSE;
00398 
00399     QString where;
00400     for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) {
00401         if(file.endsWith((*cppit))) {
00402             if(QFile::exists(file.left(file.length() - (*cppit).length()) + Option::ui_ext))
00403                 return FALSE;
00404             else
00405                 where = "SOURCES";
00406             break;
00407         }
00408     }
00409     if(where.isEmpty()) {
00410         for(QStringList::Iterator hit = Option::h_ext.begin(); hit != Option::h_ext.end(); ++hit) {
00411             if(file.endsWith((*hit))) {
00412                 where = "HEADERS";
00413                 break;
00414             }
00415         }
00416     }
00417     if(where.isEmpty()) {
00418         if(file.endsWith(Option::ui_ext))
00419             where = "INTERFACES";
00420         else if(file.endsWith(".c"))
00421             where = "SOURCES";
00422         else if(file.endsWith(Option::lex_ext))
00423             where = "LEXSOURCES";
00424         else if(file.endsWith(Option::yacc_ext))
00425             where = "YACCSOURCES";
00426         else if(file.endsWith(".ts"))
00427             where = "TRANSLATIONS";
00428     }
00429 
00430     QString newfile = fileFixify(file);
00431     if(!where.isEmpty() && !project->variables()[where].contains(file)) {
00432         project->variables()[where] += newfile;
00433         return TRUE;
00434     }
00435     return FALSE;
00436 }
00437 
00438 
00439 QString
00440 ProjectGenerator::getWritableVar(const QString &v, bool fixPath)
00441 {
00442     QStringList &vals = project->variables()[v];
00443     if(vals.isEmpty())
00444         return "";
00445 
00446     QString ret;
00447     if(v.endsWith("_REMOVE"))
00448         ret = v.left(v.length() - 7) + " -= ";
00449     else if(v.endsWith("_ASSIGN"))
00450         ret = v.left(v.length() - 7) + " = ";
00451     else
00452         ret = v + " += ";
00453     QString join = vals.join(" ");
00454     if(ret.length() + join.length() > 80) {
00455         QString spaces;
00456         for(unsigned int i = 0; i < ret.length(); i++)
00457             spaces += " ";
00458         join = vals.join(" \\\n" + spaces);
00459     } 
00460 #if 0
00461     // ### Commented out for now so that project generation works.
00462     // Sam: it had to do with trailing \'s (ie considered continuation lines)
00463     if(fixPath)
00464         join = join.replace("\\", "/");
00465 #else
00466     Q_UNUSED(fixPath);
00467 #endif
00468     return ret + join + "\n";
00469 }
00470 
00471 bool
00472 ProjectGenerator::openOutput(QFile &file) const
00473 {
00474     QString outdir;
00475     if(!file.name().isEmpty()) {
00476         QFileInfo fi(file);
00477         if(fi.isDir())
00478             outdir = fi.dirPath() + QDir::separator();
00479     }
00480     if(!outdir.isEmpty() || file.name().isEmpty()) {
00481         QString dir = QDir::currentDirPath();
00482         int s = dir.findRev('/');
00483         if(s != -1)
00484             dir = dir.right(dir.length() - (s + 1));
00485         file.setName(outdir + dir + ".pro");
00486     }
00487     return MakefileGenerator::openOutput(file);
00488 }

Generated on Sat Nov 5 16:18:20 2005 for OPIE by  doxygen 1.4.2