CMake 使用 install 与 export 导出库

废话不多说,先上成品!

给 VSCode 用的 Snippet 👇

{
	"CMake install configure": {
		"prefix": "cmake-install",
		"body": [
			"# --- BEGIN CMAKE INSTALL/EXPORT ---",
			"include(GNUInstallDirs)",
			"set(PROJECT_EXPORT_TARGETS ${1:\\${PROJECT_NAME\\}})",
			"set(PROJECT_EXPORT_NAME ${2:\\${PROJECT_NAME\\}})",
			"set(PROJECT_INSTALL_CMAKEDIR ${3:\\${CMAKE_INSTALL_LIBDIR\\}/cmake/\\${PROJECT_EXPORT_NAME\\}})",
			"",
			"# create aliases for targets",
			"foreach(target \\${PROJECT_EXPORT_TARGETS\\})",
			"\tadd_library(\\${PROJECT_EXPORT_NAME\\}::\\${target\\} ALIAS \\${target\\})",
			"endforeach()",
			"# install targets",
			"install(",
			"\tTARGETS \\${PROJECT_EXPORT_TARGETS\\}",
			"\tEXPORT \\${PROJECT_EXPORT_NAME\\}Targets",
			"\tARCHIVE DESTINATION \\${CMAKE_INSTALL_LIBDIR\\}",
			"\tLIBRARY DESTINATION \\${CMAKE_INSTALL_LIBDIR\\}",
			"\tRUNTIME DESTINATION \\${CMAKE_INSTALL_BINDIR\\}",
			"\tPUBLIC_HEADER DESTINATION \\${CMAKE_INSTALL_INCLUDEDIR\\}",
			"\tINCLUDES DESTINATION \\${CMAKE_INSTALL_INCLUDEDIR\\}",
			")",
			"",
			"# install project headers",
			"install(",
			"\tDIRECTORY \\${CMAKE_CURRENT_SOURCE_DIR\\}/include/",
			"\tDESTINATION \\${CMAKE_INSTALL_INCLUDEDIR\\}",
			")",
			"",
			"# install CMake xxxTargets.cmake",
			"export(",
			"\tEXPORT \\${PROJECT_EXPORT_NAME\\}Targets",
			"\tNAMESPACE \\${PROJECT_EXPORT_NAME\\}::",
			"\tFILE \\${PROJECT_EXPORT_NAME\\}Targets.cmake",
			")",
			"install(",
			"\tFILES \\${CMAKE_CURRENT_BINARY_DIR\\}/\\${PROJECT_EXPORT_NAME\\}Targets.cmake",
			"\tDESTINATION \\${PROJECT_INSTALL_CMAKEDIR\\}",
			")",
			"",
			"# install xxxConfigVersion.cmake",
			"include(CMakePackageConfigHelpers)",
			"write_basic_package_version_file(",
			"\t\\${PROJECT_EXPORT_NAME\\}ConfigVersion.cmake",
			"\tVERSION \\${PROJECT_VERSION\\}",
			"\tCOMPATIBILITY AnyNewerVersion",
			")",
			"install(",
			"\tFILES \\${CMAKE_CURRENT_BINARY_DIR\\}/\\${PROJECT_EXPORT_NAME\\}ConfigVersion.cmake",
			"\tDESTINATION \\${PROJECT_INSTALL_CMAKEDIR\\}",
			")",
			"",
			"# install CMake xxxConfig.cmake",
			"install(",
			"\tEXPORT \\${PROJECT_EXPORT_NAME\\}Targets",
			"\tFILE \\${PROJECT_EXPORT_NAME\\}Config.cmake",
			"\tNAMESPACE \\${PROJECT_EXPORT_NAME\\}::",
			"\tDESTINATION \\${PROJECT_INSTALL_CMAKEDIR\\}",
			")",
			"# --- END CMAKE INSTALL/EXPORT ---"
		],
		"description": "CMake 导出目标"
	}
}

简单讲讲

第一部分

#! 常用的模块了,设置 GNU 风格的 install 目录,变量命名为 CMAKE_INSTALL_xxxDIR
include(GNUInstallDirs)

#! 设置需要导出的目标(列表)
set(PROJECT_EXPORT_TARGETS ${PROJECT_NAME})

#! 设置导出的命名,在这份 snippet 中应用于 xxxConfig.cmake/xxxTargets.cmake/
#! xxxConfigVersion.cmake 和导出的目标的命名空间
set(PROJECT_EXPORT_NAME ${PROJECT_NAME})

#! 设置生成的 cmake 文件的 install 目录,通常会放在顶层目录为 share 或 lib/lib64 的
#! 目录下,在这里选择放在 lib/lib64 目录下
set(PROJECT_INSTALL_CMAKEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_EXPORT_NAME})

