Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

register QML elements at build time #428

Merged
merged 5 commits into from
Feb 9, 2023
Merged

Conversation

Be-ing
Copy link
Contributor

@Be-ing Be-ing commented Feb 7, 2023

by specifying #[cxxqt::qobject(qml_uri = "foo.bar", qml_verison = "1.0")]

TODO:

  • add tests
  • code documentation
  • user documentation

@Be-ing Be-ing marked this pull request as draft February 7, 2023 21:16
@Be-ing Be-ing force-pushed the qml_element branch 6 times, most recently from 8fd1360 to bd9019b Compare February 8, 2023 01:25
@Be-ing Be-ing marked this pull request as ready for review February 8, 2023 01:25
@Be-ing
Copy link
Contributor Author

Be-ing commented Feb 8, 2023

The trick to getting static QML plugin registration working with the Q_IMPORT_PLUGIN C++ macro, as well as static initialization of Qt resource files, turned out to be the (relatively new) +whole-archive link option. Only a few of the generated C++ files need to be linked this way, so they are compiled into a separate static library, which then gets linked into the staticlib crate or executable. This gets rid of all the C++ boilerplate in the cargo_without_cmake example! 🦀 Users don't need to write any C++ to write a QML application.

Unfortunately this only works when Cargo links the executable though. CMake has an analogous WHOLE_ARCHIVE feature, but when I tried to use it like:

target_link_libraries(${APP_NAME} PRIVATE ${CRATE} "$<LINK_LIBRARY:WHOLE_ARCHIVE,${CRATE}-static>")

linking failed because the static library includes duplicate symbols:

[40/68] Linking CXX executable examples/qml_minimal/example_qml_minimal
FAILED: examples/qml_minimal/example_qml_minimal 
: && /usr/lib64/ccache/clang++ -g -fuse-ld=mold examples/qml_minimal/CMakeFiles/example_qml_minimal.dir/example_qml_minimal_autogen/mocs_compilation.cpp.o examples/qml_minimal/CMakeFiles/example_qml_minimal.dir/cpp/main.cpp.o examples/qml_minimal/CMakeFiles/example_qml_minimal.dir/example_qml_minimal_autogen/CCBC4FUR7J/qrc_qml.cpp.o -o examples/qml_minimal/example_qml_minimal  -Xlinker --push-state -Xlinker --whole-archive  examples/qml_minimal/libqml_minimal.a  -Xlinker --pop-state  /usr/lib64/libQt6QuickControls2.so.6.4.1  /usr/lib64/libQt6Quick.so.6.4.1  /usr/lib64/libQt6QmlModels.so.6.4.1  /usr/lib64/libQt6Qml.so.6.4.1  /usr/lib64/libQt6Network.so.6.4.1  /usr/lib64/libQt6OpenGL.so.6.4.1  /usr/lib64/libQt6Gui.so.6.4.1  /usr/lib64/libQt6Core.so.6.4.1  /usr/lib64/libGLX.so  /usr/lib64/libOpenGL.so  -ldl  -lrt  -lpthread  -lgcc_s  -lc  -lm  -lutil && :
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$MyObject$number_changed
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$MyObject$string_changed
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$MyObject$rust
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$MyObject$qt_thread
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$MyObjectCxxQtThread$queue_boxed_fn
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$MyObjectCxxQtThread$queue_boxed_fn$func$0
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxx_qt_my_object$cxxbridge1$new_cpp_object_my_object_qt
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$MyObject$rust_mut
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): MyObjectRust::layout::size()
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): MyObjectRust::layout::align()
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): MyObjectRust::getNumber(MyObject const&) const
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): MyObjectRust::setNumber(MyObject&, int)
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): MyObjectRust::getString(MyObject const&) const
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): MyObjectRust::setString(MyObject&, QString)
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): MyObjectRust::incrementNumberWrapper(MyObject&)
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): MyObjectRust::sayHiWrapper(MyObject const&, QString const&, int) const
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxx_qt_my_object::MyObjectCxxQtThreadQueuedFn::layout::size()
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxx_qt_my_object::MyObjectCxxQtThreadQueuedFn::layout::align()
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxx_qt_my_object::createRs()
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$unique_ptr$MyObjectCxxQtThread$null
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$unique_ptr$MyObjectCxxQtThread$raw
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$unique_ptr$MyObjectCxxQtThread$get
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$unique_ptr$MyObjectCxxQtThread$release
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$unique_ptr$MyObjectCxxQtThread$drop
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$unique_ptr$MyObject$null
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$unique_ptr$MyObject$raw
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$unique_ptr$MyObject$get
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$unique_ptr$MyObject$release
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): cxxbridge1$unique_ptr$MyObject$drop
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): rust::cxxbridge1::Box<cxx_qt_my_object::MyObjectCxxQtThreadQueuedFn>::allocation::alloc()
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): rust::cxxbridge1::Box<cxx_qt_my_object::MyObjectCxxQtThreadQueuedFn>::allocation::dealloc(cxx_qt_my_object::MyObjectCxxQtThreadQueuedFn*)
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): rust::cxxbridge1::Box<cxx_qt_my_object::MyObjectCxxQtThreadQueuedFn>::drop()
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): rust::cxxbridge1::Box<MyObjectRust>::allocation::alloc()
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): rust::cxxbridge1::Box<MyObjectRust>::allocation::dealloc(MyObjectRust*)
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxx.o): rust::cxxbridge1::Box<MyObjectRust>::drop()
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): MyObject::MyObject(QObject*)
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): MyObject::~MyObject()
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): MyObject::~MyObject()
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): MyObject::~MyObject()
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): MyObject::unsafeRust() const
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): MyObject::unsafeRustMut()
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): MyObject::qtThread() const
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): MyObject::getNumber() const
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): MyObject::setNumber(int const&)
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): MyObject::getString() const
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): MyObject::setString(QString const&)
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): MyObject::incrementNumber()
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): MyObject::sayHi(QString const&, int) const
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): cxx_qt_my_object::newCppObject()
mold: error: duplicate symbol: examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): examples/qml_minimal/libqml_minimal.a(c634a18e71c5f7cd-my_object.cxxqt.o): MyObject::MyObject(QObject*)
clang-15: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.

