每次构建项目时,如何强制 Xcode 重新构建项目中的 Info.plist 文件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3730186/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 03:33:41  来源:igfitidea点击:

How do I force Xcode to rebuild the Info.plist file in my project every time I build the project?

xcodexcodebuild

提问by TraxusIV

Apparently the file gets cached, so it only gets built when it gets changed. I have environment variables set to increment my version number though, and such, and update them independently of the plist (in the project build settings, actually). Is there a script that I can use as a script build phase that will force the Info.plist to update? Some other convenient way?

显然文件被缓存了,所以只有在它被改变时才会被构建。我将环境变量设置为增加我的版本号,等等,并独立于 plist 更新它们(实际上是在项目构建设置中)。是否有一个脚本可以用作脚本构建阶段来强制 Info.plist 更新?还有什么方便的方法?

采纳答案by LeeH

Select 'Edit Scheme', then select 'Build' from the control on the left.

选择“Edit Scheme”,然后从左侧的控件中选择“Build”。

Then, add a Pre-actions step, ensure the scheme in question is selected in the 'Provide build settings from' pulldown and then add this into the edit window below:

然后,添加一个 Pre-actions 步骤,确保在“Provide build settings from”下拉菜单中选择了有问题的方案,然后将其添加到下面的编辑窗口中:

rm "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}"

This will delete the cached version of Info.plist, causing XCode to rebuild it each time you hit build.

这将删除 的缓存版本Info.plist,导致每次点击构建时 XCode 都会重建它。

Alternatively, if you let Xcode preprocess a template Info.plist file, simply touching the template with

或者,如果您让 Xcode 预处理模板 Info.plist 文件,只需使用

touch ${PROJECT_DIR}/${INFOPLIST_FILE}

works too.

也有效。

Debugging pre-action scripts

调试预操作脚本

The pre-actions steps do not provide any information when you have made a mistake. You can debug the use of your build variables by adding this line to the script

当您犯错时,预操作步骤不会提供任何信息。您可以通过将此行添加到脚本来调试构建变量的使用

echo "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" > ~/debug.txt

Then check the content of ~/debug.txtto verify that the pre-action script has run and to see if you've used the correct path.

然后检查 的内容~/debug.txt以验证预操作脚本是否已运行并查看您是否使用了正确的路径。

回答by GayleDDS

I too have automated setting my version numbers. I created a Run ScriptBuild Phase. The key is to update the target build directory copy of the Info.plist not build directory one. You also need to have your run script after the copy bundle phase. It ok to edit the bundle file directly because is before code signing. You don't want to generate the file thats reinventing the wheel.

我也有自动设置我的版本号。我创建了一个运行脚本构建阶段。关键是更新Info.plist的目标构建目录副本而不是构建目录一。在复制捆绑阶段之后,您还需要拥有运行脚本。可以直接编辑捆绑文件,因为是在代码签名之前。您不想生成重新发明轮子的文件。

Here's my script:

这是我的脚本:

# ---------------------------- IMPORTANT ----------------------------
# You must set GITHash to something like 'Set by build script' in the file
# file '<Project Name>-Info.plist' in the 'Supporting Files' group
# -------------------------------------------------------------------
#
# Get the version number from the tag in git and the number of commits as the build number
#
appVersion=$(git describe --long | cut -f 1 -d "-") 
appBuild=$(git describe --long | cut -f 2 -d "-") 
gitHash=$(git describe --long | cut -f 3 -d "-")
echo "From GIT Version = $appVersion Build = $appBuild"

#
# Set the version info in plist file
#
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $appVersion" "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $appBuild" "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
/usr/libexec/PlistBuddy -c "Set :GITHash $gitHash" "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
echo "Updated ${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"

FYII also auto set the version and build on the About tab with the following code

仅供参考,我还使用以下代码自动设置版本并在“关于”选项卡上构建

NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
NSString *appDisplayName = infoDictionary[@"CFBundleDisplayName"];
NSString *majorVersion = infoDictionary[@"CFBundleShortVersionString"];
NSString *minorVersion = infoDictionary[@"CFBundleVersion"];

self.appDescription.text = [NSString stringWithFormat:@"Dirty Dog Software\n%@\nVersion: %@(%@)",
                       appDisplayName, majorVersion, minorVersion];                           

回答by bosmacs

