source: trunk/qmake/generators/makefile.cpp@ 976

Last change on this file since 976 was 976, checked in by Dmitry A. Kuminov, 14 years ago

qmake: Ensure Makefile generation order in ordered builds.

When one sub-project links to another sub-project's library, ordered
builds are used. However, it's not enough only to build the projects in
the respective order, Makefile generation should be ordered too. One
reason for that is projects using library names with version suffixes
which qmake deduces at Makefile generation time from the .prl file if
the actual library file is not there (not yet built). The .prl file is
generated along with the respective Makefile; hence the dependency.

This applicable to at least Windows and OS/2 and fixes parallel build issues
with some libraries (e.g. QtHelp which depends on QtCLucene).

File size: 134.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** 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 have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "makefile.h"
43#include "option.h"
44#include "cachekeys.h"
45#include "meta.h"
46#include <qdir.h>
47#include <qfile.h>
48#include <qtextstream.h>
49#include <qregexp.h>
50#include <qhash.h>
51#include <qdebug.h>
52#include <qbuffer.h>
53#include <qsettings.h>
54#include <qdatetime.h>
55#if defined(Q_OS_UNIX)
56#include <unistd.h>
57#else
58#include <io.h>
59#endif
60#include <qdebug.h>
61#include <stdio.h>
62#include <stdlib.h>
63#include <time.h>
64#include <fcntl.h>
65#include <sys/types.h>
66#include <sys/stat.h>
67
68QT_BEGIN_NAMESPACE
69
70// Well, Windows doesn't have this, so here's the macro
71#ifndef S_ISDIR
72# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
73#endif
74
75bool MakefileGenerator::canExecute(const QStringList &cmdline, int *a) const
76{
77 int argv0 = -1;
78 for(int i = 0; i < cmdline.count(); ++i) {
79 if(!cmdline.at(i).contains('=')) {
80 argv0 = i;
81 break;
82 }
83 }
84 if(a)
85 *a = argv0;
86 if(argv0 != -1) {
87 const QString c = Option::fixPathToLocalOS(cmdline.at(argv0), true);
88 if(exists(c))
89 return true;
90 }
91 return false;
92}
93
94QString MakefileGenerator::mkdir_p_asstring(const QString &dir, bool escape) const
95{
96 QString ret = "@$(CHK_DIR_EXISTS) ";
97 if(escape)
98 ret += escapeFilePath(dir);
99 else
100 ret += dir;
101 ret += " ";
102 if(isDosLikeShell())
103 ret += "$(MKDIR)";
104 else
105 ret += "|| $(MKDIR)";
106 ret += " ";
107 if(escape)
108 ret += escapeFilePath(dir);
109 else
110 ret += dir;
111 ret += " ";
112 return ret;
113}
114
115bool MakefileGenerator::mkdir(const QString &in_path) const
116{
117 QString path = Option::fixPathToLocalOS(in_path);
118 if(QFile::exists(path))
119 return true;
120
121 QDir d;
122 if(path.startsWith(QDir::separator())) {
123 d.cd(QString(QDir::separator()));
124 path.remove(0, 1);
125 }
126 bool ret = true;
127#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
128 bool driveExists = true;
129 if(!QDir::isRelativePath(path)) {
130 if(QFile::exists(path.left(3))) {
131 d.cd(path.left(3));
132 path.remove(0, 3);
133 } else {
134 warn_msg(WarnLogic, "Cannot access drive '%s' (%s)",
135 path.left(3).toLatin1().data(), path.toLatin1().data());
136 driveExists = false;
137 }
138 }
139 if(driveExists)
140#endif
141 {
142 QStringList subs = path.split(QDir::separator());
143 for(QStringList::Iterator subit = subs.begin(); subit != subs.end(); ++subit) {
144 if(!d.cd(*subit)) {
145 d.mkdir((*subit));
146 if(d.exists((*subit))) {
147 d.cd((*subit));
148 } else {
149 ret = false;
150 break;
151 }
152 }
153 }
154 }
155 return ret;
156}
157
158// ** base makefile generator
159MakefileGenerator::MakefileGenerator() :
160 init_opath_already(false), init_already(false), no_io(false), project(0)
161{
162}
163
164
165void
166MakefileGenerator::verifyCompilers()
167{
168 QMap<QString, QStringList> &v = project->variables();
169 QStringList &quc = v["QMAKE_EXTRA_COMPILERS"];
170 for(int i = 0; i < quc.size(); ) {
171 bool error = false;
172 QString comp = quc.at(i);
173 if(v[comp + ".output"].isEmpty()) {
174 if(!v[comp + ".output_function"].isEmpty()) {
175 v[comp + ".output"].append("${QMAKE_FUNC_FILE_IN_" + v[comp + ".output_function"].first() + "}");
176 } else {
177 error = true;
178 warn_msg(WarnLogic, "Compiler: %s: No output file specified", comp.toLatin1().constData());
179 }
180 } else if(v[comp + ".input"].isEmpty()) {
181 error = true;
182 warn_msg(WarnLogic, "Compiler: %s: No input variable specified", comp.toLatin1().constData());
183 }
184 if(error)
185 quc.removeAt(i);
186 else
187 ++i;
188 }
189}
190
191void
192MakefileGenerator::initOutPaths()
193{
194 if(init_opath_already)
195 return;
196 verifyCompilers();
197 init_opath_already = true;
198 QMap<QString, QStringList> &v = project->variables();
199 //for shadow builds
200 if(!v.contains("QMAKE_ABSOLUTE_SOURCE_PATH")) {
201 if(Option::mkfile::do_cache && !Option::mkfile::cachefile.isEmpty() &&
202 v.contains("QMAKE_ABSOLUTE_SOURCE_ROOT")) {
203 QString root = v["QMAKE_ABSOLUTE_SOURCE_ROOT"].first();
204 root = QDir::fromNativeSeparators(root);
205 if(!root.isEmpty()) {
206 QFileInfo fi = fileInfo(Option::mkfile::cachefile);
207 if(!fi.makeAbsolute()) {
208 QString cache_r = fi.path(), pwd = Option::output_dir;
209 if(pwd.startsWith(cache_r) && !pwd.startsWith(root)) {
210 pwd = root + pwd.mid(cache_r.length());
211 if(exists(pwd))
212 v.insert("QMAKE_ABSOLUTE_SOURCE_PATH", QStringList(pwd));
213 }
214 }
215 }
216 }
217 }
218 if(!v["QMAKE_ABSOLUTE_SOURCE_PATH"].isEmpty()) {
219 QString &asp = v["QMAKE_ABSOLUTE_SOURCE_PATH"].first();
220 asp = QDir::fromNativeSeparators(asp);
221 if(asp.isEmpty() || asp == Option::output_dir) //if they're the same, why bother?
222 v["QMAKE_ABSOLUTE_SOURCE_PATH"].clear();
223 }
224
225 QString currentDir = qmake_getpwd(); //just to go back to
226
227 //some builtin directories
228 if(project->isEmpty("PRECOMPILED_DIR") && !project->isEmpty("OBJECTS_DIR"))
229 v["PRECOMPILED_DIR"] = v["OBJECTS_DIR"];
230 QString dirs[] = { QString("OBJECTS_DIR"), QString("DESTDIR"), QString("QMAKE_PKGCONFIG_DESTDIR"),
231 QString("SUBLIBS_DIR"), QString("DLLDESTDIR"), QString("QMAKE_LIBTOOL_DESTDIR"),
232 QString("PRECOMPILED_DIR"), QString() };
233 for(int x = 0; !dirs[x].isEmpty(); x++) {
234 if(v[dirs[x]].isEmpty())
235 continue;
236 const QString orig_path = v[dirs[x]].first();
237
238 QString &pathRef = v[dirs[x]].first();
239 pathRef = fileFixify(pathRef, Option::output_dir, Option::output_dir);
240
241#ifdef Q_OS_WIN
242 // We don't want to add a separator for DLLDESTDIR on Windows (###why?)
243 if(!(dirs[x] == "DLLDESTDIR"))
244#endif
245 {
246 if(!pathRef.endsWith(Option::dir_sep))
247 pathRef += Option::dir_sep;
248 }
249
250 if(noIO())
251 continue;
252
253 QString path = project->first(dirs[x]); //not to be changed any further
254 path = fileFixify(path, currentDir, Option::output_dir);
255 debug_msg(3, "Fixed output_dir %s (%s) into %s", dirs[x].toLatin1().constData(),
256 orig_path.toLatin1().constData(), path.toLatin1().constData());
257 if(!mkdir(path))
258 warn_msg(WarnLogic, "%s: Cannot access directory '%s'", dirs[x].toLatin1().constData(),
259 path.toLatin1().constData());
260 }
261
262 //out paths from the extra compilers
263 const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
264 for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
265 QString tmp_out = project->values((*it) + ".output").first();
266 if(tmp_out.isEmpty())
267 continue;
268 const QStringList &tmp = project->values((*it) + ".input");
269 for(QStringList::ConstIterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
270 QStringList &inputs = project->values((*it2));
271 for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
272 (*input) = fileFixify((*input), Option::output_dir, Option::output_dir);
273 QString path = unescapeFilePath(replaceExtraCompilerVariables(tmp_out, (*input), QString()));
274 path = Option::fixPathToTargetOS(path);
275 int slash = path.lastIndexOf(Option::dir_sep);
276 if(slash != -1) {
277 path = path.left(slash);
278 // Make out path only if it does not contain makefile variables
279 if(!path.contains("${"))
280 if(path != "." &&
281 !mkdir(fileFixify(path, qmake_getpwd(), Option::output_dir)))
282 warn_msg(WarnLogic, "%s: Cannot access directory '%s'",
283 (*it).toLatin1().constData(), path.toLatin1().constData());
284 }
285 }
286 }
287 }
288
289 if(!v["DESTDIR"].isEmpty()) {
290 QDir d(v["DESTDIR"].first());
291 if(Option::fixPathToLocalOS(d.absolutePath()) == Option::fixPathToLocalOS(Option::output_dir))
292 v.remove("DESTDIR");
293 }
294}
295
296QMakeProject
297*MakefileGenerator::projectFile() const
298{
299 return project;
300}
301
302void
303MakefileGenerator::setProjectFile(QMakeProject *p)
304{
305 if(project)
306 return;
307 project = p;
308 init();
309 usePlatformDir();
310 findLibraries();
311 if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE &&
312 project->isActiveConfig("link_prl")) //load up prl's'
313 processPrlFiles();
314}
315
316QStringList
317MakefileGenerator::findFilesInVPATH(QStringList l, uchar flags, const QString &vpath_var)
318{
319 QStringList vpath;
320 QMap<QString, QStringList> &v = project->variables();
321 for(int val_it = 0; val_it < l.count(); ) {
322 bool remove_file = false;
323 QString &val = l[val_it];
324 if(!val.isEmpty()) {
325 QString file = fixEnvVariables(val);
326 if(!(flags & VPATH_NoFixify))
327 file = fileFixify(file, qmake_getpwd(), Option::output_dir);
328 if (file.at(0) == '\"' && file.at(file.length() - 1) == '\"')
329 file = file.mid(1, file.length() - 2);
330
331 if(exists(file)) {
332 ++val_it;
333 continue;
334 }
335 bool found = false;
336 if(QDir::isRelativePath(val)) {
337 if(vpath.isEmpty()) {
338 if(!vpath_var.isEmpty())
339 vpath = v[vpath_var];
340 vpath += v["VPATH"] + v["QMAKE_ABSOLUTE_SOURCE_PATH"] + v["DEPENDPATH"];
341 if(Option::output_dir != qmake_getpwd())
342 vpath += Option::output_dir;
343 }
344 for(QStringList::Iterator vpath_it = vpath.begin();
345 vpath_it != vpath.end(); ++vpath_it) {
346 QString real_dir = Option::fixPathToLocalOS((*vpath_it));
347 if(exists(real_dir + QDir::separator() + val)) {
348 QString dir = (*vpath_it);
349 if(!dir.endsWith(Option::dir_sep))
350 dir += Option::dir_sep;
351 val = dir + val;
352 if(!(flags & VPATH_NoFixify))
353 val = fileFixify(val);
354 found = true;
355 debug_msg(1, "Found file through vpath %s -> %s",
356 file.toLatin1().constData(), val.toLatin1().constData());
357 break;
358 }
359 }
360 }
361 if(!found) {
362 QString dir, regex = val, real_dir;
363 if(regex.lastIndexOf(Option::dir_sep) != -1) {
364 dir = regex.left(regex.lastIndexOf(Option::dir_sep) + 1);
365 real_dir = dir;
366 if(!(flags & VPATH_NoFixify))
367 real_dir = fileFixify(real_dir, qmake_getpwd(), Option::output_dir);
368 regex.remove(0, dir.length());
369 }
370 if(real_dir.isEmpty() || exists(real_dir)) {
371 QStringList files = QDir(real_dir).entryList(QStringList(regex));
372 if(files.isEmpty()) {
373 debug_msg(1, "%s:%d Failure to find %s in vpath (%s)",
374 __FILE__, __LINE__,
375 val.toLatin1().constData(), vpath.join("::").toLatin1().constData());
376 if(flags & VPATH_RemoveMissingFiles)
377 remove_file = true;
378 else if(flags & VPATH_WarnMissingFiles)
379 warn_msg(WarnLogic, "Failure to find: %s", val.toLatin1().constData());
380 } else {
381 l.removeAt(val_it);
382 QString a;
383 for(int i = (int)files.count()-1; i >= 0; i--) {
384 if(files[i] == "." || files[i] == "..")
385 continue;
386 a = dir + files[i];
387 if(!(flags & VPATH_NoFixify))
388 a = fileFixify(a);
389 l.insert(val_it, a);
390 }
391 }
392 } else {
393 debug_msg(1, "%s:%d Cannot match %s%c%s, as %s does not exist.",
394 __FILE__, __LINE__, real_dir.toLatin1().constData(),
395 QDir::separator().toLatin1(),
396 regex.toLatin1().constData(), real_dir.toLatin1().constData());
397 if(flags & VPATH_RemoveMissingFiles)
398 remove_file = true;
399 else if(flags & VPATH_WarnMissingFiles)
400 warn_msg(WarnLogic, "Failure to find: %s", val.toLatin1().constData());
401 }
402 }
403 }
404 if(remove_file)
405 l.removeAt(val_it);
406 else
407 ++val_it;
408 }
409 return l;
410}
411
412void
413MakefileGenerator::initCompiler(const MakefileGenerator::Compiler &comp)
414{
415 QMap<QString, QStringList> &v = project->variables();
416 QStringList &l = v[comp.variable_in];
417 // find all the relevant file inputs
418 if(!init_compiler_already.contains(comp.variable_in)) {
419 init_compiler_already.insert(comp.variable_in, true);
420 if(!noIO())
421 l = findFilesInVPATH(l, (comp.flags & Compiler::CompilerRemoveNoExist) ?
422 VPATH_RemoveMissingFiles : VPATH_WarnMissingFiles, "VPATH_" + comp.variable_in);
423 }
424}
425
426void
427MakefileGenerator::init()
428{
429 initOutPaths();
430 if(init_already)
431 return;
432 verifyCompilers();
433 init_already = true;
434
435 QMap<QString, QStringList> &v = project->variables();
436 QStringList &quc = v["QMAKE_EXTRA_COMPILERS"];
437
438 //make sure the COMPILERS are in the correct input/output chain order
439 for(int comp_out = 0, jump_count = 0; comp_out < quc.size(); ++comp_out) {
440 continue_compiler_chain:
441 if(jump_count > quc.size()) //just to avoid an infinite loop here
442 break;
443 if(project->variables().contains(quc.at(comp_out) + ".variable_out")) {
444 const QStringList &outputs = project->variables().value(quc.at(comp_out) + ".variable_out");
445 for(int out = 0; out < outputs.size(); ++out) {
446 for(int comp_in = 0; comp_in < quc.size(); ++comp_in) {
447 if(comp_in == comp_out)
448 continue;
449 if(project->variables().contains(quc.at(comp_in) + ".input")) {
450 const QStringList &inputs = project->variables().value(quc.at(comp_in) + ".input");
451 for(int in = 0; in < inputs.size(); ++in) {
452 if(inputs.at(in) == outputs.at(out) && comp_out > comp_in) {
453 ++jump_count;
454 //move comp_out to comp_in and continue the compiler chain
455 quc.move(comp_out, comp_in);
456 comp_out = comp_in;
457 goto continue_compiler_chain;
458 }
459 }
460 }
461 }
462 }
463 }
464 }
465
466 if(!project->isEmpty("QMAKE_SUBSTITUTES")) {
467 const QStringList &subs = v["QMAKE_SUBSTITUTES"];
468 for(int i = 0; i < subs.size(); ++i) {
469 QString inn = subs.at(i) + ".input", outn = subs.at(i) + ".output";
470 if (v.contains(inn) || v.contains(outn)) {
471 if (!v.contains(inn) || !v.contains(outn)) {
472 warn_msg(WarnLogic, "Substitute '%s' has only one of .input and .output",
473 subs.at(i).toLatin1().constData());
474 continue;
475 }
476 const QStringList &tinn = v[inn], &toutn = v[outn];
477 if (tinn.length() != 1) {
478 warn_msg(WarnLogic, "Substitute '%s.input' does not have exactly one value",
479 subs.at(i).toLatin1().constData());
480 continue;
481 }
482 if (toutn.length() != 1) {
483 warn_msg(WarnLogic, "Substitute '%s.output' does not have exactly one value",
484 subs.at(i).toLatin1().constData());
485 continue;
486 }
487 inn = tinn.first();
488 outn = toutn.first();
489 } else {
490 inn = subs.at(i);
491 if(!inn.endsWith(".in")) {
492 warn_msg(WarnLogic, "Substitute '%s' does not end with '.in'",
493 inn.toLatin1().constData());
494 continue;
495 }
496 outn = inn.left(inn.length()-3);
497 }
498 QFile in(fileFixify(inn));
499 QFile out(fileFixify(outn, qmake_getpwd(), Option::output_dir));
500 if(in.open(QFile::ReadOnly)) {
501 QString contents;
502 QStack<int> state;
503 enum { IN_CONDITION, MET_CONDITION, PENDING_CONDITION };
504 for(int count = 1; !in.atEnd(); ++count) {
505 QString line = QString::fromUtf8(in.readLine());
506 if(line.startsWith("!!IF ")) {
507 if(state.isEmpty() || state.top() == IN_CONDITION) {
508 QString test = line.mid(5, line.length()-(5+1));
509 if(project->test(test))
510 state.push(IN_CONDITION);
511 else
512 state.push(PENDING_CONDITION);
513 } else {
514 state.push(MET_CONDITION);
515 }
516 } else if(line.startsWith("!!ELIF ")) {
517 if(state.isEmpty()) {
518 warn_msg(WarnLogic, "(%s:%d): Unexpected else condition",
519 in.fileName().toLatin1().constData(), count);
520 } else if(state.top() == PENDING_CONDITION) {
521 QString test = line.mid(7, line.length()-(7+1));
522 if(project->test(test)) {
523 state.pop();
524 state.push(IN_CONDITION);
525 }
526 } else if(state.top() == IN_CONDITION) {
527 state.pop();
528 state.push(MET_CONDITION);
529 }
530 } else if(line.startsWith("!!ELSE")) {
531 if(state.isEmpty()) {
532 warn_msg(WarnLogic, "(%s:%d): Unexpected else condition",
533 in.fileName().toLatin1().constData(), count);
534 } else if(state.top() == PENDING_CONDITION) {
535 state.pop();
536 state.push(IN_CONDITION);
537 } else if(state.top() == IN_CONDITION) {
538 state.pop();
539 state.push(MET_CONDITION);
540 }
541 } else if(line.startsWith("!!ENDIF")) {
542 if(state.isEmpty())
543 warn_msg(WarnLogic, "(%s:%d): Unexpected endif",
544 in.fileName().toLatin1().constData(), count);
545 else
546 state.pop();
547 } else if(state.isEmpty() || state.top() == IN_CONDITION) {
548 contents += project->expand(line).join(QString(Option::field_sep));
549 }
550 }
551 if(out.exists() && out.open(QFile::ReadOnly)) {
552 QString old = QString::fromUtf8(out.readAll());
553 if(contents == old) {
554 v["QMAKE_INTERNAL_INCLUDED_FILES"].append(in.fileName());
555 continue;
556 }
557 out.close();
558 if(!out.remove()) {
559 warn_msg(WarnLogic, "Cannot clear substitute '%s'",
560 out.fileName().toLatin1().constData());
561 continue;
562 }
563 }
564 mkdir(QFileInfo(out).absolutePath());
565 if(out.open(QFile::WriteOnly)) {
566 v["QMAKE_INTERNAL_INCLUDED_FILES"].append(in.fileName());
567 out.write(contents.toUtf8());
568 } else {
569 warn_msg(WarnLogic, "Cannot open substitute for output '%s'",
570 out.fileName().toLatin1().constData());
571 }
572 } else {
573 warn_msg(WarnLogic, "Cannot open substitute for input '%s'",
574 in.fileName().toLatin1().constData());
575 }
576 }
577 }
578
579 int x;
580
581 //build up a list of compilers
582 QList<Compiler> compilers;
583 {
584 const char *builtins[] = { "OBJECTS", "SOURCES", "PRECOMPILED_HEADER", 0 };
585 for(x = 0; builtins[x]; ++x) {
586 Compiler compiler;
587 compiler.variable_in = builtins[x];
588 compiler.flags = Compiler::CompilerBuiltin;
589 compiler.type = QMakeSourceFileInfo::TYPE_C;
590 if(!strcmp(builtins[x], "OBJECTS"))
591 compiler.flags |= Compiler::CompilerNoCheckDeps;
592 compilers.append(compiler);
593 }
594 for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
595 const QStringList &inputs = v[(*it) + ".input"];
596 for(x = 0; x < inputs.size(); ++x) {
597 Compiler compiler;
598 compiler.variable_in = inputs.at(x);
599 compiler.flags = Compiler::CompilerNoFlags;
600 if(v[(*it) + ".CONFIG"].indexOf("ignore_no_exist") != -1)
601 compiler.flags |= Compiler::CompilerRemoveNoExist;
602 if(v[(*it) + ".CONFIG"].indexOf("no_dependencies") != -1)
603 compiler.flags |= Compiler::CompilerNoCheckDeps;
604
605 QString dep_type;
606 if(!project->isEmpty((*it) + ".dependency_type"))
607 dep_type = project->first((*it) + ".dependency_type");
608 if (dep_type.isEmpty())
609 compiler.type = QMakeSourceFileInfo::TYPE_UNKNOWN;
610 else if(dep_type == "TYPE_UI")
611 compiler.type = QMakeSourceFileInfo::TYPE_UI;
612 else
613 compiler.type = QMakeSourceFileInfo::TYPE_C;
614 compilers.append(compiler);
615 }
616 }
617 }
618 { //do the path fixifying
619 QStringList paths;
620 for(x = 0; x < compilers.count(); ++x) {
621 if(!paths.contains(compilers.at(x).variable_in))
622 paths << compilers.at(x).variable_in;
623 }
624 paths << "INCLUDEPATH" << "QMAKE_INTERNAL_INCLUDED_FILES" << "PRECOMPILED_HEADER";
625 for(int y = 0; y < paths.count(); y++) {
626 QStringList &l = v[paths[y]];
627 for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
628 if((*it).isEmpty())
629 continue;
630 if(exists((*it)))
631 (*it) = fileFixify((*it));
632 }
633 }
634 }
635
636 if(noIO() || !doDepends())
637 QMakeSourceFileInfo::setDependencyMode(QMakeSourceFileInfo::NonRecursive);
638 for(x = 0; x < compilers.count(); ++x)
639 initCompiler(compilers.at(x));
640
641 //merge actual compiler outputs into their variable_out. This is done last so that
642 //files are already properly fixified.
643 for(QStringList::Iterator it = quc.begin(); it != quc.end(); ++it) {
644 QString tmp_out = project->values((*it) + ".output").first();
645 if(tmp_out.isEmpty())
646 continue;
647 if(project->values((*it) + ".CONFIG").indexOf("combine") != -1) {
648 QStringList &compilerInputs = project->values((*it) + ".input");
649 // Don't generate compiler output if it doesn't have input.
650 if (compilerInputs.isEmpty() || project->values(compilerInputs.first()).isEmpty())
651 continue;
652 if(tmp_out.indexOf("$") == -1) {
653 if(!verifyExtraCompiler((*it), QString())) //verify
654 continue;
655 QString out = fileFixify(tmp_out, Option::output_dir, Option::output_dir);
656 bool pre_dep = (project->values((*it) + ".CONFIG").indexOf("target_predeps") != -1);
657 if(project->variables().contains((*it) + ".variable_out")) {
658 const QStringList &var_out = project->variables().value((*it) + ".variable_out");
659 for(int i = 0; i < var_out.size(); ++i) {
660 QString v = var_out.at(i);
661 if(v == QLatin1String("SOURCES"))
662 v = "GENERATED_SOURCES";
663 else if(v == QLatin1String("OBJECTS"))
664 pre_dep = false;
665 QStringList &list = project->values(v);
666 if(!list.contains(out))
667 list.append(out);
668 }
669 } else if(project->values((*it) + ".CONFIG").indexOf("no_link") == -1) {
670 QStringList &list = project->values("OBJECTS");
671 pre_dep = false;
672 if(!list.contains(out))
673 list.append(out);
674 } else {
675 QStringList &list = project->values("UNUSED_SOURCES");
676 if(!list.contains(out))
677 list.append(out);
678 }
679 if(pre_dep) {
680 QStringList &list = project->variables()["PRE_TARGETDEPS"];
681 if(!list.contains(out))
682 list.append(out);
683 }
684 }
685 } else {
686 QStringList &tmp = project->values((*it) + ".input");
687 for(QStringList::Iterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
688 const QStringList inputs = project->values((*it2));
689 for(QStringList::ConstIterator input = inputs.constBegin(); input != inputs.constEnd(); ++input) {
690 if((*input).isEmpty())
691 continue;
692 QString in = Option::fixPathToTargetOS((*input), false);
693 if(!verifyExtraCompiler((*it), in)) //verify
694 continue;
695 QString out = replaceExtraCompilerVariables(tmp_out, (*input), QString());
696 out = fileFixify(out, Option::output_dir, Option::output_dir);
697 bool pre_dep = (project->values((*it) + ".CONFIG").indexOf("target_predeps") != -1);
698 if(project->variables().contains((*it) + ".variable_out")) {
699 const QStringList &var_out = project->variables().value((*it) + ".variable_out");
700 for(int i = 0; i < var_out.size(); ++i) {
701 QString v = var_out.at(i);
702 if(v == QLatin1String("SOURCES"))
703 v = "GENERATED_SOURCES";
704 else if(v == QLatin1String("OBJECTS"))
705 pre_dep = false;
706 QStringList &list = project->values(v);
707 if(!list.contains(out))
708 list.append(out);
709 }
710 } else if(project->values((*it) + ".CONFIG").indexOf("no_link") == -1) {
711 pre_dep = false;
712 QStringList &list = project->values("OBJECTS");
713 if(!list.contains(out))
714 list.append(out);
715 } else {
716 QStringList &list = project->values("UNUSED_SOURCES");
717 if(!list.contains(out))
718 list.append(out);
719 }
720 if(pre_dep) {
721 QStringList &list = project->variables()["PRE_TARGETDEPS"];
722 if(!list.contains(out))
723 list.append(out);
724 }
725 }
726 }
727 }
728 }
729
730 //handle dependencies
731 depHeuristicsCache.clear();
732 if(!noIO()) {
733 // dependency paths
734 QStringList incDirs = v["DEPENDPATH"] + v["QMAKE_ABSOLUTE_SOURCE_PATH"];
735 if(project->isActiveConfig("depend_includepath"))
736 incDirs += v["INCLUDEPATH"];
737 if(!project->isActiveConfig("no_include_pwd")) {
738 QString pwd = qmake_getpwd();
739 if(pwd.isEmpty())
740 pwd = ".";
741 incDirs += pwd;
742 }
743 QList<QMakeLocalFileName> deplist;
744 for(QStringList::Iterator it = incDirs.begin(); it != incDirs.end(); ++it)
745 deplist.append(QMakeLocalFileName(unescapeFilePath((*it))));
746 QMakeSourceFileInfo::setDependencyPaths(deplist);
747 debug_msg(1, "Dependency Directories: %s", incDirs.join(" :: ").toLatin1().constData());
748 //cache info
749 if(project->isActiveConfig("qmake_cache")) {
750 QString cache_file;
751 if(!project->isEmpty("QMAKE_INTERNAL_CACHE_FILE")) {
752 cache_file = QDir::fromNativeSeparators(project->first("QMAKE_INTERNAL_CACHE_FILE"));
753 } else {
754 cache_file = ".qmake.internal.cache";
755 if(project->isActiveConfig("build_pass"))
756 cache_file += ".BUILD." + project->first("BUILD_PASS");
757 }
758 if(cache_file.indexOf('/') == -1)
759 cache_file.prepend(Option::output_dir + '/');
760 QMakeSourceFileInfo::setCacheFile(cache_file);
761 }
762
763 //add to dependency engine
764 for(x = 0; x < compilers.count(); ++x) {
765 const MakefileGenerator::Compiler &comp = compilers.at(x);
766 if(!(comp.flags & Compiler::CompilerNoCheckDeps))
767 addSourceFiles(v[comp.variable_in], QMakeSourceFileInfo::SEEK_DEPS,
768 (QMakeSourceFileInfo::SourceFileType)comp.type);
769 }
770 }
771
772 processSources(); //remove anything in SOURCES which is included (thus it need not be linked in)
773
774 //all sources and generated sources must be turned into objects at some point (the one builtin compiler)
775 v["OBJECTS"] += createObjectList(v["SOURCES"]) + createObjectList(v["GENERATED_SOURCES"]);
776
777 //Translation files
778 if(!project->isEmpty("TRANSLATIONS")) {
779 QStringList &trf = project->values("TRANSLATIONS");
780 for(QStringList::Iterator it = trf.begin(); it != trf.end(); ++it)
781 (*it) = Option::fixPathToLocalOS((*it));
782 }
783
784 { //get the output_dir into the pwd
785 if(fileFixify(Option::output_dir) != fileFixify(qmake_getpwd()))
786 project->values("INCLUDEPATH").append(fileFixify(Option::output_dir,
787 Option::output_dir,
788 Option::output_dir));
789 }
790
791 //fix up the target deps
792 QString fixpaths[] = { QString("PRE_TARGETDEPS"), QString("POST_TARGETDEPS"), QString() };
793 for(int path = 0; !fixpaths[path].isNull(); path++) {
794 QStringList &l = v[fixpaths[path]];
795 for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it) {
796 if(!(*val_it).isEmpty())
797 (*val_it) = escapeDependencyPath(Option::fixPathToTargetOS((*val_it), false, false));
798 }
799 }
800
801 //extra depends
802 if(!project->isEmpty("DEPENDS")) {
803 QStringList &l = v["DEPENDS"];
804 for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
805 QStringList files = v[(*it) + ".file"] + v[(*it) + ".files"]; //why do I support such evil things?
806 for(QStringList::Iterator file_it = files.begin(); file_it != files.end(); ++file_it) {
807 QStringList &out_deps = findDependencies(*file_it);
808 QStringList &in_deps = v[(*it) + ".depends"]; //even more evilness..
809 for(QStringList::Iterator dep_it = in_deps.begin(); dep_it != in_deps.end(); ++dep_it) {
810 if(exists(*dep_it)) {
811 out_deps.append(*dep_it);
812 } else {
813 QString dir, regex = Option::fixPathToLocalOS((*dep_it));
814 if(regex.lastIndexOf(Option::dir_sep) != -1) {
815 dir = regex.left(regex.lastIndexOf(Option::dir_sep) + 1);
816 regex.remove(0, dir.length());
817 }
818 QStringList files = QDir(dir).entryList(QStringList(regex));
819 if(files.isEmpty()) {
820 warn_msg(WarnLogic, "Dependency for [%s]: Not found %s", (*file_it).toLatin1().constData(),
821 (*dep_it).toLatin1().constData());
822 } else {
823 for(int i = 0; i < files.count(); i++)
824 out_deps.append(dir + files[i]);
825 }
826 }
827 }
828 }
829 }
830 }
831
832 // escape qmake command
833 QStringList &qmk = project->values("QMAKE_QMAKE");
834 qmk = escapeFilePaths(qmk);
835}
836
837bool
838MakefileGenerator::processPrlFile(QString &file)
839{
840 bool ret = false, try_replace_file=false;
841 QString meta_file, orig_file = file;
842 if(QMakeMetaInfo::libExists(file)) {
843 try_replace_file = true;
844 meta_file = file;
845 file = "";
846 } else {
847 QString tmp = file;
848 int ext = tmp.lastIndexOf('.');
849 if(ext != -1)
850 tmp = tmp.left(ext);
851 meta_file = tmp;
852 }
853// meta_file = fileFixify(meta_file);
854 QString real_meta_file = Option::fixPathToLocalOS(meta_file);
855 if(!meta_file.isEmpty()) {
856 QString f = fileFixify(real_meta_file, qmake_getpwd(), Option::output_dir);
857 if(QMakeMetaInfo::libExists(f)) {
858 QMakeMetaInfo libinfo;
859 debug_msg(1, "Processing PRL file: %s", real_meta_file.toLatin1().constData());
860 if(!libinfo.readLib(f)) {
861 fprintf(stderr, "Error processing meta file: %s\n", real_meta_file.toLatin1().constData());
862 } else if(project->isActiveConfig("no_read_prl_" + libinfo.type().toLower())) {
863 debug_msg(2, "Ignored meta file %s [%s]", real_meta_file.toLatin1().constData(), libinfo.type().toLatin1().constData());
864 } else {
865 ret = true;
866 QMap<QString, QStringList> &vars = libinfo.variables();
867 for(QMap<QString, QStringList>::Iterator it = vars.begin(); it != vars.end(); ++it)
868 processPrlVariable(it.key(), it.value());
869 if(try_replace_file && !libinfo.isEmpty("QMAKE_PRL_TARGET")) {
870 QString dir;
871 int slsh = real_meta_file.lastIndexOf(Option::dir_sep);
872 if(slsh != -1)
873 dir = real_meta_file.left(slsh+1);
874 file = libinfo.first("QMAKE_PRL_TARGET");
875 if(QDir::isRelativePath(file))
876 file.prepend(dir);
877 }
878 }
879 }
880 if(ret) {
881 QString mf = QMakeMetaInfo::findLib(meta_file);
882 if(project->values("QMAKE_PRL_INTERNAL_FILES").indexOf(mf) == -1)
883 project->values("QMAKE_PRL_INTERNAL_FILES").append(mf);
884 if(project->values("QMAKE_INTERNAL_INCLUDED_FILES").indexOf(mf) == -1)
885 project->values("QMAKE_INTERNAL_INCLUDED_FILES").append(mf);
886 }
887 }
888 if(try_replace_file && file.isEmpty()) {
889#if 0
890 warn_msg(WarnLogic, "Found prl [%s] file with no target [%s]!", meta_file.toLatin1().constData(),
891 orig_file.toLatin1().constData());
892#endif
893 file = orig_file;
894 }
895 return ret;
896}
897
898void
899MakefileGenerator::filterIncludedFiles(const QString &var)
900{
901 QStringList &inputs = project->values(var);
902 for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ) {
903 if(QMakeSourceFileInfo::included((*input)) > 0)
904 input = inputs.erase(input);
905 else
906 ++input;
907 }
908}
909
910void
911MakefileGenerator::processPrlVariable(const QString &var, const QStringList &l)
912{
913 if(var == "QMAKE_PRL_LIBS") {
914 QString where = "QMAKE_LIBS";
915 if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS"))
916 where = project->first("QMAKE_INTERNAL_PRL_LIBS");
917 QStringList &out = project->values(where);
918 for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
919 if(out.indexOf((*it)) == -1)
920 out.append((*it));
921 }
922 } else if(var == "QMAKE_PRL_DEFINES") {
923 QStringList &out = project->values("DEFINES");
924 for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
925 if(out.indexOf((*it)) == -1 &&
926 project->values("PRL_EXPORT_DEFINES").indexOf((*it)) == -1)
927 out.append((*it));
928 }
929 }
930}
931
932void
933MakefileGenerator::processPrlFiles()
934{
935 QHash<QString, bool> processed;
936 for(bool ret = false; true; ret = false) {
937 //read in any prl files included..
938 QStringList l_out;
939 QString where = "QMAKE_LIBS";
940 if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS"))
941 where = project->first("QMAKE_INTERNAL_PRL_LIBS");
942 QStringList &l = project->values(where);
943 for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
944 QString file = (*it);
945 if(!processed.contains(file) && processPrlFile(file)) {
946 processed.insert(file, true);
947 ret = true;
948 }
949 if(!file.isEmpty())
950 l_out.append(file);
951 }
952 if(ret)
953 l = l_out;
954 else
955 break;
956 }
957}
958
959void
960MakefileGenerator::writePrlFile(QTextStream &t)
961{
962 QString target = project->first("TARGET");
963 int slsh = target.lastIndexOf(Option::dir_sep);
964 if(slsh != -1)
965 target.remove(0, slsh + 1);
966
967 if (Option::target_mode != Option::TARG_OS2_MODE) {
968 // QMAKE_PRL_BUILD_DIR is not used on OS/2 so don't add it to avoid
969 // possible confusion by exposing the full path to the build directory
970 QString bdir = Option::output_dir;
971 if(bdir.isEmpty())
972 bdir = qmake_getpwd();
973 t << "QMAKE_PRL_BUILD_DIR = " << bdir << endl;
974 }
975
976 if(!project->projectFile().isEmpty() && project->projectFile() != "-")
977 t << "QMAKE_PRO_INPUT = " << project->projectFile().section('/', -1) << endl;
978
979 if(!project->isEmpty("QMAKE_ABSOLUTE_SOURCE_PATH"))
980 t << "QMAKE_PRL_SOURCE_DIR = " << project->first("QMAKE_ABSOLUTE_SOURCE_PATH") << endl;
981
982 t << "QMAKE_PRL_TARGET = " << target << endl;
983 if(!project->isEmpty("TARGET_SHORT"))
984 t << "QMAKE_PRL_TARGET_SHORT = " << project->first("TARGET_SHORT") << endl;
985
986 if(!project->isEmpty("PRL_EXPORT_DEFINES"))
987 t << "QMAKE_PRL_DEFINES = " << project->values("PRL_EXPORT_DEFINES").join(" ") << endl;
988 if(!project->isEmpty("PRL_EXPORT_CFLAGS"))
989 t << "QMAKE_PRL_CFLAGS = " << project->values("PRL_EXPORT_CFLAGS").join(" ") << endl;
990 if(!project->isEmpty("PRL_EXPORT_CXXFLAGS"))
991 t << "QMAKE_PRL_CXXFLAGS = " << project->values("PRL_EXPORT_CXXFLAGS").join(" ") << endl;
992 if(!project->isEmpty("CONFIG"))
993 t << "QMAKE_PRL_CONFIG = " << project->values("CONFIG").join(" ") << endl;
994 if(!project->isEmpty("TARGET_VERSION_EXT"))
995 t << "QMAKE_PRL_VERSION = " << project->first("TARGET_VERSION_EXT") << endl;
996 else if(!project->isEmpty("VERSION"))
997 t << "QMAKE_PRL_VERSION = " << project->first("VERSION") << endl;
998 if(project->isActiveConfig("staticlib") || project->isActiveConfig("explicitlib") ||
999 !project->isEmpty("PRL_EXPORT_LIBS")) {
1000 t << "QMAKE_PRL_LIBS = ";
1001 if (!project->isEmpty("PRL_EXPORT_LIBS")) {
1002 // PRL_EXPORT_LIBS overrides anything else
1003 t << project->values("PRL_EXPORT_LIBS").join(" ");
1004 } else {
1005 QStringList libs;
1006 if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS"))
1007 libs = project->values("QMAKE_INTERNAL_PRL_LIBS");
1008 else
1009 libs << "QMAKE_LIBS"; //obvious one
1010 if(project->isActiveConfig("staticlib"))
1011 libs << "QMAKE_LIBS_PRIVATE";
1012 for(QStringList::Iterator it = libs.begin(); it != libs.end(); ++it)
1013 t << project->values((*it)).join(" ").replace('\\', "\\\\") << " ";
1014 }
1015 t << endl;
1016 }
1017}
1018
1019bool
1020MakefileGenerator::writeProjectMakefile()
1021{
1022 usePlatformDir();
1023 QTextStream t(&Option::output);
1024
1025 //header
1026 writeHeader(t);
1027
1028 QList<SubTarget*> targets;
1029 {
1030 QStringList builds = project->values("BUILDS");
1031 for(QStringList::Iterator it = builds.begin(); it != builds.end(); ++it) {
1032 SubTarget *st = new SubTarget;
1033 targets.append(st);
1034 st->makefile = "$(MAKEFILE)." + (*it);
1035 st->name = (*it);
1036 st->target = project->isEmpty((*it) + ".target") ? (*it) : project->first((*it) + ".target");
1037 }
1038 }
1039 if(project->isActiveConfig("build_all")) {
1040 t << "first: all" << endl;
1041 QList<SubTarget*>::Iterator it;
1042
1043 //install
1044 t << "install: ";
1045 for(it = targets.begin(); it != targets.end(); ++it)
1046 t << (*it)->target << "-install ";
1047 t << endl;
1048
1049 //uninstall
1050 t << "uninstall: ";
1051 for(it = targets.begin(); it != targets.end(); ++it)
1052 t << (*it)->target << "-uninstall ";
1053 t << endl;
1054 } else {
1055 t << "first: " << targets.first()->target << endl
1056 << "install: " << targets.first()->target << "-install" << endl
1057 << "uninstall: " << targets.first()->target << "-uninstall" << endl;
1058 }
1059
1060 writeSubTargets(t, targets, SubTargetsNoFlags);
1061 if(!project->isActiveConfig("no_autoqmake")) {
1062 for(QList<SubTarget*>::Iterator it = targets.begin(); it != targets.end(); ++it)
1063 t << (*it)->makefile << ": " <<
1064 Option::fixPathToTargetOS(fileFixify(Option::output.fileName())) << endl;
1065 }
1066 qDeleteAll(targets);
1067 return true;
1068}
1069
1070bool
1071MakefileGenerator::write()
1072{
1073 if(!project)
1074 return false;
1075 writePrlFile();
1076 if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE || //write makefile
1077 Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
1078 QTextStream t(&Option::output);
1079 if(!writeMakefile(t)) {
1080#if 1
1081 warn_msg(WarnLogic, "Unable to generate output for: %s [TEMPLATE %s]",
1082 Option::output.fileName().toLatin1().constData(),
1083 project->first("TEMPLATE").toLatin1().constData());
1084 if(Option::output.exists())
1085 Option::output.remove();
1086#endif
1087 }
1088 }
1089 return true;
1090}
1091
1092QString
1093MakefileGenerator::prlFileName(bool fixify)
1094{
1095 QString ret = project->first("TARGET_PRL");;
1096 if(ret.isEmpty())
1097 ret = project->first("TARGET");
1098 int slsh = ret.lastIndexOf(Option::dir_sep);
1099 if(slsh != -1)
1100 ret.remove(0, slsh);
1101 if(!ret.endsWith(Option::prl_ext)) {
1102 int dot = ret.indexOf('.');
1103 if(dot != -1)
1104 ret.truncate(dot);
1105 ret += Option::prl_ext;
1106 }
1107 if(!project->isEmpty("QMAKE_BUNDLE"))
1108 ret.prepend(project->first("QMAKE_BUNDLE") + Option::dir_sep);
1109 if(fixify) {
1110 if(!project->isEmpty("DESTDIR"))
1111 ret.prepend(project->first("DESTDIR"));
1112 ret = Option::fixPathToLocalOS(fileFixify(ret, qmake_getpwd(), Option::output_dir));
1113 }
1114 return ret;
1115}
1116
1117void
1118MakefileGenerator::writePrlFile()
1119{
1120 if((Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
1121 Option::qmake_mode == Option::QMAKE_GENERATE_PRL)
1122 && project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()
1123 && project->isActiveConfig("create_prl")
1124 && (project->first("TEMPLATE") == "lib"
1125 || project->first("TEMPLATE") == "vclib")
1126 && !project->isActiveConfig("plugin")) { //write prl file
1127 QString local_prl = prlFileName();
1128 QString prl = fileFixify(local_prl);
1129 mkdir(fileInfo(local_prl).path());
1130 QFile ft(local_prl);
1131 if(ft.open(QIODevice::WriteOnly)) {
1132 project->values("ALL_DEPS").append(prl);
1133 project->values("QMAKE_INTERNAL_PRL_FILE").append(prl);
1134 QTextStream t(&ft);
1135 writePrlFile(t);
1136 }
1137 }
1138}
1139
1140// Manipulate directories, so it's possible to build
1141// several cross-platform targets concurrently
1142void
1143MakefileGenerator::usePlatformDir()
1144{
1145 QString pltDir(project->first("QMAKE_PLATFORM_DIR"));
1146 if(pltDir.isEmpty())
1147 return;
1148 QChar sep = QDir::separator();
1149 QString slashPltDir = sep + pltDir;
1150
1151 QString dirs[] = { QString("OBJECTS_DIR"), QString("DESTDIR"), QString("QMAKE_PKGCONFIG_DESTDIR"),
1152 QString("SUBLIBS_DIR"), QString("DLLDESTDIR"), QString("QMAKE_LIBTOOL_DESTDIR"),
1153 QString("PRECOMPILED_DIR"), QString("QMAKE_LIBDIR_QT"), QString() };
1154 for(int i = 0; !dirs[i].isEmpty(); ++i) {
1155 QString filePath = project->first(dirs[i]);
1156 project->values(dirs[i]) = QStringList(filePath + (filePath.isEmpty() ? pltDir : slashPltDir));
1157 }
1158
1159 QString libs[] = { QString("QMAKE_LIBS_QT"), QString("QMAKE_LIBS_QT_THREAD"), QString("QMAKE_LIBS_QT_ENTRY"), QString() };
1160 for(int i = 0; !libs[i].isEmpty(); ++i) {
1161 QString filePath = project->first(libs[i]);
1162 int fpi = filePath.lastIndexOf(sep);
1163 if(fpi == -1)
1164 project->values(libs[i]).prepend(pltDir + sep);
1165 else
1166 project->values(libs[i]) = QStringList(filePath.left(fpi) + slashPltDir + filePath.mid(fpi));
1167 }
1168}
1169
1170void
1171MakefileGenerator::writeObj(QTextStream &t, const QString &src)
1172{
1173 QStringList &srcl = project->values(src);
1174 QStringList objl = createObjectList(srcl);
1175
1176 QStringList::Iterator oit = objl.begin();
1177 QStringList::Iterator sit = srcl.begin();
1178 QString stringSrc("$src");
1179 QString stringObj("$obj");
1180 for(;sit != srcl.end() && oit != objl.end(); ++oit, ++sit) {
1181 if((*sit).isEmpty())
1182 continue;
1183
1184 t << escapeDependencyPath((*oit)) << ": " << escapeDependencyPath((*sit)) << " " << escapeDependencyPaths(findDependencies((*sit))).join(" \\\n\t\t");
1185
1186 QString comp, cimp;
1187 for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) {
1188 if((*sit).endsWith((*cppit))) {
1189 comp = "QMAKE_RUN_CXX";
1190 cimp = "QMAKE_RUN_CXX_IMP";
1191 break;
1192 }
1193 }
1194 if(comp.isEmpty()) {
1195 comp = "QMAKE_RUN_CC";
1196 cimp = "QMAKE_RUN_CC_IMP";
1197 }
1198 bool use_implicit_rule = !project->isEmpty(cimp);
1199 use_implicit_rule = false;
1200 if(use_implicit_rule) {
1201 if(!project->isEmpty("OBJECTS_DIR")) {
1202 use_implicit_rule = false;
1203 } else {
1204 int dot = (*sit).lastIndexOf('.');
1205 if(dot == -1 || ((*sit).left(dot) + Option::obj_ext != (*oit)))
1206 use_implicit_rule = false;
1207 }
1208 }
1209 if (!use_implicit_rule && !project->isEmpty(comp)) {
1210 QString p = var(comp), srcf(*sit);
1211 p.replace(stringSrc, escapeFilePath(srcf));
1212 p.replace(stringObj, escapeFilePath((*oit)));
1213 t << "\n\t" << p;
1214 }
1215 t << endl << endl;
1216 }
1217}
1218
1219QString
1220MakefileGenerator::filePrefixRoot(const QString &root, const QString &path)
1221{
1222 QString ret(root + path);
1223 if(path.length() > 2 && path[1] == ':') { //c:\foo
1224 if (Option::target_mode == Option::TARG_OS2_MODE)
1225 ret = root + path.mid(2);
1226 else
1227 ret = path.mid(0, 2) + root + path.mid(2);
1228 }
1229 while(ret.endsWith("\\"))
1230 ret = ret.left(ret.length()-1);
1231 return ret;
1232}
1233
1234void
1235MakefileGenerator::writeInstalls(QTextStream &t, const QString &installs, bool noBuild)
1236{
1237 const QString del_suffix =
1238 Option::target_mode == Option::TARG_OS2_MODE ?
1239 QString(" >nul 2>&1"): // reduce noise
1240 QString::null;
1241
1242 const QString inst_prefix =
1243 Option::target_mode == Option::TARG_OS2_MODE ?
1244 QString::null: // report errors (copy command overwrites quietly)
1245 QString("-");
1246
1247 QString rm_dir_contents("-$(DEL_FILE)");
1248 if (!isDosLikeShell()) //ick
1249 rm_dir_contents = "-$(DEL_FILE) -r";
1250
1251 QString all_installs, all_uninstalls;
1252 QStringList &l = project->values(installs);
1253 for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
1254 const QStringList &installConfigValues = project->values((*it) + ".CONFIG");
1255 QString pvar = (*it) + ".path";
1256 if(installConfigValues.indexOf("no_path") == -1 &&
1257 installConfigValues.indexOf("dummy_install") == -1 &&
1258 project->values(pvar).isEmpty()) {
1259 warn_msg(WarnLogic, "%s is not defined: install target not created\n", pvar.toLatin1().constData());
1260 continue;
1261 }
1262
1263 bool do_default = true;
1264 const QString root = "$(INSTALL_ROOT)";
1265 QString target, dst;
1266 if(installConfigValues.indexOf("no_path") == -1 &&
1267 installConfigValues.indexOf("dummy_install") == -1) {
1268 dst = fileFixify(unescapeFilePath(project->values(pvar).first()), FileFixifyAbsolute, false);
1269 }
1270 dst = escapeFilePath(dst);
1271 if(dst.right(Option::dir_sep.length()) != Option::dir_sep)
1272 dst += Option::dir_sep;
1273
1274 QStringList tmp, uninst = project->values((*it) + ".uninstall");
1275 //other
1276 tmp = project->values((*it) + ".extra");
1277 if(tmp.isEmpty())
1278 tmp = project->values((*it) + ".commands"); //to allow compatible name
1279 if(!tmp.isEmpty()) {
1280 do_default = false;
1281 if(!target.isEmpty())
1282 target += "\n\t";
1283 target += tmp.join(" ");
1284 }
1285 //masks
1286 tmp = project->values((*it) + ".files");
1287 if(!installConfigValues.contains("no_vpath"))
1288 tmp = findFilesInVPATH(tmp, VPATH_NoFixify);
1289 if(!installConfigValues.contains("no_fixify"))
1290 tmp = fileFixify(tmp, FileFixifyAbsolute);
1291 if(!tmp.isEmpty()) {
1292 if(!target.isEmpty())
1293 target += "\n";
1294 do_default = false;
1295 for(QStringList::Iterator wild_it = tmp.begin(); wild_it != tmp.end(); ++wild_it) {
1296 QString wild = Option::fixPathToTargetOS((*wild_it), false, false);
1297 QString dirstr = qmake_getpwd(), filestr = wild;
1298 int slsh = filestr.lastIndexOf(Option::dir_sep);
1299 if(slsh != -1) {
1300 dirstr = filestr.left(slsh+1);
1301 filestr.remove(0, slsh+1);
1302 }
1303 if(!dirstr.endsWith(Option::dir_sep))
1304 dirstr += Option::dir_sep;
1305 bool is_target = QFileInfo(wild) ==
1306 QFileInfo(Option::fixPathToTargetOS(QDir(Option::output_dir)
1307 .absoluteFilePath(var("DEST_TARGET"))));
1308 if(is_target || exists(wild)) { //real file or target
1309 QString file = wild;
1310 QFileInfo fi(fileInfo(wild));
1311 if(!target.isEmpty())
1312 target += "\t";
1313 QString dst_file = filePrefixRoot(root, dst);
1314 if(fi.isDir() && project->isActiveConfig("copy_dir_files")) {
1315 if(!dst_file.endsWith(Option::dir_sep))
1316 dst_file += Option::dir_sep;
1317 dst_file += fi.fileName();
1318 }
1319 QString cmd;
1320 if (fi.isDir())
1321 cmd = inst_prefix + "$(INSTALL_DIR)";
1322 else if (is_target || fi.isExecutable())
1323 cmd = inst_prefix + "$(INSTALL_PROGRAM)";
1324 else
1325 cmd = inst_prefix + "$(INSTALL_FILE)";
1326 cmd += " " + escapeFilePath(wild) + " " + dst_file + "\n";
1327 target += cmd;
1328 if(!project->isActiveConfig("debug") && !project->isActiveConfig("nostrip") &&
1329 !fi.isDir() && fi.isExecutable() && !project->isEmpty("QMAKE_STRIP"))
1330 target += QString("\t-") + var("QMAKE_STRIP") + " " +
1331 filePrefixRoot(root, fileFixify(dst + filestr, FileFixifyAbsolute, false)) + "\n";
1332 if(!uninst.isEmpty())
1333 uninst.append("\n\t");
1334 uninst.append(rm_dir_contents + " " + filePrefixRoot(root, fileFixify(dst + filestr, FileFixifyAbsolute, false)) + del_suffix);
1335 continue;
1336 }
1337 QString local_dirstr = Option::fixPathToLocalOS(dirstr, true);
1338 QStringList files = QDir(local_dirstr).entryList(QStringList(filestr));
1339 if (installConfigValues.contains("no_check_exist") && files.isEmpty()) {
1340 if(!target.isEmpty())
1341 target += "\t";
1342 QString dst_file = filePrefixRoot(root, dst);
1343 QFileInfo fi(fileInfo(wild));
1344 QString cmd;
1345 if (installConfigValues.contains("directory")) {
1346 cmd = inst_prefix + QLatin1String("$(INSTALL_DIR)");
1347 if (!dst_file.endsWith(Option::dir_sep))
1348 dst_file += Option::dir_sep;
1349 dst_file += fi.fileName();
1350 } else if (installConfigValues.contains("executable")) {
1351 cmd = inst_prefix + QLatin1String("$(INSTALL_PROGRAM)");
1352 } else if (installConfigValues.contains("data")) {
1353 cmd = inst_prefix + QLatin1String("$(INSTALL_FILE)");
1354 } else {
1355 cmd = inst_prefix + QString(fi.isExecutable() ? "$(INSTALL_PROGRAM)" : "$(INSTALL_FILE)");
1356 }
1357 cmd += " " + wild + " " + dst_file + "\n";
1358 target += cmd;
1359 if(!uninst.isEmpty())
1360 uninst.append("\n\t");
1361 uninst.append(rm_dir_contents + " " + filePrefixRoot(root, fileFixify(dst + filestr, FileFixifyAbsolute, false)) + del_suffix);
1362 }
1363 for(int x = 0; x < files.count(); x++) {
1364 QString file = files[x];
1365 if(file == "." || file == "..") //blah
1366 continue;
1367 if(!uninst.isEmpty())
1368 uninst.append("\n\t");
1369 uninst.append(rm_dir_contents + " " + filePrefixRoot(root, fileFixify(dst + file, FileFixifyAbsolute, false)) + del_suffix);
1370 QFileInfo fi(fileInfo(dirstr + file));
1371 if(!target.isEmpty())
1372 target += "\t";
1373 QString dst_file = filePrefixRoot(root, fileFixify(dst, FileFixifyAbsolute, false));
1374 if(fi.isDir() && project->isActiveConfig("copy_dir_files")) {
1375 if(!dst_file.endsWith(Option::dir_sep))
1376 dst_file += Option::dir_sep;
1377 dst_file += fi.fileName();
1378 }
1379 QString cmd = inst_prefix + QString(fi.isDir() ? "$(INSTALL_DIR)" : "$(INSTALL_FILE)") + " " +
1380 dirstr + file + " " + dst_file + "\n";
1381 target += cmd;
1382 if(!project->isActiveConfig("debug") && !project->isActiveConfig("nostrip") &&
1383 !fi.isDir() && fi.isExecutable() && !project->isEmpty("QMAKE_STRIP"))
1384 target += QString("\t-") + var("QMAKE_STRIP") + " " +
1385 filePrefixRoot(root, fileFixify(dst + file, FileFixifyAbsolute, false)) +
1386 "\n";
1387 }
1388 }
1389 }
1390 //default?
1391 if(do_default) {
1392 target = defaultInstall((*it));
1393 uninst = project->values((*it) + ".uninstall");
1394 }
1395
1396 if(!target.isEmpty() || installConfigValues.indexOf("dummy_install") != -1) {
1397 if(noBuild || installConfigValues.indexOf("no_build") != -1)
1398 t << "install_" << (*it) << ":";
1399 else if(project->isActiveConfig("build_all"))
1400 t << "install_" << (*it) << ": all";
1401 else
1402 t << "install_" << (*it) << ": first";
1403 const QStringList &deps = project->values((*it) + ".depends");
1404 if(!deps.isEmpty()) {
1405 for(QStringList::ConstIterator dep_it = deps.begin(); dep_it != deps.end(); ++dep_it) {
1406 QString targ = var((*dep_it) + ".target");
1407 if(targ.isEmpty())
1408 targ = (*dep_it);
1409 t << " " << escapeDependencyPath(targ);
1410 }
1411 }
1412 if(project->isEmpty("QMAKE_NOFORCE"))
1413 t << " FORCE";
1414 t << "\n\t";
1415 const QStringList &dirs = project->values(pvar);
1416 for(QStringList::ConstIterator pit = dirs.begin(); pit != dirs.end(); ++pit) {
1417 QString tmp_dst = fileFixify((*pit), FileFixifyAbsolute, false);
1418 if (!isDosLikeShell() && !tmp_dst.endsWith(Option::dir_sep))
1419 tmp_dst += Option::dir_sep;
1420 t << mkdir_p_asstring(filePrefixRoot(root, tmp_dst)) << "\n\t";
1421 }
1422 t << target << endl << endl;
1423 if(!uninst.isEmpty()) {
1424 t << "uninstall_" << (*it) << ": ";
1425 if(project->isEmpty("QMAKE_NOFORCE"))
1426 t << " FORCE";
1427 t << "\n\t"
1428 << uninst.join(" ") << "\n\t"
1429 << "-$(DEL_DIR) " << filePrefixRoot(root, dst) << " " << del_suffix << endl << endl;
1430 }
1431 t << endl;
1432
1433 if(installConfigValues.indexOf("no_default_install") == -1) {
1434 all_installs += QString("install_") + (*it) + " ";
1435 if(!uninst.isEmpty())
1436 all_uninstalls += "uninstall_" + (*it) + " ";
1437 }
1438 } else {
1439 debug_msg(1, "no definition for install %s: install target not created",(*it).toLatin1().constData());
1440 }
1441 }
1442 t << "install: " << var("INSTALLDEPS") << " " << all_installs;
1443 if(project->isEmpty("QMAKE_NOFORCE"))
1444 t << " FORCE";
1445 t << "\n\n";
1446 t << "uninstall: " << all_uninstalls << " " << var("UNINSTALLDEPS");
1447 if(project->isEmpty("QMAKE_NOFORCE"))
1448 t << " FORCE";
1449 t << "\n\n";
1450}
1451
1452QString
1453MakefileGenerator::var(const QString &var)
1454{
1455 return val(project->values(var));
1456}
1457
1458QString
1459MakefileGenerator::val(const QStringList &varList)
1460{
1461 return valGlue(varList, "", " ", "");
1462}
1463
1464QString
1465MakefileGenerator::varGlue(const QString &var, const QString &before, const QString &glue, const QString &after)
1466{
1467 return valGlue(project->values(var), before, glue, after);
1468}
1469
1470QString
1471MakefileGenerator::valGlue(const QStringList &varList, const QString &before, const QString &glue, const QString &after)
1472{
1473 QString ret;
1474 for(QStringList::ConstIterator it = varList.begin(); it != varList.end(); ++it) {
1475 if(!(*it).isEmpty()) {
1476 if(!ret.isEmpty())
1477 ret += glue;
1478 ret += (*it);
1479 }
1480 }
1481 return ret.isEmpty() ? QString("") : before + ret + after;
1482}
1483
1484
1485QString
1486MakefileGenerator::varList(const QString &var)
1487{
1488 return valList(project->values(var));
1489}
1490
1491QString
1492MakefileGenerator::valList(const QStringList &varList)
1493{
1494 return valGlue(varList, "", " \\\n\t\t", "");
1495}
1496
1497QStringList
1498MakefileGenerator::createObjectList(const QStringList &sources)
1499{
1500 QStringList ret;
1501 QString objdir;
1502 if(!project->values("OBJECTS_DIR").isEmpty())
1503 objdir = project->first("OBJECTS_DIR");
1504 for(QStringList::ConstIterator it = sources.begin(); it != sources.end(); ++it) {
1505 QFileInfo fi(fileInfo(Option::fixPathToLocalOS((*it))));
1506 QString dir;
1507 if(objdir.isEmpty() && project->isActiveConfig("object_with_source")) {
1508 QString fName = Option::fixPathToTargetOS((*it), false);
1509 int dl = fName.lastIndexOf(Option::dir_sep);
1510 if(dl != -1)
1511 dir = fName.left(dl + 1);
1512 } else {
1513 dir = objdir;
1514 }
1515 ret.append(dir + fi.completeBaseName() + Option::obj_ext);
1516 }
1517 return ret;
1518}
1519
1520ReplaceExtraCompilerCacheKey::ReplaceExtraCompilerCacheKey(const QString &v, const QStringList &i, const QStringList &o)
1521{
1522 hash = 0;
1523 pwd = qmake_getpwd();
1524 var = v;
1525 {
1526 QStringList il = i;
1527 il.sort();
1528 in = il.join("::");
1529 }
1530 {
1531 QStringList ol = o;
1532 ol.sort();
1533 out = ol.join("::");
1534 }
1535}
1536
1537bool ReplaceExtraCompilerCacheKey::operator==(const ReplaceExtraCompilerCacheKey &f) const
1538{
1539 return (hashCode() == f.hashCode() &&
1540 f.in == in &&
1541 f.out == out &&
1542 f.var == var &&
1543 f.pwd == pwd);
1544}
1545
1546
1547QString
1548MakefileGenerator::replaceExtraCompilerVariables(const QString &orig_var, const QStringList &in, const QStringList &out)
1549{
1550 //lazy cache
1551 ReplaceExtraCompilerCacheKey cacheKey(orig_var, in, out);
1552 QString cacheVal = extraCompilerVariablesCache.value(cacheKey);
1553 if(!cacheVal.isNull())
1554 return cacheVal;
1555
1556 //do the work
1557 QString ret = orig_var;
1558 QRegExp reg_var("\\$\\{.*\\}");
1559 reg_var.setMinimal(true);
1560 for(int rep = 0; (rep = reg_var.indexIn(ret, rep)) != -1; ) {
1561 QStringList val;
1562 const QString var = ret.mid(rep + 2, reg_var.matchedLength() - 3);
1563 bool filePath = false;
1564 if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_VAR_"))) {
1565 const QString varname = var.mid(10);
1566 val += project->values(varname);
1567 }
1568 if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_VAR_FIRST_"))) {
1569 const QString varname = var.mid(16);
1570 val += project->first(varname);
1571 }
1572
1573 if(val.isEmpty() && !in.isEmpty()) {
1574 if(var.startsWith(QLatin1String("QMAKE_FUNC_FILE_IN_"))) {
1575 filePath = true;
1576 const QString funcname = var.mid(19);
1577 val += project->expand(funcname, QList<QStringList>() << in);
1578 } else if(var == QLatin1String("QMAKE_FILE_BASE") || var == QLatin1String("QMAKE_FILE_IN_BASE")) {
1579 //filePath = true;
1580 for(int i = 0; i < in.size(); ++i) {
1581 QFileInfo fi(fileInfo(Option::fixPathToLocalOS(in.at(i))));
1582 QString base = fi.completeBaseName();
1583 if(base.isNull())
1584 base = fi.fileName();
1585 val += base;
1586 }
1587 } else if(var == QLatin1String("QMAKE_FILE_EXT")) {
1588 filePath = true;
1589 for(int i = 0; i < in.size(); ++i) {
1590 QFileInfo fi(fileInfo(Option::fixPathToLocalOS(in.at(i))));
1591 QString ext;
1592 // Ensure complementarity with QMAKE_FILE_BASE
1593 int baseLen = fi.completeBaseName().length();
1594 if(baseLen == 0)
1595 ext = fi.fileName();
1596 else
1597 ext = fi.fileName().remove(0, baseLen);
1598 val += ext;
1599 }
1600 } else if(var == QLatin1String("QMAKE_FILE_PATH") || var == QLatin1String("QMAKE_FILE_IN_PATH")) {
1601 filePath = true;
1602 for(int i = 0; i < in.size(); ++i)
1603 val += fileInfo(Option::fixPathToLocalOS(in.at(i))).path();
1604 } else if(var == QLatin1String("QMAKE_FILE_NAME") || var == QLatin1String("QMAKE_FILE_IN")) {
1605 filePath = true;
1606 for(int i = 0; i < in.size(); ++i)
1607 val += fileInfo(Option::fixPathToLocalOS(in.at(i))).filePath();
1608
1609 }
1610 }
1611 if(val.isEmpty() && !out.isEmpty()) {
1612 if(var.startsWith(QLatin1String("QMAKE_FUNC_FILE_OUT_"))) {
1613 filePath = true;
1614 const QString funcname = var.mid(20);
1615 val += project->expand(funcname, QList<QStringList>() << out);
1616 } else if(var == QLatin1String("QMAKE_FILE_OUT")) {
1617 filePath = true;
1618 for(int i = 0; i < out.size(); ++i)
1619 val += fileInfo(Option::fixPathToLocalOS(out.at(i))).filePath();
1620 } else if(var == QLatin1String("QMAKE_FILE_OUT_BASE")) {
1621 //filePath = true;
1622 for(int i = 0; i < out.size(); ++i) {
1623 QFileInfo fi(fileInfo(Option::fixPathToLocalOS(out.at(i))));
1624 QString base = fi.completeBaseName();
1625 if(base.isNull())
1626 base = fi.fileName();
1627 val += base;
1628 }
1629 }
1630 }
1631 if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_FUNC_"))) {
1632 const QString funcname = var.mid(11);
1633 val += project->expand(funcname, QList<QStringList>() << in << out);
1634 }
1635
1636 if(!val.isEmpty()) {
1637 QString fullVal;
1638 if(filePath) {
1639 for(int i = 0; i < val.size(); ++i) {
1640 const QString file = Option::fixPathToTargetOS(unescapeFilePath(val.at(i)), false);
1641 if(!fullVal.isEmpty())
1642 fullVal += " ";
1643 fullVal += escapeFilePath(file);
1644 }
1645 } else {
1646 fullVal = val.join(" ");
1647 }
1648 ret.replace(rep, reg_var.matchedLength(), fullVal);
1649 rep += fullVal.length();
1650 } else {
1651 rep += reg_var.matchedLength();
1652 }
1653 }
1654
1655 //cache the value
1656 extraCompilerVariablesCache.insert(cacheKey, ret);
1657 return ret;
1658}
1659
1660bool
1661MakefileGenerator::verifyExtraCompiler(const QString &comp, const QString &file_unfixed)
1662{
1663 if(noIO())
1664 return false;
1665 const QString file = Option::fixPathToLocalOS(file_unfixed);
1666
1667 if(project->values(comp + ".CONFIG").indexOf("moc_verify") != -1) {
1668 if(!file.isNull()) {
1669 QMakeSourceFileInfo::addSourceFile(file, QMakeSourceFileInfo::SEEK_MOCS);
1670 if(!mocable(file)) {
1671 return false;
1672 } else {
1673 project->values("MOCABLES").append(file);
1674 }
1675 }
1676 } else if(project->values(comp + ".CONFIG").indexOf("function_verify") != -1) {
1677 QString tmp_out = project->values(comp + ".output").first();
1678 if(tmp_out.isEmpty())
1679 return false;
1680 QStringList verify_function = project->values(comp + ".verify_function");
1681 if(verify_function.isEmpty())
1682 return false;
1683
1684 for(int i = 0; i < verify_function.size(); ++i) {
1685 bool invert = false;
1686 QString verify = verify_function.at(i);
1687 if(verify.at(0) == QLatin1Char('!')) {
1688 invert = true;
1689 verify = verify.mid(1);
1690 }
1691
1692 if(project->values(comp + ".CONFIG").indexOf("combine") != -1) {
1693 bool pass = project->test(verify, QList<QStringList>() << QStringList(tmp_out) << QStringList(file));
1694 if(invert)
1695 pass = !pass;
1696 if(!pass)
1697 return false;
1698 } else {
1699 QStringList &tmp = project->values(comp + ".input");
1700 for(QStringList::Iterator it = tmp.begin(); it != tmp.end(); ++it) {
1701 QStringList &inputs = project->values((*it));
1702 for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
1703 if((*input).isEmpty())
1704 continue;
1705 QString in = fileFixify(Option::fixPathToTargetOS((*input), false));
1706 if(in == file) {
1707 bool pass = project->test(verify,
1708 QList<QStringList>() << QStringList(replaceExtraCompilerVariables(tmp_out, (*input), QString())) <<
1709 QStringList(file));
1710 if(invert)
1711 pass = !pass;
1712 if(!pass)
1713 return false;
1714 break;
1715 }
1716 }
1717 }
1718 }
1719 }
1720 } else if(project->values(comp + ".CONFIG").indexOf("verify") != -1) {
1721 QString tmp_out = project->values(comp + ".output").first();
1722 if(tmp_out.isEmpty())
1723 return false;
1724 QString tmp_cmd;
1725 if(!project->isEmpty(comp + ".commands")) {
1726 int argv0 = -1;
1727 QStringList cmdline = project->values(comp + ".commands");
1728 for(int i = 0; i < cmdline.count(); ++i) {
1729 if(!cmdline.at(i).contains('=')) {
1730 argv0 = i;
1731 break;
1732 }
1733 }
1734 if(argv0 != -1) {
1735 cmdline[argv0] = Option::fixPathToTargetOS(cmdline.at(argv0), false);
1736 tmp_cmd = cmdline.join(" ");
1737 }
1738 }
1739
1740 if(project->values(comp + ".CONFIG").indexOf("combine") != -1) {
1741 QString cmd = replaceExtraCompilerVariables(tmp_cmd, QString(), tmp_out);
1742 if(system(cmd.toLatin1().constData()))
1743 return false;
1744 } else {
1745 QStringList &tmp = project->values(comp + ".input");
1746 for(QStringList::Iterator it = tmp.begin(); it != tmp.end(); ++it) {
1747 QStringList &inputs = project->values((*it));
1748 for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
1749 if((*input).isEmpty())
1750 continue;
1751 QString in = fileFixify(Option::fixPathToTargetOS((*input), false));
1752 if(in == file) {
1753 QString out = replaceExtraCompilerVariables(tmp_out, (*input), QString());
1754 QString cmd = replaceExtraCompilerVariables(tmp_cmd, in, out);
1755 if(system(cmd.toLatin1().constData()))
1756 return false;
1757 break;
1758 }
1759 }
1760 }
1761 }
1762 }
1763 return true;
1764}
1765
1766void
1767MakefileGenerator::writeExtraTargets(QTextStream &t)
1768{
1769 QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
1770 for(QStringList::Iterator it = qut.begin(); it != qut.end(); ++it) {
1771 QString targ = var((*it) + ".target"),
1772 cmd = var((*it) + ".commands"), deps;
1773 if(targ.isEmpty())
1774 targ = (*it);
1775 QStringList &deplist = project->values((*it) + ".depends");
1776 for(QStringList::Iterator dep_it = deplist.begin(); dep_it != deplist.end(); ++dep_it) {
1777 QString dep = var((*dep_it) + ".target");
1778 if(dep.isEmpty())
1779 dep = (*dep_it);
1780 deps += " " + escapeDependencyPath(dep);
1781 }
1782 if(project->values((*it) + ".CONFIG").indexOf("fix_target") != -1)
1783 targ = fileFixify(targ);
1784 if(project->isEmpty("QMAKE_NOFORCE") &&
1785 project->values((*it) + ".CONFIG").indexOf("phony") != -1)
1786 deps += QString(" ") + "FORCE";
1787 t << escapeDependencyPath(targ) << ":" << deps;
1788 if(!cmd.isEmpty())
1789 t << "\n\t" << cmd;
1790 t << endl << endl;
1791
1792 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(targ);
1793 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(targ)) << deps.split(" ", QString::SkipEmptyParts);
1794 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(targ)) << cmd;
1795 }
1796}
1797
1798void
1799MakefileGenerator::writeExtraCompilerTargets(QTextStream &t)
1800{
1801 QString clean_targets;
1802 const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
1803 for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
1804 QString tmp_out = fileFixify(project->values((*it) + ".output").first(),
1805 Option::output_dir, Option::output_dir);
1806 QString tmp_cmd;
1807 if(!project->isEmpty((*it) + ".commands")) {
1808 QStringList cmdline = project->values((*it) + ".commands");
1809 int argv0 = findExecutable(cmdline);
1810 if(argv0 != -1) {
1811 cmdline[argv0] = escapeFilePath(Option::fixPathToTargetOS(cmdline.at(argv0), false));
1812 tmp_cmd = cmdline.join(" ");
1813 }
1814 }
1815 QStringList tmp_dep = project->values((*it) + ".depends");
1816 QString tmp_dep_cmd;
1817 QString dep_cd_cmd;
1818 if(!project->isEmpty((*it) + ".depend_command")) {
1819 int argv0 = -1;
1820 QStringList cmdline = project->values((*it) + ".depend_command");
1821 for(int i = 0; i < cmdline.count(); ++i) {
1822 if(!cmdline.at(i).contains('=')) {
1823 argv0 = i;
1824 break;
1825 }
1826 }
1827 if(argv0 != -1) {
1828 const QString c = Option::fixPathToLocalOS(cmdline.at(argv0), true);
1829 if(exists(c)) {
1830 cmdline[argv0] = escapeFilePath(Option::fixPathToLocalOS(cmdline.at(argv0), false));
1831 } else {
1832 cmdline[argv0] = escapeFilePath(cmdline.at(argv0));
1833 }
1834 QFileInfo cmdFileInfo(cmdline[argv0]);
1835 if (!cmdFileInfo.isAbsolute() || cmdFileInfo.exists())
1836 tmp_dep_cmd = cmdline.join(" ");
1837 }
1838 dep_cd_cmd = QLatin1String("cd ")
1839 + escapeFilePath(Option::fixPathToLocalOS(Option::output_dir, false))
1840 + QLatin1String(" && ");
1841 }
1842 QStringList &vars = project->values((*it) + ".variables");
1843 if(tmp_out.isEmpty() || tmp_cmd.isEmpty())
1844 continue;
1845 QStringList tmp_inputs;
1846 {
1847 const QStringList &comp_inputs = project->values((*it) + ".input");
1848 for(QStringList::ConstIterator it2 = comp_inputs.begin(); it2 != comp_inputs.end(); ++it2) {
1849 const QStringList &tmp = project->values((*it2));
1850 for(QStringList::ConstIterator input = tmp.begin(); input != tmp.end(); ++input) {
1851 QString in = Option::fixPathToTargetOS((*input), false);
1852 if(verifyExtraCompiler((*it), in))
1853 tmp_inputs.append((*input));
1854 }
1855 }
1856 }
1857
1858 t << "compiler_" << (*it) << "_make_all:";
1859 if(project->values((*it) + ".CONFIG").indexOf("combine") != -1) {
1860 // compilers with a combined input only have one output
1861 QString input = project->values((*it) + ".output").first();
1862 t << " " << escapeDependencyPath(replaceExtraCompilerVariables(tmp_out, input, QString()));
1863 } else {
1864 for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
1865 QString in = Option::fixPathToTargetOS((*input), false);
1866 t << " " << escapeDependencyPath(replaceExtraCompilerVariables(tmp_out, (*input), QString()));
1867 }
1868 }
1869 t << endl;
1870
1871 if(project->values((*it) + ".CONFIG").indexOf("no_clean") == -1) {
1872 QString tmp_clean = project->values((*it) + ".clean").join(" ");
1873 QString tmp_clean_cmds = project->values((*it) + ".clean_commands").join(" ");
1874 if(!tmp_inputs.isEmpty())
1875 clean_targets += QString("compiler_" + (*it) + "_clean ");
1876 t << "compiler_" << (*it) << "_clean:";
1877 bool wrote_clean_cmds = false, wrote_clean = false;
1878 if(tmp_clean_cmds.isEmpty()) {
1879 wrote_clean_cmds = true;
1880 } else if(tmp_clean_cmds.indexOf("${QMAKE_") == -1) {
1881 t << "\n\t" << tmp_clean_cmds;
1882 wrote_clean_cmds = true;
1883 }
1884 if(tmp_clean.isEmpty())
1885 tmp_clean = tmp_out;
1886
1887 const QString del_statement("-$(DEL_FILE)");
1888 const QString del_suffix =
1889 Option::target_mode == Option::TARG_OS2_MODE ?
1890 QString(" >nul 2>&1") : // reduce noise
1891 QString::null;
1892
1893 if(tmp_clean.indexOf("${QMAKE_") == -1) {
1894 t << "\n\t" << del_statement << " " << tmp_clean << del_suffix;
1895 wrote_clean = true;
1896 }
1897 if(!wrote_clean_cmds || !wrote_clean) {
1898 QStringList cleans;
1899 if(!wrote_clean) {
1900 if(project->isActiveConfig("no_delete_multiple_files")) {
1901 for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input)
1902 cleans.append(" " + replaceExtraCompilerVariables(tmp_clean, (*input),
1903 replaceExtraCompilerVariables(tmp_out, (*input), QString())));
1904 } else {
1905 QString files, file;
1906 const int commandlineLimit =
1907 Option::target_mode == Option::TARG_OS2_MODE ?
1908 1000: // OS/2 CMD.EXE limit (1024 - suffix - reserve)
1909 2047; // NT limit, expanded
1910 for(int input = 0; input < tmp_inputs.size(); ++input) {
1911 file = replaceExtraCompilerVariables(tmp_clean, tmp_inputs.at(input),
1912 replaceExtraCompilerVariables(tmp_out, tmp_inputs.at(input), QString()));
1913 file = " " + escapeFilePath(file);
1914 if(del_statement.length() + files.length() +
1915 qMax(fixEnvVariables(file).length(), file.length()) > commandlineLimit) {
1916 cleans.append(files);
1917 files.clear();
1918 }
1919 files += file;
1920 }
1921 if(!files.isEmpty())
1922 cleans.append(files);
1923 }
1924 }
1925 if(!cleans.isEmpty()) {
1926 t << valGlue(cleans, "\n\t" + del_statement, del_suffix + "\n\t" + del_statement, del_suffix);
1927 }
1928 if(!wrote_clean_cmds) {
1929 for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
1930 t << "\n\t" << replaceExtraCompilerVariables(tmp_clean_cmds, (*input),
1931 replaceExtraCompilerVariables(tmp_out, (*input), QString()));
1932 }
1933 }
1934 }
1935 t << endl;
1936 }
1937 if(project->values((*it) + ".CONFIG").indexOf("combine") != -1) {
1938 if(tmp_out.indexOf("${QMAKE_") != -1) {
1939 warn_msg(WarnLogic, "QMAKE_EXTRA_COMPILERS(%s) with combine has variable output.",
1940 (*it).toLatin1().constData());
1941 continue;
1942 }
1943 QStringList deps, inputs;
1944 if(!tmp_dep.isEmpty())
1945 deps += fileFixify(tmp_dep, Option::output_dir, Option::output_dir);
1946 for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
1947 deps += findDependencies((*input));
1948 inputs += Option::fixPathToTargetOS((*input), false);
1949 if(!tmp_dep_cmd.isEmpty() && doDepends()) {
1950 char buff[256];
1951 QString dep_cmd = replaceExtraCompilerVariables(tmp_dep_cmd, (*input),
1952 tmp_out);
1953 dep_cmd = dep_cd_cmd + fixEnvVariables(dep_cmd);
1954 if(FILE *proc = QT_POPEN(dep_cmd.toLatin1().constData(), "r")) {
1955 QString indeps;
1956 while(!feof(proc)) {
1957 int read_in = (int)fread(buff, 1, 255, proc);
1958 if(!read_in)
1959 break;
1960 indeps += QByteArray(buff, read_in);
1961 }
1962 QT_PCLOSE(proc);
1963 if(!indeps.isEmpty()) {
1964 QStringList dep_cmd_deps = indeps.replace('\n', ' ').simplified().split(' ');
1965 for(int i = 0; i < dep_cmd_deps.count(); ++i) {
1966 QString &file = dep_cmd_deps[i];
1967 if(!exists(file)) {
1968 QString localFile;
1969 QList<QMakeLocalFileName> depdirs = QMakeSourceFileInfo::dependencyPaths();
1970 for(QList<QMakeLocalFileName>::Iterator it = depdirs.begin();
1971 it != depdirs.end(); ++it) {
1972 if(exists((*it).real() + Option::dir_sep + file)) {
1973 localFile = (*it).local() + Option::dir_sep + file;
1974 break;
1975 }
1976 }
1977 file = localFile;
1978 }
1979 if(!file.isEmpty())
1980 file = fileFixify(file);
1981 }
1982 deps += dep_cmd_deps;
1983 }
1984 }
1985 }
1986 }
1987 for(int i = 0; i < inputs.size(); ) {
1988 if(tmp_out == inputs.at(i))
1989 inputs.removeAt(i);
1990 else
1991 ++i;
1992 }
1993 for(int i = 0; i < deps.size(); ) {
1994 if(tmp_out == deps.at(i))
1995 deps.removeAt(i);
1996 else
1997 ++i;
1998 }
1999 if (inputs.isEmpty())
2000 continue;
2001
2002 QString cmd;
2003 if (isForSymbianSbsv2()) {
2004 // In sbsv2 the command inputs and outputs need to use absolute paths
2005 cmd = replaceExtraCompilerVariables(tmp_cmd,
2006 fileFixify(escapeFilePaths(inputs), FileFixifyAbsolute),
2007 fileFixify(QStringList(tmp_out), FileFixifyAbsolute));
2008 } else {
2009 cmd = replaceExtraCompilerVariables(tmp_cmd, escapeFilePaths(inputs), QStringList(tmp_out));
2010 }
2011
2012 t << escapeDependencyPath(tmp_out) << ":";
2013 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(tmp_out);
2014 // compiler.CONFIG+=explicit_dependencies means that ONLY compiler.depends gets to cause Makefile dependencies
2015 if(project->values((*it) + ".CONFIG").indexOf("explicit_dependencies") != -1) {
2016 t << " " << valList(escapeDependencyPaths(fileFixify(tmp_dep, Option::output_dir, Option::output_dir)));
2017 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(tmp_out)) << tmp_dep;
2018 } else {
2019 t << " " << valList(escapeDependencyPaths(inputs)) << " " << valList(escapeDependencyPaths(deps));
2020 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(tmp_out)) << inputs << deps;
2021 }
2022 t << "\n\t" << cmd << endl << endl;
2023 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(tmp_out)) << cmd;
2024 continue;
2025 }
2026 for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
2027 QString in = Option::fixPathToTargetOS((*input), false);
2028 QStringList deps = findDependencies((*input));
2029 deps += escapeDependencyPath(in);
2030 QString out = replaceExtraCompilerVariables(tmp_out, (*input), QString());
2031 if(!tmp_dep.isEmpty()) {
2032 QStringList pre_deps = fileFixify(tmp_dep, Option::output_dir, Option::output_dir);
2033 for(int i = 0; i < pre_deps.size(); ++i)
2034 deps += replaceExtraCompilerVariables(pre_deps.at(i), (*input), out);
2035 }
2036 QString cmd = replaceExtraCompilerVariables(tmp_cmd, (*input), out);
2037 // NOTE: The var -> QMAKE_COMP_var replace feature is unsupported, do not use!
2038 if (isForSymbianSbsv2()) {
2039 // In sbsv2 the command inputs and outputs need to use absolute paths
2040 cmd = replaceExtraCompilerVariables(tmp_cmd,
2041 fileFixify((*input), FileFixifyAbsolute),
2042 fileFixify(out, FileFixifyAbsolute));
2043 } else {
2044 cmd = replaceExtraCompilerVariables(tmp_cmd, (*input), out);
2045 }
2046 for(QStringList::ConstIterator it3 = vars.constBegin(); it3 != vars.constEnd(); ++it3)
2047 cmd.replace("$(" + (*it3) + ")", "$(QMAKE_COMP_" + (*it3)+")");
2048 if(!tmp_dep_cmd.isEmpty() && doDepends()) {
2049 char buff[256];
2050 QString dep_cmd = replaceExtraCompilerVariables(tmp_dep_cmd, (*input), out);
2051 dep_cmd = dep_cd_cmd + fixEnvVariables(dep_cmd);
2052 if(FILE *proc = QT_POPEN(dep_cmd.toLatin1().constData(), "r")) {
2053 QString indeps;
2054 while(!feof(proc)) {
2055 int read_in = (int)fread(buff, 1, 255, proc);
2056 if(!read_in)
2057 break;
2058 indeps += QByteArray(buff, read_in);
2059 }
2060 QT_PCLOSE(proc);
2061 if(!indeps.isEmpty()) {
2062 QStringList dep_cmd_deps = indeps.replace('\n', ' ').simplified().split(' ');
2063 for(int i = 0; i < dep_cmd_deps.count(); ++i) {
2064 QString &file = dep_cmd_deps[i];
2065 if(!exists(file)) {
2066 QString localFile;
2067 QList<QMakeLocalFileName> depdirs = QMakeSourceFileInfo::dependencyPaths();
2068 for(QList<QMakeLocalFileName>::Iterator it = depdirs.begin();
2069 it != depdirs.end(); ++it) {
2070 if(exists((*it).real() + Option::dir_sep + file)) {
2071 localFile = (*it).local() + Option::dir_sep + file;
2072 break;
2073 }
2074 }
2075 file = localFile;
2076 }
2077 if(!file.isEmpty())
2078 file = fileFixify(file);
2079 }
2080 deps += dep_cmd_deps;
2081 }
2082 }
2083 //use the depend system to find includes of these included files
2084 QStringList inc_deps;
2085 for(int i = 0; i < deps.size(); ++i) {
2086 const QString dep = deps.at(i);
2087 if(QFile::exists(dep)) {
2088 SourceFileType type = TYPE_UNKNOWN;
2089 if(type == TYPE_UNKNOWN) {
2090 for(QStringList::Iterator cit = Option::c_ext.begin();
2091 cit != Option::c_ext.end(); ++cit) {
2092 if(dep.endsWith((*cit))) {
2093 type = TYPE_C;
2094 break;
2095 }
2096 }
2097 }
2098 if(type == TYPE_UNKNOWN) {
2099 for(QStringList::Iterator cppit = Option::cpp_ext.begin();
2100 cppit != Option::cpp_ext.end(); ++cppit) {
2101 if(dep.endsWith((*cppit))) {
2102 type = TYPE_C;
2103 break;
2104 }
2105 }
2106 }
2107 if(type == TYPE_UNKNOWN) {
2108 for(QStringList::Iterator hit = Option::h_ext.begin();
2109 type == TYPE_UNKNOWN && hit != Option::h_ext.end(); ++hit) {
2110 if(dep.endsWith((*hit))) {
2111 type = TYPE_C;
2112 break;
2113 }
2114 }
2115 }
2116 if(type != TYPE_UNKNOWN) {
2117 if(!QMakeSourceFileInfo::containsSourceFile(dep, type))
2118 QMakeSourceFileInfo::addSourceFile(dep, type);
2119 inc_deps += QMakeSourceFileInfo::dependencies(dep);
2120 }
2121 }
2122 }
2123 deps += inc_deps;
2124 }
2125 for(int i = 0; i < deps.size(); ) {
2126 QString &dep = deps[i];
2127 dep = Option::fixPathToTargetOS(unescapeFilePath(dep), false);
2128 if(out == dep)
2129 deps.removeAt(i);
2130 else
2131 ++i;
2132 }
2133 t << escapeDependencyPath(out) << ": " << valList(escapeDependencyPaths(deps)) << "\n\t"
2134 << cmd << endl << endl;
2135 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(out);
2136 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(out)) << deps;
2137 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(out)) << cmd;
2138 }
2139 }
2140 t << "compiler_clean: " << clean_targets << endl << endl;
2141}
2142
2143void
2144MakefileGenerator::writeExtraCompilerVariables(QTextStream &t)
2145{
2146 bool first = true;
2147 const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
2148 for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
2149 const QStringList &vars = project->values((*it) + ".variables");
2150 for(QStringList::ConstIterator varit = vars.begin(); varit != vars.end(); ++varit) {
2151 if(first) {
2152 t << "\n####### Custom Compiler Variables" << endl;
2153 first = false;
2154 }
2155 t << "QMAKE_COMP_" << (*varit) << " = "
2156 << valList(project->values((*varit))) << endl;
2157 }
2158 }
2159 if(!first)
2160 t << endl;
2161}
2162
2163void
2164MakefileGenerator::writeExtraVariables(QTextStream &t)
2165{
2166 bool first = true;
2167 QMap<QString, QStringList> &vars = project->variables();
2168 QStringList &exports = project->values("QMAKE_EXTRA_VARIABLES");
2169 for(QMap<QString, QStringList>::Iterator it = vars.begin(); it != vars.end(); ++it) {
2170 for(QStringList::Iterator exp_it = exports.begin(); exp_it != exports.end(); ++exp_it) {
2171 QRegExp rx((*exp_it), Qt::CaseInsensitive, QRegExp::Wildcard);
2172 if(rx.exactMatch(it.key())) {
2173 if(first) {
2174 t << "\n####### Custom Variables" << endl;
2175 first = false;
2176 }
2177 t << "EXPORT_" << it.key() << " = " << it.value().join(" ") << endl;
2178 }
2179 }
2180 }
2181 if(!first)
2182 t << endl;
2183}
2184
2185bool
2186MakefileGenerator::writeStubMakefile(QTextStream &t)
2187{
2188 t << "QMAKE = " << var("QMAKE_QMAKE") << endl;
2189 QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
2190 for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it)
2191 t << *it << " ";
2192 //const QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
2193 t << "first all clean install distclean uninstall: " << "qmake" << endl
2194 << "qmake_all:" << endl;
2195 writeMakeQmake(t);
2196 if(project->isEmpty("QMAKE_NOFORCE"))
2197 t << "FORCE:" << endl << endl;
2198 return true;
2199}
2200
2201bool
2202MakefileGenerator::writeMakefile(QTextStream &t)
2203{
2204 t << "####### Compile" << endl << endl;
2205 writeObj(t, "SOURCES");
2206 writeObj(t, "GENERATED_SOURCES");
2207
2208 t << "####### Install" << endl << endl;
2209 writeInstalls(t, "INSTALLS");
2210
2211 if(project->isEmpty("QMAKE_NOFORCE"))
2212 t << "FORCE:" << endl << endl;
2213 return true;
2214}
2215
2216QString MakefileGenerator::buildArgs(const QString &outdir)
2217{
2218 QString ret;
2219 //special variables
2220 if(!project->isEmpty("QMAKE_ABSOLUTE_SOURCE_PATH"))
2221 ret += " QMAKE_ABSOLUTE_SOURCE_PATH=" + escapeFilePath(project->first("QMAKE_ABSOLUTE_SOURCE_PATH"));
2222
2223 //warnings
2224 else if(Option::warn_level == WarnNone)
2225 ret += " -Wnone";
2226 else if(Option::warn_level == WarnAll)
2227 ret += " -Wall";
2228 else if(Option::warn_level & WarnParser)
2229 ret += " -Wparser";
2230 //other options
2231 if(!Option::user_template.isEmpty())
2232 ret += " -t " + Option::user_template;
2233 if(!Option::user_template_prefix.isEmpty())
2234 ret += " -tp " + Option::user_template_prefix;
2235 if(!Option::mkfile::do_cache)
2236 ret += " -nocache";
2237 if(!Option::mkfile::do_deps)
2238 ret += " -nodepend";
2239 if(!Option::mkfile::do_dep_heuristics)
2240 ret += " -nodependheuristics";
2241 if(!Option::mkfile::qmakespec_commandline.isEmpty())
2242 ret += " -spec " + specdir(outdir);
2243 if (Option::target_mode_overridden) {
2244 if (Option::target_mode == Option::TARG_MACX_MODE)
2245 ret += " -macx";
2246 else if (Option::target_mode == Option::TARG_UNIX_MODE)
2247 ret += " -unix";
2248 else if (Option::target_mode == Option::TARG_WIN_MODE)
2249 ret += " -win32";
2250 else if(Option::target_mode == Option::TARG_OS2_MODE)
2251 ret += " -os2";
2252 }
2253
2254 //configs
2255 for(QStringList::Iterator it = Option::user_configs.begin();
2256 it != Option::user_configs.end(); ++it)
2257 ret += " -config " + (*it);
2258 //arguments
2259 for(QStringList::Iterator it = Option::before_user_vars.begin();
2260 it != Option::before_user_vars.end(); ++it) {
2261 if((*it).left(qstrlen("QMAKE_ABSOLUTE_SOURCE_PATH")) != "QMAKE_ABSOLUTE_SOURCE_PATH")
2262 ret += " " + escapeFilePath((*it));
2263 }
2264 if(Option::after_user_vars.count()) {
2265 ret += " -after ";
2266 for(QStringList::Iterator it = Option::after_user_vars.begin();
2267 it != Option::after_user_vars.end(); ++it) {
2268 if((*it).left(qstrlen("QMAKE_ABSOLUTE_SOURCE_PATH")) != "QMAKE_ABSOLUTE_SOURCE_PATH")
2269 ret += " " + escapeFilePath((*it));
2270 }
2271 }
2272 return ret;
2273}
2274
2275//could get stored argv, but then it would have more options than are
2276//probably necesary this will try to guess the bare minimum..
2277QString MakefileGenerator::build_args(const QString &outdir)
2278{
2279 QString ret = "$(QMAKE)";
2280
2281 // general options and arguments
2282 ret += buildArgs(outdir);
2283
2284 //output
2285 QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
2286 if(!ofile.isEmpty() && ofile != project->first("QMAKE_MAKEFILE"))
2287 ret += " -o " + escapeFilePath(ofile);
2288
2289 //inputs
2290 ret += " " + escapeFilePath(fileFixify(project->projectFile(), outdir));
2291
2292 return ret;
2293}
2294
2295void
2296MakefileGenerator::writeHeader(QTextStream &t)
2297{
2298 t << "#############################################################################" << endl;
2299 t << "# Makefile for building: " << escapeFilePath(var("TARGET")) << endl;
2300 t << "# Generated by qmake (" << qmake_version() << ") (Qt " << QT_VERSION_STR << ") on: ";
2301 t << QDateTime::currentDateTime().toString() << endl;
2302 t << "# Project: " << fileFixify(project->projectFile()) << endl;
2303 t << "# Template: " << var("TEMPLATE") << endl;
2304 if(!project->isActiveConfig("build_pass"))
2305 t << "# Command: " << build_args().replace("$(QMAKE)", var("QMAKE_QMAKE")) << endl;
2306 t << "#############################################################################" << endl;
2307 t << endl;
2308}
2309
2310QList<MakefileGenerator::SubTarget*>
2311MakefileGenerator::findSubDirsSubTargets() const
2312{
2313 QList<SubTarget*> targets;
2314 {
2315 const QStringList subdirs = project->values("SUBDIRS");
2316 for(int subdir = 0; subdir < subdirs.size(); ++subdir) {
2317 QString fixedSubdir = subdirs[subdir];
2318 fixedSubdir = fixedSubdir.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
2319
2320 SubTarget *st = new SubTarget;
2321 st->name = subdirs[subdir];
2322 targets.append(st);
2323
2324 bool fromFile = false;
2325 QString file = subdirs[subdir];
2326 if(!project->isEmpty(fixedSubdir + ".file")) {
2327 if(!project->isEmpty(fixedSubdir + ".subdir"))
2328 warn_msg(WarnLogic, "Cannot assign both file and subdir for subdir %s",
2329 subdirs[subdir].toLatin1().constData());
2330 file = project->first(fixedSubdir + ".file");
2331 fromFile = true;
2332 } else if(!project->isEmpty(fixedSubdir + ".subdir")) {
2333 file = project->first(fixedSubdir + ".subdir");
2334 fromFile = false;
2335 } else {
2336 fromFile = file.endsWith(Option::pro_ext);
2337 }
2338 file = Option::fixPathToTargetOS(file);
2339
2340 if(fromFile) {
2341 int slsh = file.lastIndexOf(Option::dir_sep);
2342 if(slsh != -1) {
2343 st->in_directory = file.left(slsh+1);
2344 st->profile = file.mid(slsh+1);
2345 } else {
2346 st->profile = file;
2347 }
2348 } else {
2349 if(!file.isEmpty() && !project->isActiveConfig("subdir_first_pro"))
2350 st->profile = file.section(Option::dir_sep, -1) + Option::pro_ext;
2351 st->in_directory = file;
2352 }
2353 while(st->in_directory.endsWith(Option::dir_sep))
2354 st->in_directory.chop(1);
2355 if(fileInfo(st->in_directory).isRelative())
2356 st->out_directory = st->in_directory;
2357 else
2358 st->out_directory = fileFixify(st->in_directory, qmake_getpwd(), Option::output_dir);
2359 if(!project->isEmpty(fixedSubdir + ".makefile")) {
2360 st->makefile = project->first(fixedSubdir + ".makefile");
2361 } else {
2362 st->makefile = "$(MAKEFILE)";
2363 if(!st->profile.isEmpty()) {
2364 QString basename = st->in_directory;
2365 int new_slsh = basename.lastIndexOf(Option::dir_sep);
2366 if(new_slsh != -1)
2367 basename = basename.mid(new_slsh+1);
2368 if(st->profile != basename + Option::pro_ext)
2369 st->makefile += "." + st->profile.left(st->profile.length() - Option::pro_ext.length());
2370 }
2371 }
2372 if(!project->isEmpty(fixedSubdir + ".depends")) {
2373 const QStringList depends = project->values(fixedSubdir + ".depends");
2374 for(int depend = 0; depend < depends.size(); ++depend) {
2375 bool found = false;
2376 for(int subDep = 0; subDep < subdirs.size(); ++subDep) {
2377 if(subdirs[subDep] == depends.at(depend)) {
2378 QString fixedSubDep = subdirs[subDep];
2379 fixedSubDep = fixedSubDep.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
2380 if(!project->isEmpty(fixedSubDep + ".target")) {
2381 st->depends += project->first(fixedSubDep + ".target");
2382 } else {
2383 QString d = Option::fixPathToLocalOS(subdirs[subDep]);
2384 if(!project->isEmpty(fixedSubDep + ".file"))
2385 d = project->first(fixedSubDep + ".file");
2386 else if(!project->isEmpty(fixedSubDep + ".subdir"))
2387 d = project->first(fixedSubDep + ".subdir");
2388 st->depends += "sub-" + d.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
2389 }
2390 found = true;
2391 break;
2392 }
2393 }
2394 if(!found) {
2395 QString depend_str = depends.at(depend);
2396 st->depends += depend_str.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
2397 }
2398 }
2399 }
2400 if(!project->isEmpty(fixedSubdir + ".target")) {
2401 st->target = project->first(fixedSubdir + ".target");
2402 } else {
2403 st->target = "sub-" + file;
2404 st->target = st->target.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
2405 }
2406 }
2407 }
2408 return targets;
2409}
2410
2411void
2412MakefileGenerator::writeSubDirs(QTextStream &t)
2413{
2414 QList<SubTarget*> targets = findSubDirsSubTargets();
2415 t << "first: make_default" << endl;
2416 int flags = SubTargetInstalls;
2417 if(project->isActiveConfig("ordered"))
2418 flags |= SubTargetOrdered;
2419 writeSubTargets(t, targets, flags);
2420 qDeleteAll(targets);
2421}
2422
2423void
2424MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubTarget*> targets, int flags)
2425{
2426 // blasted includes
2427 QStringList &qeui = project->values("QMAKE_EXTRA_INCLUDES");
2428 for(QStringList::Iterator qeui_it = qeui.begin(); qeui_it != qeui.end(); ++qeui_it)
2429 t << "include " << (*qeui_it) << endl;
2430
2431 if (!(flags & SubTargetSkipDefaultVariables)) {
2432 QString ofile = Option::fixPathToTargetOS(Option::output.fileName());
2433 if(ofile.lastIndexOf(Option::dir_sep) != -1)
2434 ofile.remove(0, ofile.lastIndexOf(Option::dir_sep) +1);
2435 t << "MAKEFILE = " << ofile << endl;
2436 /* Calling Option::fixPathToTargetOS() is necessary for MinGW/MSYS, which requires
2437 * back-slashes to be turned into slashes. */
2438 t << "QMAKE = " << var("QMAKE_QMAKE") << endl;
2439 t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl;
2440 t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl;
2441 t << "MKDIR = " << var("QMAKE_MKDIR") << endl;
2442 t << "COPY = " << var("QMAKE_COPY") << endl;
2443 t << "COPY_FILE = " << var("QMAKE_COPY_FILE") << endl;
2444 t << "COPY_DIR = " << var("QMAKE_COPY_DIR") << endl;
2445 t << "INSTALL_FILE = " << var("QMAKE_INSTALL_FILE") << endl;
2446 t << "INSTALL_PROGRAM = " << var("QMAKE_INSTALL_PROGRAM") << endl;
2447 t << "INSTALL_DIR = " << var("QMAKE_INSTALL_DIR") << endl;
2448 t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl;
2449 t << "SYMLINK = " << var("QMAKE_SYMBOLIC_LINK") << endl;
2450 t << "DEL_DIR = " << var("QMAKE_DEL_DIR") << endl;
2451 t << "MOVE = " << var("QMAKE_MOVE") << endl;
2452 t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl;
2453 t << "MKDIR = " << var("QMAKE_MKDIR") << endl;
2454 t << "SUBTARGETS = "; // subtargets are sub-directory
2455 for(int target = 0; target < targets.size(); ++target)
2456 t << " \\\n\t\t" << targets.at(target)->target;
2457 t << endl << endl;
2458 }
2459 writeExtraVariables(t);
2460
2461 QStringList targetSuffixes;
2462 const QString abs_source_path = project->first("QMAKE_ABSOLUTE_SOURCE_PATH");
2463 if (!(flags & SubTargetSkipDefaultTargets)) {
2464 targetSuffixes << "make_default" << "make_first" << "all" << "clean" << "distclean"
2465 << QString((flags & SubTargetInstalls) ? "install_subtargets" : "install")
2466 << QString((flags & SubTargetInstalls) ? "uninstall_subtargets" : "uninstall");
2467 }
2468
2469 QString prev_mkfile;
2470
2471 // generate target rules
2472 for(int target = 0; target < targets.size(); ++target) {
2473 SubTarget *subtarget = targets.at(target);
2474 QString in_directory = subtarget->in_directory;
2475 if(!in_directory.isEmpty() && !in_directory.endsWith(Option::dir_sep))
2476 in_directory += Option::dir_sep;
2477 QString out_directory = subtarget->out_directory;
2478 if(!out_directory.isEmpty() && !out_directory.endsWith(Option::dir_sep))
2479 out_directory += Option::dir_sep;
2480 if(!abs_source_path.isEmpty() && out_directory.startsWith(abs_source_path))
2481 out_directory = Option::output_dir + out_directory.mid(abs_source_path.length());
2482
2483 QString mkfile = subtarget->makefile;
2484 if(!in_directory.isEmpty())
2485 mkfile.prepend(out_directory);
2486
2487 QString in_directory_cdin, in_directory_cdout, out_directory_cdin, out_directory_cdout;
2488#define MAKE_CD_IN_AND_OUT(directory) \
2489 if(!directory.isEmpty()) { \
2490 if(project->isActiveConfig("cd_change_global")) { \
2491 directory ## _cdin = "\n\tcd " + directory + "\n\t"; \
2492 QDir pwd(Option::output_dir); \
2493 QStringList in = directory.split(Option::dir_sep), out; \
2494 for(int i = 0; i < in.size(); i++) { \
2495 if(in.at(i) == "..") \
2496 out.prepend(fileInfo(pwd.path()).fileName()); \
2497 else if(in.at(i) != ".") \
2498 out.prepend(".."); \
2499 pwd.cd(in.at(i)); \
2500 } \
2501 directory ## _cdout = "\n\t@cd " + escapeFilePath(out.join(Option::dir_sep)); \
2502 } else { \
2503 directory ## _cdin = "\n\tcd " + escapeFilePath(directory) + " && "; \
2504 } \
2505 } else { \
2506 directory ## _cdin = "\n\t"; \
2507 }
2508 MAKE_CD_IN_AND_OUT(in_directory);
2509 MAKE_CD_IN_AND_OUT(out_directory);
2510
2511 //qmake it
2512 if(!subtarget->profile.isEmpty()) {
2513 QString out = subtarget->makefile;
2514 QString in = fileFixify(in_directory + subtarget->profile, out_directory, QString(), FileFixifyAbsolute);
2515 if(out.startsWith(out_directory))
2516 out = out.mid(out_directory.length());
2517 t << mkfile << ": ";
2518 if(flags & SubTargetOrdered) {
2519 // ensure Makefile generation order as the dependent project may
2520 // need a .prl file from the one it depends on in order to
2521 // generate a correct library name if version suffixes are used
2522 if (!prev_mkfile.isEmpty())
2523 t << prev_mkfile;
2524 prev_mkfile = mkfile;
2525 }
2526 t << "\n\t";
2527 if(!in_directory.isEmpty()) {
2528 t << mkdir_p_asstring(out_directory)
2529 << out_directory_cdin
2530 << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out
2531 << in_directory_cdout << endl;
2532 } else {
2533 t << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out << endl;
2534 }
2535 t << subtarget->target << "-qmake_all: ";
2536 if(flags & SubTargetOrdered) {
2537 // ensure Makefile generation order, see above
2538 if(target)
2539 t << targets.at(target-1)->target << "-qmake_all";
2540 }
2541 if(project->isEmpty("QMAKE_NOFORCE"))
2542 t << " FORCE";
2543 t << "\n\t";
2544 if(!in_directory.isEmpty()) {
2545 t << mkdir_p_asstring(out_directory)
2546 << out_directory_cdin
2547 << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out
2548 << in_directory_cdout << endl;
2549 } else {
2550 t << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out << endl;
2551 }
2552 }
2553
2554 QString makefilein = " -f " + subtarget->makefile;
2555
2556 { //actually compile
2557 t << subtarget->target << ": " << mkfile;
2558 if(!subtarget->depends.isEmpty())
2559 t << " " << valList(subtarget->depends);
2560 if(project->isEmpty("QMAKE_NOFORCE"))
2561 t << " FORCE";
2562 t << out_directory_cdin
2563 << "$(MAKE)" << makefilein
2564 << out_directory_cdout << endl;
2565 }
2566
2567 for(int suffix = 0; suffix < targetSuffixes.size(); ++suffix) {
2568 QString s = targetSuffixes.at(suffix);
2569 if(s == "install_subtargets")
2570 s = "install";
2571 else if(s == "uninstall_subtargets")
2572 s = "uninstall";
2573 else if(s == "make_first")
2574 s = "first";
2575 else if(s == "make_default")
2576 s = QString();
2577
2578 if(flags & SubTargetOrdered) {
2579 t << subtarget->target << "-" << targetSuffixes.at(suffix) << "-ordered: " << mkfile;
2580 if(target)
2581 t << " " << targets.at(target-1)->target << "-" << targetSuffixes.at(suffix) << "-ordered ";
2582 if(project->isEmpty("QMAKE_NOFORCE"))
2583 t << " FORCE";
2584 t << out_directory_cdin
2585 << "$(MAKE)" << makefilein << " " << s
2586 << out_directory_cdout << endl;
2587 }
2588 t << subtarget->target << "-" << targetSuffixes.at(suffix) << ": " << mkfile;
2589 if(!subtarget->depends.isEmpty())
2590 t << " " << valGlue(subtarget->depends, QString(), "-" + targetSuffixes.at(suffix) + " ",
2591 "-"+targetSuffixes.at(suffix));
2592 if(project->isEmpty("QMAKE_NOFORCE"))
2593 t << " FORCE";
2594 t << out_directory_cdin
2595 << "$(MAKE)" << makefilein << " " << s
2596 << out_directory_cdout << endl;
2597 }
2598 }
2599 t << endl;
2600
2601 if (!(flags & SubTargetSkipDefaultTargets)) {
2602 if(project->values("QMAKE_INTERNAL_QMAKE_DEPS").indexOf("qmake_all") == -1)
2603 project->values("QMAKE_INTERNAL_QMAKE_DEPS").append("qmake_all");
2604
2605 writeMakeQmake(t);
2606
2607 t << "qmake_all:";
2608 if(!targets.isEmpty()) {
2609 for(QList<SubTarget*>::Iterator it = targets.begin(); it != targets.end(); ++it) {
2610 if(!(*it)->profile.isEmpty())
2611 t << " " << (*it)->target << "-" << "qmake_all";
2612 }
2613 }
2614 if(project->isEmpty("QMAKE_NOFORCE"))
2615 t << " FORCE";
2616 if(project->isActiveConfig("no_empty_targets"))
2617 t << "\n\t" << "@cd .";
2618 t << endl << endl;
2619 }
2620
2621 for(int s = 0; s < targetSuffixes.size(); ++s) {
2622 QString suffix = targetSuffixes.at(s);
2623 if(!(flags & SubTargetInstalls) && suffix.endsWith("install"))
2624 continue;
2625
2626 t << suffix << ":";
2627 for(int target = 0; target < targets.size(); ++target) {
2628 SubTarget *subTarget = targets.at(target);
2629 if((suffix == "make_first" || suffix == "make_default")
2630 && project->values(subTarget->name + ".CONFIG").indexOf("no_default_target") != -1) {
2631 continue;
2632 }
2633 QString targetRule = subTarget->target + "-" + suffix;
2634 if(flags & SubTargetOrdered)
2635 targetRule += "-ordered";
2636 t << " " << targetRule;
2637 }
2638 if(suffix == "all" || suffix == "make_first")
2639 t << varGlue("ALL_DEPS"," "," ","");
2640 if(suffix == "clean")
2641 t << varGlue("CLEAN_DEPS"," "," ","");
2642 if(project->isEmpty("QMAKE_NOFORCE"))
2643 t << " FORCE";
2644 t << endl;
2645 const QString del_suffix =
2646 Option::target_mode == Option::TARG_OS2_MODE ?
2647 QString(" >nul 2>&1"): // reduce noise
2648 QString::null;
2649 if(suffix == "clean") {
2650 t << varGlue("QMAKE_CLEAN","\t-$(DEL_FILE) ",del_suffix+"\n\t-$(DEL_FILE) ", del_suffix) << endl;
2651 } else if(suffix == "distclean") {
2652 QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
2653 if(!ofile.isEmpty())
2654 t << "\t-$(DEL_FILE) " << ofile << del_suffix << endl;
2655 t << varGlue("QMAKE_DISTCLEAN","\t-$(DEL_FILE) "," ","\n");
2656 } else if(project->isActiveConfig("no_empty_targets")) {
2657 t << "\t" << "@cd ." << endl;
2658 }
2659 }
2660
2661 // user defined targets
2662 QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
2663 for(QStringList::Iterator qut_it = qut.begin(); qut_it != qut.end(); ++qut_it) {
2664 QString targ = var((*qut_it) + ".target"),
2665 cmd = var((*qut_it) + ".commands"), deps;
2666 if(targ.isEmpty())
2667 targ = (*qut_it);
2668 t << endl;
2669
2670 QStringList &deplist = project->values((*qut_it) + ".depends");
2671 for(QStringList::Iterator dep_it = deplist.begin(); dep_it != deplist.end(); ++dep_it) {
2672 QString dep = var((*dep_it) + ".target");
2673 if(dep.isEmpty())
2674 dep = Option::fixPathToTargetOS(*dep_it, false);
2675 deps += " " + dep;
2676 }
2677 if(project->values((*qut_it) + ".CONFIG").indexOf("recursive") != -1) {
2678 QSet<QString> recurse;
2679 if(project->isSet((*qut_it) + ".recurse")) {
2680 recurse = project->values((*qut_it) + ".recurse").toSet();
2681 } else {
2682 for(int target = 0; target < targets.size(); ++target)
2683 recurse.insert(targets.at(target)->name);
2684 }
2685 for(int target = 0; target < targets.size(); ++target) {
2686 SubTarget *subtarget = targets.at(target);
2687 QString in_directory = subtarget->in_directory;
2688 if(!in_directory.isEmpty() && !in_directory.endsWith(Option::dir_sep))
2689 in_directory += Option::dir_sep;
2690 QString out_directory = subtarget->out_directory;
2691 if(!out_directory.isEmpty() && !out_directory.endsWith(Option::dir_sep))
2692 out_directory += Option::dir_sep;
2693 if(!abs_source_path.isEmpty() && out_directory.startsWith(abs_source_path))
2694 out_directory = Option::output_dir + out_directory.mid(abs_source_path.length());
2695
2696 if(!recurse.contains(subtarget->name))
2697 continue;
2698 QString mkfile = subtarget->makefile;
2699 if(!in_directory.isEmpty()) {
2700 if(!out_directory.endsWith(Option::dir_sep))
2701 mkfile.prepend(out_directory + Option::dir_sep);
2702 else
2703 mkfile.prepend(out_directory);
2704 }
2705 QString out_directory_cdin, out_directory_cdout;
2706 MAKE_CD_IN_AND_OUT(out_directory);
2707
2708 // note that we always pass the makefile as argument since it's
2709 // hard to tell if it matches the platform make's default
2710 // file name or not (and it can be also specified indirectly
2711 // through $(MAKEFILE))
2712 QString makefilein = " -f " + subtarget->makefile;
2713
2714 //write the rule/depends
2715 if(flags & SubTargetOrdered) {
2716 const QString dep = subtarget->target + "-" + (*qut_it) + "_ordered";
2717 t << dep << ": " << mkfile;
2718 if(target)
2719 t << " " << targets.at(target-1)->target << "-" << (*qut_it) << "_ordered ";
2720 deps += " " + dep;
2721 } else {
2722 const QString dep = subtarget->target + "-" + (*qut_it);
2723 t << dep << ": " << mkfile;
2724 if(!subtarget->depends.isEmpty())
2725 t << " " << valGlue(subtarget->depends, QString(), "-" + (*qut_it) + " ", "-" + (*qut_it));
2726 deps += " " + dep;
2727 }
2728
2729 QString sub_targ = targ;
2730 if(project->isSet((*qut_it) + ".recurse_target"))
2731 sub_targ = project->first((*qut_it) + ".recurse_target");
2732
2733 //write the commands
2734 if(!out_directory.isEmpty()) {
2735 t << out_directory_cdin
2736 << "$(MAKE)" << makefilein << " " << sub_targ
2737 << out_directory_cdout << endl;
2738 } else {
2739 t << "\n\t"
2740 << "$(MAKE)" << makefilein << " " << sub_targ << endl;
2741 }
2742 }
2743 }
2744 if(project->isEmpty("QMAKE_NOFORCE") &&
2745 project->values((*qut_it) + ".CONFIG").indexOf("phony") != -1)
2746 deps += " FORCE";
2747 t << targ << ":" << deps << "\n";
2748 if(!cmd.isEmpty())
2749 t << "\t" << cmd << endl;
2750 }
2751
2752 if(flags & SubTargetInstalls) {
2753 project->values("INSTALLDEPS") += "install_subtargets";
2754 project->values("UNINSTALLDEPS") += "uninstall_subtargets";
2755 writeInstalls(t, "INSTALLS", true);
2756 }
2757
2758 if(project->isEmpty("QMAKE_NOFORCE"))
2759 t << "FORCE:" << endl << endl;
2760}
2761
2762void
2763MakefileGenerator::writeMakeQmake(QTextStream &t)
2764{
2765 QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
2766 if(project->isEmpty("QMAKE_FAILED_REQUIREMENTS") && !project->isEmpty("QMAKE_INTERNAL_PRL_FILE")) {
2767 QStringList files = fileFixify(Option::mkfile::project_files);
2768 t << escapeDependencyPath(project->first("QMAKE_INTERNAL_PRL_FILE")) << ": " << "\n\t"
2769 << "@$(QMAKE) -prl " << buildArgs() << " " << files.join(" ") << endl;
2770 }
2771
2772 QString pfile = project->projectFile();
2773 if(pfile != "(stdin)") {
2774 QString qmake = build_args();
2775 if(!ofile.isEmpty() && !project->isActiveConfig("no_autoqmake")) {
2776 t << escapeFilePath(ofile) << ": " << escapeDependencyPath(fileFixify(pfile)) << " ";
2777 if(Option::mkfile::do_cache)
2778 t << escapeDependencyPath(fileFixify(Option::mkfile::cachefile)) << " ";
2779 if(!specdir().isEmpty()) {
2780 if(exists(Option::fixPathToLocalOS(specdir()+QDir::separator()+"qmake.conf")))
2781 t << escapeDependencyPath(specdir() + Option::dir_sep + "qmake.conf") << " ";
2782 }
2783 const QStringList &included = project->values("QMAKE_INTERNAL_INCLUDED_FILES");
2784 t << escapeDependencyPaths(included).join(" \\\n\t\t") << "\n\t"
2785 << qmake << endl;
2786 for(int include = 0; include < included.size(); ++include) {
2787 const QString i(included.at(include));
2788 if(!i.isEmpty())
2789 t << i << ":" << endl;
2790 }
2791 }
2792 if(project->first("QMAKE_ORIG_TARGET") != "qmake") {
2793 t << "qmake: " <<
2794 project->values("QMAKE_INTERNAL_QMAKE_DEPS").join(" \\\n\t\t");
2795 if(project->isEmpty("QMAKE_NOFORCE"))
2796 t << " FORCE";
2797 t << "\n\t" << "@" << qmake << endl << endl;
2798 }
2799 }
2800}
2801
2802QFileInfo
2803MakefileGenerator::fileInfo(QString file) const
2804{
2805 static QHash<FileInfoCacheKey, QFileInfo> *cache = 0;
2806 static QFileInfo noInfo = QFileInfo();
2807 if(!cache) {
2808 cache = new QHash<FileInfoCacheKey, QFileInfo>;
2809 qmakeAddCacheClear(qmakeDeleteCacheClear_QHashFileInfoCacheKeyQFileInfo, (void**)&cache);
2810 }
2811 FileInfoCacheKey cacheKey(file);
2812 QFileInfo value = cache->value(cacheKey, noInfo);
2813 if (value != noInfo)
2814 return value;
2815
2816 QFileInfo fi(file);
2817 if (fi.exists())
2818 cache->insert(cacheKey, fi);
2819 return fi;
2820}
2821
2822QString
2823MakefileGenerator::unescapeFilePath(const QString &path) const
2824{
2825 QString ret = path;
2826 if(!ret.isEmpty()) {
2827 if(ret.contains(QLatin1String("\\ ")))
2828 ret.replace(QLatin1String("\\ "), QLatin1String(" "));
2829 if(ret.contains(QLatin1Char('\"')))
2830 ret.remove(QLatin1Char('\"'));
2831 }
2832 return ret;
2833}
2834
2835QStringList
2836MakefileGenerator::escapeFilePaths(const QStringList &paths) const
2837{
2838 QStringList ret;
2839 for(int i = 0; i < paths.size(); ++i)
2840 ret.append(escapeFilePath(paths.at(i)));
2841 return ret;
2842}
2843
2844QStringList
2845MakefileGenerator::escapeDependencyPaths(const QStringList &paths) const
2846{
2847 QStringList ret;
2848 for(int i = 0; i < paths.size(); ++i)
2849 ret.append(escapeDependencyPath(paths.at(i)));
2850 return ret;
2851}
2852
2853QStringList
2854MakefileGenerator::unescapeFilePaths(const QStringList &paths) const
2855{
2856 QStringList ret;
2857 for(int i = 0; i < paths.size(); ++i)
2858 ret.append(unescapeFilePath(paths.at(i)));
2859 return ret;
2860}
2861
2862QStringList
2863MakefileGenerator::fileFixify(const QStringList& files, const QString &out_dir, const QString &in_dir,
2864 FileFixifyType fix, bool canon) const
2865{
2866 if(files.isEmpty())
2867 return files;
2868 QStringList ret;
2869 for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
2870 if(!(*it).isEmpty())
2871 ret << fileFixify((*it), out_dir, in_dir, fix, canon);
2872 }
2873 return ret;
2874}
2875
2876QString
2877MakefileGenerator::fileFixify(const QString& file, const QString &out_d, const QString &in_d,
2878 FileFixifyType fix, bool canon) const
2879{
2880 if(file.isEmpty())
2881 return file;
2882 QString ret = unescapeFilePath(file);
2883
2884 //setup the cache
2885 static QHash<FileFixifyCacheKey, QString> *cache = 0;
2886 if(!cache) {
2887 cache = new QHash<FileFixifyCacheKey, QString>;
2888 qmakeAddCacheClear(qmakeDeleteCacheClear_QHashFileFixifyCacheKeyQString, (void**)&cache);
2889 }
2890 FileFixifyCacheKey cacheKey(ret, out_d, in_d, fix, canon);
2891 QString cacheVal = cache->value(cacheKey);
2892 if(!cacheVal.isNull())
2893 return cacheVal;
2894
2895 //do the fixin'
2896 QString pwd = qmake_getpwd();
2897 if (!pwd.endsWith('/'))
2898 pwd += '/';
2899 QString orig_file = ret;
2900 if(ret.startsWith(QLatin1Char('~'))) {
2901 if(ret.startsWith(QLatin1String("~/")))
2902 ret = QDir::homePath() + ret.mid(1);
2903 else
2904 warn_msg(WarnLogic, "Unable to expand ~ in %s", ret.toLatin1().constData());
2905 }
2906 if(fix == FileFixifyAbsolute || (fix == FileFixifyDefault && project->isActiveConfig("no_fixpath"))) {
2907 if(fix == FileFixifyAbsolute && QDir::isRelativePath(ret)) //already absolute
2908 ret.prepend(pwd);
2909 ret = Option::fixPathToTargetOS(ret, false, canon);
2910 } else { //fix it..
2911 QString out_dir = QDir(Option::output_dir).absoluteFilePath(out_d);
2912 QString in_dir = QDir(pwd).absoluteFilePath(in_d);
2913 {
2914 QFileInfo in_fi(fileInfo(in_dir));
2915 if(in_fi.exists())
2916 in_dir = in_fi.canonicalFilePath();
2917 QFileInfo out_fi(fileInfo(out_dir));
2918 if(out_fi.exists())
2919 out_dir = out_fi.canonicalFilePath();
2920 }
2921
2922 QString qfile(Option::fixPathToLocalOS(ret, true, canon));
2923 QFileInfo qfileinfo(fileInfo(qfile));
2924 if(out_dir != in_dir || !qfileinfo.isRelative()) {
2925 if(qfileinfo.isRelative()) {
2926 ret = in_dir + "/" + qfile;
2927 qfileinfo.setFile(ret);
2928 }
2929 ret = Option::fixPathToTargetOS(ret, false, canon);
2930 if(canon && qfileinfo.exists() &&
2931 file == Option::fixPathToTargetOS(ret, true, canon))
2932 ret = Option::fixPathToTargetOS(qfileinfo.canonicalFilePath());
2933 QString match_dir = Option::fixPathToTargetOS(out_dir, false, canon);
2934 if(ret == match_dir) {
2935 ret = "";
2936 } else if(ret.startsWith(match_dir + Option::dir_sep)) {
2937 ret = ret.mid(match_dir.length() + Option::dir_sep.length());
2938 } else {
2939 //figure out the depth
2940 int depth = 4;
2941 if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
2942 Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
2943 if(project && !project->isEmpty("QMAKE_PROJECT_DEPTH"))
2944 depth = project->first("QMAKE_PROJECT_DEPTH").toInt();
2945 else if(Option::mkfile::cachefile_depth != -1)
2946 depth = Option::mkfile::cachefile_depth;
2947 }
2948 //calculate how much can be removed
2949 QString dot_prefix;
2950 for(int i = 1; i <= depth; i++) {
2951 int sl = match_dir.lastIndexOf(Option::dir_sep);
2952 if(sl == -1)
2953 break;
2954 match_dir = match_dir.left(sl);
2955 if(match_dir.isEmpty())
2956 break;
2957 if(ret.startsWith(match_dir + Option::dir_sep)) {
2958 //concat
2959 int remlen = ret.length() - (match_dir.length() + 1);
2960 if(remlen < 0)
2961 remlen = 0;
2962 ret = ret.right(remlen);
2963 //prepend
2964 for(int o = 0; o < i; o++)
2965 dot_prefix += ".." + Option::dir_sep;
2966 }
2967 }
2968 ret.prepend(dot_prefix);
2969 }
2970 } else {
2971 ret = Option::fixPathToTargetOS(ret, false, canon);
2972 }
2973 }
2974 if(ret.isEmpty())
2975 ret = ".";
2976 debug_msg(3, "Fixed[%d,%d] %s :: to :: %s [%s::%s] [%s::%s]", fix, canon, orig_file.toLatin1().constData(),
2977 ret.toLatin1().constData(), in_d.toLatin1().constData(), out_d.toLatin1().constData(),
2978 pwd.toLatin1().constData(), Option::output_dir.toLatin1().constData());
2979 cache->insert(cacheKey, ret);
2980 return ret;
2981}
2982
2983void
2984MakefileGenerator::checkMultipleDefinition(const QString &f, const QString &w)
2985{
2986 if(!(Option::warn_level & WarnLogic))
2987 return;
2988 QString file = f;
2989 int slsh = f.lastIndexOf(Option::dir_sep);
2990 if(slsh != -1)
2991 file.remove(0, slsh + 1);
2992 QStringList &l = project->values(w);
2993 for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it) {
2994 QString file2((*val_it));
2995 slsh = file2.lastIndexOf(Option::dir_sep);
2996 if(slsh != -1)
2997 file2.remove(0, slsh + 1);
2998 if(file2 == file) {
2999 warn_msg(WarnLogic, "Found potential symbol conflict of %s (%s) in %s",
3000 file.toLatin1().constData(), (*val_it).toLatin1().constData(), w.toLatin1().constData());
3001 break;
3002 }
3003 }
3004}
3005
3006QMakeLocalFileName
3007MakefileGenerator::fixPathForFile(const QMakeLocalFileName &file, bool forOpen)
3008{
3009 if(forOpen)
3010 return QMakeLocalFileName(fileFixify(file.real(), qmake_getpwd(), Option::output_dir));
3011 return QMakeLocalFileName(fileFixify(file.real()));
3012}
3013
3014QFileInfo
3015MakefileGenerator::findFileInfo(const QMakeLocalFileName &file)
3016{
3017 return fileInfo(file.local());
3018}
3019
3020QMakeLocalFileName
3021MakefileGenerator::findFileForDep(const QMakeLocalFileName &dep, const QMakeLocalFileName &file)
3022{
3023 QMakeLocalFileName ret;
3024 if(!project->isEmpty("SKIP_DEPENDS")) {
3025 bool found = false;
3026 QStringList &nodeplist = project->values("SKIP_DEPENDS");
3027 for(QStringList::Iterator it = nodeplist.begin();
3028 it != nodeplist.end(); ++it) {
3029 QRegExp regx((*it));
3030 if(regx.indexIn(dep.local()) != -1) {
3031 found = true;
3032 break;
3033 }
3034 }
3035 if(found)
3036 return ret;
3037 }
3038
3039 ret = QMakeSourceFileInfo::findFileForDep(dep, file);
3040 if(!ret.isNull())
3041 return ret;
3042
3043 //these are some "hacky" heuristics it will try to do on an include
3044 //however these can be turned off at runtime, I'm not sure how
3045 //reliable these will be, most likely when problems arise turn it off
3046 //and see if they go away..
3047 if(Option::mkfile::do_dep_heuristics) {
3048 if(depHeuristicsCache.contains(dep.real()))
3049 return depHeuristicsCache[dep.real()];
3050
3051 if(Option::output_dir != qmake_getpwd()
3052 && QDir::isRelativePath(dep.real())) { //is it from the shadow tree
3053 QList<QMakeLocalFileName> depdirs = QMakeSourceFileInfo::dependencyPaths();
3054 depdirs.prepend(fileInfo(file.real()).absoluteDir().path());
3055 QString pwd = qmake_getpwd();
3056 if(pwd.at(pwd.length()-1) != '/')
3057 pwd += '/';
3058 for(int i = 0; i < depdirs.count(); i++) {
3059 QString dir = depdirs.at(i).real();
3060 if(!QDir::isRelativePath(dir) && dir.startsWith(pwd))
3061 dir = dir.mid(pwd.length());
3062 if(QDir::isRelativePath(dir)) {
3063 if(!dir.endsWith(Option::dir_sep))
3064 dir += Option::dir_sep;
3065 QString shadow = fileFixify(dir + dep.local(), pwd, Option::output_dir);
3066 if(exists(shadow)) {
3067 ret = QMakeLocalFileName(shadow);
3068 goto found_dep_from_heuristic;
3069 }
3070 }
3071 }
3072 }
3073 { //is it from an EXTRA_TARGET
3074 const QString dep_basename = dep.local().section(Option::dir_sep, -1);
3075 QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
3076 for(QStringList::Iterator it = qut.begin(); it != qut.end(); ++it) {
3077 QString targ = var((*it) + ".target");
3078 if(targ.isEmpty())
3079 targ = (*it);
3080 QString out = Option::fixPathToTargetOS(targ);
3081 if(out == dep.real() || out.section(Option::dir_sep, -1) == dep_basename) {
3082 ret = QMakeLocalFileName(out);
3083 goto found_dep_from_heuristic;
3084 }
3085 }
3086 }
3087 { //is it from an EXTRA_COMPILER
3088 const QString dep_basename = dep.local().section(Option::dir_sep, -1);
3089 const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
3090 for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
3091 QString tmp_out = project->values((*it) + ".output").first();
3092 if(tmp_out.isEmpty())
3093 continue;
3094 QStringList &tmp = project->values((*it) + ".input");
3095 for(QStringList::Iterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
3096 QStringList &inputs = project->values((*it2));
3097 for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
3098 QString out = Option::fixPathToTargetOS(unescapeFilePath(replaceExtraCompilerVariables(tmp_out, (*input), QString())));
3099 if(out == dep.real() || out.section(Option::dir_sep, -1) == dep_basename) {
3100 ret = QMakeLocalFileName(fileFixify(out, qmake_getpwd(), Option::output_dir));
3101 goto found_dep_from_heuristic;
3102 }
3103 }
3104 }
3105 }
3106 }
3107 found_dep_from_heuristic:
3108 depHeuristicsCache.insert(dep.real(), ret);
3109 }
3110 return ret;
3111}
3112
3113QStringList
3114&MakefileGenerator::findDependencies(const QString &file)
3115{
3116 const QString fixedFile = fileFixify(file);
3117 if(!dependsCache.contains(fixedFile)) {
3118#if 1
3119 QStringList deps = QMakeSourceFileInfo::dependencies(file);
3120 if(file != fixedFile)
3121 deps += QMakeSourceFileInfo::dependencies(fixedFile);
3122#else
3123 QStringList deps = QMakeSourceFileInfo::dependencies(fixedFile);
3124#endif
3125 dependsCache.insert(fixedFile, deps);
3126 }
3127 return dependsCache[fixedFile];
3128}
3129
3130QString
3131MakefileGenerator::specdir(const QString &outdir)
3132{
3133#if 0
3134 if(!spec.isEmpty())
3135 return spec;
3136#endif
3137 spec = fileFixify(Option::mkfile::qmakespec, outdir);
3138 return spec;
3139}
3140
3141bool
3142MakefileGenerator::openOutput(QFile &file, const QString &build) const
3143{
3144 {
3145 QString outdir;
3146 if(!file.fileName().isEmpty()) {
3147 if(QDir::isRelativePath(file.fileName()))
3148 file.setFileName(Option::output_dir + "/" + file.fileName()); //pwd when qmake was run
3149 QFileInfo fi(fileInfo(file.fileName()));
3150 if(fi.isDir())
3151 outdir = file.fileName() + '/';
3152 }
3153 if(!outdir.isEmpty() || file.fileName().isEmpty()) {
3154 QString fname = "Makefile";
3155 if(!project->isEmpty("MAKEFILE"))
3156 fname = project->first("MAKEFILE");
3157 file.setFileName(outdir + fname);
3158 }
3159 }
3160 if(QDir::isRelativePath(file.fileName())) {
3161 QString fname = Option::output_dir; //pwd when qmake was run
3162 if(!fname.endsWith("/"))
3163 fname += "/";
3164 fname += file.fileName();
3165 file.setFileName(fname);
3166 }
3167 if(!build.isEmpty())
3168 file.setFileName(file.fileName() + "." + build);
3169 if(project->isEmpty("QMAKE_MAKEFILE"))
3170 project->values("QMAKE_MAKEFILE").append(file.fileName());
3171 int slsh = file.fileName().lastIndexOf('/');
3172 if(slsh != -1)
3173 mkdir(file.fileName().left(slsh));
3174 if(file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
3175 QFileInfo fi(fileInfo(Option::output.fileName()));
3176 QString od;
3177 if(fi.isSymLink())
3178 od = fileInfo(fi.readLink()).absolutePath();
3179 else
3180 od = fi.path();
3181 od = QDir::fromNativeSeparators(od);
3182 if(QDir::isRelativePath(od)) {
3183 QString dir = Option::output_dir;
3184 if (!dir.endsWith('/') && !od.isEmpty())
3185 dir += '/';
3186 od.prepend(dir);
3187 }
3188 Option::output_dir = od;
3189 return true;
3190 }
3191 return false;
3192}
3193
3194QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.