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

source code of qtbase/qmake/generators/unix/unixmake.cpp