Fork me on GitHub
0%

CMake && Makefile

简介

官网 : https://cmake.org/cmake/help/v3.15/

CMake是跨平台的 免费和开源软件,用于使用独立于编译器的方法构建自动化、测试、打包和安装软件。CMake 本身并不是一个构建系统;它生成另一个系统的构建文件。它支持目录层次结构和依赖于多个库的应用程序。它与原生构建环境结合使用,例如Make、Qt Creator、Ninja、Android Studio、Apple 的Xcode和Microsoft Visual Studio。(摘自Wiki)

CMakeLists.txt

常用命令

  1. cmake要求最低版本(选择性添加)

    1
    cmake_minimum_required(VERSION 3.0.0)
  2. 编译工程名

    1
    2
    3
    4
    project(xxx)
    //会自动创建两个变量,PROJECT_SOURCE_DIR和PROJECT_NAME
    //${PROJECT_SOURCE_DIR}:本CMakeLists.txt所在的文件夹路径
    //${PROJECT_NAME}:本CMakeLists.txt的project名称
  3. 给部分文件或路径组合起别名, 通过${变量}获取变量内容

    1
    set(变量 文件名/路径/...)
  4. 设置c / c++ 编译编译参数

    1
    2
    3
    set (CMAKE_C_FLAGS "-Wall -O2")
    set (CMAKE_CXX_FLAGS "-Wall -std=C++11")
    add_compile_options( -Wall ) //该命令译选项是针对所有编译器的(包括c和c++编译器)
  5. 为当前路径以及子目录的源文件加入由-D引入的define flag

    1
    add_definitions(-DFOO -DDEBUG ...)
  6. 对子文件夹进行cmake编译

    1
    add_subdirectory(子文件夹名) 
  7. 打印信息

    1
    2
    message() 			//直接添加打印的内容和变量即可,不需要双引号
    message("PROJECT_SOURCE_DIR = ${PROJECT_SOURCE_DIR}")
  8. 帮助信息

    1
    1. 查看cmake默认变量		cmake --help-variable-list

判断语句

用法:根据某个宏确定编译内容。比较字符串,相同返回true

1
2
3
4
5
if (CPU_PLATFORM_BIT STREQUAL "64")
add_library(mylib generic_64bit.c)
else()
add_library(mylib generic_32bit.c)
endif()

编译目标

二进制文件

1
2
add_executable(目标文件名字xxx 源文件)
//example: add_executable(${PROJECT_NAME} example_person.cpp)

动态 / 静态库

将指定的源文件生成链接文件,然后添加到工程中去

1
2
3
4
5
6
7
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[source1] [source2] [...])

//<name>表示库文件的名字,该库文件会根据命令里列出的源文件来创建。生成的库自动补全,如libxxx.so
//STATIC、SHARED和MODULE的作用是指定生成的库文件的类型。STATIC库是目标文件的归档文件,在链接其它目标的时候使用。SHARED库会被 动态链接(动态链接库),在运行时会被加载。MODULE库是一种不会被链接到其它目标中的插件,但是可能会在运行时使用dlopen-系列的函数 。默认状态下,库文件将会在于源文件目录树的构建目录树的位置被创建,该命令也会在这里被调用。
//source1 source2分别表示各个源文件

链接/路径

头文件路径

添加头文件的查找路径, 相当于命令行的 -l 参数

1
include_directories(../thirdparty/comm/include)

链接库路径

添加需要链接的库文件目录, 相当于命令行 -L 参数

1
link_directories("/home/server/third/lib")

链接库

将目标文件与库文件进行链接

1
2
3
4
target_link_libraries(<target> [item1] [item2] [...] [[debug|optimized|general] <item>] ...)

//<target>是指通过add_executable()和add_library()指令生成已经创建的目标文件
//[item]表示库文件没有后缀的名字。默认情况下,库依赖项是传递的。当这个目标链接到另一个目标时,链接到这个目标的库也会出现在另一个目标的连接线上。这个传递的接口存储在interface_link_libraries的目标属性中,可以通过设置该属性直接重写传递接口。

安装(Install)

  • 详情参考:https://cmake.org/cmake/help/v3.22/command/install.html
  • 作用:用于定义安装规则,安装内容包括可执行文件,静态库,动态库以及文件,目录,脚本等
  • 使用方法:make install
  • 目标安装路径:${CMAKE_INSTALL_PREFIX}指定,默认/usr/local

目标文件安装

目标文件指的是我们通过 add_executable / add_library 得到的二进制文件或者库,只有这些文件才可以通过该命令装载到相应的位置

  • 可执行文件:RUNTIME
  • 动态库:LIBRARY
  • 静态库:ARCHIVE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
install(TARGETS targets... [EXPORT <export-name>]
[RUNTIME_DEPENDENCIES args...|RUNTIME_DEPENDENCY_SET <set-name>]
[[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|
PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[NAMELINK_COMPONENT <component>]
[OPTIONAL] [EXCLUDE_FROM_ALL]
[NAMELINK_ONLY|NAMELINK_SKIP]
] [...]
[INCLUDES DESTINATION [<dir> ...]]
)

// example:
install(TARGETS mybin mystaiclib mydynamiclib
RUNTIME DESTINATION bin
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib)

普通文件安装

普通文件即需要导出的头文件、依赖的三方库等等,该命令同时可以指定导出文件的权限(默认644)

1
2
3
4
5
6
7
8
9
install(<FILES|PROGRAMS> files...
TYPE <type> | DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL] [EXCLUDE_FROM_ALL])

