惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fortinet All Blogs
aimingoo的专栏
aimingoo的专栏
人人都是产品经理
人人都是产品经理
G
GRAHAM CLULEY
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
L
LangChain Blog
AWS News Blog
AWS News Blog
V
Vulnerabilities – Threatpost
博客园 - 司徒正美
Last Week in AI
Last Week in AI
P
Privacy International News Feed
C
CERT Recently Published Vulnerability Notes
Simon Willison's Weblog
Simon Willison's Weblog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Schneier on Security
Y
Y Combinator Blog
月光博客
月光博客
博客园 - Franky
T
Threatpost
Security Latest
Security Latest
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - 【当耐特】
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
大猫的无限游戏
大猫的无限游戏
A
Arctic Wolf
T
The Exploit Database - CXSecurity.com
A
About on SuperTechFans
I
Intezer
C
CXSECURITY Database RSS Feed - CXSecurity.com
C
Cisco Blogs
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
美团技术团队
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Microsoft Security Blog
Microsoft Security Blog
Know Your Adversary
Know Your Adversary
IT之家
IT之家
D
Docker
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
NISL@THU
NISL@THU
博客园 - 三生石上(FineUI控件)
L
Lohrmann on Cybersecurity
小众软件
小众软件
PCI Perspectives
PCI Perspectives
N
News and Events Feed by Topic
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Google DeepMind News
Google DeepMind News
爱范儿
爱范儿
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Engineering at Meta
Engineering at Meta

I'm OWenT

