怎样编写一个CMakeLists.txt文件(由简到难,较详细)

打印 上一主题 下一主题

主题 1049|帖子 1049|积分 3147

在Linux体系下,经常使用CMakeLists.txt文件来链接、编译C++工程,大部分人clone的代码里都是有CMakeLists.txt文件的,只必要cmake .. 和make就完事了,但在工作中,你必须要有从无到有编写CMakeLists.txt文件的本领。
一、简朴CMakeLists.txt文件的编写

1.先看文件目录


在repository4文件夹下存在好几个C++相关的文件和一个CMakeLIsts.txt文件,文件清单如下表:
文件(夹)名文件(夹)范例/用途repository4工程文件夹calc.h头文件,声明了几个函数界说add.cpp/div.cpp/mult.cpp/sub.cpp头文件中声明的函数的实现test_calc.cpp调用前面实现的几个函数CMakeLists.txt编写此文件,用于此工程的链接和编译 以上除CMakeLists.txt文件以外的文件自己在保证调用、语法、功能正确的前提下任意编写。
2.编写CMakeLists.txt文件

(1)编写简朴CMakeLists.txt文件的流程
     (2)编写简朴CMakeLists.txt文件
  1. cmake_minimum_required(VERSION 3.16)  #cmake最低版本要求,不得高于你计算机安装的版本
  2. set(CMAKE_CXX_STANDARD 11)  #指定C++标准
  3. project(test_calculate)  #设置项目名称“test_calculate”
  4. aux_source_directory(${PROJECT_SOURCE_DIR} SRC)  #添加源文件到变量SRC中,宏PROJECT_SOURCE_DIR指的是正在编写的CMakeLists.txt文件所在的文件夹
  5. include_directories(${PROJECT_SOURCE_DIR})  #制定头文件路径,${}符号是CMake语法中从变量取出变量的值的符号
  6. add_executable(test_cacl ${SRC})  #使用源文件生成可执行文件test_cacl,源文件的路径及名称位于SRC变量中
复制代码
这黑白常简朴的一个CMakeLists.txt文件,仅用于新手了解cmake的体验装
3.建立build文件夹,运行CMakeLists.txt文件,测试文件是否编写正确

建立build文件夹是为了保证源码文件的整洁,链接、编译天生的文件均在build文件夹下。
  1. mkdir build  #在repository4文件夹下执行此命令生成build文件夹
  2. cd build  #进入build文件夹
  3. cmake ..  #使用CMakeLists.txt文件链接项目
  4. make  #编译项目 生成可执行文件
  5. ./test_calc  #在编译正确后,运行生成的可执行文件test_calc
复制代码
运行截图如下

4.稍微改进一下,显得整洁点

上面文件夹的内容显得比力凌乱,对其举行整理,整理后的文件目录如下,文件内容不变

将函数实现的cpp文件放在src目录下,将头文件放在include目录下
对应的CMakeLists.txt文件的内容如下
  1. cmake_minimum_required(VERSION 3.16)
  2. set(CMAKE_CXX_STANDARD 11)
  3. project(test_calculate)
  4. aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC)  #修改
  5. include_directories(${PROJECT_SOURCE_DIR}/include)  #修改
  6. add_executable(test_cacl ${SRC} test_calc.cpp)  #修改
复制代码
执行命令不变!
二、编写嵌套CMakeLists.txt文件

1.先看文件目录


在repository5文件夹下存在好几个C++相关的文件和一个CMakeLIsts.txt文件,文件清单如下表:
文件(夹)名文件夹中的文件文件(夹)范例/用途repository5以下文件(夹)工程文件夹build链接、编译产生的文件用于存放链接、编译时产生的文件includecalc.h / convert.h头文件,声明了几个函数界说calccalc.cpp / CMakeLists.txt头文件calc.h中声明的函数的实现convconv.cpp / CMakeLists.txt头文件convert.h中声明的函数的实现test_calctest_calc.cpp / CMakeLists.txt调用calc.cpp实现的几个函数test_convtest_conv.cpp / CMakeLists.txt调用conv.cpp实现的几个函数CMakeLists.txt编写此文件,用于此工程的链接和编译 这些文件总共天生两个可执行文件,一个是计算相关的calc,一个是转换大小写字母的conv,我们必要在实现头文件中声明的那些函数的源文件所在的文件夹调用头文件中函数的测试文件(main函数)所在的文件夹 以及 工程项目repository文件夹中创建CMakeLists.txt文件。
以上除CMakeLists.txt文件以外的文件自己在保证调用、语法、功能正确的前提下任意编写。
2.编写嵌套CMakeLists.txt文件

