为vi/vim自动更新tag文件和cscope数据库

xyf问:

请教大哥一个ctags或cscope使用的问题,我的开发工程很大,有上百万行代码,

之前使用sourceinsight,每次编辑代码,会自动更新函数、变量等符号信息,使用vim替换sourceinsight后,每次修改源代码后,如果不手动更新一下tag和cscope.out,相关的跳转就会不正确了,但是更新的话又要很久,请问有没有增量更新tag或cscope.out,或者你是如何解决这个棘手的问题的?谢谢!

这个问题我在写vim使用进阶: 使用标签(tag)文件时也想过,当时在网上查找了很多资料,不过并没有找到一个好的解决方法。后来在开发的过程中,实时更新tag文件和cscope数据库的需求并不强烈,也就放下了。

刚好xyf问这个问题,我把当时我的一些想法写出来,供大家参考。这些都只是初步的想法,没有经过实际的验证。有兴趣的朋友可以试一下,把试验结果反馈给我。

我以Linux系统为例,在windows上应该也能够实现(借助cygwin,以及windows的计划任务)。

其实source insight自动更新函数、变量等信息都是在后台自动进行的,那么我们也可以考虑在后台自动更新tag文件和cscope数据库。

在Linux下,我们可以利用cron守护进程来完成后台自动更新的操作。思路是,让cron进程每隔15分钟(或者更短)扫描一下tag文件和cscope数据创建后发生改变的项目文件,然后为这些项目文件单独生成一个tag文件和cscope数据库。我们知道,在vi里我们可以同时添加多个tag文件和cscope数据库,我们只要把发生改变文件的tags和cscope数据库加到vim里,应该就可以查找、跳转新增的函数了。不过这样做有一个缺点:就是那些修改过的文件,会在vim的tag文件和cscope数据库中出现两次(在原有tag文件中以及新生成的tag文件中),也许会造成使用的不便。

另外,还可以在稍长的时间里(比如一小时)由cron自动为项目中全部文件生成一次tag文件和cscope数据库,并把修改文件的tags和cscope数据库清空。如果担心为全部项目生成tag文件太耗系统资源的话,可以使用Linux中的at守护进程来生成全部文件的tag和cscops数据库,at服务只在系统空闲时才会运行。

下面给一个demo脚本,来自动查找项目中最近改变的文件,并为其生成tag文件和cscope数据库(未经过测试):

#! /bin/sh

# This demo script will find the modified files in your project,
# and generate a ctags file and cscope database for these files.
# This script is NOT tested yet!
# by Easwy Yang, 2009/03/29

# vars
PRJ_DIR=/home/easwy/prjtest
PRJ_TAG_FILE=${PRJ_DIR}/tags
PRJ_MOD_TAG_FILE=${PRJ_DIR}/newtags
PRJ_MOD_CSCOPE_FILE=${PRJ_DIR}/newcscope.out
MOD_FILES=${PRJ_DIR}/mod_files

FIND=/usr/bin/find
CTAGS=/usr/bin/ctags
CSCOPE=/usr/bin/cscope

# find modified files
# you can modify this command to exclude the object files, etc.
${FIND} ${PRJ_DIR} -type f -newer ${PRJ_TAG_FILE} > ${MOD_FILES}

# generate tag file
${CTAGS} -f${PRJ_MOD_TAG_FILE} -L${MOD_FILES}
${CSCOPE} -bq -f${PRJ_MOD_CSCOPE_FILE} -i${MOD_FILES} 

在这个脚本里,/home/easwy/prjtest是项目所在的目录,tags文件则是参考文件,在项目目录中修改日期比tags文件更新的文件都会被find命令查找出来,并且为之生成tag文件和cscope数据库。

希望对大家有所帮助,更希望这一块砖头,能引出更好的玉。

