深度剖析qt cmake 的qt_add_qml_module函数

前言

qt_add_qml_module 函数是一个高层次的 CMake 函数,用于创建和管理 QML 模块。它简化了将 QML 代码与 C++ 代码集成以及与其他资源文件集成的过程。这个函数旨在用于 Qt 6 和更高版本的项目。

在调用qt_add_qml_module时,它会执行以下操作:

  1. 创建一个动态链接库——作为QML模块的插件——该库将包含定义自定义QML元素的C++类和其他资源。

2. 源文件列表中指定的源文件将作为静态库编译(),并被链接到QML插件动态库(如果存在)以及其他依赖项之中。您可以通过使用add_library命令来向Qt应用程序/库中添加这些源文件。

  1. 自动创建一个qrc文件,该文件将包含指定源文件中的所有资源,例如图片、样式表和翻译文件。

  2. 生成一个描述该QML模块的JSON文件,其中包含URI、版本号、导出对象名等信息。

  3. 将QML插件动态链接库和描述文件复制到项目二进制目录的特定子目录中。该位置取决于是单个项目还是多个项目,以及是在构建目录还是安装目录中构建应用程序/库。

需要注意的是,qt_add_qml_module是一个高层次的抽象,其内部实现是由诸如qt6_add_qml_module等低层次的CMake宏来完成的。这些底层宏负责执行实际的编译、链接和安装操作,并将相关的属性和配置传递给这些操作。

Qt6QmlMacros.cmake 文件是 Qt 6 框架中的一个 CMake 脚本文件。它包含了 Qt QML 模块的相关 CMake 宏和函数,用于处理 QML 文件和构建 QML 模块。当您在项目中使用 Qt 6 的 QML 功能时,这个文件提供了一些便捷的函数,如 qt_add_qml_moduleqt6_target_qml_sources 等,以简化 QML 项目的构建过程。

简而言之,Qt6QmlMacros.cmake 是 Qt 6 框架提供的一个 CMake 辅助脚本,用于简化 QML 项目的构建和配置。这个文件会在您使用 find_package(Qt6Qml) 或类似命令时被包含在您的 CMake 项目中。

参数

qt_add_qml_module 函数提供了多个参数,以便您在创建 QML 模块时进行配置。以下是按功能分类的参数列表:

  1. 模块基本信息:
    • TARGET:目标名称,用于指定模块关联的可执行文件或库。
    • URI:模块的 URI,用于在 QML 代码中 import。
    • VERSION:模块的版本号。
  2. QML 文件和资源:
    • QML_FILES:QML 文件列表,包括所有要编译到模块中的 QML 文件。
    • SOURCES:源文件列表,包括所有要编译到模块中的源代码文件和资源文件。可以包含 QML 文件。
  3. 其他资源文件:
    • RESOURCE_FILES:要包含在模块中的其他资源文件,例如图片、音频等。
    • STATIC_RESOURCE_FILES:静态资源文件,类似于 RESOURCE_FILES,但会嵌入到二进制文件中。
    • RESOURCE_PREFIX:资源文件的前缀路径。
  4. 输出设置:
    • OUTPUT_DIRECTORY:模块输出的目录。
    • DEBUG_OUTPUT_DIRECTORY:模块在调试模式下的输出目录。
    • RELEASE_OUTPUT_DIRECTORY:模块在发布模式下的输出目录。
  5. 其他选项:
    • INSTALL_QMLDIR:用于指定安装模块时的 QML 目录。
    • NO_PLUGIN_OPTION:禁用生成插件文件的选项。

这些参数可以根据您的需求进行选择和配置,以便为您的项目创建适当的 QML 模块。

参数的底层调用

