Linux下的cmake使用,从简单的用法到一个基本工程的创建

目录

前言:

一、安装CMake

         二、cmake的使用

        1、在一个目录文件下单个源文件

        2.同一目录下多个源文件

         3.不同目录下多个源文件

        4.一个正规一点工程文件

         二、动态库和静态库的编译控制

        三、对库进行连接

        总结


前言:

        CMake是开源、跨平台的构建工具,可以让我们通过编写简单的配置文件去生成本地的Makefile,这个配置文件是独立于运行平台和编译器的,这样就不用亲自去编写Makefile了,而且配置文件可以直接拿到其它平台上使用,无需修改,非常方便。

一、安装CMake

在Ubuntu中使用指令:sudo apt install cmake

     使用普通用户不能直接使用 apt install cmake 指令下载,会提示权限不够,so,在这里使用sudo来增加用户使用权限。

 在 Centos 中我是先使用su - root 切换到root 用户,在使用指令:yum install cmake

下载完成后可使用指令:cmake -version 查看cmake的版本号

下面我将在Ubuntu系统中进行演示。

 二、cmake的使用

 1、在一个目录文件下单个源文件

         首先让我们从最简单的代码入手,先来体验下cmake是如何操作的。编写main.c:

#include <stdio.h>

int main()
{
    printf("HELLO WORLD\n");
    return 0;
}

           在用一目录下创建CMakeLists.txt文件,内容如下:        

#在linux中 # 代表注释
cmake_minimum_required (VERSION 3.5)    #指定使用cmake的版本要在3.5以上

project(demo)        #创建的工程名字

add_executable(main main.c) #对源文件进行链接,生成二进制可执行文件
                            #参数1:文件名;参数2:要链接的源文件

         以上都创建好后,使用cmake . 指令,在cmake 后有一个点,. 表示当前目录

        

        ls 一下,可以看到生成有Makefile文件,和一些cmake相关的文件

       

       然后再使用make命令,可以看到以下输出内容:

       

       ls 一下,可以看到生成了名为 main 可执行文件 

       

       接下来输入 ./main 运行该可执行文件,会输出以下信息:

       

2.同一目录下多个源文件

在该目录下添加如下文件,使用指令tree,具体用法可自行百度。

若没有该指令,看使用:sudo apt install tree 进行下载。

 

a.c文件内容如下:        

#include<stdio.h>

void fun_a()
{
	printf("This is A\n");
}

a.h 文件内容如下:

#ifndef __A_H__
#define __A_H__

void fun_a();

#endif

         剩下的 b.c 与 b.h 文件内容与之差不多,这里为了内容不碎杂就不展示了。

        接下来修改CMakeLists文件,添加刚刚创建好的 a.c  b.c 文件,内容如下:

cmake_minimum_required (VERSION 3.5) //指定使用cmake的版本要在3.5以上

project(demo)        //创建的工程名字

add_executable(main main.c a.c b.c) //对源文件进行链接,生成二进制可执行文件
                            //参数1:文件名;参数2:要链接的源文件

         在重新运行camke .  然后再make一下,会输出一下信息

                再执行生成的可执行文件,输出以下信息:

        

         这样虽然可以将多个源文件进行添加链接,但如果有上百上千个源文件呢?一个一个的进行添加,这样肯定是不现实的。so,cmake提供了一个命令可以把指定目录下所有的源文件存储在一个变量中,这个命令就是 aux_source_directory(dir var)。参数1:dir是指定目录,参数2:var是用于存放源文件列表的变量。

         接下来我们重新修改CMakeLists文件,内容如下:

cmake_minimum_required (VERSION 3.5) #指定使用cmake的版本要在3.5以上

project(demo)        #创建的工程名字

aux_source_directory(. SRC_LIST)     #把当前目录下的源文件存列表存放到变量SRC_LIST里,然后在                
                                     #add_executable里调用SRC_LIST(注意调用变量时的写法)。

add_executable(main ${SRC_LIST})    #对源文件进行链接,生成二进制可执行文件
                                    #参数1:文件名;参数2:要链接的源文件



        重新执行cmake . 和 make ,并运行main

        

        aux_source_directory()也存在弊端,它会把指定目录下的所有源文件都加进来,可能会加入一些我们不需要的文件,此时我们可以使用set命令去新建变量来存放需要的源文件。

