Picking apart Rubygems and Rake

I’m prepping for the 2.0.1 release of rubygame, which includes a number of bug fixes and improvements under the hood, especially in the way method arguments are processed in C methods. All the code is ready and waiting to be packaged up, but I’m tinkering with the Rakefile to make it play well with Rubygems, especially on win32 platforms.

In the 2.0.0 release, there were some snags supporting win32 users. The precompiled binaries were packaged up in gems so users wouldn’t have to compile rubygame for themselves (this is more of an issue on win32 than on linux, which has the GNU C compiler available as a general rule). Unfortunately, the gem didn’t work correctly for all users: despite the precompiled binaries being present, the Rakefile didn’t think they had been compiled, and would throw a fit when it was unable to compile them for itself.

The problem seems to be that some ruby installations on win32 expect dynamic libraries to have the .so extension (in *nix fashion), while others expect .dll (in win32 fashion). The libraries in the rubygame gem end with .so, but the Rakefile takes its hints from the ruby installation’s config: on some systems, it would be looking for .dll instead!

My first thought, then, was to have the Rakefile rename the libraries at install-time to conform to the system’s expectations. Even though they have the .so extension, the libraries are win32-style DLLs inside, so I don’t expect ruby to get confused when it tries to load them (it seems to ignore the extension anyway).

But here’s the catch: when using rubygems, the install task isn’t run. I had read online somewhere that rubygems expects the Rakefile for compiled ruby extension to respond to two tasks: extension (for compiling) and install (for installing to the appropriate directory). It turns out, though, that it presently only uses the extension task. (This I discovered only after rummaging through the source code for rubygems last night.)

Fortunately, this is not a big problem, because I can prepend a fix_filenames task as a dependency of the extension task, right before the build task (which compiles the libraries if doesn’t see that they have already been compiled):

1
2
3
4
5
6
7
8
9
10
11
12

desc "(Called when installing via Rubygems)"
task :extension => [:fix_filenames, :build]

task :fix_filenames do
  Rake::Task[:install_ext].prerequisites.each do |prereq|
    prereq = prereq.ext('so')
    if File.exist? prereq
      mv prereq prereq.ext(DLEXT)
    end
  end
end

This takes each of the prerequisites to the install_ext task (each prereq is a String pathname to a compiled library file), checks whether the file exists with a .so extension, and if so, renames it to the system extension for dynamic libraries (DLEXT is derived from Config::CONFIG[‘DLEXT’]).

Let’s see if that does the trick.


UPDATE: One more tweak is needed to the fix_filenames task, because the mv command will complain if the source and destination are the same, which causes the task to fail on systems which actually *do* use .so, like Linux. Here’s a corrected version:

1
2
3
4
5
6
7
8
9
10
11

task :fix_filenames do
  unless DLEXT == 'so'
    Rake::Task[:install_ext].prerequisites.each do |prereq|
      prereq = prereq.ext('so')
      if File.exist? prereq
        mv prereq prereq.ext(DLEXT)
      end
    end
  end
end

Ramble: Curves Roadblock

I’ve been pounding my head against the wall over these Bézier curves for the past couple days. For a change of pace, I’m going to pound my head against this blog instead, and hope that something useful falls out.

The overall challenge is to preserve the apparent position of an object (i.e. the character) along the curve while dragging one of the handles.

Currently, the character’s position is defined between 0.0 and 1.0, representing how far along the entire curve he is (0.5 being halfway around; for a closed curve, 0.0 and 1.0 are at the same location).

The character should seem to stick to a point on the curve, moving as that point moves. This means that the character’s actual position along the curve needs to be adjusted to compensate for the change in overall curve length.

Without such adjustment, the character will seem to slide along the curve, even if the curve is being modified somewhere else (because the curve’s overall length will change).

One possible solution would be to modify how the character defines its position. If it were defined as the position 0.0 to 1.0 along an individual segment, then modifying other segments would not affect the character’s position. More, modifying the segment that the character is on would cause the character to move along with the segment as it’s stretched/squashed. Problems solved.

(Even better than defining 0.0 to 1.0 along a segment, would be to define 0.0 to L, the length of the segment. This will make it easier to have the character seem to move at a constant speed.)

This somewhat complicates the way the character’s true position is defined, because it will have to remember which segment it is on, and find out what the next segment is when it walks off of the current one. This can be accomplished by having each segment remember the previous and next segments in the chair (i.e. linked list of segments).

This entails a substantial rearranging of how the curve system is currently defined. Much of the power and responsibility of th Curve class will be shifted off to other classes, especially Segment.