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

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

qmake/GNUMAKE/os/2: Add the MAKEFILE variable to the generated make file that contains its name. Always convert separators to native when escaping paths for the DOS-like command processors.

File size: 27.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the qmake application of the Qt Toolkit.
7**
8** Copyright (C) 2009 netlabs.org. OS/2 parts.
9**
10** $QT_BEGIN_LICENSE:LGPL$
11** Commercial Usage
12** Licensees holding valid Qt Commercial licenses may use this file in
13** accordance with the Qt Commercial License Agreement provided with the
14** Software or, alternatively, in accordance with the terms contained in
15** a written agreement between you and Nokia.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Nokia gives you certain
26** additional rights. These rights are described in the Nokia Qt LGPL
27** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
28** 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 are unsure which license is appropriate for your use, please
39** contact the sales department 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 QString ret = path;
94 QString trimmed = path.trimmed();
95 if (!trimmed.startsWith("$(") || !trimmed.endsWith(")")) {
96 ret.remove('\"');
97 ret.replace(' ', "\\ ");
98 }
99 return ret;
100}
101
102QString
103GNUMakefileGenerator::findBestVersion(const QString &d, const QString &stem, const QString &ext)
104{
105 QString bd = Option::fixPathToLocalOS(d, true);
106 if(!exists(bd))
107 return QString();
108
109 QString dllStem = stem + QTDLL_POSTFIX;
110
111 QString versionOverride;
112 if(!project->values("QMAKE_" + stem.toUpper() + "_VERSION_OVERRIDE").isEmpty())
113 versionOverride = project->values("QMAKE_" + stem.toUpper() + "_VERSION_OVERRIDE").first();
114
115 if (project->isActiveConfig("link_prl")) {
116 QMakeMetaInfo libinfo;
117 QString libfile = QMakeMetaInfo::findLib(bd + Option::dir_sep + dllStem);
118 if (project->values("QMAKE_PRL_INTERNAL_FILES").contains(libfile)) {
119 // already processed, return emtpy string to discard this lib
120 return QString("");
121 }
122 if (libinfo.readLib(libfile)) {
123 project->values("QMAKE_PRL_INTERNAL_FILES") += libfile;
124 QString target = libinfo.first("QMAKE_PRL_TARGET");
125 if (target == project->first("TARGET")) {
126 // circular reference to ourselves, return emtpy string to discard this lib
127 return QString("");
128 }
129 if (target.isEmpty())
130 target = dllStem;
131 if (!versionOverride.isEmpty())
132 target += versionOverride;
133 else if (!libinfo.values("QMAKE_PRL_CONFIG").contains("staticlib") &&
134 !libinfo.isEmpty("QMAKE_PRL_VERSION"))
135 target += libinfo.first("QMAKE_PRL_VERSION").replace(".", "");
136
137 const QStringList &in = libinfo.values("QMAKE_PRL_LIBS");
138 QStringList &out = project->values("QMAKE_PRL_LIBS");
139 for (QStringList::ConstIterator it = in.begin(); it != in.end(); ++it) {
140 if (!out.contains(*it))
141 out.append((*it));
142 }
143 return target;
144 }
145 }
146
147 if (!versionOverride.isEmpty())
148 return stem + versionOverride;
149
150 int biggest = -1;
151 if(!project->isActiveConfig("no_versionlink")) {
152 QDir dir(bd);
153 QStringList entries = dir.entryList();
154 QRegExp regx(QString("((lib)?%1([0-9]*))(%2)$").arg(dllStem).arg(ext), Qt::CaseInsensitive);
155 for(QStringList::Iterator it = entries.begin(); it != entries.end(); ++it) {
156 if(regx.exactMatch((*it))) {
157 if (!regx.cap(3).isEmpty()) {
158 bool ok = true;
159 int num = regx.cap(3).toInt(&ok);
160 biggest = qMax(biggest, (!ok ? -1 : num));
161 }
162 }
163 }
164 }
165 if (biggest != -1)
166 return stem + QString::number(biggest);
167
168 return QString();
169}
170
171bool GNUMakefileGenerator::findLibraries()
172{
173 QStringList &l = project->values("QMAKE_LIBS");
174
175 QList<QMakeLocalFileName> dirs;
176 {
177 QStringList &libpaths = project->values("QMAKE_LIBDIR");
178 for(QStringList::Iterator libpathit = libpaths.begin();
179 libpathit != libpaths.end(); ++libpathit)
180 dirs.append(QMakeLocalFileName((*libpathit)));
181 }
182
183 QStringList::Iterator it = l.begin();
184
185 do {
186 project->values("QMAKE_PRL_LIBS").clear();
187 bool erase = false;
188 for (; it != l.end(); erase ? (erase = false, it = l.erase(it)) : ++it) {
189 if((*it).startsWith("-L")) {
190 dirs.append(QMakeLocalFileName((*it).mid(2)));
191 } else {
192 QString stem = *it, out;
193
194 if (stem.startsWith("-l"))
195 stem = stem.mid(2);
196
197 QString suffix;
198 if (!project->isEmpty("QMAKE_" + stem.toUpper() + "_SUFFIX"))
199 suffix = project->first("QMAKE_" + stem.toUpper() + "_SUFFIX");
200 for (QList<QMakeLocalFileName>::Iterator dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) {
201 QString best = findBestVersion((*dir_it).local(), stem,
202 QString("%1.lib").arg(suffix));
203 if (!best.isNull()) {
204 if (best.isEmpty()) {
205 // we are asked to discard this lib, do it
206 erase = true;
207 break;
208 }
209 out = best.prepend("-l");
210 break;
211 }
212 }
213 if (!out.isEmpty()) // We assume if it never finds it then its correct
214 if (!project->values("QMAKE_LIBS").contains(out))
215 (*it) = out;
216 }
217 }
218
219 // add the libraries from PRL and process them normally
220 QStringList l2 = project->values("QMAKE_PRL_LIBS");
221 int oldSize = l.size();
222 for (QStringList::ConstIterator it2 = l2.begin(); it2 != l2.end(); ++it2) {
223 if (!l.contains(*it2))
224 l.append(*it2);
225 }
226 it = l.begin() + oldSize;
227
228 } while(it != l.end());
229
230 return true;
231}
232
233bool GNUMakefileGenerator::writeMakefile(QTextStream &t)
234{
235 writeHeader(t);
236
237 /* function to convert from DOS-like to Unix-like space escaping in file
238 * names */
239 t << "null :=" << endl << "space := $(null) # end of the line" << endl;
240 t << "# function to change DOS-like space escaping to Unix-like" << endl;
241 t << "q = $(subst %,\\%,$(subst ;,\\;,$(subst &,\\&,"
242 "$(subst +,\\+,$(subst $(space),\\ ,$(subst \",,$(1)))))))" << endl << endl;
243
244 if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) {
245 t << "all clean:" << "\n\t"
246 << "@echo \"Some of the required modules ("
247 << var("QMAKE_FAILED_REQUIREMENTS") << ") are not available.\"" << "\n\t"
248 << "@echo \"Skipped.\"" << endl << endl;
249 writeMakeQmake(t);
250 return true;
251 }
252
253 if(project->first("TEMPLATE") == "app" ||
254 project->first("TEMPLATE") == "lib") {
255 if(Option::mkfile::do_stub_makefile) {
256 t << "QMAKE = " << (project->isEmpty("QMAKE_QMAKE") ? QString("qmake") : var("QMAKE_QMAKE")) << endl;
257 QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
258 for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it)
259 t << *it << " ";
260 t << "first all clean install distclean uninstall: qmake" << endl
261 << "qmake_all:" << endl;
262 writeMakeQmake(t);
263 if(project->isEmpty("QMAKE_NOFORCE"))
264 t << "FORCE:" << endl << endl;
265 return true;
266 }
267 writeGNUParts(t);
268 return MakefileGenerator::writeMakefile(t);
269 }
270 else if(project->first("TEMPLATE") == "subdirs") {
271 writeSubDirs(t);
272 return true;
273 }
274 return false;
275 }
276
277void GNUMakefileGenerator::writeGNUParts(QTextStream &t)
278{
279 t << "QMAKESPECDIR = " << escapeFilePath(specdir()) << endl;
280
281 QString ofile = escapeFilePath(Option::output.fileName());
282 if(ofile.lastIndexOf(Option::dir_sep) != -1)
283 ofile = ofile.right(ofile.length() - ofile.lastIndexOf(Option::dir_sep) -1);
284 t << "MAKEFILE = " << ofile << endl << endl;
285
286 writeStandardParts(t);
287
288 if (!preCompHeaderOut.isEmpty()) {
289 QString header = project->first("PRECOMPILED_HEADER");
290 QString cHeader = preCompHeaderOut + Option::dir_sep + "c";
291 t << escapeDependencyPath(cHeader) << ": " << escapeDependencyPath(header) << " "
292 << escapeDependencyPaths(findDependencies(header)).join(" \\\n\t\t")
293 << "\n\t" << mkdir_p_asstring(preCompHeaderOut)
294 << "\n\t" << "$(CC) -x c-header -c $(CFLAGS) $(INCPATH) -o " << cHeader << " " << header
295 << endl << endl;
296 QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++";
297 t << escapeDependencyPath(cppHeader) << ": " << escapeDependencyPath(header) << " "
298 << escapeDependencyPaths(findDependencies(header)).join(" \\\n\t\t")
299 << "\n\t" << mkdir_p_asstring(preCompHeaderOut)
300 << "\n\t" << "$(CXX) -x c++-header -c $(CXXFLAGS) $(INCPATH) -o " << cppHeader << " " << header
301 << endl << endl;
302 }
303}
304
305void GNUMakefileGenerator::init()
306{
307 if(init_flag)
308 return;
309 init_flag = true;
310
311 if(project->first("TEMPLATE") == "app") {
312 mode = App;
313 project->values("QMAKE_APP_FLAG").append("1");
314 } else if(project->first("TEMPLATE") == "lib") {
315 mode = project->isActiveConfig("staticlib") ? StaticLib : DLL;
316 project->values("QMAKE_LIB_FLAG").append("1");
317 } else if(project->first("TEMPLATE") == "subdirs") {
318 MakefileGenerator::init();
319 if(project->isEmpty("QMAKE_COPY_FILE"))
320 project->values("QMAKE_COPY_FILE").append("$(COPY)");
321 if(project->isEmpty("QMAKE_COPY_DIR"))
322 project->values("QMAKE_COPY_DIR").append("$(COPY)");
323 if(project->isEmpty("QMAKE_INSTALL_FILE"))
324 project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)");
325 if(project->isEmpty("QMAKE_INSTALL_PROGRAM"))
326 project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)");
327 if(project->isEmpty("QMAKE_INSTALL_DIR"))
328 project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)");
329 if(project->values("MAKEFILE").isEmpty())
330 project->values("MAKEFILE").append("Makefile");
331 if(project->values("QMAKE_QMAKE").isEmpty())
332 project->values("QMAKE_QMAKE").append("qmake");
333 return;
334 }
335
336 project->values("TARGET_PRL").append(project->first("TARGET"));
337
338 processVars();
339
340 // LIBS defined in Profile comes first for gcc
341 project->values("QMAKE_LIBS") += escapeFilePaths(project->values("LIBS"));
342
343 QStringList &configs = project->values("CONFIG");
344
345 if(project->isActiveConfig("qt_dll"))
346 if(configs.indexOf("qt") == -1)
347 configs.append("qt");
348
349 if(project->isActiveConfig("dll")) {
350 QString destDir = "";
351 if(!project->first("DESTDIR").isEmpty())
352 destDir = Option::fixPathToTargetOS(project->first("DESTDIR") + Option::dir_sep, false, false);
353 }
354
355 MakefileGenerator::init();
356
357 // precomp
358 if (!project->first("PRECOMPILED_HEADER").isEmpty()
359 && project->isActiveConfig("precompile_header")) {
360 QString preCompHeader = var("PRECOMPILED_DIR")
361 + QFileInfo(project->first("PRECOMPILED_HEADER")).fileName();
362 preCompHeaderOut = preCompHeader + ".gch";
363 project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c");
364 project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c++");
365
366 project->values("QMAKE_RUN_CC").clear();
367 project->values("QMAKE_RUN_CC").append("$(CC) -c -include " + preCompHeader +
368 " $(CFLAGS) $(INCPATH) -o $obj $src");
369 project->values("QMAKE_RUN_CC_IMP").clear();
370 project->values("QMAKE_RUN_CC_IMP").append("$(CC) -c -include " + preCompHeader +
371 " $(CFLAGS) $(INCPATH) -o $@ $<");
372 project->values("QMAKE_RUN_CXX").clear();
373 project->values("QMAKE_RUN_CXX").append("$(CXX) -c -include " + preCompHeader +
374 " $(CXXFLAGS) $(INCPATH) -o $obj $src");
375 project->values("QMAKE_RUN_CXX_IMP").clear();
376 project->values("QMAKE_RUN_CXX_IMP").append("$(CXX) -c -include " + preCompHeader +
377 " $(CXXFLAGS) $(INCPATH) -o $@ $<");
378 }
379}
380
381void GNUMakefileGenerator::fixTargetExt()
382{
383 if (mode == App)
384 project->values("TARGET_EXT").append(".exe");
385 else if (mode == DLL)
386 project->values("TARGET_EXT").append(project->first("TARGET_VERSION_EXT") + ".dll");
387 else
388 project->values("TARGET_EXT").append(".lib");
389}
390
391void GNUMakefileGenerator::writeIncPart(QTextStream &t)
392{
393 t << "INCPATH =";
394
395 QString opt = var("QMAKE_CFLAGS_INCDIR");
396 QStringList &incs = project->values("INCLUDEPATH");
397 QString incsSemicolon;
398 for(QStringList::Iterator incit = incs.begin(); incit != incs.end(); ++incit) {
399 QString inc = escapeFilePath(*incit);
400 t << " " << opt << inc;
401 incsSemicolon += inc + Option::dirlist_sep;
402 }
403 t << " " << opt << "$(QMAKESPECDIR)" << endl;
404 incsSemicolon += "$(QMAKESPECDIR)";
405 t << "INCLUDEPATH = " << incsSemicolon << endl;
406 /* createCompilerResponseFiles() will need QAKESPECDIR expanded) */
407 project->values("INCLUDEPATH").append(specdir());
408}
409
410void GNUMakefileGenerator::writeLibsPart(QTextStream &t)
411{
412 if (mode == StaticLib) {
413 t << "LIB = " << var("QMAKE_LIB") << endl;
414 } else {
415 t << "LINK = " << var("QMAKE_LINK") << endl;
416 t << "LFLAGS = " << var("QMAKE_LFLAGS") << endl;
417 t << "LIBS =";
418 if(!project->values("QMAKE_LIBDIR").isEmpty())
419 writeLibDirPart(t);
420 QString opt = var("QMAKE_LFLAGS_LIB");
421 QString optL = var("QMAKE_LFLAGS_LIBDIR");
422 QStringList libs = project->values("QMAKE_LIBS");