UPDATE
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
#!/bin/sh
PLIST="$HOME/.MacOSX/environment.plist"
SLEEP_TIME=10
# Uncomment following to echo launchctl commands to stdout
# Key StandardOutPath will have to be set in the plist file
# (au.id.pbw.plist2launchctl) for output to be captured.
#ECHO_TO_STDOUT=true
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 /
N
}
/^.*<string>.*[[:space:]].*<\/string>.*$/ {
d
}
/^(launchctl .*)\n[[:space:]]*<string>(.+)<\/string>[[:space:]]*$/ {
s//\1\2/
p
d
}'`
[[ -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"
Notes:
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.
<? xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>au.id.pbw.plist2launchctl</string>
<key>KeepAlive</key>
<false/>
<key>Program</key>
<string>/usr/libexec/plist2launchctl</string>
<key>RunAtLoad</key>
<true/>
<key>UserName<key>
<string>pbw</string>
</dict>
</plist>
Notes:
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.
<key>StandardOutPath</key>
<string>/Users/pbw/plist.log</string>
<key>Debug</key>
<true/>
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.