.NET 再次裁剪发布报 Pipelines 未找到:检查旧 deps.json
SDK 10.0.402 的 Linux ARM64 裁剪自包含发布,在增加 System.Text.Json 调用后可能复用旧 deps.json。核对 DLL 与清单,用新的 artifacts 和发布目录重建,并比较复用输出时的清单生成日志。
裁剪发布成功,运行时却报找不到 System.IO.Pipelines, Version=10.0.0.0;发布目录里明明存在对应 DLL。这时除了查文件,还要比较发布用的 .deps.json 是否仍来自上一次裁剪结果。
一个具体组合是 Linux ARM64、.NET SDK 10.0.402、运行时 10.0.13,应用以源生成方式使用 System.Text.Json,并启用自包含、PublishTrimmed=true 和 TrimMode=full。第一次仅反序列化可运行;增加序列化调用后,复用原 obj、bin 和发布目录再发布,才出现加载失败。它不依赖第三方序列化库,也不能扩大成所有 System.IO.Pipelines 缺失问题。
对照 DLL 与两份依赖清单
先保存失败产物和原始发布参数,再比较构建目录、obj 中间目录、发布目录里的依赖清单。符合这条表现时,构建用的 .deps.json 已包含 System.IO.Pipelines,裁剪发布使用的中间清单和最终副本却没有,前后发布清单甚至完全相同。
DLL 存在不代表运行时依赖清单允许按预期解析它;开发机直接运行成功,也不能证明自包含发布包完整。反过来,如果真实发布目录根本没有 DLL,应先查裁剪保留规则和文件复制,不能仅凭同名异常套用清单陈旧的判断。
10.0.402 的正式 GeneratePublishDependencyFile 目标采用增量 Inputs/Outputs,并用一组属性构造输入缓存;生成清单时又接收运行时程序集集合。这为检查“可达 API 改变后,依赖清单目标是否仍被跳过”提供了具体位置,但完整根因仍须由失败工程的构建日志确认,不能把静态目标片段直接当作所有工程的结论。
用新的一组中间目录和发布目录重新生成
保留相同 SDK、RID、配置及裁剪属性,先让这一应用在本任务独占的新 artifacts 与发布目录中完整发布。下面演示对应 Linux ARM64 组合:
dotnet publish Probe.csproj -c Release -r linux-arm64 --self-contained true --artifacts-path ./artifacts-fresh -o ./publish-fresh
Probe.csproj、两个目录都是示例;执行前确认目录归属且没有旧产物。原来通过命令行传入的裁剪、源生成、包源及其他属性仍须保留。这会执行还原、构建和裁剪,使用项目原有的依赖与网络策略,不是只读检查。
只换 -o 不一定隔离旧 obj 中的清单,因而要同时隔离中间产物。也不要一边使用新的 artifacts 根,一边继续拷贝旧发布目录的 DLL 或 deps 文件;那会重新混合不同构建的文件。
如果新目录发布正常、复用原输出再次发布仍失败,应把两份日志中依赖清单生成目标的输入、跳过理由和最终程序集集合保存下来。只对当前应用采取独立产物目录的临时策略,不需要清空全局 NuGet 缓存或修改安装目录中的 SDK targets。
验证新增调用确实能在发布包中执行
在原有隔离运行环境直接启动新发布产物,覆盖新增的序列化调用,并检查返回内容;确认 .deps.json、程序集和运行配置来自同一次发布。随后再修改一次会影响实际 API 使用的源码,重复复用输出的发布对照,观察清单是否随需要变化。
不要手写一条 System.IO.Pipelines 清单记录来掩盖生成问题,也不要为了消除异常直接放开全部反射或关闭所有裁剪。那些修改会改变应用体积、序列化或部署契约,应另有依据。后续采用正式 SDK 更新时,仍需用独立输出、复用输出两轮组合验收,不能只看发布退出码。


