Foundry 修复解析:通过 Source Remapping 导入合约的动态测试链接与增量重建问题 Foundry 修复解析通过 Source Remapping 导入合约的动态测试链接与增量重建问题【免费下载链接】foundryFoundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.项目地址: https://gitcode.com/GitHub_Trending/fo/foundry本篇文章聚焦 Foundry 一条针对forge test的补丁修复通过 source remappings 导入的合约包括被继承的测试合约在动态测试链接dynamic test linking及后续增量重建中的缺陷以及升级后必须执行的一次性forge test --force重建操作。读完本文你将理解动态测试链接的底层机制、它与 remapping 交互时的边界条件以及如何在升级后正确清理旧缓存、避免误判增量编译。修复概述一条补丁解决两类症状该 changelog 条目.changelog/fix-remapped-test-linking.md声明了以下修复范围forge与foundry-common两个 crate 各发布一个 patch 版本修复动态测试链接dynamic test linking当测试合约所引用的目标合约是通过 source remapping 导入时链接结果不再出错修复后续增量重建incremental rebuilds在第一次构建之后后续增量编译不再产生错误或遗漏覆盖“被继承的测试合约”inherited test contracts即测试基类同样通过 remapping 导入的场景升级指引受影响的版本会在out/目录中缓存错误的工件因此升级后需对每个项目执行一次forge test --force强制全量重建。要理解这条修复必须先理解动态测试链接本身。背景什么是动态测试链接配置开关与默认值dynamic_test_linking是 Foundry 编译配置中的一项布尔开关定义于 crates/config/src/lib.rs默认值为true见同文件第 2927 行的默认实现。它也可以通过 CLI 的--no-dynamic-test-linking显式关闭该选项定义于 crates/cli/src/opts/build/core.rs会在生成配置时把dynamic_test_linking写为false。# foundry.toml [dynamic_test_linking] # 实际为顶层布尔项示例写法如下正确的配置写法是顶层布尔项dynamic_test_linking true机制把“字节码依赖”改写为 cheatcode 调用动态测试链接的核心思想是测试代码中凡是依赖目标合约字节码的地方new Contract、type(Contract).creationCode都在编译前被预处理改写为对 VM cheatcodedeployCode/getCode的调用从而让测试在执行期动态部署/获取目标合约字节码而不是在编译期静态内联。这条预处理管线在 crates/common/src/compile.rs 中接线当dynamic_test_linking开启时ProjectCompiler::compile会给编译器挂载DynamicTestLinkingPreprocessor预处理插件随后才执行compiler.compile()。同样使用这一开关的命令还包括forge buildcrates/forge/src/cmd/build.rs、forge testcrates/forge/src/cmd/test/mod.rs、forge coveragecrates/forge/src/cmd/coverage.rs、forge selectorscrates/forge/src/cmd/selectors.rs以及 mutation 测试crates/forge/src/mutation/orchestrator.rs。改写过程实现在 crates/common/src/preprocessor/deps.rsPreprocessorDependencies::new通过 Solar 编译器对测试/脚本合约做语义分析收集“字节码依赖”BytecodeDependency其类型包括CreationCodetype(X).creationCode和Newnew X(...)含构造参数、value、salt、try/catch场景remove_bytecode_dependencies把这些依赖逐一替换为对VmContractHelper接口中deployCode(...)/getCode(...)的调用对带构造参数的目标合约还会生成DeployHelper{id}.sol辅助合约与encodeArgs辅助函数并在源文件末尾追加import {DeployHelper..., encodeArgs...} from foundry-pp/DeployHelper{id}.sol。这就是“动态”二字的由来测试运行时才决定部署哪个字节码工件运行时工件查找依赖“运行中测试的上下文”。缺陷所在remapping 场景下的链接与增量重建问题的两个层面从源码注释可以还原问题的成因。在 crates/common/src/preprocessor/deps.rs 的can_rewrite函数中有一段关键判断// Runtime artifact lookup uses the running tests context, which can differ from the source // containing an inherited helper. Any remapping matching the generated path is therefore // unsafe unless every possible runtime context is known. if remappings.iter().any(|remapping| remapping_matches_path(remapping, generated_path)) { return false; }结合remapping_applies与remapping_matches_path同文件第 342-359 行的实现可以归纳出两个层面的缺陷动态链接指向错误工件当测试通过 remapping例如test/test/、src/src/导入目标合约时预处理生成的辅助导入路径foundry-pp/DeployHelper{id}.sol与目标路径都可能被 remapping 重定向。由于运行时工件查找使用的是“正在运行的测试的上下文”而继承的辅助代码来自另一个源文件两者的 remapping 解析上下文不一致导致deployCode拿到错误的工件名测试部署到错误的字节码。增量重建不触发被 remapping 导入的文件在首次编译后其“源单元身份”与缓存记录不一致源码中通过strip_prefix(root_dir)归一化路径再与source_units比较。当这些文件发生变化时Foundry 的增量缓存误判为“未变化”从而跳过重新编译导致forge test后续运行时继续使用陈旧的工件——这正是 changelog 中“subsequent incremental rebuilds”所描述的症状。继承的测试合约为何是重点PreprocessorDependencies::new中对“候选合约”的判定第 53-68 行仅保留位于测试/脚本路径集合中的源文件而“被继承的测试合约”指测试基类通过 remapping 导入的场景。此时依赖收集阶段is_path_in_dir需要同时兼容相对路径、绝对路径与符号链接路径第 332-339 行而 remapped 导入常常解析为绝对或符号链接路径与编译器输入用的相对路径不一致预处理阶段被继承测试文件同样被当作“mock 或可重写目标”处理若其自身还依赖源目录下的目标合约改写生成的辅助文件与can_rewrite的判定共同决定了它能否被安全重写修复前这类继承关系中的任意一环出错都会让整个测试基类的动态链接失效且失败会在缓存建立后“潜伏”直到增量场景才暴露。修复策略与验证方式修复后的正确行为修复后can_rewrite的 remapping 检查作为安全兜底只要生成的辅助路径或依赖路径命中任何 remapping且无法证明所有运行时上下文一致就放弃改写、保留原生字节码依赖keep_native分支见 deps.rs 第 114-144 行从而避免生成错误的辅助代码。换言之修复不是简单“允许 remapping 场景”而是让每个 remapping 场景都能被正确分类到“可安全改写”或“保留原生”两条路径之一杜绝了此前“看似改写了、实则链接错误”的中间态。升级操作一次forge test --forcechangelog 明确给出升级步骤After upgrading, runforge test --forceonce in each project with artifacts cached by an affected version.原因在于受影响版本已经向out/或自定义cache_path写入了基于错误上下文生成的工件与缓存条目。普通增量编译会命中这些损坏缓存继续产出错误结果。--force强制清空并全量重建对应配置项force见 crates/config/src/lib.rs让所有工件在修复后的预处理逻辑下重新生成。此后增量编译即可正常工作。对使用脚本的项目crates/forge/tests/cli/script.rs 中的测试注释提示dynamic_test_linking配置项目前尚未被forge script完全采纳升级排查时如需最小化变量可在 script 流程中确认该开关的实际生效范围。配置与命令速查配置项 / 命令位置说明dynamic_test_linking truecrates/config/src/lib.rs默认true开启测试/脚本的动态字节码链接预处理--no-dynamic-test-linkingcrates/cli/src/opts/build/core.rs覆盖配置显式关闭动态测试链接forge test --force升级后一次性执行清空受影响版本的损坏工件缓存并全量重建force truecrates/config/src/lib.rs每次运行前强制清理并重建所有工件总结本条补丁修复的是动态测试链接在与 source remapping 交互时的两个连环缺陷运行时工件查找上下文不一致导致的链接错误以及缓存误判导致的增量重建失效。修复的核心手段是在 crates/common/src/preprocessor/deps.rs 中强化can_rewrite的 remapping 安全判定将“不安全即保留原生依赖”的兜底逻辑落实到位。对于升级用户最关键的实践动作是在每个缓存过受影响版本工件的项目中执行一次forge test --force确保损坏缓存被一次性清空后续增量构建恢复正确。【免费下载链接】foundryFoundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.项目地址: https://gitcode.com/GitHub_Trending/fo/foundry创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考