Installation of Reason
project and
development environments via npm
.
Requirements:
npm
(currently tested on Mac, Linux, and Windows Linux subsystem).
ReasonProject
installs the Reason
toolchain into a local directory using
npm
. ReasonProject
can therefore be used as a template for new projects,
but can also be used to install the toolchain into the global
environment. ReasonProject
includes: the
compiler toolchain, the source formatter, REPL, and IDE support for popular
editors.
A sandboxed environment models dependencies and builds them into a local directory so that it works reliably for anyone. Installing tools into your global environment is simply a matter of sourcing the sandboxed environment in your
.bashrc
. It's easy to make sandboxed local environments global, and very hard to do the reverse, soReasonProject
starts with local environments.
Install by cloning the repo and running npm install
. This installs all
dependencies locally into the ReasonProject
directory.
git clone https://github.com/reasonml/ReasonProject.git
cd ReasonProject
npm install
Once built, ReasonProject
generates an environment that you can temporarily
load when executing commands. The environment contains an enhanced PATH
containing binaries built by your dependencies, but this environment isn't
loaded into your global path. Some npm run
commands have been setup to allow
you to run some basic tasks, as well as any custom command, all within the
project environment.
There are a couple of built in commands declared in package.json
that you can
execute via npm run
. For example: "npm run start"
, "npm run reasonBuild"
and "npm run clean"
. You can also add your
own named scripts
which give you a nicer alias like npm run myScriptName
.
npm run reasonBuild # Rebuilds after changing
npm run start # Runs the compiled app
npm run clean # Clean if you need to!
A single test file ./src/Test.re
is included. Make a simple change to it and
then run the commands above to see it effect the output.
The rtop
REPL is built into the sandbox. The command npm run top
starts the
REPL.
# Opens `rtop` from the sandbox.
npm run top
To do anything beyond those basic, preconfigured commands, you just prefix your
command with npm run env --
. You should pass the actual command you want to
run after --
.
# By default nothing is found!
which refmt
> Not Found!
# Prefix with "npm run env --" and it finds it!
npm run env -- which refmt
> ~/ReasonProject/node_modules/reason/_build/ocamlfind/bin/refmt
If this becomes tedious, you can add your
own named scripts
so that you can do npm run yourScriptName
instead.
All of the IDE plugins, including integration with error highlighting, autocomplete, and syntax highlighting are included inside of the built project.
Configure your EDITOR
to load the Reason
plugins from your instance of
ReasonProject
. See the instructions for
Atom and
Vim.
The editor config above mostly exists to load the actual editor support, from
the ReasonProject
build. The only thing we need is to make sure the PATH
contains all the important stuff from ReasonProject
's build. There are two
approaches: one continues to avoid global variables (as we've done so far), and
the other doesn't.
You can continue to develop entirely in the isolated sandbox without polluting global environment variables, by opening your editor within the sandbox environment:
npm run env -- vim
npm run env -- atom
npm run env -- mvim
Because you've prepared your
editor
to load editor support from the environment, npm run env -- yourEditor
ensures that your editor will find the editor support in your environment
variables.
Note: If you use
atom
, and already haveopam
installed, then there's a known issue whereatom
has problems loading, but you can fix it easily by commenting out any part in yourbashrc
that sources opam environments. We will come up with a long term solution at some point.
Pure sandboxed based development doesn't always work for certain workflows.
(prefixing all commands with npm run
may not work well). In that case, you
can easily inject your successfully built project's environment into the global
PATH
, by putting the following in your .bashrc
:
# In your .bashrc
pushd ~/pathTo/ReasonProject/ && \
eval $(~/pathTo/ReasonProject/node_modules/.bin/dependencyEnv) && \
popd
ReasonProject
is meant to be the starting point of your own project. You'll want
to make use of existing libraries in your app, so
browse the growing set of opam
packages ported to npm
under
opam-alpha
. If there's something
that hasn't yet been ported from opam
, make a pull request to
this repo and the package will automatically
be ported (as soon as the daemon picks it up).
Option 1
: Install a dependency into the project sandbox, and use --save
so that your package.json
is updated.
npm install --save @opam-alpha/cstruct
Option 2
: Edit the package.json
manually to include your new dependency and run npm install
.
Note: Sometimes options
1
and2
above fail because some other dependency that is rebuilt as a result of theinstall
was not designed to build in an idempotent manner. In that case, just add the new dependency to yourpackage.json
"dependencies"
,rm -r node_modules
, and then runnpm install
. This installs from a clean slate.
Note:
opam-alpha
is "alpha" - we may move to a new namespaceopam-beta
once we apply the lessons we've learned fromopam-alpha
. All the should exist as they are, but a next generationopam-beta
universe onnpm
would have everythingopam-alpha
has (and then some). The work to upgrade your projects will likely be minimal.
This merely adds and builds the dependency. It doesn't mean your build system
will know to link to it. Accomplishing that is build system dependent, but if
using the example build system (rebuild
, which is based on ocamlbuild
), you
can get an idea for the options by doing npm run buildHelp
. Typically you
need to configure the reasonBuild
entry in package.json
to add the -pkg dependencyPackage
. Consult your dependency's docs.
npm
allows scripts
to be specified in your project's package.json
. These
scripts
are a named set of commands. A few scripts have special meaning, such
as the postinstall
script.
The
postinstall
script is how your project compiles itself. It is guaranteed that thepostinstall
script executes any time you runnpm install
in this package, or any time another package installs you as a dependency. You're also guaranteed that yourpostinstall
script is executed after all of your dependencies'postinstall
scripts.
You can add new named scripts in the package.json
scripts
field. Once
added, you can then run them via npm run scriptName
from within the project
root.
eval $(dependencyEnv)
is commonly used in these scripts
. This eval
statement augments the environment for the duration of the named script, which
ensures that important binaries (such as refmt
) are in the PATH
.
When the entire purpose of developer tools is to generate a binary (such as a compiler) to be included in your
PATH
, or produce a library whose path should be specified in an special environment variable, it's almost like the environment variable is the public API of that package.dependencyEnv
allows your script to see the environment variables that your immediate dependencies wanted to publish as their public API. You can learn how packages can publish environment variables in the dependency-env repo.
You can have multiple clones/forks/builds of ReasonProject
- one for each of
your projects. When you make changes, you can share the project easily with
anyone else because you are modelling all dependencies via package.json
. If
also using the global
environment, you may want to
designate one special ReasonProject
, that is only used for augmenting the
global path.
ReasonProject
sets up your environment for building an application. We
haven't yet mentioned how to then share your work with other people as an
npm
dependency itself. More coming soon.
In general, if something goes wrong, try deleting the local node_modules
directory that was installed, and then try reinstalling using npm install -f
(to avoid using a stale cache). Then if that doesn't work, follow the following
steps to debug your specific failed dependency.
When npm install
fails to install one of your dependencies, it's typically
because a postinstall
step of a package has failed. Read the logs to
determine which one is failing. npm
will delete the directory of the failed
package so the failed install won't be in node_modules
, but you can
usually try to reinstall it explicitly, and debug the installation. Suppose the
@opam-alpha/qcheck
package failed to install. Let's recreate the failure so
we can debug it.
#####Do a dry run:
Let's see what an npm install
for this package would install. The --dry-run
flag avoids actually installing anything.
npm install --dry-run @opam-alpha/qcheck
In my project, it says it would only need to install the following packages.
That's because all of the other ones must have already been installed in
node_modules
.
# Output
test@1.0.0 /Users/jwalke/Desktop/tmp
└─┬ @opam-alpha/qcheck@0.4.0
└── qcheck-actual@0.4.0 (git://github.com/npm-opam/qcheck.git)
Note: Sometimes it won't traverse
git
dependencies to find all the potentially installed package. That's okay.
So we want to install that now, but without executing the install scripts so we
pass the --ignore-scripts
flag. Without that flag, it would fail when running
the scripts again, and then remove the package again!
npm install --ignore-scripts @opam-alpha/qcheck@0.4.0
This will just install the source code, and let us know what it actually installed.
Now, make sure npm
didn't do something weird with installing new versions of package
that didn't show up in the dry run, and make sure it installed things
as flat as possible in node_modules
, as opposed to nesting node_modules
for compiled ocaml packages inside of other node_modules
. Ideally, everything
is a flat list inside the ReasonProject/node_modules
directory.
cd
into the package that was failing to build correctly (it was actually the qcheck-actual
package
in our case). Run the build command in package.json
's postinstall
field by doing npm run postinstall
.
cd node_modules/qcheck-actual
npm run postinstall
Now the package should fail as it did when you tried to install your top level project, but without removing the packages, so you can debug the issue, fix it, then try rebuilding again. Usually you need to reconfigure the problematic package, or fix the build script. Fix it, and push an update for the package.
Finally, once you've pushed a fix to the package for the issue, rm
the entire project's node_modules
directory and re-run npm install
from the top again. This just makes sure you've got everything
nice and clean as if you installed it for the first time.
npm run whereisocamlmerlin