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