cmake对第三方库的支持方式

一、介绍

在程序开发的过程中,往往会引入库(动态库和静态库)。有时会只有其中一种,有时可能会二者混用。在这种情况下,就引出了一个问题,如何在工程中对库进行支持(这里不涉及库的版本管理,也就是不处理DLL HELL之类的问题)。这个有很多种方式,不同的平台和不同的开发工具,有着不同的处理手段。比如在VS上和在Idea上都有不同,而不同的语言可能处理也有不同,这些都需要开发者自己根据实际情况来处理。
这里只提在Linux上使用Cmake进行第三方库管理的一些方法。

二、四种形式

在Linux使用第三库的方式,如果采用Cmake构建管理的话,主要有以下几种:
1、直接编译编译
这是最常用的一种方式,开发者都用过。老的方式使用方法一般是:

mkdir build
cd build 
cmake ..
make -j5
make install

不过高版本的CMake支持了下面的更简单的方式:

cmake -B build -DCMAKE_PREFIX_PATH=/home/fpc/Qt65/6.5.0/gcc_64   (通过 -S 可以 指定编译路径 -G指定构造方式 如Ninja)
cmake --build build -j5(--parallel 5)

B站上的大哥把这个叫做现代CMake,有道理。
这种方式比较适合初学者或者对项目管理简单的地方。优点是容易理解,好操作;缺点是需要手动在项目的CMake文件进行库的编译链接控制和管理。类似于下面:

find_package (库名称 1.6.0 REQUIRED)

add_executable (myapp main.cpp)
target_link_libraries (myapp 库名称)

如果有版本不同的或者不同的机器不同的开发环境,团队间配合时可能出现一些意想不到的问题。这种问题的特点是随机且不容易发现,所以在稍微大一些项目中如果采用这种方式 ,就必须有严格的库版本管理和依赖管理说明。可是大家都明白,靠人,往往是靠不住的。

2、CMake配合git submodules子模块控制

这种方法适用于团队间的源码级的有效管理,不适合只提供库的第三方,而且,第三方库和其它工程为平行关系,即第三库不属于某个工程,一定注意。在合适的路径下使用:

增加子模块:
cd examples
git submodule add http://xxx.xxx/libname.git
下载子模块:
git clone --recursive   git@xxxxxx/parent_libname.git
或者没有使用 --recursive 下载完成后,进入子目录执行:
git submodule update --init

然后在工程项目的总CMakeLists.txt文件中增加:

add_subdirectory(${CMAKE_SOURCE_DIR}/examples/libname)

在应用第三方库的项目CMakeLists.txt中增加:

add_dependencies(SUPRA_Lib libname)
TARGET_INCLUDE_DIRECTORIES(SUPRA_Lib
        PUBLIC ${SUPRA_Lib_INCLUDEDIRS} libname_include
)

target_link_libraries(SUPRA_Lib ${SUPRA_Lib_LIBRARIES} libname_path)

这种方式可以紧绑定库,不为外界影响。源码级管理,方便控制。缺点也很明显,带着一大堆的外部的库如果升级比较麻烦,同时,每一个使用的项目都得增加上面的依赖项目等。

3、直接加到工程子目录
这和上面的不同在于,这个第三方库真得成为了某个工程的子目录,也就是说add_subdirectory在某个项目的CMakeLists.txt下,比如谷歌的glog就提供了这样的方式:

add_subdirectory(${CMAKE_SOURCE_DIR}/examples/libname)
target_link_libraries(firt libname)

就是这么简单豪放,直接就可以用,不用处理什么头文件还有add_dependencies啊find_package之类的。缺点是可能不如分成模块划分更清晰一些,大工程可能有点让人维护上不方便。

4、使用外部工程的方法
这个的使用详细方法可以参考一下CMake的文档,这里给出一个例子:

include(ExternalProject)

SET(NodeEditor_DIR "${CMAKE_CURRENT_BINARY_DIR}/NodeEditor")
SET(NodeEditor_GIT_REPOSITORY "https://github.com/goeblr/nodeeditor.git" CACHE STRING "")
SET(NodeEditor_GIT_TAG "3bcba3740d68932f3ffaa4b3dc73e624fe51e2db" CACHE STRING "")
ExternalProject_Add( 
	NodeEditor
	PREFIX "${NodeEditor_DIR}"
	
	LOG_DOWNLOAD TRUE
	LOG_UPDATE TRUE
	LOG_CONFIGURE TRUE
	LOG_BUILD TRUE
	LOG_INSTALL TRUE
	
	SOURCE_DIR "${NodeEditor_DIR}"
	BINARY_DIR "${NodeEditor_DIR}_build"
	STAMP_DIR "${NodeEditor_DIR}_stamp"
	TMP_DIR "${NodeEditor_DIR}_tmp"
	#--Download step--------------
	GIT_REPOSITORY ${NodeEditor_GIT_REPOSITORY}
	GIT_TAG ${NodeEditor_GIT_TAG}
	#--Configure step-------------
	CMAKE_ARGS
	  -DCMAKE_RUNTIME_OUTPUT_DIRECTORY:PATH=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
	  -DCMAKE_LIBRARY_OUTPUT_DIRECTORY:PATH=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
	  -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY:PATH=${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}
	  -DCMAKE_BUILD_TYPE=Release
	  -DBUILD_EXAMPLES=OFF
	  -DQt5_DIR=${Qt5_DIR}
	  -DCMAKE_INSTALL_PREFIX=${NodeEditor_DIR}_install
	  -DNODE_EDITOR_STATIC=
	#--Build step-----------------
	#BUILD_ALWAYS 0
	#--Install step-----------------
	INSTALL_DIR=${NodeEditor_DIR}_install
)
SET(NodeEditor_LIBRARIES "nodes")
INCLUDE_DIRECTORIES(SUPRA_GUI "${NodeEditor_DIR}_install/include")
LINK_DIRECTORIES(SUPRA_GUI "${NodeEditor_DIR}_install/lib")

然后还在当前工程中:

TARGET_LINK_LIBRARIES(SUPRA_GUI
	${NodeEditor_LIBRARIES})
add_dependencies(SUPRA_GUI SUPRA_Lib NodeEditor)

这种方式并不简单,但应用灵活,可以说凡是可以用到的代码都可以如此操作。缺点也很明显,复杂啊,而且还要处理很多和编译无关的东西。

三、总结

CMake目前看应该是C++工程应用中的主流模式了。用它的好处是比写MakeFile要简单多了,更适合于开发者理解,相对来说简单很多。缺点是这玩意儿宏定义太多,各种错误也不好理解,如果遇到一些稀奇古怪的问题,可能老手都得流泪。瑕不掩瑜,勤学多用,主动避开一些坑儿,就会发现CMake还是挺不错的一种工具。

猜你喜欢

转载自blog.csdn.net/fpcc/article/details/131582753