Cmake的简单学习历程,用于记录与分享,共勉。
一、单个文件cmake编译
mkdir cmake_test
cd cmake_test
touch main.c
touch CMakeLists.txt
code .
mkdir build
第一句的意思是要求cmake3.0版本以上,可以根据实际设定最低要求版本。
第二句是设置项目名称。
第三句是关键,意思是使用源代码main.c文件,最后生成可执行的elf文件为main。
cd build
cmake ..
ls
./main
make clean
二、同一目录下多个文件cmake编译
1.在cmke_test文件夹下新建fun1.c和fun1.h文件。
2.填入以下内容,函数fun1的功能是打印输入的数值。
3.修改main.c文件,调用fun1来打印。
4修改CMakeLists.txt文件。
可以看到我们把fun1.c文件添加在main.c的后面,表示调用的源文件是main.c和fun1.c。
5.运行cmake编译成make文件,再运行make编译生成main文件,运行main查看输出结果。
可以看到调用fun1函数成功打印了test_fun1:10。
6.从以上的实验可以看出,如果在同一目录下有多个源文件,那么只要在add_executable里把所有源文件都添加进去就可以了。但是如果有很多个源文件,这样每个添加的方式很不合理,无法体现cmake的优越性,cmake提供了一个命令可以把指定目录下所有的源文件存储在一个变量中,这个命令就是 aux_source_directory(dir var)。
第一个参数dir是指定目录,第二个参数var是用于存放源文件列表的变量。
7.新建fun2.c和fun2.h文件,内容如下:
8.修改mian.c文件调用fun2函数。
9.修改CMakeLists.txt文件,
aux_source_directory把当前目录下的源文件存放到变量SRC_LIST里,然后在add_executable里调用SRC_LIST(注意调用变量时的写法 ${变量})
10.编译通过后运行main,可以看到fun1,和fun2都打印了对应的值。
三、多个目录下的不同源文件cmake编译
一般情况下,我们不同功能的源文件会用单独的文件夹保存,这样我们就有在多个目录下调用不同源文件的需求。
1.在cmake_test文件夹下新建fun1和fun2文件夹,在把之前 fun1和fun2的相关文件移动到对应的文件夹下。整理后的文件夹结构如下:
2.修改CMakeLists.txt,增加include_directories (fun1 fun2)命令,意思是告诉编译器可以到fun1和fun2找头文件,多个路径可以用空格键分隔。aux_source_directory命令把两个文件夹保存到对应的变量中。
add_executable把源代码都添加进去。
四、头文件和源文件分开放cmake编译
1.当我们有时候需要把头文件放在同一个文件,而把源文件放在另一个文件夹时,我们可以新建include文件夹用于存放.h文件,新建src文件夹存放.c文件。整理后结构如下图所示(.gitignore文件是Git的文件,与实际项目无关,可以忽略)
2.修改CMakeLists.txt的内容,add_subdirectory (src)命令的意思是添加源文件的目录到工程中,参数src是源文件所在的文件夹名,还有其他参数用于指定外部文件夹在输出文件夹中的位置等,这里不需要用到可以忽略。
3.上面指定了src为源文件的目录,所以当我们运行cmake命令时,就会查找src目录下的CMakeLists.txt文件,这样我们需要在src目录下新建一个CMakeLists.txt,内容如下:
最后一句是set命令,用于定义变量。
第一个参数EXECUTABLE_OUT_PATH
为cmake自带的预定义变量,表示目标二进制可执行文件的存放位置。
第二个参数${PROJECT_SOURCE_DIR}/build表示当前工程根目录下的build文件夹。其中PROJECT_SOURCE_DIR也是cmake自带的预定义变量,表示工程根目录。
4.最后工程项目的文件结构如下图所示:
5.接下来运行cmake .. && make编译,并且查看生成的文件,可以看到main已经生成,还生成了src文件夹,保存src文件夹下的CMakeLists.txt编译生成的文件。
6.运行./main,可以看到程序已经可以运行。
五、cmake编译生成.a静态库
1.在cmake_test文件夹下新建lib文件夹,用于保存库的源文件和头文件。然后把测试的fun相关的源文件和头文件放进lib文件夹,并且在lib文件夹下新建CMakeLists.txt文件。最后的结构如下所示。
2.lib下的CMakeLists.txt文件的内容为:
其中,add_library的功能为生成动态或静态库,第一个参数指定库的名字,输出名字时,cmake会自动把库的前缀和后缀补全,例如此命令会生成名字为‘liblibFun_static.a’的静态库;第二个参数指定是动态库还是静态库,如果没有指定,默认为静态库,SHARED则指定为动态库;第三个参数指定生成库的源文件。
set_target_properties设置输出的名称,如果是动态库还可以设置版本号等。使用此命令可以把上面命令生成的‘liblibFun_static.a’自动重命名为‘libfun.a’。
LIBRARY_OUTPUT_PATH:指定库文件的默认输出路径,这里指定在工程目录下的lib目录。
3.开始编译
cmake .. && make
4.可以看到lib文件夹已经生成了libfun.a的静态库。
六、cmake链接静态库
1.在根目录的CMakeLists.txt文件中添加add_subdirectory (src),表示把告诉cmake去src目录下执行另一个CMakeLists.txt。
2.在src目录下添加CMakeLists.txt文件,内容如下:
以上出现了两个新的命令:
link_directories:告诉cmake要调用的库的路径,
target_link_libraries:链接目标文件和库文件。如果不指定全名,默认是链接动态库,由于我们只有静态库,所以可以不用设置,如果同时存在动态库和静态库,又只想调用静态库,则库名称需要写全名,如target_link_libraries (main libfun.a)
3.为了测试我们的库是否调用成功,我们修改main.c文件来变化输出的内容。将fun1和fun2的输出值都加1。
4.编译
cmake .. && make
5.可以使用ls命令查看已经生成main文件,运行./main成功。
七、cmake添加编译选项
有时编译时需要添加一些编译选项,如src下有多个项目,有时只需要编译一个项目即可,但是每次都修改add_executable参数会比较麻烦,所以增加编译控制选项来修改编译的项目和生成的名称。
1.修改根目录下的CMakeLists.txt文件。
在最前面增加了if逻辑判断,如果没有设置PROJ,则参数FATAL_ERROR输出一个致命性错误并且停止后续的编译,后面的是打印的提示信息,正确的例子:cmake .. -DPROJ=testfun1。如果已经设置了PROJ,则把接收到的PROJ的值打印出来。
project (${PROJ}):设置项目名称,后面可以直接调用变量PROJECT_NAME表示我们设置项目名称。
2.在src文件夹下新建testfun1和testfun2文件夹,并且原来src目录下的main.c文件复制一份后分别移到新建的两个文件夹中,最终文件夹架构为:
3.分别修改testfun1和testfun2下的main.c文件。
4.修改src目录下的CMakeLists.txt文件。
aux_source_directory (${PROJECT_NAME} SRC_LIST):把设置好的项目目录(src/xxx)添加到变量SRC_LIST中。
add_executable (${PROJECT_NAME} ${SRC_LIST}):设置生成文件为工程名xxx。
target_link_libraries (${PROJECT_NAME} fun):链接库。
5.开始编译项目testfun1。并且运行./testfun1。
6.编译项目testfun2,并且运行./testfun2。
八、总结
到此,基础的cmake操作已经学习完毕,但是还有很多cmake高级的用法没有学习到,比如其他命令,或者不同的参数的意义等。还是要在实践中检验,cmake作为跨平台的编译工具真的很值得学习,能够在后续的开发中收益满满。
此文是自我学习的记录与分享,资料参考自网络,如果文中有不对的地方,请不吝留言指正,感谢大家。