Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions

  




 

 

Ruby Programming
Previous Page Home Next Page

Creating a Makefile with extconf.rb

Figure 17.2 on page 182 shows the overall workflow when building an extension. The key to the whole process is the extconf.rb program which you, as a developer, create. In extconf.rb, you write a simple program that determines what features are available on the user's system and where those features may be located. Executing extconf.rb builds a customized Makefile, tailored for both your application and the system on which it's being compiled. When you run the make command against this Makefile, your extension is built and (optionally) installed.

The simplest extconf.rb may be just two lines long, and for many extensions this is sufficient.

require 'mkmf'
create_makefile("Test")

The first line brings in the mkmf library module (documented beginning on page 451). This contains all the commands we'll be using. The second line creates a Makefile for an extension called ``Test.'' (Note that ``Test'' is the name of the extension; the makefile will always be called ``Makefile.'') Test will be built from all the C source files in the current directory.

Let's say that we run this extconf.rb program in a directory containing a single source file, main.c. The result is a Makefile that will build our extension. On our system, this contains the following commands.

gcc -fPIC -I/usr/local/lib/ruby/1.6/i686-linux -g -O2  \
  -c main.c -o main.o
gcc -shared -o Test.so main.o -lc

The result of this compilation is Test.so, which may be dynamically linked into Ruby at runtime with ``require''. See how the mkmf commands have located platform-specific libraries and used compiler-specific options automatically. Pretty neat, eh?

Although this basic program works for many simple extensions, you may have to do some more work if your extension needs header files or libraries that aren't included in the default compilation environment, or if you conditionally compile code based on the presence of libraries or functions.

A common requirement is to specify nonstandard directories where include files and libraries may be found. This is a two-step process. First, your extconf.rb should contain one or more dir_config commands. This specifies a tag for a set of directories. Then, when you run the extconf.rb program, you tell mkmf where the corresponding physical directories are on the current system.

If extconf.rb contains the line dir_config( name ), then you give the location of the corresponding directories with the command-line options:

--with-name-include=directory

* Add directory/include to the compile command.
--with-name-lib=directory

* Add directory/lib to the link command.

If (as is common) your include and library directories are both subdirectories of some other directory, and (as is also common) they're called include and lib, you can take a shortcut:

--with-name-dir=directory

* Add directory/lib and directory/include to the link command and compile command, respectively.

There's a twist here. As well as specifying all these --with options when you run extconf.rb, you can also use the --with options that were specified when Ruby was built for your machine. This means you can find out the locations of libraries that are used by Ruby itself.

To make all this concrete, lets say you need to use libraries and include files for the CD jukebox we're developing. Your extconf.rb program might contain

require 'mkmf'
dir_config('cdjukebox')
# .. more stuff
create_makefile("CDJukeBox")

You'd then run extconf.rb with something like:

% ruby extconf.rb --with-cdjukebox-dir=/usr/local/cdjb

The generated Makefile would assume that the libraries were in /usr/local/cdjb/lib and the include files were in /usr/local/cdjb/include.

The dir_config command adds to the list of places to search for libraries and include files. It does not, however, link the libraries into your application. To do that, you'll need to use one or more have_library or find_library commands.

have_library looks for a given entry point in a named library. If it finds the entry point, it adds the library to the list of libraries to be used when linking your extension. find_library is similar, but allows you to specify a list of directories to search for the library.

require 'mkmf'
dir_config('cdjukebox')
have_library('cdjb', 'CDPlayerNew')
create_makefile("CDJukeBox")

On some platforms, a popular library may be in one of several places. The X Window system, for example, is notorious for living in different directories on different systems. The find_library command will search a list of supplied directories to find the right one (this is different from have_library, which uses only configuration information for the search). For example, to create a Makefile that uses X Windows and a jpeg library, extconf.rb might contain

require 'mkmf'

if have_library("jpeg","jpeg_mem_init") and    find_library("X11", "XOpenDisplay", "/usr/X11/lib",                 "/usr/X11R6/lib", "/usr/openwin/lib") then     create_makefile("XThing") else     puts "No X/JPEG support available" end

We've added some additional functionality to this program. All of the mkmf commands return false if they fail. This means that we can write an extconf.rb that generates a Makefile only if everything it needs is present. The Ruby distribution does this so that it will try to compile only those extensions that are supported on your system.

You also may want your extension code to be able to configure the features it uses depending on the target environment. For example, our CD jukebox may be able to use a high-performance MP3 decoder if the end user has one installed. We can check by looking for its header file.

require 'mkmf'
dir_config('cdjukebox')
have_library('cdjb', 'CDPlayerNew')
have_header('hp_mp3.h')
create_makefile("CDJukeBox")

We can also check to see if the target environment has a particular function in any of the libraries we'll be using. For example, the setpriority call would be useful but isn't always available. We can check for it with:

require 'mkmf'
dir_config('cdjukebox')
have_func('setpriority')
create_makefile("CDJukeBox")

Both have_header and have_func define preprocessor constants if they find their targets. The names are formed by converting the target name to uppercase and prepending ``HAVE_''. Your C code can take advantage of this using constructs such as:

#if defined(HAVE_HP_MP3_H)
#  include <hp_mp3.h>
#endif

#if defined(HAVE_SETPRIORITY)   err = setpriority(PRIOR_PROCESS, 0, -10) #endif

If you have special requirements that can't be met with all these mkmf commands, your program can directly add to the global variables $CFLAGS and $LFLAGS, which are passed to the compiler and linker, respectively.
Ruby Programming
Previous Page Home Next Page

 
 
  Published under the terms of the Open Publication License Design by Interspire