“为vi/vim自动更新tag文件和cscope数据库”的35个回复

  1. 使用gnu global,自动更新就非常好实现了,因为支持增量更新
    每次修改一个文件就更新一下数据库
    在内核里面,这个操作也只要2s的时间
    把gtags.vim gtags-cscope.vim移动进plugin

    生成数据库的脚本
    widon@widon-laptop:~$ cat /usr/local/bin/gt.sh
    find . -name “*.[ch]” > gtags.files
    gtags

    .vimrc里面添加
    function! UpdateGtags(f)
    let dir = fnamemodify(a:f, ‘:p:h’)
    ” exe ‘silent !cd ‘ . dir . ‘ && global -u &> /dev/null &’
    exec ‘cd ‘ . dir
    exec ‘silent !global -u &’
    exec ‘cd -‘
    endfunction

    autocmd BufWritePost *.[ch] call UpdateGtags(expand(”))

    let g:GtagsCscope_Auto_Load = 1
    let g:GtagsCscope_Auto_Map = 1
    let g:GtagsCscope_Absolute_Path = 1

    这样就可以完全替代cscope了。。

  2. @karl
    我现在的办法,在project顶层放置项目文件如:workspace.vim
    在~/.vimrc中
    “”””””””””””””””” load project related configuration
    ” workspace file base name
    let s:WorkspaceFileName = “workspace.vim”

    function! GetWorkspaceFileName()
    let s:current_dir=getcwd()
    let s:root_dir=’/’
    let l:workspace_file_full_path=”/”
    while s:current_dir != s:root_dir
    let s:tmp_string=s:current_dir . “/” . s:WorkspaceFileName
    if filereadable(s:tmp_string)
    let l:workspace_file_full_path=s:tmp_string
    break
    endif
    let s:index=strridx(s:current_dir, “/”)
    ” if s:current_dir==”/home”, s:index will == 0, but strpart() needs
    ” length == at least 1 to get ‘/’
    if s:index == 0
    let s:index=1
    endif
    ” cut string, remove the characters after last ‘/’
    let s:current_dir=strpart(s:current_dir, 0, s:index)
    endwhile
    “return the full path of workspace file if succeeds, otherwise ‘/’
    return l:workspace_file_full_path
    endfunction

    ” get full path of workspace file
    let s:workspace_file_full_path=GetWorkspaceFileName()

    if filereadable(s:workspace_file_full_path)
    exec ‘source’ . s:workspace_file_full_path
    endif
    这样,每次执行vim的时候,会自动寻找并加载项目顶层的workspace.vim

  3. @fanhe
    ctags文件其实也可以包含时间戳,因为tag文件中每个tag都是可以包含注释的。
    发现现在对tag自动更新要求不太高,通常每天更新一次tag就可以满足我的需求了,呵呵

  4. ctags的自动更新是一个很悲剧的问题,就一个tags来说,可以对以文件为单位,检查文件时间戳,如果更改了,就更新那些文件的tags,文本模式的查找替换理论上可以实现,但是貌似tags文件不保存文件时间戳。所以,CodeLite直接放弃文本的tags,采用sqlite3来保存tags信息,也把文件时间戳保存了,所以每次有文件保存的时候,只更新数据库中当前文件的tags,我觉得是很好的一个方案,不过和vim的集成肯定不是太好,这个工作我在做。反正,codelite的tags处理是相当之成功的了。

  5. cscope update:
    cscope.out ,好像不支援 append 的功能,如要更新,要全部重做一次。Automatically create and update cscope database提供了一組 Hot-Key,來幫忙不用退出vim,”一指”完成。
    Hot-Key如下:
    nmap :!find . -iname ‘*.c’ -o -iname ‘*.cpp’ -o -iname ‘*.h’ -o -iname ‘*.hpp’ > cscope.files
    \ :!cscope -b -i cscope.files -f cscope.out
    \ :cs reset

  6. TagsParser : Automatic tagfile updating and tag viewer
    http://www.vim.org/scripts/script.php?script_id=1535
    看了博主系列文章,写得很详细,很有感触。关于ctags的自动更新,可以看下官方的TagsParser。下面这些可供参考
    http://vim.wikia.com/wiki/Autocmd_to_update_ctags_file
    http://www.vim.org/scripts/script.php?script_id=100
    http://www.vim.org/scripts/script.php?script_id=1343
    http://stackoverflow.com/questions/155449/vim-auto-generate-ctags
    说实在的,我也不知道哪个会更好点

  7. 是否可以用autocmd来实现?扑捉对文件的写入事件,当对文件有写入动作之后,重新生成Tag。或者直接用一个快捷键,就像IDE里的编译一样,按下键之后,就重新生成了。

  8. Easwy :@karltaglist中文名的问题我没解决过,一般也很少使用中文做目录名。看看taglist的FAQ吧::help taglist-faq

    又试了一下,可以了,之前可能敲错了。

  9. @karl
    既然是用ssh登陆运行VIM,那么VIM进程是跑在Server上的,寄存器之类的也是存在Server上,自然没有办法通过VIM寄存器在client上共享剪贴板。
    我一般是用terminal的特性,在vim中用shift+鼠标左键拖拽 选定要复制的区域,然后复制(xterm选定后已经自动复制了),到另一个窗口shift+Insert粘贴(vim要在非Normal模式下)。
    另外,*和+寄存器只有在图形模式下(gvim)才有。

  10. Easwy :@karl脚本其实也很简单的,你:help一下就差不多了
    taglist的中文路径问题,好像有人已经解决过了,在网上搜一下吧vim中不能设置不同大小的字体。不过我觉得,既然已经选择了vim,要学会适应它的风格,毕竟每个程序都不一样的,不能用一个程序的标准去要求其它的程序,这种先入为主太偏颇了

    网上只搜到这哥们的办法:
    http://qzone.qq.com/blog/6698756-1218395519
    但试了并不好使
    虽然运行taglist不报错了,但没有显示函数列表出来

  11. @Easwy

    Easwy :@karl脚本其实也很简单的,你:help一下就差不多了
    taglist的中文路径问题,好像有人已经解决过了,在网上搜一下吧vim中不能设置不同大小的字体。不过我觉得,既然已经选择了vim,要学会适应它的风格,毕竟每个程序都不一样的,不能用一个程序的标准去要求其它的程序,这种先入为主太偏颇了

    嗯 好
    我在UBUNTU下,用SSH登陆上RH AS的主机,使用VIM,没法和系统共享剪贴板,用:reg查看,没有看到*和+寄存器,请问是为什么,应该怎样设才能有*和+两个寄存器?--才能使各个SSH窗口之间通过*和+寄存器进行拷贝?

  12. @karl
    脚本其实也很简单的,你:help一下就差不多了

    taglist的中文路径问题,好像有人已经解决过了,在网上搜一下吧
    vim中不能设置不同大小的字体。
    不过我觉得,既然已经选择了vim,要学会适应它的风格,毕竟每个程序都不一样的,不能用一个程序的标准去要求其它的程序,这种先入为主太偏颇了

  13. oldbee :

    Easwy :@karl不过我通常都是在项目的根目录下生成整个项目的tag文件/cscope文件,因为使用了lookupfile插件,直接在vim中打开文件已经很方便了,不需要cd到某个目录再打开文件。

    @karl我和Easwy的做法基本一样,在项目根目录下建立一个workspace.vim,然后在.vimrc里面if filereadable(”workspace.vim”)source workspace.vimendif
    workspace.vim里放置项目相关配置:path/cscope_db/ctags/lookupfile_tags/DoxygenToolkit_comment/errorformat,以及一些特殊的filetype。

    我更倾向于用这种方法:

    生成一个绝对路径的tag文件,以及绝对路径的cscope文件,就可以解决你的问题。不过你也许需要在vimrc里设置一下,发现当前目录是项目A的子目录时,使用项目A的tag,否则就用项目B的。

    还不知脚本怎么写。。。
    我看代码关联一般是在si里面看,查找变量在文件中被修改的地方一般在vi中用*#查找。。。
    现在有两个问题:
    1、windows下的taglist遇到中文路径没法识别
    2、上回装了一个srcexpl.vim插件,用着感觉怎么看着就不如SI的关联窗口,后来发现问题所在,在SI中,代码的关联窗口的字体是比代码窗口的字体小一号,而在srcexpl.vim插件中的字体是和主窗口一样大小的。。。有没有办法把字体变小?

  14. Easwy :
    @karl
    不过我通常都是在项目的根目录下生成整个项目的tag文件/cscope文件,因为使用了lookupfile插件,直接在vim中打开文件已经很方便了,不需要cd到某个目录再打开文件。

    @karl
    我和Easwy的做法基本一样,在项目根目录下建立一个workspace.vim,然后在.vimrc里面
    if filereadable(“workspace.vim”)
    source workspace.vim
    endif

    workspace.vim里放置项目相关配置:path/cscope_db/ctags/lookupfile_tags/DoxygenToolkit_comment/errorformat,以及一些特殊的filetype。

  15. @karl
    生成一个绝对路径的tag文件,以及绝对路径的cscope文件,就可以解决你的问题。不过你也许需要在vimrc里设置一下,发现当前目录是项目A的子目录时,使用项目A的tag,否则就用项目B的。

    不过我通常都是在项目的根目录下生成整个项目的tag文件/cscope文件,因为使用了lookupfile插件,直接在vim中打开文件已经很方便了,不需要cd到某个目录再打开文件。
    在开发过程中我基本上根本不退出vim,在vim里就可以搞定一切,实在不行,就再开一个putty/rxvt终端来做vim里不好做的工作。

  16. 是的:)
    而且还有一个问题就是:vi要在做tag所在目录为相对目径的情况下,打开源文件,才能用cscope搜定义什么的。。。但我一般都会先切换到下面的子目录去。。。然后再vi xxx.cpp,这样子就会导致没法搜索。不知这个问题有没有解决的办法?

    1. set noautochdir “自动改变当前目录位置autochdir

      我用了这个之后,就不会出现你说的那种情况了;但是首次打开vim时最好随便打开一个顶层文件。以初始化当前目录为workdir。

  17. 两个不同的工程。。。tag文件如果所有的工程做成一个的话,查找的时候会重复,不准之类的。。。如何在vimrc里针对一个工程的目录配置一个tag文件?

  18. 好!
    除了这一个问题,还有一个不同工程间tag的切换的问题,vi好像有个tag switch的插件,不过偶还没去看怎么用。。。现在用vi只是用来查找,看代码的关联关系还是在si中方便。。。

  19. @Easwy
    关于自动更新tag的一点儿想法:
    1.cscope是支持增量更新的,cscope.files不变的话,默认参数就是增量。
    2.而ctags是不支持增量更新的。但对于支持多tags的编辑器(如VIM),可以考虑分层次的生成多tags,然后根据修改的文件更新(重建)相应的tags即可。可以参考http://ctags.sourceforge.net/faq.html#15
    3.最后将文件保存指令和更新tags MAP成一个快捷键。

    其实个人对自动更新tags的需求也不是很强,对于一个session,有taglist就行了。做完本地和代码库的同步后,总是会重新建立数据库的,用server也没有觉得慢,而且这点时间可以休息休息眼睛^_^

  20. @alickguo
    我似乎也遇到过这样的问题,我记不清是什么原因导致的,好像是因为在vim中切换目录?
    遇到这种情况,不必关闭vim,先使用”:cs kill 0″断开和cscope的连接,然后再”:cs add cscope.out”重新连就可以了。
    如果总出现这种情况又找不到原因的话,把上面两个命令map成一个快捷键,会使你的困扰小一点。

  21. 请教一个Cygwin下vim+cscope的问题.
    cscope connecte后过了大概两三小时,再使用cs find 查找时会出现查找失败,vim关闭重开后重新connecte后又恢复了,找了n久没有找到解决方法,错误信息如下:
    E262: error reading cscope connection 0
    E259: no matches found for cscope query f of ddm_frame.c
    vim与cscope的版本信息如下:
    $ vim –version
    VIM – Vi IMproved 7.2 (2008 Aug 9, compiled Feb 23 2009 13:47:33)
    Compiled by alickguo@0kigv8dr4ef5

    $ cscope –version
    cscope: version 15.6

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注