aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/Qt6QmlBuildInternals.cmake
blob: 6ff18273f8193d56c967a7298985bcb68691938d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
#
# QtDeclarative Specific extensions
#

include_guard(GLOBAL)

macro(qt_internal_get_internal_add_qml_module_keywords
        option_args single_args multi_args
        internal_option_args internal_single_args internal_multi_args)
    set(${option_args}
        DESIGNER_SUPPORTED
        FOLLOW_FOREIGN_VERSIONING
        NO_PLUGIN
        NO_PLUGIN_OPTIONAL
        NO_CREATE_PLUGIN_TARGET
        NO_GENERATE_PLUGIN_SOURCE
        NO_GENERATE_QMLTYPES
        NO_GENERATE_QMLDIR
        NO_LINT
        NO_CACHEGEN
    )
    set(${single_args}
        URI
        VERSION
        PLUGIN_TARGET
        TYPEINFO
        CLASS_NAME
        CLASSNAME  # TODO: Remove once all other repos have been updated to use
                   #       CLASS_NAME instead.
    )
    set(${multi_args}
        QML_FILES
        RESOURCES
        IMPORTS
        IMPORT_PATH
        OPTIONAL_IMPORTS
        DEPENDENCIES
        PAST_MAJOR_VERSIONS
    )
    # Args used by qt_internal_add_qml_module directly, which should not be passed to any other
    # functions.
    #
    # INSTALL_SOURCE_QMLTYPES takes a path to an existing plugins.qmltypes file that should be
    # installed.
    #
    # INSTALL_SOURCE_QMLDIR takes a path to an existing qmldir file that should be installed.
    set(${internal_option_args}
    )
    set(${internal_single_args}
        INSTALL_SOURCE_QMLTYPES
        INSTALL_SOURCE_QMLDIR
    )
    set(${internal_multi_args}
    )

endmacro()

