source: trunk/qmake/generators/os2/gnumake.cpp@ 1168

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

OS/2: qmake: Let INSTALL_ROOT override the dirve letter too.

This allows to redirect make install to another drive.

File size: 35.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the qmake application of the Qt Toolkit.
8**
9** Copyright (C) 2010 netlabs.org. OS/2 parts.
10**
11** $QT_BEGIN_LICENSE:LGPL$
12** Commercial Usage
13** Licensees holding valid Qt Commercial licenses may use this file in
14** accordance with the Qt Commercial License Agreement provided with the
15** Software or, alternatively, in accordance with the terms contained in
16** a written agreement between you and Nokia.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 2.1 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 2.1 requirements
24** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** In addition, as a special exception, Nokia gives you certain additional
27** rights. These rights are described in the Nokia Qt LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** GNU General Public License Usage
31** Alternatively, this file may be used under the terms of the GNU
32** General Public License version 3.0 as published by the Free Software
33** Foundation and appearing in the file LICENSE.GPL included in the
34** packaging of this file. Please review the following information to
35** ensure the GNU General Public License version 3.0 requirements will be
36** met: http://www.gnu.org/copyleft/gpl.html.
37**
38** If you have questions regarding the use of this file, please contact
39** Nokia at [email protected].
40** $QT_END_LICENSE$
41**
42****************************************************************************/
43
44#include "gnumake.h"
45#include "option.h"
46#include "meta.h"
47#include <qregexp.h>
48#include <qdir.h>
49#include <stdlib.h>
50#include <time.h>
51
52QT_BEGIN_NAMESPACE
53
54GNUMakefileGenerator::GNUMakefileGenerator() : Win32MakefileGenerator(), init_flag(false)
55{
56 if (isDosLikeShell())
57 quote = "\"";
58 else
59 quote = "'";
60}
61
62bool GNUMakefileGenerator::isDosLikeShell() const
63{
64#ifdef Q_OS_OS2
65 return Option::shellPath.isEmpty();
66#else
67 return Win32MakefileGenerator::isDosLikeShell();
68#endif
69}
70
71QString GNUMakefileGenerator::escapeFilePath(const QString &path) const
72{
73 QString ret = path;
74 // we assume that make variables contain paths already properly escaped
75 if (!ret.isEmpty() && !ret.contains(QRegExp("\\$\\(([^$)]+)\\)"))) {
76 ret = unescapeFilePath(ret);
77 // fix separators and remove the trailing slahsh (if any) since ancient
78 // tools such as CMD.EXE (e.g. its "cd" command) don't like them
79 ret = Option::fixPathToTargetOS(ret, false);
80 if (!isDosLikeShell()) {
81 ret.replace(' ', "\\ ");
82 } else {
83 if (ret.contains(QRegExp("[ +&;%]")))
84 ret = quote + ret + quote;
85 }
86 }
87 return ret;
88}
89
90QString GNUMakefileGenerator::escapeDependencyPath(const QString &path) const
91{
92 // dependency escaping is always done Unix-way since this is a requirement
93 // of GNU make which allows " and ' to be part of the file name.
94 // Note that if the string is a make variable reference, we don't eascape
95 // but instead use the q function defined in writeMakefile() that will do
96 // it at runtime.
97 QString ret = path;
98 if (!ret.isEmpty()) {
99 bool matched = false;
100 if (path.contains("$("))
101 ret = escapeFileVars(path, &matched);
102 if (!matched) {
103 // didn't find variable references, do plain escaping
104 ret = unescapeFilePath(ret);
105 ret.replace(' ', "\\ ");
106 // and we still want to normalize slashes as this is what we do in
107 // other places (e.g. .cpp targets); this function will do it for
108 // QMAKE_EXTRA_TARGETS as well (this will also remove the trailing
109 // slash, if any, see above)
110 ret = Option::fixPathToTargetOS(ret, false);
111 }
112 }
113 return ret;
114}
115
116// Searches for the best version of the requested library named \a stem in
117// \a dir and append the result to the \a found list. If the best version is
118// found, it is appended first in the list, otherwise \a stem is appended as
119// is. If a .prl file corresponding to \a stem is found, the library name is
120// taken from there. If the .prl refers to other libraries the found one
121// depends on, they are also appended to the returned list (as is).
122//
123// The function returns \c true if the best version for the given \a stem was
124// actually found and \c false otherwise (\a found will remain untouched in this
125// case).
126//
127// Note that if a circular reference to the project's target library or to a
128// library we already processed is detected, the function returns \c true but
129// \a found remains untouched; this indicates that the given \a stem should be
130// dropped from the resulting library list at all.
131bool
132GNUMakefileGenerator::findBestVersion(const QString &dir, const QString &stem,
133 const QString &ext, QStringList &found)
134{
135 QString localDir = Option::fixPathToLocalOS(dir, true);
136 if(!exists(localDir)) {
137 return false;
138 }
139
140 QString dllStem = stem + QTDLL_POSTFIX;
141
142 QString versionOverride;
143 if(!project->values("QMAKE_" + stem.toUpper() + "_VERSION_OVERRIDE").isEmpty())
144 versionOverride = project->values("QMAKE_" + stem.toUpper() + "_VERSION_OVERRIDE").first();
145
146 if (project->isActiveConfig("link_prl")) {
147 QMakeMetaInfo libinfo;
148 QString prl = QMakeMetaInfo::findLib(localDir + Option::dir_sep + dllStem);
149 if (project->values("QMAKE_PRL_INTERNAL_FILES").contains(prl)) {
150 // already processed, drop this stem
151 return true;
152 }
153 if (libinfo.readLib(prl)) {
154 // take the data from .prl
155 project->values("QMAKE_PRL_INTERNAL_FILES") += prl;
156 QString target = libinfo.first("QMAKE_PRL_TARGET");
157 if (target == project->first("TARGET")) {
158 // circular reference to ourselves, drop this stem
159 return true;
160 }
161 if (target.isEmpty())
162 target = dllStem;
163 if (!versionOverride.isEmpty())
164 target += versionOverride;
165 else if (!libinfo.values("QMAKE_PRL_CONFIG").contains("staticlib") &&
166 !libinfo.isEmpty("QMAKE_PRL_VERSION"))
167 target += libinfo.first("QMAKE_PRL_VERSION").replace(".", "");
168
169 // put the stem replacement as found in .prl
170 found << target;
171 // put all dependencies as is
172 foreach (const QString &lib, libinfo.values("QMAKE_PRL_LIBS"))
173 found << lib;
174
175 return true;
176 }
177 }
178
179 if (!versionOverride.isEmpty()) {
180 // unconditionally use verrsion overrride
181 found << stem + versionOverride;
182 return true;
183 }
184
185 int biggest = -1;
186 if(!project->isActiveConfig("no_versionlink")) {
187 // search for a library with the highest version number
188 QDir dir(localDir);
189 QStringList entries = dir.entryList();
190 QRegExp regx(QString("((lib)?%1([0-9]*))(%2)$").arg(dllStem).arg(ext),
191 Qt::CaseInsensitive);
192 foreach (const QString &ent, entries) {
193 if(regx.exactMatch(ent)) {
194 if (!regx.cap(3).isEmpty()) {
195 bool ok = true;
196 int num = regx.cap(3).toInt(&ok);
197 biggest = qMax(biggest, (!ok ? -1 : num));
198 }
199 }
200 }
201 }
202 if (biggest != -1) {
203 found << stem + QString::number(biggest);
204 return true;
205 }
206
207 return false;
208}
209
210bool GNUMakefileGenerator::findLibraries()
211{
212 return findLibraries("QMAKE_LIBS") && findLibraries("QMAKE_LIBS_PRIVATE");
213}
214
215bool GNUMakefileGenerator::findLibraries(const QString &where)
216{
217 QStringList &libs = project->values(where);
218
219 QList<QMakeLocalFileName> dirs;
220 foreach (const QString &path, project->values("QMAKE_LIBDIR"))
221 dirs.append(QMakeLocalFileName(path));
222
223 QStringList seen;
224
225 for (QStringList::Iterator it = libs.begin(); it != libs.end(); /*below*/) {
226 QString &lib = *it;
227
228 if (lib.startsWith("-L")) {
229 // this is a library path
230 dirs.append(QMakeLocalFileName(lib.mid(2)));
231 ++it;
232 continue;
233 }
234
235 // this must be a library file
236 if (lib.startsWith("-l"))
237 lib = lib.mid(2);
238
239 if (seen.contains(lib, Qt::CaseInsensitive)) {
240 // already processed, remove
241 it = libs.erase(it);
242 continue;
243 }
244
245 seen << lib;
246
247 QString suffix;
248 if (!project->isEmpty("QMAKE_" + lib.toUpper() + "_SUFFIX"))
249 suffix = project->first("QMAKE_" + lib.toUpper() + "_SUFFIX");
250
251 // try every libpath we have by now
252 bool found = false;
253 QStringList foundLibs;
254 foreach (const QMakeLocalFileName &dir, dirs) {
255 if ((found = findBestVersion(dir.local(), lib,
256 QString("%1.lib").arg(suffix), foundLibs)))
257 break;
258 }
259 if (!found) {
260 // assume the stem is the exact specification and use as is
261 foundLibs << lib;
262 }
263
264 if (foundLibs.isEmpty()) {
265 // circular reference detected, remove this library from the list
266 it = libs.erase(it);
267 } else {
268 // replace the library with the best found one
269 lib = foundLibs.takeFirst();
270 // this may introduce a duplicate, check that
271 bool remove = false;
272 for (QStringList::ConstIterator it2 = libs.begin(); it2 != it; ++it2)
273 if ((remove = it2->compare(lib, Qt::CaseInsensitive) == 0))
274 break;
275 // append the dependencies
276 if (!foundLibs.empty()) {
277 int itDiff = it - libs.begin();
278 foreach (const QString &foundLib, foundLibs) {
279 if (!libs.contains(foundLib, Qt::CaseInsensitive))
280 libs << foundLib;
281 }
282 // restore the iterator as appending invalidated it
283 it = libs.begin() + itDiff;
284 }
285 if (remove)
286 it = libs.erase(it);
287 else
288 ++it;
289 }
290 }
291
292 return true;
293}
294
295void GNUMakefileGenerator::writeHeader(QTextStream &t)
296{
297 MakefileGenerator::writeHeader(t);
298
299 /* function to convert from DOS-like to Unix-like space escaping in file
300 * names */
301 t << "null :=" << endl << "space := $(null) # end of the line" << endl;
302 t << "# function to change DOS-like space escaping to Unix-like" << endl;
303 t << "q = $(subst %,\\%,$(subst ;,\\;,$(subst &,\\&,"
304 "$(subst +,\\+,$(subst $(space),\\ ,$(subst \",,$(1)))))))" << endl << endl;
305
306 QString spec = specdir();
307 if (QFileInfo(spec).fileName() == "default") {
308 // use the real specdir; this allows to only have a dumb qmake.conf in
309 // the default mkspec dir and keep the rest in the real one (to avoid
310 // unnecessary duplication, in the absense of symlinks on OS/2)
311 QString origSpec = var("QMAKESPEC_ORIGINAL");
312 if (!origSpec.isEmpty()) {
313 // Note: Option::mkfile::qmakespec is asbolute, specdir() may be not
314 spec = QDir(Option::mkfile::qmakespec).absoluteFilePath(origSpec);
315 spec = fileFixify(QDir::cleanPath(spec));
316 }
317 }
318 project->values("QMAKESPECDIR") = QStringList(spec);
319 t << "QMAKESPECDIR = " << escapeFilePath(spec) << endl;
320
321 // MakefileGenerator::writeSubTargets() already puts the MAKEFILE definition
322 // for subdirs-like makefiles but we also use it in normal ones
323 if (project->first("TEMPLATE") != "subdirs" &&
324 (project->isActiveConfig("build_pass") || project->values("BUILDS").isEmpty())) {
325 QString ofile = escapeFilePath(Option::output.fileName());
326 if(ofile.lastIndexOf(Option::dir_sep) != -1)
327 ofile = ofile.right(ofile.length() - ofile.lastIndexOf(Option::dir_sep) -1);
328 t << "MAKEFILE = " << ofile << endl;
329 }
330
331 t << endl;
332}
333
334bool GNUMakefileGenerator::writeMakefile(QTextStream &t)
335{
336 writeHeader(t);
337
338 if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) {
339 t << "all clean:" << "\n\t"
340 << "@echo \"Some of the required modules ("
341 << var("QMAKE_FAILED_REQUIREMENTS") << ") are not available.\"" << "\n\t"
342 << "@echo \"Skipped.\"" << endl << endl;
343 writeMakeQmake(t);
344 return true;
345 }
346
347 if(project->first("TEMPLATE") == "app" ||
348 project->first("TEMPLATE") == "lib") {
349 if(Option::mkfile::do_stub_makefile) {
350 t << "QMAKE = " << (project->isEmpty("QMAKE_QMAKE") ? QString("qmake") : var("QMAKE_QMAKE")) << endl;
351 QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
352 for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it)
353 t << *it << " ";
354 t << "first all clean install distclean uninstall: qmake" << endl
355 << "qmake_all:" << endl;
356 writeMakeQmake(t);
357 if(project->isEmpty("QMAKE_NOFORCE"))
358 t << "FORCE:" << endl << endl;
359 return true;
360 }
361 writeGNUParts(t);
362 return MakefileGenerator::writeMakefile(t);
363 }
364 else if(project->first("TEMPLATE") == "subdirs") {
365 writeSubDirs(t);
366 if (project->isActiveConfig("ordered")) {
367 // Ordered builds are usually used when one sub-project links to
368 // another sub-project's library. Sometimes detecting the proper
369 // library name (e.g. the version suffix) requires at least a .prl
370 // for that library to exist. The .prl is created by qmake at Makefile
371 // generation time and these qmake calls are not actually ordered at
372 // dependency level so in case of parallel builds they may get
373 // executed simultaneously which breaks the build. It is not really
374 // possible to order them using dependencies because there are usually
375 // several target's flavors all depending on its Makefile and it's
376 // impossible to detect which flavor of the "parent" target this
377 // make file should depend on (depending on the "parent"'s Makefile
378 // is not enough because the required libraries may be generated by
379 // its own sub-projects at a later stage). One case showing this
380 // behavior is designer.exe depending on QtDesignerComponents.lib.
381 //
382 // The only solution to this is to disable parallelism for subdirs
383 // Makefiles completely. Which is not a big deal since "ordered"
384 // implies sequential building any way.
385 t << ".NOTPARALLEL:" << endl << endl;
386 }
387 return true;
388 }
389 return false;
390 }
391
392void GNUMakefileGenerator::writeGNUParts(QTextStream &t)
393{
394 writeStandardParts(t);
395
396 if (!preCompHeaderOut.isEmpty()) {
397 QString header = project->first("PRECOMPILED_HEADER");
398 QString cHeader = preCompHeaderOut + Option::dir_sep + "c";
399 t << escapeDependencyPath(cHeader) << ": " << escapeDependencyPath(header) << " "
400 << escapeDependencyPaths(findDependencies(header)).join(" \\\n\t\t")
401 << "\n\t" << mkdir_p_asstring(preCompHeaderOut)
402 << "\n\t" << "$(CC) -x c-header -c $(CFLAGS) $(INCPATH) -o " << cHeader << " " << header
403 << endl << endl;
404 QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++";
405 t << escapeDependencyPath(cppHeader) << ": " << escapeDependencyPath(header) << " "
406 << escapeDependencyPaths(findDependencies(header)).join(" \\\n\t\t")
407 << "\n\t" << mkdir_p_asstring(preCompHeaderOut)
408 << "\n\t" << "$(CXX) -x c++-header -c $(CXXFLAGS) $(INCPATH) -o " << cppHeader << " " << header
409 << endl << endl;
410 }
411}
412
413void GNUMakefileGenerator::init()
414{
415 if(init_flag)
416 return;
417 init_flag = true;
418
419 if(project->first("TEMPLATE") == "app") {
420 mode = App;
421 project->values("QMAKE_APP_FLAG").append("1");
422 } else if(project->first("TEMPLATE") == "lib") {
423 mode = project->isActiveConfig("staticlib") ? StaticLib : DLL;
424 project->values("QMAKE_LIB_FLAG").append("1");
425 } else if(project->first("TEMPLATE") == "subdirs") {
426 MakefileGenerator::init();
427 if(project->isEmpty("QMAKE_COPY_FILE"))
428 project->values("QMAKE_COPY_FILE").append("$(COPY)");
429 if(project->isEmpty("QMAKE_COPY_DIR"))
430 project->values("QMAKE_COPY_DIR").append("$(COPY)");
431 if(project->isEmpty("QMAKE_INSTALL_FILE"))
432 project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)");
433 if(project->isEmpty("QMAKE_INSTALL_PROGRAM"))
434 project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)");
435 if(project->isEmpty("QMAKE_INSTALL_DIR"))
436 project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)");
437 if(project->values("MAKEFILE").isEmpty())
438 project->values("MAKEFILE").append("Makefile");
439 if(project->values("QMAKE_QMAKE").isEmpty())
440 project->values("QMAKE_QMAKE").append("qmake");
441 return;
442 }
443
444 project->values("TARGET_PRL").append(project->first("TARGET"));
445
446 processVars();
447
448 // LIBS defined in Profile comes first for gcc
449 project->values("QMAKE_LIBS") += project->values("LIBS");
450 project->values("QMAKE_LIBS_PRIVATE") += project->values("LIBS_PRIVATE");
451
452 QStringList &configs = project->values("CONFIG");
453
454 if(project->isActiveConfig("qt_dll"))
455 if(configs.indexOf("qt") == -1)
456 configs.append("qt");
457
458 if(project->isActiveConfig("dll")) {
459 QString destDir = "";
460 if(!project->first("DESTDIR").isEmpty())
461 destDir = Option::fixPathToTargetOS(project->first("DESTDIR") + Option::dir_sep, false, false);
462 }
463
464 MakefileGenerator::init();
465
466 // precomp
467 if (!project->first("PRECOMPILED_HEADER").isEmpty()
468 && project->isActiveConfig("precompile_header")) {
469 QString preCompHeader = var("PRECOMPILED_DIR")
470 + QFileInfo(project->first("PRECOMPILED_HEADER")).fileName();
471 preCompHeaderOut = preCompHeader + ".gch";
472 project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c");
473 project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c++");
474
475 project->values("QMAKE_RUN_CC").clear();
476 project->values("QMAKE_RUN_CC").append("$(CC) -c -include " + preCompHeader +
477 " $(CFLAGS) $(INCPATH) -o $obj $src");
478 project->values("QMAKE_RUN_CC_IMP").clear();
479 project->values("QMAKE_RUN_CC_IMP").append("$(CC) -c -include " + preCompHeader +
480 " $(CFLAGS) $(INCPATH) -o $@ $<");
481 project->values("QMAKE_RUN_CXX").clear();
482 project->values("QMAKE_RUN_CXX").append("$(CXX) -c -include " + preCompHeader +
483 " $(CXXFLAGS) $(INCPATH) -o $obj $src");
484 project->values("QMAKE_RUN_CXX_IMP").clear();
485 project->values("QMAKE_RUN_CXX_IMP").append("$(CXX) -c -include " + preCompHeader +
486 " $(CXXFLAGS) $(INCPATH) -o $@ $<");
487 }
488}
489
490void GNUMakefileGenerator::fixTargetExt()
491{
492 if (mode == App)
493 project->values("TARGET_EXT").append(".exe");
494 else if (mode == DLL)
495 project->values("TARGET_EXT").append(project->first("TARGET_VERSION_EXT") + ".dll");
496 else
497 project->values("TARGET_EXT").append(".lib");
498}
499
500void GNUMakefileGenerator::writeIncPart(QTextStream &t)
501{
502 t << "INCPATH =";
503
504 QString opt = var("QMAKE_CFLAGS_INCDIR");
505 QStringList &incs = project->values("INCLUDEPATH");
506 QString incsSemicolon;
507 for(QStringList::Iterator incit = incs.begin(); incit != incs.end(); ++incit) {
508 QString inc = escapeFilePath(*incit);
509 t << " " << opt << inc;
510 incsSemicolon += inc + Option::dirlist_sep;
511 }
512 t << " " << opt << "$(QMAKESPECDIR)" << endl;
513 incsSemicolon += "$(QMAKESPECDIR)";
514 t << "INCLUDEPATH = " << incsSemicolon << endl;
515 /* createCompilerResponseFiles() will need QMAKESPECDIR expanded) */
516 project->values("INCLUDEPATH").append(var("QMAKESPECDIR"));
517}
518
519void GNUMakefileGenerator::writeLibsPart(QTextStream &t)
520{
521 if (mode == StaticLib) {
522 t << "LIB = " << var("QMAKE_LIB") << endl;
523 } else {
524 t << "LINK = " << var("QMAKE_LINK") << endl;
525 t << "LFLAGS = " << var("QMAKE_LFLAGS") << endl;
526 t << "LIBS =";
527 if(!project->values("QMAKE_LIBDIR").isEmpty())
528 writeLibDirPart(t);
529 QString opt = var("QMAKE_LFLAGS_LIB");
530 QString optL = var("QMAKE_LFLAGS_LIBDIR");
531 QStringList libVars;
532 libVars << "QMAKE_LIBS" << "QMAKE_LIBS_PRIVATE";
533 foreach(const QString &libVar, libVars) {
534 QStringList libs = project->values(libVar);
535 for(QStringList::Iterator it = libs.begin(); it != libs.end(); ++it) {
536 QString &lib = *it;
537
538 /* lib may be prefixed with -l which is commonly used in e.g. PRF
539 * (feature) files on all platforms; remove it before prepending
540 * the compiler-specific option. There is even more weird case
541 * when LIBS contains library paths prefixed with -L; we detect
542 * this as well and replace it with the proper option. */
543 if (lib.startsWith("-L")) {
544 lib = lib.mid(2);
545 t << " " << optL << escapeFilePath(lib);
546 } else {
547 if (lib.startsWith("-l"))
548 lib = lib.mid(2);
549 t << " " << opt << escapeFilePath(lib);
550 }
551 }
552 }
553 t << endl;
554 }
555}
556
557void GNUMakefileGenerator::writeLibDirPart(QTextStream &t)
558{
559 QString opt = var("QMAKE_LFLAGS_LIBDIR");
560 QStringList libDirs = project->values("QMAKE_LIBDIR");
561 for(QStringList::Iterator it = libDirs.begin(); it != libDirs.end(); ++it) {
562 QString libDir = escapeFilePath(*it);
563 /* libDir may be prefixed with -L which is commonly used in e.g. PRF
564 * (feature) files on all platforms; remove it before prepending
565 * the compiler-specific option */
566 if (libDir.startsWith("-L"))
567 libDir = libDir.mid(2);
568 t << " " << opt << libDir;
569 }
570}
571
572void GNUMakefileGenerator::writeObjectsPart(QTextStream &t)
573{
574 Win32MakefileGenerator::writeObjectsPart(t);
575 createLinkerResponseFiles(t);
576
577 /* this function is a nice place to also handle compiler options response
578 * files */
579 createCompilerResponseFiles(t);
580}
581
582void GNUMakefileGenerator::writeBuildRulesPart(QTextStream &t)
583{
584 t << "first: all" << endl;
585 t << "all: " << escapeDependencyPath(fileFixify(Option::output.fileName())) << " "
586 << valGlue(escapeDependencyPaths(project->values("ALL_DEPS"))," "," "," ")
587 << escapeFileVars(" $(DESTDIR_TARGET)") << endl << endl;
588 t << escapeFileVars("$(DESTDIR_TARGET): ") << escapeFileVars(var("PRE_TARGETDEPS"))
589 << escapeFileVars(" $(OBJECTS) ")
590 << escapeFileVars(var("POST_TARGETDEPS"));
591 if (!project->isEmpty("QMAKE_PRE_LINK"))
592 t << "\n\t" <<var("QMAKE_PRE_LINK");
593 if (mode == StaticLib) {
594 /* static library */
595 t << "\n\t" << var("QMAKE_RUN_LIB");
596 } else {
597 /* application or DLL */
598 t << "\n\t" << var("QMAKE_RUN_LINK");
599 if (!project->isEmpty("RES_FILE") && !project->isEmpty("QMAKE_RUN_RC2EXE")) {
600 t << "\n\t" << var("QMAKE_RUN_RC2EXE");
601 }
602 }
603 if(!project->isEmpty("QMAKE_POST_LINK"))
604 t << "\n\t" <<var("QMAKE_POST_LINK");
605 t << endl;
606}
607
608void GNUMakefileGenerator::writeRcAndDefVariables(QTextStream &t)
609{
610 if (!project->isEmpty("RC_FILE")) {
611 t << "RC_FILE = " << escapeFilePath(fileFixify(var("RC_FILE"))) << endl;
612 }
613 if (!project->isEmpty("RES_FILE")) {
614 t << "RES_FILE = " << valList(escapeFilePaths(fileFixify(project->values("RES_FILE")))) << endl;
615 }
616
617 if (mode != StaticLib) {
618 if (project->isEmpty("DEF_FILE")) {
619 /* no DEF file supplied, emit handy variable definitions to simplify
620 * DEF generation rules defined somewhere in default_post.prf */
621 bool haveSomething = false;
622 if (!project->isEmpty("DEF_FILE_VERSION")) {
623 t << "DEF_FILE_VERSION = " << var("DEF_FILE_VERSION") << endl;
624 haveSomething = true;
625 }
626 if (!project->isEmpty("DEF_FILE_DESCRIPTION")) {
627 t << "DEF_FILE_DESCRIPTION = " << var("DEF_FILE_DESCRIPTION") << endl;
628 haveSomething = true;
629 }
630 if (!project->isEmpty("DEF_FILE_VENDOR")) {
631 t << "DEF_FILE_VENDOR = " << var("DEF_FILE_VENDOR") << endl;
632 haveSomething = true;
633 }
634 if (!project->isEmpty("DEF_FILE_TEMPLATE")) {
635 t << "DEF_FILE_TEMPLATE = " << escapeFilePath(fileFixify(var("DEF_FILE_TEMPLATE"))) << endl;
636 haveSomething = true;
637 }
638 if (!project->isEmpty("DEF_FILE_MAP")) {
639 t << "DEF_FILE_MAP = " << escapeFilePath(fileFixify(var("DEF_FILE_MAP"))) << endl;
640 haveSomething = true;
641 }
642 if (mode == DLL) {
643 // the DLL needs a file in any case
644 t << "DEF_FILE = $(basename $(DESTDIR_TARGET)).def" << endl;
645 } else if (haveSomething) {
646 // the EXE needs it only if there's a description info
647 t << "DEF_FILE_EXETYPE = " << var("DEF_FILE_EXETYPE") << endl;
648 t << "DEF_FILE = $(OBJECTS_DIR)\\$(TARGET).def" << endl;
649 }
650 } else {
651 if (!project->isEmpty("DEF_FILE_TEMPLATE")) {
652 fprintf(stderr, "Both DEF_FILE and DEF_FILE_TEMPLATE are specified.\n");
653 fprintf(stderr, "Please specify one of them, not both.");
654 exit(1);
655 }
656 t << "DEF_FILE = " << escapeFilePath(fileFixify(var("DEF_FILE"))) << endl;
657 }
658 }
659
660 if (mode == DLL) {
661 // Note: $(TARGET) may be shortened here due to TARGET_SHORT, use
662 // getLibTarget() instead (shortening does not affect implib names)
663 t << "TARGET_IMPLIB = $(DESTDIR)\\" << getLibTarget() << endl;
664 }
665}
666
667void GNUMakefileGenerator::writeRcAndDefPart(QTextStream &t)
668{
669 if (!project->isEmpty("RC_FILE") && !project->isEmpty("RES_FILE") &&
670 !project->isEmpty("QMAKE_RUN_RC2RES")) {
671 if (!project->isEmpty("QMAKE_RUN_RC2RES_ENV"))
672 t << escapeFileVars("$(RES_FILE): ")
673 << var("QMAKE_RUN_RC2RES_ENV") << endl;
674 t << escapeFileVars("$(RES_FILE): $(RC_FILE)\n\t");
675 t << var("QMAKE_RUN_RC2RES") << endl << endl;
676 }
677}
678
679void GNUMakefileGenerator::processRcFileVar()
680{
681 if (Option::qmake_mode == Option::QMAKE_GENERATE_NOTHING)
682 return;
683
684 if (!project->isEmpty("RC_FILE")) {
685 if (!project->isEmpty("RES_FILE")) {
686 fprintf(stderr, "Both rc and res file specified.\n");
687 fprintf(stderr, "Please specify one of them, not both.");
688 exit(1);
689 }
690 project->values("RES_FILE").prepend(escapeFilePath(QString("$(OBJECTS_DIR)") +
691 QDir::separator() +
692 QFileInfo(var("RC_FILE")).baseName() +
693 ".res"));
694 project->values("CLEAN_FILES") += "$(RES_FILE)";
695 }
696
697 if (!project->isEmpty("RES_FILE"))
698 project->values("POST_TARGETDEPS") += escapeFileVars("$(RES_FILE)");
699}
700
701void GNUMakefileGenerator::processPrlVariable(const QString &var, const QStringList &l)
702{
703 // we don't do any processing here; everything we need is done in
704 // GNUMakefileGenerator::findLibraries()
705 return;
706}
707
708void GNUMakefileGenerator::processPrlFiles()
709{
710 // we don't do any processing here; everything we need is done in
711 // GNUMakefileGenerator::findLibraries()
712 return;
713}
714
715QString GNUMakefileGenerator::getPdbTarget()
716{
717 // we don't have .pdb files (.map and .sym are processed elsewere)
718 return QString::null;
719}
720
721QString GNUMakefileGenerator::filePrefixRoot(const QString &root, const QString &path)
722{
723 QString ret;
724 if(path.length() > 2 && path[1] == ':') { //c:\foo
725 if (root == "$(INSTALL_ROOT)")
726 ret = QString("$(if %1,%1,%2)").arg(root, path.left(2)) + path.mid(2);
727 else
728 ret = QString(path.left(2) + root + path.mid(2));
729 } else {
730 ret = root + path;
731 }
732 while(ret.endsWith("\\"))
733 ret = ret.left(ret.length()-1);
734 return ret;
735}
736
737QStringList &GNUMakefileGenerator::findDependencies(const QString &file)
738{
739 QStringList &aList = MakefileGenerator::findDependencies(file);
740 // Note: The QMAKE_IMAGE_COLLECTION file have all images
741 // as dependency, so don't add precompiled header then
742 if (file == project->first("QMAKE_IMAGE_COLLECTION")
743 || preCompHeaderOut.isEmpty())
744 return aList;
745 for (QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) {
746 if (file.endsWith(*it)) {
747 QString cHeader = preCompHeaderOut + Option::dir_sep + "c";
748 if (!aList.contains(cHeader))
749 aList += cHeader;
750 break;
751 }
752 }
753 for (QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) {
754 if (file.endsWith(*it)) {
755 QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++";
756 if (!aList.contains(cppHeader))
757 aList += cppHeader;
758 break;
759 }
760 }
761 return aList;
762}
763
764void GNUMakefileGenerator::writeProjectVarToStream(QTextStream &t, const QString &var)
765{
766 QStringList &list = project->values(var);
767 for(QStringList::Iterator it = list.begin(); it != list.end(); ++it) {
768 t << (*it) << endl;
769 }
770}
771
772QString GNUMakefileGenerator::makeResponseFileName(const QString &base)
773{
774 QString fileName = base + "." + var("TARGET");
775 if (!var("BUILD_NAME").isEmpty()) {
776 fileName += "." + var("BUILD_NAME");
777 }
778 fileName += ".rsp";
779 QString filePath = project->first("OBJECTS_DIR");
780 if (filePath.isEmpty())
781 filePath = fileFixify(Option::output_dir);
782 filePath = Option::fixPathToTargetOS(filePath + QDir::separator() + fileName);
783 return filePath;
784}
785
786static QStringList fixDefines(const QStringList &vals)
787{
788 // Existing GCC 4.x.x builds for OS/2 can't handle escaping meta symbols
789 // (e.g. quotes) with backslashes in response files (probably an OS/2-
790 // specific bug). The fix this replacing all "\<char>" occurences with
791 // "'<char>'"-like singlequoting which works.
792 //
793 // This backslash escaping is so frequently used in .pro files to pass
794 // string defines to C/C++ (in the form of "DEFINES += VAR=\\\"STRING\\\")
795 // that it makes sense to fix it here rather than introduce an OS/2-specific
796 // alteration of the DEFINES statement in each .pro file.
797
798 QStringList result;
799 foreach(const QString &val, vals) {
800 result << QString(val).replace(QRegExp("\\\\(.)"), "'\\1'");
801 }
802 return result;
803}
804
805void GNUMakefileGenerator::createCompilerResponseFiles(QTextStream &t)
806{
807 static const char *vars[] = { "CFLAGS", "CXXFLAGS", "DEFINES", "INCPATH" };
808
809 /* QMAKE_XXX_RSP_VAR is used as a flag whether it is necessary to
810 * generate response files to overcome the 1024 chars CMD.EXE limitation.
811 * When this variable is defined, a response file with the relevant
812 * information will be generated and its full path will be stored in an
813 * environment variable with the given name which can then be referred to in
814 * other places of qmake.conf (e.g. rules) */
815
816 for (size_t i = 0; i < sizeof(vars)/sizeof(vars[0]); ++i) {
817 QString rspVar =
818 project->first(QString().sprintf("QMAKE_%s_RSP_VAR", vars[i]));
819 if (!rspVar.isEmpty()) {
820 QString fileName = makeResponseFileName(vars[i]);
821 t << rspVar.leftJustified(14) << "= " << fileName << endl;
822 QFile file(QDir(Option::output_dir).absoluteFilePath(fileName));
823 if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
824 QTextStream rt(&file);
825 if (!qstrcmp(vars[i], "CFLAGS")) {
826 rt << varGlue("QMAKE_CFLAGS", QString::null, "\n", "\n");
827 } else if (!qstrcmp(vars[i], "CXXFLAGS")) {
828 rt << varGlue("QMAKE_CXXFLAGS", QString::null, "\n", "\n");
829 } else if (!qstrcmp(vars[i], "DEFINES")) {
830 rt << valGlue(fixDefines(project->values("PRL_EXPORT_DEFINES")),
831 "-D", "\n-D", "\n")
832 << valGlue(fixDefines(project->values("DEFINES")),
833 "-D", "\n-D", "\n");
834 } else if (!qstrcmp(vars[i], "INCPATH")) {
835 QString opt = var("QMAKE_CFLAGS_INCDIR");
836 rt << varGlue("INCLUDEPATH", opt, "\n" + opt, "\n");
837 } else {
838 Q_ASSERT(false);
839 }
840 rt.flush();
841 file.close();
842 }
843 project->values("QMAKE_DISTCLEAN").append("$(" + rspVar + ")");
844 }
845 }
846}
847
848void GNUMakefileGenerator::createLinkerResponseFiles(QTextStream &t)
849{
850 /* see createCompilerResponseFiles() */
851 QString var = project->first("QMAKE_OBJECTS_RSP_VAR");
852 if (!var.isEmpty()) {
853 QString fileName = makeResponseFileName("OBJECTS");
854 t << var.leftJustified(14) << "= " << fileName << endl;
855 QFile file(QDir(Option::output_dir).absoluteFilePath(fileName));
856 if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
857 QTextStream rt(&file);
858 rt << varGlue("OBJECTS", QString::null, "\n", "\n");
859 rt.flush();
860 file.close();
861 }
862 project->values("QMAKE_DISTCLEAN").append("$(" + var + ")");
863 }
864}
865
866// static
867QString GNUMakefileGenerator::escapeFileVars(const QString &vars, bool *matched)
868{
869 /* In DOS environment, we escape spaces and other illegal characters in
870 * filenames with double quotes. However, this is not appropriate for GNU
871 * make rule definitions (targets/dependencies) where Unix escaping is
872 * expected. If we'd deal only with immediate strings, we could provide
873 * necessary escaping in place, but we often use make variables instead of
874 * direct file names so we must perform such escaping on the fly. This is
875 * what we do here using the q function that we define in writeMakefile().*/
876 QString ret = vars;
877 QRegExp rx = QRegExp("(?:\\$\\(call q,\\$\\((.+)\\)\\))|"
878 "(?:\\$\\(([^$)]+)\\))");
879 rx.setMinimal(true);
880 int pos = 0;
881 bool wasMatch = false;
882 while ((pos = rx.indexIn(ret, pos)) != -1) {
883 wasMatch = true;
884 QString var = rx.cap(1);
885 if (var.isEmpty())
886 var = rx.cap(2);
887 // there are some make variables that contain multiple paths which we
888 // cannot escape (finding double-quoted spaces is too complex for
889 // the q function), so just skip them hoping they contain no spaces...
890 if (var != "OBJECTS") {
891 var = QString("$(call q,$(%1))").arg(var);
892 ret.replace(pos, rx.matchedLength(), var);
893 pos += var.length();
894 } else {
895 pos += rx.matchedLength();
896 }
897 }
898 if (matched)
899 *matched = wasMatch;
900 return ret;
901}
902
903QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.