1 /****************************************************************************
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the qmake application of the Qt Toolkit.
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
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "projectgenerator.h"
44 #include <qdatetime.h>
47 #include <qfileinfo.h>
52 QString
project_builtin_regx() //calculate the builtin regular expression..
55 QStringList builtin_exts
;
56 builtin_exts
<< Option::c_ext
<< Option::ui_ext
<< Option::yacc_ext
<< Option::lex_ext
<< ".ts" << ".xlf" << ".qrc";
57 builtin_exts
+= Option::h_ext
+ Option::cpp_ext
;
58 for(int i
= 0; i
< builtin_exts
.size(); ++i
) {
61 ret
+= QString("*") + builtin_exts
[i
];
66 ProjectGenerator::ProjectGenerator() : MakefileGenerator(), init_flag(false)
71 ProjectGenerator::init()
79 project
->read(QMakeProject::ReadFeatures
);
80 project
->variables()["CONFIG"].clear();
82 QMap
<QString
, QStringList
> &v
= project
->variables();
83 QString templ
= Option::user_template
.isEmpty() ? QString("app") : Option::user_template
;
84 if(!Option::user_template_prefix
.isEmpty())
85 templ
.prepend(Option::user_template_prefix
);
86 v
["TEMPLATE_ASSIGN"] += templ
;
89 if(Option::output
.fileName() == "-")
90 v
["TARGET_ASSIGN"] = QStringList("unknown");
92 v
["TARGET_ASSIGN"] = QStringList(QFileInfo(Option::output
).baseName());
95 if(project
->first("TEMPLATE_ASSIGN") != "subdirs") {
96 QString builtin_regex
= project_builtin_regx();
97 QStringList dirs
= Option::projfile::project_dirs
;
98 if(Option::projfile::do_pwd
) {
99 if(!v
["INCLUDEPATH"].contains("."))
100 v
["INCLUDEPATH"] += ".";
101 dirs
.prepend(qmake_getpwd());
104 for(int i
= 0; i
< dirs
.count(); ++i
) {
105 QString dir
, regex
, pd
= dirs
.at(i
);
106 bool add_depend
= false;
108 QFileInfo
fi(fileInfo(pd
));
112 if(dir
.right(1) != Option::dir_sep
)
113 dir
+= Option::dir_sep
;
114 if(Option::recursive
) {
115 QStringList files
= QDir(dir
).entryList(QDir::Files
);
116 for(int i
= 0; i
< (int)files
.count(); i
++) {
117 if(files
[i
] != "." && files
[i
] != "..")
118 dirs
.append(dir
+ files
[i
] + QDir::separator() + builtin_regex
);
121 regex
= builtin_regex
;
124 int s
= file
.lastIndexOf(Option::dir_sep
);
126 dir
= file
.left(s
+1);
135 if(!regex
.isEmpty()) {
136 int s
= regex
.lastIndexOf(Option::dir_sep
);
138 dir
= regex
.left(s
+1);
139 regex
= regex
.right(regex
.length() - (s
+1));
141 if(Option::recursive
) {
142 QStringList entries
= QDir(dir
).entryList(QDir::Dirs
);
143 for(int i
= 0; i
< (int)entries
.count(); i
++) {
144 if(entries
[i
] != "." && entries
[i
] != "..") {
145 dirs
.append(dir
+ entries
[i
] + QDir::separator() + regex
);
149 QStringList files
= QDir(dir
).entryList(QDir::nameFiltersFromString(regex
));
150 for(int i
= 0; i
< (int)files
.count(); i
++) {
151 QString file
= dir
+ files
[i
];
158 if(add_depend
&& !dir
.isEmpty() && !v
["DEPENDPATH"].contains(dir
, Qt::CaseInsensitive
)) {
159 QFileInfo
fi(fileInfo(dir
));
160 if(fi
.absoluteFilePath() != qmake_getpwd())
161 v
["DEPENDPATH"] += fileFixify(dir
);
165 if(!file_count
) { //shall we try a subdir?
166 QStringList knownDirs
= Option::projfile::project_dirs
;
167 if(Option::projfile::do_pwd
)
168 knownDirs
.prepend(".");
169 const QString out_file
= fileFixify(Option::output
.fileName());
170 for(int i
= 0; i
< knownDirs
.count(); ++i
) {
171 QString pd
= knownDirs
.at(i
);
174 QFileInfo
fi(fileInfo(newdir
));
176 newdir
= fileFixify(newdir
);
177 QStringList
&subdirs
= v
["SUBDIRS"];
178 if(exists(fi
.filePath() + QDir::separator() + fi
.fileName() + Option::pro_ext
) &&
179 !subdirs
.contains(newdir
, Qt::CaseInsensitive
)) {
180 subdirs
.append(newdir
);
182 QStringList profiles
= QDir(newdir
).entryList(QStringList("*" + Option::pro_ext
), QDir::Files
);
183 for(int i
= 0; i
< (int)profiles
.count(); i
++) {
187 else if(!nd
.isEmpty() && !nd
.endsWith(QString(QChar(QDir::separator()))))
188 nd
+= QDir::separator();
191 if(profiles
[i
] != "." && profiles
[i
] != ".." &&
192 !subdirs
.contains(nd
, Qt::CaseInsensitive
) && !out_file
.endsWith(nd
))
196 if(Option::recursive
) {
197 QStringList dirs
= QDir(newdir
).entryList(QDir::Dirs
);
198 for(int i
= 0; i
< (int)dirs
.count(); i
++) {
199 QString nd
= fileFixify(newdir
+ QDir::separator() + dirs
[i
]);
200 if(dirs
[i
] != "." && dirs
[i
] != ".." && !knownDirs
.contains(nd
, Qt::CaseInsensitive
))
201 knownDirs
.append(nd
);
206 QString regx
= pd
, dir
;
207 int s
= regx
.lastIndexOf(Option::dir_sep
);
209 dir
= regx
.left(s
+1);
210 regx
= regx
.right(regx
.length() - (s
+1));
212 QStringList files
= QDir(dir
).entryList(QDir::nameFiltersFromString(regx
), QDir::Dirs
);
213 QStringList
&subdirs
= v
["SUBDIRS"];
214 for(int i
= 0; i
< (int)files
.count(); i
++) {
215 QString
newdir(dir
+ files
[i
]);
216 QFileInfo
fi(fileInfo(newdir
));
217 if(fi
.fileName() != "." && fi
.fileName() != "..") {
218 newdir
= fileFixify(newdir
);
219 if(exists(fi
.filePath() + QDir::separator() + fi
.fileName() + Option::pro_ext
) &&
220 !subdirs
.contains(newdir
)) {
221 subdirs
.append(newdir
);
223 QStringList profiles
= QDir(newdir
).entryList(QStringList("*" + Option::pro_ext
), QDir::Files
);
224 for(int i
= 0; i
< (int)profiles
.count(); i
++) {
225 QString nd
= newdir
+ QDir::separator() + files
[i
];
227 if(files
[i
] != "." && files
[i
] != ".." && !subdirs
.contains(nd
, Qt::CaseInsensitive
)) {
228 if(newdir
+ files
[i
] != Option::output_dir
+ Option::output
.fileName())
233 if(Option::recursive
&& !knownDirs
.contains(newdir
, Qt::CaseInsensitive
))
234 knownDirs
.append(newdir
);
239 v
["TEMPLATE_ASSIGN"] = QStringList("subdirs");
244 QList
<QMakeLocalFileName
> deplist
;
246 const QStringList
&d
= v
["DEPENDPATH"];
247 for(int i
= 0; i
< d
.size(); ++i
)
248 deplist
.append(QMakeLocalFileName(d
[i
]));
250 setDependencyPaths(deplist
);
252 QStringList
&h
= v
["HEADERS"];
253 bool no_qt_files
= true;
254 QString srcs
[] = { "SOURCES", "YACCSOURCES", "LEXSOURCES", "FORMS", QString() };
255 for(int i
= 0; !srcs
[i
].isNull(); i
++) {
256 const QStringList
&l
= v
[srcs
[i
]];
257 QMakeSourceFileInfo::SourceFileType type
= QMakeSourceFileInfo::TYPE_C
;
258 QMakeSourceFileInfo::addSourceFiles(l
, QMakeSourceFileInfo::SEEK_DEPS
, type
);
259 for(int i
= 0; i
< l
.size(); ++i
) {
260 QStringList tmp
= QMakeSourceFileInfo::dependencies(l
[i
]);
262 for(int dep_it
= 0; dep_it
< tmp
.size(); ++dep_it
) {
263 QString dep
= tmp
[dep_it
];
264 dep
= fixPathToQmake(dep
);
265 QString file_dir
= dep
.section(Option::dir_sep
, 0, -2),
266 file_no_path
= dep
.section(Option::dir_sep
, -1);
267 if(!file_dir
.isEmpty()) {
268 for(int inc_it
= 0; inc_it
< deplist
.size(); ++inc_it
) {
269 QMakeLocalFileName inc
= deplist
[inc_it
];
270 if(inc
.local() == file_dir
&& !v
["INCLUDEPATH"].contains(inc
.real(), Qt::CaseInsensitive
))
271 v
["INCLUDEPATH"] += inc
.real();
274 if(no_qt_files
&& file_no_path
.indexOf(QRegExp("^q[a-z_0-9].h$")) != -1)
277 for(int hit
= 0; hit
< Option::h_ext
.size(); ++hit
) {
278 if(dep
.endsWith(Option::h_ext
.at(hit
))) {
279 h_ext
= Option::h_ext
.at(hit
);
283 if(!h_ext
.isEmpty()) {
284 for(int cppit
= 0; cppit
< Option::cpp_ext
.size(); ++cppit
) {
285 QString
src(dep
.left(dep
.length() - h_ext
.length()) +
286 Option::cpp_ext
.at(cppit
));
288 QStringList
&srcl
= v
["SOURCES"];
289 if(!srcl
.contains(src
, Qt::CaseInsensitive
))
293 } else if(dep
.endsWith(Option::lex_ext
) &&
294 file_no_path
.startsWith(Option::lex_mod
)) {
295 addConfig("lex_included");
297 if(!h
.contains(dep
, Qt::CaseInsensitive
))
304 //strip out files that are actually output from internal compilers (ie temporary files)
305 const QStringList
&quc
= project
->variables()["QMAKE_EXTRA_COMPILERS"];
306 for(QStringList::ConstIterator it
= quc
.begin(); it
!= quc
.end(); ++it
) {
307 QString tmp_out
= project
->variables()[(*it
) + ".output"].first();
308 if(tmp_out
.isEmpty())
311 QStringList var_out
= project
->variables()[(*it
) + ".variable_out"];
312 bool defaults
= var_out
.isEmpty();
313 for(int i
= 0; i
< var_out
.size(); ++i
) {
314 QString v
= var_out
.at(i
);
315 if(v
.startsWith("GENERATED_")) {
321 var_out
<< "SOURCES";
322 var_out
<< "HEADERS";
325 const QStringList
&tmp
= project
->variables()[(*it
) + ".input"];
326 for(QStringList::ConstIterator it2
= tmp
.begin(); it2
!= tmp
.end(); ++it2
) {
327 QStringList
&inputs
= project
->variables()[(*it2
)];
328 for(QStringList::Iterator input
= inputs
.begin(); input
!= inputs
.end(); ++input
) {
329 QString path
= replaceExtraCompilerVariables(tmp_out
, (*input
), QString());
330 path
= fixPathToQmake(path
).section('/', -1);
331 for(int i
= 0; i
< var_out
.size(); ++i
) {
332 QString v
= var_out
.at(i
);
333 QStringList
&list
= project
->variables()[v
];
334 for(int src
= 0; src
< list
.size(); ) {
335 if(list
[src
] == path
|| list
[src
].endsWith("/" + path
))
347 ProjectGenerator::writeMakefile(QTextStream
&t
)
349 t
<< "######################################################################" << endl
;
350 t
<< "# Automatically generated by qmake (" << qmake_version() << ") " << QDateTime::currentDateTime().toString() << endl
;
351 t
<< "######################################################################" << endl
<< endl
;
352 if(!Option::user_configs
.isEmpty())
353 t
<< "CONFIG += " << Option::user_configs
.join(" ") << endl
;
355 for(i
= 0; i
< Option::before_user_vars
.size(); ++i
)
356 t
<< Option::before_user_vars
[i
] << endl
;
357 t
<< getWritableVar("TEMPLATE_ASSIGN", false);
358 if(project
->first("TEMPLATE_ASSIGN") == "subdirs") {
359 t
<< endl
<< "# Directories" << "\n"
360 << getWritableVar("SUBDIRS");
362 t
<< getWritableVar("TARGET_ASSIGN")
363 << getWritableVar("CONFIG", false)
364 << getWritableVar("CONFIG_REMOVE", false)
365 << getWritableVar("DEPENDPATH")
366 << getWritableVar("INCLUDEPATH") << endl
;
368 t
<< "# Input" << "\n";
369 t
<< getWritableVar("HEADERS")
370 << getWritableVar("FORMS")
371 << getWritableVar("LEXSOURCES")
372 << getWritableVar("YACCSOURCES")
373 << getWritableVar("SOURCES")
374 << getWritableVar("RESOURCES")
375 << getWritableVar("TRANSLATIONS");
377 for(i
= 0; i
< Option::after_user_vars
.size(); ++i
)
378 t
<< Option::after_user_vars
[i
] << endl
;
383 ProjectGenerator::addConfig(const QString
&cfg
, bool add
)
385 QString where
= "CONFIG";
387 where
= "CONFIG_REMOVE";
388 if(!project
->variables()[where
].contains(cfg
)) {
389 project
->variables()[where
] += cfg
;
396 ProjectGenerator::addFile(QString file
)
398 file
= fileFixify(file
, qmake_getpwd());
400 int s
= file
.lastIndexOf(Option::dir_sep
);
402 dir
= file
.left(s
+1);
403 if(file
.mid(dir
.length(), Option::h_moc_mod
.length()) == Option::h_moc_mod
)
407 for(int cppit
= 0; cppit
< Option::cpp_ext
.size(); ++cppit
) {
408 if(file
.endsWith(Option::cpp_ext
[cppit
])) {
413 if(where
.isEmpty()) {
414 for(int hit
= 0; hit
< Option::h_ext
.size(); ++hit
)
415 if(file
.endsWith(Option::h_ext
.at(hit
))) {
420 if(where
.isEmpty()) {
421 for(int cit
= 0; cit
< Option::c_ext
.size(); ++cit
) {
422 if(file
.endsWith(Option::c_ext
[cit
])) {
428 if(where
.isEmpty()) {
429 if(file
.endsWith(Option::ui_ext
))
431 else if(file
.endsWith(Option::lex_ext
))
432 where
= "LEXSOURCES";
433 else if(file
.endsWith(Option::yacc_ext
))
434 where
= "YACCSOURCES";
435 else if(file
.endsWith(".ts") || file
.endsWith(".xlf"))
436 where
= "TRANSLATIONS";
437 else if(file
.endsWith(".qrc"))
441 QString newfile
= fixPathToQmake(fileFixify(file
));
443 QStringList
&endList
= project
->variables()[where
];
444 if(!endList
.contains(newfile
, Qt::CaseInsensitive
)) {
452 ProjectGenerator::getWritableVar(const QString
&v
, bool)
454 QStringList
&vals
= project
->variables()[v
];
458 // If values contain spaces, ensure that they are quoted
459 for(QStringList::iterator it
= vals
.begin(); it
!= vals
.end(); ++it
) {
460 if ((*it
).contains(' ') && !(*it
).startsWith(' '))
461 *it
= '\"' + *it
+ '\"';
465 if(v
.endsWith("_REMOVE"))
466 ret
= v
.left(v
.length() - 7) + " -= ";
467 else if(v
.endsWith("_ASSIGN"))
468 ret
= v
.left(v
.length() - 7) + " = ";
471 QString join
= vals
.join(" ");
472 if(ret
.length() + join
.length() > 80) {
474 for(int i
= 0; i
< ret
.length(); i
++)
476 join
= vals
.join(" \\\n" + spaces
);
478 return ret
+ join
+ "\n";
482 ProjectGenerator::openOutput(QFile
&file
, const QString
&build
) const
485 if(!file
.fileName().isEmpty()) {
486 QFileInfo
fi(fileInfo(file
.fileName()));
488 outdir
= fi
.path() + QDir::separator();
490 if(!outdir
.isEmpty() || file
.fileName().isEmpty()) {
491 QString dir
= qmake_getpwd();
492 int s
= dir
.lastIndexOf('/');
494 dir
= dir
.right(dir
.length() - (s
+ 1));
495 file
.setFileName(outdir
+ dir
+ Option::pro_ext
);
497 return MakefileGenerator::openOutput(file
, build
);
502 ProjectGenerator::fixPathToQmake(const QString
&file
)
505 if(Option::dir_sep
!= QLatin1String("/"))
506 ret
= ret
.replace(Option::dir_sep
, QLatin1String("/"));