如何用CMake将Git子模块依赖打包进单个静态.a库?
问题:将SDL依赖与自研代码打包为单个静态库
我正在开发一款基于C语言的图形库Pixelinator,该库依赖SDL2及其生态组件(SDL-TTF、SDL-Mixer、SDL-Image)。已将这些依赖作为Git子模块放入项目的external目录,通过CMakeLists.txt配置构建静态库,但生成的.a文件仅包含自研代码,未纳入SDL相关依赖。现咨询如何通过CMake将所有SDL依赖与自研代码打包为单个静态.a文件。
现有CMake配置代码
external目录下的CMakeLists.txt
set( BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE ) # SDL set( SDL_LIBC ON CACHE BOOL "" FORCE ) set( SDL_TEST OFF CACHE BOOL "" FORCE ) add_subdirectory(SDL) target_link_libraries( Pixelinator SDL2::SDL2main SDL2::SDL2-static ) # SDL_image set( SDL2IMAGE_SAMPLES OFF CACHE BOOL "" FORCE ) set( SDL2IMAGE_INSTALL OFF CACHE BOOL "" FORCE ) set( SDL2IMAGE_VENDORED ON CACHE BOOL "" FORCE ) set( SDL2IMAGE_DEPS_SHARED OFF CACHE BOOL "" FORCE ) add_subdirectory( sdl-image ) target_link_libraries( Pixelinator SDL2_image::SDL2_image-static) # SDL_ttf set( SDL2TTF_SAMPLES OFF CACHE BOOL "" FORCE ) set( SDL2TTF_INSTALL OFF CACHE BOOL "" FORCE ) set( SDL2TTF_VENDORED ON CACHE BOOL "" FORCE ) set( SDL2TTF_HARFBUZZ ON CACHE BOOL "" FORCE ) add_subdirectory( sdl-ttf ) target_link_libraries( Pixelinator SDL2_ttf::SDL2_ttf-static ) # SDL_mixer set( SDL2MIXER_SAMPLES OFF CACHE BOOL "" FORCE ) set( SDL2MIXER_INSTALL OFF CACHE BOOL "" FORCE ) set( SDL2MIXER_VENDORED ON CACHE BOOL "" FORCE ) set( SDL2MIXER_DEPS_SHARED OFF CACHE BOOL "" FORCE ) set( SDL2MIXER_FLAC OFF CACHE BOOL "" FORCE ) set( SDL2MIXER_MOD OFF CACHE BOOL "" FORCE ) set( SDL2MIXER_MP3 OFF CACHE BOOL "" FORCE ) set( SDL2MIXER_MIDI OFF CACHE BOOL "" FORCE ) set( SDL2MIXER_OPUS OFF CACHE BOOL "" FORCE ) add_subdirectory( sdl-mixer ) target_link_libraries( Pixelinator SDL2_mixer::SDL2_mixer-static )
项目根目录下的CMakeLists.txt
cmake_minimum_required(VERSION 3.21) cmake_policy(SET CMP0075 NEW) cmake_policy(SET CMP0091 NEW) project(Pixelinator VERSION 1.0 DESCRIPTION "A 2D Engine using SDL and C") set( CMAKE_CXX_STANDARD 11 CACHE STRING "" FORCE ) set( CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "" FORCE ) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() if(CMAKE_BUILD_TYPE EQUAL Release) message("Building Release...") endif() set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_FLAGS "-Wall -Wextra") set(CMAKE_CXX_FLAGS_DEBUG "-g") set(CMAKE_CXX_FLAGS_RELEASE "-Ofast") add_library(Pixelinator STATIC ../../Pixelinator/src/engine/audio/sound.c ../../Pixelinator/src/engine/config/config.c ../../Pixelinator/src/engine/core/core.c ../../Pixelinator/src/engine/graphics/bitmap/bitmap.c ../../Pixelinator/src/engine/graphics/bitmap/pixel_threading.c ../../Pixelinator/src/engine/graphics/sprite/sprite.c ../../Pixelinator/src/engine/graphics/draw/shapes.c ../../Pixelinator/src/engine/input/input.c ../../Pixelinator/src/engine/io/io.c ../../Pixelinator/src/engine/render/render.c ../../Pixelinator/src/engine/render/render_init.c ../../Pixelinator/src/engine/render/render_util.c ../../Pixelinator/src/engine/font_cache/SDL_FontCache.c ../../Pixelinator/src/engine/text/text.c ../../Pixelinator/src/engine/global.c ../../Pixelinator/src/engine/time.c # SDL (Not sure if needed) ../external/SDL ../external/sdl-image ../external/sdl-mixer ../external/sdl-ttf ) set(APP_EXE PixelinatorDemo) if( ${CMAKE_SYSTEM_NAME} MATCHES "Android" ) add_library( ${APP_EXE} SHARED ) else() add_executable(${APP_EXE} main.c) set_target_properties( ${APP_EXE} PROPERTIES OUTPUT_NAME ${CMAKE_PROJECT_NAME} ) endif() find_package(Threads REQUIRED) if(THREADS_HAVE_PTHREAD_ARG) target_compile_options(${APP_EXE} PUBLIC "-pthread") endif() if(CMAKE_THREAD_LIBS_INIT) target_link_libraries(${APP_EXE} "${CMAKE_THREAD_LIBS_INIT}") endif() add_subdirectory("external") add_subdirectory(glad/) include_directories("include") #target_link_libraries(Pixelinator SDL2::SDL2main SDL2::SDL2-static SDL2_image::SDL2_image-static SDL2_ttf::SDL2_ttf-static SDL2_mixer::SDL2_mixer-static) # Library is generated inside of the cmake-build-debug or release folder! target_link_libraries(${APP_EXE} glad Pixelinator)
解决方案
CMake本身不直接支持合并静态库,但可以通过自定义命令调用平台原生工具实现。以下是具体步骤:
步骤1:将自研代码改为对象库编译
把根目录中add_library(Pixelinator STATIC)改为对象库,对象库只编译代码不生成库文件,方便后续合并:
add_library(Pixelinator_OBJECTS OBJECT ../../Pixelinator/src/engine/audio/sound.c ../../Pixelinator/src/engine/config/config.c ../../Pixelinator/src/engine/core/core.c ../../Pixelinator/src/engine/graphics/bitmap/bitmap.c ../../Pixelinator/src/engine/graphics/bitmap/pixel_threading.c ../../Pixelinator/src/engine/graphics/sprite/sprite.c ../../Pixelinator/src/engine/graphics/draw/shapes.c ../../Pixelinator/src/engine/input/input.c ../../Pixelinator/src/engine/io/io.c ../../Pixelinator/src/engine/render/render.c ../../Pixelinator/src/engine/render/render_init.c ../../Pixelinator/src/engine/render/render_util.c ../../Pixelinator/src/engine/font_cache/SDL_FontCache.c ../../Pixelinator/src/engine/text/text.c ../../Pixelinator/src/engine/global.c ../../Pixelinator/src/engine/time.c ) # 删除原SDL目录的引用,子模块会单独构建
步骤2:移除external目录的链接逻辑
修改external/CMakeLists.txt,删除所有target_link_libraries(Pixelinator ...)行,因为我们不再直接链接SDL库到自研库,而是通过合并处理:
set( BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE ) # SDL set( SDL_LIBC ON CACHE BOOL "" FORCE ) set( SDL_TEST OFF CACHE BOOL "" FORCE ) add_subdirectory(SDL) # 移除 target_link_libraries(Pixelinator SDL2::SDL2main SDL2::SDL2-static ) # SDL_image set( SDL2IMAGE_SAMPLES OFF CACHE BOOL "" FORCE ) set( SDL2IMAGE_INSTALL OFF CACHE BOOL "" FORCE ) set( SDL2IMAGE_VENDORED ON CACHE BOOL "" FORCE ) set( SDL2IMAGE_DEPS_SHARED OFF CACHE BOOL "" FORCE ) add_subdirectory( sdl-image ) # 移除 target_link_libraries(Pixelinator SDL2_image::SDL2_image-static) # SDL_ttf set( SDL2TTF_SAMPLES OFF CACHE BOOL "" FORCE ) set( SDL2TTF_INSTALL OFF CACHE BOOL "" FORCE ) set( SDL2TTF_VENDORED ON CACHE BOOL "" FORCE ) set( SDL2TTF_HARFBUZZ ON CACHE BOOL "" FORCE ) add_subdirectory( sdl-ttf ) # 移除 target_link_libraries(Pixelinator SDL2_ttf::SDL2_ttf-static ) # SDL_mixer set( SDL2MIXER_SAMPLES OFF CACHE BOOL "" FORCE ) set( SDL2MIXER_INSTALL OFF CACHE BOOL "" FORCE ) set( SDL2MIXER_VENDORED ON CACHE BOOL "" FORCE ) set( SDL2MIXER_DEPS_SHARED OFF CACHE BOOL "" FORCE ) set( SDL2MIXER_FLAC OFF CACHE BOOL "" FORCE ) set( SDL2MIXER_MOD OFF CACHE BOOL "" FORCE ) set( SDL2MIXER_MP3 OFF CACHE BOOL "" FORCE ) set( SDL2MIXER_MIDI OFF CACHE BOOL "" FORCE ) set( SDL2MIXER_OPUS OFF CACHE BOOL "" FORCE ) add_subdirectory( sdl-mixer ) # 移除 target_link_libraries(Pixelinator SDL2_mixer::SDL2_mixer-static )
步骤3:添加自定义合并命令
在根目录CMakeLists.txt末尾添加以下代码,自动收集所有静态库并合并为单个文件:
# 定义需要合并的组件:自研对象文件 + 所有SDL静态库 + glad set(MERGE_LIBS $<TARGET_OBJECTS:Pixelinator_OBJECTS> $<TARGET_FILE:SDL2::SDL2-static> $<TARGET_FILE:SDL2_image::SDL2_image-static> $<TARGET_FILE:SDL2_ttf::SDL2_ttf-static> $<TARGET_FILE:SDL2_mixer::SDL2_mixer-static> $<TARGET_FILE:glad> ) # 设置合并后的库名和路径 set(MERGED_LIB_NAME "${CMAKE_PROJECT_NAME}_merged") set(MERGED_LIB_FILE "${CMAKE_STATIC_LIBRARY_PREFIX}${MERGED_LIB_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX}") # 跨平台执行合并操作 if(UNIX OR APPLE) # Linux/macOS 使用ar工具 add_custom_command( OUTPUT ${MERGED_LIB_FILE} COMMAND rm -f ${MERGED_LIB_FILE} COMMAND ar crs ${MERGED_LIB_FILE} ${MERGE_LIBS} DEPENDS Pixelinator_OBJECTS SDL2::SDL2-static SDL2_image::SDL2_image-static SDL2_ttf::SDL2_ttf-static SDL2_mixer::SDL2_mixer-static glad COMMENT "Merging static libraries into ${MERGED_LIB_FILE}" ) elseif(WIN32) # Windows 使用lib.exe工具 add_custom_command( OUTPUT ${MERGED_LIB_FILE} COMMAND lib.exe /OUT:${MERGED_LIB_FILE} ${MERGE_LIBS} DEPENDS Pixelinator_OBJECTS SDL2::SDL2-static SDL2_image::SDL2_image-static SDL2_ttf::SDL2_ttf-static SDL2_mixer::SDL2_mixer-static glad COMMENT "Merging static libraries into ${MERGED_LIB_FILE}" ) endif() # 创建自定义目标,确保构建时自动执行合并 add_custom_target(MergedPixelinator ALL DEPENDS ${MERGED_LIB_FILE}) # 如需让Demo程序链接合并后的库,修改此行 # target_link_libraries(${APP_EXE} ${MERGED_LIB_FILE})
关键说明
- 对象库是核心:它避免了重复编译,同时让我们能灵活组合编译产物。
- 平台兼容性:通过条件判断适配不同系统的库合并工具,保证跨平台可用。
- 依赖保障:自定义命令的
DEPENDS参数确保所有依赖库编译完成后才执行合并。
内容的提问来源于stack exchange,提问作者Angle-Brackets
相关产品推荐
相关产品推荐

