Rework QQmlJSResourceFileMapper

We need more generic filtering capabilities so that it can, for example,
retrieve all the .qml files in a directory, both as qrc paths and local
file paths.

Change-Id: I72a72abc6dd39adb41bcd035f7aa6777e50cb5a5
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
This commit is contained in:
Ulf Hermann 2021-02-22 16:00:56 +01:00
parent c603d30887
commit 74bf3c8c33
4 changed files with 178 additions and 32 deletions

View File

@ -34,6 +34,42 @@
QT_BEGIN_NAMESPACE
QQmlJSResourceFileMapper::Filter QQmlJSResourceFileMapper::allQmlJSFilter() {
return Filter {
QString(),
QStringList { QStringLiteral("qml"), QStringLiteral("js"), QStringLiteral("mjs") },
Directory | Recurse
};
}
QQmlJSResourceFileMapper::Filter QQmlJSResourceFileMapper::localFileFilter(const QString &file)
{
return Filter {
QFileInfo(file).canonicalFilePath(),
QStringList(),
File
};
}
QQmlJSResourceFileMapper::Filter QQmlJSResourceFileMapper::resourceFileFilter(const QString &file)
{
return Filter {
file,
QStringList(),
Resource
};
}
QQmlJSResourceFileMapper::Filter QQmlJSResourceFileMapper::resourceQmlDirectoryFilter(
const QString &directory)
{
return Filter {
directory,
QStringList { QStringLiteral("qml") },
Directory | Resource
};
}
QQmlJSResourceFileMapper::QQmlJSResourceFileMapper(const QStringList &resourceFiles)
{
for (const QString &fileName: resourceFiles) {
@ -49,32 +85,111 @@ bool QQmlJSResourceFileMapper::isEmpty() const
return qrcPathToFileSystemPath.isEmpty();
}
QStringList QQmlJSResourceFileMapper::resourcePaths(const QString &fileName)
bool QQmlJSResourceFileMapper::isFile(const QString &resourcePath) const
{
const QString absPath = QDir::cleanPath(QDir::current().absoluteFilePath(fileName));
QStringList resourcePaths;
for (auto it = qrcPathToFileSystemPath.cbegin(), end = qrcPathToFileSystemPath.cend(); it != end; ++it) {
if (QFileInfo(it.value()) == QFileInfo(absPath))
resourcePaths.append(it.key());
for (const auto &entry : qrcPathToFileSystemPath) {
if (entry.resourcePath == resourcePath)
return true;
}
return resourcePaths;
return false;
}
QStringList QQmlJSResourceFileMapper::qmlCompilerFiles(FileOutput fo) const
static bool hasSuffix(const QString &qrcPath, const QStringList &suffixes)
{
QStringList files;
for (auto it = qrcPathToFileSystemPath.constBegin(), end = qrcPathToFileSystemPath.constEnd();
it != end; ++it) {
const QString &qrcPath = it.key();
const QString suffix = QFileInfo(qrcPath).suffix();
if (suffix != QStringLiteral("qml") && suffix != QStringLiteral("js") && suffix != QStringLiteral("mjs"))
continue;
if (fo == FileOutput::AbsoluteFilePath)
files << it.value();
else
files << qrcPath;
if (suffixes.isEmpty())
return true;
const QString suffix = QFileInfo(qrcPath).suffix();
return suffixes.contains(suffix);
}
template<typename HandleMatch>
void doFilter(const QList<QQmlJSResourceFileMapper::Entry> &qrcPathToFileSystemPath,
const QQmlJSResourceFileMapper::Filter &filter,
const HandleMatch &handler)
{
if (filter.flags & QQmlJSResourceFileMapper::Directory) {
const QString terminatedDirectory = filter.path.endsWith(u'/')
? filter.path : (filter.path + u'/');
for (auto it = qrcPathToFileSystemPath.constBegin(),
end = qrcPathToFileSystemPath.constEnd(); it != end; ++it) {
const QString candidate = (filter.flags & QQmlJSResourceFileMapper::Resource)
? it->resourcePath
: it->filePath;
if (!candidate.startsWith(terminatedDirectory))
continue;
if (!hasSuffix(candidate, filter.suffixes))
continue;
if ((filter.flags & QQmlJSResourceFileMapper::Recurse)
// Crude. But shall we really allow slashes in QRC file names?
|| !candidate.mid(terminatedDirectory.length()).contains(u'/')) {
if (handler(*it))
return;
}
}
return;
}
return files;
if (!hasSuffix(filter.path, filter.suffixes))
return;
for (auto it = qrcPathToFileSystemPath.constBegin(),
end = qrcPathToFileSystemPath.constEnd(); it != end; ++it) {
if (filter.flags & QQmlJSResourceFileMapper::Resource) {
if (it->resourcePath == filter.path && handler(*it))
return;
} else if (it->filePath == filter.path && handler(*it)) {
return;
}
}
}
QList<QQmlJSResourceFileMapper::Entry> QQmlJSResourceFileMapper::filter(
const QQmlJSResourceFileMapper::Filter &filter) const
{
QList<Entry> result;
doFilter(qrcPathToFileSystemPath, filter, [&](const Entry &entry) {
result.append(entry);
return false;
});
return result;
}
QStringList QQmlJSResourceFileMapper::filePaths(
const QQmlJSResourceFileMapper::Filter &filter) const
{
QStringList result;
doFilter(qrcPathToFileSystemPath, filter, [&](const Entry &entry) {
result.append(entry.filePath);
return false;
});
return result;
}
QStringList QQmlJSResourceFileMapper::resourcePaths(
const QQmlJSResourceFileMapper::Filter &filter) const
{
QStringList result;
doFilter(qrcPathToFileSystemPath, filter, [&](const Entry &entry) {
result.append(entry.resourcePath);
return false;
});
return result;
}
QQmlJSResourceFileMapper::Entry QQmlJSResourceFileMapper::entry(
const QQmlJSResourceFileMapper::Filter &filter) const
{
Entry result;
doFilter(qrcPathToFileSystemPath, filter, [&](const Entry &entry) {
result = entry;
return true;
});
return result;
}
void QQmlJSResourceFileMapper::populateFromQrcFile(QFile &file)
@ -162,7 +277,7 @@ void QQmlJSResourceFileMapper::populateFromQrcFile(QFile &file)
const QString qrcPath = prefix + currentFileName;
if (QFile::exists(fsPath))
qrcPathToFileSystemPath.insert(qrcPath, fsPath);
qrcPathToFileSystemPath.append({qrcPath, fsPath});
continue;
}

View File

@ -46,23 +46,50 @@ QT_BEGIN_NAMESPACE
struct QQmlJSResourceFileMapper
{
enum class FileOutput {
RelativeFilePath,
AbsoluteFilePath
struct Entry
{
QString resourcePath;
QString filePath;
bool isValid() const { return !resourcePath.isEmpty() && !filePath.isEmpty(); }
};
enum FilterMode {
File = 0x0, // Default is local (non-directory) file, without recursion
Directory = 0x1, // Directory, either local or resource
Resource = 0x2, // Resource path, either to file or directory
Recurse = 0x4, // Recurse into subdirectories if Directory
};
Q_DECLARE_FLAGS(FilterFlags, FilterMode);
struct Filter {
QString path;
QStringList suffixes;
FilterFlags flags;
};
static Filter allQmlJSFilter();
static Filter localFileFilter(const QString &file);
static Filter resourceFileFilter(const QString &file);
static Filter resourceQmlDirectoryFilter(const QString &directory);
QQmlJSResourceFileMapper(const QStringList &resourceFiles);
bool isEmpty() const;
bool isFile(const QString &resourcePath) const;
QStringList resourcePaths(const QString &fileName);
QStringList qmlCompilerFiles(FileOutput fo = FileOutput::RelativeFilePath) const;
QList<Entry> filter(const Filter &filter) const;
QStringList filePaths(const Filter &filter) const;
QStringList resourcePaths(const Filter &filter) const;
Entry entry(const Filter &filter) const;
private:
void populateFromQrcFile(QFile &file);
QHash<QString, QString> qrcPathToFileSystemPath;
QList<Entry> qrcPathToFileSystemPath;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlJSResourceFileMapper::FilterFlags);
QT_END_NAMESPACE
#endif // QMLJSRESOURCEFILEMAPPER_P_H

View File

@ -167,8 +167,9 @@ int main(int argc, char **argv)
QQmlJSResourceFileMapper mapper(sources);
QQmlJSCompileError error;
if (!qQmlJSGenerateLoader(mapper.qmlCompilerFiles(), outputFileName,
parser.values(resourceFileMappingOption), &error.message)) {
if (!qQmlJSGenerateLoader(
mapper.resourcePaths(QQmlJSResourceFileMapper::allQmlJSFilter()),
outputFileName, parser.values(resourceFileMappingOption), &error.message)) {
error.augment(QLatin1String("Error generating loader stub: ")).print();
return EXIT_FAILURE;
}
@ -194,7 +195,8 @@ int main(int argc, char **argv)
// If the user didn't specify the resource path corresponding to the file on disk being
// compiled, try to determine it from the resource file, if one was supplied.
if (inputResourcePath.isEmpty()) {
const QStringList resourcePaths = fileMapper.resourcePaths(inputFile);
const QStringList resourcePaths = fileMapper.resourcePaths(
QQmlJSResourceFileMapper::localFileFilter(inputFile));
if (resourcePaths.isEmpty()) {
fprintf(stderr, "No resource path for file: %s\n", qPrintable(inputFile));
return EXIT_FAILURE;

View File

@ -631,8 +631,10 @@ int main(int argc, char *argv[])
}
}
if (!qrcFiles.isEmpty())
scanFiles << QQmlJSResourceFileMapper(qrcFiles).qmlCompilerFiles(QQmlJSResourceFileMapper::FileOutput::AbsoluteFilePath);
if (!qrcFiles.isEmpty()) {
scanFiles << QQmlJSResourceFileMapper(qrcFiles).filePaths(
QQmlJSResourceFileMapper::allQmlJSFilter());
}
g_qmlImportPaths = qmlImportPaths;