当前位置: 首页 > news >正文

【译】 再次革新 .NET 的构建和发布方式(三)

点击上方“DotNet NB”关注公众号

回复“1”获取开发者路线图

学习分享丨作者 / 郑 子 铭

这是DotNet NB 公众号的第249篇原创文章

原文 | Matt Mitchell

翻译 | 郑子铭

落实愿景——交付统一构建版本

统一构建项目大致可以分为 4 个阶段:

  • 初步构思和设计(.NET 7) ——统一构建项目的初步设计工作始于 2022 年初,当时正值 .NET 7 开发阶段,历时约 4 个月完成。该项目于 2022 年晚些时候获得全面批准启动,目标是在 .NET 9 RTM 发布前完成。其中设置了一些关键的启动/停止节点,在这些节点上,即使放弃项目,我们仍然可以在基础设施方面取得净收益。

  • 基础性工作(.NET 8) ——在 .NET 8 期间,统一构建项目专注于基础性工作,旨在提升源代码构建基础架构的可持续性,并构建支持完整构建所需的各项功能。这些投入旨在为 .NET 整体带来积极影响,即便在概念验证阶段发现了一些重大未知问题,导致我们不得不改变方向。

  • 垂直构建/代码流探索(早期 .NET 9) ——在基础工作完成后,我们开始为三大主流操作系统系列(Mac、Windows 和 Linux)分别实现垂直构建。我们的目标是尽可能多地识别产品化阶段需要解决的问题。我们尤其关注之前未知的任何产品构建连接点。与此同时,我们对代码流和代码管理的各种方案进行了更深入的研究,最终验证并确定了以下实现方案。

  • 产品化(.NET 9 后期 - .NET 10) ——在经历了春夏两季的延期后,.NET 9 的最终实现工作在 .NET 9 末期才正式启动。由于延期,发布日期被推迟到了 .NET 10。但这最终却因祸得福。这为我们争取到了大约 6 个月的额外开发时间,并使我们能够在 .NET 10 预览版/候选版周期(预览版 4)的中期开始使用统一构建产品流程。.NET 预览版 4 发布了新的构建流程,但仍然沿用旧的代码流程。预览版 5 加入了新的代码流程,此后我们再也没有回头。在接下来的几个月里,我们进一步优化了开发人员的工作流程,并为构建和代码流程的开发预留了更多的时间。

经过近 4 年的梦想和努力,Unified Build 终于随 .NET 10 RTM 一起发布了!

让我们来看看这个项目的关键组成部分。

VMR——虚拟单体存储库

dotnet /dotnet VMR,即“虚拟单体仓库”,是统一构建项目的基石。它是所有 .NET 组件(包括我们的 Linux 发行版合作伙伴)构建的源代码布局。它扮演着协调者的角色。从功能上看,它与 .NET 8.0 之前的源代码布局并没有太大区别。只是将之前的源代码布局正式化为了一个 Git 仓库(而不是源代码压缩包)。这一点至关重要,因为它允许开发人员既可以在各自的组件仓库中工作(在这些仓库中,开发工作流程可能非常精细),也可以在需要进行跨领域更改时在 VMR 中工作。.NET 获得了分布式仓库的大部分优势,同时避免了代码一致性问题。

垂直建造

垂直构建是 .NET 向一系列垂直组件(vertical build)构建资源的转型。垂直组件是指在单台机器上运行的单个构建命令,用于构建 .NET 产品的一部分,而无需其他垂直组件的输入。通常,我们会根据要构建的运行时环境来划分垂直组件。例如,Windows x64、MonoAOT、Linux arm64 和 PGO Profile Windows x86。总共有 35 到 40 个不同的垂直组件。我们将这些垂直组件分为“短栈”和“长栈”。短栈仅构建运行时环境,而长栈则会构建整个 SDK。

最初的设想是,如果我们把所有并行垂直领域的输出整合起来,就能拥有 .NET 所需的一切。这样的架构效率极高,而且对上游合作伙伴也很友好。然而,多年来,.NET 产品的设计中已经包含了一些必要的整合。例如,如果没有对跨多个操作系统构建的众多软件包的访问权限,就无法构建 .NET 工作负载包。为了解决这个问题,我们最终增加了两个构建步骤。好消息是,这些额外的步骤只涉及部分垂直领域以及这些领域内的部分组件。虽然并不完美,但尚可接受。

代码流

统一构建项目最有趣的方面或许在于代码流的管理方式。.NET 在这方面对标准开发模式进行了一些颠覆。正如前文所述,将产品维护为相互依赖的组件图,同时将代码流扁平化到一个共享的、连贯的布局中,需要实现“双向”代码流。变更需要从组件流入共享布局,而共享布局中的变更也需要能够回流到组件仓库。从概念上讲,代码流算法并不比在给定项目的单个 Git 仓库中建模的任何算法更复杂。关键在于使用没有相关 Git 历史记录的仓库来实现这一点。

注:该算法的具体细节将在后续文章中由另一位团队成员详细介绍。届时我会更新本文并添加链接。

现在,我们先来看一些基本知识:

VMR 和组件仓库都会跟踪来自合作伙伴的最新代码流。eng/Version.Details.xml虽然可以想象将其保存在其他地方,但此信息会与标准依赖项信息一起跟踪。

  • dotnet/runtime 知道上次“回流”的 VMR SHA ,即从 VMR 到 dotnet/runtime 的流。

  • VMR 知道最后一个“正向流”的 dotnet/runtime SHA ,即从 dotnet/runtime 到 VMR 的流。

其目的是确定“上次流”与当前流之间的差异。例如,在一个非常简单的例子中,当向 dotnet/runtime 提交新代码,而src/dotnet/runtimeVMR 中没有任何更改时,依赖关系流系统将执行以下步骤:

  1. 确定两个点 A 和 B,用于计算差异。在本例中,点 A 是 dotnet/runtime 最后一次提交到 VMR(或当前处于 PR 中)的版本。点 B 是 dotnet/runtime 的最新提交。

  2. 构建一个补丁文件,将 src/runtime 文件重新映射到 VMR 的目录结构。

  3. 请提交包含差异的 PR。请参阅正向流程示例和反向流程示例。

.NET 8 和 .NET 9 使用仅支持单向代码流的虚拟主版本库 (VMR)。在另一端无需进行任何更改的情况下,这些情况简单且稳定。但当开发人员开始在两端都进行更改,并且依赖关系流随着时间推移而发生变化时,情况就会变得复杂起来。

  • 计算差异点变得更有趣,需要知道“最后一次流动”的方向。

  • 合并冲突时有发生,需要以开发人员能够理解的方式进行处理。

  • 代码流的源端和目标端的变化可能会造成严重后果,因此需要强大的错误处理和恢复机制。

代码流程部分就先讲到这里。敬请期待更多内容。

场景测试验证

统一构建的最后一个主要支柱是额外的场景测试。需要明确的是,.NET 并不缺乏测试。如果可行,.NET 运行时可以在每个 PR 上花费数月的机器时间来验证其数百万个测试用例。我们的审批、构建、验证和签核流程确保了高质量的交付组件。然而,当直接在 VMR 中进行更改时,扁平化的流程会在更改和针对每个 VMR 组件进行深入验证之间引入新的延迟。虽然我们无法在 PR 和 CI 上运行所有测试,但我们确实意识到,更好的自动化场景测试可以在防止回归方面发挥重要作用。我们的目标是添加涵盖广泛产品功能的测试,这些功能与构建系统或代码库基础架构没有直接关联。相反,这些测试针对最终构建的产品执行。如果场景测试通过,则可以很好地判断产品功能已达到良好水平,贡献者也不会受到阻碍。

结果

那么,.NET 经过近四年的梦想、筹划和辛勤努力,最终获得了什么?在一个项目上投入如此多的精力,结果是否物有所值?事实证明,我们收获颇丰。

让我们先从最显而易见的结果开始,然后再深入了解一下背后的原因。

灵活性、可预测性和速度

迄今为止,我们看到的投资回报最大的莫过于灵活性。分布式产品构建速度缓慢,构建一致的版本也同样耗时。提交新的修复或内容需要协调,以避免“重置构建”,因为在分布式开源生态系统中,最终交付的内容和构建方式是紧密相连的。提交新的修复可能意味着你没有准备好进行验证。扁平化流程消除了这种一致性问题,将“做什么”和“怎么做”分离。这在推进RTM版本或服务版本发布的过程中至关重要。这意味着我们可以在发布周期的后期进行修复,从而将更多精力放在这些修复是否符合服务标准上,而不是能否实际构建和交付变更。这种灵活性对客户来说非常有利。

