diff options
Diffstat (limited to 'qmake/generators/projectgenerator.cpp')
-rw-r--r-- | qmake/generators/projectgenerator.cpp | 491 |
1 files changed, 491 insertions, 0 deletions
diff --git a/qmake/generators/projectgenerator.cpp b/qmake/generators/projectgenerator.cpp new file mode 100644 index 000000000..4decf2a90 --- /dev/null +++ b/qmake/generators/projectgenerator.cpp @@ -0,0 +1,491 @@ +/**************************************************************************** +** +** Implementation of ProjectGenerator class. +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of qmake. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "projectgenerator.h" +#include "option.h" +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qregexp.h> + +TQString project_builtin_regx() //calculate the builtin regular expression.. +{ + TQString ret; + TQStringList builtin_exts(".c"); + builtin_exts << Option::ui_ext << Option::yacc_ext << Option::lex_ext << ".ts"; + builtin_exts += Option::h_ext + Option::cpp_ext; + for(TQStringList::Iterator ext_it = builtin_exts.begin(); + ext_it != builtin_exts.end(); ++ext_it) { + if(!ret.isEmpty()) + ret += "; "; + ret += TQString("*") + (*ext_it); + } + return ret; +} + + + +ProjectGenerator::ProjectGenerator(TQMakeProject *p) : MakefileGenerator(p), init_flag(FALSE) +{ +} + +void +ProjectGenerator::init() +{ + if(init_flag) + return; + int file_count = 0; + init_flag = TRUE; + + TQMap<TQString, TQStringList> &v = project->variables(); + TQString templ = Option::user_template.isEmpty() ? TQString("app") : Option::user_template; + if(!Option::user_template_prefix.isEmpty()) + templ.prepend(Option::user_template_prefix); + v["TEMPLATE_ASSIGN"] += templ; + + //figure out target + if(Option::output.name() == "-" || Option::output.name().isEmpty()) + v["TARGET"] = TQStringList("unknown"); + + //the scary stuff + if(project->first("TEMPLATE_ASSIGN") != "subdirs") { + TQString builtin_regex = project_builtin_regx(); + TQStringList dirs = Option::projfile::project_dirs; + if(Option::projfile::do_pwd) { + if(!v["INCLUDEPATH"].contains(".")) + v["INCLUDEPATH"] += "."; + dirs.prepend(TQDir::currentDirPath()); + } + + for(TQStringList::Iterator pd = dirs.begin(); pd != dirs.end(); pd++) { + TQString dir, regex; + bool add_depend = FALSE; + if(TQFile::exists((*pd))) { + TQFileInfo fi((*pd)); + if(fi.isDir()) { + dir = (*pd); + add_depend = TRUE; + if(dir.right(1) != Option::dir_sep) + dir += Option::dir_sep; + if(Option::projfile::do_recursive) { + TQDir d(dir); + d.setFilter(TQDir::Dirs); + for(int i = 0; i < (int)d.count(); i++) { + if(d[i] != "." && d[i] != "..") + dirs.append(dir + d[i] + TQDir::separator() + builtin_regex); + } + } + regex = builtin_regex; + } else { + TQString file = (*pd); + int s = file.findRev(Option::dir_sep); + if(s != -1) + dir = file.left(s+1); + if(addFile(file)) { + add_depend = TRUE; + file_count++; + } + } + } else { //regexp + regex = (*pd); + } + if(!regex.isEmpty()) { + int s = regex.findRev(Option::dir_sep); + if(s != -1) { + dir = regex.left(s+1); + regex = regex.right(regex.length() - (s+1)); + } + if(Option::projfile::do_recursive) { + TQDir d(dir); + d.setFilter(TQDir::Dirs); + for(int i = 0; i < (int)d.count(); i++) { + if(d[i] != "." && d[i] != "..") + dirs.append(dir + d[i] + TQDir::separator() + regex); + } + } + TQDir d(dir, regex); + for(int i = 0; i < (int)d.count(); i++) { + TQString file = dir + d[i]; + if (addFile(file)) { + add_depend = TRUE; + file_count++; + } + } + } + if(add_depend && !dir.isEmpty() && !v["DEPENDPATH"].contains(dir)) { + TQFileInfo fi(dir); + if(fi.absFilePath() != TQDir::currentDirPath()) + v["DEPENDPATH"] += fileFixify(dir); + } + } + } + if(!file_count) { //shall we try a subdir? + TQStringList dirs = Option::projfile::project_dirs; + if(Option::projfile::do_pwd) + dirs.prepend("."); + const TQString out_file = fileFixify(Option::output.name()); + for(TQStringList::Iterator pd = dirs.begin(); pd != dirs.end(); pd++) { + if(TQFile::exists((*pd))) { + TQString newdir = (*pd); + TQFileInfo fi(newdir); + if(fi.isDir()) { + newdir = fileFixify(newdir); + TQStringList &subdirs = v["SUBDIRS"]; + if(TQFile::exists(fi.filePath() + TQDir::separator() + fi.fileName() + ".pro") && + !subdirs.contains(newdir)) { + subdirs.append(newdir); + } else { + TQDir d(newdir, "*.pro"); + d.setFilter(TQDir::Files); + for(int i = 0; i < (int)d.count(); i++) { + TQString nd = newdir; + if(nd == ".") + nd = ""; + else if(!nd.isEmpty() && !nd.endsWith(TQString(TQChar(TQDir::separator())))) + nd += TQDir::separator(); + nd += d[i]; + fileFixify(nd); + if(d[i] != "." && d[i] != ".." && !subdirs.contains(nd) && !out_file.endsWith(nd)) + subdirs.append(nd); + } + } + if(Option::projfile::do_recursive) { + TQDir d(newdir); + d.setFilter(TQDir::Dirs); + for(int i = 0; i < (int)d.count(); i++) { + TQString nd = fileFixify(newdir + TQDir::separator() + d[i]); + if(d[i] != "." && d[i] != ".." && !dirs.contains(nd)) + dirs.append(nd); + } + } + } + } else { //regexp + TQString regx = (*pd), dir; + int s = regx.findRev(Option::dir_sep); + if(s != -1) { + dir = regx.left(s+1); + regx = regx.right(regx.length() - (s+1)); + } + TQDir d(dir, regx); + d.setFilter(TQDir::Dirs); + TQStringList &subdirs = v["SUBDIRS"]; + for(int i = 0; i < (int)d.count(); i++) { + TQString newdir(dir + d[i]); + TQFileInfo fi(newdir); + if(fi.fileName() != "." && fi.fileName() != "..") { + newdir = fileFixify(newdir); + if(TQFile::exists(fi.filePath() + TQDir::separator() + fi.fileName() + ".pro") && + !subdirs.contains(newdir)) { + subdirs.append(newdir); + } else { + TQDir d(newdir, "*.pro"); + d.setFilter(TQDir::Files); + for(int i = 0; i < (int)d.count(); i++) { + TQString nd = newdir + TQDir::separator() + d[i]; + fileFixify(nd); + if(d[i] != "." && d[i] != ".." && !subdirs.contains(nd)) { + if(newdir + d[i] != Option::output_dir + Option::output.name()) + subdirs.append(nd); + } + } + } + if(Option::projfile::do_recursive && !dirs.contains(newdir)) + dirs.append(newdir); + } + } + } + } + v["TEMPLATE_ASSIGN"] = "subdirs"; + return; + } + + TQPtrList<MakefileDependDir> deplist; + deplist.setAutoDelete(TRUE); + { + TQStringList &d = v["DEPENDPATH"]; + for(TQStringList::Iterator it = d.begin(); it != d.end(); ++it) { + TQString r = (*it), l = Option::fixPathToLocalOS((*it)); + deplist.append(new MakefileDependDir(r, l)); + } + } + TQStringList &h = v["HEADERS"]; + bool no_qt_files = TRUE; + TQString srcs[] = { "SOURCES", "YACCSOURCES", "LEXSOURCES", "INTERFACES", TQString::null }; + for(int i = 0; !srcs[i].isNull(); i++) { + TQStringList &l = v[srcs[i]]; + for(TQStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it) { + if(generateDependencies(deplist, (*val_it), TRUE)) { + TQStringList &tmp = findDependencies((*val_it)); + if(!tmp.isEmpty()) { + for(TQStringList::Iterator dep_it = tmp.begin(); dep_it != tmp.end(); ++dep_it) { + TQString file_dir = (*dep_it).section(Option::dir_sep, 0, -2), + file_no_path = (*dep_it).section(Option::dir_sep, -1); + if(!file_dir.isEmpty()) { + for(MakefileDependDir *mdd = deplist.first(); mdd; mdd = deplist.next()) { + if(mdd->local_dir == file_dir && !v["INCLUDEPATH"].contains(mdd->real_dir)) + v["INCLUDEPATH"] += mdd->real_dir; + } + } + if(no_qt_files && file_no_path.find(TQRegExp("^q[a-z_0-9].h$")) != -1) + no_qt_files = FALSE; + TQString h_ext; + for(TQStringList::Iterator hit = Option::h_ext.begin(); + hit != Option::h_ext.end(); ++hit) { + if((*dep_it).endsWith((*hit))) { + h_ext = (*hit); + break; + } + } + if(!h_ext.isEmpty()) { + if((*dep_it).left(1).lower() == "q") { + TQString qhdr = (*dep_it).lower(); + if(file_no_path == "qthread.h") + addConfig("thread"); + } + for(TQStringList::Iterator cppit = Option::cpp_ext.begin(); + cppit != Option::cpp_ext.end(); ++cppit) { + TQString src((*dep_it).left((*dep_it).length() - h_ext.length()) + + (*cppit)); + if(TQFile::exists(src)) { + bool exists = FALSE; + TQStringList &srcl = v["SOURCES"]; + for(TQStringList::Iterator src_it = srcl.begin(); + src_it != srcl.end(); ++src_it) { + if((*src_it).lower() == src.lower()) { + exists = TRUE; + break; + } + } + if(!exists) + srcl.append(src); + } + } + } else if((*dep_it).endsWith(Option::lex_ext) && + file_no_path.startsWith(Option::lex_mod)) { + addConfig("lex_included"); + } + if(!h.contains((*dep_it))) { + if(generateMocList((*dep_it)) && !findMocDestination((*dep_it)).isEmpty()) + h += (*dep_it); + } + } + } + } + } + } + if(h.isEmpty()) + addConfig("moc", FALSE); + + //if we find a file that matches an forms it needn't be included in the project + TQStringList &u = v["INTERFACES"]; + TQString no_ui[] = { "SOURCES", "HEADERS", TQString::null }; + { + for(int i = 0; !no_ui[i].isNull(); i++) { + TQStringList &l = v[no_ui[i]]; + for(TQStringList::Iterator val_it = l.begin(); val_it != l.end(); ) { + bool found = FALSE; + for(TQStringList::Iterator ui_it = u.begin(); ui_it != u.end(); ++ui_it) { + TQString s1 = (*val_it).right((*val_it).length() - ((*val_it).findRev(Option::dir_sep) + 1)); + if(s1.findRev('.') != -1) + s1 = s1.left(s1.findRev('.')) + Option::ui_ext; + TQString u1 = (*ui_it).right((*ui_it).length() - ((*ui_it).findRev(Option::dir_sep) + 1)); + if(s1 == u1) { + found = TRUE; + break; + } + } + if(!found && (*val_it).endsWith(Option::cpp_moc_ext)) + found = TRUE; + if(found) + val_it = l.remove(val_it); + else + ++val_it; + } + } + } +} + + +bool +ProjectGenerator::writeMakefile(TQTextStream &t) +{ + t << "######################################################################" << endl; + t << "# Automatically generated by qmake (" << qmake_version() << ") " << TQDateTime::currentDateTime().toString() << endl; + t << "######################################################################" << endl << endl; + TQStringList::Iterator it; + for(it = Option::before_user_vars.begin(); it != Option::before_user_vars.end(); ++it) + t << (*it) << endl; + t << getWritableVar("TEMPLATE_ASSIGN", FALSE); + if(project->first("TEMPLATE_ASSIGN") == "subdirs") { + t << endl << "# Directories" << "\n" + << getWritableVar("SUBDIRS"); + } else { + t << getWritableVar("TARGET") + << getWritableVar("CONFIG", FALSE) + << getWritableVar("CONFIG_REMOVE", FALSE) + << getWritableVar("DEPENDPATH") + << getWritableVar("INCLUDEPATH") << endl; + + t << "# Input" << "\n"; + t << getWritableVar("HEADERS") + << getWritableVar("INTERFACES") + << getWritableVar("LEXSOURCES") + << getWritableVar("YACCSOURCES") + << getWritableVar("SOURCES") + << getWritableVar("TRANSLATIONS"); + } + for(it = Option::after_user_vars.begin(); it != Option::after_user_vars.end(); ++it) + t << (*it) << endl; + return TRUE; +} + +bool +ProjectGenerator::addConfig(const TQString &cfg, bool add) +{ + TQString where = "CONFIG"; + if(!add) + where = "CONFIG_REMOVE"; + if(!project->variables()[where].contains(cfg)) { + project->variables()[where] += cfg; + return TRUE; + } + return FALSE; +} + + +bool +ProjectGenerator::addFile(TQString file) +{ + file = fileFixify(file, TQDir::currentDirPath()); + TQString dir; + int s = file.findRev(Option::dir_sep); + if(s != -1) + dir = file.left(s+1); + if(file.mid(dir.length(), Option::h_moc_mod.length()) == Option::h_moc_mod) + return FALSE; + + TQString where; + for(TQStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) { + if(file.endsWith((*cppit))) { + if(TQFile::exists(file.left(file.length() - (*cppit).length()) + Option::ui_ext)) + return FALSE; + else + where = "SOURCES"; + break; + } + } + if(where.isEmpty()) { + for(TQStringList::Iterator hit = Option::h_ext.begin(); hit != Option::h_ext.end(); ++hit) { + if(file.endsWith((*hit))) { + where = "HEADERS"; + break; + } + } + } + if(where.isEmpty()) { + if(file.endsWith(Option::ui_ext)) + where = "INTERFACES"; + else if(file.endsWith(".c")) + where = "SOURCES"; + else if(file.endsWith(Option::lex_ext)) + where = "LEXSOURCES"; + else if(file.endsWith(Option::yacc_ext)) + where = "YACCSOURCES"; + else if(file.endsWith(".ts")) + where = "TRANSLATIONS"; + } + + TQString newfile = fileFixify(file); + if(!where.isEmpty() && !project->variables()[where].contains(file)) { + project->variables()[where] += newfile; + return TRUE; + } + return FALSE; +} + + +TQString +ProjectGenerator::getWritableVar(const TQString &v, bool fixPath) +{ + TQStringList &vals = project->variables()[v]; + if(vals.isEmpty()) + return ""; + + TQString ret; + if(v.endsWith("_REMOVE")) + ret = v.left(v.length() - 7) + " -= "; + else if(v.endsWith("_ASSIGN")) + ret = v.left(v.length() - 7) + " = "; + else + ret = v + " += "; + TQString join = vals.join(" "); + if(ret.length() + join.length() > 80) { + TQString spaces; + for(unsigned int i = 0; i < ret.length(); i++) + spaces += " "; + join = vals.join(" \\\n" + spaces); + } +#if 0 + // ### Commented out for now so that project generation works. + // Sam: it had to do with trailing \'s (ie considered continuation lines) + if(fixPath) + join = join.replace("\\", "/"); +#else + Q_UNUSED(fixPath); +#endif + return ret + join + "\n"; +} + +bool +ProjectGenerator::openOutput(TQFile &file) const +{ + TQString outdir; + if(!file.name().isEmpty()) { + TQFileInfo fi(file); + if(fi.isDir()) + outdir = fi.dirPath() + TQDir::separator(); + } + if(!outdir.isEmpty() || file.name().isEmpty()) { + TQString dir = TQDir::currentDirPath(); + int s = dir.findRev('/'); + if(s != -1) + dir = dir.right(dir.length() - (s + 1)); + file.setName(outdir + dir + ".pro"); + } + return MakefileGenerator::openOutput(file); +} |