Search code examples
crystal-lang

Can a Crystal library be statically linked to from C?


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?


Solution

  • 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