CMAKE 实现自动区分不同版本库
开发SDK时,经常需要依赖第三方库。此时需要考虑库的版本问题,一方面针对不同的编译器和Release、Debug版本灵活选择不同的第三方库,另一方面生成不同版本的库,并能被其他项目灵活引用。CMAKE此时体现其重要作用。
这里记录并分析开源OCR项目Tesseract的编译过程。
Tesseract 依赖第三方库leptonica,使用方法为:
find_package(Leptonica REQUIRED)
include_directories(${Leptonica_INCLUDE_DIRS})
target_link_libraries(libtesseract ${Leptonica_LIBRARIES})
利用CMake编译库leptonica时,会生成三个配置文件:LeptonicaConfig.cmake,LeptonicaConfig-version.cmake,LeptonicaTargets.cmake。在LeptonicaConfig.cmake中包含了LeptonicaTargets.cmake文件,并将Leptonica_LIBRARIES定义为leptonica, 在 LeptonicaTargets 中对变量leptonica根据不同编译版本有不同定义:
// File LeptonicaConfig.cmake
include(${CMAKE_CURRENT_LIST_DIR}/LeptonicaTargets.cmake)
#
# ====================================================================
# Link libraries:
# ====================================================================
set(Leptonica_LIBRARIES leptonica)
////////////////////////
//File LeptonicaTargets.cmake
# Create imported target leptonica
add_library(leptonica SHARED IMPORTED)
# Import target "leptonica" for configuration "Debug"
set_property(TARGET leptonica APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
set_target_properties(leptonica PROPERTIES
IMPORTED_IMPLIB_DEBUG "E:/ImageProcess/OCR/tesseract/leptonica-1.74.0/build_vs12/src/Debug/leptonica-1.74.0d.lib"
IMPORTED_LINK_INTERFACE_LIBRARIES_DEBUG "E:/ImageProcess/OCR/tesseract/lib/Win32/giflib416-static-mtdll-debug.lib;
E:/ImageProcess/OCR/tesseract/lib/Win32/libpng143-static-mtdll-debug.lib;
E:/ImageProcess/OCR/tesseract/lib/Win32/libtiff394-static-mtdll-debug.lib;
E:/ImageProcess/OCR/tesseract/lib/Win32/zlib128-static-mtdll-debug.lib"
IMPORTED_LOCATION_DEBUG "E:/ImageProcess/OCR/tesseract/leptonica-1.74.0/build_vs12/bin/Debug/leptonica-1.74.0d.dll" )
# Import target "leptonica" for configuration "Release"
set_property(TARGET leptonica APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_target_properties(leptonica PROPERTIES
IMPORTED_IMPLIB_RELEASE "E:/ImageProcess/OCR/tesseract/leptonica-1.74.0/build_vs12/src/Release/leptonica-1.74.0.lib"
IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE "E:/ImageProcess/OCR/tesseract/lib/Win32/giflib416-static-mtdll-debug.lib;
E:/ImageProcess/OCR/tesseract/lib/Win32/libpng143-static-mtdll.lib;
E:/ImageProcess/OCR/tesseract/lib/Win32/libtiff394-static-mtdll.lib;
E:/ImageProcess/OCR/tesseract/lib/Win32/zlib128-static-mtdll.lib"
IMPORTED_LOCATION_RELEASE "E:/ImageProcess/OCR/tesseract/leptonica-1.74.0/build_vs12/bin/Release/leptonica-1.74.0.dll")
编译Tesseract成功后,安装结果包括头文件、库文件、和CMake配置文件。
分析Tesseract项目的CMakeLists.txt,首先是配置部分,首先是生成configure文件: TesseractConfig-version.cmake、TesseractConfig.cmake;其次,待生成的库文件名对应Release、Debug有不同定义;接着,导出TesseractTargets.cmake文件;最后是安装配置文件。
// 1
configure_file(
${CMAKE_SOURCE_DIR}/cmake/templates/TesseractConfig-version.cmake.in
${CMAKE_BINARY_DIR}/TesseractConfig-version.cmake @ONLY)
configure_file(
${CMAKE_SOURCE_DIR}/cmake/templates/TesseractConfig.cmake.in
${CMAKE_BINARY_DIR}/TesseractConfig.cmake @ONLY)
// 2
if (WIN32)
set_target_properties(libtesseract PROPERTIES OUTPUT_NAME tesseract${VERSION_MAJOR}${VERSION_MINOR})
set_target_properties(libtesseract PROPERTIES DEBUG_OUTPUT_NAME tesseract${VERSION_MAJOR}${VERSION_MINOR}d)
endif()
// 3
export(TARGETS libtesseract FILE ${CMAKE_BINARY_DIR}/TesseractTargets.cmake)
// 4
install(EXPORT TesseractTargets DESTINATION cmake)
install(FILES
${CMAKE_BINARY_DIR}/TesseractConfig.cmake
${CMAKE_BINARY_DIR}/TesseractConfig-version.cmake
DESTINATION cmake)
/////////////////////////////////////////////////////////////////////
在图2中,可以看到2个多出来的文件:TesseractTargets-debug, TesseractTargets-release。这两个文件就是实现根据环境设置不同库的功能。通过比较发现,最终的安装文件TesseractTargets.cmake与编译项目中生成的TesseractTargets.cmake(与工程文件sln同目录)并不相同,安装目录中的cmake文件是通过上述步骤3 export方法导出的。源文件TesseractTargets.cmake主要内容 :
...
# Protect against multiple inclusion, which would fail when already imported targets are added once more.
set(_targetsDefined)
set(_targetsNotDefined)
set(_expectedTargets)
foreach(_expectedTarget libtesseract)
list(APPEND _expectedTargets ${_expectedTarget})
if(NOT TARGET ${_expectedTarget})
list(APPEND _targetsNotDefined ${_expectedTarget})
endif()
if(TARGET ${_expectedTarget})
list(APPEND _targetsDefined ${_expectedTarget})
endif()
endforeach()
...
# Create imported target libtesseract
add_library(libtesseract SHARED IMPORTED)
set_target_properties(libtesseract PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "TESS_IMPORTS")
# Import target "libtesseract" for configuration "Debug"
set_property(TARGET libtesseract APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
set_target_properties(libtesseract PROPERTIES
IMPORTED_IMPLIB_DEBUG "E:/ImageProcess/OCR/tesseract/tesseract-3.05.01/vs2013/Debug/tesseract305d.lib"
IMPORTED_LINK_INTERFACE_LIBRARIES_DEBUG "Ws2_32;leptonica"
IMPORTED_LOCATION_DEBUG "E:/ImageProcess/OCR/tesseract/tesseract-3.05.01/vs2013/bin/Debug/tesseract305d.dll" )
# Import target "libtesseract" for configuration "Release"
set_property(TARGET libtesseract APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_target_properties(libtesseract PROPERTIES
IMPORTED_IMPLIB_RELEASE "E:/ImageProcess/OCR/tesseract/tesseract-3.05.01/vs2013/Release/tesseract305.lib"
IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE "Ws2_32;leptonica"
IMPORTED_LOCATION_RELEASE "E:/ImageProcess/OCR/tesseract/tesseract-3.05.01/vs2013/bin/Release/tesseract305.dll" )
...
导出的目标TesseractTargets.cmake主要内容:
...
# Load information for each installed configuration.
get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
file(GLOB CONFIG_FILES "${_DIR}/TesseractTargets-*.cmake")
foreach(f ${CONFIG_FILES})
include(${f})
endforeach()
而对应的TesseractTargets-debug.cmake 和 TesseractTargets-release.cmake 的主要内容:
//File TesseractTargets-debug.cmake
...
# Commands may need to know the format version.
set(CMAKE_IMPORT_FILE_VERSION 1)
# Import target "libtesseract" for configuration "Debug"
set_property(TARGET libtesseract APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
set_target_properties(libtesseract PROPERTIES
IMPORTED_IMPLIB_DEBUG "${_IMPORT_PREFIX}/lib/tesseract305d.lib"
IMPORTED_LINK_INTERFACE_LIBRARIES_DEBUG "Ws2_32;leptonica"
IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/bin/tesseract305d.dll"
)
list(APPEND _IMPORT_CHECK_TARGETS libtesseract )
list(APPEND _IMPORT_CHECK_FILES_FOR_libtesseract "${_IMPORT_PREFIX}/lib/tesseract305d.lib" "${_IMPORT_PREFIX}/bin/tesseract305d.dll" )
...
// File TesseractTargets-release.cmake
...
# Import target "libtesseract" for configuration "Release"
set_property(TARGET libtesseract APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_target_properties(libtesseract PROPERTIES
IMPORTED_IMPLIB_RELEASE "${_IMPORT_PREFIX}/lib/tesseract305.lib"
IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE "Ws2_32;leptonica"
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/tesseract305.dll"
)
list(APPEND _IMPORT_CHECK_TARGETS libtesseract )
list(APPEND _IMPORT_CHECK_FILES_FOR_libtesseract "${_IMPORT_PREFIX}/lib/tesseract305.lib" "${_IMPORT_PREFIX}/bin/tesseract305.dll" )
...
通过比较Tesseract项目与Leptonica项目,不难总结出添加依赖库与生成新的库时实现版本控制的主要方法及流程。
第一步,使用Cmake正确安装第三方库后:
find_package(3RDLIBRARY REQUIRED)
include_directories(${3RDLIBRARY_INCLUDE_DIRS})
target_link_libraries(project ${3RDLIBRARY_LIBRARIES})
第二步,生成CMake配置文件:
CMakeLists.txt ——> XXConfig.cmake XXConfig-version.cmake
XXConfig.cmake ——> XXTargets.cmake
XXTargets.cmake ——>XXTargets.cmake(new) XXTargets-debug.cmake
XXTargets-release.cmake
显然,可以将XXTargets完成的功能置于XXConfig中,这样的话配置文件便只有2个。
关于cmake配置文件的生成规则,继续研究。