CMake: Always add the qmldir to the resource file system
We want to be able to load modules directly from the resource file system, no matter if we build statically or dynamically. It's limited by the need to load plugins, of course, but as most plugins are optional an application can easily go this way and never include any plain QML files in an application bundle. Pick-to: 6.2 Change-Id: I94657a43b47e442722a83e1fb306680aa50b1bc3 Reviewed-by: Maximilian Goldstein <max.goldstein@qt.io>
This commit is contained in:
parent
37540467ca
commit
79d0517a12
|
@ -452,33 +452,30 @@ function(qt6_add_qml_module target)
|
|||
if(NOT arg_NO_GENERATE_QMLDIR)
|
||||
_qt_internal_target_generate_qmldir(${target} ${arg_OUTPUT_DIRECTORY} ${arg_TARGET_PATH})
|
||||
|
||||
get_target_property(target_type ${target} TYPE)
|
||||
if(target_type STREQUAL "STATIC_LIBRARY")
|
||||
# Embed qmldir in static builds. The following comments relate mostly to Qt5->6 transition.
|
||||
# The requirement to keep the same resource name might no longer apply, but it doesn't
|
||||
# currently appear to cause any hinderance to keep it.
|
||||
# The qmldir resource name needs to match the one generated by qmake's qml_module.prf, to
|
||||
# ensure that all Q_INIT_RESOURCE(resource_name) calls in Qt code don't lead to undefined
|
||||
# symbol errors when linking an application project.
|
||||
# The Q_INIT_RESOURCE() calls are not strictly necessary anymore because the CMake Qt
|
||||
# build passes around the compiled resources as object files.
|
||||
# These object files have global initiliazers that don't get discared when linked into
|
||||
# an application (as opposed to when the resource libraries were embedded into the static
|
||||
# libraries when Qt was built with qmake).
|
||||
# The reason to match the naming is to ensure that applications link successfully regardless
|
||||
# if Qt was built with CMake or qmake, while the build system transition phase is still
|
||||
# happening.
|
||||
string(REPLACE "/" "_" qmldir_resource_name "qmake_${arg_TARGET_PATH}")
|
||||
set_source_files_properties(${arg_OUTPUT_DIRECTORY}/qmldir
|
||||
PROPERTIES QT_RESOURCE_ALIAS "qmldir"
|
||||
)
|
||||
set(resource_targets)
|
||||
qt6_add_resources(${target} ${qmldir_resource_name}
|
||||
FILES ${arg_OUTPUT_DIRECTORY}/qmldir
|
||||
OUTPUT_TARGETS resource_targets
|
||||
)
|
||||
list(APPEND output_targets ${resource_targets})
|
||||
endif()
|
||||
# Embed qmldir in qrc. The following comments relate mostly to Qt5->6 transition.
|
||||
# The requirement to keep the same resource name might no longer apply, but it doesn't
|
||||
# currently appear to cause any hinderance to keep it.
|
||||
# The qmldir resource name needs to match the one generated by qmake's qml_module.prf, to
|
||||
# ensure that all Q_INIT_RESOURCE(resource_name) calls in Qt code don't lead to undefined
|
||||
# symbol errors when linking an application project.
|
||||
# The Q_INIT_RESOURCE() calls are not strictly necessary anymore because the CMake Qt
|
||||
# build passes around the compiled resources as object files.
|
||||
# These object files have global initiliazers that don't get discared when linked into
|
||||
# an application (as opposed to when the resource libraries were embedded into the static
|
||||
# libraries when Qt was built with qmake).
|
||||
# The reason to match the naming is to ensure that applications link successfully regardless
|
||||
# if Qt was built with CMake or qmake, while the build system transition phase is still
|
||||
# happening.
|
||||
string(REPLACE "/" "_" qmldir_resource_name "qmake_${arg_TARGET_PATH}")
|
||||
set_source_files_properties(${arg_OUTPUT_DIRECTORY}/qmldir
|
||||
PROPERTIES QT_RESOURCE_ALIAS "qmldir"
|
||||
)
|
||||
set(resource_targets)
|
||||
qt6_add_resources(${target} ${qmldir_resource_name}
|
||||
FILES ${arg_OUTPUT_DIRECTORY}/qmldir
|
||||
OUTPUT_TARGETS resource_targets
|
||||
)
|
||||
list(APPEND output_targets ${resource_targets})
|
||||
endif()
|
||||
|
||||
if(arg_PLUGIN_TARGET AND NOT arg_NO_CREATE_PLUGIN_TARGET)
|
||||
|
|
|
@ -695,12 +695,15 @@ void QQmlTypeData::continueLoadFromIR()
|
|||
for (const QV4::CompiledData::Import *import : qAsConst(m_document->imports)) {
|
||||
if (!addImport(import, {}, &errors)) {
|
||||
Q_ASSERT(errors.size());
|
||||
for (QQmlError &error : errors) {
|
||||
error.setUrl(m_importCache.baseUrl());
|
||||
error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line));
|
||||
error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column));
|
||||
}
|
||||
setError(errors);
|
||||
|
||||
// We're only interested in the chronoligically last error. The previous
|
||||
// errors might be from unsuccessfully trying to load a module from the
|
||||
// resource file system.
|
||||
QQmlError error = errors.first();
|
||||
error.setUrl(m_importCache.baseUrl());
|
||||
error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line));
|
||||
error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column));
|
||||
setError(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,23 +62,32 @@ void tst_basicapp::loadComponent()
|
|||
void tst_basicapp::resourceFiles()
|
||||
{
|
||||
QVERIFY(QFile::exists(QStringLiteral(":/BasicApp/main.qml")));
|
||||
QVERIFY(QFile::exists(QStringLiteral(":/BasicApp/qmldir")));
|
||||
QVERIFY(QFile::exists(QStringLiteral(":/TimeExample/Clock.qml")));
|
||||
QVERIFY(QFile::exists(QStringLiteral(":/TimeExample/center.png")));
|
||||
QVERIFY(QFile::exists(QStringLiteral(":/TimeExample/clock.png")));
|
||||
QVERIFY(QFile::exists(QStringLiteral(":/TimeExample/hour.png")));
|
||||
QVERIFY(QFile::exists(QStringLiteral(":/TimeExample/minute.png")));
|
||||
QVERIFY(QFile::exists(QStringLiteral(":/TimeExample/qmldir")));
|
||||
|
||||
QVERIFY(!QFile::exists(QStringLiteral(":/BasicApp/tst_qmlbasicapp.qmltypes")));
|
||||
QVERIFY(!QFile::exists(QStringLiteral(":/TimeExample/qmlqtimeexample.qmltypes")));
|
||||
}
|
||||
|
||||
void tst_basicapp::fileSystemFiles()
|
||||
{
|
||||
const QString basedir = QCoreApplication::applicationDirPath();
|
||||
QVERIFY(QFile::exists(basedir + QStringLiteral("/main.qml")));
|
||||
QVERIFY(QFile::exists(basedir + QStringLiteral("/qmldir")));
|
||||
QVERIFY(QFile::exists(basedir + QStringLiteral("/tst_qmlbasicapp.qmltypes")));
|
||||
QVERIFY(QFile::exists(basedir + QStringLiteral("/TimeExample/Clock.qml")));
|
||||
|
||||
QVERIFY(QFile::exists(basedir + QStringLiteral("/TimeExample/center.png")));
|
||||
QVERIFY(QFile::exists(basedir + QStringLiteral("/TimeExample/clock.png")));
|
||||
QVERIFY(QFile::exists(basedir + QStringLiteral("/TimeExample/hour.png")));
|
||||
QVERIFY(QFile::exists(basedir + QStringLiteral("/TimeExample/minute.png")));
|
||||
QVERIFY(QFile::exists(basedir + QStringLiteral("/TimeExample/qmldir")));
|
||||
QVERIFY(QFile::exists(basedir + QStringLiteral("/TimeExample/qmlqtimeexample.qmltypes")));
|
||||
}
|
||||
|
||||
void tst_basicapp::qmldirContents()
|
||||
|
@ -92,6 +101,10 @@ void tst_basicapp::qmldirContents()
|
|||
QVERIFY(contents.contains("prefer :/BasicApp/"));
|
||||
QVERIFY(!contents.contains("classname"));
|
||||
QVERIFY(!contents.contains("plugin"));
|
||||
|
||||
QFile qmldirInResources(":/BasicApp/qmldir");
|
||||
QVERIFY(qmldirInResources.open(QIODevice::ReadOnly));
|
||||
QCOMPARE(qmldirInResources.readAll(), contents);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -105,6 +118,10 @@ void tst_basicapp::qmldirContents()
|
|||
QVERIFY(contents.contains("depends QtQml"));
|
||||
QVERIFY(contents.contains("prefer :/TimeExample/"));
|
||||
QVERIFY(contents.contains("Clock 1.0 Clock.qml"));
|
||||
|
||||
QFile qmldirInResources(":/TimeExample/qmldir");
|
||||
QVERIFY(qmldirInResources.open(QIODevice::ReadOnly));
|
||||
QCOMPARE(qmldirInResources.readAll(), contents);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
2:1:"org.qtproject.PureJsModule" is ambiguous.
|
||||
2:1:"org.qtproject.PureJsModule" is ambiguous.
|
||||
|
|
|
@ -56,6 +56,18 @@ qt_internal_add_resource(tst_qqmlmoduleplugin "qmake_staticPlugin"
|
|||
${qmake_staticPlugin_resource_files}
|
||||
)
|
||||
|
||||
set_source_files_properties("imports/ModuleFromQrc/badqmldir"
|
||||
PROPERTIES QT_RESOURCE_ALIAS "imports/ModuleFromQrc/qmldir"
|
||||
)
|
||||
|
||||
qt_internal_add_resource(tst_qqmlmoduleplugin "moduleFromQrc"
|
||||
PREFIX
|
||||
"/foo/"
|
||||
FILES
|
||||
"imports/ModuleFromQrc/badqmldir"
|
||||
"imports/ModuleFromQrc/Foo.qml"
|
||||
)
|
||||
|
||||
|
||||
#### Keys ignored in scope 2:.:.:tst_qqmlmoduleplugin.pro:<TRUE>:
|
||||
# CONFIG = "testcase" "-app_bundle"
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
import QtQml
|
||||
QtObject {}
|
|
@ -0,0 +1,3 @@
|
|||
module ModuleFromQrc
|
||||
plugin badbad
|
||||
Foo 1.0 Foo.qml
|
|
@ -0,0 +1,2 @@
|
|||
module ModuleFromQrc
|
||||
Foo 1.0 Foo.qml
|
|
@ -86,6 +86,7 @@ private slots:
|
|||
void parallelPluginImport();
|
||||
void multiSingleton();
|
||||
void optionalPlugin();
|
||||
void moduleFromQrc();
|
||||
|
||||
private:
|
||||
QString m_importsDirectory;
|
||||
|
@ -830,6 +831,16 @@ void tst_qqmlmoduleplugin::optionalPlugin()
|
|||
QVERIFY(!object10.isNull());
|
||||
}
|
||||
|
||||
void tst_qqmlmoduleplugin::moduleFromQrc()
|
||||
{
|
||||
QQmlEngine engine;
|
||||
engine.setImportPathList({ QStringLiteral(":/foo/imports/"), m_dataImportsDirectory });
|
||||
QQmlComponent component(&engine);
|
||||
component.setData("import ModuleFromQrc\nFoo {}\n", QUrl());
|
||||
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
|
||||
QScopedPointer<QObject> object(component.create());
|
||||
QVERIFY(!object.isNull());
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_qqmlmoduleplugin)
|
||||
|
||||
|
|
Loading…
Reference in New Issue