Родное расширение ruby: неопределенный символ
Я пытаюсь создать собственное расширение 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
в некотором роде, но я не уверен, как.
Я создал простой репозиторий, который демонстрирует проблему на GitHub. Это приведет к ошибке с той же ошибкой, если вы клонируете ее и запускаете rake
из корня проектов.
Некоторая дополнительная информация:
- Я использовал рубиновый камень
hoe
создать проект черезsow example_project
- Сбой функции пытается вызвать функцию, определенную в подкаталоге
ext/example_project/c_example_project
, Мой настоящий проект использует подмодуль git изext/example_project
каталог, который в свою очередь устанавливает подмодуль в качестве подкаталога. Подмодуль - это проект с плоской структурой (все файлы в корневом каталоге). Примечание: эта формулировка может сбивать с толку, но ключевой момент заключается в том, что есть вложенный проект c, определенный вext/example_project/c_example_project
у которого есть методы, которые я пытаюсь вызвать.
Дайте мне знать, если потребуется какое-либо разъяснение, и я сделаю все возможное, чтобы предоставить его.
3 ответа
Итак, у вас есть несколько интересных вопросов. По умолчанию mkmf фактически не поддерживает указание нескольких каталогов для создания исходных текстов.
Существует обходной путь, как показано здесь (комментарий Такехиро Кубо об установке 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
Заметьте, что я на самом деле строю абсолютный путь к каждому из исходных текстов, по какой-то причине 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;
}
объяснение
По сути, даже если вы проверяете заголовок c_example_project.h в файле extconf.rb, вы фактически не генерируете объектный файл, где some_function
определено. Таким образом, при связывании конечной динамической библиотеки, которая загружается в ruby, нет определения для some_function
и вы получите свою ошибку.
У меня нет опыта создания собственных расширений, но из mkmf
Исходный код, похоже, вы можете указать только один исходный каталог. Я переместил оба файла из c_example_project
к родительскому каталогу, и все было связано правильно. Я думаю, это то, как вы должны это сделать. Все распространенные гемы (такие как pg, nokogiri и т. Д.) Имеют такую структуру кода, все файлы *.c и *.h находятся в одном каталоге.
Вы всегда можете создать Makefile
себя, но это потребовало бы слишком много работы, чтобы поддерживать.
PS. Ваш проект успешно скомпилирован, но у вас есть ошибка сегментации, потому что вы должны вернуть правильный объект ruby string в some_function
а не указатель на строку.
photoionized имеет отличный ответ, но $obj может быть массивом вместо перечислителя. Кажется, можно просто использовать абсолютный путь.
$objs = Dir.glob([".c", "libfoobar/*.c"], base: __dir__)
.map { |f| File.expand_path(f, __dir__) }
.map { |f| f.sub(/\.c$/, ".o") }