You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用CMake基于生成的配置文件构建两个差异化共享库?

问题描述

我想要构建两个仅代码存在细微差异的共享库,计划通过CMake生成名为ENGINE_TYPE的头文件变量,分别设为0和1来构建foo和foo1库。但发现两个库最终都使用了foo1对应的代码逻辑,怀疑是CMake先生成ENGINE_TYPE为0的config.h后,又被ENGINE_TYPE为1的版本覆盖,导致编译时两个库都使用了后者的配置。请问该方案是否可行?若可行,该如何解决?

给出的CMake代码如下:

# Global Definitions
set(${PROJECT_NAME}_VERSION_MAJOR 0)
set(${PROJECT_NAME}_VERSION_MINOR 1)
set(${PROJECT_NAME}_VERSION_PATCH 0)

# Function to configure and build a library
function(configure_and_build_library TARGET_NAME ENGINE_TYPE)
    set(${PROJECT_NAME}_SOURCES src/lib/src/my.cpp)
    set(engineType ${ENGINE_TYPE})
    configure_file(include/config.h.in ${CMAKE_BINARY_DIR}/config.h @ONLY)
    add_library(${TARGET_NAME} SHARED ${${PROJECT_NAME}_SOURCES})
    target_compile_definitions(${TARGET_NAME} PRIVATE engineType=${engineType})
    set_target_properties(${TARGET_NAME} PROPERTIES VERSION ${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH} SOVERSION ${${PROJECT_NAME}_VERSION_MAJOR})
    target_link_libraries(${TARGET_NAME} ${OPENSSL_CRYPTO_LIBRARY})
endfunction()

# Build valeoengine with engineType 0
set(engineType 0)
configure_and_build_library(foo ${engineType})

# Build valeodecryptengine with engineType 1
set(engineType 1)
configure_and_build_library(foo1 ${engineType})

# Install project
if(NOT SKIP_INSTALL_ALL)
    install(TARGETS foo foo1 LIBRARY DESTINATION lib)
endif()
问题分析

这个方案完全可行,核心问题出在configure_file的输出路径:两次调用函数时,都将生成的config.h输出到同一个全局路径${CMAKE_BINARY_DIR}/config.h,第二次生成的文件会直接覆盖第一次的。最终两个库编译时都会读取最后生成的、对应ENGINE_TYPE=1的配置文件,导致逻辑一致。

另外你在函数里同时用了configure_file生成头文件,又通过target_compile_definitions定义了engineType变量——如果代码里同时引用这两个地方的变量,也可能导致逻辑混乱,需要统一配置方式。

解决方案

有两种可靠的解决方式,可根据需求选择:

方式一:为每个库生成独立的config.h(保留头文件生成方式)

修改configure_file的输出路径,为每个目标库创建单独的子目录存放对应的config.h,避免文件覆盖。同时让每个库只包含自己对应的头文件目录:

# Global Definitions
set(${PROJECT_NAME}_VERSION_MAJOR 0)
set(${PROJECT_NAME}_VERSION_MINOR 1)
set(${PROJECT_NAME}_VERSION_PATCH 0)

# Function to configure and build a library
function(configure_and_build_library TARGET_NAME ENGINE_TYPE)
    set(${PROJECT_NAME}_SOURCES src/lib/src/my.cpp)
    # 为每个目标创建单独的配置目录
    set(CONFIG_DIR ${CMAKE_BINARY_DIR}/config/${TARGET_NAME})
    file(MAKE_DIRECTORY ${CONFIG_DIR})
    # 生成对应目标的config.h到专属目录
    set(ENGINE_TYPE ${ENGINE_TYPE})
    configure_file(include/config.h.in ${CONFIG_DIR}/config.h @ONLY)
    
    add_library(${TARGET_NAME} SHARED ${${PROJECT_NAME}_SOURCES})
    # 让目标库只包含自己的配置目录
    target_include_directories(${TARGET_NAME} PRIVATE ${CONFIG_DIR})
    set_target_properties(${TARGET_NAME} PROPERTIES VERSION ${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH} SOVERSION ${${PROJECT_NAME}_VERSION_MAJOR})
    target_link_libraries(${TARGET_NAME} ${OPENSSL_CRYPTO_LIBRARY})
endfunction()

# Build foo with ENGINE_TYPE 0
configure_and_build_library(foo 0)

# Build foo1 with ENGINE_TYPE 1
configure_and_build_library(foo1 1)

# Install project
if(NOT SKIP_INSTALL_ALL)
    install(TARGETS foo foo1 LIBRARY DESTINATION lib)
endif()

注:这里将函数内的变量名统一为ENGINE_TYPE,和头文件中的变量名对应,避免混淆。

方式二:直接用编译定义替代头文件生成(更简洁)

既然已经尝试用target_compile_definitions,可以直接去掉configure_file步骤,在代码中通过预编译宏区分逻辑,完全避免文件覆盖问题:

第一步:修改代码逻辑

在my.cpp中直接使用预编译宏ENGINE_TYPE,示例:

#ifdef ENGINE_TYPE
    if (ENGINE_TYPE == 0) {
        // foo库的逻辑
    } else if (ENGINE_TYPE == 1) {
        // foo1库的逻辑
    }
#endif

第二步:修改CMake代码

# Global Definitions
set(${PROJECT_NAME}_VERSION_MAJOR 0)
set(${PROJECT_NAME}_VERSION_MINOR 1)
set(${PROJECT_NAME}_VERSION_PATCH 0)

# Function to configure and build a library
function(configure_and_build_library TARGET_NAME ENGINE_TYPE)
    set(${PROJECT_NAME}_SOURCES src/lib/src/my.cpp)
    
    add_library(${TARGET_NAME} SHARED ${${PROJECT_NAME}_SOURCES})
    # 直接为目标定义专属的预编译宏
    target_compile_definitions(${TARGET_NAME} PRIVATE ENGINE_TYPE=${ENGINE_TYPE})
    set_target_properties(${TARGET_NAME} PROPERTIES VERSION ${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH} SOVERSION ${${PROJECT_NAME}_VERSION_MAJOR})
    target_link_libraries(${TARGET_NAME} ${OPENSSL_CRYPTO_LIBRARY})
endfunction()

# Build foo with ENGINE_TYPE 0
configure_and_build_library(foo 0)

# Build foo1 with ENGINE_TYPE 1
configure_and_build_library(foo1 1)

# Install project
if(NOT SKIP_INSTALL_ALL)
    install(TARGETS foo foo1 LIBRARY DESTINATION lib)
endif()

这种方式不需要生成额外头文件,更简洁高效,也不会有文件覆盖风险。


内容的提问来源于stack exchange,提问作者mohamed Elmodather

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 19:32:32