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

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

qmake: Revert r530.

This change introduced behavior unexpected by qmake users (compared
to e.g. the Window platform where the drive letter remains intact), made it
not possible to specify the full install path (including the drive letter) and
hence is wrong.

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