新手问题 怒了,这回 bcrypt-ruby 又出问题了

shallow__pace · 2013年06月28日 · 最后由 luikore 回复于 2013年07月01日 · 11148 次阅读

gem 'bcrypt-ruby' 报错:

Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension.

    /home/sergio/.rvm/rubies/ruby-2.0.0-p195/bin/ruby extconf.rb 
creating Makefile

make
compiling wrapper.c
In file included from wrapper.c:27:0:
/home/sergio/.rvm/rubies/ruby-2.0.0-p195/include/ruby-2.0.0/ruby/backward/util.h:2:2: 警告: #warning use "ruby/util.h" instead of bare "util.h" [-Wcpp]
wrapper.c: 在函数‘crypt_gensalt_rn’中:
wrapper.c:204:3: 错误: ‘EINVAL’未声明(在此函数内第一次使用)
wrapper.c:204:3: 附注: 每个未声明的标识符在其出现的函数内只报告一次
make: *** [wrapper.o] 错误 1

这都是个啥啊。。。

这是没引入 <errno.h> 造成的,具体 bcrypt-ruby 版本是什么?

我装 3.0.1 在 mac 和 ubuntu 都没问题...

#1 楼 @luikore 3.0.1 源码里面有 errno.h,但是我系统的 errno.h 好像跟 kernel2.6 不一样,我是 kernel3.8,能否发一份 errno.h 到 [email protected] , thanks !

不管什么版本的 kernel, <errno.h> 里都有 EINVAL 的,这个问题实在诡异...

可能是 include 路径里带山寨版 <errno.h> 才造成的,就算你替换掉系统的应该也解决不了问题... 又或者是没把 glibc 认出来

你把 Gem 目录里的 Makefile 列出来看看

#3 楼 @luikore


SHELL = /bin/sh

# V=0 quiet, V=1 verbose.  other values don't work.
V = 0
Q1 = $(V:1=)
Q = $(Q1:0=@)
ECHO1 = $(V:1=@:)
ECHO = $(ECHO1:0=@echo)

#### Start of system configuration section. ####

srcdir = .
topdir = $(includedir)/$(RUBY_VERSION_NAME)
hdrdir = $(includedir)/$(RUBY_VERSION_NAME)
arch_hdrdir = $(rubyhdrdir)/$(arch)
PATH_SEPARATOR = :
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
prefix = $(DESTDIR)/home/sergio/.rvm/rubies/ruby-2.0.0-p195
rubysitearchprefix = $(rubylibprefix)/$(sitearch)
rubyarchprefix = $(rubylibprefix)/$(arch)
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
exec_prefix = $(prefix)
vendorarchhdrdir = $(vendorhdrdir)/$(sitearch)
sitearchhdrdir = $(sitehdrdir)/$(sitearch)
rubyarchhdrdir = $(rubyhdrdir)/$(arch)
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
sitehdrdir = $(rubyhdrdir)/site_ruby
rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
vendorarchdir = $(vendorlibdir)/$(sitearch)
vendorlibdir = $(vendordir)/$(ruby_version)
vendordir = $(rubylibprefix)/vendor_ruby
sitearchdir = $(DESTDIR)./.gem.20130628-3540-8x0jp8
sitelibdir = $(DESTDIR)./.gem.20130628-3540-8x0jp8
sitedir = $(rubylibprefix)/site_ruby
rubyarchdir = $(rubylibdir)/$(arch)
rubylibdir = $(rubylibprefix)/$(ruby_version)
sitearchincludedir = $(includedir)/$(sitearch)
archincludedir = $(includedir)/$(arch)
sitearchlibdir = $(libdir)/$(sitearch)
archlibdir = $(libdir)/$(arch)
ridir = $(datarootdir)/$(RI_BASE_NAME)
mandir = $(datarootdir)/man
localedir = $(datarootdir)/locale
libdir = $(exec_prefix)/lib
psdir = $(docdir)
pdfdir = $(docdir)
dvidir = $(docdir)
htmldir = $(docdir)
infodir = $(datarootdir)/info
docdir = $(datarootdir)/doc/$(PACKAGE)
oldincludedir = $(DESTDIR)/usr/include
includedir = $(prefix)/include
localstatedir = $(prefix)/var
sharedstatedir = $(prefix)/com
sysconfdir = $(prefix)/etc
datadir = $(datarootdir)
datarootdir = $(prefix)/share
libexecdir = $(exec_prefix)/libexec
sbindir = $(exec_prefix)/sbin
bindir = $(exec_prefix)/bin
archdir = $(rubyarchdir)


