GPLv2 合规争议在开源社区里从来不少见,“Google is in clear violation of the GPLv2”这样的标题一旦出现,往往像一颗石子扔进湖面,很快就变成大量转发和争论的素材。但真正处理过开源合规问题的人清楚,判断一个组织是否违反 GPLv2,不能只看声讨文章的措辞,而要落到具体证据上:它到底用了哪些 GPLv2 代码,发布的内容是不是衍生作品,有没有交付完整对应源码,有没有保留许可证声明。
我不打算替这个具体指控下结论,因为单凭一个标题拿不到足够证据。我更想做的是把“是否违规”这个问题拆开,讲清楚验证过程,让读者在遇到类似指控时,自己就能判断该信什么、该查什么、该怎么处理。适合读这篇文章的人有三类:准备在项目里引入 GPL 组件的开发者、需要做依赖合规审查的技术负责人、以及想搞清楚开源许可证到底怎么约束自己的项目维护者。下面按实际落地顺序拆一遍。
1. 先对准义务基线:GPLv2 到底要求什么
围绕 Google 的这类指控之所以有讨论度,一方面是因为公司体量大、产品线多,任何一层依赖出问题都会被放大;另一方面是因为 GPLv2 是上世纪九十年代初定稿的许可证,条款诞生年代远早于现在常见的模块化架构、动态加载机制和容器化分发方式。同一个事实,在不同人眼里可能得出完全不同的结论。所以第一步不是争论,而是把 GPLv2 的义务基线对准。
1.1 开源不等于放弃约束
很多人把 GPL 简单理解成“代码公开了,随便用”,这个理解在个人学习和非分发场景下基本成立,可一旦你把代码放入自己的产品再对外发布,GPLv2 的约束就变得非常具体。它不是一个“开放态度”的声明,而是一份带条件的授权:你可以使用、复制、修改、分发,但前提是满足许可证规定的义务。
GPLv2 最核心的机制是 copyleft,也叫“著佐权”。它的逻辑是反闭源化的:如果你基于 GPL 代码制作了衍生作品并对外分发,那么这个衍生作品整体也必须以 GPLv2 或与之兼容的许可证发布。用一句直白的话解释:别人把源码开放给你,你改完之后不能把改动关进黑盒,再当成私有财产卖出去。理解这一点,才知道“违规”到底违在哪里。
1.2 四条核心义务
GPLv2 全文并不长,落到工程交付上,可以压缩成一张表:
| 义务点 | 对应条款 | 落地含义 |
|---|---|---|
| 保留版权声明 | 第1条、第2条 | 复制和分发时不能删除原版权信息、免责声明 |
| 标注修改文件 | 第2条 | 被修改过的文件要写明修改日期和修改内容摘要 |
| 提供完整对应源码 | 第3条 | 分发二进制时必须附源码,或提供有效期不少于三年的书面要约 |
| 不附加额外限制 | 第7条 | 不能给接收者增加 GPLv2 之外的新限制 |
这里最容易被忽略,也最容易翻车的,是“完整对应源码”这六个字。“完整”指构成该程序的全部源码,不是抽出来的部分;“对应”指与发布的二进制同一个版本;“源码”在许可证里的定义是“修改该作品时优先采用的形式”。换句话说,如果你分发的是编译后的可执行文件,那源码就应该能通过某个构建流程重新得到那个可执行文件;如果你在编译前做了混淆或自动生成代码,这些处理步骤也应该在源码交付物里能复现。
实际审查中,很多团队把“放了源码”当成“完成了义务”,结果却发现三个问题:源码里没有构建脚本;源码版本和二进制版本对不上;许可证文本和版权声明没有一起给。任何一处出错,都可能构成不合规。这也是为什么真正的合规审计不是看某个目录里有没有 LICENSE 文件,而是要把整个交付物当作一个可重建的工程来检查。
2. 判断是否违规,先走三条审计主线
判断一个具体案例是否真的违反 GPLv2,不能凭感觉,也不能只看对方有没有“开源”两个字。我一般会把审计拆成三条主线,逐条查完再下结论。顺序也很重要:先确认有没有触发义务,再检查义务有没有履行,最后看有没有附加额外限制。
2.1 第一条线:确认是否存在衍生作品
判断“是否违规”的第一步,是确认对方有没有触发 GPL 义务。GPLv2 的条款只约束“复制、修改、分发” GPL 代码以及“基于 GPL 代码制作衍生作品”的行为。如果某个 GPLv2 项目只是被顺带提到,或者只被引用了很短的一段函数,结论会完全不一样。所以审计永远从“有没有衍生关系”开始。
我个人的检查顺序是这样的:
- 先导依赖清单。不同语言有不同的清单文件,比如 package.json、requirements.txt、go.mod、pom.xml、Cargo.toml,全部导出成一份统一清单。
- 扫描许可证字段。现在大多数包管理器都会在元数据里写 license 字段,先把字段扫出来,再挑选出 GPL、LGPL、AGPL 这类需要重点关注的许可证。
- 在源码里检索版权声明。不要只搜 LICENSE 文件,还要搜 “Copyright (C)” 加 GPL 声明、README 中的许可说明、代码注释顶部的许可证头。
- 对二进制做字符串和符号分析。用 strings 或 nm 之类的工具,看里面有没有特定 GPL 项目的错误信息、日志前缀或导出符号。
- 看构建脚本的链接方式。一个程序到底是静态链接、动态链接还是独立进程调用了 GPL 组件,直接影响“衍生作品”的判断。
这一步最容易踩的坑是:只扫 LICENSE 文件。很多项目的许可证写在了 README、项目主页或者包元数据里,单独搜 LICENSE 会漏掉大量真实情况。我建议把“许可证扫描”和“版权头扫描”当作两件事分开做,宁可花十分钟过滤噪声,也不要因为少扫一个字段而漏掉关键组件。
示例命令只做说明,实际以你的环境和依赖管理工具为准:
# 导出依赖清单,便于后续统一检查 npm list --all --json > dependencies.json2.2 第二条线:检查源码交付物是否完整
如果确认对方分发了一个包含 GPL 衍生作品的程序,接下来要验证的就是源码有没有给够。这条线按照“从粗到细”的顺序查:
- 有没有源码?给了全部文件,还是只给了核心模块?
- 源码和二进制是否对应?版本号、提交哈希、构建时间是否一致。
- 有没有构建脚本、依赖清单、配置文件和编译说明?
- 有没有修改文件清单?GPLv2 要求修改过的文件标注变更信息和日期。
- 如果是通过书面要约提供源码,要约是否还在有效期内,有没有写明获取方式和地址。
我验证源码完整性时会做一个小实验:把下载到的源码放进一个干净环境,严格按照它提供的构建说明执行。如果中途报错、缺少关键文件、或者需要我反复猜测参数才能继续,那这个源码交付物大概率不完整。不需要追求构建产物哈希完全一致,但至少不能被未知环境变量和未公开私有工具卡住。
这里有一个常见误区:构建脚本存在不等于构建可复现。有些团队把源码和构建脚本打包进去了,但脚本依赖