news 2026/9/23 18:40:41

使用 Infer 构建 CI 差异化分析流程:从变更文件到增量报告

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 Infer 构建 CI 差异化分析流程:从变更文件到增量报告
  • 静态分析
  • 代码质量
  • 开发工具

【免费下载链接】infer

A static analyzer for Java, C, C++, and Objective-C

项目地址:https://gitcode.com/gh_mirrors/infer/infer
点击查看免费下载

导读

本文基于 Infer 官方推荐的 CI 集成方案(website/docs/01-steps-for-ci.md),系统讲解如何在持续集成环境中只分析代码变更、对比两个版本的分析结果,并输出"新增 / 已修复 / 既有"三类差异化报告。读完本文,你将掌握--changed-files-index--reactive--incremental-analysis--eradicateinfer reportdiff的组合用法,能够直接照抄落地到 Git + Gradle/Make 等常见 CI 流水线中。

CI 集成的推荐思路

Infer 官方文档给出的 CI 集成推荐流程是:先确定本次变更涉及的文件,然后以这些文件为起点,以反应式(reactive)模式启动分析,而不是对全量项目做无差别扫描。

如果希望在同一份代码上运行多个分析器(例如默认分析器之外再叠加eradicatepulsestarvation等),更高效的做法是分离 capture 阶段:只捕获一次编译信息,让所有分析器复用同一份中间结果,避免每个分析器都重复执行构建与翻译。

这一思路建立在 Infer 两阶段工作流之上。任何语言(Java、Objective-C、C/C++)的一次 Infer 运行都分为:

  1. Capture 阶段:拦截真实编译命令(如javacclangmakegradle),把源码翻译成 Infer 内部中间表示,存放在结果目录infer-out/(可用-o修改)。值得注意的是,若没有文件被编译,也就没有文件会被分析。
  2. Analysis 阶段:对infer-out/中的文件逐个函数/方法进行分析,报告输出到标准输出与infer-out/report.txt

完整的背景可参考 01-infer-workflow.md,其中详细介绍了全局(global)与差异化(differential)两种工作流的适用场景。

通用差异化工作流(Differential Workflow)

假设项目使用 Git:feature是待分析的功能分支,main是主干分支,项目用make构建。官方推荐按以下步骤在 CI 中对比两个分支的分析结果:

# go to feature branch if not there already git checkout feature # get list of changed files git diff --name-only origin/feature..origin/main > index.txt ## first run: feature branch # run infer on the feature branch infer capture -- make -j 4 # assuming a machine with 4 cores infer analyze --changed-files-index index.txt # store the infer report cp infer-out/report.json report-feature.json ## second run: main branch git checkout main # run capture in reactive mode so that previously-captured source files are kept if they are up-to-date infer capture --reactive --mark-if-unchanged -- make -j 4 infer analyze --incremental-analysis --changed-files-index index.txt # compare reports infer reportdiff --report-current report-feature.json --report-previous infer-out/report.json

步骤一:提取变更文件清单

git diff --name-only origin/feature..origin/main > index.txt利用 Git 本身的能力(而不是 Infer 自己推断)找出两个分支之间的全部变更文件,写入index.txt。该文件随后通过--changed-files-index传给 Infer。

从 infer/src/base/Config.ml 的选项定义可以看到它的语义:

Specify the file containing the list of source files from which reactive analysis should start. Source files should be specified relative to project root or be absolute

即:文件路径要么相对项目根目录,要么使用绝对路径;分析将从这些文件开始,并通过依赖关系波及到受影响的调用方。这里 "reactive analysis should start" 中的 reactive 一词并非巧合——该选项正是为反应式分析服务的:它定义分析的"种子"文件集,而不是让分析器对全部捕获文件做一遍完整扫描。

步骤二:Feature 分支——先 capture 再 analyze

第一次运行拆成两条命令而非一条infer run,原因在于分离 capture 阶段以便复用

  • infer capture -- make -j 4-j 4假设机器有 4 核,让 make 并行编译;Infer 拦截编译调用,完成翻译。此时只做翻译,不进行分析,开销相对可控。
  • infer analyze --changed-files-index index.txt:分析从index.txt列出的文件出发。由于这是全新构建的infer-out/,分析结果即为 feature 分支的完整报告。

之后cp infer-out/report.json report-feature.json把 feature 分支的报告另存下来,供最后比对使用。

步骤三:Main 分支——反应式捕获 + 增量分析

