cmake笔记

demo1

cmake_minimum_required(VERSION 2.8) #cmake的最低版本
project(test) #定义工程名称


add_executable(main add.cpp sub.cpp mul.cpp div.cpp main.cpp) 
#定义工程会生成一个可执行程序,格式:add_executable(可执行程序名 源文件名称)
#这里的可执行程序名和project中的项目名没有任何关系

set(CMAKE_CXX_STANDARD 11) #增加std=c++11/14/17

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#指定可执行文件生成的位置,PROJECT_SOURCE_DIR为CMakeLists.txt所在目录


demo2

cmake_minimum_required(VERSION 2.8) #cmake的最低版本
project(test) #定义工程名称

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

#增加std=c++11/14/17,注意:必须在add_executable前

set(SRC add.cpp sub.cpp mul.cpp div.cpp main.cpp)
add_executable(main ${SRC})
#定义工程会生成一个可执行程序,格式:add_executable(可执行程序名 源文件名称)
#这里的可执行程序名和project中的项目名没有任何关系



set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#指定可执行文件生成的位置,PROJECT_SOURCE_DIR为CMakeLists.txt所在目录


搜索文件

aux_source_directory(${PROJECT_SOURCE_DIR} SRC)
#aux_source_directory(<dir> <var>) 搜索dir路径中所有文件,并命名为var

file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
file(GLOB HEAD ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
#file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)

注意:PROJECT_SOURCE_DIR和CMAKE_CURRENT_SOURCE_DIR在嵌套cmake下不同,一般情况下相同

指定头文件目录

include_directories(${PROJECT_SOURCE_DIR}/include)
#指定包含头文件的目录 include_directories(headpath)

项目结构

image

通过Cmake制作库文件 demo2

set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
#指定动态库/静态库生成的位置

add_library(库名称 STATIC 源文件1 [源文件2] ...)


eg.
add_library(calc_shared SHARED ${SRC})
#制作名称为calc的动态库(.so)

add_library(calc_static STATIC ${SRC})
# #制作名称为calc的静态库(.a)

注意:cmake后需要make才会生成库文件

在程序中链接静态库 场景:源文件较少 demo3

link_directories(<lib path>) #指定链接库的目录
link_libraries(<static lib> [<static lib>...]) #指定出要链接的静态库的名字
eg.
include_directories(${PROJECT_SOURCE_DIR}/include)
#指定包含头文件的目录 include_directories(headpath)

file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)

link_directories(${PROJECT_SOURCE_DIR}/lib)
#指定静态库的路径,否则自定义的链接库找不到
link_libraries(libcalc_static.a)
#指定静态库,可以全名,也可以掐头去尾
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#指定可执行文件生成的位置,PROJECT_SOURCE_DIR为CMakeLists.txt所在目录
add_executable(main ${SRC})

注意:可以是全名libxxx.a,也可以是掐头(lib)去尾(.a)之后的名字xxx

项目结构

image

在程序中链接动态库 场景:源文件较多 demo4

