gnu-global

本文引用自 http://forum.ubuntu.org.cn/viewtopic.php?f=68&t=343460 

http://blog.chinaunix.net/uid-20416834-id-120183.html 感谢作者们

 

gnu global是一个类似cscope的工具,也能提供源文件之间的交叉索引。

其独到之处在于,当生成索引文件以后,再修改整个项目里的一个文件,然后增量索引的过程非常快。

安装过程省略。

安装好以后,有global、gtags、gtags-cscope三个命令。global是查询,gtags是生成索引文件,gtags-cscope是与cscope一样的界面。

example:

代码:
$ cd project/
$ gtags


这样就生成了整个目录的索引文件,包括GTAGS、GRTAGS、GPATH三个文件。

你也可以先用find命令生成一个文件列表,叫gtags.files,然后再执行gtags,就会只索引gtags.files里的文件。

example:
代码:
$ cd project/
$ find . -name "*.[ch]" > gtags.files
$ gtags


查询使用的命令是global和gtags-cscope。前者是命令行界面,后者是与cscope兼容的ncurses界面。这里就不多介绍了,重点是如何在vim里查询:

首先进入vim,然后:

example
代码:
:set cscopeprg=gtags-cscope
:cs add GTAGS


然后就可以像cscope一样,用cs find g等命令进行查询了。

当我们更改了某个文件以后,比如project/subdir1/subdir2/file1.c,想更新索引文件(索引文件是project/GTAGS),只需这样:

example:
代码:
$ cd project/subdir1/subdir2/
$ vim file1.c
$ global -u


global -u这个命令会自动向上找到project/GTAGS,并更新其内容。而gtags相对于cscope的优势就在这里:增量更新单个文件的速度极快,几乎是瞬间完成。有了这个优势,我们就可以增加一个autocmd,每次:w的时候自动更新索引文件。

我的设置如下:
代码:
" settings of cscope.
" I use GNU global instead cscope because global is faster.
set cscopetag
set cscopeprg=gtags-cscope
set cscopequickfix=c-,d-,e-,f-,g0,i-,s-,t-
nmap <silent> <leader>j <ESC>:cstag <c-r><c-w><CR>
nmap <silent> <leader>g <ESC>:lcs f c <c-r><c-w><cr>:lw<cr>
nmap <silent> <leader>s <ESC>:lcs f s <c-r><c-w><cr>:lw<cr>
command! -nargs=+ -complete=dir FindFiles :call FindFiles(<f-args>)
au VimEnter * call VimEnterCallback()
au BufAdd *.[ch] call FindGtags(expand('<afile>'))
au BufWritePost *.[ch] call UpdateGtags(expand('<afile>'))
 
function! FindFiles(pat, ...)
     let path = ''
     for str in a:000
         let path .= str . ','
     endfor
 
     if path == ''
         let path = &path
     endif
 
     echo 'finding...'
     redraw
     call append(line('$'), split(globpath(path, a:pat), '\n'))
     echo 'finding...done!'
     redraw
endfunc
 
function! VimEnterCallback()
     for f in argv()
         if fnamemodify(f, ':e') != 'c' && fnamemodify(f, ':e') != 'h'
             continue
         endif
 
         call FindGtags(f)
     endfor
endfunc
 
function! FindGtags(f)
     let dir = fnamemodify(a:f, ':p:h')
     while 1
         let tmp = dir . '/GTAGS'
         if filereadable(tmp)
             exe 'cs add ' . tmp . ' ' . dir
             break
         elseif dir == '/'
             break
         endif
 
         let dir = fnamemodify(dir, ":h")
     endwhile
endfunc
 
function! UpdateGtags(f)
     let dir = fnamemodify(a:f, ':p:h')
     exe 'silent !cd ' . dir . ' && global -u &> /dev/null &'
endfunction


解释几句:

1. 我增加了一个命令叫FindFiles,是用来生成gtags.files文件的,用法如下:

代码:
:FindFiles {pattern} subdir1 subdir2 ...


