CMake教程——如何为你的项目编写完整的install脚本

  最近在研究vcpkg和cmake的集成如何自建port,发现必须要在项目的cmake支持install,坑很多,网上的信息比较少,错误的引导也很多,事例代码写的洋洋洒洒,很规矩很漂亮,然而在library里的CMakeList.txt里压根没有提供install的步骤脚本,也不知道他是怎么通的,但更多的情况是网友提供的install脚本单单cmake可以find_package()vcpkg不能识别。
  想通过身边的人get到此技能几乎没戏,因为绝大多数人用cmake做开发仅仅停留在用的层面,基本不会考虑写install脚本,这里做一次总结,总结下经历过的坑。

首先,我先把经历过的坑列下:

  1. 头文件和库文件能install了,结果find_package()依然找不到库的定义:
    其实,find_package()能找到库得先找到xxxConfig.cmake或者xxx-config.cmake(如果是基于别人已经编译好的库,则需要手写FindXXX.cmake);

  2. 明明生成了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;

  3. 通过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)

在踩坑的道路上,终归我是活着上岸了,不过也要感谢网友的文章,虽然它们带我下海,很多没有带我上岸:

  1. CSDN: cmake 生成供find_package使用的自定义模块
    这篇算是启蒙之篇,感谢作者带我上路,但是它真的不支持vcpkg识别。

  2. 知乎: CMake之install方法的使用
    篇幅写的很好,代码也很规矩,但是真的不能被vcpkg识别。

  3. CSDN: CMAKE_INSTALL_PREFIX无效的解决方案

  4. 简书: CMake最佳实践
    这篇文章的xxxConfig.cmake生成走了弯路,心疼下作者。

  5. CMake库打包以及支持find_package
    这篇文章接近上岸了,但是xxxConfigVersion.cmake没必要自己写,可以自动生成的,心疼下作者。

  6. Tutorial: Easily supporting CMake install and find_package()这篇文章讲解也非常清晰,可参考性比较高。

  7. GitHub: package-example
    这篇文章帮我上岸了,但我也给它简化了一些步骤。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,313评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,369评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,916评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,333评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,425评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,481评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,491评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,268评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,719评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,004评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,179评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,832评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,510评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,153评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,402评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,045评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,071评论 2 352

推荐阅读更多精彩内容

  • 我在拥抱迟暮的夕阳, 任人们无限的慨叹与欣赏。 我在人群熙攘中旅步在人群熙攘外, 孩子般踮着脚步采你陈酿朝夕的光。...
    洛水无月阅读 356评论 0 0
  • 《怦然心动》这部电影以早恋为主线,一对小孩,一棵树,特别简单的故事,两人是同班同学,小女孩茱莉一直想方设法接近小男...
    沐沐觉醒阅读 497评论 2 3
  • 阿塔华尔巴是一个统治着人口超过1000万,面积达200万平方公里大帝国的皇帝。有一天有人告诉他一个叫皮萨罗的人带着...
    去病先生阅读 210评论 0 0
  • 作者: 简.尼尔森 教育学博士, 杰出的心理学家、教育家, 美国"正面管教协会"的创始人. 她是7个孩子的母亲, ...
    Rose宇轩Mom阅读 550评论 0 1