(1)编写嵌套CMakeLists.txt文件的流程

(2)编写外层CMakeLists.txt文件
  1. cmake_minimum_required(VERSION 3.16)
  2. set(CMAKE_CXX_STANDARD 11)
  3. project(test)
  4. #添加子文件夹
  5. add_subdirectory(calc)
  6. add_subdirectory(conv)
  7. add_subdirectory(test_calc)
  8. add_subdirectory(test_conv)
复制代码
(3)编写内层CMakeLists.txt文件
a.编写calc文件夹中的CMakeLists.txt文件
  1. cmake_minimum_required(VERSION 3.16)
  2. set(CMAKE_CXX_STANDARD 11)
  3. project(calc)
  4. aux_source_directory(./ SRC)  # ./=${PROJECT_SOURCE_DIR},在当前CMakeLists.txt所在文件夹(calc)中搜索源文件,并放置在SRC变量中
  5. include_directories(../include)  #添加头文件路径,../表示上一层目录,../include即在上一层目录下的include中
  6. add_library(calculate STATIC ${SRC})  #取出SRC变量的值,生成一个名为calculate的静态库
复制代码
b.编写test_calc文件夹中的CMakeLists.txt文件
  1. cmake_minimum_required(VERSION 3.16)
  2. set(CMAKE_CXX_STANDARD 11)
  3. project(calculator)
  4. aux_source_directory(./ SRC)
  5. include_directories(../include)
  6. link_libraries(calculate)  #链接生成的静态库
  7. add_executable(calculator ${SRC})  #生成名为calculator的可执行文件
复制代码
与calc相关的CMakeLists.txt就写好了
c.剩下的两个与conv相关的CMakeLists.txt文件其实与上面两个编写是一样的,只是更改一下相关名称而已,其内容如下:
conv文件夹中的CMakeLists.txt:
  1. cmake_minimum_required(VERSION 3.16)
  2. set(CMAKE_CXX_STANDARD 11)
  3. project(conv)
  4. aux_source_directory(./ SRC)
  5. include_directories(../include)
  6. add_library(convert STATIC ${SRC})
复制代码
test_conv文件夹中的CMakeLists.txt:
  1. cmake_minimum_required(VERSION 3.16)
  2. set(CMAKE_CXX_STANDARD 11)
  3. project(convertor)
  4. aux_source_directory(./ SRC)
  5. include_directories(../include)
  6. link_libraries(convert)
  7. add_executable(convertor ${SRC})
复制代码
3.在项目文件夹下创建build文件夹,测试刚才编写的CMakeLists.txt文件

  1. mkdir build
  2. cd build
  3. cmake ..
  4. make
  5. ./test_conv/convertor
复制代码

4. 同样改进一下,指定一下天生的库、可执行文件的存放目录

(1)在外层CMakeLists.txt文件中界说要存放天生的库、可执行文件的变量以及路径
  1. set(LIBPATH ${PROJECT_SOURCE_DIR}/lib)  #设置生成库的路径,并放置在变量LIBPATH中
  2. set(EXEPATH ${PROJECT_SOURCE_DIR}/bin)  #设置可执行文件的路径,并放置在变量EXEPATH中
  3. #文件夹bin和lib会自动生成
复制代码
(2)在内层CMakeLists.txt文件中使用上面两个全局变量LIBPATH和EXEPATH
  1. set(LIBRARY_OUTPUT_PATH ${LIBPATH})  #设置库的生成目录,在calc和conv文件夹下的CMakeLists.txt中添加
  2. link_directories(../lib)  #设置要链接的库的目录,在test_calc和test_conv文件夹下的CMakeLists.txt中添加
  3. set(EXECUTABLE_OUTPUT_PATH ${EXEPATH})  #设置可执行文件的生成目录,在test_calc和test_conv文件夹下的CMakeLists.txt中添加
