我尝试创建ruby原生扩展,但当我运行rake
使用ext/example_project/extconf.rb
构建我的项目并在test/
下运行我的测试时,我得到了运行测试时出现以下错误:
./home/jbuesking/.rbenv/versions/2.3.0/bin/ruby: symbol lookup error:
/home/jbuesking/repositories/example_project/lib/example_project/example_project.so: undefined symbol: some_function
我非常确定我的文件没有正确关联,我需要以某种方式更改我的extconf.rb
和/或Rakefile
,但我不确定
我创建了一个简单的存储库来演示问题over on GitHub。如果您克隆它并从项目根目录运行rake
,它将失败并返回相同的错误。
其他一些信息:
hoe
通过sow example_project
ext/example_project/c_example_project
中定义的函数。我的实际项目使用ext/example_project
目录中的git子模块,该子模块又将子模块设置为子目录。子模块是一个具有扁平结构的c项目(根目录中的所有文件)。 注意:这个措辞可能会让人感到困惑,但关键点在于ext/example_project/c_example_project
定义了一个嵌套的c项目,其中包含我试图调用的方法。如果需要澄清,请告诉我,我会尽力提供。
答案 0 :(得分:3)
所以,你有一些有趣的问题。默认情况下,mkmf实际上不支持为构建源指定多个目录。
有一种解决方法,如此处所示(Takehiro Kubo关于设置objs的评论):
https://www.ruby-forum.com/topic/4224640
基本上,您自己在$objs
文件中构建extconf.rb
全局。
使用你的github代码,这是我添加到extconf.rb并开始工作的内容
<强> extconf.rb 强>
globs = [".", "c_example_project"].map do |directory|
File.join(File.dirname(__FILE__), directory)
end.join(",")
$objs = Dir.glob("{#{globs}}/*.c").map do |file|
File.join(File.dirname(file), "#{File.basename(file, ".c")}.o")
end
注意我实际上构建了一个到每个c源的绝对路径,出于某种原因,如果我们只是用{.,c_example_project}/*.c
进行通信,那么rake-compiler就会吓坏,大概是因为它正在运行extconf.rb文件。另一个目录。
此外,您的tests / c扩展程序中存在一些错误。在example_project.c
中进行以下更改可修复测试失败:
static VALUE example_project_c_code_function()
{
- return some_function();
+ VALUE _string = rb_str_new2(some_function());
+ int _enc = rb_enc_find_index("UTF-8");
+ rb_enc_associate_index(_string, _enc);
+ return _string;
}
<强>解释强>
基本上即使你在extconf.rb中检查c_example_project.h标头,你实际上并没有生成定义some_function
的目标文件。因此,当链接ruby加载的最终动态库时,没有some_function
的定义,您就会收到错误。
答案 1 :(得分:0)
我没有构建本机扩展的经验,但从mkmf
源代码看,您只能指定一个源目录。我将两个文件从c_example_project
移动到父目录,所有内容都正确链接。我认为你应该怎么做。所有常见的宝石(如pg,nokogiri等)都有这样的代码结构,所有* .c和* .h文件都在一个目录中。
您可以随时自行创建Makefile
,但这需要太多工作才能维护。
PS。您的项目编译成功,但是您存在分段错误,因为您应该在some_function
中返回正确的ruby字符串对象而不是指向字符串的指针。