国产大模型(GLM 5.1、Kimi K2.6)真实场景效果和 Coding Plan 额度测试 新版本libatapp的连接管理——从etcd服务发现到拓扑驱动的自动重连 新版本libatbus的设计变更——从树形路由到拓扑驱动 Protobuf又一坑 - C++标准和ABI兼容性 AI真好用-给Blog主题统一加mermaid,chart.js,excalidraw,draw.io的多种引入方式支持 给内网部署Squid-通用HTTP下载缓存 UE使用CodeChecker和clang-tidy生成静态分析报告 找出UE的循环依赖 C++小协程栈和临时变量及作用域的栈溢出问题分析 游戏服务的可观测性能力建设(C++生态) 指标上报的多线程优化和多拉取源点优化 协程(libcopp)的Channel功能和CPU命中率优化 通用RPC代码生成器 实现strong_rc_ptr(比shared_ptr更快的引用计数智能指针) 手夯一个STL allocator和对象内存分析组件 std::condition_variable 的信号丢失问题 踩坑一处(GCC)STL std::async 实现BUG导致的crash问题 GCC 14的一个warning to error BUG 给xresloader(Excel导表工具)增强UE读表支持(包含蓝图,Blueprint) Opentelemetry社区在gRPC的几个链接问题(静态库和动态库混用,musl工具链,符号裁剪) Excel转表工具(xresloader)的新验证器(验证外部Excel和文本数据,唯一性和自定义规则) protobuf v22和gRPC v1.55版本升级的依赖变化和upb适配 关于protobuf近期版本(v20/v3.20+)和 gRPC v1.54版本在某些编译环境下的一些链接和编译问题 xresloader-Excel导表工具链的近期变更汇总 打通游戏服务端框架的C++20协程改造的最后一环 Opentelemetry-cpp的Logs模块标准更新(涉及近期版本:1.8-1.9的BREAK CHANGES) 给cmake-toolset和工具链(curl等)加HTTP/2和HTTP/3支持 又开新坑之 coredns 插件: nftables和filter 关于opentelemetry-cpp社区对于C++ Head Only组件单例和符号可见性的讨论小记 填个转表工具 xresloader 去年的坑(数组尾部裁剪) 集成 upb 和 lua binding 的踩坑小记 libcopp对C++20协程的接入和接口设计 再度优化GCC、LLVM、Clang、libc++、libc++abi等套件的构建脚本 游戏服务的分布式事务优化(二)- 事务管理 游戏服务的分布式事务优化(一)- Write Ahead Log(WAL) 模块 记录一些bazel适配用编译选项 测试现代化硬件C++浮点数性能和一致性 适配Boringssl和OpenSSL 3.0 近期cmake-toolset的一些适配问题 C++20 Text Formatting/fmtlib 适配问题小记 再次重构LLVM+Clang+libcxx+libc++abi+其他相关工具的构建流程 新版GCC和LLVM+Clang终于Release啦 折腾一下nftables下的双拨 [C++20] Module partitions和符号交叉引用(声明和实现分离) [Rust] 实现一个线程安全且迭代器可以保存的链表 基于protobuf的代码生成 几个使用protobuf中C++接口的Arena的坑 Amazon Aurora DB存储引擎论文阅读小记 近期对libatapp的一些优化调整(增加服务发现和连接管理,支持yaml等) xresloader转表工具链增加了一些新功能(map,oneof支持,输出矩阵,基于模板引擎的加载代码生成等) 在游戏服务器中使用分布式事务 libcopp接入C++20 Coroutine和一些过渡期的设计 libatbus 的大幅优化 nftables初体验 容器配置开发环境小计 PALM Tree - 适合多核并发架构的B+树 - 论文阅读小记 跨平台协程库 - libcopp 简介 C++20 Coroutine 性能测试 (附带和libcopp/libco/libgo/goroutine/linux ucontext对比) 尝鲜Github Action 一些xresloader(转表工具)的改进 protobuf、flatbuffer、msgpack 针对小数据包的简单对比 协程框架(libcopp) 小幅优化 Excel转表工具(xresloader) 增加protobuf插件功能和集成 UnrealEngine 支持 Anna(支持任意扩展和超高性能的KV数据库系统)阅读笔记 C++20 Coroutine libcopp merge boost.context 1.69.0 Google去中心化分布式系统论文三件套(Percolator、Spanner、F1)读后感 Rust玩具-企业微信机器人通用服务 使用ELK辅助监控开发测试环境服务质量和问题定位 2018年的新通用伪随机数算法(xoshiro / xoroshiro)的C++(head only)实现 Webpack+vue+boostrap+ejs构建Web版GM工具 Rust的第二次接触-写个小服务器程序 理解和适配AEAD加密套件 atsf4g-co的进化:协程框架v2、对象路由系统和一些其他细节优化 协程框架(libcopp)v2优化、自适应栈池和同类库的Benchmark对比 可执行文件压缩 初识Rust 使用restructedtext编写xresloader文档 atframework的etcd模块化重构 C++的backtrace ECDH椭圆双曲线(比DH快10倍的密钥交换)算法简介和封装 protobuf-net的动态Message实现 pbc的proto3接入 atgateway内置协议流程优化-加密、算法协商和ECDH 整理一波软件源镜像同步工具+DevOps工具 Blog切换到Hugo libcopp v2的第一波优化完成 libcopp(v2) vs goroutine性能测试 libcopp的线程安全、栈池和merge boost.context 1.64.0 GCC 7和LLVM+Clang+libc++abi 4.0的构建脚本 libatbus的几个藏得很深的bug 用cmake交叉编译到iOS和Android 开源项目得一些小维护 atapp的c binding和c#适配 对象路由系统设计 2016年总结 近期的一个协程流程BUG 重写了llvm+clang+libc++和libc++abi的构建脚本 atsf4g完整游戏工程示例|I'm OWenT atframework基本框架已经完成|I'm OWenT
重构基于CMake的构建工具链
owent · 2021-06-06 · via I'm OWenT

blog-website

背景

对大型项目来说,必然会有很多的依赖项。特别是现代化的组件都会尝试去复用社区资源。而对于C/C++而言,依赖管理一直是一个比较头大的问题。 很多老式的系统和工具都会尝试去走相对标准化的安装过程,比如说用 pkg-config 或者用系统自带的包管理工具装在系统默认路径里。 当然这样很不方便,也不容易定制组件。我使用 cmake 比较多,所以一直以来在我的 atframework 项目集中有一个 utility 项目 atframe_utils,里面包含一些常用的构建脚本。 并且在 atsf4g-co 中实现了一些简单的包管理和构建流程。

