Emacs find file utility

最近在移植代码过程中,需要在三个不同的gcc(FSF, Marvell和Android)源代码目录下,查看、比较文件。在另外的目录下打开同一个文件的操作比较繁琐,于是写了点Emacs Lisp代码,自动完成带开文件的操作。

方法很简单,有一个根目录列表,

(defvar wl-find-file-gcc-root-list
  (mapcar 'expand-file-name
          '("~/project/git/marvell-toolchain/src/gcc-src/"
            "~/project/git/fsf-gcc/"
            "~/project/android/toolchain/gcc/gcc-4.4.3/")))

然后通过比较根目录字符串,得到当前buffer对应文件的相对路径,在提示用户选择其它根目录,并打开该目录下的对应文件。

(defun wl-find-file-root (filename root-list)
  (find-if (lambda (dir)
             (when (> (length filename)
                      (length dir))
               (string-equal dir (substring filename
                                            0 (length dir)))))
           root-list))

(defun wl-find-file-in-other-root (filename dir root-list)
  (let* ((root (wl-find-file-root filename root-list))
         (relative-path (substring filename (length root))))
    (find-file-existing (expand-file-name relative-path dir))))

(defun wl-iswitchb-completing-read (prompt choices
                                    &optional dummy require-match)
  "Use iswitchb completion functionality."
  (let ((iswitchb-make-buflist-hook
         (lambda ()
           (setq iswitchb-temp-buflist
                 (cond ((consp (car choices))
                        (mapcar 'car choices))
                       ((stringp (car choices))
                        choices)
                       ((symbolp (car choices))
                        (mapcar 'symbol-name choices))
                       (t
                        (error "Unknown type of choices.")))))))
    (iswitchb-read-buffer prompt nil require-match)))

(defvar wl-find-file-completing-read-function
  'wl-iswitchb-completing-read)

(defun wl-gcc-find-buffer-file-in-other-root (dir)
  (interactive
   (list
    (let ((completion-ignore-case t)
          (root (wl-find-file-root (buffer-file-name)
                                   wl-find-file-gcc-root-list)))
      (funcall wl-find-file-completing-read-function
               "Open file in: " (remove-if (lambda (elem)
                                             (string-equal elem root))
                                           wl-find-file-gcc-root-list)))))
  (wl-find-file-in-other-root (buffer-file-name)
                              dir
                              wl-find-file-gcc-root-list))

模仿Land of Lisp里面的程序风格,把代码分成functional和非functional两部分,其中wl-find-file-in-other-root是functional的,实现核心功能,而接口则是非functional,使用全局变量。

GCC源代码阅读(四)

SPECgcc定义的一套domain language,用于确定选项传递的规则,具体含义参见gcc.c这个文件,搜索The SPEC Language即可。举个简单的例子,在目前的trunk上,gcc/config/linux-android.h文件里定义了一个宏——ANDROID_CC1_SPEC,内容如下:

#define ANDROID_CC1PLUS_SPEC                                            \
  "%{!fexceptions:%{!fno-exceptions: -fno-exceptions}} "                \
  "%{!frtti:%{!fno-rtti: -fno-rtti}}"

我们只看rtti一行,这句话的意思是,如果gcc或者g++的命令行上既没有-frtti,也没有-fno-rtti,那么调用cc1plus的时候就把选项-fno-rtti加上。这句话在其它的CC1_SPEC上是没有的。

正是这段代码,导致编译Android 2.3时,Google gcc能够正常编译,而使用其它的gcc则会遇到如下的错误:

out/target/product/generic/obj/lib/libOpenSLES.so: undefined reference to `typeinfo for android::SortedVectorImpl'
out/target/product/generic/obj/lib/libOpenSLES.so: undefined reference to `vtable for __cxxabiv1::__vmi_class_type_info'

网上提供的一些方法多是把system/media/opensles/libopensles/IAndroidEffect.c改为IAndroidEffect.cpp,这样做的效果是导致编译该文件的命令行发生了变化。

Android在编译C和C++程序(根据文件后缀名判断断)时分别使用不同的命令行选项,编译C的方式如下(可忽略):

define transform-c-or-s-to-o-no-deps
@mkdir -p $(dir $@)
$(hide) $(PRIVATE_CC) \
        $(foreach incdir, \
            $(PRIVATE_C_INCLUDES) \
            $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
                $(PRIVATE_TARGET_PROJECT_INCLUDES) \
                $(PRIVATE_TARGET_C_INCLUDES) \
             ) \
          , \
            -I $(incdir) \
         ) \
        -c \
        $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
            $(PRIVATE_TARGET_GLOBAL_CFLAGS) \
            $(PRIVATE_ARM_CFLAGS) \
         ) \
        $(PRIVATE_CFLAGS) \
        $(1) \
        $(PRIVATE_DEBUG_CFLAGS) \
        -MD -o $@ $<
endef

define transform-c-to-o-no-deps
@echo "target $(PRIVATE_ARM_MODE) C: $(PRIVATE_MODULE) <= $<"
$(call transform-c-or-s-to-o-no-deps, )
endef

define transform-c-to-o
$(transform-c-to-o-no-deps)
$(hide) $(transform-d-to-p)
endef

而编译C++程序的方式如下:

define transform-cpp-to-o
@mkdir -p $(dir $@)
@echo "target $(PRIVATE_ARM_MODE) C++: $(PRIVATE_MODULE) <= $<"
$(hide) $(PRIVATE_CXX) \
        $(foreach incdir, \
            $(PRIVATE_C_INCLUDES) \
            $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
                $(PRIVATE_TARGET_PROJECT_INCLUDES) \
                $(PRIVATE_TARGET_C_INCLUDES) \
             ) \
          , \
            -I $(incdir) \
         ) \
        -c \
        $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
            $(PRIVATE_TARGET_GLOBAL_CFLAGS) \
            $(PRIVATE_TARGET_GLOBAL_CPPFLAGS) \
            $(PRIVATE_ARM_CFLAGS) \
         ) \
        -fno-rtti \
        $(PRIVATE_CFLAGS) \
        $(PRIVATE_CPPFLAGS) \
        $(PRIVATE_DEBUG_CFLAGS) \
        -MD -o $@ $<
$(hide) $(transform-d-to-p)
endef

其中最大的区别就是编译C++使用了选项-fno-rtti

然而实际上IAndroidEffect.c虽然文件后缀名为c,里面可是实实在在的C++程序,为了让编译器把.c文件当成C++程序编译,需要使用选项-x c++。那么为什么改名前Google gcc能够正确编译而其它gcc不可以呢?我们可以使用选项-###打印出gcc调用cc1plus时的选项看个究竟。通过分析发现,Google gcc调用cc1plus时使用了-fno-rtti,而其它gcc并没有使用该选项,因此默认是输出RTTI信息的。

这是因为Google gcc在默认情况下使用了ANDROID_CC1_SPEC,而其它gcc没有。通过查看gcc/config/linux-android.h文件我们知道,使用选项-mandroid即可以启用这个宏。因此,如果编译器支持,我们可以把-mandroid添加到android的TARGET_GLOBAL_CLFAGS里面,来解决上述链接错误。如果编译器不支持-mandroid选项,也可以在TARGET_GLOBAL_CFLAGS里面添加-fno-rtti选项,同样能够解决这个问题。