CMake编译工具快速入门

1、CMake简介

CMake管理一个程序项目,实际上就是在源码根目录中建立一个CMakeLists.txt文件作为根节点,再通过add_subdirectory(subdir)包含其下级源码子目录,而下级源码目录中的CMakeLists.txt进一步通过add_subdirectory(subdir)包含其下级源码目录,最终把所有源码都串接在一起,同时所有的CMakeLists.txt文件也都串接在一起。

另外,每一个CMakeLists.txt都要求通过include_directories来为当前文件夹下的源码设置头文件搜索路径。对于多个源码子目录(多个CMakeLists.txt)的项目,任意指定其中一个源码目配置为编译成可执行文件,其他源码目录配置为编译成静态lib,并在任意一个CMakeLists.txt中,通过target_link_libraries将可执行文件跟所有的库(包括静态或动态库)连接在一起。

CMake vs Autotools:

首先,拿他们对比本身没有太多的意义,他们都是产生makefile文件的工程编译管理工具。CMake产生的晚,解决了很多autotools工具的问题。

autotools是一个工具集具有强大的灵活性,但是因为步骤太多,配置繁琐,产生了很多的替代方案,cmake是其中最优秀的之一。

早期没有那么多选项,大量的开源项目都是采用autotools管理工程。

2、CMakeLists.txt

项目的每个源码目录下都要配置一个CMakeLists.txt文件,头文件目录下不需要。

如果项目根目录下没有源码,但是有多个源码子目录,那么项目根目录下也需要配置一个CMakeLists.txt,以便将所有源码子目录串接起来。

CMakeLists.txt常用指令以及变量:

  • 指令 project(project_name)

设置项目名称。这条指令一定要放在最顶层的CMakeLists.txt中,千万不要在下级CMakeLists.txt中再次使用project指令,否则会导致PROJECT_SOURCE_DIR变量错乱。

  • 变量 PROJECT_SOURCE_DIR

指向最顶层CMakeLists.txt所在目录。

  • 指令 cmake_minimum_required

cmake最低版本需求,不加入此行会受到警告信息。

只在根节点CMakeLists.txt中设置即可。

  • 指令 include_directories (include_path)

设置当前目录下的源码在编译时,对头文件的搜索路径,多个路径用空格隔开。

每个CMakeLists.txt都需要设置当前目录下源码对头文件的搜索路径。

  • 指令 link_directories (lib_path)          

设置当前目录下的源码在编译时,对库文件的搜索路径,多个路径用空格隔开。

官方不推荐这条指令,取代方案是find_library。

例如:

find_library(libpath_jsoncpp  libjsoncpp.so  ${PROJECT_SOURCE_DIR}/lib)

首先从系统库路径搜索libjsoncpp.so,然后从自定义指定路径搜索,一旦匹配成功即返回路径到变量libpath_jsoncpp。

  • 指令 set(des src)

变量复制,目标变量不存在则自动创建。

例如:

set(lib_path "hello")  //赋值变量lib_path为字符串"hello"
message("lib_path=${lib_path}") 输出变量内容

3、实例1

flat项目,所有源码都在同一个文件夹下。

demo文件夹下,有三个文件,main.c, test.c, test.h:

demo/
   |– main.c
   |– test.c
   |– test.h

在demo文件夹下建立CMakeLists.txt文件,编辑内容如下:

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ cmake_minimum_required (VERSION 2.6)          #cmake最低版本需求,不加入此行会受到警告信息
┃ project(hello)                                #设置项目名(不是指最终编译出的可执行文件名)
┃ aux_source_directory( .  src_list)            #把当前目录(.)下所有源代码文件和头文件路径赋值给变量src_list;
┃ set (EXECUTABLE_OUTPUT_PATH   ${PROJECT_SOURCE_DIR}/bin ) #设置应用程序输出路径
┃ add_executable(demo ${src_list})                          #将目标源码编译成可执行文件demo
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

在demo目录下执行cmake <根节点CMakeLists.txt路径>,就会将在当前目录下生成Makefile以及其他相关配置文件,然后在执行make命令即可调用Makefile进行编译。

为了避免在代码目录下生成一大堆目标文件,可以在项目根目录下创建一个子目录,如build,然后进入子目录,并运行cmake ..命令,哲理的..表示根节点CMakeLists.txt路径在上级目录。

执行完cmake ..,所有的配置信息文件包括Makefile都会生成在当前目录(build)下,然后再执行make命令,就会在build目录下生成目标文件。若要清理目标文件,直接删除build文件夹即可。

注:

  1. EXECUTABLE_OUTPUT_PATH为CMake的系统变量,用于设定应用程序的数据路径;
  2. PROJECT_SOURCE_DIR为包含project(项目名)的最近一个CMakeLists.txt文件所在的文件夹,通常project(...)只要在根CMakeLists.txt文件中定义一次就可以了,所以PROJECT_SOURCE_DIR通常指向项目根目录。

