1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "winmakefile.h"
5#include "option.h"
6#include "project.h"
7#include "meta.h"
8#include <qtextstream.h>
9#include <qstring.h>
10#include <qhash.h>
11#include <qregularexpression.h>
12#include <qstringlist.h>
13#include <qdir.h>
14#include <stdlib.h>
15
16#include <algorithm>
17
18QT_BEGIN_NAMESPACE
19
20ProString Win32MakefileGenerator::fixLibFlag(const ProString &lib)
21{
22 if (lib.startsWith(sub: "-l")) // Fallback for unresolved -l libs.
23 return escapeFilePath(path: lib.mid(off: 2) + QLatin1String(".lib"));
24 if (lib.startsWith(sub: "-L")) // Lib search path. Needed only by -l above.
25 return QLatin1String("/LIBPATH:")
26 + escapeFilePath(path: Option::fixPathToTargetOS(in: lib.mid(off: 2).toQString(), fix_env: false));
27 return escapeFilePath(path: Option::fixPathToTargetOS(in: lib.toQString(), fix_env: false));
28}
29
30MakefileGenerator::LibFlagType
31Win32MakefileGenerator::parseLibFlag(const ProString &flag, ProString *arg)
32{
33 LibFlagType ret = MakefileGenerator::parseLibFlag(flag, arg);
34 if (ret != LibFlagFile)
35 return ret;
36 // MSVC compatibility. This should be deprecated.
37 if (flag.startsWith(sub: "/LIBPATH:")) {
38 *arg = flag.mid(off: 9);
39 return LibFlagPath;
40 }
41 // These are pure qmake inventions. They *really* should be deprecated.
42 if (flag.startsWith(sub: "/L")) {
43 *arg = flag.mid(off: 2);
44 return LibFlagPath;
45 }
46 if (flag.startsWith(sub: "/l")) {
47 *arg = flag.mid(off: 2);
48 return LibFlagLib;
49 }
50 return LibFlagFile;
51}
52
53class LibrarySearchPath : public QMakeLocalFileName
54{
55public:
56 LibrarySearchPath() = default;
57
58 LibrarySearchPath(const QString &s)
59 : QMakeLocalFileName(s)
60 {
61 }
62
63 LibrarySearchPath(QString &&s, bool isDefault = false)
64 : QMakeLocalFileName(std::move(s)), _default(isDefault)
65 {
66 }
67
68 bool isDefault() const { return _default; }
69
70private:
71 bool _default = false;
72};
73
74bool
75Win32MakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags)
76{
77 ProStringList impexts = project->values(v: "QMAKE_LIB_EXTENSIONS");
78 if (impexts.isEmpty())
79 impexts = project->values(v: "QMAKE_EXTENSION_STATICLIB");
80 QList<LibrarySearchPath> dirs;
81 int libidx = 0;
82 for (const ProString &dlib : project->values(v: "QMAKE_DEFAULT_LIBDIRS"))
83 dirs.append(t: LibrarySearchPath(dlib.toQString(), true));
84 static const char * const lflags[] = { "LIBS", "LIBS_PRIVATE",
85 "QMAKE_LIBS", "QMAKE_LIBS_PRIVATE", nullptr };
86 for (int i = 0; lflags[i]; i++) {
87 ProStringList &l = project->values(v: lflags[i]);
88 for (ProStringList::Iterator it = l.begin(); it != l.end();) {
89 const ProString &opt = *it;
90 ProString arg;
91 LibFlagType type = parseLibFlag(flag: opt, arg: &arg);
92 if (type == LibFlagPath) {
93 const QString argqstr = arg.toQString();
94 auto dit = std::find_if(first: dirs.cbegin(), last: dirs.cend(),
95 pred: [&argqstr](const LibrarySearchPath &p)
96 {
97 return p.real() == argqstr;
98 });
99 int idx = dit == dirs.cend()
100 ? -1
101 : std::distance(first: dirs.cbegin(), last: dit);
102 if (idx >= 0 && idx < libidx) {
103 it = l.erase(pos: it);
104 continue;
105 }
106 const LibrarySearchPath lp(argqstr);
107 dirs.insert(i: libidx++, t: lp);
108 (*it) = "-L" + lp.real();
109 } else if (type == LibFlagLib) {
110 QString lib = arg.toQString();
111 ProString verovr =
112 project->first(variableName: ProKey("QMAKE_" + lib.toUpper() + "_VERSION_OVERRIDE"));
113 for (auto dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) {
114 QString cand = (*dir_it).real() + Option::dir_sep + lib;
115 if (linkPrl && processPrlFile(cand, baseOnly: true)) {
116 (*it) = cand;
117 goto found;
118 }
119 QString libBase = (*dir_it).local() + '/' + lib + verovr;
120 for (ProStringList::ConstIterator extit = impexts.cbegin();
121 extit != impexts.cend(); ++extit) {
122 if (exists(file: libBase + '.' + *extit)) {
123 *it = (dir_it->isDefault() ? lib : cand)
124 + verovr + '.' + *extit;
125 goto found;
126 }
127 }
128 }
129 // We assume if it never finds it that it's correct
130 found: ;
131 } else if (linkPrl && type == LibFlagFile) {
132 QString lib = opt.toQString();
133 if (fileInfo(file: lib).isAbsolute()) {
134 if (processPrlFile(lib, baseOnly: false))
135 (*it) = lib;
136 } else {
137 for (auto dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) {
138 QString cand = (*dir_it).real() + Option::dir_sep + lib;
139 if (processPrlFile(cand, baseOnly: false)) {
140 (*it) = cand;
141 break;
142 }
143 }
144 }
145 }
146
147 ProStringList &prl_libs = project->values(v: "QMAKE_CURRENT_PRL_LIBS");
148 for (int prl = 0; prl < prl_libs.size(); ++prl)
149 it = l.insert(before: ++it, t: prl_libs.at(i: prl));
150 prl_libs.clear();
151 ++it;
152 }
153 if (mergeLflags) {
154 ProStringList lopts;
155 for (int lit = 0; lit < l.size(); ++lit) {
156 ProString opt = l.at(i: lit);
157 if (opt.startsWith(sub: QLatin1String("-L"))) {
158 if (!lopts.contains(str: opt))
159 lopts.append(t: opt);
160 } else {
161 // Make sure we keep the dependency order of libraries
162 lopts.removeAll(str: opt);
163 lopts.append(t: opt);
164 }
165 }
166 l = lopts;
167 }
168 }
169 return true;
170}
171
172bool Win32MakefileGenerator::processPrlFileBase(QString &origFile, QStringView origName,
173 QStringView fixedBase, int slashOff)
174{
175 if (MakefileGenerator::processPrlFileBase(origFile, origName, fixedBase, slashOff))
176 return true;
177 for (int off = fixedBase.size(); off > slashOff; off--) {
178 if (!fixedBase.at(n: off - 1).isDigit()) {
179 if (off != fixedBase.size()) {
180 return MakefileGenerator::processPrlFileBase(
181 origFile, origName, fixedBase: fixedBase.left(n: off), slashOff);
182 }
183 break;
184 }
185 }
186 return false;
187}
188
189void Win32MakefileGenerator::processVars()
190{
191 if (project->first(variableName: "TEMPLATE").endsWith(sub: "aux"))
192 return;
193
194 project->values(v: "PRL_TARGET") =
195 project->values(v: "QMAKE_ORIG_TARGET") = project->values(v: "TARGET");
196 if (project->isEmpty(v: "QMAKE_PROJECT_NAME"))
197 project->values(v: "QMAKE_PROJECT_NAME") = project->values(v: "QMAKE_ORIG_TARGET");
198 else if (project->first(variableName: "TEMPLATE").startsWith(sub: "vc"))
199 project->values(v: "MAKEFILE") = project->values(v: "QMAKE_PROJECT_NAME");
200
201 project->values(v: "QMAKE_INCDIR") += project->values(v: "QMAKE_INCDIR_POST");
202 project->values(v: "QMAKE_LIBDIR") += project->values(v: "QMAKE_LIBDIR_POST");
203
204 if (!project->values(v: "QMAKE_INCDIR").isEmpty())
205 project->values(v: "INCLUDEPATH") += project->values(v: "QMAKE_INCDIR");
206
207 if (!project->values(v: "VERSION").isEmpty()) {
208 QStringList l = project->first(variableName: "VERSION").toQString().split(sep: '.');
209 if (l.size() > 0)
210 project->values(v: "VER_MAJ").append(t: l[0]);
211 if (l.size() > 1)
212 project->values(v: "VER_MIN").append(t: l[1]);
213 }
214
215 // TARGET_VERSION_EXT will be used to add a version number onto the target name
216 if (!project->isActiveConfig(config: "skip_target_version_ext")
217 && project->values(v: "TARGET_VERSION_EXT").isEmpty()
218 && !project->values(v: "VER_MAJ").isEmpty())
219 project->values(v: "TARGET_VERSION_EXT").append(t: project->first(variableName: "VER_MAJ"));
220
221 fixTargetExt();
222 processRcFileVar();
223
224 ProStringList libs;
225 ProStringList &libDir = project->values(v: "QMAKE_LIBDIR");
226 for (ProStringList::Iterator libDir_it = libDir.begin(); libDir_it != libDir.end(); ++libDir_it) {
227 QString lib = (*libDir_it).toQString();
228 if (!lib.isEmpty()) {
229 if (lib.endsWith(c: '\\'))
230 lib.chop(n: 1);
231 libs << QLatin1String("-L") + lib;
232 }
233 }
234 ProStringList &qmklibs = project->values(v: "LIBS");
235 qmklibs = libs + qmklibs;
236
237 if (project->values(v: "TEMPLATE").contains(str: "app")) {
238 project->values(v: "QMAKE_CFLAGS") += project->values(v: "QMAKE_CFLAGS_APP");
239 project->values(v: "QMAKE_CXXFLAGS") += project->values(v: "QMAKE_CXXFLAGS_APP");
240 project->values(v: "QMAKE_LFLAGS") += project->values(v: "QMAKE_LFLAGS_APP");
241 } else if (project->values(v: "TEMPLATE").contains(str: "lib") && project->isActiveConfig(config: "dll")) {
242 if(!project->isActiveConfig(config: "plugin") || !project->isActiveConfig(config: "plugin_no_share_shlib_cflags")) {
243 project->values(v: "QMAKE_CFLAGS") += project->values(v: "QMAKE_CFLAGS_SHLIB");
244 project->values(v: "QMAKE_CXXFLAGS") += project->values(v: "QMAKE_CXXFLAGS_SHLIB");
245 }
246 if (project->isActiveConfig(config: "plugin")) {
247 project->values(v: "QMAKE_CFLAGS") += project->values(v: "QMAKE_CFLAGS_PLUGIN");
248 project->values(v: "QMAKE_CXXFLAGS") += project->values(v: "QMAKE_CXXFLAGS_PLUGIN");
249 project->values(v: "QMAKE_LFLAGS") += project->values(v: "QMAKE_LFLAGS_PLUGIN");
250 } else {
251 project->values(v: "QMAKE_LFLAGS") += project->values(v: "QMAKE_LFLAGS_SHLIB");
252 }
253 }
254}
255
256void Win32MakefileGenerator::fixTargetExt()
257{
258 if (!project->values(v: "QMAKE_APP_FLAG").isEmpty()) {
259 project->values(v: "TARGET_EXT").append(t: ".exe");
260 } else if (project->isActiveConfig(config: "shared")) {
261 project->values(v: "LIB_TARGET").prepend(t: project->first(variableName: "QMAKE_PREFIX_STATICLIB")
262 + project->first(variableName: "TARGET") + project->first(variableName: "TARGET_VERSION_EXT")
263 + '.' + project->first(variableName: "QMAKE_EXTENSION_STATICLIB"));
264 project->values(v: "TARGET_EXT").append(t: project->first(variableName: "TARGET_VERSION_EXT") + "."
265 + project->first(variableName: "QMAKE_EXTENSION_SHLIB"));
266 project->values(v: "TARGET").first() = project->first(variableName: "QMAKE_PREFIX_SHLIB") + project->first(variableName: "TARGET");
267 } else {
268 project->values(v: "TARGET_EXT").append(t: "." + project->first(variableName: "QMAKE_EXTENSION_STATICLIB"));
269 project->values(v: "TARGET").first() = project->first(variableName: "QMAKE_PREFIX_STATICLIB") + project->first(variableName: "TARGET");
270 project->values(v: "LIB_TARGET").prepend(t: project->first(variableName: "TARGET") + project->first(variableName: "TARGET_EXT")); // for the .prl only
271 }
272}
273
274void Win32MakefileGenerator::processRcFileVar()
275{
276 if (Option::qmake_mode == Option::QMAKE_GENERATE_NOTHING)
277 return;
278
279 const QString manifestFile = project->first(variableName: "QMAKE_MANIFEST").toQString();
280 if (((!project->values(v: "VERSION").isEmpty() || !project->values(v: "RC_ICONS").isEmpty() || !manifestFile.isEmpty())
281 && project->values(v: "RC_FILE").isEmpty()
282 && project->values(v: "RES_FILE").isEmpty()
283 && !project->isActiveConfig(config: "no_generated_target_info")
284 && (project->isActiveConfig(config: "shared") || !project->values(v: "QMAKE_APP_FLAG").isEmpty()))
285 || !project->values(v: "QMAKE_WRITE_DEFAULT_RC").isEmpty()){
286
287 QByteArray rcString;
288 QTextStream ts(&rcString, QFile::WriteOnly);
289
290 QStringList vers = project->first(variableName: "VERSION").toQString().split(sep: ".", behavior: Qt::SkipEmptyParts);
291 for (int i = vers.size(); i < 4; i++)
292 vers += "0";
293 QString versionString = vers.join(sep: '.');
294
295 QStringList rcIcons;
296 const auto icons = project->values(v: "RC_ICONS");
297 rcIcons.reserve(asize: icons.size());
298 for (const ProString &icon : icons)
299 rcIcons.append(t: fileFixify(file: icon.toQString(), fix: FileFixifyAbsolute));
300
301 QString companyName;
302 if (!project->values(v: "QMAKE_TARGET_COMPANY").isEmpty())
303 companyName = project->values(v: "QMAKE_TARGET_COMPANY").join(sep: ' ');
304
305 QString description;
306 if (!project->values(v: "QMAKE_TARGET_DESCRIPTION").isEmpty())
307 description = project->values(v: "QMAKE_TARGET_DESCRIPTION").join(sep: ' ');
308
309 QString copyright;
310 if (!project->values(v: "QMAKE_TARGET_COPYRIGHT").isEmpty())
311 copyright = project->values(v: "QMAKE_TARGET_COPYRIGHT").join(sep: ' ');
312
313 QString productName;
314 if (!project->values(v: "QMAKE_TARGET_PRODUCT").isEmpty())
315 productName = project->values(v: "QMAKE_TARGET_PRODUCT").join(sep: ' ');
316 else
317 productName = project->first(variableName: "TARGET").toQString();
318
319 QString originalName;
320 if (!project->values(v: "QMAKE_TARGET_ORIGINAL_FILENAME").isEmpty())
321 originalName = project->values(v: "QMAKE_TARGET_ORIGINAL_FILENAME").join(sep: ' ');
322 else
323 originalName = project->first(variableName: "TARGET") + project->first(variableName: "TARGET_EXT");
324
325 QString internalName;
326 if (!project->values(v: "QMAKE_TARGET_INTERNALNAME").isEmpty())
327 internalName = project->values(v: "QMAKE_TARGET_INTERNALNAME").join(sep: ' ');
328
329 QString comments;
330 if (!project->values(v: "QMAKE_TARGET_COMMENTS").isEmpty())
331 comments = project->values(v: "QMAKE_TARGET_COMMENTS").join(sep: ' ');
332
333 QString trademarks;
334 if (!project->values(v: "QMAKE_TARGET_TRADEMARKS").isEmpty())
335 trademarks = project->values(v: "QMAKE_TARGET_TRADEMARKS").join(sep: ' ');
336
337 int rcLang = project->intValue(v: "RC_LANG", defaultValue: 1033); // default: English(USA)
338 int rcCodePage = project->intValue(v: "RC_CODEPAGE", defaultValue: 1200); // default: Unicode
339
340 ts << "#include <windows.h>\n";
341 ts << Qt::endl;
342 if (!rcIcons.isEmpty()) {
343 for (int i = 0; i < rcIcons.size(); ++i)
344 ts << QString("IDI_ICON%1\tICON\t%2").arg(a: i + 1).arg(a: cQuoted(str: rcIcons[i])) << Qt::endl;
345 ts << Qt::endl;
346 }
347 if (!manifestFile.isEmpty()) {
348 QString manifestResourceId;
349 if (project->first(variableName: "TEMPLATE") == "lib")
350 manifestResourceId = QStringLiteral("ISOLATIONAWARE_MANIFEST_RESOURCE_ID");
351 else
352 manifestResourceId = QStringLiteral("CREATEPROCESS_MANIFEST_RESOURCE_ID");
353 ts << manifestResourceId << " RT_MANIFEST \"" << manifestFile << "\"\n";
354 }
355 ts << "VS_VERSION_INFO VERSIONINFO\n";
356 ts << "\tFILEVERSION " << QString(versionString).replace(before: ".", after: ",") << Qt::endl;
357 ts << "\tPRODUCTVERSION " << QString(versionString).replace(before: ".", after: ",") << Qt::endl;
358 ts << "\tFILEFLAGSMASK 0x3fL\n";
359 ts << "#ifdef _DEBUG\n";
360 ts << "\tFILEFLAGS VS_FF_DEBUG\n";
361 ts << "#else\n";
362 ts << "\tFILEFLAGS 0x0L\n";
363 ts << "#endif\n";
364 ts << "\tFILEOS VOS_NT_WINDOWS32\n";
365 if (project->isActiveConfig(config: "shared"))
366 ts << "\tFILETYPE VFT_DLL\n";
367 else
368 ts << "\tFILETYPE VFT_APP\n";
369 ts << "\tFILESUBTYPE VFT2_UNKNOWN\n";
370 ts << "\tBEGIN\n";
371 ts << "\t\tBLOCK \"StringFileInfo\"\n";
372 ts << "\t\tBEGIN\n";
373 ts << "\t\t\tBLOCK \""
374 << QString("%1%2").arg(a: rcLang, fieldWidth: 4, base: 16, fillChar: QLatin1Char('0')).arg(a: rcCodePage, fieldWidth: 4, base: 16, fillChar: QLatin1Char('0'))
375 << "\"\n";
376 ts << "\t\t\tBEGIN\n";
377 ts << "\t\t\t\tVALUE \"CompanyName\", \"" << companyName << "\\0\"\n";
378 ts << "\t\t\t\tVALUE \"FileDescription\", \"" << description << "\\0\"\n";
379 ts << "\t\t\t\tVALUE \"FileVersion\", \"" << versionString << "\\0\"\n";
380 ts << "\t\t\t\tVALUE \"LegalCopyright\", \"" << copyright << "\\0\"\n";
381 ts << "\t\t\t\tVALUE \"OriginalFilename\", \"" << originalName << "\\0\"\n";
382 ts << "\t\t\t\tVALUE \"ProductName\", \"" << productName << "\\0\"\n";
383 ts << "\t\t\t\tVALUE \"ProductVersion\", \"" << versionString << "\\0\"\n";
384 ts << "\t\t\t\tVALUE \"InternalName\", \"" << internalName << "\\0\"\n";
385 ts << "\t\t\t\tVALUE \"Comments\", \"" << comments << "\\0\"\n";
386 ts << "\t\t\t\tVALUE \"LegalTrademarks\", \"" << trademarks << "\\0\"\n";
387 ts << "\t\t\tEND\n";
388 ts << "\t\tEND\n";
389 ts << "\t\tBLOCK \"VarFileInfo\"\n";
390 ts << "\t\tBEGIN\n";
391 ts << "\t\t\tVALUE \"Translation\", "
392 << QString("0x%1").arg(a: rcLang, fieldWidth: 4, base: 16, fillChar: QLatin1Char('0'))
393 << ", " << QString("%1").arg(a: rcCodePage, fieldWidth: 4) << Qt::endl;
394 ts << "\t\tEND\n";
395 ts << "\tEND\n";
396 ts << "/* End of Version info */\n";
397 ts << Qt::endl;
398
399 ts.flush();
400
401
402 QString rcFilename = project->first(variableName: "OUT_PWD")
403 + "/"
404 + project->first(variableName: "TARGET")
405 + "_resource"
406 + ".rc";
407 QFile rcFile(QDir::cleanPath(path: rcFilename));
408
409 bool writeRcFile = true;
410 if (rcFile.exists() && rcFile.open(flags: QFile::ReadOnly)) {
411 writeRcFile = rcFile.readAll() != rcString;
412 rcFile.close();
413 }
414 if (writeRcFile) {
415 bool ok;
416 ok = rcFile.open(flags: QFile::WriteOnly);
417 if (!ok) {
418 // The file can't be opened... try creating the containing
419 // directory first (needed for clean shadow builds)
420 QDir().mkpath(dirPath: QFileInfo(rcFile).path());
421 ok = rcFile.open(flags: QFile::WriteOnly);
422 }
423 if (!ok) {
424 ::fprintf(stderr, format: "Cannot open for writing: %s", rcFile.fileName().toLatin1().constData());
425 ::exit(status: 1);
426 }
427 rcFile.write(data: rcString);
428 rcFile.close();
429 }
430 if (project->values(v: "QMAKE_WRITE_DEFAULT_RC").isEmpty())
431 project->values(v: "RC_FILE").insert(i: 0, t: rcFile.fileName());
432 }
433 if (!project->values(v: "RC_FILE").isEmpty()) {
434 if (!project->values(v: "RES_FILE").isEmpty()) {
435 fprintf(stderr, format: "Both rc and res file specified.\n");
436 fprintf(stderr, format: "Please specify one of them, not both.");
437 exit(status: 1);
438 }
439 QString resFile = project->first(variableName: "RC_FILE").toQString();
440
441 // if this is a shadow build then use the absolute path of the rc file
442 if (Option::output_dir != qmake_getpwd()) {
443 QFileInfo fi(resFile);
444 project->values(v: "RC_FILE").first() = fi.absoluteFilePath();
445 }
446
447 resFile.replace(before: QLatin1String(".rc"), after: Option::res_ext);
448 project->values(v: "RES_FILE").prepend(t: fileInfo(file: resFile).fileName());
449 QString resDestDir;
450 if (project->isActiveConfig(config: "staticlib"))
451 resDestDir = project->first(variableName: "DESTDIR").toQString();
452 else
453 resDestDir = project->first(variableName: "OBJECTS_DIR").toQString();
454 if (!resDestDir.isEmpty()) {
455 resDestDir.append(s: Option::dir_sep);
456 project->values(v: "RES_FILE").first().prepend(other: resDestDir);
457 }
458 project->values(v: "RES_FILE").first() = Option::fixPathToTargetOS(
459 in: project->first(variableName: "RES_FILE").toQString(), fix_env: false);
460 project->values(v: "POST_TARGETDEPS") += project->values(v: "RES_FILE");
461 project->values(v: "CLEAN_FILES") += project->values(v: "RES_FILE");
462 }
463}
464
465void Win32MakefileGenerator::writeCleanParts(QTextStream &t)
466{
467 t << "clean: compiler_clean " << depVar(var: "CLEAN_DEPS");
468 {
469 const char *clean_targets[] = { "OBJECTS", "QMAKE_CLEAN", "CLEAN_FILES", nullptr };
470 for(int i = 0; clean_targets[i]; ++i) {
471 const ProStringList &list = project->values(v: clean_targets[i]);
472 const QString del_statement("-$(DEL_FILE)");
473 if(project->isActiveConfig(config: "no_delete_multiple_files")) {
474 for (ProStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
475 t << "\n\t" << del_statement
476 << ' ' << escapeFilePath(path: Option::fixPathToTargetOS(in: (*it).toQString()));
477 } else {
478 QString files, file;
479 const int commandlineLimit = 2047; // NT limit, expanded
480 for (ProStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
481 file = ' ' + escapeFilePath(path: Option::fixPathToTargetOS(in: (*it).toQString()));
482 if(del_statement.size() + files.size() +
483 qMax(a: fixEnvVariables(x: file).size(), b: file.size()) > commandlineLimit) {
484 t << "\n\t" << del_statement << files;
485 files.clear();
486 }
487 files += file;
488 }
489 if(!files.isEmpty())
490 t << "\n\t" << del_statement << files;
491 }
492 }
493 }
494 t << Qt::endl << Qt::endl;
495
496 t << "distclean: clean " << depVar(var: "DISTCLEAN_DEPS");
497 {
498 const char *clean_targets[] = { "QMAKE_DISTCLEAN", nullptr };
499 for(int i = 0; clean_targets[i]; ++i) {
500 const ProStringList &list = project->values(v: clean_targets[i]);
501 const QString del_statement("-$(DEL_FILE)");
502 if(project->isActiveConfig(config: "no_delete_multiple_files")) {
503 for (ProStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
504 t << "\n\t" << del_statement << " "
505 << escapeFilePath(path: Option::fixPathToTargetOS(in: (*it).toQString()));
506 } else {
507 QString files, file;
508 const int commandlineLimit = 2047; // NT limit, expanded
509 for (ProStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
510 file = " " + escapeFilePath(path: Option::fixPathToTargetOS(in: (*it).toQString()));
511 if(del_statement.size() + files.size() +
512 qMax(a: fixEnvVariables(x: file).size(), b: file.size()) > commandlineLimit) {
513 t << "\n\t" << del_statement << files;
514 files.clear();
515 }
516 files += file;
517 }
518 if(!files.isEmpty())
519 t << "\n\t" << del_statement << files;
520 }
521 }
522 }
523 t << "\n\t-$(DEL_FILE) $(DESTDIR_TARGET)\n";
524 {
525 QString ofile = fileFixify(file: Option::output.fileName());
526 if(!ofile.isEmpty())
527 t << "\t-$(DEL_FILE) " << escapeFilePath(path: ofile) << Qt::endl;
528 }
529 t << Qt::endl;
530}
531
532void Win32MakefileGenerator::writeIncPart(QTextStream &t)
533{
534 t << "INCPATH = ";
535
536 const ProStringList &incs = project->values(v: "INCLUDEPATH");
537 for(int i = 0; i < incs.size(); ++i) {
538 QString inc = incs.at(i).toQString();
539 inc.replace(re: QRegularExpression("\\\\$"), after: "");
540 if(!inc.isEmpty())
541 t << "-I" << escapeFilePath(path: inc) << ' ';
542 }
543 t << Qt::endl;
544}
545
546void Win32MakefileGenerator::writeStandardParts(QTextStream &t)
547{
548 writeExportedVariables(t);
549
550 t << "####### Compiler, tools and options\n\n";
551 t << "CC = " << var(var: "QMAKE_CC") << Qt::endl;
552 t << "CXX = " << var(var: "QMAKE_CXX") << Qt::endl;
553 t << "DEFINES = "
554 << varGlue(var: "PRL_EXPORT_DEFINES",before: "-D",glue: " -D",after: " ")
555 << varGlue(var: "DEFINES",before: "-D",glue: " -D",after: "") << Qt::endl;
556 t << "CFLAGS = " << var(var: "QMAKE_CFLAGS") << " $(DEFINES)\n";
557 t << "CXXFLAGS = " << var(var: "QMAKE_CXXFLAGS") << " $(DEFINES)\n";
558
559 writeIncPart(t);
560 writeLibsPart(t);
561 writeDefaultVariables(t);
562 t << Qt::endl;
563
564 t << "####### Output directory\n\n";
565 if(!project->values(v: "OBJECTS_DIR").isEmpty())
566 t << "OBJECTS_DIR = " << escapeFilePath(path: var(var: "OBJECTS_DIR").remove(re: QRegularExpression("\\\\$"))) << Qt::endl;
567 else
568 t << "OBJECTS_DIR = . \n";
569 t << Qt::endl;
570
571 t << "####### Files\n\n";
572 t << "SOURCES = " << valList(varList: escapeFilePaths(paths: project->values(v: "SOURCES")))
573 << " " << valList(varList: escapeFilePaths(paths: project->values(v: "GENERATED_SOURCES"))) << Qt::endl;
574
575 // do this here so we can set DEST_TARGET to be the complete path to the final target if it is needed.
576 QString orgDestDir = var(var: "DESTDIR");
577 QString destDir = Option::fixPathToTargetOS(in: orgDestDir, fix_env: false);
578 if (!destDir.isEmpty() && (orgDestDir.endsWith(c: '/') || orgDestDir.endsWith(s: Option::dir_sep)))
579 destDir += Option::dir_sep;
580 QString target = QString(project->first(variableName: "TARGET")+project->first(variableName: "TARGET_EXT"));
581 project->values(v: "DEST_TARGET").prepend(t: destDir + target);
582
583 writeObjectsPart(t);
584
585 writeExtraCompilerVariables(t);
586 writeExtraVariables(t);
587
588 t << "DIST = " << fileVarList(var: "DISTFILES") << ' '
589 << fileVarList(var: "HEADERS") << ' ' << fileVarList(var: "SOURCES") << Qt::endl;
590 t << "QMAKE_TARGET = " << fileVar(var: "QMAKE_ORIG_TARGET") << Qt::endl; // unused
591 // The comment is important to maintain variable compatibility with Unix
592 // Makefiles, while not interpreting a trailing-slash as a linebreak
593 t << "DESTDIR = " << escapeFilePath(path: destDir) << " #avoid trailing-slash linebreak\n";
594 t << "TARGET = " << escapeFilePath(path: target) << Qt::endl;
595 t << "DESTDIR_TARGET = " << fileVar(var: "DEST_TARGET") << Qt::endl;
596 t << Qt::endl;
597
598 writeImplicitRulesPart(t);
599
600 t << "####### Build rules\n\n";
601 writeBuildRulesPart(t);
602
603 if (project->first(variableName: "TEMPLATE") != "aux") {
604 if (project->isActiveConfig(config: "shared") && !project->values(v: "DLLDESTDIR").isEmpty()) {
605 const ProStringList &dlldirs = project->values(v: "DLLDESTDIR");
606 for (ProStringList::ConstIterator dlldir = dlldirs.begin(); dlldir != dlldirs.end(); ++dlldir) {
607 t << "\t-$(COPY_FILE) $(DESTDIR_TARGET) "
608 << escapeFilePath(path: Option::fixPathToTargetOS(in: (*dlldir).toQString(), fix_env: false)) << Qt::endl;
609 }
610 }
611 t << Qt::endl;
612
613 writeRcFilePart(t);
614 }
615
616 writeMakeQmake(t);
617
618 QStringList dist_files = fileFixify(files: Option::mkfile::project_files);
619 if(!project->isEmpty(v: "QMAKE_INTERNAL_INCLUDED_FILES"))
620 dist_files += project->values(v: "QMAKE_INTERNAL_INCLUDED_FILES").toQStringList();
621 if(!project->isEmpty(v: "TRANSLATIONS"))
622 dist_files << var(var: "TRANSLATIONS");
623 if(!project->isEmpty(v: "FORMS")) {
624 const ProStringList &forms = project->values(v: "FORMS");
625 for (ProStringList::ConstIterator formit = forms.begin(); formit != forms.end(); ++formit) {
626 QString ui_h = fileFixify(file: (*formit) + Option::h_ext.first());
627 if(exists(file: ui_h))
628 dist_files << ui_h;
629 }
630 }
631 t << "dist:\n\t"
632 << "$(ZIP) " << var(var: "QMAKE_ORIG_TARGET") << ".zip $(SOURCES) $(DIST) "
633 << escapeFilePaths(paths: dist_files).join(sep: ' ') << ' ' << fileVar(var: "TRANSLATIONS") << ' ';
634 if(!project->isEmpty(v: "QMAKE_EXTRA_COMPILERS")) {
635 const ProStringList &quc = project->values(v: "QMAKE_EXTRA_COMPILERS");
636 for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
637 const ProStringList &inputs = project->values(v: ProKey(*it + ".input"));
638 for (ProStringList::ConstIterator input = inputs.begin(); input != inputs.end(); ++input) {
639 const ProStringList &val = project->values(v: (*input).toKey());
640 t << escapeFilePaths(paths: val).join(sep: ' ') << ' ';
641 }
642 }
643 }
644 t << Qt::endl << Qt::endl;
645
646 writeCleanParts(t);
647 writeExtraTargets(t);
648 writeExtraCompilerTargets(t);
649 t << Qt::endl << Qt::endl;
650}
651
652void Win32MakefileGenerator::writeLibsPart(QTextStream &t)
653{
654 if(project->isActiveConfig(config: "staticlib") && project->first(variableName: "TEMPLATE") == "lib") {
655 t << "LIBAPP = " << var(var: "QMAKE_LIB") << Qt::endl;
656 t << "LIBFLAGS = " << var(var: "QMAKE_LIBFLAGS") << Qt::endl;
657 } else {
658 t << "LINKER = " << var(var: "QMAKE_LINK") << Qt::endl;
659 t << "LFLAGS = " << var(var: "QMAKE_LFLAGS") << Qt::endl;
660 t << "LIBS = " << fixLibFlags(var: "LIBS").join(sep: ' ') << ' '
661 << fixLibFlags(var: "LIBS_PRIVATE").join(sep: ' ') << ' '
662 << fixLibFlags(var: "QMAKE_LIBS").join(sep: ' ') << ' '
663 << fixLibFlags(var: "QMAKE_LIBS_PRIVATE").join(sep: ' ') << Qt::endl;
664 }
665}
666
667void Win32MakefileGenerator::writeObjectsPart(QTextStream &t)
668{
669 // Used in both deps and commands.
670 t << "OBJECTS = " << valList(varList: escapeDependencyPaths(paths: project->values(v: "OBJECTS"))) << Qt::endl;
671}
672
673void Win32MakefileGenerator::writeImplicitRulesPart(QTextStream &)
674{
675}
676
677void Win32MakefileGenerator::writeBuildRulesPart(QTextStream &)
678{
679}
680
681void Win32MakefileGenerator::writeRcFilePart(QTextStream &t)
682{
683 if(!project->values(v: "RC_FILE").isEmpty()) {
684 const ProString res_file = project->first(variableName: "RES_FILE");
685 const QString rc_file = fileFixify(file: project->first(variableName: "RC_FILE").toQString());
686
687 const ProStringList rcIncPaths = project->values(v: "RC_INCLUDEPATH");
688 QString incPathStr;
689 for (int i = 0; i < rcIncPaths.size(); ++i) {
690 const ProString &path = rcIncPaths.at(i);
691 if (path.isEmpty())
692 continue;
693 incPathStr += QStringLiteral(" /i ");
694 incPathStr += escapeFilePath(path);
695 }
696
697 addSourceFile(rc_file, seek: QMakeSourceFileInfo::SEEK_DEPS);
698 const QStringList rcDeps = QStringList(rc_file) << dependencies(file: rc_file);
699
700 // The resource tool may use defines. This might be the same defines passed in as the
701 // compiler, since you may use these defines in the .rc file itself.
702 // As the escape syntax for the command line defines for RC is different from that for CL,
703 // we might have to set specific defines for RC.
704 ProString defines = varGlue(var: "RC_DEFINES", before: " -D", glue: " -D", after: "");
705 if (defines.isEmpty())
706 defines = ProString(" $(DEFINES)");
707
708 // Also, we need to add the _DEBUG define manually since the compiler defines this symbol
709 // by itself, and we use it in the automatically created rc file when VERSION is defined
710 // in the .pro file.
711 t << escapeDependencyPath(path: res_file) << ": "
712 << escapeDependencyPaths(paths: rcDeps).join(sep: ' ') << "\n\t"
713 << var(var: "QMAKE_RC") << (project->isActiveConfig(config: "debug") ? " -D_DEBUG" : "")
714 << defines << incPathStr << " -fo " << escapeFilePath(path: res_file)
715 << ' ' << escapeFilePath(path: rc_file);
716 t << Qt::endl << Qt::endl;
717 }
718}
719
720QString Win32MakefileGenerator::defaultInstall(const QString &t)
721{
722 if((t != "target" && t != "dlltarget") ||
723 (t == "dlltarget" && (project->first(variableName: "TEMPLATE") != "lib" || !project->isActiveConfig(config: "shared"))) ||
724 project->first(variableName: "TEMPLATE") == "subdirs" || project->first(variableName: "TEMPLATE") == "aux")
725 return QString();
726
727 const QString root = installRoot();
728 ProStringList &uninst = project->values(v: ProKey(t + ".uninstall"));
729 QString ret;
730 QString targetdir = fileFixify(file: project->first(variableName: ProKey(t + ".path")).toQString(), fix: FileFixifyAbsolute);
731 if(targetdir.right(n: 1) != Option::dir_sep)
732 targetdir += Option::dir_sep;
733
734 const ProStringList &targets = project->values(v: ProKey(t + ".targets"));
735 for (int i = 0; i < targets.size(); ++i) {
736 QString src = targets.at(i).toQString(),
737 dst = escapeFilePath(path: filePrefixRoot(root, targetdir + src.section(asep: '/', astart: -1)));
738 if (!ret.isEmpty())
739 ret += "\n\t";
740 ret += "$(QINSTALL) " + escapeFilePath(path: Option::fixPathToTargetOS(in: src, fix_env: false)) + ' ' + dst;
741 if (!uninst.isEmpty())
742 uninst.append(t: "\n\t");
743 uninst.append(t: "-$(DEL_FILE) " + dst);
744 }
745
746 if(t == "target" && project->first(variableName: "TEMPLATE") == "lib") {
747 if(project->isActiveConfig(config: "create_prl") && !project->isActiveConfig(config: "no_install_prl") &&
748 !project->isEmpty(v: "QMAKE_INTERNAL_PRL_FILE")) {
749 QString dst_prl = Option::fixPathToTargetOS(in: project->first(variableName: "QMAKE_INTERNAL_PRL_FILE").toQString());
750 int slsh = dst_prl.lastIndexOf(s: Option::dir_sep);
751 if(slsh != -1)
752 dst_prl = dst_prl.right(n: dst_prl.size() - slsh - 1);
753 dst_prl = filePrefixRoot(root, targetdir + dst_prl);
754 if (!ret.isEmpty())
755 ret += "\n\t";
756 ret += installMetaFile(replace_rule: ProKey("QMAKE_PRL_INSTALL_REPLACE"), src: project->first(variableName: "QMAKE_INTERNAL_PRL_FILE").toQString(), dst: dst_prl);
757 if(!uninst.isEmpty())
758 uninst.append(t: "\n\t");
759 uninst.append(t: "-$(DEL_FILE) " + escapeFilePath(path: dst_prl));
760 }
761 if(project->isActiveConfig(config: "create_pc")) {
762 QString dst_pc = pkgConfigFileName(fixify: false);
763 if (!dst_pc.isEmpty()) {
764 dst_pc = filePrefixRoot(root, targetdir + dst_pc);
765 const QString dst_pc_dir = Option::fixPathToTargetOS(in: fileInfo(file: dst_pc).path(), fix_env: false);
766 if (!dst_pc_dir.isEmpty()) {
767 if (!ret.isEmpty())
768 ret += "\n\t";
769 ret += mkdir_p_asstring(dir: dst_pc_dir, escape: true);
770 }
771 if(!ret.isEmpty())
772 ret += "\n\t";
773 ret += installMetaFile(replace_rule: ProKey("QMAKE_PKGCONFIG_INSTALL_REPLACE"), src: pkgConfigFileName(fixify: true), dst: dst_pc);
774 if(!uninst.isEmpty())
775 uninst.append(t: "\n\t");
776 uninst.append(t: "-$(DEL_FILE) " + escapeFilePath(path: dst_pc));
777 }
778 }
779 if(project->isActiveConfig(config: "shared") && !project->isActiveConfig(config: "plugin")) {
780 ProString lib_target = project->first(variableName: "LIB_TARGET");
781 QString src_targ = escapeFilePath(
782 path: (project->isEmpty(v: "DESTDIR") ? QString("$(DESTDIR)") : project->first(variableName: "DESTDIR"))
783 + lib_target);
784 QString dst_targ = escapeFilePath(
785 path: filePrefixRoot(root, fileFixify(file: targetdir + lib_target, fix: FileFixifyAbsolute)));
786 if(!ret.isEmpty())
787 ret += "\n\t";
788 ret += QString("-$(INSTALL_FILE) ") + src_targ + ' ' + dst_targ;
789 if(!uninst.isEmpty())
790 uninst.append(t: "\n\t");
791 uninst.append(t: "-$(DEL_FILE) " + dst_targ);
792 }
793 }
794
795 if (t == "dlltarget" || project->values(v: ProKey(t + ".CONFIG")).indexOf(t: "no_dll") == -1) {
796 QString src_targ = "$(DESTDIR_TARGET)";
797 QString dst_targ = escapeFilePath(
798 path: filePrefixRoot(root, fileFixify(file: targetdir + "$(TARGET)", fix: FileFixifyAbsolute)));
799 if(!ret.isEmpty())
800 ret += "\n\t";
801 ret += QString("-$(INSTALL_FILE) ") + src_targ + ' ' + dst_targ;
802 if(!uninst.isEmpty())
803 uninst.append(t: "\n\t");
804 uninst.append(t: "-$(DEL_FILE) " + dst_targ);
805 }
806 return ret;
807}
808
809void Win32MakefileGenerator::writeDefaultVariables(QTextStream &t)
810{
811 MakefileGenerator::writeDefaultVariables(t);
812 t << "IDC = " << (project->isEmpty(v: "QMAKE_IDC") ? QString("idc") : var(var: "QMAKE_IDC"))
813 << Qt::endl;
814 t << "IDL = " << (project->isEmpty(v: "QMAKE_IDL") ? QString("midl") : var(var: "QMAKE_IDL"))
815 << Qt::endl;
816 t << "ZIP = " << var(var: "QMAKE_ZIP") << Qt::endl;
817 t << "DEF_FILE = " << fileVar(var: "DEF_FILE") << Qt::endl;
818 t << "RES_FILE = " << fileVar(var: "RES_FILE") << Qt::endl; // Not on mingw, can't see why not though...
819 t << "SED = " << var(var: "QMAKE_STREAM_EDITOR") << Qt::endl;
820 t << "MOVE = " << var(var: "QMAKE_MOVE") << Qt::endl;
821}
822
823QString Win32MakefileGenerator::escapeFilePath(const QString &path) const
824{
825 QString ret = path;
826 if(!ret.isEmpty()) {
827 if (ret.contains(c: ' ') || ret.contains(c: '\t'))
828 ret = "\"" + ret + "\"";
829 debug_msg(level: 2, fmt: "EscapeFilePath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData());
830 }
831 return ret;
832}
833
834QString Win32MakefileGenerator::escapeDependencyPath(const QString &path) const
835{
836 QString ret = path;
837 if (!ret.isEmpty()) {
838 static const QRegularExpression criticalChars(QStringLiteral("([\t #])"));
839 if (ret.contains(re: criticalChars))
840 ret = "\"" + ret + "\"";
841 debug_msg(level: 2, fmt: "EscapeDependencyPath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData());
842 }
843 return ret;
844}
845
846QString Win32MakefileGenerator::cQuoted(const QString &str)
847{
848 QString ret = str;
849 ret.replace(c: QLatin1Char('\\'), after: QLatin1String("\\\\"));
850 ret.replace(c: QLatin1Char('"'), after: QLatin1String("\\\""));
851 ret.prepend(c: QLatin1Char('"'));
852 ret.append(c: QLatin1Char('"'));
853 return ret;
854}
855
856ProKey Win32MakefileGenerator::fullTargetVariable() const
857{
858 return "DEST_TARGET";
859}
860
861QT_END_NAMESPACE
862

source code of qtbase/qmake/generators/win32/winmakefile.cpp