但是随着依赖日益复杂,在增加依赖和更新依赖的时候,测试多个包之间的兼容性也变得更加频繁,有时候也需要自己打patch。有些组件可能仅仅需要某几个依赖包,这时候也要导入 atframe_utils 的话就有点不合适了。 所以产生了把构建系统配置和包管理抽离出来单独维护的想法。

其实现在已经有挺多C/C++的包管理系统了。比较主流的有 bazel, vcpkg 等。还有一些不太主流的比如 conan, build2, cget, spack 等等。我没有去研究这里面每一种的细节和差异。 但是即便是比较主流的 bazelvcpkg,也无法满足我们的需求。

Bazel的问题

bazel 号称是原生支持分布式编译的构建系统。它的工程管理主要分两个阶段,首先是 WORKSPACE 声明阶段,大多数开源项目在这个阶段管理了依赖包的声明和配置;接下来是 BUILD 阶段,这就是实际编译执行的行为。

但是 bazel 有一些问题。首先,他需要所有的依赖包都提供 bazel 构建系统支持。现有支持 bazel 的包并不是特别多,而且即便支持,也并不是都支持得很好(有些环境编译还是有问题的)。有一点 bazel 还比较好的是,它的依赖包是靠包名来的索引的。所以当存在依赖包之间互相有依赖的时候,父级节点声明的包名标准化统一,那么也可以控制子依赖的版本。因为C/C++多个包之间的调用是直接使用符号的,所以并不能实现某些语言的同包名的多版本并存。上游系统有能力选择合适的依赖组合就非常重要。

其次 C/C++ 很多包和库都有功能开关,会根据系统环境和选项的不同来选择不同的功能开关组合。 而 bazel 构建的包,大多情况下由那个包本身去提供一些config,来实现不同的功能组,而编译的时候需要用户去设置使用哪些功能组。但是 C/C++ 的但大多数包和库都是通过检测环境和功能的方式多每个细节做切换的,每个功能之间的组合配置显然不实际,所以一般 bazel 构建的包都只会提供几个典型的选项,这对我们希望精确控制功能细节非常不友好。相比起来,我觉得 Rust 的 Cargo 和模仿 Cargo 的 build2 在这点上就做得非常好。

也可能是我对 bazel 的理解有限,我没有找到使用 bazel 做功能检测进行功能开关或者依赖包切换的方法。举个例子,我前段时间给 opentelemetry-cpp 提交了一个PR,就碰到了一些问题。opentelemetry-cpp 官方对编译器的支持是 GCC 4.8-最新,MSVC 2019+,Clang忘记版本最低要求了。其依赖的包里有gRPC和protobuf,其中gRPC又依赖abseil-cpp 。在使用GCC的时候:

  • gRPC 要 1.33 版本开始升级 了protobuf 到 1.34,这是第一个支持 bazel 4 的版本
  • 原先依赖的 gRPC 1.28 仅支持 bazel 3。
  • 但是 gRPC 要 1.34 版本开始 abseil-cpp 到 20200923.X ,这个版本开始不再支持 gcc 4.8 。

所以最佳的方法是高版本的gcc使用最新的gRPC,但是 gcc 4.8 仅使用 gRPC 1.33 。

  1. bazel select 和宏仅能在 BUILD 阶段使用,WORKSPACE 声明阶段无法使用。但是包的声明在 WORKSPACE 阶段。
  2. alias功能似乎也是 BUILD 阶段的功能,无法影响其他依赖库?
  3. 我也尝试过用 --override_repository 来覆盖包的信息,但是这个选项似乎仅仅能替换成本地路径。

总而言之,我没有找到合适的方法完成这个功能。希望有熟悉 bazel 的小伙伴能提供解决方案。

