Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I produce a Crystal executable with no dependencies?

I'm writing a program in Crystal, that I intend to compile and move to other systems for execution. Ideally, it should have no dependencies, as the target systems will be fresh installations of linux.

Sadly, I can't get around the libc dependency, so I will presumably have to compile the executable on a system possessing the lowest version of libc I wish to target. I figure it should be forward-compatible.

I'm having difficulty with libssl, however. Default installations of Debian Wheezy don't seem to come with libssl, so I get this error when running my executable:

error while loading shared libraries: libssl.so.1.0.0:
cannot open shared object file: No such file or directory

I assume this dependency exists because I require "http/client" in my source. However, I make no ssl-related calls, as I only use it to connect to unsecured websites.

I apparently also have a dependency on libevent-2.0.so.5. Presumably all Crystal programs do. Who knows how many other dependencies Crystal has?

My executables have to run on a freshly-installed linux system. So, how can I produce a Crystal executable with no dependencies? Other than libc, I suppose.

like image 993
Sod Almighty Avatar asked Feb 03 '16 00:02

Sod Almighty


2 Answers

In Linux, you can list the shared libraries that an executable needs using the ldd command. In OSX, otool -L could be used for the same purpose.

Normally, the linker will use shared libraries while building the executable if it can find them. So, what you need to do, is force the linker to use static libraries instead. (In the future we might add a flag to the compiler to force this choice)

You should find some of these static libraries in /opt/crystal/embedded/lib. We use these to generate a portable Crystal compiler.

In order to use these libraries you can run:

$ LIBRARY_PATH=/opt/crystal/embedded/lib crystal build my_app.cr

That should prefer libraries available in that directory before considering others installed in the standard locations.

Unfortunately, OpenSSL is not distributed with Crystal, so you must either copy or build a static version of libssl and libcrypto. It's a common library anyway, available in any Linux distribution.

Regarding libc, that's more tricky. We compile the Crystal binary for releases using old CentOS and Debian distributions to make it compatible with many more libc versions.

like image 117
waj Avatar answered Oct 22 '22 11:10

waj


The Crystal docs mention that the --static compiler flag currently only works on Alpine Linux (and recommends using an Alpine Linux Docker container).

If you are not into Docker but happen to have Vagrant on your machine, you can use my Alpine box (relativkreativ/alpine) instead of going through the hassle of building your own.

In either case, static linking works on Alpine.

Just run the following steps:

  • Enable the community repository in /etc/apk/repositories
  • Run apk update the get the latest package list
  • Install Crystal with apk add crystal shards
  • Pull in the correct libc with apk add libc-dev (a meta-package which does just that)

Once this is done, you can compile your Crystal project with crystal build src/FILE.cr -o bin/FILE --release --static.

Now having a Crystal binary with no dependencies allows you to easily distribute your project as RPM, for example (which is how I found this question in the first place).

I also summarised this in an article on my website.

like image 5
Michael Trojanek Avatar answered Oct 22 '22 09:10

Michael Trojanek