然后找到的文件就会都添加到当前buffer的最后。

比如,我们用git刚下下来一整套kernel的源码,放在linux-2.6目录下,然后我想生成列表文件,就可以这样:

代码:
:e gtags.files
:FindFiles **/*.[ch] arch/x86 arch/arm block crypto ...
:w


只增加自己想要的目录,而不要的就不增加。

2. 我添加了三条autocmd,其中:

代码:
au VimEnter * call VimEnterCallback()
au BufAdd *.[ch] call FindGtags(expand('<afile>'))


这两条命令会在你打开一个c文件的时候,自动向上查找GTAGS文件,找到以后就会执行:cs add命令添加这个GTAGS文件。

代码:
au BufWritePost *.[ch] call UpdateGtags(expand('<afile>'))



这条命令会在你修改一个c文件并:w以后,自动进入c文件所在目录并执行"global -u"更新索引文件。

最后,还有一个问题,cscope有一个-f参数,这个参数可以指定cscope.out文件的路径。而gtags-cscope的哲学不一样,它是自己一路向上寻找GTAGS文件,所以没有-f参数。而vim调用:cs add的时候,是会使用-f参数的。这样,当:cs add GTAGS文件的时候,就不能指定当前目录的子目录以外的路径。这也导致:cs add命令只能使用一个GTAGS文件。

针对这个问题,我写了一个vim的patch
http://forum.ubuntu.org.cn/viewtopic.php?f=68&t=342099,在fork出gtags-cscope子进程以后会把子进程chdir()到GTAGS文件所在的目录,这样就OK了。

 

Global源码标签工具支持C,C++,YACC,JAVA,PHP4,ASM。并且可以与shell,vi,emacs,浏览器,doxygen等等配合使用。也是款不错的工具。


以分析linux内核为例
1.准备
$cd /usr/src/linux 
$gtags


gtags会分析当前目录以及所有子目录。之后会在当前目录生成数据库文件
    $ls G* 
    GPATH GRTAGS GSYMS GTAGS
    


分别为:
GPATH 路径名称数据库;
GRTAGS 引用数据库;
GTAGS 定义数据库;
GSYMS 符号数据库,存放没有出现在GTAGS中的符号;

对于分析内核源码,这些数据库需要将近400M空间。

2.global命令使用方法
 直接查询函数:
    $global start_kernel 
    arch/alpha/boot/bootp.c 
    arch/alpha/boot/bootpz.c 
    arch/alpha/boot/main.c 
    init/main.c
    

     查询函数引用:-r
    $global -r start_kernel 
    arch/blackfin/mach-bf548/head.S 
    arch/frv/kernel/debug-stub.c 
    arch/mn10300/kernel/gdb-stub.c 
    ...
    

    使用POSIX标准正则
    $global 'ah[46]_init' 
    net/ipv4/ah4.c 
    net/ipv6/ah6.c
    

    显示函数详细信息 -x
    $global -x ah4_init 
    ah4_init 321 net/ipv4/ah4.c static int __init ah4_init(void)
    

    显示文件所在绝对路径 -a
    $global -a ah4_init 
    /usr/src/linux-source-2.6.26/net/ipv4/ah4.c
    

    查看'GTAGS'没有定义的符号 -s
     global -xs X |more 
    X                 161 arch/alpha/kernel/setup.c #define WEAK(X) \ 
    X                 39 arch/arm/common/dmabounce.c #define DO_STATS(X) do { X ; } while (0)
    X                 41 arch/arm/common/dmabounce.c #define DO_STATS(X) do { } while (0)
    ...
    

    有点类似egrep命令 -g
    $global -xg 'Linus Torvalds' | more 
    Linus%20Torvalds 8 arch/alpha/boot/bootp.c * based significantly on the arch/alpha/boot/main.c of Linus Torvalds
    Linus%20Torvalds 9 arch/alpha/boot/bootpz.c * based significantly on the arch/alpha/boot/main.c of Linus Torvalds
    Linus%20Torvalds 4 arch/alpha/boot/main.c * Copyright (C) 1994, 1995 Linus Torvalds
    ...
    

    按文件名查找 -P
    $global -P 'head\.S$' 
    arch/alpha/boot/head.S 
    arch/alpha/kernel/head.S 
    arch/arm/boot/compressed/head.S 
    arch/arm/kernel/head.S 
    ...
    

    显示文件中对象 -f
    $global -f net/ipv4/af_inet.c 
    DEFINE_SNMP_STAT 119 net/ipv4/af_inet.c DEFINE_SNMP_STAT(struct linux_mib, net_statistics) __read_mostly;
    inet_autobind 174 net/ipv4/af_inet.c static int inet_autobind(struct sock *sk)
    inet_listen 194 net/ipv4/af_inet.c int inet_listen(struct socket *sock, int backlog)
    ...
    

    限制在当前目录查找 -l
    $cd net/ipv4/ 
    $global -l ah[46]_init 
    ah4.c
    

    3.其它用法例子
生成指定文件的源码标签数据库
    $ find . -type f -print >/tmp/list # make a file set 
    $ vi /tmp/list                     # customize the file set 
    $ gtags -f /tmp/list
    

    给只读文件夹生成源码标签数据库,例如cdrom上的源码,数据库文件不能放在当前目录下,这种情况可以使用GTAGSROOT环境变量
    $ mkdir /var/dbpath 
    $ cd /cdrom/src          # the root of source tree 
    $ gtags /var/dbpath      # make tag files in /var/dbpath 
    $ export GTAGSROOT=`pwd` 
    $ export GTAGSDBPATH=/var/dbpath 
    $ global func
    

    设置多个搜寻目录
    $ pwd 
    /develop/src/mh # this is a source project 
    $ gtags 
    $ ls G*TAGS 
    GRTAGS GTAGS 
    $ global mhl 
    uip/mhlsbr.c # mhl() is found 
    $ global strlen # strlen() is not found 
    $ (cd /usr/src/lib; gtags) # library source 
    $ (cd /usr/src/sys; gtags) # kernel source 
    $ export GTAGSLIBPATH=/usr/src/lib:/usr/src/sys 
    $ global strlen 
    ../../../usr/src/lib/libc/string/strlen.c # found in library 
    $ global access 
    ../../../usr/src/sys/kern/vfs_syscalls.c # found in kernel
    

    查询某个对象全名称 -c
$global -c start_k 
start_kcs_transaction 
start_kernel 
start_kernel_proc 
start_kernel_thread

4.与应用程序配合使用
a.生成浏览器格式
生成浏览器格式需要很多磁盘空间,以390M内核为例,需要4-6G的空闲空间-_-!
    $gtags 
    $htags
    

    完成后在当前目录下多个HTML目录。htags有很多选项,如果不熟悉的话,建议使用--suggest选项

b.VIM
使用vim的话,先用gtags生成数据库,然后安装插件
    $mkdir ~/.vim 
    $mkdir ~/.vim/plugin 
    $cp /usr/share/doc/global/examples/gtags.vim.gz ~/.vim/plugin/ 
    $cd ~/.vim/plugin 
    $gzip -d gtags.vim.gz
    

    vim+gtags简单用法:
 
 :Gtags start_kernel   //打开函数浏览


  [Quickfix List]操作
  :cn //切换下一个文件
  :cp                   //切换前一个文件
  :ccN                  //N为数字,切换到指定序号文件
  :cl //查看整个列表
  :h quickfix           //list帮助


其他命令与global类似-r,-s,-f,-g 等等

tab键可以自动补全函数

打开vim时直接定位到函数
    $vim  '+Gtags start_kernel'


还有很多功能没细看,以后慢慢补充

参考文章:
http://www.gnu.org/software/global/globaldoc.html
http://www.linuxsir.org/bbs/showthread.php?t=241578
http://www.cublog.cn/u/5929/showart_2221661.html

 

 


你可能感兴趣的:(gnu-global)