cmake_minimum_required (VERSION 2.8)

project (demo)

set( SRC_LIST
	 ./main.c
	 ./testFunc1.c
	 ./testFunc.c)

add_executable(main ${SRC_LIST})

 3.不同目录下多个源文件

        为了方便管理,在程序文件比较多的时,我们会进行分类管理,把代码根据功能放在不同的目录下,这样方便查找。

        我们把之前的源文件整理一下(新建2个目录a和b),整理好后整体文件结构如下:

         

        CMakeLists.txt和main.c在同一目录下,内容修改成如下所示:

        另外,我们使用了2次aux_source_directory,因为源文件分布在2个目录下,所以添加2次。

cmake_minimum_required (VERSION 3.5)

project (demo1)

include_directories (a b) #该命令是用来向工程添加多个指定头文件的搜索路径,路径之间用空格分隔。

aux_source_directory (a SRC_LIST)
aux_source_directory (b SRC_LIST1)

add_executable (main main.c ${SRC_LIST} ${SRC_LIST1})

         再重新执行cmake . 和 make ,运行main得到的结果和上面一致。

4.一个正规一点工程文件

        正规一点来说,一般会把源文件放到src目录下,把头文件放入到include文件下,生成的对象文件放入到build目录下,最终输出的elf文件会放到bin目录下,这样整个结构更加清晰。

                                

现在我们来再次重新修改一下CMakeLists文件,内容如下:

cmake_minimum_required(VERSION 2.8)

project(dome2)

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

aux_source_directory(src SRC_LIST)

include_directories(include)

add_executable(main ${SRC_LIST})

这里又出现一个新的命令set,是用于定义变量的,EXECUTABLE_OUT_PATH和PROJECT_SOURCE_DIR是CMake自带的预定义变量,其意义如下,

EXECUTABLE_OUTPUT_PATH :目标二进制可执行文件的存放位置
PROJECT_SOURCE_DIR:工程的根目录
所以,这里set的意思是把存放elf文件的位置设置为工程根目录下的bin目录。(cmake有很多预定义变量,详细的可以网上搜索一下)

下面来运行cmake,不过这次先让我们切到build目录下,然后输入以下命令,

cmake ..          //在build目录下编译上一级目录下的MCakeLists文件。
Makefile会在build目录下生成,然后在build目录下运行make,

        

        然后去到 bin 目录下运行main,输出结果如下:

        

         这里解释一下为什么在build目录下运行cmake?从前面几个case中可以看到,如果不这样做,cmake运行时生成的附带文件就会跟源码文件混在一起,这样会对程序的目录结构造成污染,而在build目录下运行cmake,生成的附带文件就只会待在build目录下,如果我们不想要这些文件了就可以直接清空build目录,非常方便。

 二、动态库和静态库的编译控制

        有时只需要编译出动态库和静态库,然后等着让其它程序去使用。让我们看下这种情况该如何使用cmake。首先按照如下重新组织文件:
        

        下面我们重新编写CMakeLists文件,内容如下

cmake_minimum_required(VERSION 3.5)  #指定cmake版本及以上 

project(dome1)  #工程名

#指定elf程序生成的目录 
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

#指定库文件生成的目录 
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

#生成连接文件动态库的变量--- 参数1:接收变量,参数2:生成动态库变量,参数3:要链接的文件
add_library(shared SHARED ${PROJECT_SOURCE_DIR}/src/a.c
                          ${PROJECT_SOURCE_DIR}/src/b.c)

                  #生成连接文件静态库的变量--- 参数1:接收变量,参数2:生成静态>库变量,参数3:要链接的文件
add_library(static STATIC ${PROJECT_SOURCE_DIR}/src/a.c
                          ${PROJECT_SOURCE_DIR}/src/b.c)

                  #生成动态库文件,并将其命名为“test”
set_target_properties(shared PROPERTIES OUTPUT_NAME "test")

 这里出现了add_library()与set_target_properties()两个新的命令,这里我以自己的理解添加了注释,更多解释可以自行百度。

