Ruffle 开源协议深度解析:MIT/Apache-2.0 双许可体系与第三方依赖许可证审计
【免费下载链接】ruffleA Flash Player emulator written in Rust项目地址: https://gitcode.com/GitHub_Trending/ru/ruffle
本文以 Ruffle 仓库中的 web/LICENSE.md 为蓝本,完整解析这个用 Rust 编写的 Flash Player 模拟器是如何通过 “MIT 或 Apache-2.0” 双许可(dual-licensing)机制发布代码的、如何在 Cargo workspace 中统一声明许可证、如何以 400+ 条目的表格形式登记全部第三方依赖的许可证归属,以及如何借助 deny.toml 中的 cargo-deny 策略对依赖许可进行自动化审计。读完本文,你将掌握在大型 Rust 项目中落地双许可声明、继承式 license 配置与依赖许可合规检查的完整方法。
一、双许可声明:MIT 或 Apache-2.0,由使用者选择
web/LICENSE.md 开篇即给出了 Ruffle 项目的核心许可声明(文件第 1~8 行):
Ruffle is licensed under either of
- Apache License, Version 2.0
- MIT license at your option.
这也就是业界所称的dual-licensing(双许可/可选许可):每一部分代码同时被两种许可证覆盖,下游使用者可以任意选择其中一种来使用、修改和再分发 Ruffle 的代码。Ruffle 主版权所有者为 “Copyright (c) 2018-2021 Michael R. Welsh 和 Ruffle contributors”(见 web/LICENSE.md 第 12 行)。
为什么很多高性能 Rust 项目(Ruffle 的 workspace 版本为 0.6.0,见 Cargo.toml 第 51 行)倾向于这种模式?两种许可证的定位差异如下:
| 维度 | MIT(web/LICENSE.md) | Apache-2.0(web/LICENSE.md) |
|---|---|---|
| 授予的权利 | 无限制地使用、复制、修改、合并、发布、分发、再许可和出售 | 版权许可 +显式专利许可(第 3 节 Grant of Patent License) |
| 专利条款 | 无显式专利授权 | 贡献者授予不可撤销的专利许可;若你对 Work 发起专利诉讼,该专利许可自起诉之日终止 |
| 再分发义务 | 保留版权声明与许可声明 | 四项义务(第 4 节):附带许可副本、修改文件需显著标注、保留版权/专利/商标/署名通知、NOTICE 文件随附 |
| 商标 | 未涉及 | 第 6 节明确不授予商标使用权,仅限描述作品来源的合理使用 |
| 责任限制 | AS IS,免除一切默示担保,作者不承担合同/侵权/其他责任 | AS IS(第 7 节),全面免除直接/间接/特殊/附带/后果性损害(第 8 节) |
| 额外责任 | 无对应条款 | 第 9 节允许再分发者以自己名义提供担保/支持/赔偿,但必须对所有贡献者进行无过错补偿(indemnify) |
从 web/LICENSE.md 第 106~127 行可以看到 Apache-2.0 的专利条款细节:各贡献者授予的专利许可仅覆盖 “其 Contribution 单独或与 Work 结合所必然侵犯的专利权利要求”,这是该条款保护下游不被贡献者专利追索的关键设计。
双许可带来的直接好处:
- 对于只想要极简条款的用户,MIT 的三行式许可几乎零负担;
- 对于需要明确专利保护的企业用户(例如将 Ruffle 编译进商业产品或浏览器扩展),可以改走 Apache-2.0,获得显式专利授权;
- Ruffle 自身代码同时满足两类宽松许可(permissive license),不会与大多数主流 OSS 依赖发生传染式冲突。
二、在 Cargo workspace 中统一声明许可证
web/LICENSE.md 是 web 端(浏览器构建)的许可文件,但真正的机器可读许可声明位于 Cargo 配置中。Ruffle 在 workspace 根 Cargo.toml 第 45~51 行集中声明:
[workspace.package] authors = ["Ruffle LLC <ruffle@ruffle.rs>"] edition = "2024" homepage = "https://ruffle.rs" license = "MIT OR Apache-2.0" repository = "https://github.com/ruffle-rs/ruffle" version = "0.6.0"关键点在于许可表达式的写法:license = "MIT OR Apache-2.0"。这里使用的是 SPDX 许可表达式语法,OR表示 “二选一”,与 web/LICENSE.md 中 “licensed under either of … at your option” 的表述严格对应。
各成员 crate 并不重复书写许可信息,而是通过 workspace 继承复用同一份声明。例如:
- core/Cargo.toml 第 6 行:
license.workspace = true - web/Cargo.toml 第 7 行:
license.workspace = true - swf/Cargo.toml 第 8 行:
license.workspace = true - desktop/Cargo.toml 第 6 行:
license.workspace = true - render/Cargo.toml 第 6 行:
license.workspace = true
这种 “workspace 统一定义、成员继承” 的模式保证几十个 crate 的许可元数据永不漂移,也是 crates.io 发布时自动识别MIT OR Apache-2.0的依据。
值得注意的是 swf/ 子目录(SWF 文件解析/写出库)额外携带了两份独立许可文件 swf/LICENSE-APACHE 与 swf/LICENSE-MIT。这种 “子目录附带 LICENSE-XXX 文件” 的布局是 MIT/Apache 双许可项目的标准做法:打包工具(cargo package、发布脚本)可以把对应文本直接放入产物中,满足 MIT “保留声明” 与 Apache-2.0 第 4(a) 条 “向接收者提供许可副本” 的义务。
三、第三方依赖许可证登记表:406 个 crate 逐条列证
web/LICENSE.md 第 218 行起是 “Third-Party Libraries” 章节,以三列表格逐条登记 Ruffle 依赖树中的第三方库:
| 列 | 含义 |
|---|---|
| Library Name | crate 名称(多数附原始仓库链接) |
| License | 该 crate 实际采用的许可证(可多选,如 Apache-2.0/MIT 双许可) |
| Authors/Notes | 版权声明,用于再分发时保留署名 |
统计该表(第 224~630 行,共 406 个条目)中出现的许可证标注,可以清楚看到 Ruffle 依赖树的许可构成以宽松许可为绝对主体:
| 许可证标注 | 出现次数 |
|---|---|
| MIT(含 “Apache-2.0/MIT” 组合) | 747 |
| Apache-2.0(含 “Apache-2.0/MIT” 组合) | 284 |
| Unlicense(公共领域放弃) | 20 |
| BSD-3-Clause | 14 |
| Zlib | 12 |
| ISC | 9 |
| MPL-2.0 | 5 |
| BSD-2-Clause | 4 |
| 0BSD / CC0-1.0 / BSL-1.0 / Custom ISC-style | 各 1~2 |
表中几个对 Ruffle 功能至关重要的条目值得单独说明:
wgpu/wgpu-core/wgpu-types:标注MPL-2.0,Copyright (c) wgpu developers——它是 Ruffle GPU 渲染管线(见 render/wgpu/)的底层,属于文件级弱 Copyleft,修改其源文件需以 MPL-2.0 发布该文件,整体项目仍保持 MIT/Apache-2.0;symphonia:MPL-2.0,用于音频解码(见 video/ 模块);nellymoser-rs:MIT,且备注写明其派生自 MIT 许可的 nelly2pcm 与 FFmpeg 项目——SWF 音频使用的 Nellymoser 编码,这正是 “Authors/Notes” 列保留来源说明的典型价值;swf一行指向 ruffle-rs 自己的仓库,标注 Apache-2.0/MIT,与第二节中独立的 swf crate 相呼应;ring、webpki两行许可证列为空(第 500、608 行)——这正是第四节中 cargo-denyclarify规则存在的原因:这类 crate 没有机器可读的 SPDX 声明,需要人工补充说明。
四、cargo-deny 策略:许可证白名单与人工澄清
表格是给人看的,deny.toml 是给 CI 机器看的。Ruffle 使用 cargo-deny 对依赖许可做强制审计,核心配置有三块:
1. 许可白名单(deny.toml 第 9~25 行):
[licenses] version = 2 # List of explicitly allowed licenses allow = [ "MIT", "Apache-2.0", "Apache-2.0 WITH LLVM-exception", "Zlib", "BSD-2-Clause", "BSD-3-Clause", "ISC", "Unicode-3.0", "MPL-2.0", "BSL-1.0", "CC0-1.0", "OFL-1.1", "Ubuntu-font-1.0", "CDLA-Permissive-2.0", "bzip2-1.0.6", ]任何不在白名单中的许可证都会使cargo deny check licenses失败——这实际上把 web/LICENSE.md 第三部分 “Licenses of Third-Party Libraries” 收录的 0BSD、Apache-2.0、BSD-2-Clause、BSD-3-Clause、CC0-1.0、ISC、MIT、MPL-2.0、Unlicense、Zlib 十类许可全文(文件第 632~1421 行)转化成了可执行的合规边界。
2. 人工澄清条目(deny.toml 第 33~53 行):对缺少机器可读许可信息的 crate 手工指定表达式。例如 NihAV 系列(视频解复用/解码,供 FLV/视频路径使用)由作者重新以 MIT 授权,配置为:
[[licenses.clarify]] name = "nihav_core" expression = "MIT" license-files = []而ring被澄清为expression = "MIT AND ISC AND OpenSSL",并校验其 LICENSE 文件哈希{ path = "LICENSE", hash = 0xbd0eed23 }——哈希校验保证 “人工声明的许可” 与上游文件内容一致,防止上游悄悄更换许可证而 CI 无感知。
3. 依赖来源管控(deny.toml 第 75~87 行):
[sources] unknown-registry = "deny" unknown-git = "deny" [sources.allow-org] # github.com organizations to allow git sources for github = [ "ruffle-rs", ]即所有依赖必须来自 crates.io 官方 registry,git 源只允许 ruffle-rs 官方组织。许可合规与供应链安全在此合一。
五、各许可证再分发义务速查(以 web/LICENSE.md 收录文本为准)
web/LICENSE.md 第 632 行之后收录了十类第三方许可证的全文,它们也是使用者再分发 Ruffle 相关代码时最直接的义务清单:
| 许可证 | 文件锚点 | 核心再分发义务 |
|---|---|---|
| MIT | web/LICENSE.md | 所有副本或实质部分须包含版权声明与许可声明 |
| Apache-2.0 | web/LICENSE.md | 提供许可副本、修改处显著标注、保留各类通知、NOTICE 随附 |
| BSD-2-Clause | web/LICENSE.md | 源码形式保留声明;二进制形式在文档/材料中复现声明 |
| BSD-3-Clause | web/LICENSE.md | 在 BSD-2 基础上增加第 3 条:未经书面许可不得用版权方名义为衍生产品背书/宣传 |
| 0BSD | web/LICENSE.md | 无任何条件,仅免责声明 |
| CC0-1.0 | web/LICENSE.md | 版权及相关权利放弃进入公共领域;含公共许可回退条款(第 3 节)与商标/专利不豁免条款 |
| ISC | web/LICENSE.md | 全部副本中保留版权与许可声明 |
| MPL-2.0 | web/LICENSE.md | 源形式分发须告知接收者许可条款(3.1 节);可执行形式须能以不高于分发成本的方式提供源码(3.2 节);不得删改许可通知(3.4 节);附 Exhibit A 标准声明 |
| Unlicense | web/LICENSE.md | 无义务,版权明示献予公共领域 |
| Zlib | web/LICENSE.md | 不得歪曲来源;修改版须显著标记;源分发不得移除本声明 |
其中 MPL-2.0 与 Apache-2.0 是最值得细读的两类:前者第 5 节规定了违规后权利自动终止与 30/60 天通知后的恢复机制,后者第 3 节专利许可的终止条件是 “对 Work 提起专利侵权诉讼”。这两段直接决定了下游商业用户在何种情形下会丧失使用资格。
六、集成 Ruffle 时的合规实操清单
综合 web/LICENSE.md、Cargo.toml 与 deny.toml,将 Ruffle 相关代码纳入自己项目时的可执行步骤:
- 选定许可轨道:使用 Ruffle 代码(含 core/、swf/、render/ 等 crate)时,在文档中明确你走 MIT 还是 Apache-2.0;二者义务差异很小,但需要专利保护时选 Apache-2.0。
- 保留声明:保留所有被修改文件的版权/许可头;发布二进制时在产品文档或随附 LICENSE 中放入对应许可全文(可参照 swf/LICENSE-APACHE、swf/LICENSE-MIT 的 “LICENSE-后缀” 命名惯例)。
- NOTICE 随附:若分发的 Work 中包含 NOTICE 文件,按 Apache-2.0 第 4(d) 条将相关归属通知放入衍生作品的 NOTICE、源文档或界面中。
- 依赖许可核查:在自己的仓库复制 deny.toml 的
[licenses]/[sources]配置段,运行cargo deny check,把依赖许可白名单纳入 CI;对ring这类无机器可读声明的 crate 使用[[licenses.clarify]]补全并校验文件哈希。 - 尊重特殊授权:例如 NihAV 相关 crate 是作者 “kindly relicensed for us under MIT”(见 deny.toml 第 31~32 行注释),此类特例的许可以澄清条目为准,不要直接采用上游声明。
- 商标边界:Apache-2.0 第 6 节不授予 “Ruffle” 名称/商标的使用权,仅限描述作品来源的合理使用——再分发时不要暗示官方背书。
七、小结
web/LICENSE.md 虽然位于 web 子目录下,但它实际上承载了 Ruffle 项目三层合规信息:面向使用者的MIT OR Apache-2.0 双许可全文(第 1~216 行)、面向审计者的406 条第三方依赖许可登记表(第 218~630 行)、面向再分发者的十类第三方许可证全文(第 632~1421 行)。它与 Cargo.toml 中 workspace 级的license = "MIT OR Apache-2.0"声明、各 crate 的license.workspace = true继承,以及 deny.toml 的白名单/澄清/来源管控共同构成了一个 “声明—登记—机器校验” 闭环,是 Rust 生态大型项目中依赖许可治理的完整范本。
【免费下载链接】ruffleA Flash Player emulator written in Rust项目地址: https://gitcode.com/GitHub_Trending/ru/ruffle
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考