如何阻止 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
更新它可以在文件中找到的所有较低版本号。
到目前为止的路
我的用例是:
- 我正在跨多个目标同步版本和内部版本号。
- 我正在将版本和内部版本号与目标的
Settigns.bundle
同步
- 我正在从 CI 服务器读取和修改内部版本号。
我曾经将第 1 点和第 2 点作为目标构建脚本执行,第 3 点作为 CI 本身的自定义脚本执行。
在 Xcode 构建设置中存储版本和构建的新方法导致脚本出现问题,因为它们不再能够有效地修改值。至少阅读是可能的。
不幸的是,我无法找到阻止 Xcode 将版本和内部版本号存储到项目构建设置中的合法方法,但我已经设法创建了一个解决方法。
事实证明,在构建或归档时,使用了 Info.plist
中写入的值。这意味着该值在构建期间被替换,这不允许我们在同一构建期间修改它。
我也尝试过使用 xcodeproj
cli 修改项目,但是对项目的任何更改都会导致任何构建停止,因此此解决方案无效。
最终,在我尝试了很多不同的方法之后,我终于找到了一个不违反 Xcode 新行为的折衷方案。
简答:
作为目标预操作,执行一个脚本,将 CFBundleShortVersionString
和 CFBundleVersion
的相应值写入目标的 Info.plist
作为真实来源,我使用 Xcode 构建设置来读取所需目标的 MARKETING_VERSION
和 CURRENT_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
凭据行执行以下操作:
- 转到同步脚本所在的目录以执行它
- 允许在预操作期间写入日志,否则任何输出都默认静音
- 通过提供
MARKETING_VERSION
和 CURRENT_PROJECT_VERSION
来执行同步脚本
最后一步是编写您自己的同步脚本,将提供的 MARKETING_VERSION
和 CURRENT_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.plist
和 Settings.bundle
,所以我必须更新一次。
我还使用了一个通知服务扩展 BadgeCounter
,它必须具有完全相同的版本并构建为嵌入它的目标。所以我也更新这个。
从版本 11 开始,每当我在目标设置(选项卡 "General")。
我输入的实际版本和构建值现在存储在 project.pbxproj 文件中。我不希望或不喜欢这种行为,因为我使用 shell 脚本在构建时修改值。
我可以在 Info.plist 文件中手动设置正确的值,但是只要我在目标设置中更改版本号或内部版本号,Info.plist 文件就会再次更改 Xcode.
如何阻止 Xcode 11 这样做?
当我修改构建脚本以更改项目文件本身时,Xcode 将在项目文件更改后立即取消构建。
不要。
据推测,这种行为发生变化是有原因的。如果以后 Xcode 功能建立在这种行为之上,事情会变得越来越多 "constructed"。
与其尝试改变 Xcode,不如更改构建脚本检索这些值的方式:
如果您需要操作 project.pbxproj
文件,它是一个有据可查的 Next 样式 plist。您可以使用与此旧格式兼容的 plistbuddy
。如果您有更复杂的操作,您还可以使用 awk
和更多脚本。
如果我了解您的用例,您可以编写一个脚本,使用 awk
获取最高版本号,然后使用 sed
更新它可以在文件中找到的所有较低版本号。
到目前为止的路
我的用例是:
- 我正在跨多个目标同步版本和内部版本号。
- 我正在将版本和内部版本号与目标的
Settigns.bundle
同步
- 我正在从 CI 服务器读取和修改内部版本号。
我曾经将第 1 点和第 2 点作为目标构建脚本执行,第 3 点作为 CI 本身的自定义脚本执行。
在 Xcode 构建设置中存储版本和构建的新方法导致脚本出现问题,因为它们不再能够有效地修改值。至少阅读是可能的。
不幸的是,我无法找到阻止 Xcode 将版本和内部版本号存储到项目构建设置中的合法方法,但我已经设法创建了一个解决方法。
事实证明,在构建或归档时,使用了 Info.plist
中写入的值。这意味着该值在构建期间被替换,这不允许我们在同一构建期间修改它。
我也尝试过使用 xcodeproj
cli 修改项目,但是对项目的任何更改都会导致任何构建停止,因此此解决方案无效。
最终,在我尝试了很多不同的方法之后,我终于找到了一个不违反 Xcode 新行为的折衷方案。
简答:
作为目标预操作,执行一个脚本,将 CFBundleShortVersionString
和 CFBundleVersion
的相应值写入目标的 Info.plist
作为真实来源,我使用 Xcode 构建设置来读取所需目标的 MARKETING_VERSION
和 CURRENT_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
凭据行执行以下操作:
- 转到同步脚本所在的目录以执行它
- 允许在预操作期间写入日志,否则任何输出都默认静音
- 通过提供
MARKETING_VERSION
和CURRENT_PROJECT_VERSION
来执行同步脚本
最后一步是编写您自己的同步脚本,将提供的 MARKETING_VERSION
和 CURRENT_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.plist
和 Settings.bundle
,所以我必须更新一次。
我还使用了一个通知服务扩展 BadgeCounter
,它必须具有完全相同的版本并构建为嵌入它的目标。所以我也更新这个。