target_link_libraries(
    <target> 
    <PRIVATE|PUBLIC|INTERFACE> <item>... 
    [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
  • target指定要加载动态库的文件的名字
    该文件可能是一个源文件
    该文件可能是一个动态库文件
    该文件可能是一个可执行文件
  • PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为PUBLIC
    如果各个动态库之间没有依赖关系,无需做任何设置,三者没有没有区别,一般无需指定,使用默认的 PUBLIC 即可。
  • 动态库的链接具有传递性,如果动态库 A 链接了动态库B、C,动态库D链接了动态库A,此时动态库D相当于也链接了动态库B、C,并可以使用动态库B、C中定义的方法。
    PUBLIC:在public后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用。
    PRIVATE:在private后面的库仅被link到前面的target中,并且终结掉,第三方不能感知你调了啥库。(只传递一次)
    INTERFACE:在interface后面引入的库不会被链接到前面的target中,只会导出符号。(不发生传递)
include_directories(${PROJECT_SOURCE_DIR}/include)
#指定包含头文件的目录 include_directories(headpath)

file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)

link_directories(${PROJECT_SOURCE_DIR}/lib)
#指定动态库的路径,否则自定义的链接库找不到

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#指定可执行文件生成的位置,PROJECT_SOURCE_DIR为CMakeLists.txt所在目录
add_executable(main ${SRC})
target_link_libraries(main libcalc_shared.so)
#指定动态库,可以全名,也可以掐头去尾,注意:需要放在脚本末尾

日志

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 错误, 终止所有处理过程

变量操作 demo5

使用set进行拼接

set(变量名1 ${变量名1} ${变量名2} ...)

关于上面的命令其实就是将从第二个参数开始往后所有的字符串进行拼接,最后将结果存储到第一个参数中,如果第一个参数中原来有数据会对原数据就行覆盖。

使用list进行拼接

list(APPEND <list> [<element> ...])

list命令的功能比set要强大,字符串拼接只是它的其中一个功能,所以需要在它第一个参数的位置指定出我们要做的操作,APPEND表示进行数据追加,后边的参数和set就一样了。

set(TEMP "abc" "123")
file(GLOB SRC ${PROJECT_SOURCE_DIR}/src/*.cpp)
set(RES1 ${SRC} ${TEMP})
list(APPEND RES2 ${SRC} ${TEMP})
message(STATUS "message:${RES1}")
message(STATUS "message:${RES2}")

注意:file得到的是绝对路径

字符串移除

我们在通过file搜索某个目录就得到了该目录下所有的源文件,但是其中有些源文件并不是我们所需要的,在当前这么目录有五个源文件,其中main.cpp是一个测试文件。如果我们想要把计算器相关的源文件生成一个动态库给别人使用,那么只需要add.cpp、div.cp、mult.cpp、sub.cpp这四个源文件就可以了。此时,就需要将main.cpp从搜索到的数据中剔除出去,想要实现这个功能,也可以使用list

list(REMOVE_ITEM <list> <value> [<value> ...])

eg.

aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC)

message(${SRC})

list(REMOVE_ITEM SRC ${PROJECT_SOURCE_DIR}/src/main.cpp)

message(${SRC})

set(str1 "123" "abc")
set(str2 "456" "def")
list(APPEND SRC ${str1} ${str2})
message(${SRC})

list的其它操作 demo5

获取list长度

list(LENGTH <list> <output variable>)
LENGTH:子命令LENGTH用于读取列表长度
<list>:当前操作的列表
<output variable>:新创建的变量,用于存储列表的长度。

读取列表中指定索引的的元素,可以指定多个索引

list(GET <list> <element index> [<element index> ...] <output variable>)
<list>:当前操作的列表
<element index>:列表元素的索引
从0开始编号,索引0的元素为列表中的第一个元素;
索引也可以是负数,-1表示列表的最后一个元素,-2表示列表倒数第二个元素,以此类推
当索引(不管是正还是负)超过列表的长度,运行会报错
<output variable>:新创建的变量,存储指定索引元素的返回结果,也是一个列表。

将列表中的元素用连接符(字符串)连接起来组成一个字符串

list (JOIN <list> <glue> <output variable>)
<list>:当前操作的列表
<glue>:指定的连接符(字符串)
<output variable>:新创建的变量,存储返回的字符串

查找列表是否存在指定的元素,若未找到,返回-1

list(FIND <list> <value> <output variable>)
<list>:当前操作的列表
<value>:需要再列表中搜索的元素
<output variable>:新创建的变量
如果列表<list>中存在<value>,那么返回<value>在列表中的索引
如果未找到则返回-1。

在list中指定的位置之前插入若干元素

list(INSERT <list> <element_index> <element> [<element> ...])

将元素插入到列表的0索引位置

list (PREPEND <list> [<element> ...])

将列表中最后元素移除

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>)

列表排序

list (SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])

COMPARE:指定排序方法。有如下几种值可选:

  • STRING:按照字母顺序进行排序,为默认的排序方法
  • FILE_BASENAME:如果是一系列路径名,会使用basename进行排序
  • NATURAL:使用自然数顺序排序

CASE:指明是否大小写敏感。有如下几种值可选:

  • SENSITIVE: 按照大小写敏感的方式进行排序,为默认值
  • INSENSITIVE:按照大小写不敏感方式进行排序

ORDER:指明排序的顺序。有如下几种值可选:

  • ASCENDING:按照升序排列,为默认值
  • DESCENDING:按照降序排列

宏定义 demo6

在进行程序测试的时候,我们可以在代码中添加一些宏定义,通过这些宏来控制这些代码是否生效,如下所示:

#include <stdio.h>
#define NUMBER  3

int main()
{
    int a = 10;
#ifdef DEBUG
    printf("我是一个程序猿, 我不会爬树...\n");
#endif
    for(int i=0; i<NUMBER; ++i)
    {
        printf("hello, GCC!!!\n");
    }
    return 0;
}

在程序的第七行对DEBUG宏进行了判断,如果该宏被定义了,那么第八行就会进行日志输出,如果没有定义这个宏,第八行就相当于被注释掉了,因此最终无法看到日志输入出(上述代码中并没有定义这个宏)。
在gcc/g++命令中通过参数 -D指定出要定义的宏的名字,这样就相当于在代码中定义了一个宏,其名字为DEBUG。

在CMake中我们也可以做类似的事情,对应的命令叫做add_definitions:

add_definitions(-D宏名称)

eg.

int main()
{
    int a = 20,b = 12;
    printf("a = %d,b = %d\n",a,b);
    printf("a + b = %d\n",add(a,b));
    printf("a - b = %d\n",sub(a,b));
    
    printf("a * b = %d\n",mul(a,b));
    
    printf("a / b = %.3lf\n",div(a,b));
    
    #ifdef DEBUG
    printf("调试信息:xxxxx\n");
    #endif
    return 0;
}
add_definitions(-DDEBUG)

aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC)

include_directories(${PROJECT_SOURCE_DIR}/include)

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

add_executable(main ${SRC})

嵌套的CMake 源代码见demo7

image

静态库中链接静态库 demo8

静态库中链接动态库 demo9

posted @ 2024-04-10 22:16  安河桥北i  阅读(3)  评论(0编辑  收藏  举报