Using environment.plist with Mountain Lion

This post is now obsolete. For the preferred method in both Mountain Lion and Mavericks, see  Setting environment variables in OS X Mountain Lion and Mavericks.

With Mountain Lion (OS X 10.8), the environment settings from ~/.MacOSX/environment.plist are not taken into account when the background system environment is set up by launchd, the OS X initialisation process. Consequently, if you want settings like $JAVA_HOME to be available to Java applications you start from Finder, you must find an alternative for setting them.
The beauty of the environment.plist method was that, by setting the variables in the plist from within your .profile, you could keep the shell and system environments in sync. There are obvious advantages in this synchronisation. If you are like me, you have gone to a lot of trouble to put such synchronisation in place. See my previous post, Setting Environment Variables in OS X Lion.
The now-recommended method of system variable setting is to use the setenv subcommand of launchctl, like so:
launchctl setenv M2 /usr/share/maven/bin
Such commands can be saved in the file /etc/launchd.conf, in which case only the subcommand is saved:
setenv M2 /usr/share/maven/bin
You can check the state of the launchd environment with the subcommand export:
launchctl export
This will output a series of shell commands of the form
M2=" /usr/share/maven/bin"; export M2;
This serves as a test for any attempts to set the launchd environment variables.
There are three problems with using launchctl in this way. Firstly, /etc/launchd.conf is a system file (when it exists, which it does not by default), and it is in a directory (/etc) which is writable only by root. Yes, you can sudo to edit the file, but then you face the second problem, touched on above. You have two discrete sources for environment variable settings, which can easily get out of sync. Lastly, the strings defined in setenv statements like the above, must be string literals. There is no means of resolving variable content based on other variables. For example, you cannot define
setenv TMPDIR $HOME/tmp
The problem, then, is to find a way to dynamically set the launchd environment, based on your existing .profile and .MacOSX/environment.plist settings. Another bonus from this approach is that, within you .profile, you can use all of the shell facilities to define the content of variables.
When the system starts, the launchd process finds system daemon processes to launch from /System/Library/LaunchDaemons & /Library/LaunchDaemons. When a user logs in, launchd looks for user agents to start up in /System/Library/LaunchAgents, /Library/LaunchAgents & ~user/Library/LaunchAgents. While the first two are system-owned directories, the third is owned by the individual user.
We need two files: 1) a shell file which will actually issue the launchctl setenv commands, and 2) a plist file that will tell launchd where to find the executable, and how to run it. The shell file is available here; the plist file is available here.

The executable: plist2launchctl


# Uncomment following to echo launchctl commands to stdout
# Key StandardOutPath will have to be set in the plist file
# ( for output to be captured.

one_cmd () {
    eval "$@"

[[ -e "$SHFILE" ]] && {
    rm "$SHFILE"
    [[ $? -eq 1 ]] && {
        echo "$SHFILE" cannot be removed. >&2
        exit 1

cmd_list=`cat "$PLIST" | \
sed -En \
'/^[[:space:]]*<key>(.*)<\/key>[[:space:]]*/ {
s//launchctl setenv \1 /
/^.*<string>.*[[:space:]].*<\/string>.*$/ {
/^(launchctl .*)\n[[:space:]]*<string>(.+)<\/string>[[:space:]]*$/ {

[[ -n "$ECHO_TO_STDOUT" ]] && echo "$cmd_list"

echo "$cmd_list" | while read line; do one_cmd $line; done

[[ -n "$ECHO_TO_STDOUT" ]] && echo Sleeping in plist2launchctl

# Sleep for a while so launchd doesn't get upset
[[ -n "$SLEEP_TIME" ]] && sleep "$SLEEP_TIME"

The output of the whole of the cat|sed pipeline is captured in the cmd_list variable by enclosing the lot in backquotes. The sed command is itself enclosed in single quotes. It simply reads the contents of environment.plist line at a time, and merges key/string pairs into the corresponding launchctl setenv commands, storing them in cmd_list. cmd_list is then itself read line at a time, and each line is handed to eval for execution.
Permissions & Location:
To stay on the safe side, give the file -rwxr-xr-x permissions. It should be placed in /usr/libexec, which is root owned. You will have to use sudo to copy it.

The plist file:

<? xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "">
<plist version="1.0">

The UserName field will, of course, be set to your own login name.
If you need to debug the plist file, add the following lines.
These lines are used in conjunction with the ECHO_TO_STDOUT variable in the executable file. If used, the StandardOutPath will point to a writable file on your system.
Permissions & Location:
Again, give the file -rwxr-xr-x permissions. Place it in your $HOME/Library/LaunchAgents directory.