上面提到的我尝试的使用 --override_repository 方式类似这样:

maybe(
    http_archive,
    name = "com_github_grpc_grpc_legacy",
    strip_prefix = "grpc-1.33.2",
    urls = [
        "https://github.com/grpc/grpc/archive/v1.33.2.tar.gz",
    ],
)

maybe(
    http_archive,
    name = "com_github_grpc_grpc",
    sha256 = "abd9e52c69000f2c051761cfa1f12d52d8b7647b6c66828a91d462e796f2aede",
    strip_prefix = "grpc-1.38.0",
    urls = [
        "https://github.com/grpc/grpc/archive/v1.38.0.tar.gz",
    ],
)

然后命令行里执行 bazel build --override_repository=com_github_grpc_grpc=@com_github_grpc_grpc_legacy //...

vcpkg/conan 它不香吗?

那使用 vcpkg 或者 conan 之类的怎么样呢?首先我们可以在 vcpkg 的页面里找到其和 conan 的主要区别(https://github.com/microsoft/vcpkg/blob/master/docs/about/faq.md#why-not-conan)。简单地翻译一下区别如下:

Vcpkg VS Conan:

  1. Conan仅提供工具,不保证包的质量和互相兼容性。编译环境支持较vcpkg更多。
  2. Vcpkg统一了包管理副本,而Conan需要用户自己负责包之间的兼容性和搭配。也可能多个包的依赖同一个包的不同版本,这在C/C++里十分危险。
  3. Vcpck基于CMake,Conan基于Python,但是包构建过程大多数又依赖cmake。

显然易用性和安全性 vcpkg 好很多,而且基于 gitcmakevcpkg 也可以实现非常灵活的功能,但是 vcpkg 也有一些缺陷。

首先是和 bazel 类似的,很难对依赖包做一些定制。导入一个包的时候,要么不要,要么全要。比如使用 libwebsockets 的时候,本来是可以选择使用openssl,mbedtls或者其他的库作为SSL库的,或者说有些功能不需要可以连依赖库带功能都不开。但是使用 vcpkg 安装 libwebsockets 的话,就没得选了。不过现在好像设计了一个有点类似 build2 的方案(Selecting library featuresvcpkg_check_features),一定层度上解决了这个问题。

第二个问题就是 vcpkg 官方支持的编译环境比较新。Windows下要求 VS 2015 Update 3以上,Linux 下要求 GCC 6以上, macOS也要求 Homebrew 且安装gcc 6以上。其他的环境有些也能支持但是是不受官方支持的。

相对来说Conan的环境支持就比较好: https://docs.conan.io/en/latest/reference/config_files/settings.yml.html#settings-yml 。但是每个包的版本和对应工具链的兼容性得自己管理,还是十分不便。

第三个问题比较难解决。大多数 vcpkg 里的包都是配置了从github下载的,也有些只从一些其他的URL下载。虽然说可以配置github的地址,但是 vcpkg 的从github下载包版本的代码里写的是使用的github的开放平台接口。新版本好像是加了个 https://github.com/microsoft/vcpkg/blob/master/docs/users/registries.md 可以解决这个问题,不过操作方式还比较麻烦,相当于对依赖的包要自己重写 ports 了,对使用者的要求还是有点高。

还有一些周边的问题,有一个也是这几天搞 opentelemetry-cpp 的时候发现的。截至我写这篇文章的时候,vcpkg 的最后的Release版本是 2021.05.12 ,里面的protobuf版本是 3.15.8 。前几天MSVC更新了 1929版本(VS 16.10) ,然后这个版本的 protobuf 刚好不支持,这就很尴尬了。

所以综合来说,大部分情况下 vcpkg 还是挺香的。但是某些场景,比如自定义内部源、组件版本好控制和低版本编译器支持它也不是很香。

我原先项目管理使用的也是 cmake ,所以现在也是使用的 cmakegit。另外学了一手 opentelemetry-cpp 的CI检测,要保证发布版本在各种环境下都能正常构建使用。

这套工具主要的功能之一是实现原先 atframe_utils 里的一些对编译器功能的检测,比如是否开启了异常,是否支持RTTI,是否支持C++20 Coroutine等等。 我们项目都是开了比较严格的编译告警选项的(GCC和Clang下 -Wall -Wextra -Werror, MSVC下 /W4 /WX),所以要提供工具让某些功能使用这些选项。 另外还要提供工具让子模块继承部分父级项目的选项,比如如果外层使用 clang+libc++,那么依赖库和子仓库也要用 clang+libc++

我个人觉得 vcpkg 的发展前景还比较好,很多问题慢慢地都能够妥善解决,所以对于比较新的编译器环境和首支持的平台还是更推崇直接用 vcpkg。在 cmake-toolset 里我也添加了对 vcpkg 的适配支持。可以直接导入 vcpkg 的toolchain文件使用,大多数导入的依赖库都支持直接从 vcpkg 中查找 。

另外就是在不使用 vcpkg 或者 vcpkg 内未安装某个依赖的时候,我会走自己内部的统一编译安装流程,并且预留了可以由上层应用来控制下载的源和版本号,甚至是一些编译参数。这样在上层需要定制化的时候就比较容易了。

稍微列举一下整理迁移过程中的新问题吧:

NOCONFIG

有些环境会生成 NOCONFIG 的cmake config模块。然后如果父级项目指定了 CMAKE_BUILD_TYPE 的话会找不到匹配的链接目标。所以我写了工具自动导出某些特定 CMAKE_BUILD_TYPE fallback到未指定的配置。 这样可以适配一些依赖包的查找过程。

交叉编译的二进制

交叉编译的时候,有时候需要编译出host版本的二进制使用。比如我们如果使用了protobuf,那么链接库是要使用目标平台的库的,但是如果要使用 protoc 生成代码,就需要用host平台的版本了。 所以针对这类库,目前的做法是走了特殊的编译流程,同时编译出两个平台的可执行程序。

然后我先尝试的做法是目标平台不编译二进制,仅编译库。host平台仅编译可执行程序。但是发现这会导致 find_package() 查找cmake config模块时缺失部分目标。所以最后我采取了目标平台全编译,然后编完host平台的可执行程序以后,patch掉可执行程序的 IMPORTED_LOCATION 的方法。

另外就是对于 iOS ,tvOS 和 watchOS需要给可执行程序设置BUNDLE,我这里仅仅是编译时工具链,并不需要安转运行。所以我就直接把 CMAKE_MACOSX_BUNDLE 设置 OFF 了。

CMake的REGISTRY机制

CMake内置了一个包仓库机制,有些依赖包会在安装的时候注册进去。这会影响到 find_package() 的结果,所以我给继承的变量增加了 CMAKE_EXPORT_PACKAGE_REGISTRY , CMAKE_EXPORT_NO_PACKAGE_REGISTRY, CMAKE_FIND_USE_PACKAGE_REGISTRY , CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY , CMAKE_FIND_USE_PACKAGE_REGISTRY, CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY 。并且继承了 CMP0090 这个policy。

  • Windows下这个REGISTRY的数据会写在注册表 HKEY_CURRENT_USER\Software\Kitware\CMake\Packages\<PackageName>HKEY_LOCAL_MACHINE\Software\Kitware\CMake\Packages\<PackageName> 里。
  • Unix类环境这个REGISTRY的数据会写在 ~/.cmake/packages/<PackageName> 里。

如果 find_package() 找打了非预期的奇怪的路径,可以去这里面找找删掉就行了。

Windows 长路径问题(260路径长度限制)

还有个问题是使用 cmake-toolset 的时候,默认是的依赖编译目录是 BUILD目录/_deps/平台及工具链名/包名 。容易名字很长,Windows下就碰到了碰到了编译时路径过长的问题。

解决方法之一是直接改注册表, 可以用 powershell 脚本 New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force 或者注册表文件

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem]
"LongPathsEnabled"=dword:00000001