// example
install(FILES mylib.h DESTINATION include)

目录安装

参数介绍:

  • DIRECTORY:后面连接的是所在Source目录的相对路径(结尾是否是”/“对拷贝结果有一定影响)
  • PATTERN:使用正则表达式进行过滤
  • PERMISSIONS:指定PATTERN过滤后的文件权限。
1
2
3
4
5
6
7
8
9
10
11
12
13
install(DIRECTORY dirs...
TYPE <type> | DESTINATION <dir>
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS] [OPTIONAL] [MESSAGE_NEVER]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>] [EXCLUDE_FROM_ALL]
[FILES_MATCHING]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]] [...])

// example
install(DIRECTORY src/ DESTINATION include/myproj FILES_MATCHING PATTERN "*.h")

Q&A

  1. Q: 编译参数添加了-lpthread 参数, 但是还是报undefined reference to `pthread_create’

    A : 需要使用target_link_libraries( target thread) 在这里链接thread库

  2. Q: 如果在多级的目录下编译, 始终找不到问题报 undefine的问题 ?

    A : 看一下是不是链接的库或目标文件格式不同, 一个c 一个c++ , 这样就会导致找不到符号错误

  3. Q: undefined reference to `typeinfo for xxxBase_class’ ?

    A: 基类的构造和析构函数需要加{ }; 否则就会报这个问题

Makefile

基本

PHYON

.PHONY 在 makefile 中表示定义伪目标。所谓伪目标,就是它不代表一个真正的文件名,在执行 make 时可以指定这个目标来执行其所在规则定义的命令

赋值语句

  • =

    最基本的赋值方式,语句与在文中的位置无关,系统自动推导将最终的赋值作为变量的值

    1
    2
    3
    4
    5
    str=123
    target:
    @echo $(str)
    str=456
    # 最终结果:456
  • :=

    覆盖式赋值。如果变量在之前定义赋值过,本次的值将更新变量。该符号赋值只能推导该符号位置之前的值

    1
    2
    3
    4
    5
    str:=$(str1)123
    str1=456
    target:
    @echo $(str)
    # 最终结果:123(将str默认为空字符)
  • ?=

    如果当前变量在之前被赋值过,则此次赋值不执行,否则执行

    1
    2
    3
    4
    5
    str=123
    str?=456
    target:
    @echo $(str)
    # 最终结果 123
  • +=

    追加赋值。旧值保持不变,将新值黏贴到旧值后面

    1
    2
    3
    4
    5
    str=123
    str+=456
    target:
    @echo $(str)
    # 最终结果:123 456

通配符

通配符 使用说明
* 匹配0个或者是任意个字符
? 匹配任意一个字符
[] 我们可以指定匹配的字符放在 “[]” 中
% 匹配任意个字符

自动化变量

自动化变量 说明
$@ 表示规则的目标文件名。如果目标是一个文档文件(Linux 中,一般成 .a 文件为文档文件,也成为静态的库文件), 那么它代表这个文档的文件名。在多目标模式规则中,它代表的是触发规则被执行的文件名。
$% 当目标文件是一个静态库文件时,代表静态库的一个成员名。
$< 规则的第一个依赖的文件名。如果是一个目标文件使用隐含的规则来重建,则它代表由隐含规则加入的第一个依赖文件。
$? 所有比目标文件更新的依赖文件列表,空格分隔。如果目标文件时静态库文件,代表的是库文件(.o 文件)。
$^ 代表的是所有依赖文件列表,使用空格分隔。如果目标是静态库文件,它所代表的只能是所有的库成员(.o 文件)名。 一个文件可重复的出现在目标的依赖中,变量 $ 只记录它的第一次引用的情况。就是说变量 $ 会去掉重复的依赖文件。
$+ 类似“$^”,但是它保留了依赖文件中重复出现的文件。主要用在程序链接时库的交叉引用场合。
$* 在模式规则和静态模式规则中,代表“茎”。“茎”是目标模式中“%”所代表的部分(当文件名中存在目录时, “茎”也包含目录部分)。

特殊函数

添加前缀

  • 函数名:addprefix( )
  • 语法:$(addprefix PREFIX/,${NAME…})
  • 函数功能:为“NAMES…”中的每一个文件名添加前缀“PREFIX”。参数“NAMES…”是空格分割的文件名序列,将“PREFIX”添加到此序列的每一个文件名之前。
  • 返回值:以单空格分割的添加了前缀“PREFIX”的文件名序列。
1
2
$(addprefix src/,foo bar)
//返回值为“src/foo src/bar”

过滤器

  • 函数名:filter( )
  • 语法:$(filter SUFFIX…,$(SOURCES))
  • 作用:保留字串 “ $(SOURCES)” 中所有符合模式 “ SUFFIX ” 的单词
1
2
3
sources := main.c AT.txt
foo : $(sources)
cc $(filter %.c,%.s,$(sources)) -o foo

打印

用于Makefile的Debug,打印分为三个等级:info、warning、error

1
2
3
4
$(info SRC = $(SRC))
$(warning INC = $(INC))
//注意:执行到error会直接stop退出
$(error INC = $(INC))