1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the qmake application of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "makefile.h"
30#include "option.h"
31#include "cachekeys.h"
32#include "meta.h"
33
34#include <ioutils.h>
35
36#include <qdir.h>
37#include <qfile.h>
38#include <qtextstream.h>
39#include <qregularexpression.h>
40#include <qhash.h>
41#include <qdebug.h>
42#include <qbuffer.h>
43#include <qdatetime.h>
44
45#if defined(Q_OS_UNIX)
46#include <unistd.h>
47#else
48#include <io.h>
49#endif
50#include <stdio.h>
51#include <stdlib.h>
52#include <time.h>
53#include <fcntl.h>
54#include <sys/types.h>
55#include <sys/stat.h>
56
57#include <algorithm>
58
59QT_BEGIN_NAMESPACE
60
61using namespace QMakeInternal;
62
63bool MakefileGenerator::canExecute(const QStringList &cmdline, int *a) const
64{
65 int argv0 = -1;
66 for(int i = 0; i < cmdline.count(); ++i) {
67 if(!cmdline.at(i).contains('=')) {
68 argv0 = i;
69 break;
70 }
71 }
72 if(a)
73 *a = argv0;
74 if(argv0 != -1) {
75 const QString c = Option::normalizePath(cmdline.at(argv0));
76 if(exists(c))
77 return true;
78 }
79 return false;
80}
81
82QString MakefileGenerator::mkdir_p_asstring(const QString &dir, bool escape) const
83{
84 return "@" + makedir.arg(
85 escape ? escapeFilePath(Option::fixPathToTargetOS(dir, false, false)) : dir);
86}
87
88bool MakefileGenerator::mkdir(const QString &in_path) const
89{
90 QString path = Option::normalizePath(in_path);
91 if(QFile::exists(path))
92 return true;
93
94 return QDir().mkpath(path);
95}
96
97void
98MakefileGenerator::verifyCompilers()
99{
100 ProValueMap &v = project->variables();
101 ProStringList &quc = v["QMAKE_EXTRA_COMPILERS"];
102 for(int i = 0; i < quc.size(); ) {
103 bool error = false;
104 const ProString &comp = quc.at(i);
105 const ProKey okey(comp + ".output");
106 if (v[okey].isEmpty()) {
107 const ProKey ofkey(comp + ".output_function");
108 if (!v[ofkey].isEmpty()) {
109 v[okey].append("${QMAKE_FUNC_FILE_IN_" + v[ofkey].first() + "}");
110 } else {
111 error = true;
112 warn_msg(WarnLogic, "Compiler: %s: No output file specified", comp.toLatin1().constData());
113 }
114 } else if (v[ProKey(comp + ".input")].isEmpty()) {
115 error = true;
116 warn_msg(WarnLogic, "Compiler: %s: No input variable specified", comp.toLatin1().constData());
117 }
118 if(error)
119 quc.removeAt(i);
120 else
121 ++i;
122 }
123}
124
125void
126MakefileGenerator::initOutPaths()
127{
128 ProValueMap &v = project->variables();
129 //for shadow builds
130 if(!v.contains("QMAKE_ABSOLUTE_SOURCE_PATH")) {
131 if (Option::globals->do_cache && !project->cacheFile().isEmpty() &&
132 v.contains("QMAKE_ABSOLUTE_SOURCE_ROOT")) {
133 QString root = v["QMAKE_ABSOLUTE_SOURCE_ROOT"].first().toQString();
134 root = QDir::fromNativeSeparators(root);
135 if(!root.isEmpty()) {
136 QFileInfo fi = fileInfo(project->cacheFile());
137 if(!fi.makeAbsolute()) {
138 QString cache_r = fi.path(), pwd = Option::output_dir;
139 if(pwd.startsWith(cache_r) && !pwd.startsWith(root)) {
140 pwd = root + pwd.mid(cache_r.length());
141 if(exists(pwd))
142 v.insert("QMAKE_ABSOLUTE_SOURCE_PATH", ProStringList(pwd));
143 }
144 }
145 }
146 }
147 }
148 if(!v["QMAKE_ABSOLUTE_SOURCE_PATH"].isEmpty()) {
149 ProString &asp = v["QMAKE_ABSOLUTE_SOURCE_PATH"].first();
150 asp = QDir::fromNativeSeparators(asp.toQString());
151 if(asp.isEmpty() || asp == Option::output_dir) //if they're the same, why bother?
152 v["QMAKE_ABSOLUTE_SOURCE_PATH"].clear();
153 }
154
155 //some builtin directories
156 if(project->isEmpty("PRECOMPILED_DIR") && !project->isEmpty("OBJECTS_DIR"))
157 v["PRECOMPILED_DIR"] = v["OBJECTS_DIR"];
158 static const char * const dirs[] = { "OBJECTS_DIR", "DESTDIR",
159 "SUBLIBS_DIR", "DLLDESTDIR",
160 "PRECOMPILED_DIR", nullptr };
161 for (int x = 0; dirs[x]; x++) {
162 const ProKey dkey(dirs[x]);
163 if (v[dkey].isEmpty())
164 continue;
165 const ProString orig_path = v[dkey].first();
166
167 ProString &pathRef = v[dkey].first();
168 pathRef = fileFixify(pathRef.toQString(), FileFixifyFromOutdir);
169
170 if (!pathRef.endsWith(Option::dir_sep))
171 pathRef += Option::dir_sep;
172
173 if (noIO() || (project->first("TEMPLATE") == "subdirs"))
174 continue;
175
176 QString path = project->first(dkey).toQString(); //not to be changed any further
177 path = fileFixify(path, FileFixifyBackwards);
178 debug_msg(3, "Fixed output_dir %s (%s) into %s", dirs[x],
179 orig_path.toLatin1().constData(), path.toLatin1().constData());
180 if(!mkdir(path))
181 warn_msg(WarnLogic, "%s: Cannot access directory '%s'", dirs[x],
182 path.toLatin1().constData());
183 }
184
185 //out paths from the extra compilers
186 const ProStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
187 for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
188 QString tmp_out = project->first(ProKey(*it + ".output")).toQString();
189 if(tmp_out.isEmpty())
190 continue;
191 const ProStringList &tmp = project->values(ProKey(*it + ".input"));
192 for (ProStringList::ConstIterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
193 ProStringList &inputs = project->values((*it2).toKey());
194 for (ProStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
195 QString finp = fileFixify((*input).toQString(), FileFixifyFromOutdir);
196 QString path = replaceExtraCompilerVariables(tmp_out, finp, QString(), NoShell);
197 path = Option::normalizePath(path);
198 int slash = path.lastIndexOf('/');
199 if(slash != -1) {
200 path = path.left(slash);
201 // Make out path only if it does not contain makefile variables
202 if(!path.contains("${"))
203 if(path != "." &&
204 !mkdir(fileFixify(path, FileFixifyBackwards)))
205 warn_msg(WarnLogic, "%s: Cannot access directory '%s'",
206 (*it).toLatin1().constData(), path.toLatin1().constData());
207 }
208 }
209 }
210 }
211
212 if(!v["DESTDIR"].isEmpty()) {
213 QDir d(v["DESTDIR"].first().toQString());
214 if (Option::normalizePath(d.absolutePath()) == Option::normalizePath(Option::output_dir))
215 v.remove("DESTDIR");
216 }
217}
218
219QMakeProject
220*MakefileGenerator::projectFile() const
221{
222 return project;
223}
224
225void
226MakefileGenerator::setProjectFile(QMakeProject *p)
227{
228 if(project)
229 return;
230 project = p;
231 if (project->isActiveConfig("win32"))
232 target_mode = TARG_WIN_MODE;
233 else if (project->isActiveConfig("mac"))
234 target_mode = TARG_MAC_MODE;
235 else
236 target_mode = TARG_UNIX_MODE;
237 init();
238 bool linkPrl = (Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE)
239 && project->isActiveConfig("link_prl");
240 bool mergeLflags = !project->isActiveConfig("no_smart_library_merge")
241 && !project->isActiveConfig("no_lflags_merge");
242 findLibraries(linkPrl, mergeLflags);
243}
244
245ProStringList
246MakefileGenerator::findFilesInVPATH(ProStringList l, uchar flags, const QString &vpath_var)
247{
248 ProStringList vpath;
249 const ProValueMap &v = project->variables();
250 for(int val_it = 0; val_it < l.count(); ) {
251 bool remove_file = false;
252 ProString &val = l[val_it];
253 if(!val.isEmpty()) {
254 QString qval = val.toQString();
255 QString file = fixEnvVariables(qval);
256 if (file.isEmpty()) {
257 ++val_it;
258 continue;
259 }
260 if(!(flags & VPATH_NoFixify))
261 file = fileFixify(file, FileFixifyBackwards);
262
263 if(exists(file)) {
264 ++val_it;
265 continue;
266 }
267 bool found = false;
268 if (QDir::isRelativePath(qval)) {
269 if(vpath.isEmpty()) {
270 if(!vpath_var.isEmpty())
271 vpath = v[ProKey(vpath_var)];
272 vpath += v["VPATH"] + v["QMAKE_ABSOLUTE_SOURCE_PATH"];
273 if(Option::output_dir != qmake_getpwd())
274 vpath << Option::output_dir;
275 }
276 for (ProStringList::Iterator vpath_it = vpath.begin();
277 vpath_it != vpath.end(); ++vpath_it) {
278 QString real_dir = Option::normalizePath((*vpath_it).toQString());
279 if (exists(real_dir + '/' + val)) {
280 ProString dir = (*vpath_it);
281 if(!dir.endsWith(Option::dir_sep))
282 dir += Option::dir_sep;
283 val = dir + val;
284 if(!(flags & VPATH_NoFixify))
285 val = fileFixify(val.toQString());
286 found = true;
287 debug_msg(1, "Found file through vpath %s -> %s",
288 file.toLatin1().constData(), val.toLatin1().constData());
289 break;
290 }
291 }
292 }
293 if(!found) {
294 QString dir, regex = val.toQString(), real_dir;
295 if(regex.lastIndexOf(Option::dir_sep) != -1) {
296 dir = regex.left(regex.lastIndexOf(Option::dir_sep) + 1);
297 real_dir = dir;
298 if(!(flags & VPATH_NoFixify))
299 real_dir = fileFixify(real_dir, FileFixifyBackwards) + '/';
300 regex.remove(0, dir.length());
301 }
302 if(real_dir.isEmpty() || exists(real_dir)) {
303 QStringList files = QDir(real_dir).entryList(QStringList(regex),
304 QDir::NoDotAndDotDot | QDir::AllEntries);
305 if(files.isEmpty()) {
306 debug_msg(1, "%s:%d Failure to find %s in vpath (%s)",
307 __FILE__, __LINE__, val.toLatin1().constData(),
308 vpath.join(QString("::")).toLatin1().constData());
309 if(flags & VPATH_RemoveMissingFiles)
310 remove_file = true;
311 else if(flags & VPATH_WarnMissingFiles)
312 warn_msg(WarnLogic, "Failure to find: %s", val.toLatin1().constData());
313 } else {
314 l.removeAt(val_it);
315 QString a;
316 for(int i = (int)files.count()-1; i >= 0; i--) {
317 a = real_dir + files[i];
318 if(!(flags & VPATH_NoFixify))
319 a = fileFixify(a);
320 l.insert(val_it, a);
321 }
322 }
323 } else {
324 debug_msg(1, "%s:%d Cannot match %s%s, as %s does not exist.",
325 __FILE__, __LINE__, real_dir.toLatin1().constData(),
326 regex.toLatin1().constData(), real_dir.toLatin1().constData());
327 if(flags & VPATH_RemoveMissingFiles)
328 remove_file = true;
329 else if(flags & VPATH_WarnMissingFiles)
330 warn_msg(WarnLogic, "Failure to find: %s", val.toLatin1().constData());
331 }
332 }
333 }
334 if(remove_file)
335 l.removeAt(val_it);
336 else
337 ++val_it;
338 }
339 return l;
340}
341
342void
343MakefileGenerator::initCompiler(const MakefileGenerator::Compiler &comp)
344{
345 ProValueMap &v = project->variables();
346 ProStringList &l = v[ProKey(comp.variable_in)];
347 // find all the relevant file inputs
348 if(!init_compiler_already.contains(comp.variable_in)) {
349 init_compiler_already.insert(comp.variable_in, true);
350 if(!noIO())
351 l = findFilesInVPATH(l, (comp.flags & Compiler::CompilerRemoveNoExist) ?
352 VPATH_RemoveMissingFiles : VPATH_WarnMissingFiles, "VPATH_" + comp.variable_in);
353 }
354}
355
356void
357MakefileGenerator::init()
358{
359 verifyCompilers();
360 initOutPaths();
361
362 ProValueMap &v = project->variables();
363
364 v["QMAKE_BUILTIN_COMPILERS"] = ProStringList() << "C" << "CXX";
365
366 v["QMAKE_LANGUAGE_C"] = ProString("c");
367 v["QMAKE_LANGUAGE_CXX"] = ProString("c++");
368 v["QMAKE_LANGUAGE_OBJC"] = ProString("objective-c");
369 v["QMAKE_LANGUAGE_OBJCXX"] = ProString("objective-c++");
370
371 if (v["TARGET"].isEmpty())
372 warn_msg(WarnLogic, "TARGET is empty");
373
374 makedir = v["QMAKE_MKDIR_CMD"].join(' ');
375 chkexists = v["QMAKE_CHK_EXISTS"].join(' ');
376 if (makedir.isEmpty()) { // Backwards compat with Qt < 5.0.2 specs
377 if (isWindowsShell()) {
378 makedir = "if not exist %1 mkdir %1 & if not exist %1 exit 1";
379 chkexists = "if not exist %1";
380 } else {
381 makedir = "test -d %1 || mkdir -p %1";
382 chkexists = "test -e %1 ||";
383 }
384 }
385
386 if (v["QMAKE_CC_O_FLAG"].isEmpty())
387 v["QMAKE_CC_O_FLAG"].append("-o ");
388
389 if (v["QMAKE_LINK_O_FLAG"].isEmpty())
390 v["QMAKE_LINK_O_FLAG"].append("-o ");
391
392 setSystemIncludes(v["QMAKE_DEFAULT_INCDIRS"]);
393
394 ProStringList &incs = project->values("INCLUDEPATH");
395 if (!project->isActiveConfig("no_include_pwd")) {
396 if (Option::output_dir != qmake_getpwd()) {
397 // Pretend that the build dir is the source dir for #include purposes,
398 // consistently with the "transparent shadow builds" strategy. This is
399 // also consistent with #include "foo.h" falling back to #include <foo.h>
400 // behavior if it doesn't find the file in the source dir.
401 incs.prepend(Option::output_dir);
402 }
403 // This makes #include <foo.h> work if the header lives in the source dir.
404 // The benefit of that is questionable, as generally the user should use the
405 // correct include style, and extra compilers that put stuff in the source dir
406 // should add the dir themselves.
407 // More importantly, it makes #include "foo.h" work with MSVC when shadow-building,
408 // as this compiler looks files up relative to %CD%, not the source file's parent.
409 incs.prepend(qmake_getpwd());
410 }
411 incs.append(project->specDir());
412
413 const auto platform = v["QMAKE_PLATFORM"];
414 resolveDependenciesInFrameworks = platform.contains("darwin");
415
416 const char * const cacheKeys[] = { "_QMAKE_STASH_", "_QMAKE_SUPER_CACHE_", nullptr };
417 for (int i = 0; cacheKeys[i]; ++i) {
418 if (v[cacheKeys[i]].isEmpty())
419 continue;
420 const ProString &file = v[cacheKeys[i]].first();
421 if (file.isEmpty())
422 continue;
423
424 QFileInfo fi(fileInfo(file.toQString()));
425
426 // If the file lives in the output dir we treat it as 'owned' by
427 // the current project, so it should be distcleaned by it as well.
428 if (fi.path() == Option::output_dir)
429 v["QMAKE_DISTCLEAN"].append(fi.fileName());
430 }
431
432 ProStringList &quc = v["QMAKE_EXTRA_COMPILERS"];
433
434 //make sure the COMPILERS are in the correct input/output chain order
435 for(int comp_out = 0, jump_count = 0; comp_out < quc.size(); ++comp_out) {
436 continue_compiler_chain:
437 if(jump_count > quc.size()) //just to avoid an infinite loop here
438 break;
439 const ProKey vokey(quc.at(comp_out) + ".variable_out");
440 if (v.contains(vokey)) {
441 const ProStringList &outputs = v.value(vokey);
442 for(int out = 0; out < outputs.size(); ++out) {
443 for(int comp_in = 0; comp_in < quc.size(); ++comp_in) {
444 if(comp_in == comp_out)
445 continue;
446 const ProKey ikey(quc.at(comp_in) + ".input");
447 if (v.contains(ikey)) {
448 const ProStringList &inputs = v.value(ikey);
449 for(int in = 0; in < inputs.size(); ++in) {
450 if(inputs.at(in) == outputs.at(out) && comp_out > comp_in) {
451 ++jump_count;
452 //move comp_out to comp_in and continue the compiler chain
453 // quc.move(comp_out, comp_in);
454 quc.insert(comp_in, quc.value(comp_out));
455 // comp_out > comp_in, so the insertion did move everything up
456 quc.remove(comp_out + 1);
457 comp_out = comp_in;
458 goto continue_compiler_chain;
459 }
460 }
461 }
462 }
463 }
464 }
465 }
466
467 if(!project->isEmpty("QMAKE_SUBSTITUTES")) {
468 const ProStringList &subs = v["QMAKE_SUBSTITUTES"];
469 for(int i = 0; i < subs.size(); ++i) {
470 QString sub = subs.at(i).toQString();
471 QString inn = sub + ".input", outn = sub + ".output";
472 const ProKey innkey(inn), outnkey(outn);
473 if (v.contains(innkey) || v.contains(outnkey)) {
474 if (!v.contains(innkey) || !v.contains(outnkey)) {
475 warn_msg(WarnLogic, "Substitute '%s' has only one of .input and .output",
476 sub.toLatin1().constData());
477 continue;
478 }
479 const ProStringList &tinn = v[innkey], &toutn = v[outnkey];
480 if (tinn.length() != 1) {
481 warn_msg(WarnLogic, "Substitute '%s.input' does not have exactly one value",
482 sub.toLatin1().constData());
483 continue;
484 }
485 if (toutn.length() != 1) {
486 warn_msg(WarnLogic, "Substitute '%s.output' does not have exactly one value",
487 sub.toLatin1().constData());
488 continue;
489 }
490 inn = fileFixify(tinn.first().toQString(), FileFixifyToIndir);
491 outn = fileFixify(toutn.first().toQString(), FileFixifyBackwards);
492 } else {
493 inn = fileFixify(sub, FileFixifyToIndir);
494 if (!QFile::exists(inn)) {
495 // random insanity for backwards compat: .in file specified with absolute out dir
496 inn = fileFixify(sub);
497 }
498 if(!inn.endsWith(".in")) {
499 warn_msg(WarnLogic, "Substitute '%s' does not end with '.in'",
500 inn.toLatin1().constData());
501 continue;
502 }
503 outn = fileFixify(inn.left(inn.length() - 3), FileFixifyBackwards);
504 }
505
506 const ProKey confign(sub + ".CONFIG");
507 bool verbatim = false;
508 if (v.contains(confign))
509 verbatim = v[confign].contains(QLatin1String("verbatim"));
510
511 QFile in(inn);
512 if (in.open(QFile::ReadOnly)) {
513 QByteArray contentBytes;
514 if (verbatim) {
515 contentBytes = in.readAll();
516 } else {
517 QString contents;
518 QStack<int> state;
519 enum { IN_CONDITION, MET_CONDITION, PENDING_CONDITION };
520 for (int count = 1; !in.atEnd(); ++count) {
521 QString line = QString::fromLatin1(in.readLine());
522 if (line.startsWith("!!IF ")) {
523 if (state.isEmpty() || state.top() == IN_CONDITION) {
524 QString test = line.mid(5, line.length()-(5+1));
525 if (project->test(test, inn, count))
526 state.push(IN_CONDITION);
527 else
528 state.push(PENDING_CONDITION);
529 } else {
530 state.push(MET_CONDITION);
531 }
532 } else if (line.startsWith("!!ELIF ")) {
533 if (state.isEmpty()) {
534 warn_msg(WarnLogic, "(%s:%d): Unexpected else condition",
535 in.fileName().toLatin1().constData(), count);
536 } else if (state.top() == PENDING_CONDITION) {
537 QString test = line.mid(7, line.length()-(7+1));
538 if (project->test(test, inn, count)) {
539 state.pop();
540 state.push(IN_CONDITION);
541 }
542 } else if (state.top() == IN_CONDITION) {
543 state.pop();
544 state.push(MET_CONDITION);
545 }
546 } else if (line.startsWith("!!ELSE")) {
547 if (state.isEmpty()) {
548 warn_msg(WarnLogic, "(%s:%d): Unexpected else condition",
549 in.fileName().toLatin1().constData(), count);
550 } else if (state.top() == PENDING_CONDITION) {
551 state.pop();
552 state.push(IN_CONDITION);
553 } else if (state.top() == IN_CONDITION) {
554 state.pop();
555 state.push(MET_CONDITION);
556 }
557 } else if (line.startsWith("!!ENDIF")) {
558 if (state.isEmpty())
559 warn_msg(WarnLogic, "(%s:%d): Unexpected endif",
560 in.fileName().toLatin1().constData(), count);
561 else
562 state.pop();
563 } else if (state.isEmpty() || state.top() == IN_CONDITION) {
564 contents += project->expand(line, in.fileName(), count);
565 }
566 }
567 contentBytes = contents.toLatin1();
568 }
569 QFile out(outn);
570 QFileInfo outfi(out);
571 if (out.exists() && out.open(QFile::ReadOnly)) {
572 QByteArray old = out.readAll();
573 if (contentBytes == old) {
574 v["QMAKE_INTERNAL_INCLUDED_FILES"].append(in.fileName());
575 v["QMAKE_DISTCLEAN"].append(outfi.absoluteFilePath());
576 continue;
577 }
578 out.close();
579 if(!out.remove()) {
580 warn_msg(WarnLogic, "Cannot clear substitute '%s'",
581 out.fileName().toLatin1().constData());
582 continue;
583 }
584 }
585 mkdir(outfi.absolutePath());
586 if(out.open(QFile::WriteOnly)) {
587 v["QMAKE_INTERNAL_INCLUDED_FILES"].append(in.fileName());
588 v["QMAKE_DISTCLEAN"].append(outfi.absoluteFilePath());
589 out.write(contentBytes);
590 } else {
591 warn_msg(WarnLogic, "Cannot open substitute for output '%s'",
592 out.fileName().toLatin1().constData());
593 }
594 } else {
595 warn_msg(WarnLogic, "Cannot open substitute for input '%s'",
596 in.fileName().toLatin1().constData());
597 }
598 }
599 }
600
601 int x;
602
603 //build up a list of compilers
604 QList<Compiler> compilers;
605 {
606 const char *builtins[] = { "OBJECTS", "SOURCES", "PRECOMPILED_HEADER", nullptr };
607 for(x = 0; builtins[x]; ++x) {
608 Compiler compiler;
609 compiler.variable_in = builtins[x];
610 compiler.flags = Compiler::CompilerBuiltin;
611 compiler.type = QMakeSourceFileInfo::TYPE_C;
612 if(!strcmp(builtins[x], "OBJECTS"))
613 compiler.flags |= Compiler::CompilerNoCheckDeps;
614 compilers.append(compiler);
615 }
616 for (ProStringList::ConstIterator it = quc.cbegin(); it != quc.cend(); ++it) {
617 const ProStringList &inputs = v[ProKey(*it + ".input")];
618 for(x = 0; x < inputs.size(); ++x) {
619 Compiler compiler;
620 compiler.variable_in = inputs.at(x).toQString();
621 compiler.flags = Compiler::CompilerNoFlags;
622 const ProStringList &config = v[ProKey(*it + ".CONFIG")];
623 if (config.indexOf("ignore_no_exist") != -1)
624 compiler.flags |= Compiler::CompilerRemoveNoExist;
625 if (config.indexOf("no_dependencies") != -1)
626 compiler.flags |= Compiler::CompilerNoCheckDeps;
627 if (config.indexOf("add_inputs_as_makefile_deps") != -1)
628 compiler.flags |= Compiler::CompilerAddInputsAsMakefileDeps;
629
630 const ProKey dkey(*it + ".dependency_type");
631 ProString dep_type;
632 if (!project->isEmpty(dkey))
633 dep_type = project->first(dkey);
634 if (dep_type.isEmpty())
635 compiler.type = QMakeSourceFileInfo::TYPE_UNKNOWN;
636 else if(dep_type == "TYPE_UI")
637 compiler.type = QMakeSourceFileInfo::TYPE_UI;
638 else
639 compiler.type = QMakeSourceFileInfo::TYPE_C;
640 compilers.append(compiler);
641 }
642 }
643 }
644 { //do the path fixifying
645 ProStringList paths;
646 for(x = 0; x < compilers.count(); ++x) {
647 if(!paths.contains(compilers.at(x).variable_in))
648 paths << compilers.at(x).variable_in;
649 }
650 paths << "INCLUDEPATH" << "QMAKE_INTERNAL_INCLUDED_FILES" << "PRECOMPILED_HEADER";
651 for(int y = 0; y < paths.count(); y++) {
652 ProStringList &l = v[paths[y].toKey()];
653 for (ProStringList::Iterator it = l.begin(); it != l.end(); ++it) {
654 if((*it).isEmpty())
655 continue;
656 QString fn = (*it).toQString();
657 if (exists(fn))
658 (*it) = fileFixify(fn);
659 }
660 }
661 }
662
663 if(noIO() || !doDepends() || project->isActiveConfig("GNUmake"))
664 QMakeSourceFileInfo::setDependencyMode(QMakeSourceFileInfo::NonRecursive);
665 for(x = 0; x < compilers.count(); ++x)
666 initCompiler(compilers.at(x));
667
668 //merge actual compiler outputs into their variable_out. This is done last so that
669 //files are already properly fixified.
670 for (ProStringList::Iterator it = quc.begin(); it != quc.end(); ++it) {
671 const ProKey ikey(*it + ".input");
672 const ProKey vokey(*it + ".variable_out");
673 const ProStringList &config = project->values(ProKey(*it + ".CONFIG"));
674 const ProString &tmp_out = project->first(ProKey(*it + ".output"));
675 if(tmp_out.isEmpty())
676 continue;
677 if (config.indexOf("combine") != -1) {
678 const ProStringList &compilerInputs = project->values(ikey);
679 // Don't generate compiler output if it doesn't have input.
680 if (compilerInputs.isEmpty() || project->values(compilerInputs.first().toKey()).isEmpty())
681 continue;
682 if(tmp_out.indexOf("$") == -1) {
683 if(!verifyExtraCompiler((*it), QString())) //verify
684 continue;
685 QString out = fileFixify(tmp_out.toQString(), FileFixifyFromOutdir);
686 bool pre_dep = (config.indexOf("target_predeps") != -1);
687 if (v.contains(vokey)) {
688 const ProStringList &var_out = v.value(vokey);
689 for(int i = 0; i < var_out.size(); ++i) {
690 ProKey v = var_out.at(i).toKey();
691 if(v == QLatin1String("SOURCES"))
692 v = "GENERATED_SOURCES";
693 else if(v == QLatin1String("OBJECTS"))
694 pre_dep = false;
695 ProStringList &list = project->values(v);
696 if(!list.contains(out))
697 list.append(out);
698 }
699 } else if (config.indexOf("no_link") == -1) {
700 ProStringList &list = project->values("OBJECTS");
701 pre_dep = false;
702 if(!list.contains(out))
703 list.append(out);
704 } else {
705 ProStringList &list = project->values("UNUSED_SOURCES");
706 if(!list.contains(out))
707 list.append(out);
708 }
709 if(pre_dep) {
710 ProStringList &list = project->values("PRE_TARGETDEPS");
711 if(!list.contains(out))
712 list.append(out);
713 }
714 }
715 } else {
716 const ProStringList &tmp = project->values(ikey);
717 for (ProStringList::ConstIterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
718 const ProStringList &inputs = project->values((*it2).toKey());
719 for (ProStringList::ConstIterator input = inputs.constBegin(); input != inputs.constEnd(); ++input) {
720 if((*input).isEmpty())
721 continue;
722 QString inpf = (*input).toQString();
723 if (!verifyExtraCompiler((*it).toQString(), inpf)) //verify
724 continue;
725 QString out = replaceExtraCompilerVariables(tmp_out.toQString(), inpf, QString(), NoShell);
726 out = fileFixify(out, FileFixifyFromOutdir);
727 bool pre_dep = (config.indexOf("target_predeps") != -1);
728 if (v.contains(vokey)) {
729 const ProStringList &var_out = project->values(vokey);
730 for(int i = 0; i < var_out.size(); ++i) {
731 ProKey v = var_out.at(i).toKey();
732 if(v == QLatin1String("SOURCES"))
733 v = "GENERATED_SOURCES";
734 else if(v == QLatin1String("OBJECTS"))
735 pre_dep = false;
736 ProStringList &list = project->values(v);
737 if(!list.contains(out))
738 list.append(out);
739 }
740 } else if (config.indexOf("no_link") == -1) {
741 pre_dep = false;
742 ProStringList &list = project->values("OBJECTS");
743 if(!list.contains(out))
744 list.append(out);
745 } else {
746 ProStringList &list = project->values("UNUSED_SOURCES");
747 if(!list.contains(out))
748 list.append(out);
749 }
750 if(pre_dep) {
751 ProStringList &list = project->values("PRE_TARGETDEPS");
752 if(!list.contains(out))
753 list.append(out);
754 }
755 }
756 }
757 }
758 }
759
760 //handle dependencies
761 depHeuristicsCache.clear();
762 if(!noIO()) {
763 // dependency paths
764 ProStringList incDirs = v["DEPENDPATH"] + v["QMAKE_ABSOLUTE_SOURCE_PATH"];
765 if(project->isActiveConfig("depend_includepath"))
766 incDirs += v["INCLUDEPATH"];
767 QList<QMakeLocalFileName> deplist;
768 deplist.reserve(incDirs.size());
769 for (ProStringList::Iterator it = incDirs.begin(); it != incDirs.end(); ++it)
770 deplist.append(QMakeLocalFileName((*it).toQString()));
771 QMakeSourceFileInfo::setDependencyPaths(deplist);
772 debug_msg(1, "Dependency Directories: %s",
773 incDirs.join(QString(" :: ")).toLatin1().constData());
774
775 //add to dependency engine
776 for(x = 0; x < compilers.count(); ++x) {
777 const MakefileGenerator::Compiler &comp = compilers.at(x);
778 if(!(comp.flags & Compiler::CompilerNoCheckDeps)) {
779 const ProKey ikey(comp.variable_in);
780 addSourceFiles(v[ikey], QMakeSourceFileInfo::SEEK_DEPS,
781 (QMakeSourceFileInfo::SourceFileType)comp.type);
782
783 if (comp.flags & Compiler::CompilerAddInputsAsMakefileDeps) {
784 ProStringList &l = v[ikey];
785 for (int i=0; i < l.size(); ++i) {
786 if(v["QMAKE_INTERNAL_INCLUDED_FILES"].indexOf(l.at(i)) == -1)
787 v["QMAKE_INTERNAL_INCLUDED_FILES"].append(l.at(i));
788 }
789 }
790 }
791 }
792 }
793
794 processSources(); //remove anything in SOURCES which is included (thus it need not be linked in)
795
796 //all sources and generated sources must be turned into objects at some point (the one builtin compiler)
797 v["OBJECTS"] += createObjectList(v["SOURCES"]) + createObjectList(v["GENERATED_SOURCES"]);
798
799 //Translation files
800 if(!project->isEmpty("TRANSLATIONS")) {
801 ProStringList &trf = project->values("TRANSLATIONS");
802 for (ProStringList::Iterator it = trf.begin(); it != trf.end(); ++it)
803 (*it) = Option::fixPathToTargetOS((*it).toQString());
804 }
805
806 //fix up the target deps
807 static const char * const fixpaths[] = { "PRE_TARGETDEPS", "POST_TARGETDEPS", nullptr };
808 for (int path = 0; fixpaths[path]; path++) {
809 ProStringList &l = v[fixpaths[path]];
810 for (ProStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it) {
811 if(!(*val_it).isEmpty())
812 (*val_it) = Option::fixPathToTargetOS((*val_it).toQString(), false, false);
813 }
814 }
815
816 //extra depends
817 if(!project->isEmpty("DEPENDS")) {
818 ProStringList &l = v["DEPENDS"];
819 for (ProStringList::Iterator it = l.begin(); it != l.end(); ++it) {
820 const ProStringList &files = v[ProKey(*it + ".file")] + v[ProKey(*it + ".files")]; //why do I support such evil things?
821 for (ProStringList::ConstIterator file_it = files.begin(); file_it != files.end(); ++file_it) {
822 QStringList &out_deps = findDependencies((*file_it).toQString());
823 const ProStringList &in_deps = v[ProKey(*it + ".depends")]; //even more evilness..
824 for (ProStringList::ConstIterator dep_it = in_deps.begin(); dep_it != in_deps.end(); ++dep_it) {
825 QString dep = (*dep_it).toQString();
826 if (exists(dep)) {
827 out_deps.append(dep);
828 } else {
829 QString dir, regex = Option::normalizePath(dep);
830 if (regex.lastIndexOf('/') != -1) {
831 dir = regex.left(regex.lastIndexOf('/') + 1);
832 regex.remove(0, dir.length());
833 }
834 QStringList files = QDir(dir).entryList(QStringList(regex));
835 if(files.isEmpty()) {
836 warn_msg(WarnLogic, "Dependency for [%s]: Not found %s", (*file_it).toLatin1().constData(),
837 dep.toLatin1().constData());
838 } else {
839 for(int i = 0; i < files.count(); i++)
840 out_deps.append(dir + files[i]);
841 }
842 }
843 }
844 }
845 }
846 }
847
848 // escape qmake command
849 project->values("QMAKE_QMAKE") =
850 ProStringList(escapeFilePath(Option::fixPathToTargetOS(Option::globals->qmake_abslocation, false)));
851}
852
853bool
854MakefileGenerator::processPrlFile(QString &file, bool baseOnly)
855{
856 QString f = fileFixify(file, FileFixifyBackwards);
857 // Explicitly given full .prl name
858 if (!baseOnly && f.endsWith(Option::prl_ext))
859 return processPrlFileCore(file, QStringView(), f);
860 // Explicitly given or derived (from -l) base name
861 if (processPrlFileCore(file, QStringView(), f + Option::prl_ext))
862 return true;
863 if (!baseOnly) {
864 // Explicitly given full library name
865 int off = qMax(f.lastIndexOf('/'), f.lastIndexOf('\\')) + 1;
866 int ext = QStringView(f).mid(off).lastIndexOf('.');
867 if (ext != -1)
868 return processPrlFileBase(file, QStringView(f).mid(off), QStringView{f}.left(off + ext), off);
869 }
870 return false;
871}
872
873bool
874MakefileGenerator::processPrlFileBase(QString &origFile, QStringView origName,
875 QStringView fixedBase, int /*slashOff*/)
876{
877 return processPrlFileCore(origFile, origName, fixedBase + Option::prl_ext);
878}
879
880bool
881MakefileGenerator::processPrlFileCore(QString &origFile, QStringView origName,
882 const QString &fixedFile)
883{
884 const QString meta_file = QMakeMetaInfo::checkLib(fixedFile);
885 if (meta_file.isEmpty())
886 return false;
887 QMakeMetaInfo libinfo;
888 debug_msg(1, "Processing PRL file: %s", meta_file.toLatin1().constData());
889 if (!libinfo.readLib(meta_file)) {
890 fprintf(stderr, "Error processing meta file %s\n", meta_file.toLatin1().constData());
891 return false;
892 }
893 if (project->isActiveConfig("no_read_prl_qmake")) {
894 debug_msg(2, "Ignored meta file %s", meta_file.toLatin1().constData());
895 return false;
896 }
897 ProString tgt = libinfo.first("QMAKE_PRL_TARGET");
898 if (tgt.isEmpty()) {
899 fprintf(stderr, "Error: %s does not define QMAKE_PRL_TARGET\n",
900 meta_file.toLatin1().constData());
901 return false;
902 }
903 if (!tgt.contains('.') && !libinfo.values("QMAKE_PRL_CONFIG").contains("lib_bundle")) {
904 fprintf(stderr, "Error: %s defines QMAKE_PRL_TARGET without extension\n",
905 meta_file.toLatin1().constData());
906 return false;
907 }
908 if (origName.isEmpty()) {
909 // We got a .prl file as input, replace it with an actual library.
910 int off = qMax(origFile.lastIndexOf('/'), origFile.lastIndexOf('\\')) + 1;
911 debug_msg(1, " Replacing library reference %s with %s",
912 origFile.mid(off).toLatin1().constData(),
913 tgt.toQString().toLatin1().constData());
914 origFile.replace(off, 1000, tgt.toQString());
915 } else if (tgt != origName) {
916 // We got an actual library as input, and found the wrong .prl for it.
917 debug_msg(2, "Mismatched meta file %s (want %s, got %s)",
918 meta_file.toLatin1().constData(),
919 origName.toLatin1().constData(), tgt.toLatin1().constData());
920 return false;
921 }
922 project->values("QMAKE_CURRENT_PRL_LIBS") = libinfo.values("QMAKE_PRL_LIBS");
923 ProStringList &defs = project->values("DEFINES");
924 const ProStringList &prl_defs = project->values("PRL_EXPORT_DEFINES");
925 for (const ProString &def : libinfo.values("QMAKE_PRL_DEFINES"))
926 if (!defs.contains(def) && prl_defs.contains(def))
927 defs.append(def);
928 QString mf = fileFixify(meta_file);
929 if (!project->values("QMAKE_PRL_INTERNAL_FILES").contains(mf))
930 project->values("QMAKE_PRL_INTERNAL_FILES").append(mf);
931 if (!project->values("QMAKE_INTERNAL_INCLUDED_FILES").contains(mf))
932 project->values("QMAKE_INTERNAL_INCLUDED_FILES").append(mf);
933 return true;
934}
935
936void
937MakefileGenerator::filterIncludedFiles(const char *var)
938{
939 ProStringList &inputs = project->values(var);
940 auto isIncluded = [this](const ProString &input) {
941 return QMakeSourceFileInfo::included(input.toQString()) > 0;
942 };
943 inputs.erase(std::remove_if(inputs.begin(), inputs.end(),
944 isIncluded),
945 inputs.end());
946}
947
948static QString
949qv(const ProString &val)
950{
951 return ' ' + QMakeEvaluator::quoteValue(val);
952}
953
954static QString
955qv(const ProStringList &val)
956{
957 QString ret;
958 for (const ProString &v : val)
959 ret += qv(v);
960 return ret;
961}
962
963void
964MakefileGenerator::writePrlFile(QTextStream &t)
965{
966 QString bdir = Option::output_dir;
967 if(bdir.isEmpty())
968 bdir = qmake_getpwd();
969 t << "QMAKE_PRL_BUILD_DIR =" << qv(bdir) << Qt::endl;
970
971 t << "QMAKE_PRO_INPUT =" << qv(project->projectFile().section('/', -1)) << Qt::endl;
972
973 if(!project->isEmpty("QMAKE_ABSOLUTE_SOURCE_PATH"))
974 t << "QMAKE_PRL_SOURCE_DIR =" << qv(project->first("QMAKE_ABSOLUTE_SOURCE_PATH")) << Qt::endl;
975 t << "QMAKE_PRL_TARGET =" << qv(project->first("LIB_TARGET")) << Qt::endl;
976 if(!project->isEmpty("PRL_EXPORT_DEFINES"))
977 t << "QMAKE_PRL_DEFINES =" << qv(project->values("PRL_EXPORT_DEFINES")) << Qt::endl;
978 if(!project->isEmpty("PRL_EXPORT_CFLAGS"))
979 t << "QMAKE_PRL_CFLAGS =" << qv(project->values("PRL_EXPORT_CFLAGS")) << Qt::endl;
980 if(!project->isEmpty("PRL_EXPORT_CXXFLAGS"))
981 t << "QMAKE_PRL_CXXFLAGS =" << qv(project->values("PRL_EXPORT_CXXFLAGS")) << Qt::endl;
982 if(!project->isEmpty("CONFIG"))
983 t << "QMAKE_PRL_CONFIG =" << qv(project->values("CONFIG")) << Qt::endl;
984 if(!project->isEmpty("TARGET_VERSION_EXT"))
985 t << "QMAKE_PRL_VERSION = " << project->first("TARGET_VERSION_EXT") << Qt::endl;
986 else if(!project->isEmpty("VERSION"))
987 t << "QMAKE_PRL_VERSION = " << project->first("VERSION") << Qt::endl;
988 if(project->isActiveConfig("staticlib") || project->isActiveConfig("explicitlib")) {
989 ProStringList libs;
990 if (!project->isActiveConfig("staticlib"))
991 libs << "LIBS" << "QMAKE_LIBS";
992 else
993 libs << "LIBS" << "LIBS_PRIVATE" << "QMAKE_LIBS" << "QMAKE_LIBS_PRIVATE";
994 t << "QMAKE_PRL_LIBS =";
995 for (ProStringList::Iterator it = libs.begin(); it != libs.end(); ++it)
996 t << qv(project->values((*it).toKey()));
997 t << Qt::endl;
998
999 t << "QMAKE_PRL_LIBS_FOR_CMAKE = ";
1000 QString sep;
1001 for (ProStringList::Iterator it = libs.begin(); it != libs.end(); ++it) {
1002 t << sep << project->values((*it).toKey()).join(';').replace('\\', "\\\\");
1003 sep = ';';
1004 }
1005 t << Qt::endl;
1006 }
1007}
1008
1009bool
1010MakefileGenerator::writeProjectMakefile()
1011{
1012 QTextStream t(&Option::output);
1013
1014 //header
1015 writeHeader(t);
1016
1017 QList<SubTarget*> targets;
1018 {
1019 ProStringList builds = project->values("BUILDS");
1020 targets.reserve(builds.size());
1021 for (ProStringList::Iterator it = builds.begin(); it != builds.end(); ++it) {
1022 SubTarget *st = new SubTarget;
1023 targets.append(st);
1024 st->makefile = "$(MAKEFILE)." + (*it);
1025 st->name = (*it).toQString();
1026 const ProKey tkey(*it + ".target");
1027 st->target = (project->isEmpty(tkey) ? (*it) : project->first(tkey)).toQString();
1028 }
1029 }
1030 if(project->isActiveConfig("build_all")) {
1031 t << "first: all\n";
1032 QList<SubTarget*>::Iterator it;
1033
1034 //install
1035 t << "install: ";
1036 for (SubTarget *s : qAsConst(targets))
1037 t << s->target << '-';
1038 t << "install " << Qt::endl;
1039
1040 //uninstall
1041 t << "uninstall: ";
1042 for(it = targets.begin(); it != targets.end(); ++it)
1043 t << (*it)->target << "-uninstall ";
1044 t << Qt::endl;
1045 } else {
1046 t << "first: " << targets.first()->target << Qt::endl
1047 << "install: " << targets.first()->target << "-install\n"
1048 << "uninstall: " << targets.first()->target << "-uninstall\n";
1049 }
1050
1051 writeSubTargets(t, targets, SubTargetsNoFlags);
1052 if(!project->isActiveConfig("no_autoqmake")) {
1053 QString mkf = escapeDependencyPath(fileFixify(Option::output.fileName()));
1054 for(QList<SubTarget*>::Iterator it = targets.begin(); it != targets.end(); ++it)
1055 t << escapeDependencyPath((*it)->makefile) << ": " << mkf << Qt::endl;
1056 }
1057 qDeleteAll(targets);
1058 return true;
1059}
1060
1061bool
1062MakefileGenerator::write()
1063{
1064 if(!project)
1065 return false;
1066 writePrlFile();
1067 if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE || //write makefile
1068 Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
1069 QTextStream t(&Option::output);
1070 if(!writeMakefile(t)) {
1071#if 1
1072 warn_msg(WarnLogic, "Unable to generate output for: %s [TEMPLATE %s]",
1073 Option::output.fileName().toLatin1().constData(),
1074 project->first("TEMPLATE").toLatin1().constData());
1075 if(Option::output.exists())
1076 Option::output.remove();
1077#endif
1078 }
1079 }
1080 return true;
1081}
1082
1083QString
1084MakefileGenerator::prlFileName(bool fixify)
1085{
1086 QString ret = project->first("PRL_TARGET") + Option::prl_ext;
1087 if(fixify) {
1088 if(!project->isEmpty("DESTDIR"))
1089 ret.prepend(project->first("DESTDIR").toQString());
1090 ret = fileFixify(ret, FileFixifyBackwards);
1091 }
1092 return ret;
1093}
1094
1095void
1096MakefileGenerator::writePrlFile()
1097{
1098 if((Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
1099 Option::qmake_mode == Option::QMAKE_GENERATE_PRL)
1100 && project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()
1101 && project->isActiveConfig("create_prl")
1102 && (project->first("TEMPLATE") == "lib"
1103 || project->first("TEMPLATE") == "vclib"
1104 || project->first("TEMPLATE") == "aux")
1105 && (!project->isActiveConfig("plugin") || project->isActiveConfig("static"))) { //write prl file
1106 QString local_prl = prlFileName();
1107 QString prl = fileFixify(local_prl);
1108 mkdir(fileInfo(local_prl).path());
1109 QFile ft(local_prl);
1110 if(ft.open(QIODevice::WriteOnly)) {
1111 project->values("ALL_DEPS").append(prl);
1112 project->values("QMAKE_INTERNAL_PRL_FILE").append(prl);
1113 project->values("QMAKE_DISTCLEAN").append(prl);
1114 QTextStream t(&ft);
1115 writePrlFile(t);
1116 }
1117 }
1118}
1119
1120void
1121MakefileGenerator::writeObj(QTextStream &t, const char *src)
1122{
1123 const ProStringList &srcl = project->values(src);
1124 const ProStringList objl = createObjectList(srcl);
1125
1126 ProStringList::ConstIterator oit = objl.begin();
1127 ProStringList::ConstIterator sit = srcl.begin();
1128 QLatin1String stringSrc("$src");
1129 QLatin1String stringObj("$obj");
1130 for(;sit != srcl.end() && oit != objl.end(); ++oit, ++sit) {
1131 if((*sit).isEmpty())
1132 continue;
1133
1134 QString srcf = (*sit).toQString();
1135 QString dstf = (*oit).toQString();
1136 t << escapeDependencyPath(dstf) << ": " << escapeDependencyPath(srcf)
1137 << " " << finalizeDependencyPaths(findDependencies(srcf)).join(" \\\n\t\t");
1138
1139 ProKey comp;
1140 for (const ProString &compiler : project->values("QMAKE_BUILTIN_COMPILERS")) {
1141 // Unfortunately we were not consistent about the C++ naming
1142 ProString extensionSuffix = compiler;
1143 if (extensionSuffix == "CXX")
1144 extensionSuffix = ProString("CPP");
1145
1146 // Nor the C naming
1147 ProString compilerSuffix = compiler;
1148 if (compilerSuffix == "C")
1149 compilerSuffix = ProString("CC");
1150
1151 for (const ProString &extension : project->values(ProKey("QMAKE_EXT_" + extensionSuffix))) {
1152 if ((*sit).endsWith(extension)) {
1153 comp = ProKey("QMAKE_RUN_" + compilerSuffix);
1154 break;
1155 }
1156 }
1157
1158 if (!comp.isNull())
1159 break;
1160 }
1161
1162 if (comp.isEmpty())
1163 comp = "QMAKE_RUN_CC";
1164 if (!project->isEmpty(comp)) {
1165 QString p = var(comp);
1166 p.replace(stringSrc, escapeFilePath(srcf));
1167 p.replace(stringObj, escapeFilePath(dstf));
1168 t << "\n\t" << p;
1169 }
1170 t << Qt::endl << Qt::endl;
1171 }
1172}
1173
1174QString
1175MakefileGenerator::filePrefixRoot(const QString &root, const QString &path)
1176{
1177 QString ret(path);
1178 if(path.length() > 2 && path[1] == ':') //c:\foo
1179 ret.insert(2, root);
1180 else
1181 ret.prepend(root);
1182 while (ret.endsWith('\\'))
1183 ret.chop(1);
1184 return ret;
1185}
1186
1187void
1188MakefileGenerator::writeInstalls(QTextStream &t, bool noBuild)
1189{
1190 QString rm_dir_contents("-$(DEL_FILE)");
1191 if (!isWindowsShell()) //ick
1192 rm_dir_contents = "-$(DEL_FILE) -r";
1193
1194 QString all_installs, all_uninstalls;
1195 QSet<QString> made_dirs, removed_dirs;
1196 const ProStringList &l = project->values("INSTALLS");
1197 for (ProStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
1198 const ProKey pvar(*it + ".path");
1199 const ProStringList &installConfigValues = project->values(ProKey(*it + ".CONFIG"));
1200 if (installConfigValues.indexOf("no_path") == -1 &&
1201 installConfigValues.indexOf("dummy_install") == -1 &&
1202 project->values(pvar).isEmpty()) {
1203 warn_msg(WarnLogic, "%s is not defined: install target not created\n", pvar.toLatin1().constData());
1204 continue;
1205 }
1206
1207 bool do_default = true;
1208 const QString root = installRoot();
1209 QString dst;
1210 if (installConfigValues.indexOf("no_path") == -1 &&
1211 installConfigValues.indexOf("dummy_install") == -1) {
1212 dst = fileFixify(project->first(pvar).toQString(), FileFixifyAbsolute, false);
1213 if(!dst.endsWith(Option::dir_sep))
1214 dst += Option::dir_sep;
1215 }
1216
1217 QStringList tmp, inst, uninst;
1218 //other
1219 ProStringList tmp2 = project->values(ProKey(*it + ".extra"));
1220 if (tmp2.isEmpty())
1221 tmp2 = project->values(ProKey(*it + ".commands")); //to allow compatible name
1222 if (!tmp2.isEmpty()) {
1223 do_default = false;
1224 inst << tmp2.join(' ');
1225 }
1226 //masks
1227 tmp2 = findFilesInVPATH(project->values(ProKey(*it + ".files")), VPATH_NoFixify);
1228 tmp = fileFixify(tmp2.toQStringList(), FileFixifyAbsolute);
1229 if(!tmp.isEmpty()) {
1230 do_default = false;
1231 QString base_path = project->first(ProKey(*it + ".base")).toQString();
1232 if (!base_path.isEmpty()) {
1233 base_path = Option::fixPathToTargetOS(base_path, false, true);
1234 if (!base_path.endsWith(Option::dir_sep))
1235 base_path += Option::dir_sep;
1236 }
1237 for(QStringList::Iterator wild_it = tmp.begin(); wild_it != tmp.end(); ++wild_it) {
1238 QString wild = Option::fixPathToTargetOS((*wild_it), false, false);
1239 QString dirstr = qmake_getpwd(), filestr = wild;
1240 int slsh = filestr.lastIndexOf(Option::dir_sep);
1241 if(slsh != -1) {
1242 dirstr = filestr.left(slsh+1);
1243 filestr.remove(0, slsh+1);
1244 }
1245 if(!dirstr.endsWith(Option::dir_sep))
1246 dirstr += Option::dir_sep;
1247 QString dst_dir = dst;
1248 if (!base_path.isEmpty()) {
1249 if (!dirstr.startsWith(base_path)) {
1250 warn_msg(WarnLogic, "File %s in install rule %s does not start with base %s",
1251 qPrintable(wild), qPrintable((*it).toQString()),
1252 qPrintable(base_path));
1253 } else {
1254 QString dir_sfx = dirstr.mid(base_path.length());
1255 dst_dir += dir_sfx;
1256 if (!dir_sfx.isEmpty() && !made_dirs.contains(dir_sfx)) {
1257 made_dirs.insert(dir_sfx);
1258 QString tmp_dst = fileFixify(dst_dir, FileFixifyAbsolute, false);
1259 tmp_dst.chop(1);
1260 inst << mkdir_p_asstring(filePrefixRoot(root, tmp_dst));
1261 for (int i = dst.length(); i < dst_dir.length(); i++) {
1262 if (dst_dir.at(i) == Option::dir_sep) {
1263 QString subd = dst_dir.left(i);
1264 if (!removed_dirs.contains(subd)) {
1265 removed_dirs.insert(subd);
1266 tmp_dst = fileFixify(subd, FileFixifyAbsolute, false);
1267 uninst << "-$(DEL_DIR) "
1268 + escapeFilePath(filePrefixRoot(root, tmp_dst));
1269 }
1270 }
1271 }
1272 }
1273 }
1274 }
1275 bool is_target = (wild == fileFixify(var("TARGET"), FileFixifyAbsolute));
1276 const bool noStrip = installConfigValues.contains("nostrip");
1277 if(is_target || exists(wild)) { //real file or target
1278 QFileInfo fi(fileInfo(wild));
1279 QString dst_file = filePrefixRoot(root, dst_dir);
1280 if (!dst_file.endsWith(Option::dir_sep))
1281 dst_file += Option::dir_sep;
1282 dst_file += fi.fileName();
1283 QString cmd;
1284 if (is_target || (!fi.isDir() && fi.isExecutable()))
1285 cmd = QLatin1String("$(QINSTALL_PROGRAM)");
1286 else
1287 cmd = QLatin1String("$(QINSTALL)");
1288 cmd += " " + escapeFilePath(wild) + " " + escapeFilePath(dst_file);
1289 inst << cmd;
1290 if (!noStrip && !project->isActiveConfig("debug_info") && !project->isActiveConfig("nostrip") &&
1291 !fi.isDir() && fi.isExecutable() && !project->isEmpty("QMAKE_STRIP"))
1292 inst << QString("-") + var("QMAKE_STRIP") + " " +
1293 escapeFilePath(filePrefixRoot(root, fileFixify(dst_dir + filestr, FileFixifyAbsolute, false)));
1294 uninst.append(rm_dir_contents + " " + escapeFilePath(filePrefixRoot(root, fileFixify(dst_dir + filestr, FileFixifyAbsolute, false))));
1295 continue;
1296 }
1297 QString local_dirstr = Option::normalizePath(dirstr);
1298 QStringList files = QDir(local_dirstr).entryList(QStringList(filestr),
1299 QDir::NoDotAndDotDot | QDir::AllEntries);
1300 if (installConfigValues.contains("no_check_exist") && files.isEmpty()) {
1301 QString dst_file = filePrefixRoot(root, dst_dir);
1302 if (!dst_file.endsWith(Option::dir_sep))
1303 dst_file += Option::dir_sep;
1304 dst_file += filestr;
1305 QString cmd;
1306 if (installConfigValues.contains("executable"))
1307 cmd = QLatin1String("$(QINSTALL_PROGRAM)");
1308 else
1309 cmd = QLatin1String("$(QINSTALL)");
1310 cmd += " " + escapeFilePath(wild) + " " + escapeFilePath(dst_file);
1311 inst << cmd;
1312 uninst.append(rm_dir_contents + " " + escapeFilePath(filePrefixRoot(root, fileFixify(dst_dir + filestr, FileFixifyAbsolute, false))));
1313 }
1314 for(int x = 0; x < files.count(); x++) {
1315 QString file = files[x];
1316 uninst.append(rm_dir_contents + " " + escapeFilePath(filePrefixRoot(root, fileFixify(dst_dir + file, FileFixifyAbsolute, false))));
1317 QFileInfo fi(fileInfo(dirstr + file));
1318 QString dst_file = filePrefixRoot(root, fileFixify(dst_dir, FileFixifyAbsolute, false));
1319 if (!dst_file.endsWith(Option::dir_sep))
1320 dst_file += Option::dir_sep;
1321 dst_file += fi.fileName();
1322 QString cmd = QLatin1String("$(QINSTALL) ") +
1323 escapeFilePath(dirstr + file) + " " + escapeFilePath(dst_file);
1324 inst << cmd;
1325 if (!noStrip && !project->isActiveConfig("debug_info") && !project->isActiveConfig("nostrip") &&
1326 !fi.isDir() && fi.isExecutable() && !project->isEmpty("QMAKE_STRIP"))
1327 inst << QString("-") + var("QMAKE_STRIP") + " " +
1328 escapeFilePath(filePrefixRoot(root, fileFixify(dst_dir + file, FileFixifyAbsolute, false)));
1329 }
1330 }
1331 }
1332 QString target;
1333 //default?
1334 if (do_default)
1335 target = defaultInstall((*it).toQString());
1336 else
1337 target = inst.join("\n\t");
1338 QString puninst = project->values(ProKey(*it + ".uninstall")).join(' ');
1339 if (!puninst.isEmpty())
1340 uninst << puninst;
1341
1342 if (!target.isEmpty() || installConfigValues.indexOf("dummy_install") != -1) {
1343 if (noBuild || installConfigValues.indexOf("no_build") != -1)
1344 t << "install_" << (*it) << ":";
1345 else if(project->isActiveConfig("build_all"))
1346 t << "install_" << (*it) << ": all";
1347 else
1348 t << "install_" << (*it) << ": first";
1349 const ProStringList &deps = project->values(ProKey(*it + ".depends"));
1350 if(!deps.isEmpty()) {
1351 for (ProStringList::ConstIterator dep_it = deps.begin(); dep_it != deps.end(); ++dep_it) {
1352 QString targ = var(ProKey(*dep_it + ".target"));
1353 if(targ.isEmpty())
1354 targ = (*dep_it).toQString();
1355 t << " " << escapeDependencyPath(targ);
1356 }
1357 }
1358 t << " FORCE\n\t";
1359 const ProStringList &dirs = project->values(pvar);
1360 for (ProStringList::ConstIterator pit = dirs.begin(); pit != dirs.end(); ++pit) {
1361 QString tmp_dst = fileFixify((*pit).toQString(), FileFixifyAbsolute, false);
1362 t << mkdir_p_asstring(filePrefixRoot(root, tmp_dst)) << "\n\t";
1363 }
1364 t << target << Qt::endl << Qt::endl;
1365 if(!uninst.isEmpty()) {
1366 t << "uninstall_" << (*it) << ": FORCE";
1367 for (int i = uninst.size(); --i >= 0; )
1368 t << "\n\t" << uninst.at(i);
1369 t << "\n\t-$(DEL_DIR) " << escapeFilePath(filePrefixRoot(root, dst)) << " \n\n";
1370 }
1371 t << Qt::endl;
1372
1373 if (installConfigValues.indexOf("no_default_install") == -1) {
1374 all_installs += QString("install_") + (*it) + " ";
1375 if(!uninst.isEmpty())
1376 all_uninstalls += "uninstall_" + (*it) + " ";
1377 }
1378 } else {
1379 debug_msg(1, "no definition for install %s: install target not created",(*it).toLatin1().constData());
1380 }
1381 }
1382 t << "install:" << depVar("INSTALLDEPS") << ' ' << all_installs
1383 << " FORCE\n\nuninstall: " << all_uninstalls << depVar("UNINSTALLDEPS")
1384 << " FORCE\n\n";
1385}
1386
1387QString
1388MakefileGenerator::var(const ProKey &var) const
1389{
1390 return val(project->values(var));
1391}
1392
1393QString
1394MakefileGenerator::fileVar(const ProKey &var) const
1395{
1396 return val(escapeFilePaths(project->values(var)));
1397}
1398
1399QString
1400MakefileGenerator::fileVarList(const ProKey &var) const
1401{
1402 return valList(escapeFilePaths(project->values(var)));
1403}
1404
1405QString
1406MakefileGenerator::depVar(const ProKey &var) const
1407{
1408 return val(escapeDependencyPaths(project->values(var)));
1409}
1410
1411QString
1412MakefileGenerator::val(const ProStringList &varList) const
1413{
1414 return valGlue(varList, "", " ", "");
1415}
1416
1417QString
1418MakefileGenerator::val(const QStringList &varList) const
1419{
1420 return valGlue(varList, "", " ", "");
1421}
1422
1423QString
1424MakefileGenerator::varGlue(const ProKey &var, const QString &before, const QString &glue, const QString &after) const
1425{
1426 return valGlue(project->values(var), before, glue, after);
1427}
1428
1429QString
1430MakefileGenerator::fileVarGlue(const ProKey &var, const QString &before, const QString &glue, const QString &after) const
1431{
1432 return valGlue(escapeFilePaths(project->values(var)), before, glue, after);
1433}
1434
1435QString
1436MakefileGenerator::fixFileVarGlue(const ProKey &var, const QString &before, const QString &glue, const QString &after) const
1437{
1438 ProStringList varList;
1439 const auto values = project->values(var);
1440 varList.reserve(values.size());
1441 for (const ProString &val : values)
1442 varList << escapeFilePath(Option::fixPathToTargetOS(val.toQString()));
1443 return valGlue(varList, before, glue, after);
1444}
1445
1446QString
1447MakefileGenerator::valGlue(const ProStringList &varList, const QString &before, const QString &glue, const QString &after) const
1448{
1449 QString ret;
1450 for (ProStringList::ConstIterator it = varList.begin(); it != varList.end(); ++it) {
1451 if (!(*it).isEmpty()) {
1452 if (!ret.isEmpty())
1453 ret += glue;
1454 ret += (*it).toQString();
1455 }
1456 }
1457 return ret.isEmpty() ? QString("") : before + ret + after;
1458}
1459
1460QString
1461MakefileGenerator::valGlue(const QStringList &varList, const QString &before, const QString &glue, const QString &after) const
1462{
1463 QString ret;
1464 for(QStringList::ConstIterator it = varList.begin(); it != varList.end(); ++it) {
1465 if(!(*it).isEmpty()) {
1466 if(!ret.isEmpty())
1467 ret += glue;
1468 ret += (*it);
1469 }
1470 }
1471 return ret.isEmpty() ? QString("") : before + ret + after;
1472}
1473
1474
1475QString
1476MakefileGenerator::varList(const ProKey &var) const
1477{
1478 return valList(project->values(var));
1479}
1480
1481QString
1482MakefileGenerator::valList(const ProStringList &varList) const
1483{
1484 return valGlue(varList, "", " \\\n\t\t", "");
1485}
1486
1487QString
1488MakefileGenerator::valList(const QStringList &varList) const
1489{
1490 return valGlue(varList, "", " \\\n\t\t", "");
1491}
1492
1493ProStringList
1494MakefileGenerator::createObjectList(const ProStringList &sources)
1495{
1496 ProStringList ret;
1497 QString objdir;
1498 if(!project->values("OBJECTS_DIR").isEmpty())
1499 objdir = project->first("OBJECTS_DIR").toQString();
1500 for (ProStringList::ConstIterator it = sources.begin(); it != sources.end(); ++it) {
1501 QString sfn = (*it).toQString();
1502 QFileInfo fi(fileInfo(Option::normalizePath(sfn)));
1503 QString dir;
1504 if (project->isActiveConfig("object_parallel_to_source")) {
1505 // The source paths are relative to the output dir, but we need source-relative paths
1506 QString sourceRelativePath = fileFixify(sfn, FileFixifyBackwards);
1507
1508 if (sourceRelativePath.startsWith(".." + Option::dir_sep))
1509 sourceRelativePath = fileFixify(sourceRelativePath, FileFixifyAbsolute);
1510
1511 if (QDir::isAbsolutePath(sourceRelativePath))
1512 sourceRelativePath.remove(0, sourceRelativePath.indexOf(Option::dir_sep) + 1);
1513
1514 dir = objdir; // We still respect OBJECTS_DIR
1515
1516 int lastDirSepPosition = sourceRelativePath.lastIndexOf(Option::dir_sep);
1517 if (lastDirSepPosition != -1)
1518 dir += QStringView{sourceRelativePath}.left(lastDirSepPosition + 1);
1519
1520 if (!noIO()) {
1521 // Ensure that the final output directory of each object exists
1522 QString outRelativePath = fileFixify(dir, FileFixifyBackwards);
1523 if (!outRelativePath.isEmpty() && !mkdir(outRelativePath))
1524 warn_msg(WarnLogic, "Cannot create directory '%s'", outRelativePath.toLatin1().constData());
1525 }
1526 } else {
1527 dir = objdir;
1528 }
1529 ret.append(dir + fi.completeBaseName() + Option::obj_ext);
1530 }
1531 return ret;
1532}
1533
1534ReplaceExtraCompilerCacheKey::ReplaceExtraCompilerCacheKey(
1535 const QString &v, const QStringList &i, const QStringList &o, MakefileGenerator::ReplaceFor s)
1536{
1537 static QString doubleColon = QLatin1String("::");
1538
1539 hash = 0;
1540 pwd = qmake_getpwd();
1541 var = v;
1542 {
1543 QStringList il = i;
1544 il.sort();
1545 in = il.join(doubleColon);
1546 }
1547 {
1548 QStringList ol = o;
1549 ol.sort();
1550 out = ol.join(doubleColon);
1551 }
1552 forShell = s;
1553}
1554
1555bool ReplaceExtraCompilerCacheKey::operator==(const ReplaceExtraCompilerCacheKey &f) const
1556{
1557 return (hashCode() == f.hashCode() &&
1558 f.forShell == forShell &&
1559 f.in == in &&
1560 f.out == out &&
1561 f.var == var &&
1562 f.pwd == pwd);
1563}
1564
1565
1566QString
1567MakefileGenerator::replaceExtraCompilerVariables(
1568 const QString &orig_var, const QStringList &in, const QStringList &out, ReplaceFor forShell)
1569{
1570 //lazy cache
1571 ReplaceExtraCompilerCacheKey cacheKey(orig_var, in, out, forShell);
1572 QString cacheVal = extraCompilerVariablesCache.value(cacheKey);
1573 if(!cacheVal.isNull())
1574 return cacheVal;
1575
1576 //do the work
1577 QString ret = orig_var;
1578 QRegularExpression reg_var("\\$\\{.*\\}", QRegularExpression::InvertedGreedinessOption);
1579 QRegularExpressionMatch match;
1580 for (int rep = 0; (match = reg_var.match(ret, rep)).hasMatch(); ) {
1581 rep = match.capturedStart();
1582 QStringList val;
1583 const ProString var(ret.mid(rep + 2, match.capturedLength() - 3));
1584 bool filePath = false;
1585 if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_VAR_"))) {
1586 const ProKey varname = var.mid(10).toKey();
1587 val += project->values(varname).toQStringList();
1588 }
1589 if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_VAR_FIRST_"))) {
1590 const ProKey varname = var.mid(16).toKey();
1591 val += project->first(varname).toQString();
1592 }
1593
1594 if(val.isEmpty() && !in.isEmpty()) {
1595 if(var.startsWith(QLatin1String("QMAKE_FUNC_FILE_IN_"))) {
1596 filePath = true;
1597 const ProKey funcname = var.mid(19).toKey();
1598 val += project->expand(funcname, QList<ProStringList>() << ProStringList(in));
1599 } else if(var == QLatin1String("QMAKE_FILE_BASE") || var == QLatin1String("QMAKE_FILE_IN_BASE")) {
1600 filePath = true;
1601 for(int i = 0; i < in.size(); ++i) {
1602 QFileInfo fi(fileInfo(Option::normalizePath(in.at(i))));
1603 QString base = fi.completeBaseName();
1604 if(base.isNull())
1605 base = fi.fileName();
1606 val += base;
1607 }
1608 } else if (var == QLatin1String("QMAKE_FILE_EXT") || var == QLatin1String("QMAKE_FILE_IN_EXT")) {
1609 filePath = true;
1610 for(int i = 0; i < in.size(); ++i) {
1611 QFileInfo fi(fileInfo(Option::normalizePath(in.at(i))));
1612 QString ext;
1613 // Ensure complementarity with QMAKE_FILE_BASE
1614 int baseLen = fi.completeBaseName().length();
1615 if(baseLen == 0)
1616 ext = fi.fileName();
1617 else
1618 ext = fi.fileName().remove(0, baseLen);
1619 val += ext;
1620 }
1621 } else if (var == QLatin1String("QMAKE_FILE_IN_NAME")) {
1622 filePath = true;
1623 for (int i = 0; i < in.size(); ++i)
1624 val += fileInfo(Option::normalizePath(in.at(i))).fileName();
1625 } else if(var == QLatin1String("QMAKE_FILE_PATH") || var == QLatin1String("QMAKE_FILE_IN_PATH")) {
1626 filePath = true;
1627 for(int i = 0; i < in.size(); ++i)
1628 val += fileInfo(Option::normalizePath(in.at(i))).path();
1629 } else if(var == QLatin1String("QMAKE_FILE_NAME") || var == QLatin1String("QMAKE_FILE_IN")) {
1630 filePath = true;
1631 for(int i = 0; i < in.size(); ++i)
1632 val += fileInfo(Option::normalizePath(in.at(i))).filePath();
1633
1634 }
1635 }
1636 if(val.isEmpty() && !out.isEmpty()) {
1637 if(var.startsWith(QLatin1String("QMAKE_FUNC_FILE_OUT_"))) {
1638 filePath = true;
1639 const ProKey funcname = var.mid(20).toKey();
1640 val += project->expand(funcname, QList<ProStringList>() << ProStringList(out));
1641 } else if (var == QLatin1String("QMAKE_FILE_OUT_PATH")) {
1642 filePath = true;
1643 for (int i = 0; i < out.size(); ++i)
1644 val += fileInfo(Option::normalizePath(out.at(i))).path();
1645 } else if(var == QLatin1String("QMAKE_FILE_OUT")) {
1646 filePath = true;
1647 for(int i = 0; i < out.size(); ++i)
1648 val += fileInfo(Option::normalizePath(out.at(i))).filePath();
1649 } else if(var == QLatin1String("QMAKE_FILE_OUT_BASE")) {
1650 filePath = true;
1651 for(int i = 0; i < out.size(); ++i) {
1652 QFileInfo fi(fileInfo(Option::normalizePath(out.at(i))));
1653 QString base = fi.completeBaseName();
1654 if(base.isNull())
1655 base = fi.fileName();
1656 val += base;
1657 }
1658 }
1659 }
1660 if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_FUNC_"))) {
1661 const ProKey funcname = var.mid(11).toKey();
1662 val += project->expand(funcname, QList<ProStringList>() << ProStringList(in) << ProStringList(out));
1663 }
1664
1665 if(!val.isEmpty()) {
1666 QString fullVal;
1667 if (filePath && forShell != NoShell) {
1668 for(int i = 0; i < val.size(); ++i) {
1669 if(!fullVal.isEmpty())
1670 fullVal += " ";
1671 if (forShell == LocalShell)
1672 fullVal += IoUtils::shellQuote(Option::fixPathToLocalOS(val.at(i), false));
1673 else
1674 fullVal += escapeFilePath(Option::fixPathToTargetOS(val.at(i), false));
1675 }
1676 } else {
1677 fullVal = val.join(' ');
1678 }
1679 ret.replace(match.capturedStart(), match.capturedLength(), fullVal);
1680 rep += fullVal.length();
1681 } else {
1682 rep = match.capturedEnd();
1683 }
1684 }
1685
1686 //cache the value
1687 extraCompilerVariablesCache.insert(cacheKey, ret);
1688 return ret;
1689}
1690
1691bool
1692MakefileGenerator::verifyExtraCompiler(const ProString &comp, const QString &file_unfixed)
1693{
1694 if(noIO())
1695 return false;
1696 const QString file = Option::normalizePath(file_unfixed);
1697
1698 const ProStringList &config = project->values(ProKey(comp + ".CONFIG"));
1699 if (config.indexOf("moc_verify") != -1) {
1700 if(!file.isNull()) {
1701 QMakeSourceFileInfo::addSourceFile(file, QMakeSourceFileInfo::SEEK_MOCS);
1702 if(!mocable(file)) {
1703 return false;
1704 } else {
1705 project->values("MOCABLES").append(file);
1706 }
1707 }
1708 } else if (config.indexOf("function_verify") != -1) {
1709 ProString tmp_out = project->first(ProKey(comp + ".output"));
1710 if(tmp_out.isEmpty())
1711 return false;
1712 ProStringList verify_function = project->values(ProKey(comp + ".verify_function"));
1713 if(verify_function.isEmpty())
1714 return false;
1715
1716 for(int i = 0; i < verify_function.size(); ++i) {
1717 bool invert = false;
1718 ProString verify = verify_function.at(i);
1719 if(verify.at(0) == QLatin1Char('!')) {
1720 invert = true;
1721 verify = verify.mid(1);
1722 }
1723
1724 if (config.indexOf("combine") != -1) {
1725 bool pass = project->test(verify.toKey(), QList<ProStringList>() << ProStringList(tmp_out) << ProStringList(file));
1726 if(invert)
1727 pass = !pass;
1728 if(!pass)
1729 return false;
1730 } else {
1731 const ProStringList &tmp = project->values(ProKey(comp + ".input"));
1732 for (ProStringList::ConstIterator it = tmp.begin(); it != tmp.end(); ++it) {
1733 const ProStringList &inputs = project->values((*it).toKey());
1734 for (ProStringList::ConstIterator input = inputs.begin(); input != inputs.end(); ++input) {
1735 if((*input).isEmpty())
1736 continue;
1737 QString inpf = (*input).toQString();
1738 QString in = fileFixify(inpf);
1739 if(in == file) {
1740 bool pass = project->test(verify.toKey(),
1741 QList<ProStringList>() << ProStringList(replaceExtraCompilerVariables(tmp_out.toQString(), inpf, QString(), NoShell)) <<
1742 ProStringList(file));
1743 if(invert)
1744 pass = !pass;
1745 if(!pass)
1746 return false;
1747 break;
1748 }
1749 }
1750 }
1751 }
1752 }
1753 } else if (config.indexOf("verify") != -1) {
1754 QString tmp_out = project->first(ProKey(comp + ".output")).toQString();
1755 if(tmp_out.isEmpty())
1756 return false;
1757 const QString tmp_cmd = project->values(ProKey(comp + ".commands")).join(' ');
1758 if (config.indexOf("combine") != -1) {
1759 QString cmd = replaceExtraCompilerVariables(tmp_cmd, QString(), tmp_out, LocalShell);
1760 if(system(cmd.toLatin1().constData()))
1761 return false;
1762 } else {
1763 const ProStringList &tmp = project->values(ProKey(comp + ".input"));
1764 for (ProStringList::ConstIterator it = tmp.begin(); it != tmp.end(); ++it) {
1765 const ProStringList &inputs = project->values((*it).toKey());
1766 for (ProStringList::ConstIterator input = inputs.begin(); input != inputs.end(); ++input) {
1767 if((*input).isEmpty())
1768 continue;
1769 QString inpf = (*input).toQString();
1770 QString in = fileFixify(inpf);
1771 if(in == file) {
1772 QString out = replaceExtraCompilerVariables(tmp_out, inpf, QString(), NoShell);
1773 QString cmd = replaceExtraCompilerVariables(tmp_cmd, in, out, LocalShell);
1774 if(system(cmd.toLatin1().constData()))
1775 return false;
1776 break;
1777 }
1778 }
1779 }
1780 }
1781 }
1782 return true;
1783}
1784
1785void
1786MakefileGenerator::writeExtraTargets(QTextStream &t)
1787{
1788 const ProStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
1789 for (ProStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it) {
1790 QString targ = var(ProKey(*it + ".target")),
1791 cmd = var(ProKey(*it + ".commands")), deps;
1792 if(targ.isEmpty())
1793 targ = (*it).toQString();
1794 const ProStringList &deplist = project->values(ProKey(*it + ".depends"));
1795 for (ProStringList::ConstIterator dep_it = deplist.begin(); dep_it != deplist.end(); ++dep_it) {
1796 QString dep = var(ProKey(*dep_it + ".target"));
1797 if(dep.isEmpty())
1798 dep = (*dep_it).toQString();
1799 deps += " " + escapeDependencyPath(dep);
1800 }
1801 const ProStringList &config = project->values(ProKey(*it + ".CONFIG"));
1802 if (config.indexOf("fix_target") != -1)
1803 targ = fileFixify(targ, FileFixifyFromOutdir);
1804 if (config.indexOf("phony") != -1)
1805 deps += QLatin1String(" FORCE");
1806 t << escapeDependencyPath(targ) << ":" << deps;
1807 if(!cmd.isEmpty())
1808 t << "\n\t" << cmd;
1809 t << Qt::endl << Qt::endl;
1810 }
1811}
1812
1813static QStringList splitDeps(const QString &indeps, bool lineMode)
1814{
1815 if (!lineMode)
1816 return indeps.simplified().split(' ');
1817 QStringList deps = indeps.split('\n', Qt::SkipEmptyParts);
1818#ifdef Q_OS_WIN
1819 for (auto &dep : deps) {
1820 if (dep.endsWith(QLatin1Char('\r')))
1821 dep.chop(1);
1822 }
1823#endif
1824 return deps;
1825}
1826
1827QString MakefileGenerator::resolveDependency(const QDir &outDir, const QString &file)
1828{
1829 const QList<QMakeLocalFileName> &depdirs = QMakeSourceFileInfo::dependencyPaths();
1830 for (const auto &depdir : depdirs) {
1831 const QString &local = depdir.local();
1832 QString lf = outDir.absoluteFilePath(local + '/' + file);
1833 if (exists(lf))
1834 return lf;
1835
1836 if (resolveDependenciesInFrameworks) {
1837 // Given a file like "QtWidgets/QWidget", try to resolve it
1838 // as framework header "QtWidgets.framework/Headers/QWidget".
1839 int cut = file.indexOf('/');
1840 if (cut < 0 || cut + 1 >= file.size())
1841 continue;
1842 QStringView framework = QStringView{file}.left(cut);
1843 QStringView include = QStringView(file).mid(cut + 1);
1844 if (local.endsWith('/' + framework + ".framework/Headers")) {
1845 lf = outDir.absoluteFilePath(local + '/' + include);
1846 if (exists(lf))
1847 return lf;
1848 }
1849 }
1850 }
1851 return {};
1852}
1853
1854void MakefileGenerator::callExtraCompilerDependCommand(const ProString &extraCompiler,
1855 const QString &tmp_dep_cmd,
1856 const QString &inpf,
1857 const QString &tmp_out,
1858 bool dep_lines,
1859 QStringList *deps,
1860 bool existingDepsOnly,
1861 bool checkCommandAvailability)
1862{
1863 char buff[256];
1864 QString dep_cmd = replaceExtraCompilerVariables(tmp_dep_cmd, inpf, tmp_out, LocalShell);
1865 if (checkCommandAvailability && !canExecute(dep_cmd))
1866 return;
1867 dep_cmd = QLatin1String("cd ")
1868 + IoUtils::shellQuote(Option::fixPathToLocalOS(Option::output_dir, false))
1869 + QLatin1String(" && ")
1870 + fixEnvVariables(dep_cmd);
1871 if (FILE *proc = QT_POPEN(dep_cmd.toLatin1().constData(), QT_POPEN_READ)) {
1872 QByteArray depData;
1873 while (int read_in = feof(proc) ? 0 : (int)fread(buff, 1, 255, proc))
1874 depData.append(buff, read_in);
1875 QT_PCLOSE(proc);
1876 const QString indeps = QString::fromLocal8Bit(depData);
1877 if (indeps.isEmpty())
1878 return;
1879 QDir outDir(Option::output_dir);
1880 QStringList dep_cmd_deps = splitDeps(indeps, dep_lines);
1881 for (int i = 0; i < dep_cmd_deps.count(); ++i) {
1882 QString &file = dep_cmd_deps[i];
1883 const QString absFile = outDir.absoluteFilePath(file);
1884 if (absFile == file) {
1885 // already absolute; don't do any checks.
1886 } else if (exists(absFile)) {
1887 file = absFile;
1888 } else {
1889 const QString localFile = resolveDependency(outDir, file);
1890 if (localFile.isEmpty()) {
1891 if (exists(file)) {
1892 warn_msg(WarnDeprecated, ".depend_command for extra compiler %s"
1893 " prints paths relative to source directory",
1894 extraCompiler.toLatin1().constData());
1895 } else if (existingDepsOnly) {
1896 file.clear();
1897 } else {
1898 file = absFile; // fallback for generated resources
1899 }
1900 } else {
1901 file = localFile;
1902 }
1903 }
1904 if (!file.isEmpty())
1905 file = fileFixify(file);
1906 }
1907 deps->append(dep_cmd_deps);
1908 }
1909}
1910
1911void
1912MakefileGenerator::writeExtraCompilerTargets(QTextStream &t)
1913{
1914 QString clean_targets;
1915 const ProStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
1916 for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
1917 const ProStringList &config = project->values(ProKey(*it + ".CONFIG"));
1918 QString tmp_out = fileFixify(project->first(ProKey(*it + ".output")).toQString(),
1919 FileFixifyFromOutdir);
1920 const QString tmp_cmd = project->values(ProKey(*it + ".commands")).join(' ');
1921 const QString tmp_dep_cmd = project->values(ProKey(*it + ".depend_command")).join(' ');
1922 const bool dep_lines = (config.indexOf("dep_lines") != -1);
1923 const ProStringList &vars = project->values(ProKey(*it + ".variables"));
1924 if(tmp_out.isEmpty() || tmp_cmd.isEmpty())
1925 continue;
1926 ProStringList tmp_inputs;
1927 {
1928 const ProStringList &comp_inputs = project->values(ProKey(*it + ".input"));
1929 for (ProStringList::ConstIterator it2 = comp_inputs.begin(); it2 != comp_inputs.end(); ++it2) {
1930 const ProStringList &tmp = project->values((*it2).toKey());
1931 for (ProStringList::ConstIterator input = tmp.begin(); input != tmp.end(); ++input) {
1932 if (verifyExtraCompiler((*it), (*input).toQString()))
1933 tmp_inputs.append((*input));
1934 }
1935 }
1936 }
1937
1938 t << "compiler_" << (*it) << "_make_all:";
1939 if (config.indexOf("combine") != -1) {
1940 // compilers with a combined input only have one output
1941 QString input = project->first(ProKey(*it + ".output")).toQString();
1942 t << ' ' << escapeDependencyPath(fileFixify(
1943 replaceExtraCompilerVariables(tmp_out, input, QString(), NoShell),
1944 FileFixifyFromOutdir));
1945 } else {
1946 for (ProStringList::ConstIterator input = tmp_inputs.cbegin(); input != tmp_inputs.cend(); ++input) {
1947 t << ' ' << escapeDependencyPath(fileFixify(
1948 replaceExtraCompilerVariables(tmp_out, (*input).toQString(), QString(), NoShell),
1949 FileFixifyFromOutdir));
1950 }
1951 }
1952 t << Qt::endl;
1953
1954 if (config.indexOf("no_clean") == -1) {
1955 QStringList raw_clean = project->values(ProKey(*it + ".clean")).toQStringList();
1956 if (raw_clean.isEmpty())
1957 raw_clean << tmp_out;
1958 QString tmp_clean;
1959 for (const QString &rc : qAsConst(raw_clean))
1960 tmp_clean += ' ' + escapeFilePath(Option::fixPathToTargetOS(rc));
1961 QString tmp_clean_cmds = project->values(ProKey(*it + ".clean_commands")).join(' ');
1962 if(!tmp_inputs.isEmpty())
1963 clean_targets += QString("compiler_" + (*it) + "_clean ");
1964 t << "compiler_" << (*it) << "_clean:";
1965 bool wrote_clean_cmds = false, wrote_clean = false;
1966 if(tmp_clean_cmds.isEmpty()) {
1967 wrote_clean_cmds = true;
1968 } else if(tmp_clean_cmds.indexOf("${QMAKE_") == -1) {
1969 t << "\n\t" << tmp_clean_cmds;
1970 wrote_clean_cmds = true;
1971 }
1972 if(tmp_clean.indexOf("${QMAKE_") == -1) {
1973 t << "\n\t-$(DEL_FILE)" << tmp_clean;
1974 wrote_clean = true;
1975 }
1976 if(!wrote_clean_cmds || !wrote_clean) {
1977 QStringList cleans;
1978 const QString del_statement("-$(DEL_FILE)");
1979 if(!wrote_clean) {
1980 QStringList dels;
1981 for (ProStringList::ConstIterator input = tmp_inputs.cbegin(); input != tmp_inputs.cend(); ++input) {
1982 QString tinp = (*input).toQString();
1983 QString out = replaceExtraCompilerVariables(tmp_out, tinp, QString(), NoShell);
1984 for (const QString &rc : qAsConst(raw_clean)) {
1985 dels << ' ' + escapeFilePath(fileFixify(
1986 replaceExtraCompilerVariables(rc, tinp, out, NoShell),
1987 FileFixifyFromOutdir));
1988 }
1989 }
1990 if(project->isActiveConfig("no_delete_multiple_files")) {
1991 cleans = dels;
1992 } else {
1993 QString files;
1994 const int commandlineLimit = 2047; // NT limit, expanded
1995 for (const QString &file : qAsConst(dels)) {
1996 if(del_statement.length() + files.length() +
1997 qMax(fixEnvVariables(file).length(), file.length()) > commandlineLimit) {
1998 cleans.append(files);
1999 files.clear();
2000 }
2001 files += file;
2002 }
2003 if(!files.isEmpty())
2004 cleans.append(files);
2005 }
2006 }
2007 if(!cleans.isEmpty())
2008 t << valGlue(cleans, "\n\t" + del_statement, "\n\t" + del_statement, "");
2009 if(!wrote_clean_cmds) {
2010 for (ProStringList::ConstIterator input = tmp_inputs.cbegin(); input != tmp_inputs.cend(); ++input) {
2011 QString tinp = (*input).toQString();
2012 t << "\n\t" << replaceExtraCompilerVariables(tmp_clean_cmds, tinp,
2013 replaceExtraCompilerVariables(tmp_out, tinp, QString(), NoShell), TargetShell);
2014 }
2015 }
2016 }
2017 t << Qt::endl;
2018 }
2019 const bool existingDepsOnly = config.contains("dep_existing_only");
2020 QStringList tmp_dep = project->values(ProKey(*it + ".depends")).toQStringList();
2021 if (config.indexOf("combine") != -1) {
2022 if (tmp_out.contains(QRegularExpression("(^|[^$])\\$\\{QMAKE_(?!VAR_)"))) {
2023 warn_msg(WarnLogic, "QMAKE_EXTRA_COMPILERS(%s) with combine has variable output.",
2024 (*it).toLatin1().constData());
2025 continue;
2026 }
2027 QStringList deps, inputs;
2028 if(!tmp_dep.isEmpty())
2029 deps += fileFixify(tmp_dep, FileFixifyFromOutdir);
2030 for (ProStringList::ConstIterator input = tmp_inputs.cbegin(); input != tmp_inputs.cend(); ++input) {
2031 QString inpf = (*input).toQString();
2032 deps += findDependencies(inpf);
2033 inputs += Option::fixPathToTargetOS(inpf, false);
2034 if(!tmp_dep_cmd.isEmpty() && doDepends()) {
2035 callExtraCompilerDependCommand(*it, tmp_dep_cmd, inpf,
2036 tmp_out, dep_lines, &deps, existingDepsOnly);
2037 }
2038 }
2039 for(int i = 0; i < inputs.size(); ) {
2040 if(tmp_out == inputs.at(i))
2041 inputs.removeAt(i);
2042 else
2043 ++i;
2044 }
2045 for(int i = 0; i < deps.size(); ) {
2046 if(tmp_out == deps.at(i))
2047 deps.removeAt(i);
2048 else
2049 ++i;
2050 }
2051 if (inputs.isEmpty())
2052 continue;
2053
2054 QString out = replaceExtraCompilerVariables(tmp_out, QString(), QString(), NoShell);
2055 QString cmd = replaceExtraCompilerVariables(tmp_cmd, inputs, QStringList() << out, TargetShell);
2056 t << escapeDependencyPath(fileFixify(out, FileFixifyFromOutdir)) << ":";
2057 // compiler.CONFIG+=explicit_dependencies means that ONLY compiler.depends gets to cause Makefile dependencies
2058 if (config.indexOf("explicit_dependencies") != -1) {
2059 t << " " << valList(escapeDependencyPaths(fileFixify(tmp_dep, FileFixifyFromOutdir)));
2060 } else {
2061 t << " " << valList(escapeDependencyPaths(inputs)) << " " << valList(finalizeDependencyPaths(deps));
2062 }
2063 t << "\n\t" << cmd << Qt::endl << Qt::endl;
2064 continue;
2065 }
2066 for (ProStringList::ConstIterator input = tmp_inputs.cbegin(); input != tmp_inputs.cend(); ++input) {
2067 QString inpf = (*input).toQString();
2068 QStringList deps;
2069 deps << fileFixify(inpf, FileFixifyFromOutdir);
2070 deps += findDependencies(inpf);
2071 QString out = fileFixify(replaceExtraCompilerVariables(tmp_out, inpf, QString(), NoShell),
2072 FileFixifyFromOutdir);
2073 if(!tmp_dep.isEmpty()) {
2074 QStringList pre_deps = fileFixify(tmp_dep, FileFixifyFromOutdir);
2075 for(int i = 0; i < pre_deps.size(); ++i)
2076 deps << fileFixify(replaceExtraCompilerVariables(pre_deps.at(i), inpf, out, NoShell),
2077 FileFixifyFromOutdir);
2078 }
2079 QString cmd = replaceExtraCompilerVariables(tmp_cmd, inpf, out, TargetShell);
2080 // NOTE: The var -> QMAKE_COMP_var replace feature is unsupported, do not use!
2081 for (ProStringList::ConstIterator it3 = vars.constBegin(); it3 != vars.constEnd(); ++it3)
2082 cmd.replace("$(" + (*it3) + ")", "$(QMAKE_COMP_" + (*it3)+")");
2083 if(!tmp_dep_cmd.isEmpty() && doDepends()) {
2084 callExtraCompilerDependCommand(*it, tmp_dep_cmd, inpf,
2085 tmp_out, dep_lines, &deps, existingDepsOnly);
2086 //use the depend system to find includes of these included files
2087 QStringList inc_deps;
2088 for(int i = 0; i < deps.size(); ++i) {
2089 const QString dep = fileFixify(deps.at(i), FileFixifyFromOutdir | FileFixifyAbsolute);
2090 if(QFile::exists(dep)) {
2091 SourceFileType type = TYPE_UNKNOWN;
2092 if(type == TYPE_UNKNOWN) {
2093 for(QStringList::Iterator cit = Option::c_ext.begin();
2094 cit != Option::c_ext.end(); ++cit) {
2095 if(dep.endsWith((*cit))) {
2096 type = TYPE_C;
2097 break;
2098 }
2099 }
2100 }
2101 if(type == TYPE_UNKNOWN) {
2102 for(QStringList::Iterator cppit = Option::cpp_ext.begin();
2103 cppit != Option::cpp_ext.end(); ++cppit) {
2104 if(dep.endsWith((*cppit))) {
2105 type = TYPE_C;
2106 break;
2107 }
2108 }
2109 }
2110 if(type == TYPE_UNKNOWN) {
2111 for(QStringList::Iterator hit = Option::h_ext.begin();
2112 type == TYPE_UNKNOWN && hit != Option::h_ext.end(); ++hit) {
2113 if(dep.endsWith((*hit))) {
2114 type = TYPE_C;
2115 break;
2116 }
2117 }
2118 }
2119 if(type != TYPE_UNKNOWN) {
2120 if(!QMakeSourceFileInfo::containsSourceFile(dep, type))
2121 QMakeSourceFileInfo::addSourceFile(dep, type);
2122 inc_deps += QMakeSourceFileInfo::dependencies(dep);
2123 }
2124 }
2125 }
2126 deps += fileFixify(inc_deps, FileFixifyFromOutdir);
2127 }
2128 for(int i = 0; i < deps.size(); ) {
2129 QString &dep = deps[i];
2130 if(out == dep)
2131 deps.removeAt(i);
2132 else
2133 ++i;
2134 }
2135 t << escapeDependencyPath(out) << ": " << valList(finalizeDependencyPaths(deps)) << "\n\t"
2136 << cmd << Qt::endl << Qt::endl;
2137 }
2138 }
2139 t << "compiler_clean: " << clean_targets << Qt::endl << Qt::endl;
2140}
2141
2142void
2143MakefileGenerator::writeExtraCompilerVariables(QTextStream &t)
2144{
2145 bool first = true;
2146 const ProStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
2147 for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
2148 const ProStringList &vars = project->values(ProKey(*it + ".variables"));
2149 for (ProStringList::ConstIterator varit = vars.begin(); varit != vars.end(); ++varit) {
2150 if(first) {
2151 t << "\n####### Custom Compiler Variables\n";
2152 first = false;
2153 }
2154 t << "QMAKE_COMP_" << (*varit) << " = "
2155 << valList(project->values((*varit).toKey())) << Qt::endl;
2156 }
2157 }
2158 if(!first)
2159 t << Qt::endl;
2160}
2161
2162void
2163MakefileGenerator::writeExtraVariables(QTextStream &t)
2164{
2165 t << Qt::endl;
2166
2167 ProStringList outlist;
2168 const ProValueMap &vars = project->variables();
2169 const ProStringList &exports = project->values("QMAKE_EXTRA_VARIABLES");
2170 for (ProStringList::ConstIterator exp_it = exports.begin(); exp_it != exports.end(); ++exp_it) {
2171 auto rx = QRegularExpression::fromWildcard((*exp_it).toQString(), Qt::CaseInsensitive);
2172 for (ProValueMap::ConstIterator it = vars.begin(); it != vars.end(); ++it) {
2173 if (rx.match(it.key().toQString()).hasMatch())
2174 outlist << ("EXPORT_" + it.key() + " = " + it.value().join(' '));
2175 }
2176 }
2177 if (!outlist.isEmpty()) {
2178 t << "####### Custom Variables\n";
2179 t << outlist.join('\n') << Qt::endl << Qt::endl;
2180 }
2181}
2182
2183// This is a more powerful alternative to the above function.
2184// It's meant to be internal, as one can make quite a mess with it.
2185void
2186MakefileGenerator::writeExportedVariables(QTextStream &t)
2187{
2188 const auto &vars = project->values("QMAKE_EXPORTED_VARIABLES");
2189 if (vars.isEmpty())
2190 return;
2191 for (const auto &exp : vars) {
2192 const ProString &name = project->first(ProKey(exp + ".name"));
2193 const ProString &value = project->first(ProKey(exp + ".value"));
2194 if (!value.isEmpty())
2195 t << name << " = " << value << Qt::endl;
2196 else
2197 t << name << " =\n";
2198 }
2199 t << Qt::endl;
2200}
2201
2202bool
2203MakefileGenerator::writeDummyMakefile(QTextStream &t)
2204{
2205 if (project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty())
2206 return false;
2207 t << "QMAKE = " << var("QMAKE_QMAKE") << Qt::endl;
2208 const ProStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
2209 for (ProStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it)
2210 t << *it << " ";
2211 t << "first all clean install distclean uninstall qmake_all:\n\t"
2212 << "@echo \"Some of the required modules ("
2213 << var("QMAKE_FAILED_REQUIREMENTS") << ") are not available.\"\n\t"
2214 << "@echo \"Skipped.\"\n\n";
2215 writeMakeQmake(t);
2216 t << "FORCE:\n\n";
2217 return true;
2218}
2219
2220bool
2221MakefileGenerator::writeMakefile(QTextStream &t)
2222{
2223 t << "####### Compile\n\n";
2224 writeObj(t, "SOURCES");
2225 writeObj(t, "GENERATED_SOURCES");
2226
2227 t << "####### Install\n\n";
2228 writeInstalls(t);
2229
2230 t << "FORCE:\n\n";
2231 return true;
2232}
2233
2234void
2235MakefileGenerator::writeDefaultVariables(QTextStream &t)
2236{
2237 t << "QMAKE = " << var("QMAKE_QMAKE") << Qt::endl;
2238 t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << Qt::endl;
2239 t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << Qt::endl;
2240 t << "MKDIR = " << var("QMAKE_MKDIR") << Qt::endl;
2241 t << "COPY = " << var("QMAKE_COPY") << Qt::endl;
2242 t << "COPY_FILE = " << var("QMAKE_COPY_FILE") << Qt::endl;
2243 t << "COPY_DIR = " << var("QMAKE_COPY_DIR") << Qt::endl;
2244 t << "INSTALL_FILE = " << var("QMAKE_INSTALL_FILE") << Qt::endl;
2245 t << "INSTALL_PROGRAM = " << var("QMAKE_INSTALL_PROGRAM") << Qt::endl;
2246 t << "INSTALL_DIR = " << var("QMAKE_INSTALL_DIR") << Qt::endl;
2247 t << "QINSTALL = " << var("QMAKE_QMAKE") << " -install qinstall" << Qt::endl;
2248 t << "QINSTALL_PROGRAM = " << var("QMAKE_QMAKE") << " -install qinstall -exe" << Qt::endl;
2249 t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << Qt::endl;
2250 t << "SYMLINK = " << var("QMAKE_SYMBOLIC_LINK") << Qt::endl;
2251 t << "DEL_DIR = " << var("QMAKE_DEL_DIR") << Qt::endl;
2252 t << "MOVE = " << var("QMAKE_MOVE") << Qt::endl;
2253}
2254
2255QString MakefileGenerator::buildArgs(bool withExtra)
2256{
2257 QString ret;
2258
2259 for (const QString &arg : qAsConst(Option::globals->qmake_args))
2260 ret += " " + shellQuote(arg);
2261 if (withExtra && !Option::globals->qmake_extra_args.isEmpty()) {
2262 ret += " --";
2263 for (const QString &arg : qAsConst(Option::globals->qmake_extra_args))
2264 ret += " " + shellQuote(arg);
2265 }
2266 return ret;
2267}
2268
2269//could get stored argv, but then it would have more options than are
2270//probably necesary this will try to guess the bare minimum..
2271QString MakefileGenerator::fullBuildArgs()
2272{
2273 QString ret;
2274
2275 //output
2276 QString ofile = fileFixify(Option::output.fileName());
2277 if (!ofile.isEmpty() && ofile != project->first("QMAKE_MAKEFILE").toQStringView())
2278 ret += " -o " + escapeFilePath(ofile);
2279
2280 //inputs
2281 ret += " " + escapeFilePath(fileFixify(project->projectFile()));
2282
2283 // general options and arguments
2284 ret += buildArgs(true);
2285
2286 return ret;
2287}
2288
2289void
2290MakefileGenerator::writeHeader(QTextStream &t)
2291{
2292 t << "#############################################################################\n";
2293 t << "# Makefile for building: " << escapeFilePath(var("TARGET")) << Qt::endl;
2294 t << "# Generated by qmake (" QMAKE_VERSION_STR ") (Qt " QT_VERSION_STR ")\n";
2295 t << "# Project: " << fileFixify(project->projectFile()) << Qt::endl;
2296 t << "# Template: " << var("TEMPLATE") << Qt::endl;
2297 if(!project->isActiveConfig("build_pass"))
2298 t << "# Command: " << var("QMAKE_QMAKE") << fullBuildArgs() << Qt::endl;
2299 t << "#############################################################################\n";
2300 t << Qt::endl;
2301 QString ofile = Option::fixPathToTargetOS(Option::output.fileName());
2302 if (ofile.lastIndexOf(Option::dir_sep) != -1)
2303 ofile.remove(0, ofile.lastIndexOf(Option::dir_sep) +1);
2304 t << "MAKEFILE = " << escapeFilePath(ofile) << Qt::endl << Qt::endl;
2305 t << "EQ = =\n\n";
2306}
2307
2308QList<MakefileGenerator::SubTarget*>
2309MakefileGenerator::findSubDirsSubTargets() const
2310{
2311 QList<SubTarget*> targets;
2312 {
2313 const ProStringList &subdirs = project->values("SUBDIRS");
2314 for(int subdir = 0; subdir < subdirs.size(); ++subdir) {
2315 ProString ofile = subdirs[subdir];
2316 QString oname = ofile.toQString();
2317 QString fixedSubdir = oname;
2318 fixedSubdir.replace(QRegularExpression("[^a-zA-Z0-9_]"),"-");
2319
2320 SubTarget *st = new SubTarget;
2321 st->name = oname;
2322 targets.append(st);
2323
2324 bool fromFile = false;
2325 const ProKey fkey(fixedSubdir + ".file");
2326 const ProKey skey(fixedSubdir + ".subdir");
2327 if (!project->isEmpty(fkey)) {
2328 if (!project->isEmpty(skey))
2329 warn_msg(WarnLogic, "Cannot assign both file and subdir for subdir %s",
2330 subdirs[subdir].toLatin1().constData());
2331 ofile = project->first(fkey);
2332 fromFile = true;
2333 } else if (!project->isEmpty(skey)) {
2334 ofile = project->first(skey);
2335 fromFile = false;
2336 } else {
2337 fromFile = ofile.endsWith(Option::pro_ext);
2338 }
2339 QString file = Option::fixPathToTargetOS(ofile.toQString());
2340
2341 if(fromFile) {
2342 int slsh = file.lastIndexOf(Option::dir_sep);
2343 if(slsh != -1) {
2344 st->in_directory = file.left(slsh+1);
2345 st->profile = file.mid(slsh+1);
2346 } else {
2347 st->profile = file;
2348 }
2349 } else {
2350 if (!file.isEmpty() && !project->isActiveConfig("subdir_first_pro")) {
2351 const QString baseName = file.section(Option::dir_sep, -1);
2352 if (baseName.isEmpty()) {
2353 warn_msg(WarnLogic, "Ignoring invalid SUBDIRS entry %s",
2354 subdirs[subdir].toLatin1().constData());
2355 continue;
2356 }
2357 st->profile = baseName + Option::pro_ext;
2358 }
2359 st->in_directory = file;
2360 }
2361 while(st->in_directory.endsWith(Option::dir_sep))
2362 st->in_directory.chop(1);
2363 if(fileInfo(st->in_directory).isRelative())
2364 st->out_directory = st->in_directory;
2365 else
2366 st->out_directory = fileFixify(st->in_directory, FileFixifyBackwards);
2367 const ProKey mkey(fixedSubdir + ".makefile");
2368 if (!project->isEmpty(mkey)) {
2369 st->makefile = project->first(mkey).toQString();
2370 } else {
2371 st->makefile = "Makefile";
2372 if(!st->profile.isEmpty()) {
2373 QString basename = st->in_directory;
2374 int new_slsh = basename.lastIndexOf(Option::dir_sep);
2375 if(new_slsh != -1)
2376 basename = basename.mid(new_slsh+1);
2377 if(st->profile != basename + Option::pro_ext)
2378 st->makefile += "." + st->profile.left(st->profile.length() - Option::pro_ext.length());
2379 }
2380 }
2381 const ProKey dkey(fixedSubdir + ".depends");
2382 if (!project->isEmpty(dkey)) {
2383 const ProStringList &depends = project->values(dkey);
2384 for(int depend = 0; depend < depends.size(); ++depend) {
2385 bool found = false;
2386 for(int subDep = 0; subDep < subdirs.size(); ++subDep) {
2387 if(subdirs[subDep] == depends.at(depend)) {
2388 QString subName = subdirs[subDep].toQString();
2389 QString fixedSubDep = subName;
2390 fixedSubDep.replace(QRegularExpression("[^a-zA-Z0-9_]"),"-");
2391 const ProKey dtkey(fixedSubDep + ".target");
2392 if (!project->isEmpty(dtkey)) {
2393 st->depends += project->first(dtkey);
2394 } else {
2395 QString d = Option::fixPathToTargetOS(subName);
2396 const ProKey dfkey(fixedSubDep + ".file");
2397 if (!project->isEmpty(dfkey)) {
2398 d = project->first(dfkey).toQString();
2399 } else {
2400 const ProKey dskey(fixedSubDep + ".subdir");
2401 if (!project->isEmpty(dskey))
2402 d = project->first(dskey).toQString();
2403 }
2404 st->depends += "sub-" + d.replace(QRegularExpression("[^a-zA-Z0-9_]"),"-");
2405 }
2406 found = true;
2407 break;
2408 }
2409 }
2410 if(!found) {
2411 QString depend_str = depends.at(depend).toQString();
2412 st->depends += depend_str.replace(QRegularExpression("[^a-zA-Z0-9_]"),"-");
2413 }
2414 }
2415 }
2416 const ProKey tkey(fixedSubdir + ".target");
2417 if (!project->isEmpty(tkey)) {
2418 st->target = project->first(tkey).toQString();
2419 } else {
2420 st->target = "sub-" + file;
2421 st->target.replace(QRegularExpression("[^a-zA-Z0-9_]"), "-");
2422 }
2423 }
2424 }
2425 return targets;
2426}
2427
2428void
2429MakefileGenerator::writeSubDirs(QTextStream &t)
2430{
2431 QList<SubTarget*> targets = findSubDirsSubTargets();
2432 t << "first: make_first\n";
2433 int flags = SubTargetInstalls;
2434 if(project->isActiveConfig("ordered"))
2435 flags |= SubTargetOrdered;
2436 writeSubTargets(t, targets, flags);
2437 qDeleteAll(targets);
2438}
2439
2440void MakefileGenerator::writeSubMakeCall(QTextStream &t, const QString &callPrefix,
2441 const QString &makeArguments)
2442{
2443 t << callPrefix << "$(MAKE)" << makeArguments << Qt::endl;
2444}
2445
2446void
2447MakefileGenerator::writeSubTargetCall(QTextStream &t,
2448 const QString &in_directory, const QString &in, const QString &out_directory, const QString &out,
2449 const QString &out_directory_cdin, const QString &makefilein)
2450{
2451 QString pfx;
2452 if (!in.isEmpty()) {
2453 if (!in_directory.isEmpty())
2454 t << "\n\t" << mkdir_p_asstring(out_directory);
2455 pfx = "( " + chkexists.arg(out) +
2456 + " $(QMAKE) -o " + out + ' ' + in + buildArgs(false)
2457 + " ) && ";
2458 }
2459 writeSubMakeCall(t, out_directory_cdin + pfx, makefilein);
2460}
2461
2462static void chopEndLines(QString *s)
2463{
2464 while (!s->isEmpty()) {
2465 const ushort c = s->at(s->size() - 1).unicode();
2466 if (c != '\n' && c != '\r')
2467 break;
2468 s->chop(1);
2469 }
2470}
2471
2472void
2473MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubTarget*> targets, int flags)
2474{
2475 // blasted includes
2476 const ProStringList &qeui = project->values("QMAKE_EXTRA_INCLUDES");
2477 for (ProStringList::ConstIterator qeui_it = qeui.begin(); qeui_it != qeui.end(); ++qeui_it)
2478 t << "include " << (*qeui_it) << Qt::endl;
2479
2480 if (!(flags & SubTargetSkipDefaultVariables)) {
2481 writeDefaultVariables(t);
2482 t << "SUBTARGETS = "; // subtargets are sub-directory
2483 for(int target = 0; target < targets.size(); ++target)
2484 t << " \\\n\t\t" << targets.at(target)->target;
2485 t << Qt::endl << Qt::endl;
2486 }
2487 writeExtraVariables(t);
2488
2489 QStringList targetSuffixes;
2490 const QString abs_source_path = project->first("QMAKE_ABSOLUTE_SOURCE_PATH").toQString();
2491 if (!(flags & SubTargetSkipDefaultTargets)) {
2492 targetSuffixes << "make_first" << "all" << "clean" << "distclean"
2493 << QString((flags & SubTargetInstalls) ? "install_subtargets" : "install")
2494 << QString((flags & SubTargetInstalls) ? "uninstall_subtargets" : "uninstall");
2495 }
2496
2497 struct SequentialInstallData
2498 {
2499 QString targetPrefix;
2500 QString commands;
2501 QTextStream commandsStream;
2502 SequentialInstallData() : commandsStream(&commands) {}
2503 };
2504 std::unique_ptr<SequentialInstallData> sequentialInstallData;
2505 bool dont_recurse = project->isActiveConfig("dont_recurse");
2506
2507 // generate target rules
2508 for(int target = 0; target < targets.size(); ++target) {
2509 SubTarget *subtarget = targets.at(target);
2510 QString in_directory = subtarget->in_directory;
2511 if(!in_directory.isEmpty() && !in_directory.endsWith(Option::dir_sep))
2512 in_directory += Option::dir_sep;
2513 QString out_directory = subtarget->out_directory;
2514 if(!out_directory.isEmpty() && !out_directory.endsWith(Option::dir_sep))
2515 out_directory += Option::dir_sep;
2516 if(!abs_source_path.isEmpty() && out_directory.startsWith(abs_source_path))
2517 out_directory = Option::output_dir + out_directory.mid(abs_source_path.length());
2518
2519 QString out_directory_cdin = out_directory.isEmpty() ? QString("\n\t")
2520 : "\n\tcd " + escapeFilePath(out_directory) + " && ";
2521 QString makefilein = " -f " + escapeFilePath(subtarget->makefile);
2522
2523 //qmake it
2524 QString out;
2525 QString in;
2526 if(!subtarget->profile.isEmpty()) {
2527 out = subtarget->makefile;
2528 in = escapeFilePath(fileFixify(in_directory + subtarget->profile, FileFixifyAbsolute));
2529 if(out.startsWith(in_directory))
2530 out = out.mid(in_directory.length());
2531 out = escapeFilePath(out);
2532 t << subtarget->target << "-qmake_all: ";
2533 if (flags & SubTargetOrdered) {
2534 if (target)
2535 t << targets.at(target - 1)->target << "-qmake_all";
2536 } else {
2537 if (!subtarget->depends.isEmpty())
2538 t << valGlue(subtarget->depends, QString(), "-qmake_all ", "-qmake_all");
2539 }
2540 t << " FORCE\n\t";
2541 if(!in_directory.isEmpty()) {
2542 t << mkdir_p_asstring(out_directory)
2543 << out_directory_cdin;
2544 }
2545 t << "$(QMAKE) -o " << out << ' ' << in << buildArgs(false);
2546 if (!dont_recurse)
2547 writeSubMakeCall(t, out_directory_cdin, makefilein + " qmake_all");
2548 else
2549 t << Qt::endl;
2550 }
2551
2552 { //actually compile
2553 t << subtarget->target << ":";
2554 auto extraDeps = extraSubTargetDependencies();
2555 if (!extraDeps.isEmpty())
2556 t << " " << valList(extraDeps);
2557 if(!subtarget->depends.isEmpty())
2558 t << " " << valList(subtarget->depends);
2559 t << " FORCE";
2560 writeSubTargetCall(t, in_directory, in, out_directory, out,
2561 out_directory_cdin, makefilein);
2562 }
2563
2564 for(int suffix = 0; suffix < targetSuffixes.size(); ++suffix) {
2565 QString s = targetSuffixes.at(suffix);
2566 if(s == "install_subtargets")
2567 s = "install";
2568 else if(s == "uninstall_subtargets")
2569 s = "uninstall";
2570 else if(s == "make_first")
2571 s = QString();
2572
2573 if (project->isActiveConfig("build_all") && s == "install") {
2574 if (!sequentialInstallData)
2575 sequentialInstallData.reset(new SequentialInstallData);
2576 sequentialInstallData->targetPrefix += subtarget->target + '-';
2577 writeSubTargetCall(sequentialInstallData->commandsStream, in_directory, in,
2578 out_directory, out, out_directory_cdin,
2579 makefilein + " " + s);
2580 chopEndLines(&sequentialInstallData->commands);
2581 }
2582
2583 if(flags & SubTargetOrdered) {
2584 t << subtarget->target << "-" << targetSuffixes.at(suffix) << "-ordered:";
2585 if(target)
2586 t << " " << targets.at(target-1)->target << "-" << targetSuffixes.at(suffix) << "-ordered ";
2587 t << " FORCE";
2588 writeSubTargetCall(t, in_directory, in, out_directory, out,
2589 out_directory_cdin, makefilein + " " + s);
2590 }
2591 t << subtarget->target << "-" << targetSuffixes.at(suffix) << ":";
2592 if(!subtarget->depends.isEmpty())
2593 t << " " << valGlue(subtarget->depends, QString(), "-" + targetSuffixes.at(suffix) + " ",
2594 "-"+targetSuffixes.at(suffix));
2595 t << " FORCE";
2596 writeSubTargetCall(t, in_directory, in, out_directory, out,
2597 out_directory_cdin, makefilein + " " + s);
2598 }
2599 }
2600 t << Qt::endl;
2601
2602 if (sequentialInstallData) {
2603 t << sequentialInstallData->targetPrefix << "install: FORCE"
2604 << sequentialInstallData->commands << Qt::endl << Qt::endl;
2605 }
2606
2607 if (!(flags & SubTargetSkipDefaultTargets)) {
2608 writeMakeQmake(t, true);
2609
2610 t << "qmake_all:";
2611 if(!targets.isEmpty()) {
2612 for(QList<SubTarget*>::Iterator it = targets.begin(); it != targets.end(); ++it) {
2613 if(!(*it)->profile.isEmpty())
2614 t << " " << (*it)->target << "-qmake_all";
2615 }
2616 }
2617 t << " FORCE\n\n";
2618 }
2619
2620 for(int s = 0; s < targetSuffixes.size(); ++s) {
2621 QString suffix = targetSuffixes.at(s);
2622 if(!(flags & SubTargetInstalls) && suffix.endsWith("install"))
2623 continue;
2624
2625 t << suffix << ":";
2626 for(int target = 0; target < targets.size(); ++target) {
2627 SubTarget *subTarget = targets.at(target);
2628 const ProStringList &config = project->values(ProKey(subTarget->name + ".CONFIG"));
2629 if (suffix == "make_first"
2630 && config.indexOf("no_default_target") != -1) {
2631 continue;
2632 }
2633 if((suffix == "install_subtargets" || suffix == "uninstall_subtargets")
2634 && config.indexOf("no_default_install") != -1) {
2635 continue;
2636 }
2637 QString targetRule = subTarget->target + "-" + suffix;
2638 if(flags & SubTargetOrdered)
2639 targetRule += "-ordered";
2640 t << " " << targetRule;
2641 }
2642 if(suffix == "all" || suffix == "make_first")
2643 t << ' ' << depVar("ALL_DEPS");
2644 if(suffix == "clean")
2645 t << ' ' << depVar("CLEAN_DEPS");
2646 else if (suffix == "distclean")
2647 t << ' ' << depVar("DISTCLEAN_DEPS");
2648 t << " FORCE\n";
2649 if(suffix == "clean") {
2650 t << fixFileVarGlue("QMAKE_CLEAN", "\t-$(DEL_FILE) ", "\n\t-$(DEL_FILE) ", "\n");
2651 } else if(suffix == "distclean") {
2652 QString ofile = fileFixify(Option::output.fileName());
2653 if(!ofile.isEmpty())
2654 t << "\t-$(DEL_FILE) " << escapeFilePath(ofile) << Qt::endl;
2655 t << fixFileVarGlue("QMAKE_DISTCLEAN", "\t-$(DEL_FILE) ", " ", "\n");
2656 }
2657 }
2658
2659 // user defined targets
2660 const ProStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
2661 for (ProStringList::ConstIterator qut_it = qut.begin(); qut_it != qut.end(); ++qut_it) {
2662 const ProStringList &config = project->values(ProKey(*qut_it + ".CONFIG"));
2663 QString targ = var(ProKey(*qut_it + ".target")),
2664 cmd = var(ProKey(*qut_it + ".commands")), deps;
2665 if(targ.isEmpty())
2666 targ = (*qut_it).toQString();
2667 t << Qt::endl;
2668
2669 const ProStringList &deplist = project->values(ProKey(*qut_it + ".depends"));
2670 for (ProStringList::ConstIterator dep_it = deplist.begin(); dep_it != deplist.end(); ++dep_it) {
2671 QString dep = var(ProKey(*dep_it + ".target"));
2672 if(dep.isEmpty())
2673 dep = (*dep_it).toQString();
2674 deps += ' ' + escapeDependencyPath(Option::fixPathToTargetOS(dep, false));
2675 }
2676 if (config.indexOf("recursive") != -1) {
2677 QSet<QString> recurse;
2678 const ProKey rkey(*qut_it + ".recurse");
2679 if (project->isSet(rkey)) {
2680 const QStringList values = project->values(rkey).toQStringList();
2681 recurse = QSet<QString>(values.begin(), values.end());
2682 } else {
2683 for(int target = 0; target < targets.size(); ++target)
2684 recurse.insert(targets.at(target)->name);
2685 }
2686 for(int target = 0; target < targets.size(); ++target) {
2687 SubTarget *subtarget = targets.at(target);
2688 QString in_directory = subtarget->in_directory;
2689 if(!in_directory.isEmpty() && !in_directory.endsWith(Option::dir_sep))
2690 in_directory += Option::dir_sep;
2691 QString out_directory = subtarget->out_directory;
2692 if(!out_directory.isEmpty() && !out_directory.endsWith(Option::dir_sep))
2693 out_directory += Option::dir_sep;
2694 if(!abs_source_path.isEmpty() && out_directory.startsWith(abs_source_path))
2695 out_directory = Option::output_dir + out_directory.mid(abs_source_path.length());
2696
2697 if(!recurse.contains(subtarget->name))
2698 continue;
2699
2700 QString out_directory_cdin = out_directory.isEmpty() ? QString("\n\t")
2701 : "\n\tcd " + escapeFilePath(out_directory) + " && ";
2702 QString makefilein = " -f " + escapeFilePath(subtarget->makefile);
2703
2704 QString out;
2705 QString in;
2706 if (!subtarget->profile.isEmpty()) {
2707 out = subtarget->makefile;
2708 in = escapeFilePath(fileFixify(in_directory + subtarget->profile, FileFixifyAbsolute));
2709 if (out.startsWith(in_directory))
2710 out = out.mid(in_directory.length());
2711 out = escapeFilePath(out);
2712 }
2713
2714 //write the rule/depends
2715 if(flags & SubTargetOrdered) {
2716 const QString dep = subtarget->target + "-" + (*qut_it) + "_ordered";
2717 t << dep << ":";
2718 if(target)
2719 t << " " << targets.at(target-1)->target << "-" << (*qut_it) << "_ordered ";
2720 deps += " " + dep;
2721 } else {
2722 const QString dep = subtarget->target + "-" + (*qut_it);
2723 t << dep << ":";
2724 if(!subtarget->depends.isEmpty())
2725 t << " " << valGlue(subtarget->depends, QString(), "-" + (*qut_it) + " ", "-" + (*qut_it));
2726 deps += " " + dep;
2727 }
2728
2729 QString sub_targ = targ;
2730 const ProKey rtkey(*qut_it + ".recurse_target");
2731 if (project->isSet(rtkey))
2732 sub_targ = project->first(rtkey).toQString();
2733
2734 //write the commands
2735 writeSubTargetCall(t, in_directory, in, out_directory, out,
2736 out_directory_cdin, makefilein + " " + sub_targ);
2737 }
2738 }
2739 if (config.indexOf("phony") != -1)
2740 deps += " FORCE";
2741 t << escapeDependencyPath(Option::fixPathToTargetOS(targ, false)) << ":" << deps << "\n";
2742 if(!cmd.isEmpty())
2743 t << "\t" << cmd << Qt::endl;
2744 }
2745
2746 if(flags & SubTargetInstalls) {
2747 project->values("INSTALLDEPS") += "install_subtargets";
2748 project->values("UNINSTALLDEPS") += "uninstall_subtargets";
2749 writeInstalls(t, true);
2750 }
2751 t << "FORCE:\n\n";
2752}
2753
2754void
2755MakefileGenerator::writeMakeQmake(QTextStream &t, bool noDummyQmakeAll)
2756{
2757 QString ofile = fileFixify(Option::output.fileName());
2758 if(project->isEmpty("QMAKE_FAILED_REQUIREMENTS") && !project->isEmpty("QMAKE_INTERNAL_PRL_FILE")) {
2759 QStringList files = escapeFilePaths(fileFixify(Option::mkfile::project_files));
2760 t << escapeDependencyPath(project->first("QMAKE_INTERNAL_PRL_FILE").toQString()) << ": \n\t"
2761 << "@$(QMAKE) -prl " << files.join(' ') << ' ' << buildArgs(true) << Qt::endl;
2762 }
2763
2764 QString qmake = "$(QMAKE)" + fullBuildArgs();
2765 if(!ofile.isEmpty() && !project->isActiveConfig("no_autoqmake")) {
2766 t << escapeDependencyPath(ofile) << ": "
2767 << escapeDependencyPath(fileFixify(project->projectFile())) << " ";
2768 if (Option::globals->do_cache) {
2769 if (!project->confFile().isEmpty())
2770 t << escapeDependencyPath(fileFixify(project->confFile())) << " ";
2771 if (!project->cacheFile().isEmpty())
2772 t << escapeDependencyPath(fileFixify(project->cacheFile())) << " ";
2773 }
2774 if(!specdir().isEmpty()) {
2775 if (exists(Option::normalizePath(specdir() + "/qmake.conf")))
2776 t << escapeDependencyPath(specdir() + Option::dir_sep + "qmake.conf") << " ";
2777 }
2778 const ProStringList &included = escapeDependencyPaths(project->values("QMAKE_INTERNAL_INCLUDED_FILES"));
2779 t << included.join(QString(" \\\n\t\t")) << "\n\t"
2780 << qmake << Qt::endl;
2781 const ProStringList &extraCommands = project->values("QMAKE_MAKE_QMAKE_EXTRA_COMMANDS");
2782 if (!extraCommands.isEmpty())
2783 t << "\t" << extraCommands.join(QString("\n\t")) << Qt::endl;
2784 for(int include = 0; include < included.size(); ++include) {
2785 const ProString &i = included.at(include);
2786 if(!i.isEmpty())
2787 t << i << ":\n";
2788 }
2789 }
2790 if(project->first("QMAKE_ORIG_TARGET") != "qmake") {
2791 t << "qmake: FORCE\n\t@" << qmake << Qt::endl << Qt::endl;
2792 if (!noDummyQmakeAll)
2793 t << "qmake_all: FORCE\n\n";
2794 }
2795}
2796
2797QFileInfo
2798MakefileGenerator::fileInfo(QString file) const
2799{
2800 static QHash<FileInfoCacheKey, QFileInfo> *cache = nullptr;
2801 static QFileInfo noInfo = QFileInfo();
2802 if(!cache) {
2803 cache = new QHash<FileInfoCacheKey, QFileInfo>;
2804 qmakeAddCacheClear(qmakeDeleteCacheClear<QHash<FileInfoCacheKey, QFileInfo> >, (void**)&cache);
2805 }
2806 FileInfoCacheKey cacheKey(file);
2807 QFileInfo value = cache->value(cacheKey, noInfo);
2808 if (value != noInfo)
2809 return value;
2810
2811 QFileInfo fi(file);
2812 if (fi.exists())
2813 cache->insert(cacheKey, fi);
2814 return fi;
2815}
2816
2817MakefileGenerator::LibFlagType
2818MakefileGenerator::parseLibFlag(const ProString &flag, ProString *arg)
2819{
2820 if (flag.startsWith("-L")) {
2821 *arg = flag.mid(2);
2822 return LibFlagPath;
2823 }
2824 if (flag.startsWith("-l")) {
2825 *arg = flag.mid(2);
2826 return LibFlagLib;
2827 }
2828 if (flag.startsWith('-'))
2829 return LibFlagOther;
2830 return LibFlagFile;
2831}
2832
2833ProStringList
2834MakefileGenerator::fixLibFlags(const ProKey &var)
2835{
2836 const ProStringList &in = project->values(var);
2837 ProStringList ret;
2838
2839 ret.reserve(in.length());
2840 for (const ProString &v : in)
2841 ret << fixLibFlag(v);
2842 return ret;
2843}
2844
2845ProString MakefileGenerator::fixLibFlag(const ProString &)
2846{
2847 qFatal("MakefileGenerator::fixLibFlag() called");
2848 return ProString();
2849}
2850
2851ProString
2852MakefileGenerator::escapeFilePath(const ProString &path) const
2853{
2854 return ProString(escapeFilePath(path.toQString()));
2855}
2856
2857QStringList
2858MakefileGenerator::escapeFilePaths(const QStringList &paths) const
2859{
2860 QStringList ret;
2861 for(int i = 0; i < paths.size(); ++i)
2862 ret.append(escapeFilePath(paths.at(i)));
2863 return ret;
2864}
2865
2866ProStringList
2867MakefileGenerator::escapeFilePaths(const ProStringList &paths) const
2868{
2869 ProStringList ret;
2870 const int size = paths.size();
2871 ret.reserve(size);
2872 for (int i = 0; i < size; ++i)
2873 ret.append(escapeFilePath(paths.at(i)));
2874 return ret;
2875}
2876
2877QString
2878MakefileGenerator::escapeDependencyPath(const QString &path) const
2879{
2880 QString ret = path;
2881 if (!ret.isEmpty()) {
2882 // Unix make semantics, to be inherited by unix and mingw generators.
2883#ifdef Q_OS_UNIX
2884 // When running on Unix, we need to escape colons (which may appear
2885 // anywhere in a path, and would be mis-parsed as dependency separators).
2886 static const QRegularExpression criticalChars(QStringLiteral("([\t :#])"));
2887#else
2888 // MinGW make has a hack for colons which denote drive letters, and no
2889 // other colons may appear in paths. And escaping colons actually breaks
2890 // the make from the Android SDK.
2891 static const QRegularExpression criticalChars(QStringLiteral("([\t #])"));
2892#endif
2893 ret.replace(criticalChars, QStringLiteral("\\\\1"));
2894 ret.replace(QLatin1Char('='), QStringLiteral("$(EQ)"));
2895 debug_msg(2, "escapeDependencyPath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData());
2896 }
2897 return ret;
2898}
2899
2900ProString
2901MakefileGenerator::escapeDependencyPath(const ProString &path) const
2902{
2903 return ProString(escapeDependencyPath(path.toQString()));
2904}
2905
2906QStringList
2907MakefileGenerator::escapeDependencyPaths(const QStringList &paths) const
2908{
2909 QStringList ret;
2910 const int size = paths.size();
2911 ret.reserve(size);
2912 for (int i = 0; i < size; ++i)
2913 ret.append(escapeDependencyPath(paths.at(i)));
2914 return ret;
2915}
2916
2917ProStringList
2918MakefileGenerator::escapeDependencyPaths(const ProStringList &paths) const
2919{
2920 ProStringList ret;
2921 const int size = paths.size();
2922 ret.reserve(size);
2923 for (int i = 0; i < size; ++i)
2924 ret.append(escapeDependencyPath(paths.at(i).toQString()));
2925 return ret;
2926}
2927
2928QStringList
2929MakefileGenerator::finalizeDependencyPaths(const QStringList &paths) const
2930{
2931 QStringList ret;
2932 const int size = paths.size();
2933 ret.reserve(size);
2934 for (int i = 0; i < size; ++i)
2935 ret.append(escapeDependencyPath(Option::fixPathToTargetOS(paths.at(i), false)));
2936 return ret;
2937}
2938
2939QStringList
2940MakefileGenerator::fileFixify(const QStringList &files, FileFixifyTypes fix, bool canon) const
2941{
2942 if(files.isEmpty())
2943 return files;
2944 QStringList ret;
2945 for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
2946 if(!(*it).isEmpty())
2947 ret << fileFixify((*it), fix, canon);
2948 }
2949 return ret;
2950}
2951
2952QString
2953MakefileGenerator::fileFixify(const QString &file, FileFixifyTypes fix, bool canon) const
2954{
2955 if(file.isEmpty())
2956 return file;
2957 QString ret = file;
2958
2959 //do the fixin'
2960 QString orig_file = ret;
2961 if(ret.startsWith(QLatin1Char('~'))) {
2962 if(ret.startsWith(QLatin1String("~/")))
2963 ret = QDir::homePath() + ret.mid(1);
2964 else
2965 warn_msg(WarnLogic, "Unable to expand ~ in %s", ret.toLatin1().constData());
2966 }
2967 if ((fix & FileFixifyAbsolute)
2968 || (!(fix & FileFixifyRelative) && project->isActiveConfig("no_fixpath"))) {
2969 if ((fix & FileFixifyAbsolute) && QDir::isRelativePath(ret)) {
2970 QString pwd = !(fix & FileFixifyFromOutdir) ? project->projectDir() : Option::output_dir;
2971 {
2972 QFileInfo in_fi(fileInfo(pwd));
2973 if (in_fi.exists())
2974 pwd = in_fi.canonicalFilePath();
2975 }
2976 if (!pwd.endsWith(QLatin1Char('/')))
2977 pwd += QLatin1Char('/');
2978 ret.prepend(pwd);
2979 }
2980 ret = Option::fixPathToTargetOS(ret, false, canon);
2981 } else { //fix it..
2982 QString out_dir = (fix & FileFixifyToIndir) ? project->projectDir() : Option::output_dir;
2983 QString in_dir = !(fix & FileFixifyFromOutdir) ? project->projectDir() : Option::output_dir;
2984 {
2985 QFileInfo in_fi(fileInfo(in_dir));
2986 if(in_fi.exists())
2987 in_dir = in_fi.canonicalFilePath();
2988 QFileInfo out_fi(fileInfo(out_dir));
2989 if(out_fi.exists())
2990 out_dir = out_fi.canonicalFilePath();
2991 }
2992
2993 QString qfile(Option::normalizePath(ret));
2994 QFileInfo qfileinfo(fileInfo(qfile));
2995 if(out_dir != in_dir || !qfileinfo.isRelative()) {
2996 if(qfileinfo.isRelative()) {
2997 ret = in_dir + "/" + qfile;
2998 qfileinfo.setFile(ret);
2999 }
3000 ret = Option::fixPathToTargetOS(ret, false, canon);
3001 QString match_dir = Option::fixPathToTargetOS(out_dir, false, canon);
3002 if(ret == match_dir) {
3003 ret = "";
3004 } else if(ret.startsWith(match_dir + Option::dir_sep)) {
3005 ret = ret.mid(match_dir.length() + Option::dir_sep.length());
3006 } else {
3007 //figure out the depth
3008 int depth = 4;
3009 if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
3010 Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
3011 if(project && !project->isEmpty("QMAKE_PROJECT_DEPTH"))
3012 depth = project->first("QMAKE_PROJECT_DEPTH").toInt();
3013 else if(Option::mkfile::cachefile_depth != -1)
3014 depth = Option::mkfile::cachefile_depth;
3015 }
3016 //calculate how much can be removed
3017 QString dot_prefix;
3018 for(int i = 1; i <= depth; i++) {
3019 int sl = match_dir.lastIndexOf(Option::dir_sep);
3020 if(sl == -1)
3021 break;
3022 match_dir = match_dir.left(sl);
3023 if(match_dir.isEmpty())
3024 break;
3025 if(ret.startsWith(match_dir + Option::dir_sep)) {
3026 //concat
3027 int remlen = ret.length() - (match_dir.length() + 1);
3028 if(remlen < 0)
3029 remlen = 0;
3030 ret = ret.right(remlen);
3031 //prepend
3032 for(int o = 0; o < i; o++)
3033 dot_prefix += ".." + Option::dir_sep;
3034 break;
3035 }
3036 }
3037 ret.prepend(dot_prefix);
3038 }
3039 } else {
3040 ret = Option::fixPathToTargetOS(ret, false, canon);
3041 }
3042 }
3043 if(ret.isEmpty())
3044 ret = ".";
3045 debug_msg(3, "Fixed[%d,%d] %s :: to :: %s [%s::%s]",
3046 int(fix), canon, orig_file.toLatin1().constData(), ret.toLatin1().constData(),
3047 qmake_getpwd().toLatin1().constData(), Option::output_dir.toLatin1().constData());
3048 return ret;
3049}
3050
3051QMakeLocalFileName
3052MakefileGenerator::fixPathForFile(const QMakeLocalFileName &file, bool forOpen)
3053{
3054 if(forOpen)
3055 return QMakeLocalFileName(fileFixify(file.real(), FileFixifyBackwards));
3056 return QMakeLocalFileName(fileFixify(file.real()));
3057}
3058
3059QFileInfo
3060MakefileGenerator::findFileInfo(const QMakeLocalFileName &file)
3061{
3062 return fileInfo(file.local());
3063}
3064
3065QMakeLocalFileName
3066MakefileGenerator::findFileForDep(const QMakeLocalFileName &dep, const QMakeLocalFileName &file)
3067{
3068 QMakeLocalFileName ret;
3069 if(!project->isEmpty("SKIP_DEPENDS")) {
3070 bool found = false;
3071 const ProStringList &nodeplist = project->values("SKIP_DEPENDS");
3072 for (ProStringList::ConstIterator it = nodeplist.begin();
3073 it != nodeplist.end(); ++it) {
3074 QRegularExpression regx((*it).toQString());
3075 if (regx.match(dep.local()).hasMatch()) {
3076 found = true;
3077 break;
3078 }
3079 }
3080 if(found)
3081 return ret;
3082 }
3083
3084 ret = QMakeSourceFileInfo::findFileForDep(dep, file);
3085 if(!ret.isNull())
3086 return ret;
3087
3088 //these are some "hacky" heuristics it will try to do on an include
3089 //however these can be turned off at runtime, I'm not sure how
3090 //reliable these will be, most likely when problems arise turn it off
3091 //and see if they go away..
3092 if(Option::mkfile::do_dep_heuristics) {
3093 if(depHeuristicsCache.contains(dep.real()))
3094 return depHeuristicsCache[dep.real()];
3095
3096 if(Option::output_dir != qmake_getpwd()
3097 && QDir::isRelativePath(dep.real())) { //is it from the shadow tree
3098 QList<QMakeLocalFileName> depdirs = QMakeSourceFileInfo::dependencyPaths();
3099 depdirs.prepend(fileInfo(file.real()).absoluteDir().path());
3100 QString pwd = qmake_getpwd();
3101 if(pwd.at(pwd.length()-1) != '/')
3102 pwd += '/';
3103 for(int i = 0; i < depdirs.count(); i++) {
3104 QString dir = depdirs.at(i).real();
3105 if(!QDir::isRelativePath(dir) && dir.startsWith(pwd))
3106 dir = dir.mid(pwd.length());
3107 if(QDir::isRelativePath(dir)) {
3108 if(!dir.endsWith(Option::dir_sep))
3109 dir += Option::dir_sep;
3110 QString shadow = fileFixify(dir + dep.local(), FileFixifyBackwards);
3111 if(exists(shadow)) {
3112 ret = QMakeLocalFileName(shadow);
3113 goto found_dep_from_heuristic;
3114 }
3115 }
3116 }
3117 }
3118 { //is it from an EXTRA_TARGET
3119 const QString dep_basename = dep.local().section('/', -1);
3120 const ProStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
3121 for (ProStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it) {
3122 QString targ = var(ProKey(*it + ".target"));
3123 if(targ.isEmpty())
3124 targ = (*it).toQString();
3125 QString out = Option::fixPathToTargetOS(targ);
3126 if(out == dep.real() || out.section(Option::dir_sep, -1) == dep_basename) {
3127 ret = QMakeLocalFileName(out);
3128 goto found_dep_from_heuristic;
3129 }
3130 }
3131 }
3132 { //is it from an EXTRA_COMPILER
3133 const QString dep_basename = dep.local().section('/', -1);
3134 const ProStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
3135 for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
3136 const ProString &tmp_out = project->first(ProKey(*it + ".output"));
3137 if(tmp_out.isEmpty())
3138 continue;
3139 const ProStringList &tmp = project->values(ProKey(*it + ".input"));
3140 for (ProStringList::ConstIterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
3141 const ProStringList &inputs = project->values((*it2).toKey());
3142 for (ProStringList::ConstIterator input = inputs.begin(); input != inputs.end(); ++input) {
3143 QString out = Option::fixPathToTargetOS(
3144 replaceExtraCompilerVariables(tmp_out.toQString(), (*input).toQString(), QString(), NoShell));
3145 if (out == dep.real() || out.section(Option::dir_sep, -1) == dep_basename) {
3146 ret = QMakeLocalFileName(fileFixify(out, FileFixifyBackwards));
3147 goto found_dep_from_heuristic;
3148 }
3149 }
3150 }
3151 }
3152 }
3153 found_dep_from_heuristic:
3154 depHeuristicsCache.insert(dep.real(), ret);
3155 }
3156 return ret;
3157}
3158
3159QStringList
3160&MakefileGenerator::findDependencies(const QString &file)
3161{
3162 const QString fixedFile = fileFixify(file);
3163 if(!dependsCache.contains(fixedFile)) {
3164#if 1
3165 QStringList deps = QMakeSourceFileInfo::dependencies(file);
3166 if(file != fixedFile)
3167 deps += QMakeSourceFileInfo::dependencies(fixedFile);
3168#else
3169 QStringList deps = QMakeSourceFileInfo::dependencies(fixedFile);
3170#endif
3171 dependsCache.insert(fixedFile, deps);
3172 }
3173 return dependsCache[fixedFile];
3174}
3175
3176QString
3177MakefileGenerator::specdir()
3178{
3179 if (spec.isEmpty())
3180 spec = fileFixify(project->specDir());
3181 return spec;
3182}
3183
3184bool
3185MakefileGenerator::openOutput(QFile &file, const QString &build) const
3186{
3187 debug_msg(3, "asked to open output file '%s' in %s",
3188 qPrintable(file.fileName()), qPrintable(Option::output_dir));
3189
3190 if (file.fileName().isEmpty()) {
3191 file.setFileName(!project->isEmpty("MAKEFILE")
3192 ? project->first("MAKEFILE").toQString() : "Makefile");
3193 }
3194
3195 file.setFileName(QDir(Option::output_dir).absoluteFilePath(file.fileName()));
3196
3197 if (!build.isEmpty())
3198 file.setFileName(file.fileName() + "." + build);
3199
3200 if (project->isEmpty("QMAKE_MAKEFILE"))
3201 project->values("QMAKE_MAKEFILE").append(file.fileName());
3202
3203 // Make required directories. Note that we do this based on the
3204 // filename, not Option::output_dir, as the filename may include
3205 // generator specific directories not included in output_dir.
3206 int slsh = file.fileName().lastIndexOf('/');
3207 if (slsh != -1)
3208 mkdir(file.fileName().left(slsh));
3209
3210 debug_msg(3, "opening output file %s", qPrintable(file.fileName()));
3211 return file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
3212}
3213
3214QString
3215MakefileGenerator::pkgConfigFileName(bool fixify)
3216{
3217 QString ret = project->first("QMAKE_PKGCONFIG_FILE").toQString();
3218 if (ret.isEmpty()) {
3219 ret = project->first("TARGET").toQString();
3220 int slsh = ret.lastIndexOf(Option::dir_sep);
3221 if (slsh != -1)
3222 ret = ret.right(ret.length() - slsh - 1);
3223 if (ret.startsWith("lib"))
3224 ret = ret.mid(3);
3225 int dot = ret.indexOf('.');
3226 if (dot != -1)
3227 ret = ret.left(dot);
3228 }
3229 ret += Option::pkgcfg_ext;
3230 QString subdir = project->first("QMAKE_PKGCONFIG_DESTDIR").toQString();
3231 if(!subdir.isEmpty()) {
3232 // initOutPaths() appends dir_sep, but just to be safe..
3233 if (!subdir.endsWith(Option::dir_sep))
3234 ret.prepend(Option::dir_sep);
3235 ret.prepend(subdir);
3236 }
3237 if(fixify) {
3238 if(QDir::isRelativePath(ret) && !project->isEmpty("DESTDIR"))
3239 ret.prepend(project->first("DESTDIR").toQString());
3240 ret = fileFixify(ret, FileFixifyBackwards);
3241 }
3242 return ret;
3243}
3244
3245QString
3246MakefileGenerator::pkgConfigPrefix() const
3247{
3248 if(!project->isEmpty("QMAKE_PKGCONFIG_PREFIX"))
3249 return project->first("QMAKE_PKGCONFIG_PREFIX").toQString();
3250 return project->propertyValue(ProKey("QT_INSTALL_PREFIX")).toQString();
3251}
3252
3253QString
3254MakefileGenerator::pkgConfigFixPath(QString path) const
3255{
3256 QString prefix = pkgConfigPrefix();
3257 if(path.startsWith(prefix))
3258 path.replace(prefix, QLatin1String("${prefix}"));
3259 return path;
3260}
3261
3262void
3263MakefileGenerator::writePkgConfigFile()
3264{
3265 QString fname = pkgConfigFileName();
3266 mkdir(fileInfo(fname).path());
3267 QFile ft(fname);
3268 if(!ft.open(QIODevice::WriteOnly))
3269 return;
3270 QString ffname(fileFixify(fname));
3271 project->values("ALL_DEPS").append(ffname);
3272 project->values("QMAKE_DISTCLEAN").append(ffname);
3273 QTextStream t(&ft);
3274
3275 QString prefix = pkgConfigPrefix();
3276 QString libDir = project->first("QMAKE_PKGCONFIG_LIBDIR").toQString();
3277 if(libDir.isEmpty())
3278 libDir = prefix + "/lib";
3279 QString includeDir = project->first("QMAKE_PKGCONFIG_INCDIR").toQString();
3280 if(includeDir.isEmpty())
3281 includeDir = prefix + "/include";
3282
3283 t << "prefix=" << prefix << Qt::endl;
3284 t << "exec_prefix=${prefix}\n"
3285 << "libdir=" << pkgConfigFixPath(libDir) << "\n"
3286 << "includedir=" << pkgConfigFixPath(includeDir) << Qt::endl;
3287 t << Qt::endl;
3288
3289 //extra PKGCONFIG variables
3290 const ProStringList &pkgconfig_vars = project->values("QMAKE_PKGCONFIG_VARIABLES");
3291 for(int i = 0; i < pkgconfig_vars.size(); ++i) {
3292 const ProString &var = project->first(ProKey(pkgconfig_vars.at(i) + ".name"));
3293 QString val = project->values(ProKey(pkgconfig_vars.at(i) + ".value")).join(' ');
3294 if(var.isEmpty())
3295 continue;
3296 if(val.isEmpty()) {
3297 const ProStringList &var_vars = project->values(ProKey(pkgconfig_vars.at(i) + ".variable"));
3298 for(int v = 0; v < var_vars.size(); ++v) {
3299 const ProStringList &vars = project->values(var_vars.at(v).toKey());
3300 for(int var = 0; var < vars.size(); ++var) {
3301 if(!val.isEmpty())
3302 val += " ";
3303 val += pkgConfigFixPath(vars.at(var).toQString());
3304 }
3305 }
3306 }
3307 if (!val.isEmpty())
3308 t << var << "=" << val << Qt::endl;
3309 }
3310
3311 t << Qt::endl;
3312
3313 QString name = project->first("QMAKE_PKGCONFIG_NAME").toQString();
3314 if(name.isEmpty()) {
3315 name = project->first("QMAKE_ORIG_TARGET").toQString().toLower();
3316 name.replace(0, 1, name[0].toUpper());
3317 }
3318 t << "Name: " << name << Qt::endl;
3319 QString desc = project->values("QMAKE_PKGCONFIG_DESCRIPTION").join(' ');
3320 if(desc.isEmpty()) {
3321 if(name.isEmpty()) {
3322 desc = project->first("QMAKE_ORIG_TARGET").toQString().toLower();
3323 desc.replace(0, 1, desc[0].toUpper());
3324 } else {
3325 desc = name;
3326 }
3327 if(project->first("TEMPLATE") == "lib") {
3328 if(project->isActiveConfig("plugin"))
3329 desc += " Plugin";
3330 else
3331 desc += " Library";
3332 } else if(project->first("TEMPLATE") == "app") {
3333 desc += " Application";
3334 }
3335 }
3336 t << "Description: " << desc << Qt::endl;
3337 ProString version = project->first("QMAKE_PKGCONFIG_VERSION");
3338 if (version.isEmpty())
3339 version = project->first("VERSION");
3340 if (!version.isEmpty())
3341 t << "Version: " << version << Qt::endl;
3342
3343 if (project->first("TEMPLATE") == "lib") {
3344 // libs
3345 t << "Libs: ";
3346 QString pkgConfiglibName;
3347 if (target_mode == TARG_MAC_MODE && project->isActiveConfig("lib_bundle")) {
3348 if (libDir != QLatin1String("/Library/Frameworks"))
3349 t << "-F${libdir} ";
3350 ProString bundle;
3351 if (!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME"))
3352 bundle = project->first("QMAKE_FRAMEWORK_BUNDLE_NAME");
3353 else
3354 bundle = project->first("TARGET");
3355 int suffix = bundle.lastIndexOf(".framework");
3356 if (suffix != -1)
3357 bundle = bundle.left(suffix);
3358 t << "-framework ";
3359 pkgConfiglibName = bundle.toQString();
3360 } else {
3361 if (!project->values("QMAKE_DEFAULT_LIBDIRS").contains(libDir))
3362 t << "-L${libdir} ";
3363 pkgConfiglibName = "-l" + project->first("QMAKE_ORIG_TARGET");
3364 if (project->isActiveConfig("shared"))
3365 pkgConfiglibName += project->first("TARGET_VERSION_EXT").toQString();
3366 }
3367 t << shellQuote(pkgConfiglibName) << " \n";
3368
3369 if (project->isActiveConfig("staticlib")) {
3370 ProStringList libs;
3371 libs << "LIBS"; // FIXME: this should not be conditional on staticlib
3372 libs << "LIBS_PRIVATE";
3373 libs << "QMAKE_LIBS"; // FIXME: this should not be conditional on staticlib
3374 libs << "QMAKE_LIBS_PRIVATE";
3375 libs << "QMAKE_LFLAGS_THREAD"; //not sure about this one, but what about things like -pthread?
3376 t << "Libs.private:";
3377 for (ProStringList::ConstIterator it = libs.cbegin(); it != libs.cend(); ++it)
3378 t << ' ' << fixLibFlags((*it).toKey()).join(' ');
3379 t << Qt::endl;
3380 }
3381 }
3382
3383 // flags
3384 // ### too many
3385 t << "Cflags: "
3386 // << var("QMAKE_CXXFLAGS") << " "
3387 << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ")
3388 << varGlue("PRL_EXPORT_CXXFLAGS", "", " ", " ")
3389 << varGlue("QMAKE_PKGCONFIG_CFLAGS", "", " ", " ")
3390 // << varGlue("DEFINES","-D"," -D"," ")
3391 ;
3392 if (!project->values("QMAKE_DEFAULT_INCDIRS").contains(includeDir))
3393 t << "-I${includedir}";
3394 if (target_mode == TARG_MAC_MODE && project->isActiveConfig("lib_bundle")
3395 && libDir != QLatin1String("/Library/Frameworks")) {
3396 t << " -F${libdir}";
3397 }
3398 t << Qt::endl;
3399
3400 // requires
3401 const QString requiresString = project->values("QMAKE_PKGCONFIG_REQUIRES").join(' ');
3402 if (!requiresString.isEmpty()) {
3403 t << "Requires: " << requiresString << Qt::endl;
3404 }
3405
3406 t << Qt::endl;
3407}
3408
3409static QString windowsifyPath(const QString &str)
3410{
3411 // The paths are escaped in prl files, so every slash needs to turn into two backslashes.
3412 // Then each backslash needs to be escaped for sed. And another level for C quoting here.
3413 return QString(str).replace('/', QLatin1String("\\\\\\\\"));
3414}
3415
3416QString MakefileGenerator::installMetaFile(const ProKey &replace_rule, const QString &src, const QString &dst)
3417{
3418 QString ret;
3419 if (project->isEmpty(replace_rule)
3420 || project->isActiveConfig("no_sed_meta_install")) {
3421 ret += "$(INSTALL_FILE) " + escapeFilePath(src) + ' ' + escapeFilePath(dst);
3422 } else {
3423 QString sedargs;
3424 const ProStringList &replace_rules = project->values(replace_rule);
3425 for (int r = 0; r < replace_rules.size(); ++r) {
3426 const ProString match = project->first(ProKey(replace_rules.at(r) + ".match")),
3427 replace = project->first(ProKey(replace_rules.at(r) + ".replace"));
3428 if (!match.isEmpty() /*&& match != replace*/) {
3429 sedargs += " -e " + shellQuote("s," + match + "," + replace + ",g");
3430 if (isWindowsShell() && project->first(ProKey(replace_rules.at(r) + ".CONFIG")).contains("path"))
3431 sedargs += " -e " + shellQuote("s," + windowsifyPath(match.toQString())
3432 + "," + windowsifyPath(replace.toQString()) + ",gi");
3433 }
3434 }
3435 if (sedargs.isEmpty()) {
3436 ret += "$(INSTALL_FILE) " + escapeFilePath(src) + ' ' + escapeFilePath(dst);
3437 } else {
3438 ret += "$(SED) " + sedargs + ' ' + escapeFilePath(src) + " > " + escapeFilePath(dst);
3439 }
3440 }
3441 return ret;
3442}
3443
3444QString MakefileGenerator::shellQuote(const QString &str)
3445{
3446 return isWindowsShell() ? IoUtils::shellQuoteWin(str) : IoUtils::shellQuoteUnix(str);
3447}
3448
3449/*
3450 * Returns the name of the variable that contains the fully resolved target
3451 * (including DESTDIR) of this generator.
3452 */
3453ProKey MakefileGenerator::fullTargetVariable() const
3454{
3455 return "TARGET";
3456}
3457
3458QString MakefileGenerator::createResponseFile(
3459 const QString &baseName,
3460 const ProStringList &objList,
3461 const QString &prefix)
3462{
3463 QString fileName = baseName + '.' + fileVar("QMAKE_ORIG_TARGET");
3464 if (!var("BUILD_NAME").isEmpty())
3465 fileName += '.' + var("BUILD_NAME");
3466 if (!var("MAKEFILE").isEmpty())
3467 fileName += '.' + var("MAKEFILE");
3468 QString filePath = Option::output_dir + QDir::separator() + fileName;
3469 QFile file(filePath);
3470 if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
3471 return QString();
3472 QTextStream t(&file);
3473 for (ProStringList::ConstIterator it = objList.constBegin(); it != objList.constEnd(); ++it) {
3474 QString path = (*it).toQString();
3475 // In response files, whitespace and special characters are
3476 // escaped with a backslash; backslashes themselves can either
3477 // be escaped into double backslashes, or, as this is a list of
3478 // path names, converted to forward slashes.
3479 path.replace(QLatin1Char('\\'), QLatin1String("/"))
3480 .replace(QLatin1Char(' '), QLatin1String("\\ "))
3481 .replace(QLatin1Char('\t'), QLatin1String("\\\t"))
3482 .replace(QLatin1Char('"'), QLatin1String("\\\""))
3483 .replace(QLatin1Char('\''), QLatin1String("\\'"));
3484 t << prefix << path << Qt::endl;
3485 }
3486 t.flush();
3487 file.close();
3488 return fileName;
3489}
3490
3491QT_END_NAMESPACE
3492