If there was a way to somehow pass the path of the +whole-archive static library built by cc in the Rust build script, CMake could link that with WHOLE_ARCHIVE. I tried this with a proof of concept using an absolute path and it worked. However I don't think there's a way to pass that information from Cargo to CMake. So CMake users still need to call qmlRegisterType in C++. That's not so bad by itself, but CMake users also can't use the qt_add_qml_module CMake function which precompiles QML. 😕 I haven't implemented calling qmlcachegen or qmltc from cxx-qt-build yet, but as far as I can tell it should be feasible.

@Be-ing
Copy link
Contributor Author

Be-ing commented Feb 8, 2023

It turns out the duplicate symbol errors before were because some C++ files were getting compiled twice and linked twice. 🙃 So it is possible to link with WHOLE_ARCHIVE using CMake to avoid needing to register QML elements in C++. The CMake code is a little more complicated, but it will be worth it after qmlcachegen/qmltc are supported by cxx-qt-build, which otherwise wouldn't be feasible.

Somehow this caused #110 to affect a few more tests on macOS 🤷 It also triggers what seems to be a bug in valgrind in a few tests on Ubuntu, which I can't reproduce locally on Fedora 37.

@vimpostor
Copy link
Member

vimpostor commented Feb 8, 2023

🦀 Users don't need to write any C++ to write a QML application.

Thanks for shaving this yak, this is really impressive! 🦀

Ironically, cxx-qt is now even better than the official Qt cmake API, as this is now also working for Qt5. 🎉 (i.e. at least the fully declarative type declaration, not sure if qmltc and qmlsc will also be possible)

@Be-ing
Copy link
Contributor Author

Be-ing commented Feb 8, 2023

(i.e. at least the fully declarative type declaration, not sure if qmltc and qmlsc will also be possible)

Checking for the presence of the qmltc executable and using it conditionally would be easy. qmlcachegen exists with Qt5 as well.

@Be-ing Be-ing force-pushed the qml_element branch 2 times, most recently from a0acc8f to bcac4ed Compare February 8, 2023 12:59
Copy link
Collaborator

@LeonMatthesKDAB LeonMatthesKDAB left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more cleanups to be done, but overall approach is good! 👍
We can finally claim that you don't need C++ anymore 🥳

@Be-ing Be-ing force-pushed the qml_element branch 3 times, most recently from fea483d to b0ebd28 Compare February 9, 2023 00:23
by specifying
[cxxqt::qobject(qml_uri = "foo.bar", qml_verison = "1.0")]

Fixes KDAB#241
to avoid C++ symbol name collisions if more than one qrc file
is used
@Be-ing
Copy link
Contributor Author

Be-ing commented Feb 9, 2023

I'd like to add a C++ generation test in cxx-qt-gen, but I don't see how to write the output files? Is there a tool for this somewhere that I'm missing?

@LeonMatthesKDAB
Copy link
Collaborator

LeonMatthesKDAB commented Feb 9, 2023

I'd like to add a C++ generation test in cxx-qt-gen, but I don't see how to write the output files? Is there a tool for this somewhere that I'm missing?

No, unfortunately not, the easiest way is to start with an empty file, which is gonna give you the complete diff to what is currently being generated.
Then just copy the diff into the file :D

We should probably work on such a tool at some point though, that just updates the test_output files.

@Be-ing
Copy link
Contributor Author

Be-ing commented Feb 9, 2023

We should probably work on such a tool at some point though, that just updates the test_output files.

How about adding that in another PR?

@LeonMatthesKDAB
Copy link
Collaborator

We should probably work on such a tool at some point though, that just updates the test_output files.

How about adding that in another PR?

Of course, shouldn't be part of this PR.

@Be-ing
Copy link
Contributor Author

Be-ing commented Feb 9, 2023

Ready to merge then?

Copy link
Collaborator

@LeonMatthesKDAB LeonMatthesKDAB left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, how about @vimpostor ?

@LeonMatthesKDAB LeonMatthesKDAB enabled auto-merge (rebase) February 9, 2023 13:52
@LeonMatthesKDAB LeonMatthesKDAB merged commit f56a68d into KDAB:main Feb 9, 2023
Be-ing added a commit to Be-ing/cxx-qt that referenced this pull request Feb 9, 2023
Be-ing added a commit to Be-ing/cxx-qt that referenced this pull request Feb 9, 2023
Be-ing added a commit to Be-ing/cxx-qt that referenced this pull request Feb 9, 2023
ahayzen-kdab pushed a commit to Be-ing/cxx-qt that referenced this pull request Feb 13, 2023
ahayzen-kdab pushed a commit that referenced this pull request Feb 13, 2023
przempore pushed a commit to przempore/cxx-qt that referenced this pull request Mar 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants