Родное расширение 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") }
Другие вопросы по тегам