blog
blog copied to clipboard
C/C++ 工程化
C/C++ 工程化
C++的工程化指的是如何组织、构建和维护大型的C++项目。它涉及到代码组织、构建系统、自动化测试、版本控制等多个方面。以下是一些建议和步骤,帮助你进行C++的工程化管理:
-
代码组织:
-
目录结构:为源文件(.cpp)、头文件(.h/.hpp)设定统一的目录,如
src/
和include/
。还可以按功能模块进一步划分子目录。 - 模块化:将功能相似的代码放在同一个模块或命名空间中,减少不必要的耦合。
-
目录结构:为源文件(.cpp)、头文件(.h/.hpp)设定统一的目录,如
-
版本控制:
- 使用Git、SVN等版本控制工具来管理项目代码。
- 为项目设定明确的版本和分支策略。
-
构建系统:
- 使用像CMake、Makefile、Bazel等工具来定义和管理构建流程。
- 设定目标输出,如动态库、静态库、可执行文件等。
- 确保跨平台兼容性,如Linux、Windows、Mac等。
-
依赖管理:
- 使用如vcpkg、conan等包管理工具来管理第三方库和依赖。
- 尽量确保依赖的版本稳定,避免版本冲突。
-
自动化测试:
- 使用单元测试框架,如Google Test、Catch2等,编写单元测试。
- 使用CI/CD工具,如Jenkins、Travis CI、GitHub Actions等,进行持续集成和自动化测试。
- 考虑代码覆盖率,并使用工具如gcov、lcov等进行覆盖率分析。
-
代码质量检查:
- 使用代码格式化工具,如clang-format,确保代码风格统一。
- 使用静态代码分析工具,如clang-tidy、cppcheck,检查代码质量和潜在问题。
-
文档:
- 使用Doxygen、Sphinx等工具生成代码文档。
- 编写项目的README、设计文档、API说明等。
-
日志与调试:
- 使用日志库,如spdlog、log4cpp,记录关键的运行信息。
- 使用调试和性能分析工具,如gdb、valgrind、perf等。
-
代码审查:
- 使用像Gerrit、Phabricator或GitHub的Pull Request等工具和流程进行代码审查。
-
发布与部署:
- 设定版本号规则,如语义化版本。
- 使用Docker、安装脚本等方式,简化部署和分发过程。
总的来说,C++的工程化管理是一个综合性的过程,需要不断地迭代和完善,确保项目的高效、稳定和可维护。
C 工程化软件包
编辑器:VSCode
Linux 上需要的软件包[Ubuntu]
sudo apt-get update
sudo apt-get upgrade
# Mandatory
sudo apt-get install gcc g++ gdb
sudo apt-get install make cmake
sudo apt-get install git
sudo apt-get install doxygen
sudo apt-get install python3 python3-pip
# Optional
sudo apt-get install lcov gcovr
sudo apt-get install ccache
sudo apt-get install cppcheck
sudo apt-get install llvm clang-format clang-tidy
sudo apt-get install curl zip unzip tar
mac 上需要的软件包
Compiler Setup:
XCode: https://www.ics.uci.edu/~pattis/common/handouts/macclion/clang.html
lldb --version
clang --version
clang++ --version
Brew:
https://brew.sh/index_de
Brew:
brew install git
brew install make
brew install cmake
brew install doxygen
brew install lcov
brew install gcovr
brew install ccache
Windows
通过WSL子系统安装Linux版本包 https://github.com/WangShuXian6/blog/issues/125#issuecomment-1732532787 或安装Windows版本包 https://github.com/WangShuXian6/blog/issues/168#issue-1875128134
CMake
CMake https://github.com/WangShuXian6/blog/issues/171#issuecomment-1732571978 make https://www.gnu.org/software/make/ windows 版本 make https://www.gnu.org/software/make/ 配置make环境变量 右键我的电脑->属性->高级系统设置->高级->环境变量->系统变量->path 然后在path中新建环境变量,目录定位到make for windows安装目录即可
C:\Program Files (x86)\GnuWin32\bin
重启终端和编辑器
make -v
doxygen
doxygen >https://www.doxygen.nl/download.html
右键我的电脑->属性->高级系统设置->高级->环境变量->系统变量->path 然后在path中新建环境变量
C:\Program Files\doxygen\bin
doxygen -v
Graphviz
https://graphviz.org/ Graphviz 是开源的图形可视化软件 https://graphviz.org/download/ ubuntu
sudo apt install graphviz
配置graphviz环境变量 右键我的电脑->属性->高级系统设置->高级->环境变量->系统变量->path 然后在path中新建环境变量,目录定位到graphviz for windows安装目录即可
C:\Program Files\Graphviz\bin
测试
dot -v
删除重建命令
linux
rm -rf build
mkdir build
windows cmd
rmdir /s /q build
mkdir build
PowerShell
Remove-Item -Recurse -Force build
New-Item -Type Directory -Name build
Conan
https://conan.io/ 需要python3.5以上
conan -v
# 创建默认配置文件
conan profile detect
vcpkg
https://github.com/microsoft/vcpkg https://vcpkg.io/en/ https://github.com/microsoft/vcpkg/blob/master/README_zh_CN.md
安装 建议在项目的
\external
目录下安装vcpkg 因为vcpk路径需要硬编码到项目中,如果在系统全局安装,需要频繁更改该路径
cd external
git clone https://github.com/microsoft/vcpkg
.\vcpkg\bootstrap-vcpkg.bat
cd vcpkg
# 查看本次vcpkg的git log
git log
新建
\vcpkg.json
将git log值赋予\vcpkg.json
的builtin-baseline
\CMakeLists.txt
include(${CMAKE_SOURCE_DIR}/external/vcpkg/scripts/buildsystems/vcpkg.cmake)
find_package(nlohmann_json REQUIRED)
编译构建
代码组织
Clang-Format
Cmake-Format
版本控制
构建系统
CMake
https://cmake.org/download/ https://github.com/Modern-CMake-CN/Modern-CMake-zh_CN Modern CMake 简体中文版
依赖管理
git sub
Fetch Content
CPM
Conan
vcpkg
自动化测试
代码质量检查
文档
可视化
详解 使用 Graphviz 分析项目的依赖关系 导出依赖图
编写
\Makefile
dependency:
cd build && cmake .. --graphviz=graph.dot && dot -Tpng graph.dot -o graphImage.png
创建目录
build
[否则报错] 生成依赖图 [项目根目录下执行]
make dependency
依赖图位置为
\build\graphImage.png
这个
cmake
命令和后续的命令组合是用来生成和可视化一个 CMake 项目的依赖关系的。
具体解释如下:
-
cd build
: 这个命令是将当前工作目录切换到build
目录。通常在 CMake 项目中,我们使用一个单独的目录(例如build
)来生成构建文件,以保持源代码目录的整洁。 -
cmake ..
: 这个命令是调用cmake
并指向父目录(..
),也就是项目的源代码目录,来生成构建文件。 -
--graphviz=graph.dot
: 这是一个cmake
的选项,用于生成一个 Graphviz 格式的文件(在这里命名为graph.dot
),该文件描述了项目的依赖关系。这是一个非常有用的功能,特别是当项目很大或有复杂的依赖关系时。 -
dot -Tpng graph.dot -o graphImage.png
: 这个命令使用 Graphviz 的dot
工具来将前面生成的graph.dot
文件转换成 PNG 图像格式,并保存为graphImage.png
。这样,你可以直观地查看和理解项目的依赖关系。
总的来说,这个命令组合的目的是为了生成和可视化 CMake 项目的依赖关系图。这对于理解大型项目的结构和依赖关系非常有帮助。
--graphviz=graph.dot
是一个专门的 CMake 命令行选项,用于生成项目的依赖关系图。这个选项让 CMake 产生一个 Graphviz 格式的文件,该文件描述了整个项目中的目标(如库或可执行文件)之间的依赖关系。
详细解释如下:
-
--graphviz
: 这是指示 CMake 生成 Graphviz 文件的命令行选项。 -
graph.dot
: 这是生成的 Graphviz 文件的名称。在这个例子中,文件名被设置为graph.dot
,但你也可以选择其他名称。
这个功能特别有用,因为它可以帮助开发者可视化地了解项目中各个目标之间的依赖关系,特别是在大型项目中,这样的依赖关系图可以更直观地展示复杂的依赖结构。
当你运行带有
--graphviz=graph.dot
选项的cmake
命令后,CMake 会在当前工作目录下生成一个名为graph.dot
的文件。这个文件是纯文本格式,使用 Graphviz 的 DOT 语言描述了项目的依赖关系。为了将这个.dot
文件转换为图形,你可以使用 Graphviz 的dot
工具,如你之前提到的那样,将其转换为 PNG、SVG 等格式。
--graphviz
是 CMake 自带的一个命令行选项。使用这个选项,CMake 可以生成一个 Graphviz 格式的.dot
文件,描述项目中的目标(例如库和可执行文件)之间的依赖关系。这个功能允许开发者在复杂的项目中快速和直观地了解各个组件之间的依赖结构。
日志与调试
代码审查
Clang-Tidy
发布与部署
CMake 构建工程化
CMake 构建 单文件
查看CMake版本
cmake --version
配置 VSCode 编译器 例如g++
,CMake 不包含编译器 新建main.cpp
#include <iostream>
int main()
{
std::cout << "hello world\n";
return 0;
}
新建文件夹
build
新建CMakeLists.txt
# CMake 最低版本
cmake_minimum_required(VERSION 3.22)
#
project(
CppProjectTemplate # 项目名
VERSION 1.0.0 # 项目版本
LANGUAGES C CXX # 项目包含 C,C++文件
)
add_executable(
Executable # 生成的可执行文件名
main.cpp # 要编译的文件
)
将构建文件写入 build 目录
cd build/
cmake ..
在 build 目录下 构建可执行文件
cmake --build .
生成可执行文件
build\Debug\Executable.exe
执行
.\Debug\Executable
返回
hello world
CMake 构建 多文件 包含库
新建文件夹
build
库
my_lib.h
// 确保头文件在一个编译单元中只被包含一次
#pragma once
void print_hello_world();
my_lib.cpp
#include <iostream>
#include "my_lib.h"
void print_hello_world()
{
std::cout << "hello world\n";
}
主文件
main.cpp
#include <iostream>
#include "my_lib.h"
void print_hello_world()
{
std::cout << "hello world\n";
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(CppProjectTemplate VERSION 1.0.0 LANGUAGES C CXX)
# 构建库文件
add_library(Library #库名字
STATIC
my_lib.cpp #库文件
)
# 构建可执行文件
add_executable(Executable main.cpp)
# 将库文件 Library 链接到可执行文件 Executable
target_link_libraries(
Executable PUBLIC Library
)
写入构建文件
cd build/
cmake ..
构建库和可执行文件,并链接两者
cmake --build .
这将生成库文件和可执行文件
build\Debug\Library.lib
build\Debug\Executable.exe
单独构建库文件
cmake --build . --target Library
单独构建可执行文件[这将自动构建可执行文件所依赖的库]
cmake --build . --target Executable
构建多个项目
\CMakeLists.txt
add_library(Library STATIC my_lib.cpp)
target_include_directories(Library PUBLIC "./")
\src\CMakeLists.txt
add_subdirectory(my_lib)
\src\my_lib\CMakeLists.txt
add_library(Library STATIC my_lib.cpp)
# 包含当前目录文件
target_include_directories(Library PUBLIC "./")
src\my_lib\my_lib.h
#pragma once
void print_hello_world();
\src\my_lib\my_lib.cpp
#include <iostream>
#include "my_lib.h"
void print_hello_world()
{
std::cout << "Hello World!\n";
}
\app\CMakeLists.txt
add_executable(Executable main.cpp)
# 链接库
target_link_libraries(Executable PUBLIC Library)
\app\main.cpp
#include <iostream>
#include "my_lib.h"
int main()
{
print_hello_world();
return 0;
}
\build
正常编译即可
cd build/
cmake ..
cmake --build .
执行
app\Debug\Executable.exe
CMake 变量,选项开关
可以在根目录下的最外一层
\CMakeLists.txt
中添加变量和选项,内层CMakeLists.txt
都可以引用变量\CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(CppProjectTemplate VERSION 1.0.0 LANGUAGES C CXX)
# C++版本为17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 关闭自定义扩展
set(CMAKE_CXX_EXTENSIONS OFF)
# 变量 设置变量 LIBRARY_NAME 值为 Library,自动识别为 字符串格式 “Library”
set(LIBRARY_NAME Library)
set(EXECUTABLE_NAME Executable)
# 选项 COMPILE_EXECUTABLE 选项为 关闭 ,中间为描述
option(COMPILE_EXECUTABLE "Whether to compile the executable" OFF)
# add_subdirectory 要遵从依赖顺序 被依赖项目需要在前面
add_subdirectory(src)
# 如果开启,则构建可执行文件 可使用 else()
if (COMPILE_EXECUTABLE)
add_subdirectory(app)
endif()
编译时传递变量 COMPILE_EXECUTABLE=ON 变量需加上前缀
D
cd build/
cmake .. -DCOMPILE_EXECUTABLE=ON
添加一个编译前脚本Makefile文件来清空build文件夹
\Makefile
prepare:
rm -rf build
mkdir build
执行该脚本[windows下暂不可用]
make prepare
优化 CMakeLists
CMake 历史悠久,老版本的教程不再适应现代语言的工程化 绝大部分教程不应使用[例如通过脚本扫描所有文件以编译]
集合 源文件cpp和头文件h
set(LIBRARY_SOURCES
"my_lib.cc"
"my_lib2.cc")
set(LIBRARY_HEADERS
"my_lib.h"
"my_lib2.h")
add_library(${LIBRARY_NAME} STATIC
${LIBRARY_SOURCES}
${LIBRARY_HEADERS})
target_include_directories(${LIBRARY_NAME} PUBLIC
"./"
"${CMAKE_BINARY_DIR}/configured_files/include")
set(EXE_SOURCES
"main.cc")
add_executable(${EXECUTABLE_NAME} ${EXE_SOURCES})
target_link_libraries(${EXECUTABLE_NAME} PUBLIC ${LIBRARY_NAME})
仅构建指定目标
在具由多个项目和目标的项目中,只编译指定目标,例如特定库 库名 Library 已定义
cmake --build . --target Library
使用 Graphviz 分析项目的依赖关系 导出依赖图
编写
\Makefile
dependency:
cd build && cmake .. --graphviz=graph.dot && dot -Tpng graph.dot -o graphImage.png
创建目录
build
[否则报错] 生成依赖图 [项目根目录下执行]
make dependency
依赖图位置为
\build\graphImage.png
Fetch Content 第三方库
第三方库需要支持cmake,即包含
CMakeLists.txt
文件 FetchContent 是 CMake 提供的一个模块,允许在构建过程中从各种来源下载、构建和可选地安装内容。它尤其对于依赖管理和项目集成非常有用。
定义并下载第三方库
\CMakeLists.txt
include(FetchContent)
# 定义第三方库
FetchContent_Declare(
# 库名
nlohmann_json
# 网址
GIT_REPOSITORY https://github.com/nlohmann/json
# 版本
GIT_TAG v3.11.2
# 关闭循环深克隆
GIT_SHALLOW TRUE)
# 下载到本地
FetchContent_MakeAvailable(nlohmann_json)
FetchContent
模块会将内容(例如,从Git仓库下载的项目)放置在构建树的一个特定子目录中。默认情况下,这个目录是在你的构建目录(通常是当前的CMake构建目录)下的_deps
子目录
在app中使用第三方库
\app\CMakeLists.txt
set(EXE_SOURCES
"main.cpp")
add_executable(${EXECUTABLE_NAME} ${EXE_SOURCES})
target_link_libraries(${EXECUTABLE_NAME} PUBLIC
${LIBRARY_NAME}
nlohmann_json)
\app\main.cpp
#include <iostream>
#include <nlohmann/json.hpp>
#include "config.hpp"
#include "my_lib.h"
int main()
{
std::cout << project_name << '\n';
std::cout << project_version << '\n';
std::cout << "JSON:" << NLOHMANN_JSON_VERSION_MAJOR << "."
<< NLOHMANN_JSON_VERSION_MINOR << "."
<< NLOHMANN_JSON_VERSION_PATCH << "\n";
print_hello_world();
return 0;
}
库中使用第三方
\src\my_lib\CMakeLists.txt
set(LIBRARY_SOURCES
"my_lib.cc")
set(LIBRARY_HEADERS
"my_lib.h")
set(LIBRARY_INCLUDES
"./"
"${CMAKE_BINARY_DIR}/configured_files/include")
add_library(${LIBRARY_NAME} STATIC
${LIBRARY_SOURCES}
${LIBRARY_HEADERS})
target_include_directories(${LIBRARY_NAME} PUBLIC
${LIBRARY_INCLUDES})
target_link_libraries(${LIBRARY_NAME} PUBLIC
nlohmann_json::nlohmann_json
fmt::fmt
spdlog::spdlog
cxxopts::cxxopts)
\src\my_lib\my_lib.cpp
#include <iostream>
#include <nlohmann/json.hpp>
#include "my_lib.h"
void print_hello_world()
{
std::cout << "Hello World!\n";
std::cout << "JSON Lib Version:" << NLOHMANN_JSON_VERSION_MAJOR << "."
<< NLOHMANN_JSON_VERSION_MINOR << "."
<< NLOHMANN_JSON_VERSION_PATCH << "\n";
}
使用 doxygen 生成文档
新建文档目录
docs
\docs\Doxyfile
定义文档信息
# Configuration for Doxygen for use with CMake
# Only options that deviate from the default are included
# To create a new Doxyfile containing all available options, call `doxygen -g`
# ---------------------------------------------------------------------------
# Project related configuration options
# ---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "C++ Project Template"
PROJECT_NUMBER = 1.0
PROJECT_BRIEF =
PROJECT_LOGO =
OUTPUT_DIRECTORY = ./
OUTPUT_LANGUAGE = English
MARKDOWN_SUPPORT = YES
# ---------------------------------------------------------------------------
# Build related configuration options
# ---------------------------------------------------------------------------
EXTRACT_ALL = YES
RECURSIVE = YES
GENERATE_HTML = YES
GENERATE_LATEX = NO
# ---------------------------------------------------------------------------
# Configuration options related to the input files
# ---------------------------------------------------------------------------
INPUT = ../src \
INPUT ../include \
INPUT ../app
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.c \
*.cc \
*.cpp \
*.c++ \
*.h \
*.hpp \
*.h++ \
*.md \
*.dox \
*.doc \
*.txt
新建 cmake 目录存放自定义函数
\cmake\Docs.cmake
find_package(Doxygen)
# CMAKE_SOURCE_DIR 代表项目根目录
if (DOXYGEN_FOUND)
add_custom_target(
docs
${DOXYGEN_EXECUTABLE}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/docs
)
endif()
添加注释
app\main.cpp
#include <iostream>
/**
* @brief Prints out hello world .
*
*/
int main()
{
std::cout << "Hello World!\n";
return 0;
}
编译 构建
cd build/
cmake ..
cmake --build .
生成文档
cd docs
doxygen
在 VSCode 底部栏快捷运行
点击生成【首次需要配置编译环境 可选择 gcc 等】 点击
all
,输入docs
命令
使用 CPM 管理第三方库
https://github.com/cpm-cmake/CPM.cmake
下载 https://github.com/cpm-cmake/CPM.cmake/releases 中的
CPM.cmake
文件 放入项目 cmake 目录下\CMakeLists.txt
中添加
include(CPM)
cpmaddpackage("gh:nlohmann/json#v3.11.2")
此处等同
https://github.com/nlohmann/json
库的版本v3.11.2
在app中使用第三方库
\app\CMakeLists.txt
set(EXE_SOURCES
"main.cpp")
add_executable(${EXECUTABLE_NAME} ${EXE_SOURCES})
target_link_libraries(${EXECUTABLE_NAME} PUBLIC
${LIBRARY_NAME}
nlohmann_json)
使用 Conan 管理库
项目根目录下新建
\conanfile.py
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain
class CompressorRecipe(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "CMakeDeps"
def requirements(self):
// 依赖添加处
self.requires("nlohmann_json/3.11.2")
def generate(self):
tc = CMakeToolchain(self)
tc.user_presets_path = False
tc.generate()
\CMakeLists.txt
# CMAKE_BINARY_DIR 表示编译目录 此处即 build
include(${CMAKE_BINARY_DIR}/conan_toolchain.cmake)
find_package(nlohmann_json REQUIRED)
\Makefile
如果Conan没有预编译文件,则在本地编译--build missing
# 调试版本
conan_d:
rm -rf build
mkdir build
cd build && conan install .. -s build_type=Debug -s compiler.cppstd=17 --output-folder=. --build missing
# 发布版本
conan_r:
rm -rf build
mkdir build
cd build && conan install .. -s build_type=Release -s compiler.cppstd=17 --output-folder=. --build missing
先下载依赖 make 命令,在项目根目录执行
make conan_d
编译 构建
cd build/
cmake ..
cmake --build .
使用 vcpkg 管理库
https://github.com/microsoft/vcpkg https://vcpkg.io/en/ https://github.com/microsoft/vcpkg/blob/master/README_zh_CN.md
安装 建议在项目的
\external
目录下安装vcpkg 因为vcpk路径需要硬编码到项目中,如果在系统全局安装,需要频繁更改该路径
cd external
git clone https://github.com/microsoft/vcpkg
.\vcpkg\bootstrap-vcpkg.bat
cd vcpkg
# 查看本次vcpkg的git log
git log
08a6cdd73b8dd750778fa3d84cf2cf844e24530f
新建
\vcpkg.json
{
"name": "cpptemplateproject",
"version-string": "1.0.0",
"dependencies": [
{
"name": "cxxopts",
"version>=": "3.1.1"
}
],
"overrides": [
{
"name": "catch2",
"version": "2.13.9"
},
{
"name": "catch2",
"version>=": "2.13.9"
}
],
"builtin-baseline": "08a6cdd73b8dd750778fa3d84cf2cf844e24530f"
}
将最新的 git log值赋予
\vcpkg.json
的builtin-baseline
dependencies
只会下载最新库overrides
才可以指定精确版本
\CMakeLists.txt
include(${CMAKE_SOURCE_DIR}/external/vcpkg/scripts/buildsystems/vcpkg.cmake)
find_package(nlohmann_json REQUIRED)
编译构建
cd build/
cmake ..
cmake --build .
cmake ..
编译时,将会将vcpkg包下载到external\vcpkg\packages\cxxopts_x64-windows
等目录中供后续构建cmake ..
编译后,编辑器将可以正确解析新的库
示例
vcpkg.json
{
"name": "cpptemplateproject",
"version-string": "1.0.0",
"dependencies": [
{
"name": "cxxopts",
"version>=": "3.1.1"
}
],
"overrides": [
],
"builtin-baseline": "40619a55c3e76dc4005c8d1b7395071471bb8b96"
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(CppProjectTemplate VERSION 1.0.0 LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS ON)
set(EXECUTABLE_NAME Executable)
# 硬编码 vdpkg 路径
include(${CMAKE_SOURCE_DIR}/external/vcpkg/scripts/buildsystems/vcpkg.cmake)
# 手动找寻包
find_package(cxxopts REQUIRED)
# CMake 载入并执行当前项目目录下的 cmake 子目录中的 Docs.cmake 文件
add_subdirectory(app)
external\vcpkg
build
app\CMakeLists.txt
set(EXE_SOURCES
"main.cpp")
add_executable(${EXECUTABLE_NAME} ${EXE_SOURCES})
# 链接包
target_link_libraries(
${EXECUTABLE_NAME}
cxxopts::cxxopts)
app\main.cpp
#include <iostream>
#include <cxxopts.hpp>
/**
* @brief Prints out hello world .
*
*/
int main()
{
std::cout << "Hello World!\n";
std::cout << "CXXOPTS:" << CXXOPTS__VERSION_MAJOR << "."
<< CXXOPTS__VERSION_MINOR << "." << CXXOPTS__VERSION_PATCH
<< "\n";
return 0;
}
cd build/
cmake ..
cmake --build .
app\Debug\Executable.exe
显示
Hello World!
CXXOPTS:3.1.1