QtBase  v6.3.1
projectgenerator.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the qmake application of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 #include "projectgenerator.h"
30 #include "option.h"
31 #include <qdatetime.h>
32 #include <qdir.h>
33 #include <qfile.h>
34 #include <qfileinfo.h>
35 #include <qregularexpression.h>
36 
38 
39 static QString project_builtin_regx() //calculate the builtin regular expression..
40 {
41  QString ret;
42  QStringList builtin_exts;
43  builtin_exts << Option::c_ext << Option::ui_ext << Option::yacc_ext << Option::lex_ext << ".ts" << ".xlf" << ".qrc";
44  builtin_exts += Option::h_ext + Option::cpp_ext;
45  for(int i = 0; i < builtin_exts.size(); ++i) {
46  if(!ret.isEmpty())
47  ret += "; ";
48  ret += QString("*") + builtin_exts[i];
49  }
50  return ret;
51 }
52 
53 void
55 {
56  int file_count = 0;
58 
59  project->loadSpec();
60  project->evaluateFeatureFile("default_pre.prf");
61  project->evaluateFeatureFile("default_post.prf");
63  project->values("CONFIG").clear();
65 
68  if (!Option::globals->user_template_prefix.isEmpty())
69  templ.prepend(Option::globals->user_template_prefix);
70  v["TEMPLATE_ASSIGN"] += templ;
71 
72  //the scary stuff
73  if(project->first("TEMPLATE_ASSIGN") != "subdirs") {
74  QString builtin_regex = project_builtin_regx();
77  if(!v["INCLUDEPATH"].contains("."))
78  v["INCLUDEPATH"] += ".";
79  dirs.prepend(qmake_getpwd());
80  }
81 
82  for(int i = 0; i < dirs.count(); ++i) {
83  QString dir, regex, pd = dirs.at(i);
84  bool add_depend = false;
85  if(exists(pd)) {
86  QFileInfo fi(fileInfo(pd));
87  if(fi.isDir()) {
88  dir = pd;
89  add_depend = true;
90  if(dir.right(1) != Option::dir_sep)
92  if (Option::recursive) {
94  for (int i = 0; i < files.count(); i++)
95  dirs.append(dir + files[i] + QDir::separator() + builtin_regex);
96  }
97  regex = builtin_regex;
98  } else {
99  QString file = pd;
100  int s = file.lastIndexOf(Option::dir_sep);
101  if(s != -1)
102  dir = file.left(s+1);
103  if(addFile(file)) {
104  add_depend = true;
105  file_count++;
106  }
107  }
108  } else { //regexp
109  regex = pd;
110  }
111  if(!regex.isEmpty()) {
112  int s = regex.lastIndexOf(Option::dir_sep);
113  if(s != -1) {
114  dir = regex.left(s+1);
115  regex = regex.right(regex.length() - (s+1));
116  }
117  const QDir d(dir);
118  if (Option::recursive) {
119  QStringList entries = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
120  for (int i = 0; i < entries.count(); i++)
121  dirs.append(dir + entries[i] + QDir::separator() + regex);
122  }
123  QStringList files = d.entryList(QDir::nameFiltersFromString(regex));
124  for(int i = 0; i < (int)files.count(); i++) {
125  QString file = d.absoluteFilePath(files[i]);
126  if (addFile(file)) {
127  add_depend = true;
128  file_count++;
129  }
130  }
131  }
132  if(add_depend && !dir.isEmpty() && !v["DEPENDPATH"].contains(dir, Qt::CaseInsensitive)) {
134  if(fi.absoluteFilePath() != qmake_getpwd())
135  v["DEPENDPATH"] += fileFixify(dir);
136  }
137  }
138  }
139  if(!file_count) { //shall we try a subdir?
142  knownDirs.prepend(".");
143  const QString out_file = fileFixify(Option::output.fileName());
144  for(int i = 0; i < knownDirs.count(); ++i) {
145  QString pd = knownDirs.at(i);
146  if(exists(pd)) {
147  QString newdir = pd;
148  QFileInfo fi(fileInfo(newdir));
149  if(fi.isDir()) {
150  newdir = fileFixify(newdir, FileFixifyFromOutdir);
151  ProStringList &subdirs = v["SUBDIRS"];
153  !subdirs.contains(newdir, Qt::CaseInsensitive)) {
154  subdirs.append(newdir);
155  } else {
156  QStringList profiles = QDir(newdir).entryList(QStringList("*" + Option::pro_ext), QDir::Files);
157  for(int i = 0; i < (int)profiles.count(); i++) {
158  QString nd = newdir;
159  if(nd == ".")
160  nd = "";
161  else if (!nd.isEmpty() && !nd.endsWith(QDir::separator()))
162  nd += QDir::separator();
163  nd += profiles[i];
164  fileFixify(nd);
165  if (!subdirs.contains(nd, Qt::CaseInsensitive) && !out_file.endsWith(nd))
166  subdirs.append(nd);
167  }
168  }
169  if (Option::recursive) {
171  for(int i = 0; i < (int)dirs.count(); i++) {
172  QString nd = fileFixify(newdir + QDir::separator() + dirs[i]);
173  if (!knownDirs.contains(nd, Qt::CaseInsensitive))
174  knownDirs.append(nd);
175  }
176  }
177  }
178  } else { //regexp
179  QString regx = pd, dir;
180  int s = regx.lastIndexOf(Option::dir_sep);
181  if(s != -1) {
182  dir = regx.left(s+1);
183  regx = regx.right(regx.length() - (s+1));
184  }
187  ProStringList &subdirs = v["SUBDIRS"];
188  for(int i = 0; i < (int)files.count(); i++) {
189  QString newdir(dir + files[i]);
190  QFileInfo fi(fileInfo(newdir));
191  {
192  newdir = fileFixify(newdir);
194  !subdirs.contains(newdir)) {
195  subdirs.append(newdir);
196  } else {
197  QStringList profiles = QDir(newdir).entryList(QStringList("*" + Option::pro_ext), QDir::Files);
198  for(int i = 0; i < (int)profiles.count(); i++) {
199  QString nd = newdir + QDir::separator() + files[i];
200  fileFixify(nd);
201  if(files[i] != "." && files[i] != ".." && !subdirs.contains(nd, Qt::CaseInsensitive)) {
202  if(newdir + files[i] != Option::output_dir + Option::output.fileName())
203  subdirs.append(nd);
204  }
205  }
206  }
207  if (Option::recursive && !knownDirs.contains(newdir, Qt::CaseInsensitive))
208  knownDirs.append(newdir);
209  }
210  }
211  }
212  }
213  v["TEMPLATE_ASSIGN"] = ProStringList("subdirs");
214  return;
215  }
216 
217  //setup deplist
219  {
220  const ProStringList &d = v["DEPENDPATH"];
221  for(int i = 0; i < d.size(); ++i)
222  deplist.append(QMakeLocalFileName(d[i].toQString()));
223  }
224  setDependencyPaths(deplist);
225 
226  ProStringList &h = v["HEADERS"];
227  bool no_qt_files = true;
228  static const char *srcs[] = { "SOURCES", "YACCSOURCES", "LEXSOURCES", "FORMS", nullptr };
229  for (int i = 0; srcs[i]; i++) {
230  const ProStringList &l = v[srcs[i]];
233  for(int i = 0; i < l.size(); ++i) {
234  QStringList tmp = QMakeSourceFileInfo::dependencies(l.at(i).toQString());
235  if(!tmp.isEmpty()) {
236  for(int dep_it = 0; dep_it < tmp.size(); ++dep_it) {
237  QString dep = tmp[dep_it];
238  dep = fixPathToQmake(dep);
239  QString file_dir = dep.section(Option::dir_sep, 0, -2),
240  file_no_path = dep.section(Option::dir_sep, -1);
241  if(!file_dir.isEmpty()) {
242  for(int inc_it = 0; inc_it < deplist.size(); ++inc_it) {
243  QMakeLocalFileName inc = deplist[inc_it];
244  if(inc.local() == file_dir && !v["INCLUDEPATH"].contains(inc.real(), Qt::CaseInsensitive))
245  v["INCLUDEPATH"] += inc.real();
246  }
247  }
248  if (no_qt_files && file_no_path.contains(QRegularExpression("^q[a-z_0-9].h$")))
249  no_qt_files = false;
250  QString h_ext;
251  for(int hit = 0; hit < Option::h_ext.size(); ++hit) {
252  if(dep.endsWith(Option::h_ext.at(hit))) {
253  h_ext = Option::h_ext.at(hit);
254  break;
255  }
256  }
257  if(!h_ext.isEmpty()) {
258  for(int cppit = 0; cppit < Option::cpp_ext.size(); ++cppit) {
259  QString src(dep.left(dep.length() - h_ext.length()) +
260  Option::cpp_ext.at(cppit));
261  if(exists(src)) {
262  ProStringList &srcl = v["SOURCES"];
263  if(!srcl.contains(src, Qt::CaseInsensitive))
264  srcl.append(src);
265  }
266  }
267  } else if(dep.endsWith(Option::lex_ext) &&
268  file_no_path.startsWith(Option::lex_mod)) {
269  addConfig("lex_included");
270  }
271  if(!h.contains(dep, Qt::CaseInsensitive))
272  h += dep;
273  }
274  }
275  }
276  }
277 
278  //strip out files that are actually output from internal compilers (ie temporary files)
279  const ProStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
280  for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
281  QString tmp_out = project->first(ProKey(*it + ".output")).toQString();
282  if(tmp_out.isEmpty())
283  continue;
284 
285  ProStringList var_out = project->values(ProKey(*it + ".variable_out"));
286  bool defaults = var_out.isEmpty();
287  for(int i = 0; i < var_out.size(); ++i) {
288  ProString v = var_out.at(i);
289  if(v.startsWith("GENERATED_")) {
290  defaults = true;
291  break;
292  }
293  }
294  if(defaults) {
295  var_out << "SOURCES";
296  var_out << "HEADERS";
297  var_out << "FORMS";
298  }
299  const ProStringList &tmp = project->values(ProKey(*it + ".input"));
300  for (ProStringList::ConstIterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
301  ProStringList &inputs = project->values((*it2).toKey());
302  for (ProStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
303  QString path = replaceExtraCompilerVariables(tmp_out, (*input).toQString(), QString(), NoShell);
304  path = fixPathToQmake(path).section('/', -1);
305  for(int i = 0; i < var_out.size(); ++i) {
306  ProString v = var_out.at(i);
307  ProStringList &list = project->values(v.toKey());
308  for(int src = 0; src < list.size(); ) {
309  if(list[src] == path || list[src].endsWith("/" + path))
310  list.removeAt(src);
311  else
312  ++src;
313  }
314  }
315  }
316  }
317  }
318 }
319 
320 bool
322 {
323  t << "######################################################################" << Qt::endl;
324  t << "# Automatically generated by qmake (" QMAKE_VERSION_STR ") " << QDateTime::currentDateTime().toString() << Qt::endl;
325  t << "######################################################################" << Qt::endl << Qt::endl;
326  if (!Option::globals->extra_cmds[QMakeEvalBefore].isEmpty())
328  t << getWritableVar("TEMPLATE_ASSIGN", false);
329  if(project->first("TEMPLATE_ASSIGN") == "subdirs") {
330  t << Qt::endl << "# Directories" << "\n"
331  << getWritableVar("SUBDIRS");
332  } else {
333  //figure out target
334  QString ofn = QFileInfo(static_cast<QFile *>(t.device())->fileName()).completeBaseName();
335  if (ofn.isEmpty() || ofn == "-")
336  ofn = "unknown";
337  project->values("TARGET_ASSIGN") = ProStringList(ofn);
338 
339  t << getWritableVar("TARGET_ASSIGN")
340  << getWritableVar("CONFIG", false)
341  << getWritableVar("CONFIG_REMOVE", false)
342  << getWritableVar("INCLUDEPATH") << Qt::endl;
343 
344  t << "# You can make your code fail to compile if you use deprecated APIs.\n"
345  "# In order to do so, uncomment the following line.\n"
346  "# Please consult the documentation of the deprecated API in order to know\n"
347  "# how to port your code away from it.\n"
348  "# You can also select to disable deprecated APIs only up to a certain version of Qt.\n"
349  "#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0\n\n";
350 
351  t << "# Input" << "\n";
352  t << getWritableVar("HEADERS")
353  << getWritableVar("FORMS")
354  << getWritableVar("LEXSOURCES")
355  << getWritableVar("YACCSOURCES")
356  << getWritableVar("SOURCES")
357  << getWritableVar("RESOURCES")
358  << getWritableVar("TRANSLATIONS");
359  }
360  if (!Option::globals->extra_cmds[QMakeEvalAfter].isEmpty())
362  return true;
363 }
364 
365 bool
366 ProjectGenerator::addConfig(const QString &cfg, bool add)
367 {
368  ProKey where = "CONFIG";
369  if(!add)
370  where = "CONFIG_REMOVE";
371  if (!project->values(where).contains(cfg)) {
372  project->values(where) += cfg;
373  return true;
374  }
375  return false;
376 }
377 
378 bool
379 ProjectGenerator::addFile(QString file)
380 {
382  QString dir;
383  int s = file.lastIndexOf(Option::dir_sep);
384  if(s != -1)
385  dir = file.left(s+1);
387  return false;
388 
389  ProKey where;
390  for(int cppit = 0; cppit < Option::cpp_ext.size(); ++cppit) {
391  if(file.endsWith(Option::cpp_ext[cppit])) {
392  where = "SOURCES";
393  break;
394  }
395  }
396  if(where.isEmpty()) {
397  for(int hit = 0; hit < Option::h_ext.size(); ++hit)
398  if(file.endsWith(Option::h_ext.at(hit))) {
399  where = "HEADERS";
400  break;
401  }
402  }
403  if(where.isEmpty()) {
404  for(int cit = 0; cit < Option::c_ext.size(); ++cit) {
405  if(file.endsWith(Option::c_ext[cit])) {
406  where = "SOURCES";
407  break;
408  }
409  }
410  }
411  if(where.isEmpty()) {
412  if(file.endsWith(Option::ui_ext))
413  where = "FORMS";
414  else if(file.endsWith(Option::lex_ext))
415  where = "LEXSOURCES";
416  else if(file.endsWith(Option::yacc_ext))
417  where = "YACCSOURCES";
418  else if(file.endsWith(".ts") || file.endsWith(".xlf"))
419  where = "TRANSLATIONS";
420  else if(file.endsWith(".qrc"))
421  where = "RESOURCES";
422  }
423 
424  QString newfile = fixPathToQmake(fileFixify(file));
425 
426  ProStringList &endList = project->values(where);
427  if(!endList.contains(newfile, Qt::CaseInsensitive)) {
428  endList += newfile;
429  return true;
430  }
431  return false;
432 }
433 
434 QString
435 ProjectGenerator::getWritableVar(const char *vk, bool)
436 {
437  const ProKey v(vk);
438  ProStringList &vals = project->values(v);
439  if(vals.isEmpty())
440  return "";
441 
442  // If values contain spaces, ensure that they are quoted
443  for (ProStringList::iterator it = vals.begin(); it != vals.end(); ++it) {
444  if ((*it).contains(' ') && !(*it).startsWith(' '))
445  *it = "\"" + *it + "\"";
446  }
447 
448  QString ret;
449  if(v.endsWith("_REMOVE"))
450  ret = v.left(v.length() - 7) + " -= ";
451  else if(v.endsWith("_ASSIGN"))
452  ret = v.left(v.length() - 7) + " = ";
453  else
454  ret = v + " += ";
455  QString join = vals.join(' ');
456  if(ret.length() + join.length() > 80) {
457  QString spaces;
458  for(int i = 0; i < ret.length(); i++)
459  spaces += " ";
460  join = vals.join(" \\\n" + spaces);
461  }
462  return ret + join + "\n";
463 }
464 
465 bool
467 {
469  if (!fileName.endsWith(Option::pro_ext)) {
470  if (fileName.isEmpty())
473  }
475 }
476 
477 
478 QString
479 ProjectGenerator::fixPathToQmake(const QString &file)
480 {
481  QString ret = file;
482  if(Option::dir_sep != QLatin1String("/"))
483  ret.replace(Option::dir_sep, QLatin1String("/"));
484  return ret;
485 }
486 
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
bool exists(QString file) const
Definition: makefile.h:288
QFileInfo fileInfo(QString file) const
Definition: makefile.cpp:2827
void verifyCompilers()
Definition: makefile.cpp:98
virtual QString replaceExtraCompilerVariables(const QString &, const QStringList &, const QStringList &, ReplaceFor forShell)
Definition: makefile.cpp:1596
QString fileFixify(const QString &file, FileFixifyTypes fix=FileFixifyDefault, bool canon=true) const
Definition: makefile.cpp:2982
virtual bool openOutput(QFile &, const QString &build) const
Definition: makefile.cpp:3214
QMakeProject * project
Definition: makefile.h:139
bool isEmpty() const
Definition: proitems.h:121
QString toQString() const
Definition: proitems.cpp:153
QString join(const ProString &sep) const
Definition: proitems.cpp:359
bool contains(const ProString &str, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition: proitems.cpp:434
bool openOutput(QFile &, const QString &) const override
void init() override
bool writeMakefile(QTextStream &) override
static QDateTime currentDateTime()
The QDir class provides access to directory structures and their contents.
Definition: qdir.h:55
QStringList entryList(Filters filters=NoFilter, SortFlags sort=NoSort) const
Definition: qdir.cpp:1334
static QStringList nameFiltersFromString(const QString &nameFilter)
Definition: qdir.cpp:2395
static QChar separator()
Definition: qdir.h:234
@ Files
Definition: qdir.h:58
@ NoDotAndDotDot
Definition: qdir.h:79
@ Dirs
Definition: qdir.h:57
The QFile class provides an interface for reading from and writing to files.
Definition: qfile.h:94
void setFileName(const QString &name)
Definition: qfile.cpp:327
QString fileName() const override
Definition: qfile.cpp:302
The QFileInfo class provides system-independent file information.
Definition: qfileinfo.h:57
QString fileName() const
Definition: qfileinfo.cpp:772
QString absoluteFilePath() const
Definition: qfileinfo.cpp:556
QString completeBaseName() const
Definition: qfileinfo.cpp:835
bool isDir() const
Definition: qfileinfo.cpp:1048
QString filePath() const
Definition: qfileinfo.cpp:753
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal.
Definition: qstring.h:84
qsizetype size() const noexcept
Definition: qlist.h:414
bool isEmpty() const noexcept
Definition: qlist.h:418
iterator Iterator
Definition: qlist.h:279
iterator end()
Definition: qlist.h:624
const_reference at(qsizetype i) const noexcept
Definition: qlist.h:457
iterator begin()
Definition: qlist.h:623
void append(parameter_type t)
Definition: qlist.h:469
void clear()
Definition: qlist.h:445
const_iterator ConstIterator
Definition: qlist.h:280
QString user_template
Definition: qmakeglobals.h:115
QString extra_cmds[4]
Definition: qmakeglobals.h:116
const QString & local() const
const QString & real() const
Definition: makefiledeps.h:52
VisitReturn evaluateConfigFeatures()
ProString first(const ProKey &variableName) const
ProStringList & values(const ProKey &v)
Definition: project.h:63
VisitReturn evaluateFeatureFile(const QString &fileName, bool silent=false)
const ProValueMap & variables() const
Definition: project.h:65
QStringList dependencies(const QString &file)
void addSourceFiles(const ProStringList &, uchar seek, SourceFileType type=TYPE_C)
void setDependencyPaths(const QList< QMakeLocalFileName > &)
The QRegularExpression class provides pattern matching using regular expressions.
The QString class provides a Unicode character string.
Definition: qstring.h:388
QString right(qsizetype n) const
Definition: qstring.cpp:4970
QString & prepend(QChar c)
Definition: qstring.h:656
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition: qstring.h:514
QString section(QChar sep, qsizetype start, qsizetype end=-1, SectionFlags flags=SectionDefault) const
Definition: qstring.h:1272
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition: qstring.cpp:5143
bool isEmpty() const
Definition: qstring.h:1216
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition: qstring.h:1353
QString left(qsizetype n) const
Definition: qstring.cpp:4951
qsizetype length() const
Definition: qstring.h:415
The QStringList class provides a list of strings.
The QTextStream class provides a convenient interface for reading and writing text.
Definition: qtextstream.h:62
set contains("Julia")
typename C::iterator iterator
@ CaseInsensitive
Definition: qnamespace.h:1283
QTextStream & endl(QTextStream &stream)
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool endsWith(QByteArrayView haystack, QByteArrayView needle) noexcept
str spaces(int indent)
Definition: pro2cmake.py:794
#define QString()
Definition: parse-defines.h:51
QList< QString > QStringList
Definition: qcontainerfwd.h:64
QString qmake_getpwd()
Definition: main.cpp:434
@ QMakeEvalBefore
Definition: qmakeglobals.h:53
@ QMakeEvalAfter
Definition: qmakeglobals.h:53
GLenum type
Definition: qopengl.h:270
GLsizei const GLfloat * v
[13]
GLenum src
GLfloat GLfloat GLfloat GLfloat h
GLdouble GLdouble t
[9]
Definition: qopenglext.h:243
GLsizei const GLchar *const * path
Definition: qopenglext.h:4283
GLdouble s
[6]
Definition: qopenglext.h:235
GLenum GLenum GLenum input
Definition: qopenglext.h:10816
QFile file
[0]
QFileInfo fi("c:/temp/foo")
[newstuff]
QFile defaults(defaultsPath)
QString dir
[11]
QStringList files
[8]
QStringList::Iterator it
QStringList list
[0]
static bool do_pwd
Definition: option.h:180
static QStringList project_dirs
Definition: option.h:181
static QMakeGlobals * globals
Definition: option.h:73
static QStringList cpp_ext
Definition: option.h:85
static QStringList h_ext
Definition: option.h:84
static QString lex_mod
Definition: option.h:94
static QStringList c_ext
Definition: option.h:86
static QString pro_ext
Definition: option.h:97
static QString lex_ext
Definition: option.h:91
static QString dir_sep
Definition: option.h:96
static QString yacc_ext
Definition: option.h:92
static bool recursive
Definition: option.h:171
static QString output_dir
Definition: option.h:168
static QString h_moc_mod
Definition: option.h:93
static QString ui_ext
Definition: option.h:83
static QFile output
Definition: option.h:167
static bool postProcessProject(QMakeProject *)
Definition: option.cpp:456
void build(const QString &name)
QString toQString(const T &t)
void add(int &result, const int &sum)