diff --git a/api_review_qt_6_2_qml.md b/api_review_qt_6_2_qml.md index 052c16dfb63d63d333c2d27dc94a4a17bceb2b90..7bb04df8c9e2440948353944cbac84dec3967ff5 100644 --- a/api_review_qt_6_2_qml.md +++ b/api_review_qt_6_2_qml.md @@ -20,7 +20,10 @@ qt_add_qml_module(Qml **Ulf:** I don't really care about the exact name, but looking at other Qt plugins, we generally seem to use no uppercase letters in plugin names. So, maybe just lowercase whatever it currently does and call it a day. -**Decision:** TBD +**Decision:** +AP: Craig: Check what actual name we write out into the qmldir file. +For the output name of the plugin, just lower case it and append '_plugin', see what breaks. +Up to Craig if we should rename the target name as well. ## Discussion about the behavior of target creation by default @@ -33,7 +36,7 @@ All three of the following examples work: ```cmake # CASE 1: Pre-create plugin target, NO_CREATE_PLUGIN_TARGET not needed since auto-detected qt_add_qml_plugin(FooPlugin) -qt_add_qml_module(Foo PLUGIN_TARGET FooPlugin) +qt_add_qml_module(Foo PLUGIN_TARGET FooPlugin) ``` ```cmake @@ -59,15 +62,26 @@ I'm fine with either way. The only thing out of those options that I see as stri ```cmake # CASE 4: No plugin at all; executable needs to link or be identical to the backing target +# In this case, the qmldir entry for the plugin should not be added either. qt_add_qml_module(Foo NO_CREATE_PLUGIN_TARGET) ``` +```cmake +qt_add_qml_module(Foo) <- this is ok, creates a default optional plugin target. (#1) +qt_add_qml_module(Foo NO_CREATE_PLUGIN_TARGET PLUGIN_TARGET MyPlugFoo) <- this is ok, the plugin targed will be created later. (#2) +qt_add_qml_module(Foo NO_PLUGIN PLUGIN_TARGET MyPlugFoo) <- this should be a FATAL_ERROR, NO_PLUGIN is a new proposed option (#3) +qt_add_qml_module(Foo NO_CREATE_PLUGIN_TARGET ) <- this should be a FATAL_ERROR in Qt6.2, can revisit for later version. (#4) +``` + ```cmake # CASE 5: Plugin target same as backing target; that is, the whole module lives in the plugin qt_add_qml_module(Foo PLUGIN_TARGET Foo NO_PLUGIN_OPTIONAL) ``` -**Decisions:** TBD +**Decisions:** +AP: Add a NO_PLUGIN option. +AP: Handle #3 and #4 in CASE 4 above. +AP: NO_PLUGIN_OPTIONAL should be automatically assumed, if $target == $plugin_target. ## Should we rename NO_GENERATE_SOURCE to NO_GENERATE_PLUGIN_SOURCE in qt_add_qml_plugin()? @@ -75,13 +89,19 @@ JIRA: [QTBUG-95090](https://bugreports.qt.io/browse/QTBUG-95090) This would make the keyword the same as the associated keyword in `qt_add_qml_module()`. Potential counter-argument: the `qt_add_qml_plugin()` command name is already about plugins. -**Decision:** TBD +**Decision:** + +AP: Rename the option to `NO_GENERATE_PLUGIN_SOURCE` in `qt_add_qml_plugin`. ## Should we always enable AUTOMOC for qml modules? -I think we tacitly agreed the answer is yes, but have we fully thought through the consequences of this? Type registration does have code paths for AUTOMOC being off, so it might not be +I think we tacitly agreed the answer is yes, but have we fully thought through the consequences of this? Type registration does have code paths for AUTOMOC being off, so it might not be -**Decision:** TBD +**Decision:** +We are not requiring AUTOMOC to be enabled on a target. +If it's disabled by the user project, it's up to the project developer to fix fall out. +Document that enabling AUTOMOC is highly recommended. +In 6.3+ we should revisit to make sure that registration can work even without AUTOMOC, if the user project mocs files manually. ## What should be the default output directory of qml modules? How does it impact tooling or running an app? @@ -93,7 +113,7 @@ Consider an app whose executable is a QML module, and it uses a couple of other *Source directory structure:* ``` -+-- main.qml ++-- main.qml <- has import TimeExample +-- Foo.qml +-- TimeExample | +-- Clock.qml @@ -117,6 +137,36 @@ Build directory structure should be the same as the source directory structure, +-- qmldir ``` +*Build directory structure:* +``` ++-- main.qml ++-- Foo.qml ++-- qmldir ++-- BasicApp.exe ++-- TimeExample +| +-- Clock.qml +| +-- qmldir ++-- OtherStuff + +-- blah.qml + +-- qmldir +``` + +*Build directory structure if QT_QML_OUTPUT_DIRECTORY == ./my-qml-import +``` ++-- BasicApp.exe ++-- my-qml-import +| +-- BasicApp +| +-- main.qml +| +-- Foo.qml +| +-- qmldir +| +-- TimeExample +| +-- Clock.qml +| +-- qmldir +| +-- OtherStuff +| +-- blah.qml +| +-- qmldir +``` + Running `qmllint` on any of the `BasicApp` qml files will find `TimeExample` via the implicit import path. But let's say that `TimeExample` does an `import OtherStuff` and we run `qmllint` on `TimeExample`'s qml files. In order for `qmllint` to find `OtherStuff`, we would likely need to add the root of the above directory tree to `qmllint`'s import path. If `QT_QML_OUTPUT_DIRECTORY` is set, then that is easy because that variable is precisely the path we would need to add. If that variable is not set, we would have to walk up the directory tree from `TimeExample` and add the base dir, then assume all other QML modules will also be under that base location. * Do we require projects to follow this rigid directory structure? @@ -136,7 +186,7 @@ NOTE: May merge the discussion of the next topic into this one too. * The case of loading the starting point from the resource file system is important, but separate. Currently you do have to set up an import path for this, and you do have to link all the modules together, and all the plugins need to be optional. That's quite a few extra requirements. * We might merge those cases and add the extra requirements to the rigid structure. I'm actually not opposed to this, but it should be an explicit step. * The argument about linting a file from `TimeExample` is important. We still need an import path in the build directory even if we load the starting point from the resource file system by default. For finding the root of the import path a few things have been proposed: - 1. Remove TARGET_PATH from the end of CMAKE_CURRENT_BINARY_DIR. If it doesn't match, warn. Otherwise that's the import path. The upside of this is that it's very simple. The downside is that it dictates a project structure where all the modules are in the same import path (unless externally provided) + 1. Remove TARGET_PATH from the end of CMAKE_CURRENT_BINARY_DIR. If it doesn't match, warn. Otherwise that's the import path. The upside of this is that it's very simple. The downside is that it dictates a project structure where all the modules are in the same import path (unless externally provided) OPTION1_MARKER! 2. Mark some directories as import path with some CMake construct. Warn if URIs of QML modules below them don't match the target paths. This has the upside of being precise and extensible, but the downside of requiring a manual setting. I don't know if it actually works. We might just automatically mark any directory that contains an executable as that is potentially an application binary dir. If so, we need a way to override it. 3. The tools themselves could automatically deduce the import path like the build system would in option 1. This has the upside of requiring no change to the build system and the downside of adding overhead and complexity to the tools. It also might produce false positives for cases where you invent your own project structure. * Whatever way we choose for finding the import path, we still have the problem of placing the `BasicApp` module in a suitable directory. The following aspects need to be considered: @@ -145,7 +195,13 @@ NOTE: May merge the discussion of the next topic into this one too. 3. If we determine the import path independently of the module location, we can build modules into CMAKE_CURRENT_BINARY_DIR without further special casing. * We need to be able to import the other modules with minimal effort at run time. In the best case this means not setting any explicit import path. This _seems_ to be related to the location of `BasicApp` but is actually _not_. It's a function of the locations of the executable and the other modules, and of whether we load from the host file system or the resource file system. -**Decisions:** TBD +**Decisions:** +AP: Generate a special qrc resource metadata file, which the QML engine will try to look up, and if it finds it, it adds the provided import path as a default import + for an executable. +AP: If we are trying to run qmllint on a qml file belonging to $backing_target and $backing_target is an executable, + CMake should pass -I ${CMAKE_CURRENT_BINARY_DIR} of the executable to qmlint. + Same should be done for qmlcachegen, qmlcachegenplus and qml type compiler. +AP: If we are trying to run qmllint on a qml file belonging to a library, not an executable, we need to use the backtracking algorithm that Ulf suggests (See OPTION1_MARKER!) ## What's our expectation on linking to a backing library without a plugin, and ensure that it works? @@ -167,12 +223,18 @@ Open questions: **Ulf:** * I've invented static-backing-lib/shared-plugin case in order to make it possible to have modules fully contained in their plugins. I've since learned that there is a better way to do this. We can therefore drop this case. -* Multiple QML modules in a single executable. This remains a valid case. +* Multiple QML modules in a single executable. This remains a valid case. * We could use it to cut down on the number of shared libraries we need to ship for QtQuick.Controls for example. The QML modules can contain singletons in this case. We just need to make the qmldir and qmltypes files available in the right locations. Therefore, build dir layout and conformance to import paths (see above) does matter. `qmllint` etc need to find the qmldir and qmltypes files. The fact that the binary that contains the types is elsewhere doesn't change anything about the logical location of the module. * Optimally, a plugin for such a module would link to the combined dynamic library that contains all the modules (if it is a library and not an executable). However, restricting the case to modules without plugins for now would be fine (and not solve the qqc2 case, but I guess we can hack that somehow). * I still don't know how a user should set up the project so that the initializers don't get discarded by the linker. Just linking the static backing targets doesn't work, does it? Object libraries are frowned upon, AFAIU. So, how would it work? -**Decisions:** TBD +**Decisions:** +AP: Ulf: Create a QT_IMPORT_QML_PLUGIN macro that will call QT_IMPORT_PLUGIN in Qt 6.2, but will be a no-op in 6.3+, + once automatic handling of the plugins will happen from CMake's side. +AP: Craig: Plugin target STATICality has to be the same as the main backing library STATICality. Ensure it is so and document it. +AP: Don't document the use case where an executable links to shared backing libraries only, for 6.2, because of the discarded symbol issue. + This will be solved in later Qt versions, by creating obj libs that reference the type registration function, kinda like for static plugin case. +AP: DO document the use case where an executable links to static backing libraries only for 6.2. ## Do we want to restrict `qt_target_qml_sources()` to require that the target given to it must be a QML module? @@ -182,7 +244,8 @@ Certain simple use cases can use an ordinary target that isn't a QML module and * If so, how would we expect them to add that `main.qml` file to the app resources? * `qt_add_resources()` would work, but seems to go against our "use the provided `qt_target_qml_sources()` command for adding qml files" mantra. -**Decision:** TBD +**Decision:** +AP: Yes, we want to do this restriction in Qt 6.2. We can choose to relax it in a later Qt release. ## We now run qmllint on the files in the source directory. There won't be any qmldir files there. Is that a problem?