3

When I try to generate a TAGS file, emacs executes /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ctags, which causes problems because it is not compatible with the settings emacs uses.

I figured out how to work around the issue by modifying exec-path, but I'm still confused about why it found that path in the first place. The Xcode path was not part of exec-path prior to my modification. The Xcode path is not included in $PATH at any time that I'm aware of. As far as I can tell there aren't any symlinks involved.

I'm running Emacs 24.5 on Mac OS 10.10.3.

  • I don't know, but suspect that /usr/bin/ctags is nothing but a wrapper to run the one inside the Xcode bundle. One reason I believe this, is the fact that a great many developer oriented programs in /usr/bin are exactly the same length: 14160 bytes. (Addendum: execsnoop disagrees, but maybe Apple has ways to fool dtrace sometimes.) – Harald Hanche-Olsen Aug 16 '15 at 8:25
  • You can tell which ctags you're using by passing it the --version argument. The GNU Emacs versions says so, as does Exuberant Ctags (which I invariably use instead). I imagine whatever Xcode is using would provide some similar output. – phils Sep 16 '15 at 6:17
0

By default, Emacs will install a command line program etags which could be used by manually running cli find -name *.c | etags -.

See http://www.gnu.org/software/emacs/manual/html_node/emacs/Create-Tags-Table.html#Create-Tags-Table

So it has nothing to do with Emacs in this case. It's XCode installer who changed the link destination of "/usr/bin/ctags".

Solution 1: Change the link back by sudo ln -s /where-the-ctags-exists/ctags /usr/bin/ctags.

Solution 2: Modify the environment variable PATH instead. Insert below code into ~/.bash_profile,

export path=/where-the-ctags-exists:$PATH

  • I agree it has nothing to do with Emacs in this case. After reading Harald's comment to my question, and doing some experiments, it is clear that /usr/bin/ctags is executing the one in the Xcode bundle. However, I don't understand what you mean by link, or how to change it back. ls -l /usr/bin/ctags returns -rwxr-xr-x 1 root wheel 14160 Sep 29 2014 /usr/bin/ctags. As far as I can tell, it's not a link. How would I change it back? – Jay Aug 17 '15 at 14:24
  • What emacs add on u use – chen bin Aug 17 '15 at 22:34
  • 2
    (So this popped to the top, I suppose because no answer has been accepted.) You can't just go and change stuff in /usr/bin on recent versions of OS X, because of system integrity protection. In any case, a better way to override an OS supplied binary is to put overriding binaries in a different directory and then adding that to the front of the path. – Harald Hanche-Olsen Jun 12 '16 at 7:50
  • Is there some variable that can be used to specify what command is executed when Emacs runs ctags? If so, it should be easy to customize it so it points to the correct executable. – DoMiNeLa10 Sep 10 '16 at 21:27
  • @Jay, you can sudo rm /usr/bin/ctags before `sudo ln -s /path/of/latest/ctags /usr/bin/ctags. – chen bin Sep 11 '16 at 23:32
0

Emacs has builtin etags ant its portable, on MacOS there are a default one: ctags at /usr/bin/ctags (but it is not exuberant). Emacs accepts etags generated TAG file, on other side ctags exuberant or not, you can add -e option to let ctags to generate the TAG file that Emacs can accept.

When the Emacs accepted TAG file had been generated, you can add it to Emacs' tags-table-list, not the exec-path:

(add-to-list 'tags-table-list tags-dir t #'string=)

tags-table-list is a variable defined in ‘etags.el’.

Its value is ("~/.emacs.d/.tags/home/g_25.3.1/" "~/.emacs.d/.tags/source/g_25.3.1/" "~/.emacs.d/.tags/c/g_25.3.1/" "~/.emacs.d/.tags/os/g_25.3.1/")

Documentation: List of file names of tags tables to search. An element that is a directory means the file "TAGS" in that directory. To switch to a new list of tags tables, setting this variable is sufficient. If you set this variable, do not also set ‘tags-file-name’. Use the ‘etags’ program to make a tags table file.

There are a working example at more reasonable Emacs

(defun make-tags (home tags-file file-filter dir-filter &optional renew)
    "Make tags."
    (when (file-exists-p home)
        (let ((tags-dir (file-name-directory tags-file)))
            (if (file-exists-p tags-file)
                    (when renew (delete-file tags-file))
                (when (not (file-exists-p tags-dir))
                    (make-directory tags-dir t)))
            (dir-iterate home
                                     file-filter
                                     dir-filter
                                     (lambda (f)
                                         (eshell-command
                                            (format "etags -o %s -l auto -a %s ; echo %s"
                                                            tags-file f f))))
            (when (file-exists-p tags-file)
                (add-to-list 'tags-table-list tags-dir t #'string=)))))


(defun make-emacs-home-tags (tags-file &optional renew)
    "Make TAGS-FILE for Emacs' home directory."
    (let ((lisp-ff (lambda (f _) (string-match "\\\.el$" f)))
                (home-df
                 (lambda (d _)
                     (not
                        (string-match
                         "^\\\..*/$\\|^theme/$\\|^g_.*/$\\|^t_.*/$\\|^private/$" d)))))
        (make-tags emacs-home tags-file lisp-ff home-df renew)))

make-tags function need dir-iterate function to iterate directories:

(defun dir-iterate (dir ff df fn)
    "Iterating DIR, if FILE-FILTER return T then call FN, 
and if DIR-FILTER return T then iterate into deeper DIR.

     (defun FILE-FILTER (file-name absolute-name))
     (defun DIR-FILTER (dir-name absolute-name))

\(FN DIR FILE-FILTER DIR-FILTER FN\)"
    (dolist (f (file-name-all-completions "" dir))
        (unless (member f '("./" "../"))
            (let ((a (expand-file-name f dir)))
                (if (directory-name-p f)
                        (when (and df (funcall df f a)
                                             (dir-iterate a ff df fn)))
                    (when (and ff (funcall ff f a)
                                         (funcall fn a))))))))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.