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

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

qmake: Make MakefileGenerator::writeHeader() virtual.

This lets generators have additional definitions common to
all generated makefiles in a single place. This is used by
e.g. the OS/2 GNUMAKE generator.

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