欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Ruby C Extension 排坑

程序员文章站 2022-05-23 14:00:35
...

Ruby C Extension 排坑

在编写OjRunner的过程中,一开始是参考了Tristan Penman’s Blog,在没有使用gem install之前,一切安好,但是gen install之后,也提示build native extension successfully,但是执行require 'oj_runner'之后报LoadError: cannot load such file -- oj_runner/oj_runner。这段代码是lib/oj_runner.rb中所有,目的是为了引入C编译之后的库。

先来看一下目录结构,红框中的文件是由我们创建的。源码写完之后会放到Github上去。
Ruby C Extension 排坑

首先我们来尝试直接加载

# now at ext/oj_runner
$ ruby extconf.rb
# generate extconf.h & Makefile & mkmf.log
$ make
# generate oj_runner.o & oj_runner.so
$ irb
> require 'oj_runner'
# => true
> OjRunner.run({})
# => 123

这样是没有问题的。再尝试RubyGems中通过添加-I参数的方法。

# now at project root
$ ruby -Ilib:ext -r oj_runner -e "puts OjRunner.run({})"
123

这样也没有问题,这是因为使用-I参数,相当于将lib和ext添加到了include path中,这样lib/oj_runner.rb中的require 'oj_runner/oj_runner'相当于查找到了ext/oj_runner/oj_runner.so,所以此时的问题并没有暴露出来。

先不管Gemfile和Rakefile中的内容,尝试通过gemspec文件将该测试包安装到gem中

# now at project root
$ gem build oj_runner.gemspec
$ sudo gem install oj_runner-0.0.2.gem
$ irb
> require 'oj_runner'
LoadError: cannot load such file -- oj_runner/oj_runner...

此时无法找到oj_runner/oj_runner,是因为即使ext也被安装到了gem里去,但是并没有被包含在路径中。查看gem包的安装路径的结构(我的是/var/lib/gems/2.3.0/gems/oj_runner-0.0.2),可以看到ext和lib下都有oj_runner.so,但显然ext并不是被找到的那个,lib下也有一个,但是被放错了位置。

Ruby C Extension 排坑

那想,如果把require 'oj_runner/oj_runner'改为require 'oj_runner',可不可以。但因为同一个文件下存在oj_runner.rb和oj_runner.so,并不能正确的引用,导致出现undefined method 'run' for OjRunner:Class

仔细看了RubyGems,其中有一句

When the extension is built the files in ext/my_malloc/lib/ will be installed into the lib/ directory for you.

并且查阅了ruby-doc,由此得知,安装的时候会根据create_makefile中的target参数将编译生成的oj_runner.so拷贝到lib中的对应位置,因此原Tristan Penman’s Blog其实写错了extconf.rb文件,应该改为create_makefile('oj_runner/oj_runner')

Ruby C Extension 排坑

或者把ext/extconf.rb中改为oj_runner_lib对应修改oj_runner.c中为Init_oj_runner_lib,这样就不会因为包名重复导致无法正确导入,并且可以得到下图的效果

Ruby C Extension 排坑
Ruby C Extension 排坑

相关标签: Ruby