1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "gbuild.h"
43 #include "option.h"
44 #include "meta.h"
45 #include <qdir.h>
46 #include <qregexp.h>
47 #include <qcryptographichash.h>
48 #include <qdebug.h>
49 #include <stdlib.h>
50 #include <time.h>
51 #ifdef Q_OS_UNIX
52 #  include <sys/types.h>
53 #  include <sys/stat.h>
54 #endif
55 
56 QT_BEGIN_NAMESPACE
57 
58 unsigned int dllbase = 0x01000000;
59 #define DLLOFFSET 0x600000
60 
GBuildMakefileGenerator()61 GBuildMakefileGenerator::GBuildMakefileGenerator() : MakefileGenerator()
62 {
63     nativebins << "moc" << "rcc" << "uic" << "bootstrap";
64 }
65 
66 bool
writeMakefile(QTextStream & text)67 GBuildMakefileGenerator::writeMakefile(QTextStream &text)
68 {
69     QStringList tmp;
70     QString filename(Option::output.fileName());
71     QString pathtoremove(qmake_getpwd());
72     QString relpath(pathtoremove);
73     QString strtarget(project->first("TARGET"));
74     bool isnativebin = nativebins.contains(strtarget);
75     relpath.replace(Option::output_dir, "");
76 
77     /* correct output for non-prl, non-recursive case */
78     QString outname(qmake_getpwd());
79     outname += QDir::separator();
80     outname += strtarget;
81     outname += projectSuffix();
82     Option::output.close();
83     Option::output.setFileName(outname);
84     MakefileGenerator::openOutput(Option::output, QString());
85 
86     if (strtarget != fileInfo(project->projectFile()).baseName().section('.', -2, -2)) {
87         QString gpjname(strtarget);
88         QString outputName(qmake_getpwd());
89         outputName += QDir::separator();
90         outputName += fileInfo(project->projectFile()).baseName();
91         outputName += projectSuffix();
92         QFile f(outputName);
93         f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
94         QTextStream t(&f);
95         t << "#!gbuild\n";
96         t << "[Project]\n";
97         t << gpjname << projectSuffix() << "\n";
98         if ((project->first("TEMPLATE") == "lib")
99                 && project->isActiveConfig("shared"))
100             t << gpjname << "_shared" << projectSuffix() << "\n";
101         t.flush();
102         gpjname += projectSuffix();
103         Option::output.close();
104         Option::output.setFileName(gpjname);
105         MakefileGenerator::openOutput(Option::output, QString());
106     }
107 
108     if ((project->first("TEMPLATE") == "app")
109             && (!isnativebin)) {
110         QTextStream t(&Option::output);
111         QString intname(strtarget);
112         intname += ".int";
113         /* this is for bulding an INTEGRITY application.
114          * generate the .int integrate file and the .gpj INTEGRITY Application
115          * project file, then go on with regular files */
116         t << "#!gbuild" << "\n";
117         t << "[INTEGRITY Application]" << "\n";
118         t << "\t:binDirRelative=.\n";
119         t << "\t-o " << strtarget << "\n";
120         t << intname << "\n";
121         t << strtarget << "_app" << projectSuffix() << "\n";
122         t.flush();
123 
124         /* generate integrate file */
125         QFile f(intname);
126         f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
127         QTextStream ti(&f);
128         ti << "# This is a file automatically generated by qmake" << "\n";
129         ti << "# Modifications will be lost next time you run qmake" << "\n";
130         ti << "Kernel" << "\n";
131         ti << "\tFilename\tDynamicDownload" << "\n";
132         ti << "EndKernel" << "\n" << "\n";
133         ti << "AddressSpace" << "\n";
134         ti << "\tName\t" << strtarget << "\n";
135         ti << "\tFilename\t" << strtarget << "_app" << "\n";
136         ti << "\tMemoryPoolSize\t0x100000" << "\n";
137         ti << "\tLanguage\tC++" << "\n";
138         /* FIXME : heap size is huge to be big enough for every example
139          * it should probably be tailored for each example, btu there is no
140          * good way to guess that */
141         ti << "\tHeapSize\t0x00D00000" << "\n";
142         ti << "\tTask\tInitial" << "\n";
143         ti << "\t\tStackSize\t0x30000" << "\n";
144         ti << "\t\tStartIt\tTrue" << "\n";
145         ti << "\tEndTask" << "\n";
146         ti << "EndAddressSpace" << "\n";
147         ti.flush();
148 
149         /* change current project file to <projectname>_app.gpj and continue
150          * generation */
151         outname.insert(outname.lastIndexOf("."), "_app");
152         Option::output.close();
153         Option::output.setFileName(outname);
154         MakefileGenerator::openOutput(Option::output, QString());
155     } else if ((project->first("TEMPLATE") == "lib")
156             && project->isActiveConfig("dll")) {
157         QString gpjname(strtarget);
158         gpjname += "_shared";
159         gpjname += projectSuffix();
160         QFile f(gpjname);
161         f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
162         QTextStream t(&f);
163         t << "#!gbuild\n"
164             "[Program]\n"
165             "\t-A libINTEGRITY.so\n"
166             "\t-A libc.so\n"
167             "\t-A libscxx.so\n"
168             "\t-A libQtCore.so\n"
169             "\t-e __ghsbegin_text\n"
170             "\t-startfile=-\n"
171             "\t:syslibraries=-\n"
172             "\t-Onolink\n";
173         t << "\t-o lib" << strtarget << ".so\n";
174         t << "\t-l" << strtarget << "\n";
175         t << "\t-extractall=-l" << strtarget << "\n";
176         t << "\t:outputDir=work/" << filename.section(QDir::separator(), 0, -1).remove(".gpj") << "\n";
177         t << strtarget << "_shared.ld\n";
178         t << "$(__OS_DIR)/intlib/sharedobjbssinit.c\n";
179         t.flush();
180 
181         QFile fl(strtarget + "_shared.ld");
182         fl.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
183         QTextStream tl(&fl);
184         tl << "CONSTANTS {\n"
185              "    __INTEGRITY_MinPageAlign          = 16K\n"
186              "    __INTEGRITY_MaxPageAlign          = 16K\n"
187              "    __INTEGRITY_LibCBaseAddress       = \n";
188         tl << dllbase << "\n";
189         tl << "}\n"
190              "-sec\n"
191              "{\n"
192              "        .picbase __INTEGRITY_LibCBaseAddress :\n"
193              "        .text :\n"
194              "        .syscall :\n"
195              "        .intercall :\n"
196              "        .interfunc :\n"
197              "        .secinfo :\n"
198              "        .rodata align(16) :\n"
199              "        .fixaddr :\n"
200              "        .fixtype :\n"
201              "        .rombeg :\n"
202              "        .textchecksum :\n"
203              "        // The above sections may be large. Leave a bigger gap for large pages.\n"
204              "        .pidbase align(__INTEGRITY_MaxPageAlign) :\n"
205              "        .sdabase :\n"
206              "        .data :\n"
207              "        .toc :\n"
208              "        .opd :\n"
209              "        .datachecksum :\n"
210              "        .sbss : \n"
211              "        .bss align(__INTEGRITY_MinPageAlign) :\n"
212              "        .argsection(__INTEGRITY_MaxPageAlign) :\n"
213              "        .heap : \n"
214              "}\n";
215         tl.flush();
216         dllbase += DLLOFFSET;
217     }
218 
219     warn_msg(WarnParser, Option::output.fileName().toAscii());
220     QTextStream t(&Option::output);
221     QString primaryTarget;
222     if (!project->values("QMAKE_CXX").isEmpty())
223         primaryTarget = project->values("QMAKE_CXX").at(0);
224 
225     pathtoremove += QDir::separator();
226     filename.remove(qmake_getpwd());
227 
228     //HEADER
229     t << "#!gbuild" << "\n";
230 
231     /* find the architecture out of the compiler name */
232     if (filename.endsWith("projects.gpj")) {
233         primaryTarget.remove(0, 5);
234         t << "macro QT_BUILD_DIR=%expand_path(.)\n";
235         t << "macro __OS_DIR=" << project->values("INTEGRITY_DIR").first() << "\n";
236         t << "primaryTarget=" << primaryTarget << "_integrity.tgt" << "\n";
237         t << "customization=util/integrity/qt.bod\n";
238     }
239     /* project type */
240     if (project->first("TEMPLATE") == "app") {
241         t << "[Program]" << "\n";
242         if (isnativebin) {
243             t << "\t:binDir=bin\n";
244             t << "\t-o " << strtarget << "\n";
245         } else {
246             t << "\t:binDirRelative=.\n";
247             t << "\t-o " << strtarget << "_app\n";
248         }
249     } else if (project->first("TEMPLATE") == "lib") {
250         t << "[Library]" << "\n";
251         t << "\t:binDir=lib" << "\n";
252         t << "\t-o lib" << strtarget << ".a" << "\n";
253     } else if (project->first("TEMPLATE") == "subdirs")
254         t << "[Project]" << "\n";
255     else
256         t << project->first("TEMPLATE") << "\n";
257 
258     /* compilations options */
259     t << "\t:sourceDir=." << "\n";
260 
261     t << "\t:outputDir=work" << relpath << "\n";
262     t << "\t-I${%expand_path(.)}/work" << relpath << "\n";
263     t << "\t--cxx_include_directory ${%expand_path(.)}/work" << relpath << "\n";
264     if (filename.endsWith("projects.gpj")) {
265         t << "\t:sourceDir=work\n";
266         t << "\t-Iwork\n";
267         t << "\t-Llib\n";
268         t << "\t";
269         QStringList &l = project->values("QMAKE_CXXFLAGS");
270         for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
271             if ((*it).startsWith("-"))
272                 t << "\n" << "\t" << (*it);
273             else
274                 t << " " << (*it);
275         }
276         t << "\n";
277     }
278     t << "\n";
279 
280     if (project->first("TEMPLATE") != "project")
281         t << varGlue("DEFINES", "\t-D", "\n\t-D", "\n");
282 
283     t << "\t-I.\n\t-I" << specdir() << "\n";
284     t << varGlue("INCLUDEPATH", "\t-I", "\n\t-I", "\n");
285     t << "\t--cxx_include_directory .\n\t--cxx_include_directory " << specdir() << "\n";
286     t << varGlue("INCLUDEPATH", "\t--cxx_include_directory ", "\n\t--cxx_include_directory ", "\n");
287 
288     if (project->first("TEMPLATE") == "app") {
289         /* include linker flags if it's an application */
290         QString src[] = { "QMAKE_LFLAGS", "QMAKE_FRAMEWORKPATH_FLAGS", "QMAKE_LIBDIR_FLAGS", "QMAKE_LIBS", "LIBS", QString() };
291         for (int i = 0; !src[i].isNull(); i++) {
292             /* skip target libraries for native tools */
293             if (isnativebin && (i == 0))
294                 continue;
295             t << "\t";
296             QStringList &l = project->values(src[i]);
297             for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
298                 if ((*it).startsWith("-"))
299                     t << "\n" << "\t" << (*it);
300                 else
301                     t << " " << (*it);
302             }
303             t << "\n";
304         }
305     }
306 
307     /* first subdirectories/subprojects */
308     {
309         QStringList &l = project->values("SUBDIRS");
310         for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
311             QString gpjname((*it));
312             /* avoid native tools */
313             if (nativebins.contains(gpjname.section("_", -1)))
314                 continue;
315             if (!project->first((*it) + ".subdir").isEmpty())
316                 gpjname = project->first((*it) + ".subdir");
317             /* some SUBDIRS are not actually subdirs, instead .pro files */
318             if (gpjname.endsWith(".pro"))
319                 gpjname.chop(4);
320             else
321                 gpjname += QDir::separator() + gpjname.section(QDir::separator(), -1);
322             gpjname += projectSuffix();
323             /* make relative */
324             if (!project->values("QT_SOURCE_TREE").isEmpty()) {
325                 gpjname.replace(project->values("QT_SOURCE_TREE").first() + QDir::separator(), "");
326             }
327             t << gpjname << "\n";
328         }
329     }
330 
331     {
332         QStringList &l = project->values("RESOURCES");
333         for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
334             QString tmpstr((*it).replace(pathtoremove, ""));
335             t << tmpstr << "\t[Qt Resource]\n";
336             tmpstr = tmpstr.section(".", -2, -1).section(QDir::separator(), -1);
337             tmpstr.remove(".qrc");
338             t << "\t-name " << tmpstr << "\n";
339             tmpstr.insert(tmpstr.lastIndexOf(QDir::separator()) + 1, "qrc_");
340             tmpstr.append(".cpp");
341             t << "\t-o work/" << relpath << QDir::separator() << tmpstr << "\n";
342         }
343     }
344     {
345         QStringList &l = project->values("FORMS");
346         for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
347             QString tmpstr((*it).replace(pathtoremove, ""));
348             t << tmpstr << "\t[Qt Dialog]\n";
349             tmpstr = tmpstr.section(".", 0, 0).section(QDir::separator(), -1);
350             tmpstr.insert(tmpstr.lastIndexOf(QDir::separator()) + 1, "ui_");
351             tmpstr.remove(".ui");
352             tmpstr.append(".h");
353             t << "\t-o work/" << relpath << QDir::separator() << tmpstr << "\n";
354         }
355     }
356 
357     /* source files for this project */
358     QString src[] = { "HEADERS", "SOURCES", QString() };
359     for (int i = 0; !src[i].isNull(); i++) {
360         QStringList &l = project->values(src[i]);
361         for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
362             if ((*it).isEmpty())
363                 continue;
364             /* native tools aren't preprocessed */
365             if (!isnativebin)
366                 t << writeOne((*it), pathtoremove);
367             else
368                 t << (*it).remove(pathtoremove) << "\n";
369         }
370     }
371     t << "\n";
372 
373     {
374         QStringList &l = project->values("GENERATED_SOURCES");
375         for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
376             t << "work/" << relpath << QDir::separator() << (*it).section(QDir::separator(), -1) << "\n";
377         }
378     }
379 
380     return true;
381 }
382 
writeOne(QString filename,QString pathtoremove)383 QString GBuildMakefileGenerator::writeOne(QString filename, QString pathtoremove)
384 {
385     QString s("");
386     QString origfilename(filename);
387     s += filename.remove(pathtoremove);
388     if (filename.endsWith(Option::h_ext.first()) && mocable(origfilename)) {
389         QString corename(filename.section(QDir::separator(), -1));
390         corename.remove(Option::h_ext.first());
391         corename.append(Option::cpp_ext.first());
392         corename.prepend(Option::h_moc_mod);
393         s += "\t[MOC/Qt Header]\n";
394         s += "\t-o ";
395         s += "work/";
396         s += pathtoremove;
397         s += QDir::separator();
398         s += corename;
399         s += "\n";
400     } else if (filename.section(QDir::separator(), -1).startsWith("qrc_")) {
401         QString tmpstr(filename.section("/", -1).section(".", 0, -1).remove("qrc_").remove(".cpp"));
402         s += "\n\t:depends=";
403         s += tmpstr;
404         s += ".qrc";
405         s += "\n";
406     } else if (filename.endsWith(Option::cpp_ext.first()) && mocable(origfilename)) {
407         QString tmpstr(filename.section("/", -1));
408         QString filepath(pathtoremove);
409         if (!project->values("QT_SOURCE_TREE").isEmpty()) {
410             filepath.remove(project->values("QT_SOURCE_TREE").first());
411             filepath.remove(0, 1);
412         }
413         s += "\n\t:preexecShellSafe='${QT_BUILD_DIR}/bin/moc ";
414         s += "-nn ";
415         s += varGlue("DEFINES", "-D", " -D", " ");
416         s += varGlue("INCLUDEPATH", "-I", " -I", " ");
417         s += filepath;
418         s += filename;
419         s += " -o ";
420         tmpstr.replace(Option::cpp_ext.first(), Option::cpp_moc_ext);
421         s += "work/";
422         s += pathtoremove;
423         s += QDir::separator();
424         s += tmpstr;
425         s += "\n";
426     } else
427         s += "\n";
428     return s;
429 }
430 
431 bool
openOutput(QFile & file,const QString & build) const432 GBuildMakefileGenerator::openOutput(QFile &file, const QString &build) const
433 {
434     debug_msg(1, "file is %s", file.fileName().toLatin1().constData());
435     QFileInfo fi(file);
436     if (fi.filePath().isEmpty())
437         file.setFileName(qmake_getpwd() + QDir::separator() + file.fileName());
438     if (!file.fileName().endsWith(projectSuffix())) {
439         QString outputName(file.fileName());
440         outputName += QDir::separator();
441         outputName += fileInfo(project->projectFile()).baseName();
442         outputName += projectSuffix();
443         warn_msg(WarnParser, outputName.toAscii());
444         file.setFileName(outputName);
445     }
446     debug_msg(1, "file is %s", file.fileName().toLatin1().constData());
447     bool ret = MakefileGenerator::openOutput(file, QString());
448     return ret;
449 }
450 
451 QT_END_NAMESPACE
452