当前位置:网站首页>CMake速成
CMake速成
2022-07-06 09:29:00 【狗蛋儿l】
转载:
https://blog.csdn.net/whahu1989/article/details/82078563?ops_request_misc=&request_id=&biz_id=102&utm_term=cmake%E4%BD%BF%E7%94%A8&utm_medium=distribute.pc_search_result.none-task-blog-2blogsobaiduweb~default-3-82078563.nonecase&spm=1018.2226.3001.4450
ubuntu安装cmake:
apt-get install cmake
简单样例
cmake_minimum_required (VERSION 2.8)
project (demo)
add_executable(main main.c)
第一行意思是表示cmake的最低版本要求是2.8,我们安装的是3.10.2;第二行是表示本工程信息,也就是工程名叫demo;第三行比较关键,表示最终要生成的elf文件的名字叫main,使用的源文件是main.c
在终端下切到main.c所在的目录下,然后输入以下命令运行cmake .
PS: 如果想重新生成main,输入make clean就可以删除main这个elf文件。
同一目录下多个源文件
接下来进入稍微复杂的例子:在同一个目录下有多个源文件。
在之前的目录下添加2个文件,testFunc.c和testFunc.h。
cmake_minimum_required (VERSION 2.8)
project (demo)
add_executable(main main.c testFunc.c)
可以类推,如果在同一目录下有多个源文件,那么只要在add_executable里把所有源文件都添加进去就可以了。但是如果有一百个源文件,再这样做就有点坑了,无法体现cmake的优越性,cmake提供了一个命令可以把指定目录下所有的源文件存储在一个变量中,这个命令就是 aux_source_directory(dir var)。
第一个参数dir是指定目录,第二个参数var是用于存放源文件列表的变量。
cmake_minimum_required (VERSION 2.8)
project (demo)
aux_source_directory(. SRC_LIST)
add_executable(main ${
SRC_LIST})
使用aux_source_directory把当前目录下的源文件存列表存放到变量SRC_LIST里,然后在add_executable里调用SRC_LIST(注意调用变量时的写法)。
再次执行cmake和make,并运行main。
不同目录下多个源文件
一般来说,当程序文件比较多时,我们会进行分类管理,把代码根据功能放在不同的目录下,这样方便查找。那么这种情况下如何编写CMakeLists.txt呢?
cmake_minimum_required (VERSION 2.8)
project (demo)
include_directories (test_func test_func1)
aux_source_directory (test_func SRC_LIST)
aux_source_directory (test_func1 SRC_LIST1)
add_executable (main main.c ${
SRC_LIST} ${
SRC_LIST1})
这里出现了一个新的命令:include_directories。该命令是用来向工程添加多个指定头文件的搜索路径,路径之间用空格分隔。
因为main.c里include了testFunc.h和testFunc1.h,如果没有这个命令来指定头文件所在位置,就会无法编译。当然,也可以在main.c里使用include来指定路径,如下
#include "test_func/testFunc.h"
#include "test_func1/testFunc1.h"
只是这种写法不好看。
另外,我们使用了2次aux_source_directory,因为源文件分布在2个目录下,所以添加2次。
动态库和静态库的编译控制
有时只需要编译出动态库和静态库,然后等着让其它程序去使用。让我们看下这种情况该如何使用cmake。
我们会在build目录下运行cmake,并把生成的库文件存放到lib目录下。
cmake_minimum_required (VERSION 3.5)
project (demo)
set (SRC_LIST ${
PROJECT_SOURCE_DIR}/testFunc/testFunc.c)
add_library (testFunc_shared SHARED ${
SRC_LIST})
add_library (testFunc_static STATIC ${
SRC_LIST})
set_target_properties (testFunc_shared PROPERTIES OUTPUT_NAME "testFunc")
set_target_properties (testFunc_static PROPERTIES OUTPUT_NAME "testFunc")
set (LIBRARY_OUTPUT_PATH ${
PROJECT_SOURCE_DIR}/lib)
这里又出现了新的命令和预定义变量,
add_library:
生成动态库或静态库(第1个参数指定库的名字;第2个参数决定是动态还是静态,如果没有就默认静态;第3个参数指定生成库的源文件)set_target_properties: 设置最终生成的库的名称,还有其它功能,如设置库的版本号等等
LIBRARY_OUTPUT_PATH: 库文件的默认输出路径,这里设置为工程目录下的lib目录
好了,让我们进入build目录下运行cmake …,成功后再运行make
PS:前面使用set_target_properties重新定义了库的输出名称,如果不使用set_target_properties也可以,那么库的名称就是add_library里定义的名称,只是连续2次使用add_library指定库名称时(第一个参数),这个名称不能相同,而set_target_properties可以把名称设置为相同,只是最终生成的库文件后缀不同(一个是.so,一个是.a),这样相对来说会好看点。
对库进行链接
既然我们已经生成了库,那么就进行链接测试下。重新建一个工程目录,然后把上节生成的库拷贝过来,然后在在工程目录下新建src目录和bin目录,在src目录下添加一个main.c,整体结构如下,
cmake_minimum_required (VERSION 3.5)
project (demo)
set (EXECUTABLE_OUTPUT_PATH ${
PROJECT_SOURCE_DIR}/bin)
set (SRC_LIST ${
PROJECT_SOURCE_DIR}/src/main.c)
# find testFunc.h
include_directories (${
PROJECT_SOURCE_DIR}/testFunc/inc)
find_library(TESTFUNC_LIB testFunc HINTS ${
PROJECT_SOURCE_DIR}/testFunc/lib)
add_executable (main ${
SRC_LIST})
target_link_libraries (main ${
TESTFUNC_LIB})
这里出现2个新的命令,
- find_library:
在指定目录下查找指定库,并把库的绝对路径存放到变量里,其第一个参数是变量名称,第二个参数是库名称,第三个参数是HINTS,第4个参数是路径,其它用法可以参考cmake文档 - target_link_libraries: 把目标文件与库文件进行链接
使用find_library的好处是在执行cmake …时就会去查找库是否存在,这样可以提前发现错误,不用等到链接时。
cd到build目录下,然后运行cmake … && make,最后进入到bin目录下查看,发现main已经生成,运行之,
ps:在lib目录下有testFunc的静态库和动态库,find_library(TESTFUNC_LIB testFunc ...
默认是查找动态库,如果想直接指定使用动态库还是静态库,可以写成find_library(TESTFUNC_LIB libtestFunc.so ...
或者find_library(TESTFUNC_LIB libtestFunc.a ...
ps: 查看elf文件使用了哪些库,可以使用readelf -d ./xx
来查看
添加编译选项
有时编译程序时想添加一些编译选项,如-Wall,-std=c++11等,就可以使用add_compile_options来进行操作。
cmake_minimum_required (VERSION 2.8)
project (demo)
set (EXECUTABLE_OUTPUT_PATH ${
PROJECT_SOURCE_DIR}/bin)
add_compile_options(-std=c++11 -Wall)
add_executable(main main.cpp)
添加控制选项
有时希望在编译代码时只编译一些指定的源码,可以使用cmake的option命令,主要遇到的情况分为2种:
本来要生成多个bin或库文件,现在只想生成部分指定的bin或库文件
对于同一个bin文件,只想编译其中部分代码(使用宏来控制)
第1种情况
假设我们现在的工程会生成2个bin文件,main1和main2,现在整体结构体如下,
cmake_minimum_required(VERSION 3.5)
project(demo)
option(MYDEBUG "enable debug compilation" OFF)
set (EXECUTABLE_OUTPUT_PATH ${
PROJECT_SOURCE_DIR}/bin)
add_subdirectory(src)
这里使用了option命令,其第一个参数是这个option的名字,第二个参数是字符串,用来描述这个option是来干嘛的,第三个是option的值,ON或OFF,也可以不写,不写就是默认OFF。
然后编写src目录下的CMakeLists.txt,如下
cmake_minimum_required (VERSION 3.5)
add_executable(main1 main1.c)
if (MYDEBUG)
add_executable(main2 main2.c)
else()
message(STATUS "Currently is not in debug mode")
endif()
注意,这里使用了if-else来根据option来决定是否编译main2.c
其中main1.c和main2.c的内容如下,
// main1.c
#include <stdio.h>
int main(void)
{
printf("hello, this main1\n");
return 0;
}
// main2.c
#include <stdio.h>
int main(void)
{
printf("hello, this main2\n");
return 0;
}
然后cd到build目录下输入cmake .. && make
就可以只编译出main1,如果想编译出main2,就把MYDEBUG设置为ON,再次输入cmake .. && make
重新编译。
每次想改变MYDEBUG时都需要去修改CMakeLists.txt,有点麻烦,其实可以通过cmake的命令行去操作,例如我们想把MYDEBUG设置为OFF,先cd到build目录,然后输入cmake .. -DMYDEBUG=ON
,这样就可以编译出main1和main2 (在bin目录下)
第2种情况
假设我们有个main.c,其内容如下,
#include <stdio.h>
int main(void)
{
#ifdef WWW1
printf("hello world1\n");
#endif
#ifdef WWW2
printf("hello world2\n");
#endif
return 0;
}
可以通过定义宏来控制打印的信息,我们CMakeLists.txt内容如下,
cmake_minimum_required(VERSION 3.5)
project(demo)
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
option(WWW1 "print one message" OFF)
option(WWW2 "print another message" OFF)
if (WWW1)
add_definitions(-DWWW1)
endif()
if (WWW2)
add_definitions(-DWWW2)
endif()
add_executable(main main.c)
这里把option的名字保持和main.c里的宏名称一致,这样更加直观,也可以选择不同的名字。通过与add_definitions()的配合,就可以控制单个bin文件的打印输出了。
整体工程结构如下,
cd到build目录下执行cmake .. && make
,然后到bin目录下执行./main
,可以看到打印为空,
接着分别按照下面指令去执行,然后查看打印效果,
cmake … -DWWW1=ON -DWWW2=OFF && make
cmake … -DWWW1=OFF -DWWW2=ON && make
cmake … -DWWW1=ON -DWWW2=ON && make
这里有个小坑要注意下:假设有2个options叫A和B,先调用cmake设置了A,下次再调用cmake去设置B,如果没有删除上次执行cmake时产生的缓存文件,那么这次虽然没设置A,也会默认使用A上次的option值。
所以如果option有变化,要么删除上次执行cmake时产生的缓存文件,要么把所有的option都显式的指定其值。
边栏推荐
- Log statistics (double pointer)
- Codeforces Round #800 (Div. 2)AC
- Codeforces Round #799 (Div. 4)A~H
- QT implementation fillet window
- Spark独立集群Worker和Executor的概念
- Educational Codeforces Round 130 (Rated for Div. 2)A~C
- Base dice (dynamic programming + matrix fast power)
- Research Report on market supply and demand and strategy of double drum magnetic separator industry in China
- 新手必会的静态站点生成器——Gridsome
- js封装数组反转的方法--冯浩的博客
猜你喜欢
随机推荐
Codeforces Round #800 (Div. 2)AC
Codeforces round 797 (Div. 3) no f
Specify the format time, and fill in zero before the month and days
QWidget代码设置样式表探讨
力扣——第298场周赛
Anaconda下安装Jupyter notebook
input 只能输入数字,限定输入
图图的学习笔记-进程
第7章 __consumer_offsets topic
How to insert mathematical formulas in CSDN blog
Pull branch failed, fatal: 'origin/xxx' is not a commit and a branch 'xxx' cannot be created from it
Problem - 1646C. Factorials and Powers of Two - Codeforces
图像处理一百题(11-20)
Li Kou: the 81st biweekly match
Useeffect, triggered when function components are mounted and unloaded
Share an example of running dash application in raspberry pie.
Research Report on hearing health care equipment industry - market status analysis and development prospect prediction
第6章 DataNode
Local visualization tools are connected to redis of Alibaba cloud CentOS server
Problem - 922D、Robot Vacuum Cleaner - Codeforces