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 "unixmake.h"
30 #include "option.h"
31 #include <qfile.h>
32 #include <qhash.h>
33 #include <qdir.h>
34 #include <time.h>
35 #include <qdebug.h>
36
37 QT_BEGIN_NAMESPACE
38
libdirToFlags(const ProKey & key)39 ProStringList UnixMakefileGenerator::libdirToFlags(const ProKey &key)
40 {
41 ProStringList results;
42 for (const auto &libdir : qAsConst(project->values(key))) {
43 if (!project->isEmpty("QMAKE_LFLAGS_RPATH") && project->isActiveConfig("rpath_libdirs"))
44 project->values("QMAKE_LFLAGS") += var("QMAKE_LFLAGS_RPATH") + libdir;
45 results.append("-L" + escapeFilePath(libdir));
46 }
47 return results;
48 }
49
50 void
init()51 UnixMakefileGenerator::init()
52 {
53 ProStringList &configs = project->values("CONFIG");
54 if(project->isEmpty("ICON") && !project->isEmpty("RC_FILE"))
55 project->values("ICON") = project->values("RC_FILE");
56 if(project->isEmpty("QMAKE_EXTENSION_PLUGIN"))
57 project->values("QMAKE_EXTENSION_PLUGIN").append(project->first("QMAKE_EXTENSION_SHLIB"));
58
59 project->values("QMAKE_ORIG_TARGET") = project->values("TARGET");
60
61 //version handling
62 if (project->isEmpty("VERSION")) {
63 project->values("VERSION").append(
64 "1.0." + (project->isEmpty("VER_PAT") ? QString("0") : project->first("VER_PAT")));
65 }
66 QStringList l = project->first("VERSION").toQString().split('.');
67 l << "0" << "0"; //make sure there are three
68 project->values("VER_MAJ").append(l[0]);
69 project->values("VER_MIN").append(l[1]);
70 project->values("VER_PAT").append(l[2]);
71
72 QString sroot = project->sourceRoot();
73 for (const ProString &iif : project->values("QMAKE_INTERNAL_INCLUDED_FILES")) {
74 if (iif == project->cacheFile())
75 continue;
76 if (iif.startsWith(sroot) && iif.at(sroot.length()) == QLatin1Char('/'))
77 project->values("DISTFILES") += fileFixify(iif.toQString(), FileFixifyRelative);
78 }
79
80 /* this should probably not be here, but I'm using it to wrap the .t files */
81 if(project->first("TEMPLATE") == "app")
82 project->values("QMAKE_APP_FLAG").append("1");
83 else if(project->first("TEMPLATE") == "lib")
84 project->values("QMAKE_LIB_FLAG").append("1");
85 else if(project->first("TEMPLATE") == "subdirs") {
86 MakefileGenerator::init();
87 if(project->isEmpty("MAKEFILE"))
88 project->values("MAKEFILE").append("Makefile");
89 return; /* subdirs is done */
90 }
91
92 project->values("QMAKE_ORIG_DESTDIR") = project->values("DESTDIR");
93 if((!project->isEmpty("QMAKE_LIB_FLAG") && !project->isActiveConfig("staticlib")) ||
94 (project->isActiveConfig("qt") && project->isActiveConfig("plugin"))) {
95 if(configs.indexOf("dll") == -1) configs.append("dll");
96 } else if(!project->isEmpty("QMAKE_APP_FLAG") || project->isActiveConfig("dll")) {
97 configs.removeAll("staticlib");
98 }
99 if(!project->isEmpty("QMAKE_INCREMENTAL"))
100 project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_INCREMENTAL");
101 else if(!project->isEmpty("QMAKE_LFLAGS_PREBIND") &&
102 !project->values("QMAKE_LIB_FLAG").isEmpty() &&
103 project->isActiveConfig("dll"))
104 project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_PREBIND");
105 project->values("QMAKE_INCDIR") += project->values("QMAKE_INCDIR_POST");
106 project->values("QMAKE_RPATHDIR") += project->values("QMAKE_RPATHDIR_POST");
107 project->values("QMAKE_RPATHLINKDIR") += project->values("QMAKE_RPATHLINKDIR_POST");
108 if(!project->isEmpty("QMAKE_INCDIR"))
109 project->values("INCLUDEPATH") += project->values("QMAKE_INCDIR");
110 // The order of the next two lines is relevant due to side effect on QMAKE_LFLAGS.
111 ProStringList ldadd = project->values("QMAKE_LIBDIR_FLAGS") + libdirToFlags("QMAKE_LIBDIR");
112 ProStringList ldaddpost = libdirToFlags("QMAKE_LIBDIR_POST");
113 if (project->isActiveConfig("mac")) {
114 if (!project->isEmpty("QMAKE_FRAMEWORKPATH")) {
115 const ProStringList &fwdirs = project->values("QMAKE_FRAMEWORKPATH");
116 for (int i = 0; i < fwdirs.size(); ++i)
117 project->values("QMAKE_FRAMEWORKPATH_FLAGS") += "-F" + escapeFilePath(fwdirs[i]);
118 }
119 ldadd += project->values("QMAKE_FRAMEWORKPATH_FLAGS");
120 }
121 ProStringList &qmklibs = project->values("LIBS");
122 qmklibs = ldadd + qmklibs;
123 ProStringList &qmklibspost = project->values("QMAKE_LIBS");
124 qmklibspost = ldaddpost + qmklibspost;
125 if (!project->isEmpty("QMAKE_RPATHDIR") && !project->isEmpty("QMAKE_LFLAGS_RPATH")) {
126 const ProStringList &rpathdirs = project->values("QMAKE_RPATHDIR");
127 for (int i = 0; i < rpathdirs.size(); ++i) {
128 QString rpathdir = rpathdirs[i].toQString();
129 if (rpathdir.length() > 1 && rpathdir.at(0) == '$' && rpathdir.at(1) != '(') {
130 rpathdir.replace(0, 1, "\\$$"); // Escape from make and the shell
131 } else if (!rpathdir.startsWith('@') && fileInfo(rpathdir).isRelative()) {
132 QString rpathbase = project->first("QMAKE_REL_RPATH_BASE").toQString();
133 if (rpathbase.isEmpty()) {
134 fprintf(stderr, "Error: This platform does not support relative paths in QMAKE_RPATHDIR (%s)\n",
135 rpathdir.toLatin1().constData());
136 continue;
137 }
138 if (rpathbase.startsWith('$'))
139 rpathbase.replace(0, 1, "\\$$"); // Escape from make and the shell
140 if (rpathdir == ".")
141 rpathdir = rpathbase;
142 else
143 rpathdir.prepend(rpathbase + '/');
144 project->values("QMAKE_LFLAGS").insertUnique(project->values("QMAKE_LFLAGS_REL_RPATH"));
145 }
146 project->values("QMAKE_LFLAGS") += var("QMAKE_LFLAGS_RPATH") + escapeFilePath(rpathdir);
147 }
148 }
149 if (!project->isEmpty("QMAKE_RPATHLINKDIR")) {
150 const ProStringList &rpathdirs = project->values("QMAKE_RPATHLINKDIR");
151 for (int i = 0; i < rpathdirs.size(); ++i) {
152 if (!project->isEmpty("QMAKE_LFLAGS_RPATHLINK"))
153 project->values("QMAKE_LFLAGS") += var("QMAKE_LFLAGS_RPATHLINK") + escapeFilePath(QFileInfo(rpathdirs[i].toQString()).absoluteFilePath());
154 }
155 }
156
157 if(project->isActiveConfig("GNUmake") && !project->isEmpty("QMAKE_CFLAGS_DEPS"))
158 include_deps = true; //do not generate deps
159
160 MakefileGenerator::init();
161
162 if (project->isActiveConfig("objective_c"))
163 project->values("QMAKE_BUILTIN_COMPILERS") << "OBJC" << "OBJCXX";
164
165 for (const ProString &compiler : project->values("QMAKE_BUILTIN_COMPILERS")) {
166 QString compile_flag = var("QMAKE_COMPILE_FLAG");
167 if(compile_flag.isEmpty())
168 compile_flag = "-c";
169
170 if(doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER")) {
171 QString pchFlags = var(ProKey("QMAKE_" + compiler + "FLAGS_USE_PRECOMPILE"));
172
173 QString pchBaseName;
174 if(!project->isEmpty("PRECOMPILED_DIR")) {
175 pchBaseName = Option::fixPathToTargetOS(project->first("PRECOMPILED_DIR").toQString());
176 if(!pchBaseName.endsWith(Option::dir_sep))
177 pchBaseName += Option::dir_sep;
178 }
179 pchBaseName += project->first("QMAKE_ORIG_TARGET").toQString();
180
181 // replace place holders
182 pchFlags.replace(QLatin1String("${QMAKE_PCH_INPUT}"),
183 escapeFilePath(project->first("PRECOMPILED_HEADER").toQString()));
184 pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT_BASE}"), escapeFilePath(pchBaseName));
185 if (project->isActiveConfig("icc_pch_style")) {
186 // icc style
187 pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT}"),
188 escapeFilePath(pchBaseName + project->first("QMAKE_PCH_OUTPUT_EXT")));
189 const ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS");
190 for (const ProString &arch : pchArchs) {
191 QString suffix = project->first("QMAKE_PCH_OUTPUT_EXT").toQString();
192 suffix.replace(QLatin1String("${QMAKE_PCH_ARCH}"), arch.toQString());
193 pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT_") + arch + QLatin1Char('}'),
194 escapeFilePath(pchBaseName + suffix));
195 }
196 } else {
197 // gcc style (including clang_pch_style)
198 QString headerSuffix;
199 if (project->isActiveConfig("clang_pch_style"))
200 headerSuffix = project->first("QMAKE_PCH_OUTPUT_EXT").toQString();
201
202 pchBaseName += project->first("QMAKE_PCH_OUTPUT_EXT").toQString();
203 pchBaseName += Option::dir_sep;
204
205 ProString language = project->first(ProKey("QMAKE_LANGUAGE_" + compiler));
206 if (!language.isEmpty()) {
207 pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT}"),
208 escapeFilePath(pchBaseName + language + headerSuffix));
209 const ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS");
210 for (const ProString &arch : pchArchs) {
211 QString file = pchBaseName + language + headerSuffix;
212 file.replace(QLatin1String("${QMAKE_PCH_ARCH}"), arch.toQString());
213 if (project->isActiveConfig("clang_pch_style")
214 && (file.endsWith(QLatin1String(".pch"))
215 || file.endsWith(QLatin1String(".gch")))) {
216 file.chop(4); // must omit header suffix for -include to recognize the PCH
217 }
218 pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT_") + arch + QLatin1Char('}'),
219 escapeFilePath(file));
220 }
221 }
222 }
223
224 if (!pchFlags.isEmpty())
225 compile_flag += " " + pchFlags;
226 }
227
228 QString compilerExecutable;
229 if (compiler == "C" || compiler == "OBJC") {
230 compilerExecutable = "$(CC)";
231 compile_flag += " $(CFLAGS)";
232 } else {
233 compilerExecutable = "$(CXX)";
234 compile_flag += " $(CXXFLAGS)";
235 }
236
237 compile_flag += " $(INCPATH)";
238
239 ProString compilerVariable = compiler;
240 if (compilerVariable == "C")
241 compilerVariable = ProString("CC");
242
243 const ProKey runComp("QMAKE_RUN_" + compilerVariable);
244 if(project->isEmpty(runComp))
245 project->values(runComp).append(compilerExecutable + " " + compile_flag + " " + var("QMAKE_CC_O_FLAG") + "$obj $src");
246 const ProKey runCompImp("QMAKE_RUN_" + compilerVariable + "_IMP");
247 if(project->isEmpty(runCompImp))
248 project->values(runCompImp).append(compilerExecutable + " " + compile_flag + " " + var("QMAKE_CC_O_FLAG") + "\"$@\" \"$<\"");
249 }
250
251 if (project->isActiveConfig("mac") && !project->isEmpty("TARGET") &&
252 ((project->isActiveConfig("build_pass") || project->isEmpty("BUILDS")))) {
253 ProString bundle;
254 if(project->isActiveConfig("bundle") && !project->isEmpty("QMAKE_BUNDLE_EXTENSION")) {
255 bundle = project->first("TARGET");
256 if(!project->isEmpty("QMAKE_BUNDLE_NAME"))
257 bundle = project->first("QMAKE_BUNDLE_NAME");
258 if(!bundle.endsWith(project->first("QMAKE_BUNDLE_EXTENSION")))
259 bundle += project->first("QMAKE_BUNDLE_EXTENSION");
260 } else if(project->first("TEMPLATE") == "app" && project->isActiveConfig("app_bundle")) {
261 bundle = project->first("TARGET");
262 if(!project->isEmpty("QMAKE_APPLICATION_BUNDLE_NAME"))
263 bundle = project->first("QMAKE_APPLICATION_BUNDLE_NAME");
264 if(!bundle.endsWith(".app"))
265 bundle += ".app";
266 if(project->isEmpty("QMAKE_BUNDLE_LOCATION"))
267 project->values("QMAKE_BUNDLE_LOCATION").append("Contents/MacOS");
268 project->values("QMAKE_PKGINFO").append(project->first("DESTDIR") + bundle + "/Contents/PkgInfo");
269 project->values("QMAKE_BUNDLE_RESOURCE_FILE").append(project->first("DESTDIR") + bundle + "/Contents/Resources/empty.lproj");
270 } else if(project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib") &&
271 ((!project->isActiveConfig("plugin") && project->isActiveConfig("lib_bundle")) ||
272 (project->isActiveConfig("plugin") && project->isActiveConfig("plugin_bundle")))) {
273 bundle = project->first("TARGET");
274 if(project->isActiveConfig("plugin")) {
275 if(!project->isEmpty("QMAKE_PLUGIN_BUNDLE_NAME"))
276 bundle = project->first("QMAKE_PLUGIN_BUNDLE_NAME");
277 if (project->isEmpty("QMAKE_BUNDLE_EXTENSION"))
278 project->values("QMAKE_BUNDLE_EXTENSION").append(".plugin");
279 if (!bundle.endsWith(project->first("QMAKE_BUNDLE_EXTENSION")))
280 bundle += project->first("QMAKE_BUNDLE_EXTENSION");
281 if(project->isEmpty("QMAKE_BUNDLE_LOCATION"))
282 project->values("QMAKE_BUNDLE_LOCATION").append("Contents/MacOS");
283 } else {
284 if(!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME"))
285 bundle = project->first("QMAKE_FRAMEWORK_BUNDLE_NAME");
286 if (project->isEmpty("QMAKE_BUNDLE_EXTENSION"))
287 project->values("QMAKE_BUNDLE_EXTENSION").append(".framework");
288 if (!bundle.endsWith(project->first("QMAKE_BUNDLE_EXTENSION")))
289 bundle += project->first("QMAKE_BUNDLE_EXTENSION");
290 }
291 }
292 if(!bundle.isEmpty()) {
293 project->values("QMAKE_BUNDLE") = ProStringList(bundle);
294 } else {
295 project->values("QMAKE_BUNDLE").clear();
296 project->values("QMAKE_BUNDLE_LOCATION").clear();
297 }
298 } else { //no bundling here
299 project->values("QMAKE_BUNDLE").clear();
300 project->values("QMAKE_BUNDLE_LOCATION").clear();
301 }
302
303 init2();
304 ProString target = project->first("TARGET");
305 int slsh = target.lastIndexOf(Option::dir_sep);
306 if (slsh != -1)
307 target.chopFront(slsh + 1);
308 project->values("LIB_TARGET").prepend(target);
309 }
310
311 QStringList
findDependencies(const QString & f)312 &UnixMakefileGenerator::findDependencies(const QString &f)
313 {
314 QStringList &ret = MakefileGenerator::findDependencies(f);
315 if (doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER")) {
316 ProString file = f;
317 QString header_prefix;
318 if(!project->isEmpty("PRECOMPILED_DIR"))
319 header_prefix = project->first("PRECOMPILED_DIR").toQString();
320 header_prefix += project->first("QMAKE_ORIG_TARGET").toQString();
321 header_prefix += project->first("QMAKE_PCH_OUTPUT_EXT").toQString();
322 if (project->isActiveConfig("icc_pch_style")) {
323 // icc style
324 ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS");
325 if (pchArchs.isEmpty())
326 pchArchs << ProString(); // normal single-arch PCH
327 for (const ProString &arch : qAsConst(pchArchs)) {
328 auto pfx = header_prefix;
329 if (!arch.isEmpty())
330 pfx.replace(QLatin1String("${QMAKE_PCH_ARCH}"), arch.toQString());
331 for (QStringList::Iterator it = Option::cpp_ext.begin();
332 it != Option::cpp_ext.end(); ++it) {
333 if (file.endsWith(*it)) {
334 ret += pfx;
335 break;
336 }
337 }
338 }
339 } else {
340 // gcc style (including clang_pch_style)
341 QString header_suffix = project->isActiveConfig("clang_pch_style")
342 ? project->first("QMAKE_PCH_OUTPUT_EXT").toQString() : "";
343 header_prefix += Option::dir_sep + project->first("QMAKE_PRECOMP_PREFIX");
344
345 for (const ProString &compiler : project->values("QMAKE_BUILTIN_COMPILERS")) {
346 if (project->isEmpty(ProKey("QMAKE_" + compiler + "FLAGS_PRECOMPILE")))
347 continue;
348
349 ProString language = project->first(ProKey("QMAKE_LANGUAGE_" + compiler));
350 if (language.isEmpty())
351 continue;
352
353 // Unfortunately we were not consistent about the C++ naming
354 ProString extensionSuffix = compiler;
355 if (extensionSuffix == "CXX")
356 extensionSuffix = ProString("CPP");
357
358 for (const ProString &extension : project->values(ProKey("QMAKE_EXT_" + extensionSuffix))) {
359 if (!file.endsWith(extension.toQString()))
360 continue;
361
362 ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS");
363 if (pchArchs.isEmpty())
364 pchArchs << ProString(); // normal single-arch PCH
365 for (const ProString &arch : qAsConst(pchArchs)) {
366 QString precompiledHeader = header_prefix + language + header_suffix;
367 if (!arch.isEmpty()) {
368 precompiledHeader.replace(QLatin1String("${QMAKE_PCH_ARCH}"),
369 arch.toQString());
370 }
371 if (!ret.contains(precompiledHeader))
372 ret += precompiledHeader;
373 }
374
375 goto foundPrecompiledDependency;
376 }
377 }
378 foundPrecompiledDependency:
379 ; // Hurray!!
380 }
381 }
382 return ret;
383 }
384
385 ProString
fixLibFlag(const ProString & lib)386 UnixMakefileGenerator::fixLibFlag(const ProString &lib)
387 {
388 return escapeFilePath(lib);
389 }
390
391 bool
findLibraries(bool linkPrl,bool mergeLflags)392 UnixMakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags)
393 {
394 QVector<QMakeLocalFileName> libdirs, frameworkdirs;
395 int libidx = 0, fwidx = 0;
396 for (const ProString &dlib : project->values("QMAKE_DEFAULT_LIBDIRS"))
397 libdirs.append(QMakeLocalFileName(dlib.toQString()));
398 frameworkdirs.append(QMakeLocalFileName("/System/Library/Frameworks"));
399 frameworkdirs.append(QMakeLocalFileName("/Library/Frameworks"));
400 ProStringList extens;
401 extens << project->first("QMAKE_EXTENSION_SHLIB") << "a";
402 static const char * const lflags[] = { "LIBS", "LIBS_PRIVATE",
403 "QMAKE_LIBS", "QMAKE_LIBS_PRIVATE", nullptr };
404 for (int i = 0; lflags[i]; i++) {
405 ProStringList &l = project->values(lflags[i]);
406 for (ProStringList::Iterator it = l.begin(); it != l.end(); ) {
407 QString opt = (*it).toQString();
408 if(opt.startsWith("-")) {
409 if(opt.startsWith("-L")) {
410 QString lib = opt.mid(2);
411 QMakeLocalFileName f(lib);
412 int idx = libdirs.indexOf(f);
413 if (idx >= 0 && idx < libidx) {
414 it = l.erase(it);
415 continue;
416 }
417 libdirs.insert(libidx++, f);
418 } else if(opt.startsWith("-l")) {
419 QString lib = opt.mid(2);
420 for (const QMakeLocalFileName &libdir : qAsConst(libdirs)) {
421 QString libBase = libdir.local() + '/'
422 + project->first("QMAKE_PREFIX_SHLIB") + lib;
423 if (linkPrl && processPrlFile(libBase, true))
424 goto found;
425 for (ProStringList::Iterator extit = extens.begin(); extit != extens.end(); ++extit) {
426 if (exists(libBase + '.' + (*extit)))
427 goto found;
428 }
429 }
430 found: ;
431 } else if (target_mode == TARG_MAC_MODE && opt.startsWith("-F")) {
432 QMakeLocalFileName f(opt.mid(2));
433 if (!frameworkdirs.contains(f))
434 frameworkdirs.insert(fwidx++, f);
435 } else if (target_mode == TARG_MAC_MODE && opt == "-framework") {
436 if (linkPrl) {
437 opt = (*++it).toQString();
438 static const QChar suffixMarker = ',';
439 const int suffixPosition = opt.indexOf(suffixMarker);
440 const bool hasSuffix = suffixPosition >= 0;
441 QString frameworkName = opt;
442 if (hasSuffix) {
443 frameworkName.truncate(suffixPosition);
444 opt.remove(suffixMarker); // Apply suffix by removing marker
445 }
446 for (const QMakeLocalFileName &dir : qAsConst(frameworkdirs)) {
447 auto processPrlIfFound = [&](QString directory) {
448 QString suffixedPrl = directory + opt;
449 if (processPrlFile(suffixedPrl, true))
450 return true;
451 if (hasSuffix) {
452 QString unsuffixedPrl = directory + frameworkName;
453 if (processPrlFile(unsuffixedPrl, true))
454 return true;
455 }
456 return false;
457 };
458 QString frameworkDirectory = dir.local() + "/" + frameworkName + + ".framework/";
459 if (processPrlIfFound(frameworkDirectory + "Resources/")
460 || processPrlIfFound(frameworkDirectory))
461 break;
462 }
463 } else {
464 if (opt.length() == 10)
465 ++it;
466 // Skip
467 }
468 }
469 } else if (linkPrl) {
470 processPrlFile(opt, false);
471 }
472
473 ProStringList &prl_libs = project->values("QMAKE_CURRENT_PRL_LIBS");
474 for (int prl = 0; prl < prl_libs.size(); ++prl)
475 it = l.insert(++it, prl_libs.at(prl));
476 prl_libs.clear();
477 ++it;
478 }
479
480 if (mergeLflags) {
481 QHash<ProKey, ProStringList> lflags;
482 for(int lit = 0; lit < l.size(); ++lit) {
483 ProKey arch("default");
484 ProString opt = l.at(lit);
485 if (opt.startsWith('-')) {
486 if (target_mode == TARG_MAC_MODE && opt.startsWith("-Xarch")) {
487 if (opt.length() > 7) {
488 arch = opt.mid(7).toKey();
489 opt = l.at(++lit);
490 }
491 }
492
493 if (opt.startsWith("-L")
494 || (target_mode == TARG_MAC_MODE && opt.startsWith("-F"))) {
495 if (!lflags[arch].contains(opt))
496 lflags[arch].append(opt);
497 } else if (opt.startsWith("-l") || opt == "-pthread") {
498 // Make sure we keep the dependency order of libraries
499 lflags[arch].removeAll(opt);
500 lflags[arch].append(opt);
501 } else if (target_mode == TARG_MAC_MODE
502 && (opt == "-framework" || opt == "-force_load")) {
503 // Handle space separated options
504 ProString dashOpt = opt;
505 opt = l.at(++lit);
506 if (opt.startsWith("-Xarch"))
507 opt = l.at(++lit); // The user has done the right thing and prefixed each part
508 for(int x = 0; x < lflags[arch].size(); ++x) {
509 if (lflags[arch].at(x) == dashOpt && lflags[arch].at(++x) == opt) {
510 lflags[arch].remove(x - 1, 2);
511 break;
512 }
513 }
514 lflags[arch].append(dashOpt);
515 lflags[arch].append(opt);
516 } else {
517 lflags[arch].append(opt);
518 }
519 } else if(!opt.isNull()) {
520 for (const ProString &ext : extens) {
521 if (opt.size() > ext.size() && opt.endsWith(ext)
522 && opt.at(opt.size() - ext.size() - 1) == '.') {
523 // Make sure we keep the dependency order of libraries
524 lflags[arch].removeAll(opt);
525 lflags[arch].append(opt);
526 goto found2;
527 }
528 }
529 if(!lflags[arch].contains(opt))
530 lflags[arch].append(opt);
531 found2: ;
532 }
533 }
534
535 l = lflags.take("default");
536
537 // Process architecture specific options (Xarch)
538 QHash<ProKey, ProStringList>::const_iterator archIterator = lflags.constBegin();
539 while (archIterator != lflags.constEnd()) {
540 const ProStringList &archOptions = archIterator.value();
541 for (int i = 0; i < archOptions.size(); ++i) {
542 l.append(QLatin1String("-Xarch_") + archIterator.key());
543 l.append(archOptions.at(i));
544 }
545 ++archIterator;
546 }
547 }
548 }
549 return false;
550 }
551
552 #ifdef Q_OS_WIN // MinGW x-compiling for QNX
installRoot() const553 QString UnixMakefileGenerator::installRoot() const
554 {
555 /*
556 We include a magic prefix on the path to bypass mingw-make's "helpful"
557 intervention in the environment, recognising variables that look like
558 paths and adding the msys system root as prefix, which we don't want.
559 Once this hack has smuggled INSTALL_ROOT into make's variable space, we
560 can trivially strip the magic prefix back off to get the path we meant.
561 */
562 return QStringLiteral("$(INSTALL_ROOT:@msyshack@%=%)");
563 }
564 #endif
565
566 QString
defaultInstall(const QString & t)567 UnixMakefileGenerator::defaultInstall(const QString &t)
568 {
569 if(t != "target" || project->first("TEMPLATE") == "subdirs")
570 return QString();
571
572 enum { NoBundle, SolidBundle, SlicedBundle } bundle = NoBundle;
573 bool isAux = (project->first("TEMPLATE") == "aux");
574 const QString root = installRoot();
575 ProStringList &uninst = project->values(ProKey(t + ".uninstall"));
576 QString ret, destdir = project->first("DESTDIR").toQString();
577 if(!destdir.isEmpty() && destdir.right(1) != Option::dir_sep)
578 destdir += Option::dir_sep;
579 QString targetdir = fileFixify(project->first("target.path").toQString(), FileFixifyAbsolute);
580 if(targetdir.right(1) != Option::dir_sep)
581 targetdir += Option::dir_sep;
582
583 ProStringList links;
584 QString target="$(TARGET)";
585 const ProStringList &targets = project->values(ProKey(t + ".targets"));
586 if(!project->isEmpty("QMAKE_BUNDLE")) {
587 target = project->first("QMAKE_BUNDLE").toQString();
588 bundle = project->isActiveConfig("sliced_bundle") ? SlicedBundle : SolidBundle;
589 } else if(project->first("TEMPLATE") == "app") {
590 target = "$(QMAKE_TARGET)";
591 } else if(project->first("TEMPLATE") == "lib") {
592 if (!project->isActiveConfig("staticlib")
593 && !project->isActiveConfig("plugin")
594 && !project->isActiveConfig("unversioned_libname")) {
595 if(project->isEmpty("QMAKE_HPUX_SHLIB")) {
596 links << "$(TARGET0)" << "$(TARGET1)" << "$(TARGET2)";
597 } else {
598 links << "$(TARGET0)";
599 }
600 }
601 }
602 for(int i = 0; i < targets.size(); ++i) {
603 QString src = targets.at(i).toQString(),
604 dst = escapeFilePath(filePrefixRoot(root, targetdir + src.section('/', -1)));
605 if(!ret.isEmpty())
606 ret += "\n\t";
607 ret += "$(QINSTALL) " + escapeFilePath(Option::fixPathToTargetOS(src, false)) + ' ' + dst;
608 if(!uninst.isEmpty())
609 uninst.append("\n\t");
610 uninst.append("-$(DEL_FILE) " + dst);
611 }
612
613 {
614 QString src_targ = target;
615 if(!destdir.isEmpty())
616 src_targ = Option::fixPathToTargetOS(destdir + target, false);
617 QString plain_targ = filePrefixRoot(root, fileFixify(targetdir + target, FileFixifyAbsolute));
618 QString dst_targ = plain_targ;
619 plain_targ = escapeFilePath(plain_targ);
620 if (bundle != NoBundle) {
621 QString suffix;
622 if (project->first("TEMPLATE") == "lib") {
623 if (!project->isActiveConfig("shallow_bundle"))
624 suffix += "/Versions/" + project->first("QMAKE_FRAMEWORK_VERSION");
625 suffix += "/$(TARGET)";
626 } else {
627 suffix = "/" + project->first("QMAKE_BUNDLE_LOCATION") + "/$(QMAKE_TARGET)";
628 }
629 dst_targ += suffix;
630 if (bundle == SolidBundle) {
631 if (!ret.isEmpty())
632 ret += "\n\t";
633 ret += "$(DEL_FILE) -r " + plain_targ + "\n\t";
634 } else {
635 src_targ += suffix;
636 }
637 }
638 src_targ = escapeFilePath(src_targ);
639 dst_targ = escapeFilePath(dst_targ);
640
641 QString copy_cmd;
642 if (bundle == SolidBundle) {
643 copy_cmd += "$(QINSTALL) " + src_targ + ' ' + plain_targ;
644 } else if (project->first("TEMPLATE") == "lib" && project->isActiveConfig("staticlib")) {
645 copy_cmd += "$(QINSTALL) " + src_targ + ' ' + dst_targ;
646 } else if (!isAux) {
647 if (bundle == SlicedBundle) {
648 if (!ret.isEmpty())
649 ret += "\n\t";
650 ret += mkdir_p_asstring("\"`dirname " + dst_targ + "`\"", false);
651 }
652 copy_cmd += "$(QINSTALL_PROGRAM) " + src_targ + ' ' + dst_targ;
653 }
654 if(project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib")
655 && project->values(ProKey(t + ".CONFIG")).indexOf("fix_rpath") != -1) {
656 if (!ret.isEmpty())
657 ret += "\n\t";
658 if(!project->isEmpty("QMAKE_FIX_RPATH")) {
659 ret += copy_cmd;
660 ret += "\n\t-" + var("QMAKE_FIX_RPATH") + ' ' + dst_targ + ' ' + dst_targ;
661 } else if(!project->isEmpty("QMAKE_LFLAGS_RPATH")) {
662 ret += "-$(LINK) $(LFLAGS) " + var("QMAKE_LFLAGS_RPATH") + targetdir + " -o " +
663 dst_targ + " $(OBJECTS) $(LIBS) $(OBJCOMP)";
664 } else {
665 ret += copy_cmd;
666 }
667 } else if (!copy_cmd.isEmpty()) {
668 if (!ret.isEmpty())
669 ret += "\n\t";
670 ret += copy_cmd;
671 }
672
673 if (isAux) {
674 } else if (project->first("TEMPLATE") == "lib" && project->isActiveConfig("staticlib")) {
675 if(!project->isEmpty("QMAKE_RANLIB"))
676 ret += QString("\n\t$(RANLIB) ") + dst_targ;
677 } else if (!project->isActiveConfig("debug_info") && !project->isActiveConfig("nostrip")
678 && !project->isEmpty("QMAKE_STRIP")) {
679 ret += "\n\t-$(STRIP)";
680 if (project->first("TEMPLATE") == "lib") {
681 if (!project->isEmpty("QMAKE_STRIPFLAGS_LIB"))
682 ret += " " + var("QMAKE_STRIPFLAGS_LIB");
683 } else if (project->first("TEMPLATE") == "app") {
684 if (!project->isEmpty("QMAKE_STRIPFLAGS_APP"))
685 ret += " " + var("QMAKE_STRIPFLAGS_APP");
686 }
687 ret += ' ' + dst_targ;
688 }
689 if(!uninst.isEmpty())
690 uninst.append("\n\t");
691 if (bundle == SolidBundle)
692 uninst.append("-$(DEL_FILE) -r " + plain_targ);
693 else if (!isAux)
694 uninst.append("-$(DEL_FILE) " + dst_targ);
695 if (bundle == SlicedBundle) {
696 int dstlen = project->first("DESTDIR").length();
697 for (const ProString &src : project->values("QMAKE_BUNDLED_FILES")) {
698 ProString file = src.mid(dstlen);
699 QString dst = escapeFilePath(
700 filePrefixRoot(root, fileFixify(targetdir + file, FileFixifyAbsolute)));
701 if (!ret.isEmpty())
702 ret += "\n\t";
703 ret += mkdir_p_asstring("\"`dirname " + dst + "`\"", false) + "\n\t";
704 ret += "-$(DEL_FILE) " + dst + "\n\t"; // Can't overwrite symlinks to directories
705 ret += "$(QINSTALL) " + escapeFilePath(src) + " " + dst;
706 if (!uninst.isEmpty())
707 uninst.append("\n\t");
708 uninst.append("-$(DEL_FILE) " + dst);
709 }
710 }
711 if(!links.isEmpty()) {
712 for(int i = 0; i < links.size(); ++i) {
713 if (target_mode == TARG_UNIX_MODE || target_mode == TARG_MAC_MODE) {
714 QString link = Option::fixPathToTargetOS(destdir + links[i], false);
715 int lslash = link.lastIndexOf(Option::dir_sep);
716 if(lslash != -1)
717 link = link.right(link.length() - (lslash + 1));
718 QString dst_link = escapeFilePath(
719 filePrefixRoot(root, fileFixify(targetdir + link, FileFixifyAbsolute)));
720 ret += "\n\t-$(SYMLINK) $(TARGET) " + dst_link;
721 if(!uninst.isEmpty())
722 uninst.append("\n\t");
723 uninst.append("-$(DEL_FILE) " + dst_link);
724 }
725 }
726 }
727 }
728 if (isAux || project->first("TEMPLATE") == "lib") {
729 QStringList types;
730 types << "prl" << "libtool" << "pkgconfig";
731 for(int i = 0; i < types.size(); ++i) {
732 const QString type = types.at(i);
733 QString meta;
734 if(type == "prl" && project->isActiveConfig("create_prl") && !project->isActiveConfig("no_install_prl") &&
735 !project->isEmpty("QMAKE_INTERNAL_PRL_FILE"))
736 meta = prlFileName(false);
737 if (type == "libtool" && project->isActiveConfig("create_libtool"))
738 meta = libtoolFileName(false);
739 if(type == "pkgconfig" && project->isActiveConfig("create_pc"))
740 meta = pkgConfigFileName(false);
741 if(!meta.isEmpty()) {
742 QString src_meta = meta;
743 if(!destdir.isEmpty())
744 src_meta = Option::fixPathToTargetOS(destdir + meta, false);
745 QString dst_meta = filePrefixRoot(root, fileFixify(targetdir + meta, FileFixifyAbsolute));
746 if(!uninst.isEmpty())
747 uninst.append("\n\t");
748 uninst.append("-$(DEL_FILE) " + escapeFilePath(dst_meta));
749 const QString dst_meta_dir = fileInfo(dst_meta).path();
750 if(!dst_meta_dir.isEmpty()) {
751 if(!ret.isEmpty())
752 ret += "\n\t";
753 ret += mkdir_p_asstring(dst_meta_dir, true);
754 }
755 if (!ret.isEmpty())
756 ret += "\n\t";
757 ret += installMetaFile(ProKey("QMAKE_" + type.toUpper() + "_INSTALL_REPLACE"), src_meta, dst_meta);
758 }
759 }
760 }
761 return ret;
762 }
763
764 QString
escapeFilePath(const QString & path) const765 UnixMakefileGenerator::escapeFilePath(const QString &path) const
766 {
767 QString ret = path;
768 if(!ret.isEmpty()) {
769 ret.replace(QLatin1Char(' '), QLatin1String("\\ "))
770 .replace(QLatin1Char('\t'), QLatin1String("\\\t"));
771 debug_msg(2, "EscapeFilePath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData());
772 }
773 return ret;
774 }
775
776 QT_END_NAMESPACE
777