哪些工具支持编辑 project.pbxproj 个文件?

What tools support editing project.pbxproj files?

我想使用命令行直接编辑 project.pbxproj(针对 CI 服务器脚本)

什么工具可以让我做到这一点?

我曾经使用PlistBuddy编辑输出Info.plist;然而,我真正想做的是编辑这个用户定义的字段,它在多个地方使用,我真的不想在每个 plist 位置寻找它

我知道这个问题已经回答了一段时间,但由于最初的问题是关于支持操作 .pbxproj 文件的工具,而且许多其他人可能正在寻找相同的信息,所以我是这样做的它。我花了很长时间才弄明白这一点,因为当我开始尝试这个时我对 Xcode 非常不熟悉,所以我希望这可以节省其他人我不得不投入的悲伤时间。

您可以使用 plutil 命令将 .pbxproj 文件从旧 .plist 格式转换为 XML 或 JSON 格式能够更容易地操纵。我正在使用 JSON。为此,只需 运行:

plutil -convert json project.pbxproj

这将转换 project.pbxproj 的格式,但请注意 - 与常识相反 - 输出不会是另一个扩展名为 JSON 的文件,例如 project.json .将发生的是 project.pbxproj 将转换为 JSON 格式,但保留其神秘的 .pbxproj 扩展名。因此,即使文件的格式已更改,Xcode 仍会选择它并以其新的 JSON 格式使用它。

然后您可以使用您选择的任何 JSON 操作工具轻松更改 project.pbxproj。我在 Groovy 脚本中使用 Groovy 的 JsonSlurper class。

注意 我也探索了 XML 选项,但我发现 XML 格式的 project.pbxproj 文件解析起来很麻烦。元素没有正确嵌套以允许轻松遍历树。它受到以下问题的困扰:

<key>someKey</key>
<dict>
    <!--More elements which provide configuration for the key above-->
</dict>

所以它本质上是位置性的。您必须查找与您要操作的设置相对应的 key 元素,然后跳转到它之后的 dict 元素。这意味着您必须将每个 XML 元素的子元素装入一个数组中,以便为它们建立索引。

project.pbxproj 也是一个 old-style ASCII property list 文件。所以你可以使用/usr/libexec/PlistBuddy来编辑它。

像这样打印一些 User-Defined 键的值,

# Get the key A83311AA20DA4A80004B8C0E in your project.pbxproj
# LZD_NOTIFICATION_SERVICE_BUNDLE_ID is defined by me,
# Replace key paths with your own.
/usr/libexec/PlistBuddy -c 'print :objects:A83311AA20DA4A80004B8C0E:buildSettings:LZD_NOTIFICATION_SERVICE_BUNDLE_ID' LAAppAdapter.xcodeproj/project.pbxproj

像这样设置它的值,

/usr/libexec/PlistBuddy -c 'set :objects:A83311AA20DA4A80004B8C0E:buildSettings:LZD_NOTIFICATION_SERVICE_BUNDLE_ID com.dawnsong.notification-service' LAAppAdapter.xcodeproj/project.pbxproj

UPDATEPlistBuddy 会自动将 project.pbxproj 转换为 xml-format plist 文件,因为 macOS Catalina 或更早版本。请考虑将设置项移动到 xcconfig 文件而不是 ,因为 xcconfigproject.pbxproj 更小更简单,并且在编辑时不容易出错使用 perl 脚本.

这里有 3 个 open-source 实现 .pbxproj 文件编辑的工具:

就个人而言,我使用基于 NodeJS 的工具获得了最佳体验。到目前为止,它已经可靠地满足了我们所有的需求。

下面列出了一个示例 javascript 文件 update-project.js,它设置了开发团队 ID、应用程序权利,将 GoogleService-Info.plist 文件添加到项目并将其作为构建目标。以此为灵感,根据您的需要调整脚本及其路径:

const fs = require('fs')
const xcode = require('xcode')

if (process.argv.length !== 3) {
  console.error("Please pass the development team ID as the first argument")
  process.exit(1)
}
const developmentTeamId = process.argv[2]
const path = 'ios/App/App.xcodeproj/project.pbxproj'
const project = xcode.project(path)

project.parse(error => {
  const targetKey = project.findTargetKey('App')
  const appGroupKey = project.findPBXGroupKey({path: 'App'})
  project.addBuildProperty('CODE_SIGN_ENTITLEMENTS', 'App/App.entitlements')
  project.addBuildProperty('DEVELOPMENT_TEAM', developmentTeamId)
  project.addFile('App.entitlements', appGroupKey)
  project.removeFile('GoogleService-Info.plist', appGroupKey)
  const f = project.addFile('GoogleService-Info.plist', appGroupKey, {target: targetKey})
  f.uuid = project.generateUuid()
  project.addToPbxBuildFileSection(f)
  project.addToPbxResourcesBuildPhase(f)
  fs.writeFileSync(path, project.writeSync())
})

以上脚本可以用

执行

yarn run update-project <arguments...>

鉴于 update-project 已在 package.json 中注册:

{
  ...,
  "scripts": {
    ...
    "update-project": "node update-project.js"
  },
  ...
}