Skip to content

Instantly share code, notes, and snippets.

@Khady
Created April 14, 2018 14:38
Show Gist options
  • Save Khady/0ba37a201bf66a472dd1b998beac7f83 to your computer and use it in GitHub Desktop.
Save Khady/0ba37a201bf66a472dd1b998beac7f83 to your computer and use it in GitHub Desktop.
Env config to use reason-cli and opam

Proper configuration to use reason-cli and opam

How to properly configure the editor for someone using both ReasonML installed from the reason-cli (which is the recommended way to install reason) and ocaml from opam? This question arries when we are doing both native and bucklescript development.

First thing to remember is that all the editor plugins that really need configuration are based on merlin (as far as I know). More precisely on the ocamlmerlin binary. And they will either take the first ocamlmerlin binary they will find in the path, or the binary from the currently selected opam switch.

The configuration presented bellow allows us to properly configure our environment prior to launch emacs/vscode/vim/… Once this is done, we can launch our editor from the shell with the expected environment and it will pickup the good version of merlin. In theory it is also possible to launch our editor from our OS launcher rather than from the editor, but then we have to be sure to load the good environment globaly. And it doesn’t correspond to the usual workflow when mixing native and bucklescript development. So I will not try to get to this point.

I am using yarn rather than npm because I am more familiar with it and the global installation system seems easier to manage. But I guess it is possible to reach the same state with npm.

Also I am using opam2 because it provides nice new features over opam1 and is already very stable.

Putting merlin in the environment

Once we have installed reason-cli globally using yarn, we will have the ocamlmerlin binary in a path like $HOME/.yarn/bin. We can get this path from the command yarn global bin.

For opam, the binary will be in $HOME/.opam/SWITCH/bin. And this can be added to our path by calling eval $(opam env --sw=SWITCH).

So what we want to do is just to add one of those two paths to the environment and then launch our editor.

By hand in bash, it would be something like this for yarn:

PATH="$(yarn global bin):$PATH" emacs

And like this for opam:

PATH="$(opam env --switch=SWITCH):$PATH" emacs

But it is troublesome to do this every time we want to launch our editor. It is actually very troublesome because we have to prefix all the commands that are using binaries from yarn or opam.

So we want to update our environment globaly for the current shell. This can be done with export. But it is even easier to use a small utilitary like direnv.

Automatic environment setup using direnv

direnv behavior is pretty simple. It will look for a .envrc file in the current directory or any of its parents. This .envrc file is actually a bash script that is sourced by direnv once found. Every time we change directory, it will look for a .envrc and update our environment.

The configuration of direnv for each shell is a bit different. I will not detail it here. It is better to check the direnv documentation.

The important parts are what to put in the .envrc file and where to put this file.

The answer to where is at the root of our project.

And the content of the file is actually very short.

For a project using opam (so targeting native or bytecode compilation):

eval $(opam env --shell=bash --inplace-path --set-switch --readonly --switch=SWITCH)

If we are using a local switch, the --switch=SWITCH part can be removed.

For a project using reason-cli (targeting javascript by using bucklescript), the configuration is not much longer:

PATH_add $(yarn global bin)

PATH_add is a function provided by direnv.

Then we need to execute direnv allow to tell direnv we know about the .envrc in the directory of our project and it is legitimate. This is only needed when we create a new .envrc.

Once all this is done, our .envrc should be automatically used by direnv. We can check if it works by executing which ocamlmerlin and making sure this is actualy the merlin binary we want to use.

Launching the editor

We are now ready to launch the editor. To do so, we need to be in the project directory so that direnv does it jobs. And then execute emacs, code, vim, atom or any other command corresponding to wer favorite editor. That’s all.

Note concerning emacs

I am not sure how all the editors plugins are working. But for emacs by default the merlin package will try to look for the ocamlmerlin binary in opam. Even if the environment has not been populated by opam env. This behavior is not the one expected here. It would take merlin from opam even if we added the reason-cli’s merlin to our path. It is not hard to change this behavior though. Just add this line to emacs configuration:

(setq merlin-command "ocamlmerlin")

The whole emacs configuration I am using for reason and ocaml is available in this GITHUB REPOSITORY. It also makes the configuration of reason-mode more robust. For someone using use-package, it should automatically install everything that it required.

There is a direnv package for emacs that could allow us to skip configuration of the environment prior to launch emacs. But using this plugin rather than setting up the environment first has a big disavantage. As soon as we will jump to a file which is not in a subdirectory of the .envrc file, merlin will not be in the path. If we use merlin-locate on a function or value comming from a library installed by opam it will work. But it is not possible to use merlin in the file we landed in.

Note that this problem doesn’t exist while using yarn or local switches with opam because all the installed libraries will be in subdirectories or .envrc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment