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,它将失败并出现相同的错误。

一些附加信息:

如果需要任何说明,请告诉我,我会尽力提供。

我没有任何构建本机扩展的经验,但从 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") }