如何阻止 Xcode 11 将 CFBundleVersion 和 CFBundleShortVersionString 更改为 $(CURRENT_PROJECT_VERSION) 和 $(MARKETING_VERSION)?

How to stop Xcode 11 from changing CFBundleVersion and CFBundleShortVersionString to $(CURRENT_PROJECT_VERSION) and $(MARKETING_VERSION)?

从版本 11 开始,每当我在目标设置(选项卡 "General")。

我输入的实际版本和构建值现在存储在 project.pbxproj 文件中。我不希望或不喜欢这种行为,因为我使用 shell 脚本在构建时修改值。

我可以在 Info.plist 文件中手动设置正确的值,但是只要我在目标设置中更改版本号或内部版本号,Info.plist 文件就会再次更改 Xcode.

如何阻止 Xcode 11 这样做?

当我修改构建脚本以更改项目文件本身时,Xcode 将在项目文件更改后立即取消构建。

不要。

据推测,这种行为发生变化是有原因的。如果以后 Xcode 功能建立在这种行为之上,事情会变得越来越多 "constructed"。

与其尝试改变 Xcode,不如更改构建脚本检索这些值的方式:

如果您需要操作 project.pbxproj 文件,它是一个有据可查的 Next 样式 plist。您可以使用与此旧格式兼容的 plistbuddy。如果您有更复杂的操作,您还可以使用 awk 和更多脚本。

如果我了解您的用例,您可以编写一个脚本,使用 awk 获取最高版本号,然后使用 sed 更新它可以在文件中找到的所有较低版本号。

到目前为止的路

我的用例是:

  1. 我正在跨多个目标同步版本和内部版本号。
  2. 我正在将版本和内部版本号与目标的 Settigns.bundle
  3. 同步
  4. 我正在从 CI 服务器读取和修改内部版本号。

我曾经将第 1 点和第 2 点作为目标构建脚本执行,第 3 点作为 CI 本身的自定义脚本执行。

在 Xcode 构建设置中存储版本和构建的新方法导致脚本出现问题,因为它们不再能够有效地修改值。至少阅读是可能的。

不幸的是,我无法找到阻止 Xcode 将版本和内部版本号存储到项目构建设置中的合法方法,但我已经设法创建了一个解决方法。

事实证明,在构建或归档时,使用了 Info.plist 中写入的值。这意味着该值在构建期间被替换,这不允许我们在同一构建期间修改它。

我也尝试过使用 xcodeproj cli 修改项目,但是对项目的任何更改都会导致任何构建停止,因此此解决方案无效。

最终,在我尝试了很多不同的方法之后,我终于找到了一个不违反 Xcode 新行为的折衷方案。

简答:

作为目标预操作,执行一个脚本,将 CFBundleShortVersionStringCFBundleVersion 的相应值写入目标的 Info.plist

作为真实来源,我使用 Xcode 构建设置来读取所需目标的 MARKETING_VERSIONCURRENT_PROJECT_VERSION 的值。

这样,当您修改项目设置中的值时 - 在下一个 build/archive - 它们将被写入 Info.plist,允许任何现有的脚本逻辑继续工作.

详细解答

根据构建操作修改资源的唯一方法是使用 pre-action 脚本。如果您尝试从构建脚本执行此操作 - 更改不会立即生效,也不会出现在 build/archive.

的末尾

要添加预构建操作 - 转到编辑方案。

然后展开构建和存档部分。 在 Pre-action 下,单击 Provide build and settings from 下拉菜单和 select 您希望从中读取值的真实目标源。

添加以下脚本:

# 1) 
cd ${PROJECT_DIR}

# 2) 
exec > Pruvit-Int.prebuild.sync_project_version_and_build_with_info_plists.log 2>&1

# 3) 
./sync_project_version_and_build_with_info_plists.sh $MARKETING_VERSION $CURRENT_PROJECT_VERSION

凭据行执行以下操作:

  1. 转到同步脚本所在的目录以执行它
  2. 允许在预操作期间写入日志,否则任何输出都默认静音
  3. 通过提供 MARKETING_VERSIONCURRENT_PROJECT_VERSION
  4. 来执行同步脚本

最后一步是编写您自己的同步脚本,将提供的 MARKETING_VERSIONCURRENT_PROJECT_VERSION 的值读取到相应的 target/s 以及您需要的任何其他时间。

在我的例子中,脚本如下:

#!/bin/bash

#IMPORTANT - this script must run as pre-action of each target's Build and Archive actions

version_number=
build_number=

echo "version_number is $version_number"
echo "build_number is $build_number"

#update Pruvit/Info.plist
pruvitInfoPlist="Pruvit/Info.plist"
/usr/libexec/PlistBuddy -c "Set CFBundleShortVersionString $version_number" $pruvitInfoPlist
/usr/libexec/PlistBuddy -c "Set CFBundleVersion $build_number" $pruvitInfoPlist

#update Pruvit/Settings.bundle
settingsPlist="Pruvit/Settings.bundle/Root.plist"
/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:0:DefaultValue $version_number" $settingsPlist
/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:1:DefaultValue $build_number" $settingsPlist

#update BadgeCounter/Info.plist
badgeCounterInfoPlist="BadgeCounter/Info.plist"
/usr/libexec/PlistBuddy -c "Set CFBundleShortVersionString $version_number" $badgeCounterInfoPlist
/usr/libexec/PlistBuddy -c "Set CFBundleVersion $build_number" $badgeCounterInfoPlist

我在我的两个应用程序目标之间使用共享 Info.plistSettings.bundle,所以我必须更新一次。

我还使用了一个通知服务扩展 BadgeCounter,它必须具有完全相同的版本并构建为嵌入它的目标。所以我也更新这个。