Ein Tutorial zum Erstellen eines Ruby-Edelsteins mit nativen C-Erweiterungen und zum Debuggen mit gdb.
Der in diesem Artikel verwendete Code wurde in GitHub hochgeladen.
Dieser Schritt ist nicht obligatorisch, Sie können ihn also überspringen.
Durch das Erstellen von "Ruby" können Sie auch Ruby selbst während der Entwicklung des Gem-Debugs durchlaufen, was die Entwicklung von Gem unterstützt.
Verwenden Sie hier rbenv + ruby-build. Angenommen, rbenv ist in ~ / .rbenv
installiert.
# --keep keeps source code in ~/.rbenv/sources/2.6.1/ruby-2.6.1/
rbenv install --keep --verbose 2.6.1
rbenv shell 2.6.1
# check that ruby is debuggable
type ruby # => ruby is /home/wsh/.rbenv/shims/ruby
rbenv which ruby # => /home/wsh/.rbenv/versions/2.6.1/bin/ruby
gdb -q ~/.rbenv/versions/2.6.1/bin/ruby
# (gdb) break main
# (gdb) run
# Breakpoint 1, main (argc=1, argv=0x7fffffffdd58) at ./main.c:30
# 30 {
# (gdb) list
# 25 #include <stdlib.h>
# 26 #endif
# 27
# 28 int
# 29 main(int argc, char **argv)
# 30 {
# 31 #ifdef RUBY_DEBUG_ENV
# 32 ruby_set_debug_option(getenv("RUBY_DEBUG"));
# 33 #endif
# 34 #ifdef HAVE_LOCALE_H
Unter macOS [Codezeichenproblem](https://www.google.com/search?safe=off&ei=ijNmXN3jB8aq8QXvvpfgDA&q=please+check+gdb+is+codesigned+-+see+taskgated%288%29&oq=please+check+gdb+ ist + codiert + - + siehe + taskgated% 288% 29 & gs_l = psy-ab.3..0.232958.232958..233371 ... 0.0..0.92.92.1 ...... 0 .... 2j1..gws -wiz ....... 0i71.1sEziP0mz_E), verwenden Sie also lldb
anstelle von gdb
oder GDB Wiki Description ) Und führen Sie eine Codesignatur durch.
Das Deaktivieren der Optimierung (-O0
) erleichtert das Debuggen:
git clone https://github.com/ruby/ruby.git
cd ruby/
autoconf -v # as written in README.md
mkdir build; cd build/
# --disable-install-doc saves build time
../configure --prefix=$HOME/.rbenv/versions/trunk --disable-install-doc --enable-debug-env optflags="-O0"
make V=1 -j4
make install
rbenv shell trunk
ruby --version # 2.?.?dev
gdb -q ~/.rbenv/versions/trunk/bin/ruby
or:
wget https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.1.tar.gz
tar xvf ruby-2.6.1.tar.gz
cd ruby-2.6.1/
mkdir build/; cd build/
../configure --prefix=$HOME/.rbenv/versions/trunk --disable-install-doc --enable-debug-env optflags="-O0"
make V=1 -j4
make install
rbenv shell 2.6.1-dbg
ruby --version # 2.6.1
gdb -q ~/.rbenv/versions/2.6.1-dbg/bin/ruby
debug = flags = -ggdb3
ist die Standardeinstellung, sodass Sie keine Debug-Optionen angeben müssen.
--MacOS / usr / lib / libcrypto.dylib
ist zu alt, um das OpenSSL-Modul zu erstellen. Fügen Sie also nach Brew Install OpenSL`` CPPFLAGS = "- I / usr / local / opt / openssl
zu Configure
hinzu. Add / include "LDFL AGS =" - L / usr / local / opt / openssl / lib "`.V = 1
anzeigen.Von nun an werde ich rbenv shell trunk
(~ / .rbenv / version / trunk /
) verwenden.
rbenv shell trunk
cd ~/work/
bundle gem example_ext --coc --ext --mit --test
cd example_ext/
bin/setup # as described in ./README.md
Ein Fehler tritt wie unten gezeigt auf.
$ bin/setup
bundle install
+ bundle install
You have one or more invalid gemspecs that need to be fixed.
The gemspec at /home/wsh/work/example_ext/example_ext.gemspec is not valid. Please fix this gemspec.
The validation error was 'metadata['homepage_uri'] has invalid link: "TODO: Put your gem's website or public repo URL here."'
Bearbeiten und korrigieren Sie example_ext.gemspec
:
diff --git a/example_ext.gemspec b/example_ext.gemspec
index 4b9d3c1..1446707 100644
--- a/example_ext.gemspec
+++ b/example_ext.gemspec
@@ -9,9 +9,9 @@ Gem::Specification.new do |spec|
spec.authors = ["Wataru Ashihara"]
spec.email = ["[email protected]"]
- spec.summary = %q{TODO: Write a short summary, because RubyGems requires one.}
- spec.description = %q{TODO: Write a longer description or delete this line.}
- spec.homepage = "TODO: Put your gem's website or public repo URL here."
+ spec.summary = %q{Write a short summary, because RubyGems requires one.}
+ spec.description = %q{Write a longer description or delete this line.}
+ # spec.homepage = "TODO: Put your gem's website or public repo URL here."
spec.license = "MIT"
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
@@ -19,9 +19,9 @@ Gem::Specification.new do |spec|
if spec.respond_to?(:metadata)
spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
- spec.metadata["homepage_uri"] = spec.homepage
- spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
- spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
+ # spec.metadata["homepage_uri"] = spec.homepage
+ # spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
else
raise "RubyGems 2.0 or newer is required to protect against " \
"public gem pushes."
Dies sollte den Build bestehen.
bin/setup
bundle exec rake install # as described in README.md
bundle exec ruby -e 'require "example_ext"; p ExampleExt::VERSION'
# => "0.1.0"
Lassen Sie uns die C-Erweiterung implementieren.
diff --git a/ext/example_ext/example_ext.c b/ext/example_ext/example_ext.c
index c89d90a..f47c72e 100644
--- a/ext/example_ext/example_ext.c
+++ b/ext/example_ext/example_ext.c
@@ -2,8 +2,18 @@
VALUE rb_mExampleExt;
+static VALUE
+example_hello(int argc, VALUE *argv)
+{
+ printf("hello\n");
+
+ return Qnil;
+}
+
void
Init_example_ext(void)
{
rb_mExampleExt = rb_define_module("ExampleExt");
+
+ rb_define_module_function(rb_mExampleExt, "hello", example_hello, -1);
}
bin/setup
bundle exec rake install
bundle exec ruby -e 'require "example_ext"; ExampleExt::hello'
# => hello
Die Ruby C-API wird selten dokumentiert. Der endgültige Leitfaden zur Ruby-C-API (http://silverhammermba.github.io/emberb/c/) wird als erster zu lesender Leitfaden empfohlen.
Erstellen Sie die C-Erweiterung mit deaktivierter Optimierung (-O0
) und mit Debug-Symbolen ( -ggdb3
).
cd ext/example_ext/
vim extconf.rb
ruby extconf.rb # specify -ggdb3 -O0
make V=1 # check -ggdb3 -O0
make clean
cd ../../
bundle exec rake install
diff --git a/ext/example_ext/extconf.rb b/ext/example_ext/extconf.rb
index f657c82..2ca74f1 100644
--- a/ext/example_ext/extconf.rb
+++ b/ext/example_ext/extconf.rb
@@ -1,3 +1,6 @@
require "mkmf"
+CONFIG["debugflags"] = "-ggdb3"
+CONFIG["optflags"] = "-O0"
+
create_makefile("example_ext/example_ext")
$ make V=1
gcc -I. ... -O0 -ggdb3 ... -o example_ext.o -c example_ext.c
...
COINFIG
entspricht RbConfig :: MAKEFILE_CONFIG
, in dem die Build-Konfiguration von ruby
gespeichert ist. Daher ist der obige Unterschied möglicherweise nicht erforderlich, aber stellen Sie sicher, dass "make V = 1" "-O0 -ggdb3" ist.
[Kompilierungsdatenbank (compile_commands.json
)](in bear und intercept-build Beim Erstellen (https://clang.llvm.org/docs/JSONCompilationDatabase.html) kann die statische Analyse wie folgt durchgeführt werden.
bundle exec rake clean && bundle exec bear rake build
jq '.' compile_commands.json > compile_commands.json.orig
jq --arg IPWD "-I$PWD" --arg IRUBY \
"-I$HOME/src/ruby/include" '.[].arguments |= [ .[0], $IPWD, $IRUBY, .[1:][] ]' \
compile_commands.json.orig > compile_commands.json
Versuchen Sie das Debuggen mit gdb
.
# (a) recommended:
bundle exec \
gdb -q -ex 'set breakpoint pending on' -ex 'b example_hello' -ex run --args ruby -e 'require "example_ext"; ExampleExt::hello'
# (b) or:
env RUBYLIB=./lib \
gdb -q -ex 'set breakpoint pending on' -ex 'b example_hello' -ex run --args ~/.rbenv/versions/trunk/bin/ruby -e 'require "example_ext"; ExampleExt::hello'
# (c) unrecommended:
gdb -q -ex 'set breakpoint pending on' -ex 'b example_hello' -ex run --args ~/.rbenv/versions/trunk/bin/ruby -e 'require "example_ext"; ExampleExt::hello'
TL; DR:
(a): In "bundle exec" erfordert "require_ext" (indirekt) "/ path / to / example_ext / example_ext.so" ("example_ext.bundle" unter macOS). Die Debug-Informationen für "example_ext.so" beziehen sich auf "/ path / to / example_ext / ext / example_ext / example_ext.c". In (b) müssen Sie die Position der "Ruby" -Binärdatei direkt angeben. Dies liegt daran, dass der Befehl "ruby" ohne "bundle exec" auf ein Shell-Skript verweist und von gdb nicht gelesen werden kann. Sie müssen auch RUBY_LIB = ./ lib
angeben. Ohne sie (c) wäre die geladene gemeinsam genutzte Bibliothek "~ / .rbenv / version / trunk /.../ example_ext.so" und debug ".rbenv / version / trunk /.../ example_ext.c". Wird gemacht [^ 1]. Auch in diesem Fall können Sie mit gdb set replace-path
auf die Originalquelle verweisen.
Versuchen Sie für weitere Details, den folgenden Befehl auszuführen.
echo $PATH
which -a ruby
bundle exec echo $PATH
bundle exec which -a ruby
file ~/.rbenv/shims/ruby
file ~/.rbenv/versions/trunk/bin/ruby
ruby -e 'p $:' # or $LOAD_PATH
bundle exec ruby -e 'p $:'
env RUBYLIB=./lib ruby -e 'p $:'
<img alt = "Creative Commons License" style = "Rahmenbreite: 0" src = "https" //i.creativecommons.org/l/by/4.0/88x31.png "/>
Diese Arbeit ist <eine rel =" Lizenz "href =" http://creativecommons.org/ Lizenzen / von / 4.0 / "> Creative Commons Attribution 4.0 International License .
Für Betreuer der offiziellen Dokumentation für verwandte Software: Wenn Sie den Inhalt dieses Artikels in die offizielle Dokumentation aufnehmen möchten, kommentieren Sie diesen Artikel oder Twitter @wata_ash. ) Bitte kontaktieren Sie uns.
[^ 1]: Die Task "Rake-Installation" führt die Task "Build" aus, die "gem install / path / to / example_ext / pkg / example_ext-0.1.0.gem" ausführt. gem install example_ext-0.1.0.gem
erweitert die C-Quelle auf ~ / .rbenv / version / trunk / lib / ruby / gems / 2.7.0 / gems / example_ext-0.1.0 /
und kompiliert dann Daher verweist der Quellpfad der Debug-Informationen auf "~ / .rbenv / version / trunk / lib / ruby / gems / 2.7.0 / gems / example_ext-0.1.0 / ext / example_ext / example_ext.c". Ich werde am Ende. Dies ist nicht dokumentiert, sondern die Quelle (1) (2) [(3)](https://github.com/rubygems/rubygems/blob/v3. 0.2 / lib / rubygems / installer.rb # L318) (4) Und Bundle Exec Ruby-Rtracer ~ / .rbenv / Versionen / Trunk / Bin / Rake - Trace installieren
.