/**************************************************************************** ** ** Implementation of ProjectGenerator class. ** ** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. ** ** 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 requirements 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 #include #include #include 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 &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"].tqcontains(".")) 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.tqfindRev(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.tqfindRev(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"].tqcontains(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.tqcontains(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.tqcontains(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.tqcontains(nd)) dirs.append(nd); } } } } else { //regexp TQString regx = (*pd), dir; int s = regx.tqfindRev(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.tqcontains(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.tqcontains(nd)) { if(newdir + d[i] != Option::output_dir + Option::output.name()) subdirs.append(nd); } } } if(Option::projfile::do_recursive && !dirs.tqcontains(newdir)) dirs.append(newdir); } } } } v["TEMPLATE_ASSIGN"] = "subdirs"; return; } TQPtrList 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 = tqfindDependencies((*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"].tqcontains(mdd->real_dir)) v["INCLUDEPATH"] += mdd->real_dir; } } if(no_qt_files && file_no_path.tqfind(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(TQT_TQSTRING((*dep_it).left(1)).lower() == "q") { TQString qhdr = (*dep_it).lower(); if(file_no_path == "tqthread.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.tqcontains((*dep_it))) { if(generateMocList((*dep_it)) && !tqfindMocDestination((*dep_it)).isEmpty()) h += (*dep_it); } } } } } } if(h.isEmpty()) addConfig("tqmoc", FALSE); //if we tqfind 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).tqfindRev(Option::dir_sep) + 1)); if(s1.tqfindRev('.') != -1) s1 = s1.left(s1.tqfindRev('.')) + Option::ui_ext; TQString u1 = (*ui_it).right((*ui_it).length() - ((*ui_it).tqfindRev(Option::dir_sep) + 1)); if(s1 == u1) { found = TRUE; break; } } if(!found && (*val_it).endsWith(Option::cpp_tqmoc_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::tqcurrentDateTime().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].tqcontains(cfg)) { project->variables()[where] += cfg; return TRUE; } return FALSE; } bool ProjectGenerator::addFile(TQString file) { file = fileFixify(file, TQDir::currentDirPath()); TQString dir; int s = file.tqfindRev(Option::dir_sep); if(s != -1) dir = file.left(s+1); if(file.mid(dir.length(), Option::h_tqmoc_mod.length()) == Option::h_tqmoc_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].tqcontains(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.tqreplace("\\", "/"); #else TQ_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.tqfindRev('/'); if(s != -1) dir = dir.right(dir.length() - (s + 1)); file.setName(outdir + dir + ".pro"); } return MakefileGenerator::openOutput(file); }