moc: port to RAII handling of fopen()ed FILE*s

Similar to what we did for androiddeployqt, port moc's output file
handling from raw FILE* to unique_ptr<FILE> with an fclose() "deleter".

Introduce a openFileForWrite() helper function to abstract away the
difference between Unixoid and Windows FILE handling.

Keep the actual type of the unique_ptr instantiation local to
openFileForWrite() function so that creation and destruction are
defined close together, notwithstanding the occasional need to spell
out the type of the return value in callers.

NB: The moc copy in qtscxml does not contain this code.

Pick-to: 6.5
Change-Id: I0c9bca0bf3e29c137c02cc563098c5f2e2708cf3
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit 785a5e6eb4745dc3219cc33dc5ea2e671a40b5f3)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Marc Mutz 2024-05-14 16:16:08 +02:00 committed by Qt Cherry-pick Bot
parent f05cf3f11f
commit 72bf7a7679

View File

@ -19,7 +19,8 @@
#include <qcoreapplication.h> #include <qcoreapplication.h>
#include <qcommandlineoption.h> #include <qcommandlineoption.h>
#include <qcommandlineparser.h> #include <qcommandlineparser.h>
#include <qscopedpointer.h>
#include <memory>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -58,10 +59,21 @@ void error(const char *msg = "Invalid argument")
fprintf(stderr, "moc: %s\n", msg); 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) static inline bool hasNext(const Symbols &symbols, int i)
{ return (i < symbols.size()); } { return (i < symbols.size()); }
@ -178,7 +190,7 @@ int runMoc(int argc, char **argv)
QString filename; QString filename;
QString output; QString output;
QFile in; QFile in;
FILE *out = nullptr; File out;
// Note that moc isn't translated. // Note that moc isn't translated.
// If you use this code as an example for a translated app, make sure to translate the strings. // If you use this code as an example for a translated app, make sure to translate the strings.
@ -525,16 +537,12 @@ int runMoc(int argc, char **argv)
// 3. and output meta object code // 3. and output meta object code
QScopedPointer<FILE, ScopedPointerFileCloser> jsonOutput; File jsonOutput;
bool outputToFile = true; bool outputToFile = true;
if (output.size()) { // output file specified if (output.size()) { // output file specified
#if defined(_MSC_VER) out = openFileForWriting(output);
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
if (!out) if (!out)
#endif
{ {
const auto fopen_errno = errno; const auto fopen_errno = errno;
fprintf(stderr, "moc: Cannot create %s. Error: %s\n", fprintf(stderr, "moc: Cannot create %s. Error: %s\n",
@ -545,37 +553,29 @@ int runMoc(int argc, char **argv)
if (parser.isSet(jsonOption)) { if (parser.isSet(jsonOption)) {
const QString jsonOutputFileName = output + ".json"_L1; const QString jsonOutputFileName = output + ".json"_L1;
FILE *f; jsonOutput = openFileForWriting(jsonOutputFileName);
#if defined(_MSC_VER) if (!jsonOutput) {
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
{
const auto fopen_errno = errno; const auto fopen_errno = errno;
fprintf(stderr, "moc: Cannot create JSON output file %s. Error: %s\n", fprintf(stderr, "moc: Cannot create JSON output file %s. Error: %s\n",
QFile::encodeName(jsonOutputFileName).constData(), QFile::encodeName(jsonOutputFileName).constData(),
strerror(fopen_errno)); strerror(fopen_errno));
} }
jsonOutput.reset(f);
} }
} else { // use stdout } else { // use stdout
out = stdout; out.reset(stdout);
outputToFile = false; outputToFile = false;
} }
if (pp.preprocessOnly) { if (pp.preprocessOnly) {
fprintf(out, "%s\n", composePreprocessorOutput(moc.symbols).constData()); fprintf(out.get(), "%s\n", composePreprocessorOutput(moc.symbols).constData());
} else { } else {
if (moc.classList.isEmpty()) if (moc.classList.isEmpty())
moc.note("No relevant classes found. No output generated."); moc.note("No relevant classes found. No output generated.");
else else
moc.generate(out, jsonOutput.data()); moc.generate(out.get(), jsonOutput.get());
} }
if (output.size()) out.reset();
fclose(out);
if (parser.isSet(depFileOption)) { if (parser.isSet(depFileOption)) {
// 4. write a Make-style dependency file (can also be consumed by Ninja). // 4. write a Make-style dependency file (can also be consumed by Ninja).
@ -593,26 +593,17 @@ int runMoc(int argc, char **argv)
fprintf(stderr, "moc: Writing to stdout, but no depfile path specified.\n"); fprintf(stderr, "moc: Writing to stdout, but no depfile path specified.\n");
} }
QScopedPointer<FILE, ScopedPointerFileCloser> depFileHandle; File depFileHandle = openFileForWriting(depOutputFileName);
FILE *depFileHandleRaw; if (!depFileHandle) {
#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
{
const auto fopen_errno = errno; const auto fopen_errno = errno;
fprintf(stderr, "moc: Cannot create dep output file '%s'. Error: %s\n", fprintf(stderr, "moc: Cannot create dep output file '%s'. Error: %s\n",
QFile::encodeName(depOutputFileName).constData(), QFile::encodeName(depOutputFileName).constData(),
strerror(fopen_errno)); strerror(fopen_errno));
} }
depFileHandle.reset(depFileHandleRaw);
if (!depFileHandle.isNull()) { if (depFileHandle) {
// First line is the path to the generated file. // First line is the path to the generated file.
fprintf(depFileHandle.data(), "%s: ", fprintf(depFileHandle.get(), "%s: ",
escapeAndEncodeDependencyPath(depRuleName).constData()); escapeAndEncodeDependencyPath(depRuleName).constData());
QByteArrayList dependencies; QByteArrayList dependencies;
@ -644,7 +635,7 @@ int runMoc(int argc, char **argv)
// Join dependencies, output them, and output a final new line. // Join dependencies, output them, and output a final new line.
const auto dependenciesJoined = dependencies.join(QByteArrayLiteral(" \\\n ")); const auto dependenciesJoined = dependencies.join(QByteArrayLiteral(" \\\n "));
fprintf(depFileHandle.data(), "%s\n", dependenciesJoined.constData()); fprintf(depFileHandle.get(), "%s\n", dependenciesJoined.constData());
} }
} }