x1cmake_minimum_required(VERSION 3.16)
2
3# project
4project(
5 myproject
6 VERSION 1.0
7 DESCRIPTION "project description"
8 LANGUAGES CXX
9)
10
11# require out-of-source builds
12file(TO_CMAKE_PATH "${PROJECT_BINARY_DIR}/CMakeLists.txt" LOC_PATH)
13if (EXISTS "${LOC_PATH}")
14 message(FATAL_ERROR "You cannot build in a source directory (or any
15 directory with CMakeLists.txt). Please make a build subdirectory.
16 Feel free to remove CMakeCache.txt and CMakeFiles.")
17endif()
18
19
20# Only do these if this is the main project, and not if it is included
21# through add_subdirectory
22if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
23
24 # Let's ensure -std=c++xx instead of -std=g++xx
25 set(CMAKE_CXX_EXTENSION OFF)
26
27 # Set the default cxx standard
28 set(CMAKE_CXX_STANDARD 17)
29
30 # Let's nicely support folders in IDE's
31 set_property(GLOBAL PROPERTY USE_FOLDERS ON)
32
33 # Enable testing if it's in the main app.
34 include(CTest)
35
36 # Fetchcontent may be required
37 include(FetchContent)
38
39endif()
40
41# library
42add_library(
43 mylibrary STATIC
44 lib.hpp lib.cpp
45)
46target_include_directories(mylibrary PUBLIC ${INCLUDE_DIR})
47target_compile_definitions(mylibrary PUBLIC MY_PUBLIC)
48target_complie_features(mylibrary PUBLIC cxx_std_17)
49
50# executable
51add_excutable(
52 myprogram main.cpp
53)
54target_link_libraries(
55 myprogram PUBLIC mylibrary
56)
57
58# add subdirectories
59add_subdirectory(src)
60#...
61
62# add tests
63if (BUILD_TESTING)
64 add_subdirectory(test)
65endif()
刚开始接触感觉 PUBLIC,PRIVATE 和 INTERFACE 难以理解,所以创建一个项目以加深理解。按照文档说明,大部分东西都分成了 PRIVATE 和 INTERFACE 两种类型,按照字面理解,应该是 PRIVATE 继承后会被隐藏,而 INTERFACE 不会,相当于 c++ 中的 private 和 public。而 PUBLIC 同时包含两者。
为了验证以上说法,需要两个库被接连地继承。
依赖关系为:example.cpp --> greet.hpp, greet.cpp --> time.hpp, time.cpp
通过 target_compile_definitions
添加 #deines
(TIME_PUBLIC, TIME_PRIVATE, TIME_INTERFACE),使用 target_link_libraries
继承,结果如下表
TIME_PUBLIC | TIME_PRIVATE | TIME_INTERFACE | Link | |||
---|---|---|---|---|---|---|
time.cpp | Y | Y | N | |||
greet.cpp | greet public | Y | N | Y | ||
greet private | Y | N | Y | |||
greet interface | N | N | N | |||
example.cpp | greet public | example public | Y | N | Y | Y |
example private | Y | N | Y | Y | ||
example interface | N | N | N | N | ||
greet private | example public | N | N | N | Y | |
example private | N | N | N | Y | ||
example interface | N | N | N | N | ||
greet interface | example public | Y | N | Y | Y | |
example private | Y | N | Y | Y | ||
example interface | N | N | N | N |
由上表可知
private 自身可以看到,继承不可以看到
interface 自身不可以看到,继承可以看到
public 就是 private 和 interface 的或,即自身可以看到,继承也可以看到
实际上不需要把例子搞得那么复杂,因为 definition
中相当于有一次依赖了,只需要再一次的 greet 对 time 依赖即有两层依赖,example 的行为完全可以推断出来。
xxxxxxxxxx
161# set 语句设置变量
2set(MY_LOCAL_VAR "This is local")
3
4# CACHE 关键词设置缓存变量
5# STRING 显式声明该变量类型为字符串
6# 最后面的字符串用来描述变量,命令行输入 `cmake -LH` 时会打印该字符串
7# 缓存变量特点是可以在命令行中设置,比如,在命令行输入 `cmake -DMY_CACHE_VAR="command line"时,
8# 变量的值被替换成"command line",而不是脚本中设置的 "This is cached variable."
9# 即可以在命令行中覆盖。
10set(MY_CACHE_VAR "This is cached variable." CACHED STRING "Description")
11
12# 对于布尔值得缓存变量,可以使用更简单的语法 `option`
13# 其中“on or off” 相当于 "Description" off 是默认值
14option(MY_OPTION "on or off" off)
15# 即等价于
16set(MY_OPTION off CACHED BOOL "on or off")
安装可执行文件或者库
xxxxxxxxxx
41# 安装到 build/bin 目录下
2install(TARGETS myprogram DESTINATION "${CMAKE_BINARY_DIR}/bin")
3# 安装库到 /usr/local/lib 目录下
4install(TARGETS mylibrary DESTINATION /usr/local/lib)
xxxxxxxxxx
171# avoid install googletest
2set(INSTALL_GTEST OFF)
3
4# fetch googletest from github, the hash should be changed in requirement
5FetchContent_Declare(
6 googletest
7 URL https://github.com/google/googletest/archive/5476968f6948923e2411081fd9372e71a59d8e77.zip
8)
9
10
11# test with googletest
12add_executable(test_name test.cpp)
13# link googletest
14target_link_libraries(test_name PRIVATE gtest_main)
15# use googletest
16include(GoogleTest)
17gtest_discover_tests(test_name)
有时候会把测试数据写在文件内,需要在测试时读取,但由于测试的二进制文件都是在 build 文件夹内构建的,路径会发生变化,需要复制数据文件到 build 目录下同时编译时更改数据文件。复制文件利用 cmake 中的 file
命令,修改路径利用宏定义。
假设在 test 目录下,有 test.cpp
和 CMakeLists.txt
,还有包含测试数据文件 test_data
的子目录test/data
。
在 CMakeLists.txt
中
xxxxxxxxxx
111# 将测试数据复制到 build/test/data 中
2flie(
3 COPY "test_data"
4 DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/data"
5)
6
7# 利用宏在编译时修改路径
8target_compile_definitions(
9 test
10 PRIVATE TEST_DATA_DIRECTORY="${CMAKE_CURRENT_BINARY_DIR}/data"
11)
其中 CMAKE_CURRENT_BINARY_DIR
是在编译时生成的路径,表示生成当前二进制可执行文件的路径,如果 test 目录在项目根目录下,那么该变量表示 build/test 。而 CMAKE_BINARY_DIR
则表示顶级二进制文件目录,按我理解是 build 文件。
在 test.cpp
中
xxxxxxxxxx
51// 同时在 test.cpp 中定义 TEST_DATA_DIRECTORY,避免 IDE 报错
2
3
4
5const std::string kTestDataDirectory = TEST_DATA_DIRECTORY;