JesseBuesking JesseBuesking - 26 days ago 16
Ruby Question

ruby native extension: undefined symbol

I'm attempting to create a ruby native extension, but when I run

rake
which uses
ext/example_project/extconf.rb
to build my project and run my tests under
test/
, I get the following error when the tests are run:

./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


I'm pretty sure my files are not being linked correctly and that I need to alter my
extconf.rb
and/or
Rakefile
in some way, but I'm not sure how.

I've created a simple repository that demonstrates the issue over on GitHub. It'll fail with the same error if you clone it and run
rake
from the projects root.

Some additional information:


  • I used the ruby gem
    hoe
    to create the project via
    sow example_project

  • The failing function is attempting to call a function defined in the subdirectory
    ext/example_project/c_example_project
    . My actual project uses a git submodule from the
    ext/example_project
    directory, which in turn sets up the submodule as a subdirectory. The submodule is a c project with a flattened structure (all files in the root directory). Note: That wording may be confusing, but the key point is that there's a nested c project defined at
    ext/example_project/c_example_project
    which has methods I'm trying to call.



Let me know if any clarification is needed, and I'll do my best to provide it.

Answer

So, there are some interesting issues you have here. By default, mkmf doesn't actually support specifying multiple directories for building sources.

There is a workaround, as seen here (Takehiro Kubo's comment about setting objs):

https://www.ruby-forum.com/topic/4224640

Basically, you construct the $objs global in your extconf.rb file yourself.

Using your github code, here's what I added to the extconf.rb and got to work

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

Notice I'm actually constructing an absolute path to each of the c sources, for some reason rake-compiler was freaking out if we were just globbing with {.,c_example_project}/*.c, presumably since it's running the extconf.rb file from another directory.

In addition, your tests/c extensions have a few errors in them. Making the following change in example_project.c fixes the test failure:

 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;
 }

Explanation

Basically even though you're checking the c_example_project.h header in your extconf.rb, you're not actually generating the object file where some_function is defined. So, when linking the final dynamic library that ruby loads up, there's no definition for some_function and you get your error.

Comments