You could try using the touchcommand to update the timestamp, which I assume is what Xcode uses to determine whether it should be rebuilt, e.g.

您可以尝试使用该touch命令来更新时间戳,我认为这是 Xcode 用来确定是否应该重建的,例如

$ touch Info.plist

回答by bosmacs

One way to do it would be to have a Run Script build phase to generate Info.plistfrom an Info.plist.templatefile, and then rm Info.plistafter creating the bundle. Idea from a comment Dave DeLongposted on his blog here.

一种方法是让运行脚本构建阶段Info.plistInfo.plist.template文件生成,然后rm Info.plist在创建包之后。从评论的想法戴夫·德隆贴在他的博客在这里

If it's caching pretty severely (i.e. caching even if you don't have the Info.plist open in File History) you might want to rmthe cached version as well in this script.

如果缓存非常严重(即,即使您没有在文件历史记录中打开 Info.plist 也rm缓存),您可能还希望在此脚本中缓存版本。

回答by lukelutman

In Xcode 4, you can use a pre build action to touch the info plist.

在 Xcode 4 中,您可以使用预构建操作来触摸信息 plist。

Note: This is not the same as a run script build phase. A pre build action happens before Xcode checks the info plist, a run script build phase seems to happen after Xcode has already checked the info plist (which is why it only works every other time).

注意:这与运行脚本构建阶段不同。预构建操作发生在 Xcode 检查 info plist 之前,运行脚本构建阶段似乎发生在 Xcode 已经检查过 info plist 之后(这就是它只每隔一次工作的原因)。

  1. Go to Edit Scheme > Build > Pre-actions
  2. Click the "+" button and choose "New Run Script Action"
  3. Choose your target in the "Provide build settings from" dropdown
  4. Add touch "${SRCROOT}/${INFOPLIST_FILE}"in the script box
  1. 转到编辑方案 > 构建 > 预操作
  2. 单击“+”按钮并选择“新建运行脚本操作”
  3. 在“提供构建设置来自”下拉菜单中选择您的目标
  4. touch "${SRCROOT}/${INFOPLIST_FILE}"在脚本框中添加

回答by ExitToShell

There is an option in the "Build" tab of the target's Get Info window, down in "Packaging" labeled "Preprocess Info.plist file" that you can check. I believe that will update the file every build.

目标的“获取信息”窗口的“构建”选项卡中有一个选项,位于标有“预处理 Info.plist 文件”的“打包”下方,您可以检查。我相信每次构建都会更新文件。

回答by Andrew

Make sure you also refresh your view of the plist.
When viewing the plist in Xcode just click the disclosure triangle next to the root element in the plist (Information Property List). And then click it again to expand it.
You'll see that the values have been refreshed. I was running a similar script and thought it was working only intermittently until I realized that the plist view was simply not refreshing.

确保您还刷新了 plist 的视图。
在 Xcode 中查看 plist 时,只需单击 plist ( Information Property List) 中根元素旁边的显示三角形。然后再次单击它以展开它。
您将看到值已刷新。我正在运行一个类似的脚本,并认为它只是间歇性地工作,直到我意识到 plist 视图根本没有刷新。

回答by Ken Thomases

Another approach is to create an "aggregate" target which only has a build phase. Use that to touch the Info.plist file. Make that target a dependency of the target which builds your app.

另一种方法是创建一个只有构建阶段的“聚合”目标。使用它来触摸 Info.plist 文件。使该目标成为构建您的应用程序的目标的依赖项。

Because it's a separate target and a dependency of your app target, it will be built before your app target's Info.plist files are even checked. (As noted elsewhere, build phases of the app target itself happen too late, after the modification time of the Info.plist files has already been checked.)

因为它是一个单独的目标并且是您的应用程序目标的依赖项,所以它将在您的应用程序目标的 Info.plist 文件甚至被检查之前构建。(正如其他地方所指出的,在 Info.plist 文件的修改时间已经被检查之后,应用程序目标本身的构建阶段发生得太晚了。)

回答by jvarela

For those who have struggled to get a project to do automatic versioning, I created a macOS GitHub projectthat demonstrates how to implement it in a very easy, straightforward way, thanks to the ideas of @GayleDDS and Daniel Farrelly and his blog about versioning and some implementation of my own. I think you can translate it into your projects on other platforms without any hassle.

