使用CMake ExternalProject集成GMP库时的链接错误排查与解决
问题描述
我正在基于GNU MP开展项目,使用CMake的ExternalProject组件管理该库,编写了CMake配置文件与测试代码后,执行cmake --fresh ..配置正常,但执行make时出现链接错误,错误日志显示libgmpxx.a中的函数找不到libgmp里的定义。怀疑是链接顺序问题,请问该问题的成因是什么?如何解决?我的gcc版本为12.3.0。
项目结构
proj | build |- CMakeLists.txt |- test.cpp
CMakeLists.txt
cmake_minimum_required(VERSION 3.25) include(ExternalProject) include(ProcessorCount) project(proj) # BELOW SOURCE IS MOSTLY FROM # https://go.googlesource.com/gollvm/+/96cc8367c6d13a58fccf1f0c5e262d03df742451/CMakeLists.txt processorcount(PROCESSOR_COUNT) set(EXTINSTALLDIR "${CMAKE_CURRENT_BINARY_DIR}/external/install") set(EXTLIBDIR "${EXTINSTALLDIR}/lib") set(EXTINCLUDEDIR "${EXTINSTALLDIR}/include") set(EXTCPPFLAGS "CFLAGS=-I${EXTINCLUDEDIR}") if(NOT "${CMAKE_SYSROOT}" STREQUAL "") string(APPEND EXTCPPFLAGS " -fPIC -DPIC") endif() set(EXTLDFLAGS "LDFLAGS=-L${EXTLIBDIR}") set(EXTCC "CC=${CMAKE_C_COMPILER}" "CXX=${CMAKE_CXX_COMPILER}") find_package(Threads REQUIRED) ExternalProject_Add(local_libgmp URL https://gmplib.org/download/gmp/gmp-6.3.0.tar.xz DOWNLOAD_DIR ${CMAKE_CURRENT_BINARY_DIR}/external-downloads BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/external/gmp-build SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/external/gmp CONFIGURE_COMMAND <SOURCE_DIR>/configure --enable-cxx --prefix=${EXTINSTALLDIR} ${EXTCPPFLAGS} ${EXTLDFLAGS} ${EXTCC} BUILD_COMMAND make install -j${PROCESSOR_COUNT} LOG_CONFIGURE 1 LOG_BUILD 1 LOG_INSTALL 1 TEST_COMMAND make check -j${PROCESSOR_COUNT} ) add_library(${PROJECT_NAME}::libgmp STATIC IMPORTED) #ExternalProject_Get_Property(local_libgmp BINARY_DIR) set_target_properties(${PROJECT_NAME}::libgmp PROPERTIES IMPORTED_LOCATION ${EXTLIBDIR}/libgmp.a ) add_library(${PROJECT_NAME}::libgmpxx STATIC IMPORTED) #ExternalProject_Get_Property(local_libgmp BINARY_DIR) set_target_properties(${PROJECT_NAME}::libgmpxx PROPERTIES IMPORTED_LOCATION ${EXTLIBDIR}/libgmpxx.a ) add_dependencies(${PROJECT_NAME}::libgmp local_libgmp) add_dependencies(${PROJECT_NAME}::libgmpxx local_libgmp) # END add_executable(Test test.cpp) target_include_directories(Test PRIVATE ${EXTINCLUDEDIR}) target_link_directories(Test PRIVATE ${EXTLIBDIR}) target_link_libraries(Test PRIVATE pthread ${PROJECT_NAME}::libgmp ${PROJECT_NAME}::libgmpxx)
test.cpp
#include <iostream> #include <gmpxx.h> int main() { std::cout << "Hello, world!" << std::endl; std::cout << 11_mpz + 12_mpz << std::endl; }
链接错误日志
[ 9%] Completed 'local_libgmp' [ 81%] Built target local_libgmp [ 90%] Building CXX object CMakeFiles/Test.dir/test.cpp.o [100%] Linking CXX executable Test /usr/bin/ld: external/install/lib/libgmpxx.a(osmpz.o): in function `operator<<(std::ostream&, __mpz_struct const*)': osmpz.cc:(.text+0x58): undefined reference to `__gmpz_get_str' /usr/bin/ld: external/install/lib/libgmpxx.a(osdoprnti.o): in function `__gmp_doprnt_integer_ostream(std::ostream&, doprnt_params_t*, char*)': osdoprnti.cc:(.text+0x7c): undefined reference to `__gmp_doprnt_integer' /usr/bin/ld: osdoprnti.cc:(.text+0x88): undefined reference to `__gmp_asprintf_final' /usr/bin/ld: external/install/lib/libgmpxx.a(osfuns.o):(.data.rel.ro+0x8): undefined reference to `__gmp_asprintf_memory' /usr/bin/ld: external/install/lib/libgmpxx.a(osfuns.o):(.data.rel.ro+0x10): undefined reference to `__gmp_asprintf_reps' collect2: error: ld returned 1 exit status make[2]: *** [CMakeFiles/Test.dir/build.make:99: Test] Error 1 make[1]: *** [CMakeFiles/Makefile2:111: CMakeFiles/Test.dir/all] Error 2 make: *** [Makefile:91: all] Error 2
问题成因
GNU链接器(ld)处理静态库时遵循顺序扫描规则:只有当遇到未定义符号时,才会从后续的库中查找对应定义。你的链接顺序是libgmp在前、libgmpxx在后,但libgmpxx本身依赖libgmp中的函数。当链接器处理完libgmp后再扫描libgmpxx,发现未定义符号时不会回头重新扫描libgmp,导致这些符号无法解析。
解决方法
方法1:手动调整链接顺序
将被依赖的库放在依赖库的后面,也就是先写libgmpxx,再写libgmp:
target_link_libraries(Test PRIVATE pthread ${PROJECT_NAME}::libgmpxx ${PROJECT_NAME}::libgmp)
方法2:让导入库自动处理依赖(更规范)
修改libgmpxx的导入库配置,添加对libgmp的依赖关系,CMake会自动处理正确的链接顺序:
add_library(${PROJECT_NAME}::libgmpxx STATIC IMPORTED) set_target_properties(${PROJECT_NAME}::libgmpxx PROPERTIES IMPORTED_LOCATION ${EXTLIBDIR}/libgmpxx.a IMPORTED_LINK_INTERFACE_LIBRARIES ${PROJECT_NAME}::libgmp )
之后链接时只需指定libgmpxx即可,CMake会自动将libgmp添加到正确位置:
target_link_libraries(Test PRIVATE pthread ${PROJECT_NAME}::libgmpxx)
额外优化:完善导入库接口
可以将头文件路径附加到导入库的属性中,无需给Test单独设置target_include_directories:
set_target_properties(${PROJECT_NAME}::libgmp PROPERTIES IMPORTED_LOCATION ${EXTLIBDIR}/libgmp.a INTERFACE_INCLUDE_DIRECTORIES ${EXTINCLUDEDIR} ) set_target_properties(${PROJECT_NAME}::libgmpxx PROPERTIES IMPORTED_LOCATION ${EXTLIBDIR}/libgmpxx.a INTERFACE_INCLUDE_DIRECTORIES ${EXTINCLUDEDIR} IMPORTED_LINK_INTERFACE_LIBRARIES ${PROJECT_NAME}::libgmp )
此时Test的配置可简化为:
add_executable(Test test.cpp) target_link_libraries(Test PRIVATE pthread ${PROJECT_NAME}::libgmpxx)
内容的提问来源于stack exchange,提问作者user5876164
相关产品推荐
相关产品推荐

