How to build for release?

I always feel confused about building with Crystal.
The build methods adopted in GitHub Actions are quite inconsistent.

Code search results (github.com)

What is the appropriate command to build a release version?

  1. Is shards build --production --release --static --no-debug sufficient?
  2. When should I use crystal build?
  3. How to build universal binary for Darwin with GitHub Actions?

There are some Crystal build options like --static, --release, --no-debug, -Dpreview_mt that are not displayed in the shards prompt, but they donā€™t cause any errors. Should I use them? When should I use them?

Reference
crystal-build
shards-build
github-actions
static_linking

1 Like

First of all, shards build always delegate all it unknown options into Crystal.

Then, if not add --no-debug, it always include debug info, add this option will reduce file size significantly.

Add -Dpreview_mt only if you know what you want.

--static not always work for current version Compiler, in fact, AFAIK, it only work when build static binary from alpine docker container, for build a linux static binary or an universal binary for Darwin without use docker, you probably want to check Use zig cc as a alternative linker (draft version) - #2 by zw963

you should always use --release when release a binary.

For --production, as said by shards help,

--production                     same as `--frozen --without-development`
1 Like

Working with zig is quite an interesting idea.
Currently I have got an effective workflow that is able to build release versions adapted to all Tier 1 platforms, most Tier 2 platforms and some Tier 3 platforms.
Itā€™s partially relying on qemu and I am glad to find more efficient methods.

1 Like

Nice to see Chinese people using Crystal to make products!

Iā€™m not fighting alone~

1 Like

@Sunrise Hereā€™s some explanation: ā€œto buildā€ means compiling and linking an executable program.

The shards build command makes sure dependencies are installed before building, then it calls crystal build. Itā€™s a mere convenience, and not required at all. Basically it does:

$ shards check || shards install
$ crystal build <target.main> -o bin/<target> <args>

The --production and --frozen options are shardsā€™ options. They tell Shards to not install the development dependencies, and to install the locked dependencies. See shards install --help for more details.

The crystal build command is the command that builds your program. Thereā€™s a plethora of arguments you can pass to customize the build. See crystal build --help for details.

For building a production version of your program, youā€™ll likely want --release, that enables both --single-module and -O3 that will enable the most aggressive optimizations. It also adds the :release compile time flag that may be used to differentiate a development build vs a release build.

By default we only enable debug lines sections, in order to have file:line information in backtraces (exceptions and debuggers). This can be disabled with --no-debug. On the opposite, you can enable full debug info with --debug to be able to inspect variables in debuggers. I recommend to use neither unless you need to debug, in which case --debug will be useful.

The --static option will compile a static binary, that doesnā€™t need any shared library to run (theyā€™re embedded right into the executable). It can be useful but can be tricky.

The -Dpreview_mt compile time flag enables a multi-threaded environment (by default crystal programs are single threaded). It makes the stdlib thread safe, and allows to run fibers in parallel in multiple threads. That being said itā€™s still in preview, community shards arenā€™t necessarily thread safe, and itā€™s under heavy refactor in RFC 0002 to finally make it official.

To build a program for production, you can configure a target in shard.yml then call:

$ shards build --production --frozen <target> --release

Or decompose in two calls, which is less confusing:

$ shards install --production --frozen
$ crystal build src/myapp.cr -o bin/myapp --release

Sadly, I canā€™t help with macOS universal builds.

11 Likes

Thank you for the detailed explanation.

I finally found a way to create fat binary for macOS on any platform and that is konoui/lipo. I have added it to my workflow and it works as expected. :smiling_face_with_three_hearts:

1 Like

The most awesome answer I have ever seen!

We need add this answer into somewhere in official book!

ping @straight-shoota