如何在CMake中指定自定义链接顺序与链接命令?
针对玩具操作系统这类要求精确链接顺序的场景,完全可以绕开CMake默认的依赖管理逻辑,实现crti.o crtbegin.o mykernel.o [其他库] crtend.o crtn.o的严格顺序,同时解决你当前方案的硬编码和扩展性问题。
更优雅的实现方式
1. 用生成器表达式替代硬编码路径
CMake的链接命令模板支持生成器表达式,能自动解析目标的对象文件路径,不用写死CMakeFiles/crti.dir/crti.s.obj这类路径。同时,要保留<LINK_LIBRARIES>占位符,这样后续添加其他库时不会破坏顺序:
# 定义crt对象目标 add_library(crti OBJECT path/to/crti.s) add_library(crtn OBJECT path/to/crtn.s) # 内核可执行目标 add_executable(awesome_kernel myAwesomeKernel.cc) # 确保crt目标先于内核编译 add_dependencies(awesome_kernel crti crtn) # 设置链接依赖文件,确保脚本或crt文件变更时触发重链接 set_property(TARGET awesome_kernel APPEND PROPERTY LINK_DEPENDS path/to/linker_script $<TARGET_PROPERTY:crti,SOURCE_DIR>/crti.s $<TARGET_PROPERTY:crtn,SOURCE_DIR>/crtn.s ) # 自定义C++链接命令模板,严格控制顺序 set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} -T path/to/linker_script \ $<TARGET_OBJECTS:crti> ${CRTBEGIN_OBJ} <OBJECTS> <LINK_LIBRARIES> ${CRTEND_OBJ} $<TARGET_OBJECTS:crtn> \ -o <TARGET>" )
$<TARGET_OBJECTS:crti>:自动展开为crti目标编译出的所有对象文件路径,适配多配置(Debug/Release)和不同平台的路径差异。<OBJECTS>:对应内核自身的源文件编译产物(比如myAwesomeKernel.o)。<LINK_LIBRARIES>:自动填充target_link_libraries(awesome_kernel ...)添加的所有库,确保它们被链接在crtbegin.o和crtend.o之间,完全兼容后续扩展。
2. 备选方案:针对单个目标设置链接选项
如果不想修改全局的CMAKE_CXX_LINK_EXECUTABLE,可以给awesome_kernel单独配置链接标志,用生成器表达式注入crt对象:
get_target_property(CRTI_OBJS crti OBJECTS) get_target_property(CRTN_OBJS crtn OBJECTS) target_link_options(awesome_kernel PRIVATE "-T path/to/linker_script" "${CRTI_OBJS}" "${CRTBEGIN_OBJ}" # 这里CMake会自动插入内核对象和链接库,不过顺序可能受CMake依赖调整影响,稳定性不如第一种方案 "${CRTEND_OBJ}" "${CRTN_OBJS}" )
注意:这种方式下CMake可能会自动调整链接顺序来解决符号依赖,所以对于要求绝对严格顺序的场景,优先用自定义链接命令模板。
你的两个疑问解答
1. 如何移除硬编码的对象文件路径?
用**生成器表达式$<TARGET_OBJECTS:目标名>**即可,它会在生成构建文件阶段自动解析目标的所有对象文件的绝对路径,完美替代硬编码路径。如果需要在配置阶段获取路径,可以用get_target_property(OBJ_LIST 目标名 OBJECTS),但生成器表达式更适合在链接命令模板中使用,能适配多配置场景。
2. <OBJECTS>占位符的工作原理?
<OBJECTS>是CMake内置的链接命令占位符,会在生成具体构建规则时,自动展开为当前可执行目标的所有源文件编译产物——包括通过add_executable或target_sources添加的.cc/.s等文件对应的.o/.obj文件。
你之前的方案无法添加其他库,是因为模板里缺少<LINK_LIBRARIES>占位符——这个占位符会自动填充target_link_libraries添加的所有库,所以必须把它加在<OBJECTS>之后、crtend.o之前,才能让新添加的库融入到要求的顺序里。
内容的提问来源于stack exchange,提问作者curbalman

