最近在研究vcpkg和cmake的集成如何自建port,发现必须要在项目的cmake支持install,坑很多,网上的信息比较少,错误的引导也很多,事例代码写的洋洋洒洒,很规矩很漂亮,然而在library里的CMakeList.txt里压根没有提供install
的步骤脚本,也不知道他是怎么通的,但更多的情况是网友提供的install
脚本单单cmake
可以find_package()
而vcpkg
不能识别。
想通过身边的人get到此技能几乎没戏,因为绝大多数人用cmake做开发仅仅停留在用的层面,基本不会考虑写install脚本,这里做一次总结,总结下经历过的坑。
首先,我先把经历过的坑列下:
头文件和库文件能install了,结果
find_package()
依然找不到库的定义:
其实,find_package()
能找到库得先找到xxxConfig.cmake
或者xxx-config.cmake
(如果是基于别人已经编译好的库,则需要手写FindXXX.cmake
);明明生成了
xxxConfig.cmake
却依然不能被vcpkg识别,一直提示没有找到xxxConfig.cmake
或者xxx-config.cmake
(如果不用vcpkg,指定CMAKE_PREFIX_PATH
或安装到系统目录,cmake
倒是能找到):
configure_file()
生成的XXXConfig.cmake
信息不完整,即便config文件生成了也不能被vcpkg识别,必须用configure_package_config_file()
, 然而IDE(我用的是CLion)里却又不提示有此API;通过
find_package()
查找库时候会报错:
库项目里必须指定set(CMAKE_BUILD_TYPE Release)
, 否则vcpkg下载项目后默认以Debug模式编译项目,然而vcpkg又要求在port.profile
里将Debug
目录删除,导致托管的库在被使用时候提示找不到debug目录下的so或者lib。
如何编写完整的cmake install脚本:
首先,先呈现下一般cmake library项目的结构组成部分:
├── CMakeLists.txt
├── include
│ └── hello.h
│ └── world.h
│ └── ccc.h
├── source
│ └── CMakeLists.txt
│ └── hello.cpp
│ └── world.cpp
├── tests
│ └── CMakeLists.txt
│ └── test1.cpp
│ └── test2.cpp
├── example
│ └── CMakeLists.txt
│ └── example1.cpp
│ └── example1.cpp
└── source
└── ReadMe.md
以下呈现source目录中的CMakeList.txt的通用脚本:
project(xxx VERSION 1.0.0)
aux_source_directory(. DIR_SRCS)
add_library(${PROJECT_NAME} SHARED ${DIR_SRCS})
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
# ------------------------------- install script -------------------------------
set_target_properties(${PROJECT_NAME} PROPERTIES
PUBLIC_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/include/logger.h
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
ARCHIVE FALSE # means don't generate static lib.
)
# Generate the version file for the config file
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
VERSION ${PACKAGE_VERSION}
COMPATIBILITY SameMinorVersion
)
# Create config file
configure_package_config_file(
cmake/Config.cmake.in ${PROJECT_NAME}Config.cmake
INSTALL_DESTINATION lib/cmake/${PROJECT_NAME}
)
# Install config files
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
DESTINATION
lib/cmake/${PROJECT_NAME}
)
# Exporting Targets from the Build Tree
install(EXPORT ${PROJECT_NAME}Targets
FILE ${PROJECT_NAME}Targets.cmake
DESTINATION lib/cmake/${PROJECT_NAME}
)
# Install the target and create export-set
install(TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}Targets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
PUBLIC_HEADER DESTINATION include
)
Config.cmake.in内容如下:
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
# find_dependency(OtherLib REQUIRED)
check_required_components("@PROJECT_NAME@")
文件放在跟install script的CMakeList.txt同级目录下
如何集成到项目中
find_package(xxx 1.0.0 CONFIG REQUIRED)
target_link_libraries(main PRIVATE xxx)
在踩坑的道路上,终归我是活着上岸了,不过也要感谢网友的文章,虽然它们带我下海,很多没有带我上岸:
CSDN: cmake 生成供find_package使用的自定义模块
这篇算是启蒙之篇,感谢作者带我上路,但是它真的不支持vcpkg识别。知乎: CMake之install方法的使用
篇幅写的很好,代码也很规矩,但是真的不能被vcpkg识别。简书: CMake最佳实践
这篇文章的xxxConfig.cmake生成走了弯路,心疼下作者。CMake库打包以及支持find_package
这篇文章接近上岸了,但是xxxConfigVersion.cmake没必要自己写,可以自动生成的,心疼下作者。Tutorial: Easily supporting CMake install and find_package()这篇文章讲解也非常清晰,可参考性比较高。
GitHub: package-example
这篇文章帮我上岸了,但我也给它简化了一些步骤。