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:
hoe
to create the project via sow example_project
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.
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.