# Cross Platform (MacOSX/\*nix) static linking

**URL:** <https://discuss.ocaml.org/t/cross-platform-macosx-nix-static-linking/2528>\
**Category:** Ecosystem\
**Tags:** ssl, libev, esy, lwt, jbuilder\
**Created:** [September 7, 2018, 10:43am UTC](https://discuss.ocaml.org/t/cross-platform-macosx-nix-static-linking/2528 "2018-09-07T10:43:40Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![mdedetrich](https://avatars.discourse-cdn.com/v4/letter/m/e47c2d/32.png) [@mdedetrich](https://discuss.ocaml.org/u/mdedetrich)\
**Post date:** [September 7, 2018, 10:43am UTC](https://discuss.ocaml.org/t/cross-platform-macosx-nix-static-linking/2528/1 "2018-09-07T10:43:40Z")

</div>

Note that this is a related to [Statically Link](https://discuss.ocaml.org/t/statically-link/1464/10)

So we started a greenfield project at work using OCaml/Reason (we are mainly a Scala based team) and we have successfully deployed however we noticed an issue that we need some help in.

The issue comes across making executable that can run without any runtime C dependencies, we need this due to our CDP build system. Since OCaml produces native bytecode, you basically have to do static linking for the platform. To be clear, the libraries which contain C dependencies which we are trying to static link are ocaml-ssl ([https://github.com/savonet/ocaml-ssl](https://github.com/savonet/ocaml-ssl)) which binds to the openssh library and also libev which is used by `conf-libev`.

There is a guide on how to do this here [http://rgrinberg.com/posts/static-binaries-tutorial/](http://rgrinberg.com/posts/static-binaries-tutorial/) however the main issue is that global static linking (i.e. using `(flags (-ccopt -static))`) doesn’t work on MacOS (note that MacOS perfectly supports static linking specific dependency, just not every dependency in your project). This leaves to solutions for us. Most of our developers use machines that have MacOS for local development, however in production we deploy docker containers which run linux.

1. The ideal (or perfect) solution is to only statically link the `libev` and `openssh` library on every platform. Unfortunately I haven’t managed to figure out how to do this using `-ccopt` flags. These flags are directly passed into `ld` however with a lot of searching I haven’t actually managed to figure out how to only statically link `openssl`/`libev` (I know there is a `-l` flag that can be passed into `ld` to do this, but I didn’t manage to get it to work in an agnostic way. Furthermore it has the known issue that it will always pick up the dynamic `.dynlib`/`.so` libraries over the static `.a` files, so you need a manual prepossessing step to deal with this.
2. The less ideal solution is to allow you to build your project with an optional flag (i.e. something like `--static-linux`) so that by default it uses dynamic libraries but in our build systems on linux we can just build with `--static-linux`). The issue is I haven’t managed to figure this out with `esy`/`dune` (`esy` just uses `dune` under the hood). Firstly someone on discord recommended me to use the `env` stanza to conditionally add the `(flags (-ccopt -static))` flags and then use `--profile` in `dune` however the issue with this is that `esy` uses the `-p` (i.e. package flag) to build dune projects which can’t be mixed with `--profile`. I also had issues just getting the `env` command to work when running dune directly, i.e. if you have a trivial dune file as shown

```auto
(library
  (name lib)
  (public_name hello-ocaml)
(libraries lambda-term lwt lwt_ssl))

```

Where would you put the `env` stanza, i.e. would you put it here

```auto
(env
 (static-linux (flags (:standard -ccopt -static))))
(library
  (name lib)
  (public_name hello-ocaml)
(libraries lambda-term lwt lwt_ssl))

```

Or here

```auto
(library
  (name lib)
  (env
    (static-linux (flags (:standard -ccopt -static))))
  (public_name hello-ocaml)
(libraries lambda-term lwt lwt_ssl))

```

Note that in this case I still have the issue that I cant do something like `dune build --profile=static-linux -p hello` because the flags don’t mix.

Anyone have any recommendation on what to do here?

---

<div class="post-metadata">

**Author:** ![Khady](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/khady/32/469_2.png) [@Khady](https://discuss.ocaml.org/u/Khady)\
**Post date:** [September 7, 2018, 11:55am UTC](https://discuss.ocaml.org/t/cross-platform-macosx-nix-static-linking/2528/2 "2018-09-07T11:55:27Z")

</div>

I don’t have a good answer to your problem. But two remarks:

- if in prod you have only docker containers running linux, why do you need static linking on macos? You should be able to build the binary for production in a container too. Or I am missing something?
- esy uses what you tell it to use. It doesn’t do any magic. You can control what is in the build field of your package.json. You are not forced to use the `-p` flag. You are not forced to put a dune command in this field. It could be a script running a configure part at first checking for the environment and deciding what are the good compilation flags to use. The `-p` flag is not magic, it is just `--root . --only-packages PACKAGE --promote ignore --no-config --profile release`. You can use all those and replace the --profile part. Also if all you dependencies are using dune, you can vendor them and set some global flags for everything using the `env` stanza in `dune-workspace`.

---

<div class="post-metadata">

**Author:** ![mdedetrich](https://avatars.discourse-cdn.com/v4/letter/m/e47c2d/32.png) [@mdedetrich](https://discuss.ocaml.org/u/mdedetrich)\
**Post date:** [September 7, 2018, 12:19pm UTC](https://discuss.ocaml.org/t/cross-platform-macosx-nix-static-linking/2528/3 "2018-09-07T12:19:13Z")

</div>

> [@Khady](#):
>
> if in prod you have only docker containers running linux, why do you need static linking on macos? You should be able to build the binary for production in a container too. Or I am missing something?

You aren’t missing anything, this is precisely what I wan’t to do (I am just figuring out how to do this).

> [@Khady](#):
>
> esy uses what you tell it to use. It doesn’t do any magic. You can control what is in the build field of your package.json. You are not forced to use the `-p` flag. You are not forced to put a dune command in this field. It could be a script running a configure part at first checking for the environment and deciding what are the good compilation flags to use. The `-p` flag is not magic, it is just `--root . --only-packages PACKAGE --promote ignore --no-config --profile release` . You can use all those and replace the --profile part. Also if all you dependencies are using dune, you can vendor them and set some global flags for everything using the `env` stanza in `dune-workspace` .

This is the precisely the info I needed, will try this out now, thanks!

---

<div class="post-metadata">

**Author:** ![bobbypriambodo](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/bobbypriambodo/32/179_2.png) [@bobbypriambodo](https://discuss.ocaml.org/u/bobbypriambodo)\
**Post date:** [September 7, 2018, 1:49pm UTC](https://discuss.ocaml.org/t/cross-platform-macosx-nix-static-linking/2528/4 "2018-09-07T13:49:39Z")

</div>

> [@mdedetrich](#):
>
> however the main issue is that global static linking (i.e. using `(flags (-ccopt -static))` ) doesn’t work on MacOS

I don’t think this is the case if you use Docker. If you run the build with static link flags in a container, the linked libraries will be of the container and not the host MacOS. Any C libraries installed (for example from running `opam depext` inside the container) would be available for static linking AFAIK.

Unless, of course, if you are planning to bypass Docker on your dev env, which would defeat one of the purposes of having Docker to prevent the phrase “it works on my machine”. I believe it would be better if your devs also build and run the binary from a Docker container locally to make it (almost) identical with your prod environment.