切回main后,两条命令出现了三个关键参数:

  • --reactive(短选项-r):告诉 Infer不要删除已有的infer-out/。默认情况下,重新运行 Infer 会清空旧的结果目录(这也是全局工作流每次全量分析的来源);而反应式模式保留之前捕获的、内容未变的源文件翻译结果,只有发生变化的文件才需要重新捕获。对于大项目,这能显著缩短第二次运行的时间。
  • --mark-if-unchanged(即手册中的--mark-unchanged-procs):在捕获阶段对新捕获过程做结构等价性比对——若新过程与旧版本结构等价,则标记为"未变更";同时该选项还阻止捕获阶段删除结果数据库,使未变更的结果能在后续增量分析中被复用。从 infer/man/man1/infer-capture.txt 的描述看,它正是为"重跑时尽量保留旧结果"服务的。
  • --incremental-analysis只对变更文件执行增量分析,它会自动打开--mark-unchanged-procs。手册(infer/man/man1/infer-analyze.txt)同时注明它不兼容--reanalyze--continue-analysis,使用时需注意。

三者配合的逻辑是:capture 阶段尽量保留未变文件的中间产物,analysis 阶段只重算真正受影响的函数,从而让第二次运行只付出"变更带来的代价"。

步骤四:生成差异化报告

infer reportdiff --report-current report-feature.json --report-previous infer-out/report.json

infer reportdiff接收两份标准 JSON 报告:--report-current是最新版本(此处为 feature 分支),--report-previous是基线版本(此处为 main 分支)。它会在结果目录下的infer-out/differential/中生成三个文件,格式与普通 Infer JSON 报告完全一致:

文件内容
introduced.json在 feature 分支中出现、main 中不存在的问题(新增
fixed.json在 main 中存在、feature 分支中已消失的问题(已修复
preexisting.json两个分支中都存在的问题(既有

语义与 man 页(infer/man/man1/infer-reportdiff.txt)中introduced/fixed/preexisting的定义一一对应。此外infer reportdiff还支持--file-renamings(提供文件重命名映射,避免因文件改名被误判为"新增 + 修复")、--costs-current/--costs-previous(对比成本分析报告)等选项。

这一输出契约在测试基建中也有体现:仓库的 infer/tests/diff.make 定义了默认构建目标即$(INFER_OUT)/differential/introduced.json,并把introduced.jsonfixed.jsonpreexisting.json三个文件逐一用infer report --from-json-report转成文本期望文件(*.exp.test)再与基准比对,验证差异化报告的正确性。

实战示例:Android Gradle + 多分析器

以下 CI 脚本同时运行默认分析与eradicate(空安全)分析器。假设feature为功能分支,main为主干分支:

git diff --name-only origin/feature..origin/main > index.txt infer capture -- ./gradlew --offline assembleDebug infer analyze --fail-on-issue --eradicate --changed-files-index ./index.txt

这条脚本浓缩了官方推荐的四个要点:

  • 用 Git 找出变更文件git diff --name-only,不依赖 Infer 自行比对;
  • capture 只跑一次,输出复用:后续所有分析器共享同一份infer-out/
  • 叠加额外分析器--eradicate与默认分析器并行运行。eradicate是 Infer 的 Java/Kotlin 空安全分析器,检查@Nullable/@NonNull注解是否被正确传播;
  • 只分析变更文件--changed-files-index ./index.txt把分析范围限定在变更文件及其依赖闭包内,大幅缩短 CI 关键路径。

--fail-on-issue让 Infer 在发现问题时以非零退出码结束,这是接入 CI 的必要条件——构建门禁(build gate)据此判定是否阻止合并。需要注意的是,若把infer analyze改为infer run,则--fail-on-issue需要在 capture 之前的全局参数位置传入;分离式写法天然规避了该问题。

为什么不直接infer run

很多人会问:既然infer run -- gradle build一行就能完成,为什么 CI 脚本要拆成 capture 与 analyze 两步?原因有二:

  1. 多分析器复用infer run的 capture 产物在一次运行结束后仍在infer-out/中保留;若想跑第二个分析器,只需再次infer analyze --<新分析器>,而不必重跑gradle build。对 Gradle 这类增量构建系统,这省去的往往是构建本身的数分钟开销。
  2. reactive 语义更清晰infer analyze单独执行时不会清空infer-out/(它分析的文件就在那里),配合--reactive捕获的既有产物,天然契合"只分析增量"的差异化场景。

结合源码理解背后的机制

  • --changed-files-index的定位:在 infer/src/base/Config.ml 中定义于Analyze命令的通用参数区,说明它专属于分析阶段。分析器以索引文件中的源文件为根,沿过程间依赖图(Analysis Dependency Graph)展开,相关实现可追溯至 infer/src/backend/AnalysisDependencyGraph.ml 与 infer/src/base/SourceFile.ml。
  • reactive 捕获的两种推进方式infer capture --reactive适合"一批变更一次分析";若在多次编辑之间连续运行,可加--continue让多次捕获累积成一次分析(--continue的语义是"保留之前标记为变更的文件/过程,并叠加本次新增的变更")。官方工作流文档 01-infer-workflow.md 给出了完整示例,且提示"分析当前变更的隔离结果"可以简写为infer run --reactive --continue -- analyze
  • 差异化输出是受测试保护的契约:infer/tests/diff.make 把introduced/fixed/preexisting三份 JSON 直接作为测试目标,说明这三类文件是 Infer 差分能力对外承诺的稳定接口,CI 脚本可以放心依赖其格式。

落地建议与注意事项

  1. 首次运行建议从干净项目开始:首次全量 capture 前先make clean/gradle clean,确保所有编译命令都被捕获(见 01-infer-workflow.md 的 tl;dr)。之后的运行才适合叠加--reactive
  2. index.txt路径规范:文件内路径应相对项目根目录或为绝对路径,否则--changed-files-index可能解析不到目标源文件。
  3. 多分析器并行:需要多个分析器时,保持"一次 capture + 多次 analyze"的写法;每个infer analyze用对应开关(如--eradicate--pulse--starvation)扩展分析器集合,或用--<checker>-only只跑指定分析器。
  4. 报告比较infer reportdiff的三个输出文件与普通报告同格式,可直接接入后续的告警聚合、PR 评论或看板;文件重命名较多时,用--file-renamings提供映射以减少误报。
  5. CI 门禁:用--fail-on-issue结合introduced.json(而非全量报告)决定是否阻断合并,能避免"历史遗留问题阻塞新变更"的困境。

将上述脚本嵌入 Git 钩子、Jenkins/GitHub Actions 或任意 CI 平台,即可获得"变更驱动、增量分析、前后对比"的完整静态分析闭环。

  • 静态分析
  • 代码质量
  • 开发工具

【免费下载链接】infer

A static analyzer for Java, C, C++, and Objective-C

项目地址:https://gitcode.com/gh_mirrors/infer/infer
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 18:40:37

8683性能优化:告别代码跑不通,高频面试题实战拆解

8683性能优化:告别代码跑不通,高频面试题实战拆解 复制来的代码跑不通,是不是经常卡在这里?不知道哪里错了,调了三天没结果,最后只能硬着头皮去问同事。这其实是很多开发者的日常噩梦,尤其是在准备面试或者接手新项目时,这种“黑盒”状态最让人焦虑。其实,大部分性能瓶颈和逻辑错误,都藏在那些不起眼的细节里…

作者头像 李华
网站建设 2026/9/23 18:40:23

《2012》下载一文搞懂

《2012》下载源码解析:3步搞定官方文档痛点 官方文档往往篇幅冗长,新手容易迷失在细节中,抓不住核心逻辑。 很多开发者面对《2012》下载相关需求时,常被繁杂的配置项劝退,不知从何下手。 通过源码解析,我们可以剥离表层噪音,直击数据获取与解析的核心骨架。 项目目标与场景定位…

作者头像 李华
网站建设 2026/9/23 18:40:18

互联网与大数据的关系是什么?从概念到应用彻底讲透

“互联网和大数据是什么意思”、“互联网包括大数据吗”、“大数据与互联网的关系是什么”——这几个问题&#xff0c;我在不同场合被问过太多次了&#xff0c;有刚入行的大数据开发新人&#xff0c;有做产品经理的同事&#xff0c;甚至连家里面退休的长辈刷短视频时都问过我&a…

作者头像 李华
网站建设 2026/9/23 18:40:09

小米网关一二三代怎么选?从Zigbee到Mesh看懂智能家居中枢

1. 从“智能家居死机”说起&#xff1a;为什么网关才是全屋智能的命门用了几年智能家居&#xff0c;我最大的感悟是&#xff1a;很多人买设备前纠结传感器买哪家、开关选什么牌子&#xff0c;结果装完发现设备频繁掉线、响应延迟、场景联动像个段子——大概率不是设备本身的问题…

作者头像 李华
网站建设 2026/9/23 18:40:11

3d全息投影视频源选型避坑:2024速查手册

3d全息投影视频源选型避坑:2024速查手册 刚把项目里的 three.js 从 r128 升到 r160,跑起来直接白屏?控制台报 WebGL context lost ,检查代码发现 WebGLRenderer 的初始化参数全变了。这种“版本升级后 API 全变了”的崩溃感,是搞 3D…

作者头像 李华
网站建设 2026/9/23 18:39:42

记忆棒手写实现保姆级教程:告别卡顿的3个性能坑

记忆棒手写实现保姆级教程:告别卡顿的3个性能坑 还在死磕语法细节?刚学会几个API,脑子一热想搭个完整项目,结果卡在“这块逻辑怎么串起来”上,代码跑不起来,心态直接崩了。别慌,这种“懂皮毛、缺骨架”的痛点,90%的开发者都踩过。今天这篇保姆级教程,不整虚的,直接带你从底层原理到落地代码,手把手拆解【…

作者头像 李华