# This function is essentially a wrapper around qt6_add_qml_module().
# It creates the targets explicitly and sets up internal properties before
# passing those targets to qt6_add_qml_module() for further updates.
# All keywords supported by qt_internal_add_module() can be used, as can most
# keywords for qt6_add_qml_module() except RESOURCE_PREFIX and
# OUTPUT_TARGETS.
#
# OUTPUT_DIRECTORY and INSTALL_DIRECTORY will be given more appropriate defaults
# if not provided by the caller. The defaults are usually what you want to use.
#
# - SOURCES is only passed through to qt_internal_add_plugin() or
#   qt_internal_add_module() but not to qt6_add_qml_module().
#
# See qt_internal_add_plugin() and qt6_add_qml_module() for the full set of
# supported keywords.
function(qt_internal_add_qml_module target)

    qt_internal_get_internal_add_module_keywords(
        module_option_args
        module_single_args
        module_multi_args
    )

    qt_internal_get_internal_add_qml_module_keywords(
        qml_module_option_args
        qml_module_single_args
        qml_module_multi_args
        qml_module_internal_option_args
        qml_module_internal_single_args
        qml_module_internal_multi_args
    )
    # TODO: Remove these once all repos have been updated to not use them
    set(ignore_option_args
        SKIP_TYPE_REGISTRATION  # Now always done
        PLUGIN_OPTIONAL         # Now the default
        GENERATE_QMLTYPES       # Now the default
        INSTALL_QMLTYPES        # Now the default
    )

    set(option_args
        ${module_option_args}
        ${qml_module_option_args}
        ${ignore_option_args}
        ${qml_module_internal_option_args}
    )
    set(single_args
        ${module_single_args}
        ${qml_module_single_args}
        ${qml_module_internal_single_args}
    )
    set(multi_args
        ${module_multi_args}
        ${qml_module_multi_args}
        ${qml_module_internal_multi_args}
    )

    qt_parse_all_arguments(arg "qt_internal_add_qml_module"
        "${option_args}"
        "${single_args}"
        "${multi_args}"
        ${ARGN}
    )

    set(QT_QML_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_QMLDIR}")
    string(REPLACE "." "/" target_path ${arg_URI})
    if(NOT arg_OUTPUT_DIRECTORY)
        set(arg_OUTPUT_DIRECTORY "${QT_QML_OUTPUT_DIRECTORY}/${target_path}")
    endif()
    if(NOT arg_INSTALL_DIRECTORY)
        set(arg_INSTALL_DIRECTORY "${INSTALL_QMLDIR}/${target_path}")
    endif()
    if(arg_NO_PLUGIN)
        unset(arg_PLUGIN_TARGET)
    elseif(NOT arg_PLUGIN_TARGET)
        set(arg_PLUGIN_TARGET ${target}plugin)
    endif()

    # TODO: Support for old keyword, remove once all repos no longer use CLASSNAME
    if(arg_CLASSNAME)
        if(arg_CLASS_NAME AND NOT arg_CLASSNAME STREQUAL arg_CLASS_NAME)
            message(FATAL_ERROR
                "Both CLASSNAME and CLASS_NAME were given and were different. "
                "Update call site to only use CLASS_NAME."
            )
        endif()
        set(arg_CLASS_NAME "${arg_CLASSNAME}")
        unset(arg_CLASSNAME)
    endif()

    set(plugin_args "")
    if(arg_NO_PLUGIN OR NOT arg_PLUGIN_TARGET STREQUAL target)
        # Allow using an existing backing target.
        if(NOT TARGET ${target})
            # Create the backing target now to handle module-related things
            qt_remove_args(module_args
                ARGS_TO_REMOVE
                    ${ignore_option_args}
                    ${qml_module_option_args}
                    ${qml_module_single_args}
                    ${qml_module_multi_args}
                    ${qml_module_internal_option_args}
                    ${qml_module_internal_single_args}
                    ${qml_module_internal_multi_args}
                    OUTPUT_DIRECTORY
                    INSTALL_DIRECTORY
                ALL_ARGS
                    ${option_args}
                    ${single_args}
                    ${multi_args}
                ARGS
                    ${ARGN}
            )
            qt_internal_add_module(${target} ${module_args})
        elseif(arg_SOURCES)
            # If a module target was pre-created, we still need to pass the additional sources.
            target_sources(${target} PRIVATE ${arg_SOURCES})
        endif()
    else()
        # Since we are not creating a separate backing target, we have to pass
        # through the default args to the plugin target creation instead
        qt_internal_get_internal_add_plugin_keywords(
            plugin_option_args plugin_single_args plugin_multi_args
        )
        set(args_to_remove ${option_args} ${single_args} ${multi_args})
        list(REMOVE_ITEM args_to_remove
            ${plugin_option_args}
            ${plugin_single_args}
            ${plugin_multi_args}
        )
        qt_remove_args(plugin_args
            ARGS_TO_REMOVE
                ${args_to_remove}
                DEFAULT_IF
                OUTPUT_DIRECTORY
                INSTALL_DIRECTORY
                CLASS_NAME
                CLASSNAME
            ALL_ARGS
                ${option_args}
                ${single_args}
                ${multi_args}
            ARGS
                ${ARGN}
        )
    endif()

    set(add_qml_module_args "")

    if(NOT arg_NO_PLUGIN AND NOT arg_NO_CREATE_PLUGIN_TARGET)
        # If the qt_internal_add_qml_module call didn't specify a CLASS_NAME, we need to pre-compute
        # it here and pass it along to qt_internal_add_plugin -> qt_add_plugin so that
        # qt_add_qml_plugin does not complain about differing class names (the default pre-computed
        # class name of a regular plugin and qml plugin are different).
        if(NOT arg_CLASS_NAME)
            _qt_internal_compute_qml_plugin_class_name_from_uri("${arg_URI}" arg_CLASS_NAME)
        endif()

        # Create plugin target now so we can set internal things
        list(APPEND plugin_args
            PLUGIN_TYPE qml_plugin
            DEFAULT_IF FALSE
            OUTPUT_DIRECTORY ${arg_OUTPUT_DIRECTORY}
            INSTALL_DIRECTORY ${arg_INSTALL_DIRECTORY}
            CLASS_NAME ${arg_CLASS_NAME}
        )

        qt_internal_add_plugin(${arg_PLUGIN_TARGET} ${plugin_args})

        # Use the plugin target name as the main part of the plugin basename.
        set(plugin_basename "${arg_PLUGIN_TARGET}")

        # If the target name already ends with a "plugin" suffix, remove it and re-add it to the end
        # of the base name after the infix.
        if(plugin_basename MATCHES "(.+)plugin$")
            set(plugin_basename "${CMAKE_MATCH_1}")
        endif()

        # Add a the infix if Qt was configured with one.
        if(QT_LIBINFIX)
            string(APPEND plugin_basename "${QT_LIBINFIX}")
        endif()

        # Add the "plugin" suffix after the infix.
        string(APPEND plugin_basename "plugin")

        # Lowercase the whole thing and use it as the basename of the plugin library.
        string(TOLOWER "${plugin_basename}" plugin_basename)
        set_target_properties(${arg_PLUGIN_TARGET} PROPERTIES
            OUTPUT_NAME "${plugin_basename}"
        )

        get_target_property(export_name ${arg_PLUGIN_TARGET} EXPORT_NAME)
        if(export_name)
            list(APPEND add_qml_module_args
                INSTALLED_PLUGIN_TARGET "${QT_CMAKE_EXPORT_NAMESPACE}::${export_name}"
            )
        else()
            list(APPEND add_qml_module_args
                INSTALLED_PLUGIN_TARGET "${QT_CMAKE_EXPORT_NAMESPACE}::${arg_PLUGIN_TARGET}"
                )
        endif()

        if(NOT arg_PLUGIN_TARGET STREQUAL target)
            get_target_property(lib_type ${arg_PLUGIN_TARGET} TYPE)
            if(lib_type STREQUAL "STATIC_LIBRARY")
                # This is needed so that the dependency on the backing target
                # is included in the plugin's find_package() support.
                # The naming of backing targets and plugins don't typically
                # follow the pattern of other plugins with regard to Private
                # suffixes, so the dependency logic in qt_internal_add_plugin()
                # doesn't find these. For non-static builds, the private
                # dependency doesn't get exposed to find_package(), so we don't
                # have to make the dependency known for that case.
                set_property(TARGET ${arg_PLUGIN_TARGET} APPEND PROPERTY
                    _qt_target_deps "${INSTALL_CMAKE_NAMESPACE}${target}\;${PROJECT_VERSION}"
                )
            endif()
        endif()
    endif()

    # TODO: Check if we need arg_SOURCES in this condition
    if (arg_SOURCES AND NOT arg_TYPEINFO)
        set(arg_TYPEINFO "plugins.qmltypes")
    endif()

    # Pass through options if given (these are present/absent, not true/false)
    foreach(opt IN LISTS qml_module_option_args)
        if(arg_${opt})
            list(APPEND add_qml_module_args ${opt})
        endif()
    endforeach()

    # Pass through single and multi-value args as provided.
    foreach(arg IN LISTS qml_module_single_args qml_module_multi_args)
        if(DEFINED arg_${arg})
            list(APPEND add_qml_module_args ${arg} ${arg_${arg}})
        endif()
    endforeach()

    if (arg_FOLLOW_FOREIGN_VERSIONING)
        message(FATAL_ERROR "Do not set FOLLOW_FOREIGN_VERSIONING for module ${target}. It is already set by default for internal modules.")
    endif()

    # Update the backing and plugin targets with qml-specific things.
    qt6_add_qml_module(${target}
        ${add_qml_module_args}
        __QT_INTERNAL_INSTALL_METATYPES_JSON
        OUTPUT_DIRECTORY ${arg_OUTPUT_DIRECTORY}
        RESOURCE_PREFIX "/qt-project.org/imports"
        OUTPUT_TARGETS output_targets
        FOLLOW_FOREIGN_VERSIONING
    )

    if(output_targets)
        set(plugin_export_targets)
        set(backing_lib_export_targets)

        # Separate which output target should go to which export set.
        # The plugin initializer library should go to the plugin export set, the rest should go to
        # the backing lib export set.
        # In the case when the plugin target is the same as the backing lib target, all of the
        # output targets will go to the plugin export set.

        foreach(output_target IN LISTS output_targets)
            get_target_property(is_plugin_init ${output_target} _is_qt_plugin_init_target)
            if(is_plugin_init)
                list(APPEND plugin_export_targets ${output_target})

                # Plugin initializers associated with an internal module need the internal
                # platform flags.
                qt_internal_link_internal_platform_for_object_library("${output_target}")
            else()
                list(APPEND backing_lib_export_targets ${output_target})
            endif()
        endforeach()

        if(backing_lib_export_targets)
            qt_install(TARGETS ${backing_lib_export_targets}
                EXPORT "${INSTALL_CMAKE_NAMESPACE}${target}Targets"
                DESTINATION "${arg_INSTALL_DIRECTORY}"
            )
            qt_internal_record_rcc_object_files(${target} "${backing_lib_export_targets}"
                INSTALL_DIRECTORY "${arg_INSTALL_DIRECTORY}"
            )

            qt_internal_add_targets_to_additional_targets_export_file(
                TARGETS ${backing_lib_export_targets}
                EXPORT_NAME_PREFIX "${INSTALL_CMAKE_NAMESPACE}${target}"
            )
        endif()

        if(arg_PLUGIN_TARGET AND plugin_export_targets)
            qt_install(TARGETS ${plugin_export_targets}
                EXPORT "${INSTALL_CMAKE_NAMESPACE}${arg_PLUGIN_TARGET}Targets"
                DESTINATION "${arg_INSTALL_DIRECTORY}"
            )

            qt_internal_add_targets_to_additional_targets_export_file(
                TARGETS ${plugin_export_targets}
                EXPORT_NAME_PREFIX "${INSTALL_CMAKE_NAMESPACE}${arg_PLUGIN_TARGET}"
            )
        endif()
    endif()

    if(DEFINED arg_QML_FILES OR DEFINED arg_RESOURCES)
        foreach(resource_file IN LISTS arg_QML_FILES arg_RESOURCES)
            __qt_get_relative_resource_path_for_file(file_resource_path ${resource_file})
            get_filename_component(resource_dir  ${file_resource_path} DIRECTORY)
            get_filename_component(resource_name ${file_resource_path} NAME)
            if(resource_dir)
                set(dest "${arg_INSTALL_DIRECTORY}/${resource_dir}")
            else()
                set(dest "${arg_INSTALL_DIRECTORY}")
            endif()
            qt_install(
                FILES ${resource_file}
                DESTINATION ${dest}
                RENAME ${resource_name}
            )
        endforeach()
    endif()

    if(NOT arg_NO_GENERATE_QMLTYPES)
        qt_install(
            FILES ${arg_OUTPUT_DIRECTORY}/$<TARGET_PROPERTY:${target},QT_QML_MODULE_TYPEINFO>
            DESTINATION "${arg_INSTALL_DIRECTORY}"
        )
    endif()

    if(NOT arg_NO_GENERATE_QMLDIR)
        qt_install(
            FILES ${arg_OUTPUT_DIRECTORY}/qmldir
            DESTINATION "${arg_INSTALL_DIRECTORY}"
        )
    endif()

    if(arg_INSTALL_SOURCE_QMLTYPES)
        set(files ${arg_INSTALL_SOURCE_QMLTYPES})
        if(QT_WILL_INSTALL)
            install(
                FILES ${files}
                DESTINATION "${arg_INSTALL_DIRECTORY}"
            )
        else()
            file(
                COPY ${files}
                DESTINATION "${arg_OUTPUT_DIRECTORY}"
            )
        endif()
    endif()

    if(arg_INSTALL_SOURCE_QMLDIR)
        set(files ${arg_INSTALL_SOURCE_QMLDIR})
        if(QT_WILL_INSTALL)
            install(
                FILES ${files}
                DESTINATION "${arg_INSTALL_DIRECTORY}"
            )
        else()
            file(
                COPY ${files}
                DESTINATION "${arg_OUTPUT_DIRECTORY}"
            )
        endif()
    endif()
