Getting development on stanc3 up and running locally

Setup

Using Opam and Dune

The recommended way to develop stanc3 is using opam and dune on a Unix-like system (MacOS or Linux). See Windows development for tips on developing on Windows.

This requires you have make, git, curl, and m4 installed via apt, homebrew, or your system's package manager.

  1. Download the repository (or your fork of it) from Github
  2. Install the necessary dependencies by running

    cd scripts; bash -x ./setup_dev_env.sh.

    If you already have opam installed, you can instead run

    opam update; opam install --deps-only --with-test .

    If you wish to install stanc on your system, you can run the above opam command without --deps-only.

  3. You can now build a stanc binary by running make. If this succeeds, you have the required software.

Windows development

We recommend using Windows Subsystem for Linux (WSL) for development on Windows. While this will not produce native-Windows binaries by default (though they can be cross-compiled), it is far easier to use and set up than the other options for Windows development.

Once you have installed and configured WSL, you can proceed through the steps above through the WSL shell.

Alternative: Using Nix

Nix is a declarative package manager with a focus on reproducible builds. We provide the ability to use Nix to build, test and run Stanc3. We recommend trying the opam instructions first if you are not an existing Nix user, with these as a backup.

If you have nix installed, you can build Stanc3 by running the following command in the stanc3 directory:

nix-build

The binary will be in result/bin/stanc. It may take a minute the first time you run it. Alternatively, the following is sometimes a faster way to build:

nix-shell --command "dune build"

To run the test suite, run:

nix-shell --command "dune build --profile release @runtest"

To install Stanc3 to your system, run:

nix-env -i -f default.nix

To drop into a sandboxed development shell with all of the build dependencies of Stanc3 plus packages for an OCaml development environment (dune, ocp-indent, ocamlformat, merlin and utop), run:

nix-shell

Development

Useful commands

Testing

Once you have the necessary software, you can make your edits to the source files in src/ and tests in test/. Running the command dune runtest (or make test) will show any test output which is different from the expected output. In our continuous integration, this results in test failures. If you are knowingly changing the output, and the differences highlighted by dune runtest look correct, you can run dune promote to change the expected output to match your current tests.

Test coverage

We have support for computing test coverage using the bisect_ppx library. To generate a coverage report, run make testcoverage. This will print a summary of the coverage to the terminal, and generate a detailed HTML report in the _coverage directory. This report can be opened in a web browser.

Code coverage of the master branch is available on codecov.io.

Code formatting

We recommend setting up your editor to use OCamlformat automatically. This will differ by editor, but is possible in most popular editors such as Emacs, VSCode, etc. See Editor advice for more specific tips.

If you would like to manually run the auto-formatter on all OCaml code in the repository, run make format. Similar to the above, you will need to run dune promote to accept these changes into the code.

If you would like to run the formatter before each commit (recommended), run

scripts/hooks/install_hooks.sh

Note: ocamlformat does not support .mly or .mll files used in the frontend.

Updating the parser error messages

If the parser has been edited, the command dune build @update_messages will add any new error states to the parser.messages file. These will say

TODO: PARSER MESSAGE NEEDED HERE.

See adding or changing the syntax error messages for more information.

Note that python3 is required to run this command.

Building these docs

Docs can be built with make doc. This will generate a _build/default/_doc/_html/index.html file which can be opened in a web browser.

Developing with stanc3 and CmdStan

In order to use the locally built development version of stanc3 with CmdStan, you need to set the STANC3 makefile variable in the make/local file in CmdStan. Example:

STANC3=relative/or/absolute/path/to/the/cloned/stanc3/folder

This will automatically rebuild the stanc3 binary used in CmdStan if a change was made to the stanc3 source files.

Editor advice

For working on this project, we recommend using either VSCode or Emacs as an editor, due to their good OCaml support through Merlin: syntax highlighting, auto-completion, type inference, automatic case splitting, and more. For people who prefer a GUI and have not memorized all Emacs or Vim keystrokes, VSCode might have the less steep learning curve. Anything with Merlin support is fine.

Setting up VSCode

Install instructions for VSCode can be found here.

For Windows users: We recommend using Remote - WSL to program within WSL from a native Windows installation of VSCode. This extension allows you to connect to the WSL instance as if it is a remote machine, removing the need for an X11 server or other method of running a Linux GUI app directly.

Once in VSCode (on any platform), simply install the OCaml extension and you should be ready to go.

Setting up Emacs

The best way to edit OCaml in Emacs is through Tuareg mode.

To get full Merlin support, you will need to run opam user-setup install after installing Merlin in our dev dependencies.

If you use use-package.el, this snippet should set up Tuareg+Merlin+OCamlformat

(use-package tuareg
  :config
  (electric-indent-mode 0)
  (face-spec-set
   'tuareg-font-lock-constructor-face
   '((((class color) (background light)) (:foreground "SaddleBrown"))
     (((class color) (background dark)) (:foreground "burlywood1"))))
  (add-hook 'tuareg-mode-hook
            (lambda ()
              (prettify-symbols-mode 0))))
(use-package merlin
  :config
  (add-hook 'tuareg-mode-hook 'merlin-mode t)
  (with-eval-after-load 'company
    (add-to-list 'company-backends 'merlin-company-backend)))

(require 'ocamlformat)
(add-hook 'tuareg-mode-hook
          (lambda ()
            (define-key tuareg-mode-map (kbd "C-M-<tab>") #'ocamlformat)
            (add-hook 'before-save-hook #'ocamlformat-before-save)))

Finding your way around the code

Even to an experienced OCaml developer, there are certain practices in the stanc3 codebase which might be unfamiliar. We have tried to document those on our Core ideas page.

Advanced development

Fuzzing

OCaml has support for building binaries instrumented for use with theAFL++ fuzzing tool. This requires a new opam switch which includes the ocaml-option-afl base package.

opam switch create "stanc-fuzz" --packages "ocaml-option-afl,ocaml.4.14.1"
eval $(opam env)
./scripts/install_build_deps.sh

After this, the normal build process will produce a binary ready for fuzzing.

Profiling

Profiling support is not directly provided in the stanc3 repository, but you can use the Landmarks library to profile the code.

This requires editing the dune files in the src/ directory to include the landmarks library, similar to how the bisect_ppx library is instrumented by default.

(instrumentation
  (backend landmarks --auto))

You can then build a profiled version of the code by running dune build src/stanc/stanc.exe --instrument-with landmarks --force.

To configure the profiler at runtime, consult the Landmarks documentation. A good starting command is

OCAML_LANDMARKS="on,output=prof.txt" stanc.exe mymodel.stan