ruby 本机扩展:未定义的符号
ruby native extension: undefined symbol
我正在尝试创建 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
and/or Rakefile
,但我不确定如何.
我创建了一个简单的存储库来演示问题 over on GitHub。如果您从项目根目录克隆它并 运行 rake
,它将失败并出现相同的错误。
一些附加信息:
- 我使用 ruby gem
hoe
通过 sow example_project
创建项目
- 失败的函数正在尝试调用子目录
ext/example_project/c_example_project
中定义的函数。我的实际项目使用 ext/example_project
目录中的 git 子模块,该子模块又将子模块设置为子目录。子模块是一个扁平结构的c项目(根目录下的所有文件)。 注意:该措辞可能令人困惑,但关键是在 ext/example_project/c_example_project
处定义了一个嵌套的 c 项目,其中包含我要调用的方法。
如果需要任何说明,请告诉我,我会尽力提供。
我没有任何构建本机扩展的经验,但从 mkmf
源代码来看,您似乎只能指定一个源目录。我将两个文件从 c_example_project
移动到父目录,所有内容都已正确链接。我认为这就是你应该做的。所有常见的 gems(如 pg、nokogiri 等)都有这样的代码结构,所有 *.c 和 *.h 文件都在一个目录中。
您始终可以自己创建 Makefile
,但这需要太多的维护工作。
PS。您的项目编译成功,但存在分段错误,因为您应该 return 在 some_function
中正确 ruby 字符串对象,而不是指向字符串的指针。
所以,这里有一些有趣的问题。默认情况下,mkmf 实际上不支持为构建源指定多个目录。
这里有一个解决方法(Takehiro Kubo 关于设置 objs 的评论):
https://www.ruby-forum.com/topic/4224640
基本上,您在 extconf.rb
文件中自己构建 $objs
全局变量。
使用您的 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 源构建一个绝对路径,出于某种原因 rake-compiler 如果我们只是使用 {.,c_example_project}/*.c
就吓坏了,大概是因为它是 运行 来自另一个目录的 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 header,您实际上并没有生成 object 文件,其中 some_function
被定义为。因此,当链接 ruby 加载的最终动态库时,some_function
没有定义,您会收到错误消息。
photoionized 有一个很好的答案,但是 $obj 可以是数组而不是 Enumerator.It 似乎可以简单地使用绝对路径。
$objs = Dir.glob([".c", "libfoobar/*.c"], base: __dir__)
.map { |f| File.expand_path(f, __dir__) }
.map { |f| f.sub(/\.c$/, ".o") }
我正在尝试创建 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
and/or Rakefile
,但我不确定如何.
我创建了一个简单的存储库来演示问题 over on GitHub。如果您从项目根目录克隆它并 运行 rake
,它将失败并出现相同的错误。
一些附加信息:
- 我使用 ruby gem
hoe
通过sow example_project
创建项目
- 失败的函数正在尝试调用子目录
ext/example_project/c_example_project
中定义的函数。我的实际项目使用ext/example_project
目录中的 git 子模块,该子模块又将子模块设置为子目录。子模块是一个扁平结构的c项目(根目录下的所有文件)。 注意:该措辞可能令人困惑,但关键是在ext/example_project/c_example_project
处定义了一个嵌套的 c 项目,其中包含我要调用的方法。
如果需要任何说明,请告诉我,我会尽力提供。
我没有任何构建本机扩展的经验,但从 mkmf
源代码来看,您似乎只能指定一个源目录。我将两个文件从 c_example_project
移动到父目录,所有内容都已正确链接。我认为这就是你应该做的。所有常见的 gems(如 pg、nokogiri 等)都有这样的代码结构,所有 *.c 和 *.h 文件都在一个目录中。
您始终可以自己创建 Makefile
,但这需要太多的维护工作。
PS。您的项目编译成功,但存在分段错误,因为您应该 return 在 some_function
中正确 ruby 字符串对象,而不是指向字符串的指针。
所以,这里有一些有趣的问题。默认情况下,mkmf 实际上不支持为构建源指定多个目录。
这里有一个解决方法(Takehiro Kubo 关于设置 objs 的评论):
https://www.ruby-forum.com/topic/4224640
基本上,您在 extconf.rb
文件中自己构建 $objs
全局变量。
使用您的 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 源构建一个绝对路径,出于某种原因 rake-compiler 如果我们只是使用 {.,c_example_project}/*.c
就吓坏了,大概是因为它是 运行 来自另一个目录的 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 header,您实际上并没有生成 object 文件,其中 some_function
被定义为。因此,当链接 ruby 加载的最终动态库时,some_function
没有定义,您会收到错误消息。
photoionized 有一个很好的答案,但是 $obj 可以是数组而不是 Enumerator.It 似乎可以简单地使用绝对路径。
$objs = Dir.glob([".c", "libfoobar/*.c"], base: __dir__)
.map { |f| File.expand_path(f, __dir__) }
.map { |f| f.sub(/\.c$/, ".o") }