Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the qmake application of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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 Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "metamakefile.h"
43#include "qregexp.h"
44#include "qdir.h"
45#include "qdebug.h"
46#include "makefile.h"
47#include "project.h"
48#include "cachekeys.h"
49
50#define BUILDSMETATYPE 1
51#define SUBDIRSMETATYPE 2
52
53QT_BEGIN_NAMESPACE
54
55MetaMakefileGenerator::~MetaMakefileGenerator()
56{
57 if(own_project)
58 delete project;
59}
60
61#ifndef QT_QMAKE_PARSER_ONLY
62
63class BuildsMetaMakefileGenerator : public MetaMakefileGenerator
64{
65private:
66 bool init_flag;
67 struct Build {
68 QString name, build;
69 MakefileGenerator *makefile;
70 };
71 QList<Build *> makefiles;
72 void clearBuilds();
73 MakefileGenerator *processBuild(const QString &);
74
75public:
76
77 BuildsMetaMakefileGenerator(QMakeProject *p, const QString &n, bool op) : MetaMakefileGenerator(p, n, op), init_flag(false) { }
78 virtual ~BuildsMetaMakefileGenerator() { clearBuilds(); }
79
80 virtual bool init();
81 virtual int type() const { return BUILDSMETATYPE; }
82 virtual bool write(const QString &);
83};
84
85void
86BuildsMetaMakefileGenerator::clearBuilds()
87{
88 for(int i = 0; i < makefiles.count(); i++) {
89 Build *build = makefiles[i];
90 if(QMakeProject *p = build->makefile->projectFile()) {
91 if(p != project)
92 delete p;
93 }
94 delete build->makefile;
95 delete build;
96 }
97 makefiles.clear();
98}
99
100bool
101BuildsMetaMakefileGenerator::init()
102{
103 if(init_flag)
104 return false;
105 init_flag = true;
106
107 const QStringList &builds = project->variables()["BUILDS"];
108 bool use_single_build = builds.isEmpty();
109 if(builds.count() > 1 && Option::output.fileName() == "-") {
110 use_single_build = true;
111 warn_msg(WarnLogic, "Cannot direct to stdout when using multiple BUILDS.");
112 } else if(0 && !use_single_build && project->first("TEMPLATE") == "subdirs") {
113 use_single_build = true;
114 warn_msg(WarnLogic, "Cannot specify multiple builds with TEMPLATE subdirs.");
115 }
116 if(!use_single_build) {
117 for(int i = 0; i < builds.count(); i++) {
118 QString build = builds[i];
119 MakefileGenerator *makefile = processBuild(build);
120 if(!makefile)
121 return false;
122 if(!makefile->supportsMetaBuild()) {
123 warn_msg(WarnLogic, "QMAKESPEC does not support multiple BUILDS.");
124 clearBuilds();
125 use_single_build = true;
126 break;
127 } else {
128 Build *b = new Build;
129 b->name = name;
130 if(builds.count() != 1)
131 b->build += build;
132 b->makefile = makefile;
133 makefiles += b;
134 }
135 }
136 }
137 if(use_single_build) {
138 Build *build = new Build;
139 build->name = name;
140 build->makefile = createMakefileGenerator(project, false);
141 if (build->makefile){
142 makefiles += build;
143 }else {
144 delete build;
145 return false;
146 }
147 }
148 return true;
149}
150
151bool
152BuildsMetaMakefileGenerator::write(const QString &oldpwd)
153{
154 Build *glue = 0;
155 if(!makefiles.isEmpty() && !makefiles.first()->build.isNull()) {
156 glue = new Build;
157 glue->name = name;
158 glue->makefile = createMakefileGenerator(project, true);
159 makefiles += glue;
160 }
161
162 bool ret = true;
163 const QString &output_name = Option::output.fileName();
164 for(int i = 0; ret && i < makefiles.count(); i++) {
165 Option::output.setFileName(output_name);
166 Build *build = makefiles[i];
167
168 bool using_stdout = false;
169 if(build->makefile && (Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
170 Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT)
171 && (!build->makefile->supportsMergedBuilds()
172 || (build->makefile->supportsMergedBuilds() && (!glue || build == glue)))) {
173 //open output
174 if(!(Option::output.isOpen())) {
175 if(Option::output.fileName() == "-") {
176 Option::output.setFileName("");
177 Option::output_dir = qmake_getpwd();
178 Option::output.open(stdout, QIODevice::WriteOnly | QIODevice::Text);
179 using_stdout = true;
180 } else {
181 if(Option::output.fileName().isEmpty() &&
182 Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE)
183 Option::output.setFileName(project->first("QMAKE_MAKEFILE"));
184 Option::output_dir = oldpwd;
185 QString build_name = build->name;
186 if(!build->build.isEmpty()) {
187 if(!build_name.isEmpty())
188 build_name += ".";
189 build_name += build->build;
190 }
191 if(!build->makefile->openOutput(Option::output, build_name)) {
192 fprintf(stderr, "Failure to open file: %s\n",
193 Option::output.fileName().isEmpty() ? "(stdout)" :
194 Option::output.fileName().toLatin1().constData());
195 return false;
196 }
197 }
198 }
199 } else {
200 using_stdout = true; //kind of..
201 }
202
203 if(!build->makefile) {
204 ret = false;
205 } else if(build == glue) {
206 ret = build->makefile->writeProjectMakefile();
207 } else {
208 ret = build->makefile->write();
209 if (glue && glue->makefile->supportsMergedBuilds())
210 ret = glue->makefile->mergeBuildProject(build->makefile);
211 }
212 if(!using_stdout) {
213 Option::output.close();
214 if(!ret)
215 Option::output.remove();
216 }
217
218 // debugging
219 if(Option::debug_level) {
220 debug_msg(1, "Dumping all variables:");
221 QMap<QString, QStringList> &vars = project->variables();
222 for(QMap<QString, QStringList>::Iterator it = vars.begin(); it != vars.end(); ++it) {
223 if(!it.key().startsWith(".") && !it.value().isEmpty())
224 debug_msg(1, "%s === %s", it.key().toLatin1().constData(),
225 it.value().join(" :: ").toLatin1().constData());
226 }
227 }
228 }
229 return ret;
230}
231
232MakefileGenerator
233*BuildsMetaMakefileGenerator::processBuild(const QString &build)
234{
235 if(project) {
236 debug_msg(1, "Meta Generator: Parsing '%s' for build [%s].",
237 project->projectFile().toLatin1().constData(),build.toLatin1().constData());
238
239 //initialize the base
240 QMap<QString, QStringList> basevars;
241 if(!project->isEmpty(build + ".CONFIG"))
242 basevars["CONFIG"] += project->values(build + ".CONFIG");
243 basevars["CONFIG"] += build;
244 basevars["CONFIG"] += "build_pass";
245 basevars["BUILD_PASS"] = QStringList(build);
246 QStringList buildname = project->values(build + ".name");
247 basevars["BUILD_NAME"] = (buildname.isEmpty() ? QStringList(build) : buildname);
248
249 //create project
250 QMakeProject *build_proj = new QMakeProject(project->properties(), basevars);
251
252 //all the user configs must be set again afterwards (for .pro tests and for .prf tests)
253 const QStringList old_after_user_config = Option::after_user_configs;
254 const QStringList old_user_config = Option::user_configs;
255 Option::after_user_configs += basevars["CONFIG"];
256 Option::user_configs += basevars["CONFIG"];
257 build_proj->read(project->projectFile());
258 Option::after_user_configs = old_after_user_config;
259 Option::user_configs = old_user_config;
260
261 //done
262 return createMakefileGenerator(build_proj);
263 }
264 return 0;
265}
266
267class SubdirsMetaMakefileGenerator : public MetaMakefileGenerator
268{
269protected:
270 bool init_flag;
271 struct Subdir {
272 Subdir() : makefile(0), indent(0) { }
273 ~Subdir() { delete makefile; }
274 QString input_dir;
275 QString output_dir, output_file;
276 MetaMakefileGenerator *makefile;
277 int indent;
278 };
279 QList<Subdir *> subs;
280 MakefileGenerator *processBuild(const QString &);
281
282public:
283 SubdirsMetaMakefileGenerator(QMakeProject *p, const QString &n, bool op) : MetaMakefileGenerator(p, n, op), init_flag(false) { }
284 virtual ~SubdirsMetaMakefileGenerator();
285
286 virtual bool init();
287 virtual int type() const { return SUBDIRSMETATYPE; }
288 virtual bool write(const QString &);
289};
290
291bool
292SubdirsMetaMakefileGenerator::init()
293{
294 if(init_flag)
295 return false;
296 init_flag = true;
297 bool hasError = false;
298
299 // It might make sense to bequeath the CONFIG option to the recursed
300 // projects. OTOH, one would most likely have it in all projects anyway -
301 // either through a qmakespec, a .qmake.cache or explicitly - as otherwise
302 // running qmake in a subdirectory would have a different auto-recurse
303 // setting than in parent directories.
304 bool recurse = Option::recursive == Option::QMAKE_RECURSIVE_YES
305 || (Option::recursive == Option::QMAKE_RECURSIVE_DEFAULT
306 && project->isRecursive());
307 if(recurse) {
308 QString old_output_dir = Option::output_dir;
309 QString old_output = Option::output.fileName();
310 QString oldpwd = qmake_getpwd();
311 QString thispwd = oldpwd;
312 if(!thispwd.endsWith('/'))
313 thispwd += '/';
314 const QStringList &subdirs = project->values("SUBDIRS");
315 static int recurseDepth = -1;
316 ++recurseDepth;
317 for(int i = 0; i < subdirs.size(); ++i) {
318 Subdir *sub = new Subdir;
319 sub->indent = recurseDepth;
320
321 QFileInfo subdir(subdirs.at(i));
322 if(!project->isEmpty(subdirs.at(i) + ".file"))
323 subdir = project->first(subdirs.at(i) + ".file");
324 else if(!project->isEmpty(subdirs.at(i) + ".subdir"))
325 subdir = project->first(subdirs.at(i) + ".subdir");
326 QString sub_name;
327 if(subdir.isDir())
328 subdir = QFileInfo(subdir.filePath() + "/" + subdir.fileName() + Option::pro_ext);
329 else
330 sub_name = subdir.baseName();
331 if(!subdir.isRelative()) { //we can try to make it relative
332 QString subdir_path = subdir.filePath();
333 if(subdir_path.startsWith(thispwd))
334 subdir = QFileInfo(subdir_path.mid(thispwd.length()));
335 }
336
337 //handle sub project
338 QMakeProject *sub_proj = new QMakeProject(project->properties());
339 for (int ind = 0; ind < sub->indent; ++ind)
340 printf(" ");
341 sub->input_dir = subdir.absolutePath();
342 if(subdir.isRelative() && old_output_dir != oldpwd) {
343 sub->output_dir = old_output_dir + "/" + subdir.path();
344 printf("Reading %s [%s]\n", subdir.absoluteFilePath().toLatin1().constData(), sub->output_dir.toLatin1().constData());
345 } else { //what about shadow builds?
346 sub->output_dir = sub->input_dir;
347 printf("Reading %s\n", subdir.absoluteFilePath().toLatin1().constData());
348 }
349 qmake_setpwd(sub->input_dir);
350 Option::output_dir = sub->output_dir;
351 bool tmpError = !sub_proj->read(subdir.fileName());
352 if(!sub_proj->variables()["QMAKE_FAILED_REQUIREMENTS"].isEmpty()) {
353 fprintf(stderr, "Project file(%s) not recursed because all requirements not met:\n\t%s\n",
354 subdir.fileName().toLatin1().constData(),
355 sub_proj->values("QMAKE_FAILED_REQUIREMENTS").join(" ").toLatin1().constData());
356 delete sub;
357 delete sub_proj;
358 Option::output_dir = old_output_dir;
359 qmake_setpwd(oldpwd);
360 continue;
361 } else {
362 hasError |= tmpError;
363 }
364 sub->makefile = MetaMakefileGenerator::createMetaGenerator(sub_proj, sub_name);
365 if(0 && sub->makefile->type() == SUBDIRSMETATYPE) {
366 subs.append(sub);
367 } else {
368 const QString output_name = Option::output.fileName();
369 Option::output.setFileName(sub->output_file);
370 hasError |= !sub->makefile->write(sub->output_dir);
371 delete sub;
372 qmakeClearCaches();
373 sub = 0;
374 Option::output.setFileName(output_name);
375 }
376 Option::output_dir = old_output_dir;
377 qmake_setpwd(oldpwd);
378
379 }
380 --recurseDepth;
381 Option::output.setFileName(old_output);
382 Option::output_dir = old_output_dir;
383 qmake_setpwd(oldpwd);
384 }
385
386 Subdir *self = new Subdir;
387 self->input_dir = qmake_getpwd();
388 self->output_dir = Option::output_dir;
389 if(!recurse || (!Option::output.fileName().endsWith(Option::dir_sep) && !QFileInfo(Option::output).isDir()))
390 self->output_file = Option::output.fileName();
391 self->makefile = new BuildsMetaMakefileGenerator(project, name, false);
392 self->makefile->init();
393 subs.append(self);
394
395 return !hasError;
396}
397
398bool
399SubdirsMetaMakefileGenerator::write(const QString &oldpwd)
400{
401 bool ret = true;
402 const QString &pwd = qmake_getpwd();
403 const QString &output_dir = Option::output_dir;
404 const QString &output_name = Option::output.fileName();
405 for(int i = 0; ret && i < subs.count(); i++) {
406 const Subdir *sub = subs.at(i);
407 qmake_setpwd(subs.at(i)->input_dir);
408 Option::output_dir = QFileInfo(subs.at(i)->output_dir).absoluteFilePath();
409 if(Option::output_dir.at(Option::output_dir.length()-1) != QLatin1Char('/'))
410 Option::output_dir += QLatin1Char('/');
411 Option::output.setFileName(subs.at(i)->output_file);
412 if(i != subs.count()-1) {
413 for (int ind = 0; ind < sub->indent; ++ind)
414 printf(" ");
415 printf("Writing %s\n", QDir::cleanPath(Option::output_dir+"/"+
416 Option::output.fileName()).toLatin1().constData());
417 }
418 QString writepwd = Option::fixPathToLocalOS(qmake_getpwd());
419 if(!writepwd.startsWith(Option::fixPathToLocalOS(oldpwd)))
420 writepwd = oldpwd;
421 if(!(ret = subs.at(i)->makefile->write(writepwd)))
422 break;
423 //restore because I'm paranoid
424 qmake_setpwd(pwd);
425 Option::output.setFileName(output_name);
426 Option::output_dir = output_dir;
427 }
428 return ret;
429}
430
431SubdirsMetaMakefileGenerator::~SubdirsMetaMakefileGenerator()
432{
433 for(int i = 0; i < subs.count(); i++)
434 delete subs[i];
435 subs.clear();
436}
437
438//Factory things
439QT_BEGIN_INCLUDE_NAMESPACE
440#include "unixmake.h"
441#include "mingw_make.h"
442#include "projectgenerator.h"
443#include "pbuilder_pbx.h"
444#include "msvc_nmake.h"
445#include "borland_bmake.h"
446#include "msvc_vcproj.h"
447#include "msvc_vcxproj.h"
448#include "symmake_abld.h"
449#include "symmake_sbsv2.h"
450#include "symbian_makefile.h"
451#include "gbuild.h"
452QT_END_INCLUDE_NAMESPACE
453
454MakefileGenerator *
455MetaMakefileGenerator::createMakefileGenerator(QMakeProject *proj, bool noIO)
456{
457 MakefileGenerator *mkfile = NULL;
458 if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
459 mkfile = new ProjectGenerator;
460 mkfile->setProjectFile(proj);
461 return mkfile;
462 }
463
464 QString gen = proj->first("MAKEFILE_GENERATOR");
465 if(gen.isEmpty()) {
466 fprintf(stderr, "MAKEFILE_GENERATOR variable not set as a result of parsing : %s. Possibly qmake was not able to find files included using \"include(..)\" - enable qmake debugging to investigate more.\n",
467 proj->projectFile().toLatin1().constData());
468 } else if(gen == "UNIX") {
469 mkfile = new UnixMakefileGenerator;
470 } else if(gen == "MINGW") {
471 mkfile = new MingwMakefileGenerator;
472 } else if(gen == "PROJECTBUILDER" || gen == "XCODE") {
473 mkfile = new ProjectBuilderMakefileGenerator;
474 } else if(gen == "MSVC.NET") {
475 if (proj->first("TEMPLATE").startsWith("vc"))
476 mkfile = new VcprojGenerator;
477 else
478 mkfile = new NmakeMakefileGenerator;
479 } else if(gen == "MSBUILD") {
480 // Visual Studio >= v11.0
481 if (proj->first("TEMPLATE").startsWith("vc"))
482 mkfile = new VcxprojGenerator;
483 else
484 mkfile = new NmakeMakefileGenerator;
485 } else if(gen == "BMAKE") {
486 mkfile = new BorlandMakefileGenerator;
487 } else if(gen == "SYMBIAN_ABLD") {
488 mkfile = new SymbianAbldMakefileGenerator;
489 } else if(gen == "SYMBIAN_SBSV2") {
490 mkfile = new SymbianSbsv2MakefileGenerator;
491 } else if(gen == "SYMBIAN_UNIX") {
492 mkfile = new SymbianMakefileTemplate<UnixMakefileGenerator>;
493 } else if(gen == "SYMBIAN_MINGW") {
494 mkfile = new SymbianMakefileTemplate<MingwMakefileGenerator>;
495 } else if(gen == "GBUILD") {
496 mkfile = new GBuildMakefileGenerator;
497 } else {
498 fprintf(stderr, "Unknown generator specified: %s\n", gen.toLatin1().constData());
499 }
500 if (mkfile) {
501 mkfile->setNoIO(noIO);
502 mkfile->setProjectFile(proj);
503 }
504 return mkfile;
505}
506
507MetaMakefileGenerator *
508MetaMakefileGenerator::createMetaGenerator(QMakeProject *proj, const QString &name, bool op, bool *success)
509{
510 MetaMakefileGenerator *ret = 0;
511 if ((Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
512 Option::qmake_mode == Option::QMAKE_GENERATE_PRL)) {
513 if (proj->first("TEMPLATE").endsWith("subdirs"))
514 ret = new SubdirsMetaMakefileGenerator(proj, name, op);
515 }
516 if (!ret)
517 ret = new BuildsMetaMakefileGenerator(proj, name, op);
518 bool res = ret->init();
519 if (success)
520 *success = res;
521 return ret;
522}
523
524#endif // QT_QMAKE_PARSER_ONLY
525
526bool
527MetaMakefileGenerator::modesForGenerator(const QString &gen,
528 Option::HOST_MODE *host_mode, Option::TARG_MODE *target_mode)
529{
530 if (gen == "UNIX") {
531#ifdef Q_OS_MAC
532 *host_mode = Option::HOST_MACX_MODE;
533 *target_mode = Option::TARG_MACX_MODE;
534#else
535 *host_mode = Option::HOST_UNIX_MODE;
536 *target_mode = Option::TARG_UNIX_MODE;
537#endif
538 } else if (gen == "MSVC.NET" || gen == "BMAKE" || gen == "MSBUILD") {
539 *host_mode = Option::HOST_WIN_MODE;
540 *target_mode = Option::TARG_WIN_MODE;
541 } else if (gen == "MINGW") {
542#if defined(Q_OS_MAC)
543 *host_mode = Option::HOST_MACX_MODE;
544#elif defined(Q_OS_UNIX)
545 *host_mode = Option::HOST_UNIX_MODE;
546#else
547 *host_mode = Option::HOST_WIN_MODE;
548#endif
549 *target_mode = Option::TARG_WIN_MODE;
550 } else if (gen == "PROJECTBUILDER" || gen == "XCODE") {
551 *host_mode = Option::HOST_MACX_MODE;
552 *target_mode = Option::TARG_MACX_MODE;
553 } else if (gen == "SYMBIAN_ABLD" || gen == "SYMBIAN_SBSV2" || gen == "SYMBIAN_UNIX" || gen == "SYMBIAN_MINGW") {
554#if defined(Q_OS_MAC)
555 *host_mode = Option::HOST_MACX_MODE;
556#elif defined(Q_OS_UNIX)
557 *host_mode = Option::HOST_UNIX_MODE;
558#else
559 *host_mode = Option::HOST_WIN_MODE;
560#endif
561 *target_mode = Option::TARG_SYMBIAN_MODE;
562 } else if (gen == "GBUILD") {
563 *host_mode = Option::HOST_UNIX_MODE;
564 *target_mode = Option::TARG_INTEGRITY_MODE;
565 } else {
566 fprintf(stderr, "Unknown generator specified: %s\n", gen.toLatin1().constData());
567 return false;
568 }
569 return true;
570}
571
572QT_END_NAMESPACE
573

Warning: That file was not part of the compilation database. It may have many parsing errors.