diff options
author | Liang Qi <liang.qi@qt.io> | 2020-01-04 15:32:34 +0100 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2020-01-04 15:32:34 +0100 |
commit | a503bf54705d89204007e8f887b668fe5ef519fc (patch) | |
tree | b2d1a452da39450eac78a1aeb1df116a73f64b23 /src/tools/moc/main.cpp | |
parent | e65c43fd0ffa57248e7d750570b7deae171a82f5 (diff) | |
parent | 1f87fb359fdff14e42662384a9c8a0bcb3837671 (diff) |
Merge remote-tracking branch 'origin/5.15' into dev
Conflicts:
src/corelib/tools/qvector.h
Make QVector(DataPointer dd) public to be able to properly merge
5b4b437b30b320e2cd7c9a566999a39772e5d431 from 5.15 into dev.
src/widgets/kernel/qapplication.cpp
tests/auto/tools/moc/allmocs_baseline_in.json
Done-With: Christian Ehrlicher <ch.ehrlicher@gmx.de>
Change-Id: I929ba7c036d570382d0454c2c75f6f0d96ddbc01
Diffstat (limited to 'src/tools/moc/main.cpp')
-rw-r--r-- | src/tools/moc/main.cpp | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/src/tools/moc/main.cpp b/src/tools/moc/main.cpp index 4aa040a9bb..b8c2d7f594 100644 --- a/src/tools/moc/main.cpp +++ b/src/tools/moc/main.cpp @@ -175,6 +175,49 @@ static QStringList argumentsFromCommandLineAndFile(const QStringList &arguments) return allArguments; } +// Escape characters in given path. Dependency paths are Make-style, not NMake/Jom style. +// The paths can also be consumed by Ninja. +// "$" replaced by "$$" +// "#" replaced by "\#" +// " " replaced by "\ " +// "\#" replaced by "\\#" +// "\ " replaced by "\\\ " +// +// The escape rules are according to what clang / llvm escapes when generating a Make-style +// dependency file. +// Is a template function, because input param can be either a QString or a QByteArray. +template <typename T> struct CharType; +template <> struct CharType<QString> { using type = QLatin1Char; }; +template <> struct CharType<QByteArray> { using type = char; }; +template <typename StringType> +StringType escapeDependencyPath(const StringType &path) +{ + using CT = typename CharType<StringType>::type; + StringType escapedPath; + int size = path.size(); + escapedPath.reserve(size); + for (int i = 0; i < size; ++i) { + if (path[i] == CT('$')) { + escapedPath.append(CT('$')); + } else if (path[i] == CT('#')) { + escapedPath.append(CT('\\')); + } else if (path[i] == CT(' ')) { + escapedPath.append(CT('\\')); + int backwards_it = i - 1; + while (backwards_it > 0 && path[backwards_it] == CT('\\')) { + escapedPath.append(CT('\\')); + --backwards_it; + } + } + escapedPath.append(path[i]); + } + return escapedPath; +} + +QByteArray escapeAndEncodeDependencyPath(const QString &path) +{ + return QFile::encodeName(escapeDependencyPath(path)); +} int runMoc(int argc, char **argv) { @@ -308,6 +351,22 @@ int runMoc(int argc, char **argv) collectOption.setDescription(QStringLiteral("Instead of processing C++ code, collect previously generated JSON output into a single file.")); parser.addOption(collectOption); + QCommandLineOption depFileOption(QStringLiteral("output-dep-file")); + depFileOption.setDescription( + QStringLiteral("Output a Make-style dep file for build system consumption.")); + parser.addOption(depFileOption); + + QCommandLineOption depFilePathOption(QStringLiteral("dep-file-path")); + depFilePathOption.setDescription(QStringLiteral("Path where to write the dep file.")); + depFilePathOption.setValueName(QStringLiteral("file")); + parser.addOption(depFilePathOption); + + QCommandLineOption depFileRuleNameOption(QStringLiteral("dep-file-rule-name")); + depFileRuleNameOption.setDescription( + QStringLiteral("The rule name (first line) of the dep file.")); + depFileRuleNameOption.setValueName(QStringLiteral("rule name")); + parser.addOption(depFileRuleNameOption); + parser.addPositionalArgument(QStringLiteral("[header-file]"), QStringLiteral("Header file to read from, otherwise stdin.")); parser.addPositionalArgument(QStringLiteral("[@option-file]"), @@ -476,6 +535,7 @@ int runMoc(int argc, char **argv) // 1. preprocess const auto includeFiles = parser.values(includeOption); + QStringList validIncludesFiles; for (const QString &includeName : includeFiles) { QByteArray rawName = pp.resolveInclude(QFile::encodeName(includeName), moc.filename); if (rawName.isEmpty()) { @@ -488,6 +548,7 @@ int runMoc(int argc, char **argv) moc.symbols += Symbol(0, MOC_INCLUDE_BEGIN, rawName); moc.symbols += pp.preprocessed(rawName, &f); moc.symbols += Symbol(0, MOC_INCLUDE_END, rawName); + validIncludesFiles.append(includeName); } else { fprintf(stderr, "Warning: Cannot open %s included by moc file %s: %s\n", rawName.constData(), @@ -507,6 +568,7 @@ int runMoc(int argc, char **argv) QScopedPointer<FILE, ScopedPointerFileCloser> jsonOutput; + bool outputToFile = true; if (output.size()) { // output file specified #if defined(_MSC_VER) if (_wfopen_s(&out, reinterpret_cast<const wchar_t *>(output.utf16()), L"w") != 0) @@ -535,6 +597,7 @@ int runMoc(int argc, char **argv) } } else { // use stdout out = stdout; + outputToFile = false; } if (pp.preprocessOnly) { @@ -549,6 +612,74 @@ int runMoc(int argc, char **argv) if (output.size()) fclose(out); + if (parser.isSet(depFileOption)) { + // 4. write a Make-style dependency file (can also be consumed by Ninja). + QString depOutputFileName; + QString depRuleName = output; + + if (parser.isSet(depFileRuleNameOption)) + depRuleName = parser.value(depFileRuleNameOption); + + if (parser.isSet(depFilePathOption)) { + depOutputFileName = parser.value(depFilePathOption); + } else if (outputToFile) { + depOutputFileName = output + QLatin1String(".d"); + } else { + fprintf(stderr, "moc: Writing to stdout, but no depfile path specified.\n"); + } + + QScopedPointer<FILE, ScopedPointerFileCloser> depFileHandle; + FILE *depFileHandleRaw; +#if defined(_MSC_VER) + if (_wfopen_s(&depFileHandleRaw, + reinterpret_cast<const wchar_t *>(depOutputFileName.utf16()), L"w") != 0) +#else + depFileHandleRaw = fopen(QFile::encodeName(depOutputFileName).constData(), "w"); + if (!depFileHandleRaw) +#endif + fprintf(stderr, "moc: Cannot create dep output file '%s'. %s\n", + QFile::encodeName(depOutputFileName).constData(), + strerror(errno)); + depFileHandle.reset(depFileHandleRaw); + + if (!depFileHandle.isNull()) { + // First line is the path to the generated file. + fprintf(depFileHandle.data(), "%s: ", + escapeAndEncodeDependencyPath(depRuleName).constData()); + + QByteArrayList dependencies; + + // If there's an input file, it's the first dependency. + if (!filename.isEmpty()) { + dependencies.append(escapeAndEncodeDependencyPath(filename).constData()); + } + + // Additional passed-in includes are dependencies (like moc_predefs.h). + for (const QString &includeName : validIncludesFiles) { + dependencies.append(escapeAndEncodeDependencyPath(includeName).constData()); + } + + // Plugin metadata json files discovered via Q_PLUGIN_METADATA macros are also + // dependencies. + for (const QString &pluginMetadataFile : moc.parsedPluginMetadataFiles) { + dependencies.append(escapeAndEncodeDependencyPath(pluginMetadataFile).constData()); + } + + // All pre-processed includes are dependnecies. + // Sort the entries for easier human consumption. + auto includeList = pp.preprocessedIncludes.values(); + std::sort(includeList.begin(), includeList.end()); + + for (QByteArray &includeName : includeList) { + dependencies.append(escapeDependencyPath(includeName)); + } + + // Join dependencies, output them, and output a final new line. + const auto dependenciesJoined = dependencies.join(QByteArrayLiteral(" \\\n ")); + fprintf(depFileHandle.data(), "%s\n", dependenciesJoined.constData()); + } + } + return 0; } |