但是似乎并不是都有的工具对这个都有良好支持,并且这需要管理员权限。所以最终我再Windwows下构建的时候会把依赖编译目录改成 用户目录/cmake-toolset-<hash> 中,用来减少一定的长度。

Windows SDK版本

新版本的MSVC支持C11了,但是需要使用新版本的Windows SDK,这可以通过 CMAKE_SYSTEM_VERSION 来指定。特别是某些C的依赖包,使用了C11支持以后不需要再做额外的patch就能编译了(比如lua,libwebsockets等)。 具体查找并使用最新版本Windows SDK的做法可以参考CI脚本 https://github.com/atframework/cmake-toolset/blob/main/ci/do_ci.ps1 。

CI的内存不足

因为使用的github的免费Action,有些依赖包开多进程编译还是会OOM。然后cmake对docker环境检测CPU数量和控制并发数也不准确,所以我加入了低内存模式。人工降低并发度来让github action不OOM。

最后

我的大部分工具已经迁移到新的 cmake-toolset 了,后续的也会慢慢迁移。以后再碰到什么问题再来写吧。

最终重构抽离出来的构建工具集 (cmake-toolset) 位于 https://github.com/atframework/cmake-toolset 。主版本号保证API兼容,二级版本号指示是否有新功能,三级版本号是实现优化和修订。

