C++构建系统
当C++系统依赖模块多了之后,构建系统本身的工程质量就成为了重要的瓶颈,这方面开源项目里做的最好的是Chromium项目。
全局视角
构建系统有很多东西,从全局视角来说,这是一个目录
- 理解 C/C++ 代码的基本编写
- 理解 C99
- 理解 C++ 11/14/17/20 标准
- 理解 C++ 现代多线程支持
- 掌握 Google 编码规范
- 理解 build-system 最终是通过 gcc/g++ 执行命令的,因此需要理解 gcc 的构建流程
- 理解 GCC 的预处理,编译,链接,汇编
- 理解静态链接,动态链接,理解静态库和动态库
- 理解导出符号和导入符号
- 理解 GCC 的编译参数
- 理解 GCC 的警告选项
- 理解 GCC 的编译和链接优化选项
- 理解 GCC 的预处理,编译,链接,汇编
- 理解 build-system, meta-build-system, package manager
- 最基本的,要熟悉 build-system 的 Makefile
- 作为对比,应该熟悉 Ninja
- 在这个基础上,理解 meta-build-system: CMake
- 作为对比,应该熟悉 GN
- 在这个基础上,理解 pacakge-manager : Conan
- 最基本的,要熟悉 build-system 的 Makefile
- 理解 单元测试,例如 google test
- 进而理解 测试覆盖率,gcov, lcov 工具
- 只有 build-system 还是不够的,常常需要 Python编写一个 build 套装,熟悉并理解 Python
- 如何用 Python 执行命令
- 如何用 Python 做文件操作
- 如何用 Python 做环境变量变更
前置依赖
理解 GCC 的 Warning 选项
-Werror
: 把警告当作错误- 相反的是:
-Wno-error
- 相反的是:
-Wfatal-errors
: 警告当作错误,遇到第一个Warning编译器就当作出错退出- 相反的是:
-Wno-fatal-errors
- 相反的是:
-Wall
: 打开除了extra
的所有警告-Wextra
: 打开extra
的所有警告-Werror=xxx
: 将xxx警告视为错误
-Wall 打开的警告包含这些:
-Waddress
-Warray-bounds=1 (only with -O2)
-Warray-compare
-Warray-parameter=2 (C and Objective-C only)
-Wbool-compare
-Wbool-operation
-Wc++11-compat -Wc++14-compat
-Wcatch-value (C++ and Objective-C++ only)
-Wchar-subscripts
-Wcomment
-Wdangling-pointer=2
-Wduplicate-decl-specifier (C and Objective-C only)
-Wenum-compare (in C/ObjC; this is on by default in C++)
-Wenum-int-mismatch (C and Objective-C only)
-Wformat
-Wformat-overflow
-Wformat-truncation
-Wint-in-bool-context
-Wimplicit (C and Objective-C only)
-Wimplicit-int (C and Objective-C only)
-Wimplicit-function-declaration (C and Objective-C only)
-Winit-self (only for C++)
-Wlogical-not-parentheses
-Wmain (only for C/ObjC and unless -ffreestanding)
-Wmaybe-uninitialized
-Wmemset-elt-size
-Wmemset-transposed-args
-Wmisleading-indentation (only for C/C++)
-Wmismatched-dealloc
-Wmismatched-new-delete (only for C/C++)
-Wmissing-attributes
-Wmissing-braces (only for C/ObjC)
-Wmultistatement-macros
-Wnarrowing (only for C++)
-Wnonnull
-Wnonnull-compare
-Wopenmp-simd
-Wparentheses
-Wpessimizing-move (only for C++)
-Wpointer-sign
-Wrange-loop-construct (only for C++)
-Wreorder
-Wrestrict
-Wreturn-type
-Wself-move (only for C++)
-Wsequence-point
-Wsign-compare (only in C++)
-Wsizeof-array-div
-Wsizeof-pointer-div
-Wsizeof-pointer-memaccess
-Wstrict-aliasing
-Wstrict-overflow=1
-Wswitch
-Wtautological-compare
-Wtrigraphs
-Wuninitialized
-Wunknown-pragmas
-Wunused-function
-Wunused-label
-Wunused-value
-Wunused-variable
-Wuse-after-free=2
-Wvla-parameter (C and Objective-C only)
-Wvolatile-register-var
-Wzero-length-bounds
- clang-tidy:
GCC 编译和链接问题
收集C++构建系统(2023):
C++ 的构建系统,分为3层:Build-System, Meta-Build-System, Package-Manager
- Build-System
- Makefile
- Ninja
- Meta-Build-System
- CMake:
- GN
- GN
- gn_quick_start
- 当前Chromium构建系统
- GYP
- GYP Generate Your Project
- 前Chromium构建系统
- GYP Generate Your Project
- Bazel
- AutoTools
- Package-Manager
Makefile 的FAQ
阮一峰:Make 命令教程
makefiletutorial
Makefile 里执行 Shell 命令要点
- shell 脚本在 target 里执行才有效,其他地方都被忽略
- make 把每一行 Shell 脚本当作一个独立的单元,在单独的进程中运行,因此如果要想配置变量后执行,必须放在同一行。
- make 在调用 Shell 之前进行预处理,既展开所有的 Makefile 的变量和函数,这些变量和函数都以
$
开头。 - make 预处理时,所有以
$
开头都,都会被展开,因此 Shell 语句要引用自己的变量,就应该以$$
开头,此外 Shell 变量时不需要括号的,在.sh文件里用$xx
,在Makefile里就得写成$$xx
- wildcard 通配符查找文件
- substr 替换字符串
- patsubstr 通配符替换字符串
- filter 从用空格分割的字符串列表里保留含有通配符的条目
- filter-out 从用空格分割的字符串列表里过滤掉含有通配符的条目
CMake FAQ
成为 CMake 熟练工,孰能生巧。
- 基本变量
- 基本函数
- add_subdirectory : 进来用子目录+子目录/CMakeLists.txt 来层次化的组织子模块和子模块构建
- add_executable
- add_library
- include_directories
- target_include_directories
- link_libraries
- target_link_libraries
- add_subdirectory : 进来用子目录+子目录/CMakeLists.txt 来层次化的组织子模块和子模块构建
配置的环境问题
- 构建系统依赖的环境非常复杂
- 操作系统的环境变量
- docker 的环境变量
- 命令行选项
- 从配置文件里读取的配置
- 构建系统对环境的使用也很复杂
- 有些是依赖命令输入参数
- 有些是依赖默认参数
- 有些是依赖操作系统/docker的环境变量
- 典型例子:
- 一个构建系统,同时有 make/cmake/conan包管理,三者对变量的使用像意大利面条一样
Conan 包管理
- 以conan包管理为例子,这些都会是构建系统的卡点
- 是否要下载二进制包
- 是否要远端构建后下载(远端算力问题,没算力要等待)
- 拉源代码的耗时
- 本地编译的耗时
- conan commands 文档: https://docs.conan.io/1/reference/commands.html
构建时间成本
- build 的时间 = compile的时间 + link的时间 + 除错的时间 + 测试执行的时间
- compile 的时间 = O(源代码数量)
- link 的时间 = O(每个模块export的符号数)
- 因此要控制每个模块 export 的符号数,非必要不导出
- 除错的时间=除代码错误的时间+除构建脚本错误的时间
- 除构建脚本的时间 = 除Makefile错误的时间 + 除CMake错误的时间+除conan包配置错误的时间
- 除CMake错误的时间 = include错误 + link 错误
- CMake 基本变量,option, 函数不熟悉的时间
- include 错误 = include_directories 错误 + target_include_directories 错误
- link 错误 = link_libraries 错误 + target_link_libraries 错误
- compile 错误 = compile options 错误 + compile flag 错误
- compile flag 错误 = CMAKE_C_FLAGS 错误 + CMAKE_CXX_FLAGS 错误
- 测试执行的时间 = 除测试依赖错误的时间 + 除测试耗时未知模块的时间
- 除测试依赖错误的时间 = 除 LD_LIBRARY_PATH 缺失的时间 + 除数据依赖缺失的时间
- make -j n 可以指定同时参与 build 的 cpu 核心数
- 加钱可以解决的问题,就加钱,加机器配置
- Chromium 的 Ninja 就是为了加快编译速度的动机搞出来的。
Chromium 项目的构建系统
- 跨平台构建系统: GN: https://gn.googlesource.com/gn/+/HEAD/docs/
- 编译后端:ninja: https://ninja-build.org/
- dev_tool: https://www.chromium.org/teams/devtools/
- 仓库内的多模块管理: // todo
单元测试 & 测试覆盖分析工具
- google test
- gcov
- lcov
- lcov-result-merger
- 使用gpt轻松转换成了python版本
- 写了一个python给lcov输出的多项目html子文件夹合成顶级index.html的代码, todo: 上链接
- lcov-result-merger
顶级软件的测试
收集C++静态/动态代码分析软件(2023)
- 商业软件:
- 二进制文件分析
- GNU binutils: https://www.gnu.org/software/binutils/
- nm, objdump, readelf ..
- GNU binutils: https://www.gnu.org/software/binutils/
- 代码安全
- https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard
- Secure Coding in C and C++, 2nd Edition:https://www.informit.com/store/secure-coding-in-c-and-c-plus-plus-9780321822130
- 圈复杂度计算:
- parasoft (会计算宏展开)
- lizard(不计算宏展开): https://github.com/terryyin/lizard
- pip install lizard
- 用法:
lizard -l cpp -s cyclomatic_complexity
- pmccabe(不计算宏展开): https://github.com/datacom-teracom/pmccabe
- apt-get inistall pmccabe
- 用法:
ind . -name '*.cpp' -print | xargs pmccabe
程序崩溃分析
- Linux
- coredump
- 启用 core dump 命令: ulimit -c unlimited
- 执行程序,如果崩溃生成 core 文件
- gdb executable-file core-dump-file,更多命令问GPT好了
- bt 查看崩溃堆栈
- info threads 查看线程信息
- thread n 切换到线程 n
- frame n 切换到第几帧
- thread n 切换到线程 n
- minidump : https://chromium.googlesource.com/chromium/src/+/HEAD/docs/linux/minidump_to_core.md
- Google breakpad : https://github.com/google/breakpad
- coredump
- Windows
Python作为构建系统脚本的常见问题
- 使用Python编写的时候,容易没有模块化设计,一堆散装脚本放在一起,没有统一的入口
- 最佳实践:提供统一的main.py 入口,使用统一的路由命令行设计,例如,使用 pyouter: https://github.com/fanfeilong/pyouter
- 每个子命令,应该是一个Action子模块
- 可以有公共的 common 基础库
- 对于工具链整合,很容易全部用 os.system, subprocess.popen, subprocess.run 执行系统命令来完成一系列子命令的组合执行
- 核心问题是状态的控制是非常规的:
- 命令的执行应该是同步的还是异步的
- 命令执行的输出日志需要重定向到 stdout,stdpipe,还是日志文件
- 命令的执行进度条是否要回显
- 子命令执行的结果,错误码如何正确处理
- 如何并发执行一组相同的可并行操作
- 另外一个问题是碎片化导致的系统可观察性低,可诊断性差的问题,解决办法请看:Python执行命令的正确做法
- 核心问题是状态的控制是非常规的:
- 对于文件和文件夹的操作
- Python本身也有对文件系统的直接操作的库,如何保持一致或者确定边界。
- os.copy, os.rmdir, os.mkdir, os.makedirs
- shutil.copy, shutil.copy2, shutil.copytree, shutil.copytree2, shutil.rmtree, shutil.tar
- 使用 os.system("rm -rf xxx") 也能操作,优劣?
- 典型模式:
- 【生成/拷贝/移动】【文件/文件夹】前,判断是否已存在,做已存在处理,典型做法是删除
- Python本身也有对文件系统的直接操作的库,如何保持一致或者确定边界。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix