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

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

qmake: gnumake/os2: More precise escaping of illegal characters.

Escaping is now even more smart and will not try to escape strings like
'$(basename $(MYVAR)).ext', instead properly replacing them with
'$(basename $(call q,$(MYVAR))).ext'. This in particular affects extra
targets (that can have something like above in the .target spec).

File size: 32.6 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
292bool GNUMakefileGenerator::writeMakefile(QTextStream &t)
293{
294 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 QString ofile = escapeFilePath(Option::output.fileName());
306 if(ofile.lastIndexOf(Option::dir_sep) != -1)
307 ofile = ofile.right(ofile.length() - ofile.lastIndexOf(Option::dir_sep) -1);
308 t << "MAKEFILE = " << ofile << endl << endl;
309
310 if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) {
311 t << "all clean:" << "\n\t"
312 << "@echo \"Some of the required modules ("
313 << var("QMAKE_FAILED_REQUIREMENTS") << ") are not available.\"" << "\n\t"
314 << "@echo \"Skipped.\"" << endl << endl;
315 writeMakeQmake(t);
316 return true;
317 }
318
319 if(project->first("TEMPLATE") == "app" ||
320 project->first("TEMPLATE") == "lib") {
321 if(Option::mkfile::do_stub_makefile) {
322 t << "QMAKE = " << (project->isEmpty("QMAKE_QMAKE") ? QString("qmake") : var("QMAKE_QMAKE")) << endl;
323 QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
324 for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it)
325 t << *it << " ";
326 t << "first all clean install distclean uninstall: qmake" << endl
327 << "qmake_all:" << endl;
328 writeMakeQmake(t);
329 if(project->isEmpty("QMAKE_NOFORCE"))
330 t << "FORCE:" << endl << endl;
331 return true;
332 }
333 writeGNUParts(t);
334 return MakefileGenerator::writeMakefile(t);
335 }
336 else if(project->first("TEMPLATE") == "subdirs") {
337 writeSubDirs(t);
338 return true;
339 }
340 return false;
341 }
342
343void GNUMakefileGenerator::writeGNUParts(QTextStream &t)
344{
345 writeStandardParts(t);
346
347 if (!preCompHeaderOut.isEmpty()) {
348 QString header = project->first("PRECOMPILED_HEADER");
349 QString cHeader = preCompHeaderOut + Option::dir_sep + "c";
350 t << escapeDependencyPath(cHeader) << ": " << escapeDependencyPath(header) << " "
351 << escapeDependencyPaths(findDependencies(header)).join(" \\\n\t\t")
352 << "\n\t" << mkdir_p_asstring(preCompHeaderOut)
353 << "\n\t" << "$(CC) -x c-header -c $(CFLAGS) $(INCPATH) -o " << cHeader << " " << header
354 << endl << endl;
355 QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++";
356 t << escapeDependencyPath(cppHeader) << ": " << escapeDependencyPath(header) << " "
357 << escapeDependencyPaths(findDependencies(header)).join(" \\\n\t\t")
358 << "\n\t" << mkdir_p_asstring(preCompHeaderOut)
359 << "\n\t" << "$(CXX) -x c++-header -c $(CXXFLAGS) $(INCPATH) -o " << cppHeader << " " << header
360 << endl << endl;
361 }
362}
363
364void GNUMakefileGenerator::init()
365{
366 if(init_flag)
367 return;
368 init_flag = true;
369
370 if(project->first("TEMPLATE") == "app") {
371 mode = App;
372 project->values("QMAKE_APP_FLAG").append("1");
373 } else if(project->first("TEMPLATE") == "lib") {
374 mode = project->isActiveConfig("staticlib") ? StaticLib : DLL;
375 project->values("QMAKE_LIB_FLAG").append("1");
376 } else if(project->first("TEMPLATE") == "subdirs") {
377 MakefileGenerator::init();
378 if(project->isEmpty("QMAKE_COPY_FILE"))
379 project->values("QMAKE_COPY_FILE").append("$(COPY)");
380 if(project->isEmpty("QMAKE_COPY_DIR"))
381 project->values("QMAKE_COPY_DIR").append("$(COPY)");
382 if(project->isEmpty("QMAKE_INSTALL_FILE"))
383 project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)");
384 if(project->isEmpty("QMAKE_INSTALL_PROGRAM"))
385 project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)");
386 if(project->isEmpty("QMAKE_INSTALL_DIR"))
387 project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)");
388 if(project->values("MAKEFILE").isEmpty())
389 project->values("MAKEFILE").append("Makefile");
390 if(project->values("QMAKE_QMAKE").isEmpty())
391 project->values("QMAKE_QMAKE").append("qmake");
392 return;
393 }
394
395 project->values("TARGET_PRL").append(project->first("TARGET"));
396
397 processVars();
398
399 // LIBS defined in Profile comes first for gcc
400 project->values("QMAKE_LIBS") += project->values("LIBS");
401 project->values("QMAKE_LIBS_PRIVATE") += project->values("LIBS_PRIVATE");
402
403 QStringList &configs = project->values("CONFIG");
404
405 if(project->isActiveConfig("qt_dll"))
406 if(configs.indexOf("qt") == -1)
407 configs.append("qt");
408
409 if(project->isActiveConfig("dll")) {
410 QString destDir = "";
411 if(!project->first("DESTDIR").isEmpty())
412 destDir = Option::fixPathToTargetOS(project->first("DESTDIR") + Option::dir_sep, false, false);
413 }
414
415 MakefileGenerator::init();
416
417 // precomp
418 if (!project->first("PRECOMPILED_HEADER").isEmpty()
419 && project->isActiveConfig("precompile_header")) {
420 QString preCompHeader = var("PRECOMPILED_DIR")
421 + QFileInfo(project->first("PRECOMPILED_HEADER")).fileName();
422 preCompHeaderOut = preCompHeader + ".gch";
423 project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c");
424 project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c++");
425
426 project->values("QMAKE_RUN_CC").clear();
427 project->values("QMAKE_RUN_CC").append("$(CC) -c -include " + preCompHeader +
428 " $(CFLAGS) $(INCPATH) -o $obj $src");
429 project->values("QMAKE_RUN_CC_IMP").clear();
430 project->values("QMAKE_RUN_CC_IMP").append("$(CC) -c -include " + preCompHeader +
431 " $(CFLAGS) $(INCPATH) -o $@ $<");
432 project->values("QMAKE_RUN_CXX").clear();
433 project->values("QMAKE_RUN_CXX").append("$(CXX) -c -include " + preCompHeader +
434 " $(CXXFLAGS) $(INCPATH) -o $obj $src");
435 project->values("QMAKE_RUN_CXX_IMP").clear();
436 project->values("QMAKE_RUN_CXX_IMP").append("$(CXX) -c -include " + preCompHeader +
437 " $(CXXFLAGS) $(INCPATH) -o $@ $<");
438 }
439}
440
441void GNUMakefileGenerator::fixTargetExt()
442{
443 if (mode == App)
444 project->values("TARGET_EXT").append(".exe");
445 else if (mode == DLL)
446 project->values("TARGET_EXT").append(project->first("TARGET_VERSION_EXT") + ".dll");
447 else
448 project->values("TARGET_EXT").append(".lib");
449}
450
451void GNUMakefileGenerator::writeIncPart(QTextStream &t)
452{
453 t << "INCPATH =";
454
455 QString opt = var("QMAKE_CFLAGS_INCDIR");
456 QStringList &incs = project->values("INCLUDEPATH");
457 QString incsSemicolon;
458 for(QStringList::Iterator incit = incs.begin(); incit != incs.end(); ++incit) {
459 QString inc = escapeFilePath(*incit);
460 t << " " << opt << inc;
461 incsSemicolon += inc + Option::dirlist_sep;
462 }
463 t << " " << opt << "$(QMAKESPECDIR)" << endl;
464 incsSemicolon += "$(QMAKESPECDIR)";
465 t << "INCLUDEPATH = " << incsSemicolon << endl;
466 /* createCompilerResponseFiles() will need QAKESPECDIR expanded) */
467 project->values("INCLUDEPATH").append(specdir());
468}
469
470void GNUMakefileGenerator::writeLibsPart(QTextStream &t)
471{
472 if (mode == StaticLib) {
473 t << "LIB = " << var("QMAKE_LIB") << endl;
474 } else {
475 t << "LINK = " << var("QMAKE_LINK") << endl;
476 t << "LFLAGS = " << var("QMAKE_LFLAGS") << endl;
477 t << "LIBS =";
478 if(!project->values("QMAKE_LIBDIR").isEmpty())
479 writeLibDirPart(t);
480 QString opt = var("QMAKE_LFLAGS_LIB");
481 QString optL = var("QMAKE_LFLAGS_LIBDIR");
482 QStringList libVars;
483 libVars << "QMAKE_LIBS" << "QMAKE_LIBS_PRIVATE";
484 foreach(const QString &libVar, libVars) {
485 QStringList libs = project->values(libVar);
486 for(QStringList::Iterator it = libs.begin(); it != libs.end(); ++it) {
487 QString &lib = *it;
488
489 /* lib may be prefixed with -l which is commonly used in e.g. PRF
490 * (feature) files on all platforms; remove it before prepending
491 * the compiler-specific option. There is even more weird case
492 * when LIBS contains library paths prefixed with -L; we detect
493 * this as well and replace it with the proper option. */
494 if (lib.startsWith("-L")) {
495 lib = lib.mid(2);
496 t << " " << optL << escapeFilePath(lib);
497 } else {
498 if (lib.startsWith("-l"))
499 lib = lib.mid(2);
500 t << " " << opt << escapeFilePath(lib);
501 }
502 }
503 }
504 t << endl;
505 }
506}
507
508void GNUMakefileGenerator::writeLibDirPart(QTextStream &t)
509{
510 QString opt = var("QMAKE_LFLAGS_LIBDIR");
511 QStringList libDirs = project->values("QMAKE_LIBDIR");
512 for(QStringList::Iterator it = libDirs.begin(); it != libDirs.end(); ++it) {
513 QString libDir = escapeFilePath(*it);
514 /* libDir may be prefixed with -L which is commonly used in e.g. PRF
515 * (feature) files on all platforms; remove it before prepending
516 * the compiler-specific option */
517 if (libDir.startsWith("-L"))
518 libDir = libDir.mid(2);
519 t << " " << opt << libDir;
520 }
521}
522
523void GNUMakefileGenerator::writeObjectsPart(QTextStream &t)
524{
525 Win32MakefileGenerator::writeObjectsPart(t);
526 createLinkerResponseFiles(t);
527
528 /* this function is a nice place to also handle compiler options response
529 * files */
530 createCompilerResponseFiles(t);
531}
532
533void GNUMakefileGenerator::writeBuildRulesPart(QTextStream &t)
534{
535 t << "first: all" << endl;
536 t << "all: " << escapeDependencyPath(fileFixify(Option::output.fileName())) << " "
537 << valGlue(escapeDependencyPaths(project->values("ALL_DEPS"))," "," "," ")
538 << escapeFileVars(" $(DESTDIR_TARGET)") << endl << endl;
539 t << escapeFileVars("$(DESTDIR_TARGET): ") << escapeFileVars(var("PRE_TARGETDEPS"))
540 << escapeFileVars(" $(OBJECTS) ")
541 << escapeFileVars(var("POST_TARGETDEPS"));
542 if (!project->isEmpty("QMAKE_PRE_LINK"))
543 t << "\n\t" <<var("QMAKE_PRE_LINK");
544 if (mode == StaticLib) {
545 /* static library */
546 t << "\n\t" << var("QMAKE_RUN_LIB");
547 } else {
548 /* application or DLL */
549 t << "\n\t" << var("QMAKE_RUN_LINK");
550 if (!project->isEmpty("RES_FILE") && !project->isEmpty("QMAKE_RUN_RC2EXE")) {
551 t << "\n\t" << var("QMAKE_RUN_RC2EXE");
552 }
553 }
554 if(!project->isEmpty("QMAKE_POST_LINK"))
555 t << "\n\t" <<var("QMAKE_POST_LINK");
556 t << endl;
557}
558
559void GNUMakefileGenerator::writeRcAndDefVariables(QTextStream &t)
560{
561 if (!project->isEmpty("RC_FILE")) {
562 t << "RC_FILE = " << escapeFilePath(fileFixify(var("RC_FILE"))) << endl;
563 }
564 if (!project->isEmpty("RES_FILE")) {
565 t << "RES_FILE = " << valList(escapeFilePaths(fileFixify(project->values("RES_FILE")))) << endl;
566 }
567
568 if (mode != StaticLib) {
569 if (project->isEmpty("DEF_FILE")) {
570 /* no DEF file supplied, emit handy variable definitions to simplify
571 * DEF generation rules defined somewhere in default_post.prf */
572 bool haveSomething = false;
573 if (!project->isEmpty("DEF_FILE_VERSION")) {
574 t << "DEF_FILE_VERSION = " << var("DEF_FILE_VERSION") << endl;
575 haveSomething = true;
576 }
577 if (!project->isEmpty("DEF_FILE_DESCRIPTION")) {
578 t << "DEF_FILE_DESCRIPTION = " << var("DEF_FILE_DESCRIPTION") << endl;
579 haveSomething = true;
580 }
581 if (!project->isEmpty("DEF_FILE_VENDOR")) {
582 t << "DEF_FILE_VENDOR = " << var("DEF_FILE_VENDOR") << endl;
583 haveSomething = true;
584 }
585 if (!project->isEmpty("DEF_FILE_TEMPLATE")) {
586 t << "DEF_FILE_TEMPLATE = " << escapeFilePath(fileFixify(var("DEF_FILE_TEMPLATE"))) << endl;
587 haveSomething = true;
588 }
589 if (!project->isEmpty("DEF_FILE_MAP")) {
590 t << "DEF_FILE_MAP = " << escapeFilePath(fileFixify(var("DEF_FILE_MAP"))) << endl;
591 haveSomething = true;
592 }
593 if (mode == DLL) {
594 // the DLL needs a file in any case
595 t << "DEF_FILE = $(basename $(DESTDIR_TARGET)).def" << endl;
596 } else if (haveSomething) {
597 // the EXE needs it only if there's a description info
598 t << "DEF_FILE_EXETYPE = " << var("DEF_FILE_EXETYPE") << endl;
599 t << "DEF_FILE = $(OBJECTS_DIR)\\$(TARGET).def" << endl;
600 }
601 } else {
602 if (!project->isEmpty("DEF_FILE_TEMPLATE")) {
603 fprintf(stderr, "Both DEF_FILE and DEF_FILE_TEMPLATE are specified.\n");
604 fprintf(stderr, "Please specify one of them, not both.");
605 exit(1);
606 }
607 t << "DEF_FILE = " << escapeFilePath(fileFixify(var("DEF_FILE"))) << endl;
608 }
609 }
610
611 if (mode == DLL) {
612 // Note: $(TARGET) may be shortened here due to TARGET_SHORT, use
613 // getLibTarget() instead (shortening does not affect implib names)
614 t << "TARGET_IMPLIB = $(DESTDIR)\\" << getLibTarget() << endl;
615 }
616}
617
618void GNUMakefileGenerator::writeRcAndDefPart(QTextStream &t)
619{
620 if (!project->isEmpty("RC_FILE") && !project->isEmpty("RES_FILE") &&
621 !project->isEmpty("QMAKE_RUN_RC2RES")) {
622 if (!project->isEmpty("QMAKE_RUN_RC2RES_ENV"))
623 t << escapeFileVars("$(RES_FILE): ")
624 << var("QMAKE_RUN_RC2RES_ENV") << endl;
625 t << escapeFileVars("$(RES_FILE): $(RC_FILE)\n\t");
626 t << var("QMAKE_RUN_RC2RES") << endl << endl;
627 }
628}
629
630void GNUMakefileGenerator::processRcFileVar()
631{
632 if (Option::qmake_mode == Option::QMAKE_GENERATE_NOTHING)
633 return;
634
635 if (!project->isEmpty("RC_FILE")) {
636 if (!project->isEmpty("RES_FILE")) {
637 fprintf(stderr, "Both rc and res file specified.\n");
638 fprintf(stderr, "Please specify one of them, not both.");
639 exit(1);
640 }
641 project->values("RES_FILE").prepend(escapeFilePath(QString("$(OBJECTS_DIR)") +
642 QDir::separator() +
643 QFileInfo(var("RC_FILE")).baseName() +
644 ".res"));
645 project->values("CLEAN_FILES") += "$(RES_FILE)";
646 }
647
648 if (!project->isEmpty("RES_FILE"))
649 project->values("POST_TARGETDEPS") += escapeFileVars("$(RES_FILE)");
650}
651
652void GNUMakefileGenerator::processPrlVariable(const QString &var, const QStringList &l)
653{
654 // we don't do any processing here; everything we need is done in
655 // GNUMakefileGenerator::findLibraries()
656 return;
657}
658
659void GNUMakefileGenerator::processPrlFiles()
660{
661 // we don't do any processing here; everything we need is done in
662 // GNUMakefileGenerator::findLibraries()
663 return;
664}
665
666QString GNUMakefileGenerator::getPdbTarget()
667{
668 // we don't have .pdb files (.map and .sym are processed elsewere)
669 return QString::null;
670}
671
672QStringList &GNUMakefileGenerator::findDependencies(const QString &file)
673{
674 QStringList &aList = MakefileGenerator::findDependencies(file);
675 // Note: The QMAKE_IMAGE_COLLECTION file have all images
676 // as dependency, so don't add precompiled header then
677 if (file == project->first("QMAKE_IMAGE_COLLECTION")
678 || preCompHeaderOut.isEmpty())
679 return aList;
680 for (QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) {
681 if (file.endsWith(*it)) {
682 QString cHeader = preCompHeaderOut + Option::dir_sep + "c";
683 if (!aList.contains(cHeader))
684 aList += cHeader;
685 break;
686 }
687 }
688 for (QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) {
689 if (file.endsWith(*it)) {
690 QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++";
691 if (!aList.contains(cppHeader))
692 aList += cppHeader;
693 break;
694 }
695 }
696 return aList;
697}
698
699void GNUMakefileGenerator::writeProjectVarToStream(QTextStream &t, const QString &var)
700{
701 QStringList &list = project->values(var);
702 for(QStringList::Iterator it = list.begin(); it != list.end(); ++it) {
703 t << (*it) << endl;
704 }
705}
706
707QString GNUMakefileGenerator::makeResponseFileName(const QString &base)
708{
709 QString fileName = base + "." + var("TARGET");
710 if (!var("BUILD_NAME").isEmpty()) {
711 fileName += "." + var("BUILD_NAME");
712 }
713 fileName += ".rsp";
714 QString filePath = project->first("OBJECTS_DIR");
715 if (filePath.isEmpty())
716 filePath = fileFixify(Option::output_dir);
717 filePath = Option::fixPathToTargetOS(filePath + QDir::separator() + fileName);
718 return filePath;
719}
720
721static QStringList fixDefines(const QStringList &vals)
722{
723 // Existing GCC 4.x.x builds for OS/2 can't handle escaping meta symbols
724 // (e.g. quotes) with backslashes in response files (probably an OS/2-
725 // specific bug). The fix this replacing all "\<char>" occurences with
726 // "'<char>'"-like singlequoting which works.
727 //
728 // This backslash escaping is so frequently used in .pro files to pass
729 // string defines to C/C++ (in the form of "DEFINES += VAR=\\\"STRING\\\")
730 // that it makes sense to fix it here rather than introduce an OS/2-specific
731 // alteration of the DEFINES statement in each .pro file.
732
733 QStringList result;
734 foreach(const QString &val, vals) {
735 result << QString(val).replace(QRegExp("\\\\(.)"), "'\\1'");
736 }
737 return result;
738}
739
740void GNUMakefileGenerator::createCompilerResponseFiles(QTextStream &t)
741{
742 static const char *vars[] = { "CFLAGS", "CXXFLAGS", "DEFINES", "INCPATH" };
743
744 /* QMAKE_XXX_RSP_VAR is used as a flag whether it is necessary to
745 * generate response files to overcome the 1024 chars CMD.EXE limitation.
746 * When this variable is defined, a response file with the relevant
747 * information will be generated and its full path will be stored in an
748 * environment variable with the given name which can then be referred to in
749 * other places of qmake.conf (e.g. rules) */
750
751 for (size_t i = 0; i < sizeof(vars)/sizeof(vars[0]); ++i) {
752 QString rspVar =
753 project->first(QString().sprintf("QMAKE_%s_RSP_VAR", vars[i]));
754 if (!rspVar.isEmpty()) {
755 QString fileName = makeResponseFileName(vars[i]);
756 t << rspVar.leftJustified(14) << "= " << fileName << endl;
757 QFile file(QDir(Option::output_dir).absoluteFilePath(fileName));
758 if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
759 QTextStream rt(&file);
760 if (!qstrcmp(vars[i], "CFLAGS")) {
761 rt << varGlue("QMAKE_CFLAGS", QString::null, "\n", "\n");
762 } else if (!qstrcmp(vars[i], "CXXFLAGS")) {
763 rt << varGlue("QMAKE_CXXFLAGS", QString::null, "\n", "\n");
764 } else if (!qstrcmp(vars[i], "DEFINES")) {
765 rt << valGlue(fixDefines(project->values("PRL_EXPORT_DEFINES")),
766 "-D", "\n-D", "\n")
767 << valGlue(fixDefines(project->values("DEFINES")),
768 "-D", "\n-D", "\n");
769 } else if (!qstrcmp(vars[i], "INCPATH")) {
770 QString opt = var("QMAKE_CFLAGS_INCDIR");
771 rt << varGlue("INCLUDEPATH", opt, "\n" + opt, "\n");
772 } else {
773 Q_ASSERT(false);
774 }
775 rt.flush();
776 file.close();
777 }
778 project->values("QMAKE_DISTCLEAN").append("$(" + rspVar + ")");
779 }
780 }
781}
782
783void GNUMakefileGenerator::createLinkerResponseFiles(QTextStream &t)
784{
785 /* see createCompilerResponseFiles() */
786 QString var = project->first("QMAKE_OBJECTS_RSP_VAR");
787 if (!var.isEmpty()) {
788 QString fileName = makeResponseFileName("OBJECTS");
789 t << var.leftJustified(14) << "= " << fileName << endl;
790 QFile file(QDir(Option::output_dir).absoluteFilePath(fileName));
791 if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
792 QTextStream rt(&file);
793 rt << varGlue("OBJECTS", QString::null, "\n", "\n");
794 rt.flush();
795 file.close();
796 }
797 project->values("QMAKE_DISTCLEAN").append("$(" + var + ")");
798 }
799}
800
801// static
802QString GNUMakefileGenerator::escapeFileVars(const QString &vars)
803{
804 /* In DOS environment, we escape spaces and other illegal characters in
805 * filenames with double quotes. However, this is not appropriate for GNU
806 * make rule definitions (targets/dependencies) where Unix escaping is
807 * expected. If we'd deal only with immediate strings, we could provide
808 * necessary escaping in place, but we often use make variables instead of
809 * direct file names so we must perform such escaping on the fly. This is
810 * what we do here using the q function that we define in writeMakefile().*/
811 QString ret = vars;
812 QRegExp rx = QRegExp("(?:\\$\\(call q,\\$\\((.+)\\)\\))|"
813 "(?:\\$\\(([^$)]+)\\))");
814 rx.setMinimal(true);
815 int pos = 0;
816 while ((pos = rx.indexIn(ret, pos)) != -1) {
817 QString var = rx.cap(1);
818 if (var.isEmpty())
819 var = rx.cap(2);
820 // there are some make variables that contain multiple paths which we
821 // cannot escape (finding double-quoted spaces is too complex for
822 // the q function), so just skip them hoping they contain no spaces...
823 if (var != "OBJECTS") {
824 var = QString("$(call q,$(%1))").arg(var);
825 ret.replace(pos, rx.matchedLength(), var);
826 pos += var.length();
827 } else {
828 pos += rx.matchedLength();
829 }
830 }
831 return ret;
832}
833
834QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.