用beagle/grep让阅读海量源代码不再是难题

阅读源代码是一门技术活,有一个老外专门写了一本书,《Code Reading》,教人怎样阅读代码,用什么工具阅读代码等等。

我最早用过souce insight(界面挺漂亮,快捷键挺方便),后来用过开源的source navigator(那叫一个慢),对这些工具有一些不满意的地方:

  1. 有局限性。当代码变得复杂,比如C语言里用了很多宏,C++里用了很多继承的时候,用这些工具会找不到,或者找出来很多噪音,这时通常就没办法自己扩展了

  2. 不够快。尤其是当代码量变得庞大,我是说像Android的代码那样,快上两个G了,这些工具基本没法用了。至少是没法直接用,必须分而治之,针对自己感兴趣的部分代码创建不同的阅读项目,这又引出一个新的问题,你怎么能有信心目前看的这个东西跟别的代码项目没有关系呢?如果有关系,并且是很重要的,对你理解代码很有帮助的,那就会浪费时间了。总之让人没有安全感。

后来又使用了tags/cscope。这两个工具再加上grep对阅读不太大的代码库已经足够了,包括Linux Kernel。

但是当阅读Android的代码的时候,单独的grep或者cscope里的正则表达式匹配就不够了,代码库太大,搜索的时间很长。

这时候beagle就派上用场了。Beagle是Novell公司主导开发的开源软件,其中又用到了Apache的Lucene,可以用来做搜索引擎的技术。用beagle可以对任意的文本创建索引,以便加快搜索速度。类似的在Windows上有Live Search。但是beagle的好处在于,它除了有桌面图形界面之外,还有命令行接口!

配合beagle的索引功能,可以让搜索几乎任意大的代码库都只需要很短的时间。具体的配置是用beagle-build-index先生成一个索引,再用beagle-static-query把包含要搜索的字符串的文件列出来,最后再用grep精确定位到这些文件中包含要搜索的字符串的那些行上。

生成beagle索引的方法:

下面是我自己有beagle阅读源代码的具体办法:

首先,在代码库的最上层目录执行:

mkdir .beagle; beagle-build-index --recursive --deny-pattern .beagle --enable-deletion --target .beagle/ .

列出匹配文件的beagle命令是

beagle-static-query --add-static-backend "$beagle_dir" --backend none --max-hits 100000 "$longest_token"|sed -e 's!^file://!!'|grep -v '#'|sort -u|~/.my-beagle-filter

其中,$beagle_dir应该是你的代码库最上层目录下的.beagle文件夹的路径,$longest_token则是我自己处理过的要搜索的正则表达式中的最大的字符数字串,因为beagle建立索引的时候应该是只针对一个一个的单词进行创建的。

后面的那些sed/grep等的处理则是让beagle-static-query的输出变成Posix的路径。~/.my-beagle-filter做一些额外的处理,比如,在阅读Linux Kernel源代码的时候,如果只关注x86的架构,那么就可以在这里把别的架构的文件都过滤掉。

所有的这些配置都可以在http://windows-config.googlecode.com/svn/trunk里找到:

svn co http://windows-config.googlecode.com/svn/trunk

我一般把我的HOME目录里重要的配置文件全部放在上面这个svn上。所以~/.my-beagle-filter的内容您也可以在http://windows-config.googlecode.com/svn/trunk/.my-beagle-filter里看到,其他的以~开头的目录或者文件的路径也依此类推。

最后一步就是用grep针对匹配的文件进行精确的定位,这个脚本在~/bin/beagle-grep.sh

要注意的是,任何工具都是有局限的,比如我这套配置脚本,也有很大的局限性。但是目前它已经能满足我的大部分需要了。只是提醒大家千万不要过分的依赖任何某个特定的工具。如果某个工具你觉得好用,你就偷着乐吧;如果不好用的话,你得忍受,实在受不了的时候就千万别勉强了,该出手时就出手,写一个自己的工具出来,让大家都来用!

后记:比如我最近发现的一些局限性:

  1. beagle默认只会为长度小于30的词创建索引,如果长度超过30,它就没法进行搜索了。你可以用这个方法自己验证一下:

      mkdir tmpxx
      cd tmpxx
      for x in `seq 25 35`; do 
          for y in `seq $x`; do
              echo -n x
          done > hello.txt
          mkdir -p .beagle; beagle-build-index --recursive --deny-pattern .beagle --enable-deletion --target .beagle/ . >/dev/null 2>&1
          echo "for token length `cat hello.txt|wc -c`"; my-beagle `cat hello.txt`; echo 
      done
    

    至于为什么会这样的限制,就需要去看源代码才能弄明白了。但是我觉得意义不大,我可以肯定地说,如果我读的源代码里,一个变量名的长度超过30个字符,那这个变量名应该不会是很重要的变量,那种我需要做全局搜索的变量。

  2. beagle在我的系统(ubuntu 10.04 amd64)默认安装无法处理.xml文件;对java文件会把一些关键字全部过滤掉,而我不想它们被过滤。这时候需要修改/etc/beagle/external-filters.xml,加上这样的内容:

        <external-filters>
        <filter>
          <mimetype>text/x-java</mimetype>
          <extension>.java</extension>
          <command>cat</command>
          <arguments>%s</arguments>
        </filter>
        <filter>
          <mimetype>text/xml</mimetype>
          <extension>.xml</extension>
          <command>cat</command>
          <arguments>%s</arguments>
        </filter>
        </external-filters>
    
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: