Is it possible to build libraries?

Hello everyone,

sorry, in case this question should have been answered already a dozen of times, but I simply couldn’t find it:

Is it possible to build libraries (.a/.so/.dylib) for further use in c/ ruby/ whatever?

1 Like

No

2 Likes

Well, it is technically possible. But not very practical.

The language and compiler totally enable you to do this. You can define fun methods and use --cross-compile to generate an object file which you can then link into a static or shared library as you wish.

But if you use the standard library you’ll meet some problems. Crystal’s stdlib is not made for that and it never will be. It expects to be the king of the process and manages everything about it, like memory management, signal handling etc.

You could compile your library without the standard library. But without it, there’s not much you can do. Basically, you have to implement everything you need for yourself.

3 Likes

What a pity. This would have been awesome in combination with ruby. Speeding “slow” parts up with crystal and keep everything else interpreted. I’m anyway very impressed by Crystal.

2 Likes

Thanks. Good to know. Even without stdlib I can still think of possible cases where this could become handy (like maybe just a tiny addon to some already external library or something like that).

To have such a combination, there is Anyolite. GitHub - Anyolite/anyolite: Embedded mruby/Ruby for Crystal
From the README: Anyolite is a Crystal shard which adds a fully functional mruby (or even regular Ruby) interpreter to Crystal.

4 Likes

In case anyone else is trying to do something like that, maybe this helps:

Well, this needs such a “do not try at home” (or don’t use it for anything important) warning, but if someone wants to fool around with it, this is how I got it working on osx (and I assume it will be the same or similar on linux).

# bar.cr
fun anotherHello : Void
  puts "Crystal says \"Hi\", too!"
end

fun seven(i: LibC::Int) : LibC::Int
  return 7*i
end

fun barInit : Void
  Crystal.main(0, Pointer(UInt8*).null)
end
// foo.cc
extern "C"{
  void anotherHello();
  int seven(int);
  void barInit();
  }
  
#include <iostream>
using namespace std;

int main() {
  barInit();
  cout << "Hello, World!" << endl;
  anotherHello();
  cout << seven(6) <<endl;
  return 0;
}
# building
crystal build --cross-compile bar.cr
ld -unexported_symbol _main -r -dynamic bar.o /opt/local/lib/libiconv.a /opt/local/lib/libpcre.a /opt/local/lib/libevent.a /opt/local/lib/libgc.a -o _bar.o
ar r libbar.a _bar.o
c++ foo.cc libbar.a -o foo

And the resulting ./foo seemed(!) to work fine, but might be not fine at all.

If you want to access argv, you’ll have to modify barInit (btw: it doesn’t work without barInit() - if I understood it correctly, it’s because GC.init is needed, which is done by Crystal.main; my attempts to replace it directly with GC.init failed).

Maybe it’s all fine, maybe it leaks memory, maybe it crashes the whole system - I didn’t recognise any issues, but maybe I was just ignorant or lucky or both, so better don’t rely on that.

Have fun!

2 Likes

if you build a dylib from ilbbar.o as in
c++ -shared _bar.o -o libbar.dylib

You can use your Crystal library in Ruby as in:

require 'ffi'
module Bar
  extend FFI::Library; ffi_lib 'libbar'; attach_function :seven, [:int], :int
end
Bar.seven 6
# => 42

:grinning:

Edit 1: While it worked fine in the above case, it doesn’t do so in other cases (I assume(!) those are the cases which require barInit, which itself already crashes as well when called via ruby ffi).

Edit 2: GC.init isn’t even the problem (it works apparently fine). One can’t use p or puts (but calling LibC.write 1,… is fine and a quick replacement, by overload those two) nor does string interpolation work; There will be likely more things one would have to replace, but it doesn’t seem there wouldn’t be many (I have found so far only those two/three issues, which might be all related just to the later (as p and puts probably use it). I did not experience any other issues with string, io, … So if someone really want to do this, it might not take much effort to get it working

1 Like

This may not be related to this question, but rubygem recently added an official gem template for writing native extensions in Rust.

Maybe someday when we have a best practice for writing Ruby extensions in Crystal, it will be added as an official RubyGem template.

1 Like