woboq / qmetaobject-rs

Integrate Qml and Rust by building the QMetaObject at compile time.
MIT License
644 stars 89 forks source link

Add support for Qt Quick Compiler #127

Open AdrianEddy opened 3 years ago

AdrianEddy commented 3 years ago

It would be nice to have a way to enable Qt Quick Compiler when using qml files embedded in qrc.

Looks like it just requires to invoke qmlcachegen and compile the resulting cpp file

ratijas commented 3 years ago

Hi, to the best of my knowledge, Qt5's qmlcachegen generates some kind of QML bytecode in *.qmlc format, no intermediate cpp files needed, and it's not a native compiled binary anyway.

With that being said, we could technically extend qrc!() macro to invoke qmlcachegen on the fly, and include resulting bytecode with or in place of original QML components — similar to what AUTO_RCC and AUTO_MOC do in CMake.

Can you share your tell more about the app you are building that requires pre-compiled QML, and what would be your workflow without Rust, i.e. in pure Qt/CMake?

AdrianEddy commented 3 years ago

Hmm, when creating a simple QtQuick project in Qt Creator and enabling Qt Quick Compiler, the build step includes calling qmlcachegen.exe and it produces a cpp file for each .qml file and a qmlcache_loader.cpp file. They are later compiled by C++ compiler and included at link stage. I attach simple project (run qmake and make there), but the full output is like this:

test-qtquickcompiler.zip

`make` output

``` Microsoft (R) Program Maintenance Utility Version 14.29.30038.1 Copyright (C) Microsoft Corporation. All rights reserved. "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\bin\HostX64\x64\nmake.exe" -f Makefile.Release Microsoft (R) Program Maintenance Utility Version 14.29.30038.1 Copyright (C) Microsoft Corporation. All rights reserved. D:\Programy\Qt\6.1.1\msvc2019_64\bin\qmlcachegen.exe --resource=D:/test/test/qml.qrc -o release\main_qml.cpp main.qml D:\Programy\Qt\6.1.1\msvc2019_64\bin\qmlcachegen.exe --resource-file-mapping="D:/test/test/qml.qrc=D:/test/test/qml_qmlcache.qrc" -o release\qmlcache_loader.cpp qml.qrc D:\Programy\Qt\6.1.1\msvc2019_64\bin\rcc.exe -name qml_qmlcache qml_qmlcache.qrc -o release\qrc_qml_qmlcache.cpp cl -c -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -permissive- -Zc:__cplusplus -Zc:externConstexpr -O2 -MD -std:c++17 -utf-8 -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -EHsc -DUNICODE -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DNDEBUG -DQT_NO_DEBUG -DQT_QUICK_LIB -DQT_OPENGL_LIB -DQT_GUI_LIB -DQT_QMLMODELS_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -I. -I..\..\Programy\Qt\6.1.1\msvc2019_64\include -I..\..\Programy\Qt\6.1.1\msvc2019_64\include\QtQuick -I..\..\Programy\Qt\6.1.1\msvc2019_64\include\QtOpenGL -I..\..\Programy\Qt\6.1.1\msvc2019_64\include\QtGui -I..\..\Programy\Qt\6.1.1\msvc2019_64\include\QtQmlModels -I..\..\Programy\Qt\6.1.1\msvc2019_64\include\QtQml -I..\..\Programy\Qt\6.1.1\msvc2019_64\include\QtNetwork -I..\..\Programy\Qt\6.1.1\msvc2019_64\include\QtCore -Irelease -I/include -I..\..\Programy\Qt\6.1.1\msvc2019_64\mkspecs\win32-msvc -Forelease\ @C:\Users\Eddy\AppData\Local\Temp\nm17DE.tmp main.cpp cl -c -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -permissive- -Zc:__cplusplus -Zc:externConstexpr -O2 -MD -std:c++17 -utf-8 -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -EHsc -DUNICODE -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DNDEBUG -DQT_NO_DEBUG -DQT_QUICK_LIB -DQT_OPENGL_LIB -DQT_GUI_LIB -DQT_QMLMODELS_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -I. -I..\..\Programy\Qt\6.1.1\msvc2019_64\include -I..\..\Programy\Qt\6.1.1\msvc2019_64\include\QtQuick -I..\..\Programy\Qt\6.1.1\msvc2019_64\include\QtOpenGL -I..\..\Programy\Qt\6.1.1\msvc2019_64\include\QtGui -I..\..\Programy\Qt\6.1.1\msvc2019_64\include\QtQmlModels -I..\..\Programy\Qt\6.1.1\msvc2019_64\include\QtQml -I..\..\Programy\Qt\6.1.1\msvc2019_64\include\QtNetwork -I..\..\Programy\Qt\6.1.1\msvc2019_64\include\QtCore -Irelease -I/include -I..\..\Programy\Qt\6.1.1\msvc2019_64\mkspecs\win32-msvc -Forelease\ @C:\Users\Eddy\AppData\Local\Temp\nm1C06.tmp main_qml.cpp qmlcache_loader.cpp qrc_qml_qmlcache.cpp Generating Code... link /NOLOGO /DYNAMICBASE /NXCOMPAT /OPT:REF /OPT:ICF /INCREMENTAL:NO /SUBSYSTEM:WINDOWS "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" /MANIFEST:embed /OUT:release\test.exe @C:\Users\Eddy\AppData\Local\Temp\nm2500.tmp ```

