summaryrefslogtreecommitdiffstats
path: root/src/tools/moc/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/moc/main.cpp')
-rw-r--r--src/tools/moc/main.cpp228
1 files changed, 92 insertions, 136 deletions
diff --git a/src/tools/moc/main.cpp b/src/tools/moc/main.cpp
index 1cb383c92f..89fb367ca7 100644
--- a/src/tools/moc/main.cpp
+++ b/src/tools/moc/main.cpp
@@ -1,32 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include <depfile_shared.h>
#include "preprocessor.h"
#include "moc.h"
#include "outputrevision.h"
@@ -43,10 +19,13 @@
#include <qcoreapplication.h>
#include <qcommandlineoption.h>
#include <qcommandlineparser.h>
-#include <qscopedpointer.h>
+
+#include <memory>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
/*
This function looks at two file names and returns the name of the
infile with a path relative to outfile.
@@ -80,10 +59,21 @@ void error(const char *msg = "Invalid argument")
fprintf(stderr, "moc: %s\n", msg);
}
-struct ScopedPointerFileCloser
+static auto openFileForWriting(const QString &name)
{
- static inline void cleanup(FILE *handle) { if (handle) fclose(handle); }
-};
+ struct Closer { void operator()(FILE *handle) const { fclose(handle); } };
+ using R = std::unique_ptr<FILE, Closer>;
+
+#ifdef _MSC_VER
+ FILE *file;
+ if (_wfopen_s(&file, reinterpret_cast<const wchar_t *>(name.utf16()), L"w") != 0)
+ return R{};
+ return R{file};
+#else
+ return R{fopen(QFile::encodeName(name).constData(), "w")};
+#endif
+}
+using File = decltype(openFileForWriting({}));
static inline bool hasNext(const Symbols &symbols, int i)
{ return (i < symbols.size()); }
@@ -145,13 +135,14 @@ QByteArray composePreprocessorOutput(const Symbols &symbols) {
return output;
}
-static QStringList argumentsFromCommandLineAndFile(const QStringList &arguments)
+static QStringList argumentsFromCommandLineAndFile(const QStringList &arguments, bool &hasOptionFiles)
{
QStringList allArguments;
+ hasOptionFiles = false;
allArguments.reserve(arguments.size());
for (const QString &argument : arguments) {
// "@file" doesn't start with a '-' so we can't use QCommandLineParser for it
- if (argument.startsWith(QLatin1Char('@'))) {
+ if (argument.startsWith(u'@')) {
QString optionsFile = argument;
optionsFile.remove(0, 1);
if (optionsFile.isEmpty()) {
@@ -163,6 +154,7 @@ static QStringList argumentsFromCommandLineAndFile(const QStringList &arguments)
error("Cannot open options file specified with @");
return QStringList();
}
+ hasOptionFiles = true;
while (!f.atEnd()) {
QString line = QString::fromLocal8Bit(f.readLine().trimmed());
if (!line.isEmpty())
@@ -175,50 +167,6 @@ 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)
{
QCoreApplication app(argc, argv);
@@ -242,7 +190,7 @@ int runMoc(int argc, char **argv)
QString filename;
QString output;
QFile in;
- FILE *out = nullptr;
+ File out;
// Note that moc isn't translated.
// If you use this code as an example for a translated app, make sure to translate the strings.
@@ -347,6 +295,10 @@ int runMoc(int argc, char **argv)
jsonOption.setDescription(QStringLiteral("In addition to generating C++ code, create a machine-readable JSON file in a file that matches the output file and an extra .json extension."));
parser.addOption(jsonOption);
+ QCommandLineOption debugIncludesOption(QStringLiteral("debug-includes"));
+ debugIncludesOption.setDescription(QStringLiteral("Display debug messages of each considered include path."));
+ parser.addOption(debugIncludesOption);
+
QCommandLineOption collectOption(QStringLiteral("collect-json"));
collectOption.setDescription(QStringLiteral("Instead of processing C++ code, collect previously generated JSON output into a single file."));
parser.addOption(collectOption);
@@ -378,7 +330,8 @@ int runMoc(int argc, char **argv)
parser.addPositionalArgument(QStringLiteral("[MOC generated json file]"),
QStringLiteral("MOC generated json output"));
- const QStringList arguments = argumentsFromCommandLineAndFile(app.arguments());
+ bool hasOptionFiles = false;
+ const QStringList arguments = argumentsFromCommandLineAndFile(app.arguments(), hasOptionFiles);
if (arguments.isEmpty())
return 1;
@@ -387,10 +340,10 @@ int runMoc(int argc, char **argv)
const QStringList files = parser.positionalArguments();
output = parser.value(outputOption);
if (parser.isSet(collectOption))
- return collectJson(files, output);
+ return collectJson(files, output, hasOptionFiles);
- if (files.count() > 1) {
- error(qPrintable(QLatin1String("Too many input files specified: '") + files.join(QLatin1String("' '")) + QLatin1Char('\'')));
+ if (files.size() > 1) {
+ error(qPrintable("Too many input files specified: '"_L1 + files.join("' '"_L1) + u'\''));
parser.showHelp(1);
} else if (!files.isEmpty()) {
filename = files.first();
@@ -398,6 +351,7 @@ int runMoc(int argc, char **argv)
const bool ignoreConflictingOptions = parser.isSet(ignoreConflictsOption);
pp.preprocessOnly = parser.isSet(preprocessOption);
+ pp.setDebugIncludes(parser.isSet(debugIncludesOption));
if (parser.isSet(noIncludeOption)) {
moc.noInclude = true;
autoInclude = false;
@@ -425,7 +379,7 @@ int runMoc(int argc, char **argv)
for (const QString &path : includePaths)
pp.includes += Preprocessor::IncludePath(QFile::encodeName(path));
QString compilerFlavor = parser.value(compilerFlavorOption);
- if (compilerFlavor.isEmpty() || compilerFlavor == QLatin1String("unix")) {
+ if (compilerFlavor.isEmpty() || compilerFlavor == "unix"_L1) {
// traditional Unix compilers use both CPATH and CPLUS_INCLUDE_PATH
// $CPATH feeds to #include <...> and #include "...", whereas
// CPLUS_INCLUDE_PATH is equivalent to GCC's -isystem, so we parse later
@@ -435,14 +389,14 @@ int runMoc(int argc, char **argv)
const auto cplus_include_path = qgetenv("CPLUS_INCLUDE_PATH").split(QDir::listSeparator().toLatin1());
for (const QByteArray &p : cplus_include_path)
pp.includes += Preprocessor::IncludePath(p);
- } else if (compilerFlavor == QLatin1String("msvc")) {
+ } else if (compilerFlavor == "msvc"_L1) {
// MSVC uses one environment variable: INCLUDE
const auto include = qgetenv("INCLUDE").split(QDir::listSeparator().toLatin1());
for (const QByteArray &p : include)
pp.includes += Preprocessor::IncludePath(p);
} else {
- error(qPrintable(QLatin1String("Unknown compiler flavor '") + compilerFlavor +
- QLatin1String("'; valid values are: msvc, unix.")));
+ error(qPrintable("Unknown compiler flavor '"_L1 + compilerFlavor +
+ "'; valid values are: msvc, unix."_L1));
parser.showHelp(1);
}
@@ -457,7 +411,7 @@ int runMoc(int argc, char **argv)
for (const QString &arg : defines) {
QByteArray name = arg.toLocal8Bit();
QByteArray value("1");
- int eq = name.indexOf('=');
+ const qsizetype eq = name.indexOf('=');
if (eq >= 0) {
value = name.mid(eq + 1);
name = name.left(eq);
@@ -481,16 +435,16 @@ int runMoc(int argc, char **argv)
pp.macros.remove(macro);
}
const QStringList noNotesCompatValues = parser.values(noNotesWarningsCompatOption);
- if (parser.isSet(noNotesOption) || noNotesCompatValues.contains(QLatin1String("n")))
+ if (parser.isSet(noNotesOption) || noNotesCompatValues.contains("n"_L1))
moc.displayNotes = false;
- if (parser.isSet(noWarningsOption) || noNotesCompatValues.contains(QLatin1String("w")))
+ if (parser.isSet(noWarningsOption) || noNotesCompatValues.contains("w"_L1))
moc.displayWarnings = moc.displayNotes = false;
if (autoInclude) {
- int spos = filename.lastIndexOf(QDir::separator());
- int ppos = filename.lastIndexOf(QLatin1Char('.'));
+ qsizetype spos = filename.lastIndexOf(QDir::separator());
+ qsizetype ppos = filename.lastIndexOf(u'.');
// spos >= -1 && ppos > spos => ppos >= 0
- moc.noInclude = (ppos > spos && filename.at(ppos + 1).toLower() != QLatin1Char('h'));
+ moc.noInclude = (ppos > spos && filename.at(ppos + 1).toLower() != u'h');
}
if (defaultInclude) {
if (moc.includePath.isEmpty()) {
@@ -507,11 +461,14 @@ int runMoc(int argc, char **argv)
if (filename.isEmpty()) {
filename = QStringLiteral("standard input");
- in.open(stdin, QIODevice::ReadOnly);
+ if (!in.open(stdin, QIODevice::ReadOnly)) {
+ fprintf(stderr, "moc: cannot open standard input: %s\n", qPrintable(in.errorString()));
+ return 1;
+ }
} else {
in.setFileName(filename);
if (!in.open(QIODevice::ReadOnly)) {
- fprintf(stderr, "moc: %s: No such file\n", qPrintable(filename));
+ fprintf(stderr, "moc: cannot open %s: %s\n", qPrintable(filename), qPrintable(in.errorString()));
return 1;
}
moc.filename = filename.toLocal8Bit();
@@ -519,13 +476,13 @@ int runMoc(int argc, char **argv)
const auto metadata = parser.values(metadataOption);
for (const QString &md : metadata) {
- int split = md.indexOf(QLatin1Char('='));
+ qsizetype split = md.indexOf(u'=');
QString key = md.left(split);
QString value = md.mid(split + 1);
if (split == -1 || key.isEmpty() || value.isEmpty()) {
error("missing key or value for option '-M'");
- } else if (key.indexOf(QLatin1Char('.')) != -1) {
+ } else if (key.indexOf(u'.') != -1) {
// Don't allow keys with '.' for now, since we might need this
// format later for more advanced meta data API
error("A key cannot contain the letter '.' for option '-M'");
@@ -539,6 +496,17 @@ int runMoc(int argc, char **argv)
moc.currentFilenames.push(filename.toLocal8Bit());
moc.includes = pp.includes;
+ if (Q_UNLIKELY(parser.isSet(debugIncludesOption))) {
+ fprintf(stderr, "debug-includes: include search list:\n");
+
+ for (auto &includePath : pp.includes) {
+ fprintf(stderr, "debug-includes: '%s' framework: %d \n",
+ includePath.path.constData(),
+ includePath.isFrameworkPath);
+ }
+ fprintf(stderr, "debug-includes: end of search list.\n");
+ }
+
// 1. preprocess
const auto includeFiles = parser.values(includeOption);
QStringList validIncludesFiles;
@@ -572,51 +540,45 @@ int runMoc(int argc, char **argv)
// 3. and output meta object code
- QScopedPointer<FILE, ScopedPointerFileCloser> jsonOutput;
+ File 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)
-#else
- out = fopen(QFile::encodeName(output).constData(), "w"); // create output file
+ out = openFileForWriting(output);
if (!out)
-#endif
{
- fprintf(stderr, "moc: Cannot create %s\n", QFile::encodeName(output).constData());
+ const auto fopen_errno = errno;
+ fprintf(stderr, "moc: Cannot create %s. Error: %s\n",
+ QFile::encodeName(output).constData(),
+ strerror(fopen_errno));
return 1;
}
if (parser.isSet(jsonOption)) {
- const QString jsonOutputFileName = output + QLatin1String(".json");
- FILE *f;
-#if defined(_MSC_VER)
- if (_wfopen_s(&f, reinterpret_cast<const wchar_t *>(jsonOutputFileName.utf16()), L"w") != 0)
-#else
- f = fopen(QFile::encodeName(jsonOutputFileName).constData(), "w");
- if (!f)
-#endif
- fprintf(stderr, "moc: Cannot create JSON output file %s. %s\n",
+ const QString jsonOutputFileName = output + ".json"_L1;
+ jsonOutput = openFileForWriting(jsonOutputFileName);
+ if (!jsonOutput) {
+ const auto fopen_errno = errno;
+ fprintf(stderr, "moc: Cannot create JSON output file %s. Error: %s\n",
QFile::encodeName(jsonOutputFileName).constData(),
- strerror(errno));
- jsonOutput.reset(f);
+ strerror(fopen_errno));
+ }
}
} else { // use stdout
- out = stdout;
+ out.reset(stdout);
outputToFile = false;
}
if (pp.preprocessOnly) {
- fprintf(out, "%s\n", composePreprocessorOutput(moc.symbols).constData());
+ fprintf(out.get(), "%s\n", composePreprocessorOutput(moc.symbols).constData());
} else {
if (moc.classList.isEmpty())
moc.note("No relevant classes found. No output generated.");
else
- moc.generate(out, jsonOutput.data());
+ moc.generate(out.get(), jsonOutput.get());
}
- if (output.size())
- fclose(out);
+ out.reset();
if (parser.isSet(depFileOption)) {
// 4. write a Make-style dependency file (can also be consumed by Ninja).
@@ -629,28 +591,22 @@ int runMoc(int argc, char **argv)
if (parser.isSet(depFilePathOption)) {
depOutputFileName = parser.value(depFilePathOption);
} else if (outputToFile) {
- depOutputFileName = output + QLatin1String(".d");
+ depOutputFileName = output + ".d"_L1;
} 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",
+ File depFileHandle = openFileForWriting(depOutputFileName);
+ if (!depFileHandle) {
+ const auto fopen_errno = errno;
+ fprintf(stderr, "moc: Cannot create dep output file '%s'. Error: %s\n",
QFile::encodeName(depOutputFileName).constData(),
- strerror(errno));
- depFileHandle.reset(depFileHandleRaw);
+ strerror(fopen_errno));
+ }
- if (!depFileHandle.isNull()) {
+ if (depFileHandle) {
// First line is the path to the generated file.
- fprintf(depFileHandle.data(), "%s: ",
+ fprintf(depFileHandle.get(), "%s: ",
escapeAndEncodeDependencyPath(depRuleName).constData());
QByteArrayList dependencies;
@@ -682,7 +638,7 @@ int runMoc(int argc, char **argv)
// Join dependencies, output them, and output a final new line.
const auto dependenciesJoined = dependencies.join(QByteArrayLiteral(" \\\n "));
- fprintf(depFileHandle.data(), "%s\n", dependenciesJoined.constData());
+ fprintf(depFileHandle.get(), "%s\n", dependenciesJoined.constData());
}
}