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

Last change on this file since 751 was 673, checked in by Dmitry A. Kuminov, 16 years ago

qmake: os2/gnumake: Replaced hard-coded DEF_FILE generation target with more flexible and consistent code in default_post.prf.

File size: 30.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 if (!isDosLikeShell()) {
75 ret.remove('\"');
76 ret.replace('\\', "/");
77 ret.replace(' ', "\\ ");
78 } else {
79 ret = Option::fixPathToTargetOS(ret, false);
80 ret.replace(QRegExp("\""), "");
81 ret.replace(QRegExp("[\\\\/]$"), "");
82 if (ret.contains(QRegExp("[ +&;%]")))
83 ret = quote + ret + quote;
84 }
85 return ret;
86}
87
88QString GNUMakefileGenerator::escapeDependencyPath(const QString &path) const
89{
90 // dependency escaping is always done Unix-way since this is a requirement
91 // of GNU make which allows " and ' to be part of the file name.
92 // Note that if the string is a make variable reference, we don't eascape
93 // but instead use the q function defined in writeMakefile() that will do
94 // it at runtime.
95 QString ret = path;
96 QString trimmed = path.trimmed();
97 if (!trimmed.startsWith("$(") || !trimmed.endsWith(")")) {
98 ret.remove('\"');
99 ret.replace(' ', "\\ ");
100 } else {
101 ret = escapeFileVars(ret);
102 }
103 return ret;
104}
105
106// Searches for the best version of the requested library named \a stem in
107// \a dir and append the result to the \a found list. If the best version is
108// found, it is appended first in the list, otherwise \a stem is appended as
109// is. If a .prl file corresponding to \a stem is found, the library name is
110// taken from there. If the .prl refers to other libraries the found one
111// depends on, they are also appended to the returned list (as is).
112//
113// The function returns \c true if the best version for the given \a stem was
114// actually found and \c false otherwise (\a found will remain untouched in this
115// case).
116//
117// Note that if a circular reference to the project's target library or to a
118// library we already processed is detected, the function returns \c true but
119// \a found remains untouched; this indicates that the given \a stem should be
120// dropped from the resulting library list at all.
121bool
122GNUMakefileGenerator::findBestVersion(const QString &dir, const QString &stem,
123 const QString &ext, QStringList &found)
124{
125 QString localDir = Option::fixPathToLocalOS(dir, true);
126 if(!exists(localDir)) {
127 return false;
128 }
129
130 QString dllStem = stem + QTDLL_POSTFIX;
131
132 QString versionOverride;
133 if(!project->values("QMAKE_" + stem.toUpper() + "_VERSION_OVERRIDE").isEmpty())
134 versionOverride = project->values("QMAKE_" + stem.toUpper() + "_VERSION_OVERRIDE").first();
135
136 if (project->isActiveConfig("link_prl")) {
137 QMakeMetaInfo libinfo;
138 QString prl = QMakeMetaInfo::findLib(localDir + Option::dir_sep + dllStem);
139 if (project->values("QMAKE_PRL_INTERNAL_FILES").contains(prl)) {
140 // already processed, drop this stem
141 return true;
142 }
143 if (libinfo.readLib(prl)) {
144 // take the data from .prl
145 project->values("QMAKE_PRL_INTERNAL_FILES") += prl;
146 QString target = libinfo.first("QMAKE_PRL_TARGET");
147 if (target == project->first("TARGET")) {
148 // circular reference to ourselves, drop this stem
149 return true;
150 }
151 if (target.isEmpty())
152 target = dllStem;
153 if (!versionOverride.isEmpty())
154 target += versionOverride;
155 else if (!libinfo.values("QMAKE_PRL_CONFIG").contains("staticlib") &&
156 !libinfo.isEmpty("QMAKE_PRL_VERSION"))
157 target += libinfo.first("QMAKE_PRL_VERSION").replace(".", "");
158
159 // put the stem replacement as found in .prl
160 found << target;
161 // put all dependencies as is
162 foreach (const QString &lib, libinfo.values("QMAKE_PRL_LIBS"))
163 found << lib;
164
165 return true;
166 }
167 }
168
169 if (!versionOverride.isEmpty()) {
170 // unconditionally use verrsion overrride
171 found << stem + versionOverride;
172 return true;
173 }
174
175 int biggest = -1;
176 if(!project->isActiveConfig("no_versionlink")) {
177 // search for a library with the highest version number
178 QDir dir(localDir);
179 QStringList entries = dir.entryList();
180 QRegExp regx(QString("((lib)?%1([0-9]*))(%2)$").arg(dllStem).arg(ext),
181 Qt::CaseInsensitive);
182 foreach (const QString &ent, entries) {
183 if(regx.exactMatch(ent)) {
184 if (!regx.cap(3).isEmpty()) {
185 bool ok = true;
186 int num = regx.cap(3).toInt(&ok);
187 biggest = qMax(biggest, (!ok ? -1 : num));
188 }
189 }
190 }
191 }
192 if (biggest != -1) {
193 found << stem + QString::number(biggest);
194 return true;
195 }
196
197 return false;
198}
199
200bool GNUMakefileGenerator::findLibraries()
201{
202 return findLibraries("QMAKE_LIBS") && findLibraries("QMAKE_LIBS_PRIVATE");
203}
204
205bool GNUMakefileGenerator::findLibraries(const QString &where)
206{
207 QStringList &libs = project->values(where);
208
209 QList<QMakeLocalFileName> dirs;
210 foreach (const QString &path, project->values("QMAKE_LIBDIR"))
211 dirs.append(QMakeLocalFileName(path));
212
213 QStringList seen;
214
215 for (QStringList::Iterator it = libs.begin(); it != libs.end(); /*below*/) {
216 QString &lib = *it;
217
218 if (lib.startsWith("-L")) {
219 // this is a library path
220 dirs.append(QMakeLocalFileName((*it).mid(2)));
221 ++it;
222 continue;
223 }
224
225 // this must be a library file
226 if (lib.startsWith("-l"))
227 lib = lib.mid(2);
228
229 if (seen.contains(lib, Qt::CaseInsensitive)) {
230 // already processed, remove
231 it = libs.erase(it);
232 continue;
233 }
234
235 seen << lib;
236
237 QString suffix;
238 if (!project->isEmpty("QMAKE_" + lib.toUpper() + "_SUFFIX"))
239 suffix = project->first("QMAKE_" + lib.toUpper() + "_SUFFIX");
240
241 // try every libpath we have by now
242 bool found = false;
243 QStringList foundLibs;
244 foreach (const QMakeLocalFileName &dir, dirs) {
245 if ((found = findBestVersion(dir.local(), lib,
246 QString("%1.lib").arg(suffix), foundLibs)))
247 break;
248 }
249 if (!found) {
250 // assume the stem is the exact specification and use as is
251 foundLibs << lib;
252 }
253
254 if (foundLibs.isEmpty()) {
255 // circular reference detected, remove this library from the list
256 it = libs.erase(it);
257 } else {
258 // replace the library with the best found one
259 lib = foundLibs.takeFirst();
260 // this may introduce a duplicate, check that
261 bool remove = false;
262 for (QStringList::ConstIterator it2 = libs.begin(); it2 != it; ++it2)
263 if ((remove = it2->compare(lib, Qt::CaseInsensitive) == 0))
264 break;
265 // append the dependencies
266 if (!foundLibs.empty()) {
267 int itDiff = it - libs.begin();
268 foreach (const QString &foundLib, foundLibs) {
269 if (!libs.contains(foundLib, Qt::CaseInsensitive))
270 libs << foundLib;
271 }
272 // restore the iterator as appending invalidated it
273 it = libs.begin() + itDiff;
274 }
275 if (remove)
276 it = libs.erase(it);
277 else
278 ++it;
279 }
280 }
281
282 return true;
283}
284
285bool GNUMakefileGenerator::writeMakefile(QTextStream &t)
286{
287 writeHeader(t);
288
289 /* function to convert from DOS-like to Unix-like space escaping in file
290 * names */
291 t << "null :=" << endl << "space := $(null) # end of the line" << endl;
292 t << "# function to change DOS-like space escaping to Unix-like" << endl;
293 t << "q = $(subst %,\\%,$(subst ;,\\;,$(subst &,\\&,"
294 "$(subst +,\\+,$(subst $(space),\\ ,$(subst \",,$(1)))))))" << endl << endl;
295
296 t << "QMAKESPECDIR = " << escapeFilePath(specdir()) << endl;
297
298 QString ofile = escapeFilePath(Option::output.fileName());
299 if(ofile.lastIndexOf(Option::dir_sep) != -1)
300 ofile = ofile.right(ofile.length() - ofile.lastIndexOf(Option::dir_sep) -1);
301 t << "MAKEFILE = " << ofile << endl << endl;
302
303 if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) {
304 t << "all clean:" << "\n\t"
305 << "@echo \"Some of the required modules ("
306 << var("QMAKE_FAILED_REQUIREMENTS") << ") are not available.\"" << "\n\t"
307 << "@echo \"Skipped.\"" << endl << endl;
308 writeMakeQmake(t);
309 return true;
310 }
311
312 if(project->first("TEMPLATE") == "app" ||
313 project->first("TEMPLATE") == "lib") {
314 if(Option::mkfile::do_stub_makefile) {
315 t << "QMAKE = " << (project->isEmpty("QMAKE_QMAKE") ? QString("qmake") : var("QMAKE_QMAKE")) << endl;
316 QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
317 for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it)
318 t << *it << " ";
319 t << "first all clean install distclean uninstall: qmake" << endl
320 << "qmake_all:" << endl;
321 writeMakeQmake(t);
322 if(project->isEmpty("QMAKE_NOFORCE"))
323 t << "FORCE:" << endl << endl;
324 return true;
325 }
326 writeGNUParts(t);
327 return MakefileGenerator::writeMakefile(t);
328 }
329 else if(project->first("TEMPLATE") == "subdirs") {
330 writeSubDirs(t);
331 return true;
332 }
333 return false;
334 }
335
336void GNUMakefileGenerator::writeGNUParts(QTextStream &t)
337{
338 writeStandardParts(t);
339
340 if (!preCompHeaderOut.isEmpty()) {
341 QString header = project->first("PRECOMPILED_HEADER");
342 QString cHeader = preCompHeaderOut + Option::dir_sep + "c";
343 t << escapeDependencyPath(cHeader) << ": " << escapeDependencyPath(header) << " "
344 << escapeDependencyPaths(findDependencies(header)).join(" \\\n\t\t")
345 << "\n\t" << mkdir_p_asstring(preCompHeaderOut)
346 << "\n\t" << "$(CC) -x c-header -c $(CFLAGS) $(INCPATH) -o " << cHeader << " " << header
347 << endl << endl;
348 QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++";
349 t << escapeDependencyPath(cppHeader) << ": " << escapeDependencyPath(header) << " "
350 << escapeDependencyPaths(findDependencies(header)).join(" \\\n\t\t")
351 << "\n\t" << mkdir_p_asstring(preCompHeaderOut)
352 << "\n\t" << "$(CXX) -x c++-header -c $(CXXFLAGS) $(INCPATH) -o " << cppHeader << " " << header
353 << endl << endl;
354 }
355}
356
357void GNUMakefileGenerator::init()
358{
359 if(init_flag)
360 return;
361 init_flag = true;
362
363 if(project->first("TEMPLATE") == "app") {
364 mode = App;
365 project->values("QMAKE_APP_FLAG").append("1");
366 } else if(project->first("TEMPLATE") == "lib") {
367 mode = project->isActiveConfig("staticlib") ? StaticLib : DLL;
368 project->values("QMAKE_LIB_FLAG").append("1");
369 } else if(project->first("TEMPLATE") == "subdirs") {
370 MakefileGenerator::init();
371 if(project->isEmpty("QMAKE_COPY_FILE"))
372 project->values("QMAKE_COPY_FILE").append("$(COPY)");
373 if(project->isEmpty("QMAKE_COPY_DIR"))
374 project->values("QMAKE_COPY_DIR").append("$(COPY)");
375 if(project->isEmpty("QMAKE_INSTALL_FILE"))
376 project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)");
377 if(project->isEmpty("QMAKE_INSTALL_PROGRAM"))
378 project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)");
379 if(project->isEmpty("QMAKE_INSTALL_DIR"))
380 project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)");
381 if(project->values("MAKEFILE").isEmpty())
382 project->values("MAKEFILE").append("Makefile");
383 if(project->values("QMAKE_QMAKE").isEmpty())
384 project->values("QMAKE_QMAKE").append("qmake");
385 return;
386 }
387
388 project->values("TARGET_PRL").append(project->first("TARGET"));
389
390 processVars();
391
392 // LIBS defined in Profile comes first for gcc
393 project->values("QMAKE_LIBS") += escapeFilePaths(project->values("LIBS"));
394 project->values("QMAKE_LIBS_PRIVATE") += escapeFilePaths(project->values("LIBS_PRIVATE"));
395
396 QStringList &configs = project->values("CONFIG");
397
398 if(project->isActiveConfig("qt_dll"))
399 if(configs.indexOf("qt") == -1)
400 configs.append("qt");
401
402 if(project->isActiveConfig("dll")) {
403 QString destDir = "";
404 if(!project->first("DESTDIR").isEmpty())
405 destDir = Option::fixPathToTargetOS(project->first("DESTDIR") + Option::dir_sep, false, false);
406 }
407
408 MakefileGenerator::init();
409
410 // precomp
411 if (!project->first("PRECOMPILED_HEADER").isEmpty()
412 && project->isActiveConfig("precompile_header")) {
413 QString preCompHeader = var("PRECOMPILED_DIR")
414 + QFileInfo(project->first("PRECOMPILED_HEADER")).fileName();
415 preCompHeaderOut = preCompHeader + ".gch";
416 project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c");
417 project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c++");
418
419 project->values("QMAKE_RUN_CC").clear();
420 project->values("QMAKE_RUN_CC").append("$(CC) -c -include " + preCompHeader +
421 " $(CFLAGS) $(INCPATH) -o $obj $src");
422 project->values("QMAKE_RUN_CC_IMP").clear();
423 project->values("QMAKE_RUN_CC_IMP").append("$(CC) -c -include " + preCompHeader +
424 " $(CFLAGS) $(INCPATH) -o $@ $<");
425 project->values("QMAKE_RUN_CXX").clear();
426 project->values("QMAKE_RUN_CXX").append("$(CXX) -c -include " + preCompHeader +
427 " $(CXXFLAGS) $(INCPATH) -o $obj $src");
428 project->values("QMAKE_RUN_CXX_IMP").clear();
429 project->values("QMAKE_RUN_CXX_IMP").append("$(CXX) -c -include " + preCompHeader +
430 " $(CXXFLAGS) $(INCPATH) -o $@ $<");
431 }
432}
433
434void GNUMakefileGenerator::fixTargetExt()
435{
436 if (mode == App)
437 project->values("TARGET_EXT").append(".exe");
438 else if (mode == DLL)
439 project->values("TARGET_EXT").append(project->first("TARGET_VERSION_EXT") + ".dll");
440 else
441 project->values("TARGET_EXT").append(".lib");
442}
443
444void GNUMakefileGenerator::writeIncPart(QTextStream &t)
445{
446 t << "INCPATH =";
447
448 QString opt = var("QMAKE_CFLAGS_INCDIR");
449 QStringList &incs = project->values("INCLUDEPATH");
450 QString incsSemicolon;
451 for(QStringList::Iterator incit = incs.begin(); incit != incs.end(); ++incit) {
452 QString inc = escapeFilePath(*incit);
453 t << " " << opt << inc;
454 incsSemicolon += inc + Option::dirlist_sep;
455 }
456 t << " " << opt << "$(QMAKESPECDIR)" << endl;
457 incsSemicolon += "$(QMAKESPECDIR)";
458 t << "INCLUDEPATH = " << incsSemicolon << endl;
459 /* createCompilerResponseFiles() will need QAKESPECDIR expanded) */
460 project->values("INCLUDEPATH").append(specdir());
461}
462
463void GNUMakefileGenerator::writeLibsPart(QTextStream &t)
464{
465 if (mode == StaticLib) {
466 t << "LIB = " << var("QMAKE_LIB") << endl;
467 } else {
468 t << "LINK = " << var("QMAKE_LINK") << endl;
469 t << "LFLAGS = " << var("QMAKE_LFLAGS") << endl;
470 t << "LIBS =";
471 if(!project->values("QMAKE_LIBDIR").isEmpty())
472 writeLibDirPart(t);
473 QString opt = var("QMAKE_LFLAGS_LIB");
474 QString optL = var("QMAKE_LFLAGS_LIBDIR");
475 QStringList libVars;
476 libVars << "QMAKE_LIBS" << "QMAKE_LIBS_PRIVATE";
477 foreach(const QString &libVar, libVars) {
478 QStringList libs = project->values(libVar);
479 for(QStringList::Iterator it = libs.begin(); it != libs.end(); ++it) {
480 QString lib = escapeFilePath(*it);
481 /* lib may be prefixed with -l which is commonly used in e.g. PRF
482 * (feature) files on all platforms; remove it before prepending
483 * the compiler-specific option. There is even more weird case
484 * when LIBS contains library paths prefixed with -L; we detect
485 * this as well and replace it with the proper option. */
486 if (lib.startsWith("-L")) {
487 lib = lib.mid(2);
488 t << " " << optL << lib;
489 } else {
490 if (lib.startsWith("-l"))
491 lib = lib.mid(2);
492 t << " " << opt << lib;
493 }
494 }
495 }
496 t << endl;
497 }
498}
499
500void GNUMakefileGenerator::writeLibDirPart(QTextStream &t)
501{
502 QString opt = var("QMAKE_LFLAGS_LIBDIR");
503 QStringList libDirs = project->values("QMAKE_LIBDIR");
504 for(QStringList::Iterator it = libDirs.begin(); it != libDirs.end(); ++it) {
505 QString libDir = escapeFilePath(*it);
506 /* libDir may be prefixed with -L which is commonly used in e.g. PRF
507 * (feature) files on all platforms; remove it before prepending
508 * the compiler-specific option */
509 if (libDir.startsWith("-L"))
510 libDir = libDir.mid(2);
511 t << " " << opt << libDir;
512 }
513}
514
515void GNUMakefileGenerator::writeObjectsPart(QTextStream &t)
516{
517 Win32MakefileGenerator::writeObjectsPart(t);
518 createLinkerResponseFiles(t);
519
520 /* this function is a nice place to also handle compiler options response
521 * files */
522 createCompilerResponseFiles(t);
523}
524
525void GNUMakefileGenerator::writeBuildRulesPart(QTextStream &t)
526{
527 t << "first: all" << endl;
528 t << "all: " << escapeDependencyPath(fileFixify(Option::output.fileName())) << " "
529 << valGlue(escapeDependencyPaths(project->values("ALL_DEPS"))," "," "," ")
530 << escapeFileVars(" $(DESTDIR_TARGET)") << endl << endl;
531 t << escapeFileVars("$(DESTDIR_TARGET): ") << escapeFileVars(var("PRE_TARGETDEPS"))
532 << escapeFileVars(" $(OBJECTS) ")
533 << escapeFileVars(var("POST_TARGETDEPS"));
534 if (!project->isEmpty("QMAKE_PRE_LINK"))
535 t << "\n\t" <<var("QMAKE_PRE_LINK");
536 if (mode == StaticLib) {
537 /* static library */
538 t << "\n\t" << var("QMAKE_RUN_LIB");
539 } else {
540 /* application or DLL */
541 t << "\n\t" << var("QMAKE_RUN_LINK");
542 if (!project->isEmpty("RES_FILE") && !project->isEmpty("QMAKE_RUN_RC2EXE")) {
543 t << "\n\t" << var("QMAKE_RUN_RC2EXE");
544 }
545 }
546 if(!project->isEmpty("QMAKE_POST_LINK"))
547 t << "\n\t" <<var("QMAKE_POST_LINK");
548 t << endl;
549}
550
551void GNUMakefileGenerator::writeRcAndDefVariables(QTextStream &t)
552{
553 if (!project->isEmpty("RC_FILE")) {
554 t << "RC_FILE = " << escapeFilePath(var("RC_FILE")) << endl;
555 }
556 if (!project->isEmpty("RES_FILE")) {
557 t << "RES_FILE = " << valList(escapeFilePaths(project->values("RES_FILE"))) << endl;
558 }
559
560 if (mode != StaticLib) {
561 if (project->isEmpty("DEF_FILE")) {
562 /* no DEF file supplied, emit handy variable definitions to simplify
563 * DEF generation rules defined somewhere in default_post.prf */
564 bool haveSomething = false;
565 if (!project->isEmpty("DEF_FILE_VERSION")) {
566 t << "DEF_FILE_VERSION = " << var("DEF_FILE_VERSION") << endl;
567 haveSomething = true;
568 }
569 if (!project->isEmpty("DEF_FILE_DESCRIPTION")) {
570 t << "DEF_FILE_DESCRIPTION = " << var("DEF_FILE_DESCRIPTION") << endl;
571 haveSomething = true;
572 }
573 if (!project->isEmpty("DEF_FILE_VENDOR")) {
574 t << "DEF_FILE_VENDOR = " << var("DEF_FILE_VENDOR") << endl;
575 haveSomething = true;
576 }
577 if (!project->isEmpty("DEF_FILE_TEMPLATE")) {
578 t << "DEF_FILE_TEMPLATE = " << escapeFilePath(var("DEF_FILE_TEMPLATE")) << endl;
579 haveSomething = true;
580 }
581 if (!project->isEmpty("DEF_FILE_MAP")) {
582 t << "DEF_FILE_MAP = " << escapeFilePath(var("DEF_FILE_MAP")) << endl;
583 haveSomething = true;
584 }
585 if (mode == DLL) {
586 // the DLL needs a file in any case
587 t << "DEF_FILE = $(basename $(DESTDIR_TARGET)).def" << endl;
588 } else if (haveSomething) {
589 // the EXE needs it only if there's a description info
590 t << "DEF_FILE_EXETYPE = " << escapeFilePath(var("DEF_FILE_EXETYPE")) << endl;
591 t << "DEF_FILE = $(OBJECTS_DIR)\\$(TARGET).def" << endl;
592 }
593 } else {
594 if (!project->isEmpty("DEF_FILE_TEMPLATE")) {
595 fprintf(stderr, "Both DEF_FILE and DEF_FILE_TEMPLATE are specified.\n");
596 fprintf(stderr, "Please specify one of them, not both.");
597 exit(1);
598 }
599 t << "DEF_FILE = " << escapeFilePath(var("DEF_FILE")) << endl;
600 }
601 }
602
603 if (mode == DLL) {
604 /* we neeed $(TARGET_IMPLIB) to be in sync with getLibTarget() */
605 t << "TARGET_IMPLIB = $(basename $(DESTDIR_TARGET)).lib" << endl;
606 }
607}
608
609void GNUMakefileGenerator::writeRcAndDefPart(QTextStream &t)
610{
611 if (!project->isEmpty("RC_FILE") && !project->isEmpty("RES_FILE") &&
612 !project->isEmpty("QMAKE_RUN_RC2RES")) {
613 t << escapeFileVars("$(RES_FILE): $(RC_FILE)\n\t");
614 t << var("QMAKE_RUN_RC2RES") << endl;
615 }
616}
617
618void GNUMakefileGenerator::processRcFileVar()
619{
620 if (Option::qmake_mode == Option::QMAKE_GENERATE_NOTHING)
621 return;
622
623 if (!project->isEmpty("RC_FILE")) {
624 if (!project->isEmpty("RES_FILE")) {
625 fprintf(stderr, "Both rc and res file specified.\n");
626 fprintf(stderr, "Please specify one of them, not both.");
627 exit(1);
628 }
629 project->values("RES_FILE").prepend(escapeFilePath(QString("$(OBJECTS_DIR)") +
630 QDir::separator() +
631 QFileInfo(var("RC_FILE")).baseName() +
632 ".res"));
633 project->values("CLEAN_FILES") += "$(RES_FILE)";
634 }
635
636 if (!project->isEmpty("RES_FILE"))
637 project->values("POST_TARGETDEPS") += escapeFileVars("$(RES_FILE)");
638}
639
640void GNUMakefileGenerator::processPrlVariable(const QString &var, const QStringList &l)
641{
642 // we don't do any processing here; everything we need is done in
643 // GNUMakefileGenerator::findLibraries()
644 return;
645}
646
647void GNUMakefileGenerator::processPrlFiles()
648{
649 // we don't do any processing here; everything we need is done in
650 // GNUMakefileGenerator::findLibraries()
651 return;
652}
653
654QString GNUMakefileGenerator::getLibTarget()
655{
656 if (mode == DLL)
657 return QString("$(notdir $(TARGET_IMPLIB))");
658 return Win32MakefileGenerator::getLibTarget();
659}
660
661QStringList &GNUMakefileGenerator::findDependencies(const QString &file)
662{
663 QStringList &aList = MakefileGenerator::findDependencies(file);
664 // Note: The QMAKE_IMAGE_COLLECTION file have all images
665 // as dependency, so don't add precompiled header then
666 if (file == project->first("QMAKE_IMAGE_COLLECTION")
667 || preCompHeaderOut.isEmpty())
668 return aList;
669 for (QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) {
670 if (file.endsWith(*it)) {
671 QString cHeader = preCompHeaderOut + Option::dir_sep + "c";
672 if (!aList.contains(cHeader))
673 aList += cHeader;
674 break;
675 }
676 }
677 for (QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) {
678 if (file.endsWith(*it)) {
679 QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++";
680 if (!aList.contains(cppHeader))
681 aList += cppHeader;
682 break;
683 }
684 }
685 return aList;
686}
687
688void GNUMakefileGenerator::writeProjectVarToStream(QTextStream &t, const QString &var)
689{
690 QStringList &list = project->values(var);
691 for(QStringList::Iterator it = list.begin(); it != list.end(); ++it) {
692 t << (*it) << endl;
693 }
694}
695
696QString GNUMakefileGenerator::makeResponseFileName(const QString &base)
697{
698 QString fileName = base + "." + var("TARGET");
699 if (!var("BUILD_NAME").isEmpty()) {
700 fileName += "." + var("BUILD_NAME");
701 }
702 fileName += ".rsp";
703 QString filePath = project->first("OBJECTS_DIR");
704 if (filePath.isEmpty())
705 filePath = Option::output_dir;
706 filePath = Option::fixPathToTargetOS(filePath + QDir::separator() + fileName);
707 return filePath;
708}
709
710void GNUMakefileGenerator::createCompilerResponseFiles(QTextStream &t)
711{
712 static const char *vars[] = { "CFLAGS", "CXXFLAGS", "DEFINES", "INCPATH" };
713
714 /* QMAKE_XXX_RSP_VAR is used as a flag whether it is necessary to
715 * generate response files to overcome the 1024 chars CMD.EXE limitation.
716 * When this variable is defined, a response file with the relevant
717 * information will be generated and its full path will be stored in an
718 * environment variable with the given name which can then be referred to in
719 * other places of qmake.conf (e.g. rules) */
720
721 for (size_t i = 0; i < sizeof(vars)/sizeof(vars[0]); ++i) {
722 QString rspVar =
723 project->first(QString().sprintf("QMAKE_%s_RSP_VAR", vars[i]));
724 if (!rspVar.isEmpty()) {
725 QString fileName = makeResponseFileName(vars[i]);
726 t << rspVar.leftJustified(14) << "= " << fileName << endl;
727 QFile file(fileName);
728 if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
729 QTextStream rt(&file);
730 if (!qstrcmp(vars[i], "CFLAGS")) {
731 rt << varGlue("QMAKE_CFLAGS", QString::null, "\n", "\n");
732 } else if (!qstrcmp(vars[i], "CXXFLAGS")) {
733 rt << varGlue("QMAKE_CXXFLAGS", QString::null, "\n", "\n");
734 } else if (!qstrcmp(vars[i], "DEFINES")) {
735 rt << varGlue("PRL_EXPORT_DEFINES", "-D", "\n-D", "\n")
736 << varGlue("DEFINES", "-D", "\n-D", "\n");
737 } else if (!qstrcmp(vars[i], "INCPATH")) {
738 QString opt = var("QMAKE_CFLAGS_INCDIR");
739 rt << varGlue("INCLUDEPATH", opt, "\n" + opt, "\n");
740 } else {
741 Q_ASSERT(false);
742 }
743 rt.flush();
744 file.close();
745 }
746 project->values("QMAKE_DISTCLEAN").append("$(" + rspVar + ")");
747 }
748 }
749}
750
751void GNUMakefileGenerator::createLinkerResponseFiles(QTextStream &t)
752{
753 /* see createCompilerResponseFiles() */
754 QString var = project->first("QMAKE_OBJECTS_RSP_VAR");
755 if (!var.isEmpty()) {
756 QString fileName = makeResponseFileName("OBJECTS");
757 t << var.leftJustified(14) << "= " << fileName << endl;
758 QFile file(fileName);
759 if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
760 QTextStream rt(&file);
761 rt << varGlue("OBJECTS", QString::null, "\n", "\n");
762 rt.flush();
763 file.close();
764 }
765 project->values("QMAKE_DISTCLEAN").append("$(" + var + ")");
766 }
767}
768
769// static
770QString GNUMakefileGenerator::escapeFileVars(const QString &vars)
771{
772 /* In DOS environment, we escape spaces and other illegal characters in
773 * filenames with double quotes. However, this is not appropriate for GNU
774 * make rule definitions (targets/dependencies) where Unix escaping is
775 * expected. If we'd deal only with immediate strings, we could provide
776 * necessary escaping in place, but we often use make variables instead of
777 * direct file names so we must perform such escaping on the fly. This is
778 * what we do here using the q function that we define in writeMakefile().*/
779 QString ret = vars;
780 QRegExp rx = QRegExp("(?:\\$\\(call q,\\$\\((.+)\\)\\))|"
781 "(?:\\$\\(([^$)]+)\\))");
782 rx.setMinimal(true);
783 int pos = 0;
784 while ((pos = rx.indexIn(ret, pos)) != -1) {
785 QString var = rx.cap(1);
786 if (var.isEmpty())
787 var = rx.cap(2);
788 // there are some make variables that contain multiple paths which we
789 // cannot escape (finding double-quoted spaces is too complex for
790 // the q function), so just skip them hoping they contain no spaces...
791 if (var != "OBJECTS") {
792 var = QString("$(call q,$(%1))").arg(var);
793 ret.replace(pos, rx.matchedLength(), var);
794 pos += var.length();
795 } else {
796 pos += rx.matchedLength();
797 }
798 }
799 return ret;
800}
801
802QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.