Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2016 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the qmake application of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:GPL-EXCEPT$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU
20** General Public License version 3 as published by the Free Software
21** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include "project.h"
31#include "property.h"
32#include "option.h"
33#include "cachekeys.h"
34#include "metamakefile.h"
35#include <qnamespace.h>
36#include <qdebug.h>
37#include <qregexp.h>
38#include <qdir.h>
39#include <qdiriterator.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <ctype.h>
43#include <fcntl.h>
44#include <sys/types.h>
45#include <sys/stat.h>
46
47#if defined(Q_OS_UNIX)
48#include <errno.h>
49#include <unistd.h>
50#endif
51
52#ifdef Q_OS_WIN
53# include <qt_windows.h>
54#endif
55
56using namespace QMakeInternal;
57
58QT_BEGIN_NAMESPACE
59
60#ifdef Q_OS_WIN
61
62struct SedSubst {
63 QRegExp from;
64 QString to;
65};
66Q_DECLARE_TYPEINFO(SedSubst, Q_MOVABLE_TYPE);
67
68static int doSed(int argc, char **argv)
69{
70 QVector<SedSubst> substs;
71 QList<const char *> inFiles;
72 for (int i = 0; i < argc; i++) {
73 if (!strcmp(argv[i], "-e")) {
74 if (++i == argc) {
75 fprintf(stderr, "Error: sed option -e requires an argument\n");
76 return 3;
77 }
78 QString cmd = QString::fromLocal8Bit(argv[i]);
79 for (int j = 0; j < cmd.length(); j++) {
80 QChar c = cmd.at(j);
81 if (c.isSpace())
82 continue;
83 if (c != QLatin1Char('s')) {
84 fprintf(stderr, "Error: unrecognized sed command '%c'\n", c.toLatin1());
85 return 3;
86 }
87 QChar sep = ++j < cmd.length() ? cmd.at(j) : QChar();
88 Qt::CaseSensitivity matchcase = Qt::CaseSensitive;
89 bool escaped = false;
90 int phase = 1;
91 QStringList phases;
92 QString curr;
93 while (++j < cmd.length()) {
94 c = cmd.at(j);
95 if (!escaped) {
96 if (c == QLatin1Char(';'))
97 break;
98 if (c == QLatin1Char('\\')) {
99 escaped = true;
100 continue;
101 }
102 if (c == sep) {
103 phase++;
104 phases << curr;
105 curr.clear();
106 continue;
107 }
108 }
109 if (phase == 1
110 && (c == QLatin1Char('+') || c == QLatin1Char('?') || c == QLatin1Char('|')
111 || c == QLatin1Char('{') || c == QLatin1Char('}')
112 || c == QLatin1Char('(') || c == QLatin1Char(')'))) {
113 // translate sed rx to QRegExp
114 escaped ^= 1;
115 }
116 if (escaped) {
117 escaped = false;
118 curr += QLatin1Char('\\');
119 }
120 curr += c;
121 }
122 if (escaped) {
123 fprintf(stderr, "Error: unterminated escape sequence in sed s command\n");
124 return 3;
125 }
126 if (phase != 3) {
127 fprintf(stderr, "Error: sed s command requires three arguments (%d, %c, %s)\n", phase, sep.toLatin1(), qPrintable(curr));
128 return 3;
129 }
130 if (curr.contains(QLatin1Char('i'))) {
131 curr.remove(QLatin1Char('i'));
132 matchcase = Qt::CaseInsensitive;
133 }
134 if (curr != QLatin1String("g")) {
135 fprintf(stderr, "Error: sed s command supports only g & i options; g is required\n");
136 return 3;
137 }
138 SedSubst subst;
139 subst.from = QRegExp(phases.at(0), matchcase);
140 subst.to = phases.at(1);
141 subst.to.replace(QLatin1String("\\\\"), QLatin1String("\\")); // QString::replace(rx, sub) groks \1, but not \\.
142 substs << subst;
143 }
144 } else if (argv[i][0] == '-' && argv[i][1] != 0) {
145 fprintf(stderr, "Error: unrecognized sed option '%s'\n", argv[i]);
146 return 3;
147 } else {
148 inFiles << argv[i];
149 }
150 }
151 if (inFiles.isEmpty())
152 inFiles << "-";
153 for (const char *inFile : qAsConst(inFiles)) {
154 FILE *f;
155 if (!strcmp(inFile, "-")) {
156 f = stdin;
157 } else if (!(f = fopen(inFile, "r"))) {
158 perror(inFile);
159 return 1;
160 }
161 QTextStream is(f);
162 while (!is.atEnd()) {
163 QString line = is.readLine();
164 for (int i = 0; i < substs.size(); i++)
165 line.replace(substs.at(i).from, substs.at(i).to);
166 puts(qPrintable(line));
167 }
168 if (f != stdin)
169 fclose(f);
170 }
171 return 0;
172}
173
174static int doLink(int argc, char **argv)
175{
176 bool isSymlink = false;
177 bool force = false;
178 QList<const char *> inFiles;
179 for (int i = 0; i < argc; i++) {
180 if (!strcmp(argv[i], "-s")) {
181 isSymlink = true;
182 } else if (!strcmp(argv[i], "-f")) {
183 force = true;
184 } else if (argv[i][0] == '-') {
185 fprintf(stderr, "Error: unrecognized ln option '%s'\n", argv[i]);
186 return 3;
187 } else {
188 inFiles << argv[i];
189 }
190 }
191 if (inFiles.size() != 2) {
192 fprintf(stderr, "Error: this ln requires exactly two file arguments\n");
193 return 3;
194 }
195 if (!isSymlink) {
196 fprintf(stderr, "Error: this ln supports faking symlinks only\n");
197 return 3;
198 }
199 QString target = QString::fromLocal8Bit(inFiles[0]);
200 QString linkname = QString::fromLocal8Bit(inFiles[1]);
201
202 QDir destdir;
203 QFileInfo tfi(target);
204 QFileInfo lfi(linkname);
205 if (lfi.isDir()) {
206 destdir.setPath(linkname);
207 lfi.setFile(destdir, tfi.fileName());
208 } else {
209 destdir.setPath(lfi.path());
210 }
211 if (!destdir.exists()) {
212 fprintf(stderr, "Error: destination directory %s does not exist\n", qPrintable(destdir.path()));
213 return 1;
214 }
215 tfi.setFile(destdir.absoluteFilePath(tfi.filePath()));
216 if (!tfi.exists()) {
217 fprintf(stderr, "Error: this ln does not support symlinking non-existing targets\n");
218 return 3;
219 }
220 if (tfi.isDir()) {
221 fprintf(stderr, "Error: this ln does not support symlinking directories\n");
222 return 3;
223 }
224 if (lfi.exists()) {
225 if (!force) {
226 fprintf(stderr, "Error: %s exists\n", qPrintable(lfi.filePath()));
227 return 1;
228 }
229 if (!QFile::remove(lfi.filePath())) {
230 fprintf(stderr, "Error: cannot overwrite %s\n", qPrintable(lfi.filePath()));
231 return 1;
232 }
233 }
234 if (!QFile::copy(tfi.filePath(), lfi.filePath())) {
235 fprintf(stderr, "Error: cannot copy %s to %s\n",
236 qPrintable(tfi.filePath()), qPrintable(lfi.filePath()));
237 return 1;
238 }
239
240 return 0;
241}
242
243#endif
244
245static int installFile(const QString &source, const QString &target, bool exe = false,
246 bool preservePermissions = false)
247{
248 QFile sourceFile(source);
249 QFile targetFile(target);
250 if (targetFile.exists()) {
251#ifdef Q_OS_WIN
252 targetFile.setPermissions(targetFile.permissions() | QFile::WriteUser);
253#endif
254 QFile::remove(target);
255 } else {
256 QDir::root().mkpath(QFileInfo(target).absolutePath());
257 }
258
259 if (!sourceFile.copy(target)) {
260 fprintf(stderr, "Error copying %s to %s: %s\n", source.toLatin1().constData(), qPrintable(target), qPrintable(sourceFile.errorString()));
261 return 3;
262 }
263
264 QFileDevice::Permissions targetPermissions = preservePermissions
265 ? sourceFile.permissions()
266 : (QFileDevice::ReadOwner | QFileDevice::WriteOwner
267 | QFileDevice::ReadUser | QFileDevice::WriteUser
268 | QFileDevice::ReadGroup | QFileDevice::ReadOther);
269 if (exe) {
270 targetPermissions |= QFileDevice::ExeOwner | QFileDevice::ExeUser |
271 QFileDevice::ExeGroup | QFileDevice::ExeOther;
272 }
273 if (!targetFile.setPermissions(targetPermissions)) {
274 fprintf(stderr, "Error setting permissions on %s: %s\n",
275 qPrintable(target), qPrintable(targetFile.errorString()));
276 return 3;
277 }
278
279 // Copy file times
280 QString error;
281 if (!IoUtils::touchFile(target, sourceFile.fileName(), &error)) {
282 fprintf(stderr, "%s", qPrintable(error));
283 return 3;
284 }
285 return 0;
286}
287
288static int installFileOrDirectory(const QString &source, const QString &target,
289 bool preservePermissions = false)
290{
291 QFileInfo fi(source);
292 if (false) {
293#if defined(Q_OS_UNIX)
294 } else if (fi.isSymLink()) {
295 QString linkTarget;
296 if (!IoUtils::readLinkTarget(fi.absoluteFilePath(), &linkTarget)) {
297 fprintf(stderr, "Could not read link %s: %s\n", qPrintable(fi.absoluteFilePath()), strerror(errno));
298 return 3;
299 }
300 QFile::remove(target);
301 if (::symlink(linkTarget.toLocal8Bit().constData(), target.toLocal8Bit().constData()) < 0) {
302 fprintf(stderr, "Could not create link: %s\n", strerror(errno));
303 return 3;
304 }
305#endif
306 } else if (fi.isDir()) {
307 QDir::current().mkpath(target);
308
309 QDirIterator it(source, QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden);
310 while (it.hasNext()) {
311 it.next();
312 const QFileInfo &entry = it.fileInfo();
313 const QString &entryTarget = target + QDir::separator() + entry.fileName();
314
315 const int recursionResult = installFileOrDirectory(entry.filePath(), entryTarget, true);
316 if (recursionResult != 0)
317 return recursionResult;
318 }
319 } else {
320 const int fileCopyResult = installFile(source, target, /*exe*/ false, preservePermissions);
321 if (fileCopyResult != 0)
322 return fileCopyResult;
323 }
324 return 0;
325}
326
327static int doQInstall(int argc, char **argv)
328{
329 bool installExecutable = false;
330 if (argc == 3 && !strcmp(argv[0], "-exe")) {
331 installExecutable = true;
332 --argc;
333 ++argv;
334 }
335
336 if (argc != 2 && !installExecutable) {
337 fprintf(stderr, "Error: usage: [-exe] source target\n");
338 return 3;
339 }
340
341 const QString source = QString::fromLocal8Bit(argv[0]);
342 const QString target = QString::fromLocal8Bit(argv[1]);
343
344 if (installExecutable)
345 return installFile(source, target, /*exe=*/true);
346 return installFileOrDirectory(source, target);
347}
348
349
350static int doInstall(int argc, char **argv)
351{
352 if (!argc) {
353 fprintf(stderr, "Error: -install requires further arguments\n");
354 return 3;
355 }
356#ifdef Q_OS_WIN
357 if (!strcmp(argv[0], "sed"))
358 return doSed(argc - 1, argv + 1);
359 if (!strcmp(argv[0], "ln"))
360 return doLink(argc - 1, argv + 1);
361#endif
362 if (!strcmp(argv[0], "qinstall"))
363 return doQInstall(argc - 1, argv + 1);
364 fprintf(stderr, "Error: unrecognized -install subcommand '%s'\n", argv[0]);
365 return 3;
366}
367
368
369#ifdef Q_OS_WIN
370
371static int dumpMacros(const wchar_t *cmdline)
372{
373 // from http://stackoverflow.com/questions/3665537/how-to-find-out-cl-exes-built-in-macros
374 int argc;
375 wchar_t **argv = CommandLineToArgvW(cmdline, &argc);
376 if (!argv)
377 return 2;
378 for (int i = 0; i < argc; ++i) {
379 if (argv[i][0] != L'-' || argv[i][1] != 'D')
380 continue;
381
382 wchar_t *value = wcschr(argv[i], L'=');
383 if (value) {
384 *value = 0;
385 ++value;
386 } else {
387 // point to the NUL at the end, so we don't print anything
388 value = argv[i] + wcslen(argv[i]);
389 }
390 wprintf(L"#define %Ls %Ls\n", argv[i] + 2, value);
391 }
392 return 0;
393}
394
395#endif // Q_OS_WIN
396
397/* This is to work around lame implementation on Darwin. It has been noted that the getpwd(3) function
398 is much too slow, and called much too often inside of Qt (every fileFixify). With this we use a locally
399 cached copy because I can control all the times it is set (because Qt never sets the pwd under me).
400*/
401static QString pwd;
402QString qmake_getpwd()
403{
404 if(pwd.isNull())
405 pwd = QDir::currentPath();
406 return pwd;
407}
408bool qmake_setpwd(const QString &p)
409{
410 if(QDir::setCurrent(p)) {
411 pwd = QDir::currentPath();
412 return true;
413 }
414 return false;
415}
416
417int runQMake(int argc, char **argv)
418{
419 // stderr is unbuffered by default, but stdout buffering depends on whether
420 // there is a terminal attached. Buffering can make output from stderr and stdout
421 // appear out of sync, so force stdout to be unbuffered as well.
422 // This is particularly important for things like QtCreator and scripted builds.
423 setvbuf(stdout, (char *)NULL, _IONBF, 0);
424
425 // Workaround for inferior/missing command line tools on Windows: make our own!
426 if (argc >= 2 && !strcmp(argv[1], "-install"))
427 return doInstall(argc - 2, argv + 2);
428
429#ifdef Q_OS_WIN
430 {
431 // Support running as Visual C++'s compiler
432 const wchar_t *cmdline = _wgetenv(L"MSC_CMD_FLAGS");
433 if (!cmdline || !*cmdline)
434 cmdline = _wgetenv(L"MSC_IDE_FLAGS");
435 if (cmdline && *cmdline)
436 return dumpMacros(cmdline);
437 }
438#endif
439
440 QMakeVfs vfs;
441 Option::vfs = &vfs;
442 QMakeGlobals globals;
443 Option::globals = &globals;
444
445 // parse command line
446 int ret = Option::init(argc, argv);
447 if(ret != Option::QMAKE_CMDLINE_SUCCESS) {
448 if ((ret & Option::QMAKE_CMDLINE_ERROR) != 0)
449 return 1;
450 return 0;
451 }
452
453 QString oldpwd = qmake_getpwd();
454
455 Option::output_dir = oldpwd; //for now this is the output dir
456 if (!Option::output.fileName().isEmpty() && Option::output.fileName() != "-") {
457 // The output 'filename', as given by the -o option, might include one
458 // or more directories, so we may need to rebase the output directory.
459 QFileInfo fi(Option::output);
460
461 QDir dir(QDir::cleanPath(fi.isDir() ? fi.absoluteFilePath() : fi.absolutePath()));
462
463 // Don't treat Xcode project directory as part of OUT_PWD
464 if (dir.dirName().endsWith(QLatin1String(".xcodeproj"))) {
465 // Note: we're intentionally not using cdUp(), as the dir may not exist
466 dir.setPath(QDir::cleanPath(dir.filePath("..")));
467 }
468
469 Option::output_dir = dir.path();
470 QString absoluteFilePath = QDir::cleanPath(fi.absoluteFilePath());
471 Option::output.setFileName(absoluteFilePath.mid(Option::output_dir.length() + 1));
472 }
473
474 QMakeProperty prop;
475 if(Option::qmake_mode == Option::QMAKE_QUERY_PROPERTY ||
476 Option::qmake_mode == Option::QMAKE_SET_PROPERTY ||
477 Option::qmake_mode == Option::QMAKE_UNSET_PROPERTY)
478 return prop.exec() ? 0 : 101;
479 globals.setQMakeProperty(&prop);
480
481 ProFileCache proFileCache;
482 Option::proFileCache = &proFileCache;
483 QMakeParser parser(&proFileCache, &vfs, &Option::evalHandler);
484 Option::parser = &parser;
485
486 QMakeProject project;
487 int exit_val = 0;
488 QStringList files;
489 if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT)
490 files << "(*hack*)"; //we don't even use files, but we do the for() body once
491 else
492 files = Option::mkfile::project_files;
493 for(QStringList::Iterator pfile = files.begin(); pfile != files.end(); pfile++) {
494 if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
495 Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
496 QString fn = Option::normalizePath(*pfile);
497 if(!QFile::exists(fn)) {
498 fprintf(stderr, "Cannot find file: %s.\n",
499 QDir::toNativeSeparators(fn).toLatin1().constData());
500 exit_val = 2;
501 continue;
502 }
503
504 //setup pwd properly
505 debug_msg(1, "Resetting dir to: %s",
506 QDir::toNativeSeparators(oldpwd).toLatin1().constData());
507 qmake_setpwd(oldpwd); //reset the old pwd
508 int di = fn.lastIndexOf(QLatin1Char('/'));
509 if(di != -1) {
510 debug_msg(1, "Changing dir to: %s",
511 QDir::toNativeSeparators(fn.left(di)).toLatin1().constData());
512 if(!qmake_setpwd(fn.left(di)))
513 fprintf(stderr, "Cannot find directory: %s\n",
514 QDir::toNativeSeparators(fn.left(di)).toLatin1().constData());
515 fn = fn.right(fn.length() - di - 1);
516 }
517
518 Option::prepareProject(fn);
519
520 // read project..
521 if(!project.read(fn)) {
522 fprintf(stderr, "Error processing project file: %s\n",
523 QDir::toNativeSeparators(*pfile).toLatin1().constData());
524 exit_val = 3;
525 continue;
526 }
527 if (Option::mkfile::do_preprocess) {
528 project.dump();
529 continue; //no need to create makefile
530 }
531 }
532
533 bool success = true;
534 MetaMakefileGenerator *mkfile = MetaMakefileGenerator::createMetaGenerator(&project, QString(), false, &success);
535 if (!success)
536 exit_val = 3;
537
538 if (mkfile && !mkfile->write()) {
539 if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT)
540 fprintf(stderr, "Unable to generate project file.\n");
541 else
542 fprintf(stderr, "Unable to generate makefile for: %s\n",
543 QDir::toNativeSeparators(*pfile).toLatin1().constData());
544 exit_val = 5;
545 }
546 delete mkfile;
547 mkfile = nullptr;
548 }
549 qmakeClearCaches();
550 return exit_val;
551}
552
553QT_END_NAMESPACE
554
555int main(int argc, char **argv)
556{
557 return QT_PREPEND_NAMESPACE(runQMake)(argc, argv);
558}
559

Warning: That file was not part of the compilation database. It may have many parsing errors.