Compiling multiple C source files into a single library

D

Daniel Berger

Hi,

In the past I've always put all my code into a single source file and
built it that way. However, I'd like to split out my C source files
into a "one class per file" arrangement for a particular project, but
I'm not sure how to do it. I'm using rake-compiler on Snow Leopard,
btw.

Here's the layout I have:

my_project
Rakefile
ext/
foo/
extconf.rb
foo.c
bar.c

foo.c and bar.c are pretty basic C source files:

// foo.c
#include <ruby.h>

void Init_foo(){
VALUE cFoo = rb_define_class("Foo", rb_cObject);
rb_define_const(cFoo, "VERSION", rb_str_new2("0.0.1"));
}

// bar.c
#include <ruby.h>

// I also tried Init_foo here, but that lead to a compiler error
void Init_bar(){
VALUE cBar = rb_define_class("Bar", rb_cObject);
rb_define_const(cBar, "VERSION", rb_str_new2("0.0.1"));
}

The extconf.rb file is just "require 'mkmf'; create_makefile('foo')".

The Rake task is simply "Rake::ExtensionTask.new('foo')".

Running "rake compile:stuff" builds like so:

gcc -I. -I/opt/ree/lib/ruby/1.8/i686-darwin10.2.0 -I/opt/ree/lib/ruby/
1.8/i686-darwin10.2.0 -I../../../../ext/foo -D_XOPEN_SOURCE -
D_DARWIN_C_SOURCE -fno-common -g -Os -fno-strict-aliasing -pipe -
fno-common -c ../../../../ext/foo/bar.c
gcc -I. -I/opt/ree/lib/ruby/1.8/i686-darwin10.2.0 -I/opt/ree/lib/ruby/
1.8/i686-darwin10.2.0 -I../../../../ext/foo -D_XOPEN_SOURCE -
D_DARWIN_C_SOURCE -fno-common -g -Os -fno-strict-aliasing -pipe -
fno-common -c ../../../../ext/foo/foo.c
cc -dynamic -bundle -undefined suppress -flat_namespace -o foo.bundle
bar.o foo.o -L. -L/opt/ree/lib -L. -ldl -lobjc
cd -
cp tmp/i686-darwin10.2.0/foo/1.8.7/foo.bundle lib/foo.bundle

But, if I run the following test.rb program it fails:

$:.unshift 'lib'
require 'stuff' # ok
p Foo::VERSION # ok
p Bar::VERSION # fail, uninitialized constant Bar

Why isn't Bar visible? What's the right way to do what I'm attempting?

Regards,

Dan
 
A

Aaron Patterson

Hi,

In the past I've always put all my code into a single source file and
built it that way. However, I'd like to split out my C source files
into a "one class per file" arrangement for a particular project, but
I'm not sure how to do it. I'm using rake-compiler on Snow Leopard,
btw.

Here's the layout I have:

my_project
Rakefile
ext/
foo/
extconf.rb
foo.c
bar.c

foo.c and bar.c are pretty basic C source files:

// foo.c
#include <ruby.h>

void Init_foo(){
VALUE cFoo = rb_define_class("Foo", rb_cObject);
rb_define_const(cFoo, "VERSION", rb_str_new2("0.0.1"));
}

Call Init_bar() from Init_foo(). Ruby will only call one function when
your extension loads. The "Init_*" function maps to the name of the
shared object you build. So if you build "foo.bundle", ruby will call
"Init_foo", and only "Init_foo". Other initialization is up to you.
// bar.c
#include <ruby.h>

// I also tried Init_foo here, but that lead to a compiler error
void Init_bar(){
VALUE cBar = rb_define_class("Bar", rb_cObject);
rb_define_const(cBar, "VERSION", rb_str_new2("0.0.1"));
}

The extconf.rb file is just "require 'mkmf'; create_makefile('foo')".

The create_makefile() call determines the name of the shared object
built. Since you're building "foo.bundle", *only* Init_foo will be
called.
The Rake task is simply "Rake::ExtensionTask.new('foo')".

Running "rake compile:stuff" builds like so:

gcc -I. -I/opt/ree/lib/ruby/1.8/i686-darwin10.2.0 -I/opt/ree/lib/ruby/
1.8/i686-darwin10.2.0 -I../../../../ext/foo -D_XOPEN_SOURCE -
D_DARWIN_C_SOURCE -fno-common -g -Os -fno-strict-aliasing -pipe -
fno-common -c ../../../../ext/foo/bar.c
gcc -I. -I/opt/ree/lib/ruby/1.8/i686-darwin10.2.0 -I/opt/ree/lib/ruby/
1.8/i686-darwin10.2.0 -I../../../../ext/foo -D_XOPEN_SOURCE -
D_DARWIN_C_SOURCE -fno-common -g -Os -fno-strict-aliasing -pipe -
fno-common -c ../../../../ext/foo/foo.c
cc -dynamic -bundle -undefined suppress -flat_namespace -o foo.bundle
bar.o foo.o -L. -L/opt/ree/lib -L. -ldl -lobjc
cd -
cp tmp/i686-darwin10.2.0/foo/1.8.7/foo.bundle lib/foo.bundle

But, if I run the following test.rb program it fails:

$:.unshift 'lib'
require 'stuff' # ok
p Foo::VERSION # ok
p Bar::VERSION # fail, uninitialized constant Bar

Why isn't Bar visible? What's the right way to do what I'm attempting?

Just call Init_bar() from inside Init_foo().

Hope that helps!
 
D

Daniel Berger

Call Init_bar() from Init_foo(). =A0Ruby will only call one function when
your extension loads. =A0The "Init_*" function maps to the name of the
shared object you build. =A0So if you build "foo.bundle", ruby will call
"Init_foo", and only "Init_foo". =A0Other initialization is up to you.




The create_makefile() call determines the name of the shared object
built. =A0Since you're building "foo.bundle", *only* Init_foo will be
called.









Just call Init_bar() from inside Init_foo().

Hope that helps!

Aha, thanks!

Dan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top