20

I've read through the "C bindings" in the tutorial but I'm a novice at C stuff.

Could someone please let me know if a Crystal program can be built as a static library to link to, and if so could you please provide a simple example?

Lye Fish
  • 2,538
  • 15
  • 25

1 Answers1

29

Yes, but it is not recommended to do so. Crystal depends on a GC which makes it less desirable to produce shared (or static) libraries. Thus there are also no syntax level constructs to aid in the creation of such nor a simple compiler invocation to do so. The C bindings section in the documentation is about making libraries written in C available to Crystal programs.

Here's a simple example anyhow:

logger.cr

fun init = crystal_init : Void
  # We need to initialize the GC
  GC.init

  # We need to invoke Crystal's "main" function, the one that initializes
  # all constants and runs the top-level code (none in this case, but without
  # constants like STDOUT and others the last line will crash).
  # We pass 0 and null to argc and argv.
  LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null)
end

fun log = crystal_log(text: UInt8*): Void
  puts String.new(text)
end

logger.h

#ifndef _CRYSTAL_LOGGER_H
#define _CRYSTAL_LOGGER_H

void crystal_init(void);
void crystal_log(char* text);
#endif

main.c

#include "logger.h"

int main(void) {
  crystal_init();
  crystal_log("Hello world!");
}

We can create a shared library with

crystal build --single-module --link-flags="-shared" -o liblogger.so

Or a static library with

crystal build logger.cr --single-module --emit obj
rm logger # we're not interested in the executable
strip -N main logger.o # Drop duplicated main from the object file
ar rcs liblogger.a logger.o

Let's confirm our functions got included

nm liblogger.so | grep crystal_
nm liblogger.a | grep crystal_

Alright, time to compile our C program

# Folder where we can store either liblogger.so or liblogger.a but
# not both at the same time, so we can sure to use the right one
rm -rf lib
mkdir lib
cp liblogger.so lib
gcc main.c -o dynamic_main -Llib -llogger
LD_LIBRARY_PATH="lib" ./dynamic_main

Or the static version

# Folder where we can store either liblogger.so or liblogger.a but
# not both at the same time, so we can sure to use the right one
rm -rf lib
mkdir lib
cp liblogger.a lib
gcc main.c -o static_main -Llib -levent -ldl -lpcl -lpcre -lgc -llogger
./static_main

With much inspiration from https://gist.github.com/3bd3aadd71db206e828f

Jonne Haß
  • 4,792
  • 18
  • 30
  • Thanks so much! This is for a single yet larger utility in a program that gets called only once, so there's minimal interaction. It's actually called from a *Go* program and the utility is being ported from *Nim*. If this part works out, I'll likely port the *Go* program to *Crystal* as well but this will work in the meantime. Thanks again! – Lye Fish Oct 03 '15 at 15:26
  • You've got all the new-breed systems programming languages covered in that Lye, all you need is some Rust and you've got the whole collection lol. Anyway @Jonne Haß , excellent summary. – Shayne May 06 '16 at 06:20
  • @Jonne Haß: the `-N` flag to `strip` is incorrect on OSX (probably because BSD.) Do you know what the OSX equivalent is? – andrewdotnich Sep 14 '16 at 00:39
  • @LyeFish why not add V too? – iconoclast May 11 '21 at 17:58