How to setup ctags for linux C development?

2.6k views Asked by At

I am getting into C development under Linux. However I'm struggling with correctly setting up ctags to work with vim. It correctly registers the tags file and offers completion based on it.

I'm not sure what is the proper way to get "necessary" includes. Currently I have some "hand-picked" headers:

AM_CTAGSFLAGS =             \
    --recurse=yes           \
    --tag-relative=yes      \
    --extras=*              \
    --fields=*              \
    --c-kinds=*             \
    --language-force=C      \
    /usr/include/bits       \
    /usr/include/fcntl.h    \
    /usr/include/stdio.h    \
    /usr/include/stdlib.h   \
    /usr/include/string.h   \
    /usr/include/sys        \
    /usr/include/unistd.h

In a first step, I just put same headers as I have #include ... but that missed some stuff (like /usr/include/bits/...). So I have added those.

But I feel like after all these years someone somewhere had to come up with better solution. So, how is this commonly done?

1

There are 1 answers

0
David Harrison On BEST ANSWER

That's an excellent question, graywolf!

I've been using this approach for years and here's how I use it:

  1. Generate a tags file for a logical component that your system uses. A logical component is typically what you may think of as an import in other languages (i.e. I'm using OpenGL or some sound library).
  2. For a given logical component, you may have to ignore some text in the header file. This is to keep the ctags program from misinterpreting certain constructs and thus misrepresenting those particular tags in the generated file (think preprocessor macros or compiler directives) or not even placing them into the output file at all.
  3. Modify your path Vim variable to point, in order, to the files that you have generated. I choose an innermost-out pattern. The project code itself first, libraries within the project next, followed by separate system libraries, followed lastly by a final tag file that represents "everything else".

I usually have a single tags file for the ANSI C headers (or C++ if you use that instead) appropriate for a particular standard (typically c99. c++11 if you use C++), which typically is my "everything else" tags file.

The hardest part is feeding in the ignore list to ctags. Since you are using C, I'll assume you use the C headers from the system (just the compiler-standard ones that should be available with the language). I use the following ignore list for when I generate these (on Ubuntu 14.04):

__attribute__
__attribute_deprecated__
__attribute_format_arg__+
__attribute_format_strfmon__+
__attribute_malloc__
__attribute_noinline__
__attribute_pure__
__attribute_used__
__attribute_warn_unused_result__
__attribute_alloc_size__+
__attribute_const__
__attribute_artificial__
__wur
__THROW
__THROWNL
__BEGIN_DECLS
__END_DECLS
__BEGIN_NAMESPACE_STD
__END_NAMESPACE_STD
__USING_NAMESPACE_STD+
__BEGIN_NAMESPACE_C99
__END_NAMESPACE_C99
__USING_NAMESPACE_C99+
__warndecl+
__warnattr+
__errordecl+
__flexarr=[]
__fortify_function
__REDICRECT+
__REDIRECT_NTH+
__REDIRECT_NTHNL+
__ASMNAME+
__ASMNAME2+
__nonnull+
__always_inline
__extern_inline=extern
__extern_always_inline=extern
__extension__
__restrict
__restrict_arr

This is the most important part. If you don't see much output for headers that you've run ctags on, that's probably what's going on. It drove me nuts until I figured this out. Those keep ctags from being fooled.

As far as the headers input into ctags, you have the right idea. Don't forget the compiler-specific system headers that are usually located somewhere else like /usr/include/x86_64-linux-gnu. Those will help you drill down to your system's constants if necessary.