1/*
2 Copyright (C) 2007 Matthias Kretz <kretz@kde.org>
3
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions
6 are met:
7
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24*/
25
26#include <QtCore/QCoreApplication>
27#include <QtCore/QDateTime>
28#include <QtCore/QDir>
29#include <QtCore/QFile>
30#include <QtCore/QFileInfo>
31#include <QtCore/QHash>
32#include <QtCore/QProcess>
33#include <QtCore/QQueue>
34#include <QtCore/QRegExp>
35#include <QtCore/QStringList>
36#include <QtCore/QTextStream>
37#include <QtCore/QtDebug>
38#include <cstdlib>
39#include <sys/types.h>
40#include <time.h>
41#include <errno.h>
42
43#ifdef Q_OS_WIN
44#include <windows.h>
45#include <sys/utime.h>
46#else
47#include <utime.h>
48#endif
49
50#if defined(Q_OS_DARWIN) || defined(Q_OS_MAC)
51#include <unistd.h>
52#endif
53
54// currently this is only used for the version number, Alex
55#include "automoc4_config.h"
56
57class AutoMoc
58{
59 public:
60 AutoMoc();
61 bool run();
62
63 private:
64 void dotFilesCheck(bool);
65 void lazyInitMocDefinitions();
66 void lazyInit();
67 bool touch(const QString &filename);
68 bool generateMoc(const QString &sourceFile, const QString &mocFileName);
69 void printUsage(const QString &);
70 void printVersion();
71 void echoColor(const QString &msg)
72 {
73 QProcess cmakeEcho;
74 cmakeEcho.setProcessChannelMode(QProcess::ForwardedChannels);
75 QStringList args(cmakeEchoColorArgs);
76 args << msg;
77 cmakeEcho.start(cmakeExecutable, args, QIODevice::NotOpen);
78 cmakeEcho.waitForFinished(-1);
79 }
80
81 QString builddir;
82 QString mocExe;
83 QStringList mocIncludes;
84 QStringList mocDefinitions;
85 QStringList cmakeEchoColorArgs;
86 QString cmakeExecutable;
87 QFile dotFiles;
88 const bool verbose;
89 QTextStream cerr;
90 QTextStream cout;
91 bool failed;
92 bool automocCppChanged;
93 bool generateAll;
94 bool doTouch;
95};
96
97void AutoMoc::printUsage(const QString &path)
98{
99 cout << "Usage: " << path << " <outfile> <srcdir> <builddir> <moc executable> <cmake executable> [--touch]" << endl;
100}
101
102void AutoMoc::printVersion()
103{
104 cout << "automoc4 " << AUTOMOC4_VERSION << endl;
105}
106
107void AutoMoc::dotFilesCheck(bool x)
108{
109 if (!x) {
110 cerr << "Error: syntax error in " << dotFiles.fileName() << endl;
111 ::exit(EXIT_FAILURE);
112 }
113}
114
115int main(int argc, char **argv)
116{
117 QCoreApplication app(argc, argv);
118 if (!AutoMoc().run()) {
119 return EXIT_FAILURE;
120 }
121 return 0;
122}
123
124AutoMoc::AutoMoc()
125 : verbose(!qgetenv("VERBOSE").isEmpty()), cerr(stderr), cout(stdout), failed(false),
126 automocCppChanged(false), generateAll(false), doTouch(false)
127{
128 const QByteArray colorEnv = qgetenv("COLOR");
129 cmakeEchoColorArgs << QLatin1String("-E") << QLatin1String("cmake_echo_color")
130 << QLatin1String("--switch=") + colorEnv << QLatin1String("--blue")
131 << QLatin1String("--bold");
132}
133
134void AutoMoc::lazyInitMocDefinitions()
135{
136 static bool done = false;
137 if (done) {
138 return;
139 }
140 done = true;
141 QByteArray line = dotFiles.readLine();
142 dotFilesCheck(line == "MOC_COMPILE_DEFINITIONS:\n");
143 line = dotFiles.readLine().trimmed();
144 const QStringList &cdefList = QString::fromUtf8(line).split(';', QString::SkipEmptyParts);
145 line = dotFiles.readLine();
146 dotFilesCheck(line == "MOC_DEFINITIONS:\n");
147 line = dotFiles.readLine().trimmed();
148 if (!cdefList.isEmpty()) {
149 foreach (const QString &def, cdefList) {
150 Q_ASSERT(!def.isEmpty());
151 mocDefinitions << QLatin1String("-D") + def;
152 }
153 } else {
154 const QStringList &defList = QString::fromUtf8(line).split(' ', QString::SkipEmptyParts);
155 foreach (const QString &def, defList) {
156 Q_ASSERT(!def.isEmpty());
157 if (def.startsWith(QLatin1String("-D"))) {
158 mocDefinitions << def;
159 }
160 }
161 }
162}
163
164void AutoMoc::lazyInit()
165{
166 const QStringList &args = QCoreApplication::arguments();
167
168 mocExe = args[4];
169 cmakeExecutable = args[5];
170
171 if (args.size() > 6) {
172 if (args[6] == QLatin1String("--touch")) {
173 doTouch = true;
174 }
175 }
176
177 lazyInitMocDefinitions();
178
179 QByteArray line = dotFiles.readLine();
180 dotFilesCheck(line == "MOC_INCLUDES:\n");
181 line = dotFiles.readLine().trimmed();
182 const QStringList &incPaths = QString::fromUtf8(line).split(';', QString::SkipEmptyParts);
183 QSet<QString> frameworkPaths;
184 foreach (const QString &path, incPaths) {
185 Q_ASSERT(!path.isEmpty());
186 mocIncludes << "-I" + path;
187 if (path.endsWith(QLatin1String(".framework/Headers"))) {
188 QDir framework(path);
189 // Go up twice to get to the framework root
190 framework.cdUp();
191 framework.cdUp();
192 frameworkPaths << framework.path();
193 }
194 }
195
196 foreach (const QString &path, frameworkPaths) {
197 mocIncludes << "-F" << path;
198 }
199
200 line = dotFiles.readLine();
201 dotFilesCheck(line == "CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE:\n");
202 line = dotFiles.readLine();
203 if (line == "ON\n") {
204 line = dotFiles.readLine();
205 dotFilesCheck(line == "CMAKE_BINARY_DIR:\n");
206 const QString &binDir = QLatin1String("-I") + QString::fromUtf8(dotFiles.readLine().trimmed());
207
208 line = dotFiles.readLine();
209 dotFilesCheck(line == "CMAKE_SOURCE_DIR:\n");
210 const QString &srcDir = QLatin1String("-I") + QString::fromUtf8(dotFiles.readLine().trimmed());
211
212 QStringList sortedMocIncludes;
213 QMutableListIterator<QString> it(mocIncludes);
214 while (it.hasNext()) {
215 if (it.next().startsWith(binDir)) {
216 sortedMocIncludes << it.value();
217 it.remove();
218 }
219 }
220 it.toFront();
221 while (it.hasNext()) {
222 if (it.next().startsWith(srcDir)) {
223 sortedMocIncludes << it.value();
224 it.remove();
225 }
226 }
227 sortedMocIncludes += mocIncludes;
228 mocIncludes = sortedMocIncludes;
229 }
230}
231
232bool AutoMoc::run()
233{
234 const QStringList &args = QCoreApplication::arguments();
235 Q_ASSERT(args.size() > 0);
236 if (args.size() == 2) {
237 if ((args[1]=="--help") || (args[1]=="-h")) {
238 printUsage(args[0]);
239 ::exit(0);
240 }
241 else if (args[1]=="--version") {
242 printVersion();
243 ::exit(0);
244 }
245 else {
246 printUsage(args[0]);
247 ::exit(EXIT_FAILURE);
248 }
249 }
250 else if (args.size() < 6) {
251 printUsage(args[0]);
252 ::exit(EXIT_FAILURE);
253 }
254 QFile outfile(args[1]);
255 const QFileInfo outfileInfo(outfile);
256
257 QString srcdir(args[2]);
258 if (!srcdir.endsWith('/')) {
259 srcdir += '/';
260 }
261 builddir = args[3];
262 if (!builddir.endsWith('/')) {
263 builddir += '/';
264 }
265
266 dotFiles.setFileName(args[1] + QLatin1String(".files"));
267 dotFiles.open(QIODevice::ReadOnly | QIODevice::Text);
268
269 const QByteArray &line = dotFiles.readLine();
270 dotFilesCheck(line == "SOURCES:\n");
271 const QStringList &sourceFiles = QString::fromUtf8(dotFiles.readLine().trimmed()).split(';', QString::SkipEmptyParts);
272
273 if (outfile.exists()) {
274 // set generateAll = true if MOC_COMPILE_DEFINITIONS changed
275 outfile.open(QIODevice::ReadOnly | QIODevice::Text);
276 QByteArray buf = outfile.readLine();
277 // the second line contains the joined mocDefinitions
278 buf = outfile.readLine();
279 buf.chop(1); // remove trailing \n
280 lazyInitMocDefinitions();
281 generateAll = (buf != mocDefinitions.join(QString(QLatin1Char(' '))).toUtf8());
282 outfile.close();
283 } else {
284 generateAll = true;
285 }
286
287 // the program goes through all .cpp files to see which moc files are included. It is not really
288 // interesting how the moc file is named, but what file the moc is created from. Once a moc is
289 // included the same moc may not be included in the _automoc.cpp file anymore. OTOH if there's a
290 // header containing Q_OBJECT where no corresponding moc file is included anywhere a
291 // moc_<filename>.cpp file is created and included in the _automoc.cpp file.
292 QHash<QString, QString> includedMocs; // key = moc source filepath, value = moc output filepath
293 QHash<QString, QString> notIncludedMocs; // key = moc source filepath, value = moc output filename
294
295 QRegExp mocIncludeRegExp(QLatin1String("[\n]\\s*#\\s*include\\s+[\"<]((?:[^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"));
296 QRegExp qObjectRegExp(QLatin1String("[\n]\\s*Q_OBJECT\\b"));
297 QStringList headerExtensions;
298#if defined(Q_OS_WIN)
299 // not case sensitive
300 headerExtensions << ".h" << ".hpp" << ".hxx";
301#elif defined(Q_OS_DARWIN) || defined(Q_OS_MAC)
302 headerExtensions << ".h" << ".hpp" << ".hxx";
303
304 // detect case-sensitive filesystem
305 long caseSensitive = pathconf(srcdir.toLocal8Bit(), _PC_CASE_SENSITIVE);
306 if (caseSensitive == 1) {
307 headerExtensions << ".H";
308 }
309#else
310 headerExtensions << ".h" << ".hpp" << ".hxx" << ".H";
311#endif
312 /* not safe: if a moc file is missing it's hard to get it generated if this check is "active"
313 const QDateTime &lastRun = QFileInfo(dotFiles).lastModified();
314 if (!generateAll) {
315 bool dirty = false;
316 foreach (const QString &absFilename, sourceFiles) {
317 const QFileInfo sourceFileInfo(absFilename);
318 if (sourceFileInfo.lastModified() >= lastRun) {
319 dirty = true;
320 break;
321 }
322 const QString &absPathBaseName = sourceFileInfo.absolutePath() + QLatin1Char('/') + sourceFileInfo.completeBaseName();
323 foreach (const QString &ext, headerExtensions) {
324 const QFileInfo header(absPathBaseName + ext);
325 if (header.exists() && header.lastModified() >= lastRun) {
326 dirty = true;
327 break;
328 }
329 const QFileInfo pheader(absPathBaseName + QLatin1String("_p") + ext);
330 if (pheader.exists() && pheader.lastModified() >= lastRun) {
331 dirty = true;
332 break;
333 }
334 }
335 if (dirty) {
336 break;
337 }
338 }
339 if (!dirty) {
340 return true;
341 }
342 }
343 */
344
345 foreach (const QString &absFilename, sourceFiles) {
346 //qDebug() << absFilename;
347 const QFileInfo sourceFileInfo(absFilename);
348 if (absFilename.endsWith(QLatin1String(".cpp")) || absFilename.endsWith(QLatin1String(".cc")) ||
349 absFilename.endsWith(QLatin1String(".mm")) || absFilename.endsWith(QLatin1String(".cxx")) ||
350 absFilename.endsWith(QLatin1String(".C"))) {
351 //qDebug() << "check .cpp file";
352 QFile sourceFile(absFilename);
353 sourceFile.open(QIODevice::ReadOnly);
354 const QByteArray contents = sourceFile.readAll();
355 if (contents.isEmpty()) {
356 cerr << "automoc4: empty source file: " << absFilename << endl;
357 continue;
358 }
359 const QString contentsString = QString::fromUtf8(contents);
360 const QString absPath = sourceFileInfo.absolutePath() + '/';
361 Q_ASSERT(absPath.endsWith('/'));
362 int matchOffset = mocIncludeRegExp.indexIn(contentsString);
363 if (matchOffset < 0) {
364 // no moc #include, look whether we need to create a moc from the .h nevertheless
365 //qDebug() << "no moc #include in the .cpp file";
366 const QString basename = sourceFileInfo.completeBaseName();
367 foreach (const QString &ext, headerExtensions) {
368 const QString headername = absPath + basename + ext;
369 if (QFile::exists(headername) && !includedMocs.contains(headername) &&
370 !notIncludedMocs.contains(headername)) {
371 const QString currentMoc = "moc_" + basename + ".cpp";
372 QFile header(headername);
373 header.open(QIODevice::ReadOnly);
374 const QByteArray contents = header.readAll();
375 if (qObjectRegExp.indexIn(QString::fromUtf8(contents)) >= 0) {
376 //qDebug() << "header contains Q_OBJECT macro";
377 notIncludedMocs.insert(headername, currentMoc);
378 }
379 break;
380 }
381 }
382 foreach (const QString &ext, headerExtensions) {
383 const QString privateHeaderName = absPath + basename + "_p" + ext;
384 if (QFile::exists(privateHeaderName) && !includedMocs.contains(privateHeaderName) &&
385 !notIncludedMocs.contains(privateHeaderName)) {
386 const QString currentMoc = "moc_" + basename + "_p.cpp";
387 QFile header(privateHeaderName);
388 header.open(QIODevice::ReadOnly);
389 const QByteArray contents = header.readAll();
390 if (qObjectRegExp.indexIn(QString::fromUtf8(contents)) >= 0) {
391 //qDebug() << "header contains Q_OBJECT macro";
392 notIncludedMocs.insert(privateHeaderName, currentMoc);
393 }
394 break;
395 }
396 }
397 } else {
398 do { // call this for every moc include in the file
399 const QString currentMoc = mocIncludeRegExp.cap(1);
400 //qDebug() << "found moc include: " << currentMoc << " at offset " << matchOffset;
401 const QFileInfo currentMocInfo(currentMoc);
402 QString basename = currentMocInfo.completeBaseName();
403 const bool moc_style = basename.startsWith(QLatin1String("moc_"));
404
405 // If the moc include is of the moc_foo.cpp style we expect the Q_OBJECT class
406 // declaration in a header file.
407 // If the moc include is of the foo.moc style we need to look for a Q_OBJECT
408 // macro in the current source file, if it contains the macro we generate the
409 // moc file from the source file, else from the header.
410 //
411 // TODO: currently any .moc file name will be used if the source contains
412 // Q_OBJECT
413 if (moc_style || qObjectRegExp.indexIn(contentsString) < 0) {
414 if (moc_style) {
415 // basename should be the part of the moc filename used for finding the
416 // correct header, so we need to remove the moc_ part
417 basename = basename.right(basename.length() - 4);
418 }
419
420 bool headerFound = false;
421 foreach (const QString &ext, headerExtensions) {
422 const QString &sourceFilePath = absPath + basename + ext;
423 if (QFile::exists(sourceFilePath)) {
424 headerFound = true;
425 includedMocs.insert(sourceFilePath, currentMoc);
426 notIncludedMocs.remove(sourceFilePath);
427 break;
428 }
429 }
430 if (!headerFound) {
431 // the moc file is in a subdir => look for the header in the same subdir
432 if (currentMoc.indexOf('/') != -1) {
433 const QString &filepath = absPath + currentMocInfo.path() + QLatin1Char('/') + basename;
434
435 foreach (const QString &ext, headerExtensions) {
436 const QString &sourceFilePath = filepath + ext;
437 if (QFile::exists(sourceFilePath)) {
438 headerFound = true;
439 includedMocs.insert(sourceFilePath, currentMoc);
440 notIncludedMocs.remove(sourceFilePath);
441 break;
442 }
443 }
444 if (!headerFound) {
445 cerr << "automoc4: The file \"" << absFilename <<
446 "\" includes the moc file \"" << currentMoc << "\", but neither \"" <<
447 absPath + basename + '{' + headerExtensions.join(",") + "}\" nor \"" <<
448 filepath + '{' + headerExtensions.join(",") + '}' <<
449 "\" exist." << endl;
450 ::exit(EXIT_FAILURE);
451 }
452 } else {
453 cerr << "automoc4: The file \"" << absFilename <<
454 "\" includes the moc file \"" << currentMoc << "\", but \"" <<
455 absPath + basename + '{' + headerExtensions.join(",") + '}' <<
456 "\" does not exist." << endl;
457 ::exit(EXIT_FAILURE);
458 }
459 }
460 } else {
461 includedMocs.insert(absFilename, currentMoc);
462 notIncludedMocs.remove(absFilename);
463 }
464
465 matchOffset = mocIncludeRegExp.indexIn(contentsString,
466 matchOffset + currentMoc.length());
467 } while(matchOffset >= 0);
468 }
469 } else if (absFilename.endsWith(QLatin1String(".h")) || absFilename.endsWith(QLatin1String(".hpp")) ||
470 absFilename.endsWith(QLatin1String(".hxx")) || absFilename.endsWith(QLatin1String(".H"))) {
471 if (!includedMocs.contains(absFilename) && !notIncludedMocs.contains(absFilename)) {
472 // if this header is not getting processed yet and is explicitly mentioned for the
473 // automoc the moc is run unconditionally on the header and the resulting file is
474 // included in the _automoc.cpp file (unless there's a .cpp file later on that
475 // includes the moc from this header)
476 const QString currentMoc = "moc_" + sourceFileInfo.completeBaseName() + ".cpp";
477 notIncludedMocs.insert(absFilename, currentMoc);
478 }
479 } else {
480 if (verbose) {
481 cout << "automoc4: ignoring file '" << absFilename << "' with unknown suffix" << endl;
482 }
483 }
484 }
485
486 // run moc on all the moc's that are #included in source files
487 QHash<QString, QString>::ConstIterator end = includedMocs.constEnd();
488 QHash<QString, QString>::ConstIterator it = includedMocs.constBegin();
489 for (; it != end; ++it) {
490 generateMoc(it.key(), it.value());
491 }
492
493 QByteArray automocSource;
494 QTextStream outStream(&automocSource, QIODevice::WriteOnly);
495 outStream << "/* This file is autogenerated, do not edit\n"
496 << mocDefinitions.join(QString(QLatin1Char(' '))) << "\n*/\n";
497
498 if (notIncludedMocs.isEmpty()) {
499 outStream << "enum some_compilers { need_more_than_nothing };\n";
500 } else {
501 // run moc on the remaining headers and include them in the _automoc.cpp file
502 end = notIncludedMocs.constEnd();
503 it = notIncludedMocs.constBegin();
504 for (; it != end; ++it) {
505 if (generateMoc(it.key(), it.value())) {
506 automocCppChanged = true;
507 }
508 outStream << "#include \"" << it.value() << "\"\n";
509 }
510 }
511
512 if (failed) {
513 // if any moc process failed we don't want to touch the _automoc.cpp file so that
514 // automoc4 is rerun until the issue is fixed
515 cerr << "returning failed.."<< endl;
516 return false;
517 }
518 outStream.flush();
519
520 if (!automocCppChanged) {
521 // compare contents of the _automoc.cpp file
522 outfile.open(QIODevice::ReadOnly | QIODevice::Text);
523 const QByteArray oldContents = outfile.readAll();
524 outfile.close();
525 if (oldContents == automocSource) {
526 // nothing changed: don't touch the _automoc.cpp file
527 return true;
528 }
529 }
530 // either the contents of the _automoc.cpp file or one of the mocs included by it have changed
531
532 // source file that includes all remaining moc files (_automoc.cpp file)
533 outfile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
534 outfile.write(automocSource);
535 outfile.close();
536
537 // update the timestamp on the _automoc.cpp.files file to make sure we get called again
538 dotFiles.close();
539 if (doTouch && !touch(dotFiles.fileName())) {
540 return false;
541 }
542
543 return true;
544}
545
546bool AutoMoc::touch(const QString &_filename)
547{
548 // sleep for 1s in order to make the modification time greater than the modification time of
549 // the files written before. Equal modification time is not good enough. Just using utime with
550 // time(NULL) + 1 is also not a good solution as then make will complain about clock skew.
551#ifdef Q_OS_WIN
552 Sleep(1000);
553 _wutime(reinterpret_cast<const wchar_t *>(_filename.utf16()), 0);
554#else
555 const QByteArray &filename = QFile::encodeName(_filename);
556 const struct timespec sleepDuration = { 1, 0 };
557 nanosleep(&sleepDuration, NULL);
558
559 int err = utime(filename.constData(), NULL);
560 if (err == -1) {
561 err = errno;
562 cerr << strerror(err) << "\n";
563 return false;
564 }
565#endif
566 return true;
567}
568
569bool AutoMoc::generateMoc(const QString &sourceFile, const QString &mocFileName)
570{
571 //qDebug() << Q_FUNC_INFO << sourceFile << mocFileName;
572 const QString mocFilePath = builddir + mocFileName;
573 QFileInfo mocInfo(mocFilePath);
574 if (generateAll || mocInfo.lastModified() <= QFileInfo(sourceFile).lastModified()) {
575 QDir mocDir = mocInfo.dir();
576 // make sure the directory for the resulting moc file exists
577 if (!mocDir.exists()) {
578 mocDir.mkpath(mocDir.path());
579 }
580
581 static bool initialized = false;
582 if (!initialized) {
583 initialized = true;
584 lazyInit();
585 }
586 if (verbose) {
587 echoColor("Generating " + mocFilePath + " from " + sourceFile);
588 } else {
589 echoColor("Generating " + mocFileName);
590 }
591
592 QProcess mocProc;
593 mocProc.setProcessChannelMode(QProcess::ForwardedChannels);
594 QStringList args(mocIncludes + mocDefinitions);
595#ifdef Q_OS_WIN
596 args << "-DWIN32";
597#endif
598 args << QLatin1String("-o") << mocFilePath << sourceFile;
599 //qDebug() << "executing: " << mocExe << args;
600 if (verbose) {
601 cout << mocExe << " " << args.join(QLatin1String(" ")) << endl;
602 }
603 mocProc.start(mocExe, args, QIODevice::NotOpen);
604 if (mocProc.waitForStarted()) {
605 const bool result = mocProc.waitForFinished(-1);
606 if (!result || mocProc.exitCode()) {
607 cerr << "automoc4: process for " << mocFilePath
608 << " failed: " << mocProc.errorString() << endl;
609 cerr << "pid to wait for: " << mocProc.pid() << endl;
610 failed = true;
611 QFile::remove(mocFilePath);
612 }
613 return true;
614 } else {
615 cerr << "automoc4: process for " << mocFilePath << "failed to start: "
616 << mocProc.errorString() << endl;
617 failed = true;
618 }
619 }
620 return false;
621}
622