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

source code of qtbase/qmake/generators/win32/mingw_make.cpp