Why don't I see a path to etags in PATH when running Emacs from http://emacsformacosx.com/ from the OS X dock?

This looks like an OS X bug coupled with different undefined behavior in Ruby and Emacs.

The root cause is that when launching Emacs from Finder, OS X is passing the PATH environment variable to the process twice! I wrote a test case and submitted it to Apple's bug reporter (id 19801095). Here's my test case:

#!/bin/bash

mkdir -p /tmp/test.app/Contents/MacOS/

cat > /tmp/test.app/Contents/MacOS/test <<EOF
#!/usr/bin/env ruby
\$stdout.reopen('/tmp/test.app.log', "w")
ENV.each_pair {|k,v| puts "#{k}=#{v}" if k == 'PATH' }
EOF
chmod +x /tmp/test.app/Contents/MacOS/test

launchctl setenv PATH "Extra PATH"
open -W /tmp/test.app
cat /tmp/test.app.log
launchctl unsetenv PATH

If you save that and then run it from the Terminal then it will print:

PATH=/usr/bin:/bin:/usr/sbin:/sbin
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
PATH=Extra PATH

This only happens on 10.10. 10.9 will only print one PATH.

So that is the root cause. What's actually happening? The Emacs from http://emacsformacosx.com uses a Ruby launcher script so that it can ship a binary that runs optimally on OS X installs from 10.6 to 10.10. This launcher script manipulates the PATH:

ENV['PATH'] += ':' + File.join(base_dir,     "bin-#{arch_version}") +
               ':' + File.join(base_dir, "libexec-#{arch_version}")

So ENV['PATH'] only manipulates the first instance of PATH in the list. When Emacs launches, it only pays attention to the last instance of PATH. Which on is right? Well, POSIX talks about this case in the spec:

If more than one string in an environment of a process has the same name, the consequences are undefined.

That means that neither of these programs are technically in the wrong.

Ok, so why does it behave differently from Terminal? That's because something is filtering out the duplicate PATHs in the environment when you are running from Terminal. I suspect bash. But it could also be Terminal.app. Either way, there's only one PATH in the environment and so the Ruby launcher and Emacs communicate correctly.

So what's the solution then? I think the Ruby launcher needs to change to deal with this, since it appears to be the default behavior in 10.10. That's a shame, since it's just more cruft. Luckily (as you can see from the above test script), Ruby can get to both PATHs so it can filter out everything but the last instance which would make it work the same way Emacs works.

Edit: There's now a bug report on the Emacs build project

Edit: This is now fixed. Nightly builds after February 11th and releases beginning with 24.5 should work (24.5 is currently a pretest but should be released shortly).