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 "mingw_make.h"
30#include "option.h"
31
32#include <proitems.h>
33
34#include <qregularexpression.h>
35#include <qdir.h>
36#include <stdlib.h>
37#include <time.h>
38
39QT_BEGIN_NAMESPACE
40
41QString MingwMakefileGenerator::escapeDependencyPath(const QString &path) const
42{
43 QString ret = path;
44 ret.replace('\\', "/"); // ### this shouldn't be here
45 return MakefileGenerator::escapeDependencyPath(ret);
46}
47
48ProString MingwMakefileGenerator::fixLibFlag(const ProString &lib)
49{
50 if (lib.startsWith("-l")) // Fallback for unresolved -l libs.
51 return QLatin1String("-l") + escapeFilePath(lib.mid(2));
52 if (lib.startsWith("-L")) // Lib search path. Needed only by -l above.
53 return QLatin1String("-L")
54 + escapeFilePath(Option::fixPathToTargetOS(lib.mid(2).toQString(), false));
55 if (lib.startsWith("lib")) // Fallback for unresolved MSVC-style libs.
56 return QLatin1String("-l") + escapeFilePath(lib.mid(3).toQString());
57 return escapeFilePath(Option::fixPathToTargetOS(lib.toQString(), false));
58}
59
60MakefileGenerator::LibFlagType
61MingwMakefileGenerator::parseLibFlag(const ProString &flag, ProString *arg)
62{
63 // Skip MSVC handling from Win32MakefileGenerator
64 return MakefileGenerator::parseLibFlag(flag, arg);
65}
66
67bool MingwMakefileGenerator::processPrlFileBase(QString &origFile, QStringView origName,
68 QStringView fixedBase, int slashOff)
69{
70 if (origName.startsWith(u"lib")) {
71 QString newFixedBase = fixedBase.left(slashOff) + fixedBase.mid(slashOff + 3);
72 if (Win32MakefileGenerator::processPrlFileBase(origFile, origName,
73 QStringView(newFixedBase), slashOff)) {
74 return true;
75 }
76 }
77 return Win32MakefileGenerator::processPrlFileBase(origFile, origName, fixedBase, slashOff);
78}
79
80bool MingwMakefileGenerator::writeMakefile(QTextStream &t)
81{
82 writeHeader(t);
83 if (writeDummyMakefile(t))
84 return true;
85
86 if(project->first("TEMPLATE") == "app" ||
87 project->first("TEMPLATE") == "lib" ||
88 project->first("TEMPLATE") == "aux") {
89 if(project->isActiveConfig("create_pc") && project->first("TEMPLATE") == "lib")
90 writePkgConfigFile();
91 writeMingwParts(t);
92 return MakefileGenerator::writeMakefile(t);
93 }
94 else if(project->first("TEMPLATE") == "subdirs") {
95 writeSubDirs(t);
96 return true;
97 }
98 return false;
99 }
100
101QString MingwMakefileGenerator::installRoot() const
102{
103 /*
104 We include a magic prefix on the path to bypass mingw-make's "helpful"
105 intervention in the environment, recognising variables that look like
106 paths and adding the msys system root as prefix, which we don't want.
107 Once this hack has smuggled INSTALL_ROOT into make's variable space, we
108 can trivially strip the magic prefix back off to get the path we meant.
109 */
110 return QStringLiteral("$(INSTALL_ROOT:@msyshack@%=%)");
111}
112
113void MingwMakefileGenerator::writeMingwParts(QTextStream &t)
114{
115 writeStandardParts(t);
116
117 if (!preCompHeaderOut.isEmpty()) {
118 QString header = project->first("PRECOMPILED_HEADER").toQString();
119 QString cHeader = preCompHeaderOut + Option::dir_sep + "c";
120 t << escapeDependencyPath(cHeader) << ": " << escapeDependencyPath(header) << " "
121 << finalizeDependencyPaths(findDependencies(header)).join(" \\\n\t\t")
122 << "\n\t" << mkdir_p_asstring(preCompHeaderOut)
123 << "\n\t$(CC) -x c-header -c $(CFLAGS) $(INCPATH) -o " << escapeFilePath(cHeader)
124 << ' ' << escapeFilePath(header) << Qt::endl << Qt::endl;
125 QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++";
126 t << escapeDependencyPath(cppHeader) << ": " << escapeDependencyPath(header) << " "
127 << finalizeDependencyPaths(findDependencies(header)).join(" \\\n\t\t")
128 << "\n\t" << mkdir_p_asstring(preCompHeaderOut)
129 << "\n\t$(CXX) -x c++-header -c $(CXXFLAGS) $(INCPATH) -o " << escapeFilePath(cppHeader)
130 << ' ' << escapeFilePath(header) << Qt::endl << Qt::endl;
131 }
132}
133
134void MingwMakefileGenerator::init()
135{
136 /* this should probably not be here, but I'm using it to wrap the .t files */
137 if(project->first("TEMPLATE") == "app")
138 project->values("QMAKE_APP_FLAG").append("1");
139 else if(project->first("TEMPLATE") == "lib")
140 project->values("QMAKE_LIB_FLAG").append("1");
141 else if(project->first("TEMPLATE") == "subdirs") {
142 MakefileGenerator::init();
143 if(project->values("MAKEFILE").isEmpty())
144 project->values("MAKEFILE").append("Makefile");
145 return;
146 }
147
148 processVars();
149
150 project->values("LIBS") += project->values("RES_FILE");
151
152 if (project->isActiveConfig("dll")) {
153 QString destDir = "";
154 if(!project->first("DESTDIR").isEmpty())
155 destDir = Option::fixPathToTargetOS(project->first("DESTDIR") + Option::dir_sep, false, false);
156 project->values("MINGW_IMPORT_LIB").prepend(destDir + project->first("LIB_TARGET"));
157 project->values("QMAKE_LFLAGS").append(QString("-Wl,--out-implib,") + fileVar("MINGW_IMPORT_LIB"));
158 }
159
160 if (!project->values("DEF_FILE").isEmpty()) {
161 QString defFileName = fileFixify(project->first("DEF_FILE").toQString());
162 project->values("QMAKE_LFLAGS").append(QString("-Wl,") + escapeFilePath(defFileName));
163 }
164
165 if (project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib")
166 project->values("QMAKE_LFLAGS").append("-static");
167
168 MakefileGenerator::init();
169
170 // precomp
171 if (!project->first("PRECOMPILED_HEADER").isEmpty()
172 && project->isActiveConfig("precompile_header")) {
173 QString preCompHeader = var("PRECOMPILED_DIR")
174 + QFileInfo(project->first("PRECOMPILED_HEADER").toQString()).fileName();
175 preCompHeaderOut = preCompHeader + ".gch";
176 project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c");
177 project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c++");
178
179 preCompHeader = escapeFilePath(preCompHeader);
180 project->values("QMAKE_RUN_CC").clear();
181 project->values("QMAKE_RUN_CC").append("$(CC) -c -include " + preCompHeader +
182 " $(CFLAGS) $(INCPATH) " + var("QMAKE_CC_O_FLAG") + "$obj $src");
183 project->values("QMAKE_RUN_CC_IMP").clear();
184 project->values("QMAKE_RUN_CC_IMP").append("$(CC) -c -include " + preCompHeader +
185 " $(CFLAGS) $(INCPATH) " + var("QMAKE_CC_O_FLAG") + "$@ $<");
186 project->values("QMAKE_RUN_CXX").clear();
187 project->values("QMAKE_RUN_CXX").append("$(CXX) -c -include " + preCompHeader +
188 " $(CXXFLAGS) $(INCPATH) " + var("QMAKE_CC_O_FLAG") + "$obj $src");
189 project->values("QMAKE_RUN_CXX_IMP").clear();
190 project->values("QMAKE_RUN_CXX_IMP").append("$(CXX) -c -include " + preCompHeader +
191 " $(CXXFLAGS) $(INCPATH) " + var("QMAKE_CC_O_FLAG") + "$@ $<");
192 }
193
194 if(project->isActiveConfig("dll")) {
195 project->values("QMAKE_DISTCLEAN").append(project->first("MINGW_IMPORT_LIB"));
196 }
197}
198
199void MingwMakefileGenerator::writeIncPart(QTextStream &t)
200{
201 t << "INCPATH = ";
202
203 const ProStringList &incs = project->values("INCLUDEPATH");
204 QFile responseFile;
205 QTextStream responseStream;
206 QChar sep(' ');
207 int totalLength = std::accumulate(incs.constBegin(), incs.constEnd(), 0,
208 [](int total, const ProString &inc) {
209 return total + inc.size() + 2;
210 });
211 if (totalLength > project->intValue("QMAKE_RESPONSEFILE_THRESHOLD", 8000)) {
212 const QString fileName = createResponseFile("incpath", incs, "-I");
213 if (!fileName.isEmpty()) {
214 t << '@' + fileName;
215 t << Qt::endl;
216 return;
217 }
218 }
219 for (const ProString &incit: qAsConst(incs)) {
220 QString inc = incit.toQString();
221 inc.replace(QRegularExpression("\\\\$"), "");
222 inc.replace('\\', '/');
223 t << "-I" << escapeFilePath(inc) << sep;
224 }
225 t << Qt::endl;
226}
227
228void MingwMakefileGenerator::writeLibsPart(QTextStream &t)
229{
230 if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
231 t << "LIB = " << var("QMAKE_LIB") << Qt::endl;
232 } else {
233 t << "LINKER = " << var("QMAKE_LINK") << Qt::endl;
234 t << "LFLAGS = " << var("QMAKE_LFLAGS") << Qt::endl;
235 t << "LIBS = "
236 << fixLibFlags("LIBS").join(' ') << ' '
237 << fixLibFlags("LIBS_PRIVATE").join(' ') << ' '
238 << fixLibFlags("QMAKE_LIBS").join(' ') << ' '
239 << fixLibFlags("QMAKE_LIBS_PRIVATE").join(' ') << Qt::endl;
240 }
241}
242
243void MingwMakefileGenerator::writeObjectsPart(QTextStream &t)
244{
245 const ProString &objmax = project->first("QMAKE_LINK_OBJECT_MAX");
246 if (objmax.isEmpty() || project->values("OBJECTS").count() < objmax.toInt()) {
247 objectsLinkLine = "$(OBJECTS)";
248 } else if (project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
249 // QMAKE_LIB is used for win32, including mingw, whereas QMAKE_AR is used on Unix.
250 QString ar_cmd = var("QMAKE_LIB");
251 if (ar_cmd.isEmpty())
252 ar_cmd = "ar -rc";
253 const QString ar_response_file =
254 createResponseFile(var("QMAKE_LINK_OBJECT_SCRIPT"), project->values("OBJECTS"));
255 objectsLinkLine = ar_cmd + ' ' + var("DEST_TARGET") + " @" + escapeFilePath(ar_response_file);
256 } else {
257 const QString ld_response_file =
258 createResponseFile(var("QMAKE_LINK_OBJECT_SCRIPT"), project->values("OBJECTS"));
259 objectsLinkLine = "@" + escapeFilePath(ld_response_file);
260 }
261 Win32MakefileGenerator::writeObjectsPart(t);
262}
263
264void MingwMakefileGenerator::writeBuildRulesPart(QTextStream &t)
265{
266 t << "first: all\n";
267 t << "all: " << escapeDependencyPath(fileFixify(Option::output.fileName()))
268 << ' ' << depVar("ALL_DEPS") << ' ' << depVar("DEST_TARGET") << "\n\n";
269 t << depVar("DEST_TARGET") << ": "
270 << depVar("PRE_TARGETDEPS") << " $(OBJECTS) " << depVar("POST_TARGETDEPS");
271 if (project->first("TEMPLATE") == "aux") {
272 t << "\n\n";
273 return;
274 }
275
276 if(!project->isEmpty("QMAKE_PRE_LINK"))
277 t << "\n\t" <<var("QMAKE_PRE_LINK");
278 if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
279 t << "\n\t-$(DEL_FILE) $(DESTDIR_TARGET) 2>" << var("QMAKE_SHELL_NULL_DEVICE");
280 const ProString &objmax = project->first("QMAKE_LINK_OBJECT_MAX");
281 if (objmax.isEmpty() || project->values("OBJECTS").count() < objmax.toInt()) {
282 t << "\n\t$(LIB) $(DESTDIR_TARGET) " << objectsLinkLine << " " ;
283 } else {
284 t << "\n\t" << objectsLinkLine << " " ;
285 }
286 } else {
287 t << "\n\t$(LINKER) $(LFLAGS) " << var("QMAKE_LINK_O_FLAG") << "$(DESTDIR_TARGET) " << objectsLinkLine << " $(LIBS)";
288 }
289 if(!project->isEmpty("QMAKE_POST_LINK"))
290 t << "\n\t" <<var("QMAKE_POST_LINK");
291 t << Qt::endl;
292}
293
294void MingwMakefileGenerator::writeRcFilePart(QTextStream &t)
295{
296 const QString rc_file = fileFixify(project->first("RC_FILE").toQString());
297
298 ProStringList rcIncPaths = project->values("RC_INCLUDEPATH");
299 rcIncPaths.prepend(fileInfo(rc_file).path());
300 QString incPathStr;
301 for (int i = 0; i < rcIncPaths.count(); ++i) {
302 const ProString &path = rcIncPaths.at(i);
303 if (path.isEmpty())
304 continue;
305 incPathStr += QStringLiteral(" --include-dir=");
306 if (path != "." && QDir::isRelativePath(path.toQString()))
307 incPathStr += "./";
308 incPathStr += escapeFilePath(path);
309 }
310
311 if (!rc_file.isEmpty()) {
312
313 ProString defines = varGlue("RC_DEFINES", " -D", " -D", "");
314 if (defines.isEmpty())
315 defines = ProString(" $(DEFINES)");
316
317 addSourceFile(rc_file, QMakeSourceFileInfo::SEEK_DEPS);
318 const QStringList rcDeps = QStringList(rc_file) << dependencies(rc_file);
319
320 t << escapeDependencyPath(var("RES_FILE")) << ": "
321 << escapeDependencyPaths(rcDeps).join(' ') << "\n\t"
322 << var("QMAKE_RC") << " -i " << escapeFilePath(rc_file) << " -o " << fileVar("RES_FILE")
323 << incPathStr << defines << "\n\n";
324 }
325}
326
327QStringList &MingwMakefileGenerator::findDependencies(const QString &file)
328{
329 QStringList &aList = MakefileGenerator::findDependencies(file);
330 if (preCompHeaderOut.isEmpty())
331 return aList;
332 for (QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) {
333 if (file.endsWith(*it)) {
334 QString cHeader = preCompHeaderOut + Option::dir_sep + "c";
335 if (!aList.contains(cHeader))
336 aList += cHeader;
337 break;
338 }
339 }
340 for (QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) {
341 if (file.endsWith(*it)) {
342 QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++";
343 if (!aList.contains(cppHeader))
344 aList += cppHeader;
345 break;
346 }
347 }
348 return aList;
349}
350
351QT_END_NAMESPACE
352