Details
-
Suggestion
-
Resolution: Unresolved
-
P2: Important
-
None
Description
Background
Based on many support requests and forum posts, a common cry for help is, "My project obviously runs just fine... so why does Qt Creator say that it can't find my imports?!" Furthermore, those messages displayed by the IDE can differ between different QML tools. It's really not obvious to people why this is.
Case Study
The attached project contains a variety of "non-recommended" structures that are commonly found in user projects:
CMakeLists.txt ├── App/ │ ├── CMakeLists.txt # URI: App │ ├── ExemplaryLib/ │ │ └── CMakeLists.txt # URI: ExemplaryLib │ ├── nested/ │ │ └── DeepLib/ │ │ └── CMakeLists.txt # URI: DeepLib │ └── MismatchedUriLib/ │ └── CMakeLists.txt # URI: Mismatched.URI.Lib └── OutOfTreeLib/ └── CMakeLists.txt # URI: OutOfTreeLib
Scenario 1: Vanilla project
The attached project does not specify DEPENDENCIES or QT_QML_GENERATE_QMLLS_INI, so I guess we can expect some issues. Let's see... who can find which import in Main.qml?:
Import statement | Runtime QML engine | qmlsc | qmllint | qmltc | qmlls |
---|---|---|---|---|---|
import App | ✔ | ✔ | ✔ | ✔ | ✘ |
import ExemplaryLib | ✔ | ✔ | ✔ | ✘ | ✘ |
import DeepLib | ✔ | ✘ | ✘ | ✘ | ✘ |
import Mismatched.URI.Lib | ✔ | ✘ | ✘ | ✘ | ✘ |
import OutOfTreeLib | ✔ | ✘ | ✘ | ✘ | ✔ |
Observations
- "App" and "ExemplaryLib" follows the "recommended structure" relative to each other, so qmlsc and qmllint are happy.
- qmltc failed to appreciate the fact that ExemplaryLib has followed our recommendations.
- Worse and more confusingly, qmlls can't find ExemplaryLib... but it can find OutOfTreeLib!
Scenario 2: Specifying DEPENDENCIES
Now, let's say I specify the DEPENDENCIES correctly:
Import statement | Runtime QML engine | qmlsc | qmllint | qmltc | qmlls |
---|---|---|---|---|---|
import App | ✔ | ✔ | ✔ | ✔ | ✘ |
import ExemplaryLib | ✔ | ✔ | ✔ | ✘ | ✘ |
import DeepLib | ✔ | ✔ | ✔ | ✘ | ✘ |
import Mismatched.URI.Lib | ✔ | ✘ | ✘ | ✘ | ✘ |
import OutOfTreeLib | ✔ | ✔ | ✔ | ✘ | ✔ |
Observations
- Yay, qmlsc and qmllint see more modules now. But still not all.
- qmltc or qmlls enjoyed no benefits whatsoever.
Scenario 3: Setting QT_QML_OUTPUT_DIRECTORY to ${CMAKE_BINARY_DIR}
Suppose that a different developer inherited the attached project, and he didn't know about DEPENDENCIES. Instead, he read somewhere that setting QT_QML_OUTPUT_DIRECTORY might help so he tried it:
Import statement | Runtime QML engine | qmlsc | qmllint | qmltc | qmlls |
---|---|---|---|---|---|
import App | ✔ | ✔ | ✔ | ✔ | ✔ |
import ExemplaryLib | ✔ | ✘ | ✔ | ✘ | ✔ |
import DeepLib | ✔ | ✘ | ✔ | ✘ | ✔ |
import Mismatched.URI.Lib | ✔ | ✘ | ✔ | ✘ | ✔ |
import OutOfTreeLib | ✔ | ✘ | ✔ | ✘ | ✔ |
Observations
- Magic! Now qmllint and qmlls see everything, just like the runtime engine!
- And yet, qmlsc is now worse off than when we started.
Notes
- This study was done with Qt 6.9.0
- I wanted to test QML Preview too, but couldn't due to
QTBUG-137197 - I am aware of the known limitations of qmltc so I'm not expecting type compilation to succeed. However, I do expect qmltc to see the same import paths as the other tools (i.e. qmltc should not complain, "Failed to import ExemplaryLib. Are your import paths set up properly?")
Overall observations
- It is very bad for our tools to disagree with each other like this (where one tools says that import paths are fine while a different tool says they're not).
- It is a lousy developer experience when our tools can't find imports when the app runs perfectly fine.
- Scenario 1 shows that qmlls already treats the build directory as an import path by default. But the other tools don't.
- Scenario 3 shows that qmllint also treats the build directory as an import path IF we manually point the output directory to it. But the other tools don't.
Proposals
By default, we should maximize the chances that our QML tools "see" exactly what the runtime QQmlEngine would "see", even if the project doesn't follow our recommended structure. Here is an easy way to do so:
- Set QT_QML_OUTPUT_DIRECTORY to ${CMAKE_BINARY_DIR} by default (a new Policy, perhaps?)
- Make the build directory a default import path for all build tools, just like qmlls
- In the event that the user decides to set a different output directory: All our QML tools should still always use the same import paths as each other.
- Import paths are currently stored in *.rsp files and passed to qmllint. Could the *.rsp import paths be refactored out and shared between all tools? This would reduce the risk of future divergence.
Rationale
Just the first 2 proposals alone will give us this out of the box:
Import statement | Runtime QML engine | qmlsc | qmllint | qmltc | qmlls |
---|---|---|---|---|---|
import App | ✔ | ✔ | ✔ | ✔ | ✔ |
import ExemplaryLib | ✔ | ✔ | ✔ | ✔ | ✔ |
import DeepLib | ✔ | ✔ | ✔ | ✔ | ✔ |
import Mismatched.URI.Lib | ✔ | ✔ | ✔ | ✔ | ✔ |
import OutOfTreeLib | ✔ | ✔ | ✔ | ✔ | ✔ |
...even if the user was unaware of DEPENDENCIES or .qmlls.ini files.
This means:
- Less confusion/frustration in the QML community, less calls for help, and more time for everyone to do more useful things.
- qmlls can work usefully most of the time, even if .qmlls.ini files cannot be used (e.g. when the source code is in a read-only location)
- We no longer have to impose restrictions/recommendations on users' project structure
Attachments
Issue Links
- relates to
-
QTBUG-122986 Document recommended project structure for projects that have a QML module used by two or more executables
-
- Reported
-
-
QTBUG-133873 Provide comprehensive documentation for imports in QML
-
- Reported
-