CMake入门学习

Posted ych9527

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CMake入门学习相关的知识,希望对你有一定的参考价值。

一、CMakeLists.txt简单编写

  • add_executable
    • 在这个函数之之中,填充elf可执行文件名称,以及依赖的.c文件即可
cmake_minimum_required (VERSION 2.8) //cmake最低版本要求为2.8 
project (demo) //工程名demo 
add_executable(main main.c testFunc.c) //最终生成的elf文件是main,使用的源文件是main.c和testFunc.c
  • aux_source_directory(dir var)

    • add_executable之中添加所有依赖的.c文件,可以cmake . 生成elf可执行文件,但是如果工程项目非常之大,这样是比较麻烦的。因此有了新命令aux_source_directory(dir var),可以将指定目录下的所有源文件存储在一个变量之中

      • dir:指定目录

      • var:用于存放源文件列表的变量

        //此时有多个源文件
        cmake_minimum_required (VERSION 2.8) //cmake最低版本要求为2.8
        
        project (demo) //工程名demo
        
        aux_source_directory(. SRC_LIST) //把当前目录下的源文件存列表存放到变量SRC_LIST里
        
        add_executable(main $SRC_LIST) //在add_executable里调用SRC_LIST
        
      • aux_source_directory()也存在弊端,它会把指定目录下的所有源文件都加进来,可能会加入一些我们不需要的文件,此时我们可以使用set命令去新建变量来存放需要的源文件,如下

        cmake_minimum_required (VERSION 2.8)
        
        project (demo)
        
        set( SRC_LIST
        	 ./main.c
        	 ./testFunc1.c
        	 ./testFunc.c)
        
  • include_directories

    • 向工程添加多个指定头文件的搜索路径,路径之间用空格分隔,和在main.c之中用 //include "name.h"包含头文件是一样的效果

      //有两个文件夹,分别装有一个头文件和一个cpp文件,将其整理分门别类
      
      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)
      

二、正规项目组织方式

  • 正规组织方法如下

    • 源文件放到src目录下

    • 把头文件放入到include文件下

    • 生成的对象文件放入到build目录下

    • 最终输出的elf文件会放到bin目录下,这样整个结构更加清晰

      [ych@2ec71362f5e6 ~]$ tree test/
      test/
      ├── bin //elf可执行文件
      ├── built //生成的对象
      ├── CMakeLists.txt 
      ├── include //头文件
      │   ├── testFunc1.h
      │   └── testFunc.h
      └── src //源文件
          ├── main.c
          ├── testFunc1.c
          └── testFunc.c
      
  • add_subdirectory()

    • 这个命令可以向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制的存放位置

    • add_subdirectory(子文件夹名)表示对子文件夹项目进行cmake编译

      make_minimum_required (VERSION 2.8)
      
      project (demo)
      
      add_subdirectory (src) //表示对子文件夹项目进行cmake编译
      
      • 这里指定src目录下存放了源文件,当执行cmake时,就会进入src目录下去找src目录下的CMakeLists.txt,所以在src目录下也建立一个CMakeLists.txt

      • 使用了2个CMakeLists.txt,最外层的CMakeLists.txt用于掌控全局,使用add_subdirectory来控制其它目录下的CMakeLists.txt的运行

        aux_source_directory (. SRC_LIST) //把当前目录下的源文件存列表存放到变量SRC_LIST里
        
        include_directories (../include) //头文件所在位置
         
        add_executable (main $SRC_LIST) //将变量中的源文件拿出来
        
         // set 用于定义变量
         //本句的意思是,将生成的可执行放到当前工程目录的bin目录之下
        set (EXECUTABLE_OUTPUT_PATH $PROJECT_SOURCE_DIR/bin) 
        
      • EXECUTABLE_OUT_PATH和PROJECT_SOURCE_DIR是CMake自带的预定义变量,其意义如下

        • EXECUTABLE_OUTPUT_PATH :目标二进制可执行文件的存放位置
        • PROJECT_SOURCE_DIR:工程的根目录
    • 如果使用一个CMakeLists.txt,改动如下

      • **小技巧:**在built目录下执行cmake … ,cmake运行生成的所有附带文件就全部集中在built文件夹之下,不会和其它文件混在一起,如果不想要了,直接清空built文件夹即可
      cmake_minimum_required (VERSION 2.8)
      
      project (demo)
      
      set (EXECUTABLE_OUTPUT_PATH $PROJECT_SOURCE_DIR/bin) //将生成的可执行放在bin目录下
      
      aux_source_directory (src SRC_LIST) //将src下所有的源文件添加到变量之中
      
      include_directories (include) //头文件所在位置
       
      add_executable (main $SRC_LIST) //进行生成
      

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

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

    test/
    ├── built
    ├── CMakeLists.txt
    ├── lib
    ├── main.c
    └── testFunc
        ├── testFunc.c
        └── testFunc.h
    
  • CMakeLists.txt文件内容如下

    cmake_minimum_required (VERSION 3.5)
    
    project (demo)
    
    set (SRC_LIST $PROJECT_SOURCE_DIR/testFunc/testFunc.c)// 将工程根目录下的.C文件拿出来放到变量之中
    
    add_library (testFunc_shared SHARED $SRC_LIST) //add_library: 生成动态库或静态库
    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)//库文件的默认输出路径,设置为lib
    
    • add_library: 生成动态库或静态库(第1个参数指定库的名字;第2个参数决定是动态还是静态,如果没有就默认静态;第3个参数指定生成库的源文件)

    • set_target_properties: 设置最终生成的库的名称,还有其它功能,如设置库的版本号等等

      [ych@2ec71362f5e6 lib]$ ls
      libtestFunc.a  libtestFunc.so //可以看到动静态库的名字是很整齐的
       
      如果不使用set_target_properties也可以,那么库的名称就是add_library里定义的名称,只是连续2次使用add_library指定库名称时(第一个参数),这个名称不能相同,而set_target_properties可以把名称设置为相同,只是最终生成的库文件后缀不同(一个是.so,一个是.a),这样相对来说会好看点。
      
    • LIBRARY_OUTPUT_PATH: 库文件的默认输出路径,这里设置为工程目录下的lib目录

