[Unix]编译定制一个可移动的Vim

Posted on 2012年1月07日 21:33

    Mint 12源里面的Vim并不支持lua脚本扩展,于是就想自己编译一个。如果能编译成可以移动的就更好了,这样使用不同机器的时候只需要scp一个目录就可以了。

    下载了Vim 7.3的源码包准备编译,首先就发现不能外部构建,必须要在源代码目录下configure,因为源代码根目录下的configure文件是这样写的:

cd src && exec ./configure "$@"

然后研究了一下可用的选项,最后使用的选项如下:

./configure --prefix=/opt/vim73/ --with-features=huge --disable-darwin \
--disable-selinux --enable-luainterp=yes  --enable-cscope --enable-xim \
--enable-gui=gtk2 --without-local-dir --with-vim-name=pvim --with-ex-name=pex \
--with-view-name=pview

不过这些选项里并没有指明使用的vimrc以及其他.vim文件的路径,如此编译安装后,使用的的vimrc应该还是和系统自带的Vim是一样的。我希望编译出的Vim使用它安装目录下的vimrc。不过configure脚本似乎没有选项可以指明这个。

 

    细想一下,默认的vimrc的路径有可能是在代码中指定的,于是用grep搜索代码,发现src/feature.h这个文件甚是可以,打开一看发现这个文件就是预留用来自定义某些配置的。关于vimrc主要有一下几个宏:

/* #define VIMRC_FILE   ".vimrc" */
/* #define GVIMRC_FILE  ".gvimrc" */

/* #define USR_VIMRC_FILE       "~/foo/.vimrc" */
/* #define USR_VIMRC_FILE2      "~/bar/.vimrc" */
/* #define USR_VIMRC_FILE3      "$VIM/.vimrc" */

/* #define USR_GVIMRC_FILE      "~/foo/.gvimrc" */
/* #define USR_GVIMRC_FILE2     "~/bar/.gvimrc" */
/* #define USR_GVIMRC_FILE3     "$VIM/.gvimrc" */

/* #define SYS_VIMRC_FILE       "/etc/vimrc" */
/* #define SYS_GVIMRC_FILE      "/etc/gvimrc" */

和上面贴的一样,在src/feature.h中这些宏都是注释掉的。这其中主要起作用的宏有:

USR_VIMRC_FILE
SYS_VIMRC_FILE
USR_GVIMRC_FILE
SYS_GVIMRC_FILE

添加这几个宏的定义,使得vimrc的路径依赖于VIMRUNTIME变量。然后make && make install。

 

    看起来似乎大功告成,但这时我发现只要将安装好的Vim移动到别的路径,Vim就找不到各种配置文件了。看来VIMRUNTIME的预设值也是在编译时确定的。google了一下这个变量,发现如果在Vim执行前用export设置这个变量的值到正确的路径,那么Vim仍然能够找到vimrc等配置文件。

 

    这样依赖就有两种方法来解决:

1.用shell脚本wrap一下Vim的可执行文件。

2.修改Vim源文件,在main函数刚开始的地方用setenv设置这个环境变量。

    考虑了一下决定采用第二种方法,结果发现在程序中获取可执行文件的路径竟然相当麻烦(上一篇博客讲了这个问题)。不过毕竟我的环境基本是Linux,于是写了一个Linux Only的patch,哪天需要在新环境使用再改吧。

diff --git a/src/main.c b/src/main.c
index 435e607..cf3a0fc 100644
--- a/src/main.c
+++ b/src/main.c
@@ -174,6 +174,29 @@ main
 #endif
 
     /*
+     * Set $VIMRUNTIME.
+     */
+#ifdef UNIX
+    char exe_path_buff[512];
+    char *exe_path_ptr;
+    ssize_t exe_path_len = 
+        readlink("/proc/self/exe", exe_path_buff, sizeof(exe_path_buff));
+    if(exe_path_len > 0)
+    {
+        exe_path_buff[exe_path_len] = 0;
+        exe_path_ptr = exe_path_buff + exe_path_len;
+        while(exe_path_ptr != exe_path_buff && *exe_path_ptr != '/')
+            exe_path_ptr --;
+        *exe_path_ptr = 0;
+        while(exe_path_ptr != exe_path_buff && *exe_path_ptr != '/')
+            exe_path_ptr --;
+        *exe_path_ptr = 0;
+        strcat(exe_path_buff, "/share/vim/vim73/");
+        setenv("VIMRUNTIME", exe_path_buff, 0);
+    }
+#endif
+
+    /*
      * Do any system-specific initialisations.  These can NOT use IObuff or
      * NameBuff.  Thus emsg2() cannot be called!
      */

[Unix]在程序中取得当前程序可执行文件的路径

Posted on 2012年1月07日 02:44

  出乎意料,这个貌似简单的问题麻烦的紧,在Unix下没有通用且简单的方法解决。stackoverflow上有人问了这个问题:

http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe

简单但平台相关的方法(stackoverflow上的回答):

  • Mac OS X: _NSGetExecutablePath() (man 3 dyld)
  • Linux: readlink /proc/self/exe
  • Solaris: getexecname()
  • FreeBSD: sysctl CTL_KERN KERN_PROC KERN_PROC_PATHNAME -1
  • BSD with procfs: readlink /proc/curproc/file
  • Windows: GetModuleFileName() with hModule = NULL
下面有人补充了一个FreeBSD的另一个方法:readlink /proc/curproc/file

HP的一个页面上有一个通用的方法:
http://h21007.www2.hp.com/portal/site/dspp/menuitem.863c3e4cbcdc3f3515b49c108973a801?ciid=88086d6e1de021106d6e1de02110275d6e10RCRD
程序挺长,方法是根据argv[0]来判断是由绝对/相对路径执行的当前程序还是通过$PATH的方式执行的。对于后一种执行方式,通过在$PATH中依次查找的方式找到具体的路径。

想来想去,我也没想到更好的方法。对于上面那个通用的方法,到$PATH中查找的过程可以改为通过popen调用which的方式简化一些。

PS:有意思的是,搜索的时候发现不管是中文页面还是英文页面,都有不少语文不好的同学建议使用getcwd()...