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