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

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

qmake: Reverted r976 and reapplied r804.

Adding dependencies to Makefiles themselves doesn't solve all
problems since sometimes the proper .prl file is created not at
the relevant subdir target's Makefile generation time but at a later
stage, when that subdir is built (which may also involve nested
subdirs). So we simply disable parallelism for subdirs Makefiles
at all again.

File size: 133.2 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 = (wild == fileFixify(var("TARGET"), FileFixifyAbsolute));
1306 if(is_target || exists(wild)) { //real file or target
1307 QString file = wild;
1308 QFileInfo fi(fileInfo(wild));
1309 if(!target.isEmpty())
1310 target += "\t";
1311 QString dst_file = filePrefixRoot(root, dst);
1312 if(fi.isDir() && project->isActiveConfig("copy_dir_files")) {
1313 if(!dst_file.endsWith(Option::dir_sep))
1314 dst_file += Option::dir_sep;
1315 dst_file += fi.fileName();
1316 }
1317 QString cmd;
1318 if (fi.isDir())
1319 cmd = inst_prefix + "$(INSTALL_DIR)";
1320 else if (is_target || fi.isExecutable())
1321 cmd = inst_prefix + "$(INSTALL_PROGRAM)";
1322 else
1323 cmd = inst_prefix + "$(INSTALL_FILE)";
1324 cmd += " " + escapeFilePath(wild) + " " + dst_file + "\n";
1325 target += cmd;
1326 if(!project->isActiveConfig("debug") && !project->isActiveConfig("nostrip") &&
1327 !fi.isDir() && fi.isExecutable() && !project->isEmpty("QMAKE_STRIP"))
1328 target += QString("\t-") + var("QMAKE_STRIP") + " " +
1329 filePrefixRoot(root, fileFixify(dst + filestr, FileFixifyAbsolute, false)) + "\n";
1330 if(!uninst.isEmpty())
1331 uninst.append("\n\t");
1332 uninst.append(rm_dir_contents + " " + filePrefixRoot(root, fileFixify(dst + filestr, FileFixifyAbsolute, false)) + del_suffix);
1333 continue;
1334 }
1335 QString local_dirstr = Option::fixPathToLocalOS(dirstr, true);
1336 QStringList files = QDir(local_dirstr).entryList(QStringList(filestr));
1337 if (installConfigValues.contains("no_check_exist") && files.isEmpty()) {
1338 if(!target.isEmpty())
1339 target += "\t";
1340 QString dst_file = filePrefixRoot(root, dst);
1341 QFileInfo fi(fileInfo(wild));
1342 QString cmd;
1343 if (installConfigValues.contains("directory")) {
1344 cmd = inst_prefix + QLatin1String("$(INSTALL_DIR)");
1345 if (!dst_file.endsWith(Option::dir_sep))
1346 dst_file += Option::dir_sep;
1347 dst_file += fi.fileName();
1348 } else if (installConfigValues.contains("executable")) {
1349 cmd = inst_prefix + QLatin1String("$(INSTALL_PROGRAM)");
1350 } else if (installConfigValues.contains("data")) {
1351 cmd = inst_prefix + QLatin1String("$(INSTALL_FILE)");
1352 } else {
1353 cmd = inst_prefix + QString(fi.isExecutable() ? "$(INSTALL_PROGRAM)" : "$(INSTALL_FILE)");
1354 }
1355 cmd += " " + wild + " " + dst_file + "\n";
1356 target += cmd;
1357 if(!uninst.isEmpty())
1358 uninst.append("\n\t");
1359 uninst.append(rm_dir_contents + " " + filePrefixRoot(root, fileFixify(dst + filestr, FileFixifyAbsolute, false)) + del_suffix);
1360 }
1361 for(int x = 0; x < files.count(); x++) {
1362 QString file = files[x];
1363 if(file == "." || file == "..") //blah
1364 continue;
1365 if(!uninst.isEmpty())
1366 uninst.append("\n\t");
1367 uninst.append(rm_dir_contents + " " + filePrefixRoot(root, fileFixify(dst + file, FileFixifyAbsolute, false)) + del_suffix);
1368 QFileInfo fi(fileInfo(dirstr + file));
1369 if(!target.isEmpty())
1370 target += "\t";
1371 QString dst_file = filePrefixRoot(root, fileFixify(dst, FileFixifyAbsolute, false));
1372 if(fi.isDir() && project->isActiveConfig("copy_dir_files")) {
1373 if(!dst_file.endsWith(Option::dir_sep))
1374 dst_file += Option::dir_sep;
1375 dst_file += fi.fileName();
1376 }
1377 QString cmd = inst_prefix + QString(fi.isDir() ? "$(INSTALL_DIR)" : "$(INSTALL_FILE)") + " " +
1378 dirstr + file + " " + dst_file + "\n";
1379 target += cmd;
1380 if(!project->isActiveConfig("debug") && !project->isActiveConfig("nostrip") &&
1381 !fi.isDir() && fi.isExecutable() && !project->isEmpty("QMAKE_STRIP"))
1382 target += QString("\t-") + var("QMAKE_STRIP") + " " +
1383 filePrefixRoot(root, fileFixify(dst + file, FileFixifyAbsolute, false)) +
1384 "\n";
1385 }
1386 }
1387 }
1388 //default?
1389 if(do_default) {
1390 target = defaultInstall((*it));
1391 uninst = project->values((*it) + ".uninstall");
1392 }
1393
1394 if(!target.isEmpty() || installConfigValues.indexOf("dummy_install") != -1) {
1395 if(noBuild || installConfigValues.indexOf("no_build") != -1)
1396 t << "install_" << (*it) << ":";
1397 else if(project->isActiveConfig("build_all"))
1398 t << "install_" << (*it) << ": all";
1399 else
1400 t << "install_" << (*it) << ": first";
1401 const QStringList &deps = project->values((*it) + ".depends");
1402 if(!deps.isEmpty()) {
1403 for(QStringList::ConstIterator dep_it = deps.begin(); dep_it != deps.end(); ++dep_it) {
1404 QString targ = var((*dep_it) + ".target");
1405 if(targ.isEmpty())
1406 targ = (*dep_it);
1407 t << " " << escapeDependencyPath(targ);
1408 }
1409 }
1410 if(project->isEmpty("QMAKE_NOFORCE"))
1411 t << " FORCE";
1412 t << "\n\t";
1413 const QStringList &dirs = project->values(pvar);
1414 for(QStringList::ConstIterator pit = dirs.begin(); pit != dirs.end(); ++pit) {
1415 QString tmp_dst = fileFixify((*pit), FileFixifyAbsolute, false);
1416 if (!isDosLikeShell() && !tmp_dst.endsWith(Option::dir_sep))
1417 tmp_dst += Option::dir_sep;
1418 t << mkdir_p_asstring(filePrefixRoot(root, tmp_dst)) << "\n\t";
1419 }
1420 t << target << endl << endl;
1421 if(!uninst.isEmpty()) {
1422 t << "uninstall_" << (*it) << ": ";
1423 if(project->isEmpty("QMAKE_NOFORCE"))
1424 t << " FORCE";
1425 t << "\n\t"
1426 << uninst.join(" ") << "\n\t"
1427 << "-$(DEL_DIR) " << filePrefixRoot(root, dst) << " " << del_suffix << endl << endl;
1428 }
1429 t << endl;
1430
1431 if(installConfigValues.indexOf("no_default_install") == -1) {
1432 all_installs += QString("install_") + (*it) + " ";
1433 if(!uninst.isEmpty())
1434 all_uninstalls += "uninstall_" + (*it) + " ";
1435 }
1436 } else {
1437 debug_msg(1, "no definition for install %s: install target not created",(*it).toLatin1().constData());
1438 }
1439 }
1440 t << "install: " << var("INSTALLDEPS") << " " << all_installs;
1441 if(project->isEmpty("QMAKE_NOFORCE"))
1442 t << " FORCE";
1443 t << "\n\n";
1444 t << "uninstall: " << all_uninstalls << " " << var("UNINSTALLDEPS");
1445 if(project->isEmpty("QMAKE_NOFORCE"))
1446 t << " FORCE";
1447 t << "\n\n";
1448}
1449
1450QString
1451MakefileGenerator::var(const QString &var)
1452{
1453 return val(project->values(var));
1454}
1455
1456QString
1457MakefileGenerator::val(const QStringList &varList)
1458{
1459 return valGlue(varList, "", " ", "");
1460}
1461
1462QString
1463MakefileGenerator::varGlue(const QString &var, const QString &before, const QString &glue, const QString &after)
1464{
1465 return valGlue(project->values(var), before, glue, after);
1466}
1467
1468QString
1469MakefileGenerator::valGlue(const QStringList &varList, const QString &before, const QString &glue, const QString &after)
1470{
1471 QString ret;
1472 for(QStringList::ConstIterator it = varList.begin(); it != varList.end(); ++it) {
1473 if(!(*it).isEmpty()) {
1474 if(!ret.isEmpty())
1475 ret += glue;
1476 ret += (*it);
1477 }
1478 }
1479 return ret.isEmpty() ? QString("") : before + ret + after;
1480}
1481
1482
1483QString
1484MakefileGenerator::varList(const QString &var)
1485{
1486 return valList(project->values(var));
1487}
1488
1489QString
1490MakefileGenerator::valList(const QStringList &varList)
1491{
1492 return valGlue(varList, "", " \\\n\t\t", "");
1493}
1494
1495QStringList
1496MakefileGenerator::createObjectList(const QStringList &sources)
1497{
1498 QStringList ret;
1499 QString objdir;
1500 if(!project->values("OBJECTS_DIR").isEmpty())
1501 objdir = project->first("OBJECTS_DIR");
1502 for(QStringList::ConstIterator it = sources.begin(); it != sources.end(); ++it) {
1503 QFileInfo fi(fileInfo(Option::fixPathToLocalOS((*it))));
1504 QString dir;
1505 if(objdir.isEmpty() && project->isActiveConfig("object_with_source")) {
1506 QString fName = Option::fixPathToTargetOS((*it), false);
1507 int dl = fName.lastIndexOf(Option::dir_sep);
1508 if(dl != -1)
1509 dir = fName.left(dl + 1);
1510 } else {
1511 dir = objdir;
1512 }
1513 ret.append(dir + fi.completeBaseName() + Option::obj_ext);
1514 }
1515 return ret;
1516}
1517
1518ReplaceExtraCompilerCacheKey::ReplaceExtraCompilerCacheKey(const QString &v, const QStringList &i, const QStringList &o)
1519{
1520 hash = 0;
1521 pwd = qmake_getpwd();
1522 var = v;
1523 {
1524 QStringList il = i;
1525 il.sort();
1526 in = il.join("::");
1527 }
1528 {
1529 QStringList ol = o;
1530 ol.sort();
1531 out = ol.join("::");
1532 }
1533}
1534
1535bool ReplaceExtraCompilerCacheKey::operator==(const ReplaceExtraCompilerCacheKey &f) const
1536{
1537 return (hashCode() == f.hashCode() &&
1538 f.in == in &&
1539 f.out == out &&
1540 f.var == var &&
1541 f.pwd == pwd);
1542}
1543
1544
1545QString
1546MakefileGenerator::replaceExtraCompilerVariables(const QString &orig_var, const QStringList &in, const QStringList &out)
1547{
1548 //lazy cache
1549 ReplaceExtraCompilerCacheKey cacheKey(orig_var, in, out);
1550 QString cacheVal = extraCompilerVariablesCache.value(cacheKey);
1551 if(!cacheVal.isNull())
1552 return cacheVal;
1553
1554 //do the work
1555 QString ret = orig_var;
1556 QRegExp reg_var("\\$\\{.*\\}");
1557 reg_var.setMinimal(true);
1558 for(int rep = 0; (rep = reg_var.indexIn(ret, rep)) != -1; ) {
1559 QStringList val;
1560 const QString var = ret.mid(rep + 2, reg_var.matchedLength() - 3);
1561 bool filePath = false;
1562 if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_VAR_"))) {
1563 const QString varname = var.mid(10);
1564 val += project->values(varname);
1565 }
1566 if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_VAR_FIRST_"))) {
1567 const QString varname = var.mid(16);
1568 val += project->first(varname);
1569 }
1570
1571 if(val.isEmpty() && !in.isEmpty()) {
1572 if(var.startsWith(QLatin1String("QMAKE_FUNC_FILE_IN_"))) {
1573 filePath = true;
1574 const QString funcname = var.mid(19);
1575 val += project->expand(funcname, QList<QStringList>() << in);
1576 } else if(var == QLatin1String("QMAKE_FILE_BASE") || var == QLatin1String("QMAKE_FILE_IN_BASE")) {
1577 //filePath = true;
1578 for(int i = 0; i < in.size(); ++i) {
1579 QFileInfo fi(fileInfo(Option::fixPathToLocalOS(in.at(i))));
1580 QString base = fi.completeBaseName();
1581 if(base.isNull())
1582 base = fi.fileName();
1583 val += base;
1584 }
1585 } else if(var == QLatin1String("QMAKE_FILE_EXT")) {
1586 filePath = true;
1587 for(int i = 0; i < in.size(); ++i) {
1588 QFileInfo fi(fileInfo(Option::fixPathToLocalOS(in.at(i))));
1589 QString ext;
1590 // Ensure complementarity with QMAKE_FILE_BASE
1591 int baseLen = fi.completeBaseName().length();
1592 if(baseLen == 0)
1593 ext = fi.fileName();
1594 else
1595 ext = fi.fileName().remove(0, baseLen);
1596 val += ext;
1597 }
1598 } else if(var == QLatin1String("QMAKE_FILE_PATH") || var == QLatin1String("QMAKE_FILE_IN_PATH")) {
1599 filePath = true;
1600 for(int i = 0; i < in.size(); ++i)
1601 val += fileInfo(Option::fixPathToLocalOS(in.at(i))).path();
1602 } else if(var == QLatin1String("QMAKE_FILE_NAME") || var == QLatin1String("QMAKE_FILE_IN")) {
1603 filePath = true;
1604 for(int i = 0; i < in.size(); ++i)
1605 val += fileInfo(Option::fixPathToLocalOS(in.at(i))).filePath();
1606
1607 }
1608 }
1609 if(val.isEmpty() && !out.isEmpty()) {
1610 if(var.startsWith(QLatin1String("QMAKE_FUNC_FILE_OUT_"))) {
1611 filePath = true;
1612 const QString funcname = var.mid(20);
1613 val += project->expand(funcname, QList<QStringList>() << out);
1614 } else if(var == QLatin1String("QMAKE_FILE_OUT")) {
1615 filePath = true;
1616 for(int i = 0; i < out.size(); ++i)
1617 val += fileInfo(Option::fixPathToLocalOS(out.at(i))).filePath();
1618 } else if(var == QLatin1String("QMAKE_FILE_OUT_BASE")) {
1619 //filePath = true;
1620 for(int i = 0; i < out.size(); ++i) {
1621 QFileInfo fi(fileInfo(Option::fixPathToLocalOS(out.at(i))));
1622 QString base = fi.completeBaseName();
1623 if(base.isNull())
1624 base = fi.fileName();
1625 val += base;
1626 }
1627 }
1628 }
1629 if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_FUNC_"))) {
1630 const QString funcname = var.mid(11);
1631 val += project->expand(funcname, QList<QStringList>() << in << out);
1632 }
1633
1634 if(!val.isEmpty()) {
1635 QString fullVal;
1636 if(filePath) {
1637 for(int i = 0; i < val.size(); ++i) {
1638 const QString file = Option::fixPathToTargetOS(unescapeFilePath(val.at(i)), false);
1639 if(!fullVal.isEmpty())
1640 fullVal += " ";
1641 fullVal += escapeFilePath(file);
1642 }
1643 } else {
1644 fullVal = val.join(" ");
1645 }
1646 ret.replace(rep, reg_var.matchedLength(), fullVal);
1647 rep += fullVal.length();
1648 } else {
1649 rep += reg_var.matchedLength();
1650 }
1651 }
1652
1653 //cache the value
1654 extraCompilerVariablesCache.insert(cacheKey, ret);
1655 return ret;
1656}
1657
1658bool
1659MakefileGenerator::verifyExtraCompiler(const QString &comp, const QString &file_unfixed)
1660{
1661 if(noIO())
1662 return false;
1663 const QString file = Option::fixPathToLocalOS(file_unfixed);
1664
1665 if(project->values(comp + ".CONFIG").indexOf("moc_verify") != -1) {
1666 if(!file.isNull()) {
1667 QMakeSourceFileInfo::addSourceFile(file, QMakeSourceFileInfo::SEEK_MOCS);
1668 if(!mocable(file)) {
1669 return false;
1670 } else {
1671 project->values("MOCABLES").append(file);
1672 }
1673 }
1674 } else if(project->values(comp + ".CONFIG").indexOf("function_verify") != -1) {
1675 QString tmp_out = project->values(comp + ".output").first();
1676 if(tmp_out.isEmpty())
1677 return false;
1678 QStringList verify_function = project->values(comp + ".verify_function");
1679 if(verify_function.isEmpty())
1680 return false;
1681
1682 for(int i = 0; i < verify_function.size(); ++i) {
1683 bool invert = false;
1684 QString verify = verify_function.at(i);
1685 if(verify.at(0) == QLatin1Char('!')) {
1686 invert = true;
1687 verify = verify.mid(1);
1688 }
1689
1690 if(project->values(comp + ".CONFIG").indexOf("combine") != -1) {
1691 bool pass = project->test(verify, QList<QStringList>() << QStringList(tmp_out) << QStringList(file));
1692 if(invert)
1693 pass = !pass;
1694 if(!pass)
1695 return false;
1696 } else {
1697 QStringList &tmp = project->values(comp + ".input");
1698 for(QStringList::Iterator it = tmp.begin(); it != tmp.end(); ++it) {
1699 QStringList &inputs = project->values((*it));
1700 for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
1701 if((*input).isEmpty())
1702 continue;
1703 QString in = fileFixify(Option::fixPathToTargetOS((*input), false));
1704 if(in == file) {
1705 bool pass = project->test(verify,
1706 QList<QStringList>() << QStringList(replaceExtraCompilerVariables(tmp_out, (*input), QString())) <<
1707 QStringList(file));
1708 if(invert)
1709 pass = !pass;
1710 if(!pass)
1711 return false;
1712 break;
1713 }
1714 }
1715 }
1716 }
1717 }
1718 } else if(project->values(comp + ".CONFIG").indexOf("verify") != -1) {
1719 QString tmp_out = project->values(comp + ".output").first();
1720 if(tmp_out.isEmpty())
1721 return false;
1722 QString tmp_cmd;
1723 if(!project->isEmpty(comp + ".commands")) {
1724 int argv0 = -1;
1725 QStringList cmdline = project->values(comp + ".commands");
1726 for(int i = 0; i < cmdline.count(); ++i) {
1727 if(!cmdline.at(i).contains('=')) {
1728 argv0 = i;
1729 break;
1730 }
1731 }
1732 if(argv0 != -1) {
1733 cmdline[argv0] = Option::fixPathToTargetOS(cmdline.at(argv0), false);
1734 tmp_cmd = cmdline.join(" ");
1735 }
1736 }
1737
1738 if(project->values(comp + ".CONFIG").indexOf("combine") != -1) {
1739 QString cmd = replaceExtraCompilerVariables(tmp_cmd, QString(), tmp_out);
1740 if(system(cmd.toLatin1().constData()))
1741 return false;
1742 } else {
1743 QStringList &tmp = project->values(comp + ".input");
1744 for(QStringList::Iterator it = tmp.begin(); it != tmp.end(); ++it) {
1745 QStringList &inputs = project->values((*it));
1746 for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
1747 if((*input).isEmpty())
1748 continue;
1749 QString in = fileFixify(Option::fixPathToTargetOS((*input), false));
1750 if(in == file) {
1751 QString out = replaceExtraCompilerVariables(tmp_out, (*input), QString());
1752 QString cmd = replaceExtraCompilerVariables(tmp_cmd, in, out);
1753 if(system(cmd.toLatin1().constData()))
1754 return false;
1755 break;
1756 }
1757 }
1758 }
1759 }
1760 }
1761 return true;
1762}
1763
1764void
1765MakefileGenerator::writeExtraTargets(QTextStream &t)
1766{
1767 QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
1768 for(QStringList::Iterator it = qut.begin(); it != qut.end(); ++it) {
1769 QString targ = var((*it) + ".target"),
1770 cmd = var((*it) + ".commands"), deps;
1771 if(targ.isEmpty())
1772 targ = (*it);
1773 QStringList &deplist = project->values((*it) + ".depends");
1774 for(QStringList::Iterator dep_it = deplist.begin(); dep_it != deplist.end(); ++dep_it) {
1775 QString dep = var((*dep_it) + ".target");
1776 if(dep.isEmpty())
1777 dep = (*dep_it);
1778 deps += " " + escapeDependencyPath(dep);
1779 }
1780 if(project->values((*it) + ".CONFIG").indexOf("fix_target") != -1)
1781 targ = fileFixify(targ);
1782 if(project->isEmpty("QMAKE_NOFORCE") &&
1783 project->values((*it) + ".CONFIG").indexOf("phony") != -1)
1784 deps += QString(" ") + "FORCE";
1785 t << escapeDependencyPath(targ) << ":" << deps;
1786 if(!cmd.isEmpty())
1787 t << "\n\t" << cmd;
1788 t << endl << endl;
1789
1790 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(targ);
1791 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(targ)) << deps.split(" ", QString::SkipEmptyParts);
1792 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(targ)) << cmd;
1793 }
1794}
1795
1796void
1797MakefileGenerator::writeExtraCompilerTargets(QTextStream &t)
1798{
1799 QString clean_targets;
1800 const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
1801 for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
1802 QString tmp_out = fileFixify(project->values((*it) + ".output").first(),
1803 Option::output_dir, Option::output_dir);
1804 QString tmp_cmd;
1805 if(!project->isEmpty((*it) + ".commands")) {
1806 QStringList cmdline = project->values((*it) + ".commands");
1807 int argv0 = findExecutable(cmdline);
1808 if(argv0 != -1) {
1809 cmdline[argv0] = escapeFilePath(Option::fixPathToTargetOS(cmdline.at(argv0), false));
1810 tmp_cmd = cmdline.join(" ");
1811 }
1812 }
1813 QStringList tmp_dep = project->values((*it) + ".depends");
1814 QString tmp_dep_cmd;
1815 QString dep_cd_cmd;
1816 if(!project->isEmpty((*it) + ".depend_command")) {
1817 int argv0 = -1;
1818 QStringList cmdline = project->values((*it) + ".depend_command");
1819 for(int i = 0; i < cmdline.count(); ++i) {
1820 if(!cmdline.at(i).contains('=')) {
1821 argv0 = i;
1822 break;
1823 }
1824 }
1825 if(argv0 != -1) {
1826 const QString c = Option::fixPathToLocalOS(cmdline.at(argv0), true);
1827 if(exists(c)) {
1828 cmdline[argv0] = escapeFilePath(Option::fixPathToLocalOS(cmdline.at(argv0), false));
1829 } else {
1830 cmdline[argv0] = escapeFilePath(cmdline.at(argv0));
1831 }
1832 QFileInfo cmdFileInfo(cmdline[argv0]);
1833 if (!cmdFileInfo.isAbsolute() || cmdFileInfo.exists())
1834 tmp_dep_cmd = cmdline.join(" ");
1835 }
1836 dep_cd_cmd = QLatin1String("cd ")
1837 + escapeFilePath(Option::fixPathToLocalOS(Option::output_dir, false))
1838 + QLatin1String(" && ");
1839 }
1840 QStringList &vars = project->values((*it) + ".variables");
1841 if(tmp_out.isEmpty() || tmp_cmd.isEmpty())
1842 continue;
1843 QStringList tmp_inputs;
1844 {
1845 const QStringList &comp_inputs = project->values((*it) + ".input");
1846 for(QStringList::ConstIterator it2 = comp_inputs.begin(); it2 != comp_inputs.end(); ++it2) {
1847 const QStringList &tmp = project->values((*it2));
1848 for(QStringList::ConstIterator input = tmp.begin(); input != tmp.end(); ++input) {
1849 QString in = Option::fixPathToTargetOS((*input), false);
1850 if(verifyExtraCompiler((*it), in))
1851 tmp_inputs.append((*input));
1852 }
1853 }
1854 }
1855
1856 t << "compiler_" << (*it) << "_make_all:";
1857 if(project->values((*it) + ".CONFIG").indexOf("combine") != -1) {
1858 // compilers with a combined input only have one output
1859 QString input = project->values((*it) + ".output").first();
1860 t << " " << escapeDependencyPath(replaceExtraCompilerVariables(tmp_out, input, QString()));
1861 } else {
1862 for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
1863 QString in = Option::fixPathToTargetOS((*input), false);
1864 t << " " << escapeDependencyPath(replaceExtraCompilerVariables(tmp_out, (*input), QString()));
1865 }
1866 }
1867 t << endl;
1868
1869 if(project->values((*it) + ".CONFIG").indexOf("no_clean") == -1) {
1870 QString tmp_clean = project->values((*it) + ".clean").join(" ");
1871 QString tmp_clean_cmds = project->values((*it) + ".clean_commands").join(" ");
1872 if(!tmp_inputs.isEmpty())
1873 clean_targets += QString("compiler_" + (*it) + "_clean ");
1874 t << "compiler_" << (*it) << "_clean:";
1875 bool wrote_clean_cmds = false, wrote_clean = false;
1876 if(tmp_clean_cmds.isEmpty()) {
1877 wrote_clean_cmds = true;
1878 } else if(tmp_clean_cmds.indexOf("${QMAKE_") == -1) {
1879 t << "\n\t" << tmp_clean_cmds;
1880 wrote_clean_cmds = true;
1881 }
1882 if(tmp_clean.isEmpty())
1883 tmp_clean = tmp_out;
1884
1885 const QString del_statement("-$(DEL_FILE)");
1886 const QString del_suffix =
1887 Option::target_mode == Option::TARG_OS2_MODE ?
1888 QString(" >nul 2>&1") : // reduce noise
1889 QString::null;
1890
1891 if(tmp_clean.indexOf("${QMAKE_") == -1) {
1892 t << "\n\t" << del_statement << " " << tmp_clean << del_suffix;
1893 wrote_clean = true;
1894 }
1895 if(!wrote_clean_cmds || !wrote_clean) {
1896 QStringList cleans;
1897 if(!wrote_clean) {
1898 if(project->isActiveConfig("no_delete_multiple_files")) {
1899 for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input)
1900 cleans.append(" " + replaceExtraCompilerVariables(tmp_clean, (*input),
1901 replaceExtraCompilerVariables(tmp_out, (*input), QString())));
1902 } else {
1903 QString files, file;
1904 const int commandlineLimit =
1905 Option::target_mode == Option::TARG_OS2_MODE ?
1906 1000: // OS/2 CMD.EXE limit (1024 - suffix - reserve)
1907 2047; // NT limit, expanded
1908 for(int input = 0; input < tmp_inputs.size(); ++input) {
1909 file = replaceExtraCompilerVariables(tmp_clean, tmp_inputs.at(input),
1910 replaceExtraCompilerVariables(tmp_out, tmp_inputs.at(input), QString()));
1911 file = " " + escapeFilePath(file);
1912 if(del_statement.length() + files.length() +
1913 qMax(fixEnvVariables(file).length(), file.length()) > commandlineLimit) {
1914 cleans.append(files);
1915 files.clear();
1916 }
1917 files += file;
1918 }
1919 if(!files.isEmpty())
1920 cleans.append(files);
1921 }
1922 }
1923 if(!cleans.isEmpty()) {
1924 t << valGlue(cleans, "\n\t" + del_statement, del_suffix + "\n\t" + del_statement, del_suffix);
1925 }
1926 if(!wrote_clean_cmds) {
1927 for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
1928 t << "\n\t" << replaceExtraCompilerVariables(tmp_clean_cmds, (*input),
1929 replaceExtraCompilerVariables(tmp_out, (*input), QString()));
1930 }
1931 }
1932 }
1933 t << endl;
1934 }
1935 if(project->values((*it) + ".CONFIG").indexOf("combine") != -1) {
1936 if(tmp_out.indexOf("${QMAKE_") != -1) {
1937 warn_msg(WarnLogic, "QMAKE_EXTRA_COMPILERS(%s) with combine has variable output.",
1938 (*it).toLatin1().constData());
1939 continue;
1940 }
1941 QStringList deps, inputs;
1942 if(!tmp_dep.isEmpty())
1943 deps += fileFixify(tmp_dep, Option::output_dir, Option::output_dir);
1944 for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
1945 deps += findDependencies((*input));
1946 inputs += Option::fixPathToTargetOS((*input), false);
1947 if(!tmp_dep_cmd.isEmpty() && doDepends()) {
1948 char buff[256];
1949 QString dep_cmd = replaceExtraCompilerVariables(tmp_dep_cmd, (*input),
1950 tmp_out);
1951 dep_cmd = dep_cd_cmd + fixEnvVariables(dep_cmd);
1952 if(FILE *proc = QT_POPEN(dep_cmd.toLatin1().constData(), "r")) {
1953 QString indeps;
1954 while(!feof(proc)) {
1955 int read_in = (int)fread(buff, 1, 255, proc);
1956 if(!read_in)
1957 break;
1958 indeps += QByteArray(buff, read_in);
1959 }
1960 QT_PCLOSE(proc);
1961 if(!indeps.isEmpty()) {
1962 QStringList dep_cmd_deps = indeps.replace('\n', ' ').simplified().split(' ');
1963 for(int i = 0; i < dep_cmd_deps.count(); ++i) {
1964 QString &file = dep_cmd_deps[i];
1965 if(!exists(file)) {
1966 QString localFile;
1967 QList<QMakeLocalFileName> depdirs = QMakeSourceFileInfo::dependencyPaths();
1968 for(QList<QMakeLocalFileName>::Iterator it = depdirs.begin();
1969 it != depdirs.end(); ++it) {
1970 if(exists((*it).real() + Option::dir_sep + file)) {
1971 localFile = (*it).local() + Option::dir_sep + file;
1972 break;
1973 }
1974 }
1975 file = localFile;
1976 }
1977 if(!file.isEmpty())
1978 file = fileFixify(file);
1979 }
1980 deps += dep_cmd_deps;
1981 }
1982 }
1983 }
1984 }
1985 for(int i = 0; i < inputs.size(); ) {
1986 if(tmp_out == inputs.at(i))
1987 inputs.removeAt(i);
1988 else
1989 ++i;
1990 }
1991 for(int i = 0; i < deps.size(); ) {
1992 if(tmp_out == deps.at(i))
1993 deps.removeAt(i);
1994 else
1995 ++i;
1996 }
1997 if (inputs.isEmpty())
1998 continue;
1999
2000 QString cmd;
2001 if (isForSymbianSbsv2()) {
2002 // In sbsv2 the command inputs and outputs need to use absolute paths
2003 cmd = replaceExtraCompilerVariables(tmp_cmd,
2004 fileFixify(escapeFilePaths(inputs), FileFixifyAbsolute),
2005 fileFixify(QStringList(tmp_out), FileFixifyAbsolute));
2006 } else {
2007 cmd = replaceExtraCompilerVariables(tmp_cmd, escapeFilePaths(inputs), QStringList(tmp_out));
2008 }
2009
2010 t << escapeDependencyPath(tmp_out) << ":";
2011 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(tmp_out);
2012 // compiler.CONFIG+=explicit_dependencies means that ONLY compiler.depends gets to cause Makefile dependencies
2013 if(project->values((*it) + ".CONFIG").indexOf("explicit_dependencies") != -1) {
2014 t << " " << valList(escapeDependencyPaths(fileFixify(tmp_dep, Option::output_dir, Option::output_dir)));
2015 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(tmp_out)) << tmp_dep;
2016 } else {
2017 t << " " << valList(escapeDependencyPaths(inputs)) << " " << valList(escapeDependencyPaths(deps));
2018 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(tmp_out)) << inputs << deps;
2019 }
2020 t << "\n\t" << cmd << endl << endl;
2021 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(tmp_out)) << cmd;
2022 continue;
2023 }
2024 for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
2025 QString in = Option::fixPathToTargetOS((*input), false);
2026 QStringList deps = findDependencies((*input));
2027 deps += escapeDependencyPath(in);
2028 QString out = replaceExtraCompilerVariables(tmp_out, (*input), QString());
2029 if(!tmp_dep.isEmpty()) {
2030 QStringList pre_deps = fileFixify(tmp_dep, Option::output_dir, Option::output_dir);
2031 for(int i = 0; i < pre_deps.size(); ++i)
2032 deps += replaceExtraCompilerVariables(pre_deps.at(i), (*input), out);
2033 }
2034 QString cmd = replaceExtraCompilerVariables(tmp_cmd, (*input), out);
2035 // NOTE: The var -> QMAKE_COMP_var replace feature is unsupported, do not use!
2036 if (isForSymbianSbsv2()) {
2037 // In sbsv2 the command inputs and outputs need to use absolute paths
2038 cmd = replaceExtraCompilerVariables(tmp_cmd,
2039 fileFixify((*input), FileFixifyAbsolute),
2040 fileFixify(out, FileFixifyAbsolute));
2041 } else {
2042 cmd = replaceExtraCompilerVariables(tmp_cmd, (*input), out);
2043 }
2044 for(QStringList::ConstIterator it3 = vars.constBegin(); it3 != vars.constEnd(); ++it3)
2045 cmd.replace("$(" + (*it3) + ")", "$(QMAKE_COMP_" + (*it3)+")");
2046 if(!tmp_dep_cmd.isEmpty() && doDepends()) {
2047 char buff[256];
2048 QString dep_cmd = replaceExtraCompilerVariables(tmp_dep_cmd, (*input), out);
2049 dep_cmd = dep_cd_cmd + fixEnvVariables(dep_cmd);
2050 if(FILE *proc = QT_POPEN(dep_cmd.toLatin1().constData(), "r")) {
2051 QString indeps;
2052 while(!feof(proc)) {
2053 int read_in = (int)fread(buff, 1, 255, proc);
2054 if(!read_in)
2055 break;
2056 indeps += QByteArray(buff, read_in);
2057 }
2058 QT_PCLOSE(proc);
2059 if(!indeps.isEmpty()) {
2060 QStringList dep_cmd_deps = indeps.replace('\n', ' ').simplified().split(' ');
2061 for(int i = 0; i < dep_cmd_deps.count(); ++i) {
2062 QString &file = dep_cmd_deps[i];
2063 if(!exists(file)) {
2064 QString localFile;
2065 QList<QMakeLocalFileName> depdirs = QMakeSourceFileInfo::dependencyPaths();
2066 for(QList<QMakeLocalFileName>::Iterator it = depdirs.begin();
2067 it != depdirs.end(); ++it) {
2068 if(exists((*it).real() + Option::dir_sep + file)) {
2069 localFile = (*it).local() + Option::dir_sep + file;
2070 break;
2071 }
2072 }
2073 file = localFile;
2074 }
2075 if(!file.isEmpty())
2076 file = fileFixify(file);
2077 }
2078 deps += dep_cmd_deps;
2079 }
2080 }
2081 //use the depend system to find includes of these included files
2082 QStringList inc_deps;
2083 for(int i = 0; i < deps.size(); ++i) {
2084 const QString dep = deps.at(i);
2085 if(QFile::exists(dep)) {
2086 SourceFileType type = TYPE_UNKNOWN;
2087 if(type == TYPE_UNKNOWN) {
2088 for(QStringList::Iterator cit = Option::c_ext.begin();
2089 cit != Option::c_ext.end(); ++cit) {
2090 if(dep.endsWith((*cit))) {
2091 type = TYPE_C;
2092 break;
2093 }
2094 }
2095 }
2096 if(type == TYPE_UNKNOWN) {
2097 for(QStringList::Iterator cppit = Option::cpp_ext.begin();
2098 cppit != Option::cpp_ext.end(); ++cppit) {
2099 if(dep.endsWith((*cppit))) {
2100 type = TYPE_C;
2101 break;
2102 }
2103 }
2104 }
2105 if(type == TYPE_UNKNOWN) {
2106 for(QStringList::Iterator hit = Option::h_ext.begin();
2107 type == TYPE_UNKNOWN && hit != Option::h_ext.end(); ++hit) {
2108 if(dep.endsWith((*hit))) {
2109 type = TYPE_C;
2110 break;
2111 }
2112 }
2113 }
2114 if(type != TYPE_UNKNOWN) {
2115 if(!QMakeSourceFileInfo::containsSourceFile(dep, type))
2116 QMakeSourceFileInfo::addSourceFile(dep, type);
2117 inc_deps += QMakeSourceFileInfo::dependencies(dep);
2118 }
2119 }
2120 }
2121 deps += inc_deps;
2122 }
2123 for(int i = 0; i < deps.size(); ) {
2124 QString &dep = deps[i];
2125 dep = Option::fixPathToTargetOS(unescapeFilePath(dep), false);
2126 if(out == dep)
2127 deps.removeAt(i);
2128 else
2129 ++i;
2130 }
2131 t << escapeDependencyPath(out) << ": " << valList(escapeDependencyPaths(deps)) << "\n\t"
2132 << cmd << endl << endl;
2133 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(out);
2134 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(out)) << deps;
2135 project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(out)) << cmd;
2136 }
2137 }
2138 t << "compiler_clean: " << clean_targets << endl << endl;
2139}
2140
2141void
2142MakefileGenerator::writeExtraCompilerVariables(QTextStream &t)
2143{
2144 bool first = true;
2145 const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
2146 for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
2147 const QStringList &vars = project->values((*it) + ".variables");
2148 for(QStringList::ConstIterator varit = vars.begin(); varit != vars.end(); ++varit) {
2149 if(first) {
2150 t << "\n####### Custom Compiler Variables" << endl;
2151 first = false;
2152 }
2153 t << "QMAKE_COMP_" << (*varit) << " = "
2154 << valList(project->values((*varit))) << endl;
2155 }
2156 }
2157 if(!first)
2158 t << endl;
2159}
2160
2161void
2162MakefileGenerator::writeExtraVariables(QTextStream &t)
2163{
2164 bool first = true;
2165 QMap<QString, QStringList> &vars = project->variables();
2166 QStringList &exports = project->values("QMAKE_EXTRA_VARIABLES");
2167 for(QMap<QString, QStringList>::Iterator it = vars.begin(); it != vars.end(); ++it) {
2168 for(QStringList::Iterator exp_it = exports.begin(); exp_it != exports.end(); ++exp_it) {
2169 QRegExp rx((*exp_it), Qt::CaseInsensitive, QRegExp::Wildcard);
2170 if(rx.exactMatch(it.key())) {
2171 if(first) {
2172 t << "\n####### Custom Variables" << endl;
2173 first = false;
2174 }
2175 t << "EXPORT_" << it.key() << " = " << it.value().join(" ") << endl;
2176 }
2177 }
2178 }
2179 if(!first)
2180 t << endl;
2181}
2182
2183bool
2184MakefileGenerator::writeStubMakefile(QTextStream &t)
2185{
2186 t << "QMAKE = " << var("QMAKE_QMAKE") << endl;
2187 QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
2188 for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it)
2189 t << *it << " ";
2190 //const QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
2191 t << "first all clean install distclean uninstall: " << "qmake" << endl
2192 << "qmake_all:" << endl;
2193 writeMakeQmake(t);
2194 if(project->isEmpty("QMAKE_NOFORCE"))
2195 t << "FORCE:" << endl << endl;
2196 return true;
2197}
2198
2199bool
2200MakefileGenerator::writeMakefile(QTextStream &t)
2201{
2202 t << "####### Compile" << endl << endl;
2203 writeObj(t, "SOURCES");
2204 writeObj(t, "GENERATED_SOURCES");
2205
2206 t << "####### Install" << endl << endl;
2207 writeInstalls(t, "INSTALLS");
2208
2209 if(project->isEmpty("QMAKE_NOFORCE"))
2210 t << "FORCE:" << endl << endl;
2211 return true;
2212}
2213
2214QString MakefileGenerator::buildArgs(const QString &outdir)
2215{
2216 QString ret;
2217 //special variables
2218 if(!project->isEmpty("QMAKE_ABSOLUTE_SOURCE_PATH"))
2219 ret += " QMAKE_ABSOLUTE_SOURCE_PATH=" + escapeFilePath(project->first("QMAKE_ABSOLUTE_SOURCE_PATH"));
2220
2221 //warnings
2222 else if(Option::warn_level == WarnNone)
2223 ret += " -Wnone";
2224 else if(Option::warn_level == WarnAll)
2225 ret += " -Wall";
2226 else if(Option::warn_level & WarnParser)
2227 ret += " -Wparser";
2228 //other options
2229 if(!Option::user_template.isEmpty())
2230 ret += " -t " + Option::user_template;
2231 if(!Option::user_template_prefix.isEmpty())
2232 ret += " -tp " + Option::user_template_prefix;
2233 if(!Option::mkfile::do_cache)
2234 ret += " -nocache";
2235 if(!Option::mkfile::do_deps)
2236 ret += " -nodepend";
2237 if(!Option::mkfile::do_dep_heuristics)
2238 ret += " -nodependheuristics";
2239 if(!Option::mkfile::qmakespec_commandline.isEmpty())
2240 ret += " -spec " + specdir(outdir);
2241 if (Option::target_mode_overridden) {
2242 if (Option::target_mode == Option::TARG_MACX_MODE)
2243 ret += " -macx";
2244 else if (Option::target_mode == Option::TARG_UNIX_MODE)
2245 ret += " -unix";
2246 else if (Option::target_mode == Option::TARG_WIN_MODE)
2247 ret += " -win32";
2248 else if(Option::target_mode == Option::TARG_OS2_MODE)
2249 ret += " -os2";
2250 }
2251
2252 //configs
2253 for(QStringList::Iterator it = Option::user_configs.begin();
2254 it != Option::user_configs.end(); ++it)
2255 ret += " -config " + (*it);
2256 //arguments
2257 for(QStringList::Iterator it = Option::before_user_vars.begin();
2258 it != Option::before_user_vars.end(); ++it) {
2259 if((*it).left(qstrlen("QMAKE_ABSOLUTE_SOURCE_PATH")) != "QMAKE_ABSOLUTE_SOURCE_PATH")
2260 ret += " " + escapeFilePath((*it));
2261 }
2262 if(Option::after_user_vars.count()) {
2263 ret += " -after ";
2264 for(QStringList::Iterator it = Option::after_user_vars.begin();
2265 it != Option::after_user_vars.end(); ++it) {
2266 if((*it).left(qstrlen("QMAKE_ABSOLUTE_SOURCE_PATH")) != "QMAKE_ABSOLUTE_SOURCE_PATH")
2267 ret += " " + escapeFilePath((*it));
2268 }
2269 }
2270 return ret;
2271}
2272
2273//could get stored argv, but then it would have more options than are
2274//probably necesary this will try to guess the bare minimum..
2275QString MakefileGenerator::build_args(const QString &outdir)
2276{
2277 QString ret = "$(QMAKE)";
2278
2279 // general options and arguments
2280 ret += buildArgs(outdir);
2281
2282 //output
2283 QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
2284 if(!ofile.isEmpty() && ofile != project->first("QMAKE_MAKEFILE"))
2285 ret += " -o " + escapeFilePath(ofile);
2286
2287 //inputs
2288 ret += " " + escapeFilePath(fileFixify(project->projectFile(), outdir));
2289
2290 return ret;
2291}
2292
2293void
2294MakefileGenerator::writeHeader(QTextStream &t)
2295{
2296 t << "#############################################################################" << endl;
2297 t << "# Makefile for building: " << escapeFilePath(var("TARGET")) << endl;
2298 t << "# Generated by qmake (" << qmake_version() << ") (Qt " << QT_VERSION_STR << ") on: ";
2299 t << QDateTime::currentDateTime().toString() << endl;
2300 t << "# Project: " << fileFixify(project->projectFile()) << endl;
2301 t << "# Template: " << var("TEMPLATE") << endl;
2302 if(!project->isActiveConfig("build_pass"))
2303 t << "# Command: " << build_args().replace("$(QMAKE)", var("QMAKE_QMAKE")) << endl;
2304 t << "#############################################################################" << endl;
2305 t << endl;
2306}
2307
2308QList<MakefileGenerator::SubTarget*>
2309MakefileGenerator::findSubDirsSubTargets() const
2310{
2311 QList<SubTarget*> targets;
2312 {
2313 const QStringList subdirs = project->values("SUBDIRS");
2314 for(int subdir = 0; subdir < subdirs.size(); ++subdir) {
2315 QString fixedSubdir = subdirs[subdir];
2316 fixedSubdir = fixedSubdir.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
2317
2318 SubTarget *st = new SubTarget;
2319 st->name = subdirs[subdir];
2320 targets.append(st);
2321
2322 bool fromFile = false;
2323 QString file = subdirs[subdir];
2324 if(!project->isEmpty(fixedSubdir + ".file")) {
2325 if(!project->isEmpty(fixedSubdir + ".subdir"))
2326 warn_msg(WarnLogic, "Cannot assign both file and subdir for subdir %s",
2327 subdirs[subdir].toLatin1().constData());
2328 file = project->first(fixedSubdir + ".file");
2329 fromFile = true;
2330 } else if(!project->isEmpty(fixedSubdir + ".subdir")) {
2331 file = project->first(fixedSubdir + ".subdir");
2332 fromFile = false;
2333 } else {
2334 fromFile = file.endsWith(Option::pro_ext);
2335 }
2336 file = Option::fixPathToTargetOS(file);
2337
2338 if(fromFile) {
2339 int slsh = file.lastIndexOf(Option::dir_sep);
2340 if(slsh != -1) {
2341 st->in_directory = file.left(slsh+1);
2342 st->profile = file.mid(slsh+1);
2343 } else {
2344 st->profile = file;
2345 }
2346 } else {
2347 if(!file.isEmpty() && !project->isActiveConfig("subdir_first_pro"))
2348 st->profile = file.section(Option::dir_sep, -1) + Option::pro_ext;
2349 st->in_directory = file;
2350 }
2351 while(st->in_directory.endsWith(Option::dir_sep))
2352 st->in_directory.chop(1);
2353 if(fileInfo(st->in_directory).isRelative())
2354 st->out_directory = st->in_directory;
2355 else
2356 st->out_directory = fileFixify(st->in_directory, qmake_getpwd(), Option::output_dir);
2357 if(!project->isEmpty(fixedSubdir + ".makefile")) {
2358 st->makefile = project->first(fixedSubdir + ".makefile");
2359 } else {
2360 st->makefile = "$(MAKEFILE)";
2361 if(!st->profile.isEmpty()) {
2362 QString basename = st->in_directory;
2363 int new_slsh = basename.lastIndexOf(Option::dir_sep);
2364 if(new_slsh != -1)
2365 basename = basename.mid(new_slsh+1);
2366 if(st->profile != basename + Option::pro_ext)
2367 st->makefile += "." + st->profile.left(st->profile.length() - Option::pro_ext.length());
2368 }
2369 }
2370 if(!project->isEmpty(fixedSubdir + ".depends")) {
2371 const QStringList depends = project->values(fixedSubdir + ".depends");
2372 for(int depend = 0; depend < depends.size(); ++depend) {
2373 bool found = false;
2374 for(int subDep = 0; subDep < subdirs.size(); ++subDep) {
2375 if(subdirs[subDep] == depends.at(depend)) {
2376 QString fixedSubDep = subdirs[subDep];
2377 fixedSubDep = fixedSubDep.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
2378 if(!project->isEmpty(fixedSubDep + ".target")) {
2379 st->depends += project->first(fixedSubDep + ".target");
2380 } else {
2381 QString d = Option::fixPathToLocalOS(subdirs[subDep]);
2382 if(!project->isEmpty(fixedSubDep + ".file"))
2383 d = project->first(fixedSubDep + ".file");
2384 else if(!project->isEmpty(fixedSubDep + ".subdir"))
2385 d = project->first(fixedSubDep + ".subdir");
2386 st->depends += "sub-" + d.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
2387 }
2388 found = true;
2389 break;
2390 }
2391 }
2392 if(!found) {
2393 QString depend_str = depends.at(depend);
2394 st->depends += depend_str.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
2395 }
2396 }
2397 }
2398 if(!project->isEmpty(fixedSubdir + ".target")) {
2399 st->target = project->first(fixedSubdir + ".target");
2400 } else {
2401 st->target = "sub-" + file;
2402 st->target = st->target.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
2403 }
2404 }
2405 }
2406 return targets;
2407}
2408
2409void
2410MakefileGenerator::writeSubDirs(QTextStream &t)
2411{
2412 QList<SubTarget*> targets = findSubDirsSubTargets();
2413 t << "first: make_default" << endl;
2414 int flags = SubTargetInstalls;
2415 if(project->isActiveConfig("ordered"))
2416 flags |= SubTargetOrdered;
2417 writeSubTargets(t, targets, flags);
2418 qDeleteAll(targets);
2419}
2420
2421void
2422MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubTarget*> targets, int flags)
2423{
2424 // blasted includes
2425 QStringList &qeui = project->values("QMAKE_EXTRA_INCLUDES");
2426 for(QStringList::Iterator qeui_it = qeui.begin(); qeui_it != qeui.end(); ++qeui_it)
2427 t << "include " << (*qeui_it) << endl;
2428
2429 if (!(flags & SubTargetSkipDefaultVariables)) {
2430 QString ofile = Option::fixPathToTargetOS(Option::output.fileName());
2431 if(ofile.lastIndexOf(Option::dir_sep) != -1)
2432 ofile.remove(0, ofile.lastIndexOf(Option::dir_sep) +1);
2433 t << "MAKEFILE = " << ofile << endl;
2434 /* Calling Option::fixPathToTargetOS() is necessary for MinGW/MSYS, which requires
2435 * back-slashes to be turned into slashes. */
2436 t << "QMAKE = " << var("QMAKE_QMAKE") << endl;
2437 t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl;
2438 t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl;
2439 t << "MKDIR = " << var("QMAKE_MKDIR") << endl;
2440 t << "COPY = " << var("QMAKE_COPY") << endl;
2441 t << "COPY_FILE = " << var("QMAKE_COPY_FILE") << endl;
2442 t << "COPY_DIR = " << var("QMAKE_COPY_DIR") << endl;
2443 t << "INSTALL_FILE = " << var("QMAKE_INSTALL_FILE") << endl;
2444 t << "INSTALL_PROGRAM = " << var("QMAKE_INSTALL_PROGRAM") << endl;
2445 t << "INSTALL_DIR = " << var("QMAKE_INSTALL_DIR") << endl;
2446 t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl;
2447 t << "SYMLINK = " << var("QMAKE_SYMBOLIC_LINK") << endl;
2448 t << "DEL_DIR = " << var("QMAKE_DEL_DIR") << endl;
2449 t << "MOVE = " << var("QMAKE_MOVE") << endl;
2450 t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl;
2451 t << "MKDIR = " << var("QMAKE_MKDIR") << endl;
2452 t << "SUBTARGETS = "; // subtargets are sub-directory
2453 for(int target = 0; target < targets.size(); ++target)
2454 t << " \\\n\t\t" << targets.at(target)->target;
2455 t << endl << endl;
2456 }
2457 writeExtraVariables(t);
2458
2459 QStringList targetSuffixes;
2460 const QString abs_source_path = project->first("QMAKE_ABSOLUTE_SOURCE_PATH");
2461 if (!(flags & SubTargetSkipDefaultTargets)) {
2462 targetSuffixes << "make_default" << "make_first" << "all" << "clean" << "distclean"
2463 << QString((flags & SubTargetInstalls) ? "install_subtargets" : "install")
2464 << QString((flags & SubTargetInstalls) ? "uninstall_subtargets" : "uninstall");
2465 }
2466
2467 // generate target rules
2468 for(int target = 0; target < targets.size(); ++target) {
2469 SubTarget *subtarget = targets.at(target);
2470 QString in_directory = subtarget->in_directory;
2471 if(!in_directory.isEmpty() && !in_directory.endsWith(Option::dir_sep))
2472 in_directory += Option::dir_sep;
2473 QString out_directory = subtarget->out_directory;
2474 if(!out_directory.isEmpty() && !out_directory.endsWith(Option::dir_sep))
2475 out_directory += Option::dir_sep;
2476 if(!abs_source_path.isEmpty() && out_directory.startsWith(abs_source_path))
2477 out_directory = Option::output_dir + out_directory.mid(abs_source_path.length());
2478
2479 QString mkfile = subtarget->makefile;
2480 if(!in_directory.isEmpty())
2481 mkfile.prepend(out_directory);
2482
2483 QString in_directory_cdin, in_directory_cdout, out_directory_cdin, out_directory_cdout;
2484#define MAKE_CD_IN_AND_OUT(directory) \
2485 if(!directory.isEmpty()) { \
2486 if(project->isActiveConfig("cd_change_global")) { \
2487 directory ## _cdin = "\n\tcd " + directory + "\n\t"; \
2488 QDir pwd(Option::output_dir); \
2489 QStringList in = directory.split(Option::dir_sep), out; \
2490 for(int i = 0; i < in.size(); i++) { \
2491 if(in.at(i) == "..") \
2492 out.prepend(fileInfo(pwd.path()).fileName()); \
2493 else if(in.at(i) != ".") \
2494 out.prepend(".."); \
2495 pwd.cd(in.at(i)); \
2496 } \
2497 directory ## _cdout = "\n\t@cd " + escapeFilePath(out.join(Option::dir_sep)); \
2498 } else { \
2499 directory ## _cdin = "\n\tcd " + escapeFilePath(directory) + " && "; \
2500 } \
2501 } else { \
2502 directory ## _cdin = "\n\t"; \
2503 }
2504 MAKE_CD_IN_AND_OUT(in_directory);
2505 MAKE_CD_IN_AND_OUT(out_directory);
2506
2507 //qmake it
2508 if(!subtarget->profile.isEmpty()) {
2509 QString out = subtarget->makefile;
2510 QString in = fileFixify(in_directory + subtarget->profile, out_directory, QString(), FileFixifyAbsolute);
2511 if(out.startsWith(out_directory))
2512 out = out.mid(out_directory.length());
2513 t << mkfile << ": " << "\n\t";
2514 if(!in_directory.isEmpty()) {
2515 t << mkdir_p_asstring(out_directory)
2516 << out_directory_cdin
2517 << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out
2518 << in_directory_cdout << endl;
2519 } else {
2520 t << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out << endl;
2521 }
2522 t << subtarget->target << "-qmake_all: ";
2523 if(project->isEmpty("QMAKE_NOFORCE"))
2524 t << " FORCE";
2525 t << "\n\t";
2526 if(!in_directory.isEmpty()) {
2527 t << mkdir_p_asstring(out_directory)
2528 << out_directory_cdin
2529 << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out
2530 << in_directory_cdout << endl;
2531 } else {
2532 t << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out << endl;
2533 }
2534 }
2535
2536 QString makefilein = " -f " + subtarget->makefile;
2537
2538 { //actually compile
2539 t << subtarget->target << ": " << mkfile;
2540 if(!subtarget->depends.isEmpty())
2541 t << " " << valList(subtarget->depends);
2542 if(project->isEmpty("QMAKE_NOFORCE"))
2543 t << " FORCE";
2544 t << out_directory_cdin
2545 << "$(MAKE)" << makefilein
2546 << out_directory_cdout << endl;
2547 }
2548
2549 for(int suffix = 0; suffix < targetSuffixes.size(); ++suffix) {
2550 QString s = targetSuffixes.at(suffix);
2551 if(s == "install_subtargets")
2552 s = "install";
2553 else if(s == "uninstall_subtargets")
2554 s = "uninstall";
2555 else if(s == "make_first")
2556 s = "first";
2557 else if(s == "make_default")
2558 s = QString();
2559
2560 if(flags & SubTargetOrdered) {
2561 t << subtarget->target << "-" << targetSuffixes.at(suffix) << "-ordered: " << mkfile;
2562 if(target)
2563 t << " " << targets.at(target-1)->target << "-" << targetSuffixes.at(suffix) << "-ordered ";
2564 if(project->isEmpty("QMAKE_NOFORCE"))
2565 t << " FORCE";
2566 t << out_directory_cdin
2567 << "$(MAKE)" << makefilein << " " << s
2568 << out_directory_cdout << endl;
2569 }
2570 t << subtarget->target << "-" << targetSuffixes.at(suffix) << ": " << mkfile;
2571 if(!subtarget->depends.isEmpty())
2572 t << " " << valGlue(subtarget->depends, QString(), "-" + targetSuffixes.at(suffix) + " ",
2573 "-"+targetSuffixes.at(suffix));
2574 if(project->isEmpty("QMAKE_NOFORCE"))
2575 t << " FORCE";
2576 t << out_directory_cdin
2577 << "$(MAKE)" << makefilein << " " << s
2578 << out_directory_cdout << endl;
2579 }
2580 }
2581 t << endl;
2582
2583 if (!(flags & SubTargetSkipDefaultTargets)) {
2584 if(project->values("QMAKE_INTERNAL_QMAKE_DEPS").indexOf("qmake_all") == -1)
2585 project->values("QMAKE_INTERNAL_QMAKE_DEPS").append("qmake_all");
2586
2587 writeMakeQmake(t);
2588
2589 t << "qmake_all:";
2590 if(!targets.isEmpty()) {
2591 for(QList<SubTarget*>::Iterator it = targets.begin(); it != targets.end(); ++it) {
2592 if(!(*it)->profile.isEmpty())
2593 t << " " << (*it)->target << "-" << "qmake_all";
2594 }
2595 }
2596 if(project->isEmpty("QMAKE_NOFORCE"))
2597 t << " FORCE";
2598 if(project->isActiveConfig("no_empty_targets"))
2599 t << "\n\t" << "@cd .";
2600 t << endl << endl;
2601 }
2602
2603 for(int s = 0; s < targetSuffixes.size(); ++s) {
2604 QString suffix = targetSuffixes.at(s);
2605 if(!(flags & SubTargetInstalls) && suffix.endsWith("install"))
2606 continue;
2607
2608 t << suffix << ":";
2609 for(int target = 0; target < targets.size(); ++target) {
2610 SubTarget *subTarget = targets.at(target);
2611 if((suffix == "make_first" || suffix == "make_default")
2612 && project->values(subTarget->name + ".CONFIG").indexOf("no_default_target") != -1) {
2613 continue;
2614 }
2615 QString targetRule = subTarget->target + "-" + suffix;
2616 if(flags & SubTargetOrdered)
2617 targetRule += "-ordered";
2618 t << " " << targetRule;
2619 }
2620 if(suffix == "all" || suffix == "make_first")
2621 t << varGlue("ALL_DEPS"," "," ","");
2622 if(suffix == "clean")
2623 t << varGlue("CLEAN_DEPS"," "," ","");
2624 if(project->isEmpty("QMAKE_NOFORCE"))
2625 t << " FORCE";
2626 t << endl;
2627 const QString del_suffix =
2628 Option::target_mode == Option::TARG_OS2_MODE ?
2629 QString(" >nul 2>&1"): // reduce noise
2630 QString::null;
2631 if(suffix == "clean") {
2632 t << varGlue("QMAKE_CLEAN","\t-$(DEL_FILE) ",del_suffix+"\n\t-$(DEL_FILE) ", del_suffix) << endl;
2633 } else if(suffix == "distclean") {
2634 QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
2635 if(!ofile.isEmpty())
2636 t << "\t-$(DEL_FILE) " << ofile << del_suffix << endl;
2637 t << varGlue("QMAKE_DISTCLEAN","\t-$(DEL_FILE) "," ","\n");
2638 } else if(project->isActiveConfig("no_empty_targets")) {
2639 t << "\t" << "@cd ." << endl;
2640 }
2641 }
2642
2643 // user defined targets
2644 QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
2645 for(QStringList::Iterator qut_it = qut.begin(); qut_it != qut.end(); ++qut_it) {
2646 QString targ = var((*qut_it) + ".target"),
2647 cmd = var((*qut_it) + ".commands"), deps;
2648 if(targ.isEmpty())
2649 targ = (*qut_it);
2650 t << endl;
2651
2652 QStringList &deplist = project->values((*qut_it) + ".depends");
2653 for(QStringList::Iterator dep_it = deplist.begin(); dep_it != deplist.end(); ++dep_it) {
2654 QString dep = var((*dep_it) + ".target");
2655 if(dep.isEmpty())
2656 dep = Option::fixPathToTargetOS(*dep_it, false);
2657 deps += " " + dep;
2658 }
2659 if(project->values((*qut_it) + ".CONFIG").indexOf("recursive") != -1) {
2660 QSet<QString> recurse;
2661 if(project->isSet((*qut_it) + ".recurse")) {
2662 recurse = project->values((*qut_it) + ".recurse").toSet();
2663 } else {
2664 for(int target = 0; target < targets.size(); ++target)
2665 recurse.insert(targets.at(target)->name);
2666 }
2667 for(int target = 0; target < targets.size(); ++target) {
2668 SubTarget *subtarget = targets.at(target);
2669 QString in_directory = subtarget->in_directory;
2670 if(!in_directory.isEmpty() && !in_directory.endsWith(Option::dir_sep))
2671 in_directory += Option::dir_sep;
2672 QString out_directory = subtarget->out_directory;
2673 if(!out_directory.isEmpty() && !out_directory.endsWith(Option::dir_sep))
2674 out_directory += Option::dir_sep;
2675 if(!abs_source_path.isEmpty() && out_directory.startsWith(abs_source_path))
2676 out_directory = Option::output_dir + out_directory.mid(abs_source_path.length());
2677
2678 if(!recurse.contains(subtarget->name))
2679 continue;
2680 QString mkfile = subtarget->makefile;
2681 if(!in_directory.isEmpty()) {
2682 if(!out_directory.endsWith(Option::dir_sep))
2683 mkfile.prepend(out_directory + Option::dir_sep);
2684 else
2685 mkfile.prepend(out_directory);
2686 }
2687 QString out_directory_cdin, out_directory_cdout;
2688 MAKE_CD_IN_AND_OUT(out_directory);
2689
2690 // note that we always pass the makefile as argument since it's
2691 // hard to tell if it matches the platform make's default
2692 // file name or not (and it can be also specified indirectly
2693 // through $(MAKEFILE))
2694 QString makefilein = " -f " + subtarget->makefile;
2695
2696 //write the rule/depends
2697 if(flags & SubTargetOrdered) {
2698 const QString dep = subtarget->target + "-" + (*qut_it) + "_ordered";
2699 t << dep << ": " << mkfile;
2700 if(target)
2701 t << " " << targets.at(target-1)->target << "-" << (*qut_it) << "_ordered ";
2702 deps += " " + dep;
2703 } else {
2704 const QString dep = subtarget->target + "-" + (*qut_it);
2705 t << dep << ": " << mkfile;
2706 if(!subtarget->depends.isEmpty())
2707 t << " " << valGlue(subtarget->depends, QString(), "-" + (*qut_it) + " ", "-" + (*qut_it));
2708 deps += " " + dep;
2709 }
2710
2711 QString sub_targ = targ;
2712 if(project->isSet((*qut_it) + ".recurse_target"))
2713 sub_targ = project->first((*qut_it) + ".recurse_target");
2714
2715 //write the commands
2716 if(!out_directory.isEmpty()) {
2717 t << out_directory_cdin
2718 << "$(MAKE)" << makefilein << " " << sub_targ
2719 << out_directory_cdout << endl;
2720 } else {
2721 t << "\n\t"
2722 << "$(MAKE)" << makefilein << " " << sub_targ << endl;
2723 }
2724 }
2725 }
2726 if(project->isEmpty("QMAKE_NOFORCE") &&
2727 project->values((*qut_it) + ".CONFIG").indexOf("phony") != -1)
2728 deps += " FORCE";
2729 t << targ << ":" << deps << "\n";
2730 if(!cmd.isEmpty())
2731 t << "\t" << cmd << endl;
2732 }
2733
2734 if(flags & SubTargetInstalls) {
2735 project->values("INSTALLDEPS") += "install_subtargets";
2736 project->values("UNINSTALLDEPS") += "uninstall_subtargets";
2737 writeInstalls(t, "INSTALLS", true);
2738 }
2739
2740 if(project->isEmpty("QMAKE_NOFORCE"))
2741 t << "FORCE:" << endl << endl;
2742}
2743
2744void
2745MakefileGenerator::writeMakeQmake(QTextStream &t)
2746{
2747 QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
2748 if(project->isEmpty("QMAKE_FAILED_REQUIREMENTS") && !project->isEmpty("QMAKE_INTERNAL_PRL_FILE")) {
2749 QStringList files = fileFixify(Option::mkfile::project_files);
2750 t << escapeDependencyPath(project->first("QMAKE_INTERNAL_PRL_FILE")) << ": " << "\n\t"
2751 << "@$(QMAKE) -prl " << buildArgs() << " " << files.join(" ") << endl;
2752 }
2753
2754 QString pfile = project->projectFile();
2755 if(pfile != "(stdin)") {
2756 QString qmake = build_args();
2757 if(!ofile.isEmpty() && !project->isActiveConfig("no_autoqmake")) {
2758 t << escapeFilePath(ofile) << ": " << escapeDependencyPath(fileFixify(pfile)) << " ";
2759 if(Option::mkfile::do_cache)
2760 t << escapeDependencyPath(fileFixify(Option::mkfile::cachefile)) << " ";
2761 if(!specdir().isEmpty()) {
2762 if(exists(Option::fixPathToLocalOS(specdir()+QDir::separator()+"qmake.conf")))
2763 t << escapeDependencyPath(specdir() + Option::dir_sep + "qmake.conf") << " ";
2764 }
2765 const QStringList &included = project->values("QMAKE_INTERNAL_INCLUDED_FILES");
2766 t << escapeDependencyPaths(included).join(" \\\n\t\t") << "\n\t"
2767 << qmake << endl;
2768 for(int include = 0; include < included.size(); ++include) {
2769 const QString i(included.at(include));
2770 if(!i.isEmpty())
2771 t << i << ":" << endl;
2772 }
2773 }
2774 if(project->first("QMAKE_ORIG_TARGET") != "qmake") {
2775 t << "qmake: " <<
2776 project->values("QMAKE_INTERNAL_QMAKE_DEPS").join(" \\\n\t\t");
2777 if(project->isEmpty("QMAKE_NOFORCE"))
2778 t << " FORCE";
2779 t << "\n\t" << "@" << qmake << endl << endl;
2780 }
2781 }
2782}
2783
2784QFileInfo
2785MakefileGenerator::fileInfo(QString file) const
2786{
2787 static QHash<FileInfoCacheKey, QFileInfo> *cache = 0;
2788 static QFileInfo noInfo = QFileInfo();
2789 if(!cache) {
2790 cache = new QHash<FileInfoCacheKey, QFileInfo>;
2791 qmakeAddCacheClear(qmakeDeleteCacheClear_QHashFileInfoCacheKeyQFileInfo, (void**)&cache);
2792 }
2793 FileInfoCacheKey cacheKey(file);
2794 QFileInfo value = cache->value(cacheKey, noInfo);
2795 if (value != noInfo)
2796 return value;
2797
2798 QFileInfo fi(file);
2799 if (fi.exists())
2800 cache->insert(cacheKey, fi);
2801 return fi;
2802}
2803
2804QString
2805MakefileGenerator::unescapeFilePath(const QString &path) const
2806{
2807 QString ret = path;
2808 if(!ret.isEmpty()) {
2809 if(ret.contains(QLatin1String("\\ ")))
2810 ret.replace(QLatin1String("\\ "), QLatin1String(" "));
2811 if(ret.contains(QLatin1Char('\"')))
2812 ret.remove(QLatin1Char('\"'));
2813 }
2814 return ret;
2815}
2816
2817QStringList
2818MakefileGenerator::escapeFilePaths(const QStringList &paths) const
2819{
2820 QStringList ret;
2821 for(int i = 0; i < paths.size(); ++i)
2822 ret.append(escapeFilePath(paths.at(i)));
2823 return ret;
2824}
2825
2826QStringList
2827MakefileGenerator::escapeDependencyPaths(const QStringList &paths) const
2828{
2829 QStringList ret;
2830 for(int i = 0; i < paths.size(); ++i)
2831 ret.append(escapeDependencyPath(paths.at(i)));
2832 return ret;
2833}
2834
2835QStringList
2836MakefileGenerator::unescapeFilePaths(const QStringList &paths) const
2837{
2838 QStringList ret;
2839 for(int i = 0; i < paths.size(); ++i)
2840 ret.append(unescapeFilePath(paths.at(i)));
2841 return ret;
2842}
2843
2844QStringList
2845MakefileGenerator::fileFixify(const QStringList& files, const QString &out_dir, const QString &in_dir,
2846 FileFixifyType fix, bool canon) const
2847{
2848 if(files.isEmpty())
2849 return files;
2850 QStringList ret;
2851 for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
2852 if(!(*it).isEmpty())
2853 ret << fileFixify((*it), out_dir, in_dir, fix, canon);
2854 }
2855 return ret;
2856}
2857
2858QString
2859MakefileGenerator::fileFixify(const QString& file, const QString &out_d, const QString &in_d,
2860 FileFixifyType fix, bool canon) const
2861{
2862 if(file.isEmpty())
2863 return file;
2864 QString ret = unescapeFilePath(file);
2865
2866 //setup the cache
2867 static QHash<FileFixifyCacheKey, QString> *cache = 0;
2868 if(!cache) {
2869 cache = new QHash<FileFixifyCacheKey, QString>;
2870 qmakeAddCacheClear(qmakeDeleteCacheClear_QHashFileFixifyCacheKeyQString, (void**)&cache);
2871 }
2872 FileFixifyCacheKey cacheKey(ret, out_d, in_d, fix, canon);
2873 QString cacheVal = cache->value(cacheKey);
2874 if(!cacheVal.isNull())
2875 return cacheVal;
2876
2877 //do the fixin'
2878 QString pwd = qmake_getpwd();
2879 if (!pwd.endsWith('/'))
2880 pwd += '/';
2881 QString orig_file = ret;
2882 if(ret.startsWith(QLatin1Char('~'))) {
2883 if(ret.startsWith(QLatin1String("~/")))
2884 ret = QDir::homePath() + ret.mid(1);
2885 else
2886 warn_msg(WarnLogic, "Unable to expand ~ in %s", ret.toLatin1().constData());
2887 }
2888 if(fix == FileFixifyAbsolute || (fix == FileFixifyDefault && project->isActiveConfig("no_fixpath"))) {
2889 if(fix == FileFixifyAbsolute && QDir::isRelativePath(ret)) //already absolute
2890 ret.prepend(pwd);
2891 ret = Option::fixPathToTargetOS(ret, false, canon);
2892 } else { //fix it..
2893 QString out_dir = QDir(Option::output_dir).absoluteFilePath(out_d);
2894 QString in_dir = QDir(pwd).absoluteFilePath(in_d);
2895 {
2896 QFileInfo in_fi(fileInfo(in_dir));
2897 if(in_fi.exists())
2898 in_dir = in_fi.canonicalFilePath();
2899 QFileInfo out_fi(fileInfo(out_dir));
2900 if(out_fi.exists())
2901 out_dir = out_fi.canonicalFilePath();
2902 }
2903
2904 QString qfile(Option::fixPathToLocalOS(ret, true, canon));
2905 QFileInfo qfileinfo(fileInfo(qfile));
2906 if(out_dir != in_dir || !qfileinfo.isRelative()) {
2907 if(qfileinfo.isRelative()) {
2908 ret = in_dir + "/" + qfile;
2909 qfileinfo.setFile(ret);
2910 }
2911 ret = Option::fixPathToTargetOS(ret, false, canon);
2912 if(canon && qfileinfo.exists() &&
2913 file == Option::fixPathToTargetOS(ret, true, canon))
2914 ret = Option::fixPathToTargetOS(qfileinfo.canonicalFilePath());
2915 QString match_dir = Option::fixPathToTargetOS(out_dir, false, canon);
2916 if(ret == match_dir) {
2917 ret = "";
2918 } else if(ret.startsWith(match_dir + Option::dir_sep)) {
2919 ret = ret.mid(match_dir.length() + Option::dir_sep.length());
2920 } else {
2921 //figure out the depth
2922 int depth = 4;
2923 if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
2924 Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
2925 if(project && !project->isEmpty("QMAKE_PROJECT_DEPTH"))
2926 depth = project->first("QMAKE_PROJECT_DEPTH").toInt();
2927 else if(Option::mkfile::cachefile_depth != -1)
2928 depth = Option::mkfile::cachefile_depth;
2929 }
2930 //calculate how much can be removed
2931 QString dot_prefix;
2932 for(int i = 1; i <= depth; i++) {
2933 int sl = match_dir.lastIndexOf(Option::dir_sep);
2934 if(sl == -1)
2935 break;
2936 match_dir = match_dir.left(sl);
2937 if(match_dir.isEmpty())
2938 break;
2939 if(ret.startsWith(match_dir + Option::dir_sep)) {
2940 //concat
2941 int remlen = ret.length() - (match_dir.length() + 1);
2942 if(remlen < 0)
2943 remlen = 0;
2944 ret = ret.right(remlen);
2945 //prepend
2946 for(int o = 0; o < i; o++)
2947 dot_prefix += ".." + Option::dir_sep;
2948 }
2949 }
2950 ret.prepend(dot_prefix);
2951 }
2952 } else {
2953 ret = Option::fixPathToTargetOS(ret, false, canon);
2954 }
2955 }
2956 if(ret.isEmpty())
2957 ret = ".";
2958 debug_msg(3, "Fixed[%d,%d] %s :: to :: %s [%s::%s] [%s::%s]", fix, canon, orig_file.toLatin1().constData(),
2959 ret.toLatin1().constData(), in_d.toLatin1().constData(), out_d.toLatin1().constData(),
2960 pwd.toLatin1().constData(), Option::output_dir.toLatin1().constData());
2961 cache->insert(cacheKey, ret);
2962 return ret;
2963}
2964
2965void
2966MakefileGenerator::checkMultipleDefinition(const QString &f, const QString &w)
2967{
2968 if(!(Option::warn_level & WarnLogic))
2969 return;
2970 QString file = f;
2971 int slsh = f.lastIndexOf(Option::dir_sep);
2972 if(slsh != -1)
2973 file.remove(0, slsh + 1);
2974 QStringList &l = project->values(w);
2975 for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it) {
2976 QString file2((*val_it));
2977 slsh = file2.lastIndexOf(Option::dir_sep);
2978 if(slsh != -1)
2979 file2.remove(0, slsh + 1);
2980 if(file2 == file) {
2981 warn_msg(WarnLogic, "Found potential symbol conflict of %s (%s) in %s",
2982 file.toLatin1().constData(), (*val_it).toLatin1().constData(), w.toLatin1().constData());
2983 break;
2984 }
2985 }
2986}
2987
2988QMakeLocalFileName
2989MakefileGenerator::fixPathForFile(const QMakeLocalFileName &file, bool forOpen)
2990{
2991 if(forOpen)
2992 return QMakeLocalFileName(fileFixify(file.real(), qmake_getpwd(), Option::output_dir));
2993 return QMakeLocalFileName(fileFixify(file.real()));
2994}
2995
2996QFileInfo
2997MakefileGenerator::findFileInfo(const QMakeLocalFileName &file)
2998{
2999 return fileInfo(file.local());
3000}
3001
3002QMakeLocalFileName
3003MakefileGenerator::findFileForDep(const QMakeLocalFileName &dep, const QMakeLocalFileName &file)
3004{
3005 QMakeLocalFileName ret;
3006 if(!project->isEmpty("SKIP_DEPENDS")) {
3007 bool found = false;
3008 QStringList &nodeplist = project->values("SKIP_DEPENDS");
3009 for(QStringList::Iterator it = nodeplist.begin();
3010 it != nodeplist.end(); ++it) {
3011 QRegExp regx((*it));
3012 if(regx.indexIn(dep.local()) != -1) {
3013 found = true;
3014 break;
3015 }
3016 }
3017 if(found)
3018 return ret;
3019 }
3020
3021 ret = QMakeSourceFileInfo::findFileForDep(dep, file);
3022 if(!ret.isNull())
3023 return ret;
3024
3025 //these are some "hacky" heuristics it will try to do on an include
3026 //however these can be turned off at runtime, I'm not sure how
3027 //reliable these will be, most likely when problems arise turn it off
3028 //and see if they go away..
3029 if(Option::mkfile::do_dep_heuristics) {
3030 if(depHeuristicsCache.contains(dep.real()))
3031 return depHeuristicsCache[dep.real()];
3032
3033 if(Option::output_dir != qmake_getpwd()
3034 && QDir::isRelativePath(dep.real())) { //is it from the shadow tree
3035 QList<QMakeLocalFileName> depdirs = QMakeSourceFileInfo::dependencyPaths();
3036 depdirs.prepend(fileInfo(file.real()).absoluteDir().path());
3037 QString pwd = qmake_getpwd();
3038 if(pwd.at(pwd.length()-1) != '/')
3039 pwd += '/';
3040 for(int i = 0; i < depdirs.count(); i++) {
3041 QString dir = depdirs.at(i).real();
3042 if(!QDir::isRelativePath(dir) && dir.startsWith(pwd))
3043 dir = dir.mid(pwd.length());
3044 if(QDir::isRelativePath(dir)) {
3045 if(!dir.endsWith(Option::dir_sep))
3046 dir += Option::dir_sep;
3047 QString shadow = fileFixify(dir + dep.local(), pwd, Option::output_dir);
3048 if(exists(shadow)) {
3049 ret = QMakeLocalFileName(shadow);
3050 goto found_dep_from_heuristic;
3051 }
3052 }
3053 }
3054 }
3055 { //is it from an EXTRA_TARGET
3056 const QString dep_basename = dep.local().section(Option::dir_sep, -1);
3057 QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
3058 for(QStringList::Iterator it = qut.begin(); it != qut.end(); ++it) {
3059 QString targ = var((*it) + ".target");
3060 if(targ.isEmpty())
3061 targ = (*it);
3062 QString out = Option::fixPathToTargetOS(targ);
3063 if(out == dep.real() || out.section(Option::dir_sep, -1) == dep_basename) {
3064 ret = QMakeLocalFileName(out);
3065 goto found_dep_from_heuristic;
3066 }
3067 }
3068 }
3069 { //is it from an EXTRA_COMPILER
3070 const QString dep_basename = dep.local().section(Option::dir_sep, -1);
3071 const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
3072 for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
3073 QString tmp_out = project->values((*it) + ".output").first();
3074 if(tmp_out.isEmpty())
3075 continue;
3076 QStringList &tmp = project->values((*it) + ".input");
3077 for(QStringList::Iterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
3078 QStringList &inputs = project->values((*it2));
3079 for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
3080 QString out = Option::fixPathToTargetOS(unescapeFilePath(replaceExtraCompilerVariables(tmp_out, (*input), QString())));
3081 if(out == dep.real() || out.section(Option::dir_sep, -1) == dep_basename) {
3082 ret = QMakeLocalFileName(fileFixify(out, qmake_getpwd(), Option::output_dir));
3083 goto found_dep_from_heuristic;
3084 }
3085 }
3086 }
3087 }
3088 }
3089 found_dep_from_heuristic:
3090 depHeuristicsCache.insert(dep.real(), ret);
3091 }
3092 return ret;
3093}
3094
3095QStringList
3096&MakefileGenerator::findDependencies(const QString &file)
3097{
3098 const QString fixedFile = fileFixify(file);
3099 if(!dependsCache.contains(fixedFile)) {
3100#if 1
3101 QStringList deps = QMakeSourceFileInfo::dependencies(file);
3102 if(file != fixedFile)
3103 deps += QMakeSourceFileInfo::dependencies(fixedFile);
3104#else
3105 QStringList deps = QMakeSourceFileInfo::dependencies(fixedFile);
3106#endif
3107 dependsCache.insert(fixedFile, deps);
3108 }
3109 return dependsCache[fixedFile];
3110}
3111
3112QString
3113MakefileGenerator::specdir(const QString &outdir)
3114{
3115#if 0
3116 if(!spec.isEmpty())
3117 return spec;
3118#endif
3119 spec = fileFixify(Option::mkfile::qmakespec, outdir);
3120 return spec;
3121}
3122
3123bool
3124MakefileGenerator::openOutput(QFile &file, const QString &build) const
3125{
3126 {
3127 QString outdir;
3128 if(!file.fileName().isEmpty()) {
3129 if(QDir::isRelativePath(file.fileName()))
3130 file.setFileName(Option::output_dir + "/" + file.fileName()); //pwd when qmake was run
3131 QFileInfo fi(fileInfo(file.fileName()));
3132 if(fi.isDir())
3133 outdir = file.fileName() + '/';
3134 }
3135 if(!outdir.isEmpty() || file.fileName().isEmpty()) {
3136 QString fname = "Makefile";
3137 if(!project->isEmpty("MAKEFILE"))
3138 fname = project->first("MAKEFILE");
3139 file.setFileName(outdir + fname);
3140 }
3141 }
3142 if(QDir::isRelativePath(file.fileName())) {
3143 QString fname = Option::output_dir; //pwd when qmake was run
3144 if(!fname.endsWith("/"))
3145 fname += "/";
3146 fname += file.fileName();
3147 file.setFileName(fname);
3148 }
3149 if(!build.isEmpty())
3150 file.setFileName(file.fileName() + "." + build);
3151 if(project->isEmpty("QMAKE_MAKEFILE"))
3152 project->values("QMAKE_MAKEFILE").append(file.fileName());
3153 int slsh = file.fileName().lastIndexOf('/');
3154 if(slsh != -1)
3155 mkdir(file.fileName().left(slsh));
3156 if(file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
3157 QFileInfo fi(fileInfo(Option::output.fileName()));
3158 QString od;
3159 if(fi.isSymLink())
3160 od = fileInfo(fi.readLink()).absolutePath();
3161 else
3162 od = fi.path();
3163 od = QDir::fromNativeSeparators(od);
3164 if(QDir::isRelativePath(od)) {
3165 QString dir = Option::output_dir;
3166 if (!dir.endsWith('/') && !od.isEmpty())
3167 dir += '/';
3168 od.prepend(dir);
3169 }
3170 Option::output_dir = od;
3171 return true;
3172 }
3173 return false;
3174}
3175
3176QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.