Dependency Cycle with Qt6 / QML

Hi,

I have some project, where I introduced some qml. The command is the following:

qt_add_qml_module(BeansClient
    URI Beans.Gui
    VERSION 1.0
    QML_FILES
        qml/Windows/Home/main.qml
        qml/Components/NavigationBar.qml
    RESOURCES
        qml/resources/images/logo.png
)

and it works fine. I then wanted to move this code in the qml folder and did a

add_subdirectory(qml) 

and

qt_add_qml_module(BeansClient
    URI Beans.Gui
    VERSION 1.0
    QML_FILES
        Windows/Home/main.qml
        Components/NavigationBar.qml
    RESOURCES
        resources/images/logo.png
)

But this lead to the following error message:

ninja: error: dependency cycle: src/BeansClient_autogen/timestamp -> src/qml/BeansClient_tooling -> src/qml/.rcc/qmlcache/BeansClient_Windows/Home/main_qml.cpp -> src/qml/Beans/Gui/BeansClient.qmltypes -> src/meta_types/qt6beansclient_debug_metatypes.json -> src/meta_types/BeansClient_json_file_list.txt -> src/BeansClient_autogen/timestamp

I saw a similar topic about this here https://gitlab.kitware.com/cmake/cmake/-/issues/21977. But either this a different issue or the one here is not solved.

Any help is appreciated.

Edit: With make there is also an error message, which might be more helpful?

CMake Error: The inter-target dependency graph contains the following strongly connected component (cycle):
  "BeansClient_autogen" of type UTILITY
    depends on "BeansClient_tooling" (strong)
  "BeansClient_automoc_json_extraction" of type UTILITY
    depends on "BeansClient_autogen" (strong)
  "BeansClient_tooling" of type INTERFACE_LIBRARY
    depends on "BeansClient_automoc_json_extraction" (strong)
At least one of these targets is not a STATIC_LIBRARY.  Cyclic dependencies are allowed only among static libraries.
CMake Generate step failed.  Build files cannot be regenerated correctly.

I’ve been out of the loop on that code for a while, but @alcroito @jobor might have some thoughts on this one.

#21977 looks unrelated.

It’s not entirely clear how exactly your project setup looks like.
Maybe you could post a small example that exhibits the issue.
But your issue seems similar to Loading...

Quote from the comments:

You should keep the QML files in the same directory as the CMakeLists.txt that declares the module. Otherwise you’re in for a world of pain.

Sure! I thought it would be difficult to reproduce, but in fact it’s very easy.

qm_cycle.zip (4.4 KB)

Here is a small sample project. Currently it doesn’t work. If you remove in src/CMakeLists.txt the add_subdirectory and use the qml module command there, it should work.

So I see two potential issues:

So the comments there state that you should always put the qml files in the same directory for reasons.

Question 1: Why doesn’t the docs mention it?
Question 2: Why is there no explicit warning about this
Question 3: Most importantly: How should this work in any real project? This is crazy!! Putting everything in one folder, what a mess. And the only solution is to declare a new module for each subfolder? This makes QML a lot harder to use and in the end will lead to projects where having it dumped into one folder and a huge mess. I think this is inacceptable. Just imagine the requirement would be for the whole C++ project. Please don’t use subfolders :sweat_smile:

Thanks for the example. It’s indeed QTBUG-99768 you’re hitting.

Question 1: Why doesn’t the docs mention it?

They mention it implicitly by having the executable target and the qt_add_qml_module call in the same directory scope in every example.

It could be made clearer like suggested by Nils in QTBUG-99768.

Question 2: Why is there no explicit warning about this

This can be done. Ideally, we’d find a way to lift the restriction.

Question 3: Most importantly: How should this work in any real project?

You don’t have to “put everything into one folder”.

The restriction is that you have to put your add_executable and qt_add_qml_module calls into the same CMakeLists.txt (or at least the same CMake directory scope).

I’m a little bit confused right now what the issue really is about. Because the quote part:
"You should keep the QML files in the same directory as the CMakeLists.txt that declares the module. "

Sounds like the qml files, i.e. main.qml, button.qml, *.qml should all be in the same directory as the CMakeLists.txt or “I’m in a world of pain”.
This means

- myfolder 
  - CMakeLists.txt 
  - main.qml <- Same folder
  - button.qml <- Same folder
  - subfolder 
     - subfolderComponent.qml <- Not the same folder!

But the issue / you are also talking about qt_add_qml_module and add_executable should only be in the same scope. And the rest (-> The qml files themself) can be anywhere.

Did I just misunderstood the quote or are these two separate statements / problems?

And what would be the way forward? From what I can see the linked Issue QTBUG-99768 is not a bug, but a feature request, which is mainly motivated by the bug (as it turned out in the comments). So in general, this should probably be converted into a bug, because bugs have higher priority than feature requests for obvious reasons. And I think this bug is quite bad. (Ideally the restriction would be completly fixed, but the bad part is that it’s right now very easy to do wrong).
Is this even a Qt Bug or a CMake Bug?

And I thought the issue here is also [QTBUG-95200] qt6_add_qml_module only works when used in same folder than the backing library - Qt Bug Tracker, which is marked as fixed. I don’t fully understand if it actually was fixed in the end. Maybe @craig.scott does know something about it here?

That the terms folder/subdirectory/directory scope are used in different context doesn’t make things clearer I have to admit.

add_subdirectory introduces a new directory scope, and that causes the problem you’re facing. You can work around the issue by avoiding add_subdirectory for your qt_add_qml_module call. You can do that by either putting qt_add_qml_module back into your CMakeLists.txt that calls add_executable or by including a .cmake file that calls qt_add_qml_module.

Creating a follow-up bug report at bugreports.qt.io would be appropriate.

Thanks!

One thing I wonder as well is: Would it be useful to have additional commands like target_sources for qml modules?

With my regular C++ files I usually define a target in the top level source folder and then do add_subdirectory and target sources to collect the files.
I think it would be cool to do the same with qml files (and also C++ files, other ressources in a qml module)

You can certainly file a suggestion for that.
With the current infrastructure it would be non-trivial to implement though.