复制代码
(3)添加后在当前工程的build路径下执行以下命令来测试CMakeLists.txt文件
  1. cmake ..
  2. make
  3. ../bin/convertor  #当前在build文件夹中,bin文件夹和build并列,因此需要../
复制代码

三、cmake常用函数

1.底子命令

命令功能备注#单行解释#[ [ ] ]块解释${ }从变量或宏中取出其值例如:${PROJECT_SOURCE_DIR}cmake_minimum_required(VERSION 3.0)指定使用的 cmake 的最低版本非必须,如果不加可能会有告诫project()界说工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言如果不必要这些都是可以忽略的,只必要指定出工程名字即可add_executable(可执行步伐名 源文件名称)使用后面的源文件天生可执行步伐源文件名可以是一个也可以是多个,如有多个可用空格或;间隔set(VAR [value]将[value]存入变量VAR中[value]可以是多个,空格或;间隔set(CMAKE_CXX_STANDARD 11)设置C++的标准CMAKE_CXX_STANDARD是预界说的宏aux_source_directory(< dir > < variable >)dir:要搜刮的目录,variable:将从dir目录下搜刮到的源文件列表存储到该变量中file(GLOB/GLOB_RECURSE 变量名 要搜刮的文件路径和文件范例)GLOB: 将指定目录下搜刮到的满足条件的全部文件名天生一个列表,并将其存储到变量中,GLOB_RECURSE:递归搜刮指定目录,将搜刮到的满足条件的文件名天生一个列表,并将其存储到变量中GLOB与GLOB_RECURSE 二选一include_directories(headpath)添加含头文件路径headpath:头文件路径add_library(库名称 STATIC/SHARED 源文件1 [源文件2] …)使用源文件天生库STATIC:静态库,SHARED:动态库 2.包罗库文件

  1. link_libraries(<static lib> [<static lib>...])
复制代码
参数1:指定出要链接的第一个静态库的名字,可以是全名 libxxx.a,也可以是掐头(lib)去尾(.a)之后的名字 xxx;
参数2-N:要链接的其它静态库的名字(根据实际情况是否必要);
如果该静态库不是体系提供的(自己制作大概使用第三方提供的静态库)可能出现静态库找不到的情况,此时可以将静态库的路径也指定出来:
  1. link_directories(<lib path>)
复制代码
在cmake中链接动态库的命令如下:
  1. target_link_libraries(
  2.     <target>
  3.     <PRIVATE|PUBLIC|INTERFACE> <item>...
  4.     [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
复制代码
target:指定要加载的库的文件的名字:该文件可能是一个源文件、一个动态库/静态库文件、一个可执行文件
PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默以为PUBLIC;
如果各个动态库之间没有依靠关系,无需做任何设置,三者没有没有区别,一般无需指定,使用默认的 PUBLIC 即可,动态库的链接具有传递性,如果动态库 A 链接了动态库B、C,动态库D链接了动态库A,此时动态库D相当于也链接了动态库B、C,并可以使用动态库B、C中界说的方法。
3.日记

  1. message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)
复制代码
(无) :重要消息
STATUS :非重要消息
WARNING:CMake 告诫, 会继续执行
AUTHOR_WARNING:CMake 告诫 (dev), 会继续执行
SEND_ERROR:CMake 错误, 继续执行,但是会跳过天生的步调
FATAL_ERROR:CMake 错误, 终止全部处理过程
例如:
  1. # 输出一般日志信息
  2. message(STATUS "source path: ${PROJECT_SOURCE_DIR}")
  3. # 输出警告信息
  4. message(WARNING "source path: ${PROJECT_SOURCE_DIR}")
  5. # 输出错误信息
  6. message(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}")
复制代码
4.变量操作

命令功能set(变量名1 ${变量名1} ${变量名2} …)将从第二个参数开始今后全部的字符串举行拼接,结果覆盖写入到第一个参数中list(APPEND < list> [< element> …])APPEND表示举行数据追加,后边的参数和set就一样了list(REMOVE_ITEM < list> < value>)从list中移除后面的valuelist(LENGTH < list> < outputvariable>)获取 list 的长度,< output variable>:新创建的变量,用于存储列表的长度list(GET < list> < element index> < output variable>)读取列表中指定索引的的元素,可以指定多个索引list (JOIN < list> < glue> < output variable>)将列表中的元素用连接符(glue)连接起来组成一个字符串list(FIND < list> < value> < output variable>)查找列表是否存在指定的元素,若果未找到,返回-1list(INSERT < list> < element_index> < element> [< element> …])在list中指定的位置插入若干元素list (PREPEND < list> [< element> …])将元素插入到列表的0索引位置list (POP_BACK < list> [< out-var>…])将列表中末了元素移除list (POP_FRONT < list> [< out-var>…])将列表中首元素移除list (REMOVE_ITEM < list> < value> [< value> …])将指定的元素从列表中移除list (REMOVE_AT < list> < index> [< index> …])将指定索引的元素从列表中移除list (REMOVE_DUPLICATES < list>)移除列表中的重复元素list(REVERSE < list>)列表翻转 列表排序
  1. list (SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
  2. /*
  3. COMPARE:指定排序方法。有如下几种值可选:
  4.         STRING:按照字母顺序进行排序,为默认的排序方法
  5.         FILE_BASENAME:如果是一系列路径名,会使用basename进行排序
  6.         NATURAL:使用自然数顺序排序
  7. CASE:指明是否大小写敏感。有如下几种值可选:
  8.         SENSITIVE: 按照大小写敏感的方式进行排序,为默认值
  9.         INSENSITIVE:按照大小写不敏感方式进行排序
  10. ORDER:指明排序的顺序。有如下几种值可选:
  11.         ASCENDING:按照升序排列,为默认值
  12.         DESCENDING:按照降序排列
  13. */
复制代码
5.宏界说

在举行步伐测试的时间,我们可以在代码中添加一些宏界说,通过这些宏来控制这些代码是否生效,如下所示:
  1. #include <stdio.h>
  2. #define NUMBER  3
  3. int main()
  4. {
  5.     int a = 10;
  6. #ifdef DEBUG
  7.     printf("我是一个程序猿, 我不会爬树...\n");
  8. #endif
  9.     for(int i=0; i<NUMBER; ++i)
  10.     {
  11.         printf("hello, GCC!!!\n");
  12.     }
  13.     return 0;
  14. }
复制代码
在步伐的第七行对DEBUG宏举行了判断,如果该宏被界说了,那么第八行就会举行日记输出,如果没有界说这个宏,第八行就相当于被解释掉了,因此最终无法看到日记输入出(上述代码中并没有界说这个宏)。
为了让测试更灵活,我们可以不在代码中界说这个宏,而是在测试的时间去把它界说出来,其中一种方式就是在gcc/g++命令中去指定,如下:
  1. gcc test.c -DDEBUG -o app
复制代码
在gcc/g++命令中通过参数 -D指定出要界说的宏的名字,这样就相当于在代码中界说了一个宏,其名字DEBUG
在CMake中我们也可以做类似的事情,对应的命令叫做add_definitions():
  1. add_definitions(-D宏名称)//针对上面的例子在对应CMakeLists.txt文件中写这句就即是gcc test.c -DDEBUG -o app
  2. //add_definitions(-DDEBUG)
复制代码
下面的列表中为各人整理了一些CMake中常用的宏:
宏功能PROJECT_SOURCE_DIR使用cmake命令后紧跟的目录,一般是工程的根目录PROJECT_BINARY_DIR执行cmake命令的目录CMAKE_CURRENT_SOURCE_DIR当前处理的CMakeLists.txt所在的路径CMAKE_CURRENT_BINARY_DIRtarget 编译目录EXECUTABLE_OUTPUT_PATH重新界说目的二进制可执行文件的存放位置LIBRARY_OUTPUT_PATH重新界说目的链接库文件的存放位置PROJECT_NAME返回通过PROJECT指令界说的项目名称CMAKE_BINARY_DIR项目实际构建路径,假设在build目录举行的构建,那么得到的就是这个目录的路径
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

郭卫东

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表