CC = gcc
CXX = g++
LIBRUBY = $(LIBRUBY_SO)
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
LIBRUBYARG_SHARED = -Wl,-R -Wl,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME)
LIBRUBYARG_STATIC = -Wl,-R -Wl,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME)-static
empty =
OUTFLAG = -o $(empty)
COUTFLAG = -o $(empty)

RUBY_EXTCONF_H = 
cflags   =  $(optflags) $(debugflags) $(warnflags)
optflags = -O3 -fno-fast-math
debugflags = -ggdb3
warnflags = -Wall -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wunused-variable -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wimplicit-function-declaration
CCDLFLAGS = -fPIC
CFLAGS   = $(CCDLFLAGS) $(cflags)  -fPIC $(ARCH_FLAG)
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
DEFS     = 
CPPFLAGS =   $(DEFS) $(cppflags)
CXXFLAGS = $(CCDLFLAGS) $(cxxflags) $(ARCH_FLAG)
ldflags  = -L. -fstack-protector -rdynamic -Wl,-export-dynamic
dldflags =  
ARCH_FLAG = 
DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
LDSHARED = $(CC) -shared
LDSHAREDXX = $(CXX) -shared
AR = ar
EXEEXT = 

RUBY_INSTALL_NAME = ruby
RUBY_SO_NAME = ruby
RUBYW_INSTALL_NAME = 
RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
RUBYW_BASE_NAME = rubyw
RUBY_BASE_NAME = ruby

arch = x86_64-linux
sitearch = $(arch)
ruby_version = 2.0.0
ruby = $(bindir)/ruby
RUBY = $(ruby)
ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/defines.h $(arch_hdrdir)/ruby/config.h

RM = rm -f
RM_RF = $(RUBY) -run -e rm -- -rf
RMDIRS = rmdir --ignore-fail-on-non-empty -p
MAKEDIRS = /bin/mkdir -p
INSTALL = /usr/bin/install -c
INSTALL_PROG = $(INSTALL) -m 0755
INSTALL_DATA = $(INSTALL) -m 644
COPY = cp
TOUCH = exit >

#### End of system configuration section. ####

preload = 

libpath = . $(libdir)
LIBPATH =  -L. -L$(libdir) -Wl,-R$(libdir)
DEFFILE = 

CLEANFILES = mkmf.log
DISTCLEANFILES = 
DISTCLEANDIRS = 

extout = 
extout_prefix = 
target_prefix = 
LOCAL_LIBS = 
LIBS = $(LIBRUBYARG_SHARED)  -lpthread -lrt -ldl -lcrypt -lm   -lc
ORIG_SRCS = wrapper.c crypt_blowfish.c bcrypt_ext.c crypt.c crypt_gensalt.c
SRCS = $(ORIG_SRCS) 
OBJS = wrapper.o crypt_blowfish.o bcrypt_ext.o crypt.o crypt_gensalt.o
HDRS = $(srcdir)/ow-crypt.h $(srcdir)/crypt.h
TARGET = bcrypt_ext
TARGET_NAME = bcrypt_ext
TARGET_ENTRY = Init_$(TARGET_NAME)
DLLIB = $(TARGET).so
EXTSTATIC = 
STATIC_LIB = 

BINDIR        = $(bindir)
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
RUBYLIBDIR    = $(sitelibdir)$(target_prefix)
RUBYARCHDIR   = $(sitearchdir)$(target_prefix)
HDRDIR        = $(rubyhdrdir)/ruby$(target_prefix)
ARCHHDRDIR    = $(rubyhdrdir)/$(arch)/ruby$(target_prefix)

TARGET_SO     = $(DLLIB)
CLEANLIBS     = $(TARGET).so 
CLEANOBJS     = *.o  *.bak

all:    $(DLLIB)
static: $(STATIC_LIB)
.PHONY: all install static install-so install-rb
.PHONY: clean clean-so clean-static clean-rb

clean-static::
clean-rb-default::
clean-rb::
clean-so::
clean: clean-so clean-static clean-rb-default clean-rb
        -$(Q)$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time

distclean-rb-default::
distclean-rb::
distclean-so::
distclean-static::
distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
        -$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
        -$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
        -$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true

realclean: distclean
install: install-so install-rb

install-so: $(DLLIB) ./.RUBYARCHDIR.time
    $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
