source: trunk/qmake/generators/projectgenerator.cpp

Last change on this file was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 15.0 KB
Line 
1/****************************************************************************
2** $Id: projectgenerator.cpp 2 2005-11-16 15:49:26Z dmik $
3**
4** Implementation of ProjectGenerator class.
5**
6** Copyright (C) 1992-2003 Trolltech AS. All rights reserved.
7**
8** This file is part of qmake.
9**
10** This file may be distributed under the terms of the Q Public License
11** as defined by Trolltech AS of Norway and appearing in the file
12** LICENSE.QPL included in the packaging of this file.
13**
14** This file may be distributed and/or modified under the terms of the
15** GNU General Public License version 2 as published by the Free Software
16** Foundation and appearing in the file LICENSE.GPL included in the
17** packaging of this file.
18**
19** Licensees holding valid Qt Enterprise Edition licenses may use this
20** file in accordance with the Qt Commercial License Agreement provided
21** with the Software.
22**
23** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
24** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
25**
26** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
27** information about Qt Commercial License Agreements.
28** See http://www.trolltech.com/qpl/ for QPL licensing information.
29** See http://www.trolltech.com/gpl/ for GPL licensing information.
30**
31** Contact info@trolltech.com if any conditions of this licensing are
32** not clear to you.
33**
34**********************************************************************/
35
36#include "projectgenerator.h"
37#include "option.h"
38#include <qdir.h>
39#include <qfile.h>
40#include <qfileinfo.h>
41#include <qregexp.h>
42
43QString project_builtin_regx() //calculate the builtin regular expression..
44{
45 QString ret;
46 QStringList builtin_exts(".c");
47 builtin_exts << Option::ui_ext << Option::yacc_ext << Option::lex_ext << ".ts";
48 builtin_exts += Option::h_ext + Option::cpp_ext;
49 for(QStringList::Iterator ext_it = builtin_exts.begin();
50 ext_it != builtin_exts.end(); ++ext_it) {
51 if(!ret.isEmpty())
52 ret += "; ";
53 ret += QString("*") + (*ext_it);
54 }
55 return ret;
56}
57
58
59
60ProjectGenerator::ProjectGenerator(QMakeProject *p) : MakefileGenerator(p), init_flag(FALSE)
61{
62}
63
64void
65ProjectGenerator::init()
66{
67 if(init_flag)
68 return;
69 int file_count = 0;
70 init_flag = TRUE;
71
72 QMap<QString, QStringList> &v = project->variables();
73 QString templ = Option::user_template.isEmpty() ? QString("app") : Option::user_template;
74 if(!Option::user_template_prefix.isEmpty())
75 templ.prepend(Option::user_template_prefix);
76 v["TEMPLATE_ASSIGN"] += templ;
77
78 //figure out target
79 if(Option::output.name() == "-" || Option::output.name().isEmpty())
80 v["TARGET"] = QStringList("unknown");
81
82 //the scary stuff
83 if(project->first("TEMPLATE_ASSIGN") != "subdirs") {
84 QString builtin_regex = project_builtin_regx();
85 QStringList dirs = Option::projfile::project_dirs;
86 if(Option::projfile::do_pwd) {
87 if(!v["INCLUDEPATH"].contains("."))
88 v["INCLUDEPATH"] += ".";
89 dirs.prepend(QDir::currentDirPath());
90 }
91
92 for(QStringList::Iterator pd = dirs.begin(); pd != dirs.end(); pd++) {
93 QString dir, regex;
94 bool add_depend = FALSE;
95 if(QFile::exists((*pd))) {
96 QFileInfo fi((*pd));
97 if(fi.isDir()) {
98 dir = (*pd);
99 add_depend = TRUE;
100 if(dir.right(1) != Option::dir_sep)
101 dir += Option::dir_sep;
102 if(Option::projfile::do_recursive) {
103 QDir d(dir);
104 d.setFilter(QDir::Dirs);
105 for(int i = 0; i < (int)d.count(); i++) {
106 if(d[i] != "." && d[i] != "..")
107 dirs.append(dir + d[i] + QDir::separator() + builtin_regex);
108 }
109 }
110 regex = builtin_regex;
111 } else {
112 QString file = (*pd);
113 int s = file.findRev(Option::dir_sep);
114 if(s != -1)
115 dir = file.left(s+1);
116 if(addFile(file)) {
117 add_depend = TRUE;
118 file_count++;
119 }
120 }
121 } else { //regexp
122 regex = (*pd);
123 }
124 if(!regex.isEmpty()) {
125 int s = regex.findRev(Option::dir_sep);
126 if(s != -1) {
127 dir = regex.left(s+1);
128 regex = regex.right(regex.length() - (s+1));
129 }
130 if(Option::projfile::do_recursive) {
131 QDir d(dir);
132 d.setFilter(QDir::Dirs);
133 for(int i = 0; i < (int)d.count(); i++) {
134 if(d[i] != "." && d[i] != "..")
135 dirs.append(dir + d[i] + QDir::separator() + regex);
136 }
137 }
138 QDir d(dir, regex);
139 for(int i = 0; i < (int)d.count(); i++) {
140 QString file = dir + d[i];
141 if (addFile(file)) {
142 add_depend = TRUE;
143 file_count++;
144 }
145 }
146 }
147 if(add_depend && !dir.isEmpty() && !v["DEPENDPATH"].contains(dir)) {
148 QFileInfo fi(dir);
149 if(fi.absFilePath() != QDir::currentDirPath())
150 v["DEPENDPATH"] += fileFixify(dir);
151 }
152 }
153 }
154 if(!file_count) { //shall we try a subdir?
155 QStringList dirs = Option::projfile::project_dirs;
156 if(Option::projfile::do_pwd)
157 dirs.prepend(".");
158 const QString out_file = fileFixify(Option::output.name());
159 for(QStringList::Iterator pd = dirs.begin(); pd != dirs.end(); pd++) {
160 if(QFile::exists((*pd))) {
161 QString newdir = (*pd);
162 QFileInfo fi(newdir);
163 if(fi.isDir()) {
164 newdir = fileFixify(newdir);
165 QStringList &subdirs = v["SUBDIRS"];
166 if(QFile::exists(fi.filePath() + QDir::separator() + fi.fileName() + ".pro") &&
167 !subdirs.contains(newdir)) {
168 subdirs.append(newdir);
169 } else {
170 QDir d(newdir, "*.pro");
171 d.setFilter(QDir::Files);
172 for(int i = 0; i < (int)d.count(); i++) {
173 QString nd = newdir;
174 if(nd == ".")
175 nd = "";
176 else if(!nd.isEmpty() && !nd.endsWith(QString(QChar(QDir::separator()))))
177 nd += QDir::separator();
178 nd += d[i];
179 fileFixify(nd);
180 if(d[i] != "." && d[i] != ".." && !subdirs.contains(nd) && !out_file.endsWith(nd))
181 subdirs.append(nd);
182 }
183 }
184 if(Option::projfile::do_recursive) {
185 QDir d(newdir);
186 d.setFilter(QDir::Dirs);
187 for(int i = 0; i < (int)d.count(); i++) {
188 QString nd = fileFixify(newdir + QDir::separator() + d[i]);
189 if(d[i] != "." && d[i] != ".." && !dirs.contains(nd))
190 dirs.append(nd);
191 }
192 }
193 }
194 } else { //regexp
195 QString regx = (*pd), dir;
196 int s = regx.findRev(Option::dir_sep);
197 if(s != -1) {
198 dir = regx.left(s+1);
199 regx = regx.right(regx.length() - (s+1));
200 }
201 QDir d(dir, regx);
202 d.setFilter(QDir::Dirs);
203 QStringList &subdirs = v["SUBDIRS"];
204 for(int i = 0; i < (int)d.count(); i++) {
205 QString newdir(dir + d[i]);
206 QFileInfo fi(newdir);
207 if(fi.fileName() != "." && fi.fileName() != "..") {
208 newdir = fileFixify(newdir);
209 if(QFile::exists(fi.filePath() + QDir::separator() + fi.fileName() + ".pro") &&
210 !subdirs.contains(newdir)) {
211 subdirs.append(newdir);
212 } else {
213 QDir d(newdir, "*.pro");
214 d.setFilter(QDir::Files);
215 for(int i = 0; i < (int)d.count(); i++) {
216 QString nd = newdir + QDir::separator() + d[i];
217 fileFixify(nd);
218 if(d[i] != "." && d[i] != ".." && !subdirs.contains(nd)) {
219 if(newdir + d[i] != Option::output_dir + Option::output.name())
220 subdirs.append(nd);
221 }
222 }
223 }
224 if(Option::projfile::do_recursive && !dirs.contains(newdir))
225 dirs.append(newdir);
226 }
227 }
228 }
229 }
230 v["TEMPLATE_ASSIGN"] = "subdirs";
231 return;
232 }
233
234 QPtrList<MakefileDependDir> deplist;
235 deplist.setAutoDelete(TRUE);
236 {
237 QStringList &d = v["DEPENDPATH"];
238 for(QStringList::Iterator it = d.begin(); it != d.end(); ++it) {
239 QString r = (*it), l = Option::fixPathToLocalOS((*it));
240 deplist.append(new MakefileDependDir(r, l));
241 }
242 }
243 QStringList &h = v["HEADERS"];
244 bool no_qt_files = TRUE;
245 QString srcs[] = { "SOURCES", "YACCSOURCES", "LEXSOURCES", "INTERFACES", QString::null };
246 for(int i = 0; !srcs[i].isNull(); i++) {
247 QStringList &l = v[srcs[i]];
248 for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it) {
249 if(generateDependencies(deplist, (*val_it), TRUE)) {
250 QStringList &tmp = findDependencies((*val_it));
251 if(!tmp.isEmpty()) {
252 for(QStringList::Iterator dep_it = tmp.begin(); dep_it != tmp.end(); ++dep_it) {
253 QString file_dir = (*dep_it).section(Option::dir_sep, 0, -2),
254 file_no_path = (*dep_it).section(Option::dir_sep, -1);
255 if(!file_dir.isEmpty()) {
256 for(MakefileDependDir *mdd = deplist.first(); mdd; mdd = deplist.next()) {
257 if(mdd->local_dir == file_dir && !v["INCLUDEPATH"].contains(mdd->real_dir))
258 v["INCLUDEPATH"] += mdd->real_dir;
259 }
260 }
261 if(no_qt_files && file_no_path.find(QRegExp("^q[a-z_0-9].h$")) != -1)
262 no_qt_files = FALSE;
263 QString h_ext;
264 for(QStringList::Iterator hit = Option::h_ext.begin();
265 hit != Option::h_ext.end(); ++hit) {
266 if((*dep_it).endsWith((*hit))) {
267 h_ext = (*hit);
268 break;
269 }
270 }
271 if(!h_ext.isEmpty()) {
272 if((*dep_it).left(1).lower() == "q") {
273 QString qhdr = (*dep_it).lower();
274 if(file_no_path == "qthread.h")
275 addConfig("thread");
276 }
277 for(QStringList::Iterator cppit = Option::cpp_ext.begin();
278 cppit != Option::cpp_ext.end(); ++cppit) {
279 QString src((*dep_it).left((*dep_it).length() - h_ext.length()) +
280 (*cppit));
281 if(QFile::exists(src)) {
282 bool exists = FALSE;
283 QStringList &srcl = v["SOURCES"];
284 for(QStringList::Iterator src_it = srcl.begin();
285 src_it != srcl.end(); ++src_it) {
286 if((*src_it).lower() == src.lower()) {
287 exists = TRUE;
288 break;
289 }
290 }
291 if(!exists)
292 srcl.append(src);
293 }
294 }
295 } else if((*dep_it).endsWith(Option::lex_ext) &&
296 file_no_path.startsWith(Option::lex_mod)) {
297 addConfig("lex_included");
298 }
299 if(!h.contains((*dep_it))) {
300 if(generateMocList((*dep_it)) && !findMocDestination((*dep_it)).isEmpty())
301 h += (*dep_it);
302 }
303 }
304 }
305 }
306 }
307 }
308 if(h.isEmpty())
309 addConfig("moc", FALSE);
310
311 //if we find a file that matches an forms it needn't be included in the project
312 QStringList &u = v["INTERFACES"];
313 QString no_ui[] = { "SOURCES", "HEADERS", QString::null };
314 {
315 for(int i = 0; !no_ui[i].isNull(); i++) {
316 QStringList &l = v[no_ui[i]];
317 for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ) {
318 bool found = FALSE;
319 for(QStringList::Iterator ui_it = u.begin(); ui_it != u.end(); ++ui_it) {
320 QString s1 = (*val_it).right((*val_it).length() - ((*val_it).findRev(Option::dir_sep) + 1));
321 if(s1.findRev('.') != -1)
322 s1 = s1.left(s1.findRev('.')) + Option::ui_ext;
323 QString u1 = (*ui_it).right((*ui_it).length() - ((*ui_it).findRev(Option::dir_sep) + 1));
324 if(s1 == u1) {
325 found = TRUE;
326 break;
327 }
328 }
329 if(!found && (*val_it).endsWith(Option::cpp_moc_ext))
330 found = TRUE;
331 if(found)
332 val_it = l.remove(val_it);
333 else
334 ++val_it;
335 }
336 }
337 }
338}
339
340
341bool
342ProjectGenerator::writeMakefile(QTextStream &t)
343{
344 t << "######################################################################" << endl;
345 t << "# Automatically generated by qmake (" << qmake_version() << ") " << QDateTime::currentDateTime().toString() << endl;
346 t << "######################################################################" << endl << endl;
347 QStringList::Iterator it;
348 for(it = Option::before_user_vars.begin(); it != Option::before_user_vars.end(); ++it)
349 t << (*it) << endl;
350 t << getWritableVar("TEMPLATE_ASSIGN", FALSE);
351 if(project->first("TEMPLATE_ASSIGN") == "subdirs") {
352 t << endl << "# Directories" << "\n"
353 << getWritableVar("SUBDIRS");
354 } else {
355 t << getWritableVar("TARGET")
356 << getWritableVar("CONFIG", FALSE)
357 << getWritableVar("CONFIG_REMOVE", FALSE)
358 << getWritableVar("DEPENDPATH")
359 << getWritableVar("INCLUDEPATH") << endl;
360
361 t << "# Input" << "\n";
362 t << getWritableVar("HEADERS")
363 << getWritableVar("INTERFACES")
364 << getWritableVar("LEXSOURCES")
365 << getWritableVar("YACCSOURCES")
366 << getWritableVar("SOURCES")
367 << getWritableVar("TRANSLATIONS");
368 }
369 for(it = Option::after_user_vars.begin(); it != Option::after_user_vars.end(); ++it)
370 t << (*it) << endl;
371 return TRUE;
372}
373
374bool
375ProjectGenerator::addConfig(const QString &cfg, bool add)
376{
377 QString where = "CONFIG";
378 if(!add)
379 where = "CONFIG_REMOVE";
380 if(!project->variables()[where].contains(cfg)) {
381 project->variables()[where] += cfg;
382 return TRUE;
383 }
384 return FALSE;
385}
386
387
388bool
389ProjectGenerator::addFile(QString file)
390{
391 file = fileFixify(file, QDir::currentDirPath());
392 QString dir;
393 int s = file.findRev(Option::dir_sep);
394 if(s != -1)
395 dir = file.left(s+1);
396 if(file.mid(dir.length(), Option::h_moc_mod.length()) == Option::h_moc_mod)
397 return FALSE;
398
399 QString where;
400 for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) {
401 if(file.endsWith((*cppit))) {
402 if(QFile::exists(file.left(file.length() - (*cppit).length()) + Option::ui_ext))
403 return FALSE;
404 else
405 where = "SOURCES";
406 break;
407 }
408 }
409 if(where.isEmpty()) {
410 for(QStringList::Iterator hit = Option::h_ext.begin(); hit != Option::h_ext.end(); ++hit) {
411 if(file.endsWith((*hit))) {
412 where = "HEADERS";
413 break;
414 }
415 }
416 }
417 if(where.isEmpty()) {
418 if(file.endsWith(Option::ui_ext))
419 where = "INTERFACES";
420 else if(file.endsWith(".c"))
421 where = "SOURCES";
422 else if(file.endsWith(Option::lex_ext))
423 where = "LEXSOURCES";
424 else if(file.endsWith(Option::yacc_ext))
425 where = "YACCSOURCES";
426 else if(file.endsWith(".ts"))
427 where = "TRANSLATIONS";
428 }
429
430 QString newfile = fileFixify(file);
431 if(!where.isEmpty() && !project->variables()[where].contains(file)) {
432 project->variables()[where] += newfile;
433 return TRUE;
434 }
435 return FALSE;
436}
437
438
439QString
440ProjectGenerator::getWritableVar(const QString &v, bool fixPath)
441{
442 QStringList &vals = project->variables()[v];
443 if(vals.isEmpty())
444 return "";
445
446 QString ret;
447 if(v.endsWith("_REMOVE"))
448 ret = v.left(v.length() - 7) + " -= ";
449 else if(v.endsWith("_ASSIGN"))
450 ret = v.left(v.length() - 7) + " = ";
451 else
452 ret = v + " += ";
453 QString join = vals.join(" ");
454 if(ret.length() + join.length() > 80) {
455 QString spaces;
456 for(unsigned int i = 0; i < ret.length(); i++)
457 spaces += " ";
458 join = vals.join(" \\\n" + spaces);
459 }
460#if 0
461 // ### Commented out for now so that project generation works.
462 // Sam: it had to do with trailing \'s (ie considered continuation lines)
463 if(fixPath)
464 join = join.replace("\\", "/");
465#else
466 Q_UNUSED(fixPath);
467#endif
468 return ret + join + "\n";
469}
470
471bool
472ProjectGenerator::openOutput(QFile &file) const
473{
474 QString outdir;
475 if(!file.name().isEmpty()) {
476 QFileInfo fi(file);
477 if(fi.isDir())
478 outdir = fi.dirPath() + QDir::separator();
479 }
480 if(!outdir.isEmpty() || file.name().isEmpty()) {
481 QString dir = QDir::currentDirPath();
482 int s = dir.findRev('/');
483 if(s != -1)
484 dir = dir.right(dir.length() - (s + 1));
485 file.setName(outdir + dir + ".pro");
486 }
487 return MakefileGenerator::openOutput(file);
488}
Note: See TracBrowser for help on using the repository browser.