Run Script Phase & Environment Variables

Is there still no (in whatever creative) way to set Xcode's own environment variables from a "Run Script Phase" script?


As you might know, the problem with "Run Script" build phase scripts always was that those scripts are forked as a separate process by Xcode. So a script inherits downwards Xcode's environment variables, but any changes made to those environment variables by scripts will not propagate back to Xcode due to that.


Is there maybe in the meantime a way to force scripts being run directly within Xcode's build process instead of being forked? Or is there some RPC that would allow to modify Xcode's environment variables by script?

Post not yet marked as solved Up vote post of Crudebyte Down vote post of Crudebyte
12k views

Replies

What are you trying to accomplish?

Well, it's about customizing the build process in general. There are various use cases for this. For instance encryption keys which should not be part of source files, or automatically adjusting relative framework pathes (e.g. due to differences in teams). Another very typical use case is dynamically filling Info.plist fields by Xcode scripts.


So let's just stick with the latter use case: Say you wanted to automatically compile an Xcode project using always latest git HEAD commit's SHA1 as user visible build number for the generated app binary. If there was a way to let a "Run Script" build phase script adjust Xcode's environment, then all you had to do was adding a tiny script like this to the Xcode project "Run Script" build phase:


FOO_GIT_REV=`(cd $SRCROOT && git rev-parse --short=4 --verify HEAD)`
export FOO_GIT_REV


And in the Xcode target's Info.plist you would simply use that environment variable like:


<key>CFBundleVersion</key>
<string>$(FOO_GIT_REV)</string>


and that's it.


Of course there are workarounds for this example. Typically people use something like this instead in an Xcode "Run Script" build phase script:


system("/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion ${FOO_GIT_REV}\" \"${INFOPLIST_FILE}\"");


So you extract the git commit hash and write it literally to the Info.plist file instead within the same Xcode script. But the problem with this approach is that the script is constantly modifying Info.plist itself of course, so you are mixing a file being under version control (Info.plist) with dynamically generated content by a build script. Hence you always have that Info.plist on your dirty list e.g. with "git status" and you cannot git ignore it, since the rest of the Info.plist file is still relevant for version control of course.


If there was some way to manipulate Xcode's environment variables by script, that would convey many build configuration tasks in an easy and convenient way. Because you can actually reference environment variables almost everywhere in Xcode already.

I just filed a feature request for this (FB7399724), since I assume there is still no way in Xcode for "Run Script" phase scripts to modify Xcode's environment variables.


My suggestion was to add a new editable list "Output Environment Variables" to the "Run Script" build phase pane, similar to the existing editable lists there ("Input Files", "Output Files", ...). So once a script ended execution, Xcode would read the current value of the listed environment variables from the script's process and apply them globally to Xcode's environment.

Why not just undo your hack in another run script at the end? You can also add pre and post run scripts to your scheme, which might push it further out off Xcode's core build sequence.

Not a bad idea actually. But it's still an unnecessarily too complicated hack for a hack, and dealing directly (by parsing & modifying) with the files is error prone and unclean. Think about the other example: auto adjusting pathes in your Xcode project. Depending on the case you might need some more thorough custom parsing/adjusting (e.g. with regexs) to potentially merge with other things there.


Anyway, I still hope this issue will be fixed at its root by simply allowing to adjust/add Xcode environment variables by build phase scripts. It is much cleaner, simpler and would also avoid confusions by members of your team why some certain project setting has just changed "magically". ;-) I mean if you reference a variable e.g. in the project file or any .plist file, then any developer clearly really sees there is a variable. Period. And on doubt (s)he can simply grep for where exactly this variable is filled.

What I do is to create an Info.template.plist file, which is under version control.


In the run script phase, I copy it, and modify the resulting Info.plist, that is not under version control.


This of course remains an evil hack, and exporting variables from the run script phase would be preferable


(And, if you are reading this, this approach has other problems, especially when fastlane depends on that Info.plist …)

Since I last replied to this thread, I've found a better way. You can override environment variables using custom parameters In the project. Those are static overrides. If you need dynamic overrides, you can run xcbuild from the command line. Maybe you could do it all in the interface with aggregate builds, but I haven't tried that.

But lets say that you want to have your own environments defined for Development vs Production, using xcconfig files is the best way to do this. But, lets say that you want to make a Production archive - and you need to switch the default value of CURRENT_ENVIRONMENT from Development - this can only be done via the command line arguments.

What would be nice, would be able to create a schema for Production which sets this environment in a pre-build run script phase. Then it would be possible to do an archive from within the Xcode UI.