add_library: 生成动态库或静态库(第1个参数指定库的名字;第2个参数决定是动态还是静态,如果没有就默认静态;第3个参数指定生成库的源文件)
set_target_properties: 设置最终生成的库的名称,还有其它功能,如设置库的版本号等等
LIBRARY_OUTPUT_PATH: 库文件的默认输出路径,这里设置为工程目录下的lib目录。

编写好后,去到build目录运行cmake .. 和 make

        

 去到 lib 目录,ls 一下可以看到生成了两个以 .a 和 .so结尾的文件,

        

PS:前面使用set_target_properties重新定义了库的输出名称,如果不使用set_target_properties也可以,那么库的名称就是add_library里定义的名称,只是连续2次使用add_library指定库名称时(第一个参数),这个名称不能相同,而set_target_properties可以把名称设置为相同,只是最终生成的库文件后缀不同(一个是.so,一个是.a),这样相对来说会好看点。

三、对库进行连接

        既然我们已经生成了库,那么就进行链接测试下。重新建一个工程目录,然后把上节生成的库拷贝过来,然后在在工程目录下新建src目录和bin目录,在src目录下添加一个main.c,整体结构如下,

         

         main.c 内容如下:

#include <stdio.h>
#include "a.h"
#include "b.h"

int main()
{
	fun_a();
	fun_b();

	return 0;
}

再次重新编写CMakeLists文件,内容如下:

cmake_minimum_required(VERSION 3.5)

project(dome5)

#指定目标二进制可执行文件的存放位置
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

#指定库文件的存放位置
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

#指定查找库文件的位置,参数1:查找到接收的变量,参数2:要查找库文件的名字,参数3:预定变量,参数4:指定的目录
#默认与动态库文件链接,若要对静态库链接可带后缀名
find_library(TEST_LIB test HINTS ${PROJECT_SOURCE_DIR}/lib)

#指定存放头文件目录
include_directories(include)

#指定要链接的源文件,参数1:接收变量,参数2:指定源文件存放的目录
set(SRC_LIST ${PROJECT_SOURCE_DIR}/src/main.c)

#链接main文件,生成二进制可执行文件。参数1:生成二进制文件的名字,参数2:链接的main文件
add_executable(main ${SRC_LIST})

#库文件与二进制可执行文件进行链接。参数1:二进制可执行文件名,参数2:库文件名
target_link_libraries(main ${TEST_LIB})

        这里出现2个新的命令,

        find_library: 在指定目录下查找指定库,并把库的绝对路径存放到变量里,其第一个参数是变量名称,第二个参数是库名称,第三个参数是HINTS,第4个参数是路径,其它用法可以参考cmake文档
        target_link_libraries: 把目标文件与库文件进行链接
使用find_library的好处是在执行cmake ..时就会去查找库是否存在,这样可以提前发现错误,不用等到链接时。

        我们去到build 目录再次执行 cmake.. 和 cmake 后,回到bin目录运行main,输出一下内容:

        

        测试成功。

PS:在lib目录下有testFunc的静态库和动态库,find_library(TESTFUNC_LIB testFunc ...默认是查找动态库,如果想直接指定使用动态库还是静态库,可以写成find_library(TESTFUNC_LIB libtestFunc.so ...或者find_library(TESTFUNC_LIB libtestFunc.a ...

        查看elf文件使用了哪些库,可以使用readelf -d ./xx来查看。

        自己对生成库的一个小理解:对一个工程而言,会涉及到数个源文件,而有时候我们并不需要将所以的源文件都要对其进行添加链接,对此我们就可以将工程的源文件进行模块划分,生成一个个库文件,在需要到时再进行添加,或者说在其他工程也需要到该源文件时,我们就可以直接拷贝过来直接使用,这样就很难大程序的节省了我们的时间。

总结

以上是自己学习CMake的一点学习记录,通过简单的例子让大家入门CMake,学习的同时也阅读了很多网友的博客。CMake的知识点还有很多,具体详情可以在网上搜索。总之,CMake可以让我们不用去编写复杂的Makefile,并且跨平台,是个非常强大并值得一学的工具。

如果有写的不对的地方,希望能留言指正,谢谢阅读。

 

  • 10
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值