使用支持所有 vcpkg/apt-get/brew 的 cmake 添加 FreeType 依赖项

Add FreeType dependency with cmake supporting all vcpkg/apt-get/brew

我有一个项目依赖于 FreeType,并使用 CMake 作为构建系统。 CMake 有一个 FindFreeType built-in module which is supposed to be used like this, see for example this other SO question:

find_package(Freetype REQUIRED)
target_link_libraries(mylib ${FREETYPE_LIBRARIES})
target_include_directories(mylib PRIVATE ${FREETYPE_INCLUDE_DIRS})

从 CMake 3.10 开始,还有 Freetype::Freetype 导入目标,因此我们可以避免 target_include_directories:

find_package(Freetype REQUIRED)
target_link_libraries(mylib Freetype::Freetype)

这在 Ubuntu 18.04 上运行良好,通过 apt install libfreetype6-dev 安装了 FreeType。我假设当通过自制程序安装软件包时它也适用于 macOS(我还没有测试过)。

但是,在 Windows,我希望允许开发人员依赖安装了 vcpkg 的 FreeType:

git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg integrate install
.\vcpkg install freetype:x64-windows

他们将通过 运行 以下 CMake 命令定位:

cmake .. -G "Visual Studio 15 2017" -A x64 -DCMAKE_TOOLCHAIN_FILE=C:/Users/Boris/vcpkg/scripts/buildsystems/vcpkg.cmake

不幸的是,上面的 CMake 命令对这个问题开头的两个 CMakeLists.txt 中的任何一个都不起作用,因为在安装 FreeType 时找到和 link 的正确方法通过 vcpkg 如下:

find_package(freetype CONFIG REQUIRED) # `Freetype` works too, but vcpkg doc recommends `freetype`
target_link_libraries(mylib freetype)  # Here, all-lowercase is required

特别是,vcpkg 提供的 freetype-config.cmake 配置文件定义了目标 freetype(不像内置查找模块那样 Freetype::Freetype),并且没有定义任何 FREETYPE_LIBRARIESFREETYPE_INCLUDE_DIRS 个变量。

什么是使我的 CMakeLists.txt 与 "traditional" 查找 FreeType 的方法以及 vcpkg 兼容的正确方法?

假设是 CMake 3.10 之前的版本,我正在考虑以下内容:

if(DEFINED VCPKG_TARGET_TRIPLET)
    find_package(freetype CONFIG REQUIRED)
    set(FREETYPE_LIBRARIES freetype)
    set(FREETYPE_INCLUDE_DIRS "")
else()
    find_package(Freetype REQUIRED)
endif()
target_link_libraries(mylib ${FREETYPE_LIBRARIES})
target_include_directories(mylib PRIVATE ${FREETYPE_INCLUDE_DIRS})

这看起来是个好习惯吗?有更好的主意吗?

感觉很难看,此外,开发人员总是有可能希望将 vcpkg 用于某些其他依赖项而不是 FreeType(例如,显式提供 FREETYPE_DIR),所以这个技巧不会'在所有情况下都不够,我们需要另一个 CMake 选项,如 MYLIB_IGNORE_VCPKG_FREETYPE,它开始变得更丑陋。

Would that seem like good practice? Any better idea?

不要对可能的包管理器一无所知。

执行以下操作:

find_package(Freetype CONFIG) # should find freetype-config.cmake if available
find_package(Freetype REQUIRED) # Will not be executed if Freetype_FOUND ist already set
# if you do not want two find_package calls consider using CMAKE_FIND_PACKAGE_PREFER_CONFIG

然后测试目标Freetype::Freetypefreetype是否存在

 if(TARGET freetype AND NOT TARGET Freetype::Freetype)
     add_library(Freetype::Freetype ALIAS freetype) # target freetype is defined by freetype-targets.cmake
     # might need to add freetype to global scope if cmake errors here
     # alternativly if the above does not work for you you can use
     # add_library(Freetype::Freetype INTERFACE IMPORTED)
     # target_link_libraries(Freetype::Freetype INTERFACE freetype)
 endif()
 if(NOT TARGET Freetype::Freetype)
     # insert error here
     # or create the target correctly (see cmakes newer FindFreetype.cmake)
 endif()
 target_link_libraries(mylib PRIVATE Freetype::Freetype)

如果您不想为目标添加别名,您还可以定义一个名为 FREETYPE_TARGET 的变量,并将其设置为正确的链接目标。

在旧版本的vcpkg中(<2020年1月,参见vcpkg#9311),安装Freetype时曾经有过如下信息:

The package freetype is compatible with built-in CMake targets:

    find_package(Freetype REQUIRED)
    target_link_libraries(main PRIVATE Freetype::Freetype)

在当前版本中,他们改为推荐 find_package(freetype CONFIG REQUIRED),这确保配置包 vcpkg/<...>/freetype-config.cmake 优先于 CMake 的 built-in 模块包 FindFreetype.cmake.

但是,使用 built-in 模块包仍然可以正常工作:它将找到 vcpkg-installed 库,并定义一个 Freetype::Freetype 目标(如果 CMake >= 3.10)而不是一个 freetype 目标。

请注意,默认情况下,find_package(Freetype REQUIRED) 首先搜索模块包,然后搜索配置包。但是,用户可以设置 CMAKE_FIND_PACKAGE_PREFER_CONFIG,这将改为使用配置包。因此,确保定义 Freetype::Freetype 的可靠方法是执行以下操作:

find_package(Freetype MODULE REQUIRED)
target_link_libraries(mylib Freetype::Freetype)

但是,如果 CMake < 3.10,这仍然没有定义 Freetype::Freetype 目标。在这种情况下,一种选择是在您的 CMAKE_MODULE_PATH.

中提供一份更新版本的 FindFreetype.cmake

为了更加稳健,您甚至可以将其称为 Find<Mylib>Freetype.cmake,在不太可能的情况下,您的 CMakeLists.txt 被用作另一个项目的子目录,该项目也修改了 CMAKE_MODULE_PATH 并提供不兼容的 FindFreetype.cmake.