CMake教程系列-11-toolchain及重载

CMake教程系列-11-toolchain及重载

简介

cmake支持使用toolchain(工具链),大家应该有些toolchain这个概念。我们可以编写toolchain并提供给用户以自动的设置或应用一些功能。

编译链

其实cmake自带一些toolchain,我们在使用默认设置时(传入Generator)时就已经在用了。其位置在 cmake/share/cmake-<VERSION>/Modules/Platform 中。我们可以发现这个目录中包含了各个平台及各种编译器匹配的一些cmake MODULE文件(<PLATFORM>-<COMPILER>.cmake)。

打开其中一个文件,我们可以看到其中设置了一些cmake的默认变量。其中重要的有:

  • CMAKE_<LANG>_COMPILER
  • CMAKE_<LANG>_FLAGS*
  • CMAKE_*_FLAG
  • CMAKE_<LANG>_STANDARD
  • CMAKE_AR
  • *_VERSION
  • CMAKE_*_PATH
  • CMAKE_*_PREFIX
  • CMAKE_*_SUFFIX

我们可以发现,这些设置是针对以下几点的:

  1. 编译器
  2. 编译器默认选项
  3. 编译器版本
  4. 语言标准
  5. 各种路径
  6. 生成文件前缀
  7. 生成文件后缀

这也就是为什么我们不需要设置什么就可以直接找到并使用编译器的原因。

我们完全可以编写一套针对于自定义编译器的规则,并提供给下游使用。这就是toolchain的第一种实现。

值得一提的是,cmake支持交叉编译。所以在这些变量中,我们可以发现一些包含“HOST”的变量及一些包含“TARGET”的变量。

所有相关的选项可在官方文档中查阅: cmake.org/cmake/help/la


功能链

当然,有时候我们需要做的并没有这么多:可能是提供一种feature,可能是优化某些函数。此时,我们也可以将其cmake代码保存为toolchain并向下游提供。这时toolchain的第二种实现。

我们在这里以vcpkg toolchain当做示例讨论:

scripts/buildsystems/vcpkg.cmake · JackBoosY/vcpkg - Gitee.com

我们可以看到,toolchain中设置了一些选项,以供用户使用。并使用了一些特殊的用法例如:

...
function(add_library)
    z_vcpkg_function_arguments(ARGS)
    _add_library(${ARGS})
    set(target_name "${ARGV0}")

    list(FIND ARGS "IMPORTED" IMPORTED_IDX)
    list(FIND ARGS "INTERFACE" INTERFACE_IDX)
    list(FIND ARGS "ALIAS" ALIAS_IDX)
    if(IMPORTED_IDX EQUAL -1 AND INTERFACE_IDX EQUAL -1 AND ALIAS_IDX EQUAL -1)
        get_target_property(IS_LIBRARY_SHARED "${target_name}" TYPE)
        if(VCPKG_APPLOCAL_DEPS AND Z_VCPKG_TARGET_TRIPLET_PLAT MATCHES "windows|uwp" AND (IS_LIBRARY_SHARED STREQUAL "SHARED_LIBRARY" OR IS_LIBRARY_SHARED STREQUAL "MODULE_LIBRARY"))
            z_vcpkg_set_powershell_path()
            add_custom_command(TARGET "${target_name}" POST_BUILD
                COMMAND "${Z_VCPKG_POWERSHELL_PATH}" -noprofile -executionpolicy Bypass -file "${Z_VCPKG_TOOLCHAIN_DIR}/msbuild/applocal.ps1"
                    -targetBinary "$<TARGET_FILE:${target_name}>"
                    -installedDir "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}$<$<CONFIG:Debug>:/debug>/bin"
                    -OutVariable out
            )
        endif()
        set_target_properties("${target_name}" PROPERTIES VS_USER_PROPS do_not_import_user.props)
        set_target_properties("${target_name}" PROPERTIES VS_GLOBAL_VcpkgEnabled false)
    endif()
endfunction()
...

add_library 不是cmake提供的函数用来声明一个库吗?为什么这里还能定义一个相同名称的函数?

还有,为什么里面会调用以“_”开头的相同名称函数?这么写是什么意思?

这就是cmake的重载功能:我们可以重载任意已有的函数,来实现特殊功能:

我们可以再次声明已知函数,并使用包含前缀“_”的相同函数名来调用原始函数。

在上面的例子中,vcpkg重载了 add_library 函数,并在调用原始 add_library 之后执行了一个自定义命令。其命令内容为通过 powershell 脚本来分析此库需要使用的依赖,并执行自动复制到安装路径下。


我相信,当你从“使用者”变为“提供者”时,toolchain是你最佳的实现方式。


以上内容来源为官方文档: cmake-toolchains(7) - CMake 3.23.0 Documentation


在最后一篇内容中,我将介绍cmake policy。各位下篇见。

编辑于 2022-04-01 22:36