clean-static::
    -$(Q)$(RM) $(STATIC_LIB)
install-rb: pre-install-rb install-rb-default
install-rb-default: pre-install-rb-default
pre-install-rb: Makefile
pre-install-rb-default: Makefile
pre-install-rb-default:
    $(ECHO) installing default bcrypt_ext libraries
./.RUBYARCHDIR.time:
    $(Q) $(MAKEDIRS) $(RUBYARCHDIR)
    $(Q) $(TOUCH) $@

site-install: site-install-so site-install-rb
site-install-so: install-so
site-install-rb: install-rb

.SUFFIXES: .c .m .cc .mm .cxx .cpp .C .o

.cc.o:
    $(ECHO) compiling $(<)
    $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<

.mm.o:
    $(ECHO) compiling $(<)
    $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<

.cxx.o:
    $(ECHO) compiling $(<)
    $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<

.cpp.o:
    $(ECHO) compiling $(<)
    $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<

.C.o:
    $(ECHO) compiling $(<)
    $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<

.c.o:
    $(ECHO) compiling $(<)
    $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<

.m.o:
    $(ECHO) compiling $(<)
    $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<

$(DLLIB): $(OBJS) Makefile
    $(ECHO) linking shared-object $(DLLIB)
    -$(Q)$(RM) $(@)
    $(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)



$(OBJS): $(HDRS) $(ruby_headers)

我在 ubuntu 上用这个 makefile 改下用户名可以编译成功。

再试试这个:

echo "#include <errno.h>\nint i=EINVAL;main(){};" | gcc -x c -

如果没输出,环境应该就是正常的...

#5 楼 @luikore

<stdin>:1:19: 警告: #include 指示的末尾有多余的标识符 [默认启用]
/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/crt1.o:在函数‘_start’中:
(.text+0x20):对‘main’未定义的引用
collect2: 错误: ld 返回 1

#5 楼 @luikore 好诡异,那个 EINVAL 我自己在 errno.h 定义为 22 就可以了,然后报另外一个错

compiling crypt_blowfish.c
crypt_blowfish.c: 在函数‘BF_crypt’中:
crypt_blowfish.c:585:3: 错误: ‘ERANGE’未声明(在此函数内第一次使用)
crypt_blowfish.c:585:3: 附注: 每个未声明的标识符在其出现的函数内只报告一次
crypt_blowfish.c: 在函数‘_crypt_gensalt_blowfish_rn’中:
crypt_blowfish.c:768:3: 错误: ‘ERANGE’未声明(在此函数内第一次使用)
make: *** [crypt_blowfish.o] 错误 1

这个我再在errno.h中定义 ERANGE 为 32 就死活不出来,那个 c 还 include 了 string.h,我就到 string.h 里面定义 ERANGE 也没用。。。

你的系统编码是啥?echo $LANG 看看

说明 include 进来的 errno.h 可能不是你改的那个

echo $C_INCLUDE_PATH 看看这些目录里有没有 errno.h

#10 楼 @luikore 这个什么都没有显示啊。。

#11 楼 @Shallow__pace 好吧,彻底不知道为什么了 ($C_INCLUDE_PATH 是空的是对的)... gcc -v 呢?

使用内建 specs。
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.7/lto-wrapper
目标:x86_64-linux-gnu
配置为:../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.7.3-1ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-4.7/README.Bugs --enable-languages=c,c++,go,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.7 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.7 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --with-system-zlib --enable-objc-gc --with-cloog --enable-cloog-backend=ppl --disable-cloog-version-check --disable-ppl-version-check --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
线程模型:posix
gcc 版本 4.7.3 (Ubuntu/Linaro 4.7.3-1ubuntu1) 

跟我的一模一样... 为什么你就编译不了...

#include <errno.h> 改成 #include <asm/errno.h> 可以么?

再不行就没招了,除非重装 build essentials 系列...

#14 楼 @luikore 我手动改掉所有的 EINVAL 和 ERANGE 可以在源代码目录下 make,然后 make install,然后我不就不知道该怎么办了,gem list 里面还没有 bcrypt-ruby

你可以在 gem 目录里 gem build bcrypt-ruby.gemspec 就出现一个 .gem 文件,然后 gem install 这个文件就可以了

#17 楼 @luikore ERROR: Gemspec file not found: bcrypt-ruby.gemspec 现在 make install 成功了,在哪个目录 gem build ?

#18 楼 @Shallow__pace

