Crystal installation using Linuxbrew is not working

Hello!

I try to follow the steps from this guide and install Crystal using brew on linux.
For this command brew install crystal-lang --with-llvm I get Error: invalid option: --with-llvm.
And if I use this one brew install crystal-lang it installs fine, although for the next step on hello world program I get this error.

Installation using snap works just fine though :ok_hand:

I can reproduce a similar issue on an empty ubuntu image with homebrew installed:

$ docker run --rm homebrew/ubuntu22.04
# brew install crystal
# crystal run 'puts %(hello world)'
/home/linuxbrew/.cache/crystal/crystal-run-eval.tmp: error while loading shared libraries: libgc.so.1: cannot open shared object file: No such file or directory

In your case, there is probably a libgc.so.1 available in your path, but it’s not from homebrew and an older version missing the GC_get_my_stackbottom symbol.

A workaround is to specify the homebrew lib directory explicitly:

export LD_LIBRARY_PATH=/home/linuxbrew/.linuxbrew/lib

The homebrew package certainly used to work. It’s unclear why it doesn’t anymore.
In the formular there is even a test to ensure compilation works (homebrew-core/Formula/c/crystal.rb at 7778fbcc32cbbee53c6b75e2e352cfef8b3c00e4 · Homebrew/homebrew-core · GitHub).

Probably the homebrew formula should configure CRYSTAL_LIBRARY_RPATH to point to homebrew’s lib directory. That should bake the lookup paths into the binary so it can find them even if homebrew’s lib directory is not in ld’s lookup path.

You can do that manually with

export CRYSTAL_LIBRARY_RPATH=$(brew --prefix)/lib

Thanks for taking a look on this :pray:
Not sure if it’s related, but I see the different LLVM version bundled into the snap package.
15.0.7 instead of 17.0.6
image

The LLVM version is inconsequential for this porblem.

I submitted a patch to the homebrew formula:

1 Like

If delay-loading is removed on Windows, that would mean deprecating CRYSTAL_LIBRARY_RPATH on Linux too. On the other hand I don’t know if a better solution exists (CRYSTAL_CONFIG_BUILD_OPTS…?)

Yeah I think we’ll need a feature to configure RPATH. And keeping the existing config variable should be good.

I went to see how runtim linking for binaries compiled with GCC from homebrew work. That should have the same need to find the libraries in homebrew lib.

$ brew install gcc
$ cat << EOF > test.c
unsigned GC_get_version(void);

int main() {
  GC_get_version();

  return 0;
}
EOF
$ gcc-13 test.c -o test -lgc
$ readelf -a test | grep -i linuxbrew
      [Requesting program interpreter: /home/linuxbrew/.linuxbrew/lib/ld.so]
 0x000000000000001d (RUNPATH)            Library runpath: [/home/linuxbrew/.linuxbrew/lib/gcc/current:/home/linuxbrew/.linuxbrew/lib]

And indeed, the runpath is configured accordingly.

It seems the GCC formular is essentially doing the same thing as my RPATH patch: configure an implicit rpath that points to the homebrew lib dir for all produced binaries.

Additionally, in binaries built by GCC, the program interpreter als points to ld.so from homebrew. Not sure what’s the specific purpose of that, though. Rpath is sufficient for library discovery. But I guess it generally makes sense to use homebrew’s linker as well.
I don’t think there’s a way to configure that in Crystal.
Maybe via forwarding --link-flags…?

So as long as Crystal is using Brew’s linker, isn’t there no need for us to explicitly use Brew’s directories as the RUNPATH? A similar thing happens on Termux and Crystal doesn’t need this config variable over there

Oh, yeah that could be a great idea. I just checked this:

$ CC=$(brew --prefix)/bin/gcc-13 crystal build hello.cr
$ readelf -a empty | grep -i linuxbrew/lib/
      [Requesting program interpreter: /home/linuxbrew/.linuxbrew/lib/ld.so]
 0x000000000000001d (RUNPATH)            Library runpath: [/home/linuxbrew/.linuxbrew/lib/gcc/current:/home/linuxbrew/.linuxbrew/lib]

So this has exactly the same result as the C binary. No need for explicit runpath configuration.

However, there is currently no way to configure the compiler’s default linker. I suppose it should be pretty straightforward to introduce CRYSTAL_CONFIG_CC, though.

This would require adding gcc as a dependency of the Crystal formula. Currently it is not a dependency. Crystal just uses the default cc, which may come from a system package.

Thank you for looking into this issue. Unfortunately, I’m still encountering the same error after following the test commands.

$ docker run -it homebrew/ubuntu22.04
$ brew install crystal
$ crystal eval 'puts %(hello world)'
/home/linuxbrew/.cache/crystal/crystal-run-eval.tmp: error while loading shared libraries: libgc.so.1: cannot open shared object file: No such file or directory

Please let me know if I am missing something.

I’m also curious if the brew install crystal-lang --with-llvm should be fixed as well. I don’t see the --with-llvm option inside brew package. Is it obsolete for the current version?

I guess we’ll need a new revision for the updated formula to actually roll out.

Probably, yes.