endfunction()

# This function is an internal wrapper around qt6_target_compile_qml_to_cpp().
# It sets up some pre-defined Qt-specific arguments when calling the
# qt6_target_compile_qml_to_cpp(). All keywords supported by
# qt6_target_compile_qml_to_cpp() can be used.
#
# Unlike the public command version, the following changes are present:
# - NAMESPACE argument is set to QT_NAMESPACE by default
#
# See qt6_target_compile_qml_to_cpp() for the full set of supported keywords.
function(qt_internal_target_compile_qml_to_cpp target)
    set(option_args "")
    set(single_args NAMESPACE)
    set(multi_args QML_FILES IMPORT_PATHS)
    qt_parse_all_arguments(arg "qt_internal_target_compile_qml_to_cpp"
        "${option_args}"
        "${single_args}"
        "${multi_args}"
        ${ARGN}
    )

    # internal-only logic:
    if(NOT arg_NAMESPACE)
        # NB: assume Qt Core is present. we're in an internal function, so it's
        # a safe enough assumption

        # Apparently, -DQT_NAMESPACE only exists within qtbase or qt5's
        # top-level build. Thus, in many other cases that option is unspecified.
        # To avoid dealing with this, we can query the QT_NAMESPACE for QtCore's
        # compile definitions (because we have it there).
        get_target_property(arg_NAMESPACE ${QT_CMAKE_EXPORT_NAMESPACE}::Core _qt_namespace)
    endif()

    set(target_compile_qml_to_cpp_args "")
    # Pass through options if given (these are present/absent, not true/false)
    foreach(opt IN LISTS option_args)
        if(arg_${opt})
            list(APPEND target_compile_qml_to_cpp_args ${opt})
        endif()
    endforeach()
    # Pass through single and multi-value args as provided.
    foreach(arg IN LISTS single_args multi_args)
        if(DEFINED arg_${arg})
            list(APPEND target_compile_qml_to_cpp_args ${arg} ${arg_${arg}})
        endif()
    endforeach()

    qt6_target_compile_qml_to_cpp(${target}
        ${target_compile_qml_to_cpp_args}
    )

endfunction()