Forum: Ruby How to create a gem which conditionally compiles/installs a C extension?

Posted by Rocky B. (rocky_b)
on 2012-11-25 00:37
I have a gem that needs to compile and install a C extension on MRI
1.9.2. But on MRI 1.9.3 it doesn't. What's the best way to accomplish
this?

Here are some possibilities and the ramifications of them.

* Create the gem in such a way to distinguish MRI 1.9.3 versus MRI
1.9.2. I think this would be the cleanest, but I don't think this is
possible. Were this possible, in the gemspec one could
put something add like:

  Gem::Specification.new do |spec|
     spec.extensions   = ['ext/extconf.rb'] if '1.9.2' == RUBY_VERSION
     ...

* Create a bogus extension for 1.9.3. This forces users to have a C
compiler installed when it's really not needed. This also has the
possibly weird effect that if the gem was originally built for 1.9.3 and
one goes back to 1.9.2 and that extension is around and gems are shared
across 1.9.3 and 1.9.2 then this breaks. (The more expected direction of
building on 1.9.2 and upgrading to 1.9.3 would work however because on
1.9.3 the code doesn't try to load the extension). Perhaps this is too
much of an edge case to be concerned about.

* Figure out some way in the gem to dynamically disable compiling the
extension on MRI 1.9.3. For example in ext/extconf.rb:

   if '1.9.2' == RUBY_VERSION
        create_makefile(...)
   else
       ???
   end

Thoughts and comments?
Posted by Jon Forums (jonm)
on 2012-11-25 03:44
> * Figure out some way in the gem to dynamically disable compiling the
> extension on MRI 1.9.3. For example in ext/extconf.rb:
>
>    if '1.9.2' == RUBY_VERSION
>         create_makefile(...)
>    else
>        ???
>    end

`Gem::Ext::ExtConfBuilder` makes this difficult by (incorrectly)
assuming it should always run make after extconf.rb; you must create a
Makefile (even if it's just a no-op stub) and have an activated make on
your system.

I recently had a similar idea (comined pure-ruby and native gem with
build decision made at install time by extconf.rb) and briefly toyed
with wrapping the extconf.rb with a Rakefile. The quick hack didn't
work, and I haven't had time to swing back and dance with the little
monster.

  https://github.com/jonforums/ffi-bogus

Perhaps it just needs another set of eyes and a bit more time. That
said, a Rakefile + extconf.rb solution is a kludge. The right solution
is to find a clever way to make RubyGems more nimble and not break
existing extconf.rb builds.

Jon
Posted by Rocky B. (rocky_b)
on 2012-11-25 15:46
Jon Forums wrote in post #1086252:
>> * Figure out some way in the gem to dynamically disable compiling the
>> extension on MRI 1.9.3. For example in ext/extconf.rb:
>>
>>    if '1.9.2' == RUBY_VERSION
>>         create_makefile(...)
>>    else
>>        ???
>>    end
>
> `Gem::Ext::ExtConfBuilder` makes this difficult by (incorrectly)
> assuming it should always run make after extconf.rb; you must create a
> Makefile (even if it's just a no-op stub) and have an activated make on
> your system.
>
> I recently had a similar idea (comined pure-ruby and native gem with
> build decision made at install time by extconf.rb) and briefly toyed
> with wrapping the extconf.rb with a Rakefile. The quick hack didn't
> work, and I haven't had time to swing back and dance with the little
> monster.
>
>   https://github.com/jonforums/ffi-bogus
>
> Perhaps it just needs another set of eyes and a bit more time. That
> said, a Rakefile + extconf.rb solution is a kludge. The right solution
> is to find a clever way to make RubyGems more nimble and not break
> existing extconf.rb builds.

Agreed.

I couldn't see how to make it work either. So until something better
comes along I've gone with the bogus C extension approach when it is not
needed.

See https://github.com/rocky/rb-threadframe/tree/master/ext

>
> Jon
Posted by Jon Forums (jonm)
on 2012-11-26 00:32
>> I recently had a similar idea (comined pure-ruby and native gem with
>> build decision made at install time by extconf.rb) and briefly toyed
>> with wrapping the extconf.rb with a Rakefile. The quick hack didn't
>> work, and I haven't had time to swing back and dance with the little
>> monster.
>>
>>   https://github.com/jonforums/ffi-bogus
>>
>> Perhaps it just needs another set of eyes and a bit more time. That
>> said, a Rakefile + extconf.rb solution is a kludge. The right solution
>> is to find a clever way to make RubyGems more nimble and not break
>> existing extconf.rb builds.
>
> Agreed.
>
> I couldn't see how to make it work either. So until something better
> comes along I've gone with the bogus C extension approach when it is not
> needed.
>
> See https://github.com/rocky/rb-threadframe/tree/master/ext


When you get a moment would you try replacing

https://github.com/rubygems/rubygems/blob/master/l...

with this little hack

  return unless File.exist? 'Makefile'

and try your idea of not creating a Makefile from extconf.rb upon
certain conditions.

It assumes (a) all sucessful exits from extconf.rb (with or without
generating a Makefile) is what the author intended, and (b) all
extconf.rb failures will be handled as-is by RG.

When I get time I'll test on both my Win7 and Arch systems. If it turns
out to be this easy *and* solid, well...

Jon
Posted by Rocky B. (rocky_b)
on 2012-11-26 03:35
Rocky B. wrote in post #1086286:
> Jon Forums wrote in post #1086252:
>>> * Figure out some way in the gem to dynamically disable compiling the
>>> extension on MRI 1.9.3. For example in ext/extconf.rb:
>>>
>>>    if '1.9.2' == RUBY_VERSION
>>>         create_makefile(...)
>>>    else
>>>        ???
>>>    end
>>
>> `Gem::Ext::ExtConfBuilder` makes this difficult by (incorrectly)
>> assuming it should always run make after extconf.rb; you must create a
>> Makefile (even if it's just a no-op stub) and have an activated make on
>> your system.
>>
>> I recently had a similar idea (comined pure-ruby and native gem with
>> build decision made at install time by extconf.rb) and briefly toyed
>> with wrapping the extconf.rb with a Rakefile. The quick hack didn't
>> work, and I haven't had time to swing back and dance with the little
>> monster.
>>
>>   https://github.com/jonforums/ffi-bogus
>>
>> Perhaps it just needs another set of eyes and a bit more time. That
>> said, a Rakefile + extconf.rb solution is a kludge. The right solution
>> is to find a clever way to make RubyGems more nimble and not break
>> existing extconf.rb builds.
>
> Agreed.
>
> I couldn't see how to make it work either. So until something better
> comes along I've gone with the bogus C extension approach when it is not
> needed.
>
> See https://github.com/rocky/rb-threadframe/tree/master/ext
>
>>
>> Jon

Sigh.

The gems have been released and my patience for gem hacking has worn
very thin.

Tell you what... fork the code, patch it and submit a merge request on
github. Then I'll try it out in the various circumstances.

One potential problem I see (as mentioned previously) is that if one
goes from MRI 1.9.3 to 1.9.2 the extension won't be there and you'll get
an error. Right now what happens is that the C extension checks the Ruby
version and suggests reinstalling the gem. This is a slightly better 
failure message.

But again, what's really needed here is more and better gem intelligence
with regard to the version of Ruby that one is running.
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.