基于CMake构建管理工程,静态库与动态库

基于CMake构建管理工程,静态库与动态库

写在入门之前

CMake是构建工程,对工程进行版本管理的重要工具,但是很多同学,包括我本人,在学校时往往不重视,甚至根本没听过这个工具。完成作业时都是按步骤在Visual Studio建立工程(solutions),生成一堆文件,如果“正式一些”,还会用git进行版本管理,如果随意了干脆直接用_v1,_v2进行“版本管理”。当然,仅仅是为了完成作业,这没有问题,但是把这些想法带到工作中就行不通了。其实,在工作初期,我对这些管理文件还是很排斥,后来不断总结才慢慢掌握这些技能。所以,我记录下这些关于CMake的科普,希望能让大家更快地上手CMake。

使用CMake至少有这些好处:

  1. 可以对工程进行严格的版本管理。基于Visual Studio配置工程时会生成一堆文件,根本不知道哪些有用,哪些是临时文件,让人头大;而且在VS界面进行配置修改是很不直观的,你很可能忘记上次改了什么,导致工程跑不通了。使用CMake可以很好地避免这些问题,所有配置都可以写在CMakeLists里,如果这个文件没改,每次生成的工程应该是一模一样的。
  2. CMake支持跨平台。有时公司希望在Linux运行原本在Windows上构建的程序(反之亦然),如果工程是基于.sln构建的而且很大,这就让人抓狂了。这时可以体现使用CMake的优势,如果CMake和源文件被小心地编写(使用的都是支持跨平台接口),刚才的问题就不是问题。
  3. CMake构建工程效率高,一些博客提到这点,但我目前没有直观感受。

Sample1: CMake Hello World

这一节会基于Windows平台介绍第一个CMakeLists,
示例可以在https://github.com/LeiChenIntel/wheels/tree/main/cmake_guide/sample1获取,
需要下载并安装CMakeVisual Studio
当CMake安装成功后,在命令行输入cmake会出现如图Usage:
在这里插入图片描述
在CMakeLists.txt目录下直接运行cmake会导致生成大量文件,因此再建立一个/build目录放文件是较好的选择。

基于命令行构建工程

进入sample1文件夹,在命令行依次输入:

cd wheels\cmake_guide\sample1
mkdir build
cd build
cmake ..
cmake --build . --config Release

进入/build,…表示运行上级目录的CMakeLists.txt,构建工程.sln;. 表示当前目录,build工程。
另一种build方法是在/build目录里打开.sln,在VS中build。
在这里插入图片描述

基于CMake-GUI构建工程

打开cmake-gui,填写CMakeList.txt路径,文件生成路径,generator和平台(一般64位机器选x64)信息,依次点击Configure和Generate,生成如下log,点击Open Project可直接打开VS2019。
在这里插入图片描述
在CMake-GUI会看到类似的log:

Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.19042.
The C compiler identification is MSVC 19.28.29335.0
The CXX compiler identification is MSVC 19.28.29335.0
Detecting C compiler ABI info
Detecting C compiler ABI info - done
Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.28.29333/bin/Hostx64/x64/cl.exe - skipped
Detecting C compile features
Detecting C compile features - done
Detecting CXX compiler ABI info
Detecting CXX compiler ABI info - done
Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.28.29333/bin/Hostx64/x64/cl.exe - skipped
Detecting CXX compile features
Detecting CXX compile features - done
PROJECT_NAME: CMakeWheels
Configuring done
Generating done

CMakeLists的说明

一个简单的CMakeLists如下所示:

cmake_minimum_required(VERSION 3.10)

# set project name
project(CMakeWheels)

# use "message" to print log and information
message("PROJECT_NAME: " ${PROJECT_NAME})

# set C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# rename/group header files and source files
set(HEADER_FILES ${PROJECT_SOURCE_DIR}/sample1.h)
set(SOURCE_FILES ${PROJECT_SOURCE_DIR}/sample1.cpp)

# add the executable
add_executable(sample1 ${HEADER_FILES} ${SOURCE_FILES})

# add solution include directories
target_include_directories(sample1 PUBLIC ${PROJECT_SOURCE_DIR})

一些说明:

project(CMakeWheels)

设置工程名CMakeWheels,对应Visual Studio中solution的名字。

message("PROJECT_NAME: " ${PROJECT_NAME})

CMake目前没有调试工具,如果执行过程中出了问题,用message可以在执行过程中打印log是重要的调试方法;
CMake里或有很多定义好的类似宏的变量,比如,PROJECT_NAME,PROJECT_SOURCE_DIR,可以用${.}获取具体值,更多的变量可以查官方文档 https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

设置C++标准,如果不加应该不会影响cmake执行。

set(HEADER_FILES ${PROJECT_SOURCE_DIR}/sample1.h)
set(SOURCE_FILES ${PROJECT_SOURCE_DIR}/sample1.cpp)

