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

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

qmake: Proper fix to create install rules in Makefiles at first run.

The previous fix from Nokia (3d6f1ebf) did not actually work since it
would not take many things into account, e.g. the target extension,
shadow builds and so on.

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