使用带有 check_c_source_runs() 或 try_run() 的接口库

Using an interface library with check_c_source_runs() or try_run()

用例:我正在尝试编译一个测试程序,该程序使用 SDL2_ttf(使用 SDL2、Freetype、PNG 和 Zlib)探测 TrueType(tm) 字体列表。 SDL2_ttf::SDL2_ttf 接口库存在并成功链接到目标可执行文件。我的问题是如何让 check_c_source_runs() 获取定义,包括目录和库。我宁愿不必手动从属性中提取所有内容,如以下代码片段所示:

include(CheckCSourceRuns)

get_property(defs TARGET SDL2_ttf::SDL2_ttf PROPERTY INTERFACE_COMPILE_DEFINITIONS)
get_property(incs TARGET SDL2_ttf::SDL2_ttf PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
get_property(libs TARGET SDL2_ttf::SDL2_ttf PROPERTY INTERFACE_LINK_LIBRARIES)

## Transform the definitions with "-D"
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.12")
    list(TRANSFORM defs PREPEND "-D")
    list(TRANSFORM incs PREPEND "-I")
else ()
    ## Code that does what list(TRANSFORM...) does in less capable CMake
    ## versions.
endif ()

set(CMAKE_REQUIRED_DEFINITIONS ${defs})
set(CMAKE_REQUIRED_INCLUDES    ${incs})
set(CMAKE_REQUIRED_LIBRARIES   ${libs})
check_c_source_runs("
#include <stdint.h>
#include <SDL.h>
#include <SDL_ttf.h>
int main(int argc, char *argv[])
{
    const char *fonts[] = {\"DejaVuSans.ttf\", \"LucidaSansRegular.ttf\", \"FreeSans.ttf\", \"AppleGothic.ttf\", \"tahoma.ttf\"};
    size_t i, cnt = 0;
    SDL_Init(SDL_INIT_VIDEO);
    TTF_Init();
    for (i = 0; i < sizeof(fonts)/sizeof(fonts[0]); ++i) {
        TTF_Font *ttf = TTF_OpenFont(fonts[i], 10);
        if (ttf != NULL) {
            fputs(fonts[i], stderr);
            if (cnt++ > 0) {
                fputc(';', stderr);
            }
            TTF_CloseFont(ttf);
        }
    }
    TTF_Quit();
    SDL_Quit();
    return 0;
}" ttfprobe_run)

Link 库很复杂,因为在 SDL2_ttf::SDL2_ttf 中引用了接口库,例如FreeType::FreeType.

建议?

函数 try_compiletry_run 以及基于它们的所有内容(例如 check_c_source_runs)实际上是 构建其他一些 CMake 项目。因为不能给CMake工程传targets,所以有两种方式:

  1. 将所有需要的目标属性提取到变量中,并将它们传递给新生成的项目。正如你已经做的那样。

  2. 手动为其他项目编写CMakeLists.txt,并在其中调用find_package和其他包发现函数。

例如,您可以为其他项目编写 CMakeLists.txt

# Source file is in SOURCE_FILE parameter,
# resulted executable is copied into the file pointed by EXE_FILE parameter.
cmake_minimum_required(...)
project(test_project)

# This will create 'SDL2_ttf::SDL2_ttf' target
find_package(SDL2_ttf REQUIRED)

add_executable(test_exe ${SOURCE_FILE})
target_link_libraries(test_exe SDL2_ttf::SDL2_ttf)

add_custom_command(OUTPUT ${EXE_FILE}
    COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:test_exe> ${EXE_FILE}
    DEPENDS $<TARGET_FILE:test_exe>
)

add_custom_target(copy_exe ALL DEPENDS ${EXE_FILE})

主要挑战是根据需要将尽可能多的变量传递给其他项目,以便在与主项目相同的 "environment" 中构建它。

下面的示例仅处理可能影响 find_package(SDL2_ttf) 调用的变量:

# Main project
# Somewhere you have this call too.
find_package(SDL2_ttf REQUIRED)

# List of arguments for the subproject
set(SUBPROJECT_ARGS
    # This affects on searching for possible `FindSDL2_ttf.cmake` script
    -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}
    # This affects on searching for `find_*` calls in find script.
    -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}
)

if (SDL2_ttf_DIR)
    # This is a directory with `SDL2_ttfConfig.cmake` script
    list(APPEND SUBPROJECT_ARGS -DSDL2_ttf_DIR=${SDL2_ttf_DIR})
endif()

# build subproject
try_compile(TTF_TEST_RESULT # Variable which will contain result of building the subproject
 ${CMAKE_CURRENT_BINARY_DIR}/ttf_test # Build directory for the subproject
 <src-dir> # Source directory for the subproject, where its `CMakeLists.txt` resides.
 test_project # Project name of the subproject
 CMAKE_FLAGS
 -DSOURCE_FILE=<src-file> # Source file 
 -DEXE_FILE=<exe-file> # Path to the resulted executable file
 ${SUBPROJECT_ARGS} # The rest of arguments for subproject
 OUTPUT_VAR TTF_TEST_OUTPUT # Variable which will contain output of the build process
)

if (TTF_TEST_RESULT)
  # Subproject has been built successfully, now we can try to execute resulted file
  ...
endif()

棘手?是的。但这就是 CMake 的工作方式...