四、对库进行链接

  • 新建目录,结构如下

    file/
    ├── bin
    ├── build
    ├── CMakeLists.txt
    ├── src
    │   └── main.c
    └── testFunc
        ├── inc
        └── lib
    
    • CMakeLists.txt内容如下

      cmake_minimum_required (VERSION 3.5)
      
      project (demo)
      
      
      set (EXECUTABLE_OUTPUT_PATH $PROJECT_SOURCE_DIR/bin)//设置生成的elf文件在bin目录之下
      
      set (SRC_LIST $PROJECT_SOURCE_DIR/src/main.c)//将点C文件添加至变量之中
      
      
      include_directories ($PROJECT_SOURCE_DIR/testFunc/inc)//添加头文件的位置
      
      find_library(TESTFUNC_LIB testFunc HINTS $PROJECT_SOURCE_DIR/testFunc/lib)//在指定目录下查找指定库
       
      //在lib目录下有testFunc的静态库和动态库,find_library(TESTFUNC_LIB testFunc ...默认是查找动态库,如果想直接指定使用动态库还是静态库,可以写成find_library(TESTFUNC_LIB libtestFunc.so ...或者find_library(TESTFUNC_LIB libtestFunc.a
      
      add_executable (main $SRC_LIST)//将变量中的.C拿出来
      
      target_link_libraries (main $TESTFUNC_LIB)//把目标文件与库文件进行链接
      
      • find_library: 在指定目录下查找指定库,并把库的绝对路径存放到变量里
        • 第一个参数是变量名称
        • 第二个参数是库名称
        • 第三个参数是HINTS
        • 第4个参数是路径
      • target_link_libraries: 把目标文件与库文件进行链接

五、添加编译选项

  • add_compile_options()

    • 有时编译程序时想添加一些编译选项,如-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种:

    1. 本来要生成多个bin或库文件,现在只想生成部分指定的bin或库文件
    2. 对于同一个bin文件,只想编译其中部分代码(使用宏来控制)
  • 情况一

    • 工程架构如下
    proc/
    ├── bin
    ├── build
    ├── CMakeLists.txt
    └── src
        ├── CMakeLists.txt
        ├── main1.c
        └── main2.c
    
    • 外层CMakeLists.txt

      cmake_minimum_required(VERSION 2.8)
      
      project(demo)
      
      #第一个参数:option名字
      #第二个参数:字符串,描述option干啥的
      #第三个:optins的值,不写就是OFF
      option(MYDEBUG "enable debug compilation" OFF)
      
      set (EXECUTABLE_OUTPUT_PATH $PROJECT_SOURCE_DIR/bin) //将elf放置bin目录之下
      
      add_subdirectory(src) # 对子文件夹src项目进行cmake编译
      
      • option命令
        • 第一个参数是这个option的名字
        • 第二个参数是字符串,用来描述这个option是来干嘛的
        • 第三个是option的值,ON或OFF,也可以不写,不写就是默认OFF
    • src里层CMakeLists.txt

      cmake_minimum_required(VERSION 2.8)
      add_executable(main1 main1.c)
      
      if(MYDEBUG) //如果option为NO,就编译main2
          add_executable(main2 main2.c)
      else()
          message(STATUS "Currently is not in debug mode")    
      endif()
      
    • cmake … -DMYDEBUG=ON

      • 直接在编译的时候设置option的值

        cmake .. && make
        ls ../bin/
        main1
        
        cmake .. -DMYDEBUG=ON
        ls ../bin/
        main1  main2
        
        
  • 情况二

    • 假设有如下文件

      #include <stdio.h>
      
      int main(void)
      
      #ifdef WWW1
          printf("hello world1\\n");
      #endif    
      
      #ifdef WWW2     
          printf("hello world2\\n");
      #endif
      
          return 0;
      
      
    • add_definitions()

    • 通过宏定义来打印文件,CMakeLists.txt文件内容如下

    • 通过与add_definitions()的配合,就可以控制单个bin文件的打印输出了

    cmake_minimum_required(VERSION 3.5)
    
    project(demo)
    
    set (EXECUTABLE_OUTPUT_PATH $PROJECT_SOURCE_DIR/bin) #生成的可执行文件放置bin目录之中
    
    #设置option的名字
    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)
    
    // 效果如下
    cmake .. -DWWW1=ON -DWWW2=OFF
    ../bin/main 
    hello world1
    
    cmake .. -DWWW1=OFF -DWWW2=ON  
    ../bin/main 
    hello world2
    
    cmake .. -DWWW1=ON -DWWW2=ON
    ../bin/main 
    hello world1
    hello world2
    

以上是关于CMake入门学习的主要内容,如果未能解决你的问题,请参考以下文章

CMake入门学习

CMake入门学习

CMake入门学习

CMake 入门学习4 软件包管理

CMake 入门学习4 软件包管理

Windows下配置CMake(入门级教程,适合新人收藏学习)