ratijas commented 3 years ago

Ah, Qt6. That's totally different thing. Haven't tried yet. Gotta take a look. Hang on.

AdrianEddy commented 3 years ago

Ah, Qt6. That's totally different thing. Haven't tried yet. Gotta take a look. Hang on.

Qt 5.12 and 5.15 seem to do the same:

output

``` Microsoft (R) Program Maintenance Utility Version 14.29.30038.1 Copyright (C) Microsoft Corporation. All rights reserved. "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\bin\HostX64\x64\nmake.exe" -f Makefile.Release Microsoft (R) Program Maintenance Utility Version 14.29.30038.1 Copyright (C) Microsoft Corporation. All rights reserved. D:\Programy\Qt\5.12.11\msvc2017_64\bin\qmlcachegen.exe --resource=D:/test/test/qml.qrc -o release\main_qml.cpp main.qml D:\Programy\Qt\5.12.11\msvc2017_64\bin\qmlcachegen.exe --resource-file-mapping=D:/test/test/qml.qrc -o release\qmlcache_loader.cpp qml.qrc cl -c -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -EHsc -DUNICODE -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DQT_NO_DEBUG -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -I. -I..\..\Programy\Qt\5.12.11\msvc2017_64\include -I..\..\Programy\Qt\5.12.11\msvc2017_64\include\QtQuick -I..\..\Programy\Qt\5.12.11\msvc2017_64\include\QtGui -I..\..\Programy\Qt\5.12.11\msvc2017_64\include\QtANGLE -I..\..\Programy\Qt\5.12.11\msvc2017_64\include\QtQml -I..\..\Programy\Qt\5.12.11\msvc2017_64\include\QtNetwork -I..\..\Programy\Qt\5.12.11\msvc2017_64\include\QtCore -Irelease -I/include -I..\..\Programy\Qt\5.12.11\msvc2017_64\mkspecs\win32-msvc -Forelease\ @C:\Users\Eddy\AppData\Local\Temp\nm6CC6.tmp main.cpp cl -c -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -EHsc -DUNICODE -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DQT_NO_DEBUG -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -I. -I..\..\Programy\Qt\5.12.11\msvc2017_64\include -I..\..\Programy\Qt\5.12.11\msvc2017_64\include\QtQuick -I..\..\Programy\Qt\5.12.11\msvc2017_64\include\QtGui -I..\..\Programy\Qt\5.12.11\msvc2017_64\include\QtANGLE -I..\..\Programy\Qt\5.12.11\msvc2017_64\include\QtQml -I..\..\Programy\Qt\5.12.11\msvc2017_64\include\QtNetwork -I..\..\Programy\Qt\5.12.11\msvc2017_64\include\QtCore -Irelease -I/include -I..\..\Programy\Qt\5.12.11\msvc2017_64\mkspecs\win32-msvc -Forelease\ @C:\Users\Eddy\AppData\Local\Temp\nm6E9B.tmp main_qml.cpp qmlcache_loader.cpp Generating Code... link /NOLOGO /DYNAMICBASE /NXCOMPAT /INCREMENTAL:NO /SUBSYSTEM:WINDOWS "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" /MANIFEST:embed /OUT:release\test.exe @C:\Users\Eddy\AppData\Local\Temp\nm7071.tmp ```

ratijas commented 3 years ago

Mystery solved: qmlcachegen has more than one operating mode. Which one will be chosen depends on output file name.

https://code.woboq.org/qt5/qtdeclarative/tools/qmlcachegen/qmlcachegen.cpp.html#437

enum Output {
    GenerateCpp,
    GenerateCacheFile,
    GenerateLoader
} target = GenerateCacheFile;

if (outputFileName.endsWith(QLatin1String(".cpp"))) {
    target = GenerateCpp;
    if (outputFileName.endsWith(QLatin1String("qmlcache_loader.cpp")))
        target = GenerateLoader;
}

EDIT

Woboq code browser is a little bit out of date (Generated on 2019-Aug-01 from project qtdeclarative revision v5.13.0-452-g4bac72aa13). Now, in Qt6, there's a fourth option:

    if (target == GenerateLoader && parser.isSet(resourceNameOption))
        target = GenerateLoaderStandAlone;
AdrianEddy commented 2 years ago

For reference, here's how I implemented it in my app: https://github.com/gyroflow/gyroflow/blob/1f685e483f8abc27e9f0ec5f177563ae1407430f/build.rs#L10-L63