相当于为sample1.h和sample1.cpp取了别名,之后可以用HEADER_FILES和SOURCES_FILES代替,而且一个别名下不一定只有一个文件,比如加上一个demo.cpp也是可行的:

set(SOURCE_FILES 
	${PROJECT_SOURCE_DIR}/sample1.cpp
	${PROJECT_SOURCE_DIR}/demo.cpp)
add_executable(sample1 ${HEADER_FILES} ${SOURCE_FILES})

设置项目名sample1,对应VS中solution下project名,说明sample1中包含哪些源文件。

target_include_directories(sample1 PUBLIC ${PROJECT_SOURCE_DIR})

如果项目有引用头文件,需要添加路径,否则工程会找不到头文件:
在这里插入图片描述
PUBLIC参数关系到除了sample1别的库是否能访问源文件,可写的参数有PRIVATE和INTERFACE,作为一个CMake Hello World不再详述了,具体意义可以看官方文档。
直接用include_directories()在所有工程中添加路径也是可行的,不过官方不推荐这样的作法,具体说明哪个工程添加路径会让工程更清晰。

Sample2: CMake 构建静态库

示例可以在https://github.com/LeiChenIntel/wheels/tree/main/cmake_guide/sample2获取。

很多时候我们希望把写好的程序打包成一个库,给出接口,方便自己和别人调用,这在工程中是常见的作法,CMake作为工程管理工具可以做到这一点。
先简单说一下库,在系统中,库分为静态库.lib和动态库.dll(在linux系统中是.a和.so),

  • 静态库的特点:编译时函数会被打包进可执行文件.exe,这样编译完成的可执行文件不用任何依赖也可以运行,调用函数效率高。但如果库要更新,哪怕是微小的改变,必须重新编译,这一点对于程序发布是不利的,因为可执行文件会变得很大,而且每次要更新,为了搞定这点有了动态库。
  • 动态库的特点:编译时函数被存在了.dll中,在程序运行时动态链接这些文件,这样就将程序各个部分剥离开来,如果要更新,只要替换对应的某个dll就行了(如果接口没变的话),其他部分可以保持不变。当然,程序运行必须能找到dll,否则会出现cannot find dll的问题,同时,因为要从外部链接文件,程序运行的效率会略有下降。

Sample2关心的是构建静态库,动态库内容在Sample3。

CMakeLists的说明

cmake_minimum_required(VERSION 3.10)

# set project name
project(CMakeWheels)

# use "message" to print log and information
message("PROJECT_NAME: " ${PROJECT_NAME})

# set C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# add calculations static library
add_library(calculations STATIC Calculations.h Calculations.cpp)

# rename/group header files and source files
set(SOURCE_FILES ${PROJECT_SOURCE_DIR}/sample2.cpp)

# add the executable
add_executable(sample2 ${SOURCE_FILES})

# add solution include directories
# include Calculations.h
target_include_directories(sample2 PUBLIC ${PROJECT_SOURCE_DIR})

# add link libraries
target_link_libraries(sample2 PUBLIC calculations)

一些说明:

add_library(calculations STATIC Calculations.h Calculations.cpp)

STATIC参数说明编译成静态库,calculations是库的名字。

target_link_libraries(sample2 PUBLIC calculations)

用法与target_include_directories类似,见Sample1。虽然也有link_libraries函数,但更推荐使用target_link_libraries让结构变得明确。

build完成后可以看到库文件.lib和可执行文件.exe已经生成了。这时基于头文件和lib,别人就能使用库了。虽然两个文件在一个文件夹下,sample2.exe是不依赖calculations.lib的,将其拷贝到别的目录下可以直接运行。当然,在工程上,直接写一个sample2调用打印库函数并不是一个太好的方法,更多的时候会写一些测试用例(test case),比如使用Google Test写例子。
在这里插入图片描述

Sample3: CMake 构建动态库

示例可以在https://github.com/LeiChenIntel/wheels/tree/main/cmake_guide/sample3获取。

对于CMakeLists来说,动态库和静态库的构建是几乎一样的,只需要修改add_library的参数即可,但是在Windows上写动态库例子的时候发现了很多“坑”。比如,__declspec(dllexport)与__declspec(dllimport)两个动态库前缀的使用,外部变量和函数又不太一样。这些应该会另开一章,这里还是专注CMake。

CMakeLists的说明

add_library(calculations SHARED Calculations.h Calculations.cpp)

将STATIC换成SHARED即可。

build工程后,Release文件夹下会生成如下文件,可以看到有calculations.dll和calculations.lib。虽然是要生成动态库,但还是会生成一个.lib(如果程序想在运行时调用.dll,链接时是需要这个.lib的),当然.exe运行时只要.dll。
在这里插入图片描述
通过替换dll(红框),可以观察到输出不同,这就保证了不重新build整个工程的情况下对程序进行更新:
在这里插入图片描述
在这里插入图片描述

参考链接

CMake官方文档
https://cmake.org/cmake/help/latest/guide/tutorial/index.html

其他
https://github.com/ttroy50/cmake-examples

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值