source: trunk/qmake/generators/win32/winmakefile.cpp

Last change on this file was 81, checked in by dmik, 19 years ago

Fixed: when generating a makefile for a subdirs template, this makefile was internally referred to as $(MAKEFILE) (Makefile by default) instead of the output name specified on the command line.

  • Property svn:keywords set to Id
File size: 15.7 KB
Line 
1/****************************************************************************
2** $Id: winmakefile.cpp 81 2006-04-15 14:57:34Z dmik $
3**
4** Implementation of Win32MakefileGenerator class.
5**
6** Copyright (C) 1992-2003 Trolltech AS. All rights reserved.
7**
8** This file is part of qmake.
9**
10** This file may be distributed under the terms of the Q Public License
11** as defined by Trolltech AS of Norway and appearing in the file
12** LICENSE.QPL included in the packaging of this file.
13**
14** This file may be distributed and/or modified under the terms of the
15** GNU General Public License version 2 as published by the Free Software
16** Foundation and appearing in the file LICENSE.GPL included in the
17** packaging of this file.
18**
19** Licensees holding valid Qt Enterprise Edition licenses may use this
20** file in accordance with the Qt Commercial License Agreement provided
21** with the Software.
22**
23** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
24** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
25**
26** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
27** information about Qt Commercial License Agreements.
28** See http://www.trolltech.com/qpl/ for QPL licensing information.
29** See http://www.trolltech.com/gpl/ for GPL licensing information.
30**
31** Contact info@trolltech.com if any conditions of this licensing are
32** not clear to you.
33**
34**********************************************************************/
35
36#include "winmakefile.h"
37#include "option.h"
38#include "project.h"
39#include "meta.h"
40#include <qtextstream.h>
41#include <qstring.h>
42#include <qdict.h>
43#include <qregexp.h>
44#include <qstringlist.h>
45#include <qdir.h>
46
47
48Win32MakefileGenerator::Win32MakefileGenerator(QMakeProject *p) : MakefileGenerator(p)
49{
50
51}
52
53
54struct SubDir
55{
56 QString directory, profile, target, makefile;
57};
58
59void
60Win32MakefileGenerator::writeSubDirs(QTextStream &t)
61{
62 QPtrList<SubDir> subdirs;
63 {
64 QStringList subdirs_in = project->variables()["SUBDIRS"];
65 for(QStringList::Iterator it = subdirs_in.begin(); it != subdirs_in.end(); ++it) {
66 QString file = (*it);
67 file = fileFixify(file);
68 SubDir *sd = new SubDir;
69 subdirs.append(sd);
70 sd->makefile = "$(MAKEFILE)";
71 if((*it).right(4) == ".pro") {
72 int slsh = file.findRev(Option::dir_sep);
73 if(slsh != -1) {
74 sd->directory = file.left(slsh+1);
75 sd->profile = file.mid(slsh+1);
76 } else {
77 sd->profile = file;
78 }
79 } else {
80 sd->directory = file;
81 }
82 while(sd->directory.right(1) == Option::dir_sep)
83 sd->directory = sd->directory.left(sd->directory.length() - 1);
84 if(!sd->profile.isEmpty()) {
85 QString basename = sd->directory;
86 int new_slsh = basename.findRev(Option::dir_sep);
87 if(new_slsh != -1)
88 basename = basename.mid(new_slsh+1);
89 if(sd->profile != basename + ".pro")
90 sd->makefile += "." + sd->profile.left(sd->profile.length() - 4); //no need for the .pro
91 }
92 sd->target = "sub-" + (*it);
93 sd->target.replace('/', '-');
94 sd->target.replace('.', '_');
95 }
96 }
97 QPtrListIterator<SubDir> it(subdirs);
98
99 t << "MAKEFILE = " << (project->isEmpty("MAKEFILE") ? QString("Makefile") : var("MAKEFILE")) << endl;
100 t << "QMAKE = " << (project->isEmpty("QMAKE_QMAKE") ? QString("qmake") : var("QMAKE_QMAKE")) << endl;
101 t << "SUBTARGETS = ";
102 for( it.toFirst(); it.current(); ++it)
103 t << " \\\n\t\t" << it.current()->target;
104 t << endl << endl;
105 t << "all: " << Option::fixPathToTargetOS( fileFixify( Option::output.name() ) )
106 << " $(SUBTARGETS)" << endl << endl;
107
108 for( it.toFirst(); it.current(); ++it) {
109 bool have_dir = !(*it)->directory.isEmpty();
110
111 //make the makefile
112 QString mkfile = (*it)->makefile;
113 if(have_dir)
114 mkfile.prepend((*it)->directory + Option::dir_sep);
115 t << mkfile << ":";
116 if(have_dir)
117 t << "\n\t" << "cd " << (*it)->directory;
118 t << "\n\t" << "$(QMAKE) " << (*it)->profile << " " << buildArgs();
119 t << " -o " << (*it)->makefile;
120 if(have_dir) {
121 int subLevels = it.current()->directory.contains(Option::dir_sep) + 1;
122 t << "\n\t" << "@cd ..";
123 for(int i = 1; i < subLevels; i++ )
124 t << Option::dir_sep << "..";
125 }
126 t << endl;
127
128 //now actually build
129 t << (*it)->target << ": " << mkfile;
130 if(project->variables()["QMAKE_NOFORCE"].isEmpty())
131 t << " FORCE";
132 if(have_dir)
133 t << "\n\t" << "cd " << (*it)->directory;
134 t << "\n\t" << "$(MAKE)";
135 t << " -f " << (*it)->makefile;
136 if(have_dir) {
137 int subLevels = it.current()->directory.contains(Option::dir_sep) + 1;
138 t << "\n\t" << "@cd ..";
139 for(int i = 1; i < subLevels; i++ )
140 t << Option::dir_sep << "..";
141 }
142 t << endl << endl;
143 }
144
145 if(project->variables()["QMAKE_INTERNAL_QMAKE_DEPS"].findIndex("qmake_all") == -1)
146 project->variables()["QMAKE_INTERNAL_QMAKE_DEPS"].append("qmake_all");
147 writeMakeQmake(t);
148
149 t << "qmake_all:";
150 if ( !subdirs.isEmpty() ) {
151 for( it.toFirst(); it.current(); ++it) {
152 QString subdir = (*it)->directory;
153 QString profile = (*it)->profile;
154 int subLevels = subdir.contains(Option::dir_sep) + 1;
155 t << "\n\t"
156 << "cd " << subdir << "\n\t";
157 int lastSlash = subdir.findRev(Option::dir_sep);
158 if(lastSlash != -1)
159 subdir = subdir.mid( lastSlash + 1 );
160 t << "$(QMAKE) "
161 << ( !profile.isEmpty() ? profile : subdir + ".pro" )
162 << " -o " << (*it)->makefile
163 << " " << buildArgs() << "\n\t"
164 << "@cd ..";
165 for(int i = 1; i < subLevels; i++ )
166 t << Option::dir_sep << "..";
167 }
168 } else {
169 // Borland make does not like empty an empty command section, so insert
170 // a dummy command.
171 t << "\n\t" << "@cd .";
172 }
173 t << endl << endl;
174
175 QStringList targs;
176 targs << "clean" << "install_subdirs" << "mocables" << "uicables" << "uiclean" << "mocclean";
177 targs += project->values("SUBDIR_TARGETS");
178 for(QStringList::Iterator targ_it = targs.begin(); targ_it != targs.end(); ++targ_it) {
179 t << (*targ_it) << ": qmake_all";
180 QString targ = (*targ_it);
181 if(targ == "install_subdirs")
182 targ = "install";
183 else if(targ == "uninstall_subdirs")
184 targ = "uninstall";
185 if(targ == "clean")
186 t << varGlue("QMAKE_CLEAN","\n\t-$(DEL_FILE) ","\n\t-$(DEL_FILE) ", "");
187 if (!subdirs.isEmpty()) {
188 for( it.toFirst(); it.current(); ++it) {
189 int subLevels = (*it)->directory.contains(Option::dir_sep) + 1;
190 bool have_dir = !(*it)->directory.isEmpty();
191 if(have_dir)
192 t << "\n\t" << "cd " << (*it)->directory;
193 QString in_file = " -f " + (*it)->makefile;
194 t << "\n\t" << "$(MAKE) " << in_file << " " << targ;
195 if(have_dir) {
196 t << "\n\t" << "@cd ..";
197 for(int i = 1; i < subLevels; i++ )
198 t << Option::dir_sep << "..";
199 }
200 }
201 } else {
202 // Borland make does not like empty an empty command section, so
203 // insert a dummy command.
204 t << "\n\t" << "@cd .";
205 }
206 t << endl << endl;
207 }
208
209 //installations
210 project->variables()["INSTALLDEPS"] += "install_subdirs";
211 project->variables()["UNINSTALLDEPS"] += "uninstall_subdirs";
212 writeInstalls(t, "INSTALLS");
213
214 // user defined targets
215 QStringList &qut = project->variables()["QMAKE_EXTRA_WIN_TARGETS"];
216 for(QStringList::Iterator sit = qut.begin(); sit != qut.end(); ++sit) {
217 QString targ = var((*sit) + ".target"),
218 cmd = var((*sit) + ".commands"), deps;
219 if(targ.isEmpty())
220 targ = (*sit);
221 QStringList &deplist = project->variables()[(*sit) + ".depends"];
222 for(QStringList::Iterator dep_it = deplist.begin(); dep_it != deplist.end(); ++dep_it) {
223 QString dep = var((*dep_it) + ".target");
224 if(dep.isEmpty())
225 dep = (*dep_it);
226 deps += " " + dep;
227 }
228 if(!project->variables()["QMAKE_NOFORCE"].isEmpty() &&
229 project->variables()[(*sit) + ".CONFIG"].findIndex("phony") != -1)
230 deps += QString(" ") + "FORCE";
231 t << "\n\n" << targ << ":" << deps << "\n\t"
232 << cmd;
233 }
234
235 if(project->variables()["QMAKE_NOFORCE"].isEmpty())
236 t << "FORCE:" << endl << endl;
237}
238
239
240int
241Win32MakefileGenerator::findHighestVersion(const QString &d, const QString &stem)
242{
243 QString bd = Option::fixPathToLocalOS(d, TRUE);
244 if(!QFile::exists(bd))
245 return -1;
246 if(!project->variables()["QMAKE_" + stem.upper() + "_VERSION_OVERRIDE"].isEmpty())
247 return project->variables()["QMAKE_" + stem.upper() + "_VERSION_OVERRIDE"].first().toInt();
248
249 QDir dir(bd);
250 int biggest=-1;
251 QStringList entries = dir.entryList();
252 QString dllStem = stem + QTDLL_POSTFIX;
253 QRegExp regx( "(" + dllStem + "([0-9]*)).lib", FALSE );
254 for(QStringList::Iterator it = entries.begin(); it != entries.end(); ++it) {
255 if(regx.exactMatch((*it)))
256 biggest = QMAX(biggest, (regx.cap(1) == dllStem ||
257 regx.cap(2).isEmpty()) ? -1 : regx.cap(2).toInt());
258 }
259 QMakeMetaInfo libinfo;
260 if(libinfo.readLib(bd + dllStem)) {
261 if(!libinfo.isEmpty("QMAKE_PRL_VERSION"))
262 biggest = QMAX(biggest, libinfo.first("QMAKE_PRL_VERSION").replace(".", "").toInt());
263 }
264 return biggest;
265}
266
267QString
268Win32MakefileGenerator::findDependency(const QString &dep)
269{
270 {
271 QStringList &qut = project->variables()["QMAKE_EXTRA_WIN_TARGETS"];
272 for(QStringList::Iterator it = qut.begin(); it != qut.end(); ++it) {
273 QString targ = var((*it) + ".target");
274 if(targ.isEmpty())
275 targ = (*it);
276 if(targ.endsWith(dep))
277 return targ;
278 }
279 }
280 {
281 QStringList &quc = project->variables()["QMAKE_EXTRA_WIN_COMPILERS"];
282 for(QStringList::Iterator it = quc.begin(); it != quc.end(); ++it) {
283 QString tmp_out = project->variables()[(*it) + ".output"].first();
284 QString tmp_cmd = project->variables()[(*it) + ".commands"].join(" ");
285 if(tmp_out.isEmpty() || tmp_cmd.isEmpty())
286 continue;
287 QStringList &tmp = project->variables()[(*it) + ".input"];
288 for(QStringList::Iterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
289 QStringList &inputs = project->variables()[(*it2)];
290 for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
291 QString out = tmp_out;
292 QFileInfo fi(Option::fixPathToLocalOS((*input)));
293 out.replace("${QMAKE_FILE_BASE}", fi.baseName());
294 out.replace("${QMAKE_FILE_NAME}", fi.fileName());
295 if(out.endsWith(dep))
296 return out;
297 }
298 }
299 }
300 }
301 return MakefileGenerator::findDependency(dep);
302}
303
304bool
305Win32MakefileGenerator::findLibraries(const QString &where)
306{
307
308 QStringList &l = project->variables()[where];
309 QPtrList<MakefileDependDir> dirs;
310 {
311 QStringList &libpaths = project->variables()["QMAKE_LIBDIR"];
312 for(QStringList::Iterator libpathit = libpaths.begin(); libpathit != libpaths.end(); ++libpathit) {
313 QString r = (*libpathit), l = r;
314 fixEnvVariables(l);
315 dirs.append(new MakefileDependDir(r.replace("\"",""), l.replace("\"","")));
316 }
317 }
318 dirs.setAutoDelete(TRUE);
319 for(QStringList::Iterator it = l.begin(); it != l.end(); ) {
320 QChar quote;
321 bool modified_opt = FALSE, remove = FALSE;
322 QString opt = (*it).stripWhiteSpace();
323 if((opt[0] == '\'' || opt[0] == '"') && opt[(int)opt.length()-1] == opt[0]) {
324 quote = opt[0];
325 opt = opt.mid(1, opt.length()-2);
326 }
327 if(opt.startsWith("/LIBPATH:")) {
328 QString r = opt.mid(9), l = Option::fixPathToLocalOS(r);
329 dirs.append(new MakefileDependDir(r.replace("\"",""),
330 l.replace("\"","")));
331 } else if(opt.startsWith("-L") || opt.startsWith("/L")) {
332 QString r = opt.mid(2), l = Option::fixPathToLocalOS(r);
333 dirs.append(new MakefileDependDir(r.replace("\"",""),
334 l.replace("\"","")));
335 remove = TRUE; //we eat this switch
336 } else if(opt.startsWith("-l") || opt.startsWith("/l")) {
337 QString lib = opt.right(opt.length() - 2), out;
338 if(!lib.isEmpty()) {
339 for(MakefileDependDir *mdd = dirs.first(); mdd; mdd = dirs.next() ) {
340 QString extension;
341 int ver = findHighestVersion(mdd->local_dir, lib);
342 if(ver > 0)
343 extension += QString::number(ver);
344 extension += ".lib";
345 if(QMakeMetaInfo::libExists(mdd->local_dir + Option::dir_sep + lib) ||
346 QFile::exists(mdd->local_dir + Option::dir_sep + lib + extension)) {
347 out = mdd->real_dir + Option::dir_sep + lib + extension;
348 break;
349 }
350 }
351 }
352 if(out.isEmpty()) {
353 remove = TRUE; //just eat it since we cannot find one..
354 } else {
355 modified_opt = TRUE;
356 (*it) = out;
357 }
358 } else if(!QFile::exists(Option::fixPathToLocalOS(opt))) {
359 QPtrList<MakefileDependDir> lib_dirs;
360 QString file = opt;
361 int slsh = file.findRev(Option::dir_sep);
362 if(slsh != -1) {
363 QString r = file.left(slsh+1), l = r;
364 fixEnvVariables(l);
365 lib_dirs.append(new MakefileDependDir(r.replace("\"",""), l.replace("\"","")));
366 file = file.right(file.length() - slsh - 1);
367 } else {
368 lib_dirs = dirs;
369 }
370 if (!project->variables()["QMAKE_QT_DLL"].isEmpty()) {
371 if(file.endsWith(".lib")) {
372 file = file.left(file.length() - 4);
373 if(!file.at(file.length()-1).isNumber()) {
374 for(MakefileDependDir *mdd = lib_dirs.first(); mdd; mdd = lib_dirs.next() ) {
375 QString lib_tmpl(file + "%1" + ".lib");
376 int ver = findHighestVersion(mdd->local_dir, file);
377 if(ver != -1) {
378 if(ver)
379 lib_tmpl = lib_tmpl.arg(ver);
380 else
381 lib_tmpl = lib_tmpl.arg("");
382 if(slsh != -1) {
383 QString dir = mdd->real_dir;
384 if(!dir.endsWith(Option::dir_sep))
385 dir += Option::dir_sep;
386 lib_tmpl.prepend(dir);
387 }
388 modified_opt = TRUE;
389 (*it) = lib_tmpl;
390 break;
391 }
392 }
393 }
394 }
395 }
396 }
397 if(remove) {
398 it = l.remove(it);
399 } else {
400 if(!quote.isNull() && modified_opt)
401 (*it) = quote + (*it) + quote;
402 ++it;
403 }
404 }
405 return TRUE;
406}
407
408void
409Win32MakefileGenerator::processPrlFiles()
410{
411 QDict<void> processed;
412 QPtrList<MakefileDependDir> libdirs;
413 libdirs.setAutoDelete(TRUE);
414 {
415 QStringList &libpaths = project->variables()["QMAKE_LIBDIR"];
416 for(QStringList::Iterator libpathit = libpaths.begin(); libpathit != libpaths.end(); ++libpathit) {
417 QString r = (*libpathit), l = r;
418 fixEnvVariables(l);
419 libdirs.append(new MakefileDependDir(r.replace("\"",""),
420 l.replace("\"","")));
421 }
422 }
423 for(bool ret = FALSE; TRUE; ret = FALSE) {
424 //read in any prl files included..
425 QStringList l_out;
426 QString where = "QMAKE_LIBS";
427 if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS"))
428 where = project->first("QMAKE_INTERNAL_PRL_LIBS");
429 QStringList &l = project->variables()[where];
430 for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
431 QString opt = (*it);
432 if(opt.startsWith("/")) {
433 if(opt.startsWith("/LIBPATH:")) {
434 QString r = opt.mid(9), l = r;
435 fixEnvVariables(l);
436 libdirs.append(new MakefileDependDir(r.replace("\"",""),
437 l.replace("\"","")));
438 }
439 } else {
440 if(!processed[opt]) {
441 if(processPrlFile(opt)) {
442 processed.insert(opt, (void*)1);
443 ret = TRUE;
444 } else {
445 for(MakefileDependDir *mdd = libdirs.first(); mdd; mdd = libdirs.next() ) {
446 QString prl = mdd->local_dir + Option::dir_sep + opt;
447 if(processed[prl]) {
448 break;
449 } else if(processPrlFile(prl)) {
450 processed.insert(prl, (void*)1);
451 ret = TRUE;
452 break;
453 }
454 }
455 }
456 }
457 }
458 if(!opt.isEmpty())
459 l_out.append(opt);
460 }
461 if(ret)
462 l = l_out;
463 else
464 break;
465 }
466}
Note: See TracBrowser for help on using the repository browser.