Sway 依赖管理实战:forc add 命令详解与 Forc.toml 写入机制
【免费下载链接】sway🌴 Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway
在 Sway 项目(基于 Forc 工具链)中,管理Forc.toml清单文件里的依赖最标准的方式就是forc add命令:它一次性把版本依赖、本地路径依赖、Git 依赖或 IPFS 依赖写入清单的[dependencies]或[contract-dependencies]区段,并同步更新Forc.lock。读完后你能掌握forc add的完整参数用法、依赖来源的合法性校验规则,以及从源码层面理解它如何安全地修改清单文件(包括 dry-run 与失败回滚机制),从而在真实项目中可靠地完成依赖管理。
命令用法与参数
forc add的官方用法为:
forc add [OPTIONS] <DEP_SPEC>...其中位置参数<DEP_SPEC>支持同时添加多个依赖,每个依赖的格式为name[@version],例如custom_lib@0.1.0或custom_contract。源码中该参数被定义为可接受 1 到多个值的字符串向量(见 add.rs 中dependencies: Vec<String>的声明),版本部分在解析阶段会做 semver 校验,非法版本(如foo@not-a-version)会直接报错invalid version requirement。
完整选项如下:
| 选项 | 说明 |
|---|---|
--path <PATH> | 添加本地路径依赖 |
--git <URI> | 添加 Git 来源依赖 |
--branch <branch> | Git 分支(须与--git组合) |
--tag <tag> | Git 标签(须与--git组合) |
--rev <rev> | Git 修订号(须与--git组合) |
--ipfs <CID> | 添加 IPFS 来源依赖 |
--contract-dep | 写入[contract-dependencies]区段而非[dependencies] |
--salt <SALT> | 合约部署用盐值(仅对合约依赖生效) |
--package <SPEC> | 在 workspace 中指定要修改的具体包 |
--manifest-path <PATH> | 指定Forc.toml的路径 |
--dry-run | 只展示将发生的变更,不写入文件 |
--offline | 不获取任何远程依赖 |
--ipfs-node <FUEL\|PUBLIC\|LOCAL\|URL> | 拉取 IPFS 来源依赖时使用的 IPFS 节点 |
从 CLI 参数定义看(shared.rs),这些选项被组织成四个共享参数组,且带有严格的互斥约束:
- Source 组(
SourceArgs):path、git、ipfs三者构成一个 clap 互斥组,即同一条命令里三种来源只能选其一; - Git 引号组(
GitRef):branch、tag、rev三者互斥(multiple(false)),并且必须要求同时给出git(.requires("git"))。这意味着forc add mylib --branch main(缺少--git)在命令行解析阶段就会被拒绝。
官方帮助信息中给出的典型示例(同样定义在 add.rs 的cli_examples宏里):
# 添加一个版本依赖 forc add <DEP>[@<VERSION>] # 添加一个合约依赖 forc add <DEP>[@<VERSION>] --contract-dep # 预演(不实际写入) forc add <DEP>[@<VERSION>] --dry-run依赖来源的解析规则
forc add接收参数后,会将其转换为内部结构ModifyOpts(dep_modifier.rs),并调用dep_modifier::modify_dependencies执行实际修改。每个<DEP_SPEC>先被解析为DepSpec { name, version_req }:按第一个@切分,名称在前、版本要求在后;空字符串会被拒绝(Dependency spec cannot be empty)。
随后,resolve_dependency依据“命令行来源参数 + 版本要求”的组合决定最终写入清单的依赖形态,核心逻辑如下:
- 只给了版本号(如
mylib@1.0.0):写入简单的Dependency::Simple(version),在清单中表现为mylib = "1.0.0"; - 给了来源参数(
--path/--git/--ipfs):写入Dependency::Detailed(details),清单中表现为内联表(inline table); - 两者都没给:如果该包名恰好是当前 workspace 的同级成员(sibling),则自动推导相对路径写成路径依赖(例如
../pkg1);否则报错dependency ... source not specified. Please specify a source (e.g., git, path) or version。源码中此处还留有注释,表明 registry(如 forc.pub)的集成是后续计划。
写入前,DependencyDetails::validate()(manifest/mod.rs)会执行合法性校验,以下是实际会触发的错误规则:
| 非法组合 | 报错信息 |
|---|---|
有branch/tag/rev但没有git | Details reserved for git sources used without a git field |
branch、tag、rev三者同时给出 | Cannot specifybranch,tag, andrevtogether for dependency with a Git source |
branch与tag同时给出 | Cannot specify bothbranchandtagfor dependency with a Git source |
rev与tag同时给出 | Cannot specify bothrevandtagfor dependency with a Git source |
branch与rev同时给出 | Cannot specify bothbranchandrevfor dependency with a Git source |
version与git同时存在 | Both version and git details provided for same dependency |
version与ipfs同时存在 | Both version and ipfs details provided for same dependency |
version与path同时存在 | Both version and path details provided for same dependency |
namespace存在但无version | Namespace can only be specified for sources with version |
这些规则在 dep_modifier.rs 的测试模块(test_resolve_dependency_detailed_variant_failure等)中都有对应的单元测试覆盖,是验证上述行为的直接依据。
值得注意的一个细节:Git 来源的字段会按原样写入清单内联表,其中 IPFS CID 写入的键名是cid(见generate_table函数:inline.insert("cid", ...)),而 CLI 参数名是--ipfs,两者不要混淆。
合约依赖:--contract-dep 与 --salt
当依赖的是合约(contract)时,需要用--contract-dep将依赖写入[contract-dependencies]区段而不是[dependencies]。该命令的实现会额外处理salt(盐值):
- salt 用于合约部署时计算确定性的合约 ID;
- 提供
--salt时,值必须以0x开头的十六进制字符串(HexSalt::from_str会先剥离0x前缀再解析,见 manifest/mod.rs 中HexSalt的FromStr实现),非法格式会报Invalid salt format; - 未提供 salt 时,使用
fuel_tx::Salt::default()作为默认盐值。
写入清单后,合约依赖的条目形如:
[contract-dependencies] some_contract = { version = "1.0.0", salt = "0x2222222222222222222222222222222222222222222222222222222222222222" }仓库中的真实示例可以参考 multi_contract_calls/caller/Forc.toml,其中就包含[contract-dependencies]区段,演示了调用方合约依赖被调用方合约的写法。关于 salt 与合约 ID 的对应关系,可结合 forc contract-id 命令文档 进一步查看。
workspace 支持:--package 与同级包自动推导
当在 workspace 根目录(Forc.toml为[workspace]清单)执行forc add时,resolve_package_path无法确定要修改哪个成员,因此必须提供--package <SPEC>,否则会报错并列出所有可用成员:
`forc add` could not determine which package to modify. Use --package. Available: pkg1, pkg2如果--package指定的包名不存在,则报package(s) <name> not found in workspace <root>。这些行为在 dep_modifier.rs 的测试(test_resolve_package_path_workspace_package_not_set、test_resolve_package_path_workspace_package_not_found)中被逐条验证。
另一个实用能力是workspace 同级包自动推导:当依赖名与某个 workspace 成员同名、且未显式指定来源和版本时,forc add会自动将其解析为相对路径依赖。例如 workspace 下有pkg1和pkg2,执行forc add pkg1 --package pkg2会写入:
[dependencies] pkg1 = { path = "../pkg1" }(相对路径以两个包的共同父目录为基准计算,见resolve_dependency中strip_prefix的处理;对应测试为test_resolve_dependency_from_workspace_sibling。)同时,命令会阻止包依赖自身——若目标包与当前包同名同目录,报错cannot add ... as a dependency to itself。
--manifest-path、--dry-run 与 --offline
--manifest-path:直接指定Forc.toml路径。若未提供,则从当前工作目录向上定位清单文件(ManifestFile::from_dir(cwd))。
--dry-run:实现上分两步。首先正常完成清单修改与构建计划(build plan)计算,确认依赖解析无误后,把清单内容回写为修改前的备份,并输出Dry run enabled. toml file not modified.日志(dep_modifier.rs 中modify_dependencies的opts.dry_run分支)。因此在 dry-run 模式下,清单文件最终保持原样,其价值在于先验证依赖能否被成功解析,再执行正式写入。
--offline:禁止在依赖管理过程中访问网络,只使用已下载的依赖缓存。该标志会透传给构建计划生成(BuildPlan::from_lock_and_manifests),在离线环境或 CI 场景中保证行为可复现。
清单写入与失败回滚机制
forc add对Forc.toml的修改并非简单的字符串拼接,而是基于toml_edit的DocumentMut解析-修改-序列化流程(dep_modifier.rs 中modify_dependencies函数),这样能保留原有注释与格式。其完整流程为:
- 读取目标包的
Forc.toml,解析为 TOML 文档,并保存一份备份(backup_doc); - 加载旧
Forc.lock; - 按区段(
dependencies或contract-dependencies)逐条插入新依赖——Section::add_deps_manifest_table会先确保区段表存在,简单版本依赖写成字符串值,详细依赖写成内联表; - 写回文件后,基于新清单重建构建计划并刷新 lock 文件;
- 若构建计划重建失败(例如远程依赖拉不下来),自动将清单恢复为备份内容,并以失败退出——保证不会留下一个无法构建的
Forc.toml。
这套“写入-校验-回滚”机制意味着:只要你执行forc add后看到命令成功返回,清单与 lock 文件就是自洽可用的;反之任何解析或获取失败都不会污染你的清单文件。
相关命令与延伸阅读
forc remove:与forc add共享同一套dep_modifier实现(Action::Remove),负责从[dependencies]或[contract-dependencies]移除依赖,参数风格一致,参见 forc remove 文档;forc build:添加依赖后用于编译验证,参见 forc build 文档;forc update:刷新 lock 文件中的依赖版本,参见 forc update 文档;- 更底层的清单结构与来源获取逻辑,可查看 forc-pkg/src/manifest/mod.rs(
Dependency、DependencyDetails、ContractDependency定义)与 forc-pkg/src/source/mod.rs(IPFSNode的FUEL/PUBLIC/LOCAL/自定义 URL 解析)。
小结
forc add以“位置参数 + 来源选项”的组合覆盖了版本、本地路径、Git、IPFS 四类依赖来源,并通过--contract-dep/--salt支持合约依赖、通过--package精确操作 workspace 成员。理解其背后的解析校验规则(版本与来源互斥、Git 引用互斥)和“写入-校验-回滚”的清单处理流程,能帮助你在自动化脚本与 CI 中安全地管理 Sway 项目依赖;配合--dry-run预演与--offline离线模式,可在不触碰现有工程文件的前提下验证依赖变更的正确性。
【免费下载链接】sway🌴 Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考