x1cmake_minimum_required(VERSION 3.16)2
3# project4project(5 myproject6 VERSION 1.07 DESCRIPTION "project description"8 LANGUAGES CXX9)10
11# require out-of-source builds12file(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 any15 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 included21# through add_subdirectory22if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)23 24 # Let's ensure -std=c++xx instead of -std=g++xx25 set(CMAKE_CXX_EXTENSION OFF)26 27 # Set the default cxx standard28 set(CMAKE_CXX_STANDARD 17)29 30 # Let's nicely support folders in IDE's31 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 required37 include(FetchContent)38
39endif()40
41# library42add_library(43 mylibrary STATIC44 lib.hpp lib.cpp45)46target_include_directories(mylibrary PUBLIC ${INCLUDE_DIR})47target_compile_definitions(mylibrary PUBLIC MY_PUBLIC)48target_complie_features(mylibrary PUBLIC cxx_std_17)49
50# executable51add_excutable(52 myprogram main.cpp53)54target_link_libraries(55 myprogram PUBLIC mylibrary56)57
58# add subdirectories59add_subdirectory(src)60#...61
62# add tests63if (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 的行为完全可以推断出来。
xxxxxxxxxx161# 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")
安装可执行文件或者库
xxxxxxxxxx41# 安装到 build/bin 目录下2install(TARGETS myprogram DESTINATION "${CMAKE_BINARY_DIR}/bin")3# 安装库到 /usr/local/lib 目录下4install(TARGETS mylibrary DESTINATION /usr/local/lib)
xxxxxxxxxx171# avoid install googletest2set(INSTALL_GTEST OFF)3
4# fetch googletest from github, the hash should be changed in requirement5FetchContent_Declare(6 googletest7 URL https://github.com/google/googletest/archive/5476968f6948923e2411081fd9372e71a59d8e77.zip8)9
10
11# test with googletest12add_executable(test_name test.cpp)13# link googletest14target_link_libraries(test_name PRIVATE gtest_main)15# use googletest16include(GoogleTest)17gtest_discover_tests(test_name)
有时候会把测试数据写在文件内,需要在测试时读取,但由于测试的二进制文件都是在 build 文件夹内构建的,路径会发生变化,需要复制数据文件到 build 目录下同时编译时更改数据文件。复制文件利用 cmake 中的 file 命令,修改路径利用宏定义。
假设在 test 目录下,有 test.cpp 和 CMakeLists.txt,还有包含测试数据文件 test_data 的子目录test/data。
在 CMakeLists.txt 中
xxxxxxxxxx111# 将测试数据复制到 build/test/data 中2flie(3 COPY "test_data" 4 DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/data"5)6
7# 利用宏在编译时修改路径8target_compile_definitions(9 test10 PRIVATE TEST_DATA_DIRECTORY="${CMAKE_CURRENT_BINARY_DIR}/data"11)其中 CMAKE_CURRENT_BINARY_DIR 是在编译时生成的路径,表示生成当前二进制可执行文件的路径,如果 test 目录在项目根目录下,那么该变量表示 build/test 。而 CMAKE_BINARY_DIR 则表示顶级二进制文件目录,按我理解是 build 文件。
在 test.cpp 中
xxxxxxxxxx51// 同时在 test.cpp 中定义 TEST_DATA_DIRECTORY,避免 IDE 报错2345const std::string kTestDataDirectory = TEST_DATA_DIRECTORY;