|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the qmake application of the Qt Toolkit. |
|
8 ** |
|
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 |
|
14 ** this package. |
|
15 ** |
|
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. |
|
23 ** |
|
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. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
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 |
|
53 QT_BEGIN_NAMESPACE |
|
54 |
|
55 MetaMakefileGenerator::~MetaMakefileGenerator() |
|
56 { |
|
57 if(own_project) |
|
58 delete project; |
|
59 } |
|
60 |
|
61 class BuildsMetaMakefileGenerator : public MetaMakefileGenerator |
|
62 { |
|
63 private: |
|
64 bool init_flag; |
|
65 struct Build { |
|
66 QString name, build; |
|
67 MakefileGenerator *makefile; |
|
68 }; |
|
69 QList<Build *> makefiles; |
|
70 void clearBuilds(); |
|
71 MakefileGenerator *processBuild(const QString &); |
|
72 |
|
73 public: |
|
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 |
|
83 void |
|
84 BuildsMetaMakefileGenerator::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 |
|
98 bool |
|
99 BuildsMetaMakefileGenerator::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 if (build->makefile){ |
|
140 makefiles += build; |
|
141 }else { |
|
142 delete build; |
|
143 return false; |
|
144 } |
|
145 } |
|
146 return true; |
|
147 } |
|
148 |
|
149 bool |
|
150 BuildsMetaMakefileGenerator::write(const QString &oldpwd) |
|
151 { |
|
152 Build *glue = 0; |
|
153 if(!makefiles.isEmpty() && !makefiles.first()->build.isNull()) { |
|
154 glue = new Build; |
|
155 glue->name = name; |
|
156 glue->makefile = createMakefileGenerator(project, true); |
|
157 makefiles += glue; |
|
158 } |
|
159 |
|
160 bool ret = true; |
|
161 const QString &output_name = Option::output.fileName(); |
|
162 for(int i = 0; ret && i < makefiles.count(); i++) { |
|
163 Option::output.setFileName(output_name); |
|
164 Build *build = makefiles[i]; |
|
165 |
|
166 bool using_stdout = false; |
|
167 if(build->makefile && (Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE || |
|
168 Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) |
|
169 && (!build->makefile->supportsMergedBuilds() |
|
170 || (build->makefile->supportsMergedBuilds() && (!glue || build == glue)))) { |
|
171 //open output |
|
172 if(!(Option::output.isOpen())) { |
|
173 if(Option::output.fileName() == "-") { |
|
174 Option::output.setFileName(""); |
|
175 Option::output_dir = qmake_getpwd(); |
|
176 Option::output.open(stdout, QIODevice::WriteOnly | QIODevice::Text); |
|
177 using_stdout = true; |
|
178 } else { |
|
179 if(Option::output.fileName().isEmpty() && |
|
180 Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE) |
|
181 Option::output.setFileName(project->first("QMAKE_MAKEFILE")); |
|
182 Option::output_dir = oldpwd; |
|
183 QString build_name = build->name; |
|
184 if(!build->build.isEmpty()) { |
|
185 if(!build_name.isEmpty()) |
|
186 build_name += "."; |
|
187 build_name += build->build; |
|
188 } |
|
189 if(!build->makefile->openOutput(Option::output, build_name)) { |
|
190 fprintf(stderr, "Failure to open file: %s\n", |
|
191 Option::output.fileName().isEmpty() ? "(stdout)" : |
|
192 Option::output.fileName().toLatin1().constData()); |
|
193 return false; |
|
194 } |
|
195 } |
|
196 } |
|
197 } else { |
|
198 using_stdout = true; //kind of.. |
|
199 } |
|
200 |
|
201 if(!build->makefile) { |
|
202 ret = false; |
|
203 } else if(build == glue) { |
|
204 ret = build->makefile->writeProjectMakefile(); |
|
205 } else { |
|
206 ret = build->makefile->write(); |
|
207 if (glue && glue->makefile->supportsMergedBuilds()) |
|
208 ret = glue->makefile->mergeBuildProject(build->makefile); |
|
209 } |
|
210 if(!using_stdout) { |
|
211 Option::output.close(); |
|
212 if(!ret) |
|
213 Option::output.remove(); |
|
214 } |
|
215 |
|
216 // debugging |
|
217 if(Option::debug_level) { |
|
218 QMap<QString, QStringList> &vars = project->variables(); |
|
219 for(QMap<QString, QStringList>::Iterator it = vars.begin(); it != vars.end(); ++it) { |
|
220 if(!it.key().startsWith(".") && !it.value().isEmpty()) |
|
221 debug_msg(1, "%s === %s", it.key().toLatin1().constData(), |
|
222 it.value().join(" :: ").toLatin1().constData()); |
|
223 } |
|
224 } |
|
225 } |
|
226 return ret; |
|
227 } |
|
228 |
|
229 MakefileGenerator |
|
230 *BuildsMetaMakefileGenerator::processBuild(const QString &build) |
|
231 { |
|
232 if(project) { |
|
233 debug_msg(1, "Meta Generator: Parsing '%s' for build [%s].", |
|
234 project->projectFile().toLatin1().constData(),build.toLatin1().constData()); |
|
235 |
|
236 //initialize the base |
|
237 QMap<QString, QStringList> basevars; |
|
238 if(!project->isEmpty(build + ".CONFIG")) |
|
239 basevars["CONFIG"] += project->values(build + ".CONFIG"); |
|
240 basevars["CONFIG"] += build; |
|
241 basevars["CONFIG"] += "build_pass"; |
|
242 basevars["BUILD_PASS"] = QStringList(build); |
|
243 QStringList buildname = project->values(build + ".name"); |
|
244 basevars["BUILD_NAME"] = (buildname.isEmpty() ? QStringList(build) : buildname); |
|
245 |
|
246 //create project |
|
247 QMakeProject *build_proj = new QMakeProject(project->properties(), basevars); |
|
248 |
|
249 //all the user configs must be set again afterwards (for .pro tests and for .prf tests) |
|
250 const QStringList old_after_user_config = Option::after_user_configs; |
|
251 const QStringList old_user_config = Option::user_configs; |
|
252 Option::after_user_configs += basevars["CONFIG"]; |
|
253 Option::user_configs += basevars["CONFIG"]; |
|
254 build_proj->read(project->projectFile()); |
|
255 Option::after_user_configs = old_after_user_config; |
|
256 Option::user_configs = old_user_config; |
|
257 |
|
258 //done |
|
259 return createMakefileGenerator(build_proj); |
|
260 } |
|
261 return 0; |
|
262 } |
|
263 |
|
264 class SubdirsMetaMakefileGenerator : public MetaMakefileGenerator |
|
265 { |
|
266 protected: |
|
267 bool init_flag; |
|
268 struct Subdir { |
|
269 Subdir() : makefile(0), indent(0) { } |
|
270 ~Subdir() { delete makefile; } |
|
271 QString input_dir; |
|
272 QString output_dir, output_file; |
|
273 MetaMakefileGenerator *makefile; |
|
274 int indent; |
|
275 }; |
|
276 QList<Subdir *> subs; |
|
277 MakefileGenerator *processBuild(const QString &); |
|
278 |
|
279 public: |
|
280 SubdirsMetaMakefileGenerator(QMakeProject *p, const QString &n, bool op) : MetaMakefileGenerator(p, n, op), init_flag(false) { } |
|
281 virtual ~SubdirsMetaMakefileGenerator(); |
|
282 |
|
283 virtual bool init(); |
|
284 virtual int type() const { return SUBDIRSMETATYPE; } |
|
285 virtual bool write(const QString &); |
|
286 }; |
|
287 |
|
288 bool |
|
289 SubdirsMetaMakefileGenerator::init() |
|
290 { |
|
291 if(init_flag) |
|
292 return false; |
|
293 init_flag = true; |
|
294 bool hasError = false; |
|
295 |
|
296 if(Option::recursive) { |
|
297 QString old_output_dir = Option::output_dir; |
|
298 QString old_output = Option::output.fileName(); |
|
299 QString oldpwd = qmake_getpwd(); |
|
300 QString thispwd = oldpwd; |
|
301 if(!thispwd.endsWith('/')) |
|
302 thispwd += '/'; |
|
303 const QStringList &subdirs = project->values("SUBDIRS"); |
|
304 static int recurseDepth = -1; |
|
305 ++recurseDepth; |
|
306 for(int i = 0; i < subdirs.size(); ++i) { |
|
307 Subdir *sub = new Subdir; |
|
308 sub->indent = recurseDepth; |
|
309 |
|
310 QFileInfo subdir(subdirs.at(i)); |
|
311 if(!project->isEmpty(subdirs.at(i) + ".file")) |
|
312 subdir = project->first(subdirs.at(i) + ".file"); |
|
313 else if(!project->isEmpty(subdirs.at(i) + ".subdir")) |
|
314 subdir = project->first(subdirs.at(i) + ".subdir"); |
|
315 QString sub_name; |
|
316 if(subdir.isDir()) |
|
317 subdir = QFileInfo(subdir.filePath() + "/" + subdir.fileName() + Option::pro_ext); |
|
318 else |
|
319 sub_name = subdir.baseName(); |
|
320 if(!subdir.isRelative()) { //we can try to make it relative |
|
321 QString subdir_path = subdir.filePath(); |
|
322 if(subdir_path.startsWith(thispwd)) |
|
323 subdir = QFileInfo(subdir_path.mid(thispwd.length())); |
|
324 } |
|
325 |
|
326 //handle sub project |
|
327 QMakeProject *sub_proj = new QMakeProject(project->properties()); |
|
328 if (!Option::mkfile::listgen) { |
|
329 for (int ind = 0; ind < sub->indent; ++ind) |
|
330 printf(" "); |
|
331 } |
|
332 |
|
333 sub->input_dir = subdir.absolutePath(); |
|
334 if(subdir.isRelative() && old_output_dir != oldpwd) { |
|
335 sub->output_dir = old_output_dir + "/" + subdir.path(); |
|
336 printf("Reading %s [%s]\n", subdir.absoluteFilePath().toLatin1().constData(), sub->output_dir.toLatin1().constData()); |
|
337 } else { //what about shadow builds? |
|
338 sub->output_dir = sub->input_dir; |
|
339 printf("Reading %s\n", subdir.absoluteFilePath().toLatin1().constData()); |
|
340 } |
|
341 qmake_setpwd(sub->input_dir); |
|
342 Option::output_dir = sub->output_dir; |
|
343 bool tmpError = !sub_proj->read(subdir.fileName()); |
|
344 if(!sub_proj->variables()["QMAKE_FAILED_REQUIREMENTS"].isEmpty()) { |
|
345 fprintf(stderr, "Project file(%s) not recursed because all requirements not met:\n\t%s\n", |
|
346 subdir.fileName().toLatin1().constData(), |
|
347 sub_proj->values("QMAKE_FAILED_REQUIREMENTS").join(" ").toLatin1().constData()); |
|
348 delete sub; |
|
349 delete sub_proj; |
|
350 Option::output_dir = old_output_dir; |
|
351 qmake_setpwd(oldpwd); |
|
352 continue; |
|
353 } else { |
|
354 hasError |= tmpError; |
|
355 } |
|
356 sub->makefile = MetaMakefileGenerator::createMetaGenerator(sub_proj, sub_name); |
|
357 if(0 && sub->makefile->type() == SUBDIRSMETATYPE) { |
|
358 subs.append(sub); |
|
359 } else { |
|
360 const QString output_name = Option::output.fileName(); |
|
361 Option::output.setFileName(sub->output_file); |
|
362 hasError |= !sub->makefile->write(sub->output_dir); |
|
363 delete sub; |
|
364 qmakeClearCaches(); |
|
365 sub = 0; |
|
366 Option::output.setFileName(output_name); |
|
367 } |
|
368 Option::output_dir = old_output_dir; |
|
369 qmake_setpwd(oldpwd); |
|
370 |
|
371 } |
|
372 --recurseDepth; |
|
373 Option::output.setFileName(old_output); |
|
374 Option::output_dir = old_output_dir; |
|
375 qmake_setpwd(oldpwd); |
|
376 } |
|
377 |
|
378 Subdir *self = new Subdir; |
|
379 self->input_dir = qmake_getpwd(); |
|
380 self->output_dir = Option::output_dir; |
|
381 if(!Option::recursive || (!Option::output.fileName().endsWith(Option::dir_sep) && !QFileInfo(Option::output).isDir())) |
|
382 self->output_file = Option::output.fileName(); |
|
383 self->makefile = new BuildsMetaMakefileGenerator(project, name, false); |
|
384 self->makefile->init(); |
|
385 subs.append(self); |
|
386 |
|
387 return !hasError; |
|
388 } |
|
389 |
|
390 bool |
|
391 SubdirsMetaMakefileGenerator::write(const QString &oldpwd) |
|
392 { |
|
393 bool ret = true; |
|
394 const QString &pwd = qmake_getpwd(); |
|
395 const QString &output_dir = Option::output_dir; |
|
396 const QString &output_name = Option::output.fileName(); |
|
397 for(int i = 0; ret && i < subs.count(); i++) { |
|
398 const Subdir *sub = subs.at(i); |
|
399 qmake_setpwd(subs.at(i)->input_dir); |
|
400 Option::output_dir = QFileInfo(subs.at(i)->output_dir).absoluteFilePath(); |
|
401 if(Option::output_dir.at(Option::output_dir.length()-1) != QLatin1Char('/')) |
|
402 Option::output_dir += QLatin1Char('/'); |
|
403 Option::output.setFileName(subs.at(i)->output_file); |
|
404 if(i != subs.count()-1) { |
|
405 if (!Option::mkfile::listgen) { |
|
406 for (int ind = 0; ind < sub->indent; ++ind) |
|
407 printf(" "); |
|
408 } |
|
409 printf("Writing %s\n", QDir::cleanPath(Option::output_dir+"/"+ |
|
410 Option::output.fileName()).toLatin1().constData()); |
|
411 } |
|
412 QString writepwd = Option::fixPathToLocalOS(qmake_getpwd()); |
|
413 if(!writepwd.startsWith(Option::fixPathToLocalOS(oldpwd))) |
|
414 writepwd = oldpwd; |
|
415 if(!(ret = subs.at(i)->makefile->write(writepwd))) |
|
416 break; |
|
417 //restore because I'm paranoid |
|
418 qmake_setpwd(pwd); |
|
419 Option::output.setFileName(output_name); |
|
420 Option::output_dir = output_dir; |
|
421 } |
|
422 return ret; |
|
423 } |
|
424 |
|
425 SubdirsMetaMakefileGenerator::~SubdirsMetaMakefileGenerator() |
|
426 { |
|
427 for(int i = 0; i < subs.count(); i++) |
|
428 delete subs[i]; |
|
429 subs.clear(); |
|
430 } |
|
431 |
|
432 //Factory things |
|
433 QT_BEGIN_INCLUDE_NAMESPACE |
|
434 #include "unixmake.h" |
|
435 #include "mingw_make.h" |
|
436 #include "projectgenerator.h" |
|
437 #include "pbuilder_pbx.h" |
|
438 #include "msvc_nmake.h" |
|
439 #include "borland_bmake.h" |
|
440 #include "msvc_dsp.h" |
|
441 #include "msvc_vcproj.h" |
|
442 #include "symmake_abld.h" |
|
443 #include "symmake_sbsv2.h" |
|
444 QT_END_INCLUDE_NAMESPACE |
|
445 |
|
446 MakefileGenerator * |
|
447 MetaMakefileGenerator::createMakefileGenerator(QMakeProject *proj, bool noIO) |
|
448 { |
|
449 MakefileGenerator *mkfile = NULL; |
|
450 if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) { |
|
451 mkfile = new ProjectGenerator; |
|
452 mkfile->setProjectFile(proj); |
|
453 return mkfile; |
|
454 } |
|
455 |
|
456 QString gen = proj->first("MAKEFILE_GENERATOR"); |
|
457 if(gen.isEmpty()) { |
|
458 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", |
|
459 proj->projectFile().toLatin1().constData()); |
|
460 } else if(gen == "UNIX") { |
|
461 mkfile = new UnixMakefileGenerator; |
|
462 } else if(gen == "MINGW") { |
|
463 mkfile = new MingwMakefileGenerator; |
|
464 } else if(gen == "PROJECTBUILDER" || gen == "XCODE") { |
|
465 mkfile = new ProjectBuilderMakefileGenerator; |
|
466 } else if(gen == "MSVC") { |
|
467 // Visual Studio =< v6.0 |
|
468 if(proj->first("TEMPLATE").indexOf(QRegExp("^vc.*")) != -1) |
|
469 mkfile = new DspMakefileGenerator; |
|
470 else |
|
471 mkfile = new NmakeMakefileGenerator; |
|
472 } else if(gen == "MSVC.NET") { |
|
473 // Visual Studio >= v7.0 |
|
474 if(proj->first("TEMPLATE").indexOf(QRegExp("^vc.*")) != -1 || proj->first("TEMPLATE").indexOf(QRegExp("^ce.*")) != -1) |
|
475 mkfile = new VcprojGenerator; |
|
476 else |
|
477 mkfile = new NmakeMakefileGenerator; |
|
478 } else if(gen == "BMAKE") { |
|
479 mkfile = new BorlandMakefileGenerator; |
|
480 } else if(gen == "SYMBIAN_ABLD") { |
|
481 mkfile = new SymbianAbldMakefileGenerator; |
|
482 } else if(gen == "SYMBIAN_SBSV2") { |
|
483 mkfile = new SymbianSbsv2MakefileGenerator; |
|
484 } else { |
|
485 fprintf(stderr, "Unknown generator specified: %s\n", gen.toLatin1().constData()); |
|
486 } |
|
487 if (mkfile) { |
|
488 mkfile->setNoIO(noIO); |
|
489 mkfile->setProjectFile(proj); |
|
490 } |
|
491 return mkfile; |
|
492 } |
|
493 |
|
494 MetaMakefileGenerator * |
|
495 MetaMakefileGenerator::createMetaGenerator(QMakeProject *proj, const QString &name, bool op, bool *success) |
|
496 { |
|
497 MetaMakefileGenerator *ret = 0; |
|
498 if ((Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE || |
|
499 Option::qmake_mode == Option::QMAKE_GENERATE_PRL)) { |
|
500 if (proj->first("TEMPLATE").endsWith("subdirs")) |
|
501 ret = new SubdirsMetaMakefileGenerator(proj, name, op); |
|
502 } |
|
503 if (!ret) |
|
504 ret = new BuildsMetaMakefileGenerator(proj, name, op); |
|
505 bool res = ret->init(); |
|
506 if (success) |
|
507 *success = res; |
|
508 return ret; |
|
509 } |
|
510 |
|
511 QT_END_NAMESPACE |