# Issue with Running Crystal Executable on Another macOS Machine

**URL:** <https://forum.crystal-lang.org/t/issue-with-running-crystal-executable-on-another-macos-machine/7080>\
**Category:** Help & Support\
**Created:** [August 8, 2024, 11:04pm UTC](https://forum.crystal-lang.org/t/issue-with-running-crystal-executable-on-another-macos-machine/7080 "2024-08-08T23:04:38Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![galapagdos](https://avatars.discourse-cdn.com/v4/letter/g/ac91a4/32.png) [@galapagdos](https://forum.crystal-lang.org/u/galapagdos)\
**Post date:** [August 8, 2024, 11:04pm UTC](https://forum.crystal-lang.org/t/issue-with-running-crystal-executable-on-another-macos-machine/7080/1 "2024-08-08T23:04:38Z")

</div>

I’m currently working on a Crystal project, and I’ve run into an issue when trying to run my compiled binary on another macOS machine. The application throws the following error:

```auto
dyld[29586]: Library not loaded: /usr/local/opt/pcre2/lib/libpcre2-8.0.dylib
  Referenced from: <2270A1BE-CF50-31DF-8947-32B67C9A0FCD> /Users/SOMEONE/Downloads/SOMEExECUTABLE
  Reason: tried: '/usr/local/opt/pcre2/lib/libpcre2-8.0.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/usr/local/opt/pcre2/lib/libpcre2-8.0.dylib' (no such file), '/usr/local/opt/pcre2/lib/libpcre2-8.0.dylib' (no such file)
zsh: abort ./SOMEPROGRAM

```

The issue arises because the target machine does not have `libpcre2-8.0.dylib` installed in the expected location.  
I compiled the program with the following command to link the static library:

```auto
crystal build your_program.cr --release --link-flags="/usr/local/opt/pcre2/lib/libpcre2-8.a"

```

However, it seems like the binary still requires the dynamic library at runtime.

**My questions are:**

1. **How can I fully statically link `libpcre2` and other dependencies so that the binary does not require the dynamic library at runtime?**
2. **Is there a way to compile my Crystal program to avoid needing `pcre2` altogether?**
3. **Are there best practices for creating a fully portable Crystal binary on macOS?**

Any guidance on how to solve this issue would be greatly appreciated!

Thank you!

---

<div class="post-metadata">

**Author:** ![kojix2](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/kojix2/32/1583_2.png) [@kojix2](https://forum.crystal-lang.org/u/kojix2)\
**Post date:** [August 9, 2024, 12:45am UTC](https://forum.crystal-lang.org/t/issue-with-running-crystal-executable-on-another-macos-machine/7080/2 "2024-08-09T00:45:32Z")

</div>

As you may already know, the official Crystal documentation says

[https://crystal-lang.org/reference/1.13/guides/static\_linking.html#macos](https://crystal-lang.org/reference/1.13/guides/static_linking.html#macos)

> macOS[¶](https://crystal-lang.org/reference/1.13/guides/static_linking.html#macos)
> 
> macOS doesn’t [officially support fully static linking](https://developer.apple.com/library/content/qa/qa1118/_index.html) because the required system libraries are not available as static libraries.

The solutions for this issue are

1. Using the install\_name\_tool command to modify the path of the dylib.
2. Preparing a Homebrew tap for your tool.
3. Creating an [App Bundle](https://en.wikipedia.org/wiki/Bundle_(macOS)).

Many Mac users manage their tools with Homebrew, so option 2 is simple.  
I haven’t tried option 3, so I hope someone more experienced can explain how to do it.

```auto
MyApp.app/
└── Contents/
    ├── MacOS/
    │ ├── my_executable # Crystal executable file
    │ └── launch.sh # Shell script
    ├── Frameworks/
    │ └── libpcre2-8.0.dylib # Required library
    └── Info.plist # Metadata file

```

---

<div class="post-metadata">

**Author:** ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)\
**Post date:** [August 9, 2024, 5:20am UTC](https://forum.crystal-lang.org/t/issue-with-running-crystal-executable-on-another-macos-machine/7080/3 "2024-08-09T05:20:27Z")

</div>

macOS doesn’t support _fully_ statically linked binaries, but the missing part is the system library. It should be entirely possible to statically link `libpcre2`.

> [@galapagdos](#):
>
> 1. **How can I fully statically link `libpcre2` and other dependencies so that the binary does not require the dynamic library at runtime?**

Following the instructions on [Static Linking - Crystal](https://crystal-lang.org/reference/1.13/guides/static_linking.html). You’ll need a static build of libpcre2 for that.

> [@galapagdos](#):
>
> 1. **Is there a way to compile my Crystal program to avoid needing `pcre2` altogether?**

`libpcre2` is used for regular expressions. If your program doesn’t use regular expressions, you wouldn’t need `libpcre2`.

> [@galapagdos](#):
>
> 1. **Are there best practices for creating a fully portable Crystal binary on macOS?**

Fully portable builds are impossible on macOS because there is no static version of the system library.  
So portability only reaches as far as the system library is binary compatible with other versions of the operating system.  
So there’s some limit.

I don’t have much experience with building for macOS, maybe others can chip in on that.

---

<div class="post-metadata">

**Author:** ![aboulafia](https://avatars.discourse-cdn.com/v4/letter/a/f19dbf/32.png) [@aboulafia](https://forum.crystal-lang.org/u/aboulafia)\
**Post date:** [August 10, 2024, 4:07pm UTC](https://forum.crystal-lang.org/t/issue-with-running-crystal-executable-on-another-macos-machine/7080/4 "2024-08-10T16:07:06Z")

</div>

For what it’s worth I don’t seem to have problems with static builds on macOS

Dynamic build :

```auto
crystal build my_toy_lang.cr
otool -L my_toy_lang
/opt/local/lib/libpcre2-8.0.dylib (compatibility version 14.0.0, current version 14.0.0)|
/opt/local/lib/libgc.1.dylib (compatibility version 7.0.0, current version 7.3.0)|
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1336.61.1)|
/opt/local/lib/libevent-2.1.7.dylib (compatibility version 8.0.0, current version 8.1.0)|
/opt/local/lib/libiconv.2.dylib (compatibility version 9.0.0, current version 9.1.0)|

```

Static build :

```auto
crystal build my_toy_lang.cr --static
otool -L my_toy_lang
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1336.61.1)

```

Only the system lib is still dynamic but not the other ones (and obviously the size of the executable increases with static build)

---

<div class="post-metadata">

**Author:** ![galapagdos](https://avatars.discourse-cdn.com/v4/letter/g/ac91a4/32.png) [@galapagdos](https://forum.crystal-lang.org/u/galapagdos)\
**Post date:** [August 10, 2024, 8:46pm UTC](https://forum.crystal-lang.org/t/issue-with-running-crystal-executable-on-another-macos-machine/7080/5 "2024-08-10T20:46:41Z")

</div>

I din’t need regex at all, even basic program as:  
puts “Hello World!” is using it as I can see in otool:

```auto
otool -L ./helloworld      
./helloworld:
        /opt/homebrew/opt/pcre2/lib/libpcre2-8.0.dylib (compatibility version 14.0.0, current version 14.0.0)

```

Is there’s a way to build it without this library?

---

<div class="post-metadata">

**Author:** ![galapagdos](https://avatars.discourse-cdn.com/v4/letter/g/ac91a4/32.png) [@galapagdos](https://forum.crystal-lang.org/u/galapagdos)\
**Post date:** [August 10, 2024, 8:48pm UTC](https://forum.crystal-lang.org/t/issue-with-running-crystal-executable-on-another-macos-machine/7080/6 "2024-08-10T20:48:21Z")

</div>

encountered an issue while trying to create static builds on macOS. When running the following command:

bash

crystal build helloworld.cr --static

I received the following error:

```auto
ld: library not found for -lcrt0.o (this usually means you need to install the development package for libcrt0.o)
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Error: execution of command failed with exit status 1: cc "${@}" -o /Users/idan/Documents/dictionary/helloworld -rdynamic -static -L/opt/homebrew/Cellar/crystal/1.13.1/bin/../../../../lib -L/opt/homebrew/Cellar/pcre2/10.44/lib -lpcre2-8 -D_THREAD_SAFE -pthread -L/opt/homebrew/Cellar/bdw-gc/8.2.6/lib -lgc -lpthread -L/opt/homebrew/Cellar/libevent/2.1.12_1/lib -levent -liconv

```

It seems that the linker is unable to find -lcrt0.o, which is typically indicative of a missing development package. I would appreciate any advice on how to resolve this issue and successfully build a static binary.

Thank you very much for your help!

---

<div class="post-metadata">

**Author:** ![kojix2](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/kojix2/32/1583_2.png) [@kojix2](https://forum.crystal-lang.org/u/kojix2)\
**Post date:** [August 11, 2024, 10:10am UTC](https://forum.crystal-lang.org/t/issue-with-running-crystal-executable-on-another-macos-machine/7080/7 "2024-08-11T10:10:00Z")

</div>

The discussion here involves two different topics.

(1) Creating a statically linked executable on Mac

I think this is very challenging. Many sites like StackOverflow appear when searching for issues related to missing `-lcrt0`, but there doesn’t seem to be an easy solution.

(2) Do not link `libpcre2` when regular expressions are not used

This issue is different from (1), and I felt it was worth investigating further.

`hw1.cr`

```cr
puts "hello world"

```

At first glance, this code seems simple, but it’s not as straightforward as it looks. It uses strings and performs the complex operation of outputting to standard output. Let’s compile this code and see what functions are included.

```auto
crystal build --release hw1.cr
nm hw1

```

You’ll notice that it includes many functions. Unless specified otherwise, Crystal loads the following libraries:

> <https://github.com/crystal-lang/crystal/blob/master/src/prelude.cr>

For much simpler code, the standard library isn’t necessary.

```cr
a = 3

```

```auto
crystal build --prelude empty --release simple.cr
nm simple

```

Here, the number of functions is significantly reduced.

```auto
nm hw1 | grep pcre2

```

```auto
                 U pcre2_config_8

```

You can see that this function is being called.

Crystal can output intermediate representation llvm-ir.

```auto
crystal build --emit llvm-ir hw1.cr

```

`hw1.ll` can be compiled like this:

```auto
clang hw1.ll -lm -lz -levent -lgc -lpcre2-8

```

Let’s open `hw1.ll` in a text editor and search for `pcre2`. You’ll find the following declaration:

```auto
declare i32 @pcre2_config_8(i32, ptr) local_unnamed_addr

```

It’s only used here:

```auto
  %41 = call i32 @pcre2_config_8(i32 11, ptr %40), !dbg !17427

```

Let’s try modifying the following line like this. It will likely fail since `ptr %40`.

```auto
  %41 = add i32 0, 17

```

Let’s try compiling without `pcre2`.

```auto
clang hw1.ll -lm -lz -levent -lgc

```

We did it. It compiles without `pcre2`.

```auto
ldd a.out

```

Indeed, it’s not linked.

Running it produces an error.

```auto
Unhandled exception: Invalid libpcre2 version (RuntimeError)
  from /usr/local/share/crystal/src/regex/pcre2.cr:18:33 in '~Regex::PCRE2::version_number:init'

```

The issue lies here.

```cr
module Regex::PCRE2
  @re : LibPCRE2::Code*
  @jit : Bool

  def self.version : String
    String.new(24) do |pointer|
      size = LibPCRE2.config(LibPCRE2::CONFIG_VERSION, pointer) ## %41 HERE!!
      {size - 1, size - 1}
    end
  end

  class_getter version_number : {Int32, Int32} = begin
    version = self.version
    dot = version.index('.') || raise RuntimeError.new("Invalid libpcre2 version") ## THE ERROR!!
    space = version.index(' ', dot) || raise RuntimeError.new("Invalid libpcre2 version")
    # PCRE2 versions can contain -RC{N} which would make `.to_i` fail unless strict is set to false
    {version.byte_slice(0, dot).to_i, version.byte_slice(dot + 1, space - dot - 1).to_i(strict: false)}
  end

```

You can see that the call is exactly the same as `%41`.

```cr
LibPCRE2.config(LibPCRE2::CONFIG_VERSION, pointer)

```

Even though `size` was set to 17, it likely fails because `pointer` was not properly set, leading to an incorrect string generation. However, what it is really doing is simply getting the pcre2 version.

So, let’s modify `/usr/local/share/crystal/src/regex/pcre2.cr` as follows:

```auto
    version = "10.42 2022-12-11" # self.version

```

At least this way, `pcre2` will no longer be necessary.

```auto
crystal build hw1.cr

```

There are certainly programs that do not need regular expressions. For example, programs that only perform numerical calculations. It might be useful if pcre2 is not linked when it is not needed.

---

<div class="post-metadata">

**Author:** ![weirdbricks](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/weirdbricks/32/1741_2.png) [@weirdbricks](https://forum.crystal-lang.org/u/weirdbricks)\
**Post date:** [August 12, 2024, 12:49am UTC](https://forum.crystal-lang.org/t/issue-with-running-crystal-executable-on-another-macos-machine/7080/8 "2024-08-12T00:49:57Z")

</div>

This sounds great to me. Curious: does removing pcre also make the building faster ?

---

<div class="post-metadata">

**Author:** ![jgaskins](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/jgaskins/32/2449_2.png) [@jgaskins](https://forum.crystal-lang.org/u/jgaskins)\
**Post date:** [August 12, 2024, 12:55am UTC](https://forum.crystal-lang.org/t/issue-with-running-crystal-executable-on-another-macos-machine/7080/9 "2024-08-12T00:55:19Z")

</div>

Removing dependencies will always build times, but I wouldn’t imagine the time savings would be noticeable in this case. The Crystal PCRE bindings aren’t that big, so they don’t take that long to compile, and the library itself is already compiled, so removing it saves at most a few milliseconds of link time.

---

<div class="post-metadata">

**Author:** ![kojix2](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/kojix2/32/1583_2.png) [@kojix2](https://forum.crystal-lang.org/u/kojix2)\
**Post date:** [August 12, 2024, 1:09am UTC](https://forum.crystal-lang.org/t/issue-with-running-crystal-executable-on-another-macos-machine/7080/10 "2024-08-12T01:09:04Z")

</div>

They are always curious to know what version of libpcre2 we are using. I have sent a [pull request](https://github.com/crystal-lang/crystal/pull/14891) to protect our secret from them. Hopefully this issue will be fixed.