这种灵活性部分源于构建速度的提升。这听起来可能非常慢(.NET 是一个庞大而复杂的产品),但 .NET 的目标是在 4 小时内生成未签名版本,在 7 小时内生成签名版本。这比 .NET 8.0 和 .NET 9.0 中显著延长了构建时间。即使一切顺利,8.0 或 9.0 的构建也很容易耗时 24 小时。7 小时内完成签名版本意味着每天需要验证大约 3 次新的 .NET 资源。构建时间的显著提升主要来自于简化构建流程,减少不必要的开销。

部分灵活性也源于可预测性。分布式产品构建涉及更多环节,更多的人为干预点,系统和流程出现故障的可能性也更大。这往往会导致结果难以预测。“如果我向 dotnet/runtime 提交一个修复,什么时候才能生成可用的构建版本? ”在分布式系统中,这是一个难以回答的问题。我知道 dotnet/runtime 的构建需要多长时间,但这个更改何时才能通过依赖流向下游显现?届时是否有人可以审核并批准它?下游的 PR/CI 验证状态如何?在我们获得一个完整的构建版本之前,是否会有新的重要更改合并到 dotnet/aspnetcore 中,从而导致验证工作延误?在 .NET 10 中,这个问题要容易得多。更改会流入 VMR(或直接在 VMR 中进行),并会在下一个构建版本中显现。下一个构建版本需要 N 小时。

基础设施的稳健性和完整性

在那些耀眼的指标背后,是多年来对基础设施进行的质量提升,这些提升日复一日地带来显著效益。.NET 8 中对源代码构建基础设施的改进降低了 Linux 发行版源代码构建的运行成本。之前的成本很大一部分来自于变更提交到最终通过依赖关系图到达共享源代码布局并发现其是否会破坏构建之间的延迟。在预览版中期之前,源代码构建 .NET SDK 经常无法“预构建干净”,也无法由发行版合作伙伴发布。.NET 8 的基础设施改进使得在 PR 阶段更容易识别新的预构建输入,从而更容易诊断和解决这些问题,避免它们进入源代码布局。现在,我们 100% 实现了预构建干净。这减轻了源代码构建团队的负担,使他们能够腾出精力在其他领域开展工作。他们增加了构建并行性、更可预测的依赖关系流、更好的日志记录、移除了不必要的复杂性……等等。让产品获得成功的投资。

为了支持在所有平台上对各种类型的归档文件进行签名,我们的签名工具必须进行全面改造。如果没有这项工作,我们就无法发布统一构建版本。但这种扩展支持带来的益处远不止于核心 .NET 产品。许多辅助代码库也因此简化了构建流程,避免了将文件从 Mac/Linux 传输到运行签名工具的 Windows 机器。更低的构建开销,更快更简单的构建过程。

未来方向

那么,统一构建项目下一步该如何发展呢?虽然我们不会像之前那样大力投资 .NET 11,但我们会针对基础设施进行有针对性的改进,以提升开发者的工作流程和用户体验,主要集中在代码流方面。我特别感兴趣的一个领域是 AI 代理,它可以监控代码流,将产品创建过程中涉及的各个系统连接起来,并识别问题。从 PR 到产品发布,变更的交付涉及众多系统和参与方(Azure DevOps、GitHub、代码流服务及其配置、代码镜像、开发者审批、机器分配等等)。如果一切顺利,就万事大吉。但如果出现问题,通常需要人工追踪,才能准确找出出错的环节。这既繁琐又耗时。我们虽然有工具,但主要还是需要将众多环节连接起来。我们可以为此编写一个规则引擎,但我感觉它会很脆弱而且非常复杂。能够以更模糊的方式审视系统的代理,才是这类任务的理想选择。更少的辛劳,更好的 .NET。

最后,在 .NET 11 之后,或许还会有另一波消除连接点的浪潮。这样做的好处显而易见:更简单、更快速,也更便于贡献者使用。我们现在已经确切地知道,如果消除剩余的连接点,构建速度会有多快(不到 4 小时)。

结论

