Generate Ctags Files for C/C++ Source Files and All of Their Included Header Files

Last updated on October 10, 2016

This post is for those people who use Exuberant Ctags. If you are using other versions of ctags, this post may not be useful.

When using ctags to generate the tags file for C/C++ projects, usually we use the following command:

ctags -R .

For some users that need more info of the symbols, they may use this command instead:

ctags -R --c++-kinds=+p --fields=+iaS --extra=+q .

No matter which one you use, the generated tags file only contains the symbols in the files in your project source tree, but not any external file, such as standard header files (e.g. stdio.h, stdlib.h), etc. thus editors or IDEs that use tags files, such as Vim, are not able to locate symbols in external header files. There was a solution: generate a tags file for any external header files first, and let the editor or IDE read both the generated tags file and the tags file for the project source tree. For example, the following command will generate a tags file for all your system header files on UNIX/Linux:

ctags -R --c++-kinds=+p --fields=+iaS --extra=+q /usr/include

This command usually takes a very long time to finish, and finally it gives a quite large tags file, which causes the editor or IDE a long time to search this tags file for symbols. To solve this problem, I came up with another idea.

Why must we generate a tags file containing all the symbols in the system header? If we only generate the tags file only for the header files that are related to our projects, would it be faster? That’s the point of this idea. We could first search for the header files that are included in our projects, and then we use ctags to generate a tags file for these files and our source files, in this way, a much smaller tags file that containing all the symbols that maybe useful for the project is generated.

To do this, I wrote a shell script ctags_with_dep.sh:

#!/bin/bash

# ./ctags_with_dep.sh file1.c file2.c ... to generate a tags file for these files.

gcc -M "$@" | sed -e 's/[\\ ]/\n/g' | \
        sed -e '/^$/d' -e '/\.o:[ \t]*$/d' | \
        ctags -L - --c++-kinds=+p --fields=+iaS --extra=+q

This script is also available on github gist. If you only want to use it, download the script and use the following command to generate the tags file:

./ctags_with_dep.sh file1.c file2.c file3.cpp ...

Read on if you want to know what’s happening here. This script will first use gcc -M to output the list of header files that are included in our C or C++ source files. However, the output could not be directly used by ctags, thus this script uses sed commands to filter the output. Finally, this script uses a pipe to put the file list to the stdin of the ctags program — ctags will read the file list from stdin if -L - is passed to it on the command line.

What if you have other directories besides the standard /usr/include that containing the header files you need? You could do a little modification on this script. For example, you have some header files in ~/include, then you could pass -I ~/include to the gcc command. Just like below:

gcc -M -I ~/include "$@" | sed -e 's/[\\ ]/\n/g' | \
        sed -e '/^$/d' -e '/\.o:[ \t]*$/d' | \
        ctags -L - --c++-kinds=+p --fields=+iaS --extra=+q

If you have any suggestion on this idea, please let me know.

18 thoughts on “Generate Ctags Files for C/C++ Source Files and All of Their Included Header Files

  1. Horses Mouth

    In my view you are using cscope and ctags better to use few key stroke as follow:

    find ../linux-3.13/ -name ‘*.h’ -o -name ‘*.c’ -o -name ‘Makefile’ -o -name ‘*.cpp’ -o -name ‘*.asm’ -o -name ‘*.S’ > cscope.files

    #cscope -b # this will gives cscope database cscope.out
    #ctags -L cscope.files

    At vim editor
    :set tags=/home/user/ctags

    Reply
  2. leon

    For me I found that for the sake of getting the script to run under windows/msys I had to change it to:
    gcc -M $* | sed -e ‘s/ /n/g’ | sed -e ‘/^[\]*$/d’ -e ‘/[.o]:/,+1d’ | sort -u | ctags -L – –c++-kinds=+p –fields=+iaS –extra=+q
    But thanks for your great work

    Reply
  3. Rahul Naskar

    At last I have found out how to generate tags for the emacs editor…

    g++ -M main.c | sed -e ‘s/[\ ]/n/g’ | sed -e ‘/^$/d’ -e ‘/.o:[ t]*$/d’ | xargs etags -R –declarations

    Versions used:

    OS: Ubuntu 12.04
    Linux chichen-itza 3.8.0-29-generic #42~precise1-Ubuntu SMP Wed Aug 14 16:19:23 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

    Etags:
    etags (GNU Emacs 24.3)

    Editor: Emacs 23.3
    GNU
    Emacs 23.3.1 (x86_64-pc-linux-gnu, GTK+ Version 2.24.10) of 2013-05-17 on panlong, modified by Debian

    –Wish this helps somebody!

    Reply
      1. Rahul Naskar

        Can you suggest what’s the best solution for integrating autocomplete for the mentioned configuration, please?

        BTW, Hong, I’m terribly confused about the number of makes and models floating around in the internet for emacs and ctags. I mean the combination of it.
        I think it would be nice if we can make a short piece, separating out the compatible ones.
        It might really help others, in this maze!

        Reply
  4. adrianratnapala

    Ha! thanks for that. I have stuck this into my current project’s Makefile. It is a big improvement on the one-big-tagsfile. To be precise, I have a rule:

    tags:
    ctags
    *.py
    $$(find -name ‘*.c’
    | xargs gcc -M $(CFLAGS)
    | sed
    -e ‘s/^.*://’
    -e ‘s/\//g’
    )

    Reply
  5. Lertsenem

    If your goal is to use ctags with vim (and I hope that’s not a really bold assumption), a cleaner way to achieve this is to generate the ctags associated with your headers once for all

    cd /usr/include

    ctags -R

    And then add this tags file while you are browsing your project with vim like this :

    :se tags+=/usr/include/tags

    Reply
    1. Hong Xu

      Yes, your way is also a solution. And I have given some explanations on why I would say my way is better than your solution in some cases in this post:

      There was a solution: generate a tags file for any external header
      files first, and let the editor or IDE read both the generated tags file and the
      tags file for the project source tree. For example, the following command will
      generate a tags file for all your system header files on UNIX/Linux:

      ctags -R –c++-kinds=+p –fields=+iaS –extra=+q /usr/include

      This command usually takes a very long time to finish, and finally it gives a
      quite large tags file, which causes the editor or IDE a long time to search this
      tags file for symbols. To solve this problem, I came up with another idea.

      Reply
    2. Mehrshad Khansarian

      Thank you very very much for your comment. Although the author is right as well, your method is just much less cumbersome. I tried it with Qt libraries.
      Just out of curiosity, I could not find what “:se” means. Could someone explain?

      Reply
      1. Lertsenem

        In the code you exposed, the dash in the ‘-R’ option is a long one.
        That is to say : ‘–’ is not the same character as ‘-‘. Thus the code line does not work when you copy/paste it in your shell.

        Reply

Leave a Reply

Your email address will not be published. Required fields are marked *