Files
RedBear-OS/local/recipes/qt/qtdeclarative/source/tools/qmldom/qmldomtool.cpp
T
vasilito f31522130f fix: comprehensive boot warnings and exceptions — fixable silenced, unfixable diagnosed
Build system (5 gaps hardened):
- COOKBOOK_OFFLINE defaults to true (fork-mode)
- normalize_patch handles diff -ruN format
- New 'repo validate-patches' command (25/25 relibc patches)
- 14 patched Qt/Wayland/display recipes added to protected list
- relibc archive regenerated with current patch chain

Boot fixes (fixable):
- Full ISO EFI partition: 16 MiB → 1 MiB (matches mini, BIOS hardcoded 2 MiB offset)
- D-Bus system bus: absolute /usr/bin/dbus-daemon path (was skipped)
- redbear-sessiond: absolute /usr/bin/redbear-sessiond path (was skipped)
- daemon framework: silenced spurious INIT_NOTIFY warnings for oneshot_async services (P0-daemon-silence-init-notify.patch)
- udev-shim: demoted INIT_NOTIFY warning to INFO (expected for oneshot_async)
- relibc: comprehensive named semaphores (sem_open/close/unlink) replacing upstream todo!() stubs
- greeterd: Wayland socket timeout 15s → 30s (compositor DRM wait)
- greeter-ui: built and linked (header guard unification, sem_compat stubs removed)
- mc: un-ignored in both configs, fixed glib/libiconv/pcre2 transitive deps
- greeter config: removed stale keymapd dependency from display/greeter services
- prefix toolchain: relibc headers synced, _RELIBC_STDLIB_H guard unified

Unfixable (diagnosed, upstream):
- i2c-hidd: abort on no-I2C-hardware (QEMU) — process::exit → relibc abort
- kded6/greeter-ui: page fault 0x8 — Qt library null deref
- Thread panics fd != -1 — Rust std library on Redox
- DHCP timeout / eth0 MAC — QEMU user-mode networking
- hwrngd/thermald — no hardware RNG/thermal in VM
- live preload allocation — BIOS memory fragmentation, continues on demand
2026-05-05 20:20:37 +01:00

325 lines
12 KiB
C++

// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/qdebug.h>
#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdir.h>
#include <QtCore/QTextStream>
#include <QtCore/QThread>
#include <QtQmlDom/private/qqmldomtop_p.h>
#include <QtQmlDom/private/qqmldomfilewriter_p.h>
#include <QtQmlDom/private/qqmldomoutwriter_p.h>
#include <QtQmlDom/private/qqmldomelements_p.h>
#include <QtQmlDom/private/qqmldomfieldfilter_p.h>
#include <QtQmlDom/private/qqmldomastdumper_p.h>
#include <cstdio>
#include <optional>
#if QT_CONFIG(commandlineparser)
# include <QtCore/qcommandlineparser.h>
#endif
#include <QtCore/qlibraryinfo.h>
using namespace QQmlJS::Dom;
namespace tt {
Q_NAMESPACE
enum class Dependencies { None, Required };
Q_ENUM_NS(Dependencies);
};
using namespace tt;
int main(int argc, char *argv[])
{
FieldFilter filter = FieldFilter::defaultFilter();
QCoreApplication a(argc, argv);
QCoreApplication::setApplicationName("qmldom");
QCoreApplication::setApplicationVersion("1.0");
#if QT_CONFIG(commandlineparser)
QCommandLineParser parser;
parser.setApplicationDescription(QLatin1String("QML dom tool"));
parser.addHelpOption();
parser.addVersionOption();
QCommandLineOption dumpOption(QStringList() << "d"
<< "dump",
QLatin1String("Dumps the code model"));
parser.addOption(dumpOption);
QCommandLineOption reformatOption(QStringList() << "r"
<< "reformat",
QLatin1String("reformats the files explicitly passed in"));
parser.addOption(reformatOption);
QCommandLineOption filterOption(
QStringList() << "f"
<< "filter-fields",
QLatin1String("commas separated list of fields to filter out. Prepending a field with "
"'-' skips the field, with '+' it adds it. The field might be prepended "
"by '<type>:' to apply only to elements of that type"
"The default filters are ")
+ filter.describeFieldsFilter(),
QLatin1String("fields"));
parser.addOption(filterOption);
QCommandLineOption qmltypesDirsOption(
QStringList() << "I"
<< "qmldirs",
QLatin1String("Look for qmltypes files in specified directory"),
QLatin1String("directory"));
parser.addOption(qmltypesDirsOption);
QCommandLineOption qmltypesFilesOption(QStringList() << "i"
<< "qmltypes",
QLatin1String("Include the specified qmltypes files"),
QLatin1String("qmltypes"));
parser.addOption(qmltypesFilesOption);
QCommandLineOption pathToDumpOption(
QStringList() << "path-to-dump",
QLatin1String("adds a path to dump. By default the base path of each file is dumped. "
"If any path starts with $ ($env for example) then the environment (and "
"not the loaded files) is used as basis."),
QLatin1String("pathToDump"));
parser.addOption(pathToDumpOption);
QCommandLineOption dependenciesOption(
QStringList() << "D"
<< "dependencies",
QLatin1String("Dependencies to load: none, required, reachable"),
QLatin1String("dependenciesToLoad"), QLatin1String("none"));
parser.addOption(dependenciesOption);
QCommandLineOption reformatDirOption(
QStringList() << "reformat-dir",
QLatin1String(
"Target directory for the reformatted files, "
"if not given the files are reformatted in place (but backup files are kept)"),
QLatin1String("reformatDir"));
parser.addOption(reformatDirOption);
QCommandLineOption nBackupsOption(
QStringList() << "backups",
QLatin1String("Number of backup files to generate (default is 2, the oldest, "
"and the last version are kept), "),
QLatin1String("nBackups"));
parser.addOption(nBackupsOption);
QCommandLineOption dumpAstOption(QStringList() << "dump-ast",
QLatin1String("Dumps the AST of the given QML file."));
parser.addOption(dumpAstOption);
parser.addPositionalArgument(QLatin1String("files"),
QLatin1String("list of qml or js files to verify"));
parser.process(a);
const auto positionalArguments = parser.positionalArguments();
if (positionalArguments.isEmpty()) {
parser.showHelp(-1);
}
if (parser.isSet(filterOption)) {
qDebug() << "filters: " << parser.values(filterOption);
for (const QString &fFields : parser.values(filterOption)) {
if (!filter.addFilter(fFields)) {
return 1;
}
}
filter.setFiltred();
}
std::optional<DomType> fileType;
if (parser.isSet(reformatOption))
fileType = DomType::QmlFile;
Dependencies dep = Dependencies::None;
for (const QString &depName : parser.values(dependenciesOption)) {
QMetaEnum metaEnum = QMetaEnum::fromType<Dependencies>();
bool found = false;
for (int i = 0; i < metaEnum.keyCount(); ++i) {
if (QLatin1String(metaEnum.key(i)).compare(depName, Qt::CaseInsensitive) == 0) {
found = true;
dep = Dependencies(metaEnum.value(i));
}
}
if (!found) {
QStringList values;
for (int i = 0; i < metaEnum.keyCount(); ++i)
values.append(QString::fromUtf8(metaEnum.key(i)).toLower());
qDebug().noquote() << "Invalid dependencies argument, expected one of "
<< values.join(QLatin1Char(','));
return 1;
}
}
int nBackups = 2;
if (parser.isSet(nBackupsOption)) {
bool intOk;
nBackups = parser.value(nBackupsOption).toInt(&intOk);
if (!intOk) {
qDebug() << "expected an integer giving the number of backups after --backups, not "
<< parser.value(nBackupsOption);
}
}
QList<Path> pathsToDump;
for (const QString &pStr : parser.values(pathToDumpOption)) {
pathsToDump.append(Path::fromString(pStr));
}
if (pathsToDump.isEmpty())
pathsToDump.append(Path());
// use host qml import path as a sane default if nothing else has been provided
QStringList qmltypeDirs = parser.isSet(qmltypesDirsOption)
? parser.values(qmltypesDirsOption)
: QStringList { QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) };
if (!parser.isSet(qmltypesFilesOption))
qmltypeDirs << ".";
QStringList qmltypeFiles =
parser.isSet(qmltypesFilesOption) ? parser.values(qmltypesFilesOption) : QStringList {};
#else
QStringList qmltypeDirs {};
QStringList qmltypeFiles {};
#endif
{
QDebug dbg = qDebug();
dbg << "dirs:\n";
for (const QString &d : std::as_const(qmltypeDirs))
dbg << " '" << d << "'\n";
dbg << "files:\n";
for (const QString &f : std::as_const(positionalArguments))
dbg << " '" << f << "'\n";
dbg << "fieldFilter: " << filter.describeFieldsFilter();
dbg << "\n";
}
DomEnvironment::Options options = DomEnvironment::Option::SingleThreaded;
if (dep == Dependencies::None)
options = options | DomEnvironment::Option::NoDependencies;
std::shared_ptr<DomEnvironment> envPtr(new DomEnvironment(qmltypeDirs, options));
DomItem env(envPtr);
qDebug() << "will load\n";
if (dep != Dependencies::None)
envPtr->loadBuiltins();
QList<DomItem> loadedFiles(positionalArguments.size());
qsizetype iPos = 0;
for (const QString &s : std::as_const(positionalArguments)) {
envPtr->loadFile(
FileToLoad::fromFileSystem(envPtr, s),
[&loadedFiles, iPos](Path, const DomItem &, const DomItem &newIt) {
loadedFiles[iPos] = newIt;
},
fileType);
}
envPtr->loadPendingDependencies();
bool hadFailures = false;
const qsizetype largestFileSizeToCheck = 32000;
if (parser.isSet(reformatOption)) {
for (auto &qmlFile : loadedFiles) {
QString qmlFilePath = qmlFile.canonicalFilePath();
if (qmlFile.internalKind() != DomType::QmlFile) {
qWarning() << "cannot reformat" << qmlFile.internalKindStr() << "(" << qmlFilePath
<< ")";
continue;
}
qDebug() << "reformatting" << qmlFilePath;
FileWriter fw;
LineWriterOptions lwOptions;
WriteOutChecks checks = WriteOutCheck::Default;
if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>())
if (qmlFilePtr->code().size() > largestFileSizeToCheck)
checks = WriteOutCheck::None;
QString target = qmlFilePath;
QString rDir = parser.value(reformatDirOption);
if (!rDir.isEmpty()) {
QFileInfo f(qmlFilePath);
QDir d(rDir);
target = d.filePath(f.fileName());
}
auto res = qmlFile.writeOut(target, nBackups, lwOptions, &fw, checks);
switch (fw.status) {
case FileWriter::Status::ShouldWrite:
case FileWriter::Status::SkippedDueToFailure:
qWarning() << "failure reformatting " << qmlFilePath;
break;
case FileWriter::Status::DidWrite:
qDebug() << "success";
break;
case FileWriter::Status::SkippedEqual:
qDebug() << "no change";
}
hadFailures = hadFailures || !res;
}
} else if (parser.isSet(dumpAstOption)) {
if (pathsToDump.size() > 1) {
qWarning() << "--dump-ast can only be used with a single file";
return 1;
}
for (auto &fileItem : loadedFiles) {
const auto file = fileItem.fileObject().ownerAs<QmlFile>();
if (!file) {
qWarning() << "cannot dump AST for" << fileItem.canonicalPath();
qWarning() << "is it a valid QML file?";
continue;
}
const QString ast =
QQmlJS::Dom::astNodeDump(file->ast(), AstDumperOption::DumpNode, 1, 0);
QTextStream ts(stdout);
ts << ast << Qt::flush;
}
} else if (parser.isSet(dumpOption) || !parser.isSet(reformatOption)
|| !parser.isSet(dumpAstOption)) {
qDebug() << "will dump\n";
QTextStream ts(stdout);
auto sink = [&ts](QStringView v) {
ts << v; /* ts.flush(); */
};
qsizetype iPathToDump = 0;
bool globalPaths = false;
for (const auto &p : pathsToDump)
if (p.headKind() == Path::Kind::Root)
globalPaths = true;
if (globalPaths)
loadedFiles = QList<DomItem>({ env });
bool dumpDict = pathsToDump.size() > 1 || loadedFiles.size() > 1;
if (dumpDict)
sink(u"{\n");
while (iPathToDump < pathsToDump.size()) {
for (auto &fileItem : loadedFiles) {
Path p = pathsToDump.at(iPathToDump++ % pathsToDump.size());
if (dumpDict) {
if (iPathToDump > 1)
sink(u",\n");
sink(u"\"");
if (fileItem.internalKind() != DomType::DomEnvironment) {
sinkEscaped(sink, fileItem.canonicalFilePath(),
EscapeOptions::NoOuterQuotes);
sink(u"/");
}
sinkEscaped(sink, p.toString(), EscapeOptions::NoOuterQuotes);
sink(u"\":\n");
}
fileItem.path(p).dump(sink, 0, filter);
}
}
if (dumpDict)
sink(u"}\n");
Qt::endl(ts).flush();
}
for (int i = 0; i < 100; ++i)
QThread::yieldCurrentThread(); // let buggy integrations catch up with the output
// return a.exec();
return 0;
}
#include "qmldomtool.moc"