就在 bcrypt-ruby-3.0.1 下面啊

gem build bcrypt-ruby.gemspec
mv bcrypt-ruby-3.0.1.gem .. # 这个目录会要被覆盖掉, 跳出去先
cd ..
gem ins bcrypt-ruby-3.0.1.gem

另外执行了 make install 可能会导致以后你装新的这个 gem 出问题的... 到时记得去 site_ruby 的目录把那个 so 找出来拆掉...

#19 楼 @luikore 我靠。。受不了了。我直接在github上下了个bcrypt-ruby

  • 解压,直接gem build bcrypt-ruby.gemspec
  • 然后直接 gem install --local bcrypt-ruby.gem

就直接安装成功了

#21 楼 @Shallow__pace 就是 远在天边近在眼前 的东西... 不过算了恭喜...

#22 楼 @luikore 项目里面 bundle install 也 ok 了,我晕。。这个真心不解是什么意思。。

可能是你失败的 bash 环境关掉了又开了个新的正常的

#24 楼 @luikore 哈哈,安装成功了就不管了,现在没钱换电脑,用的还是linuxmint,有钱换 mac 了环境应该会稳定很多。。。你也是 mac 下虚拟机装的 ubuntu 吗?

#25 楼 @Shallow__pace 是的,为了验证我还在 vps 上的 ubuntu 试了下...

#26 楼 @luikore 哈哈,你这是不求甚解的精神啊,留个邮箱或者 qq 吧,以后还有问题要请教您呢

#27 楼 @Shallow__pace

我的邮箱:

echo U2FsdGVkX18/nXhKLPwmczrKbSQD8J/3aXi/RWuQ+fmog2Z6Xw235yr6f1xcCDlg | openssl base64 -d | openssl aes-128-ecb -d -k hello

#28 楼 @luikore 我晕。。又这么长一串。。学习了

#28 楼 @luikore 哇哈哈,用这邮箱一搜索就发现你在 github 上有几个 repo 里面还有这信息呢

#31 楼 @luikore 昨天安装的bcrypt-ruby还是用不了,我修改了下C_INCLUDE_PATH,报错信息改变了,现在是另外一个。。这个不知道怎么解决了。

/usr/bin/ld: errno: TLS definition in /lib/x86_64-linux-gnu/libc.so.6 section .tbss mismatches non-TLS reference in wrapper.o
/lib/x86_64-linux-gnu/libc.so.6: 无法读取符号: 错误的值
collect2: 错误: ld 返回 1
make: *** [bcrypt_ext.so] 错误 1

#32 楼 @Shallow__pace 之前你做了多余的事情,make install 了 要在 ~/.rvm/rubies/ruby-p195/lib/ruby/site_ruby/~/.rvm/rubies/ruby-p195/lib/ruby/site_ruby/ 里找 bcrypt_ext.so 删掉先...

然后就是... 你为什么改 C_INCLUDE_PATH ?

#33 楼 @luikore bcrypt_ext.so找不到。。。 改C_INCLUDE_PATH是担心我的 C 环境变量不对,可能 gem 中的#include<errno.h>指向的不是我修改的,然后我就将环境变量重新指向/usr/include,果然前面的定义问题就没了

#35 楼 @luikore 用搜索搜的文件系统。。。

#36 楼 @Shallow__pace 搜索会不会忽略 .rvm 这些隐藏文件夹的内容...

#37 楼 @luikore 在.rvm 下面搜索也是这样。。。网上说是要在 makefile 里面加上 - include /usr/include/errno.h , 但是每次 gem install,gem 里面的包就会重置。。改 makefile 也没用啊

建议在命令行下搜,图形界面搜索坑死你... 那两个目录内容很少,在里面 tree -a 就可以了。

~/.rvm/rubies/ruby-2.0.0-p195/lib/ruby/site_ruby/2.0.0/x86_64-linux/bcrypt_ext.so 至少这个要删掉。

你不是 git clone 了么?改 ext/mri/extconf.rb 成下面这样然后 gem build 就好了

require 'mkmf'
$CFLAGS = "-I/usr/include #{$CFLAGS}" # 加这行就可以了
dir_config("bcrypt_ext")
create_makefile("bcrypt_ext")

#39 楼 @luikore 还是报刚才这个错误,官方 3.10 版本也是改动了这一部分去掉警告信息,加上你刚才改的,就变成了

