C++ 完整构建实践:库、CLI、测试与安装后消费 · P2Report完整构建项目输入输出是什么至P2Report的库CLI测试消费者有哪些完整文件
本系列以前置值语义、RAII、基本线程/函数接口为基础。示例未执行;本轮仅完成静态文本与模板核对,不将预期输出称为实际运行结果。
专题入口:C++ 大型构建模型:目标图、阶段与缓存 · C++ 构建依赖:PUBLIC、私有头与可搬移包 · C++ 完整构建实践:库、CLI、测试与安装后消费 · C++ 构建排错:头文件、包发现、配置与规模
C++ 完整构建实践:库、CLI、测试与安装后消费
本系列以前置值语义、RAII、基本线程/函数接口为基础。示例未执行;本轮仅完成静态文本与模板核对,不将预期输出称为实际运行结果。
专题入口:C++ 大型构建模型:目标图、阶段与缓存 · C++ 构建依赖:PUBLIC、私有头与可搬移包 · C++ 完整构建实践:库、CLI、测试与安装后消费 · C++ 构建排错:头文件、包发现、配置与规模
P2Report完整构建项目输入输出是什么
P2Report完整构建项目输入输出是什么:库 summarize(string_view) 返回字节数和换行符数;CLI 只接收一个文本参数。abc 的预期结果为 bytes=3 newlines=0 version=1.0.0,参数数量不对返回2。这里不把 UTF-8 字节数称为字符数,也不把无结尾换行的文本推成“行数”。
项目没有运行时外部依赖、数据库或下载步骤。输入借用只在 summarize 调用期间使用,不保留指针;返回 Report 是独立值,version 返回静态字面量视图。tests 显式检查空输入与 ab 换行 c 换行,避免只测无换行的快乐路径。示例未执行,以上为规格。
P2Report的库CLI测试消费者有哪些完整文件
P2Report的库CLI测试消费者有哪些完整文件:上游 CMakeLists.txt 管理实体库、接口目标、CLI、测试与安装规则,consumer 有自己的 CMakeLists.txt 和 main.cpp。以下直接包含同一工程的全部文件,README 与版本文件也来自源码,不复制维护另一份示例。
阅读顺序建议公共头 → report.cpp → 顶层目标图 → tests → Config模板 → 独立consumer。先检查各 include 的拥有者,再检查导出集是否完整;可在纸上画 p2-report → report → options 与 p2-consumer → imported report → imported options 两条图,对照 build/install 接口差异。
.gitignore(同源文件,示例未执行)
/out/
/stage/
/CMakeUserPresets.jsonCMakeLists.txt(同源文件,示例未执行)
cmake_minimum_required(VERSION 3.28)
project(P2Report VERSION 1.0.0 LANGUAGES CXX)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
include(CTest)
add_library(p2_options INTERFACE)
add_library(P2::options ALIAS p2_options)
set_target_properties(p2_options PROPERTIES EXPORT_NAME options)
target_compile_features(p2_options INTERFACE cxx_std_20)
add_library(p2_report STATIC src/report.cpp)
add_library(P2::report ALIAS p2_report)
set_target_properties(p2_report PROPERTIES EXPORT_NAME report)
target_link_libraries(p2_report PUBLIC P2::options)
target_include_directories(p2_report
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}/generated)
configure_file(cmake/version.hpp.in generated/version.hpp @ONLY)
if(MSVC)
target_compile_options(p2_report PRIVATE /W4)
else()
target_compile_options(p2_report PRIVATE -Wall -Wextra -Wpedantic)
endif()
add_executable(p2-report app/main.cpp)
target_link_libraries(p2-report PRIVATE P2::report)
if(BUILD_TESTING)
add_executable(p2-report-tests tests/report_test.cpp)
target_link_libraries(p2-report-tests PRIVATE P2::report)
add_test(NAME report.contract COMMAND p2-report-tests)
endif()
install(TARGETS p2_report p2_options EXPORT P2ReportTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(TARGETS p2-report RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
set(P2Report_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/P2Report")
configure_package_config_file(cmake/P2ReportConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/P2ReportConfig.cmake
INSTALL_DESTINATION ${P2Report_INSTALL_CMAKEDIR})
write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/P2ReportConfigVersion.cmake
VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion)
install(EXPORT P2ReportTargets NAMESPACE P2::
DESTINATION ${P2Report_INSTALL_CMAKEDIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/P2ReportConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/P2ReportConfigVersion.cmake
DESTINATION ${P2Report_INSTALL_CMAKEDIR})CMakePresets.json(同源文件,示例未执行)
{
"version": 8,
"cmakeMinimumRequired": {"major": 3, "minor": 28, "patch": 0},
"configurePresets": [
{"name": "base", "hidden": true, "generator": "Ninja",
"binaryDir": "${sourceDir}/out/${presetName}",
"cacheVariables": {"BUILD_TESTING": "ON"}},
{"name": "debug", "inherits": "base", "cacheVariables": {"CMAKE_BUILD_TYPE": "Debug"}},
{"name": "release", "inherits": "base", "cacheVariables": {"CMAKE_BUILD_TYPE": "Release"}}
],
"buildPresets": [
{"name": "debug", "configurePreset": "debug"},
{"name": "release", "configurePreset": "release"}
],
"testPresets": [
{"name": "debug", "configurePreset": "debug", "output": {"outputOnFailure": true},
"execution": {"noTestsAction": "error"}},
{"name": "release", "configurePreset": "release", "output": {"outputOnFailure": true},
"execution": {"noTestsAction": "error"}}
]
}README.md(同源文件,示例未执行)
# P2Report:目标、安装包与下游消费者
示例未执行。项目版本 1.0.0;CMake 3.28 / C++20 / Ninja 的教学规格见 VERSIONS.md。无第三方库、无下载脚本。库统计字节数与换行符数量,不做 Unicode 字符或逻辑行数解释。CLI 从一个参数读取文本,参数不足/过多返回2;成功返回0。
## 文件职责
include/p2/report.hpp 是公共 API;src/detail.hpp 和生成的 version.hpp 只由库实现使用。p2_options 是 INTERFACE 用法要求目标,p2_report 是 STATIC 实体目标;库通过 PUBLIC 连接 options,让下游获得 C++20。CLI 和测试 PRIVATE 连接库。consumer 是完全独立的 CMake 工程,只能通过安装包找到库;它不使用 add_subdirectory 或源码目录的绝对路径。
## 读者操作规格(本轮禁止执行)
以下均在 examples/native/p2-cmake-project 目录。PowerShell、Bash、Zsh 可逐行执行相同 cmake/ctest 命令;先确认 cmake、Ninja 与本机 C++20 工具链已经就绪。不要把下面的预期结果当作已取得的运行证据。
```text
cmake --preset debug
cmake --build --preset debug
ctest --preset debug
cmake --preset release
cmake --build --preset release
ctest --preset release
cmake --install out/release --prefix stage
```
Debug 与 Release 的 report.contract 都预期通过。CTest 测试程序不用 assert,Release 下也检查空输入、5字节2换行和版本值;标准输出 report: contracts passed。CLI Windows:`./out/debug/p2-report.exe abc`;Linux/macOS:`./out/debug/p2-report abc`;预期 bytes=3 newlines=0 version=1.0.0。
安装前缀 stage 是当前项目内的相对路径,重新运行安装会覆盖其中的同名产物。安装不是打包:本例不生成 CPack 归档,不提供卸载脚本,不改全局前缀。导出的 P2::report 和 P2::options 与源码树 ALIAS 名称一致;静态库、公共头、Targets/Config/Version 文件一起构成安装包。
独立 consumer 使用安装前缀的绝对路径,避免相对路径含义混淆:
PowerShell:
```powershell
$p2Prefix = (Resolve-Path ./stage).Path
cmake -S consumer -B out/consumer -G Ninja -DCMAKE_BUILD_TYPE=Release "-DCMAKE_PREFIX_PATH=$p2Prefix"
cmake --build out/consumer
./out/consumer/p2-consumer.exe
```
Bash/Zsh:
```sh
p2_prefix="$(pwd)/stage"
cmake -S consumer -B out/consumer -G Ninja -DCMAKE_BUILD_TYPE=Release "-DCMAKE_PREFIX_PATH=$p2_prefix"
cmake --build out/consumer
./out/consumer/p2-consumer
```
预期 consumer bytes=4 newlines=1、退出0。CMake 默认可能选 lib64;CMAKE_PREFIX_PATH 指向前缀而不是臆测 lib 路径。查包仍失败时读取 stage 下实际的 P2ReportConfig.cmake 位置并用 P2Report_DIR 指向其目录,不要修改消费者为源码 include 路径。
## 验收与负例
1. 公共头/定义在 include 与库内一致,空串、换行、版本规格由 tests 覆盖。
2. 安装后独立配置 consumer,确认 C++20 与 include 路径随 imported target 传播;此步骤未执行。
3. 把库 PUBLIC 头目录改 PRIVATE,应在编译 CLI/消费者时找不到 p2/report.hpp;把 options 遗漏出 export 集应导致导出依赖不完整。这些是读者可做的负例,本轮均未执行。
4. 改生成器或工具链时使用新的 out 子目录;不要在既有缓存上直接替换编译器。
大型项目沿相同依赖图按模块分目录;接口变化触及所有消费者,私有实现变化应局限库重编及必要重链接。Unity build、PCH、LTO、并行度是独立的测量决策,本例不默认启用,也不承诺速度提升。VERSIONS.md(同源文件,示例未执行)
# 教学版本与验证边界
示例未执行。核对日期:2026-10-17。教学基础固定 CMake 3.28 文档(3.28.6 手册)、Presets schema 8、C++20;这不是最新版本或维护支持声明。Ninja 与支持 C++20 的编译器/标准库须由读者预先准备,本轮没有安装或探测。
Windows 原生可用已初始化 MSVC 开发者 PowerShell + Ninja;Linux 可用 GCC/Clang + Ninja;macOS 可用 AppleClang + Ninja。具体编译器小版本、SDK 和架构待实际记录;三平台均未配置、未构建、未测试、未安装。单配置 Ninja 中 Debug/Release 各有独立缓存。预期安装只在同平台、同架构、兼容 ABI 的工具链内消费,不承诺预编译库跨工具链通用。
资料:[Presets 3.28](https://cmake.org/cmake/help/v3.28/manual/cmake-presets.7.html)、[导入导出](https://cmake.org/cmake/help/v3.28/guide/importing-exporting/index.html)、[包配置辅助](https://cmake.org/cmake/help/v3.28/module/CMakePackageConfigHelpers.html)。app/main.cpp(同源文件,示例未执行)
#include <p2/report.hpp>
#include <iostream>
int main(int argc, char** argv) {
if (argc != 2) {
std::cerr << "usage: p2-report TEXT\n";
return 2;
}
const auto result = p2::summarize(argv[1]);
std::cout << "bytes=" << result.bytes << " newlines=" << result.newlines
<< " version=" << p2::version() << '\n';
return 0;
}cmake/P2ReportConfig.cmake.in(同源文件,示例未执行)
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/P2ReportTargets.cmake")
check_required_components(P2Report)cmake/version.hpp.in(同源文件,示例未执行)
#pragma once
#define P2_REPORT_VERSION "@PROJECT_VERSION@"consumer/CMakeLists.txt(同源文件,示例未执行)
cmake_minimum_required(VERSION 3.28)
project(P2Consumer LANGUAGES CXX)
find_package(P2Report 1 CONFIG REQUIRED)
add_executable(p2-consumer main.cpp)
target_link_libraries(p2-consumer PRIVATE P2::report)consumer/main.cpp(同源文件,示例未执行)
#include <p2/report.hpp>
#include <iostream>
int main() {
const auto result = p2::summarize("xyz\n");
if (result.bytes != 4 || result.newlines != 1) return 1;
std::cout << "consumer bytes=" << result.bytes << " newlines=" << result.newlines << '\n';
return 0;
}include/p2/report.hpp(同源文件,示例未执行)
#pragma once
#include <cstddef>
#include <string_view>
namespace p2 {
struct Report { std::size_t bytes; std::size_t newlines; };
Report summarize(std::string_view text) noexcept;
std::string_view version() noexcept;
}src/detail.hpp(同源文件,示例未执行)
#pragma once
namespace p2::detail {
constexpr bool is_newline(char ch) noexcept { return ch == '\n'; }
}src/report.cpp(同源文件,示例未执行)
#include <p2/report.hpp>
#include "detail.hpp"
#include "version.hpp"
namespace p2 {
Report summarize(std::string_view text) noexcept {
Report report{text.size(), 0};
for (char ch : text) {
if (detail::is_newline(ch)) ++report.newlines;
}
return report;
}
std::string_view version() noexcept { return P2_REPORT_VERSION; }
}tests/report_test.cpp(同源文件,示例未执行)
#include <p2/report.hpp>
#include <iostream>
int main() {
const auto empty = p2::summarize("");
const auto text = p2::summarize("ab\nc\n");
if (empty.bytes != 0 || empty.newlines != 0 || text.bytes != 5 ||
text.newlines != 2 || p2::version() != "1.0.0") {
std::cerr << "report contract failed\n";
return 1;
}
std::cout << "report: contracts passed\n";
return 0;
}继续阅读
- 知识专题
- 排错
- 知识专题