如果你读到这里,非常感谢!很高兴能深入探讨 .NET 的构建和发布方式。你已经了解到,分布式依赖流产品构建模型并非总是能够可靠且可预测地交付软件。这类系统往往具有较高的复杂性和开销,从而增加开发时间。你也了解了 .NET 统一构建项目起源于 .NET Linux 发行版源代码构建,以及将这些概念应用于 .NET 时遇到的困难。最后,你还了解了 .NET 如何应用这些概念,以及我们在日常工作中看到的显著改进。

详细介绍扁平化代码流程算法的博文很快就会发布。敬请期待!

链接

  • 统一构建设计文档

  • 产品构建的滚动 CI/PR 构建

原文链接

Reinventing how .NET Builds and Ships (Again)

推荐阅读:

【译】 再次革新 .NET 的构建和发布方式(二)

【译】 再次革新 .NET 的构建和发布方式(一)

【译】 数据摄取构建模块简介(预览版)(二)

【译】 数据摄取构建模块简介(预览版)(一)

【译】 如何使用 .NET MAUI 构建 iOS 小部件

【译】 Microsoft.Testing.Platform 现已在 Azure DevOps 中得到全面支持

点击下方卡片关注DotNet NB

一起交流学习

▲点击上方卡片关注DotNet NB,一起交流学习

请在公众号后台

回复【路线图】获取.NET 2024开发者路线

回复【原创内容】获取公众号原创内容

回复【峰会视频】获取.NET Conf大会视频

回复【个人简介】获取作者个人简介

回复【年终总结】获取作者年终回顾

回复加群加入DotNet NB 交流学习群

长按识别下方二维码,或点击阅读原文。和我一起,交流学习,分享心得。
http://www.cnnetsun.cn/news/1646472.html

相关文章:

  • 3种核心映射技术深度解析:DS4Windows实现PS手柄在Windows平台的完整兼容方案
  • 从删库到跑路?不,从卸载MariaDB开始:Linux离线部署MySQL 5.7完整避坑指南
  • 用PLECS和C代码手把手教你实现数字滤波(附完整工程文件)
  • 【Linux 物联网网关主控系统-Linux主控部分(一)】
  • 救命!2026爆款PPT一键制作工具实测,新手也能5分钟出片,告别熬夜手搓无标题
  • 快速验证电路构想:用快马平台一键生成mos管交互仿真原型
  • 3种让Windows用户告别模拟器的APK直装方案:开源工具APK-Installer全解析
  • OpenCore Configurator:告别复杂配置,三步搞定黑苹果引导设置
  • 2025届最火的六大AI学术网站实测分析
  • 2026最权威的AI辅助写作平台实际效果
  • Ostrakon-VL-8B真实案例:某连锁咖啡店通过AI巡检降低人工巡店成本65%
  • VMware vSphere Replication 9.0实战:跨存储灾备从规划到落地
  • 实战应用:基于快马平台开发企业内网服务可用性监控系统
  • SEO_详解SEO工作原理及其对业务增长的重要性
  • 百川2-13B量化模型知识蒸馏:为OpenClaw定制轻量技能模型
  • 【功率预测+储能】储能都配上了,为什么很多场站还是没把收益拉起来?
  • AG32芯片烧录神器:深度体验AGM DAP-LINK下载器的三大核心功能
  • UniversalTime:嵌入式系统毫秒级确定性时间库
  • Flutter环境配置全攻略:flutter doctor命令详解与常见问题解决
  • Python课程大作业避坑指南:以‘世界杯连连看’为例,聊聊初学者常犯的5个错误
  • SOLIDWORKS Simulation 带孔矩形板拓扑优化实战:从建模到轻量化设计
  • (实战经验)LoRA微调ChatGLM-6B避坑指南:FP16梯度问题与tokenizer修复
  • 3分钟掌握前端Word文档生成:用纯JavaScript创建专业DOCX文件
  • DOM 节点
  • 3步拯救无法扫描的二维码:零基础也能学会的修复技巧
  • 深入理解Qt字节序转换:从qFromBigEndian源码看跨平台数据处理的底层实现
  • OpenClaw技能开发:为SecGPT-14B编写自定义漏洞检测模块
  • 从VPULSE到VPWL_ENH:手把手教你用Pspice搭建和仿真5种常见信号源电路
  • 别再乱插了!工程师必懂的接插件选型避坑指南(从BTB到FPC,附选型清单)
  • 自动化工具效能倍增:KeymouseGo从重复操作解放到流程智能化指南