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