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 "metamakefile.h"
48 #include "cachekeys.h"
50 #define BUILDSMETATYPE 1
51 #define SUBDIRSMETATYPE 2
55 MetaMakefileGenerator::~MetaMakefileGenerator()
61 class BuildsMetaMakefileGenerator
: public MetaMakefileGenerator
67 MakefileGenerator
*makefile
;
69 QList
<Build
*> makefiles
;
71 MakefileGenerator
*processBuild(const QString
&);
75 BuildsMetaMakefileGenerator(QMakeProject
*p
, const QString
&n
, bool op
) : MetaMakefileGenerator(p
, n
, op
), init_flag(false) { }
76 virtual ~BuildsMetaMakefileGenerator() { clearBuilds(); }
79 virtual int type() const { return BUILDSMETATYPE
; }
80 virtual bool write(const QString
&);
84 BuildsMetaMakefileGenerator::clearBuilds()
86 for(int i
= 0; i
< makefiles
.count(); i
++) {
87 Build
*build
= makefiles
[i
];
88 if(QMakeProject
*p
= build
->makefile
->projectFile()) {
92 delete build
->makefile
;
99 BuildsMetaMakefileGenerator::init()
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.");
114 if(!use_single_build
) {
115 for(int i
= 0; i
< builds
.count(); i
++) {
116 QString build
= builds
[i
];
117 MakefileGenerator
*makefile
= processBuild(build
);
120 if(!makefile
->supportsMetaBuild()) {
121 warn_msg(WarnLogic
, "QMAKESPEC does not support multiple BUILDS.");
123 use_single_build
= true;
126 Build
*b
= new Build
;
128 if(builds
.count() != 1)
130 b
->makefile
= makefile
;
135 if(use_single_build
) {
136 Build
*build
= new Build
;
138 build
->makefile
= createMakefileGenerator(project
, false);
139 if (build
->makefile
){
150 BuildsMetaMakefileGenerator::write(const QString
&oldpwd
)
153 if(!makefiles
.isEmpty() && !makefiles
.first()->build
.isNull()) {
156 glue
->makefile
= createMakefileGenerator(project
, 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
];
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
)))) {
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
);
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())
187 build_name
+= build
->build
;
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());
198 using_stdout
= true; //kind of..
201 if(!build
->makefile
) {
203 } else if(build
== glue
) {
204 ret
= build
->makefile
->writeProjectMakefile();
206 ret
= build
->makefile
->write();
207 if (glue
&& glue
->makefile
->supportsMergedBuilds())
208 ret
= glue
->makefile
->mergeBuildProject(build
->makefile
);
211 Option::output
.close();
213 Option::output
.remove();
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());
230 *BuildsMetaMakefileGenerator::processBuild(const QString
&build
)
233 debug_msg(1, "Meta Generator: Parsing '%s' for build [%s].",
234 project
->projectFile().toLatin1().constData(),build
.toLatin1().constData());
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
);
247 QMakeProject
*build_proj
= new QMakeProject(project
->properties(), basevars
);
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
;
259 return createMakefileGenerator(build_proj
);
264 class SubdirsMetaMakefileGenerator
: public MetaMakefileGenerator
269 Subdir() : makefile(0), indent(0) { }
270 ~Subdir() { delete makefile
; }
272 QString output_dir
, output_file
;
273 MetaMakefileGenerator
*makefile
;
276 QList
<Subdir
*> subs
;
277 MakefileGenerator
*processBuild(const QString
&);
280 SubdirsMetaMakefileGenerator(QMakeProject
*p
, const QString
&n
, bool op
) : MetaMakefileGenerator(p
, n
, op
), init_flag(false) { }
281 virtual ~SubdirsMetaMakefileGenerator();
284 virtual int type() const { return SUBDIRSMETATYPE
; }
285 virtual bool write(const QString
&);
289 SubdirsMetaMakefileGenerator::init()
294 bool hasError
= false;
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('/'))
303 const QStringList
&subdirs
= project
->values("SUBDIRS");
304 static int recurseDepth
= -1;
306 for(int i
= 0; i
< subdirs
.size(); ++i
) {
307 Subdir
*sub
= new Subdir
;
308 sub
->indent
= recurseDepth
;
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");
317 subdir
= QFileInfo(subdir
.filePath() + "/" + subdir
.fileName() + Option::pro_ext
);
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()));
327 QMakeProject
*sub_proj
= new QMakeProject(project
->properties());
328 for (int ind
= 0; ind
< sub
->indent
; ++ind
)
330 sub
->input_dir
= subdir
.absolutePath();
331 if(subdir
.isRelative() && old_output_dir
!= oldpwd
) {
332 sub
->output_dir
= old_output_dir
+ "/" + subdir
.path();
333 printf("Reading %s [%s]\n", subdir
.absoluteFilePath().toLatin1().constData(), sub
->output_dir
.toLatin1().constData());
334 } else { //what about shadow builds?
335 sub
->output_dir
= sub
->input_dir
;
336 printf("Reading %s\n", subdir
.absoluteFilePath().toLatin1().constData());
338 qmake_setpwd(sub
->input_dir
);
339 Option::output_dir
= sub
->output_dir
;
340 bool tmpError
= !sub_proj
->read(subdir
.fileName());
341 if(!sub_proj
->variables()["QMAKE_FAILED_REQUIREMENTS"].isEmpty()) {
342 fprintf(stderr
, "Project file(%s) not recursed because all requirements not met:\n\t%s\n",
343 subdir
.fileName().toLatin1().constData(),
344 sub_proj
->values("QMAKE_FAILED_REQUIREMENTS").join(" ").toLatin1().constData());
347 Option::output_dir
= old_output_dir
;
348 qmake_setpwd(oldpwd
);
351 hasError
|= tmpError
;
353 sub
->makefile
= MetaMakefileGenerator::createMetaGenerator(sub_proj
, sub_name
);
354 if(0 && sub
->makefile
->type() == SUBDIRSMETATYPE
) {
357 const QString output_name
= Option::output
.fileName();
358 Option::output
.setFileName(sub
->output_file
);
359 hasError
|= !sub
->makefile
->write(sub
->output_dir
);
363 Option::output
.setFileName(output_name
);
365 Option::output_dir
= old_output_dir
;
366 qmake_setpwd(oldpwd
);
370 Option::output
.setFileName(old_output
);
371 Option::output_dir
= old_output_dir
;
372 qmake_setpwd(oldpwd
);
375 Subdir
*self
= new Subdir
;
376 self
->input_dir
= qmake_getpwd();
377 self
->output_dir
= Option::output_dir
;
378 if(!Option::recursive
|| (!Option::output
.fileName().endsWith(Option::dir_sep
) && !QFileInfo(Option::output
).isDir()))
379 self
->output_file
= Option::output
.fileName();
380 self
->makefile
= new BuildsMetaMakefileGenerator(project
, name
, false);
381 self
->makefile
->init();
388 SubdirsMetaMakefileGenerator::write(const QString
&oldpwd
)
391 const QString
&pwd
= qmake_getpwd();
392 const QString
&output_dir
= Option::output_dir
;
393 const QString
&output_name
= Option::output
.fileName();
394 for(int i
= 0; ret
&& i
< subs
.count(); i
++) {
395 const Subdir
*sub
= subs
.at(i
);
396 qmake_setpwd(subs
.at(i
)->input_dir
);
397 Option::output_dir
= QFileInfo(subs
.at(i
)->output_dir
).absoluteFilePath();
398 if(Option::output_dir
.at(Option::output_dir
.length()-1) != QLatin1Char('/'))
399 Option::output_dir
+= QLatin1Char('/');
400 Option::output
.setFileName(subs
.at(i
)->output_file
);
401 if(i
!= subs
.count()-1) {
402 for (int ind
= 0; ind
< sub
->indent
; ++ind
)
404 printf("Writing %s\n", QDir::cleanPath(Option::output_dir
+"/"+
405 Option::output
.fileName()).toLatin1().constData());
407 QString writepwd
= Option::fixPathToLocalOS(qmake_getpwd());
408 if(!writepwd
.startsWith(Option::fixPathToLocalOS(oldpwd
)))
410 if(!(ret
= subs
.at(i
)->makefile
->write(writepwd
)))
412 //restore because I'm paranoid
414 Option::output
.setFileName(output_name
);
415 Option::output_dir
= output_dir
;
420 SubdirsMetaMakefileGenerator::~SubdirsMetaMakefileGenerator()
422 for(int i
= 0; i
< subs
.count(); i
++)
428 QT_BEGIN_INCLUDE_NAMESPACE
429 #include "unixmake.h"
430 #include "mingw_make.h"
431 #include "projectgenerator.h"
432 #include "pbuilder_pbx.h"
433 #include "msvc_nmake.h"
434 #include "borland_bmake.h"
435 #include "msvc_dsp.h"
436 #include "msvc_vcproj.h"
437 #include "symmake_abld.h"
438 #include "symmake_sbsv2.h"
439 QT_END_INCLUDE_NAMESPACE
442 MetaMakefileGenerator::createMakefileGenerator(QMakeProject
*proj
, bool noIO
)
444 MakefileGenerator
*mkfile
= NULL
;
445 if(Option::qmake_mode
== Option::QMAKE_GENERATE_PROJECT
) {
446 mkfile
= new ProjectGenerator
;
447 mkfile
->setProjectFile(proj
);
451 QString gen
= proj
->first("MAKEFILE_GENERATOR");
453 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",
454 proj
->projectFile().toLatin1().constData());
455 } else if(gen
== "UNIX") {
456 mkfile
= new UnixMakefileGenerator
;
457 } else if(gen
== "MINGW") {
458 mkfile
= new MingwMakefileGenerator
;
459 } else if(gen
== "PROJECTBUILDER" || gen
== "XCODE") {
460 mkfile
= new ProjectBuilderMakefileGenerator
;
461 } else if(gen
== "MSVC") {
462 // Visual Studio =< v6.0
463 if(proj
->first("TEMPLATE").indexOf(QRegExp("^vc.*")) != -1)
464 mkfile
= new DspMakefileGenerator
;
466 mkfile
= new NmakeMakefileGenerator
;
467 } else if(gen
== "MSVC.NET") {
468 // Visual Studio >= v7.0
469 if(proj
->first("TEMPLATE").indexOf(QRegExp("^vc.*")) != -1 || proj
->first("TEMPLATE").indexOf(QRegExp("^ce.*")) != -1)
470 mkfile
= new VcprojGenerator
;
472 mkfile
= new NmakeMakefileGenerator
;
473 } else if(gen
== "BMAKE") {
474 mkfile
= new BorlandMakefileGenerator
;
475 } else if(gen
== "SYMBIAN_ABLD") {
476 mkfile
= new SymbianAbldMakefileGenerator
;
477 } else if(gen
== "SYMBIAN_SBSV2") {
478 mkfile
= new SymbianSbsv2MakefileGenerator
;
480 fprintf(stderr
, "Unknown generator specified: %s\n", gen
.toLatin1().constData());
483 mkfile
->setNoIO(noIO
);
484 mkfile
->setProjectFile(proj
);
489 MetaMakefileGenerator
*
490 MetaMakefileGenerator::createMetaGenerator(QMakeProject
*proj
, const QString
&name
, bool op
, bool *success
)
492 MetaMakefileGenerator
*ret
= 0;
493 if ((Option::qmake_mode
== Option::QMAKE_GENERATE_MAKEFILE
||
494 Option::qmake_mode
== Option::QMAKE_GENERATE_PRL
)) {
495 if (proj
->first("TEMPLATE").endsWith("subdirs"))
496 ret
= new SubdirsMetaMakefileGenerator(proj
, name
, op
);
499 ret
= new BuildsMetaMakefileGenerator(proj
, name
, op
);
500 bool res
= ret
->init();