对于那些努力让项目进行自动版本控制的人,我创建了一个macOS GitHub 项目,该项目演示了如何以非常简单、直接的方式实现它,这要感谢@GayleDDS 和 Daniel Farrelly 的想法以及他关于版本控制和我自己的一些实现。我认为你可以毫不费力地将它翻译成你在其他平台上的项目。

Here is a little guide of how you can replicate it in your projects (Xcode 8).

这是一个关于如何在项目中复制它的小指南(Xcode 8)。

  1. To decrease the chances of you getting this wrong, please ensure that you already added all your targets to your project beforehand.
  2. Go to the menu File > New File...and then scroll down and choose Configuration Settings File
  3. Name it BuildNumberand don't forget to add this configuration file to all targetsyou want to keep in sync; do so by clicking on the respective checkboxes before saving this file
  4. You should see a file called BuildNumber.xcconfigshow up in your project
  5. Add to this file the following text: CURRENT_PROJECT_VERSION = 1. If you want, you can start with another number. This is going to be your build number that will be increased every time you build. Please do not the change the name of the variable, though.
  6. If you have more than one target, add the following script to the first target that is built by navigating to Project > Target > Build Phases
  7. Navigate to the menu Editor > Add Build Phase > Add Run Script Phase
  8. A Run Scriptphase should have been added to your target; so drag it until is under Target Dependencies(this detail is important!).
  9. Now it comes the interesting part and I will explain the script almost line by line:
  1. 为了减少您出错的机会,请确保您事先已将所有目标添加到您的项目中。
  2. 转到菜单File > New File...然后向下滚动并选择Configuration Settings File
  3. 命名BuildNumber并不要忘记将此配置文件添加到您要保持同步的所有目标;通过在保存此文件之前单击相应的复选框来执行此操作
  4. 您应该BuildNumber.xcconfig在项目中看到一个名为show up的文件
  5. 将以下文本添加到此文件中:CURRENT_PROJECT_VERSION = 1. 如果需要,您可以从另一个号码开始。这将是您每次构建时都会增加的构建号。但是请不要更改变量的名称
  6. 如果您有多个目标,请将以下脚本添加到通过导航到Project > Target > Build Phases 构建的第一个目标
  7. 导航到菜单Editor > Add Build Phase > Add Run Script Phase
  8. 一个运行脚本阶段应该已经添加到你的目标中;所以拖动它直到在目标依赖项下(这个细节很重要!)。
  9. 现在是有趣的部分,我将几乎逐行解释脚本:

Start by creating a path to PlistBuddy in the first line of the script:

首先在脚本的第一行创建 PlistBuddy 的路径:

plistbuddy="/usr/libexec/PlistBuddy"

Add line 2 to read the config file and then read the CURRENT_PROJECT_VERSION variable you set up in step 5.

添加第 2 行以读取配置文件,然后读取您在步骤 5 中设置的 CURRENT_PROJECT_VERSION 变量。

OLD_VERSION=`cat "$SRCROOT/BuildNumber.xcconfig" | awk '/CURRENT_PROJECT_VERSION/ { print  }'`

Line 3-11 set up NEW_VERSION with the bumped up value, then save the config file with the new build number; the remaining lines are to save the marketing version and the build number to the plist:

第 3-11 行使用增加的值设置 NEW_VERSION,然后使用新的内部版本号保存配置文件;剩下的几行是将营销版本和内部版本号保存到 plist:

NEW_VERSION=`cat "$SRCROOT/BuildNumber.xcconfig" | awk '/CURRENT_PROJECT_VERSION/ { print  + 1 }'`
sed -i '' "s/CURRENT_PROJECT_VERSION = .*/CURRENT_PROJECT_VERSION = $NEW_VERSION/" "$SRCROOT/BuildNumber.xcconfig"
CURRENT_PROJECT_VERSION=$NEW_VERSION
BUILD_STR="Build $NEW_VERSION"
COPYRIGHT_STR="? 2017 MyCompany.net"
APP_VERSION_STR="1.3.5"
$plistbuddy -c "Set :CFBundleShortVersionString $APP_VERSION_STR" "${SRCROOT}/$TARGETNAME/Info.plist"
$plistbuddy -c "Set :CFBundleVersion $BUILD_STR" "${SRCROOT}/$EXECUTABLE_NAME/Info.plist"
$plistbuddy -c "Set :NSHumanReadableCopyright $COPYRIGHT_STR" "$INFOPLIST_FILE"

