Xcode 报 Multiple commands produce Info.plist:找出重复写入同一产物的步骤
Info.plist 同时被复制和处理时会出现重复产物。按构建日志移除资源复制关系,核对生成模式、INFOPLIST_FILE 和各配置覆盖,再检查最终 App 清单。
手动添加 Info.plist 后,构建突然报 Multiple commands produce '.../Example.app/Info.plist',删除 DerivedData 又复现。展开错误后,如果一条是从源码目录复制文件的 copy command,另一条是生成或处理清单的 process command,问题在构建规则:两个步骤都想写入 App 包内的同一个文件。
本文针对 Xcode 管理的 iOS App target,尤其是支持自动生成 Info.plist 的工程。输出路径若是 GoogleService-Info.plist、某张图片或整个 .app 目录,不能直接套用本文删除资源项的处理。
先看完整输出路径和两条命令
在 Report navigator 打开失败的构建,展开 Multiple commands produce。记录输出路径、target 名称,以及每条命令的输入与输出。例如:
Multiple commands produce '.../Example.app/Info.plist'
Target 'Example' has copy command from 'Example/Info.plist' ...
Target 'Example' has process command with output '.../Example.app/Info.plist'
这里即使磁盘上只有一份源文件,也会冲突:同一源文件可能既作为普通资源被复制,又作为 bundle 信息清单被处理。不要只在项目导航栏寻找“两份同名文件”。
App、扩展和测试 target 可以各自拥有 Info.plist。文件数量本身不是错误,关键是两个命令是否在写同一条产物路径。
从 Copy Bundle Resources 移除 App 清单
选中报错中的 target,打开 Build Phases > Copy Bundle Resources,查找该 target 使用的 Info.plist。如果存在,从这个构建阶段移除。这里移除的是资源复制关系,不是删除磁盘文件。
同时检查 File inspector 的 Target Membership,避免手动清单再次被作为普通资源加入。对于采用文件夹同步等较新工程组织方式的项目,界面可能不显示逐项资源列表,应核对目标成员关系及对应资源排除设置,并以最终构建日志确认复制命令是否消失。
App 的主 Info.plist 由专门步骤处理,不能当普通资源原样复制。也不要顺手删除名称相似的第三方配置文件:GoogleService-Info.plist 等通常是应用需要读取的资源,必须根据它自己的重复命令判断。
再核对生成模式与输入文件
在同一 target 的 Build Settings 中查看 Generate Info.plist File(GENERATE_INFOPLIST_FILE)和 Info.plist File(INFOPLIST_FILE)。查看实际失败的 Debug 或 Release 配置,并留意 project、target 和 .xcconfig 的覆盖关系。
常用配置有两种:
- 由 Xcode 生成:
GENERATE_INFOPLIST_FILE=YES,在 target 的 Info 或对应内置 build settings 中配置内容;没有自定义输入文件时,不需要为了修复错误新建一份清单。 - 提供文件作为输入:
INFOPLIST_FILE指向实际源文件,例如Example/Info.plist,但这份文件不进入 Copy Bundle Resources。设为GENERATE_INFOPLIST_FILE=NO时,Xcode处理该文件中的构建变量并生成最终清单。
GENERATE_INFOPLIST_FILE=YES 与 INFOPLIST_FILE 同时存在并不必然冲突:Xcode支持将输入文件与生成值合并。重复写入来自额外的复制或生成步骤,不能只凭这两个设置同时存在就断言配置错误。
如果决定关闭自动生成,必须先保证 INFOPLIST_FILE 指向可读取的源文件。只把 YES 改成 NO,可能把重复产物错误换成“没有 Info.plist、无法签名”。
对于 workspace 工程,可用下面的只读命令核对当前 scheme 的解析结果。替换 workspace、scheme 和配置名;工程文件则使用 -project:
xcodebuild \
-workspace Example.xcworkspace \
-scheme Example \
-configuration Debug \
-showBuildSettings
输出可能包含多个 target,应在报错 target 对应的段落检查上述两个设置。文件路径与 build settings 变量都要在该配置下能够正确解析。
列表里没有重复项,就继续追踪写入者
如果已经移除资源复制关系,但诊断仍指向同一个 Info.plist,检查 Run Script、Copy Files、自定义 build rule 和生成工程的配置。脚本可能直接写入 TARGET_BUILD_DIR 内的 App 包,也可能与 Xcode 的处理步骤声明相同输出。
让自定义脚本生成一份独立的输入文件,再交给 Xcode处理,通常比脚本直接覆盖最终包内清单更容易控制。具体改法取决于完整日志;不要盲目删除脚本的 Input/Output Files 声明,那可能只隐藏依赖关系,保留实际竞争写入。
修改构建关系前保留版本差异。需要恢复时,恢复被移除的构建项和原设置;不必删除整个项目或重新创建所有 target。
验收要看最终 App 包
重新构建后,确认日志里不再有第二个步骤写入同一路径。找到这次实际生成的 .app,用只读命令检查清单,示例路径须替换:
plutil -p '/absolute/path/to/Example.app/Info.plist'
核对 bundle identifier、版本号,以及此次改动涉及的权限用途字符串等必要键。源文件正确,不代表最终合并结果符合预期。
最后分别构建需要交付的配置;只修 Debug 时,Release 中的独立覆盖仍可能留下冲突。清理构建目录可以用于修复后的复查,但不能替代消除重复写入规则。


