123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622 |
- #include <QtCore/QCoreApplication>
- #include <QtCore/QDateTime>
- #include <QtCore/QDir>
- #include <QtCore/QFile>
- #include <QtCore/QFileInfo>
- #include <QtCore/QHash>
- #include <QtCore/QProcess>
- #include <QtCore/QQueue>
- #include <QtCore/QRegExp>
- #include <QtCore/QStringList>
- #include <QtCore/QTextStream>
- #include <QtCore/QtDebug>
- #include <cstdlib>
- #include <sys/types.h>
- #include <time.h>
- #include <errno.h>
- #ifdef Q_OS_WIN
- #include <windows.h>
- #include <sys/utime.h>
- #else
- #include <utime.h>
- #endif
- #if defined(Q_OS_DARWIN) || defined(Q_OS_MAC)
- #include <unistd.h>
- #endif
- #include "automoc4_config.h"
- class AutoMoc
- {
- public:
- AutoMoc();
- bool run();
- private:
- void dotFilesCheck(bool);
- void lazyInitMocDefinitions();
- void lazyInit();
- bool touch(const QString &filename);
- bool generateMoc(const QString &sourceFile, const QString &mocFileName);
- void printUsage(const QString &);
- void printVersion();
- void echoColor(const QString &msg)
- {
- QProcess cmakeEcho;
- cmakeEcho.setProcessChannelMode(QProcess::ForwardedChannels);
- QStringList args(cmakeEchoColorArgs);
- args << msg;
- cmakeEcho.start(cmakeExecutable, args, QIODevice::NotOpen);
- cmakeEcho.waitForFinished(-1);
- }
- QString builddir;
- QString mocExe;
- QStringList mocIncludes;
- QStringList mocDefinitions;
- QStringList cmakeEchoColorArgs;
- QString cmakeExecutable;
- QFile dotFiles;
- const bool verbose;
- QTextStream cerr;
- QTextStream cout;
- bool failed;
- bool automocCppChanged;
- bool generateAll;
- bool doTouch;
- };
- void AutoMoc::printUsage(const QString &path)
- {
- cout << "Usage: " << path << " <outfile> <srcdir> <builddir> <moc executable> <cmake executable> [--touch]" << endl;
- }
- void AutoMoc::printVersion()
- {
- cout << "automoc4 " << AUTOMOC4_VERSION << endl;
- }
- void AutoMoc::dotFilesCheck(bool x)
- {
- if (!x) {
- cerr << "Error: syntax error in " << dotFiles.fileName() << endl;
- ::exit(EXIT_FAILURE);
- }
- }
- int main(int argc, char **argv)
- {
- QCoreApplication app(argc, argv);
- if (!AutoMoc().run()) {
- return EXIT_FAILURE;
- }
- return 0;
- }
- AutoMoc::AutoMoc()
- : verbose(!qgetenv("VERBOSE").isEmpty()), cerr(stderr), cout(stdout), failed(false),
- automocCppChanged(false), generateAll(false), doTouch(false)
- {
- const QByteArray colorEnv = qgetenv("COLOR");
- cmakeEchoColorArgs << QLatin1String("-E") << QLatin1String("cmake_echo_color")
- << QLatin1String("--switch=") + colorEnv << QLatin1String("--blue")
- << QLatin1String("--bold");
- }
- void AutoMoc::lazyInitMocDefinitions()
- {
- static bool done = false;
- if (done) {
- return;
- }
- done = true;
- QByteArray line = dotFiles.readLine();
- dotFilesCheck(line == "MOC_COMPILE_DEFINITIONS:\n");
- line = dotFiles.readLine().trimmed();
- const QStringList &cdefList = QString::fromUtf8(line).split(';', QString::SkipEmptyParts);
- line = dotFiles.readLine();
- dotFilesCheck(line == "MOC_DEFINITIONS:\n");
- line = dotFiles.readLine().trimmed();
- if (!cdefList.isEmpty()) {
- foreach (const QString &def, cdefList) {
- Q_ASSERT(!def.isEmpty());
- mocDefinitions << QLatin1String("-D") + def;
- }
- } else {
- const QStringList &defList = QString::fromUtf8(line).split(' ', QString::SkipEmptyParts);
- foreach (const QString &def, defList) {
- Q_ASSERT(!def.isEmpty());
- if (def.startsWith(QLatin1String("-D"))) {
- mocDefinitions << def;
- }
- }
- }
- }
- void AutoMoc::lazyInit()
- {
- const QStringList &args = QCoreApplication::arguments();
- mocExe = args[4];
- cmakeExecutable = args[5];
- if (args.size() > 6) {
- if (args[6] == QLatin1String("--touch")) {
- doTouch = true;
- }
- }
- lazyInitMocDefinitions();
- QByteArray line = dotFiles.readLine();
- dotFilesCheck(line == "MOC_INCLUDES:\n");
- line = dotFiles.readLine().trimmed();
- const QStringList &incPaths = QString::fromUtf8(line).split(';', QString::SkipEmptyParts);
- QSet<QString> frameworkPaths;
- foreach (const QString &path, incPaths) {
- Q_ASSERT(!path.isEmpty());
- mocIncludes << "-I" + path;
- if (path.endsWith(QLatin1String(".framework/Headers"))) {
- QDir framework(path);
-
- framework.cdUp();
- framework.cdUp();
- frameworkPaths << framework.path();
- }
- }
- foreach (const QString &path, frameworkPaths) {
- mocIncludes << "-F" << path;
- }
- line = dotFiles.readLine();
- dotFilesCheck(line == "CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE:\n");
- line = dotFiles.readLine();
- if (line == "ON\n") {
- line = dotFiles.readLine();
- dotFilesCheck(line == "CMAKE_BINARY_DIR:\n");
- const QString &binDir = QLatin1String("-I") + QString::fromUtf8(dotFiles.readLine().trimmed());
- line = dotFiles.readLine();
- dotFilesCheck(line == "CMAKE_SOURCE_DIR:\n");
- const QString &srcDir = QLatin1String("-I") + QString::fromUtf8(dotFiles.readLine().trimmed());
- QStringList sortedMocIncludes;
- QMutableListIterator<QString> it(mocIncludes);
- while (it.hasNext()) {
- if (it.next().startsWith(binDir)) {
- sortedMocIncludes << it.value();
- it.remove();
- }
- }
- it.toFront();
- while (it.hasNext()) {
- if (it.next().startsWith(srcDir)) {
- sortedMocIncludes << it.value();
- it.remove();
- }
- }
- sortedMocIncludes += mocIncludes;
- mocIncludes = sortedMocIncludes;
- }
- }
- bool AutoMoc::run()
- {
- const QStringList &args = QCoreApplication::arguments();
- Q_ASSERT(args.size() > 0);
- if (args.size() == 2) {
- if ((args[1]=="--help") || (args[1]=="-h")) {
- printUsage(args[0]);
- ::exit(0);
- }
- else if (args[1]=="--version") {
- printVersion();
- ::exit(0);
- }
- else {
- printUsage(args[0]);
- ::exit(EXIT_FAILURE);
- }
- }
- else if (args.size() < 6) {
- printUsage(args[0]);
- ::exit(EXIT_FAILURE);
- }
- QFile outfile(args[1]);
- const QFileInfo outfileInfo(outfile);
- QString srcdir(args[2]);
- if (!srcdir.endsWith('/')) {
- srcdir += '/';
- }
- builddir = args[3];
- if (!builddir.endsWith('/')) {
- builddir += '/';
- }
- dotFiles.setFileName(args[1] + QLatin1String(".files"));
- dotFiles.open(QIODevice::ReadOnly | QIODevice::Text);
- const QByteArray &line = dotFiles.readLine();
- dotFilesCheck(line == "SOURCES:\n");
- const QStringList &sourceFiles = QString::fromUtf8(dotFiles.readLine().trimmed()).split(';', QString::SkipEmptyParts);
- if (outfile.exists()) {
-
- outfile.open(QIODevice::ReadOnly | QIODevice::Text);
- QByteArray buf = outfile.readLine();
-
- buf = outfile.readLine();
- buf.chop(1);
- lazyInitMocDefinitions();
- generateAll = (buf != mocDefinitions.join(QString(QLatin1Char(' '))).toUtf8());
- outfile.close();
- } else {
- generateAll = true;
- }
-
-
-
-
-
- QHash<QString, QString> includedMocs;
- QHash<QString, QString> notIncludedMocs;
- QRegExp mocIncludeRegExp(QLatin1String("[\n]\\s*#\\s*include\\s+[\"<]((?:[^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"));
- QRegExp qObjectRegExp(QLatin1String("[\n]\\s*Q_OBJECT\\b"));
- QStringList headerExtensions;
- #if defined(Q_OS_WIN)
-
- headerExtensions << ".h" << ".hpp" << ".hxx";
- #elif defined(Q_OS_DARWIN) || defined(Q_OS_MAC)
- headerExtensions << ".h" << ".hpp" << ".hxx";
-
- long caseSensitive = pathconf(srcdir.toLocal8Bit(), _PC_CASE_SENSITIVE);
- if (caseSensitive == 1) {
- headerExtensions << ".H";
- }
- #else
- headerExtensions << ".h" << ".hpp" << ".hxx" << ".H";
- #endif
-
- foreach (const QString &absFilename, sourceFiles) {
-
- const QFileInfo sourceFileInfo(absFilename);
- if (absFilename.endsWith(QLatin1String(".cpp")) || absFilename.endsWith(QLatin1String(".cc")) ||
- absFilename.endsWith(QLatin1String(".mm")) || absFilename.endsWith(QLatin1String(".cxx")) ||
- absFilename.endsWith(QLatin1String(".C"))) {
-
- QFile sourceFile(absFilename);
- sourceFile.open(QIODevice::ReadOnly);
- const QByteArray contents = sourceFile.readAll();
- if (contents.isEmpty()) {
- cerr << "automoc4: empty source file: " << absFilename << endl;
- continue;
- }
- const QString contentsString = QString::fromUtf8(contents);
- const QString absPath = sourceFileInfo.absolutePath() + '/';
- Q_ASSERT(absPath.endsWith('/'));
- int matchOffset = mocIncludeRegExp.indexIn(contentsString);
- if (matchOffset < 0) {
-
-
- const QString basename = sourceFileInfo.completeBaseName();
- foreach (const QString &ext, headerExtensions) {
- const QString headername = absPath + basename + ext;
- if (QFile::exists(headername) && !includedMocs.contains(headername) &&
- !notIncludedMocs.contains(headername)) {
- const QString currentMoc = "moc_" + basename + ".cpp";
- QFile header(headername);
- header.open(QIODevice::ReadOnly);
- const QByteArray contents = header.readAll();
- if (qObjectRegExp.indexIn(QString::fromUtf8(contents)) >= 0) {
-
- notIncludedMocs.insert(headername, currentMoc);
- }
- break;
- }
- }
- foreach (const QString &ext, headerExtensions) {
- const QString privateHeaderName = absPath + basename + "_p" + ext;
- if (QFile::exists(privateHeaderName) && !includedMocs.contains(privateHeaderName) &&
- !notIncludedMocs.contains(privateHeaderName)) {
- const QString currentMoc = "moc_" + basename + "_p.cpp";
- QFile header(privateHeaderName);
- header.open(QIODevice::ReadOnly);
- const QByteArray contents = header.readAll();
- if (qObjectRegExp.indexIn(QString::fromUtf8(contents)) >= 0) {
-
- notIncludedMocs.insert(privateHeaderName, currentMoc);
- }
- break;
- }
- }
- } else {
- do {
- const QString currentMoc = mocIncludeRegExp.cap(1);
-
- const QFileInfo currentMocInfo(currentMoc);
- QString basename = currentMocInfo.completeBaseName();
- const bool moc_style = basename.startsWith(QLatin1String("moc_"));
-
-
-
-
-
-
-
-
- if (moc_style || qObjectRegExp.indexIn(contentsString) < 0) {
- if (moc_style) {
-
-
- basename = basename.right(basename.length() - 4);
- }
- bool headerFound = false;
- foreach (const QString &ext, headerExtensions) {
- const QString &sourceFilePath = absPath + basename + ext;
- if (QFile::exists(sourceFilePath)) {
- headerFound = true;
- includedMocs.insert(sourceFilePath, currentMoc);
- notIncludedMocs.remove(sourceFilePath);
- break;
- }
- }
- if (!headerFound) {
-
- if (currentMoc.indexOf('/') != -1) {
- const QString &filepath = absPath + currentMocInfo.path() + QLatin1Char('/') + basename;
- foreach (const QString &ext, headerExtensions) {
- const QString &sourceFilePath = filepath + ext;
- if (QFile::exists(sourceFilePath)) {
- headerFound = true;
- includedMocs.insert(sourceFilePath, currentMoc);
- notIncludedMocs.remove(sourceFilePath);
- break;
- }
- }
- if (!headerFound) {
- cerr << "automoc4: The file \"" << absFilename <<
- "\" includes the moc file \"" << currentMoc << "\", but neither \"" <<
- absPath + basename + '{' + headerExtensions.join(",") + "}\" nor \"" <<
- filepath + '{' + headerExtensions.join(",") + '}' <<
- "\" exist." << endl;
- ::exit(EXIT_FAILURE);
- }
- } else {
- cerr << "automoc4: The file \"" << absFilename <<
- "\" includes the moc file \"" << currentMoc << "\", but \"" <<
- absPath + basename + '{' + headerExtensions.join(",") + '}' <<
- "\" does not exist." << endl;
- ::exit(EXIT_FAILURE);
- }
- }
- } else {
- includedMocs.insert(absFilename, currentMoc);
- notIncludedMocs.remove(absFilename);
- }
- matchOffset = mocIncludeRegExp.indexIn(contentsString,
- matchOffset + currentMoc.length());
- } while(matchOffset >= 0);
- }
- } else if (absFilename.endsWith(QLatin1String(".h")) || absFilename.endsWith(QLatin1String(".hpp")) ||
- absFilename.endsWith(QLatin1String(".hxx")) || absFilename.endsWith(QLatin1String(".H"))) {
- if (!includedMocs.contains(absFilename) && !notIncludedMocs.contains(absFilename)) {
-
-
-
-
- const QString currentMoc = "moc_" + sourceFileInfo.completeBaseName() + ".cpp";
- notIncludedMocs.insert(absFilename, currentMoc);
- }
- } else {
- if (verbose) {
- cout << "automoc4: ignoring file '" << absFilename << "' with unknown suffix" << endl;
- }
- }
- }
-
- QHash<QString, QString>::ConstIterator end = includedMocs.constEnd();
- QHash<QString, QString>::ConstIterator it = includedMocs.constBegin();
- for (; it != end; ++it) {
- generateMoc(it.key(), it.value());
- }
- QByteArray automocSource;
- QTextStream outStream(&automocSource, QIODevice::WriteOnly);
- outStream << "/* This file is autogenerated, do not edit\n"
- << mocDefinitions.join(QString(QLatin1Char(' '))) << "\n*/\n";
- if (notIncludedMocs.isEmpty()) {
- outStream << "enum some_compilers { need_more_than_nothing };\n";
- } else {
-
- end = notIncludedMocs.constEnd();
- it = notIncludedMocs.constBegin();
- for (; it != end; ++it) {
- if (generateMoc(it.key(), it.value())) {
- automocCppChanged = true;
- }
- outStream << "#include \"" << it.value() << "\"\n";
- }
- }
- if (failed) {
-
-
- cerr << "returning failed.."<< endl;
- return false;
- }
- outStream.flush();
- if (!automocCppChanged) {
-
- outfile.open(QIODevice::ReadOnly | QIODevice::Text);
- const QByteArray oldContents = outfile.readAll();
- outfile.close();
- if (oldContents == automocSource) {
-
- return true;
- }
- }
-
-
- outfile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
- outfile.write(automocSource);
- outfile.close();
-
- dotFiles.close();
- if (doTouch && !touch(dotFiles.fileName())) {
- return false;
- }
- return true;
- }
- bool AutoMoc::touch(const QString &_filename)
- {
-
-
-
- #ifdef Q_OS_WIN
- Sleep(1000);
- _wutime(reinterpret_cast<const wchar_t *>(_filename.utf16()), 0);
- #else
- const QByteArray &filename = QFile::encodeName(_filename);
- const struct timespec sleepDuration = { 1, 0 };
- nanosleep(&sleepDuration, NULL);
- int err = utime(filename.constData(), NULL);
- if (err == -1) {
- err = errno;
- cerr << strerror(err) << "\n";
- return false;
- }
- #endif
- return true;
- }
- bool AutoMoc::generateMoc(const QString &sourceFile, const QString &mocFileName)
- {
-
- const QString mocFilePath = builddir + mocFileName;
- QFileInfo mocInfo(mocFilePath);
- if (generateAll || mocInfo.lastModified() <= QFileInfo(sourceFile).lastModified()) {
- QDir mocDir = mocInfo.dir();
-
- if (!mocDir.exists()) {
- mocDir.mkpath(mocDir.path());
- }
- static bool initialized = false;
- if (!initialized) {
- initialized = true;
- lazyInit();
- }
- if (verbose) {
- echoColor("Generating " + mocFilePath + " from " + sourceFile);
- } else {
- echoColor("Generating " + mocFileName);
- }
- QProcess mocProc;
- mocProc.setProcessChannelMode(QProcess::ForwardedChannels);
- QStringList args(mocIncludes + mocDefinitions);
- #ifdef Q_OS_WIN
- args << "-DWIN32";
- #endif
- args << QLatin1String("-o") << mocFilePath << sourceFile;
-
- if (verbose) {
- cout << mocExe << " " << args.join(QLatin1String(" ")) << endl;
- }
- mocProc.start(mocExe, args, QIODevice::NotOpen);
- if (mocProc.waitForStarted()) {
- const bool result = mocProc.waitForFinished(-1);
- if (!result || mocProc.exitCode()) {
- cerr << "automoc4: process for " << mocFilePath
- << " failed: " << mocProc.errorString() << endl;
- cerr << "pid to wait for: " << mocProc.pid() << endl;
- failed = true;
- QFile::remove(mocFilePath);
- }
- return true;
- } else {
- cerr << "automoc4: process for " << mocFilePath << "failed to start: "
- << mocProc.errorString() << endl;
- failed = true;
- }
- }
- return false;
- }
|