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

女生的网名这么多〃 提交于 2019-12-18 10:34:28

问题


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?


回答1:


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

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:

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.

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

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.txt to verify that the pre-action script has run and to see if you've used the correct path.




回答2:


I too have automated setting my version numbers. I created a Run Script Build 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.

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}"

FYI I 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];                           



回答3:


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

$ touch Info.plist



回答4:


One way to do it would be to have a Run Script build phase to generate Info.plist from an Info.plist.template file, and then rm Info.plist after creating the bundle. Idea from a comment Dave DeLong posted on his blog here.

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 rm the cached version as well in this script.




回答5:


In Xcode 4, you can use a pre build action to touch the info 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).

  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



回答6:


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.




回答7:


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.




回答8:


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.

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.)




回答9:


For those who have struggled to get a project to do automatic versioning, I created a macOS GitHub project that 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.

Here is a little guide of how you can replicate it in your projects (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 BuildNumber and don't forget to add this configuration file to all targets you 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.xcconfig show 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 Script phase 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:

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

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.

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

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:

NEW_VERSION=`cat "$SRCROOT/BuildNumber.xcconfig" | awk '/CURRENT_PROJECT_VERSION/ { print $3 + 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.




回答10:


Working with 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.

Solution 1: Most simple and elegant

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

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

touch $INFOPLIST_FILE

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

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

Solution 2: More complex and hacky

Another solution is to delete the cached Info.plist in the build dir.

I wrote this super simple small bash script

#!/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.

Background:

I have three different configurations: Config, Alpha, AppStore and 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.

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

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!

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

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

Discussion

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



来源:https://stackoverflow.com/questions/3730186/how-do-i-force-xcode-to-rebuild-the-info-plist-file-in-my-project-every-time-i-b

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!