从零开始 CMake 学习笔记 (C)static-library
开始前先默念三遍口诀:
- Declare a target
- Declare target’s traits
- It’s all about targets
本系列主要根据GitHub上的 cmake-examples 项目进行翻译总结,同时对于不清晰的概念及函数进行查阅理解记录形成。
文章目录
1 介绍
显示了一个 hello world 示例,它首先创建并链接了一个静态库。同上一节类似,使用不同的文件夹来作为源文件和include文件。整体的文件架构如下所示:
1.1 文件树
1.2 文件简介
- CMakeLists.txt - 包含了你希望运行的 CMake 命令
cmake_minimum_required(VERSION 3.5)
project(hello_library)
############################################################
//创建一个库
############################################################
//从库源文件生成一个静态库
add_library(hello_library STATIC
src/Hello.cpp
)
target_include_directories(hello_library
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
############################################################
// 创建一个可执行对象
############################################################
// 添加可执行文件
add_executable(hello_binary
src/main.cpp
)
# 将新创建的静态库 hello_library 链接到新的要生成的 hello_binary 目标
target_link_libraries( hello_binary
PRIVATE
hello_library
)
- include/static/Hello.h - 要包含的头文件,会生成一个静态库链接
#ifndef __HELLO_H__
#define __HELLO_H__
class Hello
{
public:
void print();
};
#endif
- src/Hello.cpp - 要编译的源文件
#include <iostream>
#include "static/Hello.h"
void Hello::print()
{
std::cout << "Hello Static Library!" << std::endl;
}
- src/main.cpp - main 文件
#include "static/Hello.h"
int main(int argc, char *argv[])
{
Hello hi;
hi.print();
return 0;
}
2 概念解析
2.1 添加静态库
add_library() 函数用于从一些源文件创建库,调用方法如下:
add_library(hello_library STATIC
src/Hello.cpp
)
上面这句命令将基于 Hello.cpp 源文件创建一个名为 hello_library
的静态库。
注意:
在上面这句命令中,我们直接将源文件传递给了 add_library() 函数调用,而没有像上一节从零开始 CMake 学习笔记 (B)hello-headers中创建 SOURCES
源变量。现代CMake,鼓励直接传递源文件给 add_library() 函数这种方式。
2.2 添加include路径
在此示例中,使用 target_include_directories() 函数将用到的头文件包含在库中,并将范围设置为 PUBLIC
。
target_include_directories(hello_library
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
使用这句命令后,在接下来这些情况会包含头文件的路径:
- 在编译 hello_library 这个静态库的时候;
- 在编译任何链接到
hello_library
这个静态库的其他库的时候,比如后面的hello_binary
库的时候。
2.2.1 拓展 - target_include_directories() 方法
target_include_directories()
该函数用于指定包含的头文件的目录。与这个函数相似的函数有 target_link_libraries()
、target_compile_options()
这两个,这里以 target_include_directories()
函数来分析其方法,该函数的所有参数为:
target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
该函数参数中的 target
由add_executable()
或add_library()
等命令创建生成,同时不能是一个 ALIAS 目标(即使用ALIAS重命名的目标)。
//TODO
通过显式使用 AFTER
或 BEFORE
,可以独立于默认值在追加和前置之间进行选择。目前还没有碰到合适的案例来分析,这里后面再补充。
//TODO
SYSTEM
目前也没搞明白。
而 INTERFACE
, PUBLIC
和 PRIVATE
关键字来指定以下参数的范围。而范围的含义为:
PRIVATE
- 跟在该参数后面的目录(如上面的${PROJECT_SOURCE_DIR}/include
) 仅会被添加到正在构建的这个目标(如上面的hello_library
)的 include 目录中。INTERFACE
- 该目录将添加到在编译任何链接到hello_library
这个静态库的其他库的 include 目录中。PUBLIC
- 既包含在此库的 include 目录中,也包含任何链接到 hello_library 这个静态库的其他库的 include 目录中。
//TODO
这里有待补充!
2.3 链接库
当创建一个可执行文件时,你必须告诉编译器有关这个库的信息。例如本例中,我们要通过 main.cpp 来编译生成一个 hello_binary
的库,而在 main.cpp 中包含了静态库中的 Hello 对象,那么我们就通过 target_link_libraries() 这个函数把这些信息告诉它。
add_executable(hello_binary
src/main.cpp
)
target_link_libraries( hello_binary
PRIVATE
hello_library
)
创建将使用您的库的可执行文件时,您必须告诉编译器有关该库的信息。这可以使用 target_link_libraries() 函数来完成。
3 运行结果
构建此示例的标准输出如下所示:
C-static-library$ mkdir build
C-static-library$ cd build
C-static-library$ cmake ..
-- The C compiler identification is GNU 9.4.0
-- The CXX compiler identification is GNU 9.4.0
-- Check for working C compiler: /usr/bin/cc
-- Balabala...
-- Balabala...
C-static-library$ make
Scanning dependencies of target hello_library
[ 50%] Building CXX object CMakeFiles/hello_library.dir/src/Hello.cpp.o
Linking CXX static library libhello_library.a
[ 50%] Built target hello_library
Scanning dependencies of target hello_binary
[100%] Building CXX object CMakeFiles/hello_binary.dir/src/main.cpp.o
Linking CXX executable hello_binary
[100%] Built target hello_binary
C-static-library$ ls
CMakeCache.txt CMakeFiles cmake_install.cmake hello_binary libhello_library.a Makefile
C-static-library$ ./hello_binary
Hello Static Library!
后记:
从上面这个例子中我们就可以体会到前面的口诀:1. Declare a target、 2. Declare target’s traits ,在CMake中,我们先写创建这个库的语句,然后再补充这个库要用到的依赖等。