4、实例2

层次化结构:

demo/
  ┣main.c
  ┣include/
      ┣test1.h
      ┣test2.h
  ┣src/
     ┣test1.c
     ┣test2.c

在demo文件夹下建立CMakeLists.txt文件,编辑内容如下:

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ cmake_minimum_required (VERSION 2.6)          #cmake最低版本需求,不加入此行会受到警告信息
┃ project(hello)                                #设置项目名(不是指最终编译出的可执行文件名)
┃        
┃ aux_source_directory( .  src_list)            #把当前目录(.)下所有源代码文件和头文件路径赋值给变量src_list;
┃ include_directories (include /usr/include)    #设置头文件搜索路径,多个路径用空格隔开,
┃                                               #若有相对路径则是基于当前CMakeLists.txt所在目录;
┃ add_subdirectory(src)                         #添加其他源码子目录
┃ set (EXECUTABLE_OUTPUT_PATH   ${PROJECT_SOURCE_DIR}/bin ) #设置应用程序输出路径
┃ add_executable(demo ${src_list})                          #将目标源码编译成可执行文件demo
┃ link_directories (${PROJECT_SOURCE_DIR}/lib)              #设置库文件搜索路径  
┃ target_link_libraries (demo libpthread.so libsqlite3.so libcurl.so libjsoncpp.so) #设置添加到目标程序中去的库(静态或动态)    
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

在src文件夹下建立CMakeLists.txt文件,编辑内容如下:

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃aux_source_directory(. dir_sub_src)   #把当前目录(.)下所有源代码文件和头文件加入变量dir_sub_src
┃include_directories (../include)      #为当前目录下的源码文件设置头文件搜索路径,相对路径基于当前CMakeLists.txt所在目录。
┃add_library (testlib ${dir_sub_src})  #把当前目录源码编译成静态库testlib
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

5、实例3

层次化结构(多个源码子目录,但项目根目录下没源码文件的情况):

 demo/
  ┣include/
      ┣test1.h
      ┣test2.h
  ┣app/
      ┣main.c
  ┣src/
      ┣test1.c
      ┣test2.c

在demo文件夹下建立CMakeLists.txt根节点文件,编辑内容如下:

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ cmake_minimum_required (VERSION 2.6)          #cmake最低版本需求,不加入此行会受到警告信息
┃ project(hello)                                #设置项目名(不是指最终编译出的可执行文件名)
┃ add_subdirectory(app)                         #添加源码子目录app(只允许添加子目录,而且一次只能添加一个子目录)
┃ add_subdirectory(src)                         #添加源码子目录src(只允许添加子目录,而且一次只能添加一个子目录)
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

在demo/app文件夹下建立CMakeLists.txt根节点文件,编辑内容如下:

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ aux_source_directory(.  src_list)             #把当前目录(.)下所有源代码文件和头文件路径赋值给变量src_list;
┃ include_directories (../include)              #设置头文件搜索路径,多个路径用空格隔开
┃ set (EXECUTABLE_OUTPUT_PATH   ${PROJECT_SOURCE_DIR}/bin ) #设置应用程序输出路径
┃ add_executable(demo ${src_list})                    #将目标源码编译成可执行文件demo
┃ link_directories (${PROJECT_SOURCE_DIR}/lib)        #设置库文件搜索路径  
┃ target_link_libraries (demo testlib libpthread.so)  #设置添加到目标程序中去的库(静态或动态)  
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

注:

在调用add_executable指定输出可执行文件时,要在同一个CMakeLists.txt文件中通过set (EXECUTABLE_OUTPUT_PATH)设置应用程序输出路径。在调用target_link_libraries连接库文件时,要在同一个CMakeLists.txt文件中通过link_directoriesadd设置库文件搜索路径。

在demo/src文件夹下建立CMakeLists.txt文件,编辑内容如下:

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃aux_source_directory(. dir_sub_src)   #把当前目录(.)下所有源代码文件和头文件路径赋值给变量dir_sub_src
┃include_directories (../include)      #为当前目录下的源码文件设置头文件搜索路径,相对路径基于当前CMakeLists.txt所在目录。
┃add_library (testlib ${dir_sub_src})  #把当前目录源码编译成静态库testlib
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

对于一些全局公共配置,只需要在根节点CMakeLists.txt中配置即可,例如下面这些公共配置:

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ cmake_minimum_required (VERSION 2.6)          #cmake最低版本需求,不加入此行会受到警告信息
┃ project(hello)                                #设置项目名(不是指最终编译出的可执行文件名)
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

猜你喜欢

转载自blog.csdn.net/qq_35029061/article/details/131010820