Permalink
Browse files

work

  • Loading branch information...
1 parent a996f43 commit 9e42020045eaecd672253508d25f9b854e24e578 @jdleesmiller committed Aug 25, 2011
Showing with 87 additions and 39 deletions.
  1. +87 −39 c-extensions.md
View
@@ -36,85 +36,104 @@ To add in the C extension, we'll go through a few steps.
<a id="tutorial-ext"> </a>
Create an `ext` folder
----------------------
-First we'll add one more folder, `ext`, that contains two files:
+
+Just as we put the ruby source files in the `lib/hola` folder, we put the C
+extension source files in `ext/hola`. This folder contains two files:
% tree ext
ext
- |-- extconf.rb
- `-- hola_ext.c
+ `-- hola
+ |-- extconf.rb
+ `-- hola.c
-`extconf.rb` is a script that describes how to build the extension:
+`extconf.rb` tells rubygems how to build the extension. For `hola`, it reads:
- % cat ext/extconf.rb
+ % cat ext/hola/extconf.rb
require 'mkmf'
- create_makefile('hola_ext')
+ create_makefile('hola/hola')
-When you install a gem that contains the code for a C extension (and has [the
-appropriate settings](#tutorial-gemspec) in its `gemspec`):
+`mkmf` is part of ruby's standard library; it automates the process of creating
+a [Makefile](http://en.wikipedia.org/wiki/Makefile) which, in turn, automates
+the process of building the extension. So, when rubygems installs a gem, it
+first runs the gem's `extconf.rb`, and then it runs `make` on the resulting
+`Makefile`.
-1. Rubygems calls its `extconf.rb`.
-2. `extconf.rb` creates a [Makefile](http://en.wikipedia.org/wiki/Makefile)
-using `mkmf`, which is part of the ruby standard library.
-3. Rubygems runs `make` to build the extension. The result is a [shared
-object](http://en.wikipedia.org/wiki/Shared_object) (DLL on Windows).
-4. Rubygems installs the shared object into the gem's `lib` folder.
+The output from `make` is a [dynamically linked
+library](http://en.wikipedia.org/wiki/Library_(computing)). In this case, the
+output is a file called `hola/hola.so`, on Linux, where `.so` stands for 'shared
+object'. The extension is platform-dependent; it would be `.dll` on Windows, or
+`.dylib` on Mac OS X. The name is what we pass to `create_makefile`.
-In this case, the shared object is `hola_ext.so` (on Linux), because the
-argument to `create_makefile` is `hola_ext`. In general, `<gem name>_ext` is a
-good name to use for the extension (see below).
+In general, a good convention for the name is `<gem name>/<gem name>`. While it
+is annoying to have to type the gem's name so many times, it will be seen that
+this is important for avoiding naming conflicts.
-`hola_ext.c` contains the C source for the extension:
+The content of the extension is in `hola.c`, which reads:
- % cat ext/hola_ext.c
+ % cat ext/hola/hola.c
#include <ruby.h>
-
+
/* our new native method; it just returns the string "bonjour!" */
static VALUE hola_bonjour(VALUE self) {
return rb_str_new2("bonjour!");
}
-
+
/* this is called by ruby when loading the extension */
- void Init_hola_ext(void) {
+ void Init_hola(void) {
/* assume we haven't yet defined class Hola */
VALUE klass = rb_define_class("Hola", rb_cObject);
/* the hola_bonjour function can be called from ruby as "Hola.bonjour" */
rb_define_singleton_method(klass, "bonjour", hola_bonjour, 0);
}
-Some references for ruby's C API are given at the end of guide. For now, the
-important features of `hola_ext.c` are:
+`ruby.h` provides declarations for ruby's C API. The `Makefile` generated by
+`mkmf` ensures that the compiler knows where this header file is. Some
+references for ruby's C API are given at the end of guide.
-1. It includes `ruby.h`, which provides declarations for the ruby C API. The
-Makefile generated by `mkmf` makes sure that the compiler knows where this
-header file is.
-2. It contains an `Init_<gem name>_ext` function; this is used to map ruby
-classes, modules and methods to C functions.
+Again, the name of the file is important, because it matches the second `hola`
+in the `hola/hola` that we passed to `create_makefile` in `extconf.rb`.
+Similarly, the name `Init_hola` is important, because ruby looks for a function
+with this name when it loads `hola/hola.so`.
<a id="tutorial-gemspec"> </a>
Modify the `gemspec`
--------------------
-To tell rubygems that a gem contains a C extension, we have to point it to
+To tell rubygems that a gem contains a C extension, we have to tell it about
`extconf.rb`, and we have to include the C source files in the `files` list.
% cat hola.gemspec
Gem::Specification.new do |s|
... (other stuff) ...
- s.files = Dir.glob('lib/**/*.rb') + Dir.glob('ext/*.c')
- s.extensions = ['ext/extconf.rb']
+ s.files = Dir.glob('lib/**/*.rb') + Dir.glob('ext/**/*{.c,.h}')
+ s.extensions = ['ext/hola/extconf.rb']
s.executables = ['hola']
... (other stuff) ...
end
-Rubygems automatically adds the extensions and executables to the files list, so
-you don't have to list them. (Here we're computing the `files` list
-dynamically; when we run `gem build` below, it will include all matching files.)
+Here we're computing the `files` list dynamically; when we run `gem build`
+below, it will include all matching files in the gem. Rubygems automatically
+adds the extensions and executables to the files list, so you don't have to list
+them under `files`.
+
+<a id="tutorial-load"> </a>
+Load the extension
+--------------------
+
+The final step is to load the shared object when the gem is loaded. To do this,
+we simply require `hola/hola` at the top of `lib/hola.rb`:
+
+ % cat lib/hola.rb
+ require 'hola/hola'
+
+ class Hola
+ ... (rest of file unchanged) ...
-At this point we can build and install the gem:
+At this point we can build, install and use gem:
% gem build hola.gemspec
Successfully built RubyGem
@@ -127,9 +146,9 @@ At this point we can build and install the gem:
1 gem installed
% irb -rubygems
ruby-1.8.7-p352 :001 > require 'hola'
- => true
+ => true
ruby-1.8.7-p352 :002 > Hola.bonjour
- => "bonjour!"
+ => "bonjour!"
The string `"bonjour!"` came from our C extension. Hooray!
@@ -174,6 +193,9 @@ automate the build process:
desc "Run tests"
task :default => :test
+(This should work on any *nix machine, but it would require some small
+modifications to run on Windows.)
+
Now typing `rake` will run the tests after building (or rebuilding) the
extension, as necessary:
@@ -192,5 +214,31 @@ extension, as necessary:
4 tests, 4 assertions, 0 failures, 0 errors
If the C source and `extconf.rb` build script have not changed, then running
-rake a second time results in only the test suite running.
+`rake` a second time runs only the test suite.
+
+Some other Guidelines
+=====================
+
+Extension Naming
+----------------
+
+Because rubygems copies the shared object (`.so`) file to the gem's `lib`
+directory when it is installed, loading the gem puts the
+
+
+Portability
+-----------
+
+This section needs help! [Please contribute.](http://github.com/rubygems/guides)
+
+Next Steps
+==========
+
+There are several other tutorials, including:
+* [a 5 minute tutorial on RubyInsider](http://www.rubyinside.com/how-to-create-a-ruby-extension-in-c-in-under-5-minutes-100.html)
+* [a longer tutorial](http://tenderlovemaking.com/2009/12/18/writing-ruby-c-extensions-part-1/)
+
+The main references for ruby's C API are:
+* [a chapter in the Pickaxe book](http://www.ruby-doc.org/docs/ProgrammingRuby/html/ext_ruby.html)
+* [the README.EXT file](https://github.com/ruby/ruby/blob/trunk/README.EXT)

0 comments on commit 9e42020

Please sign in to comment.