Building Mac executables for older OSes

Hi all -

I’m looking for advice/experiences compiling OCaml executables on MacOS, with the intention being that the executable would be runnable on versions of OSX earlier than the version it was compiled on. We currently use a CI machine which intentionally runs an older MacOS version, but this is becoming untenable.

Does anyone have any experience with this (or better yet, examples of doing it?). My first instinct is we will need to build the ocamlc from source using an older XCode SDK like those available from the XCodeLegacy project.

1 Like

No experience but that seems like a good idea. You can try to create an empty switch with opam switch create --empty then adjust its environment (see the environment file in opam - Manual, not sure if it’s possible to act on that file with the opam file tool itslef) so that the C toolchain of your choice gets picked up and then install ocaml and the packages you need.

I don’t exactly remember what problem I ran into, but to deal with it I put -mmacosx-version-min=MM.NN in the CC variable of a custom variant of an OCaml compiler (dkml-compiler). It needed to be at the OCaml compiler level because every object file needs to be compiled with it in your Opam switch. Your Xcode needs to support the SDK version as well … XCodeLegacy seems like it solves that part for you.

I don’t believe I ran into any more problems.

In fact you likely rather want to tweak the setenv: fields of the switch-config file of the switch.

@dbuenzli made me remember that I’ve already upstreamed the variant into Opam, and you can just tweak the environment from the command line. Try the following:

# Choose an SDK with CFLAGS
$ env CFLAGS=-mmacosx-version-min=10.12 \
     opam switch create dkml-4.12.1+minos-10.12 \
     '--formula="dkml-base-compiler" {>= "4.12.1~" & < "4.13.0~"}'

# Make sure you see that SDK in OCaml configuration
$ opam exec --switch dkml-4.12.1+minos-10.12 -- ocamlc -config
version: 4.12.1
...
c_compiler: /usr/bin/clang -arch x86_64
ocamlc_cflags: -O2 -fno-strict-aliasing -fwrapv  -mmacosx-version-min=10.12
... 

That switch should target 10.12+ in the example above.

Be aware there are some differences between the dkml-base-compiler and the ordinary ocaml-base-compiler:
a) on ARM64 it will compile to x86_64, but it always includes ARM64 as a cross-compiler toolchain (even if you are on x86_64)
b) I haven’t announced it yet (it needs an upgrade to 4.14.x and/or 5.x), so you would likely be the first to use it

(There may be a way to do that from the command line with ocaml-base-compiler as well)

2 Likes

Hi,

For shipping OCaml code as part of Docker Desktop I’ve been building everything with MACOSX_DEPLOYMENT_TARGET set e.g. vpnkit/config.yml at 44428dd66e020e34aa415e30c5f2287f3469b082 · moby/vpnkit · GitHub

I’m not sure of the difference between this and -mmacosx-version-min though.

4 Likes

I think it’s best to use MACOSX_DEPLOYMENT_TARGET rather than filter down the command line version. When I looked at this a few years ago (for vpnkit/Docker in fact), it seemed more reliable to set the environment variable and have it percolate down to all the toolchain commands, rather than specify it to just clang. Try not to mix-and-match the CLI flag and the environment variable either, since the CLI overrides the env variable and can lead to unpredictable behaviour if they’re out of sync for any reason.

2 Likes

https://opensource.apple.com/source/cctools/cctools-667.10.0/libstuff/macosx_deployment_target.c.auto.html

Agree with @avsm: Both the clang option and environment variable do the same thing, except the option has higher precedence. If you don’t have any other tool setting the clang option then the environment variable is simpler especially when you can set it at the top of the CI file like @djs55 showed. For desktop use you’ll need to use the setenv option of Opam (mentioned earlier in this thread) to make sure the environment variable is consistently applied over the lifetime of your switch, or just use the single command line I showed earlier.

We really need a page somewhere official that describes this. This question is basic to doing any macOS development on OCaml.

3 Likes

Thanks all for the insightful replies. I’m hoping to try MACOSX_DEPLOYMENT_TARGET soon, unfortunately I don’t have direct access to a Mac for testing this.

No need for old machines. I ship MacOS binaries as simply as this:

MACOSX_DEPLOYMENT_TARGET=10.12 opam switch create ....
eval $(opam env)
MACOSX_DEPLOYMENT_TARGET=10.12 opam install <required package>
MACOSX_DEPLOYMENT_TARGET=10.12 ./buildmycode
MACOSX_DEPLOYMENT_TARGET=10.12 codesign .....

You will have to experiment to find the earliest version number under which this process will complete.

3 Likes

Setting both MACOSX_DEPLOYMENT_TARGET and SDKROOT (after installing the correct SDK) worked as desired. We have an executable compiled on OSX11 which runs on versions as old as OSX10.11

Thanks all!

2 Likes