I also show in lines 8-11 how to use different environmental variables that Xcode sets up for its scripts. You can add additional lines to edit the info.plist for other targets and then keep the version of your main app and its helpers in sync. Go to GitHub, download the project and play with it and then adapt it to your needs. Happy automatic versioning with Xcode.

我还在第 8-11 行展示了如何使用 Xcode 为其脚本设置的不同环境变量。您可以添加额外的行来编辑其他目标的 info.plist,然后保持主应用程序及其助手的版本同步。转到GitHub,下载项目并使用它,然后根据您的需要进行调整。使用 Xcode 进行愉快的自动版本控制。

回答by Sajjon

Working with Xcode 8.3.2

使用 Xcode 8.3.2

Inspired by the answer by @LeeH from 2013 I have come up with two solutions for me to rebuild the Info.plist.

受到@LeeH 在 2013 年的回答的启发,我想出了两个解决方案来重建 Info.plist。

Solution 1: Most simple and elegant

方案一:最简单优雅

The most simple solution is to force Xcode to read your custom variables by touchingyour targets Info.plist.

最简单的解决方案是通过触摸您的目标来强制 Xcode 读取您的自定义变量Info.plist

Add a build phase, see screenshot below (originally screenshotted for Solution 2), and just add this line:

添加一个构建阶段,见下面的截图(最初为解决方案 2截图),然后添加这一行:

touch $INFOPLIST_FILE

That is all! Now Xcode should force reload your custom variables into the Info.plistfile.

就这些!现在 Xcode 应该强制将您的自定义变量重新加载到Info.plist文件中。

This solution is the same as @lukelutman suggested, but using Build phase. Pre-action script did not work for me.

此解决方案与@lukelutman 建议的相同,但使用构建阶段。动作前脚本对我不起作用。

Solution 2: More complex and hacky

解决方案 2:更复杂和 hacky

Another solution is to delete the cachedInfo.plistin the build dir.

另一种解决方案是删除构建目录中缓存Info.plist

I wrote this super simple small bash script

我写了这个超级简单的小型 bash 脚本

#!/bin/bash
info_plist="$CONFIGURATION_BUILD_DIR/$PRODUCT_NAME.app/Info.plist"
echo "Removing Info.plist from build dir in order to force rebuild of it and reading of correct xcconfig variables, plist path $info_plist"
rm "$info_plist"

I then saved it and called it from build phases for the target. I put it as the first build phase.

然后我保存它并从目标的构建阶段调用它。我把它作为第一个构建阶段。

enter image description here

enter image description here

Background:

背景:

I have three different configurations: Config, Alpha, AppStoreand I am using Universal Links, Push Notifications and other stuff that requires the use of an Entitlements file. But I did not want to have three entitlement files, one for each config.

我有三种不同的配置:ConfigAlphaAppStore和我正在使用通用链接、推送通知和其他需要使用权利文件的东西。但我不想拥有三个授权文件,每个配置一个。

My project already relies heavily on config files (.xcconfig). I actually set up the Entitlement file (MyAppsProductName.entitlements) using custom config variables.

我的项目已经严重依赖配置文件 ( .xcconfig)。我实际上MyAppsProductName.entitlements使用自定义配置变量设置了权利文件 ( )。

But I wanted to read the same config variables runtime, I figured that I could do it if they would be added to my targets Info.plist. Which worked!

但是我想读取相同的配置变量运行时,我想如果将它们添加到我的目标 Info.plist 中我可以做到。哪个有效!

But I noticed that when I changed the values in my .xcconfigfile, then the Info.plistfile did not change value. I noticed that if I performed a clean build, then the values in the Info.plistgot updates according to the values inside the .xcconfigfile. Xcode indeed caches the Info.plistfile.

但是我注意到当我更改.xcconfig文件中的值时,Info.plist文件并没有更改值。我注意到,如果我执行了一个干净的构建,那么Info.plist根据.xcconfig文件中的值得到更新的值。Xcode 确实缓存了Info.plist文件。

So these solutions above fixes this problem. Hope it helps! :)

所以上面的这些解决方案解决了这个问题。希望能帮助到你!:)

Discussion

讨论

I have no idea if Solution 2has any advantage over Solution 1... Probably not? Any input anyone?

我不知道解决方案 2是否比解决方案 1有任何优势......可能没有?任何输入任何人?