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 <qregexp.h>
35 #include <qdir.h>
36 #include <stdlib.h>
37 #include <time.h>
38
39 QT_BEGIN_NAMESPACE
40
escapeDependencyPath(const QString & path) const41 QString 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
fixLibFlag(const ProString & lib)48 ProString 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
60 MakefileGenerator::LibFlagType
parseLibFlag(const ProString & flag,ProString * arg)61 MingwMakefileGenerator::parseLibFlag(const ProString &flag, ProString *arg)
62 {
63 // Skip MSVC handling from Win32MakefileGenerator
64 return MakefileGenerator::parseLibFlag(flag, arg);
65 }
66
processPrlFileBase(QString & origFile,const QStringRef & origName,const QStringRef & fixedBase,int slashOff)67 bool MingwMakefileGenerator::processPrlFileBase(QString &origFile, const QStringRef &origName,
68 const QStringRef &fixedBase, int slashOff)
69 {
70 if (origName.startsWith("lib")) {
71 QString newFixedBase = fixedBase.left(slashOff) + fixedBase.mid(slashOff + 3);
72 if (Win32MakefileGenerator::processPrlFileBase(origFile, origName,
73 QStringRef(&newFixedBase), slashOff)) {
74 return true;
75 }
76 }
77 return Win32MakefileGenerator::processPrlFileBase(origFile, origName, fixedBase, slashOff);
78 }
79
writeMakefile(QTextStream & t)80 bool 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
installRoot() const101 QString 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
writeMingwParts(QTextStream & t)113 void 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
init()134 void 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
writeIncPart(QTextStream & t)199 void MingwMakefileGenerator::writeIncPart(QTextStream &t)
200 {
201 t << "INCPATH = ";
202
203 const ProStringList &incs = project->values("INCLUDEPATH");
204 for (ProStringList::ConstIterator incit = incs.begin(); incit != incs.end(); ++incit) {
205 QString inc = (*incit).toQString();
206 inc.replace(QRegExp("\\\\$"), "");
207
208 t << "-I" << escapeFilePath(inc) << ' ';
209 }
210 t << Qt::endl;
211 }
212
writeLibsPart(QTextStream & t)213 void MingwMakefileGenerator::writeLibsPart(QTextStream &t)
214 {
215 if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
216 t << "LIB = " << var("QMAKE_LIB") << Qt::endl;
217 } else {
218 t << "LINKER = " << var("QMAKE_LINK") << Qt::endl;
219 t << "LFLAGS = " << var("QMAKE_LFLAGS") << Qt::endl;
220 t << "LIBS = "
221 << fixLibFlags("LIBS").join(' ') << ' '
222 << fixLibFlags("LIBS_PRIVATE").join(' ') << ' '
223 << fixLibFlags("QMAKE_LIBS").join(' ') << ' '
224 << fixLibFlags("QMAKE_LIBS_PRIVATE").join(' ') << Qt::endl;
225 }
226 }
227
writeObjectsPart(QTextStream & t)228 void MingwMakefileGenerator::writeObjectsPart(QTextStream &t)
229 {
230 const ProString &objmax = project->first("QMAKE_LINK_OBJECT_MAX");
231 if (objmax.isEmpty() || project->values("OBJECTS").count() < objmax.toInt()) {
232 objectsLinkLine = "$(OBJECTS)";
233 } else if (project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
234 QString ar_response_file = var("QMAKE_LINK_OBJECT_SCRIPT") + "." + var("TARGET");
235 if (!var("BUILD_NAME").isEmpty()) {
236 ar_response_file += "." + var("BUILD_NAME");
237 }
238 if (!var("MAKEFILE").isEmpty())
239 ar_response_file += "." + var("MAKEFILE");
240 // QMAKE_LIB is used for win32, including mingw, whereas QMAKE_AR is used on Unix.
241 QString ar_cmd = var("QMAKE_LIB");
242 if (ar_cmd.isEmpty())
243 ar_cmd = "ar -rc";
244 createResponseFile(ar_response_file, project->values("OBJECTS"));
245 objectsLinkLine = ar_cmd + ' ' + var("DEST_TARGET") + " @" + escapeFilePath(ar_response_file);
246 } else {
247 QString ld_response_file = var("QMAKE_LINK_OBJECT_SCRIPT") + "." + var("TARGET");
248 if (!var("BUILD_NAME").isEmpty())
249 ld_response_file += "." + var("BUILD_NAME");
250 if (!var("MAKEFILE").isEmpty())
251 ld_response_file += "." + var("MAKEFILE");
252 createResponseFile(ld_response_file, project->values("OBJECTS"));
253 objectsLinkLine = "@" + escapeFilePath(ld_response_file);
254 }
255 Win32MakefileGenerator::writeObjectsPart(t);
256 }
257
writeBuildRulesPart(QTextStream & t)258 void MingwMakefileGenerator::writeBuildRulesPart(QTextStream &t)
259 {
260 t << "first: all\n";
261 t << "all: " << escapeDependencyPath(fileFixify(Option::output.fileName()))
262 << ' ' << depVar("ALL_DEPS") << ' ' << depVar("DEST_TARGET") << "\n\n";
263 t << depVar("DEST_TARGET") << ": "
264 << depVar("PRE_TARGETDEPS") << " $(OBJECTS) " << depVar("POST_TARGETDEPS");
265 if (project->first("TEMPLATE") == "aux") {
266 t << "\n\n";
267 return;
268 }
269
270 if(!project->isEmpty("QMAKE_PRE_LINK"))
271 t << "\n\t" <<var("QMAKE_PRE_LINK");
272 if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
273 t << "\n\t-$(DEL_FILE) $(DESTDIR_TARGET) 2>" << var("QMAKE_SHELL_NULL_DEVICE");
274 const ProString &objmax = project->first("QMAKE_LINK_OBJECT_MAX");
275 if (objmax.isEmpty() || project->values("OBJECTS").count() < objmax.toInt()) {
276 t << "\n\t$(LIB) $(DESTDIR_TARGET) " << objectsLinkLine << " " ;
277 } else {
278 t << "\n\t" << objectsLinkLine << " " ;
279 }
280 } else {
281 t << "\n\t$(LINKER) $(LFLAGS) " << var("QMAKE_LINK_O_FLAG") << "$(DESTDIR_TARGET) " << objectsLinkLine << " $(LIBS)";
282 }
283 if(!project->isEmpty("QMAKE_POST_LINK"))
284 t << "\n\t" <<var("QMAKE_POST_LINK");
285 t << Qt::endl;
286 }
287
writeRcFilePart(QTextStream & t)288 void MingwMakefileGenerator::writeRcFilePart(QTextStream &t)
289 {
290 const QString rc_file = fileFixify(project->first("RC_FILE").toQString());
291
292 ProStringList rcIncPaths = project->values("RC_INCLUDEPATH");
293 rcIncPaths.prepend(fileInfo(rc_file).path());
294 QString incPathStr;
295 for (int i = 0; i < rcIncPaths.count(); ++i) {
296 const ProString &path = rcIncPaths.at(i);
297 if (path.isEmpty())
298 continue;
299 incPathStr += QStringLiteral(" --include-dir=");
300 if (path != "." && QDir::isRelativePath(path.toQString()))
301 incPathStr += "./";
302 incPathStr += escapeFilePath(path);
303 }
304
305 if (!rc_file.isEmpty()) {
306
307 ProString defines = varGlue("RC_DEFINES", " -D", " -D", "");
308 if (defines.isEmpty())
309 defines = ProString(" $(DEFINES)");
310
311 addSourceFile(rc_file, QMakeSourceFileInfo::SEEK_DEPS);
312 const QStringList rcDeps = QStringList(rc_file) << dependencies(rc_file);
313
314 t << escapeDependencyPath(var("RES_FILE")) << ": "
315 << escapeDependencyPaths(rcDeps).join(' ') << "\n\t"
316 << var("QMAKE_RC") << " -i " << escapeFilePath(rc_file) << " -o " << fileVar("RES_FILE")
317 << incPathStr << defines << "\n\n";
318 }
319 }
320
findDependencies(const QString & file)321 QStringList &MingwMakefileGenerator::findDependencies(const QString &file)
322 {
323 QStringList &aList = MakefileGenerator::findDependencies(file);
324 if (preCompHeaderOut.isEmpty())
325 return aList;
326 for (QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) {
327 if (file.endsWith(*it)) {
328 QString cHeader = preCompHeaderOut + Option::dir_sep + "c";
329 if (!aList.contains(cHeader))
330 aList += cHeader;
331 break;
332 }
333 }
334 for (QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) {
335 if (file.endsWith(*it)) {
336 QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++";
337 if (!aList.contains(cppHeader))
338 aList += cppHeader;
339 break;
340 }
341 }
342 return aList;
343 }
344
345 QT_END_NAMESPACE
346