require "mkmf"
  have_header('ruby/util.h')
  $CFLAGS = "-I/usr/include #{$CFLAGS}"
  dir_config("bcrypt_ext")
  create_makefile("bcrypt_ext")

然后 gem build bcrypt-ruby.gemspec

Successfully built RubyGem
Name: bcrypt-ruby
Version: 3.1.0
File: bcrypt-ruby-3.1.0.gem

然后 gem install -l bcrypt-ruby-3.1.0.gem 报错:

linking shared-object bcrypt_ext.so
/usr/bin/ld: errno: TLS definition in /lib/x86_64-linux-gnu/libc.so.6 section .tbss mismatches non-TLS reference in wrapper.o
/lib/x86_64-linux-gnu/libc.so.6: 无法读取符号: 错误的值
collect2: 错误: ld 返回 1
make: *** [bcrypt_ext.so] 错误 1
export LD_LIBRARY_PATH=/usr/lib

这样可以么?

大概你的问题根源是 .bash_profile 之类的出了问题,的把环境变量搞坏了...

还有一种可能...

errno 是 TLS 的,就是 thread local storage 或许你的 errno.h 里写了 extern int errno; , 就变成 non-TLS reference 了,同名但不同 storage 所以产生了链接错误...

#42 楼 @luikore 确实,我在 errno.h 里面找到了extern int errno,然后去掉这一行自己定义错误变量,现在报错信息变了

wrapper.c: 在函数‘crypt_gensalt_rn’中:
wrapper.c:208:3: 错误: ‘errno’未声明(在此函数内第一次使用)
wrapper.c:208:3: 附注: 每个未声明的标识符在其出现的函数内只报告一次

但是代码这一个部分又不知道该怎么处理了

__set_errno(EINVAL);

#43 楼 @Shallow__pace 重装系统应该就好了...

#44 楼 @luikore = = 好纠结的问题,重装系统。。。好吧。。

#44 楼 @luikore = = 网上买个装系统的 u 盘还几天才到,我又仔细看了下,最根本的问题就肯定就是一句/usr/bin/ld: errno: TLS definition in /lib/x86_64-linux-gnu/libc.so.6 section .tbss mismatches non-TLS reference in wrapper.o 要解决的话应该就是makefile,我errno.h应该没什么问题了. 你再确定一下$CFLAGS = "-I/usr/include #{$CFLAGS}" # 加这行就可以了这句话对不对。。 网上说编译的时候加 -include /usr/include/errno.h即可,我不太懂这句话是什么意思

#46 楼 @Shallow__pace

makefile 可能和你的问题一点关系都没有,有一万种可能会导致 TLS 和 non-TLS 链到一起 (是不是 errno 都不一定)... pkg config 不对,gcc 坏了,ruby 安装时的环境不同,甚至某个毫无联系的包出问题都有可能...

preprend $CFLAGS 已经比较暴力了,结果必定是用到 /usr/include/errno.h 的。

最大的问题是别的机器上不可重现... 架空想办法已经到极限了...

#47 楼 @luikore 搞定啦。。。自己手动改了 makefile,然后将 mri 目录下文件的 errno 代码全部删除掉,上传到我的 github,gem install bcrypt-ruby, github:xxxx'就可以了。。 现在就是用annotate`的时候报这个错误

You don't have bcrypt-ruby installed in your application. Please add it to your Gemfile and run bundle install
Unable to annotate user.rb: can't activate bcrypt-ruby (~> 3.0.0), already activated bcrypt-ruby-3.1.0. Make sure all dependencies are added to Gemfile.
Nothing annotated.

这个错误怎么解决

别重装系统啊,开个虚拟机试验试验...

#48 楼 @Shallow__pace Gemfile 和 Gemfile.lock 找到版本号改改,或者不用 annotate 这种邪物...

#50 楼 @luikore 为啥邪物啊,我每个 project 都用... 很方便啊感觉

#50 楼 @luikore #51 楼 @blacktulip

我每个项目都用 annotate 和 magic_encoding 挺好的啊

#51 楼 @blacktulip #52 楼 @huobazi

违反 DRY 原则,annotate 更新不及时就容易隐藏一些 bug, 弄个编辑器插件显示表结构就够了

#53 楼 @luikore 有这种插件啊,求推荐 vim 的和 textmate 的

#54 楼 @blacktulip 原来没有么... textmate 加个一个命令就可以满足大部分 https://gist.github.com/luikore/5899132

需要 登录 后方可回复, 如果你还没有账号请 注册新账号