以下是支持的环境(配置在CI测试中):

  • 默认行为:
    • 启用cmake能够识别的编译器所支持的最新标准(当前最新会开启 C++20C11)
    • 对支持的编译环境启用 C++20 协程支持
    • 对支持的编译环境启用 C++20 Module支持
    • MSVC:
      • 默认使用 UTF-8 编码
      • 对高版本编译器设置 __cplusplus == _MSVC_LANG
        • 即设置 /Zc:__cplusplus
        • 即和C++标准保持一致
        • 可通过 -DCOMPILER_OPTION_MSVC_ZC_CPP=OFF 来关闭
      • (非 vcpkg 模式)默认设置 CMAKE_MSVC_RUNTIME_LIBRARYMultiThreaded$<$<CONFIG:Debug>:Debug>$<$<NOT:$<STREQUAL:${VCPKG_CRT_LINKAGE},static>>:DLL> (影响 /MDd, /MD/MTd, /MT)
    • Clang/AppleClang: 尝试优先使用 libc++ 作为STL库。
      • 可通过 -DCOMPILER_OPTION_CLANG_ENABLE_LIBCXX=OFF 来关闭
    • Windows:
      • option(COMPILER_OPTION_WINDOWS_ENABLE_NOMINMAX "Add #define NOMINMAX." ON)
      • option(COMPILER_OPTION_WINDOWS_ENABLE_WIN32_LEAN_AND_MEAN "Add #define WIN32_LEAN_AND_MEAN." OFF)
    • 额外的编译告警
      • 提供 COMPILER_STRICT_CFLAGS 来开启严格的编译警告,并且关闭一些常用的设计模式导致的告警。(-Wall -Werror, /W4 /WX)
      • 提供 COMPILER_STRICT_EXTRA_CFLAGS 来开启更严格的编译警告,并且关闭一些常用的设计模式导致的告警。(-Wextra)
  • 支持平台:
    • Linux
    • Windows
    • MinGW
    • macOS
    • Android
    • iOS
    • iPhone.Simulator
  • 编译器:
    • GCC 4.8-最新(当前GCC 11)
    • Clang 5.0-最新(当前GCC 12)
    • Visual Studio 2017-最新(当前Visual Studio 2019, 16.10, MSVC 1929)
  • 特殊的包:
    • SSL替代: openssl,libressl,mbedtls(boringssl依赖golang,会在未来添加)

      未来还会添加: libsodium

欢迎有兴趣的小伙伴互相交流。