source: trunk/qmake/generators/metamakefile.cpp@ 247

Last change on this file since 247 was 29, checked in by Dmitry A. Kuminov, 16 years ago

qmake: More OS/2-specific fixes. Enabled GNUMakefileGenerator (turned on by MAKEFILE_GENERATOR=GNUMAKE).

File size: 18.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the qmake application of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
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
61class BuildsMetaMakefileGenerator : public MetaMakefileGenerator
62{
63 bool init_flag;
64private:
65 struct Build {
66 QString name, build;
67 MakefileGenerator *makefile;
68 };
69 QList<Build *> makefiles;
70 void clearBuilds();
71 MakefileGenerator *processBuild(const QString &);
72
73public:
74
75 BuildsMetaMakefileGenerator(QMakeProject *p, const QString &n, bool op) : MetaMakefileGenerator(p, n, op), init_flag(false) { }
76 virtual ~BuildsMetaMakefileGenerator() { clearBuilds(); }
77
78 virtual bool init();
79 virtual int type() const { return BUILDSMETATYPE; }
80 virtual bool write(const QString &);
81};
82
83void
84BuildsMetaMakefileGenerator::clearBuilds()
85{
86 for(int i = 0; i < makefiles.count(); i++) {
87 Build *build = makefiles[i];
88 if(QMakeProject *p = build->makefile->projectFile()) {
89 if(p != project)
90 delete p;
91 }
92 delete build->makefile;
93 delete build;
94 }
95 makefiles.clear();
96}
97
98bool
99BuildsMetaMakefileGenerator::init()
100{
101 if(init_flag)
102 return false;
103 init_flag = true;
104
105 const QStringList &builds = project->variables()["BUILDS"];
106 bool use_single_build = builds.isEmpty();
107 if(builds.count() > 1 && Option::output.fileName() == "-") {
108 use_single_build = true;
109 warn_msg(WarnLogic, "Cannot direct to stdout when using multiple BUILDS.");
110 } else if(0 && !use_single_build && project->first("TEMPLATE") == "subdirs") {
111 use_single_build = true;
112 warn_msg(WarnLogic, "Cannot specify multiple builds with TEMPLATE subdirs.");
113 }
114 if(!use_single_build) {
115 for(int i = 0; i < builds.count(); i++) {
116 QString build = builds[i];
117 MakefileGenerator *makefile = processBuild(build);
118 if(!makefile)
119 return false;
120 if(!makefile->supportsMetaBuild()) {
121 warn_msg(WarnLogic, "QMAKESPEC does not support multiple BUILDS.");
122 clearBuilds();
123 use_single_build = true;
124 break;
125 } else {
126 Build *b = new Build;
127 b->name = name;
128 if(builds.count() != 1)
129 b->build += build;
130 b->makefile = makefile;
131 makefiles += b;
132 }
133 }
134 }
135 if(use_single_build) {
136 Build *build = new Build;
137 build->name = name;
138 build->makefile = createMakefileGenerator(project, false);
139 makefiles += build;
140 }
141 return true;
142}
143
144bool
145BuildsMetaMakefileGenerator::write(const QString &oldpwd)
146{
147 Build *glue = 0;
148 if(!makefiles.isEmpty() && !makefiles.first()->build.isNull()) {
149 glue = new Build;
150 glue->name = name;
151 glue->makefile = createMakefileGenerator(project, true);
152 makefiles += glue;
153 }
154
155 bool ret = true;
156 const QString &output_name = Option::output.fileName();
157 for(int i = 0; ret && i < makefiles.count(); i++) {
158 Option::output.setFileName(output_name);
159 Build *build = makefiles[i];
160
161 bool using_stdout = false;
162 if(build->makefile && (Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
163 Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT)
164 && (!build->makefile->supportsMergedBuilds()
165 || (build->makefile->supportsMergedBuilds() && (!glue || build == glue)))) {
166 //open output
167 if(!(Option::output.isOpen())) {
168 if(Option::output.fileName() == "-") {
169 Option::output.setFileName("");
170 Option::output_dir = qmake_getpwd();
171 Option::output.open(stdout, QIODevice::WriteOnly | QIODevice::Text);
172 using_stdout = true;
173 } else {
174 if(Option::output.fileName().isEmpty() &&
175 Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE)
176 Option::output.setFileName(project->first("QMAKE_MAKEFILE"));
177 Option::output_dir = oldpwd;
178 QString build_name = build->name;
179 if(!build->build.isEmpty()) {
180 if(!build_name.isEmpty())
181 build_name += ".";
182 build_name += build->build;
183 }
184 if(!build->makefile->openOutput(Option::output, build_name)) {
185 fprintf(stderr, "Failure to open file: %s\n",
186 Option::output.fileName().isEmpty() ? "(stdout)" :
187 Option::output.fileName().toLatin1().constData());
188 return false;
189 }
190 }
191 }
192 } else {
193 using_stdout = true; //kind of..
194 }
195
196 if(!build->makefile) {
197 ret = false;
198 } else if(build == glue) {
199 ret = build->makefile->writeProjectMakefile();
200 } else {
201 ret = build->makefile->write();
202 if (glue && glue->makefile->supportsMergedBuilds())
203 ret = glue->makefile->mergeBuildProject(build->makefile);
204 }
205 if(!using_stdout) {
206 Option::output.close();
207 if(!ret)
208 Option::output.remove();
209 }
210
211 // debugging
212 if(Option::debug_level) {
213 QMap<QString, QStringList> &vars = project->variables();
214 for(QMap<QString, QStringList>::Iterator it = vars.begin(); it != vars.end(); ++it) {
215 if(!it.key().startsWith(".") && !it.value().isEmpty())
216 debug_msg(1, "%s === %s", it.key().toLatin1().constData(),
217 it.value().join(" :: ").toLatin1().constData());
218 }
219 }
220 }
221 return ret;
222}
223
224MakefileGenerator
225*BuildsMetaMakefileGenerator::processBuild(const QString &build)
226{
227 if(project) {
228 debug_msg(1, "Meta Generator: Parsing '%s' for build [%s].",
229 project->projectFile().toLatin1().constData(),build.toLatin1().constData());
230
231 //initialize the base
232 QMap<QString, QStringList> basevars;
233 if(!project->isEmpty(build + ".CONFIG"))
234 basevars["CONFIG"] += project->values(build + ".CONFIG");
235 basevars["CONFIG"] += build;
236 basevars["CONFIG"] += "build_pass";
237 basevars["BUILD_PASS"] = QStringList(build);
238 QStringList buildname = project->values(build + ".name");
239 basevars["BUILD_NAME"] = (buildname.isEmpty() ? QStringList(build) : buildname);
240
241 //create project
242 QMakeProject *build_proj = new QMakeProject(project->properties(), basevars);
243
244 //all the user configs must be set again afterwards (for .pro tests and for .prf tests)
245 const QStringList old_after_user_config = Option::after_user_configs;
246 const QStringList old_user_config = Option::user_configs;
247 Option::after_user_configs += basevars["CONFIG"];
248 Option::user_configs += basevars["CONFIG"];
249 build_proj->read(project->projectFile());
250 Option::after_user_configs = old_after_user_config;
251 Option::user_configs = old_user_config;
252
253 //done
254 return createMakefileGenerator(build_proj);
255 }
256 return 0;
257}
258
259class SubdirsMetaMakefileGenerator : public MetaMakefileGenerator
260{
261 bool init_flag;
262private:
263 struct Subdir {
264 Subdir() : makefile(0), indent(0) { }
265 ~Subdir() { delete makefile; }
266 QString input_dir;
267 QString output_dir, output_file;
268 MetaMakefileGenerator *makefile;
269 int indent;
270 };
271 QList<Subdir *> subs;
272 MakefileGenerator *processBuild(const QString &);
273
274public:
275 SubdirsMetaMakefileGenerator(QMakeProject *p, const QString &n, bool op) : MetaMakefileGenerator(p, n, op), init_flag(false) { }
276 virtual ~SubdirsMetaMakefileGenerator();
277
278 virtual bool init();
279 virtual int type() const { return SUBDIRSMETATYPE; }
280 virtual bool write(const QString &);
281};
282
283bool
284SubdirsMetaMakefileGenerator::init()
285{
286 if(init_flag)
287 return false;
288 init_flag = true;
289
290 if(Option::recursive) {
291 QString old_output_dir = QDir::cleanPath(Option::output_dir);
292 if(!old_output_dir.endsWith('/'))
293 old_output_dir += '/';
294 QString old_output = Option::output.fileName();
295 QString oldpwd = QDir::cleanPath(qmake_getpwd());
296 if(!oldpwd.endsWith('/'))
297 oldpwd += '/';
298 const QStringList &subdirs = project->values("SUBDIRS");
299 static int recurseDepth = -1;
300 ++recurseDepth;
301 for(int i = 0; i < subdirs.size(); ++i) {
302 Subdir *sub = new Subdir;
303 sub->indent = recurseDepth;
304
305 QFileInfo subdir(subdirs.at(i));
306 if(!project->isEmpty(subdirs.at(i) + ".file"))
307 subdir = project->first(subdirs.at(i) + ".file");
308 else if(!project->isEmpty(subdirs.at(i) + ".subdir"))
309 subdir = project->first(subdirs.at(i) + ".subdir");
310 QString sub_name;
311 if(subdir.isDir())
312 subdir = QFileInfo(subdir.filePath() + "/" + subdir.fileName() + Option::pro_ext);
313 else
314 sub_name = subdir.baseName();
315 if(!subdir.isRelative()) { //we can try to make it relative
316 QString subdir_path = subdir.filePath();
317 if(subdir_path.startsWith(oldpwd))
318 subdir = QFileInfo(subdir_path.mid(oldpwd.length()));
319 }
320
321 //handle sub project
322 QMakeProject *sub_proj = new QMakeProject(project->properties());
323 for (int ind = 0; ind < sub->indent; ++ind)
324 printf(" ");
325 sub->input_dir = subdir.absolutePath();
326 if(subdir.isRelative() && old_output_dir != oldpwd) {
327 sub->output_dir = old_output_dir + "/" + subdir.path();
328 printf("Reading %s [%s]\n", subdir.absoluteFilePath().toLatin1().constData(), sub->output_dir.toLatin1().constData());
329 } else { //what about shadow builds?
330 sub->output_dir = sub->input_dir;
331 printf("Reading %s\n", subdir.absoluteFilePath().toLatin1().constData());
332 }
333 qmake_setpwd(sub->input_dir);
334 Option::output_dir = sub->output_dir;
335 if(Option::output_dir.at(Option::output_dir.length()-1) != QLatin1Char('/'))
336 Option::output_dir += QLatin1Char('/');
337 sub_proj->read(subdir.fileName());
338 if(!sub_proj->variables()["QMAKE_FAILED_REQUIREMENTS"].isEmpty()) {
339 fprintf(stderr, "Project file(%s) not recursed because all requirements not met:\n\t%s\n",
340 subdir.fileName().toLatin1().constData(),
341 sub_proj->values("QMAKE_FAILED_REQUIREMENTS").join(" ").toLatin1().constData());
342 delete sub;
343 delete sub_proj;
344 continue;
345 }
346 sub->makefile = MetaMakefileGenerator::createMetaGenerator(sub_proj, sub_name);
347 if(0 && sub->makefile->type() == SUBDIRSMETATYPE) {
348 subs.append(sub);
349 } else {
350 const QString output_name = Option::output.fileName();
351 Option::output.setFileName(sub->output_file);
352 sub->makefile->write(sub->output_dir);
353 delete sub;
354 qmakeClearCaches();
355 sub = 0;
356 Option::output.setFileName(output_name);
357 }
358 Option::output_dir = old_output_dir;
359 qmake_setpwd(oldpwd);
360
361 }
362 --recurseDepth;
363 Option::output.setFileName(old_output);
364 Option::output_dir = old_output_dir;
365 qmake_setpwd(oldpwd);
366 }
367
368 Subdir *self = new Subdir;
369 self->input_dir = qmake_getpwd();
370 self->output_dir = Option::output_dir;
371 if(!Option::recursive || (!Option::output.fileName().endsWith(Option::dir_sep) && !QFileInfo(Option::output).isDir()))
372 self->output_file = Option::output.fileName();
373 self->makefile = new BuildsMetaMakefileGenerator(project, name, false);
374 self->makefile->init();
375 subs.append(self);
376 return true;
377}
378
379bool
380SubdirsMetaMakefileGenerator::write(const QString &oldpwd)
381{
382 bool ret = true;
383 const QString &pwd = qmake_getpwd();
384 const QString &output_dir = Option::output_dir;
385 const QString &output_name = Option::output.fileName();
386 for(int i = 0; ret && i < subs.count(); i++) {
387 const Subdir *sub = subs.at(i);
388 qmake_setpwd(subs.at(i)->input_dir);
389 Option::output_dir = QFileInfo(subs.at(i)->output_dir).absoluteFilePath();
390 if(Option::output_dir.at(Option::output_dir.length()-1) != QLatin1Char('/'))
391 Option::output_dir += QLatin1Char('/');
392 Option::output.setFileName(subs.at(i)->output_file);
393 if(i != subs.count()-1) {
394 for (int ind = 0; ind < sub->indent; ++ind)
395 printf(" ");
396 printf("Writing %s\n", QDir::cleanPath(Option::output_dir+"/"+
397 Option::output.fileName()).toLatin1().constData());
398 }
399 QString writepwd = Option::fixPathToLocalOS(qmake_getpwd());
400 if(!writepwd.startsWith(Option::fixPathToLocalOS(oldpwd)))
401 writepwd = oldpwd;
402 if(!(ret = subs.at(i)->makefile->write(writepwd)))
403 break;
404 //restore because I'm paranoid
405 qmake_setpwd(pwd);
406 Option::output.setFileName(output_name);
407 Option::output_dir = output_dir;
408 }
409 return ret;
410}
411
412SubdirsMetaMakefileGenerator::~SubdirsMetaMakefileGenerator()
413{
414 for(int i = 0; i < subs.count(); i++)
415 delete subs[i];
416 subs.clear();
417}
418
419//Factory things
420QT_BEGIN_INCLUDE_NAMESPACE
421#include "unixmake.h"
422#include "mingw_make.h"
423#include "gnumake.h"
424#include "projectgenerator.h"
425#include "pbuilder_pbx.h"
426#include "msvc_nmake.h"
427#include "borland_bmake.h"
428#include "msvc_dsp.h"
429#include "msvc_vcproj.h"
430QT_END_INCLUDE_NAMESPACE
431
432MakefileGenerator *
433MetaMakefileGenerator::createMakefileGenerator(QMakeProject *proj, bool noIO)
434{
435 MakefileGenerator *mkfile = NULL;
436 if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
437 mkfile = new ProjectGenerator;
438 mkfile->setProjectFile(proj);
439 return mkfile;
440 }
441
442 QString gen = proj->first("MAKEFILE_GENERATOR");
443 if(gen.isEmpty()) {
444 fprintf(stderr, "No generator specified in config file: %s\n",
445 proj->projectFile().toLatin1().constData());
446 } else if(gen == "UNIX") {
447 mkfile = new UnixMakefileGenerator;
448 } else if(gen == "MINGW") {
449 mkfile = new MingwMakefileGenerator;
450 } else if(gen == "GNUMAKE") {
451 mkfile = new GNUMakefileGenerator;
452 } else if(gen == "PROJECTBUILDER" || gen == "XCODE") {
453 mkfile = new ProjectBuilderMakefileGenerator;
454 } else if(gen == "MSVC") {
455 // Visual Studio =< v6.0
456 if(proj->first("TEMPLATE").indexOf(QRegExp("^vc.*")) != -1)
457 mkfile = new DspMakefileGenerator;
458 else
459 mkfile = new NmakeMakefileGenerator;
460 } else if(gen == "MSVC.NET") {
461 // Visual Studio >= v7.0
462 if(proj->first("TEMPLATE").indexOf(QRegExp("^vc.*")) != -1 || proj->first("TEMPLATE").indexOf(QRegExp("^ce.*")) != -1)
463 mkfile = new VcprojGenerator;
464 else
465 mkfile = new NmakeMakefileGenerator;
466 } else if(gen == "BMAKE") {
467 mkfile = new BorlandMakefileGenerator;
468 } else {
469 fprintf(stderr, "Unknown generator specified: %s\n", gen.toLatin1().constData());
470 }
471 if (mkfile) {
472 mkfile->setNoIO(noIO);
473 mkfile->setProjectFile(proj);
474 }
475 return mkfile;
476}
477
478MetaMakefileGenerator *
479MetaMakefileGenerator::createMetaGenerator(QMakeProject *proj, const QString &name, bool op)
480{
481 MetaMakefileGenerator *ret = 0;
482 if((Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
483 Option::qmake_mode == Option::QMAKE_GENERATE_PRL)) {
484 if(proj->first("TEMPLATE").endsWith("subdirs"))
485 ret = new SubdirsMetaMakefileGenerator(proj, name, op);
486 }
487 if(!ret)
488 ret = new BuildsMetaMakefileGenerator(proj, name, op);
489 ret->init();
490 return ret;
491}
492
493QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.