source: trunk/qmake/generators/win32/msvc_dsp.cpp@ 441

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

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 51.5 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** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "msvc_dsp.h"
43#include "option.h"
44
45#include <qdir.h>
46#include <qset.h>
47
48#include <stdlib.h>
49
50QT_BEGIN_NAMESPACE
51
52DspMakefileGenerator::DspMakefileGenerator() : Win32MakefileGenerator(), init_flag(false)
53{
54}
55
56bool DspMakefileGenerator::writeMakefile(QTextStream &t)
57{
58 if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) {
59 /* for now just dump, I need to generated an empty dsp or something.. */
60 fprintf(stderr, "Project file not generated because all requirements not met:\n\t%s\n",
61 var("QMAKE_FAILED_REQUIREMENTS").toLatin1().constData());
62 return true;
63 }
64
65 // Generate workspace file
66 if(project->first("TEMPLATE") == "vcsubdirs") {
67 if (!project->isActiveConfig("build_pass")) {
68 debug_msg(1, "Generator: MSVC: Writing workspave file");
69 writeSubDirs(t);
70 } else {
71 debug_msg(1, "Generator: MSVC: Not writing workspace file for build_pass configs");
72 }
73 return true;
74 } else if (project->first("TEMPLATE") == "vcapp" || project->first("TEMPLATE") == "vclib") {
75 if(!project->isActiveConfig("build_pass"))
76 return writeDspParts(t);
77 return true;
78 }
79 return project->isActiveConfig("build_pass");
80}
81
82bool DspMakefileGenerator::hasBuiltinCompiler(const QString &filename) const
83{
84 for (int i = 0; i < Option::cpp_ext.count(); ++i)
85 if (filename.endsWith(Option::cpp_ext.at(i)))
86 return true;
87 for (int i = 0; i < Option::c_ext.count(); ++i)
88 if (filename.endsWith(Option::c_ext.at(i)))
89 return true;
90 return false;
91}
92
93QString DspMakefileGenerator::replaceExtraCompilerVariables(const QString &var, const QStringList &in, const QStringList &out)
94{
95 QString ret = MakefileGenerator::replaceExtraCompilerVariables(var, in, out);
96 ret.replace("$(DEFINES)", varGlue("PRL_EXPORT_DEFINES"," -D"," -D","") +
97 varGlue("DEFINES"," -D"," -D",""));
98
99 QString incpath = this->var("MSVCDSP_INCPATH");
100 incpath.replace("/I", "-I");
101 ret.replace("$(INCPATH)", incpath);
102 return ret;
103}
104
105
106// if config is part of a multibuild thenthe gule (this) has the correct MSVCDSP_PROJECT
107QString DspMakefileGenerator::configName(DspMakefileGenerator * config)
108{
109 return var("MSVCDSP_PROJECT") + config->var("MSVCDSP_CONFIG_NAME");
110}
111
112bool DspMakefileGenerator::writeDspHeader(QTextStream &t)
113{
114 DspMakefileGenerator * config = this;
115 if (mergedProjects.count())
116 config = mergedProjects.at(0);
117
118 t << "# Microsoft Developer Studio Project File - Name=\"" << var("MSVCDSP_PROJECT") << "\" - Package Owner=<4>" << endl;
119 t << "# Microsoft Developer Studio Generated Build File, Format Version 6.00" << endl;
120 t << "# ** DO NOT EDIT **" << endl;
121 t << endl;
122 t << "# TARGTYPE \"Win32 (x86) " << var("MSVCDSP_TARGETTYPE") << "\" " << var("MSVCDSP_DSPTYPE") << endl;
123 t << endl;
124 t << "CFG=\"" << configName(config) << "\"" << endl;
125 t << "!MESSAGE This is not a valid makefile. To build this project using NMAKE," << endl;
126 t << "!MESSAGE use the Export Makefile command and run" << endl;
127 t << "!MESSAGE " << endl;
128 t << "!MESSAGE NMAKE /f " << escapeFilePath(var("TARGET")) << ".mak." << endl;
129 t << "!MESSAGE " << endl;
130 t << "!MESSAGE You can specify a configuration when running NMAKE" << endl;
131 t << "!MESSAGE by defining the macro CFG on the command line. For example:" << endl;
132 t << "!MESSAGE " << endl;
133 t << "!MESSAGE NMAKE /f " << escapeFilePath(var("TARGET")) << ".mak CFG=\"" << configName(config) << "\"" << endl;
134 t << "!MESSAGE " << endl;
135 t << "!MESSAGE Possible choices for configuration are:" << endl;
136 t << "!MESSAGE " << endl;
137 if (mergedProjects.count()) {
138 for (int i = 0; i < mergedProjects.count(); ++i) {
139 DspMakefileGenerator * config = mergedProjects.at(i);
140 t << "!MESSAGE \"" << configName(config) << "\" (based on \"Win32 (x86) " << config->var("MSVCDSP_TARGETTYPE") << "\")" << endl;
141 }
142 } else {
143 t << "!MESSAGE \"" << configName(config) << "\" (based on \"Win32 (x86) " << config->var("MSVCDSP_TARGETTYPE") << "\")" << endl;
144 }
145 t << "!MESSAGE " << endl;
146 t << endl;
147 t << "# Begin Project" << endl;
148 t << "# PROP AllowPerConfigDependencies 0" << endl;
149 t << "# PROP Scc_ProjName \"\"" << endl;
150 t << "# PROP Scc_LocalPath \"\"" << endl;
151 t << "CPP=" << config->var("QMAKE_CC") << endl;
152 t << "MTL=" << config->var("QMAKE_IDL") << endl;
153 t << "RSC=" << config->var("QMAKE_RC") << endl;
154 t << "BSC32=bscmake.exe" << endl;
155
156 return true;
157}
158
159
160bool DspMakefileGenerator::writeDspParts(QTextStream &t)
161{
162 //bool staticLibTarget = var("MSVCDSP_DSPTYPE") == "0x0104";
163
164 writeDspHeader(t);
165 writeDspConfig(t, this);
166 t << endl;
167 t << "# Begin Target" << endl;
168 t << endl;
169 t << "# Name \"" << configName(this) << "\"" << endl;
170 t << endl;
171
172
173 QStringList listNames = QString("SOURCES|DEF_FILE").split("|");
174 QStringList allListNames = listNames;
175 writeFileGroup(t, listNames, "Source Files", "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat");
176 listNames = QStringList("HEADERS");
177 allListNames += listNames;
178 writeFileGroup(t, QStringList("HEADERS"), "Header Files", "h;hpp;hxx;hm;inl");
179 listNames = QString("FORMS|INTERFACES|FORMS3").split("|");
180 allListNames += listNames;
181 writeFileGroup(t, listNames, "Form Files", "ui");
182 listNames = QStringList("IMAGES");
183 allListNames += listNames;
184 writeFileGroup(t, QStringList("IMAGES"), "Image Files", "");
185 listNames = QString("RC_FILE|RESOURCES").split("|");
186 allListNames += listNames;
187 writeFileGroup(t, listNames, "Resources", "rc;qrc");
188 listNames = QStringList("TRANSLATIONS");
189 allListNames += listNames;
190 writeFileGroup(t, listNames, "Translations", "ts;xlf");
191 listNames = QStringList("LEXSOURCES");
192 allListNames += listNames;
193 writeFileGroup(t, listNames, "Lexables", "l");
194 listNames = QStringList("YACCSOURCES");
195 allListNames += listNames;
196 writeFileGroup(t, listNames, "Yaccables", "y");
197 listNames = QStringList("TYPELIBS");
198 allListNames += listNames;
199 writeFileGroup(t, listNames, "Type Libraries", "tlb;olb");
200
201 if (!project->isEmpty("QMAKE_EXTRA_COMPILERS")) {
202 const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
203 for (QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
204 const QStringList &inputs = project->values((*it)+".input");
205 for (QStringList::ConstIterator input = inputs.begin(); input != inputs.end(); ++input) {
206 if (!allListNames.contains((*input)) && *input != "UIC3_HEADERS")
207 writeFileGroup(t, QStringList((*input)), (*input) + " Files", "");
208 }
209 }
210 }
211
212 project->values("SWAPPED_BUILD_STEPS") = swappedBuildSteps.keys();
213
214 writeFileGroup(t, QString("GENERATED_SOURCES|GENERATED_FILES|SWAPPED_BUILD_STEPS").split("|"), "Generated", "");
215
216 t << "# End Target" << endl;
217 t << "# End Project" << endl;
218 return true;
219}
220
221void
222DspMakefileGenerator::init()
223{
224 if(init_flag)
225 return;
226 QStringList::Iterator it;
227 init_flag = true;
228
229 platform = "Win32";
230 if(!project->values("QMAKE_PLATFORM").isEmpty())
231 platform = varGlue("QMAKE_PLATFORM", "", " ", "");
232
233 // this should probably not be here, but I'm using it to wrap the .t files
234 if(project->first("TEMPLATE") == "vcapp")
235 project->values("QMAKE_APP_FLAG").append("1");
236 else if(project->first("TEMPLATE") == "vclib")
237 project->values("QMAKE_LIB_FLAG").append("1");
238
239 if(project->values("QMAKESPEC").isEmpty())
240 project->values("QMAKESPEC").append(qgetenv("QMAKESPEC"));
241
242 project->values("QMAKE_LIBS") += escapeFilePaths(project->values("LIBS"));
243 processVars();
244
245 if(!project->values("VERSION").isEmpty()) {
246 QString version = project->values("VERSION").first();
247 int firstDot = version.indexOf(".");
248 QString major = version.left(firstDot);
249 QString minor = version.right(version.length() - firstDot - 1);
250 minor.replace(".", "");
251 project->values("MSVCDSP_LFLAGS").append("/VERSION:" + major + "." + minor);
252 }
253
254 QString msvcdsp_project;
255 if(!project->isEmpty("TARGET")) {
256 project->values("TARGET") = unescapeFilePaths(project->values("TARGET"));
257 msvcdsp_project = project->first("TARGET");
258 }
259
260 MakefileGenerator::init();
261
262 if(msvcdsp_project.isEmpty())
263 msvcdsp_project = Option::output.fileName();
264
265 msvcdsp_project = msvcdsp_project.right(msvcdsp_project.length() - msvcdsp_project.lastIndexOf("\\") - 1);
266 int dotFind = msvcdsp_project.lastIndexOf(".");
267 if(dotFind != -1)
268 msvcdsp_project = msvcdsp_project.left(dotFind);
269 msvcdsp_project.replace("-", "");
270
271 project->values("MSVCDSP_PROJECT").append(msvcdsp_project);
272
273 QStringList &proj = project->values("MSVCDSP_PROJECT");
274
275 for(QStringList::Iterator it = proj.begin(); it != proj.end(); ++it)
276 (*it).replace(QRegExp("\\.[a-zA-Z0-9_]*$"), "");
277
278 if(!project->values("QMAKE_APP_FLAG").isEmpty()) {
279 if(project->isActiveConfig("console")) {
280 project->values("MSVCDSP_TARGETTYPE").append("Console Application");
281 project->values("MSVCDSP_DSPTYPE").append("0x0103");
282 project->values("MSVCDSP_DEFINES").append(" /D \"_CONSOLE\" ");
283 } else {
284 project->values("MSVCDSP_TARGETTYPE").append("Application");
285 project->values("MSVCDSP_DSPTYPE").append("0x0101");
286 project->values("MSVCDSP_DEFINES").append(" /D \"_WINDOWS\" ");
287 }
288 } else {
289 if(project->isActiveConfig("dll")) {
290 project->values("MSVCDSP_TARGETTYPE").append("Dynamic-Link Library");
291 project->values("MSVCDSP_DSPTYPE").append("0x0102");
292 project->values("MSVCDSP_DEFINES").append(" /D \"_USRDLL\" ");
293 } else {
294 project->values("MSVCDSP_TARGETTYPE").append("Static Library");
295 project->values("MSVCDSP_DSPTYPE").append("0x0104");
296 project->values("MSVCDSP_DEFINES").append(" /D \"_LIB\" ");
297 }
298 }
299
300 project->values("MSVCDSP_LFLAGS") += project->values("QMAKE_LFLAGS");
301
302 if(!project->values("QMAKE_LIBDIR").isEmpty())
303 project->values("MSVCDSP_LFLAGS").append(valGlue(
304 escapeFilePaths(project->values("QMAKE_LIBDIR")),
305 "/LIBPATH:"," /LIBPATH:",""));
306
307 project->values("MSVCDSP_DEFINES").append(varGlue("DEFINES","/D ","" " /D ",""));
308 project->values("MSVCDSP_DEFINES").append(varGlue("PRL_EXPORT_DEFINES","/D ","" " /D ",""));
309 project->values("MSVCDSP_DEFINES").append(" /D \"WIN32\" ");
310
311 QStringList &libs = project->values("QMAKE_LIBS");
312 for(QStringList::Iterator libit = libs.begin(); libit != libs.end(); ++libit) {
313 project->values("MSVCDSP_LIBS").append(" " + escapeFilePath(*libit));
314 }
315
316 QStringList &incs = project->values("INCLUDEPATH");
317 for(QStringList::Iterator incit = incs.begin(); incit != incs.end(); ++incit) {
318 QString inc = (*incit);
319 project->values("MSVCDSP_INCPATH").append("/I" + escapeFilePath(inc));
320 }
321 project->values("MSVCDSP_INCPATH").append("/I" + escapeFilePath(specdir()));
322
323 QString dest;
324 QString preLinkStep;
325 QString postLinkStep;
326 QString copyDllStep;
327
328 if(!project->values("QMAKE_PRE_LINK").isEmpty())
329 preLinkStep += var("QMAKE_PRE_LINK");
330
331 if(!project->values("QMAKE_POST_LINK").isEmpty())
332 postLinkStep += var("QMAKE_POST_LINK");
333
334 // don't destroy the target, it is used by prl writer.
335 if(!project->values("DESTDIR").isEmpty()) {
336 dest = project->first("DESTDIR");
337 project->values("DESTDIR").first() = dest;
338 dest = project->values("TARGET").first() + project->first("TARGET_EXT");
339 dest.prepend(project->first("DESTDIR"));
340 Option::fixPathToTargetOS(dest);
341 dest = escapeFilePath(dest);
342
343 project->values("MSVCDSP_TARGET").append(
344 QString("/out:") + dest);
345 if(project->isActiveConfig("dll")) {
346 QString imp = dest;
347 imp.replace(".dll", ".lib");
348 project->values("MSVCDSP_TARGET").append(QString(" /implib:") + escapeFilePath(imp));
349 }
350 }
351
352 if(project->isActiveConfig("dll") && !project->values("DLLDESTDIR").isEmpty()) {
353 QStringList dlldirs = project->values("DLLDESTDIR");
354 if(dlldirs.count())
355 copyDllStep += "\t";
356 for(QStringList::Iterator dlldir = dlldirs.begin(); dlldir != dlldirs.end(); ++dlldir) {
357 copyDllStep += "copy \"$(TargetPath)\" " + escapeFilePath(Option::fixPathToTargetOS(*dlldir)) + "\t";
358 }
359 }
360
361 if(!preLinkStep.isEmpty()) {
362 project->values("MSVCDSP_PRE_LINK").append(
363 "# Begin Special Build Tool\n"
364 "SOURCE=$(InputPath)\n"
365 "PreLink_Desc=Post Build Step\n"
366 "PreLink_Cmds=" + preLinkStep + "\n"
367 "# End Special Build Tool\n");
368 }
369
370 if(!postLinkStep.isEmpty() || !copyDllStep.isEmpty()) {
371 project->values("MSVCDSP_POST_LINK").append(
372 "# Begin Special Build Tool\n"
373 "SOURCE=$(InputPath)\n"
374 "PostBuild_Desc=Post Build Step\n"
375 "PostBuild_Cmds=" + postLinkStep + copyDllStep + "\n"
376 "# End Special Build Tool\n");
377 }
378
379 QStringList &formList = project->values("FORMS");
380 for(QStringList::ConstIterator hit = formList.begin(); hit != formList.end(); ++hit) {
381 if(exists(*hit + ".h"))
382 project->values("SOURCES").append(*hit + ".h");
383 }
384 QStringList &form3List = project->values("FORMS3");
385 for(QStringList::ConstIterator hit = form3List.begin(); hit != form3List.end(); ++hit) {
386 if(exists(*hit + ".h"))
387 project->values("SOURCES").append(*hit + ".h");
388 }
389
390 project->values("QMAKE_INTERNAL_PRL_LIBS") << "MSVCDSP_LIBS";
391
392 // Move some files around //### is this compat?
393 if (!project->values("IMAGES").isEmpty()) {
394 QString imageFactory(project->first("QMAKE_IMAGE_COLLECTION"));
395 project->values("GENERATED_SOURCES") += imageFactory;
396 project->values("SOURCES").removeAll(imageFactory);
397 }
398
399 // Setup PCH variables
400 precompH = project->first("PRECOMPILED_HEADER");
401 namePCH = fileInfo(precompH).fileName();
402 usePCH = !precompH.isEmpty() && project->isActiveConfig("precompile_header");
403 if (usePCH) {
404 // Created files
405 precompObj = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch" + Option::obj_ext;
406 precompPch = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch.pch";
407
408 // Add PRECOMPILED_HEADER to HEADERS
409 if (!project->values("HEADERS").contains(precompH))
410 project->values("HEADERS") += precompH;
411 // Add precompile compiler options
412 project->values("PRECOMPILED_FLAGS") = QStringList("/Fp" + precompPch + " /Yu" + escapeFilePath(namePCH) + " /FI" + escapeFilePath(namePCH) + " ");
413 // Return to variable pool
414 project->values("PRECOMPILED_OBJECT") = QStringList(precompObj);
415 project->values("PRECOMPILED_PCH") = QStringList(precompPch);
416 }
417
418 QString buildName;
419 if (!var("BUILD_NAME").isEmpty())
420 buildName = var("BUILD_NAME");
421 else if (project->isActiveConfig("debug"))
422 buildName = "Debug";
423 else
424 buildName = "Release";
425
426 project->values("MSVCDSP_CONFIG_NAME") = QStringList(" - " + platform + " " + buildName);
427}
428
429void DspMakefileGenerator::processPrlVariable(const QString &var, const QStringList &l)
430{
431 if(var == "QMAKE_PRL_DEFINES") {
432 QStringList &out = project->values("MSVCDSP_DEFINES");
433 for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
434 if(out.indexOf((*it)) == -1)
435 out.append((" /D \"" + *it + "\""));
436 }
437 } else {
438 MakefileGenerator::processPrlVariable(var, l);
439 }
440}
441
442bool DspMakefileGenerator::openOutput(QFile &file, const QString &build) const
443{
444 QString outdir;
445 if(!file.fileName().isEmpty()) {
446 if(QDir::isRelativePath(file.fileName()))
447 file.setFileName(Option::output_dir + "/" + file.fileName()); //pwd when qmake was run
448 QFileInfo fi(fileInfo(file.fileName()));
449 if(fi.isDir())
450 outdir = file.fileName() + QDir::separator();
451 }
452
453 if(!outdir.isEmpty() || file.fileName().isEmpty()) {
454 QString ext = project->first("DSP_EXTENSION");
455 if(project->first("TEMPLATE") == "vcsubdirs") {
456 if (!project->first("DSW_EXTENSION").isEmpty())
457 ext = project->first("DSW_EXTENSION");
458 else
459 ext = ".dsw";
460 }
461 QString outputName = unescapeFilePath(project->first("QMAKE_DSP_PROJECT_NAME"));
462 if (!project->first("MAKEFILE").isEmpty())
463 outputName = unescapeFilePath(project->first("MAKEFILE"));
464 if (outputName.isEmpty())
465 outputName = unescapeFilePath(project->first("QMAKE_ORIG_TARGET"));
466 file.setFileName(outdir + outputName + ext);
467 }
468
469 if(QDir::isRelativePath(file.fileName())) {
470 QString ofile = Option::fixPathToLocalOS(file.fileName());
471 int slashfind = ofile.lastIndexOf(Option::dir_sep);
472 if(slashfind == -1) {
473 ofile = ofile.replace(QRegExp("-"), "_");
474 } else {
475 int hypenfind = ofile.indexOf('-', slashfind);
476 while (hypenfind != -1 && slashfind < hypenfind) {
477 ofile = ofile.replace(hypenfind, 1, "_");
478 hypenfind = ofile.indexOf('-', hypenfind + 1);
479 }
480 }
481 file.setFileName(Option::fixPathToLocalOS(qmake_getpwd() + Option::dir_sep + ofile));
482 }
483 return Win32MakefileGenerator::openOutput(file, build);
484}
485
486bool DspMakefileGenerator::mergeBuildProject(MakefileGenerator *other)
487{
488
489 mergedProjects.prepend(static_cast<DspMakefileGenerator*>(other));
490 return true;
491}
492
493bool DspMakefileGenerator::writeProjectMakefile()
494{
495 bool ret = true;
496
497 QTextStream t(&Option::output);
498 // Check if all requirements are fulfilled
499 if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) {
500 fprintf(stderr, "Project file not generated because all requirements not met:\n\t%s\n",
501 var("QMAKE_FAILED_REQUIREMENTS").toLatin1().constData());
502 return true;
503 }
504
505 // Generate project file
506 if(project->first("TEMPLATE") == "vcapp" ||
507 project->first("TEMPLATE") == "vclib") {
508 if (!mergedProjects.count()) {
509 warn_msg(WarnLogic, "Generator: MSVC DSP: no single configuration created, cannot output project!");
510 return false;
511 }
512 debug_msg(1, "Generator: MSVC 6: Writing project file");
513
514 writeDspHeader(t);
515 for (int i = 0; i < mergedProjects.count(); ++i) {
516 DspMakefileGenerator* config = mergedProjects.at(i);
517 t << endl;
518 if (i == 0)
519 t << "!IF";
520 else
521 t << "!ELSEIF";
522 t << " \"$(CFG)\" == \"" << configName(config) << "\"" << endl;
523 t << endl;
524 writeDspConfig(t, config);
525 }
526 t << endl;
527 t << "!ENDIF " << endl;
528 t << endl;
529 t << "# Begin Target" << endl;
530 t << endl;
531 for (int i = 0; i < mergedProjects.count(); ++i)
532 t << "# Name \"" << configName(mergedProjects.at(i)) << "\"" << endl;
533 t << endl;
534
535 QMap< QString, QSet<QString> > files;
536
537 // merge source files
538 for (int i = 0; i < mergedProjects.count(); ++i) {
539
540 DspMakefileGenerator* config = mergedProjects.at(i);
541
542 files["DEF_FILE"] += config->project->values("DEF_FILE").toSet();
543 files["SOURCES"] += config->project->values("SOURCES").toSet();
544 files["HEADERS"] += config->project->values("HEADERS").toSet();
545 files["INTERFACES"] += config->project->values("INTERFACES").toSet();
546 files["FORMS"] += config->project->values("FORMS").toSet();
547 files["FORMS"] += config->project->values("FORMS3").toSet();
548 files["IMAGES"] += config->project->values("IMAGES").toSet();
549 files["RC_FILE"] += config->project->values("RC_FILE").toSet();
550 files["RESOURCES"] += config->project->values("RESOURCES").toSet();
551 files["TRANSLATIONS"] += config->project->values("TRANSLATIONS").toSet();
552 files["LEXSOURCES"] += config->project->values("LEXSOURCES").toSet();
553 files["YACCSOURCES"] += config->project->values("YACCSOURCES").toSet();
554 files["TYPELIBS"] += config->project->values("TYPELIBS").toSet();
555
556 if (!config->project->isEmpty("QMAKE_EXTRA_COMPILERS")) {
557 const QStringList &quc = config->project->values("QMAKE_EXTRA_COMPILERS");
558 for (QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
559 const QStringList &inputs = project->values((*it)+".input");
560 for (QStringList::ConstIterator input = inputs.begin(); input != inputs.end(); ++input) {
561 if (*input != "UIC3_HEADERS")
562 files[(*input)] += config->project->values((*input)).toSet();
563 }
564 }
565 }
566 }
567
568 QStringList keys = files.keys();
569 for (int k = 0; k < keys.size(); ++k)
570 project->values(keys.at(k)) = QList<QString>::fromSet(files[keys.at(k)]);
571
572 QStringList listNames = QString("SOURCES|DEF_FILE").split("|");
573 QStringList allListNames = listNames;
574 writeFileGroup(t, listNames, "Source Files", "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat");
575 listNames = QStringList("HEADERS");
576 allListNames += listNames;
577 writeFileGroup(t, listNames, "Header Files", "h;hpp;hxx;hm;inl");
578 listNames = QString("FORMS|INTERFACES|FORMS3").split("|");
579 allListNames += listNames;
580 writeFileGroup(t, listNames, "Form Files", "ui");
581 listNames = QStringList("IMAGES");
582 allListNames += listNames;
583 writeFileGroup(t, listNames, "Image Files", "");
584 listNames = QString("RC_FILE|RESOURCES").split("|");
585 allListNames += listNames;
586 writeFileGroup(t, listNames, "Resources", "rc;qrc");
587 listNames = QStringList("TRANSLATIONS");
588 allListNames += listNames;
589 writeFileGroup(t, listNames, "Translations", "ts;xlf");
590 listNames = QStringList("LEXSOURCES");
591 allListNames += listNames;
592 writeFileGroup(t, listNames, "Lexables", "l");
593 listNames = QStringList("YACCSOURCES");
594 allListNames += listNames;
595 writeFileGroup(t, listNames, "Yaccables", "y");
596 listNames = QStringList("TYPELIBS");
597 allListNames += listNames;
598 writeFileGroup(t, listNames, "Type Libraries", "tlb;olb");
599
600 for (int l = 0; l < allListNames.size(); ++l)
601 keys.removeAll(allListNames.at(l));
602
603 for (int k = 0; k < keys.size(); ++k)
604 writeFileGroup(t, QStringList(keys.at(k)), keys.at(k) + " Files", "");
605
606 // done last as generated may have changed when creating build rules for the above
607 for (int i = 0; i < mergedProjects.count(); ++i) {
608
609 DspMakefileGenerator* config = mergedProjects.at(i);
610
611 config->project->values("SWAPPED_BUILD_STEPS") = config->swappedBuildSteps.keys();
612 files["SWAPPED_BUILD_STEPS"] += config->project->values("SWAPPED_BUILD_STEPS").toSet();
613
614 files["GENERATED_SOURCES"] += config->project->values("GENERATED_SOURCES").toSet();
615 files["GENERATED_FILES"] += config->project->values("GENERATED_FILES").toSet();
616 }
617
618 project->values("SWAPPED_BUILD_STEPS") = QList<QString>::fromSet(files["SWAPPED_BUILD_STEPS"]);
619 project->values("GENERATED_SOURCES") = QList<QString>::fromSet(files["GENERATED_SOURCES"]);
620 project->values("GENERATED_FILES") = QList<QString>::fromSet(files["GENERATED_FILES"]);
621
622 writeFileGroup(t, QString("GENERATED_SOURCES|GENERATED_FILES|SWAPPED_BUILD_STEPS").split("|"), "Generated", "");
623 t << endl;
624 t << "# End Target" << endl;
625 t << "# End Project" << endl;
626 }else if(project->first("TEMPLATE") == "vcsubdirs") {
627 ret = writeMakefile(t);
628 }
629
630 return ret;
631}
632
633const char _dswHeader60[] = "Microsoft Developer Studio Workspace File, Format Version 6.00\n";
634const char _dswWarning[] = "# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\n";
635const char _dswDevider[] = "###############################################################################\n";
636const char _dswProjectName[] = "Project: \"%1\"=%2 - Package Owner=<4>\n"; // %1 = project name, %2 = project path
637const char _dswPackage5Start[] = "Package=<5>\n{{{\n";
638const char _dswPackage5Stop[] = "}}}\n";
639const char _dswPackage4Start[] = "Package=<4>\n{{{\n";
640const char _dswPackage4Stop[] = "}}}\n";
641const char _dswProjectDep[] = " Begin Project Dependency\n Project_Dep_Name %1\n End Project Dependency\n"; // %1 = project name
642const char _dswGlobal[] = "Global:\n\nPackage=<5>\n{{{\n}}}\n\nPackage=<3>\n{{{\n}}}\n\n";
643
644
645struct WorkspaceDepend {
646 QString dspProjectFile, orig_target, target;
647 QStringList dependencies;
648};
649
650void DspMakefileGenerator::writeSubDirs(QTextStream &t)
651{
652 // Output headers
653 t << _dswHeader60;
654 t << _dswWarning;
655 t << endl;
656
657 QHash<QString, WorkspaceDepend*> workspace_depends;
658 QList<WorkspaceDepend*> workspace_cleanup;
659 QStringList subdirs = project->values("SUBDIRS");
660 QString oldpwd = qmake_getpwd();
661
662 // Make sure that all temp projects are configured
663 // for release so that the depends are created
664 // without the debug <lib>dxxx.lib name mangling
665 QStringList old_after_vars = Option::after_user_vars;
666 Option::after_user_vars.append("CONFIG+=release");
667
668 for(int i = 0; i < subdirs.size(); ++i) {
669 QString tmp = subdirs.at(i);
670 if(!project->isEmpty(tmp + ".file")) {
671 if(!project->isEmpty(tmp + ".subdir"))
672 warn_msg(WarnLogic, "Cannot assign both file and subdir for subdir %s",
673 tmp.toLatin1().constData());
674 tmp = project->first(tmp + ".file");
675 } else if(!project->isEmpty(tmp + ".subdir")) {
676 tmp = project->first(tmp + ".subdir");
677 }
678
679 QFileInfo fi(fileInfo(Option::fixPathToLocalOS(tmp, true)));
680 if(fi.exists()) {
681 if(fi.isDir()) {
682 QString profile = tmp;
683 if(!profile.endsWith(Option::dir_sep))
684 profile += Option::dir_sep;
685 profile += fi.baseName() + ".pro";
686 subdirs.append(profile);
687 } else {
688 QMakeProject tmp_proj;
689 QString dir = fi.path(), fn = fi.fileName();
690 if(!dir.isEmpty()) {
691 if(!qmake_setpwd(dir))
692 fprintf(stderr, "Cannot find directory: %s\n", dir.toLatin1().constData());
693 }
694 if(tmp_proj.read(fn)) {
695 // Check if all requirements are fulfilled
696 if(!tmp_proj.variables()["QMAKE_FAILED_REQUIREMENTS"].isEmpty()) {
697 fprintf(stderr, "Project file(%s) not added to Workspace because all requirements not met:\n\t%s\n",
698 fn.toLatin1().constData(), tmp_proj.values("QMAKE_FAILED_REQUIREMENTS").join(" ").toLatin1().constData());
699 continue;
700 }
701 if(tmp_proj.first("TEMPLATE") == "vcsubdirs") {
702 QStringList tmp_proj_subdirs = tmp_proj.variables()["SUBDIRS"];
703 for(int x = 0; x < tmp_proj_subdirs.size(); ++x) {
704 QString tmpdir = tmp_proj_subdirs.at(x);
705 if(!tmp_proj.isEmpty(tmpdir + ".file")) {
706 if(!tmp_proj.isEmpty(tmpdir + ".subdir"))
707 warn_msg(WarnLogic, "Cannot assign both file and subdir for subdir %s",
708 tmpdir.toLatin1().constData());
709 tmpdir = tmp_proj.first(tmpdir + ".file");
710 } else if(!tmp_proj.isEmpty(tmpdir + ".subdir")) {
711 tmpdir = tmp_proj.first(tmpdir + ".subdir");
712 }
713 subdirs += fileFixify(tmpdir);
714 }
715 } else if(tmp_proj.first("TEMPLATE") == "vcapp" || tmp_proj.first("TEMPLATE") == "vclib") {
716 // Initialize a 'fake' project to get the correct variables
717 // and to be able to extract all the dependencies
718 DspMakefileGenerator tmp_dsp;
719 tmp_dsp.setNoIO(true);
720 tmp_dsp.setProjectFile(&tmp_proj);
721 if(Option::debug_level) {
722 QMap<QString, QStringList> &vars = tmp_proj.variables();
723 for(QMap<QString, QStringList>::Iterator it = vars.begin();
724 it != vars.end(); ++it) {
725 if(it.key().left(1) != "." && !it.value().isEmpty())
726 debug_msg(1, "%s: %s === %s", fn.toLatin1().constData(), it.key().toLatin1().constData(),
727 it.value().join(" :: ").toLatin1().constData());
728 }
729 }
730
731 // We assume project filename is [QMAKE_ORIG_TARGET].vcproj
732 QString dsp = unescapeFilePath(tmp_dsp.project->first("MSVCDSP_PROJECT") + project->first("DSP_EXTENSION"));
733
734 // If file doesn't exsist, then maybe the users configuration
735 // doesn't allow it to be created. Skip to next...
736 if(!exists(qmake_getpwd() + Option::dir_sep + dsp)) {
737 warn_msg(WarnLogic, "Ignored (not found) '%s'", QString(qmake_getpwd() + Option::dir_sep + dsp).toLatin1().constData());
738 goto nextfile; // # Dirty!
739 }
740
741 WorkspaceDepend *newDep = new WorkspaceDepend;
742 newDep->dspProjectFile = fileFixify(dsp);
743 newDep->orig_target = unescapeFilePath(tmp_proj.first("QMAKE_ORIG_TARGET"));
744 newDep->target = tmp_proj.first("MSVCDSP_PROJECT").section(Option::dir_sep, -1) + tmp_proj.first("TARGET_EXT");
745
746 // We want to store it as the .lib name.
747 if(newDep->target.endsWith(".dll"))
748 newDep->target = newDep->target.left(newDep->target.length()-3) + "lib";
749
750 // All projects having mocable sourcefiles are dependent on moc.exe
751 if(tmp_proj.variables()["CONFIG"].contains("moc"))
752 newDep->dependencies << "moc.exe";
753
754 // All extra compilers which has valid input are considered dependencies
755 const QStringList &quc = tmp_proj.variables()["QMAKE_EXTRA_COMPILERS"];
756 for(QStringList::ConstIterator it = quc.constBegin(); it != quc.constEnd(); ++it) {
757 const QStringList &invar = tmp_proj.variables().value((*it) + ".input");
758 for(QStringList::ConstIterator iit = invar.constBegin(); iit != invar.constEnd(); ++iit) {
759 const QStringList fileList = tmp_proj.variables().value(*iit);
760 if (!fileList.isEmpty()) {
761 QString dep = tmp_proj.first((*it) + ".commands").section('/', -1).section('\\', -1);
762 if (!newDep->dependencies.contains(dep))
763 newDep->dependencies << dep;
764 }
765 }
766 }
767
768 // Add all unknown libs to the deps
769 QStringList where("QMAKE_LIBS");
770 if(!tmp_proj.isEmpty("QMAKE_INTERNAL_PRL_LIBS"))
771 where = tmp_proj.variables()["QMAKE_INTERNAL_PRL_LIBS"];
772
773 for(QStringList::iterator wit = where.begin();
774 wit != where.end(); ++wit) {
775 QStringList &l = tmp_proj.variables()[(*wit)];
776 for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
777 QString opt = (*it).trimmed();
778 if(!opt.startsWith("/") && // Not a switch
779 opt != newDep->target && // Not self
780 opt != "opengl32.lib" && // We don't care about these libs
781 opt != "glu32.lib" && // to make depgen alittle faster
782 opt != "kernel32.lib" &&
783 opt != "user32.lib" &&
784 opt != "gdi32.lib" &&
785 opt != "comdlg32.lib" &&
786 opt != "advapi32.lib" &&
787 opt != "shell32.lib" &&
788 opt != "ole32.lib" &&
789 opt != "oleaut32.lib" &&
790 opt != "uuid.lib" &&
791 opt != "imm32.lib" &&
792 opt != "winmm.lib" &&
793 opt != "wsock32.lib" &&
794 opt != "ws2_32.lib" &&
795 opt != "winspool.lib" &&
796 opt != "delayimp.lib")
797 {
798 newDep->dependencies << opt.section(Option::dir_sep, -1);
799 }
800 }
801 }
802 workspace_cleanup.append(newDep);
803 workspace_depends.insert(newDep->target, newDep);
804
805 debug_msg(1, "Generator: MSVC: Added project (name:'%s' path:'%s' deps:'%s')",
806 qPrintable(newDep->target) , qPrintable(newDep->dspProjectFile),
807 qPrintable(newDep->dependencies.join(";")));
808 }
809 }
810nextfile:
811 qmake_setpwd(oldpwd);
812 }
813 }
814 }
815
816 // Restore previous after_user_var options
817 Option::after_user_vars = old_after_vars;
818
819 // Output all projects
820 QString dswProjectName = QLatin1String(_dswProjectName);
821 QString dswProjectDep = QLatin1String(_dswProjectDep);
822 for(QList<WorkspaceDepend*>::Iterator it = workspace_cleanup.begin(); it != workspace_cleanup.end(); ++it) {
823 t << _dswDevider;
824 t << endl;
825 t << dswProjectName.arg((*it)->orig_target).arg((*it)->dspProjectFile);
826 t << endl;
827 t << _dswPackage5Start;
828 t << _dswPackage5Stop;
829 t << endl;
830 t << _dswPackage4Start;
831
832 // Output project dependencies
833 for(QStringList::iterator dit = (*it)->dependencies.begin(); dit != (*it)->dependencies.end(); ++dit) {
834 if(WorkspaceDepend *vc = workspace_depends[*dit])
835 t << dswProjectDep.arg(vc->orig_target);
836 }
837
838 t << _dswPackage4Stop;
839 }
840
841 // Output global part
842 t << _dswDevider << endl;
843 t << _dswGlobal;
844 t << _dswDevider;
845 t << endl << endl;
846}
847
848class FolderGroup
849{
850public:
851 QString name;
852 QString filter;
853 QMap<QString, FolderGroup *> subFolders;
854 QMap<QString, QString> files;
855
856 void insertStructured(const QString &file, const QString &fileListName)
857 {
858 QStringList path = QFileInfo(file).path().split("/");
859 if (!path.isEmpty() && path.at(0) == ".")
860 path.takeAt(0);
861 FolderGroup *currentFolder = this;
862 for (int i = 0; i < path.size(); i++) {
863 if (currentFolder->subFolders.contains(path.at(i))) {
864 currentFolder = currentFolder->subFolders.value(path.at(i));
865 } else {
866 FolderGroup *newFolder = new FolderGroup;
867 newFolder->name = path.at(i);
868 currentFolder->subFolders.insert(path.at(i), newFolder);
869 currentFolder = newFolder;
870 }
871 }
872 currentFolder->files.insert(file, fileListName);
873 }
874
875 void insertFlat(const QString &file, const QString &fileListName)
876 {
877 files.insert(file, fileListName);
878 }
879
880 ~FolderGroup()
881 {
882 qDeleteAll(subFolders.values());
883 }
884};
885
886bool DspMakefileGenerator::writeFileGroup(QTextStream &t, const QStringList &listNames, const QString &group, const QString &filter)
887{
888 FolderGroup root;
889 root.name = group;
890 root.filter = filter;
891
892 for (int i = 0; i < listNames.count(); ++i) {
893 QStringList list = project->values(listNames.at(i));
894 for (int j = 0; j < list.count(); ++j) {
895 const QString name = list.at(j);
896 if (name.isEmpty())
897 continue;
898 if (project->isActiveConfig("flat"))
899 root.insertFlat(name, listNames.at(i));
900 else
901 root.insertStructured(name, listNames.at(i));
902 }
903 }
904
905 if (root.files.isEmpty() && root.subFolders.isEmpty())
906 return true;
907
908 writeSubFileGroup(t, &root);
909
910 return true;
911}
912
913void DspMakefileGenerator::writeSubFileGroup(QTextStream &t, FolderGroup *folder)
914{
915 t << "# Begin Group \"" << folder->name << "\"" << endl;
916 t << "# PROP Default_Filter \"" << folder->filter << "\"" << endl;
917 QMap<QString, FolderGroup *>::const_iterator folderIt = folder->subFolders.begin();
918 while (folderIt != folder->subFolders.end()) {
919 writeSubFileGroup(t, folderIt.value());
920 ++folderIt;
921 }
922 QMap<QString, QString>::const_iterator it = folder->files.begin();
923 while (it != folder->files.end()) {
924 t << "# Begin Source File" << endl;
925 t << "SOURCE=" << escapeFilePath(it.key()) << endl;
926 writeBuildstepForFile(t, it.key(), it.value());
927 t << "# End Source File" << endl;
928 t << endl;
929 ++it;
930 }
931 t << "# End Group" << endl;
932 t << endl;
933}
934
935bool DspMakefileGenerator::writeBuildstepForFile(QTextStream &t, const QString &file, const QString &listName)
936{
937
938 if (!mergedProjects.count()) {
939 t << writeBuildstepForFileForConfig(file, listName, this);
940 return true;
941 }
942
943 //only add special build rules when needed
944
945 QStringList specialBuilds;
946 int i = 0;
947 for (i = 0; i < mergedProjects.count(); ++i)
948 specialBuilds += writeBuildstepForFileForConfig(file, listName, mergedProjects.at(i));
949
950 // no special build just return
951 if (specialBuilds.join("").isEmpty())
952 return true;
953
954 for (i = 0; i < mergedProjects.count(); ++i) {
955 if (i == 0)
956 t << "!IF";
957 else
958 t << "!ELSEIF";
959 t << " \"$(CFG)\" == \"" << configName(mergedProjects.at(i)) << "\"" << endl;
960 t << endl;
961 t << specialBuilds.at(i);
962 t << endl;
963 }
964
965 t << "!ENDIF" << endl;
966
967 return true;
968}
969
970bool DspMakefileGenerator::writeDspConfig(QTextStream &t, DspMakefileGenerator *config)
971{
972
973 bool isDebug = config->project->isActiveConfig("debug");
974 bool staticLibTarget = config->var("MSVCDSP_DSPTYPE") == "0x0104";
975
976 QString outDir = Option::fixPathToTargetOS(config->project->first("DESTDIR"));
977 while (outDir.endsWith(Option::dir_sep))
978 outDir.chop(1);
979 outDir = config->escapeFilePath(outDir);
980
981 QString intDir = config->project->first("OBJECTS_DIR");
982 while (intDir.endsWith(Option::dir_sep))
983 intDir.chop(1);
984 intDir = config->escapeFilePath(intDir);
985
986 t << "# PROP BASE Use_MFC 0" << endl;
987 t << "# PROP BASE Use_Debug_Libraries " << (isDebug ? "1" : "0") << endl;
988 t << "# PROP BASE Output_Dir " << outDir << endl;
989 t << "# PROP BASE Intermediate_Dir " << intDir << endl;
990 t << "# PROP BASE Target_Dir \"\"" << endl;
991 t << "# PROP Use_MFC 0" << endl;
992 t << "# PROP Use_Debug_Libraries " << (isDebug ? "1" : "0") << endl;
993
994 t << "# PROP Output_Dir " << outDir << endl;
995 t << "# PROP Intermediate_Dir " << intDir << endl;
996 if (config->project->isActiveConfig("dll") || config->project->isActiveConfig("plugin"))
997 t << "# PROP Ignore_Export_Lib 1" << endl;
998 t << "# PROP Target_Dir \"\"" << endl;
999 t << "# ADD CPP " << config->var("MSVCDSP_INCPATH") << " /c /FD " << config->var("QMAKE_CXXFLAGS") << " " << config->var("MSVCDSP_DEFINES") << " " << config->var("PRECOMPILED_FLAGS") << endl;
1000 t << "# ADD MTL /nologo /mktyplib203 /win32 /D " << (isDebug ? "\"_DEBUG\"" : "\"NDEBUG\"") << endl;
1001 t << "# ADD RSC /l 0x409 /d " << (isDebug ? "\"_DEBUG\"" : "\"NDEBUG\"") << endl;
1002 t << "# ADD BSC32 /nologo" << endl;
1003 if (staticLibTarget) {
1004 t << "LIB32=" << config->var("QMAKE_LIB") << endl;
1005 t << "# ADD LIB32 " << config->var("MSVCDSP_TARGET") << " " << config->var("PRECOMPILED_OBJECT") << endl;
1006 } else {
1007 t << "LINK32=" << config->var("QMAKE_LINK") << endl;
1008 t << "# ADD LINK32 " << config->var("MSVCDSP_LFLAGS") << " " << config->var("MSVCDSP_LIBS") << " " << config->var("MSVCDSP_TARGET") << " " << config->var("PRECOMPILED_OBJECT") << endl;
1009 }
1010
1011 if (!config->project->values("MSVCDSP_PRE_LINK").isEmpty())
1012 t << config->project->values("MSVCDSP_PRE_LINK").first();
1013
1014 if (!config->project->values("MSVCDSP_POST_LINK").isEmpty())
1015 t << config->project->values("MSVCDSP_POST_LINK").first();
1016
1017 return true;
1018}
1019
1020QString DspMakefileGenerator::writeBuildstepForFileForConfig(const QString &file, const QString &listName, DspMakefileGenerator *config)
1021{
1022 QString ret;
1023 QTextStream t(&ret);
1024
1025 // exclude from build
1026 if (!config->project->values(listName).contains(file)) {
1027 t << "# PROP Exclude_From_Build 1" << endl;
1028 return ret;
1029 }
1030
1031 if (config->usePCH) {
1032 bool c_file = false;
1033 for (QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) {
1034 if (file.endsWith(*it)) {
1035 c_file = true;
1036 break;
1037 }
1038 }
1039 if(c_file) {
1040 t << "# SUBTRACT CPP /FI" << config->escapeFilePath(config->namePCH) << " /Yu" << config->escapeFilePath(config->namePCH) << " /Fp" << endl;
1041 return ret;
1042 } else if (config->precompH.endsWith(file)) {
1043 // ### dependency list quickly becomes too long for VS to grok...
1044 t << "USERDEP_" << file << "=" << config->valGlue(config->escapeFilePaths(config->findDependencies(config->precompH)), "", "\t", "") << endl;
1045 t << endl;
1046 t << "# Begin Custom Build - Creating precompiled header from " << file << "..." << endl;
1047 t << "InputPath=.\\" << config->escapeFilePath(file) << endl << endl;
1048 t << config->precompPch + ": $(SOURCE) \"$(IntDir)\" \"$(OUTDIR)\"" << endl;
1049 t << "\t" << config->var("QMAKE_CC") << " /TP /W3 /FD /c /Yc /Fp" << config->precompPch << " /Fo" << config->precompObj << " /Fd\"$(IntDir)\\\\\" " << file << " ";
1050 t << config->var("MSVCDSP_INCPATH") << " " << config->var("MSVCDSP_DEFINES") << " " << config->var("QMAKE_CXXFLAGS") << endl;
1051 t << "# End Custom Build" << endl << endl;
1052 return ret;
1053 }
1054 }
1055
1056 QString fileBase = QFileInfo(file).completeBaseName();
1057
1058 bool hasBuiltin = config->hasBuiltinCompiler(file);
1059 BuildStep allSteps;
1060
1061 if (!config->swappedBuildSteps.contains(file)) {
1062 QStringList compilers = config->project->values("QMAKE_EXTRA_COMPILERS");
1063 for (int i = 0; i < compilers.count(); ++i) {
1064 QString compiler = compilers.at(i);
1065 if (config->project->values(compiler + ".input").isEmpty())
1066 continue;
1067 QString input = config->project->values(compiler + ".input").first();
1068 QStringList inputList = config->project->values(input);
1069 if (!inputList.contains(file))
1070 continue;
1071
1072 QStringList compilerCommands = config->project->values(compiler + ".commands");
1073 QStringList compilerOutput = config->project->values(compiler + ".output");
1074 if (compilerCommands.isEmpty() || compilerOutput.isEmpty())
1075 continue;
1076
1077 QStringList compilerName = config->project->values(compiler + ".name");
1078 if (compilerName.isEmpty())
1079 compilerName << compiler;
1080 QStringList compilerDepends = config->project->values(compiler + ".depends");
1081 QString compilerDependsCommand = config->project->values(compiler + ".depend_command").join(" ");
1082 if (!compilerDependsCommand.isEmpty()) {
1083 if(!config->canExecute(compilerDependsCommand))
1084 compilerDependsCommand = QString();
1085 }
1086 QStringList compilerConfig = config->project->values(compiler + ".CONFIG");
1087
1088 if (!config->verifyExtraCompiler(compiler, file))
1089 continue;
1090
1091 bool combineAll = compilerConfig.contains("combine");
1092 if (combineAll && inputList.first() != file)
1093 continue;
1094
1095 QString fileIn("$(InputPath)");
1096
1097 if (combineAll && !inputList.isEmpty()) {
1098 fileIn = inputList.join(" ");
1099 compilerDepends += inputList;
1100 }
1101
1102 QString fileOut = compilerOutput.first();
1103 QString fileOutBase = QFileInfo(fileOut).completeBaseName();
1104 fileOut.replace("${QMAKE_FILE_IN}", fileIn);
1105 fileOut.replace("${QMAKE_FILE_BASE}", fileBase);
1106 fileOut.replace("${QMAKE_FILE_OUT_BASE}", fileOutBase);
1107 fileOut.replace('/', '\\');
1108
1109 BuildStep step;
1110 for (int i2 = 0; i2 < compilerDepends.count(); ++i2) {
1111 QString dependency = compilerDepends.at(i2);
1112 dependency.replace("${QMAKE_FILE_IN}", fileIn);
1113 dependency.replace("${QMAKE_FILE_BASE}", fileBase);
1114 dependency.replace("${QMAKE_FILE_OUT_BASE}", fileOutBase);
1115 dependency.replace('/', '\\');
1116 if (!step.deps.contains(dependency, Qt::CaseInsensitive))
1117 step.deps << dependency;
1118 }
1119 // depends command
1120 if (!compilerDependsCommand.isEmpty() && config->doDepends()) {
1121 char buff[256];
1122 QString dep_cmd = config->replaceExtraCompilerVariables(compilerDependsCommand, file,
1123 fileOut);
1124 dep_cmd = Option::fixPathToLocalOS(dep_cmd, true, false);
1125 if(config->canExecute(dep_cmd)) {
1126 if(FILE *proc = QT_POPEN(dep_cmd.toLatin1().constData(), "r")) {
1127 QString indeps;
1128 while(!feof(proc)) {
1129 int read_in = (int)fread(buff, 1, 255, proc);
1130 if(!read_in)
1131 break;
1132 indeps += QByteArray(buff, read_in);
1133 }
1134 QT_PCLOSE(proc);
1135 if(!indeps.isEmpty())
1136 step.deps += config->fileFixify(indeps.replace('\n', ' ').simplified().split(' '));
1137 }
1138 }
1139 }
1140
1141
1142 QString mappedFile;
1143 if (hasBuiltin) {
1144 mappedFile = fileOut;
1145 fileOut = fileIn;
1146 fileIn = file;
1147 }
1148
1149 step.buildStep += " \\\n\t";
1150 QString command(compilerCommands.join(" "));
1151 // Replace any newlines with proper line-continuance
1152 command.replace("\n", " \\\n\t");
1153 // Might be a macro, and not a valid filename, so the replaceExtraCompilerVariables() would eat it
1154 command.replace("${QMAKE_FILE_IN}", config->escapeFilePath(fileIn));
1155 command.replace("${QMAKE_FILE_BASE}", config->escapeFilePath(fileBase));
1156 command.replace("${QMAKE_FILE_OUT_BASE}", config->escapeFilePath(fileOutBase));
1157 command.replace("${QMAKE_FILE_OUT}", config->escapeFilePath(fileOut));
1158
1159 command = config->replaceExtraCompilerVariables(command, fileIn, fileOut);
1160
1161 step.buildName = compilerName.first();
1162 step.buildStep += command;
1163 step.buildOutputs += fileOut;
1164
1165 if (hasBuiltin) {
1166 step.deps << fileIn;
1167 config->swappedBuildSteps[mappedFile] = step;
1168 } else {
1169 allSteps << step;
1170 }
1171 }
1172 } else {
1173 allSteps << config->swappedBuildSteps.value(file);
1174 }
1175
1176 if (allSteps.buildStep.isEmpty())
1177 return ret;
1178
1179 int i;
1180 QStringList dependencyList;
1181 // remove dependencies that are also output
1182 for (i = 0; i < 1; ++i) {
1183 QStringList buildOutput(allSteps.buildOutputs.at(i));
1184
1185 for (int i2 = 0; i2 < allSteps.deps.count(); ++i2) {
1186 QString dependency = allSteps.deps.at(i2);
1187 if (!buildOutput.contains(dependency) && !dependencyList.contains(dependency))
1188 dependencyList << dependency;
1189 }
1190 }
1191 QString allDependencies = config->valGlue(dependencyList, "", "\t", "");
1192 t << "USERDEP_" << file << "=" << allDependencies << endl;
1193 t << "# PROP Ignore_Default_Tool 1" << endl;
1194 t << "# Begin Custom Build - Running " << allSteps.buildName << " on " << file << endl;
1195 t << "InputPath=" << file << endl;
1196 t << "BuildCmds= " << allSteps.buildStep << endl;
1197 for (i = 0; i < allSteps.buildOutputs.count(); ++i) {
1198 t << config->escapeFilePath(allSteps.buildOutputs.at(i))
1199 << " : $(SOURCE) $(INTDIR) $(OUTDIR)\n\t$(BuildCmds)\n";
1200 }
1201 t << endl;
1202 t << "# End Custom Build" << endl;
1203
1204 return ret;
1205}
1206
1207QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.