掌握 Forc 依赖管理:从 Forc.toml 到 git、IPFS、path 与 registry 四种来源的完整实战
【免费下载链接】sway🌴 Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway
本篇技术指南以 Sway 编译器仓库(sway)中 Forc 的依赖管理文档为核心,系统讲解 Forc 的依赖管理系统:它如何通过git、ipfs、path以及社区registry(forc.pub)四种来源拉取和共享 Forc 库,以及如何使用forc add、forc remove、forc update三个命令完成依赖的增删与更新。读完本文,你将掌握在 Sway 智能合约项目中声明、解析与更新依赖的完整流程,并理解其底层清单解析与锁文件机制。
一、依赖管理概览:四种来源与两种声明方式
Forc 内置了一套与 Cargo 类似的依赖管理系统,可以从四种来源拉取包:
| 来源 | 说明 | 适用场景 |
|---|---|---|
git | 从 Git 仓库拉取,可指定branch/tag/rev | 共享开发中的库、跟随分支迭代 |
ipfs | 从 IPFS 网络按 CID 拉取 | 内容寻址、不可变分发 |
path | 引用本地文件系统中的包目录 | 本地开发、多包同仓协作 |
registry | 从社区注册中心(如 forc.pub)按版本拉取 | 发布与消费正式版本的库 |
依赖的声明有两种途径:命令式(forc add)和手写清单(直接编辑Forc.toml)。两者的最终产物是相同的——都在Forc.toml的[dependencies]或[contract-dependencies]表中写入依赖条目,随后由forc build自动解析并获取。
二、使用forc add添加依赖
forc add支持多种来源和可选标志,其通用语法为:
forc add <dep> [--path <PATH>] [--git <URL> --tag <TAG>] [--ipfs <CID>] [--contract-dep]其中<dep>采用name[@version]的 DEP_SPEC 格式(见下文源码解析)。以下是原文档给出的各来源示例。
从 Git 分支添加:
forc add custom_lib --git https://github.com/FuelLabs/custom_lib --branch master从本地路径添加:
forc add custom_lib --path ../custom_lib从 IPFS 添加:
forc add custom_lib --ipfs QmYwAPJzv5CZsnA...从注册中心(forc.pub)添加:
forc add custom_lib@0.0.1作为契约依赖添加:
forc add my_contract --git https://github.com/example/contract --contract-dep可选标志
| 标志 | 作用 |
|---|---|
--salt <HEX> | 为契约依赖指定自定义 salt(十六进制,需以0x开头) |
--package <NAME> | 在 workspace 中定位到指定的成员包 |
--manifest-path <PATH> | 指定要修改的Forc.toml清单文件路径 |
--dry-run | 只打印将要发生的变更,不真正写入文件 |
--offline | 离线模式,禁止 Forc 联网获取依赖 |
--ipfs-node <NODE> | 指定用于拉取 IPFS 来源的节点,可选FUEL、PUBLIC、LOCAL或自定义网关 URL |
forc add还支持一次添加多个依赖,并且命令行示例还提供了forc add <DEP>[@<VERSION>] --contract-dep与forc add <DEP>[@<VERSION>] --dry-run两种组合用法。
注意事项
原文档明确提醒了两个尚未支持的用法:
⚠️ 使用registry来源的项目目前不支持离线模式;且不支持通配符声明(如
custom_lib = *)获取该包的最新版本,也不支持 caret 声明(如custom_lib = ^0.1)获取 SemVer 兼容的最新可选版本。
依赖添加成功后,运行forc build会自动获取并解析这些依赖。
三、手动编辑Forc.toml声明依赖
如果不想使用命令,也可以直接编辑清单文件。若Forc.toml中尚不存在[dependencies]或[contract-dependencies]表,需要手动添加,然后在表内列出包名与来源。
本地路径依赖:
[dependencies] custom_lib = { path = "../custom_lib" }IPFS 来源依赖:
[dependencies] custom_lib = { ipfs = "QmYwAPJzv5CZsnA..." }注册中心(forc.pub)依赖:
[dependencies] custom_lib = "0.0.1"依赖条目的两种 TOML 形态
从源码结构看,forc-pkg的清单解析将依赖定义为Simple(String)与Detailed(DependencyDetails)两种形态(见 forc-pkg/src/manifest/mod.rs):
- 简写形态:
custom_lib = "0.0.1",仅指定版本,等价于custom_lib = { version = "0.0.1" }; - 详细形态:以 TOML 内联表给出完整细节,支持的字段包括
version、path、git、branch、tag、rev、ipfs(写入清单时对应字段名为cid)、namespace、package。
仓库中一个真实的可参考示例是 examples/counter/Forc.toml,它通过 path 来源引用仓库内的标准库:
[project] authors = ["Fuel Labs <contact@fuel.sh>"] entry = "main.sw" license = "Apache-2.0" name = "counter" [dependencies] std = { path = "../../sway-lib-std" }此外,forc add的源码测试还展示了一个重要的便捷行为(见 forc-pkg/src/manifest/dep_modifier.rs):当你在一个 workspace 内、且没有显式指定来源时,forc add会把同 workspace 的兄弟成员自动解析为path依赖(测试中断言路径被解析为"../pkg1")。也就是说,forc add pkg1 --package pkg2在 workspace 中会自动写成pkg1 = { path = "../pkg1" }。
四、源码视角:forc add的底层工作流
理解命令背后的实现,有助于排查依赖问题。forc add的入口定义在 forc/src/cli/commands/add.rs,其核心执行逻辑委托给forc-pkg的dep_modifier::modify_dependencies(见 forc-pkg/src/manifest/dep_modifier.rs)。整体流程为:
- 定位清单:优先使用
--manifest-path指定的文件,否则从当前工作目录向上查找Forc.toml; - 解析目标包:通过
resolve_package_path判断清单是单包还是 workspace。若是 workspace 且未传--package,会直接报错并列出可用成员,提示“请使用--package指定要修改的包”; - 解析依赖规格:
DepSpec::from_str将name[@version]拆分为包名与版本要求,并对版本要求做semver::VersionReq合法性校验(非法版本会报 “invalid version requirement”); - 确定依赖数据:
resolve_dependency根据--git/--path/--ipfs及 DEP_SPEC 中的版本组装DependencyDetails——有版本号时走Dependency::Simple,有显式来源时走Dependency::Detailed,两者都没有则回退到 workspace 兄弟成员的 path 推断;连兄弟成员都不是则报错“请指定来源(如 git、path)或版本”; - 写入清单:在
[dependencies]或[contract-dependencies]表中插入条目;若表格不存在则先创建; - 验证与回滚:写入后立即基于新清单重建构建计划(
BuildPlan),如果解析失败会把Forc.toml恢复为修改前的备份内容;若启用了--dry-run,则提示“Dry run enabled. toml file not modified.”并还原文件; - 更新锁文件:修改成功后,同步更新
Forc.lock。
Git 来源的引用约束
DependencyDetails::validate(见 forc-pkg/src/manifest/mod.rs)对依赖字段做了严格的组合校验,违反任一规则都会报错:
branch/tag/rev必须在有git字段时才能使用;- 同一 Git 依赖中,
branch、tag、rev三者互斥,不能同时指定两个及以上; version不能与git、ipfs、path同时出现;namespace只能与带版本号的来源一起使用。
forc add的命令行层面对 Git 引用同样做了约束:--branch、--tag、--rev三个选项互斥且必须配合--git使用(见 forc/src/cli/shared.rs)。
五、契约依赖(contract-dependencies)与 salt
Sway 项目中,除了普通库依赖,还支持“契约依赖”——即在[contract-dependencies]表中声明的依赖,通常用于让一个合约调用或引用另一个已编译合约的 ID。forc add中通过--contract-dep开关进入该表,并可用--salt <HEX>指定自定义部署盐值。
从实现看(见 forc-pkg/src/manifest/mod.rs),契约依赖在ContractDependency结构中多携带一个salt: HexSalt字段,HexSalt的解析要求字符串以0x开头且为合法的 64 位十六进制(fuel_tx::Salt)。若未显式指定,则使用默认盐值(全零 Salt)。写入清单后,契约依赖条目会带上salt字段,例如:
[contract-dependencies] my_contract = { git = "https://github.com/example/contract", salt = "0x2222222222222222222222222222222222222222222222222222222222222222" }forc add与forc remove的源码测试覆盖了契约依赖带盐、默认盐以及非法盐值(“Invalid salt format”)的完整行为,可作为理解该功能的参考。
六、使用forc remove移除依赖
移除依赖使用forc remove命令,通用语法为:
forc remove <dep> [--contract-dep] [--package <NAME>] [--manifest-path <PATH>]从[dependencies]中移除:
forc remove custom_lib从[contract-dependencies]中移除:
forc remove my_contract --contract-dep定位 workspace 中的指定包:
forc remove custom_lib --package my_project与forc add相同,forc remove也支持--dry-run、--offline、--ipfs-node,并且其底层同样调用dep_modifier::modify_dependencies,只是Action变为Remove(见 forc/src/cli/commands/remove.rs)。移除时若依赖在对应表中不存在,会报错“the dependencyxxxcould not be found independencies/contract-dependencies”。
七、使用forc update更新依赖
forc update用于更新 Forc 依赖,并维护项目根目录下的Forc.lock锁文件:
forc update更新行为因来源而异:
- path 与 ipfs 依赖:更新无效,path 依赖始终直接引用本地目录,IPFS 依赖由 CID 内容寻址;
- git 分支依赖:更新到该分支的最新提交(HEAD);
- git tag 依赖:保持指向原 tag 不动;
- registry 依赖:按 SemVer 兼容范围更新到允许的最新版本。
常用变体
# 仅更新名为 std 的依赖 forc update -d std # 预检模式:只输出哪些依赖已过期、哪些已是最新,不真正写入锁文件 forc update --check # 指定项目路径 forc update --path <PROJECT_PATH>锁文件与更新机制
从 forc/src/ops/forc_update.rs 的实现可以看到:
- 读取清单后,从现有
Forc.lock(若存在)加载旧锁; - 基于所有 workspace 成员清单重新构建
BuildPlan并生成新锁(Lock::from_graph); - 计算新旧锁的差异并打印(
lock::print_diff),即那些被移除与新增的包; - 若未开启
--check,则把新锁序列化写回Forc.lock,并提示“Created new lock file at ...”; - 若开启
--check,则提示“--checkenabled:Forc.lockwas not changed”,不落盘。
锁文件本身由 forc-pkg/src/lock.rs 定义:它是一组[[package]]条目(对齐 Cargo 风格),每条记录包名、版本、来源字符串及依赖行;依赖行采用紧凑的单行格式(<dep_name>) <pkg_name> <source_string> (<salt>),便于 git diff。另外值得注意:当项目尚不存在Forc.lock时,forc build也会在构建前自动执行一次更新以生成锁文件,这保证了构建结果的可复现性。
八、隐式std依赖与 workspace 协同
隐式 std:如果Forc.toml的[dependencies]中没有显式声明std,Forc 会在解析清单时自动补入一个指向sway-lib-std的默认依赖(见 forc-pkg/src/manifest/mod.rs),其版本与当前forc-pkg版本绑定,以确保sway-core与标准库兼容。该行为可通过环境变量定制:
FORC_IMPLICIT_STD_PATH:指定本地 std 库路径;FORC_IMPLICIT_STD_GIT/FORC_IMPLICIT_STD_GIT_TAG/FORC_IMPLICIT_STD_GIT_BRANCH:指定从哪个 Git 仓库、tag 或分支拉取 std。
若确实不需要 std,可在[project]中设置implicit_std = false。
workspace 协同:在多包 workspace 中,Forc.lock位于 workspace 根目录,所有成员共享同一份锁文件;--package参数用于把forc add/forc remove的操作精确落到指定成员包上。仓库中的 examples/Forc.toml 即是一个由大量示例合约组成的 workspace 真实案例。
九、总结与最佳实践
- 共享代码优先走 registry:已发布的库用
custom_lib@0.0.1简写形式;注意当前 registry 来源不支持离线模式与通配符/caret 版本声明。 - 迭代中的库用 git:按需选择
--branch、--tag或--rev,三者互斥;需要跟随最新提交时用分支,需要固定版本时用 tag。 - 本地开发用 path:跨包改动时可无缝引用,且
forc update对其无影响,天然保证“所见即所得”。 - 契约间依赖用
--contract-dep与--salt:在[contract-dependencies]中声明,并注意 salt 的0x前缀格式。 - 善用 dry-run 与
--check:forc add --dry-run与forc update --check都能在不修改任何文件的前提下预览变更,适合在 CI 或提交前验证依赖改动。 - 锁文件务必入库:
Forc.lock固定了整棵依赖图的具体版本与提交,是构建可复现性的保证;forc build在缺少锁文件时也会自动生成。
【免费下载链接】sway🌴 Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考