qt_add_qml_module中,以下是参数及其相关调用:

  1. TARGET:这个参数定义了目标名称,它是CMake中创建的目标的名称。这个参数不会直接调用其他函数或宏。
  2. URI:定义了QML模块的唯一标识符。它用于生成模块的描述文件,例如.qmltypes文件。这个参数不会直接调用其他函数或宏。
  3. VERSION:定义了QML模块的版本号。它用于生成模块的描述文件,例如.qmltypes文件。这个参数不会直接调用其他函数或宏。
  4. QML_FILES:这个参数用于指定QML文件列表。它会传递给qt6_target_qml_sources宏,以便将QML文件添加到目标中并在构建过程中正确处理。
  5. SOURCES:这个参数用于指定源文件列表,通常包括C++源文件。这些源文件会被传递给底层的target_sources函数,以便将这些源文件添加到目标中。
  6. RESOURCES:这个参数用于指定资源文件列表,例如图片、样式表和翻译文件。这些资源文件会传递给qt6_target_qml_resources宏,以便将它们添加到目标中并在构建过程中正确处理。
  7. PLUGIN_TARGET:这个参数定义了插件目标的名称。这个参数不会直接调用其他函数或宏。
  8. OUTPUT_DIRECTORY:这个参数用于指定构建输出目录。这个参数不会直接调用其他函数或宏。
  9. INSTALL_DIRECTORY:这个参数用于指定安装输出目录。这个参数不会直接调用其他函数或宏。

这些参数在qt_add_qml_module内部通过调用底层的CMake函数和Qt宏来处理和实现所需的功能。

模块基本信息

从源码角度出发,我们来看看 qt_add_qml_module 中的以下参数:

  1. TARGET:此参数指定了与 QML 模块关联的可执行文件或库。在 qt_add_qml_module 函数内部,TARGET 会用于设置目标属性,以便将 QML 模块正确链接到目标可执行文件或库。在函数内部,TARGET 参数被用于调用 qt6_add_qml_module 函数,这是一个实际处理 QML 模块编译、链接和资源管理的底层函数。
  2. URI:此参数指定了 QML 模块的 URI,以便在 QML 代码中使用 import 语句导入。URI 参数会传递给 qt6_add_qml_module 函数,该函数会创建一个描述 QML 模块的 JSON 文件。JSON 文件中的 URI 信息在运行时用于解析 QML 模块,确保 QML 模块被正确地导入和实例化。
  3. VERSION:此参数用于指定 QML 模块的版本号。VERSION 参数与 URI 类似,会传递给 qt6_add_qml_module 函数。在 JSON 文件中,版本信息会与模块的 URI 一起存储。在运行时,版本信息用于确保正确的模块版本被加载,从而避免因为使用了错误版本的模块而导致的潜在问题。

这些参数都会传递给 qt6_add_qml_module 函数,该函数负责处理 QML 模块的具体实现,包括编译、链接和资源管理。qt_add_qml_module 本身是一个更高层次的抽象,用于简化 QML 模块的创建和管理。

QML 文件和资源

qt_add_qml_module 函数的 SOURCESQML_FILES 参数都用于指定 QML 模块中的 QML 文件。然而,它们之间有一些区别:

  1. SOURCES 参数:这个参数用于指定 QML 模块的源文件(包括 QML 文件和其他相关资源文件)。当您使用这个参数时,需要提供源文件的相对路径(相对于 CMAKE_CURRENT_SOURCE_DIR)。这些文件会被拷贝到构建目录中的特定位置,并在构建过程中进行处理。
  2. QML_FILES 参数:这个参数仅用于指定 QML 模块的 QML 文件。您可以为这个参数提供相对路径或绝对路径。在构建过程中,这些文件会被拷贝到构建目录的特定位置,并进行处理。但是,与 SOURCES 参数不同,QML_FILES 参数不包括其他资源文件,仅包括 QML 文件。

在大多数情况下,您可能只需要使用 QML_FILES 参数。但是,如果您的项目中包含除 QML 文件之外的其他资源文件,您可能需要使用 SOURCES 参数。不过,请注意,当使用 SOURCES 参数时,需要确保文件路径是相对于 CMAKE_CURRENT_SOURCE_DIR 的相对路径。

解析QML_FILES 参数内部机制

当您调用qt_add_qml_module时,它会在内部调用qt6_target_qml_sources。具体来说,qt_add_qml_module的实现如下:

macro(qt_add_qml_module target)
    set(options)
    set(oneValueArgs URI VERSION)
    set(multiValueArgs QML_FILES PREFER_SHARED)
    cmake_parse_arguments(_args "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

    ...

    qt6_target_qml_sources(${target} ${_args_QML_FILES})
    ...
endmacro()

从这段代码可以看出,qt_add_qml_module获取QML_FILES参数(即${QML_RELATIVE_FILES}),并将其传递给qt6_target_qml_sources。所以,您所说的${QML_RELATIVE_FILES}参数作为qt6_target_qml_sources的参数是正确的。

接下来,让我们看一下qt6_target_qml_sources函数的部分实现:

function(qt6_target_qml_sources target)
    set(options)
    set(oneValueArgs)
    set(multiValueArgs FILES)
    cmake_parse_arguments(_args "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

    ...

    foreach(_file IN LISTS _args_FILES)
        get_filename_component(_file_abs "${_file}" ABSOLUTE)
        ...
    endforeach()

    ...
endfunction()

qt6_target_qml_sources函数接受一个FILES参数,它是传递给qt_add_qml_moduleQML_FILES参数。在函数内部,它会遍历_args_FILES列表,即我们传递给它的QML文件路径列表。对于每个文件,它会获取绝对路径,然后执行一些操作。

所以,对于qt_add_qml_moduleqt6_target_qml_sources,它们按文件分别处理QML文件列表,而不是将整个列表作为单个参数传递给qt6_target_qml_sources。它们遍历QML文件列表,然后针对每个文件执行操作。

解析SOURCES 参数内部机制

SOURCES参数实际上是直接传递给target_sources函数。qt_add_qml_module中的SOURCES参数用于指定源文件列表,通常包括C++源文件。这些源文件会被传递给底层的target_sources函数,以便将这些源文件添加到目标中。

以下是qt_add_qml_module宏的一部分源代码,用于处理SOURCES参数:

if(SOURCES)
    target_sources(${target} PRIVATE ${SOURCES})
endif()

在这段代码中,您可以看到SOURCES参数直接传递给了target_sources函数,该函数将指定的源文件添加到目标中。

如果SOURCES参数中包含了QML文件,target_sources不会自动调用qt6_target_qml_resourcestarget_sources函数仅用于将源文件添加到目标中,它不会对文件类型进行特殊处理。

如果你需要将QML文件作为资源添加到项目中,你应该使用QML_FILES参数。将QML文件添加到SOURCES参数中可能会导致问题,因为这些文件不会被正确处理为QML资源。

qt_add_qml_module中,QML_FILES参数用于指定QML文件,这些文件将被正确处理并嵌入到生成的二进制文件中。

SOURCESQML_FILES 内部实现差异

qt_add_qml_module 函数中,SOURCES 参数用于指定源代码文件(如C++源代码),而 QML_FILES 参数用于指定QML文件。这两个参数在处理过程中有所不同:

  1. 使用 QML_FILES 参数:

    当您使用 QML_FILES 参数时,qt_add_qml_module 会将其作为一个包含所有 QML 文件路径的列表。函数内部会将这个列表传递给 qt6_target_qml_sources,确保 QML 文件被编译为可执行文件或库的一部分,并在运行时可用。

  2. 使用 SOURCES 参数:

    当您使用 SOURCES 参数时,qt_add_qml_module 会将其作为一个包含所有源代码文件(如C++源代码)的列表。SOURCES 参数不应该包含QML文件,因为它不会将这些文件作为资源处理。在这种情况下,您应该使用 QML_FILES 参数来指定QML文件。

总之,在 qt_add_qml_module 函数中,QML_FILES 参数用于处理QML文件,而 SOURCES 参数用于处理其他类型的源代码文件。为了确保QML文件被正确处理,您应该使用 QML_FILES 参数来指定它们。



其他资源文件

qt_add_qml_module 函数中,不同参数的存在是为了提供更多灵活性和控制。尽管 SOURCES 参数可以用来指定源代码文件,但它并不是专门用于处理资源文件(如图片、音频等)的。使用特定的资源参数可以让您更清晰地组织项目,并明确指定资源文件及其用途。

  • RESOURCE_FILES:此参数允许您将资源文件(如图片、音频等)明确地与源代码文件分开。这有助于组织项目并提高代码可读性。
  • STATIC_RESOURCE_FILES:与 RESOURCE_FILES 类似,此参数用于指定静态资源文件。将资源文件嵌入到二进制文件中可以确保这些资源在部署时始终可用。嵌入资源还可以简化部署过程,因为您不需要担心将外部资源文件与应用程序一起分发。
  • RESOURCE_PREFIX:此参数为资源文件提供了一个前缀路径。使用前缀路径可以帮助您更好地组织资源,并确保在运行时可以正确访问这些资源。

总之,这些参数为项目提供了更多的组织灵活性。虽然您可以使用 SOURCES 参数来指定资源文件,但使用专门的资源参数可以帮助您更清晰地组织项目,并更好地控制资源文件的处理。

输出设置

qt_add_qml_module 函数中,以下参数用于控制模块输出的位置:

  • OUTPUT_DIRECTORY:用于指定模块输出的目录。当您没有为 DEBUG_OUTPUT_DIRECTORYRELEASE_OUTPUT_DIRECTORY 分别指定调试和发布模式下的输出目录时,此参数将作为默认值。
  • DEBUG_OUTPUT_DIRECTORY:用于指定模块在调试模式下的输出目录。如果设置了此参数,它将优先于 OUTPUT_DIRECTORY 参数。
  • RELEASE_OUTPUT_DIRECTORY:用于指定模块在发布模式下的输出目录。如果设置了此参数,它将优先于 OUTPUT_DIRECTORY 参数。

qt_add_qml_module 函数内部,这些参数将传递给底层宏 qt6_target_qml_sources,它会处理模块输出目录的设置。源码中的以下部分演示了这些参数是如何应用的:

set_property(TARGET ${target} PROPERTY
    QT_QML_MODULE_INSTALL_QML_OUTPUT_DIRECTORY "${ARGN_OUTPUT_DIRECTORY}")
set_property(TARGET ${target} PROPERTY
    QT_QML_MODULE_INSTALL_QML_DEBUG_OUTPUT_DIRECTORY "${ARGN_DEBUG_OUTPUT_DIRECTORY}")
set_property(TARGET ${target} PROPERTY
    QT_QML_MODULE_INSTALL_QML_RELEASE_OUTPUT_DIRECTORY "${ARGN_RELEASE_OUTPUT_DIRECTORY}")

这些 set_property 调用用于将模块输出目录的相关参数与目标关联。这些属性随后将在安装步骤(如 install() 命令)中使用,以确保模块在构建过程中输出到正确的位置。

在调试和发布模式下,通过设置 DEBUG_OUTPUT_DIRECTORYRELEASE_OUTPUT_DIRECTORY,您可以更好地控制模块输出目录,从而组织项目和构建过程。

其他选项

从源码角度来看,INSTALL_QMLDIRNO_PLUGIN_OPTION 这两个参数在 qt_add_qml_module 函数中的作用如下:

  1. INSTALL_QMLDIR 参数:

INSTALL_QMLDIR 参数用于指定在安装 QML 模块时使用的 QML 目录。当您使用 qt_add_qml_module 函数创建一个 QML 模块时,通常会将生成的 QML 插件和相关资源文件安装到特定的目录。这个目录通常位于 Qt 安装目录下的 qml 子目录中。

INSTALL_QMLDIR 参数允许您自定义安装目录,而不是使用默认的 QML 目录。这在某些特殊情况下可能非常有用,比如在需要与其他已安装的 QML 模块共享资源或避免目录冲突时。

qt_add_qml_module 函数的内部实现中,INSTALL_QMLDIR 参数会影响到生成的 CMake 安装命令。如果提供了自定义的 INSTALL_QMLDIR,则 CMake 会将 QML 插件和相关资源文件安装到指定的目录,而不是默认的 QML 目录。

  1. NO_PLUGIN_OPTION 参数:

NO_PLUGIN_OPTION 参数用于控制是否生成 QML 插件。当使用 qt_add_qml_module 函数时,默认情况下会为模块生成一个 QML 插件。这个插件是一个动态链接库,包含了自定义 QML 元素的 C++ 类和其他资源。

在某些情况下,您可能不希望生成 QML 插件。例如,当您的项目仅使用纯 QML 代码,而不包含任何 C++ 类时,生成插件可能没有意义。在这种情况下,您可以使用 NO_PLUGIN_OPTION 参数来禁用插件生成。

qt_add_qml_module 函数的内部实现中,NO_PLUGIN_OPTION 参数会影响到插件生成的条件判断。如果设置了 NO_PLUGIN_OPTION,则函数内部的相关逻辑会跳过插件的生成过程,只处理 QML 和资源文件。

猜你喜欢

转载自blog.csdn.net/qq_21438461/article/details/130475251