第二部分

#! 在同一个 CMake build tree 中,每个项目的 targets 对外都是可见的,比如 A 项目中有一个
#! foobar 的静态库目标,那么在 B 项目中就可以直接链接 foobar 目标。
#! 但是一般情况在 CMake 使用第三方库引入库都会有命名空间(命名冲突问题),这时链接的目标可
#! 能就是 foobar::foobar !(如果不是的话假设就是!!!)
#! 出于统一性考虑,应尽可能使得同一 build tree 内外链接同名目标。
#! 然而 build tree 内即使进行了 install/export 操作,依旧不能直接使用带有命名空间的目标
#! (可能以某种参数进行的 install/export 可以做到,但是我写的时候实在是在这上面找不到办法)
#! 故而曲线救国,在这里额外对导出的目标定义带命名空间的别名,完美!
#! NOTE: 不会吧不会吧?不会有人连这种别名都能造成命名冲突吧?
#! NOTE: 我自认为因为别名没在这里被导出,所以不会影响外部 CMake 项目的引入,或许只不过是我
#> 多虑了,其实根本就不存在这种问题呢?不过说到底我对此机理并未深究,故留个疑问在这。
foreach(target ${PROJECT_EXPORT_TARGETS})
	add_library(${PROJECT_EXPORT_NAME}::${target} ALIAS ${target})
endforeach()

第三部分

#! 重头之一:配置待导出目标们的 install 选项,主要包括相关文件的 install 目录,
#! 其中 EXPORT 参数指示了被导出目标集合的导出名,该项将在 export 中被使用以代替显
#! 式地列出目标。另
install(
	TARGETS ${PROJECT_EXPORT_TARGETS}
	EXPORT ${PROJECT_EXPORT_NAME}Targets
	ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
	LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
	RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
	PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
	INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

第四部分

#! 导出头文件
#! 实际上该命令单纯地用于 install 目录(当路径后跟着 "/" 时表示仅导入目录下的文件与子目录)
#! 但是由于我个人的习惯,即项目结构一般服从如下:
#! Project
#! |   CMakeLists.txt
#! +---include
#! |   \---ProjectInc
#! |           [Public Headers]
#! \---src
#!         *.cpp
#!         [Private Headers]
#!         CMakeLists.txt
#! 所以就方便如下这样直接导出公共头文件了!另外在这份 snippet 中还有几个地方也需要前置要求
#! 如上的目录结构,如果要直接挪用的话可能需要注意一下!
install(
	DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/
	DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

第五部分

#! 重头之二:生成用于导入被导出目标的 xxxTargets.cmake 文件
#! export 支持 export(TARGETS) 命令和 export(EXPORT) 命令,在第三部分中提过,install
#! 中指定了待导入目标们的导出名,在此处则直接使用导出名指代被导出的目标。这与代换后的
#! export(TARGETS) 命令是等价的。
export(
	EXPORT ${PROJECT_EXPORT_NAME}Targets
	NAMESPACE ${PROJECT_EXPORT_NAME}::
	FILE ${PROJECT_EXPORT_NAME}Targets.cmake
)

#! export 执行生成的 xxxTargets.cmake 存在于 ${CMAKE_CURRENT_BINARY_DIR} 目录下(也就
#! 是 CMake 构造目录下的与源代码同名子目录),故而需要额外导入该文件。
install(
	FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_EXPORT_NAME}Targets.cmake
	DESTINATION ${PROJECT_INSTALL_CMAKEDIR}
)

第六部分

#! 生成 xxxConfig.cmake 的版本配置脚本 xxxConfigVersion.cmake
#! NOTE: 不是特别重要,可以跳过~
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
	${PROJECT_EXPORT_NAME}ConfigVersion.cmake
	VERSION ${PROJECT_VERSION}
	COMPATIBILITY AnyNewerVersion
)

#! 原因同第五部分,需要手动导入!
install(
	FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_EXPORT_NAME}ConfigVersion.cmake
	DESTINATION ${PROJECT_INSTALL_CMAKEDIR}
)

第七部分

#! 重头之三:生成库的配置文件 xxxConfig.cmake xxxConfig-<config>.cmake
#! 其中 EXPORT 是一个已经被指派的导出名,剩下的部分敢敢当当,一目了然!
install(
	EXPORT ${PROJECT_EXPORT_NAME}Targets
	FILE ${PROJECT_EXPORT_NAME}Config.cmake
	NAMESPACE ${PROJECT_EXPORT_NAME}::
	DESTINATION ${PROJECT_INSTALL_CMAKEDIR}
)
posted @ 2023-03-05 02:18  周上行Ryer  阅读(1009)  评论(0编辑  收藏  举报